ccusage 17.2.0 → 18.0.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/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { $ as uniq, A as projectBlockUsage, D as calculateBurnRate, E as DEFAULT_SESSION_DURATION_HOURS, F as _usingCtx, G as DEFAULT_RECENT_DAYS, I as BLOCKS_COMPACT_WIDTH_THRESHOLD, J as MIN_REFRESH_INTERVAL_SECONDS, K as DEFAULT_REFRESH_INTERVAL_SECONDS, L as BLOCKS_DEFAULT_TERMINAL_WIDTH, M as formatDateCompact, N as getFileModifiedTime, O as filterRecentBlocks, P as unreachable, R as BLOCKS_WARNING_THRESHOLD, U as DEFAULT_CONTEXT_USAGE_THRESHOLDS, V as CONFIG_FILE_NAME, W as DEFAULT_LOCALE, Y as MIN_RENDER_INTERVAL_MS, Z as WEEK_DAYS, _ as loadWeeklyUsageData, at as inspect, c as getEarliestTimestamp, ct as isSuccess, dt as toArray, et as unwrap, f as loadDailyUsageData, ft as __commonJSMin, g as loadSessionUsageById, h as loadSessionData, i as createUniqueHash, it as inspectError, j as PricingFetcher, k as identifySessionBlocks, l as getUsageLimitResetTime, lt as andThen, m as loadSessionBlockData, mt as __toESM, n as calculateContextTokens, nt as pipe, ot as fail, p as loadMonthlyUsageData, pt as __require, q as MAX_REFRESH_INTERVAL_SECONDS, r as calculateCostForEntry, rt as map$2, s as getClaudePaths, st as succeed, tt as try_, u as globUsageFiles, ut as isFailure, w as usageDataSchema, x as sortFilesByTimestamp, z as BURN_RATE_THRESHOLDS } from "./data-loader-Cwm3YlHL.js";
2
+ import { $ as inspectError, D as calculateBurnRate, E as DEFAULT_SESSION_DURATION_HOURS, F as BLOCKS_COMPACT_WIDTH_THRESHOLD, H as DEFAULT_LOCALE, I as BLOCKS_DEFAULT_TERMINAL_WIDTH, J as uniq, K as WEEK_DAYS, L as BLOCKS_WARNING_THRESHOLD, M as getFileModifiedTime, N as unreachable, O as filterRecentBlocks, P as _usingCtx, Q as map$2, U as DEFAULT_RECENT_DAYS, V as DEFAULT_CONTEXT_USAGE_THRESHOLDS, W as DEFAULT_REFRESH_INTERVAL_SECONDS, X as try_, Y as unwrap, Z as pipe, _ as loadWeeklyUsageData, at as __commonJSMin, et as inspect, f as loadDailyUsageData, g as loadSessionUsageById, h as loadSessionData, it as toArray, j as formatDateCompact, k as projectBlockUsage, m as loadSessionBlockData, n as calculateContextTokens, nt as isSuccess, ot as __require, p as loadMonthlyUsageData, rt as isFailure, s as getClaudePaths, st as __toESM, tt as succeed, z as CONFIG_FILE_NAME } from "./data-loader-Cn1U7E-7.js";
3
3
  import { D as maxValue, E as integer$1, F as string, I as transform, L as trim, M as parse$1, N as pipe$1, O as minValue, P as safeParse, R as union, T as flatten, d as filterDateSchema, k as number, n as SortOrders, t as CostModes, w as check, y as statuslineHookJsonSchema, z as getTotalTokens } from "./_types-BFL_bTNX.js";
4
4
  import { n as createTotalsObject, t as calculateTotals } from "./calculate-cost-CKH-OC_c.js";
5
- import { a as version, i as name, n as logger, r as description, t as log } from "./logger-CxSjxkLv.js";
6
- import { n as printMismatchReport, t as detectMismatches } from "./debug-BjAuokNY.js";
5
+ import { a as version, i as name, n as logger, r as description, t as log } from "./logger-ClkaWGUb.js";
6
+ import { n as printMismatchReport, t as detectMismatches } from "./debug-DjEsvIbZ.js";
7
7
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
8
- import a, { readFile, stat } from "node:fs/promises";
8
+ import a from "node:fs/promises";
9
9
  import path, { join } from "node:path";
10
10
  import process$1 from "node:process";
11
11
  import { tmpdir } from "node:os";
@@ -1194,19 +1194,19 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1194
1194
  function hyperlink(url, text) {
1195
1195
  const OSC = "\x1B]";
1196
1196
  const BEL = "\x07";
1197
- const SEP$1 = ";";
1197
+ const SEP = ";";
1198
1198
  return [
1199
1199
  OSC,
1200
1200
  "8",
1201
- SEP$1,
1202
- SEP$1,
1201
+ SEP,
1202
+ SEP,
1203
1203
  url || text,
1204
1204
  BEL,
1205
1205
  text,
1206
1206
  OSC,
1207
1207
  "8",
1208
- SEP$1,
1209
- SEP$1,
1208
+ SEP,
1209
+ SEP,
1210
1210
  BEL
1211
1211
  ].join("");
1212
1212
  }
@@ -2466,7 +2466,7 @@ var require_picocolors = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2466
2466
  module.exports = createColors();
2467
2467
  module.exports.createColors = createColors;
2468
2468
  }));
2469
- var import_picocolors$5 = /* @__PURE__ */ __toESM(require_picocolors(), 1);
2469
+ var import_picocolors$3 = /* @__PURE__ */ __toESM(require_picocolors(), 1);
2470
2470
  var import_cli_table3 = /* @__PURE__ */ __toESM(require_cli_table3(), 1);
