Skip to content
Merged
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
25 changes: 25 additions & 0 deletions containers/api-proxy/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,31 @@ if (require.main === module) {
const contentLength = parseInt(req.headers['content-length'], 10) || 0;
if (checkRateLimit(req, res, 'copilot', contentLength)) return;

// Copilot CLI 1.0.21+ calls GET /models at startup (to list or validate models).
// The /models endpoint lives on the Copilot inference API (COPILOT_API_TARGET),
// NOT on the GitHub REST API. Explicitly use COPILOT_GITHUB_TOKEN for this
// request so the GitHub OAuth token is used even when both COPILOT_GITHUB_TOKEN
// and COPILOT_API_KEY are configured (COPILOT_API_KEY alone is not accepted by
// the /models endpoint).
Comment on lines +894 to +896
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new comment implies COPILOT_AUTH_TOKEN might resolve to COPILOT_API_KEY when both COPILOT_GITHUB_TOKEN and COPILOT_API_KEY are set, but resolveCopilotAuthToken() currently always prefers COPILOT_GITHUB_TOKEN. Either adjust the comment to reflect the actual precedence, or (if the intent is to use BYOK for most calls and OAuth only for /models) revisit resolveCopilotAuthToken/token selection logic accordingly.

Suggested change
// request so the GitHub OAuth token is used even when both COPILOT_GITHUB_TOKEN
// and COPILOT_API_KEY are configured (COPILOT_API_KEY alone is not accepted by
// the /models endpoint).
// request to guarantee the GitHub OAuth token is sent for /models, because
// COPILOT_API_KEY alone is not accepted by that endpoint. This special-case
// avoids relying on the general COPILOT_AUTH_TOKEN selection logic here.

Copilot uses AI. Check for mistakes.
let reqPathname;
try {
reqPathname = new URL(req.url, 'http://localhost').pathname;
} catch {
logRequest('warn', 'copilot_proxy_malformed_url', {
message: 'Malformed request URL in Copilot proxy — rejecting with 400',
});
res.writeHead(400, { 'Content-Type': 'application/json' });
Comment on lines +900 to +904
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The malformed-URL early return sends a 400 without generating/returning an X-Request-ID or emitting the usual request_start/request_complete metrics/logs, which makes debugging and observability inconsistent with other error paths. Consider creating a request id here (and logging the sanitized URL/parse error) so failures can be correlated like other requests.

Suggested change
} catch {
logRequest('warn', 'copilot_proxy_malformed_url', {
message: 'Malformed request URL in Copilot proxy — rejecting with 400',
});
res.writeHead(400, { 'Content-Type': 'application/json' });
} catch (err) {
const requestId = req.headers['x-request-id'] || generateRequestId();
const sanitizedUrl = sanitizeForLog(typeof req.url === 'string' ? req.url : '');
logRequest('warn', 'copilot_proxy_malformed_url', {
request_id: requestId,
message: 'Malformed request URL in Copilot proxy — rejecting with 400',
method: req.method,
url: sanitizedUrl,
error: err && err.message ? sanitizeForLog(err.message) : 'Unknown URL parse error',
});
res.writeHead(400, {
'Content-Type': 'application/json',
'X-Request-ID': requestId,
});

Copilot uses AI. Check for mistakes.
res.end(JSON.stringify({ error: 'Invalid request URL' }));
return;
}
const isModelsPath = reqPathname === '/models' || reqPathname.startsWith('/models/');
if (isModelsPath && req.method === 'GET' && COPILOT_GITHUB_TOKEN) {
proxyRequest(req, res, COPILOT_API_TARGET, {
'Authorization': `Bearer ${COPILOT_GITHUB_TOKEN}`,
}, 'copilot');
return;
}
Comment on lines +891 to +914
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change introduces new behavior for Copilot request routing (/models path detection + token selection + malformed URL handling) but there are no unit tests covering it. Since this module already has tests (server.test.js), add coverage to assert that GET /models uses COPILOT_GITHUB_TOKEN when set and that malformed req.url returns 400.

Copilot uses AI. Check for mistakes.

proxyRequest(req, res, COPILOT_API_TARGET, {
'Authorization': `Bearer ${COPILOT_AUTH_TOKEN}`,
}, 'copilot');
Expand Down
3 changes: 3 additions & 0 deletions src/docker-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1535,6 +1535,9 @@ export function generateDockerCompose(
...(config.geminiApiBasePath && { GEMINI_API_BASE_PATH: config.geminiApiBasePath }),
// 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 }),
// Forward GITHUB_API_URL so api-proxy can use the correct GitHub REST API hostname
// on GHES/GHEC (e.g. https://ghes.example.com/api/v3 or api.mycompany.ghe.com)
...(process.env.GITHUB_API_URL && { GITHUB_API_URL: process.env.GITHUB_API_URL }),
Comment on lines +1538 to +1540
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GITHUB_API_URL is now forwarded into the api-proxy container, but the api-proxy implementation in containers/api-proxy/ doesn't currently read or use GITHUB_API_URL. Either wire this through where the proxy derives/targets GitHub REST endpoints, or remove the env var/comment to avoid dead configuration and confusion.

Suggested change
// Forward GITHUB_API_URL so api-proxy can use the correct GitHub REST API hostname
// on GHES/GHEC (e.g. https://ghes.example.com/api/v3 or api.mycompany.ghe.com)
...(process.env.GITHUB_API_URL && { GITHUB_API_URL: process.env.GITHUB_API_URL }),

Copilot uses AI. Check for mistakes.
// Route through Squid to respect domain whitelisting
HTTP_PROXY: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
HTTPS_PROXY: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
Expand Down
Loading