tledger 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -80
- package/bin/token-ledger-rates.mjs +62 -0
- package/bin/token-ledger-terminal.mjs +133 -257
- package/bin/token-ledger-trend-image.mjs +945 -0
- package/bin/token-ledger-trend-terminal.mjs +609 -0
- package/bin/token-ledger-trend.mjs +745 -0
- package/bin/token-ledger-tui.mjs +15 -21
- package/bin/token-ledger.mjs +408 -248
- package/lib/{token-ledger-collector.mjs → token-ledger-importer.mjs} +256 -409
- package/package.json +18 -14
- package/lib/token-ledger-models.mjs +0 -113
package/README.md
CHANGED
|
@@ -1,115 +1,157 @@
|
|
|
1
1
|
# Token Ledger
|
|
2
2
|
|
|
3
|
-
Token Ledger is a
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Auto Review is shown separately with its token total, distinct-turn share, and
|
|
7
|
-
cached-input share.
|
|
3
|
+
Token Ledger is a small, local-only terminal dashboard for Codex usage. It
|
|
4
|
+
shows ranked project bars, model mix, usage type, cache coverage, and reset
|
|
5
|
+
cycle context without sending your data anywhere.
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
not sign in, fetch account-wide usage, or combine activity from other machines.
|
|
7
|
+
## Install
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
Requires Node.js 22.13 or newer.
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
Once published, install the CLI with:
|
|
15
12
|
|
|
16
|
-
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g tledger
|
|
15
|
+
```
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
release from [nodejs.org](https://nodejs.org/en/download) if either command is
|
|
20
|
-
missing, then confirm the versions in a new terminal:
|
|
17
|
+
For a one-time run without a permanent global install:
|
|
21
18
|
|
|
22
19
|
```bash
|
|
23
|
-
|
|
24
|
-
npm --version
|
|
20
|
+
npx tledger week
|
|
25
21
|
```
|
|
26
22
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Token Ledger has no runtime npm dependencies and runs no installation script.
|
|
23
|
+
Until the package is published, install it from this repository:
|
|
30
24
|
|
|
31
25
|
```bash
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
git clone <repository-url>
|
|
27
|
+
cd token-ledger
|
|
28
|
+
npm install -g .
|
|
34
29
|
```
|
|
35
30
|
|
|
36
|
-
|
|
31
|
+
The package uses a small runtime image encoder for PNG report output, Node's
|
|
32
|
+
built-in SQLite support, and reads Codex data directly from the local machine.
|
|
33
|
+
|
|
34
|
+
## Run
|
|
35
|
+
|
|
36
|
+
The shortest useful command is:
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
tledger
|
|
40
|
-
tledger week # same default, stated explicitly
|
|
41
|
-
tledger day # today
|
|
42
|
-
tledger day yesterday
|
|
43
|
-
tledger week 2026-08-05
|
|
44
|
-
tledger month # rolling 30-day window
|
|
45
|
-
tledger 90d # rolling 90-day window
|
|
46
|
-
tledger 90d 2026-08-05 # 90 days ending on a chosen day
|
|
47
|
-
tledger all # every dated event in the local snapshot
|
|
39
|
+
tledger week
|
|
48
40
|
```
|
|
49
41
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
The local timezone and top 10 projects are selected automatically. The default
|
|
54
|
-
view is interactive in a terminal; use arrow keys or `j`/`k` to move and `q` or
|
|
55
|
-
Escape to exit. If the selected period is empty, Token Ledger reports the most
|
|
56
|
-
recent local activity date and prints the exact command for opening it.
|
|
42
|
+
It defaults to today's seven-day window, the computer's local timezone, the
|
|
43
|
+
top 10 projects, and the local Codex data directory. The default terminal view
|
|
44
|
+
is interactive; press `q` or `esc` to exit.
|
|
57
45
|
|
|
58
|
-
|
|
46
|
+
Other common views:
|
|
59
47
|
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
--top
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
--no-archived Skip archived sessions during collection
|
|
68
|
-
--raw-projects Keep singleton project labels separate
|
|
69
|
-
-anon Show Project 1, Project 2, etc. instead of project names
|
|
70
|
-
--static Print once instead of opening the interactive view
|
|
71
|
-
--plain Print once without ANSI color
|
|
72
|
-
--ascii Use ASCII bars
|
|
73
|
-
--width <40-200> Set the static layout width
|
|
74
|
-
--date <day> Set today, yesterday, or YYYY-MM-DD
|
|
75
|
-
-v, --version Show the installed version
|
|
76
|
-
--help Show complete CLI help
|
|
48
|
+
```bash
|
|
49
|
+
tledger day 2026-08-05
|
|
50
|
+
tledger week --top 5
|
|
51
|
+
tledger week --static
|
|
52
|
+
tledger trend 7d --static
|
|
53
|
+
tledger trend 7d --image --image-output artifacts/token-ledger-trend-7d.png
|
|
54
|
+
tledger report 7d
|
|
77
55
|
```
|
|
78
56
|
|
|
79
|
-
|
|
57
|
+
`tledger report [7d|14d|30d]` is the one-step report output: it writes the
|
|
58
|
+
dashboard PNG (identical to `trend --image`) to
|
|
59
|
+
`token-ledger-report-<period>.png` in the current directory. It accepts the
|
|
60
|
+
same flags as the trend view (`--drain`, `--date`, `--tz`, `--image-output`,
|
|
61
|
+
`--image-width`) and prints progress while rendering and encoding the image.
|
|
62
|
+
|
|
63
|
+
The terminal trend view is a compact approximation of the image view. For the
|
|
64
|
+
full chart grammar, use `--image`: it writes a PNG with two aligned panels
|
|
65
|
+
sharing one time axis — the observed weekly meter as a line on its own 0–100%
|
|
66
|
+
panel on top, and calendar-day columns of local token volume stacked by model
|
|
67
|
+
below, each annotated with the observed drop ("−18.1%" means the meter fell
|
|
68
|
+
18.1 points across that column's days). The drain numbers come straight from
|
|
69
|
+
the meter — no pricing model involved — so token volume and actual limit
|
|
70
|
+
consumption can be compared per day at a glance.
|
|
71
|
+
|
|
72
|
+
Turns run in fast mode (service tier "priority") are drawn as a darker shade
|
|
73
|
+
at the top of their model's segment, with each model's fast share in the
|
|
74
|
+
legend; fast-mode turns are weighted 1.5× in the credit estimate because they
|
|
75
|
+
debit the plan limit at a higher rate.
|
|
76
|
+
|
|
77
|
+
Pass `--drain` to flip the columns into limit-drain units instead: each column
|
|
78
|
+
becomes the weekly limit percentage the meter dropped, stacked by model using
|
|
79
|
+
rate-card credit weights (an estimate — the official card is the best
|
|
80
|
+
available proxy for per-model debit, but subscription limits are not billed
|
|
81
|
+
per token), on the same percent scale as the meter line.
|
|
82
|
+
|
|
83
|
+
Meter windows are keyed by their server-reported reset timestamp, so a fresh
|
|
84
|
+
window that starts days early (a provider-initiated limit restart) is drawn as
|
|
85
|
+
its own cycle and labeled `restart`, while a true weekly expiry is labeled
|
|
86
|
+
`reset`.
|
|
87
|
+
Stale readings from sessions still reporting a superseded window are dropped
|
|
88
|
+
instead of being fused into the line as phantom drain. Drops observed after a
|
|
89
|
+
sparse meter gap (over 36 hours) are spread across the covered days and marked
|
|
90
|
+
with `≈`. When a range has no usable meter drain, columns fall back to raw
|
|
91
|
+
local token counts and the chart says so.
|
|
92
|
+
|
|
93
|
+
`--image` defaults to `token-ledger-trend-7d.png` in the current directory.
|
|
94
|
+
Use `--image-output <file.png>` to choose the path and `--image-width <px>` to
|
|
95
|
+
choose a width from 900 to 2400 pixels.
|
|
96
|
+
|
|
97
|
+
Use `trend 14d` for daily columns across two weeks. At 30 days, the terminal
|
|
98
|
+
uses readable multi-day bins: three-day bins at ordinary widths and two-day
|
|
99
|
+
bins on wider terminals. The image view uses the same readable multi-day
|
|
100
|
+
binning at 30 days. The image also reports the separate local rate-card credit
|
|
101
|
+
estimate when token breakdowns are available; that estimate is an absolute
|
|
102
|
+
credit count and never rescales the observed-drain columns.
|
|
103
|
+
|
|
104
|
+
The CLI automatically checks the local Codex source files before rendering. If
|
|
105
|
+
they are newer than the local cache, it rebuilds the privacy-reduced snapshot.
|
|
106
|
+
The first refresh may scan historical rollout files; later runs use the cache
|
|
107
|
+
for one hour before checking source freshness again. Use `--refresh` when you
|
|
108
|
+
need to force an immediate rebuild.
|
|
109
|
+
|
|
110
|
+
Useful overrides:
|
|
80
111
|
|
|
81
112
|
```bash
|
|
82
|
-
|
|
83
|
-
tledger --
|
|
84
|
-
|
|
113
|
+
# Use another timezone instead of the computer's local timezone
|
|
114
|
+
tledger week --tz America/New_York
|
|
115
|
+
|
|
116
|
+
# Force a complete local refresh
|
|
117
|
+
tledger week --refresh
|
|
118
|
+
|
|
119
|
+
# Skip the freshness check and use the existing cache
|
|
120
|
+
tledger week --no-refresh
|
|
121
|
+
|
|
122
|
+
# Read a specific privacy-reduced snapshot
|
|
123
|
+
tledger week --input /path/to/token-ledger-snapshot.json
|
|
85
124
|
```
|
|
86
125
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
scan.
|
|
126
|
+
`--static` prints once for pipes, logs, or terminals without interactive input.
|
|
127
|
+
`--plain` or `NO_COLOR=1` disables ANSI color. `--youplot` is an optional
|
|
128
|
+
legacy renderer and is not required for the default dashboard.
|
|
91
129
|
|
|
92
130
|
## Local data and privacy
|
|
93
131
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
132
|
+
The CLI reads from `CODEX_HOME` when set, otherwise `~/.codex`. It uses local
|
|
133
|
+
Codex rollout JSONL files, the session index, and local state metadata. It
|
|
134
|
+
writes its privacy-reduced cache to:
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
~/.token-ledger/token-ledger-snapshot.json
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The collector does not export message bodies, reasoning text, tool arguments or
|
|
141
|
+
results, credentials, file contents, or full local paths. Display titles may
|
|
142
|
+
contain user-written text. The CLI makes no network requests.
|
|
104
143
|
|
|
105
|
-
##
|
|
144
|
+
## Keyboard controls
|
|
145
|
+
|
|
146
|
+
In the interactive dashboard:
|
|
147
|
+
|
|
148
|
+
- `↑` / `↓` or `j` / `k` moves between projects.
|
|
149
|
+
- `q` or `esc` exits.
|
|
150
|
+
|
|
151
|
+
## Verify from source
|
|
106
152
|
|
|
107
153
|
```bash
|
|
108
154
|
npm test
|
|
109
|
-
npm run
|
|
110
|
-
npm run
|
|
155
|
+
npm run lint
|
|
156
|
+
npm pack --dry-run
|
|
111
157
|
```
|
|
112
|
-
|
|
113
|
-
## License
|
|
114
|
-
|
|
115
|
-
[MIT](LICENSE)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Pricing weights are used only for the separate attribution lens. They are
|
|
2
|
+
// never used to scale or relabel the actual-token columns.
|
|
3
|
+
export const RATE_CARD_AS_OF = "2026-08-17";
|
|
4
|
+
|
|
5
|
+
// Fast mode (service tier "priority") debits the plan limit at a higher rate.
|
|
6
|
+
export const FAST_MODE_MULTIPLIER = 1.5;
|
|
7
|
+
|
|
8
|
+
export const RATE_CARD = {
|
|
9
|
+
"gpt-5.6-sol": { input: 125, cached: 12.5, output: 750 },
|
|
10
|
+
"gpt-5.6-terra": { input: 50, cached: 5, output: 300 },
|
|
11
|
+
"gpt-5.6-luna": { input: 5, cached: 0.5, output: 30 },
|
|
12
|
+
"gpt-5.5": { input: 125, cached: 12.5, output: 750 },
|
|
13
|
+
"gpt-5.5-cyber": { input: 500, cached: 50, output: 3_000 },
|
|
14
|
+
"gpt-5.4": { input: 62.5, cached: 6.25, output: 375 },
|
|
15
|
+
"gpt-5.4-mini": { input: 18.75, cached: 1.875, output: 113 },
|
|
16
|
+
"gpt-5.3-codex": { input: 43.75, cached: 4.375, output: 350 },
|
|
17
|
+
"gpt-5.2": { input: 43.75, cached: 4.375, output: 350 },
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function normalizeModel(model) {
|
|
21
|
+
const value = String(model || "unknown")
|
|
22
|
+
.trim()
|
|
23
|
+
.toLowerCase()
|
|
24
|
+
.replaceAll("_", "-");
|
|
25
|
+
if (RATE_CARD[value]) return value;
|
|
26
|
+
if (value.startsWith("gpt-5.6-sol")) return "gpt-5.6-sol";
|
|
27
|
+
if (value.startsWith("gpt-5.6-terra")) return "gpt-5.6-terra";
|
|
28
|
+
if (value.startsWith("gpt-5.6-luna")) return "gpt-5.6-luna";
|
|
29
|
+
if (value.startsWith("gpt-5.5-cyber")) return "gpt-5.5-cyber";
|
|
30
|
+
if (value.startsWith("gpt-5.5")) return "gpt-5.5";
|
|
31
|
+
if (value.startsWith("gpt-5.4-mini")) return "gpt-5.4-mini";
|
|
32
|
+
if (value.startsWith("gpt-5.4")) return "gpt-5.4";
|
|
33
|
+
if (value.startsWith("gpt-5.3-codex")) return "gpt-5.3-codex";
|
|
34
|
+
if (value.startsWith("gpt-5.2")) return "gpt-5.2";
|
|
35
|
+
return value || "unknown";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function hasDetailedBreakdown(usage) {
|
|
39
|
+
const totalTokens = Number(usage.totalTokens) || 0;
|
|
40
|
+
const inputTokens = Number(usage.inputTokens) || 0;
|
|
41
|
+
const outputTokens = Number(usage.outputTokens) || 0;
|
|
42
|
+
if (totalTokens === 0) return true;
|
|
43
|
+
return (
|
|
44
|
+
inputTokens + outputTokens === totalTokens &&
|
|
45
|
+
(inputTokens > 0 || outputTokens > 0)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function creditsForUsage(model, usage) {
|
|
50
|
+
if (!hasDetailedBreakdown(usage)) return null;
|
|
51
|
+
const rate = RATE_CARD[normalizeModel(model)];
|
|
52
|
+
if (!rate) return null;
|
|
53
|
+
const inputTokens = Math.max(0, Number(usage.inputTokens) || 0);
|
|
54
|
+
const cachedInputTokens = Math.max(0, Number(usage.cachedInputTokens) || 0);
|
|
55
|
+
const outputTokens = Math.max(0, Number(usage.outputTokens) || 0);
|
|
56
|
+
const cached = Math.min(inputTokens, cachedInputTokens);
|
|
57
|
+
const uncached = Math.max(0, inputTokens - cached);
|
|
58
|
+
return (
|
|
59
|
+
(uncached * rate.input + cached * rate.cached + outputTokens * rate.output) /
|
|
60
|
+
1_000_000
|
|
61
|
+
);
|
|
62
|
+
}
|