























@@ -11,6 +11,16 @@ import type { agentCommand as AgentCommand } from "./agent.js";
11111212const loadConfig = vi.hoisted(() => vi.fn());
1313const callGateway = vi.hoisted(() => vi.fn());
14+const isGatewayCredentialsRequiredError = vi.hoisted(() =>
15+vi.fn(
16+(value: unknown) => value instanceof Error && value.name === "GatewayCredentialsRequiredError",
17+),
18+);
19+const isGatewayExplicitAuthRequiredError = vi.hoisted(() =>
20+vi.fn(
21+(value: unknown) => value instanceof Error && value.name === "GatewayExplicitAuthRequiredError",
22+),
23+);
1424const isGatewayTransportError = vi.hoisted(() =>
1525vi.fn((value: unknown) => {
1626if (!(value instanceof Error) || value.name !== "GatewayTransportError") {
@@ -210,6 +220,8 @@ function createGatewayNormalCloseError() {
210220vi.mock("../config/io.js", () => ({ getRuntimeConfig: loadConfig, loadConfig }));
211221vi.mock("../gateway/call.js", () => ({
212222 callGateway,
223+ isGatewayCredentialsRequiredError,
224+ isGatewayExplicitAuthRequiredError,
213225 isGatewayTransportError,
214226randomIdempotencyKey: () => "idem-1",
215227}));
@@ -332,12 +344,74 @@ describe("agentCliCommand", () => {
332344expect(params.sessionId).toBeUndefined();
333345expect(params.to).toBeUndefined();
334346expect(request.config).toBe(loadConfig.mock.results[0]?.value);
335-expect(loadConfig).toHaveBeenCalledWith({ skipPluginValidation: true, pin: false });
347+expect(loadConfig).toHaveBeenCalledWith({
348+skipPluginValidation: true,
349+pin: false,
350+skipShellEnvFallback: true,
351+});
336352expect(agentCommand).not.toHaveBeenCalled();
337353expect(loadAgentSessionModuleMock).not.toHaveBeenCalled();
338354});
339355});
340356357+it("retries gateway dispatch with shell env fallback only when credentials need it", async () => {
358+await withTempStore(async ({ store }) => {
359+const fastConfig = {
360+agents: { defaults: { timeoutSeconds: 600 } },
361+session: { store, mainKey: "main" },
362+};
363+const shellEnvConfig = {
364+ ...fastConfig,
365+gateway: { auth: { mode: "token" as const } },
366+};
367+loadConfig.mockReset();
368+loadConfig.mockReturnValueOnce(fastConfig);
369+loadConfig.mockReturnValueOnce(shellEnvConfig);
370+const authError = new Error("gateway agent requires credentials");
371+authError.name = "GatewayCredentialsRequiredError";
372+callGateway.mockRejectedValueOnce(authError);
373+mockGatewaySuccessReply();
374+375+await agentCliCommand({ message: "hi", sessionKey: "agent:main:incident-42" }, runtime);
376+377+expect(loadConfig.mock.calls).toEqual([
378+[{ skipPluginValidation: true, pin: false, skipShellEnvFallback: true }],
379+[{ skipPluginValidation: true, pin: false, skipShellEnvFallback: false }],
380+]);
381+expect(callGateway).toHaveBeenCalledTimes(2);
382+expect(requireRecord(callGateway.mock.calls[0]?.[0], "first gateway request").config).toBe(
383+fastConfig,
384+);
385+expect(requireRecord(callGateway.mock.calls[1]?.[0], "second gateway request").config).toBe(
386+shellEnvConfig,
387+);
388+});
389+});
390+391+it("retries gateway dispatch with shell env fallback for env URL auth", async () => {
392+await withTempStore(async ({ store }) => {
393+const fastConfig = {
394+agents: { defaults: { timeoutSeconds: 600 } },
395+session: { store, mainKey: "main" },
396+};
397+loadConfig.mockReset();
398+loadConfig.mockReturnValueOnce(fastConfig);
399+loadConfig.mockReturnValueOnce(fastConfig);
400+const authError = new Error("gateway url override requires explicit credentials");
401+authError.name = "GatewayExplicitAuthRequiredError";
402+callGateway.mockRejectedValueOnce(authError);
403+mockGatewaySuccessReply();
404+405+await agentCliCommand({ message: "hi", sessionKey: "agent:main:incident-42" }, runtime);
406+407+expect(loadConfig.mock.calls).toEqual([
408+[{ skipPluginValidation: true, pin: false, skipShellEnvFallback: true }],
409+[{ skipPluginValidation: true, pin: false, skipShellEnvFallback: false }],
410+]);
411+expect(callGateway).toHaveBeenCalledTimes(2);
412+});
413+});
414+341415it("scopes legacy explicit session keys to the requested agent", async () => {
342416await withTempStore(
343417async () => {
@@ -1486,8 +1560,8 @@ describe("agentCliCommand", () => {
14861560expect(fallbackOpts.sessionId).toMatch(/^gateway-fallback-/);
14871561expect(fallbackOpts.sessionKey).toBe(`agent:ops:explicit:${fallbackOpts.sessionId}`);
14881562expect(loadConfig.mock.calls).toEqual([
1489-[{ skipPluginValidation: true, pin: false }],
1490-[{ skipPluginValidation: true, pin: false }],
1563+[{ skipPluginValidation: true, pin: false, skipShellEnvFallback: true }],
1564+[{ skipPluginValidation: true, pin: false, skipShellEnvFallback: true }],
14911565]);
14921566},
14931567{ agents: { list: [{ id: "ops", default: true }, { id: "main" }] } },
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。