4.1 KiB
Agent Note: Source dsh HTTP proxy overlay
Status: implemented
English | 中文
Problem
Machines whose ambient proxy is SOCKS can reach OpenRouter with curl and still fail pnpm dsh HTTPS fetches: Node, undici, and pi-ai accept http:// / https:// proxy URLs and reject socks5 / socks5h. Setting HTTPS_PROXY in the calling PowerShell or bash session also routes later shell commands through that HTTP proxy. Product .env files cannot supply HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, or NO_PROXY because those names are bootstrap-only. Assigning those names on process.env after Node has started does not rebind fetch: undici reads NODE_USE_ENV_PROXY and HTTPS_PROXY at process start. Node --env-file-if-exists also leaves inherited names in place, so a user-level HTTPS_PROXY=socks5h://… still wins.
Decision
The root dsh script runs scripts/run-source-dsh.ts as node --import tsx/esm scripts/run-source-dsh.ts. applySourceDshHttpProxy writes HTTP proxy variables onto a copy of process.env. When gitignored .dsh-http-proxy.env or DSH_HTTP_PROXY supplies an overlay and DSH_SOURCE_HTTP_PROXY_APPLIED is unset, the wrapper respawns the same Node argv with that environment so NODE_USE_ENV_PROXY=1 and the HTTP proxy URLs exist before fetch initializes. The marker prevents a second respawn. The calling shell is unchanged.
DSH_HTTP_PROXY, when set to a non-empty HTTP(S) URL, overwrites HTTP_PROXY / HTTPS_PROXY / ALL_PROXY (both cases) and sets NODE_USE_ENV_PROXY=1. When NO_PROXY and no_proxy are both unset, it sets them to localhost,127.0.0.1,::1. Otherwise the overlay file's parsed assignments are applied, then any remaining SOCKS value on those six names is replaced by the file's HTTP URL so an omitted ALL_PROXY cannot keep socks5h://. A socks5 or socks5h winning overlay fails before the CLI boots. Absent both sources, the inherited environment is unchanged and there is no respawn. The wrapper prints the applied HTTP URL once to stderr before respawn.
The installed apps/cli/lib/bin.js path is unchanged: it has no checkout overlay file.
Alternatives considered
Tell the user to export HTTPS_PROXY in the shell before pnpm dsh. That reaches OpenRouter, but the same variables remain on later commands in that session, including tools that should keep a SOCKS proxy.
Put proxy names in the invoking .env or $DSH_HOME/.env. Rejected by the configuration-source-ownership decision: bootstrap network variables may come only from the inherited process environment.
Assign NODE_USE_ENV_PROXY only inside the wrapper after Node has started. OpenRouter traffic uses fetch; Node binds the env-proxy dispatcher at process start, so a later process.env write does not change it.
Use Node --env-file-if-exists without respawning. That file does not replace names the parent already set, so an inherited socks5h:// HTTPS_PROXY still reaches fetch.
Consequences
Source pnpm dsh can use an HTTP overlay without mutating the calling shell. An inherited SOCKS HTTPS_PROXY is replaced in the respawned Node process, not in the shell. Contributors without DSH_HTTP_PROXY or .dsh-http-proxy.env see the previous inheritance behavior and no extra process. A SOCKS-only overlay fails loud instead of timing out against OpenRouter. The published bin still requires the caller to pass HTTP proxy variables when the host needs a proxy. --use-env-proxy is not on the command: Node 22.19 in CI rejects the flag.
Testing
scripts/apply-source-dsh-http-proxy.spec.ts covers no-overlay, DSH_HTTP_PROXY overwrite of SOCKS, file parse, file-versus-DSH_HTTP_PROXY precedence, SOCKS leftover on omitted names, SOCKS rejection, and the one-shot respawn marker. apps/cli/tests/source-launch.compat.spec.ts pins the root dsh command to scripts/run-source-dsh.ts and smokes that vector.