diff --git a/deploy/NetworkDiscoveryAgent.ts b/deploy/NetworkDiscoveryAgent.ts new file mode 100644 index 0000000..c223321 --- /dev/null +++ b/deploy/NetworkDiscoveryAgent.ts @@ -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 { + 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); +}); diff --git a/package.json b/package.json index ba904ab..88bc608 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,9 @@ "db:seed": "bun run prisma/seed.ts", "db:studio": "prisma studio", "prepare": "husky install", - "validate": "bun run type-check && bun run lint && bun run test" + "validate": "bun run type-check && bun run lint && bun run test", + "deploy:network-discovery": "bun run deploy/NetworkDiscoveryAgent.ts", + "agent:network-discovery": "bun run deploy/NetworkDiscoveryAgent.ts" }, "dependencies": { "@aws-sdk/client-s3": "^3.937.0",