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 +33 -1
- package/dist/_token-utils-WjkbrjKv.js +12 -0
- package/dist/{_types-CmSE0O0q.js → _types-BHFM59hI.js} +3 -3
- package/dist/{calculate-cost-B0RYn0Vm.js → calculate-cost-BDqO4yWA.js} +2 -9
- package/dist/calculate-cost.d.ts +41 -11
- package/dist/calculate-cost.js +3 -2
- package/dist/{data-loader-CzOPffdg.js → data-loader-R9pMvoQp.js} +66 -13
- package/dist/{data-loader-BuHgMcpg.d.ts → data-loader-a9CiVyT5.d.ts} +32 -2
- package/dist/data-loader.d.ts +3 -3
- package/dist/data-loader.js +6 -5
- package/dist/{debug-CCsUo8-n.js → debug-Cyr1LS0T.js} +15 -10
- package/dist/debug.js +6 -5
- package/dist/index.js +63 -33
- package/dist/{logger-CeR-gFvq.js → logger-DeTONwj8.js} +3 -3
- package/dist/logger.js +1 -1
- package/dist/{mcp-DGCqhgBz.js → mcp-Kff7A2dF.js} +29 -20
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +7 -6
- package/dist/{pricing-fetcher-CrV0acwD.d.ts → pricing-fetcher-B3SvKOod.d.ts} +8 -3
- package/dist/{pricing-fetcher-fT0o6CKK.js → pricing-fetcher-DaK2jizg.js} +264 -158
- package/dist/pricing-fetcher.d.ts +1 -1
- package/dist/pricing-fetcher.js +3 -3
- package/dist/{prompt-j5YimnLx.js → prompt-DsUFNEY7.js} +2 -2
- package/package.json +5 -2
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 ([](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
|
|
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
|
|
33
|
+
export { calculateTotals, createTotalsObject };
|
package/dist/calculate-cost.d.ts
CHANGED
|
@@ -1,17 +1,51 @@
|
|
|
1
|
-
import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-
|
|
2
|
-
import "./pricing-fetcher-
|
|
1
|
+
import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-a9CiVyT5.js";
|
|
2
|
+
import "./pricing-fetcher-B3SvKOod.js";
|
|
3
3
|
|
|
4
|
-
//#region src/
|
|
4
|
+
//#region src/_token-utils.d.ts
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Token
|
|
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
|
|
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
|
-
|
|
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
|
package/dist/calculate-cost.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import
|
|
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-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
|
|
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
|
|
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-
|
|
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 };
|
package/dist/data-loader.d.ts
CHANGED
|
@@ -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-
|
|
2
|
-
import "./pricing-fetcher-
|
|
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 };
|
package/dist/data-loader.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import "./pricing-fetcher-
|
|
2
|
-
import "./
|
|
3
|
-
import
|
|
4
|
-
import "./
|
|
5
|
-
|
|
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-
|
|
2
|
-
import { getClaudePaths, glob, usageDataSchema } from "./data-loader-
|
|
3
|
-
import { logger } from "./logger-
|
|
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)
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
}
|
|
97
|
+
}
|
|
93
98
|
}
|
|
94
99
|
return stats;
|
|
95
100
|
} catch (_) {
|
package/dist/debug.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import "./pricing-fetcher-
|
|
2
|
-
import "./
|
|
3
|
-
import "./
|
|
4
|
-
import "./
|
|
5
|
-
import
|
|
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 };
|