UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

513 lines (512 loc) 18.7 kB
import { i as gatewayStartupUnavailableDetails } from "./startup-unavailable-CRTM-3cy.js"; import { r as getPluginRegistryState } from "./runtime-state-DRIYGASz.js"; import { n as createCoreGatewayMethodDescriptors, r as isCoreGatewayMethodClassified } from "./core-descriptors-CqQ0q50E.js"; import { t as ADMIN_SCOPE } from "./operator-scopes-CS3xdS-V.js"; import { n as authorizeOperatorScopesForMethod } from "./method-scopes-BtdN3y7s.js"; import { i as createPluginGatewayMethodDescriptors, n as createGatewayMethodRegistry, t as createGatewayMethodDescriptorsFromHandlers } from "./registry-CCu2s_6W.js"; import { n as withPluginRuntimeGatewayRequestScope } from "./gateway-request-scope-BAEdAUQ6.js"; import { in as errorShape, rn as ErrorCodes } from "./schema-BwaBORnA.js"; import "./src-oj0IwW6K.js"; import { t as consumeControlPlaneWriteBudget } from "./control-plane-rate-limit-DcNqNwry.js"; import { n as resolveControlPlaneActor, t as formatControlPlaneActor } from "./control-plane-audit-CfUQZQQ4.js"; import { n as parseGatewayRole, t as isRoleAuthorizedForMethod } from "./role-policy-TJzWk9R_.js"; //#region src/gateway/server-methods.ts function lazyHandlerModule(loadModule, selectHandlers) { let handlersPromise = null; return () => handlersPromise ??= loadModule().then(selectHandlers); } function createLazyCoreHandlers(params) { return Object.fromEntries(params.methods.map((method) => [method, async (opts) => { const handler = (await params.loadHandlers())[method]; if (!handler) throw new Error(`lazy gateway handler not found: ${method}`); await handler(opts); }])); } const loadAgentHandlers = lazyHandlerModule(() => import("./agent-ByCuLVjR.js"), (module) => module.agentHandlers); const loadAgentsHandlers = lazyHandlerModule(() => import("./agents-Bv74GIuu.js"), (module) => module.agentsHandlers); const loadArtifactsHandlers = lazyHandlerModule(() => import("./artifacts-BOFOdfbK.js"), (module) => module.artifactsHandlers); const loadChannelsHandlers = lazyHandlerModule(() => import("./channels-DdDjZb0-.js"), (module) => module.channelsHandlers); const loadChatHandlers = lazyHandlerModule(() => import("./chat-CO9BibHe.js"), (module) => module.chatHandlers); const loadCommandsHandlers = lazyHandlerModule(() => import("./commands-BhTc3Hig.js"), (module) => module.commandsHandlers); const loadConfigHandlers = lazyHandlerModule(() => import("./config-COR5J4DH.js"), (module) => module.configHandlers); const loadConnectHandlers = lazyHandlerModule(() => import("./connect-DjNKijVB.js"), (module) => module.connectHandlers); const loadCronHandlers = lazyHandlerModule(() => import("./cron-t-G59VLn.js"), (module) => module.cronHandlers); const loadDeviceHandlers = lazyHandlerModule(() => import("./devices-C2HvbMtK.js"), (module) => module.deviceHandlers); const loadDiagnosticsHandlers = lazyHandlerModule(() => import("./diagnostics-pnImIxpn.js"), (module) => module.diagnosticsHandlers); const loadDoctorHandlers = lazyHandlerModule(() => import("./doctor-B9vJA5aF.js"), (module) => module.doctorHandlers); const loadEnvironmentsHandlers = lazyHandlerModule(() => import("./environments-YqCrBUSH.js"), (module) => module.environmentsHandlers); const loadExecApprovalsHandlers = lazyHandlerModule(() => import("./exec-approvals-CriXi0B4.js"), (module) => module.execApprovalsHandlers); const loadHealthHandlers = lazyHandlerModule(() => import("./health-n6-o1YlZ.js"), (module) => module.healthHandlers); const loadLogsHandlers = lazyHandlerModule(() => import("./logs-v1jzyEYN.js"), (module) => module.logsHandlers); const loadModelsAuthStatusHandlers = lazyHandlerModule(() => import("./models-auth-status-CSGYA_oB.js"), (module) => module.modelsAuthStatusHandlers); const loadModelsHandlers = lazyHandlerModule(() => import("./models-CaP7uU1V.js"), (module) => module.modelsHandlers); const loadNativeHookRelayHandlers = lazyHandlerModule(() => import("./native-hook-relay-DGmdzoBK.js"), (module) => module.nativeHookRelayHandlers); const loadNodePendingHandlers = lazyHandlerModule(() => import("./nodes-pending-D5KI4QFj.js"), (module) => module.nodePendingHandlers); const loadNodeHandlers = lazyHandlerModule(() => import("./nodes-D_YCZ4cB.js"), (module) => module.nodeHandlers); const loadPluginHostHookHandlers = lazyHandlerModule(() => import("./plugin-host-hooks-DUXaqtB4.js"), (module) => module.pluginHostHookHandlers); const loadPushHandlers = lazyHandlerModule(() => import("./push-CIoQF-_f.js"), (module) => module.pushHandlers); const loadRestartHandlers = lazyHandlerModule(() => import("./restart-B7_GWhYo.js"), (module) => module.restartHandlers); const loadSendHandlers = lazyHandlerModule(() => import("./send-BRl9npEL.js"), (module) => module.sendHandlers); const loadSessionsHandlers = lazyHandlerModule(() => import("./sessions-DIMLrA4R.js"), (module) => module.sessionsHandlers); const loadSkillsHandlers = lazyHandlerModule(() => import("./skills-B38UBRtQ.js"), (module) => module.skillsHandlers); const loadSystemHandlers = lazyHandlerModule(() => import("./system-D71w027Q.js"), (module) => module.systemHandlers); const loadTalkHandlers = lazyHandlerModule(() => import("./talk-heGVlYsT.js"), (module) => module.talkHandlers); const loadTasksHandlers = lazyHandlerModule(() => import("./tasks-BJWpCbtR.js"), (module) => module.tasksHandlers); const loadToolsCatalogHandlers = lazyHandlerModule(() => import("./tools-catalog-C6xhh23U.js"), (module) => module.toolsCatalogHandlers); const loadToolsEffectiveHandlers = lazyHandlerModule(() => import("./tools-effective-D0MnkUfm.js"), (module) => module.toolsEffectiveHandlers); const loadToolsInvokeHandlers = lazyHandlerModule(() => import("./tools-invoke-DvEwvI90.js"), (module) => module.toolsInvokeHandlers); const loadTtsHandlers = lazyHandlerModule(() => import("./tts-CzT-cXam.js"), (module) => module.ttsHandlers); const loadUpdateHandlers = lazyHandlerModule(() => import("./update-Bskw-3av.js"), (module) => module.updateHandlers); const loadUsageHandlers = lazyHandlerModule(() => import("./usage-C_QPjote.js"), (module) => module.usageHandlers); const loadVoicewakeRoutingHandlers = lazyHandlerModule(() => import("./voicewake-routing-VAldfElg.js"), (module) => module.voicewakeRoutingHandlers); const loadVoicewakeHandlers = lazyHandlerModule(() => import("./voicewake-DCuaWm4I.js"), (module) => module.voicewakeHandlers); const loadWebHandlers = lazyHandlerModule(() => import("./web-BYJQ529u.js"), (module) => module.webHandlers); const loadWizardHandlers = lazyHandlerModule(() => import("./wizard-CoX9VS0A.js"), (module) => module.wizardHandlers); function authorizeGatewayMethod(method, client, params) { if (!client?.connect) return null; if (method === "health") return null; const roleRaw = client.connect.role ?? "operator"; const role = parseGatewayRole(roleRaw); if (!role) return errorShape(ErrorCodes.INVALID_REQUEST, `unauthorized role: ${roleRaw}`); const scopes = client.connect.scopes ?? []; if (!isRoleAuthorizedForMethod(role, method)) return errorShape(ErrorCodes.INVALID_REQUEST, `unauthorized role: ${role}`); if (role === "node") return null; if (scopes.includes("operator.admin")) return null; const scopeAuth = authorizeOperatorScopesForMethod(method, scopes, params); if (!scopeAuth.allowed) return errorShape(ErrorCodes.INVALID_REQUEST, `missing scope: ${scopeAuth.missingScope}`); return null; } const coreGatewayHandlers = { ...createLazyCoreHandlers({ methods: ["connect"], loadHandlers: loadConnectHandlers }), ...createLazyCoreHandlers({ methods: ["logs.tail"], loadHandlers: loadLogsHandlers }), ...createLazyCoreHandlers({ methods: ["voicewake.get", "voicewake.set"], loadHandlers: loadVoicewakeHandlers }), ...createLazyCoreHandlers({ methods: ["voicewake.routing.get", "voicewake.routing.set"], loadHandlers: loadVoicewakeRoutingHandlers }), ...createLazyCoreHandlers({ methods: ["health", "status"], loadHandlers: loadHealthHandlers }), ...createLazyCoreHandlers({ methods: [ "channels.status", "channels.start", "channels.stop", "channels.logout" ], loadHandlers: loadChannelsHandlers }), ...createLazyCoreHandlers({ methods: [ "chat.history", "chat.startup", "chat.metadata", "chat.message.get", "chat.abort", "chat.send", "chat.inject" ], loadHandlers: loadChatHandlers }), ...createLazyCoreHandlers({ methods: ["commands.list"], loadHandlers: loadCommandsHandlers }), ...createLazyCoreHandlers({ methods: [ "wake", "cron.list", "cron.status", "cron.get", "cron.add", "cron.update", "cron.remove", "cron.run", "cron.runs" ], loadHandlers: loadCronHandlers }), ...createLazyCoreHandlers({ methods: [ "device.pair.list", "device.pair.approve", "device.pair.reject", "device.pair.remove", "device.token.rotate", "device.token.revoke" ], loadHandlers: loadDeviceHandlers }), ...createLazyCoreHandlers({ methods: ["diagnostics.stability"], loadHandlers: loadDiagnosticsHandlers }), ...createLazyCoreHandlers({ methods: [ "doctor.memory.status", "doctor.memory.dreamDiary", "doctor.memory.backfillDreamDiary", "doctor.memory.resetDreamDiary", "doctor.memory.resetGroundedShortTerm", "doctor.memory.repairDreamingArtifacts", "doctor.memory.dedupeDreamDiary", "doctor.memory.remHarness" ], loadHandlers: loadDoctorHandlers }), ...createLazyCoreHandlers({ methods: ["environments.list", "environments.status"], loadHandlers: loadEnvironmentsHandlers }), ...createLazyCoreHandlers({ methods: [ "exec.approvals.get", "exec.approvals.set", "exec.approvals.node.get", "exec.approvals.node.set" ], loadHandlers: loadExecApprovalsHandlers }), ...createLazyCoreHandlers({ methods: ["web.login.start", "web.login.wait"], loadHandlers: loadWebHandlers }), ...createLazyCoreHandlers({ methods: ["models.list"], loadHandlers: loadModelsHandlers }), ...createLazyCoreHandlers({ methods: ["models.authLogout", "models.authStatus"], loadHandlers: loadModelsAuthStatusHandlers }), ...createLazyCoreHandlers({ methods: ["nativeHook.invoke"], loadHandlers: loadNativeHookRelayHandlers }), ...createLazyCoreHandlers({ methods: ["plugins.uiDescriptors", "plugins.sessionAction"], loadHandlers: loadPluginHostHookHandlers }), ...createLazyCoreHandlers({ methods: [ "config.get", "config.schema", "config.schema.lookup", "config.set", "config.patch", "config.apply", "config.openFile" ], loadHandlers: loadConfigHandlers }), ...createLazyCoreHandlers({ methods: [ "wizard.start", "wizard.next", "wizard.cancel", "wizard.status" ], loadHandlers: loadWizardHandlers }), ...createLazyCoreHandlers({ methods: [ "talk.session.create", "talk.session.join", "talk.session.appendAudio", "talk.session.startTurn", "talk.session.endTurn", "talk.session.cancelTurn", "talk.session.cancelOutput", "talk.session.submitToolResult", "talk.session.steer", "talk.session.close", "talk.client.create", "talk.client.toolCall", "talk.client.steer", "talk.catalog", "talk.config", "talk.speak", "talk.mode" ], loadHandlers: loadTalkHandlers }), ...createLazyCoreHandlers({ methods: [ "tasks.list", "tasks.get", "tasks.cancel" ], loadHandlers: loadTasksHandlers }), ...createLazyCoreHandlers({ methods: ["tools.catalog"], loadHandlers: loadToolsCatalogHandlers }), ...createLazyCoreHandlers({ methods: ["tools.effective"], loadHandlers: loadToolsEffectiveHandlers }), ...createLazyCoreHandlers({ methods: ["tools.invoke"], loadHandlers: loadToolsInvokeHandlers }), ...createLazyCoreHandlers({ methods: [ "tts.status", "tts.enable", "tts.disable", "tts.convert", "tts.setProvider", "tts.personas", "tts.setPersona", "tts.providers" ], loadHandlers: loadTtsHandlers }), ...createLazyCoreHandlers({ methods: [ "skills.upload.begin", "skills.upload.chunk", "skills.upload.commit", "skills.status", "skills.bins", "skills.search", "skills.detail", "skills.securityVerdicts", "skills.skillCard", "skills.install", "skills.update", "skills.proposals.list", "skills.proposals.inspect", "skills.proposals.create", "skills.proposals.update", "skills.proposals.revise", "skills.proposals.requestRevision", "skills.proposals.apply", "skills.proposals.reject", "skills.proposals.quarantine" ], loadHandlers: loadSkillsHandlers }), ...createLazyCoreHandlers({ methods: [ "sessions.list", "sessions.cleanup", "sessions.subscribe", "sessions.unsubscribe", "sessions.messages.subscribe", "sessions.messages.unsubscribe", "sessions.preview", "sessions.describe", "sessions.resolve", "sessions.compaction.list", "sessions.compaction.get", "sessions.create", "sessions.compaction.branch", "sessions.compaction.restore", "sessions.send", "sessions.steer", "sessions.abort", "sessions.patch", "sessions.pluginPatch", "sessions.reset", "sessions.delete", "sessions.get", "sessions.compact" ], loadHandlers: loadSessionsHandlers }), ...createLazyCoreHandlers({ methods: [ "gateway.identity.get", "last-heartbeat", "set-heartbeats", "system-presence", "system-event" ], loadHandlers: loadSystemHandlers }), ...createLazyCoreHandlers({ methods: ["update.status", "update.run"], loadHandlers: loadUpdateHandlers }), ...createLazyCoreHandlers({ methods: [ "node.pair.request", "node.pair.list", "node.pair.approve", "node.pair.reject", "node.pair.remove", "node.pair.verify", "node.rename", "node.list", "node.describe", "node.pluginSurface.refresh", "node.pending.pull", "node.pending.ack", "node.invoke", "node.invoke.result", "node.event" ], loadHandlers: loadNodeHandlers }), ...createLazyCoreHandlers({ methods: ["node.pending.drain", "node.pending.enqueue"], loadHandlers: loadNodePendingHandlers }), ...createLazyCoreHandlers({ methods: [ "push.test", "push.web.vapidPublicKey", "push.web.subscribe", "push.web.unsubscribe", "push.web.test" ], loadHandlers: loadPushHandlers }), ...createLazyCoreHandlers({ methods: ["gateway.restart.request", "gateway.restart.preflight"], loadHandlers: loadRestartHandlers }), ...createLazyCoreHandlers({ methods: [ "message.action", "send", "poll" ], loadHandlers: loadSendHandlers }), ...createLazyCoreHandlers({ methods: [ "usage.status", "usage.cost", "sessions.usage", "sessions.usage.timeseries", "sessions.usage.logs" ], loadHandlers: loadUsageHandlers }), ...createLazyCoreHandlers({ methods: [ "agent", "agent.identity.get", "agent.wait" ], loadHandlers: loadAgentHandlers }), ...createLazyCoreHandlers({ methods: [ "agents.list", "agents.create", "agents.update", "agents.delete", "agents.files.list", "agents.files.get", "agents.files.set" ], loadHandlers: loadAgentsHandlers }), ...createLazyCoreHandlers({ methods: [ "artifacts.list", "artifacts.get", "artifacts.download" ], loadHandlers: loadArtifactsHandlers }) }; /** Builds the per-request method registry from core, plugin, and explicit extra handlers. */ function createRequestGatewayMethodRegistry(extraHandlers) { const activePluginRegistry = getPluginRegistryState()?.activeRegistry; const activePluginHandlers = activePluginRegistry?.gatewayHandlers ?? {}; const extraHandlerEntries = Object.entries(extraHandlers ?? {}); const pluginMethodNames = new Set(Object.keys(activePluginHandlers)); const coreDescriptorHandlers = { ...coreGatewayHandlers }; for (const [method, extraHandler] of extraHandlerEntries) if (!pluginMethodNames.has(method) && isCoreGatewayMethodClassified(method)) coreDescriptorHandlers[method] = extraHandler; const coreDescriptors = createCoreGatewayMethodDescriptors(coreDescriptorHandlers); for (const descriptor of coreDescriptors) { const extraHandler = extraHandlers?.[descriptor.name]; if (extraHandler && !pluginMethodNames.has(descriptor.name)) descriptor.handler = extraHandler; } const coreMethodNames = new Set(coreDescriptors.map((descriptor) => descriptor.name)); const auxHandlers = Object.fromEntries(extraHandlerEntries.filter(([method]) => !pluginMethodNames.has(method) && !coreMethodNames.has(method))); return createGatewayMethodRegistry([ ...coreDescriptors, ...activePluginRegistry ? createPluginGatewayMethodDescriptors(activePluginRegistry) : [], ...createGatewayMethodDescriptorsFromHandlers({ handlers: auxHandlers, owner: { kind: "aux", area: "gateway-extra" }, defaultScope: ADMIN_SCOPE }) ]); } /** Authorizes and dispatches one gateway JSON-RPC-style request. */ async function handleGatewayRequest(opts) { const { req, respond, client, isWebchatConnect, context } = opts; const methodRegistry = opts.methodRegistry ?? createRequestGatewayMethodRegistry(opts.extraHandlers); const authError = authorizeGatewayMethod(req.method, client, req.params); if (authError) { respond(false, void 0, authError); return; } if (context.unavailableGatewayMethods?.has(req.method)) { respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, `${req.method} unavailable during gateway startup`, { retryable: true, retryAfterMs: 500, details: { ...gatewayStartupUnavailableDetails(), method: req.method } })); return; } if (methodRegistry.isControlPlaneWrite(req.method)) { const budget = consumeControlPlaneWriteBudget({ client }); if (!budget.allowed) { const actor = resolveControlPlaneActor(client); context.logGateway.warn(`control-plane write rate-limited method=${req.method} ${formatControlPlaneActor(actor)} retryAfterMs=${budget.retryAfterMs} key=${budget.key}`); respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, `rate limit exceeded for ${req.method}; retry after ${Math.ceil(budget.retryAfterMs / 1e3)}s`, { retryable: true, retryAfterMs: budget.retryAfterMs, details: { method: req.method, limit: "3 per 60s" } })); return; } } const handler = methodRegistry.getHandler(req.method); if (!handler) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `unknown method: ${req.method}`)); return; } const invokeHandler = () => handler({ req, params: req.params ?? {}, client, isWebchatConnect, respond, context }); await withPluginRuntimeGatewayRequestScope({ context, client, isWebchatConnect }, invokeHandler); } //#endregion export { coreGatewayHandlers, handleGatewayRequest };