UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

261 lines (260 loc) 11.4 kB
import { r as defaultRuntime } from "./runtime-CF2WjnNZ.js"; import { _ as resolveDeviceIdentityForGatewayCall, f as isGatewayClientRequestError } from "./call-BWR5pPgw.js"; import { r as isGatewayTransportError } from "./transport-error-D_LRKgla.js"; import { n as t } from "./i18n-hynzGFbD.js"; import { n as WizardCancelledError } from "./prompts-DLsO8MlU.js"; import { randomUUID } from "node:crypto"; import { setTimeout } from "node:timers/promises"; //#region src/commands/onboard-remote-gateway.ts const GATEWAY_SETUP_DETECT_TIMEOUT_MS = 4e4; const GATEWAY_SETUP_ACTIVATE_TIMEOUT_MS = 15e4; const GATEWAY_CODEX_SETUP_ACTIVATE_TIMEOUT_MS = 48e4; const GATEWAY_SETUP_VERIFY_TIMEOUT_MS = 3e4; const GATEWAY_SYSTEM_AGENT_CHAT_TIMEOUT_MS = 19e4; const GATEWAY_RESTART_WAIT_TIMEOUT_MS = 45e3; const GATEWAY_RESTART_IDENTITY_ERROR = "Inference settings were saved, but the Gateway did not provide a boot identity. Update and restart the remote Gateway, then run onboarding again."; function toSetupInferenceDetection(result) { return { candidates: result.candidates.map((candidate) => ({ kind: candidate.kind, ...candidate.brandId !== void 0 ? { brandId: candidate.brandId } : {}, label: candidate.label, detail: candidate.detail, modelRef: candidate.modelRef, ...candidate.icon !== void 0 ? { icon: candidate.icon } : {}, ...candidate.website !== void 0 ? { website: candidate.website } : {}, recommended: false, ...candidate.credentials !== void 0 ? { credentials: candidate.credentials } : {} })), manualProviders: result.manualProviders.map((provider) => ({ id: provider.id, ...provider.brandId !== void 0 ? { brandId: provider.brandId } : {}, label: provider.label, ...provider.hint !== void 0 ? { hint: provider.hint } : {}, ...provider.icon !== void 0 ? { icon: provider.icon } : {}, ...provider.website !== void 0 ? { website: provider.website } : {} })), authOptions: (result.authOptions ?? []).map((option) => Object.assign({ id: option.id, ...option.brandId !== void 0 ? { brandId: option.brandId } : {}, label: option.label, kind: option.kind, featured: option.featured }, option.hint !== void 0 ? { hint: option.hint } : {}, option.groupLabel !== void 0 ? { groupLabel: option.groupLabel } : {}, option.icon !== void 0 ? { icon: option.icon } : {}, option.website !== void 0 ? { website: option.website } : {})), ...result.prepareOptions !== void 0 ? { prepareOptions: result.prepareOptions.map((option) => Object.assign({ id: option.id, label: option.label }, option.brandId !== void 0 ? { brandId: option.brandId } : {}, option.hint !== void 0 ? { hint: option.hint } : {}, option.icon !== void 0 ? { icon: option.icon } : {}, option.website !== void 0 ? { website: option.website } : {})) } : {}, recommendedInstalls: result.recommendedInstalls ?? [], unavailableCandidates: (result.unavailableCandidates ?? []).map((candidate) => ({ id: candidate.id, label: candidate.label, detail: candidate.detail, reason: candidate.reason })), workspace: result.workspace, ...result.configuredModel !== void 0 ? { configuredModel: result.configuredModel } : {}, setupComplete: result.setupComplete }; } function isSetupInferenceFailureStatus(value) { return value === "auth" || value === "rate_limit" || value === "billing" || value === "timeout" || value === "format" || value === "unavailable" || value === "unknown"; } function toSetupInferenceActivationResult(result) { if (result.ok) { if (!result.modelRef?.trim() || typeof result.latencyMs !== "number" || !Array.isArray(result.lines)) throw new Error("Gateway returned an invalid successful inference activation result."); return { ok: true, modelRef: result.modelRef, latencyMs: result.latencyMs, lines: result.lines, ...result.gatewayRestartRequired ? { gatewayRestartRequired: true } : {} }; } if (!isSetupInferenceFailureStatus(result.status) || !result.error?.trim()) throw new Error("Gateway returned an invalid failed inference activation result."); return { ok: false, status: result.status, error: result.error }; } function activationTimeoutMs(kind) { return kind === "codex-cli" ? GATEWAY_CODEX_SETUP_ACTIVATE_TIMEOUT_MS : GATEWAY_SETUP_ACTIVATE_TIMEOUT_MS; } function bindGatewayConfig(target) { return { ...target.config, gateway: { ...target.config.gateway, mode: "remote", remote: { ...target.config.gateway?.remote, url: target.gatewayUrl } } }; } function assertVerifiedActivation(params) { if (params.requestedModelRef && params.activation.modelRef.trim() !== params.requestedModelRef.trim()) throw new Error(`Gateway activated ${params.activation.modelRef}, not the selected ${params.requestedModelRef}.`); if (!params.verification.ok) throw new Error(`Gateway inference verification failed: ${params.verification.error}`); if (params.verification.modelRef.trim() !== params.activation.modelRef.trim()) throw new Error(`Gateway verified ${params.verification.modelRef}, not the activated ${params.activation.modelRef}.`); } /** * Configure missing inference on the selected remote Gateway, then let that * Gateway's OpenClaw finish setup before handing off to its normal TUI. * The local config is routing input only; every setup mutation runs through * Gateway RPC. */ async function runRemoteGatewayInferenceOnboarding(target, runtime = defaultRuntime, deps = {}) { const callGateway = deps.callGateway ?? (await import("./call-Bo4Qx1ax.js")).callGatewayCli; const runGuidedOnboarding = deps.runGuidedOnboarding ?? (await import("./onboard-guided-CL7xWQwP.js")).runGuidedOnboarding; const boundConfig = bindGatewayConfig(target); const explicitAuth = Boolean(target.token || target.password); let gatewayWorkspace; const request = async (params) => await callGateway({ ...params, config: boundConfig, ...explicitAuth ? { url: target.gatewayUrl } : {}, ...target.token ? { token: target.token } : {}, ...target.password ? { password: target.password } : {}, ...target.tlsFingerprint ? { tlsFingerprint: target.tlsFingerprint } : {}, ignoreEnvUrlOverride: true }); const detect = async () => { const detection = toSetupInferenceDetection(await request({ method: "openclaw.setup.detect", params: {}, timeoutMs: GATEWAY_SETUP_DETECT_TIMEOUT_MS })); gatewayWorkspace = detection.workspace; return detection; }; const activate = async (params) => { let activationBootId; const activation = toSetupInferenceActivationResult(await request({ method: "openclaw.setup.activate", params: { kind: params.kind, ...params.modelRef !== void 0 ? { modelRef: params.modelRef } : {}, ...params.authChoice !== void 0 ? { authChoice: params.authChoice } : {}, ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {}, ...gatewayWorkspace ? { workspace: gatewayWorkspace } : {} }, timeoutMs: activationTimeoutMs(params.kind), onHelloOk: (hello) => { activationBootId = hello.server.bootId?.trim(); } })); if (!activation.ok) return activation; const restartBootId = activation.gatewayRestartRequired ? activationBootId : void 0; if (activation.gatewayRestartRequired && !restartBootId) throw new Error(GATEWAY_RESTART_IDENTITY_ERROR); const restartDeadline = Date.now() + GATEWAY_RESTART_WAIT_TIMEOUT_MS; let retryDelayMs = 250; for (;;) { const remainingBeforeAttemptMs = restartDeadline - Date.now(); if (restartBootId && remainingBeforeAttemptMs <= 0) throw new Error("Inference settings were saved, but the Gateway did not finish restarting and verifying inference. Check the remote Gateway, then run onboarding again."); const restartWait = new AbortController(); let requestedDelay = retryDelayMs; try { const verification = await request({ method: "openclaw.setup.verify", params: {}, timeoutMs: restartBootId ? Math.min(GATEWAY_SETUP_VERIFY_TIMEOUT_MS, remainingBeforeAttemptMs) : GATEWAY_SETUP_VERIFY_TIMEOUT_MS, signal: restartWait.signal, onHelloOk: (hello) => { if (!restartBootId) return; const bootId = hello.server.bootId?.trim(); if (!bootId || bootId === restartBootId) restartWait.abort(bootId ? "unchanged-boot" : "missing-boot"); } }); if (!restartBootId || verification.ok || verification.status !== "unavailable") { assertVerifiedActivation({ activation, verification, ...params.modelRef ? { requestedModelRef: params.modelRef } : {} }); return activation; } } catch (error) { if (restartWait.signal.reason === "missing-boot") throw new Error(GATEWAY_RESTART_IDENTITY_ERROR, { cause: error }); if (!(restartBootId && (restartWait.signal.reason === "unchanged-boot" || isGatewayTransportError(error) || isGatewayClientRequestError(error) && error.retryable))) throw error; if (isGatewayClientRequestError(error)) requestedDelay = error.retryAfterMs ?? retryDelayMs; } await setTimeout(Math.min(requestedDelay, Math.max(0, restartDeadline - Date.now()))); retryDelayMs = Math.min(retryDelayMs * 2, 2e3); } }; await runGuidedOnboarding({}, runtime, { detect, activate, handoffMode: "chat", runSetupMemoryImportStep: async () => ({ status: "skipped", providers: [] }), ...deps.createPrompter ? { createPrompter: deps.createPrompter } : {}, runSystemAgentChat: async () => { const prompter = await (deps.createPrompter?.() ?? import("./clack-prompter-Dr4XxfrM.js").then(({ createClackPrompter }) => createClackPrompter())); await prompter.intro("OpenClaw"); const deviceIdentity = resolveDeviceIdentityForGatewayCall(); const sessionId = randomUUID(); let reply = await request({ method: "openclaw.chat", deviceIdentity, params: { sessionId, welcomeVariant: "onboarding" }, timeoutMs: GATEWAY_SYSTEM_AGENT_CHAT_TIMEOUT_MS }); let agentDraft; try { for (;;) { await prompter.note(reply.reply, "OpenClaw"); if (reply.action === "exit") { await prompter.outro("OpenClaw setup finished."); return; } if (reply.action === "open-agent") { agentDraft = reply.agentDraft; await prompter.outro("Opening your agent…"); break; } const message = await prompter.text({ message: "Reply to OpenClaw", ...reply.sensitive ? { sensitive: true } : {}, validate: (value) => value.trim() ? void 0 : "Required" }); reply = await request({ method: "openclaw.chat", deviceIdentity, params: { sessionId, message }, timeoutMs: GATEWAY_SYSTEM_AGENT_CHAT_TIMEOUT_MS }); } } catch (error) { if (error instanceof WizardCancelledError) { await prompter.outro("OpenClaw setup paused."); return; } throw error; } await (deps.runTui ?? (await import("./tui-Cjf5Sw9V.js")).runTui)({ config: boundConfig, deliver: false, ...agentDraft === "hatch" ? { message: t("wizard.finalize.bootstrapHatchMessage") } : {}, boundGateway: { url: target.gatewayUrl, ...target.token ? { token: target.token } : {}, ...target.password ? { password: target.password } : {}, ...target.tlsFingerprint ? { tlsFingerprint: target.tlsFingerprint } : {} } }); } }); } //#endregion export { runRemoteGatewayInferenceOnboarding };