docs/claude-comparison-since-0.18.0beta2.md
7b79b2d vs HEAD)This document compares Claude data fetching behavior between:
7b79b2d080c6b00f6c8f52f89ac115f33a7ca8b0HEAD: 37841489f849567a598d2a8ba601eb6f1228644eFocus areas:
LAContext.interactionNotAllowed) before any promptable path.invalid_grant => terminal block until auth fingerprint changes.400/401 (non-invalid_grant) => transient exponential backoff (up to 6h).7b79b2d)flowchart TD
A["Claude OAuth fetch starts"] --> B["load(environment)"]
B --> C{"token found?"}
C -- "no" --> K["OAuth not available / fail"]
C -- "yes" --> D{"token expired?"}
D -- "yes" --> E["Fail: token expired (run claude)"]
D -- "no" --> F{"has user:profile scope?"}
F -- "no" --> G["Fail: missing scope"]
F -- "yes" --> H["GET /api/oauth/usage with Bearer access token"]
H --> I{"HTTP success?"}
I -- "yes" --> J["Map usage response -> snapshot"]
I -- "no" --> L["Invalidate cache, surface OAuth error"]
HEAD)flowchart TD
A["Claude OAuth fetch starts"] --> B["hasCachedCredentials?"]
B --> C{"cached/refreshable creds exist?"}
C -- "yes" --> D["loadWithAutoRefresh(allowKeychainPrompt=false unless bootstrap needed)"]
C -- "no" --> E["Gate: should allow keychain prompt now?"]
E --> F["loadWithAutoRefresh(allowKeychainPrompt=true if gate allows)"]
D --> G{"expired?"}
F --> G
G -- "no" --> H["Use access token directly"]
G -- "yes" --> I["POST /v1/oauth/token refresh_token grant"]
I --> J{"Refresh status"}
J -- "200" --> K["Save refreshed creds to CodexBar keychain cache + memory"]
J -- "400/401 + invalid_grant" --> L["Record terminal auth failure; block refresh until auth fingerprint changes"]
J -- "400/401 other" --> M["Record transient failure; exponential backoff"]
K --> H
H --> N{"has user:profile scope?"}
N -- "no" --> O["Fail: missing scope"]
N -- "yes" --> P["GET /api/oauth/usage with Bearer access token"]
P --> Q["Map usage response -> snapshot"]
flowchart TD
A["load(...)"] --> B["Environment token (CODEXBAR_CLAUDE_OAUTH_TOKEN)"]
B -->|miss| C["Memory cache (valid + unexpired)"]
C -->|miss| D["CodexBar keychain cache: com.steipete.codexbar.cache/oauth.claude"]
D -->|miss| E["~/.claude/.credentials.json"]
E -->|miss| F{"allowKeychainPrompt && prompt gate open?"}
F -- "yes" --> G["Claude keychain service: Claude Code-credentials (promptable fallback)"]
F -- "no" --> H["Stop without keychain prompt path"]
flowchart TD
A["Claude Web fetch starts"] --> B{"manual cookie header configured?"}
B -- "yes" --> C["Extract sessionKey from manual header"]
B -- "no" --> D["Enumerate cookie import candidates"]
D --> E["Try browser cookie import (claude.ai domain)"]
E --> F{"sessionKey found?"}
C --> F
F -- "no" --> G["Fail: noSessionKeyFound"]
F -- "yes" --> H["GET organizations + usage endpoints"]
H --> I["Build web usage snapshot + identity + optional cost extras"]
flowchart TD
A["cookieImportCandidates(...)"] --> B{"browser uses keychain for decryption?"}
B -- "no (Safari/Firefox/Zen)" --> C["Keep candidate even if keychain disabled"]
B -- "yes (Chromium family)" --> D{"Keychain disabled?"}
D -- "yes" --> E["Drop candidate"]
D -- "no" --> F["Check BrowserCookieAccessGate cooldown"]
F --> G{"cooldown active?"}
G -- "yes" --> E
G -- "no" --> H["Attempt import; on access denied record 6h cooldown"]
.cli)flowchart TD
A["CLI command (provider=claude)"] --> B{"source mode"}
B -- "auto" --> C["Strategy order: web -> cli"]
B -- "web" --> D["Web strategy only"]
B -- "cli" --> E["CLI PTY strategy only"]
B -- "oauth" --> F["OAuth strategy only"]
E --> G["ClaudeStatusProbe via PTY session"]
G --> H["Parse /usage output -> snapshot"]
Notes:
ANTHROPIC_* vars before launching the subprocess.flowchart TD
A["App runtime source=auto"] --> B["Try OAuth strategy first"]
B -->|"available + success"| Z["Done"]
B -->|"unavailable or fallback on error"| C["Try Web strategy"]
C -->|"success"| Z
C -->|"unavailable or fallback on error"| D["Try CLI strategy"]
D -->|"success"| Z
D -->|"fail"| E["Provider fetch failure"]
ClaudeUsageFetcher(dataSource:.auto) Heuristic (Now)flowchart TD
A["ClaudeUsageFetcher.loadLatestUsage(dataSource=.auto)"] --> B["Probe OAuth creds non-interactively"]
B --> C{"usable OAuth creds?"}
C -- "yes" --> D["Use OAuth path"]
C -- "no" --> E{"has web session?"}
E -- "yes" --> F["Use Web path"]
E -- "no" --> G{"claude binary present?"}
G -- "yes" --> H["Try CLI PTY, if fails then OAuth"]
G -- "no" --> I["Fallback to OAuth"]
stateDiagram-v2
[*] --> PromptAllowed
PromptAllowed --> CooldownBlocked: recordDenied()
CooldownBlocked --> CooldownBlocked: shouldAllowPrompt() before deniedUntil
CooldownBlocked --> PromptAllowed: time >= deniedUntil (6h)
stateDiagram-v2
[*] --> Open
Open --> TransientBackoff: recordTransientFailure (400/401 non-invalid_grant)
TransientBackoff --> Open: cooldown expires
TransientBackoff --> Open: auth fingerprint changes
Open --> TerminalBlocked: recordTerminalAuthFailure (invalid_grant)
TerminalBlocked --> TerminalBlocked: unchanged auth fingerprint
TerminalBlocked --> Open: auth fingerprint changes
TransientBackoff --> TerminalBlocked: terminal auth failure observed
TerminalBlocked --> Open: recordSuccess
/Users/ratulsarna/Developer/staipete/CodexBar/Sources/CodexBarCore/Providers/Claude/ClaudeUsageFetcher.swift/Users/ratulsarna/Developer/staipete/CodexBar/Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift/Users/ratulsarna/Developer/staipete/CodexBar/Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift/Users/ratulsarna/Developer/staipete/CodexBar/Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthKeychainAccessGate.swift/Users/ratulsarna/Developer/staipete/CodexBar/Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthRefreshFailureGate.swift/Users/ratulsarna/Developer/staipete/CodexBar/Sources/CodexBarCore/KeychainAccessPreflight.swift/Users/ratulsarna/Developer/staipete/CodexBar/Sources/CodexBarCore/KeychainNoUIQuery.swift/Users/ratulsarna/Developer/staipete/CodexBar/Sources/CodexBarCore/BrowserCookieImportOrder.swift/Users/ratulsarna/Developer/staipete/CodexBar/Sources/CodexBarCore/BrowserCookieAccessGate.swift