UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

259 lines (258 loc) 8.66 kB
import { i as formatErrorMessage } from "./errors-BXgSefBE.js"; import { n as VERSION } from "./version-Crcn9X9T.js"; import { n as assertExplicitGatewayAuthModeWhenBothConfigured } from "./auth-mode-policy-Byq0myX3.js"; import { i as isLoopbackHost } from "./net-DTe7AQiu.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, t as GATEWAY_CLIENT_CAPS } from "./client-info-CcqJJIan.js"; import "./version-51ymduTn.js"; import { g as resolveExplicitGatewayAuth, i as buildGatewayConnectionDetails, u as ensureExplicitGatewayAuth } from "./call-B5-GYOlf.js"; import { n as GatewayClient, r as GatewayClientRequestError } from "./client-C2g2lFC5.js"; import { t as startGatewayClientWhenEventLoopReady } from "./client-start-readiness-BIoVQZze.js"; import "./src-oj0IwW6K.js"; import { t as resolveGatewayInteractiveSurfaceAuth } from "./auth-surface-resolution-Dr_JIxWO.js"; import { n as TUI_SETUP_AUTH_SOURCE_ENV, t as TUI_SETUP_AUTH_SOURCE_CONFIG } from "./setup-launch-env-DehdAyoV.js"; import { randomUUID } from "node:crypto"; //#region src/tui/gateway-chat.ts const STARTUP_CHAT_HISTORY_RETRY_TIMEOUT_MS = 6e4; const STARTUP_CHAT_HISTORY_DEFAULT_RETRY_MS = 500; const STARTUP_CHAT_HISTORY_MAX_RETRY_MS = 5e3; function throwGatewayAuthResolutionError(reason) { throw new Error([ reason, "Fix: set OPENCLAW_GATEWAY_TOKEN/OPENCLAW_GATEWAY_PASSWORD, pass --token/--password,", "or resolve the configured secret provider for this credential." ].join("\n")); } function isRetryableStartupUnavailable(err, method) { if (!(err instanceof GatewayClientRequestError)) return false; if (err.gatewayCode !== "UNAVAILABLE" || !err.retryable) return false; const details = err.details; if (!details || typeof details !== "object") return true; const detailMethod = details.method; return typeof detailMethod !== "string" || detailMethod === method; } function resolveStartupRetryDelayMs(err) { const retryAfterMs = typeof err.retryAfterMs === "number" ? err.retryAfterMs : STARTUP_CHAT_HISTORY_DEFAULT_RETRY_MS; return Math.min(Math.max(retryAfterMs, 100), STARTUP_CHAT_HISTORY_MAX_RETRY_MS); } function sleep(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } var GatewayChatClient = class GatewayChatClient { constructor(connection) { this.connection = connection; this.readyPromise = new Promise((resolve) => { this.resolveReady = resolve; }); this.client = new GatewayClient({ url: connection.url, token: connection.token, password: connection.password, preauthHandshakeTimeoutMs: connection.preauthHandshakeTimeoutMs, clientName: GATEWAY_CLIENT_NAMES.TUI, clientDisplayName: "openclaw-tui", clientVersion: VERSION, platform: process.platform, mode: GATEWAY_CLIENT_MODES.UI, deviceIdentity: connection.allowInsecureLocalOperatorUi ? null : void 0, caps: [GATEWAY_CLIENT_CAPS.TOOL_EVENTS], instanceId: randomUUID(), minProtocol: 4, maxProtocol: 4, onHelloOk: (hello) => { this.hello = hello; this.resolveReady?.(); this.onConnected?.(); }, onEvent: (evt) => { this.onEvent?.({ event: evt.event, payload: evt.payload, seq: evt.seq }); }, onClose: (_code, reason) => { this.readyPromise = new Promise((resolve) => { this.resolveReady = resolve; }); this.onDisconnected?.(reason); }, onGap: (info) => { this.onGap?.(info); } }); } static async connect(opts) { return new GatewayChatClient(await resolveGatewayConnection(opts)); } start() { startGatewayClientWhenEventLoopReady(this.client, { clientOptions: { preauthHandshakeTimeoutMs: this.connection.preauthHandshakeTimeoutMs } }).then((readiness) => { if (!readiness.ready && !readiness.aborted) this.onDisconnected?.("gateway event loop readiness timeout"); }).catch((err) => { this.onDisconnected?.(err instanceof Error ? err.message : String(err)); }); } stop() { this.client.stop(); } async waitForReady() { await this.readyPromise; } async sendChat(opts) { const runId = opts.runId ?? randomUUID(); await this.client.request("chat.send", { sessionKey: opts.sessionKey, ...opts.agentId ? { agentId: opts.agentId } : {}, ...opts.sessionId ? { sessionId: opts.sessionId } : {}, message: opts.message, thinking: opts.thinking, deliver: opts.deliver, timeoutMs: opts.timeoutMs, idempotencyKey: runId }); return { runId }; } async abortChat(opts) { return await this.client.request("chat.abort", { sessionKey: opts.sessionKey, ...opts.agentId ? { agentId: opts.agentId } : {}, runId: opts.runId }); } async loadHistory(opts) { const startedAt = Date.now(); for (;;) try { return await this.client.request("chat.history", { sessionKey: opts.sessionKey, ...opts.agentId ? { agentId: opts.agentId } : {}, limit: opts.limit }); } catch (err) { if (Date.now() - startedAt < STARTUP_CHAT_HISTORY_RETRY_TIMEOUT_MS && isRetryableStartupUnavailable(err, "chat.history")) { await sleep(resolveStartupRetryDelayMs(err)); continue; } throw err; } } async listSessions(opts) { return await this.client.request("sessions.list", opts ?? {}); } async listAgents() { return await this.client.request("agents.list", {}); } async patchSession(opts) { return await this.client.request("sessions.patch", opts); } async resetSession(key, reason, opts) { return await this.client.request("sessions.reset", { key, ...opts?.agentId ? { agentId: opts.agentId } : {}, ...reason ? { reason } : {} }); } async getGatewayStatus() { return await this.client.request("status"); } async listModels() { const res = await this.client.request("models.list"); return Array.isArray(res?.models) ? res.models : []; } async listCommands(opts) { const res = await this.client.request("commands.list", opts ?? {}); return Array.isArray(res?.commands) ? res.commands : []; } }; async function resolveGatewayConnection(opts) { const config = getRuntimeConfig(); const env = process.env; const gatewayAuthMode = config.gateway?.auth?.mode; const isRemoteMode = config.gateway?.mode === "remote"; const preferConfiguredAuth = env[TUI_SETUP_AUTH_SOURCE_ENV] === TUI_SETUP_AUTH_SOURCE_CONFIG; const urlOverride = typeof opts.url === "string" && opts.url.trim().length > 0 ? opts.url.trim() : void 0; const explicitAuth = resolveExplicitGatewayAuth({ token: opts.token, password: opts.password }); ensureExplicitGatewayAuth({ urlOverride, urlOverrideSource: "cli", explicitAuth, errorHint: "Fix: pass --token or --password when using --url." }); const url = buildGatewayConnectionDetails({ config, ...urlOverride ? { url: urlOverride } : {} }).url; const allowInsecureLocalOperatorUi = (() => { if (config.gateway?.controlUi?.allowInsecureAuth !== true) return false; try { return isLoopbackHost(new URL(url).hostname); } catch { return false; } })(); if (urlOverride) return { url, token: explicitAuth.token, password: explicitAuth.password, preauthHandshakeTimeoutMs: config.gateway?.handshakeTimeoutMs, allowInsecureLocalOperatorUi }; if (isRemoteMode) { const resolved = await resolveGatewayInteractiveSurfaceAuth({ config, env, explicitAuth, surface: "remote" }); if (resolved.failureReason) throwGatewayAuthResolutionError(resolved.failureReason); return { url, token: resolved.token, password: resolved.password, preauthHandshakeTimeoutMs: config.gateway?.handshakeTimeoutMs, allowInsecureLocalOperatorUi: false }; } if (gatewayAuthMode === "none" || gatewayAuthMode === "trusted-proxy") { const resolved = await resolveGatewayInteractiveSurfaceAuth({ config, env, explicitAuth, surface: "local" }); return { url, token: resolved.token, password: resolved.password, preauthHandshakeTimeoutMs: config.gateway?.handshakeTimeoutMs, allowInsecureLocalOperatorUi }; } try { assertExplicitGatewayAuthModeWhenBothConfigured(config); } catch (err) { throwGatewayAuthResolutionError(formatErrorMessage(err)); } const resolved = await resolveGatewayInteractiveSurfaceAuth({ config, env, explicitAuth, suppressEnvAuthFallback: preferConfiguredAuth, surface: "local" }); if (resolved.failureReason) throwGatewayAuthResolutionError(resolved.failureReason); return { url, token: resolved.token, password: resolved.password, preauthHandshakeTimeoutMs: config.gateway?.handshakeTimeoutMs, allowInsecureLocalOperatorUi }; } //#endregion export { GatewayChatClient };