UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

124 lines (123 loc) 5.65 kB
import { t as isTruthyEnvValue } from "./env-Di49gJwo.js"; import { f as resolveHomeDir } from "./utils-CCC-BEJH.js"; import { _ as recoverConfigFromLastKnownGood, g as recoverConfigFromJsonRootSuffix, u as readConfigFileSnapshot } from "./io-Gi7-pyU-.js"; import { n as formatConfigIssueLines } from "./issue-format-CwCoGNbt.js"; import { t as note } from "./note-BRSJp0UF.js"; import { r as noteIncludeConfinementWarning } from "./doctor-config-analysis-BUgmtVDJ.js"; import { t as findDoctorLegacyConfigIssues } from "./legacy-config-issues-BOTisc-X.js"; import path from "node:path"; import fs from "node:fs/promises"; //#region src/commands/doctor-config-preflight.ts /** Config preflight for doctor: legacy config/state migration, recovery, and snapshot loading. */ let doctorStateMigrationsPromise = null; let doctorCronPromise = null; function loadDoctorStateMigrations() { doctorStateMigrationsPromise ??= import("./doctor-state-migrations-DN_uDqjB.js"); return doctorStateMigrationsPromise; } function loadDoctorCron() { doctorCronPromise ??= import("./cron-DftHso_S.js"); return doctorCronPromise; } async function maybeMigrateLegacyConfig() { const changes = []; const home = resolveHomeDir(); if (!home) return changes; const targetDir = path.join(home, ".openclaw"); const targetPath = path.join(targetDir, "openclaw.json"); try { await fs.access(targetPath); return changes; } catch {} const legacyCandidates = [path.join(home, ".clawdbot", "clawdbot.json")]; let legacyPath = null; for (const candidate of legacyCandidates) try { await fs.access(candidate); legacyPath = candidate; break; } catch {} if (!legacyPath) return changes; await fs.mkdir(targetDir, { recursive: true }); try { await fs.copyFile(legacyPath, targetPath, fs.constants.COPYFILE_EXCL); changes.push(`Migrated legacy config: ${legacyPath} -> ${targetPath}`); } catch {} return changes; } function collectDoctorLegacyIssues(snapshot) { if (!snapshot.exists) return []; const resolvedRaw = snapshot.sourceConfig ?? snapshot.config ?? {}; return findDoctorLegacyConfigIssues(resolvedRaw, snapshot.parsed ?? resolvedRaw); } function addDoctorLegacyIssues(snapshot) { const legacyIssues = collectDoctorLegacyIssues(snapshot); if (legacyIssues.length === 0) return snapshot; return { ...snapshot, legacyIssues }; } /** Returns true during updater-managed config rewrites where plugin validation may be stale. */ function shouldSkipPluginValidationForDoctorConfigPreflight(env = process.env) { return isTruthyEnvValue(env.OPENCLAW_UPDATE_IN_PROGRESS); } function noteStateMigrationResult(result) { if (result.changes.length > 0) note(result.changes.map((entry) => `- ${entry}`).join("\n"), "Doctor changes"); if (result.warnings.length > 0) note(result.warnings.map((entry) => `- ${entry}`).join("\n"), "Doctor warnings"); } /** * Runs early doctor config checks before the main config repair flow. * * It may migrate legacy state/config paths, recover corrupt target config when requested, and * returns the best-effort config snapshot used by later doctor checks. */ async function runDoctorConfigPreflight(options = {}) { if (options.migrateState !== false) { const { autoMigrateLegacyStateDir } = await loadDoctorStateMigrations(); noteStateMigrationResult(await autoMigrateLegacyStateDir({ env: process.env })); } if (options.migrateLegacyConfig !== false) { const legacyConfigChanges = await maybeMigrateLegacyConfig(); if (legacyConfigChanges.length > 0) note(legacyConfigChanges.map((entry) => `- ${entry}`).join("\n"), "Doctor changes"); } const readOptions = { skipPluginValidation: shouldSkipPluginValidationForDoctorConfigPreflight() }; let snapshot = addDoctorLegacyIssues(await readConfigFileSnapshot(readOptions)); if (options.repairPrefixedConfig === true && snapshot.exists && !snapshot.valid) { if (await recoverConfigFromJsonRootSuffix(snapshot)) { note("Removed non-JSON prefix from openclaw.json; original saved as .clobbered.*.", "Config"); snapshot = addDoctorLegacyIssues(await readConfigFileSnapshot(readOptions)); } else if (await recoverConfigFromLastKnownGood({ snapshot, reason: "doctor-invalid-config" })) { note("Restored openclaw.json from last-known-good; original saved as .clobbered.*.", "Config"); snapshot = addDoctorLegacyIssues(await readConfigFileSnapshot(readOptions)); } } const invalidConfigNote = options.invalidConfigNote ?? "Config invalid; doctor will run with best-effort config."; if (invalidConfigNote && snapshot.exists && !snapshot.valid && snapshot.legacyIssues.length === 0) { note(invalidConfigNote, "Config"); noteIncludeConfinementWarning(snapshot); } const warnings = snapshot.warnings ?? []; if (warnings.length > 0) note(formatConfigIssueLines(warnings, "-").join("\n"), "Config warnings"); const baseConfig = snapshot.sourceConfig ?? snapshot.config ?? {}; if (options.migrateState !== false) { if (snapshot.valid) { const { repairLegacyCronStoreWithoutPrompt } = await loadDoctorCron(); noteStateMigrationResult(await repairLegacyCronStoreWithoutPrompt({ cfg: baseConfig })); } const { autoMigrateLegacyState, autoMigrateLegacyTaskStateSidecars } = await loadDoctorStateMigrations(); noteStateMigrationResult(snapshot.valid ? await autoMigrateLegacyState({ cfg: baseConfig, env: process.env, recoverCorruptTargetStore: options.recoverCorruptTargetStore }) : await autoMigrateLegacyTaskStateSidecars({ env: process.env })); } return { snapshot, baseConfig }; } //#endregion export { shouldSkipPluginValidationForDoctorConfigPreflight as n, runDoctorConfigPreflight as t };