diff --git a/src/cli/capability-cli.test.ts b/src/cli/capability-cli.test.ts index 6b3978c0a27b..c81e99f2b250 100644 --- a/src/cli/capability-cli.test.ts +++ b/src/cli/capability-cli.test.ts @@ -2979,6 +2979,34 @@ describe("capability cli", () => { expect(firstEmbeddingProviderCall()?.model).toBe("text-embedding-3-large"); }); + it.each([ + { + name: "embedding create", + argv: ["capability", "embedding", "create", "--text", "hello"], + }, + { + name: "image generate", + argv: ["capability", "image", "generate", "--prompt", "portrait"], + }, + { + name: "image edit", + argv: ["capability", "image", "edit", "--file", "photo.png", "--prompt", "crop it"], + }, + { + name: "video generate", + argv: ["capability", "video", "generate", "--prompt", "clip"], + }, + ])("rejects malformed model refs before $name provider dispatch", async ({ argv }) => { + for (const model of ["openai/", "/gpt-4.1-mini"]) { + await expect(runCap(...argv, "--model", model, "--json")).rejects.toThrow("exit 1"); + expectRuntimeErrorContains("Model overrides must use the form ."); + expect(mocks.resolveCommandConfigWithSecrets).not.toHaveBeenCalled(); + expect(mocks.createEmbeddingProvider).not.toHaveBeenCalled(); + expect(mocks.generateImage).not.toHaveBeenCalled(); + expect(mocks.generateVideo).not.toHaveBeenCalled(); + } + }); + it("cleans provider auth profiles and usage stats on logout", async () => { mocks.loadAuthProfileStoreForRuntime.mockReturnValue({ profiles: { diff --git a/src/cli/capability-cli/embedding.ts b/src/cli/capability-cli/embedding.ts index 6ee49873ea97..dfee2c518836 100644 --- a/src/cli/capability-cli/embedding.ts +++ b/src/cli/capability-cli/embedding.ts @@ -16,8 +16,8 @@ import { formatEnvelopeForText, providerHasGenericConfig, providerSummaryText, + requireProviderModelOverride, resolveLocalCapabilityRuntimeConfig, - resolveModelRefOverride, } from "./shared.js"; async function closeEmbeddingProviderWithRetry(provider: { @@ -40,18 +40,19 @@ async function runMemoryEmbeddingCreate(params: { provider?: string; model?: string; }) { + const modelRef = requireProviderModelOverride(params.model); const cfg = await resolveLocalCapabilityRuntimeConfig({ commandName: "infer embedding create", targetIds: getMemoryEmbeddingCommandSecretTargetIds(), }); - const modelRef = resolveModelRefOverride(params.model); - const requestedProvider = normalizeOptionalString(params.provider) || modelRef.provider || "auto"; + const requestedProvider = + normalizeOptionalString(params.provider) || modelRef?.provider || "auto"; const result = await createEmbeddingProvider({ config: cfg, agentDir: resolveAgentDir(cfg, resolveDefaultAgentId(cfg)), provider: requestedProvider, fallback: "none", - model: modelRef.model ?? "", + model: modelRef?.model ?? "", }); if (!result.provider) { throw new Error(result.providerUnavailableReason ?? "No embedding provider available."); diff --git a/src/cli/capability-cli/image.ts b/src/cli/capability-cli/image.ts index 683574be7808..5816d2d623d5 100644 --- a/src/cli/capability-cli/image.ts +++ b/src/cli/capability-cli/image.ts @@ -64,6 +64,7 @@ async function runImageGenerate(params: { output?: string; timeoutMs?: number; }) { + requireProviderModelOverride(params.model); const cfg = await resolveLocalCapabilityRuntimeConfig({ commandName: `infer ${params.capability}`, targetIds: getModelsCommandSecretTargetIds(), diff --git a/src/cli/capability-cli/video.ts b/src/cli/capability-cli/video.ts index 7d7f740f3dd1..109237cc368e 100644 --- a/src/cli/capability-cli/video.ts +++ b/src/cli/capability-cli/video.ts @@ -109,6 +109,7 @@ async function runVideoGenerate(params: { watermark?: boolean; timeoutMs?: number; }) { + requireProviderModelOverride(params.model); const cfg = await resolveLocalCapabilityRuntimeConfig({ commandName: "infer video.generate", targetIds: getModelsCommandSecretTargetIds(),