UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

466 lines (465 loc) • 21.3 kB
import { c as normalizeOptionalLowercaseString } from "./string-coerce-6kR-86XC.mjs"; import { t as ExitError } from "./runtime-BC29JSZp.mjs"; import { t as formatCliCommand } from "./command-format-Cte1SWM4.mjs"; import { b as resolveFutureConfigActionBlock, f as resetPublishedConfigRuntimeEnv, o as createConfigRuntimeEnv, r as cloneEnvWithPlatformSemantics, v as ALLOW_OLDER_BINARY_DESTRUCTIVE_ACTIONS_ENV, y as formatFutureConfigActionBlock } from "./config-env-vars-oFV7TqIg.mjs"; import { o as withArtifactPreservingStateReads } from "./openclaw-state-db-readonly-BLZyK3So.mjs"; import { r as CONFIG_AUDIT_STORE_LABEL } from "./io.audit-B4vcApAC.mjs"; import { t as GATEWAY_CONFIG_SELECTION_ENV_KEYS } from "./gateway-env-selection-Dvz2Zk9I.mjs"; import { t as getGatewayRunRuntimeHooks } from "./runtime-hooks-Dp2OHV46.mjs"; import { t as describeConfigSnapshotInputChange } from "./snapshot-inputs-DRbOtoHS.mjs"; //#region src/cli/gateway-cli/future-config-guard.ts function resolveGatewayRunFutureConfigBlock(params) { const processServiceMode = Boolean(process.env.OPENCLAW_SERVICE_MARKER?.trim()); const candidateConfig = params.config ?? (params.snapshot?.valid ? params.snapshot.sourceConfig ?? params.snapshot.config : void 0); const candidateServiceMode = !params.opts.reset && Boolean(candidateConfig ? createConfigRuntimeEnv(candidateConfig, process.env).OPENCLAW_SERVICE_MARKER?.trim() : void 0); const serviceMode = processServiceMode || candidateServiceMode; const futureAction = params.opts.reset ? { action: "reset the dev gateway state", exitCode: 1 } : serviceMode ? { action: "start the gateway service", exitCode: 78 } : params.opts.force ? { action: "force-kill gateway port listeners", exitCode: 1 } : { action: "run gateway state preparation", exitCode: 1 }; const guardEnv = serviceMode ? cloneEnvWithPlatformSemantics(process.env) : process.env; if (serviceMode) delete guardEnv[ALLOW_OLDER_BINARY_DESTRUCTIVE_ACTIONS_ENV]; const block = resolveFutureConfigActionBlock({ action: futureAction.action, snapshot: params.snapshot, config: params.config, env: guardEnv }); return block ? { block, exitCode: futureAction.exitCode, serviceMode } : null; } function enforceGatewayRunFutureConfigGuard(params) { const resolved = resolveGatewayRunFutureConfigBlock(params); if (!resolved) return true; if (resolved.serviceMode) delete process.env[ALLOW_OLDER_BINARY_DESTRUCTIVE_ACTIONS_ENV]; params.runtime.error(formatFutureConfigActionBlock(resolved.block)); params.runtime.exit(resolved.exitCode); return false; } //#endregion //#region src/cli/gateway-cli/pre-bootstrap.ts let selectedGatewayRunEnvironment; let appliedGatewayRunConfigEnvironment; let lastGuardedGatewayRunSnapshot; let preparedGatewayRunBootstrap; let preparedGatewayRunReset; let gatewayRunTargetSelectedByConfig = false; function getGatewayStartGuardErrors(params) { if ((params.allowUnconfigured ?? preparedGatewayRunBootstrap?.allowUnconfigured) || params.mode === "local" || !params.configExists && preparedGatewayRunBootstrap?.dev) return []; if (!params.configExists) return [`Missing config. Run \`${formatCliCommand("openclaw setup")}\` or set gateway.mode=local (or pass --allow-unconfigured).`]; return [params.mode === void 0 ? [ "Gateway start blocked: existing config is missing gateway.mode.", "Treat this as suspicious or clobbered config.", `Re-run \`${formatCliCommand("openclaw onboard --mode local")}\` or \`${formatCliCommand("openclaw setup")}\`, set gateway.mode=local manually, or pass --allow-unconfigured.` ].join(" ") : `Gateway start blocked: set gateway.mode=local (current: ${params.mode}) or pass --allow-unconfigured.`, `Config write audit: ${CONFIG_AUDIT_STORE_LABEL}`]; } async function pinGatewayRunRuntimePaths() { const [{ pinRuntimePaths }, { pinConfigDir }] = await Promise.all([import("./paths-Cwl4mMlv.mjs"), import("./utils-IThmeNVh.mjs")]); pinRuntimePaths(process.env); pinConfigDir(process.env); } const GATEWAY_RESET_SELECTION_ENV_KEYS = /* @__PURE__ */ new Set([ ...GATEWAY_CONFIG_SELECTION_ENV_KEYS, "OPENCLAW_PROFILE", "OPENCLAW_WORKSPACE_DIR" ]); function resolveGatewayConfigSelectionSignature(env) { return JSON.stringify([...GATEWAY_CONFIG_SELECTION_ENV_KEYS].map((key) => [key, env[key]])); } function snapshotGatewayConfigSelectionEnvironment(env) { return Object.fromEntries([...GATEWAY_CONFIG_SELECTION_ENV_KEYS].map((key) => [key, env[key]])); } function restoreGatewayConfigSelectionEnvironment(snapshot) { for (const key of GATEWAY_CONFIG_SELECTION_ENV_KEYS) { const value = snapshot[key]; if (value === void 0) delete process.env[key]; else process.env[key] = value; } } function resolveInvocationDestructiveOverride() { if (process.env.OPENCLAW_SERVICE_MARKER?.trim()) { delete process.env[ALLOW_OLDER_BINARY_DESTRUCTIVE_ACTIONS_ENV]; return; } return process.env[ALLOW_OLDER_BINARY_DESTRUCTIVE_ACTIONS_ENV]; } function applyInvocationDestructiveOverride(value) { if (process.env.OPENCLAW_SERVICE_MARKER?.trim() || value === void 0) delete process.env[ALLOW_OLDER_BINARY_DESTRUCTIVE_ACTIONS_ENV]; else process.env[ALLOW_OLDER_BINARY_DESTRUCTIVE_ACTIONS_ENV] = value; } function restoreGatewayEnvChanges(params) { const keys = /* @__PURE__ */ new Set([...Object.keys(params.before), ...Object.keys(params.after)]); for (const key of keys) { const preservedKey = process.platform === "win32" ? key.toUpperCase() : key; if (params.preservedKeys?.has(preservedKey)) continue; if (params.before[key] === params.after[key] || process.env[key] !== params.after[key]) continue; const previous = params.before[key]; if (previous === void 0) delete process.env[key]; else process.env[key] = previous; } } function restoreSupersededGatewaySelectionEnv(params) { restoreGatewayEnvChanges({ before: params.beforeCurrentPass, after: { ...process.env }, preservedKeys: GATEWAY_CONFIG_SELECTION_ENV_KEYS }); if (params.environmentSelection) restoreGatewayEnvChanges({ before: params.environmentSelection.before, after: params.environmentSelection.after, preservedKeys: GATEWAY_CONFIG_SELECTION_ENV_KEYS }); } function restoreAppliedGatewayRunConfigEnvironment(preserveSelection = true) { const applied = appliedGatewayRunConfigEnvironment; appliedGatewayRunConfigEnvironment = void 0; if (!applied) return; restoreGatewayEnvChanges({ before: applied.before, after: applied.after, ...preserveSelection ? { preservedKeys: GATEWAY_CONFIG_SELECTION_ENV_KEYS } : {} }); } async function readGuardedGatewayRunConfig(params) { const { readConfigFileSnapshot } = await import("./config/config.js"); const { createConfigIO } = await import("./io.factory-Gmw-24AO.mjs"); return await withArtifactPreservingStateReads(async () => { const current = await readConfigFileSnapshot({ isolateEnv: true, observe: false, pluginValidation: "core-only" }); const guard = (snapshot) => enforceGatewayRunFutureConfigGuard({ ...params, snapshot }); if (!guard(current)) return null; const recovery = await createConfigIO({ configPath: current.path, env: cloneEnvWithPlatformSemantics(process.env), observe: false, pluginValidation: "core-only" }).prepareConfigRecovery(current); return recovery ? guard(recovery.snapshot) ? recovery.snapshot : null : current; }); } function describeGatewayRunConfigChange(expected, current, options = {}) { return current.valid !== expected.valid ? "config validity changed" : describeConfigSnapshotInputChange(expected, current, { ...options, compareResolvedConfig: current.valid }); } function resolveGatewayConfigSelectionDeclarationSignature(entries) { const normalized = new Map(Object.entries(entries).map(([key, value]) => [key.toUpperCase(), value])); return JSON.stringify([...GATEWAY_CONFIG_SELECTION_ENV_KEYS].map((key) => [key, normalized.get(key)])); } async function guardGatewayRunSelectedConfig(params) { lastGuardedGatewayRunSnapshot = void 0; const [applyTrustedGatewayEnv, { applyConfigEnvVars }, { normalizeEnv }, { normalizeStateDirEnv }, { collectEnvSecretRefIds }, { clearMissingManagedServiceEnvKeys, readManagedSystemdServiceEnvKeysFromEnvironment }] = await Promise.all([ createTrustedGatewayEnvLoader(), import("./config-env-vars-BOJcQbrc.mjs"), import("./env-DBx4_TNW.mjs"), import("./paths-Cwl4mMlv.mjs"), import("./resolution-facts-DnxJOtUp.mjs"), import("./service-managed-env-Cj6UoZ41.mjs") ]); const invocationDestructiveOverride = resolveInvocationDestructiveOverride(); if (params.environmentSelection) { restoreAppliedGatewayRunConfigEnvironment(); restoreGatewayEnvChanges({ before: params.environmentSelection.before, after: params.environmentSelection.after, preservedKeys: GATEWAY_CONFIG_SELECTION_ENV_KEYS }); } const applySelectedConfigEnv = (snapshot) => { restoreAppliedGatewayRunConfigEnvironment(params.opts.reset !== true); if (snapshot.valid && params.opts.reset !== true) { const envBeforeApply = { ...process.env }; applyConfigEnvVars(snapshot.sourceConfig, process.env); normalizeStateDirEnv(process.env); normalizeEnv(); appliedGatewayRunConfigEnvironment = { before: envBeforeApply, after: { ...process.env } }; } applyInvocationDestructiveOverride(invocationDestructiveOverride); }; for (;;) { const envBeforeTrustedApply = { ...process.env }; const trustedSelectionSignature = resolveGatewayConfigSelectionSignature(process.env); const trustedEnvLoad = applyTrustedGatewayEnv(invocationDestructiveOverride); if (resolveGatewayConfigSelectionSignature(process.env) !== trustedSelectionSignature) { if (trustedEnvLoad.stateEnvAppliedKeys.some((key) => GATEWAY_CONFIG_SELECTION_ENV_KEYS.has(key.toUpperCase()))) { const fallbackSelectorKeys = new Set(trustedEnvLoad.gatewayEnvAppliedKeys.map((key) => key.toUpperCase()).filter((key) => GATEWAY_CONFIG_SELECTION_ENV_KEYS.has(key))); restoreGatewayEnvChanges({ before: envBeforeTrustedApply, after: { ...process.env }, preservedKeys: new Set([...GATEWAY_CONFIG_SELECTION_ENV_KEYS].filter((key) => !fallbackSelectorKeys.has(key))) }); } restoreSupersededGatewaySelectionEnv({ beforeCurrentPass: envBeforeTrustedApply, environmentSelection: params.environmentSelection }); continue; } const snapshot = await readGuardedGatewayRunConfig(params); if (!snapshot) return false; if (!snapshot.valid) { lastGuardedGatewayRunSnapshot = snapshot; return true; } clearMissingManagedServiceEnvKeys({ environment: process.env, managedKeys: readManagedSystemdServiceEnvKeysFromEnvironment(process.env), presentKeys: trustedEnvLoad.dotenvPresentKeys, preserveKeys: collectEnvSecretRefIds(snapshot.sourceConfig) }); const selectionSignature = resolveGatewayConfigSelectionSignature(process.env); applySelectedConfigEnv(snapshot); if (resolveGatewayConfigSelectionSignature(process.env) !== selectionSignature) { gatewayRunTargetSelectedByConfig = true; restoreSupersededGatewaySelectionEnv({ beforeCurrentPass: envBeforeTrustedApply, environmentSelection: params.environmentSelection }); continue; } lastGuardedGatewayRunSnapshot = snapshot; return true; } } async function guardGatewayRunReset(params) { gatewayRunTargetSelectedByConfig = false; const envBeforeGuard = { ...process.env }; try { return await guardGatewayRunSelectedConfig(params); } finally { restoreAppliedGatewayRunConfigEnvironment(false); restoreGatewayEnvChanges({ before: envBeforeGuard, after: { ...process.env }, preservedKeys: GATEWAY_RESET_SELECTION_ENV_KEYS }); } } async function recheckGatewayRunReset(params) { const expected = preparedGatewayRunReset; preparedGatewayRunReset = void 0; const rejectDrift = async () => { if (expected) { restoreGatewayConfigSelectionEnvironment(expected.selectionEnvironment); await pinGatewayRunRuntimePaths(); } params.runtime.error("Refusing to reset the dev gateway state because the selected config or state target changed during startup. Retry the reset so the new target can be validated."); params.runtime.exit(1); return false; }; if (!expected || resolveGatewayConfigSelectionSignature(process.env) !== expected.selectionSignature) return await rejectDrift(); if (!await guardGatewayRunReset(params)) return false; const current = lastGuardedGatewayRunSnapshot; if (resolveGatewayConfigSelectionSignature(process.env) !== expected.selectionSignature || !current || describeGatewayRunConfigChange(expected.snapshot, current)) return await rejectDrift(); return true; } async function applyFinalGatewayRunConfigEnv(params) { const preparedSnapshot = preparedGatewayRunBootstrap?.snapshot; preparedGatewayRunBootstrap = void 0; if (!params.snapshot.valid) { restoreAppliedGatewayRunConfigEnvironment(false); if (preparedSnapshot) { params.runtime.error("Refusing to start the gateway because the final config read became invalid. Retry startup after fixing the config."); params.runtime.exit(1); return false; } await pinGatewayRunRuntimePaths(); return true; } const invocationDestructiveOverride = resolveInvocationDestructiveOverride(); const envBeforeApply = { ...process.env }; const selectionSignature = resolveGatewayConfigSelectionSignature(process.env); const [{ applyConfigEnvVars, collectConfigRuntimeEnvOwnership, collectConfigRuntimeEnvVars, initializePublishedConfigRuntimeEnv }, { normalizeEnv }, { normalizeStateDirEnv }, { clearShellEnvAppliedKeys }] = await Promise.all([ import("./config-env-vars-BOJcQbrc.mjs"), import("./env-DBx4_TNW.mjs"), import("./paths-Cwl4mMlv.mjs"), import("./shell-env-B-o388P9.mjs") ]); const finalConfigEnv = collectConfigRuntimeEnvVars(params.snapshot.sourceConfig); if (preparedSnapshot && resolveGatewayConfigSelectionDeclarationSignature(collectConfigRuntimeEnvVars(preparedSnapshot.sourceConfig)) !== resolveGatewayConfigSelectionDeclarationSignature(finalConfigEnv)) { params.runtime.error("Refusing to start the gateway because the final config read changed config or state selection. Retry startup so the selected target can be validated."); params.runtime.exit(1); return false; } restoreAppliedGatewayRunConfigEnvironment(); const envBeforeConfigApply = { ...process.env }; const replacedLowerPrecedenceKeys = []; applyConfigEnvVars(params.snapshot.sourceConfig, process.env, { lowerPrecedenceEnv: params.lowerPrecedenceEnv, onLowerPrecedenceKeysReplaced: (keys) => { replacedLowerPrecedenceKeys.push(...keys); clearShellEnvAppliedKeys(keys); } }); normalizeStateDirEnv(process.env); normalizeEnv(); applyInvocationDestructiveOverride(invocationDestructiveOverride); appliedGatewayRunConfigEnvironment = { before: envBeforeApply, after: { ...process.env } }; if (resolveGatewayConfigSelectionSignature(process.env) === selectionSignature) { initializePublishedConfigRuntimeEnv(params.snapshot.sourceConfig, { ownedEnv: collectConfigRuntimeEnvOwnership(params.snapshot.sourceConfig, envBeforeConfigApply, process.env, { replacedLowerPrecedenceKeys }) }); return true; } appliedGatewayRunConfigEnvironment = void 0; restoreGatewayEnvChanges({ before: envBeforeApply, after: { ...process.env } }); params.runtime.error("Refusing to start the gateway because the final config read changed config or state selection. Retry startup so the selected target can be validated."); params.runtime.exit(1); return false; } function clearGatewayRunConfigEnvironment() { restoreAppliedGatewayRunConfigEnvironment(); resetPublishedConfigRuntimeEnv(); } async function createTrustedGatewayEnvLoader() { const [path, { isConfigRuntimeEnvVarAllowed }, { loadGlobalRuntimeDotEnvFiles }, { normalizeEnv }, { normalizeStateDirEnv, resolveStateDir }, { resolveConfigDir }, { readManagedSystemdServiceEnvKeysFromEnvironment }] = await Promise.all([ import("node:path"), import("./config-env-vars-BOJcQbrc.mjs"), import("./dotenv-global-B5EYuDlJ.mjs"), import("./env-DBx4_TNW.mjs"), import("./paths-Cwl4mMlv.mjs"), import("./utils-IThmeNVh.mjs"), import("./service-managed-env-Cj6UoZ41.mjs") ]); return (invocationDestructiveOverride) => { normalizeStateDirEnv(process.env); const stateEnvPath = path.join(resolveStateDir(process.env), ".env"); const configEnvPath = path.join(resolveConfigDir(process.env), ".env"); const loaded = loadGlobalRuntimeDotEnvFiles({ ...gatewayRunTargetSelectedByConfig ? { entryFilter: isConfigRuntimeEnvVarAllowed } : {}, overrideKeys: readManagedSystemdServiceEnvKeysFromEnvironment(process.env), quiet: true, stateEnvPath, ...path.resolve(stateEnvPath) === path.resolve(configEnvPath) ? {} : { additionalEnvPaths: [configEnvPath] } }); normalizeStateDirEnv(process.env); normalizeEnv(); applyInvocationDestructiveOverride(invocationDestructiveOverride); return loaded; }; } async function reloadTrustedGatewayRunEnvironment(params) { const applyTrustedGatewayEnv = await createTrustedGatewayEnvLoader(); const envBeforeReload = { ...process.env }; const selectionSignature = resolveGatewayConfigSelectionSignature(process.env); const invocationDestructiveOverride = resolveInvocationDestructiveOverride(); applyTrustedGatewayEnv(invocationDestructiveOverride); if (resolveGatewayConfigSelectionSignature(process.env) !== selectionSignature) { restoreGatewayEnvChanges({ before: envBeforeReload, after: { ...process.env } }); applyInvocationDestructiveOverride(invocationDestructiveOverride); await pinGatewayRunRuntimePaths(); params.runtime.error("Refusing to start the gateway because trusted dotenv reload after startup mutations changed config or state selection. Retry startup so the selected target can be validated."); params.runtime.exit(1); return false; } await pinGatewayRunRuntimePaths(); return true; } async function selectGatewayRunEnvironment(params) { gatewayRunTargetSelectedByConfig = false; preparedGatewayRunBootstrap = void 0; preparedGatewayRunReset = void 0; restoreAppliedGatewayRunConfigEnvironment(params.opts.reset !== true); const envBeforeGuard = { ...process.env }; selectedGatewayRunEnvironment = void 0; let guarded; try { guarded = await guardGatewayRunSelectedConfig(params); } finally { if (params.opts.reset) { restoreAppliedGatewayRunConfigEnvironment(false); restoreGatewayEnvChanges({ before: envBeforeGuard, after: { ...process.env }, preservedKeys: GATEWAY_RESET_SELECTION_ENV_KEYS }); } } selectedGatewayRunEnvironment = { before: envBeforeGuard, after: { ...process.env } }; await pinGatewayRunRuntimePaths(); return guarded; } async function prepareGatewayRunBootstrap(params) { preparedGatewayRunReset = void 0; await getGatewayRunRuntimeHooks().releaseManagedProxy?.(); const environmentSelection = selectedGatewayRunEnvironment; selectedGatewayRunEnvironment = void 0; if (!environmentSelection) gatewayRunTargetSelectedByConfig = false; const guarded = params.opts.reset ? await guardGatewayRunReset(params) : await guardGatewayRunSelectedConfig({ ...params, environmentSelection }); await pinGatewayRunRuntimePaths(); const shouldBootstrap = guarded && !params.opts.reset; preparedGatewayRunBootstrap = shouldBootstrap && lastGuardedGatewayRunSnapshot ? { snapshot: lastGuardedGatewayRunSnapshot, allowUnconfigured: params.opts.allowUnconfigured === true, dev: Boolean(params.opts.dev) || normalizeOptionalLowercaseString(process.env.OPENCLAW_PROFILE) === "dev" } : void 0; if (guarded && params.opts.reset && lastGuardedGatewayRunSnapshot) preparedGatewayRunReset = { selectionEnvironment: snapshotGatewayConfigSelectionEnvironment(process.env), selectionSignature: resolveGatewayConfigSelectionSignature(process.env), snapshot: lastGuardedGatewayRunSnapshot }; return shouldBootstrap; } async function recheckGatewayRunBootstrap(params) { const deferredExitRuntime = { ...params.runtime, exit: (code) => { throw new ExitError(code); } }; const expected = preparedGatewayRunBootstrap?.snapshot; if (!expected) { params.runtime.error("Refusing to run gateway state preparation without a prepared config snapshot. Retry startup."); throw new ExitError(1); } const current = params.snapshot ? enforceGatewayRunFutureConfigGuard({ opts: params.opts, runtime: deferredExitRuntime, snapshot: params.snapshot }) ? params.snapshot : null : await readGuardedGatewayRunConfig({ ...params, runtime: deferredExitRuntime }); if (!current) return false; const change = describeGatewayRunConfigChange(expected, current, { allowPathChange: params.snapshot !== void 0 }); if (!change) return true; params.runtime.error(`Refusing to run gateway state preparation because the selected config changed during startup (${change}). Retry startup so the new config can be validated.`); throw new ExitError(1); } //#endregion export { recheckGatewayRunBootstrap as a, selectGatewayRunEnvironment as c, prepareGatewayRunBootstrap as i, enforceGatewayRunFutureConfigGuard as l, clearGatewayRunConfigEnvironment as n, recheckGatewayRunReset as o, getGatewayStartGuardErrors as r, reloadTrustedGatewayRunEnvironment as s, applyFinalGatewayRunConfigEnv as t };