fix(usage): consumo do Claude (plano Team, 429, reset, GLM dup) + tile Extra + botão force#27
Conversation
|
Warning Review limit reached
Next review available in: 10 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (10)
WalkthroughO PR amplia o mapeamento de planos Claude, trata HTTP 429 com ChangesUso do Claude e rate limiting
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Overlay
participant preload
participant main
participant collectUsage
participant ClaudeAPI
Overlay->>preload: chama forceUsage()
preload->>main: envia evento force-usage
main->>collectUsage: solicita coleta
collectUsage->>ClaudeAPI: consulta uso do Claude
ClaudeAPI-->>collectUsage: HTTP 429 e Retry-After
collectUsage-->>main: retorna último valor e cooldown
main-->>preload: envia usage-meta
preload-->>Overlay: atualiza tooltip e estado do botão
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@main.js`:
- Around line 1178-1184: Preserve the last valid Claude usage data when forcing
a refresh during an active cooldown. Update the force-usage handler and the
cache-clearing logic used by _clearClaudeCache/readClaudeUsage so Claude cache
entries are not removed while claudeCooldownUntil is still in the future; only
invalidate them when no cooldown is active, while retaining the existing GLM and
Codex behavior.
In `@src/renderer.js`:
- Around line 643-663: O listener de clique do botão $forceUsage permite
chamadas durante o cooldown. Em applyUsageMeta e no handler de click, verifique
claudeCooldownUntil contra Date.now() antes de chamar
window.trafficLight.forceUsage(), atualizando o estado visual sem iniciar o
spinner quando bloqueado; mantenha a chamada e o feedback atuais apenas fora do
cooldown.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5229d659-fe9d-4c5e-ae12-8ab061105354
📒 Files selected for processing (10)
CHANGELOG.mdmain.jspreload.jssrc/i18n.jssrc/index.htmlsrc/renderer.jssrc/styles.csssrc/usage.jstest/rename.test.jstest/usage.test.js
✅ Files skipped from review due to trivial changes (1)
- src/i18n.js
|
Ambos os achados eram válidos e foram corrigidos em 843667e: 1. 2. Verificado E2E: |
Duas causas combinadas faziam o tile de consumo do Claude desaparecer do overlay: 1. Contas não-Max (Team, tier interno default_raven) não eram reconhecidas — o rótulo do plano virava null e o tile sumia. Agora parseClaudeConfig reconhece claude_team/claude_pro/claude_enterprise e cai num rótulo genérico "Claude" para qualquer conta presente mas não mapeada; só retorna null quando NÃO há conta OAuth. 2. Ao levar HTTP 429 da API de uso, o loop de 60s rebatia na mesma janela e renovava a penalidade indefinidamente — o % nunca voltava. Agora um 429 agenda um cooldown que respeita o header Retry-After (fallback 15 min): durante ele o coletor não bate na API e mantém o último valor conhecido (ou o plano-só), sem sumir nem piscar. Testes: 165 → 175 (novos casos p/ plano Team, parseRetryAfter e o cooldown do 429).
…cooldown persistido) O 429 da API de uso escalava o Retry-After a cada toque (1007s → 2871s): em dev com `bun start`, cada restart perdia o cooldown em memória, re-batia no boot e re-armava a punição. - Cache próprio do Claude de 5 min (antes usava o global de 30s): a API /api/oauth/usage é fortemente rate-limited e as janelas são de 5h/7d, então consultar a cada 60s era desperdício e pressão desnecessária. - Cooldown do 429 persistido em disco (claude-cooldown.json, SÓ o timestamp — nunca o token): sobrevive a restart, então boot em dev não re-bate nem re-escala. Injetado via collectUsage → readClaudeUsage. Testes: 175 → 178.
Botão ⟳ que fura o cache de conveniência (5min) e recoleta na hora. É SEGURO: respeita o cooldown do 429 persistido — durante a janela de rate limit fica apagado com tooltip "aguarde Xmin" e NÃO bate na API, evitando re-escalar a penalidade. - index.html: botão ⟳ no header (SVG refresh) - main.js: handler force-usage (limpa caches + recoleta), emite usage-meta com claudeCooldownUntil p/ o tooltip do botão - renderer: applyUsageMeta troca data-tip (↻↔"aguarde Nmin"), classe .is-cooldown apaga o botão, .is-spinning gira no clique (feedback) - i18n pt/en: tooltip_force_usage + tooltip_force_cooldown - mock do teste atualizado (onUsageMeta/forceUsage) 178/178 verdes; verificado ponta a ponta: force limpa cache e recupera o %; force durante o 429 faz 0 chamadas à API.
Dois bugs do consumo do Claude:
1. Reset do plano sumia no tile plano-só (Claude Team durante o 429).
O planLimitsEndDate (reset semanal) estava no .claude.json e o
parseClaudeConfig lia, mas claudePlanLabel descartava tudo e o
planOnly hardcodeava resetAt:null. Refatorado p/ readClaudeConfig
devolver o objeto completo; o tile agora mostra o reset + passes.
2. 429 escalava indefinidamente. O endpoint /api/oauth/usage é
compartilhado com o próprio Claude Code (/status) — limite agregado.
Cada cooldown que expirava e rebatia recebia Retry-After cada vez
maior. Backoff exponencial: a cada 429 seguido, alonga a espera
(Retry-After × 1.5^fails, teto 1h) e persiste {until, fails}.
Testes: 178 → 182.
…At 1ms diferente)
A mesma conta z.ai Pro chegava por 2 credenciais (tokens distintos,
sufixos 6baca3 e c3c374) — ex.: /proc + OpenCode auth.json. A API
devolve resetAt com ~1ms de diferença ("...09.995Z" vs "...09.996Z"),
e a chave de dedup por conteúdo usava o resetAt exato → não colapsava
→ "MCP (mês) 2×".
Agora a chave normaliza resetAt p/ segundos (floor(parse/1000), tolera
sub-segundo) sem colapsar contas realmente distintas (resets >1s apart).
Teste reproduz as 3 entradas reais e confirma colapso p/ 2. 182 → 183.
…noso) O planLimitsEndDate (ex.: Jul 13) é o fim do ciclo do PLANO, não o reset da janela de uso 5h/7d — que reseta várias vezes até lá. O commit anterior o pôs no tile plano-só e mostrava "3d" logo após a janela ter resetado (o usuário resetou às 12pm e via "3d"). Agora o tile plano-só não expõe reset (honesto: sem a API OAuth não sabemos o reset da janela); passes continua (info local legítima). Quando a API responde, o resets_at real de cada janela já vem via parseAnthropicUsage.
Investigação com payload REAL da conta Team (API respondeu 200 após o 429 sarar) revelou duas coisas: 1. As credenciais (~/.claude/.credentials.json) têm rateLimitTier e subscriptionType — fonte MAIS confiável do plano que o .claude.json (que trazia o tier opaco 'default_raven'). Agora o plano é resolvido primeiro pelas credenciais → "Claude Max 5×" em vez do genérico. 2. O payload traz extra_usage (overage medido em $): a conta tinha used_credits 5042 / monthly_limit 5000 = 100% ($50.4/$50). Novo tile "Extra" (id claude-extra, não colide com 7d) mostra % + valor, respeitando currency/decimal_places. Confirmado E2E que o parse das janelas 5h/7d sempre esteve correto — o % só não vinha por causa do 429 (não era bug de schema). 183 → 188.
… à toa) Dois achados do review (CodeRabbit) na feature do botão de force: 1. main.js: force-usage limpava o cache do Claude incondicionalmente. Clicar ⟳ DURANTE o cooldown zerava o cache → readClaudeUsage respeitava o cooldown (não batia na API, correto) mas perdia o fallback e caía em plano-só — o tile perdia o % só pelo clique. Agora só limpa o cache do Claude FORA do cooldown (GLM/Codex sempre). 2. renderer.js: o botão ficava apagado (.is-cooldown) mas continuava clicável — o clique disparava spinner falso e forceUsage() à toa. Agora o clique é bloqueado durante o cooldown (o tooltip já explica). E2E confirma: force no cooldown preserva 5h/7d reais (não regride). 188 verdes.
843667e to
41c600f
Compare
Contexto
O consumo do Claude parou de aparecer no overlay depois que a conta migrou para Team. A investigação (com payload REAL da API, não palpite) revelou uma cadeia de causas — a raiz era o plano não reconhecido + rate-limit (429) do endpoint de uso, que é compartilhado com o próprio Claude Code (
/status).Correções
Reconhecimento de plano
~/.claude/.credentials.json:rateLimitTier/subscriptionType) — fonte do tier REAL (default_claude_max_5x), enquanto o.claude.jsontrazia o tier opacodefault_raven. Rótulo fica preciso ("Claude Max 5×").claude_team/claude_pro/claude_enterprise; rótulo genérico "Claude" para conta presente mas não mapeada.nullsó quando não há conta.Rate-limit (429) — o motivo do "% não vinha"
Retry-After × 1.5^fails, teto 1 h): o endpoint é compartilhado com o Claude Code, então rebater a cada expiração só escalava a punição (observado1007s → 2871s).claude-cooldown.json,{until, fails}— nunca o token): sobrevive a restart, entãobun start/dev não re-bate no boot nem re-escala.parseRetryAfter(delta-seconds ou HTTP-date).Reset correto
planLimitsEndDatecomo reset da janela (é o fim do ciclo do plano, não da janela 5h/7d — mostrava "3d" logo após a janela resetar). O reset real vem doresets_atde cada janela via API.GLM duplicado
resetAtdiferindo ~1ms não colapsava → "MCP (mês) 2×". A chave de dedup agora normalizaresetAtpara segundos.Novas features
Botão "atualizar uso agora" (⟳)
Força a recoleta (fura o cache de conveniência de 5 min). Seguro: respeita o cooldown do 429 — fica apagado com tooltip "aguarde Xmin" e não bate na API durante o rate-limit.
Tile "Extra" (overage)
Contas com crédito extra medido em dinheiro (
extra_usage) ganham um tile mostrando % + valor (ex.:$50.4/$50.0), respeitandocurrency/decimal_places. Descoberto no payload real: a conta tinha overage estourado (100%).Verificação
%só não vinha por causa do 429 (não era bug de schema). Quando a API respondeu 200:five_hour 7%,seven_day 24%,extra_usage 100%— exatamente o que o overlay passa a mostrar.collectUsagecom o payload real:Claude Max 5× · 5h 7% / 7 dias 24% / Extra 100% $50.4/$50.0.Arquivos
src/usage.js,main.js,preload.js,src/renderer.js,src/index.html,src/styles.css,src/i18n.js,test/usage.test.js,test/rename.test.js,CHANGELOG.md