diff --git a/src/cli/run-main.ts b/src/cli/run-main.ts index b9cfd5c31..ca4cf1668 100644 --- a/src/cli/run-main.ts +++ b/src/cli/run-main.ts @@ -7,6 +7,7 @@ import { loadDotEnv } from "../infra/dotenv.js"; import { normalizeEnv } from "../infra/env.js"; import { isMainModule } from "../infra/is-main.js"; import { ensureOpenClawCliOnPath } from "../infra/path-env.js"; +import { configureGlobalProxy } from "../infra/proxy.js"; import { assertSupportedRuntime } from "../infra/runtime-guard.js"; import { formatUncaughtError } from "../infra/errors.js"; import { installUnhandledRejectionHandler } from "../infra/unhandled-rejections.js"; @@ -27,6 +28,7 @@ export async function runCli(argv: string[] = process.argv) { const normalizedArgv = stripWindowsNodeExec(argv); loadDotEnv({ quiet: true }); normalizeEnv(); + configureGlobalProxy(); ensureOpenClawCliOnPath(); // Enforce the minimum supported runtime before doing any work. diff --git a/src/infra/proxy.ts b/src/infra/proxy.ts new file mode 100644 index 000000000..9f02b4d6c --- /dev/null +++ b/src/infra/proxy.ts @@ -0,0 +1,41 @@ +import { setGlobalDispatcher, EnvHttpProxyAgent } from "undici"; +import { createSubsystemLogger } from "../logging/subsystem.js"; + +const log = createSubsystemLogger("infra/proxy"); + +/** + * Configures the global undici dispatcher to respect standard proxy environment variables: + * - HTTP_PROXY / http_proxy + * - HTTPS_PROXY / https_proxy + * - NO_PROXY / no_proxy + * + * This affects all global fetch() calls and undici-based requests. + */ +export function configureGlobalProxy(): void { + const hasProxyEnv = + process.env.HTTPS_PROXY || + process.env.https_proxy || + process.env.HTTP_PROXY || + process.env.http_proxy; + + if (!hasProxyEnv) { + return; + } + + try { + // EnvHttpProxyAgent is the industry-standard way in undici to handle env-based proxies. + // It automatically manages the logic for NO_PROXY and selecting the right proxy per protocol. + const agent = new EnvHttpProxyAgent(); + setGlobalDispatcher(agent); + + // We don't log the full proxy URL by default to avoid leaking potential credentials + // but we log that the proxy system is active. + log.debug("global proxy dispatcher initialized via environment variables"); + } catch (err) { + // If proxy configuration fails, we log it but don't crash the app. + // Standard behavior is to fallback to direct connection if possible. + log.error( + `failed to initialize global proxy dispatcher: ${err instanceof Error ? err.message : String(err)}`, + ); + } +}