Metadata-Version: 2.5
Name: tokentray
Version: 0.1.1
Summary: Cross-platform system-tray monitor for Claude Code and OpenAI Codex quota, with pretty desktop alerts.
Project-URL: Homepage, https://github.com/messy-snail/tokentray
Project-URL: Issues, https://github.com/messy-snail/tokentray/issues
Author: hskang
License-Expression: MIT
License-File: LICENSE
Keywords: claude-code,openai-codex,quota,system-tray,usage-monitoring
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: MacOS X
Classifier: Environment :: Win32 (MS Windows)
Classifier: Environment :: X11 Applications :: Qt
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: httpx>=0.28
Requires-Dist: keyring>=25
Requires-Dist: platformdirs>=4.3
Requires-Dist: pyside6>=6.8
Requires-Dist: rich>=13
Requires-Dist: tomli-w>=1.0
Requires-Dist: typer>=0.15
Description-Content-Type: text/markdown

<div align="center">

<img src="https://raw.githubusercontent.com/messy-snail/tokentray/main/packaging/resources/tokentray-256.png" alt="tokentray icon" width="96">

# tokentray

**Claude Code and Codex usage limits, right in your system tray.**

[![PyPI](https://img.shields.io/pypi/v/tokentray?style=flat-square&logo=pypi&logoColor=white&label=PyPI&color=3775A9)](https://pypi.org/project/tokentray/)
[![Release](https://img.shields.io/github/v/release/messy-snail/tokentray?style=flat-square&logo=github&logoColor=white&label=release&color=8957E5)](https://github.com/messy-snail/tokentray/releases/latest)
[![CI](https://img.shields.io/github/actions/workflow/status/messy-snail/tokentray/ci.yml?style=flat-square&logo=githubactions&logoColor=white&label=CI)](https://github.com/messy-snail/tokentray/actions/workflows/ci.yml)
[![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-3776AB?style=flat-square&logo=python&logoColor=white)](https://www.python.org/)
[![License](https://img.shields.io/badge/license-MIT-22C55E?style=flat-square)][LICENSE]

![Windows](https://img.shields.io/badge/Windows-0078D6?style=for-the-badge&logo=data:image/svg%2Bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI%2BPHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0wIDBoMTEuNHYxMS40SDB6TTEyLjYgMEgyNHYxMS40SDEyLjZ6TTAgMTIuNmgxMS40VjI0SDB6TTEyLjYgMTIuNkgyNFYyNEgxMi42eiIvPjwvc3ZnPg%3D%3D)
![macOS](https://img.shields.io/badge/macOS-000000?style=for-the-badge&logo=apple&logoColor=white)
![Linux](https://img.shields.io/badge/Linux-FCC624?style=for-the-badge&logo=linux&logoColor=black)

**English** · [한국어](https://github.com/messy-snail/tokentray/blob/main/README.ko.md)

</div>

<p align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/messy-snail/tokentray/main/docs/images/hero-en-dark.png">
    <img src="https://raw.githubusercontent.com/messy-snail/tokentray/main/docs/images/hero-en-light.png" alt="tokentray detail panel, an alert card and tray icon states" width="760">
  </picture>
</p>

> [!NOTE]
> **Credits.** tokentray is a from-scratch Python reimplementation derived from
> [haomingkoo/claude-codex-monitor](https://github.com/haomingkoo/claude-codex-monitor)
> (MIT), originally a SwiftBar plugin and a PowerShell tray script. It follows that
> project's quota endpoints, pace and burn-out formulas and colour tiers, and brings
> them to Windows, macOS and Linux with one shared interface.

## Features

- 🟢 **Tray rings at a glance** - Claude on the outside, Codex inside. Green above
  50% left, amber down to 21%, red at 20% or below, grey without data.
- 📊 **Detail panel** - remaining %, time to reset, burn-out estimate and pace for
  every limit, including Opus/Sonnet and Codex model limits.
- 🔔 **Alerts before you run out** - at 50, 25 and 10% remaining, plus reminders
  before a reset.
- 🔑 **One-click login recovery** - opens a terminal with `claude auth login` or
  `codex login` when a login expires.
- 🌐 **Webhooks** - forward alerts to Slack, Discord, ntfy or any HTTP endpoint.
- 🖥️ **Windows, macOS and Linux**, in English or Korean.

A pace of **1.0x** means you will hit the limit exactly at reset if you keep
going at your average rate so far.

<p align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/messy-snail/tokentray/main/docs/images/login-recovery-en-dark.png">
    <img src="https://raw.githubusercontent.com/messy-snail/tokentray/main/docs/images/login-recovery-en-light.png" alt="Expired Claude Code login with Log in and Check again buttons" width="250">
  </picture>
  &nbsp;
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/messy-snail/tokentray/main/docs/images/integrations-en-dark.png">
    <img src="https://raw.githubusercontent.com/messy-snail/tokentray/main/docs/images/integrations-en-light.png" alt="Notification integrations window with a Discord webhook" width="344">
  </picture>
</p>

## Install

> [!TIP]
> **uv is the recommended way.** It downloads a matching Python for you and keeps
> tokentray in its own environment, so nothing else on your system changes.

### ![uv](https://img.shields.io/badge/uv-recommended-DE5FE9?style=flat-square&logo=uv&logoColor=white)

Install uv once:

```bash
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
```

```powershell
# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
```

`brew install uv` and `winget install --id=astral-sh.uv -e` work too. Then:

```bash
uv tool install tokentray
```

If the shell cannot find `tokentray` afterwards, run `uv tool update-shell` and
open a new terminal.

### pipx

```bash
pipx install tokentray
```

### pip

Needs Python 3.11-3.13. Install into a virtual environment:

```bash
python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
python -m pip install tokentray
```

### Standalone builds (no Python needed)

| Platform | Download | Then |
|---|---|---|
| ![Windows](https://img.shields.io/badge/Windows-0078D6?style=flat-square&logo=data:image/svg%2Bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI%2BPHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0wIDBoMTEuNHYxMS40SDB6TTEyLjYgMEgyNHYxMS40SDEyLjZ6TTAgMTIuNmgxMS40VjI0SDB6TTEyLjYgMTIuNkgyNFYyNEgxMi42eiIvPjwvc3ZnPg%3D%3D) | [`tokentray-windows-x86_64.zip`](https://github.com/messy-snail/tokentray/releases/latest/download/tokentray-windows-x86_64.zip) | Unzip, run `tokentray\tokentray-gui.exe` |
| ![macOS](https://img.shields.io/badge/macOS-000000?style=flat-square&logo=apple&logoColor=white) | [`tokentray-macos-arm64.zip`](https://github.com/messy-snail/tokentray/releases/latest/download/tokentray-macos-arm64.zip) | Move `tokentray.app` to Applications |
| ![Linux](https://img.shields.io/badge/Linux-FCC624?style=flat-square&logo=linux&logoColor=black) | [`tokentray-linux-x86_64.tar.gz`](https://github.com/messy-snail/tokentray/releases/latest/download/tokentray-linux-x86_64.tar.gz) | Unpack, run `./install.sh` |

> [!IMPORTANT]
> Standalone builds are not code-signed, so Windows SmartScreen and macOS
> Gatekeeper warn on first launch. See
> [Platform notes](https://github.com/messy-snail/tokentray#platform-notes).

## Quick start

Sign in to [Claude Code](https://code.claude.com/docs/en/setup) or
[Codex](https://developers.openai.com/codex/cli/) first - one of them is enough.

```bash
tokentray setup              # choose a language, check logins, offer start-at-login
tokentray                    # start in the background and return to the terminal
```

With a standalone build, the `tokentray` CLI sits next to the app (inside
`tokentray.app/Contents/MacOS` on macOS).

Once the app starts, you can close the terminal. Starting the app at the end of
`setup` works the same way. Quit from the tray menu or with `tokentray stop`.
For debugging, use `tokentray run --foreground` to keep it attached to the terminal.

## Alerts and polling

| Setting | Default | What it does |
|---|---|---|
| `poll_interval` | `120` | Seconds between usage checks (minimum 30) |
| `thresholds` | `50,25,10` | Remaining % that raises an alert |
| `remind_before` | `60,30,10` | Minutes before a reset to remind you |
| `popup.duration` | `8` | Seconds an alert card stays up |
| `popup.position` | `auto` | `bottom-right`, `top-right`, or `auto` (top on macOS, bottom elsewhere) |
| `native_notifications` | Windows `false`, others `true` | Also send OS notifications |

```bash
tokentray config set poll_interval 300    # check every 5 minutes
tokentray config set thresholds 50,20,5   # alert at 50%, 20% and 5% left
tokentray config set remind_before 30,10  # remind 30 and 10 minutes before reset
tokentray config get thresholds
```

Changes reach the running app straight away, except `poll_interval` and
`language`, which apply after a restart. `tokentray config path` shows where the
config file lives.

> [!NOTE]
> **Usage lookups may be temporarily rate limited.**
> When HTTP 429 occurs, TokenTray shows the last available reading and retries
> automatically after waiting. This response alone does not mean account suspension
> or exhausted conversation usage. Manual refresh does not bypass the wait.

<details>
<summary><b>Troubleshooting refresh failures</b></summary>

Refresh results name each service and its reason, for example **Claude refresh
failed: request rate limit** while Codex remains updated.

TokenTray keeps the last available reading and waits at least as long as the
server's `Retry-After` and your polling interval. If the server provides no valid
wait time, repeated 429s back off for 5, 10, 20, 40, then 60 minutes, never less
than your configured interval. Successful lookup resets this backoff. The
displayed retry time is the earliest retry time, not a promise of recovery.
Restarting, manual refresh, login recheck, and notification tests do not bypass
the wait. Requests also have a 30-second minimum spacing; rapid refreshes retain
recent data instead of making another request.

`poll_interval` controls normal polling. Throttling can make actual intervals
longer without changing your setting. No officially guaranteed polling interval
has been confirmed for `/api/oauth/usage`; 2 or 5 minutes is not a server
guarantee. TokenTray cannot control requests from Claude Code, other tools, or
other devices. For troubleshooting, use **Open log** in the tray menu; network
diagnostics omit tokens, raw headers, and response bodies.

</details>

> [!TIP]
> Need some quiet? Choose **Pause alerts for 1 hour** in the tray menu. **Test
> notification** previews the alert cards, using cached data while requests are limited.

<details>
<summary><b>More settings and webhooks</b></summary>

| Setting | Default | What it does |
|---|---|---|
| `language` | from locale | `en` or `ko` |
| `webhook.enabled` / `webhook.kind` | `false` / `ntfy` | External alerts via `slack`, `discord`, `ntfy` or `generic` |
| `codex.refresh` | `false` | Let tokentray refresh the Codex token |
| `linux.force_xwayland` | `true` | See Platform notes |

Choose **Notification integrations…** in the tray menu to set up one webhook,
validate the URL and send a test, or use the terminal:

```bash
tokentray webhook setup --service slack
tokentray webhook test
```

The URL is kept in the OS keyring. Alerts are only sent while tokentray runs on
a device that is online; if several devices watch the same account, enable
webhooks on just one to avoid duplicates. For Slack, set
`packaging/resources/tokentray-512.png` as the app icon; Discord lets you set
the webhook avatar in the channel settings.

</details>

<details>
<summary><b>All commands</b></summary>

| Command | Description |
|---|---|
| `tokentray` | Start the tray app |
| `tokentray setup` | Interactive first-run configuration |
| `tokentray status` | Print current quota (asks the running app first) |
| `tokentray open` / `refresh` / `stop` | Control a running instance |
| `tokentray doctor` | Check credentials, keyring, tray and notification delivery |
| `tokentray config get/set/path` | Read and write settings |
| `tokentray state reset --welcome/--alerts/--all` | Re-arm the first-run notice or the alert memory |
| `tokentray webhook setup/test` | Configure and test a webhook |
| `tokentray autostart enable/disable/status` | Manage start-at-login |

</details>

## Platform notes

<details>
<summary><img src="https://img.shields.io/badge/Windows-0078D6?style=flat-square&logo=data:image/svg%2Bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI%2BPHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0wIDBoMTEuNHYxMS40SDB6TTEyLjYgMEgyNHYxMS40SDEyLjZ6TTAgMTIuNmgxMS40VjI0SDB6TTEyLjYgMTIuNkgyNFYyNEgxMi42eiIvPjwvc3ZnPg%3D%3D" alt="Windows"></summary>

- A new tray icon may land among the hidden icons. The welcome card links to the
  taskbar setting, where the entry's name starts with `tokentray`.
- On the SmartScreen prompt for a standalone build, choose *More info* then
  *Run anyway*.
- Installed with uv or pip, the background process shows in Task Manager as
  `pythonw.exe`.

</details>

<details>
<summary><img src="https://img.shields.io/badge/macOS-000000?style=flat-square&logo=apple&logoColor=white" alt="macOS"></summary>

- Builds are ad-hoc signed, not notarised. If Gatekeeper blocks the app:

  ```bash
  xattr -dr com.apple.quarantine /Applications/tokentray.app
  ```

- Apple silicon only. The app lives in the menu bar, so there is no Dock icon.
- The CLI is at `/Applications/tokentray.app/Contents/MacOS/tokentray`; symlink
  it onto your `PATH` to type `tokentray`.
- Reading Claude Code credentials from the login keychain may show an access
  prompt the first time.
- Notification Center delivery from a tray app is unreliable, so tokentray's own
  alert cards are the main channel. `tokentray doctor` shows what your build can do.

</details>

<details>
<summary><img src="https://img.shields.io/badge/Linux-FCC624?style=flat-square&logo=linux&logoColor=black" alt="Linux"></summary>

- x86_64 tarball only. `./install.sh` adds a launcher entry and icon for your
  user; `./install.sh --uninstall` removes them.
- Qt needs a few system libraries that minimal installs, including stock Ubuntu
  24.04, may lack:

  ```bash
  sudo apt install libxcb-cursor0 libxkbcommon0 libegl1 libgl1 libdbus-1-3 libfontconfig1 libglib2.0-0
  ```

- GNOME needs the AppIndicator extension to show a tray icon; KDE works out of
  the box.
- On Wayland, tokentray runs through XWayland so alert cards can sit next to the
  tray. With `linux.force_xwayland = false` it runs natively, but the compositor
  decides where cards appear.
- Without a keyring service such as SecretService, secrets fall back to an
  owner-only local file, and setup tells you so.

</details>

## More details

<details>
<summary><b>Login credentials</b></summary>

tokentray reads the credentials Claude Code and Codex already saved:

| Service | Location |
|---|---|
| Claude Code | `~/.claude/.credentials.json` (or `$CLAUDE_CONFIG_DIR`), then the macOS login keychain |
| Codex | `~/.codex/auth.json` (or `$CODEX_HOME`) |

- **Log in** in the panel or an alert opens a terminal running the login command
  and watches for new credentials for up to five minutes. If no terminal can be
  opened, copy the command shown.
- Claude token refresh is left to Claude Code. Codex refresh is opt-in
  (`codex.refresh`) and re-reads `auth.json` before writing, so it never
  overwrites the Codex CLI.
- A token pasted during `setup` is stored in the OS keyring. A pasted Claude token
  cannot be renewed when it expires.
- `doctor` reporting `no token in file` does not always mean you are logged out:
  the file may hold plan metadata while sign-in is handled elsewhere.

</details>

<details>
<summary><b>How alerts are delivered</b></summary>

- **Windows** shows tokentray's own cards. Set `native_notifications = true` for
  Windows banners instead; if a banner cannot be submitted, the card is shown.
  Welcome and login-recovery alerts always use cards so their buttons work.
- **macOS and Linux** show cards and also send an OS notification.
- **Test notification** fetches fresh usage and shows one card per service. It
  does not touch alert history or send webhooks.
- Every OS notification attempt is logged; open the log from the tray menu.

</details>

## Privacy

> [!NOTE]
> No telemetry. Claude and Codex tokens are sent only to `api.anthropic.com`,
> `chatgpt.com` and `auth.openai.com` over HTTPS. Webhooks receive alert text,
> never tokens.

Caches and the fallback secret file are owner-only (`0600`) on macOS and Linux;
on Windows they inherit your user profile's permissions.

## Contributing

Bug reports, ideas and pull requests are welcome, in English or Korean.

- 🐛 **Found a bug?** Open a [bug report](https://github.com/messy-snail/tokentray/issues/new?template=bug_report.yml)
  with `tokentray --version` and `tokentray doctor` output.
- 💡 **Have an idea?** Open a [feature request](https://github.com/messy-snail/tokentray/issues/new?template=feature_request.yml).
- 🔒 **Security issue?** Report it privately - see [SECURITY.md].
- 🛠️ **Want to send code?** Start with [CONTRIBUTING.md].

## License

MIT - see [LICENSE], which also keeps the MIT notice of
[haomingkoo/claude-codex-monitor](https://github.com/haomingkoo/claude-codex-monitor).
Standalone builds bundle Qt through PySide6 (LGPLv3) as dynamically linked libraries.

<!-- Absolute, because this file is also the PyPI long description and a
     relative link there resolves against pypi.org. -->
[LICENSE]: https://github.com/messy-snail/tokentray/blob/main/LICENSE
[SECURITY.md]: https://github.com/messy-snail/tokentray/blob/main/SECURITY.md
[CONTRIBUTING.md]: https://github.com/messy-snail/tokentray/blob/main/CONTRIBUTING.md
