UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

274 lines (273 loc) • 12.1 kB
import { l as normalizeOptionalString } from "./string-coerce-6kR-86XC.mjs"; import { t as formatErrorMessage } from "./errors-BSdPMFao.mjs"; import { Bn as validateNodePluginToolsUpdateParams, En as validateNodeDescribeParams, Mn as validateNodeListParams, Un as validateNodeSkillsUpdateParams } from "./src-D2gvuSyP.mjs"; import { t as ErrorCodes } from "./gateway-error-details-DIefe7Zy.mjs"; import { f as errorShape } from "./error-codes-DI20vDEB.mjs"; import { i as listDevicePairing } from "./device-pairing-BGCnp3WV.mjs"; import { i as recordRemoteNodeInfo, o as refreshRemoteNodeBins, u as updatePairedNodeSessionHost } from "./remote-DEzwkkt9.mjs"; import { n as projectNodePairing } from "./device-pairing-node.records-Bnhzp9L1.mjs"; import { a as listNodePairing } from "./device-pairing-node-CcQMIt42.mjs"; import { a as formatNodeRunnerInventoryIssue, n as NODE_RUNNER_UPDATE_REQUIRED_ISSUE, o as parseNodeRunnerInventoryDeclaration } from "./node-runner-inventory-V3no6LqA.mjs"; import { d as updateNodeRunnerInventory, t as collectNodeCatalogRuntimeState } from "./node-registry-private-BuH6xC9e.mjs"; import { f as refreshClientPluginNodeCapability, i as hasAuthorizedClientPluginNodeCapabilityUrl, l as pluginNodeCapabilityScopedHostUrlsConflict } from "./plugin-node-capability-Dow7CoAE.mjs"; import { t as assertValidParams } from "./validation-B7TobU6-.mjs"; import { n as respondUnavailableOnThrow } from "./response-DiopswxY.mjs"; import { o as projectPairedDeviceNodeBindings } from "./device-pairing-node-state-Bk5YCI0v.mjs"; import { n as getKnownNode, r as listKnownNodes, t as createKnownNodeCatalog } from "./node-catalog-LjMwIiVz.mjs"; import { t as nodeInvokePolicy } from "./nodes-policy-BQXSGmh2.mjs"; import { t as resolveLocalNodeId } from "./local-id-BcCpeyPm.mjs"; //#region src/gateway/server-methods/nodes.read.ts function safeNodeReadProjection(node, ownDeviceId) { if (!node.paired && !node.connected) return null; const { pendingRequestId, pendingDeclaredCaps: _pendingDeclaredCaps, pendingDeclaredCommands: _pendingDeclaredCommands, pendingDeclaredPermissions: _pendingDeclaredPermissions, ...safeNode } = node; return node.nodeId === ownDeviceId && pendingRequestId ? { ...safeNode, pendingRequestId } : safeNode; } function nodeReadCallerDeviceId(client) { return normalizeOptionalString(client?.connect?.device?.id); } function respondRunnerInventoryRetry(respond, message) { respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, message)); } function isVisibleNode(node) { return node !== null; } async function listNodesForClient(params) { const devicePairing = await listDevicePairing(); const nodePairing = projectNodePairing(devicePairing.paired); const connectedNodes = params.context.nodeRegistry.listConnectedForPairingStates(projectPairedDeviceNodeBindings(devicePairing.paired)); const runtimeState = collectNodeCatalogRuntimeState(params.context.nodeRegistry, connectedNodes); const catalog = createKnownNodeCatalog({ pairedDevices: devicePairing.paired, pairedNodes: nodePairing.paired, pendingNodes: nodePairing.pending, connectedNodes, ...runtimeState }); const localNodeId = await resolveLocalNodeId().catch((error) => { params.context.logGateway.warn(`failed to resolve same-install node-host identity: ${formatErrorMessage(error)}`); return null; }); const nodes = (params.nodeId ? [getKnownNode(catalog, params.nodeId)].filter(isVisibleNode) : listKnownNodes(catalog)).map((node) => node.nodeId === localNodeId ? Object.assign({}, node, { gatewayLocal: true }) : node); if (nodeInvokePolicy.canReadPendingNodePairing(params.client)) return { nodes, connectedNodes }; const ownDeviceId = nodeReadCallerDeviceId(params.client); return { nodes: nodes.map((node) => safeNodeReadProjection(node, ownDeviceId)).filter(isVisibleNode), connectedNodes }; } function normalizePluginSurfaceRefreshParams(params) { if (!params || typeof params !== "object") return; const surface = normalizeOptionalString(params.surface); if (!surface) return; const observedUrl = normalizeOptionalString(params.observedUrl); return { surface, ...observedUrl ? { observedUrl } : {} }; } function respondRefreshedPluginSurface(params) { const currentUrl = params.client?.pluginSurfaceUrls?.[params.surface]; const capabilitySurface = params.client?.pluginNodeCapabilitySurfaces?.[params.surface] ?? { surface: params.surface }; if (params.client && currentUrl && params.observedUrl && pluginNodeCapabilityScopedHostUrlsConflict(currentUrl, params.observedUrl) && hasAuthorizedClientPluginNodeCapabilityUrl({ client: params.client, surface: capabilitySurface, url: currentUrl })) { params.respond(true, { surface: params.surface, pluginSurfaceUrls: { [params.surface]: currentUrl } }, void 0); return; } const refreshed = params.client ? refreshClientPluginNodeCapability({ client: params.client, surface: capabilitySurface }) : void 0; if (!refreshed) { params.respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, `${params.surface} plugin surface unavailable`)); return; } params.respond(true, { surface: refreshed.surface, pluginSurfaceUrls: { [refreshed.surface]: refreshed.scopedUrl }, expiresAtMs: refreshed.expiresAtMs }, void 0); } const handlePluginSurfaceRefresh = ({ params, respond, client }) => { const parsed = normalizePluginSurfaceRefreshParams(params); if (!parsed) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "surface required")); return; } respondRefreshedPluginSurface({ surface: parsed.surface, observedUrl: parsed.observedUrl, client, respond }); }; function refreshConnectedNodeSurfaceCaches(params) { const cfg = params.cfg ?? params.context.getRuntimeConfig(); const { nodeSession } = params; recordRemoteNodeInfo({ nodeId: nodeSession.nodeId, connId: nodeSession.connId, displayName: nodeSession.displayName, platform: nodeSession.platform, deviceFamily: nodeSession.deviceFamily, commands: nodeSession.commands, remoteIp: nodeSession.remoteIp, pairingGeneration: nodeSession.pairingGeneration }); refreshRemoteNodeBins({ nodeId: nodeSession.nodeId, platform: nodeSession.platform, deviceFamily: nodeSession.deviceFamily, commands: nodeSession.commands, cfg }).catch((err) => params.context.logGateway.warn(`remote bin probe failed for ${nodeSession.nodeId}: ${formatErrorMessage(err)}`)); } const nodeReadHandlers = { "node.list": async ({ params, respond, client, context }) => { if (!assertValidParams(params, validateNodeListParams, "node.list", respond)) return; await respondUnavailableOnThrow(respond, async () => { const { nodes, connectedNodes } = await listNodesForClient({ client, context }); const activeNodeId = context.nodeRegistry.getActiveNode(connectedNodes)?.nodeId; const nodesWithPresence = activeNodeId ? nodes.map((node) => node.nodeId === activeNodeId ? { ...node, active: true } : node) : nodes; respond(true, { ts: Date.now(), activeNodeId, nodes: nodesWithPresence }, void 0); }); }, "node.describe": async ({ params, respond, client, context }) => { if (!assertValidParams(params, validateNodeDescribeParams, "node.describe", respond)) return; const { nodeId } = params; const id = normalizeOptionalString(nodeId) ?? ""; if (!id) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "nodeId required")); return; } await respondUnavailableOnThrow(respond, async () => { const { nodes, connectedNodes } = await listNodesForClient({ client, context, nodeId: id }); const node = nodes[0]; if (!node) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "unknown nodeId")); return; } respond(true, { ts: Date.now(), ...node, ...context.nodeRegistry.getActiveNode(connectedNodes)?.nodeId === id ? { active: true } : {} }, void 0); }); }, "plugin.surface.refresh": handlePluginSurfaceRefresh, "node.pluginSurface.refresh": handlePluginSurfaceRefresh, "node.pluginTools.update": async ({ params, respond, client, context }) => { if (!assertValidParams(params, validateNodePluginToolsUpdateParams, "node.pluginTools.update", respond)) return; const nodeId = normalizeOptionalString(client?.connect?.device?.id ?? client?.connect?.client?.id); if (!nodeId) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "nodeId required")); return; } const updated = context.nodeRegistry.updateNodePluginTools(nodeId, client?.connId, params.tools); if (!updated) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "unknown nodeId")); return; } respond(true, { nodeId, tools: updated.nodePluginTools }, void 0); }, "node.skills.update": async ({ params, respond, client, context }) => { if (!assertValidParams(params, validateNodeSkillsUpdateParams, "node.skills.update", respond)) return; const nodeId = normalizeOptionalString(client?.connect?.device?.id ?? client?.connect?.client?.id); if (!nodeId) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "nodeId required")); return; } const updated = context.nodeRegistry.updateNodeSkills(nodeId, client?.connId, params.skills); if (!updated) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "unknown nodeId")); return; } respond(true, { nodeId, skills: updated.nodeSkills }, void 0); }, "node.runnerInventory.update": async ({ params, respond, client, context }) => { const declaration = parseNodeRunnerInventoryDeclaration(params); if (!declaration) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid node runner inventory")); return; } const nodeId = normalizeOptionalString(client?.connect?.device?.id); const updated = nodeId ? updateNodeRunnerInventory({ registry: context.nodeRegistry, nodeId, connId: client?.connId, declaration }) : null; if (!nodeId || !updated) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "unknown nodeId")); return; } if (declaration.protocolFeatures.length === 1 && declaration.protocolFeatures[0] !== "node-worker-supervisor-v6") { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, formatNodeRunnerInventoryIssue(nodeId, NODE_RUNNER_UPDATE_REQUIRED_ISSUE))); return; } const connId = client?.connId; const currentSession = nodeId ? context.nodeRegistry.get(nodeId) : void 0; const pairingGeneration = currentSession && currentSession.connId === connId ? currentSession.pairingGeneration : void 0; if (!connId || !pairingGeneration) { const pendingSurface = nodeId ? (await listNodePairing()).pending.find((entry) => entry.nodeId === nodeId) : void 0; respondRunnerInventoryRetry(respond, pendingSurface ? `node capability surface is awaiting operator approval; run \`openclaw nodes approve ${pendingSurface.requestId}\` (see \`openclaw nodes pending\`), then this node retries automatically` : "node runner inventory publication is not current; retry after pairing completes"); return; } const sessionHost = "workerHost" in declaration && declaration.workerHost.enabled; try { if (!await updatePairedNodeSessionHost({ nodeId, sessionHost, expectedPairingGeneration: { nodeId, key: pairingGeneration }, isConnectionCurrent: () => { const current = context.nodeRegistry.get(nodeId); return current?.connId === connId && current.pairingGeneration === pairingGeneration && current.client.invalidated !== true; } })) { respondRunnerInventoryRetry(respond, "node runner inventory publication lost its pairing ownership; retry"); return; } } catch (error) { context.logGateway.warn(`failed to persist runner host consent for ${nodeId}: ${formatErrorMessage(error)}`); respondRunnerInventoryRetry(respond, "node runner inventory persistence failed; retry"); return; } respond(true, { nodeId }, void 0); } }; //#endregion export { refreshConnectedNodeSurfaceCaches as n, nodeReadHandlers as t };