@zhangferry-dev/tokendash 1.4.2 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/assets/{index-B4YgU_cb.js → index-CY4G_b0x.js} +49 -49
- package/dist/client/assets/index-_yA9tOzZ.css +1 -0
- package/dist/client/index.html +2 -2
- package/dist/client/popover.html +822 -396
- package/dist/electron-server.cjs +255 -101
- package/dist/electron-server.cjs.map +3 -3
- package/dist/server/analyticsParser.js +66 -2
- package/dist/server/claudeBlocksParser.js +50 -7
- package/dist/server/claudeJsonlParser.d.ts +7 -1
- package/dist/server/claudeJsonlParser.js +75 -23
- package/dist/server/codexParser.d.ts +6 -2
- package/dist/server/codexParser.js +134 -80
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.js +25 -9
- package/electron/main.cjs +35 -50
- package/electron/preload.cjs +11 -0
- package/electron/updateService.cjs +148 -0
- package/package.json +1 -1
- package/dist/client/assets/index-iYDpTV63.css +0 -1
package/dist/electron-server.cjs
CHANGED
|
@@ -32,7 +32,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
32
32
|
var index_exports = {};
|
|
33
33
|
__export(index_exports, {
|
|
34
34
|
createApp: () => createApp,
|
|
35
|
-
main: () => main
|
|
35
|
+
main: () => main,
|
|
36
|
+
resolveStaticAssetBaseDir: () => resolveStaticAssetBaseDir
|
|
36
37
|
});
|
|
37
38
|
module.exports = __toCommonJS(index_exports);
|
|
38
39
|
var import_express = __toESM(require("express"), 1);
|
|
@@ -254,20 +255,24 @@ var TokenUsageSchema = import_zod2.z.object({
|
|
|
254
255
|
}).default({ input_tokens: 0, cached_input_tokens: 0, output_tokens: 0, reasoning_output_tokens: 0, total_tokens: 0 });
|
|
255
256
|
var TokenCountInfoSchema = import_zod2.z.object({
|
|
256
257
|
total_token_usage: TokenUsageSchema,
|
|
257
|
-
last_token_usage: TokenUsageSchema
|
|
258
|
+
last_token_usage: TokenUsageSchema.optional()
|
|
258
259
|
}).nullable().default(null);
|
|
259
260
|
var TokenCountPayloadSchema = import_zod2.z.object({
|
|
260
261
|
type: import_zod2.z.literal("token_count"),
|
|
261
262
|
info: TokenCountInfoSchema
|
|
262
263
|
});
|
|
263
|
-
function
|
|
264
|
-
return
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
264
|
+
function subtractTokenUsage(current, previous) {
|
|
265
|
+
return {
|
|
266
|
+
timestamp: "",
|
|
267
|
+
inputTokens: Math.max(0, current.input_tokens - (previous?.input_tokens ?? 0)),
|
|
268
|
+
cachedInputTokens: Math.max(0, current.cached_input_tokens - (previous?.cached_input_tokens ?? 0)),
|
|
269
|
+
outputTokens: Math.max(0, current.output_tokens - (previous?.output_tokens ?? 0)),
|
|
270
|
+
reasoningOutputTokens: Math.max(0, current.reasoning_output_tokens - (previous?.reasoning_output_tokens ?? 0)),
|
|
271
|
+
totalTokens: Math.max(0, current.total_tokens - (previous?.total_tokens ?? 0))
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
function displayInputTokens(inputTokens, cachedInputTokens) {
|
|
275
|
+
return Math.max(0, inputTokens - cachedInputTokens);
|
|
271
276
|
}
|
|
272
277
|
function getSessionsDir() {
|
|
273
278
|
return (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".codex", "sessions");
|
|
@@ -313,7 +318,9 @@ function parseCodexSession(filepath) {
|
|
|
313
318
|
let model = "";
|
|
314
319
|
let createdAt = "";
|
|
315
320
|
const tokenEvents = [];
|
|
321
|
+
let previousTotalUsage = null;
|
|
316
322
|
const seenTotalUsageSnapshots = /* @__PURE__ */ new Set();
|
|
323
|
+
const seenUsageEvents = /* @__PURE__ */ new Set();
|
|
317
324
|
for (const line of lines) {
|
|
318
325
|
const trimmed = line.trim();
|
|
319
326
|
if (!trimmed) continue;
|
|
@@ -347,18 +354,40 @@ function parseCodexSession(filepath) {
|
|
|
347
354
|
}
|
|
348
355
|
const info = parseResult.data.info;
|
|
349
356
|
if (!info) continue;
|
|
350
|
-
const totalUsageKey =
|
|
357
|
+
const totalUsageKey = [
|
|
358
|
+
info.total_token_usage.input_tokens,
|
|
359
|
+
info.total_token_usage.cached_input_tokens,
|
|
360
|
+
info.total_token_usage.output_tokens,
|
|
361
|
+
info.total_token_usage.reasoning_output_tokens,
|
|
362
|
+
info.total_token_usage.total_tokens
|
|
363
|
+
].join(":");
|
|
351
364
|
if (seenTotalUsageSnapshots.has(totalUsageKey)) continue;
|
|
352
365
|
seenTotalUsageSnapshots.add(totalUsageKey);
|
|
353
|
-
const last = info.last_token_usage;
|
|
354
|
-
|
|
366
|
+
const last = info.last_token_usage ?? info.total_token_usage;
|
|
367
|
+
const rawEvent = info.last_token_usage ? subtractTokenUsage(last, null) : subtractTokenUsage(last, previousTotalUsage);
|
|
368
|
+
previousTotalUsage = info.total_token_usage;
|
|
369
|
+
if (rawEvent.inputTokens === 0 && rawEvent.cachedInputTokens === 0 && rawEvent.outputTokens === 0 && rawEvent.reasoningOutputTokens === 0) {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
const event = {
|
|
373
|
+
...rawEvent,
|
|
355
374
|
timestamp,
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
375
|
+
cachedInputTokens: Math.min(rawEvent.cachedInputTokens, rawEvent.inputTokens)
|
|
376
|
+
};
|
|
377
|
+
const eventKey = [
|
|
378
|
+
timestamp,
|
|
379
|
+
model,
|
|
380
|
+
event.inputTokens,
|
|
381
|
+
event.cachedInputTokens,
|
|
382
|
+
event.outputTokens,
|
|
383
|
+
event.reasoningOutputTokens,
|
|
384
|
+
event.totalTokens
|
|
385
|
+
].join(":");
|
|
386
|
+
if (seenUsageEvents.has(eventKey)) {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
seenUsageEvents.add(eventKey);
|
|
390
|
+
tokenEvents.push(event);
|
|
362
391
|
}
|
|
363
392
|
}
|
|
364
393
|
}
|
|
@@ -408,6 +437,12 @@ function addAcc(a, ev) {
|
|
|
408
437
|
a.reasoningOutputTokens += ev.reasoningOutputTokens;
|
|
409
438
|
a.totalTokens += ev.totalTokens;
|
|
410
439
|
}
|
|
440
|
+
function displayAcc(acc) {
|
|
441
|
+
return {
|
|
442
|
+
...acc,
|
|
443
|
+
inputTokens: displayInputTokens(acc.inputTokens, acc.cachedInputTokens)
|
|
444
|
+
};
|
|
445
|
+
}
|
|
411
446
|
function mergeAcc(a, b) {
|
|
412
447
|
a.inputTokens += b.inputTokens;
|
|
413
448
|
a.cachedInputTokens += b.cachedInputTokens;
|
|
@@ -415,32 +450,41 @@ function mergeAcc(a, b) {
|
|
|
415
450
|
a.reasoningOutputTokens += b.reasoningOutputTokens;
|
|
416
451
|
a.totalTokens += b.totalTokens;
|
|
417
452
|
}
|
|
418
|
-
function
|
|
419
|
-
|
|
453
|
+
function addAccToBucket(bucket, ev, model) {
|
|
454
|
+
addAcc(bucket.acc, ev);
|
|
455
|
+
if (!model) return;
|
|
456
|
+
if (!bucket.models.has(model)) bucket.models.set(model, emptyAcc());
|
|
457
|
+
addAcc(bucket.models.get(model), ev);
|
|
458
|
+
}
|
|
459
|
+
function accToEntry(date, acc, modelAccs) {
|
|
460
|
+
const display = displayAcc(acc);
|
|
461
|
+
const modelNames = [...modelAccs.keys()];
|
|
462
|
+
const modelBreakdowns = buildModelBreakdowns(modelAccs);
|
|
463
|
+
const totalCost = modelBreakdowns.reduce((sum, model) => sum + model.cost, 0);
|
|
420
464
|
return {
|
|
421
465
|
date,
|
|
422
|
-
inputTokens:
|
|
423
|
-
outputTokens:
|
|
466
|
+
inputTokens: display.inputTokens,
|
|
467
|
+
outputTokens: display.outputTokens,
|
|
424
468
|
cacheCreationTokens: 0,
|
|
425
|
-
cacheReadTokens:
|
|
426
|
-
totalTokens:
|
|
427
|
-
totalCost
|
|
428
|
-
modelsUsed:
|
|
429
|
-
modelBreakdowns
|
|
469
|
+
cacheReadTokens: display.cachedInputTokens,
|
|
470
|
+
totalTokens: display.totalTokens,
|
|
471
|
+
totalCost,
|
|
472
|
+
modelsUsed: modelNames,
|
|
473
|
+
modelBreakdowns
|
|
430
474
|
};
|
|
431
475
|
}
|
|
432
|
-
function buildModelBreakdowns(
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
})
|
|
476
|
+
function buildModelBreakdowns(modelAccs) {
|
|
477
|
+
return [...modelAccs.entries()].map(([modelName, acc]) => {
|
|
478
|
+
const display = displayAcc(acc);
|
|
479
|
+
return {
|
|
480
|
+
modelName,
|
|
481
|
+
inputTokens: display.inputTokens,
|
|
482
|
+
outputTokens: display.outputTokens,
|
|
483
|
+
cacheCreationTokens: 0,
|
|
484
|
+
cacheReadTokens: display.cachedInputTokens,
|
|
485
|
+
cost: calculateCost(acc, /* @__PURE__ */ new Set([modelName]))
|
|
486
|
+
};
|
|
487
|
+
});
|
|
444
488
|
}
|
|
445
489
|
function groupSessions(sessions, options) {
|
|
446
490
|
const tz = options.timezone || "Asia/Shanghai";
|
|
@@ -470,32 +514,32 @@ function groupSessions(sessions, options) {
|
|
|
470
514
|
break;
|
|
471
515
|
}
|
|
472
516
|
if (!grouped.has(key)) {
|
|
473
|
-
grouped.set(key, { acc: emptyAcc(), models: /* @__PURE__ */ new
|
|
517
|
+
grouped.set(key, { acc: emptyAcc(), models: /* @__PURE__ */ new Map() });
|
|
474
518
|
}
|
|
475
|
-
|
|
476
|
-
addAcc(entry.acc, ev);
|
|
477
|
-
if (session.model) entry.models.add(session.model);
|
|
519
|
+
addAccToBucket(grouped.get(key), ev, session.model);
|
|
478
520
|
}
|
|
479
521
|
}
|
|
480
522
|
return grouped;
|
|
481
523
|
}
|
|
482
|
-
function
|
|
483
|
-
const sessions = parseAllSessions();
|
|
524
|
+
function buildDailyResponse(sessions, options) {
|
|
484
525
|
const grouped = groupSessions(sessions, { groupBy: "day", ...options });
|
|
485
526
|
const daily = [];
|
|
486
527
|
const totalsAcc = emptyAcc();
|
|
487
|
-
|
|
488
|
-
|
|
528
|
+
const totalModels = /* @__PURE__ */ new Map();
|
|
529
|
+
for (const [date, { acc, models }] of grouped) {
|
|
530
|
+
daily.push(accToEntry(date, acc, models));
|
|
489
531
|
mergeAcc(totalsAcc, acc);
|
|
532
|
+
for (const [model, modelAcc] of models) {
|
|
533
|
+
if (!totalModels.has(model)) totalModels.set(model, emptyAcc());
|
|
534
|
+
mergeAcc(totalModels.get(model), modelAcc);
|
|
535
|
+
}
|
|
490
536
|
}
|
|
491
537
|
daily.sort((a, b) => a.date.localeCompare(b.date));
|
|
492
|
-
const
|
|
493
|
-
for (const s of sessions) if (s.model) models.add(s.model);
|
|
494
|
-
const totalCost = calculateCost(totalsAcc, models);
|
|
538
|
+
const totalCost = buildModelBreakdowns(totalModels).reduce((sum, model) => sum + model.cost, 0);
|
|
495
539
|
return {
|
|
496
540
|
daily,
|
|
497
541
|
totals: {
|
|
498
|
-
inputTokens: totalsAcc.inputTokens,
|
|
542
|
+
inputTokens: displayInputTokens(totalsAcc.inputTokens, totalsAcc.cachedInputTokens),
|
|
499
543
|
outputTokens: totalsAcc.outputTokens,
|
|
500
544
|
cacheCreationTokens: 0,
|
|
501
545
|
cacheReadTokens: totalsAcc.cachedInputTokens,
|
|
@@ -504,41 +548,37 @@ function getDailyResponse(options) {
|
|
|
504
548
|
}
|
|
505
549
|
};
|
|
506
550
|
}
|
|
507
|
-
function
|
|
508
|
-
const sessions = parseAllSessions();
|
|
551
|
+
function buildProjectsResponse(sessions, options) {
|
|
509
552
|
const tz = options?.timezone || "Asia/Shanghai";
|
|
510
|
-
const
|
|
553
|
+
const projectGroups = /* @__PURE__ */ new Map();
|
|
511
554
|
for (const session of sessions) {
|
|
512
555
|
const projectName = extractProjectName(session.cwd);
|
|
513
|
-
|
|
556
|
+
if (options?.project && projectName !== options.project) continue;
|
|
557
|
+
if (!projectGroups.has(projectName)) projectGroups.set(projectName, /* @__PURE__ */ new Map());
|
|
558
|
+
const dailyMap = projectGroups.get(projectName);
|
|
514
559
|
for (const ev of session.tokenEvents) {
|
|
515
560
|
const evDate = new Date(ev.timestamp);
|
|
516
561
|
if (options?.since && evDate < options.since) continue;
|
|
517
562
|
if (options?.until && evDate > options.until) continue;
|
|
518
563
|
const dayKey = getDateKey(ev.timestamp, tz);
|
|
519
564
|
if (!dailyMap.has(dayKey)) {
|
|
520
|
-
dailyMap.set(dayKey, { acc: emptyAcc(), models: /* @__PURE__ */ new
|
|
565
|
+
dailyMap.set(dayKey, { acc: emptyAcc(), models: /* @__PURE__ */ new Map() });
|
|
521
566
|
}
|
|
522
|
-
|
|
523
|
-
if (session.model) dailyMap.get(dayKey).models.add(session.model);
|
|
524
|
-
}
|
|
525
|
-
if (!projects[projectName]) projects[projectName] = [];
|
|
526
|
-
for (const [date, { acc, models }] of dailyMap) {
|
|
527
|
-
projects[projectName].push(accToEntry(date, acc, models));
|
|
567
|
+
addAccToBucket(dailyMap.get(dayKey), ev, session.model);
|
|
528
568
|
}
|
|
529
569
|
}
|
|
530
|
-
|
|
531
|
-
|
|
570
|
+
const projects = {};
|
|
571
|
+
for (const [projectName, dailyMap] of projectGroups) {
|
|
572
|
+
projects[projectName] = [...dailyMap.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([date, { acc, models }]) => accToEntry(date, acc, models));
|
|
532
573
|
}
|
|
533
574
|
return { projects };
|
|
534
575
|
}
|
|
535
|
-
function
|
|
536
|
-
const sessions = parseAllSessions();
|
|
576
|
+
function buildBlocksResponse(sessions, options) {
|
|
537
577
|
const grouped = groupSessions(sessions, { groupBy: "hour", ...options });
|
|
538
578
|
const blocks = [];
|
|
539
579
|
let idx = 0;
|
|
540
580
|
for (const [hourKey, { acc, models }] of grouped) {
|
|
541
|
-
const cost =
|
|
581
|
+
const cost = buildModelBreakdowns(models).reduce((sum, model) => sum + model.cost, 0);
|
|
542
582
|
const [datePart, timePart] = hourKey.split(" ");
|
|
543
583
|
const hour = timePart.split(":")[0];
|
|
544
584
|
blocks.push({
|
|
@@ -550,20 +590,29 @@ function getBlocksResponse(options) {
|
|
|
550
590
|
isGap: false,
|
|
551
591
|
entries: acc.totalTokens > 0 ? 1 : 0,
|
|
552
592
|
tokenCounts: {
|
|
553
|
-
inputTokens: acc.inputTokens,
|
|
593
|
+
inputTokens: displayInputTokens(acc.inputTokens, acc.cachedInputTokens),
|
|
554
594
|
outputTokens: acc.outputTokens,
|
|
555
595
|
cacheCreationInputTokens: 0,
|
|
556
596
|
cacheReadInputTokens: acc.cachedInputTokens
|
|
557
597
|
},
|
|
558
598
|
totalTokens: acc.totalTokens,
|
|
559
599
|
costUSD: cost,
|
|
560
|
-
models: [...models]
|
|
600
|
+
models: [...models.keys()]
|
|
561
601
|
});
|
|
562
602
|
idx++;
|
|
563
603
|
}
|
|
564
604
|
blocks.sort((a, b) => a.startTime.localeCompare(b.startTime));
|
|
565
605
|
return { blocks };
|
|
566
606
|
}
|
|
607
|
+
function getDailyResponse(options) {
|
|
608
|
+
return buildDailyResponse(parseAllSessions(), options);
|
|
609
|
+
}
|
|
610
|
+
function getProjectsResponse(options) {
|
|
611
|
+
return buildProjectsResponse(parseAllSessions(), options);
|
|
612
|
+
}
|
|
613
|
+
function getBlocksResponse(options) {
|
|
614
|
+
return buildBlocksResponse(parseAllSessions(), options);
|
|
615
|
+
}
|
|
567
616
|
|
|
568
617
|
// src/server/openclawParser.ts
|
|
569
618
|
var import_node_fs3 = require("node:fs");
|
|
@@ -1154,18 +1203,18 @@ var import_node_path5 = require("node:path");
|
|
|
1154
1203
|
var import_node_os5 = require("node:os");
|
|
1155
1204
|
var MODEL_PRICING2 = {
|
|
1156
1205
|
// Claude 4.6
|
|
1157
|
-
"claude-opus-4-6": { inputPer1M: 15, cacheReadPer1M: 1.5, outputPer1M: 75 },
|
|
1158
|
-
"claude-sonnet-4-6": { inputPer1M: 3, cacheReadPer1M: 0.3, outputPer1M: 15 },
|
|
1206
|
+
"claude-opus-4-6": { inputPer1M: 15, cacheCreationPer1M: 18.75, cacheReadPer1M: 1.5, outputPer1M: 75 },
|
|
1207
|
+
"claude-sonnet-4-6": { inputPer1M: 3, cacheCreationPer1M: 3.75, cacheReadPer1M: 0.3, outputPer1M: 15 },
|
|
1159
1208
|
// Claude 4.5
|
|
1160
|
-
"claude-sonnet-4-5-20250514": { inputPer1M: 3, cacheReadPer1M: 0.3, outputPer1M: 15 },
|
|
1161
|
-
"claude-haiku-4-5-20251001": { inputPer1M: 0.8, cacheReadPer1M: 0.08, outputPer1M: 4 },
|
|
1209
|
+
"claude-sonnet-4-5-20250514": { inputPer1M: 3, cacheCreationPer1M: 3.75, cacheReadPer1M: 0.3, outputPer1M: 15 },
|
|
1210
|
+
"claude-haiku-4-5-20251001": { inputPer1M: 0.8, cacheCreationPer1M: 1, cacheReadPer1M: 0.08, outputPer1M: 4 },
|
|
1162
1211
|
// Older Claude models
|
|
1163
|
-
"claude-3-5-sonnet-20241022": { inputPer1M: 3, cacheReadPer1M: 0.3, outputPer1M: 15 },
|
|
1164
|
-
"claude-3-5-haiku-20241022": { inputPer1M: 0.8, cacheReadPer1M: 0.08, outputPer1M: 4 },
|
|
1165
|
-
"claude-3-opus-20240229": { inputPer1M: 15, cacheReadPer1M: 1.5, outputPer1M: 75 },
|
|
1166
|
-
"claude-3-haiku-20240307": { inputPer1M: 0.25, cacheReadPer1M: 0.03, outputPer1M: 1.25 }
|
|
1212
|
+
"claude-3-5-sonnet-20241022": { inputPer1M: 3, cacheCreationPer1M: 3.75, cacheReadPer1M: 0.3, outputPer1M: 15 },
|
|
1213
|
+
"claude-3-5-haiku-20241022": { inputPer1M: 0.8, cacheCreationPer1M: 1, cacheReadPer1M: 0.08, outputPer1M: 4 },
|
|
1214
|
+
"claude-3-opus-20240229": { inputPer1M: 15, cacheCreationPer1M: 18.75, cacheReadPer1M: 1.5, outputPer1M: 75 },
|
|
1215
|
+
"claude-3-haiku-20240307": { inputPer1M: 0.25, cacheCreationPer1M: 0.3, cacheReadPer1M: 0.03, outputPer1M: 1.25 }
|
|
1167
1216
|
};
|
|
1168
|
-
var DEFAULT_PRICING2 = { inputPer1M: 3, cacheReadPer1M: 0.3, outputPer1M: 15 };
|
|
1217
|
+
var DEFAULT_PRICING2 = { inputPer1M: 3, cacheCreationPer1M: 3.75, cacheReadPer1M: 0.3, outputPer1M: 15 };
|
|
1169
1218
|
function getPricing(model) {
|
|
1170
1219
|
if (MODEL_PRICING2[model]) return MODEL_PRICING2[model];
|
|
1171
1220
|
const lower = model.toLowerCase();
|
|
@@ -1174,16 +1223,55 @@ function getPricing(model) {
|
|
|
1174
1223
|
}
|
|
1175
1224
|
return DEFAULT_PRICING2;
|
|
1176
1225
|
}
|
|
1177
|
-
function calculateCost2(inputTokens, cacheReadTokens, outputTokens, model) {
|
|
1226
|
+
function calculateCost2(inputTokens, cacheReadTokens, outputTokens, model, cacheCreationTokens = 0) {
|
|
1178
1227
|
const p = getPricing(model);
|
|
1179
|
-
|
|
1180
|
-
|
|
1228
|
+
return inputTokens / 1e6 * p.inputPer1M + cacheCreationTokens / 1e6 * p.cacheCreationPer1M + cacheReadTokens / 1e6 * p.cacheReadPer1M + outputTokens / 1e6 * p.outputPer1M;
|
|
1229
|
+
}
|
|
1230
|
+
function totalClaudeTokens(inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens) {
|
|
1231
|
+
return inputTokens + outputTokens + cacheCreationTokens + cacheReadTokens;
|
|
1181
1232
|
}
|
|
1182
1233
|
var CLAUDE_PROJECTS_DIR = (0, import_node_path5.join)((0, import_node_os5.homedir)(), ".claude", "projects");
|
|
1183
1234
|
var fileCache = /* @__PURE__ */ new Map();
|
|
1235
|
+
var projectNameCache = /* @__PURE__ */ new Map();
|
|
1184
1236
|
function extractProjectName2(dirName) {
|
|
1185
|
-
|
|
1186
|
-
|
|
1237
|
+
if (!dirName.startsWith("-")) return dirName;
|
|
1238
|
+
const cached = projectNameCache.get(dirName);
|
|
1239
|
+
if (cached) return cached;
|
|
1240
|
+
const segments = dirName.replace(/^-/, "").split("-").filter(Boolean);
|
|
1241
|
+
if (segments.length === 0) {
|
|
1242
|
+
projectNameCache.set(dirName, dirName);
|
|
1243
|
+
return dirName;
|
|
1244
|
+
}
|
|
1245
|
+
if (segments.length === 1) {
|
|
1246
|
+
projectNameCache.set(dirName, segments[0]);
|
|
1247
|
+
return segments[0];
|
|
1248
|
+
}
|
|
1249
|
+
let bestName = segments[segments.length - 1];
|
|
1250
|
+
for (let splitAt = segments.length - 1; splitAt >= 1; splitAt--) {
|
|
1251
|
+
const parentSegments = segments.slice(0, splitAt);
|
|
1252
|
+
const candidateName = segments.slice(splitAt).join("-");
|
|
1253
|
+
let parentPath = "/";
|
|
1254
|
+
let valid = true;
|
|
1255
|
+
for (const seg of parentSegments) {
|
|
1256
|
+
const regular = (0, import_node_path5.join)(parentPath, seg);
|
|
1257
|
+
const hidden = (0, import_node_path5.join)(parentPath, "." + seg);
|
|
1258
|
+
if ((0, import_node_fs5.existsSync)(regular)) {
|
|
1259
|
+
parentPath = regular;
|
|
1260
|
+
} else if ((0, import_node_fs5.existsSync)(hidden)) {
|
|
1261
|
+
parentPath = hidden;
|
|
1262
|
+
} else {
|
|
1263
|
+
valid = false;
|
|
1264
|
+
break;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
if (!valid) continue;
|
|
1268
|
+
if ((0, import_node_fs5.existsSync)((0, import_node_path5.join)(parentPath, candidateName)) || (0, import_node_fs5.existsSync)((0, import_node_path5.join)(parentPath, "." + candidateName))) {
|
|
1269
|
+
bestName = candidateName;
|
|
1270
|
+
break;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
projectNameCache.set(dirName, bestName);
|
|
1274
|
+
return bestName;
|
|
1187
1275
|
}
|
|
1188
1276
|
function matchesProject(dirName, filter) {
|
|
1189
1277
|
return extractProjectName2(dirName) === extractProjectName2(filter);
|
|
@@ -1245,7 +1333,7 @@ function parseAllSessions2(project) {
|
|
|
1245
1333
|
const outputTokens = usage.output_tokens || 0;
|
|
1246
1334
|
const cacheCreationTokens = usage.cache_creation_input_tokens || 0;
|
|
1247
1335
|
const cacheReadTokens = usage.cache_read_input_tokens || 0;
|
|
1248
|
-
const totalTokens = inputTokens
|
|
1336
|
+
const totalTokens = totalClaudeTokens(inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens);
|
|
1249
1337
|
if (totalTokens === 0) continue;
|
|
1250
1338
|
entries.push({
|
|
1251
1339
|
timestamp: obj.timestamp,
|
|
@@ -1329,8 +1417,8 @@ function getDailyResponse4(project, tz = DEFAULT_TZ) {
|
|
|
1329
1417
|
agg.outputTokens += e.outputTokens;
|
|
1330
1418
|
agg.cacheCreationTokens += e.cacheCreationTokens;
|
|
1331
1419
|
agg.cacheReadTokens += e.cacheReadTokens;
|
|
1332
|
-
agg.totalTokens += e.inputTokens
|
|
1333
|
-
const cost = calculateCost2(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model);
|
|
1420
|
+
agg.totalTokens += totalClaudeTokens(e.inputTokens, e.outputTokens, e.cacheCreationTokens, e.cacheReadTokens);
|
|
1421
|
+
const cost = calculateCost2(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model, e.cacheCreationTokens);
|
|
1334
1422
|
agg.totalCost += cost;
|
|
1335
1423
|
if (!agg.models.has(e.model)) {
|
|
1336
1424
|
agg.models.set(e.model, { input: 0, output: 0, cacheCreation: 0, cacheRead: 0, cost: 0 });
|
|
@@ -1358,7 +1446,7 @@ function getProjectsResponse4(tz = DEFAULT_TZ) {
|
|
|
1358
1446
|
const projectMap = /* @__PURE__ */ new Map();
|
|
1359
1447
|
for (const e of entries) {
|
|
1360
1448
|
const date = getDateKey4(e.timestamp, tz);
|
|
1361
|
-
const projectName = e.projectDir;
|
|
1449
|
+
const projectName = extractProjectName2(e.projectDir);
|
|
1362
1450
|
if (!projectMap.has(projectName)) {
|
|
1363
1451
|
projectMap.set(projectName, /* @__PURE__ */ new Map());
|
|
1364
1452
|
}
|
|
@@ -1380,8 +1468,8 @@ function getProjectsResponse4(tz = DEFAULT_TZ) {
|
|
|
1380
1468
|
agg.outputTokens += e.outputTokens;
|
|
1381
1469
|
agg.cacheCreationTokens += e.cacheCreationTokens;
|
|
1382
1470
|
agg.cacheReadTokens += e.cacheReadTokens;
|
|
1383
|
-
agg.totalTokens += e.inputTokens
|
|
1384
|
-
const cost = calculateCost2(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model);
|
|
1471
|
+
agg.totalTokens += totalClaudeTokens(e.inputTokens, e.outputTokens, e.cacheCreationTokens, e.cacheReadTokens);
|
|
1472
|
+
const cost = calculateCost2(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model, e.cacheCreationTokens);
|
|
1385
1473
|
agg.totalCost += cost;
|
|
1386
1474
|
if (!agg.models.has(e.model)) {
|
|
1387
1475
|
agg.models.set(e.model, { input: 0, output: 0, cacheCreation: 0, cacheRead: 0, cost: 0 });
|
|
@@ -1419,13 +1507,13 @@ function getBlocksResponse4(project, tz = DEFAULT_TZ) {
|
|
|
1419
1507
|
bucket.outputTokens += e.outputTokens;
|
|
1420
1508
|
bucket.cacheCreationTokens += e.cacheCreationTokens;
|
|
1421
1509
|
bucket.cacheReadTokens += e.cacheReadTokens;
|
|
1422
|
-
bucket.costUSD += calculateCost2(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model);
|
|
1510
|
+
bucket.costUSD += calculateCost2(e.inputTokens, e.cacheReadTokens, e.outputTokens, e.model, e.cacheCreationTokens);
|
|
1423
1511
|
bucket.models.add(e.model);
|
|
1424
1512
|
}
|
|
1425
1513
|
const blocks = [];
|
|
1426
1514
|
let idx = 0;
|
|
1427
1515
|
for (const [hourKey, bucket] of hourMap) {
|
|
1428
|
-
const totalTokens = bucket.inputTokens
|
|
1516
|
+
const totalTokens = totalClaudeTokens(bucket.inputTokens, bucket.outputTokens, bucket.cacheCreationTokens, bucket.cacheReadTokens);
|
|
1429
1517
|
blocks.push({
|
|
1430
1518
|
id: `claude-${idx}`,
|
|
1431
1519
|
startTime: `${hourKey}:00:00`,
|
|
@@ -1693,9 +1781,46 @@ function countLines(text) {
|
|
|
1693
1781
|
return text.split("\n").length;
|
|
1694
1782
|
}
|
|
1695
1783
|
var CLAUDE_PROJECTS_DIR2 = (0, import_node_path6.join)((0, import_node_os6.homedir)(), ".claude", "projects");
|
|
1784
|
+
var projectNameCache2 = /* @__PURE__ */ new Map();
|
|
1696
1785
|
function extractProjectName3(dirName) {
|
|
1697
|
-
|
|
1698
|
-
|
|
1786
|
+
if (!dirName.startsWith("-")) return dirName;
|
|
1787
|
+
const cached = projectNameCache2.get(dirName);
|
|
1788
|
+
if (cached) return cached;
|
|
1789
|
+
const segments = dirName.replace(/^-/, "").split("-").filter(Boolean);
|
|
1790
|
+
if (segments.length === 0) {
|
|
1791
|
+
projectNameCache2.set(dirName, dirName);
|
|
1792
|
+
return dirName;
|
|
1793
|
+
}
|
|
1794
|
+
if (segments.length === 1) {
|
|
1795
|
+
projectNameCache2.set(dirName, segments[0]);
|
|
1796
|
+
return segments[0];
|
|
1797
|
+
}
|
|
1798
|
+
let bestName = segments[segments.length - 1];
|
|
1799
|
+
for (let splitAt = segments.length - 1; splitAt >= 1; splitAt--) {
|
|
1800
|
+
const parentSegments = segments.slice(0, splitAt);
|
|
1801
|
+
const candidateName = segments.slice(splitAt).join("-");
|
|
1802
|
+
let parentPath = "/";
|
|
1803
|
+
let valid = true;
|
|
1804
|
+
for (const seg of parentSegments) {
|
|
1805
|
+
const regular = (0, import_node_path6.join)(parentPath, seg);
|
|
1806
|
+
const hidden = (0, import_node_path6.join)(parentPath, "." + seg);
|
|
1807
|
+
if ((0, import_node_fs6.existsSync)(regular)) {
|
|
1808
|
+
parentPath = regular;
|
|
1809
|
+
} else if ((0, import_node_fs6.existsSync)(hidden)) {
|
|
1810
|
+
parentPath = hidden;
|
|
1811
|
+
} else {
|
|
1812
|
+
valid = false;
|
|
1813
|
+
break;
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
if (!valid) continue;
|
|
1817
|
+
if ((0, import_node_fs6.existsSync)((0, import_node_path6.join)(parentPath, candidateName)) || (0, import_node_fs6.existsSync)((0, import_node_path6.join)(parentPath, "." + candidateName))) {
|
|
1818
|
+
bestName = candidateName;
|
|
1819
|
+
break;
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
projectNameCache2.set(dirName, bestName);
|
|
1823
|
+
return bestName;
|
|
1699
1824
|
}
|
|
1700
1825
|
function matchesProject2(dirName, filter) {
|
|
1701
1826
|
return extractProjectName3(dirName) === extractProjectName3(filter);
|
|
@@ -1880,6 +2005,21 @@ function computeAnalytics(toolCalls, timezone = "Asia/Shanghai") {
|
|
|
1880
2005
|
toolCallTrend.push(entry);
|
|
1881
2006
|
}
|
|
1882
2007
|
toolCallTrend.sort((a, b) => a.date.localeCompare(b.date));
|
|
2008
|
+
if (toolCallTrend.length > 0) {
|
|
2009
|
+
const allTools = /* @__PURE__ */ new Set();
|
|
2010
|
+
for (const entry of toolCallTrend) {
|
|
2011
|
+
for (const key of Object.keys(entry)) {
|
|
2012
|
+
if (key !== "date") allTools.add(key);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
for (const entry of toolCallTrend) {
|
|
2016
|
+
for (const tool of allTools) {
|
|
2017
|
+
if (entry[tool] === void 0) {
|
|
2018
|
+
entry[tool] = 0;
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
1883
2023
|
return { codeChangeTrend, toolUsageDistribution, productivityKPIs, toolCallTrend };
|
|
1884
2024
|
}
|
|
1885
2025
|
|
|
@@ -2048,11 +2188,11 @@ function resolvePort(value) {
|
|
|
2048
2188
|
return Number.isInteger(value) && value && value > 0 ? value : 3456;
|
|
2049
2189
|
}
|
|
2050
2190
|
function listen(app, port) {
|
|
2051
|
-
return new Promise((
|
|
2191
|
+
return new Promise((resolve2, reject) => {
|
|
2052
2192
|
const server = app.listen(port);
|
|
2053
2193
|
const handleListening = () => {
|
|
2054
2194
|
cleanup();
|
|
2055
|
-
|
|
2195
|
+
resolve2(server);
|
|
2056
2196
|
};
|
|
2057
2197
|
const handleError = (error) => {
|
|
2058
2198
|
cleanup();
|
|
@@ -2081,16 +2221,29 @@ async function listenWithPortFallback(app, preferredPort) {
|
|
|
2081
2221
|
}
|
|
2082
2222
|
throw new Error(`Could not find an available port starting from ${preferredPort}`);
|
|
2083
2223
|
}
|
|
2224
|
+
function resolveStaticAssetBaseDir(moduleUrl = __esbuild_import_meta_url, baseDir) {
|
|
2225
|
+
if (baseDir) return { baseDir: (0, import_node_path8.resolve)(baseDir), isProduction: true };
|
|
2226
|
+
const moduleDir = (0, import_node_path8.dirname)((0, import_node_url.fileURLToPath)(moduleUrl));
|
|
2227
|
+
const isProduction = moduleUrl.includes("/dist/");
|
|
2228
|
+
if (!isProduction) return { baseDir: (0, import_node_path8.resolve)(moduleDir), isProduction: false };
|
|
2229
|
+
if ((0, import_node_path8.basename)(moduleDir) === "server") {
|
|
2230
|
+
return { baseDir: (0, import_node_path8.resolve)((0, import_node_path8.dirname)(moduleDir)), isProduction: true };
|
|
2231
|
+
}
|
|
2232
|
+
return { baseDir: (0, import_node_path8.resolve)(moduleDir), isProduction: true };
|
|
2233
|
+
}
|
|
2084
2234
|
function createApp(_port, baseDir) {
|
|
2085
2235
|
const app = (0, import_express.default)();
|
|
2086
2236
|
const router = import_express.default.Router();
|
|
2087
2237
|
registerApiRoutes(router);
|
|
2088
2238
|
app.use("/api", router);
|
|
2089
|
-
const _baseDir
|
|
2090
|
-
const isProduction = baseDir ? true : __esbuild_import_meta_url.includes("dist/");
|
|
2239
|
+
const { baseDir: _baseDir, isProduction } = resolveStaticAssetBaseDir(__esbuild_import_meta_url, baseDir);
|
|
2091
2240
|
const popoverPath = isProduction ? (0, import_node_path8.join)(_baseDir, "client", "popover.html") : (0, import_node_path8.join)(_baseDir, "..", "..", "public", "popover.html");
|
|
2092
|
-
app.get("/popover.html", (_req, res) => {
|
|
2093
|
-
|
|
2241
|
+
app.get("/popover.html", (_req, res, next) => {
|
|
2242
|
+
if (!(0, import_node_fs8.existsSync)(popoverPath)) {
|
|
2243
|
+
next();
|
|
2244
|
+
return;
|
|
2245
|
+
}
|
|
2246
|
+
res.type("html").send((0, import_node_fs8.readFileSync)(popoverPath, "utf8"));
|
|
2094
2247
|
});
|
|
2095
2248
|
if (isProduction) {
|
|
2096
2249
|
const clientPath = (0, import_node_path8.join)(_baseDir, "client");
|
|
@@ -2170,6 +2323,7 @@ async function main() {
|
|
|
2170
2323
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2171
2324
|
0 && (module.exports = {
|
|
2172
2325
|
createApp,
|
|
2173
|
-
main
|
|
2326
|
+
main,
|
|
2327
|
+
resolveStaticAssetBaseDir
|
|
2174
2328
|
});
|
|
2175
2329
|
//# sourceMappingURL=electron-server.cjs.map
|