UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

189 lines (188 loc) 7.53 kB
import { a as normalizeLowercaseStringOrEmpty, c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js"; import { d as resolveGatewayPort } from "./paths-mvMm5bYV.js"; import { i as formatErrorMessage } from "./errors-BXgSefBE.js"; import { a as trimToUndefined } from "./credential-planner-wESmL_7M.js"; import { r as resolveGatewayCredentialsFromConfig } from "./credentials-irvgw8Le.js"; import { i as getRuntimeConfig } from "./io-Gi7-pyU-.js"; import "./config-C9RxTsn1.js"; import { i as GATEWAY_CLIENT_NAMES, r as GATEWAY_CLIENT_MODES } from "./client-info-CcqJJIan.js"; import { o as callGateway } from "./call-B5-GYOlf.js"; import { n as loadDeviceIdentityIfPresent, r as loadOrCreateDeviceIdentity } from "./device-identity-CEPJolq9.js"; import { u as resolveLeastPrivilegeOperatorScopesForMethod } from "./method-scopes-BtdN3y7s.js"; import { t as getOperatorApprovalRuntimeToken } from "./operator-approval-runtime-token-BPk6svmE.js"; import { b as readStringParam, g as readPositiveIntegerParam } from "./common-C4yy9V-D.js"; //#region src/agents/tools/gateway.ts /** * Gateway call helpers for built-in tools. * * Resolves gateway URL/token overrides, local credentials, and least-privilege operator scopes. */ /** Reads common gateway options from tool parameters while preserving explicit token whitespace. */ function readGatewayCallOptions(params) { return { gatewayUrl: readStringParam(params, "gatewayUrl", { trim: false }), gatewayToken: readStringParam(params, "gatewayToken", { trim: false }), timeoutMs: readPositiveIntegerParam(params, "timeoutMs") }; } /** * Canonicalizes websocket URLs for allowlist comparisons without retaining paths or credentials. */ function canonicalizeToolGatewayWsUrl(raw) { const input = raw.trim(); let url; try { url = new URL(input); } catch (error) { const message = formatErrorMessage(error); throw new Error(`invalid gatewayUrl: ${input} (${message})`, { cause: error }); } if (url.protocol !== "ws:" && url.protocol !== "wss:") throw new Error(`invalid gatewayUrl protocol: ${url.protocol} (expected ws:// or wss://)`); if (url.username || url.password) throw new Error("invalid gatewayUrl: credentials are not allowed"); if (url.search || url.hash) throw new Error("invalid gatewayUrl: query/hash not allowed"); if (url.pathname && url.pathname !== "/") throw new Error("invalid gatewayUrl: path not allowed"); return { origin: url.origin, key: `${url.protocol}//${normalizeLowercaseStringOrEmpty(url.host)}` }; } function resolveLocalGatewayUrlKeys(cfg) { const port = resolveGatewayPort(cfg); return new Set([ `ws://127.0.0.1:${port}`, `wss://127.0.0.1:${port}`, `ws://localhost:${port}`, `wss://localhost:${port}`, `ws://[::1]:${port}`, `wss://[::1]:${port}` ]); } function resolveConfiguredRemoteGatewayKey(cfg) { let remoteKey; const remoteUrl = normalizeOptionalString(cfg.gateway?.remote?.url) ?? ""; if (remoteUrl) try { remoteKey = canonicalizeToolGatewayWsUrl(remoteUrl).key; } catch {} return remoteKey; } function resolveDefaultGatewayTarget(params) { if (params.envGatewayUrl) return "remote"; if (params.cfg.gateway?.mode === "remote" && normalizeOptionalString(params.cfg.gateway.remote?.url)) return "remote"; return "local"; } function validateGatewayUrlOverrideForAgentTools(params) { const { cfg } = params; const localAllowed = resolveLocalGatewayUrlKeys(cfg); const remoteKey = resolveConfiguredRemoteGatewayKey(cfg); const parsed = canonicalizeToolGatewayWsUrl(params.urlOverride); if (localAllowed.has(parsed.key)) return { url: parsed.origin, target: "local" }; if (remoteKey && parsed.key === remoteKey) return { url: parsed.origin, target: "remote" }; const port = resolveGatewayPort(cfg); throw new Error([ "gatewayUrl override rejected.", `Allowed: ws(s) loopback on port ${port} (127.0.0.1/localhost/[::1])`, "Or: configure gateway.remote.url and omit gatewayUrl to use the configured remote gateway." ].join(" ")); } function resolveGatewayOverrideToken(params) { if (params.explicitToken) return params.explicitToken; return resolveGatewayCredentialsFromConfig({ cfg: params.cfg, env: process.env, modeOverride: params.target, remoteTokenFallback: params.target === "remote" ? "remote-only" : "remote-env-local", remotePasswordFallback: params.target === "remote" ? "remote-only" : "remote-env-local" }).token; } /** * Resolves the gateway URL, token, and timeout for agent tool calls. */ function resolveGatewayOptions(opts) { const cfg = getRuntimeConfig(); const validatedOverride = trimToUndefined(opts?.gatewayUrl) !== void 0 ? validateGatewayUrlOverrideForAgentTools({ cfg, urlOverride: String(opts?.gatewayUrl) }) : void 0; const explicitToken = trimToUndefined(opts?.gatewayToken); const token = validatedOverride ? resolveGatewayOverrideToken({ cfg, target: validatedOverride.target, explicitToken }) : explicitToken; const timeoutMs = typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) ? Math.max(1, Math.floor(opts.timeoutMs)) : 3e4; const envGatewayUrl = trimToUndefined(process.env.OPENCLAW_GATEWAY_URL); const target = validatedOverride?.target ?? resolveDefaultGatewayTarget({ cfg, envGatewayUrl }); return { url: validatedOverride?.url, token, timeoutMs, target }; } const APPROVAL_RUNTIME_METHODS = new Set([ "exec.approval.request", "exec.approval.resolve", "exec.approval.waitDecision", "plugin.approval.request", "plugin.approval.waitDecision" ]); function resolveApprovalRuntimeTokenForGatewayTool(params) { if (!APPROVAL_RUNTIME_METHODS.has(params.method)) return; if (trimToUndefined(params.opts.gatewayUrl) !== void 0) return; if (params.target !== "local") return; return getOperatorApprovalRuntimeToken(); } function resolveApprovalRequesterDeviceIdentityForGatewayTool(params) { if (!APPROVAL_RUNTIME_METHODS.has(params.method)) return; if (trimToUndefined(params.opts.gatewayUrl) !== void 0) return; if (params.target !== "remote") return; try { const identity = loadOrCreateDeviceIdentity(); if (loadDeviceIdentityIfPresent()?.deviceId !== identity.deviceId) throw new Error("device identity is not persisted"); return identity; } catch (error) { throw new Error(["remote approval gateway calls require a stable device identity.", "Fix the OpenClaw state directory permissions or use the local approval-runtime gateway."].join(" "), { cause: error }); } } /** * Calls a gateway method as the agent-tool backend client with least-privilege scopes. */ async function callGatewayTool(method, opts, params, extra) { const gateway = resolveGatewayOptions(opts); const scopes = Array.isArray(extra?.scopes) ? extra.scopes : resolveLeastPrivilegeOperatorScopesForMethod(method, params); const approvalRuntimeToken = resolveApprovalRuntimeTokenForGatewayTool({ method, opts, target: gateway.target }); const deviceIdentity = resolveApprovalRequesterDeviceIdentityForGatewayTool({ method, opts, target: gateway.target }); return await callGateway({ url: gateway.url, token: gateway.token, method, params, timeoutMs: gateway.timeoutMs, expectFinal: extra?.expectFinal, clientName: GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT, clientDisplayName: "agent", mode: GATEWAY_CLIENT_MODES.BACKEND, ...approvalRuntimeToken ? { approvalRuntimeToken } : {}, ...deviceIdentity ? { deviceIdentity } : {}, scopes }); } //#endregion export { readGatewayCallOptions as n, resolveGatewayOptions as r, callGatewayTool as t };