diff --git a/src/agents/provider-auth-aliases.test.ts b/src/agents/provider-auth-aliases.test.ts index c71837b80b6..a665b315428 100644 --- a/src/agents/provider-auth-aliases.test.ts +++ b/src/agents/provider-auth-aliases.test.ts @@ -6,6 +6,20 @@ const pluginRegistryMocks = vi.hoisted(() => { loadPluginManifestRegistryForInstalledIndex: loadManifestRegistry, loadPluginManifestRegistryForPluginRegistry: loadManifestRegistry, loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })), + loadPluginMetadataSnapshot: vi.fn((params: unknown) => { + const registry = loadManifestRegistry(params) ?? { plugins: [], diagnostics: [] }; + return { + index: { + plugins: registry.plugins.map((plugin: { id: string; origin?: string }) => ({ + pluginId: plugin.id, + origin: plugin.origin ?? "global", + enabled: true, + enabledByDefault: true, + })), + }, + plugins: registry.plugins, + }; + }), }; }); @@ -20,6 +34,10 @@ vi.mock("../plugins/plugin-registry.js", () => ({ loadPluginRegistrySnapshot: pluginRegistryMocks.loadPluginRegistrySnapshot, })); +vi.mock("../plugins/plugin-metadata-snapshot.js", () => ({ + loadPluginMetadataSnapshot: pluginRegistryMocks.loadPluginMetadataSnapshot, +})); + import { resetProviderAuthAliasMapCacheForTest, resolveProviderIdForAuth, @@ -32,6 +50,7 @@ describe("provider auth aliases", () => { pluginRegistryMocks.loadPluginManifestRegistryForPluginRegistry.mockReset(); pluginRegistryMocks.loadPluginRegistrySnapshot.mockReset(); pluginRegistryMocks.loadPluginRegistrySnapshot.mockReturnValue({ plugins: [] }); + pluginRegistryMocks.loadPluginMetadataSnapshot.mockClear(); }); it("treats deprecated auth choice ids as provider auth aliases", () => { diff --git a/src/agents/provider-auth-aliases.ts b/src/agents/provider-auth-aliases.ts index 9603289946c..1d673994986 100644 --- a/src/agents/provider-auth-aliases.ts +++ b/src/agents/provider-auth-aliases.ts @@ -5,8 +5,8 @@ import { normalizePluginConfigId, } from "../plugins/plugin-config-trust.js"; import { resolvePluginControlPlaneFingerprint } from "../plugins/plugin-control-plane-context.js"; +import { loadPluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js"; import type { PluginOrigin } from "../plugins/plugin-origin.types.js"; -import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js"; import { normalizeProviderId } from "./provider-id.js"; export type ProviderAuthAliasLookupParams = { @@ -118,15 +118,14 @@ export function resolveProviderAuthAliasMap( if (cached) { return cached; } - const registry = loadPluginManifestRegistryForPluginRegistry({ - config: params?.config, + const snapshot = loadPluginMetadataSnapshot({ + config: params?.config ?? {}, workspaceDir: params?.workspaceDir, env, - includeDisabled: true, }); const preferredAliases = new Map(); const aliases: Record = Object.create(null) as Record; - for (const plugin of registry.plugins) { + for (const plugin of snapshot.plugins) { if (!shouldUsePluginAuthAliases(plugin, params)) { continue; } diff --git a/src/channels/plugins/read-only-command-defaults.test.ts b/src/channels/plugins/read-only-command-defaults.test.ts new file mode 100644 index 00000000000..0ad3fec1dfe --- /dev/null +++ b/src/channels/plugins/read-only-command-defaults.test.ts @@ -0,0 +1,68 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const loadPluginMetadataSnapshot = vi.hoisted(() => vi.fn()); + +vi.mock("../../plugins/plugin-metadata-snapshot.js", () => ({ + loadPluginMetadataSnapshot, +})); + +import { resolveReadOnlyChannelCommandDefaults } from "./read-only-command-defaults.js"; + +describe("resolveReadOnlyChannelCommandDefaults", () => { + beforeEach(() => { + loadPluginMetadataSnapshot.mockReset(); + loadPluginMetadataSnapshot.mockReturnValue({ + index: { plugins: [] }, + plugins: [], + }); + }); + + it("resolves command defaults from the shared metadata snapshot", () => { + const env = { HOME: "/home/demo" } as NodeJS.ProcessEnv; + loadPluginMetadataSnapshot.mockReturnValue({ + index: { + plugins: [ + { + pluginId: "demo", + origin: "global", + enabled: true, + enabledByDefault: true, + }, + ], + }, + plugins: [ + { + id: "demo", + origin: "global", + channels: ["demo"], + channelConfigs: { + demo: { + commands: { + nativeCommandsAutoEnabled: true, + nativeSkillsAutoEnabled: false, + }, + }, + }, + }, + ], + }); + + expect( + resolveReadOnlyChannelCommandDefaults("demo", { + config: {}, + env, + stateDir: "/state", + workspaceDir: "/workspace", + }), + ).toEqual({ + nativeCommandsAutoEnabled: true, + nativeSkillsAutoEnabled: false, + }); + expect(loadPluginMetadataSnapshot).toHaveBeenCalledWith({ + config: {}, + env, + stateDir: "/state", + workspaceDir: "/workspace", + }); + }); +}); diff --git a/src/channels/plugins/read-only-command-defaults.ts b/src/channels/plugins/read-only-command-defaults.ts index 470a95519d4..393f2292db9 100644 --- a/src/channels/plugins/read-only-command-defaults.ts +++ b/src/channels/plugins/read-only-command-defaults.ts @@ -1,9 +1,8 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { isBlockedObjectKey } from "../../infra/prototype-keys.js"; import { isInstalledPluginEnabled } from "../../plugins/installed-plugin-index.js"; -import { loadPluginManifestRegistryForInstalledIndex } from "../../plugins/manifest-registry-installed.js"; import type { PluginManifestRecord } from "../../plugins/manifest-registry.js"; -import { loadPluginRegistrySnapshot } from "../../plugins/plugin-registry.js"; +import { loadPluginMetadataSnapshot } from "../../plugins/plugin-metadata-snapshot.js"; import { normalizeOptionalString } from "../../shared/string-coerce.js"; import type { ChannelPlugin } from "./types.plugin.js"; @@ -65,20 +64,13 @@ export function resolveReadOnlyChannelCommandDefaults( if (!normalizedChannelId || !isSafeManifestChannelId(normalizedChannelId)) { return undefined; } - const index = loadPluginRegistrySnapshot({ + const snapshot = loadPluginMetadataSnapshot({ config: options.config, stateDir: options.stateDir, workspaceDir: options.workspaceDir, env: options.env ?? process.env, }); - const registry = loadPluginManifestRegistryForInstalledIndex({ - index, - config: options.config, - workspaceDir: options.workspaceDir, - env: options.env ?? process.env, - includeDisabled: true, - }); - for (const record of registry.plugins) { + for (const record of snapshot.plugins) { if (!record.channels.includes(normalizedChannelId)) { continue; } @@ -88,7 +80,7 @@ export function resolveReadOnlyChannelCommandDefaults( ) { continue; } - if (!isInstalledPluginEnabled(index, record.id, options.config)) { + if (!isInstalledPluginEnabled(snapshot.index, record.id, options.config)) { continue; } const channelConfigValue = record.channelConfigs diff --git a/src/plugins/manifest-contract-runtime.test.ts b/src/plugins/manifest-contract-runtime.test.ts new file mode 100644 index 00000000000..3448262f962 --- /dev/null +++ b/src/plugins/manifest-contract-runtime.test.ts @@ -0,0 +1,68 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const loadPluginMetadataSnapshot = vi.hoisted(() => vi.fn()); + +vi.mock("./plugin-metadata-snapshot.js", () => ({ + loadPluginMetadataSnapshot, +})); + +import { resolveManifestContractRuntimePluginResolution } from "./manifest-contract-runtime.js"; + +describe("resolveManifestContractRuntimePluginResolution", () => { + beforeEach(() => { + loadPluginMetadataSnapshot.mockReset(); + loadPluginMetadataSnapshot.mockReturnValue({ + index: { plugins: [] }, + plugins: [], + }); + }); + + it("resolves contract plugins from the shared metadata snapshot", () => { + loadPluginMetadataSnapshot.mockReturnValue({ + index: { + plugins: [ + { + pluginId: "bundled-search", + origin: "bundled", + enabled: true, + enabledByDefault: true, + }, + { + pluginId: "external-search", + origin: "global", + enabled: true, + enabledByDefault: true, + }, + ], + }, + plugins: [ + { + id: "bundled-search", + origin: "bundled", + contracts: { webSearchProviders: ["search"] }, + }, + { + id: "external-search", + origin: "global", + contracts: { webSearchProviders: ["search"] }, + }, + ], + }); + + expect( + resolveManifestContractRuntimePluginResolution({ + cfg: {}, + contract: "webSearchProviders", + value: "search", + }), + ).toEqual({ + pluginIds: ["bundled-search", "external-search"], + bundledCompatPluginIds: ["bundled-search"], + }); + expect(loadPluginMetadataSnapshot).toHaveBeenCalledWith({ + config: {}, + env: process.env, + preferPersisted: false, + }); + }); +}); diff --git a/src/plugins/manifest-contract-runtime.ts b/src/plugins/manifest-contract-runtime.ts index f6a3df013e5..c7216266967 100644 --- a/src/plugins/manifest-contract-runtime.ts +++ b/src/plugins/manifest-contract-runtime.ts @@ -3,9 +3,8 @@ import { hasManifestContractValue, listAvailableManifestContractPlugins, } from "./manifest-contract-eligibility.js"; -import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js"; import type { PluginManifestContractListKey } from "./manifest-registry.js"; -import { loadPluginRegistrySnapshot } from "./plugin-registry.js"; +import { loadPluginMetadataSnapshot } from "./plugin-metadata-snapshot.js"; export type ManifestContractRuntimePluginResolution = { pluginIds: string[]; @@ -21,17 +20,12 @@ export function resolveManifestContractRuntimePluginResolution(params: { contract: PluginManifestContractListKey; value?: string; }): ManifestContractRuntimePluginResolution { - const index = loadPluginRegistrySnapshot({ - config: params.cfg, + const snapshot = loadPluginMetadataSnapshot({ + config: params.cfg ?? {}, env: process.env, ...DEMAND_ONLY_CONTRACT_LOOKUP_OPTIONS, }); - const allContractPlugins = loadPluginManifestRegistryForInstalledIndex({ - index, - config: params.cfg, - env: process.env, - includeDisabled: true, - }).plugins.filter((plugin) => + const allContractPlugins = snapshot.plugins.filter((plugin) => hasManifestContractValue({ plugin, contract: params.contract, @@ -42,7 +36,7 @@ export function resolveManifestContractRuntimePluginResolution(params: { .filter((plugin) => plugin.origin === "bundled") .map((plugin) => plugin.id); const pluginIds = listAvailableManifestContractPlugins({ - snapshot: { index, plugins: allContractPlugins }, + snapshot: { index: snapshot.index, plugins: allContractPlugins }, contract: params.contract, value: params.value, config: params.cfg, diff --git a/src/plugins/plugin-metadata-snapshot.ts b/src/plugins/plugin-metadata-snapshot.ts index 62e9bb97e8d..098710db6c2 100644 --- a/src/plugins/plugin-metadata-snapshot.ts +++ b/src/plugins/plugin-metadata-snapshot.ts @@ -180,7 +180,9 @@ function loadPluginMetadataSnapshotImpl( const registryResult = loadPluginRegistrySnapshotWithMetadata({ config: params.config, workspaceDir: params.workspaceDir, + ...(params.stateDir ? { stateDir: params.stateDir } : {}), env: params.env, + ...(params.preferPersisted !== undefined ? { preferPersisted: params.preferPersisted } : {}), ...(params.index ? { index: params.index } : {}), }); const registrySnapshotMs = performance.now() - registryStartedAt; diff --git a/src/plugins/plugin-metadata-snapshot.types.ts b/src/plugins/plugin-metadata-snapshot.types.ts index 90335a01961..062b6778a48 100644 --- a/src/plugins/plugin-metadata-snapshot.types.ts +++ b/src/plugins/plugin-metadata-snapshot.types.ts @@ -55,6 +55,8 @@ export type PluginMetadataManifestView = Pick vi.fn()); const loadPluginManifestRegistryForInstalledIndexMock = vi.hoisted(() => vi.fn()); +const loadPluginMetadataSnapshotMock = vi.hoisted(() => vi.fn()); vi.mock("./plugin-registry.js", async (importOriginal) => ({ ...(await importOriginal()), @@ -10,36 +11,39 @@ vi.mock("./plugin-registry.js", async (importOriginal) => ({ vi.mock("./manifest-registry-installed.js", () => ({ loadPluginManifestRegistryForInstalledIndex: loadPluginManifestRegistryForInstalledIndexMock, })); +vi.mock("./plugin-metadata-snapshot.js", () => ({ + loadPluginMetadataSnapshot: loadPluginMetadataSnapshotMock, +})); afterEach(() => { loadPluginRegistrySnapshotMock.mockReset(); loadPluginManifestRegistryForInstalledIndexMock.mockReset(); + loadPluginMetadataSnapshotMock.mockReset(); }); describe("setup-registry runtime fallback", () => { it("uses bundled registry cliBackends when the setup-registry runtime is unavailable", async () => { - loadPluginRegistrySnapshotMock.mockReturnValue({ - diagnostics: [], - plugins: [ - { - pluginId: "openai", - origin: "bundled", - enabled: true, - }, - { - pluginId: "disabled", - origin: "bundled", - enabled: false, - }, - { - pluginId: "local", - origin: "workspace", - enabled: true, - }, - ], - }); - loadPluginManifestRegistryForInstalledIndexMock.mockReturnValue({ - diagnostics: [], + loadPluginMetadataSnapshotMock.mockReturnValue({ + index: { + diagnostics: [], + plugins: [ + { + pluginId: "openai", + origin: "bundled", + enabled: true, + }, + { + pluginId: "disabled", + origin: "bundled", + enabled: false, + }, + { + pluginId: "local", + origin: "workspace", + enabled: true, + }, + ], + }, plugins: [ { id: "openai", @@ -60,25 +64,26 @@ describe("setup-registry runtime fallback", () => { }); expect(resolvePluginSetupCliBackendRuntime({ backend: "local-cli" })).toBeUndefined(); expect(resolvePluginSetupCliBackendRuntime({ backend: "disabled-cli" })).toBeUndefined(); - expect(loadPluginRegistrySnapshotMock).toHaveBeenCalledTimes(3); - expect(loadPluginRegistrySnapshotMock).toHaveBeenCalledWith({}); - expect(loadPluginManifestRegistryForInstalledIndexMock).toHaveBeenCalledWith({ - index: expect.objectContaining({ - plugins: expect.arrayContaining([expect.objectContaining({ pluginId: "openai" })]), - }), + expect(loadPluginMetadataSnapshotMock).toHaveBeenCalledTimes(3); + expect(loadPluginMetadataSnapshotMock).toHaveBeenCalledWith({ + config: {}, + env: process.env, }); }); it("preserves fail-closed setup lookup when the runtime module explicitly declines to resolve", async () => { - loadPluginRegistrySnapshotMock.mockReturnValue({ - diagnostics: [], - plugins: [ - { - pluginId: "openai", - origin: "bundled", - enabled: true, - }, - ], + loadPluginMetadataSnapshotMock.mockReturnValue({ + index: { + diagnostics: [], + plugins: [ + { + pluginId: "openai", + origin: "bundled", + enabled: true, + }, + ], + }, + plugins: [], }); const { __testing, resolvePluginSetupCliBackendRuntime } = @@ -89,6 +94,6 @@ describe("setup-registry runtime fallback", () => { }); expect(resolvePluginSetupCliBackendRuntime({ backend: "codex-cli" })).toBeUndefined(); - expect(loadPluginRegistrySnapshotMock).not.toHaveBeenCalled(); + expect(loadPluginMetadataSnapshotMock).not.toHaveBeenCalled(); }); }); diff --git a/src/plugins/setup-registry.runtime.ts b/src/plugins/setup-registry.runtime.ts index 4846755ea75..070d6542220 100644 --- a/src/plugins/setup-registry.runtime.ts +++ b/src/plugins/setup-registry.runtime.ts @@ -1,7 +1,7 @@ import { createRequire } from "node:module"; import { normalizeProviderId } from "../agents/provider-id.js"; -import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js"; -import { loadPluginRegistrySnapshot } from "./plugin-registry.js"; +import { isInstalledPluginEnabled } from "./installed-plugin-index.js"; +import { loadPluginMetadataSnapshot } from "./plugin-metadata-snapshot.js"; type SetupRegistryRuntimeModule = Pick< typeof import("./setup-registry.js"), @@ -30,11 +30,9 @@ export const __testing = { }; function resolveBundledSetupCliBackends(): SetupCliBackendRuntimeEntry[] { - const index = loadPluginRegistrySnapshot({}); - return loadPluginManifestRegistryForInstalledIndex({ - index, - }).plugins.flatMap((plugin) => { - if (plugin.origin !== "bundled") { + const snapshot = loadPluginMetadataSnapshot({ config: {}, env: process.env }); + return snapshot.plugins.flatMap((plugin) => { + if (plugin.origin !== "bundled" || !isInstalledPluginEnabled(snapshot.index, plugin.id)) { return []; } return [...plugin.cliBackends, ...(plugin.setup?.cliBackends ?? [])].map( diff --git a/src/plugins/status.test.ts b/src/plugins/status.test.ts index 4258b0bbec1..86403f2bd80 100644 --- a/src/plugins/status.test.ts +++ b/src/plugins/status.test.ts @@ -15,6 +15,21 @@ const loadPluginMetadataRegistrySnapshotMock = vi.fn(); const loadPluginManifestRegistryForPluginRegistryMock = vi.fn(); const loadPluginRegistrySnapshotWithMetadataMock = vi.fn(); const loadPluginManifestRegistryForInstalledIndexMock = vi.fn(); +const loadPluginMetadataSnapshotMock = vi.fn((rawParams: unknown = {}) => { + const params = rawParams as { index?: unknown }; + const manifestRegistry = loadPluginManifestRegistryForInstalledIndexMock(params) ?? { + plugins: [], + diagnostics: [], + }; + return { + index: params.index ?? createInstalledPluginIndexSnapshot([]), + manifestRegistry, + plugins: manifestRegistry.plugins, + byPluginId: new Map( + manifestRegistry.plugins.map((plugin: { id: string }) => [plugin.id, plugin]), + ), + }; +}); const applyPluginAutoEnableMock = vi.fn(); const resolveBundledProviderCompatPluginIdsMock = vi.fn(); const withBundledPluginAllowlistCompatMock = vi.fn(); @@ -61,6 +76,10 @@ vi.mock("./manifest-registry-installed.js", () => ({ loadPluginManifestRegistryForInstalledIndexMock(...args), })); +vi.mock("./plugin-metadata-snapshot.js", () => ({ + loadPluginMetadataSnapshot: (...args: unknown[]) => loadPluginMetadataSnapshotMock(...args), +})); + vi.mock("./providers.js", () => ({ resolveBundledProviderCompatPluginIds: (...args: unknown[]) => resolveBundledProviderCompatPluginIdsMock(...args), @@ -370,6 +389,7 @@ describe("plugin status reports", () => { loadPluginManifestRegistryForPluginRegistryMock.mockReset(); loadPluginRegistrySnapshotWithMetadataMock.mockReset(); loadPluginManifestRegistryForInstalledIndexMock.mockReset(); + loadPluginMetadataSnapshotMock.mockClear(); applyPluginAutoEnableMock.mockReset(); resolveBundledProviderCompatPluginIdsMock.mockReset(); withBundledPluginAllowlistCompatMock.mockReset(); diff --git a/src/plugins/status.ts b/src/plugins/status.ts index a1ed11778fa..1bc3762a5ec 100644 --- a/src/plugins/status.ts +++ b/src/plugins/status.ts @@ -20,12 +20,11 @@ import { type PluginInspectShape, } from "./inspect-shape.js"; import { loadOpenClawPlugins } from "./loader.js"; -import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js"; import type { PluginManifestRecord } from "./manifest-registry.js"; import type { PluginDiagnostic } from "./manifest-types.js"; import { tracePluginLifecyclePhase } from "./plugin-lifecycle-trace.js"; +import { loadPluginMetadataSnapshot } from "./plugin-metadata-snapshot.js"; import { - loadPluginManifestRegistryForPluginRegistry, loadPluginRegistrySnapshotWithMetadata, type PluginRegistrySnapshotDiagnostic, type PluginRegistrySnapshotSource, @@ -230,14 +229,13 @@ export function buildPluginRegistrySnapshotReport( }), { surface: "status" }, ); - const manifestRegistry = loadPluginManifestRegistryForInstalledIndex({ + const metadataSnapshot = loadPluginMetadataSnapshot({ index: result.snapshot, config, - env: params?.env, + env: params?.env ?? process.env, workspaceDir: params?.workspaceDir, - includeDisabled: true, }); - const manifestByPluginId = new Map(manifestRegistry.plugins.map((plugin) => [plugin.id, plugin])); + const manifestByPluginId = metadataSnapshot.byPluginId; return { workspaceDir: params?.workspaceDir, ...createEmptyPluginRegistry(), @@ -258,12 +256,11 @@ function buildPluginReport( const initialWorkspaceDir = params?.workspaceDir ?? resolveAgentWorkspaceDir(rawConfig, resolveDefaultAgentId(rawConfig), params?.env); - const manifestRegistry = !loadModules - ? loadPluginManifestRegistryForPluginRegistry({ + const metadataSnapshot = !loadModules + ? loadPluginMetadataSnapshot({ config: rawConfig, - env: params?.env, + env: params?.env ?? process.env, workspaceDir: initialWorkspaceDir, - includeDisabled: true, }) : undefined; const baseContext = resolvePluginRuntimeLoadContext({ @@ -271,7 +268,7 @@ function buildPluginReport( env: params?.env, logger: params?.logger, workspaceDir: initialWorkspaceDir, - manifestRegistry, + manifestRegistry: metadataSnapshot?.manifestRegistry, }); const workspaceDir = baseContext.workspaceDir ?? initialWorkspaceDir ?? resolveDefaultAgentWorkspaceDir(); @@ -294,7 +291,7 @@ function buildPluginReport( config, workspaceDir, env: params?.env, - manifestRegistry, + manifestRegistry: metadataSnapshot?.manifestRegistry, }); const effectiveConfig = withBundledPluginAllowlistCompat({ config, @@ -344,7 +341,7 @@ function buildPluginReport( logger: params?.logger, loadModules: false, onlyPluginIds, - manifestRegistry, + manifestRegistry: metadataSnapshot?.manifestRegistry, runtimeContext: context, }), { surface: "status", onlyPluginCount: onlyPluginIds?.length }, diff --git a/src/secrets/provider-env-vars.dynamic.test.ts b/src/secrets/provider-env-vars.dynamic.test.ts index dcc05677c8e..b6118d16338 100644 --- a/src/secrets/provider-env-vars.dynamic.test.ts +++ b/src/secrets/provider-env-vars.dynamic.test.ts @@ -44,6 +44,20 @@ const pluginRegistryMocks = vi.hoisted(() => { loadPluginManifestRegistryForInstalledIndex: loadManifestRegistry, loadPluginManifestRegistryForPluginRegistry: loadManifestRegistry, loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })), + loadPluginMetadataSnapshot: vi.fn((params: unknown) => { + const registry = loadManifestRegistry(params) ?? { plugins: [], diagnostics: [] }; + return { + index: { + plugins: registry.plugins.map((plugin) => ({ + pluginId: plugin.id, + origin: plugin.origin, + enabled: true, + enabledByDefault: true, + })), + }, + plugins: registry.plugins, + }; + }), }; }); @@ -58,6 +72,10 @@ vi.mock("../plugins/plugin-registry.js", () => ({ loadPluginRegistrySnapshot: pluginRegistryMocks.loadPluginRegistrySnapshot, })); +vi.mock("../plugins/plugin-metadata-snapshot.js", () => ({ + loadPluginMetadataSnapshot: pluginRegistryMocks.loadPluginMetadataSnapshot, +})); + describe("provider env vars dynamic manifest metadata", () => { beforeEach(() => { pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReset(); @@ -67,6 +85,7 @@ describe("provider env vars dynamic manifest metadata", () => { }); pluginRegistryMocks.loadPluginRegistrySnapshot.mockReset(); pluginRegistryMocks.loadPluginRegistrySnapshot.mockReturnValue({ plugins: [] }); + pluginRegistryMocks.loadPluginMetadataSnapshot.mockClear(); __testing.resetProviderEnvVarCachesForTests(); }); @@ -153,9 +172,9 @@ describe("provider env vars dynamic manifest metadata", () => { source: "external cloud credentials", }, ]); - expect( - pluginRegistryMocks.loadPluginManifestRegistryForPluginRegistry.mock.calls.at(-1)?.[0], - ).toMatchObject({ includeDisabled: false }); + expect(pluginRegistryMocks.loadPluginMetadataSnapshot.mock.calls.at(-1)?.[0]).toMatchObject({ + preferPersisted: false, + }); }); it("excludes untrusted workspace plugin auth evidence by default", async () => { diff --git a/src/secrets/provider-env-vars.ts b/src/secrets/provider-env-vars.ts index bb353def006..2e0d6b73138 100644 --- a/src/secrets/provider-env-vars.ts +++ b/src/secrets/provider-env-vars.ts @@ -1,11 +1,12 @@ import { resolveProviderAuthAliasMap } from "../agents/provider-auth-aliases.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; +import { isInstalledPluginEnabled } from "../plugins/installed-plugin-index.js"; import type { PluginManifestRecord } from "../plugins/manifest-registry.js"; import { isWorkspacePluginAllowedByConfig, normalizePluginConfigId, } from "../plugins/plugin-config-trust.js"; -import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js"; +import { loadPluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js"; import { hasKind } from "../plugins/slots.js"; const CORE_PROVIDER_AUTH_ENV_VAR_CANDIDATES = { @@ -117,15 +118,14 @@ function appendUniqueAuthEvidence( function resolveManifestProviderAuthEnvVarCandidates( params?: ProviderEnvVarLookupParams, ): Record { - const registry = loadPluginManifestRegistryForPluginRegistry({ - config: params?.config, + const snapshot = loadPluginMetadataSnapshot({ + config: params?.config ?? {}, workspaceDir: params?.workspaceDir, - env: params?.env, + env: params?.env ?? process.env, preferPersisted: false, - includeDisabled: true, }); const candidates: Record = {}; - for (const plugin of registry.plugins) { + for (const plugin of snapshot.plugins) { if (!shouldUsePluginProviderEnvVars(plugin, params)) { continue; } @@ -155,15 +155,17 @@ function resolveManifestProviderAuthEnvVarCandidates( function resolveManifestProviderAuthEvidence( params?: ProviderEnvVarLookupParams, ): Record { - const registry = loadPluginManifestRegistryForPluginRegistry({ - config: params?.config, + const snapshot = loadPluginMetadataSnapshot({ + config: params?.config ?? {}, workspaceDir: params?.workspaceDir, - env: params?.env, + env: params?.env ?? process.env, preferPersisted: false, - includeDisabled: false, }); const evidenceByProvider: Record = {}; - for (const plugin of registry.plugins) { + for (const plugin of snapshot.plugins) { + if (!isInstalledPluginEnabled(snapshot.index, plugin.id, params?.config)) { + continue; + } if (!shouldUsePluginProviderAuthEvidence(plugin, params)) { continue; }