Files
claude-usage/README.md
Sterister 692244476b docs — remove emojis from README
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 15:58:26 +02:00

5.6 KiB

claude-usage

A Linux system tray indicator that shows your Claude.ai usage — current session (5-hour) and weekly — directly in the panel.

Renders a small ring icon with the percentage, updates every 10 minutes, and has an optional "live" mode that keeps a Chromium instance open and reads the DOM every 15 seconds.

How it works

Claude.ai does not expose a public usage API, so this scrapes https://claude.ai/settings/usage using a logged-in Playwright session:

  • scraper.ts — Playwright + Chromium. On Linux, it uses Xvfb (a virtual display) to run the browser "headed" (required to pass Cloudflare) without showing any windows. The session is persisted to session/state.json so you only need to log in once.
  • tray.py — GTK3 + AyatanaAppIndicator3. Draws the icon with Cairo, calls the scraper via bun, and renders the result as a circular progress ring (green / yellow / red by percent).

Platform support

Component Linux macOS Windows
scraper.ts (CLI) Full Works without Xvfb (browser visible) Works without Xvfb (browser visible)
tray.py (system tray) Full Not supported Not supported

The tray indicator depends on GTK3 + AyatanaAppIndicator3, which is Linux-only. Porting it to Windows or macOS would require swapping in something like pystray (cross-platform tray) or rumps (macOS).

The scraper itself is portable — it's just Bun + Playwright. The only Linux-specific part is the Xvfb wrapper used to hide the browser window during automated polling.


Linux

Requirements

  • bun — installed at ~/.bun/bin/bun (the path is hard-coded in tray.py; edit if yours differs)
  • Python 3 with PyGObject, AyatanaAppIndicator3, cairo
  • Xvfb — virtual display server
  • Playwright Chromium

On Debian/Ubuntu/Mint:

sudo apt install python3-gi gir1.2-ayatanaappindicator3-0.1 python3-cairo xvfb

Install

The tray script expects the repo at ~/.local/share/claude-usage/:

git clone https://gt.zyon.no/Stian/claude-usage.git ~/.local/share/claude-usage
cd ~/.local/share/claude-usage
bun install playwright
bunx playwright install chromium

First login (opens a real Chromium window so you can log in manually):

bun scraper.ts --login

After that, the session is stored in session/state.json.

Run the tray

python3 ~/.local/share/claude-usage/tray.py &

Add it as a startup application in Cinnamon/GNOME so it launches at login.

Tray menu:

  • Session / Weekly — current percent + reset time
  • Update Now — force an immediate refresh
  • Live Mode — switch to 15-second polling (keeps Chromium open in the background)
  • Login — re-run the login flow if your session expires
  • Quit

CLI usage

bun scraper.ts            # pretty output with colors and progress bars
bun scraper.ts --json     # single JSON line — useful for scripts
bun scraper.ts --live     # JSON Lines, one per 15 seconds (Ctrl+C to stop)
bun scraper.ts --login    # open browser for a fresh login

Windows

The tray (tray.py) won't run on Windows. The scraper does, with two changes.

Requirements

  • Bun for Windows (or Node.js — bun is just used as a TypeScript runner)
  • Playwright Chromium: bunx playwright install chromium

Patch the scraper

Two pieces in scraper.ts reference Linux-only Xvfb. On Windows, replace each check() and live() Xvfb spawn block with a plain Chromium launch (drop the Xvfb spawn and the env: { ..., DISPLAY: display } part). The browser window will be visible while it's running — that's the trade-off without Xvfb. If you want it hidden, run Chromium with headless: true and accept that Cloudflare may block you intermittently.

A simpler alternative: only ever invoke --login and --json (one-shot calls), and accept that a browser window flashes up briefly each time.

Run

bun scraper.ts --login        # log in once
bun scraper.ts --json         # poll usage

To poll on a schedule, set up a Task Scheduler job that runs the --json command and pipes the output somewhere useful (a log file, a notification, a Streamdeck button — your choice).

A proper Windows tray port would mean rewriting tray.py against pystray + Pillow, which isn't done here.


macOS

Same story as Windows: the scraper works (no Xvfb needed — just remove the Xvfb spawn), the tray does not. Use rumps if you want a macOS menubar version.


Output format

{
  "sessionUsed": 42,
  "sessionResets": "Mon 14:00",
  "weeklyUsed": 18,
  "weeklyResets": "Tue 09:00",
  "sonnetUsed": 12,
  "extraSpent": "0.00",
  "loggedIn": true
}

If the session has expired: {"loggedIn": false} and exit code 1.

Files

~/.local/share/claude-usage/
├── scraper.ts          # Playwright scraper
├── tray.py             # GTK tray indicator (Linux)
└── session/
    └── state.json      # Playwright storage state (cookies) — gitignored

The tray writes logs to /tmp/claude-usage-tray.log and generated icons to /tmp/claude-usage-icons/.

Security

session/state.json contains your Claude.ai cookies and is equivalent to your login — do not share it. It is excluded via .gitignore.

The tray enforces a single instance via a PID file at /tmp/claude-usage-tray.pid; restarting it kills any older instances automatically.

License

No license specified — treat as personal/internal use unless the repo owner adds one.