UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

349 lines (348 loc) 15.4 kB
import { o as resolveCompatibilityHostVersion } from "./version-Crcn9X9T.js"; import { t as isBlockedObjectKey } from "./prototype-keys-D2nJOZIy.js"; import { i as openOpenClawStateDatabase, o as runOpenClawStateWriteTransaction } from "./openclaw-state-db-ULtH_b7x.js"; import { l as clearLoadInstalledPluginIndexInstallRecordsCache, s as resolveInstalledPluginIndexStorePath } from "./installed-plugin-index-record-reader-CPHayOZI.js"; import { c as resolveEffectiveEnableState, s as normalizePluginsConfig } from "./config-state-CikNNz7T.js"; import { t as isPluginEnabledByDefaultForPlatform } from "./default-enablement-CEIbpabL.js"; import { t as clearPluginMetadataLifecycleCaches } from "./plugin-metadata-lifecycle-C3dWg4tn.js"; import { a as refreshInstalledPluginIndex, c as hasMissingConfigPathActivationMetadata, f as extractPluginInstallRecordsFromInstalledPluginIndex, g as hashJson, l as INSTALLED_PLUGIN_INDEX_WARNING, m as resolveInstalledPluginIndexPolicyHash, o as diffInstalledPluginIndexInvalidationReasons, p as resolveCompatRegistryVersion, r as loadInstalledPluginIndex } from "./installed-plugin-index-BSYm7o5l.js"; import { At as boolean, Et as array, Nn as record, Rn as string, Tn as object, Xn as union, Zn as unknown, dn as literal, wn as number } from "./schemas-6cH6bZ7o.js"; import { n as safeParseWithSchema } from "./zod-parse-Bip-sZi_.js"; import { existsSync, readFileSync } from "node:fs"; //#region src/plugins/installed-plugin-index-store.ts /** Persists, inspects, and refreshes the installed plugin index in the state database. */ const StringArraySchema = array(string()); const INSTALLED_PLUGIN_INDEX_SQLITE_KEY = "installed-plugin-index"; const InstalledPluginIndexStartupSchema = object({ sidecar: boolean(), memory: boolean(), deferConfiguredChannelFullLoadUntilAfterListen: boolean(), agentHarnesses: StringArraySchema, configPaths: StringArraySchema.optional() }); const InstalledPluginIndexContributionSchema = object({ channels: StringArraySchema, channelConfigs: StringArraySchema, providers: StringArraySchema, modelCatalogProviders: StringArraySchema, modelSupportPrefixes: StringArraySchema, modelSupportPatterns: StringArraySchema, autoEnableProviderIds: StringArraySchema, commandAliases: StringArraySchema, contracts: record(string(), StringArraySchema) }); const InstalledPluginFileSignatureSchema = object({ size: number(), mtimeMs: number(), ctimeMs: number().optional() }); const InstalledPluginIndexRecordSchema = object({ pluginId: string(), packageName: string().optional(), packageVersion: string().optional(), installRecord: record(string(), unknown()).optional(), installRecordHash: string().optional(), packageInstall: unknown().optional(), packageChannel: unknown().optional(), manifestPath: string(), manifestHash: string(), manifestFile: InstalledPluginFileSignatureSchema.optional(), format: string().optional(), bundleFormat: string().optional(), source: string().optional(), setupSource: string().optional(), packageJson: object({ path: string(), hash: string(), fileSignature: InstalledPluginFileSignatureSchema.optional() }).optional(), rootDir: string(), origin: string(), enabled: boolean(), enabledByDefault: boolean().optional(), enabledByDefaultOnPlatforms: StringArraySchema.optional(), syntheticAuthRefs: StringArraySchema.optional(), startup: InstalledPluginIndexStartupSchema, contributions: InstalledPluginIndexContributionSchema.optional(), compat: array(string()) }); const InstalledPluginInstallRecordSchema = record(string(), unknown()); const PluginDiagnosticSchema = object({ level: union([literal("warn"), literal("error")]), message: string(), pluginId: string().optional(), source: string().optional() }); const InstalledPluginIndexSchema = object({ version: literal(1), warning: string().optional(), hostContractVersion: string(), compatRegistryVersion: string(), migrationVersion: literal(1), policyHash: string(), generatedAtMs: number(), refreshReason: string().optional(), installRecords: record(string(), InstalledPluginInstallRecordSchema).optional(), plugins: array(InstalledPluginIndexRecordSchema), diagnostics: array(PluginDiagnosticSchema) }); function copySafeInstallRecords(records) { if (!records) return; const safeRecords = {}; for (const [pluginId, record] of Object.entries(records)) { if (isBlockedObjectKey(pluginId)) continue; safeRecords[pluginId] = record; } return safeRecords; } function parseInstalledPluginIndex(value) { const parsed = safeParseWithSchema(InstalledPluginIndexSchema, value); if (!parsed) return null; const installRecords = copySafeInstallRecords(parsed.installRecords) ?? copySafeInstallRecords(extractPluginInstallRecordsFromInstalledPluginIndex(parsed)) ?? {}; return { version: parsed.version, ...parsed.warning ? { warning: parsed.warning } : {}, hostContractVersion: parsed.hostContractVersion, compatRegistryVersion: parsed.compatRegistryVersion, migrationVersion: parsed.migrationVersion, policyHash: parsed.policyHash, generatedAtMs: parsed.generatedAtMs, ...parsed.refreshReason ? { refreshReason: parsed.refreshReason } : {}, installRecords, plugins: parsed.plugins, diagnostics: parsed.diagnostics }; } function resolveStateDatabaseOptions(options = {}) { if (options.filePath) return { ...options.env ? { env: options.env } : {}, path: options.filePath }; if (options.stateDir) return { env: { ...options.env ?? process.env, OPENCLAW_STATE_DIR: options.stateDir } }; return options.env ? { env: options.env } : {}; } function isExplicitLegacyJsonStorePath(options) { return Boolean(options.filePath && options.filePath.endsWith(".json")); } function readLegacyRecordContainer(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return; const legacy = value; return legacy.installRecords ?? legacy.records; } function readPersistedInstalledPluginIndexFromLegacyJson(options) { if (!options.filePath || !existsSync(options.filePath)) return null; try { const parsed = JSON.parse(readFileSync(options.filePath, "utf8")); const current = parseInstalledPluginIndex(parsed); if (current) return current; const installRecords = readLegacyRecordContainer(parsed); if (!installRecords) return null; return parseInstalledPluginIndex({ version: 1, hostContractVersion: "legacy-file", compatRegistryVersion: "legacy-file", migrationVersion: 1, policyHash: "legacy-file", generatedAtMs: 0, installRecords, plugins: [], diagnostics: [] }); } catch { return null; } } function assertWritableInstalledPluginIndexStoreOptions(options) { if (isExplicitLegacyJsonStorePath(options)) 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 parseJsonColumn(value) { try { return JSON.parse(value); } catch { return; } } function parseInstalledPluginIndexSqliteRow(row) { if (!row) return null; return parseInstalledPluginIndex({ version: Number(row.version), ...row.warning ? { warning: row.warning } : {}, hostContractVersion: row.host_contract_version, compatRegistryVersion: row.compat_registry_version, migrationVersion: Number(row.migration_version), policyHash: row.policy_hash, generatedAtMs: Number(row.generated_at_ms), ...row.refresh_reason ? { refreshReason: row.refresh_reason } : {}, installRecords: parseJsonColumn(row.install_records_json), plugins: parseJsonColumn(row.plugins_json), diagnostics: parseJsonColumn(row.diagnostics_json) }); } function readPersistedInstalledPluginIndexFromSqlite(options = {}) { if (isExplicitLegacyJsonStorePath(options)) return readPersistedInstalledPluginIndexFromLegacyJson(options); if (!existsSync(resolveInstalledPluginIndexStorePath(options))) return null; try { return parseInstalledPluginIndexSqliteRow(openOpenClawStateDatabase(resolveStateDatabaseOptions(options)).db.prepare(` SELECT version, warning, host_contract_version, compat_registry_version, migration_version, policy_hash, generated_at_ms, refresh_reason, install_records_json, plugins_json, diagnostics_json FROM installed_plugin_index WHERE index_key = ? `).get(INSTALLED_PLUGIN_INDEX_SQLITE_KEY)); } catch { return null; } } function writePersistedInstalledPluginIndexToSqlite(index, options = {}) { assertWritableInstalledPluginIndexStoreOptions(options); const persisted = { ...index, warning: INSTALLED_PLUGIN_INDEX_WARNING, installRecords: copySafeInstallRecords(index.installRecords) ?? {} }; const now = Date.now(); runOpenClawStateWriteTransaction(({ db }) => { db.prepare(` INSERT INTO installed_plugin_index ( index_key, version, host_contract_version, compat_registry_version, migration_version, policy_hash, generated_at_ms, refresh_reason, install_records_json, plugins_json, diagnostics_json, warning, updated_at_ms ) VALUES ( @index_key, @version, @host_contract_version, @compat_registry_version, @migration_version, @policy_hash, @generated_at_ms, @refresh_reason, @install_records_json, @plugins_json, @diagnostics_json, @warning, @updated_at_ms ) ON CONFLICT(index_key) DO UPDATE SET version = excluded.version, host_contract_version = excluded.host_contract_version, compat_registry_version = excluded.compat_registry_version, migration_version = excluded.migration_version, policy_hash = excluded.policy_hash, generated_at_ms = excluded.generated_at_ms, refresh_reason = excluded.refresh_reason, install_records_json = excluded.install_records_json, plugins_json = excluded.plugins_json, diagnostics_json = excluded.diagnostics_json, warning = excluded.warning, updated_at_ms = excluded.updated_at_ms `).run({ index_key: INSTALLED_PLUGIN_INDEX_SQLITE_KEY, version: persisted.version, host_contract_version: persisted.hostContractVersion, compat_registry_version: persisted.compatRegistryVersion, migration_version: persisted.migrationVersion, policy_hash: persisted.policyHash, generated_at_ms: persisted.generatedAtMs, refresh_reason: persisted.refreshReason ?? null, install_records_json: JSON.stringify(persisted.installRecords), plugins_json: JSON.stringify(persisted.plugins), diagnostics_json: JSON.stringify(persisted.diagnostics), warning: persisted.warning, updated_at_ms: now }); }, resolveStateDatabaseOptions(options)); } async function readPersistedInstalledPluginIndex(options = {}) { return readPersistedInstalledPluginIndexFromSqlite(options); } function readPersistedInstalledPluginIndexSync(options = {}) { return readPersistedInstalledPluginIndexFromSqlite(options); } async function writePersistedInstalledPluginIndex(index, options = {}) { const filePath = resolveInstalledPluginIndexStorePath(options); writePersistedInstalledPluginIndexToSqlite(index, options); clearPluginMetadataLifecycleCaches(); clearLoadInstalledPluginIndexInstallRecordsCache(); return filePath; } function writePersistedInstalledPluginIndexSync(index, options = {}) { const filePath = resolveInstalledPluginIndexStorePath(options); writePersistedInstalledPluginIndexToSqlite(index, options); clearPluginMetadataLifecycleCaches(); clearLoadInstalledPluginIndexInstallRecordsCache(); return filePath; } function hasPolicyRefreshTargets(persisted, policyPluginIds) { if (!policyPluginIds || policyPluginIds.length === 0) return true; const pluginIds = new Set(persisted.plugins.map((plugin) => plugin.pluginId)); return policyPluginIds.every((pluginId) => pluginIds.has(pluginId)); } function canRefreshPersistedPolicyState(persisted, params) { if (!persisted || params.reason !== "policy-changed") return false; const env = params.env ?? process.env; if (persisted.version !== 1 || persisted.hostContractVersion !== resolveCompatibilityHostVersion(env) || persisted.compatRegistryVersion !== resolveCompatRegistryVersion() || persisted.migrationVersion !== 1 || hasMissingConfigPathActivationMetadata(persisted)) return false; if (params.installRecords && hashJson(params.installRecords) !== hashJson(persisted.installRecords ?? {})) return false; return hasPolicyRefreshTargets(persisted, params.policyPluginIds); } function refreshPersistedPolicyState(persisted, params) { const normalizedConfig = normalizePluginsConfig(params.config?.plugins); return { ...persisted, policyHash: resolveInstalledPluginIndexPolicyHash(params.config), generatedAtMs: (params.now?.() ?? /* @__PURE__ */ new Date()).getTime(), refreshReason: params.reason, plugins: persisted.plugins.map((plugin) => ({ ...plugin, enabled: resolveEffectiveEnableState({ id: plugin.pluginId, origin: plugin.origin, config: normalizedConfig, rootConfig: params.config, enabledByDefault: isPluginEnabledByDefaultForPlatform(plugin) }).enabled })) }; } async function inspectPersistedInstalledPluginIndex(params = {}) { const persisted = await readPersistedInstalledPluginIndex(params); const current = loadInstalledPluginIndex({ ...params, installRecords: params.installRecords ?? extractPluginInstallRecordsFromInstalledPluginIndex(persisted) }); if (!persisted) return { state: "missing", refreshReasons: ["missing"], persisted: null, current }; const refreshReasons = diffInstalledPluginIndexInvalidationReasons(persisted, current); return { state: refreshReasons.length > 0 ? "stale" : "fresh", refreshReasons, persisted, current }; } async function refreshPersistedInstalledPluginIndex(params) { const persisted = params.reason === "policy-changed" || !params.installRecords ? await readPersistedInstalledPluginIndex(params) : null; if (canRefreshPersistedPolicyState(persisted, params)) { const index = refreshPersistedPolicyState(persisted, params); await writePersistedInstalledPluginIndex(index, params); return index; } const index = refreshInstalledPluginIndex({ ...params, installRecords: params.installRecords ?? extractPluginInstallRecordsFromInstalledPluginIndex(persisted) }); await writePersistedInstalledPluginIndex(index, params); return index; } function refreshPersistedInstalledPluginIndexSync(params) { const persisted = params.reason === "policy-changed" || !params.installRecords ? readPersistedInstalledPluginIndexSync(params) : null; if (canRefreshPersistedPolicyState(persisted, params)) { const index = refreshPersistedPolicyState(persisted, params); writePersistedInstalledPluginIndexSync(index, params); return index; } const index = refreshInstalledPluginIndex({ ...params, installRecords: params.installRecords ?? extractPluginInstallRecordsFromInstalledPluginIndex(persisted) }); writePersistedInstalledPluginIndexSync(index, params); return index; } //#endregion export { refreshPersistedInstalledPluginIndex as a, writePersistedInstalledPluginIndexSync as c, readPersistedInstalledPluginIndexSync as i, parseInstalledPluginIndex as n, refreshPersistedInstalledPluginIndexSync as o, readPersistedInstalledPluginIndex as r, writePersistedInstalledPluginIndex as s, inspectPersistedInstalledPluginIndex as t };