Skip to content

About

KDE Plasma 6 widget showing remaining quota and pay-per-use cost for Codex, Claude, Gemini, Antigravity and Grok

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

TallyBar

A KDE Plasma 6 panel widget that shows how much of your AI coding assistants' usage you have left — and what that usage would have cost at pay-per-use rates.

TallyBar showing Claude's 5-hour and weekly limits with a pay-per-use cost estimate TallyBar showing Antigravity's Gemini and Claude/GPT quota windows

Why

Every AI coding tool meters you differently. Claude has a rolling session window and weekly caps, plus separate ones for individual models; Codex has its own session/weekly pair; Antigravity splits its quota between Gemini and everything else; Grok bills by the week. Each hides that number behind a different CLI flag, web page, or nothing at all — and none of them tell you what the tokens you just burned would have cost on the API.

TallyBar puts all of it in the panel: one bar per limit, one number per bill.

Clicking the cost row opens a flyout with the spend graph — by hour, by day, or as a month heat grid. Clicking any bar or day expands it into a per-model breakdown.

Cost flyout, Day tab: token usage per hour across today Cost flyout, Week tab: daily token usage bars for the last seven days Cost flyout, Month tab: a calendar heat grid of daily spend for the current month

A pointer rests on the busiest bar of each cost flyout tab until its tooltip shows, then clicks it open to the per-model breakdown

What it shows

Provider Quota / limits Where that comes from
Codex session window, weekly window, plan the codex CLI's app-server over JSON-RPC; browser cookies as fallback
Claude session window, weekly window, per-model windows claude.ai, using your browser's existing session; plan read from disk
Gemini daily usage, Google One AI credit pool gemini.google.com, using your browser's existing session
Antigravity Gemini window, Claude/GPT window, plan the local Antigravity language server, plus Google's Cloud Code API
Grok weekly window xAI's billing service via the grok CLI's sign-in; else the CLI's log
Grok Bot weekly window; on-demand spend when capped the Grok Bot desktop app's sign-in, against Cursor's dashboard service

Cost is computed separately, from local log files only — the transcripts each CLI already writes to ~/.claude, ~/.codex, ~/.gemini, ~/.grok and Antigravity's trajectory databases. Tokens are priced against the LiteLLM model catalog (fetched once and cached on disk), which is why the heading reads "Cost (if pay-per-use)" — it is what your usage would have cost on the API, not what you were charged. Each request is priced on its own, so a model that costs more for long prompts (Claude Haiku 5.5 past 100K tokens, GPT-5.5 past 272K, Gemini 2.5 and 3.1 Pro past 200K, Grok 4.x from 200K) is billed at the higher rate only for the requests whose prompt crosses that size. Grok Bot has no local cost history yet: its tab shows the weekly bar only.

Clicking the cost row opens a separate chart window with a 7-day bar chart, a month grid, a 24-hour histogram, and a per-model breakdown you can expand by clicking a bar.

Requirements

  • KDE Plasma 6.0 or newer
  • Python 3.11 or newer — the backend uses asyncio.TaskGroup and asyncio.timeout, both new in 3.11
  • Nothing else. The backend is standard-library only: no pip install, no virtualenv, no daemon. (.venv in this repo is for running the tests.)

Optional, per provider: KWallet for decrypting Chromium-family cookies, and whichever CLIs you actually use. Every provider is independent — a missing one shows a status line, never an error dialog.

Install

Download io.github.dlansama.tallybar.plasmoid from the latest release and install it with the tool Plasma ships:

kpackagetool6 --type Plasma/Applet --install io.github.dlansama.tallybar.plasmoid

Or install from source:

git clone https://github.com/DLANSAMA/TallyBarPlasmoid.git
cd TallyBarPlasmoid
make install

Then right-click your panel → Add Widgets… → search for TallyBar.

make install wraps kpackagetool6; make build produces the same .plasmoid package the release ships.

To update an existing install, make upgrade, then restart the shell so Plasma drops its cached copy of the QML:

systemctl --user restart plasma-plasmashell.service

make remove uninstalls it.

First run

The widget refreshes every 5 minutes by default and paints the last known values immediately on cold start, so it is never blank while a fetch is in flight.

Each provider connects itself:

  • Codex works as soon as its CLI has been used once — it is read from the local app-server.
  • Grok works as soon as the grok CLI has been signed in and used once. The bar is read live with the CLI's sign-in; when that has expired, or the network is off, it comes from the CLI's own log files instead.
  • Antigravity is read from its language server, which runs while Antigravity (or agy) is open. Plan and quota keep working from a cached token afterwards.
  • Claude and Gemini use the session cookies already in your browser (Chrome, Chromium, Brave, Edge or Firefox). If you are signed in to claude.ai or gemini.google.com in one of those, there is nothing to configure; if you are not, the widget shows a Sign in button that opens the right page.
  • Grok Bot uses the sign-in the Grok Bot desktop app keeps: open the app and sign in once. That sign-in is encrypted with the Chromium Safe Storage key, so reading it needs KWallet, like the browser cookies.

On a machine with none of these installed, the widget still starts and shows one status line per provider (No matching browser cookies, Grok Build data dir (~/.grok) not found, …) rather than failing.

Configuration

The gear icon opens a settings flyout: refresh interval, which providers get a tab, what the panel itself displays (percentage, cost or bars), notification thresholds, per-provider mute, and an optional monthly budget.

Settings live in ~/.tallybar/config.json. The rest of that directory is the widget's own state — cached snapshot, the Antigravity token ledger, the monthly cost archive, and parse caches. All of it is regenerable: delete it and the next refresh rebuilds it.

How it works

The widget is two halves separated by a process boundary:

