Metadata-Version: 2.4
Name: token-paid
Version: 1.2.0
Summary: Inspect token usage in local Codex rollout JSONL files and WorkBuddy trace JSON files
Author: Codex usage CLI contributors
License-Expression: MIT
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# token-paid

[简体中文](README.zh-CN.md)

`token-paid` is a read-only command-line tool for inspecting token usage. It has
no third-party runtime dependencies and never modifies the files it reads.

Version 1.2 adds a responsive, ASCII-compatible usage dashboard, a shared
English/Chinese interface, and ID-based record/session discovery for both
commands. It retains token-paid's detailed Codex call inspection and WorkBuddy
support.

It ships two commands:

| Command | Reads | Source files |
| --- | --- | --- |
| `token-paid` | Codex | `rollout-*.jsonl` session files |
| `token-paid-wb` | WorkBuddy | `trace_*.json` workflow files |

## Features

### `token-paid` (Codex)

- Shows a dashboard title, session overview, aligned tables, human-readable
  durations, and thousands-separated numbers.
- Switches table columns automatically below 120 terminal columns; `--compact`
  forces the compact layout.
- Supports English and Chinese through `--lang` and `TOKEN_PAID_LANG`.
- Analyzes the most recently modified session by default.
- Lists recent distinct Threads with `--list [N]`, selects one with `--thread`,
  and aggregates a root conversation plus child Threads with `--session`.
- Shows per-model-call usage with `--rounds`.
- Ranks the N newest sessions by total tokens with `--top N`.
- Exports per-call rows from the newest session with `--csv PATH`.
- Reports input, cached input, non-cached input, output, reasoning output, and
  total tokens.
- Reports `token_count` events, recognized model calls, timestamps, and duration.
- Reports the model and model provider in the session summary, per-call table,
  leaderboard, and CSV export when the rollout records them.
- Tolerates blank lines, malformed JSON, empty files, missing fields, UTF-8 BOM,
  and common snake_case/camelCase field variants.
- Uses `last_token_usage` when available, derives calls from increasing
  cumulative totals when necessary, and sums calls when no cumulative total
  exists.

### `token-paid-wb` (WorkBuddy)

- Uses the same responsive dashboard and language mechanism as `token-paid`.
- Reads every `trace_*.json` under `~/.workbuddy/traces/<worker-pid>/`, one
  file per agent workflow (task).
- Shows the newest trace by default.
- Lists recent traces with `--list [N]`, selects one with `--trace`, and
  aggregates all traces from a WorkBuddy session with `--session`.
- Aggregates all traces with `--all`.
- Breaks totals down by model (`--by-model`), day (`--by-day`), session
  (`--by-session`), or status (`--by-status`).
- Ranks the heaviest traces with `--top N`.
- Filters by status (`--status ok|error|cancelled`) and start date
  (`--since YYYY-MM-DD`).
- Exports one row per trace with `--csv PATH`.
- Decodes generation responses when they contain per-call model and usage data,
  including duration, input, cache, output, reasoning, and total tokens.
- Shows decoded WorkBuddy calls only when `--rounds` is requested, matching the
  opt-in behavior of the Codex command.
- Distinguishes the configured model in `modelInfo` from the response model in
  each generation result.
- Prefers complete generation-call totals when `modelInfo` missed the final
  call; otherwise safely falls back to `modelInfo`.

## Requirements

- Python 3.9 or newer
- A local Codex sessions directory and/or a local WorkBuddy traces directory

## Install from PyPI

After the package has been published:

```console
python -m pip install token-paid
```

For a user-only installation:

```console
python -m pip install --user token-paid
```

If the installer reports that the Python Scripts directory is not on `PATH`,
add the exact directory shown in that warning to your user `PATH`, then open a
new terminal.

## Install from source

Run this command in the directory containing `pyproject.toml`:

```console
python -m pip install .
```

Or install only for the current user:

```console
python -m pip install --user .
```

Installing registers both `token-paid` and `token-paid-wb`.

## Usage

```console
token-paid
token-paid --lang zh
token-paid --lang en --rounds
token-paid --compact --rounds
token-paid --rounds
token-paid --top 10
token-paid --list
token-paid --list 50
token-paid --thread 01a067bc
token-paid --thread 01a067bc --rounds --csv "thread-usage.csv"
token-paid --session 01a043e5
token-paid --session 01a043e5 --rounds
token-paid --csv "usage.csv"
token-paid --top 10 --rounds --csv "usage.csv"
token-paid --sessions-path "C:\path\to\sessions"
```

