← back to Designer Wallcoverings

DW-Agents/create-agent-template.ts

330 lines

#!/usr/bin/env tsx
/**
 * Agent Template Creator
 * Quickly create new DW-Agents with standard structure
 */

import { promises as fs } from 'fs';
import path from 'path';

interface AgentConfig {
  name: string;
  port: number;
  description: string;
  features: string[];
}

async function createAgent(config: AgentConfig) {
  const agentDir = `/root/DW-Agents/dw-agents`;
  const fileName = `${config.name.toLowerCase().replace(/\s+/g, '-')}-agent.ts`;
  const filePath = path.join(agentDir, fileName);

  const template = `#!/usr/bin/env tsx
/**
 * ${config.name} Agent
 * ${config.description}
 * 
 * Features:
${config.features.map(f => ` * - ${f}`).join('\n')}
 */

import express from 'express';
import { exec } from 'child_process';
import { promises as fs } from 'fs';
import path from 'path';
import { promisify } from 'util';

const execAsync = promisify(exec);
const app = express();
const PORT = process.env.PORT || ${config.port};

// Agent state
let isRunning = false;
let lastUpdate = new Date();
const agentData: any[] = [];

// Middleware
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));

// CORS middleware
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  next();
});

// Routes
app.get('/', (req, res) => {
  res.json({
    agent: '${config.name}',
    status: isRunning ? 'online' : 'offline',
    lastUpdate: lastUpdate.toISOString(),
    port: PORT,
    endpoints: [
      'GET / - This status endpoint',
      'GET /health - Health check',
      'GET /data - Get agent data',
      'POST /action - Trigger agent action',
      'GET /dashboard - HTML dashboard'
    ]
  });
});

app.get('/health', (req, res) => {
  res.json({ 
    status: 'healthy', 
    uptime: process.uptime(),
    memory: process.memoryUsage(),
    timestamp: new Date().toISOString()
  });
});

app.get('/data', (req, res) => {
  res.json({
    data: agentData,
    count: agentData.length,
    lastUpdate: lastUpdate.toISOString()
  });
});

app.post('/action', async (req, res) => {
  try {
    const { action, params } = req.body;
    
    console.log(\`📡 ${config.name} received action: \${action}\`);
    
    // Handle different actions
    switch (action) {
      case 'start':
        await startAgent();
        break;
      case 'stop':
        await stopAgent();
        break;
      case 'refresh':
        await refreshData();
        break;
      default:
        return res.status(400).json({ error: 'Unknown action' });
    }
    
    res.json({ success: true, action, timestamp: new Date().toISOString() });
    
  } catch (error) {
    console.error('❌ Action failed:', error);
    res.status(500).json({ error: error.message });
  }
});

app.get('/dashboard', (req, res) => {
  const html = \`
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>${config.name} Dashboard</title>
  <style>
    body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
    .container { max-width: 1200px; margin: 0 auto; }
    .header { background: #2c3e50; color: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
    .card { background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
    .status { padding: 8px 16px; border-radius: 4px; color: white; }
    .online { background: #27ae60; }
    .offline { background: #e74c3c; }
    button { background: #3498db; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; margin: 5px; }
    button:hover { background: #2980b9; }
    .data-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; }
  </style>
</head>
<body>
  <div class="container">
    <div class="header">
      <h1>🤖 ${config.name}</h1>
      <p>${config.description}</p>
    </div>
    
    <div class="card">
      <h2>Agent Status</h2>
      <span class="status \${isRunning ? 'online' : 'offline'}">
        \${isRunning ? 'ONLINE' : 'OFFLINE'}
      </span>
      <p>Last Update: \${lastUpdate.toLocaleString()}</p>
      <p>Data Points: \${agentData.length}</p>
      
      <div style="margin-top: 20px;">
        <button onclick="triggerAction('start')">Start</button>
        <button onclick="triggerAction('stop')">Stop</button>
        <button onclick="triggerAction('refresh')">Refresh</button>
        <button onclick="location.reload()">Reload Page</button>
      </div>
    </div>
    
    <div class="card">
      <h2>Features</h2>
      <ul>
${config.features.map(f => `        <li>${f}</li>`).join('\n')}
      </ul>
    </div>
    
    <div class="card">
      <h2>Recent Data</h2>
      <div id="data-display">Loading...</div>
    </div>
  </div>
  
  <script>
    async function triggerAction(action) {
      try {
        const response = await fetch('/action', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ action })
        });
        
        if (response.ok) {
          alert(\`Action '\${action}' completed successfully!\`);
          location.reload();
        } else {
          alert(\`Action '\${action}' failed!\`);
        }
      } catch (error) {
        alert(\`Error: \${error.message}\`);
      }
    }
    
    async function loadData() {
      try {
        const response = await fetch('/data');
        const data = await response.json();
        
        const display = document.getElementById('data-display');
        if (data.data.length === 0) {
          display.innerHTML = '<p>No data available</p>';
        } else {
          display.innerHTML = '<pre>' + JSON.stringify(data.data.slice(-5), null, 2) + '</pre>';
        }
      } catch (error) {
        document.getElementById('data-display').innerHTML = \`<p>Error loading data: \${error.message}</p>\`;
      }
    }
    
    loadData();
    setInterval(loadData, 5000); // Refresh every 5 seconds
  </script>
</body>
</html>
  \`;
  
  res.send(html);
});

// Agent functions
async function startAgent() {
  console.log(\`🚀 Starting ${config.name}...\`);
  isRunning = true;
  lastUpdate = new Date();
  
  // Add your agent logic here
  // Example: monitoring, data collection, processing, etc.
}

async function stopAgent() {
  console.log(\`🛑 Stopping ${config.name}...\`);
  isRunning = false;
  lastUpdate = new Date();
}

async function refreshData() {
  console.log(\`🔄 Refreshing ${config.name} data...\`);
  
  try {
    // Add your data refresh logic here
    // Example:
    const newData = {
      timestamp: new Date().toISOString(),
      value: Math.random(),
      status: 'collected'
    };
    
    agentData.push(newData);
    
    // Keep only last 100 records
    if (agentData.length > 100) {
      agentData.shift();
    }
    
    lastUpdate = new Date();
    console.log(\`✅ Data refreshed: \${agentData.length} records\`);
    
  } catch (error) {
    console.error('❌ Data refresh failed:', error);
    throw error;
  }
}

// Auto-refresh data every 60 seconds
setInterval(() => {
  if (isRunning) {
    refreshData().catch(console.error);
  }
}, 60000);

// Start server
app.listen(PORT, () => {
  console.log(\`🤖 ${config.name} running on http://45.61.58.125:\${PORT}\`);
  console.log(\`📊 Dashboard: http://45.61.58.125:\${PORT}/dashboard\`);
  
  // Auto-start the agent
  startAgent().catch(console.error);
});

// Handle graceful shutdown
process.on('SIGTERM', async () => {
  console.log(\`👋 ${config.name} shutting down...\`);
  await stopAgent();
  process.exit(0);
});

process.on('SIGINT', async () => {
  console.log(\`👋 ${config.name} shutting down...\`);
  await stopAgent();
  process.exit(0);
});

export default app;
`;

  await fs.writeFile(filePath, template);
  await fs.chmod(filePath, '755'); // Make executable
  
  console.log(`✅ Created agent: ${filePath}`);
  console.log(`🚀 To run: PORT=${config.port} pm2 start ${fileName} --name "${config.name.toLowerCase().replace(/\s+/g, '-')}" --interpreter npx --interpreter-args tsx`);
  console.log(`🌐 Dashboard: http://45.61.58.125:${config.port}/dashboard`);
  
  return filePath;
}

// Example usage
if (require.main === module) {
  const agentConfig: AgentConfig = {
    name: process.argv[2] || 'Test Agent',
    port: parseInt(process.argv[3]) || 9999,
    description: process.argv[4] || 'A test agent for demonstration purposes',
    features: [
      'Real-time monitoring',
      'Data collection and storage',
      'Web dashboard interface',
      'REST API endpoints',
      'Automated reporting'
    ]
  };
  
  createAgent(agentConfig).then((path) => {
    console.log(`Agent created successfully at: ${path}`);
  });
}

export { createAgent };