UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

203 lines (202 loc) 12.5 kB
import { c as resolveUserPath } from "./home-dir-BPhrG-aM.js"; import { o as isGatewayPluginMetadataSnapshotActive } from "./current-plugin-metadata-state-B1UoAr4G.js"; import { t as clearPluginMetadataLifecycleCaches } from "./plugin-metadata-lifecycle-C6m-q5Au.js"; import { o as resolveCompatibilityHostVersion } from "./version-v1kuAkGj.js"; import { s as runOpenClawStateWriteTransaction } from "./openclaw-state-db-BRTnL-D8.js"; import { _ as resolveInstalledPluginIndexPolicyHash, c as INSTALLED_PLUGIN_INDEX_WARNING, f as extractPluginInstallRecordsFromInstalledPluginIndex, g as resolveCompatRegistryVersion, h as withBundledPluginEnablementCompat, m as isBundledProviderCompatPlugin, r as hasInstalledPluginIndexWorkspaceScopeMismatch, s as refreshInstalledPluginIndex } from "./installed-plugin-index-wrDsjyMD.js"; import { l as normalizePluginsConfig, u as resolveEffectiveEnableState } from "./config-state-BkU1frVq.js"; import { C as hashStableJson } from "./discovery-D_VDsZuY.js"; import { r as resolveInstalledPluginIndexInstallOwner, t as isInstalledPluginIndexInstallOwnerAmbiguous } from "./installed-plugin-index-install-owner-Bd-Byre8.js"; import { t as isPluginEnabledByDefaultForPlatform } from "./default-enablement-CEIbpabL.js"; import { a as inspectPluginInstallRecordMap, c as serializePluginInstallRecordMap, l as setPluginInstallRecordMapEntry, o as parsePluginInstallRecord, r as createPluginInstallRecordMap } from "./plugin-install-record-map-B44vZyl2.js"; import { d as resolveInstalledPluginIndexStorePath, s as clearLoadInstalledPluginIndexInstallRecordsCache, t as findForeignManagedNpmInstallRecordPluginIds, u as resolveInstalledPluginIndexStateDatabaseOptions } from "./installed-plugin-index-record-reader-SXWwf_BU.js"; import { r as hasMissingConfigPathActivationMetadata } from "./installed-plugin-index-config-path-scope-BAeCI-f_.js"; import { i as readInstalledPluginIndexRow, o as readPersistedInstalledPluginIndexSync, r as parseInstalledPluginIndexSqliteRow, t as INSTALLED_PLUGIN_INDEX_STATE_KEY } from "./installed-plugin-index-store-MVV6aa7C.js"; import { n as hasMissingInstalledPluginOwnerMetadata } from "./installed-plugin-package-ownership-DGTVMT4h.js"; import { existsSync } from "node:fs"; //#region src/plugins/installed-plugin-index-store-write.ts /** Writes, restores, and refreshes the installed plugin index in the state database. */ function assertWritableInstalledPluginIndexStoreOptions(options) { if (options.filePath?.endsWith(".json")) throw new Error("Explicit JSON installed plugin index paths are retired. Use the shared SQLite state DB or run openclaw doctor --fix to migrate legacy plugins/installs.json."); } function preparePersistedInstalledPluginIndex(index) { const installRecords = createPluginInstallRecordMap(); for (const [pluginId, rawRecord] of Object.entries(index.installRecords)) { const record = parsePluginInstallRecord(rawRecord); if (!record) throw new Error("Invalid plugin install record"); setPluginInstallRecordMapEntry(installRecords, pluginId, record); } return { ...index, warning: INSTALLED_PLUGIN_INDEX_WARNING, installRecords }; } function resolveNextInstalledPluginIndexRevision(current) { return Math.max(Date.now(), (current ?? 0) + 1); } function writePersistedInstalledPluginIndexRow(database, index, revision) { const persistedIndex = { version: index.version, warning: index.warning ?? "DO NOT EDIT. This file is generated by OpenClaw from plugin manifests, install records, and config policy. Use `openclaw plugins registry --refresh`, `openclaw plugins install/update/uninstall`, or `openclaw plugins enable/disable` instead.", hostContractVersion: index.hostContractVersion, compatRegistryVersion: index.compatRegistryVersion, migrationVersion: index.migrationVersion, policyHash: index.policyHash, generatedAtMs: index.generatedAtMs, ...index.workspaceDir !== void 0 ? { workspaceDir: index.workspaceDir } : {}, ...index.refreshReason ? { refreshReason: index.refreshReason } : {}, installRecords: JSON.parse(serializePluginInstallRecordMap(index.installRecords)), plugins: index.plugins.map((plugin) => { const installOwner = resolveInstalledPluginIndexInstallOwner(plugin); return { ...plugin, ...installOwner ? { installOwner } : {}, ...isInstalledPluginIndexInstallOwnerAmbiguous(plugin) ? { installOwnerAmbiguous: true } : {} }; }), diagnostics: index.diagnostics }; const valueJson = JSON.stringify({ revision, index: persistedIndex }); database.prepare(` INSERT INTO config_machine_state (state_key, value_json, updated_at_ms) VALUES (?, ?, ?) ON CONFLICT(state_key) DO UPDATE SET value_json = excluded.value_json, updated_at_ms = excluded.updated_at_ms `).run(INSTALLED_PLUGIN_INDEX_STATE_KEY, valueJson, revision); } function writePersistedInstalledPluginIndexToSqlite(index, options = {}, lease) { assertWritableInstalledPluginIndexStoreOptions(options); const persisted = preparePersistedInstalledPluginIndex(index); return runOpenClawStateWriteTransaction(({ db }) => { const previousRow = readInstalledPluginIndexRow(db); if (previousRow) { const previousInstallRecords = previousRow.index?.installRecords; if (previousInstallRecords === void 0 || inspectPluginInstallRecordMap(previousInstallRecords).status === "invalid") throw new Error("Persisted plugin install records are invalid. Repair the state before writing plugin installation metadata."); } lease?.assertOwnedInTransaction(db); const revision = resolveNextInstalledPluginIndexRevision(previousRow ? previousRow.revision : null); writePersistedInstalledPluginIndexRow(db, persisted, revision); return { previous: parseInstalledPluginIndexSqliteRow(previousRow), revision }; }, resolveInstalledPluginIndexStateDatabaseOptions(options)); } function clearPersistedInstalledPluginIndexCaches() { if (!isGatewayPluginMetadataSnapshotActive()) clearPluginMetadataLifecycleCaches(); clearLoadInstalledPluginIndexInstallRecordsCache(); } async function writePersistedInstalledPluginIndex(index, options = {}) { return writePersistedInstalledPluginIndexSync(index, options); } /** Restore a snapshot only while the caller's tentative write is still current. */ async function restorePersistedInstalledPluginIndexIfCurrent(index, expectedRevision, options) { const { lease, ...storeOptions } = options; assertWritableInstalledPluginIndexStoreOptions(storeOptions); if (!existsSync(resolveInstalledPluginIndexStorePath(storeOptions))) return false; const restored = runOpenClawStateWriteTransaction(({ db }) => { lease.assertOwnedInTransaction(db); const currentRow = readInstalledPluginIndexRow(db); const currentRevision = currentRow ? currentRow.revision : null; if (currentRevision !== expectedRevision) return false; if (index) writePersistedInstalledPluginIndexRow(db, preparePersistedInstalledPluginIndex(index), resolveNextInstalledPluginIndexRevision(currentRevision)); else db.prepare("DELETE FROM config_machine_state WHERE state_key = ?").run(INSTALLED_PLUGIN_INDEX_STATE_KEY); return true; }, resolveInstalledPluginIndexStateDatabaseOptions(storeOptions)); clearPersistedInstalledPluginIndexCaches(); return restored; } function writePersistedInstalledPluginIndexSync(index, options = {}) { const filePath = resolveInstalledPluginIndexStorePath(options); writePersistedInstalledPluginIndexToSqlite(index, options); clearPersistedInstalledPluginIndexCaches(); return filePath; } function writePersistedInstalledPluginIndexWithLeaseSync(index, options) { const { lease, ...storeOptions } = options; const filePath = resolveInstalledPluginIndexStorePath(storeOptions); writePersistedInstalledPluginIndexToSqlite(index, storeOptions, lease); clearPersistedInstalledPluginIndexCaches(); return filePath; } function hasCompletePolicyRefreshProjection(persisted, policyPluginIds, env) { const pluginIds = new Set(persisted.plugins.map((plugin) => plugin.pluginId)); if (policyPluginIds?.some((pluginId) => !pluginIds.has(pluginId))) return false; const installOwners = new Set(persisted.plugins.map(resolveInstalledPluginIndexInstallOwner)); return Object.entries(persisted.installRecords).every(([installOwner, record]) => { if (installOwners.has(installOwner)) return true; const installedPath = record.installPath?.trim() || record.sourcePath?.trim(); return !installedPath || !existsSync(resolveUserPath(installedPath, env)); }); } function canRefreshPersistedPolicyState(persisted, params) { if (!persisted || params.reason !== "policy-changed") return false; if ((params.diagnostics?.length ?? 0) > 0 || persisted.diagnostics.some((diagnostic) => diagnostic.code === "workspace-scope-omitted") || hasInstalledPluginIndexWorkspaceScopeMismatch(persisted, params.workspaceDir)) return false; const env = params.env ?? process.env; if (persisted.version !== 1 || persisted.hostContractVersion !== resolveCompatibilityHostVersion(env) || persisted.compatRegistryVersion !== resolveCompatRegistryVersion() || persisted.migrationVersion !== 1 || hasMissingConfigPathActivationMetadata(persisted) || hasMissingInstalledPluginOwnerMetadata(persisted, env)) return false; if (params.installRecords && hashStableJson(params.installRecords) !== hashStableJson(persisted.installRecords ?? {})) return false; return hasCompletePolicyRefreshProjection(persisted, params.policyPluginIds, env); } function refreshPersistedPolicyState(persisted, params) { const activationConfig = withBundledPluginEnablementCompat({ config: params.config, env: params.env, pluginIds: persisted.plugins.filter((plugin) => isBundledProviderCompatPlugin({ origin: plugin.origin, providers: plugin.contributions?.providers, contracts: plugin.contributions?.contracts })).map((plugin) => plugin.pluginId), activation: "defaults" }); const normalizedConfig = normalizePluginsConfig(activationConfig?.plugins); return { ...persisted, policyHash: resolveInstalledPluginIndexPolicyHash(params.config, params.env), generatedAtMs: (params.now?.() ?? /* @__PURE__ */ new Date()).getTime(), refreshReason: params.reason, plugins: persisted.plugins.map((plugin) => ({ ...plugin, enabled: resolveEffectiveEnableState({ id: plugin.pluginId, origin: plugin.origin, channelIds: plugin.contributions?.channels, config: normalizedConfig, rootConfig: activationConfig, enabledByDefault: isPluginEnabledByDefaultForPlatform(plugin) }).enabled })) }; } async function refreshPersistedInstalledPluginIndex(params) { return refreshPersistedInstalledPluginIndexSync(params); } function resolveRefreshedPersistedInstalledPluginIndex(params) { const persisted = params.reason === "policy-changed" || !params.installRecords ? readPersistedInstalledPluginIndexSync(params) : null; if (canRefreshPersistedPolicyState(persisted, params)) return refreshPersistedPolicyState(persisted, params); if (params.reason === "manual" && !params.installRecords) { const foreignPluginIds = findForeignManagedNpmInstallRecordPluginIds(extractPluginInstallRecordsFromInstalledPluginIndex(persisted), params); if (foreignPluginIds.length > 0) throw new Error(`Plugin registry refresh cannot verify npm install ownership outside the selected state directory: ${foreignPluginIds.join(", ")}. Reinstall copied plugins in this state directory, then run \`openclaw plugins registry --refresh\` again.`); } return refreshInstalledPluginIndex({ ...params, installRecords: params.installRecords ?? extractPluginInstallRecordsFromInstalledPluginIndex(persisted) }); } function refreshPersistedInstalledPluginIndexSync(params) { const index = resolveRefreshedPersistedInstalledPluginIndex(params); writePersistedInstalledPluginIndexSync(index, params); return index; } function refreshPersistedInstalledPluginIndexWithLeaseSync(params) { const { lease, ...storeParams } = params; const receipt = writePersistedInstalledPluginIndexToSqlite(resolveRefreshedPersistedInstalledPluginIndex(storeParams), storeParams, lease); clearPersistedInstalledPluginIndexCaches(); return receipt; } //#endregion export { writePersistedInstalledPluginIndex as a, restorePersistedInstalledPluginIndexIfCurrent as i, refreshPersistedInstalledPluginIndexSync as n, writePersistedInstalledPluginIndexSync as o, refreshPersistedInstalledPluginIndexWithLeaseSync as r, writePersistedInstalledPluginIndexWithLeaseSync as s, refreshPersistedInstalledPluginIndex as t };