ccusage 15.1.0 → 15.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -2
- package/dist/_token-utils-WjkbrjKv.js +12 -0
- package/dist/{_types-Cr2YEzKm.js → _types-BHFM59hI.js} +4 -9
- package/dist/{calculate-cost-CoS7we68.js → calculate-cost-BDqO4yWA.js} +4 -15
- package/dist/calculate-cost.d.ts +41 -11
- package/dist/calculate-cost.js +3 -2
- package/dist/{data-loader-BeaFK_sH.js → data-loader-R9pMvoQp.js} +67 -34
- package/dist/{data-loader-DZczD-9E.d.ts → data-loader-a9CiVyT5.d.ts} +32 -8
- package/dist/data-loader.d.ts +3 -3
- package/dist/data-loader.js +6 -5
- package/dist/{debug-BmJuGBXC.js → debug-Cyr1LS0T.js} +22 -11
- package/dist/debug.js +6 -5
- package/dist/index.js +443 -335
- package/dist/{logger-Cke8hliP.js → logger-DeTONwj8.js} +3 -3
- package/dist/logger.d.ts +4 -1
- package/dist/logger.js +1 -1
- package/dist/{mcp-DKqp_F9c.js → mcp-Kff7A2dF.js} +235 -43
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +7 -5
- package/dist/{pricing-fetcher-BZe7AafW.d.ts → pricing-fetcher-B3SvKOod.d.ts} +10 -3
- package/dist/{pricing-fetcher-Dm8hcn_h.js → pricing-fetcher-DaK2jizg.js} +180 -45
- package/dist/pricing-fetcher.d.ts +1 -1
- package/dist/pricing-fetcher.js +3 -3
- package/dist/{prompt-j5YimnLx.js → prompt-DsUFNEY7.js} +2 -2
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, CLAUDE_PROJECTS_DIR_NAME, DEFAULT_RECENT_DAYS, DEFAULT_REFRESH_INTERVAL_SECONDS, MAX_REFRESH_INTERVAL_SECONDS, MCP_DEFAULT_PORT, MIN_REFRESH_INTERVAL_SECONDS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, __commonJSMin, __require, __toESM, require_usingCtx } from "./pricing-fetcher-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
2
|
+
import { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, CLAUDE_PROJECTS_DIR_NAME, 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, __commonJSMin, __require, __toESM, isFailure, require_usingCtx, try_ } from "./pricing-fetcher-DaK2jizg.js";
|
|
3
|
+
import { getTotalTokens } from "./_token-utils-WjkbrjKv.js";
|
|
4
|
+
import { CostModes, SortOrders, filterDateSchema } from "./_types-BHFM59hI.js";
|
|
5
|
+
import { calculateTotals, createTotalsObject } from "./calculate-cost-BDqO4yWA.js";
|
|
6
|
+
import { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, filterRecentBlocks, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, glob, identifySessionBlocks, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, projectBlockUsage, sortFilesByTimestamp, uniq, usageDataSchema } from "./data-loader-R9pMvoQp.js";
|
|
7
|
+
import { description, log, logger, name, version } from "./logger-DeTONwj8.js";
|
|
8
|
+
import { detectMismatches, printMismatchReport } from "./debug-Cyr1LS0T.js";
|
|
9
|
+
import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-Kff7A2dF.js";
|
|
9
10
|
import { readFile } from "node:fs/promises";
|
|
10
11
|
import path from "node:path";
|
|
11
12
|
import process$1 from "node:process";
|
|
@@ -1194,7 +1195,7 @@ var require_picocolors = __commonJSMin((exports, module) => {
|
|
|
1194
1195
|
* @throws TypeError if date format is invalid
|
|
1195
1196
|
*/
|
|
1196
1197
|
function parseDateArg(value) {
|
|
1197
|
-
const result =
|
|
1198
|
+
const result = filterDateSchema.safeParse(value);
|
|
1198
1199
|
if (!result.success) throw new TypeError(result.error.issues[0]?.message ?? "Invalid date format");
|
|
1199
1200
|
return result.data;
|
|
1200
1201
|
}
|
|
@@ -1257,6 +1258,14 @@ const sharedArgs = {
|
|
|
1257
1258
|
short: "O",
|
|
1258
1259
|
description: "Use cached pricing data for Claude models instead of fetching from API",
|
|
1259
1260
|
default: false
|
|
1261
|
+
},
|
|
1262
|
+
color: {
|
|
1263
|
+
type: "boolean",
|
|
1264
|
+
description: "Enable colored output (default: auto). FORCE_COLOR=1 has the same effect."
|
|
1265
|
+
},
|
|
1266
|
+
noColor: {
|
|
1267
|
+
type: "boolean",
|
|
1268
|
+
description: "Disable colored output (default: auto). NO_COLOR=1 has the same effect."
|
|
1260
1269
|
}
|
|
1261
1270
|
};
|
|
1262
1271
|
/**
|
|
@@ -2149,7 +2158,7 @@ var require_colors = __commonJSMin((exports, module) => {
|
|
|
2149
2158
|
var util = __require("node:util");
|
|
2150
2159
|
var ansiStyles = colors$1.styles = require_styles();
|
|
2151
2160
|
var defineProps = Object.defineProperties;
|
|
2152
|
-
var newLineRegex = new RegExp(/[\r\n]+/g);
|
|
2161
|
+
var newLineRegex = /* @__PURE__ */ new RegExp(/[\r\n]+/g);
|
|
2153
2162
|
colors$1.supportsColor = require_supports_colors().supportsColor;
|
|
2154
2163
|
if (typeof colors$1.enabled === "undefined") colors$1.enabled = colors$1.supportsColor() !== false;
|
|
2155
2164
|
colors$1.enable = function() {
|
|
@@ -2931,7 +2940,7 @@ function stringWidth(string, options = {}) {
|
|
|
2931
2940
|
return width;
|
|
2932
2941
|
}
|
|
2933
2942
|
var import_cli_table3 = __toESM(require_cli_table3(), 1);
|
|
2934
|
-
var import_picocolors$
|
|
2943
|
+
var import_picocolors$6 = __toESM(require_picocolors(), 1);
|
|
2935
2944
|
/**
|
|
2936
2945
|
* Responsive table class that adapts column widths based on terminal size
|
|
2937
2946
|
* Automatically adjusts formatting and layout for different screen sizes
|
|
@@ -3167,11 +3176,96 @@ function pushBreakdownRows(table, breakdowns, extraColumns = 1, trailingColumns
|
|
|
3167
3176
|
const row = [` └─ ${formatModelName(breakdown.modelName)}`];
|
|
3168
3177
|
for (let i = 0; i < extraColumns; i++) row.push("");
|
|
3169
3178
|
const totalTokens = breakdown.inputTokens + breakdown.outputTokens + breakdown.cacheCreationTokens + breakdown.cacheReadTokens;
|
|
3170
|
-
row.push(import_picocolors$
|
|
3179
|
+
row.push(import_picocolors$6.default.gray(formatNumber(breakdown.inputTokens)), import_picocolors$6.default.gray(formatNumber(breakdown.outputTokens)), import_picocolors$6.default.gray(formatNumber(breakdown.cacheCreationTokens)), import_picocolors$6.default.gray(formatNumber(breakdown.cacheReadTokens)), import_picocolors$6.default.gray(formatNumber(totalTokens)), import_picocolors$6.default.gray(formatCurrency(breakdown.cost)));
|
|
3171
3180
|
for (let i = 0; i < trailingColumns; i++) row.push("");
|
|
3172
3181
|
table.push(row);
|
|
3173
3182
|
}
|
|
3174
3183
|
}
|
|
3184
|
+
/**
|
|
3185
|
+
* Manages live monitoring of Claude usage with efficient data reloading
|
|
3186
|
+
*/
|
|
3187
|
+
var LiveMonitor = class {
|
|
3188
|
+
config;
|
|
3189
|
+
fetcher = null;
|
|
3190
|
+
lastFileTimestamps = /* @__PURE__ */ new Map();
|
|
3191
|
+
processedHashes = /* @__PURE__ */ new Set();
|
|
3192
|
+
allEntries = [];
|
|
3193
|
+
constructor(config) {
|
|
3194
|
+
this.config = config;
|
|
3195
|
+
if (config.mode !== "display") this.fetcher = new PricingFetcher();
|
|
3196
|
+
}
|
|
3197
|
+
/**
|
|
3198
|
+
* Implements Disposable interface
|
|
3199
|
+
*/
|
|
3200
|
+
[Symbol.dispose]() {
|
|
3201
|
+
this.fetcher?.[Symbol.dispose]();
|
|
3202
|
+
}
|
|
3203
|
+
/**
|
|
3204
|
+
* Gets the current active session block with minimal file reading
|
|
3205
|
+
* Only reads new or modified files since last check
|
|
3206
|
+
*/
|
|
3207
|
+
async getActiveBlock() {
|
|
3208
|
+
const claudeDir = path.join(this.config.claudePath, CLAUDE_PROJECTS_DIR_NAME);
|
|
3209
|
+
const files = await glob([USAGE_DATA_GLOB_PATTERN], {
|
|
3210
|
+
cwd: claudeDir,
|
|
3211
|
+
absolute: true
|
|
3212
|
+
});
|
|
3213
|
+
if (files.length === 0) return null;
|
|
3214
|
+
const filesToRead = [];
|
|
3215
|
+
for (const file of files) {
|
|
3216
|
+
const timestamp = await getEarliestTimestamp(file);
|
|
3217
|
+
const lastTimestamp = this.lastFileTimestamps.get(file);
|
|
3218
|
+
if (timestamp != null && (lastTimestamp == null || timestamp.getTime() > lastTimestamp)) {
|
|
3219
|
+
filesToRead.push(file);
|
|
3220
|
+
this.lastFileTimestamps.set(file, timestamp.getTime());
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
if (filesToRead.length > 0) {
|
|
3224
|
+
const sortedFiles = await sortFilesByTimestamp(filesToRead);
|
|
3225
|
+
for (const file of sortedFiles) {
|
|
3226
|
+
const content = await readFile(file, "utf-8").catch(() => {
|
|
3227
|
+
return "";
|
|
3228
|
+
});
|
|
3229
|
+
const lines = content.trim().split("\n").filter((line) => line.length > 0);
|
|
3230
|
+
for (const line of lines) try {
|
|
3231
|
+
const parsed = JSON.parse(line);
|
|
3232
|
+
const result = usageDataSchema.safeParse(parsed);
|
|
3233
|
+
if (!result.success) continue;
|
|
3234
|
+
const data = result.data;
|
|
3235
|
+
const uniqueHash = createUniqueHash(data);
|
|
3236
|
+
if (uniqueHash != null && this.processedHashes.has(uniqueHash)) continue;
|
|
3237
|
+
if (uniqueHash != null) this.processedHashes.add(uniqueHash);
|
|
3238
|
+
const costUSD = await (this.config.mode === "display" ? Promise.resolve(data.costUSD ?? 0) : calculateCostForEntry(data, this.config.mode, this.fetcher));
|
|
3239
|
+
const usageLimitResetTime = getUsageLimitResetTime(data);
|
|
3240
|
+
this.allEntries.push({
|
|
3241
|
+
timestamp: new Date(data.timestamp),
|
|
3242
|
+
usage: {
|
|
3243
|
+
inputTokens: data.message.usage.input_tokens ?? 0,
|
|
3244
|
+
outputTokens: data.message.usage.output_tokens ?? 0,
|
|
3245
|
+
cacheCreationInputTokens: data.message.usage.cache_creation_input_tokens ?? 0,
|
|
3246
|
+
cacheReadInputTokens: data.message.usage.cache_read_input_tokens ?? 0
|
|
3247
|
+
},
|
|
3248
|
+
costUSD,
|
|
3249
|
+
model: data.message.model ?? "<synthetic>",
|
|
3250
|
+
version: data.version,
|
|
3251
|
+
usageLimitResetTime: usageLimitResetTime ?? void 0
|
|
3252
|
+
});
|
|
3253
|
+
} catch {}
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
const blocks = identifySessionBlocks(this.allEntries, this.config.sessionDurationHours);
|
|
3257
|
+
const sortedBlocks = this.config.order === "asc" ? blocks : blocks.reverse();
|
|
3258
|
+
return sortedBlocks.find((block) => block.isActive) ?? null;
|
|
3259
|
+
}
|
|
3260
|
+
/**
|
|
3261
|
+
* Clears all cached data to force a full reload
|
|
3262
|
+
*/
|
|
3263
|
+
clearCache() {
|
|
3264
|
+
this.lastFileTimestamps.clear();
|
|
3265
|
+
this.processedHashes.clear();
|
|
3266
|
+
this.allEntries = [];
|
|
3267
|
+
}
|
|
3268
|
+
};
|
|
3175
3269
|
/** Options for {@linkcode delay}. */
|
|
3176
3270
|
/**
|
|
3177
3271
|
* Resolve a {@linkcode Promise} after a given amount of milliseconds.
|
|
@@ -3225,6 +3319,54 @@ function pushBreakdownRows(table, breakdowns, extraColumns = 1, trailingColumns
|
|
|
3225
3319
|
}
|
|
3226
3320
|
});
|
|
3227
3321
|
}
|
|
3322
|
+
const isBrowser = globalThis.window?.document !== void 0;
|
|
3323
|
+
const isNode = globalThis.process?.versions?.node !== void 0;
|
|
3324
|
+
const isBun = globalThis.process?.versions?.bun !== void 0;
|
|
3325
|
+
const isDeno = globalThis.Deno?.version?.deno !== void 0;
|
|
3326
|
+
const isElectron = globalThis.process?.versions?.electron !== void 0;
|
|
3327
|
+
const isJsDom = globalThis.navigator?.userAgent?.includes("jsdom") === true;
|
|
3328
|
+
const isWebWorker = typeof WorkerGlobalScope !== "undefined" && globalThis instanceof WorkerGlobalScope;
|
|
3329
|
+
const isDedicatedWorker = typeof DedicatedWorkerGlobalScope !== "undefined" && globalThis instanceof DedicatedWorkerGlobalScope;
|
|
3330
|
+
const isSharedWorker = typeof SharedWorkerGlobalScope !== "undefined" && globalThis instanceof SharedWorkerGlobalScope;
|
|
3331
|
+
const isServiceWorker = typeof ServiceWorkerGlobalScope !== "undefined" && globalThis instanceof ServiceWorkerGlobalScope;
|
|
3332
|
+
const platform = globalThis.navigator?.userAgentData?.platform;
|
|
3333
|
+
const isMacOs = platform === "macOS" || globalThis.navigator?.platform === "MacIntel" || globalThis.navigator?.userAgent?.includes(" Mac ") === true || globalThis.process?.platform === "darwin";
|
|
3334
|
+
const isWindows$1 = platform === "Windows" || globalThis.navigator?.platform === "Win32" || globalThis.process?.platform === "win32";
|
|
3335
|
+
const isLinux = platform === "Linux" || globalThis.navigator?.platform?.startsWith("Linux") === true || globalThis.navigator?.userAgent?.includes(" Linux ") === true || globalThis.process?.platform === "linux";
|
|
3336
|
+
const isIos = platform === "iOS" || globalThis.navigator?.platform === "MacIntel" && globalThis.navigator?.maxTouchPoints > 1 || /iPad|iPhone|iPod/.test(globalThis.navigator?.platform);
|
|
3337
|
+
const isAndroid = platform === "Android" || globalThis.navigator?.platform === "Android" || globalThis.navigator?.userAgent?.includes(" Android ") === true || globalThis.process?.platform === "android";
|
|
3338
|
+
const ESC = "\x1B[";
|
|
3339
|
+
const SEP = ";";
|
|
3340
|
+
const isTerminalApp = !isBrowser && process$1.env.TERM_PROGRAM === "Apple_Terminal";
|
|
3341
|
+
const isWindows = !isBrowser && process$1.platform === "win32";
|
|
3342
|
+
const cwdFunction = isBrowser ? () => {
|
|
3343
|
+
throw new Error("`process.cwd()` only works in Node.js, not the browser.");
|
|
3344
|
+
} : process$1.cwd;
|
|
3345
|
+
const cursorTo = (x, y) => {
|
|
3346
|
+
if (typeof x !== "number") throw new TypeError("The `x` argument is required");
|
|
3347
|
+
if (typeof y !== "number") return ESC + (x + 1) + "G";
|
|
3348
|
+
return ESC + (y + 1) + SEP + (x + 1) + "H";
|
|
3349
|
+
};
|
|
3350
|
+
const cursorLeft = ESC + "G";
|
|
3351
|
+
const cursorSavePosition = isTerminalApp ? "\x1B7" : ESC + "s";
|
|
3352
|
+
const cursorRestorePosition = isTerminalApp ? "\x1B8" : ESC + "u";
|
|
3353
|
+
const cursorGetPosition = ESC + "6n";
|
|
3354
|
+
const cursorNextLine = ESC + "E";
|
|
3355
|
+
const cursorPrevLine = ESC + "F";
|
|
3356
|
+
const cursorHide = ESC + "?25l";
|
|
3357
|
+
const cursorShow = ESC + "?25h";
|
|
3358
|
+
const eraseEndLine = ESC + "K";
|
|
3359
|
+
const eraseStartLine = ESC + "1K";
|
|
3360
|
+
const eraseLine = ESC + "2K";
|
|
3361
|
+
const eraseDown = ESC + "J";
|
|
3362
|
+
const eraseUp = ESC + "1J";
|
|
3363
|
+
const eraseScreen = ESC + "2J";
|
|
3364
|
+
const scrollUp = ESC + "S";
|
|
3365
|
+
const scrollDown = ESC + "T";
|
|
3366
|
+
const clearScreen = "\x1Bc";
|
|
3367
|
+
const clearTerminal = isWindows ? `${eraseScreen}${ESC}0f` : `${eraseScreen}${ESC}3J${ESC}H`;
|
|
3368
|
+
const enterAlternativeScreen = ESC + "?1049h";
|
|
3369
|
+
const exitAlternativeScreen = ESC + "?1049l";
|
|
3228
3370
|
const toZeroIfInfinity = (value) => Number.isFinite(value) ? value : 0;
|
|
3229
3371
|
function parseNumber(milliseconds) {
|
|
3230
3372
|
return {
|
|
@@ -3336,255 +3478,170 @@ function prettyMilliseconds(milliseconds, options) {
|
|
|
3336
3478
|
if (typeof options.unitCount === "number") result = result.slice(0, Math.max(options.unitCount, 1));
|
|
3337
3479
|
return sign + result.join(separator);
|
|
3338
3480
|
}
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
fetcher = null;
|
|
3345
|
-
lastFileTimestamps = /* @__PURE__ */ new Map();
|
|
3346
|
-
processedHashes = /* @__PURE__ */ new Set();
|
|
3347
|
-
allEntries = [];
|
|
3348
|
-
constructor(config) {
|
|
3349
|
-
this.config = config;
|
|
3350
|
-
if (config.mode !== "display") this.fetcher = new PricingFetcher();
|
|
3351
|
-
}
|
|
3352
|
-
/**
|
|
3353
|
-
* Implements Disposable interface
|
|
3354
|
-
*/
|
|
3355
|
-
[Symbol.dispose]() {
|
|
3356
|
-
this.fetcher?.[Symbol.dispose]();
|
|
3357
|
-
}
|
|
3358
|
-
/**
|
|
3359
|
-
* Gets the current active session block with minimal file reading
|
|
3360
|
-
* Only reads new or modified files since last check
|
|
3361
|
-
*/
|
|
3362
|
-
async getActiveBlock() {
|
|
3363
|
-
const claudeDir = path.join(this.config.claudePath, CLAUDE_PROJECTS_DIR_NAME);
|
|
3364
|
-
const files = await glob([USAGE_DATA_GLOB_PATTERN], {
|
|
3365
|
-
cwd: claudeDir,
|
|
3366
|
-
absolute: true
|
|
3367
|
-
});
|
|
3368
|
-
if (files.length === 0) return null;
|
|
3369
|
-
const filesToRead = [];
|
|
3370
|
-
for (const file of files) {
|
|
3371
|
-
const timestamp = await getEarliestTimestamp(file);
|
|
3372
|
-
const lastTimestamp = this.lastFileTimestamps.get(file);
|
|
3373
|
-
if (timestamp != null && (lastTimestamp == null || timestamp.getTime() > lastTimestamp)) {
|
|
3374
|
-
filesToRead.push(file);
|
|
3375
|
-
this.lastFileTimestamps.set(file, timestamp.getTime());
|
|
3376
|
-
}
|
|
3377
|
-
}
|
|
3378
|
-
if (filesToRead.length > 0) {
|
|
3379
|
-
const sortedFiles = await sortFilesByTimestamp(filesToRead);
|
|
3380
|
-
for (const file of sortedFiles) {
|
|
3381
|
-
const content = await readFile(file, "utf-8");
|
|
3382
|
-
const lines = content.trim().split("\n").filter((line) => line.length > 0);
|
|
3383
|
-
for (const line of lines) try {
|
|
3384
|
-
const parsed = JSON.parse(line);
|
|
3385
|
-
const result = usageDataSchema.safeParse(parsed);
|
|
3386
|
-
if (!result.success) continue;
|
|
3387
|
-
const data = result.data;
|
|
3388
|
-
const uniqueHash = createUniqueHash(data);
|
|
3389
|
-
if (uniqueHash != null && this.processedHashes.has(uniqueHash)) continue;
|
|
3390
|
-
if (uniqueHash != null) this.processedHashes.add(uniqueHash);
|
|
3391
|
-
const costUSD = await (this.config.mode === "display" ? Promise.resolve(data.costUSD ?? 0) : calculateCostForEntry(data, this.config.mode, this.fetcher));
|
|
3392
|
-
this.allEntries.push({
|
|
3393
|
-
timestamp: new Date(data.timestamp),
|
|
3394
|
-
usage: {
|
|
3395
|
-
inputTokens: data.message.usage.input_tokens ?? 0,
|
|
3396
|
-
outputTokens: data.message.usage.output_tokens ?? 0,
|
|
3397
|
-
cacheCreationInputTokens: data.message.usage.cache_creation_input_tokens ?? 0,
|
|
3398
|
-
cacheReadInputTokens: data.message.usage.cache_read_input_tokens ?? 0
|
|
3399
|
-
},
|
|
3400
|
-
costUSD,
|
|
3401
|
-
model: data.message.model ?? "<synthetic>",
|
|
3402
|
-
version: data.version
|
|
3403
|
-
});
|
|
3404
|
-
} catch {}
|
|
3405
|
-
}
|
|
3406
|
-
}
|
|
3407
|
-
const blocks = identifySessionBlocks(this.allEntries, this.config.sessionDurationHours);
|
|
3408
|
-
const sortedBlocks = this.config.order === "asc" ? blocks : blocks.reverse();
|
|
3409
|
-
return sortedBlocks.find((block) => block.isActive) ?? null;
|
|
3410
|
-
}
|
|
3411
|
-
/**
|
|
3412
|
-
* Clears all cached data to force a full reload
|
|
3413
|
-
*/
|
|
3414
|
-
clearCache() {
|
|
3415
|
-
this.lastFileTimestamps.clear();
|
|
3416
|
-
this.processedHashes.clear();
|
|
3417
|
-
this.allEntries = [];
|
|
3418
|
-
}
|
|
3419
|
-
};
|
|
3420
|
-
const isBrowser = globalThis.window?.document !== void 0;
|
|
3421
|
-
const isNode = globalThis.process?.versions?.node !== void 0;
|
|
3422
|
-
const isBun = globalThis.process?.versions?.bun !== void 0;
|
|
3423
|
-
const isDeno = globalThis.Deno?.version?.deno !== void 0;
|
|
3424
|
-
const isElectron = globalThis.process?.versions?.electron !== void 0;
|
|
3425
|
-
const isJsDom = globalThis.navigator?.userAgent?.includes("jsdom") === true;
|
|
3426
|
-
const isWebWorker = typeof WorkerGlobalScope !== "undefined" && globalThis instanceof WorkerGlobalScope;
|
|
3427
|
-
const isDedicatedWorker = typeof DedicatedWorkerGlobalScope !== "undefined" && globalThis instanceof DedicatedWorkerGlobalScope;
|
|
3428
|
-
const isSharedWorker = typeof SharedWorkerGlobalScope !== "undefined" && globalThis instanceof SharedWorkerGlobalScope;
|
|
3429
|
-
const isServiceWorker = typeof ServiceWorkerGlobalScope !== "undefined" && globalThis instanceof ServiceWorkerGlobalScope;
|
|
3430
|
-
const platform = globalThis.navigator?.userAgentData?.platform;
|
|
3431
|
-
const isMacOs = platform === "macOS" || globalThis.navigator?.platform === "MacIntel" || globalThis.navigator?.userAgent?.includes(" Mac ") === true || globalThis.process?.platform === "darwin";
|
|
3432
|
-
const isWindows$1 = platform === "Windows" || globalThis.navigator?.platform === "Win32" || globalThis.process?.platform === "win32";
|
|
3433
|
-
const isLinux = platform === "Linux" || globalThis.navigator?.platform?.startsWith("Linux") === true || globalThis.navigator?.userAgent?.includes(" Linux ") === true || globalThis.process?.platform === "linux";
|
|
3434
|
-
const isIos = platform === "iOS" || globalThis.navigator?.platform === "MacIntel" && globalThis.navigator?.maxTouchPoints > 1 || /iPad|iPhone|iPod/.test(globalThis.navigator?.platform);
|
|
3435
|
-
const isAndroid = platform === "Android" || globalThis.navigator?.platform === "Android" || globalThis.navigator?.userAgent?.includes(" Android ") === true || globalThis.process?.platform === "android";
|
|
3436
|
-
const ESC = "\x1B[";
|
|
3437
|
-
const SEP = ";";
|
|
3438
|
-
const isTerminalApp = !isBrowser && process$1.env.TERM_PROGRAM === "Apple_Terminal";
|
|
3439
|
-
const isWindows = !isBrowser && process$1.platform === "win32";
|
|
3440
|
-
const cwdFunction = isBrowser ? () => {
|
|
3441
|
-
throw new Error("`process.cwd()` only works in Node.js, not the browser.");
|
|
3442
|
-
} : process$1.cwd;
|
|
3443
|
-
const cursorTo = (x, y) => {
|
|
3444
|
-
if (typeof x !== "number") throw new TypeError("The `x` argument is required");
|
|
3445
|
-
if (typeof y !== "number") return ESC + (x + 1) + "G";
|
|
3446
|
-
return ESC + (y + 1) + SEP + (x + 1) + "H";
|
|
3447
|
-
};
|
|
3448
|
-
const cursorUp = (count = 1) => ESC + count + "A";
|
|
3449
|
-
const cursorDown = (count = 1) => ESC + count + "B";
|
|
3450
|
-
const cursorLeft = ESC + "G";
|
|
3451
|
-
const cursorSavePosition = isTerminalApp ? "\x1B7" : ESC + "s";
|
|
3452
|
-
const cursorRestorePosition = isTerminalApp ? "\x1B8" : ESC + "u";
|
|
3453
|
-
const cursorGetPosition = ESC + "6n";
|
|
3454
|
-
const cursorNextLine = ESC + "E";
|
|
3455
|
-
const cursorPrevLine = ESC + "F";
|
|
3456
|
-
const cursorHide = ESC + "?25l";
|
|
3457
|
-
const cursorShow = ESC + "?25h";
|
|
3458
|
-
const eraseEndLine = ESC + "K";
|
|
3459
|
-
const eraseStartLine = ESC + "1K";
|
|
3460
|
-
const eraseLine = ESC + "2K";
|
|
3461
|
-
const eraseDown = ESC + "J";
|
|
3462
|
-
const eraseUp = ESC + "1J";
|
|
3463
|
-
const eraseScreen = ESC + "2J";
|
|
3464
|
-
const scrollUp = ESC + "S";
|
|
3465
|
-
const scrollDown = ESC + "T";
|
|
3466
|
-
const clearScreen = "\x1Bc";
|
|
3467
|
-
const clearTerminal = isWindows ? `${eraseScreen}${ESC}0f` : `${eraseScreen}${ESC}3J${ESC}H`;
|
|
3468
|
-
const enterAlternativeScreen = ESC + "?1049h";
|
|
3469
|
-
const exitAlternativeScreen = ESC + "?1049l";
|
|
3470
|
-
/**
|
|
3471
|
-
* Terminal control sequences for live display updates
|
|
3472
|
-
*/
|
|
3473
|
-
const TERMINAL_CONTROL = {
|
|
3474
|
-
HIDE_CURSOR: cursorHide,
|
|
3475
|
-
SHOW_CURSOR: cursorShow,
|
|
3476
|
-
CLEAR_SCREEN: clearScreen,
|
|
3477
|
-
CLEAR_LINE: eraseLine,
|
|
3478
|
-
MOVE_TO_TOP: cursorTo(0, 0),
|
|
3479
|
-
MOVE_UP: (n) => cursorUp(n),
|
|
3480
|
-
MOVE_DOWN: (n) => cursorDown(n),
|
|
3481
|
-
MOVE_TO_COLUMN: (n) => cursorTo(n - 1, void 0)
|
|
3482
|
-
};
|
|
3481
|
+
const SYNC_START = "\x1B[?2026h";
|
|
3482
|
+
const SYNC_END = "\x1B[?2026l";
|
|
3483
|
+
const DISABLE_LINE_WRAP = "\x1B[?7l";
|
|
3484
|
+
const ENABLE_LINE_WRAP = "\x1B[?7h";
|
|
3485
|
+
const ANSI_RESET = "\x1B[0m";
|
|
3483
3486
|
/**
|
|
3484
3487
|
* Manages terminal state for live updates
|
|
3488
|
+
* Provides a clean interface for terminal operations with automatic TTY checking
|
|
3489
|
+
* and cursor state management for live monitoring displays
|
|
3485
3490
|
*/
|
|
3486
3491
|
var TerminalManager = class {
|
|
3487
3492
|
stream;
|
|
3488
3493
|
cursorHidden = false;
|
|
3489
|
-
|
|
3494
|
+
buffer = [];
|
|
3495
|
+
useBuffering = false;
|
|
3496
|
+
alternateScreenActive = false;
|
|
3497
|
+
syncMode = false;
|
|
3490
3498
|
constructor(stream = process$1.stdout) {
|
|
3491
3499
|
this.stream = stream;
|
|
3492
|
-
this.originalWrite = stream.write.bind(stream);
|
|
3493
3500
|
}
|
|
3494
3501
|
/**
|
|
3495
|
-
* Hides the terminal cursor
|
|
3502
|
+
* Hides the terminal cursor for cleaner live updates
|
|
3503
|
+
* Only works in TTY environments (real terminals)
|
|
3496
3504
|
*/
|
|
3497
3505
|
hideCursor() {
|
|
3498
3506
|
if (!this.cursorHidden && this.stream.isTTY) {
|
|
3499
|
-
this.stream.write(
|
|
3507
|
+
this.stream.write(cursorHide);
|
|
3500
3508
|
this.cursorHidden = true;
|
|
3501
3509
|
}
|
|
3502
3510
|
}
|
|
3503
3511
|
/**
|
|
3504
3512
|
* Shows the terminal cursor
|
|
3513
|
+
* Should be called during cleanup to restore normal terminal behavior
|
|
3505
3514
|
*/
|
|
3506
3515
|
showCursor() {
|
|
3507
3516
|
if (this.cursorHidden && this.stream.isTTY) {
|
|
3508
|
-
this.stream.write(
|
|
3517
|
+
this.stream.write(cursorShow);
|
|
3509
3518
|
this.cursorHidden = false;
|
|
3510
3519
|
}
|
|
3511
3520
|
}
|
|
3512
3521
|
/**
|
|
3513
|
-
* Clears the entire screen and moves cursor to top
|
|
3522
|
+
* Clears the entire screen and moves cursor to top-left corner
|
|
3523
|
+
* Essential for live monitoring displays that need to refresh completely
|
|
3514
3524
|
*/
|
|
3515
3525
|
clearScreen() {
|
|
3516
3526
|
if (this.stream.isTTY) {
|
|
3517
|
-
this.stream.write(
|
|
3518
|
-
this.stream.write(
|
|
3527
|
+
this.stream.write(clearScreen);
|
|
3528
|
+
this.stream.write(cursorTo(0, 0));
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3531
|
+
/**
|
|
3532
|
+
* Writes text to the terminal stream
|
|
3533
|
+
* Supports buffering mode for performance optimization
|
|
3534
|
+
*/
|
|
3535
|
+
write(text) {
|
|
3536
|
+
if (this.useBuffering) this.buffer.push(text);
|
|
3537
|
+
else this.stream.write(text);
|
|
3538
|
+
}
|
|
3539
|
+
/**
|
|
3540
|
+
* Enables buffering mode - collects all writes in memory instead of sending immediately
|
|
3541
|
+
* This prevents flickering when doing many rapid updates
|
|
3542
|
+
*/
|
|
3543
|
+
startBuffering() {
|
|
3544
|
+
this.useBuffering = true;
|
|
3545
|
+
this.buffer = [];
|
|
3546
|
+
}
|
|
3547
|
+
/**
|
|
3548
|
+
* Sends all buffered content to terminal at once
|
|
3549
|
+
* This creates smooth, atomic updates without flickering
|
|
3550
|
+
*/
|
|
3551
|
+
flush() {
|
|
3552
|
+
if (this.useBuffering && this.buffer.length > 0) {
|
|
3553
|
+
if (this.syncMode && this.stream.isTTY) this.stream.write(SYNC_START + this.buffer.join("") + SYNC_END);
|
|
3554
|
+
else this.stream.write(this.buffer.join(""));
|
|
3555
|
+
this.buffer = [];
|
|
3519
3556
|
}
|
|
3557
|
+
this.useBuffering = false;
|
|
3520
3558
|
}
|
|
3521
3559
|
/**
|
|
3522
|
-
*
|
|
3560
|
+
* Switches to alternate screen buffer (like vim/less does)
|
|
3561
|
+
* This preserves what was on screen before and allows full-screen apps
|
|
3523
3562
|
*/
|
|
3524
|
-
|
|
3525
|
-
if (this.
|
|
3563
|
+
enterAlternateScreen() {
|
|
3564
|
+
if (!this.alternateScreenActive && this.stream.isTTY) {
|
|
3565
|
+
this.stream.write(enterAlternativeScreen);
|
|
3566
|
+
this.stream.write(DISABLE_LINE_WRAP);
|
|
3567
|
+
this.alternateScreenActive = true;
|
|
3568
|
+
}
|
|
3526
3569
|
}
|
|
3527
3570
|
/**
|
|
3528
|
-
*
|
|
3571
|
+
* Returns to normal screen, restoring what was there before
|
|
3529
3572
|
*/
|
|
3530
|
-
|
|
3531
|
-
if (this.
|
|
3573
|
+
exitAlternateScreen() {
|
|
3574
|
+
if (this.alternateScreenActive && this.stream.isTTY) {
|
|
3575
|
+
this.stream.write(ENABLE_LINE_WRAP);
|
|
3576
|
+
this.stream.write(exitAlternativeScreen);
|
|
3577
|
+
this.alternateScreenActive = false;
|
|
3578
|
+
}
|
|
3532
3579
|
}
|
|
3533
3580
|
/**
|
|
3534
|
-
*
|
|
3581
|
+
* Enables sync mode - terminal will wait for END signal before showing updates
|
|
3582
|
+
* Prevents the user from seeing partial/torn screen updates
|
|
3535
3583
|
*/
|
|
3536
|
-
|
|
3537
|
-
|
|
3584
|
+
enableSyncMode() {
|
|
3585
|
+
this.syncMode = true;
|
|
3538
3586
|
}
|
|
3539
3587
|
/**
|
|
3540
|
-
*
|
|
3588
|
+
* Disables synchronized output mode
|
|
3541
3589
|
*/
|
|
3542
|
-
|
|
3543
|
-
this.
|
|
3590
|
+
disableSyncMode() {
|
|
3591
|
+
this.syncMode = false;
|
|
3544
3592
|
}
|
|
3545
3593
|
/**
|
|
3546
|
-
* Gets terminal width
|
|
3594
|
+
* Gets terminal width in columns
|
|
3595
|
+
* Falls back to 80 columns if detection fails
|
|
3547
3596
|
*/
|
|
3548
3597
|
get width() {
|
|
3549
3598
|
return this.stream.columns || 80;
|
|
3550
3599
|
}
|
|
3551
3600
|
/**
|
|
3552
|
-
* Gets terminal height
|
|
3601
|
+
* Gets terminal height in rows
|
|
3602
|
+
* Falls back to 24 rows if detection fails
|
|
3553
3603
|
*/
|
|
3554
3604
|
get height() {
|
|
3555
3605
|
return this.stream.rows || 24;
|
|
3556
3606
|
}
|
|
3557
3607
|
/**
|
|
3558
|
-
*
|
|
3608
|
+
* Returns true if output goes to a real terminal (not a file or pipe)
|
|
3609
|
+
* We only send fancy ANSI codes to real terminals
|
|
3559
3610
|
*/
|
|
3560
3611
|
get isTTY() {
|
|
3561
3612
|
return this.stream.isTTY ?? false;
|
|
3562
3613
|
}
|
|
3563
3614
|
/**
|
|
3564
|
-
*
|
|
3615
|
+
* Restores terminal to normal state - MUST call before program exits
|
|
3616
|
+
* Otherwise user's terminal might be left in a broken state
|
|
3565
3617
|
*/
|
|
3566
3618
|
cleanup() {
|
|
3567
3619
|
this.showCursor();
|
|
3620
|
+
this.exitAlternateScreen();
|
|
3621
|
+
this.disableSyncMode();
|
|
3568
3622
|
}
|
|
3569
3623
|
};
|
|
3570
3624
|
/**
|
|
3571
|
-
* Creates a progress bar string
|
|
3572
|
-
*
|
|
3573
|
-
*
|
|
3574
|
-
*
|
|
3575
|
-
* @param
|
|
3576
|
-
* @param
|
|
3625
|
+
* Creates a progress bar string with customizable appearance
|
|
3626
|
+
*
|
|
3627
|
+
* Example: createProgressBar(75, 100, 20) -> "[████████████████░░░░] 75.0%"
|
|
3628
|
+
*
|
|
3629
|
+
* @param value - Current progress value
|
|
3630
|
+
* @param max - Maximum value (100% point)
|
|
3631
|
+
* @param width - Character width of the progress bar (excluding brackets and text)
|
|
3632
|
+
* @param options - Customization options for appearance and display
|
|
3633
|
+
* @param options.showPercentage - Whether to show percentage after the bar
|
|
3577
3634
|
* @param options.showValues - Whether to show current/max values
|
|
3578
|
-
* @param options.fillChar - Character for filled portion
|
|
3579
|
-
* @param options.emptyChar - Character for empty portion
|
|
3580
|
-
* @param options.leftBracket - Left bracket character
|
|
3581
|
-
* @param options.rightBracket - Right bracket character
|
|
3582
|
-
* @param options.colors - Color configuration
|
|
3583
|
-
* @param options.colors.low - Color for low percentage
|
|
3584
|
-
* @param options.colors.medium - Color for medium percentage
|
|
3585
|
-
* @param options.colors.high - Color for high percentage
|
|
3586
|
-
* @param options.colors.critical - Color for critical percentage
|
|
3587
|
-
* @returns Formatted progress bar string
|
|
3635
|
+
* @param options.fillChar - Character for filled portion (default: '█')
|
|
3636
|
+
* @param options.emptyChar - Character for empty portion (default: '░')
|
|
3637
|
+
* @param options.leftBracket - Left bracket character (default: '[')
|
|
3638
|
+
* @param options.rightBracket - Right bracket character (default: ']')
|
|
3639
|
+
* @param options.colors - Color configuration for different thresholds
|
|
3640
|
+
* @param options.colors.low - Color for low percentage values
|
|
3641
|
+
* @param options.colors.medium - Color for medium percentage values
|
|
3642
|
+
* @param options.colors.high - Color for high percentage values
|
|
3643
|
+
* @param options.colors.critical - Color for critical percentage values
|
|
3644
|
+
* @returns Formatted progress bar string with optional percentage/values
|
|
3588
3645
|
*/
|
|
3589
3646
|
function createProgressBar(value, max, width, options = {}) {
|
|
3590
3647
|
const { showPercentage = true, showValues = false, fillChar = "█", emptyChar = "░", leftBracket = "[", rightBracket = "]", colors: colors$2 = {} } = options;
|
|
@@ -3600,17 +3657,23 @@ function createProgressBar(value, max, width, options = {}) {
|
|
|
3600
3657
|
if (color !== "") bar += color;
|
|
3601
3658
|
bar += fillChar.repeat(fillWidth);
|
|
3602
3659
|
bar += emptyChar.repeat(emptyWidth);
|
|
3603
|
-
if (color !== "") bar +=
|
|
3660
|
+
if (color !== "") bar += ANSI_RESET;
|
|
3604
3661
|
bar += rightBracket;
|
|
3605
3662
|
if (showPercentage) bar += ` ${percentage.toFixed(1)}%`;
|
|
3606
3663
|
if (showValues) bar += ` (${value}/${max})`;
|
|
3607
3664
|
return bar;
|
|
3608
3665
|
}
|
|
3609
3666
|
/**
|
|
3610
|
-
* Centers text within a
|
|
3611
|
-
*
|
|
3612
|
-
*
|
|
3613
|
-
*
|
|
3667
|
+
* Centers text within a specified width using spaces for padding
|
|
3668
|
+
*
|
|
3669
|
+
* Uses string-width to handle Unicode characters and ANSI escape codes properly.
|
|
3670
|
+
* If text is longer than width, returns original text without truncation.
|
|
3671
|
+
*
|
|
3672
|
+
* Example: centerText("Hello", 10) -> " Hello "
|
|
3673
|
+
*
|
|
3674
|
+
* @param text - Text to center (may contain ANSI color codes)
|
|
3675
|
+
* @param width - Total character width including padding
|
|
3676
|
+
* @returns Text with spaces added for centering
|
|
3614
3677
|
*/
|
|
3615
3678
|
function centerText(text, width) {
|
|
3616
3679
|
const textLength = stringWidth(text);
|
|
@@ -3619,12 +3682,40 @@ function centerText(text, width) {
|
|
|
3619
3682
|
const rightPadding = width - textLength - leftPadding;
|
|
3620
3683
|
return " ".repeat(leftPadding) + text + " ".repeat(rightPadding);
|
|
3621
3684
|
}
|
|
3622
|
-
var import_picocolors$
|
|
3623
|
-
|
|
3685
|
+
var import_picocolors$5 = __toESM(require_picocolors(), 1);
|
|
3686
|
+
/**
|
|
3687
|
+
* Delay with AbortSignal support and graceful error handling
|
|
3688
|
+
*/
|
|
3689
|
+
async function delayWithAbort(ms, signal) {
|
|
3690
|
+
await delay(ms, { signal });
|
|
3691
|
+
}
|
|
3692
|
+
/**
|
|
3693
|
+
* Shows waiting message when no Claude session is active
|
|
3694
|
+
* Uses efficient cursor positioning instead of full screen clear
|
|
3695
|
+
*/
|
|
3696
|
+
async function renderWaitingState(terminal, config, signal) {
|
|
3697
|
+
terminal.startBuffering();
|
|
3698
|
+
terminal.write(cursorTo(0, 0));
|
|
3699
|
+
terminal.write(eraseDown);
|
|
3700
|
+
terminal.write(import_picocolors$5.default.yellow("No active session block found. Waiting...\n"));
|
|
3701
|
+
terminal.write(cursorHide);
|
|
3702
|
+
terminal.flush();
|
|
3703
|
+
await delayWithAbort(config.refreshInterval, signal);
|
|
3704
|
+
}
|
|
3705
|
+
/**
|
|
3706
|
+
* Displays the live monitoring dashboard for active Claude session
|
|
3707
|
+
* Uses buffering and sync mode to prevent screen flickering
|
|
3708
|
+
*/
|
|
3709
|
+
function renderActiveBlock(terminal, activeBlock, config) {
|
|
3710
|
+
terminal.startBuffering();
|
|
3711
|
+
terminal.write(cursorTo(0, 0));
|
|
3712
|
+
terminal.write(eraseDown);
|
|
3713
|
+
renderLiveDisplay(terminal, activeBlock, config);
|
|
3714
|
+
terminal.write(cursorHide);
|
|
3715
|
+
terminal.flush();
|
|
3716
|
+
}
|
|
3624
3717
|
/**
|
|
3625
3718
|
* Format token counts with K suffix for display
|
|
3626
|
-
* @param num - Number of tokens
|
|
3627
|
-
* @returns Formatted string like "12.3k" or "999"
|
|
3628
3719
|
*/
|
|
3629
3720
|
function formatTokensShort(num) {
|
|
3630
3721
|
if (num >= 1e3) return `${(num / 1e3).toFixed(1)}k`;
|
|
@@ -3638,69 +3729,6 @@ const DETAIL_COLUMN_WIDTHS = {
|
|
|
3638
3729
|
col2: 37
|
|
3639
3730
|
};
|
|
3640
3731
|
/**
|
|
3641
|
-
* Starts live monitoring of the active session block
|
|
3642
|
-
*/
|
|
3643
|
-
async function startLiveMonitoring(config) {
|
|
3644
|
-
try {
|
|
3645
|
-
var _usingCtx = (0, import_usingCtx.default)();
|
|
3646
|
-
const terminal = new TerminalManager();
|
|
3647
|
-
const abortController = new AbortController();
|
|
3648
|
-
const cleanup = () => {
|
|
3649
|
-
abortController.abort();
|
|
3650
|
-
terminal.cleanup();
|
|
3651
|
-
terminal.clearScreen();
|
|
3652
|
-
logger.info("Live monitoring stopped.");
|
|
3653
|
-
if (process$1.exitCode == null) process$1.exit(0);
|
|
3654
|
-
};
|
|
3655
|
-
process$1.on("SIGINT", cleanup);
|
|
3656
|
-
process$1.on("SIGTERM", cleanup);
|
|
3657
|
-
terminal.hideCursor();
|
|
3658
|
-
const monitor = _usingCtx.u(new LiveMonitor({
|
|
3659
|
-
claudePath: config.claudePath,
|
|
3660
|
-
sessionDurationHours: config.sessionDurationHours,
|
|
3661
|
-
mode: config.mode,
|
|
3662
|
-
order: config.order
|
|
3663
|
-
}));
|
|
3664
|
-
try {
|
|
3665
|
-
while (!abortController.signal.aborted) {
|
|
3666
|
-
const activeBlock = await monitor.getActiveBlock();
|
|
3667
|
-
if (activeBlock == null) {
|
|
3668
|
-
terminal.clearScreen();
|
|
3669
|
-
terminal.write(import_picocolors$4.default.yellow("No active session block found. Waiting...\n"));
|
|
3670
|
-
try {
|
|
3671
|
-
await delay(config.refreshInterval, { signal: abortController.signal });
|
|
3672
|
-
} catch (error) {
|
|
3673
|
-
if ((error instanceof DOMException || error instanceof Error) && error.name === "AbortError") break;
|
|
3674
|
-
throw error;
|
|
3675
|
-
}
|
|
3676
|
-
continue;
|
|
3677
|
-
}
|
|
3678
|
-
terminal.clearScreen();
|
|
3679
|
-
renderLiveDisplay(terminal, activeBlock, config);
|
|
3680
|
-
try {
|
|
3681
|
-
await delay(config.refreshInterval, { signal: abortController.signal });
|
|
3682
|
-
} catch (error) {
|
|
3683
|
-
if ((error instanceof DOMException || error instanceof Error) && error.name === "AbortError") break;
|
|
3684
|
-
throw error;
|
|
3685
|
-
}
|
|
3686
|
-
}
|
|
3687
|
-
} catch (error) {
|
|
3688
|
-
if ((error instanceof DOMException || error instanceof Error) && error.name === "AbortError") return;
|
|
3689
|
-
terminal.clearScreen();
|
|
3690
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3691
|
-
terminal.write(import_picocolors$4.default.red(`Error: ${errorMessage}\n`));
|
|
3692
|
-
logger.error(`Live monitoring error: ${errorMessage}`);
|
|
3693
|
-
try {
|
|
3694
|
-
await delay(config.refreshInterval, { signal: abortController.signal });
|
|
3695
|
-
} catch {}
|
|
3696
|
-
}
|
|
3697
|
-
} catch (_) {
|
|
3698
|
-
_usingCtx.e = _;
|
|
3699
|
-
} finally {
|
|
3700
|
-
_usingCtx.d();
|
|
3701
|
-
}
|
|
3702
|
-
}
|
|
3703
|
-
/**
|
|
3704
3732
|
* Renders the live display for an active session block
|
|
3705
3733
|
*/
|
|
3706
3734
|
function renderLiveDisplay(terminal, block, config) {
|
|
@@ -3713,7 +3741,6 @@ function renderLiveDisplay(terminal, block, config) {
|
|
|
3713
3741
|
renderCompactLiveDisplay(terminal, block, config, totalTokens, elapsed, remaining);
|
|
3714
3742
|
return;
|
|
3715
3743
|
}
|
|
3716
|
-
terminal.clearScreen();
|
|
3717
3744
|
const boxWidth = Math.min(120, width - 2);
|
|
3718
3745
|
const boxMargin = Math.floor((width - boxWidth) / 2);
|
|
3719
3746
|
const marginStr = " ".repeat(boxMargin);
|
|
@@ -3725,8 +3752,8 @@ function renderLiveDisplay(terminal, block, config) {
|
|
|
3725
3752
|
const sessionPercent = elapsed / sessionDuration * 100;
|
|
3726
3753
|
const sessionProgressBar = createProgressBar(elapsed, sessionDuration, barWidth, {
|
|
3727
3754
|
showPercentage: false,
|
|
3728
|
-
fillChar: import_picocolors$
|
|
3729
|
-
emptyChar: import_picocolors$
|
|
3755
|
+
fillChar: import_picocolors$5.default.cyan("█"),
|
|
3756
|
+
emptyChar: import_picocolors$5.default.gray("░"),
|
|
3730
3757
|
leftBracket: "[",
|
|
3731
3758
|
rightBracket: "]"
|
|
3732
3759
|
});
|
|
@@ -3743,96 +3770,100 @@ function renderLiveDisplay(terminal, block, config) {
|
|
|
3743
3770
|
hour12: true
|
|
3744
3771
|
});
|
|
3745
3772
|
terminal.write(`${marginStr}┌${"─".repeat(boxWidth - 2)}┐\n`);
|
|
3746
|
-
terminal.write(`${marginStr}│${import_picocolors$
|
|
3773
|
+
terminal.write(`${marginStr}│${import_picocolors$5.default.bold(centerText("CLAUDE CODE - LIVE TOKEN USAGE MONITOR", boxWidth - 2))}│\n`);
|
|
3747
3774
|
terminal.write(`${marginStr}├${"─".repeat(boxWidth - 2)}┤\n`);
|
|
3748
3775
|
terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
|
|
3749
|
-
const sessionLabel = import_picocolors$
|
|
3776
|
+
const sessionLabel = import_picocolors$5.default.bold("⏱️ SESSION");
|
|
3750
3777
|
const sessionLabelWidth = stringWidth(sessionLabel);
|
|
3751
3778
|
const sessionBarStr = `${sessionLabel}${"".padEnd(Math.max(0, labelWidth - sessionLabelWidth))} ${sessionProgressBar} ${sessionPercent.toFixed(1).padStart(6)}%`;
|
|
3752
3779
|
const sessionBarPadded = sessionBarStr + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(sessionBarStr)));
|
|
3753
3780
|
terminal.write(`${marginStr}│ ${sessionBarPadded}│\n`);
|
|
3754
|
-
const col1 = `${import_picocolors$
|
|
3755
|
-
const col2 = `${import_picocolors$
|
|
3756
|
-
const col3 = `${import_picocolors$
|
|
3781
|
+
const col1 = `${import_picocolors$5.default.gray("Started:")} ${startTime}`;
|
|
3782
|
+
const col2 = `${import_picocolors$5.default.gray("Elapsed:")} ${prettyMilliseconds(elapsed * 60 * 1e3, { compact: true })}`;
|
|
3783
|
+
const col3 = `${import_picocolors$5.default.gray("Remaining:")} ${prettyMilliseconds(remaining * 60 * 1e3, { compact: true })} (${endTime})`;
|
|
3757
3784
|
const col1Visible = stringWidth(col1);
|
|
3758
3785
|
const col2Visible = stringWidth(col2);
|
|
3759
3786
|
const pad1 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col1 - col1Visible));
|
|
3760
3787
|
const pad2 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col2 - col2Visible));
|
|
3761
|
-
const sessionDetails = ` ${col1}${pad1}${
|
|
3788
|
+
const sessionDetails = ` ${col1}${pad1}${pad2}${col3}`;
|
|
3762
3789
|
const sessionDetailsPadded = sessionDetails + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(sessionDetails)));
|
|
3790
|
+
let usageLimitResetTimePadded = null;
|
|
3791
|
+
if (block.usageLimitResetTime !== void 0 && now < block.usageLimitResetTime) {
|
|
3792
|
+
const resetTime = block.usageLimitResetTime?.toLocaleTimeString(void 0, {
|
|
3793
|
+
hour: "2-digit",
|
|
3794
|
+
minute: "2-digit",
|
|
3795
|
+
hour12: true
|
|
3796
|
+
}) ?? null;
|
|
3797
|
+
const usageLimitResetTime = resetTime !== null ? import_picocolors$5.default.red(`❌ USAGE LIMIT. RESET AT ${resetTime}`) : "";
|
|
3798
|
+
usageLimitResetTimePadded = resetTime !== null ? usageLimitResetTime + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(usageLimitResetTime))) : null;
|
|
3799
|
+
}
|
|
3763
3800
|
terminal.write(`${marginStr}│ ${sessionDetailsPadded}│\n`);
|
|
3801
|
+
if (usageLimitResetTimePadded !== null) terminal.write(`${marginStr}│ ${usageLimitResetTimePadded}│\n`);
|
|
3764
3802
|
terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
|
|
3765
3803
|
terminal.write(`${marginStr}├${"─".repeat(boxWidth - 2)}┤\n`);
|
|
3766
3804
|
terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
|
|
3767
3805
|
const tokenPercent = config.tokenLimit != null && config.tokenLimit > 0 ? totalTokens / config.tokenLimit * 100 : 0;
|
|
3768
|
-
let barColor = import_picocolors$
|
|
3769
|
-
if (tokenPercent > 100) barColor = import_picocolors$
|
|
3770
|
-
else if (tokenPercent > 80) barColor = import_picocolors$
|
|
3806
|
+
let barColor = import_picocolors$5.default.green;
|
|
3807
|
+
if (tokenPercent > 100) barColor = import_picocolors$5.default.red;
|
|
3808
|
+
else if (tokenPercent > 80) barColor = import_picocolors$5.default.yellow;
|
|
3771
3809
|
const usageBar = config.tokenLimit != null && config.tokenLimit > 0 ? createProgressBar(totalTokens, config.tokenLimit, barWidth, {
|
|
3772
3810
|
showPercentage: false,
|
|
3773
3811
|
fillChar: barColor("█"),
|
|
3774
|
-
emptyChar: import_picocolors$
|
|
3812
|
+
emptyChar: import_picocolors$5.default.gray("░"),
|
|
3775
3813
|
leftBracket: "[",
|
|
3776
3814
|
rightBracket: "]"
|
|
3777
|
-
}) : `[${import_picocolors$
|
|
3815
|
+
}) : `[${import_picocolors$5.default.green("█".repeat(Math.floor(barWidth * .1)))}${import_picocolors$5.default.gray("░".repeat(barWidth - Math.floor(barWidth * .1)))}]`;
|
|
3778
3816
|
const burnRate = calculateBurnRate(block);
|
|
3779
|
-
const rateIndicator = burnRate != null ? burnRate.tokensPerMinute > 1e3 ? import_picocolors$
|
|
3780
|
-
const rateDisplay = burnRate != null ? `${import_picocolors$
|
|
3781
|
-
const usageLabel = import_picocolors$
|
|
3817
|
+
const rateIndicator = burnRate != null ? burnRate.tokensPerMinute > 1e3 ? import_picocolors$5.default.red("⚡ HIGH") : burnRate.tokensPerMinute > 500 ? import_picocolors$5.default.yellow("⚡ MODERATE") : import_picocolors$5.default.green("✓ NORMAL") : "";
|
|
3818
|
+
const rateDisplay = burnRate != null ? `${import_picocolors$5.default.bold("Burn Rate:")} ${Math.round(burnRate.tokensPerMinute)} token/min ${rateIndicator}` : `${import_picocolors$5.default.bold("Burn Rate:")} N/A`;
|
|
3819
|
+
const usageLabel = import_picocolors$5.default.bold("🔥 USAGE");
|
|
3782
3820
|
const usageLabelWidth = stringWidth(usageLabel);
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
const col1Visible$1 = stringWidth(col1$1);
|
|
3804
|
-
const pad1$1 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col1 - col1Visible$1));
|
|
3805
|
-
const pad2$1 = " ".repeat(DETAIL_COLUMN_WIDTHS.col2);
|
|
3806
|
-
const usageDetails = ` ${col1$1}${pad1$1}${pad2$1}${col3$1}`;
|
|
3807
|
-
const usageDetailsPadded = usageDetails + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(usageDetails)));
|
|
3808
|
-
terminal.write(`${marginStr}│ ${usageDetailsPadded}│\n`);
|
|
3809
|
-
}
|
|
3821
|
+
const { usageBarStr, usageCol1, usageCol2, usageCol3 } = config.tokenLimit != null && config.tokenLimit > 0 ? {
|
|
3822
|
+
usageBarStr: `${usageLabel}${"".padEnd(Math.max(0, labelWidth - usageLabelWidth))} ${usageBar} ${tokenPercent.toFixed(1).padStart(6)}% (${formatTokensShort(totalTokens)}/${formatTokensShort(config.tokenLimit)})`,
|
|
3823
|
+
usageCol1: `${import_picocolors$5.default.gray("Tokens:")} ${formatNumber(totalTokens)} (${rateDisplay})`,
|
|
3824
|
+
usageCol2: `${import_picocolors$5.default.gray("Limit:")} ${formatNumber(config.tokenLimit)} tokens`,
|
|
3825
|
+
usageCol3: `${import_picocolors$5.default.gray("Cost:")} ${formatCurrency(block.costUSD)}`
|
|
3826
|
+
} : {
|
|
3827
|
+
usageBarStr: `${usageLabel}${"".padEnd(Math.max(0, labelWidth - usageLabelWidth))} ${usageBar} (${formatTokensShort(totalTokens)} tokens)`,
|
|
3828
|
+
usageCol1: `${import_picocolors$5.default.gray("Tokens:")} ${formatNumber(totalTokens)} (${rateDisplay})`,
|
|
3829
|
+
usageCol2: "",
|
|
3830
|
+
usageCol3: `${import_picocolors$5.default.gray("Cost:")} ${formatCurrency(block.costUSD)}`
|
|
3831
|
+
};
|
|
3832
|
+
const usageBarPadded = usageBarStr + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(usageBarStr)));
|
|
3833
|
+
terminal.write(`${marginStr}│ ${usageBarPadded}│\n`);
|
|
3834
|
+
const usageCol1Visible = stringWidth(usageCol1);
|
|
3835
|
+
const usageCol2Visible = stringWidth(usageCol2);
|
|
3836
|
+
const usagePad1 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col1 - usageCol1Visible));
|
|
3837
|
+
const usagePad2 = usageCol2.length > 0 ? " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col2 - usageCol2Visible)) : " ".repeat(DETAIL_COLUMN_WIDTHS.col2);
|
|
3838
|
+
const usageDetails = ` ${usageCol1}${usagePad1}${usageCol2}${usagePad2}${usageCol3}`;
|
|
3839
|
+
const usageDetailsPadded = usageDetails + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(usageDetails)));
|
|
3840
|
+
terminal.write(`${marginStr}│ ${usageDetailsPadded}│\n`);
|
|
3810
3841
|
terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
|
|
3811
3842
|
terminal.write(`${marginStr}├${"─".repeat(boxWidth - 2)}┤\n`);
|
|
3812
3843
|
terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
|
|
3813
3844
|
const projection = projectBlockUsage(block);
|
|
3814
3845
|
if (projection != null) {
|
|
3815
3846
|
const projectedPercent = config.tokenLimit != null && config.tokenLimit > 0 ? projection.totalTokens / config.tokenLimit * 100 : 0;
|
|
3816
|
-
let projBarColor = import_picocolors$
|
|
3817
|
-
if (projectedPercent > 100) projBarColor = import_picocolors$
|
|
3818
|
-
else if (projectedPercent > 80) projBarColor = import_picocolors$
|
|
3847
|
+
let projBarColor = import_picocolors$5.default.green;
|
|
3848
|
+
if (projectedPercent > 100) projBarColor = import_picocolors$5.default.red;
|
|
3849
|
+
else if (projectedPercent > 80) projBarColor = import_picocolors$5.default.yellow;
|
|
3819
3850
|
const projectionBar = config.tokenLimit != null && config.tokenLimit > 0 ? createProgressBar(projection.totalTokens, config.tokenLimit, barWidth, {
|
|
3820
3851
|
showPercentage: false,
|
|
3821
3852
|
fillChar: projBarColor("█"),
|
|
3822
|
-
emptyChar: import_picocolors$
|
|
3853
|
+
emptyChar: import_picocolors$5.default.gray("░"),
|
|
3823
3854
|
leftBracket: "[",
|
|
3824
3855
|
rightBracket: "]"
|
|
3825
|
-
}) : `[${import_picocolors$
|
|
3826
|
-
const limitStatus = config.tokenLimit != null && config.tokenLimit > 0 ? projectedPercent > 100 ? import_picocolors$
|
|
3827
|
-
const projLabel = import_picocolors$
|
|
3856
|
+
}) : `[${import_picocolors$5.default.green("█".repeat(Math.floor(barWidth * .15)))}${import_picocolors$5.default.gray("░".repeat(barWidth - Math.floor(barWidth * .15)))}]`;
|
|
3857
|
+
const limitStatus = config.tokenLimit != null && config.tokenLimit > 0 ? projectedPercent > 100 ? import_picocolors$5.default.red("❌ WILL EXCEED LIMIT") : projectedPercent > 80 ? import_picocolors$5.default.yellow("⚠️ APPROACHING LIMIT") : import_picocolors$5.default.green("✓ WITHIN LIMIT") : import_picocolors$5.default.green("✓ ON TRACK");
|
|
3858
|
+
const projLabel = import_picocolors$5.default.bold("📈 PROJECTION");
|
|
3828
3859
|
const projLabelWidth = stringWidth(projLabel);
|
|
3829
3860
|
if (config.tokenLimit != null && config.tokenLimit > 0) {
|
|
3830
3861
|
const projBarStr = `${projLabel}${"".padEnd(Math.max(0, labelWidth - projLabelWidth))} ${projectionBar} ${projectedPercent.toFixed(1).padStart(6)}% (${formatTokensShort(projection.totalTokens)}/${formatTokensShort(config.tokenLimit)})`;
|
|
3831
3862
|
const projBarPadded = projBarStr + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(projBarStr)));
|
|
3832
3863
|
terminal.write(`${marginStr}│ ${projBarPadded}│\n`);
|
|
3833
|
-
const col1$1 = `${import_picocolors$
|
|
3834
|
-
const col2$1 = `${import_picocolors$
|
|
3835
|
-
const col3$1 = `${import_picocolors$
|
|
3864
|
+
const col1$1 = `${import_picocolors$5.default.gray("Status:")} ${limitStatus}`;
|
|
3865
|
+
const col2$1 = `${import_picocolors$5.default.gray("Tokens:")} ${formatNumber(projection.totalTokens)}`;
|
|
3866
|
+
const col3$1 = `${import_picocolors$5.default.gray("Cost:")} ${formatCurrency(projection.totalCost)}`;
|
|
3836
3867
|
const col1Visible$1 = stringWidth(col1$1);
|
|
3837
3868
|
const col2Visible$1 = stringWidth(col2$1);
|
|
3838
3869
|
const pad1$1 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col1 - col1Visible$1));
|
|
@@ -3844,9 +3875,9 @@ function renderLiveDisplay(terminal, block, config) {
|
|
|
3844
3875
|
const projBarStr = `${projLabel}${"".padEnd(Math.max(0, labelWidth - projLabelWidth))} ${projectionBar} (${formatTokensShort(projection.totalTokens)} tokens)`;
|
|
3845
3876
|
const projBarPadded = projBarStr + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(projBarStr)));
|
|
3846
3877
|
terminal.write(`${marginStr}│ ${projBarPadded}│\n`);
|
|
3847
|
-
const col1$1 = `${import_picocolors$
|
|
3848
|
-
const col2$1 = `${import_picocolors$
|
|
3849
|
-
const col3$1 = `${import_picocolors$
|
|
3878
|
+
const col1$1 = `${import_picocolors$5.default.gray("Status:")} ${limitStatus}`;
|
|
3879
|
+
const col2$1 = `${import_picocolors$5.default.gray("Tokens:")} ${formatNumber(projection.totalTokens)}`;
|
|
3880
|
+
const col3$1 = `${import_picocolors$5.default.gray("Cost:")} ${formatCurrency(projection.totalCost)}`;
|
|
3850
3881
|
const col1Visible$1 = stringWidth(col1$1);
|
|
3851
3882
|
const col2Visible$1 = stringWidth(col2$1);
|
|
3852
3883
|
const pad1$1 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col1 - col1Visible$1));
|
|
@@ -3865,7 +3896,7 @@ function renderLiveDisplay(terminal, block, config) {
|
|
|
3865
3896
|
}
|
|
3866
3897
|
terminal.write(`${marginStr}├${"─".repeat(boxWidth - 2)}┤\n`);
|
|
3867
3898
|
const refreshText = `↻ Refreshing every ${config.refreshInterval / 1e3}s • Press Ctrl+C to stop`;
|
|
3868
|
-
terminal.write(`${marginStr}│${import_picocolors$
|
|
3899
|
+
terminal.write(`${marginStr}│${import_picocolors$5.default.gray(centerText(refreshText, boxWidth - 2))}│\n`);
|
|
3869
3900
|
terminal.write(`${marginStr}└${"─".repeat(boxWidth - 2)}┘\n`);
|
|
3870
3901
|
}
|
|
3871
3902
|
/**
|
|
@@ -3873,20 +3904,86 @@ function renderLiveDisplay(terminal, block, config) {
|
|
|
3873
3904
|
*/
|
|
3874
3905
|
function renderCompactLiveDisplay(terminal, block, config, totalTokens, elapsed, remaining) {
|
|
3875
3906
|
const width = terminal.width;
|
|
3876
|
-
terminal.write(`${import_picocolors$
|
|
3907
|
+
terminal.write(`${import_picocolors$5.default.bold(centerText("LIVE MONITOR", width))}\n`);
|
|
3877
3908
|
terminal.write(`${"─".repeat(width)}\n`);
|
|
3878
3909
|
const sessionPercent = elapsed / (elapsed + remaining) * 100;
|
|
3879
3910
|
terminal.write(`Session: ${sessionPercent.toFixed(1)}% (${Math.floor(elapsed / 60)}h ${Math.floor(elapsed % 60)}m)\n`);
|
|
3880
3911
|
if (config.tokenLimit != null && config.tokenLimit > 0) {
|
|
3881
3912
|
const tokenPercent = totalTokens / config.tokenLimit * 100;
|
|
3882
|
-
const status = tokenPercent > 100 ? import_picocolors$
|
|
3913
|
+
const status = tokenPercent > 100 ? import_picocolors$5.default.red("OVER") : tokenPercent > 80 ? import_picocolors$5.default.yellow("WARN") : import_picocolors$5.default.green("OK");
|
|
3883
3914
|
terminal.write(`Tokens: ${formatNumber(totalTokens)}/${formatNumber(config.tokenLimit)} ${status}\n`);
|
|
3884
3915
|
} else terminal.write(`Tokens: ${formatNumber(totalTokens)}\n`);
|
|
3885
3916
|
terminal.write(`Cost: ${formatCurrency(block.costUSD)}\n`);
|
|
3886
3917
|
const burnRate = calculateBurnRate(block);
|
|
3887
3918
|
if (burnRate != null) terminal.write(`Rate: ${formatNumber(burnRate.tokensPerMinute)}/min\n`);
|
|
3888
3919
|
terminal.write(`${"─".repeat(width)}\n`);
|
|
3889
|
-
terminal.write(import_picocolors$
|
|
3920
|
+
terminal.write(import_picocolors$5.default.gray(`Refresh: ${config.refreshInterval / 1e3}s | Ctrl+C: stop\n`));
|
|
3921
|
+
}
|
|
3922
|
+
var import_picocolors$4 = __toESM(require_picocolors(), 1);
|
|
3923
|
+
var import_usingCtx = __toESM(require_usingCtx(), 1);
|
|
3924
|
+
async function startLiveMonitoring(config) {
|
|
3925
|
+
try {
|
|
3926
|
+
var _usingCtx = (0, import_usingCtx.default)();
|
|
3927
|
+
const terminal = new TerminalManager();
|
|
3928
|
+
const abortController = new AbortController();
|
|
3929
|
+
let lastRenderTime = 0;
|
|
3930
|
+
const cleanup = () => {
|
|
3931
|
+
abortController.abort();
|
|
3932
|
+
terminal.cleanup();
|
|
3933
|
+
terminal.clearScreen();
|
|
3934
|
+
logger.info("Live monitoring stopped.");
|
|
3935
|
+
if (process$1.exitCode == null) process$1.exit(0);
|
|
3936
|
+
};
|
|
3937
|
+
process$1.on("SIGINT", cleanup);
|
|
3938
|
+
process$1.on("SIGTERM", cleanup);
|
|
3939
|
+
terminal.enterAlternateScreen();
|
|
3940
|
+
terminal.enableSyncMode();
|
|
3941
|
+
terminal.clearScreen();
|
|
3942
|
+
terminal.hideCursor();
|
|
3943
|
+
const monitor = _usingCtx.u(new LiveMonitor({
|
|
3944
|
+
claudePath: config.claudePath,
|
|
3945
|
+
sessionDurationHours: config.sessionDurationHours,
|
|
3946
|
+
mode: config.mode,
|
|
3947
|
+
order: config.order
|
|
3948
|
+
}));
|
|
3949
|
+
const monitoringResult = await try_({
|
|
3950
|
+
try: async () => {
|
|
3951
|
+
while (!abortController.signal.aborted) {
|
|
3952
|
+
const now = Date.now();
|
|
3953
|
+
const timeSinceLastRender = now - lastRenderTime;
|
|
3954
|
+
if (timeSinceLastRender < MIN_RENDER_INTERVAL_MS) {
|
|
3955
|
+
await delayWithAbort(MIN_RENDER_INTERVAL_MS - timeSinceLastRender, abortController.signal);
|
|
3956
|
+
continue;
|
|
3957
|
+
}
|
|
3958
|
+
const activeBlock = await monitor.getActiveBlock();
|
|
3959
|
+
monitor.clearCache();
|
|
3960
|
+
if (activeBlock == null) {
|
|
3961
|
+
await renderWaitingState(terminal, config, abortController.signal);
|
|
3962
|
+
continue;
|
|
3963
|
+
}
|
|
3964
|
+
renderActiveBlock(terminal, activeBlock, config);
|
|
3965
|
+
lastRenderTime = Date.now();
|
|
3966
|
+
await delayWithAbort(config.refreshInterval, abortController.signal);
|
|
3967
|
+
}
|
|
3968
|
+
},
|
|
3969
|
+
catch: (error) => error
|
|
3970
|
+
})();
|
|
3971
|
+
if (isFailure(monitoringResult)) {
|
|
3972
|
+
const error = monitoringResult.error;
|
|
3973
|
+
if ((error instanceof DOMException || error instanceof Error) && error.name === "AbortError") return;
|
|
3974
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3975
|
+
terminal.startBuffering();
|
|
3976
|
+
terminal.clearScreen();
|
|
3977
|
+
terminal.write(import_picocolors$4.default.red(`Error: ${errorMessage}\n`));
|
|
3978
|
+
terminal.flush();
|
|
3979
|
+
logger.error(`Live monitoring error: ${errorMessage}`);
|
|
3980
|
+
await delayWithAbort(config.refreshInterval, abortController.signal).catch(() => {});
|
|
3981
|
+
}
|
|
3982
|
+
} catch (_) {
|
|
3983
|
+
_usingCtx.e = _;
|
|
3984
|
+
} finally {
|
|
3985
|
+
_usingCtx.d();
|
|
3986
|
+
}
|
|
3890
3987
|
}
|
|
3891
3988
|
var import_picocolors$3 = __toESM(require_picocolors(), 1);
|
|
3892
3989
|
/**
|
|
@@ -4011,7 +4108,7 @@ const blocksCommand = define({
|
|
|
4011
4108
|
let maxTokensFromAll = 0;
|
|
4012
4109
|
if (ctx.values.tokenLimit === "max") {
|
|
4013
4110
|
for (const block of blocks) if (!(block.isGap ?? false) && !block.isActive) {
|
|
4014
|
-
const blockTokens = block.tokenCounts
|
|
4111
|
+
const blockTokens = getTotalTokens(block.tokenCounts);
|
|
4015
4112
|
if (blockTokens > maxTokensFromAll) maxTokensFromAll = blockTokens;
|
|
4016
4113
|
}
|
|
4017
4114
|
if (!ctx.values.json && maxTokensFromAll > 0) logger.info(`Using max tokens from previous sessions: ${formatNumber(maxTokensFromAll)}`);
|
|
@@ -4037,8 +4134,13 @@ const blocksCommand = define({
|
|
|
4037
4134
|
}
|
|
4038
4135
|
const refreshInterval = Math.max(MIN_REFRESH_INTERVAL_SECONDS, Math.min(MAX_REFRESH_INTERVAL_SECONDS, ctx.values.refreshInterval));
|
|
4039
4136
|
if (refreshInterval !== ctx.values.refreshInterval) logger.warn(`Refresh interval adjusted to ${refreshInterval} seconds (valid range: ${MIN_REFRESH_INTERVAL_SECONDS}-${MAX_REFRESH_INTERVAL_SECONDS})`);
|
|
4137
|
+
const paths = getClaudePaths();
|
|
4138
|
+
if (paths.length === 0) {
|
|
4139
|
+
logger.error("No valid Claude data directory found");
|
|
4140
|
+
throw new Error("No valid Claude data directory found");
|
|
4141
|
+
}
|
|
4040
4142
|
await startLiveMonitoring({
|
|
4041
|
-
claudePath:
|
|
4143
|
+
claudePath: paths[0],
|
|
4042
4144
|
tokenLimit: parseTokenLimit(tokenLimitValue, maxTokensFromAll),
|
|
4043
4145
|
refreshInterval: refreshInterval * 1e3,
|
|
4044
4146
|
sessionDurationHours: ctx.values.sessionLength,
|
|
@@ -4073,7 +4175,8 @@ const blocksCommand = define({
|
|
|
4073
4175
|
percentUsed: projection.totalTokens / limit * 100,
|
|
4074
4176
|
status: projection.totalTokens > limit ? "exceeds" : projection.totalTokens > limit * BLOCKS_WARNING_THRESHOLD ? "warning" : "ok"
|
|
4075
4177
|
} : void 0;
|
|
4076
|
-
})() : void 0
|
|
4178
|
+
})() : void 0,
|
|
4179
|
+
usageLimitResetTime: block.usageLimitResetTime
|
|
4077
4180
|
};
|
|
4078
4181
|
}) };
|
|
4079
4182
|
log(JSON.stringify(jsonOutput, null, 2));
|
|
@@ -4107,7 +4210,7 @@ const blocksCommand = define({
|
|
|
4107
4210
|
if (ctx.values.tokenLimit != null) {
|
|
4108
4211
|
const limit = parseTokenLimit(ctx.values.tokenLimit, maxTokensFromAll);
|
|
4109
4212
|
if (limit != null && limit > 0) {
|
|
4110
|
-
const currentTokens = block.tokenCounts
|
|
4213
|
+
const currentTokens = getTotalTokens(block.tokenCounts);
|
|
4111
4214
|
const remainingTokens = Math.max(0, limit - currentTokens);
|
|
4112
4215
|
const percentUsed = projection.totalTokens / limit * 100;
|
|
4113
4216
|
const status = percentUsed > 100 ? import_picocolors$3.default.red("EXCEEDS LIMIT") : percentUsed > BLOCKS_WARNING_THRESHOLD * 100 ? import_picocolors$3.default.yellow("WARNING") : import_picocolors$3.default.green("OK");
|
|
@@ -4158,7 +4261,7 @@ const blocksCommand = define({
|
|
|
4158
4261
|
gapRow.push(import_picocolors$3.default.gray("-"));
|
|
4159
4262
|
table.push(gapRow);
|
|
4160
4263
|
} else {
|
|
4161
|
-
const totalTokens = block.tokenCounts
|
|
4264
|
+
const totalTokens = getTotalTokens(block.tokenCounts);
|
|
4162
4265
|
const status = block.isActive ? import_picocolors$3.default.green("ACTIVE") : "";
|
|
4163
4266
|
const row = [
|
|
4164
4267
|
formatBlockTime(block, useCompactFormat),
|
|
@@ -4175,7 +4278,7 @@ const blocksCommand = define({
|
|
|
4175
4278
|
table.push(row);
|
|
4176
4279
|
if (block.isActive) {
|
|
4177
4280
|
if (actualTokenLimit != null && actualTokenLimit > 0) {
|
|
4178
|
-
const currentTokens = block.tokenCounts
|
|
4281
|
+
const currentTokens = getTotalTokens(block.tokenCounts);
|
|
4179
4282
|
const remainingTokens = Math.max(0, actualTokenLimit - currentTokens);
|
|
4180
4283
|
const remainingText = remainingTokens > 0 ? formatNumber(remainingTokens) : import_picocolors$3.default.red("0");
|
|
4181
4284
|
const remainingPercent = (actualTokenLimit - currentTokens) / actualTokenLimit * 100;
|
|
@@ -4732,8 +4835,13 @@ const mcpCommand = define({
|
|
|
4732
4835
|
async run(ctx) {
|
|
4733
4836
|
const { type, mode, port } = ctx.values;
|
|
4734
4837
|
if (type === "stdio") logger.level = 0;
|
|
4838
|
+
const paths = getClaudePaths();
|
|
4839
|
+
if (paths.length === 0) {
|
|
4840
|
+
logger.error("No valid Claude data directory found");
|
|
4841
|
+
throw new Error("No valid Claude data directory found");
|
|
4842
|
+
}
|
|
4735
4843
|
const options = {
|
|
4736
|
-
claudePath:
|
|
4844
|
+
claudePath: paths[0],
|
|
4737
4845
|
mode
|
|
4738
4846
|
};
|
|
4739
4847
|
if (type === "stdio") {
|