Ajouter l'agent claude-usage : limites du plan, cache local et envoi #50
Labels
No labels
autopilot:pending-human
source:analyste
source:defenseur
source:human
source:medic
status:approved
status:blocked
status:in-progress
status:needs-clarification
status:needs-fix
status:ready
status:review
status:triage
type:bug
type:feature
type:infra
type:refactor
type:schema
type:security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: maximus/vps-health-api#50
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Refs : spec-decisions-suivi-claude.md + spec-plan-suivi-claude.md (dépôt la-compagnie-maximus). Ce corps est autosuffisant : tout ce qu'il faut pour implémenter est ci-dessous.
Point d'entrée de l'agent lancé toutes les 15 min par le crontab du ThinkPad : lit les limites du plan Claude, maintient le cache des jours figés, assemble le snapshot et le pousse vers
POST /claude-usage.Depends on
Contrat du snapshot (
schemaVersion: 1, figé)Corps de
POST /claude-usage, partagé entrevps-health-api(agent + serveur) etla-compagnie-maximus(dashboard) :Bornes serveur (
sanitizeClaudeUsage, liste blanche, tout champ inconnu jeté) : corps ≤ 64 Ko ;days≤ 31,date^\d{4}-\d{2}-\d{2}$;models≤ 12 clés^[a-z0-9][a-z0-9.\-]{0,63}$;limits.items≤ 12 ;projects≤ 9 ; nombresNumber.isSafeInteger≥ 0,percent0-100 ;statusdans l'enum ;detail^[a-z0-9_]{1,32}$sinon jeté ; autres chaînes ≤ 128 car. ; dates ISO validées. Le serveur ajoutereceivedAt.Réponse de
GET /claude-usage:{ staleAfterSeconds, receivedAt, ageSeconds, stale, snapshot }, ou{ "status": "no_data" }si le fichier est absent ou illisible.Faits sondés (2026-09-27)
~/.claude/.credentials.jsoncontientclaudeAiOauth.{accessToken, refreshToken, expiresAt (ms epoch), ...}. Les refresh tokens sont à usage unique : l'agent ne doit jamais rafraîchir ni écrire ce fichier, sinon Claude Code est déconnecté.GET https://api.anthropic.com/api/oauth/usage(en-têtesAuthorization: Bearer <accessToken>,anthropic-beta: oauth-2025-04-20,User-Agent: claude-code/<version>) renvoie un tableaulimits[]:{kind, group, percent, severity, resets_at, scope: {model: {display_name}} | null, is_active}, ex.session1 %,weekly_all50 %,weekly_scopedFable 63 %. Aussifive_hour/seven_day{utilization, resets_at}et une vingtaine de clés opaquesnull. Endpoint non documenté, rate-limit fort sansRetry-After.Tâches
agent/claude-usage/limits.js:fetchavecredirect: "error", timeout 10 s, un seul appel, aucun retry.CLAUDE_USAGE_LIMITS=off→status: "disabled", aucun appel.unavailable/no_credentials; illisible ou JSON tronqué (réécriture en cours par Claude Code) →unavailable/parse_error, sans retry ni log du contenu ;expiresAtdépassé →token_expired, aucun appel ;claudeCodeVersionnull →unavailable/no_version.limits[]→ items (resets_at→resetsAt,scope.model.display_name→model) ; sinon replifive_hour→{kind:"session", group:"session"}etseven_day→{kind:"weekly_all", group:"weekly"}; tout vide ou forme inattendue →unavailable/bad_response; HTTP non 2xx →http_error/http_<code>; timeout →timeout; autre erreur réseau →network.detailvient uniquement de cet enum, jamais d'un message d'erreur ou d'un corps de réponse.agent/claude-usage/state.js:~/.local/state/maximus-claude-usage/state.json(mode 600, écriture atomique tmp suffixé pid + rename) ; figer tout jour antérieur à hier Toronto, vide compris ({models:{}, projects:{}}) ; élaguer les jours figés de plus de 35 j ;lastGoodLimitsmis à jour seulement surstatus: "ok"et renvoyé avec sonfetchedAtsinon ; fichier illisible → état vide (scan complet) sans échec.agent/claude-usage/push-claude-usage.js: agrège (moduleaggregate.js), met à jour et écrit le cache avant le push, assemble le snapshot, mesure le corps (> 65 536 octets → logpayload_too_large, code 2, pas d'envoi), pousse avecAuthorization: Bearer $CLAUDE_USAGE_INGEST_TOKEN, timeout 10 s, sans retry.CLAUDE_USAGE_API_URLdoit être enhttps:(sinon code 1).--dry-runimprime le snapshot sans l'envoyer. Codes : 0 succès, 1 configuration, 2 push échoué ou payload trop gros. Aucun jeton dans la sortie ; erreurs HTTP journalisées par code, jamais par objet d'erreur brut.agent/claude-usage/run-claude-usage.sh:flock -n ~/.local/state/maximus-claude-usage/run.lock(sortie 0 si un passage tourne déjà) ; source~/.config/maximus-claude-usage.env(CLAUDE_USAGE_API_URL,CLAUDE_USAGE_INGEST_TOKEN,CLAUDE_USAGE_LIMITSetNODE_BINoptionnels) ; même style queagent/run-push.sh.agent/README.md, section dédiée : commandecpexacte deagent/claude-usage/vers~/.local/share/maximus-claude-usage/(copie figée, jamais le working tree), fichier env (mode 600), ligne crontab*/15 * * * * $HOME/.local/share/maximus-claude-usage/run-claude-usage.sh 2>&1 | logger -t claude-usage, et une phrase expliquant pourquoi cet agent vit dansvps-health-api(contrat et sanitizer testés ensemble).Fichiers concernés
agent/claude-usage/limits.js(nouveau)agent/claude-usage/state.js(nouveau)agent/claude-usage/push-claude-usage.js(nouveau)agent/claude-usage/run-claude-usage.sh(nouveau, exécutable)agent/README.md__tests__/agent-claude-usage.test.js__tests__/fixtures/claude-usage/(réponse de l'endpoint anonymisée)Critères d'acceptation
weekly_scopedavecmodel: "Fable";limits[]absent → replifive_hour/seven_day; toutnull→unavailableCLAUDE_USAGE_LIMITS=off, credentials tronqués, credentials absents :fetch(mocké) jamais appeléhttp_error/http_429, items = dernières limites valides avec leurfetchedAtdetailreste dans l'enumAuthorizationdu jeton OAuth n'est envoyé qu'àhttps://api.anthropic.com; aucune variable d'env ne change cette URLfs)CLAUDE_USAGE_INGEST_TOKENn'apparaissent ni dans le snapshot ni dans stdout/stderrstate.jsoncorrompu → passage complet sans échec ; élagage au-delà de 35 jsanitizeClaudeUsage(import de../claude-usage.js)CLAUDE_USAGE_API_URLenhttp:→ code 1 ; snapshot > 64 Ko → code 2 sans envoirun-claude-usage.shlancé deux fois en parallèle : le second sort 0 sans travaillerReview caveats
redirect: "error": le même processus tient le bearer OAuth et une URL de push configurable.flock: le premier scan complet peut chevaucher le passage suivant.User-Agent: claude-code/<version>imite le client officiel sur un endpoint non documenté (un appel / 15 min, interrupteuroff).Décisions prises en planification
~/.local/state/, séparé de la copie figée du code dans~/.local/share/.agent/claude-usage/.Spec source
la-compagnie-maximus :
spec-plan-suivi-claude.md, Issue 3