UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

306 lines (305 loc) • 16.5 kB
import { c as getPluginCache, k as withPluginCache, s as createPluginCache } from "./plugin-cache-CfBhQFSN.mjs"; import { a as measureGatewayBootstrapStep } from "./startup-trace-DQN0c5Oa.mjs"; import { a as isConfigReadFailure, n as createConfigReadError } from "./io.invalid-config-BqEcsQ06.mjs"; import { r as withArtifactPreservingStateReads } from "./artifact-preserving-state-reads-BQnQLAJR.mjs"; import { o as resolveCompatibilityHostVersion } from "./version-D0DaWqMn.mjs"; import { a as resolveUpdateRehearsalRoot } from "./update-rehearsal-paths-FgHeU_TR.mjs"; import { i as listAgentIds } from "./agent-roster-DH6r_8f4.mjs"; import "./agent-scope-config-BEk6-ThS.mjs"; import { _ as readAgentDatabaseAdmissionRefusal } from "./agent-database-admission-CRwW7FsI.mjs"; import { f as withOpenClawStateDatabaseReadSnapshot } from "./openclaw-state-db-readonly-ywwvuxdQ.mjs"; import { f as shouldDeferConfiguredPluginInstallRepair } from "./update-phase-YMT_h9sM.mjs"; import { b as formatPluginVerificationDiagnostic, h as buildDegradedPluginsFromVerificationFailures, m as PLUGIN_AVAILABILITY_POLICY, v as describePluginAvailabilityFailure } from "./discovery-D42cUZXk.mjs"; import { f as resolveEffectiveEnableState, u as normalizePluginsConfig } from "./config-state-Ba1MVBAI.mjs"; import { t as createConfigIO } from "./io.factory-QmLuiLlX.mjs"; import { n as completePluginMetadataSnapshot } from "./plugin-metadata-snapshot-DZdnLZdU.mjs"; import { c as readConfigFileSnapshot, d as readConfigFileSnapshotWithPluginMetadata } from "./io.runtime-B134MOMz.mjs"; import "./io-BGddrCVX.mjs"; import { t as note } from "./note-Bi0VsI6R.mjs"; import { m as withDeferredPluginDoctorMigrations } from "./doctor-contract-registry-6RykhA6j.mjs"; import { t as describeConfigSnapshotInputChange } from "./snapshot-inputs-DvxsX3U7.mjs"; import { a as throwStartupMigrationIdentityChanged, i as throwStartupMigrationGuardRejected, n as refuseStartupMigrationsForLiveGatewayOwner, r as rethrowStartupConfigFailure } from "./doctor-startup-migration-refusal-Bmnke-_e.mjs"; import { t as addDoctorLegacyIssues } from "./legacy-config-issues-CFxv73h_.mjs"; //#region src/commands/doctor-config-preflight-measure.ts async function measureDoctorConfigPreflightStep(name, run, measure, metrics) { const tracedRun = () => measureGatewayBootstrapStep(`cli.bootstrap.${name}`, run, metrics); return measure ? await measure(`doctor.config-preflight.${name}`, tracedRun) : await tracedRun(); } //#endregion //#region src/commands/config-preflight-snapshot.ts const STARTUP_STATE_ADMISSION_TIMEOUT_MS = 3e5; async function readConfigPreflightSnapshot(params) { const cache = params.allowCurrentPluginMetadata ? getPluginCache() : createPluginCache(); return withPluginCache(cache, async () => { const sharedOptions = { ...params.isolateEnv ? { isolateEnv: true } : {}, ...params.observe === false ? { observe: false } : {}, ...params.measure ? { measure: params.measure } : {}, ...params.allowCurrentPluginMetadata ? {} : { allowCurrentPluginMetadata: false } }; let deferred = params.deferredPluginMigrations; if (params.preparePluginMigrations) { const core = await readConfigFileSnapshot({ ...sharedOptions, pluginValidation: "core-only", deferredPluginMigrations: deferred }); await params.prepareSnapshot?.(core); deferred = await params.preparePluginMigrations(core); } const readOptions = { ...sharedOptions, deferredPluginMigrations: deferred }; return withDeferredPluginDoctorMigrations(deferred?.map((entry) => entry.pluginId) ?? [], async () => { if (params.includePluginMetadata && !params.skipPluginValidation) { const result = await readConfigFileSnapshotWithPluginMetadata(readOptions); const pluginMetadataSnapshot = completePluginMetadataSnapshot({ snapshot: result.pluginMetadataSnapshot, config: result.snapshot.sourceConfig ?? result.snapshot.config ?? {} }); return { snapshot: params.purpose === "doctor" || !result.snapshot.valid ? addDoctorLegacyIssues(result.snapshot, pluginMetadataSnapshot) : result.snapshot, ...pluginMetadataSnapshot ? { pluginMetadataSnapshot } : {} }; } const snapshot = await readConfigFileSnapshot({ ...readOptions, skipPluginValidation: params.skipPluginValidation }); if (!params.preparePluginMigrations) await params.prepareSnapshot?.(snapshot); return { snapshot: params.purpose === "doctor" || !snapshot.valid ? addDoctorLegacyIssues(snapshot) : snapshot }; }); }); } /** Admit the same config and state before the lease and again before persistent writes. */ async function readAdmittedConfigSnapshot(params) { return await withArtifactPreservingStateReads(async () => { await measureDoctorConfigPreflightStep("admission.live-owner", () => refuseStartupMigrationsForLiveGatewayOwner(params.env)); try { const selected = await measureDoctorConfigPreflightStep("admission.core-config", () => readConfigFileSnapshot({ observe: false, isolateEnv: true, pluginValidation: "core-only" })); if (isConfigReadFailure(selected) || selected.issues.some((issue) => issue.errorCode === "CONFIG_SOURCE_INVALID")) return { snapshot: selected }; const admitted = await measureDoctorConfigPreflightStep("admission.state-snapshot", () => withOpenClawStateDatabaseReadSnapshot(async () => { const recoveryOptions = { configPath: selected.path, observe: false, env: params.env }; const coreRecovery = await measureDoctorConfigPreflightStep("admission.core-recovery", () => createConfigIO({ ...recoveryOptions, pluginValidation: "core-only" }).prepareConfigRecovery(selected)); const candidate = coreRecovery?.snapshot ?? selected; await assertStartupStateReady({ cfg: candidate.sourceConfig ?? candidate.config, env: params.env }); if (candidate.valid) await params.validateConfig?.(candidate); let read = await measureDoctorConfigPreflightStep("admission.plugin-config", () => params.readSnapshot(coreRecovery ? { isolateEnv: true } : void 0)); assertPreflightConfigUnchanged(selected, read.snapshot); const recovery = await measureDoctorConfigPreflightStep("admission.config-recovery", () => createConfigIO(recoveryOptions).prepareConfigRecovery(read.snapshot)); if (Boolean(coreRecovery) !== Boolean(recovery)) throwStartupMigrationIdentityChanged(); if (recovery) { assertPreflightConfigUnchanged(candidate, recovery.snapshot); read = { snapshot: recovery.snapshot, pluginMetadataSnapshot: recovery.pluginMetadataSnapshot }; } if (read.snapshot.valid) { await params.validateConfig?.(read.snapshot); await measureDoctorConfigPreflightStep("admission.device-identity", async () => { const { loadDeviceIdentityIfPresent } = await import("./device-identity-CVa77z9y.mjs"); loadDeviceIdentityIfPresent({ env: params.env }); }); } return { ...read, ...recovery ? { recovery } : {} }; }, { env: params.env, admissionTimeoutMs: STARTUP_STATE_ADMISSION_TIMEOUT_MS })); if (params.beforeStatePreparation && !await measureDoctorConfigPreflightStep("admission.config-guard", () => params.beforeStatePreparation?.(admitted.snapshot))) throwStartupMigrationGuardRejected(); return admitted; } catch (error) { return rethrowStartupConfigFailure(error); } }); } function assertPreflightConfigUnchanged(before, after) { const unreadable = [before, after].find(isConfigReadFailure); if (unreadable) throw createConfigReadError(unreadable); const change = describeConfigSnapshotInputChange(before, after); if (change) throwStartupMigrationIdentityChanged(change); } /** Admission runs before lease acquisition: even acquiring a lease commits SQLite writes. */ async function assertStartupStateReady(params) { const { assertOpenClawDatabasesReady } = await measureDoctorConfigPreflightStep("admission.database-runtime-import", () => import("./openclaw-database-preflight-B5LWN22M.mjs")); const agentCount = listAgentIds(params.cfg).length; const admissionMetrics = { agentCount }; await measureDoctorConfigPreflightStep("admission.database-readiness", () => assertOpenClawDatabasesReady({ env: params.env, config: params.cfg, operation: "gateway-startup", onAgentInspection: (stats) => { Object.assign(admissionMetrics, stats); } }), void 0, () => admissionMetrics); const [{ assertSessionStoreMigrationComplete }, { resolveAllAgentSessionStoreCandidateTargetsSync }, { inspectOpenClawRegisteredAgentDatabases }] = await measureDoctorConfigPreflightStep("admission.session-runtime-import", () => Promise.all([ import("./startup-migration-DiV4EFgi.mjs"), import("./targets-5qHRX6W6.mjs"), import("./openclaw-agent-db-registry-ncpiYUTR.mjs") ])); const registeredDatabases = await measureDoctorConfigPreflightStep("admission.agent-inventory", () => inspectOpenClawRegisteredAgentDatabases({ env: params.env, includeIncompatibleSchemaVersions: true })); const targets = await measureDoctorConfigPreflightStep("admission.session-targets", () => resolveAllAgentSessionStoreCandidateTargetsSync(params.cfg, { env: params.env, registeredDatabases }).filter((target) => !readAgentDatabaseAdmissionRefusal(target.agentId, { env: params.env })), void 0, () => ({ agentCount, registeredDatabaseCount: registeredDatabases.length })); await measureDoctorConfigPreflightStep("admission.session-readiness", () => assertSessionStoreMigrationComplete({ ...params, targets }), void 0, () => ({ targetCount: targets.length })); const { assertConfiguredWorkspaceStateReady } = await measureDoctorConfigPreflightStep("admission.workspace-runtime-import", () => import("./workspace-state-dirs-DjYXDBqE.mjs")); await measureDoctorConfigPreflightStep("admission.workspace-readiness", () => assertConfiguredWorkspaceStateReady(params), void 0, () => ({ agentCount })); } //#endregion //#region src/commands/doctor-config-preflight-plugin-verification.ts async function planStartupPluginVerification(params) { const { planStartupPluginConvergence } = await measureDoctorConfigPreflightStep("plugin-plan-import", () => import("./startup-plugin-convergence-plan-RrozYpNT.mjs"), params.measure); return await measureDoctorConfigPreflightStep("plugin-plan", () => planStartupPluginConvergence({ config: params.cfg, env: params.env }), params.measure); } function buildStartupPluginQuarantine(params) { return buildDegradedPluginsFromVerificationFailures(params.failures.filter((failure) => resolveEffectiveEnableState({ id: failure.pluginId, origin: "global", config: normalizePluginsConfig(params.cfg.plugins), rootConfig: params.cfg }).enabled)); } async function runDoctorPluginConvergence(params) { const plan = await planStartupPluginVerification(params); if (!plan.required) return { quarantinedPlugins: [] }; const { inspectPluginMigrationAvailability } = await import("./plugin-migration-availability-DyQZIHSo.mjs"); const isUpdateRehearsal = Boolean(resolveUpdateRehearsalRoot(params.env)); if (isUpdateRehearsal) note("Plugin refresh deferred to live update finalization; the canary verifies copied plugin payloads without downloading replacements.", `Doctor ${PLUGIN_AVAILABILITY_POLICY.severity}s`); if (isUpdateRehearsal || shouldDeferConfiguredPluginInstallRepair(params.env)) { const payloads = await verifyStartupPluginPayloads(params, plan.installRecords); const { pending, ...migrationInspection } = await inspectPluginMigrationAvailability({ ...params, installRecords: plan.installRecords, deferInstallation: true }); return { ...payloads, migrationInspection, deferredPlugins: [...new Map([...payloads.deferredPlugins ?? [], ...pending].map((entry) => [entry.pluginId, entry])).values()] }; } const { runPostCorePluginConvergence } = await measureDoctorConfigPreflightStep("plugin-convergence-import", () => import("./post-core-plugin-convergence-Czdqh8_0.mjs"), params.measure); const convergence = await measureDoctorConfigPreflightStep("plugin-convergence", () => runPostCorePluginConvergence({ cfg: params.cfg, env: params.env, compatibilityHostVersion: resolveCompatibilityHostVersion(params.env) }), params.measure); if (convergence.changes.length > 0) note(convergence.changes.map((entry) => `- ${entry}`).join("\n"), "Doctor changes"); const notices = convergence.notices ?? []; if (notices.length > 0) note(notices.map((notice) => `- ${notice.message} ${notice.guidance.join(" ")}`.trim()).join("\n"), "Doctor notices"); const warnings = convergence.warnings.map((warning) => `${warning.message} ${warning.guidance.join(" ")}`.trim()); if (warnings.length > 0) note(warnings.map((warning) => `- ${warning}`).join("\n"), `Doctor ${PLUGIN_AVAILABILITY_POLICY.severity}s`); const quarantinedPlugins = buildStartupPluginQuarantine({ cfg: params.cfg, failures: convergence.smokeFailures }); const { pending, ...migrationInspection } = await inspectPluginMigrationAvailability({ ...params, installRecords: convergence.installRecords, deferInstallation: false }); const deferredPlugins = new Map(pending.map((plugin) => [plugin.pluginId, plugin])); const deferPlugin = (pluginId, reason) => { deferredPlugins.set(pluginId, { ...deferredPlugins.get(pluginId), pluginId, reason, command: "openclaw update repair" }); }; for (const warning of convergence.warnings) if (warning.pluginId) deferPlugin(warning.pluginId, warning.reason); for (const plugin of quarantinedPlugins) deferPlugin(plugin.pluginId, plugin.diagnostic.detail); return { ...warnings.length > 0 ? { warnings } : {}, quarantinedPlugins, ...migrationInspection.requiredPluginIds.length > 0 || migrationInspection.inspectionRequiredPluginIds.length > 0 || migrationInspection.statelessPlugins.length > 0 ? { migrationInspection } : {}, ...deferredPlugins.size > 0 ? { deferredPlugins: [...deferredPlugins.values()] } : {} }; } async function refreshStartupPluginQuarantine(params) { const plan = await planStartupPluginVerification(params); if (!plan.required) return { quarantinedPlugins: [] }; if (resolveUpdateRehearsalRoot(params.env) || shouldDeferConfiguredPluginInstallRepair(params.env)) return verifyStartupPluginPayloads(params, plan.installRecords); const warnings = []; const { maybeRepairPluginOpenClawHostLinks } = await import("./doctor-plugin-host-links-B1Va4sUf.mjs"); let effectFailure; try { await maybeRepairPluginOpenClawHostLinks({ env: params.env, installRecords: plan.installRecords, prompter: { shouldRepair: true }, beforePersistentEffect: async () => { if (effectFailure) throw effectFailure.error; try { await params.beforePersistentEffect?.(); } catch (error) { effectFailure = { error }; throw error; } } }); } catch (error) { if (effectFailure) throw effectFailure.error; warnings.push(`Could not repair OpenClaw host links: ${String(error)}. Run openclaw doctor --fix.`); } const verification = await verifyStartupPluginPayloads(params, plan.installRecords); return { ...verification, ...warnings.length > 0 ? { warnings: [...warnings, ...verification.warnings ?? []] } : {} }; } async function verifyStartupPluginPayloads(params, records) { const { runActivePluginPayloadSmokeCheck } = await measureDoctorConfigPreflightStep("plugin-payload-verification-import", () => import("./active-payload-verification-Bxb_pwgs.mjs"), params.measure); const smoke = await measureDoctorConfigPreflightStep("plugin-payload-verification", () => runActivePluginPayloadSmokeCheck({ cfg: params.cfg, records, env: params.env }), params.measure); const quarantinedPlugins = buildStartupPluginQuarantine({ cfg: params.cfg, failures: smoke.failures }); if (quarantinedPlugins.length > 0) note(quarantinedPlugins.map((plugin) => `- ${describePluginAvailabilityFailure(plugin.pluginId, formatPluginVerificationDiagnostic(plugin.diagnostic)).message}`).join("\n"), `Doctor ${PLUGIN_AVAILABILITY_POLICY.severity}s`); return { quarantinedPlugins, deferredPlugins: quarantinedPlugins.map((plugin) => ({ pluginId: plugin.pluginId, reason: plugin.diagnostic.detail, command: "openclaw update repair" })) }; } //#endregion export { readConfigPreflightSnapshot as a, readAdmittedConfigSnapshot as i, runDoctorPluginConvergence as n, measureDoctorConfigPreflightStep as o, assertPreflightConfigUnchanged as r, refreshStartupPluginQuarantine as t };