ccusage 15.3.0 → 15.4.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.
@@ -1,4 +1,4 @@
1
- import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-a9CiVyT5.js";
1
+ import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-7c4yXbqG.js";
2
2
  import "./pricing-fetcher-B3SvKOod.js";
3
3
 
4
4
  //#region src/_token-utils.d.ts
@@ -446,6 +446,19 @@ declare function calculateCostForEntry(data: UsageData, mode: CostMode, fetcher:
446
446
  * @returns Usage limit expiration date
447
447
  */
448
448
  declare function getUsageLimitResetTime(data: UsageData): Date | null;
449
+ /**
450
+ * Result of glob operation with base directory information
451
+ */
452
+ type GlobResult = {
453
+ file: string;
454
+ baseDir: string;
455
+ };
456
+ /**
457
+ * Glob files from multiple Claude paths in parallel
458
+ * @param claudePaths - Array of Claude base paths
459
+ * @returns Array of file paths with their base directories
460
+ */
461
+ declare function globUsageFiles(claudePaths: string[]): Promise<GlobResult[]>;
449
462
  /**
450
463
  * Date range filter for limiting usage data by date
451
464
  */
@@ -492,4 +505,4 @@ declare function loadMonthlyUsageData(options?: LoadOptions): Promise<MonthlyUsa
492
505
  */
493
506
  declare function loadSessionBlockData(options?: LoadOptions): Promise<SessionBlock[]>;
494
507
  //#endregion
495
- export { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
508
+ export { DailyUsage, DateFilter, GlobResult, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, globUsageFiles, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
@@ -1,7 +1,7 @@
1
- import { CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, isFailure, isPromise, require_usingCtx } from "./pricing-fetcher-DaK2jizg.js";
1
+ import { CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, isFailure, isPromise, require_usingCtx } from "./pricing-fetcher-KIIpXo8L.js";
2
2
  import { getTotalTokens } from "./_token-utils-WjkbrjKv.js";
3
3
  import { activityDateSchema, arrayType, booleanType, createDailyDate, createMonthlyDate, createProjectPath, createSessionId, dailyDateSchema, isoTimestampSchema, messageIdSchema, modelNameSchema, monthlyDateSchema, numberType, objectType, projectPathSchema, requestIdSchema, sessionIdSchema, stringType, versionSchema } from "./_types-BHFM59hI.js";
4
- import { logger } from "./logger-DeTONwj8.js";
4
+ import { logger } from "./logger-mXUMXvyg.js";
5
5
  import a, { readFile } from "node:fs/promises";
6
6
  import path, { posix } from "node:path";
7
7
  import process$1 from "node:process";
@@ -251,7 +251,7 @@ const unwrap = (...args) => {
251
251
  const apply = (r) => {
252
252
  if (isFailure(r)) {
253
253
  if (hasDefault$1) return defaultValue$1;
254
- throw new Error(String(r.error));
254
+ throw r.error;
255
255
  }
256
256
  return r.value;
257
257
  };
@@ -263,7 +263,7 @@ const unwrap = (...args) => {
263
263
  const apply = (r) => {
264
264
  if (isFailure(r)) {
265
265
  if (hasDefault) return defaultValue;
266
- throw new Error(String(r.error));
266
+ throw r.error;
267
267
  }
268
268
  return r.value;
269
269
  };
@@ -3237,9 +3237,12 @@ function calculateBurnRate(block) {
3237
3237
  if (durationMinutes <= 0) return null;
3238
3238
  const totalTokens = getTotalTokens(block.tokenCounts);
3239
3239
  const tokensPerMinute = totalTokens / durationMinutes;
3240
+ const nonCacheTokens = (block.tokenCounts.inputTokens ?? 0) + (block.tokenCounts.outputTokens ?? 0);
3241
+ const tokensPerMinuteForIndicator = nonCacheTokens / durationMinutes;
3240
3242
  const costPerHour = block.costUSD / durationMinutes * 60;
3241
3243
  return {
3242
3244
  tokensPerMinute,
3245
+ tokensPerMinuteForIndicator,
3243
3246
  costPerHour
3244
3247
  };
3245
3248
  }
@@ -3515,16 +3518,26 @@ function extractUniqueModels(entries, getModel) {
3515
3518
  return uniq(entries.map(getModel).filter((m$1) => m$1 != null && m$1 !== "<synthetic>"));
3516
3519
  }
3517
3520
  /**
3521
+ * Shared method for formatting dates with proper timezone handling
3522
+ * @param dateStr - Input date string
3523
+ * @param twoLine - Whether to format as two lines (true) or single line (false)
3524
+ * @returns Formatted date string
3525
+ */
3526
+ function formatDateInternal(dateStr, twoLine) {
3527
+ const date = new Date(dateStr);
3528
+ const hasTimezone = /Z|[+-]\d{2}:\d{2}/.test(dateStr);
3529
+ const year = hasTimezone ? date.getUTCFullYear() : date.getFullYear();
3530
+ const month = String(hasTimezone ? date.getUTCMonth() + 1 : date.getMonth() + 1).padStart(2, "0");
3531
+ const day = String(hasTimezone ? date.getUTCDate() : date.getDate()).padStart(2, "0");
3532
+ return twoLine ? `${year}\n${month}-${day}` : `${year}-${month}-${day}`;
3533
+ }
3534
+ /**
3518
3535
  * Formats a date string to YYYY-MM-DD format
3519
3536
  * @param dateStr - Input date string
3520
3537
  * @returns Formatted date string in YYYY-MM-DD format
3521
3538
  */
3522
3539
  function formatDate(dateStr) {
3523
- const date = new Date(dateStr);
3524
- const year = date.getFullYear();
3525
- const month = String(date.getMonth() + 1).padStart(2, "0");
3526
- const day = String(date.getDate()).padStart(2, "0");
3527
- return `${year}-${month}-${day}`;
3540
+ return formatDateInternal(dateStr, false);
3528
3541
  }
3529
3542
  /**
3530
3543
  * Formats a date string to compact format with year on first line and month-day on second
@@ -3532,11 +3545,7 @@ function formatDate(dateStr) {
3532
3545
  * @returns Formatted date string with newline separator (YYYY\nMM-DD)
3533
3546
  */
3534
3547
  function formatDateCompact(dateStr) {
3535
- const date = new Date(dateStr);
3536
- const year = date.getFullYear();
3537
- const month = String(date.getMonth() + 1).padStart(2, "0");
3538
- const day = String(date.getDate()).padStart(2, "0");
3539
- return `${year}\n${month}-${day}`;
3548
+ return formatDateInternal(dateStr, true);
3540
3549
  }
3541
3550
  /**
3542
3551
  * Generic function to sort items by date based on sort order
@@ -3644,6 +3653,25 @@ function getUsageLimitResetTime(data) {
3644
3653
  return resetTime;
3645
3654
  }
3646
3655
  /**
3656
+ * Glob files from multiple Claude paths in parallel
3657
+ * @param claudePaths - Array of Claude base paths
3658
+ * @returns Array of file paths with their base directories
3659
+ */
3660
+ async function globUsageFiles(claudePaths) {
3661
+ const filePromises = claudePaths.map(async (claudePath) => {
3662
+ const claudeDir = path.join(claudePath, CLAUDE_PROJECTS_DIR_NAME);
3663
+ const files = await glob([USAGE_DATA_GLOB_PATTERN], {
3664
+ cwd: claudeDir,
3665
+ absolute: true
3666
+ }).catch(() => []);
3667
+ return files.map((file) => ({
3668
+ file,
3669
+ baseDir: claudeDir
3670
+ }));
3671
+ });
3672
+ return (await Promise.all(filePromises)).flat();
3673
+ }
3674
+ /**
3647
3675
  * Loads and aggregates Claude usage data by day
3648
3676
  * Processes all JSONL files in the Claude projects directory and groups usage by date
3649
3677
  * @param options - Optional configuration for loading and filtering data
@@ -3653,17 +3681,10 @@ async function loadDailyUsageData(options) {
3653
3681
  try {
3654
3682
  var _usingCtx = (0, import_usingCtx.default)();
3655
3683
  const claudePaths = toArray(options?.claudePath ?? getClaudePaths());
3656
- const allFiles = [];
3657
- for (const claudePath of claudePaths) {
3658
- const claudeDir = path.join(claudePath, CLAUDE_PROJECTS_DIR_NAME);
3659
- const files = await glob([USAGE_DATA_GLOB_PATTERN], {
3660
- cwd: claudeDir,
3661
- absolute: true
3662
- });
3663
- allFiles.push(...files);
3664
- }
3665
- if (allFiles.length === 0) return [];
3666
- const sortedFiles = await sortFilesByTimestamp(allFiles);
3684
+ const allFiles = await globUsageFiles(claudePaths);
3685
+ const fileList = allFiles.map((f$1) => f$1.file);
3686
+ if (fileList.length === 0) return [];
3687
+ const sortedFiles = await sortFilesByTimestamp(fileList);
3667
3688
  const mode = options?.mode ?? "auto";
3668
3689
  const fetcher = _usingCtx.u(mode === "display" ? null : new PricingFetcher(options?.offline));
3669
3690
  const processedHashes = /* @__PURE__ */ new Set();
@@ -3721,18 +3742,7 @@ async function loadSessionData(options) {
3721
3742
  try {
3722
3743
  var _usingCtx3 = (0, import_usingCtx.default)();
3723
3744
  const claudePaths = toArray(options?.claudePath ?? getClaudePaths());
3724
- const filesWithBase = [];
3725
- for (const claudePath of claudePaths) {
3726
- const claudeDir = path.join(claudePath, CLAUDE_PROJECTS_DIR_NAME);
3727
- const files = await glob([USAGE_DATA_GLOB_PATTERN], {
3728
- cwd: claudeDir,
3729
- absolute: true
3730
- });
3731
- for (const file of files) filesWithBase.push({
3732
- file,
3733
- baseDir: claudeDir
3734
- });
3735
- }
3745
+ const filesWithBase = await globUsageFiles(claudePaths);
3736
3746
  if (filesWithBase.length === 0) return [];
3737
3747
  const fileToBaseMap = new Map(filesWithBase.map((f$1) => [f$1.file, f$1.baseDir]));
3738
3748
  const sortedFilesWithBase = await sortFilesByTimestamp(filesWithBase.map((f$1) => f$1.file)).then((sortedFiles) => sortedFiles.map((file) => ({
@@ -3912,4 +3922,4 @@ async function loadSessionBlockData(options) {
3912
3922
  _usingCtx4.d();
3913
3923
  }
3914
3924
  }
3915
- export { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, dailyUsageSchema, filterRecentBlocks, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, glob, identifySessionBlocks, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, projectBlockUsage, sessionUsageSchema, sortFilesByTimestamp, uniq, unwrap, usageDataSchema };
3925
+ export { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, dailyUsageSchema, filterRecentBlocks, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, glob, globUsageFiles, identifySessionBlocks, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, projectBlockUsage, sessionUsageSchema, sortFilesByTimestamp, uniq, unwrap, usageDataSchema };
@@ -1,3 +1,3 @@
1
- import { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-a9CiVyT5.js";
1
+ import { DailyUsage, DateFilter, GlobResult, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, globUsageFiles, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-7c4yXbqG.js";
2
2
  import "./pricing-fetcher-B3SvKOod.js";
3
- export { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
3
+ export { DailyUsage, DateFilter, GlobResult, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, globUsageFiles, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
@@ -1,6 +1,6 @@
1
- import "./pricing-fetcher-DaK2jizg.js";
1
+ import "./pricing-fetcher-KIIpXo8L.js";
2
2
  import "./_token-utils-WjkbrjKv.js";
3
3
  import "./_types-BHFM59hI.js";
4
- import { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-R9pMvoQp.js";
5
- import "./logger-DeTONwj8.js";
6
- export { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
4
+ import { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, globUsageFiles, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-BNrxyvc9.js";
5
+ import "./logger-mXUMXvyg.js";
6
+ export { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, globUsageFiles, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
@@ -1,6 +1,6 @@
1
- import { CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, PricingFetcher, USAGE_DATA_GLOB_PATTERN, __toESM, isFailure, require_usingCtx, try_ } from "./pricing-fetcher-DaK2jizg.js";
2
- import { getClaudePaths, glob, unwrap, usageDataSchema } from "./data-loader-R9pMvoQp.js";
3
- import { logger } from "./logger-DeTONwj8.js";
1
+ import { CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, PricingFetcher, USAGE_DATA_GLOB_PATTERN, __toESM, isFailure, require_usingCtx, try_ } from "./pricing-fetcher-KIIpXo8L.js";
2
+ import { getClaudePaths, glob, unwrap, usageDataSchema } from "./data-loader-BNrxyvc9.js";
3
+ import { logger } from "./logger-mXUMXvyg.js";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import path from "node:path";
6
6
  var import_usingCtx = __toESM(require_usingCtx(), 1);
package/dist/debug.js CHANGED
@@ -1,7 +1,7 @@
1
- import "./pricing-fetcher-DaK2jizg.js";
1
+ import "./pricing-fetcher-KIIpXo8L.js";
2
2
  import "./_token-utils-WjkbrjKv.js";
3
3
  import "./_types-BHFM59hI.js";
4
- import "./data-loader-R9pMvoQp.js";
5
- import "./logger-DeTONwj8.js";
6
- import { detectMismatches, printMismatchReport } from "./debug-Cyr1LS0T.js";
4
+ import "./data-loader-BNrxyvc9.js";
5
+ import "./logger-mXUMXvyg.js";
6
+ import { detectMismatches, printMismatchReport } from "./debug-Brn3ODNf.js";
7
7
  export { detectMismatches, printMismatchReport };
package/dist/index.js CHANGED
@@ -1,14 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, CLAUDE_PROJECTS_DIR_NAME, DEFAULT_RECENT_DAYS, DEFAULT_REFRESH_INTERVAL_SECONDS, MAX_REFRESH_INTERVAL_SECONDS, MCP_DEFAULT_PORT, MIN_REFRESH_INTERVAL_SECONDS, MIN_RENDER_INTERVAL_MS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, __commonJSMin, __require, __toESM, isFailure, require_usingCtx, try_ } from "./pricing-fetcher-DaK2jizg.js";
2
+ import { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, BURN_RATE_THRESHOLDS, DEFAULT_RECENT_DAYS, DEFAULT_REFRESH_INTERVAL_SECONDS, MAX_REFRESH_INTERVAL_SECONDS, MCP_DEFAULT_PORT, MIN_REFRESH_INTERVAL_SECONDS, MIN_RENDER_INTERVAL_MS, PricingFetcher, __commonJSMin, __require, __toESM, isFailure, require_usingCtx, try_ } from "./pricing-fetcher-KIIpXo8L.js";
3
3
  import { getTotalTokens } from "./_token-utils-WjkbrjKv.js";
4
4
  import { CostModes, SortOrders, filterDateSchema } from "./_types-BHFM59hI.js";
5
5
  import { calculateTotals, createTotalsObject } from "./calculate-cost-BDqO4yWA.js";
6
- import { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, filterRecentBlocks, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, glob, identifySessionBlocks, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, projectBlockUsage, sortFilesByTimestamp, uniq, usageDataSchema } from "./data-loader-R9pMvoQp.js";
7
- import { description, log, logger, name, version } from "./logger-DeTONwj8.js";
8
- import { detectMismatches, printMismatchReport } from "./debug-Cyr1LS0T.js";
9
- import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-Kff7A2dF.js";
6
+ import { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, filterRecentBlocks, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, globUsageFiles, identifySessionBlocks, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, projectBlockUsage, sortFilesByTimestamp, uniq, usageDataSchema } from "./data-loader-BNrxyvc9.js";
7
+ import { description, log, logger, name, version } from "./logger-mXUMXvyg.js";
8
+ import { detectMismatches, printMismatchReport } from "./debug-Brn3ODNf.js";
9
+ import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-CQw7snVU.js";
10
10
  import { readFile } from "node:fs/promises";
11
- import path from "node:path";
12
11
  import process$1 from "node:process";
13
12
  import { createServer } from "node:http";
14
13
  import { Http2ServerRequest } from "node:http2";
@@ -3205,14 +3204,11 @@ var LiveMonitor = class {
3205
3204
  * Only reads new or modified files since last check
3206
3205
  */
3207
3206
  async getActiveBlock() {
3208
- const claudeDir = path.join(this.config.claudePath, CLAUDE_PROJECTS_DIR_NAME);
3209
- const files = await glob([USAGE_DATA_GLOB_PATTERN], {
3210
- cwd: claudeDir,
3211
- absolute: true
3212
- });
3213
- if (files.length === 0) return null;
3207
+ const results = await globUsageFiles(this.config.claudePaths);
3208
+ const allFiles = results.map((r) => r.file);
3209
+ if (allFiles.length === 0) return null;
3214
3210
  const filesToRead = [];
3215
- for (const file of files) {
3211
+ for (const file of allFiles) {
3216
3212
  const timestamp = await getEarliestTimestamp(file);
3217
3213
  const lastTimestamp = this.lastFileTimestamps.get(file);
3218
3214
  if (timestamp != null && (lastTimestamp == null || timestamp.getTime() > lastTimestamp)) {
@@ -3684,6 +3680,17 @@ function centerText(text, width) {
3684
3680
  }
3685
3681
  var import_picocolors$5 = __toESM(require_picocolors(), 1);
3686
3682
  /**
3683
+ * Get rate indicator (HIGH/MODERATE/NORMAL) based on burn rate
3684
+ */
3685
+ function getRateIndicator(burnRate) {
3686
+ if (burnRate == null) return "";
3687
+ switch (true) {
3688
+ case burnRate.tokensPerMinuteForIndicator > BURN_RATE_THRESHOLDS.HIGH: return import_picocolors$5.default.red("⚡ HIGH");
3689
+ case burnRate.tokensPerMinuteForIndicator > BURN_RATE_THRESHOLDS.MODERATE: return import_picocolors$5.default.yellow("⚡ MODERATE");
3690
+ default: return import_picocolors$5.default.green("✓ NORMAL");
3691
+ }
3692
+ }
3693
+ /**
3687
3694
  * Delay with AbortSignal support and graceful error handling
3688
3695
  */
3689
3696
  async function delayWithAbort(ms, signal) {
@@ -3734,7 +3741,7 @@ const DETAIL_COLUMN_WIDTHS = {
3734
3741
  function renderLiveDisplay(terminal, block, config) {
3735
3742
  const width = terminal.width;
3736
3743
  const now = /* @__PURE__ */ new Date();
3737
- const totalTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
3744
+ const totalTokens = getTotalTokens(block.tokenCounts);
3738
3745
  const elapsed = (now.getTime() - block.startTime.getTime()) / (1e3 * 60);
3739
3746
  const remaining = (block.endTime.getTime() - now.getTime()) / (1e3 * 60);
3740
3747
  if (width < 60) {
@@ -3814,7 +3821,7 @@ function renderLiveDisplay(terminal, block, config) {
3814
3821
  rightBracket: "]"
3815
3822
  }) : `[${import_picocolors$5.default.green("█".repeat(Math.floor(barWidth * .1)))}${import_picocolors$5.default.gray("░".repeat(barWidth - Math.floor(barWidth * .1)))}]`;
3816
3823
  const burnRate = calculateBurnRate(block);
3817
- const rateIndicator = burnRate != null ? burnRate.tokensPerMinute > 1e3 ? import_picocolors$5.default.red("⚡ HIGH") : burnRate.tokensPerMinute > 500 ? import_picocolors$5.default.yellow("⚡ MODERATE") : import_picocolors$5.default.green("✓ NORMAL") : "";
3824
+ const rateIndicator = getRateIndicator(burnRate);
3818
3825
  const rateDisplay = burnRate != null ? `${import_picocolors$5.default.bold("Burn Rate:")} ${Math.round(burnRate.tokensPerMinute)} token/min ${rateIndicator}` : `${import_picocolors$5.default.bold("Burn Rate:")} N/A`;
3819
3826
  const usageLabel = import_picocolors$5.default.bold("🔥 USAGE");
3820
3827
  const usageLabelWidth = stringWidth(usageLabel);
@@ -3941,7 +3948,7 @@ async function startLiveMonitoring(config) {
3941
3948
  terminal.clearScreen();
3942
3949
  terminal.hideCursor();
3943
3950
  const monitor = _usingCtx.u(new LiveMonitor({
3944
- claudePath: config.claudePath,
3951
+ claudePaths: config.claudePaths,
3945
3952
  sessionDurationHours: config.sessionDurationHours,
3946
3953
  mode: config.mode,
3947
3954
  order: config.order
@@ -4041,8 +4048,7 @@ function formatModels(models) {
4041
4048
  * @returns Parsed token limit or undefined if invalid
4042
4049
  */
4043
4050
  function parseTokenLimit(value, maxFromAll) {
4044
- if (value == null || value === "") return void 0;
4045
- if (value === "max") return maxFromAll > 0 ? maxFromAll : void 0;
4051
+ if (value == null || value === "" || value === "max") return maxFromAll > 0 ? maxFromAll : void 0;
4046
4052
  const limit = Number.parseInt(value, 10);
4047
4053
  return Number.isNaN(limit) ? void 0 : limit;
4048
4054
  }
@@ -4106,7 +4112,7 @@ const blocksCommand = define({
4106
4112
  process$1.exit(0);
4107
4113
  }
4108
4114
  let maxTokensFromAll = 0;
4109
- if (ctx.values.tokenLimit === "max") {
4115
+ if (ctx.values.tokenLimit === "max" || ctx.values.tokenLimit == null || ctx.values.tokenLimit === "") {
4110
4116
  for (const block of blocks) if (!(block.isGap ?? false) && !block.isActive) {
4111
4117
  const blockTokens = getTotalTokens(block.tokenCounts);
4112
4118
  if (blockTokens > maxTokensFromAll) maxTokensFromAll = blockTokens;
@@ -4140,7 +4146,7 @@ const blocksCommand = define({
4140
4146
  throw new Error("No valid Claude data directory found");
4141
4147
  }
4142
4148
  await startLiveMonitoring({
4143
- claudePath: paths[0],
4149
+ claudePaths: paths,
4144
4150
  tokenLimit: parseTokenLimit(tokenLimitValue, maxTokensFromAll),
4145
4151
  refreshInterval: refreshInterval * 1e3,
4146
4152
  sessionDurationHours: ctx.values.sessionLength,
@@ -4162,7 +4168,7 @@ const blocksCommand = define({
4162
4168
  isGap: block.isGap ?? false,
4163
4169
  entries: block.entries.length,
4164
4170
  tokenCounts: block.tokenCounts,
4165
- totalTokens: block.tokenCounts.inputTokens + block.tokenCounts.outputTokens,
4171
+ totalTokens: getTotalTokens(block.tokenCounts),
4166
4172
  costUSD: block.costUSD,
4167
4173
  models: block.models,
4168
4174
  burnRate,
@@ -4463,6 +4469,7 @@ var Request = class extends GlobalRequest {
4463
4469
  super(input, options);
4464
4470
  }
4465
4471
  };
4472
+ var wrapBodyStream = Symbol("wrapBodyStream");
4466
4473
  var newRequestFromIncoming = (method, url, incoming, abortController) => {
4467
4474
  const headerRecord = [];
4468
4475
  const rawHeaders = incoming.rawHeaders;
@@ -4487,7 +4494,19 @@ var newRequestFromIncoming = (method, url, incoming, abortController) => {
4487
4494
  controller.enqueue(incoming.rawBody);
4488
4495
  controller.close();
4489
4496
  } });
4490
- else init$1.body = Readable.toWeb(incoming);
4497
+ else if (incoming[wrapBodyStream]) {
4498
+ let reader;
4499
+ init$1.body = new ReadableStream({ async pull(controller) {
4500
+ try {
4501
+ reader ||= Readable.toWeb(incoming).getReader();
4502
+ const { done, value } = await reader.read();
4503
+ if (done) controller.close();
4504
+ else controller.enqueue(value);
4505
+ } catch (error) {
4506
+ controller.error(error);
4507
+ }
4508
+ } });
4509
+ } else init$1.body = Readable.toWeb(incoming);
4491
4510
  return new Request(url, init$1);
4492
4511
  };
4493
4512
  var getRequestCache = Symbol("getRequestCache");
@@ -4698,6 +4717,7 @@ global.fetch = (info$1, init$1) => {
4698
4717
  };
4699
4718
  return webFetch(info$1, init$1);
4700
4719
  };
4720
+ var outgoingEnded = Symbol("outgoingEnded");
4701
4721
  var regBuffer = /^no$/i;
4702
4722
  var regContentType = /^(application\/json\b|text\/(?!event-stream\b))/i;
4703
4723
  var handleRequestError = () => new Response(null, { status: 400 });
@@ -4726,8 +4746,9 @@ var responseViaCache = async (res, outgoing) => {
4726
4746
  else if (body instanceof Blob) outgoing.end(new Uint8Array(await body.arrayBuffer()));
4727
4747
  else {
4728
4748
  flushHeaders(outgoing);
4729
- return writeFromReadableStream(body, outgoing)?.catch((e) => handleResponseError(e, outgoing));
4749
+ await writeFromReadableStream(body, outgoing)?.catch((e) => handleResponseError(e, outgoing));
4730
4750
  }
4751
+ outgoing[outgoingEnded]?.();
4731
4752
  };
4732
4753
  var responseViaResponseObject = async (res, outgoing, options = {}) => {
4733
4754
  if (res instanceof Promise) if (options.errorHandler) try {
@@ -4756,8 +4777,10 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
4756
4777
  outgoing.writeHead(res.status, resHeaderRecord);
4757
4778
  outgoing.end();
4758
4779
  }
4780
+ outgoing[outgoingEnded]?.();
4759
4781
  };
4760
4782
  var getRequestListener = (fetchCallback, options = {}) => {
4783
+ const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
4761
4784
  if (options.overrideGlobalObjects !== false && global.Request !== Request) {
4762
4785
  Object.defineProperty(global, "Request", { value: Request });
4763
4786
  Object.defineProperty(global, "Response", { value: Response2 });
@@ -4766,11 +4789,32 @@ var getRequestListener = (fetchCallback, options = {}) => {
4766
4789
  let res, req;
4767
4790
  try {
4768
4791
  req = newRequest(incoming, options.hostname);
4792
+ let incomingEnded = !autoCleanupIncoming || incoming.method === "GET" || incoming.method === "HEAD";
4793
+ if (!incomingEnded) {
4794
+ incoming[wrapBodyStream] = true;
4795
+ incoming.on("end", () => {
4796
+ incomingEnded = true;
4797
+ });
4798
+ if (incoming instanceof Http2ServerRequest) outgoing[outgoingEnded] = () => {
4799
+ if (!incomingEnded) setTimeout(() => {
4800
+ if (!incomingEnded) setTimeout(() => {
4801
+ incoming.destroy();
4802
+ outgoing.destroy();
4803
+ });
4804
+ });
4805
+ };
4806
+ }
4769
4807
  outgoing.on("close", () => {
4770
4808
  const abortController = req[abortControllerKey];
4771
- if (!abortController) return;
4772
- if (incoming.errored) req[abortControllerKey].abort(incoming.errored.toString());
4773
- else if (!outgoing.writableFinished) req[abortControllerKey].abort("Client connection prematurely closed.");
4809
+ if (abortController) {
4810
+ if (incoming.errored) req[abortControllerKey].abort(incoming.errored.toString());
4811
+ else if (!outgoing.writableFinished) req[abortControllerKey].abort("Client connection prematurely closed.");
4812
+ }
4813
+ if (!incomingEnded) setTimeout(() => {
4814
+ if (!incomingEnded) setTimeout(() => {
4815
+ incoming.destroy();
4816
+ });
4817
+ });
4774
4818
  });
4775
4819
  res = fetchCallback(req, {
4776
4820
  incoming,
@@ -4796,7 +4840,8 @@ var createAdaptorServer = (options) => {
4796
4840
  const fetchCallback = options.fetch;
4797
4841
  const requestListener = getRequestListener(fetchCallback, {
4798
4842
  hostname: options.hostname,
4799
- overrideGlobalObjects: options.overrideGlobalObjects
4843
+ overrideGlobalObjects: options.overrideGlobalObjects,
4844
+ autoCleanupIncoming: options.autoCleanupIncoming
4800
4845
  });
4801
4846
  const createServer$1 = options.createServer || createServer;
4802
4847
  const server = createServer$1(options.serverOptions || {}, requestListener);
@@ -951,7 +951,7 @@ function _getDefaultLogLevel() {
951
951
  }
952
952
  const consola = createConsola$1();
953
953
  var name = "ccusage";
954
- var version = "15.3.0";
954
+ var version = "15.4.0";
955
955
  var description = "Usage analysis tool for Claude Code";
956
956
  /**
957
957
  * Application logger instance with package name tag
package/dist/logger.js CHANGED
@@ -1,2 +1,2 @@
1
- import { log, logger } from "./logger-DeTONwj8.js";
1
+ import { log, logger } from "./logger-mXUMXvyg.js";
2
2
  export { log, logger };
@@ -1,9 +1,9 @@
1
- import { __commonJSMin, __toESM, require_usingCtx } from "./pricing-fetcher-DaK2jizg.js";
1
+ import { __commonJSMin, __toESM, require_usingCtx } from "./pricing-fetcher-KIIpXo8L.js";
2
2
  import { getTotalTokens } from "./_token-utils-WjkbrjKv.js";
3
3
  import { ZodFirstPartyTypeKind, ZodOptional, ZodType, arrayType, booleanType, discriminatedUnionType, enumType, filterDateSchema, literalType, numberType, objectType, optionalType, recordType, stringType, unionType, unknownType } from "./_types-BHFM59hI.js";
4
4
  import { calculateTotals, createTotalsObject } from "./calculate-cost-BDqO4yWA.js";
5
- import { getClaudePaths, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData } from "./data-loader-R9pMvoQp.js";
6
- import { name, version } from "./logger-DeTONwj8.js";
5
+ import { getClaudePaths, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData } from "./data-loader-BNrxyvc9.js";
6
+ import { name, version } from "./logger-mXUMXvyg.js";
7
7
  import process from "node:process";
8
8
  const LATEST_PROTOCOL_VERSION = "2025-06-18";
9
9
  const SUPPORTED_PROTOCOL_VERSIONS = [
@@ -606,7 +606,7 @@ const ElicitRequestSchema = RequestSchema.extend({
606
606
  const ElicitResultSchema = ResultSchema.extend({
607
607
  action: enumType([
608
608
  "accept",
609
- "reject",
609
+ "decline",
610
610
  "cancel"
611
611
  ]),
612
612
  content: optionalType(recordType(stringType(), unknownType()))
@@ -1054,7 +1054,7 @@ var HonoRequest = class {
1054
1054
  return bodyCache[key] = raw$1[key]();
1055
1055
  };
1056
1056
  json() {
1057
- return this.#cachedBody("json");
1057
+ return this.#cachedBody("text").then((text) => JSON.parse(text));
1058
1058
  }
1059
1059
  text() {
1060
1060
  return this.#cachedBody("text");
@@ -8561,10 +8561,6 @@ var McpServer = class {
8561
8561
  return registeredResourceTemplate;
8562
8562
  }
8563
8563
  }
8564
- /**
8565
- * Registers a resource with a config object and callback.
8566
- * For static resources, use a URI string. For dynamic resources, use a ResourceTemplate.
8567
- */
8568
8564
  registerResource(name$1, uriOrTemplate, config, readCallback) {
8569
8565
  if (typeof uriOrTemplate === "string") {
8570
8566
  if (this._registeredResources[uriOrTemplate]) throw new Error(`Resource ${uriOrTemplate} is already registered`);
@@ -8766,7 +8762,10 @@ var McpServer = class {
8766
8762
  if (this.isConnected()) this.server.sendPromptListChanged();
8767
8763
  }
8768
8764
  };
8769
- const EMPTY_OBJECT_JSON_SCHEMA = { type: "object" };
8765
+ const EMPTY_OBJECT_JSON_SCHEMA = {
8766
+ type: "object",
8767
+ properties: {}
8768
+ };
8770
8769
  function isZodRawShape(obj) {
8771
8770
  if (typeof obj !== "object" || obj === null) return false;
8772
8771
  const isEmptyObject = Object.keys(obj).length === 0;
package/dist/mcp.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { LoadOptions } from "./data-loader-a9CiVyT5.js";
1
+ import { LoadOptions } from "./data-loader-7c4yXbqG.js";
2
2
  import "./pricing-fetcher-B3SvKOod.js";
3
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
4
  import { Hono } from "hono/tiny";
package/dist/mcp.js CHANGED
@@ -1,8 +1,8 @@
1
- import "./pricing-fetcher-DaK2jizg.js";
1
+ import "./pricing-fetcher-KIIpXo8L.js";
2
2
  import "./_token-utils-WjkbrjKv.js";
3
3
  import "./_types-BHFM59hI.js";
4
4
  import "./calculate-cost-BDqO4yWA.js";
5
- import "./data-loader-R9pMvoQp.js";
6
- import "./logger-DeTONwj8.js";
7
- import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-Kff7A2dF.js";
5
+ import "./data-loader-BNrxyvc9.js";
6
+ import "./logger-mXUMXvyg.js";
7
+ import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-CQw7snVU.js";
8
8
  export { createMcpHttpApp, createMcpServer, startMcpServerStdio };
@@ -1,5 +1,5 @@
1
1
  import { modelPricingSchema } from "./_types-BHFM59hI.js";
2
- import { logger } from "./logger-DeTONwj8.js";
2
+ import { logger } from "./logger-mXUMXvyg.js";
3
3
  import { createRequire } from "node:module";
4
4
  import path from "node:path";
5
5
  import F, { homedir } from "node:os";
@@ -47,6 +47,7 @@ const andThrough = (fn) => (result) => {
47
47
  };
48
48
  return isPromise(result) ? result.then(apply) : apply(result);
49
49
  };
50
+ const isSuccess = (result) => "Success" === result.type;
50
51
  const succeed = (...args) => {
51
52
  const value = args[0];
52
53
  if (void 0 === value) return { type: "Success" };
@@ -71,7 +72,6 @@ const fail = (...args) => {
71
72
  error
72
73
  };
73
74
  };
74
- const isSuccess = (result) => "Success" === result.type;
75
75
  const inspect = (fn) => (result) => {
76
76
  const apply = (r) => {
77
77
  if (isSuccess(r)) fn(r.value);
@@ -226,6 +226,13 @@ const MAX_REFRESH_INTERVAL_SECONDS = 60;
226
226
  * Prevents terminal flickering and excessive CPU usage during rapid updates
227
227
  */
228
228
  const MIN_RENDER_INTERVAL_MS = 16;
229
+ /**
230
+ * Burn rate thresholds for indicator display (tokens per minute)
231
+ */
232
+ const BURN_RATE_THRESHOLDS = {
233
+ HIGH: 1e3,
234
+ MODERATE: 500
235
+ };
229
236
  var require_usingCtx = __commonJSMin((exports, module) => {
230
237
  function _usingCtx() {
231
238
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r$1, e$1) {
@@ -317,14 +324,6 @@ var PricingFetcher = class {
317
324
  loadOfflinePricing = try_({
318
325
  try: async () => {
319
326
  const pricing = new Map(Object.entries({
320
- "claude-instant-1": {
321
- "input_cost_per_token": 163e-8,
322
- "output_cost_per_token": 551e-8
323
- },
324
- "claude-instant-1.2": {
325
- "input_cost_per_token": 163e-9,
326
- "output_cost_per_token": 551e-9
327
- },
328
327
  "claude-2": {
329
328
  "input_cost_per_token": 8e-6,
330
329
  "output_cost_per_token": 24e-6
@@ -539,4 +538,4 @@ var PricingFetcher = class {
539
538
  return cost;
540
539
  }
541
540
  };
542
- export { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, DEFAULT_REFRESH_INTERVAL_SECONDS, MAX_REFRESH_INTERVAL_SECONDS, MCP_DEFAULT_PORT, MIN_REFRESH_INTERVAL_SECONDS, MIN_RENDER_INTERVAL_MS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, isFailure, isPromise, require_usingCtx, try_ };
541
+ export { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, BURN_RATE_THRESHOLDS, CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, DEFAULT_REFRESH_INTERVAL_SECONDS, MAX_REFRESH_INTERVAL_SECONDS, MCP_DEFAULT_PORT, MIN_REFRESH_INTERVAL_SECONDS, MIN_RENDER_INTERVAL_MS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, isFailure, isPromise, require_usingCtx, try_ };
@@ -1,4 +1,4 @@
1
- import { PricingFetcher } from "./pricing-fetcher-DaK2jizg.js";
1
+ import { PricingFetcher } from "./pricing-fetcher-KIIpXo8L.js";
2
2
  import "./_types-BHFM59hI.js";
3
- import "./logger-DeTONwj8.js";
3
+ import "./logger-mXUMXvyg.js";
4
4
  export { PricingFetcher };
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "ccusage",
3
- "type": "module",
4
- "version": "15.3.0",
3
+ "version": "15.4.0",
5
4
  "description": "Usage analysis tool for Claude Code",
6
- "author": "ryoppippi",
7
- "license": "MIT",
8
- "funding": "https://github.com/ryoppippi/ccusage?sponsor=1",
9
5
  "homepage": "https://github.com/ryoppippi/ccusage#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/ryoppippi/ccusage/issues"
8
+ },
10
9
  "repository": {
11
10
  "type": "git",
12
11
  "url": "git+https://github.com/ryoppippi/ccusage.git"
13
12
  },
14
- "bugs": {
15
- "url": "https://github.com/ryoppippi/ccusage/issues"
16
- },
13
+ "funding": "https://github.com/ryoppippi/ccusage?sponsor=1",
14
+ "license": "MIT",
15
+ "author": "ryoppippi",
16
+ "type": "module",
17
17
  "exports": {
18
18
  ".": "./dist/index.js",
19
19
  "./calculate-cost": "./dist/calculate-cost.js",
@@ -32,6 +32,6 @@
32
32
  "dist"
33
33
  ],
34
34
  "engines": {
35
- "node": ">=20.19.3"
35
+ "node": ">=20.19.4"
36
36
  }
37
37
  }