2471
2471
  function ansiRegex({ onlyFirst = false } = {}) {
2472
2472
  return new RegExp(`(?:\\u001B\\][\\s\\S]*?(?:\\u0007|\\u001B\\u005C|\\u009C))|[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]`, onlyFirst ? void 0 : "g");
@@ -2660,13 +2660,16 @@ function formatCurrency(amount) {
2660
2660
  return `$${amount.toFixed(2)}`;
2661
2661
  }
2662
2662
  function formatModelName(modelName) {
2663
- const match = modelName.match(/claude-(\w+)-([\d-]+)-(\d{8})/);
2663
+ const piMatch = modelName.match(/^\[pi\] (.+)$/);
2664
+ if (piMatch?.[1] != null) return `[pi] ${formatModelName(piMatch[1])}`;
2665
+ const anthropicMatch = modelName.match(/^anthropic\/claude-(\w+)-([\d.]+)$/);
2666
+ if (anthropicMatch != null) return `${anthropicMatch[1]}-${anthropicMatch[2]}`;
2667
+ const match = modelName.match(/^claude-(\w+)-([\d-]+)-(\d{8})$/);
2664
2668
  if (match != null) return `${match[1]}-${match[2]}`;
2669
+ const noDateMatch = modelName.match(/^claude-(\w+)-([\d-]+)$/);
2670
+ if (noDateMatch != null) return `${noDateMatch[1]}-${noDateMatch[2]}`;
2665
2671
  return modelName;
2666
2672
  }
2667
- function formatModelsDisplay(models) {
2668
- return uniq(models.map(formatModelName)).sort().join(", ");
2669
- }
2670
2673
  function formatModelsDisplayMultiline(models) {
2671
2674
  return uniq(models.map(formatModelName)).sort().map((model) => `- ${model}`).join("\n");
2672
2675
  }
@@ -2675,7 +2678,7 @@ function pushBreakdownRows(table, breakdowns, extraColumns = 1, trailingColumns
2675
2678
  const row = [` └─ ${formatModelName(breakdown.modelName)}`];
2676
2679
  for (let i = 0; i < extraColumns; i++) row.push("");
2677
2680
  const totalTokens = breakdown.inputTokens + breakdown.outputTokens + breakdown.cacheCreationTokens + breakdown.cacheReadTokens;
2678
- row.push(import_picocolors$5.default.gray(formatNumber(breakdown.inputTokens)), import_picocolors$5.default.gray(formatNumber(breakdown.outputTokens)), import_picocolors$5.default.gray(formatNumber(breakdown.cacheCreationTokens)), import_picocolors$5.default.gray(formatNumber(breakdown.cacheReadTokens)), import_picocolors$5.default.gray(formatNumber(totalTokens)), import_picocolors$5.default.gray(formatCurrency(breakdown.cost)));
2681
+ row.push(import_picocolors$3.default.gray(formatNumber(breakdown.inputTokens)), import_picocolors$3.default.gray(formatNumber(breakdown.outputTokens)), import_picocolors$3.default.gray(formatNumber(breakdown.cacheCreationTokens)), import_picocolors$3.default.gray(formatNumber(breakdown.cacheReadTokens)), import_picocolors$3.default.gray(formatNumber(totalTokens)), import_picocolors$3.default.gray(formatCurrency(breakdown.cost)));
2679
2682
  for (let i = 0; i < trailingColumns; i++) row.push("");
2680
2683
  table.push(row);
2681
2684
  }
@@ -2750,14 +2753,14 @@ function formatUsageDataRow(firstColumnValue, data, lastActivity) {
2750
2753
  function formatTotalsRow(totals, includeLastActivity = false) {
2751
2754
  const totalTokens = totals.inputTokens + totals.outputTokens + totals.cacheCreationTokens + totals.cacheReadTokens;
2752
2755
  const row = [
2753
- import_picocolors$5.default.yellow("Total"),
2756
+ import_picocolors$3.default.yellow("Total"),
2754
2757
  "",
2755
- import_picocolors$5.default.yellow(formatNumber(totals.inputTokens)),
2756
- import_picocolors$5.default.yellow(formatNumber(totals.outputTokens)),
2757
- import_picocolors$5.default.yellow(formatNumber(totals.cacheCreationTokens)),
2758
- import_picocolors$5.default.yellow(formatNumber(totals.cacheReadTokens)),
2759
- import_picocolors$5.default.yellow(formatNumber(totalTokens)),
2760
- import_picocolors$5.default.yellow(formatCurrency(totals.totalCost))
2758
+ import_picocolors$3.default.yellow(formatNumber(totals.inputTokens)),
2759
+ import_picocolors$3.default.yellow(formatNumber(totals.outputTokens)),
2760
+ import_picocolors$3.default.yellow(formatNumber(totals.cacheCreationTokens)),
2761
+ import_picocolors$3.default.yellow(formatNumber(totals.cacheReadTokens)),
2762
+ import_picocolors$3.default.yellow(formatNumber(totalTokens)),
2763
+ import_picocolors$3.default.yellow(formatCurrency(totals.totalCost))
2761
2764
  ];
2762
2765
  if (includeLastActivity) row.push("");
2763
2766
  return row;
@@ -2875,9 +2878,9 @@ function mergeConfigWithArgs(ctx, config, debug$4 = false) {
2875
2878
  logger.info("");
2876
2879
  logger.info(`Merging options for '${commandName ?? "unknown"}' command:`);
2877
2880
  const bySource = {
2878
- "defaults": [],
2881
+ defaults: [],
2879
2882
  "command config": [],
2880
- "CLI": []
2883
+ CLI: []
2881
2884
  };
2882
2885
  for (const [key, source] of Object.entries(sources)) if (bySource[source] != null) bySource[source].push(`${key}=${JSON.stringify(merged[key])}`);
2883
2886
  if (bySource.defaults.length > 0) logger.info(` • From defaults: ${bySource.defaults.join(", ")}`);
@@ -3235,828 +3238,6 @@ const sharedCommandConfig = {
3235
3238
  args: sharedArgs,
3236
3239
  toKebab: true
3237
3240
  };
3238
- const isBrowser = globalThis.window?.document !== void 0;
3239
- globalThis.process?.versions?.node;
3240
- globalThis.process?.versions?.bun;
3241
- globalThis.Deno?.version?.deno;
3242
- globalThis.process?.versions?.electron;
3243
- globalThis.navigator?.userAgent?.includes("jsdom");
3244
- typeof WorkerGlobalScope !== "undefined" && globalThis instanceof WorkerGlobalScope;
3245
- typeof DedicatedWorkerGlobalScope !== "undefined" && globalThis instanceof DedicatedWorkerGlobalScope;
3246
- typeof SharedWorkerGlobalScope !== "undefined" && globalThis instanceof SharedWorkerGlobalScope;
3247
- typeof ServiceWorkerGlobalScope !== "undefined" && globalThis instanceof ServiceWorkerGlobalScope;
3248
- const platform = globalThis.navigator?.userAgentData?.platform;
3249
- platform === "macOS" || globalThis.navigator?.platform === "MacIntel" || globalThis.navigator?.userAgent?.includes(" Mac ") === true || globalThis.process?.platform;
3250
- platform === "Windows" || globalThis.navigator?.platform === "Win32" || globalThis.process?.platform;
3251
- platform === "Linux" || globalThis.navigator?.platform?.startsWith("Linux") === true || globalThis.navigator?.userAgent?.includes(" Linux ") === true || globalThis.process?.platform;
3252
- platform === "iOS" || globalThis.navigator?.platform === "MacIntel" && globalThis.navigator?.maxTouchPoints > 1 || /iPad|iPhone|iPod/.test(globalThis.navigator?.platform);
3253
- platform === "Android" || globalThis.navigator?.platform === "Android" || globalThis.navigator?.userAgent?.includes(" Android ") === true || globalThis.process?.platform;
3254
- const ESC = "\x1B[";
3255
- const SEP = ";";
3256
- const isTerminalApp = !isBrowser && process$1.env.TERM_PROGRAM === "Apple_Terminal";
3257
- const isWindows = !isBrowser && process$1.platform === "win32";
3258
- isBrowser || process$1.cwd;
3259
- const cursorTo = (x, y) => {
3260
- if (typeof x !== "number") throw new TypeError("The `x` argument is required");
3261
- if (typeof y !== "number") return ESC + (x + 1) + "G";
3262
- return ESC + (y + 1) + SEP + (x + 1) + "H";
3263
- };
3264
- const cursorForward = (count = 1) => ESC + count + "C";
3265
- ESC + "";
3266
- isTerminalApp || ESC + "";
3267
- isTerminalApp || ESC + "";
3268
- ESC + "";
3269
- ESC + "";
3270
- ESC + "";
3271
- const cursorHide = ESC + "?25l";
3272
- const cursorShow = ESC + "?25h";
3273
- ESC + "";
3274
- ESC + "";
3275
- ESC + "";
3276
- const eraseDown = ESC + "J";
3277
- ESC + "";
3278
- const eraseScreen = ESC + "2J";
3279
- ESC + "";
3280
- ESC + "";
3281
- const clearScreen = "\x1Bc";
3282
- `${eraseScreen}${ESC}`;
3283
- isWindows ? `${eraseScreen}${ESC}` : `${eraseScreen}${ESC}${ESC}`;
3284
- const enterAlternativeScreen = ESC + "?1049h";
3285
- const exitAlternativeScreen = ESC + "?1049l";
3286
- const SYNC_START = "\x1B[?2026h";
3287
- const SYNC_END = "\x1B[?2026l";
3288
- const DISABLE_LINE_WRAP = "\x1B[?7l";
3289
- const ENABLE_LINE_WRAP = "\x1B[?7h";
3290
- const ANSI_RESET = "\x1B[0m";
3291
- var TerminalManager = class {
3292
- stream;
3293
- cursorHidden = false;
3294
- buffer = [];
3295
- useBuffering = false;
3296
- alternateScreenActive = false;
3297
- syncMode = false;
3298
- constructor(stream = process$1.stdout) {
3299
- this.stream = stream;
3300
- }
3301
- hideCursor() {
3302
- if (!this.cursorHidden && this.stream.isTTY) {
3303
- this.stream.write(cursorHide);
3304
- this.cursorHidden = true;
3305
- }
3306
- }
3307
- showCursor() {
3308
- if (this.cursorHidden && this.stream.isTTY) {
3309
- this.stream.write(cursorShow);
3310
- this.cursorHidden = false;
3311
- }
3312
- }
3313
- clearScreen() {
3314
- if (this.stream.isTTY) {
3315
- this.stream.write(clearScreen);
3316
- this.stream.write(cursorTo(0, 0));
3317
- }
3318
- }
3319
- write(text) {
3320
- if (this.useBuffering) this.buffer.push(text);
3321
- else this.stream.write(text);
3322
- }
3323
- startBuffering() {
3324
- this.useBuffering = true;
3325
- this.buffer = [];
3326
- }
3327
- flush() {
3328
- if (this.useBuffering && this.buffer.length > 0) {
3329
- if (this.syncMode && this.stream.isTTY) this.stream.write(SYNC_START + this.buffer.join("") + SYNC_END);
3330
- else this.stream.write(this.buffer.join(""));
3331
- this.buffer = [];
3332
- }
3333
- this.useBuffering = false;
3334
- }
3335
- enterAlternateScreen() {
3336
- if (!this.alternateScreenActive && this.stream.isTTY) {
3337
- this.stream.write(enterAlternativeScreen);
3338
- this.stream.write(DISABLE_LINE_WRAP);
3339
- this.alternateScreenActive = true;
3340
- }
3341
- }
3342
- exitAlternateScreen() {
3343
- if (this.alternateScreenActive && this.stream.isTTY) {
3344
- this.stream.write(ENABLE_LINE_WRAP);
3345
- this.stream.write(exitAlternativeScreen);
3346
- this.alternateScreenActive = false;
3347
- }
3348
- }
3349
- enableSyncMode() {
3350
- this.syncMode = true;
3351
- }
3352
- disableSyncMode() {
3353
- this.syncMode = false;
3354
- }
3355
- get width() {
3356
- return this.stream.columns || 80;
3357
- }
3358
- get height() {
3359
- return this.stream.rows || 24;
3360
- }
3361
- get isTTY() {
3362
- return this.stream.isTTY ?? false;
3363
- }
3364
- cleanup() {
3365
- this.showCursor();
3366
- this.exitAlternateScreen();
3367
- this.disableSyncMode();
3368
- }
3369
- };
3370
- function createProgressBar(value$1, max, width, options = {}) {
3371
- const { showPercentage = true, showValues = false, fillChar = "█", emptyChar = "░", leftBracket = "[", rightBracket = "]", colors: colors$2 = {} } = options;
3372
- const percentage = max > 0 ? Math.min(100, value$1 / max * 100) : 0;
3373
- const fillWidth = Math.round(percentage / 100 * width);
3374
- const emptyWidth = width - fillWidth;
3375
- let color = "";
3376
- if (colors$2.critical != null && percentage >= 90) color = colors$2.critical;
3377
- else if (colors$2.high != null && percentage >= 80) color = colors$2.high;
3378
- else if (colors$2.medium != null && percentage >= 50) color = colors$2.medium;
3379
- else if (colors$2.low != null) color = colors$2.low;
3380
- let bar = leftBracket;
3381
- if (color !== "") bar += color;
3382
- bar += fillChar.repeat(fillWidth);
3383
- bar += emptyChar.repeat(emptyWidth);
3384
- if (color !== "") bar += ANSI_RESET;
3385
- bar += rightBracket;
3386
- if (showPercentage) bar += ` ${percentage.toFixed(1)}%`;
3387
- if (showValues) bar += ` (${value$1}/${max})`;
3388
- return bar;
3389
- }
3390
- function centerText(text, width) {
3391
- const textLength = stringWidth(text);
3392
- if (textLength >= width) return text;
3393
- const leftPadding = Math.floor((width - textLength) / 2);
3394
- const rightPadding = width - textLength - leftPadding;
3395
- return " ".repeat(leftPadding) + text + " ".repeat(rightPadding);
3396
- }
3397
- const SAVE_CURSOR = "\x1B7";
3398
- const RESTORE_CURSOR = "\x1B8";
3399
- function drawEmoji(emoji) {
3400
- return `${SAVE_CURSOR}${emoji}${RESTORE_CURSOR}${cursorForward(stringWidth(emoji))}`;
3401
- }
3402
- var Node = class {
3403
- value;
3404
- next;
3405
- constructor(value$1) {
3406
- this.value = value$1;
3407
- }
3408
- };
3409
- var Queue = class {
3410
- #head;
3411
- #tail;
3412
- #size;
3413
- constructor() {
3414
- this.clear();
3415
- }
3416
- enqueue(value$1) {
3417
- const node = new Node(value$1);
3418
- if (this.#head) {
3419
- this.#tail.next = node;
3420
- this.#tail = node;
3421
- } else {
3422
- this.#head = node;
3423
- this.#tail = node;
3424
- }
3425
- this.#size++;
3426
- }
3427
- dequeue() {
3428
- const current = this.#head;
3429
- if (!current) return;
3430
- this.#head = this.#head.next;
3431
- this.#size--;
3432
- return current.value;
3433
- }
3434
- peek() {
3435
- if (!this.#head) return;
3436
- return this.#head.value;
3437
- }
3438
- clear() {
3439
- this.#head = void 0;
3440
- this.#tail = void 0;
3441
- this.#size = 0;
3442
- }
3443
- get size() {
3444
- return this.#size;
3445
- }
3446
- *[Symbol.iterator]() {
3447
- let current = this.#head;
3448
- while (current) {
3449
- yield current.value;
3450
- current = current.next;
3451
- }
3452
- }
3453
- *drain() {
3454
- while (this.#head) yield this.dequeue();
3455
- }
3456
- };
3457
- function pLimit(concurrency) {
3458
- validateConcurrency(concurrency);
3459
- const queue = new Queue();
3460
- let activeCount = 0;
3461
- const resumeNext = () => {
3462
- if (activeCount < concurrency && queue.size > 0) {
3463
- activeCount++;
3464
- queue.dequeue()();
3465
- }
3466
- };
3467
- const next = () => {
3468
- activeCount--;
3469
- resumeNext();
3470
- };
3471
- const run$1 = async (function_, resolve$1, arguments_) => {
3472
- const result = (async () => function_(...arguments_))();
3473
- resolve$1(result);
3474
- try {
3475
- await result;
3476
- } catch {}
3477
- next();
3478
- };
3479
- const enqueue = (function_, resolve$1, arguments_) => {
3480
- new Promise((internalResolve) => {
3481
- queue.enqueue(internalResolve);
3482
- }).then(run$1.bind(void 0, function_, resolve$1, arguments_));
3483
- if (activeCount < concurrency) resumeNext();
3484
- };
3485
- const generator = (function_, ...arguments_) => new Promise((resolve$1) => {
3486
- enqueue(function_, resolve$1, arguments_);
3487
- });
3488
- Object.defineProperties(generator, {
3489
- activeCount: { get: () => activeCount },
3490
- pendingCount: { get: () => queue.size },
3491
- clearQueue: { value() {
3492
- queue.clear();
3493
- } },
3494
- concurrency: {
3495
- get: () => concurrency,
3496
- set(newConcurrency) {
3497
- validateConcurrency(newConcurrency);
3498
- concurrency = newConcurrency;
3499
- queueMicrotask(() => {
3500
- while (activeCount < concurrency && queue.size > 0) resumeNext();
3501
- });
3502
- }
3503
- },
3504
- map: { async value(array, function_) {
3505
- const promises = array.map((value$1, index) => this(function_, value$1, index));
3506
- return Promise.all(promises);
3507
- } }
3508
- });
3509
- return generator;
3510
- }
3511
- function validateConcurrency(concurrency) {
3512
- if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) throw new TypeError("Expected `concurrency` to be a number from 1 and up");
3513
- }
3514
- const RETENTION_HOURS = 24;
3515
- const FILE_CONCURRENCY = 5;
3516
- async function isRecentFile(filePath, cutoffTime) {
3517
- return pipe(try_({
3518
- try: stat(filePath),
3519
- catch: (error) => error
3520
- }), map$2((fileStats) => fileStats.mtime >= cutoffTime), unwrap(false));
3521
- }
3522
- function createLiveMonitorState(config) {
3523
- const fetcher = config.mode !== "display" ? new PricingFetcher() : null;
3524
- return {
3525
- fetcher,
3526
- lastFileTimestamps: /* @__PURE__ */ new Map(),
3527
- processedHashes: /* @__PURE__ */ new Set(),
3528
- allEntries: [],
3529
- [Symbol.dispose]() {
3530
- fetcher?.[Symbol.dispose]();
3531
- }
3532
- };
3533
- }
3534
- function clearLiveMonitorCache(state) {
3535
- state.lastFileTimestamps.clear();
3536
- state.processedHashes.clear();
3537
- state.allEntries = [];
3538
- }
3539
- function cleanupOldEntries(state, cutoffTime) {
3540
- const initialCount = state.allEntries.length;
3541
- state.allEntries = state.allEntries.filter((entry) => entry.timestamp >= cutoffTime);
3542
- if (initialCount - state.allEntries.length > 100) state.processedHashes.clear();
3543
- }
3544
- async function processFileContent(state, config, content, cutoffTime) {
3545
- const lines = content.trim().split("\n").filter((line) => line.length > 0);
3546
- for (const line of lines) {
3547
- const dataResult = pipe(try_({
3548
- try: () => JSON.parse(line),
3549
- catch: (error) => error
3550
- })(), andThen((data$1) => {
3551
- const parseResult = safeParse(usageDataSchema, data$1);
3552
- return parseResult.success ? succeed(parseResult.output) : fail(parseResult.issues);
3553
- }));
3554
- if (isFailure(dataResult)) continue;
3555
- const data = unwrap(dataResult);
3556
- const entryTime = new Date(data.timestamp);
3557
- if (entryTime < cutoffTime) continue;
3558
- const uniqueHash = createUniqueHash(data);
3559
- if (uniqueHash != null && state.processedHashes.has(uniqueHash)) continue;
3560
- if (uniqueHash != null) state.processedHashes.add(uniqueHash);
3561
- const costUSD = await (config.mode === "display" ? Promise.resolve(data.costUSD ?? 0) : calculateCostForEntry(data, config.mode, state.fetcher));
3562
- const usageLimitResetTime = getUsageLimitResetTime(data);
3563
- state.allEntries.push({
3564
- timestamp: entryTime,
3565
- usage: {
3566
- inputTokens: data.message.usage.input_tokens ?? 0,
3567
- outputTokens: data.message.usage.output_tokens ?? 0,
3568
- cacheCreationInputTokens: data.message.usage.cache_creation_input_tokens ?? 0,
3569
- cacheReadInputTokens: data.message.usage.cache_read_input_tokens ?? 0
3570
- },
3571
- costUSD,
3572
- model: data.message.model ?? "<synthetic>",
3573
- version: data.version,
3574
- usageLimitResetTime: usageLimitResetTime ?? void 0
3575
- });
3576
- }
3577
- }
3578
- async function getActiveBlock(state, config) {
3579
- const cutoffTime = /* @__PURE__ */ new Date(Date.now() - RETENTION_HOURS * 60 * 60 * 1e3);
3580
- const allFiles = (await globUsageFiles(config.claudePaths)).map((r) => r.file);
3581
- if (allFiles.length === 0) return null;
3582
- const candidateFiles = [];
3583
- for (const file of allFiles) if (await isRecentFile(file, cutoffTime)) candidateFiles.push(file);
3584
- const filesToRead = [];
3585
- for (const file of candidateFiles) {
3586
- const timestamp$1 = await getEarliestTimestamp(file);
3587
- const lastTimestamp = state.lastFileTimestamps.get(file);
3588
- if (timestamp$1 != null && (lastTimestamp == null || timestamp$1.getTime() > lastTimestamp)) {
3589
- filesToRead.push(file);
3590
- state.lastFileTimestamps.set(file, timestamp$1.getTime());
3591
- }
3592
- }
3593
- cleanupOldEntries(state, cutoffTime);
3594
- if (filesToRead.length > 0) {
3595
- const sortedFiles = await sortFilesByTimestamp(filesToRead);
3596
- const fileLimit = pLimit(FILE_CONCURRENCY);
3597
- const fileResults = await Promise.allSettled(sortedFiles.map(async (file) => fileLimit(async () => ({
3598
- file,
3599
- content: await readFile(file, "utf-8")
3600
- }))));
3601
- for (const result of fileResults) if (result.status === "fulfilled") {
3602
- const { content } = result.value;
3603
- await processFileContent(state, config, content, cutoffTime);
3604
- }
3605
- }
3606
- const blocks = identifySessionBlocks(state.allEntries, config.sessionDurationHours);
3607
- return (config.order === "asc" ? blocks : blocks.reverse()).find((block) => block.isActive) ?? null;
3608
- }
3609
- function delay(ms, options = {}) {
3610
- const { signal, persistent = true } = options;
3611
- if (signal?.aborted) return Promise.reject(signal.reason);
3612
- return new Promise((resolve$1, reject) => {
3613
- const abort = () => {
3614
- clearTimeout(+i);
3615
- reject(signal?.reason);
3616
- };
3617
- const done = () => {
3618
- signal?.removeEventListener("abort", abort);
3619
- resolve$1();
3620
- };
3621
- const i = setArbitraryLengthTimeout(done, ms);
3622
- signal?.addEventListener("abort", abort, { once: true });
3623
- if (persistent === false) try {
3624
- Deno.unrefTimer(i);
3625
- } catch (error) {
3626
- if (!(error instanceof ReferenceError)) throw error;
3627
- console.error("`persistent` option is only available in Deno");
3628
- }
3629
- });
3630
- }
3631
- const I32_MAX = 2 ** 31 - 1;
3632
- function setArbitraryLengthTimeout(callback, delay$1) {
3633
- let currentDelay = delay$1 = Math.trunc(Math.max(delay$1, 0) || 0);
3634
- const start = Date.now();
3635
- let timeoutId;
3636
- const queueTimeout = () => {
3637
- currentDelay = delay$1 - (Date.now() - start);
3638
- timeoutId = currentDelay > I32_MAX ? setTimeout(queueTimeout, I32_MAX) : setTimeout(callback, currentDelay);
3639
- };
3640
- queueTimeout();
3641
- return { valueOf: () => timeoutId };
3642
- }
3643
- Symbol.asyncIterator;
3644
- const toZeroIfInfinity = (value$1) => Number.isFinite(value$1) ? value$1 : 0;
3645
- function parseNumber(milliseconds) {
3646
- return {
3647
- days: Math.trunc(milliseconds / 864e5),
3648
- hours: Math.trunc(milliseconds / 36e5 % 24),
3649
- minutes: Math.trunc(milliseconds / 6e4 % 60),
3650
- seconds: Math.trunc(milliseconds / 1e3 % 60),
3651
- milliseconds: Math.trunc(milliseconds % 1e3),
3652
- microseconds: Math.trunc(toZeroIfInfinity(milliseconds * 1e3) % 1e3),
3653
- nanoseconds: Math.trunc(toZeroIfInfinity(milliseconds * 1e6) % 1e3)
3654
- };
3655
- }
3656
- function parseBigint(milliseconds) {
3657
- return {
3658
- days: milliseconds / 86400000n,
3659
- hours: milliseconds / 3600000n % 24n,
3660
- minutes: milliseconds / 60000n % 60n,
3661
- seconds: milliseconds / 1000n % 60n,
3662
- milliseconds: milliseconds % 1000n,
3663
- microseconds: 0n,
3664
- nanoseconds: 0n
3665
- };
3666
- }
3667
- function parseMilliseconds(milliseconds) {
3668
- switch (typeof milliseconds) {
3669
- case "number":
3670
- if (Number.isFinite(milliseconds)) return parseNumber(milliseconds);
3671
- break;
3672
- case "bigint": return parseBigint(milliseconds);
3673
- }
3674
- throw new TypeError("Expected a finite number or bigint");
3675
- }
3676
- const isZero = (value$1) => value$1 === 0 || value$1 === 0n;
3677
- const pluralize = (word, count) => count === 1 || count === 1n ? word : `${word}s`;
3678
- const SECOND_ROUNDING_EPSILON = 1e-7;
3679
- const ONE_DAY_IN_MILLISECONDS = 24n * 60n * 60n * 1000n;
3680
- function prettyMilliseconds(milliseconds, options) {
3681
- const isBigInt = typeof milliseconds === "bigint";
3682
- if (!isBigInt && !Number.isFinite(milliseconds)) throw new TypeError("Expected a finite number or bigint");
3683
- options = { ...options };
3684
- const sign = milliseconds < 0 ? "-" : "";
3685
- milliseconds = milliseconds < 0 ? -milliseconds : milliseconds;
3686
- if (options.colonNotation) {
3687
- options.compact = false;
3688
- options.formatSubMilliseconds = false;
3689
- options.separateMilliseconds = false;
3690
- options.verbose = false;
3691
- }
3692
- if (options.compact) {
3693
- options.unitCount = 1;
3694
- options.secondsDecimalDigits = 0;
3695
- options.millisecondsDecimalDigits = 0;
3696
- }
3697
- let result = [];
3698
- const floorDecimals = (value$1, decimalDigits) => {
3699
- const flooredInterimValue = Math.floor(value$1 * 10 ** decimalDigits + SECOND_ROUNDING_EPSILON);
3700
- return (Math.round(flooredInterimValue) / 10 ** decimalDigits).toFixed(decimalDigits);
3701
- };
3702
- const add = (value$1, long, short, valueString) => {
3703
- if ((result.length === 0 || !options.colonNotation) && isZero(value$1) && !(options.colonNotation && short === "m")) return;
3704
- valueString ??= String(value$1);
3705
- if (options.colonNotation) {
3706
- const wholeDigits = valueString.includes(".") ? valueString.split(".")[0].length : valueString.length;
3707
- const minLength = result.length > 0 ? 2 : 1;
3708
- valueString = "0".repeat(Math.max(0, minLength - wholeDigits)) + valueString;
3709
- } else valueString += options.verbose ? " " + pluralize(long, value$1) : short;
3710
- result.push(valueString);
3711
- };
3712
- const parsed = parseMilliseconds(milliseconds);
3713
- const days = BigInt(parsed.days);
3714
- if (options.hideYearAndDays) add(BigInt(days) * 24n + BigInt(parsed.hours), "hour", "h");
3715
- else {
3716
- if (options.hideYear) add(days, "day", "d");
3717
- else {
3718
- add(days / 365n, "year", "y");
3719
- add(days % 365n, "day", "d");
3720
- }
3721
- add(Number(parsed.hours), "hour", "h");
3722
- }
3723
- add(Number(parsed.minutes), "minute", "m");
3724
- if (!options.hideSeconds) if (options.separateMilliseconds || options.formatSubMilliseconds || !options.colonNotation && milliseconds < 1e3 && !options.subSecondsAsDecimals) {
3725
- const seconds = Number(parsed.seconds);
3726
- const milliseconds$1 = Number(parsed.milliseconds);
3727
- const microseconds = Number(parsed.microseconds);
3728
- const nanoseconds = Number(parsed.nanoseconds);
3729
- add(seconds, "second", "s");
3730
- if (options.formatSubMilliseconds) {
3731
- add(milliseconds$1, "millisecond", "ms");
3732
- add(microseconds, "microsecond", "µs");
3733
- add(nanoseconds, "nanosecond", "ns");
3734
- } else {
3735
- const millisecondsAndBelow = milliseconds$1 + microseconds / 1e3 + nanoseconds / 1e6;
3736
- const millisecondsDecimalDigits = typeof options.millisecondsDecimalDigits === "number" ? options.millisecondsDecimalDigits : 0;
3737
- const millisecondsString = millisecondsDecimalDigits ? millisecondsAndBelow.toFixed(millisecondsDecimalDigits) : millisecondsAndBelow >= 1 ? Math.round(millisecondsAndBelow) : Math.ceil(millisecondsAndBelow);
3738
- add(Number.parseFloat(millisecondsString), "millisecond", "ms", millisecondsString);
3739
- }
3740
- } else {
3741
- const secondsFixed = floorDecimals((isBigInt ? Number(milliseconds % ONE_DAY_IN_MILLISECONDS) : milliseconds) / 1e3 % 60, typeof options.secondsDecimalDigits === "number" ? options.secondsDecimalDigits : 1);
3742
- const secondsString = options.keepDecimalsOnWholeSeconds ? secondsFixed : secondsFixed.replace(/\.0+$/, "");
3743
- add(Number.parseFloat(secondsString), "second", "s", secondsString);
3744
- }
3745
- if (result.length === 0) return sign + "0" + (options.verbose ? " milliseconds" : "ms");
3746
- const separator = options.colonNotation ? ":" : " ";
3747
- if (typeof options.unitCount === "number") result = result.slice(0, Math.max(options.unitCount, 1));
3748
- return sign + result.join(separator);
3749
- }
3750
- var import_picocolors$4 = /* @__PURE__ */ __toESM(require_picocolors(), 1);
3751
- function getRateIndicator(burnRate) {
3752
- if (burnRate == null) return "";
3753
- switch (true) {
3754
- case burnRate.tokensPerMinuteForIndicator > BURN_RATE_THRESHOLDS.HIGH: return import_picocolors$4.default.red(`${drawEmoji("⚡")} HIGH`);
3755
- case burnRate.tokensPerMinuteForIndicator > BURN_RATE_THRESHOLDS.MODERATE: return import_picocolors$4.default.yellow(`${drawEmoji("⚡")} MODERATE`);
3756
- default: return import_picocolors$4.default.green(`${drawEmoji("✓")} NORMAL`);
3757
- }
3758
- }
3759
- async function delayWithAbort(ms, signal) {
3760
- await delay(ms, { signal });
3761
- }
3762
- async function renderWaitingState(terminal, config, signal) {
3763
- terminal.startBuffering();
3764
- terminal.write(cursorTo(0, 0));
3765
- terminal.write(eraseDown);
3766
- terminal.write(import_picocolors$4.default.yellow("No active session block found. Waiting...\n"));
3767
- terminal.write(cursorHide);
3768
- terminal.flush();
3769
- await delayWithAbort(config.refreshInterval, signal);
3770
- }
3771
- function renderActiveBlock(terminal, activeBlock, config) {
3772
- terminal.startBuffering();
3773
- terminal.write(cursorTo(0, 0));
3774
- terminal.write(eraseDown);
3775
- renderLiveDisplay(terminal, activeBlock, config);
3776
- terminal.write(cursorHide);
3777
- terminal.flush();
3778
- }
3779
- function formatTokensShort(num) {
3780
- if (num >= 1e3) return `${(num / 1e3).toFixed(1)}k`;
3781
- return num.toString();
3782
- }
3783
- function renderLiveDisplay(terminal, block, config) {
3784
- const width = terminal.width;
3785
- const now = /* @__PURE__ */ new Date();
3786
- const totalTokens = getTotalTokens(block.tokenCounts);
3787
- const elapsed = (now.getTime() - block.startTime.getTime()) / (1e3 * 60);
3788
- const remaining = (block.endTime.getTime() - now.getTime()) / (1e3 * 60);
3789
- const formatTokenDisplay = (tokens, useShort) => {
3790
- return useShort ? formatTokensShort(tokens) : formatNumber(tokens);
3791
- };
3792
- if (width < 60) {
3793
- renderCompactLiveDisplay(terminal, block, config, totalTokens, elapsed, remaining);
3794
- return;
3795
- }
3796
- const boxWidth = Math.min(120, width - 2);
3797
- const boxMargin = Math.floor((width - boxWidth) / 2);
3798
- const marginStr = " ".repeat(boxMargin);
3799
- const sessionDuration = elapsed + remaining;
3800
- const sessionRightText = `${(elapsed / sessionDuration * 100).toFixed(1).padStart(6)}%`;
3801
- const tokenPercent = config.tokenLimit != null && config.tokenLimit > 0 ? totalTokens / config.tokenLimit * 100 : 0;
3802
- const usageRightText = config.tokenLimit != null && config.tokenLimit > 0 ? `${tokenPercent.toFixed(1).padStart(6)}% (${formatTokensShort(totalTokens)}/${formatTokensShort(config.tokenLimit)})` : `(${formatTokensShort(totalTokens)} tokens)`;
3803
- const projection = projectBlockUsage(block);
3804
- const projectedPercent = projection != null && config.tokenLimit != null && config.tokenLimit > 0 ? projection.totalTokens / config.tokenLimit * 100 : 0;
3805
- const projectionRightText = projection != null ? config.tokenLimit != null && config.tokenLimit > 0 ? `${projectedPercent.toFixed(1).padStart(6)}% (${formatTokensShort(projection.totalTokens)}/${formatTokensShort(config.tokenLimit)})` : `(${formatTokensShort(projection.totalTokens)} tokens)` : "";
3806
- const maxRightTextWidth = Math.max(stringWidth(sessionRightText), stringWidth(usageRightText), projection != null ? stringWidth(projectionRightText) : 0);
3807
- const labelWidth = 14;
3808
- const barWidth = boxWidth - labelWidth - maxRightTextWidth - 4 - 4;
3809
- const sessionProgressBar = createProgressBar(elapsed, sessionDuration, barWidth, {
3810
- showPercentage: false,
3811
- fillChar: import_picocolors$4.default.cyan("█"),
3812
- emptyChar: import_picocolors$4.default.gray("░"),
3813
- leftBracket: "[",
3814
- rightBracket: "]"
3815
- });
3816
- const startTime = block.startTime.toLocaleTimeString(void 0, {
3817
- hour: "2-digit",
3818
- minute: "2-digit",
3819
- second: "2-digit",
3820
- hour12: true
3821
- });
3822
- const endTime = block.endTime.toLocaleTimeString(void 0, {
3823
- hour: "2-digit",
3824
- minute: "2-digit",
3825
- second: "2-digit",
3826
- hour12: true
3827
- });
3828
- const detailsIndent = 3;
3829
- const detailsSpacing = 2;
3830
- const detailsAvailableWidth = boxWidth - 3 - detailsIndent;
3831
- terminal.write(`${marginStr}┌${"─".repeat(boxWidth - 2)}┐\n`);
3832
- terminal.write(`${marginStr}│${import_picocolors$4.default.bold(centerText("CLAUDE CODE - LIVE TOKEN USAGE MONITOR", boxWidth - 2))}│\n`);
3833
- terminal.write(`${marginStr}│${import_picocolors$4.default.yellow(centerText("⚠️ DEPRECATED - Will be removed in next major version", boxWidth - 2))}│\n`);
3834
- terminal.write(`${marginStr}│${import_picocolors$4.default.gray(centerText("Use https://claude.ai/settings/usage instead", boxWidth - 2))}│\n`);
3835
- terminal.write(`${marginStr}├${"─".repeat(boxWidth - 2)}┤\n`);
3836
- terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
3837
- const sessionLabel = `${drawEmoji("⏱️")}${import_picocolors$4.default.bold(" SESSION")}`;
3838
- const sessionLabelWidth = stringWidth(sessionLabel);
3839
- const sessionBarStr = `${sessionLabel}${"".padEnd(Math.max(0, labelWidth - sessionLabelWidth))} ${sessionProgressBar} ${sessionRightText}`;
3840
- const sessionBarPadded = sessionBarStr + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(sessionBarStr)));
3841
- terminal.write(`${marginStr}│ ${sessionBarPadded}│\n`);
3842
- const sessionCol1 = `${import_picocolors$4.default.gray("Started:")} ${startTime}`;
3843
- const sessionCol2 = `${import_picocolors$4.default.gray("Elapsed:")} ${prettyMilliseconds(elapsed * 60 * 1e3, { compact: true })}`;
3844
- const sessionCol3 = `${import_picocolors$4.default.gray("Remaining:")} ${prettyMilliseconds(remaining * 60 * 1e3, { compact: true })} (${endTime})`;
3845
- let sessionDetails = `${" ".repeat(detailsIndent)}${sessionCol1}${" ".repeat(detailsSpacing)}${sessionCol2}${" ".repeat(detailsSpacing)}${sessionCol3}`;
3846
- if (stringWidth(sessionCol1) + stringWidth(sessionCol2) + stringWidth(sessionCol3) + detailsSpacing * 2 > detailsAvailableWidth) sessionDetails = `${" ".repeat(detailsIndent)}${sessionCol1}${" ".repeat(detailsSpacing)}${sessionCol3}`;
3847
- const sessionDetailsPadded = sessionDetails + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(sessionDetails)));
3848
- let usageLimitResetTimePadded = null;
3849
- if (block.usageLimitResetTime !== void 0 && now < block.usageLimitResetTime) {
3850
- const resetTime = block.usageLimitResetTime?.toLocaleTimeString(void 0, {
3851
- hour: "2-digit",
3852
- minute: "2-digit",
3853
- hour12: true
3854
- }) ?? null;
3855
- const usageLimitResetTime = resetTime !== null ? import_picocolors$4.default.red(`${drawEmoji("❌")} USAGE LIMIT. RESET AT ${resetTime}`) : "";
3856
- usageLimitResetTimePadded = resetTime !== null ? usageLimitResetTime + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(usageLimitResetTime))) : null;
3857
- }
3858
- terminal.write(`${marginStr}│ ${sessionDetailsPadded}│\n`);
3859
- if (usageLimitResetTimePadded !== null) terminal.write(`${marginStr}│ ${usageLimitResetTimePadded}│\n`);
3860
- terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
3861
- terminal.write(`${marginStr}├${"─".repeat(boxWidth - 2)}┤\n`);
3862
- terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
3863
- let barColor = import_picocolors$4.default.green;
3864
- if (tokenPercent > 100) barColor = import_picocolors$4.default.red;
3865
- else if (tokenPercent > 80) barColor = import_picocolors$4.default.yellow;
3866
- const usageBar = config.tokenLimit != null && config.tokenLimit > 0 ? createProgressBar(totalTokens, config.tokenLimit, barWidth, {
3867
- showPercentage: false,
3868
- fillChar: barColor("█"),
3869
- emptyChar: import_picocolors$4.default.gray("░"),
3870
- leftBracket: "[",
3871
- rightBracket: "]"
3872
- }) : `[${import_picocolors$4.default.green("█".repeat(Math.floor(barWidth * .1)))}${import_picocolors$4.default.gray("░".repeat(barWidth - Math.floor(barWidth * .1)))}]`;
3873
- const burnRate = calculateBurnRate(block);
3874
- const rateIndicator = getRateIndicator(burnRate);
3875
- const buildRateDisplay = (useShort) => {
3876
- if (burnRate == null) return `${import_picocolors$4.default.bold("Burn Rate:")} N/A`;
3877
- const rateValue = Math.round(burnRate.tokensPerMinute);
3878
- const formattedRate = useShort ? formatTokensShort(rateValue) : formatNumber(rateValue);
3879
- return `${import_picocolors$4.default.bold("Burn Rate:")} ${formattedRate} token/min ${rateIndicator}`;
3880
- };
3881
- const usageLabel = `${drawEmoji("🔥")}${import_picocolors$4.default.bold(" USAGE")}`;
3882
- const usageLabelWidth = stringWidth(usageLabel);
3883
- const usageBarStr = `${usageLabel}${"".padEnd(Math.max(0, labelWidth - usageLabelWidth))} ${usageBar} ${usageRightText}`;
3884
- let rateDisplay = buildRateDisplay(false);
3885
- let usageCol1 = `${import_picocolors$4.default.gray("Tokens:")} ${formatTokenDisplay(totalTokens, false)} (${rateDisplay})`;
3886
- let usageCol2 = config.tokenLimit != null && config.tokenLimit > 0 ? `${import_picocolors$4.default.gray("Limit:")} ${formatTokenDisplay(config.tokenLimit, false)} tokens` : "";
3887
- const usageCol3 = `${import_picocolors$4.default.gray("Cost:")} ${formatCurrency(block.costUSD)}`;
3888
- let totalWidth = stringWidth(usageCol1);
3889
- if (usageCol2.length > 0) totalWidth += detailsSpacing + stringWidth(usageCol2);
3890
- totalWidth += detailsSpacing + stringWidth(usageCol3);
3891
- let useTwoLineLayout = false;
3892
- if (totalWidth > detailsAvailableWidth) {
3893
- useTwoLineLayout = true;
3894
- rateDisplay = buildRateDisplay(true);
3895
- usageCol1 = `${import_picocolors$4.default.gray("Tokens:")} ${formatTokenDisplay(totalTokens, true)} (${rateDisplay})`;
3896
- if (usageCol2.length > 0) usageCol2 = `${import_picocolors$4.default.gray("Limit:")} ${formatTokenDisplay(config.tokenLimit, true)} tokens`;
3897
- }
3898
- const usageBarPadded = usageBarStr + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(usageBarStr)));
3899
- terminal.write(`${marginStr}│ ${usageBarPadded}│\n`);
3900
- if (useTwoLineLayout) {
3901
- const usageDetailsLine1 = `${" ".repeat(detailsIndent)}${usageCol1}`;
3902
- const usageDetailsLine1Padded = usageDetailsLine1 + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(usageDetailsLine1)));
3903
- terminal.write(`${marginStr}│ ${usageDetailsLine1Padded}│\n`);
3904
- let usageDetailsLine2;
3905
- if (usageCol2.length > 0) usageDetailsLine2 = `${" ".repeat(detailsIndent)}${usageCol2}${" ".repeat(detailsSpacing)}${usageCol3}`;
3906
- else usageDetailsLine2 = `${" ".repeat(detailsIndent)}${usageCol3}`;
3907
- const usageDetailsLine2Padded = usageDetailsLine2 + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(usageDetailsLine2)));
3908
- terminal.write(`${marginStr}│ ${usageDetailsLine2Padded}│\n`);
3909
- } else {
3910
- let usageDetails;
3911
- if (usageCol2.length > 0) usageDetails = `${" ".repeat(detailsIndent)}${usageCol1}${" ".repeat(detailsSpacing)}${usageCol2}${" ".repeat(detailsSpacing)}${usageCol3}`;
3912
- else usageDetails = `${" ".repeat(detailsIndent)}${usageCol1}${" ".repeat(detailsSpacing)}${usageCol3}`;
3913
- const usageDetailsPadded = usageDetails + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(usageDetails)));
3914
- terminal.write(`${marginStr}│ ${usageDetailsPadded}│\n`);
3915
- }
3916
- terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
3917
- terminal.write(`${marginStr}├${"─".repeat(boxWidth - 2)}┤\n`);
3918
- terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
3919
- if (projection != null) {
3920
- let projBarColor = import_picocolors$4.default.green;
3921
- if (projectedPercent > 100) projBarColor = import_picocolors$4.default.red;
3922
- else if (projectedPercent > 80) projBarColor = import_picocolors$4.default.yellow;
3923
- const projectionBar = config.tokenLimit != null && config.tokenLimit > 0 ? createProgressBar(projection.totalTokens, config.tokenLimit, barWidth, {
3924
- showPercentage: false,
3925
- fillChar: projBarColor("█"),
3926
- emptyChar: import_picocolors$4.default.gray("░"),
3927
- leftBracket: "[",
3928
- rightBracket: "]"
3929
- }) : `[${import_picocolors$4.default.green("█".repeat(Math.floor(barWidth * .15)))}${import_picocolors$4.default.gray("░".repeat(barWidth - Math.floor(barWidth * .15)))}]`;
3930
- const limitStatus = config.tokenLimit != null && config.tokenLimit > 0 ? projectedPercent > 100 ? import_picocolors$4.default.red(`${drawEmoji("❌")} WILL EXCEED LIMIT`) : projectedPercent > 80 ? import_picocolors$4.default.yellow(`${drawEmoji("⚠️")} APPROACHING LIMIT`) : import_picocolors$4.default.green(`${drawEmoji("✓")} WITHIN LIMIT`) : import_picocolors$4.default.green(`${drawEmoji("✓")} ON TRACK`);
3931
- const projLabel = `${drawEmoji("📈")}${import_picocolors$4.default.bold(" PROJECTION")}`;
3932
- const projLabelWidth = stringWidth(projLabel);
3933
- const projBarStr = `${projLabel}${"".padEnd(Math.max(0, labelWidth - projLabelWidth))} ${projectionBar} ${projectionRightText}`;
3934
- const projBarPadded = projBarStr + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(projBarStr)));
3935
- terminal.write(`${marginStr}│ ${projBarPadded}│\n`);
3936
- const projCol1 = `${import_picocolors$4.default.gray("Status:")} ${limitStatus}`;
3937
- let projCol2 = `${import_picocolors$4.default.gray("Tokens:")} ${formatTokenDisplay(projection.totalTokens, false)}`;
3938
- const projCol3 = `${import_picocolors$4.default.gray("Cost:")} ${formatCurrency(projection.totalCost)}`;
3939
- const projTotalWidth = stringWidth(projCol1) + stringWidth(projCol2) + stringWidth(projCol3) + detailsSpacing * 2;
3940
- let projUseTwoLineLayout = false;
3941
- if (projTotalWidth > detailsAvailableWidth) {
3942
- projUseTwoLineLayout = true;
3943
- projCol2 = `${import_picocolors$4.default.gray("Tokens:")} ${formatTokenDisplay(projection.totalTokens, true)}`;
3944
- }
3945
- if (projUseTwoLineLayout) {
3946
- const projDetailsLine1 = `${" ".repeat(detailsIndent)}${projCol1}`;
3947
- const projDetailsLine1Padded = projDetailsLine1 + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(projDetailsLine1)));
3948
- terminal.write(`${marginStr}│ ${projDetailsLine1Padded}│\n`);
3949
- const projDetailsLine2 = `${" ".repeat(detailsIndent)}${projCol2}${" ".repeat(detailsSpacing)}${projCol3}`;
3950
- const projDetailsLine2Padded = projDetailsLine2 + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(projDetailsLine2)));
3951
- terminal.write(`${marginStr}│ ${projDetailsLine2Padded}│\n`);
3952
- } else {
3953
- const projDetails = `${" ".repeat(detailsIndent)}${projCol1}${" ".repeat(detailsSpacing)}${projCol2}${" ".repeat(detailsSpacing)}${projCol3}`;
3954
- const projDetailsPadded = projDetails + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(projDetails)));
3955
- terminal.write(`${marginStr}│ ${projDetailsPadded}│\n`);
3956
- }
3957
- terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
3958
- }
3959
- if (block.models.length > 0) {
3960
- terminal.write(`${marginStr}├${"─".repeat(boxWidth - 2)}┤\n`);
3961
- const modelsLine = `${drawEmoji("⚙️")} Models: ${formatModelsDisplay(block.models)}`;
3962
- const modelsLinePadded = modelsLine + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(modelsLine)));
3963
- terminal.write(`${marginStr}│ ${modelsLinePadded}│\n`);
3964
- }
3965
- terminal.write(`${marginStr}├${"─".repeat(boxWidth - 2)}┤\n`);
3966
- const refreshText = `${drawEmoji("↻")} Refreshing every ${config.refreshInterval / 1e3}s • Press Ctrl+C to stop`;
3967
- terminal.write(`${marginStr}│${import_picocolors$4.default.gray(centerText(refreshText, boxWidth - 2))}│\n`);
3968
- terminal.write(`${marginStr}└${"─".repeat(boxWidth - 2)}┘\n`);
3969
- }
3970
- function renderCompactLiveDisplay(terminal, block, config, totalTokens, elapsed, remaining) {
3971
- const width = terminal.width;
3972
- terminal.write(`${import_picocolors$4.default.bold(centerText("LIVE MONITOR", width))}\n`);
3973
- terminal.write(`${"─".repeat(width)}\n`);
3974
- const sessionPercent = elapsed / (elapsed + remaining) * 100;
3975
- terminal.write(`Session: ${sessionPercent.toFixed(1)}% (${Math.floor(elapsed / 60)}h ${Math.floor(elapsed % 60)}m)\n`);
3976
- if (config.tokenLimit != null && config.tokenLimit > 0) {
3977
- const tokenPercent = totalTokens / config.tokenLimit * 100;
3978
- const status = tokenPercent > 100 ? import_picocolors$4.default.red("OVER") : tokenPercent > 80 ? import_picocolors$4.default.yellow("WARN") : import_picocolors$4.default.green("OK");
3979
- terminal.write(`Tokens: ${formatNumber(totalTokens)}/${formatNumber(config.tokenLimit)} ${status}\n`);
3980
- } else terminal.write(`Tokens: ${formatNumber(totalTokens)}\n`);
3981
- terminal.write(`Cost: ${formatCurrency(block.costUSD)}\n`);
3982
- const burnRate = calculateBurnRate(block);
3983
- if (burnRate != null) terminal.write(`Rate: ${formatNumber(burnRate.tokensPerMinute)}/min\n`);
3984
- terminal.write(`${"─".repeat(width)}\n`);
3985
- terminal.write(import_picocolors$4.default.gray(`Refresh: ${config.refreshInterval / 1e3}s | Ctrl+C: stop\n`));
3986
- }
3987
- var import_picocolors$3 = /* @__PURE__ */ __toESM(require_picocolors(), 1);
3988
- async function startLiveMonitoring(config) {
3989
- try {
3990
- var _usingCtx$1 = _usingCtx();
3991
- const terminal = new TerminalManager();
3992
- const abortController = new AbortController();
3993
- let lastRenderTime = 0;
3994
- const monitorConfig = {
3995
- claudePaths: config.claudePaths,
3996
- sessionDurationHours: config.sessionDurationHours,
3997
- mode: config.mode,
3998
- order: config.order
3999
- };
4000
- const monitorState = _usingCtx$1.u(createLiveMonitorState(monitorConfig));
4001
- const cleanup = () => {
4002
- abortController.abort();
4003
- terminal.cleanup();
4004
- terminal.clearScreen();
4005
- logger.info("Live monitoring stopped.");
4006
- if (process$1.exitCode == null) process$1.exit(0);
4007
- };
4008
- process$1.on("SIGINT", cleanup);
4009
- process$1.on("SIGTERM", cleanup);
4010
- terminal.enterAlternateScreen();
4011
- terminal.enableSyncMode();
4012
- terminal.clearScreen();
4013
- terminal.hideCursor();
4014
- const monitoringResult = await try_({
4015
- try: async () => {
4016
- while (!abortController.signal.aborted) {
4017
- const timeSinceLastRender = Date.now() - lastRenderTime;
4018
- if (timeSinceLastRender < MIN_RENDER_INTERVAL_MS) {
4019
- await delayWithAbort(MIN_RENDER_INTERVAL_MS - timeSinceLastRender, abortController.signal);
4020
- continue;
4021
- }
4022
- const activeBlock = await getActiveBlock(monitorState, monitorConfig);
4023
- clearLiveMonitorCache(monitorState);
4024
- if (activeBlock == null) {
4025
- await renderWaitingState(terminal, config, abortController.signal);
4026
- continue;
4027
- }
4028
- renderActiveBlock(terminal, activeBlock, config);
4029
- lastRenderTime = Date.now();
4030
- let resizeEventHandler;
4031
- try {
4032
- await Promise.race([delayWithAbort(config.refreshInterval, abortController.signal), new Promise((resolve$1) => {
4033
- resizeEventHandler = resolve$1;
4034
- process$1.stdout.once("resize", resolve$1);
4035
- })]);
4036
- } finally {
4037
- if (resizeEventHandler != null) process$1.stdout.removeListener("resize", resizeEventHandler);
4038
- }
4039
- }
4040
- },
4041
- catch: (error) => error
4042
- })();
4043
- if (isFailure(monitoringResult)) {
4044
- const error = monitoringResult.error;
4045
- if ((error instanceof DOMException || error instanceof Error) && error.name === "AbortError") return;
4046
- const errorMessage = error instanceof Error ? error.message : String(error);
4047
- terminal.startBuffering();
4048
- terminal.clearScreen();
4049
- terminal.write(import_picocolors$3.default.red(`Error: ${errorMessage}\n`));
4050
- terminal.flush();
4051
- logger.error(`Live monitoring error: ${errorMessage}`);
4052
- await delayWithAbort(config.refreshInterval, abortController.signal).catch(() => {});
4053
- }
4054
- } catch (_$1) {
4055
- _usingCtx$1.e = _$1;
4056
- } finally {
4057
- _usingCtx$1.d();
4058
- }
4059
- }
4060
3241
  var import_picocolors$2 = /* @__PURE__ */ __toESM(require_picocolors(), 1);
