import { ErrorCodes, errorShape } from "./protocol/index.js"; import { agentHandlers } from "./server-methods/agent.js"; import { agentsHandlers } from "./server-methods/agents.js"; import { channelsHandlers } from "./server-methods/channels.js"; import { chatHandlers } from "./server-methods/chat.js"; import { configHandlers } from "./server-methods/config.js"; import { connectHandlers } from "./server-methods/connect.js"; import { cronHandlers } from "./server-methods/cron.js"; import { deviceHandlers } from "./server-methods/devices.js"; import { execApprovalsHandlers } from "./server-methods/exec-approvals.js"; import { healthHandlers } from "./server-methods/health.js"; import { logsHandlers } from "./server-methods/logs.js"; import { modelsHandlers } from "./server-methods/models.js"; import { nodeHandlers } from "./server-methods/nodes.js"; import { sendHandlers } from "./server-methods/send.js"; import { sessionsHandlers } from "./server-methods/sessions.js"; import { skillsHandlers } from "./server-methods/skills.js"; import { systemHandlers } from "./server-methods/system.js"; import { talkHandlers } from "./server-methods/talk.js"; import type { GatewayRequestHandlers, GatewayRequestOptions } from "./server-methods/types.js"; import { updateHandlers } from "./server-methods/update.js"; import { usageHandlers } from "./server-methods/usage.js"; import { voicewakeHandlers } from "./server-methods/voicewake.js"; import { webHandlers } from "./server-methods/web.js"; import { wizardHandlers } from "./server-methods/wizard.js"; const ADMIN_SCOPE = "operator.admin"; const APPROVALS_SCOPE = "operator.approvals"; const PAIRING_SCOPE = "operator.pairing"; const APPROVAL_METHODS = new Set(["exec.approval.request", "exec.approval.resolve"]); const NODE_ROLE_METHODS = new Set(["node.invoke.result", "node.event"]); const PAIRING_METHODS = new Set([ "node.pair.request", "node.pair.list", "node.pair.approve", "node.pair.reject", "node.pair.verify", "device.pair.list", "device.pair.approve", "device.pair.reject", ]); const ADMIN_METHOD_PREFIXES = ["exec.approvals."]; function authorizeGatewayMethod(method: string, client: GatewayRequestOptions["client"]) { if (!client?.connect) return null; const role = client.connect.role ?? "operator"; const scopes = client.connect.scopes ?? []; if (role === "node") { if (NODE_ROLE_METHODS.has(method)) return null; return errorShape(ErrorCodes.INVALID_REQUEST, `unauthorized role: ${role}`); } if (role !== "operator") { return errorShape(ErrorCodes.INVALID_REQUEST, `unauthorized role: ${role}`); } if (scopes.includes(ADMIN_SCOPE)) return null; if (APPROVAL_METHODS.has(method) && !scopes.includes(APPROVALS_SCOPE)) { return errorShape(ErrorCodes.INVALID_REQUEST, "missing scope: operator.approvals"); } if (PAIRING_METHODS.has(method) && !scopes.includes(PAIRING_SCOPE)) { return errorShape(ErrorCodes.INVALID_REQUEST, "missing scope: operator.pairing"); } if (ADMIN_METHOD_PREFIXES.some((prefix) => method.startsWith(prefix))) { return errorShape(ErrorCodes.INVALID_REQUEST, "missing scope: operator.admin"); } return null; } export const coreGatewayHandlers: GatewayRequestHandlers = { ...connectHandlers, ...logsHandlers, ...voicewakeHandlers, ...healthHandlers, ...channelsHandlers, ...chatHandlers, ...cronHandlers, ...deviceHandlers, ...execApprovalsHandlers, ...webHandlers, ...modelsHandlers, ...configHandlers, ...wizardHandlers, ...talkHandlers, ...skillsHandlers, ...sessionsHandlers, ...systemHandlers, ...updateHandlers, ...nodeHandlers, ...sendHandlers, ...usageHandlers, ...agentHandlers, ...agentsHandlers, }; export async function handleGatewayRequest( opts: GatewayRequestOptions & { extraHandlers?: GatewayRequestHandlers }, ): Promise { const { req, respond, client, isWebchatConnect, context } = opts; const authError = authorizeGatewayMethod(req.method, client); if (authError) { respond(false, undefined, authError); return; } const handler = opts.extraHandlers?.[req.method] ?? coreGatewayHandlers[req.method]; if (!handler) { respond( false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `unknown method: ${req.method}`), ); return; } await handler({ req, params: (req.params ?? {}) as Record, client, isWebchatConnect, respond, context, }); }