openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
162 lines (161 loc) • 8.28 kB
JavaScript
import { S as createConfigIO } from "./io.runtime-B9iJRs3w.js";
import { n as resolvePathViaExistingAncestorSync } from "./boundary-path-DMNeww4q.js";
import { t as formatCliCommand } from "./command-format-C7YfyMTd.js";
import { a as isDefaultInstallIdentity, f as resolveConfigPath, w as resolveStateDir } from "./paths-D2sRr1a_.js";
import "./openclaw-state-db-contract-DYCYxE4w.js";
import { s as resolveOpenClawStateSqlitePath } from "./openclaw-state-db-schema-version-c1ZL6JGz.js";
import { n as acquireGatewayLifecycleCoordinator, r as acquireStateDatabaseCoordinator } from "./state-database-coordinator-opgcBiXJ.js";
import "./io-bdCzpGWJ.js";
import { a as readGatewayServiceState, o as resolveGatewayService } from "./service-Cc6NX6Jm.js";
import { n as waitForGatewayHealthyRestart, o as renderRestartDiagnostics } from "./restart-health-Btw0xxAB.js";
import "./openclaw-agent-db-contract-CGTyjij4.js";
import { c as resolveConfiguredAgentDatabaseTargets, s as resolveConfiguredAgentDatabaseCandidatePaths } from "./targets-Cknmo7YZ.js";
import { r as preflightOpenClawDatabaseSchemas, t as OpenClawDatabaseSchemaPreflightError } from "./openclaw-database-preflight-DmhpNBQv.js";
import { i as maybeStopManagedServiceBeforeMutableUpdate, o as revalidateManagedGatewayServiceAfterUpdate, r as maybeResumeWindowsTaskAutoStartAfterPackageUpdate } from "./update-command-service-maintenance-BOVvMegl.js";
import { c as resolveUpdatedGatewayRestartPort } from "./update-command-service-plan-Y641LZ_0.js";
import { i as isServiceRepairExternallyManaged, o as resolveUpdateParentGatewayActivation, s as shouldManageGatewayService } from "./doctor-service-repair-policy-S9bPaCiq.js";
import { n as resolveDoctorRepairMode, t as isDoctorUpdateRepairMode } from "./doctor-repair-mode-DYiiDZuy.js";
import path from "node:path";
//#region src/commands/doctor-maintenance.ts
/** Coordinates explicit Doctor repair with the managed Gateway lifecycle. */
async function assertDoctorMaintenanceSchemasCompatible(env) {
const snapshot = await createConfigIO({
env: { ...env },
observe: false,
pluginValidation: "core-only"
}).readConfigFileSnapshot();
const cfg = snapshot.sourceConfig ?? snapshot.config;
const schemas = preflightOpenClawDatabaseSchemas({
env,
supportedVersions: {
state: 15,
agent: 19
},
configuredAgentDatabaseTargets: (registeredAgentDatabases) => resolveConfiguredAgentDatabaseTargets(cfg, {
env,
registeredDatabases: registeredAgentDatabases
}),
configuredAgentDatabaseCandidatePaths: resolveConfiguredAgentDatabaseCandidatePaths(cfg, { env }),
verifyCurrentSchemaShape: true
});
if (schemas.incompatible.length > 0) throw new OpenClawDatabaseSchemaPreflightError(schemas.incompatible, { operation: "doctor" });
}
function assertDoctorServiceSelection(env, serviceEnv) {
const selection = (candidate) => {
const stateDir = resolveStateDir(candidate);
return [stateDir, resolveConfigPath(candidate, stateDir)].map((value) => resolvePathViaExistingAncestorSync(value));
};
const before = selection(env);
if (selection(serviceEnv).some((value, index) => value !== before[index])) throw new Error("Doctor and the managed Gateway select different config or state directories. Run doctor with the Gateway's installation and profile; the service was left unchanged.");
}
function assertDoctorMaintenanceInspection(inspection, env) {
const kind = inspection.serviceUpdateVerdict?.kind;
if (!inspection.blockMessage && inspection.inspected && (kind === "owned" || kind === "absent" || inspection.offline === true)) return;
throw new Error(kind === "owned" && inspection.blockMessage ? inspection.blockMessage : `Gateway service ownership or shutdown could not be verified. Run ${formatCliCommand("openclaw gateway status --deep", env)} and stop it through its service owner before retrying.`);
}
async function beginDoctorMaintenance(params) {
if (!(params.options.repair === true || params.options.yes === true)) return;
const env = { ...process.env };
const parentActivation = isDoctorUpdateRepairMode(resolveDoctorRepairMode(params.options)) ? resolveUpdateParentGatewayActivation(env) : void 0;
await assertDoctorMaintenanceSchemasCompatible(env);
let stopped;
const coordinators = [];
let repairStoresMayBeOpen = false;
const release = async () => {
try {
if (repairStoresMayBeOpen) {
repairStoresMayBeOpen = false;
const [{ closeOpenClawAgentDatabasesAsync }, { closeOpenClawStateDatabaseByPath }] = await Promise.all([import("./openclaw-agent-db-Bfc4Wmxr.js"), import("./openclaw-state-db-B2_DN17n.js")]);
await closeOpenClawAgentDatabasesAsync();
closeOpenClawStateDatabaseByPath(resolveOpenClawStateSqlitePath(env));
}
} finally {
for (const coordinator of coordinators.splice(0).toReversed()) coordinator.release();
const recovery = stopped?.windowsTaskAutoStartRecovery;
try {
await maybeResumeWindowsTaskAutoStartAfterPackageUpdate(stopped);
} finally {
recovery?.complete();
}
}
};
try {
if (params.root && isDefaultInstallIdentity(env) && !isServiceRepairExternallyManaged() && await shouldManageGatewayService(env)) {
const inspection = await maybeStopManagedServiceBeforeMutableUpdate({
updateInstallKind: "package",
root: params.root,
shouldRestart: true,
jsonMode: true,
phase: "inspect"
});
assertDoctorMaintenanceInspection(inspection, env);
if (parentActivation !== void 0 && inspection.serviceUpdateVerdict?.kind !== "absent" && inspection.offline !== true) throw new Error("The update parent owns Gateway activation. Stop the service through its owner before retrying the update; Doctor will not stop or restart it.");
if (inspection.serviceUpdateVerdict?.kind === "owned") {
if (inspection.serviceEnv) assertDoctorServiceSelection(env, inspection.serviceEnv);
if (parentActivation === void 0) {
inspection.serviceUpdateVerdict.refreshDefinition = false;
stopped = await maybeStopManagedServiceBeforeMutableUpdate({
updateInstallKind: "package",
root: params.root,
shouldRestart: true,
jsonMode: true,
expectedService: inspection
});
assertDoctorMaintenanceInspection(stopped, env);
if (stopped.stopped) params.runtime.log("Stopped the managed Gateway for Doctor repair.");
}
} else if (inspection.serviceUpdateVerdict?.kind !== "absent") params.runtime.log("The stopped Gateway service was left unchanged; repairing Doctor's selected state only.");
}
const databasePath = path.resolve(resolveOpenClawStateSqlitePath(env));
coordinators.push(acquireGatewayLifecycleCoordinator({
databasePath,
busyTimeoutMs: 250
}));
coordinators.push(acquireStateDatabaseCoordinator({
databasePath,
busyTimeoutMs: 250
}));
repairStoresMayBeOpen = true;
} catch (error) {
await release();
throw new Error(`Doctor could not enter maintenance. Stop the Gateway through its service owner, then run ${formatCliCommand("openclaw doctor --fix", env)}. ${String(error)}`, { cause: error });
}
return {
release,
async finish(cfg) {
await release();
if (!stopped?.stopped || !stopped.serviceEnv || !params.root) return;
const service = resolveGatewayService();
const state = await readGatewayServiceState(service, {
env: stopped.serviceEnv,
requireEffective: true
});
assertDoctorServiceSelection(env, state.env);
await revalidateManagedGatewayServiceAfterUpdate({
state,
root: params.root,
preManagedServiceStop: stopped
});
await service.restart({
env: state.env,
stdout: process.stdout,
preserveDefinition: true
});
const port = await resolveUpdatedGatewayRestartPort({
config: cfg,
serviceEnv: state.env,
serviceCommand: state.command
});
const health = await waitForGatewayHealthyRestart({
service,
port,
env: state.env,
requireRunningService: true
});
if (!health.healthy) throw new Error(`Doctor repaired state, but the managed Gateway did not become ready: ${renderRestartDiagnostics(health).join(" ")}. Run ${formatCliCommand("openclaw gateway status --deep", env)}.`);
params.runtime.log("Gateway restarted and verified after Doctor repair.");
}
};
}
//#endregion
export { beginDoctorMaintenance };