diff --git a/src/agents/auth-profile-runtime-contract.test.ts b/src/agents/auth-profile-runtime-contract.test.ts index 4d1888e6630..62a5628a553 100644 --- a/src/agents/auth-profile-runtime-contract.test.ts +++ b/src/agents/auth-profile-runtime-contract.test.ts @@ -33,6 +33,22 @@ vi.mock("../plugins/manifest-registry.js", async (importOriginal) => { }; }); +vi.mock("../plugins/manifest-registry-installed.js", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + loadPluginManifestRegistryForInstalledIndex: loadPluginManifestRegistry, + }; +}); + +vi.mock("../plugins/plugin-registry.js", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + loadPluginRegistrySnapshot: () => ({ plugins: [] }), + }; +}); + vi.mock("./cli-runner.js", () => ({ runCliAgent: runCliAgentMock, })); diff --git a/src/agents/models-config.providers.auth-aliases.test.ts b/src/agents/models-config.providers.auth-aliases.test.ts index 8a4f23f7c5d..da6539d146e 100644 --- a/src/agents/models-config.providers.auth-aliases.test.ts +++ b/src/agents/models-config.providers.auth-aliases.test.ts @@ -61,6 +61,12 @@ vi.mock("../plugins/manifest-registry.js", () => ({ loadPluginManifestRegistry, resolveManifestContractOwnerPluginId, })); +vi.mock("../plugins/manifest-registry-installed.js", () => ({ + loadPluginManifestRegistryForInstalledIndex: loadPluginManifestRegistry, +})); +vi.mock("../plugins/plugin-registry.js", () => ({ + loadPluginRegistrySnapshot: () => ({ plugins: [] }), +})); vi.mock("../plugins/provider-runtime.js", () => ({ resolveProviderSyntheticAuthWithPlugin, })); diff --git a/src/agents/provider-auth-aliases.test.ts b/src/agents/provider-auth-aliases.test.ts index 6684632d511..0fbb2e7b06c 100644 --- a/src/agents/provider-auth-aliases.test.ts +++ b/src/agents/provider-auth-aliases.test.ts @@ -1,16 +1,24 @@ import { describe, expect, it, vi } from "vitest"; -const loadPluginManifestRegistry = vi.hoisted(() => vi.fn()); +const pluginRegistryMocks = vi.hoisted(() => ({ + loadPluginManifestRegistryForInstalledIndex: vi.fn(), + loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })), +})); -vi.mock("../plugins/manifest-registry.js", () => ({ - loadPluginManifestRegistry, +vi.mock("../plugins/manifest-registry-installed.js", () => ({ + loadPluginManifestRegistryForInstalledIndex: + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex, +})); + +vi.mock("../plugins/plugin-registry.js", () => ({ + loadPluginRegistrySnapshot: pluginRegistryMocks.loadPluginRegistrySnapshot, })); import { resolveProviderIdForAuth } from "./provider-auth-aliases.js"; describe("provider auth aliases", () => { it("treats deprecated auth choice ids as provider auth aliases", () => { - loadPluginManifestRegistry.mockReturnValue({ + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ plugins: [ { id: "openai", diff --git a/src/agents/provider-auth-aliases.ts b/src/agents/provider-auth-aliases.ts index 120940ed69e..bf791cedf1c 100644 --- a/src/agents/provider-auth-aliases.ts +++ b/src/agents/provider-auth-aliases.ts @@ -1,11 +1,12 @@ import type { OpenClawConfig } from "../config/types.openclaw.js"; -import { loadPluginManifestRegistry } from "../plugins/manifest-registry.js"; +import { loadPluginManifestRegistryForInstalledIndex } from "../plugins/manifest-registry-installed.js"; import type { PluginManifestRecord } from "../plugins/manifest-registry.js"; import { isWorkspacePluginAllowedByConfig, normalizePluginConfigId, } from "../plugins/plugin-config-trust.js"; import type { PluginOrigin } from "../plugins/plugin-origin.types.js"; +import { loadPluginRegistrySnapshot } from "../plugins/plugin-registry.js"; import { normalizeProviderId } from "./provider-id.js"; export type ProviderAuthAliasLookupParams = { @@ -83,11 +84,18 @@ function setPreferredAlias(params: { export function resolveProviderAuthAliasMap( params?: ProviderAuthAliasLookupParams, ): Record { - const registry = loadPluginManifestRegistry({ + const index = loadPluginRegistrySnapshot({ config: params?.config, workspaceDir: params?.workspaceDir, env: params?.env, }); + const registry = loadPluginManifestRegistryForInstalledIndex({ + index, + config: params?.config, + workspaceDir: params?.workspaceDir, + env: params?.env, + includeDisabled: true, + }); const preferredAliases = new Map(); const aliases: Record = Object.create(null) as Record; for (const plugin of registry.plugins) { diff --git a/src/secrets/channel-env-vars.dynamic.test.ts b/src/secrets/channel-env-vars.dynamic.test.ts index 4cf1855fd43..7ff4711d688 100644 --- a/src/secrets/channel-env-vars.dynamic.test.ts +++ b/src/secrets/channel-env-vars.dynamic.test.ts @@ -9,23 +9,37 @@ type MockManifestRegistry = { diagnostics: unknown[]; }; -const loadPluginManifestRegistry = vi.hoisted(() => - vi.fn<() => MockManifestRegistry>(() => ({ plugins: [], diagnostics: [] })), -); +const pluginRegistryMocks = vi.hoisted(() => ({ + loadPluginManifestRegistryForInstalledIndex: vi.fn<() => MockManifestRegistry>(() => ({ + plugins: [], + diagnostics: [], + })), + loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })), +})); -vi.mock("../plugins/manifest-registry.js", () => ({ - loadPluginManifestRegistry, +vi.mock("../plugins/manifest-registry-installed.js", () => ({ + loadPluginManifestRegistryForInstalledIndex: + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex, +})); + +vi.mock("../plugins/plugin-registry.js", () => ({ + loadPluginRegistrySnapshot: pluginRegistryMocks.loadPluginRegistrySnapshot, })); describe("channel env vars dynamic manifest metadata", () => { beforeEach(() => { vi.resetModules(); - loadPluginManifestRegistry.mockReset(); - loadPluginManifestRegistry.mockReturnValue({ plugins: [], diagnostics: [] }); + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReset(); + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ + plugins: [], + diagnostics: [], + }); + pluginRegistryMocks.loadPluginRegistrySnapshot.mockReset(); + pluginRegistryMocks.loadPluginRegistrySnapshot.mockReturnValue({ plugins: [] }); }); it("includes later-installed plugin env vars without a bundled generated map", async () => { - loadPluginManifestRegistry.mockReturnValue({ + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ plugins: [ { id: "external-mattermost", diff --git a/src/secrets/channel-env-vars.ts b/src/secrets/channel-env-vars.ts index 98a7976bcac..b199990e031 100644 --- a/src/secrets/channel-env-vars.ts +++ b/src/secrets/channel-env-vars.ts @@ -1,5 +1,6 @@ import type { OpenClawConfig } from "../config/types.openclaw.js"; -import { loadPluginManifestRegistry } from "../plugins/manifest-registry.js"; +import { loadPluginManifestRegistryForInstalledIndex } from "../plugins/manifest-registry-installed.js"; +import { loadPluginRegistrySnapshot } from "../plugins/plugin-registry.js"; export { isSafeChannelEnvVarTriggerName } from "./channel-env-var-names.js"; type ChannelEnvVarLookupParams = { @@ -32,11 +33,18 @@ function appendUniqueEnvVarCandidates( export function resolveChannelEnvVars( params?: ChannelEnvVarLookupParams, ): Record { - const registry = loadPluginManifestRegistry({ + const index = loadPluginRegistrySnapshot({ config: params?.config, workspaceDir: params?.workspaceDir, env: params?.env, }); + const registry = loadPluginManifestRegistryForInstalledIndex({ + index, + config: params?.config, + workspaceDir: params?.workspaceDir, + env: params?.env, + includeDisabled: true, + }); const candidates: Record = {}; for (const plugin of registry.plugins) { if (!plugin.channelEnvVars) { diff --git a/src/secrets/provider-env-vars.dynamic.test.ts b/src/secrets/provider-env-vars.dynamic.test.ts index ca92f270323..d51caa8cc2d 100644 --- a/src/secrets/provider-env-vars.dynamic.test.ts +++ b/src/secrets/provider-env-vars.dynamic.test.ts @@ -25,23 +25,37 @@ type MockManifestRegistry = { diagnostics: unknown[]; }; -const loadPluginManifestRegistry = vi.hoisted(() => - vi.fn<() => MockManifestRegistry>(() => ({ plugins: [], diagnostics: [] })), -); +const pluginRegistryMocks = vi.hoisted(() => ({ + loadPluginManifestRegistryForInstalledIndex: vi.fn<() => MockManifestRegistry>(() => ({ + plugins: [], + diagnostics: [], + })), + loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })), +})); -vi.mock("../plugins/manifest-registry.js", () => ({ - loadPluginManifestRegistry, +vi.mock("../plugins/manifest-registry-installed.js", () => ({ + loadPluginManifestRegistryForInstalledIndex: + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex, +})); + +vi.mock("../plugins/plugin-registry.js", () => ({ + loadPluginRegistrySnapshot: pluginRegistryMocks.loadPluginRegistrySnapshot, })); describe("provider env vars dynamic manifest metadata", () => { beforeEach(() => { - loadPluginManifestRegistry.mockReset(); - loadPluginManifestRegistry.mockReturnValue({ plugins: [], diagnostics: [] }); + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReset(); + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ + plugins: [], + diagnostics: [], + }); + pluginRegistryMocks.loadPluginRegistrySnapshot.mockReset(); + pluginRegistryMocks.loadPluginRegistrySnapshot.mockReturnValue({ plugins: [] }); __testing.resetProviderEnvVarCachesForTests(); }); it("includes later-installed plugin env vars without a bundled generated map", async () => { - loadPluginManifestRegistry.mockReturnValue({ + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ plugins: [ { id: "external-fireworks", @@ -64,7 +78,7 @@ describe("provider env vars dynamic manifest metadata", () => { }); it("includes setup provider env vars without loading setup runtime", async () => { - loadPluginManifestRegistry.mockReturnValue({ + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ plugins: [ { id: "external-model-studio", @@ -88,7 +102,7 @@ describe("provider env vars dynamic manifest metadata", () => { }); it("appends setup provider env vars after explicit provider auth env vars", async () => { - loadPluginManifestRegistry.mockReturnValue({ + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ plugins: [ { id: "external-fireworks", @@ -113,7 +127,7 @@ describe("provider env vars dynamic manifest metadata", () => { }); it("keeps lazy manifest-backed exports cold until accessed and resolves them once", async () => { - loadPluginManifestRegistry.mockReturnValue({ + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ plugins: [ { id: "external-fireworks", @@ -126,19 +140,22 @@ describe("provider env vars dynamic manifest metadata", () => { diagnostics: [], }); - expect(loadPluginManifestRegistry).not.toHaveBeenCalled(); + expect(pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex).not.toHaveBeenCalled(); expect(PROVIDER_ENV_VARS.fireworks).toEqual(["FIREWORKS_ALT_API_KEY"]); expect(PROVIDER_AUTH_ENV_VAR_CANDIDATES.fireworks).toEqual(["FIREWORKS_ALT_API_KEY"]); - const initialLoads = loadPluginManifestRegistry.mock.calls.length; + const initialLoads = + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mock.calls.length; expect(initialLoads).toBeGreaterThan(0); void PROVIDER_ENV_VARS.fireworks; void PROVIDER_AUTH_ENV_VAR_CANDIDATES.fireworks; - expect(loadPluginManifestRegistry).toHaveBeenCalledTimes(initialLoads); + expect(pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex).toHaveBeenCalledTimes( + initialLoads, + ); }); it("reuses the lazy default lookup cache for repeated provider env var reads", async () => { - loadPluginManifestRegistry.mockReturnValue({ + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ plugins: [ { id: "external-fireworks", @@ -152,14 +169,17 @@ describe("provider env vars dynamic manifest metadata", () => { }); expect(getProviderEnvVars("fireworks")).toEqual(["FIREWORKS_ALT_API_KEY"]); - const initialLoads = loadPluginManifestRegistry.mock.calls.length; + const initialLoads = + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mock.calls.length; expect(initialLoads).toBeGreaterThan(0); expect(getProviderEnvVars("fireworks")).toEqual(["FIREWORKS_ALT_API_KEY"]); - expect(loadPluginManifestRegistry).toHaveBeenCalledTimes(initialLoads); + expect(pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex).toHaveBeenCalledTimes( + initialLoads, + ); }); it("keeps workspace plugin env vars in default lookups", async () => { - loadPluginManifestRegistry.mockReturnValue({ + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ plugins: [ { id: "workspace-audio", @@ -179,7 +199,7 @@ describe("provider env vars dynamic manifest metadata", () => { }); it("excludes untrusted workspace plugin env vars when requested", async () => { - loadPluginManifestRegistry.mockReturnValue({ + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ plugins: [ { id: "workspace-audio", @@ -229,7 +249,7 @@ describe("provider env vars dynamic manifest metadata", () => { }); it("keeps explicitly trusted workspace plugin env vars when requested", async () => { - loadPluginManifestRegistry.mockReturnValue({ + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ plugins: [ { id: "workspace-audio", @@ -257,7 +277,7 @@ describe("provider env vars dynamic manifest metadata", () => { }); it("does not trust arbitrary workspace plugin ids from the context engine slot", async () => { - loadPluginManifestRegistry.mockReturnValue({ + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ plugins: [ { id: "workspace-audio", @@ -287,7 +307,7 @@ describe("provider env vars dynamic manifest metadata", () => { }); it("keeps selected workspace context engine env vars when requested", async () => { - loadPluginManifestRegistry.mockReturnValue({ + pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ plugins: [ { id: "workspace-engine", diff --git a/src/secrets/provider-env-vars.ts b/src/secrets/provider-env-vars.ts index bccfd7c5089..46d249a35d4 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 { loadPluginManifestRegistry } from "../plugins/manifest-registry.js"; +import { loadPluginManifestRegistryForInstalledIndex } from "../plugins/manifest-registry-installed.js"; import type { PluginManifestRecord } from "../plugins/manifest-registry.js"; import { isWorkspacePluginAllowedByConfig, normalizePluginConfigId, } from "../plugins/plugin-config-trust.js"; +import { loadPluginRegistrySnapshot } from "../plugins/plugin-registry.js"; import { hasKind } from "../plugins/slots.js"; const CORE_PROVIDER_AUTH_ENV_VAR_CANDIDATES = { @@ -76,11 +77,18 @@ function appendUniqueEnvVarCandidates( function resolveManifestProviderAuthEnvVarCandidates( params?: ProviderEnvVarLookupParams, ): Record { - const registry = loadPluginManifestRegistry({ + const index = loadPluginRegistrySnapshot({ config: params?.config, workspaceDir: params?.workspaceDir, env: params?.env, }); + const registry = loadPluginManifestRegistryForInstalledIndex({ + index, + config: params?.config, + workspaceDir: params?.workspaceDir, + env: params?.env, + includeDisabled: true, + }); const candidates: Record = {}; for (const plugin of registry.plugins) { if (!shouldUsePluginProviderEnvVars(plugin, params)) { diff --git a/src/secrets/target-registry-data.ts b/src/secrets/target-registry-data.ts index da2174213ac..84ee403cb82 100644 --- a/src/secrets/target-registry-data.ts +++ b/src/secrets/target-registry-data.ts @@ -1,7 +1,6 @@ -import { - loadPluginManifestRegistry, - type PluginManifestRecord, -} from "../plugins/manifest-registry.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 { loadBundledChannelSecretContractApi } from "./channel-contract-api.js"; import type { SecretTargetRegistryEntry } from "./target-registry-types.js"; @@ -51,7 +50,11 @@ function hasWebProviderContract( function listBundledWebProviderSecretTargetRegistryEntries(): SecretTargetRegistryEntry[] { const entries: SecretTargetRegistryEntry[] = []; - for (const record of loadPluginManifestRegistry({}).plugins) { + const index = loadPluginRegistrySnapshot({}); + for (const record of loadPluginManifestRegistryForInstalledIndex({ + index, + includeDisabled: true, + }).plugins) { if (record.origin !== "bundled") { continue; } @@ -70,7 +73,11 @@ function listBundledWebProviderSecretTargetRegistryEntries(): SecretTargetRegist function listChannelSecretTargetRegistryEntries(): SecretTargetRegistryEntry[] { const entries: SecretTargetRegistryEntry[] = []; - for (const record of loadPluginManifestRegistry({}).plugins) { + const index = loadPluginRegistrySnapshot({}); + for (const record of loadPluginManifestRegistryForInstalledIndex({ + index, + includeDisabled: true, + }).plugins) { if (record.origin !== "bundled") { continue; }