tokburn 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tokburn contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,397 @@
1
+ # tokburn-cli
2
+
3
+ **See exactly how fast you're burning tokens and money across Claude Code sessions.**
4
+
5
+ tokburn is a lightweight local proxy that sits between your tools and the Anthropic API, silently tracking every token in and out. No cloud, no accounts, no data leaving your machine.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npm i -g tokburn
11
+ tokburn init
12
+ ```
13
+
14
+ The setup wizard starts the proxy, configures your shell, and sets up the Claude Code status line -- all in one command.
15
+
16
+ Or do it manually:
17
+
18
+ ```bash
19
+ tokburn start
20
+ export ANTHROPIC_BASE_URL=http://127.0.0.1:4088
21
+ ```
22
+
23
+ That's it. Use Claude Code (or any Anthropic API client) normally. tokburn quietly logs every request.
24
+
25
+ ---
26
+
27
+ ## `tokburn init` -- Setup Wizard
28
+
29
+ Interactive setup that detects your environment and configures everything:
30
+
31
+ ```
32
+ tokburn setup
33
+ ───────────────────────────────────
34
+
35
+ Detected: zsh shell, Claude Code installed
36
+
37
+ [1/4] Which Claude plan are you on?
38
+
39
+ 1) Pro ~500K tokens / 5hr window
40
+ 2) Max ~2M tokens / 5hr window
41
+ 3) API only (no plan limits)
42
+
43
+ > 1
44
+
45
+ [2/4] Start the proxy daemon?
46
+ Enables per-request tracking for detailed breakdowns.
47
+
48
+ 1) Yes, start now
49
+ 2) No, skip
50
+
51
+ > 1
52
+
53
+ tokburn proxy started (PID 12345)
54
+
55
+ [3/4] Add ANTHROPIC_BASE_URL to ~/.zshrc?
56
+ Required for the proxy to intercept API calls.
57
+
58
+ 1) Yes, add it
59
+ 2) No, I'll do it manually
60
+
61
+ > 1
62
+
63
+ Added to ~/.zshrc
64
+
65
+ [4/4] Configure Claude Code status line?
66
+
67
+ 1) Recommended model | ctx% | repo | limits | cost
68
+ 2) Minimal model | current rate limit
69
+ 3) Full everything including burn rate
70
+ 4) Custom pick your own modules
71
+ 5) Skip
72
+
73
+ > 1
74
+
75
+ Status line configured with 5 modules.
76
+
77
+ ───────────────────────────────────
78
+ Done. tokburn is ready.
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Status Line Integration
84
+
85
+ tokburn installs a modular status line into Claude Code that renders live data below your prompt.
86
+
87
+ ### Presets
88
+
89
+ **Recommended** -- the default, shows model, context window, repo, both rate limits, and cost:
90
+ ```
91
+ Opus 4.6 | ctx 13% | tokburn (main*) | $1.95
92
+ current ●●○○○○○○○○ 9% ↻ 3hr 32min
93
+ weekly ●●●●○○○○○○ 45% ↻ Fri 12:30PM
94
+ ```
95
+
96
+ **Minimal** -- just model info and the current 5-hour rate limit:
97
+ ```
98
+ Opus 4.6 | ctx 13%
99
+ current ●●○○○○○○○○ 9% ↻ 3hr 32min
100
+ ```
101
+
102
+ **Full** -- everything, including session token count and burn rate:
103
+ ```
104
+ Opus 4.6 | ctx 13% | tokburn (main*) | 142.8K tok | $1.95 | ~2.1K/min
105
+ current ●●○○○○○○○○ 9% ↻ 3hr 32min
106
+ weekly ●●●●○○○○○○ 45% ↻ Fri 12:30PM
107
+ ```
108
+
109
+ ### Module Picker
110
+
111
+ Choose option `4) Custom` during `tokburn init` to toggle individual modules:
112
+
113
+ ```
114
+ Status line modules:
115
+
116
+ [x] 1. Model + context Opus 4.6 | ctx 13%
117
+ [x] 2. Repo + branch tokburn (master*)
118
+ [x] 3. Current rate limit ○○○○○○○○○○ 9% 3hr 32min
119
+ [x] 4. Weekly rate limit ●●●●○○○○○○ 45% Fri 12:30PM
120
+ [ ] 5. Token count 142.8K tok
121
+ [x] 6. Cost estimate $1.95
122
+ [ ] 7. Burn rate (proxy) ~2.1K/min
123
+
124
+ Toggle a number (1-7), or press enter to confirm:
125
+ ```
126
+
127
+ Available modules:
128
+
129
+ | Module | Key | Description |
130
+ |---|---|---|
131
+ | Model + context | `model_context` | Active model name and context window usage % |
132
+ | Repo + branch | `repo_branch` | Current git repo name and branch (with dirty indicator) |
133
+ | Current rate limit | `current_limit` | 5-hour window dot bar with % used and reset countdown |
134
+ | Weekly rate limit | `weekly_limit` | 7-day window dot bar with % used and reset time |
135
+ | Token count | `token_count` | Total tokens consumed this session |
136
+ | Cost estimate | `cost` | Estimated USD cost based on model pricing |
137
+ | Burn rate | `burn_rate` | Tokens per minute from proxy data |
138
+
139
+ ---
140
+
141
+ ## Task Cards
142
+
143
+ The hidden `_task-summary` command renders per-task token summaries. Used by Claude Code hooks to show usage after each task.
144
+
145
+ **Collapsed** -- single-line summary:
146
+ ```
147
+ > Refactored auth middleware 12.4K tok $0.07 26% left
148
+ ```
149
+
150
+ **Expanded** -- full breakdown with progress bar:
151
+ ```
152
+ v Refactored auth middleware
153
+ ┌────────────────────────────────────────────────────┐
154
+ | This Task Session Total |
155
+ | In: 8,200 (2.0K cached) In: 142,800 |
156
+ | Out: 4,200 Out: 58,200 |
157
+ | Cost: $0.07 Cost: $1.95 |
158
+ | |
159
+ | ████████████████████████░░░░░░ 74% of 5hr limit |
160
+ | ~1hr 18min remaining 23 requests today |
161
+ └────────────────────────────────────────────────────┘
162
+ ```
163
+
164
+ At 90%+ usage, the collapsed card switches to a `!!` warning indicator.
165
+
166
+ ---
167
+
168
+ ## Commands
169
+
170
+ | Command | Description |
171
+ |---|---|
172
+ | `tokburn init` | Interactive setup wizard -- plan, proxy, shell, status line |
173
+ | `tokburn start` | Start the proxy daemon in the background |
174
+ | `tokburn stop` | Stop the proxy daemon |
175
+ | `tokburn status` | Show proxy status and quick today summary |
176
+ | `tokburn today` | Detailed breakdown by model with cost estimates |
177
+ | `tokburn week` | Last 7 days as an ASCII table |
178
+ | `tokburn live` | Real-time TUI dashboard (refreshes every second) |
179
+ | `tokburn reset` | Clear today's usage data (history preserved) |
180
+ | `tokburn export` | Dump all usage data as CSV |
181
+ | `tokburn scan` | Parse Claude Code JSONL logs for historical data |
182
+
183
+ Hidden commands (used internally by hooks and status line):
184
+
185
+ | Command | Description |
186
+ |---|---|
187
+ | `tokburn _burn-rate` | Output current tokens/min rate (single value) |
188
+ | `tokburn _task-summary [name]` | Render task card to stdout |
189
+
190
+ ### `tokburn start`
191
+
192
+ Start the proxy daemon. Listens on `localhost:4088` by default.
193
+
194
+ ### `tokburn stop`
195
+
196
+ Stop the proxy daemon.
197
+
198
+ ### `tokburn status`
199
+
200
+ Show whether the proxy is running and a quick summary of today's usage.
201
+
202
+ ```
203
+ tokburn proxy: ● running
204
+ Today: 142,850 tokens (23 requests) * $0.42
205
+ ```
206
+
207
+ ### `tokburn today`
208
+
209
+ Detailed breakdown of today's usage by model with cost estimates.
210
+
211
+ ```
212
+ tokburn -- Today (2026-03-31)
213
+ ───────────────────────────────────────
214
+ Total Tokens | 142,850
215
+ Input | 98,200
216
+ Output | 44,650
217
+ Requests | 23
218
+ Est. Cost | $0.42
219
+
220
+ By Model:
221
+ claude-sonnet-4 | 89,200 in | 31,400 out | $0.28
222
+ claude-haiku-4 | 9,000 in | 13,250 out | $0.06
223
+ ```
224
+
225
+ ### `tokburn week`
226
+
227
+ Last 7 days as an ASCII table.
228
+
229
+ ```
230
+ tokburn -- Last 7 Days
231
+ ──────────────────────────────────────────────────────
232
+ Date | Input | Output | Total | Cost
233
+ ──────────────────────────────────────────────────────
234
+ 2026-03-31 | 98,200 | 44,650 | 142,850 | $0.42
235
+ 2026-03-30 | 156,000 | 72,100 | 228,100 | $0.68
236
+ ...
237
+ ──────────────────────────────────────────────────────
238
+ Total | 523,400 | 241,200 | 764,600 | $2.14
239
+ ```
240
+
241
+ ### `tokburn live`
242
+
243
+ Real-time terminal dashboard with dot-indicator progress bar, burn rate, and recent requests. Refreshes every second. Press `q` to quit.
244
+
245
+ ### `tokburn reset`
246
+
247
+ Clear today's usage data. Historical data is preserved.
248
+
249
+ ### `tokburn export`
250
+
251
+ Dump all usage data as CSV.
252
+
253
+ ```bash
254
+ tokburn export > usage.csv
255
+ tokburn export -o usage.csv
256
+ ```
257
+
258
+ ### `tokburn scan`
259
+
260
+ Parse Claude Code's own JSONL log files for historical usage data.
261
+
262
+ ```bash
263
+ tokburn scan
264
+ tokburn scan -d /path/to/claude/projects
265
+ ```
266
+
267
+ ---
268
+
269
+ ## Benchmarks
270
+
271
+ Run with `npm test` (or `npm run benchmark` for benchmarks only):
272
+
273
+ | Metric | Result |
274
+ |---|---|
275
+ | Proxy overhead (JSON) | **0.26ms** avg |
276
+ | SSE TTFB overhead | **0.36ms** |
277
+ | Throughput | **1,198 req/s** (100 concurrent) |
278
+ | SSE parsing accuracy | **100% exact** from API usage fields |
279
+ | Fallback estimation | **avg 17.8% error** (chars/4 heuristic) |
280
+ | Cost calculation | **Exact match** to Anthropic published pricing |
281
+
282
+ The proxy forwards responses immediately and extracts usage asynchronously via `setImmediate()`. Your client never waits for tokburn.
283
+
284
+ ---
285
+
286
+ ## How It Works
287
+
288
+ tokburn runs a local HTTP proxy on `localhost:4088`. When you point your Anthropic API client at this proxy (via `ANTHROPIC_BASE_URL`), every request flows through it:
289
+
290
+ 1. Your request hits the local proxy
291
+ 2. The proxy forwards it to `api.anthropic.com` immediately
292
+ 3. The response streams back to your client with zero added latency
293
+ 4. After the response completes, tokburn asynchronously parses the usage data and logs it
294
+
295
+ For streaming (SSE) responses, tokburn pipes chunks directly to the client while buffering a copy for parsing. For non-streaming JSON responses, it parses the usage object from the response body.
296
+
297
+ All data is stored locally in `~/.tokburn/usage.jsonl` as newline-delimited JSON.
298
+
299
+ ---
300
+
301
+ ## Privacy
302
+
303
+ tokburn is fully local:
304
+
305
+ - The proxy runs on `localhost` only
306
+ - No data is sent anywhere except the original Anthropic API destination
307
+ - All usage logs stay in `~/.tokburn/` on your machine
308
+ - No analytics, no telemetry, no phone-home
309
+
310
+ ---
311
+
312
+ ## Configuration
313
+
314
+ Config lives at `~/.tokburn/config.json`:
315
+
316
+ ```json
317
+ {
318
+ "port": 4088,
319
+ "target": "https://api.anthropic.com",
320
+ "plan": "pro",
321
+ "statusline_modules": ["model_context", "repo_branch", "current_limit", "weekly_limit", "cost"],
322
+ "pricing": {}
323
+ }
324
+ ```
325
+
326
+ ### Custom Pricing
327
+
328
+ Override default per-million-token pricing:
329
+
330
+ ```json
331
+ {
332
+ "pricing": {
333
+ "claude-sonnet-4": { "input": 3, "output": 15 },
334
+ "claude-opus-4": { "input": 15, "output": 75 },
335
+ "claude-haiku-4": { "input": 0.80, "output": 4 }
336
+ }
337
+ }
338
+ ```
339
+
340
+ ### Custom Port
341
+
342
+ ```json
343
+ {
344
+ "port": 9090
345
+ }
346
+ ```
347
+
348
+ ### Custom Target
349
+
350
+ Point at a different API-compatible endpoint:
351
+
352
+ ```json
353
+ {
354
+ "target": "https://your-gateway.example.com"
355
+ }
356
+ ```
357
+
358
+ ---
359
+
360
+ ## Data Format
361
+
362
+ Each line in `~/.tokburn/usage.jsonl`:
363
+
364
+ ```json
365
+ {"timestamp":"2026-03-31T14:32:07.123Z","model":"claude-sonnet-4-20250514","input_tokens":1200,"output_tokens":340,"conversation_id":null,"latency_ms":2847}
366
+ ```
367
+
368
+ ---
369
+
370
+ ## Use with Claude Code
371
+
372
+ ```bash
373
+ # One-time setup
374
+ tokburn init
375
+
376
+ # Or manually:
377
+ tokburn start
378
+ export ANTHROPIC_BASE_URL=http://127.0.0.1:4088
379
+
380
+ # Use Claude Code normally
381
+ claude
382
+
383
+ # Check your burn rate
384
+ tokburn today
385
+ ```
386
+
387
+ Add to your shell profile (`.bashrc`, `.zshrc`) to make it permanent:
388
+
389
+ ```bash
390
+ export ANTHROPIC_BASE_URL=http://127.0.0.1:4088
391
+ ```
392
+
393
+ ---
394
+
395
+ ## License
396
+
397
+ MIT
package/card.js ADDED
@@ -0,0 +1,134 @@
1
+ /**
2
+ * tokburn — card.js
3
+ * Renders collapsible per-task token summary cards.
4
+ * Used by _task-summary command and tokburn live TUI.
5
+ */
6
+
7
+ const { calculateCost } = require('./costs');
8
+
9
+ const CARD_WIDTH = 54;
10
+ const INNER = CARD_WIDTH - 4; // inside the "| ... |" borders
11
+
12
+ // ── Public API ──────────────────────────────────────────────────────────────────
13
+
14
+ function renderCollapsed(taskName, taskTokens, sessionPct) {
15
+ const name = truncate(taskName || 'Task', 26);
16
+ const tok = abbreviate(taskTokens) + ' tok';
17
+ const cost = '$' + estimateCost(taskTokens).toFixed(2);
18
+ const left = Math.max(0, 100 - Math.round(sessionPct)) + '% left';
19
+
20
+ const icon = sessionPct >= 90 ? '!!' : '>';
21
+ // Right-align the numbers
22
+ const parts = [tok, cost, left];
23
+ return ' ' + icon + ' ' + name + ' ' + parts.join(' ');
24
+ }
25
+
26
+ function renderExpanded(taskName, taskInput, taskOutput, taskCached,
27
+ sessionInput, sessionOutput, sessionCost,
28
+ sessionPct, timeRemaining, requestCount) {
29
+ const lines = [];
30
+
31
+ // Header
32
+ lines.push(' v ' + truncate(taskName || 'Task', CARD_WIDTH - 4));
33
+
34
+ // Top border
35
+ lines.push(' ' + '\u250c' + '\u2500'.repeat(CARD_WIDTH - 2) + '\u2510');
36
+
37
+ // Column layout: left = this task, right = session total
38
+ const midCol = Math.floor(INNER / 2);
39
+
40
+ lines.push(boxRow('This Task', 'Session Total', midCol));
41
+ const cachedStr = taskCached > 0 ? ' (' + abbreviate(taskCached) + ' cached)' : '';
42
+ lines.push(boxRow(
43
+ 'In: ' + fmtNum(taskInput) + cachedStr,
44
+ 'In: ' + fmtNum(sessionInput),
45
+ midCol
46
+ ));
47
+ lines.push(boxRow(
48
+ 'Out: ' + fmtNum(taskOutput),
49
+ 'Out: ' + fmtNum(sessionOutput),
50
+ midCol
51
+ ));
52
+
53
+ const taskCostVal = estimateCost(taskInput + taskOutput);
54
+ lines.push(boxRow(
55
+ 'Cost: $' + taskCostVal.toFixed(2),
56
+ 'Cost: $' + sessionCost.toFixed(2),
57
+ midCol
58
+ ));
59
+
60
+ // Empty separator
61
+ lines.push(boxRow('', '', midCol));
62
+
63
+ // Progress bar
64
+ const barSpace = CARD_WIDTH - 24; // space for bar + label
65
+ const pctClamped = Math.min(100, Math.max(0, sessionPct));
66
+ const filled = Math.round(barSpace * (pctClamped / 100));
67
+ const empty = barSpace - filled;
68
+ const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(empty);
69
+ const pctLabel = Math.round(sessionPct) + '% of 5hr limit';
70
+ const barLine = bar + ' ' + pctLabel;
71
+ lines.push(padBox(barLine));
72
+
73
+ // Time + requests
74
+ const timeStr = timeRemaining ? '~' + timeRemaining + ' remaining' : '';
75
+ const reqStr = requestCount + ' requests today';
76
+ lines.push(boxRow(timeStr, reqStr, midCol));
77
+
78
+ // Bottom border
79
+ lines.push(' ' + '\u2514' + '\u2500'.repeat(CARD_WIDTH - 2) + '\u2518');
80
+
81
+ return lines.join('\n');
82
+ }
83
+
84
+ // ── Box drawing ─────────────────────────────────────────────────────────────────
85
+
86
+ function boxRow(left, right, midCol) {
87
+ left = truncate(String(left), midCol - 1);
88
+ right = truncate(String(right), INNER - midCol - 1);
89
+
90
+ const leftPadded = left + ' '.repeat(Math.max(0, midCol - left.length));
91
+ const rightPadded = right + ' '.repeat(Math.max(0, INNER - midCol - right.length));
92
+ const content = leftPadded + rightPadded;
93
+
94
+ // Ensure exact width
95
+ const trimmed = content.length > INNER
96
+ ? content.substring(0, INNER)
97
+ : content + ' '.repeat(INNER - content.length);
98
+
99
+ return ' \u2502 ' + trimmed + ' \u2502';
100
+ }
101
+
102
+ function padBox(content) {
103
+ const str = String(content);
104
+ const trimmed = str.length > INNER
105
+ ? str.substring(0, INNER)
106
+ : str + ' '.repeat(INNER - str.length);
107
+ return ' \u2502 ' + trimmed + ' \u2502';
108
+ }
109
+
110
+ // ── Helpers ─────────────────────────────────────────────────────────────────────
111
+
112
+ function abbreviate(n) {
113
+ if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
114
+ if (n >= 1000) return (n / 1000).toFixed(1) + 'K';
115
+ return String(n);
116
+ }
117
+
118
+ function fmtNum(n) {
119
+ return n.toLocaleString('en-US');
120
+ }
121
+
122
+ function truncate(str, maxLen) {
123
+ if (str.length <= maxLen) return str;
124
+ return str.substring(0, maxLen - 3) + '...';
125
+ }
126
+
127
+ function estimateCost(tokens) {
128
+ // Assume ~70% input, 30% output, sonnet pricing as default
129
+ const input = Math.round(tokens * 0.7);
130
+ const output = tokens - input;
131
+ return calculateCost('claude-sonnet-4', input, output);
132
+ }
133
+
134
+ module.exports = { renderCollapsed, renderExpanded, CARD_WIDTH };