feat: add account balance status bar item (carries #1970)#2257
Conversation
- Merge upstream Tokens status item with Balance from PR Hmbown#1970 - Keep Balance opt-in (not in default_footer) - Fix clippy: collapsible if, useless format!, redundant closure - Show balance only when total > 0 Co-Authored-By: MoriTang <ts25504@gmail.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
| let currency = match info.currency.as_str() { | ||
| "CNY" | "cny" => "¥", | ||
| _ => "$", | ||
| }; |
There was a problem hiding this comment.
Unknown currency codes silently display a
$ prefix
Any currency that isn't "CNY" or "cny" — including "USD", "EUR", "KRW", etc. — falls through to the _ => "$" arm. This means a hypothetical future "EUR" balance would show as $42.50 rather than €42.50, which is misleading. Matching "USD" explicitly and falling back to the raw code string is safer.
| let currency = match info.currency.as_str() { | |
| "CNY" | "cny" => "¥", | |
| _ => "$", | |
| }; | |
| let currency: std::borrow::Cow<'_, str> = match info.currency.as_str() { | |
| "CNY" | "cny" => "¥".into(), | |
| "USD" | "usd" => "$".into(), | |
| other => other.into(), | |
| }; |
Previously the balance chip only appeared after a completed turn. Now it also fetches: - On first frame (startup) for DeepSeek/DeepSeekCN providers - After switching to DeepSeek, and clears when switching away Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Balance (account remaining) is more actionable than session cost, so it should drop later when the footer is width-constrained. Tier order: status → cost → balance → model (was: status → balance → cost → model) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When a dev build writes new status item variants (e.g. "balance") to config.toml, the stable build must not crash with "unknown variant". Add a tolerant deserializer that filters unrecognized keys via StatusItem::from_key() and logs a warning. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Independent review (Devin): Carry fidelity: confirmed clean carry of #1970 — no scope creep beyond the target feature. Rate-limiting / caching: balance fetches fire on startup (once), on every Bug — hardcoded base URL: Provider-gating: Silent visual regression: status_picker changes Test coverage: good. 7 new v0.8.48 (PR #2256) overlap: high — both PRs touch |
- Use config.deepseek_base_url() instead of hardcoded api.deepseek.com - Add 60-second debounce (BALANCE_FETCH_COOLDOWN) to prevent rapid consecutive balance API calls during provider switches - Fix [x] → [✓] regression in status_picker Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
|
This is a useful statusline direction, thanks for carving it down from the earlier provider work. I am keeping it open because it is currently conflicting, and the balance fetch still appears to run for DeepSeek users on startup/turn/provider switch even when the Balance status item is not enabled. That status chip should feel opt-in, not like surprise network activity. Please rebase, gate the fetch on the Balance item actually being enabled, and then run the focused status picker/footer tests plus a manual custom |
Merge current origin/main into Hmbown#2257 and keep the rescue scoped to the balance statusline feature. Credits Hmbown#2257 by @HUQIANTAO and the carried Hmbown#1970 balance work by @MoriTang. The rescue gates startup, turn-complete, and provider-switch balance fetches on StatusItem::Balance, preserves current main Cargo metadata, and adds the missing Vietnamese balance label.
|
Rescued this on top of current main and pushed What changed:
Verified:
This should address the surprise-network-activity blocker; leaving the PR open for CI/review to settle. |
|
The remaining red macOS leg here is the same unrelated provider-picker test drift ( Local focused verification on the rescued branch passed: |
Summary
https://api.deepseek.com/user/balancefor DeepSeek/DeepSeekCN providers/statusline— does not appear in the default footerChanges from #1970
main(resolved conflicts with upstreamTokensstatus item)footer_balance_spans_empty_when_balance_is_zerotest — zero balance now hides the chipTest plan
cargo fmt --all -- --checkpassescargo clippy --workspace --all-targets --all-features --locked -- -D warningspassescargo test --workspace --all-features --lockedpasses (4 pre-existing failures unrelated)/statuslinetoggle Balance, verify chip appears in footerSupersedes #1970
Greptile Summary
This PR carries forward #1970, adding an opt-in DeepSeek account balance chip to the footer status bar, a
--proxyflag forcodewhale update, and a new Matrix colour theme.StatusItem::Balanceis gated behind/statuslineopt-in and ashould_fetch_deepseek_balanceguard that checks both the provider (Deepseek/DeepseekCN only) and the presence of the status item, so non-DeepSeek and non-opted-in users see no new network traffic. A 60-second cooldown prevents flooding. TheStatusPickerViewnow filters rows by provider so the Balance toggle is invisible for users on other providers.codewhale update --proxy <URL>threads areqwest::Proxythrough all blocking HTTP paths (GitHub release fetch + asset download); mirror-based releases correctly skip the network for metadata.validate_and_build_proxyis well-tested with both valid and malformed URLs.UiThemepalette is added with a special reasoning-colour override and"hacker"as a normalised alias.Confidence Score: 5/5
Safe to merge; all three balance fetch sites are correctly gated behind the opt-in status item and DeepSeek-provider check, so non-opted-in users are unaffected.
The feature is well-scoped and thoroughly tested with 6 balance unit tests and explicit guards at every fetch site. The one logic gap — the else branch in the provider-switch handler can clear a valid balance when rapidly toggling between DeepSeek variants within the 60-second cooldown — is a cosmetic edge case with no data-loss or security implications.
The SwitchProvider handler in crates/tui/src/tui/ui.rs around the balance-clear else branch deserves a second look for the rapid intra-DeepSeek switch case.
Important Files Changed
Sequence Diagram
sequenceDiagram participant User participant App participant should_fetch as should_fetch_deepseek_balance participant tokio as tokio::spawn participant API as DeepSeek /user/balance participant Cell as balance_cell (Mutex) participant Footer as footer_balance_spans User->>App: Start session (DeepSeek + Balance in status_items) App->>should_fetch: check provider + status item should_fetch-->>App: true App->>tokio: spawn background fetch tokio->>API: GET /user/balance (Bearer token) API-->>tokio: BalanceResponse JSON tokio->>Cell: lock + write BalanceInfo User->>App: Send message to TurnComplete App->>should_fetch: check provider + status item + cooldown should_fetch-->>App: true (cooldown 60s+) App->>tokio: spawn background fetch tokio->>API: GET /user/balance API-->>tokio: BalanceResponse JSON tokio->>Cell: lock + write BalanceInfo App->>Footer: render frame Footer->>Cell: lock + read BalanceInfo Cell-->>Footer: Some(BalanceInfo) Footer-->>App: Span bal $42.5 User->>App: switch to Openrouter App->>Cell: lock + clear (None) Footer->>Cell: lock + read Cell-->>Footer: None Footer-->>App: chip hiddenComments Outside Diff (2)
crates/tui/src/tui/views/status_picker.rs, line 801 (link)[✓]→[x]visual regressionThe checked mark was silently changed from the Unicode checkmark
[✓]to a plain ASCII[x]. This isn't listed in the PR description's Clippy fixes and isn't related to the balance feature — it changes the visual appearance of the/statuslinepicker for all users, making checked rows look less distinct. If intentional (e.g. terminal-compat), it should be called out explicitly.crates/tui/src/tui/ui.rs, line 602-652 (link)The PR description explicitly calls the Balance chip "opt-in via
/statusline", yet the balance fetch fires unconditionally on startup (here) and after every turn completion — with no check for whetherStatusItem::Balanceis even present inapp.status_items. A DeepSeek user who has never touched/statuslineand never enabled the Balance chip will silently get periodicGET /user/balancecalls made with their API key on every session start and every turn. Addingapp.status_items.contains(&StatusItem::Balance)as an additional guard in all three fetch sites (startup, turn completion, and provider switch) would make the fetch truly opt-in and consistent with the stated design.Reviews (6): Last reviewed commit: "fix: gate DeepSeek balance fetch opt-in" | Re-trigger Greptile