UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

204 lines (186 loc) 6.3 kB
#!/usr/bin/env node /** * Standalone CLI shim for @claude-flow/plugin-agent-federation. * Maps `ruflo-federation <subcommand> [args]` -> CLICommandDefinition handlers. * * Auth/config via .env (loaded from CWD or any parent): * FEDERATION_NODE_NAME - this node's identity (default: hostname) * FEDERATION_BIND_HOST - bind address (default: 0.0.0.0) * FEDERATION_BIND_PORT - bind port (default: 8443) * FEDERATION_TRUST_LEVEL - default trust tier (default: untrusted) */ import { readFileSync, existsSync } from 'node:fs'; import { resolve } from 'node:path'; import { hostname } from 'node:os'; import { AgentFederationPlugin } from './plugin.js'; import type { PluginContext } from '@claude-flow/shared/src/plugin-interface.js'; function loadDotenv(): { loaded: string | null; count: number } { let dir = process.cwd(); for (let i = 0; i < 8; i++) { const candidate = resolve(dir, '.env'); if (existsSync(candidate)) { const text = readFileSync(candidate, 'utf8'); let count = 0; for (const rawLine of text.split('\n')) { const line = rawLine.trim(); if (!line || line.startsWith('#')) continue; const eq = line.indexOf('='); if (eq < 1) continue; const key = line.slice(0, eq).trim(); let val = line.slice(eq + 1).trim(); if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) { val = val.slice(1, -1); } if (process.env[key] === undefined) { process.env[key] = val; count++; } } return { loaded: candidate, count }; } const parent = resolve(dir, '..'); if (parent === dir) break; dir = parent; } return { loaded: null, count: 0 }; } loadDotenv(); interface ParsedArgs { _: string[]; [k: string]: unknown; } function parseArgs(argv: string[]): ParsedArgs { const out: ParsedArgs = { _: [] }; for (let i = 0; i < argv.length; i++) { const a = argv[i]!; if (a.startsWith('--')) { const eq = a.indexOf('='); if (eq > -1) { out[a.slice(2, eq)] = a.slice(eq + 1); } else { const key = a.slice(2); const next = argv[i + 1]; if (next === undefined || next.startsWith('-')) { out[key] = true; } else { out[key] = next; i++; } } } else if (a.startsWith('-') && a.length > 1) { const key = a.slice(1); const next = argv[i + 1]; if (next === undefined || next.startsWith('-')) { out[key] = true; } else { out[key] = next; i++; } } else { out._.push(a); } } return out; } function aliasShortToLong(args: ParsedArgs): ParsedArgs { const aliases: Record<string, string> = { n: 'name', h: 'host', p: 'port', t: 'token', u: 'url', f: 'format', }; for (const [s, l] of Object.entries(aliases)) { if (s in args && !(l in args)) { args[l] = args[s]; } } return args; } /** * Resolve federation subcommand. Supports multi-word like * `federation peers add`, `federation peers remove`. */ function resolveCommand( argv: string[], commandNames: string[], ): { name: string; rest: string[] } | null { const tokens = argv.slice(); for (const len of [3, 2, 1]) { if (tokens.length < len) continue; const probe = 'federation ' + tokens.slice(0, len).join(' '); if (commandNames.includes(probe)) { return { name: probe, rest: tokens.slice(len) }; } } return null; } function makeContext(): PluginContext { const noop = () => undefined; return { config: { nodeName: process.env.FEDERATION_NODE_NAME ?? hostname(), bindHost: process.env.FEDERATION_BIND_HOST ?? '0.0.0.0', bindPort: Number(process.env.FEDERATION_BIND_PORT ?? 8443), defaultTrustLevel: process.env.FEDERATION_TRUST_LEVEL ?? 'untrusted', }, eventBus: { emit: noop, on: noop, off: noop, once: noop } as unknown as PluginContext['eventBus'], logger: { info: () => {}, warn: (...a: unknown[]) => console.warn('[warn]', ...a), error: (...a: unknown[]) => console.error('[error]', ...a), debug: (...a: unknown[]) => process.env.DEBUG && console.error('[debug]', ...a), } as unknown as PluginContext['logger'], services: { get: () => undefined, register: noop, has: () => false } as unknown as PluginContext['services'], }; } function printHelp(commandNames: string[]): void { console.log('ruflo-federation — Cross-installation agent federation CLI'); console.log(''); console.log('Usage: ruflo-federation <subcommand> [options]'); console.log(''); console.log('Config (via .env or env vars):'); console.log(' FEDERATION_NODE_NAME, FEDERATION_BIND_HOST, FEDERATION_BIND_PORT, FEDERATION_TRUST_LEVEL'); console.log(''); console.log('Subcommands:'); for (const n of commandNames) { console.log(' ' + n.replace(/^federation /, '')); } console.log(''); console.log('Run "ruflo-federation <subcommand> --help" for command-specific options.'); } async function main(): Promise<void> { const argv = process.argv.slice(2); const plugin = new AgentFederationPlugin(); const context = makeContext(); await plugin.initialize(context); const commands = plugin.registerCLICommands(); const commandNames = commands.map((c) => c.name); if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h') { printHelp(commandNames); return; } const resolved = resolveCommand(argv, commandNames); if (!resolved) { console.error('Unknown subcommand: ' + argv.join(' ')); console.error('Run "ruflo-federation --help" to list available subcommands.'); process.exitCode = 1; return; } const cmd = commands.find((c) => c.name === resolved.name)!; const args = aliasShortToLong(parseArgs(resolved.rest)); try { await cmd.handler(args as never); } catch (err) { const msg = err instanceof Error ? err.message : String(err); console.error('Error: ' + msg); process.exitCode = 1; } } main() .then(() => process.exit(process.exitCode ?? 0)) .catch((err) => { console.error('Fatal: ' + (err instanceof Error ? err.message : String(err))); process.exit(1); });