Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
"open": "^11.0.0",
"semver": "7.8.5",
"smol-toml": "^1.6.1",
"undici": "^8.5.0",
"yaml": "^2.9.0",
"zod": "^4.4.3"
},
Expand Down
4 changes: 4 additions & 0 deletions packages/core-internal/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,9 @@ export * from "./shared/debug-log.js";
export * from "./shared/fetch-timeout.js";
export * from "./shared/pkgseer-graphql.js";
export * from "./shared/pkgseer-registry.js";
export {
configureProxyAwareFetch,
hasProxyEnv,
} from "./shared/proxy-fetch.js";
export * from "./shared/request-headers.js";
export * from "./shared/telemetry.js";
43 changes: 43 additions & 0 deletions packages/core-internal/src/shared/proxy-fetch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { describe, expect, it } from "bun:test";
import { configureProxyAwareFetch, hasProxyEnv } from "./proxy-fetch.js";

describe("hasProxyEnv", () => {
it("returns false when no proxy variables are set", () => {
const env = {};
expect(hasProxyEnv(env)).toBe(false);
});

it("returns true when HTTPS_PROXY is set", () => {
const env = { HTTPS_PROXY: "http://proxy.example.com:8080" };
expect(hasProxyEnv(env)).toBe(true);
});

it("returns true when https_proxy is set", () => {
const env = { https_proxy: "http://proxy.example.com:8080" };
expect(hasProxyEnv(env)).toBe(true);
});

it("returns true when HTTP_PROXY is set", () => {
const env = { HTTP_PROXY: "http://proxy.example.com:8080" };
expect(hasProxyEnv(env)).toBe(true);
});

it("returns true when http_proxy is set", () => {
const env = { http_proxy: "http://proxy.example.com:8080" };
expect(hasProxyEnv(env)).toBe(true);
});
});

describe("configureProxyAwareFetch", () => {
it("does not throw when proxy variables are set", () => {
expect(() =>
configureProxyAwareFetch({
HTTPS_PROXY: "http://proxy.example.com:8080",
}),
).not.toThrow();
});

it("does not throw when no proxy variables are set", () => {
expect(() => configureProxyAwareFetch({})).not.toThrow();
});
});
47 changes: 47 additions & 0 deletions packages/core-internal/src/shared/proxy-fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Proxy-aware fetch setup.
*
* Node.js native `fetch` ignores `HTTP_PROXY`/`HTTPS_PROXY` by default. This
* module detects the standard proxy environment variables and, when present,
* installs an `undici` dispatcher so that all subsequent native fetch calls
* route through the proxy.
*
* Supports `NO_PROXY` for selective opt-out, matching common corporate proxy
* conventions.
*/
import { EnvHttpProxyAgent, setGlobalDispatcher } from "undici";

let proxyFetchConfigured = false;

/**
* Returns true when at least one of the standard proxy environment variables
* is set. Checks lower- and upper-case variants, matching curl/git convention.
*/
export function hasProxyEnv(env: NodeJS.ProcessEnv = process.env): boolean {
return Boolean(
env.HTTP_PROXY || env.http_proxy || env.HTTPS_PROXY || env.https_proxy,
);
}

/**
* Configures Node.js native fetch to honor `HTTP_PROXY`/`HTTPS_PROXY` via
* undici's `EnvHttpProxyAgent`. Safe to call multiple times; subsequent calls
* are no-ops.
*
* This should run as early as possible in the process lifetime, before any
* network request is issued.
*/
export function configureProxyAwareFetch(
env: NodeJS.ProcessEnv = process.env,
): void {
if (proxyFetchConfigured) {
return;
}
if (!hasProxyEnv(env)) {
return;
}

const agent = new EnvHttpProxyAgent();
setGlobalDispatcher(agent);
proxyFetchConfigured = true;
}
6 changes: 6 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
#!/usr/bin/env node
import {
configureProxyAwareFetch,
endTelemetrySpan,
flushTelemetry,
isTelemetryEnabled,
startTelemetrySpan,
withTelemetrySpan,
} from "@githits/core-internal";

// Honor HTTP_PROXY/HTTPS_PROXY for all native fetch calls. Must run before
// any network request so that corporate proxy environments work out of the box.
configureProxyAwareFetch();

import { colorizeBrand, shouldUseColors } from "@githits/mcp/internal";
import { Command } from "commander";
import { version } from "../package.json";
Expand Down
5 changes: 5 additions & 0 deletions src/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
type AgentInfo,
type CodeNavigationService,
CodeNavigationServiceImpl,
configureProxyAwareFetch,
createClientHeaderBuilder,
createStaticTokenProvider,
endTelemetrySpan,
Expand Down Expand Up @@ -266,6 +267,10 @@ export interface CreateContainerOptions {
export async function createContainer(
options: CreateContainerOptions = {},
): Promise<Dependencies> {
// Ensure proxy env vars are honored for all service fetch calls. Idempotent;
// has no effect when HTTP_PROXY/HTTPS_PROXY are not set.
configureProxyAwareFetch();

return withTelemetrySpan("container.create", async () => {
const resolveStoredToken = options.resolveStoredToken ?? true;
const mcpUrl = getMcpUrl();
Expand Down