@statforge/claudestat 1.2.0 → 1.2.2
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 +198 -86
- package/dist/claude-auth.d.ts +7 -0
- package/dist/claude-auth.js +12 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +12 -0
- package/dist/daemon.js +32 -15
- package/dist/db.d.ts +18 -0
- package/dist/db.js +50 -0
- package/dist/doctor.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +164 -76
- package/dist/insights.d.ts +26 -0
- package/dist/insights.js +172 -20
- package/dist/mcp-server.d.ts +1 -1
- package/dist/mcp-server.js +148 -21
- package/dist/notifier.js +24 -6
- package/dist/pattern-analyzer.d.ts +1 -0
- package/dist/pattern-analyzer.js +13 -0
- package/dist/quota-tracker.d.ts +6 -0
- package/dist/quota-tracker.js +79 -32
- package/dist/roast.js +129 -40
- package/dist/routes/events.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Works with Claude Pro, Max 5, and Max 20. Zero cloud dependencies. Pure Node.js.
|
|
|
12
12
|
[](LICENSE)
|
|
13
13
|
[](https://nodejs.org)
|
|
14
14
|
[]()
|
|
15
|
-
[]()
|
|
16
16
|
[](CONTRIBUTING.md)
|
|
17
17
|
|
|
18
18
|
[Installation](#installation) • [Quick Start](#quick-start) • [Commands](#commands) • [Dashboard](#dashboard) • [Contributing](#contributing)
|
|
@@ -139,13 +139,14 @@ That's it. Start a Claude Code session and watch the events flow in.
|
|
|
139
139
|
| `claudestat uninstall` | Remove hooks from Claude Code |
|
|
140
140
|
| `claudestat watch` | Live terminal trace view |
|
|
141
141
|
| `claudestat status` | Show quota, cost, and burn rate |
|
|
142
|
-
| `claudestat status --compact` | One-line output for tmux status bar |
|
|
143
142
|
| `claudestat config` | View or edit configuration |
|
|
144
143
|
| `claudestat top` | Rank tools by cost, call count, or duration |
|
|
145
144
|
| `claudestat weekly` | Weekly usage summary with actionable tips |
|
|
146
|
-
| `claudestat
|
|
147
|
-
| `claudestat share [session-id]` | Generate shareable session card (ASCII/JSON) |
|
|
145
|
+
| `claudestat insights` | Deep usage insights: cost breakdown, cache savings, efficiency, peak hours, model breakdown |
|
|
148
146
|
| `claudestat roast` | Sarcastic usage analysis with roast jokes |
|
|
147
|
+
| `claudestat roast --stats` | Raw stats with visual bars |
|
|
148
|
+
| `claudestat version` | Show version and check for npm updates |
|
|
149
|
+
| `claudestat export [format]` | Export session data to JSON or CSV |
|
|
149
150
|
| `claudestat doctor` | Check installation health and diagnose issues |
|
|
150
151
|
|
|
151
152
|
### `claudestat watch`
|
|
@@ -173,15 +174,32 @@ Ranks your most-used tools by estimated cost, call count, or duration across all
|
|
|
173
174
|
```
|
|
174
175
|
claudestat top
|
|
175
176
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
2
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
177
|
+
🏆 claudestat top by est. cost (last 30 days)
|
|
178
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
179
|
+
|
|
180
|
+
1 Edit ████████████████░░░░ $146.47 21%
|
|
181
|
+
2479 calls · 38.5m
|
|
182
|
+
2 Bash ███████████████░░░░░ $140.66 20%
|
|
183
|
+
2651 calls · 153.6m
|
|
184
|
+
3 Read ██████████████░░░░░░ $126.08 18%
|
|
185
|
+
2315 calls · 34.0m
|
|
186
|
+
4 Grep ████░░░░░░░░░░░░░░░░ $39.93 6%
|
|
187
|
+
699 calls · 9.3m
|
|
188
|
+
5 ToolSearch ██░░░░░░░░░░░░░░░░░░ $21.83 3%
|
|
189
|
+
469 calls · 7.4m
|
|
190
|
+
6 Glob ██░░░░░░░░░░░░░░░░░░ $13.96 2%
|
|
191
|
+
269 calls · 5.7m
|
|
192
|
+
7 Write █░░░░░░░░░░░░░░░░░░░ $12.93 2%
|
|
193
|
+
237 calls · 87.1m
|
|
194
|
+
8 mcp__plugin_engr… █░░░░░░░░░░░░░░░░░░░ $8.10 1%
|
|
195
|
+
149 calls · 2.6m
|
|
196
|
+
9 Agent █░░░░░░░░░░░░░░░░░░░ $8.09 1%
|
|
197
|
+
168 calls · 95.7m
|
|
198
|
+
10 WebFetch █░░░░░░░░░░░░░░░░░░░ $5.86 1%
|
|
199
|
+
106 calls · 9.9m
|
|
200
|
+
Other — $184.79 26%
|
|
201
|
+
|
|
202
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
185
203
|
```
|
|
186
204
|
|
|
187
205
|
Options: `--by cost|count|duration` · `--days 7|30|90` · `--limit N`
|
|
@@ -193,88 +211,177 @@ Weekly usage summary with an actionable tip. Detects patterns like Bash overuse,
|
|
|
193
211
|
```
|
|
194
212
|
claudestat weekly
|
|
195
213
|
|
|
196
|
-
📊 claudestat weekly
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
214
|
+
📊 claudestat weekly May 8 – May 13
|
|
215
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
216
|
+
|
|
217
|
+
💰 $198.38 total · 40 sessions · 114 loops
|
|
218
|
+
|
|
219
|
+
🔧 Top tool Bash 22% of cost
|
|
220
|
+
|
|
221
|
+
📈 Efficiency ██████████████████░░ 91/100
|
|
222
|
+
|
|
223
|
+
💾 Cache hit ████████████████████ 100%
|
|
224
|
+
|
|
225
|
+
📦 Tokens 73K in + 1.2M out
|
|
226
|
+
|
|
227
|
+
⚡ Tip: 114 loops detected — consider using /compact earlier to prevent context thrashing
|
|
228
|
+
|
|
229
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
201
230
|
```
|
|
202
231
|
|
|
203
232
|
Options: `--json` for machine-readable output.
|
|
204
233
|
|
|
205
234
|
### `claudestat status`
|
|
206
235
|
|
|
236
|
+
Shows your current quota usage with visual progress bars, plan detection, and burn rate.
|
|
237
|
+
|
|
207
238
|
```
|
|
208
239
|
claudestat status
|
|
209
240
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
241
|
+
📊 claudestat PRO plan
|
|
242
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
243
|
+
|
|
244
|
+
5h ████████████████████ 100% resets 4:10 AM
|
|
245
|
+
|
|
246
|
+
Week ██████░░░░░░░░░░░░░░ 31% resets May 18
|
|
247
|
+
|
|
248
|
+
🔥 490 tok/min · 101 prompts used
|
|
249
|
+
|
|
250
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
214
251
|
```
|
|
215
252
|
|
|
216
|
-
|
|
253
|
+
Options: `--json` for machine-readable output.
|
|
217
254
|
|
|
218
|
-
|
|
255
|
+
### `claudestat insights`
|
|
256
|
+
|
|
257
|
+
Deep usage insights: cost breakdown by project, cache savings, output/input ratio, efficiency trend, peak activity hours, and model breakdown.
|
|
219
258
|
|
|
220
|
-
```bash
|
|
221
|
-
claudestat status --compact
|
|
222
|
-
C:45%🟡 W:9%🟢 pro
|
|
223
259
|
```
|
|
260
|
+
claudestat insights
|
|
224
261
|
|
|
225
|
-
|
|
262
|
+
💡 claudestat insights last 7 days
|
|
263
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
226
264
|
|
|
227
|
-
|
|
265
|
+
💰 $4.96/session · 40 sessions · $198.38 total
|
|
228
266
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
267
|
+
🗂 Top projects
|
|
268
|
+
no project █████████░░░░░░░░░░░ $93.69 47%
|
|
269
|
+
|
|
270
|
+
claudestat ████████░░░░░░░░░░░░ $74.60 38%
|
|
271
|
+
|
|
272
|
+
wodrival ███░░░░░░░░░░░░░░░░░ $24.95 13%
|
|
273
|
+
|
|
274
|
+
aprendiendo-in ░░░░░░░░░░░░░░░░░░░░ $3.32 2%
|
|
275
|
+
|
|
276
|
+
other ░░░░░░░░░░░░░░░░░░░░ $1.81 1%
|
|
277
|
+
|
|
278
|
+
⚡ Cache ~$1029.43 saved · 100% hit rate
|
|
279
|
+
|
|
280
|
+
📊 16× output/input · cache-heavy workload
|
|
281
|
+
|
|
282
|
+
📈 Efficiency 91/100 ↓ -2 vs prev period · 114 loops
|
|
283
|
+
|
|
284
|
+
⏰ Activity by time of day
|
|
285
|
+
🌙 00:00–05:59 ████████████████████ 18 sessions
|
|
286
|
+
|
|
287
|
+
🌅 06:00–11:59 ███████░░░░░░░░░░░░░ 6 sessions
|
|
288
|
+
|
|
289
|
+
☀️ 12:00–17:59 ███░░░░░░░░░░░░░░░░░ 3 sessions
|
|
290
|
+
|
|
291
|
+
🌆 18:00–23:59 ██████████████░░░░░░ 13 sessions
|
|
292
|
+
|
|
293
|
+
🤖 Models
|
|
294
|
+
claude-sonnet-4-6 ████████████████████ $197.11 99% · 23 sessions
|
|
295
|
+
|
|
296
|
+
claude-haiku-4-5-20251001 ░░░░░░░░░░░░░░░░░░░░ $1.26 1% · 15 sessions
|
|
297
|
+
|
|
298
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
243
299
|
```
|
|
244
300
|
|
|
245
|
-
Options:
|
|
246
|
-
- `--format ascii|json` — output format (default: ascii)
|
|
247
|
-
- `--copy` — copy to clipboard automatically (macOS only)
|
|
301
|
+
Options: `--days 7|14|30|90` · `--json` for machine-readable output.
|
|
248
302
|
|
|
249
|
-
### `claudestat
|
|
303
|
+
### `claudestat config`
|
|
250
304
|
|
|
251
|
-
|
|
305
|
+
```
|
|
306
|
+
claudestat config
|
|
307
|
+
|
|
308
|
+
⚙️ claudestat config
|
|
309
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
310
|
+
|
|
311
|
+
Plan PRO
|
|
312
|
+
Alerts enabled
|
|
313
|
+
|
|
314
|
+
Kill switch OFF
|
|
315
|
+
████████████████████
|
|
316
|
+
|
|
317
|
+
Cycle thresholds 70%, 85%, 95%
|
|
318
|
+
yellow ████████░░ orange █████████░ red ██████████
|
|
319
|
+
|
|
320
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
321
|
+
```
|
|
252
322
|
|
|
253
323
|
```bash
|
|
254
|
-
|
|
324
|
+
# Enable kill switch at 90% quota
|
|
325
|
+
claudestat config --kill-switch true --threshold 90
|
|
326
|
+
|
|
327
|
+
# Force plan detection
|
|
328
|
+
claudestat config --plan max5 # pro | max5 | max20 | auto
|
|
255
329
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
Bash calls: 1,240
|
|
260
|
-
Loops: 8
|
|
261
|
-
Efficiency: 72/100
|
|
330
|
+
# Toggle daemon rate limit alerts
|
|
331
|
+
claudestat config --alerts false
|
|
332
|
+
```
|
|
262
333
|
|
|
263
|
-
|
|
334
|
+
Config is stored at `~/.claudestat/config.json` (macOS/Linux) or `%USERPROFILE%\.claudestat\config.json` (Windows).
|
|
264
335
|
|
|
265
|
-
|
|
266
|
-
That's once every 2.3 minutes.
|
|
267
|
-
Are you okay?
|
|
336
|
+
### `claudestat roast`
|
|
268
337
|
|
|
269
|
-
|
|
270
|
-
Claude was writing with amnesia half the time.
|
|
338
|
+
Get a sarcastic analysis of your Claude Code usage — humor with insights.
|
|
271
339
|
|
|
272
|
-
|
|
273
|
-
|
|
340
|
+
```bash
|
|
341
|
+
claudestat roast
|
|
274
342
|
|
|
275
|
-
|
|
343
|
+
🔥 Your Claude Code Roast (30 days)
|
|
344
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
345
|
+
|
|
346
|
+
Score ██████████████████░░ 92/100 ★★★★★
|
|
347
|
+
|
|
348
|
+
Scorecard
|
|
349
|
+
┌─────────────────┬──────────────┬──────────────┐
|
|
350
|
+
│ Metric │ Value │ Rating │
|
|
351
|
+
├─────────────────┼──────────────┼──────────────┤
|
|
352
|
+
│ Sessions │ 47 │ normal │
|
|
353
|
+
│ Total cost │ $12.40 │ frugal │
|
|
354
|
+
│ Avg/session │ $0.26/session│ efficient │
|
|
355
|
+
│ Bash calls │ 1240 │ 🔨 overload │
|
|
356
|
+
│ Loops │ 8 │ clean │
|
|
357
|
+
│ Efficiency │ 92/100 │ 🏆 elite │
|
|
358
|
+
│ Tokens │ 4.2M │ — │
|
|
359
|
+
│ Top tool │ Bash 38% │ — │
|
|
360
|
+
└─────────────────┴──────────────┴──────────────┘
|
|
361
|
+
|
|
362
|
+
Roast Cards
|
|
363
|
+
|
|
364
|
+
┌──────────────────────────────────────────────────┐
|
|
365
|
+
│ 🖥️ BASH OVERLOAD │
|
|
366
|
+
│ 1240 calls in 30d — once every 2.3 min │
|
|
367
|
+
│ Are you okay? │
|
|
368
|
+
└──────────────────────────────────────────────────┘
|
|
369
|
+
|
|
370
|
+
┌──────────────────────────────────────────────────┐
|
|
371
|
+
│ 🔄 LOOP MONEY PIT │
|
|
372
|
+
│ $4.20 wasted on loops — that's 14 coffees │
|
|
373
|
+
│ Just saying. │
|
|
374
|
+
└──────────────────────────────────────────────────┘
|
|
375
|
+
|
|
376
|
+
Verdict
|
|
377
|
+
You're a machine. Or maybe you're just not using Claude enough.
|
|
378
|
+
|
|
379
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
380
|
+
github.com/DeibyGS/claudestat
|
|
276
381
|
```
|
|
277
382
|
|
|
383
|
+
Options: `--stats` for raw stats with visual bars · `--months N` to look back N months.
|
|
384
|
+
|
|
278
385
|
### `claudestat doctor`
|
|
279
386
|
|
|
280
387
|
Diagnoses common installation problems — useful if `claudestat start` fails or hooks are not firing.
|
|
@@ -291,6 +398,9 @@ claudestat doctor
|
|
|
291
398
|
✓ Hook script deployed (~/.claudestat/hooks/event.js)
|
|
292
399
|
✓ Daemon running (localhost:7337)
|
|
293
400
|
✓ Global CLI symlink valid
|
|
401
|
+
✓ No duplicate claudestat binaries in PATH
|
|
402
|
+
✓ Version match (installed: v1.2.2)
|
|
403
|
+
✓ NVM prefix matches active binary
|
|
294
404
|
──────────────────────────────────────────────
|
|
295
405
|
All checks passed — claudestat is healthy!
|
|
296
406
|
```
|
|
@@ -299,6 +409,19 @@ If a check fails, `doctor` prints the exact fix command to run.
|
|
|
299
409
|
|
|
300
410
|
---
|
|
301
411
|
|
|
412
|
+
### `claudestat version`
|
|
413
|
+
|
|
414
|
+
Shows the current version and checks npm for updates.
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
claudestat version
|
|
418
|
+
|
|
419
|
+
1.2.2
|
|
420
|
+
latest ✓
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
If a newer version is available, it shows: `latest: 1.3.0 — run npm update`.
|
|
424
|
+
|
|
302
425
|
### `claudestat export`
|
|
303
426
|
|
|
304
427
|
Export session data to JSON or CSV. Supports date and project filters.
|
|
@@ -326,23 +449,6 @@ Each row includes: `id`, `started_at`, `cwd`, `project_path`, `total_cost_usd`,
|
|
|
326
449
|
|
|
327
450
|
---
|
|
328
451
|
|
|
329
|
-
### `claudestat config`
|
|
330
|
-
|
|
331
|
-
```bash
|
|
332
|
-
# Enable kill switch — block new sessions when quota exceeds 95%
|
|
333
|
-
claudestat config --kill-switch true --threshold 95
|
|
334
|
-
|
|
335
|
-
# Force plan detection instead of auto
|
|
336
|
-
claudestat config --plan max5 # pro | max5 | max20 | auto
|
|
337
|
-
|
|
338
|
-
# Disable daemon rate limit alerts
|
|
339
|
-
claudestat config --alerts false
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
Config is stored at `~/.claudestat/config.json` (macOS/Linux) or `%USERPROFILE%\.claudestat\config.json` (Windows).
|
|
343
|
-
|
|
344
|
-
---
|
|
345
|
-
|
|
346
452
|
## MCP Server
|
|
347
453
|
|
|
348
454
|
claudestat includes an MCP (Model Context Protocol) server that lets Claude Code query its own usage stats — Claude can tell you its quota, session cost, and top tools in real time.
|
|
@@ -351,10 +457,12 @@ claudestat includes an MCP (Model Context Protocol) server that lets Claude Code
|
|
|
351
457
|
|
|
352
458
|
| Tool | Description |
|
|
353
459
|
|------|------------|
|
|
354
|
-
| `get_quota_status` | 5h cycle usage %, plan, weekly hours, burn rate |
|
|
460
|
+
| `get_quota_status` | 5h cycle usage %, plan, weekly hours, burn rate (with on-demand API refresh + disk cache) |
|
|
355
461
|
| `get_current_session` | Latest session: cost, tokens, efficiency, loops |
|
|
356
462
|
| `get_session_stats` | Aggregated stats for N days |
|
|
357
|
-
| `get_top_tools` | Top 10 tools by cost/count/duration |
|
|
463
|
+
| `get_top_tools` | Top 10 tools by cost/count/duration (default 30 days) |
|
|
464
|
+
| `get_usage_insights` | Deep insights: cost per project, cache savings, efficiency trend, peak hours, model breakdown |
|
|
465
|
+
| `get_model_breakdown` | Cost and session count broken down by Claude model (Sonnet, Haiku, Opus) |
|
|
358
466
|
| `get_weekly_insight` | Weekly summary with actionable tip |
|
|
359
467
|
|
|
360
468
|
### Register with Claude Code
|
|
@@ -367,8 +475,10 @@ Once registered, ask Claude things like:
|
|
|
367
475
|
- *"What's my current quota status?"*
|
|
368
476
|
- *"Show me my latest session cost"*
|
|
369
477
|
- *"What are my top 5 tools by cost this week?"*
|
|
478
|
+
- *"Give me usage insights for the last 14 days"*
|
|
479
|
+
- *"Break down my usage by model"*
|
|
370
480
|
|
|
371
|
-
Zero extra dependencies — stdio JSON-RPC, works without the daemon running.
|
|
481
|
+
Zero extra dependencies — stdio JSON-RPC, works without the daemon running. Uses on-demand API refresh with shared disk cache for accurate quota data.
|
|
372
482
|
|
|
373
483
|
---
|
|
374
484
|
|
|
@@ -525,7 +635,7 @@ Whether you want to fix a bug, improve a dashboard view, add a new pattern to th
|
|
|
525
635
|
1. Fork the repository
|
|
526
636
|
2. Create a branch: `git checkout -b feat/your-feature`
|
|
527
637
|
3. Make your changes
|
|
528
|
-
4. Run the test suite: `
|
|
638
|
+
4. Run the test suite: `node --require tsx/cjs tests/index.ts` (243 tests)
|
|
529
639
|
5. Open a PR with a clear description of what you changed and why
|
|
530
640
|
|
|
531
641
|
### Good first areas
|
|
@@ -542,11 +652,13 @@ git clone https://github.com/YOUR_USERNAME/claudestat
|
|
|
542
652
|
cd claudestat
|
|
543
653
|
npm install
|
|
544
654
|
npm run dev:full # starts daemon + dashboard hot-reload together
|
|
545
|
-
|
|
655
|
+
node --require tsx/cjs tests/index.ts # run all tests
|
|
546
656
|
```
|
|
547
657
|
|
|
548
658
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for full guidelines.
|
|
549
659
|
|
|
660
|
+
This project follows the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md).
|
|
661
|
+
|
|
550
662
|
---
|
|
551
663
|
|
|
552
664
|
## Contributors
|
package/dist/claude-auth.d.ts
CHANGED
|
@@ -18,7 +18,14 @@ export interface ClaudeAuthInfo {
|
|
|
18
18
|
expiresAt: number;
|
|
19
19
|
tokenValid: boolean;
|
|
20
20
|
source: 'keychain' | 'file' | 'unknown';
|
|
21
|
+
accessToken?: string;
|
|
21
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Devuelve el accessToken OAuth para autenticar llamadas a la API de Anthropic.
|
|
25
|
+
* Usa el mismo caché de 5 minutos que readClaudeAuth().
|
|
26
|
+
* Retorna null si no hay credenciales disponibles (Linux sin configurar, token expirado).
|
|
27
|
+
*/
|
|
28
|
+
export declare function getOAuthAccessToken(): string | null;
|
|
22
29
|
/**
|
|
23
30
|
* Lee las credenciales de autenticación de Claude Code.
|
|
24
31
|
* Intenta: keychain (macOS) → archivo → unknown.
|
package/dist/claude-auth.js
CHANGED
|
@@ -17,6 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.getOAuthAccessToken = getOAuthAccessToken;
|
|
20
21
|
exports.readClaudeAuth = readClaudeAuth;
|
|
21
22
|
exports.subscriptionTypeToPlan = subscriptionTypeToPlan;
|
|
22
23
|
const child_process_1 = require("child_process");
|
|
@@ -50,6 +51,7 @@ function readFromKeychain() {
|
|
|
50
51
|
expiresAt: oa.expiresAt ?? 0,
|
|
51
52
|
tokenValid: Date.now() < (oa.expiresAt ?? 0),
|
|
52
53
|
source: 'keychain',
|
|
54
|
+
accessToken: oa.accessToken,
|
|
53
55
|
};
|
|
54
56
|
}
|
|
55
57
|
catch {
|
|
@@ -82,6 +84,7 @@ function readFromFile() {
|
|
|
82
84
|
expiresAt: oa.expiresAt ?? 0,
|
|
83
85
|
tokenValid: Date.now() < (oa.expiresAt ?? 0),
|
|
84
86
|
source: 'file',
|
|
87
|
+
accessToken: oa.accessToken,
|
|
85
88
|
};
|
|
86
89
|
}
|
|
87
90
|
catch {
|
|
@@ -90,6 +93,15 @@ function readFromFile() {
|
|
|
90
93
|
}
|
|
91
94
|
return null;
|
|
92
95
|
}
|
|
96
|
+
// ─── Token OAuth para llamadas a la API ───────────────────────────────────────
|
|
97
|
+
/**
|
|
98
|
+
* Devuelve el accessToken OAuth para autenticar llamadas a la API de Anthropic.
|
|
99
|
+
* Usa el mismo caché de 5 minutos que readClaudeAuth().
|
|
100
|
+
* Retorna null si no hay credenciales disponibles (Linux sin configurar, token expirado).
|
|
101
|
+
*/
|
|
102
|
+
function getOAuthAccessToken() {
|
|
103
|
+
return readClaudeAuth().accessToken ?? null;
|
|
104
|
+
}
|
|
93
105
|
// ─── API pública ──────────────────────────────────────────────────────────────
|
|
94
106
|
/**
|
|
95
107
|
* Lee las credenciales de autenticación de Claude Code.
|
package/dist/config.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export interface ClaudestatConfig {
|
|
|
23
23
|
killSwitchEnabled: boolean;
|
|
24
24
|
killSwitchThreshold: number;
|
|
25
25
|
warnThresholds: number[];
|
|
26
|
+
weeklyWarnThresholds: number[];
|
|
27
|
+
resetReminderMins: number;
|
|
26
28
|
plan: ClaudePlan | null;
|
|
27
29
|
reportsEnabled: boolean;
|
|
28
30
|
reportFrequency: ReportFrequency;
|
package/dist/config.js
CHANGED
|
@@ -34,6 +34,8 @@ const DEFAULTS = {
|
|
|
34
34
|
killSwitchEnabled: false,
|
|
35
35
|
killSwitchThreshold: 95,
|
|
36
36
|
warnThresholds: [70, 85, 95],
|
|
37
|
+
weeklyWarnThresholds: [50, 75, 90],
|
|
38
|
+
resetReminderMins: 10,
|
|
37
39
|
plan: null,
|
|
38
40
|
reportsEnabled: false,
|
|
39
41
|
reportFrequency: 'weekly',
|
|
@@ -80,6 +82,16 @@ function validateConfig(raw) {
|
|
|
80
82
|
}
|
|
81
83
|
if ('plan' in cfg && !VALID_PLANS.has(cfg.plan))
|
|
82
84
|
return `plan debe ser uno de: free, pro, max5, max20 o null`;
|
|
85
|
+
if ('weeklyWarnThresholds' in cfg) {
|
|
86
|
+
const v = cfg.weeklyWarnThresholds;
|
|
87
|
+
if (!Array.isArray(v) || v.length !== 3 || v.some(n => typeof n !== 'number' || isNaN(n) || n < 1 || n > 100))
|
|
88
|
+
return 'weeklyWarnThresholds must be an array of 3 numbers between 1 and 100';
|
|
89
|
+
}
|
|
90
|
+
if ('resetReminderMins' in cfg) {
|
|
91
|
+
const v = cfg.resetReminderMins;
|
|
92
|
+
if (typeof v !== 'number' || isNaN(v) || v < 0 || v > 60)
|
|
93
|
+
return 'resetReminderMins must be a number between 0 and 60';
|
|
94
|
+
}
|
|
83
95
|
if ('alertsEnabled' in cfg && typeof cfg.alertsEnabled !== 'boolean')
|
|
84
96
|
return 'alertsEnabled debe ser boolean';
|
|
85
97
|
if ('reportsEnabled' in cfg && typeof cfg.reportsEnabled !== 'boolean')
|
package/dist/daemon.js
CHANGED
|
@@ -139,7 +139,7 @@ async function migrateSessionSummaries(limit = 5) {
|
|
|
139
139
|
const summary = await (0, summarizer_1.summarizeSession)(events, s.total_cost_usd ?? 0, projectName);
|
|
140
140
|
if (summary) {
|
|
141
141
|
db_1.dbOps.updateSessionSummary(s.id, summary);
|
|
142
|
-
console.log(`[daemon] Summary
|
|
142
|
+
console.log(`[daemon] Summary generated for session ${s.id.slice(0, 8)}: "${summary}"`);
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
catch (err) {
|
|
@@ -175,7 +175,20 @@ const LEVEL_COLOR = {
|
|
|
175
175
|
orange: '\x1b[33m',
|
|
176
176
|
red: '\x1b[31m',
|
|
177
177
|
};
|
|
178
|
-
let
|
|
178
|
+
let _lastCycleAlertLevel = null;
|
|
179
|
+
let _lastWeeklyAlertLevel = null;
|
|
180
|
+
let _resetReminderFired = false;
|
|
181
|
+
function checkAlertLevel(level, lastLevel, logMsg, notifTitle, notifBody) {
|
|
182
|
+
if (!level)
|
|
183
|
+
return null;
|
|
184
|
+
const prevRank = lastLevel ? LEVEL_RANK[lastLevel] ?? 0 : 0;
|
|
185
|
+
const currRank = LEVEL_RANK[level];
|
|
186
|
+
if (currRank > prevRank) {
|
|
187
|
+
process.stderr.write(`${LEVEL_COLOR[level]}${logMsg}\x1b[0m\n`);
|
|
188
|
+
(0, notifier_1.sendDesktopNotification)(notifTitle, notifBody);
|
|
189
|
+
}
|
|
190
|
+
return currRank > prevRank ? level : lastLevel;
|
|
191
|
+
}
|
|
179
192
|
function startAlertPolling() {
|
|
180
193
|
alertInterval = setInterval(() => {
|
|
181
194
|
try {
|
|
@@ -183,20 +196,23 @@ function startAlertPolling() {
|
|
|
183
196
|
if (!cfg.alertsEnabled)
|
|
184
197
|
return;
|
|
185
198
|
const data = (0, quota_tracker_1.computeQuota)(cfg.plan ?? undefined);
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
199
|
+
const resetMins = Math.ceil(data.cycleResetMs / 60000);
|
|
200
|
+
// ── Cycle 5h alerts ──────────────────────────────────────────────────────
|
|
201
|
+
_lastCycleAlertLevel = checkAlertLevel((0, config_1.getWarnLevel)(data.cyclePct, cfg.warnThresholds), _lastCycleAlertLevel, `[claudestat] ⚠️ 5h cycle at ${data.cyclePct}% (${data.cyclePrompts}/${data.cycleLimit} prompts)`, 'claudestat — 5h cycle alert', `${data.cyclePct}% of cycle used · resets in ${resetMins}m`);
|
|
202
|
+
// ── Weekly alerts ────────────────────────────────────────────────────────
|
|
203
|
+
_lastWeeklyAlertLevel = checkAlertLevel((0, config_1.getWarnLevel)(data.weeklyPctAll, cfg.weeklyWarnThresholds), _lastWeeklyAlertLevel, `[claudestat] ⚠️ Weekly usage at ${data.weeklyPctAll}%`, 'claudestat — Weekly usage alert', `${data.weeklyPctAll}% of weekly quota used`);
|
|
204
|
+
// ── Reset reminder ───────────────────────────────────────────────────────
|
|
205
|
+
const reminderMs = (cfg.resetReminderMins ?? 10) * 60000;
|
|
206
|
+
if (reminderMs > 0) {
|
|
207
|
+
if (data.cycleResetMs > reminderMs * 1.5) {
|
|
208
|
+
_resetReminderFired = false; // cycle reset happened — arm reminder again
|
|
209
|
+
}
|
|
210
|
+
else if (data.cycleResetMs <= reminderMs && data.cycleResetMs > 0 && !_resetReminderFired) {
|
|
211
|
+
const mins = Math.ceil(data.cycleResetMs / 60000);
|
|
212
|
+
process.stderr.write(`\x1b[36m[claudestat] ⏰ Quota resets in ${mins}m — good time to wrap up\x1b[0m\n`);
|
|
213
|
+
(0, notifier_1.sendDesktopNotification)('claudestat — Quota reset soon', `Your 5h cycle resets in ${mins} min — good time to start a new task`);
|
|
214
|
+
_resetReminderFired = true;
|
|
198
215
|
}
|
|
199
|
-
_lastAlertLevel = level;
|
|
200
216
|
}
|
|
201
217
|
}
|
|
202
218
|
catch {
|
|
@@ -281,6 +297,7 @@ function startDaemon() {
|
|
|
281
297
|
}
|
|
282
298
|
// Polling de alertas de rate limit cada 60s
|
|
283
299
|
startAlertPolling();
|
|
300
|
+
// API quota data is refreshed on-demand by the CLI status command (disk cache shared)
|
|
284
301
|
});
|
|
285
302
|
// Manejo de error de puerto ocupado — fuera del callback para capturar EADDRINUSE
|
|
286
303
|
_server.on('error', (err) => {
|
package/dist/db.d.ts
CHANGED
|
@@ -144,4 +144,22 @@ export declare const dbOps: {
|
|
|
144
144
|
earliest: number;
|
|
145
145
|
latest: number;
|
|
146
146
|
};
|
|
147
|
+
getProjectCosts(days?: number): {
|
|
148
|
+
project: string;
|
|
149
|
+
session_count: number;
|
|
150
|
+
total_cost: number;
|
|
151
|
+
}[];
|
|
152
|
+
getHourlyDistribution(days?: number): {
|
|
153
|
+
hour: number;
|
|
154
|
+
session_count: number;
|
|
155
|
+
}[];
|
|
156
|
+
getCacheReadByModel(days: number): {
|
|
157
|
+
model: string;
|
|
158
|
+
cache_read: number;
|
|
159
|
+
}[];
|
|
160
|
+
getModelBreakdown(days: number): {
|
|
161
|
+
model: string;
|
|
162
|
+
total_cost: number;
|
|
163
|
+
session_count: number;
|
|
164
|
+
}[];
|
|
147
165
|
};
|
package/dist/db.js
CHANGED
|
@@ -416,6 +416,26 @@ const stmts = {
|
|
|
416
416
|
MAX(last_event_at) AS latest
|
|
417
417
|
FROM sessions
|
|
418
418
|
WHERE started_at >= ?
|
|
419
|
+
`),
|
|
420
|
+
getProjectCosts: db.prepare(`
|
|
421
|
+
SELECT
|
|
422
|
+
COALESCE(project_path, 'no project') AS project,
|
|
423
|
+
COUNT(*) AS session_count,
|
|
424
|
+
COALESCE(SUM(total_cost_usd), 0) AS total_cost
|
|
425
|
+
FROM sessions
|
|
426
|
+
WHERE started_at >= ? AND total_cost_usd > 0
|
|
427
|
+
GROUP BY project_path
|
|
428
|
+
ORDER BY total_cost DESC
|
|
429
|
+
LIMIT 5
|
|
430
|
+
`),
|
|
431
|
+
getHourlyDistribution: db.prepare(`
|
|
432
|
+
SELECT
|
|
433
|
+
CAST(strftime('%H', datetime(started_at/1000, 'unixepoch', 'localtime')) AS INTEGER) AS hour,
|
|
434
|
+
COUNT(*) AS session_count
|
|
435
|
+
FROM sessions
|
|
436
|
+
WHERE started_at >= ?
|
|
437
|
+
GROUP BY hour
|
|
438
|
+
ORDER BY hour ASC
|
|
419
439
|
`),
|
|
420
440
|
getUnattributedCost: db.prepare(`
|
|
421
441
|
WITH period_cost AS (
|
|
@@ -582,4 +602,34 @@ exports.dbOps = {
|
|
|
582
602
|
const since = Date.now() - days * 86400000;
|
|
583
603
|
return stmts.getCostProjection.get(since);
|
|
584
604
|
},
|
|
605
|
+
getProjectCosts(days = 7) {
|
|
606
|
+
const since = Date.now() - days * 86400000;
|
|
607
|
+
return stmts.getProjectCosts.all(since);
|
|
608
|
+
},
|
|
609
|
+
getHourlyDistribution(days = 7) {
|
|
610
|
+
const since = Date.now() - days * 86400000;
|
|
611
|
+
return stmts.getHourlyDistribution.all(since);
|
|
612
|
+
},
|
|
613
|
+
getCacheReadByModel(days) {
|
|
614
|
+
const since = Date.now() - days * 86400000;
|
|
615
|
+
return db.prepare(`
|
|
616
|
+
SELECT COALESCE(dominant_model, 'unknown') as model, SUM(total_cache_read) as cache_read
|
|
617
|
+
FROM sessions
|
|
618
|
+
WHERE started_at >= ?
|
|
619
|
+
GROUP BY dominant_model
|
|
620
|
+
`).all(since);
|
|
621
|
+
},
|
|
622
|
+
getModelBreakdown(days) {
|
|
623
|
+
const since = Date.now() - days * 86400000;
|
|
624
|
+
return db.prepare(`
|
|
625
|
+
SELECT
|
|
626
|
+
COALESCE(dominant_model, 'unknown') as model,
|
|
627
|
+
SUM(total_cost_usd) as total_cost,
|
|
628
|
+
COUNT(*) as session_count
|
|
629
|
+
FROM sessions
|
|
630
|
+
WHERE started_at >= ?
|
|
631
|
+
GROUP BY dominant_model
|
|
632
|
+
ORDER BY total_cost DESC
|
|
633
|
+
`).all(since);
|
|
634
|
+
},
|
|
585
635
|
};
|
package/dist/doctor.js
CHANGED
|
@@ -183,6 +183,7 @@ async function runDoctor() {
|
|
|
183
183
|
const failed = checks.filter(c => !c.ok).length;
|
|
184
184
|
if (failed === 0) {
|
|
185
185
|
console.log(' \x1b[32mAll checks passed — claudestat is healthy!\x1b[0m\n');
|
|
186
|
+
process.exit(0);
|
|
186
187
|
}
|
|
187
188
|
else {
|
|
188
189
|
console.log(` \x1b[31m${failed} check(s) failed — see fixes above\x1b[0m\n`);
|