ccusage 0.8.0 → 9.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +41 -6
  2. package/dist/calculate-cost.d.ts +2 -2
  3. package/dist/chunk-BLXvPPr8.js +30 -0
  4. package/dist/{data-loader-Bz6Vemxw.js → data-loader-CotJGlGF.js} +235 -294
  5. package/dist/{data-loader-amTZCtBR.d.ts → data-loader-OGaMjZTD.d.ts} +8 -80
  6. package/dist/data-loader.d.ts +3 -3
  7. package/dist/data-loader.js +5 -5
  8. package/dist/{debug-DQXeZIjf.js → debug-DH_5GWYh.js} +5 -4
  9. package/dist/debug.js +5 -5
  10. package/dist/{dist-DAarI-SJ.js → dist-BZzwBtZs.js} +1 -1
  11. package/dist/{effect-WSjEuzC9-ChJ5OQQf.js → effect-WSjEuzC9-DHMVzzyB.js} +1 -1
  12. package/dist/{esm-Dqsc1zmX.js → esm-BU3FhOe-.js} +1 -1
  13. package/dist/{index-CISmcbXk-x9eVmhGM.js → index-CISmcbXk-CW1Gj6Ab.js} +5 -5
  14. package/dist/index.js +199 -1760
  15. package/dist/{logger-DN-RT9jL.js → logger-BgKOQAEs.js} +3 -3
  16. package/dist/logger.js +1 -1
  17. package/dist/{mcp-DZXbfmbs.js → mcp-BUmEAQZm.js} +42 -50
  18. package/dist/mcp.d.ts +2 -2
  19. package/dist/mcp.js +7 -7
  20. package/dist/pricing-fetcher-1KHPTXC3.js +205 -0
  21. package/dist/{pricing-fetcher-Dq-OLBp4.d.ts → pricing-fetcher-BkSZh4lR.d.ts} +83 -3
  22. package/dist/pricing-fetcher.d.ts +2 -2
  23. package/dist/pricing-fetcher.js +3 -3
  24. package/dist/{sury-DmrZ3_Oj-l0qqtY-f.js → sury-DmrZ3_Oj-Cpjsc2Lm.js} +1 -1
  25. package/dist/utils.table-DRzF8vZJ.js +1838 -0
  26. package/dist/utils.table.d.ts +24 -0
  27. package/dist/utils.table.js +3 -0
  28. package/dist/valibot-CQk-M5rL-BcaCeUrF.js +10 -0
  29. package/dist/{zod-Db63SLXj-N1oN-yiY.js → zod-Db63SLXj-BIXn64AP.js} +3 -3
  30. package/package.json +2 -1
  31. package/dist/pricing-fetcher-DMNBE90M.js +0 -79
  32. package/dist/valibot-CQk-M5rL-BNHzwpA0.js +0 -10
  33. /package/dist/{arktype-C-GObzDh-Bx7Fdrqj.js → arktype-C-GObzDh-CNoBqQrr.js} +0 -0
  34. /package/dist/{core-B0ovMhJe.js → core-DHCbAXJf.js} +0 -0
  35. /package/dist/{dist-BEQ1tJCL.js → dist-DCvt9hEv.js} +0 -0
  36. /package/dist/{prompt-CUbwSrjo.js → prompt-DtZgx4wU.js} +0 -0
  37. /package/dist/{types-DS8M8QF_.js → types-BlyCnKwN.js} +0 -0
@@ -1,39 +1,14 @@
1
- import { array, number, object, optional, pipe, regex, safeParse, string } from "./dist-BEQ1tJCL.js";
2
- import { logger } from "./logger-DN-RT9jL.js";
3
- import { PricingFetcher } from "./pricing-fetcher-DMNBE90M.js";
4
- import { createRequire } from "node:module";
5
- import { readFile } from "node:fs/promises";
1
+ import { __commonJS, __require, __toESM } from "./chunk-BLXvPPr8.js";
2
+ import { array, number, object, optional, pipe, regex, safeParse, string } from "./dist-DCvt9hEv.js";
3
+ import { logger } from "./logger-BgKOQAEs.js";
4
+ import { PricingFetcher } from "./pricing-fetcher-1KHPTXC3.js";
5
+ import fsPromises, { readFile } from "node:fs/promises";
6
6
  import { homedir } from "node:os";
7
7
  import path from "node:path";
8
+ import process$1 from "node:process";
9
+ import fs from "node:fs";
8
10
  import path$1, { posix } from "path";
9
11
 
10
- //#region rolldown:runtime
11
- var __create = Object.create;
12
- var __defProp = Object.defineProperty;
13
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
14
- var __getOwnPropNames = Object.getOwnPropertyNames;
15
- var __getProtoOf = Object.getPrototypeOf;
16
- var __hasOwnProp = Object.prototype.hasOwnProperty;
17
- var __commonJS = (cb, mod) => function() {
18
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
19
- };
20
- var __copyProps = (to, from, except, desc) => {
21
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
22
- key = keys[i];
23
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
24
- get: ((k) => from[k]).bind(null, key),
25
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
26
- });
27
- }
28
- return to;
29
- };
30
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
31
- value: mod,
32
- enumerable: true
33
- }) : target, mod));
34
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
35
-
36
- //#endregion
37
12
  //#region node_modules/@jsr/core__unknownutil/is/string.js
