Add NetworkDiscoveryAgent deployment script (Agent 8)
Create standalone deployment entry point for the Network Discovery Agent that maps and analyzes geographic distribution of the plant network. Includes status reporting, graceful shutdown, and configurable options.
This commit is contained in:
parent
e76550e73a
commit
5e8ae1e259
2 changed files with 266 additions and 1 deletions
263
deploy/NetworkDiscoveryAgent.ts
Normal file
263
deploy/NetworkDiscoveryAgent.ts
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
#!/usr/bin/env bun
|
||||
/**
|
||||
* NetworkDiscoveryAgent Deployment Script
|
||||
* Agent 8 - Geographic Network Discovery and Analysis
|
||||
*
|
||||
* This script provides standalone deployment for the NetworkDiscoveryAgent,
|
||||
* which maps and analyzes the geographic distribution of the plant network.
|
||||
*
|
||||
* Responsibilities:
|
||||
* - Map plant distribution across regions
|
||||
* - Identify network hotspots and clusters
|
||||
* - Suggest grower/consumer connections
|
||||
* - Track network growth patterns
|
||||
* - Detect coverage gaps
|
||||
*
|
||||
* Usage:
|
||||
* bun run deploy/NetworkDiscoveryAgent.ts
|
||||
* bun run deploy:network-discovery
|
||||
*
|
||||
* Environment Variables:
|
||||
* AGENT_INTERVAL_MS - Execution interval (default: 600000 = 10 min)
|
||||
* AGENT_LOG_LEVEL - Log level: debug, info, warn, error (default: info)
|
||||
* AGENT_AUTO_RESTART - Auto-restart on failure (default: true)
|
||||
* AGENT_MAX_RETRIES - Max retry attempts (default: 3)
|
||||
*/
|
||||
|
||||
import { getNetworkDiscoveryAgent, NetworkDiscoveryAgent } from '../lib/agents/NetworkDiscoveryAgent';
|
||||
|
||||
// Configuration from environment
|
||||
const config = {
|
||||
intervalMs: parseInt(process.env.AGENT_INTERVAL_MS || '600000'),
|
||||
logLevel: process.env.AGENT_LOG_LEVEL || 'info',
|
||||
autoRestart: process.env.AGENT_AUTO_RESTART !== 'false',
|
||||
maxRetries: parseInt(process.env.AGENT_MAX_RETRIES || '3'),
|
||||
};
|
||||
|
||||
// Logger utility
|
||||
const log = {
|
||||
debug: (...args: any[]) => config.logLevel === 'debug' && console.log('[DEBUG]', ...args),
|
||||
info: (...args: any[]) => ['debug', 'info'].includes(config.logLevel) && console.log('[INFO]', ...args),
|
||||
warn: (...args: any[]) => ['debug', 'info', 'warn'].includes(config.logLevel) && console.warn('[WARN]', ...args),
|
||||
error: (...args: any[]) => console.error('[ERROR]', ...args),
|
||||
};
|
||||
|
||||
/**
|
||||
* Format uptime as human-readable string
|
||||
*/
|
||||
function formatUptime(ms: number): string {
|
||||
const seconds = Math.floor(ms / 1000);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const days = Math.floor(hours / 24);
|
||||
|
||||
if (days > 0) return `${days}d ${hours % 24}h ${minutes % 60}m`;
|
||||
if (hours > 0) return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
||||
if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
|
||||
return `${seconds}s`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display agent status
|
||||
*/
|
||||
function displayStatus(agent: NetworkDiscoveryAgent): void {
|
||||
const metrics = agent.getMetrics();
|
||||
const analysis = agent.getNetworkAnalysis();
|
||||
const clusters = agent.getClusters();
|
||||
const gaps = agent.getCoverageGaps();
|
||||
const suggestions = agent.getConnectionSuggestions();
|
||||
const growth = agent.getGrowthHistory();
|
||||
const regions = agent.getRegionalStats();
|
||||
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log(' NETWORK DISCOVERY AGENT - STATUS REPORT');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
// Agent Metrics
|
||||
console.log('\n AGENT METRICS');
|
||||
console.log(' ' + '-'.repeat(40));
|
||||
console.log(` Status: ${agent.status}`);
|
||||
console.log(` Uptime: ${formatUptime(metrics.uptime)}`);
|
||||
console.log(` Tasks Completed: ${metrics.tasksCompleted}`);
|
||||
console.log(` Tasks Failed: ${metrics.tasksFailed}`);
|
||||
console.log(` Avg Execution: ${Math.round(metrics.averageExecutionMs)}ms`);
|
||||
console.log(` Last Run: ${metrics.lastRunAt || 'Never'}`);
|
||||
|
||||
// Network Analysis
|
||||
console.log('\n NETWORK ANALYSIS');
|
||||
console.log(' ' + '-'.repeat(40));
|
||||
console.log(` Total Nodes: ${analysis.totalNodes}`);
|
||||
console.log(` Connections: ${analysis.totalConnections}`);
|
||||
console.log(` Clusters: ${clusters.length}`);
|
||||
console.log(` Hotspots: ${analysis.hotspots.length}`);
|
||||
console.log(` Coverage Gaps: ${gaps.length}`);
|
||||
console.log(` Suggestions: ${suggestions.length}`);
|
||||
|
||||
// Cluster Details
|
||||
if (clusters.length > 0) {
|
||||
console.log('\n TOP CLUSTERS');
|
||||
console.log(' ' + '-'.repeat(40));
|
||||
const topClusters = clusters.slice(0, 5);
|
||||
for (const cluster of topClusters) {
|
||||
console.log(` - ${cluster.activityLevel.toUpperCase()} activity cluster`);
|
||||
console.log(` Nodes: ${cluster.nodes.length}, Radius: ${cluster.radius}km`);
|
||||
if (cluster.dominantSpecies.length > 0) {
|
||||
console.log(` Species: ${cluster.dominantSpecies.slice(0, 3).join(', ')}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Coverage Gaps
|
||||
if (gaps.length > 0) {
|
||||
console.log('\n COVERAGE GAPS');
|
||||
console.log(' ' + '-'.repeat(40));
|
||||
for (const gap of gaps.slice(0, 3)) {
|
||||
console.log(` - ${gap.populationDensity.toUpperCase()} area`);
|
||||
console.log(` Distance to nearest: ${gap.distanceToNearest}km`);
|
||||
console.log(` Potential demand: ${gap.potentialDemand}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Top Suggestions
|
||||
if (suggestions.length > 0) {
|
||||
console.log('\n TOP CONNECTION SUGGESTIONS');
|
||||
console.log(' ' + '-'.repeat(40));
|
||||
for (const suggestion of suggestions.slice(0, 3)) {
|
||||
console.log(` - Strength: ${suggestion.strength}%`);
|
||||
console.log(` Distance: ${suggestion.distance}km`);
|
||||
console.log(` Reason: ${suggestion.reason}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Regional Stats
|
||||
if (regions.length > 0) {
|
||||
console.log('\n REGIONAL STATISTICS');
|
||||
console.log(' ' + '-'.repeat(40));
|
||||
for (const region of regions) {
|
||||
if (region.nodeCount > 0) {
|
||||
console.log(` ${region.region}:`);
|
||||
console.log(` Nodes: ${region.nodeCount}, Plants: ${region.plantCount}`);
|
||||
console.log(` Species: ${region.uniqueSpecies}, Activity: ${region.avgActivityScore}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Growth Trend
|
||||
if (growth.length > 0) {
|
||||
const latest = growth[growth.length - 1];
|
||||
console.log('\n NETWORK GROWTH');
|
||||
console.log(' ' + '-'.repeat(40));
|
||||
console.log(` Total Nodes: ${latest.totalNodes}`);
|
||||
console.log(` Total Connections: ${latest.totalConnections}`);
|
||||
console.log(` New Nodes/Week: ${latest.newNodesWeek}`);
|
||||
console.log(` Geographic Span: ${latest.geographicExpansion}km`);
|
||||
}
|
||||
|
||||
// Alerts
|
||||
const alerts = agent.getAlerts();
|
||||
const unacknowledged = alerts.filter(a => !a.acknowledged);
|
||||
if (unacknowledged.length > 0) {
|
||||
console.log('\n ACTIVE ALERTS');
|
||||
console.log(' ' + '-'.repeat(40));
|
||||
for (const alert of unacknowledged.slice(0, 5)) {
|
||||
console.log(` [${alert.severity.toUpperCase()}] ${alert.title}`);
|
||||
console.log(` ${alert.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n' + '='.repeat(60));
|
||||
}
|
||||
|
||||
/**
|
||||
* Main deployment function
|
||||
*/
|
||||
async function deploy(): Promise<void> {
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log(' DEPLOYING NETWORK DISCOVERY AGENT (Agent 8)');
|
||||
console.log('='.repeat(60));
|
||||
console.log(`\n Configuration:`);
|
||||
console.log(` - Interval: ${config.intervalMs}ms (${config.intervalMs / 60000} min)`);
|
||||
console.log(` - Log Level: ${config.logLevel}`);
|
||||
console.log(` - Auto Restart: ${config.autoRestart}`);
|
||||
console.log(` - Max Retries: ${config.maxRetries}`);
|
||||
console.log('');
|
||||
|
||||
// Get agent instance
|
||||
const agent = getNetworkDiscoveryAgent();
|
||||
|
||||
// Register event handlers
|
||||
agent.on('task_completed', (data) => {
|
||||
log.info(`Task completed: ${JSON.stringify(data.result)}`);
|
||||
});
|
||||
|
||||
agent.on('task_failed', (data) => {
|
||||
log.error(`Task failed: ${data.error}`);
|
||||
});
|
||||
|
||||
agent.on('agent_started', () => {
|
||||
log.info('Network Discovery Agent started');
|
||||
});
|
||||
|
||||
agent.on('agent_stopped', () => {
|
||||
log.info('Network Discovery Agent stopped');
|
||||
});
|
||||
|
||||
// Start the agent
|
||||
log.info('Starting Network Discovery Agent...');
|
||||
|
||||
try {
|
||||
await agent.start();
|
||||
log.info('Agent started successfully');
|
||||
|
||||
// Run initial discovery
|
||||
log.info('Running initial network discovery...');
|
||||
await agent.runOnce();
|
||||
log.info('Initial discovery complete');
|
||||
|
||||
// Display initial status
|
||||
displayStatus(agent);
|
||||
|
||||
// Set up periodic status display
|
||||
const statusInterval = setInterval(() => {
|
||||
displayStatus(agent);
|
||||
}, config.intervalMs);
|
||||
|
||||
// Handle shutdown signals
|
||||
const shutdown = async (signal: string) => {
|
||||
log.info(`Received ${signal}, shutting down...`);
|
||||
clearInterval(statusInterval);
|
||||
|
||||
try {
|
||||
await agent.stop();
|
||||
log.info('Agent stopped gracefully');
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
log.error('Error during shutdown:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
process.on('SIGINT', () => shutdown('SIGINT'));
|
||||
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
||||
|
||||
// Keep the process running
|
||||
log.info(`Agent running. Press Ctrl+C to stop.`);
|
||||
log.info(`Next discovery in ${config.intervalMs / 60000} minutes...`);
|
||||
|
||||
} catch (error) {
|
||||
log.error('Failed to start agent:', error);
|
||||
|
||||
if (config.autoRestart) {
|
||||
log.info('Auto-restart enabled, retrying in 10 seconds...');
|
||||
setTimeout(() => deploy(), 10000);
|
||||
} else {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run deployment
|
||||
deploy().catch((error) => {
|
||||
console.error('Deployment failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -15,7 +15,9 @@
|
|||
"cy:open": "cypress open",
|
||||
"cy:run": "cypress run",
|
||||
"test:e2e": "start-server-and-test 'bun run preview' http://localhost:3001 cy:open",
|
||||
"test:e2e:ci": "start-server-and-test 'bun run preview' http://localhost:3001 cy:run"
|
||||
"test:e2e:ci": "start-server-and-test 'bun run preview' http://localhost:3001 cy:run",
|
||||
"deploy:network-discovery": "bun run deploy/NetworkDiscoveryAgent.ts",
|
||||
"agent:network-discovery": "bun run deploy/NetworkDiscoveryAgent.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/forms": "^0.4.0",
|
||||
|
|
|
|||
Loading…
Reference in a new issue