ccusage 15.2.0 → 15.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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-DaK2jizg.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-DeTONwj8.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,7 +3235,7 @@ 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;
3207
3240
  const costPerHour = block.costUSD / durationMinutes * 60;
3208
3241
  return {
@@ -3222,7 +3255,7 @@ function projectBlockUsage(block) {
3222
3255
  const now = /* @__PURE__ */ new Date();
3223
3256
  const remainingTime = block.endTime.getTime() - now.getTime();
3224
3257
  const remainingMinutes = Math.max(0, remainingTime / (1e3 * 60));
3225
- const currentTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
3258
+ const currentTokens = getTotalTokens(block.tokenCounts);
3226
3259
  const projectedAdditionalTokens = burnRate.tokensPerMinute * remainingMinutes;
3227
3260
  const totalTokens = currentTokens + projectedAdditionalTokens;
3228
3261
  const projectedAdditionalCost = burnRate.costPerHour / 60 * remainingMinutes;
@@ -3241,7 +3274,7 @@ function projectBlockUsage(block) {
3241
3274
  */
3242
3275
  function filterRecentBlocks(blocks, days = DEFAULT_RECENT_DAYS) {
3243
3276
  const now = /* @__PURE__ */ new Date();
3244
- const cutoffTime = new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
3277
+ const cutoffTime = /* @__PURE__ */ new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
3245
3278
  return blocks.filter((block) => {
3246
3279
  return block.startTime >= cutoffTime || block.isActive;
3247
3280
  });
@@ -3304,10 +3337,12 @@ const usageDataSchema = objectType({
3304
3337
  cache_read_input_tokens: numberType().optional()
3305
3338
  }),
3306
3339
  model: modelNameSchema.optional(),
3307
- id: messageIdSchema.optional()
3340
+ id: messageIdSchema.optional(),
3341
+ content: arrayType(objectType({ text: stringType().optional() })).optional()
3308
3342
  }),
3309
3343
  costUSD: numberType().optional(),
3310
- requestId: requestIdSchema.optional()
3344
+ requestId: requestIdSchema.optional(),
3345
+ isApiErrorMessage: booleanType().optional()
3311
3346
  });
3312
3347
  /**
3313
3348
  * Zod schema for model-specific usage breakdown data
@@ -3582,17 +3617,33 @@ async function sortFilesByTimestamp(files) {
3582
3617
  async function calculateCostForEntry(data, mode, fetcher) {
3583
3618
  if (mode === "display") return data.costUSD ?? 0;
3584
3619
  if (mode === "calculate") {
3585
- if (data.message.model != null) return fetcher.calculateCostFromTokens(data.message.usage, data.message.model);
3620
+ if (data.message.model != null) return unwrap(fetcher.calculateCostFromTokens(data.message.usage, data.message.model), 0);
3586
3621
  return 0;
3587
3622
  }
3588
3623
  if (mode === "auto") {
3589
3624
  if (data.costUSD != null) return data.costUSD;
3590
- if (data.message.model != null) return fetcher.calculateCostFromTokens(data.message.usage, data.message.model);
3625
+ if (data.message.model != null) return unwrap(fetcher.calculateCostFromTokens(data.message.usage, data.message.model), 0);
3591
3626
  return 0;
3592
3627
  }
3593
3628
  unreachable(mode);
3594
3629
  }
3595
3630
  /**
3631
+ * Get Claude Code usage limit expiration date
3632
+ * @param data - Usage data entry
3633
+ * @returns Usage limit expiration date
3634
+ */
3635
+ function getUsageLimitResetTime(data) {
3636
+ let resetTime = null;
3637
+ if (data.isApiErrorMessage === true) {
3638
+ const timestampMatch = data.message?.content?.find((c) => c.text != null && c.text.includes("Claude AI usage limit reached"))?.text?.match(/\|(\d+)/) ?? null;
3639
+ if (timestampMatch?.[1] != null) {
3640
+ const resetTimestamp = Number.parseInt(timestampMatch[1]);
3641
+ resetTime = resetTimestamp > 0 ? /* @__PURE__ */ new Date(resetTimestamp * 1e3) : null;
3642
+ }
3643
+ }
3644
+ return resetTime;
3645
+ }
3646
+ /**
3596
3647
  * Loads and aggregates Claude usage data by day
3597
3648
  * Processes all JSONL files in the Claude projects directory and groups usage by date
3598
3649
  * @param options - Optional configuration for loading and filtering data
@@ -3829,6 +3880,7 @@ async function loadSessionBlockData(options) {
3829
3880
  if (isDuplicateEntry(uniqueHash, processedHashes)) continue;
3830
3881
  markAsProcessed(uniqueHash, processedHashes);
3831
3882
  const cost = fetcher != null ? await calculateCostForEntry(data, mode, fetcher) : data.costUSD ?? 0;
3883
+ const usageLimitResetTime = getUsageLimitResetTime(data);
3832
3884
  allEntries.push({
3833
3885
  timestamp: new Date(data.timestamp),
3834
3886
  usage: {
@@ -3839,7 +3891,8 @@ async function loadSessionBlockData(options) {
3839
3891
  },
3840
3892
  costUSD: cost,
3841
3893
  model: data.message.model ?? "unknown",
3842
- version: data.version
3894
+ version: data.version,
3895
+ usageLimitResetTime: usageLimitResetTime ?? void 0
3843
3896
  });
3844
3897
  } catch (error) {
3845
3898
  logger.debug(`Skipping invalid JSON line in 5-hour blocks: ${error instanceof Error ? error.message : String(error)}`);
@@ -3859,4 +3912,4 @@ async function loadSessionBlockData(options) {
3859
3912
  _usingCtx4.d();
3860
3913
  }
3861
3914
  }
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 };
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 };
@@ -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-DaK2jizg.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-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 };
@@ -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-DaK2jizg.js";
2
+ import { getClaudePaths, glob, unwrap, usageDataSchema } from "./data-loader-R9pMvoQp.js";
3
+ import { logger } from "./logger-DeTONwj8.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-DaK2jizg.js";
2
+ import "./_token-utils-WjkbrjKv.js";
3
+ import "./_types-BHFM59hI.js";
4
+ import "./data-loader-R9pMvoQp.js";
5
+ import "./logger-DeTONwj8.js";
6
+ import { detectMismatches, printMismatchReport } from "./debug-Cyr1LS0T.js";
6
7
  export { detectMismatches, printMismatchReport };