refactor: split api-proxy server.js into focused modules#2559
Conversation
Create proxy-request.js (~489 lines), model-discovery.js (~221 lines), and management.js (~127 lines) extracted from server.js (1284→664 lines). - proxy-request.js: HTTP/WebSocket proxy, rate-limit enforcement (isValidRequestId, checkRateLimit, proxyRequest, proxyWebSocket). Self-contained: reads HTTPS_PROXY from env at load time, creates own RateLimiter instance, exports it for management. - model-discovery.js: fetchJson, httpProbe, extractModelIds, buildModelsJson, writeModelsJson. buildModelsJson/writeModelsJson accept explicit params (adapters, cachedModels, modelAliases) to avoid direct dependency on server.js state. - management.js: createManagementHandlers() factory that returns healthResponse, reflectEndpoints, handleManagementEndpoint. Uses getter callbacks for shared server state so functions always reflect current values. - server.js: imports from new modules, keeps state management (cachedModels, keyValidationResults), startup orchestration (validateApiKeys, fetchStartupModels), and createProviderServer factory. Re-exports all symbols for backward compatibility. - Dockerfile: adds proxy-request.js, model-discovery.js, management.js to COPY list. All 569 tests pass unchanged. Agent-Logs-Url: https://github.com/github/gh-aw-firewall/sessions/1966046a-0827-4850-8200-9bdecc4822a6 Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
…-request.js Agent-Logs-Url: https://github.com/github/gh-aw-firewall/sessions/1966046a-0827-4850-8200-9bdecc4822a6 Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (1 files)
Coverage comparison generated by |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR refactors the api-proxy into smaller modules so the security-sensitive forwarding path, startup model discovery, and management endpoints are separated while server.js continues to own shared state and startup orchestration.
Changes:
- Extracts HTTP/WebSocket proxying and rate limiting into
proxy-request.js. - Extracts model probing/fetching and
models.jsonsnapshot helpers intomodel-discovery.js. - Extracts
/health,/metrics, and/reflecthandling intomanagement.js, and updates the container image to include the new modules.
Show a summary per file
| File | Description |
|---|---|
containers/api-proxy/server.js |
Rewired the main entrypoint to consume the new modules and keep orchestration/state logic. |
containers/api-proxy/proxy-request.js |
New module containing request forwarding, WebSocket tunneling, request ID validation, and rate-limit enforcement. |
containers/api-proxy/model-discovery.js |
New module for startup probes, model-list fetching/parsing, and writing models.json. |
containers/api-proxy/management.js |
New factory-based management endpoint handlers reading shared state via getters. |
containers/api-proxy/Dockerfile |
Adds the new split modules to the image build. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comments suppressed due to low confidence (1)
containers/api-proxy/server.js:20
- This header comment is no longer accurate: the file still reads multiple environment variables (for example
AWF_MODEL_ALIASESandAWF_VALIDATE_KEYS) and still hard-codes provider names in places like the body-transform wiring. Leaving the "ZERO ... env-var reads" claim here is misleading for future audits of this security-sensitive entrypoint.
* All provider-specific knowledge (credentials, URLs, auth headers, body
* transforms, model lists) lives exclusively in providers/*.js.
* This file contains ZERO hard-coded provider names, ports, or env-var reads.
- Files reviewed: 5/5 changed files
- Comments generated: 2
| const tls = require('tls'); | ||
| const { URL } = require('url'); | ||
| const { HttpsProxyAgent } = require('https-proxy-agent'); | ||
| const { generateRequestId, sanitizeForLog, logRequest } = require('./logging'); |
| // ── Module-level constants ──────────────────────────────────────────────────── | ||
| const HTTPS_PROXY = process.env.HTTPS_PROXY || process.env.HTTP_PROXY; | ||
| const proxyAgent = HTTPS_PROXY ? new HttpsProxyAgent(HTTPS_PROXY) : undefined; |
|
@copilot merge main and address review feedback. make sure you preserve recent updates to byok handling |
This comment has been minimized.
This comment has been minimized.
- Merge e61f158: X-Initiator billing injection, billing header extraction, token-tracker billingInfo/initiatorSent, new tests, workflow updates - Move extractBillingHeaders into proxy-request.js (lives with proxyRequest) - Add X-Initiator: agent injection for all Copilot-bound requests - Pass billingInfo + initiatorSent to trackTokenUsage and request_complete log - Export extractBillingHeaders from proxy-request.js; re-export from server.js - Fix review comment 1: remove dead generateRequestId import from server.js - Fix review comment 2: model-discovery.js imports proxyAgent from proxy-request.js instead of duplicating HttpsProxyAgent bootstrap - 581 tests pass (12 new from main branch) Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Done in commit
All 581 tests pass (including 12 new ones from main). Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
🧪 Smoke Test: Copilot BYOK (Offline) Mode
Running in BYOK offline mode ( Author: Overall: PASS (BYOK inference confirmed; pre-step vars unexpanded — likely template issue in workflow)
|
Smoke Test Results✅ GitHub MCP: Last 2 merged PRs retrieved Overall Status: PASS
|
🔬 Smoke Test Results
Overall: FAIL — pre-computed step outputs were not passed to this job (template variables unexpanded). PR author:
|
Codex Smoke: FAIL
Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "registry.npmjs.org"See Network Configuration for more information.
|
🏗️ Build Test Suite Results
Overall: 8/8 ecosystems passed — ✅ PASS
|
Smoke Test Results — FAIL
|
Chroot Version Comparison Results
Overall: ❌ Not all tests passed — Python and Node.js versions differ between host and chroot.
|
containers/api-proxy/server.jshad grown to 1,284 lines with six distinct responsibilities colocated. The security-critical credential injection path (proxyRequest) was particularly hard to audit mixed in with rate limiting, model discovery, and management endpoints.New modules
proxy-request.jsproxyRequest,proxyWebSocket,checkRateLimit,isValidRequestId,extractBillingHeaders— the entire credential injection, SSRF-prevention, and billing-header extraction pathmodel-discovery.jsfetchJson,httpProbe,extractModelIds,buildModelsJson,writeModelsJsonmanagement.js/health,/metrics,/reflecthandlers viacreateManagementHandlers()factoryserver.js(1,284 → ~667 lines)Retains: state management (
cachedModels,keyValidationResults), startup orchestration (validateApiKeys,fetchStartupModels,probeProvider),createProviderServerfactory, and re-exports of all symbols for backward compatibility.Design notes
proxy-request.jsis self-contained — readsHTTPS_PROXYat load time, owns itsRateLimiterinstance (exported sohealthResponsecan calllimiter.getAllStatus()), and containsextractBillingHeadersalongside theproxyRequestfunction that calls it. All Copilot-bound requests receiveX-Initiator: agentby default (respecting any client-supplied value) to prevent billing inflation on autonomous turns.model-discovery.jsimports the already-constructedproxyAgentfromproxy-request.jsrather than duplicating theHTTPS_PROXY/HttpsProxyAgentbootstrap, ensuring both modules always use the same agent.management.jsuses getter callbacks rather than direct state captures, so/healthand/reflectalways reflect current values:buildModelsJson/writeModelsJsoninmodel-discovery.jsaccept explicit(adapters, cachedModels, modelAliases)params;server.jsprovides zero-argument wrappers that bind current state, preserving the calling convention expected by tests.