← 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');
});