openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
151 lines (150 loc) • 7.25 kB
JavaScript
import { r as createLazyRuntimeModule } from "./lazy-runtime-CgCh8H_K.js";
import { t as formatCliCommand } from "./command-format-C7YfyMTd.js";
import { w as resolveStateDir } from "./paths-D2sRr1a_.js";
import { r as stylePromptTitle } from "./prompt-style-BQVvtDcR.js";
import fs from "node:fs";
import { intro, outro } from "@clack/prompts";
//#region src/flows/doctor-health.ts
const intro$1 = (message) => intro(stylePromptTitle(message) ?? message);
const outro$1 = (message) => outro(stylePromptTitle(message) ?? message);
const loadConfigModule = createLazyRuntimeModule(() => import("./config/config.js"));
async function assertDoctorDatabaseSchemasCompatible() {
const [databasePreflight, agentDatabase, stateDatabase] = await Promise.all([
import("./openclaw-database-preflight-of3VjD09.js"),
import("./openclaw-agent-db-contract-D9PUUtGr.js"),
import("./openclaw-state-db-contract-Ciz9ElQz.js")
]);
const databaseSchemas = databasePreflight.preflightOpenClawDatabaseSchemas({
env: process.env,
supportedVersions: {
state: stateDatabase.OPENCLAW_STATE_SCHEMA_VERSION,
agent: agentDatabase.OPENCLAW_AGENT_SCHEMA_VERSION
}
});
if (databaseSchemas.incompatible.length > 0) throw new databasePreflight.OpenClawDatabaseSchemaPreflightError(databaseSchemas.incompatible, { operation: "doctor" });
const unreadableStateDatabase = databaseSchemas.indeterminate.find((database) => database.kind === "state");
if (unreadableStateDatabase) throw new Error(`Doctor cannot continue because the shared state database is unreadable: ${unreadableStateDatabase.path}: ${unreadableStateDatabase.reason}. The database was left unchanged; doctor will not recreate it because that could discard persistent operator data. Stop the Gateway and other OpenClaw processes, then restore this file from a verified backup or repair it manually. After recovery, run ${formatCliCommand("openclaw doctor --fix")} again. See ${stateDatabase.OPENCLAW_DATABASE_SCHEMA_DOCS_URL}.`);
}
function stateDirectoryExistsAtDoctorStart() {
try {
return fs.statSync(resolveStateDir()).isDirectory();
} catch {
return false;
}
}
/** Runs the full interactive doctor flow against the provided or default runtime. */
async function runDoctorHealthFlow(runtime, options = {}) {
const effectiveRuntime = runtime ?? (await import("./runtime-DixL8FcQ.js")).defaultRuntime;
const stateDirExistedAtStart = stateDirectoryExistsAtDoctorStart();
intro$1("OpenClaw doctor");
const { createDoctorPrompter } = await import("./doctor-prompter-DzatNY0G.js");
const prompter = createDoctorPrompter({
runtime: effectiveRuntime,
options
});
const { resolveOpenClawPackageRoot } = await import("./openclaw-root-BE8vlD1q.js");
const root = await resolveOpenClawPackageRoot({
moduleUrl: import.meta.url,
argv1: process.argv[1],
cwd: process.cwd()
});
const { maybeOfferUpdateBeforeDoctor } = await import("./doctor-update-CRs55eRG.js");
if ((await maybeOfferUpdateBeforeDoctor({
runtime: effectiveRuntime,
options,
root,
confirm: (p) => prompter.confirm(p),
outro: outro$1
})).handled) return;
await assertDoctorDatabaseSchemasCompatible();
if (options.repair === true || options.yes === true || options.generateGatewayToken === true) {
const { assertConfigWriteAllowedInCurrentMode } = await loadConfigModule();
assertConfigWriteAllowedInCurrentMode();
}
const { beginDoctorMaintenance } = await import("./doctor-maintenance-BRbfb7tx.js");
const maintenance = await beginDoctorMaintenance({
options,
root,
runtime: effectiveRuntime
});
let exitCode;
try {
const { maybeRepairUiProtocolFreshness } = await import("./doctor-ui-DEM9GZ0y.js");
const { noteSourceInstallIssues } = await import("./doctor-install-CzcMiwfj.js");
const { noteStalePluginRuntimeSymlinks } = await import("./plugin-runtime-symlinks-DAlgAibd.js");
const { noteStartupOptimizationHints } = await import("./doctor-platform-notes-u2dKqvJ4.js");
await maybeRepairUiProtocolFreshness(effectiveRuntime, prompter);
noteSourceInstallIssues(root);
await noteStalePluginRuntimeSymlinks(root);
noteStartupOptimizationHints();
const { loadAndMaybeMigrateDoctorConfig } = await import("./doctor-config-flow-j00DTj1G.js");
const configResult = await loadAndMaybeMigrateDoctorConfig({
options,
confirm: (p) => prompter.confirm(p),
runtime: effectiveRuntime,
prompter
});
const { CONFIG_PATH } = await loadConfigModule();
const ctx = {
runtime: effectiveRuntime,
options,
prompter,
configResult,
cfg: configResult.cfg,
cfgForPersistence: structuredClone(configResult.cfg),
sourceConfigValid: configResult.sourceConfigValid ?? true,
configPath: configResult.path ?? CONFIG_PATH,
stateDirExistedAtStart,
gatewayMaintenanceActive: maintenance !== void 0,
runWithPluginMetadataSnapshot: configResult.runWithPluginMetadataSnapshot,
invalidatePluginMetadataSnapshot: configResult.invalidatePluginMetadataSnapshot
};
const { runDoctorHealthContributions } = await import("./doctor-health-contributions-Bzhef3DP.js");
await runDoctorHealthContributions(ctx);
if (ctx.configWriteRefusal) {
outro$1(ctx.configResultWriteCommitted === true ? "Doctor finished, but some config fixes were not applied." : "Doctor finished, but config fixes were not applied.");
exitCode = 1;
return;
}
if (options.repair === true || options.yes === true) {
const { assertSessionStoreMigrationComplete } = await import("./startup-migration-BV-eQ791.js");
assertSessionStoreMigrationComplete({
cfg: ctx.cfg,
env: process.env
});
const { assertOpenClawDatabasesReady } = await import("./openclaw-database-preflight-of3VjD09.js");
const { resolveConfiguredAgentDatabaseTargets } = await import("./targets-nZHFRtE8.js");
assertOpenClawDatabasesReady({
env: process.env,
operation: "doctor",
configuredAgentDatabaseTargets: resolveConfiguredAgentDatabaseTargets(ctx.cfg, { env: process.env })
});
const { assertConfiguredWorkspaceStateReady } = await import("./workspace-state-dirs-B8xySjIm.js");
assertConfiguredWorkspaceStateReady({ cfg: ctx.cfg });
const { assertNoPendingLegacyExecApprovals } = await import("./exec-approvals-migration-gate-DqHllFZW.js");
assertNoPendingLegacyExecApprovals();
}
await maintenance?.finish(ctx.cfg);
if (ctx.postInstallDoctorResult) {
const { UPDATE_POST_INSTALL_DOCTOR_ADVISORY_EXIT_CODE, UPDATE_POST_INSTALL_DOCTOR_RESULT_PATH_ENV, writeUpdatePostInstallDoctorResult } = await import("./update-doctor-result-DpZ1G9Ee.js");
const resultPath = process.env[UPDATE_POST_INSTALL_DOCTOR_RESULT_PATH_ENV]?.trim();
if (resultPath) {
await writeUpdatePostInstallDoctorResult({
resultPath,
result: ctx.postInstallDoctorResult
});
exitCode = UPDATE_POST_INSTALL_DOCTOR_ADVISORY_EXIT_CODE;
return;
}
}
} catch (error) {
if (maintenance) effectiveRuntime.error("Doctor could not complete maintenance. Check the reported service state, resolve the failure, and rerun doctor --fix.");
throw error;
} finally {
await maintenance?.release();
if (exitCode !== void 0) effectiveRuntime.exit(exitCode);
}
outro$1("Doctor complete.");
}
//#endregion
export { runDoctorHealthFlow };