@yishiguji/tokenarena 0.16.2 → 0.16.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
@@ -367,8 +367,25 @@ function selectSnapshot(current, candidate) {
367
367
  }
368
368
  };
369
369
  }
370
+ function getScanDirectories() {
371
+ const directories = [];
372
+ for (const root of getClaudeRoots()) {
373
+ directories.push(join2(root, "projects"));
374
+ }
375
+ for (const root of getClaudeRoots()) {
376
+ for (const directory of ["transcripts", "sessions"]) {
377
+ directories.push(join2(root, directory));
378
+ }
379
+ }
380
+ return directories;
381
+ }
370
382
  var ClaudeCodeParser = class {
371
383
  tool = TOOL;
384
+ listSourceFiles() {
385
+ return getScanDirectories().flatMap(
386
+ (directory) => findJsonlFiles(directory)
387
+ );
388
+ }
372
389
  async parse() {
373
390
  const snapshots = /* @__PURE__ */ new Map();
374
391
  const sessionEvents = /* @__PURE__ */ new Map();
@@ -737,12 +754,16 @@ var CodexParser = class {
737
754
  isInstalled() {
738
755
  return this.dataDirs.some((directory) => existsSync4(directory));
739
756
  }
757
+ /** The same list `parse()` reads, so the two can never drift apart. */
758
+ listSourceFiles() {
759
+ return [...new Set(this.dataDirs.flatMap(findJsonlFiles))].sort();
760
+ }
740
761
  async parse() {
741
762
  const entries = [];
742
763
  const sessionEvents = [];
743
764
  const threads = /* @__PURE__ */ new Map();
744
765
  const threadIndex = /* @__PURE__ */ new Map();
745
- const files = [...new Set(this.dataDirs.flatMap(findJsonlFiles))].sort();
766
+ const files = this.listSourceFiles();
746
767
  for (const path of files) {
747
768
  const file = readRollout(path);
748
769
  if (!file) continue;
@@ -6661,7 +6682,7 @@ async function handleConfig(args) {
6661
6682
  }
6662
6683
 
6663
6684
  // src/services/sync-service.ts
6664
- import { hostname as hostname6 } from "os";
6685
+ import { hostname as hostname7 } from "os";
6665
6686
 
6666
6687
  // src/domain/project-identity.ts
6667
6688
  import { createHmac } from "crypto";
@@ -7293,38 +7314,179 @@ function saveUploadManifest(manifest) {
7293
7314
  );
7294
7315
  }
7295
7316
 
7317
+ // src/services/parse-cache.ts
7318
+ import { createHash as createHash7 } from "crypto";
7319
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync14, statSync as statSync6, writeFileSync as writeFileSync5 } from "fs";
7320
+ import { hostname as hostname6 } from "os";
7321
+ import { join as join35 } from "path";
7322
+
7323
+ // src/infrastructure/runtime/cli-version.ts
7324
+ import { dirname as dirname7, join as join34 } from "path";
7325
+ import { fileURLToPath } from "url";
7326
+ var FALLBACK_VERSION = "0.0.0";
7327
+ var versionCache = /* @__PURE__ */ new Map();
7328
+ function readPackageVersion(startDir) {
7329
+ let dir = startDir;
7330
+ while (true) {
7331
+ const contents = readFileSafe(join34(dir, "package.json"));
7332
+ if (contents !== null) {
7333
+ try {
7334
+ const { version } = JSON.parse(contents);
7335
+ return typeof version === "string" && version.length > 0 ? version : void 0;
7336
+ } catch {
7337
+ return void 0;
7338
+ }
7339
+ }
7340
+ const parent = dirname7(dir);
7341
+ if (parent === dir) {
7342
+ return void 0;
7343
+ }
7344
+ dir = parent;
7345
+ }
7346
+ }
7347
+ function getCliVersion(metaUrl = import.meta.url) {
7348
+ const startDir = dirname7(fileURLToPath(metaUrl));
7349
+ const cached = versionCache.get(startDir);
7350
+ if (cached !== void 0) {
7351
+ return cached;
7352
+ }
7353
+ const version = readPackageVersion(startDir) ?? FALLBACK_VERSION;
7354
+ versionCache.set(startDir, version);
7355
+ return version;
7356
+ }
7357
+
7358
+ // src/services/parse-cache.ts
7359
+ var CACHE_FORMAT_VERSION = 1;
7360
+ var SETTLE_MS = 5e3;
7361
+ function getParseCacheDir() {
7362
+ return join35(getStateDir(), "parse-cache");
7363
+ }
7364
+ function getParseCachePath(toolId) {
7365
+ return join35(getParseCacheDir(), `${toolId}.json`);
7366
+ }
7367
+ function computeScanFingerprint(files, now = Date.now()) {
7368
+ const hash = createHash7("sha256");
7369
+ hash.update(`v${CACHE_FORMAT_VERSION}\0`);
7370
+ hash.update(`${getCliVersion()}\0`);
7371
+ hash.update(`${hostname6()}\0`);
7372
+ for (const file of [...files].sort()) {
7373
+ let stats;
7374
+ try {
7375
+ stats = statSync6(file);
7376
+ } catch {
7377
+ return null;
7378
+ }
7379
+ if (now - stats.mtimeMs < SETTLE_MS) {
7380
+ return null;
7381
+ }
7382
+ hash.update(`${file}\0${stats.mtimeMs}\0${stats.size}\0`);
7383
+ }
7384
+ return hash.digest("hex");
7385
+ }
7386
+ function loadCachedParseResult(toolId, fingerprint2) {
7387
+ if (!fingerprint2) {
7388
+ return null;
7389
+ }
7390
+ try {
7391
+ const envelope = JSON.parse(
7392
+ readFileSync14(getParseCachePath(toolId), "utf-8")
7393
+ );
7394
+ if (envelope.formatVersion !== CACHE_FORMAT_VERSION || envelope.fingerprint !== fingerprint2 || !Array.isArray(envelope.result?.buckets) || !Array.isArray(envelope.result?.sessions)) {
7395
+ return null;
7396
+ }
7397
+ return {
7398
+ buckets: envelope.result.buckets,
7399
+ sessions: envelope.result.sessions
7400
+ };
7401
+ } catch {
7402
+ return null;
7403
+ }
7404
+ }
7405
+ function saveCachedParseResult(toolId, fingerprint2, result) {
7406
+ if (!fingerprint2 || result.incomplete) {
7407
+ return;
7408
+ }
7409
+ const envelope = {
7410
+ formatVersion: CACHE_FORMAT_VERSION,
7411
+ fingerprint: fingerprint2,
7412
+ result: { buckets: result.buckets, sessions: result.sessions }
7413
+ };
7414
+ try {
7415
+ mkdirSync3(getParseCacheDir(), { recursive: true });
7416
+ writeFileSync5(getParseCachePath(toolId), JSON.stringify(envelope), "utf-8");
7417
+ } catch {
7418
+ }
7419
+ }
7420
+
7296
7421
  // src/services/parser-service.ts
7422
+ var PARSER_CONCURRENCY = 8;
7423
+ async function parseWithCache(parser) {
7424
+ const files = parser.listSourceFiles?.();
7425
+ const fingerprint2 = files ? computeScanFingerprint(files) : null;
7426
+ const cached = loadCachedParseResult(parser.tool.id, fingerprint2);
7427
+ if (cached) {
7428
+ return { status: "ok", result: cached, cached: true };
7429
+ }
7430
+ try {
7431
+ const result = await parser.parse();
7432
+ saveCachedParseResult(parser.tool.id, fingerprint2, result);
7433
+ return { status: "ok", result, cached: false };
7434
+ } catch (err) {
7435
+ return {
7436
+ status: "failed",
7437
+ message: err instanceof Error ? err.message : String(err)
7438
+ };
7439
+ }
7440
+ }
7441
+ async function mapWithConcurrency(items, limit, run2) {
7442
+ const results = new Array(items.length);
7443
+ let cursor = 0;
7444
+ async function worker() {
7445
+ while (cursor < items.length) {
7446
+ const index = cursor++;
7447
+ results[index] = await run2(items[index]);
7448
+ }
7449
+ }
7450
+ await Promise.all(
7451
+ Array.from({ length: Math.min(limit, items.length) }, () => worker())
7452
+ );
7453
+ return results;
7454
+ }
7297
7455
  async function runAllParsers() {
7298
7456
  const allBuckets = [];
7299
7457
  const allSessions = [];
7300
7458
  const parserResults = [];
7301
7459
  const failedSources = /* @__PURE__ */ new Set();
7302
- for (const parser of getAllParsers()) {
7303
- try {
7304
- const result = await parser.parse();
7305
- const buckets = result.buckets;
7306
- const sessions = result.sessions;
7307
- if (result.incomplete) {
7308
- failedSources.add(parser.tool.id);
7309
- logger.warn(
7310
- `${parser.tool.id} parser reported an incomplete scan; preserving its remote history.`
7311
- );
7312
- continue;
7313
- }
7314
- if (buckets.length > 0) allBuckets.push(...buckets);
7315
- if (sessions.length > 0) allSessions.push(...sessions);
7316
- if (buckets.length > 0 || sessions.length > 0) {
7317
- parserResults.push({
7318
- source: parser.tool.id,
7319
- buckets: buckets.length,
7320
- sessions: sessions.length
7321
- });
7322
- }
7323
- } catch (err) {
7460
+ const parsers2 = getAllParsers();
7461
+ const settled = await mapWithConcurrency(
7462
+ parsers2,
7463
+ PARSER_CONCURRENCY,
7464
+ parseWithCache
7465
+ );
7466
+ for (const [index, parser] of parsers2.entries()) {
7467
+ const outcome = settled[index];
7468
+ if (outcome.status === "failed") {
7469
+ failedSources.add(parser.tool.id);
7470
+ logger.warn(`${parser.tool.id} parser failed: ${outcome.message}`);
7471
+ continue;
7472
+ }
7473
+ const { buckets, sessions, incomplete } = outcome.result;
7474
+ if (incomplete) {
7324
7475
  failedSources.add(parser.tool.id);
7325
7476
  logger.warn(
7326
- `${parser.tool.id} parser failed: ${err instanceof Error ? err.message : String(err)}`
7477
+ `${parser.tool.id} parser reported an incomplete scan; preserving its remote history.`
7327
7478
  );
7479
+ continue;
7480
+ }
7481
+ if (buckets.length > 0) allBuckets.push(...buckets);
7482
+ if (sessions.length > 0) allSessions.push(...sessions);
7483
+ if (buckets.length > 0 || sessions.length > 0) {
7484
+ parserResults.push({
7485
+ source: parser.tool.id,
7486
+ buckets: buckets.length,
7487
+ sessions: sessions.length,
7488
+ ...outcome.cached ? { cached: true } : {}
7489
+ });
7328
7490
  }
7329
7491
  }
7330
7492
  return {
@@ -7399,7 +7561,7 @@ function persistUploadManifest(manifest, quiet) {
7399
7561
  function toDeviceMetadata(config) {
7400
7562
  return {
7401
7563
  deviceId: getOrCreateDeviceId(config),
7402
- hostname: hostname6().replace(/\.local$/, "")
7564
+ hostname: hostname7().replace(/\.local$/, "")
7403
7565
  };
7404
7566
  }
7405
7567
  function toUploadBuckets(buckets, settings, device) {
@@ -7598,7 +7760,8 @@ async function runSync(config, opts = {}) {
7598
7760
  const parts = [];
7599
7761
  if (p.buckets > 0) parts.push(`${p.buckets} buckets`);
7600
7762
  if (p.sessions > 0) parts.push(`${p.sessions} sessions`);
7601
- logger.info(` ${p.source}: ${parts.join(", ")}`);
7763
+ const suffix = p.cached ? " (unchanged)" : "";
7764
+ logger.info(` ${p.source}: ${parts.join(", ")}${suffix}`);
7602
7765
  }
7603
7766
  }
7604
7767
  const apiUrl = config.apiUrl || "https://token.guji.uno";
@@ -7850,14 +8013,14 @@ import { execFileSync as execFileSync7, spawn } from "child_process";
7850
8013
  import { existsSync as existsSync36 } from "fs";
7851
8014
  import { appendFile, mkdir, readFile } from "fs/promises";
7852
8015
  import { homedir as homedir33, platform as platform5 } from "os";
7853
- import { dirname as dirname7, join as join34, posix as posix3, win32 } from "path";
8016
+ import { dirname as dirname8, join as join36, posix as posix3, win32 } from "path";
7854
8017
 
7855
8018
  // src/infrastructure/service/index.ts
7856
8019
  import { platform as platform4 } from "os";
7857
8020
 
7858
8021
  // src/infrastructure/service/linux-systemd.ts
7859
8022
  import { execFileSync as execFileSync5 } from "child_process";
7860
- import { existsSync as existsSync34, mkdirSync as mkdirSync3, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
8023
+ import { existsSync as existsSync34, mkdirSync as mkdirSync4, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
7861
8024
  import { homedir as homedir31, platform as platform2 } from "os";
7862
8025
  import { posix } from "path";
7863
8026
 
@@ -8019,8 +8182,8 @@ function createLinuxSystemdServiceBackend() {
8019
8182
  execPath: command.execPath,
8020
8183
  args: command.args
8021
8184
  });
8022
- mkdirSync3(serviceDir, { recursive: true });
8023
- writeFileSync5(serviceFile, content, "utf-8");
8185
+ mkdirSync4(serviceDir, { recursive: true });
8186
+ writeFileSync6(serviceFile, content, "utf-8");
8024
8187
  execSystemctl(["daemon-reload"]);
8025
8188
  execSystemctl(["enable", SYSTEMD_SERVICE_NAME]);
8026
8189
  execSystemctl(["start", SYSTEMD_SERVICE_NAME]);
@@ -8179,7 +8342,7 @@ function createLinuxSystemdServiceBackend() {
8179
8342
 
8180
8343
  // src/infrastructure/service/macos-launchd.ts
8181
8344
  import { execFileSync as execFileSync6 } from "child_process";
8182
- import { existsSync as existsSync35, mkdirSync as mkdirSync4, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "fs";
8345
+ import { existsSync as existsSync35, mkdirSync as mkdirSync5, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "fs";
8183
8346
  import { homedir as homedir32, platform as platform3 } from "os";
8184
8347
  import { posix as posix2 } from "path";
8185
8348
  var MACOS_LAUNCHD_LABEL = "com.guji.tokenarena";
@@ -8321,8 +8484,8 @@ function writeLaunchAgentPlist() {
8321
8484
  standardErrorPath: stderrPath
8322
8485
  });
8323
8486
  ensureAppDirs();
8324
- mkdirSync4(getMacosLaunchAgentDir(), { recursive: true });
8325
- writeFileSync6(plistFile, plist, "utf-8");
8487
+ mkdirSync5(getMacosLaunchAgentDir(), { recursive: true });
8488
+ writeFileSync7(plistFile, plist, "utf-8");
8326
8489
  return plistFile;
8327
8490
  }
8328
8491
  function bootstrapLaunchAgent() {
@@ -8593,7 +8756,7 @@ function resolvePowerShellProfilePath() {
8593
8756
  const systemRoot = process.env.SYSTEMROOT || "C:\\Windows";
8594
8757
  const candidates = [
8595
8758
  "pwsh.exe",
8596
- join34(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
8759
+ join36(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
8597
8760
  ];
8598
8761
  for (const command of candidates) {
8599
8762
  try {
@@ -8818,7 +8981,7 @@ async function setupShellAlias() {
8818
8981
  return;
8819
8982
  }
8820
8983
  try {
8821
- await mkdir(dirname7(setup.configFile), { recursive: true });
8984
+ await mkdir(dirname8(setup.configFile), { recursive: true });
8822
8985
  let existingContent = "";
8823
8986
  if (existsSync36(setup.configFile)) {
8824
8987
  existingContent = await readFile(setup.configFile, "utf-8");
@@ -9083,41 +9246,6 @@ function buildLocalUsageDashboardData(input2) {
9083
9246
  };
9084
9247
  }
9085
9248
 
9086
- // src/infrastructure/runtime/cli-version.ts
9087
- import { dirname as dirname8, join as join35 } from "path";
9088
- import { fileURLToPath } from "url";
9089
- var FALLBACK_VERSION = "0.0.0";
9090
- var versionCache = /* @__PURE__ */ new Map();
9091
- function readPackageVersion(startDir) {
9092
- let dir = startDir;
9093
- while (true) {
9094
- const contents = readFileSafe(join35(dir, "package.json"));
9095
- if (contents !== null) {
9096
- try {
9097
- const { version } = JSON.parse(contents);
9098
- return typeof version === "string" && version.length > 0 ? version : void 0;
9099
- } catch {
9100
- return void 0;
9101
- }
9102
- }
9103
- const parent = dirname8(dir);
9104
- if (parent === dir) {
9105
- return void 0;
9106
- }
9107
- dir = parent;
9108
- }
9109
- }
9110
- function getCliVersion(metaUrl = import.meta.url) {
9111
- const startDir = dirname8(fileURLToPath(metaUrl));
9112
- const cached = versionCache.get(startDir);
9113
- if (cached !== void 0) {
9114
- return cached;
9115
- }
9116
- const version = readPackageVersion(startDir) ?? FALLBACK_VERSION;
9117
- versionCache.set(startDir, version);
9118
- return version;
9119
- }
9120
-
9121
9249
  // src/infrastructure/ui/local-usage-dashboard.ts
9122
9250
  var MAX_RANK_ROWS = 6;
9123
9251
  var MAX_SESSION_ROWS = 6;
@@ -9516,7 +9644,7 @@ async function runSyncCommand(opts = {}) {
9516
9644
  }
9517
9645
 
9518
9646
  // src/commands/uninstall.ts
9519
- import { existsSync as existsSync37, readFileSync as readFileSync14, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "fs";
9647
+ import { existsSync as existsSync37, readFileSync as readFileSync15, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
9520
9648
  import { homedir as homedir34, platform as platform6 } from "os";
9521
9649
  function removeShellAlias() {
9522
9650
  const shell = process.env.SHELL;
@@ -9543,7 +9671,7 @@ function removeShellAlias() {
9543
9671
  }
9544
9672
  if (!existsSync37(configFile)) return;
9545
9673
  try {
9546
- let content = readFileSync14(configFile, "utf-8");
9674
+ let content = readFileSync15(configFile, "utf-8");
9547
9675
  const aliasPatterns = [
9548
9676
  // zsh / bash format: alias ta="tokenarena"
9549
9677
  new RegExp(
@@ -9565,7 +9693,7 @@ function removeShellAlias() {
9565
9693
  content = next;
9566
9694
  }
9567
9695
  }
9568
- writeFileSync7(configFile, content, "utf-8");
9696
+ writeFileSync8(configFile, content, "utf-8");
9569
9697
  logger.info(`Removed shell alias from ${configFile}`);
9570
9698
  } catch (err) {
9571
9699
  logger.warn(