ccusage 15.5.1 → 15.6.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 +11 -0
- package/dist/{_types-BHFM59hI.js → _types-ed8-0BH6.js} +14 -8
- package/dist/calculate-cost.d.ts +3 -3
- package/dist/calculate-cost.js +1 -1
- package/dist/{data-loader-BAp4u-7e.js → data-loader-CwryIrwx.js} +125 -42
- package/dist/{data-loader-CgvyDaQD.d.ts → data-loader-DZMUuqrf.d.ts} +162 -5
- package/dist/data-loader.d.ts +3 -3
- package/dist/data-loader.js +5 -5
- package/dist/{debug-IQBgd8CJ.js → debug-C8bVFK5q.js} +6 -6
- package/dist/debug.js +5 -5
- package/dist/index.js +315 -135
- package/dist/{logger-COLgmk2z.js → logger-C35JCduT.js} +15 -15
- package/dist/logger.js +1 -1
- package/dist/{mcp-Ta2UK9sc.js → mcp-CqzLeEbX.js} +51 -35
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +5 -5
- package/dist/{pricing-fetcher-B3SvKOod.d.ts → pricing-fetcher-AYfCy7g-.d.ts} +6 -2
- package/dist/{pricing-fetcher-eNV76BjF.js → pricing-fetcher-CNjC0nXU.js} +18 -18
- package/dist/pricing-fetcher.d.ts +1 -1
- package/dist/pricing-fetcher.js +3 -3
- package/dist/{prompt-DsUFNEY7.js → prompt-lm8M58zJ.js} +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
<a href="https://packagephobia.com/result?p=ccusage"><img src="https://packagephobia.com/badge?p=ccusage" alt="install size" /></a>
|
|
10
10
|
<a href="https://deepwiki.com/ryoppippi/ccusage"><img src="https://img.shields.io/badge/DeepWiki-ryoppippi%2Fccusage-blue.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAyCAYAAAAnWDnqAAAAAXNSR0IArs4c6QAAA05JREFUaEPtmUtyEzEQhtWTQyQLHNak2AB7ZnyXZMEjXMGeK/AIi+QuHrMnbChYY7MIh8g01fJoopFb0uhhEqqcbWTp06/uv1saEDv4O3n3dV60RfP947Mm9/SQc0ICFQgzfc4CYZoTPAswgSJCCUJUnAAoRHOAUOcATwbmVLWdGoH//PB8mnKqScAhsD0kYP3j/Yt5LPQe2KvcXmGvRHcDnpxfL2zOYJ1mFwrryWTz0advv1Ut4CJgf5uhDuDj5eUcAUoahrdY/56ebRWeraTjMt/00Sh3UDtjgHtQNHwcRGOC98BJEAEymycmYcWwOprTgcB6VZ5JK5TAJ+fXGLBm3FDAmn6oPPjR4rKCAoJCal2eAiQp2x0vxTPB3ALO2CRkwmDy5WohzBDwSEFKRwPbknEggCPB/imwrycgxX2NzoMCHhPkDwqYMr9tRcP5qNrMZHkVnOjRMWwLCcr8ohBVb1OMjxLwGCvjTikrsBOiA6fNyCrm8V1rP93iVPpwaE+gO0SsWmPiXB+jikdf6SizrT5qKasx5j8ABbHpFTx+vFXp9EnYQmLx02h1QTTrl6eDqxLnGjporxl3NL3agEvXdT0WmEost648sQOYAeJS9Q7bfUVoMGnjo4AZdUMQku50McDcMWcBPvr0SzbTAFDfvJqwLzgxwATnCgnp4wDl6Aa+Ax283gghmj+vj7feE2KBBRMW3FzOpLOADl0Isb5587h/U4gGvkt5v60Z1VLG8BhYjbzRwyQZemwAd6cCR5/XFWLYZRIMpX39AR0tjaGGiGzLVyhse5C9RKC6ai42ppWPKiBagOvaYk8lO7DajerabOZP46Lby5wKjw1HCRx7p9sVMOWGzb/vA1hwiWc6jm3MvQDTogQkiqIhJV0nBQBTU+3okKCFDy9WwferkHjtxib7t3xIUQtHxnIwtx4mpg26/HfwVNVDb4oI9RHmx5WGelRVlrtiw43zboCLaxv46AZeB3IlTkwouebTr1y2NjSpHz68WNFjHvupy3q8TFn3Hos2IAk4Ju5dCo8B3wP7VPr/FGaKiG+T+v+TQqIrOqMTL1VdWV1DdmcbO8KXBz6esmYWYKPwDL5b5FA1a0hwapHiom0r/cKaoqr+27/XcrS5UwSMbQAAAABJRU5ErkJggg==" alt="DeepWiki"></a>
|
|
11
11
|
<!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->
|
|
12
|
+
<a href="https://claudelog.com/"><img src="https://claudelog.com/img/claude_log_badge.svg" alt="ClaudeLog - A comprehensive knowledge base for Claude." /></a>
|
|
12
13
|
<a href="https://github.com/hesreallyhim/awesome-claude-code"><img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Claude Code" /></a>
|
|
13
14
|
</p>
|
|
14
15
|
|
|
@@ -113,6 +114,16 @@ Check out these [47 Claude Code ProTips from Greg Baugues.](https://www.youtube.
|
|
|
113
114
|
</a>
|
|
114
115
|
</p>
|
|
115
116
|
|
|
117
|
+
## Claude Code Resources
|
|
118
|
+
|
|
119
|
+
[`ClaudeLog`](https://claudelog.com) by [InventorBlack](https://www.reddit.com/user/inventor_black/)
|
|
120
|
+
A comprehensive knowledge base with detailed breakdowns of advanced topics, including:
|
|
121
|
+
|
|
122
|
+
- Advanced [mechanics](https://claudelog.com/mechanics/you-are-the-main-thread/) and [CLAUDE.md best practices](https://claudelog.com/mechanics/claude-md-supremacy).
|
|
123
|
+
- Practical technique guides for [plan mode](https://claudelog.com/mechanics/plan-mode), [ultrathink](https://claudelog.com/faqs/what-is-ultrathink/), and [sub-agents](https://claudelog.com/mechanics/task-agent-tools/).
|
|
124
|
+
- Concepts like [agent-first design](https://claudelog.com/mechanics/agent-first-design/), [agent engineering](https://claudelog.com/mechanics/agent-engineering/), and [humanizing agents](https://claudelog.com/mechanics/humanising-agents/).
|
|
125
|
+
- [Configuration guides](https://claudelog.com/configuration).
|
|
126
|
+
|
|
116
127
|
## Star History
|
|
117
128
|
|
|
118
129
|
<a href="https://www.star-history.com/#ryoppippi/ccusage&Date">
|
|
@@ -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 =
|
|
728
|
+
const dateRegex = 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
|
|
737
|
+
return 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
|
|
745
|
+
return new RegExp(`^${regex}$`);
|
|
746
746
|
}
|
|
747
747
|
function isValidIP(ip, version) {
|
|
748
748
|
if ((version === "v4" || !version) && ipv4Regex.test(ip)) return true;
|
|
@@ -2492,7 +2492,7 @@ var ZodDiscriminatedUnion = class ZodDiscriminatedUnion extends ZodType {
|
|
|
2492
2492
|
* @param params
|
|
2493
2493
|
*/
|
|
2494
2494
|
static create(discriminator, options, params) {
|
|
2495
|
-
const optionsMap =
|
|
2495
|
+
const optionsMap = new Map();
|
|
2496
2496
|
for (const type of options) {
|
|
2497
2497
|
const discriminatorValues = getDiscriminator(type.shape[discriminator]);
|
|
2498
2498
|
if (!discriminatorValues.length) throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`);
|
|
@@ -2730,7 +2730,7 @@ var ZodMap = class extends ZodType {
|
|
|
2730
2730
|
};
|
|
2731
2731
|
});
|
|
2732
2732
|
if (ctx.common.async) {
|
|
2733
|
-
const finalMap =
|
|
2733
|
+
const finalMap = new Map();
|
|
2734
2734
|
return Promise.resolve().then(async () => {
|
|
2735
2735
|
for (const pair of pairs) {
|
|
2736
2736
|
const key = await pair.key;
|
|
@@ -2745,7 +2745,7 @@ var ZodMap = class extends ZodType {
|
|
|
2745
2745
|
};
|
|
2746
2746
|
});
|
|
2747
2747
|
} else {
|
|
2748
|
-
const finalMap =
|
|
2748
|
+
const finalMap = new Map();
|
|
2749
2749
|
for (const pair of pairs) {
|
|
2750
2750
|
const key = pair.key;
|
|
2751
2751
|
const value = pair.value;
|
|
@@ -2808,7 +2808,7 @@ var ZodSet = class ZodSet extends ZodType {
|
|
|
2808
2808
|
}
|
|
2809
2809
|
const valueType = this._def.valueType;
|
|
2810
2810
|
function finalizeSet(elements$1) {
|
|
2811
|
-
const parsedSet =
|
|
2811
|
+
const parsedSet = new Set();
|
|
2812
2812
|
for (const element of elements$1) {
|
|
2813
2813
|
if (element.status === "aborted") return INVALID;
|
|
2814
2814
|
if (element.status === "dirty") status.dirty();
|
|
@@ -3573,13 +3573,19 @@ const isoTimestampSchema = stringType().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d
|
|
|
3573
3573
|
const dailyDateSchema = stringType().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").brand();
|
|
3574
3574
|
const activityDateSchema = stringType().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").brand();
|
|
3575
3575
|
const monthlyDateSchema = stringType().regex(/^\d{4}-\d{2}$/, "Date must be in YYYY-MM format").brand();
|
|
3576
|
+
const weeklyDateSchema = stringType().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").brand();
|
|
3576
3577
|
const filterDateSchema = stringType().regex(/^\d{8}$/, "Date must be in YYYYMMDD format").brand();
|
|
3577
3578
|
const projectPathSchema = stringType().min(1, "Project path cannot be empty").brand();
|
|
3578
3579
|
const versionSchema = stringType().regex(/^\d+\.\d+\.\d+/, "Invalid version format").brand();
|
|
3579
3580
|
const createSessionId = (value) => sessionIdSchema.parse(value);
|
|
3580
3581
|
const createDailyDate = (value) => dailyDateSchema.parse(value);
|
|
3581
3582
|
const createMonthlyDate = (value) => monthlyDateSchema.parse(value);
|
|
3583
|
+
const createWeeklyDate = (value) => weeklyDateSchema.parse(value);
|
|
3582
3584
|
const createProjectPath = (value) => projectPathSchema.parse(value);
|
|
3585
|
+
function createBucket(value) {
|
|
3586
|
+
if (weeklyDateSchema.safeParse(value).success) return createWeeklyDate(value);
|
|
3587
|
+
return createMonthlyDate(value);
|
|
3588
|
+
}
|
|
3583
3589
|
/**
|
|
3584
3590
|
* Available cost calculation modes
|
|
3585
3591
|
* - auto: Use pre-calculated costs when available, otherwise calculate from tokens
|
|
@@ -3604,4 +3610,4 @@ const modelPricingSchema = objectType({
|
|
|
3604
3610
|
cache_creation_input_token_cost: numberType().optional(),
|
|
3605
3611
|
cache_read_input_token_cost: numberType().optional()
|
|
3606
3612
|
});
|
|
3607
|
-
export { CostModes, SortOrders, ZodFirstPartyTypeKind, ZodOptional, ZodType, activityDateSchema, arrayType, booleanType, createDailyDate, createMonthlyDate, createProjectPath, createSessionId, dailyDateSchema, discriminatedUnionType, enumType, filterDateSchema, isoTimestampSchema, literalType, messageIdSchema, modelNameSchema, modelPricingSchema, monthlyDateSchema, numberType, objectType, optionalType, projectPathSchema, recordType, requestIdSchema, sessionIdSchema, stringType, unionType, unknownType, versionSchema };
|
|
3613
|
+
export { CostModes, SortOrders, ZodFirstPartyTypeKind, ZodOptional, ZodType, activityDateSchema, arrayType, booleanType, createBucket, createDailyDate, createMonthlyDate, createProjectPath, createSessionId, createWeeklyDate, dailyDateSchema, discriminatedUnionType, enumType, filterDateSchema, isoTimestampSchema, literalType, messageIdSchema, modelNameSchema, modelPricingSchema, monthlyDateSchema, numberType, objectType, optionalType, projectPathSchema, recordType, requestIdSchema, sessionIdSchema, stringType, unionType, unknownType, versionSchema, weeklyDateSchema };
|
package/dist/calculate-cost.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import "./
|
|
1
|
+
import "./pricing-fetcher-AYfCy7g-.js";
|
|
2
|
+
import { DailyUsage, MonthlyUsage, SessionUsage, WeeklyUsage } from "./data-loader-DZMUuqrf.js";
|
|
3
3
|
|
|
4
4
|
//#region src/_token-utils.d.ts
|
|
5
5
|
|
|
@@ -63,7 +63,7 @@ type TotalsObject = TokenTotals & {
|
|
|
63
63
|
* @param data - Array of daily, monthly, or session usage data
|
|
64
64
|
* @returns Aggregated token totals and cost
|
|
65
65
|
*/
|
|
66
|
-
declare function calculateTotals(data: Array<DailyUsage | MonthlyUsage | SessionUsage>): TokenTotals;
|
|
66
|
+
declare function calculateTotals(data: Array<DailyUsage | MonthlyUsage | WeeklyUsage | SessionUsage>): TokenTotals;
|
|
67
67
|
// Re-export getTotalTokens from shared utilities for backward compatibility
|
|
68
68
|
|
|
69
69
|
/**
|
package/dist/calculate-cost.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { getTotalTokens } from "./_token-utils-WjkbrjKv.js";
|
|
2
|
-
import "./_types-
|
|
2
|
+
import "./_types-ed8-0BH6.js";
|
|
3
3
|
import { calculateTotals, createTotalsObject } from "./calculate-cost-BDqO4yWA.js";
|
|
4
4
|
export { calculateTotals, createTotalsObject, getTotalTokens };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, isFailure, isPromise, require_usingCtx } from "./pricing-fetcher-
|
|
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-CNjC0nXU.js";
|
|
2
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-
|
|
4
|
-
import { logger } from "./logger-
|
|
3
|
+
import { activityDateSchema, arrayType, booleanType, createBucket, createDailyDate, createMonthlyDate, createProjectPath, createSessionId, createWeeklyDate, dailyDateSchema, isoTimestampSchema, messageIdSchema, modelNameSchema, monthlyDateSchema, numberType, objectType, projectPathSchema, requestIdSchema, sessionIdSchema, stringType, unionType, versionSchema, weeklyDateSchema } from "./_types-ed8-0BH6.js";
|
|
4
|
+
import { logger } from "./logger-C35JCduT.js";
|
|
5
5
|
import a, { readFile } from "node:fs/promises";
|
|
6
6
|
import path, { posix } from "node:path";
|
|
7
7
|
import process$1 from "node:process";
|
|
@@ -935,7 +935,7 @@ var require_walker = __commonJSMin((exports) => {
|
|
|
935
935
|
counts: new counter_1.Counter(),
|
|
936
936
|
options,
|
|
937
937
|
queue: new queue_1.Queue((error, state) => this.callbackInvoker(state, error, callback$1)),
|
|
938
|
-
symlinks:
|
|
938
|
+
symlinks: new Map(),
|
|
939
939
|
visited: [""].slice(0, 0),
|
|
940
940
|
controller: new AbortController()
|
|
941
941
|
};
|
|
@@ -2931,7 +2931,7 @@ function isDynamicPattern(pattern, options) {
|
|
|
2931
2931
|
return scan$2.isGlob || scan$2.negated;
|
|
2932
2932
|
}
|
|
2933
2933
|
function log(...tasks) {
|
|
2934
|
-
console.log(`[tinyglobby ${
|
|
2934
|
+
console.log(`[tinyglobby ${new Date().toLocaleTimeString("es")}]`, ...tasks);
|
|
2935
2935
|
}
|
|
2936
2936
|
const PARENT_DIRECTORY = /^(\/?\.\.)+/;
|
|
2937
2937
|
const ESCAPING_BACKSLASHES = /\\(?=[()[\]{}!*+?@|])/g;
|
|
@@ -3118,7 +3118,7 @@ function identifySessionBlocks(entries, sessionDurationHours = DEFAULT_SESSION_D
|
|
|
3118
3118
|
const sortedEntries = [...entries].sort((a$1, b$1) => a$1.timestamp.getTime() - b$1.timestamp.getTime());
|
|
3119
3119
|
let currentBlockStart = null;
|
|
3120
3120
|
let currentBlockEntries = [];
|
|
3121
|
-
const now =
|
|
3121
|
+
const now = new Date();
|
|
3122
3122
|
for (const entry of sortedEntries) {
|
|
3123
3123
|
const entryTime = entry.timestamp;
|
|
3124
3124
|
if (currentBlockStart == null) {
|
|
@@ -3255,7 +3255,7 @@ function projectBlockUsage(block) {
|
|
|
3255
3255
|
if (!block.isActive || (block.isGap ?? false)) return null;
|
|
3256
3256
|
const burnRate = calculateBurnRate(block);
|
|
3257
3257
|
if (burnRate == null) return null;
|
|
3258
|
-
const now =
|
|
3258
|
+
const now = new Date();
|
|
3259
3259
|
const remainingTime = block.endTime.getTime() - now.getTime();
|
|
3260
3260
|
const remainingMinutes = Math.max(0, remainingTime / (1e3 * 60));
|
|
3261
3261
|
const currentTokens = getTotalTokens(block.tokenCounts);
|
|
@@ -3276,21 +3276,22 @@ function projectBlockUsage(block) {
|
|
|
3276
3276
|
* @returns Filtered array of recent or active blocks
|
|
3277
3277
|
*/
|
|
3278
3278
|
function filterRecentBlocks(blocks, days = DEFAULT_RECENT_DAYS) {
|
|
3279
|
-
const now =
|
|
3280
|
-
const cutoffTime =
|
|
3279
|
+
const now = new Date();
|
|
3280
|
+
const cutoffTime = new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
|
|
3281
3281
|
return blocks.filter((block) => {
|
|
3282
3282
|
return block.startTime >= cutoffTime || block.isActive;
|
|
3283
3283
|
});
|
|
3284
3284
|
}
|
|
3285
3285
|
var import_usingCtx = __toESM(require_usingCtx(), 1);
|
|
3286
3286
|
/**
|
|
3287
|
-
* Get
|
|
3288
|
-
*
|
|
3287
|
+
* Get Claude data directories to search for usage data
|
|
3288
|
+
* When CLAUDE_CONFIG_DIR is set: uses only those paths
|
|
3289
|
+
* When not set: uses default paths (~/.config/claude and ~/.claude)
|
|
3289
3290
|
* @returns Array of valid Claude data directory paths
|
|
3290
3291
|
*/
|
|
3291
3292
|
function getClaudePaths() {
|
|
3292
3293
|
const paths = [];
|
|
3293
|
-
const normalizedPaths =
|
|
3294
|
+
const normalizedPaths = new Set();
|
|
3294
3295
|
const envPaths = (process$1.env[CLAUDE_CONFIG_DIR_ENV] ?? "").trim();
|
|
3295
3296
|
if (envPaths !== "") {
|
|
3296
3297
|
const envPathList = envPaths.split(",").map((p) => p.trim()).filter((p) => p !== "");
|
|
@@ -3306,6 +3307,9 @@ function getClaudePaths() {
|
|
|
3306
3307
|
}
|
|
3307
3308
|
}
|
|
3308
3309
|
}
|
|
3310
|
+
if (paths.length > 0) return paths;
|
|
3311
|
+
throw new Error(`No valid Claude data directories found in CLAUDE_CONFIG_DIR. Please ensure the following exists:
|
|
3312
|
+
- ${envPaths}/${CLAUDE_PROJECTS_DIR_NAME}`.trim());
|
|
3309
3313
|
}
|
|
3310
3314
|
const defaultPaths = [DEFAULT_CLAUDE_CONFIG_PATH, path.join(USER_HOME_DIR, DEFAULT_CLAUDE_CODE_PATH)];
|
|
3311
3315
|
for (const defaultPath of defaultPaths) {
|
|
@@ -3416,10 +3420,38 @@ const monthlyUsageSchema = objectType({
|
|
|
3416
3420
|
project: stringType().optional()
|
|
3417
3421
|
});
|
|
3418
3422
|
/**
|
|
3423
|
+
* Zod schema for weekly usage aggregation data
|
|
3424
|
+
*/
|
|
3425
|
+
const weeklyUsageSchema = objectType({
|
|
3426
|
+
week: weeklyDateSchema,
|
|
3427
|
+
inputTokens: numberType(),
|
|
3428
|
+
outputTokens: numberType(),
|
|
3429
|
+
cacheCreationTokens: numberType(),
|
|
3430
|
+
cacheReadTokens: numberType(),
|
|
3431
|
+
totalCost: numberType(),
|
|
3432
|
+
modelsUsed: arrayType(modelNameSchema),
|
|
3433
|
+
modelBreakdowns: arrayType(modelBreakdownSchema),
|
|
3434
|
+
project: stringType().optional()
|
|
3435
|
+
});
|
|
3436
|
+
/**
|
|
3437
|
+
* Zod schema for bucket usage aggregation data
|
|
3438
|
+
*/
|
|
3439
|
+
const bucketUsageSchema = objectType({
|
|
3440
|
+
bucket: unionType([weeklyDateSchema, monthlyDateSchema]),
|
|
3441
|
+
inputTokens: numberType(),
|
|
3442
|
+
outputTokens: numberType(),
|
|
3443
|
+
cacheCreationTokens: numberType(),
|
|
3444
|
+
cacheReadTokens: numberType(),
|
|
3445
|
+
totalCost: numberType(),
|
|
3446
|
+
modelsUsed: arrayType(modelNameSchema),
|
|
3447
|
+
modelBreakdowns: arrayType(modelBreakdownSchema),
|
|
3448
|
+
project: stringType().optional()
|
|
3449
|
+
});
|
|
3450
|
+
/**
|
|
3419
3451
|
* Aggregates token counts and costs by model name
|
|
3420
3452
|
*/
|
|
3421
3453
|
function aggregateByModel(entries, getModel, getUsage, getCost) {
|
|
3422
|
-
const modelAggregates =
|
|
3454
|
+
const modelAggregates = new Map();
|
|
3423
3455
|
const defaultStats = {
|
|
3424
3456
|
inputTokens: 0,
|
|
3425
3457
|
outputTokens: 0,
|
|
@@ -3447,7 +3479,7 @@ function aggregateByModel(entries, getModel, getUsage, getCost) {
|
|
|
3447
3479
|
* Aggregates model breakdowns from multiple sources
|
|
3448
3480
|
*/
|
|
3449
3481
|
function aggregateModelBreakdowns(breakdowns) {
|
|
3450
|
-
const modelAggregates =
|
|
3482
|
+
const modelAggregates = new Map();
|
|
3451
3483
|
const defaultStats = {
|
|
3452
3484
|
inputTokens: 0,
|
|
3453
3485
|
outputTokens: 0,
|
|
@@ -3543,34 +3575,45 @@ function extractUniqueModels(entries, getModel) {
|
|
|
3543
3575
|
return uniq(entries.map(getModel).filter((m$1) => m$1 != null && m$1 !== "<synthetic>"));
|
|
3544
3576
|
}
|
|
3545
3577
|
/**
|
|
3546
|
-
*
|
|
3547
|
-
*
|
|
3548
|
-
* @param twoLine - Whether to format as two lines (true) or single line (false)
|
|
3549
|
-
* @returns Formatted date string
|
|
3578
|
+
* Date formatter using Intl.DateTimeFormat for consistent formatting
|
|
3579
|
+
* Using UTC to avoid timezone issues
|
|
3550
3580
|
*/
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
return twoLine ? `${year}\n${month}-${day}` : `${year}-${month}-${day}`;
|
|
3558
|
-
}
|
|
3581
|
+
const dateFormatter = new Intl.DateTimeFormat("en-CA", {
|
|
3582
|
+
timeZone: "UTC",
|
|
3583
|
+
year: "numeric",
|
|
3584
|
+
month: "2-digit",
|
|
3585
|
+
day: "2-digit"
|
|
3586
|
+
});
|
|
3559
3587
|
/**
|
|
3560
3588
|
* Formats a date string to YYYY-MM-DD format
|
|
3561
3589
|
* @param dateStr - Input date string
|
|
3562
3590
|
* @returns Formatted date string in YYYY-MM-DD format
|
|
3563
3591
|
*/
|
|
3564
3592
|
function formatDate(dateStr) {
|
|
3565
|
-
|
|
3593
|
+
const date = new Date(dateStr);
|
|
3594
|
+
return dateFormatter.format(date);
|
|
3566
3595
|
}
|
|
3567
3596
|
/**
|
|
3597
|
+
* Date parts formatter for extracting year, month, and day separately
|
|
3598
|
+
*/
|
|
3599
|
+
const datePartsFormatter = new Intl.DateTimeFormat("en", {
|
|
3600
|
+
timeZone: "UTC",
|
|
3601
|
+
year: "numeric",
|
|
3602
|
+
month: "2-digit",
|
|
3603
|
+
day: "2-digit"
|
|
3604
|
+
});
|
|
3605
|
+
/**
|
|
3568
3606
|
* Formats a date string to compact format with year on first line and month-day on second
|
|
3569
3607
|
* @param dateStr - Input date string
|
|
3570
3608
|
* @returns Formatted date string with newline separator (YYYY\nMM-DD)
|
|
3571
3609
|
*/
|
|
3572
3610
|
function formatDateCompact(dateStr) {
|
|
3573
|
-
|
|
3611
|
+
const date = new Date(dateStr);
|
|
3612
|
+
const parts = datePartsFormatter.formatToParts(date);
|
|
3613
|
+
const year = parts.find((p) => p.type === "year")?.value ?? "";
|
|
3614
|
+
const month = parts.find((p) => p.type === "month")?.value ?? "";
|
|
3615
|
+
const day = parts.find((p) => p.type === "day")?.value ?? "";
|
|
3616
|
+
return `${year}\n${month}-${day}`;
|
|
3574
3617
|
}
|
|
3575
3618
|
/**
|
|
3576
3619
|
* Generic function to sort items by date based on sort order
|
|
@@ -3672,7 +3715,7 @@ function getUsageLimitResetTime(data) {
|
|
|
3672
3715
|
const timestampMatch = data.message?.content?.find((c) => c.text != null && c.text.includes("Claude AI usage limit reached"))?.text?.match(/\|(\d+)/) ?? null;
|
|
3673
3716
|
if (timestampMatch?.[1] != null) {
|
|
3674
3717
|
const resetTimestamp = Number.parseInt(timestampMatch[1]);
|
|
3675
|
-
resetTime = resetTimestamp > 0 ?
|
|
3718
|
+
resetTime = resetTimestamp > 0 ? new Date(resetTimestamp * 1e3) : null;
|
|
3676
3719
|
}
|
|
3677
3720
|
}
|
|
3678
3721
|
return resetTime;
|
|
@@ -3713,7 +3756,7 @@ async function loadDailyUsageData(options) {
|
|
|
3713
3756
|
const sortedFiles = await sortFilesByTimestamp(projectFilteredFiles);
|
|
3714
3757
|
const mode = options?.mode ?? "auto";
|
|
3715
3758
|
const fetcher = _usingCtx.u(mode === "display" ? null : new PricingFetcher(options?.offline));
|
|
3716
|
-
const processedHashes =
|
|
3759
|
+
const processedHashes = new Set();
|
|
3717
3760
|
const allEntries = [];
|
|
3718
3761
|
for (const file of sortedFiles) {
|
|
3719
3762
|
const content = await readFile(file, "utf-8");
|
|
@@ -3787,7 +3830,7 @@ async function loadSessionData(options) {
|
|
|
3787
3830
|
})));
|
|
3788
3831
|
const mode = options?.mode ?? "auto";
|
|
3789
3832
|
const fetcher = _usingCtx3.u(mode === "display" ? null : new PricingFetcher(options?.offline));
|
|
3790
|
-
const processedHashes =
|
|
3833
|
+
const processedHashes = new Set();
|
|
3791
3834
|
const allEntries = [];
|
|
3792
3835
|
for (const { file, baseDir } of sortedFilesWithBase) {
|
|
3793
3836
|
const relativePath = path.relative(baseDir, file);
|
|
@@ -3854,15 +3897,55 @@ async function loadSessionData(options) {
|
|
|
3854
3897
|
* @returns Array of monthly usage summaries sorted by month
|
|
3855
3898
|
*/
|
|
3856
3899
|
async function loadMonthlyUsageData(options) {
|
|
3900
|
+
return loadBucketUsageData((data) => createMonthlyDate(data.date.substring(0, 7)), options).then((usages) => usages.map(({ bucket,...rest }) => ({
|
|
3901
|
+
month: createMonthlyDate(bucket.toString()),
|
|
3902
|
+
...rest
|
|
3903
|
+
})));
|
|
3904
|
+
}
|
|
3905
|
+
/**
|
|
3906
|
+
* @param date - The date to get the week for
|
|
3907
|
+
* @param startDay - The day to start the week on (0 = Sunday, 1 = Monday, ..., 6 = Saturday)
|
|
3908
|
+
* @returns The date of the first day of the week for the given date
|
|
3909
|
+
*/
|
|
3910
|
+
function getDateWeek(date, startDay) {
|
|
3911
|
+
const d$1 = new Date(date);
|
|
3912
|
+
const day = d$1.getDay();
|
|
3913
|
+
const shift = (day - startDay + 7) % 7;
|
|
3914
|
+
d$1.setDate(d$1.getDate() - shift);
|
|
3915
|
+
return createWeeklyDate(d$1.toISOString().substring(0, 10));
|
|
3916
|
+
}
|
|
3917
|
+
/**
|
|
3918
|
+
* Convert day name to number (0 = Sunday, 1 = Monday, ..., 6 = Saturday)
|
|
3919
|
+
*/
|
|
3920
|
+
function getDayNumber(day) {
|
|
3921
|
+
const dayMap = {
|
|
3922
|
+
sunday: 0,
|
|
3923
|
+
monday: 1,
|
|
3924
|
+
tuesday: 2,
|
|
3925
|
+
wednesday: 3,
|
|
3926
|
+
thursday: 4,
|
|
3927
|
+
friday: 5,
|
|
3928
|
+
saturday: 6
|
|
3929
|
+
};
|
|
3930
|
+
return dayMap[day];
|
|
3931
|
+
}
|
|
3932
|
+
async function loadWeeklyUsageData(options) {
|
|
3933
|
+
const startDay = options?.startOfWeek != null ? getDayNumber(options.startOfWeek) : getDayNumber("sunday");
|
|
3934
|
+
return loadBucketUsageData((data) => getDateWeek(new Date(data.date), startDay), options).then((usages) => usages.map(({ bucket,...rest }) => ({
|
|
3935
|
+
week: createWeeklyDate(bucket.toString()),
|
|
3936
|
+
...rest
|
|
3937
|
+
})));
|
|
3938
|
+
}
|
|
3939
|
+
async function loadBucketUsageData(groupingFn, options) {
|
|
3857
3940
|
const dailyData = await loadDailyUsageData(options);
|
|
3858
3941
|
const needsProjectGrouping = options?.groupByProject === true || options?.project != null;
|
|
3859
|
-
const groupingKey = needsProjectGrouping ? (data) => `${data
|
|
3860
|
-
const
|
|
3861
|
-
const
|
|
3862
|
-
for (const [groupKey, dailyEntries] of Object.entries(
|
|
3942
|
+
const groupingKey = needsProjectGrouping ? (data) => `${groupingFn(data)}\x00${data.project ?? "unknown"}` : (data) => `${groupingFn(data)}`;
|
|
3943
|
+
const grouped = groupBy(dailyData, groupingKey);
|
|
3944
|
+
const buckets = [];
|
|
3945
|
+
for (const [groupKey, dailyEntries] of Object.entries(grouped)) {
|
|
3863
3946
|
if (dailyEntries == null) continue;
|
|
3864
3947
|
const parts = groupKey.split("\0");
|
|
3865
|
-
const
|
|
3948
|
+
const bucket = createBucket(parts[0] ?? groupKey);
|
|
3866
3949
|
const project = parts.length > 1 ? parts[1] : void 0;
|
|
3867
3950
|
const allBreakdowns = dailyEntries.flatMap((daily) => daily.modelBreakdowns);
|
|
3868
3951
|
const modelAggregates = aggregateModelBreakdowns(allBreakdowns);
|
|
@@ -3881,8 +3964,8 @@ async function loadMonthlyUsageData(options) {
|
|
|
3881
3964
|
totalCacheReadTokens += daily.cacheReadTokens;
|
|
3882
3965
|
totalCost += daily.totalCost;
|
|
3883
3966
|
}
|
|
3884
|
-
const
|
|
3885
|
-
|
|
3967
|
+
const bucketUsage = {
|
|
3968
|
+
bucket,
|
|
3886
3969
|
inputTokens: totalInputTokens,
|
|
3887
3970
|
outputTokens: totalOutputTokens,
|
|
3888
3971
|
cacheCreationTokens: totalCacheCreationTokens,
|
|
@@ -3892,9 +3975,9 @@ async function loadMonthlyUsageData(options) {
|
|
|
3892
3975
|
modelBreakdowns,
|
|
3893
3976
|
...project != null && { project }
|
|
3894
3977
|
};
|
|
3895
|
-
|
|
3978
|
+
buckets.push(bucketUsage);
|
|
3896
3979
|
}
|
|
3897
|
-
return sortByDate(
|
|
3980
|
+
return sortByDate(buckets, (item) => item.bucket, options?.order);
|
|
3898
3981
|
}
|
|
3899
3982
|
/**
|
|
3900
3983
|
* Loads usage data and organizes it into session blocks (typically 5-hour billing periods)
|
|
@@ -3920,7 +4003,7 @@ async function loadSessionBlockData(options) {
|
|
|
3920
4003
|
const sortedFiles = await sortFilesByTimestamp(blocksFilteredFiles);
|
|
3921
4004
|
const mode = options?.mode ?? "auto";
|
|
3922
4005
|
const fetcher = _usingCtx4.u(mode === "display" ? null : new PricingFetcher(options?.offline));
|
|
3923
|
-
const processedHashes =
|
|
4006
|
+
const processedHashes = new Set();
|
|
3924
4007
|
const allEntries = [];
|
|
3925
4008
|
for (const file of sortedFiles) {
|
|
3926
4009
|
const content = await readFile(file, "utf-8");
|
|
@@ -3966,4 +4049,4 @@ async function loadSessionBlockData(options) {
|
|
|
3966
4049
|
_usingCtx4.d();
|
|
3967
4050
|
}
|
|
3968
4051
|
}
|
|
3969
|
-
export { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, dailyUsageSchema, extractProjectFromPath, filterRecentBlocks, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, glob, globUsageFiles, identifySessionBlocks, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, projectBlockUsage, sessionUsageSchema, sortFilesByTimestamp, uniq, unwrap, usageDataSchema };
|
|
4052
|
+
export { DEFAULT_SESSION_DURATION_HOURS, bucketUsageSchema, calculateBurnRate, calculateCostForEntry, createUniqueHash, dailyUsageSchema, extractProjectFromPath, filterRecentBlocks, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, glob, globUsageFiles, identifySessionBlocks, loadBucketUsageData, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, loadWeeklyUsageData, modelBreakdownSchema, monthlyUsageSchema, projectBlockUsage, sessionUsageSchema, sortFilesByTimestamp, uniq, unwrap, usageDataSchema, weeklyUsageSchema };
|