@yishiguji/tokenarena 0.16.5 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +316 -59
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4494,57 +4494,314 @@ var RooCodeParser = class {
|
|
|
4494
4494
|
registerParser(new RooCodeParser());
|
|
4495
4495
|
|
|
4496
4496
|
// src/parsers/snow.ts
|
|
4497
|
-
import { existsSync as existsSync22 } from "fs";
|
|
4497
|
+
import { existsSync as existsSync22, readdirSync as readdirSync12, statSync as statSync6 } from "fs";
|
|
4498
4498
|
import { homedir as homedir22 } from "os";
|
|
4499
|
-
import { join as join23 } from "path";
|
|
4500
|
-
var
|
|
4499
|
+
import { basename as basename10, join as join23 } from "path";
|
|
4500
|
+
var DEFAULT_SNOW_DIR = join23(homedir22(), ".snow");
|
|
4501
|
+
var USAGE_DIR_NAME = "usage";
|
|
4502
|
+
var SESSIONS_DIR_NAME = "sessions";
|
|
4503
|
+
var NON_TRANSCRIPT_DIRS = /* @__PURE__ */ new Set(["subagent", "compressed"]);
|
|
4504
|
+
var SESSION_MATCH_WINDOW_MS = 5 * 60 * 1e3;
|
|
4505
|
+
var DAMAGED_TRANSCRIPT_SETTLE_MS = 60 * 1e3;
|
|
4506
|
+
var MIN_TIMESTAMP_MS = Date.UTC(2020, 0, 1);
|
|
4507
|
+
var MAX_TIMESTAMP_MS = Date.UTC(2080, 0, 1);
|
|
4501
4508
|
function toNonNegativeNumber2(value) {
|
|
4509
|
+
if (typeof value !== "number" && typeof value !== "string") return 0;
|
|
4502
4510
|
const numberValue = Number(value);
|
|
4503
4511
|
return Number.isFinite(numberValue) && numberValue >= 0 ? numberValue : 0;
|
|
4504
4512
|
}
|
|
4513
|
+
function toTime(value) {
|
|
4514
|
+
const time = typeof value === "string" ? Date.parse(value) : typeof value === "number" ? value : Number.NaN;
|
|
4515
|
+
return time >= MIN_TIMESTAMP_MS && time < MAX_TIMESTAMP_MS ? time : null;
|
|
4516
|
+
}
|
|
4517
|
+
function isRecord(value) {
|
|
4518
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4519
|
+
}
|
|
4520
|
+
function findSourceFiles(dir, extension, results = []) {
|
|
4521
|
+
let entries;
|
|
4522
|
+
try {
|
|
4523
|
+
entries = readdirSync12(dir, { withFileTypes: true });
|
|
4524
|
+
} catch (error) {
|
|
4525
|
+
const code = error.code;
|
|
4526
|
+
if (code === "ENOENT" || code === "ENOTDIR") return results;
|
|
4527
|
+
throw error;
|
|
4528
|
+
}
|
|
4529
|
+
for (const entry of entries) {
|
|
4530
|
+
const fullPath = join23(dir, entry.name);
|
|
4531
|
+
if (entry.isDirectory()) {
|
|
4532
|
+
if (extension === ".json" && NON_TRANSCRIPT_DIRS.has(entry.name)) {
|
|
4533
|
+
continue;
|
|
4534
|
+
}
|
|
4535
|
+
findSourceFiles(fullPath, extension, results);
|
|
4536
|
+
} else if (entry.name.endsWith(extension)) {
|
|
4537
|
+
results.push(fullPath);
|
|
4538
|
+
}
|
|
4539
|
+
}
|
|
4540
|
+
return results;
|
|
4541
|
+
}
|
|
4542
|
+
function resolveProject(session) {
|
|
4543
|
+
if (typeof session.projectPath === "string" && session.projectPath) {
|
|
4544
|
+
const segments = session.projectPath.split(/[\\/]+/).filter(Boolean);
|
|
4545
|
+
return segments.at(-1) || "unknown";
|
|
4546
|
+
}
|
|
4547
|
+
return "unknown";
|
|
4548
|
+
}
|
|
4549
|
+
function readTranscript(filePath) {
|
|
4550
|
+
const content = readFileSafe(filePath);
|
|
4551
|
+
if (content === null) return { status: "damaged" };
|
|
4552
|
+
let parsed;
|
|
4553
|
+
try {
|
|
4554
|
+
parsed = JSON.parse(content);
|
|
4555
|
+
} catch {
|
|
4556
|
+
return { status: "damaged" };
|
|
4557
|
+
}
|
|
4558
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.messages)) {
|
|
4559
|
+
return { status: "foreign" };
|
|
4560
|
+
}
|
|
4561
|
+
const session = parsed;
|
|
4562
|
+
const compressedFrom = typeof session.compressedFrom === "string" && session.compressedFrom ? session.compressedFrom : void 0;
|
|
4563
|
+
const copiedUntil = compressedFrom !== void 0 || typeof session.branchedFrom === "string" ? Math.max(
|
|
4564
|
+
toTime(session.compressedAt) ?? Number.NEGATIVE_INFINITY,
|
|
4565
|
+
toTime(session.createdAt) ?? Number.NEGATIVE_INFINITY
|
|
4566
|
+
) : Number.NEGATIVE_INFINITY;
|
|
4567
|
+
const messages = [];
|
|
4568
|
+
let lastMessageAt = 0;
|
|
4569
|
+
for (const message of parsed.messages) {
|
|
4570
|
+
if (!isRecord(message)) continue;
|
|
4571
|
+
if (message.role !== "user" && message.role !== "assistant") continue;
|
|
4572
|
+
const time = toTime(message.timestamp);
|
|
4573
|
+
if (time === null) continue;
|
|
4574
|
+
if (time > lastMessageAt) lastMessageAt = time;
|
|
4575
|
+
if (time <= copiedUntil) continue;
|
|
4576
|
+
messages.push({ role: message.role, time });
|
|
4577
|
+
}
|
|
4578
|
+
return {
|
|
4579
|
+
status: "ok",
|
|
4580
|
+
transcript: {
|
|
4581
|
+
filePath,
|
|
4582
|
+
sessionId: typeof session.id === "string" && session.id ? session.id : basename10(filePath, ".json"),
|
|
4583
|
+
project: resolveProject(session),
|
|
4584
|
+
updatedAt: toTime(session.updatedAt) ?? lastMessageAt,
|
|
4585
|
+
...compressedFrom !== void 0 ? { compressedFrom } : {},
|
|
4586
|
+
messages
|
|
4587
|
+
}
|
|
4588
|
+
};
|
|
4589
|
+
}
|
|
4590
|
+
function isNewerCopy(candidate, current) {
|
|
4591
|
+
if (candidate.updatedAt !== current.updatedAt) {
|
|
4592
|
+
return candidate.updatedAt > current.updatedAt;
|
|
4593
|
+
}
|
|
4594
|
+
if (candidate.messages.length !== current.messages.length) {
|
|
4595
|
+
return candidate.messages.length > current.messages.length;
|
|
4596
|
+
}
|
|
4597
|
+
return candidate.filePath < current.filePath;
|
|
4598
|
+
}
|
|
4599
|
+
function resolveConversationId(transcript, transcripts) {
|
|
4600
|
+
let current = transcript;
|
|
4601
|
+
const visited = /* @__PURE__ */ new Set([current.sessionId]);
|
|
4602
|
+
while (current.compressedFrom && !visited.has(current.compressedFrom)) {
|
|
4603
|
+
visited.add(current.compressedFrom);
|
|
4604
|
+
const source = transcripts.get(current.compressedFrom);
|
|
4605
|
+
if (!source) return current.compressedFrom;
|
|
4606
|
+
current = source;
|
|
4607
|
+
}
|
|
4608
|
+
return current.sessionId;
|
|
4609
|
+
}
|
|
4610
|
+
function mayStillBeWriting(filePath, now) {
|
|
4611
|
+
try {
|
|
4612
|
+
return now - statSync6(filePath).mtimeMs < DAMAGED_TRANSCRIPT_SETTLE_MS;
|
|
4613
|
+
} catch {
|
|
4614
|
+
return true;
|
|
4615
|
+
}
|
|
4616
|
+
}
|
|
4617
|
+
function distanceToNearest(sortedTimes, time) {
|
|
4618
|
+
let low = 0;
|
|
4619
|
+
let high = sortedTimes.length;
|
|
4620
|
+
while (low < high) {
|
|
4621
|
+
const middle = low + high >>> 1;
|
|
4622
|
+
if (sortedTimes[middle] < time) low = middle + 1;
|
|
4623
|
+
else high = middle;
|
|
4624
|
+
}
|
|
4625
|
+
let distance = Number.POSITIVE_INFINITY;
|
|
4626
|
+
if (low < sortedTimes.length) distance = sortedTimes[low] - time;
|
|
4627
|
+
if (low > 0) distance = Math.min(distance, time - sortedTimes[low - 1]);
|
|
4628
|
+
return distance;
|
|
4629
|
+
}
|
|
4630
|
+
function attributeUsage(entries, windows) {
|
|
4631
|
+
if (windows.length === 0) return;
|
|
4632
|
+
const byStart = [...windows].sort((left, right) => left.start - right.start);
|
|
4633
|
+
const byTime = entries.map((entry) => ({ entry, time: entry.timestamp.getTime() })).sort((left, right) => left.time - right.time);
|
|
4634
|
+
const open = [];
|
|
4635
|
+
let nextWindow = 0;
|
|
4636
|
+
for (const { entry, time } of byTime) {
|
|
4637
|
+
while (nextWindow < byStart.length && byStart[nextWindow].start <= time) {
|
|
4638
|
+
open.push(byStart[nextWindow]);
|
|
4639
|
+
nextWindow++;
|
|
4640
|
+
}
|
|
4641
|
+
let kept = 0;
|
|
4642
|
+
let best;
|
|
4643
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
4644
|
+
for (const window of open) {
|
|
4645
|
+
if (window.end < time) continue;
|
|
4646
|
+
open[kept++] = window;
|
|
4647
|
+
const distance = distanceToNearest(window.assistantTimes, time);
|
|
4648
|
+
if (best === void 0 || distance < bestDistance || distance === bestDistance && window.sessionId < best.sessionId) {
|
|
4649
|
+
best = window;
|
|
4650
|
+
bestDistance = distance;
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4653
|
+
open.length = kept;
|
|
4654
|
+
if (best) entry.sessionId = best.sessionId;
|
|
4655
|
+
}
|
|
4656
|
+
}
|
|
4505
4657
|
var SnowParser = class {
|
|
4506
|
-
constructor(
|
|
4507
|
-
this.
|
|
4508
|
-
this.tool = {
|
|
4658
|
+
constructor(snowDir = DEFAULT_SNOW_DIR) {
|
|
4659
|
+
this.snowDir = snowDir;
|
|
4660
|
+
this.tool = {
|
|
4661
|
+
id: "snow",
|
|
4662
|
+
name: "Snow CLI",
|
|
4663
|
+
dataDir: join23(snowDir, USAGE_DIR_NAME)
|
|
4664
|
+
};
|
|
4509
4665
|
}
|
|
4510
|
-
|
|
4666
|
+
snowDir;
|
|
4511
4667
|
tool;
|
|
4512
4668
|
async parse() {
|
|
4669
|
+
const usage = this.parseUsage();
|
|
4670
|
+
const sessions = this.parseSessions();
|
|
4671
|
+
attributeUsage(usage.entries, sessions.windows);
|
|
4672
|
+
return {
|
|
4673
|
+
buckets: aggregateToBuckets(usage.entries),
|
|
4674
|
+
sessions: extractSessions(sessions.events, usage.entries),
|
|
4675
|
+
...usage.incomplete ? { incomplete: true } : {}
|
|
4676
|
+
};
|
|
4677
|
+
}
|
|
4678
|
+
listSourceFiles() {
|
|
4679
|
+
return [
|
|
4680
|
+
...findSourceFiles(join23(this.snowDir, USAGE_DIR_NAME), ".jsonl"),
|
|
4681
|
+
...findSourceFiles(join23(this.snowDir, SESSIONS_DIR_NAME), ".json")
|
|
4682
|
+
];
|
|
4683
|
+
}
|
|
4684
|
+
/**
|
|
4685
|
+
* Build session timing events plus the windows used to attribute usage
|
|
4686
|
+
* records. Message timestamps drive turn boundaries, so active time, session
|
|
4687
|
+
* duration, and message counts all come from real transcripts.
|
|
4688
|
+
*
|
|
4689
|
+
* Transcripts only feed sessions, never buckets, so a damaged one is skipped
|
|
4690
|
+
* rather than holding back the whole source: Snow cannot open it either and
|
|
4691
|
+
* will not rewrite it, so deferring would block Snow uploads for good.
|
|
4692
|
+
*/
|
|
4693
|
+
parseSessions() {
|
|
4694
|
+
const transcripts = /* @__PURE__ */ new Map();
|
|
4695
|
+
const damaged = [];
|
|
4696
|
+
const now = Date.now();
|
|
4697
|
+
for (const filePath of findSourceFiles(
|
|
4698
|
+
join23(this.snowDir, SESSIONS_DIR_NAME),
|
|
4699
|
+
".json"
|
|
4700
|
+
)) {
|
|
4701
|
+
const read = readTranscript(filePath);
|
|
4702
|
+
if (read.status === "damaged") {
|
|
4703
|
+
if (!mayStillBeWriting(filePath, now)) damaged.push(filePath);
|
|
4704
|
+
continue;
|
|
4705
|
+
}
|
|
4706
|
+
if (read.status === "foreign") continue;
|
|
4707
|
+
const current = transcripts.get(read.transcript.sessionId);
|
|
4708
|
+
if (!current || isNewerCopy(read.transcript, current)) {
|
|
4709
|
+
transcripts.set(read.transcript.sessionId, read.transcript);
|
|
4710
|
+
}
|
|
4711
|
+
}
|
|
4712
|
+
if (damaged.length > 0) {
|
|
4713
|
+
const more = damaged.length > 1 ? ` and ${damaged.length - 1} more` : "";
|
|
4714
|
+
logger.warn(
|
|
4715
|
+
`Skipped damaged Snow session file ${damaged[0]}${more}. Token usage is still counted.`
|
|
4716
|
+
);
|
|
4717
|
+
}
|
|
4718
|
+
const events = [];
|
|
4719
|
+
const windows = [];
|
|
4720
|
+
for (const transcript of transcripts.values()) {
|
|
4721
|
+
if (transcript.messages.length === 0) continue;
|
|
4722
|
+
const sessionId = resolveConversationId(transcript, transcripts);
|
|
4723
|
+
const assistantTimes = [];
|
|
4724
|
+
let start = Number.POSITIVE_INFINITY;
|
|
4725
|
+
let end = Number.NEGATIVE_INFINITY;
|
|
4726
|
+
for (const message of transcript.messages) {
|
|
4727
|
+
events.push({
|
|
4728
|
+
sessionId,
|
|
4729
|
+
source: "snow",
|
|
4730
|
+
project: transcript.project,
|
|
4731
|
+
timestamp: new Date(message.time),
|
|
4732
|
+
role: message.role
|
|
4733
|
+
});
|
|
4734
|
+
if (message.role === "assistant") assistantTimes.push(message.time);
|
|
4735
|
+
if (message.time < start) start = message.time;
|
|
4736
|
+
if (message.time > end) end = message.time;
|
|
4737
|
+
}
|
|
4738
|
+
windows.push({
|
|
4739
|
+
sessionId,
|
|
4740
|
+
assistantTimes: assistantTimes.sort((left, right) => left - right),
|
|
4741
|
+
start: start - SESSION_MATCH_WINDOW_MS,
|
|
4742
|
+
end: end + SESSION_MATCH_WINDOW_MS
|
|
4743
|
+
});
|
|
4744
|
+
}
|
|
4745
|
+
return { events, windows };
|
|
4746
|
+
}
|
|
4747
|
+
/**
|
|
4748
|
+
* Parse usage JSONL. Session attribution happens afterwards, because it needs
|
|
4749
|
+
* the transcripts.
|
|
4750
|
+
*
|
|
4751
|
+
* The bucket project stays `unknown` on purpose; see the note on
|
|
4752
|
+
* `SESSION_MATCH_WINDOW_MS`.
|
|
4753
|
+
*/
|
|
4754
|
+
parseUsage() {
|
|
4513
4755
|
const entries = [];
|
|
4514
|
-
|
|
4756
|
+
let incomplete = false;
|
|
4757
|
+
for (const filePath of findSourceFiles(
|
|
4758
|
+
join23(this.snowDir, USAGE_DIR_NAME),
|
|
4759
|
+
".jsonl"
|
|
4760
|
+
)) {
|
|
4515
4761
|
const content = readFileSafe(filePath);
|
|
4516
|
-
if (
|
|
4762
|
+
if (content === null) {
|
|
4763
|
+
if (existsSync22(filePath)) incomplete = true;
|
|
4764
|
+
continue;
|
|
4765
|
+
}
|
|
4517
4766
|
for (const line of content.split("\n")) {
|
|
4518
4767
|
if (!line.trim()) continue;
|
|
4768
|
+
let record;
|
|
4519
4769
|
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
|
-
});
|
|
4770
|
+
const parsed = JSON.parse(line);
|
|
4771
|
+
if (!isRecord(parsed)) continue;
|
|
4772
|
+
record = parsed;
|
|
4540
4773
|
} catch {
|
|
4774
|
+
continue;
|
|
4541
4775
|
}
|
|
4776
|
+
const time = toTime(record.timestamp);
|
|
4777
|
+
if (time === null) continue;
|
|
4778
|
+
const reportedInputTokens = toNonNegativeNumber2(record.inputTokens);
|
|
4779
|
+
const outputTokens = toNonNegativeNumber2(record.outputTokens);
|
|
4780
|
+
const cachedTokens = toNonNegativeNumber2(record.cacheReadInputTokens);
|
|
4781
|
+
const cacheCreationTokens = toNonNegativeNumber2(
|
|
4782
|
+
record.cacheCreationInputTokens
|
|
4783
|
+
);
|
|
4784
|
+
const reasoningTokens = toNonNegativeNumber2(record.reasoningTokens);
|
|
4785
|
+
const inputTokens = record.cacheCreationInputTokens === void 0 && cachedTokens <= reportedInputTokens ? reportedInputTokens - cachedTokens : reportedInputTokens;
|
|
4786
|
+
if (inputTokens + outputTokens + cachedTokens + cacheCreationTokens + reasoningTokens === 0)
|
|
4787
|
+
continue;
|
|
4788
|
+
entries.push({
|
|
4789
|
+
source: "snow",
|
|
4790
|
+
model: typeof record.model === "string" && record.model ? record.model : "unknown",
|
|
4791
|
+
project: "unknown",
|
|
4792
|
+
timestamp: new Date(time),
|
|
4793
|
+
inputTokens,
|
|
4794
|
+
outputTokens,
|
|
4795
|
+
reasoningTokens,
|
|
4796
|
+
cachedTokens,
|
|
4797
|
+
cacheCreationTokens
|
|
4798
|
+
});
|
|
4542
4799
|
}
|
|
4543
4800
|
}
|
|
4544
|
-
return {
|
|
4801
|
+
return { entries, incomplete };
|
|
4545
4802
|
}
|
|
4546
4803
|
isInstalled() {
|
|
4547
|
-
return existsSync22(this.
|
|
4804
|
+
return existsSync22(join23(this.snowDir, USAGE_DIR_NAME));
|
|
4548
4805
|
}
|
|
4549
4806
|
};
|
|
4550
4807
|
registerParser(new SnowParser());
|
|
@@ -5174,9 +5431,9 @@ var ZCodeParser = class {
|
|
|
5174
5431
|
registerParser(new ZCodeParser());
|
|
5175
5432
|
|
|
5176
5433
|
// src/parsers/qodercli.ts
|
|
5177
|
-
import { existsSync as existsSync25, readdirSync as
|
|
5434
|
+
import { existsSync as existsSync25, readdirSync as readdirSync13 } from "fs";
|
|
5178
5435
|
import { homedir as homedir25 } from "os";
|
|
5179
|
-
import { basename as
|
|
5436
|
+
import { basename as basename11, join as join26 } from "path";
|
|
5180
5437
|
var TOOL_ID16 = "qodercli";
|
|
5181
5438
|
var TOOL_NAME15 = "Qoder CLI";
|
|
5182
5439
|
var DEFAULT_PROJECTS_DIR = join26(homedir25(), ".qoder", "projects");
|
|
@@ -5241,7 +5498,7 @@ function projectFromEncodedSlug(slug) {
|
|
|
5241
5498
|
}
|
|
5242
5499
|
function projectFromCwd(cwd) {
|
|
5243
5500
|
if (!cwd) return "unknown";
|
|
5244
|
-
const leaf =
|
|
5501
|
+
const leaf = basename11(cwd.replace(/[\\/]+$/, ""));
|
|
5245
5502
|
return leaf || "unknown";
|
|
5246
5503
|
}
|
|
5247
5504
|
function totalCreditsUsed(quota) {
|
|
@@ -5344,7 +5601,7 @@ var QoderCliParser = class {
|
|
|
5344
5601
|
parseProjectSessions(sessionEvents) {
|
|
5345
5602
|
if (!existsSync25(this.projectsDir)) return;
|
|
5346
5603
|
for (const filePath of findJsonlFiles(this.projectsDir)) {
|
|
5347
|
-
if (
|
|
5604
|
+
if (basename11(filePath).startsWith("verified-")) continue;
|
|
5348
5605
|
const content = readFileSafe(filePath);
|
|
5349
5606
|
if (!content) continue;
|
|
5350
5607
|
if (classifyQoderEntrypoint(content) !== CLI_ENTRYPOINT) continue;
|
|
@@ -5382,7 +5639,7 @@ var QoderCliParser = class {
|
|
|
5382
5639
|
if (!existsSync25(this.runsDir)) return [];
|
|
5383
5640
|
let runDirs;
|
|
5384
5641
|
try {
|
|
5385
|
-
runDirs =
|
|
5642
|
+
runDirs = readdirSync13(this.runsDir, { withFileTypes: true }).filter(
|
|
5386
5643
|
(d) => d.isDirectory()
|
|
5387
5644
|
);
|
|
5388
5645
|
} catch {
|
|
@@ -5416,12 +5673,12 @@ var QoderCliParser = class {
|
|
|
5416
5673
|
registerParser(new QoderCliParser());
|
|
5417
5674
|
|
|
5418
5675
|
// src/parsers/grok-build.ts
|
|
5419
|
-
import { existsSync as existsSync26, readdirSync as
|
|
5676
|
+
import { existsSync as existsSync26, readdirSync as readdirSync14 } from "fs";
|
|
5420
5677
|
import { homedir as homedir26 } from "os";
|
|
5421
|
-
import { basename as
|
|
5678
|
+
import { basename as basename12, join as join27 } from "path";
|
|
5422
5679
|
var TOOL_ID17 = "grok-build";
|
|
5423
5680
|
var TOOL_NAME16 = "Grok Build";
|
|
5424
|
-
var
|
|
5681
|
+
var DEFAULT_DATA_DIR6 = join27(homedir26(), ".grok", "sessions");
|
|
5425
5682
|
var DEFAULT_ARCHIVE_DIR2 = join27(homedir26(), ".grok", "archived_sessions");
|
|
5426
5683
|
function createToolDefinition12(dataDir) {
|
|
5427
5684
|
return {
|
|
@@ -5437,10 +5694,10 @@ function toSafeNumber12(value) {
|
|
|
5437
5694
|
function projectFromEncodedCwd(encoded) {
|
|
5438
5695
|
try {
|
|
5439
5696
|
const decoded = decodeURIComponent(encoded);
|
|
5440
|
-
const leaf =
|
|
5697
|
+
const leaf = basename12(decoded.replace(/[\\/]+$/, ""));
|
|
5441
5698
|
return leaf || "unknown";
|
|
5442
5699
|
} catch {
|
|
5443
|
-
const leaf =
|
|
5700
|
+
const leaf = basename12(encoded);
|
|
5444
5701
|
return leaf || "unknown";
|
|
5445
5702
|
}
|
|
5446
5703
|
}
|
|
@@ -5494,7 +5751,7 @@ function findSessionDirs(dataDir) {
|
|
|
5494
5751
|
if (!existsSync26(dataDir)) return results;
|
|
5495
5752
|
let projectDirs;
|
|
5496
5753
|
try {
|
|
5497
|
-
projectDirs =
|
|
5754
|
+
projectDirs = readdirSync14(dataDir, { withFileTypes: true });
|
|
5498
5755
|
} catch {
|
|
5499
5756
|
return results;
|
|
5500
5757
|
}
|
|
@@ -5505,7 +5762,7 @@ function findSessionDirs(dataDir) {
|
|
|
5505
5762
|
const project = projectFromEncodedCwd(projectEntry.name);
|
|
5506
5763
|
let sessionEntries;
|
|
5507
5764
|
try {
|
|
5508
|
-
sessionEntries =
|
|
5765
|
+
sessionEntries = readdirSync14(projectDir, { withFileTypes: true });
|
|
5509
5766
|
} catch {
|
|
5510
5767
|
continue;
|
|
5511
5768
|
}
|
|
@@ -5534,7 +5791,7 @@ function readFallbackModel(sessionDir) {
|
|
|
5534
5791
|
}
|
|
5535
5792
|
}
|
|
5536
5793
|
var GrokBuildParser = class {
|
|
5537
|
-
constructor(dataDir =
|
|
5794
|
+
constructor(dataDir = DEFAULT_DATA_DIR6, archiveDir = dataDir === DEFAULT_DATA_DIR6 ? DEFAULT_ARCHIVE_DIR2 : void 0) {
|
|
5538
5795
|
this.dataDir = dataDir;
|
|
5539
5796
|
this.archiveDir = archiveDir;
|
|
5540
5797
|
this.tool = createToolDefinition12(dataDir);
|
|
@@ -5635,7 +5892,7 @@ registerParser(new GrokBuildParser());
|
|
|
5635
5892
|
// src/parsers/atomcode.ts
|
|
5636
5893
|
import { existsSync as existsSync27 } from "fs";
|
|
5637
5894
|
import { homedir as homedir27 } from "os";
|
|
5638
|
-
import { basename as
|
|
5895
|
+
import { basename as basename13, join as join28 } from "path";
|
|
5639
5896
|
var TOOL_ID18 = "atomcode";
|
|
5640
5897
|
var TOOL_NAME17 = "AtomCode";
|
|
5641
5898
|
var DEFAULT_SESSIONS_DIR6 = join28(homedir27(), ".atomcode", "sessions");
|
|
@@ -5690,7 +5947,7 @@ function getMetaModel(meta) {
|
|
|
5690
5947
|
}
|
|
5691
5948
|
function getMetaProject(meta) {
|
|
5692
5949
|
if (typeof meta?.working_dir === "string" && meta.working_dir) {
|
|
5693
|
-
return
|
|
5950
|
+
return basename13(meta.working_dir) || "unknown";
|
|
5694
5951
|
}
|
|
5695
5952
|
return "unknown";
|
|
5696
5953
|
}
|
|
@@ -5781,9 +6038,9 @@ var AtomCodeParser = class {
|
|
|
5781
6038
|
registerParser(new AtomCodeParser());
|
|
5782
6039
|
|
|
5783
6040
|
// src/parsers/dsh.ts
|
|
5784
|
-
import { existsSync as existsSync28, readdirSync as
|
|
6041
|
+
import { existsSync as existsSync28, readdirSync as readdirSync15, readFileSync as readFileSync9 } from "fs";
|
|
5785
6042
|
import { homedir as homedir28 } from "os";
|
|
5786
|
-
import { basename as
|
|
6043
|
+
import { basename as basename14, join as join29 } from "path";
|
|
5787
6044
|
import * as zlib from "zlib";
|
|
5788
6045
|
var TOOL_ID19 = "dsh";
|
|
5789
6046
|
var TOOL_NAME18 = "DeepSeek Harness";
|
|
@@ -5883,7 +6140,7 @@ function findSessionLogs(dir) {
|
|
|
5883
6140
|
const results = [];
|
|
5884
6141
|
if (!existsSync28(dir)) return results;
|
|
5885
6142
|
try {
|
|
5886
|
-
for (const entry of
|
|
6143
|
+
for (const entry of readdirSync15(dir, { withFileTypes: true })) {
|
|
5887
6144
|
const fullPath = join29(dir, entry.name);
|
|
5888
6145
|
if (entry.isDirectory()) {
|
|
5889
6146
|
results.push(...findSessionLogs(fullPath));
|
|
@@ -5933,7 +6190,7 @@ var DshParser = class {
|
|
|
5933
6190
|
const relativeParts = filePath.slice(sessionsDir.length + 1).split(/[\\/]/);
|
|
5934
6191
|
const header = rows[0]?.type === "session" ? rows[0] : void 0;
|
|
5935
6192
|
const sessionId = (header?.id ?? decodeSegment(relativeParts[1] ?? "")) || "unknown";
|
|
5936
|
-
const project = typeof header?.cwd === "string" && header.cwd ?
|
|
6193
|
+
const project = typeof header?.cwd === "string" && header.cwd ? basename14(header.cwd) || "unknown" : projectFromDirName(relativeParts[0] ?? "");
|
|
5937
6194
|
let currentModel = "unknown";
|
|
5938
6195
|
for (const row of rows) {
|
|
5939
6196
|
if (row.type === "request/context") {
|
|
@@ -7311,7 +7568,7 @@ function saveUploadManifest(manifest) {
|
|
|
7311
7568
|
|
|
7312
7569
|
// src/services/parse-cache.ts
|
|
7313
7570
|
import { createHash as createHash7 } from "crypto";
|
|
7314
|
-
import { mkdirSync as mkdirSync3, readFileSync as readFileSync14, statSync as
|
|
7571
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync14, statSync as statSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
7315
7572
|
import { hostname as hostname6 } from "os";
|
|
7316
7573
|
import { join as join35 } from "path";
|
|
7317
7574
|
|
|
@@ -7367,7 +7624,7 @@ function computeScanFingerprint(files, now = Date.now()) {
|
|
|
7367
7624
|
for (const file of [...files].sort()) {
|
|
7368
7625
|
let stats;
|
|
7369
7626
|
try {
|
|
7370
|
-
stats =
|
|
7627
|
+
stats = statSync7(file);
|
|
7371
7628
|
} catch {
|
|
7372
7629
|
return null;
|
|
7373
7630
|
}
|
|
@@ -7575,12 +7832,12 @@ function createProjectIdentityResolver(settings) {
|
|
|
7575
7832
|
return identity;
|
|
7576
7833
|
};
|
|
7577
7834
|
}
|
|
7578
|
-
function toUploadBuckets(buckets, settings, device,
|
|
7835
|
+
function toUploadBuckets(buckets, settings, device, resolveProject2 = createProjectIdentityResolver(
|
|
7579
7836
|
settings
|
|
7580
7837
|
)) {
|
|
7581
7838
|
const aggregated = /* @__PURE__ */ new Map();
|
|
7582
7839
|
for (const bucket of buckets) {
|
|
7583
|
-
const project =
|
|
7840
|
+
const project = resolveProject2(bucket.project);
|
|
7584
7841
|
const key = [
|
|
7585
7842
|
bucket.source,
|
|
7586
7843
|
bucket.model,
|
|
@@ -7616,11 +7873,11 @@ function toUploadBuckets(buckets, settings, device, resolveProject = createProje
|
|
|
7616
7873
|
}
|
|
7617
7874
|
return Array.from(aggregated.values());
|
|
7618
7875
|
}
|
|
7619
|
-
function toUploadSessions(sessions, settings, device,
|
|
7876
|
+
function toUploadSessions(sessions, settings, device, resolveProject2 = createProjectIdentityResolver(
|
|
7620
7877
|
settings
|
|
7621
7878
|
)) {
|
|
7622
7879
|
return sessions.map((session) => {
|
|
7623
|
-
const project =
|
|
7880
|
+
const project = resolveProject2(session.project);
|
|
7624
7881
|
return {
|
|
7625
7882
|
source: session.source,
|
|
7626
7883
|
projectKey: project.projectKey,
|
|
@@ -7799,18 +8056,18 @@ async function runSync(config, opts = {}) {
|
|
|
7799
8056
|
deviceId: device.deviceId,
|
|
7800
8057
|
settings
|
|
7801
8058
|
});
|
|
7802
|
-
const
|
|
8059
|
+
const resolveProject2 = createProjectIdentityResolver(settings);
|
|
7803
8060
|
const uploadBuckets = toUploadBuckets(
|
|
7804
8061
|
allBuckets,
|
|
7805
8062
|
settings,
|
|
7806
8063
|
device,
|
|
7807
|
-
|
|
8064
|
+
resolveProject2
|
|
7808
8065
|
);
|
|
7809
8066
|
const uploadSessions = toUploadSessions(
|
|
7810
8067
|
allSessions,
|
|
7811
8068
|
settings,
|
|
7812
8069
|
device,
|
|
7813
|
-
|
|
8070
|
+
resolveProject2
|
|
7814
8071
|
);
|
|
7815
8072
|
const previousManifest = loadUploadManifest();
|
|
7816
8073
|
const uploadDiff = diffUploadManifest({
|