| |
| import { normalizeSortedUniqueStringEntries } from "@openclaw/normalization-core/string-normalization"; |
| import { resolvePluginLoadCacheContext } from "./loader-load-context.js"; |
| import type { PluginLoadOptions } from "./loader-types.js"; |
| import type { PluginManifestRecord } from "./manifest-registry.js"; |
| import { matchesPluginRuntimeArtifactSelection } from "./plugin-runtime-artifact-binding.js"; |
| import type { PluginRecord, PluginRegistry } from "./registry-types.js"; |
| import { |
| getActivePluginRegistry, |
| getActivePluginRegistryKey, |
| getActivePluginRegistryWorkspaceDir, |
| } from "./runtime.js"; |
| import { getPluginRuntimeLoadContextState } from "./runtime/load-context-state.js"; |
|
|
| export function getActiveRuntimePluginRegistry(): PluginRegistry | null { |
| return getActivePluginRegistry(); |
| } |
|
|
| |
| export function resolveCompatibleRuntimePluginRegistry( |
| options?: PluginLoadOptions, |
| ): PluginRegistry | undefined { |
| const activeRegistry = getActivePluginRegistry() ?? undefined; |
| if (!activeRegistry || options === undefined) { |
| return activeRegistry; |
| } |
| const activeCacheKey = getActivePluginRegistryKey(); |
| if (!activeCacheKey) { |
| return undefined; |
| } |
| const requestedKey = resolvePluginLoadCacheContext(options).cacheKey; |
| if (requestedKey === activeCacheKey) { |
| return activeRegistry; |
| } |
| const identity = getPluginRuntimeLoadContextState(activeRegistry)?.loaderCacheIdentity; |
| return identity && |
| ((identity.requestKey === activeCacheKey && identity.resolvedKey === requestedKey) || |
| (identity.resolvedKey === activeCacheKey && identity.requestKey === requestedKey)) |
| ? activeRegistry |
| : undefined; |
| } |
|
|
| function isRuntimePluginRecordLoaded(plugin: PluginRecord): boolean { |
| return plugin.status === "loaded" && (plugin.format === "bundle" || plugin.imported !== false); |
| } |
|
|
| |
| export function listRuntimePluginIdsFromRegistry(registry: PluginRegistry): string[] { |
| return normalizeSortedUniqueStringEntries( |
| registry.plugins.filter(isRuntimePluginRecordLoaded).map((plugin) => plugin.id), |
| ); |
| } |
|
|
| export function listLoadedRuntimePluginIds(): string[] { |
| const registry = getActivePluginRegistry(); |
| return registry ? listRuntimePluginIdsFromRegistry(registry) : []; |
| } |
|
|
| function normalizeRequiredPluginIds(ids?: readonly string[]): string[] | undefined { |
| if (ids === undefined) { |
| return undefined; |
| } |
| return normalizeSortedUniqueStringEntries(ids); |
| } |
|
|
| export function registryContainsRuntimePluginIds( |
| registry: PluginRegistry, |
| pluginIds: readonly string[] | undefined, |
| ): boolean { |
| if (pluginIds === undefined) { |
| return true; |
| } |
| if (pluginIds.length === 0 && registry.plugins.length > 0) { |
| return false; |
| } |
| const missing = new Set(pluginIds); |
| for (const plugin of registry.plugins) { |
| if (plugin.status === undefined || isRuntimePluginRecordLoaded(plugin)) { |
| missing.delete(plugin.id); |
| } |
| } |
| if (pluginIds.length > 0 && missing.size === 0) { |
| return true; |
| } |
| |
| |
| if (registry.plugins.some((plugin) => missing.has(plugin.id))) { |
| return false; |
| } |
| for (const [key, value] of Object.entries(registry)) { |
| if (key === "diagnostics" || key === "channelSetups" || !Array.isArray(value)) { |
| continue; |
| } |
| for (const entry of value) { |
| if (entry && typeof entry === "object" && "pluginId" in entry) { |
| const pluginId = entry.pluginId; |
| if (typeof pluginId === "string" && pluginId.length > 0) { |
| if (pluginIds.length === 0) { |
| return false; |
| } |
| missing.delete(pluginId); |
| if (missing.size === 0) { |
| return true; |
| } |
| } |
| } |
| } |
| } |
| return pluginIds.length === 0; |
| } |
|
|
| |
| export function createRuntimePluginManifestLookup( |
| registry: PluginRegistry, |
| manifestPlugins: readonly PluginManifestRecord[], |
| preferBuiltPluginArtifacts?: boolean, |
| ): (pluginId: string) => PluginRecord | undefined { |
| |
| const records = new Map( |
| registry.plugins.filter(isRuntimePluginRecordLoaded).map((plugin) => [plugin.id, plugin]), |
| ); |
| const manifests = new Map(manifestPlugins.toReversed().map((plugin) => [plugin.id, plugin])); |
| return (pluginId) => { |
| const record = records.get(pluginId); |
| const manifest = manifests.get(pluginId); |
| return record && |
| manifest && |
| record.origin === manifest.origin && |
| matchesPluginRuntimeArtifactSelection(record, manifest, preferBuiltPluginArtifacts) |
| ? record |
| : undefined; |
| }; |
| } |
|
|
| export function getLoadedRuntimePluginRegistry( |
| params: { |
| env?: NodeJS.ProcessEnv; |
| loadOptions?: PluginLoadOptions; |
| workspaceDir?: string; |
| requiredPluginIds?: readonly string[]; |
| } = {}, |
| ): PluginRegistry | undefined { |
| const requiredPluginIds = normalizeRequiredPluginIds( |
| params.requiredPluginIds ?? params.loadOptions?.onlyPluginIds, |
| ); |
| if (params.loadOptions && requiredPluginIds === undefined) { |
| |
| |
| return resolveCompatibleRuntimePluginRegistry(params.loadOptions); |
| } |
|
|
| const activeWorkspaceDir = getActivePluginRegistryWorkspaceDir(); |
| const requestedWorkspaceDir = params.workspaceDir ?? params.loadOptions?.workspaceDir; |
| if ( |
| (Object.hasOwn(params, "workspaceDir") || |
| params.loadOptions || |
| requestedWorkspaceDir !== undefined) && |
| activeWorkspaceDir !== requestedWorkspaceDir |
| ) { |
| return undefined; |
| } |
| const registry = getActivePluginRegistry(); |
| if (!registry) { |
| return undefined; |
| } |
| if ( |
| !registryContainsRuntimePluginIds(registry, requiredPluginIds) || |
| (params.loadOptions?.manifestRegistry && |
| requiredPluginIds !== undefined && |
| !requiredPluginIds.every( |
| createRuntimePluginManifestLookup( |
| registry, |
| params.loadOptions.manifestRegistry.plugins, |
| params.loadOptions.preferBuiltPluginArtifacts, |
| ), |
| )) |
| ) { |
| return undefined; |
| } |
| |
| |
| if (params.loadOptions?.discovery && !params.loadOptions.manifestRegistry) { |
| return resolveCompatibleRuntimePluginRegistry(params.loadOptions); |
| } |
| return registry; |
| } |
|
|