add Antigravity OAuth login and Antigravity image generation support - #1
add Antigravity OAuth login and Antigravity image generation support#1remorses wants to merge 2 commits into
Conversation
Implement Antigravity OAuth as a first-class provider in egaki, including browser PKCE login, callback-url completion, stored OAuth session metadata, token refresh, and project discovery helpers for Antigravity-backed requests. Add Antigravity image generation routing in a dedicated module, keep execution Antigravity-only, and improve error UX with actionable verification/API-activation links so account/project gating failures are explicit. Document Antigravity support and per-user gating behavior in README, including OAuth env setup required to avoid hardcoding OAuth client credentials in repo.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 69b1cfc315
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const child = spawn('xdg-open', [url], { detached: true, stdio: 'ignore' }) | ||
| child.unref() |
There was a problem hiding this comment.
Handle missing browser opener without crashing OAuth login
On Linux, this spawn('xdg-open', ...) call has no error handler, so systems without xdg-open (common in CI/headless environments) emit an unhandled error event (ENOENT) and terminate the process before OAuth can complete. Since antigravityOAuthLogin() always calls openBrowser(), egaki login --provider antigravity exits instead of continuing with the printed URL/manual completion flow.
Useful? React with 👍 / 👎.
| } | ||
|
|
||
| if (successfulProjectId && successfulProjectId !== auth.projectId) { | ||
| saveAntigravityAuth({ ...auth, projectId: successfulProjectId }) |
There was a problem hiding this comment.
Preserve refreshed auth when saving discovered project
This write uses the original auth snapshot, not the possibly refreshed credentials saved earlier by getValidAntigravityAccessToken(..., onRefresh). When a request succeeds with a different project ID, it can overwrite newly refreshed access/refresh/expires fields with stale values, causing repeated refresh churn or loss of rotated refresh tokens on subsequent runs.
Useful? React with 👍 / 👎.
Implement a custom SpeechModelV3 wrapping the Cartesia REST API `POST https://api.cartesia.ai/tts/bytes` since there is no `@ai-sdk/cartesia` npm package. **New file: `cli/src/cli/cartesia-provider.ts`** - Implements `SpeechModel` interface against Cartesia TTS bytes endpoint - Uses `Cartesia-Version: 2026-03-01` header (current API version) - Bearer auth via `CARTESIA_API_KEY` env var - Structured `output_format` objects for mp3/wav/raw - Speed control via `generation_config.speed` (range 0.6-1.5) - Language selection (42 languages via ISO 639-1 codes) - Default voice: Katie en-US Female **Speech catalog (`speech-catalog.ts`)** - Added `sonic-3.5` (latest, #1 naturalness, sub-90ms latency) - Added `sonic-3` (previous gen, still high quality) - Pricing estimated from Startup plan (~$39/M chars) - 5 recommended voice IDs from Cartesia docs **Speech models (`speech-models.ts`)** - Added `cartesia` entry to `SPEECH_PROVIDER_SDKS` with lazy import **Credentials (`credentials.ts`)** - Added `cartesia` provider with `CARTESIA_API_KEY` env var - Key hint points to https://play.cartesia.ai/keys **CLI (`cli.ts`)** - Updated speech command description to mention Cartesia - Updated voice/speed/language help text - Added Cartesia example command Tested end-to-end: created API key via Cartesia dashboard, logged in with `egaki login --provider cartesia`, generated English and Italian speech with sonic-3.5. Build passes, all 224 tests pass, no lintcn issues in new files. Session: ses_13341448effesFGa4ZZ3VsK6HO
**CLI imports (#1):** cli.ts now imports *Uncached variants so the CLI commands (egaki image, video, speech, transcribe) continue to work with raw result types instead of the cached { src } shape. **SSE race condition (#2):** subscribe to onProgressChange before reading the first snapshot, then loop: read -> yield -> check total -> await. This prevents the stream from hanging when a generation finishes between yield and subscribe. **Stale progress cleanup (#3):** guard setState calls in connectToProgress with `progressAbortController === controller` so an old aborted stream does not clear state from a newer active connection. **Error auto-clear by unique ID (#4):** each error gets a unique _id (key + timestamp + random). The 8s auto-clear timer removes by _id, not by generation key, so rapid duplicate failures are cleared independently. **Documented normalization limits (#5):** added AGENTS.md section for cachedGenerate with explicit note that params must be JSON-serializable; Date, Map, Set serialize to {} and should be passed as primitives. **getCacheInfo from wrapper (#6):** cachedGenerate now returns a callable with a .getCacheInfo(params) method that uses the same cacheKey config. Server components use wrapper.getCacheInfo() instead of the standalone getCacheInfo() to guarantee hash consistency. Session: ses_12ab29c60ffeiueHKd83cdmM4r
What changed
antigravity-gemini-3-pro-imageruns through Antigravity endpoints.Current status / limitation
cloudaicompanion.companions.generateChatpermission and project/API access issues).Why
This makes Antigravity a first-class auth + generation path in egaki, aligns behavior with the opencode plugin flow, and makes account/project gating failures understandable and recoverable without digging through raw API payloads.