By default, the tool reads `%CODEX_HOME%\sessions` when `CODEX_HOME` is set.
Otherwise, it reads `%USERPROFILE%\.codex\sessions` on Windows.

`--top N` first selects the N most recently modified sessions, then ranks that
set by `total_tokens`.

### `token-paid` command reference

| Command or option | Operation performed |
| --- | --- |
| `token-paid` | Recursively finds `rollout-*.jsonl` under the sessions directory, selects the newest file by modification time, and displays session metadata, cumulative usage, and the latest model call. Files are read only. |
| `--rounds` | Adds a per-model-call table for the newest session. Full mode shows round, timestamp, model, provider, Input, Cached Input, Non-cached Input, Output, Reasoning Output, Total, and usage source. Compact mode hides secondary table columns while retaining the core summary counters. |
| `--top N` | Selects the N most recently modified sessions first, reads them, and then ranks that set by total tokens. It does not search all history for the N largest sessions. The newest-session detail is still printed first. |
| `--list [N]` | Lists the newest N distinct Codex Threads so their IDs can be copied into `--thread` or `--session`; the default is 20. Multiple rollout files belonging to a resumed Thread are merged into one row before the limit is applied. This discovery mode does not print calls or write CSV. |
| `--thread ID_OR_PREFIX` | Selects one Thread by its full ID or an unambiguous prefix. A resumed Thread may span several rollout files; all of its segments are aggregated and listed as members. Add `--rounds` to combine every call chronologically, or `--csv PATH` to export calls from every segment. |
| `--session ID_OR_PREFIX` | Selects one root session by its full ID or an unambiguous prefix. It aggregates the root Thread, child/subagent Threads, and resumed rollout segments sharing that session ID, then prints the member table. Add `--rounds` for one chronological call table with a source Thread ID, or `--csv PATH` to export every member call. |
| `--csv PATH` | Writes one row per recognized model call. By default it exports the newest rollout; with `--thread` or `--session`, it exports all selected members. Combining it only with `--top N` still exports the newest rollout, not the leaderboard. Rows include both `ThreadId` and `SessionId`; missing parent directories are created and the file uses UTF-8 with BOM. |
| `--sessions-path PATH` | Recursively searches the supplied directory instead of the default Codex sessions directory. It does not change `CODEX_HOME`. |
| `--lang zh` / `--lang en` | Changes terminal titles, field names, help, notices, and errors. It does not translate the stable CSV column names or affect calculations. |
| `--compact` | Forces compact per-call and leaderboard tables. Without it, terminals narrower than 120 columns enter compact mode automatically. |
| `-h` / `--help` | Prints help in the selected language and exits without reading session files. |

Common combinations:

```console
# Newest session plus every recognized model call
token-paid --rounds

# Newest-session detail plus a ranking of the 10 newest sessions
token-paid --top 10

# Discover IDs, then inspect one resumed Thread
token-paid --list 20
token-paid --thread 01a067bc --rounds

# Aggregate a root session and all of its child Threads
token-paid --session 01a043e5
token-paid --session 01a043e5 --rounds --csv "session-usage.csv"

# Calls, 10-session ranking, and a call-level CSV for the newest session
token-paid --rounds --top 10 --csv "usage.csv"
```

`cached_input_tokens` is already included in `input_tokens`, and
`reasoning_output_tokens` is already included in `output_tokens`. They are
shown as diagnostic details and are not added to Total a second time.

### Language and terminal layout

Language selection uses this priority: `--lang` > `TOKEN_PAID_LANG` > system
locale > English. Supported values are `en` and `zh`.

```powershell
$env:TOKEN_PAID_LANG = "zh"
token-paid --rounds
token-paid-wb --all --by-model
```

The dashboard uses ASCII borders and separators for PowerShell/CMD
compatibility. Tables show all diagnostic columns in terminals at least 120
columns wide and switch to essential columns in narrower terminals. Summary
sections keep the core counters in either mode. Use `--compact` to force the
compact table layout.

## WorkBuddy usage (`token-paid-wb`)

### Where the data comes from

WorkBuddy writes one JSON document per agent workflow (task) to:

```text
~/.workbuddy/traces/<worker-pid>/trace_<id>.json
```

On Windows this resolves to `%USERPROFILE%\.workbuddy\traces`, and it can be
overridden with the `WORKBUDDY_HOME` environment variable or `--traces-path`.

