openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
190 lines (189 loc) • 11 kB
JavaScript
import { d as getPluginMetadataSnapshotCache, n as adoptProcessPluginCache } from "./plugin-cache-Bn9I3v9Q.mjs";
import { t as ExitError } from "./runtime-BC29JSZp.mjs";
import { S as resolveIsConfigReadOnly } from "./paths-FklbMVOH.mjs";
import { s as resolveOpenClawStateSqlitePath } from "./openclaw-state-db.paths-BvBEgHyD.mjs";
import { a as isExistingOpenClawStateSchema, i as getExistingOpenClawStateSchemaPath } from "./openclaw-state-db-schema-policy-BxAJZRG6.mjs";
import { a as isConfigReadFailure, n as createConfigReadError, r as createInvalidConfigError, t as configFailureHeading } from "./io.invalid-config-DkKUeCUl.mjs";
import { D as setRuntimeConfigSnapshot } from "./runtime-snapshot-CifcQ780.mjs";
import { c as readConfigFileSnapshot } from "./io.runtime-FunTy0y1.mjs";
import "./config-B-SAEPyK.mjs";
import { a as withSuppressedNotes } from "./note-BpWs6H5N.mjs";
//#region src/cli/program/config-guard.ts
const ALLOWED_INVALID_COMMANDS = /* @__PURE__ */ new Set([
"audit",
"doctor",
"logs",
"health",
"help",
"status"
]);
const ALLOWED_INVALID_GATEWAY_SUBCOMMANDS = /* @__PURE__ */ new Set([
"run",
"status",
"probe",
"health",
"discover",
"call",
"install",
"uninstall",
"start",
"stop",
"restart"
]);
let didRunStartupConfigPreflight = false;
let configSnapshotPromise = null;
function resetConfigGuardStateForTests() {
didRunStartupConfigPreflight = false;
configSnapshotPromise = null;
}
function shouldPrepareGatewayState(commandPath) {
const commandName = commandPath[0];
const subcommandName = commandPath[1];
return commandName === "gateway" && (subcommandName === void 0 || subcommandName === "run" || subcommandName.trim() === "");
}
function isGatewayStartupCommand(commandPath) {
const [commandName, subcommandName] = commandPath;
return commandName === "gateway" && (subcommandName === void 0 || subcommandName === "run" || subcommandName === "start" || subcommandName === "restart");
}
async function getConfigSnapshot(options, measure) {
if (options?.observe === false) return readConfigFileSnapshot({
...options,
...measure ? { measure } : {}
});
if (!configSnapshotPromise) {
const pendingSnapshot = readConfigFileSnapshot(measure ? { measure } : void 0);
configSnapshotPromise = pendingSnapshot;
pendingSnapshot.catch(() => {
if (configSnapshotPromise === pendingSnapshot) configSnapshotPromise = null;
});
}
return configSnapshotPromise;
}
async function ensureConfigReady(params, recoveryDeps) {
const commandPath = params.commandPath ?? [];
const commandName = commandPath[0];
const subcommandName = commandPath[1];
const existingStatePath = getExistingOpenClawStateSchemaPath();
const isManagedNodeRuntime = existingStatePath !== void 0 && (commandName === "node" && subcommandName === "run" || commandName === "connect");
if (existingStatePath !== void 0) {
if (!isManagedNodeRuntime) throw new Error("The managed node runtime cannot run shared-state maintenance commands.");
if (!isExistingOpenClawStateSchema(resolveOpenClawStateSqlitePath())) throw new Error("The managed node runtime state directory changed after launcher admission.");
}
const isRestartController = (commandName === "gateway" || commandName === "daemon") && subcommandName === "restart";
let preflightResult = null;
const shouldRunStartupPreflight = !params.validateConfigOnly && commandName !== "doctor" && !isManagedNodeRuntime && commandName !== "config" && commandName !== "health" && commandName !== "logs" && commandName !== "sessions" && !(commandName === "gateway" && subcommandName === "call") && !isRestartController && !(commandName === "update" && subcommandName === "status");
const runStartupPreflight = async () => {
didRunStartupConfigPreflight = true;
const runStartupConfigPreflight = async () => (await import("./startup-config-preflight-dp5mXDL5.mjs")).runStartupConfigPreflight({
gateway: shouldPrepareGatewayState(commandPath),
...params.measure ? { measure: params.measure } : {},
...commandName === "status" ? { observe: false } : {},
...shouldPrepareGatewayState(commandPath) ? { validateStartupConfig: async (snapshot) => {
const { getGatewayStartGuardErrors } = await import("./pre-bootstrap-DRP-KOAG.mjs");
const errors = getGatewayStartGuardErrors({
allowUnconfigured: params.allowInvalid,
configExists: snapshot.exists,
mode: snapshot.config.gateway?.mode
});
if (errors.length > 0) {
params.runtime.error(errors.join("\n"));
throw new ExitError(78);
}
} } : {},
...params.beforeStatePreparation ? { beforeStatePreparation: params.beforeStatePreparation } : {}
});
try {
return !params.suppressDoctorStdout ? await runStartupConfigPreflight() : await withSuppressedNotes(runStartupConfigPreflight);
} catch (error) {
if (shouldPrepareGatewayState(commandPath)) await (await import("./startup-maintenance-DpMMFjJM.mjs")).handleGatewayStartupMaintenance(error);
if (error instanceof ExitError) params.runtime.exit(error.code);
throw error;
}
};
if (!didRunStartupConfigPreflight && shouldRunStartupPreflight) preflightResult = await runStartupPreflight();
const configSnapshotOptions = params.validateConfigOnly || commandName === "logs" ? {
observe: false,
pluginValidation: "core-only"
} : isManagedNodeRuntime || commandName === "status" || commandName === "gateway" && subcommandName === "call" || isRestartController ? { observe: false } : void 0;
const snapshot = preflightResult?.snapshot ?? await getConfigSnapshot(configSnapshotOptions, params.measure);
const isBareGatewayForegroundRun = commandName === "gateway" && (subcommandName === void 0 || subcommandName.trim() === "");
const allowInvalid = commandName ? params.allowInvalid === true || ALLOWED_INVALID_COMMANDS.has(commandName) || isBareGatewayForegroundRun || commandName === "gateway" && subcommandName && ALLOWED_INVALID_GATEWAY_SUBCOMMANDS.has(subcommandName) : false;
const [{ formatConfigIssueLines }, { renderConfigValidationIssueLines }] = await Promise.all([import("./issue-format-C9j1eviF.mjs"), import("./issue-location-DVwWZ4WF.mjs")]);
const issues = snapshot.exists && !snapshot.valid ? renderConfigValidationIssueLines(snapshot) : [];
const legacyIssues = snapshot.legacyIssues.length > 0 ? formatConfigIssueLines(snapshot.legacyIssues, "-") : [];
if (!(snapshot.exists && !snapshot.valid)) {
setRuntimeConfigSnapshot(snapshot.runtimeConfig ?? snapshot.config, snapshot.sourceConfig);
if (shouldPrepareGatewayState(commandPath) && preflightResult?.pluginMetadataSnapshot) adoptProcessPluginCache(getPluginMetadataSnapshotCache(preflightResult.pluginMetadataSnapshot));
return;
}
const [{ colorize, isRich, theme }, { shortenHomePath }, { formatCliCommand }, { isPluginPackagingRuntimeOutputInvalidConfigSnapshot }, { formatPluginPackagingRuntimeOutputRecoveryHint }] = await Promise.all([
import("./terminal-core/theme.js"),
import("./utils-IThmeNVh.mjs"),
import("./command-format-D-ZqRvLg.mjs"),
import("./recovery-policy-XB211LEa.mjs"),
import("./config-recovery-hints-Den0EbQc.mjs")
]);
const rich = isRich();
const muted = (value) => colorize(rich, theme.muted, value);
const error = (value) => colorize(rich, theme.error, value);
const heading = (value) => colorize(rich, theme.heading, value);
const commandText = (value) => colorize(rich, theme.command, value);
const readFailure = isConfigReadFailure(snapshot);
params.runtime.error(heading(configFailureHeading(snapshot)));
params.runtime.error(`${muted("File:")} ${muted(shortenHomePath(snapshot.path))}`);
if (issues.length > 0) {
params.runtime.error(muted("Problem:"));
params.runtime.error(issues.map((issue) => ` ${error(issue)}`).join("\n"));
}
if (legacyIssues.length > 0) {
params.runtime.error(muted("Legacy config keys detected:"));
params.runtime.error(legacyIssues.map((issue) => ` ${error(issue)}`).join("\n"));
}
params.runtime.error("");
const isPluginPackagingFailure = isPluginPackagingRuntimeOutputInvalidConfigSnapshot(snapshot);
const isReadOnlyConfig = resolveIsConfigReadOnly();
const isGatewayStartup = isGatewayStartupCommand(commandPath);
const mustBlockInvalid = !allowInvalid || isGatewayStartup && params.allowInvalid !== true;
const shouldOfferRecovery = mustBlockInvalid && !readFailure && !params.suppressDoctorStdout && !isReadOnlyConfig && !isManagedNodeRuntime;
if (readFailure) params.runtime.error(muted("Resolve the read error shown above, then retry."));
else if (isPluginPackagingFailure || isReadOnlyConfig || !shouldOfferRecovery) {
const fixHint = isPluginPackagingFailure ? formatPluginPackagingRuntimeOutputRecoveryHint() : isReadOnlyConfig ? (await import("./config-write-guard-CHPV_Pr_.mjs")).createConfigMutationError({ configPath: snapshot.path }).message : commandText(formatCliCommand("openclaw doctor --fix"));
params.runtime.error(`${muted("Fix:")} ${fixHint}`);
}
params.runtime.error(`${muted("Inspect:")} ${commandText(formatCliCommand("openclaw config validate"))}`);
params.runtime.error(muted(readFailure ? "Audit, status, health, logs, and doctor commands still run when config cannot be read." : "Audit, status, health, logs, and doctor commands still run with invalid config."));
if (mustBlockInvalid && (await import("./json-output-mode-CObM-WjA.mjs")).isJsonOutputModeActive(process.argv)) {
const { writeInvalidConfigCliJson } = await import("./config-validation-output-BbjDeojt.mjs");
writeInvalidConfigCliJson(params.runtime, snapshot);
}
if (isPluginPackagingFailure && isGatewayStartup) {
params.runtime.exit(78);
return;
}
if (shouldOfferRecovery && !isPluginPackagingFailure) {
const { offerInvalidConfigRecovery } = await import("./invalid-config-recovery-C5U2yiVM.mjs");
if ((await offerInvalidConfigRecovery({
runtime: params.runtime,
deps: recoveryDeps,
retry: async () => {
configSnapshotPromise = null;
const retrySnapshot = shouldRunStartupPreflight ? (await (await import("./startup-config-preflight-dp5mXDL5.mjs")).runStartupConfigPreflight({
gateway: false,
...params.measure ? { measure: params.measure } : {},
...configSnapshotOptions?.observe === false ? { observe: false } : {}
})).snapshot : await getConfigSnapshot(configSnapshotOptions, params.measure);
if (retrySnapshot.exists && !retrySnapshot.valid) {
const retryIssues = renderConfigValidationIssueLines(retrySnapshot);
throw (isConfigReadFailure(retrySnapshot) ? createConfigReadError : createInvalidConfigError)(retrySnapshot.path, retryIssues.join("\n") || "Unknown validation issue.");
}
setRuntimeConfigSnapshot(retrySnapshot.runtimeConfig ?? retrySnapshot.config, retrySnapshot.sourceConfig);
}
})).status === "recovered") return;
params.runtime.exit(isGatewayStartup ? 78 : 1);
return;
}
if (mustBlockInvalid) params.runtime.exit(isGatewayStartup && !readFailure ? 78 : 1);
}
const testApi = { resetConfigGuardStateForTests };
//#endregion
export { ensureConfigReady, testApi };