@yishiguji/tokenarena 0.18.1 → 0.18.3

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 CHANGED
@@ -6297,12 +6297,13 @@ registerParser(new AtomCodeParser());
6297
6297
  // src/parsers/dsh.ts
6298
6298
  import { existsSync as existsSync28, readdirSync as readdirSync15, readFileSync as readFileSync9 } from "fs";
6299
6299
  import { homedir as homedir29 } from "os";
6300
- import { basename as basename14, join as join30 } from "path";
6300
+ import { basename as basename14, dirname as dirname7, join as join30 } from "path";
6301
6301
  import * as zlib from "zlib";
6302
6302
  var TOOL_ID20 = "dsh";
6303
6303
  var TOOL_NAME18 = "DeepSeek Harness";
6304
6304
  var DEFAULT_SESSIONS_DIR7 = join30(homedir29(), ".dsh", "sessions");
6305
- var LOG_BASENAME = "session";
6305
+ var SESSION_LOG_PATTERN = /^session(?:\.v(\d+))?\.jsonl(?:\.zstd)?$/;
6306
+ var LEGACY_GENERATION = 0;
6306
6307
  function getDshSessionsDirs(env = process.env) {
6307
6308
  const dirs = [
6308
6309
  env.TOKEN_ARENA_DSH_DIR,
@@ -6311,10 +6312,18 @@ function getDshSessionsDirs(env = process.env) {
6311
6312
  ].filter((value) => Boolean(value));
6312
6313
  return Array.from(new Set(dirs));
6313
6314
  }
6315
+ function getSessionLogGeneration(fileName) {
6316
+ const match = SESSION_LOG_PATTERN.exec(fileName);
6317
+ if (!match) return null;
6318
+ return match[1] === void 0 ? LEGACY_GENERATION : Number(match[1]);
6319
+ }
6314
6320
  function toNonNegativeNumber4(value) {
6315
6321
  const numberValue = Number(value);
6316
6322
  return Number.isFinite(numberValue) && numberValue >= 0 ? numberValue : 0;
6317
6323
  }
6324
+ function toModelName(value) {
6325
+ return typeof value === "string" && value ? value : null;
6326
+ }
6318
6327
  function scanZstdFrames(buffer) {
6319
6328
  const ZSTD_MAGIC = 4247762216;
6320
6329
  const frames = [];
@@ -6370,45 +6379,85 @@ function scanZstdFrames(buffer) {
6370
6379
  return frames;
6371
6380
  }
6372
6381
  function decompressZstdLog(buffer) {
6373
- if (typeof zlib.zstdDecompressSync !== "function") return null;
6382
+ if (typeof zlib.zstdDecompressSync !== "function") {
6383
+ return { text: null, damaged: false };
6384
+ }
6385
+ const frames = scanZstdFrames(buffer);
6386
+ if (frames.length === 0) return { text: null, damaged: false };
6374
6387
  const chunks = [];
6375
- for (const [start, end] of scanZstdFrames(buffer)) {
6388
+ let damaged = false;
6389
+ for (const [start, end] of frames) {
6376
6390
  try {
6377
6391
  chunks.push(zlib.zstdDecompressSync(buffer.subarray(start, end)));
6378
6392
  } catch {
6393
+ damaged = true;
6379
6394
  }
6380
6395
  }
6381
- if (chunks.length === 0) return null;
6382
- return Buffer.concat(chunks).toString("utf-8");
6396
+ if (chunks.length === 0) return { text: null, damaged };
6397
+ return { text: Buffer.concat(chunks).toString("utf-8"), damaged };
6383
6398
  }
6384
- function readSessionLog(filePath) {
6399
+ function readSessionLog(log2) {
6385
6400
  let buffer;
6386
6401
  try {
6387
- buffer = readFileSync9(filePath);
6402
+ buffer = readFileSync9(log2.filePath);
6388
6403
  } catch {
6389
- return null;
6404
+ return { text: null, damaged: false };
6390
6405
  }
6391
- if (filePath.endsWith(".zstd")) {
6406
+ if (log2.compressed) {
6392
6407
  return decompressZstdLog(buffer);
6393
6408
  }
6394
- return buffer.toString("utf-8");
6409
+ return { text: buffer.toString("utf-8"), damaged: false };
6395
6410
  }
6396
- function findSessionLogs(dir) {
6411
+ function findSessionLogs(dir, rootDir = dir) {
6397
6412
  const results = [];
6398
6413
  if (!existsSync28(dir)) return results;
6399
6414
  try {
6400
6415
  for (const entry of readdirSync15(dir, { withFileTypes: true })) {
6401
6416
  const fullPath = join30(dir, entry.name);
6402
6417
  if (entry.isDirectory()) {
6403
- results.push(...findSessionLogs(fullPath));
6404
- } else if (entry.name === `${LOG_BASENAME}.jsonl` || entry.name === `${LOG_BASENAME}.jsonl.zstd`) {
6405
- results.push(fullPath);
6418
+ results.push(...findSessionLogs(fullPath, rootDir));
6419
+ continue;
6406
6420
  }
6421
+ const generation = getSessionLogGeneration(entry.name);
6422
+ if (generation === null) continue;
6423
+ results.push({
6424
+ filePath: fullPath,
6425
+ generation,
6426
+ compressed: entry.name.endsWith(".zstd"),
6427
+ rootDir
6428
+ });
6407
6429
  }
6408
6430
  } catch {
6409
6431
  }
6410
6432
  return results;
6411
6433
  }
6434
+ function isNewerLog(candidate, current) {
6435
+ if (candidate.generation !== current.generation) {
6436
+ return candidate.generation > current.generation;
6437
+ }
6438
+ if (candidate.compressed !== current.compressed) return candidate.compressed;
6439
+ return candidate.filePath < current.filePath;
6440
+ }
6441
+ function selectCurrentSessionLogs(logs) {
6442
+ const byDirectory = /* @__PURE__ */ new Map();
6443
+ for (const log2 of logs) {
6444
+ const directory = dirname7(log2.filePath);
6445
+ const current = byDirectory.get(directory);
6446
+ if (!current || isNewerLog(log2, current)) {
6447
+ byDirectory.set(directory, log2);
6448
+ }
6449
+ }
6450
+ return Array.from(byDirectory.values()).sort(
6451
+ (left, right) => left.filePath < right.filePath ? -1 : left.filePath > right.filePath ? 1 : 0
6452
+ );
6453
+ }
6454
+ function currentSessionLogs(roots) {
6455
+ const logs = [];
6456
+ for (const dir of roots) {
6457
+ logs.push(...findSessionLogs(dir));
6458
+ }
6459
+ return selectCurrentSessionLogs(logs);
6460
+ }
6412
6461
  function decodeSegment(segment) {
6413
6462
  return segment.replace(
6414
6463
  /~([0-9A-F]{4})/g,
@@ -6438,101 +6487,121 @@ var DshParser = class {
6438
6487
  const entries = [];
6439
6488
  const sessionEvents = [];
6440
6489
  const seenEntryKeys = /* @__PURE__ */ new Set();
6441
- for (const sessionsDir of this.sessionsDirs) {
6442
- for (const filePath of findSessionLogs(sessionsDir)) {
6443
- const content = readSessionLog(filePath);
6444
- if (!content) continue;
6445
- const rows = parseJsonl(content);
6446
- if (rows.length === 0) continue;
6447
- const relativeParts = filePath.slice(sessionsDir.length + 1).split(/[\\/]/);
6448
- const header = rows[0]?.type === "session" ? rows[0] : void 0;
6449
- const sessionId = (header?.id ?? decodeSegment(relativeParts[1] ?? "")) || "unknown";
6450
- const project = typeof header?.cwd === "string" && header.cwd ? basename14(header.cwd) || "unknown" : projectFromDirName(relativeParts[0] ?? "");
6451
- let currentModel = "unknown";
6452
- for (const row of rows) {
6453
- if (row.type === "request/context") {
6454
- const model2 = row.data?.model;
6455
- if (typeof model2 === "string" && model2) currentModel = model2;
6456
- continue;
6457
- }
6458
- if (row.type === "request/header") {
6459
- const model2 = row.data?.header?.config?.model;
6460
- if (typeof model2 === "string" && model2) currentModel = model2;
6461
- continue;
6462
- }
6463
- const timestamp = typeof row.time === "number" && Number.isFinite(row.time) ? new Date(row.time) : null;
6464
- if (row.type === "user/message") {
6465
- const sourceKind = row.data?.source?.kind ?? row.data?.message?.source?.kind;
6466
- if (sourceKind !== "user") continue;
6467
- if (timestamp) {
6468
- sessionEvents.push({
6469
- sessionId,
6470
- source: TOOL_ID20,
6471
- project,
6472
- timestamp,
6473
- role: "user"
6474
- });
6475
- }
6476
- continue;
6477
- }
6478
- const isCompaction = row.type === "compaction/summary";
6479
- if (row.type !== "assistant/message" && !isCompaction) continue;
6480
- if (timestamp && !isCompaction) {
6490
+ let incomplete = false;
6491
+ const logs = currentSessionLogs(this.sessionsDirs);
6492
+ for (const log2 of logs) {
6493
+ const { text: content, damaged } = readSessionLog(log2);
6494
+ if (content === null) {
6495
+ if (existsSync28(log2.filePath)) incomplete = true;
6496
+ continue;
6497
+ }
6498
+ if (damaged) {
6499
+ logger.warn(
6500
+ `dsh session log ${log2.filePath} has frames that could not be decompressed.`
6501
+ );
6502
+ incomplete = true;
6503
+ continue;
6504
+ }
6505
+ const rows = parseJsonl(content);
6506
+ if (rows.length === 0) continue;
6507
+ const relativeParts = log2.filePath.slice(log2.rootDir.length + 1).split(/[\\/]/);
6508
+ const header = rows[0]?.type === "session" ? rows[0] : void 0;
6509
+ const sessionId = (header?.id ?? decodeSegment(relativeParts[1] ?? "")) || "unknown";
6510
+ const project = typeof header?.cwd === "string" && header.cwd ? basename14(header.cwd) || "unknown" : projectFromDirName(relativeParts[0] ?? "");
6511
+ let currentModel = "unknown";
6512
+ for (const row of rows) {
6513
+ if (row.type === "request/context") {
6514
+ const model2 = row.data?.model;
6515
+ if (typeof model2 === "string" && model2) currentModel = model2;
6516
+ continue;
6517
+ }
6518
+ if (row.type === "request/header") {
6519
+ const model2 = row.data?.header?.config?.model;
6520
+ if (typeof model2 === "string" && model2) currentModel = model2;
6521
+ continue;
6522
+ }
6523
+ const timestamp = typeof row.time === "number" && Number.isFinite(row.time) ? new Date(row.time) : null;
6524
+ if (row.type === "user/message") {
6525
+ const sourceKind = row.data?.source?.kind ?? row.data?.message?.source?.kind;
6526
+ if (sourceKind !== "user") continue;
6527
+ if (timestamp) {
6481
6528
  sessionEvents.push({
6482
6529
  sessionId,
6483
6530
  source: TOOL_ID20,
6484
6531
  project,
6485
6532
  timestamp,
6486
- role: "assistant"
6533
+ role: "user"
6487
6534
  });
6488
6535
  }
6489
- const usage = row.data?.usage;
6490
- if (!usage || timestamp === null) continue;
6491
- const model = (isCompaction && typeof row.data?.model === "string" ? row.data.model : "") || currentModel;
6492
- const inputTokens = toNonNegativeNumber4(usage.inputTokens);
6493
- const cachedTokens = toNonNegativeNumber4(usage.cacheReadTokens);
6494
- const cacheCreationTokens = toNonNegativeNumber4(
6495
- usage.cacheWriteTokens
6496
- );
6497
- const reasoningTokens = toNonNegativeNumber4(usage.reasoningTokens);
6498
- const outputTokens = Math.max(
6499
- 0,
6500
- toNonNegativeNumber4(usage.outputTokens) - reasoningTokens
6501
- );
6502
- if (inputTokens + outputTokens + cachedTokens + cacheCreationTokens + reasoningTokens === 0)
6503
- continue;
6504
- const entryKey = [
6505
- sessionId,
6506
- timestamp.toISOString(),
6507
- model,
6508
- inputTokens,
6509
- outputTokens,
6510
- cachedTokens,
6511
- cacheCreationTokens,
6512
- reasoningTokens
6513
- ].join("|");
6514
- if (seenEntryKeys.has(entryKey)) continue;
6515
- seenEntryKeys.add(entryKey);
6516
- entries.push({
6536
+ continue;
6537
+ }
6538
+ const isCompaction = row.type === "compaction/summary";
6539
+ if (row.type !== "assistant/message" && !isCompaction) continue;
6540
+ if (timestamp && !isCompaction) {
6541
+ sessionEvents.push({
6517
6542
  sessionId,
6518
6543
  source: TOOL_ID20,
6519
- model,
6520
6544
  project,
6521
6545
  timestamp,
6522
- inputTokens,
6523
- outputTokens,
6524
- reasoningTokens,
6525
- cachedTokens,
6526
- cacheCreationTokens
6546
+ role: "assistant"
6527
6547
  });
6528
6548
  }
6549
+ const usage = row.data?.usage;
6550
+ if (!usage || timestamp === null) continue;
6551
+ const declaredModel = toModelName(row.data?.model) ?? toModelName(row.data?.message?.source?.model);
6552
+ if (declaredModel) currentModel = declaredModel;
6553
+ const model = declaredModel ?? currentModel;
6554
+ const inputTokens = toNonNegativeNumber4(usage.inputTokens);
6555
+ const cachedTokens = toNonNegativeNumber4(usage.cacheReadTokens);
6556
+ const cacheCreationTokens = toNonNegativeNumber4(usage.cacheWriteTokens);
6557
+ const reasoningTokens = toNonNegativeNumber4(usage.reasoningTokens);
6558
+ const outputTokens = Math.max(
6559
+ 0,
6560
+ toNonNegativeNumber4(usage.outputTokens) - reasoningTokens
6561
+ );
6562
+ if (inputTokens + outputTokens + cachedTokens + cacheCreationTokens + reasoningTokens === 0)
6563
+ continue;
6564
+ const entryKey = [
6565
+ sessionId,
6566
+ timestamp.toISOString(),
6567
+ model,
6568
+ inputTokens,
6569
+ outputTokens,
6570
+ cachedTokens,
6571
+ cacheCreationTokens,
6572
+ reasoningTokens
6573
+ ].join("|");
6574
+ if (seenEntryKeys.has(entryKey)) continue;
6575
+ seenEntryKeys.add(entryKey);
6576
+ entries.push({
6577
+ sessionId,
6578
+ source: TOOL_ID20,
6579
+ model,
6580
+ project,
6581
+ timestamp,
6582
+ inputTokens,
6583
+ outputTokens,
6584
+ reasoningTokens,
6585
+ cachedTokens,
6586
+ cacheCreationTokens
6587
+ });
6529
6588
  }
6530
6589
  }
6531
6590
  return {
6532
6591
  buckets: aggregateToBuckets(entries),
6533
- sessions: extractSessions(sessionEvents, entries)
6592
+ sessions: extractSessions(sessionEvents, entries),
6593
+ ...incomplete ? { incomplete: true } : {}
6534
6594
  };
6535
6595
  }
6596
+ /**
6597
+ * Exhaustive: every file `parse()` reads, generation selection included.
6598
+ *
6599
+ * A log that is discovered but no longer selected must not appear here either,
6600
+ * or an upgraded session would look changed while contributing nothing.
6601
+ */
6602
+ listSourceFiles() {
6603
+ return currentSessionLogs(this.sessionsDirs).map((log2) => log2.filePath);
6604
+ }
6536
6605
  isInstalled() {
6537
6606
  return this.sessionsDirs.some((dir) => existsSync28(dir));
6538
6607
  }
@@ -6542,7 +6611,7 @@ registerParser(new DshParser());
6542
6611
  // src/parsers/cherry-studio.ts
6543
6612
  import { existsSync as existsSync29 } from "fs";
6544
6613
  import { homedir as homedir30 } from "os";
6545
- import { dirname as dirname7, join as join31, resolve as resolve3 } from "path";
6614
+ import { dirname as dirname8, join as join31, resolve as resolve3 } from "path";
6546
6615
  var TOOL_ID21 = "cherry-studio";
6547
6616
  var TOOL_NAME19 = "Cherry Studio";
6548
6617
  var DB_RELATIVE = join31("Data", "cherrystudio.sqlite");
@@ -6597,7 +6666,7 @@ function createToolDefinition13(dbPath) {
6597
6666
  return {
6598
6667
  id: TOOL_ID21,
6599
6668
  name: TOOL_NAME19,
6600
- dataDir: dbPath ? dirname7(dbPath) : join31(getDefaultUserDataDir(), "Data")
6669
+ dataDir: dbPath ? dirname8(dbPath) : join31(getDefaultUserDataDir(), "Data")
6601
6670
  };
6602
6671
  }
6603
6672
  function toSafeCount(value) {
@@ -7830,7 +7899,7 @@ import { hostname as hostname6 } from "os";
7830
7899
  import { join as join36 } from "path";
7831
7900
 
7832
7901
  // src/infrastructure/runtime/cli-version.ts
7833
- import { dirname as dirname8, join as join35 } from "path";
7902
+ import { dirname as dirname9, join as join35 } from "path";
7834
7903
  import { fileURLToPath } from "url";
7835
7904
  var FALLBACK_VERSION = "0.0.0";
7836
7905
  var versionCache = /* @__PURE__ */ new Map();
@@ -7846,7 +7915,7 @@ function readPackageVersion(startDir) {
7846
7915
  return void 0;
7847
7916
  }
7848
7917
  }
7849
- const parent = dirname8(dir);
7918
+ const parent = dirname9(dir);
7850
7919
  if (parent === dir) {
7851
7920
  return void 0;
7852
7921
  }
@@ -7854,7 +7923,7 @@ function readPackageVersion(startDir) {
7854
7923
  }
7855
7924
  }
7856
7925
  function getCliVersion(metaUrl = import.meta.url) {
7857
- const startDir = dirname8(fileURLToPath(metaUrl));
7926
+ const startDir = dirname9(fileURLToPath(metaUrl));
7858
7927
  const cached = versionCache.get(startDir);
7859
7928
  if (cached !== void 0) {
7860
7929
  return cached;
@@ -8547,7 +8616,7 @@ import { execFileSync as execFileSync8, spawn } from "child_process";
8547
8616
  import { existsSync as existsSync36 } from "fs";
8548
8617
  import { appendFile, mkdir, readFile } from "fs/promises";
8549
8618
  import { homedir as homedir34, platform as platform5 } from "os";
8550
- import { dirname as dirname9, join as join37, posix as posix3, win32 } from "path";
8619
+ import { dirname as dirname10, join as join37, posix as posix3, win32 } from "path";
8551
8620
 
8552
8621
  // src/infrastructure/service/index.ts
8553
8622
  import { platform as platform4 } from "os";
@@ -9521,7 +9590,7 @@ async function setupShellAlias() {
9521
9590
  return;
9522
9591
  }
9523
9592
  try {
9524
- await mkdir(dirname9(setup.configFile), { recursive: true });
9593
+ await mkdir(dirname10(setup.configFile), { recursive: true });
9525
9594
  let existingContent = "";
9526
9595
  if (existsSync36(setup.configFile)) {
9527
9596
  existingContent = await readFile(setup.configFile, "utf-8");