38
13
  /**
39
14
  * Return `true` if the type of `x` is `string`.
@@ -289,6 +264,19 @@ function isObject$1(x) {
289
264
  throw new UnreachableError(args);
290
265
  }
291
266
 
267
+ //#endregion
268
+ //#region node_modules/es-toolkit/dist/array/groupBy.mjs
269
+ function groupBy(arr, getKeyFromItem) {
270
+ const result = {};
271
+ for (let i = 0; i < arr.length; i++) {
272
+ const item = arr[i];
273
+ const key = getKeyFromItem(item);
274
+ if (!Object.hasOwn(result, key)) result[key] = [];
275
+ result[key].push(item);
276
+ }
277
+ return result;
278
+ }
279
+
292
280
  //#endregion
293
281
  //#region node_modules/fast-sort/dist/sort.mjs
294
282
  var castComparer = function(comparer) {
@@ -390,6 +378,34 @@ var inPlaceSort = createNewSortInstance({
390
378
  inPlaceSorting: true
391
379
  });
392
380
 
381
+ //#endregion
382
+ //#region node_modules/path-type/index.js
383
+ async function isType(fsStatType, statsMethodName, filePath) {
384
+ if (typeof filePath !== "string") throw new TypeError(`Expected a string, got ${typeof filePath}`);
385
+ try {
386
+ const stats = await fsPromises[fsStatType](filePath);
387
+ return stats[statsMethodName]();
388
+ } catch (error) {
389
+ if (error.code === "ENOENT") return false;
390
+ throw error;
391
+ }
392
+ }
393
+ function isTypeSync(fsStatType, statsMethodName, filePath) {
394
+ if (typeof filePath !== "string") throw new TypeError(`Expected a string, got ${typeof filePath}`);
395
+ try {
396
+ return fs[fsStatType](filePath)[statsMethodName]();
397
+ } catch (error) {
398
+ if (error.code === "ENOENT") return false;
399
+ throw error;
400
+ }
401
+ }
402
+ const isFile = isType.bind(void 0, "stat", "isFile");
403
+ const isDirectory = isType.bind(void 0, "stat", "isDirectory");
404
+ const isSymlink = isType.bind(void 0, "lstat", "isSymbolicLink");
405
+ const isFileSync = isTypeSync.bind(void 0, "statSync", "isFile");
406
+ const isDirectorySync = isTypeSync.bind(void 0, "statSync", "isDirectory");
407
+ const isSymlinkSync = isTypeSync.bind(void 0, "lstatSync", "isSymbolicLink");
408
+
393
409
  //#endregion
394
410
  //#region node_modules/fdir/dist/utils.js
395
411
  var require_utils$1 = __commonJS({ "node_modules/fdir/dist/utils.js"(exports) {
@@ -2497,7 +2513,7 @@ var require_picomatch$1 = __commonJS({ "node_modules/picomatch/lib/picomatch.js"
2497
2513
  * @api public
2498
2514
  */
