From 5b92619f38c0495890445b2970789654cd4a7698 Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Thu, 26 Feb 2026 09:05:05 -0800 Subject: [PATCH] feat(proxy): make copilot api target configurable for enterprise environments Auto-derive api.enterprise.githubcopilot.com from GITHUB_SERVER_URL when the server is not github.com (GHEC/GHES). Allow explicit override via COPILOT_API_TARGET env var or --copilot-api-target CLI flag. Forward GITHUB_SERVER_URL and GITHUB_API_URL to both agent and api-proxy containers by default. --- containers/api-proxy/README.md | 3 +++ containers/api-proxy/server.js | 27 ++++++++++++++++++++++++++- src/cli.ts | 7 +++++++ src/docker-manager.ts | 10 ++++++++++ src/types.ts | 22 ++++++++++++++++++++++ 5 files changed, 68 insertions(+), 1 deletion(-) diff --git a/containers/api-proxy/README.md b/containers/api-proxy/README.md index b6b8805a..4726d299 100644 --- a/containers/api-proxy/README.md +++ b/containers/api-proxy/README.md @@ -35,6 +35,9 @@ Required (at least one): - `OPENAI_API_KEY` - OpenAI API key for authentication - `ANTHROPIC_API_KEY` - Anthropic API key for authentication +Optional: +- `COPILOT_API_TARGET` - Target hostname for GitHub Copilot API requests (default: `api.githubcopilot.com`). Useful for GHES deployments. + Set by AWF: - `HTTP_PROXY` - Squid proxy URL (http://172.30.0.10:3128) - `HTTPS_PROXY` - Squid proxy URL (http://172.30.0.10:3128) diff --git a/containers/api-proxy/server.js b/containers/api-proxy/server.js index f6f62533..9c6acc4f 100644 --- a/containers/api-proxy/server.js +++ b/containers/api-proxy/server.js @@ -46,12 +46,37 @@ const OPENAI_API_KEY = process.env.OPENAI_API_KEY; const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY; const COPILOT_GITHUB_TOKEN = process.env.COPILOT_GITHUB_TOKEN; +// Configurable Copilot API target host (supports GHES/GHEC / custom endpoints) +// Priority: COPILOT_API_TARGET env var > auto-derive from GITHUB_SERVER_URL > default +function deriveCopilotApiTarget() { + if (process.env.COPILOT_API_TARGET) { + return process.env.COPILOT_API_TARGET; + } + // For GitHub Enterprise Cloud (*.ghe.com) or GitHub Enterprise Server + // (any GITHUB_SERVER_URL that isn't https://github.com), route to the + // enterprise Copilot API endpoint instead of the individual one. + const serverUrl = process.env.GITHUB_SERVER_URL; + if (serverUrl) { + try { + const hostname = new URL(serverUrl).hostname; + if (hostname !== 'github.com') { + return 'api.enterprise.githubcopilot.com'; + } + } catch { + // Invalid URL — fall through to default + } + } + return 'api.githubcopilot.com'; +} +const COPILOT_API_TARGET = deriveCopilotApiTarget(); + // Squid proxy configuration (set via HTTP_PROXY/HTTPS_PROXY in docker-compose) const HTTPS_PROXY = process.env.HTTPS_PROXY || process.env.HTTP_PROXY; logRequest('info', 'startup', { message: 'Starting AWF API proxy sidecar', squid_proxy: HTTPS_PROXY || 'not configured', + copilot_api_target: COPILOT_API_TARGET, providers: { openai: !!OPENAI_API_KEY, anthropic: !!ANTHROPIC_API_KEY, @@ -433,7 +458,7 @@ if (COPILOT_GITHUB_TOKEN) { const contentLength = parseInt(req.headers['content-length'], 10) || 0; if (checkRateLimit(req, res, 'copilot', contentLength)) return; - proxyRequest(req, res, 'api.githubcopilot.com', { + proxyRequest(req, res, COPILOT_API_TARGET, { 'Authorization': `Bearer ${COPILOT_GITHUB_TOKEN}`, }, 'copilot'); }); diff --git a/src/cli.ts b/src/cli.ts index 2abcf276..b089e2f2 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -784,6 +784,12 @@ program ' Supports OpenAI (Codex) and Anthropic (Claude) APIs.', false ) + .option( + '--copilot-api-target ', + 'Target hostname for GitHub Copilot API requests in the api-proxy sidecar.\n' + + ' Defaults to api.githubcopilot.com. Useful for GHES deployments.\n' + + ' Can also be set via COPILOT_API_TARGET env var.', + ) .option( '--rate-limit-rpm ', 'Enable rate limiting: max requests per minute per provider (requires --enable-api-proxy)', @@ -1064,6 +1070,7 @@ program openaiApiKey: process.env.OPENAI_API_KEY, anthropicApiKey: process.env.ANTHROPIC_API_KEY, copilotGithubToken: process.env.COPILOT_GITHUB_TOKEN, + copilotApiTarget: options.copilotApiTarget || process.env.COPILOT_API_TARGET, }; // Build rate limit config when API proxy is enabled diff --git a/src/docker-manager.ts b/src/docker-manager.ts index f95230ed..8f7c7235 100644 --- a/src/docker-manager.ts +++ b/src/docker-manager.ts @@ -444,6 +444,9 @@ export function generateDockerCompose( if (process.env.USER) environment.USER = process.env.USER; if (process.env.TERM) environment.TERM = process.env.TERM; if (process.env.XDG_CONFIG_HOME) environment.XDG_CONFIG_HOME = process.env.XDG_CONFIG_HOME; + // Enterprise environment variables — needed for GHEC/GHES Copilot authentication + if (process.env.GITHUB_SERVER_URL) environment.GITHUB_SERVER_URL = process.env.GITHUB_SERVER_URL; + if (process.env.GITHUB_API_URL) environment.GITHUB_API_URL = process.env.GITHUB_API_URL; } // Additional environment variables from --env flags (these override everything) @@ -977,6 +980,10 @@ export function generateDockerCompose( ...(config.openaiApiKey && { OPENAI_API_KEY: config.openaiApiKey }), ...(config.anthropicApiKey && { ANTHROPIC_API_KEY: config.anthropicApiKey }), ...(config.copilotGithubToken && { COPILOT_GITHUB_TOKEN: config.copilotGithubToken }), + // Configurable Copilot API target (for GHES/GHEC support) + ...(config.copilotApiTarget && { COPILOT_API_TARGET: config.copilotApiTarget }), + // Forward GITHUB_SERVER_URL so api-proxy can auto-derive enterprise endpoints + ...(process.env.GITHUB_SERVER_URL && { GITHUB_SERVER_URL: process.env.GITHUB_SERVER_URL }), // Route through Squid to respect domain whitelisting HTTP_PROXY: `http://${networkConfig.squidIp}:${SQUID_PORT}`, HTTPS_PROXY: `http://${networkConfig.squidIp}:${SQUID_PORT}`, @@ -1050,6 +1057,9 @@ export function generateDockerCompose( if (config.copilotGithubToken) { environment.COPILOT_API_URL = `http://${networkConfig.proxyIp}:${API_PROXY_PORTS.COPILOT}`; logger.debug(`GitHub Copilot API will be proxied through sidecar at http://${networkConfig.proxyIp}:${API_PROXY_PORTS.COPILOT}`); + if (config.copilotApiTarget) { + logger.debug(`Copilot API target overridden to: ${config.copilotApiTarget}`); + } // Set placeholder token for GitHub Copilot CLI compatibility // Real authentication happens via COPILOT_API_URL pointing to api-proxy diff --git a/src/types.ts b/src/types.ts index 209ffef3..1ee1c8bf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -472,6 +472,28 @@ export interface WrapperConfig { * @default undefined */ copilotGithubToken?: string; + + /** + * Target hostname for GitHub Copilot API requests (used by API proxy sidecar) + * + * When enableApiProxy is true, this hostname is passed to the Node.js sidecar + * as `COPILOT_API_TARGET`. The proxy will forward Copilot API requests to this host + * instead of the default `api.githubcopilot.com`. + * + * Useful for GitHub Enterprise Server (GHES) deployments where the Copilot API + * endpoint differs from the public default. + * + * Can be set via: + * - CLI flag: `--copilot-api-target ` + * - Environment variable: `COPILOT_API_TARGET` + * + * @default 'api.githubcopilot.com' + * @example + * ```bash + * awf --enable-api-proxy --copilot-api-target api.github.mycompany.com -- command + * ``` + */ + copilotApiTarget?: string; } /**