ccusage 9.0.0 → 10.0.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 +65 -1
- package/dist/calculate-cost.d.ts +1 -1
- package/dist/{data-loader-Ca8k_uX0.js → data-loader-Bp9KV0_a.js} +198 -3
- package/dist/{data-loader-OGaMjZTD.d.ts → data-loader-DPQaq8_n.d.ts} +34 -2
- package/dist/data-loader.d.ts +2 -2
- package/dist/data-loader.js +4 -4
- package/dist/{debug-zCcXwR8p.js → debug-bjc38_Gx.js} +3 -3
- package/dist/debug.js +4 -4
- package/dist/index.js +292 -9
- package/dist/{logger-DKw-DPXD.js → logger-C2GZVy1v.js} +1 -1
- package/dist/logger.js +1 -1
- package/dist/{mcp-BRFYI5rd.js → mcp-DVjQeqCy.js} +3 -3
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +4 -4
- package/dist/{pricing-fetcher-BlxDpqFj.js → pricing-fetcher-BxlbW4km.js} +1 -1
- package/dist/pricing-fetcher.js +2 -2
- package/dist/{utils.table-DRzF8vZJ.js → utils.table-USks3NGv.js} +8 -2
- package/dist/utils.table.d.ts +4 -1
- package/dist/utils.table.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,10 +45,12 @@ This tool helps you understand the value you're getting from your subscription b
|
|
|
45
45
|
- 📊 **Model Breakdown**: View per-model cost breakdown with `--breakdown` flag
|
|
46
46
|
- 📅 **Date Filtering**: Filter reports by date range using `--since` and `--until`
|
|
47
47
|
- 📁 **Custom Path**: Support for custom Claude data directory locations
|
|
48
|
-
- 🎨 **Beautiful Output**: Colorful table-formatted display
|
|
48
|
+
- 🎨 **Beautiful Output**: Colorful table-formatted display with responsive width adjustment
|
|
49
49
|
- 📄 **JSON Output**: Export data in structured JSON format with `--json`
|
|
50
50
|
- 💰 **Cost Tracking**: Shows costs in USD for each day/month/session
|
|
51
51
|
- 🔄 **Cache Token Support**: Tracks and displays cache creation and cache read tokens separately
|
|
52
|
+
- 🌐 **Offline Mode**: Use pre-cached pricing data without network connectivity with `--offline` (Claude models only)
|
|
53
|
+
- 📏 **Responsive Tables**: Automatic table width adjustment for narrow terminals with intelligent word wrapping
|
|
52
54
|
|
|
53
55
|
## Important Disclaimer
|
|
54
56
|
|
|
@@ -147,6 +149,10 @@ ccusage daily --order desc # Show newest dates first (default)
|
|
|
147
149
|
|
|
148
150
|
# Show per-model cost breakdown
|
|
149
151
|
ccusage daily --breakdown # Show cost breakdown by model (opus-4, sonnet-4, etc.)
|
|
152
|
+
|
|
153
|
+
# Use offline mode (no network required)
|
|
154
|
+
ccusage daily --offline # Use pre-cached pricing data
|
|
155
|
+
ccusage daily -O # Short alias for --offline
|
|
150
156
|
```
|
|
151
157
|
|
|
152
158
|
`ccusage` is an alias for `ccusage daily`, so you can run it without specifying the subcommand.
|
|
@@ -183,6 +189,10 @@ ccusage monthly --order desc # Show newest months first (default)
|
|
|
183
189
|
|
|
184
190
|
# Show per-model cost breakdown
|
|
185
191
|
ccusage monthly --breakdown # Show cost breakdown by model
|
|
192
|
+
|
|
193
|
+
# Use offline mode (no network required)
|
|
194
|
+
ccusage monthly --offline # Use pre-cached pricing data
|
|
195
|
+
ccusage monthly -O # Short alias for --offline
|
|
186
196
|
```
|
|
187
197
|
|
|
188
198
|
### Session Report
|
|
@@ -214,8 +224,61 @@ ccusage session --order desc # Show newest sessions first (default)
|
|
|
214
224
|
|
|
215
225
|
# Show per-model cost breakdown
|
|
216
226
|
ccusage session --breakdown # Show cost breakdown by model
|
|
227
|
+
|
|
228
|
+
# Use offline mode (no network required)
|
|
229
|
+
ccusage session --offline # Use pre-cached pricing data
|
|
230
|
+
ccusage session -O # Short alias for --offline
|
|
217
231
|
```
|
|
218
232
|
|
|
233
|
+
### 5-Hour Blocks Report
|
|
234
|
+
|
|
235
|
+
Shows usage grouped by Claude's 5-hour billing windows:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
# Show all 5-hour blocks
|
|
239
|
+
ccusage blocks
|
|
240
|
+
|
|
241
|
+
# Show only the active block with detailed projections
|
|
242
|
+
ccusage blocks --active
|
|
243
|
+
|
|
244
|
+
# Show blocks from the last 3 days (including active)
|
|
245
|
+
ccusage blocks --recent
|
|
246
|
+
|
|
247
|
+
# Set a token limit to see if you'll exceed it
|
|
248
|
+
ccusage blocks -t 500000
|
|
249
|
+
|
|
250
|
+
# Use the highest previous block as the token limit
|
|
251
|
+
ccusage blocks -t max
|
|
252
|
+
|
|
253
|
+
# Combine options
|
|
254
|
+
ccusage blocks --recent -t max
|
|
255
|
+
|
|
256
|
+
# Output in JSON format
|
|
257
|
+
ccusage blocks --json
|
|
258
|
+
|
|
259
|
+
# Control cost calculation mode
|
|
260
|
+
ccusage blocks --mode auto # Use costUSD when available, calculate otherwise (default)
|
|
261
|
+
ccusage blocks --mode calculate # Always calculate costs from tokens
|
|
262
|
+
ccusage blocks --mode display # Always show pre-calculated costUSD values
|
|
263
|
+
|
|
264
|
+
# Control sort order
|
|
265
|
+
ccusage blocks --order asc # Show oldest blocks first
|
|
266
|
+
ccusage blocks --order desc # Show newest blocks first (default)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
The blocks report helps you understand Claude Code's 5-hour rolling session windows:
|
|
270
|
+
|
|
271
|
+
- Sessions start with your first message and last for 5 hours
|
|
272
|
+
- Shows active blocks with time remaining and burn rate projections
|
|
273
|
+
- Helps track if you're approaching token limits within a session
|
|
274
|
+
- The `-t max` option automatically uses your highest previous block as the limit
|
|
275
|
+
|
|
276
|
+
#### Blocks-specific options:
|
|
277
|
+
|
|
278
|
+
- `-t, --token-limit <number|max>`: Set token limit for quota warnings (use "max" for highest previous block)
|
|
279
|
+
- `-a, --active`: Show only active block with detailed projections
|
|
280
|
+
- `-r, --recent`: Show blocks from last 3 days (including active)
|
|
281
|
+
|
|
219
282
|
### Options
|
|
220
283
|
|
|
221
284
|
All commands support the following options:
|
|
@@ -226,6 +289,7 @@ All commands support the following options:
|
|
|
226
289
|
- `-m, --mode <mode>`: Cost calculation mode: `auto` (default), `calculate`, or `display`
|
|
227
290
|
- `-o, --order <order>`: Sort order: `desc` (newest first, default) or `asc` (oldest first).
|
|
228
291
|
- `-b, --breakdown`: Show per-model cost breakdown (splits usage by Opus, Sonnet, etc.)
|
|
292
|
+
- `-O, --offline`: Use pre-cached pricing data for Claude models (no network connection required)
|
|
229
293
|
- `-d, --debug`: Show pricing mismatch information for debugging
|
|
230
294
|
- `--debug-samples <number>`: Number of sample discrepancies to show in debug output (default: 5)
|
|
231
295
|
- `-h, --help`: Display help message
|
package/dist/calculate-cost.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./pricing-fetcher-BkSZh4lR.js";
|
|
2
|
-
import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-
|
|
2
|
+
import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-DPQaq8_n.js";
|
|
3
3
|
|
|
4
4
|
//#region src/calculate-cost.d.ts
|
|
5
5
|
type TokenData = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { __commonJS, __require, __toESM } from "./chunk-BLXvPPr8.js";
|
|
2
2
|
import { array, number, object, optional, pipe, regex, safeParse, string } from "./dist-DCvt9hEv.js";
|
|
3
|
-
import { logger } from "./logger-
|
|
4
|
-
import { PricingFetcher } from "./pricing-fetcher-
|
|
3
|
+
import { logger } from "./logger-C2GZVy1v.js";
|
|
4
|
+
import { PricingFetcher } from "./pricing-fetcher-BxlbW4km.js";
|
|
5
5
|
import fsPromises, { readFile } from "node:fs/promises";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import path from "node:path";
|
|
@@ -3041,6 +3041,144 @@ async function glob(patternsOrOptions, options) {
|
|
|
3041
3041
|
return crawl(opts, cwd, false);
|
|
3042
3042
|
}
|
|
3043
3043
|
|
|
3044
|
+
//#endregion
|
|
3045
|
+
//#region src/five-hour-blocks.internal.ts
|
|
3046
|
+
const FIVE_HOURS_MS = 5 * 60 * 60 * 1e3;
|
|
3047
|
+
const DEFAULT_RECENT_DAYS = 3;
|
|
3048
|
+
function identifyFiveHourBlocks(entries) {
|
|
3049
|
+
if (entries.length === 0) return [];
|
|
3050
|
+
const blocks = [];
|
|
3051
|
+
const sortedEntries = [...entries].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
3052
|
+
let currentBlockStart = null;
|
|
3053
|
+
let currentBlockEntries = [];
|
|
3054
|
+
const now = /* @__PURE__ */ new Date();
|
|
3055
|
+
for (const entry of sortedEntries) {
|
|
3056
|
+
const entryTime = entry.timestamp;
|
|
3057
|
+
if (currentBlockStart == null) {
|
|
3058
|
+
currentBlockStart = entryTime;
|
|
3059
|
+
currentBlockEntries = [entry];
|
|
3060
|
+
} else {
|
|
3061
|
+
const timeSinceBlockStart = entryTime.getTime() - currentBlockStart.getTime();
|
|
3062
|
+
const lastEntry = currentBlockEntries.at(-1);
|
|
3063
|
+
if (lastEntry == null) continue;
|
|
3064
|
+
const lastEntryTime = lastEntry.timestamp;
|
|
3065
|
+
const timeSinceLastEntry = entryTime.getTime() - lastEntryTime.getTime();
|
|
3066
|
+
if (timeSinceBlockStart > FIVE_HOURS_MS || timeSinceLastEntry > FIVE_HOURS_MS) {
|
|
3067
|
+
const block = createBlock(currentBlockStart, currentBlockEntries, now);
|
|
3068
|
+
blocks.push(block);
|
|
3069
|
+
if (timeSinceLastEntry > FIVE_HOURS_MS) {
|
|
3070
|
+
const gapBlock = createGapBlock(lastEntryTime, entryTime);
|
|
3071
|
+
if (gapBlock != null) blocks.push(gapBlock);
|
|
3072
|
+
}
|
|
3073
|
+
currentBlockStart = entryTime;
|
|
3074
|
+
currentBlockEntries = [entry];
|
|
3075
|
+
} else currentBlockEntries.push(entry);
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
if (currentBlockStart != null && currentBlockEntries.length > 0) {
|
|
3079
|
+
const block = createBlock(currentBlockStart, currentBlockEntries, now);
|
|
3080
|
+
blocks.push(block);
|
|
3081
|
+
}
|
|
3082
|
+
return blocks;
|
|
3083
|
+
}
|
|
3084
|
+
function createBlock(startTime, entries, now) {
|
|
3085
|
+
const endTime = new Date(startTime.getTime() + FIVE_HOURS_MS);
|
|
3086
|
+
const lastEntry = entries[entries.length - 1];
|
|
3087
|
+
const actualEndTime = lastEntry != null ? lastEntry.timestamp : startTime;
|
|
3088
|
+
const isActive = now.getTime() - actualEndTime.getTime() < FIVE_HOURS_MS && now < endTime;
|
|
3089
|
+
const tokenCounts = {
|
|
3090
|
+
inputTokens: 0,
|
|
3091
|
+
outputTokens: 0,
|
|
3092
|
+
cacheCreationInputTokens: 0,
|
|
3093
|
+
cacheReadInputTokens: 0
|
|
3094
|
+
};
|
|
3095
|
+
let costUSD = 0;
|
|
3096
|
+
const modelsSet = /* @__PURE__ */ new Set();
|
|
3097
|
+
for (const entry of entries) {
|
|
3098
|
+
tokenCounts.inputTokens += entry.usage.inputTokens;
|
|
3099
|
+
tokenCounts.outputTokens += entry.usage.outputTokens;
|
|
3100
|
+
tokenCounts.cacheCreationInputTokens += entry.usage.cacheCreationInputTokens;
|
|
3101
|
+
tokenCounts.cacheReadInputTokens += entry.usage.cacheReadInputTokens;
|
|
3102
|
+
costUSD += entry.costUSD != null ? entry.costUSD : 0;
|
|
3103
|
+
modelsSet.add(entry.model);
|
|
3104
|
+
}
|
|
3105
|
+
return {
|
|
3106
|
+
id: startTime.toISOString(),
|
|
3107
|
+
startTime,
|
|
3108
|
+
endTime,
|
|
3109
|
+
actualEndTime,
|
|
3110
|
+
isActive,
|
|
3111
|
+
entries,
|
|
3112
|
+
tokenCounts,
|
|
3113
|
+
costUSD,
|
|
3114
|
+
models: Array.from(modelsSet)
|
|
3115
|
+
};
|
|
3116
|
+
}
|
|
3117
|
+
function createGapBlock(lastActivityTime, nextActivityTime) {
|
|
3118
|
+
const gapDuration = nextActivityTime.getTime() - lastActivityTime.getTime();
|
|
3119
|
+
if (gapDuration <= FIVE_HOURS_MS) return null;
|
|
3120
|
+
const gapStart = new Date(lastActivityTime.getTime() + FIVE_HOURS_MS);
|
|
3121
|
+
const gapEnd = nextActivityTime;
|
|
3122
|
+
return {
|
|
3123
|
+
id: `gap-${gapStart.toISOString()}`,
|
|
3124
|
+
startTime: gapStart,
|
|
3125
|
+
endTime: gapEnd,
|
|
3126
|
+
isActive: false,
|
|
3127
|
+
isGap: true,
|
|
3128
|
+
entries: [],
|
|
3129
|
+
tokenCounts: {
|
|
3130
|
+
inputTokens: 0,
|
|
3131
|
+
outputTokens: 0,
|
|
3132
|
+
cacheCreationInputTokens: 0,
|
|
3133
|
+
cacheReadInputTokens: 0
|
|
3134
|
+
},
|
|
3135
|
+
costUSD: 0,
|
|
3136
|
+
models: []
|
|
3137
|
+
};
|
|
3138
|
+
}
|
|
3139
|
+
function calculateBurnRate(block) {
|
|
3140
|
+
if (block.entries.length === 0 || (block.isGap ?? false)) return null;
|
|
3141
|
+
const firstEntryData = block.entries[0];
|
|
3142
|
+
const lastEntryData = block.entries[block.entries.length - 1];
|
|
3143
|
+
if (firstEntryData == null || lastEntryData == null) return null;
|
|
3144
|
+
const firstEntry = firstEntryData.timestamp;
|
|
3145
|
+
const lastEntry = lastEntryData.timestamp;
|
|
3146
|
+
const durationMinutes = (lastEntry.getTime() - firstEntry.getTime()) / (1e3 * 60);
|
|
3147
|
+
if (durationMinutes <= 0) return null;
|
|
3148
|
+
const totalTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
|
|
3149
|
+
const tokensPerMinute = totalTokens / durationMinutes;
|
|
3150
|
+
const costPerHour = block.costUSD / durationMinutes * 60;
|
|
3151
|
+
return {
|
|
3152
|
+
tokensPerMinute,
|
|
3153
|
+
costPerHour
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3156
|
+
function projectBlockUsage(block) {
|
|
3157
|
+
if (!block.isActive || (block.isGap ?? false)) return null;
|
|
3158
|
+
const burnRate = calculateBurnRate(block);
|
|
3159
|
+
if (burnRate == null) return null;
|
|
3160
|
+
const now = /* @__PURE__ */ new Date();
|
|
3161
|
+
const remainingTime = block.endTime.getTime() - now.getTime();
|
|
3162
|
+
const remainingMinutes = Math.max(0, remainingTime / (1e3 * 60));
|
|
3163
|
+
const currentTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
|
|
3164
|
+
const projectedAdditionalTokens = burnRate.tokensPerMinute * remainingMinutes;
|
|
3165
|
+
const totalTokens = currentTokens + projectedAdditionalTokens;
|
|
3166
|
+
const projectedAdditionalCost = burnRate.costPerHour / 60 * remainingMinutes;
|
|
3167
|
+
const totalCost = block.costUSD + projectedAdditionalCost;
|
|
3168
|
+
return {
|
|
3169
|
+
totalTokens: Math.round(totalTokens),
|
|
3170
|
+
totalCost: Math.round(totalCost * 100) / 100,
|
|
3171
|
+
remainingMinutes: Math.round(remainingMinutes)
|
|
3172
|
+
};
|
|
3173
|
+
}
|
|
3174
|
+
function filterRecentBlocks(blocks, days = DEFAULT_RECENT_DAYS) {
|
|
3175
|
+
const now = /* @__PURE__ */ new Date();
|
|
3176
|
+
const cutoffTime = new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
|
|
3177
|
+
return blocks.filter((block) => {
|
|
3178
|
+
return block.startTime >= cutoffTime || block.isActive;
|
|
3179
|
+
});
|
|
3180
|
+
}
|
|
3181
|
+
|
|
3044
3182
|
//#endregion
|
|
3045
3183
|
//#region node_modules/rolldown/node_modules/@oxc-project/runtime/src/helpers/usingCtx.js
|
|
3046
3184
|
var require_usingCtx = __commonJS({ "node_modules/rolldown/node_modules/@oxc-project/runtime/src/helpers/usingCtx.js"(exports, module) {
|
|
@@ -3553,6 +3691,63 @@ async function loadMonthlyUsageData(options) {
|
|
|
3553
3691
|
}
|
|
3554
3692
|
return sortByDate(monthlyArray, (item) => `${item.month}-01`, options?.order);
|
|
3555
3693
|
}
|
|
3694
|
+
async function loadFiveHourBlockData(options) {
|
|
3695
|
+
try {
|
|
3696
|
+
var _usingCtx4 = (0, import_usingCtx.default)();
|
|
3697
|
+
const claudePath = options?.claudePath ?? getDefaultClaudePath();
|
|
3698
|
+
const claudeDir = path.join(claudePath, "projects");
|
|
3699
|
+
const files = await glob(["**/*.jsonl"], {
|
|
3700
|
+
cwd: claudeDir,
|
|
3701
|
+
absolute: true
|
|
3702
|
+
});
|
|
3703
|
+
if (files.length === 0) return [];
|
|
3704
|
+
const sortedFiles = await sortFilesByTimestamp(files);
|
|
3705
|
+
const mode = options?.mode ?? "auto";
|
|
3706
|
+
const fetcher = _usingCtx4.u(mode === "display" ? null : new PricingFetcher());
|
|
3707
|
+
const processedHashes = /* @__PURE__ */ new Set();
|
|
3708
|
+
const allEntries = [];
|
|
3709
|
+
for (const file of sortedFiles) {
|
|
3710
|
+
const content = await readFile(file, "utf-8");
|
|
3711
|
+
const lines = content.trim().split("\n").filter((line) => line.length > 0);
|
|
3712
|
+
for (const line of lines) try {
|
|
3713
|
+
const parsed = JSON.parse(line);
|
|
3714
|
+
const result = safeParse(UsageDataSchema, parsed);
|
|
3715
|
+
if (!result.success) continue;
|
|
3716
|
+
const data = result.output;
|
|
3717
|
+
const uniqueHash = createUniqueHash(data);
|
|
3718
|
+
if (isDuplicateEntry(uniqueHash, processedHashes)) continue;
|
|
3719
|
+
markAsProcessed(uniqueHash, processedHashes);
|
|
3720
|
+
const cost = fetcher != null ? await calculateCostForEntry(data, mode, fetcher) : data.costUSD ?? 0;
|
|
3721
|
+
allEntries.push({
|
|
3722
|
+
timestamp: new Date(data.timestamp),
|
|
3723
|
+
usage: {
|
|
3724
|
+
inputTokens: data.message.usage.input_tokens,
|
|
3725
|
+
outputTokens: data.message.usage.output_tokens,
|
|
3726
|
+
cacheCreationInputTokens: data.message.usage.cache_creation_input_tokens ?? 0,
|
|
3727
|
+
cacheReadInputTokens: data.message.usage.cache_read_input_tokens ?? 0
|
|
3728
|
+
},
|
|
3729
|
+
costUSD: cost,
|
|
3730
|
+
model: data.message.model ?? "unknown",
|
|
3731
|
+
version: data.version
|
|
3732
|
+
});
|
|
3733
|
+
} catch (error) {
|
|
3734
|
+
logger.debug(`Skipping invalid JSON line in 5-hour blocks: ${error instanceof Error ? error.message : String(error)}`);
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3737
|
+
const blocks = identifyFiveHourBlocks(allEntries);
|
|
3738
|
+
const filtered = options?.since != null && options.since !== "" || options?.until != null && options.until !== "" ? blocks.filter((block) => {
|
|
3739
|
+
const blockDateStr = formatDate(block.startTime.toISOString()).replace(/-/g, "");
|
|
3740
|
+
if (options.since != null && options.since !== "" && blockDateStr < options.since) return false;
|
|
3741
|
+
if (options.until != null && options.until !== "" && blockDateStr > options.until) return false;
|
|
3742
|
+
return true;
|
|
3743
|
+
}) : blocks;
|
|
3744
|
+
return sortByDate(filtered, (block) => block.startTime, options?.order);
|
|
3745
|
+
} catch (_) {
|
|
3746
|
+
_usingCtx4.e = _;
|
|
3747
|
+
} finally {
|
|
3748
|
+
_usingCtx4.d();
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3556
3751
|
|
|
3557
3752
|
//#endregion
|
|
3558
|
-
export { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, glob, loadDailyUsageData, loadMonthlyUsageData, loadSessionData, require_usingCtx, sortFilesByTimestamp };
|
|
3753
|
+
export { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateBurnRate, calculateCostForEntry, createUniqueHash, filterRecentBlocks, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, glob, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, projectBlockUsage, require_usingCtx, sortFilesByTimestamp };
|
|
@@ -1,7 +1,38 @@
|
|
|
1
1
|
import { ArraySchema, CostMode, InferOutput, NumberSchema, ObjectSchema, OptionalSchema, PricingFetcher, RegexAction, SchemaWithPipe, SortOrder, StringSchema } from "./pricing-fetcher-BkSZh4lR.js";
|
|
2
2
|
|
|
3
|
+
//#region src/five-hour-blocks.internal.d.ts
|
|
4
|
+
type LoadedUsageEntry = {
|
|
5
|
+
timestamp: Date;
|
|
6
|
+
usage: {
|
|
7
|
+
inputTokens: number;
|
|
8
|
+
outputTokens: number;
|
|
9
|
+
cacheCreationInputTokens: number;
|
|
10
|
+
cacheReadInputTokens: number;
|
|
11
|
+
};
|
|
12
|
+
costUSD: number | null;
|
|
13
|
+
model: string;
|
|
14
|
+
version?: string;
|
|
15
|
+
};
|
|
16
|
+
type TokenCounts = {
|
|
17
|
+
inputTokens: number;
|
|
18
|
+
outputTokens: number;
|
|
19
|
+
cacheCreationInputTokens: number;
|
|
20
|
+
cacheReadInputTokens: number;
|
|
21
|
+
};
|
|
22
|
+
type FiveHourBlock = {
|
|
23
|
+
id: string;
|
|
24
|
+
startTime: Date;
|
|
25
|
+
endTime: Date;
|
|
26
|
+
actualEndTime?: Date;
|
|
27
|
+
isActive: boolean;
|
|
28
|
+
isGap?: boolean;
|
|
29
|
+
entries: LoadedUsageEntry[];
|
|
30
|
+
tokenCounts: TokenCounts;
|
|
31
|
+
costUSD: number;
|
|
32
|
+
models: string[];
|
|
33
|
+
};
|
|
34
|
+
//#endregion
|
|
3
35
|
//#region src/data-loader.d.ts
|
|
4
|
-
|
|
5
36
|
/**
|
|
6
37
|
* Default path for Claude data directory
|
|
7
38
|
* Uses environment variable CLAUDE_CONFIG_DIR if set, otherwise defaults to ~/.claude
|
|
@@ -120,5 +151,6 @@ type LoadOptions = {
|
|
|
120
151
|
declare function loadDailyUsageData(options?: LoadOptions): Promise<DailyUsage[]>;
|
|
121
152
|
declare function loadSessionData(options?: LoadOptions): Promise<SessionUsage[]>;
|
|
122
153
|
declare function loadMonthlyUsageData(options?: LoadOptions): Promise<MonthlyUsage[]>;
|
|
154
|
+
declare function loadFiveHourBlockData(options?: LoadOptions): Promise<FiveHourBlock[]>;
|
|
123
155
|
//#endregion
|
|
124
|
-
export { DailyUsage, DailyUsageSchema, DateFilter, LoadOptions, ModelBreakdown, ModelBreakdownSchema, MonthlyUsage, MonthlyUsageSchema, SessionUsage, SessionUsageSchema, UsageData, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp };
|
|
156
|
+
export { DailyUsage, DailyUsageSchema, DateFilter, LoadOptions, ModelBreakdown, ModelBreakdownSchema, MonthlyUsage, MonthlyUsageSchema, SessionUsage, SessionUsageSchema, UsageData, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp };
|
package/dist/data-loader.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import "./pricing-fetcher-BkSZh4lR.js";
|
|
2
|
-
import { DailyUsage, DailyUsageSchema, DateFilter, LoadOptions, ModelBreakdown, ModelBreakdownSchema, MonthlyUsage, MonthlyUsageSchema, SessionUsage, SessionUsageSchema, UsageData, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp } from "./data-loader-
|
|
3
|
-
export { DailyUsage, DailyUsageSchema, DateFilter, LoadOptions, ModelBreakdown, ModelBreakdownSchema, MonthlyUsage, MonthlyUsageSchema, SessionUsage, SessionUsageSchema, UsageData, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp };
|
|
2
|
+
import { DailyUsage, DailyUsageSchema, DateFilter, LoadOptions, ModelBreakdown, ModelBreakdownSchema, MonthlyUsage, MonthlyUsageSchema, SessionUsage, SessionUsageSchema, UsageData, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp } from "./data-loader-DPQaq8_n.js";
|
|
3
|
+
export { DailyUsage, DailyUsageSchema, DateFilter, LoadOptions, ModelBreakdown, ModelBreakdownSchema, MonthlyUsage, MonthlyUsageSchema, SessionUsage, SessionUsageSchema, UsageData, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp };
|
package/dist/data-loader.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp } from "./data-loader-
|
|
1
|
+
import { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp } from "./data-loader-Bp9KV0_a.js";
|
|
2
2
|
import "./dist-DCvt9hEv.js";
|
|
3
|
-
import "./logger-
|
|
4
|
-
import "./pricing-fetcher-
|
|
3
|
+
import "./logger-C2GZVy1v.js";
|
|
4
|
+
import "./pricing-fetcher-BxlbW4km.js";
|
|
5
5
|
|
|
6
|
-
export { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp };
|
|
6
|
+
export { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { __toESM } from "./chunk-BLXvPPr8.js";
|
|
2
|
-
import { UsageDataSchema, glob, require_usingCtx } from "./data-loader-
|
|
2
|
+
import { UsageDataSchema, glob, require_usingCtx } from "./data-loader-Bp9KV0_a.js";
|
|
3
3
|
import { safeParse } from "./dist-DCvt9hEv.js";
|
|
4
|
-
import { logger } from "./logger-
|
|
5
|
-
import { PricingFetcher } from "./pricing-fetcher-
|
|
4
|
+
import { logger } from "./logger-C2GZVy1v.js";
|
|
5
|
+
import { PricingFetcher } from "./pricing-fetcher-BxlbW4km.js";
|
|
6
6
|
import { readFile } from "node:fs/promises";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import path from "node:path";
|
package/dist/debug.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import "./data-loader-
|
|
1
|
+
import "./data-loader-Bp9KV0_a.js";
|
|
2
2
|
import "./dist-DCvt9hEv.js";
|
|
3
|
-
import "./logger-
|
|
4
|
-
import "./pricing-fetcher-
|
|
5
|
-
import { detectMismatches, printMismatchReport } from "./debug-
|
|
3
|
+
import "./logger-C2GZVy1v.js";
|
|
4
|
+
import "./pricing-fetcher-BxlbW4km.js";
|
|
5
|
+
import { detectMismatches, printMismatchReport } from "./debug-bjc38_Gx.js";
|
|
6
6
|
|
|
7
7
|
export { detectMismatches, printMismatchReport };
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { __commonJS, __toESM } from "./chunk-BLXvPPr8.js";
|
|
3
3
|
import { calculateTotals, createTotalsObject, getTotalTokens } from "./calculate-cost-2IwHSzmi.js";
|
|
4
|
-
import { formatDateCompact, getDefaultClaudePath, loadDailyUsageData, loadMonthlyUsageData, loadSessionData } from "./data-loader-
|
|
4
|
+
import { calculateBurnRate, filterRecentBlocks, formatDateCompact, getDefaultClaudePath, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, projectBlockUsage } from "./data-loader-Bp9KV0_a.js";
|
|
5
5
|
import { safeParse } from "./dist-DCvt9hEv.js";
|
|
6
|
-
import { description, log, logger, name, version } from "./logger-
|
|
7
|
-
import { CostModes, SortOrders, dateSchema } from "./pricing-fetcher-
|
|
8
|
-
import { detectMismatches, printMismatchReport } from "./debug-
|
|
9
|
-
import { ResponsiveTable } from "./utils.table-
|
|
6
|
+
import { description, log, logger, name, version } from "./logger-C2GZVy1v.js";
|
|
7
|
+
import { CostModes, SortOrders, dateSchema } from "./pricing-fetcher-BxlbW4km.js";
|
|
8
|
+
import { detectMismatches, printMismatchReport } from "./debug-bjc38_Gx.js";
|
|
9
|
+
import { ResponsiveTable } from "./utils.table-USks3NGv.js";
|
|
10
10
|
import "./types-BlyCnKwN.js";
|
|
11
|
-
import { createMcpServer } from "./mcp-
|
|
11
|
+
import { createMcpServer } from "./mcp-DVjQeqCy.js";
|
|
12
12
|
import "./index-CISmcbXk-CW1Gj6Ab.js";
|
|
13
13
|
import process$1 from "node:process";
|
|
14
14
|
|
|
@@ -1273,7 +1273,7 @@ const sharedArgs = {
|
|
|
1273
1273
|
type: "boolean",
|
|
1274
1274
|
negatable: true,
|
|
1275
1275
|
short: "O",
|
|
1276
|
-
description: "Use cached data instead of fetching from API",
|
|
1276
|
+
description: "Use cached pricing data for Claude models instead of fetching from API",
|
|
1277
1277
|
default: false
|
|
1278
1278
|
}
|
|
1279
1279
|
};
|
|
@@ -1284,7 +1284,7 @@ const sharedCommandConfig = {
|
|
|
1284
1284
|
|
|
1285
1285
|
//#endregion
|
|
1286
1286
|
//#region src/utils.internal.ts
|
|
1287
|
-
var import_picocolors$
|
|
1287
|
+
var import_picocolors$4 = __toESM(require_picocolors(), 1);
|
|
1288
1288
|
function formatNumber(num) {
|
|
1289
1289
|
return num.toLocaleString("en-US");
|
|
1290
1290
|
}
|
|
@@ -1313,12 +1313,294 @@ function pushBreakdownRows(table, breakdowns, extraColumns = 1, trailingColumns
|
|
|
1313
1313
|
const row = [` └─ ${formatModelName(breakdown.modelName)}`];
|
|
1314
1314
|
for (let i = 0; i < extraColumns; i++) row.push("");
|
|
1315
1315
|
const totalTokens = breakdown.inputTokens + breakdown.outputTokens + breakdown.cacheCreationTokens + breakdown.cacheReadTokens;
|
|
1316
|
-
row.push(import_picocolors$
|
|
1316
|
+
row.push(import_picocolors$4.default.gray(formatNumber(breakdown.inputTokens)), import_picocolors$4.default.gray(formatNumber(breakdown.outputTokens)), import_picocolors$4.default.gray(formatNumber(breakdown.cacheCreationTokens)), import_picocolors$4.default.gray(formatNumber(breakdown.cacheReadTokens)), import_picocolors$4.default.gray(formatNumber(totalTokens)), import_picocolors$4.default.gray(formatCurrency(breakdown.cost)));
|
|
1317
1317
|
for (let i = 0; i < trailingColumns; i++) row.push("");
|
|
1318
1318
|
table.push(row);
|
|
1319
1319
|
}
|
|
1320
1320
|
}
|
|
1321
1321
|
|
|
1322
|
+
//#endregion
|
|
1323
|
+
//#region src/commands/blocks.ts
|
|
1324
|
+
var import_picocolors$3 = __toESM(require_picocolors(), 1);
|
|
1325
|
+
const RECENT_DAYS_DEFAULT = 3;
|
|
1326
|
+
const WARNING_THRESHOLD = .8;
|
|
1327
|
+
const COMPACT_WIDTH_THRESHOLD = 120;
|
|
1328
|
+
const DEFAULT_TERMINAL_WIDTH = 120;
|
|
1329
|
+
function formatBlockTime(block, compact = false) {
|
|
1330
|
+
const start = compact ? block.startTime.toLocaleString(void 0, {
|
|
1331
|
+
month: "2-digit",
|
|
1332
|
+
day: "2-digit",
|
|
1333
|
+
hour: "2-digit",
|
|
1334
|
+
minute: "2-digit"
|
|
1335
|
+
}) : block.startTime.toLocaleString();
|
|
1336
|
+
if (block.isGap ?? false) {
|
|
1337
|
+
const end = compact ? block.endTime.toLocaleString(void 0, {
|
|
1338
|
+
hour: "2-digit",
|
|
1339
|
+
minute: "2-digit"
|
|
1340
|
+
}) : block.endTime.toLocaleString();
|
|
1341
|
+
const duration$1 = Math.round((block.endTime.getTime() - block.startTime.getTime()) / (1e3 * 60 * 60));
|
|
1342
|
+
return compact ? `${start}-${end}\n(${duration$1}h gap)` : `${start} - ${end} (${duration$1}h gap)`;
|
|
1343
|
+
}
|
|
1344
|
+
const duration = block.actualEndTime != null ? Math.round((block.actualEndTime.getTime() - block.startTime.getTime()) / (1e3 * 60)) : 0;
|
|
1345
|
+
if (block.isActive) {
|
|
1346
|
+
const now = /* @__PURE__ */ new Date();
|
|
1347
|
+
const elapsed = Math.round((now.getTime() - block.startTime.getTime()) / (1e3 * 60));
|
|
1348
|
+
const remaining = Math.round((block.endTime.getTime() - now.getTime()) / (1e3 * 60));
|
|
1349
|
+
const elapsedHours = Math.floor(elapsed / 60);
|
|
1350
|
+
const elapsedMins = elapsed % 60;
|
|
1351
|
+
const remainingHours = Math.floor(remaining / 60);
|
|
1352
|
+
const remainingMins = remaining % 60;
|
|
1353
|
+
if (compact) return `${start}\n(${elapsedHours}h${elapsedMins}m/${remainingHours}h${remainingMins}m)`;
|
|
1354
|
+
return `${start} (${elapsedHours}h ${elapsedMins}m elapsed, ${remainingHours}h ${remainingMins}m remaining)`;
|
|
1355
|
+
}
|
|
1356
|
+
const hours = Math.floor(duration / 60);
|
|
1357
|
+
const mins = duration % 60;
|
|
1358
|
+
if (compact) return hours > 0 ? `${start}\n(${hours}h${mins}m)` : `${start}\n(${mins}m)`;
|
|
1359
|
+
if (hours > 0) return `${start} (${hours}h ${mins}m)`;
|
|
1360
|
+
return `${start} (${mins}m)`;
|
|
1361
|
+
}
|
|
1362
|
+
function formatModels(models, compact = false) {
|
|
1363
|
+
if (models.length === 0) return "-";
|
|
1364
|
+
return compact ? formatModelsDisplay(models) : formatModelsDisplay(models);
|
|
1365
|
+
}
|
|
1366
|
+
function parseTokenLimit(value, maxFromAll) {
|
|
1367
|
+
if (value == null || value === "") return void 0;
|
|
1368
|
+
if (value === "max") return maxFromAll > 0 ? maxFromAll : void 0;
|
|
1369
|
+
const limit = Number.parseInt(value, 10);
|
|
1370
|
+
return Number.isNaN(limit) ? void 0 : limit;
|
|
1371
|
+
}
|
|
1372
|
+
const blocksCommand = define({
|
|
1373
|
+
name: "blocks",
|
|
1374
|
+
description: "Show usage report grouped by 5-hour billing blocks",
|
|
1375
|
+
args: {
|
|
1376
|
+
...sharedCommandConfig.args,
|
|
1377
|
+
active: {
|
|
1378
|
+
type: "boolean",
|
|
1379
|
+
short: "a",
|
|
1380
|
+
description: "Show only active block with projections",
|
|
1381
|
+
default: false
|
|
1382
|
+
},
|
|
1383
|
+
recent: {
|
|
1384
|
+
type: "boolean",
|
|
1385
|
+
short: "r",
|
|
1386
|
+
description: `Show blocks from last ${RECENT_DAYS_DEFAULT} days (including active)`,
|
|
1387
|
+
default: false
|
|
1388
|
+
},
|
|
1389
|
+
tokenLimit: {
|
|
1390
|
+
type: "string",
|
|
1391
|
+
short: "t",
|
|
1392
|
+
description: "Token limit for quota warnings (e.g., 500000 or \"max\" for highest previous block)"
|
|
1393
|
+
}
|
|
1394
|
+
},
|
|
1395
|
+
toKebab: true,
|
|
1396
|
+
async run(ctx) {
|
|
1397
|
+
if (ctx.values.json) logger.level = 0;
|
|
1398
|
+
let blocks = await loadFiveHourBlockData({
|
|
1399
|
+
since: ctx.values.since,
|
|
1400
|
+
until: ctx.values.until,
|
|
1401
|
+
claudePath: getDefaultClaudePath(),
|
|
1402
|
+
mode: ctx.values.mode,
|
|
1403
|
+
order: ctx.values.order
|
|
1404
|
+
});
|
|
1405
|
+
if (blocks.length === 0) {
|
|
1406
|
+
if (ctx.values.json) log(JSON.stringify({ blocks: [] }));
|
|
1407
|
+
else logger.warn("No Claude usage data found.");
|
|
1408
|
+
process$1.exit(0);
|
|
1409
|
+
}
|
|
1410
|
+
let maxTokensFromAll = 0;
|
|
1411
|
+
if (ctx.values.tokenLimit === "max") {
|
|
1412
|
+
for (const block of blocks) if (!(block.isGap ?? false) && !block.isActive) {
|
|
1413
|
+
const blockTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
|
|
1414
|
+
if (blockTokens > maxTokensFromAll) maxTokensFromAll = blockTokens;
|
|
1415
|
+
}
|
|
1416
|
+
if (!ctx.values.json && maxTokensFromAll > 0) logger.info(`Using max tokens from previous sessions: ${formatNumber(maxTokensFromAll)}`);
|
|
1417
|
+
}
|
|
1418
|
+
if (ctx.values.recent) blocks = filterRecentBlocks(blocks, RECENT_DAYS_DEFAULT);
|
|
1419
|
+
if (ctx.values.active) {
|
|
1420
|
+
blocks = blocks.filter((block) => block.isActive);
|
|
1421
|
+
if (blocks.length === 0) {
|
|
1422
|
+
if (ctx.values.json) log(JSON.stringify({
|
|
1423
|
+
blocks: [],
|
|
1424
|
+
message: "No active block"
|
|
1425
|
+
}));
|
|
1426
|
+
else logger.info("No active 5-hour block found.");
|
|
1427
|
+
process$1.exit(0);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
if (ctx.values.json) {
|
|
1431
|
+
const jsonOutput = { blocks: blocks.map((block) => {
|
|
1432
|
+
const burnRate = block.isActive ? calculateBurnRate(block) : null;
|
|
1433
|
+
const projection = block.isActive ? projectBlockUsage(block) : null;
|
|
1434
|
+
return {
|
|
1435
|
+
id: block.id,
|
|
1436
|
+
startTime: block.startTime.toISOString(),
|
|
1437
|
+
endTime: block.endTime.toISOString(),
|
|
1438
|
+
actualEndTime: block.actualEndTime != null ? block.actualEndTime.toISOString() : null,
|
|
1439
|
+
isActive: block.isActive,
|
|
1440
|
+
isGap: block.isGap ?? false,
|
|
1441
|
+
entries: block.entries.length,
|
|
1442
|
+
tokenCounts: block.tokenCounts,
|
|
1443
|
+
totalTokens: block.tokenCounts.inputTokens + block.tokenCounts.outputTokens,
|
|
1444
|
+
costUSD: block.costUSD,
|
|
1445
|
+
models: block.models,
|
|
1446
|
+
burnRate,
|
|
1447
|
+
projection,
|
|
1448
|
+
tokenLimitStatus: projection != null && ctx.values.tokenLimit != null ? (() => {
|
|
1449
|
+
const limit = parseTokenLimit(ctx.values.tokenLimit, maxTokensFromAll);
|
|
1450
|
+
return limit != null ? {
|
|
1451
|
+
limit,
|
|
1452
|
+
projectedUsage: projection.totalTokens,
|
|
1453
|
+
percentUsed: projection.totalTokens / limit * 100,
|
|
1454
|
+
status: projection.totalTokens > limit ? "exceeds" : projection.totalTokens > limit * WARNING_THRESHOLD ? "warning" : "ok"
|
|
1455
|
+
} : void 0;
|
|
1456
|
+
})() : void 0
|
|
1457
|
+
};
|
|
1458
|
+
}) };
|
|
1459
|
+
log(JSON.stringify(jsonOutput, null, 2));
|
|
1460
|
+
} else if (ctx.values.active && blocks.length === 1) {
|
|
1461
|
+
const block = blocks[0];
|
|
1462
|
+
if (block == null) {
|
|
1463
|
+
logger.warn("No active block found.");
|
|
1464
|
+
process$1.exit(0);
|
|
1465
|
+
}
|
|
1466
|
+
const burnRate = calculateBurnRate(block);
|
|
1467
|
+
const projection = projectBlockUsage(block);
|
|
1468
|
+
logger.box("Current 5-Hour Block Status");
|
|
1469
|
+
const now = /* @__PURE__ */ new Date();
|
|
1470
|
+
const elapsed = Math.round((now.getTime() - block.startTime.getTime()) / (1e3 * 60));
|
|
1471
|
+
const remaining = Math.round((block.endTime.getTime() - now.getTime()) / (1e3 * 60));
|
|
1472
|
+
log(`Block Started: ${import_picocolors$3.default.cyan(block.startTime.toLocaleString())} (${import_picocolors$3.default.yellow(`${Math.floor(elapsed / 60)}h ${elapsed % 60}m`)} ago)`);
|
|
1473
|
+
log(`Time Remaining: ${import_picocolors$3.default.green(`${Math.floor(remaining / 60)}h ${remaining % 60}m`)}\n`);
|
|
1474
|
+
log(import_picocolors$3.default.bold("Current Usage:"));
|
|
1475
|
+
log(` Input Tokens: ${formatNumber(block.tokenCounts.inputTokens)}`);
|
|
1476
|
+
log(` Output Tokens: ${formatNumber(block.tokenCounts.outputTokens)}`);
|
|
1477
|
+
log(` Total Cost: ${formatCurrency(block.costUSD)}\n`);
|
|
1478
|
+
if (burnRate != null) {
|
|
1479
|
+
log(import_picocolors$3.default.bold("Burn Rate:"));
|
|
1480
|
+
log(` Tokens/minute: ${formatNumber(burnRate.tokensPerMinute)}`);
|
|
1481
|
+
log(` Cost/hour: ${formatCurrency(burnRate.costPerHour)}\n`);
|
|
1482
|
+
}
|
|
1483
|
+
if (projection != null) {
|
|
1484
|
+
log(import_picocolors$3.default.bold("Projected Usage (if current rate continues):"));
|
|
1485
|
+
log(` Total Tokens: ${formatNumber(projection.totalTokens)}`);
|
|
1486
|
+
log(` Total Cost: ${formatCurrency(projection.totalCost)}\n`);
|
|
1487
|
+
if (ctx.values.tokenLimit != null) {
|
|
1488
|
+
const limit = parseTokenLimit(ctx.values.tokenLimit, maxTokensFromAll);
|
|
1489
|
+
if (limit != null && limit > 0) {
|
|
1490
|
+
const currentTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
|
|
1491
|
+
const remainingTokens = Math.max(0, limit - currentTokens);
|
|
1492
|
+
const percentUsed = projection.totalTokens / limit * 100;
|
|
1493
|
+
const status = percentUsed > 100 ? import_picocolors$3.default.red("EXCEEDS LIMIT") : percentUsed > WARNING_THRESHOLD * 100 ? import_picocolors$3.default.yellow("WARNING") : import_picocolors$3.default.green("OK");
|
|
1494
|
+
log(import_picocolors$3.default.bold("Token Limit Status:"));
|
|
1495
|
+
log(` Limit: ${formatNumber(limit)} tokens`);
|
|
1496
|
+
log(` Current Usage: ${formatNumber(currentTokens)} (${(currentTokens / limit * 100).toFixed(1)}%)`);
|
|
1497
|
+
log(` Remaining: ${formatNumber(remainingTokens)} tokens`);
|
|
1498
|
+
log(` Projected Usage: ${percentUsed.toFixed(1)}% ${status}`);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
} else {
|
|
1503
|
+
logger.box("Claude Code Token Usage Report - 5-Hour Blocks");
|
|
1504
|
+
const actualTokenLimit = parseTokenLimit(ctx.values.tokenLimit, maxTokensFromAll);
|
|
1505
|
+
const tableHeaders = [
|
|
1506
|
+
"Block Start",
|
|
1507
|
+
"Duration/Status",
|
|
1508
|
+
"Models",
|
|
1509
|
+
"Tokens"
|
|
1510
|
+
];
|
|
1511
|
+
const tableAligns = [
|
|
1512
|
+
"left",
|
|
1513
|
+
"left",
|
|
1514
|
+
"left",
|
|
1515
|
+
"right"
|
|
1516
|
+
];
|
|
1517
|
+
if (actualTokenLimit != null && actualTokenLimit > 0) {
|
|
1518
|
+
tableHeaders.push("%");
|
|
1519
|
+
tableAligns.push("right");
|
|
1520
|
+
}
|
|
1521
|
+
tableHeaders.push("Cost");
|
|
1522
|
+
tableAligns.push("right");
|
|
1523
|
+
const table = new ResponsiveTable({
|
|
1524
|
+
head: tableHeaders,
|
|
1525
|
+
style: { head: ["cyan"] },
|
|
1526
|
+
colAligns: tableAligns
|
|
1527
|
+
});
|
|
1528
|
+
const terminalWidth = process$1.stdout.columns || DEFAULT_TERMINAL_WIDTH;
|
|
1529
|
+
const useCompactFormat = terminalWidth < COMPACT_WIDTH_THRESHOLD;
|
|
1530
|
+
for (const block of blocks) if (block.isGap ?? false) {
|
|
1531
|
+
const gapRow = [
|
|
1532
|
+
import_picocolors$3.default.gray(formatBlockTime(block, useCompactFormat)),
|
|
1533
|
+
import_picocolors$3.default.gray("(inactive)"),
|
|
1534
|
+
import_picocolors$3.default.gray("-"),
|
|
1535
|
+
import_picocolors$3.default.gray("-")
|
|
1536
|
+
];
|
|
1537
|
+
if (actualTokenLimit != null && actualTokenLimit > 0) gapRow.push(import_picocolors$3.default.gray("-"));
|
|
1538
|
+
gapRow.push(import_picocolors$3.default.gray("-"));
|
|
1539
|
+
table.push(gapRow);
|
|
1540
|
+
} else {
|
|
1541
|
+
const totalTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
|
|
1542
|
+
const status = block.isActive ? import_picocolors$3.default.green("ACTIVE") : "";
|
|
1543
|
+
const row = [
|
|
1544
|
+
formatBlockTime(block, useCompactFormat),
|
|
1545
|
+
status,
|
|
1546
|
+
formatModels(block.models, useCompactFormat),
|
|
1547
|
+
formatNumber(totalTokens)
|
|
1548
|
+
];
|
|
1549
|
+
if (actualTokenLimit != null && actualTokenLimit > 0) {
|
|
1550
|
+
const percentage = totalTokens / actualTokenLimit * 100;
|
|
1551
|
+
const percentText = `${percentage.toFixed(1)}%`;
|
|
1552
|
+
row.push(percentage > 100 ? import_picocolors$3.default.red(percentText) : percentText);
|
|
1553
|
+
}
|
|
1554
|
+
row.push(formatCurrency(block.costUSD));
|
|
1555
|
+
table.push(row);
|
|
1556
|
+
if (block.isActive) {
|
|
1557
|
+
if (actualTokenLimit != null && actualTokenLimit > 0) {
|
|
1558
|
+
const currentTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
|
|
1559
|
+
const remainingTokens = Math.max(0, actualTokenLimit - currentTokens);
|
|
1560
|
+
const remainingText = remainingTokens > 0 ? formatNumber(remainingTokens) : import_picocolors$3.default.red("0");
|
|
1561
|
+
const remainingPercent = (actualTokenLimit - currentTokens) / actualTokenLimit * 100;
|
|
1562
|
+
const remainingPercentText = remainingPercent > 0 ? `${remainingPercent.toFixed(1)}%` : import_picocolors$3.default.red("0.0%");
|
|
1563
|
+
const remainingRow = [
|
|
1564
|
+
{
|
|
1565
|
+
content: import_picocolors$3.default.gray(`(assuming ${formatNumber(actualTokenLimit)} token limit)`),
|
|
1566
|
+
hAlign: "right"
|
|
1567
|
+
},
|
|
1568
|
+
import_picocolors$3.default.blue("REMAINING"),
|
|
1569
|
+
"",
|
|
1570
|
+
remainingText,
|
|
1571
|
+
remainingPercentText,
|
|
1572
|
+
""
|
|
1573
|
+
];
|
|
1574
|
+
table.push(remainingRow);
|
|
1575
|
+
}
|
|
1576
|
+
const projection = projectBlockUsage(block);
|
|
1577
|
+
if (projection != null) {
|
|
1578
|
+
const projectedTokens = formatNumber(projection.totalTokens);
|
|
1579
|
+
const projectedText = actualTokenLimit != null && actualTokenLimit > 0 && projection.totalTokens > actualTokenLimit ? import_picocolors$3.default.red(projectedTokens) : projectedTokens;
|
|
1580
|
+
const projectedRow = [
|
|
1581
|
+
{
|
|
1582
|
+
content: import_picocolors$3.default.gray("(assuming current burn rate)"),
|
|
1583
|
+
hAlign: "right"
|
|
1584
|
+
},
|
|
1585
|
+
import_picocolors$3.default.yellow("PROJECTED"),
|
|
1586
|
+
"",
|
|
1587
|
+
projectedText
|
|
1588
|
+
];
|
|
1589
|
+
if (actualTokenLimit != null && actualTokenLimit > 0) {
|
|
1590
|
+
const percentage = projection.totalTokens / actualTokenLimit * 100;
|
|
1591
|
+
const percentText = `${percentage.toFixed(1)}%`;
|
|
1592
|
+
projectedRow.push(percentText);
|
|
1593
|
+
}
|
|
1594
|
+
projectedRow.push(formatCurrency(projection.totalCost));
|
|
1595
|
+
table.push(projectedRow);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
log(table.toString());
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
});
|
|
1603
|
+
|
|
1322
1604
|
//#endregion
|
|
1323
1605
|
//#region src/commands/daily.ts
|
|
1324
1606
|
var import_picocolors$2 = __toESM(require_picocolors(), 1);
|
|
@@ -1701,6 +1983,7 @@ const subCommands = /* @__PURE__ */ new Map();
|
|
|
1701
1983
|
subCommands.set("daily", dailyCommand);
|
|
1702
1984
|
subCommands.set("monthly", monthlyCommand);
|
|
1703
1985
|
subCommands.set("session", sessionCommand);
|
|
1986
|
+
subCommands.set("blocks", blocksCommand);
|
|
1704
1987
|
subCommands.set("mcp", mcpCommand);
|
|
1705
1988
|
const mainCommand = dailyCommand;
|
|
1706
1989
|
await cli(process$1.argv.slice(2), mainCommand, {
|
package/dist/logger.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { __commonJS, __require, __toESM } from "./chunk-BLXvPPr8.js";
|
|
2
|
-
import { getDefaultClaudePath, loadDailyUsageData, loadSessionData } from "./data-loader-
|
|
2
|
+
import { getDefaultClaudePath, loadDailyUsageData, loadSessionData } from "./data-loader-Bp9KV0_a.js";
|
|
3
3
|
import { description, literal, object, optional, pipe, union } from "./dist-DCvt9hEv.js";
|
|
4
|
-
import { name, version } from "./logger-
|
|
5
|
-
import { CostModes, dateSchema } from "./pricing-fetcher-
|
|
4
|
+
import { name, version } from "./logger-C2GZVy1v.js";
|
|
5
|
+
import { CostModes, dateSchema } from "./pricing-fetcher-BxlbW4km.js";
|
|
6
6
|
import { anyType, arrayType, booleanType, discriminatedUnionType, enumType, literalType, numberType, objectType, optionalType, recordType, stringType, unionType, unknownType } from "./types-BlyCnKwN.js";
|
|
7
7
|
import { toJsonSchema } from "./index-CISmcbXk-CW1Gj6Ab.js";
|
|
8
8
|
import process$1 from "node:process";
|
package/dist/mcp.d.ts
CHANGED
package/dist/mcp.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import "./data-loader-
|
|
1
|
+
import "./data-loader-Bp9KV0_a.js";
|
|
2
2
|
import "./dist-DCvt9hEv.js";
|
|
3
|
-
import "./logger-
|
|
4
|
-
import "./pricing-fetcher-
|
|
3
|
+
import "./logger-C2GZVy1v.js";
|
|
4
|
+
import "./pricing-fetcher-BxlbW4km.js";
|
|
5
5
|
import "./types-BlyCnKwN.js";
|
|
6
|
-
import { createMcpServer } from "./mcp-
|
|
6
|
+
import { createMcpServer } from "./mcp-DVjQeqCy.js";
|
|
7
7
|
import "./index-CISmcbXk-CW1Gj6Ab.js";
|
|
8
8
|
|
|
9
9
|
export { createMcpServer };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { number, object, optional, pipe, regex, safeParse, string } from "./dist-DCvt9hEv.js";
|
|
2
|
-
import { logger } from "./logger-
|
|
2
|
+
import { logger } from "./logger-C2GZVy1v.js";
|
|
3
3
|
|
|
4
4
|
//#region src/consts.internal.ts
|
|
5
5
|
const LITELLM_PRICING_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
|
package/dist/pricing-fetcher.js
CHANGED
|
@@ -1769,7 +1769,10 @@ var ResponsiveTable = class {
|
|
|
1769
1769
|
toString() {
|
|
1770
1770
|
const terminalWidth = process$1.stdout.columns || 120;
|
|
1771
1771
|
const dataRows = this.rows.filter((row) => !this.isSeparatorRow(row));
|
|
1772
|
-
const allRows = [this.head.map(String), ...dataRows.map((row) => row.map(
|
|
1772
|
+
const allRows = [this.head.map(String), ...dataRows.map((row) => row.map((cell) => {
|
|
1773
|
+
if (typeof cell === "object" && cell != null && "content" in cell) return String(cell.content);
|
|
1774
|
+
return String(cell ?? "");
|
|
1775
|
+
}))];
|
|
1773
1776
|
const contentWidths = this.head.map((_, colIndex) => {
|
|
1774
1777
|
const maxLength = Math.max(...allRows.map((row) => stringWidth(String(row[colIndex] ?? ""))));
|
|
1775
1778
|
return maxLength;
|
|
@@ -1827,7 +1830,10 @@ var ResponsiveTable = class {
|
|
|
1827
1830
|
}
|
|
1828
1831
|
}
|
|
1829
1832
|
isSeparatorRow(row) {
|
|
1830
|
-
return row.every((cell) =>
|
|
1833
|
+
return row.every((cell) => {
|
|
1834
|
+
if (typeof cell === "object" && cell != null && "content" in cell) return cell.content === "" || /^─+$/.test(cell.content);
|
|
1835
|
+
return typeof cell === "string" && (cell === "" || /^─+$/.test(cell));
|
|
1836
|
+
});
|
|
1831
1837
|
}
|
|
1832
1838
|
isDateString(text) {
|
|
1833
1839
|
return /^\d{4}-\d{2}-\d{2}$/.test(text);
|
package/dist/utils.table.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
//#region src/utils.table.d.ts
|
|
2
|
-
type TableRow = (string | number
|
|
2
|
+
type TableRow = (string | number | {
|
|
3
|
+
content: string;
|
|
4
|
+
hAlign?: 'left' | 'right' | 'center';
|
|
5
|
+
})[];
|
|
3
6
|
type TableOptions = {
|
|
4
7
|
head: string[];
|
|
5
8
|
colAligns?: ('left' | 'right' | 'center')[];
|
package/dist/utils.table.js
CHANGED