ccusage 15.3.1 → 15.5.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 +6 -0
- package/dist/calculate-cost.d.ts +1 -1
- package/dist/{data-loader-DqK3z1AK.js → data-loader-BVwVxx4T.js} +107 -56
- package/dist/{data-loader-a9CiVyT5.d.ts → data-loader-CgvyDaQD.d.ts} +28 -1
- package/dist/data-loader.d.ts +2 -2
- package/dist/data-loader.js +4 -4
- package/dist/{debug-Cby_QhQQ.js → debug-DtmspKbf.js} +3 -3
- package/dist/debug.js +4 -4
- package/dist/index.js +262 -31
- package/dist/{logger-D7tlrIfv.js → logger-CZzaUNvP.js} +1 -1
- package/dist/logger.js +1 -1
- package/dist/{mcp-CFT0dcvs.js → mcp-DRNUcHZe.js} +9 -10
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +4 -4
- package/dist/{pricing-fetcher-DpoTR8Md.js → pricing-fetcher-B2yEbqCO.js} +25 -11
- package/dist/pricing-fetcher.js +2 -2
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -62,6 +62,11 @@ ccusage blocks --live # Real-time usage dashboard
|
|
|
62
62
|
ccusage daily --since 20250525 --until 20250530
|
|
63
63
|
ccusage daily --json # JSON output
|
|
64
64
|
ccusage daily --breakdown # Per-model cost breakdown
|
|
65
|
+
|
|
66
|
+
# Project analysis
|
|
67
|
+
ccusage daily --instances # Group by project/instance
|
|
68
|
+
ccusage daily --project myproject # Filter to specific project
|
|
69
|
+
ccusage daily --instances --project myproject --json # Combined usage
|
|
65
70
|
```
|
|
66
71
|
|
|
67
72
|
## Features
|
|
@@ -83,6 +88,7 @@ ccusage daily --breakdown # Per-model cost breakdown
|
|
|
83
88
|
- 🔄 **Cache Token Support**: Tracks and displays cache creation and cache read tokens separately
|
|
84
89
|
- 🌐 **Offline Mode**: Use pre-cached pricing data without network connectivity with `--offline` (Claude models only)
|
|
85
90
|
- 🔌 **MCP Integration**: Built-in Model Context Protocol server for integration with other tools
|
|
91
|
+
- 🏗️ **Multi-Instance Support**: Group usage by project with `--instances` flag and filter by specific projects
|
|
86
92
|
- 🚀 **Ultra-Small Bundle**: Unlike other CLI tools, we pay extreme attention to bundle size - incredibly small even without minification!
|
|
87
93
|
|
|
88
94
|
## Documentation
|
package/dist/calculate-cost.d.ts
CHANGED
|
@@ -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-B2yEbqCO.js";
|
|
2
2
|
import { getTotalTokens } from "./_token-utils-WjkbrjKv.js";
|
|
3
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-
|
|
4
|
+
import { logger } from "./logger-CZzaUNvP.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";
|
|
@@ -251,7 +251,7 @@ const unwrap = (...args) => {
|
|
|
251
251
|
const apply = (r) => {
|
|
252
252
|
if (isFailure(r)) {
|
|
253
253
|
if (hasDefault$1) return defaultValue$1;
|
|
254
|
-
throw
|
|
254
|
+
throw r.error;
|
|
255
255
|
}
|
|
256
256
|
return r.value;
|
|
257
257
|
};
|
|
@@ -263,7 +263,7 @@ const unwrap = (...args) => {
|
|
|
263
263
|
const apply = (r) => {
|
|
264
264
|
if (isFailure(r)) {
|
|
265
265
|
if (hasDefault) return defaultValue;
|
|
266
|
-
throw
|
|
266
|
+
throw r.error;
|
|
267
267
|
}
|
|
268
268
|
return r.value;
|
|
269
269
|
};
|
|
@@ -3327,6 +3327,19 @@ function getClaudePaths() {
|
|
|
3327
3327
|
return paths;
|
|
3328
3328
|
}
|
|
3329
3329
|
/**
|
|
3330
|
+
* Extract project name from Claude JSONL file path
|
|
3331
|
+
* @param jsonlPath - Absolute path to JSONL file
|
|
3332
|
+
* @returns Project name extracted from path, or "unknown" if malformed
|
|
3333
|
+
*/
|
|
3334
|
+
function extractProjectFromPath(jsonlPath) {
|
|
3335
|
+
const normalizedPath = jsonlPath.replace(/[/\\]/g, path.sep);
|
|
3336
|
+
const segments = normalizedPath.split(path.sep);
|
|
3337
|
+
const projectsIndex = segments.findIndex((segment) => segment === CLAUDE_PROJECTS_DIR_NAME);
|
|
3338
|
+
if (projectsIndex === -1 || projectsIndex + 1 >= segments.length) return "unknown";
|
|
3339
|
+
const projectName = segments[projectsIndex + 1];
|
|
3340
|
+
return projectName != null && projectName.trim() !== "" ? projectName : "unknown";
|
|
3341
|
+
}
|
|
3342
|
+
/**
|
|
3330
3343
|
* Zod schema for validating Claude usage data from JSONL files
|
|
3331
3344
|
*/
|
|
3332
3345
|
const usageDataSchema = objectType({
|
|
@@ -3369,7 +3382,8 @@ const dailyUsageSchema = objectType({
|
|
|
3369
3382
|
cacheReadTokens: numberType(),
|
|
3370
3383
|
totalCost: numberType(),
|
|
3371
3384
|
modelsUsed: arrayType(modelNameSchema),
|
|
3372
|
-
modelBreakdowns: arrayType(modelBreakdownSchema)
|
|
3385
|
+
modelBreakdowns: arrayType(modelBreakdownSchema),
|
|
3386
|
+
project: stringType().optional()
|
|
3373
3387
|
});
|
|
3374
3388
|
/**
|
|
3375
3389
|
* Zod schema for session-based usage aggregation data
|
|
@@ -3398,7 +3412,8 @@ const monthlyUsageSchema = objectType({
|
|
|
3398
3412
|
cacheReadTokens: numberType(),
|
|
3399
3413
|
totalCost: numberType(),
|
|
3400
3414
|
modelsUsed: arrayType(modelNameSchema),
|
|
3401
|
-
modelBreakdowns: arrayType(modelBreakdownSchema)
|
|
3415
|
+
modelBreakdowns: arrayType(modelBreakdownSchema),
|
|
3416
|
+
project: stringType().optional()
|
|
3402
3417
|
});
|
|
3403
3418
|
/**
|
|
3404
3419
|
* Aggregates token counts and costs by model name
|
|
@@ -3499,6 +3514,16 @@ function filterByDateRange(items, getDate, since, until) {
|
|
|
3499
3514
|
});
|
|
3500
3515
|
}
|
|
3501
3516
|
/**
|
|
3517
|
+
* Filters items by project name
|
|
3518
|
+
*/
|
|
3519
|
+
function filterByProject(items, getProject, projectFilter) {
|
|
3520
|
+
if (projectFilter == null) return items;
|
|
3521
|
+
return items.filter((item) => {
|
|
3522
|
+
const projectName = getProject(item);
|
|
3523
|
+
return projectName === projectFilter;
|
|
3524
|
+
});
|
|
3525
|
+
}
|
|
3526
|
+
/**
|
|
3502
3527
|
* Checks if an entry is a duplicate based on hash
|
|
3503
3528
|
*/
|
|
3504
3529
|
function isDuplicateEntry(uniqueHash, processedHashes) {
|
|
@@ -3518,16 +3543,26 @@ function extractUniqueModels(entries, getModel) {
|
|
|
3518
3543
|
return uniq(entries.map(getModel).filter((m$1) => m$1 != null && m$1 !== "<synthetic>"));
|
|
3519
3544
|
}
|
|
3520
3545
|
/**
|
|
3546
|
+
* Shared method for formatting dates with proper timezone handling
|
|
3547
|
+
* @param dateStr - Input date string
|
|
3548
|
+
* @param twoLine - Whether to format as two lines (true) or single line (false)
|
|
3549
|
+
* @returns Formatted date string
|
|
3550
|
+
*/
|
|
3551
|
+
function formatDateInternal(dateStr, twoLine) {
|
|
3552
|
+
const date = new Date(dateStr);
|
|
3553
|
+
const hasTimezone = /Z|[+-]\d{2}:\d{2}/.test(dateStr);
|
|
3554
|
+
const year = hasTimezone ? date.getUTCFullYear() : date.getFullYear();
|
|
3555
|
+
const month = String(hasTimezone ? date.getUTCMonth() + 1 : date.getMonth() + 1).padStart(2, "0");
|
|
3556
|
+
const day = String(hasTimezone ? date.getUTCDate() : date.getDate()).padStart(2, "0");
|
|
3557
|
+
return twoLine ? `${year}\n${month}-${day}` : `${year}-${month}-${day}`;
|
|
3558
|
+
}
|
|
3559
|
+
/**
|
|
3521
3560
|
* Formats a date string to YYYY-MM-DD format
|
|
3522
3561
|
* @param dateStr - Input date string
|
|
3523
3562
|
* @returns Formatted date string in YYYY-MM-DD format
|
|
3524
3563
|
*/
|
|
3525
3564
|
function formatDate(dateStr) {
|
|
3526
|
-
|
|
3527
|
-
const year = date.getFullYear();
|
|
3528
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
3529
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
3530
|
-
return `${year}-${month}-${day}`;
|
|
3565
|
+
return formatDateInternal(dateStr, false);
|
|
3531
3566
|
}
|
|
3532
3567
|
/**
|
|
3533
3568
|
* Formats a date string to compact format with year on first line and month-day on second
|
|
@@ -3535,11 +3570,7 @@ function formatDate(dateStr) {
|
|
|
3535
3570
|
* @returns Formatted date string with newline separator (YYYY\nMM-DD)
|
|
3536
3571
|
*/
|
|
3537
3572
|
function formatDateCompact(dateStr) {
|
|
3538
|
-
|
|
3539
|
-
const year = date.getFullYear();
|
|
3540
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
3541
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
3542
|
-
return `${year}\n${month}-${day}`;
|
|
3573
|
+
return formatDateInternal(dateStr, true);
|
|
3543
3574
|
}
|
|
3544
3575
|
/**
|
|
3545
3576
|
* Generic function to sort items by date based on sort order
|
|
@@ -3647,6 +3678,25 @@ function getUsageLimitResetTime(data) {
|
|
|
3647
3678
|
return resetTime;
|
|
3648
3679
|
}
|
|
3649
3680
|
/**
|
|
3681
|
+
* Glob files from multiple Claude paths in parallel
|
|
3682
|
+
* @param claudePaths - Array of Claude base paths
|
|
3683
|
+
* @returns Array of file paths with their base directories
|
|
3684
|
+
*/
|
|
3685
|
+
async function globUsageFiles(claudePaths) {
|
|
3686
|
+
const filePromises = claudePaths.map(async (claudePath) => {
|
|
3687
|
+
const claudeDir = path.join(claudePath, CLAUDE_PROJECTS_DIR_NAME);
|
|
3688
|
+
const files = await glob([USAGE_DATA_GLOB_PATTERN], {
|
|
3689
|
+
cwd: claudeDir,
|
|
3690
|
+
absolute: true
|
|
3691
|
+
}).catch(() => []);
|
|
3692
|
+
return files.map((file) => ({
|
|
3693
|
+
file,
|
|
3694
|
+
baseDir: claudeDir
|
|
3695
|
+
}));
|
|
3696
|
+
});
|
|
3697
|
+
return (await Promise.all(filePromises)).flat();
|
|
3698
|
+
}
|
|
3699
|
+
/**
|
|
3650
3700
|
* Loads and aggregates Claude usage data by day
|
|
3651
3701
|
* Processes all JSONL files in the Claude projects directory and groups usage by date
|
|
3652
3702
|
* @param options - Optional configuration for loading and filtering data
|
|
@@ -3656,17 +3706,11 @@ async function loadDailyUsageData(options) {
|
|
|
3656
3706
|
try {
|
|
3657
3707
|
var _usingCtx = (0, import_usingCtx.default)();
|
|
3658
3708
|
const claudePaths = toArray(options?.claudePath ?? getClaudePaths());
|
|
3659
|
-
const allFiles =
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
absolute: true
|
|
3665
|
-
});
|
|
3666
|
-
allFiles.push(...files);
|
|
3667
|
-
}
|
|
3668
|
-
if (allFiles.length === 0) return [];
|
|
3669
|
-
const sortedFiles = await sortFilesByTimestamp(allFiles);
|
|
3709
|
+
const allFiles = await globUsageFiles(claudePaths);
|
|
3710
|
+
const fileList = allFiles.map((f$1) => f$1.file);
|
|
3711
|
+
if (fileList.length === 0) return [];
|
|
3712
|
+
const projectFilteredFiles = filterByProject(fileList, (filePath) => extractProjectFromPath(filePath), options?.project);
|
|
3713
|
+
const sortedFiles = await sortFilesByTimestamp(projectFilteredFiles);
|
|
3670
3714
|
const mode = options?.mode ?? "auto";
|
|
3671
3715
|
const fetcher = _usingCtx.u(mode === "display" ? null : new PricingFetcher(options?.offline));
|
|
3672
3716
|
const processedHashes = /* @__PURE__ */ new Set();
|
|
@@ -3684,17 +3728,24 @@ async function loadDailyUsageData(options) {
|
|
|
3684
3728
|
markAsProcessed(uniqueHash, processedHashes);
|
|
3685
3729
|
const date = formatDate(data.timestamp);
|
|
3686
3730
|
const cost = fetcher != null ? await calculateCostForEntry(data, mode, fetcher) : data.costUSD ?? 0;
|
|
3731
|
+
const project = extractProjectFromPath(file);
|
|
3687
3732
|
allEntries.push({
|
|
3688
3733
|
data,
|
|
3689
3734
|
date,
|
|
3690
3735
|
cost,
|
|
3691
|
-
model: data.message.model
|
|
3736
|
+
model: data.message.model,
|
|
3737
|
+
project
|
|
3692
3738
|
});
|
|
3693
3739
|
} catch {}
|
|
3694
3740
|
}
|
|
3695
|
-
const
|
|
3696
|
-
const
|
|
3741
|
+
const needsProjectGrouping = options?.groupByProject === true || options?.project != null;
|
|
3742
|
+
const groupingKey = needsProjectGrouping ? (entry) => `${entry.date}\x00${entry.project}` : (entry) => entry.date;
|
|
3743
|
+
const groupedData = groupBy(allEntries, groupingKey);
|
|
3744
|
+
const results = Object.entries(groupedData).map(([groupKey, entries]) => {
|
|
3697
3745
|
if (entries == null) return void 0;
|
|
3746
|
+
const parts = groupKey.split("\0");
|
|
3747
|
+
const date = parts[0] ?? groupKey;
|
|
3748
|
+
const project = parts.length > 1 ? parts[1] : void 0;
|
|
3698
3749
|
const modelAggregates = aggregateByModel(entries, (entry) => entry.model, (entry) => entry.data.message.usage, (entry) => entry.cost);
|
|
3699
3750
|
const modelBreakdowns = createModelBreakdowns(modelAggregates);
|
|
3700
3751
|
const totals = calculateTotals(entries, (entry) => entry.data.message.usage, (entry) => entry.cost);
|
|
@@ -3703,11 +3754,13 @@ async function loadDailyUsageData(options) {
|
|
|
3703
3754
|
date: createDailyDate(date),
|
|
3704
3755
|
...totals,
|
|
3705
3756
|
modelsUsed,
|
|
3706
|
-
modelBreakdowns
|
|
3757
|
+
modelBreakdowns,
|
|
3758
|
+
...project != null && { project }
|
|
3707
3759
|
};
|
|
3708
3760
|
}).filter((item) => item != null);
|
|
3709
|
-
const
|
|
3710
|
-
|
|
3761
|
+
const dateFiltered = filterByDateRange(results, (item) => item.date, options?.since, options?.until);
|
|
3762
|
+
const finalFiltered = filterByProject(dateFiltered, (item) => item.project, options?.project);
|
|
3763
|
+
return sortByDate(finalFiltered, (item) => item.date, options?.order);
|
|
3711
3764
|
} catch (_) {
|
|
3712
3765
|
_usingCtx.e = _;
|
|
3713
3766
|
} finally {
|
|
@@ -3724,21 +3777,11 @@ async function loadSessionData(options) {
|
|
|
3724
3777
|
try {
|
|
3725
3778
|
var _usingCtx3 = (0, import_usingCtx.default)();
|
|
3726
3779
|
const claudePaths = toArray(options?.claudePath ?? getClaudePaths());
|
|
3727
|
-
const filesWithBase =
|
|
3728
|
-
for (const claudePath of claudePaths) {
|
|
3729
|
-
const claudeDir = path.join(claudePath, CLAUDE_PROJECTS_DIR_NAME);
|
|
3730
|
-
const files = await glob([USAGE_DATA_GLOB_PATTERN], {
|
|
3731
|
-
cwd: claudeDir,
|
|
3732
|
-
absolute: true
|
|
3733
|
-
});
|
|
3734
|
-
for (const file of files) filesWithBase.push({
|
|
3735
|
-
file,
|
|
3736
|
-
baseDir: claudeDir
|
|
3737
|
-
});
|
|
3738
|
-
}
|
|
3780
|
+
const filesWithBase = await globUsageFiles(claudePaths);
|
|
3739
3781
|
if (filesWithBase.length === 0) return [];
|
|
3740
|
-
const
|
|
3741
|
-
const
|
|
3782
|
+
const projectFilteredWithBase = filterByProject(filesWithBase, (item) => extractProjectFromPath(item.file), options?.project);
|
|
3783
|
+
const fileToBaseMap = new Map(projectFilteredWithBase.map((f$1) => [f$1.file, f$1.baseDir]));
|
|
3784
|
+
const sortedFilesWithBase = await sortFilesByTimestamp(projectFilteredWithBase.map((f$1) => f$1.file)).then((sortedFiles) => sortedFiles.map((file) => ({
|
|
3742
3785
|
file,
|
|
3743
3786
|
baseDir: fileToBaseMap.get(file) ?? ""
|
|
3744
3787
|
})));
|
|
@@ -3795,8 +3838,9 @@ async function loadSessionData(options) {
|
|
|
3795
3838
|
modelBreakdowns
|
|
3796
3839
|
};
|
|
3797
3840
|
}).filter((item) => item != null);
|
|
3798
|
-
const
|
|
3799
|
-
|
|
3841
|
+
const dateFiltered = filterByDateRange(results, (item) => item.lastActivity, options?.since, options?.until);
|
|
3842
|
+
const sessionFiltered = filterByProject(dateFiltered, (item) => item.projectPath, options?.project);
|
|
3843
|
+
return sortByDate(sessionFiltered, (item) => item.lastActivity, options?.order);
|
|
3800
3844
|
} catch (_) {
|
|
3801
3845
|
_usingCtx3.e = _;
|
|
3802
3846
|
} finally {
|
|
@@ -3811,10 +3855,15 @@ async function loadSessionData(options) {
|
|
|
3811
3855
|
*/
|
|
3812
3856
|
async function loadMonthlyUsageData(options) {
|
|
3813
3857
|
const dailyData = await loadDailyUsageData(options);
|
|
3814
|
-
const
|
|
3858
|
+
const needsProjectGrouping = options?.groupByProject === true || options?.project != null;
|
|
3859
|
+
const groupingKey = needsProjectGrouping ? (data) => `${data.date.substring(0, 7)}\x00${data.project ?? "unknown"}` : (data) => data.date.substring(0, 7);
|
|
3860
|
+
const groupedByMonth = groupBy(dailyData, groupingKey);
|
|
3815
3861
|
const monthlyArray = [];
|
|
3816
|
-
for (const [
|
|
3862
|
+
for (const [groupKey, dailyEntries] of Object.entries(groupedByMonth)) {
|
|
3817
3863
|
if (dailyEntries == null) continue;
|
|
3864
|
+
const parts = groupKey.split("\0");
|
|
3865
|
+
const month = parts[0] ?? groupKey;
|
|
3866
|
+
const project = parts.length > 1 ? parts[1] : void 0;
|
|
3818
3867
|
const allBreakdowns = dailyEntries.flatMap((daily) => daily.modelBreakdowns);
|
|
3819
3868
|
const modelAggregates = aggregateModelBreakdowns(allBreakdowns);
|
|
3820
3869
|
const modelBreakdowns = createModelBreakdowns(modelAggregates);
|
|
@@ -3840,7 +3889,8 @@ async function loadMonthlyUsageData(options) {
|
|
|
3840
3889
|
cacheReadTokens: totalCacheReadTokens,
|
|
3841
3890
|
totalCost,
|
|
3842
3891
|
modelsUsed: uniq(models),
|
|
3843
|
-
modelBreakdowns
|
|
3892
|
+
modelBreakdowns,
|
|
3893
|
+
...project != null && { project }
|
|
3844
3894
|
};
|
|
3845
3895
|
monthlyArray.push(monthlyUsage);
|
|
3846
3896
|
}
|
|
@@ -3866,7 +3916,8 @@ async function loadSessionBlockData(options) {
|
|
|
3866
3916
|
allFiles.push(...files);
|
|
3867
3917
|
}
|
|
3868
3918
|
if (allFiles.length === 0) return [];
|
|
3869
|
-
const
|
|
3919
|
+
const blocksFilteredFiles = filterByProject(allFiles, (filePath) => extractProjectFromPath(filePath), options?.project);
|
|
3920
|
+
const sortedFiles = await sortFilesByTimestamp(blocksFilteredFiles);
|
|
3870
3921
|
const mode = options?.mode ?? "auto";
|
|
3871
3922
|
const fetcher = _usingCtx4.u(mode === "display" ? null : new PricingFetcher(options?.offline));
|
|
3872
3923
|
const processedHashes = /* @__PURE__ */ new Set();
|
|
@@ -3902,17 +3953,17 @@ async function loadSessionBlockData(options) {
|
|
|
3902
3953
|
}
|
|
3903
3954
|
}
|
|
3904
3955
|
const blocks = identifySessionBlocks(allEntries, options?.sessionDurationHours);
|
|
3905
|
-
const
|
|
3956
|
+
const dateFiltered = options?.since != null && options.since !== "" || options?.until != null && options.until !== "" ? blocks.filter((block) => {
|
|
3906
3957
|
const blockDateStr = formatDate(block.startTime.toISOString()).replace(/-/g, "");
|
|
3907
3958
|
if (options.since != null && options.since !== "" && blockDateStr < options.since) return false;
|
|
3908
3959
|
if (options.until != null && options.until !== "" && blockDateStr > options.until) return false;
|
|
3909
3960
|
return true;
|
|
3910
3961
|
}) : blocks;
|
|
3911
|
-
return sortByDate(
|
|
3962
|
+
return sortByDate(dateFiltered, (block) => block.startTime, options?.order);
|
|
3912
3963
|
} catch (_) {
|
|
3913
3964
|
_usingCtx4.e = _;
|
|
3914
3965
|
} finally {
|
|
3915
3966
|
_usingCtx4.d();
|
|
3916
3967
|
}
|
|
3917
3968
|
}
|
|
3918
|
-
export { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, dailyUsageSchema, filterRecentBlocks, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, glob, identifySessionBlocks, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, projectBlockUsage, sessionUsageSchema, sortFilesByTimestamp, uniq, unwrap, usageDataSchema };
|
|
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 };
|
|
@@ -55,6 +55,12 @@ type SessionBlock = {
|
|
|
55
55
|
* @returns Array of valid Claude data directory paths
|
|
56
56
|
*/
|
|
57
57
|
declare function getClaudePaths(): string[];
|
|
58
|
+
/**
|
|
59
|
+
* Extract project name from Claude JSONL file path
|
|
60
|
+
* @param jsonlPath - Absolute path to JSONL file
|
|
61
|
+
* @returns Project name extracted from path, or "unknown" if malformed
|
|
62
|
+
*/
|
|
63
|
+
declare function extractProjectFromPath(jsonlPath: string): string;
|
|
58
64
|
/**
|
|
59
65
|
* Zod schema for validating Claude usage data from JSONL files
|
|
60
66
|
*/
|
|
@@ -220,6 +226,7 @@ declare const dailyUsageSchema: z.ZodObject<{
|
|
|
220
226
|
cacheReadTokens: number;
|
|
221
227
|
cost: number;
|
|
222
228
|
}>, "many">;
|
|
229
|
+
project: z.ZodOptional<z.ZodString>;
|
|
223
230
|
}, "strip", z.ZodTypeAny, {
|
|
224
231
|
date: string & z.BRAND<"DailyDate">;
|
|
225
232
|
inputTokens: number;
|
|
@@ -236,6 +243,7 @@ declare const dailyUsageSchema: z.ZodObject<{
|
|
|
236
243
|
cacheReadTokens: number;
|
|
237
244
|
cost: number;
|
|
238
245
|
}[];
|
|
246
|
+
project?: string | undefined;
|
|
239
247
|
}, {
|
|
240
248
|
date: string;
|
|
241
249
|
inputTokens: number;
|
|
@@ -252,6 +260,7 @@ declare const dailyUsageSchema: z.ZodObject<{
|
|
|
252
260
|
cacheReadTokens: number;
|
|
253
261
|
cost: number;
|
|
254
262
|
}[];
|
|
263
|
+
project?: string | undefined;
|
|
255
264
|
}>;
|
|
256
265
|
/**
|
|
257
266
|
* Type definition for daily usage aggregation
|
|
@@ -369,6 +378,7 @@ declare const monthlyUsageSchema: z.ZodObject<{
|
|
|
369
378
|
cacheReadTokens: number;
|
|
370
379
|
cost: number;
|
|
371
380
|
}>, "many">;
|
|
381
|
+
project: z.ZodOptional<z.ZodString>;
|
|
372
382
|
}, "strip", z.ZodTypeAny, {
|
|
373
383
|
month: string & z.BRAND<"MonthlyDate">;
|
|
374
384
|
inputTokens: number;
|
|
@@ -385,6 +395,7 @@ declare const monthlyUsageSchema: z.ZodObject<{
|
|
|
385
395
|
cacheReadTokens: number;
|
|
386
396
|
cost: number;
|
|
387
397
|
}[];
|
|
398
|
+
project?: string | undefined;
|
|
388
399
|
}, {
|
|
389
400
|
month: string;
|
|
390
401
|
inputTokens: number;
|
|
@@ -401,6 +412,7 @@ declare const monthlyUsageSchema: z.ZodObject<{
|
|
|
401
412
|
cacheReadTokens: number;
|
|
402
413
|
cost: number;
|
|
403
414
|
}[];
|
|
415
|
+
project?: string | undefined;
|
|
404
416
|
}>;
|
|
405
417
|
/**
|
|
406
418
|
* Type definition for monthly usage aggregation
|
|
@@ -446,6 +458,19 @@ declare function calculateCostForEntry(data: UsageData, mode: CostMode, fetcher:
|
|
|
446
458
|
* @returns Usage limit expiration date
|
|
447
459
|
*/
|
|
448
460
|
declare function getUsageLimitResetTime(data: UsageData): Date | null;
|
|
461
|
+
/**
|
|
462
|
+
* Result of glob operation with base directory information
|
|
463
|
+
*/
|
|
464
|
+
type GlobResult = {
|
|
465
|
+
file: string;
|
|
466
|
+
baseDir: string;
|
|
467
|
+
};
|
|
468
|
+
/**
|
|
469
|
+
* Glob files from multiple Claude paths in parallel
|
|
470
|
+
* @param claudePaths - Array of Claude base paths
|
|
471
|
+
* @returns Array of file paths with their base directories
|
|
472
|
+
*/
|
|
473
|
+
declare function globUsageFiles(claudePaths: string[]): Promise<GlobResult[]>;
|
|
449
474
|
/**
|
|
450
475
|
* Date range filter for limiting usage data by date
|
|
451
476
|
*/
|
|
@@ -462,6 +487,8 @@ type LoadOptions = {
|
|
|
462
487
|
order?: SortOrder; // Sort order for dates
|
|
463
488
|
offline?: boolean; // Use offline mode for pricing
|
|
464
489
|
sessionDurationHours?: number; // Session block duration in hours
|
|
490
|
+
groupByProject?: boolean; // Group data by project instead of aggregating
|
|
491
|
+
project?: string; // Filter to specific project name
|
|
465
492
|
} & DateFilter;
|
|
466
493
|
/**
|
|
467
494
|
* Loads and aggregates Claude usage data by day
|
|
@@ -492,4 +519,4 @@ declare function loadMonthlyUsageData(options?: LoadOptions): Promise<MonthlyUsa
|
|
|
492
519
|
*/
|
|
493
520
|
declare function loadSessionBlockData(options?: LoadOptions): Promise<SessionBlock[]>;
|
|
494
521
|
//#endregion
|
|
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 };
|
|
522
|
+
export { DailyUsage, DateFilter, GlobResult, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, extractProjectFromPath, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, globUsageFiles, 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, getUsageLimitResetTime, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-
|
|
1
|
+
import { DailyUsage, DateFilter, GlobResult, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, extractProjectFromPath, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, globUsageFiles, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-CgvyDaQD.js";
|
|
2
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 };
|
|
3
|
+
export { DailyUsage, DateFilter, GlobResult, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, extractProjectFromPath, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, globUsageFiles, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
|
package/dist/data-loader.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "./pricing-fetcher-
|
|
1
|
+
import "./pricing-fetcher-B2yEbqCO.js";
|
|
2
2
|
import "./_token-utils-WjkbrjKv.js";
|
|
3
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-
|
|
5
|
-
import "./logger-
|
|
6
|
-
export { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
|
|
4
|
+
import { calculateCostForEntry, createUniqueHash, dailyUsageSchema, extractProjectFromPath, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, globUsageFiles, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-BVwVxx4T.js";
|
|
5
|
+
import "./logger-CZzaUNvP.js";
|
|
6
|
+
export { calculateCostForEntry, createUniqueHash, dailyUsageSchema, extractProjectFromPath, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, globUsageFiles, 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, isFailure, require_usingCtx, try_ } from "./pricing-fetcher-
|
|
2
|
-
import { getClaudePaths, glob, unwrap, 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-B2yEbqCO.js";
|
|
2
|
+
import { getClaudePaths, glob, unwrap, usageDataSchema } from "./data-loader-BVwVxx4T.js";
|
|
3
|
+
import { logger } from "./logger-CZzaUNvP.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);
|
package/dist/debug.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import "./pricing-fetcher-
|
|
1
|
+
import "./pricing-fetcher-B2yEbqCO.js";
|
|
2
2
|
import "./_token-utils-WjkbrjKv.js";
|
|
3
3
|
import "./_types-BHFM59hI.js";
|
|
4
|
-
import "./data-loader-
|
|
5
|
-
import "./logger-
|
|
6
|
-
import { detectMismatches, printMismatchReport } from "./debug-
|
|
4
|
+
import "./data-loader-BVwVxx4T.js";
|
|
5
|
+
import "./logger-CZzaUNvP.js";
|
|
6
|
+
import { detectMismatches, printMismatchReport } from "./debug-DtmspKbf.js";
|
|
7
7
|
export { detectMismatches, printMismatchReport };
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, BURN_RATE_THRESHOLDS,
|
|
2
|
+
import { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, BURN_RATE_THRESHOLDS, DEFAULT_RECENT_DAYS, DEFAULT_REFRESH_INTERVAL_SECONDS, MAX_REFRESH_INTERVAL_SECONDS, MCP_DEFAULT_PORT, MIN_REFRESH_INTERVAL_SECONDS, MIN_RENDER_INTERVAL_MS, PROJECT_ALIASES_ENV, PricingFetcher, __commonJSMin, __require, __toESM, isFailure, require_usingCtx, try_ } from "./pricing-fetcher-B2yEbqCO.js";
|
|
3
3
|
import { getTotalTokens } from "./_token-utils-WjkbrjKv.js";
|
|
4
4
|
import { CostModes, SortOrders, filterDateSchema } from "./_types-BHFM59hI.js";
|
|
5
5
|
import { calculateTotals, createTotalsObject } from "./calculate-cost-BDqO4yWA.js";
|
|
6
|
-
import { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, filterRecentBlocks, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime,
|
|
7
|
-
import { description, log, logger, name, version } from "./logger-
|
|
8
|
-
import { detectMismatches, printMismatchReport } from "./debug-
|
|
9
|
-
import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-
|
|
6
|
+
import { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, filterRecentBlocks, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, globUsageFiles, identifySessionBlocks, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, projectBlockUsage, sortFilesByTimestamp, uniq, usageDataSchema } from "./data-loader-BVwVxx4T.js";
|
|
7
|
+
import { description, log, logger, name, version } from "./logger-CZzaUNvP.js";
|
|
8
|
+
import { detectMismatches, printMismatchReport } from "./debug-DtmspKbf.js";
|
|
9
|
+
import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-DRNUcHZe.js";
|
|
10
10
|
import { readFile } from "node:fs/promises";
|
|
11
|
-
import path from "node:path";
|
|
12
11
|
import process$1 from "node:process";
|
|
13
12
|
import { createServer } from "node:http";
|
|
14
13
|
import { Http2ServerRequest } from "node:http2";
|
|
@@ -3205,14 +3204,11 @@ var LiveMonitor = class {
|
|
|
3205
3204
|
* Only reads new or modified files since last check
|
|
3206
3205
|
*/
|
|
3207
3206
|
async getActiveBlock() {
|
|
3208
|
-
const
|
|
3209
|
-
const
|
|
3210
|
-
|
|
3211
|
-
absolute: true
|
|
3212
|
-
});
|
|
3213
|
-
if (files.length === 0) return null;
|
|
3207
|
+
const results = await globUsageFiles(this.config.claudePaths);
|
|
3208
|
+
const allFiles = results.map((r) => r.file);
|
|
3209
|
+
if (allFiles.length === 0) return null;
|
|
3214
3210
|
const filesToRead = [];
|
|
3215
|
-
for (const file of
|
|
3211
|
+
for (const file of allFiles) {
|
|
3216
3212
|
const timestamp = await getEarliestTimestamp(file);
|
|
3217
3213
|
const lastTimestamp = this.lastFileTimestamps.get(file);
|
|
3218
3214
|
if (timestamp != null && (lastTimestamp == null || timestamp.getTime() > lastTimestamp)) {
|
|
@@ -3745,7 +3741,7 @@ const DETAIL_COLUMN_WIDTHS = {
|
|
|
3745
3741
|
function renderLiveDisplay(terminal, block, config) {
|
|
3746
3742
|
const width = terminal.width;
|
|
3747
3743
|
const now = /* @__PURE__ */ new Date();
|
|
3748
|
-
const totalTokens = block.tokenCounts
|
|
3744
|
+
const totalTokens = getTotalTokens(block.tokenCounts);
|
|
3749
3745
|
const elapsed = (now.getTime() - block.startTime.getTime()) / (1e3 * 60);
|
|
3750
3746
|
const remaining = (block.endTime.getTime() - now.getTime()) / (1e3 * 60);
|
|
3751
3747
|
if (width < 60) {
|
|
@@ -3952,7 +3948,7 @@ async function startLiveMonitoring(config) {
|
|
|
3952
3948
|
terminal.clearScreen();
|
|
3953
3949
|
terminal.hideCursor();
|
|
3954
3950
|
const monitor = _usingCtx.u(new LiveMonitor({
|
|
3955
|
-
|
|
3951
|
+
claudePaths: config.claudePaths,
|
|
3956
3952
|
sessionDurationHours: config.sessionDurationHours,
|
|
3957
3953
|
mode: config.mode,
|
|
3958
3954
|
order: config.order
|
|
@@ -4052,8 +4048,7 @@ function formatModels(models) {
|
|
|
4052
4048
|
* @returns Parsed token limit or undefined if invalid
|
|
4053
4049
|
*/
|
|
4054
4050
|
function parseTokenLimit(value, maxFromAll) {
|
|
4055
|
-
if (value == null || value === "") return void 0;
|
|
4056
|
-
if (value === "max") return maxFromAll > 0 ? maxFromAll : void 0;
|
|
4051
|
+
if (value == null || value === "" || value === "max") return maxFromAll > 0 ? maxFromAll : void 0;
|
|
4057
4052
|
const limit = Number.parseInt(value, 10);
|
|
4058
4053
|
return Number.isNaN(limit) ? void 0 : limit;
|
|
4059
4054
|
}
|
|
@@ -4117,7 +4112,7 @@ const blocksCommand = define({
|
|
|
4117
4112
|
process$1.exit(0);
|
|
4118
4113
|
}
|
|
4119
4114
|
let maxTokensFromAll = 0;
|
|
4120
|
-
if (ctx.values.tokenLimit === "max") {
|
|
4115
|
+
if (ctx.values.tokenLimit === "max" || ctx.values.tokenLimit == null || ctx.values.tokenLimit === "") {
|
|
4121
4116
|
for (const block of blocks) if (!(block.isGap ?? false) && !block.isActive) {
|
|
4122
4117
|
const blockTokens = getTotalTokens(block.tokenCounts);
|
|
4123
4118
|
if (blockTokens > maxTokensFromAll) maxTokensFromAll = blockTokens;
|
|
@@ -4151,7 +4146,7 @@ const blocksCommand = define({
|
|
|
4151
4146
|
throw new Error("No valid Claude data directory found");
|
|
4152
4147
|
}
|
|
4153
4148
|
await startLiveMonitoring({
|
|
4154
|
-
|
|
4149
|
+
claudePaths: paths,
|
|
4155
4150
|
tokenLimit: parseTokenLimit(tokenLimitValue, maxTokensFromAll),
|
|
4156
4151
|
refreshInterval: refreshInterval * 1e3,
|
|
4157
4152
|
sessionDurationHours: ctx.values.sessionLength,
|
|
@@ -4173,7 +4168,7 @@ const blocksCommand = define({
|
|
|
4173
4168
|
isGap: block.isGap ?? false,
|
|
4174
4169
|
entries: block.entries.length,
|
|
4175
4170
|
tokenCounts: block.tokenCounts,
|
|
4176
|
-
totalTokens: block.tokenCounts
|
|
4171
|
+
totalTokens: getTotalTokens(block.tokenCounts),
|
|
4177
4172
|
costUSD: block.costUSD,
|
|
4178
4173
|
models: block.models,
|
|
4179
4174
|
burnRate,
|
|
@@ -4334,11 +4329,162 @@ const blocksCommand = define({
|
|
|
4334
4329
|
}
|
|
4335
4330
|
}
|
|
4336
4331
|
});
|
|
4332
|
+
/**
|
|
4333
|
+
* Group daily usage data by project for JSON output
|
|
4334
|
+
*/
|
|
4335
|
+
function groupByProject(dailyData) {
|
|
4336
|
+
const projects = {};
|
|
4337
|
+
for (const data of dailyData) {
|
|
4338
|
+
const projectName = data.project ?? "unknown";
|
|
4339
|
+
if (projects[projectName] == null) projects[projectName] = [];
|
|
4340
|
+
projects[projectName].push({
|
|
4341
|
+
date: data.date,
|
|
4342
|
+
inputTokens: data.inputTokens,
|
|
4343
|
+
outputTokens: data.outputTokens,
|
|
4344
|
+
cacheCreationTokens: data.cacheCreationTokens,
|
|
4345
|
+
cacheReadTokens: data.cacheReadTokens,
|
|
4346
|
+
totalTokens: getTotalTokens(data),
|
|
4347
|
+
totalCost: data.totalCost,
|
|
4348
|
+
modelsUsed: data.modelsUsed,
|
|
4349
|
+
modelBreakdowns: data.modelBreakdowns
|
|
4350
|
+
});
|
|
4351
|
+
}
|
|
4352
|
+
return projects;
|
|
4353
|
+
}
|
|
4354
|
+
/**
|
|
4355
|
+
* Group daily usage data by project for table display
|
|
4356
|
+
*/
|
|
4357
|
+
function groupDataByProject(dailyData) {
|
|
4358
|
+
const projects = {};
|
|
4359
|
+
for (const data of dailyData) {
|
|
4360
|
+
const projectName = data.project ?? "unknown";
|
|
4361
|
+
if (projects[projectName] == null) projects[projectName] = [];
|
|
4362
|
+
projects[projectName].push(data);
|
|
4363
|
+
}
|
|
4364
|
+
return projects;
|
|
4365
|
+
}
|
|
4366
|
+
/**
|
|
4367
|
+
* Cache for parsed aliases to avoid repeated parsing
|
|
4368
|
+
*/
|
|
4369
|
+
let aliasCache = null;
|
|
4370
|
+
/**
|
|
4371
|
+
* Parse project aliases from environment variable
|
|
4372
|
+
* @returns Map of raw project names to their aliases
|
|
4373
|
+
*/
|
|
4374
|
+
function getProjectAliases() {
|
|
4375
|
+
if (aliasCache !== null) return aliasCache;
|
|
4376
|
+
aliasCache = /* @__PURE__ */ new Map();
|
|
4377
|
+
const aliasEnv = (process$1.env[PROJECT_ALIASES_ENV] ?? "").trim();
|
|
4378
|
+
if (aliasEnv === "") return aliasCache;
|
|
4379
|
+
const pairs = aliasEnv.split(",").map((pair) => pair.trim()).filter((pair) => pair !== "");
|
|
4380
|
+
for (const pair of pairs) {
|
|
4381
|
+
const parts = pair.split("=").map((s) => s.trim());
|
|
4382
|
+
const rawName = parts[0];
|
|
4383
|
+
const alias = parts[1];
|
|
4384
|
+
if (rawName != null && alias != null && rawName !== "" && alias !== "") aliasCache.set(rawName, alias);
|
|
4385
|
+
}
|
|
4386
|
+
return aliasCache;
|
|
4387
|
+
}
|
|
4388
|
+
/**
|
|
4389
|
+
* Extract meaningful project name from directory-style project paths
|
|
4390
|
+
* Uses improved heuristics to handle complex project structures
|
|
4391
|
+
*
|
|
4392
|
+
* @param projectName - Raw project name from directory path
|
|
4393
|
+
* @returns Cleaned and formatted project name
|
|
4394
|
+
*
|
|
4395
|
+
* @example
|
|
4396
|
+
* ```typescript
|
|
4397
|
+
* // Basic cleanup
|
|
4398
|
+
* parseProjectName('-Users-phaedrus-Development-ccusage')
|
|
4399
|
+
* // → 'ccusage'
|
|
4400
|
+
*
|
|
4401
|
+
* // Complex project with feature branch
|
|
4402
|
+
* parseProjectName('-Users-phaedrus-Development-adminifi-edugakko-api--feature-ticket-002-configure-dependabot')
|
|
4403
|
+
* // → 'configure-dependabot'
|
|
4404
|
+
*
|
|
4405
|
+
* // Handle unknown projects
|
|
4406
|
+
* parseProjectName('unknown')
|
|
4407
|
+
* // → 'Unknown Project'
|
|
4408
|
+
* ```
|
|
4409
|
+
*/
|
|
4410
|
+
function parseProjectName(projectName) {
|
|
4411
|
+
if (projectName === "unknown" || projectName === "") return "Unknown Project";
|
|
4412
|
+
let cleaned = projectName;
|
|
4413
|
+
if (cleaned.match(/^[A-Z]:\\Users\\|^\\Users\\/) != null) {
|
|
4414
|
+
const segments = cleaned.split("\\");
|
|
4415
|
+
const userIndex = segments.findIndex((seg) => seg === "Users");
|
|
4416
|
+
if (userIndex !== -1 && userIndex + 3 < segments.length) cleaned = segments.slice(userIndex + 3).join("-");
|
|
4417
|
+
}
|
|
4418
|
+
if (cleaned.startsWith("-Users-") || cleaned.startsWith("/Users/")) {
|
|
4419
|
+
const separator = cleaned.startsWith("-Users-") ? "-" : "/";
|
|
4420
|
+
const segments = cleaned.split(separator).filter((s) => s.length > 0);
|
|
4421
|
+
const userIndex = segments.findIndex((seg) => seg === "Users");
|
|
4422
|
+
if (userIndex !== -1 && userIndex + 3 < segments.length) cleaned = segments.slice(userIndex + 3).join("-");
|
|
4423
|
+
}
|
|
4424
|
+
if (cleaned === projectName) cleaned = projectName.replace(/^[/\\-]+|[/\\-]+$/g, "");
|
|
4425
|
+
if (cleaned.match(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/i) != null) {
|
|
4426
|
+
const parts = cleaned.split("-");
|
|
4427
|
+
if (parts.length >= 5) cleaned = parts.slice(-2).join("-");
|
|
4428
|
+
}
|
|
4429
|
+
if (cleaned.includes("--")) {
|
|
4430
|
+
const parts = cleaned.split("--");
|
|
4431
|
+
if (parts.length >= 2 && parts[0] != null) cleaned = parts[0];
|
|
4432
|
+
}
|
|
4433
|
+
if (cleaned.includes("-") && cleaned.length > 20) {
|
|
4434
|
+
const segments = cleaned.split("-");
|
|
4435
|
+
const meaningfulSegments = segments.filter((seg) => seg.length > 2 && seg.match(/^(?:dev|development|feat|feature|fix|bug|test|staging|prod|production|main|master|branch)$/i) == null);
|
|
4436
|
+
if (meaningfulSegments.length >= 2) {
|
|
4437
|
+
const lastSegments = meaningfulSegments.slice(-2);
|
|
4438
|
+
if (lastSegments.join("-").length >= 6) cleaned = lastSegments.join("-");
|
|
4439
|
+
else if (meaningfulSegments.length >= 3) cleaned = meaningfulSegments.slice(-3).join("-");
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
cleaned = cleaned.replace(/^[/\\-]+|[/\\-]+$/g, "");
|
|
4443
|
+
return cleaned !== "" ? cleaned : projectName !== "" ? projectName : "Unknown Project";
|
|
4444
|
+
}
|
|
4445
|
+
/**
|
|
4446
|
+
* Format project name for display with custom alias support
|
|
4447
|
+
*
|
|
4448
|
+
* @param projectName - Raw project name from directory path
|
|
4449
|
+
* @returns User-friendly project name with alias support
|
|
4450
|
+
*
|
|
4451
|
+
* @example
|
|
4452
|
+
* ```typescript
|
|
4453
|
+
* // Without aliases
|
|
4454
|
+
* formatProjectName('-Users-phaedrus-Development-ccusage')
|
|
4455
|
+
* // → 'ccusage'
|
|
4456
|
+
*
|
|
4457
|
+
* // With alias (when CCUSAGE_PROJECT_ALIASES="ccusage=Usage Tracker")
|
|
4458
|
+
* formatProjectName('-Users-phaedrus-Development-ccusage')
|
|
4459
|
+
* // → 'Usage Tracker'
|
|
4460
|
+
* ```
|
|
4461
|
+
*/
|
|
4462
|
+
function formatProjectName(projectName) {
|
|
4463
|
+
const aliases = getProjectAliases();
|
|
4464
|
+
if (aliases.has(projectName)) return aliases.get(projectName);
|
|
4465
|
+
const parsed = parseProjectName(projectName);
|
|
4466
|
+
if (aliases.has(parsed)) return aliases.get(parsed);
|
|
4467
|
+
return parsed;
|
|
4468
|
+
}
|
|
4337
4469
|
var import_picocolors$2 = __toESM(require_picocolors(), 1);
|
|
4338
4470
|
const dailyCommand = define({
|
|
4339
4471
|
name: "daily",
|
|
4340
4472
|
description: "Show usage report grouped by date",
|
|
4341
4473
|
...sharedCommandConfig,
|
|
4474
|
+
args: {
|
|
4475
|
+
...sharedCommandConfig.args,
|
|
4476
|
+
instances: {
|
|
4477
|
+
type: "boolean",
|
|
4478
|
+
short: "i",
|
|
4479
|
+
description: "Show usage breakdown by project/instance",
|
|
4480
|
+
default: false
|
|
4481
|
+
},
|
|
4482
|
+
project: {
|
|
4483
|
+
type: "string",
|
|
4484
|
+
short: "p",
|
|
4485
|
+
description: "Filter to specific project name"
|
|
4486
|
+
}
|
|
4487
|
+
},
|
|
4342
4488
|
async run(ctx) {
|
|
4343
4489
|
if (ctx.values.json) logger.level = 0;
|
|
4344
4490
|
const dailyData = await loadDailyUsageData({
|
|
@@ -4346,7 +4492,9 @@ const dailyCommand = define({
|
|
|
4346
4492
|
until: ctx.values.until,
|
|
4347
4493
|
mode: ctx.values.mode,
|
|
4348
4494
|
order: ctx.values.order,
|
|
4349
|
-
offline: ctx.values.offline
|
|
4495
|
+
offline: ctx.values.offline,
|
|
4496
|
+
groupByProject: ctx.values.instances,
|
|
4497
|
+
project: ctx.values.project
|
|
4350
4498
|
});
|
|
4351
4499
|
if (dailyData.length === 0) {
|
|
4352
4500
|
if (ctx.values.json) log(JSON.stringify([]));
|
|
@@ -4359,7 +4507,10 @@ const dailyCommand = define({
|
|
|
4359
4507
|
printMismatchReport(mismatchStats, ctx.values.debugSamples);
|
|
4360
4508
|
}
|
|
4361
4509
|
if (ctx.values.json) {
|
|
4362
|
-
const jsonOutput = {
|
|
4510
|
+
const jsonOutput = ctx.values.instances && dailyData.some((d) => d.project != null) ? {
|
|
4511
|
+
projects: groupByProject(dailyData),
|
|
4512
|
+
totals: createTotalsObject(totals)
|
|
4513
|
+
} : {
|
|
4363
4514
|
daily: dailyData.map((data) => ({
|
|
4364
4515
|
date: data.date,
|
|
4365
4516
|
inputTokens: data.inputTokens,
|
|
@@ -4369,7 +4520,8 @@ const dailyCommand = define({
|
|
|
4369
4520
|
totalTokens: getTotalTokens(data),
|
|
4370
4521
|
totalCost: data.totalCost,
|
|
4371
4522
|
modelsUsed: data.modelsUsed,
|
|
4372
|
-
modelBreakdowns: data.modelBreakdowns
|
|
4523
|
+
modelBreakdowns: data.modelBreakdowns,
|
|
4524
|
+
...data.project != null && { project: data.project }
|
|
4373
4525
|
})),
|
|
4374
4526
|
totals: createTotalsObject(totals)
|
|
4375
4527
|
};
|
|
@@ -4415,7 +4567,46 @@ const dailyCommand = define({
|
|
|
4415
4567
|
],
|
|
4416
4568
|
compactThreshold: 100
|
|
4417
4569
|
});
|
|
4418
|
-
|
|
4570
|
+
if (ctx.values.instances && dailyData.some((d) => d.project != null)) {
|
|
4571
|
+
const projectGroups = groupDataByProject(dailyData);
|
|
4572
|
+
let isFirstProject = true;
|
|
4573
|
+
for (const [projectName, projectData] of Object.entries(projectGroups)) {
|
|
4574
|
+
if (!isFirstProject) table.push([
|
|
4575
|
+
"",
|
|
4576
|
+
"",
|
|
4577
|
+
"",
|
|
4578
|
+
"",
|
|
4579
|
+
"",
|
|
4580
|
+
"",
|
|
4581
|
+
"",
|
|
4582
|
+
""
|
|
4583
|
+
]);
|
|
4584
|
+
table.push([
|
|
4585
|
+
import_picocolors$2.default.cyan(`Project: ${formatProjectName(projectName)}`),
|
|
4586
|
+
"",
|
|
4587
|
+
"",
|
|
4588
|
+
"",
|
|
4589
|
+
"",
|
|
4590
|
+
"",
|
|
4591
|
+
"",
|
|
4592
|
+
""
|
|
4593
|
+
]);
|
|
4594
|
+
for (const data of projectData) {
|
|
4595
|
+
table.push([
|
|
4596
|
+
data.date,
|
|
4597
|
+
formatModelsDisplayMultiline(data.modelsUsed),
|
|
4598
|
+
formatNumber(data.inputTokens),
|
|
4599
|
+
formatNumber(data.outputTokens),
|
|
4600
|
+
formatNumber(data.cacheCreationTokens),
|
|
4601
|
+
formatNumber(data.cacheReadTokens),
|
|
4602
|
+
formatNumber(getTotalTokens(data)),
|
|
4603
|
+
formatCurrency(data.totalCost)
|
|
4604
|
+
]);
|
|
4605
|
+
if (ctx.values.breakdown) pushBreakdownRows(table, data.modelBreakdowns);
|
|
4606
|
+
}
|
|
4607
|
+
isFirstProject = false;
|
|
4608
|
+
}
|
|
4609
|
+
} else for (const data of dailyData) {
|
|
4419
4610
|
table.push([
|
|
4420
4611
|
data.date,
|
|
4421
4612
|
formatModelsDisplayMultiline(data.modelsUsed),
|
|
@@ -4474,6 +4665,7 @@ var Request = class extends GlobalRequest {
|
|
|
4474
4665
|
super(input, options);
|
|
4475
4666
|
}
|
|
4476
4667
|
};
|
|
4668
|
+
var wrapBodyStream = Symbol("wrapBodyStream");
|
|
4477
4669
|
var newRequestFromIncoming = (method, url, incoming, abortController) => {
|
|
4478
4670
|
const headerRecord = [];
|
|
4479
4671
|
const rawHeaders = incoming.rawHeaders;
|
|
@@ -4498,7 +4690,19 @@ var newRequestFromIncoming = (method, url, incoming, abortController) => {
|
|
|
4498
4690
|
controller.enqueue(incoming.rawBody);
|
|
4499
4691
|
controller.close();
|
|
4500
4692
|
} });
|
|
4501
|
-
else
|
|
4693
|
+
else if (incoming[wrapBodyStream]) {
|
|
4694
|
+
let reader;
|
|
4695
|
+
init$1.body = new ReadableStream({ async pull(controller) {
|
|
4696
|
+
try {
|
|
4697
|
+
reader ||= Readable.toWeb(incoming).getReader();
|
|
4698
|
+
const { done, value } = await reader.read();
|
|
4699
|
+
if (done) controller.close();
|
|
4700
|
+
else controller.enqueue(value);
|
|
4701
|
+
} catch (error) {
|
|
4702
|
+
controller.error(error);
|
|
4703
|
+
}
|
|
4704
|
+
} });
|
|
4705
|
+
} else init$1.body = Readable.toWeb(incoming);
|
|
4502
4706
|
return new Request(url, init$1);
|
|
4503
4707
|
};
|
|
4504
4708
|
var getRequestCache = Symbol("getRequestCache");
|
|
@@ -4709,6 +4913,7 @@ global.fetch = (info$1, init$1) => {
|
|
|
4709
4913
|
};
|
|
4710
4914
|
return webFetch(info$1, init$1);
|
|
4711
4915
|
};
|
|
4916
|
+
var outgoingEnded = Symbol("outgoingEnded");
|
|
4712
4917
|
var regBuffer = /^no$/i;
|
|
4713
4918
|
var regContentType = /^(application\/json\b|text\/(?!event-stream\b))/i;
|
|
4714
4919
|
var handleRequestError = () => new Response(null, { status: 400 });
|
|
@@ -4737,8 +4942,9 @@ var responseViaCache = async (res, outgoing) => {
|
|
|
4737
4942
|
else if (body instanceof Blob) outgoing.end(new Uint8Array(await body.arrayBuffer()));
|
|
4738
4943
|
else {
|
|
4739
4944
|
flushHeaders(outgoing);
|
|
4740
|
-
|
|
4945
|
+
await writeFromReadableStream(body, outgoing)?.catch((e) => handleResponseError(e, outgoing));
|
|
4741
4946
|
}
|
|
4947
|
+
outgoing[outgoingEnded]?.();
|
|
4742
4948
|
};
|
|
4743
4949
|
var responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
4744
4950
|
if (res instanceof Promise) if (options.errorHandler) try {
|
|
@@ -4767,8 +4973,10 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
|
4767
4973
|
outgoing.writeHead(res.status, resHeaderRecord);
|
|
4768
4974
|
outgoing.end();
|
|
4769
4975
|
}
|
|
4976
|
+
outgoing[outgoingEnded]?.();
|
|
4770
4977
|
};
|
|
4771
4978
|
var getRequestListener = (fetchCallback, options = {}) => {
|
|
4979
|
+
const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
|
|
4772
4980
|
if (options.overrideGlobalObjects !== false && global.Request !== Request) {
|
|
4773
4981
|
Object.defineProperty(global, "Request", { value: Request });
|
|
4774
4982
|
Object.defineProperty(global, "Response", { value: Response2 });
|
|
@@ -4777,11 +4985,32 @@ var getRequestListener = (fetchCallback, options = {}) => {
|
|
|
4777
4985
|
let res, req;
|
|
4778
4986
|
try {
|
|
4779
4987
|
req = newRequest(incoming, options.hostname);
|
|
4988
|
+
let incomingEnded = !autoCleanupIncoming || incoming.method === "GET" || incoming.method === "HEAD";
|
|
4989
|
+
if (!incomingEnded) {
|
|
4990
|
+
incoming[wrapBodyStream] = true;
|
|
4991
|
+
incoming.on("end", () => {
|
|
4992
|
+
incomingEnded = true;
|
|
4993
|
+
});
|
|
4994
|
+
if (incoming instanceof Http2ServerRequest) outgoing[outgoingEnded] = () => {
|
|
4995
|
+
if (!incomingEnded) setTimeout(() => {
|
|
4996
|
+
if (!incomingEnded) setTimeout(() => {
|
|
4997
|
+
incoming.destroy();
|
|
4998
|
+
outgoing.destroy();
|
|
4999
|
+
});
|
|
5000
|
+
});
|
|
5001
|
+
};
|
|
5002
|
+
}
|
|
4780
5003
|
outgoing.on("close", () => {
|
|
4781
5004
|
const abortController = req[abortControllerKey];
|
|
4782
|
-
if (
|
|
4783
|
-
|
|
4784
|
-
|
|
5005
|
+
if (abortController) {
|
|
5006
|
+
if (incoming.errored) req[abortControllerKey].abort(incoming.errored.toString());
|
|
5007
|
+
else if (!outgoing.writableFinished) req[abortControllerKey].abort("Client connection prematurely closed.");
|
|
5008
|
+
}
|
|
5009
|
+
if (!incomingEnded) setTimeout(() => {
|
|
5010
|
+
if (!incomingEnded) setTimeout(() => {
|
|
5011
|
+
incoming.destroy();
|
|
5012
|
+
});
|
|
5013
|
+
});
|
|
4785
5014
|
});
|
|
4786
5015
|
res = fetchCallback(req, {
|
|
4787
5016
|
incoming,
|
|
@@ -4807,7 +5036,8 @@ var createAdaptorServer = (options) => {
|
|
|
4807
5036
|
const fetchCallback = options.fetch;
|
|
4808
5037
|
const requestListener = getRequestListener(fetchCallback, {
|
|
4809
5038
|
hostname: options.hostname,
|
|
4810
|
-
overrideGlobalObjects: options.overrideGlobalObjects
|
|
5039
|
+
overrideGlobalObjects: options.overrideGlobalObjects,
|
|
5040
|
+
autoCleanupIncoming: options.autoCleanupIncoming
|
|
4811
5041
|
});
|
|
4812
5042
|
const createServer$1 = options.createServer || createServer;
|
|
4813
5043
|
const server = createServer$1(options.serverOptions || {}, requestListener);
|
|
@@ -5038,7 +5268,8 @@ const sessionCommand = define({
|
|
|
5038
5268
|
totalCost: data.totalCost,
|
|
5039
5269
|
lastActivity: data.lastActivity,
|
|
5040
5270
|
modelsUsed: data.modelsUsed,
|
|
5041
|
-
modelBreakdowns: data.modelBreakdowns
|
|
5271
|
+
modelBreakdowns: data.modelBreakdowns,
|
|
5272
|
+
projectPath: data.projectPath
|
|
5042
5273
|
})),
|
|
5043
5274
|
totals: createTotalsObject(totals)
|
|
5044
5275
|
};
|
|
@@ -951,7 +951,7 @@ function _getDefaultLogLevel() {
|
|
|
951
951
|
}
|
|
952
952
|
const consola = createConsola$1();
|
|
953
953
|
var name = "ccusage";
|
|
954
|
-
var version = "15.
|
|
954
|
+
var version = "15.5.0";
|
|
955
955
|
var description = "Usage analysis tool for Claude Code";
|
|
956
956
|
/**
|
|
957
957
|
* Application logger instance with package name tag
|
package/dist/logger.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { log, logger } from "./logger-
|
|
1
|
+
import { log, logger } from "./logger-CZzaUNvP.js";
|
|
2
2
|
export { log, logger };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { __commonJSMin, __toESM, require_usingCtx } from "./pricing-fetcher-
|
|
1
|
+
import { __commonJSMin, __toESM, require_usingCtx } from "./pricing-fetcher-B2yEbqCO.js";
|
|
2
2
|
import { getTotalTokens } from "./_token-utils-WjkbrjKv.js";
|
|
3
3
|
import { ZodFirstPartyTypeKind, ZodOptional, ZodType, arrayType, booleanType, discriminatedUnionType, enumType, filterDateSchema, literalType, numberType, objectType, optionalType, recordType, stringType, unionType, unknownType } from "./_types-BHFM59hI.js";
|
|
4
4
|
import { calculateTotals, createTotalsObject } from "./calculate-cost-BDqO4yWA.js";
|
|
5
|
-
import { getClaudePaths, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData } from "./data-loader-
|
|
6
|
-
import { name, version } from "./logger-
|
|
5
|
+
import { getClaudePaths, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData } from "./data-loader-BVwVxx4T.js";
|
|
6
|
+
import { name, version } from "./logger-CZzaUNvP.js";
|
|
7
7
|
import process from "node:process";
|
|
8
8
|
const LATEST_PROTOCOL_VERSION = "2025-06-18";
|
|
9
9
|
const SUPPORTED_PROTOCOL_VERSIONS = [
|
|
@@ -606,7 +606,7 @@ const ElicitRequestSchema = RequestSchema.extend({
|
|
|
606
606
|
const ElicitResultSchema = ResultSchema.extend({
|
|
607
607
|
action: enumType([
|
|
608
608
|
"accept",
|
|
609
|
-
"
|
|
609
|
+
"decline",
|
|
610
610
|
"cancel"
|
|
611
611
|
]),
|
|
612
612
|
content: optionalType(recordType(stringType(), unknownType()))
|
|
@@ -1054,7 +1054,7 @@ var HonoRequest = class {
|
|
|
1054
1054
|
return bodyCache[key] = raw$1[key]();
|
|
1055
1055
|
};
|
|
1056
1056
|
json() {
|
|
1057
|
-
return this.#cachedBody("
|
|
1057
|
+
return this.#cachedBody("text").then((text) => JSON.parse(text));
|
|
1058
1058
|
}
|
|
1059
1059
|
text() {
|
|
1060
1060
|
return this.#cachedBody("text");
|
|
@@ -8561,10 +8561,6 @@ var McpServer = class {
|
|
|
8561
8561
|
return registeredResourceTemplate;
|
|
8562
8562
|
}
|
|
8563
8563
|
}
|
|
8564
|
-
/**
|
|
8565
|
-
* Registers a resource with a config object and callback.
|
|
8566
|
-
* For static resources, use a URI string. For dynamic resources, use a ResourceTemplate.
|
|
8567
|
-
*/
|
|
8568
8564
|
registerResource(name$1, uriOrTemplate, config, readCallback) {
|
|
8569
8565
|
if (typeof uriOrTemplate === "string") {
|
|
8570
8566
|
if (this._registeredResources[uriOrTemplate]) throw new Error(`Resource ${uriOrTemplate} is already registered`);
|
|
@@ -8766,7 +8762,10 @@ var McpServer = class {
|
|
|
8766
8762
|
if (this.isConnected()) this.server.sendPromptListChanged();
|
|
8767
8763
|
}
|
|
8768
8764
|
};
|
|
8769
|
-
const EMPTY_OBJECT_JSON_SCHEMA = {
|
|
8765
|
+
const EMPTY_OBJECT_JSON_SCHEMA = {
|
|
8766
|
+
type: "object",
|
|
8767
|
+
properties: {}
|
|
8768
|
+
};
|
|
8770
8769
|
function isZodRawShape(obj) {
|
|
8771
8770
|
if (typeof obj !== "object" || obj === null) return false;
|
|
8772
8771
|
const isEmptyObject = Object.keys(obj).length === 0;
|
package/dist/mcp.d.ts
CHANGED
package/dist/mcp.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import "./pricing-fetcher-
|
|
1
|
+
import "./pricing-fetcher-B2yEbqCO.js";
|
|
2
2
|
import "./_token-utils-WjkbrjKv.js";
|
|
3
3
|
import "./_types-BHFM59hI.js";
|
|
4
4
|
import "./calculate-cost-BDqO4yWA.js";
|
|
5
|
-
import "./data-loader-
|
|
6
|
-
import "./logger-
|
|
7
|
-
import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-
|
|
5
|
+
import "./data-loader-BVwVxx4T.js";
|
|
6
|
+
import "./logger-CZzaUNvP.js";
|
|
7
|
+
import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-DRNUcHZe.js";
|
|
8
8
|
export { createMcpHttpApp, createMcpServer, startMcpServerStdio };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { modelPricingSchema } from "./_types-BHFM59hI.js";
|
|
2
|
-
import { logger } from "./logger-
|
|
2
|
+
import { logger } from "./logger-CZzaUNvP.js";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import F, { homedir } from "node:os";
|
|
@@ -47,6 +47,7 @@ const andThrough = (fn) => (result) => {
|
|
|
47
47
|
};
|
|
48
48
|
return isPromise(result) ? result.then(apply) : apply(result);
|
|
49
49
|
};
|
|
50
|
+
const isSuccess = (result) => "Success" === result.type;
|
|
50
51
|
const succeed = (...args) => {
|
|
51
52
|
const value = args[0];
|
|
52
53
|
if (void 0 === value) return { type: "Success" };
|
|
@@ -71,7 +72,6 @@ const fail = (...args) => {
|
|
|
71
72
|
error
|
|
72
73
|
};
|
|
73
74
|
};
|
|
74
|
-
const isSuccess = (result) => "Success" === result.type;
|
|
75
75
|
const inspect = (fn) => (result) => {
|
|
76
76
|
const apply = (r) => {
|
|
77
77
|
if (isSuccess(r)) fn(r.value);
|
|
@@ -192,6 +192,28 @@ const DEFAULT_CLAUDE_CONFIG_PATH = `${XDG_CONFIG_DIR}/claude`;
|
|
|
192
192
|
*/
|
|
193
193
|
const CLAUDE_CONFIG_DIR_ENV = "CLAUDE_CONFIG_DIR";
|
|
194
194
|
/**
|
|
195
|
+
* Environment variable for custom project name aliases
|
|
196
|
+
*
|
|
197
|
+
* Allows users to configure readable display names for cryptic or long project directory names.
|
|
198
|
+
* This is particularly useful for:
|
|
199
|
+
* - UUID-based project directories generated by Claude Code
|
|
200
|
+
* - Long path-based project names that are hard to read in tables
|
|
201
|
+
* - Custom branding of project names for organizational consistency
|
|
202
|
+
*
|
|
203
|
+
* Format: "raw-name=alias,another-name=other-alias"
|
|
204
|
+
* Example: "a2cd99ed-a586-4fe4-8f59-b0026409ec09=My Project,long-project-name=Short"
|
|
205
|
+
*
|
|
206
|
+
* Environment variable approach is used because:
|
|
207
|
+
* - No config file needed - simple and lightweight configuration
|
|
208
|
+
* - Works in any shell/environment without file management
|
|
209
|
+
* - Easy to set temporarily for specific invocations
|
|
210
|
+
* - Follows Unix conventions for tool configuration
|
|
211
|
+
* - Doesn't require file system permissions or config directory setup
|
|
212
|
+
*
|
|
213
|
+
* Used to override display names for project directories in all output formats.
|
|
214
|
+
*/
|
|
215
|
+
const PROJECT_ALIASES_ENV = "CCUSAGE_PROJECT_ALIASES";
|
|
216
|
+
/**
|
|
195
217
|
* Claude projects directory name within the data directory
|
|
196
218
|
* Contains subdirectories for each project with usage data
|
|
197
219
|
*/
|
|
@@ -324,14 +346,6 @@ var PricingFetcher = class {
|
|
|
324
346
|
loadOfflinePricing = try_({
|
|
325
347
|
try: async () => {
|
|
326
348
|
const pricing = new Map(Object.entries({
|
|
327
|
-
"claude-instant-1": {
|
|
328
|
-
"input_cost_per_token": 163e-8,
|
|
329
|
-
"output_cost_per_token": 551e-8
|
|
330
|
-
},
|
|
331
|
-
"claude-instant-1.2": {
|
|
332
|
-
"input_cost_per_token": 163e-9,
|
|
333
|
-
"output_cost_per_token": 551e-9
|
|
334
|
-
},
|
|
335
349
|
"claude-2": {
|
|
336
350
|
"input_cost_per_token": 8e-6,
|
|
337
351
|
"output_cost_per_token": 24e-6
|
|
@@ -546,4 +560,4 @@ var PricingFetcher = class {
|
|
|
546
560
|
return cost;
|
|
547
561
|
}
|
|
548
562
|
};
|
|
549
|
-
export { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, BURN_RATE_THRESHOLDS, CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, DEFAULT_REFRESH_INTERVAL_SECONDS, MAX_REFRESH_INTERVAL_SECONDS, MCP_DEFAULT_PORT, MIN_REFRESH_INTERVAL_SECONDS, MIN_RENDER_INTERVAL_MS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, isFailure, isPromise, require_usingCtx, try_ };
|
|
563
|
+
export { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, BURN_RATE_THRESHOLDS, CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, DEFAULT_REFRESH_INTERVAL_SECONDS, MAX_REFRESH_INTERVAL_SECONDS, MCP_DEFAULT_PORT, MIN_REFRESH_INTERVAL_SECONDS, MIN_RENDER_INTERVAL_MS, PROJECT_ALIASES_ENV, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, isFailure, isPromise, require_usingCtx, try_ };
|
package/dist/pricing-fetcher.js
CHANGED
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccusage",
|
|
3
|
-
"
|
|
4
|
-
"version": "15.3.1",
|
|
3
|
+
"version": "15.5.0",
|
|
5
4
|
"description": "Usage analysis tool for Claude Code",
|
|
6
|
-
"author": "ryoppippi",
|
|
7
|
-
"license": "MIT",
|
|
8
|
-
"funding": "https://github.com/ryoppippi/ccusage?sponsor=1",
|
|
9
5
|
"homepage": "https://github.com/ryoppippi/ccusage#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/ryoppippi/ccusage/issues"
|
|
8
|
+
},
|
|
10
9
|
"repository": {
|
|
11
10
|
"type": "git",
|
|
12
11
|
"url": "git+https://github.com/ryoppippi/ccusage.git"
|
|
13
12
|
},
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
"funding": "https://github.com/ryoppippi/ccusage?sponsor=1",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "ryoppippi",
|
|
16
|
+
"type": "module",
|
|
17
17
|
"exports": {
|
|
18
18
|
".": "./dist/index.js",
|
|
19
19
|
"./calculate-cost": "./dist/calculate-cost.js",
|
|
@@ -32,6 +32,6 @@
|
|
|
32
32
|
"dist"
|
|
33
33
|
],
|
|
34
34
|
"engines": {
|
|
35
|
-
"node": ">=20.19.
|
|
35
|
+
"node": ">=20.19.4"
|
|
36
36
|
}
|
|
37
37
|
}
|