Files
tasq/node_modules/agentic-flow/dist/cli/federation-cli.js
T
2026-04-09 19:01:53 +08:00

432 lines
17 KiB
JavaScript

#!/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 <command> [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 <port> Hub server port [default: 8443]
--db-path <path> Database path [default: :memory:]
--max-agents <number> Maximum concurrent agents [default: 1000]
--verbose, -v Enable verbose logging
AGENT OPTIONS:
--agent-id <id> Custom agent ID [default: auto-generated]
--tenant <id> Tenant ID [default: 'default']
--lifetime <seconds> Agent lifetime [default: 300]
--hub <endpoint> Hub WebSocket endpoint [default: ws://localhost:8443]
--type <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