feat: add claude.ai API client and session key configuration

Introduce a client for fetching subscription data from the claude.ai web
API, enabling rate-limit monitoring and overage tracking in the dashboard.

New package internal/claudeai:
- Client authenticates via session cookie (sk-ant-sid... prefix validated)
- FetchAll() retrieves orgs, usage windows, and overage in one call,
  returning partial data when individual requests fail
- FetchOrganizations/FetchUsage/FetchOverageLimit for granular access
- Defensive utilization parsing handles polymorphic API responses: int
  (75), float (0.75 or 75.0), and string ("75%" or "0.75"), normalizing
  all to 0.0-1.0 range
- 10s request timeout, 1MB body limit, proper status code handling
  (401/403 -> ErrUnauthorized, 429 -> ErrRateLimited)

Types (claudeai/types.go):
- Organization, UsageResponse, UsageWindow (raw), OverageLimit
- SubscriptionData (TUI-ready aggregate), ParsedUsage, ParsedWindow

Config changes (config/config.go):
- Add ClaudeAIConfig struct with session_key and org_id fields
- Add GetSessionKey() with CLAUDE_SESSION_KEY env var fallback
- Fix directory permissions 0o755 -> 0o750 (gosec G301)
- Fix Save() to propagate encoder errors before closing file
This commit is contained in:
teernisse
2026-02-20 16:07:26 -05:00
parent 892f578565
commit 547d402578
3 changed files with 329 additions and 18 deletions

View File

@@ -0,0 +1,59 @@
package claudeai
import (
"encoding/json"
"time"
)
// Organization represents a claude.ai organization.
type Organization struct {
UUID string `json:"uuid"`
Name string `json:"name"`
Capabilities []string `json:"capabilities"`
}
// UsageResponse is the raw API response from the usage endpoint.
type UsageResponse struct {
FiveHour *UsageWindow `json:"five_hour"`
SevenDay *UsageWindow `json:"seven_day"`
SevenDayOpus *UsageWindow `json:"seven_day_opus"`
SevenDaySonnet *UsageWindow `json:"seven_day_sonnet"`
}
// UsageWindow is a single rate-limit window from the API.
// Utilization can be int, float, or string — kept as raw JSON for defensive parsing.
type UsageWindow struct {
Utilization json.RawMessage `json:"utilization"`
ResetsAt *string `json:"resets_at"`
}
// OverageLimit is the raw API response from the overage spend limit endpoint.
type OverageLimit struct {
IsEnabled bool `json:"isEnabled"`
UsedCredits float64 `json:"usedCredits"`
MonthlyCreditLimit float64 `json:"monthlyCreditLimit"`
Currency string `json:"currency"`
}
// SubscriptionData is the parsed, TUI-ready aggregate of all claude.ai API data.
type SubscriptionData struct {
Org Organization
Usage *ParsedUsage
Overage *OverageLimit
FetchedAt time.Time
Error error
}
// ParsedUsage holds normalized usage windows.
type ParsedUsage struct {
FiveHour *ParsedWindow
SevenDay *ParsedWindow
SevenDayOpus *ParsedWindow
SevenDaySonnet *ParsedWindow
}
// ParsedWindow is a single rate-limit window, normalized for display.
type ParsedWindow struct {
Pct float64 // 0.0-1.0
ResetsAt time.Time
}