From aec1bfa0bbc82b1e123225f4026b57d913dea4cf Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 26 Apr 2026 03:24:48 -0700 Subject: [PATCH] fix(models): keep cold catalog lookup registry indexed --- .../models/list.provider-catalog.test.ts | 15 ++-- src/commands/models/list.provider-catalog.ts | 9 ++- src/plugins/synthetic-auth.runtime.test.ts | 72 +++++++++++-------- src/plugins/synthetic-auth.runtime.ts | 10 +-- 4 files changed, 65 insertions(+), 41 deletions(-) diff --git a/src/commands/models/list.provider-catalog.test.ts b/src/commands/models/list.provider-catalog.test.ts index e599126a945..9752770dfa0 100644 --- a/src/commands/models/list.provider-catalog.test.ts +++ b/src/commands/models/list.provider-catalog.test.ts @@ -6,7 +6,7 @@ import { } from "./list.provider-catalog.js"; const providerDiscoveryMocks = vi.hoisted(() => ({ - loadPluginRegistrySnapshot: vi.fn(), + loadPluginRegistrySnapshotWithMetadata: vi.fn(), resolvePluginContributionOwners: vi.fn(), resolveProviderOwners: vi.fn(), resolveBundledProviderCompatPluginIds: vi.fn(), @@ -17,7 +17,8 @@ const providerDiscoveryMocks = vi.hoisted(() => ({ vi.mock("../../plugins/plugin-registry.js", () => ({ loadPluginManifestRegistryForPluginRegistry: () => ({ diagnostics: [], plugins: [] }), - loadPluginRegistrySnapshot: providerDiscoveryMocks.loadPluginRegistrySnapshot, + loadPluginRegistrySnapshotWithMetadata: + providerDiscoveryMocks.loadPluginRegistrySnapshotWithMetadata, resolvePluginContributionOwners: providerDiscoveryMocks.resolvePluginContributionOwners, resolveProviderOwners: providerDiscoveryMocks.resolveProviderOwners, })); @@ -115,8 +116,11 @@ const defaultProviders = [chutesProvider, moonshotProvider, openaiProvider]; describe("loadProviderCatalogModelsForList", () => { beforeEach(() => { vi.clearAllMocks(); - providerDiscoveryMocks.loadPluginRegistrySnapshot.mockReturnValue({ - plugins: [], + providerDiscoveryMocks.loadPluginRegistrySnapshotWithMetadata.mockReturnValue({ + source: "persisted", + snapshot: { + plugins: [], + }, diagnostics: [], }); providerDiscoveryMocks.resolveProviderOwners.mockImplementation( @@ -197,9 +201,10 @@ describe("loadProviderCatalogModelsForList", () => { }), ).resolves.toEqual(["moonshot"]); - expect(providerDiscoveryMocks.loadPluginRegistrySnapshot).toHaveBeenCalledWith({ + expect(providerDiscoveryMocks.loadPluginRegistrySnapshotWithMetadata).toHaveBeenCalledWith({ config: baseParams.cfg, env: baseParams.env, + cache: true, }); expect(providerDiscoveryMocks.resolveOwningPluginIdsForProvider).not.toHaveBeenCalled(); }); diff --git a/src/commands/models/list.provider-catalog.ts b/src/commands/models/list.provider-catalog.ts index 26b33b272f8..14491a15beb 100644 --- a/src/commands/models/list.provider-catalog.ts +++ b/src/commands/models/list.provider-catalog.ts @@ -5,7 +5,7 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { formatErrorMessage } from "../../infra/errors.js"; import { createSubsystemLogger } from "../../logging/subsystem.js"; import { - loadPluginRegistrySnapshot, + loadPluginRegistrySnapshotWithMetadata, resolvePluginContributionOwners, resolveProviderOwners, type PluginRegistrySnapshot, @@ -70,10 +70,15 @@ function resolveInstalledIndexPluginIdsForProviderFilter(params: { env?: NodeJS.ProcessEnv; providerFilter: string; }): string[] | undefined { - const index = loadPluginRegistrySnapshot({ + const snapshot = loadPluginRegistrySnapshotWithMetadata({ config: params.cfg, env: params.env, + cache: true, }); + if (snapshot.source !== "persisted" && snapshot.source !== "provided") { + return []; + } + const index = snapshot.snapshot; const pluginIds = [ ...collectMatchingContributionOwners(index, "providers", params.providerFilter, params.cfg), ...collectMatchingContributionOwners(index, "cliBackends", params.providerFilter, params.cfg), diff --git a/src/plugins/synthetic-auth.runtime.test.ts b/src/plugins/synthetic-auth.runtime.test.ts index 0c3ecd81e61..1d51d37b8a3 100644 --- a/src/plugins/synthetic-auth.runtime.test.ts +++ b/src/plugins/synthetic-auth.runtime.test.ts @@ -2,26 +2,20 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; const getPluginRegistryState = vi.hoisted(() => vi.fn()); const pluginRegistryMocks = vi.hoisted(() => ({ - loadPluginManifestRegistryForInstalledIndex: vi.fn(), - loadPluginRegistrySnapshot: vi.fn((_params?: unknown) => ({ plugins: [] })), + loadPluginRegistrySnapshotWithMetadata: vi.fn((_params?: unknown) => ({ + source: "persisted", + snapshot: { plugins: [] }, + diagnostics: [], + })), })); vi.mock("./runtime-state.js", () => ({ getPluginRegistryState, })); -vi.mock("./manifest-registry-installed.js", () => ({ - loadPluginManifestRegistryForInstalledIndex: - pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex, -})); - vi.mock("./plugin-registry.js", () => ({ - loadPluginRegistrySnapshot: pluginRegistryMocks.loadPluginRegistrySnapshot, - loadPluginManifestRegistryForPluginRegistry: () => - pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex({ - index: pluginRegistryMocks.loadPluginRegistrySnapshot({ cache: true }), - includeDisabled: true, - }), + loadPluginRegistrySnapshotWithMetadata: + pluginRegistryMocks.loadPluginRegistrySnapshotWithMetadata, })); import { resolveRuntimeSyntheticAuthProviderRefs } from "./synthetic-auth.runtime.js"; @@ -29,19 +23,24 @@ import { resolveRuntimeSyntheticAuthProviderRefs } from "./synthetic-auth.runtim describe("synthetic auth runtime refs", () => { beforeEach(() => { getPluginRegistryState.mockReset(); - pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex - .mockReset() - .mockReturnValue({ plugins: [] }); - pluginRegistryMocks.loadPluginRegistrySnapshot.mockReset().mockReturnValue({ plugins: [] }); + pluginRegistryMocks.loadPluginRegistrySnapshotWithMetadata.mockReset().mockReturnValue({ + source: "persisted", + snapshot: { plugins: [] }, + diagnostics: [], + }); }); - it("uses manifest-owned synthetic auth refs before the runtime registry exists", () => { - pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({ - plugins: [ - { syntheticAuthRefs: [" local-provider ", "local-provider", "local-cli"] }, - { syntheticAuthRefs: ["remote-provider"] }, - { syntheticAuthRefs: [] }, - ], + it("uses persisted registry synthetic auth refs before the runtime registry exists", () => { + pluginRegistryMocks.loadPluginRegistrySnapshotWithMetadata.mockReturnValue({ + source: "persisted", + snapshot: { + plugins: [ + { syntheticAuthRefs: [" local-provider ", "local-provider", "local-cli"] }, + { syntheticAuthRefs: ["remote-provider"] }, + { syntheticAuthRefs: [] }, + ], + }, + diagnostics: [], }); expect(resolveRuntimeSyntheticAuthProviderRefs()).toEqual([ @@ -49,13 +48,27 @@ describe("synthetic auth runtime refs", () => { "local-cli", "remote-provider", ]); - expect(pluginRegistryMocks.loadPluginRegistrySnapshot).toHaveBeenCalledWith({ cache: true }); - expect(pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex).toHaveBeenCalledWith({ - index: expect.anything(), - includeDisabled: true, + expect(pluginRegistryMocks.loadPluginRegistrySnapshotWithMetadata).toHaveBeenCalledWith({ + cache: true, }); }); + it("does not derive the registry just to resolve synthetic auth refs", () => { + pluginRegistryMocks.loadPluginRegistrySnapshotWithMetadata.mockReturnValue({ + source: "derived", + snapshot: { + plugins: [ + { syntheticAuthRefs: [" local-provider ", "local-provider", "local-cli"] }, + { syntheticAuthRefs: ["remote-provider"] }, + { syntheticAuthRefs: [] }, + ], + }, + diagnostics: [], + }); + + expect(resolveRuntimeSyntheticAuthProviderRefs()).toEqual([]); + }); + it("prefers the active runtime registry when plugins are already loaded", () => { getPluginRegistryState.mockReturnValue({ activeRegistry: { @@ -84,7 +97,6 @@ describe("synthetic auth runtime refs", () => { }); expect(resolveRuntimeSyntheticAuthProviderRefs()).toEqual(["runtime-provider", "runtime-cli"]); - expect(pluginRegistryMocks.loadPluginManifestRegistryForInstalledIndex).not.toHaveBeenCalled(); - expect(pluginRegistryMocks.loadPluginRegistrySnapshot).not.toHaveBeenCalled(); + expect(pluginRegistryMocks.loadPluginRegistrySnapshotWithMetadata).not.toHaveBeenCalled(); }); }); diff --git a/src/plugins/synthetic-auth.runtime.ts b/src/plugins/synthetic-auth.runtime.ts index efed10f1de3..b8d6b4b1c46 100644 --- a/src/plugins/synthetic-auth.runtime.ts +++ b/src/plugins/synthetic-auth.runtime.ts @@ -1,5 +1,5 @@ import { normalizeProviderId } from "../agents/provider-id.js"; -import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js"; +import { loadPluginRegistrySnapshotWithMetadata } from "./plugin-registry.js"; import { getPluginRegistryState } from "./runtime-state.js"; function uniqueProviderRefs(values: readonly string[]): string[] { @@ -18,10 +18,12 @@ function uniqueProviderRefs(values: readonly string[]): string[] { } function resolveManifestSyntheticAuthProviderRefs(): string[] { + const result = loadPluginRegistrySnapshotWithMetadata({ cache: true }); + if (result.source !== "persisted" && result.source !== "provided") { + return []; + } return uniqueProviderRefs( - loadPluginManifestRegistryForPluginRegistry({ includeDisabled: true }).plugins.flatMap( - (plugin) => plugin.syntheticAuthRefs ?? [], - ), + result.snapshot.plugins.flatMap((plugin) => plugin.syntheticAuthRefs ?? []), ); }