@yishiguji/tokenarena 0.16.2 → 0.16.4

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();
@@ -628,13 +645,15 @@ function readRollout(path) {
628
645
  let latestTimestampOrder = 0n;
629
646
  let minLineTimestampOrder = null;
630
647
  let line = 0;
631
- let lineCount = 0;
648
+ let maxOrdinal = null;
632
649
  for (const text of content.split("\n")) {
633
650
  line++;
634
651
  if (!text.trim()) continue;
635
- lineCount++;
636
652
  try {
637
653
  const event = JSON.parse(text);
654
+ if (typeof event.ordinal === "number" && Number.isSafeInteger(event.ordinal) && event.ordinal >= 0 && (maxOrdinal === null || event.ordinal > maxOrdinal)) {
655
+ maxOrdinal = event.ordinal;
656
+ }
638
657
  const timestamp = readTimestamp(event.timestamp);
639
658
  const order = timestampOrder(event.timestamp, timestamp);
640
659
  if (order !== null) {
@@ -685,7 +704,7 @@ function readRollout(path) {
685
704
  forkedFrom && spawnedFrom && forkedFrom !== spawnedFrom
686
705
  ),
687
706
  forkOrdinalExclusive: typeof meta?.forked_from_ordinal_exclusive === "number" && Number.isSafeInteger(meta.forked_from_ordinal_exclusive) && meta.forked_from_ordinal_exclusive > 0 ? meta.forked_from_ordinal_exclusive : null,
688
- lineCount,
707
+ maxOrdinal,
689
708
  rootTimestampOrder: timestampOrder(
690
709
  rootMeta?.timestamp,
691
710
  readTimestamp(rootMeta?.timestamp)
@@ -737,12 +756,16 @@ var CodexParser = class {
737
756
  isInstalled() {
738
757
  return this.dataDirs.some((directory) => existsSync4(directory));
739
758
  }
759
+ /** The same list `parse()` reads, so the two can never drift apart. */
760
+ listSourceFiles() {
761
+ return [...new Set(this.dataDirs.flatMap(findJsonlFiles))].sort();
762
+ }
740
763
  async parse() {
741
764
  const entries = [];
742
765
  const sessionEvents = [];
743
766
  const threads = /* @__PURE__ */ new Map();
744
767
  const threadIndex = /* @__PURE__ */ new Map();
745
- const files = [...new Set(this.dataDirs.flatMap(findJsonlFiles))].sort();
768
+ const files = this.listSourceFiles();
746
769
  for (const path of files) {
747
770
  const file = readRollout(path);
748
771
  if (!file) continue;
@@ -788,13 +811,11 @@ var CodexParser = class {
788
811
  if (file.parentConflict || file.parentId) {
789
812
  const parent = file.parentId ? threadIndex.get(file.parentId) : void 0;
790
813
  const cutoff = file.rootTimestampOrder;
791
- const coverageOk = parent ? file.forkOrdinalExclusive !== null ? (
792
- // 多文件父线程按最新文件(通常就是产生该 fork 的 rollout)
793
- // 的行数核验;其余文件属于同一线程的历史分段。
794
- parent.files.some(
795
- (rollout) => rollout.lineCount >= (file.forkOrdinalExclusive ?? 0)
796
- )
797
- ) : cutoff !== null && // 父最大时间戳落后不超过 1 秒,或父内容完全早于截止点
814
+ const parentMaxOrdinal = parent ? parent.files.reduce(
815
+ (max, rollout) => rollout.maxOrdinal !== null && (max === null || rollout.maxOrdinal > max) ? rollout.maxOrdinal : max,
816
+ null
817
+ ) : null;
818
+ const coverageOk = parent ? file.forkOrdinalExclusive !== null && parentMaxOrdinal !== null ? parentMaxOrdinal >= file.forkOrdinalExclusive - 1 : cutoff !== null && // 父最大时间戳落后不超过 1 秒,或父内容完全早于截止点
798
819
  // 且父最早行时间戳也早于截止点(父在截止前已停止写入)。
799
820
  (parent.latestTimestampOrder >= cutoff - 1000000n || parent.minLineTimestampOrder !== null && parent.minLineTimestampOrder < cutoff) : false;
800
821
  if (file.parentConflict || !parent || parent === thread || cutoff === null || !processThread(parent) || parent.invalidTokenTimestamp || !coverageOk) {
@@ -1261,7 +1282,15 @@ import { dirname, join as join6 } from "path";
1261
1282
  import { execFileSync } from "child_process";
1262
1283
  import { statSync } from "fs";
1263
1284
  import { pathToFileURL } from "url";
1264
- function withSuppressedSqliteWarning(fn) {
1285
+ var warningScope = Promise.resolve();
1286
+ async function withSuppressedSqliteWarning(fn) {
1287
+ const previousScope = warningScope;
1288
+ let releaseScope = () => {
1289
+ };
1290
+ warningScope = new Promise((resolve5) => {
1291
+ releaseScope = resolve5;
1292
+ });
1293
+ await previousScope;
1265
1294
  const originalEmitWarning = process.emitWarning;
1266
1295
  process.emitWarning = ((warning, ...args) => {
1267
1296
  const warningName = typeof warning === "string" ? typeof args[0] === "string" ? args[0] : "" : warning.name;
@@ -1271,9 +1300,12 @@ function withSuppressedSqliteWarning(fn) {
1271
1300
  }
1272
1301
  originalEmitWarning.call(process, warning, ...args);
1273
1302
  });
1274
- return fn().finally(() => {
1303
+ try {
1304
+ return await fn();
1305
+ } finally {
1275
1306
  process.emitWarning = originalEmitWarning;
1276
- });
1307
+ releaseScope();
1308
+ }
1277
1309
  }
1278
1310
  function warnWhenWalSkipped(dbPath) {
1279
1311
  try {
@@ -2354,25 +2386,9 @@ function getOpenCodeDataDirs(env = process.env) {
2354
2386
  ].filter((value) => Boolean(value));
2355
2387
  return Array.from(new Set(dirs));
2356
2388
  }
2357
- async function withSuppressedSqliteWarning2(fn) {
2358
- const originalEmitWarning = process.emitWarning;
2359
- process.emitWarning = ((warning, ...args) => {
2360
- const warningName = typeof warning === "string" ? typeof args[0] === "string" ? args[0] : "" : warning.name;
2361
- const warningMessage = typeof warning === "string" ? warning : warning.message;
2362
- if (warningName === "ExperimentalWarning" && warningMessage.includes("SQLite")) {
2363
- return;
2364
- }
2365
- originalEmitWarning.call(process, warning, ...args);
2366
- });
2367
- try {
2368
- return await fn();
2369
- } finally {
2370
- process.emitWarning = originalEmitWarning;
2371
- }
2372
- }
2373
2389
  async function readSqliteRowsWithBuiltin2(dbPath, query) {
2374
2390
  try {
2375
- return await withSuppressedSqliteWarning2(async () => {
2391
+ return await withSuppressedSqliteWarning(async () => {
2376
2392
  const sqliteModuleId = "node:sqlite";
2377
2393
  const sqlite = await import(sqliteModuleId);
2378
2394
  const db = new sqlite.DatabaseSync(dbPath);
@@ -6661,7 +6677,7 @@ async function handleConfig(args) {
6661
6677
  }
6662
6678
 
6663
6679
  // src/services/sync-service.ts
6664
- import { hostname as hostname6 } from "os";
6680
+ import { hostname as hostname7 } from "os";
6665
6681
 
6666
6682
  // src/domain/project-identity.ts
6667
6683
  import { createHmac } from "crypto";
@@ -7293,38 +7309,179 @@ function saveUploadManifest(manifest) {
7293
7309
  );
7294
7310
  }
7295
7311
 
7312
+ // src/services/parse-cache.ts
7313
+ import { createHash as createHash7 } from "crypto";
7314
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync14, statSync as statSync6, writeFileSync as writeFileSync5 } from "fs";
7315
+ import { hostname as hostname6 } from "os";
7316
+ import { join as join35 } from "path";
7317
+
7318
+ // src/infrastructure/runtime/cli-version.ts
7319
+ import { dirname as dirname7, join as join34 } from "path";
7320
+ import { fileURLToPath } from "url";
7321
+ var FALLBACK_VERSION = "0.0.0";
7322
+ var versionCache = /* @__PURE__ */ new Map();
7323
+ function readPackageVersion(startDir) {
7324
+ let dir = startDir;
7325
+ while (true) {
7326
+ const contents = readFileSafe(join34(dir, "package.json"));
7327
+ if (contents !== null) {
7328
+ try {
7329
+ const { version } = JSON.parse(contents);
7330
+ return typeof version === "string" && version.length > 0 ? version : void 0;
7331
+ } catch {
7332
+ return void 0;
7333
+ }
7334
+ }
7335
+ const parent = dirname7(dir);
7336
+ if (parent === dir) {
7337
+ return void 0;
7338
+ }
7339
+ dir = parent;
7340
+ }
7341
+ }
7342
+ function getCliVersion(metaUrl = import.meta.url) {
7343
+ const startDir = dirname7(fileURLToPath(metaUrl));
7344
+ const cached = versionCache.get(startDir);
7345
+ if (cached !== void 0) {
7346
+ return cached;
7347
+ }
7348
+ const version = readPackageVersion(startDir) ?? FALLBACK_VERSION;
7349
+ versionCache.set(startDir, version);
7350
+ return version;
7351
+ }
7352
+
7353
+ // src/services/parse-cache.ts
7354
+ var CACHE_FORMAT_VERSION = 1;
7355
+ var SETTLE_MS = 5e3;
7356
+ function getParseCacheDir() {
7357
+ return join35(getStateDir(), "parse-cache");
7358
+ }
7359
+ function getParseCachePath(toolId) {
7360
+ return join35(getParseCacheDir(), `${toolId}.json`);
7361
+ }
7362
+ function computeScanFingerprint(files, now = Date.now()) {
7363
+ const hash = createHash7("sha256");
7364
+ hash.update(`v${CACHE_FORMAT_VERSION}\0`);
7365
+ hash.update(`${getCliVersion()}\0`);
7366
+ hash.update(`${hostname6()}\0`);
7367
+ for (const file of [...files].sort()) {
7368
+ let stats;
7369
+ try {
7370
+ stats = statSync6(file);
7371
+ } catch {
7372
+ return null;
7373
+ }
7374
+ if (now - stats.mtimeMs < SETTLE_MS) {
7375
+ return null;
7376
+ }
7377
+ hash.update(`${file}\0${stats.mtimeMs}\0${stats.size}\0`);
7378
+ }
7379
+ return hash.digest("hex");
7380
+ }
7381
+ function loadCachedParseResult(toolId, fingerprint2) {
7382
+ if (!fingerprint2) {
7383
+ return null;
7384
+ }
7385
+ try {
7386
+ const envelope = JSON.parse(
7387
+ readFileSync14(getParseCachePath(toolId), "utf-8")
7388
+ );
7389
+ if (envelope.formatVersion !== CACHE_FORMAT_VERSION || envelope.fingerprint !== fingerprint2 || !Array.isArray(envelope.result?.buckets) || !Array.isArray(envelope.result?.sessions)) {
7390
+ return null;
7391
+ }
7392
+ return {
7393
+ buckets: envelope.result.buckets,
7394
+ sessions: envelope.result.sessions
7395
+ };
7396
+ } catch {
7397
+ return null;
7398
+ }
7399
+ }
7400
+ function saveCachedParseResult(toolId, fingerprint2, result) {
7401
+ if (!fingerprint2 || result.incomplete) {
7402
+ return;
7403
+ }
7404
+ const envelope = {
7405
+ formatVersion: CACHE_FORMAT_VERSION,
7406
+ fingerprint: fingerprint2,
7407
+ result: { buckets: result.buckets, sessions: result.sessions }
7408
+ };
7409
+ try {
7410
+ mkdirSync3(getParseCacheDir(), { recursive: true });
7411
+ writeFileSync5(getParseCachePath(toolId), JSON.stringify(envelope), "utf-8");
7412
+ } catch {
7413
+ }
7414
+ }
7415
+
7296
7416
  // src/services/parser-service.ts
7417
+ var PARSER_CONCURRENCY = 8;
7418
+ async function parseWithCache(parser) {
7419
+ try {
7420
+ const files = parser.listSourceFiles?.();
7421
+ const fingerprint2 = files ? computeScanFingerprint(files) : null;
7422
+ const cached = loadCachedParseResult(parser.tool.id, fingerprint2);
7423
+ if (cached) {
7424
+ return { status: "ok", result: cached, cached: true };
7425
+ }
7426
+ const result = await parser.parse();
7427
+ saveCachedParseResult(parser.tool.id, fingerprint2, result);
7428
+ return { status: "ok", result, cached: false };
7429
+ } catch (err) {
7430
+ return {
7431
+ status: "failed",
7432
+ message: err instanceof Error ? err.message : String(err)
7433
+ };
7434
+ }
7435
+ }
7436
+ async function mapWithConcurrency(items, limit, run2) {
7437
+ const results = new Array(items.length);
7438
+ let cursor = 0;
7439
+ async function worker() {
7440
+ while (cursor < items.length) {
7441
+ const index = cursor++;
7442
+ results[index] = await run2(items[index]);
7443
+ }
7444
+ }
7445
+ await Promise.all(
7446
+ Array.from({ length: Math.min(limit, items.length) }, () => worker())
7447
+ );
7448
+ return results;
7449
+ }
7297
7450
  async function runAllParsers() {
7298
7451
  const allBuckets = [];
7299
7452
  const allSessions = [];
7300
7453
  const parserResults = [];
7301
7454
  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) {
7455
+ const parsers2 = getAllParsers();
7456
+ const settled = await mapWithConcurrency(
7457
+ parsers2,
7458
+ PARSER_CONCURRENCY,
7459
+ parseWithCache
7460
+ );
7461
+ for (const [index, parser] of parsers2.entries()) {
7462
+ const outcome = settled[index];
7463
+ if (outcome.status === "failed") {
7464
+ failedSources.add(parser.tool.id);
7465
+ logger.warn(`${parser.tool.id} parser failed: ${outcome.message}`);
7466
+ continue;
7467
+ }
7468
+ const { buckets, sessions, incomplete } = outcome.result;
7469
+ if (incomplete) {
7324
7470
  failedSources.add(parser.tool.id);
7325
7471
  logger.warn(
7326
- `${parser.tool.id} parser failed: ${err instanceof Error ? err.message : String(err)}`
7472
+ `${parser.tool.id} parser reported an incomplete scan; preserving its remote history.`
7327
7473
  );
7474
+ continue;
7475
+ }
7476
+ if (buckets.length > 0) allBuckets.push(...buckets);
7477
+ if (sessions.length > 0) allSessions.push(...sessions);
7478
+ if (buckets.length > 0 || sessions.length > 0) {
7479
+ parserResults.push({
7480
+ source: parser.tool.id,
7481
+ buckets: buckets.length,
7482
+ sessions: sessions.length,
7483
+ ...outcome.cached ? { cached: true } : {}
7484
+ });
7328
7485
  }
7329
7486
  }
7330
7487
  return {
@@ -7399,17 +7556,31 @@ function persistUploadManifest(manifest, quiet) {
7399
7556
  function toDeviceMetadata(config) {
7400
7557
  return {
7401
7558
  deviceId: getOrCreateDeviceId(config),
7402
- hostname: hostname6().replace(/\.local$/, "")
7559
+ hostname: hostname7().replace(/\.local$/, "")
7403
7560
  };
7404
7561
  }
7405
- function toUploadBuckets(buckets, settings, device) {
7562
+ function createProjectIdentityResolver(settings) {
7563
+ const identities = /* @__PURE__ */ new Map();
7564
+ return (project) => {
7565
+ const name = project || "unknown";
7566
+ let identity = identities.get(name);
7567
+ if (!identity) {
7568
+ identity = toProjectIdentity({
7569
+ project: name,
7570
+ mode: settings.projectMode,
7571
+ salt: settings.projectHashSalt
7572
+ });
7573
+ identities.set(name, identity);
7574
+ }
7575
+ return identity;
7576
+ };
7577
+ }
7578
+ function toUploadBuckets(buckets, settings, device, resolveProject = createProjectIdentityResolver(
7579
+ settings
7580
+ )) {
7406
7581
  const aggregated = /* @__PURE__ */ new Map();
7407
7582
  for (const bucket of buckets) {
7408
- const project = toProjectIdentity({
7409
- project: bucket.project || "unknown",
7410
- mode: settings.projectMode,
7411
- salt: settings.projectHashSalt
7412
- });
7583
+ const project = resolveProject(bucket.project);
7413
7584
  const key = [
7414
7585
  bucket.source,
7415
7586
  bucket.model,
@@ -7445,13 +7616,11 @@ function toUploadBuckets(buckets, settings, device) {
7445
7616
  }
7446
7617
  return Array.from(aggregated.values());
7447
7618
  }
7448
- function toUploadSessions(sessions, settings, device) {
7619
+ function toUploadSessions(sessions, settings, device, resolveProject = createProjectIdentityResolver(
7620
+ settings
7621
+ )) {
7449
7622
  return sessions.map((session) => {
7450
- const project = toProjectIdentity({
7451
- project: session.project || "unknown",
7452
- mode: settings.projectMode,
7453
- salt: settings.projectHashSalt
7454
- });
7623
+ const project = resolveProject(session.project);
7455
7624
  return {
7456
7625
  source: session.source,
7457
7626
  projectKey: project.projectKey,
@@ -7598,7 +7767,8 @@ async function runSync(config, opts = {}) {
7598
7767
  const parts = [];
7599
7768
  if (p.buckets > 0) parts.push(`${p.buckets} buckets`);
7600
7769
  if (p.sessions > 0) parts.push(`${p.sessions} sessions`);
7601
- logger.info(` ${p.source}: ${parts.join(", ")}`);
7770
+ const suffix = p.cached ? " (unchanged)" : "";
7771
+ logger.info(` ${p.source}: ${parts.join(", ")}${suffix}`);
7602
7772
  }
7603
7773
  }
7604
7774
  const apiUrl = config.apiUrl || "https://token.guji.uno";
@@ -7629,8 +7799,19 @@ async function runSync(config, opts = {}) {
7629
7799
  deviceId: device.deviceId,
7630
7800
  settings
7631
7801
  });
7632
- const uploadBuckets = toUploadBuckets(allBuckets, settings, device);
7633
- const uploadSessions = toUploadSessions(allSessions, settings, device);
7802
+ const resolveProject = createProjectIdentityResolver(settings);
7803
+ const uploadBuckets = toUploadBuckets(
7804
+ allBuckets,
7805
+ settings,
7806
+ device,
7807
+ resolveProject
7808
+ );
7809
+ const uploadSessions = toUploadSessions(
7810
+ allSessions,
7811
+ settings,
7812
+ device,
7813
+ resolveProject
7814
+ );
7634
7815
  const previousManifest = loadUploadManifest();
7635
7816
  const uploadDiff = diffUploadManifest({
7636
7817
  buckets: uploadBuckets,
@@ -7683,7 +7864,9 @@ async function runSync(config, opts = {}) {
7683
7864
  skippedParts.length > 0 ? `No new or updated usage data to upload. Skipped ${skippedParts.join(" + ")}.` : "No new or updated usage data to upload."
7684
7865
  );
7685
7866
  }
7686
- persistUploadManifest(uploadDiff.nextManifest, quiet);
7867
+ if (uploadDiff.removedBuckets > 0 || uploadDiff.removedSessions > 0) {
7868
+ persistUploadManifest(uploadDiff.nextManifest, quiet);
7869
+ }
7687
7870
  markSyncSucceeded(source, { buckets: 0, sessions: 0 });
7688
7871
  return { buckets: 0, sessions: 0 };
7689
7872
  }
@@ -7850,14 +8033,14 @@ import { execFileSync as execFileSync7, spawn } from "child_process";
7850
8033
  import { existsSync as existsSync36 } from "fs";
7851
8034
  import { appendFile, mkdir, readFile } from "fs/promises";
7852
8035
  import { homedir as homedir33, platform as platform5 } from "os";
7853
- import { dirname as dirname7, join as join34, posix as posix3, win32 } from "path";
8036
+ import { dirname as dirname8, join as join36, posix as posix3, win32 } from "path";
7854
8037
 
7855
8038
  // src/infrastructure/service/index.ts
7856
8039
  import { platform as platform4 } from "os";
7857
8040
 
7858
8041
  // src/infrastructure/service/linux-systemd.ts
7859
8042
  import { execFileSync as execFileSync5 } from "child_process";
7860
- import { existsSync as existsSync34, mkdirSync as mkdirSync3, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
8043
+ import { existsSync as existsSync34, mkdirSync as mkdirSync4, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
7861
8044
  import { homedir as homedir31, platform as platform2 } from "os";
7862
8045
  import { posix } from "path";
7863
8046
 
@@ -8019,8 +8202,8 @@ function createLinuxSystemdServiceBackend() {
8019
8202
  execPath: command.execPath,
8020
8203
  args: command.args
8021
8204
  });
8022
- mkdirSync3(serviceDir, { recursive: true });
8023
- writeFileSync5(serviceFile, content, "utf-8");
8205
+ mkdirSync4(serviceDir, { recursive: true });
8206
+ writeFileSync6(serviceFile, content, "utf-8");
8024
8207
  execSystemctl(["daemon-reload"]);
8025
8208
  execSystemctl(["enable", SYSTEMD_SERVICE_NAME]);
8026
8209
  execSystemctl(["start", SYSTEMD_SERVICE_NAME]);
@@ -8179,7 +8362,7 @@ function createLinuxSystemdServiceBackend() {
8179
8362
 
8180
8363
  // src/infrastructure/service/macos-launchd.ts
8181
8364
  import { execFileSync as execFileSync6 } from "child_process";
8182
- import { existsSync as existsSync35, mkdirSync as mkdirSync4, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "fs";
8365
+ import { existsSync as existsSync35, mkdirSync as mkdirSync5, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "fs";
8183
8366
  import { homedir as homedir32, platform as platform3 } from "os";
8184
8367
  import { posix as posix2 } from "path";
8185
8368
  var MACOS_LAUNCHD_LABEL = "com.guji.tokenarena";
@@ -8321,8 +8504,8 @@ function writeLaunchAgentPlist() {
8321
8504
  standardErrorPath: stderrPath
8322
8505
  });
8323
8506
  ensureAppDirs();
8324
- mkdirSync4(getMacosLaunchAgentDir(), { recursive: true });
8325
- writeFileSync6(plistFile, plist, "utf-8");
8507
+ mkdirSync5(getMacosLaunchAgentDir(), { recursive: true });
8508
+ writeFileSync7(plistFile, plist, "utf-8");
8326
8509
  return plistFile;
8327
8510
  }
8328
8511
  function bootstrapLaunchAgent() {
@@ -8593,7 +8776,7 @@ function resolvePowerShellProfilePath() {
8593
8776
  const systemRoot = process.env.SYSTEMROOT || "C:\\Windows";
8594
8777
  const candidates = [
8595
8778
  "pwsh.exe",
8596
- join34(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
8779
+ join36(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
8597
8780
  ];
8598
8781
  for (const command of candidates) {
8599
8782
  try {
@@ -8818,7 +9001,7 @@ async function setupShellAlias() {
8818
9001
  return;
8819
9002
  }
8820
9003
  try {
8821
- await mkdir(dirname7(setup.configFile), { recursive: true });
9004
+ await mkdir(dirname8(setup.configFile), { recursive: true });
8822
9005
  let existingContent = "";
8823
9006
  if (existsSync36(setup.configFile)) {
8824
9007
  existingContent = await readFile(setup.configFile, "utf-8");
@@ -9083,41 +9266,6 @@ function buildLocalUsageDashboardData(input2) {
9083
9266
  };
9084
9267
  }
9085
9268
 
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
9269
  // src/infrastructure/ui/local-usage-dashboard.ts
9122
9270
  var MAX_RANK_ROWS = 6;
9123
9271
  var MAX_SESSION_ROWS = 6;
@@ -9516,7 +9664,7 @@ async function runSyncCommand(opts = {}) {
9516
9664
  }
9517
9665
 
9518
9666
  // src/commands/uninstall.ts
9519
- import { existsSync as existsSync37, readFileSync as readFileSync14, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "fs";
9667
+ import { existsSync as existsSync37, readFileSync as readFileSync15, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
9520
9668
  import { homedir as homedir34, platform as platform6 } from "os";
9521
9669
  function removeShellAlias() {
9522
9670
  const shell = process.env.SHELL;
@@ -9543,7 +9691,7 @@ function removeShellAlias() {
9543
9691
  }
9544
9692
  if (!existsSync37(configFile)) return;
9545
9693
  try {
9546
- let content = readFileSync14(configFile, "utf-8");
9694
+ let content = readFileSync15(configFile, "utf-8");
9547
9695
  const aliasPatterns = [
9548
9696
  // zsh / bash format: alias ta="tokenarena"
9549
9697
  new RegExp(
@@ -9565,7 +9713,7 @@ function removeShellAlias() {
9565
9713
  content = next;
9566
9714
  }
9567
9715
  }
9568
- writeFileSync7(configFile, content, "utf-8");
9716
+ writeFileSync8(configFile, content, "utf-8");
9569
9717
  logger.info(`Removed shell alias from ${configFile}`);
9570
9718
  } catch (err) {
9571
9719
  logger.warn(