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 +21 -0
- package/README.md +397 -0
- package/card.js +134 -0
- package/cli.js +307 -0
- package/config.js +45 -0
- package/costs.js +69 -0
- package/display.js +342 -0
- package/init.js +260 -0
- package/package.json +60 -0
- package/proxy.js +304 -0
- package/statusline.js +205 -0
- package/statusline.sh +48 -0
- package/store.js +121 -0
- package/tracker.js +35 -0
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 };
|