Skip to content

About

A Windows system tray app that shows AI usage

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

23 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tokenometer

A Windows system tray app that shows your claude.ai usage (5-hour and weekly limits) as live, color-coded gauges — no browser tab required.

Features

How it looks:

System tray tokenometer ss1

When you hover on it:

System tray tokenometer ss2

When you click on it:

System tray tokenometer ss3

  • Tray icon renders two concentric rings: inner = 5-hour usage, outer = weekly usage
  • Click the tray icon for a bigger popup view with reset times
  • Colors shift green → amber → red as usage climbs, with configurable thresholds
  • Optionally invert the gauges to count down what's left instead of up what's used
  • One-time login (embedded browser, auto-captures the session — no copy/pasting cookies)
  • Session persists across restarts; you only see the login window again once it actually expires
  • Detailed local logging for troubleshooting

Installing

Grab TokenometerSetup.exe from a release (or build it yourself, see below) and run it. It:

  • Installs per-user to %LocalAppData%\Programs\Tokenometer — no admin rights needed
  • Adds a Start Menu entry and a proper uninstaller
  • Optionally registers itself to launch at sign-in

Requires the .NET 8 Desktop Runtime — Windows will prompt you to install it automatically if it's missing.

First-time setup

  1. Right-click the tray icon → Settings... → Log in to claude.ai... and sign in through the embedded window. It closes automatically once your session is captured — your organization id is captured at the same time (from claude.ai's own lastActiveOrg cookie), no DevTools required.
  2. That's it — the gauges start updating (polling every 3 minutes by default).

The login window may sit open for a few seconds after you sign in: sessionKey arrives the moment you authenticate, but the lastActiveOrg cookie the organization id comes from only lands once claude.ai redirects into your organization. It waits up to 20s for that, then closes.

There's no manual override for the organization id — if claude.ai ever renames the lastActiveOrg cookie and auto-detection breaks, logging out and back in is the way to retry it (see Troubleshooting below).

Until you log in, the app shows mock/random data so you can see what the gauges look like.

Tray menu

Item What it does
Check now Force an immediate refresh instead of waiting for the next poll
Settings... Opens the settings window (below)
Exit Quits the app

Settings window

Item What it does
Log in to claude.ai... Opens the embedded login window; also auto-detects your organization id
Log out Clears the sign-in marker, the embedded browser's own cookies, and the auto-detected organization id
Gauge Display... Change the amber/red usage thresholds (default 70% / 90%), and toggle showing remaining instead of used
View log... Opens the log file for troubleshooting
Verbose logging Records every poll, not just errors. Off by default; ticking it asks for confirmation, since it's meant for active troubleshooting rather than everyday use

Where your data lives

Everything is stored under %AppData%\Tokenometer\:

File Contents
signed-in.marker Empty file; its presence is how the app knows you've logged in. Your session itself lives only in the WebView2\ profile below, never in a second copy
organization-id.txt The organization id, auto-detected during login (not a secret)
gauge-settings.json Your color threshold and gauge-direction preferences
log.txt / log.old.txt Diagnostic logs (rotated at 5MB), never contains the cookie value itself
log-settings.json Whether the log records per-poll detail (verbose)
WebView2\ The embedded browser's profile (cookies, cache)

Uninstalling the app does not delete this folder, so reinstalling picks up right where you left off. Delete it manually if you want a clean slate.

Building from source

Requires the .NET 8 SDK.

dotnet build Tokenometer.slnx

To publish a distributable build:

dotnet publish Tokenometer.csproj -c Release -r win-x64 --self-contained false -p:PublishSingleFile=false -o publish

Running the tests

dotnet test Tokenometer.Tests/Tokenometer.Tests.csproj

Tests cover the pure logic (gauge colour thresholds, the used-vs-remaining decision, the claude.ai response parser), UsageClient and UsagePoller's orchestration against fakes, and the file-backed stores against a temp directory — including the session.dat migration and the gauge-settings cache. The stores take an injectable folder for exactly that reason, and a module initializer redirects Logger so a test run never appends fixture data to your real log.txt.

WinForms UI and the WebView2 browser automation aren't unit tested; that would need UI automation rather than xUnit, and is a deliberate boundary rather than a gap that got missed. Worth knowing what that boundary costs: the organization-id race fixed in 0.3.0 lived in the timing between claude.ai setting two cookies, and no unit test would have caught it — only a genuine first-ever login on a clean profile did.

Building the installer

Requires Inno Setup 6.

& "$env:LOCALAPPDATA\Programs\Inno Setup 6\ISCC.exe" installer.iss

This produces installer-output\TokenometerSetup.exe.

Note that installer.iss reads the version out of publish\Tokenometer.exe, so publish before compiling the installer. To cut a release, bump <Version> in Tokenometer.csproj — that's the only place it lives, and the installer and the startup log line both follow from it.

Troubleshooting

Start with View log... in the tray menu — by default it records startup, logins, logouts and anything that failed, and a healthy poll writes nothing. If that's not enough to see what's going on, tick Verbose logging in Settings (it'll ask you to confirm), reproduce the problem, then untick it again — that adds a line for every fetch attempt, HTTP status and timing. Either way the file rotates at 5MB into log.old.txt.

Common issues:

  • "Organization ID not set" — auto-detection didn't run yet or failed; log out and back in via Settings to retry it.
  • HTTP error / Cloudflare challenge page in the log — claude.ai's private endpoint or protection may have changed; this project may need updating.
  • Login window closes instantly without asking you to log in — this is expected if your session is still valid from a previous login (the embedded browser profile persists). Use Settings → "Log out" first if you need to force a fresh login.

Architecture, briefly

Tokenometer/
  Program.cs                    entry point
  TrayApplicationContext.cs     composition root — tray icon, menu, wiring
  Forms/                        LoginForm, GaugePopupForm, GaugeSettingsForm, SettingsForm
  Usage/                        UsageClient, UsagePoller, UsageResponseParser, IUsageFetcher
  Browser/                      BrowserUsageFetcher, BrowserCookies, SharedBrowserEnvironment (the hidden WebView2 host)
  Rendering/                    GaugeRenderer (GDI+), GaugeColorSelector, GaugeDisplay, GaugeThresholds
  Storage/                      SignInState, OrganizationSettings, GaugeSettings, LogSettings + their interfaces
  Logging/                      Logger
Tokenometer.Tests/               xUnit tests + fakes for the above interfaces

UsageClient doesn't reach for SignInState/OrganizationSettings/BrowserUsageFetcher directly — it takes ISignInState, IOrganizationSettings, and IUsageFetcher through its constructor. TrayApplicationContext is the only place that wires the real implementations together, which is what lets UsageClientTests exercise the actual fetch/parse/error-handling logic against fakes instead of a live browser and network.

Response parsing (UsageResponseParser), the color-threshold decision (GaugeColorSelector), and the used-vs-remaining decision (GaugeDisplay) are all pure functions for the same reason — split out from the networking and GDI+ drawing code they used to live inside, specifically so they don't need a browser or a graphics context to test.

Note that inverting a gauge changes only the arc and the number, never the color: thresholds describe usage, so an almost-spent limit stays red whether it reads "95%" or "5% left". Feeding the inverted value into GaugeColorSelector would paint a brand-new session red, which is why the two decisions are kept apart.

About

A Windows system tray app that shows AI usage

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages