v1.0.0 — TypeScript + Chrome extension (reference implementation)
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>/usageis Cloudflare-fronted and rejects non-browser traffic even with a valid bearer tokenchatgpt.com/backend-api/wham/usageaccepts a bearer but the path was undocumented- GitHub Copilot exposed quota only via the rendered
/settings/billingpage
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-check→osascript) 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 pattern —
chrome.windows.create({state:"minimized",width:1,height:1})thenchrome.scripting.executeScriptinto 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-serverbranch 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 theclaude.aiendpoint)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 apiusing the existinggh authtoken
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).