Each document has this shape:

```json
{
  "trace": {
    "traceId": "trace_6bb196ea267646168467cabf42c2e447",
    "name": "Agent workflow",
    "workerPid": 14228,
    "startedAt": "2026-09-03T14:55:33.236Z",
    "endedAt": "2026-09-03T14:58:56.400Z",
    "duration": 203164,
    "status": "ok",
    "spanCount": 59,
    "totalTokens": 378922,
    "sessionId": "145eb1e9-6dca-4200-9a36-3d4152a86128",
    "agentName": "cli",
    "modelInfo": {
      "models": ["hy3"],
      "totalInputTokens": 376017,
      "totalOutputTokens": 2905,
      "totalCachedTokens": 324864,
      "lastCallInputTokens": 376017,
      "callCount": 8
    }
  },
  "spans": []
}
```

`duration` is in milliseconds. Token counters live on `trace` and, when the run
actually reached a model, on `trace.modelInfo`.

### Commands

```console
token-paid-wb
token-paid-wb --rounds
token-paid-wb --lang zh
token-paid-wb --compact --top 10
token-paid-wb --list
token-paid-wb --list 50
token-paid-wb --trace 6bb196ea
token-paid-wb --trace trace_6bb196ea --rounds
token-paid-wb --session 145eb1e9
token-paid-wb --session 145eb1e9 --rounds --csv "workbuddy-session.csv"
token-paid-wb --top 10
token-paid-wb --all
token-paid-wb --all --by-model --by-day
token-paid-wb --by-session
token-paid-wb --all --since 2026-09-01 --status ok
token-paid-wb --all --csv "workbuddy-usage.csv"
token-paid-wb --traces-path "C:\path\to\traces"
```

Without any selection flag, only the newest trace is read and `--csv` exports
just that trace. Add `--all` (or `--top N`) to export more.

`--limit N` caps the number of rows in leaderboards (default `25`; `0` means
unlimited).

### `token-paid-wb` command reference

| Command or option | Operation performed |
| --- | --- |
| `token-paid-wb` | Recursively finds `trace_*.json` and displays the most recently modified readable trace that passes the filters, including workflow, status, configured and response models, calls, duration, and token totals. It does not show per-call rows unless `--rounds` is used. |
| `--rounds` | Appends the decoded generation-call table for the newest matching trace after the summary. It can be combined with `--top`, `--all`, or grouping options; cumulative totals still use complete generation data even when this display option is absent. |
| `--list [N]` | Lists the newest N readable traces with full Trace ID, Session ID, start time, status, model, calls, and total tokens; the default is 20. Use it to discover values for `--trace` and `--session`. It cannot be combined with `--rounds` or `--csv`. |
| `--trace ID_OR_PREFIX` | Selects one trace by full ID or an unambiguous prefix. The optional literal `trace_` prefix may be omitted. Add `--rounds` for that trace's decoded calls or `--csv PATH` to export that trace. `--status` and `--since` are intentionally not applied to exact-trace lookup. |
| `--session ID_OR_PREFIX` | Selects every trace sharing one WorkBuddy Session ID, prints aggregate totals and a trace member table, and accepts a full ID or unambiguous prefix. `--status` and `--since` may narrow the members first. Add `--rounds` to merge all decoded calls chronologically with their source Trace IDs, or `--csv PATH` to export all selected traces. |
| `--top N` | Without `--all` or grouping options, selects the N newest matching traces, displays the newest trace in detail, and then ranks those N traces by total tokens. |
| `--all` | Reads every trace matching `--status` and `--since`, then displays a grand summary and leaderboard. Group tables are only added when their corresponding `--by-*` options are present. |
| `--by-model` | Reads all matching traces and groups trace count, model calls, and tokens by model. |
| `--by-day` | Reads all matching traces and groups them by start date. Traces without a start time use the unknown-date group. |
| `--by-session` | Reads all matching traces and groups them by `sessionId`; traces without one use the no-session group. |
| `--by-status` | Reads all matching traces and groups them by their recorded status, such as `ok`, `error`, or `cancelled`. |
| `--status NAME` | Filters by status before newest-trace selection, `--top`, `--all`, grouping, and CSV export. Matching is case-insensitive; `all` disables the filter. |
| `--since YYYY-MM-DD` | Before selection and aggregation, keeps traces whose start time is on or after the local date. Traces without a start time are excluded. |
| `--limit N` | Limits displayed rows in leaderboards and grouped tables only. It does not limit traces included in the grand totals or CSV. The default is 25; `0` is unlimited. |
| `--csv PATH` | Writes one row per selected trace. By default this is the newest trace; with `--top N` it is the N selected traces; with `--all` or any `--by-*` option it is every matching trace. Aggregate response models and reasoning tokens are included; prompt text is never exported. |
| `--traces-path PATH` | Recursively searches the supplied directory instead of the default WorkBuddy traces directory. It does not change `WORKBUDDY_HOME`. |
| `--lang zh` / `--lang en` | Uses the same language mechanism as the Codex command and affects terminal UI only. |
| `--compact` | Uses compact widths for the per-call table and compact columns for leaderboard/grouped tables while retaining the summary token fields. |
| `-h` / `--help` | Prints help in the selected language and exits without reading trace files. |

