From b05bed25f935658a2aac3cfbafd5864818461e66 Mon Sep 17 00:00:00 2001 From: truffle Date: Fri, 24 Apr 2026 05:11:56 +0000 Subject: [PATCH 1/4] fix(vscode): forward VS Code http.proxy settings to spawned CLI process VS Code's http.proxy and http.noProxy settings are not reflected in process.env, so the CLI server spawned by ServerManager inherits no proxy configuration. Users behind a corporate proxy see silent auth failures on the Providers tab and all LLM calls bypass the proxy. Translate the settings via a small buildProxyEnv() helper and spread them into the spawn env after process.env (so the VS Code setting wins when both are present). The standard HTTP_PROXY / HTTPS_PROXY / NO_PROXY env vars are already honored by Bun's fetch and by most HTTP clients the CLI reaches for. Fixes #8213. --- .../services/cli-backend/server-manager.ts | 27 ++++++ .../unit/server-manager-proxy-env.test.ts | 83 +++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts diff --git a/packages/kilo-vscode/src/services/cli-backend/server-manager.ts b/packages/kilo-vscode/src/services/cli-backend/server-manager.ts index e63488e8b20..dee94534129 100644 --- a/packages/kilo-vscode/src/services/cli-backend/server-manager.ts +++ b/packages/kilo-vscode/src/services/cli-backend/server-manager.ts @@ -73,6 +73,12 @@ export class ServerManager { cwd: spawnCwd, env: { ...process.env, + // VS Code's http.proxy / http.noProxy settings are not reflected in + // process.env, so spawned children bypass the user's configured proxy + // and fail behind corporate firewalls. Forward them as the standard + // HTTP_PROXY / HTTPS_PROXY / NO_PROXY env vars that Bun's fetch and + // most HTTP clients already respect. + ...buildProxyEnv(), // Force mimalloc (the allocator Bun ships with) to return freed pages // to the OS immediately instead of retaining them in its arenas. // Without this, Bun.spawn's piped stdio accumulates ~2 MB of native @@ -227,6 +233,27 @@ function stripAnsi(str: string): string { return str.replace(/\x1b\[[0-9;]*m/g, "") } +/** + * Translate VS Code's `http.proxy` / `http.noProxy` settings into the standard + * HTTP_PROXY / HTTPS_PROXY / NO_PROXY env vars, so the spawned CLI (and any + * HTTP client it uses) honors the user's proxy configuration. Returns an empty + * object when no proxy is configured, so callers can spread unconditionally. + */ +export function buildProxyEnv(): Record { + const httpConfig = vscode.workspace.getConfiguration("http") + const proxy = httpConfig.get("proxy") + const noProxy = httpConfig.get("noProxy") + const env: Record = {} + if (proxy && proxy.trim() !== "") { + env.HTTP_PROXY = proxy + env.HTTPS_PROXY = proxy + } + if (Array.isArray(noProxy) && noProxy.length > 0) { + env.NO_PROXY = noProxy.join(",") + } + return env +} + export function toErrorMessage( error: string, stderrLines: string[], diff --git a/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts b/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts new file mode 100644 index 00000000000..64e426a85fc --- /dev/null +++ b/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts @@ -0,0 +1,83 @@ +import { describe, it, expect, afterEach } from "bun:test" +import * as vscode from "vscode" +import { buildProxyEnv } from "../../src/services/cli-backend/server-manager" + +type WorkspaceStub = { getConfiguration: (section?: string) => { get: (key: string) => unknown } } + +const workspace = vscode.workspace as unknown as WorkspaceStub +const originalGetConfiguration = workspace.getConfiguration + +function stubHttpConfig(values: { proxy?: unknown; noProxy?: unknown }): void { + workspace.getConfiguration = (section?: string) => { + if (section === "http") { + return { + get: (key: string) => { + if (key === "proxy") return values.proxy + if (key === "noProxy") return values.noProxy + return undefined + }, + } + } + return { get: () => undefined } + } +} + +afterEach(() => { + workspace.getConfiguration = originalGetConfiguration +}) + +describe("buildProxyEnv", () => { + it("returns an empty object when neither proxy nor noProxy is configured", () => { + stubHttpConfig({ proxy: undefined, noProxy: undefined }) + + expect(buildProxyEnv()).toEqual({}) + }) + + it("forwards http.proxy as HTTP_PROXY and HTTPS_PROXY", () => { + stubHttpConfig({ proxy: "http://proxy.corp.example:8080" }) + + expect(buildProxyEnv()).toEqual({ + HTTP_PROXY: "http://proxy.corp.example:8080", + HTTPS_PROXY: "http://proxy.corp.example:8080", + }) + }) + + it("joins http.noProxy into a comma-separated NO_PROXY value", () => { + stubHttpConfig({ noProxy: ["localhost", "127.0.0.1", "*.internal"] }) + + expect(buildProxyEnv()).toEqual({ + NO_PROXY: "localhost,127.0.0.1,*.internal", + }) + }) + + it("forwards both proxy and noProxy when both are configured", () => { + stubHttpConfig({ + proxy: "http://proxy.corp.example:8080", + noProxy: ["localhost", "*.internal"], + }) + + expect(buildProxyEnv()).toEqual({ + HTTP_PROXY: "http://proxy.corp.example:8080", + HTTPS_PROXY: "http://proxy.corp.example:8080", + NO_PROXY: "localhost,*.internal", + }) + }) + + it("ignores an http.proxy that is only whitespace", () => { + stubHttpConfig({ proxy: " " }) + + expect(buildProxyEnv()).toEqual({}) + }) + + it("ignores an empty http.noProxy array", () => { + stubHttpConfig({ noProxy: [] }) + + expect(buildProxyEnv()).toEqual({}) + }) + + it("ignores a non-array http.noProxy value", () => { + stubHttpConfig({ noProxy: "localhost" }) + + expect(buildProxyEnv()).toEqual({}) + }) +}) From 778e9475ce7472d88a921935c5f368a9b8d69f20 Mon Sep 17 00:00:00 2001 From: truffle Date: Fri, 24 Apr 2026 07:04:45 +0000 Subject: [PATCH 2/4] fix(vscode): honor http.proxySupport=off by clearing env vars VS Code's `http.proxySupport: "off"` is the documented opt-in to disable proxy support entirely. The previous patch returned an empty object in that case, so the spawned child still inherited ambient shell HTTP_PROXY. Explicitly set HTTP_PROXY/HTTPS_PROXY/NO_PROXY to "" when proxySupport is off, and short-circuit before reading `http.proxy` / `http.noProxy` so the disable setting wins. Two regression tests. --- .../services/cli-backend/server-manager.ts | 18 ++++++++--- .../unit/server-manager-proxy-env.test.ts | 31 ++++++++++++++++++- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/packages/kilo-vscode/src/services/cli-backend/server-manager.ts b/packages/kilo-vscode/src/services/cli-backend/server-manager.ts index dee94534129..cb4df387277 100644 --- a/packages/kilo-vscode/src/services/cli-backend/server-manager.ts +++ b/packages/kilo-vscode/src/services/cli-backend/server-manager.ts @@ -234,15 +234,25 @@ function stripAnsi(str: string): string { } /** - * Translate VS Code's `http.proxy` / `http.noProxy` settings into the standard - * HTTP_PROXY / HTTPS_PROXY / NO_PROXY env vars, so the spawned CLI (and any - * HTTP client it uses) honors the user's proxy configuration. Returns an empty - * object when no proxy is configured, so callers can spread unconditionally. + * Translate VS Code's `http.proxy` / `http.noProxy` / `http.proxySupport` + * settings into the standard HTTP_PROXY / HTTPS_PROXY / NO_PROXY env vars, so + * the spawned CLI honors the user's proxy configuration. Returns an empty + * object when no override is needed, so callers can spread unconditionally. + * + * `http.proxySupport: "off"` is VS Code's opt-in way to disable proxy support + * entirely; when set, we explicitly clear the env vars so ambient shell + * HTTP_PROXY doesn't leak into the spawned child. */ export function buildProxyEnv(): Record { const httpConfig = vscode.workspace.getConfiguration("http") const proxy = httpConfig.get("proxy") const noProxy = httpConfig.get("noProxy") + const proxySupport = httpConfig.get("proxySupport") + + if (proxySupport === "off") { + return { HTTP_PROXY: "", HTTPS_PROXY: "", NO_PROXY: "" } + } + const env: Record = {} if (proxy && proxy.trim() !== "") { env.HTTP_PROXY = proxy diff --git a/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts b/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts index 64e426a85fc..2c64450512d 100644 --- a/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts +++ b/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts @@ -7,13 +7,18 @@ type WorkspaceStub = { getConfiguration: (section?: string) => { get: (key: stri const workspace = vscode.workspace as unknown as WorkspaceStub const originalGetConfiguration = workspace.getConfiguration -function stubHttpConfig(values: { proxy?: unknown; noProxy?: unknown }): void { +function stubHttpConfig(values: { + proxy?: unknown + noProxy?: unknown + proxySupport?: unknown +}): void { workspace.getConfiguration = (section?: string) => { if (section === "http") { return { get: (key: string) => { if (key === "proxy") return values.proxy if (key === "noProxy") return values.noProxy + if (key === "proxySupport") return values.proxySupport return undefined }, } @@ -80,4 +85,28 @@ describe("buildProxyEnv", () => { expect(buildProxyEnv()).toEqual({}) }) + + it("explicitly clears env vars when http.proxySupport is off", () => { + stubHttpConfig({ proxySupport: "off" }) + + expect(buildProxyEnv()).toEqual({ + HTTP_PROXY: "", + HTTPS_PROXY: "", + NO_PROXY: "", + }) + }) + + it("http.proxySupport=off wins over a configured http.proxy/http.noProxy", () => { + stubHttpConfig({ + proxy: "http://proxy.corp.example:8080", + noProxy: ["localhost"], + proxySupport: "off", + }) + + expect(buildProxyEnv()).toEqual({ + HTTP_PROXY: "", + HTTPS_PROXY: "", + NO_PROXY: "", + }) + }) }) From 417401703c60fd44bfb7ae101f462569b3306da6 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Wed, 6 May 2026 16:19:06 +0300 Subject: [PATCH 3/4] fix(vscode): clear proxy env --- .../services/cli-backend/server-manager.ts | 33 +++++++++++++++++-- .../unit/server-manager-proxy-env.test.ts | 33 ++++++++++++------- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/packages/kilo-vscode/src/services/cli-backend/server-manager.ts b/packages/kilo-vscode/src/services/cli-backend/server-manager.ts index cb4df387277..017ca4942f9 100644 --- a/packages/kilo-vscode/src/services/cli-backend/server-manager.ts +++ b/packages/kilo-vscode/src/services/cli-backend/server-manager.ts @@ -245,22 +245,51 @@ function stripAnsi(str: string): string { */ export function buildProxyEnv(): Record { const httpConfig = vscode.workspace.getConfiguration("http") - const proxy = httpConfig.get("proxy") - const noProxy = httpConfig.get("noProxy") + const proxyInfo = httpConfig.inspect("proxy") + const noProxyInfo = httpConfig.inspect("noProxy") const proxySupport = httpConfig.get("proxySupport") if (proxySupport === "off") { return { HTTP_PROXY: "", HTTPS_PROXY: "", NO_PROXY: "" } } + const proxy = httpConfig.get("proxy") + const noProxy = httpConfig.get("noProxy") + const proxySet = + proxyInfo !== undefined && + [ + proxyInfo.globalValue, + proxyInfo.workspaceValue, + proxyInfo.workspaceFolderValue, + proxyInfo.globalLanguageValue, + proxyInfo.workspaceLanguageValue, + proxyInfo.workspaceFolderLanguageValue, + ].some((value) => value !== undefined) + const noProxySet = + noProxyInfo !== undefined && + [ + noProxyInfo.globalValue, + noProxyInfo.workspaceValue, + noProxyInfo.workspaceFolderValue, + noProxyInfo.globalLanguageValue, + noProxyInfo.workspaceLanguageValue, + noProxyInfo.workspaceFolderLanguageValue, + ].some((value) => value !== undefined) const env: Record = {} if (proxy && proxy.trim() !== "") { env.HTTP_PROXY = proxy env.HTTPS_PROXY = proxy } + if (proxySet && proxy !== undefined && proxy.trim() === "") { + env.HTTP_PROXY = "" + env.HTTPS_PROXY = "" + } if (Array.isArray(noProxy) && noProxy.length > 0) { env.NO_PROXY = noProxy.join(",") } + if (noProxySet && Array.isArray(noProxy) && noProxy.length === 0) { + env.NO_PROXY = "" + } return env } diff --git a/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts b/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts index 2c64450512d..5ed8ea3b5aa 100644 --- a/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts +++ b/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts @@ -2,16 +2,15 @@ import { describe, it, expect, afterEach } from "bun:test" import * as vscode from "vscode" import { buildProxyEnv } from "../../src/services/cli-backend/server-manager" -type WorkspaceStub = { getConfiguration: (section?: string) => { get: (key: string) => unknown } } +type Info = { globalValue?: unknown; workspaceValue?: unknown; workspaceFolderValue?: unknown } +type WorkspaceStub = { + getConfiguration: (section?: string) => { get: (key: string) => unknown; inspect: (key: string) => Info } +} const workspace = vscode.workspace as unknown as WorkspaceStub const originalGetConfiguration = workspace.getConfiguration -function stubHttpConfig(values: { - proxy?: unknown - noProxy?: unknown - proxySupport?: unknown -}): void { +function stubHttpConfig(values: { proxy?: unknown; noProxy?: unknown; proxySupport?: unknown }): void { workspace.getConfiguration = (section?: string) => { if (section === "http") { return { @@ -21,9 +20,16 @@ function stubHttpConfig(values: { if (key === "proxySupport") return values.proxySupport return undefined }, + inspect: (key: string) => { + if (key === "proxy" && values.proxy !== undefined) return { workspaceValue: values.proxy } + if (key === "noProxy" && values.noProxy !== undefined) return { workspaceValue: values.noProxy } + if (key === "proxySupport" && values.proxySupport !== undefined) + return { workspaceValue: values.proxySupport } + return {} + }, } } - return { get: () => undefined } + return { get: () => undefined, inspect: () => ({}) } } } @@ -68,16 +74,21 @@ describe("buildProxyEnv", () => { }) }) - it("ignores an http.proxy that is only whitespace", () => { + it("clears env vars when http.proxy is only whitespace", () => { stubHttpConfig({ proxy: " " }) - expect(buildProxyEnv()).toEqual({}) + expect(buildProxyEnv()).toEqual({ + HTTP_PROXY: "", + HTTPS_PROXY: "", + }) }) - it("ignores an empty http.noProxy array", () => { + it("clears env var when http.noProxy is an empty array", () => { stubHttpConfig({ noProxy: [] }) - expect(buildProxyEnv()).toEqual({}) + expect(buildProxyEnv()).toEqual({ + NO_PROXY: "", + }) }) it("ignores a non-array http.noProxy value", () => { From 8cde26d7be0b13efb95c415885b8781bfaa1f6f4 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Wed, 6 May 2026 16:41:57 +0300 Subject: [PATCH 4/4] fix(vscode): clear lowercase proxy --- .../src/services/cli-backend/server-manager.ts | 16 +++++++++++----- .../tests/unit/server-manager-proxy-env.test.ts | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/kilo-vscode/src/services/cli-backend/server-manager.ts b/packages/kilo-vscode/src/services/cli-backend/server-manager.ts index 017ca4942f9..1151adeb8c2 100644 --- a/packages/kilo-vscode/src/services/cli-backend/server-manager.ts +++ b/packages/kilo-vscode/src/services/cli-backend/server-manager.ts @@ -235,13 +235,13 @@ function stripAnsi(str: string): string { /** * Translate VS Code's `http.proxy` / `http.noProxy` / `http.proxySupport` - * settings into the standard HTTP_PROXY / HTTPS_PROXY / NO_PROXY env vars, so - * the spawned CLI honors the user's proxy configuration. Returns an empty - * object when no override is needed, so callers can spread unconditionally. + * settings into the standard proxy env vars, so the spawned CLI honors the + * user's proxy configuration. Returns an empty object when no override is + * needed, so callers can spread unconditionally. * * `http.proxySupport: "off"` is VS Code's opt-in way to disable proxy support * entirely; when set, we explicitly clear the env vars so ambient shell - * HTTP_PROXY doesn't leak into the spawned child. + * HTTP_PROXY/http_proxy doesn't leak into the spawned child. */ export function buildProxyEnv(): Record { const httpConfig = vscode.workspace.getConfiguration("http") @@ -250,7 +250,7 @@ export function buildProxyEnv(): Record { const proxySupport = httpConfig.get("proxySupport") if (proxySupport === "off") { - return { HTTP_PROXY: "", HTTPS_PROXY: "", NO_PROXY: "" } + return { HTTP_PROXY: "", HTTPS_PROXY: "", NO_PROXY: "", http_proxy: "", https_proxy: "", no_proxy: "" } } const proxy = httpConfig.get("proxy") @@ -279,16 +279,22 @@ export function buildProxyEnv(): Record { if (proxy && proxy.trim() !== "") { env.HTTP_PROXY = proxy env.HTTPS_PROXY = proxy + env.http_proxy = proxy + env.https_proxy = proxy } if (proxySet && proxy !== undefined && proxy.trim() === "") { env.HTTP_PROXY = "" env.HTTPS_PROXY = "" + env.http_proxy = "" + env.https_proxy = "" } if (Array.isArray(noProxy) && noProxy.length > 0) { env.NO_PROXY = noProxy.join(",") + env.no_proxy = noProxy.join(",") } if (noProxySet && Array.isArray(noProxy) && noProxy.length === 0) { env.NO_PROXY = "" + env.no_proxy = "" } return env } diff --git a/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts b/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts index 5ed8ea3b5aa..6aac3ae2bcb 100644 --- a/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts +++ b/packages/kilo-vscode/tests/unit/server-manager-proxy-env.test.ts @@ -50,6 +50,8 @@ describe("buildProxyEnv", () => { expect(buildProxyEnv()).toEqual({ HTTP_PROXY: "http://proxy.corp.example:8080", HTTPS_PROXY: "http://proxy.corp.example:8080", + http_proxy: "http://proxy.corp.example:8080", + https_proxy: "http://proxy.corp.example:8080", }) }) @@ -58,6 +60,7 @@ describe("buildProxyEnv", () => { expect(buildProxyEnv()).toEqual({ NO_PROXY: "localhost,127.0.0.1,*.internal", + no_proxy: "localhost,127.0.0.1,*.internal", }) }) @@ -71,6 +74,9 @@ describe("buildProxyEnv", () => { HTTP_PROXY: "http://proxy.corp.example:8080", HTTPS_PROXY: "http://proxy.corp.example:8080", NO_PROXY: "localhost,*.internal", + http_proxy: "http://proxy.corp.example:8080", + https_proxy: "http://proxy.corp.example:8080", + no_proxy: "localhost,*.internal", }) }) @@ -80,6 +86,8 @@ describe("buildProxyEnv", () => { expect(buildProxyEnv()).toEqual({ HTTP_PROXY: "", HTTPS_PROXY: "", + http_proxy: "", + https_proxy: "", }) }) @@ -88,6 +96,7 @@ describe("buildProxyEnv", () => { expect(buildProxyEnv()).toEqual({ NO_PROXY: "", + no_proxy: "", }) }) @@ -104,6 +113,9 @@ describe("buildProxyEnv", () => { HTTP_PROXY: "", HTTPS_PROXY: "", NO_PROXY: "", + http_proxy: "", + https_proxy: "", + no_proxy: "", }) }) @@ -118,6 +130,9 @@ describe("buildProxyEnv", () => { HTTP_PROXY: "", HTTPS_PROXY: "", NO_PROXY: "", + http_proxy: "", + https_proxy: "", + no_proxy: "", }) }) })