feat: add Google Gemini API proxy support (port 10003)#1640
feat: add Google Gemini API proxy support (port 10003)#1640
Conversation
Add full Gemini API proxy support to the AWF api-proxy sidecar, matching the pattern of existing OpenAI, Anthropic, and Copilot providers. This enables Gemini CLI to work inside the AWF sandbox without requiring workarounds in gh-aw. Changes: - Add GEMINI port 10003 to API_PROXY_PORTS - Add geminiApiKey, geminiApiTarget, geminiApiBasePath to WrapperConfig - Add --gemini-api-target and --gemini-api-base-path CLI flags - Add GEMINI_API_KEY to excluded env vars when api-proxy is enabled - Set placeholder GEMINI_API_KEY in agent container (Gemini CLI v0.65.0+ exits 41 without auth when GEMINI_API_BASE_URL is set) - Set GEMINI_API_BASE_URL pointing to sidecar in agent env - Add .gemini to whitelisted home subdirectories (bind mount + chroot) - Add Gemini proxy server in server.js using x-goog-api-key header - Expose port 10003 in Dockerfile Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
| Metric | Base | PR | Delta |
|---|---|---|---|
| Lines | 85.81% | 85.64% | 📉 -0.17% |
| Statements | 85.69% | 85.53% | 📉 -0.16% |
| Functions | 86.71% | 86.71% | ➡️ +0.00% |
| Branches | 78.50% | 78.02% | 📉 -0.48% |
📁 Per-file Coverage Changes (2 files)
| File | Lines (Before → After) | Statements (Before → After) |
|---|---|---|
src/docker-manager.ts |
86.1% → 85.6% (-0.46%) | 85.6% → 85.2% (-0.44%) |
src/cli.ts |
60.6% → 60.6% (+0.02%) | 61.1% → 61.1% (+0.02%) |
Coverage comparison generated by scripts/ci/compare-coverage.ts
This comment has been minimized.
This comment has been minimized.
Chroot Version Comparison Results
Overall: ❌ Not all tests passed — Python and Node.js versions differ between host and chroot environments.
|
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
Adds Google Gemini support to the existing API-proxy sidecar pattern so Gemini CLI can run in AWF mode without exposing the real API key to the agent container, while also ensuring Gemini CLI state (~/.gemini) is persisted in chroot mode.
Changes:
- Introduces a new Gemini proxy listener on port 10003 in the api-proxy sidecar, with support for custom target host/base-path and
x-goog-api-keyinjection. - Extends CLI/config/types to support
geminiApiKey,geminiApiTarget, andgeminiApiBasePathand wires them into Docker Compose generation. - Adds
~/.geminito the chroot home subdirectory allowlist and updates the corresponding test.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/types.ts | Adds Gemini proxy port constant and new wrapper config fields for Gemini proxy settings. |
| src/docker-manager.ts | Wires Gemini env handling between agent and sidecar; mounts ~/.gemini; creates chroot dir. |
| src/docker-manager.test.ts | Updates expected chroot home subdirectories to include .gemini. |
| src/cli.ts | Adds Gemini API target/base-path flags and reads GEMINI_API_KEY into config; adds target warning/allowlist support. |
| containers/api-proxy/server.js | Adds Gemini proxy server on 10003 with key injection, health endpoint, and rate limiting. |
| containers/api-proxy/Dockerfile | Exposes port 10003 in the sidecar image. |
Comments suppressed due to low confidence (1)
src/cli.ts:1783
validateApiProxyConfig()/ API-proxy status logging elsewhere in this file still only consider OpenAI/Anthropic/Copilot. WithgeminiApiKeynow supported, running with onlyGEMINI_API_KEYwill emit a misleading "no API keys found" warning and omit Gemini from the status output. Update the validation + status log to include Gemini.
copilotGithubToken: process.env.COPILOT_GITHUB_TOKEN,
geminiApiKey: process.env.GEMINI_API_KEY,
copilotApiTarget: options.copilotApiTarget || process.env.COPILOT_API_TARGET,
openaiApiTarget: options.openaiApiTarget || process.env.OPENAI_API_TARGET,
openaiApiBasePath: options.openaiApiBasePath || process.env.OPENAI_API_BASE_PATH,
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| openaiApiKey: process.env.OPENAI_API_KEY, | ||
| anthropicApiKey: process.env.ANTHROPIC_API_KEY, | ||
| copilotGithubToken: process.env.COPILOT_GITHUB_TOKEN, | ||
| geminiApiKey: process.env.GEMINI_API_KEY, | ||
| copilotApiTarget: options.copilotApiTarget || process.env.COPILOT_API_TARGET, |
There was a problem hiding this comment.
geminiApiKey is now being added to the runtime config object, but the later redaction logic only excludes openaiApiKey, anthropicApiKey, and copilotGithubToken. This will cause GEMINI_API_KEY to be logged in debug output via redactedConfig (secret disclosure). Add geminiApiKey to the redaction skip list (and any other secret-handling paths).
This issue also appears on line 1779 of the same file.
|
|
||
| proxyRequest(req, res, GEMINI_API_TARGET, { | ||
| 'x-goog-api-key': GEMINI_API_KEY, | ||
| }, 'gemini', GEMINI_API_BASE_PATH); | ||
| }); |
There was a problem hiding this comment.
Gemini auth is injected via x-goog-api-key, but the proxy’s auth-safety/logging paths are still oriented around authorization/x-api-key. Consider adding x-goog-api-key to the stripped header set and including it in the auth_inject debug logic so client-supplied Gemini keys are consistently removed/overwritten and injection can be observed in logs.
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.
Agent-Logs-Url: https://github.com/github/gh-aw-firewall/sessions/03ba1344-8f2e-4d67-890f-46e665522db4 Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
|
| Metric | Base | PR | Delta |
|---|---|---|---|
| Lines | 85.81% | 86.20% | 📈 +0.39% |
| Statements | 85.69% | 86.07% | 📈 +0.38% |
| Functions | 86.71% | 87.41% | 📈 +0.70% |
| Branches | 78.50% | 78.46% | 📉 -0.04% |
📁 Per-file Coverage Changes (2 files)
| File | Lines (Before → After) | Statements (Before → After) |
|---|---|---|
src/cli.ts |
60.6% → 61.3% (+0.78%) | 61.1% → 61.8% (+0.77%) |
src/docker-manager.ts |
86.1% → 87.0% (+0.89%) | 85.6% → 86.5% (+0.88%) |
Coverage comparison generated by scripts/ci/compare-coverage.ts
Smoke Test Results✅ GitHub MCP: test: add tests for Gemini API proxy support to fix coverage regression (#1654), fix: increase smoke-claude max-turns and fix playwright log dir permissions (#1653) Overall: PASS
|
Chroot Version Comparison Results
Overall: ❌ NOT all tests passed — Python and Node.js versions differ between host and chroot environments.
|
Smoke Test: GitHub Actions Services Connectivity ✅All checks passed:
|
🔥 Smoke Test Results
PR: feat: add Google Gemini API proxy support (port 10003) — @lpcox
|
🏗️ Build Test Suite Results
Overall: 8/8 ecosystems passed — ✅ PASS
|
Smoke Test ResultsPR titles: "test: add tests for Gemini API proxy support to fix coverage regression"; "perf: optimize security-guard token usage"
|
Problem
Gemini CLI v0.65.0+ performs a startup auth check that exits with code 41 when
GEMINI_API_BASE_URLis set but noGEMINI_API_KEYis found. In AWF mode, the real key should be held by the api-proxy sidecar (not exposed to the agent container), but no Gemini proxy endpoint exists — forcing gh-aw to work around the issue by injecting placeholder keys andmkdircommands into the container command string.Additionally,
~/.gemini/is not in AWF's whitelisted home subdirectories, causing an ENOENT when Gemini CLI attempts to save its project registry.Upstream: github/gh-aw#23695 (gh-aw workaround for this gap)
Solution
Add full Gemini API proxy support following the same pattern as OpenAI, Anthropic, and Copilot providers:
API Proxy Sidecar (
containers/api-proxy/)server.jsGEMINI_API_KEYfrom env and injects it asx-goog-api-keyheader (Google API standard)GEMINI_API_TARGET, default:generativelanguage.googleapis.com) and base path prefix (GEMINI_API_BASE_PATH)CLI (
src/cli.ts)--gemini-api-target <host>and--gemini-api-base-path <path>flagsGEMINI_API_KEYread from env and passed to configemitApiProxyTargetWarnings()Docker Manager (
src/docker-manager.ts)GEMINI_API_KEYexcluded from agent env when api-proxy enabledGEMINI_API_KEY,GEMINI_API_TARGET,GEMINI_API_BASE_PATHpassed to sidecar containerGEMINI_API_BASE_URLset in agent container pointing to sidecar (http://172.30.0.30:10003)GEMINI_API_KEYset in agent so Gemini CLI's startup auth check passes.geminiadded to whitelisted home subdirectories (bind mount + chroot dir creation)Types (
src/types.ts)GEMINI: 10003added toAPI_PROXY_PORTSgeminiApiKey,geminiApiTarget,geminiApiBasePathadded toWrapperConfigHow it works
Once this lands, the
mkdir+ placeholder workaround in gh-aw#23695 can be simplified — AWF will handle both the~/.gemini/directory and the credential isolation natively.Testing
.gemini