dsh-gemini-oauth-bridge
English | 简体中文
Bridges a Google AI subscription (AI Pro / AI Ultra, via the Antigravity / Code Assist OAuth surface) into an OpenAI-compatible endpoint that DeepSeek Harness (DSH) can use directly as a model provider.
Important boundary: Google AI subscription developer quotas only cover the AI Studio web UI and OAuth login inside official CLI tools (Gemini CLI / Antigravity); API-key usage is billed separately. This plugin speaks the same OAuth surface as the official client, so it consumes subscription quota rather than API credit — but that also means it is not a Google-sanctioned third-party integration. Account risk / breakage is possible; evaluate for yourself.
Features
- Login and go: one-click Google OAuth from Settings → Gemini OAuth (authorization code + loopback callback), with automatic
loadCodeAssistregistration /onboardUseronboarding and upstream model discovery. - OpenAI-compatible endpoint:
/gemini-oauth-bridge/v1/chat/completions(streaming SSE + non-streaming) and/v1/models(so DSH can discover models). - Full protocol translation: system messages, multimodal (data-URL images), function calling (tool_calls ↔ functionCall/functionResponse),
reasoning_content(thinking stream), usage stats, finish-reason mapping. - Gemini 3 thought-signature replay: the server remembers upstream
thoughtSignaturevalues by tool-call id and re-attaches them to functionCall parts on the next turn — OpenAI format carries no signatures, and Gemini 3 multi-turn tool calls fail without replay. - Subscription-friendly (anti-drift): dynamically tracks the latest shipping Antigravity client version for the User-Agent (stale versions are rejected for newer models); requests carry
requestType: "agent", a stable derivedsessionId, andagent-<uuid>request IDs; keep-alive connections withoutConnection: close. - 429 cooldowns: parses upstream
Retry-After/retryDelay, enters a per-model cooldown window, and refuses requests locally during it instead of hammering upstream. - Single-flight token refresh: access tokens auto-renew 120 s before expiry; concurrent requests share one refresh.
Install
dsh plugin --profile web add github:hyqhyq3/dsh-gemini-oauth-bridge
# or a local checkout
dsh plugin --profile web add link:/path/to/dsh-gemini-oauth-bridge
Restart dsh --profile web, then open Settings → Gemini OAuth.
Usage
- Click 登录 Google 账号 (Log in with Google) and finish the flow in your browser. The plugin temporarily listens on
127.0.0.1:51121for the loopback callback — this exact redirect URI is registered for the Antigravity client and the port cannot change. Port conflicts are reported explicitly. - After login the page shows the account, project ID, tier, and model list.
- Register the bridge as a DSH provider in
~/.dsh/settings.yaml(port matches your DSH web URL, default 3080):
llm-pi-ai:
providers:
gemini-oauth:
api: openai-completions
baseURL: http://127.0.0.1:3080/gemini-oauth-bridge/v1
models:
- id: gemini-3.1-pro-high
- id: gemini-3.7-flash-high
- Restart DSH and pick the models in the model selector. Step 3 is usually unnecessary: logging in and refreshing the model list write this block automatically (the
baseURLport follows the address you use to open the DSH UI). No apiKey needed (the bridge does not check auth headers by default; to add local auth, set"apiKey": "some-string"in~/.dsh/gemini-oauth-bridge.json— requests must then carryAuthorization: Bearer <value>).
Local state
~/.dsh/gemini-oauth-bridge.json (mode 0600, contains OAuth tokens — treat as a secret):
| Field | Meaning |
|---|---|
tokens |
access/refresh tokens and expiry |
email / project / tier |
login account and registration result |
models |
upstream model cache (refreshable from the UI) |
signatures |
thoughtSignature replay cache (by tool-call id, cap 800) |
apiKey |
optional local auth for the bridge endpoint |
apiConfig |
optional upstream overrides (generateBase / loadBase / onboardBase); defaults match CLIProxyAPI: generation via daily, registration via prod |
Design notes (anti-drift)
Protocol details follow the public implementation in CLIProxyAPI (Apache-2.0):
- Live version tracking: every 6 hours the latest Antigravity version is fetched from the official updater hub (UA
electron-builder, like the real updater), semver-validated, and cached; failures fall back to the cache, then to the2.9.1floor (Cloud Code rejects clients below 2.9.0 for newer models). - Per-surface User-Agents: generate/stream/loadCodeAssist use
antigravity/hub/<version> darwin/arm64; onboardUser appendsgoogle-api-nodejs-client/10.3.0; OAuth token refresh usesGo-http-client/2.0— each surface matches the real client. - Envelope semantics:
requestType: "agent",requestId: "agent-<uuid>",userAgent: "antigravity",request.sessionIdderived deterministically from the conversation's first message (one session per conversation);safetySettingsstripped;generationConfig.maxOutputTokensremoved for gemini-3 models. - Connection fingerprint: no
Connection: close; keep-alive reuse. - Backoff: on 429, reads the
Retry-Afterheader or in-bodyretryDelay, enters a per-model cooldown (capped at 30 min) with no upstream traffic during it. - Subscription tier resolution: request entitlements follow
paidTier.id(Google AI Pro →g1-pro-tier); afree-tiercurrentTieris just the registration state and does not limit quota or model access. - Automatic provider configuration: on login and on 刷新模型列表 (refresh models), the
llm-pi-ai.providers.gemini-oauthblock (with the latest model ids) is surgically merged into~/.dsh/settings.yaml— every other byte of your config is preserved, and a one-time backup is kept atsettings.yaml.bak-gemini-oauth-bridge. Internalchat_*/tab_*ids are filtered out. Takes effect after a DSH restart; set"providerSync": falsein the state file to opt out. - Public client credentials: the OAuth client credentials are public values embedded in the Antigravity app (published verbatim in CLIProxyAPI's repo); they are stored base64-encoded in this repository only to keep GitHub push protection quiet, and decoded at runtime.
Limitations
- Unofficial usage: relies on the Antigravity client's public OAuth credentials and a non-public
v1internalsurface; Google may change or restrict it at any time. Intended for individual subscribers' own use. - Single account (no rotation);
http(s)://image URLs unsupported (data URLs only); the signature cache is persisted but LRU-evicts past 800 entries. - The login callback occupies
127.0.0.1:51121; logging in while the Antigravity desktop app is also logging in may conflict. - Generation goes to the
daily-cloudcode-pachannel (matching CLIProxyAPI's default); override viaapiConfigif needed.
Development
Zero dependencies, plain ESM JavaScript (Node ≥ 22); lib/*.js ships as written, no build step.
node --check lib/index.js && node --check lib/protocol.js && node --check lib/client.js
node --test test/protocol.test.js test/host.smoke.test.js test/bridge.e2e.test.js
Test coverage: protocol translation units (OAuth URLs / envelope / stream fragments / signature replay), host-route smoke (mocked cordis ctx + real HTTP + loopback login error paths), and bridge end-to-end (mocked upstream verifying the envelope contract, SSE translation, and 429 cooldown).
License
MIT
No comments yet. Be the first to write one.