diff --git a/src/plugins/current-plugin-metadata-snapshot.ts b/src/plugins/current-plugin-metadata-snapshot.ts index 1fd1ed61223..95ac7e4fe94 100644 --- a/src/plugins/current-plugin-metadata-snapshot.ts +++ b/src/plugins/current-plugin-metadata-snapshot.ts @@ -1,6 +1,6 @@ import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resolveInstalledPluginIndexPolicyHash } from "./installed-plugin-index-policy.js"; -import type { PluginMetadataSnapshot } from "./plugin-metadata-snapshot.js"; +import type { PluginMetadataSnapshot } from "./plugin-metadata-snapshot.types.js"; let currentPluginMetadataSnapshot: PluginMetadataSnapshot | undefined; let currentPluginMetadataSnapshotConfigFingerprint: string | undefined; diff --git a/src/plugins/plugin-metadata-snapshot.ts b/src/plugins/plugin-metadata-snapshot.ts index 7733ff1f651..b598ed2dff1 100644 --- a/src/plugins/plugin-metadata-snapshot.ts +++ b/src/plugins/plugin-metadata-snapshot.ts @@ -1,62 +1,29 @@ import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resolveInstalledPluginIndexPolicyHash } from "./installed-plugin-index-policy.js"; +import type { InstalledPluginIndex } from "./installed-plugin-index.js"; import { loadPluginManifestRegistryForInstalledIndex, resolveInstalledManifestRegistryIndexFingerprint, } from "./manifest-registry-installed.js"; -import type { PluginManifestRecord, PluginManifestRegistry } from "./manifest-registry.js"; -import type { PluginDiagnostic } from "./manifest-types.js"; -import { createPluginRegistryIdNormalizer } from "./plugin-registry-contributions.js"; -import { - loadPluginRegistrySnapshotWithMetadata, - type PluginRegistrySnapshot, - type PluginRegistrySnapshotDiagnostic, -} from "./plugin-registry-snapshot.js"; - -export type PluginMetadataSnapshotOwnerMaps = { - channels: ReadonlyMap; - channelConfigs: ReadonlyMap; - providers: ReadonlyMap; - modelCatalogProviders: ReadonlyMap; - cliBackends: ReadonlyMap; - setupProviders: ReadonlyMap; - commandAliases: ReadonlyMap; - contracts: ReadonlyMap; -}; - -export type PluginMetadataSnapshotMetrics = { - registrySnapshotMs: number; - manifestRegistryMs: number; - ownerMapsMs: number; - totalMs: number; - indexPluginCount: number; - manifestPluginCount: number; -}; - -export type PluginMetadataSnapshot = { - policyHash: string; - workspaceDir?: string; - index: PluginRegistrySnapshot; - registryDiagnostics: readonly PluginRegistrySnapshotDiagnostic[]; - manifestRegistry: PluginManifestRegistry; - plugins: readonly PluginManifestRecord[]; - diagnostics: readonly PluginDiagnostic[]; - byPluginId: ReadonlyMap; - normalizePluginId: (pluginId: string) => string; - owners: PluginMetadataSnapshotOwnerMaps; - metrics: PluginMetadataSnapshotMetrics; -}; - -export type LoadPluginMetadataSnapshotParams = { - config: OpenClawConfig; - workspaceDir?: string; - env: NodeJS.ProcessEnv; - index?: PluginRegistrySnapshot; -}; +import type { PluginManifestRecord } from "./manifest-registry.js"; +import type { + LoadPluginMetadataSnapshotParams, + PluginMetadataSnapshot, + PluginMetadataSnapshotOwnerMaps, +} from "./plugin-metadata-snapshot.types.js"; +import { createPluginRegistryIdNormalizer } from "./plugin-registry-id-normalizer.js"; +import { loadPluginRegistrySnapshotWithMetadata } from "./plugin-registry-snapshot.js"; +export type { + LoadPluginMetadataSnapshotParams, + PluginMetadataSnapshot, + PluginMetadataSnapshotMetrics, + PluginMetadataSnapshotOwnerMaps, + PluginMetadataSnapshotRegistryDiagnostic, +} from "./plugin-metadata-snapshot.types.js"; function indexesMatch( - left: PluginRegistrySnapshot | undefined, - right: PluginRegistrySnapshot | undefined, + left: InstalledPluginIndex | undefined, + right: InstalledPluginIndex | undefined, ): boolean { if (!left || !right) { return true; @@ -71,7 +38,7 @@ export function isPluginMetadataSnapshotCompatible(params: { snapshot: Pick; config: OpenClawConfig; workspaceDir?: string; - index?: PluginRegistrySnapshot; + index?: InstalledPluginIndex; }): boolean { return ( params.snapshot.policyHash === resolveInstalledPluginIndexPolicyHash(params.config) && diff --git a/src/plugins/plugin-metadata-snapshot.types.ts b/src/plugins/plugin-metadata-snapshot.types.ts new file mode 100644 index 00000000000..223dcff7ec5 --- /dev/null +++ b/src/plugins/plugin-metadata-snapshot.types.ts @@ -0,0 +1,55 @@ +import type { OpenClawConfig } from "../config/types.openclaw.js"; +import type { InstalledPluginIndex } from "./installed-plugin-index.js"; +import type { PluginManifestRecord, PluginManifestRegistry } from "./manifest-registry.js"; +import type { PluginDiagnostic } from "./manifest-types.js"; + +export type PluginMetadataSnapshotOwnerMaps = { + channels: ReadonlyMap; + channelConfigs: ReadonlyMap; + providers: ReadonlyMap; + modelCatalogProviders: ReadonlyMap; + cliBackends: ReadonlyMap; + setupProviders: ReadonlyMap; + commandAliases: ReadonlyMap; + contracts: ReadonlyMap; +}; + +export type PluginMetadataSnapshotMetrics = { + registrySnapshotMs: number; + manifestRegistryMs: number; + ownerMapsMs: number; + totalMs: number; + indexPluginCount: number; + manifestPluginCount: number; +}; + +export type PluginMetadataSnapshotRegistryDiagnostic = { + level: "info" | "warn"; + code: + | "persisted-registry-disabled" + | "persisted-registry-missing" + | "persisted-registry-stale-policy" + | "persisted-registry-stale-source"; + message: string; +}; + +export type PluginMetadataSnapshot = { + policyHash: string; + workspaceDir?: string; + index: InstalledPluginIndex; + registryDiagnostics: readonly PluginMetadataSnapshotRegistryDiagnostic[]; + manifestRegistry: PluginManifestRegistry; + plugins: readonly PluginManifestRecord[]; + diagnostics: readonly PluginDiagnostic[]; + byPluginId: ReadonlyMap; + normalizePluginId: (pluginId: string) => string; + owners: PluginMetadataSnapshotOwnerMaps; + metrics: PluginMetadataSnapshotMetrics; +}; + +export type LoadPluginMetadataSnapshotParams = { + config: OpenClawConfig; + workspaceDir?: string; + env: NodeJS.ProcessEnv; + index?: InstalledPluginIndex; +}; diff --git a/src/plugins/plugin-registry-contributions.ts b/src/plugins/plugin-registry-contributions.ts index f0aa16c95fb..342029f73c2 100644 --- a/src/plugins/plugin-registry-contributions.ts +++ b/src/plugins/plugin-registry-contributions.ts @@ -13,11 +13,19 @@ import type { PluginManifestRegistry, } from "./manifest-registry.js"; import type { PluginOrigin } from "./plugin-origin.types.js"; +import { + createPluginRegistryIdNormalizer, + type PluginRegistryIdNormalizerOptions, +} from "./plugin-registry-id-normalizer.js"; import { loadPluginRegistrySnapshot, type LoadPluginRegistryParams, type PluginRegistrySnapshot, } from "./plugin-registry-snapshot.js"; +export { + createPluginRegistryIdNormalizer, + type PluginRegistryIdNormalizerOptions, +} from "./plugin-registry-id-normalizer.js"; export type PluginLookUpTable = { index: PluginRegistrySnapshot; @@ -101,23 +109,10 @@ export type ResolveManifestContractPluginIdsByCompatibilityRuntimePathParams = origin?: PluginOrigin; }; -export type PluginRegistryIdNormalizerOptions = { - manifestRegistry?: PluginManifestRegistry; - lookUpTable?: Pick; -}; - function normalizeContributionId(value: string): string { return value.trim(); } -function normalizePluginRegistryAlias(value: string): string { - return value.trim(); -} - -function normalizePluginRegistryAliasKey(value: string): string { - return normalizePluginRegistryAlias(value).toLowerCase(); -} - function sortUnique(values: Iterable): string[] { return [...new Set([...values].map((value) => value.trim()).filter(Boolean))].toSorted( (left, right) => left.localeCompare(right), @@ -298,54 +293,6 @@ export function loadPluginManifestRegistryForPluginRegistry( }); } -export function createPluginRegistryIdNormalizer( - index: PluginRegistrySnapshot, - options: PluginRegistryIdNormalizerOptions = {}, -): (pluginId: string) => string { - const aliases = new Map(); - for (const plugin of index.plugins) { - const pluginId = normalizePluginRegistryAlias(plugin.pluginId); - if (pluginId) { - aliases.set(normalizePluginRegistryAliasKey(pluginId), plugin.pluginId); - } - } - const registry = - options.lookUpTable?.manifestRegistry ?? - options.manifestRegistry ?? - loadPluginManifestRegistryForInstalledIndex({ - index, - includeDisabled: true, - }); - for (const plugin of [...registry.plugins].toSorted((left, right) => - left.id.localeCompare(right.id), - )) { - const pluginId = normalizePluginRegistryAlias(plugin.id); - if (!pluginId) { - continue; - } - aliases.set(normalizePluginRegistryAliasKey(pluginId), plugin.id); - for (const alias of [ - plugin.id, - ...listManifestContributionIds(plugin, "providers"), - ...listManifestContributionIds(plugin, "channels"), - ...listManifestContributionIds(plugin, "setupProviders"), - ...listManifestContributionIds(plugin, "cliBackends"), - ...listManifestContributionIds(plugin, "modelCatalogProviders"), - ...(plugin.legacyPluginIds ?? []), - ]) { - const normalizedAlias = normalizePluginRegistryAlias(alias); - const normalizedAliasKey = normalizePluginRegistryAliasKey(alias); - if (normalizedAlias && !aliases.has(normalizedAliasKey)) { - aliases.set(normalizedAliasKey, pluginId); - } - } - } - return (pluginId: string) => { - const trimmed = normalizePluginRegistryAlias(pluginId); - return aliases.get(normalizePluginRegistryAliasKey(trimmed)) ?? trimmed; - }; -} - export function normalizePluginsConfigWithRegistry( config: OpenClawConfig["plugins"] | undefined, index: PluginRegistrySnapshot, diff --git a/src/plugins/plugin-registry-id-normalizer.ts b/src/plugins/plugin-registry-id-normalizer.ts new file mode 100644 index 00000000000..15fb5fd3e9e --- /dev/null +++ b/src/plugins/plugin-registry-id-normalizer.ts @@ -0,0 +1,74 @@ +import type { InstalledPluginIndex } from "./installed-plugin-index.js"; +import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js"; +import type { PluginManifestRecord, PluginManifestRegistry } from "./manifest-registry.js"; + +export type PluginRegistryIdNormalizerOptions = { + manifestRegistry?: PluginManifestRegistry; + lookUpTable?: Pick<{ manifestRegistry: PluginManifestRegistry }, "manifestRegistry">; +}; + +function normalizePluginRegistryAlias(value: string): string { + return value.trim(); +} + +function normalizePluginRegistryAliasKey(value: string): string { + return normalizePluginRegistryAlias(value).toLowerCase(); +} + +function collectObjectKeys(value: Record | undefined): readonly string[] { + return value ? Object.keys(value) : []; +} + +function listPluginRegistryNormalizerAliases(plugin: PluginManifestRecord): readonly string[] { + return [ + plugin.id, + ...plugin.providers, + ...plugin.channels, + ...(plugin.setup?.providers?.map((provider) => provider.id) ?? []), + ...plugin.cliBackends, + ...(plugin.setup?.cliBackends ?? []), + ...collectObjectKeys(plugin.modelCatalog?.providers), + ...collectObjectKeys(plugin.modelCatalog?.aliases), + ...(plugin.legacyPluginIds ?? []), + ]; +} + +export function createPluginRegistryIdNormalizer( + index: InstalledPluginIndex, + options: PluginRegistryIdNormalizerOptions = {}, +): (pluginId: string) => string { + const aliases = new Map(); + for (const plugin of index.plugins) { + const pluginId = normalizePluginRegistryAlias(plugin.pluginId); + if (pluginId) { + aliases.set(normalizePluginRegistryAliasKey(pluginId), plugin.pluginId); + } + } + const registry = + options.lookUpTable?.manifestRegistry ?? + options.manifestRegistry ?? + loadPluginManifestRegistryForInstalledIndex({ + index, + includeDisabled: true, + }); + for (const plugin of [...registry.plugins].toSorted((left, right) => + left.id.localeCompare(right.id), + )) { + const pluginId = normalizePluginRegistryAlias(plugin.id); + if (!pluginId) { + continue; + } + aliases.set(normalizePluginRegistryAliasKey(pluginId), plugin.id); + for (const alias of listPluginRegistryNormalizerAliases(plugin)) { + const normalizedAlias = normalizePluginRegistryAlias(alias); + const normalizedAliasKey = normalizePluginRegistryAliasKey(alias); + if (normalizedAlias && !aliases.has(normalizedAliasKey)) { + aliases.set(normalizedAliasKey, pluginId); + } + } + } + return (pluginId: string) => { + const trimmed = normalizePluginRegistryAlias(pluginId); + return aliases.get(normalizePluginRegistryAliasKey(trimmed)) ?? trimmed; + }; +}