ccusage 15.2.0 → 15.3.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/README.md CHANGED
@@ -20,8 +20,29 @@
20
20
 
21
21
  ## Installation
22
22
 
23
+ ### Quick Start (Recommended)
24
+
25
+ Thanks to ccusage's incredibly small bundle size ([![install size](https://packagephobia.com/badge?p=ccusage)](https://packagephobia.com/result?p=ccusage)), you can run it directly without installation:
26
+
27
+ ```bash
28
+ # Using bunx (recommended for speed)
29
+ bunx ccusage
30
+
31
+ # Using npx
32
+ npx ccusage@latest
33
+
34
+ # Using deno (with security flags)
35
+ deno run -E -R=$HOME/.claude/projects/ -S=homedir -N='raw.githubusercontent.com:443' npm:ccusage@latest
36
+ ```
37
+
38
+ > 💡 **Tip**: We recommend using `bunx` instead of `npx` for a massive speed improvement!
39
+
40
+ ### Local Installation (Optional)
41
+
42
+ Since ccusage has such a small bundle size, installation is entirely optional:
43
+
23
44
  ```bash
24
- npm i -g ccusage
45
+ npm install -g ccusage
25
46
  ```
26
47
 
27
48
  ## Usage
@@ -62,6 +83,7 @@ ccusage daily --breakdown # Per-model cost breakdown
62
83
  - 🔄 **Cache Token Support**: Tracks and displays cache creation and cache read tokens separately
63
84
  - 🌐 **Offline Mode**: Use pre-cached pricing data without network connectivity with `--offline` (Claude models only)
64
85
  - 🔌 **MCP Integration**: Built-in Model Context Protocol server for integration with other tools
86
+ - 🚀 **Ultra-Small Bundle**: Unlike other CLI tools, we pay extreme attention to bundle size - incredibly small even without minification!
65
87
 
66
88
  ## Documentation
67
89
 
