UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

131 lines (130 loc) • 7.46 kB
import { k as withPluginCache, s as createPluginCache } from "./plugin-cache-CfBhQFSN.mjs"; import { f as withDeferredPluginConfigCompletion, l as readDeferredPluginMigrationsAsync } from "./deferred-plugin-migrations-DiP5orVE.mjs"; import { i as resolvePluginManifestInstallOwner } from "./manifest-registry-build-DXgw5t8c.mjs"; import { l as withPluginMetadataSnapshotScope } from "./current-plugin-metadata-snapshot-DpeEPkbV.mjs"; import { r as resolveConfigWidePluginMetadataSnapshot } from "./io.plugin-metadata-MbATaIAW.mjs"; import { n as findLegacyConfigRuleIssues } from "./legacy-DRcorE1g.mjs"; import { i as restoreDoctorConfigEnvRefs, r as prepareDoctorConfigReferenceSource } from "./config-flow-steps-BAOy-G5i.mjs"; import { t as HISTORICAL_WEBHOOK_CHANNELS } from "./legacy-webhook-pins-h4BYNMXM.mjs"; import { a as listPluginDoctorLegacyConfigRules, c as listPluginDoctorStateMigrationEntries, m as withDeferredPluginDoctorMigrations, n as applyPluginDoctorCompatibilityMigrations, r as collectDoctorConfigRepairPluginIds } from "./doctor-contract-registry-6RykhA6j.mjs"; import { t as inspectPluginMigrationAvailability } from "./plugin-migration-availability-DWT1FskW.mjs"; import { isDeepStrictEqual } from "node:util"; //#region src/commands/doctor/shared/plugin-update-config-migration.ts /** Complete selected config repairs within the package owner's publication and compensation. */ async function preparePluginUpdateConfigMigration(params) { params.assertCurrent(); const pending = await readDeferredPluginMigrationsAsync(); params.assertCurrent(); const selectedOwners = new Set(params.installOwners); const cache = createPluginCache(); const inspect = (config, run) => withPluginCache(cache, () => { const metadata = resolveConfigWidePluginMetadataSnapshot({ config, installRecords: params.installRecords, allowCurrent: false }); return withPluginMetadataSnapshotScope(metadata, () => run(metadata), { config }); }); try { const prepared = inspect(params.config, (metadata) => { const selectedIds = new Set(params.installOwners); for (const plugin of metadata.plugins) { const installOwner = resolvePluginManifestInstallOwner(plugin); if (installOwner && selectedOwners.has(installOwner)) selectedIds.add(plugin.id); } const selectedPending = pending.filter((entry) => selectedIds.has(entry.pluginId)); const inspected = /* @__PURE__ */ new Map(); const migrated = applyPluginDoctorCompatibilityMigrations(params.config, { historicalWebhookListeners: HISTORICAL_WEBHOOK_CHANNELS.some((id) => selectedIds.has(id)), config: params.config, pluginIds: [...selectedIds], manifestRegistry: metadata.manifestRegistry, onInspectedPlugin: (pluginId, hasConfigRepair) => inspected.set(pluginId, hasConfigRepair) }); if (migrated.warnings?.length) throw new Error(migrated.warnings.join("\n")); const unavailable = metadata.plugins.filter((plugin) => selectedIds.has(plugin.id) && plugin.doctorContract?.configRepair === true && inspected.get(plugin.id) !== true); if (unavailable.length > 0) throw new Error(`Plugin config repair could not be inspected: ${unavailable.map((plugin) => plugin.id).join(", ")}. Repair the plugin's Doctor artifact, then retry the update.`); if (migrated.changes.length === 0 && selectedPending.length === 0) return { config: params.config, selectedIds, selectedPending, inspected }; const unselected = collectDoctorConfigRepairPluginIds(params.config).filter((id) => !selectedIds.has(id)); return { config: withDeferredPluginDoctorMigrations(unselected, () => restoreDoctorConfigEnvRefs(migrated.config, prepareDoctorConfigReferenceSource(params.snapshot))), selectedIds, selectedPending, inspected }; }); params.assertCurrent(); const { config, selectedIds, selectedPending, inspected } = prepared; let activationWarning; return { config, changed: selectedPending.length > 0 || !isDeepStrictEqual(config, params.config), get activationWarning() { return activationWarning; }, async [Symbol.asyncDispose]() { await cache[Symbol.asyncDispose](); }, async publish(activationConfig, commit) { params.assertCurrent(); const availability = selectedPending.length > 0 ? await inspectPluginMigrationAvailability({ cfg: activationConfig, env: process.env, installRecords: params.installRecords, retainedPluginIds: [...selectedIds], deferInstallation: false }) : void 0; params.assertCurrent(); const resolvedPluginIds = inspect(activationConfig, (metadata) => { const rules = listPluginDoctorLegacyConfigRules({ config: activationConfig, pluginIds: [...selectedIds], manifestRegistry: metadata.manifestRegistry, activeOnly: true }); const legacyIssues = findLegacyConfigRuleIssues(activationConfig, rules); if (legacyIssues.length > 0) throw new Error(`Plugin config repair is incomplete: ${legacyIssues.map((issue) => `${issue.path}: ${issue.message}`).join("\n")}`); if (selectedPending.length === 0) return []; const active = /* @__PURE__ */ new Set(); const stateless = new Set(availability?.statelessPlugins.map(({ id }) => id).filter((id) => selectedIds.has(id))); listPluginDoctorStateMigrationEntries({ config: activationConfig, pluginIds: [...selectedIds], manifestRegistry: metadata.manifestRegistry, onSelectedPlugin: (id) => active.add(id), onInspectedStatelessPlugin: (id) => stateless.add(id) }); const known = new Map(metadata.plugins.map((plugin) => [plugin.id, plugin])); const uninspected = selectedPending.filter((entry) => active.has(entry.pluginId) && (entry.configPaths?.length || entry.validationExcludedPaths?.length || !known.get(entry.pluginId)?.doctorContract && entry.requiresDoctorInspection) && !inspected.has(entry.pluginId)); if (uninspected.length > 0) throw new Error(`Plugin config repair could not be inspected: ${uninspected.map((entry) => entry.pluginId).join(", ")}. Repair the plugin's Doctor artifact, then retry the update.`); const unresolved = selectedPending.filter((entry) => !known.has(entry.pluginId) || active.has(entry.pluginId) && (entry.requiresStateMigration || !stateless.has(entry.pluginId))); if (unresolved.length > 0) activationWarning = `Plugin packages are saved; activation is deferred for ${unresolved.map((entry) => entry.pluginId).join(", ")}. Existing data and settings are kept. Run openclaw doctor --fix to complete their data migrations, then reload the plugins.`; const unresolvedIds = new Set(unresolved.map((entry) => entry.pluginId)); return selectedPending.filter((entry) => active.has(entry.pluginId) && !unresolvedIds.has(entry.pluginId)).map((entry) => entry.pluginId); }); params.assertCurrent(); if (resolvedPluginIds.length === 0) return await commit(); return await withDeferredPluginConfigCompletion({ configPath: params.snapshot.path, expectedPending: pending, resolvedPluginIds, assertCurrent: params.assertCurrent }, commit); } }; } catch (error) { try { await cache[Symbol.asyncDispose](); } catch (cleanupError) { throw new AggregateError([error, cleanupError], "Plugin config migration preparation and cleanup failed", { cause: cleanupError }); } throw error; } } //#endregion export { preparePluginUpdateConfigMigration };