2499
2515
  picomatch$2.parse = (pattern, options) => {
2500
- if (Array.isArray(pattern)) return pattern.map((p$1) => picomatch$2.parse(p$1, options));
2516
+ if (Array.isArray(pattern)) return pattern.map((p) => picomatch$2.parse(p, options));
2501
2517
  return parse(pattern, {
2502
2518
  ...options,
2503
2519
  fastpaths: false
@@ -2794,7 +2810,7 @@ var require_dist$1 = __commonJS({ "node_modules/fdir/dist/index.js"(exports) {
2794
2810
  o[k2] = m[k];
2795
2811
  });
2796
2812
  var __exportStar = void 0 && (void 0).__exportStar || function(m, exports$1) {
2797
- for (var p$1 in m) if (p$1 !== "default" && !Object.prototype.hasOwnProperty.call(exports$1, p$1)) __createBinding(exports$1, m, p$1);
2813
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports$1, p)) __createBinding(exports$1, m, p);
2798
2814
  };
2799
2815
  Object.defineProperty(exports, "__esModule", { value: true });
2800
2816
  exports.fdir = void 0;
@@ -2936,9 +2952,9 @@ function processPatterns({ patterns, ignore = [], expandDirectories = true }, cw
2936
2952
  function getRelativePath(path$1$1, cwd, root) {
2937
2953
  return posix.relative(cwd, `${root}/${path$1$1}`) || ".";
2938
2954
  }
2939
- function processPath(path$1$1, cwd, root, isDirectory, absolute) {
2955
+ function processPath(path$1$1, cwd, root, isDirectory$1, absolute) {
2940
2956
  const relativePath = absolute ? path$1$1.slice(root === "/" ? 1 : root.length + 1) || "." : path$1$1;
2941
- if (root === cwd) return isDirectory && relativePath !== "." ? relativePath.slice(0, -1) : relativePath;
2957
+ if (root === cwd) return isDirectory$1 && relativePath !== "." ? relativePath.slice(0, -1) : relativePath;
2942
2958
  return getRelativePath(relativePath, cwd, root);
2943
2959
  }
2944
2960
  function formatPaths(paths, cwd, root) {
@@ -2974,20 +2990,20 @@ function crawl(options, cwd, sync$1) {
2974
2990
  nocase
2975
2991
  });
2976
2992
  const fdirOptions = {
2977
- filters: [options.debug ? (p$1, isDirectory) => {
2978
- const path$1$1 = processPath(p$1, cwd, props.root, isDirectory, options.absolute);
2993
+ filters: [options.debug ? (p, isDirectory$1) => {
2994
+ const path$1$1 = processPath(p, cwd, props.root, isDirectory$1, options.absolute);
2979
2995
  const matches = matcher(path$1$1);
2980
2996
  if (matches) log(`matched ${path$1$1}`);
2981
2997
  return matches;
2982
- } : (p$1, isDirectory) => matcher(processPath(p$1, cwd, props.root, isDirectory, options.absolute))],
2983
- exclude: options.debug ? (_, p$1) => {
2984
- const relativePath = processPath(p$1, cwd, props.root, true, true);
2998
+ } : (p, isDirectory$1) => matcher(processPath(p, cwd, props.root, isDirectory$1, options.absolute))],
2999
+ exclude: options.debug ? (_, p) => {
3000
+ const relativePath = processPath(p, cwd, props.root, true, true);
2985
3001
  const skipped = relativePath !== "." && !partialMatcher(relativePath) || ignore(relativePath);
2986
- if (skipped) log(`skipped ${p$1}`);
2987
- else log(`crawling ${p$1}`);
3002
+ if (skipped) log(`skipped ${p}`);
3003
+ else log(`crawling ${p}`);
2988
3004
  return skipped;
2989
- } : (_, p$1) => {
2990
- const relativePath = processPath(p$1, cwd, props.root, true, true);
3005
+ } : (_, p) => {
3006
+ const relativePath = processPath(p, cwd, props.root, true, true);
2991
3007
  return relativePath !== "." && !partialMatcher(relativePath) || ignore(relativePath);
2992
3008
  },
2993
3009
  pathSeparator: "/",
@@ -3025,122 +3041,6 @@ async function glob(patternsOrOptions, options) {
3025
3041
  return crawl(opts, cwd, false);
3026
3042
  }
3027
3043
 
3028
- //#endregion
3029
- //#region node_modules/picocolors/picocolors.js
3030
- var require_picocolors = __commonJS({ "node_modules/picocolors/picocolors.js"(exports, module) {
3031
- let p = process || {}, argv = p.argv || [], env = p.env || {};
3032
- let isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
3033
- let formatter = (open, close, replace = open) => (input) => {
3034
- let string$1 = "" + input, index = string$1.indexOf(close, open.length);
3035
- return ~index ? open + replaceClose(string$1, close, replace, index) + close : open + string$1 + close;
3036
- };
3037
- let replaceClose = (string$1, close, replace, index) => {
3038
- let result = "", cursor = 0;
3039
- do {
3040
- result += string$1.substring(cursor, index) + replace;
3041
- cursor = index + close.length;
3042
- index = string$1.indexOf(close, cursor);
3043
- } while (~index);
3044
- return result + string$1.substring(cursor);
3045
- };
3046
- let createColors = (enabled = isColorSupported) => {
3047
- let f = enabled ? formatter : () => String;
3048
- return {
3049
- isColorSupported: enabled,
3050
- reset: f("\x1B[0m", "\x1B[0m"),
3051
- bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
3052
- dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
3053
- italic: f("\x1B[3m", "\x1B[23m"),
3054
- underline: f("\x1B[4m", "\x1B[24m"),
3055
- inverse: f("\x1B[7m", "\x1B[27m"),
3056
- hidden: f("\x1B[8m", "\x1B[28m"),
3057
- strikethrough: f("\x1B[9m", "\x1B[29m"),
3058
- black: f("\x1B[30m", "\x1B[39m"),
3059
- red: f("\x1B[31m", "\x1B[39m"),
3060
- green: f("\x1B[32m", "\x1B[39m"),
3061
- yellow: f("\x1B[33m", "\x1B[39m"),
3062
- blue: f("\x1B[34m", "\x1B[39m"),
3063
- magenta: f("\x1B[35m", "\x1B[39m"),
3064
- cyan: f("\x1B[36m", "\x1B[39m"),
3065
- white: f("\x1B[37m", "\x1B[39m"),
3066
- gray: f("\x1B[90m", "\x1B[39m"),
3067
- bgBlack: f("\x1B[40m", "\x1B[49m"),
3068
- bgRed: f("\x1B[41m", "\x1B[49m"),
3069
- bgGreen: f("\x1B[42m", "\x1B[49m"),
3070
- bgYellow: f("\x1B[43m", "\x1B[49m"),
3071
- bgBlue: f("\x1B[44m", "\x1B[49m"),
3072
- bgMagenta: f("\x1B[45m", "\x1B[49m"),
3073
- bgCyan: f("\x1B[46m", "\x1B[49m"),
3074
- bgWhite: f("\x1B[47m", "\x1B[49m"),
3075
- blackBright: f("\x1B[90m", "\x1B[39m"),
3076
- redBright: f("\x1B[91m", "\x1B[39m"),
3077
- greenBright: f("\x1B[92m", "\x1B[39m"),
3078
- yellowBright: f("\x1B[93m", "\x1B[39m"),
3079
- blueBright: f("\x1B[94m", "\x1B[39m"),
3080
- magentaBright: f("\x1B[95m", "\x1B[39m"),
3081
- cyanBright: f("\x1B[96m", "\x1B[39m"),
3082
- whiteBright: f("\x1B[97m", "\x1B[39m"),
3083
- bgBlackBright: f("\x1B[100m", "\x1B[49m"),
3084
- bgRedBright: f("\x1B[101m", "\x1B[49m"),
3085
- bgGreenBright: f("\x1B[102m", "\x1B[49m"),
3086
- bgYellowBright: f("\x1B[103m", "\x1B[49m"),
3087
- bgBlueBright: f("\x1B[104m", "\x1B[49m"),
3088
- bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
3089
- bgCyanBright: f("\x1B[106m", "\x1B[49m"),
3090
- bgWhiteBright: f("\x1B[107m", "\x1B[49m")
3091
- };
3092
- };
3093
- module.exports = createColors();
3094
- module.exports.createColors = createColors;
3095
- } });
3096
-
3097
- //#endregion
3098
- //#region src/utils.internal.ts
3099
- var import_picocolors = __toESM(require_picocolors(), 1);
3100
- function formatNumber(num) {
3101
- return num.toLocaleString("en-US");
3102
- }
3103
- function formatCurrency(amount) {
3104
- return `$${amount.toFixed(2)}`;
3105
- }
3106
- /**
3107
- * WHY: Object.groupBy requires Node.js 21+. tsdown doesn't support runtime polyfills, only syntax transforms.
3108
- */
3109
- function groupBy(array$1, keyFn) {
3110
- return array$1.reduce((groups, item) => {
3111
- const key = keyFn(item);
3112
- if (groups[key] == null) groups[key] = [];
3113
- groups[key].push(item);
3114
- return groups;
3115
- }, {});
3116
- }
3117
- function formatModelName(modelName) {
3118
- const match = modelName.match(/claude-(\w+)-(\d+)-\d+/);
3119
- if (match != null) return `${match[1]}-${match[2]}`;
3120
- return modelName;
3121
- }
3122
- function formatModelsDisplay(models) {
3123
- const uniqueModels = [...new Set(models.map(formatModelName))];
3124
- return uniqueModels.sort().join(", ");
3125
- }
3126
- /**
3127
- * Pushes model breakdown rows to a table
3128
- * @param table - The table to push rows to
3129
- * @param breakdowns - Array of model breakdowns
3130
- * @param extraColumns - Number of extra empty columns before the data (default: 1 for models column)
3131
- * @param trailingColumns - Number of extra empty columns after the data (default: 0)
3132
- */
3133
- function pushBreakdownRows(table, breakdowns, extraColumns = 1, trailingColumns = 0) {
3134
- for (const breakdown of breakdowns) {
3135
- const row = [` └─ ${formatModelName(breakdown.modelName)}`];
3136
- for (let i = 0; i < extraColumns; i++) row.push("");
3137
- const totalTokens = breakdown.inputTokens + breakdown.outputTokens + breakdown.cacheCreationTokens + breakdown.cacheReadTokens;
3138
- row.push(import_picocolors.default.gray(formatNumber(breakdown.inputTokens)), import_picocolors.default.gray(formatNumber(breakdown.outputTokens)), import_picocolors.default.gray(formatNumber(breakdown.cacheCreationTokens)), import_picocolors.default.gray(formatNumber(breakdown.cacheReadTokens)), import_picocolors.default.gray(formatNumber(totalTokens)), import_picocolors.default.gray(formatCurrency(breakdown.cost)));
3139
- for (let i = 0; i < trailingColumns; i++) row.push("");
3140
- table.push(row);
3141
- }
3142
- }
3143
-
3144
3044
  //#endregion
3145
3045
  //#region node_modules/rolldown/node_modules/@oxc-project/runtime/src/helpers/usingCtx.js
3146
3046
  var require_usingCtx = __commonJS({ "node_modules/rolldown/node_modules/@oxc-project/runtime/src/helpers/usingCtx.js"(exports, module) {
@@ -3204,8 +3104,17 @@ var require_usingCtx = __commonJS({ "node_modules/rolldown/node_modules/@oxc-pro
3204
3104
  //#endregion
3205
3105
  //#region src/data-loader.ts
3206
3106
  var import_usingCtx = __toESM(require_usingCtx(), 1);
3107
+ const DEFAULT_CLAUDE_CODE_PATH = path.join(homedir(), ".claude");
3108
+ /**
3109
+ * Default path for Claude data directory
3110
+ * Uses environment variable CLAUDE_CONFIG_DIR if set, otherwise defaults to ~/.claude
3111
+ */
3207
3112
  function getDefaultClaudePath() {
3208
- return path.join(homedir(), ".claude");
3113
+ const envClaudeCodePath = process$1.env.CLAUDE_CONFIG_DIR?.trim() ?? DEFAULT_CLAUDE_CODE_PATH;
3114
+ if (!isDirectorySync(envClaudeCodePath)) throw new Error(` Claude data directory does not exist: ${envClaudeCodePath}.
3115
+ Please set CLAUDE_CONFIG_DIR to a valid path, or ensure ${DEFAULT_CLAUDE_CODE_PATH} exists.
3116
+ `.trim());
3117
+ return envClaudeCodePath;
3209
3118
  }
3210
3119
  const UsageDataSchema = object({
3211
3120
  timestamp: string(),
@@ -3264,6 +3173,123 @@ const MonthlyUsageSchema = object({
3264
3173
  modelsUsed: array(string()),
3265
3174
  modelBreakdowns: array(ModelBreakdownSchema)
3266
3175
  });
3176
+ /**
3177
+ * Aggregates token counts and costs by model name
3178
+ */
3179
+ function aggregateByModel(entries, getModel, getUsage, getCost) {
3180
+ const modelAggregates = /* @__PURE__ */ new Map();
3181
+ const defaultStats = {
3182
+ inputTokens: 0,
3183
+ outputTokens: 0,
3184
+ cacheCreationTokens: 0,
3185
+ cacheReadTokens: 0,
3186
+ cost: 0
3187
+ };
3188
+ for (const entry of entries) {
3189
+ const modelName = getModel(entry) ?? "unknown";
3190
+ if (modelName === "<synthetic>") continue;
3191
+ const usage = getUsage(entry);
3192
+ const cost = getCost(entry);
3193
+ const existing = modelAggregates.get(modelName) ?? defaultStats;
3194
+ modelAggregates.set(modelName, {
3195
+ inputTokens: existing.inputTokens + (usage.input_tokens ?? 0),
3196
+ outputTokens: existing.outputTokens + (usage.output_tokens ?? 0),
3197
+ cacheCreationTokens: existing.cacheCreationTokens + (usage.cache_creation_input_tokens ?? 0),
3198
+ cacheReadTokens: existing.cacheReadTokens + (usage.cache_read_input_tokens ?? 0),
3199
+ cost: existing.cost + cost
3200
+ });
3201
+ }
3202
+ return modelAggregates;
3203
+ }
3204
+ /**
3205
+ * Aggregates model breakdowns from multiple sources
3206
+ */
3207
+ function aggregateModelBreakdowns(breakdowns) {
3208
+ const modelAggregates = /* @__PURE__ */ new Map();
3209
+ const defaultStats = {
3210
+ inputTokens: 0,
3211
+ outputTokens: 0,
3212
+ cacheCreationTokens: 0,
3213
+ cacheReadTokens: 0,
3214
+ cost: 0
3215
+ };
3216
+ for (const breakdown of breakdowns) {
3217
+ if (breakdown.modelName === "<synthetic>") continue;
3218
+ const existing = modelAggregates.get(breakdown.modelName) ?? defaultStats;
3219
+ modelAggregates.set(breakdown.modelName, {
3220
+ inputTokens: existing.inputTokens + breakdown.inputTokens,
3221
+ outputTokens: existing.outputTokens + breakdown.outputTokens,
3222
+ cacheCreationTokens: existing.cacheCreationTokens + breakdown.cacheCreationTokens,
3223
+ cacheReadTokens: existing.cacheReadTokens + breakdown.cacheReadTokens,
3224
+ cost: existing.cost + breakdown.cost
3225
+ });
3226
+ }
3227
+ return modelAggregates;
3228
+ }
3229
+ /**
3230
+ * Converts model aggregates to sorted model breakdowns
3231
+ */
3232
+ function createModelBreakdowns(modelAggregates) {
3233
+ return Array.from(modelAggregates.entries()).map(([modelName, stats]) => ({
3234
+ modelName,
3235
+ ...stats
3236
+ })).sort((a, b) => b.cost - a.cost);
3237
+ }
3238
+ /**
3239
+ * Calculates total token counts and costs from entries
3240
+ */
3241
+ function calculateTotals(entries, getUsage, getCost) {
3242
+ return entries.reduce((acc, entry) => {
3243
+ const usage = getUsage(entry);
3244
+ const cost = getCost(entry);
3245
+ return {
3246
+ inputTokens: acc.inputTokens + (usage.input_tokens ?? 0),
3247
+ outputTokens: acc.outputTokens + (usage.output_tokens ?? 0),
3248
+ cacheCreationTokens: acc.cacheCreationTokens + (usage.cache_creation_input_tokens ?? 0),
3249
+ cacheReadTokens: acc.cacheReadTokens + (usage.cache_read_input_tokens ?? 0),
3250
+ cost: acc.cost + cost,
3251
+ totalCost: acc.totalCost + cost
3252
+ };
3253
+ }, {
3254
+ inputTokens: 0,
3255
+ outputTokens: 0,
3256
+ cacheCreationTokens: 0,
3257
+ cacheReadTokens: 0,
3258
+ cost: 0,
3259
+ totalCost: 0
3260
+ });
3261
+ }
3262
+ /**
3263
+ * Filters items by date range
3264
+ */
3265
+ function filterByDateRange(items, getDate, since, until) {
3266
+ if (since == null && until == null) return items;
3267
+ return items.filter((item) => {
3268
+ const dateStr = getDate(item).substring(0, 10).replace(/-/g, "");
3269
+ if (since != null && dateStr < since) return false;
3270
+ if (until != null && dateStr > until) return false;
3271
+ return true;
3272
+ });
3273
+ }
3274
+ /**
3275
+ * Checks if an entry is a duplicate based on hash
3276
+ */
3277
+ function isDuplicateEntry(uniqueHash, processedHashes) {
3278
+ if (uniqueHash == null) return false;
3279
+ return processedHashes.has(uniqueHash);
3280
+ }
3281
+ /**
3282
+ * Marks an entry as processed
3283
+ */
3284
+ function markAsProcessed(uniqueHash, processedHashes) {
3285
+ if (uniqueHash != null) processedHashes.add(uniqueHash);
3286
+ }
3287
+ /**
3288
+ * Extracts unique models from entries, excluding synthetic model
3289
+ */
3290
+ function extractUniqueModels(entries, getModel) {
3291
+ return [...new Set(entries.map(getModel).filter((m) => m != null && m !== "<synthetic>"))];
3292
+ }
3267
3293
  function formatDate(dateStr) {
3268
3294
  const date = new Date(dateStr);
3269
3295
  const year = date.getFullYear();
@@ -3271,6 +3297,28 @@ function formatDate(dateStr) {
3271
3297
  const day = String(date.getDate()).padStart(2, "0");
3272
3298
  return `${year}-${month}-${day}`;
3273
3299
  }
3300
+ function formatDateCompact(dateStr) {
3301
+ const date = new Date(dateStr);
3302
+ const year = date.getFullYear();
3303
+ const month = String(date.getMonth() + 1).padStart(2, "0");
3304
+ const day = String(date.getDate()).padStart(2, "0");
3305
+ return `${year}\n${month}-${day}`;
3306
+ }
3307
+ /**
3308
+ * Generic function to sort items by date based on sort order
3309
+ * @param items - Array of items to sort
3310
+ * @param getDate - Function to extract date/timestamp from item
3311
+ * @param order - Sort order (asc or desc)
3312
+ * @returns Sorted array
3313
+ */
3314
+ function sortByDate(items, getDate, order = "desc") {
3315
+ const sorted = sort(items);
3316
+ switch (order) {
3317
+ case "desc": return sorted.desc((item) => new Date(getDate(item)).getTime());
3318
+ case "asc": return sorted.asc((item) => new Date(getDate(item)).getTime());
3319
+ default: unreachable(order);
3320
+ }
3321
+ }
3274
3322
  /**
3275
3323
  * Create a unique identifier for deduplication using message ID and request ID
3276
3324
  */
@@ -3350,7 +3398,7 @@ async function loadDailyUsageData(options) {
3350
3398
  if (files.length === 0) return [];
3351
3399
  const sortedFiles = await sortFilesByTimestamp(files);
3352
3400
  const mode = options?.mode ?? "auto";
3353
- const fetcher = _usingCtx$1.u(mode === "display" ? null : new PricingFetcher());
3401
+ const fetcher = _usingCtx$1.u(mode === "display" ? null : new PricingFetcher(options?.offline));
3354
3402
  const processedHashes = /* @__PURE__ */ new Set();
3355
3403
  const allEntries = [];
3356
3404
  for (const file of sortedFiles) {
@@ -3362,8 +3410,8 @@ async function loadDailyUsageData(options) {
3362
3410
  if (!result.success) continue;
3363
3411
  const data = result.output;
3364
3412
  const uniqueHash = createUniqueHash(data);
3365
- if (uniqueHash != null && processedHashes.has(uniqueHash)) continue;
3366
- if (uniqueHash != null) processedHashes.add(uniqueHash);
3413
+ if (isDuplicateEntry(uniqueHash, processedHashes)) continue;
3414
+ markAsProcessed(uniqueHash, processedHashes);
3367
3415
  const date = formatDate(data.timestamp);
3368
3416
  const cost = fetcher != null ? await calculateCostForEntry(data, mode, fetcher) : data.costUSD ?? 0;
3369
3417
  allEntries.push({
@@ -3377,64 +3425,19 @@ async function loadDailyUsageData(options) {
3377
3425
  const groupedByDate = groupBy(allEntries, (entry) => entry.date);
3378
3426
  const results = Object.entries(groupedByDate).map(([date, entries]) => {
3379
3427
  if (entries == null) return void 0;
3380
- const modelAggregates = /* @__PURE__ */ new Map();
3381
- for (const entry of entries) {
3382
- const modelName = entry.model ?? "unknown";
3383
- if (modelName === "<synthetic>") continue;
3384
- const existing = modelAggregates.get(modelName) ?? {
3385
- inputTokens: 0,
3386
- outputTokens: 0,
3387
- cacheCreationTokens: 0,
3388
- cacheReadTokens: 0,
3389
- cost: 0
3390
- };
3391
- modelAggregates.set(modelName, {
3392
- inputTokens: existing.inputTokens + (entry.data.message.usage.input_tokens ?? 0),
3393
- outputTokens: existing.outputTokens + (entry.data.message.usage.output_tokens ?? 0),
3394
- cacheCreationTokens: existing.cacheCreationTokens + (entry.data.message.usage.cache_creation_input_tokens ?? 0),
3395
- cacheReadTokens: existing.cacheReadTokens + (entry.data.message.usage.cache_read_input_tokens ?? 0),
3396
- cost: existing.cost + entry.cost
3397
- });
3398
- }
3399
- const modelBreakdowns = Array.from(modelAggregates.entries()).map(([modelName, stats]) => ({
3400
- modelName,
3401
- ...stats
3402
- })).sort((a, b) => b.cost - a.cost);
3403
- const totals = entries.reduce((acc, entry) => ({
3404
- inputTokens: acc.inputTokens + (entry.data.message.usage.input_tokens ?? 0),
3405
- outputTokens: acc.outputTokens + (entry.data.message.usage.output_tokens ?? 0),
3406
- cacheCreationTokens: acc.cacheCreationTokens + (entry.data.message.usage.cache_creation_input_tokens ?? 0),
3407
- cacheReadTokens: acc.cacheReadTokens + (entry.data.message.usage.cache_read_input_tokens ?? 0),
3408
- totalCost: acc.totalCost + entry.cost
3409
- }), {
3410
- inputTokens: 0,
3411
- outputTokens: 0,
3412
- cacheCreationTokens: 0,
3413
- cacheReadTokens: 0,
3414
- totalCost: 0
3415
- });
3416
- const modelsUsed = [...new Set(entries.map((e) => e.model).filter((m) => m != null && m !== "<synthetic>"))];
3428
+ const modelAggregates = aggregateByModel(entries, (entry) => entry.model, (entry) => entry.data.message.usage, (entry) => entry.cost);
3429
+ const modelBreakdowns = createModelBreakdowns(modelAggregates);
3430
+ const totals = calculateTotals(entries, (entry) => entry.data.message.usage, (entry) => entry.cost);
3431
+ const modelsUsed = extractUniqueModels(entries, (e) => e.model);
3417
3432
  return {
3418
3433
  date,
3419
3434
  ...totals,
3420
3435
  modelsUsed,
3421
3436
  modelBreakdowns
3422
3437
  };
3423
- }).filter((item) => item != null).filter((item) => {
3424
- if (options?.since != null || options?.until != null) {
3425
- const dateStr = item.date.replace(/-/g, "");
3426
- if (options.since != null && dateStr < options.since) return false;
3427
- if (options.until != null && dateStr > options.until) return false;
3428
- }
3429
- return true;
3430
- });
3431
- const sortOrder = options?.order ?? "desc";
3432
- const sortedResults = sort(results);
3433
- switch (sortOrder) {
3434
- case "desc": return sortedResults.desc((item) => new Date(item.date).getTime());
3435
- case "asc": return sortedResults.asc((item) => new Date(item.date).getTime());
3436
- default: unreachable(sortOrder);
3437
- }
3438
+ }).filter((item) => item != null);
3439
+ const filtered = filterByDateRange(results, (item) => item.date, options?.since, options?.until);
3440
+ return sortByDate(filtered, (item) => item.date, options?.order);
3438
3441
  } catch (_) {
3439
3442
  _usingCtx$1.e = _;
3440
3443
  } finally {
@@ -3453,7 +3456,7 @@ async function loadSessionData(options) {
3453
3456
  if (files.length === 0) return [];
3454
3457
  const sortedFiles = await sortFilesByTimestamp(files);
3455
3458
  const mode = options?.mode ?? "auto";
3456
- const fetcher = _usingCtx3.u(mode === "display" ? null : new PricingFetcher());
3459
+ const fetcher = _usingCtx3.u(mode === "display" ? null : new PricingFetcher(options?.offline));
3457
3460
  const processedHashes = /* @__PURE__ */ new Set();
3458
3461
  const allEntries = [];
3459
3462
  for (const file of sortedFiles) {
@@ -3470,8 +3473,8 @@ async function loadSessionData(options) {
3470
3473
  if (!result.success) continue;
3471
3474
  const data = result.output;
3472
3475
  const uniqueHash = createUniqueHash(data);
3473
- if (uniqueHash != null && processedHashes.has(uniqueHash)) continue;
3474
- if (uniqueHash != null) processedHashes.add(uniqueHash);
3476
+ if (isDuplicateEntry(uniqueHash, processedHashes)) continue;
3477
+ markAsProcessed(uniqueHash, processedHashes);
3475
3478
  const sessionKey = `${projectPath}/${sessionId}`;
3476
3479
  const cost = fetcher != null ? await calculateCostForEntry(data, mode, fetcher) : data.costUSD ?? 0;
3477
3480
  allEntries.push({
@@ -3491,43 +3494,10 @@ async function loadSessionData(options) {
3491
3494
  const latestEntry = entries.reduce((latest, current) => current.timestamp > latest.timestamp ? current : latest);
3492
3495
  const versionSet = /* @__PURE__ */ new Set();
3493
3496
  for (const entry of entries) if (entry.data.version != null) versionSet.add(entry.data.version);
3494
- const modelAggregates = /* @__PURE__ */ new Map();
3495
- for (const entry of entries) {
3496
- const modelName = entry.model ?? "unknown";
3497
- if (modelName === "<synthetic>") continue;
3498
- const existing = modelAggregates.get(modelName) ?? {
3499
- inputTokens: 0,
3500
- outputTokens: 0,
3501
- cacheCreationTokens: 0,
3502
- cacheReadTokens: 0,
3503
- cost: 0
3504
- };
3505
- modelAggregates.set(modelName, {
3506
- inputTokens: existing.inputTokens + (entry.data.message.usage.input_tokens ?? 0),
3507
- outputTokens: existing.outputTokens + (entry.data.message.usage.output_tokens ?? 0),
3508
- cacheCreationTokens: existing.cacheCreationTokens + (entry.data.message.usage.cache_creation_input_tokens ?? 0),
3509
- cacheReadTokens: existing.cacheReadTokens + (entry.data.message.usage.cache_read_input_tokens ?? 0),
3510
- cost: existing.cost + entry.cost
3511
- });
3512
- }
3513
- const modelBreakdowns = Array.from(modelAggregates.entries()).map(([modelName, stats]) => ({
3514
- modelName,
3515
- ...stats
3516
- })).sort((a, b) => b.cost - a.cost);
3517
- const totals = entries.reduce((acc, entry) => ({
3518
- inputTokens: acc.inputTokens + (entry.data.message.usage.input_tokens ?? 0),
3519
- outputTokens: acc.outputTokens + (entry.data.message.usage.output_tokens ?? 0),
3520
- cacheCreationTokens: acc.cacheCreationTokens + (entry.data.message.usage.cache_creation_input_tokens ?? 0),
3521
- cacheReadTokens: acc.cacheReadTokens + (entry.data.message.usage.cache_read_input_tokens ?? 0),
3522
- totalCost: acc.totalCost + entry.cost
3523
- }), {
3524
- inputTokens: 0,
3525
- outputTokens: 0,
3526
- cacheCreationTokens: 0,
3527
- cacheReadTokens: 0,
3528
- totalCost: 0
3529
- });
3530
- const modelsUsed = [...new Set(entries.map((e) => e.model).filter((m) => m != null && m !== "<synthetic>"))];
3497
+ const modelAggregates = aggregateByModel(entries, (entry) => entry.model, (entry) => entry.data.message.usage, (entry) => entry.cost);
3498
+ const modelBreakdowns = createModelBreakdowns(modelAggregates);
3499
+ const totals = calculateTotals(entries, (entry) => entry.data.message.usage, (entry) => entry.cost);
3500
+ const modelsUsed = extractUniqueModels(entries, (e) => e.model);
3531
3501
  return {
3532
3502
  sessionId: latestEntry.sessionId,
3533
3503
  projectPath: latestEntry.projectPath,
@@ -3537,17 +3507,9 @@ async function loadSessionData(options) {
3537
3507
  modelsUsed,
3538
3508
  modelBreakdowns
3539
3509
  };
3540
- }).filter((item) => item != null).filter((item) => {
3541
- if (options?.since != null || options?.until != null) {
3542
- const dateStr = item.lastActivity.replace(/-/g, "");
3543
- if (options.since != null && dateStr < options.since) return false;
3544
- if (options.until != null && dateStr > options.until) return false;
3545
- }
3546
- return true;
3547
- });
3548
- const sortOrder = options?.order ?? "desc";
3549
- const sortedResults = sort(results);
3550
- return sortOrder === "desc" ? sortedResults.desc((item) => new Date(item.lastActivity).getTime()) : sortedResults.asc((item) => new Date(item.lastActivity).getTime());
3510
+ }).filter((item) => item != null);
3511
+ const filtered = filterByDateRange(results, (item) => item.lastActivity, options?.since, options?.until);
3512
+ return sortByDate(filtered, (item) => item.lastActivity, options?.order);
3551
3513
  } catch (_) {
3552
3514
  _usingCtx3.e = _;
3553
3515
  } finally {
@@ -3560,28 +3522,9 @@ async function loadMonthlyUsageData(options) {
3560
3522
  const monthlyArray = [];
3561
3523
  for (const [month, dailyEntries] of Object.entries(groupedByMonth)) {
3562
3524
  if (dailyEntries == null) continue;
3563
- const modelAggregates = /* @__PURE__ */ new Map();
3564
- for (const daily of dailyEntries) for (const breakdown of daily.modelBreakdowns) {
3565
- if (breakdown.modelName === "<synthetic>") continue;
3566
- const existing = modelAggregates.get(breakdown.modelName) ?? {
3567
- inputTokens: 0,
3568
- outputTokens: 0,
3569
- cacheCreationTokens: 0,
3570
- cacheReadTokens: 0,
3571
- cost: 0
3572
- };
3573
- modelAggregates.set(breakdown.modelName, {
3574
- inputTokens: existing.inputTokens + breakdown.inputTokens,
3575
- outputTokens: existing.outputTokens + breakdown.outputTokens,
3576
- cacheCreationTokens: existing.cacheCreationTokens + breakdown.cacheCreationTokens,
3577
- cacheReadTokens: existing.cacheReadTokens + breakdown.cacheReadTokens,
3578
- cost: existing.cost + breakdown.cost
3579
- });
3580
- }
3581
- const modelBreakdowns = Array.from(modelAggregates.entries()).map(([modelName, stats]) => ({
3582
- modelName,
3583
- ...stats
3584
- })).sort((a, b) => b.cost - a.cost);
3525
+ const allBreakdowns = dailyEntries.flatMap((daily) => daily.modelBreakdowns);
3526
+ const modelAggregates = aggregateModelBreakdowns(allBreakdowns);
3527
+ const modelBreakdowns = createModelBreakdowns(modelAggregates);
3585
3528
  const modelsSet = /* @__PURE__ */ new Set();
3586
3529
  for (const data of dailyEntries) for (const model of data.modelsUsed) if (model !== "<synthetic>") modelsSet.add(model);
3587
3530
  let totalInputTokens = 0;
@@ -3608,10 +3551,8 @@ async function loadMonthlyUsageData(options) {
3608
3551
  };
3609
3552
  monthlyArray.push(monthlyUsage);
3610
3553
  }
3611
- const sortOrder = options?.order ?? "desc";
3612
- const sortedMonthly = sort(monthlyArray);
3613
- return sortOrder === "desc" ? sortedMonthly.desc((item) => item.month) : sortedMonthly.asc((item) => item.month);
3554
+ return sortByDate(monthlyArray, (item) => `${item.month}-01`, options?.order);
3614
3555
  }
3615
3556
 
3616
3557
  //#endregion
3617
- export { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, __commonJS, __require, __toESM, calculateCostForEntry, createUniqueHash, formatCurrency, formatDate, formatModelsDisplay, formatNumber, getDefaultClaudePath, getEarliestTimestamp, glob, loadDailyUsageData, loadMonthlyUsageData, loadSessionData, pushBreakdownRows, require_picocolors, require_usingCtx, sortFilesByTimestamp };
3558
+ export { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, glob, loadDailyUsageData, loadMonthlyUsageData, loadSessionData, require_usingCtx, sortFilesByTimestamp };