#!/usr/bin/env node // federation-agent/server/registry.js · v1.0 // 服务器端 · Agent 注册管理中心 // 挂载到 gatekeeper 上,管理所有联邦 Agent 节点 // ICE-GL-ZY001 · D129 · 2026-06-10 const WebSocket = require("ws"); const http = require("http"); // ===== 配置 ===== const PORT = process.env.AGENT_PORT || 3912; const AUTH_TOKEN = process.env.AGENT_AUTH || "zy_gtw_federation_agent_v1"; // ===== 注册表 ===== class AgentRegistry { constructor() { this.agents = new Map(); // agentId → { ws, info, status } this.personas = new Map(); // personaId → [agentId, ...] this.users = new Map(); // userId → [agentId, ...] this.commandQueue = new Map(); // agentId → [pending commands] } register(ws, info) { const { agentId, personaId, userId, hostname, platform, workDir } = info; const agent = { ws, info: { agentId, personaId, userId, hostname, platform, workDir, version: info.version || "1.0.0" }, status: "online", connectedAt: new Date().toISOString(), lastHeartbeat: Date.now(), commandCount: 0, }; this.agents.set(agentId, agent); if (personaId) { if (!this.personas.has(personaId)) this.personas.set(personaId, []); this.personas.get(personaId).push(agentId); } if (userId) { if (!this.users.has(userId)) this.users.set(userId, []); this.users.get(userId).push(agentId); } console.log(`[REGISTRY] + ${agentId} (${personaId || "未注册"} @ ${userId || "未知用户"} · ${hostname})`); return agent; } unregister(agentId) { const agent = this.agents.get(agentId); if (!agent) return; this.agents.delete(agentId); if (agent.info.personaId) { const list = this.personas.get(agent.info.personaId); if (list) { const idx = list.indexOf(agentId); if (idx > -1) list.splice(idx, 1); } } if (agent.info.userId) { const list = this.users.get(agent.info.userId); if (list) { const idx = list.indexOf(agentId); if (idx > -1) list.splice(idx, 1); } } console.log(`[REGISTRY] - ${agentId} (断开)`); } sendCommand(agentId, command) { const agent = this.agents.get(agentId); if (!agent || !agent.ws || agent.ws.readyState !== WebSocket.OPEN) { // 加入待发送队列 if (!this.commandQueue.has(agentId)) this.commandQueue.set(agentId, []); this.commandQueue.get(agentId).push(command); return { ok: false, error: "agent offline" }; } agent.ws.send(JSON.stringify({ type: "command", command })); agent.commandCount++; return { ok: true }; } flushQueue(agentId) { const queue = this.commandQueue.get(agentId); if (!queue || queue.length === 0) return 0; let sent = 0; while (queue.length > 0) { const cmd = queue.shift(); const result = this.sendCommand(agentId, cmd); if (result.ok) sent++; else break; } return sent; } broadcast(personaIds, command) { const results = []; for (const personaId of personaIds) { const agentIds = this.personas.get(personaId) || []; for (const agentId of agentIds) { results.push({ agentId, ...this.sendCommand(agentId, command) }); } } return results; } list(target) { if (target === "all") { return Array.from(this.agents.values()).map(a => ({ agentId: a.info.agentId, personaId: a.info.personaId, userId: a.info.userId, hostname: a.info.hostname, status: a.status, commandCount: a.commandCount, connectedAt: a.connectedAt, })); } // 按用户过滤 const agentIds = this.users.get(target) || []; return agentIds.map(id => { const a = this.agents.get(id); return a ? { agentId: a.info.agentId, personaId: a.info.personaId, status: a.status, commandCount: a.commandCount, } : null; }).filter(Boolean); } healthCheck() { const now = Date.now(); for (const [agentId, agent] of this.agents) { if (now - agent.lastHeartbeat > 120000) { agent.status = "timeout"; console.log(`[REGISTRY] ⚠ ${agentId} 心跳超时`); } } } } // ===== WebSocket 服务器 ===== const registry = new AgentRegistry(); const server = http.createServer((req, res) => { // REST API 端点 if (req.url === "/api/agents" && req.method === "GET") { const target = new URL(req.url, "http://localhost").searchParams.get("user") || "all"; res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify(registry.list(target))); return; } if (req.url === "/api/command" && req.method === "POST") { let body = ""; req.on("data", chunk => body += chunk); req.on("end", () => { try { const { agentId, command } = JSON.parse(body); const result = registry.sendCommand(agentId, command); res.writeHead(result.ok ? 200 : 404, { "Content-Type": "application/json" }); res.end(JSON.stringify(result)); } catch (e) { res.writeHead(400, { "Content-Type": "application/json" }); res.end(JSON.stringify({ ok: false, error: e.message })); } }); return; } res.writeHead(404); res.end("Not Found"); }); const wss = new WebSocket.Server({ server, path: "/agent" }); wss.on("connection", (ws, req) => { const url = new URL(req.url, "http://localhost"); const agentId = url.searchParams.get("agentId") || "unknown"; const personaId = url.searchParams.get("persona") || ""; const userId = url.searchParams.get("user") || ""; console.log(`[WS] 新连接: ${agentId}`); // 先不发 welcome,等 register 消息 ws.agentId = agentId; ws.on("message", (data) => { try { const msg = JSON.parse(data.toString()); switch (msg.type) { case "register": { const agent = registry.register(ws, msg); ws.send(JSON.stringify({ type: "welcome", message: `光湖联邦托管 · 注册成功 · ${agent.info.personaId || agentId}`, personaId: agent.info.personaId, timestamp: Date.now(), })); // 发送队列中的命令 const flushed = registry.flushQueue(agentId); if (flushed > 0) console.log(`[REGISTRY] → ${agentId} 补发 ${flushed} 条待处理指令`); break; } case "result": { console.log(`[RESULT] ${agentId}: ${msg.command} → ${msg.ok ? "OK" : "FAIL"}`); if (msg.stderr) console.log(` stderr: ${msg.stderr.substring(0, 200)}`); break; } case "heartbeat": { const agent = registry.agents.get(agentId); if (agent) agent.lastHeartbeat = Date.now(); break; } case "health_report": { console.log(`[HEALTH] ${agentId}: uptime=${(msg.uptime / 60).toFixed(1)}min · mem=${(msg.memory.heapUsed / 1024 / 1024).toFixed(1)}MB`); break; } case "confirm_request": { console.log(`[CONFIRM] ${agentId}: 请求确认危险操作: ${msg.operation}`); break; } default: console.log(`[MSG] ${agentId}: ${msg.type}`); } } catch (e) { console.error(`[ERROR] ${agentId}: ${e.message}`); } }); ws.on("close", () => { registry.unregister(agentId); }); ws.on("error", (err) => { console.error(`[WS ERROR] ${agentId}: ${err.message}`); }); }); // 健康检查定时器 setInterval(() => registry.healthCheck(), 30000); server.listen(PORT, () => { console.log(`[REGISTRY] 光湖联邦 Agent 注册中心 · 端口 ${PORT}`); console.log(`[REGISTRY] WebSocket: ws://0.0.0.0:${PORT}/agent`); console.log(`[REGISTRY] REST API: http://0.0.0.0:${PORT}/api/agents`); }); process.on("SIGINT", () => { console.log("[REGISTRY] 关闭中..."); wss.close(); server.close(); process.exit(0); });