import { ipcMain } from "electron"; import type { HandlerContext } from "@shared/types/ipc"; import { FEATURES } from "@shared/featureFlags"; import { agentService } from "../services/engines/unifiedAgent"; import { mcpProxyManager } from "../services/packages/mcp"; import { getComputerServerStatus } from "../services/computerServer"; import { getGuiAgentServerStatus } from "../services/packages/guiAgentServer"; import { getWindowsMcpStatus } from "../services/packages/windowsMcp"; import { isWindows } from "../services/system/shellEnv"; import { recordDigitalEmployeeSnapshot, recordDigitalEmployeeGovernanceCommand, readDigitalEmployeeRuntimeRecords, readDigitalEmployeeState, readDigitalEmployeeUiState, saveDigitalEmployeeUiState, type DigitalEmployeeManagedService, type DigitalEmployeeGovernanceCommandRecord, type DigitalEmployeeServiceStatus, type DigitalEmployeeSnapshot, type DigitalEmployeeUiStateUpdate, } from "../services/digitalEmployee/stateService"; import { flushDigitalEmployeeSyncOutbox, getDigitalEmployeeSyncStatus, } from "../services/digitalEmployee/syncService"; import { listDigitalEmployeePlanTemplates } from "../services/digitalEmployee/planTemplateService"; import { listDigitalEmployeeSkillCapabilities } from "../services/digitalEmployee/skillCatalogService"; export function registerDigitalEmployeeHandlers(ctx: HandlerContext): void { ipcMain.handle("digitalEmployee:getSnapshot", async () => { const snapshot = buildSnapshot(ctx); const state = recordDigitalEmployeeSnapshot(snapshot); return { ...snapshot, state: { updatedAt: state.updatedAt, events: state.events, }, }; }); ipcMain.handle("digitalEmployee:getState", async () => { return readDigitalEmployeeState(); }); ipcMain.handle("digitalEmployee:getRuntimeRecords", async () => { return readDigitalEmployeeRuntimeRecords(); }); ipcMain.handle("digitalEmployee:getUiState", async () => { return readDigitalEmployeeUiState(); }); ipcMain.handle("digitalEmployee:getSkillCapabilities", async () => { return listDigitalEmployeeSkillCapabilities(); }); ipcMain.handle("digitalEmployee:getPlanTemplates", async () => { return listDigitalEmployeePlanTemplates(); }); ipcMain.handle( "digitalEmployee:saveUiState", async (_, update: DigitalEmployeeUiStateUpdate) => { return saveDigitalEmployeeUiState(update); }, ); ipcMain.handle("digitalEmployee:getSyncStatus", async () => { return getDigitalEmployeeSyncStatus(); }); ipcMain.handle("digitalEmployee:flushSync", async () => { return flushDigitalEmployeeSyncOutbox({ force: true }); }); ipcMain.handle( "digitalEmployee:recordGovernanceCommand", async (_, command: DigitalEmployeeGovernanceCommandRecord) => { return recordDigitalEmployeeGovernanceCommand(command); }, ); } function buildSnapshot(ctx: HandlerContext): DigitalEmployeeSnapshot { const sessions = agentService.listAllSessionsDetailed(); const generatedAt = new Date().toISOString(); const services = { agent: { running: agentService.isReady, engineType: agentService.getEngineType(), }, mcp: normalizeServiceStatus(mcpProxyManager.getStatus()), computerServer: normalizeServiceStatus(getComputerServerStatus()), lanproxy: normalizeServiceStatus(ctx.lanproxy.status()), fileServer: normalizeServiceStatus(ctx.fileServer.status()), guiServer: normalizeServiceStatus(getGuiStatus()), } satisfies Record; return { generatedAt, services, managedServices: buildManagedServices(services, generatedAt), sessions: { total: sessions.length, active: sessions.filter((session) => session.status === "active" || session.status === "pending", ).length, items: sessions.slice(0, 10), }, }; } function buildManagedServices( services: Record, generatedAt: string, ): DigitalEmployeeManagedService[] { return MANAGED_SERVICE_DEFINITIONS.map((definition) => { const status = services[definition.serviceId] ?? { running: false }; const error = status.error ?? null; const running = status.running === true; const state = error ? "degraded" : running ? "running" : "stopped"; return { serviceId: definition.serviceId, name: definition.name, kind: definition.kind, status: state, statusLabel: state === "running" ? "运行中" : state === "degraded" ? "异常" : "未运行", running, error, requiresHumanAction: Boolean(error || (!running && definition.required)), dependentTasks: definition.dependentTasks, restartCount: 0, lastSeenAt: generatedAt, instance: { pid: status.pid, port: status.port, engineType: status.engineType, serverCount: status.serverCount, serverNames: status.serverNames, }, lease: { state: state === "running" ? "active" : state, updatedAt: generatedAt, }, }; }); } const MANAGED_SERVICE_DEFINITIONS: Array<{ serviceId: string; name: string; kind: DigitalEmployeeManagedService["kind"]; required: boolean; dependentTasks: string[]; }> = [ { serviceId: "agent", name: "Agent", kind: "agent", required: true, dependentTasks: ["ACP 会话执行", "Plan Agent", "Task Agent"], }, { serviceId: "mcp", name: "MCP Proxy", kind: "mcp", required: true, dependentTasks: ["Skill Catalog", "MCP 工具调用"], }, { serviceId: "computerServer", name: "Computer Server", kind: "desktop", required: true, dependentTasks: ["/computer/chat", "远程任务接入"], }, { serviceId: "fileServer", name: "File Server", kind: "file", required: false, dependentTasks: ["Artifact 上传", "文件交付"], }, { serviceId: "lanproxy", name: "Lanproxy", kind: "network", required: false, dependentTasks: ["远程访问", "管理端回连"], }, { serviceId: "guiServer", name: "GUI Agent Server", kind: "gui", required: false, dependentTasks: ["GUI 自动化", "桌面操作"], }, ]; function normalizeServiceStatus( value: object | null | undefined, ): DigitalEmployeeServiceStatus { if (!value) return { running: false }; const record = value as Record; return { running: record.running === true, error: typeof record.error === "string" ? record.error : undefined, engineType: typeof record.engineType === "string" ? record.engineType : undefined, port: typeof record.port === "number" ? record.port : undefined, serverCount: typeof record.serverCount === "number" ? record.serverCount : undefined, serverNames: Array.isArray(record.serverNames) ? record.serverNames.filter((item): item is string => typeof item === "string") : undefined, pid: typeof record.pid === "number" ? record.pid : undefined, }; } function getGuiStatus(): object { if (!FEATURES.ENABLE_GUI_AGENT_SERVER) { return { running: false, error: "GUI Agent Server is not available" }; } return isWindows() ? getWindowsMcpStatus() : getGuiAgentServerStatus(); }