Skip to content

Releases: drogers0/aistat

v2.2.0

09 Jun 18:41
4eddc7a

Choose a tag to compare

Changelog

  • d87a40e Merge pull request #18 from drogers0/fix-switch-auto-pick-no-5h-window
  • 4eddc7a Merge pull request #19 from drogers0/copilot-ai-credits-billing
  • 5ad6fbe feat(copilot): report AI-credit usage from copilot_internal/user
  • 096942d fix(switch): rank accounts with no 5h window by real headroom

v2.1.3

04 Jun 00:50
2e6a460

Choose a tag to compare

Changelog

  • 2e6a460 Merge pull request #17 from drogers0/fix-codex-refresh-expiry
  • df88099 fix(codex): gate refresh on access-token expiry, not the id_token

v2.1.2

01 Jun 23:14
484c005

Choose a tag to compare

Highlights

  • Codex multi-account support. aistat usage now reports per-account rows for Codex (mirroring Claude's nested format), and aistat switch codex rewrites the live ~/.codex/auth.json between stored ChatGPT accounts without a browser round-trip. Identity is resolved from the id_token sub claim (JWT decode, no network).
  • Slot-vs-duration window labeling. Codex windows are labeled by limit_window_seconds, not slot position — so free-account windows (including OpenAI's recent weekly→monthly quota change) are correctly labeled thirty_day instead of being assumed to be the primary 5-hour window.
  • Provider-scoped switch / accounts CLI with bulk-on-omission: aistat switch [provider] [--to <id>], aistat accounts list|remove [provider]. Omitting the provider fans out to every provider with ≥2 stored accounts (auto-pick best by headroom).

Codex UX

  • Tighter, actionable errors for upstream OAuth failures matched to the real upstream bodies: the refresh-token rotation race (codex login to recover) and same-client token revocation (token_revoked / token_invalidated).

Fixes

  • fsync the live-credential write on Linux; fail closed on resolver error in accounts remove (#13).

Internal

  • Test suite migrated to table-driven t.Run subtests with shared testutil helpers and externalized golden output — no behavior change. See TESTING.md.

Full changelog: v2.1.1...v2.1.2

v2.1.1

29 May 22:37
5ca1bc7

Choose a tag to compare

Changelog

  • 35da414 README: polish header, add examples and one-line installer (#11)
  • 3bd99cc claude: send claude-code/ User-Agent, bump usage cache TTL to 90s (#10)
  • 5ca1bc7 installer: clearer success output + opt-in PATH edit with consent (#12)

v2.1.0

28 May 21:03
ba00f33

Choose a tag to compare

Changelog

  • 540000f CLAUDE.md: refresh for v2.1.0
  • ba00f33 Merge pull request #6 from drogers0/v2.1.0-foundation
  • e5a5574 README: trim caching section justification down to user-facing facts
  • 429b1b4 accounts: lock on a sentinel file on Linux, not the data file
  • b75faa1 accounts: move safeWriter into store_darwin.go
  • cda9d90 backoff + usage cache + darwin keychain index fix
  • 1e80677 claude: drop the top-level limits mirror and AccountResult.uuid from JSON
  • d0a3092 claude: extract recordFetchOutcome for the D8 classification
  • 79c83c7 claude: keep AccountResult.Limits in JSON even when nil/empty
  • f02b67e claude: route switch's active-usage fetch through Client.FetchUsage
  • 542e5c1 claude: unify the switch and reporting usage-read paths through the cache
  • a979896 cmd/aistat: extract handleGlobals helper
  • bcc17df cred/darwin: drop the partition-list step from WriteClaudeLiveBlob
  • 9038dd1 v2.1.0: multi-account Claude reporting + aistat switch

v2.0.0

28 May 00:44

Choose a tag to compare

Changelog

  • 2e27430 Code-review hardening (round 10, simplicity-first): five residual findings
  • ab8e666 Code-review hardening (round 2): always-on warns, --fake build tag, real UA, dedupe
  • 8136765 Code-review hardening (round 5): copilot truncation + classifier split
  • 970eb63 Code-review hardening (round 6): exit-code split + ten findings
  • 1ab989a Code-review hardening (round 7): ctx-cancel fix + flag grammar + eight findings
  • 8a504ce Code-review hardening (round 8) + rename to aistat
  • 9118a73 Code-review hardening (round 9, simplicity-first): 33 REVIEW.md findings
  • 277efc4 Code-review hardening: shared httpx, strict CLI grammar, CI/release
  • df14f18 Merge pull request #5 from drogers0/v2-rewrite
  • e5708be ci: pin go-version to 1.22.x and bump staticcheck to 2025.1.1
  • 5812126 cmd/usage-check: extract buildProviders; tighten realProviders signature
  • b95beed cmd/usage-check: tighten safeStderr parameter type
  • 67cec56 copilot: fail closed on unknown plan slug
  • dcb7221 cred: drop linux perm check; wrap unsupported-platform error
  • 9d5cbf7 cred: support Claude on Linux; share parser between platforms
  • 90c1607 go.mod: migrate module path to github.com/drogers0/aistat/v2
  • 5b6b3cc httpx: reserve Authorization and User-Agent from ExtraHeaders
  • 24a74ac providers, httpx: shared issue URL constant; limit response body to 1 MiB
  • 8c5d1bc providers: extract ProjectURL constant
  • c64e213 providers: unify on ctx-based timeouts; codex KnownWindows tripwire
  • 45dcf83 render/text: drop redundant empty-requested guard
  • 6cf1528 render: short-circuit Text on empty section list
  • 9659f5e tests: warn-wiring + textLabels drift tripwires
  • abd4e4d v2.0.0: rewrite as a single static Go binary

v1.0.0 — TypeScript + Chrome extension (reference implementation)

26 May 19:56

Choose a tag to compare

v1.0.0 — TypeScript + Chrome extension (reference implementation)

This is the stable, feature-complete state of the original llm-usage architecture: a TypeScript CLI that reads provider usage data by driving an authenticated Chrome browser session through an MV3 extension and a native messaging host.

It's tagged at the same commit as v0.0.3. Bumping to v1.0.0 marks this as the canonical reference for this architecture — v2.0.0 will be a Go rewrite that drops the browser pipeline entirely.

What this implementation does

usage-check invokes a Chrome extension via AppleScript, opening a hidden 1×1 window with tabs to claude.ai, chatgpt.com, and github.com. Each provider tab runs a scripted fetch() (or DOM scrape, for Copilot) inside the page context — inheriting the user's existing browser session cookies — and the result is relayed back through a native messaging host into a JSON cache file that the CLI renders.

Why it exists this way

When this tool was built, the only path to per-account usage data went through endpoints that required an authenticated web session:

  • claude.ai/api/organizations/<org>/usage is Cloudflare-fronted and rejects non-browser traffic even with a valid bearer token
  • chatgpt.com/backend-api/wham/usage accepts a bearer but the path was undocumented
  • GitHub Copilot exposed quota only via the rendered /settings/billing page

A direct CLI HTTP client couldn't reach any of them. Hence: drive a real Chrome instance, reuse the user's session cookies, shuttle results out over native messaging.

What's reusable for other scraping projects

If you need to scrape a Cloudflare-protected or cookie-gated surface from a CLI on macOS, the pattern here is a working blueprint:

  • AppleScript trigger (bin/usage-checkosascript) wakes a hidden Chrome window — keeps Chrome backgrounded, no Dock icon flash
  • MV3 extension with deterministic ID via manifest key (extension/manifest.json) — avoids the "paste your extension ID" install step
  • Per-provider abstraction (src/extension/providers/base.ts + claude.ts, codex.ts, copilot.ts) — clean place to add new sites
  • Hidden-tab patternchrome.windows.create({state:"minimized",width:1,height:1}) then chrome.scripting.executeScript into provider tabs
  • Native messaging host (native-host/) registered at ~/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.llm_usage.cache_host.json — extension ↔ host stdio with framed JSON, host writes to disk, CLI reads cache file
  • Install flow (install.sh, usage-check-setup) — packages the extension load, native host registration, and EXTENSION_ID resolution into one command

What to redo if you fork the pattern

  • The AppleScript shim is the flakiest piece — a small Swift binary or JXA script would be more robust
  • The native messaging host adds setup friction; a localhost HTTP relay (see the unmerged http-cache-server branch in this repo) is a simpler approach if the local-port security tradeoff is acceptable
  • CLI polling the cache file is racy under load — the HTTP relay path also fixes this

Limitations

  • macOS only (AppleScript)
  • Chrome only (MV3 + native messaging registration is Chrome-specific)
  • Requires the user to be logged into all three services in Chrome
  • Cloudflare can change challenge behavior at any time and break the Claude path silently
  • Extension install requires Developer Mode in chrome://extensions

Why v2.0.0 is coming

After this release shipped, it turned out the on-disk OAuth tokens that each vendor's CLI (claude, codex, gh) already stores can be used directly against:

  • api.anthropic.com/api/oauth/usage (verified — returns the same shape as the claude.ai endpoint)
  • chatgpt.com/backend-api/wham/usage (the same endpoint the extension hits, just with the bearer instead of a cookie)
  • GitHub's billing API via gh api using the existing gh auth token

No browser, no cookies, no AppleScript, no Cloudflare exposure. v2.0.0 will be a pure Go CLI that reads those tokens and makes three HTTP calls. The entire extension/, native-host/, install.sh, and AppleScript shim are deleted.

Branches preserved as references

  • http-cache-server — single-commit experiment replacing the native messaging host with a localhost HTTP relay. Kept as a reference for that simpler approach.
  • PR #3 (issue-2-service-worker-startup) — draft work on MV3 service-worker startup wake. Obsoleted by the v2.0.0 rewrite (no service worker exists in the Go version).

v0.0.3

30 Apr 17:30

Choose a tag to compare

Fix usage-check crash on macOS Bash 3.2 when no renderer args (PR #4).

v0.0.2

26 Mar 23:56

Choose a tag to compare

Add remaining % and reset duration to text output

v0.0.1

20 Mar 19:39

Choose a tag to compare

Initial packaged release. Install with:

npm install -g https://github.com/drogers0/llm-usage/releases/download/v0.0.1/llm-usage-0.0.1.tgz
usage-check-setup