tledger 0.1.4 → 0.2.1

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 CHANGED
@@ -1,115 +1,179 @@
1
1
  # Token Ledger
2
2
 
3
- Token Ledger is a lightweight, local-only terminal dashboard for Codex token
4
- usage. It ranks projects, names every active model—including Daybreak Blue—and
5
- shows cache and reset-cycle context without sending usage data anywhere. Codex
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
- Token Ledger reads Codex history stored on the computer where it runs. It does
10
- not sign in, fetch account-wide usage, or combine activity from other machines.
7
+ ## Install
11
8
 
12
- ![Token Ledger running with synthetic demo data](https://raw.githubusercontent.com/jskoiz/token-ledger/main/docs/token-ledger-demo.svg)
9
+ Requires Node.js 22.13 or newer.
13
10
 
14
- _The screenshot is generated from an intentionally synthetic fixture._
11
+ Once published, install the CLI with:
15
12
 
16
- ## Requirements
13
+ ```bash
14
+ npm install -g tledger
15
+ ```
17
16
 
18
- Token Ledger requires Node.js 22.13 or newer and npm. Install a supported LTS
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
- node --version
24
- npm --version
20
+ npx tledger week
25
21
  ```
26
22
 
27
- ## Install
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
- npm install --global tledger
33
- tledger --version
26
+ git clone <repository-url>
27
+ cd token-ledger
28
+ npm install -g .
34
29
  ```
35
30
 
36
- ## Use
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 # current seven-day window
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
- `week`, `month`, and custom ranges such as `90d` are inclusive rolling
51
- calendar-day windows ending today unless an end day is supplied.
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
- Useful options:
46
+ Other common views:
59
47
 
60
- ```text
61
- --tz <zone> Use another IANA timezone
62
- --top <1-100> Change the project limit
63
- --refresh Force a fresh local scan
64
- --no-refresh Use the existing cache without a freshness check
65
- --input <file> Read an explicit privacy-reduced snapshot
66
- --codex-home <dir> Read another Codex data directory
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 1d
50
+ tledger 1d --static
51
+ tledger day 2026-08-05
52
+ tledger week --top 5
53
+ tledger week --static
54
+ tledger trend 7d --static
55
+ tledger trend 7d --image --image-output artifacts/token-ledger-trend-7d.png
56
+ tledger report 7d
77
57
  ```
78
58
 
79
- ## Update or uninstall
59
+ `tledger 1d` shows the `TOKENS BY PROJECT` breakdown for the rolling 24 hours
60
+ ending when the command starts. It is different from `tledger day today`, which
61
+ covers the current calendar day from local midnight.
62
+
63
+ `tledger report [7d|14d|30d]` is the one-step report output: it writes the
64
+ dashboard PNG (identical to `trend --image`) to
65
+ `token-ledger-report-<period>.png` in the current directory. It accepts the
66
+ same flags as the trend view (`--drain`, `--date`, `--tz`, `--image-output`,
67
+ `--image-width`) and prints progress while rendering and encoding the image.
68
+
69
+ The terminal trend view is a compact approximation of the image view. For the
70
+ full chart grammar, use `--image`: it writes a PNG with two aligned panels
71
+ sharing one time axis — the observed weekly meter as a line on its own 0–100%
72
+ panel on top, and calendar-day columns of local token volume stacked by model
73
+ below, each annotated with the observed drop ("−18.1%" means the meter fell
74
+ 18.1 points across that column's days). The drain numbers come straight from
75
+ the meter — no pricing model involved — so token volume and actual limit
76
+ consumption can be compared per day at a glance.
77
+
78
+ Turns run in fast mode (service tier "priority") are drawn as a darker shade
79
+ at the top of their model's segment, with each model's fast share in the
80
+ legend; fast-mode turns are weighted 1.5× in the credit estimate because they
81
+ debit the plan limit at a higher rate.
82
+
83
+ Pass `--drain` to flip the columns into limit-drain units instead: each column
84
+ becomes the weekly limit percentage the meter dropped, stacked by model using
85
+ rate-card credit weights (an estimate — the official card is the best
86
+ available proxy for per-model debit, but subscription limits are not billed
87
+ per token), on the same percent scale as the meter line.
88
+
89
+ Meter windows are keyed by their server-reported reset timestamp, so a fresh
90
+ window that starts days early (a provider-initiated limit restart) is drawn as
91
+ its own cycle and labeled `restart`, while a true weekly expiry is labeled
92
+ `reset`.
93
+ Stale readings from sessions still reporting a superseded window are dropped
94
+ instead of being fused into the line as phantom drain. Drops observed after a
95
+ sparse meter gap (over 36 hours) are spread across the covered days and marked
96
+ with `≈`. When a range has no usable meter drain, columns fall back to raw
97
+ local token counts and the chart says so.
98
+
99
+ `--image` defaults to `token-ledger-trend-7d.png` in the current directory.
100
+ Use `--image-output <file.png>` to choose the path and `--image-width <px>` to
101
+ choose a width from 900 to 2400 pixels.
102
+
103
+ Use `trend 14d` for daily columns across two weeks. At 30 days, the terminal
104
+ uses readable multi-day bins: three-day bins at ordinary widths and two-day
105
+ bins on wider terminals. The image view uses the same readable multi-day
106
+ binning at 30 days. The image also reports the separate local rate-card credit
107
+ estimate when token breakdowns are available; that estimate is an absolute
108
+ credit count and never rescales the observed-drain columns.
109
+
110
+ The CLI automatically checks the local Codex source files before rendering. If
111
+ they are newer than the local cache, it rebuilds the privacy-reduced snapshot.
112
+ The first refresh may scan historical rollout files; later runs use the cache
113
+ for one hour before checking source freshness again. Use `--refresh` when you
114
+ need to force an immediate rebuild.
115
+
116
+ The `1d` project dashboard shows a compact snapshot-age line such as
117
+ `SNAPSHOT · fresh · 12m old`. `fresh` means the snapshot is within the
118
+ one-hour cache window, `stale` means it is older, and `age unknown` means the
119
+ snapshot has no usable capture-time metadata. The indicator does not print a
120
+ local path or trigger another source scan.
121
+
122
+ Useful overrides:
80
123
 
81
124
  ```bash
82
- npm install --global tledger@latest
83
- tledger --version
84
- npm uninstall --global tledger
125
+ # Use another timezone instead of the computer's local timezone
126
+ tledger week --tz America/New_York
127
+
128
+ # Force a complete local refresh
129
+ tledger week --refresh
130
+
131
+ # Skip the freshness check and use the existing cache
132
+ tledger week --no-refresh
133
+
134
+ # Read a specific privacy-reduced snapshot
135
+ tledger week --input /path/to/token-ledger-snapshot.json
85
136
  ```
86
137
 
87
- Uninstalling the npm package leaves the privacy-reduced cache at
88
- `~/.token-ledger/token-ledger-snapshot.json`. Remove that directory separately
89
- only when you also want the next installation to perform a completely fresh
90
- scan.
138
+ `--static` prints once for pipes, logs, or terminals without interactive input.
139
+ `--plain` or `NO_COLOR=1` disables ANSI color. `--youplot` is an optional
140
+ legacy renderer and is not required for the default dashboard.
91
141
 
92
142
  ## Local data and privacy
93
143
 
94
- By default, Token Ledger reads `CODEX_HOME` or `~/.codex` and keeps a
95
- privacy-reduced cache at `~/.token-ledger/token-ledger-snapshot.json`. It checks
96
- source freshness automatically. Fresh scans use a bounded pool of up to four
97
- workers; unchanged runs read the existing cache. Token Ledger makes no network
98
- requests and excludes
99
- message bodies, reasoning text, tool payloads, credentials, and full local
100
- paths from the cache. Project labels can still reveal local context, so keep
101
- snapshots private unless you have reviewed them. Use `-anon` to hide project
102
- names in terminal output; it does not rewrite the cached snapshot. Reset-cycle
103
- burn is an estimate, not official quota or billing data.
144
+ The CLI reads from `CODEX_HOME` when set, otherwise `~/.codex`. It uses local
145
+ Codex rollout JSONL files, the session index, and local state metadata. It
146
+ writes its privacy-reduced cache to:
147
+
148
+ ```text
149
+ ~/.token-ledger/token-ledger-snapshot.json
150
+ ```
151
+
152
+ The collector does not export message bodies, reasoning text, tool arguments or
153
+ results, credentials, file contents, or full local paths. Display titles may
154
+ contain user-written text. CLI errors and empty-state source labels show only a
155
+ safe filename label, not an absolute input or source path. When a PNG or report
156
+ is written, the explicit output path is reported so you can find the file. The
157
+ CLI makes no network requests.
158
+
159
+ ## Keyboard controls
160
+
161
+ In the interactive dashboard:
104
162
 
105
- ## Develop
163
+ - `↑` / `↓` or `j` / `k` moves between projects.
164
+ - `q`, `Q`, `Esc`, or `Ctrl-C` exits.
165
+ - Enter does not inspect a project, and `d` / `w` / `m` do not change the
166
+ range; choose the desired range in the command instead.
167
+
168
+ ## Verify from source
106
169
 
107
170
  ```bash
108
171
  npm test
109
- npm run demo
172
+ npm run lint
110
173
  npm run verify:release
174
+ npm pack --dry-run --json
111
175
  ```
112
176
 
113
- ## License
114
-
115
- [MIT](LICENSE)
177
+ `npm run verify:release` packs the allowlisted artifact, installs that tarball
178
+ in a clean temporary directory with no network or Codex data access, and runs
179
+ the installed `tledger --help` and synthetic `tledger 1d --static` smoke checks.
@@ -0,0 +1,24 @@
1
+ export const INTERACTIVE_KEY_INPUTS = Object.freeze({
2
+ up: Object.freeze(["\u001b[A", "k"]),
3
+ down: Object.freeze(["\u001b[B", "j"]),
4
+ quit: Object.freeze(["q", "Q", "\u0003", "\u001b"]),
5
+ });
6
+
7
+ export const INTERACTIVE_FOOTER = Object.freeze({
8
+ ascii: "[j/k] select [q/Q/esc/ctrl-c] quit",
9
+ unicode: "[↑↓/j/k] select [q/Q/esc/ctrl-c] quit",
10
+ });
11
+
12
+ export const INTERACTIVE_HELP = "↑/↓ or j/k move • q/Q, esc, or ctrl-c quit";
13
+
14
+ export function actionFor(input) {
15
+ const value = String(input);
16
+ if (value.includes(INTERACTIVE_KEY_INPUTS.up[0]) || value === INTERACTIVE_KEY_INPUTS.up[1]) {
17
+ return "up";
18
+ }
19
+ if (value.includes(INTERACTIVE_KEY_INPUTS.down[0]) || value === INTERACTIVE_KEY_INPUTS.down[1]) {
20
+ return "down";
21
+ }
22
+ if (INTERACTIVE_KEY_INPUTS.quit.includes(value)) return "quit";
23
+ return null;
24
+ }
@@ -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
+ }