openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
318 lines (317 loc) • 13 kB
JavaScript
import { r as defaultRuntime } from "./runtime-DlO3PTAd.mjs";
import { a as isGatewayTransportError } from "./transport-error-CsYJ5b9e.mjs";
import { l as isGatewayClientRequestError, v as resolveDeviceIdentityForGatewayCall } from "./call-B7pNY6_o.mjs";
import { r as t } from "./i18n-Cuh2pEdZ.mjs";
import { n as WizardCancelledError } from "./prompts-DLsO8MlU.mjs";
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) => ({
...candidate,
recommended: false
})),
manualProviders: result.manualProviders.map(({ groupLabel: _groupLabel, ...provider }) => provider),
authOptions: result.authOptions ?? [],
...result.prepareOptions !== void 0 ? { prepareOptions: result.prepareOptions.map(({ actionLabel: _actionLabel, ...option }) => option) } : {},
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 } : {},
...result.setupModel !== void 0 ? { setupModel: result.setupModel } : {},
...result.utilityModel !== void 0 ? { utilityModel: result.utilityModel } : {},
setupComplete: result.setupComplete
};
}
async function answerSetupStep(step, prompter) {
const message = step.message ?? step.title ?? "Continue";
if (step.externalUrl) await prompter.note(step.externalUrl, "Open this URL to continue");
switch (step.type) {
case "note":
await prompter.note(message, step.title);
return;
case "text": return await prompter.text({
message,
sensitive: step.sensitive,
placeholder: step.placeholder,
initialValue: typeof step.initialValue === "string" ? step.initialValue : void 0
});
case "select": return await prompter.select({
message,
options: step.options ?? [],
initialValue: step.initialValue
});
case "multiselect": return await prompter.multiselect({
message,
options: step.options ?? [],
initialValues: Array.isArray(step.initialValue) ? step.initialValue : void 0
});
case "confirm":
case "action":
if (step.executor === "gateway") return;
return await prompter.confirm({
message,
initialValue: typeof step.initialValue === "boolean" ? step.initialValue : void 0
});
}
}
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,
...target.configuredRemote ? {} : { transport: "direct" }
}
}
};
}
function toVerifiedActivationResult(params) {
if (params.activation.modelTarget !== params.requestedModelTarget) throw new Error("Gateway activated a different model role than the selected connection. Refresh model setup and try again.");
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}.`);
if (params.verification.modelTarget !== params.activation.modelTarget) throw new Error("Gateway verified a different model role than the activated connection.");
return {
ok: true,
...params.activation,
latencyMs: params.verification.latencyMs,
lines: []
};
}
/**
* 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-DLG6_hH-.mjs")).callGatewayCli;
const runGuidedOnboarding = deps.runGuidedOnboarding ?? (await import("./commands/onboard-guided.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 && !target.configuredRemote ? { 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 sessionId = randomUUID();
let started = false;
let terminal = false;
const prompter = params.prompter ?? await (deps.createPrompter?.() ?? import("./clack-prompter-DJxqUWif.mjs").then(({ createClackPrompter }) => createClackPrompter()));
let result;
try {
result = await request({
method: "openclaw.setup.activate.start",
params: {
sessionId,
kind: params.kind,
...params.modelTarget ? { modelTarget: params.modelTarget } : {},
...params.agentId ? { agentId: params.agentId } : {},
...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();
}
});
started = true;
terminal = result.done;
while (!result.done) {
params.signal?.throwIfAborted();
const step = result.step;
let answer;
if (step) {
if (result.error) await prompter.note(result.error);
const value = await answerSetupStep(step, prompter);
if (step.type !== "progress" && step.executor !== "gateway") answer = {
stepId: step.id,
value
};
}
result = await request({
method: "wizard.next",
params: {
sessionId,
...answer ? { answer } : {}
},
signal: params.signal,
timeoutMs: activationTimeoutMs(params.kind)
});
terminal = result.done;
}
} catch (error) {
if (!terminal && (started || !isGatewayClientRequestError(error))) try {
await request({
method: "wizard.cancel",
params: {
sessionId,
closeInput: true
},
timeoutMs: activationTimeoutMs(params.kind)
});
} catch (cancelError) {
throw new AggregateError([error, cancelError], "Remote activation failed and its setup session could not be closed.", { cause: cancelError });
}
throw error;
}
if (result.status === "cancelled") throw new WizardCancelledError(result.error);
if (result.activationRejection && result.error) return {
ok: false,
status: result.activationRejection.status,
error: result.error
};
const activation = result.modelActivation;
if (!activation) throw new Error(result.error ?? "Gateway setup ended without a verified 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: {
...params.modelTarget ? { modelTarget: params.modelTarget } : {},
...params.agentId ? { agentId: params.agentId } : {}
},
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") return toVerifiedActivationResult({
activation,
verification,
...params.modelRef ? { requestedModelRef: params.modelRef } : {},
...params.modelTarget ? { requestedModelTarget: params.modelTarget } : {}
});
} 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-DJxqUWif.mjs").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-Dx6bbgsw.mjs")).runTui)({
config: boundConfig,
deliver: false,
...agentDraft === "hatch" ? { message: t("wizard.finalize.bootstrapHatchMessage") } : {},
boundGateway: {
url: target.gatewayUrl,
...target.configuredRemote ? { configuredRemote: true } : {},
...target.token ? { token: target.token } : {},
...target.password ? { password: target.password } : {},
...target.tlsFingerprint ? { tlsFingerprint: target.tlsFingerprint } : {}
}
});
}
});
}
//#endregion
export { runRemoteGatewayInferenceOnboarding };