Download packages/util/http-proxy/src/install.ts from SaylorTwift/deepseek-harness: direct link, hf CLI and curl.
- Browser
- Download file 14.9 kB
-
https://huggingface.co/SaylorTwift/deepseek-harness/resolve/main/packages/util/http-proxy/src/install.ts
- Command line
-
hf download hf://SaylorTwift/deepseek-harness/packages/util/http-proxy/src/install.ts
-
curl -L -o install.ts https://huggingface.co/SaylorTwift/deepseek-harness/resolve/main/packages/util/http-proxy/src/install.ts
14.9 kB
| /** | |
| * Proxy installation: the transport half of this package. It owns undici's global dispatcher and the | |
| * process-wide record of which policy is active. | |
| * | |
| * `undici` is imported dynamically so the pure {@link ProxyPolicy} half stays loadable where no Node | |
| * transport exists, matching how `dsh-web-fetch-http` defers its own transport import. | |
| * @module @deepseek-ai/dsh-http-proxy/install | |
| */ | |
| import type { Dispatcher, Pool } from 'undici' | |
| import { | |
| isSupportedProxyUrl, | |
| POLICY_ENV_NAMES, | |
| PROXY_ENV_NAMES, | |
| proxyForUrl, | |
| resolveProxyPolicy, | |
| type EnvLookup, | |
| type ProxyPolicy, | |
| } from './policy.ts' | |
| /** The active policy, or `undefined` until one is installed. Process-wide, like the dispatcher it tracks. */ | |
| let active: ProxyPolicy | undefined | |
| /** | |
| * The proxy environment as the user exported it, or `undefined` when no policy is installed. | |
| * | |
| * Owned by the OUTERMOST install: one layered over the launcher's would otherwise record the outer | |
| * policy's published values as if the user had written them, and | |
| * hand every child a normalization the user never asked for. | |
| * | |
| * {@link proxyEnvironmentForChild} keeps a value the user set rather than the one this process resolved from | |
| * it, so a SOCKS proxy `curl` can use is not replaced by an HTTP proxy named for another scheme. | |
| */ | |
| let inheritedProxyEnv: Readonly<Record<string, string | undefined>> | undefined | |
| /** The dispatcher installed with {@link active}, so a route can hand back the one already routing. */ | |
| let installed: Dispatcher | undefined | |
| /** | |
| * How this process must send one request. | |
| * | |
| * A caller that branches on the answer needs the transport that answer assumed, or an install or | |
| * disposal landing between the two would send the request somewhere the branch did not clear. The | |
| * proxied arm therefore carries the dispatcher already routing by this policy: it is process-wide | |
| * and long-lived, so a caller uses it and never closes it. Disposal closes that dispatcher rather | |
| * than destroying it, so a request already dispatched when a policy is unmounted still finishes. | |
| */ | |
| export type ProxyRoute = | |
| | { readonly proxied: true; readonly proxy: string; readonly dispatcher: Dispatcher } | |
| | { readonly proxied: false } | |
| /** A route that sends nothing through a proxy, shared because it carries no per-request state. */ | |
| const DIRECT_ROUTE: ProxyRoute = { proxied: false } | |
| /** | |
| * Decide how to send one request, and hand back the transport that decision assumed. | |
| * | |
| * @param url - the request URL. | |
| * @returns the proxied route with its proxy URL and dispatcher, or the direct route. | |
| */ | |
| export function proxyRouteFor(url: URL): ProxyRoute { | |
| const policy = active | |
| const dispatcher = installed | |
| if (policy === undefined || dispatcher === undefined) return DIRECT_ROUTE | |
| const proxy = proxyForUrl(policy, url) | |
| return proxy === undefined ? DIRECT_ROUTE : { proxied: true, proxy, dispatcher } | |
| } | |
| /** | |
| * Publish a policy through the proxy environment variables, which is how the consumers that read an | |
| * environment rather than a policy object — `node:http`'s `proxyEnv` and every spawned child — see | |
| * the one resolved answer, including the `ALL_PROXY` fallback and the merged loopback bypass that | |
| * neither derives on its own. The global dispatcher does not read these; it routes by the policy. | |
| * | |
| * @param policy - the policy to publish. | |
| * @returns a function restoring every name this call changed. | |
| */ | |
| function applyPolicyEnv(policy: ProxyPolicy): () => void { | |
| const previousInherited = inheritedProxyEnv | |
| inheritedProxyEnv = previousInherited ?? snapshotProxyEnv() | |
| const published: Record<string, string | undefined> = {} | |
| for (const [field, names] of Object.entries(POLICY_ENV_NAMES)) { | |
| const value = policy[field as keyof typeof POLICY_ENV_NAMES] | |
| for (const name of names) published[name] = value | |
| } | |
| const restore = writeProxyEnv(published) | |
| return () => { | |
| restore() | |
| inheritedProxyEnv = previousInherited | |
| } | |
| } | |
| /** | |
| * Read every proxy name this package publishes, as `process.env` holds it now. | |
| * | |
| * @returns one entry per name in {@link POLICY_ENV_NAMES}; `undefined` marks an absent name. | |
| */ | |
| function snapshotProxyEnv(): Record<string, string | undefined> { | |
| const snapshot: Record<string, string | undefined> = {} | |
| for (const names of Object.values(POLICY_ENV_NAMES)) { | |
| for (const name of names) snapshot[name] = process.env[name] | |
| } | |
| return snapshot | |
| } | |
| /** | |
| * Set every proxy name to the value `values` holds for it, removing a name whose value is `undefined`. | |
| * | |
| * @param values - the value each name in {@link POLICY_ENV_NAMES} should hold. | |
| * @returns a function restoring every name to what it held before this call. | |
| */ | |
| function writeProxyEnv(values: Readonly<Record<string, string | undefined>>): () => void { | |
| // Snapshot EVERY name before writing any of them. Windows folds environment names case-insensitively, | |
| // so reading the uppercase spelling after writing the lowercase one would read back the value just | |
| // written and restore the policy instead of the user's environment. | |
| const previous = snapshotProxyEnv() | |
| for (const name of Object.keys(previous)) { | |
| const value = values[name] | |
| if (value === undefined) Reflect.deleteProperty(process.env, name) | |
| else process.env[name] = value | |
| } | |
| return () => { | |
| for (const [name, value] of Object.entries(previous)) { | |
| if (value === undefined) Reflect.deleteProperty(process.env, name) | |
| else process.env[name] = value | |
| } | |
| } | |
| } | |
| /** | |
| * Build the global dispatcher for one policy. | |
| * | |
| * Routing runs through {@link proxyForUrl} per origin, so `fetch` and every caller that asks where a | |
| * URL goes read the same answer from the same matcher. undici's `EnvHttpProxyAgent` cannot express | |
| * this policy: with no `HTTPS_PROXY` present it reuses the HTTP proxy for `https:`, which would | |
| * tunnel a scheme this package deliberately keeps direct after refusing the SOCKS or malformed URL | |
| * the user named for it — the route and the diagnostic would then disagree. | |
| * | |
| * @param policy - the policy to route by; it must proxy at least one scheme. | |
| * @returns the dispatcher to install, owning every per-origin agent its factory created. | |
| */ | |
| async function createPolicyDispatcher(policy: ProxyPolicy): Promise<Dispatcher> { | |
| const { Agent, Pool, ProxyAgent } = await import('undici') | |
| return new Agent({ | |
| factory(origin, options) { | |
| // undici declares this parameter as `Object`, discarding the pool options it actually passes. | |
| const passed = options as Pool.Options | |
| const proxy = proxyForUrl(policy, new URL(origin.toString())) | |
| if (proxy !== undefined) return new ProxyAgent({ ...passed, uri: proxy }) | |
| // What undici's own default factory builds for these options, which `factory` replaces | |
| // wholesale. It reaches for a bare `Client` only at `connections: 1`, an option this | |
| // dispatcher never carries: it is constructed with undici's defaults. | |
| return new Pool(origin, passed) | |
| }, | |
| }) | |
| } | |
| /** | |
| * Route this process's outbound HTTP through `policy`. | |
| * | |
| * Installing replaces undici's global dispatcher, which is what Node's built-in `fetch` resolves, so | |
| * every caller that issues a plain `fetch()` is covered without knowing this package exists. A policy | |
| * that proxies nothing installs a direct dispatcher and leaves the environment untouched. | |
| * | |
| * A worker thread has its own `globalThis` and so its own dispatcher; installing here does not | |
| * reach it. No worker installs one today: both this repository ships — the workflow engine and the | |
| * PTC runtime — evaluate model-authored scripts, which must not receive a proxy URL that may carry | |
| * credentials. A worker that needs the policy has to be handed one explicitly and install it itself. | |
| * | |
| * @param policy - the resolved policy to install. | |
| * @returns a disposer restoring the previous dispatcher, policy, and environment, then closing the agent. | |
| */ | |
| async function installGlobalProxy(policy: ProxyPolicy): Promise<() => Promise<void>> { | |
| const previousPolicy = active | |
| if (policy.source === 'none') { | |
| // A direct policy mounted over an installed one must actually stop proxying. Recording the policy | |
| // alone would leave the previous agent as the global dispatcher, so a plain `fetch()` would keep | |
| // tunnelling while `proxyForUrl()` reported a direct connection — and `mode: 'off'` would be a | |
| // silent no-op. With nothing installed there is nothing to displace. | |
| if (previousPolicy === undefined) { | |
| active = policy | |
| return () => { | |
| active = previousPolicy | |
| return Promise.resolve() | |
| } | |
| } | |
| const previousInstalled = installed | |
| // The install underneath published its normalized policy into `process.env`, which is what a | |
| // spawned child copies. With no policy active there is no normalization to stand behind, so the | |
| // user's own values return for the window and the outer install's come back when it ends. An | |
| // install underneath that proxied nothing published nothing, and there is nothing to put back. | |
| const restoreEnv = inheritedProxyEnv === undefined ? undefined : writeProxyEnv(inheritedProxyEnv) | |
| const undici = await import('undici') | |
| const previous = undici.getGlobalDispatcher() | |
| const direct = new undici.Agent() | |
| undici.setGlobalDispatcher(direct) | |
| active = policy | |
| installed = undefined | |
| return async () => { | |
| undici.setGlobalDispatcher(previous) | |
| active = previousPolicy | |
| installed = previousInstalled | |
| restoreEnv?.() | |
| await direct.close() | |
| } | |
| } | |
| const restoreEnv = applyPolicyEnv(policy) | |
| const { getGlobalDispatcher, setGlobalDispatcher } = await import('undici') | |
| const previousDispatcher = getGlobalDispatcher() | |
| const previousInstalled = installed | |
| const agent = await createPolicyDispatcher(policy) | |
| setGlobalDispatcher(agent) | |
| active = policy | |
| installed = agent | |
| return async () => { | |
| setGlobalDispatcher(previousDispatcher) | |
| active = previousPolicy | |
| installed = previousInstalled | |
| restoreEnv() | |
| await agent.close() | |
| } | |
| } | |
| /** | |
| * The proxy environment a spawned child needs. | |
| * | |
| * A child inherits the parent environment, which this process rewrote to its own resolved policy. | |
| * Handing that normalization straight through would replace values the user set for other tools, so | |
| * each proxy name the user exported is restored to what they wrote: a SOCKS proxy `curl` uses is | |
| * not swapped for the HTTP one this package fell back to for that scheme. | |
| * | |
| * A scheme the user named in neither casing carries the resolved value instead of being removed. | |
| * Without that the child's routing silently diverges from its parent's: `NODE_USE_ENV_PROXY` does | |
| * not read `ALL_PROXY`, so a child of a parent that resolved its proxy from that name would connect | |
| * directly while the parent proxies. | |
| * | |
| * The bypass list is always the resolved one. It only ever adds the loopback entries to what | |
| * the user wrote, so nothing is lost, and the child stops sending its own localhost traffic to a | |
| * proxy that cannot route it. | |
| * | |
| * The flag reaches only Node 22.21+ and 24+; an older runtime keeps that child direct. Such a child | |
| * also matches bypass entries with Node's own `NO_PROXY` rules, which differ from this package's in | |
| * their separators and IPv4-range support. Non-Node children (curl, git, pnpm) ignore the flag and | |
| * read the variables themselves. | |
| * | |
| * The flag is withheld when a proxy value the child receives is one this package refused. Node | |
| * parses `HTTP_PROXY` and `HTTPS_PROXY` under that flag before running the program, and exits on a | |
| * scheme other than `http:` or `https:` — so a SOCKS value kept for `curl` would stop every Node | |
| * child from starting. Without the flag such a child connects directly, as this process already | |
| * reported for that scheme, and `curl` still reads the value it was kept for. | |
| * | |
| * A worker thread is deliberately NOT served here — see the workflow engine, which runs | |
| * model-authored scripts and must not receive a proxy URL that may carry credentials. | |
| * | |
| * @returns names to apply to the child environment, where `undefined` means remove, or an empty | |
| * object when no proxy is active. | |
| */ | |
| export function proxyEnvironmentForChild(): Readonly<Record<string, string | undefined>> { | |
| const policy = active | |
| const inherited = inheritedProxyEnv | |
| if (policy === undefined || policy.source === 'none' || inherited === undefined) return {} | |
| const overlay: Record<string, string | undefined> = { NODE_USE_ENV_PROXY: '1' } | |
| for (const [field, names] of Object.entries(POLICY_ENV_NAMES)) { | |
| const resolved = policy[field as keyof typeof POLICY_ENV_NAMES] | |
| // Naming a scheme in either casing claims that scheme: the child then gets exactly what the | |
| // user wrote, in the casing they wrote it, rather than a value derived for this process. | |
| const named = field !== 'noProxy' && names.some(name => inherited[name] !== undefined) | |
| for (const name of names) overlay[name] = named ? inherited[name] : resolved | |
| } | |
| const parsedByNode = [...POLICY_ENV_NAMES.httpProxy, ...POLICY_ENV_NAMES.httpsProxy] | |
| if (parsedByNode.some(name => overlay[name] !== undefined && !isSupportedProxyUrl(overlay[name]))) { | |
| delete overlay.NODE_USE_ENV_PROXY | |
| } | |
| return overlay | |
| } | |
| /** | |
| * Resolve this process's proxy policy from `env` and install it. | |
| * | |
| * Resolution, reporting, and installation are one operation because no caller needs them apart: the | |
| * launcher does all three in sequence before the first plugin mounts, and a policy resolved but not | |
| * installed routes nothing. | |
| * | |
| * A value the environment supplies but this package cannot use is reported and skipped rather than | |
| * thrown: the variable may have been exported for another tool, and a proxy the harness cannot use | |
| * must not stop the agent from starting. | |
| * | |
| * @param env - the launch environment, whose own layering already prefers real variables over `.env` files. | |
| * @param report - receives one message per rejected value, in the order the values were considered. | |
| * @returns a disposer restoring the previous dispatcher, policy, and environment. | |
| */ | |
| export async function installProxyFromEnvironment( | |
| env: EnvLookup, | |
| report: (message: string) => void, | |
| ): Promise<() => Promise<void>> { | |
| const { policy, diagnostics } = resolveProxyPolicy(env) | |
| for (const diagnostic of diagnostics) report(diagnostic.message) | |
| return await installGlobalProxy(policy) | |
| } | |
| /** | |
| * The environment overlay that removes every proxy name from a spawned child. | |
| * | |
| * A harness that replays a recorded session must reach its own fixture server, not the proxy a | |
| * developer or a CI runner exported; `undefined` is how a spawn removes a name it inherits. | |
| * | |
| * @returns one entry per proxy name, each `undefined`. | |
| */ | |
| export function clearedProxyEnv(): Record<string, undefined> { | |
| return Object.fromEntries(PROXY_ENV_NAMES.map(name => [name, undefined])) | |
| } | |