QML frontend                       Python backend
io.github.dlansama.tallybar/contents/ui/      io.github.dlansama.tallybar/contents/code/
  main.qml  ── Plasma5Support ───►  backend.py --once --background
  (owns all state)  DataSource      (prints one JSON snapshot, exits)
        ▲                                   │
        └────────── stdout JSON ────────────┘

There is no daemon and no socket. Every refresh runs backend.py once; it gathers everything concurrently, prints a single JSON document, and exits. The frontend JSON.parses stdout and re-renders. That shape is deliberate — a widget that can't hang is worth more than one that saves a process spawn, and it makes the backend trivially testable and debuggable by hand:

# Exactly what the widget runs, but readable
python3 io.github.dlansama.tallybar/contents/code/backend.py --once --pretty

# Skip every remote call (local logs and language servers only)
python3 io.github.dlansama.tallybar/contents/code/backend.py --once --no-network --pretty

# Just the cost/token numbers
python3 io.github.dlansama.tallybar/contents/code/backend.py --cost

backend.py always prints parseable JSON, including on a fatal error — it falls back to the last cached snapshot rather than exiting silently, because a crash that prints nothing would blank the widget.

The contracts a change must not break — and the test that guards each one — are in CLAUDE.md.

Privacy and security

This widget reads credentials, so it is worth being explicit about what it does with them:

  • Nothing is sent anywhere but the provider it belongs to. There is no telemetry, no analytics, no update check. The only hosts contacted are the ones in the table above plus raw.githubusercontent.com for the price catalog.
  • Browser cookies are read, never kept. The browser's own database is never opened directly (that would fight the browser for the lock). It is copied into a private 0700 directory under $XDG_RUNTIME_DIR — tmpfs, cleared at logout — with O_NOFOLLOW, read once, and deleted. Copies stranded by a killed process are swept on the next run.
  • Chromium-family cookies are decrypted through KWallet, using the same key the browser itself uses. TallyBar never asks you for a password, and the background refresh never triggers a wallet-unlock prompt.
  • Every diagnostic string is scrubbed for tokens, API keys, JWTs and session cookies before it can reach the snapshot, the UI, or a log.
  • Everything written to disk is written atomically at 0600 under a 0700 parent — temp file, fsync, os.replace — and the files that matter carry a .bak mirror with corruption quarantine on load.
  • Credentialed requests refuse redirects. CPython's redirect handler copies the Authorization header cross-origin; the Google and xAI endpoints that receive a bearer token never legitimately redirect, so a 3xx from one is treated as an attack and raised instead of followed.
  • Refreshes stop while the screen is locked, and do not overwrite the cached snapshot when they do.

Development

python3 -m venv .venv && .venv/bin/pip install -r requirements.txt

make test         # 530 tests
make lint         # pyflakes (what CI runs)
make check        # flake8 + Qt6 qmllint (fails on QML errors) + mypy
make preview      # load a UI component in a standalone window, no Plasma needed
make screenshots  # re-render docs/screenshots/ (PNGs + demo GIF) offscreen from this machine's live usage
make render-baselines  # re-render the mock-fixture PNGs the render tests compare against
make build        # package io.github.dlansama.tallybar.plasmoid

Adding a provider

TallyBar can't support a provider until someone has seen what it actually returns, and no maintainer holds a subscription to every AI coding tool. So captures are cheap to produce and worth contributing:

python3 tools/capture_provider.py copilot     # or: cursor

It reads the credential that tool already stored locally, makes one request, scrubs the response through the same scrub_credentials the backend uses, and writes a 0600 file. It uploads nothing. Free tiers are enough — GitHub Copilot Free and Cursor's Hobby plan both yield a real response shape, which is the part that can't be guessed.

Scrubbing is best-effort: read the file before attaching it to an issue.

The arithmetic lives in modules that don't reach the network — parsers.py is pure (payload in, limit rows out) and accounting.py does token and cost maths against local files only. That is what most of the test suite exercises. The UI can be run without Plasma — see tools/preview/README.md.

CI runs the lint and the tests on every pull request and every push to main; pushing a v* tag builds the .plasmoid, stamps the version into metadata.json, and attaches the package to a GitHub release.

Known limitations

  • Linux/KDE only. It is a Plasma 6 applet; there is no port to anything else.
  • The Claude, Gemini and Grok Bot readings depend on unofficial endpoints — the same ones their own apps call, with your own session. They are not public APIs and can change without notice. When one does, that provider degrades to a status message and the others keep working.
  • Grok's live bar lasts as long as the CLI's sign-in. TallyBar uses the access token the grok CLI keeps in ~/.grok/auth.json and never renews it; it lapses about six hours after the CLI last did. After that the bar shows xAI's last reading from the CLI's log, which only updates when the CLI starts, and marks it "(cached)" past an hour. Running grok renews its sign-in.
  • Costs are estimates. They are list prices applied to locally logged token counts. Treat them as a sense of scale, not a bill. An Antigravity CLI session known only from its status-line total is billed at the base rate, since no single request's prompt size survives.

Prior art

The idea comes from CodexBar by Peter Steinberger — the same job, done as a macOS menu-bar app. TallyBar is an independent implementation for Plasma with its own backend, and it borrows CodexBar's monochrome provider marks under that project's MIT license.

License

MIT — see LICENSE. Provider logos are the trademarks of their respective owners and are bundled only to identify each provider; the bundled MIT-licensed artwork carries its upstream copyright notice. Both are recorded in io.github.dlansama.tallybar/NOTICE.

About

KDE Plasma 6 widget showing remaining quota and pay-per-use cost for Codex, Claude, Gemini, Antigravity and Grok

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages