UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

508 lines (507 loc) 16.2 kB
import { p as resolveIsNixMode } from "./paths-mvMm5bYV.js"; import { t as formatCliCommand } from "./command-format-CKGmlpAQ.js"; import { o as isSystemdUserServiceAvailable } from "./systemd-BXTM6gEb.js"; import { n as defaultRuntime } from "./runtime-B4lgFmsS.js"; import { t as createGatewayCredentialPlan } from "./credential-planner-wESmL_7M.js"; import { n as isGatewaySecretRefUnavailableError, t as GatewaySecretRefUnavailableError } from "./credentials-irvgw8Le.js"; import { t as resolveGatewayAuthToken } from "./auth-token-resolution-AiDzF8d9.js"; import { l as readBestEffortConfig, u as readConfigFileSnapshot } from "./io-Gi7-pyU-.js"; import { n as formatConfigIssueLines } from "./issue-format-CwCoGNbt.js"; import { n as isPluginPackagingRuntimeOutputInvalidConfigSnapshot } from "./recovery-policy-Dve6SNpn.js"; import "./config-C9RxTsn1.js"; import { r as resolveFutureConfigActionBlock } from "./future-version-guard-D6QWZtAq.js"; import { r as checkTokenDrift } from "./service-audit-Bj05_7pm.js"; import { a as startGatewayService, t as describeGatewayServiceRestart } from "./service-MPqQWJ3s.js"; import { o as renderSystemdUnavailableHints } from "./runtime-hints-BXTNKBW9.js"; import { t as isWSL } from "./wsl-Bb_g7JpO.js"; import { i as filterContainerGenericHints, m as createDaemonActionContext, p as buildDaemonServiceSnapshot } from "./shared-BEkveSUA.js"; import { _ as writeGatewayRestartIntentSync, t as clearGatewayRestartIntentSync } from "./restart-CiO1ILZU.js"; import { n as formatPluginPackagingRuntimeOutputRecoveryHint, t as formatInvalidConfigRecoveryHint } from "./config-recovery-hints-DCnttk3l.js"; //#region src/cli/daemon-cli/gateway-token-drift.ts function authModeDisablesToken(mode) { return mode === "password" || mode === "none" || mode === "trusted-proxy"; } function isPasswordFallbackActive(params) { const plan = createGatewayCredentialPlan({ config: params.cfg, env: params.env }); if (plan.authMode !== void 0) return false; return plan.passwordCanWin && !plan.tokenCanWin; } /** Resolve the expected Gateway token for service drift checks, or undefined when token auth is inactive. */ async function resolveGatewayTokenForDriftCheck(params) { const env = params.env ?? process.env; const mode = params.cfg.gateway?.auth?.mode; if (authModeDisablesToken(mode)) return; if (isPasswordFallbackActive({ cfg: params.cfg, env })) return; const resolved = await resolveGatewayAuthToken({ cfg: params.cfg, env, envFallback: "never", unresolvedReasonStyle: "detailed" }); if (resolved.token) return resolved.token; if (!resolved.secretRefConfigured) return; throw new GatewaySecretRefUnavailableError("gateway.auth.token"); } //#endregion //#region src/cli/daemon-cli/lifecycle-core.ts async function maybeAugmentSystemdHints(hints) { if (process.platform !== "linux") return hints; if (await isSystemdUserServiceAvailable().catch(() => false)) return hints; return [...hints, ...renderSystemdUnavailableHints({ wsl: await isWSL(), kind: "generic_unavailable" })]; } function emitActionMessage(params) { params.emit(params.payload); if (!params.json && params.payload.message) defaultRuntime.log(params.payload.message); } async function handleServiceNotLoaded(params) { const hints = filterContainerGenericHints(await maybeAugmentSystemdHints(params.renderStartHints())); params.emit({ ok: true, result: "not-loaded", message: `${params.serviceNoun} service ${params.service.notLoadedText}.`, hints, service: buildDaemonServiceSnapshot(params.service, params.loaded) }); if (!params.json) { defaultRuntime.log(`${params.serviceNoun} service ${params.service.notLoadedText}.`); for (const hint of hints) defaultRuntime.log(`Start with: ${hint}`); } } async function resolveServiceLoadedOrFail(params) { try { return await params.service.isLoaded({ env: process.env }); } catch (err) { params.fail(`${params.serviceNoun} service check failed: ${String(err)}`); return null; } } function formatPluginPackagingRuntimeOutputRecoveryHints() { return formatPluginPackagingRuntimeOutputRecoveryHint().split("\n"); } async function getConfigActionPreflightFailure(action) { let snapshot; try { snapshot = await readConfigFileSnapshot(); if (snapshot.exists && !snapshot.valid) return { message: snapshot.issues.length > 0 ? formatConfigIssueLines(snapshot.issues, "", { normalizeRoot: true }).join("\n") : "Unknown validation issue.", ...isPluginPackagingRuntimeOutputInvalidConfigSnapshot(snapshot) ? { hints: formatPluginPackagingRuntimeOutputRecoveryHints() } : {} }; } catch { return null; } const futureBlock = resolveFutureConfigActionBlock({ action, snapshot }); if (futureBlock) return { message: futureBlock.message, hints: futureBlock.hints }; return null; } async function runServiceUninstall(params) { const { stdout, emit, fail } = createDaemonActionContext({ action: "uninstall", json: Boolean(params.opts?.json) }); if (resolveIsNixMode(process.env)) { fail("Nix mode detected; service uninstall is disabled."); return; } { const preflight = await getConfigActionPreflightFailure("uninstall the gateway service"); if (preflight) { fail(`${params.serviceNoun} uninstall blocked: ${preflight.message}`, preflight.hints); return; } } let loaded; try { loaded = await params.service.isLoaded({ env: process.env }); } catch { loaded = false; } if (loaded && params.stopBeforeUninstall) try { await params.service.stop({ env: process.env, stdout }); } catch {} try { await params.service.uninstall({ env: process.env, stdout }); } catch (err) { fail(`${params.serviceNoun} uninstall failed: ${String(err)}`); return; } try { loaded = await params.service.isLoaded({ env: process.env }); } catch { loaded = false; } if (loaded && params.assertNotLoadedAfterUninstall) { fail(`${params.serviceNoun} service still loaded after uninstall.`); return; } emit({ ok: true, result: "uninstalled", service: buildDaemonServiceSnapshot(params.service, loaded) }); } async function runServiceStart(params) { const json = Boolean(params.opts?.json); const { stdout, emit, fail } = createDaemonActionContext({ action: "start", json }); const loaded = await resolveServiceLoadedOrFail({ serviceNoun: params.serviceNoun, service: params.service, fail }); if (loaded === null) return; { const preflight = await getConfigActionPreflightFailure("start the gateway service"); if (preflight) { fail(preflight.hints ? `${params.serviceNoun} start blocked: ${preflight.message}` : `${params.serviceNoun} aborted: config is invalid.\n${preflight.message}\n${formatInvalidConfigRecoveryHint()}`, preflight.hints); return; } } if (!loaded) try { const handled = await params.onNotLoaded?.({ json, stdout, fail }); if (handled) { emit({ ok: true, result: handled.result, message: handled.message, warnings: handled.warnings, service: buildDaemonServiceSnapshot(params.service, handled.loaded ?? false) }); if (!json && handled.message) defaultRuntime.log(handled.message); return; } } catch (err) { const hints = params.renderStartHints(); fail(`${params.serviceNoun} start failed: ${String(err)}`, hints); return; } try { const startResult = await startGatewayService(params.service, { env: process.env, stdout }); if (startResult.outcome === "missing-install") { await handleServiceNotLoaded({ serviceNoun: params.serviceNoun, service: params.service, loaded: startResult.state.loaded, renderStartHints: params.renderStartHints, json, emit }); return; } if (startResult.outcome === "scheduled") { emitActionMessage({ json, emit, payload: { ok: true, result: "scheduled", message: describeGatewayServiceRestart(params.serviceNoun, { outcome: "scheduled" }).message, service: buildDaemonServiceSnapshot(params.service, startResult.state.loaded) } }); return; } if (startResult.outcome === "repair-required") { try { const handled = await params.repairLoadedService?.({ json, stdout, fail, state: startResult.state, issues: startResult.issues }); if (handled) { emit({ ok: true, result: handled.result, message: handled.message, warnings: handled.warnings, service: buildDaemonServiceSnapshot(params.service, handled.loaded ?? true) }); if (!json && handled.message) defaultRuntime.log(handled.message); return; } } catch (err) { const hints = params.renderStartHints(); fail(`${params.serviceNoun} repair failed: ${String(err)}`, hints); return; } fail(`${params.serviceNoun} service needs repair before it can start: ${startResult.issues.map((issue) => issue.message).join("; ")}`, [formatCliCommand("openclaw gateway install --force")]); return; } emit({ ok: true, result: "started", service: buildDaemonServiceSnapshot(params.service, startResult.state.loaded) }); } catch (err) { const hints = params.renderStartHints(); fail(`${params.serviceNoun} start failed: ${String(err)}`, hints); } } async function runServiceStop(params) { const json = Boolean(params.opts?.json); const { stdout, emit, fail } = createDaemonActionContext({ action: "stop", json }); const loaded = await resolveServiceLoadedOrFail({ serviceNoun: params.serviceNoun, service: params.service, fail }); if (loaded === null) return; { const preflight = await getConfigActionPreflightFailure("stop the gateway service"); if (preflight) { fail(`${params.serviceNoun} stop blocked: ${preflight.message}`, preflight.hints); return; } } if (!loaded) { if (params.stopWhenNotLoaded) { try { await params.service.stop({ env: process.env, stdout, disable: params.opts?.disable }); } catch (err) { fail(`${params.serviceNoun} stop failed: ${String(err)}`); return; } emit({ ok: true, result: "stopped", service: buildDaemonServiceSnapshot(params.service, false) }); return; } try { const handled = await params.onNotLoaded?.({ json, stdout, fail }); if (handled) { emit({ ok: true, result: handled.result, message: handled.message, warnings: handled.warnings, service: buildDaemonServiceSnapshot(params.service, false) }); if (!json && handled.message) defaultRuntime.log(handled.message); return; } } catch (err) { fail(`${params.serviceNoun} stop failed: ${String(err)}`); return; } emit({ ok: true, result: "not-loaded", message: `${params.serviceNoun} service ${params.service.notLoadedText}.`, service: buildDaemonServiceSnapshot(params.service, loaded) }); if (!json) defaultRuntime.log(`${params.serviceNoun} service ${params.service.notLoadedText}.`); return; } try { await params.service.stop({ env: process.env, stdout, disable: params.opts?.disable }); } catch (err) { fail(`${params.serviceNoun} stop failed: ${String(err)}`); return; } let stopped; try { stopped = await params.service.isLoaded({ env: process.env }); } catch { stopped = false; } emit({ ok: true, result: "stopped", service: buildDaemonServiceSnapshot(params.service, stopped) }); } async function runServiceRestart(params) { const json = Boolean(params.opts?.json); const { stdout, emit, fail } = createDaemonActionContext({ action: "restart", json }); const warnings = []; const restartIntent = params.opts?.restartIntent; let handledRecovery = null; let recoveredLoadedState = null; const emitScheduledRestart = (restartStatus, serviceLoaded) => { emitActionMessage({ json, emit, payload: { ok: true, result: restartStatus.daemonActionResult, message: restartStatus.message, service: buildDaemonServiceSnapshot(params.service, serviceLoaded), warnings: warnings.length ? warnings : void 0 } }); return true; }; const loaded = await resolveServiceLoadedOrFail({ serviceNoun: params.serviceNoun, service: params.service, fail }); if (loaded === null) return false; { const preflight = await getConfigActionPreflightFailure("restart the gateway service"); if (preflight) { fail(preflight.hints ? `${params.serviceNoun} restart blocked: ${preflight.message}` : `${params.serviceNoun} aborted: config is invalid.\n${preflight.message}\n${formatInvalidConfigRecoveryHint()}`, preflight.hints); return false; } } if (!loaded) { try { handledRecovery = await params.onNotLoaded?.({ json, stdout, fail }) ?? null; } catch (err) { fail(`${params.serviceNoun} restart failed: ${String(err)}`); return false; } if (!handledRecovery) { await handleServiceNotLoaded({ serviceNoun: params.serviceNoun, service: params.service, loaded, renderStartHints: params.renderStartHints, json, emit }); return false; } if (handledRecovery.warnings?.length) warnings.push(...handledRecovery.warnings); recoveredLoadedState = handledRecovery.loaded ?? null; } if (loaded && params.checkTokenDrift) try { const command = await params.service.readCommand(process.env); const serviceToken = command?.environment?.OPENCLAW_GATEWAY_TOKEN; const driftIssue = checkTokenDrift({ serviceToken, configToken: await resolveGatewayTokenForDriftCheck({ cfg: await readBestEffortConfig(), env: { ...process.env, ...command?.environment } }) }); if (driftIssue) { const warning = driftIssue.detail ? `${driftIssue.message} ${driftIssue.detail}` : driftIssue.message; warnings.push(warning); if (!json) { defaultRuntime.log(`\n⚠️ ${driftIssue.message}`); if (driftIssue.detail) defaultRuntime.log(` ${driftIssue.detail}\n`); } } } catch (err) { if (isGatewaySecretRefUnavailableError(err, "gateway.auth.token")) { const warning = "Unable to verify gateway token drift: gateway.auth.token SecretRef is configured but unavailable in this command path."; warnings.push(warning); if (!json) defaultRuntime.log(`\n⚠️ ${warning}\n`); } } try { let restartResult = { outcome: "completed" }; if (loaded) { let wroteRestartIntent = false; if (params.serviceNoun === "Gateway") wroteRestartIntent = writeGatewayRestartIntentSync({ targetPid: (await params.service.readRuntime(process.env).catch(() => null))?.pid, reason: "gateway.restart", ...restartIntent ? { intent: restartIntent } : {} }); try { restartResult = await params.service.restart({ env: process.env, stdout }); } catch (err) { if (wroteRestartIntent) clearGatewayRestartIntentSync(); throw err; } } let restartStatus = describeGatewayServiceRestart(params.serviceNoun, restartResult); if (restartStatus.scheduled) return emitScheduledRestart(restartStatus, loaded || recoveredLoadedState === true); if (params.postRestartCheck) { const postRestartResult = await params.postRestartCheck({ json, stdout, warnings, fail }); if (postRestartResult) { restartStatus = describeGatewayServiceRestart(params.serviceNoun, postRestartResult); if (restartStatus.scheduled) return emitScheduledRestart(restartStatus, loaded || recoveredLoadedState === true); } } let restarted = loaded; if (loaded) try { restarted = await params.service.isLoaded({ env: process.env }); } catch { restarted = true; } else if (recoveredLoadedState !== null) restarted = recoveredLoadedState; emit({ ok: true, result: "restarted", message: handledRecovery?.message, service: buildDaemonServiceSnapshot(params.service, restarted), warnings: warnings.length ? warnings : void 0 }); if (!json && handledRecovery?.message) defaultRuntime.log(handledRecovery.message); return true; } catch (err) { const hints = params.renderStartHints(); fail(`${params.serviceNoun} restart failed: ${String(err)}`, hints); return false; } } //#endregion export { runServiceUninstall as i, runServiceStart as n, runServiceStop as r, runServiceRestart as t };