feat(grok): add Grok CLI usage tracking to desktop and VSIX - #5
Conversation
Adds Grok (xAI Grok Build) as a fully integrated provider alongside the existing six. Desktop (src-tauri/src/grok.rs, src/data/grok.ts, src/App.tsx): - xAI OIDC device-code login plus local import from ~/.grok/auth.json - Usage from the Grok CLI billing endpoint: credit window, monthly spend, on-demand spend, prepaid balance, and per-product breakdown - Dashboard card, account page, provider ordering, visibility toggle, pinning, notifications, JSON export, and reauthentication recovery - Raw tokens stay in the Rust backend; only display-safe data crosses IPC VSIX (quota-vscode/): - New GrokProvider and grokUsage modules with the same auth paths - Tracks: grok.credits, grok.monthlySpend, grok.onDemand - Connect, import-local, refresh, and disconnect commands; tokens are kept in SecretStorage like every other provider - Bumped to 1.2.0 and packaged OAuth scopes include grok-cli:access and api:access. Without them the billing endpoint accepts the token but answers 403, so a 403 is treated as "reconnect required" rather than retried through a token refresh, which would only reissue the same scopes. Tests: 6 Rust integration tests, 7 VSIX unit tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FCkUjKhyShFJe3k7uVdDGh
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ae89b83bb2
ℹ️ 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".
| let storage_dir = quota_storage_dir()?; | ||
| let summary = upsert_token_response_in(&storage_dir, &response).await?; | ||
| set_pending_oauth(None); | ||
| refresh_account_in(&storage_dir, &summary.id).await |
There was a problem hiding this comment.
Return the connected account before refreshing usage
When device authorization succeeds but the first billing request fails because of a transient HTTP/network/JSON error, refresh_account_in returns Err even though the account has already been persisted and the pending login has been cleared. The frontend consequently reports the connection as failed, keeps an unusable login panel whose session is already gone, and does not show the stored account until a reload. Complete the login with the saved summary and perform the initial usage refresh as a best-effort step, as the other desktop OAuth flows do.
Useful? React with 👍 / 👎.
| if (!email) throw new Error('Grok authorization did not return an account email.'); | ||
|
|
||
| const principalId = identity.principalId ?? normalize(claims?.principal_id) ?? normalize(claims?.sub); | ||
| const id = existing?.id ?? buildAccountId(email, principalId); |
There was a problem hiding this comment.
Preserve the existing Grok account during reconnection
When a user reauthenticates an existing Grok account, connect() is the only caller of this method and never supplies existing, so the matching ID is saved with an empty usage object and fresh metadata before the billing refresh runs. If that refresh then encounters any error, refreshAccount saves the already-emptied object, permanently discarding the last safe quota data that reauthentication is intended to keep visible. Resolve the stored account after deriving id and use it when constructing the replacement.
Useful? React with 👍 / 👎.
Summary
Adds Grok (xAI Grok Build) as a fully integrated provider, in both the desktop app and the VS Code extension.
Two ways to connect, matching what the other providers offer:
accounts.x.ai, shows the user code, polls for the token~/.grok/auth.json(honoursGROK_HOME)Usage comes from the Grok CLI billing endpoint (
cli-chat-proxy.grok.com/v1/billing): credit window with reset time, monthly spend, on-demand spend, prepaid balance, and a per-product breakdown. Identity comes from/v1/user, plan tier fromgrok.com/rest/subscriptions.Desktop
src-tauri/src/grok.rs— 8 Tauri commands (list, import, OAuth start/complete/cancel, refresh one, refresh all, delete)src/data/grok.ts,src/App.tsx— dashboard card, account page, provider ordering, visibility toggle, pinning, notifications, JSON export, reauthentication alertcapabilities/default.jsongains theaccounts.x.ai/auth.x.aiopener entries, without which the "Open Grok" button silently does nothingExisting users' saved provider order picks up
grokautomatically —readStoredProviderOrderalready appends keys missing from a stored order.VS Code extension
grokProvider.ts+grokUsage.ts, following the Kiro/Copilot provider shapegrok.credits,grok.monthlySpend,grok.onDemandSecretStorage, display-safe metadata in extension state — same as every other provider1.2.0and packagedOn the OAuth scopes
Worth calling out, since it is not guessable from the endpoint docs. The token must carry
grok-cli:accessandapi:accesson top ofopenid profile email offline_access. Without them the billing endpoint accepts the token and answers403rather than401.Because a refresh grant reissues the same scopes, a
403is never retried through a token refresh — it is surfaced directly as "reconnect required" so the Reauthenticate button appears. A401still goes down the normal refresh path.One other trap: sending
X-XAI-Token-Authwith any value makes every request401. Bearer only.Scope note
Anyone upgrading from an earlier build has to disconnect and reconnect Grok once (or re-import from the CLI) — a stored token predating this change carries the narrow scopes, and refreshing will not widen them.
Testing
cargo test— 6 new Grok integration tests (import single/multi-account, credential-leak assertions, quota parsing, refresh-failure classification); full suite green at 26node --test— 7 new VSIX unit tests, all passingnpm run typecheckandnpm run buildcleanPre-existing and untouched:
test/antigravityUsage.test.jsfails on non-English locales because it asserts atoLocaleString(undefined, …)result. Happy to fix that separately if useful.Not included
Cursor is deliberately excluded from the VSIX, so I kept that boundary in mind — but Grok's CLI is editor-agnostic, so it is included in both. Say the word if you would rather keep it desktop-only.