Important combinations:

- `--list`, `--trace`, and `--session` are mutually exclusive discovery/selection
  modes and cannot be combined with `--top`, `--all`, or `--by-*` options.
- `token-paid-wb --session 145eb1e9 --status ok --rounds` limits that session
  to successful traces and then combines their calls in start-time order.
- `token-paid-wb --top 10` reads the 10 newest matching traces and ranks only
  that set by total tokens.
- `token-paid-wb --all --top 10` reads and aggregates every matching trace but
  limits the displayed leaderboard to 10 rows; CSV still includes every match.
- `token-paid-wb --by-model --by-day` scans the matching traces once and prints
  both model and daily group tables.
- `token-paid-wb --status ok --since 2026-09-01 --all` applies both filters
  before calculating the grand summary and leaderboard.

### Two things to know about this data

**1. `totalTokens` is often `0` and must not be trusted on its own.** WorkBuddy
only flushes `trace.totalTokens` when a run finishes normally. In practice a
large share of trace files report `"totalTokens": 0` even though `modelInfo`
contains real usage. The tool therefore computes the total as
`totalInputTokens + totalOutputTokens` whenever a `modelInfo` block exists, and
falls back to `totalTokens` otherwise. When the two disagree, the tool prints a
note to stderr.

**2. Generation responses can contain per-call usage.** A generation span's
`toolOutput` can encode an OpenAI-compatible response containing `model` and
`usage`. Add `--rounds` to append the decoded calls after the summary:

```text
Per-call model usage
Call  Started  Model  Status  Duration  Input  Cached Input  Non-cached Input  Output  Reasoning Output  Total
```

The configured model comes from `trace.modelInfo.models`; the response model
comes from each decoded generation response. They can differ.

When the number of decoded generation calls is at least
`modelInfo.callCount`, generation sums are used because `modelInfo` can omit the
final call. If fewer calls can be decoded, cumulative totals remain based on
`modelInfo`, while the available call rows are still shown. A trace with no
`modelInfo` can also be reconstructed from generation calls; if neither source
contains detailed usage, non-zero `trace.totalTokens` remains the final
fallback.

## How totals are calculated

### Codex

For each `token_count` event, the tool prefers `last_token_usage` for the
per-call row. If that field is absent and a cumulative total increased, it
derives a row from the difference. The session total uses the latest cumulative
record when available; otherwise, it sums the recognized calls.

`cached_input_tokens` is part of `input_tokens`, and
`reasoning_output_tokens` is a detail within `output_tokens`. They should not be
added to the total a second time.

### WorkBuddy

For each trace, complete generation-call usage is preferred and summed across
input, cached input, output, reasoning output, and total tokens. If generation
data is incomplete, the total is `totalInputTokens + totalOutputTokens` from
`modelInfo`. If `modelInfo` is absent too, the tool falls back to
`trace.totalTokens`. Group totals are plain sums over the selected trace totals.

`totalCachedTokens` is part of `totalInputTokens`, so it must not be added to
the total a second time. The tool reports the cache hit rate as
`totalCachedTokens / totalInputTokens`.

## CSV and privacy

CSV files are written as UTF-8 with BOM for convenient use with Microsoft
Excel. An exported CSV can contain a session identifier and a local rollout
path. Review those fields before sharing the file.

The tool reads rollout and trace files but never modifies them.

A small number of WorkBuddy traces also contain a `prompt` field holding the
user's prompt text. `token-paid-wb` never prints, aggregates, or exports that
field. The exported CSV does contain a `TracePath` column with a local file
path, so review it before sharing.

## License

MIT
