tledger 0.1.3 → 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 CHANGED
@@ -1,103 +1,157 @@
1
1
  # Token Ledger
2
2
 
3
- Token Ledger is a lightweight, local-only terminal dashboard for Codex token
4
- usage. It ranks projects, shows model and cache mix, and adds reset-cycle
5
- context without sending usage data anywhere. Codex Auto Review is shown
6
- separately with its token total, distinct-turn share, and 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.
7
6
 
8
- Token Ledger reads Codex history stored on the computer where it runs. It does
9
- not sign in, fetch account-wide usage, or combine activity from other machines.
7
+ ## Install
10
8
 
11
- ![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.
12
10
 
13
- _The screenshot is generated from an intentionally synthetic fixture._
11
+ Once published, install the CLI with:
14
12
 
15
- ## Requirements
13
+ ```bash
14
+ npm install -g tledger
15
+ ```
16
16
 
17
- Token Ledger requires Node.js 22.13 or newer and npm. Install a supported LTS
18
- release from [nodejs.org](https://nodejs.org/en/download) if either command is
19
- missing, then confirm the versions in a new terminal:
17
+ For a one-time run without a permanent global install:
20
18
 
21
19
  ```bash
22
- node --version
23
- npm --version
20
+ npx tledger week
24
21
  ```
25
22
 
26
- ## Install
27
-
28
- Token Ledger has no runtime npm dependencies and runs no installation script.
23
+ Until the package is published, install it from this repository:
29
24
 
30
25
  ```bash
31
- npm install --global tledger
32
- tledger --help
26
+ git clone <repository-url>
27
+ cd token-ledger
28
+ npm install -g .
33
29
  ```
34
30
 
35
- ## 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:
36
37
 
37
38
  ```bash
38
- tledger # current seven-day window
39
- tledger week # same default, stated explicitly
40
- tledger day # today
41
- tledger day yesterday
42
- tledger week 2026-08-05
39
+ tledger week
43
40
  ```
44
41
 
45
- The local timezone and top 10 projects are selected automatically. The default
46
- view is interactive in a terminal; use arrow keys or `j`/`k` to move and `q` or
47
- Escape to exit. If the selected period is empty, Token Ledger reports the most
48
- 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.
49
45
 
50
- Useful options:
46
+ Other common views:
51
47
 
52
- ```text
53
- --tz <zone> Use another IANA timezone
54
- --top <1-100> Change the project limit
55
- --refresh Force a fresh local scan
56
- --no-refresh Use the existing cache without a freshness check
57
- --input <file> Read an explicit privacy-reduced snapshot
58
- --codex-home <dir> Read another Codex data directory
59
- --no-archived Skip archived sessions during collection
60
- --raw-projects Keep singleton project labels separate
61
- --static Print once instead of opening the interactive view
62
- --plain Print once without ANSI color
63
- --ascii Use ASCII bars
64
- --width <40-200> Set the static layout width
65
- --date <day> Set today, yesterday, or YYYY-MM-DD
66
- --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
67
55
  ```
68
56
 
69
- ## Update or uninstall
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:
70
111
 
71
112
  ```bash
72
- npm install --global tledger@latest
73
- npm uninstall --global tledger
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
74
124
  ```
75
125
 
76
- Uninstalling the npm package leaves the privacy-reduced cache at
77
- `~/.token-ledger/token-ledger-snapshot.json`. Remove that directory separately
78
- only when you also want the next installation to perform a completely fresh
79
- 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.
80
129
 
81
130
  ## Local data and privacy
82
131
 
83
- By default, Token Ledger reads `CODEX_HOME` or `~/.codex` and keeps a
84
- privacy-reduced cache at `~/.token-ledger/token-ledger-snapshot.json`. It checks
85
- source freshness automatically. Fresh scans use a bounded pool of up to four
86
- workers; unchanged runs read the existing cache. Token Ledger makes no network
87
- requests and excludes
88
- message bodies, reasoning text, tool payloads, credentials, and full local
89
- paths from the cache. Project labels can still reveal local context, so keep
90
- snapshots private unless you have reviewed them. Reset-cycle burn
91
- is an estimate, not official quota or billing data.
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.
92
143
 
93
- ## Develop
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
94
152
 
95
153
  ```bash
96
154
  npm test
97
- npm run demo
155
+ npm run lint
98
156
  npm pack --dry-run
99
157
  ```
100
-
101
- ## License
102
-
103
- [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
+ }