UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

258 lines (257 loc) • 15.8 kB
import { o as resolveUserPath } from "./home-dir-Dc6nN6HB.mjs"; import { t as safeParseJson } from "./json-coercion-pfKbZnCJ.mjs"; import { a as getNodeSqliteKysely, i as executeSqliteQueryTakeFirstSync, t as compileSqliteQueryBindings } from "./kysely-sync-K7ftY-g4.mjs"; import { o as resolveCompatibilityHostVersion } from "./version-BJGPJZu6.mjs"; import { l as runOpenClawStateWriteTransaction } from "./openclaw-state-db-D1jWDzK3.mjs"; import { n as readOpenClawStateLease } from "./openclaw-state-lease-store-gySh31JU.mjs"; import { o as isGatewayPluginMetadataSnapshotActive } from "./current-plugin-metadata-state-CfLPTM0e.mjs"; import { t as clearPluginMetadataLifecycleCaches } from "./plugin-metadata-lifecycle-BSDrXrxO.mjs"; import { t as INSTALLED_PLUGIN_INDEX_STATE_KEY } from "./installed-plugin-index-row-CeyMwv4w.mjs"; import { c as hasMissingConfigPathActivationMetadata, g as withBundledPluginEnablementCompat, h as isBundledProviderCompatPlugin, l as INSTALLED_PLUGIN_INDEX_WARNING, n as hasInstalledPluginIndexWorkspaceScopeMismatch, o as refreshInstalledPluginIndex, p as extractPluginInstallRecordsFromInstalledPluginIndex, v as resolveCompatRegistryVersion, y as resolveInstalledPluginIndexPolicyHash } from "./installed-plugin-index-BayjZgqB.mjs"; import { t as isPluginEnabledByDefaultForPlatform } from "./default-enablement-CEIbpabL.mjs"; import { f as resolveEffectiveEnableState, u as normalizePluginsConfig } from "./config-state-DY_WJYue.mjs"; import { n as hashStableJson } from "./installed-plugin-index-hash-N3AVaUCF.mjs"; import { r as resolveInstalledPluginIndexInstallOwner, t as isInstalledPluginIndexInstallOwnerAmbiguous } from "./installed-plugin-index-install-owner-CQJv99I6.mjs"; import { i as createPluginInstallRecordMap, l as serializePluginInstallRecordMap, o as inspectPluginInstallRecordMap, s as parsePluginInstallRecord, u as setPluginInstallRecordMapEntry } from "./installed-plugin-record-match-CVFkDlud.mjs"; import { n as resolveInstalledPluginIndexStateDatabaseOptions, r as resolveInstalledPluginIndexStorePath, t as legacyInstalledPluginIndexUnsupportedMessage } from "./installed-plugin-index-store-path-vGDuKHrq.mjs"; import { o as clearLoadInstalledPluginIndexInstallRecordsCache, t as findForeignManagedNpmInstallRecordPluginIds } from "./installed-plugin-index-record-reader-BP4r74yH.mjs"; import { t as preservePluginSourceAdmissions } from "./installed-plugin-index-source-admissions-BEyyngFY.mjs"; import { r as readPersistedInstalledPluginIndexSync, t as parseInstalledPluginIndex } from "./installed-plugin-index-store-CC8tZSgR.mjs"; import { n as hasMissingInstalledPluginOwnerMetadata } from "./installed-plugin-package-ownership-EjDH4q5b.mjs"; import { a as PLUGIN_LIFECYCLE_LEASE_IDENTITY, i as withPluginLifecycleLease } from "./plugin-lifecycle-lease-DHQMWweY.mjs"; import { existsSync } from "node:fs"; import { isDeepStrictEqual } from "node:util"; //#region src/plugins/installed-plugin-index-store-write.ts /** Writes, restores, and refreshes the installed plugin index in the state database. */ function readInstalledPluginIndexRow(database) { return executeSqliteQueryTakeFirstSync(database, getNodeSqliteKysely(database).selectFrom("config_machine_state").select([ "state_key", "value_json", "updated_at_ms" ]).where("state_key", "=", INSTALLED_PLUGIN_INDEX_STATE_KEY)); } function parseInstalledPluginIndexRow(row) { if (!row) return; const value = safeParseJson(row.value_json); if (!value || typeof value !== "object" || typeof value.revision !== "number") return; return value; } function assertWritableInstalledPluginIndexStoreOptions(options) { if (options.filePath?.endsWith(".json")) throw new Error(legacyInstalledPluginIndexUnsupportedMessage(options.filePath)); } 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.map((diagnostic) => diagnostic.level === "info" && diagnostic.code === "explicit-config-plugin-selection" ? { ...diagnostic, level: "warn" } : diagnostic) }; const valueJson = JSON.stringify({ revision, index: persistedIndex }); const { compiled, bind } = compileSqliteQueryBindings(() => getNodeSqliteKysely(database).insertInto("config_machine_state").values({ state_key: INSTALLED_PLUGIN_INDEX_STATE_KEY, value_json: valueJson, updated_at_ms: revision }).onConflict((conflict) => conflict.column("state_key").doUpdateSet((eb) => ({ value_json: eb.ref("excluded.value_json"), updated_at_ms: eb.ref("excluded.updated_at_ms") })))); database.prepare(compiled.sql).run(...bind()); } function writePersistedInstalledPluginIndexToSqlite(index, options, lease, preserveAdmissions = false) { assertWritableInstalledPluginIndexStoreOptions(options); const persisted = preparePersistedInstalledPluginIndex(index); return runOpenClawStateWriteTransaction(({ db, path: databasePath }) => { const before = readInstalledPluginIndexRow(db); const previousRow = parseInstalledPluginIndexRow(before); 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); if (preserveAdmissions) preservePluginSourceAdmissions(previousRow ? parseInstalledPluginIndex(previousRow.index) : null, persisted); const revision = resolveNextInstalledPluginIndexRevision(previousRow ? previousRow.revision : null); writePersistedInstalledPluginIndexRow(db, persisted, revision); const after = readInstalledPluginIndexRow(db); if (!after) throw new Error("Installed plugin index write did not persist its row"); return { previous: previousRow ? parseInstalledPluginIndex(previousRow.index) : null, revision, mutation: { databasePath, before: before ?? null, after } }; }, resolveInstalledPluginIndexStateDatabaseOptions(options)); } /** Merge prepared facts against the worker's current row, never the caller's old inventory. */ function publishPluginSourceAdmissionInDatabase(database, publication) { if (readOpenClawStateLease(database, PLUGIN_LIFECYCLE_LEASE_IDENTITY)) return false; const stored = parseInstalledPluginIndexRow(readInstalledPluginIndexRow(database)); const index = stored ? parseInstalledPluginIndex(stored.index) : null; const plugin = index?.plugins.find((record) => record.pluginId === publication.pluginId); if (!stored || !index || !plugin || plugin.rootDir !== publication.rootDir || plugin.installRecordHash !== publication.installRecordHash) return false; if (isDeepStrictEqual(plugin.sourceAdmissions?.[publication.key], publication.receipt)) return true; plugin.sourceAdmissions = { ...plugin.sourceAdmissions, [publication.key]: publication.receipt }; writePersistedInstalledPluginIndexRow(database, index, resolveNextInstalledPluginIndexRevision(stored.revision)); return true; } function clearPersistedInstalledPluginIndexCaches() { if (!isGatewayPluginMetadataSnapshotActive()) clearPluginMetadataLifecycleCaches(); clearLoadInstalledPluginIndexInstallRecordsCache(); } async function writePersistedInstalledPluginIndex(index, options = {}) { assertWritableInstalledPluginIndexStoreOptions(options); return await withPluginLifecycleLease(resolveInstalledPluginIndexStateDatabaseOptions(options), async (lease) => writePersistedInstalledPluginIndexWithLeaseSync(index, { ...options, lease })); } /** 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 = parseInstalledPluginIndexRow(readInstalledPluginIndexRow(db) ?? null ?? void 0); const currentRevision = currentRow ? currentRow.revision : null; if (currentRevision !== expectedRevision) return false; if (index) writePersistedInstalledPluginIndexRow(db, preparePersistedInstalledPluginIndex(index), resolveNextInstalledPluginIndexRevision(currentRevision)); else { const { compiled, bind } = compileSqliteQueryBindings(() => getNodeSqliteKysely(db).deleteFrom("config_machine_state").where("state_key", "=", INSTALLED_PLUGIN_INDEX_STATE_KEY)); db.prepare(compiled.sql).run(...bind()); } return true; }, resolveInstalledPluginIndexStateDatabaseOptions(storeOptions)); clearPersistedInstalledPluginIndexCaches(); return restored; } 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 })) }; } 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) }); } async function refreshPersistedInstalledPluginIndex(params) { const { lease: callerLease, ...storeParams } = params; assertWritableInstalledPluginIndexStoreOptions(storeParams); return await withPluginLifecycleLease(resolveInstalledPluginIndexStateDatabaseOptions(storeParams), async (lease) => { const index = resolveRefreshedPersistedInstalledPluginIndex(storeParams); writePersistedInstalledPluginIndexToSqlite(index, storeParams, { assertOwnedInTransaction(database) { lease.assertOwnedInTransaction(database); callerLease?.assertOwnedInTransaction(database); } }, true); clearPersistedInstalledPluginIndexCaches(); return index; }); } function refreshPersistedInstalledPluginIndexWithLeaseSync(params) { const { lease, ...storeParams } = params; const receipt = writePersistedInstalledPluginIndexToSqlite(resolveRefreshedPersistedInstalledPluginIndex(storeParams), storeParams, lease, true); clearPersistedInstalledPluginIndexCaches(); return receipt; } //#endregion export { writePersistedInstalledPluginIndex as a, restorePersistedInstalledPluginIndexIfCurrent as i, refreshPersistedInstalledPluginIndex as n, writePersistedInstalledPluginIndexWithLeaseSync as o, refreshPersistedInstalledPluginIndexWithLeaseSync as r, publishPluginSourceAdmissionInDatabase as t };