UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

152 lines (151 loc) • 8.79 kB
import { r as collectNestedErrorCandidates } from "./error-coercion-CvJK4lmH.mjs"; import { t as UpdateSchemaRefusalError } from "./openclaw-update-schema-refusal-M64rViod.mjs"; import { t as formatCliCommand } from "./command-format-DwEdrDKL.mjs"; import { n as StartupMaintenanceRequiredError } from "./startup-maintenance-required-rkhEAmAi.mjs"; import { c as resolveOpenClawStateSqlitePath } from "./openclaw-state-db.paths-DfifnSGp.mjs"; import { t as GatewayStateOwnerContentionError } from "./gateway-state-owner-DmHycK5o.mjs"; import { t as DoctorUnreadableStateDatabaseError } from "./state-repair-message-B5Bu99oR.mjs"; import { r as hasCommandProcessCleanupError } from "./exec-result-s7WOth0N.mjs"; import { c as hasGatewayServiceStopUnsafeError } from "./service-inspection-error-Bsltcpi4.mjs"; import { o as withArtifactPreservingStateReads, t as executeExistingOpenClawStateRead } from "./openclaw-state-db-readonly-CC57AD_q.mjs"; import { c as readStateLeaseProcessOwnerStatus } from "./openclaw-state-lease-store-gySh31JU.mjs"; import { t as DoctorMaintenanceRefusalError } from "./update-doctor-result-b1ffujJO.mjs"; import { t as ConfigWritePostCommitError } from "./io.write-errors-DuSgsySH.mjs"; import { n as GatewayLockError } from "./gateway-lock-B6mKYs2x.mjs"; import { t as ExecApprovalsMigrationRequiredError } from "./exec-approvals-migration-gate-4cEEb67t.mjs"; import { t as DoctorStateMigrationRefusalError } from "./state-migrations.messages-CXxBVY0a.mjs"; //#region src/commands/doctor-maintenance-inspection.ts /** An app's package-update authority does not own repair of its already offline state. */ async function readOfflineAppOwnedDoctorInspection(error, env) { const { GatewayServiceUpdateOwnershipError } = await import("./update-command-service-plan-D81Z2v3m.mjs"); if (hasCommandProcessCleanupError(error) || !(error instanceof GatewayServiceUpdateOwnershipError) || !error.failureFacts.some((fact) => fact.check === "managed-service" && fact.code === "service-mutation-refused")) return; const { readGatewayServiceState, resolveGatewayService } = await import("./service-C5exmZL9.mjs"); const { assertDoctorServiceSelection } = await import("./doctor-service-repair-policy-EaNh1wfg.mjs"); const state = await readGatewayServiceState(resolveGatewayService(), { env, requireEffective: true, validateEnvBeforeStatusRead: (serviceEnv) => assertDoctorServiceSelection(env, serviceEnv) }); if (!state.command) return; const { summarizeGatewayServiceLayout } = await import("./service-layout-B33QDzfj.mjs"); const { readInstallOwner, formatInstallOwnerMessage } = await import("./install-owner-C6B0L6kK.mjs"); const owner = await readInstallOwner((await summarizeGatewayServiceLayout(state.command))?.packageRootReal ?? null); const { isManagedGatewayServiceOffline } = await import("./update-command-service-publication-Lo5UT6wo.mjs"); if (!owner || !await isManagedGatewayServiceOffline(state)) return; const message = `${formatInstallOwnerMessage(owner)} Doctor can repair offline state; Gateway service installation and activation remain with the app.`; return { stopped: false, inspected: true, runtimeInspected: true, running: false, offline: true, serviceEnv: state.env, serviceMutationAllowed: false, serviceUpdateVerdict: { kind: "unavailable", message }, serviceMutationSkipMessage: message }; } /** Observe the selected installation without bootstrapping it or inheriting a discovery view. */ async function readDoctorGatewayOwnerLease(env, signal) { const options = { env: { ...env }, path: resolveOpenClawStateSqlitePath(env) }; const reply = await withArtifactPreservingStateReads(() => executeExistingOpenClawStateRead(options, { type: "doctor.gatewayOwnerLease.read" }, { current: true, signal })); signal.throwIfAborted(); if (!reply) return; if (!reply.ok || reply.type !== "doctor.gatewayOwnerLease.read") throw new Error("Unexpected Doctor Gateway owner lease read result"); return reply.lease ? { ...reply.lease, state: readStateLeaseProcessOwnerStatus(reply.lease, reply.lease.heartbeatAt) } : void 0; } /** Admission has not opened repair writers; deferral cannot authorize any later work. */ function classifyDoctorMaintenanceRefusal(error) { const causes = collectNestedErrorCandidates(error); const refusal = causes.find((cause) => cause instanceof DoctorMaintenanceRefusalError && cause.refusal.kind === "data-at-risk"); if (refusal) return refusal.refusal; if (hasGatewayServiceStopUnsafeError(error) || hasCommandProcessCleanupError(error)) return { kind: "data-at-risk", reason: "active-mutation" }; if (causes.some((cause) => cause instanceof DoctorUnreadableStateDatabaseError)) return { kind: "data-at-risk", reason: "unreadable-state" }; if (causes.some((cause) => cause instanceof DoctorStateMigrationRefusalError || cause instanceof StartupMaintenanceRequiredError || cause instanceof ExecApprovalsMigrationRequiredError || cause instanceof UpdateSchemaRefusalError)) return { kind: "data-at-risk", reason: "incomplete-migration" }; if (causes.some((cause) => cause instanceof GatewayLockError || cause instanceof ConfigWritePostCommitError && cause.rollbackStatus !== "restored")) return { kind: "data-at-risk", reason: "gateway-state-unverified" }; return { kind: "deferred", reason: causes.some((cause) => cause instanceof GatewayStateOwnerContentionError) ? "coordinator-contention" : "admission-unavailable" }; } /** The same persisted-state admission protects completion and exceptional restoration. */ async function assertDoctorMaintenanceReady(cfg, env, log, databaseTargets) { let schemaPublicationDeferred = false; let refusedDatabasePaths = []; const { assertSessionStoreMigrationComplete } = await import("./startup-migration-DgTpX2AW.mjs"); assertSessionStoreMigrationComplete({ cfg, env, operation: "doctor" }); const { assertOpenClawDatabasesReady } = await import("./openclaw-database-preflight-ClnnU5e8.mjs"); const { resolveConfiguredAgentDatabaseTargets } = await import("./targets-Be8sKF4o.mjs"); await assertOpenClawDatabasesReady({ env, config: cfg, operation: "doctor", onDeferredSchemaPublication: (publication) => { schemaPublicationDeferred = true; log(publication.message); }, onVerified: (schemas) => { refusedDatabasePaths = schemas.agentRefusals?.flatMap((refusal) => refusal.paths) ?? []; }, configuredAgentDatabaseTargets: resolveConfiguredAgentDatabaseTargets(cfg, { env }) }); const { assertConfiguredWorkspaceStateReady } = await import("./workspace-state-dirs--SUCclNd.mjs"); await assertConfiguredWorkspaceStateReady({ cfg, operation: "doctor" }); const { assertNoPendingLegacyExecApprovals } = await import("./exec-approvals-migration-gate-ChKEox6L.mjs"); assertNoPendingLegacyExecApprovals({ operation: "doctor", env }); if (!schemaPublicationDeferred && databaseTargets) { const { completeDoctorMigrationBackups } = await import("./doctor-migration-backup-artifacts-BmcblQ2C.mjs"); try { completeDoctorMigrationBackups(env, databaseTargets.filter((target) => !refusedDatabasePaths.some((refused) => refused === target.path || refused === target.realPath)).map((target) => target.path)); } catch (error) { log(`Migration backups remain protected; completion registration failed: ${String(error)}`); } } return { schemaPublicationDeferred }; } function assertDoctorMaintenanceInspection(inspection, env) { const kind = inspection.serviceUpdateVerdict?.kind; if (!inspection.blockMessage && (kind === "unavailable" || inspection.inspected && (kind === "owned" || kind === "absent" || inspection.offline === true))) return; const detail = inspection.blockMessage ?? inspection.serviceMutationSkipMessage ?? `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.`; const guidance = kind === "foreign" ? `Run ${formatCliCommand("openclaw gateway status --deep", env)} to locate the Gateway's installation, then run ${formatCliCommand("openclaw doctor --fix", env)} from that installation. The service was left unchanged.` : `Stop the Gateway service and other OpenClaw processes using this state, then run ${formatCliCommand("openclaw doctor --fix", env)} from an independent shell.`; throw new DoctorMaintenanceRefusalError(`Doctor could not enter maintenance. Error: ${detail} ${guidance}`, { kind: "data-at-risk", reason: "gateway-state-unverified" }); } //#endregion export { readOfflineAppOwnedDoctorInspection as a, readDoctorGatewayOwnerLease as i, assertDoctorMaintenanceReady as n, classifyDoctorMaintenanceRefusal as r, assertDoctorMaintenanceInspection as t };