diff --git a/src/commands/models/list.manifest-catalog.test.ts b/src/commands/models/list.manifest-catalog.test.ts new file mode 100644 index 00000000000..36dce1d902a --- /dev/null +++ b/src/commands/models/list.manifest-catalog.test.ts @@ -0,0 +1,73 @@ +import { describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + loadPluginRegistrySnapshot: vi.fn(), + resolvePluginContributionOwners: vi.fn(), + getPluginRecord: vi.fn(), + isPluginEnabled: vi.fn(), + loadPluginManifestRegistryForInstalledIndex: vi.fn(), +})); + +vi.mock("../../plugins/plugin-registry.js", () => ({ + loadPluginRegistrySnapshot: mocks.loadPluginRegistrySnapshot, + resolvePluginContributionOwners: mocks.resolvePluginContributionOwners, + getPluginRecord: mocks.getPluginRecord, + isPluginEnabled: mocks.isPluginEnabled, +})); + +vi.mock("../../plugins/manifest-registry-installed.js", () => ({ + loadPluginManifestRegistryForInstalledIndex: mocks.loadPluginManifestRegistryForInstalledIndex, +})); + +const moonshotPlugin = { + id: "moonshot", + providers: ["moonshot"], + modelCatalog: { + providers: { + moonshot: { + models: [{ id: "kimi-k2.6", name: "Kimi K2.6" }], + }, + }, + discovery: { + moonshot: "static", + }, + }, +}; + +const openrouterPlugin = { + id: "openrouter", + providers: ["openrouter"], + modelCatalog: { + providers: { + openrouter: { + models: [{ id: "auto", name: "Auto" }], + }, + }, + discovery: { + openrouter: "refreshable", + }, + }, +}; + +describe("loadStaticManifestCatalogRowsForList", () => { + it("loads all static manifest catalog rows without a provider filter", async () => { + const { loadStaticManifestCatalogRowsForList } = await import("./list.manifest-catalog.js"); + const index = { plugins: [], diagnostics: [] }; + mocks.loadPluginRegistrySnapshot.mockReturnValueOnce(index); + mocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValueOnce({ + plugins: [openrouterPlugin, moonshotPlugin], + diagnostics: [], + }); + + expect( + loadStaticManifestCatalogRowsForList({ + cfg: {}, + }).map((row) => row.ref), + ).toEqual(["moonshot/kimi-k2.6"]); + expect(mocks.loadPluginManifestRegistryForInstalledIndex).toHaveBeenCalledWith({ + index, + config: {}, + env: undefined, + }); + }); +}); diff --git a/src/commands/models/list.manifest-catalog.ts b/src/commands/models/list.manifest-catalog.ts index 6ddc93f73c0..65f82d30f52 100644 --- a/src/commands/models/list.manifest-catalog.ts +++ b/src/commands/models/list.manifest-catalog.ts @@ -17,10 +17,10 @@ function loadStaticManifestCatalogRowsForPluginIds(params: { cfg: OpenClawConfig; env?: NodeJS.ProcessEnv; index: PluginRegistrySnapshot; - pluginIds: readonly string[]; - providerFilter: string; + pluginIds?: readonly string[]; + providerFilter?: string; }): readonly NormalizedModelCatalogRow[] { - if (params.pluginIds.length === 0) { + if (params.pluginIds && params.pluginIds.length === 0) { return []; } const registry = loadPluginManifestRegistryForInstalledIndex({ @@ -31,7 +31,7 @@ function loadStaticManifestCatalogRowsForPluginIds(params: { }); const plan = planManifestModelCatalogRows({ registry, - providerFilter: params.providerFilter, + ...(params.providerFilter ? { providerFilter: params.providerFilter } : {}), }); const staticProviders = new Set( plan.entries.filter((entry) => entry.discovery === "static").map((entry) => entry.provider), @@ -79,17 +79,23 @@ function resolveDeclaredModelCatalogPluginIds(params: { export function loadStaticManifestCatalogRowsForList(params: { cfg: OpenClawConfig; - providerFilter: string; + providerFilter?: string; env?: NodeJS.ProcessEnv; }): readonly NormalizedModelCatalogRow[] { - const providerFilter = normalizeModelCatalogProviderId(params.providerFilter); - if (!providerFilter) { - return []; - } + const providerFilter = params.providerFilter + ? normalizeModelCatalogProviderId(params.providerFilter) + : undefined; const index = loadPluginRegistrySnapshot({ config: params.cfg, env: params.env, }); + if (!providerFilter) { + return loadStaticManifestCatalogRowsForPluginIds({ + cfg: params.cfg, + env: params.env, + index, + }); + } const conventionRows = loadStaticManifestCatalogRowsForPluginIds({ cfg: params.cfg, env: params.env, diff --git a/src/commands/models/list.provider-index-catalog.test.ts b/src/commands/models/list.provider-index-catalog.test.ts index b5124ea0f98..288d489a8dd 100644 --- a/src/commands/models/list.provider-index-catalog.test.ts +++ b/src/commands/models/list.provider-index-catalog.test.ts @@ -14,6 +14,14 @@ describe("loadProviderIndexCatalogRowsForList", () => { ).toContain("moonshot/kimi-k2.6"); }); + it("returns all enabled provider-index preview rows without a provider filter", () => { + expect( + loadProviderIndexCatalogRowsForList({ + cfg: baseConfig, + }).map((row) => row.ref), + ).toEqual(expect.arrayContaining(["deepseek/deepseek-chat", "moonshot/kimi-k2.6"])); + }); + it("suppresses provider-index preview rows when the provider plugin is disabled", () => { expect( loadProviderIndexCatalogRowsForList({ diff --git a/src/commands/models/list.provider-index-catalog.ts b/src/commands/models/list.provider-index-catalog.ts index 7052cccf3c1..1a369bb43d3 100644 --- a/src/commands/models/list.provider-index-catalog.ts +++ b/src/commands/models/list.provider-index-catalog.ts @@ -8,17 +8,16 @@ import type { NormalizedModelCatalogRow } from "../../model-catalog/index.js"; import { normalizePluginsConfig, resolveEffectiveEnableState } from "../../plugins/config-state.js"; export function loadProviderIndexCatalogRowsForList(params: { - providerFilter: string; + providerFilter?: string; cfg: OpenClawConfig; }): readonly NormalizedModelCatalogRow[] { - const providerFilter = normalizeModelCatalogProviderId(params.providerFilter); - if (!providerFilter) { - return []; - } + const providerFilter = params.providerFilter + ? normalizeModelCatalogProviderId(params.providerFilter) + : undefined; const index = loadOpenClawProviderIndex(); return planProviderIndexModelCatalogRows({ index, - providerFilter, + ...(providerFilter ? { providerFilter } : {}), }) .entries.filter( (entry) =>