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

199 lines (173 loc) 6.11 kB
import { FederationNode, type FederationNodeProps } from '../entities/federation-node.js'; import { TrustLevel } from '../entities/trust-level.js'; import type { WgManifestSection } from '../value-objects/wg-config.js'; export type DiscoveryMechanism = 'static' | 'dns-sd' | 'ipfs-registry'; export interface FederationManifest { readonly nodeId: string; readonly publicKey: string; readonly endpoint: string; readonly capabilities: { readonly agentTypes: readonly string[]; readonly maxConcurrentSessions: number; readonly supportedProtocols: readonly string[]; readonly complianceModes: readonly string[]; }; readonly version: string; readonly signature: string; readonly timestamp: string; /** * ADR-111 — optional WG mesh identity. Present only when the publishing * node has opted into the in-tree WG layer (`config.wgMesh: true`). * The Ed25519 manifest signature covers this block too — peers verifying * the manifest also verify the WG-key binding. */ readonly wg?: WgManifestSection; } export interface DiscoveryServiceDeps { signManifest: (manifest: Omit<FederationManifest, 'signature'>) => Promise<string>; verifyManifest: (manifest: FederationManifest) => Promise<boolean>; onPeerDiscovered?: (node: FederationNode) => void; } export interface DiscoveryConfig { readonly staticPeers: readonly string[]; readonly enableDnsSd: boolean; readonly enableIpfsRegistry: boolean; readonly ipfsRegistryCid?: string; readonly discoveryIntervalMs: number; readonly staleThresholdMs: number; } const DEFAULT_DISCOVERY_CONFIG: DiscoveryConfig = { staticPeers: [], enableDnsSd: false, enableIpfsRegistry: false, discoveryIntervalMs: 60_000, staleThresholdMs: 300_000, }; export class DiscoveryService { private readonly deps: DiscoveryServiceDeps; private readonly config: DiscoveryConfig; private readonly knownPeers: Map<string, FederationNode>; private localManifest: FederationManifest | null; private discoveryTimer: ReturnType<typeof setInterval> | null; constructor(deps: DiscoveryServiceDeps, config?: Partial<DiscoveryConfig>) { this.deps = deps; this.config = { ...DEFAULT_DISCOVERY_CONFIG, ...config }; this.knownPeers = new Map(); this.localManifest = null; this.discoveryTimer = null; } async publishManifest(manifest: Omit<FederationManifest, 'signature'>): Promise<FederationManifest> { const signature = await this.deps.signManifest(manifest); this.localManifest = { ...manifest, signature }; return this.localManifest; } getLocalManifest(): FederationManifest | null { return this.localManifest; } async discoverPeers(): Promise<FederationNode[]> { const discovered: FederationNode[] = []; for (const endpoint of this.config.staticPeers) { const existing = this.findByEndpoint(endpoint); if (!existing) { const node = FederationNode.create({ nodeId: `static-${this.hashEndpoint(endpoint)}`, publicKey: '', endpoint, capabilities: { agentTypes: [], maxConcurrentSessions: 1, supportedProtocols: ['websocket', 'http'], complianceModes: [], }, metadata: { discoveryMechanism: 'static' }, }); this.knownPeers.set(node.nodeId, node); discovered.push(node); this.deps.onPeerDiscovered?.(node); } } return discovered; } async addStaticPeer(endpoint: string, manifest?: FederationManifest): Promise<FederationNode> { if (manifest) { const valid = await this.deps.verifyManifest(manifest); if (!valid) { throw new Error(`Invalid manifest signature for endpoint: ${endpoint}`); } } const nodeId = manifest?.nodeId ?? `static-${this.hashEndpoint(endpoint)}`; const existing = this.knownPeers.get(nodeId); if (existing) { existing.markSeen(); return existing; } const node = FederationNode.create({ nodeId, publicKey: manifest?.publicKey ?? '', endpoint, capabilities: manifest?.capabilities ?? { agentTypes: [], maxConcurrentSessions: 1, supportedProtocols: ['websocket', 'http'], complianceModes: [], }, trustLevel: manifest ? TrustLevel.VERIFIED : TrustLevel.UNTRUSTED, metadata: { discoveryMechanism: 'static' as DiscoveryMechanism, version: manifest?.version, }, }); this.knownPeers.set(nodeId, node); this.deps.onPeerDiscovered?.(node); return node; } removePeer(nodeId: string): boolean { return this.knownPeers.delete(nodeId); } getPeer(nodeId: string): FederationNode | undefined { return this.knownPeers.get(nodeId); } listPeers(): FederationNode[] { return Array.from(this.knownPeers.values()); } listActivePeers(): FederationNode[] { return this.listPeers().filter(p => !p.isStale(this.config.staleThresholdMs)); } pruneStale(): string[] { const pruned: string[] = []; for (const [nodeId, node] of this.knownPeers) { if (node.isStale(this.config.staleThresholdMs)) { this.knownPeers.delete(nodeId); pruned.push(nodeId); } } return pruned; } startPeriodicDiscovery(): void { if (this.discoveryTimer) return; this.discoveryTimer = setInterval(() => { this.discoverPeers().catch(() => {}); }, this.config.discoveryIntervalMs); } stopPeriodicDiscovery(): void { if (this.discoveryTimer) { clearInterval(this.discoveryTimer); this.discoveryTimer = null; } } private findByEndpoint(endpoint: string): FederationNode | undefined { for (const node of this.knownPeers.values()) { if (node.endpoint === endpoint) return node; } return undefined; } private hashEndpoint(endpoint: string): string { let hash = 0; for (let i = 0; i < endpoint.length; i++) { const char = endpoint.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash |= 0; } return Math.abs(hash).toString(36); } }