UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

277 lines (276 loc) 12.5 kB
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js"; import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js"; import { a as formatNodeRunnerUpdateRequired, n as NODE_RUNNER_UPDATE_REQUIRED_ISSUE, o as parseNodeRunnerInventoryDeclaration } from "./node-runner-inventory-CsPbvshW.js"; import { t as ErrorCodes } from "./gateway-error-details-w0nAGBBp.js"; import { On as validateNodeSkillsUpdateParams, Tn as validateNodePluginToolsUpdateParams, _n as validateNodeListParams, dn as validateNodeDescribeParams } from "./src-BiL5aQto.js"; import { d as errorShape } from "./error-codes-Bo8q2D1o.js"; import { c as listDevicePairing } from "./device-pairing-DFIQp3ZY.js"; import { a as listNodePairing, g as updatePairedNodeSessionHost, o as projectNodePairing } from "./device-pairing-node-DiTk_P80.js"; import { i as recordRemoteNodeInfo, o as refreshRemoteNodeBins } from "./remote-D-j1AEXQ.js"; import { d as updateNodeRunnerInventory, t as collectNodeCatalogRuntimeState } from "./node-registry-private-CPqGMumT.js"; import { d as refreshClientPluginNodeCapability, i as hasAuthorizedClientPluginNodeCapabilityUrl, l as pluginNodeCapabilityScopedHostUrlsConflict } from "./plugin-node-capability-CIHQ6nIw.js"; import { r as respondUnavailableOnThrow } from "./nodes.helpers-7uACMTmw.js"; import { t as assertValidParams } from "./validation-pzrlzFvo.js"; import { o as projectPairedDeviceNodeBindings } from "./device-pairing-node-state-DxNt2jjv.js"; import { n as getKnownNode, r as listKnownNodes, t as createKnownNodeCatalog } from "./node-catalog-BbldLLoP.js"; import { t as nodeInvokePolicy } from "./nodes-policy-DGZgpCCU.js"; import { t as resolveLocalNodeId } from "./local-id-BhWlYr_Q.js"; //#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 runtimeState = collectNodeCatalogRuntimeState(params.context.nodeRegistry, params.connectedNodes); const catalog = createKnownNodeCatalog({ pairedDevices: params.pairedDevices, pairedNodes: params.pairedNodes, pendingNodes: params.pendingNodes, connectedNodes: params.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; const ownDeviceId = nodeReadCallerDeviceId(params.client); return nodes.map((node) => safeNodeReadProjection(node, ownDeviceId)).filter(isVisibleNode); } 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 devicePairing = await listDevicePairing(); const nodePairing = projectNodePairing(devicePairing.paired); const connectedNodes = context.nodeRegistry.listConnectedForPairingStates(projectPairedDeviceNodeBindings(devicePairing.paired)); const nodes = await listNodesForClient({ client, context, pairedDevices: devicePairing.paired, pairedNodes: nodePairing.paired, pendingNodes: nodePairing.pending, connectedNodes }); 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 devicePairing = await listDevicePairing(); const nodePairing = projectNodePairing(devicePairing.paired); const connectedNodes = context.nodeRegistry.listConnectedForPairingStates(projectPairedDeviceNodeBindings(devicePairing.paired)); const node = (await listNodesForClient({ client, context, nodeId: id, pairedDevices: devicePairing.paired, pairedNodes: nodePairing.paired, pendingNodes: nodePairing.pending, connectedNodes }))[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, formatNodeRunnerUpdateRequired(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 };