From d8a6c5c4a413af671c4a5ca785d9aad45cb40a2e Mon Sep 17 00:00:00 2001 From: qwen-autofix Date: Mon, 6 Jul 2026 15:27:06 +0000 Subject: [PATCH] fix(cli): use EnvHttpProxyAgent in channel proxy to respect NO_PROXY (#6401) The channel proxy path used ProxyAgent, which unconditionally routes all requests through the proxy and ignores NO_PROXY. This caused requests to hosts listed in NO_PROXY (e.g. localhost, internal IPs) to fail when a corporate proxy was configured. Switch to EnvHttpProxyAgent, matching the main CLI config path that already handles NO_PROXY correctly. --- packages/cli/src/commands/channel/proxy.ts | 6 ++-- .../cli/src/commands/channel/start.test.ts | 35 +++++++++++++------ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/commands/channel/proxy.ts b/packages/cli/src/commands/channel/proxy.ts index a22c2c95f4e..8122d35c73b 100644 --- a/packages/cli/src/commands/channel/proxy.ts +++ b/packages/cli/src/commands/channel/proxy.ts @@ -1,4 +1,4 @@ -import { ProxyAgent, setGlobalDispatcher } from 'undici'; +import { EnvHttpProxyAgent, setGlobalDispatcher } from 'undici'; import { normalizeProxyUrl } from '@qwen-code/qwen-code-core'; /** @@ -15,7 +15,9 @@ export function resolveProxy( ): string | undefined { const proxyUrl = resolveProxyUrl(cliProxy, settingsProxy); if (proxyUrl) { - setGlobalDispatcher(new ProxyAgent(proxyUrl)); + setGlobalDispatcher( + new EnvHttpProxyAgent({ httpProxy: proxyUrl, httpsProxy: proxyUrl }), + ); } return proxyUrl; } diff --git a/packages/cli/src/commands/channel/start.test.ts b/packages/cli/src/commands/channel/start.test.ts index 3ff0610b666..42d12358b34 100644 --- a/packages/cli/src/commands/channel/start.test.ts +++ b/packages/cli/src/commands/channel/start.test.ts @@ -4,8 +4,10 @@ import { pathToFileURL } from 'node:url'; import type { ChannelBaseOptions } from '@qwen-code/channel-base'; const mockSetGlobalDispatcher = vi.hoisted(() => vi.fn()); -const mockProxyAgent = vi.hoisted(() => - vi.fn((url: string) => ({ proxyUrl: url })), +const mockEnvHttpProxyAgent = vi.hoisted(() => + vi.fn((opts: { httpProxy: string; httpsProxy: string }) => ({ + proxyUrl: opts.httpProxy, + })), ); const mockNormalizeProxyUrl = vi.hoisted(() => vi.fn((url?: string) => url)); const mockStorageGetGlobalQwenDir = vi.hoisted(() => @@ -94,7 +96,7 @@ const mockSessionRouter = vi.hoisted(() => ); vi.mock('undici', () => ({ - ProxyAgent: mockProxyAgent, + EnvHttpProxyAgent: mockEnvHttpProxyAgent, setGlobalDispatcher: mockSetGlobalDispatcher, })); @@ -223,7 +225,10 @@ describe('resolveProxy', () => { ); expect(proxy).toBe('http://cli.example.com:8080'); - expect(mockProxyAgent).toHaveBeenCalledWith('http://cli.example.com:8080'); + expect(mockEnvHttpProxyAgent).toHaveBeenCalledWith({ + httpProxy: 'http://cli.example.com:8080', + httpsProxy: 'http://cli.example.com:8080', + }); expect(mockSetGlobalDispatcher).toHaveBeenCalledWith({ proxyUrl: 'http://cli.example.com:8080', }); @@ -235,9 +240,10 @@ describe('resolveProxy', () => { const proxy = resolveProxy(undefined, 'http://settings.example.com:8080'); expect(proxy).toBe('http://settings.example.com:8080'); - expect(mockProxyAgent).toHaveBeenCalledWith( - 'http://settings.example.com:8080', - ); + expect(mockEnvHttpProxyAgent).toHaveBeenCalledWith({ + httpProxy: 'http://settings.example.com:8080', + httpsProxy: 'http://settings.example.com:8080', + }); }); it('falls back to proxy environment variables', () => { @@ -246,7 +252,10 @@ describe('resolveProxy', () => { const proxy = resolveProxy(); expect(proxy).toBe('http://env.example.com:8080'); - expect(mockProxyAgent).toHaveBeenCalledWith('http://env.example.com:8080'); + expect(mockEnvHttpProxyAgent).toHaveBeenCalledWith({ + httpProxy: 'http://env.example.com:8080', + httpsProxy: 'http://env.example.com:8080', + }); }); }); @@ -310,8 +319,14 @@ describe('startCommand.handler', () => { } expect(mockLoadSettings).toHaveBeenCalledWith(process.cwd()); - expect(mockProxyAgent).toHaveBeenCalledWith(settingsProxy); - expect(mockProxyAgent).not.toHaveBeenCalledWith(envProxy); + expect(mockEnvHttpProxyAgent).toHaveBeenCalledWith({ + httpProxy: settingsProxy, + httpsProxy: settingsProxy, + }); + expect(mockEnvHttpProxyAgent).not.toHaveBeenCalledWith({ + httpProxy: envProxy, + httpsProxy: envProxy, + }); expect(mockCreateChannel).toHaveBeenCalledWith( 'telegram', mockParsedChannelConfig,