{"slug": "show-hn-llm-usage-desktop-widget", "title": "Show HN: LLM Usage Desktop Widget", "summary": "A developer released LLM Usage Widget, a desktop app for Linux, macOS, and Windows that displays Claude and Codex plan usage limits — session and weekly windows, model-scoped limits, and reset countdowns — in the system tray or menu bar. The widget reuses logins already stored by Claude Code and the Codex CLI, reading credentials from the macOS Keychain or ~/.claude/.credentials.json for Claude and ~/.codex/auth.json for Codex, and sends each token only to the provider that issued it. It queries the same undocumented internal endpoints the official CLIs use for their /usage and /status screens (api.anthropic.com/api/oauth/usage and chatgpt.com/backend-api/wham/usage), turns bars amber at 75% and red at 90%, and keeps the last numbers marked stale if a refresh fails.", "body_md": "Shows your **Claude** and **Codex** plan usage limits (session / weekly windows, model-scoped\nlimits, and time until each resets) where your system status icons live:\n\n| Linux (Ubuntu) | macOS | Windows | \n|---|---|---|\n\n| OS | Where it appears | What it shows | \n|---|---|---|\n| Linux | Top bar / panel, next to Wi-Fi and sound (AppIndicator) | Icon + `Claude 99%  Codex 13%` ; click for details | \n| macOS | Menu bar, top right | Gauge icon + `Claude 99%  Codex 13%` ; click for details | \n| Windows | An empty spot on the taskbar, plus a tray icon | `Claude 99%  Codex 13%` on the taskbar and a two-bar gauge icon; click either for details | \n\nThe dropdown lists every limit with its percentage and reset countdown, plus *Refresh now*,\nlinks to each provider's usage page, and *Quit*. Bars and icons turn amber at 75% and red at 90%\n(or whatever severity the provider reports). If a refresh fails, the last numbers are kept and marked stale.\n\nThere is also a floating always-on-top window (`--widget`) for desktops without a tray.\n\nIt reuses the logins that **Claude Code** (`claude`) and the **Codex CLI** (` codex`) already store\non your machine, so there is nothing to configure. Credentials are only read, never modified, and each\ntoken is sent only to the provider that issued it.\n\n| Provider | Credentials read from | Endpoint | \n|---|---|---|\n| Claude | macOS Keychain `Claude Code-credentials` , else`~/.claude/.credentials.json` (`$CLAUDE_CONFIG_DIR` ) | `api.anthropic.com/api/oauth/usage` | \n| Codex | `~/.codex/auth.json` (`$CODEX_HOME` ) | `chatgpt.com/backend-api/wham/usage` | \n| Bedrock | Your AWS credentials (profile, SSO, env vars), via boto3 | AWS Budgets, Cost Explorer, CloudWatch | \n\nThese are the same internal endpoints the official CLIs use for their `/usage` and `/status`\nscreens. They are undocumented and may change; if a provider shows an error after an update,\ncheck the parser for that provider in `usage_widget.py`.\n\nFirst log in to each CLI once with your subscription: `claude` → `/login`, and `codex login`.\nEach installer puts the app where it belongs for the OS, starts it right away, and starts it again\nevery time you log in.\n\nDownload from the GitHub release (built by `.github/workflows/build-installers.yml`):\n\n| OS | File | Notes | \n|---|---|---|\n| Linux (Debian/Ubuntu) | `llm-usage-widget_<ver>_all.deb` | `sudo apt install ./llm-usage-widget_*.deb` . Starts right away for every logged-in user, and at login via`/etc/xdg/autostart` . | \n| macOS | `LLM-Usage-Widget-<ver>-macos-arm64.pkg` (Apple Silicon) or`-x86_64.pkg` (Intel) | Installs `/Applications/LLM Usage.app` plus a login LaunchAgent. Release builds are signed with a Developer ID and notarized by Apple. | \n| Windows | `LLM-Usage-Widget-<ver>-windows-setup.exe` | Per-user install, no admin rights needed. \"Start automatically when I sign in\" is ticked by default. The build is unsigned, so SmartScreen may warn: click **More info → Run anyway** . Uninstall from*Settings → Apps* , the*Uninstall LLM Usage Widget* Start Menu entry, or by running`LLM-Usage-Widget-<ver>-windows-uninstall.exe` from the same release (add`/VERYSILENT` to skip the prompts). Settings in`%APPDATA%\\llm-usage-widget` are kept. | \n\nTo uninstall the macOS package, open `LLM-Usage-Widget-<ver>-macos-uninstall.pkg` from the same release\n(it asks for an administrator password), or run the copy of the uninstaller inside the app:\n\n```\nsudo \"/Applications/LLM Usage.app/Contents/Resources/uninstall.sh\"\n```\n\nEither one stops the widget for every logged-in user and removes the app, its LaunchAgent and the\npackage receipt. Settings in `~/Library/Application Support/llm-usage-widget` are left in place.\n\nOn Debian/Ubuntu: `sudo apt remove llm-usage-widget`.\n\n| OS | Install | Uninstall | \n|---|---|---|\n| Linux / macOS | `./install.sh` | `./install.sh --uninstall` | \n| Windows | double-click `install.cmd` (or`powershell -ExecutionPolicy Bypass -File install.ps1` ) | `uninstall.cmd` | \n\n- **Linux:** copies to`~/.local/share/llm-usage-widget` , adds an app-menu launcher and`~/.config/autostart/llm-usage-widget.desktop` . Uses the system Python's PyGObject and AppIndicator\n(preinstalled on Ubuntu; the script prints the package command for your distro if they're missing).\nOn GNOME the AppIndicator extension must be enabled (the default on Ubuntu; elsewhere install\n\"AppIndicator and KStatusNotifierItem Support\"). KDE, XFCE, Cinnamon and others work out of the box,\nthough some panels show only the icon, not the text label.\n- **macOS:** needs Python 3.9+ (python.org or Homebrew). Copies to`~/Library/Application Support/llm-usage-widget` with its own virtualenv and registers`~/Library/LaunchAgents/local.llm-usage-widget.plist` . Logs go to`~/Library/Logs/llm-usage-widget.log` .\n- **Windows:** needs Python 3.9+ from python.org. Copies to`%LOCALAPPDATA%\\llm-usage-widget` with its\nown virtualenv and adds Start Menu and Startup shortcuts.\n\nOn macOS the first run may ask for Keychain access to `Claude Code-credentials`; choose *Always Allow*.\nOn Windows the numbers are drawn on the taskbar itself: left of the `^` arrow when there is room, otherwise in\nthe next empty gap (with centered taskbar buttons, usually left of Start), on two smaller lines if that is what\nfits. It never covers taskbar buttons; if the taskbar is completely full it hides until there is room. Windows has\nno API for text in the taskbar, so this is a small always-on-top window that follows the taskbar and steps\naside for full-screen apps and an auto-hidden or vertical taskbar. Turn it off with *Show usage on the taskbar*\nin the menu. The tray icon may be tucked into the overflow (`^`) menu; drag it onto the taskbar to keep it visible.\n\n```\n/usr/bin/python3 usage_widget.py               # Linux (system Python, for PyGObject)\npython3 -m pip install -r requirements.txt     # macOS / Windows: pystray + Pillow\npython3 usage_widget.py                         # Windows: pythonw usage_widget.py (no console window)\n```\n\nOnly one copy runs at a time; starting a second one exits immediately.\n\n```\nusage_widget.py                  # tray / top bar / menu bar (default)\nusage_widget.py --widget         # floating always-on-top window instead\nusage_widget.py --widget --framed    # ...with a normal title bar\nusage_widget.py --once           # print usage in the terminal and exit\nusage_widget.py --interval 300   # refresh every 5 minutes (default 180 s, min 60 s)\n```\n\nClaude Code and Codex refresh their own tokens while you use them. If you haven't used one for a while, its access token may expire and the widget will say so. Open that CLI once and the widget picks up the new token on its next refresh. The widget deliberately does not refresh tokens itself, because rotating the refresh token would log the CLI out.\n\nThe Ubuntu AppIndicators extension shipped with GNOME 50 comes up half-broken at login: until it is\nreloaded, apps that start after its initial bus scan get `No such method \"RegisterStatusNotifierItem\"`\nand don't appear (this also hits Remmina, update-notifier, etc.). Apps already running during that scan\n*do* appear, so starting the widget at login usually works. `install.sh` detects the broken state and\nreloads the extension once so the icon appears immediately. If the icon is missing, reload it by hand:\n\n```\ngnome-extensions disable ubuntu-appindicators@ubuntu.com && gnome-extensions enable ubuntu-appindicators@ubuntu.com\n```\n\nThe GitHub Actions workflow builds every installer on its own OS. Push a tag matching `__version__` in\n`usage_widget.py` (e.g. `v1.0.0`) to build them and attach them to a release, or run the workflow\nby hand to download them as build artifacts. To build locally on each OS:\n\n```\npackaging/linux/build-deb.sh                                   # Linux → dist/*.deb\npython3 -m pip install pyinstaller -r requirements.txt\npackaging/macos/build-pkg.sh                                   # macOS → dist/*.pkg\npowershell -File packaging\\windows\\build.ps1                   # Windows → dist\\*-setup.exe (needs Inno Setup 6)\n```\n\nTo sign the macOS build locally, set `CODESIGN_IDENTITY` (a \"Developer ID Application\" identity) and\n`INSTALLER_SIGN_IDENTITY` (\"Developer ID Installer\"). The app is signed with the hardened runtime and a\nsecure timestamp, which is what notarization requires. Then notarize and staple the pkg:\n\n```\nxcrun notarytool submit dist/*.pkg --keychain-profile <profile> --wait\nxcrun stapler staple dist/*.pkg\n```\n\nIn CI, the macOS job signs, notarizes and staples the pkg when these repository secrets are set. A tag release fails without them:\n\n| Secret | Contents | \n|---|---|\n| `MAC_CERT_P12_BASE64` /`MAC_CERT_PASSWORD` | The Developer ID Application certificate and private key, as a base64-encoded `.p12` , and its export password | \n| `MAC_INSTALLER_CERT_P12_BASE64` /`MAC_INSTALLER_CERT_PASSWORD` | The same for the Developer ID Installer certificate | \n| `AC_API_KEY_P8_BASE64` /`AC_API_KEY_ID` /`AC_API_ISSUER_ID` | An App Store Connect API key (the base64-encoded `.p8` ), its key ID and issuer ID, used by`notarytool` | \n\nThe workflow Authenticode-signs `LLMUsageWidget.exe` and the setup `.exe` with\n[Azure Trusted Signing](https://learn.microsoft.com/azure/artifact-signing/) (the same setup as\nVirux), timestamps both, and checks the signatures before uploading. A tag build fails rather than\npublish an unsigned installer; a manual run without the secrets builds unsigned with a warning.\nIt needs these repository (or organization) secrets:\n\n| Secret | What it is | \n|---|---|\n| `AZURE_TENANT_ID` | Entra ID tenant of the app registration that signs | \n| `AZURE_CLIENT_ID` | That app registration's client ID | \n| `AZURE_CLIENT_SECRET` | A client secret for it; also how the workflow decides whether to sign | \n| `AZ_TS_ENDPOINT` | The Trusted Signing account's regional endpoint, e.g. `https://eus.codesigning.azure.net` | \n| `AZ_TS_ACCOUNT` | Trusted Signing account name | \n| `AZ_TS_PROFILE` | Certificate profile name | \n\nThe app registration needs the *Trusted Signing Certificate Profile Signer* role on the account.\n\n**Publisher name only, no address.** Windows shows the certificate's common name, the validated\norganization name (\"Aleph Void LLC\"), as the publisher in SmartScreen, the UAC prompt and the file's\n*Digital Signatures* tab. Create the **Public Trust** certificate profile with *Include street\naddress* and *Include postal code* both **unticked** (the default) so the certificate carries no\naddress. City, state and country are always part of a Public Trust certificate's details and can't\nbe removed. `packaging/windows/verify-signature.ps1` fails the build if a signature is invalid,\nnot timestamped, or its certificate includes a street address or postal code. Run it on a\ndownloaded installer to check it yourself:\n\n```\npowershell -File packaging\\windows\\verify-signature.ps1 .\\LLM-Usage-Widget-<ver>-windows-setup.exe\n```\n\nThe release's `-windows-uninstall.exe` is signed the same way. The uninstaller that Inno Setup\nwrites into the install folder at install time is not signed. It runs without elevation\n(the install is per-user), so Windows doesn't show a publisher prompt for it.\n\nIf Claude Code or Codex runs on Amazon Bedrock instead of a subscription, there are no session or weekly\nlimits to show. Instead the widget adds a **Bedrock** section that shows:\n\n- **Budget:** spend against an[AWS Budget](https://console.aws.amazon.com/billing/home#/budgets) as a\npercentage bar that resets with the budget period. This only appears if you name a budget (see below).\n- **This month:** month-to-date spend for every service with \"Bedrock\" in its name, from Cost Explorer.\nThird-party models such as Claude are billed as Marketplace services named like`Claude Sonnet 4.5 (Amazon Bedrock Edition)` , and these are included.\n- **Tokens today:** input and output tokens since local midnight, from CloudWatch. This covers both the`bedrock-runtime` endpoint (`AWS/Bedrock` ) and the OpenAI/Anthropic-compatible`bedrock-mantle` endpoint\n(`AWS/BedrockMantle` ).\n\nThe section turns on automatically when Claude Code's settings set `CLAUDE_CODE_USE_BEDROCK`\n(or an `ANTHROPIC_BASE_URL` containing `bedrock`), or when a `base_url` in `~/.codex/config.toml` points at\nBedrock. The AWS profile and region come from the same place: `AWS_PROFILE` and `AWS_REGION` in Claude\nCode's `env` block, or the region in Codex's `base_url`. In that case the Claude or Codex section\nis hidden if that CLI has no subscription login. To override any of this, add a `bedrock` entry to\n`config.json` in the widget's config folder (`~/.config/llm-usage-widget/` on Linux,\n`~/Library/Application Support/llm-usage-widget/` on macOS, `%APPDATA%\\llm-usage-widget\\` on Windows):\n\n```\n{\n  \"bedrock\": {\n    \"profile\": \"work\",\n    \"regions\": [\"us-east-1\", \"us-west-2\"],\n    \"budget\": \"bedrock-monthly\",\n    \"cost_explorer\": true,\n    \"cost_interval\": 21600,\n    \"metrics_interval\": 900\n  }\n}\n```\n\nUse `\"bedrock\": true` to turn the section on with the defaults, or `false` to hide it.\n\nThis needs **boto3**: it is in `requirements.txt` and in the macOS and Windows packages. On Linux, run\n`sudo apt install python3-boto3` (or install it for the system Python another way). The IAM identity\nneeds `ce:GetCostAndUsage`, `cloudwatch:GetMetricData`, and, for a budget, `budgets:ViewBudget` and\n`sts:GetCallerIdentity`. If a permission is missing, only that row shows an error. If the credentials\nhave expired (for example an SSO session), the section says to run `aws sso login`.\n\n**Cost:** Cost Explorer charges $0.01 per request and its data only updates a few times a day, so spend\n(and the budget) is fetched at most every `cost_interval` seconds (6 h by default, about $1.20 a month).\nToken counts are fetched every `metrics_interval` seconds (15 min). Both are cached in\n`bedrock-cache.json` next to `config.json`, so restarts don't refetch them, and *Refresh now* doesn't\nbypass the cache either. Billing data can lag by a day or more, so don't treat this as a spending alarm.\n\nWrite a `fetch_<name>()` that returns a `ProviderUsage` and add it to `PROVIDERS`.\n\nMIT. See [LICENSE](https://github.com/aleph-void/LLM-Usage-Desktop-Widget/blob/main/LICENSE).", "url": "https://wpnews.pro/news/show-hn-llm-usage-desktop-widget", "canonical_source": "https://github.com/aleph-void/LLM-Usage-Desktop-Widget", "published_at": "2026-10-18 20:41:34+00:00", "updated_at": "2026-10-18 20:48:31.007978+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-products"], "entities": ["LLM Usage Widget", "Claude", "Claude Code", "Codex", "Codex CLI", "Anthropic", "OpenAI", "Bedrock"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-llm-usage-desktop-widget", "markdown": "https://wpnews.pro/news/show-hn-llm-usage-desktop-widget.md", "text": "https://wpnews.pro/news/show-hn-llm-usage-desktop-widget.txt", "jsonld": "https://wpnews.pro/news/show-hn-llm-usage-desktop-widget.jsonld"}}