feat: add Grok zero-setup usage provider (grok.cli)#28
Conversation
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 18803ca630
ℹ️ 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 CLAUDE_USAGE_URL: &str = "https://api.anthropic.com/api/oauth/usage"; | ||
| const CLAUDE_OAUTH_BETA: &str = "oauth-2025-04-20"; | ||
|
|
||
| const GROK_SUBSCRIPTIONS_URL: &str = "https://grok.com/rest/subscriptions"; |
There was a problem hiding this comment.
Point Grok CLI auth at the CLI billing surface
For users authenticated only with grok login, this endpoint does not match the documented CLI billing surface: the Grok usage integrations I checked describe the CLI path as cli-chat-proxy.grok.com/v1/billing?format=credits / grok agent stdio x.ai/billing, not the browser grok.com/rest/subscriptions route. Sending the CLI bearer here can turn a valid CLI login into 401/403 or parse failures, so PickGauge records login_required instead of showing the Grok plan; please target the CLI billing/settings endpoint or use a browser-cookie flow consistently. Source checked: https://github.com/robinebers/openusage/blob/main/docs/providers/grok.md
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Dismissing this one on direct evidence: we verified live on a machine with only a grok login CLI credential — twice, including immediately after a fresh device-code login — that GET grok.com/rest/subscriptions with the CLI bearer returns HTTP 200 with a parseable subscriptions payload (that live capture is the test fixture). The cited doc describes a different tool's chosen surface, not a constraint of this token. If this ever regresses in the wild it degrades to an honest login_required/parse_failed state, never wrong data.
| Service::Codex => 0, | ||
| Service::Claude => 1, | ||
| Service::Ollama => 2, | ||
| Service::Grok => 2, |
There was a problem hiding this comment.
Keep plan-only Grok out of gauge rotation
When Grok is enabled (now the default), a successful Grok snapshot still has remaining_percent: None, so including it in tray_states() makes the tray rotate to an empty/unknown gauge and tooltip every cycle even though there is no quota percentage to show. This reduces the usefulness of the primary tray gauge for Codex/Claude users; filter plan-only Grok snapshots from tray rotation or render a distinct plan state instead of an unknown remaining gauge.
Useful? React with 👍 / 👎.
| .get("tier") | ||
| .and_then(Value::as_str) | ||
| .is_some_and(|tier| tier.starts_with("SUBSCRIPTION_TIER_GROK_")) |
There was a problem hiding this comment.
Include X Premium tiers when detecting Grok entitlements
When the user only has an X Premium Plus entitlement, Grok Build is still a valid CLI login (xAI says Grok Build is available to SuperGrok and X Premium Plus), and the fixture already shows X tiers as SUBSCRIPTION_TIER_X_PREMIUM. This predicate rejects every active SUBSCRIPTION_TIER_X_* subscription, so those users get missing_data instead of their plan; accept X Premium/Premium+ tiers or derive this from the CLI settings/billing response. Source checked: https://x.ai/news/grok-build-cli
Useful? React with 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ebd81ac715
ℹ️ 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".
| || snapshot | ||
| .details | ||
| .get("plan") | ||
| .and_then(serde_json::Value::as_str) | ||
| .is_none() |
There was a problem hiding this comment.
Keep Grok-only tray state from falling back to Codex
When a user disables Codex/Claude and leaves only Grok enabled, a successful Grok CLI snapshot has a plan but no remaining_percent, so this filter drops the only snapshot. tray_states() then falls through to the existing empty-state fallback and returns Codex with unknown remaining, causing the tray tooltip/icon rotation to show the wrong service even though Grok is enabled and parsed successfully.
Useful? React with 👍 / 👎.
| entries.values().find_map(|entry| { | ||
| entry | ||
| .get("key") | ||
| .and_then(Value::as_str) |
There was a problem hiding this comment.
Prefer the current Grok auth entry before legacy bearers
When auth.json contains both the current https://auth.x.ai::<client> entry and an older https://accounts.x.ai/sign-in entry, serde_json::Map is key-ordered here, so .values().find_map(...) visits the legacy entry first and can return an expired/less-capable bearer. In that state PickGauge reports login_required even though the Grok CLI has a valid current login; look up the known current scope key before falling back to legacy entries.
Useful? React with 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5255b6ad26
ℹ️ 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 ringColors: Record<Service, string> = { | ||
| codex: "var(--text)", | ||
| claude: "var(--ember)", | ||
| grok: "var(--ember)", |
There was a problem hiding this comment.
Hide plan-only Grok from the float gauge
When the Grok CLI provider succeeds it emits a plan-only snapshot (details.plan is set, but remainingPercent is null). Adding Grok to the float ring path means the existing {#each snapshots} below renders that snapshot as an extra –/unknown mini-gauge whenever the floating button is enabled, even though the backend tray logic deliberately filters successful plan-only Grok snapshots. Filter those plan-only Grok snapshots out of the float display until Grok has an actual usage percentage.
Useful? React with 👍 / 👎.
| #[serde(default = "default_true")] | ||
| pub grok: bool, |
There was a problem hiding this comment.
Keep Grok opt-in for migrated configs
For existing users, their saved config.json has no enabledServices.grok; this default makes it load as enabled, and with the existing default/likely cliEnabled setting the next refresh registers grok.cli, reads ~/.grok/auth.json, and contacts grok.com even though the user never opted into this new service. Given the app's privacy boundary around not widening what it touches unexpectedly, migrated configs should default Grok off (or require an explicit toggle) while new installs can still choose the desired default separately.
Useful? React with 👍 / 👎.
| let profile_path = match service { | ||
| Service::Codex => &paths.codex, | ||
| Service::Claude => &paths.claude, | ||
| Service::Grok => unreachable!("Grok has no managed browser profile"), | ||
| Service::Ollama => &paths.ollama, |
There was a problem hiding this comment.
Reject manual Grok web refreshes before browser setup
Now that Service can be Grok, a caller can invoke the public refresh_provider command with { service: "grok", source: "web" } (the TS API type allows it). That path sends every Web refresh through refresh_web_provider_headless, reaches this unreachable!, panics the blocking task, and skips the normal failed refresh cleanup/event flow; it should return the same explicit “web provider unavailable” error used for other Grok browser commands instead of panicking on user input.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
False positive on the code path: refresh_provider(grok, web) → refresh_web_provider_headless passes the sidecar call as a closure into refresh_provider_source_with_snapshot, whose first guard is UsageProviderId::for_service_source(Grok, Web) → None → returns "Provider source cannot be refreshed directly" (usage.rs:626) before the closure containing the unreachable! is ever invoked. The command returns a clean error, no panic. The TS type allowing the call is fine — it gets an explicit error, same as other unsupported combos (e.g. ollama+local before this PR series).
|
@codex review |
|
Codex Review: Didn't find any major issues. Keep them coming! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Adds Grok as a fourth PickGauge service with the
grok.clizero-setup provider: reads the bearer thegrokCLI already stored in~/.grok/auth.json(read-only) and callsGET grok.com/rest/subscriptionsonce per refresh to show the active plan + billing-period end as a plan-only card. No usage percentages yet — Grok's weekly quota numbers are cookie-gated and land with the web provider PR.Refs #26 (PR 1 of the 4-PR plan; PR 2 = Ollama zero-setup is #27, PR 3 web full-gauges and PR 4 headless export remain).
What changed
Service::Grok+UsageProviderId::GrokCli(grok.cli); collector incli_provider.rs; registered when Grok + CLI readings are enabled. No web provider, browser profile, or 5-hour window for Grok anywhere (Grok is weekly-only, and this PR is plan-only).confidence: medium,details.plan(+billingPeriodEnd);reset_atstays null (billing renewal is not a quota reset).config.jsonfiles without thegrokkey parse fine and default to enabled (#[serde(default)]+ test).Tested
cargo test --locked --all-targets: 232 passed.bun run checkclean;bun run test: 61 + 15 sidecar passed.GET /rest/subscriptionsHTTP 200, parsed plan "Grok Pro".Not tested yet
login_requiredmapping only).Risks
rest/subscriptionsis an undocumented endpoint; shape drift fails asparse_failed/missing_data, honestly surfaced.