fix(quota): restore Grok Settings usage for current CLI auth/billing#2353
fix(quota): restore Grok Settings usage for current CLI auth/billing#2353jasonhnd wants to merge 2 commits into
Conversation
|
| Filename | Overview |
|---|---|
| packages/server/src/services/quota-fetcher/providers/grok.ts | Adds current Grok auth and billing schema support, but retains the unsafe all-entry credential fallback identified in the previous review. |
| packages/server/src/services/quota-fetcher/service.test.ts | Adds behavioral coverage for current nested authentication and current and legacy billing response shapes. |
Reviews (4): Last reviewed commit: "fix(quota): make Grok auth-file tests wo..." | Re-trigger Greptile
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e5b1af5da9
ℹ️ 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".
a263a50 to
d2ac386
Compare
| } | ||
|
|
||
| const entries = Object.entries(record); | ||
| const preferred = entries.filter(([key]) => key.startsWith("https://auth.x.ai::")); |
There was a problem hiding this comment.
Unrelated credential sent as bearer
When ~/.grok/auth.json has no https://auth.x.ai:: entry but contains another object with a non-empty key, this fallback selects that unrelated secret and sends it as the billing request's Bearer token, disclosing the credential to cli-chat-proxy.grok.com and causing authentication to fail.
Grok CLI no longer stores a top-level access_token or usage.creditUsage. Read nested auth key tokens and config.used.val so Settings → Usage shows monthly credits again. Keep legacy shapes and env tokens working. Fixes getpaseo#2352
Inject homeDir into GrokQuotaProvider like Kimi so nested ~/.grok/auth.json tests do not depend on os.homedir() which ignores $HOME on Windows (USERPROFILE).
d2ac386 to
e10c91d
Compare
Closes #2352
What is broken
Settings → Usage never shows Grok plan credits for a normal Grok CLI login, even when:
~/.grok/auth.jsonpresent), andGET https://cli-chat-proxy.grok.com/v1/billingreturns a healthy monthly budget.Expected: a
Monthly creditsbalance (used / remaining / limit), same surface as Claude/Codex plan usage.Actual: Grok is missing, unavailable, or shows empty balances — no error that points at the real cause.
This is the Settings plan-credits path only (
GrokQuotaProviderin the daemon quota-fetcher).It is not the composer context-window meter (that is a separate path: #1390 / ACP
usage_update).Issue: #2352
Why it broke (two independent schema drifts)
Either drift alone is enough to drop the card; both are present on current Grok CLI installs. Verified against live host shapes (2026-07-23) and the code on
main.Drift 1 — auth token (
readGrokToken)Old code only accepted a top-level field:
Live
~/.grok/auth.jsonshape (structure only; no secrets):{ "https://auth.x.ai::<uuid>": { "key": "<JWT>", "refresh_token": "...", "expires_at": "...", "user_id": "...", "email": "..." } }There is no top-level
access_token. The token lives at the nested entry’skey.So without
GROK_API_KEY/GROK_TOKENin the daemon env,readGrokToken()returnednull, the billing request never ran, and Usage showed Grok as unavailable.Env order was already correct and is unchanged:
GROK_API_KEYGROK_TOKEN~/.grok/auth.jsonDrift 2 — billing response (
fetchUsage)Old code:
Live
GET /v1/billingbody (Bearer = nestedkey; headers already correct:Authorization+X-XAI-Token-Auth: xai-grok-cli):{ "config": { "monthlyLimit": { "val": 150000 }, "used": { "val": 37886 }, "onDemandCap": { "val": 0 }, "billingPeriodStart": "2026-07-01T00:00:00+00:00", "billingPeriodEnd": "2026-08-01T00:00:00+00:00", "history": [ ... ] } }There is no top-level
usage/creditUsage. Used credits areconfig.used.val.Even if auth were fixed first,
creditUsagestayednull→ used/remaining stayed null (limit alone could partially parse, but the card was still wrong / empty in practice for normal logins).The unit test previously mocked the obsolete shape (
usage: { creditUsage: 0 }), so CI did not catch the live response.What this PR changes
Scope: daemon
GrokQuotaProvideronly — no wire/protocol schema change, no Settings UI change (UI already renders balances when the daemon returns them).Files
packages/server/src/services/quota-fetcher/providers/grok.tspackages/server/src/services/quota-fetcher/service.test.tspackages/server/src/services/quota-fetcher/manifest.ts1) Token resolution
Added
extractGrokTokenFromAuth()used byreadGrokToken():fetchUsage)access_tokenif non-empty (legacy)key, preferring keys that start withhttps://auth.x.ai::when multiple existTokens are never logged.
2) Billing parse + schema
config.used.valconfig.used.val ?? usage.creditUsage(live first, legacy fallback)config.monthlyLimit.val(unchanged)max(0, limit - used)when both numbers are presentmonthly_credits/ labelMonthly creditsBilling period fields are not surfaced in
details(kept the PR minimal).What this PR deliberately does not touch
handleUsageUpdate/ composer meterlimits[]How it was verified
Done (automated)
npx vitest run packages/server/src/services/quota-fetcher/service.test.ts --bail=1 # 27 passedNew / updated coverage:
config: { monthlyLimit: { val }, used: { val } }→ correct used / remaining / limit,status: "available", balancemonthly_credits~/.grok/auth.jsonunder testHOMEwithhttps://auth.x.ai::… .key, no env token → request usesAuthorization: Bearer <nested key>and returns balancesused: 0,limit: 0)usage.creditUsagestill works whenconfig.usedis absentGROK_API_KEYstill worksAlso:
oxlint+ format on touched files, and full monorepotypecheck(after building protocol/client/highlight dist for the worktree).Test harness note: suite already sets
process.env.HOME = tempDir;os.homedir()respectsHOMEon this platform, so nested auth is exercised by writingjoin(homeDir, ".grok", "auth.json")without injecting a homeDir option intoGrokQuotaProvider.Not done (manual / live)
Optional smoke for reviewers (do not print secrets):
Acceptance criteria
~/.grok/auth.json(no env token) can yield a token in codeconfig.used.val+config.monthlyLimit.valyieldsstatus: "available"and correctmonthly_creditsused/remaining/limitusage.creditUsagestill workCloses #2352