← back to Designer Wallcoverings
DW-Agents/dw-agents/example-agent-with-hooks.ts
558 lines
/**
* Example Agent with Hooks API Integration
*
* This example shows how to integrate the shared-hooks-api module
* into any DW-Agent for unified hook execution.
*
* Port: 9999 (example)
*/
import express, { Request, Response } from 'express';
import { setupHooksAPI, executeHook, hooks, HooksClient, AVAILABLE_HOOKS } from './shared-hooks-api';
const app = express();
const PORT = 9999;
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Initialize type-safe hooks client
const hooksClient = new HooksClient(60000);
/**
* METHOD 1: Setup REST API endpoints for hooks
* This automatically creates:
* - GET /api/hooks (list all hooks)
* - GET /api/hooks/:hookName (get hook info)
* - POST /api/hooks/:hookName (execute hook)
*/
setupHooksAPI(app);
/**
* METHOD 2: Use hooks programmatically in your routes
*/
// Dashboard
app.get('/', (req: Request, res: Response) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Example Agent - Hooks Integration</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8fafc;
}
h1 { color: #1e40af; }
.section {
background: white;
padding: 20px;
margin: 20px 0;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.hook-category {
margin: 20px 0;
}
.hook-category h3 {
color: #2563eb;
border-bottom: 2px solid #2563eb;
padding-bottom: 5px;
}
.hook-item {
background: #f1f5f9;
padding: 10px 15px;
margin: 8px 0;
border-radius: 4px;
display: flex;
justify-content: space-between;
align-items: center;
}
.hook-name {
font-weight: bold;
color: #1e40af;
font-family: monospace;
}
.hook-desc {
color: #64748b;
font-size: 14px;
margin: 5px 0;
}
button {
background: #2563eb;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
button:hover {
background: #1d4ed8;
}
.test-form {
background: #eff6ff;
padding: 15px;
border-radius: 4px;
margin: 10px 0;
}
input, textarea {
width: 100%;
padding: 8px;
margin: 5px 0;
border: 1px solid #cbd5e1;
border-radius: 4px;
font-family: monospace;
}
.result {
background: #f0fdf4;
border-left: 4px solid #22c55e;
padding: 15px;
margin: 10px 0;
display: none;
}
.error {
background: #fef2f2;
border-left: 4px solid #ef4444;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.stat-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
border-radius: 8px;
text-align: center;
}
.stat-value {
font-size: 32px;
font-weight: bold;
}
.stat-label {
font-size: 14px;
opacity: 0.9;
}
</style>
</head>
<body>
<h1>Example Agent - Hooks API Integration</h1>
<div class="section">
<h2>Quick Stats</h2>
<div class="stats">
<div class="stat-card">
<div class="stat-value">${Object.keys(AVAILABLE_HOOKS).length}</div>
<div class="stat-label">Available Hooks</div>
</div>
<div class="stat-card" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">
<div class="stat-value">5</div>
<div class="stat-label">Integration Methods</div>
</div>
<div class="stat-card" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);">
<div class="stat-value">REST</div>
<div class="stat-label">API Endpoints</div>
</div>
</div>
</div>
<div class="section">
<h2>Integration Methods</h2>
<div class="hook-category">
<h3>1. REST API Endpoints (Auto-generated)</h3>
<div class="hook-item">
<div>
<div class="hook-name">GET /api/hooks</div>
<div class="hook-desc">List all available hooks with descriptions</div>
</div>
<button onclick="testEndpoint('/api/hooks', 'GET')">Test</button>
</div>
<div class="hook-item">
<div>
<div class="hook-name">POST /api/hooks/slack</div>
<div class="hook-desc">Execute Slack hook with params</div>
</div>
<button onclick="showTestForm('slack')">Test</button>
</div>
</div>
<div class="hook-category">
<h3>2. Convenience Wrappers (hooks.*)</h3>
<div class="test-form">
<p><strong>Send Slack Message</strong></p>
<input type="text" id="slack-channel" placeholder="Channel (e.g., general)" value="general">
<input type="text" id="slack-message" placeholder="Message" value="Test from Example Agent">
<button onclick="testConvenience('slack')">Send via hooks.slack.send()</button>
</div>
<div class="test-form">
<p><strong>Send Email</strong></p>
<input type="text" id="email-to" placeholder="To" value="test@example.com">
<input type="text" id="email-subject" placeholder="Subject" value="Test Email">
<textarea id="email-body" placeholder="Body" rows="3">This is a test email from Example Agent</textarea>
<button onclick="testConvenience('gmail')">Send via hooks.gmail.send()</button>
</div>
</div>
<div class="hook-category">
<h3>3. Direct Execution (executeHook)</h3>
<div class="test-form">
<p><strong>Execute Any Hook</strong></p>
<input type="text" id="direct-hook" placeholder="Hook name" value="slack">
<input type="text" id="direct-args" placeholder="Arguments" value='send general "Test message"'>
<button onclick="testDirect()">Execute</button>
</div>
</div>
<div class="hook-category">
<h3>4. Type-Safe Client (HooksClient)</h3>
<div class="test-form">
<p><strong>Health Check</strong></p>
<select id="health-service">
<option value="web">Web Server</option>
<option value="gmail">Gmail</option>
<option value="slack">Slack</option>
<option value="shopify">Shopify</option>
</select>
<button onclick="testClient('health')">Check via hooksClient.healthCheck()</button>
</div>
</div>
</div>
<div class="section">
<h2>Available Hooks</h2>
${Object.entries(AVAILABLE_HOOKS).map(([name, info]) => `
<div class="hook-category">
<div class="hook-item">
<div>
<div class="hook-name">${name}</div>
<div class="hook-desc">${info.description}</div>
</div>
<button onclick="showHookInfo('${name}')">Info</button>
</div>
</div>
`).join('')}
</div>
<div id="result" class="result"></div>
<script>
// Test REST endpoints
async function testEndpoint(path, method = 'GET', body = null) {
showLoading();
try {
const options = {
method,
headers: { 'Content-Type': 'application/json' }
};
if (body) options.body = JSON.stringify(body);
const res = await fetch(path, options);
const data = await res.json();
showResult(data, res.ok);
} catch (error) {
showResult({ error: error.message }, false);
}
}
// Test convenience wrappers
async function testConvenience(type) {
showLoading();
try {
let res;
if (type === 'slack') {
const channel = document.getElementById('slack-channel').value;
const message = document.getElementById('slack-message').value;
res = await fetch('/api/test/convenience/slack', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ channel, message })
});
} else if (type === 'gmail') {
const to = document.getElementById('email-to').value;
const subject = document.getElementById('email-subject').value;
const body = document.getElementById('email-body').value;
res = await fetch('/api/test/convenience/gmail', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ to, subject, body })
});
}
const data = await res.json();
showResult(data, res.ok);
} catch (error) {
showResult({ error: error.message }, false);
}
}
// Test direct execution
async function testDirect() {
showLoading();
const hookName = document.getElementById('direct-hook').value;
const args = document.getElementById('direct-args').value;
try {
const res = await fetch('/api/test/direct', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ hookName, args })
});
const data = await res.json();
showResult(data, res.ok);
} catch (error) {
showResult({ error: error.message }, false);
}
}
// Test type-safe client
async function testClient(type) {
showLoading();
const service = document.getElementById('health-service').value;
try {
const res = await fetch('/api/test/client/health', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ service })
});
const data = await res.json();
showResult(data, res.ok);
} catch (error) {
showResult({ error: error.message }, false);
}
}
// Show hook info
async function showHookInfo(hookName) {
testEndpoint('/api/hooks/' + hookName, 'GET');
}
// UI helpers
function showLoading() {
const el = document.getElementById('result');
el.className = 'result';
el.style.display = 'block';
el.innerHTML = '<strong>Executing...</strong>';
}
function showResult(data, success) {
const el = document.getElementById('result');
el.className = success ? 'result' : 'result error';
el.style.display = 'block';
el.innerHTML = '<strong>' + (success ? 'Success' : 'Error') + ':</strong><pre>' +
JSON.stringify(data, null, 2) + '</pre>';
}
</script>
</body>
</html>
`);
});
// Test endpoint for convenience wrappers
app.post('/api/test/convenience/slack', async (req: Request, res: Response) => {
const { channel, message } = req.body;
try {
const result = await hooks.slack.send(channel, message);
res.json(result);
} catch (error: any) {
res.status(500).json({ success: false, error: error.message });
}
});
app.post('/api/test/convenience/gmail', async (req: Request, res: Response) => {
const { to, subject, body } = req.body;
try {
const result = await hooks.gmail.send(to, subject, body);
res.json(result);
} catch (error: any) {
res.status(500).json({ success: false, error: error.message });
}
});
// Test endpoint for direct execution
app.post('/api/test/direct', async (req: Request, res: Response) => {
const { hookName, args } = req.body;
try {
const result = await executeHook(hookName, args);
res.json(result);
} catch (error: any) {
res.status(500).json({ success: false, error: error.message });
}
});
// Test endpoint for type-safe client
app.post('/api/test/client/health', async (req: Request, res: Response) => {
const { service } = req.body;
try {
const result = await hooksClient.healthCheck(service);
res.json(result);
} catch (error: any) {
res.status(500).json({ success: false, error: error.message });
}
});
/**
* METHOD 3: Real-world integration examples
*/
// Example: Process new order with multiple hooks
app.post('/api/orders/process', async (req: Request, res: Response) => {
const { orderId, customerEmail, totalAmount } = req.body;
try {
// 1. Process order via hook
const orderResult = await executeHook('order-processing', orderId);
if (!orderResult.success) {
throw new Error(`Order processing failed: ${orderResult.error}`);
}
// 2. Notify team via Slack
await hooks.slack.send('orders',
`New order processed: #${orderId} - $${totalAmount} from ${customerEmail}`
);
// 3. Send confirmation email
await hooks.gmail.send(
customerEmail,
'Order Confirmation',
`Thank you for your order #${orderId}. Total: $${totalAmount}`
);
// 4. Log to inter-agent chat
await executeHook('inter-agent-chat',
`send agent-accounting "New order: #${orderId} - $${totalAmount}"`
);
res.json({
success: true,
message: 'Order processed and notifications sent',
orderId
});
} catch (error: any) {
// Error notification via hook
await hooks.errors.notify('high', `Order processing failed: ${error.message}`);
res.status(500).json({
success: false,
error: error.message
});
}
});
// Example: Scheduled health check
app.get('/api/health/check-all', async (req: Request, res: Response) => {
const services = ['web', 'gmail', 'slack', 'shopify'];
const results: any = {};
for (const service of services) {
const result = await hooksClient.healthCheck(service);
results[service] = {
healthy: result.success,
message: result.success ? 'OK' : result.error
};
// Alert on failures
if (!result.success) {
await hooks.errors.notify('critical',
`Service ${service} health check failed: ${result.error}`
);
}
}
const allHealthy = Object.values(results).every((r: any) => r.healthy);
res.json({
success: allHealthy,
services: results,
timestamp: new Date().toISOString()
});
});
// Example: Marketing campaign with multi-channel notifications
app.post('/api/marketing/campaign/launch', async (req: Request, res: Response) => {
const { campaignName, targetAudience, budget } = req.body;
try {
// 1. Notify marketing team
await hooks.slack.send('marketing',
`Campaign launching: ${campaignName}\nAudience: ${targetAudience}\nBudget: $${budget}`
);
// 2. Notify executives
await executeHook('inter-agent-chat',
`broadcast "New marketing campaign: ${campaignName} ($${budget})"`
);
// 3. Send detailed email to stakeholders
await hooks.gmail.send(
'stakeholders@company.com',
`Campaign Launch: ${campaignName}`,
`Campaign: ${campaignName}\nTarget: ${targetAudience}\nBudget: $${budget}\nLaunch: ${new Date().toISOString()}`
);
res.json({
success: true,
message: 'Campaign launched successfully',
campaign: campaignName
});
} catch (error: any) {
await hooks.errors.notify('high', `Campaign launch failed: ${error.message}`);
res.status(500).json({ success: false, error: error.message });
}
});
// Status endpoint
app.get('/api/status', (req: Request, res: Response) => {
res.json({
success: true,
agent: 'Example Agent',
port: PORT,
hooksIntegrated: true,
availableHooks: Object.keys(AVAILABLE_HOOKS).length,
integrationMethods: [
'REST API Endpoints',
'Convenience Wrappers (hooks.*)',
'Direct Execution (executeHook)',
'Type-Safe Client (HooksClient)',
'Programmatic Integration'
]
});
});
// Start server
app.listen(PORT, () => {
console.log(`
╔═══════════════════════════════════════════════════════════╗
║ ║
║ Example Agent - Hooks API Integration ║
║ ║
╟───────────────────────────────────────────────────────────╢
║ Port: ${PORT} ║
║ Dashboard: http://localhost:${PORT} ║
║ Status: http://localhost:${PORT}/api/status ║
║ Hooks API: http://localhost:${PORT}/api/hooks ║
║ ║
║ Available Hooks: ${Object.keys(AVAILABLE_HOOKS).length} ║
║ Integration Methods: 5 ║
║ ║
╚═══════════════════════════════════════════════════════════╝
`);
console.log('\nIntegration Methods:');
console.log('1. REST API - Auto-generated endpoints');
console.log('2. Convenience Wrappers - hooks.slack.send(), hooks.gmail.send()');
console.log('3. Direct Execution - executeHook(name, args)');
console.log('4. Type-Safe Client - hooksClient.healthCheck()');
console.log('5. Programmatic - Full integration in routes\n');
});