@yishiguji/tokenarena 0.16.6 → 0.18.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.js +722 -208
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -646,11 +646,11 @@ function readRollout(path) {
|
|
|
646
646
|
let minLineTimestampOrder = null;
|
|
647
647
|
let line = 0;
|
|
648
648
|
let maxOrdinal = null;
|
|
649
|
-
for (const
|
|
649
|
+
for (const text2 of content.split("\n")) {
|
|
650
650
|
line++;
|
|
651
|
-
if (!
|
|
651
|
+
if (!text2.trim()) continue;
|
|
652
652
|
try {
|
|
653
|
-
const event = JSON.parse(
|
|
653
|
+
const event = JSON.parse(text2);
|
|
654
654
|
if (typeof event.ordinal === "number" && Number.isSafeInteger(event.ordinal) && event.ordinal >= 0 && (maxOrdinal === null || event.ordinal > maxOrdinal)) {
|
|
655
655
|
maxOrdinal = event.ordinal;
|
|
656
656
|
}
|
|
@@ -1358,7 +1358,7 @@ async function readSqliteRowsWithBuiltin(dbPath, query) {
|
|
|
1358
1358
|
throw err;
|
|
1359
1359
|
}
|
|
1360
1360
|
}
|
|
1361
|
-
function readSqliteRowsWithCli(dbPath, query) {
|
|
1361
|
+
function readSqliteRowsWithCli(dbPath, query, readOnly = false) {
|
|
1362
1362
|
const candidates = [
|
|
1363
1363
|
process.env.TOKEN_ARENA_SQLITE3,
|
|
1364
1364
|
"sqlite3",
|
|
@@ -1367,7 +1367,8 @@ function readSqliteRowsWithCli(dbPath, query) {
|
|
|
1367
1367
|
let lastError = null;
|
|
1368
1368
|
for (const command of candidates) {
|
|
1369
1369
|
try {
|
|
1370
|
-
const
|
|
1370
|
+
const args = [...readOnly ? ["-readonly"] : [], "-json", dbPath, query];
|
|
1371
|
+
const output = execFileSync(command, args, {
|
|
1371
1372
|
encoding: "utf-8",
|
|
1372
1373
|
maxBuffer: 100 * 1024 * 1024,
|
|
1373
1374
|
timeout: 3e4,
|
|
@@ -1380,7 +1381,7 @@ function readSqliteRowsWithCli(dbPath, query) {
|
|
|
1380
1381
|
} catch (err) {
|
|
1381
1382
|
lastError = err;
|
|
1382
1383
|
const nodeError = err;
|
|
1383
|
-
if (nodeError.status === 127 || nodeError.
|
|
1384
|
+
if (nodeError.status === 127 || nodeError.code === "ENOENT") {
|
|
1384
1385
|
continue;
|
|
1385
1386
|
}
|
|
1386
1387
|
throw err;
|
|
@@ -1390,6 +1391,37 @@ function readSqliteRowsWithCli(dbPath, query) {
|
|
|
1390
1391
|
`sqlite3 CLI not found. Install sqlite3 or set TOKEN_ARENA_SQLITE3 to its full path. Last error: ${lastError?.message || "not found"}`
|
|
1391
1392
|
);
|
|
1392
1393
|
}
|
|
1394
|
+
async function readSqliteRowsReadonly(dbPath, query) {
|
|
1395
|
+
return withSuppressedSqliteWarning(async () => {
|
|
1396
|
+
const sqliteModuleId = "node:sqlite";
|
|
1397
|
+
let sqlite;
|
|
1398
|
+
try {
|
|
1399
|
+
sqlite = await import(sqliteModuleId);
|
|
1400
|
+
} catch (error) {
|
|
1401
|
+
if (error.code !== "ERR_UNKNOWN_BUILTIN_MODULE") {
|
|
1402
|
+
throw error;
|
|
1403
|
+
}
|
|
1404
|
+
return readSqliteRowsWithCli(dbPath, query, true);
|
|
1405
|
+
}
|
|
1406
|
+
let supportsReadOnly = false;
|
|
1407
|
+
const probe = new sqlite.DatabaseSync(":memory:", {
|
|
1408
|
+
get readOnly() {
|
|
1409
|
+
supportsReadOnly = true;
|
|
1410
|
+
return false;
|
|
1411
|
+
}
|
|
1412
|
+
});
|
|
1413
|
+
probe.close();
|
|
1414
|
+
if (!supportsReadOnly) {
|
|
1415
|
+
return readSqliteRowsWithCli(dbPath, query, true);
|
|
1416
|
+
}
|
|
1417
|
+
const db = new sqlite.DatabaseSync(dbPath, { readOnly: true });
|
|
1418
|
+
try {
|
|
1419
|
+
return db.prepare(query).all();
|
|
1420
|
+
} finally {
|
|
1421
|
+
db.close();
|
|
1422
|
+
}
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1393
1425
|
async function readSqliteRows(dbPath, query) {
|
|
1394
1426
|
const builtinRows = await readSqliteRowsWithBuiltin(dbPath, query);
|
|
1395
1427
|
return builtinRows ?? readSqliteRowsWithCli(dbPath, query);
|
|
@@ -4039,8 +4071,8 @@ var ClineParser = class {
|
|
|
4039
4071
|
if (msgType === "say" && msgSay === "api_req_started") {
|
|
4040
4072
|
let info = null;
|
|
4041
4073
|
try {
|
|
4042
|
-
const
|
|
4043
|
-
if (typeof
|
|
4074
|
+
const text2 = msg.text;
|
|
4075
|
+
if (typeof text2 === "string") info = JSON.parse(text2);
|
|
4044
4076
|
} catch {
|
|
4045
4077
|
}
|
|
4046
4078
|
if (!info) continue;
|
|
@@ -4494,76 +4526,540 @@ var RooCodeParser = class {
|
|
|
4494
4526
|
registerParser(new RooCodeParser());
|
|
4495
4527
|
|
|
4496
4528
|
// src/parsers/snow.ts
|
|
4497
|
-
import { existsSync as existsSync22 } from "fs";
|
|
4529
|
+
import { existsSync as existsSync22, readdirSync as readdirSync12, statSync as statSync6 } from "fs";
|
|
4498
4530
|
import { homedir as homedir22 } from "os";
|
|
4499
|
-
import { join as join23 } from "path";
|
|
4500
|
-
var
|
|
4531
|
+
import { basename as basename10, join as join23 } from "path";
|
|
4532
|
+
var DEFAULT_SNOW_DIR = join23(homedir22(), ".snow");
|
|
4533
|
+
var USAGE_DIR_NAME = "usage";
|
|
4534
|
+
var SESSIONS_DIR_NAME = "sessions";
|
|
4535
|
+
var NON_TRANSCRIPT_DIRS = /* @__PURE__ */ new Set(["subagent", "compressed"]);
|
|
4536
|
+
var SESSION_MATCH_WINDOW_MS = 5 * 60 * 1e3;
|
|
4537
|
+
var DAMAGED_TRANSCRIPT_SETTLE_MS = 60 * 1e3;
|
|
4538
|
+
var MIN_TIMESTAMP_MS = Date.UTC(2020, 0, 1);
|
|
4539
|
+
var MAX_TIMESTAMP_MS = Date.UTC(2080, 0, 1);
|
|
4501
4540
|
function toNonNegativeNumber2(value) {
|
|
4541
|
+
if (typeof value !== "number" && typeof value !== "string") return 0;
|
|
4502
4542
|
const numberValue = Number(value);
|
|
4503
4543
|
return Number.isFinite(numberValue) && numberValue >= 0 ? numberValue : 0;
|
|
4504
4544
|
}
|
|
4545
|
+
function toTime(value) {
|
|
4546
|
+
const time = typeof value === "string" ? Date.parse(value) : typeof value === "number" ? value : Number.NaN;
|
|
4547
|
+
return time >= MIN_TIMESTAMP_MS && time < MAX_TIMESTAMP_MS ? time : null;
|
|
4548
|
+
}
|
|
4549
|
+
function isRecord(value) {
|
|
4550
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4551
|
+
}
|
|
4552
|
+
function findSourceFiles(dir, extension, results = []) {
|
|
4553
|
+
let entries;
|
|
4554
|
+
try {
|
|
4555
|
+
entries = readdirSync12(dir, { withFileTypes: true });
|
|
4556
|
+
} catch (error) {
|
|
4557
|
+
const code = error.code;
|
|
4558
|
+
if (code === "ENOENT" || code === "ENOTDIR") return results;
|
|
4559
|
+
throw error;
|
|
4560
|
+
}
|
|
4561
|
+
for (const entry of entries) {
|
|
4562
|
+
const fullPath = join23(dir, entry.name);
|
|
4563
|
+
if (entry.isDirectory()) {
|
|
4564
|
+
if (extension === ".json" && NON_TRANSCRIPT_DIRS.has(entry.name)) {
|
|
4565
|
+
continue;
|
|
4566
|
+
}
|
|
4567
|
+
findSourceFiles(fullPath, extension, results);
|
|
4568
|
+
} else if (entry.name.endsWith(extension)) {
|
|
4569
|
+
results.push(fullPath);
|
|
4570
|
+
}
|
|
4571
|
+
}
|
|
4572
|
+
return results;
|
|
4573
|
+
}
|
|
4574
|
+
function resolveProject(session) {
|
|
4575
|
+
if (typeof session.projectPath === "string" && session.projectPath) {
|
|
4576
|
+
const segments = session.projectPath.split(/[\\/]+/).filter(Boolean);
|
|
4577
|
+
return segments.at(-1) || "unknown";
|
|
4578
|
+
}
|
|
4579
|
+
return "unknown";
|
|
4580
|
+
}
|
|
4581
|
+
function readTranscript(filePath) {
|
|
4582
|
+
const content = readFileSafe(filePath);
|
|
4583
|
+
if (content === null) return { status: "damaged" };
|
|
4584
|
+
let parsed;
|
|
4585
|
+
try {
|
|
4586
|
+
parsed = JSON.parse(content);
|
|
4587
|
+
} catch {
|
|
4588
|
+
return { status: "damaged" };
|
|
4589
|
+
}
|
|
4590
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.messages)) {
|
|
4591
|
+
return { status: "foreign" };
|
|
4592
|
+
}
|
|
4593
|
+
const session = parsed;
|
|
4594
|
+
const compressedFrom = typeof session.compressedFrom === "string" && session.compressedFrom ? session.compressedFrom : void 0;
|
|
4595
|
+
const copiedUntil = compressedFrom !== void 0 || typeof session.branchedFrom === "string" ? Math.max(
|
|
4596
|
+
toTime(session.compressedAt) ?? Number.NEGATIVE_INFINITY,
|
|
4597
|
+
toTime(session.createdAt) ?? Number.NEGATIVE_INFINITY
|
|
4598
|
+
) : Number.NEGATIVE_INFINITY;
|
|
4599
|
+
const messages = [];
|
|
4600
|
+
let lastMessageAt = 0;
|
|
4601
|
+
for (const message of parsed.messages) {
|
|
4602
|
+
if (!isRecord(message)) continue;
|
|
4603
|
+
if (message.role !== "user" && message.role !== "assistant") continue;
|
|
4604
|
+
const time = toTime(message.timestamp);
|
|
4605
|
+
if (time === null) continue;
|
|
4606
|
+
if (time > lastMessageAt) lastMessageAt = time;
|
|
4607
|
+
if (time <= copiedUntil) continue;
|
|
4608
|
+
messages.push({ role: message.role, time });
|
|
4609
|
+
}
|
|
4610
|
+
return {
|
|
4611
|
+
status: "ok",
|
|
4612
|
+
transcript: {
|
|
4613
|
+
filePath,
|
|
4614
|
+
sessionId: typeof session.id === "string" && session.id ? session.id : basename10(filePath, ".json"),
|
|
4615
|
+
project: resolveProject(session),
|
|
4616
|
+
updatedAt: toTime(session.updatedAt) ?? lastMessageAt,
|
|
4617
|
+
...compressedFrom !== void 0 ? { compressedFrom } : {},
|
|
4618
|
+
messages
|
|
4619
|
+
}
|
|
4620
|
+
};
|
|
4621
|
+
}
|
|
4622
|
+
function isNewerCopy(candidate, current) {
|
|
4623
|
+
if (candidate.updatedAt !== current.updatedAt) {
|
|
4624
|
+
return candidate.updatedAt > current.updatedAt;
|
|
4625
|
+
}
|
|
4626
|
+
if (candidate.messages.length !== current.messages.length) {
|
|
4627
|
+
return candidate.messages.length > current.messages.length;
|
|
4628
|
+
}
|
|
4629
|
+
return candidate.filePath < current.filePath;
|
|
4630
|
+
}
|
|
4631
|
+
function resolveConversationId(transcript, transcripts) {
|
|
4632
|
+
let current = transcript;
|
|
4633
|
+
const visited = /* @__PURE__ */ new Set([current.sessionId]);
|
|
4634
|
+
while (current.compressedFrom && !visited.has(current.compressedFrom)) {
|
|
4635
|
+
visited.add(current.compressedFrom);
|
|
4636
|
+
const source = transcripts.get(current.compressedFrom);
|
|
4637
|
+
if (!source) return current.compressedFrom;
|
|
4638
|
+
current = source;
|
|
4639
|
+
}
|
|
4640
|
+
return current.sessionId;
|
|
4641
|
+
}
|
|
4642
|
+
function mayStillBeWriting(filePath, now) {
|
|
4643
|
+
try {
|
|
4644
|
+
return now - statSync6(filePath).mtimeMs < DAMAGED_TRANSCRIPT_SETTLE_MS;
|
|
4645
|
+
} catch {
|
|
4646
|
+
return true;
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
function distanceToNearest(sortedTimes, time) {
|
|
4650
|
+
let low = 0;
|
|
4651
|
+
let high = sortedTimes.length;
|
|
4652
|
+
while (low < high) {
|
|
4653
|
+
const middle = low + high >>> 1;
|
|
4654
|
+
if (sortedTimes[middle] < time) low = middle + 1;
|
|
4655
|
+
else high = middle;
|
|
4656
|
+
}
|
|
4657
|
+
let distance = Number.POSITIVE_INFINITY;
|
|
4658
|
+
if (low < sortedTimes.length) distance = sortedTimes[low] - time;
|
|
4659
|
+
if (low > 0) distance = Math.min(distance, time - sortedTimes[low - 1]);
|
|
4660
|
+
return distance;
|
|
4661
|
+
}
|
|
4662
|
+
function attributeUsage(entries, windows) {
|
|
4663
|
+
if (windows.length === 0) return;
|
|
4664
|
+
const byStart = [...windows].sort((left, right) => left.start - right.start);
|
|
4665
|
+
const byTime = entries.map((entry) => ({ entry, time: entry.timestamp.getTime() })).sort((left, right) => left.time - right.time);
|
|
4666
|
+
const open = [];
|
|
4667
|
+
let nextWindow = 0;
|
|
4668
|
+
for (const { entry, time } of byTime) {
|
|
4669
|
+
while (nextWindow < byStart.length && byStart[nextWindow].start <= time) {
|
|
4670
|
+
open.push(byStart[nextWindow]);
|
|
4671
|
+
nextWindow++;
|
|
4672
|
+
}
|
|
4673
|
+
let kept = 0;
|
|
4674
|
+
let best;
|
|
4675
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
4676
|
+
for (const window of open) {
|
|
4677
|
+
if (window.end < time) continue;
|
|
4678
|
+
open[kept++] = window;
|
|
4679
|
+
const distance = distanceToNearest(window.assistantTimes, time);
|
|
4680
|
+
if (best === void 0 || distance < bestDistance || distance === bestDistance && window.sessionId < best.sessionId) {
|
|
4681
|
+
best = window;
|
|
4682
|
+
bestDistance = distance;
|
|
4683
|
+
}
|
|
4684
|
+
}
|
|
4685
|
+
open.length = kept;
|
|
4686
|
+
if (best) entry.sessionId = best.sessionId;
|
|
4687
|
+
}
|
|
4688
|
+
}
|
|
4505
4689
|
var SnowParser = class {
|
|
4506
|
-
constructor(
|
|
4507
|
-
this.
|
|
4508
|
-
this.tool = {
|
|
4690
|
+
constructor(snowDir = DEFAULT_SNOW_DIR) {
|
|
4691
|
+
this.snowDir = snowDir;
|
|
4692
|
+
this.tool = {
|
|
4693
|
+
id: "snow",
|
|
4694
|
+
name: "Snow CLI",
|
|
4695
|
+
dataDir: join23(snowDir, USAGE_DIR_NAME)
|
|
4696
|
+
};
|
|
4509
4697
|
}
|
|
4510
|
-
|
|
4698
|
+
snowDir;
|
|
4511
4699
|
tool;
|
|
4512
4700
|
async parse() {
|
|
4701
|
+
const usage = this.parseUsage();
|
|
4702
|
+
const sessions = this.parseSessions();
|
|
4703
|
+
attributeUsage(usage.entries, sessions.windows);
|
|
4704
|
+
return {
|
|
4705
|
+
buckets: aggregateToBuckets(usage.entries),
|
|
4706
|
+
sessions: extractSessions(sessions.events, usage.entries),
|
|
4707
|
+
...usage.incomplete ? { incomplete: true } : {}
|
|
4708
|
+
};
|
|
4709
|
+
}
|
|
4710
|
+
listSourceFiles() {
|
|
4711
|
+
return [
|
|
4712
|
+
...findSourceFiles(join23(this.snowDir, USAGE_DIR_NAME), ".jsonl"),
|
|
4713
|
+
...findSourceFiles(join23(this.snowDir, SESSIONS_DIR_NAME), ".json")
|
|
4714
|
+
];
|
|
4715
|
+
}
|
|
4716
|
+
/**
|
|
4717
|
+
* Build session timing events plus the windows used to attribute usage
|
|
4718
|
+
* records. Message timestamps drive turn boundaries, so active time, session
|
|
4719
|
+
* duration, and message counts all come from real transcripts.
|
|
4720
|
+
*
|
|
4721
|
+
* Transcripts only feed sessions, never buckets, so a damaged one is skipped
|
|
4722
|
+
* rather than holding back the whole source: Snow cannot open it either and
|
|
4723
|
+
* will not rewrite it, so deferring would block Snow uploads for good.
|
|
4724
|
+
*/
|
|
4725
|
+
parseSessions() {
|
|
4726
|
+
const transcripts = /* @__PURE__ */ new Map();
|
|
4727
|
+
const damaged = [];
|
|
4728
|
+
const now = Date.now();
|
|
4729
|
+
for (const filePath of findSourceFiles(
|
|
4730
|
+
join23(this.snowDir, SESSIONS_DIR_NAME),
|
|
4731
|
+
".json"
|
|
4732
|
+
)) {
|
|
4733
|
+
const read = readTranscript(filePath);
|
|
4734
|
+
if (read.status === "damaged") {
|
|
4735
|
+
if (!mayStillBeWriting(filePath, now)) damaged.push(filePath);
|
|
4736
|
+
continue;
|
|
4737
|
+
}
|
|
4738
|
+
if (read.status === "foreign") continue;
|
|
4739
|
+
const current = transcripts.get(read.transcript.sessionId);
|
|
4740
|
+
if (!current || isNewerCopy(read.transcript, current)) {
|
|
4741
|
+
transcripts.set(read.transcript.sessionId, read.transcript);
|
|
4742
|
+
}
|
|
4743
|
+
}
|
|
4744
|
+
if (damaged.length > 0) {
|
|
4745
|
+
const more = damaged.length > 1 ? ` and ${damaged.length - 1} more` : "";
|
|
4746
|
+
logger.warn(
|
|
4747
|
+
`Skipped damaged Snow session file ${damaged[0]}${more}. Token usage is still counted.`
|
|
4748
|
+
);
|
|
4749
|
+
}
|
|
4750
|
+
const events = [];
|
|
4751
|
+
const windows = [];
|
|
4752
|
+
for (const transcript of transcripts.values()) {
|
|
4753
|
+
if (transcript.messages.length === 0) continue;
|
|
4754
|
+
const sessionId = resolveConversationId(transcript, transcripts);
|
|
4755
|
+
const assistantTimes = [];
|
|
4756
|
+
let start = Number.POSITIVE_INFINITY;
|
|
4757
|
+
let end = Number.NEGATIVE_INFINITY;
|
|
4758
|
+
for (const message of transcript.messages) {
|
|
4759
|
+
events.push({
|
|
4760
|
+
sessionId,
|
|
4761
|
+
source: "snow",
|
|
4762
|
+
project: transcript.project,
|
|
4763
|
+
timestamp: new Date(message.time),
|
|
4764
|
+
role: message.role
|
|
4765
|
+
});
|
|
4766
|
+
if (message.role === "assistant") assistantTimes.push(message.time);
|
|
4767
|
+
if (message.time < start) start = message.time;
|
|
4768
|
+
if (message.time > end) end = message.time;
|
|
4769
|
+
}
|
|
4770
|
+
windows.push({
|
|
4771
|
+
sessionId,
|
|
4772
|
+
assistantTimes: assistantTimes.sort((left, right) => left - right),
|
|
4773
|
+
start: start - SESSION_MATCH_WINDOW_MS,
|
|
4774
|
+
end: end + SESSION_MATCH_WINDOW_MS
|
|
4775
|
+
});
|
|
4776
|
+
}
|
|
4777
|
+
return { events, windows };
|
|
4778
|
+
}
|
|
4779
|
+
/**
|
|
4780
|
+
* Parse usage JSONL. Session attribution happens afterwards, because it needs
|
|
4781
|
+
* the transcripts.
|
|
4782
|
+
*
|
|
4783
|
+
* The bucket project stays `unknown` on purpose; see the note on
|
|
4784
|
+
* `SESSION_MATCH_WINDOW_MS`.
|
|
4785
|
+
*/
|
|
4786
|
+
parseUsage() {
|
|
4513
4787
|
const entries = [];
|
|
4514
|
-
|
|
4788
|
+
let incomplete = false;
|
|
4789
|
+
for (const filePath of findSourceFiles(
|
|
4790
|
+
join23(this.snowDir, USAGE_DIR_NAME),
|
|
4791
|
+
".jsonl"
|
|
4792
|
+
)) {
|
|
4515
4793
|
const content = readFileSafe(filePath);
|
|
4516
|
-
if (
|
|
4794
|
+
if (content === null) {
|
|
4795
|
+
if (existsSync22(filePath)) incomplete = true;
|
|
4796
|
+
continue;
|
|
4797
|
+
}
|
|
4517
4798
|
for (const line of content.split("\n")) {
|
|
4518
4799
|
if (!line.trim()) continue;
|
|
4800
|
+
let record;
|
|
4519
4801
|
try {
|
|
4520
|
-
const
|
|
4521
|
-
if (
|
|
4522
|
-
|
|
4523
|
-
if (Number.isNaN(timestamp.getTime())) continue;
|
|
4524
|
-
const inputTokens = toNonNegativeNumber2(record.inputTokens);
|
|
4525
|
-
const outputTokens = toNonNegativeNumber2(record.outputTokens);
|
|
4526
|
-
const cachedTokens = toNonNegativeNumber2(record.cacheReadInputTokens);
|
|
4527
|
-
const reasoningTokens = toNonNegativeNumber2(record.reasoningTokens);
|
|
4528
|
-
if (inputTokens + outputTokens + cachedTokens + reasoningTokens === 0)
|
|
4529
|
-
continue;
|
|
4530
|
-
entries.push({
|
|
4531
|
-
source: "snow",
|
|
4532
|
-
model: typeof record.model === "string" && record.model ? record.model : "unknown",
|
|
4533
|
-
project: "unknown",
|
|
4534
|
-
timestamp,
|
|
4535
|
-
inputTokens,
|
|
4536
|
-
outputTokens,
|
|
4537
|
-
reasoningTokens,
|
|
4538
|
-
cachedTokens
|
|
4539
|
-
});
|
|
4802
|
+
const parsed = JSON.parse(line);
|
|
4803
|
+
if (!isRecord(parsed)) continue;
|
|
4804
|
+
record = parsed;
|
|
4540
4805
|
} catch {
|
|
4806
|
+
continue;
|
|
4541
4807
|
}
|
|
4808
|
+
const time = toTime(record.timestamp);
|
|
4809
|
+
if (time === null) continue;
|
|
4810
|
+
const reportedInputTokens = toNonNegativeNumber2(record.inputTokens);
|
|
4811
|
+
const outputTokens = toNonNegativeNumber2(record.outputTokens);
|
|
4812
|
+
const cachedTokens = toNonNegativeNumber2(record.cacheReadInputTokens);
|
|
4813
|
+
const cacheCreationTokens = toNonNegativeNumber2(
|
|
4814
|
+
record.cacheCreationInputTokens
|
|
4815
|
+
);
|
|
4816
|
+
const reasoningTokens = toNonNegativeNumber2(record.reasoningTokens);
|
|
4817
|
+
const inputTokens = record.cacheCreationInputTokens === void 0 && cachedTokens <= reportedInputTokens ? reportedInputTokens - cachedTokens : reportedInputTokens;
|
|
4818
|
+
if (inputTokens + outputTokens + cachedTokens + cacheCreationTokens + reasoningTokens === 0)
|
|
4819
|
+
continue;
|
|
4820
|
+
entries.push({
|
|
4821
|
+
source: "snow",
|
|
4822
|
+
model: typeof record.model === "string" && record.model ? record.model : "unknown",
|
|
4823
|
+
project: "unknown",
|
|
4824
|
+
timestamp: new Date(time),
|
|
4825
|
+
inputTokens,
|
|
4826
|
+
outputTokens,
|
|
4827
|
+
reasoningTokens,
|
|
4828
|
+
cachedTokens,
|
|
4829
|
+
cacheCreationTokens
|
|
4830
|
+
});
|
|
4542
4831
|
}
|
|
4543
4832
|
}
|
|
4544
|
-
return {
|
|
4833
|
+
return { entries, incomplete };
|
|
4545
4834
|
}
|
|
4546
4835
|
isInstalled() {
|
|
4547
|
-
return existsSync22(this.
|
|
4836
|
+
return existsSync22(join23(this.snowDir, USAGE_DIR_NAME));
|
|
4548
4837
|
}
|
|
4549
4838
|
};
|
|
4550
4839
|
registerParser(new SnowParser());
|
|
4551
4840
|
|
|
4841
|
+
// src/parsers/snow-app.ts
|
|
4842
|
+
import { statSync as statSync7 } from "fs";
|
|
4843
|
+
import { homedir as homedir23 } from "os";
|
|
4844
|
+
import { dirname as dirname4, join as join24 } from "path";
|
|
4845
|
+
var TOOL_ID14 = "snow-app";
|
|
4846
|
+
var DEFAULT_DB_PATH3 = join24(homedir23(), ".snowapp", "snowapp.db");
|
|
4847
|
+
var SNAPSHOT_QUERY = `SELECT 'usage' AS kind, u.id, u.conversation_id AS sessionId,
|
|
4848
|
+
u.directory_id AS directoryId,
|
|
4849
|
+
u.model, u.created_at AS timestamp, '' AS role,
|
|
4850
|
+
u.input_tokens AS inputTokens, u.output_tokens AS outputTokens,
|
|
4851
|
+
u.cache_read_input_tokens AS cacheReadTokens,
|
|
4852
|
+
u.cache_creation_input_tokens AS cacheCreationTokens,
|
|
4853
|
+
'' AS forkedFrom, 0 AS forkMessageCount
|
|
4854
|
+
FROM usage_records u
|
|
4855
|
+
UNION ALL
|
|
4856
|
+
SELECT 'message' AS kind, m.id, m.conversation_id AS sessionId,
|
|
4857
|
+
(SELECT u.directory_id FROM usage_records u
|
|
4858
|
+
WHERE u.conversation_id = m.conversation_id
|
|
4859
|
+
ORDER BY u.created_at, u.id LIMIT 1) AS directoryId,
|
|
4860
|
+
'' AS model, m.created_at AS timestamp, m.role,
|
|
4861
|
+
0 AS inputTokens, 0 AS outputTokens, 0 AS cacheReadTokens, 0 AS cacheCreationTokens,
|
|
4862
|
+
'' AS forkedFrom, 0 AS forkMessageCount
|
|
4863
|
+
FROM chat_messages m
|
|
4864
|
+
WHERE m.role IN ('user', 'assistant')
|
|
4865
|
+
AND EXISTS (SELECT 1 FROM usage_records u WHERE u.conversation_id = m.conversation_id)
|
|
4866
|
+
UNION ALL
|
|
4867
|
+
SELECT 'conversation' AS kind, c.conversation_id AS id, c.conversation_id AS sessionId,
|
|
4868
|
+
'' AS directoryId, '' AS model, '' AS timestamp, '' AS role,
|
|
4869
|
+
0 AS inputTokens, 0 AS outputTokens, 0 AS cacheReadTokens, 0 AS cacheCreationTokens,
|
|
4870
|
+
c.forked_from_conversation_id AS forkedFrom, c.fork_message_count AS forkMessageCount
|
|
4871
|
+
FROM chat_conversations c
|
|
4872
|
+
WHERE EXISTS (SELECT 1 FROM usage_records u WHERE u.conversation_id = c.conversation_id)
|
|
4873
|
+
ORDER BY timestamp, kind, id`;
|
|
4874
|
+
function text(value) {
|
|
4875
|
+
return typeof value === "string" ? value.trim() : "";
|
|
4876
|
+
}
|
|
4877
|
+
function tokenCount4(value) {
|
|
4878
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
4879
|
+
throw new Error("Snow App contains invalid token counts; scan deferred.");
|
|
4880
|
+
}
|
|
4881
|
+
return value;
|
|
4882
|
+
}
|
|
4883
|
+
function parseTimestamp4(value) {
|
|
4884
|
+
const raw = text(value);
|
|
4885
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?(Z|[+-]\d{2}:\d{2})?$/.exec(
|
|
4886
|
+
raw
|
|
4887
|
+
);
|
|
4888
|
+
if (!match)
|
|
4889
|
+
throw new Error("Snow App contains an invalid timestamp; scan deferred.");
|
|
4890
|
+
const [year, month, day, hour, minute, second] = match.slice(1, 7).map(Number);
|
|
4891
|
+
const calendar = new Date(
|
|
4892
|
+
Date.UTC(year, month - 1, day, hour, minute, second)
|
|
4893
|
+
);
|
|
4894
|
+
if (year < 1e3 || calendar.getUTCFullYear() !== year || calendar.getUTCMonth() !== month - 1 || calendar.getUTCDate() !== day || hour > 23 || minute > 59 || second > 59) {
|
|
4895
|
+
throw new Error(
|
|
4896
|
+
"Snow App contains an invalid calendar date; scan deferred."
|
|
4897
|
+
);
|
|
4898
|
+
}
|
|
4899
|
+
const date = new Date(raw.replace(" ", "T"));
|
|
4900
|
+
if (!Number.isFinite(date.getTime()) || !match[8] && (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day || date.getHours() !== hour || date.getMinutes() !== minute)) {
|
|
4901
|
+
throw new Error(
|
|
4902
|
+
"Snow App contains an invalid local timestamp; scan deferred."
|
|
4903
|
+
);
|
|
4904
|
+
}
|
|
4905
|
+
if (date.getTime() < Date.UTC(2020, 0, 1) || date.getTime() >= Date.UTC(2080, 0, 1)) {
|
|
4906
|
+
throw new Error(
|
|
4907
|
+
"Snow App timestamp is outside the supported range; scan deferred."
|
|
4908
|
+
);
|
|
4909
|
+
}
|
|
4910
|
+
if (!match[8]) {
|
|
4911
|
+
const nextDay = new Date(date);
|
|
4912
|
+
nextDay.setDate(nextDay.getDate() + 1);
|
|
4913
|
+
const overlapMinutes = nextDay.getTimezoneOffset() - date.getTimezoneOffset();
|
|
4914
|
+
if (overlapMinutes > 0) {
|
|
4915
|
+
const later = new Date(date.getTime() + overlapMinutes * 6e4);
|
|
4916
|
+
if (later.getHours() === hour && later.getMinutes() === minute && later.getDate() === day) {
|
|
4917
|
+
throw new Error(
|
|
4918
|
+
"Snow App contains an ambiguous local timestamp; scan deferred."
|
|
4919
|
+
);
|
|
4920
|
+
}
|
|
4921
|
+
}
|
|
4922
|
+
}
|
|
4923
|
+
const elapsed = date.getTime() - roundToHalfHour(date).getTime();
|
|
4924
|
+
if (elapsed < 0 || elapsed >= 30 * 6e4) {
|
|
4925
|
+
throw new Error(
|
|
4926
|
+
"Snow App timestamp cannot be bucketed safely across DST; scan deferred."
|
|
4927
|
+
);
|
|
4928
|
+
}
|
|
4929
|
+
return date;
|
|
4930
|
+
}
|
|
4931
|
+
function projectName(value) {
|
|
4932
|
+
if (value === "") return "unknown";
|
|
4933
|
+
if (typeof value !== "string" || !/^local:(?:[A-Za-z]:[\\/]|\/|\\\\)/.test(value)) {
|
|
4934
|
+
throw new Error(
|
|
4935
|
+
"Snow App contains an unsupported directory identity; scan deferred."
|
|
4936
|
+
);
|
|
4937
|
+
}
|
|
4938
|
+
return value.slice("local:".length).split(/[\\/]+/).filter(Boolean).at(-1) || "unknown";
|
|
4939
|
+
}
|
|
4940
|
+
var SnowAppParser = class {
|
|
4941
|
+
tool;
|
|
4942
|
+
dbPath;
|
|
4943
|
+
queryRows;
|
|
4944
|
+
constructor(options = {}) {
|
|
4945
|
+
this.dbPath = options.dbPath ?? DEFAULT_DB_PATH3;
|
|
4946
|
+
this.queryRows = options.queryRows ?? readSqliteRowsReadonly;
|
|
4947
|
+
this.tool = {
|
|
4948
|
+
id: TOOL_ID14,
|
|
4949
|
+
name: "Snow App",
|
|
4950
|
+
dataDir: dirname4(this.dbPath)
|
|
4951
|
+
};
|
|
4952
|
+
}
|
|
4953
|
+
isInstalled() {
|
|
4954
|
+
try {
|
|
4955
|
+
return statSync7(this.dbPath).isFile();
|
|
4956
|
+
} catch (error) {
|
|
4957
|
+
if (error.code === "ENOENT") return false;
|
|
4958
|
+
throw error;
|
|
4959
|
+
}
|
|
4960
|
+
}
|
|
4961
|
+
// Intentionally no parse cache: DB mtime alone misses uncheckpointed WAL writes.
|
|
4962
|
+
async parse() {
|
|
4963
|
+
if (!this.isInstalled()) return { buckets: [], sessions: [] };
|
|
4964
|
+
const rows = await this.queryRows(this.dbPath, SNAPSHOT_QUERY);
|
|
4965
|
+
const entries = [];
|
|
4966
|
+
const events = [];
|
|
4967
|
+
const seen = /* @__PURE__ */ new Map();
|
|
4968
|
+
for (const row of rows) {
|
|
4969
|
+
const id = text(row.id);
|
|
4970
|
+
if (!id || row.kind !== "usage" && row.kind !== "message" && row.kind !== "conversation") {
|
|
4971
|
+
throw new Error(
|
|
4972
|
+
"Snow App contains an invalid record identity; scan deferred."
|
|
4973
|
+
);
|
|
4974
|
+
}
|
|
4975
|
+
const key = JSON.stringify([row.kind, id]);
|
|
4976
|
+
const signature = JSON.stringify(row);
|
|
4977
|
+
const previous = seen.get(key);
|
|
4978
|
+
if (previous !== void 0) {
|
|
4979
|
+
if (previous !== signature)
|
|
4980
|
+
throw new Error("Conflicting Snow App record IDs; scan deferred.");
|
|
4981
|
+
continue;
|
|
4982
|
+
}
|
|
4983
|
+
seen.set(key, signature);
|
|
4984
|
+
if (row.kind === "conversation") {
|
|
4985
|
+
if (typeof row.forkedFrom !== "string" || row.forkedFrom !== "" || row.forkMessageCount !== 0) {
|
|
4986
|
+
throw new Error(
|
|
4987
|
+
"Snow App fork metadata is unsupported or invalid; scan deferred."
|
|
4988
|
+
);
|
|
4989
|
+
}
|
|
4990
|
+
continue;
|
|
4991
|
+
}
|
|
4992
|
+
const sessionId = text(row.sessionId);
|
|
4993
|
+
const project = projectName(row.directoryId);
|
|
4994
|
+
if (row.kind === "message") {
|
|
4995
|
+
if (!sessionId || row.role !== "user" && row.role !== "assistant") {
|
|
4996
|
+
throw new Error(
|
|
4997
|
+
"Snow App contains invalid message metadata; scan deferred."
|
|
4998
|
+
);
|
|
4999
|
+
}
|
|
5000
|
+
events.push({
|
|
5001
|
+
sessionId,
|
|
5002
|
+
source: TOOL_ID14,
|
|
5003
|
+
project,
|
|
5004
|
+
role: row.role,
|
|
5005
|
+
timestamp: parseTimestamp4(row.timestamp)
|
|
5006
|
+
});
|
|
5007
|
+
continue;
|
|
5008
|
+
}
|
|
5009
|
+
const input2 = tokenCount4(row.inputTokens);
|
|
5010
|
+
const outputTokens = tokenCount4(row.outputTokens);
|
|
5011
|
+
const cachedTokens = Math.min(input2, tokenCount4(row.cacheReadTokens));
|
|
5012
|
+
const cacheCreationTokens = tokenCount4(row.cacheCreationTokens);
|
|
5013
|
+
if (cacheCreationTokens > input2 - cachedTokens || !Number.isSafeInteger(input2 + outputTokens)) {
|
|
5014
|
+
throw new Error(
|
|
5015
|
+
"Snow App cache counts exceed inclusive input; scan deferred."
|
|
5016
|
+
);
|
|
5017
|
+
}
|
|
5018
|
+
if (input2 + outputTokens === 0) continue;
|
|
5019
|
+
entries.push({
|
|
5020
|
+
sessionId: sessionId || void 0,
|
|
5021
|
+
source: TOOL_ID14,
|
|
5022
|
+
project,
|
|
5023
|
+
model: text(row.model) || "unknown",
|
|
5024
|
+
timestamp: parseTimestamp4(row.timestamp),
|
|
5025
|
+
inputTokens: input2 - cachedTokens - cacheCreationTokens,
|
|
5026
|
+
outputTokens,
|
|
5027
|
+
cachedTokens,
|
|
5028
|
+
cacheCreationTokens,
|
|
5029
|
+
reasoningTokens: 0
|
|
5030
|
+
});
|
|
5031
|
+
}
|
|
5032
|
+
const buckets = aggregateToBuckets(entries);
|
|
5033
|
+
const sessions = extractSessions(events, entries);
|
|
5034
|
+
if ([...buckets, ...sessions].some(
|
|
5035
|
+
(row) => !Number.isSafeInteger(row.totalTokens)
|
|
5036
|
+
) || sessions.some(
|
|
5037
|
+
(row) => row.durationSeconds > 2147483647 || row.activeSeconds > 2147483647
|
|
5038
|
+
)) {
|
|
5039
|
+
throw new Error(
|
|
5040
|
+
"Snow App aggregate exceeds supported counts or duration; scan deferred."
|
|
5041
|
+
);
|
|
5042
|
+
}
|
|
5043
|
+
return { buckets, sessions };
|
|
5044
|
+
}
|
|
5045
|
+
};
|
|
5046
|
+
registerParser(new SnowAppParser());
|
|
5047
|
+
|
|
4552
5048
|
// src/parsers/cursor.ts
|
|
4553
5049
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
4554
5050
|
import { copyFileSync as copyFileSync2, existsSync as existsSync23, mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
|
|
4555
|
-
import { homedir as
|
|
4556
|
-
import { dirname as
|
|
4557
|
-
var
|
|
5051
|
+
import { homedir as homedir24, tmpdir as tmpdir2 } from "os";
|
|
5052
|
+
import { dirname as dirname5, join as join25, resolve as resolve2 } from "path";
|
|
5053
|
+
var TOOL_ID15 = "cursor";
|
|
4558
5054
|
var TOOL_NAME13 = "Cursor";
|
|
4559
|
-
var STATE_DB_RELATIVE =
|
|
5055
|
+
var STATE_DB_RELATIVE = join25("User", "globalStorage", "state.vscdb");
|
|
4560
5056
|
var ACCESS_TOKEN_KEY = "cursorAuth/accessToken";
|
|
4561
5057
|
var SESSION_COOKIE = "WorkosCursorSessionToken";
|
|
4562
5058
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
4563
5059
|
function getDefaultStateDbPath() {
|
|
4564
5060
|
if (process.platform === "darwin") {
|
|
4565
|
-
return
|
|
4566
|
-
|
|
5061
|
+
return join25(
|
|
5062
|
+
homedir24(),
|
|
4567
5063
|
"Library",
|
|
4568
5064
|
"Application Support",
|
|
4569
5065
|
"Cursor",
|
|
@@ -4571,11 +5067,11 @@ function getDefaultStateDbPath() {
|
|
|
4571
5067
|
);
|
|
4572
5068
|
}
|
|
4573
5069
|
if (process.platform === "win32") {
|
|
4574
|
-
const appData = process.env.APPDATA?.trim() ||
|
|
4575
|
-
return
|
|
5070
|
+
const appData = process.env.APPDATA?.trim() || join25(homedir24(), "AppData", "Roaming");
|
|
5071
|
+
return join25(appData, "Cursor", STATE_DB_RELATIVE);
|
|
4576
5072
|
}
|
|
4577
|
-
const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim() ||
|
|
4578
|
-
return
|
|
5073
|
+
const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim() || join25(homedir24(), ".config");
|
|
5074
|
+
return join25(xdgConfigHome, "Cursor", STATE_DB_RELATIVE);
|
|
4579
5075
|
}
|
|
4580
5076
|
function getCursorStateDbPath() {
|
|
4581
5077
|
const explicit = process.env.CURSOR_STATE_DB_PATH?.trim();
|
|
@@ -4586,7 +5082,7 @@ function getCursorStateDbPath() {
|
|
|
4586
5082
|
const configDirs = process.env.CURSOR_CONFIG_DIR?.trim();
|
|
4587
5083
|
const candidates = configDirs ? configDirs.split(",").map((v) => v.trim()).filter(Boolean).map((v) => {
|
|
4588
5084
|
const r = resolve2(v);
|
|
4589
|
-
return r.endsWith(".vscdb") ? r :
|
|
5085
|
+
return r.endsWith(".vscdb") ? r : join25(r, STATE_DB_RELATIVE);
|
|
4590
5086
|
}) : [getDefaultStateDbPath()];
|
|
4591
5087
|
for (const c of candidates) {
|
|
4592
5088
|
if (existsSync23(c)) return c;
|
|
@@ -4629,8 +5125,8 @@ function readAccessToken(dbPath) {
|
|
|
4629
5125
|
return queryAccessToken(dbPath);
|
|
4630
5126
|
} catch (err) {
|
|
4631
5127
|
if (!isLockError2(err)) throw err;
|
|
4632
|
-
const snapshotDir = mkdtempSync2(
|
|
4633
|
-
const queryPath =
|
|
5128
|
+
const snapshotDir = mkdtempSync2(join25(tmpdir2(), "tokenarena-cursor-"));
|
|
5129
|
+
const queryPath = join25(snapshotDir, "state.vscdb");
|
|
4634
5130
|
try {
|
|
4635
5131
|
copyFileSync2(dbPath, queryPath);
|
|
4636
5132
|
for (const suffix of ["-shm", "-wal"]) {
|
|
@@ -4693,17 +5189,17 @@ async function fetchUsageCsv(token) {
|
|
|
4693
5189
|
}
|
|
4694
5190
|
throw new Error(`Cursor usage export auth failed (${failures.join("; ")})`);
|
|
4695
5191
|
}
|
|
4696
|
-
function parseCsv(
|
|
5192
|
+
function parseCsv(text2) {
|
|
4697
5193
|
const rows = [];
|
|
4698
5194
|
let field = "";
|
|
4699
5195
|
let row = [];
|
|
4700
5196
|
let inQuotes = false;
|
|
4701
5197
|
let i = 0;
|
|
4702
|
-
while (i <
|
|
4703
|
-
const c =
|
|
5198
|
+
while (i < text2.length) {
|
|
5199
|
+
const c = text2[i];
|
|
4704
5200
|
if (inQuotes) {
|
|
4705
5201
|
if (c === '"') {
|
|
4706
|
-
if (
|
|
5202
|
+
if (text2[i + 1] === '"') {
|
|
4707
5203
|
field += '"';
|
|
4708
5204
|
i += 2;
|
|
4709
5205
|
continue;
|
|
@@ -4763,9 +5259,9 @@ function parseInt0(value) {
|
|
|
4763
5259
|
}
|
|
4764
5260
|
function createToolDefinition9(dbPath) {
|
|
4765
5261
|
return {
|
|
4766
|
-
id:
|
|
5262
|
+
id: TOOL_ID15,
|
|
4767
5263
|
name: TOOL_NAME13,
|
|
4768
|
-
dataDir:
|
|
5264
|
+
dataDir: dirname5(dbPath)
|
|
4769
5265
|
};
|
|
4770
5266
|
}
|
|
4771
5267
|
var CursorParser = class {
|
|
@@ -4827,7 +5323,7 @@ var CursorParser = class {
|
|
|
4827
5323
|
const output = outputIdx >= 0 ? parseInt0(row[outputIdx]) : 0;
|
|
4828
5324
|
if (inputCacheWrite + inputNoCache + cacheRead + output === 0) continue;
|
|
4829
5325
|
entries.push({
|
|
4830
|
-
source:
|
|
5326
|
+
source: TOOL_ID15,
|
|
4831
5327
|
model,
|
|
4832
5328
|
project: "unknown",
|
|
4833
5329
|
timestamp,
|
|
@@ -4852,11 +5348,11 @@ registerParser(new CursorParser());
|
|
|
4852
5348
|
// src/parsers/zcode.ts
|
|
4853
5349
|
import { createHash as createHash5 } from "crypto";
|
|
4854
5350
|
import { existsSync as existsSync24 } from "fs";
|
|
4855
|
-
import { homedir as
|
|
4856
|
-
import { dirname as
|
|
4857
|
-
var
|
|
5351
|
+
import { homedir as homedir25, hostname as hostname5 } from "os";
|
|
5352
|
+
import { dirname as dirname6, join as join26 } from "path";
|
|
5353
|
+
var TOOL_ID16 = "zcode";
|
|
4858
5354
|
var TOOL_NAME14 = "ZCode";
|
|
4859
|
-
var
|
|
5355
|
+
var DEFAULT_DB_PATH4 = join26(homedir25(), ".zcode", "cli", "db", "db.sqlite");
|
|
4860
5356
|
var MODEL_USAGE_QUERY = `SELECT
|
|
4861
5357
|
model_usage.session_id as sessionId,
|
|
4862
5358
|
session.directory as directory,
|
|
@@ -4865,13 +5361,15 @@ var MODEL_USAGE_QUERY = `SELECT
|
|
|
4865
5361
|
model_usage.input_tokens as inputTokens,
|
|
4866
5362
|
model_usage.output_tokens as outputTokens,
|
|
4867
5363
|
model_usage.reasoning_tokens as reasoningTokens,
|
|
4868
|
-
model_usage.cache_read_input_tokens as cacheReadInputTokens
|
|
5364
|
+
model_usage.cache_read_input_tokens as cacheReadInputTokens,
|
|
5365
|
+
model_usage.cache_creation_input_tokens as cacheCreationInputTokens
|
|
4869
5366
|
FROM model_usage
|
|
4870
5367
|
LEFT JOIN session ON session.id = model_usage.session_id
|
|
4871
5368
|
WHERE model_usage.input_tokens > 0
|
|
4872
5369
|
OR model_usage.output_tokens > 0
|
|
4873
5370
|
OR model_usage.reasoning_tokens > 0
|
|
4874
|
-
OR model_usage.cache_read_input_tokens > 0
|
|
5371
|
+
OR model_usage.cache_read_input_tokens > 0
|
|
5372
|
+
OR model_usage.cache_creation_input_tokens > 0`;
|
|
4875
5373
|
var SESSION_QUERY = `SELECT
|
|
4876
5374
|
id,
|
|
4877
5375
|
directory,
|
|
@@ -4891,9 +5389,9 @@ var TURN_USAGE_QUERY = `SELECT
|
|
|
4891
5389
|
WHERE duration_ms IS NOT NULL`;
|
|
4892
5390
|
function createToolDefinition10(dbPath) {
|
|
4893
5391
|
return {
|
|
4894
|
-
id:
|
|
5392
|
+
id: TOOL_ID16,
|
|
4895
5393
|
name: TOOL_NAME14,
|
|
4896
|
-
dataDir:
|
|
5394
|
+
dataDir: dirname6(dbPath)
|
|
4897
5395
|
};
|
|
4898
5396
|
}
|
|
4899
5397
|
function toSafeNumber10(value) {
|
|
@@ -4940,7 +5438,7 @@ function getOrCreateDraft(drafts, sessionId, project) {
|
|
|
4940
5438
|
}
|
|
4941
5439
|
const next = {
|
|
4942
5440
|
sessionId,
|
|
4943
|
-
source:
|
|
5441
|
+
source: TOOL_ID16,
|
|
4944
5442
|
project,
|
|
4945
5443
|
firstMessageAt: null,
|
|
4946
5444
|
lastMessageAt: null,
|
|
@@ -4965,13 +5463,15 @@ function buildSessionUsage3(entries) {
|
|
|
4965
5463
|
byModel = /* @__PURE__ */ new Map();
|
|
4966
5464
|
usageBySession.set(entry.sessionId, byModel);
|
|
4967
5465
|
}
|
|
4968
|
-
const
|
|
5466
|
+
const cacheCreationTokens = entry.cacheCreationTokens ?? 0;
|
|
5467
|
+
const totalTokens = entry.inputTokens + entry.outputTokens + entry.reasoningTokens + entry.cachedTokens + cacheCreationTokens;
|
|
4969
5468
|
const existing = byModel.get(entry.model);
|
|
4970
5469
|
if (existing) {
|
|
4971
5470
|
existing.inputTokens += entry.inputTokens;
|
|
4972
5471
|
existing.outputTokens += entry.outputTokens;
|
|
4973
5472
|
existing.reasoningTokens += entry.reasoningTokens;
|
|
4974
5473
|
existing.cachedTokens += entry.cachedTokens;
|
|
5474
|
+
existing.cacheCreationTokens = (existing.cacheCreationTokens ?? 0) + cacheCreationTokens;
|
|
4975
5475
|
existing.totalTokens += totalTokens;
|
|
4976
5476
|
continue;
|
|
4977
5477
|
}
|
|
@@ -4981,6 +5481,7 @@ function buildSessionUsage3(entries) {
|
|
|
4981
5481
|
outputTokens: entry.outputTokens,
|
|
4982
5482
|
reasoningTokens: entry.reasoningTokens,
|
|
4983
5483
|
cachedTokens: entry.cachedTokens,
|
|
5484
|
+
cacheCreationTokens,
|
|
4984
5485
|
totalTokens
|
|
4985
5486
|
});
|
|
4986
5487
|
}
|
|
@@ -5068,6 +5569,10 @@ function buildSessions2(input2) {
|
|
|
5068
5569
|
(sum, usage) => sum + usage.cachedTokens,
|
|
5069
5570
|
0
|
|
5070
5571
|
);
|
|
5572
|
+
const cacheCreationTokens = modelUsages.reduce(
|
|
5573
|
+
(sum, usage) => sum + (usage.cacheCreationTokens ?? 0),
|
|
5574
|
+
0
|
|
5575
|
+
);
|
|
5071
5576
|
const totalTokens = modelUsages.reduce(
|
|
5072
5577
|
(sum, usage) => sum + usage.totalTokens,
|
|
5073
5578
|
0
|
|
@@ -5094,6 +5599,7 @@ function buildSessions2(input2) {
|
|
|
5094
5599
|
outputTokens,
|
|
5095
5600
|
reasoningTokens,
|
|
5096
5601
|
cachedTokens,
|
|
5602
|
+
cacheCreationTokens,
|
|
5097
5603
|
totalTokens,
|
|
5098
5604
|
primaryModel: modelUsages[0]?.model ?? "",
|
|
5099
5605
|
modelUsages
|
|
@@ -5105,7 +5611,7 @@ var ZCodeParser = class {
|
|
|
5105
5611
|
dbPath;
|
|
5106
5612
|
queryRows;
|
|
5107
5613
|
constructor(options = {}) {
|
|
5108
|
-
this.dbPath = options.dbPath ||
|
|
5614
|
+
this.dbPath = options.dbPath || DEFAULT_DB_PATH4;
|
|
5109
5615
|
this.queryRows = options.queryRows || readSqliteRows;
|
|
5110
5616
|
this.tool = createToolDefinition10(this.dbPath);
|
|
5111
5617
|
}
|
|
@@ -5123,23 +5629,31 @@ var ZCodeParser = class {
|
|
|
5123
5629
|
if (!timestamp) {
|
|
5124
5630
|
continue;
|
|
5125
5631
|
}
|
|
5126
|
-
const inputTokens = toSafeNumber10(row.inputTokens);
|
|
5127
|
-
const outputTokens = toSafeNumber10(row.outputTokens);
|
|
5128
|
-
const reasoningTokens = toSafeNumber10(row.reasoningTokens);
|
|
5129
5632
|
const cachedTokens = toSafeNumber10(row.cacheReadInputTokens);
|
|
5130
|
-
|
|
5633
|
+
const cacheCreationTokens = toSafeNumber10(row.cacheCreationInputTokens);
|
|
5634
|
+
const inputTokens = Math.max(
|
|
5635
|
+
0,
|
|
5636
|
+
toSafeNumber10(row.inputTokens) - cachedTokens - cacheCreationTokens
|
|
5637
|
+
);
|
|
5638
|
+
const reasoningTokens = toSafeNumber10(row.reasoningTokens);
|
|
5639
|
+
const outputTokens = Math.max(
|
|
5640
|
+
0,
|
|
5641
|
+
toSafeNumber10(row.outputTokens) - reasoningTokens
|
|
5642
|
+
);
|
|
5643
|
+
if (inputTokens + outputTokens + reasoningTokens + cachedTokens + cacheCreationTokens === 0) {
|
|
5131
5644
|
continue;
|
|
5132
5645
|
}
|
|
5133
5646
|
entries.push({
|
|
5134
5647
|
sessionId: getString3(row.sessionId) ?? void 0,
|
|
5135
|
-
source:
|
|
5648
|
+
source: TOOL_ID16,
|
|
5136
5649
|
model: getString3(row.model) ?? "unknown",
|
|
5137
5650
|
project: getPathLeaf7(getString3(row.directory)),
|
|
5138
5651
|
timestamp,
|
|
5139
5652
|
inputTokens,
|
|
5140
5653
|
outputTokens,
|
|
5141
5654
|
reasoningTokens,
|
|
5142
|
-
cachedTokens
|
|
5655
|
+
cachedTokens,
|
|
5656
|
+
cacheCreationTokens
|
|
5143
5657
|
});
|
|
5144
5658
|
}
|
|
5145
5659
|
let sessionRows = [];
|
|
@@ -5174,20 +5688,20 @@ var ZCodeParser = class {
|
|
|
5174
5688
|
registerParser(new ZCodeParser());
|
|
5175
5689
|
|
|
5176
5690
|
// src/parsers/qodercli.ts
|
|
5177
|
-
import { existsSync as existsSync25, readdirSync as
|
|
5178
|
-
import { homedir as
|
|
5179
|
-
import { basename as
|
|
5180
|
-
var
|
|
5691
|
+
import { existsSync as existsSync25, readdirSync as readdirSync13 } from "fs";
|
|
5692
|
+
import { homedir as homedir26 } from "os";
|
|
5693
|
+
import { basename as basename11, join as join27 } from "path";
|
|
5694
|
+
var TOOL_ID17 = "qodercli";
|
|
5181
5695
|
var TOOL_NAME15 = "Qoder CLI";
|
|
5182
|
-
var DEFAULT_PROJECTS_DIR =
|
|
5183
|
-
var DEFAULT_LOGS_DIR =
|
|
5184
|
-
var DEFAULT_RUNS_DIR =
|
|
5696
|
+
var DEFAULT_PROJECTS_DIR = join27(homedir26(), ".qoder", "projects");
|
|
5697
|
+
var DEFAULT_LOGS_DIR = join27(homedir26(), ".qoder", "logs", "sessions");
|
|
5698
|
+
var DEFAULT_RUNS_DIR = join27(homedir26(), ".qoder", "logs", "runs");
|
|
5185
5699
|
var CLI_ENTRYPOINT = "cli";
|
|
5186
5700
|
var IDE_ENTRYPOINT = "acp";
|
|
5187
5701
|
var CREDIT_MODEL_FALLBACK = "credits";
|
|
5188
5702
|
function createToolDefinition11(projectsDir) {
|
|
5189
5703
|
return {
|
|
5190
|
-
id:
|
|
5704
|
+
id: TOOL_ID17,
|
|
5191
5705
|
name: TOOL_NAME15,
|
|
5192
5706
|
dataDir: projectsDir
|
|
5193
5707
|
};
|
|
@@ -5227,11 +5741,11 @@ function classifyQoderEntrypoint(content) {
|
|
|
5227
5741
|
return "unknown";
|
|
5228
5742
|
}
|
|
5229
5743
|
function isQodercliBinaryPresent() {
|
|
5230
|
-
const home =
|
|
5744
|
+
const home = homedir26();
|
|
5231
5745
|
const candidates = [
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5746
|
+
join27(home, ".local", "bin", "qodercli"),
|
|
5747
|
+
join27(home, ".qoder", "bin", "qodercli"),
|
|
5748
|
+
join27(home, ".qoder-cli")
|
|
5235
5749
|
];
|
|
5236
5750
|
return candidates.some((path) => existsSync25(path));
|
|
5237
5751
|
}
|
|
@@ -5241,7 +5755,7 @@ function projectFromEncodedSlug(slug) {
|
|
|
5241
5755
|
}
|
|
5242
5756
|
function projectFromCwd(cwd) {
|
|
5243
5757
|
if (!cwd) return "unknown";
|
|
5244
|
-
const leaf =
|
|
5758
|
+
const leaf = basename11(cwd.replace(/[\\/]+$/, ""));
|
|
5245
5759
|
return leaf || "unknown";
|
|
5246
5760
|
}
|
|
5247
5761
|
function totalCreditsUsed(quota) {
|
|
@@ -5304,7 +5818,7 @@ function creditSnapshotsToEntries(snapshots) {
|
|
|
5304
5818
|
if (creditDelta > 0) {
|
|
5305
5819
|
entries.push({
|
|
5306
5820
|
sessionId: snap.sessionId,
|
|
5307
|
-
source:
|
|
5821
|
+
source: TOOL_ID17,
|
|
5308
5822
|
model: snap.model || CREDIT_MODEL_FALLBACK,
|
|
5309
5823
|
project: snap.project,
|
|
5310
5824
|
timestamp: snap.timestamp,
|
|
@@ -5344,7 +5858,7 @@ var QoderCliParser = class {
|
|
|
5344
5858
|
parseProjectSessions(sessionEvents) {
|
|
5345
5859
|
if (!existsSync25(this.projectsDir)) return;
|
|
5346
5860
|
for (const filePath of findJsonlFiles(this.projectsDir)) {
|
|
5347
|
-
if (
|
|
5861
|
+
if (basename11(filePath).startsWith("verified-")) continue;
|
|
5348
5862
|
const content = readFileSafe(filePath);
|
|
5349
5863
|
if (!content) continue;
|
|
5350
5864
|
if (classifyQoderEntrypoint(content) !== CLI_ENTRYPOINT) continue;
|
|
@@ -5364,7 +5878,7 @@ var QoderCliParser = class {
|
|
|
5364
5878
|
if (Number.isNaN(ts.getTime())) continue;
|
|
5365
5879
|
sessionEvents.push({
|
|
5366
5880
|
sessionId,
|
|
5367
|
-
source:
|
|
5881
|
+
source: TOOL_ID17,
|
|
5368
5882
|
project,
|
|
5369
5883
|
timestamp: ts,
|
|
5370
5884
|
role: obj.type === "user" ? "user" : "assistant"
|
|
@@ -5382,7 +5896,7 @@ var QoderCliParser = class {
|
|
|
5382
5896
|
if (!existsSync25(this.runsDir)) return [];
|
|
5383
5897
|
let runDirs;
|
|
5384
5898
|
try {
|
|
5385
|
-
runDirs =
|
|
5899
|
+
runDirs = readdirSync13(this.runsDir, { withFileTypes: true }).filter(
|
|
5386
5900
|
(d) => d.isDirectory()
|
|
5387
5901
|
);
|
|
5388
5902
|
} catch {
|
|
@@ -5390,15 +5904,15 @@ var QoderCliParser = class {
|
|
|
5390
5904
|
}
|
|
5391
5905
|
const allSnapshots = [];
|
|
5392
5906
|
for (const dir of runDirs) {
|
|
5393
|
-
const runPath =
|
|
5394
|
-
const logPath =
|
|
5907
|
+
const runPath = join27(this.runsDir, dir.name);
|
|
5908
|
+
const logPath = join27(runPath, "qodercli.log");
|
|
5395
5909
|
if (!existsSync25(logPath)) continue;
|
|
5396
5910
|
const logContent = readFileSafe(logPath);
|
|
5397
5911
|
if (!logContent?.includes("quota/usage response:")) {
|
|
5398
5912
|
continue;
|
|
5399
5913
|
}
|
|
5400
5914
|
const manifest = parseRunManifest(
|
|
5401
|
-
readFileSafe(
|
|
5915
|
+
readFileSafe(join27(runPath, "manifest.json"))
|
|
5402
5916
|
);
|
|
5403
5917
|
const project = manifest?.project_id ? projectFromEncodedSlug(manifest.project_id) : projectFromCwd(manifest?.cwd);
|
|
5404
5918
|
const snapshots = extractCreditSnapshotsFromLog(logContent, {
|
|
@@ -5416,16 +5930,16 @@ var QoderCliParser = class {
|
|
|
5416
5930
|
registerParser(new QoderCliParser());
|
|
5417
5931
|
|
|
5418
5932
|
// src/parsers/grok-build.ts
|
|
5419
|
-
import { existsSync as existsSync26, readdirSync as
|
|
5420
|
-
import { homedir as
|
|
5421
|
-
import { basename as
|
|
5422
|
-
var
|
|
5933
|
+
import { existsSync as existsSync26, readdirSync as readdirSync14 } from "fs";
|
|
5934
|
+
import { homedir as homedir27 } from "os";
|
|
5935
|
+
import { basename as basename12, join as join28 } from "path";
|
|
5936
|
+
var TOOL_ID18 = "grok-build";
|
|
5423
5937
|
var TOOL_NAME16 = "Grok Build";
|
|
5424
|
-
var
|
|
5425
|
-
var DEFAULT_ARCHIVE_DIR2 =
|
|
5938
|
+
var DEFAULT_DATA_DIR6 = join28(homedir27(), ".grok", "sessions");
|
|
5939
|
+
var DEFAULT_ARCHIVE_DIR2 = join28(homedir27(), ".grok", "archived_sessions");
|
|
5426
5940
|
function createToolDefinition12(dataDir) {
|
|
5427
5941
|
return {
|
|
5428
|
-
id:
|
|
5942
|
+
id: TOOL_ID18,
|
|
5429
5943
|
name: TOOL_NAME16,
|
|
5430
5944
|
dataDir
|
|
5431
5945
|
};
|
|
@@ -5437,10 +5951,10 @@ function toSafeNumber12(value) {
|
|
|
5437
5951
|
function projectFromEncodedCwd(encoded) {
|
|
5438
5952
|
try {
|
|
5439
5953
|
const decoded = decodeURIComponent(encoded);
|
|
5440
|
-
const leaf =
|
|
5954
|
+
const leaf = basename12(decoded.replace(/[\\/]+$/, ""));
|
|
5441
5955
|
return leaf || "unknown";
|
|
5442
5956
|
} catch {
|
|
5443
|
-
const leaf =
|
|
5957
|
+
const leaf = basename12(encoded);
|
|
5444
5958
|
return leaf || "unknown";
|
|
5445
5959
|
}
|
|
5446
5960
|
}
|
|
@@ -5477,7 +5991,7 @@ function pushUsageEntries(entries, args) {
|
|
|
5477
5991
|
}
|
|
5478
5992
|
modelEntries.push({
|
|
5479
5993
|
sessionId,
|
|
5480
|
-
source:
|
|
5994
|
+
source: TOOL_ID18,
|
|
5481
5995
|
model: model || fallbackModel || "unknown",
|
|
5482
5996
|
project,
|
|
5483
5997
|
timestamp,
|
|
@@ -5494,25 +6008,25 @@ function findSessionDirs(dataDir) {
|
|
|
5494
6008
|
if (!existsSync26(dataDir)) return results;
|
|
5495
6009
|
let projectDirs;
|
|
5496
6010
|
try {
|
|
5497
|
-
projectDirs =
|
|
6011
|
+
projectDirs = readdirSync14(dataDir, { withFileTypes: true });
|
|
5498
6012
|
} catch {
|
|
5499
6013
|
return results;
|
|
5500
6014
|
}
|
|
5501
6015
|
for (const projectEntry of projectDirs) {
|
|
5502
6016
|
if (!projectEntry.isDirectory()) continue;
|
|
5503
6017
|
if (projectEntry.name.endsWith(".sqlite")) continue;
|
|
5504
|
-
const projectDir =
|
|
6018
|
+
const projectDir = join28(dataDir, projectEntry.name);
|
|
5505
6019
|
const project = projectFromEncodedCwd(projectEntry.name);
|
|
5506
6020
|
let sessionEntries;
|
|
5507
6021
|
try {
|
|
5508
|
-
sessionEntries =
|
|
6022
|
+
sessionEntries = readdirSync14(projectDir, { withFileTypes: true });
|
|
5509
6023
|
} catch {
|
|
5510
6024
|
continue;
|
|
5511
6025
|
}
|
|
5512
6026
|
for (const sessionEntry of sessionEntries) {
|
|
5513
6027
|
if (!sessionEntry.isDirectory()) continue;
|
|
5514
|
-
const sessionDir =
|
|
5515
|
-
if (!existsSync26(
|
|
6028
|
+
const sessionDir = join28(projectDir, sessionEntry.name);
|
|
6029
|
+
if (!existsSync26(join28(sessionDir, "updates.jsonl"))) continue;
|
|
5516
6030
|
results.push({
|
|
5517
6031
|
sessionDir,
|
|
5518
6032
|
project,
|
|
@@ -5523,7 +6037,7 @@ function findSessionDirs(dataDir) {
|
|
|
5523
6037
|
return results;
|
|
5524
6038
|
}
|
|
5525
6039
|
function readFallbackModel(sessionDir) {
|
|
5526
|
-
const summaryPath =
|
|
6040
|
+
const summaryPath = join28(sessionDir, "summary.json");
|
|
5527
6041
|
const content = readFileSafe(summaryPath);
|
|
5528
6042
|
if (!content) return "unknown";
|
|
5529
6043
|
try {
|
|
@@ -5534,7 +6048,7 @@ function readFallbackModel(sessionDir) {
|
|
|
5534
6048
|
}
|
|
5535
6049
|
}
|
|
5536
6050
|
var GrokBuildParser = class {
|
|
5537
|
-
constructor(dataDir =
|
|
6051
|
+
constructor(dataDir = DEFAULT_DATA_DIR6, archiveDir = dataDir === DEFAULT_DATA_DIR6 ? DEFAULT_ARCHIVE_DIR2 : void 0) {
|
|
5538
6052
|
this.dataDir = dataDir;
|
|
5539
6053
|
this.archiveDir = archiveDir;
|
|
5540
6054
|
this.tool = createToolDefinition12(dataDir);
|
|
@@ -5550,7 +6064,7 @@ var GrokBuildParser = class {
|
|
|
5550
6064
|
...findSessionDirs(this.dataDir)
|
|
5551
6065
|
];
|
|
5552
6066
|
for (const { sessionDir, project, sessionId } of sessions) {
|
|
5553
|
-
const updatesPath =
|
|
6067
|
+
const updatesPath = join28(sessionDir, "updates.jsonl");
|
|
5554
6068
|
const content = readFileSafe(updatesPath);
|
|
5555
6069
|
if (!content) continue;
|
|
5556
6070
|
const fallbackModel = readFallbackModel(sessionDir);
|
|
@@ -5587,7 +6101,7 @@ var GrokBuildParser = class {
|
|
|
5587
6101
|
]),
|
|
5588
6102
|
{
|
|
5589
6103
|
sessionId: sid,
|
|
5590
|
-
source:
|
|
6104
|
+
source: TOOL_ID18,
|
|
5591
6105
|
project,
|
|
5592
6106
|
timestamp: ts,
|
|
5593
6107
|
role: "user"
|
|
@@ -5603,7 +6117,7 @@ var GrokBuildParser = class {
|
|
|
5603
6117
|
continue;
|
|
5604
6118
|
sessionEvents.set(eventKey, {
|
|
5605
6119
|
sessionId: sid,
|
|
5606
|
-
source:
|
|
6120
|
+
source: TOOL_ID18,
|
|
5607
6121
|
project,
|
|
5608
6122
|
timestamp: ts,
|
|
5609
6123
|
role: "assistant"
|
|
@@ -5627,22 +6141,22 @@ var GrokBuildParser = class {
|
|
|
5627
6141
|
};
|
|
5628
6142
|
}
|
|
5629
6143
|
isInstalled() {
|
|
5630
|
-
return existsSync26(this.dataDir) || Boolean(this.archiveDir && existsSync26(this.archiveDir)) || existsSync26(
|
|
6144
|
+
return existsSync26(this.dataDir) || Boolean(this.archiveDir && existsSync26(this.archiveDir)) || existsSync26(join28(homedir27(), ".grok")) || existsSync26(join28(homedir27(), ".local", "bin", "grok"));
|
|
5631
6145
|
}
|
|
5632
6146
|
};
|
|
5633
6147
|
registerParser(new GrokBuildParser());
|
|
5634
6148
|
|
|
5635
6149
|
// src/parsers/atomcode.ts
|
|
5636
6150
|
import { existsSync as existsSync27 } from "fs";
|
|
5637
|
-
import { homedir as
|
|
5638
|
-
import { basename as
|
|
5639
|
-
var
|
|
6151
|
+
import { homedir as homedir28 } from "os";
|
|
6152
|
+
import { basename as basename13, join as join29 } from "path";
|
|
6153
|
+
var TOOL_ID19 = "atomcode";
|
|
5640
6154
|
var TOOL_NAME17 = "AtomCode";
|
|
5641
|
-
var DEFAULT_SESSIONS_DIR6 =
|
|
6155
|
+
var DEFAULT_SESSIONS_DIR6 = join29(homedir28(), ".atomcode", "sessions");
|
|
5642
6156
|
function getAtomCodeSessionsDirs(env = process.env) {
|
|
5643
6157
|
const dirs = [
|
|
5644
6158
|
env.TOKEN_ARENA_ATOMCODE_DIR,
|
|
5645
|
-
env.ATOMCODE_HOME ?
|
|
6159
|
+
env.ATOMCODE_HOME ? join29(env.ATOMCODE_HOME, "sessions") : void 0,
|
|
5646
6160
|
DEFAULT_SESSIONS_DIR6
|
|
5647
6161
|
].filter((value) => Boolean(value));
|
|
5648
6162
|
return Array.from(new Set(dirs));
|
|
@@ -5651,7 +6165,7 @@ function toNonNegativeNumber3(value) {
|
|
|
5651
6165
|
const numberValue = Number(value);
|
|
5652
6166
|
return Number.isFinite(numberValue) && numberValue >= 0 ? numberValue : 0;
|
|
5653
6167
|
}
|
|
5654
|
-
function
|
|
6168
|
+
function parseTimestamp5(value) {
|
|
5655
6169
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5656
6170
|
const timestamp = new Date(value);
|
|
5657
6171
|
return Number.isNaN(timestamp.getTime()) ? null : timestamp;
|
|
@@ -5690,7 +6204,7 @@ function getMetaModel(meta) {
|
|
|
5690
6204
|
}
|
|
5691
6205
|
function getMetaProject(meta) {
|
|
5692
6206
|
if (typeof meta?.working_dir === "string" && meta.working_dir) {
|
|
5693
|
-
return
|
|
6207
|
+
return basename13(meta.working_dir) || "unknown";
|
|
5694
6208
|
}
|
|
5695
6209
|
return "unknown";
|
|
5696
6210
|
}
|
|
@@ -5700,7 +6214,7 @@ var AtomCodeParser = class {
|
|
|
5700
6214
|
constructor(sessionsDir) {
|
|
5701
6215
|
this.sessionsDirs = sessionsDir ? [sessionsDir] : getAtomCodeSessionsDirs();
|
|
5702
6216
|
this.tool = {
|
|
5703
|
-
id:
|
|
6217
|
+
id: TOOL_ID19,
|
|
5704
6218
|
name: TOOL_NAME17,
|
|
5705
6219
|
dataDir: this.sessionsDirs[0] ?? DEFAULT_SESSIONS_DIR6
|
|
5706
6220
|
};
|
|
@@ -5723,12 +6237,12 @@ var AtomCodeParser = class {
|
|
|
5723
6237
|
const model = getMetaModel(meta);
|
|
5724
6238
|
for (const row of rows) {
|
|
5725
6239
|
const sessionId = typeof row.session_id === "string" && row.session_id ? row.session_id : fallbackSessionId;
|
|
5726
|
-
const timestamp =
|
|
6240
|
+
const timestamp = parseTimestamp5(row.ts);
|
|
5727
6241
|
if (!timestamp) continue;
|
|
5728
6242
|
if (row.user !== void 0 || row.assistant !== void 0) {
|
|
5729
6243
|
sessionEvents.push({
|
|
5730
6244
|
sessionId,
|
|
5731
|
-
source:
|
|
6245
|
+
source: TOOL_ID19,
|
|
5732
6246
|
project,
|
|
5733
6247
|
timestamp,
|
|
5734
6248
|
role: row.user !== void 0 ? "user" : "assistant"
|
|
@@ -5757,7 +6271,7 @@ var AtomCodeParser = class {
|
|
|
5757
6271
|
seenEntryKeys.add(entryKey);
|
|
5758
6272
|
entries.push({
|
|
5759
6273
|
sessionId,
|
|
5760
|
-
source:
|
|
6274
|
+
source: TOOL_ID19,
|
|
5761
6275
|
model,
|
|
5762
6276
|
project,
|
|
5763
6277
|
timestamp,
|
|
@@ -5781,18 +6295,18 @@ var AtomCodeParser = class {
|
|
|
5781
6295
|
registerParser(new AtomCodeParser());
|
|
5782
6296
|
|
|
5783
6297
|
// src/parsers/dsh.ts
|
|
5784
|
-
import { existsSync as existsSync28, readdirSync as
|
|
5785
|
-
import { homedir as
|
|
5786
|
-
import { basename as
|
|
6298
|
+
import { existsSync as existsSync28, readdirSync as readdirSync15, readFileSync as readFileSync9 } from "fs";
|
|
6299
|
+
import { homedir as homedir29 } from "os";
|
|
6300
|
+
import { basename as basename14, join as join30 } from "path";
|
|
5787
6301
|
import * as zlib from "zlib";
|
|
5788
|
-
var
|
|
6302
|
+
var TOOL_ID20 = "dsh";
|
|
5789
6303
|
var TOOL_NAME18 = "DeepSeek Harness";
|
|
5790
|
-
var DEFAULT_SESSIONS_DIR7 =
|
|
6304
|
+
var DEFAULT_SESSIONS_DIR7 = join30(homedir29(), ".dsh", "sessions");
|
|
5791
6305
|
var LOG_BASENAME = "session";
|
|
5792
6306
|
function getDshSessionsDirs(env = process.env) {
|
|
5793
6307
|
const dirs = [
|
|
5794
6308
|
env.TOKEN_ARENA_DSH_DIR,
|
|
5795
|
-
env.DSH_HOME ?
|
|
6309
|
+
env.DSH_HOME ? join30(env.DSH_HOME, "sessions") : void 0,
|
|
5796
6310
|
DEFAULT_SESSIONS_DIR7
|
|
5797
6311
|
].filter((value) => Boolean(value));
|
|
5798
6312
|
return Array.from(new Set(dirs));
|
|
@@ -5883,8 +6397,8 @@ function findSessionLogs(dir) {
|
|
|
5883
6397
|
const results = [];
|
|
5884
6398
|
if (!existsSync28(dir)) return results;
|
|
5885
6399
|
try {
|
|
5886
|
-
for (const entry of
|
|
5887
|
-
const fullPath =
|
|
6400
|
+
for (const entry of readdirSync15(dir, { withFileTypes: true })) {
|
|
6401
|
+
const fullPath = join30(dir, entry.name);
|
|
5888
6402
|
if (entry.isDirectory()) {
|
|
5889
6403
|
results.push(...findSessionLogs(fullPath));
|
|
5890
6404
|
} else if (entry.name === `${LOG_BASENAME}.jsonl` || entry.name === `${LOG_BASENAME}.jsonl.zstd`) {
|
|
@@ -5915,7 +6429,7 @@ var DshParser = class {
|
|
|
5915
6429
|
constructor(sessionsDir) {
|
|
5916
6430
|
this.sessionsDirs = sessionsDir ? [sessionsDir] : getDshSessionsDirs();
|
|
5917
6431
|
this.tool = {
|
|
5918
|
-
id:
|
|
6432
|
+
id: TOOL_ID20,
|
|
5919
6433
|
name: TOOL_NAME18,
|
|
5920
6434
|
dataDir: this.sessionsDirs[0] ?? DEFAULT_SESSIONS_DIR7
|
|
5921
6435
|
};
|
|
@@ -5933,7 +6447,7 @@ var DshParser = class {
|
|
|
5933
6447
|
const relativeParts = filePath.slice(sessionsDir.length + 1).split(/[\\/]/);
|
|
5934
6448
|
const header = rows[0]?.type === "session" ? rows[0] : void 0;
|
|
5935
6449
|
const sessionId = (header?.id ?? decodeSegment(relativeParts[1] ?? "")) || "unknown";
|
|
5936
|
-
const project = typeof header?.cwd === "string" && header.cwd ?
|
|
6450
|
+
const project = typeof header?.cwd === "string" && header.cwd ? basename14(header.cwd) || "unknown" : projectFromDirName(relativeParts[0] ?? "");
|
|
5937
6451
|
let currentModel = "unknown";
|
|
5938
6452
|
for (const row of rows) {
|
|
5939
6453
|
if (row.type === "request/context") {
|
|
@@ -5953,7 +6467,7 @@ var DshParser = class {
|
|
|
5953
6467
|
if (timestamp) {
|
|
5954
6468
|
sessionEvents.push({
|
|
5955
6469
|
sessionId,
|
|
5956
|
-
source:
|
|
6470
|
+
source: TOOL_ID20,
|
|
5957
6471
|
project,
|
|
5958
6472
|
timestamp,
|
|
5959
6473
|
role: "user"
|
|
@@ -5966,7 +6480,7 @@ var DshParser = class {
|
|
|
5966
6480
|
if (timestamp && !isCompaction) {
|
|
5967
6481
|
sessionEvents.push({
|
|
5968
6482
|
sessionId,
|
|
5969
|
-
source:
|
|
6483
|
+
source: TOOL_ID20,
|
|
5970
6484
|
project,
|
|
5971
6485
|
timestamp,
|
|
5972
6486
|
role: "assistant"
|
|
@@ -6001,7 +6515,7 @@ var DshParser = class {
|
|
|
6001
6515
|
seenEntryKeys.add(entryKey);
|
|
6002
6516
|
entries.push({
|
|
6003
6517
|
sessionId,
|
|
6004
|
-
source:
|
|
6518
|
+
source: TOOL_ID20,
|
|
6005
6519
|
model,
|
|
6006
6520
|
project,
|
|
6007
6521
|
timestamp,
|
|
@@ -6027,11 +6541,11 @@ registerParser(new DshParser());
|
|
|
6027
6541
|
|
|
6028
6542
|
// src/parsers/cherry-studio.ts
|
|
6029
6543
|
import { existsSync as existsSync29 } from "fs";
|
|
6030
|
-
import { homedir as
|
|
6031
|
-
import { dirname as
|
|
6032
|
-
var
|
|
6544
|
+
import { homedir as homedir30 } from "os";
|
|
6545
|
+
import { dirname as dirname7, join as join31, resolve as resolve3 } from "path";
|
|
6546
|
+
var TOOL_ID21 = "cherry-studio";
|
|
6033
6547
|
var TOOL_NAME19 = "Cherry Studio";
|
|
6034
|
-
var DB_RELATIVE =
|
|
6548
|
+
var DB_RELATIVE = join31("Data", "cherrystudio.sqlite");
|
|
6035
6549
|
var USAGE_QUERY = `SELECT
|
|
6036
6550
|
u.model_id as modelId,
|
|
6037
6551
|
u.no_cache_tokens as noCacheTokens,
|
|
@@ -6054,14 +6568,14 @@ var MESSAGES_QUERY3 = `SELECT
|
|
|
6054
6568
|
ORDER BY created_at`;
|
|
6055
6569
|
function getDefaultUserDataDir(env = process.env) {
|
|
6056
6570
|
if (process.platform === "darwin") {
|
|
6057
|
-
return
|
|
6571
|
+
return join31(homedir30(), "Library", "Application Support", "CherryStudio");
|
|
6058
6572
|
}
|
|
6059
6573
|
if (process.platform === "win32") {
|
|
6060
|
-
const appData = env.APPDATA?.trim() ||
|
|
6061
|
-
return
|
|
6574
|
+
const appData = env.APPDATA?.trim() || join31(homedir30(), "AppData", "Roaming");
|
|
6575
|
+
return join31(appData, "CherryStudio");
|
|
6062
6576
|
}
|
|
6063
|
-
const xdgConfigHome = env.XDG_CONFIG_HOME?.trim() ||
|
|
6064
|
-
return
|
|
6577
|
+
const xdgConfigHome = env.XDG_CONFIG_HOME?.trim() || join31(homedir30(), ".config");
|
|
6578
|
+
return join31(xdgConfigHome, "CherryStudio");
|
|
6065
6579
|
}
|
|
6066
6580
|
function getCherryStudioDbPaths(env = process.env) {
|
|
6067
6581
|
const paths = [];
|
|
@@ -6069,10 +6583,10 @@ function getCherryStudioDbPaths(env = process.env) {
|
|
|
6069
6583
|
if (explicit) {
|
|
6070
6584
|
const resolved = resolve3(explicit);
|
|
6071
6585
|
paths.push(
|
|
6072
|
-
resolved.endsWith(".sqlite") ? resolved :
|
|
6586
|
+
resolved.endsWith(".sqlite") ? resolved : join31(resolved, DB_RELATIVE)
|
|
6073
6587
|
);
|
|
6074
6588
|
}
|
|
6075
|
-
paths.push(
|
|
6589
|
+
paths.push(join31(getDefaultUserDataDir(env), DB_RELATIVE));
|
|
6076
6590
|
return Array.from(new Set(paths));
|
|
6077
6591
|
}
|
|
6078
6592
|
function resolveCherryStudioDbPath() {
|
|
@@ -6081,9 +6595,9 @@ function resolveCherryStudioDbPath() {
|
|
|
6081
6595
|
}
|
|
6082
6596
|
function createToolDefinition13(dbPath) {
|
|
6083
6597
|
return {
|
|
6084
|
-
id:
|
|
6598
|
+
id: TOOL_ID21,
|
|
6085
6599
|
name: TOOL_NAME19,
|
|
6086
|
-
dataDir: dbPath ?
|
|
6600
|
+
dataDir: dbPath ? dirname7(dbPath) : join31(getDefaultUserDataDir(), "Data")
|
|
6087
6601
|
};
|
|
6088
6602
|
}
|
|
6089
6603
|
function toSafeCount(value) {
|
|
@@ -6148,7 +6662,7 @@ var CherryStudioParser = class {
|
|
|
6148
6662
|
// Rows the topic join could not resolve still count toward buckets,
|
|
6149
6663
|
// they just cannot take part in session timing.
|
|
6150
6664
|
sessionId: getNonEmptyString(row.sessionId) ?? void 0,
|
|
6151
|
-
source:
|
|
6665
|
+
source: TOOL_ID21,
|
|
6152
6666
|
model: getNonEmptyString(row.modelId) ?? "unknown",
|
|
6153
6667
|
project: "unknown",
|
|
6154
6668
|
timestamp,
|
|
@@ -6181,7 +6695,7 @@ var CherryStudioParser = class {
|
|
|
6181
6695
|
if (!timestamp) continue;
|
|
6182
6696
|
sessionEvents.push({
|
|
6183
6697
|
sessionId,
|
|
6184
|
-
source:
|
|
6698
|
+
source: TOOL_ID21,
|
|
6185
6699
|
project: "unknown",
|
|
6186
6700
|
timestamp,
|
|
6187
6701
|
role
|
|
@@ -6210,25 +6724,25 @@ import {
|
|
|
6210
6724
|
unlinkSync,
|
|
6211
6725
|
writeFileSync
|
|
6212
6726
|
} from "fs";
|
|
6213
|
-
import { join as
|
|
6727
|
+
import { join as join33 } from "path";
|
|
6214
6728
|
|
|
6215
6729
|
// src/infrastructure/xdg.ts
|
|
6216
|
-
import { homedir as
|
|
6217
|
-
import { join as
|
|
6730
|
+
import { homedir as homedir31 } from "os";
|
|
6731
|
+
import { join as join32 } from "path";
|
|
6218
6732
|
function getConfigHome() {
|
|
6219
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
6733
|
+
return process.env.XDG_CONFIG_HOME || join32(homedir31(), ".config");
|
|
6220
6734
|
}
|
|
6221
6735
|
function getStateHome() {
|
|
6222
|
-
return process.env.XDG_STATE_HOME ||
|
|
6736
|
+
return process.env.XDG_STATE_HOME || join32(homedir31(), ".local", "state");
|
|
6223
6737
|
}
|
|
6224
6738
|
function getRuntimeDir() {
|
|
6225
6739
|
return process.env.XDG_RUNTIME_DIR || getStateHome();
|
|
6226
6740
|
}
|
|
6227
6741
|
|
|
6228
6742
|
// src/infrastructure/config/manager.ts
|
|
6229
|
-
var CONFIG_DIR =
|
|
6743
|
+
var CONFIG_DIR = join33(getConfigHome(), "tokenarena");
|
|
6230
6744
|
var isDev = process.env.TOKEN_ARENA_DEV === "1";
|
|
6231
|
-
var CONFIG_FILE =
|
|
6745
|
+
var CONFIG_FILE = join33(CONFIG_DIR, isDev ? "config.dev.json" : "config.json");
|
|
6232
6746
|
var DEFAULT_API_URL = "https://token.guji.uno";
|
|
6233
6747
|
var VALID_CONFIG_KEYS = [
|
|
6234
6748
|
"apiKey",
|
|
@@ -7090,22 +7604,22 @@ import {
|
|
|
7090
7604
|
|
|
7091
7605
|
// src/infrastructure/runtime/paths.ts
|
|
7092
7606
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
7093
|
-
import { join as
|
|
7607
|
+
import { join as join34 } from "path";
|
|
7094
7608
|
var APP_NAME = "tokenarena";
|
|
7095
7609
|
function getRuntimeDirPath() {
|
|
7096
|
-
return
|
|
7610
|
+
return join34(getRuntimeDir(), APP_NAME);
|
|
7097
7611
|
}
|
|
7098
7612
|
function getStateDir() {
|
|
7099
|
-
return
|
|
7613
|
+
return join34(getStateHome(), APP_NAME);
|
|
7100
7614
|
}
|
|
7101
7615
|
function getSyncLockPath() {
|
|
7102
|
-
return
|
|
7616
|
+
return join34(getRuntimeDirPath(), "sync.lock");
|
|
7103
7617
|
}
|
|
7104
7618
|
function getSyncStatePath() {
|
|
7105
|
-
return
|
|
7619
|
+
return join34(getStateDir(), "status.json");
|
|
7106
7620
|
}
|
|
7107
7621
|
function getUploadManifestPath() {
|
|
7108
|
-
return
|
|
7622
|
+
return join34(getStateDir(), "upload-manifest.json");
|
|
7109
7623
|
}
|
|
7110
7624
|
function ensureAppDirs() {
|
|
7111
7625
|
mkdirSync2(getRuntimeDirPath(), { recursive: true });
|
|
@@ -7311,19 +7825,19 @@ function saveUploadManifest(manifest) {
|
|
|
7311
7825
|
|
|
7312
7826
|
// src/services/parse-cache.ts
|
|
7313
7827
|
import { createHash as createHash7 } from "crypto";
|
|
7314
|
-
import { mkdirSync as mkdirSync3, readFileSync as readFileSync14, statSync as
|
|
7828
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync14, statSync as statSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
7315
7829
|
import { hostname as hostname6 } from "os";
|
|
7316
|
-
import { join as
|
|
7830
|
+
import { join as join36 } from "path";
|
|
7317
7831
|
|
|
7318
7832
|
// src/infrastructure/runtime/cli-version.ts
|
|
7319
|
-
import { dirname as
|
|
7833
|
+
import { dirname as dirname8, join as join35 } from "path";
|
|
7320
7834
|
import { fileURLToPath } from "url";
|
|
7321
7835
|
var FALLBACK_VERSION = "0.0.0";
|
|
7322
7836
|
var versionCache = /* @__PURE__ */ new Map();
|
|
7323
7837
|
function readPackageVersion(startDir) {
|
|
7324
7838
|
let dir = startDir;
|
|
7325
7839
|
while (true) {
|
|
7326
|
-
const contents = readFileSafe(
|
|
7840
|
+
const contents = readFileSafe(join35(dir, "package.json"));
|
|
7327
7841
|
if (contents !== null) {
|
|
7328
7842
|
try {
|
|
7329
7843
|
const { version } = JSON.parse(contents);
|
|
@@ -7332,7 +7846,7 @@ function readPackageVersion(startDir) {
|
|
|
7332
7846
|
return void 0;
|
|
7333
7847
|
}
|
|
7334
7848
|
}
|
|
7335
|
-
const parent =
|
|
7849
|
+
const parent = dirname8(dir);
|
|
7336
7850
|
if (parent === dir) {
|
|
7337
7851
|
return void 0;
|
|
7338
7852
|
}
|
|
@@ -7340,7 +7854,7 @@ function readPackageVersion(startDir) {
|
|
|
7340
7854
|
}
|
|
7341
7855
|
}
|
|
7342
7856
|
function getCliVersion(metaUrl = import.meta.url) {
|
|
7343
|
-
const startDir =
|
|
7857
|
+
const startDir = dirname8(fileURLToPath(metaUrl));
|
|
7344
7858
|
const cached = versionCache.get(startDir);
|
|
7345
7859
|
if (cached !== void 0) {
|
|
7346
7860
|
return cached;
|
|
@@ -7354,10 +7868,10 @@ function getCliVersion(metaUrl = import.meta.url) {
|
|
|
7354
7868
|
var CACHE_FORMAT_VERSION = 1;
|
|
7355
7869
|
var SETTLE_MS = 5e3;
|
|
7356
7870
|
function getParseCacheDir() {
|
|
7357
|
-
return
|
|
7871
|
+
return join36(getStateDir(), "parse-cache");
|
|
7358
7872
|
}
|
|
7359
7873
|
function getParseCachePath(toolId) {
|
|
7360
|
-
return
|
|
7874
|
+
return join36(getParseCacheDir(), `${toolId}.json`);
|
|
7361
7875
|
}
|
|
7362
7876
|
function computeScanFingerprint(files, now = Date.now()) {
|
|
7363
7877
|
const hash = createHash7("sha256");
|
|
@@ -7367,7 +7881,7 @@ function computeScanFingerprint(files, now = Date.now()) {
|
|
|
7367
7881
|
for (const file of [...files].sort()) {
|
|
7368
7882
|
let stats;
|
|
7369
7883
|
try {
|
|
7370
|
-
stats =
|
|
7884
|
+
stats = statSync8(file);
|
|
7371
7885
|
} catch {
|
|
7372
7886
|
return null;
|
|
7373
7887
|
}
|
|
@@ -7575,12 +8089,12 @@ function createProjectIdentityResolver(settings) {
|
|
|
7575
8089
|
return identity;
|
|
7576
8090
|
};
|
|
7577
8091
|
}
|
|
7578
|
-
function toUploadBuckets(buckets, settings, device,
|
|
8092
|
+
function toUploadBuckets(buckets, settings, device, resolveProject2 = createProjectIdentityResolver(
|
|
7579
8093
|
settings
|
|
7580
8094
|
)) {
|
|
7581
8095
|
const aggregated = /* @__PURE__ */ new Map();
|
|
7582
8096
|
for (const bucket of buckets) {
|
|
7583
|
-
const project =
|
|
8097
|
+
const project = resolveProject2(bucket.project);
|
|
7584
8098
|
const key = [
|
|
7585
8099
|
bucket.source,
|
|
7586
8100
|
bucket.model,
|
|
@@ -7616,11 +8130,11 @@ function toUploadBuckets(buckets, settings, device, resolveProject = createProje
|
|
|
7616
8130
|
}
|
|
7617
8131
|
return Array.from(aggregated.values());
|
|
7618
8132
|
}
|
|
7619
|
-
function toUploadSessions(sessions, settings, device,
|
|
8133
|
+
function toUploadSessions(sessions, settings, device, resolveProject2 = createProjectIdentityResolver(
|
|
7620
8134
|
settings
|
|
7621
8135
|
)) {
|
|
7622
8136
|
return sessions.map((session) => {
|
|
7623
|
-
const project =
|
|
8137
|
+
const project = resolveProject2(session.project);
|
|
7624
8138
|
return {
|
|
7625
8139
|
source: session.source,
|
|
7626
8140
|
projectKey: project.projectKey,
|
|
@@ -7799,18 +8313,18 @@ async function runSync(config, opts = {}) {
|
|
|
7799
8313
|
deviceId: device.deviceId,
|
|
7800
8314
|
settings
|
|
7801
8315
|
});
|
|
7802
|
-
const
|
|
8316
|
+
const resolveProject2 = createProjectIdentityResolver(settings);
|
|
7803
8317
|
const uploadBuckets = toUploadBuckets(
|
|
7804
8318
|
allBuckets,
|
|
7805
8319
|
settings,
|
|
7806
8320
|
device,
|
|
7807
|
-
|
|
8321
|
+
resolveProject2
|
|
7808
8322
|
);
|
|
7809
8323
|
const uploadSessions = toUploadSessions(
|
|
7810
8324
|
allSessions,
|
|
7811
8325
|
settings,
|
|
7812
8326
|
device,
|
|
7813
|
-
|
|
8327
|
+
resolveProject2
|
|
7814
8328
|
);
|
|
7815
8329
|
const previousManifest = loadUploadManifest();
|
|
7816
8330
|
const uploadDiff = diffUploadManifest({
|
|
@@ -8032,8 +8546,8 @@ View your dashboard at: ${apiUrl}/usage`);
|
|
|
8032
8546
|
import { execFileSync as execFileSync8, spawn } from "child_process";
|
|
8033
8547
|
import { existsSync as existsSync36 } from "fs";
|
|
8034
8548
|
import { appendFile, mkdir, readFile } from "fs/promises";
|
|
8035
|
-
import { homedir as
|
|
8036
|
-
import { dirname as
|
|
8549
|
+
import { homedir as homedir34, platform as platform5 } from "os";
|
|
8550
|
+
import { dirname as dirname9, join as join37, posix as posix3, win32 } from "path";
|
|
8037
8551
|
|
|
8038
8552
|
// src/infrastructure/service/index.ts
|
|
8039
8553
|
import { platform as platform4 } from "os";
|
|
@@ -8041,7 +8555,7 @@ import { platform as platform4 } from "os";
|
|
|
8041
8555
|
// src/infrastructure/service/linux-systemd.ts
|
|
8042
8556
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
8043
8557
|
import { existsSync as existsSync34, mkdirSync as mkdirSync4, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
8044
|
-
import { homedir as
|
|
8558
|
+
import { homedir as homedir32, platform as platform2 } from "os";
|
|
8045
8559
|
import { posix } from "path";
|
|
8046
8560
|
|
|
8047
8561
|
// src/utils/command.ts
|
|
@@ -8122,10 +8636,10 @@ function escapeXml(value) {
|
|
|
8122
8636
|
|
|
8123
8637
|
// src/infrastructure/service/linux-systemd.ts
|
|
8124
8638
|
var SYSTEMD_SERVICE_NAME = "tokenarena";
|
|
8125
|
-
function getLinuxSystemdServiceDir(homePath =
|
|
8639
|
+
function getLinuxSystemdServiceDir(homePath = homedir32()) {
|
|
8126
8640
|
return posix.join(homePath, ".config", "systemd", "user");
|
|
8127
8641
|
}
|
|
8128
|
-
function getLinuxSystemdServiceFile(homePath =
|
|
8642
|
+
function getLinuxSystemdServiceFile(homePath = homedir32()) {
|
|
8129
8643
|
return posix.join(
|
|
8130
8644
|
getLinuxSystemdServiceDir(homePath),
|
|
8131
8645
|
`${SYSTEMD_SERVICE_NAME}.service`
|
|
@@ -8369,16 +8883,16 @@ function createLinuxSystemdServiceBackend() {
|
|
|
8369
8883
|
// src/infrastructure/service/macos-launchd.ts
|
|
8370
8884
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
8371
8885
|
import { existsSync as existsSync35, mkdirSync as mkdirSync5, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
8372
|
-
import { homedir as
|
|
8886
|
+
import { homedir as homedir33, platform as platform3 } from "os";
|
|
8373
8887
|
import { posix as posix2 } from "path";
|
|
8374
8888
|
var MACOS_LAUNCHD_LABEL = "com.guji.tokenarena";
|
|
8375
8889
|
function getCurrentUid() {
|
|
8376
8890
|
return typeof process.getuid === "function" ? process.getuid() : null;
|
|
8377
8891
|
}
|
|
8378
|
-
function getMacosLaunchAgentDir(homePath =
|
|
8892
|
+
function getMacosLaunchAgentDir(homePath = homedir33()) {
|
|
8379
8893
|
return posix2.join(homePath, "Library", "LaunchAgents");
|
|
8380
8894
|
}
|
|
8381
|
-
function getMacosLaunchAgentFile(homePath =
|
|
8895
|
+
function getMacosLaunchAgentFile(homePath = homedir33()) {
|
|
8382
8896
|
return posix2.join(
|
|
8383
8897
|
getMacosLaunchAgentDir(homePath),
|
|
8384
8898
|
`${MACOS_LAUNCHD_LABEL}.plist`
|
|
@@ -8505,7 +9019,7 @@ function writeLaunchAgentPlist() {
|
|
|
8505
9019
|
label: MACOS_LAUNCHD_LABEL,
|
|
8506
9020
|
programArguments: [command.execPath, ...command.args],
|
|
8507
9021
|
environment: getManagedServiceEnvironment(),
|
|
8508
|
-
workingDirectory:
|
|
9022
|
+
workingDirectory: homedir33(),
|
|
8509
9023
|
standardOutPath: stdoutPath,
|
|
8510
9024
|
standardErrorPath: stderrPath
|
|
8511
9025
|
});
|
|
@@ -8782,7 +9296,7 @@ function resolvePowerShellProfilePath() {
|
|
|
8782
9296
|
const systemRoot = process.env.SYSTEMROOT || "C:\\Windows";
|
|
8783
9297
|
const candidates = [
|
|
8784
9298
|
"pwsh.exe",
|
|
8785
|
-
|
|
9299
|
+
join37(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
|
|
8786
9300
|
];
|
|
8787
9301
|
for (const command of candidates) {
|
|
8788
9302
|
try {
|
|
@@ -8811,7 +9325,7 @@ function resolvePowerShellProfilePath() {
|
|
|
8811
9325
|
function resolveShellAliasSetup(options = {}) {
|
|
8812
9326
|
const currentPlatform = options.currentPlatform ?? platform5();
|
|
8813
9327
|
const env = options.env ?? process.env;
|
|
8814
|
-
const homeDir = options.homeDir ??
|
|
9328
|
+
const homeDir = options.homeDir ?? homedir34();
|
|
8815
9329
|
const pathExists = options.exists ?? existsSync36;
|
|
8816
9330
|
const shellFromEnv = env.SHELL ? basenameLikeShell(env.SHELL).toLowerCase() : "";
|
|
8817
9331
|
const shellName = shellFromEnv || (currentPlatform === "win32" ? "powershell" : "");
|
|
@@ -9007,7 +9521,7 @@ async function setupShellAlias() {
|
|
|
9007
9521
|
return;
|
|
9008
9522
|
}
|
|
9009
9523
|
try {
|
|
9010
|
-
await mkdir(
|
|
9524
|
+
await mkdir(dirname9(setup.configFile), { recursive: true });
|
|
9011
9525
|
let existingContent = "";
|
|
9012
9526
|
if (existsSync36(setup.configFile)) {
|
|
9013
9527
|
existingContent = await readFile(setup.configFile, "utf-8");
|
|
@@ -9171,16 +9685,16 @@ function buildToolSummary(input2) {
|
|
|
9171
9685
|
models.add(bucket.model || "unknown");
|
|
9172
9686
|
}
|
|
9173
9687
|
for (const session of input2.sessions) {
|
|
9174
|
-
const
|
|
9688
|
+
const projectName2 = session.project || "unknown";
|
|
9175
9689
|
const modelName = session.primaryModel || "unknown";
|
|
9176
|
-
projects.add(
|
|
9690
|
+
projects.add(projectName2);
|
|
9177
9691
|
models.add(modelName);
|
|
9178
|
-
const project = projectTotals.get(
|
|
9692
|
+
const project = projectTotals.get(projectName2);
|
|
9179
9693
|
if (project) {
|
|
9180
9694
|
project.sessions += 1;
|
|
9181
9695
|
} else {
|
|
9182
|
-
projectTotals.set(
|
|
9183
|
-
name:
|
|
9696
|
+
projectTotals.set(projectName2, {
|
|
9697
|
+
name: projectName2,
|
|
9184
9698
|
inputTokens: 0,
|
|
9185
9699
|
outputTokens: 0,
|
|
9186
9700
|
cachedTokens: 0,
|
|
@@ -9671,7 +10185,7 @@ async function runSyncCommand(opts = {}) {
|
|
|
9671
10185
|
|
|
9672
10186
|
// src/commands/uninstall.ts
|
|
9673
10187
|
import { existsSync as existsSync37, readFileSync as readFileSync15, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
|
|
9674
|
-
import { homedir as
|
|
10188
|
+
import { homedir as homedir35, platform as platform6 } from "os";
|
|
9675
10189
|
function removeShellAlias() {
|
|
9676
10190
|
const shell = process.env.SHELL;
|
|
9677
10191
|
if (!shell) return;
|
|
@@ -9680,17 +10194,17 @@ function removeShellAlias() {
|
|
|
9680
10194
|
let configFile;
|
|
9681
10195
|
switch (shellName) {
|
|
9682
10196
|
case "zsh":
|
|
9683
|
-
configFile = `${
|
|
10197
|
+
configFile = `${homedir35()}/.zshrc`;
|
|
9684
10198
|
break;
|
|
9685
10199
|
case "bash":
|
|
9686
|
-
if (platform6() === "darwin" && existsSync37(`${
|
|
9687
|
-
configFile = `${
|
|
10200
|
+
if (platform6() === "darwin" && existsSync37(`${homedir35()}/.bash_profile`)) {
|
|
10201
|
+
configFile = `${homedir35()}/.bash_profile`;
|
|
9688
10202
|
} else {
|
|
9689
|
-
configFile = `${
|
|
10203
|
+
configFile = `${homedir35()}/.bashrc`;
|
|
9690
10204
|
}
|
|
9691
10205
|
break;
|
|
9692
10206
|
case "fish":
|
|
9693
|
-
configFile = `${
|
|
10207
|
+
configFile = `${homedir35()}/.config/fish/config.fish`;
|
|
9694
10208
|
break;
|
|
9695
10209
|
default:
|
|
9696
10210
|
return;
|