

























@@ -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(
@@ -133,6 +135,23 @@ const mocks = vi.hoisted(() => ({
133135convertHeicToJpeg: vi.fn(async () => Buffer.from("jpeg-normalized")),
134136isWebSearchProviderConfigured: vi.fn(() => false),
135137isWebFetchProviderConfigured: vi.fn(() => false),
138+resolveCommandConfigWithSecrets: vi.fn(async ({ config }: { config: unknown }) => ({
139+resolvedConfig: config,
140+effectiveConfig: config,
141+diagnostics: [],
142+})),
143+getAgentRuntimeCommandSecretTargetIds: vi.fn(() => new Set(["agent-runtime-target"])),
144+getMemoryEmbeddingCommandSecretTargetIds: vi.fn(() => new Set(["memory-target"])),
145+getModelsCommandSecretTargetIds: vi.fn(() => new Set(["model-target"])),
146+getTtsCommandSecretTargetIds: vi.fn(() => new Set(["tts-target"])),
147+getWebFetchCommandSecretTargets: vi.fn(() => ({
148+targetIds: new Set(["web-fetch-target"]),
149+allowedPaths: new Set(["plugins.entries.firecrawl.config.webFetch.apiKey"]),
150+})),
151+getWebSearchCommandSecretTargets: vi.fn(() => ({
152+targetIds: new Set(["web-search-target"]),
153+allowedPaths: new Set(["plugins.entries.tavily.config.webSearch.apiKey"]),
154+})),
136155modelsStatusCommand: vi.fn(
137156async (_opts: unknown, runtime: { log: (...args: unknown[]) => void }) => {
138157runtime.log(JSON.stringify({ ok: true, providers: [{ id: "openai" }] }));
@@ -147,8 +166,12 @@ vi.mock("../runtime.js", () => ({
147166}));
148167149168vi.mock("../config/config.js", () => ({
169+getRuntimeConfigSourceSnapshot:
170+mocks.getRuntimeConfigSourceSnapshot as typeof import("../config/config.js").getRuntimeConfigSourceSnapshot,
150171getRuntimeConfig: mocks.loadConfig as typeof import("../config/config.js").getRuntimeConfig,
151172loadConfig: mocks.loadConfig as typeof import("../config/config.js").loadConfig,
173+setRuntimeConfigSnapshot:
174+mocks.setRuntimeConfigSnapshot as typeof import("../config/config.js").setRuntimeConfigSnapshot,
152175}));
153176154177vi.mock("../agents/agent-scope.js", () => ({
@@ -292,6 +315,26 @@ vi.mock("../web-fetch/runtime.js", () => ({
292315resolveWebFetchDefinition: vi.fn(),
293316}));
294317318+vi.mock("./command-config-resolution.js", () => ({
319+resolveCommandConfigWithSecrets:
320+mocks.resolveCommandConfigWithSecrets as typeof import("./command-config-resolution.js").resolveCommandConfigWithSecrets,
321+}));
322+323+vi.mock("./command-secret-targets.js", () => ({
324+getAgentRuntimeCommandSecretTargetIds:
325+mocks.getAgentRuntimeCommandSecretTargetIds as typeof import("./command-secret-targets.js").getAgentRuntimeCommandSecretTargetIds,
326+getMemoryEmbeddingCommandSecretTargetIds:
327+mocks.getMemoryEmbeddingCommandSecretTargetIds as typeof import("./command-secret-targets.js").getMemoryEmbeddingCommandSecretTargetIds,
328+getModelsCommandSecretTargetIds:
329+mocks.getModelsCommandSecretTargetIds as typeof import("./command-secret-targets.js").getModelsCommandSecretTargetIds,
330+getTtsCommandSecretTargetIds:
331+mocks.getTtsCommandSecretTargetIds as typeof import("./command-secret-targets.js").getTtsCommandSecretTargetIds,
332+getWebFetchCommandSecretTargets:
333+mocks.getWebFetchCommandSecretTargets as typeof import("./command-secret-targets.js").getWebFetchCommandSecretTargets,
334+getWebSearchCommandSecretTargets:
335+mocks.getWebSearchCommandSecretTargets as typeof import("./command-secret-targets.js").getWebSearchCommandSecretTargets,
336+}));
337+295338describe("capability cli", () => {
296339afterEach(() => {
297340vi.unstubAllGlobals();
@@ -302,6 +345,8 @@ describe("capability cli", () => {
302345mocks.runtime.log.mockClear();
303346mocks.runtime.error.mockClear();
304347mocks.runtime.writeJson.mockClear();
348+mocks.getRuntimeConfigSourceSnapshot.mockReset().mockReturnValue(null);
349+mocks.setRuntimeConfigSnapshot.mockClear();
305350mocks.loadModelCatalog
306351.mockReset()
307352.mockResolvedValue([{ id: "gpt-5.4", provider: "openai", name: "GPT-5.4" }] as never);
@@ -352,6 +397,29 @@ describe("capability cli", () => {
352397mocks.registerBuiltInMemoryEmbeddingProviders.mockClear();
353398mocks.isWebSearchProviderConfigured.mockReset().mockReturnValue(false);
354399mocks.isWebFetchProviderConfigured.mockReset().mockReturnValue(false);
400+mocks.resolveCommandConfigWithSecrets
401+.mockReset()
402+.mockImplementation(async ({ config }: { config: unknown }) => ({
403+resolvedConfig: config,
404+effectiveConfig: config,
405+diagnostics: [],
406+}));
407+mocks.getAgentRuntimeCommandSecretTargetIds
408+.mockReset()
409+.mockReturnValue(new Set(["agent-runtime-target"]));
410+mocks.getMemoryEmbeddingCommandSecretTargetIds
411+.mockReset()
412+.mockReturnValue(new Set(["memory-target"]));
413+mocks.getModelsCommandSecretTargetIds.mockReset().mockReturnValue(new Set(["model-target"]));
414+mocks.getTtsCommandSecretTargetIds.mockReset().mockReturnValue(new Set(["tts-target"]));
415+mocks.getWebFetchCommandSecretTargets.mockReset().mockReturnValue({
416+targetIds: new Set(["web-fetch-target"]),
417+allowedPaths: new Set(["plugins.entries.firecrawl.config.webFetch.apiKey"]),
418+});
419+mocks.getWebSearchCommandSecretTargets.mockReset().mockReturnValue({
420+targetIds: new Set(["web-search-target"]),
421+allowedPaths: new Set(["plugins.entries.tavily.config.webSearch.apiKey"]),
422+});
355423mocks.modelsStatusCommand.mockClear();
356424mocks.callGateway.mockImplementation((async ({ method }: { method: string }) => {
357425if (method === "tts.status") {
@@ -488,6 +556,13 @@ describe("capability cli", () => {
488556return calls[0]?.[0];
489557}
490558559+function firstCommandConfigResolutionCall() {
560+const calls = mocks.resolveCommandConfigWithSecrets.mock.calls as unknown as Array<
561+[Record<string, unknown>]
562+>;
563+return calls[0]?.[0];
564+}
565+491566function expectModelRunDispatch(transport: "local" | "gateway", modelRef: string) {
492567if (transport === "gateway") {
493568const slash = modelRef.indexOf("/");
@@ -1814,6 +1889,35 @@ describe("capability cli", () => {
18141889expect(firstJsonOutput()?.model).toBe("text-embedding-3-small");
18151890});
181618911892+it("resolves command SecretRefs before local model capability execution", async () => {
1893+const rawConfig = { agents: { defaults: { model: "openai/gpt-5.4" } } };
1894+const resolvedConfig = { agents: { defaults: { model: "openai/gpt-5.4" } }, resolved: true };
1895+const targetIds = new Set(["models.providers.*.apiKey"]);
1896+mocks.loadConfig.mockReturnValue(rawConfig);
1897+mocks.getModelsCommandSecretTargetIds.mockReturnValue(targetIds);
1898+mocks.resolveCommandConfigWithSecrets.mockResolvedValueOnce({
1899+ resolvedConfig,
1900+effectiveConfig: resolvedConfig,
1901+diagnostics: [],
1902+} as never);
1903+1904+await runRegisteredCli({
1905+register: registerCapabilityCli as (program: Command) => void,
1906+argv: ["capability", "model", "run", "--prompt", "hello", "--json"],
1907+});
1908+1909+expect(firstCommandConfigResolutionCall()).toEqual(
1910+expect.objectContaining({
1911+config: rawConfig,
1912+commandName: "infer model run",
1913+ targetIds,
1914+runtime: mocks.runtime,
1915+}),
1916+);
1917+expect(firstPreparedModelParams()?.cfg).toBe(resolvedConfig);
1918+expect(mocks.setRuntimeConfigSnapshot).toHaveBeenCalledWith(resolvedConfig);
1919+});
1920+18171921it("derives the embedding provider from a provider/model override", async () => {
18181922await runRegisteredCli({
18191923register: registerCapabilityCli as (program: Command) => void,
@@ -2098,6 +2202,159 @@ describe("capability cli", () => {
20982202});
20992203});
210022042205+it("resolves command SecretRefs before local web search execution", async () => {
2206+const rawConfig = {
2207+tools: { web: { search: { provider: "brave" } } },
2208+plugins: {
2209+entries: {
2210+tavily: {
2211+config: {
2212+webSearch: {
2213+apiKey: { source: "env", provider: "default", id: "TAVILY_API_KEY" },
2214+},
2215+},
2216+},
2217+},
2218+},
2219+};
2220+const resolvedConfig = {
2221+ ...rawConfig,
2222+tools: { web: { search: { provider: "tavily" } } },
2223+plugins: {
2224+entries: {
2225+tavily: { config: { webSearch: { apiKey: "resolved-tavily-key" } } },
2226+},
2227+},
2228+};
2229+const targetIds = new Set(["plugins.entries.tavily.config.webSearch.apiKey"]);
2230+const allowedPaths = new Set(["plugins.entries.tavily.config.webSearch.apiKey"]);
2231+mocks.loadConfig.mockReturnValue(rawConfig);
2232+mocks.getWebSearchCommandSecretTargets.mockReturnValue({
2233+ targetIds,
2234+ allowedPaths,
2235+});
2236+mocks.resolveCommandConfigWithSecrets.mockResolvedValueOnce({
2237+ resolvedConfig,
2238+effectiveConfig: resolvedConfig,
2239+diagnostics: [],
2240+} as never);
2241+const webSearchRuntime = await import("../web-search/runtime.js");
2242+vi.mocked(webSearchRuntime.runWebSearch).mockResolvedValueOnce({
2243+provider: "tavily",
2244+result: { results: [] },
2245+} as never);
2246+2247+await runRegisteredCli({
2248+register: registerCapabilityCli as (program: Command) => void,
2249+argv: ["capability", "web", "search", "--provider", "tavily", "--query", "ping", "--json"],
2250+});
2251+2252+expect(firstCommandConfigResolutionCall()).toEqual(
2253+expect.objectContaining({
2254+commandName: "infer web search",
2255+ targetIds,
2256+ allowedPaths,
2257+runtime: mocks.runtime,
2258+}),
2259+);
2260+expect(firstCommandConfigResolutionCall()?.config).toEqual(
2261+expect.objectContaining({
2262+tools: { web: { search: { provider: "tavily" } } },
2263+}),
2264+);
2265+expect(rawConfig.tools.web.search.provider).toBe("brave");
2266+expect(vi.mocked(webSearchRuntime.runWebSearch).mock.calls[0]?.[0]).toEqual(
2267+expect.objectContaining({
2268+config: resolvedConfig,
2269+preferInputConfig: true,
2270+providerId: "tavily",
2271+}),
2272+);
2273+});
2274+2275+it("resolves command SecretRefs before local web fetch execution", async () => {
2276+const rawConfig = {
2277+tools: { web: { fetch: { provider: "browser" } } },
2278+plugins: {
2279+entries: {
2280+firecrawl: {
2281+config: {
2282+webFetch: {
2283+apiKey: { source: "env", provider: "default", id: "FIRECRAWL_API_KEY" },
2284+},
2285+},
2286+},
2287+},
2288+},
2289+};
2290+const resolvedConfig = {
2291+ ...rawConfig,
2292+tools: { web: { fetch: { provider: "firecrawl" } } },
2293+plugins: {
2294+entries: {
2295+firecrawl: { config: { webFetch: { apiKey: "resolved-firecrawl-key" } } },
2296+},
2297+},
2298+};
2299+const targetIds = new Set(["plugins.entries.firecrawl.config.webFetch.apiKey"]);
2300+const allowedPaths = new Set(["plugins.entries.firecrawl.config.webFetch.apiKey"]);
2301+mocks.loadConfig.mockReturnValue(rawConfig);
2302+mocks.getWebFetchCommandSecretTargets.mockReturnValue({
2303+ targetIds,
2304+ allowedPaths,
2305+});
2306+mocks.resolveCommandConfigWithSecrets.mockResolvedValueOnce({
2307+ resolvedConfig,
2308+effectiveConfig: resolvedConfig,
2309+diagnostics: [],
2310+} as never);
2311+const webFetchRuntime = await import("../web-fetch/runtime.js");
2312+const execute = vi.fn(async () => ({ text: "ok" }));
2313+vi.mocked(webFetchRuntime.resolveWebFetchDefinition).mockReturnValueOnce({
2314+provider: { id: "firecrawl" },
2315+definition: { execute },
2316+} as never);
2317+2318+await runRegisteredCli({
2319+register: registerCapabilityCli as (program: Command) => void,
2320+argv: [
2321+"capability",
2322+"web",
2323+"fetch",
2324+"--provider",
2325+"firecrawl",
2326+"--url",
2327+"https://example.com",
2328+"--json",
2329+],
2330+});
2331+2332+expect(firstCommandConfigResolutionCall()).toEqual(
2333+expect.objectContaining({
2334+commandName: "infer web fetch",
2335+ targetIds,
2336+ allowedPaths,
2337+runtime: mocks.runtime,
2338+}),
2339+);
2340+expect(firstCommandConfigResolutionCall()?.config).toEqual(
2341+expect.objectContaining({
2342+tools: { web: { fetch: { provider: "firecrawl" } } },
2343+}),
2344+);
2345+expect(rawConfig.tools.web.fetch.provider).toBe("browser");
2346+expect(vi.mocked(webFetchRuntime.resolveWebFetchDefinition).mock.calls[0]?.[0]).toEqual(
2347+expect.objectContaining({
2348+config: resolvedConfig,
2349+providerId: "firecrawl",
2350+}),
2351+);
2352+expect(execute).toHaveBeenCalledWith({
2353+url: "https://example.com",
2354+format: undefined,
2355+});
2356+});
2357+21012358it("surfaces selected and configured embedding provider state", async () => {
21022359mocks.loadConfig.mockReturnValue({});
21032360mocks.resolveMemorySearchConfig.mockReturnValue({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。