@@ -69,6 +91,16 @@ Full documentation is available at **[ccusage.com](https://ccusage.com/)**
69
91
 
70
92
  ## Sponsors
71
93
 
94
+ ### Featured Sponsor
95
+
96
+ Check out these [47 Claude Code ProTips from Greg Baugues.](https://www.youtube.com/watch?v=TiNpzxoBPz0&lc=UgyVgQyOhfJJlheVMcB4AaABAg)
97
+
98
+ <p align="center">
99
+ <a href="https://www.youtube.com/watch?v=TiNpzxoBPz0&lc=UgyVgQyOhfJJlheVMcB4AaABAg">
100
+ <img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/public/claude_code_protips_thumbnail_v1.png" alt="47 Claude Code ProTips from Greg Baugues" width="600">
101
+ </a>
102
+ </p>
103
+
72
104
  <p align="center">
73
105
  <a href="https://github.com/sponsors/ryoppippi">
74
106
  <img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors@main/sponsors.svg">
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Calculates the total number of tokens across all token types
3
+ * Supports both raw usage data format and aggregated data format
4
+ * @param tokenCounts - Object containing counts for each token type
5
+ * @returns Total number of tokens
6
+ */
7
+ function getTotalTokens(tokenCounts) {
8
+ const cacheCreation = "cacheCreationInputTokens" in tokenCounts ? tokenCounts.cacheCreationInputTokens : tokenCounts.cacheCreationTokens;
9
+ const cacheRead = "cacheReadInputTokens" in tokenCounts ? tokenCounts.cacheReadInputTokens : tokenCounts.cacheReadTokens;
10
+ return tokenCounts.inputTokens + tokenCounts.outputTokens + cacheCreation + cacheRead;
11
+ }
12
+ export { getTotalTokens };
@@ -725,7 +725,7 @@ const ipv6CidrRegex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1
725
725
  const base64Regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;
726
726
  const base64urlRegex = /^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/;
727
727
  const dateRegexSource = `((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))`;
728
- const dateRegex = new RegExp(`^${dateRegexSource}$`);
728
+ const dateRegex = /* @__PURE__ */ new RegExp(`^${dateRegexSource}$`);
729
729
  function timeRegexSource(args) {
730
730
  let secondsRegexSource = `[0-5]\\d`;
731
731
  if (args.precision) secondsRegexSource = `${secondsRegexSource}\\.\\d{${args.precision}}`;
@@ -734,7 +734,7 @@ function timeRegexSource(args) {
734
734
  return `([01]\\d|2[0-3]):[0-5]\\d(:${secondsRegexSource})${secondsQuantifier}`;
735
735
  }
736
736
  function timeRegex(args) {
737
- return new RegExp(`^${timeRegexSource(args)}$`);
737
+ return /* @__PURE__ */ new RegExp(`^${timeRegexSource(args)}$`);
738
738
  }
739
739
  function datetimeRegex(args) {
740
740
  let regex = `${dateRegexSource}T${timeRegexSource(args)}`;
@@ -742,7 +742,7 @@ function datetimeRegex(args) {
742
742
  opts.push(args.local ? `Z?` : `Z`);
743
743
  if (args.offset) opts.push(`([+-]\\d{2}:?\\d{2})`);
744
744
  regex = `${regex}(${opts.join("|")})`;
745
- return new RegExp(`^${regex}$`);
745
+ return /* @__PURE__ */ new RegExp(`^${regex}$`);
746
746
  }
747
747
  function isValidIP(ip, version) {
748
748
  if ((version === "v4" || !version) && ipv4Regex.test(ip)) return true;
@@ -1,3 +1,4 @@
1
+ import { getTotalTokens } from "./_token-utils-WjkbrjKv.js";
1
2
  /**
2
3
  * Calculates total token usage and cost across multiple usage data entries
3
4
  * @param data - Array of daily, monthly, or session usage data
@@ -19,14 +20,6 @@ function calculateTotals(data) {
19
20
  });
20
21
  }
21
22
  /**
22
- * Calculates the sum of all token types (input, output, cache creation, cache read)
23
- * @param tokens - Token data containing different token counts
24
- * @returns Total number of tokens across all types
25
- */
26
- function getTotalTokens(tokens) {
27
- return tokens.inputTokens + tokens.outputTokens + tokens.cacheCreationTokens + tokens.cacheReadTokens;
28
- }
29
- /**
30
23
  * Creates a complete totals object by adding total token count to existing totals
31
24
  * @param totals - Token totals with cost information
32
25
  * @returns Complete totals object including total token sum
@@ -37,4 +30,4 @@ function createTotalsObject(totals) {
37
30
  totalTokens: getTotalTokens(totals)
38
31
  };
39
32
  }
40
- export { calculateTotals, createTotalsObject, getTotalTokens };
33
+ export { calculateTotals, createTotalsObject };
@@ -1,17 +1,51 @@
1
- import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-BuHgMcpg.js";
2
- import "./pricing-fetcher-CrV0acwD.js";
1
+ import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-a9CiVyT5.js";
2
+ import "./pricing-fetcher-B3SvKOod.js";
3
3
 
4
- //#region src/calculate-cost.d.ts
4
+ //#region src/_token-utils.d.ts
5
5
 
6
6
  /**
7
- * Token usage data structure containing input, output, and cache token counts
7
+ * @fileoverview Token calculation utilities
8
+ *
9
+ * This module provides shared utilities for calculating token totals
10
+ * across different token types. Used throughout the application to
11
+ * ensure consistent token counting logic.
12
+ */
13
+ /**
14
+ * Token counts structure for raw usage data (uses InputTokens suffix)
15
+ */
16
+ type TokenCounts = {
17
+ inputTokens: number;
18
+ outputTokens: number;
19
+ cacheCreationInputTokens: number;
20
+ cacheReadInputTokens: number;
21
+ };
22
+ /**
23
+ * Token counts structure for aggregated data (uses shorter names)
8
24
  */
9
- type TokenData = {
25
+ type AggregatedTokenCounts = {
10
26
  inputTokens: number;
11
27
  outputTokens: number;
12
28
  cacheCreationTokens: number;
13
29
  cacheReadTokens: number;
14
30
  };
31
+ /**
32
+ * Union type that supports both token count formats
33
+ */
34
+ type AnyTokenCounts = TokenCounts | AggregatedTokenCounts;
35
+ /**
36
+ * Calculates the total number of tokens across all token types
37
+ * Supports both raw usage data format and aggregated data format
38
+ * @param tokenCounts - Object containing counts for each token type
39
+ * @returns Total number of tokens
40
+ */
41
+ declare function getTotalTokens(tokenCounts: AnyTokenCounts): number;
42
+ //#endregion
43
+ //#region src/calculate-cost.d.ts
44
+ /**
45
+ * Alias for AggregatedTokenCounts from shared utilities
46
+ * @deprecated Use AggregatedTokenCounts from _token-utils.ts instead
47
+ */
48
+ type TokenData = AggregatedTokenCounts;
15
49
  /**
16
50
  * Token totals including cost information
17
51
  */
@@ -30,12 +64,8 @@ type TotalsObject = TokenTotals & {
30
64
  * @returns Aggregated token totals and cost
31
65
  */
32
66
  declare function calculateTotals(data: Array<DailyUsage | MonthlyUsage | SessionUsage>): TokenTotals;
33
- /**
34
- * Calculates the sum of all token types (input, output, cache creation, cache read)
35
- * @param tokens - Token data containing different token counts
36
- * @returns Total number of tokens across all types
37
- */
38
- declare function getTotalTokens(tokens: TokenData): number;
67
+ // Re-export getTotalTokens from shared utilities for backward compatibility
68
+
39
69
  /**
40
70
  * Creates a complete totals object by adding total token count to existing totals
41
71
  * @param totals - Token totals with cost information
@@ -1,3 +1,4 @@
1
- import "./_types-CmSE0O0q.js";
2
- import { calculateTotals, createTotalsObject, getTotalTokens } from "./calculate-cost-B0RYn0Vm.js";
1
+ import { getTotalTokens } from "./_token-utils-WjkbrjKv.js";
2
+ import "./_types-BHFM59hI.js";
3
+ import { calculateTotals, createTotalsObject } from "./calculate-cost-BDqO4yWA.js";
3
4
  export { calculateTotals, createTotalsObject, getTotalTokens };
@@ -1,6 +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, require_usingCtx } from "./pricing-fetcher-fT0o6CKK.js";
2
- import { activityDateSchema, arrayType, createDailyDate, createMonthlyDate, createProjectPath, createSessionId, dailyDateSchema, isoTimestampSchema, messageIdSchema, modelNameSchema, monthlyDateSchema, numberType, objectType, projectPathSchema, requestIdSchema, sessionIdSchema, versionSchema } from "./_types-CmSE0O0q.js";
3
- import { logger } from "./logger-CeR-gFvq.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-DpoTR8Md.js";
2
+ import { getTotalTokens } from "./_token-utils-WjkbrjKv.js";
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-D7tlrIfv.js";
4
5
  import a, { readFile } from "node:fs/promises";
5
6
  import path, { posix } from "node:path";
6
7
  import process$1 from "node:process";
@@ -240,6 +241,35 @@ function isObject$1(x) {
240
241
  */ function unreachable(...args) {
241
242
  throw new UnreachableError(args);
242
243
  }
244
+ const isResult = (result) => "object" == typeof result && null !== result && "type" in result && ("Success" === result.type && "value" in result || "Failure" === result.type && "error" in result);
245
+ const unwrap = (...args) => {
246
+ const firstArgument = args[0];
247
+ if (isResult(firstArgument) || isPromise(firstArgument)) {
248
+ const result = firstArgument;
249
+ const hasDefault$1 = 2 === args.length;
250
+ const defaultValue$1 = hasDefault$1 ? args[1] : void 0;
251
+ const apply = (r) => {
252
+ if (isFailure(r)) {
253
+ if (hasDefault$1) return defaultValue$1;
254
+ throw new Error(String(r.error));
255
+ }
256
+ return r.value;
257
+ };
258
+ return isPromise(result) ? result.then(apply) : apply(result);
259
+ }
260
+ const hasDefault = 1 === args.length;
261
+ const defaultValue = hasDefault ? args[0] : void 0;
262
+ return (result) => {
263
+ const apply = (r) => {
264
+ if (isFailure(r)) {
265
+ if (hasDefault) return defaultValue;
266
+ throw new Error(String(r.error));
267
+ }
268
+ return r.value;
269
+ };
270
+ return isPromise(result) ? result.then(apply) : apply(result);
271
+ };
272
+ };
243
273
  function groupBy(arr, getKeyFromItem) {
244
274
  const result = {};
245
275
  for (let i = 0; i < arr.length; i++) {
@@ -3139,12 +3169,14 @@ function createBlock(startTime, entries, now, sessionDurationMs) {
3139
3169
  };
3140
3170
  let costUSD = 0;
3141
3171
  const models = [];
3172
+ let usageLimitResetTime;
3142
3173
  for (const entry of entries) {
3143
3174
  tokenCounts.inputTokens += entry.usage.inputTokens;
3144
3175
  tokenCounts.outputTokens += entry.usage.outputTokens;
3145
3176
  tokenCounts.cacheCreationInputTokens += entry.usage.cacheCreationInputTokens;
3146
3177
  tokenCounts.cacheReadInputTokens += entry.usage.cacheReadInputTokens;
3147
3178
  costUSD += entry.costUSD ?? 0;
3179
+ usageLimitResetTime = entry.usageLimitResetTime ?? usageLimitResetTime;
3148
3180
  models.push(entry.model);
3149
3181
  }
3150
3182
  return {
@@ -3156,7 +3188,8 @@ function createBlock(startTime, entries, now, sessionDurationMs) {
3156
3188
  entries,
3157
3189
  tokenCounts,
3158
3190
  costUSD,
3159
- models: uniq(models)
3191
+ models: uniq(models),
3192
+ usageLimitResetTime
3160
3193
  };
3161
3194
  }
3162
3195
  /**
@@ -3202,11 +3235,14 @@ function calculateBurnRate(block) {
3202
3235
  const lastEntry = lastEntryData.timestamp;
3203
3236
  const durationMinutes = (lastEntry.getTime() - firstEntry.getTime()) / (1e3 * 60);
3204
3237
  if (durationMinutes <= 0) return null;
3205
- const totalTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
3238
+ const totalTokens = getTotalTokens(block.tokenCounts);
3206
3239
  const tokensPerMinute = totalTokens / durationMinutes;
3240
+ const nonCacheTokens = (block.tokenCounts.inputTokens ?? 0) + (block.tokenCounts.outputTokens ?? 0);
3241
+ const tokensPerMinuteForIndicator = nonCacheTokens / durationMinutes;
3207
3242
  const costPerHour = block.costUSD / durationMinutes * 60;
3208
3243
  return {
3209
3244
  tokensPerMinute,
3245
+ tokensPerMinuteForIndicator,
3210
3246
  costPerHour
3211
3247
  };
3212
3248
  }
@@ -3222,7 +3258,7 @@ function projectBlockUsage(block) {
3222
3258
  const now = /* @__PURE__ */ new Date();
3223
3259
  const remainingTime = block.endTime.getTime() - now.getTime();
3224
3260
  const remainingMinutes = Math.max(0, remainingTime / (1e3 * 60));
3225
- const currentTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
3261
+ const currentTokens = getTotalTokens(block.tokenCounts);
3226
3262
  const projectedAdditionalTokens = burnRate.tokensPerMinute * remainingMinutes;
3227
3263
  const totalTokens = currentTokens + projectedAdditionalTokens;
3228
3264
  const projectedAdditionalCost = burnRate.costPerHour / 60 * remainingMinutes;
@@ -3241,7 +3277,7 @@ function projectBlockUsage(block) {
3241
3277
  */
3242
3278
  function filterRecentBlocks(blocks, days = DEFAULT_RECENT_DAYS) {
3243
3279
  const now = /* @__PURE__ */ new Date();
3244
- const cutoffTime = new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
3280
+ const cutoffTime = /* @__PURE__ */ new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
3245
3281
  return blocks.filter((block) => {
3246
3282
  return block.startTime >= cutoffTime || block.isActive;
3247
3283
  });
@@ -3304,10 +3340,12 @@ const usageDataSchema = objectType({
3304
3340
  cache_read_input_tokens: numberType().optional()
3305
3341
  }),
3306
3342
  model: modelNameSchema.optional(),
3307
- id: messageIdSchema.optional()
3343
+ id: messageIdSchema.optional(),
3344
+ content: arrayType(objectType({ text: stringType().optional() })).optional()
3308
3345
  }),
3309
3346
  costUSD: numberType().optional(),
3310
- requestId: requestIdSchema.optional()
3347
+ requestId: requestIdSchema.optional(),
3348
+ isApiErrorMessage: booleanType().optional()
3311
3349
  });
3312
3350
  /**
3313
3351
  * Zod schema for model-specific usage breakdown data
@@ -3582,17 +3620,33 @@ async function sortFilesByTimestamp(files) {
3582
3620
  async function calculateCostForEntry(data, mode, fetcher) {
3583
3621
  if (mode === "display") return data.costUSD ?? 0;
3584
3622
  if (mode === "calculate") {
3585
- if (data.message.model != null) return fetcher.calculateCostFromTokens(data.message.usage, data.message.model);
3623
+ if (data.message.model != null) return unwrap(fetcher.calculateCostFromTokens(data.message.usage, data.message.model), 0);
3586
3624
  return 0;
3587
3625
  }
3588
3626
  if (mode === "auto") {
3589
3627
  if (data.costUSD != null) return data.costUSD;
3590
- if (data.message.model != null) return fetcher.calculateCostFromTokens(data.message.usage, data.message.model);
3628
+ if (data.message.model != null) return unwrap(fetcher.calculateCostFromTokens(data.message.usage, data.message.model), 0);
3591
3629
  return 0;
3592
3630
  }
3593
3631
  unreachable(mode);
3594
3632
  }
3595
3633
  /**
3634
+ * Get Claude Code usage limit expiration date
3635
+ * @param data - Usage data entry
3636
+ * @returns Usage limit expiration date
3637
+ */
3638
+ function getUsageLimitResetTime(data) {
3639
+ let resetTime = null;
3640
+ if (data.isApiErrorMessage === true) {
3641
+ const timestampMatch = data.message?.content?.find((c) => c.text != null && c.text.includes("Claude AI usage limit reached"))?.text?.match(/\|(\d+)/) ?? null;
3642
+ if (timestampMatch?.[1] != null) {
3643
+ const resetTimestamp = Number.parseInt(timestampMatch[1]);
3644
+ resetTime = resetTimestamp > 0 ? /* @__PURE__ */ new Date(resetTimestamp * 1e3) : null;
3645
+ }
3646
+ }
3647
+ return resetTime;
3648
+ }
3649
+ /**
3596
3650
  * Loads and aggregates Claude usage data by day
3597
3651
  * Processes all JSONL files in the Claude projects directory and groups usage by date
3598
3652
  * @param options - Optional configuration for loading and filtering data
@@ -3829,6 +3883,7 @@ async function loadSessionBlockData(options) {
3829
3883
  if (isDuplicateEntry(uniqueHash, processedHashes)) continue;
3830
3884
  markAsProcessed(uniqueHash, processedHashes);
3831
3885
  const cost = fetcher != null ? await calculateCostForEntry(data, mode, fetcher) : data.costUSD ?? 0;
3886
+ const usageLimitResetTime = getUsageLimitResetTime(data);
3832
3887
  allEntries.push({
3833
3888
  timestamp: new Date(data.timestamp),
3834
3889
  usage: {
@@ -3839,7 +3894,8 @@ async function loadSessionBlockData(options) {
3839
3894
  },
3840
3895
  costUSD: cost,
3841
3896
  model: data.message.model ?? "unknown",
3842
- version: data.version
3897
+ version: data.version,
3898
+ usageLimitResetTime: usageLimitResetTime ?? void 0
3843
3899
  });
3844
3900
  } catch (error) {
3845
3901
  logger.debug(`Skipping invalid JSON line in 5-hour blocks: ${error instanceof Error ? error.message : String(error)}`);
@@ -3859,4 +3915,4 @@ async function loadSessionBlockData(options) {
3859
3915
  _usingCtx4.d();
3860
3916
  }
3861
3917
  }
3862
- export { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, dailyUsageSchema, filterRecentBlocks, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, glob, identifySessionBlocks, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, projectBlockUsage, sessionUsageSchema, sortFilesByTimestamp, uniq, usageDataSchema };
3918
+ 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 };
@@ -1,4 +1,4 @@
1
- import { CostMode, PricingFetcher, SortOrder } from "./pricing-fetcher-CrV0acwD.js";
1
+ import { CostMode, PricingFetcher, SortOrder } from "./pricing-fetcher-B3SvKOod.js";
2
2
  import { z } from "zod";
3
3
 
4
4
  //#region src/_session-blocks.d.ts
@@ -17,6 +17,7 @@ type LoadedUsageEntry = {
17
17
  costUSD: number | null;
18
18
  model: string;
19
19
  version?: string;
20
+ usageLimitResetTime?: Date; // Claude API usage limit reset time
20
21
  };
21
22
  /**
22
23
  * Aggregated token counts for different token types
@@ -41,6 +42,7 @@ type SessionBlock = {
41
42
  tokenCounts: TokenCounts;
42
43
  costUSD: number;
43
44
  models: string[];
45
+ usageLimitResetTime?: Date; // Claude API usage limit reset time
44
46
  };
45
47
  /**
46
48
  * Represents usage burn rate calculations
@@ -78,6 +80,13 @@ declare const usageDataSchema: z.ZodObject<{
78
80
  }>;
79
81
  model: z.ZodOptional<z.ZodBranded<z.ZodString, "ModelName">>;
80
82
  id: z.ZodOptional<z.ZodBranded<z.ZodString, "MessageId">>;
83
+ content: z.ZodOptional<z.ZodArray<z.ZodObject<{
84
+ text: z.ZodOptional<z.ZodString>;
85
+ }, "strip", z.ZodTypeAny, {
86
+ text?: string | undefined;
87
+ }, {
88
+ text?: string | undefined;
89
+ }>, "many">>;
81
90
  }, "strip", z.ZodTypeAny, {
82
91
  usage: {
83
92
  input_tokens: number;
@@ -87,6 +96,9 @@ declare const usageDataSchema: z.ZodObject<{
87
96
  };
88
97
  model?: (string & z.BRAND<"ModelName">) | undefined;
89
98
  id?: (string & z.BRAND<"MessageId">) | undefined;
99
+ content?: {
100
+ text?: string | undefined;
101
+ }[] | undefined;
90
102
  }, {
91
103
  usage: {
92
104
  input_tokens: number;
@@ -96,9 +108,13 @@ declare const usageDataSchema: z.ZodObject<{
96
108
  };
97
109
  model?: string | undefined;
98
110
  id?: string | undefined;
111
+ content?: {
112
+ text?: string | undefined;
113
+ }[] | undefined;
99
114
  }>;
100
115
  costUSD: z.ZodOptional<z.ZodNumber>;
101
116
  requestId: z.ZodOptional<z.ZodBranded<z.ZodString, "RequestId">>;
117
+ isApiErrorMessage: z.ZodOptional<z.ZodBoolean>;
102
118
  }, "strip", z.ZodTypeAny, {
103
119
  timestamp: string & z.BRAND<"ISOTimestamp">;
104
120
  version?: (string & z.BRAND<"Version">) | undefined;
@@ -111,9 +127,13 @@ declare const usageDataSchema: z.ZodObject<{
111
127
  };
112
128
  model?: (string & z.BRAND<"ModelName">) | undefined;
113
129
  id?: (string & z.BRAND<"MessageId">) | undefined;
130
+ content?: {
131
+ text?: string | undefined;
132
+ }[] | undefined;
114
133
  };
115
134
  costUSD?: number | undefined;
116
135
  requestId?: (string & z.BRAND<"RequestId">) | undefined;
136
+ isApiErrorMessage?: boolean | undefined;
117
137
  }, {
118
138
  timestamp: string;
119
139
  version?: string | undefined;
@@ -126,9 +146,13 @@ declare const usageDataSchema: z.ZodObject<{
126
146
  };
127
147
  model?: string | undefined;
128
148
  id?: string | undefined;
149
+ content?: {
150
+ text?: string | undefined;
151
+ }[] | undefined;
129
152
  };
130
153
  costUSD?: number | undefined;
131
154
  requestId?: string | undefined;
155
+ isApiErrorMessage?: boolean | undefined;
132
156
  }>;
133
157
  /**
134
158
  * Type definition for Claude usage data entries from JSONL files
@@ -416,6 +440,12 @@ declare function sortFilesByTimestamp(files: string[]): Promise<string[]>;
416
440
  * @returns Calculated cost in USD
417
441
  */
418
442
  declare function calculateCostForEntry(data: UsageData, mode: CostMode, fetcher: PricingFetcher): Promise<number>;
443
+ /**
444
+ * Get Claude Code usage limit expiration date
445
+ * @param data - Usage data entry
446
+ * @returns Usage limit expiration date
447
+ */
448
+ declare function getUsageLimitResetTime(data: UsageData): Date | null;
419
449
  /**
420
450
  * Date range filter for limiting usage data by date
421
451
  */
@@ -462,4 +492,4 @@ declare function loadMonthlyUsageData(options?: LoadOptions): Promise<MonthlyUsa
462
492
  */
463
493
  declare function loadSessionBlockData(options?: LoadOptions): Promise<SessionBlock[]>;
464
494
  //#endregion
465
- export { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
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 };
@@ -1,3 +1,3 @@
1
- import { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-BuHgMcpg.js";
2
- import "./pricing-fetcher-CrV0acwD.js";
3
- export { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
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";
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 };
@@ -1,5 +1,6 @@
1
- import "./pricing-fetcher-fT0o6CKK.js";
2
- import "./_types-CmSE0O0q.js";
3
- import { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-CzOPffdg.js";
4
- import "./logger-CeR-gFvq.js";
5
- export { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
1
+ import "./pricing-fetcher-DpoTR8Md.js";
2
+ import "./_token-utils-WjkbrjKv.js";
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-DqK3z1AK.js";
5
+ import "./logger-D7tlrIfv.js";
6
+ export { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, 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, require_usingCtx } from "./pricing-fetcher-fT0o6CKK.js";
2
- import { getClaudePaths, glob, usageDataSchema } from "./data-loader-CzOPffdg.js";
3
- import { logger } from "./logger-CeR-gFvq.js";
1
+ import { CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, PricingFetcher, USAGE_DATA_GLOB_PATTERN, __toESM, isFailure, require_usingCtx, try_ } from "./pricing-fetcher-DpoTR8Md.js";
2
+ import { getClaudePaths, glob, unwrap, usageDataSchema } from "./data-loader-DqK3z1AK.js";
3
+ import { logger } from "./logger-D7tlrIfv.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);
@@ -37,16 +37,21 @@ async function detectMismatches(claudePath) {
37
37
  for (const file of files) {
38
38
  const content = await readFile(file, "utf-8");
39
39
  const lines = content.trim().split("\n").filter((line) => line.length > 0);
40
- for (const line of lines) try {
41
- const parsed = JSON.parse(line);
42
- const result = usageDataSchema.safeParse(parsed);
43
- if (!result.success) continue;
44
- const data = result.data;
40
+ for (const line of lines) {
41
+ const parseParser = try_({
42
+ try: () => JSON.parse(line),
43
+ catch: () => /* @__PURE__ */ new Error("Invalid JSON")
44
+ });
45
+ const parseResult = parseParser();
46
+ if (isFailure(parseResult)) continue;
47
+ const schemaResult = usageDataSchema.safeParse(parseResult.value);
48
+ if (!schemaResult.success) continue;
49
+ const data = schemaResult.data;
45
50
  stats.totalEntries++;
46
51
  if (data.costUSD !== void 0 && data.message.model != null && data.message.model !== "<synthetic>") {
47
52
  stats.entriesWithBoth++;
48
53
  const model = data.message.model;
49
- const calculatedCost = await fetcher.calculateCostFromTokens(data.message.usage, model);
54
+ const calculatedCost = await unwrap(fetcher.calculateCostFromTokens(data.message.usage, model));
50
55
  const difference = Math.abs(data.costUSD - calculatedCost);
51
56
  const percentDiff = data.costUSD > 0 ? difference / data.costUSD * 100 : 0;
52
57
  const modelStat = stats.modelStats.get(model) ?? {
@@ -89,7 +94,7 @@ async function detectMismatches(claudePath) {
89
94
  modelStat.avgPercentDiff = (modelStat.avgPercentDiff * (modelStat.total - 1) + percentDiff) / modelStat.total;
90
95
  stats.modelStats.set(model, modelStat);
91
96
  }
92
- } catch {}
97
+ }
93
98
  }
94
99
  return stats;
95
100
  } catch (_) {
package/dist/debug.js CHANGED
@@ -1,6 +1,7 @@
1
- import "./pricing-fetcher-fT0o6CKK.js";
2
- import "./_types-CmSE0O0q.js";
3
- import "./data-loader-CzOPffdg.js";
4
- import "./logger-CeR-gFvq.js";
5
- import { detectMismatches, printMismatchReport } from "./debug-CCsUo8-n.js";
1
+ import "./pricing-fetcher-DpoTR8Md.js";
2
+ import "./_token-utils-WjkbrjKv.js";
3
+ import "./_types-BHFM59hI.js";
4
+ import "./data-loader-DqK3z1AK.js";
5
+ import "./logger-D7tlrIfv.js";
6
+ import { detectMismatches, printMismatchReport } from "./debug-Cby_QhQQ.js";
6
7
  export { detectMismatches, printMismatchReport };