openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
203 lines (202 loc) • 12.5 kB
JavaScript
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 };