@yishiguji/tokenarena 0.15.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1568 -936
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/parsers/claude-code.ts
|
|
4
|
+
import { createHash as createHash2 } from "crypto";
|
|
4
5
|
import { existsSync as existsSync3, realpathSync } from "fs";
|
|
5
6
|
import { homedir } from "os";
|
|
6
7
|
import { join as join2, sep as sep2 } from "path";
|
|
@@ -16,6 +17,8 @@ var TOKEN_COUNT_KEYS = [
|
|
|
16
17
|
"cachedTokens"
|
|
17
18
|
];
|
|
18
19
|
function hasInvalidTokenCounts(entry) {
|
|
20
|
+
const cacheCreation = entry.cacheCreationTokens ?? 0;
|
|
21
|
+
if (!Number.isSafeInteger(cacheCreation) || cacheCreation < 0) return true;
|
|
19
22
|
return TOKEN_COUNT_KEYS.some((key) => {
|
|
20
23
|
const value = entry[key];
|
|
21
24
|
return !Number.isSafeInteger(value) || value < 0;
|
|
@@ -48,6 +51,7 @@ function aggregateToBuckets(entries) {
|
|
|
48
51
|
outputTokens: 0,
|
|
49
52
|
reasoningTokens: 0,
|
|
50
53
|
cachedTokens: 0,
|
|
54
|
+
cacheCreationTokens: 0,
|
|
51
55
|
totalTokens: 0
|
|
52
56
|
});
|
|
53
57
|
}
|
|
@@ -57,7 +61,8 @@ function aggregateToBuckets(entries) {
|
|
|
57
61
|
b.outputTokens += e.outputTokens || 0;
|
|
58
62
|
b.reasoningTokens += e.reasoningTokens || 0;
|
|
59
63
|
b.cachedTokens += e.cachedTokens || 0;
|
|
60
|
-
b.
|
|
64
|
+
b.cacheCreationTokens = (b.cacheCreationTokens ?? 0) + (e.cacheCreationTokens ?? 0);
|
|
65
|
+
b.totalTokens += (e.inputTokens || 0) + (e.outputTokens || 0) + (e.reasoningTokens || 0) + (e.cachedTokens || 0) + (e.cacheCreationTokens ?? 0);
|
|
61
66
|
}
|
|
62
67
|
return Array.from(map.values());
|
|
63
68
|
}
|
|
@@ -66,7 +71,7 @@ function aggregateToBuckets(entries) {
|
|
|
66
71
|
import { createHash } from "crypto";
|
|
67
72
|
import { hostname as hostname2 } from "os";
|
|
68
73
|
function toEntryTotalTokens(entry) {
|
|
69
|
-
return entry.inputTokens + entry.outputTokens + entry.reasoningTokens + entry.cachedTokens;
|
|
74
|
+
return entry.inputTokens + entry.outputTokens + entry.reasoningTokens + entry.cachedTokens + (entry.cacheCreationTokens ?? 0);
|
|
70
75
|
}
|
|
71
76
|
function buildSessionUsage(entries) {
|
|
72
77
|
const usageBySession = /* @__PURE__ */ new Map();
|
|
@@ -88,6 +93,7 @@ function buildSessionUsage(entries) {
|
|
|
88
93
|
existing.outputTokens += entry.outputTokens;
|
|
89
94
|
existing.reasoningTokens += entry.reasoningTokens;
|
|
90
95
|
existing.cachedTokens += entry.cachedTokens;
|
|
96
|
+
existing.cacheCreationTokens = (existing.cacheCreationTokens ?? 0) + (entry.cacheCreationTokens ?? 0);
|
|
91
97
|
existing.totalTokens += toEntryTotalTokens(entry);
|
|
92
98
|
continue;
|
|
93
99
|
}
|
|
@@ -97,6 +103,7 @@ function buildSessionUsage(entries) {
|
|
|
97
103
|
outputTokens: entry.outputTokens,
|
|
98
104
|
reasoningTokens: entry.reasoningTokens,
|
|
99
105
|
cachedTokens: entry.cachedTokens,
|
|
106
|
+
cacheCreationTokens: entry.cacheCreationTokens ?? 0,
|
|
100
107
|
totalTokens: toEntryTotalTokens(entry)
|
|
101
108
|
});
|
|
102
109
|
}
|
|
@@ -177,6 +184,10 @@ function extractSessions(events, entries = []) {
|
|
|
177
184
|
(sum, usage) => sum + usage.cachedTokens,
|
|
178
185
|
0
|
|
179
186
|
);
|
|
187
|
+
const cacheCreationTokens = modelUsages.reduce(
|
|
188
|
+
(sum, usage) => sum + (usage.cacheCreationTokens ?? 0),
|
|
189
|
+
0
|
|
190
|
+
);
|
|
180
191
|
const totalTokens = modelUsages.reduce(
|
|
181
192
|
(sum, usage) => sum + usage.totalTokens,
|
|
182
193
|
0
|
|
@@ -199,6 +210,7 @@ function extractSessions(events, entries = []) {
|
|
|
199
210
|
outputTokens,
|
|
200
211
|
reasoningTokens,
|
|
201
212
|
cachedTokens,
|
|
213
|
+
cacheCreationTokens,
|
|
202
214
|
totalTokens,
|
|
203
215
|
primaryModel,
|
|
204
216
|
modelUsages
|
|
@@ -330,109 +342,164 @@ function extractProject(filePath, projectsDir) {
|
|
|
330
342
|
const parts = firstSegment.split("-").filter(Boolean);
|
|
331
343
|
return parts.length > 0 ? parts[parts.length - 1] : "unknown";
|
|
332
344
|
}
|
|
345
|
+
function asRecord(value) {
|
|
346
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
347
|
+
}
|
|
348
|
+
function getIdentifier(value) {
|
|
349
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
350
|
+
}
|
|
351
|
+
function tokenCount(value) {
|
|
352
|
+
if (value == null) return 0;
|
|
353
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : Number.NaN;
|
|
354
|
+
}
|
|
355
|
+
function selectSnapshot(current, candidate) {
|
|
356
|
+
if (!current) return candidate;
|
|
357
|
+
const replace = candidate.completed !== current.completed ? candidate.completed : candidate.entry.outputTokens !== current.entry.outputTokens ? candidate.entry.outputTokens > current.entry.outputTokens : candidate.entry.timestamp > current.entry.timestamp;
|
|
358
|
+
const selected = replace ? candidate : current;
|
|
359
|
+
const owner = current.order < candidate.order ? current : candidate;
|
|
360
|
+
return {
|
|
361
|
+
...selected,
|
|
362
|
+
order: owner.order,
|
|
363
|
+
entry: {
|
|
364
|
+
...selected.entry,
|
|
365
|
+
sessionId: owner.entry.sessionId,
|
|
366
|
+
project: owner.entry.project
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
}
|
|
333
370
|
var ClaudeCodeParser = class {
|
|
334
371
|
tool = TOOL;
|
|
335
372
|
async parse() {
|
|
336
|
-
const
|
|
337
|
-
const sessionEvents =
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
for (const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
project,
|
|
369
|
-
timestamp: parsedTimestamp,
|
|
370
|
-
role: obj.type === "user" ? "user" : "assistant"
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
if (obj.type !== "assistant") continue;
|
|
374
|
-
const message = obj.message;
|
|
375
|
-
if (!message?.usage) continue;
|
|
376
|
-
const usage = message.usage;
|
|
377
|
-
if (usage.input_tokens == null && usage.output_tokens == null) {
|
|
378
|
-
continue;
|
|
379
|
-
}
|
|
380
|
-
const uuid = obj.uuid;
|
|
381
|
-
if (uuid) {
|
|
382
|
-
if (seenUuids.has(uuid)) continue;
|
|
383
|
-
seenUuids.add(uuid);
|
|
384
|
-
}
|
|
385
|
-
entries.push({
|
|
373
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
374
|
+
const sessionEvents = /* @__PURE__ */ new Map();
|
|
375
|
+
const projectSessionIds = /* @__PURE__ */ new Set();
|
|
376
|
+
let snapshotOrder = 0;
|
|
377
|
+
const scanFile = (filePath, fileIdentity, project, includeUsage) => {
|
|
378
|
+
const content = readFileSafe(filePath);
|
|
379
|
+
if (!content) return;
|
|
380
|
+
const sessionId = extractSessionId(filePath);
|
|
381
|
+
if (includeUsage) projectSessionIds.add(sessionId);
|
|
382
|
+
for (const [lineIndex, line] of content.split("\n").entries()) {
|
|
383
|
+
if (!line.trim()) continue;
|
|
384
|
+
try {
|
|
385
|
+
const record = asRecord(JSON.parse(line));
|
|
386
|
+
if (!record || typeof record.timestamp !== "string" && typeof record.timestamp !== "number") {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const timestamp = new Date(record.timestamp);
|
|
390
|
+
if (Number.isNaN(timestamp.getTime())) continue;
|
|
391
|
+
const message = asRecord(record.message);
|
|
392
|
+
const messageId = getIdentifier(message?.id);
|
|
393
|
+
const requestId = getIdentifier(record.requestId) ?? getIdentifier(record.request_id) ?? "";
|
|
394
|
+
const uuid = getIdentifier(record.uuid);
|
|
395
|
+
const anonymousId = uuid || messageId ? "" : createHash2("sha256").update(JSON.stringify([fileIdentity, lineIndex, line])).digest("hex");
|
|
396
|
+
const eventKey = uuid ? JSON.stringify(["uuid", uuid]) : messageId ? JSON.stringify([
|
|
397
|
+
"message",
|
|
398
|
+
messageId,
|
|
399
|
+
requestId,
|
|
400
|
+
record.type,
|
|
401
|
+
record.timestamp
|
|
402
|
+
]) : JSON.stringify(["anonymous", anonymousId]);
|
|
403
|
+
if (!sessionEvents.has(eventKey) && (record.type === "user" || record.type === "assistant" || record.type === "tool_use" || record.type === "tool_result")) {
|
|
404
|
+
sessionEvents.set(eventKey, {
|
|
386
405
|
sessionId,
|
|
387
406
|
source: "claude-code",
|
|
388
|
-
model: message.model || "unknown",
|
|
389
407
|
project,
|
|
390
|
-
timestamp
|
|
391
|
-
|
|
392
|
-
outputTokens: usage.output_tokens || 0,
|
|
393
|
-
reasoningTokens: 0,
|
|
394
|
-
cachedTokens: usage.cache_read_input_tokens || 0
|
|
408
|
+
timestamp,
|
|
409
|
+
role: record.type === "user" ? "user" : "assistant"
|
|
395
410
|
});
|
|
396
|
-
} catch {
|
|
397
411
|
}
|
|
412
|
+
if (!includeUsage || record.type !== "assistant" || !message)
|
|
413
|
+
continue;
|
|
414
|
+
const usage = asRecord(message.usage);
|
|
415
|
+
if (!usage) continue;
|
|
416
|
+
const cacheCreation = asRecord(usage.cache_creation);
|
|
417
|
+
const cacheCreationTokens = tokenCount(
|
|
418
|
+
usage.cache_creation_input_tokens ?? tokenCount(cacheCreation?.ephemeral_5m_input_tokens) + tokenCount(cacheCreation?.ephemeral_1h_input_tokens)
|
|
419
|
+
);
|
|
420
|
+
const event = sessionEvents.get(eventKey);
|
|
421
|
+
const entry = {
|
|
422
|
+
sessionId: event?.sessionId ?? sessionId,
|
|
423
|
+
source: "claude-code",
|
|
424
|
+
model: getIdentifier(message.model) ?? "unknown",
|
|
425
|
+
project: event?.project ?? project,
|
|
426
|
+
timestamp,
|
|
427
|
+
inputTokens: tokenCount(usage.input_tokens),
|
|
428
|
+
outputTokens: tokenCount(usage.output_tokens),
|
|
429
|
+
reasoningTokens: 0,
|
|
430
|
+
cachedTokens: tokenCount(usage.cache_read_input_tokens),
|
|
431
|
+
cacheCreationTokens
|
|
432
|
+
};
|
|
433
|
+
if (hasInvalidTokenCounts(entry) || entry.inputTokens + entry.outputTokens + entry.cachedTokens + cacheCreationTokens === 0) {
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
const messageKey = messageId ? JSON.stringify(["message", messageId]) : uuid ? JSON.stringify(["uuid", uuid]) : JSON.stringify(["anonymous", anonymousId]);
|
|
437
|
+
let requests = snapshots.get(messageKey);
|
|
438
|
+
if (!requests) {
|
|
439
|
+
requests = /* @__PURE__ */ new Map();
|
|
440
|
+
snapshots.set(messageKey, requests);
|
|
441
|
+
}
|
|
442
|
+
requests.set(
|
|
443
|
+
requestId,
|
|
444
|
+
selectSnapshot(requests.get(requestId), {
|
|
445
|
+
entry,
|
|
446
|
+
completed: getIdentifier(message.stop_reason) !== null,
|
|
447
|
+
order: snapshotOrder++
|
|
448
|
+
})
|
|
449
|
+
);
|
|
450
|
+
} catch {
|
|
398
451
|
}
|
|
399
452
|
}
|
|
453
|
+
};
|
|
454
|
+
const roots = getClaudeRoots();
|
|
455
|
+
for (const root of roots) {
|
|
456
|
+
const projectsDir = join2(root, "projects");
|
|
457
|
+
for (const filePath of findJsonlFiles(projectsDir)) {
|
|
458
|
+
scanFile(
|
|
459
|
+
filePath,
|
|
460
|
+
join2(
|
|
461
|
+
"projects",
|
|
462
|
+
projectRelativePath(filePath, projectsDir) ?? filePath
|
|
463
|
+
),
|
|
464
|
+
extractProject(filePath, projectsDir),
|
|
465
|
+
true
|
|
466
|
+
);
|
|
467
|
+
}
|
|
400
468
|
}
|
|
401
469
|
for (const root of roots) {
|
|
402
|
-
for (const
|
|
403
|
-
const
|
|
404
|
-
for (const filePath of
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
if (!timestamp) continue;
|
|
416
|
-
const parsedTimestamp = new Date(timestamp);
|
|
417
|
-
if (Number.isNaN(parsedTimestamp.getTime())) continue;
|
|
418
|
-
if (obj.type === "user" || obj.type === "assistant" || obj.type === "tool_use" || obj.type === "tool_result") {
|
|
419
|
-
sessionEvents.push({
|
|
420
|
-
sessionId,
|
|
421
|
-
source: "claude-code",
|
|
422
|
-
project: "unknown",
|
|
423
|
-
timestamp: parsedTimestamp,
|
|
424
|
-
role: obj.type === "user" ? "user" : "assistant"
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
} catch {
|
|
428
|
-
}
|
|
429
|
-
}
|
|
470
|
+
for (const directory of ["transcripts", "sessions"]) {
|
|
471
|
+
const transcriptDir = join2(root, directory);
|
|
472
|
+
for (const filePath of findJsonlFiles(transcriptDir)) {
|
|
473
|
+
if (projectSessionIds.has(extractSessionId(filePath))) continue;
|
|
474
|
+
scanFile(
|
|
475
|
+
filePath,
|
|
476
|
+
join2(
|
|
477
|
+
directory,
|
|
478
|
+
projectRelativePath(filePath, transcriptDir) ?? filePath
|
|
479
|
+
),
|
|
480
|
+
"unknown",
|
|
481
|
+
false
|
|
482
|
+
);
|
|
430
483
|
}
|
|
431
484
|
}
|
|
432
485
|
}
|
|
486
|
+
const entries = [];
|
|
487
|
+
for (const requests of snapshots.values()) {
|
|
488
|
+
const unidentified = requests.get("");
|
|
489
|
+
if (unidentified && requests.size === 2) {
|
|
490
|
+
const identified = Array.from(requests.entries()).find(([id]) => id);
|
|
491
|
+
if (identified) {
|
|
492
|
+
entries.push(selectSnapshot(unidentified, identified[1]).entry);
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
entries.push(
|
|
497
|
+
...Array.from(requests.values(), (snapshot) => snapshot.entry)
|
|
498
|
+
);
|
|
499
|
+
}
|
|
433
500
|
return {
|
|
434
501
|
buckets: aggregateToBuckets(entries),
|
|
435
|
-
sessions: extractSessions(sessionEvents, entries)
|
|
502
|
+
sessions: extractSessions(Array.from(sessionEvents.values()), entries)
|
|
436
503
|
};
|
|
437
504
|
}
|
|
438
505
|
isInstalled() {
|
|
@@ -445,216 +512,376 @@ var ClaudeCodeParser = class {
|
|
|
445
512
|
registerParser(new ClaudeCodeParser());
|
|
446
513
|
|
|
447
514
|
// src/parsers/codex.ts
|
|
515
|
+
import { existsSync as existsSync4 } from "fs";
|
|
448
516
|
import { homedir as homedir2 } from "os";
|
|
449
|
-
import { join as join3 } from "path";
|
|
517
|
+
import { basename as basename2, join as join3 } from "path";
|
|
518
|
+
|
|
519
|
+
// src/utils/logger.ts
|
|
520
|
+
var LOG_LEVELS = {
|
|
521
|
+
debug: 0,
|
|
522
|
+
info: 1,
|
|
523
|
+
warn: 2,
|
|
524
|
+
error: 3
|
|
525
|
+
};
|
|
526
|
+
var Logger = class {
|
|
527
|
+
level;
|
|
528
|
+
constructor(level = "info") {
|
|
529
|
+
this.level = level;
|
|
530
|
+
}
|
|
531
|
+
setLevel(level) {
|
|
532
|
+
this.level = level;
|
|
533
|
+
}
|
|
534
|
+
debug(msg) {
|
|
535
|
+
if (LOG_LEVELS[this.level] <= LOG_LEVELS.debug) {
|
|
536
|
+
process.stderr.write(`[debug] ${msg}
|
|
537
|
+
`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
info(msg) {
|
|
541
|
+
if (LOG_LEVELS[this.level] <= LOG_LEVELS.info) {
|
|
542
|
+
process.stdout.write(`${msg}
|
|
543
|
+
`);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
warn(msg) {
|
|
547
|
+
if (LOG_LEVELS[this.level] <= LOG_LEVELS.warn) {
|
|
548
|
+
process.stderr.write(`warn: ${msg}
|
|
549
|
+
`);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
error(msg) {
|
|
553
|
+
if (LOG_LEVELS[this.level] <= LOG_LEVELS.error) {
|
|
554
|
+
process.stderr.write(`error: ${msg}
|
|
555
|
+
`);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
log(msg) {
|
|
559
|
+
this.info(msg);
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
var logger = new Logger();
|
|
563
|
+
|
|
564
|
+
// src/parsers/codex.ts
|
|
450
565
|
var TOOL_ID = "codex";
|
|
451
|
-
var TOOL_NAME = "Codex CLI";
|
|
452
566
|
var DEFAULT_DATA_DIR = join3(homedir2(), ".codex", "sessions");
|
|
453
|
-
|
|
567
|
+
var DEFAULT_ARCHIVE_DIR = join3(homedir2(), ".codex", "archived_sessions");
|
|
568
|
+
function safeCount(value) {
|
|
569
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
|
|
570
|
+
}
|
|
571
|
+
function readUsage(value) {
|
|
572
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
573
|
+
const fields = value;
|
|
574
|
+
const names = [
|
|
575
|
+
"input_tokens",
|
|
576
|
+
"output_tokens",
|
|
577
|
+
"cached_input_tokens",
|
|
578
|
+
"cache_read_input_tokens",
|
|
579
|
+
"reasoning_output_tokens",
|
|
580
|
+
"total_tokens"
|
|
581
|
+
];
|
|
582
|
+
if (!names.some((name) => Object.hasOwn(fields, name))) return null;
|
|
454
583
|
return {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
584
|
+
input: safeCount(fields.input_tokens),
|
|
585
|
+
output: safeCount(fields.output_tokens),
|
|
586
|
+
cached: safeCount(
|
|
587
|
+
fields.cached_input_tokens ?? fields.cache_read_input_tokens
|
|
588
|
+
),
|
|
589
|
+
reasoning: safeCount(fields.reasoning_output_tokens),
|
|
590
|
+
reportedTotal: typeof fields.total_tokens === "number" ? safeCount(fields.total_tokens) : null
|
|
458
591
|
};
|
|
459
592
|
}
|
|
460
|
-
function
|
|
461
|
-
|
|
462
|
-
|
|
593
|
+
function readTimestamp(value) {
|
|
594
|
+
if (typeof value !== "string") return null;
|
|
595
|
+
const date = new Date(value);
|
|
596
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
463
597
|
}
|
|
464
|
-
function
|
|
465
|
-
|
|
598
|
+
function timestampOrder(value, timestamp) {
|
|
599
|
+
if (!timestamp) return null;
|
|
600
|
+
const fraction = typeof value === "string" ? value.match(/\.(\d+)(?:z|[+-]\d{2}:?\d{2})?$/i)?.[1] ?? "" : "";
|
|
601
|
+
const remainder = fraction.slice(3, 9).padEnd(6, "0");
|
|
602
|
+
return BigInt(timestamp.getTime()) * 1000000n + BigInt(remainder);
|
|
466
603
|
}
|
|
467
|
-
function
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
604
|
+
function readId(value) {
|
|
605
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
606
|
+
}
|
|
607
|
+
function filenameThreadId(path) {
|
|
608
|
+
return basename2(path).match(
|
|
609
|
+
/([\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12})\.jsonl$/i
|
|
610
|
+
)?.[1] ?? null;
|
|
471
611
|
}
|
|
472
612
|
function resolveCodexProject(payload) {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}
|
|
476
|
-
const repositoryUrl = payload.git?.repository_url;
|
|
477
|
-
if (repositoryUrl) {
|
|
613
|
+
const repositoryUrl = payload?.git?.repository_url;
|
|
614
|
+
if (typeof repositoryUrl === "string" && repositoryUrl) {
|
|
478
615
|
const match = repositoryUrl.match(/([^/]+\/[^/]+?)(?:\.git)?$/);
|
|
479
|
-
if (match)
|
|
480
|
-
return match[1];
|
|
481
|
-
}
|
|
616
|
+
if (match) return match[1];
|
|
482
617
|
}
|
|
483
|
-
|
|
484
|
-
|
|
618
|
+
const normalized = typeof payload?.cwd === "string" ? payload.cwd.replace(/\\/g, "/").replace(/\/+$/, "") : "";
|
|
619
|
+
return normalized?.split("/").filter(Boolean).pop() || "unknown";
|
|
620
|
+
}
|
|
621
|
+
function readRollout(path) {
|
|
622
|
+
const content = readFileSafe(path);
|
|
623
|
+
if (!content) return null;
|
|
624
|
+
const tokens = [];
|
|
625
|
+
const prompts = [];
|
|
626
|
+
let rootMeta = null;
|
|
627
|
+
let model = "unknown";
|
|
628
|
+
let latestTimestampOrder = 0n;
|
|
629
|
+
let line = 0;
|
|
630
|
+
for (const text of content.split("\n")) {
|
|
631
|
+
line++;
|
|
632
|
+
if (!text.trim()) continue;
|
|
633
|
+
try {
|
|
634
|
+
const event = JSON.parse(text);
|
|
635
|
+
const timestamp = readTimestamp(event.timestamp);
|
|
636
|
+
const order = timestampOrder(event.timestamp, timestamp);
|
|
637
|
+
if (order !== null && order > latestTimestampOrder)
|
|
638
|
+
latestTimestampOrder = order;
|
|
639
|
+
if (event.type === "session_meta" && !rootMeta) rootMeta = event;
|
|
640
|
+
if (event.type === "turn_context") {
|
|
641
|
+
model = readId(event.payload?.model) || readId(event.payload?.info?.model) || model;
|
|
642
|
+
if (timestamp && order !== null)
|
|
643
|
+
prompts.push({ line, timestamp, timestampOrder: order });
|
|
644
|
+
}
|
|
645
|
+
if (event.type !== "event_msg" || event.payload?.type !== "token_count")
|
|
646
|
+
continue;
|
|
647
|
+
const info = event.payload.info;
|
|
648
|
+
if (!info) continue;
|
|
649
|
+
const total = readUsage(info.total_token_usage);
|
|
650
|
+
const last = readUsage(info.last_token_usage);
|
|
651
|
+
if (!total && !last) continue;
|
|
652
|
+
model = readId(info.model) || readId(info.model_name) || readId(event.payload.model) || model;
|
|
653
|
+
tokens.push({
|
|
654
|
+
line,
|
|
655
|
+
timestamp,
|
|
656
|
+
timestampOrder: order,
|
|
657
|
+
model,
|
|
658
|
+
total,
|
|
659
|
+
last,
|
|
660
|
+
signature: JSON.stringify([total, last]),
|
|
661
|
+
snapshotSource: readId(event.payload.rate_limits?.limit_id) || ""
|
|
662
|
+
});
|
|
663
|
+
} catch {
|
|
664
|
+
}
|
|
485
665
|
}
|
|
486
|
-
|
|
666
|
+
const meta = rootMeta?.payload;
|
|
667
|
+
const threadId = readId(meta?.id) || readId(meta?.thread_id) || readId(meta?.threadId);
|
|
668
|
+
const forkedFrom = readId(meta?.forked_from_id);
|
|
669
|
+
const spawnedFrom = readId(
|
|
670
|
+
meta?.source?.subagent?.thread_spawn?.parent_thread_id
|
|
671
|
+
);
|
|
672
|
+
return {
|
|
673
|
+
path,
|
|
674
|
+
threadId,
|
|
675
|
+
sessionId: threadId ? `codex:${threadId}` : `codex-file:${path}`,
|
|
676
|
+
project: resolveCodexProject(meta),
|
|
677
|
+
parentId: forkedFrom || spawnedFrom,
|
|
678
|
+
parentConflict: Boolean(
|
|
679
|
+
forkedFrom && spawnedFrom && forkedFrom !== spawnedFrom
|
|
680
|
+
),
|
|
681
|
+
rootTimestampOrder: timestampOrder(
|
|
682
|
+
rootMeta?.timestamp,
|
|
683
|
+
readTimestamp(rootMeta?.timestamp)
|
|
684
|
+
),
|
|
685
|
+
latestTimestampOrder,
|
|
686
|
+
tokens,
|
|
687
|
+
prompts
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
function matchingReplayPrefix(tokens, history) {
|
|
691
|
+
let offset = 0;
|
|
692
|
+
let matched = 0;
|
|
693
|
+
for (const token of tokens) {
|
|
694
|
+
while (offset < history.length && (history[offset].signature !== token.signature || // 缺少累计值时,相同用量不能证明回放;还必须对应同一时刻和模型。
|
|
695
|
+
!token.total && (token.timestampOrder === null || token.timestampOrder !== history[offset].timestampOrder || token.model !== history[offset].model)))
|
|
696
|
+
offset++;
|
|
697
|
+
if (offset === history.length) break;
|
|
698
|
+
offset++;
|
|
699
|
+
matched++;
|
|
700
|
+
}
|
|
701
|
+
return matched;
|
|
702
|
+
}
|
|
703
|
+
function usageDelta(current, previous) {
|
|
704
|
+
return {
|
|
705
|
+
input: Math.max(0, current.input - (previous?.input ?? 0)),
|
|
706
|
+
output: Math.max(0, current.output - (previous?.output ?? 0)),
|
|
707
|
+
cached: Math.max(0, current.cached - (previous?.cached ?? 0)),
|
|
708
|
+
reasoning: Math.max(0, current.reasoning - (previous?.reasoning ?? 0)),
|
|
709
|
+
reportedTotal: null
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function highWater(previous, current) {
|
|
713
|
+
return {
|
|
714
|
+
input: Math.max(previous?.input ?? 0, current.input),
|
|
715
|
+
output: Math.max(previous?.output ?? 0, current.output),
|
|
716
|
+
cached: Math.max(previous?.cached ?? 0, current.cached),
|
|
717
|
+
reasoning: Math.max(previous?.reasoning ?? 0, current.reasoning),
|
|
718
|
+
reportedTotal: null
|
|
719
|
+
};
|
|
487
720
|
}
|
|
488
721
|
var CodexParser = class {
|
|
489
|
-
constructor(dataDir = DEFAULT_DATA_DIR) {
|
|
490
|
-
this.dataDir = dataDir;
|
|
491
|
-
this.tool = createToolDefinition(dataDir);
|
|
492
|
-
}
|
|
493
|
-
dataDir;
|
|
494
722
|
tool;
|
|
723
|
+
dataDirs;
|
|
724
|
+
constructor(dataDir = DEFAULT_DATA_DIR, archiveDir = dataDir === DEFAULT_DATA_DIR ? DEFAULT_ARCHIVE_DIR : void 0) {
|
|
725
|
+
this.dataDirs = archiveDir ? [dataDir, archiveDir] : [dataDir];
|
|
726
|
+
this.tool = { id: TOOL_ID, name: "Codex CLI", dataDir };
|
|
727
|
+
}
|
|
728
|
+
isInstalled() {
|
|
729
|
+
return this.dataDirs.some((directory) => existsSync4(directory));
|
|
730
|
+
}
|
|
495
731
|
async parse() {
|
|
496
732
|
const entries = [];
|
|
497
733
|
const sessionEvents = [];
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
if (
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
if (!timestamp || Number.isNaN(timestamp.getTime())) continue;
|
|
550
|
-
const model = info.model || payload.model || turnContextModel || sessionModel;
|
|
551
|
-
let isDuplicate = false;
|
|
552
|
-
if (info.total_token_usage) {
|
|
553
|
-
const curr = info.total_token_usage;
|
|
554
|
-
const stateKey = `${model}|${toSafeNumber(curr.input_tokens)}|${toSafeNumber(curr.output_tokens)}|${toSafeNumber(curr.cached_input_tokens)}|${toSafeNumber(curr.reasoning_output_tokens)}`;
|
|
555
|
-
isDuplicate = seenTotalStates.has(stateKey);
|
|
556
|
-
if (!isDuplicate) seenTotalStates.add(stateKey);
|
|
557
|
-
} else if (info.last_token_usage) {
|
|
558
|
-
const u = info.last_token_usage;
|
|
559
|
-
const lastKey = `${obj.timestamp}|${toSafeNumber(u.input_tokens)}|${toSafeNumber(u.output_tokens)}|${toSafeNumber(u.cached_input_tokens)}|${toSafeNumber(u.reasoning_output_tokens)}`;
|
|
560
|
-
isDuplicate = seenLastOnly.has(lastKey);
|
|
561
|
-
if (!isDuplicate) seenLastOnly.add(lastKey);
|
|
562
|
-
}
|
|
563
|
-
if (!isDuplicate) {
|
|
564
|
-
sessionEvents.push({
|
|
565
|
-
sessionId: filePath,
|
|
566
|
-
source: TOOL_ID,
|
|
567
|
-
project: sessionProject,
|
|
568
|
-
timestamp,
|
|
569
|
-
role: "assistant"
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
let usage = info.last_token_usage;
|
|
573
|
-
if (!usage && info.total_token_usage) {
|
|
574
|
-
const totalKey = `${info.model || payload.model || turnContextModel || ""}`;
|
|
575
|
-
const prev = prevTotal.get(totalKey);
|
|
576
|
-
const curr = info.total_token_usage;
|
|
577
|
-
if (prev) {
|
|
578
|
-
usage = {
|
|
579
|
-
input_tokens: toNonNegativeDelta(
|
|
580
|
-
curr.input_tokens,
|
|
581
|
-
prev.input_tokens
|
|
582
|
-
),
|
|
583
|
-
output_tokens: toNonNegativeDelta(
|
|
584
|
-
curr.output_tokens,
|
|
585
|
-
prev.output_tokens
|
|
586
|
-
),
|
|
587
|
-
cached_input_tokens: toNonNegativeDelta(
|
|
588
|
-
curr.cached_input_tokens,
|
|
589
|
-
prev.cached_input_tokens
|
|
590
|
-
),
|
|
591
|
-
reasoning_output_tokens: toNonNegativeDelta(
|
|
592
|
-
curr.reasoning_output_tokens,
|
|
593
|
-
prev.reasoning_output_tokens
|
|
594
|
-
)
|
|
595
|
-
};
|
|
596
|
-
} else {
|
|
597
|
-
usage = curr;
|
|
598
|
-
}
|
|
599
|
-
prevTotal.set(totalKey, { ...curr });
|
|
734
|
+
const threads = /* @__PURE__ */ new Map();
|
|
735
|
+
const threadIndex = /* @__PURE__ */ new Map();
|
|
736
|
+
const files = [...new Set(this.dataDirs.flatMap(findJsonlFiles))].sort();
|
|
737
|
+
for (const path of files) {
|
|
738
|
+
const file = readRollout(path);
|
|
739
|
+
if (!file) continue;
|
|
740
|
+
let thread = threads.get(file.sessionId);
|
|
741
|
+
if (!thread) {
|
|
742
|
+
thread = {
|
|
743
|
+
files: [],
|
|
744
|
+
timeline: [],
|
|
745
|
+
latestTimestampOrder: 0n,
|
|
746
|
+
invalidTokenTimestamp: false,
|
|
747
|
+
status: "new"
|
|
748
|
+
};
|
|
749
|
+
threads.set(file.sessionId, thread);
|
|
750
|
+
}
|
|
751
|
+
thread.files.push(file);
|
|
752
|
+
if (file.latestTimestampOrder > thread.latestTimestampOrder)
|
|
753
|
+
thread.latestTimestampOrder = file.latestTimestampOrder;
|
|
754
|
+
thread.invalidTokenTimestamp ||= file.tokens.some(
|
|
755
|
+
(token) => !token.timestamp
|
|
756
|
+
);
|
|
757
|
+
if (file.threadId) threadIndex.set(file.threadId, thread);
|
|
758
|
+
const rolloutId = filenameThreadId(path);
|
|
759
|
+
if (rolloutId) threadIndex.set(rolloutId, thread);
|
|
760
|
+
}
|
|
761
|
+
const processThread = (thread) => {
|
|
762
|
+
if (thread.status !== "new") return thread.status === "done";
|
|
763
|
+
thread.status = "visiting";
|
|
764
|
+
const threadEntries = [];
|
|
765
|
+
const threadEvents = [];
|
|
766
|
+
let cumulative = null;
|
|
767
|
+
const eventKeys = /* @__PURE__ */ new Set();
|
|
768
|
+
thread.files.sort((left, right) => {
|
|
769
|
+
const firstTime = (file) => file.tokens.find((token) => token.timestampOrder !== null)?.timestampOrder ?? file.rootTimestampOrder ?? 0n;
|
|
770
|
+
const leftTime = firstTime(left);
|
|
771
|
+
const rightTime = firstTime(right);
|
|
772
|
+
return leftTime < rightTime ? -1 : leftTime > rightTime ? 1 : left.path.localeCompare(right.path);
|
|
773
|
+
});
|
|
774
|
+
for (const file of thread.files) {
|
|
775
|
+
let parentPrefix = 0;
|
|
776
|
+
if (file.parentConflict || file.parentId) {
|
|
777
|
+
const parent = file.parentId ? threadIndex.get(file.parentId) : void 0;
|
|
778
|
+
const cutoff = file.rootTimestampOrder;
|
|
779
|
+
if (file.parentConflict || !parent || parent === thread || cutoff === null || !processThread(parent) || parent.invalidTokenTimestamp || parent.latestTimestampOrder < cutoff) {
|
|
780
|
+
logger.warn(
|
|
781
|
+
"Codex fork replay could not be verified; skipping " + basename2(file.path)
|
|
782
|
+
);
|
|
783
|
+
thread.status = "deferred";
|
|
784
|
+
return false;
|
|
600
785
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
0,
|
|
607
|
-
toSafeNumber(usage.input_tokens) - cachedInput
|
|
608
|
-
);
|
|
609
|
-
const outputTokens = Math.max(
|
|
610
|
-
0,
|
|
611
|
-
toSafeNumber(usage.output_tokens) - reasoningTokens
|
|
786
|
+
parentPrefix = matchingReplayPrefix(
|
|
787
|
+
file.tokens,
|
|
788
|
+
parent.timeline.filter(
|
|
789
|
+
(token) => token.timestampOrder !== null && token.timestampOrder <= cutoff
|
|
790
|
+
)
|
|
612
791
|
);
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
792
|
+
}
|
|
793
|
+
const ownPrefix = matchingReplayPrefix(file.tokens, thread.timeline);
|
|
794
|
+
const replayPrefix = Math.max(parentPrefix, ownPrefix);
|
|
795
|
+
const replayEndLine = file.tokens[replayPrefix - 1]?.line ?? -1;
|
|
796
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
797
|
+
const lastOnlyEvents = /* @__PURE__ */ new Set();
|
|
798
|
+
let previousSignature = null;
|
|
799
|
+
const addEvent = (timestamp, order, role) => {
|
|
800
|
+
const key = `${role}|${order.toString()}`;
|
|
801
|
+
if (eventKeys.has(key)) return;
|
|
802
|
+
eventKeys.add(key);
|
|
803
|
+
threadEvents.push({
|
|
804
|
+
sessionId: file.sessionId,
|
|
618
805
|
source: TOOL_ID,
|
|
619
|
-
|
|
620
|
-
project: sessionProject,
|
|
806
|
+
project: file.project,
|
|
621
807
|
timestamp,
|
|
622
|
-
|
|
623
|
-
|
|
808
|
+
role
|
|
809
|
+
});
|
|
810
|
+
};
|
|
811
|
+
for (const prompt of file.prompts) {
|
|
812
|
+
if (prompt.line > replayEndLine)
|
|
813
|
+
addEvent(prompt.timestamp, prompt.timestampOrder, "user");
|
|
814
|
+
}
|
|
815
|
+
for (const [index, token] of file.tokens.entries()) {
|
|
816
|
+
if (!token.timestamp || token.timestampOrder === null) continue;
|
|
817
|
+
const duplicate = token.total ? snapshots.get(token.snapshotSource) === token.signature || previousSignature === token.signature : lastOnlyEvents.has(
|
|
818
|
+
token.model + "|" + token.timestampOrder.toString() + "|" + token.signature
|
|
819
|
+
);
|
|
820
|
+
if (token.total) snapshots.set(token.snapshotSource, token.signature);
|
|
821
|
+
else
|
|
822
|
+
lastOnlyEvents.add(
|
|
823
|
+
token.model + "|" + token.timestampOrder.toString() + "|" + token.signature
|
|
824
|
+
);
|
|
825
|
+
previousSignature = token.signature;
|
|
826
|
+
const usage = token.last ?? (token.total ? usageDelta(token.total, cumulative) : null);
|
|
827
|
+
if (token.total) cumulative = highWater(cumulative, token.total);
|
|
828
|
+
if (!usage || duplicate || index < replayPrefix) continue;
|
|
829
|
+
addEvent(token.timestamp, token.timestampOrder, "assistant");
|
|
830
|
+
const cachedTokens = Math.min(usage.cached, usage.input);
|
|
831
|
+
const reasoningTokens = Math.min(usage.reasoning, usage.output);
|
|
832
|
+
if (usage.input === 0 && usage.output === 0) continue;
|
|
833
|
+
threadEntries.push({
|
|
834
|
+
sessionId: file.sessionId,
|
|
835
|
+
source: TOOL_ID,
|
|
836
|
+
model: token.model,
|
|
837
|
+
project: file.project,
|
|
838
|
+
timestamp: token.timestamp,
|
|
839
|
+
inputTokens: usage.input - cachedTokens,
|
|
840
|
+
outputTokens: usage.output - reasoningTokens,
|
|
624
841
|
reasoningTokens,
|
|
625
|
-
cachedTokens
|
|
842
|
+
cachedTokens
|
|
626
843
|
});
|
|
627
|
-
} catch {
|
|
628
844
|
}
|
|
845
|
+
for (let index = ownPrefix; index < file.tokens.length; index++)
|
|
846
|
+
thread.timeline.push(file.tokens[index]);
|
|
629
847
|
}
|
|
848
|
+
for (const entry of threadEntries) entries.push(entry);
|
|
849
|
+
for (const event of threadEvents) sessionEvents.push(event);
|
|
850
|
+
thread.status = "done";
|
|
851
|
+
return true;
|
|
852
|
+
};
|
|
853
|
+
let incomplete = false;
|
|
854
|
+
for (const thread of threads.values()) {
|
|
855
|
+
if (!processThread(thread)) incomplete = true;
|
|
630
856
|
}
|
|
631
857
|
return {
|
|
632
858
|
buckets: aggregateToBuckets(entries),
|
|
633
|
-
sessions: extractSessions(sessionEvents, entries)
|
|
859
|
+
sessions: extractSessions(sessionEvents, entries),
|
|
860
|
+
...incomplete ? { incomplete: true } : {}
|
|
634
861
|
};
|
|
635
862
|
}
|
|
636
863
|
};
|
|
637
864
|
registerParser(new CodexParser());
|
|
638
865
|
|
|
639
866
|
// src/parsers/gsd.ts
|
|
640
|
-
import { existsSync as
|
|
867
|
+
import { existsSync as existsSync5 } from "fs";
|
|
641
868
|
import { homedir as homedir3 } from "os";
|
|
642
869
|
import { join as join4 } from "path";
|
|
643
870
|
var TOOL_ID2 = "gsd";
|
|
644
|
-
var
|
|
871
|
+
var TOOL_NAME = "GSD-2";
|
|
645
872
|
var DEFAULT_SESSIONS_DIR = join4(homedir3(), ".gsd", "sessions");
|
|
646
|
-
function
|
|
873
|
+
function createToolDefinition(dataDir) {
|
|
647
874
|
return {
|
|
648
875
|
id: TOOL_ID2,
|
|
649
|
-
name:
|
|
876
|
+
name: TOOL_NAME,
|
|
650
877
|
dataDir
|
|
651
878
|
};
|
|
652
879
|
}
|
|
653
|
-
function
|
|
880
|
+
function toSafeNumber(value) {
|
|
654
881
|
const numberValue = Number(value);
|
|
655
882
|
return Number.isFinite(numberValue) ? numberValue : 0;
|
|
656
883
|
}
|
|
657
|
-
function
|
|
884
|
+
function getPathLeaf(value) {
|
|
658
885
|
const normalized = value.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
659
886
|
const leaf = normalized.split("/").filter(Boolean).pop();
|
|
660
887
|
return leaf || "unknown";
|
|
@@ -665,7 +892,7 @@ function normalizeForPrefix(value) {
|
|
|
665
892
|
function getUsageNumber(usage, ...keys) {
|
|
666
893
|
for (const key of keys) {
|
|
667
894
|
const value = usage[key];
|
|
668
|
-
const numberValue =
|
|
895
|
+
const numberValue = toSafeNumber(value);
|
|
669
896
|
if (numberValue > 0) {
|
|
670
897
|
return numberValue;
|
|
671
898
|
}
|
|
@@ -673,7 +900,7 @@ function getUsageNumber(usage, ...keys) {
|
|
|
673
900
|
return 0;
|
|
674
901
|
}
|
|
675
902
|
function extractGsdProjectFromCwd(cwd) {
|
|
676
|
-
return
|
|
903
|
+
return getPathLeaf(cwd);
|
|
677
904
|
}
|
|
678
905
|
function extractGsdProjectFromDir(filePath, sessionsDir = DEFAULT_SESSIONS_DIR) {
|
|
679
906
|
const normalizedFilePath = normalizeForPrefix(filePath);
|
|
@@ -690,7 +917,7 @@ function extractGsdProjectFromDir(filePath, sessionsDir = DEFAULT_SESSIONS_DIR)
|
|
|
690
917
|
try {
|
|
691
918
|
const decoded = decodeURIComponent(firstSegment);
|
|
692
919
|
if (decoded.includes("/") || decoded.includes("\\")) {
|
|
693
|
-
return
|
|
920
|
+
return getPathLeaf(decoded);
|
|
694
921
|
}
|
|
695
922
|
} catch {
|
|
696
923
|
}
|
|
@@ -700,7 +927,7 @@ function extractGsdProjectFromDir(filePath, sessionsDir = DEFAULT_SESSIONS_DIR)
|
|
|
700
927
|
var GsdParser = class {
|
|
701
928
|
constructor(sessionsDir = DEFAULT_SESSIONS_DIR) {
|
|
702
929
|
this.sessionsDir = sessionsDir;
|
|
703
|
-
this.tool =
|
|
930
|
+
this.tool = createToolDefinition(sessionsDir);
|
|
704
931
|
}
|
|
705
932
|
sessionsDir;
|
|
706
933
|
tool;
|
|
@@ -789,15 +1016,15 @@ var GsdParser = class {
|
|
|
789
1016
|
};
|
|
790
1017
|
}
|
|
791
1018
|
isInstalled() {
|
|
792
|
-
return
|
|
1019
|
+
return existsSync5(this.sessionsDir);
|
|
793
1020
|
}
|
|
794
1021
|
};
|
|
795
1022
|
registerParser(new GsdParser());
|
|
796
1023
|
|
|
797
1024
|
// src/parsers/gemini-cli.ts
|
|
798
|
-
import { existsSync as
|
|
1025
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync2 } from "fs";
|
|
799
1026
|
import { homedir as homedir4 } from "os";
|
|
800
|
-
import { basename as
|
|
1027
|
+
import { basename as basename3, join as join5 } from "path";
|
|
801
1028
|
var TOOL2 = {
|
|
802
1029
|
id: "gemini-cli",
|
|
803
1030
|
name: "Gemini CLI",
|
|
@@ -805,7 +1032,7 @@ var TOOL2 = {
|
|
|
805
1032
|
};
|
|
806
1033
|
function findSessionFiles(baseDir) {
|
|
807
1034
|
const results = [];
|
|
808
|
-
if (!
|
|
1035
|
+
if (!existsSync6(baseDir)) return results;
|
|
809
1036
|
let projectDirs;
|
|
810
1037
|
try {
|
|
811
1038
|
projectDirs = readdirSync2(baseDir, { withFileTypes: true });
|
|
@@ -847,6 +1074,7 @@ function readRecord(filePath) {
|
|
|
847
1074
|
let directories = null;
|
|
848
1075
|
let model = null;
|
|
849
1076
|
let createTime = null;
|
|
1077
|
+
let sessionId = null;
|
|
850
1078
|
for (const line of raw.split("\n")) {
|
|
851
1079
|
const trimmed = line.trim();
|
|
852
1080
|
if (!trimmed) continue;
|
|
@@ -856,6 +1084,8 @@ function readRecord(filePath) {
|
|
|
856
1084
|
} catch {
|
|
857
1085
|
continue;
|
|
858
1086
|
}
|
|
1087
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj)) continue;
|
|
1088
|
+
if (typeof obj.sessionId === "string") sessionId = obj.sessionId;
|
|
859
1089
|
if (!directories && Array.isArray(obj.directories)) {
|
|
860
1090
|
directories = obj.directories;
|
|
861
1091
|
if (typeof obj.model === "string") model = obj.model;
|
|
@@ -866,7 +1096,7 @@ function readRecord(filePath) {
|
|
|
866
1096
|
messages.push(obj);
|
|
867
1097
|
}
|
|
868
1098
|
}
|
|
869
|
-
return { messages, directories, model, createTime };
|
|
1099
|
+
return { messages, directories, model, createTime, sessionId };
|
|
870
1100
|
}
|
|
871
1101
|
let data;
|
|
872
1102
|
try {
|
|
@@ -875,6 +1105,7 @@ function readRecord(filePath) {
|
|
|
875
1105
|
return null;
|
|
876
1106
|
}
|
|
877
1107
|
return {
|
|
1108
|
+
sessionId: typeof data?.sessionId === "string" ? data.sessionId : null,
|
|
878
1109
|
messages: Array.isArray(data?.messages) ? data.messages : Array.isArray(data?.history) ? data.history : [],
|
|
879
1110
|
directories: Array.isArray(data?.directories) ? data.directories : null,
|
|
880
1111
|
model: typeof data?.model === "string" ? data.model : null,
|
|
@@ -891,7 +1122,23 @@ function projectFromDirectories(directories) {
|
|
|
891
1122
|
if (!directories || directories.length === 0) return "unknown";
|
|
892
1123
|
const first = directories[0];
|
|
893
1124
|
if (!first) return "unknown";
|
|
894
|
-
return
|
|
1125
|
+
return basename3(String(first).replace(/[\\/]+$/, "")) || "unknown";
|
|
1126
|
+
}
|
|
1127
|
+
function tokenCount2(value) {
|
|
1128
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : 0;
|
|
1129
|
+
}
|
|
1130
|
+
function selectUsageSnapshot(current, candidate) {
|
|
1131
|
+
if (!current) return candidate;
|
|
1132
|
+
const total = (entry) => entry.inputTokens + entry.outputTokens + entry.reasoningTokens + entry.cachedTokens;
|
|
1133
|
+
const currentTotal = total(current);
|
|
1134
|
+
const candidateTotal = total(candidate);
|
|
1135
|
+
if (candidateTotal === 0 && currentTotal > 0) return current;
|
|
1136
|
+
if (candidate.timestamp.getTime() !== current.timestamp.getTime()) {
|
|
1137
|
+
return candidate.timestamp > current.timestamp ? candidate : current;
|
|
1138
|
+
}
|
|
1139
|
+
if (candidateTotal !== currentTotal)
|
|
1140
|
+
return candidateTotal > currentTotal ? candidate : current;
|
|
1141
|
+
return current.model === "unknown" && candidate.model !== "unknown" ? candidate : current;
|
|
895
1142
|
}
|
|
896
1143
|
var GeminiCliParser = class {
|
|
897
1144
|
tool = TOOL2;
|
|
@@ -900,54 +1147,73 @@ var GeminiCliParser = class {
|
|
|
900
1147
|
if (sessionFiles.length === 0) {
|
|
901
1148
|
return { buckets: [], sessions: [] };
|
|
902
1149
|
}
|
|
903
|
-
const entries =
|
|
904
|
-
const sessionEvents =
|
|
1150
|
+
const entries = /* @__PURE__ */ new Map();
|
|
1151
|
+
const sessionEvents = /* @__PURE__ */ new Map();
|
|
1152
|
+
const storeEntry = (key, entry) => {
|
|
1153
|
+
entries.set(key, selectUsageSnapshot(entries.get(key), entry));
|
|
1154
|
+
};
|
|
905
1155
|
for (const filePath of sessionFiles) {
|
|
906
1156
|
const record = readRecord(filePath);
|
|
907
1157
|
if (!record) continue;
|
|
908
1158
|
const project = projectFromDirectories(record.directories);
|
|
909
|
-
|
|
1159
|
+
const sessionId = record.sessionId || filePath;
|
|
1160
|
+
for (const [index, msg] of record.messages.entries()) {
|
|
1161
|
+
if (!msg || typeof msg !== "object") continue;
|
|
910
1162
|
const role = classifyRole(msg);
|
|
911
1163
|
if (!role) continue;
|
|
912
1164
|
const timestamp = msg.timestamp || msg.createTime || record.createTime;
|
|
913
1165
|
if (!timestamp) continue;
|
|
914
1166
|
const ts = new Date(timestamp);
|
|
915
1167
|
if (Number.isNaN(ts.getTime())) continue;
|
|
916
|
-
|
|
917
|
-
sessionId
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1168
|
+
const key = JSON.stringify([
|
|
1169
|
+
sessionId,
|
|
1170
|
+
msg.id || `${filePath}:${index}`
|
|
1171
|
+
]);
|
|
1172
|
+
if ((sessionEvents.get(key)?.timestamp.getTime() ?? -Infinity) <= ts.getTime())
|
|
1173
|
+
sessionEvents.set(key, {
|
|
1174
|
+
sessionId,
|
|
1175
|
+
source: "gemini-cli",
|
|
1176
|
+
project,
|
|
1177
|
+
timestamp: ts,
|
|
1178
|
+
role
|
|
1179
|
+
});
|
|
1180
|
+
if (role !== "assistant") continue;
|
|
923
1181
|
const tokens = msg.tokens;
|
|
924
1182
|
const usage = msg.usage || msg.usageMetadata || msg.token_count;
|
|
925
1183
|
if (!tokens && !usage) continue;
|
|
926
1184
|
if (tokens) {
|
|
927
|
-
const cached = tokens.cached
|
|
928
|
-
const
|
|
929
|
-
|
|
930
|
-
|
|
1185
|
+
const cached = tokenCount2(tokens.cached);
|
|
1186
|
+
const input2 = tokenCount2(tokens.input);
|
|
1187
|
+
const thoughts = tokenCount2(tokens.thoughts);
|
|
1188
|
+
storeEntry(key, {
|
|
1189
|
+
sessionId,
|
|
931
1190
|
source: "gemini-cli",
|
|
932
1191
|
model: msg.model || record.model || "unknown",
|
|
933
1192
|
project,
|
|
934
1193
|
timestamp: ts,
|
|
935
|
-
inputTokens:
|
|
936
|
-
outputTokens: (tokens.output
|
|
1194
|
+
inputTokens: input2 >= cached ? input2 - cached : input2,
|
|
1195
|
+
outputTokens: tokenCount2(tokens.output),
|
|
937
1196
|
reasoningTokens: thoughts,
|
|
938
1197
|
cachedTokens: cached
|
|
939
1198
|
});
|
|
940
1199
|
} else if (usage) {
|
|
941
|
-
const cached = usage.cachedContentTokenCount
|
|
942
|
-
const thoughts = usage.thoughtsTokenCount
|
|
943
|
-
|
|
944
|
-
|
|
1200
|
+
const cached = tokenCount2(usage.cachedContentTokenCount);
|
|
1201
|
+
const thoughts = tokenCount2(usage.thoughtsTokenCount);
|
|
1202
|
+
const input2 = tokenCount2(
|
|
1203
|
+
usage.promptTokenCount ?? usage.input_tokens
|
|
1204
|
+
);
|
|
1205
|
+
const output = usage.candidatesTokenCount ?? usage.output_tokens;
|
|
1206
|
+
storeEntry(key, {
|
|
1207
|
+
sessionId,
|
|
945
1208
|
source: "gemini-cli",
|
|
946
1209
|
model: msg.model || record.model || "unknown",
|
|
947
1210
|
project,
|
|
948
1211
|
timestamp: ts,
|
|
949
|
-
inputTokens:
|
|
950
|
-
outputTokens:
|
|
1212
|
+
inputTokens: input2 >= cached ? input2 - cached : input2,
|
|
1213
|
+
outputTokens: output !== void 0 ? tokenCount2(output) : Math.max(
|
|
1214
|
+
0,
|
|
1215
|
+
tokenCount2(usage.totalTokenCount) - input2 - thoughts
|
|
1216
|
+
),
|
|
951
1217
|
reasoningTokens: thoughts,
|
|
952
1218
|
cachedTokens: cached
|
|
953
1219
|
});
|
|
@@ -955,15 +1221,18 @@ var GeminiCliParser = class {
|
|
|
955
1221
|
}
|
|
956
1222
|
}
|
|
957
1223
|
return {
|
|
958
|
-
buckets: aggregateToBuckets(entries),
|
|
959
|
-
sessions: extractSessions(
|
|
1224
|
+
buckets: aggregateToBuckets([...entries.values()]),
|
|
1225
|
+
sessions: extractSessions(
|
|
1226
|
+
[...sessionEvents.values()],
|
|
1227
|
+
[...entries.values()]
|
|
1228
|
+
)
|
|
960
1229
|
};
|
|
961
1230
|
}
|
|
962
1231
|
};
|
|
963
1232
|
registerParser(new GeminiCliParser());
|
|
964
1233
|
|
|
965
1234
|
// src/parsers/hermes.ts
|
|
966
|
-
import { existsSync as
|
|
1235
|
+
import { existsSync as existsSync7 } from "fs";
|
|
967
1236
|
import { homedir as homedir5 } from "os";
|
|
968
1237
|
import { dirname, join as join6 } from "path";
|
|
969
1238
|
|
|
@@ -971,53 +1240,6 @@ import { dirname, join as join6 } from "path";
|
|
|
971
1240
|
import { execFileSync } from "child_process";
|
|
972
1241
|
import { statSync } from "fs";
|
|
973
1242
|
import { pathToFileURL } from "url";
|
|
974
|
-
|
|
975
|
-
// src/utils/logger.ts
|
|
976
|
-
var LOG_LEVELS = {
|
|
977
|
-
debug: 0,
|
|
978
|
-
info: 1,
|
|
979
|
-
warn: 2,
|
|
980
|
-
error: 3
|
|
981
|
-
};
|
|
982
|
-
var Logger = class {
|
|
983
|
-
level;
|
|
984
|
-
constructor(level = "info") {
|
|
985
|
-
this.level = level;
|
|
986
|
-
}
|
|
987
|
-
setLevel(level) {
|
|
988
|
-
this.level = level;
|
|
989
|
-
}
|
|
990
|
-
debug(msg) {
|
|
991
|
-
if (LOG_LEVELS[this.level] <= LOG_LEVELS.debug) {
|
|
992
|
-
process.stderr.write(`[debug] ${msg}
|
|
993
|
-
`);
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
info(msg) {
|
|
997
|
-
if (LOG_LEVELS[this.level] <= LOG_LEVELS.info) {
|
|
998
|
-
process.stdout.write(`${msg}
|
|
999
|
-
`);
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
warn(msg) {
|
|
1003
|
-
if (LOG_LEVELS[this.level] <= LOG_LEVELS.warn) {
|
|
1004
|
-
process.stderr.write(`warn: ${msg}
|
|
1005
|
-
`);
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
error(msg) {
|
|
1009
|
-
if (LOG_LEVELS[this.level] <= LOG_LEVELS.error) {
|
|
1010
|
-
process.stderr.write(`error: ${msg}
|
|
1011
|
-
`);
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
log(msg) {
|
|
1015
|
-
this.info(msg);
|
|
1016
|
-
}
|
|
1017
|
-
};
|
|
1018
|
-
var logger = new Logger();
|
|
1019
|
-
|
|
1020
|
-
// src/infrastructure/sqlite.ts
|
|
1021
1243
|
function withSuppressedSqliteWarning(fn) {
|
|
1022
1244
|
const originalEmitWarning = process.emitWarning;
|
|
1023
1245
|
process.emitWarning = ((warning, ...args) => {
|
|
@@ -1122,7 +1344,7 @@ async function readSqliteRows(dbPath, query) {
|
|
|
1122
1344
|
|
|
1123
1345
|
// src/parsers/hermes.ts
|
|
1124
1346
|
var TOOL_ID3 = "hermes";
|
|
1125
|
-
var
|
|
1347
|
+
var TOOL_NAME2 = "Hermes Agent";
|
|
1126
1348
|
var DEFAULT_DB_PATH = join6(homedir5(), ".hermes", "state.db");
|
|
1127
1349
|
var SESSIONS_QUERY = `SELECT
|
|
1128
1350
|
id,
|
|
@@ -1144,14 +1366,14 @@ var MESSAGES_QUERY = `SELECT
|
|
|
1144
1366
|
FROM messages
|
|
1145
1367
|
WHERE role IN ('user', 'assistant')
|
|
1146
1368
|
ORDER BY timestamp`;
|
|
1147
|
-
function
|
|
1369
|
+
function createToolDefinition2(dbPath) {
|
|
1148
1370
|
return {
|
|
1149
1371
|
id: TOOL_ID3,
|
|
1150
|
-
name:
|
|
1372
|
+
name: TOOL_NAME2,
|
|
1151
1373
|
dataDir: dirname(dbPath)
|
|
1152
1374
|
};
|
|
1153
1375
|
}
|
|
1154
|
-
function
|
|
1376
|
+
function toSafeNumber2(value) {
|
|
1155
1377
|
const numberValue = Number(value);
|
|
1156
1378
|
return Number.isFinite(numberValue) ? numberValue : 0;
|
|
1157
1379
|
}
|
|
@@ -1170,10 +1392,10 @@ var HermesParser = class {
|
|
|
1170
1392
|
constructor(options = {}) {
|
|
1171
1393
|
this.dbPath = options.dbPath || DEFAULT_DB_PATH;
|
|
1172
1394
|
this.queryRows = options.queryRows || readSqliteRows;
|
|
1173
|
-
this.tool =
|
|
1395
|
+
this.tool = createToolDefinition2(this.dbPath);
|
|
1174
1396
|
}
|
|
1175
1397
|
async parse() {
|
|
1176
|
-
if (!
|
|
1398
|
+
if (!existsSync7(this.dbPath)) {
|
|
1177
1399
|
return { buckets: [], sessions: [] };
|
|
1178
1400
|
}
|
|
1179
1401
|
const sessionRows = await this.queryRows(
|
|
@@ -1186,10 +1408,10 @@ var HermesParser = class {
|
|
|
1186
1408
|
if (!sessionId) continue;
|
|
1187
1409
|
const timestamp = parseUnixTimestamp(row.startedAt);
|
|
1188
1410
|
if (!timestamp) continue;
|
|
1189
|
-
const inputTokens =
|
|
1190
|
-
const outputTokens =
|
|
1191
|
-
const cachedTokens =
|
|
1192
|
-
const reasoningTokens =
|
|
1411
|
+
const inputTokens = toSafeNumber2(row.inputTokens);
|
|
1412
|
+
const outputTokens = toSafeNumber2(row.outputTokens);
|
|
1413
|
+
const cachedTokens = toSafeNumber2(row.cacheReadTokens);
|
|
1414
|
+
const reasoningTokens = toSafeNumber2(row.reasoningTokens);
|
|
1193
1415
|
if (inputTokens === 0 && outputTokens === 0 && cachedTokens === 0 && reasoningTokens === 0) {
|
|
1194
1416
|
continue;
|
|
1195
1417
|
}
|
|
@@ -1239,17 +1461,17 @@ var HermesParser = class {
|
|
|
1239
1461
|
};
|
|
1240
1462
|
}
|
|
1241
1463
|
isInstalled() {
|
|
1242
|
-
return
|
|
1464
|
+
return existsSync7(this.dbPath);
|
|
1243
1465
|
}
|
|
1244
1466
|
};
|
|
1245
1467
|
registerParser(new HermesParser());
|
|
1246
1468
|
|
|
1247
1469
|
// src/parsers/mimocode.ts
|
|
1248
|
-
import { existsSync as
|
|
1470
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1249
1471
|
import { homedir as homedir6 } from "os";
|
|
1250
1472
|
import { join as join7 } from "path";
|
|
1251
1473
|
var TOOL_ID4 = "mimocode";
|
|
1252
|
-
var
|
|
1474
|
+
var TOOL_NAME3 = "MiMoCode";
|
|
1253
1475
|
var DEFAULT_DB_PATH2 = join7(
|
|
1254
1476
|
homedir6(),
|
|
1255
1477
|
".local",
|
|
@@ -1287,7 +1509,7 @@ function getMimocodeDbPaths() {
|
|
|
1287
1509
|
].filter((value) => Boolean(value));
|
|
1288
1510
|
return Array.from(new Set(paths));
|
|
1289
1511
|
}
|
|
1290
|
-
function
|
|
1512
|
+
function toSafeNumber3(value) {
|
|
1291
1513
|
const n = Number(value);
|
|
1292
1514
|
return Number.isFinite(n) && n >= 0 ? Math.round(n) : 0;
|
|
1293
1515
|
}
|
|
@@ -1307,7 +1529,7 @@ var MimocodeParser = class {
|
|
|
1307
1529
|
this.resolveDbPaths = resolveDbPaths;
|
|
1308
1530
|
this.tool = {
|
|
1309
1531
|
id: TOOL_ID4,
|
|
1310
|
-
name:
|
|
1532
|
+
name: TOOL_NAME3,
|
|
1311
1533
|
dataDir: DEFAULT_DB_PATH2
|
|
1312
1534
|
};
|
|
1313
1535
|
}
|
|
@@ -1316,13 +1538,15 @@ var MimocodeParser = class {
|
|
|
1316
1538
|
async parse() {
|
|
1317
1539
|
const allEntries = [];
|
|
1318
1540
|
const allSessionEvents = [];
|
|
1541
|
+
let incomplete = false;
|
|
1319
1542
|
for (const dbPath of this.resolveDbPaths()) {
|
|
1320
|
-
if (!
|
|
1543
|
+
if (!existsSync8(dbPath)) continue;
|
|
1321
1544
|
try {
|
|
1322
1545
|
const { entries, sessionEvents } = await this.parseDb(dbPath);
|
|
1323
1546
|
allEntries.push(...entries);
|
|
1324
1547
|
allSessionEvents.push(...sessionEvents);
|
|
1325
1548
|
} catch (err) {
|
|
1549
|
+
incomplete = true;
|
|
1326
1550
|
process.stderr.write(
|
|
1327
1551
|
`warn: mimocode parse failed for ${dbPath} (${err.message})
|
|
1328
1552
|
`
|
|
@@ -1331,11 +1555,12 @@ var MimocodeParser = class {
|
|
|
1331
1555
|
}
|
|
1332
1556
|
return {
|
|
1333
1557
|
buckets: aggregateToBuckets(allEntries),
|
|
1334
|
-
sessions: extractSessions(allSessionEvents, allEntries)
|
|
1558
|
+
sessions: extractSessions(allSessionEvents, allEntries),
|
|
1559
|
+
...incomplete ? { incomplete: true } : {}
|
|
1335
1560
|
};
|
|
1336
1561
|
}
|
|
1337
1562
|
isInstalled() {
|
|
1338
|
-
return this.resolveDbPaths().some((p) =>
|
|
1563
|
+
return this.resolveDbPaths().some((p) => existsSync8(p));
|
|
1339
1564
|
}
|
|
1340
1565
|
async parseDb(dbPath) {
|
|
1341
1566
|
const messageRows = await readSqliteRows(
|
|
@@ -1353,11 +1578,12 @@ var MimocodeParser = class {
|
|
|
1353
1578
|
const model = typeof row.modelID === "string" ? row.modelID : "unknown";
|
|
1354
1579
|
const sessionId = typeof row.sessionId === "string" ? row.sessionId : "unknown";
|
|
1355
1580
|
const project = extractProject2(row.directory);
|
|
1356
|
-
const inputTokens =
|
|
1357
|
-
const outputTokens =
|
|
1358
|
-
const reasoningTokens =
|
|
1359
|
-
const cachedTokens =
|
|
1360
|
-
|
|
1581
|
+
const inputTokens = toSafeNumber3(row.inputTokens);
|
|
1582
|
+
const outputTokens = toSafeNumber3(row.outputTokens);
|
|
1583
|
+
const reasoningTokens = toSafeNumber3(row.reasoningTokens);
|
|
1584
|
+
const cachedTokens = toSafeNumber3(row.cacheReadTokens);
|
|
1585
|
+
const cacheCreationTokens = toSafeNumber3(row.cacheWriteTokens);
|
|
1586
|
+
if (inputTokens + outputTokens + reasoningTokens + cachedTokens + cacheCreationTokens === 0) {
|
|
1361
1587
|
continue;
|
|
1362
1588
|
}
|
|
1363
1589
|
entries.push({
|
|
@@ -1369,7 +1595,8 @@ var MimocodeParser = class {
|
|
|
1369
1595
|
inputTokens,
|
|
1370
1596
|
outputTokens,
|
|
1371
1597
|
reasoningTokens,
|
|
1372
|
-
cachedTokens
|
|
1598
|
+
cachedTokens,
|
|
1599
|
+
cacheCreationTokens
|
|
1373
1600
|
});
|
|
1374
1601
|
}
|
|
1375
1602
|
for (const row of sessionRows) {
|
|
@@ -1392,20 +1619,131 @@ var MimocodeParser = class {
|
|
|
1392
1619
|
};
|
|
1393
1620
|
registerParser(new MimocodeParser());
|
|
1394
1621
|
|
|
1395
|
-
// src/parsers/
|
|
1396
|
-
import {
|
|
1397
|
-
import {
|
|
1398
|
-
import { homedir as homedir7, hostname as hostname3 } from "os";
|
|
1622
|
+
// src/parsers/mcode.ts
|
|
1623
|
+
import { existsSync as existsSync9 } from "fs";
|
|
1624
|
+
import { homedir as homedir7 } from "os";
|
|
1399
1625
|
import { join as join8 } from "path";
|
|
1626
|
+
function getMcodeDbPath(env = process.env) {
|
|
1627
|
+
const dataDir = [env.MINIMAX_DATA_DIR, env.MAVIS_DATA_DIR].map((value) => value?.trim()).find(Boolean) || join8(homedir7(), ".minimax");
|
|
1628
|
+
return join8(dataDir, "v2", "sqlite", "runtime-state.sqlite");
|
|
1629
|
+
}
|
|
1630
|
+
function tokenCount3(value) {
|
|
1631
|
+
const count = Number(value);
|
|
1632
|
+
return Number.isFinite(count) && count >= 0 ? Math.trunc(count) : 0;
|
|
1633
|
+
}
|
|
1634
|
+
var McodeParser = class {
|
|
1635
|
+
tool;
|
|
1636
|
+
dbPath;
|
|
1637
|
+
queryRows;
|
|
1638
|
+
constructor(options = {}) {
|
|
1639
|
+
this.dbPath = options.dbPath ?? getMcodeDbPath();
|
|
1640
|
+
this.queryRows = options.queryRows ?? readSqliteRows;
|
|
1641
|
+
this.tool = { id: "mcode", name: "MiniMax Code", dataDir: this.dbPath };
|
|
1642
|
+
}
|
|
1643
|
+
async parse() {
|
|
1644
|
+
if (!existsSync9(this.dbPath)) return { buckets: [], sessions: [] };
|
|
1645
|
+
let rows;
|
|
1646
|
+
try {
|
|
1647
|
+
rows = await this.queryRows(
|
|
1648
|
+
this.dbPath,
|
|
1649
|
+
`SELECT
|
|
1650
|
+
id, session_id AS sessionId, model, ts AS timestamp,
|
|
1651
|
+
input_tokens AS inputTokens, output_tokens AS outputTokens,
|
|
1652
|
+
reasoning_tokens AS reasoningTokens, cache_read_tokens AS cachedTokens,
|
|
1653
|
+
cache_write_tokens AS cacheCreationTokens
|
|
1654
|
+
FROM local_runtime_token_usage ORDER BY id`
|
|
1655
|
+
);
|
|
1656
|
+
} catch (error) {
|
|
1657
|
+
process.stderr.write(
|
|
1658
|
+
`warn: mcode usage parse failed (${error.message})
|
|
1659
|
+
`
|
|
1660
|
+
);
|
|
1661
|
+
return { buckets: [], sessions: [], incomplete: true };
|
|
1662
|
+
}
|
|
1663
|
+
const projects = /* @__PURE__ */ new Map();
|
|
1664
|
+
let incomplete = false;
|
|
1665
|
+
try {
|
|
1666
|
+
const sessions = await this.queryRows(
|
|
1667
|
+
this.dbPath,
|
|
1668
|
+
"SELECT session_id AS sessionId, workspace_dir AS directory FROM local_runtime_sessions"
|
|
1669
|
+
);
|
|
1670
|
+
for (const row of sessions) {
|
|
1671
|
+
const project = row.directory?.replace(/[\\/]+$/, "").split(/[\\/]/).pop();
|
|
1672
|
+
if (project) projects.set(row.sessionId, project);
|
|
1673
|
+
}
|
|
1674
|
+
} catch (error) {
|
|
1675
|
+
if (!error.message.includes(
|
|
1676
|
+
"no such table: local_runtime_sessions"
|
|
1677
|
+
)) {
|
|
1678
|
+
incomplete = true;
|
|
1679
|
+
process.stderr.write(
|
|
1680
|
+
`warn: mcode session metadata parse failed (${error.message})
|
|
1681
|
+
`
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
const entries = [];
|
|
1686
|
+
const events = [];
|
|
1687
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1688
|
+
for (const row of rows) {
|
|
1689
|
+
if (row.timestamp === null || row.timestamp === void 0) continue;
|
|
1690
|
+
const timestamp = new Date(Number(row.timestamp));
|
|
1691
|
+
if (!Number.isFinite(Number(row.timestamp)) || Number.isNaN(timestamp.getTime()))
|
|
1692
|
+
continue;
|
|
1693
|
+
const key = JSON.stringify([row.sessionId, row.id]);
|
|
1694
|
+
if (seen.has(key)) continue;
|
|
1695
|
+
seen.add(key);
|
|
1696
|
+
const nativeModel = row.model || "unknown";
|
|
1697
|
+
const model = nativeModel.includes("/") ? nativeModel.slice(nativeModel.indexOf("/") + 1) : nativeModel;
|
|
1698
|
+
const entry = {
|
|
1699
|
+
sessionId: row.sessionId,
|
|
1700
|
+
source: "mcode",
|
|
1701
|
+
project: projects.get(row.sessionId) || "unknown",
|
|
1702
|
+
model: model || "unknown",
|
|
1703
|
+
timestamp,
|
|
1704
|
+
inputTokens: tokenCount3(row.inputTokens),
|
|
1705
|
+
outputTokens: tokenCount3(row.outputTokens),
|
|
1706
|
+
reasoningTokens: tokenCount3(row.reasoningTokens),
|
|
1707
|
+
cachedTokens: tokenCount3(row.cachedTokens),
|
|
1708
|
+
cacheCreationTokens: tokenCount3(row.cacheCreationTokens)
|
|
1709
|
+
};
|
|
1710
|
+
if (entry.inputTokens + entry.outputTokens + entry.reasoningTokens + entry.cachedTokens + (entry.cacheCreationTokens ?? 0) === 0)
|
|
1711
|
+
continue;
|
|
1712
|
+
entries.push(entry);
|
|
1713
|
+
events.push({
|
|
1714
|
+
sessionId: row.sessionId,
|
|
1715
|
+
source: "mcode",
|
|
1716
|
+
project: entry.project,
|
|
1717
|
+
timestamp,
|
|
1718
|
+
role: "assistant"
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
return {
|
|
1722
|
+
buckets: aggregateToBuckets(entries),
|
|
1723
|
+
sessions: extractSessions(events, entries),
|
|
1724
|
+
...incomplete ? { incomplete: true } : {}
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
isInstalled() {
|
|
1728
|
+
return existsSync9(this.dbPath);
|
|
1729
|
+
}
|
|
1730
|
+
};
|
|
1731
|
+
registerParser(new McodeParser());
|
|
1732
|
+
|
|
1733
|
+
// src/parsers/mirasim.ts
|
|
1734
|
+
import { createHash as createHash3 } from "crypto";
|
|
1735
|
+
import { existsSync as existsSync10 } from "fs";
|
|
1736
|
+
import { homedir as homedir8, hostname as hostname3 } from "os";
|
|
1737
|
+
import { join as join9 } from "path";
|
|
1400
1738
|
var TOOL_ID5 = "mirasim";
|
|
1401
|
-
var
|
|
1402
|
-
var DEFAULT_INSIGHTS_DIR =
|
|
1739
|
+
var TOOL_NAME4 = "Mirasim";
|
|
1740
|
+
var DEFAULT_INSIGHTS_DIR = join9(homedir8(), ".mirasim", "insights");
|
|
1403
1741
|
var USAGE_FILE_PATTERN = /^usage-\d{4}-\d{2}\.ndjson$/;
|
|
1404
1742
|
var MIRASIM_OWN_AGENTS = ["gui", "pi-gui"];
|
|
1405
1743
|
function getInsightsDirs(env = process.env) {
|
|
1406
1744
|
const dirs = [
|
|
1407
1745
|
env.TOKEN_ARENA_MIRASIM_DIR,
|
|
1408
|
-
env.MIRASIM_HOME ?
|
|
1746
|
+
env.MIRASIM_HOME ? join9(env.MIRASIM_HOME, "insights") : void 0,
|
|
1409
1747
|
DEFAULT_INSIGHTS_DIR
|
|
1410
1748
|
].filter((value) => Boolean(value));
|
|
1411
1749
|
return Array.from(new Set(dirs));
|
|
@@ -1435,7 +1773,7 @@ function parseIsoDate(value) {
|
|
|
1435
1773
|
const timestamp = new Date(raw);
|
|
1436
1774
|
return Number.isNaN(timestamp.getTime()) ? null : timestamp;
|
|
1437
1775
|
}
|
|
1438
|
-
function
|
|
1776
|
+
function getPathLeaf2(value) {
|
|
1439
1777
|
if (!value) {
|
|
1440
1778
|
return "unknown";
|
|
1441
1779
|
}
|
|
@@ -1454,13 +1792,14 @@ function buildSessionUsage2(entries) {
|
|
|
1454
1792
|
byModel = /* @__PURE__ */ new Map();
|
|
1455
1793
|
usageBySession.set(entry.sessionId, byModel);
|
|
1456
1794
|
}
|
|
1457
|
-
const totalTokens = entry.inputTokens + entry.outputTokens + entry.reasoningTokens + entry.cachedTokens;
|
|
1795
|
+
const totalTokens = entry.inputTokens + entry.outputTokens + entry.reasoningTokens + entry.cachedTokens + (entry.cacheCreationTokens ?? 0);
|
|
1458
1796
|
const existing = byModel.get(entry.model);
|
|
1459
1797
|
if (existing) {
|
|
1460
1798
|
existing.inputTokens += entry.inputTokens;
|
|
1461
1799
|
existing.outputTokens += entry.outputTokens;
|
|
1462
1800
|
existing.reasoningTokens += entry.reasoningTokens;
|
|
1463
1801
|
existing.cachedTokens += entry.cachedTokens;
|
|
1802
|
+
existing.cacheCreationTokens = (existing.cacheCreationTokens ?? 0) + (entry.cacheCreationTokens ?? 0);
|
|
1464
1803
|
existing.totalTokens += totalTokens;
|
|
1465
1804
|
continue;
|
|
1466
1805
|
}
|
|
@@ -1470,6 +1809,7 @@ function buildSessionUsage2(entries) {
|
|
|
1470
1809
|
outputTokens: entry.outputTokens,
|
|
1471
1810
|
reasoningTokens: entry.reasoningTokens,
|
|
1472
1811
|
cachedTokens: entry.cachedTokens,
|
|
1812
|
+
cacheCreationTokens: entry.cacheCreationTokens ?? 0,
|
|
1473
1813
|
totalTokens
|
|
1474
1814
|
});
|
|
1475
1815
|
}
|
|
@@ -1503,6 +1843,10 @@ function buildSessions(drafts, entries) {
|
|
|
1503
1843
|
(sum, usage) => sum + usage.cachedTokens,
|
|
1504
1844
|
0
|
|
1505
1845
|
);
|
|
1846
|
+
const cacheCreationTokens = modelUsages.reduce(
|
|
1847
|
+
(sum, usage) => sum + (usage.cacheCreationTokens ?? 0),
|
|
1848
|
+
0
|
|
1849
|
+
);
|
|
1506
1850
|
const totalTokens = modelUsages.reduce(
|
|
1507
1851
|
(sum, usage) => sum + usage.totalTokens,
|
|
1508
1852
|
0
|
|
@@ -1516,7 +1860,7 @@ function buildSessions(drafts, entries) {
|
|
|
1516
1860
|
return {
|
|
1517
1861
|
source: TOOL_ID5,
|
|
1518
1862
|
project: draft.project,
|
|
1519
|
-
sessionHash:
|
|
1863
|
+
sessionHash: createHash3("sha256").update(draft.sessionId).digest("hex").slice(0, 16),
|
|
1520
1864
|
hostname: host,
|
|
1521
1865
|
firstMessageAt: draft.firstCallAt.toISOString(),
|
|
1522
1866
|
lastMessageAt: draft.lastCallEndAt.toISOString(),
|
|
@@ -1536,6 +1880,7 @@ function buildSessions(drafts, entries) {
|
|
|
1536
1880
|
outputTokens,
|
|
1537
1881
|
reasoningTokens,
|
|
1538
1882
|
cachedTokens,
|
|
1883
|
+
cacheCreationTokens,
|
|
1539
1884
|
totalTokens,
|
|
1540
1885
|
primaryModel: modelUsages[0]?.model ?? "",
|
|
1541
1886
|
modelUsages
|
|
@@ -1551,7 +1896,7 @@ var MirasimParser = class {
|
|
|
1551
1896
|
this.ownAgents = new Set(options.ownAgents ?? MIRASIM_OWN_AGENTS);
|
|
1552
1897
|
this.tool = {
|
|
1553
1898
|
id: TOOL_ID5,
|
|
1554
|
-
name:
|
|
1899
|
+
name: TOOL_NAME4,
|
|
1555
1900
|
dataDir: this.insightsDirs[0] ?? DEFAULT_INSIGHTS_DIR
|
|
1556
1901
|
};
|
|
1557
1902
|
}
|
|
@@ -1575,12 +1920,13 @@ var MirasimParser = class {
|
|
|
1575
1920
|
0,
|
|
1576
1921
|
toNonNegativeInteger(row.output) - reasoningTokens
|
|
1577
1922
|
);
|
|
1578
|
-
const cachedTokens = toNonNegativeInteger(row.cacheRead)
|
|
1579
|
-
|
|
1923
|
+
const cachedTokens = toNonNegativeInteger(row.cacheRead);
|
|
1924
|
+
const cacheCreationTokens = toNonNegativeInteger(row.cacheWrite);
|
|
1925
|
+
if (inputTokens + outputTokens + reasoningTokens + cachedTokens + cacheCreationTokens === 0) {
|
|
1580
1926
|
continue;
|
|
1581
1927
|
}
|
|
1582
1928
|
const sessionId = getString(row.sessionId);
|
|
1583
|
-
const project =
|
|
1929
|
+
const project = getPathLeaf2(getString(row.workspace));
|
|
1584
1930
|
const callId = getString(row.id) ?? [sessionId ?? "", timestamp.toISOString(), agent, model].join("|");
|
|
1585
1931
|
if (seenCallIds.has(callId)) continue;
|
|
1586
1932
|
seenCallIds.add(callId);
|
|
@@ -1593,7 +1939,8 @@ var MirasimParser = class {
|
|
|
1593
1939
|
inputTokens,
|
|
1594
1940
|
outputTokens,
|
|
1595
1941
|
reasoningTokens,
|
|
1596
|
-
cachedTokens
|
|
1942
|
+
cachedTokens,
|
|
1943
|
+
cacheCreationTokens
|
|
1597
1944
|
});
|
|
1598
1945
|
if (!sessionId) continue;
|
|
1599
1946
|
const durationMs = toNonNegativeInteger(row.durationMs);
|
|
@@ -1626,29 +1973,29 @@ var MirasimParser = class {
|
|
|
1626
1973
|
};
|
|
1627
1974
|
}
|
|
1628
1975
|
isInstalled() {
|
|
1629
|
-
return this.insightsDirs.some((dir) =>
|
|
1976
|
+
return this.insightsDirs.some((dir) => existsSync10(dir));
|
|
1630
1977
|
}
|
|
1631
1978
|
};
|
|
1632
1979
|
registerParser(new MirasimParser());
|
|
1633
1980
|
|
|
1634
1981
|
// src/parsers/copilot-cli.ts
|
|
1635
|
-
import { existsSync as
|
|
1636
|
-
import { homedir as
|
|
1637
|
-
import { basename as
|
|
1638
|
-
var ROOT_DIR =
|
|
1982
|
+
import { existsSync as existsSync11, readdirSync as readdirSync3, readFileSync as readFileSync3 } from "fs";
|
|
1983
|
+
import { homedir as homedir9 } from "os";
|
|
1984
|
+
import { basename as basename4, dirname as dirname2, join as join10 } from "path";
|
|
1985
|
+
var ROOT_DIR = join10(homedir9(), ".copilot");
|
|
1639
1986
|
var TOOL3 = {
|
|
1640
1987
|
id: "copilot-cli",
|
|
1641
1988
|
name: "GitHub Copilot CLI",
|
|
1642
1989
|
dataDir: ROOT_DIR
|
|
1643
1990
|
};
|
|
1644
1991
|
function collectEventFiles(dir, results, visited) {
|
|
1645
|
-
if (!
|
|
1992
|
+
if (!existsSync11(dir) || visited.has(dir)) {
|
|
1646
1993
|
return;
|
|
1647
1994
|
}
|
|
1648
1995
|
visited.add(dir);
|
|
1649
1996
|
try {
|
|
1650
1997
|
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
1651
|
-
const fullPath =
|
|
1998
|
+
const fullPath = join10(dir, entry.name);
|
|
1652
1999
|
if (entry.isDirectory()) {
|
|
1653
2000
|
collectEventFiles(fullPath, results, visited);
|
|
1654
2001
|
continue;
|
|
@@ -1656,7 +2003,7 @@ function collectEventFiles(dir, results, visited) {
|
|
|
1656
2003
|
if (entry.isFile() && entry.name === "events.jsonl") {
|
|
1657
2004
|
results.push({
|
|
1658
2005
|
filePath: fullPath,
|
|
1659
|
-
sessionId:
|
|
2006
|
+
sessionId: basename4(dirname2(fullPath))
|
|
1660
2007
|
});
|
|
1661
2008
|
}
|
|
1662
2009
|
}
|
|
@@ -1671,7 +2018,7 @@ function findEventFiles(baseDir) {
|
|
|
1671
2018
|
function getProjectFromContext(context) {
|
|
1672
2019
|
const projectPath = context?.gitRoot || context?.cwd;
|
|
1673
2020
|
if (!projectPath) return "unknown";
|
|
1674
|
-
return
|
|
2021
|
+
return basename4(projectPath.replace(/[\\/]+$/, "")) || "unknown";
|
|
1675
2022
|
}
|
|
1676
2023
|
var CopilotCliParser = class {
|
|
1677
2024
|
tool = TOOL3;
|
|
@@ -1682,6 +2029,7 @@ var CopilotCliParser = class {
|
|
|
1682
2029
|
}
|
|
1683
2030
|
const entries = [];
|
|
1684
2031
|
const sessionEvents = [];
|
|
2032
|
+
const cumulativeUsage = /* @__PURE__ */ new Map();
|
|
1685
2033
|
for (const { filePath, sessionId } of eventFiles) {
|
|
1686
2034
|
let content;
|
|
1687
2035
|
try {
|
|
@@ -1724,10 +2072,42 @@ var CopilotCliParser = class {
|
|
|
1724
2072
|
for (const [model, metrics] of Object.entries(modelMetrics)) {
|
|
1725
2073
|
const usage = metrics?.usage;
|
|
1726
2074
|
if (!usage) continue;
|
|
1727
|
-
const totalInput = usage.inputTokens
|
|
1728
|
-
const cachedRead = usage.cacheReadTokens
|
|
1729
|
-
const
|
|
1730
|
-
|
|
2075
|
+
const totalInput = usage.inputTokens ?? 0;
|
|
2076
|
+
const cachedRead = usage.cacheReadTokens ?? 0;
|
|
2077
|
+
const cachedWrite = usage.cacheWriteTokens ?? 0;
|
|
2078
|
+
const output = usage.outputTokens ?? 0;
|
|
2079
|
+
if ([totalInput, cachedRead, cachedWrite, output].some(
|
|
2080
|
+
(count) => !Number.isSafeInteger(count) || count < 0
|
|
2081
|
+
)) {
|
|
2082
|
+
continue;
|
|
2083
|
+
}
|
|
2084
|
+
const key = JSON.stringify([sessionId, model]);
|
|
2085
|
+
const previous = cumulativeUsage.get(key) ?? {
|
|
2086
|
+
inputTokens: 0,
|
|
2087
|
+
outputTokens: 0,
|
|
2088
|
+
cachedTokens: 0,
|
|
2089
|
+
cacheCreationTokens: 0
|
|
2090
|
+
};
|
|
2091
|
+
const inputTokens = Math.max(
|
|
2092
|
+
0,
|
|
2093
|
+
totalInput - cachedRead - cachedWrite - previous.inputTokens
|
|
2094
|
+
);
|
|
2095
|
+
const outputTokens = Math.max(0, output - previous.outputTokens);
|
|
2096
|
+
const cachedTokens = Math.max(
|
|
2097
|
+
0,
|
|
2098
|
+
cachedRead - previous.cachedTokens
|
|
2099
|
+
);
|
|
2100
|
+
const cacheCreationTokens = Math.max(
|
|
2101
|
+
0,
|
|
2102
|
+
cachedWrite - previous.cacheCreationTokens
|
|
2103
|
+
);
|
|
2104
|
+
cumulativeUsage.set(key, {
|
|
2105
|
+
inputTokens: previous.inputTokens + inputTokens,
|
|
2106
|
+
outputTokens: previous.outputTokens + outputTokens,
|
|
2107
|
+
cachedTokens: previous.cachedTokens + cachedTokens,
|
|
2108
|
+
cacheCreationTokens: previous.cacheCreationTokens + cacheCreationTokens
|
|
2109
|
+
});
|
|
2110
|
+
if (inputTokens + outputTokens + cachedTokens + cacheCreationTokens === 0) {
|
|
1731
2111
|
continue;
|
|
1732
2112
|
}
|
|
1733
2113
|
entries.push({
|
|
@@ -1736,10 +2116,11 @@ var CopilotCliParser = class {
|
|
|
1736
2116
|
model,
|
|
1737
2117
|
project: currentProject,
|
|
1738
2118
|
timestamp,
|
|
1739
|
-
inputTokens
|
|
1740
|
-
outputTokens
|
|
2119
|
+
inputTokens,
|
|
2120
|
+
outputTokens,
|
|
1741
2121
|
reasoningTokens: 0,
|
|
1742
|
-
cachedTokens
|
|
2122
|
+
cachedTokens,
|
|
2123
|
+
cacheCreationTokens
|
|
1743
2124
|
});
|
|
1744
2125
|
}
|
|
1745
2126
|
} catch {
|
|
@@ -1752,30 +2133,30 @@ var CopilotCliParser = class {
|
|
|
1752
2133
|
};
|
|
1753
2134
|
}
|
|
1754
2135
|
isInstalled() {
|
|
1755
|
-
return
|
|
2136
|
+
return existsSync11(ROOT_DIR);
|
|
1756
2137
|
}
|
|
1757
2138
|
};
|
|
1758
2139
|
registerParser(new CopilotCliParser());
|
|
1759
2140
|
|
|
1760
2141
|
// src/parsers/oh-my-pi.ts
|
|
1761
|
-
import { existsSync as
|
|
1762
|
-
import { homedir as
|
|
1763
|
-
import { join as
|
|
2142
|
+
import { existsSync as existsSync12 } from "fs";
|
|
2143
|
+
import { homedir as homedir10 } from "os";
|
|
2144
|
+
import { join as join11 } from "path";
|
|
1764
2145
|
var TOOL_ID6 = "oh-my-pi";
|
|
1765
|
-
var
|
|
1766
|
-
var DEFAULT_SESSIONS_DIR2 =
|
|
1767
|
-
function
|
|
2146
|
+
var TOOL_NAME5 = "omp";
|
|
2147
|
+
var DEFAULT_SESSIONS_DIR2 = join11(homedir10(), ".omp", "agent", "sessions");
|
|
2148
|
+
function createToolDefinition3(dataDir) {
|
|
1768
2149
|
return {
|
|
1769
2150
|
id: TOOL_ID6,
|
|
1770
|
-
name:
|
|
2151
|
+
name: TOOL_NAME5,
|
|
1771
2152
|
dataDir
|
|
1772
2153
|
};
|
|
1773
2154
|
}
|
|
1774
|
-
function
|
|
2155
|
+
function toSafeNumber4(value) {
|
|
1775
2156
|
const numberValue = Number(value);
|
|
1776
2157
|
return Number.isFinite(numberValue) ? numberValue : 0;
|
|
1777
2158
|
}
|
|
1778
|
-
function
|
|
2159
|
+
function getPathLeaf3(value) {
|
|
1779
2160
|
const normalized = value.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
1780
2161
|
const leaf = normalized.split("/").filter(Boolean).pop();
|
|
1781
2162
|
return leaf || "unknown";
|
|
@@ -1786,7 +2167,7 @@ function normalizeForPrefix2(value) {
|
|
|
1786
2167
|
function getUsageNumber2(usage, ...keys) {
|
|
1787
2168
|
for (const key of keys) {
|
|
1788
2169
|
const value = usage[key];
|
|
1789
|
-
const numberValue =
|
|
2170
|
+
const numberValue = toSafeNumber4(value);
|
|
1790
2171
|
if (numberValue > 0) {
|
|
1791
2172
|
return numberValue;
|
|
1792
2173
|
}
|
|
@@ -1794,7 +2175,7 @@ function getUsageNumber2(usage, ...keys) {
|
|
|
1794
2175
|
return 0;
|
|
1795
2176
|
}
|
|
1796
2177
|
function extractOhMyPiProjectFromCwd(cwd) {
|
|
1797
|
-
return
|
|
2178
|
+
return getPathLeaf3(cwd);
|
|
1798
2179
|
}
|
|
1799
2180
|
function extractOhMyPiProjectFromDir(filePath, sessionsDir = DEFAULT_SESSIONS_DIR2) {
|
|
1800
2181
|
const normalizedFilePath = normalizeForPrefix2(filePath);
|
|
@@ -1811,7 +2192,7 @@ function extractOhMyPiProjectFromDir(filePath, sessionsDir = DEFAULT_SESSIONS_DI
|
|
|
1811
2192
|
try {
|
|
1812
2193
|
const decoded = decodeURIComponent(firstSegment);
|
|
1813
2194
|
if (decoded.includes("/") || decoded.includes("\\")) {
|
|
1814
|
-
return
|
|
2195
|
+
return getPathLeaf3(decoded);
|
|
1815
2196
|
}
|
|
1816
2197
|
} catch {
|
|
1817
2198
|
}
|
|
@@ -1821,7 +2202,7 @@ function extractOhMyPiProjectFromDir(filePath, sessionsDir = DEFAULT_SESSIONS_DI
|
|
|
1821
2202
|
var OhMyPiParser = class {
|
|
1822
2203
|
constructor(sessionsDir = DEFAULT_SESSIONS_DIR2) {
|
|
1823
2204
|
this.sessionsDir = sessionsDir;
|
|
1824
|
-
this.tool =
|
|
2205
|
+
this.tool = createToolDefinition3(sessionsDir);
|
|
1825
2206
|
}
|
|
1826
2207
|
sessionsDir;
|
|
1827
2208
|
tool;
|
|
@@ -1910,29 +2291,45 @@ var OhMyPiParser = class {
|
|
|
1910
2291
|
};
|
|
1911
2292
|
}
|
|
1912
2293
|
isInstalled() {
|
|
1913
|
-
return
|
|
2294
|
+
return existsSync12(this.sessionsDir);
|
|
1914
2295
|
}
|
|
1915
2296
|
};
|
|
1916
2297
|
registerParser(new OhMyPiParser());
|
|
1917
2298
|
|
|
1918
2299
|
// src/parsers/opencode.ts
|
|
1919
2300
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
1920
|
-
import { existsSync as
|
|
1921
|
-
import { homedir as
|
|
1922
|
-
import { basename as
|
|
1923
|
-
var DEFAULT_DATA_DIR2 =
|
|
2301
|
+
import { existsSync as existsSync13, readdirSync as readdirSync4, readFileSync as readFileSync4 } from "fs";
|
|
2302
|
+
import { homedir as homedir11 } from "os";
|
|
2303
|
+
import { basename as basename5, join as join12 } from "path";
|
|
2304
|
+
var DEFAULT_DATA_DIR2 = join12(homedir11(), ".local", "share", "opencode");
|
|
1924
2305
|
var TOOL4 = {
|
|
1925
2306
|
id: "opencode",
|
|
1926
2307
|
name: "OpenCode",
|
|
1927
2308
|
dataDir: DEFAULT_DATA_DIR2
|
|
1928
2309
|
};
|
|
2310
|
+
function hasTokens(tokens) {
|
|
2311
|
+
return [
|
|
2312
|
+
tokens.input,
|
|
2313
|
+
tokens.output,
|
|
2314
|
+
tokens.reasoning,
|
|
2315
|
+
tokens.cache?.read,
|
|
2316
|
+
tokens.cache?.write
|
|
2317
|
+
].some((value) => typeof value === "number" && value !== 0);
|
|
2318
|
+
}
|
|
2319
|
+
function seenMessage(seen, sessionId, messageId) {
|
|
2320
|
+
if (!sessionId || !messageId) return false;
|
|
2321
|
+
const key = JSON.stringify([sessionId, messageId]);
|
|
2322
|
+
if (seen.has(key)) return true;
|
|
2323
|
+
seen.add(key);
|
|
2324
|
+
return false;
|
|
2325
|
+
}
|
|
1929
2326
|
function getOpenCodeDataDirs(env = process.env) {
|
|
1930
2327
|
const dirs = [
|
|
1931
2328
|
env.TOKEN_ARENA_OPENCODE_DIR,
|
|
1932
|
-
env.XDG_DATA_HOME ?
|
|
2329
|
+
env.XDG_DATA_HOME ? join12(env.XDG_DATA_HOME, "opencode") : void 0,
|
|
1933
2330
|
DEFAULT_DATA_DIR2,
|
|
1934
|
-
env.LOCALAPPDATA ?
|
|
1935
|
-
env.APPDATA ?
|
|
2331
|
+
env.LOCALAPPDATA ? join12(env.LOCALAPPDATA, "opencode") : void 0,
|
|
2332
|
+
env.APPDATA ? join12(env.APPDATA, "opencode") : void 0
|
|
1936
2333
|
].filter((value) => Boolean(value));
|
|
1937
2334
|
return Array.from(new Set(dirs));
|
|
1938
2335
|
}
|
|
@@ -2006,16 +2403,21 @@ function readSqliteRowsWithCli2(dbPath, query) {
|
|
|
2006
2403
|
);
|
|
2007
2404
|
}
|
|
2008
2405
|
var OpenCodeParser = class {
|
|
2009
|
-
constructor(resolveRoots = getOpenCodeDataDirs) {
|
|
2406
|
+
constructor(resolveRoots = getOpenCodeDataDirs, queryRows) {
|
|
2010
2407
|
this.resolveRoots = resolveRoots;
|
|
2408
|
+
this.queryRows = queryRows;
|
|
2011
2409
|
}
|
|
2012
2410
|
resolveRoots;
|
|
2411
|
+
queryRows;
|
|
2013
2412
|
tool = TOOL4;
|
|
2014
2413
|
async parse() {
|
|
2015
2414
|
const buckets = [];
|
|
2016
2415
|
const sessions = [];
|
|
2017
|
-
|
|
2018
|
-
|
|
2416
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2417
|
+
let incomplete = false;
|
|
2418
|
+
for (const rootDir of new Set(this.resolveRoots())) {
|
|
2419
|
+
const result = await this.parseRoot(rootDir, seen);
|
|
2420
|
+
incomplete ||= Boolean(result.incomplete);
|
|
2019
2421
|
if (result.buckets.length > 0) {
|
|
2020
2422
|
buckets.push(...result.buckets);
|
|
2021
2423
|
}
|
|
@@ -2023,28 +2425,30 @@ var OpenCodeParser = class {
|
|
|
2023
2425
|
sessions.push(...result.sessions);
|
|
2024
2426
|
}
|
|
2025
2427
|
}
|
|
2026
|
-
return { buckets, sessions };
|
|
2428
|
+
return { buckets, sessions, ...incomplete ? { incomplete: true } : {} };
|
|
2027
2429
|
}
|
|
2028
2430
|
isInstalled() {
|
|
2029
|
-
return this.resolveRoots().some((dir) =>
|
|
2431
|
+
return this.resolveRoots().some((dir) => existsSync13(dir));
|
|
2030
2432
|
}
|
|
2031
|
-
async parseRoot(rootDir) {
|
|
2032
|
-
const dbPath =
|
|
2033
|
-
const messagesDir =
|
|
2034
|
-
if (
|
|
2433
|
+
async parseRoot(rootDir, seen) {
|
|
2434
|
+
const dbPath = join12(rootDir, "opencode.db");
|
|
2435
|
+
const messagesDir = join12(rootDir, "storage", "message");
|
|
2436
|
+
if (existsSync13(dbPath)) {
|
|
2035
2437
|
try {
|
|
2036
|
-
return await this.parseFromSqlite(dbPath);
|
|
2438
|
+
return await this.parseFromSqlite(dbPath, seen);
|
|
2037
2439
|
} catch (err) {
|
|
2038
2440
|
process.stderr.write(
|
|
2039
2441
|
`warn: opencode sqlite parse failed (${err.message}), trying legacy json...
|
|
2040
2442
|
`
|
|
2041
2443
|
);
|
|
2444
|
+
return { ...this.parseFromJson(messagesDir, seen), incomplete: true };
|
|
2042
2445
|
}
|
|
2043
2446
|
}
|
|
2044
|
-
return this.parseFromJson(messagesDir);
|
|
2447
|
+
return this.parseFromJson(messagesDir, seen);
|
|
2045
2448
|
}
|
|
2046
|
-
async parseFromSqlite(dbPath) {
|
|
2449
|
+
async parseFromSqlite(dbPath, seen) {
|
|
2047
2450
|
const query = `SELECT
|
|
2451
|
+
id as messageId,
|
|
2048
2452
|
session_id as sessionID,
|
|
2049
2453
|
json_extract(data, '$.role') as role,
|
|
2050
2454
|
json_extract(data, '$.time.created') as created,
|
|
@@ -2052,7 +2456,7 @@ var OpenCodeParser = class {
|
|
|
2052
2456
|
json_extract(data, '$.tokens') as tokens,
|
|
2053
2457
|
json_extract(data, '$.path.root') as rootPath
|
|
2054
2458
|
FROM message`;
|
|
2055
|
-
const builtinRows = await readSqliteRowsWithBuiltin2(dbPath, query);
|
|
2459
|
+
const builtinRows = this.queryRows ? await this.queryRows(dbPath, query) : await readSqliteRowsWithBuiltin2(dbPath, query);
|
|
2056
2460
|
const rows = builtinRows ?? readSqliteRowsWithCli2(dbPath, query);
|
|
2057
2461
|
if (rows.length === 0) {
|
|
2058
2462
|
return { buckets: [], sessions: [] };
|
|
@@ -2062,9 +2466,10 @@ var OpenCodeParser = class {
|
|
|
2062
2466
|
for (const row of rows) {
|
|
2063
2467
|
const timestamp = new Date(row.created);
|
|
2064
2468
|
if (Number.isNaN(timestamp.getTime())) continue;
|
|
2065
|
-
const project = row.rootPath ?
|
|
2469
|
+
const project = row.rootPath ? basename5(row.rootPath) : "unknown";
|
|
2066
2470
|
const sessionId = row.sessionID || "unknown";
|
|
2067
2471
|
if (row.role !== "user" && row.role !== "assistant") continue;
|
|
2472
|
+
if (seenMessage(seen, row.sessionID, row.messageId)) continue;
|
|
2068
2473
|
sessionEvents.push({
|
|
2069
2474
|
sessionId,
|
|
2070
2475
|
source: "opencode",
|
|
@@ -2072,14 +2477,14 @@ var OpenCodeParser = class {
|
|
|
2072
2477
|
timestamp,
|
|
2073
2478
|
role: row.role === "user" ? "user" : "assistant"
|
|
2074
2479
|
});
|
|
2075
|
-
if (
|
|
2480
|
+
if (row.role !== "assistant") continue;
|
|
2076
2481
|
let tokens;
|
|
2077
2482
|
try {
|
|
2078
2483
|
tokens = typeof row.tokens === "string" ? JSON.parse(row.tokens) : row.tokens;
|
|
2079
2484
|
} catch {
|
|
2080
2485
|
continue;
|
|
2081
2486
|
}
|
|
2082
|
-
if (!tokens || !tokens
|
|
2487
|
+
if (!tokens || !hasTokens(tokens)) continue;
|
|
2083
2488
|
entries.push({
|
|
2084
2489
|
sessionId,
|
|
2085
2490
|
source: "opencode",
|
|
@@ -2089,7 +2494,8 @@ var OpenCodeParser = class {
|
|
|
2089
2494
|
inputTokens: tokens.input || 0,
|
|
2090
2495
|
outputTokens: tokens.output || 0,
|
|
2091
2496
|
reasoningTokens: tokens.reasoning || 0,
|
|
2092
|
-
cachedTokens: tokens.cache?.read || 0
|
|
2497
|
+
cachedTokens: tokens.cache?.read || 0,
|
|
2498
|
+
cacheCreationTokens: tokens.cache?.write || 0
|
|
2093
2499
|
});
|
|
2094
2500
|
}
|
|
2095
2501
|
return {
|
|
@@ -2097,8 +2503,8 @@ var OpenCodeParser = class {
|
|
|
2097
2503
|
sessions: extractSessions(sessionEvents, entries)
|
|
2098
2504
|
};
|
|
2099
2505
|
}
|
|
2100
|
-
parseFromJson(messagesDir) {
|
|
2101
|
-
if (!
|
|
2506
|
+
parseFromJson(messagesDir, seen) {
|
|
2507
|
+
if (!existsSync13(messagesDir)) return { buckets: [], sessions: [] };
|
|
2102
2508
|
const entries = [];
|
|
2103
2509
|
const sessionEvents = [];
|
|
2104
2510
|
let sessionDirs;
|
|
@@ -2110,7 +2516,7 @@ var OpenCodeParser = class {
|
|
|
2110
2516
|
return { buckets: [], sessions: [] };
|
|
2111
2517
|
}
|
|
2112
2518
|
for (const sessionDir of sessionDirs) {
|
|
2113
|
-
const sessionPath =
|
|
2519
|
+
const sessionPath = join12(messagesDir, sessionDir.name);
|
|
2114
2520
|
let messageFiles;
|
|
2115
2521
|
try {
|
|
2116
2522
|
messageFiles = readdirSync4(sessionPath).filter(
|
|
@@ -2120,7 +2526,7 @@ var OpenCodeParser = class {
|
|
|
2120
2526
|
continue;
|
|
2121
2527
|
}
|
|
2122
2528
|
for (const file of messageFiles) {
|
|
2123
|
-
const filePath =
|
|
2529
|
+
const filePath = join12(sessionPath, file);
|
|
2124
2530
|
let data;
|
|
2125
2531
|
try {
|
|
2126
2532
|
data = JSON.parse(readFileSync4(filePath, "utf-8"));
|
|
@@ -2132,8 +2538,10 @@ var OpenCodeParser = class {
|
|
|
2132
2538
|
const timestamp = new Date(created);
|
|
2133
2539
|
if (Number.isNaN(timestamp.getTime())) continue;
|
|
2134
2540
|
const rootPath = data.path?.root;
|
|
2135
|
-
const project = rootPath ?
|
|
2541
|
+
const project = rootPath ? basename5(rootPath) : "unknown";
|
|
2136
2542
|
if (data.role !== "user" && data.role !== "assistant") continue;
|
|
2543
|
+
if (seenMessage(seen, data.sessionID || sessionDir.name, data.id))
|
|
2544
|
+
continue;
|
|
2137
2545
|
sessionEvents.push({
|
|
2138
2546
|
sessionId: sessionDir.name,
|
|
2139
2547
|
source: "opencode",
|
|
@@ -2141,9 +2549,9 @@ var OpenCodeParser = class {
|
|
|
2141
2549
|
timestamp,
|
|
2142
2550
|
role: data.role === "user" ? "user" : "assistant"
|
|
2143
2551
|
});
|
|
2144
|
-
if (
|
|
2552
|
+
if (data.role !== "assistant") continue;
|
|
2145
2553
|
const tokens = data.tokens;
|
|
2146
|
-
if (!tokens || !tokens
|
|
2554
|
+
if (!tokens || !hasTokens(tokens)) continue;
|
|
2147
2555
|
entries.push({
|
|
2148
2556
|
sessionId: sessionDir.name,
|
|
2149
2557
|
source: "opencode",
|
|
@@ -2153,7 +2561,8 @@ var OpenCodeParser = class {
|
|
|
2153
2561
|
inputTokens: tokens.input || 0,
|
|
2154
2562
|
outputTokens: tokens.output || 0,
|
|
2155
2563
|
reasoningTokens: tokens.reasoning || 0,
|
|
2156
|
-
cachedTokens: tokens.cache?.read || 0
|
|
2564
|
+
cachedTokens: tokens.cache?.read || 0,
|
|
2565
|
+
cacheCreationTokens: tokens.cache?.write || 0
|
|
2157
2566
|
});
|
|
2158
2567
|
}
|
|
2159
2568
|
}
|
|
@@ -2166,16 +2575,16 @@ var OpenCodeParser = class {
|
|
|
2166
2575
|
registerParser(new OpenCodeParser());
|
|
2167
2576
|
|
|
2168
2577
|
// src/parsers/openclaw.ts
|
|
2169
|
-
import { existsSync as
|
|
2170
|
-
import { homedir as
|
|
2171
|
-
import { join as
|
|
2578
|
+
import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as readFileSync5 } from "fs";
|
|
2579
|
+
import { homedir as homedir12 } from "os";
|
|
2580
|
+
import { join as join13 } from "path";
|
|
2172
2581
|
var TOOL_ID7 = "openclaw";
|
|
2173
|
-
var
|
|
2174
|
-
var DEFAULT_DATA_DIR3 =
|
|
2582
|
+
var TOOL_NAME6 = "OpenClaw";
|
|
2583
|
+
var DEFAULT_DATA_DIR3 = join13(homedir12(), ".openclaw");
|
|
2175
2584
|
var LEGACY_ROOT_NAMES = [".clawdbot", ".moltbot", ".moldbot"];
|
|
2176
2585
|
var TOOL5 = {
|
|
2177
2586
|
id: TOOL_ID7,
|
|
2178
|
-
name:
|
|
2587
|
+
name: TOOL_NAME6,
|
|
2179
2588
|
dataDir: DEFAULT_DATA_DIR3
|
|
2180
2589
|
};
|
|
2181
2590
|
function getTokens(usage, ...keys) {
|
|
@@ -2185,16 +2594,16 @@ function getTokens(usage, ...keys) {
|
|
|
2185
2594
|
}
|
|
2186
2595
|
return 0;
|
|
2187
2596
|
}
|
|
2188
|
-
function getOpenClawRoots(homeDir =
|
|
2189
|
-
const roots = [
|
|
2597
|
+
function getOpenClawRoots(homeDir = homedir12()) {
|
|
2598
|
+
const roots = [join13(homeDir, ".openclaw")];
|
|
2190
2599
|
try {
|
|
2191
2600
|
const profileRoots = readdirSync5(homeDir, { withFileTypes: true }).filter(
|
|
2192
2601
|
(entry) => entry.isDirectory() && /^\.openclaw-.+/.test(entry.name)
|
|
2193
|
-
).map((entry) =>
|
|
2602
|
+
).map((entry) => join13(homeDir, entry.name)).sort((left, right) => left.localeCompare(right));
|
|
2194
2603
|
roots.push(...profileRoots);
|
|
2195
2604
|
} catch {
|
|
2196
2605
|
}
|
|
2197
|
-
roots.push(...LEGACY_ROOT_NAMES.map((name) =>
|
|
2606
|
+
roots.push(...LEGACY_ROOT_NAMES.map((name) => join13(homeDir, name)));
|
|
2198
2607
|
return Array.from(new Set(roots));
|
|
2199
2608
|
}
|
|
2200
2609
|
var OpenClawParser = class {
|
|
@@ -2207,8 +2616,8 @@ var OpenClawParser = class {
|
|
|
2207
2616
|
const entries = [];
|
|
2208
2617
|
const sessionEvents = [];
|
|
2209
2618
|
for (const root of this.resolveRoots()) {
|
|
2210
|
-
const agentsDir =
|
|
2211
|
-
if (!
|
|
2619
|
+
const agentsDir = join13(root, "agents");
|
|
2620
|
+
if (!existsSync14(agentsDir)) continue;
|
|
2212
2621
|
let agentDirs;
|
|
2213
2622
|
try {
|
|
2214
2623
|
agentDirs = readdirSync5(agentsDir, { withFileTypes: true }).filter(
|
|
@@ -2219,8 +2628,8 @@ var OpenClawParser = class {
|
|
|
2219
2628
|
}
|
|
2220
2629
|
for (const agentDir of agentDirs) {
|
|
2221
2630
|
const project = agentDir.name;
|
|
2222
|
-
const sessionsDir =
|
|
2223
|
-
if (!
|
|
2631
|
+
const sessionsDir = join13(agentsDir, agentDir.name, "sessions");
|
|
2632
|
+
if (!existsSync14(sessionsDir)) continue;
|
|
2224
2633
|
let files;
|
|
2225
2634
|
try {
|
|
2226
2635
|
files = readdirSync5(sessionsDir).filter((f) => f.endsWith(".jsonl"));
|
|
@@ -2228,7 +2637,7 @@ var OpenClawParser = class {
|
|
|
2228
2637
|
continue;
|
|
2229
2638
|
}
|
|
2230
2639
|
for (const file of files) {
|
|
2231
|
-
const filePath =
|
|
2640
|
+
const filePath = join13(sessionsDir, file);
|
|
2232
2641
|
let content;
|
|
2233
2642
|
try {
|
|
2234
2643
|
content = readFileSync5(filePath, "utf-8");
|
|
@@ -2301,30 +2710,30 @@ var OpenClawParser = class {
|
|
|
2301
2710
|
};
|
|
2302
2711
|
}
|
|
2303
2712
|
isInstalled() {
|
|
2304
|
-
return this.resolveRoots().some((root) =>
|
|
2713
|
+
return this.resolveRoots().some((root) => existsSync14(join13(root, "agents")));
|
|
2305
2714
|
}
|
|
2306
2715
|
};
|
|
2307
2716
|
registerParser(new OpenClawParser());
|
|
2308
2717
|
|
|
2309
2718
|
// src/parsers/qwen-code.ts
|
|
2310
|
-
import { existsSync as
|
|
2311
|
-
import { homedir as
|
|
2312
|
-
import { join as
|
|
2719
|
+
import { existsSync as existsSync15, readdirSync as readdirSync6 } from "fs";
|
|
2720
|
+
import { homedir as homedir13 } from "os";
|
|
2721
|
+
import { join as join14 } from "path";
|
|
2313
2722
|
var TOOL_ID8 = "qwen-code";
|
|
2314
|
-
var
|
|
2315
|
-
var DEFAULT_DATA_DIR4 =
|
|
2316
|
-
function
|
|
2723
|
+
var TOOL_NAME7 = "Qwen Code";
|
|
2724
|
+
var DEFAULT_DATA_DIR4 = join14(homedir13(), ".qwen", "tmp");
|
|
2725
|
+
function createToolDefinition4(dataDir) {
|
|
2317
2726
|
return {
|
|
2318
2727
|
id: TOOL_ID8,
|
|
2319
|
-
name:
|
|
2728
|
+
name: TOOL_NAME7,
|
|
2320
2729
|
dataDir
|
|
2321
2730
|
};
|
|
2322
2731
|
}
|
|
2323
|
-
function
|
|
2732
|
+
function toSafeNumber5(value) {
|
|
2324
2733
|
const numberValue = Number(value);
|
|
2325
2734
|
return Number.isFinite(numberValue) ? numberValue : 0;
|
|
2326
2735
|
}
|
|
2327
|
-
function
|
|
2736
|
+
function getPathLeaf4(value) {
|
|
2328
2737
|
const normalized = value.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
2329
2738
|
const leaf = normalized.split("/").filter(Boolean).pop();
|
|
2330
2739
|
return leaf || "unknown";
|
|
@@ -2334,16 +2743,16 @@ function normalizeForPrefix3(value) {
|
|
|
2334
2743
|
}
|
|
2335
2744
|
function findSessionFiles2(baseDir) {
|
|
2336
2745
|
const results = [];
|
|
2337
|
-
if (!
|
|
2746
|
+
if (!existsSync15(baseDir)) return results;
|
|
2338
2747
|
try {
|
|
2339
2748
|
for (const entry of readdirSync6(baseDir, { withFileTypes: true })) {
|
|
2340
2749
|
if (!entry.isDirectory()) continue;
|
|
2341
|
-
const chatsDir =
|
|
2342
|
-
if (!
|
|
2750
|
+
const chatsDir = join14(baseDir, entry.name, "chats");
|
|
2751
|
+
if (!existsSync15(chatsDir)) continue;
|
|
2343
2752
|
try {
|
|
2344
2753
|
for (const file of readdirSync6(chatsDir)) {
|
|
2345
2754
|
if (file.endsWith(".jsonl")) {
|
|
2346
|
-
results.push(
|
|
2755
|
+
results.push(join14(chatsDir, file));
|
|
2347
2756
|
}
|
|
2348
2757
|
}
|
|
2349
2758
|
} catch {
|
|
@@ -2356,7 +2765,7 @@ function findSessionFiles2(baseDir) {
|
|
|
2356
2765
|
}
|
|
2357
2766
|
function resolveQwenProject(cwd, filePath, dataDir = DEFAULT_DATA_DIR4) {
|
|
2358
2767
|
if (cwd) {
|
|
2359
|
-
return
|
|
2768
|
+
return getPathLeaf4(cwd);
|
|
2360
2769
|
}
|
|
2361
2770
|
const normalizedFilePath = normalizeForPrefix3(filePath);
|
|
2362
2771
|
const normalizedDataDir = normalizeForPrefix3(dataDir);
|
|
@@ -2371,7 +2780,7 @@ function resolveQwenProject(cwd, filePath, dataDir = DEFAULT_DATA_DIR4) {
|
|
|
2371
2780
|
var QwenCodeParser = class {
|
|
2372
2781
|
constructor(dataDir = DEFAULT_DATA_DIR4) {
|
|
2373
2782
|
this.dataDir = dataDir;
|
|
2374
|
-
this.tool =
|
|
2783
|
+
this.tool = createToolDefinition4(dataDir);
|
|
2375
2784
|
}
|
|
2376
2785
|
dataDir;
|
|
2377
2786
|
tool;
|
|
@@ -2407,10 +2816,10 @@ var QwenCodeParser = class {
|
|
|
2407
2816
|
if (obj.type !== "assistant") continue;
|
|
2408
2817
|
const usage = obj.usageMetadata || obj.usage;
|
|
2409
2818
|
if (!usage) continue;
|
|
2410
|
-
const totalInput =
|
|
2411
|
-
const totalOutput =
|
|
2412
|
-
const cachedTokens =
|
|
2413
|
-
const reasoningTokens =
|
|
2819
|
+
const totalInput = toSafeNumber5(usage.promptTokenCount) || toSafeNumber5(usage.input_tokens);
|
|
2820
|
+
const totalOutput = toSafeNumber5(usage.candidatesTokenCount) || toSafeNumber5(usage.output_tokens);
|
|
2821
|
+
const cachedTokens = toSafeNumber5(usage.cachedContentTokenCount);
|
|
2822
|
+
const reasoningTokens = toSafeNumber5(usage.thoughtsTokenCount);
|
|
2414
2823
|
if (totalInput === 0 && totalOutput === 0 && cachedTokens === 0 && reasoningTokens === 0) {
|
|
2415
2824
|
continue;
|
|
2416
2825
|
}
|
|
@@ -2439,19 +2848,19 @@ var QwenCodeParser = class {
|
|
|
2439
2848
|
};
|
|
2440
2849
|
}
|
|
2441
2850
|
isInstalled() {
|
|
2442
|
-
return
|
|
2851
|
+
return existsSync15(this.dataDir);
|
|
2443
2852
|
}
|
|
2444
2853
|
};
|
|
2445
2854
|
registerParser(new QwenCodeParser());
|
|
2446
2855
|
|
|
2447
2856
|
// src/parsers/kimi-code.ts
|
|
2448
|
-
import { existsSync as
|
|
2449
|
-
import { homedir as
|
|
2450
|
-
import { join as
|
|
2857
|
+
import { existsSync as existsSync16, readdirSync as readdirSync7 } from "fs";
|
|
2858
|
+
import { homedir as homedir14 } from "os";
|
|
2859
|
+
import { join as join15 } from "path";
|
|
2451
2860
|
var TOOL_ID9 = "kimi-code";
|
|
2452
|
-
var
|
|
2453
|
-
var DEFAULT_SESSIONS_DIR3 =
|
|
2454
|
-
var DEFAULT_CONFIG_PATH =
|
|
2861
|
+
var TOOL_NAME8 = "Kimi Code";
|
|
2862
|
+
var DEFAULT_SESSIONS_DIR3 = join15(homedir14(), ".kimi-code", "sessions");
|
|
2863
|
+
var DEFAULT_CONFIG_PATH = join15(homedir14(), ".kimi-code", "workspaces.json");
|
|
2455
2864
|
var USER_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
2456
2865
|
"UserMessage",
|
|
2457
2866
|
"user_message",
|
|
@@ -2465,18 +2874,18 @@ var ASSISTANT_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
2465
2874
|
"ModelOutput",
|
|
2466
2875
|
"AssistantOutput"
|
|
2467
2876
|
]);
|
|
2468
|
-
function
|
|
2877
|
+
function createToolDefinition5(dataDir) {
|
|
2469
2878
|
return {
|
|
2470
2879
|
id: TOOL_ID9,
|
|
2471
|
-
name:
|
|
2880
|
+
name: TOOL_NAME8,
|
|
2472
2881
|
dataDir
|
|
2473
2882
|
};
|
|
2474
2883
|
}
|
|
2475
|
-
function
|
|
2884
|
+
function toSafeNumber6(value) {
|
|
2476
2885
|
const numberValue = Number(value);
|
|
2477
2886
|
return Number.isFinite(numberValue) ? numberValue : 0;
|
|
2478
2887
|
}
|
|
2479
|
-
function
|
|
2888
|
+
function getPathLeaf5(value) {
|
|
2480
2889
|
const normalized = value.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
2481
2890
|
const leaf = normalized.split("/").filter(Boolean).pop();
|
|
2482
2891
|
return leaf || "unknown";
|
|
@@ -2486,29 +2895,29 @@ function isModelPlaceholder(value) {
|
|
|
2486
2895
|
}
|
|
2487
2896
|
function findWireFiles(baseDir) {
|
|
2488
2897
|
const results = [];
|
|
2489
|
-
if (!
|
|
2898
|
+
if (!existsSync16(baseDir)) return results;
|
|
2490
2899
|
try {
|
|
2491
2900
|
for (const workDir of readdirSync7(baseDir, { withFileTypes: true })) {
|
|
2492
2901
|
if (!workDir.isDirectory()) continue;
|
|
2493
|
-
const workDirPath =
|
|
2902
|
+
const workDirPath = join15(baseDir, workDir.name);
|
|
2494
2903
|
try {
|
|
2495
2904
|
for (const session of readdirSync7(workDirPath, {
|
|
2496
2905
|
withFileTypes: true
|
|
2497
2906
|
})) {
|
|
2498
2907
|
if (!session.isDirectory()) continue;
|
|
2499
|
-
const newWireFile =
|
|
2908
|
+
const newWireFile = join15(
|
|
2500
2909
|
workDirPath,
|
|
2501
2910
|
session.name,
|
|
2502
2911
|
"agents",
|
|
2503
2912
|
"main",
|
|
2504
2913
|
"wire.jsonl"
|
|
2505
2914
|
);
|
|
2506
|
-
if (
|
|
2915
|
+
if (existsSync16(newWireFile)) {
|
|
2507
2916
|
results.push({ filePath: newWireFile, workDirHash: workDir.name });
|
|
2508
2917
|
continue;
|
|
2509
2918
|
}
|
|
2510
|
-
const legacyWireFile =
|
|
2511
|
-
if (
|
|
2919
|
+
const legacyWireFile = join15(workDirPath, session.name, "wire.jsonl");
|
|
2920
|
+
if (existsSync16(legacyWireFile)) {
|
|
2512
2921
|
results.push({
|
|
2513
2922
|
filePath: legacyWireFile,
|
|
2514
2923
|
workDirHash: workDir.name
|
|
@@ -2558,7 +2967,7 @@ function loadProjectMap(configPath) {
|
|
|
2558
2967
|
pathValue = info.root || info.path || info.dir || void 0;
|
|
2559
2968
|
}
|
|
2560
2969
|
if (!pathValue) continue;
|
|
2561
|
-
const name = typeof info === "object" && info.name ? info.name :
|
|
2970
|
+
const name = typeof info === "object" && info.name ? info.name : getPathLeaf5(pathValue);
|
|
2562
2971
|
projectMap.set(hash, name);
|
|
2563
2972
|
}
|
|
2564
2973
|
} catch {
|
|
@@ -2572,7 +2981,7 @@ var KimiCodeParser = class {
|
|
|
2572
2981
|
constructor(options = {}) {
|
|
2573
2982
|
this.sessionsDir = options.sessionsDir || DEFAULT_SESSIONS_DIR3;
|
|
2574
2983
|
this.configPath = options.configPath || DEFAULT_CONFIG_PATH;
|
|
2575
|
-
this.tool =
|
|
2984
|
+
this.tool = createToolDefinition5(this.sessionsDir);
|
|
2576
2985
|
}
|
|
2577
2986
|
async parse() {
|
|
2578
2987
|
const wireFiles = findWireFiles(this.sessionsDir);
|
|
@@ -2610,14 +3019,17 @@ var KimiCodeParser = class {
|
|
|
2610
3019
|
if (!timestamp2 || Number.isNaN(timestamp2.getTime())) continue;
|
|
2611
3020
|
const usage = obj.usage;
|
|
2612
3021
|
if (!usage) continue;
|
|
2613
|
-
const inputTokens2 =
|
|
3022
|
+
const inputTokens2 = toSafeNumber6(
|
|
2614
3023
|
usage.input_other ?? usage.inputOther
|
|
2615
3024
|
);
|
|
2616
|
-
const outputTokens2 =
|
|
2617
|
-
const cachedTokens2 =
|
|
3025
|
+
const outputTokens2 = toSafeNumber6(usage.output);
|
|
3026
|
+
const cachedTokens2 = toSafeNumber6(
|
|
2618
3027
|
usage.input_cache_read ?? usage.inputCacheRead
|
|
2619
3028
|
);
|
|
2620
|
-
|
|
3029
|
+
const cacheCreationTokens2 = toSafeNumber6(
|
|
3030
|
+
usage.input_cache_creation ?? usage.inputCacheCreation
|
|
3031
|
+
);
|
|
3032
|
+
if (inputTokens2 === 0 && outputTokens2 === 0 && cachedTokens2 === 0 && cacheCreationTokens2 === 0) {
|
|
2621
3033
|
continue;
|
|
2622
3034
|
}
|
|
2623
3035
|
sessionEvents.push({
|
|
@@ -2637,7 +3049,8 @@ var KimiCodeParser = class {
|
|
|
2637
3049
|
inputTokens: inputTokens2,
|
|
2638
3050
|
outputTokens: outputTokens2,
|
|
2639
3051
|
reasoningTokens: 0,
|
|
2640
|
-
cachedTokens: cachedTokens2
|
|
3052
|
+
cachedTokens: cachedTokens2,
|
|
3053
|
+
cacheCreationTokens: cacheCreationTokens2
|
|
2641
3054
|
});
|
|
2642
3055
|
continue;
|
|
2643
3056
|
}
|
|
@@ -2666,11 +3079,13 @@ var KimiCodeParser = class {
|
|
|
2666
3079
|
if (obj.type !== "StatusUpdate") continue;
|
|
2667
3080
|
const tokenUsage = payload.token_usage;
|
|
2668
3081
|
if (!tokenUsage || !timestamp) continue;
|
|
2669
|
-
const inputTokens =
|
|
2670
|
-
const outputTokens =
|
|
2671
|
-
const cachedTokens =
|
|
2672
|
-
const
|
|
2673
|
-
|
|
3082
|
+
const inputTokens = toSafeNumber6(tokenUsage.input_other);
|
|
3083
|
+
const outputTokens = toSafeNumber6(tokenUsage.output);
|
|
3084
|
+
const cachedTokens = toSafeNumber6(tokenUsage.input_cache_read);
|
|
3085
|
+
const cacheCreationTokens = toSafeNumber6(
|
|
3086
|
+
tokenUsage.input_cache_creation
|
|
3087
|
+
);
|
|
3088
|
+
if (inputTokens === 0 && outputTokens === 0 && cachedTokens === 0 && cacheCreationTokens === 0) {
|
|
2674
3089
|
continue;
|
|
2675
3090
|
}
|
|
2676
3091
|
if (payload.message_id) {
|
|
@@ -2695,7 +3110,8 @@ var KimiCodeParser = class {
|
|
|
2695
3110
|
inputTokens,
|
|
2696
3111
|
outputTokens,
|
|
2697
3112
|
reasoningTokens: 0,
|
|
2698
|
-
cachedTokens
|
|
3113
|
+
cachedTokens,
|
|
3114
|
+
cacheCreationTokens
|
|
2699
3115
|
});
|
|
2700
3116
|
}
|
|
2701
3117
|
}
|
|
@@ -2705,23 +3121,23 @@ var KimiCodeParser = class {
|
|
|
2705
3121
|
};
|
|
2706
3122
|
}
|
|
2707
3123
|
isInstalled() {
|
|
2708
|
-
return
|
|
3124
|
+
return existsSync16(this.sessionsDir);
|
|
2709
3125
|
}
|
|
2710
3126
|
};
|
|
2711
3127
|
registerParser(new KimiCodeParser());
|
|
2712
3128
|
|
|
2713
3129
|
// src/parsers/letcode.ts
|
|
2714
|
-
import { existsSync as
|
|
2715
|
-
import { homedir as
|
|
2716
|
-
import { basename as
|
|
3130
|
+
import { existsSync as existsSync17 } from "fs";
|
|
3131
|
+
import { homedir as homedir15 } from "os";
|
|
3132
|
+
import { basename as basename6, join as join16 } from "path";
|
|
2717
3133
|
var TOOL_ID10 = "letcode";
|
|
2718
|
-
var
|
|
2719
|
-
var DEFAULT_CONFIG_DIR =
|
|
2720
|
-
var DEFAULT_SESSIONS_DIR4 =
|
|
3134
|
+
var TOOL_NAME9 = "LetCode";
|
|
3135
|
+
var DEFAULT_CONFIG_DIR = join16(homedir15(), ".config", "letcode");
|
|
3136
|
+
var DEFAULT_SESSIONS_DIR4 = join16(DEFAULT_CONFIG_DIR, "sessions");
|
|
2721
3137
|
function getLetcodeSessionsDirs(env = process.env) {
|
|
2722
3138
|
const dirs = [
|
|
2723
3139
|
env.TOKEN_ARENA_LETCODE_DIR,
|
|
2724
|
-
env.XDG_CONFIG_HOME ?
|
|
3140
|
+
env.XDG_CONFIG_HOME ? join16(env.XDG_CONFIG_HOME, "letcode", "sessions") : void 0,
|
|
2725
3141
|
DEFAULT_SESSIONS_DIR4
|
|
2726
3142
|
].filter((value) => Boolean(value));
|
|
2727
3143
|
return Array.from(new Set(dirs));
|
|
@@ -2776,7 +3192,7 @@ var LetcodeParser = class {
|
|
|
2776
3192
|
this.sessionsDirs = sessionsDir ? [sessionsDir] : getLetcodeSessionsDirs();
|
|
2777
3193
|
this.tool = {
|
|
2778
3194
|
id: TOOL_ID10,
|
|
2779
|
-
name:
|
|
3195
|
+
name: TOOL_NAME9,
|
|
2780
3196
|
dataDir: this.sessionsDirs[0] ?? DEFAULT_SESSIONS_DIR4
|
|
2781
3197
|
};
|
|
2782
3198
|
}
|
|
@@ -2790,7 +3206,7 @@ var LetcodeParser = class {
|
|
|
2790
3206
|
if (!content) continue;
|
|
2791
3207
|
const rows = parseJsonl(content);
|
|
2792
3208
|
if (rows.length === 0) continue;
|
|
2793
|
-
const fallbackSessionId = extractSessionId(filePath) ||
|
|
3209
|
+
const fallbackSessionId = extractSessionId(filePath) || basename6(filePath);
|
|
2794
3210
|
for (const row of rows) {
|
|
2795
3211
|
const sessionId = typeof row.session_id === "string" && row.session_id ? row.session_id : fallbackSessionId;
|
|
2796
3212
|
const timestamp = parseTimestamp2(row.timestamp_ms);
|
|
@@ -2851,31 +3267,31 @@ var LetcodeParser = class {
|
|
|
2851
3267
|
};
|
|
2852
3268
|
}
|
|
2853
3269
|
isInstalled() {
|
|
2854
|
-
return this.sessionsDirs.some((dir) =>
|
|
3270
|
+
return this.sessionsDirs.some((dir) => existsSync17(dir));
|
|
2855
3271
|
}
|
|
2856
3272
|
};
|
|
2857
3273
|
registerParser(new LetcodeParser());
|
|
2858
3274
|
|
|
2859
3275
|
// src/parsers/droid.ts
|
|
2860
|
-
import { existsSync as
|
|
2861
|
-
import { homedir as
|
|
2862
|
-
import { basename as
|
|
3276
|
+
import { existsSync as existsSync18, readdirSync as readdirSync8 } from "fs";
|
|
3277
|
+
import { homedir as homedir16 } from "os";
|
|
3278
|
+
import { basename as basename7, dirname as dirname3, join as join17 } from "path";
|
|
2863
3279
|
var TOOL_ID11 = "droid";
|
|
2864
|
-
var
|
|
2865
|
-
var DEFAULT_DATA_DIR5 =
|
|
2866
|
-
function
|
|
3280
|
+
var TOOL_NAME10 = "Droid";
|
|
3281
|
+
var DEFAULT_DATA_DIR5 = join17(homedir16(), ".factory", "sessions");
|
|
3282
|
+
function createToolDefinition6(dataDir) {
|
|
2867
3283
|
return {
|
|
2868
3284
|
id: TOOL_ID11,
|
|
2869
|
-
name:
|
|
3285
|
+
name: TOOL_NAME10,
|
|
2870
3286
|
dataDir
|
|
2871
3287
|
};
|
|
2872
3288
|
}
|
|
2873
3289
|
function findSessionFiles3(dir) {
|
|
2874
3290
|
const results = [];
|
|
2875
|
-
if (!
|
|
3291
|
+
if (!existsSync18(dir)) return results;
|
|
2876
3292
|
try {
|
|
2877
3293
|
for (const entry of readdirSync8(dir, { withFileTypes: true })) {
|
|
2878
|
-
const fullPath =
|
|
3294
|
+
const fullPath = join17(dir, entry.name);
|
|
2879
3295
|
if (entry.isDirectory()) {
|
|
2880
3296
|
results.push(...findSessionFiles3(fullPath));
|
|
2881
3297
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl") && !entry.name.endsWith(".settings.json")) {
|
|
@@ -2890,14 +3306,14 @@ function extractDroidProject(slug) {
|
|
|
2890
3306
|
const parts = slug.split("-").filter(Boolean);
|
|
2891
3307
|
return parts.length > 0 ? parts[parts.length - 1] : "unknown";
|
|
2892
3308
|
}
|
|
2893
|
-
function
|
|
3309
|
+
function toSafeNumber7(value) {
|
|
2894
3310
|
const numberValue = Number(value);
|
|
2895
3311
|
return Number.isFinite(numberValue) ? numberValue : 0;
|
|
2896
3312
|
}
|
|
2897
3313
|
var DroidParser = class {
|
|
2898
3314
|
constructor(dataDir = DEFAULT_DATA_DIR5) {
|
|
2899
3315
|
this.dataDir = dataDir;
|
|
2900
|
-
this.tool =
|
|
3316
|
+
this.tool = createToolDefinition6(dataDir);
|
|
2901
3317
|
}
|
|
2902
3318
|
dataDir;
|
|
2903
3319
|
tool;
|
|
@@ -2910,7 +3326,7 @@ var DroidParser = class {
|
|
|
2910
3326
|
const sessionEvents = [];
|
|
2911
3327
|
for (const filePath of sessionFiles) {
|
|
2912
3328
|
const sessionId = filePath;
|
|
2913
|
-
const project = extractDroidProject(
|
|
3329
|
+
const project = extractDroidProject(basename7(dirname3(filePath)));
|
|
2914
3330
|
let firstMessageTimestamp = null;
|
|
2915
3331
|
const content = readFileSafe(filePath);
|
|
2916
3332
|
if (!content) continue;
|
|
@@ -2938,9 +3354,9 @@ var DroidParser = class {
|
|
|
2938
3354
|
role
|
|
2939
3355
|
});
|
|
2940
3356
|
}
|
|
2941
|
-
const settingsPath =
|
|
3357
|
+
const settingsPath = join17(
|
|
2942
3358
|
dirname3(filePath),
|
|
2943
|
-
`${
|
|
3359
|
+
`${basename7(filePath, ".jsonl")}.settings.json`
|
|
2944
3360
|
);
|
|
2945
3361
|
const settingsContent = readFileSafe(settingsPath);
|
|
2946
3362
|
if (!settingsContent || firstMessageTimestamp === null) continue;
|
|
@@ -2952,15 +3368,15 @@ var DroidParser = class {
|
|
|
2952
3368
|
}
|
|
2953
3369
|
const tokenUsage = settings.tokenUsage;
|
|
2954
3370
|
if (!tokenUsage) continue;
|
|
2955
|
-
const cachedTokens =
|
|
2956
|
-
const reasoningTokens =
|
|
3371
|
+
const cachedTokens = toSafeNumber7(tokenUsage.cacheReadTokens);
|
|
3372
|
+
const reasoningTokens = toSafeNumber7(tokenUsage.thinkingTokens);
|
|
2957
3373
|
const inputTokens = Math.max(
|
|
2958
3374
|
0,
|
|
2959
|
-
|
|
3375
|
+
toSafeNumber7(tokenUsage.inputTokens) - cachedTokens
|
|
2960
3376
|
);
|
|
2961
3377
|
const outputTokens = Math.max(
|
|
2962
3378
|
0,
|
|
2963
|
-
|
|
3379
|
+
toSafeNumber7(tokenUsage.outputTokens) - reasoningTokens
|
|
2964
3380
|
);
|
|
2965
3381
|
if (inputTokens === 0 && outputTokens === 0 && cachedTokens === 0 && reasoningTokens === 0) {
|
|
2966
3382
|
continue;
|
|
@@ -2983,30 +3399,31 @@ var DroidParser = class {
|
|
|
2983
3399
|
};
|
|
2984
3400
|
}
|
|
2985
3401
|
isInstalled() {
|
|
2986
|
-
return
|
|
3402
|
+
return existsSync18(this.dataDir);
|
|
2987
3403
|
}
|
|
2988
3404
|
};
|
|
2989
3405
|
registerParser(new DroidParser());
|
|
2990
3406
|
|
|
2991
3407
|
// src/parsers/pi-coding-agent.ts
|
|
2992
|
-
import {
|
|
2993
|
-
import {
|
|
2994
|
-
import {
|
|
3408
|
+
import { createHash as createHash4 } from "crypto";
|
|
3409
|
+
import { existsSync as existsSync19, statSync as statSync2 } from "fs";
|
|
3410
|
+
import { homedir as homedir17 } from "os";
|
|
3411
|
+
import { join as join18 } from "path";
|
|
2995
3412
|
var TOOL_ID12 = "pi-coding-agent";
|
|
2996
|
-
var
|
|
2997
|
-
var DEFAULT_SESSIONS_DIR5 =
|
|
2998
|
-
function
|
|
3413
|
+
var TOOL_NAME11 = "pi";
|
|
3414
|
+
var DEFAULT_SESSIONS_DIR5 = join18(homedir17(), ".pi", "agent", "sessions");
|
|
3415
|
+
function createToolDefinition7(dataDir) {
|
|
2999
3416
|
return {
|
|
3000
3417
|
id: TOOL_ID12,
|
|
3001
|
-
name:
|
|
3418
|
+
name: TOOL_NAME11,
|
|
3002
3419
|
dataDir
|
|
3003
3420
|
};
|
|
3004
3421
|
}
|
|
3005
|
-
function
|
|
3422
|
+
function toSafeNumber8(value) {
|
|
3006
3423
|
const numberValue = Number(value);
|
|
3007
|
-
return Number.isFinite(numberValue) ? numberValue : 0;
|
|
3424
|
+
return Number.isFinite(numberValue) && numberValue >= 0 ? Math.trunc(numberValue) : 0;
|
|
3008
3425
|
}
|
|
3009
|
-
function
|
|
3426
|
+
function getPathLeaf6(value) {
|
|
3010
3427
|
const normalized = value.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
3011
3428
|
const leaf = normalized.split("/").filter(Boolean).pop();
|
|
3012
3429
|
return leaf || "unknown";
|
|
@@ -3017,15 +3434,55 @@ function normalizeForPrefix4(value) {
|
|
|
3017
3434
|
function getUsageNumber3(usage, ...keys) {
|
|
3018
3435
|
for (const key of keys) {
|
|
3019
3436
|
const value = usage[key];
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
return numberValue;
|
|
3437
|
+
if (value !== void 0 && value !== null) {
|
|
3438
|
+
return toSafeNumber8(value);
|
|
3023
3439
|
}
|
|
3024
3440
|
}
|
|
3025
3441
|
return 0;
|
|
3026
3442
|
}
|
|
3443
|
+
function parseTimestamp3(value) {
|
|
3444
|
+
if (value === void 0) return null;
|
|
3445
|
+
const timestamp = new Date(
|
|
3446
|
+
typeof value === "number" && Math.abs(value) <= 1e11 ? value * 1e3 : value
|
|
3447
|
+
);
|
|
3448
|
+
return Number.isNaN(timestamp.getTime()) ? null : timestamp;
|
|
3449
|
+
}
|
|
3450
|
+
function canonicalJson(value) {
|
|
3451
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
3452
|
+
if (value && typeof value === "object") {
|
|
3453
|
+
return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
|
|
3454
|
+
}
|
|
3455
|
+
return JSON.stringify(value) ?? "null";
|
|
3456
|
+
}
|
|
3457
|
+
function usageIdentity(row, kind, usage) {
|
|
3458
|
+
const message = row.message;
|
|
3459
|
+
const semantic = createHash4("sha256").update(
|
|
3460
|
+
canonicalJson({
|
|
3461
|
+
kind,
|
|
3462
|
+
timestamp: row.timestamp,
|
|
3463
|
+
messageTimestamp: message?.timestamp,
|
|
3464
|
+
provider: message?.provider,
|
|
3465
|
+
model: message?.model,
|
|
3466
|
+
responseModel: message?.responseModel,
|
|
3467
|
+
responseId: message?.responseId,
|
|
3468
|
+
api: message?.api,
|
|
3469
|
+
toolCallId: message?.toolCallId,
|
|
3470
|
+
toolName: message?.toolName,
|
|
3471
|
+
stopReason: message?.stopReason,
|
|
3472
|
+
errorMessage: message?.errorMessage,
|
|
3473
|
+
content: message?.content,
|
|
3474
|
+
summary: row.summary,
|
|
3475
|
+
usage
|
|
3476
|
+
})
|
|
3477
|
+
).digest("hex");
|
|
3478
|
+
return {
|
|
3479
|
+
semantic,
|
|
3480
|
+
request: row.id ? JSON.stringify([kind, row.id, row.timestamp]) : semantic,
|
|
3481
|
+
hasId: Boolean(row.id)
|
|
3482
|
+
};
|
|
3483
|
+
}
|
|
3027
3484
|
function extractPiProjectFromCwd(cwd) {
|
|
3028
|
-
return
|
|
3485
|
+
return getPathLeaf6(cwd);
|
|
3029
3486
|
}
|
|
3030
3487
|
function extractPiProjectFromDir(filePath, sessionsDir = DEFAULT_SESSIONS_DIR5) {
|
|
3031
3488
|
const normalizedFilePath = normalizeForPrefix4(filePath);
|
|
@@ -3042,7 +3499,7 @@ function extractPiProjectFromDir(filePath, sessionsDir = DEFAULT_SESSIONS_DIR5)
|
|
|
3042
3499
|
try {
|
|
3043
3500
|
const decoded = decodeURIComponent(firstSegment);
|
|
3044
3501
|
if (decoded.includes("/") || decoded.includes("\\")) {
|
|
3045
|
-
return
|
|
3502
|
+
return getPathLeaf6(decoded);
|
|
3046
3503
|
}
|
|
3047
3504
|
} catch {
|
|
3048
3505
|
}
|
|
@@ -3052,7 +3509,7 @@ function extractPiProjectFromDir(filePath, sessionsDir = DEFAULT_SESSIONS_DIR5)
|
|
|
3052
3509
|
var PiCodingAgentParser = class {
|
|
3053
3510
|
constructor(sessionsDir = DEFAULT_SESSIONS_DIR5) {
|
|
3054
3511
|
this.sessionsDir = sessionsDir;
|
|
3055
|
-
this.tool =
|
|
3512
|
+
this.tool = createToolDefinition7(sessionsDir);
|
|
3056
3513
|
}
|
|
3057
3514
|
sessionsDir;
|
|
3058
3515
|
tool;
|
|
@@ -3063,7 +3520,9 @@ var PiCodingAgentParser = class {
|
|
|
3063
3520
|
}
|
|
3064
3521
|
const entries = [];
|
|
3065
3522
|
const sessionEvents = [];
|
|
3066
|
-
const
|
|
3523
|
+
const seenRequests = /* @__PURE__ */ new Set();
|
|
3524
|
+
const seenSemantics = /* @__PURE__ */ new Set();
|
|
3525
|
+
const legacySemantics = /* @__PURE__ */ new Set();
|
|
3067
3526
|
for (const filePath of sessionFiles) {
|
|
3068
3527
|
const content = readFileSafe(filePath);
|
|
3069
3528
|
if (!content) continue;
|
|
@@ -3071,7 +3530,9 @@ var PiCodingAgentParser = class {
|
|
|
3071
3530
|
if (rows.length === 0) continue;
|
|
3072
3531
|
let sessionId = filePath;
|
|
3073
3532
|
let project = extractPiProjectFromDir(filePath, this.sessionsDir);
|
|
3533
|
+
let sessionTimestamp = null;
|
|
3074
3534
|
for (const row of rows) {
|
|
3535
|
+
if (!row || typeof row !== "object") continue;
|
|
3075
3536
|
if (row.type !== "session") continue;
|
|
3076
3537
|
if (row.id) {
|
|
3077
3538
|
sessionId = row.id;
|
|
@@ -3079,59 +3540,107 @@ var PiCodingAgentParser = class {
|
|
|
3079
3540
|
if (row.cwd) {
|
|
3080
3541
|
project = extractPiProjectFromCwd(row.cwd);
|
|
3081
3542
|
}
|
|
3543
|
+
sessionTimestamp = parseTimestamp3(row.timestamp);
|
|
3082
3544
|
break;
|
|
3083
3545
|
}
|
|
3546
|
+
let fallbackTimestamp = sessionTimestamp;
|
|
3547
|
+
if (!fallbackTimestamp) {
|
|
3548
|
+
try {
|
|
3549
|
+
fallbackTimestamp = statSync2(filePath).mtime;
|
|
3550
|
+
} catch {
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3084
3553
|
for (const row of rows) {
|
|
3085
|
-
if (row
|
|
3554
|
+
if (!row || typeof row !== "object") continue;
|
|
3086
3555
|
const message = row.message;
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
if (
|
|
3090
|
-
const timestamp = new Date(rawTimestamp);
|
|
3091
|
-
if (Number.isNaN(timestamp.getTime())) continue;
|
|
3092
|
-
if (message.role === "user" || message.role === "assistant") {
|
|
3556
|
+
const timestamp = parseTimestamp3(row.timestamp) ?? parseTimestamp3(message?.timestamp) ?? fallbackTimestamp;
|
|
3557
|
+
if (!timestamp) continue;
|
|
3558
|
+
if (row.type === "message" && message?.role === "user") {
|
|
3093
3559
|
sessionEvents.push({
|
|
3094
3560
|
sessionId,
|
|
3095
3561
|
source: TOOL_ID12,
|
|
3096
3562
|
project,
|
|
3097
3563
|
timestamp,
|
|
3098
|
-
role:
|
|
3564
|
+
role: "user"
|
|
3099
3565
|
});
|
|
3566
|
+
continue;
|
|
3567
|
+
}
|
|
3568
|
+
const kind = row.type === "message" ? message?.role : row.type;
|
|
3569
|
+
if (kind !== "assistant" && kind !== "toolResult" && kind !== "compaction" && kind !== "branch_summary")
|
|
3570
|
+
continue;
|
|
3571
|
+
const usage = row.type === "message" ? message?.usage : row.usage;
|
|
3572
|
+
if (!usage) {
|
|
3573
|
+
if (kind === "assistant")
|
|
3574
|
+
sessionEvents.push({
|
|
3575
|
+
sessionId,
|
|
3576
|
+
source: TOOL_ID12,
|
|
3577
|
+
project,
|
|
3578
|
+
timestamp,
|
|
3579
|
+
role: "assistant"
|
|
3580
|
+
});
|
|
3581
|
+
continue;
|
|
3100
3582
|
}
|
|
3101
|
-
if (message.role !== "assistant") continue;
|
|
3102
|
-
const usage = message.usage;
|
|
3103
|
-
if (!usage) continue;
|
|
3104
3583
|
const inputTokens = getUsageNumber3(usage, "input", "inputTokens");
|
|
3105
|
-
const
|
|
3584
|
+
const rawOutput = getUsageNumber3(usage, "output", "outputTokens");
|
|
3106
3585
|
const cachedTokens = getUsageNumber3(
|
|
3107
3586
|
usage,
|
|
3108
3587
|
"cacheRead",
|
|
3109
3588
|
"cacheReadTokens",
|
|
3110
3589
|
"cache_read"
|
|
3111
3590
|
);
|
|
3112
|
-
const
|
|
3591
|
+
const cacheCreationTokens = getUsageNumber3(
|
|
3113
3592
|
usage,
|
|
3114
|
-
"
|
|
3115
|
-
"
|
|
3116
|
-
"
|
|
3593
|
+
"cacheWrite",
|
|
3594
|
+
"cacheWriteTokens",
|
|
3595
|
+
"cache_write"
|
|
3117
3596
|
);
|
|
3118
|
-
|
|
3597
|
+
const reasoningTokens = Math.min(
|
|
3598
|
+
rawOutput,
|
|
3599
|
+
getUsageNumber3(
|
|
3600
|
+
usage,
|
|
3601
|
+
"reasoningOutputTokens",
|
|
3602
|
+
"thinkingTokens",
|
|
3603
|
+
"thoughts"
|
|
3604
|
+
)
|
|
3605
|
+
);
|
|
3606
|
+
const outputTokens = rawOutput - reasoningTokens;
|
|
3607
|
+
if (inputTokens === 0 && outputTokens === 0 && cachedTokens === 0 && cacheCreationTokens === 0 && reasoningTokens === 0) {
|
|
3608
|
+
if (kind === "assistant")
|
|
3609
|
+
sessionEvents.push({
|
|
3610
|
+
sessionId,
|
|
3611
|
+
source: TOOL_ID12,
|
|
3612
|
+
project,
|
|
3613
|
+
timestamp,
|
|
3614
|
+
role: "assistant"
|
|
3615
|
+
});
|
|
3119
3616
|
continue;
|
|
3120
3617
|
}
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3618
|
+
const identity = usageIdentity(row, kind, usage);
|
|
3619
|
+
if (seenRequests.has(identity.request) || (identity.hasId ? legacySemantics : seenSemantics).has(
|
|
3620
|
+
identity.semantic
|
|
3621
|
+
))
|
|
3622
|
+
continue;
|
|
3623
|
+
seenRequests.add(identity.request);
|
|
3624
|
+
seenSemantics.add(identity.semantic);
|
|
3625
|
+
if (!identity.hasId) legacySemantics.add(identity.semantic);
|
|
3626
|
+
sessionEvents.push({
|
|
3627
|
+
sessionId,
|
|
3628
|
+
source: TOOL_ID12,
|
|
3629
|
+
project,
|
|
3630
|
+
timestamp,
|
|
3631
|
+
role: "assistant"
|
|
3632
|
+
});
|
|
3125
3633
|
entries.push({
|
|
3126
3634
|
sessionId,
|
|
3127
3635
|
source: TOOL_ID12,
|
|
3128
|
-
model: message
|
|
3636
|
+
model: kind === "assistant" ? message?.responseModel || message?.model || "unknown" : "unknown",
|
|
3129
3637
|
project,
|
|
3130
3638
|
timestamp,
|
|
3131
3639
|
inputTokens,
|
|
3132
3640
|
outputTokens,
|
|
3133
3641
|
reasoningTokens,
|
|
3134
|
-
cachedTokens
|
|
3642
|
+
cachedTokens,
|
|
3643
|
+
cacheCreationTokens
|
|
3135
3644
|
});
|
|
3136
3645
|
}
|
|
3137
3646
|
}
|
|
@@ -3141,27 +3650,27 @@ var PiCodingAgentParser = class {
|
|
|
3141
3650
|
};
|
|
3142
3651
|
}
|
|
3143
3652
|
isInstalled() {
|
|
3144
|
-
return
|
|
3653
|
+
return existsSync19(this.sessionsDir);
|
|
3145
3654
|
}
|
|
3146
3655
|
};
|
|
3147
3656
|
registerParser(new PiCodingAgentParser());
|
|
3148
3657
|
|
|
3149
3658
|
// src/parsers/qwenpaw.ts
|
|
3150
|
-
import { existsSync as
|
|
3151
|
-
import { homedir as
|
|
3152
|
-
import { join as
|
|
3659
|
+
import { existsSync as existsSync20, readdirSync as readdirSync9 } from "fs";
|
|
3660
|
+
import { homedir as homedir18, hostname as hostname4 } from "os";
|
|
3661
|
+
import { join as join19 } from "path";
|
|
3153
3662
|
var TOOL_ID13 = "qwenpaw";
|
|
3154
|
-
var
|
|
3155
|
-
var DEFAULT_USAGE_PATH =
|
|
3156
|
-
var DEFAULT_WORKSPACE_PATH =
|
|
3157
|
-
function
|
|
3663
|
+
var TOOL_NAME12 = "QwenPaw";
|
|
3664
|
+
var DEFAULT_USAGE_PATH = join19(homedir18(), ".qwenpaw", "token_usage.json");
|
|
3665
|
+
var DEFAULT_WORKSPACE_PATH = join19(homedir18(), ".qwenpaw", "workspace");
|
|
3666
|
+
function createToolDefinition8(usagePath) {
|
|
3158
3667
|
return {
|
|
3159
3668
|
id: TOOL_ID13,
|
|
3160
|
-
name:
|
|
3669
|
+
name: TOOL_NAME12,
|
|
3161
3670
|
dataDir: usagePath
|
|
3162
3671
|
};
|
|
3163
3672
|
}
|
|
3164
|
-
function
|
|
3673
|
+
function toSafeNumber9(value) {
|
|
3165
3674
|
const numberValue = Number(value);
|
|
3166
3675
|
return Number.isFinite(numberValue) && numberValue > 0 ? numberValue : 0;
|
|
3167
3676
|
}
|
|
@@ -3190,7 +3699,7 @@ var QwenPawParser = class {
|
|
|
3190
3699
|
constructor(options = {}) {
|
|
3191
3700
|
this.usagePath = options.usagePath || DEFAULT_USAGE_PATH;
|
|
3192
3701
|
this.workspacePath = options.workspacePath || DEFAULT_WORKSPACE_PATH;
|
|
3193
|
-
this.tool =
|
|
3702
|
+
this.tool = createToolDefinition8(this.usagePath);
|
|
3194
3703
|
}
|
|
3195
3704
|
async parse() {
|
|
3196
3705
|
const entries = [];
|
|
@@ -3208,8 +3717,8 @@ var QwenPawParser = class {
|
|
|
3208
3717
|
if (!record || typeof record !== "object") {
|
|
3209
3718
|
continue;
|
|
3210
3719
|
}
|
|
3211
|
-
const inputTokens =
|
|
3212
|
-
const outputTokens =
|
|
3720
|
+
const inputTokens = toSafeNumber9(record.prompt_tokens);
|
|
3721
|
+
const outputTokens = toSafeNumber9(record.completion_tokens);
|
|
3213
3722
|
if (inputTokens === 0 && outputTokens === 0) {
|
|
3214
3723
|
continue;
|
|
3215
3724
|
}
|
|
@@ -3237,15 +3746,15 @@ var QwenPawParser = class {
|
|
|
3237
3746
|
}
|
|
3238
3747
|
async parseWorkspaceSessions() {
|
|
3239
3748
|
const events = [];
|
|
3240
|
-
if (!
|
|
3749
|
+
if (!existsSync20(this.workspacePath)) {
|
|
3241
3750
|
return events;
|
|
3242
3751
|
}
|
|
3243
3752
|
try {
|
|
3244
3753
|
const workspaceDirs = readdirSync9(this.workspacePath);
|
|
3245
3754
|
for (const workspaceDir of workspaceDirs) {
|
|
3246
|
-
const workspacePath =
|
|
3247
|
-
const chatsPath =
|
|
3248
|
-
const sessionsPath =
|
|
3755
|
+
const workspacePath = join19(this.workspacePath, workspaceDir);
|
|
3756
|
+
const chatsPath = join19(workspacePath, "chats.json");
|
|
3757
|
+
const sessionsPath = join19(workspacePath, "sessions");
|
|
3249
3758
|
const chatsContent = readFileSafe(chatsPath);
|
|
3250
3759
|
if (!chatsContent) {
|
|
3251
3760
|
continue;
|
|
@@ -3289,7 +3798,7 @@ var QwenPawParser = class {
|
|
|
3289
3798
|
}
|
|
3290
3799
|
getSessionFiles(sessionsPath) {
|
|
3291
3800
|
const files = /* @__PURE__ */ new Map();
|
|
3292
|
-
if (!
|
|
3801
|
+
if (!existsSync20(sessionsPath)) {
|
|
3293
3802
|
return files;
|
|
3294
3803
|
}
|
|
3295
3804
|
try {
|
|
@@ -3298,7 +3807,7 @@ var QwenPawParser = class {
|
|
|
3298
3807
|
if (!fileName.endsWith(".json")) {
|
|
3299
3808
|
continue;
|
|
3300
3809
|
}
|
|
3301
|
-
const filePath =
|
|
3810
|
+
const filePath = join19(sessionsPath, fileName);
|
|
3302
3811
|
const content = readFileSafe(filePath);
|
|
3303
3812
|
if (content) {
|
|
3304
3813
|
const sessionId = fileName.replace(/^[^_]+_/, "").replace(".json", "");
|
|
@@ -3396,15 +3905,15 @@ var QwenPawParser = class {
|
|
|
3396
3905
|
});
|
|
3397
3906
|
}
|
|
3398
3907
|
isInstalled() {
|
|
3399
|
-
return
|
|
3908
|
+
return existsSync20(this.usagePath) || existsSync20(this.workspacePath);
|
|
3400
3909
|
}
|
|
3401
3910
|
};
|
|
3402
3911
|
registerParser(new QwenPawParser());
|
|
3403
3912
|
|
|
3404
3913
|
// src/parsers/cline.ts
|
|
3405
|
-
import { readFileSync as readFileSync6, statSync as
|
|
3406
|
-
import { homedir as
|
|
3407
|
-
import { basename as
|
|
3914
|
+
import { readFileSync as readFileSync6, statSync as statSync3 } from "fs";
|
|
3915
|
+
import { homedir as homedir19 } from "os";
|
|
3916
|
+
import { basename as basename8, join as join20 } from "path";
|
|
3408
3917
|
var EXTENSION_ID = "saoudrizwan.claude-dev";
|
|
3409
3918
|
var HOSTS = [
|
|
3410
3919
|
"Code",
|
|
@@ -3418,28 +3927,28 @@ var HOSTS = [
|
|
|
3418
3927
|
var TOOL6 = {
|
|
3419
3928
|
id: "cline",
|
|
3420
3929
|
name: "Cline",
|
|
3421
|
-
dataDir:
|
|
3930
|
+
dataDir: join20(homedir19(), ".cline")
|
|
3422
3931
|
};
|
|
3423
3932
|
function getHostRoots() {
|
|
3424
3933
|
const out = [];
|
|
3425
3934
|
if (process.platform === "darwin") {
|
|
3426
|
-
const base =
|
|
3427
|
-
for (const h of HOSTS) out.push(
|
|
3935
|
+
const base = join20(homedir19(), "Library", "Application Support");
|
|
3936
|
+
for (const h of HOSTS) out.push(join20(base, h));
|
|
3428
3937
|
} else if (process.platform === "win32") {
|
|
3429
|
-
const appData = process.env.APPDATA?.trim() ||
|
|
3430
|
-
for (const h of HOSTS) out.push(
|
|
3938
|
+
const appData = process.env.APPDATA?.trim() || join20(homedir19(), "AppData", "Roaming");
|
|
3939
|
+
for (const h of HOSTS) out.push(join20(appData, h));
|
|
3431
3940
|
} else {
|
|
3432
|
-
const xdg = process.env.XDG_CONFIG_HOME?.trim() ||
|
|
3433
|
-
for (const h of HOSTS) out.push(
|
|
3941
|
+
const xdg = process.env.XDG_CONFIG_HOME?.trim() || join20(homedir19(), ".config");
|
|
3942
|
+
for (const h of HOSTS) out.push(join20(xdg, h));
|
|
3434
3943
|
}
|
|
3435
3944
|
return out;
|
|
3436
3945
|
}
|
|
3437
3946
|
function findClineExtensionDirs() {
|
|
3438
3947
|
const dirs = [];
|
|
3439
3948
|
for (const root of getHostRoots()) {
|
|
3440
|
-
const ext =
|
|
3949
|
+
const ext = join20(root, "User", "globalStorage", EXTENSION_ID);
|
|
3441
3950
|
try {
|
|
3442
|
-
if (
|
|
3951
|
+
if (statSync3(ext).isDirectory()) dirs.push(ext);
|
|
3443
3952
|
} catch {
|
|
3444
3953
|
}
|
|
3445
3954
|
}
|
|
@@ -3455,7 +3964,7 @@ function readJsonSafe(path) {
|
|
|
3455
3964
|
function projectFromPath(absPath) {
|
|
3456
3965
|
if (!absPath || typeof absPath !== "string") return "unknown";
|
|
3457
3966
|
const trimmed = absPath.replace(/[\\/]+$/, "");
|
|
3458
|
-
const name =
|
|
3967
|
+
const name = basename8(trimmed);
|
|
3459
3968
|
return name || "unknown";
|
|
3460
3969
|
}
|
|
3461
3970
|
var ClineParser = class {
|
|
@@ -3469,7 +3978,7 @@ var ClineParser = class {
|
|
|
3469
3978
|
const entries = [];
|
|
3470
3979
|
const sessionEvents = [];
|
|
3471
3980
|
for (const extDir of extDirs) {
|
|
3472
|
-
const history = readJsonSafe(
|
|
3981
|
+
const history = readJsonSafe(join20(extDir, "state", "taskHistory.json"));
|
|
3473
3982
|
if (!Array.isArray(history)) continue;
|
|
3474
3983
|
for (const item of history) {
|
|
3475
3984
|
try {
|
|
@@ -3480,7 +3989,7 @@ var ClineParser = class {
|
|
|
3480
3989
|
);
|
|
3481
3990
|
const fallbackModel = item.modelId && String(item.modelId).trim() || "cline-unknown";
|
|
3482
3991
|
const messages = readJsonSafe(
|
|
3483
|
-
|
|
3992
|
+
join20(extDir, "tasks", taskId, "ui_messages.json")
|
|
3484
3993
|
);
|
|
3485
3994
|
if (!Array.isArray(messages)) continue;
|
|
3486
3995
|
for (const msg of messages) {
|
|
@@ -3511,10 +4020,11 @@ var ClineParser = class {
|
|
|
3511
4020
|
model,
|
|
3512
4021
|
project,
|
|
3513
4022
|
timestamp,
|
|
3514
|
-
inputTokens
|
|
4023
|
+
inputTokens,
|
|
3515
4024
|
outputTokens,
|
|
3516
4025
|
reasoningTokens: 0,
|
|
3517
|
-
cachedTokens: cacheReads
|
|
4026
|
+
cachedTokens: cacheReads,
|
|
4027
|
+
cacheCreationTokens: cacheWrites
|
|
3518
4028
|
});
|
|
3519
4029
|
}
|
|
3520
4030
|
if (msgType === "ask" || msgType === "say" && msgSay === "user_feedback") {
|
|
@@ -3551,20 +4061,20 @@ registerParser(new ClineParser());
|
|
|
3551
4061
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
3552
4062
|
import {
|
|
3553
4063
|
copyFileSync,
|
|
3554
|
-
existsSync as
|
|
4064
|
+
existsSync as existsSync21,
|
|
3555
4065
|
mkdtempSync,
|
|
3556
4066
|
readdirSync as readdirSync10,
|
|
3557
4067
|
readFileSync as readFileSync7,
|
|
3558
4068
|
rmSync,
|
|
3559
|
-
statSync as
|
|
4069
|
+
statSync as statSync4
|
|
3560
4070
|
} from "fs";
|
|
3561
|
-
import { homedir as
|
|
3562
|
-
import { join as
|
|
3563
|
-
var KIROAGENT_RELATIVE =
|
|
4071
|
+
import { homedir as homedir20, tmpdir } from "os";
|
|
4072
|
+
import { join as join21, resolve } from "path";
|
|
4073
|
+
var KIROAGENT_RELATIVE = join21("User", "globalStorage", "kiro.kiroagent");
|
|
3564
4074
|
function getDefaultBasePath() {
|
|
3565
4075
|
if (process.platform === "darwin") {
|
|
3566
|
-
return
|
|
3567
|
-
|
|
4076
|
+
return join21(
|
|
4077
|
+
homedir20(),
|
|
3568
4078
|
"Library",
|
|
3569
4079
|
"Application Support",
|
|
3570
4080
|
"Kiro",
|
|
@@ -3572,11 +4082,11 @@ function getDefaultBasePath() {
|
|
|
3572
4082
|
);
|
|
3573
4083
|
}
|
|
3574
4084
|
if (process.platform === "win32") {
|
|
3575
|
-
const appData = process.env.APPDATA?.trim() ||
|
|
3576
|
-
return
|
|
4085
|
+
const appData = process.env.APPDATA?.trim() || join21(homedir20(), "AppData", "Roaming");
|
|
4086
|
+
return join21(appData, "Kiro", KIROAGENT_RELATIVE);
|
|
3577
4087
|
}
|
|
3578
|
-
const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim() ||
|
|
3579
|
-
return
|
|
4088
|
+
const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim() || join21(homedir20(), ".config");
|
|
4089
|
+
return join21(xdgConfigHome, "Kiro", KIROAGENT_RELATIVE);
|
|
3580
4090
|
}
|
|
3581
4091
|
var TOOL7 = {
|
|
3582
4092
|
id: "kiro",
|
|
@@ -3587,10 +4097,10 @@ function getKiroBasePath() {
|
|
|
3587
4097
|
const explicit = process.env.KIRO_BASE_PATH?.trim();
|
|
3588
4098
|
if (explicit) {
|
|
3589
4099
|
const r = resolve(explicit);
|
|
3590
|
-
return
|
|
4100
|
+
return existsSync21(r) ? r : null;
|
|
3591
4101
|
}
|
|
3592
4102
|
const def = getDefaultBasePath();
|
|
3593
|
-
return
|
|
4103
|
+
return existsSync21(def) ? def : null;
|
|
3594
4104
|
}
|
|
3595
4105
|
function isLockError(err) {
|
|
3596
4106
|
return err instanceof Error && typeof err.message === "string" && /database is locked/i.test(err.message);
|
|
@@ -3611,12 +4121,12 @@ function readDb(dbPath) {
|
|
|
3611
4121
|
return queryDb(dbPath, TOKENS_SQL);
|
|
3612
4122
|
} catch (err) {
|
|
3613
4123
|
if (!isLockError(err)) throw err;
|
|
3614
|
-
const snapshotDir = mkdtempSync(
|
|
3615
|
-
const queryPath =
|
|
4124
|
+
const snapshotDir = mkdtempSync(join21(tmpdir(), "vibe-usage-kiro-"));
|
|
4125
|
+
const queryPath = join21(snapshotDir, "devdata.sqlite");
|
|
3616
4126
|
copyFileSync(dbPath, queryPath);
|
|
3617
4127
|
for (const suffix of ["-shm", "-wal"]) {
|
|
3618
4128
|
const companion = `${dbPath}${suffix}`;
|
|
3619
|
-
if (
|
|
4129
|
+
if (existsSync21(companion))
|
|
3620
4130
|
copyFileSync(companion, `${queryPath}${suffix}`);
|
|
3621
4131
|
}
|
|
3622
4132
|
try {
|
|
@@ -3637,7 +4147,7 @@ function readJsonl(jsonlPath) {
|
|
|
3637
4147
|
if (lines.length === 0) return [];
|
|
3638
4148
|
let mtime;
|
|
3639
4149
|
try {
|
|
3640
|
-
mtime =
|
|
4150
|
+
mtime = statSync4(jsonlPath).mtime;
|
|
3641
4151
|
} catch {
|
|
3642
4152
|
mtime = /* @__PURE__ */ new Date();
|
|
3643
4153
|
}
|
|
@@ -3668,7 +4178,7 @@ function buildModelTimeline(base) {
|
|
|
3668
4178
|
}
|
|
3669
4179
|
for (const entry of entries) {
|
|
3670
4180
|
if (!entry.isDirectory() || entry.name === "dev_data") continue;
|
|
3671
|
-
const dirPath =
|
|
4181
|
+
const dirPath = join21(base, entry.name);
|
|
3672
4182
|
let files;
|
|
3673
4183
|
try {
|
|
3674
4184
|
files = readdirSync10(dirPath).filter((f) => f.endsWith(".chat"));
|
|
@@ -3677,7 +4187,7 @@ function buildModelTimeline(base) {
|
|
|
3677
4187
|
}
|
|
3678
4188
|
for (const file of files) {
|
|
3679
4189
|
try {
|
|
3680
|
-
const data = JSON.parse(readFileSync7(
|
|
4190
|
+
const data = JSON.parse(readFileSync7(join21(dirPath, file), "utf-8"));
|
|
3681
4191
|
const meta = data?.metadata;
|
|
3682
4192
|
if (!meta?.modelId || !meta?.startTime) continue;
|
|
3683
4193
|
const startMs = Number(meta.startTime);
|
|
@@ -3726,13 +4236,13 @@ var KiroParser = class {
|
|
|
3726
4236
|
async parse() {
|
|
3727
4237
|
const base = getKiroBasePath();
|
|
3728
4238
|
if (!base) return { buckets: [], sessions: [] };
|
|
3729
|
-
const dbPath =
|
|
3730
|
-
const jsonlPath =
|
|
4239
|
+
const dbPath = join21(base, "dev_data", "devdata.sqlite");
|
|
4240
|
+
const jsonlPath = join21(base, "dev_data", "tokens_generated.jsonl");
|
|
3731
4241
|
let rows;
|
|
3732
4242
|
try {
|
|
3733
|
-
if (
|
|
4243
|
+
if (existsSync21(dbPath)) {
|
|
3734
4244
|
rows = readDb(dbPath);
|
|
3735
|
-
} else if (
|
|
4245
|
+
} else if (existsSync21(jsonlPath)) {
|
|
3736
4246
|
rows = readJsonl(jsonlPath);
|
|
3737
4247
|
} else {
|
|
3738
4248
|
return { buckets: [], sessions: [] };
|
|
@@ -3782,9 +4292,9 @@ var KiroParser = class {
|
|
|
3782
4292
|
registerParser(new KiroParser());
|
|
3783
4293
|
|
|
3784
4294
|
// src/parsers/roo-code.ts
|
|
3785
|
-
import { readdirSync as readdirSync11, readFileSync as readFileSync8, statSync as
|
|
3786
|
-
import { homedir as
|
|
3787
|
-
import { basename as
|
|
4295
|
+
import { readdirSync as readdirSync11, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
|
|
4296
|
+
import { homedir as homedir21 } from "os";
|
|
4297
|
+
import { basename as basename9, join as join22 } from "path";
|
|
3788
4298
|
var EXTENSION_ID2 = "rooveterinaryinc.roo-cline";
|
|
3789
4299
|
var HOSTS2 = [
|
|
3790
4300
|
"Code",
|
|
@@ -3799,17 +4309,17 @@ function getHostRoots2() {
|
|
|
3799
4309
|
const out = [];
|
|
3800
4310
|
let roots;
|
|
3801
4311
|
if (process.platform === "darwin") {
|
|
3802
|
-
roots = [
|
|
4312
|
+
roots = [join22(homedir21(), "Library", "Application Support")];
|
|
3803
4313
|
} else if (process.platform === "win32") {
|
|
3804
4314
|
roots = [
|
|
3805
|
-
process.env.APPDATA?.trim() ||
|
|
4315
|
+
process.env.APPDATA?.trim() || join22(homedir21(), "AppData", "Roaming")
|
|
3806
4316
|
];
|
|
3807
4317
|
} else {
|
|
3808
|
-
roots = [process.env.XDG_CONFIG_HOME?.trim() ||
|
|
4318
|
+
roots = [process.env.XDG_CONFIG_HOME?.trim() || join22(homedir21(), ".config")];
|
|
3809
4319
|
}
|
|
3810
4320
|
for (const root of roots) {
|
|
3811
4321
|
for (const h of HOSTS2) {
|
|
3812
|
-
out.push(
|
|
4322
|
+
out.push(join22(root, h));
|
|
3813
4323
|
}
|
|
3814
4324
|
}
|
|
3815
4325
|
return out;
|
|
@@ -3817,9 +4327,9 @@ function getHostRoots2() {
|
|
|
3817
4327
|
function findExtensionDirs() {
|
|
3818
4328
|
const dirs = [];
|
|
3819
4329
|
for (const root of getHostRoots2()) {
|
|
3820
|
-
const ext =
|
|
4330
|
+
const ext = join22(root, "User", "globalStorage", EXTENSION_ID2);
|
|
3821
4331
|
try {
|
|
3822
|
-
if (
|
|
4332
|
+
if (statSync5(ext).isDirectory()) dirs.push(ext);
|
|
3823
4333
|
} catch {
|
|
3824
4334
|
}
|
|
3825
4335
|
}
|
|
@@ -3835,13 +4345,13 @@ function readJsonSafe2(path) {
|
|
|
3835
4345
|
function projectFromPath2(absPath) {
|
|
3836
4346
|
if (!absPath || typeof absPath !== "string") return "unknown";
|
|
3837
4347
|
const trimmed = absPath.replace(/[\\/]+$/, "");
|
|
3838
|
-
const name =
|
|
4348
|
+
const name = basename9(trimmed);
|
|
3839
4349
|
return name || "unknown";
|
|
3840
4350
|
}
|
|
3841
4351
|
function readHistoryItems(extDir) {
|
|
3842
|
-
const tasksDir =
|
|
4352
|
+
const tasksDir = join22(extDir, "tasks");
|
|
3843
4353
|
const index = readJsonSafe2(
|
|
3844
|
-
|
|
4354
|
+
join22(tasksDir, "_index.json")
|
|
3845
4355
|
);
|
|
3846
4356
|
if (index?.entries && Array.isArray(index.entries)) return index.entries;
|
|
3847
4357
|
const items = [];
|
|
@@ -3855,7 +4365,7 @@ function readHistoryItems(extDir) {
|
|
|
3855
4365
|
if (!entry.isDirectory() || entry.name.startsWith("_") || entry.name.startsWith("."))
|
|
3856
4366
|
continue;
|
|
3857
4367
|
const item = readJsonSafe2(
|
|
3858
|
-
|
|
4368
|
+
join22(tasksDir, entry.name, "history_item.json")
|
|
3859
4369
|
);
|
|
3860
4370
|
if (item && typeof item === "object") items.push(item);
|
|
3861
4371
|
}
|
|
@@ -3882,7 +4392,7 @@ var RooCodeParser = class {
|
|
|
3882
4392
|
const project = projectFromPath2(item.workspace);
|
|
3883
4393
|
const fallbackModel = item.apiConfigName && String(item.apiConfigName).trim() || "roo-unknown";
|
|
3884
4394
|
const messages = readJsonSafe2(
|
|
3885
|
-
|
|
4395
|
+
join22(extDir, "tasks", taskId, "ui_messages.json")
|
|
3886
4396
|
);
|
|
3887
4397
|
if (!Array.isArray(messages)) continue;
|
|
3888
4398
|
for (const msg of messages) {
|
|
@@ -3910,10 +4420,11 @@ var RooCodeParser = class {
|
|
|
3910
4420
|
model,
|
|
3911
4421
|
project,
|
|
3912
4422
|
timestamp,
|
|
3913
|
-
inputTokens
|
|
4423
|
+
inputTokens,
|
|
3914
4424
|
outputTokens,
|
|
3915
4425
|
reasoningTokens: 0,
|
|
3916
|
-
cachedTokens: cacheReads
|
|
4426
|
+
cachedTokens: cacheReads,
|
|
4427
|
+
cacheCreationTokens: cacheWrites
|
|
3917
4428
|
});
|
|
3918
4429
|
events.push({
|
|
3919
4430
|
sessionId: taskId,
|
|
@@ -3946,10 +4457,10 @@ var RooCodeParser = class {
|
|
|
3946
4457
|
registerParser(new RooCodeParser());
|
|
3947
4458
|
|
|
3948
4459
|
// src/parsers/snow.ts
|
|
3949
|
-
import { existsSync as
|
|
3950
|
-
import { homedir as
|
|
3951
|
-
import { join as
|
|
3952
|
-
var DEFAULT_DATA_DIR6 =
|
|
4460
|
+
import { existsSync as existsSync22 } from "fs";
|
|
4461
|
+
import { homedir as homedir22 } from "os";
|
|
4462
|
+
import { join as join23 } from "path";
|
|
4463
|
+
var DEFAULT_DATA_DIR6 = join23(homedir22(), ".snow", "usage");
|
|
3953
4464
|
function toNonNegativeNumber2(value) {
|
|
3954
4465
|
const numberValue = Number(value);
|
|
3955
4466
|
return Number.isFinite(numberValue) && numberValue >= 0 ? numberValue : 0;
|
|
@@ -3996,26 +4507,26 @@ var SnowParser = class {
|
|
|
3996
4507
|
return { buckets: aggregateToBuckets(entries), sessions: [] };
|
|
3997
4508
|
}
|
|
3998
4509
|
isInstalled() {
|
|
3999
|
-
return
|
|
4510
|
+
return existsSync22(this.dataDir);
|
|
4000
4511
|
}
|
|
4001
4512
|
};
|
|
4002
4513
|
registerParser(new SnowParser());
|
|
4003
4514
|
|
|
4004
4515
|
// src/parsers/cursor.ts
|
|
4005
4516
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
4006
|
-
import { copyFileSync as copyFileSync2, existsSync as
|
|
4007
|
-
import { homedir as
|
|
4008
|
-
import { dirname as dirname4, join as
|
|
4517
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync23, mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
|
|
4518
|
+
import { homedir as homedir23, tmpdir as tmpdir2 } from "os";
|
|
4519
|
+
import { dirname as dirname4, join as join24, resolve as resolve2 } from "path";
|
|
4009
4520
|
var TOOL_ID14 = "cursor";
|
|
4010
|
-
var
|
|
4011
|
-
var STATE_DB_RELATIVE =
|
|
4521
|
+
var TOOL_NAME13 = "Cursor";
|
|
4522
|
+
var STATE_DB_RELATIVE = join24("User", "globalStorage", "state.vscdb");
|
|
4012
4523
|
var ACCESS_TOKEN_KEY = "cursorAuth/accessToken";
|
|
4013
4524
|
var SESSION_COOKIE = "WorkosCursorSessionToken";
|
|
4014
4525
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
4015
4526
|
function getDefaultStateDbPath() {
|
|
4016
4527
|
if (process.platform === "darwin") {
|
|
4017
|
-
return
|
|
4018
|
-
|
|
4528
|
+
return join24(
|
|
4529
|
+
homedir23(),
|
|
4019
4530
|
"Library",
|
|
4020
4531
|
"Application Support",
|
|
4021
4532
|
"Cursor",
|
|
@@ -4023,25 +4534,25 @@ function getDefaultStateDbPath() {
|
|
|
4023
4534
|
);
|
|
4024
4535
|
}
|
|
4025
4536
|
if (process.platform === "win32") {
|
|
4026
|
-
const appData = process.env.APPDATA?.trim() ||
|
|
4027
|
-
return
|
|
4537
|
+
const appData = process.env.APPDATA?.trim() || join24(homedir23(), "AppData", "Roaming");
|
|
4538
|
+
return join24(appData, "Cursor", STATE_DB_RELATIVE);
|
|
4028
4539
|
}
|
|
4029
|
-
const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim() ||
|
|
4030
|
-
return
|
|
4540
|
+
const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim() || join24(homedir23(), ".config");
|
|
4541
|
+
return join24(xdgConfigHome, "Cursor", STATE_DB_RELATIVE);
|
|
4031
4542
|
}
|
|
4032
4543
|
function getCursorStateDbPath() {
|
|
4033
4544
|
const explicit = process.env.CURSOR_STATE_DB_PATH?.trim();
|
|
4034
4545
|
if (explicit) {
|
|
4035
4546
|
const resolved = resolve2(explicit);
|
|
4036
|
-
return
|
|
4547
|
+
return existsSync23(resolved) ? resolved : null;
|
|
4037
4548
|
}
|
|
4038
4549
|
const configDirs = process.env.CURSOR_CONFIG_DIR?.trim();
|
|
4039
4550
|
const candidates = configDirs ? configDirs.split(",").map((v) => v.trim()).filter(Boolean).map((v) => {
|
|
4040
4551
|
const r = resolve2(v);
|
|
4041
|
-
return r.endsWith(".vscdb") ? r :
|
|
4552
|
+
return r.endsWith(".vscdb") ? r : join24(r, STATE_DB_RELATIVE);
|
|
4042
4553
|
}) : [getDefaultStateDbPath()];
|
|
4043
4554
|
for (const c of candidates) {
|
|
4044
|
-
if (
|
|
4555
|
+
if (existsSync23(c)) return c;
|
|
4045
4556
|
}
|
|
4046
4557
|
return null;
|
|
4047
4558
|
}
|
|
@@ -4081,13 +4592,13 @@ function readAccessToken(dbPath) {
|
|
|
4081
4592
|
return queryAccessToken(dbPath);
|
|
4082
4593
|
} catch (err) {
|
|
4083
4594
|
if (!isLockError2(err)) throw err;
|
|
4084
|
-
const snapshotDir = mkdtempSync2(
|
|
4085
|
-
const queryPath =
|
|
4595
|
+
const snapshotDir = mkdtempSync2(join24(tmpdir2(), "tokenarena-cursor-"));
|
|
4596
|
+
const queryPath = join24(snapshotDir, "state.vscdb");
|
|
4086
4597
|
try {
|
|
4087
4598
|
copyFileSync2(dbPath, queryPath);
|
|
4088
4599
|
for (const suffix of ["-shm", "-wal"]) {
|
|
4089
4600
|
const companion = `${dbPath}${suffix}`;
|
|
4090
|
-
if (
|
|
4601
|
+
if (existsSync23(companion))
|
|
4091
4602
|
copyFileSync2(companion, `${queryPath}${suffix}`);
|
|
4092
4603
|
}
|
|
4093
4604
|
return queryAccessToken(queryPath);
|
|
@@ -4213,10 +4724,10 @@ function parseInt0(value) {
|
|
|
4213
4724
|
const n = Number(String(value).replace(/,/g, "").trim());
|
|
4214
4725
|
return Number.isFinite(n) && n > 0 ? Math.round(n) : 0;
|
|
4215
4726
|
}
|
|
4216
|
-
function
|
|
4727
|
+
function createToolDefinition9(dbPath) {
|
|
4217
4728
|
return {
|
|
4218
4729
|
id: TOOL_ID14,
|
|
4219
|
-
name:
|
|
4730
|
+
name: TOOL_NAME13,
|
|
4220
4731
|
dataDir: dirname4(dbPath)
|
|
4221
4732
|
};
|
|
4222
4733
|
}
|
|
@@ -4229,10 +4740,10 @@ var CursorParser = class {
|
|
|
4229
4740
|
this.dbPath = options.dbPath || getCursorStateDbPath() || "";
|
|
4230
4741
|
this.readToken = options.readToken || readAccessToken;
|
|
4231
4742
|
this.fetchCsv = options.fetchCsv || fetchUsageCsv;
|
|
4232
|
-
this.tool =
|
|
4743
|
+
this.tool = createToolDefinition9(this.dbPath);
|
|
4233
4744
|
}
|
|
4234
4745
|
async parse() {
|
|
4235
|
-
if (!this.dbPath || !
|
|
4746
|
+
if (!this.dbPath || !existsSync23(this.dbPath)) {
|
|
4236
4747
|
return { buckets: [], sessions: [] };
|
|
4237
4748
|
}
|
|
4238
4749
|
let token;
|
|
@@ -4283,9 +4794,10 @@ var CursorParser = class {
|
|
|
4283
4794
|
model,
|
|
4284
4795
|
project: "unknown",
|
|
4285
4796
|
timestamp,
|
|
4286
|
-
inputTokens:
|
|
4797
|
+
inputTokens: inputNoCache,
|
|
4287
4798
|
outputTokens: output,
|
|
4288
4799
|
cachedTokens: cacheRead,
|
|
4800
|
+
cacheCreationTokens: inputCacheWrite,
|
|
4289
4801
|
reasoningTokens: 0
|
|
4290
4802
|
});
|
|
4291
4803
|
}
|
|
@@ -4295,19 +4807,19 @@ var CursorParser = class {
|
|
|
4295
4807
|
};
|
|
4296
4808
|
}
|
|
4297
4809
|
isInstalled() {
|
|
4298
|
-
return
|
|
4810
|
+
return existsSync23(this.dbPath);
|
|
4299
4811
|
}
|
|
4300
4812
|
};
|
|
4301
4813
|
registerParser(new CursorParser());
|
|
4302
4814
|
|
|
4303
4815
|
// src/parsers/zcode.ts
|
|
4304
|
-
import { createHash as
|
|
4305
|
-
import { existsSync as
|
|
4306
|
-
import { homedir as
|
|
4307
|
-
import { dirname as dirname5, join as
|
|
4816
|
+
import { createHash as createHash5 } from "crypto";
|
|
4817
|
+
import { existsSync as existsSync24 } from "fs";
|
|
4818
|
+
import { homedir as homedir24, hostname as hostname5 } from "os";
|
|
4819
|
+
import { dirname as dirname5, join as join25 } from "path";
|
|
4308
4820
|
var TOOL_ID15 = "zcode";
|
|
4309
|
-
var
|
|
4310
|
-
var DEFAULT_DB_PATH3 =
|
|
4821
|
+
var TOOL_NAME14 = "ZCode";
|
|
4822
|
+
var DEFAULT_DB_PATH3 = join25(homedir24(), ".zcode", "cli", "db", "db.sqlite");
|
|
4311
4823
|
var MODEL_USAGE_QUERY = `SELECT
|
|
4312
4824
|
model_usage.session_id as sessionId,
|
|
4313
4825
|
session.directory as directory,
|
|
@@ -4340,14 +4852,14 @@ var TURN_USAGE_QUERY = `SELECT
|
|
|
4340
4852
|
duration_ms as durationMs
|
|
4341
4853
|
FROM turn_usage
|
|
4342
4854
|
WHERE duration_ms IS NOT NULL`;
|
|
4343
|
-
function
|
|
4855
|
+
function createToolDefinition10(dbPath) {
|
|
4344
4856
|
return {
|
|
4345
4857
|
id: TOOL_ID15,
|
|
4346
|
-
name:
|
|
4858
|
+
name: TOOL_NAME14,
|
|
4347
4859
|
dataDir: dirname5(dbPath)
|
|
4348
4860
|
};
|
|
4349
4861
|
}
|
|
4350
|
-
function
|
|
4862
|
+
function toSafeNumber10(value) {
|
|
4351
4863
|
const numberValue = Number(value);
|
|
4352
4864
|
return Number.isFinite(numberValue) && numberValue > 0 ? numberValue : 0;
|
|
4353
4865
|
}
|
|
@@ -4362,7 +4874,7 @@ function parseUnixMillis(value) {
|
|
|
4362
4874
|
const timestamp = new Date(numberValue);
|
|
4363
4875
|
return Number.isNaN(timestamp.getTime()) ? null : timestamp;
|
|
4364
4876
|
}
|
|
4365
|
-
function
|
|
4877
|
+
function getPathLeaf7(value) {
|
|
4366
4878
|
if (!value) {
|
|
4367
4879
|
return "unknown";
|
|
4368
4880
|
}
|
|
@@ -4447,7 +4959,7 @@ function buildSessions2(input2) {
|
|
|
4447
4959
|
const draft = getOrCreateDraft(
|
|
4448
4960
|
drafts,
|
|
4449
4961
|
sessionId,
|
|
4450
|
-
|
|
4962
|
+
getPathLeaf7(getString3(row.directory))
|
|
4451
4963
|
);
|
|
4452
4964
|
draft.fallbackFirstAt = parseUnixMillis(row.timeCreated);
|
|
4453
4965
|
draft.fallbackLastAt = parseUnixMillis(row.timeUpdated);
|
|
@@ -4485,7 +4997,7 @@ function buildSessions2(input2) {
|
|
|
4485
4997
|
continue;
|
|
4486
4998
|
}
|
|
4487
4999
|
const draft = getOrCreateDraft(drafts, sessionId, "unknown");
|
|
4488
|
-
draft.activeSeconds += Math.round(
|
|
5000
|
+
draft.activeSeconds += Math.round(toSafeNumber10(row.durationMs) / 1e3);
|
|
4489
5001
|
}
|
|
4490
5002
|
const usageBySession = buildSessionUsage3(input2.entries);
|
|
4491
5003
|
const host = hostname5().replace(/\.local$/, "");
|
|
@@ -4530,7 +5042,7 @@ function buildSessions2(input2) {
|
|
|
4530
5042
|
return {
|
|
4531
5043
|
source: draft.source,
|
|
4532
5044
|
project: draft.project,
|
|
4533
|
-
sessionHash:
|
|
5045
|
+
sessionHash: createHash5("sha256").update(draft.sessionId).digest("hex").slice(0, 16),
|
|
4534
5046
|
hostname: host,
|
|
4535
5047
|
firstMessageAt: firstMessageAt.toISOString(),
|
|
4536
5048
|
lastMessageAt: lastMessageAt.toISOString(),
|
|
@@ -4558,10 +5070,10 @@ var ZCodeParser = class {
|
|
|
4558
5070
|
constructor(options = {}) {
|
|
4559
5071
|
this.dbPath = options.dbPath || DEFAULT_DB_PATH3;
|
|
4560
5072
|
this.queryRows = options.queryRows || readSqliteRows;
|
|
4561
|
-
this.tool =
|
|
5073
|
+
this.tool = createToolDefinition10(this.dbPath);
|
|
4562
5074
|
}
|
|
4563
5075
|
async parse() {
|
|
4564
|
-
if (!
|
|
5076
|
+
if (!existsSync24(this.dbPath)) {
|
|
4565
5077
|
return { buckets: [], sessions: [] };
|
|
4566
5078
|
}
|
|
4567
5079
|
const usageRows = await this.queryRows(
|
|
@@ -4574,10 +5086,10 @@ var ZCodeParser = class {
|
|
|
4574
5086
|
if (!timestamp) {
|
|
4575
5087
|
continue;
|
|
4576
5088
|
}
|
|
4577
|
-
const inputTokens =
|
|
4578
|
-
const outputTokens =
|
|
4579
|
-
const reasoningTokens =
|
|
4580
|
-
const cachedTokens =
|
|
5089
|
+
const inputTokens = toSafeNumber10(row.inputTokens);
|
|
5090
|
+
const outputTokens = toSafeNumber10(row.outputTokens);
|
|
5091
|
+
const reasoningTokens = toSafeNumber10(row.reasoningTokens);
|
|
5092
|
+
const cachedTokens = toSafeNumber10(row.cacheReadInputTokens);
|
|
4581
5093
|
if (inputTokens === 0 && outputTokens === 0 && reasoningTokens === 0 && cachedTokens === 0) {
|
|
4582
5094
|
continue;
|
|
4583
5095
|
}
|
|
@@ -4585,7 +5097,7 @@ var ZCodeParser = class {
|
|
|
4585
5097
|
sessionId: getString3(row.sessionId) ?? void 0,
|
|
4586
5098
|
source: TOOL_ID15,
|
|
4587
5099
|
model: getString3(row.model) ?? "unknown",
|
|
4588
|
-
project:
|
|
5100
|
+
project: getPathLeaf7(getString3(row.directory)),
|
|
4589
5101
|
timestamp,
|
|
4590
5102
|
inputTokens,
|
|
4591
5103
|
outputTokens,
|
|
@@ -4619,31 +5131,31 @@ var ZCodeParser = class {
|
|
|
4619
5131
|
};
|
|
4620
5132
|
}
|
|
4621
5133
|
isInstalled() {
|
|
4622
|
-
return
|
|
5134
|
+
return existsSync24(this.dbPath);
|
|
4623
5135
|
}
|
|
4624
5136
|
};
|
|
4625
5137
|
registerParser(new ZCodeParser());
|
|
4626
5138
|
|
|
4627
5139
|
// src/parsers/qodercli.ts
|
|
4628
|
-
import { existsSync as
|
|
4629
|
-
import { homedir as
|
|
4630
|
-
import { basename as
|
|
5140
|
+
import { existsSync as existsSync25, readdirSync as readdirSync12 } from "fs";
|
|
5141
|
+
import { homedir as homedir25 } from "os";
|
|
5142
|
+
import { basename as basename10, join as join26 } from "path";
|
|
4631
5143
|
var TOOL_ID16 = "qodercli";
|
|
4632
|
-
var
|
|
4633
|
-
var DEFAULT_PROJECTS_DIR =
|
|
4634
|
-
var DEFAULT_LOGS_DIR =
|
|
4635
|
-
var DEFAULT_RUNS_DIR =
|
|
5144
|
+
var TOOL_NAME15 = "Qoder CLI";
|
|
5145
|
+
var DEFAULT_PROJECTS_DIR = join26(homedir25(), ".qoder", "projects");
|
|
5146
|
+
var DEFAULT_LOGS_DIR = join26(homedir25(), ".qoder", "logs", "sessions");
|
|
5147
|
+
var DEFAULT_RUNS_DIR = join26(homedir25(), ".qoder", "logs", "runs");
|
|
4636
5148
|
var CLI_ENTRYPOINT = "cli";
|
|
4637
5149
|
var IDE_ENTRYPOINT = "acp";
|
|
4638
5150
|
var CREDIT_MODEL_FALLBACK = "credits";
|
|
4639
|
-
function
|
|
5151
|
+
function createToolDefinition11(projectsDir) {
|
|
4640
5152
|
return {
|
|
4641
5153
|
id: TOOL_ID16,
|
|
4642
|
-
name:
|
|
5154
|
+
name: TOOL_NAME15,
|
|
4643
5155
|
dataDir: projectsDir
|
|
4644
5156
|
};
|
|
4645
5157
|
}
|
|
4646
|
-
function
|
|
5158
|
+
function toSafeNumber11(value) {
|
|
4647
5159
|
const numberValue = Number(value);
|
|
4648
5160
|
return Number.isFinite(numberValue) ? numberValue : 0;
|
|
4649
5161
|
}
|
|
@@ -4678,13 +5190,13 @@ function classifyQoderEntrypoint(content) {
|
|
|
4678
5190
|
return "unknown";
|
|
4679
5191
|
}
|
|
4680
5192
|
function isQodercliBinaryPresent() {
|
|
4681
|
-
const home =
|
|
5193
|
+
const home = homedir25();
|
|
4682
5194
|
const candidates = [
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
5195
|
+
join26(home, ".local", "bin", "qodercli"),
|
|
5196
|
+
join26(home, ".qoder", "bin", "qodercli"),
|
|
5197
|
+
join26(home, ".qoder-cli")
|
|
4686
5198
|
];
|
|
4687
|
-
return candidates.some((path) =>
|
|
5199
|
+
return candidates.some((path) => existsSync25(path));
|
|
4688
5200
|
}
|
|
4689
5201
|
function projectFromEncodedSlug(slug) {
|
|
4690
5202
|
const parts = slug.split("-").filter(Boolean);
|
|
@@ -4692,11 +5204,11 @@ function projectFromEncodedSlug(slug) {
|
|
|
4692
5204
|
}
|
|
4693
5205
|
function projectFromCwd(cwd) {
|
|
4694
5206
|
if (!cwd) return "unknown";
|
|
4695
|
-
const leaf =
|
|
5207
|
+
const leaf = basename10(cwd.replace(/[\\/]+$/, ""));
|
|
4696
5208
|
return leaf || "unknown";
|
|
4697
5209
|
}
|
|
4698
5210
|
function totalCreditsUsed(quota) {
|
|
4699
|
-
return
|
|
5211
|
+
return toSafeNumber11(quota.userQuota?.used) + toSafeNumber11(quota.orgResourcePackage?.used) + toSafeNumber11(quota.addOnQuota?.used);
|
|
4700
5212
|
}
|
|
4701
5213
|
function parseRunManifest(content) {
|
|
4702
5214
|
if (!content) return null;
|
|
@@ -4776,7 +5288,7 @@ var QoderCliParser = class {
|
|
|
4776
5288
|
this.projectsDir = projectsDir;
|
|
4777
5289
|
this.logsDir = logsDir;
|
|
4778
5290
|
this.runsDir = runsDir;
|
|
4779
|
-
this.tool =
|
|
5291
|
+
this.tool = createToolDefinition11(projectsDir);
|
|
4780
5292
|
}
|
|
4781
5293
|
projectsDir;
|
|
4782
5294
|
logsDir;
|
|
@@ -4793,9 +5305,9 @@ var QoderCliParser = class {
|
|
|
4793
5305
|
};
|
|
4794
5306
|
}
|
|
4795
5307
|
parseProjectSessions(sessionEvents) {
|
|
4796
|
-
if (!
|
|
5308
|
+
if (!existsSync25(this.projectsDir)) return;
|
|
4797
5309
|
for (const filePath of findJsonlFiles(this.projectsDir)) {
|
|
4798
|
-
if (
|
|
5310
|
+
if (basename10(filePath).startsWith("verified-")) continue;
|
|
4799
5311
|
const content = readFileSafe(filePath);
|
|
4800
5312
|
if (!content) continue;
|
|
4801
5313
|
if (classifyQoderEntrypoint(content) !== CLI_ENTRYPOINT) continue;
|
|
@@ -4830,7 +5342,7 @@ var QoderCliParser = class {
|
|
|
4830
5342
|
* There is no token data here — only quota/usage credit totals.
|
|
4831
5343
|
*/
|
|
4832
5344
|
parseCreditDeltas() {
|
|
4833
|
-
if (!
|
|
5345
|
+
if (!existsSync25(this.runsDir)) return [];
|
|
4834
5346
|
let runDirs;
|
|
4835
5347
|
try {
|
|
4836
5348
|
runDirs = readdirSync12(this.runsDir, { withFileTypes: true }).filter(
|
|
@@ -4841,15 +5353,15 @@ var QoderCliParser = class {
|
|
|
4841
5353
|
}
|
|
4842
5354
|
const allSnapshots = [];
|
|
4843
5355
|
for (const dir of runDirs) {
|
|
4844
|
-
const runPath =
|
|
4845
|
-
const logPath =
|
|
4846
|
-
if (!
|
|
5356
|
+
const runPath = join26(this.runsDir, dir.name);
|
|
5357
|
+
const logPath = join26(runPath, "qodercli.log");
|
|
5358
|
+
if (!existsSync25(logPath)) continue;
|
|
4847
5359
|
const logContent = readFileSafe(logPath);
|
|
4848
5360
|
if (!logContent?.includes("quota/usage response:")) {
|
|
4849
5361
|
continue;
|
|
4850
5362
|
}
|
|
4851
5363
|
const manifest = parseRunManifest(
|
|
4852
|
-
readFileSafe(
|
|
5364
|
+
readFileSafe(join26(runPath, "manifest.json"))
|
|
4853
5365
|
);
|
|
4854
5366
|
const project = manifest?.project_id ? projectFromEncodedSlug(manifest.project_id) : projectFromCwd(manifest?.cwd);
|
|
4855
5367
|
const snapshots = extractCreditSnapshotsFromLog(logContent, {
|
|
@@ -4861,67 +5373,72 @@ var QoderCliParser = class {
|
|
|
4861
5373
|
return creditSnapshotsToEntries(allSnapshots);
|
|
4862
5374
|
}
|
|
4863
5375
|
isInstalled() {
|
|
4864
|
-
return isQodercliBinaryPresent() ||
|
|
5376
|
+
return isQodercliBinaryPresent() || existsSync25(this.runsDir) || existsSync25(this.logsDir) || existsSync25(this.projectsDir);
|
|
4865
5377
|
}
|
|
4866
5378
|
};
|
|
4867
5379
|
registerParser(new QoderCliParser());
|
|
4868
5380
|
|
|
4869
5381
|
// src/parsers/grok-build.ts
|
|
4870
|
-
import { existsSync as
|
|
4871
|
-
import { homedir as
|
|
4872
|
-
import { basename as
|
|
5382
|
+
import { existsSync as existsSync26, readdirSync as readdirSync13 } from "fs";
|
|
5383
|
+
import { homedir as homedir26 } from "os";
|
|
5384
|
+
import { basename as basename11, join as join27 } from "path";
|
|
4873
5385
|
var TOOL_ID17 = "grok-build";
|
|
4874
|
-
var
|
|
4875
|
-
var DEFAULT_DATA_DIR7 =
|
|
4876
|
-
|
|
5386
|
+
var TOOL_NAME16 = "Grok Build";
|
|
5387
|
+
var DEFAULT_DATA_DIR7 = join27(homedir26(), ".grok", "sessions");
|
|
5388
|
+
var DEFAULT_ARCHIVE_DIR2 = join27(homedir26(), ".grok", "archived_sessions");
|
|
5389
|
+
function createToolDefinition12(dataDir) {
|
|
4877
5390
|
return {
|
|
4878
5391
|
id: TOOL_ID17,
|
|
4879
|
-
name:
|
|
5392
|
+
name: TOOL_NAME16,
|
|
4880
5393
|
dataDir
|
|
4881
5394
|
};
|
|
4882
5395
|
}
|
|
4883
|
-
function
|
|
5396
|
+
function toSafeNumber12(value) {
|
|
4884
5397
|
const numberValue = Number(value);
|
|
4885
|
-
return Number.isFinite(numberValue) ? numberValue : 0;
|
|
5398
|
+
return Number.isFinite(numberValue) && numberValue >= 0 ? Math.trunc(numberValue) : 0;
|
|
4886
5399
|
}
|
|
4887
5400
|
function projectFromEncodedCwd(encoded) {
|
|
4888
5401
|
try {
|
|
4889
5402
|
const decoded = decodeURIComponent(encoded);
|
|
4890
|
-
const leaf =
|
|
5403
|
+
const leaf = basename11(decoded.replace(/[\\/]+$/, ""));
|
|
4891
5404
|
return leaf || "unknown";
|
|
4892
5405
|
} catch {
|
|
4893
|
-
const leaf =
|
|
5406
|
+
const leaf = basename11(encoded);
|
|
4894
5407
|
return leaf || "unknown";
|
|
4895
5408
|
}
|
|
4896
5409
|
}
|
|
4897
5410
|
function resolveTimestamp(obj) {
|
|
4898
5411
|
const ms = obj._meta?.agentTimestampMs ?? obj.params?.update?._meta?.agentTimestampMs;
|
|
4899
5412
|
if (typeof ms === "number" && Number.isFinite(ms)) {
|
|
4900
|
-
|
|
5413
|
+
const timestamp = new Date(ms);
|
|
5414
|
+
return Number.isNaN(timestamp.getTime()) ? null : timestamp;
|
|
4901
5415
|
}
|
|
4902
5416
|
const raw = obj.timestamp;
|
|
4903
5417
|
if (raw == null) return null;
|
|
4904
5418
|
if (typeof raw === "number") {
|
|
4905
|
-
|
|
5419
|
+
const timestamp = new Date(raw < 1e12 ? raw * 1e3 : raw);
|
|
5420
|
+
return Number.isNaN(timestamp.getTime()) ? null : timestamp;
|
|
4906
5421
|
}
|
|
4907
5422
|
const ts = new Date(raw);
|
|
4908
5423
|
return Number.isNaN(ts.getTime()) ? null : ts;
|
|
4909
5424
|
}
|
|
4910
5425
|
function pushUsageEntries(entries, args) {
|
|
4911
|
-
const { sessionId, project, timestamp, usage, fallbackModel } = args;
|
|
5426
|
+
const { eventKey, sessionId, project, timestamp, usage, fallbackModel } = args;
|
|
4912
5427
|
const modelUsage = usage.modelUsage;
|
|
4913
5428
|
const models = modelUsage && Object.keys(modelUsage).length > 0 ? Object.entries(modelUsage) : [[fallbackModel, usage]];
|
|
5429
|
+
const modelEntries = [];
|
|
4914
5430
|
for (const [model, mu] of models) {
|
|
4915
|
-
|
|
4916
|
-
const
|
|
4917
|
-
const rawInput =
|
|
4918
|
-
const rawOutput =
|
|
5431
|
+
if (!mu || typeof mu !== "object") continue;
|
|
5432
|
+
const cached = toSafeNumber12(mu.cachedReadTokens);
|
|
5433
|
+
const rawInput = toSafeNumber12(mu.inputTokens);
|
|
5434
|
+
const rawOutput = toSafeNumber12(mu.outputTokens);
|
|
5435
|
+
const reasoning = Math.min(rawOutput, toSafeNumber12(mu.reasoningTokens));
|
|
4919
5436
|
const inputTokens = Math.max(0, rawInput - cached);
|
|
4920
5437
|
const outputTokens = Math.max(0, rawOutput - reasoning);
|
|
4921
5438
|
if (inputTokens === 0 && outputTokens === 0 && cached === 0 && reasoning === 0) {
|
|
4922
5439
|
continue;
|
|
4923
5440
|
}
|
|
4924
|
-
|
|
5441
|
+
modelEntries.push({
|
|
4925
5442
|
sessionId,
|
|
4926
5443
|
source: TOOL_ID17,
|
|
4927
5444
|
model: model || fallbackModel || "unknown",
|
|
@@ -4933,10 +5450,11 @@ function pushUsageEntries(entries, args) {
|
|
|
4933
5450
|
cachedTokens: cached
|
|
4934
5451
|
});
|
|
4935
5452
|
}
|
|
5453
|
+
entries.set(eventKey, modelEntries);
|
|
4936
5454
|
}
|
|
4937
5455
|
function findSessionDirs(dataDir) {
|
|
4938
5456
|
const results = [];
|
|
4939
|
-
if (!
|
|
5457
|
+
if (!existsSync26(dataDir)) return results;
|
|
4940
5458
|
let projectDirs;
|
|
4941
5459
|
try {
|
|
4942
5460
|
projectDirs = readdirSync13(dataDir, { withFileTypes: true });
|
|
@@ -4946,7 +5464,7 @@ function findSessionDirs(dataDir) {
|
|
|
4946
5464
|
for (const projectEntry of projectDirs) {
|
|
4947
5465
|
if (!projectEntry.isDirectory()) continue;
|
|
4948
5466
|
if (projectEntry.name.endsWith(".sqlite")) continue;
|
|
4949
|
-
const projectDir =
|
|
5467
|
+
const projectDir = join27(dataDir, projectEntry.name);
|
|
4950
5468
|
const project = projectFromEncodedCwd(projectEntry.name);
|
|
4951
5469
|
let sessionEntries;
|
|
4952
5470
|
try {
|
|
@@ -4956,8 +5474,8 @@ function findSessionDirs(dataDir) {
|
|
|
4956
5474
|
}
|
|
4957
5475
|
for (const sessionEntry of sessionEntries) {
|
|
4958
5476
|
if (!sessionEntry.isDirectory()) continue;
|
|
4959
|
-
const sessionDir =
|
|
4960
|
-
if (!
|
|
5477
|
+
const sessionDir = join27(projectDir, sessionEntry.name);
|
|
5478
|
+
if (!existsSync26(join27(sessionDir, "updates.jsonl"))) continue;
|
|
4961
5479
|
results.push({
|
|
4962
5480
|
sessionDir,
|
|
4963
5481
|
project,
|
|
@@ -4968,7 +5486,7 @@ function findSessionDirs(dataDir) {
|
|
|
4968
5486
|
return results;
|
|
4969
5487
|
}
|
|
4970
5488
|
function readFallbackModel(sessionDir) {
|
|
4971
|
-
const summaryPath =
|
|
5489
|
+
const summaryPath = join27(sessionDir, "summary.json");
|
|
4972
5490
|
const content = readFileSafe(summaryPath);
|
|
4973
5491
|
if (!content) return "unknown";
|
|
4974
5492
|
try {
|
|
@@ -4979,24 +5497,29 @@ function readFallbackModel(sessionDir) {
|
|
|
4979
5497
|
}
|
|
4980
5498
|
}
|
|
4981
5499
|
var GrokBuildParser = class {
|
|
4982
|
-
constructor(dataDir = DEFAULT_DATA_DIR7) {
|
|
5500
|
+
constructor(dataDir = DEFAULT_DATA_DIR7, archiveDir = dataDir === DEFAULT_DATA_DIR7 ? DEFAULT_ARCHIVE_DIR2 : void 0) {
|
|
4983
5501
|
this.dataDir = dataDir;
|
|
4984
|
-
this.
|
|
5502
|
+
this.archiveDir = archiveDir;
|
|
5503
|
+
this.tool = createToolDefinition12(dataDir);
|
|
4985
5504
|
}
|
|
4986
5505
|
dataDir;
|
|
5506
|
+
archiveDir;
|
|
4987
5507
|
tool;
|
|
4988
5508
|
async parse() {
|
|
4989
|
-
const entries =
|
|
4990
|
-
const sessionEvents =
|
|
4991
|
-
const sessions =
|
|
5509
|
+
const entries = /* @__PURE__ */ new Map();
|
|
5510
|
+
const sessionEvents = /* @__PURE__ */ new Map();
|
|
5511
|
+
const sessions = [
|
|
5512
|
+
...this.archiveDir ? findSessionDirs(this.archiveDir) : [],
|
|
5513
|
+
...findSessionDirs(this.dataDir)
|
|
5514
|
+
];
|
|
4992
5515
|
for (const { sessionDir, project, sessionId } of sessions) {
|
|
4993
|
-
const updatesPath =
|
|
5516
|
+
const updatesPath = join27(sessionDir, "updates.jsonl");
|
|
4994
5517
|
const content = readFileSafe(updatesPath);
|
|
4995
5518
|
if (!content) continue;
|
|
4996
5519
|
const fallbackModel = readFallbackModel(sessionDir);
|
|
4997
5520
|
const seenUserPrompts = /* @__PURE__ */ new Set();
|
|
4998
5521
|
const ANON_USER_OPEN = "__anon_user_open";
|
|
4999
|
-
for (const line of content.split("\n")) {
|
|
5522
|
+
for (const [lineNumber, line] of content.split("\n").entries()) {
|
|
5000
5523
|
if (!line.trim()) continue;
|
|
5001
5524
|
let obj;
|
|
5002
5525
|
try {
|
|
@@ -5004,8 +5527,11 @@ var GrokBuildParser = class {
|
|
|
5004
5527
|
} catch {
|
|
5005
5528
|
continue;
|
|
5006
5529
|
}
|
|
5530
|
+
if (!obj || typeof obj !== "object") continue;
|
|
5531
|
+
if (obj.method !== "session/update" && obj.method !== "_x.ai/session/update")
|
|
5532
|
+
continue;
|
|
5007
5533
|
const update = obj.params?.update;
|
|
5008
|
-
if (!update
|
|
5534
|
+
if (!update) continue;
|
|
5009
5535
|
const ts = resolveTimestamp(obj);
|
|
5010
5536
|
if (!ts) continue;
|
|
5011
5537
|
const sid = obj.params?.sessionId || sessionId;
|
|
@@ -5015,18 +5541,30 @@ var GrokBuildParser = class {
|
|
|
5015
5541
|
const key = promptId ?? ANON_USER_OPEN;
|
|
5016
5542
|
if (seenUserPrompts.has(key)) continue;
|
|
5017
5543
|
seenUserPrompts.add(key);
|
|
5018
|
-
sessionEvents.
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5544
|
+
sessionEvents.set(
|
|
5545
|
+
JSON.stringify([
|
|
5546
|
+
sid,
|
|
5547
|
+
"user",
|
|
5548
|
+
key,
|
|
5549
|
+
promptId ? null : ts.toISOString()
|
|
5550
|
+
]),
|
|
5551
|
+
{
|
|
5552
|
+
sessionId: sid,
|
|
5553
|
+
source: TOOL_ID17,
|
|
5554
|
+
project,
|
|
5555
|
+
timestamp: ts,
|
|
5556
|
+
role: "user"
|
|
5557
|
+
}
|
|
5558
|
+
);
|
|
5025
5559
|
continue;
|
|
5026
5560
|
}
|
|
5027
|
-
if (updateType !== "turn_completed") continue;
|
|
5561
|
+
if (updateType && updateType !== "turn_completed") continue;
|
|
5562
|
+
if (!updateType && !update.usage) continue;
|
|
5028
5563
|
seenUserPrompts.delete(ANON_USER_OPEN);
|
|
5029
|
-
|
|
5564
|
+
const eventKey = promptId ? JSON.stringify([sid, promptId]) : JSON.stringify([sid, "anonymous", updatesPath, lineNumber]);
|
|
5565
|
+
if ((sessionEvents.get(eventKey)?.timestamp.getTime() ?? -Infinity) > ts.getTime())
|
|
5566
|
+
continue;
|
|
5567
|
+
sessionEvents.set(eventKey, {
|
|
5030
5568
|
sessionId: sid,
|
|
5031
5569
|
source: TOOL_ID17,
|
|
5032
5570
|
project,
|
|
@@ -5036,6 +5574,7 @@ var GrokBuildParser = class {
|
|
|
5036
5574
|
const usage = update.usage;
|
|
5037
5575
|
if (!usage) continue;
|
|
5038
5576
|
pushUsageEntries(entries, {
|
|
5577
|
+
eventKey,
|
|
5039
5578
|
sessionId: sid,
|
|
5040
5579
|
project,
|
|
5041
5580
|
timestamp: ts,
|
|
@@ -5044,28 +5583,29 @@ var GrokBuildParser = class {
|
|
|
5044
5583
|
});
|
|
5045
5584
|
}
|
|
5046
5585
|
}
|
|
5586
|
+
const usageEntries = [...entries.values()].flat();
|
|
5047
5587
|
return {
|
|
5048
|
-
buckets: aggregateToBuckets(
|
|
5049
|
-
sessions: extractSessions(sessionEvents,
|
|
5588
|
+
buckets: aggregateToBuckets(usageEntries),
|
|
5589
|
+
sessions: extractSessions([...sessionEvents.values()], usageEntries)
|
|
5050
5590
|
};
|
|
5051
5591
|
}
|
|
5052
5592
|
isInstalled() {
|
|
5053
|
-
return
|
|
5593
|
+
return existsSync26(this.dataDir) || Boolean(this.archiveDir && existsSync26(this.archiveDir)) || existsSync26(join27(homedir26(), ".grok")) || existsSync26(join27(homedir26(), ".local", "bin", "grok"));
|
|
5054
5594
|
}
|
|
5055
5595
|
};
|
|
5056
5596
|
registerParser(new GrokBuildParser());
|
|
5057
5597
|
|
|
5058
5598
|
// src/parsers/atomcode.ts
|
|
5059
|
-
import { existsSync as
|
|
5060
|
-
import { homedir as
|
|
5061
|
-
import { basename as
|
|
5599
|
+
import { existsSync as existsSync27 } from "fs";
|
|
5600
|
+
import { homedir as homedir27 } from "os";
|
|
5601
|
+
import { basename as basename12, join as join28 } from "path";
|
|
5062
5602
|
var TOOL_ID18 = "atomcode";
|
|
5063
|
-
var
|
|
5064
|
-
var DEFAULT_SESSIONS_DIR6 =
|
|
5603
|
+
var TOOL_NAME17 = "AtomCode";
|
|
5604
|
+
var DEFAULT_SESSIONS_DIR6 = join28(homedir27(), ".atomcode", "sessions");
|
|
5065
5605
|
function getAtomCodeSessionsDirs(env = process.env) {
|
|
5066
5606
|
const dirs = [
|
|
5067
5607
|
env.TOKEN_ARENA_ATOMCODE_DIR,
|
|
5068
|
-
env.ATOMCODE_HOME ?
|
|
5608
|
+
env.ATOMCODE_HOME ? join28(env.ATOMCODE_HOME, "sessions") : void 0,
|
|
5069
5609
|
DEFAULT_SESSIONS_DIR6
|
|
5070
5610
|
].filter((value) => Boolean(value));
|
|
5071
5611
|
return Array.from(new Set(dirs));
|
|
@@ -5074,7 +5614,7 @@ function toNonNegativeNumber3(value) {
|
|
|
5074
5614
|
const numberValue = Number(value);
|
|
5075
5615
|
return Number.isFinite(numberValue) && numberValue >= 0 ? numberValue : 0;
|
|
5076
5616
|
}
|
|
5077
|
-
function
|
|
5617
|
+
function parseTimestamp4(value) {
|
|
5078
5618
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5079
5619
|
const timestamp = new Date(value);
|
|
5080
5620
|
return Number.isNaN(timestamp.getTime()) ? null : timestamp;
|
|
@@ -5113,7 +5653,7 @@ function getMetaModel(meta) {
|
|
|
5113
5653
|
}
|
|
5114
5654
|
function getMetaProject(meta) {
|
|
5115
5655
|
if (typeof meta?.working_dir === "string" && meta.working_dir) {
|
|
5116
|
-
return
|
|
5656
|
+
return basename12(meta.working_dir) || "unknown";
|
|
5117
5657
|
}
|
|
5118
5658
|
return "unknown";
|
|
5119
5659
|
}
|
|
@@ -5124,7 +5664,7 @@ var AtomCodeParser = class {
|
|
|
5124
5664
|
this.sessionsDirs = sessionsDir ? [sessionsDir] : getAtomCodeSessionsDirs();
|
|
5125
5665
|
this.tool = {
|
|
5126
5666
|
id: TOOL_ID18,
|
|
5127
|
-
name:
|
|
5667
|
+
name: TOOL_NAME17,
|
|
5128
5668
|
dataDir: this.sessionsDirs[0] ?? DEFAULT_SESSIONS_DIR6
|
|
5129
5669
|
};
|
|
5130
5670
|
}
|
|
@@ -5146,7 +5686,7 @@ var AtomCodeParser = class {
|
|
|
5146
5686
|
const model = getMetaModel(meta);
|
|
5147
5687
|
for (const row of rows) {
|
|
5148
5688
|
const sessionId = typeof row.session_id === "string" && row.session_id ? row.session_id : fallbackSessionId;
|
|
5149
|
-
const timestamp =
|
|
5689
|
+
const timestamp = parseTimestamp4(row.ts);
|
|
5150
5690
|
if (!timestamp) continue;
|
|
5151
5691
|
if (row.user !== void 0 || row.assistant !== void 0) {
|
|
5152
5692
|
sessionEvents.push({
|
|
@@ -5198,24 +5738,24 @@ var AtomCodeParser = class {
|
|
|
5198
5738
|
};
|
|
5199
5739
|
}
|
|
5200
5740
|
isInstalled() {
|
|
5201
|
-
return this.sessionsDirs.some((dir) =>
|
|
5741
|
+
return this.sessionsDirs.some((dir) => existsSync27(dir));
|
|
5202
5742
|
}
|
|
5203
5743
|
};
|
|
5204
5744
|
registerParser(new AtomCodeParser());
|
|
5205
5745
|
|
|
5206
5746
|
// src/parsers/dsh.ts
|
|
5207
|
-
import { existsSync as
|
|
5208
|
-
import { homedir as
|
|
5209
|
-
import { basename as
|
|
5747
|
+
import { existsSync as existsSync28, readdirSync as readdirSync14, readFileSync as readFileSync9 } from "fs";
|
|
5748
|
+
import { homedir as homedir28 } from "os";
|
|
5749
|
+
import { basename as basename13, join as join29 } from "path";
|
|
5210
5750
|
import * as zlib from "zlib";
|
|
5211
5751
|
var TOOL_ID19 = "dsh";
|
|
5212
|
-
var
|
|
5213
|
-
var DEFAULT_SESSIONS_DIR7 =
|
|
5752
|
+
var TOOL_NAME18 = "DeepSeek Harness";
|
|
5753
|
+
var DEFAULT_SESSIONS_DIR7 = join29(homedir28(), ".dsh", "sessions");
|
|
5214
5754
|
var LOG_BASENAME = "session";
|
|
5215
5755
|
function getDshSessionsDirs(env = process.env) {
|
|
5216
5756
|
const dirs = [
|
|
5217
5757
|
env.TOKEN_ARENA_DSH_DIR,
|
|
5218
|
-
env.DSH_HOME ?
|
|
5758
|
+
env.DSH_HOME ? join29(env.DSH_HOME, "sessions") : void 0,
|
|
5219
5759
|
DEFAULT_SESSIONS_DIR7
|
|
5220
5760
|
].filter((value) => Boolean(value));
|
|
5221
5761
|
return Array.from(new Set(dirs));
|
|
@@ -5304,10 +5844,10 @@ function readSessionLog(filePath) {
|
|
|
5304
5844
|
}
|
|
5305
5845
|
function findSessionLogs(dir) {
|
|
5306
5846
|
const results = [];
|
|
5307
|
-
if (!
|
|
5847
|
+
if (!existsSync28(dir)) return results;
|
|
5308
5848
|
try {
|
|
5309
5849
|
for (const entry of readdirSync14(dir, { withFileTypes: true })) {
|
|
5310
|
-
const fullPath =
|
|
5850
|
+
const fullPath = join29(dir, entry.name);
|
|
5311
5851
|
if (entry.isDirectory()) {
|
|
5312
5852
|
results.push(...findSessionLogs(fullPath));
|
|
5313
5853
|
} else if (entry.name === `${LOG_BASENAME}.jsonl` || entry.name === `${LOG_BASENAME}.jsonl.zstd`) {
|
|
@@ -5339,7 +5879,7 @@ var DshParser = class {
|
|
|
5339
5879
|
this.sessionsDirs = sessionsDir ? [sessionsDir] : getDshSessionsDirs();
|
|
5340
5880
|
this.tool = {
|
|
5341
5881
|
id: TOOL_ID19,
|
|
5342
|
-
name:
|
|
5882
|
+
name: TOOL_NAME18,
|
|
5343
5883
|
dataDir: this.sessionsDirs[0] ?? DEFAULT_SESSIONS_DIR7
|
|
5344
5884
|
};
|
|
5345
5885
|
}
|
|
@@ -5356,7 +5896,7 @@ var DshParser = class {
|
|
|
5356
5896
|
const relativeParts = filePath.slice(sessionsDir.length + 1).split(/[\\/]/);
|
|
5357
5897
|
const header = rows[0]?.type === "session" ? rows[0] : void 0;
|
|
5358
5898
|
const sessionId = (header?.id ?? decodeSegment(relativeParts[1] ?? "")) || "unknown";
|
|
5359
|
-
const project = typeof header?.cwd === "string" && header.cwd ?
|
|
5899
|
+
const project = typeof header?.cwd === "string" && header.cwd ? basename13(header.cwd) || "unknown" : projectFromDirName(relativeParts[0] ?? "");
|
|
5360
5900
|
let currentModel = "unknown";
|
|
5361
5901
|
for (const row of rows) {
|
|
5362
5902
|
if (row.type === "request/context") {
|
|
@@ -5399,13 +5939,16 @@ var DshParser = class {
|
|
|
5399
5939
|
if (!usage || timestamp === null) continue;
|
|
5400
5940
|
const model = (isCompaction && typeof row.data?.model === "string" ? row.data.model : "") || currentModel;
|
|
5401
5941
|
const inputTokens = toNonNegativeNumber4(usage.inputTokens);
|
|
5402
|
-
const cachedTokens = toNonNegativeNumber4(usage.cacheReadTokens)
|
|
5942
|
+
const cachedTokens = toNonNegativeNumber4(usage.cacheReadTokens);
|
|
5943
|
+
const cacheCreationTokens = toNonNegativeNumber4(
|
|
5944
|
+
usage.cacheWriteTokens
|
|
5945
|
+
);
|
|
5403
5946
|
const reasoningTokens = toNonNegativeNumber4(usage.reasoningTokens);
|
|
5404
5947
|
const outputTokens = Math.max(
|
|
5405
5948
|
0,
|
|
5406
5949
|
toNonNegativeNumber4(usage.outputTokens) - reasoningTokens
|
|
5407
5950
|
);
|
|
5408
|
-
if (inputTokens + outputTokens + cachedTokens + reasoningTokens === 0)
|
|
5951
|
+
if (inputTokens + outputTokens + cachedTokens + cacheCreationTokens + reasoningTokens === 0)
|
|
5409
5952
|
continue;
|
|
5410
5953
|
const entryKey = [
|
|
5411
5954
|
sessionId,
|
|
@@ -5414,6 +5957,7 @@ var DshParser = class {
|
|
|
5414
5957
|
inputTokens,
|
|
5415
5958
|
outputTokens,
|
|
5416
5959
|
cachedTokens,
|
|
5960
|
+
cacheCreationTokens,
|
|
5417
5961
|
reasoningTokens
|
|
5418
5962
|
].join("|");
|
|
5419
5963
|
if (seenEntryKeys.has(entryKey)) continue;
|
|
@@ -5427,7 +5971,8 @@ var DshParser = class {
|
|
|
5427
5971
|
inputTokens,
|
|
5428
5972
|
outputTokens,
|
|
5429
5973
|
reasoningTokens,
|
|
5430
|
-
cachedTokens
|
|
5974
|
+
cachedTokens,
|
|
5975
|
+
cacheCreationTokens
|
|
5431
5976
|
});
|
|
5432
5977
|
}
|
|
5433
5978
|
}
|
|
@@ -5438,18 +5983,18 @@ var DshParser = class {
|
|
|
5438
5983
|
};
|
|
5439
5984
|
}
|
|
5440
5985
|
isInstalled() {
|
|
5441
|
-
return this.sessionsDirs.some((dir) =>
|
|
5986
|
+
return this.sessionsDirs.some((dir) => existsSync28(dir));
|
|
5442
5987
|
}
|
|
5443
5988
|
};
|
|
5444
5989
|
registerParser(new DshParser());
|
|
5445
5990
|
|
|
5446
5991
|
// src/parsers/cherry-studio.ts
|
|
5447
|
-
import { existsSync as
|
|
5448
|
-
import { homedir as
|
|
5449
|
-
import { dirname as dirname6, join as
|
|
5992
|
+
import { existsSync as existsSync29 } from "fs";
|
|
5993
|
+
import { homedir as homedir29 } from "os";
|
|
5994
|
+
import { dirname as dirname6, join as join30, resolve as resolve3 } from "path";
|
|
5450
5995
|
var TOOL_ID20 = "cherry-studio";
|
|
5451
|
-
var
|
|
5452
|
-
var DB_RELATIVE =
|
|
5996
|
+
var TOOL_NAME19 = "Cherry Studio";
|
|
5997
|
+
var DB_RELATIVE = join30("Data", "cherrystudio.sqlite");
|
|
5453
5998
|
var USAGE_QUERY = `SELECT
|
|
5454
5999
|
u.model_id as modelId,
|
|
5455
6000
|
u.no_cache_tokens as noCacheTokens,
|
|
@@ -5472,14 +6017,14 @@ var MESSAGES_QUERY3 = `SELECT
|
|
|
5472
6017
|
ORDER BY created_at`;
|
|
5473
6018
|
function getDefaultUserDataDir(env = process.env) {
|
|
5474
6019
|
if (process.platform === "darwin") {
|
|
5475
|
-
return
|
|
6020
|
+
return join30(homedir29(), "Library", "Application Support", "CherryStudio");
|
|
5476
6021
|
}
|
|
5477
6022
|
if (process.platform === "win32") {
|
|
5478
|
-
const appData = env.APPDATA?.trim() ||
|
|
5479
|
-
return
|
|
6023
|
+
const appData = env.APPDATA?.trim() || join30(homedir29(), "AppData", "Roaming");
|
|
6024
|
+
return join30(appData, "CherryStudio");
|
|
5480
6025
|
}
|
|
5481
|
-
const xdgConfigHome = env.XDG_CONFIG_HOME?.trim() ||
|
|
5482
|
-
return
|
|
6026
|
+
const xdgConfigHome = env.XDG_CONFIG_HOME?.trim() || join30(homedir29(), ".config");
|
|
6027
|
+
return join30(xdgConfigHome, "CherryStudio");
|
|
5483
6028
|
}
|
|
5484
6029
|
function getCherryStudioDbPaths(env = process.env) {
|
|
5485
6030
|
const paths = [];
|
|
@@ -5487,21 +6032,21 @@ function getCherryStudioDbPaths(env = process.env) {
|
|
|
5487
6032
|
if (explicit) {
|
|
5488
6033
|
const resolved = resolve3(explicit);
|
|
5489
6034
|
paths.push(
|
|
5490
|
-
resolved.endsWith(".sqlite") ? resolved :
|
|
6035
|
+
resolved.endsWith(".sqlite") ? resolved : join30(resolved, DB_RELATIVE)
|
|
5491
6036
|
);
|
|
5492
6037
|
}
|
|
5493
|
-
paths.push(
|
|
6038
|
+
paths.push(join30(getDefaultUserDataDir(env), DB_RELATIVE));
|
|
5494
6039
|
return Array.from(new Set(paths));
|
|
5495
6040
|
}
|
|
5496
6041
|
function resolveCherryStudioDbPath() {
|
|
5497
6042
|
const candidates = getCherryStudioDbPaths();
|
|
5498
|
-
return candidates.find((candidate) =>
|
|
6043
|
+
return candidates.find((candidate) => existsSync29(candidate)) ?? "";
|
|
5499
6044
|
}
|
|
5500
|
-
function
|
|
6045
|
+
function createToolDefinition13(dbPath) {
|
|
5501
6046
|
return {
|
|
5502
6047
|
id: TOOL_ID20,
|
|
5503
|
-
name:
|
|
5504
|
-
dataDir: dbPath ? dirname6(dbPath) :
|
|
6048
|
+
name: TOOL_NAME19,
|
|
6049
|
+
dataDir: dbPath ? dirname6(dbPath) : join30(getDefaultUserDataDir(), "Data")
|
|
5505
6050
|
};
|
|
5506
6051
|
}
|
|
5507
6052
|
function toSafeCount(value) {
|
|
@@ -5533,10 +6078,10 @@ var CherryStudioParser = class {
|
|
|
5533
6078
|
constructor(options = {}) {
|
|
5534
6079
|
this.dbPath = options.dbPath || resolveCherryStudioDbPath();
|
|
5535
6080
|
this.queryRows = options.queryRows || readSqliteRows;
|
|
5536
|
-
this.tool =
|
|
6081
|
+
this.tool = createToolDefinition13(this.dbPath);
|
|
5537
6082
|
}
|
|
5538
6083
|
async parse() {
|
|
5539
|
-
if (!this.dbPath || !
|
|
6084
|
+
if (!this.dbPath || !existsSync29(this.dbPath)) {
|
|
5540
6085
|
return { buckets: [], sessions: [] };
|
|
5541
6086
|
}
|
|
5542
6087
|
const usageRows = await this.queryRows(
|
|
@@ -5547,15 +6092,19 @@ var CherryStudioParser = class {
|
|
|
5547
6092
|
for (const row of usageRows) {
|
|
5548
6093
|
const timestamp = parseEpochMillis(row.createdAt);
|
|
5549
6094
|
if (!timestamp) continue;
|
|
5550
|
-
const cachedTokens = toSafeCount(row.cacheReadTokens)
|
|
6095
|
+
const cachedTokens = toSafeCount(row.cacheReadTokens);
|
|
6096
|
+
const cacheCreationTokens = toSafeCount(row.cacheWriteTokens);
|
|
5551
6097
|
const noCacheTokens = toOptionalCount(row.noCacheTokens);
|
|
5552
|
-
const inputTokens = noCacheTokens ?? Math.max(
|
|
6098
|
+
const inputTokens = noCacheTokens ?? Math.max(
|
|
6099
|
+
0,
|
|
6100
|
+
toSafeCount(row.inputTokens) - cachedTokens - cacheCreationTokens
|
|
6101
|
+
);
|
|
5553
6102
|
const reasoningTokens = toSafeCount(row.reasoningTokens);
|
|
5554
6103
|
const outputTokens = Math.max(
|
|
5555
6104
|
0,
|
|
5556
6105
|
toSafeCount(row.outputTokens) - reasoningTokens
|
|
5557
6106
|
);
|
|
5558
|
-
if (inputTokens + outputTokens + reasoningTokens + cachedTokens === 0) {
|
|
6107
|
+
if (inputTokens + outputTokens + reasoningTokens + cachedTokens + cacheCreationTokens === 0) {
|
|
5559
6108
|
continue;
|
|
5560
6109
|
}
|
|
5561
6110
|
entries.push({
|
|
@@ -5569,7 +6118,8 @@ var CherryStudioParser = class {
|
|
|
5569
6118
|
inputTokens,
|
|
5570
6119
|
outputTokens,
|
|
5571
6120
|
reasoningTokens,
|
|
5572
|
-
cachedTokens
|
|
6121
|
+
cachedTokens,
|
|
6122
|
+
cacheCreationTokens
|
|
5573
6123
|
});
|
|
5574
6124
|
}
|
|
5575
6125
|
let messageRows;
|
|
@@ -5606,7 +6156,7 @@ var CherryStudioParser = class {
|
|
|
5606
6156
|
};
|
|
5607
6157
|
}
|
|
5608
6158
|
isInstalled() {
|
|
5609
|
-
return Boolean(this.dbPath) &&
|
|
6159
|
+
return Boolean(this.dbPath) && existsSync29(this.dbPath);
|
|
5610
6160
|
}
|
|
5611
6161
|
};
|
|
5612
6162
|
registerParser(new CherryStudioParser());
|
|
@@ -5617,31 +6167,31 @@ import { Command, Option } from "commander";
|
|
|
5617
6167
|
// src/infrastructure/config/manager.ts
|
|
5618
6168
|
import { randomUUID } from "crypto";
|
|
5619
6169
|
import {
|
|
5620
|
-
existsSync as
|
|
6170
|
+
existsSync as existsSync30,
|
|
5621
6171
|
mkdirSync,
|
|
5622
6172
|
readFileSync as readFileSync10,
|
|
5623
6173
|
unlinkSync,
|
|
5624
6174
|
writeFileSync
|
|
5625
6175
|
} from "fs";
|
|
5626
|
-
import { join as
|
|
6176
|
+
import { join as join32 } from "path";
|
|
5627
6177
|
|
|
5628
6178
|
// src/infrastructure/xdg.ts
|
|
5629
|
-
import { homedir as
|
|
5630
|
-
import { join as
|
|
6179
|
+
import { homedir as homedir30 } from "os";
|
|
6180
|
+
import { join as join31 } from "path";
|
|
5631
6181
|
function getConfigHome() {
|
|
5632
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
6182
|
+
return process.env.XDG_CONFIG_HOME || join31(homedir30(), ".config");
|
|
5633
6183
|
}
|
|
5634
6184
|
function getStateHome() {
|
|
5635
|
-
return process.env.XDG_STATE_HOME ||
|
|
6185
|
+
return process.env.XDG_STATE_HOME || join31(homedir30(), ".local", "state");
|
|
5636
6186
|
}
|
|
5637
6187
|
function getRuntimeDir() {
|
|
5638
6188
|
return process.env.XDG_RUNTIME_DIR || getStateHome();
|
|
5639
6189
|
}
|
|
5640
6190
|
|
|
5641
6191
|
// src/infrastructure/config/manager.ts
|
|
5642
|
-
var CONFIG_DIR =
|
|
6192
|
+
var CONFIG_DIR = join32(getConfigHome(), "tokenarena");
|
|
5643
6193
|
var isDev = process.env.TOKEN_ARENA_DEV === "1";
|
|
5644
|
-
var CONFIG_FILE =
|
|
6194
|
+
var CONFIG_FILE = join32(CONFIG_DIR, isDev ? "config.dev.json" : "config.json");
|
|
5645
6195
|
var DEFAULT_API_URL = "https://token.guji.uno";
|
|
5646
6196
|
var VALID_CONFIG_KEYS = [
|
|
5647
6197
|
"apiKey",
|
|
@@ -5657,7 +6207,7 @@ function getConfigDir() {
|
|
|
5657
6207
|
return CONFIG_DIR;
|
|
5658
6208
|
}
|
|
5659
6209
|
function loadConfig() {
|
|
5660
|
-
if (!
|
|
6210
|
+
if (!existsSync30(CONFIG_FILE)) return null;
|
|
5661
6211
|
try {
|
|
5662
6212
|
const raw = readFileSync10(CONFIG_FILE, "utf-8");
|
|
5663
6213
|
const config = JSON.parse(raw);
|
|
@@ -5675,7 +6225,7 @@ function saveConfig(config) {
|
|
|
5675
6225
|
`, "utf-8");
|
|
5676
6226
|
}
|
|
5677
6227
|
function deleteConfig() {
|
|
5678
|
-
if (
|
|
6228
|
+
if (existsSync30(CONFIG_FILE)) {
|
|
5679
6229
|
unlinkSync(CONFIG_FILE);
|
|
5680
6230
|
}
|
|
5681
6231
|
}
|
|
@@ -6109,11 +6659,11 @@ function toProjectIdentity(input2) {
|
|
|
6109
6659
|
}
|
|
6110
6660
|
|
|
6111
6661
|
// src/domain/upload-manifest.ts
|
|
6112
|
-
import { createHash as
|
|
6662
|
+
import { createHash as createHash6 } from "crypto";
|
|
6113
6663
|
var MANIFEST_VERSION = 1;
|
|
6114
6664
|
var SNAPSHOT_PROTOCOL_VERSION = 1;
|
|
6115
6665
|
function fingerprint(value) {
|
|
6116
|
-
return
|
|
6666
|
+
return createHash6("sha256").update(value).digest("hex").slice(0, 16);
|
|
6117
6667
|
}
|
|
6118
6668
|
function normalizeApiUrl(apiUrl) {
|
|
6119
6669
|
let end = apiUrl.length;
|
|
@@ -6162,6 +6712,7 @@ function getUploadBucketContentHash(bucket) {
|
|
|
6162
6712
|
return fingerprint(
|
|
6163
6713
|
JSON.stringify({
|
|
6164
6714
|
cachedTokens: bucket.cachedTokens,
|
|
6715
|
+
cacheCreationTokens: bucket.cacheCreationTokens ?? 0,
|
|
6165
6716
|
hostname: bucket.hostname,
|
|
6166
6717
|
inputTokens: bucket.inputTokens,
|
|
6167
6718
|
outputTokens: bucket.outputTokens,
|
|
@@ -6176,6 +6727,7 @@ function getUploadSessionContentHash(session) {
|
|
|
6176
6727
|
JSON.stringify({
|
|
6177
6728
|
activeSeconds: session.activeSeconds,
|
|
6178
6729
|
cachedTokens: session.cachedTokens,
|
|
6730
|
+
cacheCreationTokens: session.cacheCreationTokens ?? 0,
|
|
6179
6731
|
durationSeconds: session.durationSeconds,
|
|
6180
6732
|
firstMessageAt: session.firstMessageAt,
|
|
6181
6733
|
hostname: session.hostname,
|
|
@@ -6492,7 +7044,7 @@ var ApiClient = class {
|
|
|
6492
7044
|
// src/infrastructure/runtime/lock.ts
|
|
6493
7045
|
import {
|
|
6494
7046
|
closeSync,
|
|
6495
|
-
existsSync as
|
|
7047
|
+
existsSync as existsSync31,
|
|
6496
7048
|
openSync,
|
|
6497
7049
|
readFileSync as readFileSync11,
|
|
6498
7050
|
rmSync as rmSync3,
|
|
@@ -6501,22 +7053,22 @@ import {
|
|
|
6501
7053
|
|
|
6502
7054
|
// src/infrastructure/runtime/paths.ts
|
|
6503
7055
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
6504
|
-
import { join as
|
|
7056
|
+
import { join as join33 } from "path";
|
|
6505
7057
|
var APP_NAME = "tokenarena";
|
|
6506
7058
|
function getRuntimeDirPath() {
|
|
6507
|
-
return
|
|
7059
|
+
return join33(getRuntimeDir(), APP_NAME);
|
|
6508
7060
|
}
|
|
6509
7061
|
function getStateDir() {
|
|
6510
|
-
return
|
|
7062
|
+
return join33(getStateHome(), APP_NAME);
|
|
6511
7063
|
}
|
|
6512
7064
|
function getSyncLockPath() {
|
|
6513
|
-
return
|
|
7065
|
+
return join33(getRuntimeDirPath(), "sync.lock");
|
|
6514
7066
|
}
|
|
6515
7067
|
function getSyncStatePath() {
|
|
6516
|
-
return
|
|
7068
|
+
return join33(getStateDir(), "status.json");
|
|
6517
7069
|
}
|
|
6518
7070
|
function getUploadManifestPath() {
|
|
6519
|
-
return
|
|
7071
|
+
return join33(getStateDir(), "upload-manifest.json");
|
|
6520
7072
|
}
|
|
6521
7073
|
function ensureAppDirs() {
|
|
6522
7074
|
mkdirSync2(getRuntimeDirPath(), { recursive: true });
|
|
@@ -6534,7 +7086,7 @@ function isProcessAlive(pid) {
|
|
|
6534
7086
|
}
|
|
6535
7087
|
}
|
|
6536
7088
|
function readLockMetadata(lockPath) {
|
|
6537
|
-
if (!
|
|
7089
|
+
if (!existsSync31(lockPath)) {
|
|
6538
7090
|
return null;
|
|
6539
7091
|
}
|
|
6540
7092
|
try {
|
|
@@ -6608,13 +7160,13 @@ function describeExistingSyncLock() {
|
|
|
6608
7160
|
}
|
|
6609
7161
|
|
|
6610
7162
|
// src/infrastructure/runtime/state.ts
|
|
6611
|
-
import { existsSync as
|
|
7163
|
+
import { existsSync as existsSync32, readFileSync as readFileSync12, writeFileSync as writeFileSync3 } from "fs";
|
|
6612
7164
|
function getDefaultState() {
|
|
6613
7165
|
return { status: "idle" };
|
|
6614
7166
|
}
|
|
6615
7167
|
function loadSyncState() {
|
|
6616
7168
|
const path = getSyncStatePath();
|
|
6617
|
-
if (!
|
|
7169
|
+
if (!existsSync32(path)) {
|
|
6618
7170
|
return getDefaultState();
|
|
6619
7171
|
}
|
|
6620
7172
|
try {
|
|
@@ -6675,7 +7227,7 @@ function markSyncFailed(source, error, status) {
|
|
|
6675
7227
|
}
|
|
6676
7228
|
|
|
6677
7229
|
// src/infrastructure/runtime/upload-manifest.ts
|
|
6678
|
-
import { existsSync as
|
|
7230
|
+
import { existsSync as existsSync33, readFileSync as readFileSync13, writeFileSync as writeFileSync4 } from "fs";
|
|
6679
7231
|
function isRecordOfStrings(value) {
|
|
6680
7232
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6681
7233
|
return false;
|
|
@@ -6691,7 +7243,7 @@ function isUploadManifest(value) {
|
|
|
6691
7243
|
}
|
|
6692
7244
|
function loadUploadManifest() {
|
|
6693
7245
|
const path = getUploadManifestPath();
|
|
6694
|
-
if (!
|
|
7246
|
+
if (!existsSync33(path)) {
|
|
6695
7247
|
return null;
|
|
6696
7248
|
}
|
|
6697
7249
|
try {
|
|
@@ -6725,11 +7277,19 @@ async function runAllParsers() {
|
|
|
6725
7277
|
const allBuckets = [];
|
|
6726
7278
|
const allSessions = [];
|
|
6727
7279
|
const parserResults = [];
|
|
7280
|
+
const failedSources = /* @__PURE__ */ new Set();
|
|
6728
7281
|
for (const parser of getAllParsers()) {
|
|
6729
7282
|
try {
|
|
6730
7283
|
const result = await parser.parse();
|
|
6731
7284
|
const buckets = result.buckets;
|
|
6732
7285
|
const sessions = result.sessions;
|
|
7286
|
+
if (result.incomplete) {
|
|
7287
|
+
failedSources.add(parser.tool.id);
|
|
7288
|
+
logger.warn(
|
|
7289
|
+
`${parser.tool.id} parser reported an incomplete scan; preserving its remote history.`
|
|
7290
|
+
);
|
|
7291
|
+
continue;
|
|
7292
|
+
}
|
|
6733
7293
|
if (buckets.length > 0) allBuckets.push(...buckets);
|
|
6734
7294
|
if (sessions.length > 0) allSessions.push(...sessions);
|
|
6735
7295
|
if (buckets.length > 0 || sessions.length > 0) {
|
|
@@ -6740,10 +7300,18 @@ async function runAllParsers() {
|
|
|
6740
7300
|
});
|
|
6741
7301
|
}
|
|
6742
7302
|
} catch (err) {
|
|
6743
|
-
|
|
7303
|
+
failedSources.add(parser.tool.id);
|
|
7304
|
+
logger.warn(
|
|
7305
|
+
`${parser.tool.id} parser failed: ${err instanceof Error ? err.message : String(err)}`
|
|
7306
|
+
);
|
|
6744
7307
|
}
|
|
6745
7308
|
}
|
|
6746
|
-
return {
|
|
7309
|
+
return {
|
|
7310
|
+
buckets: allBuckets,
|
|
7311
|
+
sessions: allSessions,
|
|
7312
|
+
parserResults,
|
|
7313
|
+
failedSources: Array.from(failedSources)
|
|
7314
|
+
};
|
|
6747
7315
|
}
|
|
6748
7316
|
function getDetectedTools() {
|
|
6749
7317
|
return detectInstalledTools();
|
|
@@ -6788,10 +7356,7 @@ function formatScopeChangeReason(reason) {
|
|
|
6788
7356
|
}
|
|
6789
7357
|
}
|
|
6790
7358
|
function resolveSnapshotResetDeviceId(input2) {
|
|
6791
|
-
if (!input2.previous) {
|
|
6792
|
-
return null;
|
|
6793
|
-
}
|
|
6794
|
-
if (input2.scopeChangedReasons.includes("server_or_api_key")) {
|
|
7359
|
+
if (!input2.previous || input2.scopeChangedReasons.includes("server_or_api_key")) {
|
|
6795
7360
|
return null;
|
|
6796
7361
|
}
|
|
6797
7362
|
if (input2.scopeChangedReasons.includes("project_identity") || input2.scopeChangedReasons.includes("snapshot_protocol")) {
|
|
@@ -6837,6 +7402,7 @@ function toUploadBuckets(buckets, settings, device) {
|
|
|
6837
7402
|
existing.outputTokens += bucket.outputTokens;
|
|
6838
7403
|
existing.reasoningTokens += bucket.reasoningTokens || 0;
|
|
6839
7404
|
existing.cachedTokens += bucket.cachedTokens || 0;
|
|
7405
|
+
existing.cacheCreationTokens = (existing.cacheCreationTokens ?? 0) + (bucket.cacheCreationTokens ?? 0);
|
|
6840
7406
|
existing.totalTokens += bucket.totalTokens;
|
|
6841
7407
|
continue;
|
|
6842
7408
|
}
|
|
@@ -6852,6 +7418,7 @@ function toUploadBuckets(buckets, settings, device) {
|
|
|
6852
7418
|
outputTokens: bucket.outputTokens,
|
|
6853
7419
|
reasoningTokens: bucket.reasoningTokens || 0,
|
|
6854
7420
|
cachedTokens: bucket.cachedTokens || 0,
|
|
7421
|
+
cacheCreationTokens: bucket.cacheCreationTokens ?? 0,
|
|
6855
7422
|
totalTokens: bucket.totalTokens
|
|
6856
7423
|
});
|
|
6857
7424
|
}
|
|
@@ -6881,6 +7448,7 @@ function toUploadSessions(sessions, settings, device) {
|
|
|
6881
7448
|
outputTokens: session.outputTokens,
|
|
6882
7449
|
reasoningTokens: session.reasoningTokens,
|
|
6883
7450
|
cachedTokens: session.cachedTokens,
|
|
7451
|
+
cacheCreationTokens: session.cacheCreationTokens ?? 0,
|
|
6884
7452
|
totalTokens: session.totalTokens,
|
|
6885
7453
|
primaryModel: session.primaryModel,
|
|
6886
7454
|
modelUsages: session.modelUsages
|
|
@@ -6955,7 +7523,12 @@ function buildUploadBatches(device, buckets, sessions) {
|
|
|
6955
7523
|
return batches;
|
|
6956
7524
|
}
|
|
6957
7525
|
async function runSync(config, opts = {}) {
|
|
6958
|
-
const {
|
|
7526
|
+
const {
|
|
7527
|
+
quiet = false,
|
|
7528
|
+
source = "manual",
|
|
7529
|
+
throws = false,
|
|
7530
|
+
rebuild = false
|
|
7531
|
+
} = opts;
|
|
6959
7532
|
const lock = tryAcquireSyncLock(source);
|
|
6960
7533
|
if (!lock) {
|
|
6961
7534
|
const detail = describeExistingSyncLock();
|
|
@@ -6972,13 +7545,27 @@ async function runSync(config, opts = {}) {
|
|
|
6972
7545
|
let totalIngested = 0;
|
|
6973
7546
|
let totalSessionsSynced = 0;
|
|
6974
7547
|
let caughtError = null;
|
|
7548
|
+
let snapshotResetAttempted = false;
|
|
6975
7549
|
try {
|
|
6976
7550
|
const {
|
|
6977
7551
|
buckets: allBuckets,
|
|
6978
7552
|
sessions: allSessions,
|
|
6979
|
-
parserResults
|
|
7553
|
+
parserResults,
|
|
7554
|
+
failedSources = []
|
|
6980
7555
|
} = await runAllParsers();
|
|
7556
|
+
if (rebuild && failedSources.length > 0) {
|
|
7557
|
+
throw new SyncFailure(
|
|
7558
|
+
`Cannot rebuild: incomplete or failed parsers (${failedSources.join(", ")}). Remote history has not been changed.`,
|
|
7559
|
+
"error"
|
|
7560
|
+
);
|
|
7561
|
+
}
|
|
6981
7562
|
if (allBuckets.length === 0 && allSessions.length === 0) {
|
|
7563
|
+
if (rebuild) {
|
|
7564
|
+
throw new SyncFailure(
|
|
7565
|
+
"Cannot rebuild from an empty scan. Restore complete local logs first. Remote history has not been changed.",
|
|
7566
|
+
"error"
|
|
7567
|
+
);
|
|
7568
|
+
}
|
|
6982
7569
|
if (!quiet) {
|
|
6983
7570
|
logger.info("No new usage data found.");
|
|
6984
7571
|
}
|
|
@@ -7030,25 +7617,25 @@ async function runSync(config, opts = {}) {
|
|
|
7030
7617
|
scope: manifestScope,
|
|
7031
7618
|
sessions: uploadSessions
|
|
7032
7619
|
});
|
|
7033
|
-
const
|
|
7034
|
-
const changedSessions = uploadDiff.sessionsToUpload;
|
|
7035
|
-
const snapshotResetDeviceId = resolveSnapshotResetDeviceId({
|
|
7620
|
+
const snapshotResetDeviceId = rebuild ? device.deviceId : resolveSnapshotResetDeviceId({
|
|
7036
7621
|
previous: previousManifest,
|
|
7037
7622
|
scopeChangedReasons: uploadDiff.scopeChangedReasons
|
|
7038
7623
|
});
|
|
7039
|
-
if (
|
|
7624
|
+
if (snapshotResetDeviceId && failedSources.length > 0) {
|
|
7625
|
+
throw new SyncFailure(
|
|
7626
|
+
"Cannot replace the device snapshot: incomplete or failed parsers (" + failedSources.join(", ") + "). Remote history has not been changed.",
|
|
7627
|
+
"error"
|
|
7628
|
+
);
|
|
7629
|
+
}
|
|
7630
|
+
const changedBuckets = snapshotResetDeviceId ? uploadBuckets : uploadDiff.bucketsToUpload;
|
|
7631
|
+
const changedSessions = snapshotResetDeviceId ? uploadSessions : uploadDiff.sessionsToUpload;
|
|
7632
|
+
if (!rebuild && !quiet && uploadDiff.scopeChangedReasons.length > 0) {
|
|
7040
7633
|
const scopeReasons = uploadDiff.scopeChangedReasons.map(formatScopeChangeReason).join(", ");
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
);
|
|
7045
|
-
} else {
|
|
7046
|
-
logger.warn(
|
|
7047
|
-
`Upload scope changed (${scopeReasons}). TokenArena will upload the current snapshot again, but existing remote records from the previous scope will not be deleted automatically.`
|
|
7048
|
-
);
|
|
7049
|
-
}
|
|
7634
|
+
logger.warn(
|
|
7635
|
+
snapshotResetDeviceId ? `Upload scope changed (${scopeReasons}). The previous device snapshot will be replaced to apply the new privacy or protocol settings.` : `Upload scope changed (${scopeReasons}). Current records will be uploaded again; remote records from the previous server, account or device are preserved.`
|
|
7636
|
+
);
|
|
7050
7637
|
}
|
|
7051
|
-
if (!quiet && (uploadDiff.removedBuckets > 0 || uploadDiff.removedSessions > 0)) {
|
|
7638
|
+
if (!rebuild && !quiet && (uploadDiff.removedBuckets > 0 || uploadDiff.removedSessions > 0)) {
|
|
7052
7639
|
const parts = [];
|
|
7053
7640
|
if (uploadDiff.removedBuckets > 0) {
|
|
7054
7641
|
parts.push(`${uploadDiff.removedBuckets} buckets`);
|
|
@@ -7057,24 +7644,9 @@ async function runSync(config, opts = {}) {
|
|
|
7057
7644
|
parts.push(`${uploadDiff.removedSessions} sessions`);
|
|
7058
7645
|
}
|
|
7059
7646
|
logger.warn(
|
|
7060
|
-
`Detected ${parts.join(" + ")} that were present in the previous local snapshot but are missing now. Remote
|
|
7647
|
+
`Detected ${parts.join(" + ")} that were present in the previous local snapshot but are missing now. Remote history is preserved. Use tokenarena sync --rebuild to replace it only when complete local logs are available.`
|
|
7061
7648
|
);
|
|
7062
7649
|
}
|
|
7063
|
-
if (snapshotResetDeviceId) {
|
|
7064
|
-
const deleted = await apiClient.deleteDeviceData(snapshotResetDeviceId);
|
|
7065
|
-
if (!quiet) {
|
|
7066
|
-
const deletedParts = [];
|
|
7067
|
-
if (deleted.deletedBuckets > 0) {
|
|
7068
|
-
deletedParts.push(`${deleted.deletedBuckets} buckets`);
|
|
7069
|
-
}
|
|
7070
|
-
if (deleted.deletedSessions > 0) {
|
|
7071
|
-
deletedParts.push(`${deleted.deletedSessions} sessions`);
|
|
7072
|
-
}
|
|
7073
|
-
logger.info(
|
|
7074
|
-
deletedParts.length > 0 ? `Cleared ${deletedParts.join(" + ")} from the previous remote snapshot before re-uploading.` : "Previous remote snapshot was already empty before re-uploading."
|
|
7075
|
-
);
|
|
7076
|
-
}
|
|
7077
|
-
}
|
|
7078
7650
|
if (changedBuckets.length === 0 && changedSessions.length === 0) {
|
|
7079
7651
|
if (!quiet) {
|
|
7080
7652
|
const skippedParts = [];
|
|
@@ -7112,6 +7684,22 @@ async function runSync(config, opts = {}) {
|
|
|
7112
7684
|
(sum, size) => sum + size,
|
|
7113
7685
|
0
|
|
7114
7686
|
);
|
|
7687
|
+
if (snapshotResetDeviceId) {
|
|
7688
|
+
saveUploadManifest({
|
|
7689
|
+
...uploadDiff.nextManifest,
|
|
7690
|
+
// 自动隐私迁移保留旧 scope;清理中断后,下次同步仍会重试删除旧快照。
|
|
7691
|
+
scope: !rebuild && previousManifest ? previousManifest.scope : manifestScope,
|
|
7692
|
+
buckets: {},
|
|
7693
|
+
sessions: {}
|
|
7694
|
+
});
|
|
7695
|
+
snapshotResetAttempted = true;
|
|
7696
|
+
const deleted = await apiClient.deleteDeviceData(snapshotResetDeviceId);
|
|
7697
|
+
if (!quiet) {
|
|
7698
|
+
logger.info(
|
|
7699
|
+
`Replacing the device snapshot (${deleted.deletedBuckets} buckets and ${deleted.deletedSessions} sessions cleared).`
|
|
7700
|
+
);
|
|
7701
|
+
}
|
|
7702
|
+
}
|
|
7115
7703
|
let uploadedBytesBeforeBatch = 0;
|
|
7116
7704
|
if (!quiet) {
|
|
7117
7705
|
const parts = [];
|
|
@@ -7122,10 +7710,10 @@ async function runSync(config, opts = {}) {
|
|
|
7122
7710
|
parts.push(`${changedSessions.length} sessions`);
|
|
7123
7711
|
}
|
|
7124
7712
|
const skippedParts = [];
|
|
7125
|
-
if (uploadDiff.unchangedBuckets > 0) {
|
|
7713
|
+
if (!snapshotResetDeviceId && uploadDiff.unchangedBuckets > 0) {
|
|
7126
7714
|
skippedParts.push(`${uploadDiff.unchangedBuckets} unchanged buckets`);
|
|
7127
7715
|
}
|
|
7128
|
-
if (uploadDiff.unchangedSessions > 0) {
|
|
7716
|
+
if (!snapshotResetDeviceId && uploadDiff.unchangedSessions > 0) {
|
|
7129
7717
|
skippedParts.push(`${uploadDiff.unchangedSessions} unchanged sessions`);
|
|
7130
7718
|
}
|
|
7131
7719
|
logger.info(
|
|
@@ -7199,9 +7787,9 @@ View your dashboard at: ${apiUrl}/usage`);
|
|
|
7199
7787
|
);
|
|
7200
7788
|
} else if (error instanceof SyncFailure) {
|
|
7201
7789
|
caughtError = error;
|
|
7202
|
-
} else if (totalIngested > 0) {
|
|
7790
|
+
} else if (totalIngested > 0 || totalSessionsSynced > 0) {
|
|
7203
7791
|
caughtError = new SyncFailure(
|
|
7204
|
-
`Sync partially completed (${totalIngested} buckets uploaded). ${httpErr.message}`,
|
|
7792
|
+
`Sync partially completed (${totalIngested} buckets and ${totalSessionsSynced} sessions uploaded). ${httpErr.message}`,
|
|
7205
7793
|
"error",
|
|
7206
7794
|
error
|
|
7207
7795
|
);
|
|
@@ -7212,6 +7800,13 @@ View your dashboard at: ${apiUrl}/usage`);
|
|
|
7212
7800
|
error
|
|
7213
7801
|
);
|
|
7214
7802
|
}
|
|
7803
|
+
if (snapshotResetAttempted) {
|
|
7804
|
+
caughtError = new SyncFailure(
|
|
7805
|
+
(rebuild ? "Rebuild" : "Snapshot replacement") + " did not finish. Run tokenarena sync to retry. " + caughtError.message,
|
|
7806
|
+
caughtError.kind,
|
|
7807
|
+
caughtError.causeError
|
|
7808
|
+
);
|
|
7809
|
+
}
|
|
7215
7810
|
markSyncFailed(source, caughtError.message, caughtError.kind);
|
|
7216
7811
|
} finally {
|
|
7217
7812
|
lock.release();
|
|
@@ -7231,18 +7826,18 @@ View your dashboard at: ${apiUrl}/usage`);
|
|
|
7231
7826
|
|
|
7232
7827
|
// src/commands/init.ts
|
|
7233
7828
|
import { execFileSync as execFileSync7, spawn } from "child_process";
|
|
7234
|
-
import { existsSync as
|
|
7829
|
+
import { existsSync as existsSync36 } from "fs";
|
|
7235
7830
|
import { appendFile, mkdir, readFile } from "fs/promises";
|
|
7236
|
-
import { homedir as
|
|
7237
|
-
import { dirname as dirname7, join as
|
|
7831
|
+
import { homedir as homedir33, platform as platform5 } from "os";
|
|
7832
|
+
import { dirname as dirname7, join as join34, posix as posix3, win32 } from "path";
|
|
7238
7833
|
|
|
7239
7834
|
// src/infrastructure/service/index.ts
|
|
7240
7835
|
import { platform as platform4 } from "os";
|
|
7241
7836
|
|
|
7242
7837
|
// src/infrastructure/service/linux-systemd.ts
|
|
7243
7838
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
7244
|
-
import { existsSync as
|
|
7245
|
-
import { homedir as
|
|
7839
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync3, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
7840
|
+
import { homedir as homedir31, platform as platform2 } from "os";
|
|
7246
7841
|
import { posix } from "path";
|
|
7247
7842
|
|
|
7248
7843
|
// src/utils/command.ts
|
|
@@ -7292,11 +7887,17 @@ function getManagedServiceEnvironment(env = process.env) {
|
|
|
7292
7887
|
}
|
|
7293
7888
|
return next;
|
|
7294
7889
|
}
|
|
7890
|
+
var TYPESCRIPT_ENTRY_PATTERN = /\.[cm]?tsx?$/i;
|
|
7295
7891
|
function resolveManagedDaemonCommand(execPath = process.execPath, argv = process.argv) {
|
|
7296
7892
|
const scriptPath = argv[1];
|
|
7297
7893
|
if (!scriptPath) {
|
|
7298
7894
|
throw new Error("\u65E0\u6CD5\u89E3\u6790 CLI \u5165\u53E3\u8DEF\u5F84\uFF0C\u8BF7\u901A\u8FC7 tokenarena \u547D\u4EE4\u91CD\u65B0\u6267\u884C\u3002");
|
|
7299
7895
|
}
|
|
7896
|
+
if (TYPESCRIPT_ENTRY_PATTERN.test(scriptPath)) {
|
|
7897
|
+
throw new Error(
|
|
7898
|
+
"\u65E0\u6CD5\u901A\u8FC7\u672A\u6253\u5305\u7684 TypeScript \u5165\u53E3\u5B89\u88C5\u670D\u52A1\uFF0C\u8BF7\u5148\u6267\u884C pnpm build:cli\uFF0C\u518D\u4F7F\u7528\u6253\u5305\u540E\u7684 tokenarena \u547D\u4EE4\u3002"
|
|
7899
|
+
);
|
|
7900
|
+
}
|
|
7300
7901
|
return {
|
|
7301
7902
|
execPath,
|
|
7302
7903
|
args: [scriptPath, "daemon", "--service"]
|
|
@@ -7311,10 +7912,10 @@ function escapeXml(value) {
|
|
|
7311
7912
|
|
|
7312
7913
|
// src/infrastructure/service/linux-systemd.ts
|
|
7313
7914
|
var SYSTEMD_SERVICE_NAME = "tokenarena";
|
|
7314
|
-
function getLinuxSystemdServiceDir(homePath =
|
|
7915
|
+
function getLinuxSystemdServiceDir(homePath = homedir31()) {
|
|
7315
7916
|
return posix.join(homePath, ".config", "systemd", "user");
|
|
7316
7917
|
}
|
|
7317
|
-
function getLinuxSystemdServiceFile(homePath =
|
|
7918
|
+
function getLinuxSystemdServiceFile(homePath = homedir31()) {
|
|
7318
7919
|
return posix.join(
|
|
7319
7920
|
getLinuxSystemdServiceDir(homePath),
|
|
7320
7921
|
`${SYSTEMD_SERVICE_NAME}.service`
|
|
@@ -7371,7 +7972,7 @@ function ensureSystemdAvailable() {
|
|
|
7371
7972
|
}
|
|
7372
7973
|
function createLinuxSystemdServiceBackend() {
|
|
7373
7974
|
function isInstalled() {
|
|
7374
|
-
return
|
|
7975
|
+
return existsSync34(getLinuxSystemdServiceFile());
|
|
7375
7976
|
}
|
|
7376
7977
|
async function setup(skipPrompt = false) {
|
|
7377
7978
|
if (!ensureSystemdAvailable()) {
|
|
@@ -7496,7 +8097,7 @@ function createLinuxSystemdServiceBackend() {
|
|
|
7496
8097
|
}
|
|
7497
8098
|
async function uninstall(skipPrompt = false) {
|
|
7498
8099
|
const serviceFile = getLinuxSystemdServiceFile();
|
|
7499
|
-
if (!
|
|
8100
|
+
if (!existsSync34(serviceFile)) {
|
|
7500
8101
|
logger.info(formatBullet("\u670D\u52A1\u6587\u4EF6\u4E0D\u5B58\u5728\u3002", "warning"));
|
|
7501
8102
|
return;
|
|
7502
8103
|
}
|
|
@@ -7557,17 +8158,17 @@ function createLinuxSystemdServiceBackend() {
|
|
|
7557
8158
|
|
|
7558
8159
|
// src/infrastructure/service/macos-launchd.ts
|
|
7559
8160
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
7560
|
-
import { existsSync as
|
|
7561
|
-
import { homedir as
|
|
8161
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync4, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
8162
|
+
import { homedir as homedir32, platform as platform3 } from "os";
|
|
7562
8163
|
import { posix as posix2 } from "path";
|
|
7563
8164
|
var MACOS_LAUNCHD_LABEL = "com.guji.tokenarena";
|
|
7564
8165
|
function getCurrentUid() {
|
|
7565
8166
|
return typeof process.getuid === "function" ? process.getuid() : null;
|
|
7566
8167
|
}
|
|
7567
|
-
function getMacosLaunchAgentDir(homePath =
|
|
8168
|
+
function getMacosLaunchAgentDir(homePath = homedir32()) {
|
|
7568
8169
|
return posix2.join(homePath, "Library", "LaunchAgents");
|
|
7569
8170
|
}
|
|
7570
|
-
function getMacosLaunchAgentFile(homePath =
|
|
8171
|
+
function getMacosLaunchAgentFile(homePath = homedir32()) {
|
|
7571
8172
|
return posix2.join(
|
|
7572
8173
|
getMacosLaunchAgentDir(homePath),
|
|
7573
8174
|
`${MACOS_LAUNCHD_LABEL}.plist`
|
|
@@ -7694,7 +8295,7 @@ function writeLaunchAgentPlist() {
|
|
|
7694
8295
|
label: MACOS_LAUNCHD_LABEL,
|
|
7695
8296
|
programArguments: [command.execPath, ...command.args],
|
|
7696
8297
|
environment: getManagedServiceEnvironment(),
|
|
7697
|
-
workingDirectory:
|
|
8298
|
+
workingDirectory: homedir32(),
|
|
7698
8299
|
standardOutPath: stdoutPath,
|
|
7699
8300
|
standardErrorPath: stderrPath
|
|
7700
8301
|
});
|
|
@@ -7719,7 +8320,7 @@ function bootstrapLaunchAgent() {
|
|
|
7719
8320
|
}
|
|
7720
8321
|
function createMacosLaunchdServiceBackend() {
|
|
7721
8322
|
function isInstalled() {
|
|
7722
|
-
return
|
|
8323
|
+
return existsSync35(getMacosLaunchAgentFile());
|
|
7723
8324
|
}
|
|
7724
8325
|
async function setup(skipPrompt = false) {
|
|
7725
8326
|
if (!ensureLaunchctlAvailable()) {
|
|
@@ -7860,7 +8461,7 @@ function createMacosLaunchdServiceBackend() {
|
|
|
7860
8461
|
}
|
|
7861
8462
|
async function uninstall(skipPrompt = false) {
|
|
7862
8463
|
const plistFile = getMacosLaunchAgentFile();
|
|
7863
|
-
if (!
|
|
8464
|
+
if (!existsSync35(plistFile)) {
|
|
7864
8465
|
logger.info(formatBullet("\u670D\u52A1\u6587\u4EF6\u4E0D\u5B58\u5728\u3002", "warning"));
|
|
7865
8466
|
return;
|
|
7866
8467
|
}
|
|
@@ -7971,7 +8572,7 @@ function resolvePowerShellProfilePath() {
|
|
|
7971
8572
|
const systemRoot = process.env.SYSTEMROOT || "C:\\Windows";
|
|
7972
8573
|
const candidates = [
|
|
7973
8574
|
"pwsh.exe",
|
|
7974
|
-
|
|
8575
|
+
join34(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
|
|
7975
8576
|
];
|
|
7976
8577
|
for (const command of candidates) {
|
|
7977
8578
|
try {
|
|
@@ -8000,8 +8601,8 @@ function resolvePowerShellProfilePath() {
|
|
|
8000
8601
|
function resolveShellAliasSetup(options = {}) {
|
|
8001
8602
|
const currentPlatform = options.currentPlatform ?? platform5();
|
|
8002
8603
|
const env = options.env ?? process.env;
|
|
8003
|
-
const homeDir = options.homeDir ??
|
|
8004
|
-
const pathExists = options.exists ??
|
|
8604
|
+
const homeDir = options.homeDir ?? homedir33();
|
|
8605
|
+
const pathExists = options.exists ?? existsSync36;
|
|
8005
8606
|
const shellFromEnv = env.SHELL ? basenameLikeShell(env.SHELL).toLowerCase() : "";
|
|
8006
8607
|
const shellName = shellFromEnv || (currentPlatform === "win32" ? "powershell" : "");
|
|
8007
8608
|
const aliasName = "ta";
|
|
@@ -8198,7 +8799,7 @@ async function setupShellAlias() {
|
|
|
8198
8799
|
try {
|
|
8199
8800
|
await mkdir(dirname7(setup.configFile), { recursive: true });
|
|
8200
8801
|
let existingContent = "";
|
|
8201
|
-
if (
|
|
8802
|
+
if (existsSync36(setup.configFile)) {
|
|
8202
8803
|
existingContent = await readFile(setup.configFile, "utf-8");
|
|
8203
8804
|
}
|
|
8204
8805
|
const normalizedContent = existingContent.toLowerCase();
|
|
@@ -8300,6 +8901,7 @@ function createTotals() {
|
|
|
8300
8901
|
inputTokens: 0,
|
|
8301
8902
|
outputTokens: 0,
|
|
8302
8903
|
cachedTokens: 0,
|
|
8904
|
+
cacheCreationTokens: 0,
|
|
8303
8905
|
reasoningTokens: 0,
|
|
8304
8906
|
totalTokens: 0,
|
|
8305
8907
|
buckets: 0,
|
|
@@ -8312,6 +8914,7 @@ function addBucketToTotals(totals, bucket) {
|
|
|
8312
8914
|
totals.inputTokens += bucket.inputTokens || 0;
|
|
8313
8915
|
totals.outputTokens += bucket.outputTokens || 0;
|
|
8314
8916
|
totals.cachedTokens += bucket.cachedTokens || 0;
|
|
8917
|
+
totals.cacheCreationTokens += bucket.cacheCreationTokens || 0;
|
|
8315
8918
|
totals.reasoningTokens += bucket.reasoningTokens || 0;
|
|
8316
8919
|
totals.totalTokens += bucket.totalTokens || 0;
|
|
8317
8920
|
totals.buckets += 1;
|
|
@@ -8323,6 +8926,7 @@ function addBucketToRank(map, key, bucket) {
|
|
|
8323
8926
|
inputTokens: 0,
|
|
8324
8927
|
outputTokens: 0,
|
|
8325
8928
|
cachedTokens: 0,
|
|
8929
|
+
cacheCreationTokens: 0,
|
|
8326
8930
|
reasoningTokens: 0,
|
|
8327
8931
|
totalTokens: 0,
|
|
8328
8932
|
sessions: 0
|
|
@@ -8330,6 +8934,7 @@ function addBucketToRank(map, key, bucket) {
|
|
|
8330
8934
|
current.inputTokens += bucket.inputTokens || 0;
|
|
8331
8935
|
current.outputTokens += bucket.outputTokens || 0;
|
|
8332
8936
|
current.cachedTokens += bucket.cachedTokens || 0;
|
|
8937
|
+
current.cacheCreationTokens += bucket.cacheCreationTokens || 0;
|
|
8333
8938
|
current.reasoningTokens += bucket.reasoningTokens || 0;
|
|
8334
8939
|
current.totalTokens += bucket.totalTokens || 0;
|
|
8335
8940
|
map.set(name, current);
|
|
@@ -8369,6 +8974,7 @@ function buildToolSummary(input2) {
|
|
|
8369
8974
|
inputTokens: 0,
|
|
8370
8975
|
outputTokens: 0,
|
|
8371
8976
|
cachedTokens: 0,
|
|
8977
|
+
cacheCreationTokens: 0,
|
|
8372
8978
|
reasoningTokens: 0,
|
|
8373
8979
|
totalTokens: 0,
|
|
8374
8980
|
sessions: 1
|
|
@@ -8383,6 +8989,7 @@ function buildToolSummary(input2) {
|
|
|
8383
8989
|
inputTokens: 0,
|
|
8384
8990
|
outputTokens: 0,
|
|
8385
8991
|
cachedTokens: 0,
|
|
8992
|
+
cacheCreationTokens: 0,
|
|
8386
8993
|
reasoningTokens: 0,
|
|
8387
8994
|
totalTokens: 0,
|
|
8388
8995
|
sessions: 1
|
|
@@ -8456,27 +9063,38 @@ function buildLocalUsageDashboardData(input2) {
|
|
|
8456
9063
|
}
|
|
8457
9064
|
|
|
8458
9065
|
// src/infrastructure/runtime/cli-version.ts
|
|
8459
|
-
import {
|
|
8460
|
-
import { dirname as dirname8, join as join34 } from "path";
|
|
9066
|
+
import { dirname as dirname8, join as join35 } from "path";
|
|
8461
9067
|
import { fileURLToPath } from "url";
|
|
8462
9068
|
var FALLBACK_VERSION = "0.0.0";
|
|
8463
|
-
var
|
|
8464
|
-
function
|
|
8465
|
-
|
|
8466
|
-
|
|
9069
|
+
var versionCache = /* @__PURE__ */ new Map();
|
|
9070
|
+
function readPackageVersion(startDir) {
|
|
9071
|
+
let dir = startDir;
|
|
9072
|
+
while (true) {
|
|
9073
|
+
const contents = readFileSafe(join35(dir, "package.json"));
|
|
9074
|
+
if (contents !== null) {
|
|
9075
|
+
try {
|
|
9076
|
+
const { version } = JSON.parse(contents);
|
|
9077
|
+
return typeof version === "string" && version.length > 0 ? version : void 0;
|
|
9078
|
+
} catch {
|
|
9079
|
+
return void 0;
|
|
9080
|
+
}
|
|
9081
|
+
}
|
|
9082
|
+
const parent = dirname8(dir);
|
|
9083
|
+
if (parent === dir) {
|
|
9084
|
+
return void 0;
|
|
9085
|
+
}
|
|
9086
|
+
dir = parent;
|
|
8467
9087
|
}
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
)
|
|
8473
|
-
|
|
8474
|
-
const packageJson = JSON.parse(readFileSync14(packageJsonPath, "utf-8"));
|
|
8475
|
-
cachedVersion = typeof packageJson.version === "string" ? packageJson.version : FALLBACK_VERSION;
|
|
8476
|
-
} catch {
|
|
8477
|
-
cachedVersion = FALLBACK_VERSION;
|
|
9088
|
+
}
|
|
9089
|
+
function getCliVersion(metaUrl = import.meta.url) {
|
|
9090
|
+
const startDir = dirname8(fileURLToPath(metaUrl));
|
|
9091
|
+
const cached = versionCache.get(startDir);
|
|
9092
|
+
if (cached !== void 0) {
|
|
9093
|
+
return cached;
|
|
8478
9094
|
}
|
|
8479
|
-
|
|
9095
|
+
const version = readPackageVersion(startDir) ?? FALLBACK_VERSION;
|
|
9096
|
+
versionCache.set(startDir, version);
|
|
9097
|
+
return version;
|
|
8480
9098
|
}
|
|
8481
9099
|
|
|
8482
9100
|
// src/infrastructure/ui/local-usage-dashboard.ts
|
|
@@ -8599,7 +9217,7 @@ function renderOverview(tool) {
|
|
|
8599
9217
|
return [
|
|
8600
9218
|
`${bold("Overview")} ${dim(tool.source)}`,
|
|
8601
9219
|
` Tokens ${formatCompactNumber(tool.totals.totalTokens)} total \xB7 ${formatCompactNumber(tool.totals.inputTokens)} input \xB7 ${formatCompactNumber(tool.totals.outputTokens)} output`,
|
|
8602
|
-
` Details ${formatCompactNumber(tool.totals.cachedTokens)}
|
|
9220
|
+
` Details ${formatCompactNumber(tool.totals.cachedTokens)} cache read \xB7 ${formatCompactNumber(tool.totals.cacheCreationTokens)} cache write \xB7 ${formatCompactNumber(tool.totals.reasoningTokens)} reasoning`,
|
|
8603
9221
|
` Scope ${tool.totals.sessions} sessions \xB7 ${tool.totals.buckets} buckets \xB7 ${tool.totals.projects} projects \xB7 ${tool.totals.models} models`
|
|
8604
9222
|
];
|
|
8605
9223
|
}
|
|
@@ -8871,13 +9489,14 @@ async function runSyncCommand(opts = {}) {
|
|
|
8871
9489
|
}
|
|
8872
9490
|
await runSync(config, {
|
|
8873
9491
|
quiet: opts.quiet,
|
|
9492
|
+
rebuild: opts.rebuild,
|
|
8874
9493
|
source: "manual"
|
|
8875
9494
|
});
|
|
8876
9495
|
}
|
|
8877
9496
|
|
|
8878
9497
|
// src/commands/uninstall.ts
|
|
8879
|
-
import { existsSync as
|
|
8880
|
-
import { homedir as
|
|
9498
|
+
import { existsSync as existsSync37, readFileSync as readFileSync14, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
9499
|
+
import { homedir as homedir34, platform as platform6 } from "os";
|
|
8881
9500
|
function removeShellAlias() {
|
|
8882
9501
|
const shell = process.env.SHELL;
|
|
8883
9502
|
if (!shell) return;
|
|
@@ -8886,24 +9505,24 @@ function removeShellAlias() {
|
|
|
8886
9505
|
let configFile;
|
|
8887
9506
|
switch (shellName) {
|
|
8888
9507
|
case "zsh":
|
|
8889
|
-
configFile = `${
|
|
9508
|
+
configFile = `${homedir34()}/.zshrc`;
|
|
8890
9509
|
break;
|
|
8891
9510
|
case "bash":
|
|
8892
|
-
if (platform6() === "darwin" &&
|
|
8893
|
-
configFile = `${
|
|
9511
|
+
if (platform6() === "darwin" && existsSync37(`${homedir34()}/.bash_profile`)) {
|
|
9512
|
+
configFile = `${homedir34()}/.bash_profile`;
|
|
8894
9513
|
} else {
|
|
8895
|
-
configFile = `${
|
|
9514
|
+
configFile = `${homedir34()}/.bashrc`;
|
|
8896
9515
|
}
|
|
8897
9516
|
break;
|
|
8898
9517
|
case "fish":
|
|
8899
|
-
configFile = `${
|
|
9518
|
+
configFile = `${homedir34()}/.config/fish/config.fish`;
|
|
8900
9519
|
break;
|
|
8901
9520
|
default:
|
|
8902
9521
|
return;
|
|
8903
9522
|
}
|
|
8904
|
-
if (!
|
|
9523
|
+
if (!existsSync37(configFile)) return;
|
|
8905
9524
|
try {
|
|
8906
|
-
let content =
|
|
9525
|
+
let content = readFileSync14(configFile, "utf-8");
|
|
8907
9526
|
const aliasPatterns = [
|
|
8908
9527
|
// zsh / bash format: alias ta="tokenarena"
|
|
8909
9528
|
new RegExp(
|
|
@@ -8940,7 +9559,7 @@ async function runUninstall() {
|
|
|
8940
9559
|
const runtimeDir = getRuntimeDirPath();
|
|
8941
9560
|
const serviceBackend = getServiceBackend();
|
|
8942
9561
|
const hasInstalledService = serviceBackend?.isInstalled() ?? false;
|
|
8943
|
-
const hasLocalArtifacts =
|
|
9562
|
+
const hasLocalArtifacts = existsSync37(configPath) || existsSync37(configDir) || existsSync37(stateDir) || existsSync37(runtimeDir) || hasInstalledService;
|
|
8944
9563
|
if (!hasLocalArtifacts) {
|
|
8945
9564
|
logger.info(formatHeader("\u5378\u8F7D TokenArena"));
|
|
8946
9565
|
logger.info(formatBullet("\u672A\u53D1\u73B0\u672C\u5730\u914D\u7F6E\uFF0C\u65E0\u9700\u5378\u8F7D\u3002"));
|
|
@@ -8984,22 +9603,22 @@ async function runUninstall() {
|
|
|
8984
9603
|
}
|
|
8985
9604
|
}
|
|
8986
9605
|
logger.info(formatSection("\u6267\u884C\u7ED3\u679C"));
|
|
8987
|
-
if (
|
|
9606
|
+
if (existsSync37(configPath)) {
|
|
8988
9607
|
deleteConfig();
|
|
8989
9608
|
logger.info(formatBullet("\u5DF2\u5220\u9664\u914D\u7F6E\u6587\u4EF6\u3002", "success"));
|
|
8990
9609
|
}
|
|
8991
|
-
if (
|
|
9610
|
+
if (existsSync37(configDir)) {
|
|
8992
9611
|
try {
|
|
8993
9612
|
rmSync6(configDir, { recursive: false, force: true });
|
|
8994
9613
|
logger.info(formatBullet("\u5DF2\u5220\u9664\u914D\u7F6E\u76EE\u5F55\u3002", "success"));
|
|
8995
9614
|
} catch {
|
|
8996
9615
|
}
|
|
8997
9616
|
}
|
|
8998
|
-
if (
|
|
9617
|
+
if (existsSync37(stateDir)) {
|
|
8999
9618
|
rmSync6(stateDir, { recursive: true, force: true });
|
|
9000
9619
|
logger.info(formatBullet("\u5DF2\u5220\u9664\u72B6\u6001\u6570\u636E\u3002", "success"));
|
|
9001
9620
|
}
|
|
9002
|
-
if (
|
|
9621
|
+
if (existsSync37(runtimeDir)) {
|
|
9003
9622
|
rmSync6(runtimeDir, { recursive: true, force: true });
|
|
9004
9623
|
logger.info(formatBullet("\u5DF2\u5220\u9664\u8FD0\u884C\u65F6\u6570\u636E\u3002", "success"));
|
|
9005
9624
|
}
|
|
@@ -9202,7 +9821,10 @@ function createCli() {
|
|
|
9202
9821
|
program.command("init").description("Initialize configuration with API key").option("--api-url <url>", "Custom API server URL").action(async (opts) => {
|
|
9203
9822
|
await runInit(opts);
|
|
9204
9823
|
});
|
|
9205
|
-
program.command("sync").description("Manually sync usage data to server").
|
|
9824
|
+
program.command("sync").description("Manually sync usage data to server").option(
|
|
9825
|
+
"--rebuild",
|
|
9826
|
+
"Replace this device's remote history; requires complete local logs"
|
|
9827
|
+
).addOption(new Option("--quiet").hideHelp()).action(async (opts) => {
|
|
9206
9828
|
await runSyncCommand(opts);
|
|
9207
9829
|
});
|
|
9208
9830
|
program.command("daemon").description("Run continuous sync (every 30 minutes by default)").option("--interval <ms>", "Sync interval in milliseconds", parseInt).addOption(new Option("--service").hideHelp()).action(async (opts) => {
|
|
@@ -9230,10 +9852,10 @@ function createCli() {
|
|
|
9230
9852
|
}
|
|
9231
9853
|
|
|
9232
9854
|
// src/infrastructure/runtime/main-module.ts
|
|
9233
|
-
import { existsSync as
|
|
9855
|
+
import { existsSync as existsSync38, realpathSync as realpathSync2 } from "fs";
|
|
9234
9856
|
import { resolve as resolve4 } from "path";
|
|
9235
9857
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9236
|
-
function isMainModule(argvEntry = process.argv[1]
|
|
9858
|
+
function isMainModule(metaUrl, argvEntry = process.argv[1]) {
|
|
9237
9859
|
if (!argvEntry) {
|
|
9238
9860
|
return false;
|
|
9239
9861
|
}
|
|
@@ -9241,7 +9863,7 @@ function isMainModule(argvEntry = process.argv[1], metaUrl = import.meta.url) {
|
|
|
9241
9863
|
try {
|
|
9242
9864
|
return realpathSync2(argvEntry) === realpathSync2(currentModulePath);
|
|
9243
9865
|
} catch {
|
|
9244
|
-
if (!
|
|
9866
|
+
if (!existsSync38(argvEntry)) {
|
|
9245
9867
|
return false;
|
|
9246
9868
|
}
|
|
9247
9869
|
return resolve4(argvEntry) === resolve4(currentModulePath);
|
|
@@ -9249,6 +9871,7 @@ function isMainModule(argvEntry = process.argv[1], metaUrl = import.meta.url) {
|
|
|
9249
9871
|
}
|
|
9250
9872
|
|
|
9251
9873
|
// src/index.ts
|
|
9874
|
+
var EXIT_CANCELLED = 130;
|
|
9252
9875
|
function normalizeArgv(argv) {
|
|
9253
9876
|
return argv.filter((arg, index) => index < 2 || arg !== "--");
|
|
9254
9877
|
}
|
|
@@ -9256,11 +9879,20 @@ async function run(argv = process.argv) {
|
|
|
9256
9879
|
const program = createCli();
|
|
9257
9880
|
await program.parseAsync(normalizeArgv(argv));
|
|
9258
9881
|
}
|
|
9259
|
-
|
|
9260
|
-
|
|
9882
|
+
function reportFatalError(error) {
|
|
9883
|
+
if (error instanceof Error && error.name === "ExitPromptError") {
|
|
9884
|
+
process.exitCode = EXIT_CANCELLED;
|
|
9885
|
+
return;
|
|
9886
|
+
}
|
|
9887
|
+
logger.error(error instanceof Error ? error.message : String(error));
|
|
9888
|
+
process.exitCode = 1;
|
|
9889
|
+
}
|
|
9890
|
+
if (isMainModule(import.meta.url)) {
|
|
9891
|
+
run().catch(reportFatalError);
|
|
9261
9892
|
}
|
|
9262
9893
|
export {
|
|
9263
9894
|
normalizeArgv,
|
|
9895
|
+
reportFatalError,
|
|
9264
9896
|
run
|
|
9265
9897
|
};
|
|
9266
9898
|
//# sourceMappingURL=index.js.map
|