4061
3242
  function formatBlockTime(block, compact = false, locale) {
4062
3243
  const start = compact ? block.startTime.toLocaleString(locale, {
@@ -4127,16 +3308,6 @@ const blocksCommand = define({
4127
3308
  short: "n",
4128
3309
  description: `Session block duration in hours (default: ${DEFAULT_SESSION_DURATION_HOURS})`,
4129
3310
  default: DEFAULT_SESSION_DURATION_HOURS
4130
- },
4131
- live: {
4132
- type: "boolean",
4133
- description: "Live monitoring mode with real-time updates",
4134
- default: false
4135
- },
4136
- refreshInterval: {
4137
- type: "number",
4138
- description: `Refresh interval in seconds for live mode (default: ${DEFAULT_REFRESH_INTERVAL_SECONDS})`,
4139
- default: DEFAULT_REFRESH_INTERVAL_SECONDS
4140
3311
  }
4141
3312
  },
4142
3313
  toKebab: true,
@@ -4183,30 +3354,6 @@ const blocksCommand = define({
4183
3354
  process$1.exit(0);
4184
3355
  }
4185
3356
  }
4186
- if (ctx.values.live && !useJson) {
4187
- if (!ctx.values.active) logger.info("Live mode automatically shows only active blocks.");
4188
- let tokenLimitValue = ctx.values.tokenLimit;
4189
- if (tokenLimitValue == null || tokenLimitValue === "") {
4190
- tokenLimitValue = "max";
4191
- if (maxTokensFromAll > 0) logger.info(`No token limit specified, using max from previous sessions: ${formatNumber(maxTokensFromAll)}`);
4192
- }
4193
- const refreshInterval = Math.max(MIN_REFRESH_INTERVAL_SECONDS, Math.min(MAX_REFRESH_INTERVAL_SECONDS, ctx.values.refreshInterval));
4194
- if (refreshInterval !== ctx.values.refreshInterval) logger.warn(`Refresh interval adjusted to ${refreshInterval} seconds (valid range: ${MIN_REFRESH_INTERVAL_SECONDS}-${MAX_REFRESH_INTERVAL_SECONDS})`);
4195
- const paths = getClaudePaths();
4196
- if (paths.length === 0) {
4197
- logger.error("No valid Claude data directory found");
4198
- throw new Error("No valid Claude data directory found");
4199
- }
4200
- await startLiveMonitoring({
4201
- claudePaths: paths,
4202
- tokenLimit: parseTokenLimit(tokenLimitValue, maxTokensFromAll),
4203
- refreshInterval: refreshInterval * 1e3,
4204
- sessionDurationHours: ctx.values.sessionLength,
4205
- mode: ctx.values.mode,
4206
- order: ctx.values.order
4207
- });
4208
- return;
4209
- }
4210
3357
  if (useJson) {
4211
3358
  const jsonOutput = { blocks: blocks.map((block) => {
4212
3359
  const burnRate = block.isActive ? calculateBurnRate(block) : null;