




















@@ -20,6 +20,8 @@ const mocks = vi.hoisted(() => ({
2020writeStdout: vi.fn(),
2121},
2222loadConfig: vi.fn(() => ({})),
23+getRuntimeConfigSourceSnapshot: vi.fn(() => null),
24+setRuntimeConfigSnapshot: vi.fn(),
2325loadAuthProfileStoreForRuntime: vi.fn(() => ({ profiles: {}, order: {} })),
2426listProfilesForProvider: vi.fn(() => []),
2527updateAuthProfileStoreWithLock: vi.fn(
@@ -154,8 +156,12 @@ vi.mock("../runtime.js", () => ({
154156}));
155157156158vi.mock("../config/config.js", () => ({
159+getRuntimeConfigSourceSnapshot:
160+mocks.getRuntimeConfigSourceSnapshot as typeof import("../config/config.js").getRuntimeConfigSourceSnapshot,
157161getRuntimeConfig: mocks.loadConfig as typeof import("../config/config.js").getRuntimeConfig,
158162loadConfig: mocks.loadConfig as typeof import("../config/config.js").loadConfig,
163+setRuntimeConfigSnapshot:
164+mocks.setRuntimeConfigSnapshot as typeof import("../config/config.js").setRuntimeConfigSnapshot,
159165}));
160166161167vi.mock("./command-config-resolution.js", () => ({
@@ -395,6 +401,8 @@ describe("capability cli", () => {
395401.mockResolvedValue([{ id: "gpt-5.4", provider: "openai", name: "GPT-5.4" }] as never);
396402mocks.loadAuthProfileStoreForRuntime.mockReset().mockReturnValue({ profiles: {}, order: {} });
397403mocks.listProfilesForProvider.mockReset().mockReturnValue([]);
404+mocks.getRuntimeConfigSourceSnapshot.mockReset().mockReturnValue(null);
405+mocks.setRuntimeConfigSnapshot.mockClear();
398406mocks.updateAuthProfileStoreWithLock
399407.mockReset()
400408.mockImplementation(async ({ updater }: { updater: (store: any) => boolean }) => {
@@ -440,6 +448,13 @@ describe("capability cli", () => {
440448mocks.registerBuiltInMemoryEmbeddingProviders.mockClear();
441449mocks.isWebSearchProviderConfigured.mockReset().mockReturnValue(false);
442450mocks.isWebFetchProviderConfigured.mockReset().mockReturnValue(false);
451+mocks.resolveCommandConfigWithSecrets
452+.mockReset()
453+.mockImplementation(async ({ config }: { config: Record<string, unknown> }) => ({
454+resolvedConfig: config,
455+effectiveConfig: config,
456+diagnostics: [],
457+}));
443458mocks.modelsStatusCommand.mockClear();
444459mocks.callGateway.mockImplementation((async ({ method }: { method: string }) => {
445460if (method === "tts.status") {
@@ -498,6 +513,7 @@ describe("capability cli", () => {
498513};
499514type ImageDescribeParams = {
500515filePath?: string;
516+mediaUrl?: string;
501517model?: unknown;
502518prompt?: unknown;
503519provider?: unknown;
@@ -528,6 +544,13 @@ describe("capability cli", () => {
528544return calls[0]?.[0];
529545}
530546547+function firstCommandConfigResolutionCall() {
548+const calls = mocks.resolveCommandConfigWithSecrets.mock.calls as unknown as Array<
549+[Record<string, unknown>]
550+>;
551+return calls[0]?.[0];
552+}
553+531554function firstRegisteredEmbeddingBootstrapArg() {
532555const calls = mocks.registerBuiltInMemoryEmbeddingProviders.mock.calls as unknown as Array<
533556[{ registerMemoryEmbeddingProvider?: unknown }]
@@ -559,7 +582,7 @@ describe("capability cli", () => {
559582560583function firstAudioTranscriptionCall() {
561584const calls = mocks.transcribeAudioFile.mock.calls as unknown as Array<
562-[{ filePath?: string; language?: unknown; prompt?: unknown }]
585+[{ cfg?: unknown; filePath?: string; language?: unknown; prompt?: unknown }]
563586>;
564587return calls[0]?.[0];
565588}
@@ -1204,6 +1227,26 @@ describe("capability cli", () => {
12041227expect(describeCall?.timeoutMs).toBe(90000);
12051228});
120612291230+it("keeps image describe URL files as remote media references", async () => {
1231+await runRegisteredCli({
1232+register: registerCapabilityCli as (program: Command) => void,
1233+argv: [
1234+"capability",
1235+"image",
1236+"describe",
1237+"--file",
1238+"https://example.com/photo.png",
1239+"--json",
1240+],
1241+});
1242+1243+const describeCall = imageDescribeCall();
1244+expect(describeCall?.filePath).toBe("https://example.com/photo.png");
1245+expect(describeCall?.mediaUrl).toBe("https://example.com/photo.png");
1246+const outputs = firstJsonOutput()?.outputs as Array<Record<string, unknown>>;
1247+expect(outputs[0]?.path).toBe("https://example.com/photo.png");
1248+});
1249+12071250it("uses the explicit media-understanding provider for image describe model overrides", async () => {
12081251await runRegisteredCli({
12091252register: registerCapabilityCli as (program: Command) => void,
@@ -1808,6 +1851,35 @@ describe("capability cli", () => {
18081851expect(outputs[0]?.kind).toBe("audio.transcription");
18091852});
181018531854+it("resolves command SecretRefs before local audio transcription", async () => {
1855+const rawConfig = { models: { providers: { openai: { apiKey: "raw-ref" } } } };
1856+const resolvedConfig = { models: { providers: { openai: { apiKey: "resolved-key" } } } };
1857+mocks.loadConfig.mockReturnValue(rawConfig);
1858+mocks.resolveCommandConfigWithSecrets.mockResolvedValueOnce({
1859+ resolvedConfig,
1860+effectiveConfig: resolvedConfig,
1861+diagnostics: [],
1862+} as never);
1863+1864+await runRegisteredCli({
1865+register: registerCapabilityCli as (program: Command) => void,
1866+argv: ["capability", "audio", "transcribe", "--file", "memo.m4a", "--json"],
1867+});
1868+1869+expect(firstCommandConfigResolutionCall()).toEqual(
1870+expect.objectContaining({
1871+config: rawConfig,
1872+commandName: "infer audio transcribe",
1873+}),
1874+);
1875+expect(
1876+(firstCommandConfigResolutionCall()?.targetIds as Set<string>).has(
1877+"models.providers.*.apiKey",
1878+),
1879+).toBe(true);
1880+expect(firstAudioTranscriptionCall()?.cfg).toBe(resolvedConfig);
1881+});
1882+18111883it("fails audio transcribe when no transcript text is returned", async () => {
18121884mocks.transcribeAudioFile.mockResolvedValueOnce({ text: undefined } as never);
18131885@@ -1991,6 +2063,37 @@ describe("capability cli", () => {
19912063expect(firstJsonOutput()?.model).toBe("text-embedding-3-small");
19922064});
199320652066+it("resolves command SecretRefs before local model capability execution", async () => {
2067+const rawConfig = { agents: { defaults: { model: "openai/gpt-5.4" } } };
2068+const resolvedConfig = { agents: { defaults: { model: "openai/gpt-5.4" } }, resolved: true };
2069+mocks.loadConfig.mockReturnValue(rawConfig);
2070+mocks.resolveCommandConfigWithSecrets.mockResolvedValueOnce({
2071+ resolvedConfig,
2072+effectiveConfig: resolvedConfig,
2073+diagnostics: [],
2074+} as never);
2075+2076+await runRegisteredCli({
2077+register: registerCapabilityCli as (program: Command) => void,
2078+argv: ["capability", "model", "run", "--prompt", "hello", "--json"],
2079+});
2080+2081+expect(firstCommandConfigResolutionCall()).toEqual(
2082+expect.objectContaining({
2083+config: rawConfig,
2084+commandName: "infer model run",
2085+runtime: mocks.runtime,
2086+}),
2087+);
2088+expect(
2089+(firstCommandConfigResolutionCall()?.targetIds as Set<string>).has(
2090+"models.providers.*.apiKey",
2091+),
2092+).toBe(true);
2093+expect(firstPreparedModelParams()?.cfg).toBe(resolvedConfig);
2094+expect(mocks.setRuntimeConfigSnapshot).toHaveBeenCalledWith(resolvedConfig);
2095+});
2096+19942097it("derives the embedding provider from a provider/model override", async () => {
19952098await runRegisteredCli({
19962099register: registerCapabilityCli as (program: Command) => void,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。