#!/usr/bin/env node /** * Federation Hub CLI - Manage ephemeral agent federation * Supports hub server, agent lifecycle, stats, and monitoring */ import { existsSync } from 'fs'; import { resolve, dirname } from 'path'; import { fileURLToPath } from 'url'; import { spawn } from 'child_process'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); /** * Federation Hub CLI Manager */ export class FederationCLI { hubProcess = null; /** * Start federation hub server */ async startHub(config = {}) { const port = config.port || parseInt(process.env.FEDERATION_HUB_PORT || '8443'); const dbPath = config.dbPath || process.env.FEDERATION_DB_PATH || ':memory:'; const maxAgents = config.maxAgents || parseInt(process.env.FEDERATION_MAX_AGENTS || '1000'); console.log('\n🌐 Starting Federation Hub Server...'); console.log('═'.repeat(60)); console.log(`πŸ“ Port: ${port}`); console.log(`πŸ’Ύ Database: ${dbPath === ':memory:' ? 'In-Memory' : dbPath}`); console.log(`πŸ‘₯ Max Agents: ${maxAgents}`); console.log(`πŸ”’ Protocol: WebSocket (QUIC support planned)`); console.log(''); // Start hub server const hubPath = resolve(__dirname, '../federation/run-hub.js'); // Check if compiled hub exists if (!existsSync(hubPath)) { console.error(`❌ Error: Hub server not found at ${hubPath}`); console.error(' Please build the project first: npm run build'); process.exit(1); } this.hubProcess = spawn('node', [hubPath], { stdio: 'inherit', env: { ...process.env, FEDERATION_HUB_PORT: port.toString(), FEDERATION_DB_PATH: dbPath, FEDERATION_MAX_AGENTS: maxAgents.toString(), DEBUG: config.verbose ? 'federation:*' : undefined } }); this.hubProcess.on('exit', (code) => { console.log(`\nπŸ‘‹ Hub server stopped (exit code: ${code})`); process.exit(code || 0); }); // Handle signals process.on('SIGINT', () => { console.log('\n\n⏸️ Shutting down hub server...'); if (this.hubProcess) { this.hubProcess.kill('SIGINT'); } }); process.on('SIGTERM', () => { if (this.hubProcess) { this.hubProcess.kill('SIGTERM'); } }); console.log('βœ… Hub server started successfully!'); console.log('\nAgent Connection:'); console.log(` Endpoint: ws://localhost:${port}`); console.log(` Protocol: WebSocket (HTTP/2 upgrade)`); console.log('\nUse Ctrl+C to stop the server\n'); // Keep alive await new Promise(() => { }); } /** * Spawn ephemeral agent */ async spawnAgent(config = {}) { const agentId = config.agentId || `agent-${Date.now()}`; const tenantId = config.tenantId || process.env.FEDERATION_TENANT_ID || 'default'; const lifetime = config.lifetime || parseInt(process.env.AGENT_LIFETIME || '300'); // 5 minutes default const hubEndpoint = config.hubEndpoint || process.env.FEDERATION_HUB_ENDPOINT || 'ws://localhost:8443'; const agentType = config.agentType || 'worker'; console.log('\nπŸ€– Spawning Ephemeral Agent...'); console.log('═'.repeat(60)); console.log(`πŸ†” Agent ID: ${agentId}`); console.log(`🏒 Tenant: ${tenantId}`); console.log(`⏱️ Lifetime: ${lifetime}s`); console.log(`πŸ”— Hub: ${hubEndpoint}`); console.log(`πŸ“‹ Type: ${agentType}`); console.log(''); // Spawn agent process const agentPath = resolve(__dirname, '../federation/run-agent.js'); if (!existsSync(agentPath)) { console.error(`❌ Error: Agent runtime not found at ${agentPath}`); console.error(' Please build the project first: npm run build'); process.exit(1); } const agentProcess = spawn('node', [agentPath], { stdio: 'inherit', env: { ...process.env, AGENT_ID: agentId, TENANT_ID: tenantId, AGENT_LIFETIME: lifetime.toString(), HUB_ENDPOINT: hubEndpoint, AGENT_TYPE: agentType } }); agentProcess.on('exit', (code) => { console.log(`\nπŸ“Š Agent lifecycle complete (exit code: ${code})`); process.exit(code || 0); }); process.on('SIGINT', () => { console.log('\n\n⏸️ Terminating agent...'); agentProcess.kill('SIGINT'); }); process.on('SIGTERM', () => { agentProcess.kill('SIGTERM'); }); } /** * Show hub statistics */ async stats(hubEndpoint) { const endpoint = hubEndpoint || process.env.FEDERATION_HUB_ENDPOINT || 'ws://localhost:8443'; console.log('\nπŸ“Š Federation Hub Statistics'); console.log('═'.repeat(60)); console.log(`πŸ”— Hub: ${endpoint}`); console.log(''); try { // TODO: Implement WebSocket stats query // For now, show placeholder console.log('⏳ Querying hub statistics...\n'); console.log('Note: Stats API not yet implemented.'); console.log('The hub server logs real-time statistics to stdout.'); console.log('\nExpected stats:'); console.log(' β€’ Connected agents count'); console.log(' β€’ Total episodes stored'); console.log(' β€’ Active tenants'); console.log(' β€’ Uptime and performance metrics'); console.log(''); } catch (error) { console.error(`❌ Failed to query stats: ${error.message}`); process.exit(1); } } /** * Show federation status */ async status() { console.log('\nπŸ” Federation System Status'); console.log('═'.repeat(60)); console.log(''); console.log('Components:'); console.log(' βœ… FederationHubServer - WebSocket hub for agent sync'); console.log(' βœ… FederationHubClient - WebSocket client for agents'); console.log(' βœ… EphemeralAgent - Short-lived agent lifecycle'); console.log(' βœ… SecurityManager - JWT authentication & encryption'); console.log(' βœ… AgentDB Integration - Vector memory storage (150x faster)'); console.log(''); console.log('Features:'); console.log(' βœ… Tenant Isolation - Multi-tenant memory separation'); console.log(' βœ… Persistent Hub - SQLite + AgentDB storage'); console.log(' βœ… Ephemeral Agents - :memory: databases (5s-15min lifetime)'); console.log(' βœ… Semantic Search - HNSW vector indexing'); console.log(' βœ… Multi-Generation - Agents learn from past agents'); console.log(' ⏳ QUIC Transport - Native QUIC planned (WebSocket fallback)'); console.log(''); console.log('Architecture:'); console.log(' β€’ Hub: Persistent central database (disk)'); console.log(' β€’ Agents: Ephemeral local databases (RAM)'); console.log(' β€’ Sync: Real-time via WebSocket'); console.log(' β€’ Memory: Outlives agent lifecycle'); console.log(''); console.log('Documentation:'); console.log(' πŸ“– Architecture: docs/architecture/FEDERATED-AGENTDB-EPHEMERAL-AGENTS.md'); console.log(' πŸ“– Data Lifecycle: docs/architecture/FEDERATION-DATA-LIFECYCLE.md'); console.log(' πŸ“– Test Report: docs/architecture/FEDERATION-TEST-REPORT.md'); console.log(' πŸ“– Integration: docs/architecture/AGENTDB-INTEGRATION-COMPLETE.md'); console.log(''); } /** * Run multi-agent collaboration test */ async testCollaboration() { console.log('\nπŸ§ͺ Running Multi-Agent Collaboration Test...'); console.log('═'.repeat(60)); console.log(''); const testPath = resolve(__dirname, '../../tests/federation/test-agentdb-collaboration.js'); if (!existsSync(testPath)) { console.error(`❌ Error: Test not found at ${testPath}`); console.error(' Please build the project first: npm run build'); process.exit(1); } console.log('πŸ“‹ Test Scenario:'); console.log(' β€’ 5 collaborative agents (researcher, coder, tester, reviewer, isolated)'); console.log(' β€’ Real AgentDB integration'); console.log(' β€’ Cross-agent memory sharing'); console.log(' β€’ Tenant isolation validation'); console.log(''); const testProcess = spawn('node', [testPath], { stdio: 'inherit' }); testProcess.on('exit', (code) => { if (code === 0) { console.log('\nβœ… Collaboration test passed!'); } else { console.log(`\n❌ Collaboration test failed (exit code: ${code})`); } process.exit(code || 0); }); process.on('SIGINT', () => { console.log('\n\n⏸️ Terminating test...'); testProcess.kill('SIGINT'); }); process.on('SIGTERM', () => { testProcess.kill('SIGTERM'); }); } /** * Print help message */ printHelp() { console.log(` 🌐 Federation Hub CLI - Ephemeral Agent Management USAGE: npx agentic-flow federation [options] COMMANDS: start Start federation hub server spawn Spawn ephemeral agent stats Show hub statistics status Show federation system status test Run multi-agent collaboration test help Show this help message HUB SERVER OPTIONS: --port, -p Hub server port [default: 8443] --db-path Database path [default: :memory:] --max-agents Maximum concurrent agents [default: 1000] --verbose, -v Enable verbose logging AGENT OPTIONS: --agent-id Custom agent ID [default: auto-generated] --tenant Tenant ID [default: 'default'] --lifetime Agent lifetime [default: 300] --hub Hub WebSocket endpoint [default: ws://localhost:8443] --type Agent type [default: 'worker'] ENVIRONMENT VARIABLES: FEDERATION_HUB_PORT Hub server port (default: 8443) FEDERATION_DB_PATH Database path (default: :memory:) FEDERATION_MAX_AGENTS Max concurrent agents (default: 1000) FEDERATION_TENANT_ID Default tenant ID FEDERATION_HUB_ENDPOINT Hub WebSocket endpoint AGENT_LIFETIME Agent lifetime in seconds (default: 300) DEBUG OPTIONS (for detailed operation visibility): DEBUG_LEVEL Debug verbosity level β€’ SILENT (0) - No output β€’ BASIC (1) - Major events only [default] β€’ DETAILED (2) - Include all operations with timing β€’ VERBOSE (3) - All events + realtime + tasks β€’ TRACE (4) - Everything + internal state changes DEBUG_FORMAT Output format (human | json | compact) [default: human] DEBUG_OUTPUT Output destination (console | file | both) [default: console] DEBUG_OUTPUT_FILE File path for debug output [default: none] DEBUG EXAMPLES: # Enable detailed debug with timing DEBUG_LEVEL=DETAILED npx agentic-flow federation start # Maximum verbosity for troubleshooting DEBUG_LEVEL=TRACE DEBUG_FORMAT=human npx agentic-flow federation spawn # Production monitoring with JSON output to file DEBUG_LEVEL=BASIC DEBUG_FORMAT=json DEBUG_OUTPUT=file \\ DEBUG_OUTPUT_FILE=/var/log/federation.log npx agentic-flow federation start # Compact format for log aggregation DEBUG_LEVEL=DETAILED DEBUG_FORMAT=compact DEBUG_OUTPUT=both \\ DEBUG_OUTPUT_FILE=debug.log npx agentic-flow federation start EXAMPLES: # Start hub server (in-memory) npx agentic-flow federation start # Start hub with persistent storage npx agentic-flow federation start --db-path ./data/hub.db # Start hub on custom port npx agentic-flow federation start --port 9443 --verbose # Spawn ephemeral agent (5 minute lifetime) npx agentic-flow federation spawn --tenant acme-corp # Spawn agent with custom lifetime npx agentic-flow federation spawn --tenant acme-corp --lifetime 600 --type researcher # Show hub statistics npx agentic-flow federation stats # Show system status npx agentic-flow federation status # Run collaboration test npx agentic-flow federation test ARCHITECTURE: Hub: Persistent central database (SQLite + AgentDB) β€’ Episode metadata storage β€’ Vector memory with HNSW indexing (150x faster) β€’ Tenant isolation via sessionId prefixes β€’ Change log for synchronization Agents: Ephemeral local databases (:memory:) β€’ Short-lived (5 seconds to 15 minutes) β€’ Pull memories from hub on spawn β€’ Push new memories to hub during work β€’ Destroyed on cleanup (memory persists in hub) Memory Persistence: β€’ Hub database outlives all agents β€’ New agents can access memories from dead agents β€’ Multi-generation learning enabled β€’ Semantic search for pattern discovery BENEFITS: βœ… Memory outlives agents - Persistent learning across generations βœ… Tenant isolation - Multi-tenant with zero data leakage βœ… Semantic search - Find similar patterns via vector similarity βœ… 150x faster search - HNSW indexing vs brute force βœ… Scalable architecture - Ready for 100+ concurrent agents βœ… WebSocket protocol - Real-time synchronization βœ… Zero-trust security - JWT auth + AES-256 encryption DOCUMENTATION: πŸ“– Complete Architecture: docs/architecture/FEDERATED-AGENTDB-EPHEMERAL-AGENTS.md πŸ“– Data Lifecycle Explanation: docs/architecture/FEDERATION-DATA-LIFECYCLE.md πŸ“– Multi-Agent Test Report: docs/architecture/FEDERATION-TEST-REPORT.md πŸ“– AgentDB Integration: docs/architecture/AGENTDB-INTEGRATION-COMPLETE.md πŸ“– GitHub: https://github.com/ruvnet/agentic-flow `); } } /** * CLI command handler */ export async function handleFederationCommand(args) { const command = args[0]; const cli = new FederationCLI(); // Parse options const parseOptions = (args) => { const options = {}; for (let i = 0; i < args.length; i++) { if ((args[i] === '--port' || args[i] === '-p') && args[i + 1]) { options.port = parseInt(args[++i]); } else if (args[i] === '--db-path' && args[i + 1]) { options.dbPath = args[++i]; } else if (args[i] === '--max-agents' && args[i + 1]) { options.maxAgents = parseInt(args[++i]); } else if (args[i] === '--verbose' || args[i] === '-v') { options.verbose = true; } else if (args[i] === '--agent-id' && args[i + 1]) { options.agentId = args[++i]; } else if (args[i] === '--tenant' && args[i + 1]) { options.tenantId = args[++i]; } else if (args[i] === '--lifetime' && args[i + 1]) { options.lifetime = parseInt(args[++i]); } else if (args[i] === '--hub' && args[i + 1]) { options.hubEndpoint = args[++i]; } else if (args[i] === '--type' && args[i + 1]) { options.agentType = args[++i]; } } return options; }; const options = parseOptions(args.slice(1)); switch (command) { case undefined: case 'help': cli.printHelp(); break; case 'start': await cli.startHub(options); break; case 'spawn': await cli.spawnAgent(options); break; case 'stats': await cli.stats(options.hubEndpoint); break; case 'status': await cli.status(); break; case 'test': await cli.testCollaboration(); break; default: console.log(`\n❌ Unknown command: ${command}\n`); console.log('Use "npx agentic-flow federation help" for usage information\n'); process.exit(1); } } // If run directly if (import.meta.url === `file://${process.argv[1]}`) { const args = process.argv.slice(2); handleFederationCommand(args).catch((error) => { console.error('\n❌ Error:', error.message); console.error(error.stack); process.exit(1); }); } //# sourceMappingURL=federation-cli.js.map