@yishiguji/tokenarena 0.8.7 → 0.9.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 CHANGED
@@ -2370,26 +2370,172 @@ var KimiCodeParser = class {
2370
2370
  };
2371
2371
  registerParser(new KimiCodeParser());
2372
2372
 
2373
- // src/parsers/droid.ts
2374
- import { existsSync as existsSync14, readdirSync as readdirSync8 } from "fs";
2373
+ // src/parsers/letcode.ts
2374
+ import { existsSync as existsSync14 } from "fs";
2375
2375
  import { homedir as homedir13 } from "os";
2376
- import { basename as basename5, dirname as dirname3, join as join14 } from "path";
2377
- var TOOL_ID9 = "droid";
2378
- var TOOL_NAME9 = "Droid";
2379
- var DEFAULT_DATA_DIR5 = join14(homedir13(), ".factory", "sessions");
2376
+ import { basename as basename5, join as join14 } from "path";
2377
+ var TOOL_ID9 = "letcode";
2378
+ var TOOL_NAME9 = "LetCode";
2379
+ var DEFAULT_CONFIG_DIR = join14(homedir13(), ".config", "letcode");
2380
+ var DEFAULT_SESSIONS_DIR4 = join14(DEFAULT_CONFIG_DIR, "sessions");
2381
+ function getLetcodeSessionsDirs(env = process.env) {
2382
+ const dirs = [
2383
+ env.TOKEN_ARENA_LETCODE_DIR,
2384
+ env.XDG_CONFIG_HOME ? join14(env.XDG_CONFIG_HOME, "letcode", "sessions") : void 0,
2385
+ DEFAULT_SESSIONS_DIR4
2386
+ ].filter((value) => Boolean(value));
2387
+ return Array.from(new Set(dirs));
2388
+ }
2389
+ function toNonNegativeNumber(value) {
2390
+ const numberValue = Number(value);
2391
+ return Number.isFinite(numberValue) && numberValue >= 0 ? numberValue : 0;
2392
+ }
2393
+ function parseTimestamp2(value) {
2394
+ if (typeof value === "number" && Number.isFinite(value)) {
2395
+ const timestamp = new Date(value);
2396
+ return Number.isNaN(timestamp.getTime()) ? null : timestamp;
2397
+ }
2398
+ if (typeof value === "string" && value.trim()) {
2399
+ const asNumber = Number(value);
2400
+ if (Number.isFinite(asNumber)) {
2401
+ const timestamp2 = new Date(asNumber);
2402
+ if (!Number.isNaN(timestamp2.getTime())) {
2403
+ return timestamp2;
2404
+ }
2405
+ }
2406
+ const timestamp = new Date(value);
2407
+ return Number.isNaN(timestamp.getTime()) ? null : timestamp;
2408
+ }
2409
+ return null;
2410
+ }
2411
+ function shouldCountTelemetry(event) {
2412
+ if (event.kind !== "llm_request_telemetry") {
2413
+ return false;
2414
+ }
2415
+ if (event.phase !== "completed") {
2416
+ return false;
2417
+ }
2418
+ if (event.usage_completeness === "usage_missing") {
2419
+ return false;
2420
+ }
2421
+ return true;
2422
+ }
2423
+ function toSessionRole(kind) {
2424
+ if (kind === "user_message") {
2425
+ return "user";
2426
+ }
2427
+ if (kind === "assistant_message") {
2428
+ return "assistant";
2429
+ }
2430
+ return null;
2431
+ }
2432
+ var LetcodeParser = class {
2433
+ tool;
2434
+ sessionsDirs;
2435
+ constructor(sessionsDir) {
2436
+ this.sessionsDirs = sessionsDir ? [sessionsDir] : getLetcodeSessionsDirs();
2437
+ this.tool = {
2438
+ id: TOOL_ID9,
2439
+ name: TOOL_NAME9,
2440
+ dataDir: this.sessionsDirs[0] ?? DEFAULT_SESSIONS_DIR4
2441
+ };
2442
+ }
2443
+ async parse() {
2444
+ const entries = [];
2445
+ const sessionEvents = [];
2446
+ const seenEntryKeys = /* @__PURE__ */ new Set();
2447
+ for (const sessionsDir of this.sessionsDirs) {
2448
+ for (const filePath of findJsonlFiles(sessionsDir)) {
2449
+ const content = readFileSafe(filePath);
2450
+ if (!content) continue;
2451
+ const rows = parseJsonl(content);
2452
+ if (rows.length === 0) continue;
2453
+ const fallbackSessionId = extractSessionId(filePath) || basename5(filePath);
2454
+ for (const row of rows) {
2455
+ const sessionId = typeof row.session_id === "string" && row.session_id ? row.session_id : fallbackSessionId;
2456
+ const timestamp = parseTimestamp2(row.timestamp_ms);
2457
+ if (!timestamp) continue;
2458
+ const role = toSessionRole(row.kind);
2459
+ if (role) {
2460
+ sessionEvents.push({
2461
+ sessionId,
2462
+ source: TOOL_ID9,
2463
+ project: "unknown",
2464
+ timestamp,
2465
+ role
2466
+ });
2467
+ }
2468
+ if (!shouldCountTelemetry(row)) {
2469
+ continue;
2470
+ }
2471
+ const inputTokens = toNonNegativeNumber(row.provider_input_tokens);
2472
+ const outputTokens = toNonNegativeNumber(row.provider_output_tokens);
2473
+ const cachedTokens = toNonNegativeNumber(row.provider_cached_tokens);
2474
+ const reasoningTokens = toNonNegativeNumber(
2475
+ row.provider_reasoning_tokens
2476
+ );
2477
+ if (inputTokens + outputTokens + cachedTokens + reasoningTokens === 0) {
2478
+ continue;
2479
+ }
2480
+ const model = typeof row.model === "string" && row.model ? row.model : "unknown";
2481
+ const entryKey = [
2482
+ sessionId,
2483
+ timestamp.toISOString(),
2484
+ model,
2485
+ inputTokens,
2486
+ outputTokens,
2487
+ cachedTokens,
2488
+ reasoningTokens
2489
+ ].join("|");
2490
+ if (seenEntryKeys.has(entryKey)) {
2491
+ continue;
2492
+ }
2493
+ seenEntryKeys.add(entryKey);
2494
+ entries.push({
2495
+ sessionId,
2496
+ source: TOOL_ID9,
2497
+ model,
2498
+ project: "unknown",
2499
+ timestamp,
2500
+ inputTokens,
2501
+ outputTokens,
2502
+ reasoningTokens,
2503
+ cachedTokens
2504
+ });
2505
+ }
2506
+ }
2507
+ }
2508
+ return {
2509
+ buckets: aggregateToBuckets(entries),
2510
+ sessions: extractSessions(sessionEvents, entries)
2511
+ };
2512
+ }
2513
+ isInstalled() {
2514
+ return this.sessionsDirs.some((dir) => existsSync14(dir));
2515
+ }
2516
+ };
2517
+ registerParser(new LetcodeParser());
2518
+
2519
+ // src/parsers/droid.ts
2520
+ import { existsSync as existsSync15, readdirSync as readdirSync8 } from "fs";
2521
+ import { homedir as homedir14 } from "os";
2522
+ import { basename as basename6, dirname as dirname3, join as join15 } from "path";
2523
+ var TOOL_ID10 = "droid";
2524
+ var TOOL_NAME10 = "Droid";
2525
+ var DEFAULT_DATA_DIR5 = join15(homedir14(), ".factory", "sessions");
2380
2526
  function createToolDefinition7(dataDir) {
2381
2527
  return {
2382
- id: TOOL_ID9,
2383
- name: TOOL_NAME9,
2528
+ id: TOOL_ID10,
2529
+ name: TOOL_NAME10,
2384
2530
  dataDir
2385
2531
  };
2386
2532
  }
2387
2533
  function findSessionFiles3(dir) {
2388
2534
  const results = [];
2389
- if (!existsSync14(dir)) return results;
2535
+ if (!existsSync15(dir)) return results;
2390
2536
  try {
2391
2537
  for (const entry of readdirSync8(dir, { withFileTypes: true })) {
2392
- const fullPath = join14(dir, entry.name);
2538
+ const fullPath = join15(dir, entry.name);
2393
2539
  if (entry.isDirectory()) {
2394
2540
  results.push(...findSessionFiles3(fullPath));
2395
2541
  } else if (entry.isFile() && entry.name.endsWith(".jsonl") && !entry.name.endsWith(".settings.json")) {
@@ -2424,7 +2570,7 @@ var DroidParser = class {
2424
2570
  const sessionEvents = [];
2425
2571
  for (const filePath of sessionFiles) {
2426
2572
  const sessionId = filePath;
2427
- const project = extractDroidProject(basename5(dirname3(filePath)));
2573
+ const project = extractDroidProject(basename6(dirname3(filePath)));
2428
2574
  let firstMessageTimestamp = null;
2429
2575
  const content = readFileSafe(filePath);
2430
2576
  if (!content) continue;
@@ -2446,15 +2592,15 @@ var DroidParser = class {
2446
2592
  }
2447
2593
  sessionEvents.push({
2448
2594
  sessionId,
2449
- source: TOOL_ID9,
2595
+ source: TOOL_ID10,
2450
2596
  project,
2451
2597
  timestamp,
2452
2598
  role
2453
2599
  });
2454
2600
  }
2455
- const settingsPath = join14(
2601
+ const settingsPath = join15(
2456
2602
  dirname3(filePath),
2457
- `${basename5(filePath, ".jsonl")}.settings.json`
2603
+ `${basename6(filePath, ".jsonl")}.settings.json`
2458
2604
  );
2459
2605
  const settingsContent = readFileSafe(settingsPath);
2460
2606
  if (!settingsContent || firstMessageTimestamp === null) continue;
@@ -2481,7 +2627,7 @@ var DroidParser = class {
2481
2627
  }
2482
2628
  entries.push({
2483
2629
  sessionId,
2484
- source: TOOL_ID9,
2630
+ source: TOOL_ID10,
2485
2631
  model: settings.model || "unknown",
2486
2632
  project,
2487
2633
  timestamp: firstMessageTimestamp,
@@ -2497,22 +2643,22 @@ var DroidParser = class {
2497
2643
  };
2498
2644
  }
2499
2645
  isInstalled() {
2500
- return existsSync14(this.dataDir);
2646
+ return existsSync15(this.dataDir);
2501
2647
  }
2502
2648
  };
2503
2649
  registerParser(new DroidParser());
2504
2650
 
2505
2651
  // src/parsers/pi-coding-agent.ts
2506
- import { existsSync as existsSync15 } from "fs";
2507
- import { homedir as homedir14 } from "os";
2508
- import { join as join15 } from "path";
2509
- var TOOL_ID10 = "pi-coding-agent";
2510
- var TOOL_NAME10 = "pi";
2511
- var DEFAULT_SESSIONS_DIR4 = join15(homedir14(), ".pi", "agent", "sessions");
2652
+ import { existsSync as existsSync16 } from "fs";
2653
+ import { homedir as homedir15 } from "os";
2654
+ import { join as join16 } from "path";
2655
+ var TOOL_ID11 = "pi-coding-agent";
2656
+ var TOOL_NAME11 = "pi";
2657
+ var DEFAULT_SESSIONS_DIR5 = join16(homedir15(), ".pi", "agent", "sessions");
2512
2658
  function createToolDefinition8(dataDir) {
2513
2659
  return {
2514
- id: TOOL_ID10,
2515
- name: TOOL_NAME10,
2660
+ id: TOOL_ID11,
2661
+ name: TOOL_NAME11,
2516
2662
  dataDir
2517
2663
  };
2518
2664
  }
@@ -2541,7 +2687,7 @@ function getUsageNumber3(usage, ...keys) {
2541
2687
  function extractPiProjectFromCwd(cwd) {
2542
2688
  return getPathLeaf6(cwd);
2543
2689
  }
2544
- function extractPiProjectFromDir(filePath, sessionsDir = DEFAULT_SESSIONS_DIR4) {
2690
+ function extractPiProjectFromDir(filePath, sessionsDir = DEFAULT_SESSIONS_DIR5) {
2545
2691
  const normalizedFilePath = normalizeForPrefix4(filePath);
2546
2692
  const normalizedSessionsDir = normalizeForPrefix4(sessionsDir);
2547
2693
  const prefix = `${normalizedSessionsDir}/`;
@@ -2564,7 +2710,7 @@ function extractPiProjectFromDir(filePath, sessionsDir = DEFAULT_SESSIONS_DIR4)
2564
2710
  return slugParts.length > 0 ? slugParts[slugParts.length - 1] : "unknown";
2565
2711
  }
2566
2712
  var PiCodingAgentParser = class {
2567
- constructor(sessionsDir = DEFAULT_SESSIONS_DIR4) {
2713
+ constructor(sessionsDir = DEFAULT_SESSIONS_DIR5) {
2568
2714
  this.sessionsDir = sessionsDir;
2569
2715
  this.tool = createToolDefinition8(sessionsDir);
2570
2716
  }
@@ -2606,7 +2752,7 @@ var PiCodingAgentParser = class {
2606
2752
  if (message.role === "user" || message.role === "assistant") {
2607
2753
  sessionEvents.push({
2608
2754
  sessionId,
2609
- source: TOOL_ID10,
2755
+ source: TOOL_ID11,
2610
2756
  project,
2611
2757
  timestamp,
2612
2758
  role: message.role
@@ -2638,7 +2784,7 @@ var PiCodingAgentParser = class {
2638
2784
  }
2639
2785
  entries.push({
2640
2786
  sessionId,
2641
- source: TOOL_ID10,
2787
+ source: TOOL_ID11,
2642
2788
  model: message.model || "unknown",
2643
2789
  project,
2644
2790
  timestamp,
@@ -2655,23 +2801,23 @@ var PiCodingAgentParser = class {
2655
2801
  };
2656
2802
  }
2657
2803
  isInstalled() {
2658
- return existsSync15(this.sessionsDir);
2804
+ return existsSync16(this.sessionsDir);
2659
2805
  }
2660
2806
  };
2661
2807
  registerParser(new PiCodingAgentParser());
2662
2808
 
2663
2809
  // src/parsers/qwenpaw.ts
2664
- import { existsSync as existsSync16, readdirSync as readdirSync9 } from "fs";
2665
- import { homedir as homedir15, hostname as hostname3 } from "os";
2666
- import { join as join16 } from "path";
2667
- var TOOL_ID11 = "qwenpaw";
2668
- var TOOL_NAME11 = "QwenPaw";
2669
- var DEFAULT_USAGE_PATH = join16(homedir15(), ".qwenpaw", "token_usage.json");
2670
- var DEFAULT_WORKSPACE_PATH = join16(homedir15(), ".qwenpaw", "workspace");
2810
+ import { existsSync as existsSync17, readdirSync as readdirSync9 } from "fs";
2811
+ import { homedir as homedir16, hostname as hostname3 } from "os";
2812
+ import { join as join17 } from "path";
2813
+ var TOOL_ID12 = "qwenpaw";
2814
+ var TOOL_NAME12 = "QwenPaw";
2815
+ var DEFAULT_USAGE_PATH = join17(homedir16(), ".qwenpaw", "token_usage.json");
2816
+ var DEFAULT_WORKSPACE_PATH = join17(homedir16(), ".qwenpaw", "workspace");
2671
2817
  function createToolDefinition9(usagePath) {
2672
2818
  return {
2673
- id: TOOL_ID11,
2674
- name: TOOL_NAME11,
2819
+ id: TOOL_ID12,
2820
+ name: TOOL_NAME12,
2675
2821
  dataDir: usagePath
2676
2822
  };
2677
2823
  }
@@ -2728,7 +2874,7 @@ var QwenPawParser = class {
2728
2874
  continue;
2729
2875
  }
2730
2876
  entries.push({
2731
- source: TOOL_ID11,
2877
+ source: TOOL_ID12,
2732
2878
  model: resolveModel(recordKey, record),
2733
2879
  project: "unknown",
2734
2880
  timestamp,
@@ -2751,15 +2897,15 @@ var QwenPawParser = class {
2751
2897
  }
2752
2898
  async parseWorkspaceSessions() {
2753
2899
  const events = [];
2754
- if (!existsSync16(this.workspacePath)) {
2900
+ if (!existsSync17(this.workspacePath)) {
2755
2901
  return events;
2756
2902
  }
2757
2903
  try {
2758
2904
  const workspaceDirs = readdirSync9(this.workspacePath);
2759
2905
  for (const workspaceDir of workspaceDirs) {
2760
- const workspacePath = join16(this.workspacePath, workspaceDir);
2761
- const chatsPath = join16(workspacePath, "chats.json");
2762
- const sessionsPath = join16(workspacePath, "sessions");
2906
+ const workspacePath = join17(this.workspacePath, workspaceDir);
2907
+ const chatsPath = join17(workspacePath, "chats.json");
2908
+ const sessionsPath = join17(workspacePath, "sessions");
2763
2909
  const chatsContent = readFileSafe(chatsPath);
2764
2910
  if (!chatsContent) {
2765
2911
  continue;
@@ -2787,7 +2933,7 @@ var QwenPawParser = class {
2787
2933
  for (const msg of sessionMessages) {
2788
2934
  events.push({
2789
2935
  sessionId: chat.session_id,
2790
- source: TOOL_ID11,
2936
+ source: TOOL_ID12,
2791
2937
  project: workspaceDir,
2792
2938
  timestamp: new Date(msg.timestamp),
2793
2939
  role: msg.role
@@ -2803,7 +2949,7 @@ var QwenPawParser = class {
2803
2949
  }
2804
2950
  getSessionFiles(sessionsPath) {
2805
2951
  const files = /* @__PURE__ */ new Map();
2806
- if (!existsSync16(sessionsPath)) {
2952
+ if (!existsSync17(sessionsPath)) {
2807
2953
  return files;
2808
2954
  }
2809
2955
  try {
@@ -2812,7 +2958,7 @@ var QwenPawParser = class {
2812
2958
  if (!fileName.endsWith(".json")) {
2813
2959
  continue;
2814
2960
  }
2815
- const filePath = join16(sessionsPath, fileName);
2961
+ const filePath = join17(sessionsPath, fileName);
2816
2962
  const content = readFileSafe(filePath);
2817
2963
  if (content) {
2818
2964
  const sessionId = fileName.replace(/^[^_]+_/, "").replace(".json", "");
@@ -2910,15 +3056,15 @@ var QwenPawParser = class {
2910
3056
  });
2911
3057
  }
2912
3058
  isInstalled() {
2913
- return existsSync16(this.usagePath) || existsSync16(this.workspacePath);
3059
+ return existsSync17(this.usagePath) || existsSync17(this.workspacePath);
2914
3060
  }
2915
3061
  };
2916
3062
  registerParser(new QwenPawParser());
2917
3063
 
2918
3064
  // src/parsers/cline.ts
2919
3065
  import { readFileSync as readFileSync6, statSync } from "fs";
2920
- import { homedir as homedir16 } from "os";
2921
- import { basename as basename6, join as join17 } from "path";
3066
+ import { homedir as homedir17 } from "os";
3067
+ import { basename as basename7, join as join18 } from "path";
2922
3068
  var EXTENSION_ID = "saoudrizwan.claude-dev";
2923
3069
  var HOSTS = [
2924
3070
  "Code",
@@ -2932,26 +3078,26 @@ var HOSTS = [
2932
3078
  var TOOL6 = {
2933
3079
  id: "cline",
2934
3080
  name: "Cline",
2935
- dataDir: join17(homedir16(), ".cline")
3081
+ dataDir: join18(homedir17(), ".cline")
2936
3082
  };
2937
3083
  function getHostRoots() {
2938
3084
  const out = [];
2939
3085
  if (process.platform === "darwin") {
2940
- const base = join17(homedir16(), "Library", "Application Support");
2941
- for (const h of HOSTS) out.push(join17(base, h));
3086
+ const base = join18(homedir17(), "Library", "Application Support");
3087
+ for (const h of HOSTS) out.push(join18(base, h));
2942
3088
  } else if (process.platform === "win32") {
2943
- const appData = process.env.APPDATA?.trim() || join17(homedir16(), "AppData", "Roaming");
2944
- for (const h of HOSTS) out.push(join17(appData, h));
3089
+ const appData = process.env.APPDATA?.trim() || join18(homedir17(), "AppData", "Roaming");
3090
+ for (const h of HOSTS) out.push(join18(appData, h));
2945
3091
  } else {
2946
- const xdg = process.env.XDG_CONFIG_HOME?.trim() || join17(homedir16(), ".config");
2947
- for (const h of HOSTS) out.push(join17(xdg, h));
3092
+ const xdg = process.env.XDG_CONFIG_HOME?.trim() || join18(homedir17(), ".config");
3093
+ for (const h of HOSTS) out.push(join18(xdg, h));
2948
3094
  }
2949
3095
  return out;
2950
3096
  }
2951
3097
  function findClineExtensionDirs() {
2952
3098
  const dirs = [];
2953
3099
  for (const root of getHostRoots()) {
2954
- const ext = join17(root, "User", "globalStorage", EXTENSION_ID);
3100
+ const ext = join18(root, "User", "globalStorage", EXTENSION_ID);
2955
3101
  try {
2956
3102
  if (statSync(ext).isDirectory()) dirs.push(ext);
2957
3103
  } catch {
@@ -2969,7 +3115,7 @@ function readJsonSafe(path) {
2969
3115
  function projectFromPath(absPath) {
2970
3116
  if (!absPath || typeof absPath !== "string") return "unknown";
2971
3117
  const trimmed = absPath.replace(/[\\/]+$/, "");
2972
- const name = basename6(trimmed);
3118
+ const name = basename7(trimmed);
2973
3119
  return name || "unknown";
2974
3120
  }
2975
3121
  var ClineParser = class {
@@ -2983,7 +3129,7 @@ var ClineParser = class {
2983
3129
  const entries = [];
2984
3130
  const sessionEvents = [];
2985
3131
  for (const extDir of extDirs) {
2986
- const history = readJsonSafe(join17(extDir, "state", "taskHistory.json"));
3132
+ const history = readJsonSafe(join18(extDir, "state", "taskHistory.json"));
2987
3133
  if (!Array.isArray(history)) continue;
2988
3134
  for (const item of history) {
2989
3135
  try {
@@ -2994,7 +3140,7 @@ var ClineParser = class {
2994
3140
  );
2995
3141
  const fallbackModel = item.modelId && String(item.modelId).trim() || "cline-unknown";
2996
3142
  const messages = readJsonSafe(
2997
- join17(extDir, "tasks", taskId, "ui_messages.json")
3143
+ join18(extDir, "tasks", taskId, "ui_messages.json")
2998
3144
  );
2999
3145
  if (!Array.isArray(messages)) continue;
3000
3146
  for (const msg of messages) {
@@ -3065,20 +3211,20 @@ registerParser(new ClineParser());
3065
3211
  import { execFileSync as execFileSync3 } from "child_process";
3066
3212
  import {
3067
3213
  copyFileSync,
3068
- existsSync as existsSync17,
3214
+ existsSync as existsSync18,
3069
3215
  mkdtempSync,
3070
3216
  readdirSync as readdirSync10,
3071
3217
  readFileSync as readFileSync7,
3072
3218
  rmSync,
3073
3219
  statSync as statSync2
3074
3220
  } from "fs";
3075
- import { homedir as homedir17, tmpdir } from "os";
3076
- import { join as join18, resolve } from "path";
3077
- var KIROAGENT_RELATIVE = join18("User", "globalStorage", "kiro.kiroagent");
3221
+ import { homedir as homedir18, tmpdir } from "os";
3222
+ import { join as join19, resolve } from "path";
3223
+ var KIROAGENT_RELATIVE = join19("User", "globalStorage", "kiro.kiroagent");
3078
3224
  function getDefaultBasePath() {
3079
3225
  if (process.platform === "darwin") {
3080
- return join18(
3081
- homedir17(),
3226
+ return join19(
3227
+ homedir18(),
3082
3228
  "Library",
3083
3229
  "Application Support",
3084
3230
  "Kiro",
@@ -3086,11 +3232,11 @@ function getDefaultBasePath() {
3086
3232
  );
3087
3233
  }
3088
3234
  if (process.platform === "win32") {
3089
- const appData = process.env.APPDATA?.trim() || join18(homedir17(), "AppData", "Roaming");
3090
- return join18(appData, "Kiro", KIROAGENT_RELATIVE);
3235
+ const appData = process.env.APPDATA?.trim() || join19(homedir18(), "AppData", "Roaming");
3236
+ return join19(appData, "Kiro", KIROAGENT_RELATIVE);
3091
3237
  }
3092
- const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim() || join18(homedir17(), ".config");
3093
- return join18(xdgConfigHome, "Kiro", KIROAGENT_RELATIVE);
3238
+ const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim() || join19(homedir18(), ".config");
3239
+ return join19(xdgConfigHome, "Kiro", KIROAGENT_RELATIVE);
3094
3240
  }
3095
3241
  var TOOL7 = {
3096
3242
  id: "kiro",
@@ -3101,10 +3247,10 @@ function getKiroBasePath() {
3101
3247
  const explicit = process.env.KIRO_BASE_PATH?.trim();
3102
3248
  if (explicit) {
3103
3249
  const r = resolve(explicit);
3104
- return existsSync17(r) ? r : null;
3250
+ return existsSync18(r) ? r : null;
3105
3251
  }
3106
3252
  const def = getDefaultBasePath();
3107
- return existsSync17(def) ? def : null;
3253
+ return existsSync18(def) ? def : null;
3108
3254
  }
3109
3255
  function isLockError(err) {
3110
3256
  return err instanceof Error && typeof err.message === "string" && /database is locked/i.test(err.message);
@@ -3125,12 +3271,12 @@ function readDb(dbPath) {
3125
3271
  return queryDb(dbPath, TOKENS_SQL);
3126
3272
  } catch (err) {
3127
3273
  if (!isLockError(err)) throw err;
3128
- const snapshotDir = mkdtempSync(join18(tmpdir(), "vibe-usage-kiro-"));
3129
- const queryPath = join18(snapshotDir, "devdata.sqlite");
3274
+ const snapshotDir = mkdtempSync(join19(tmpdir(), "vibe-usage-kiro-"));
3275
+ const queryPath = join19(snapshotDir, "devdata.sqlite");
3130
3276
  copyFileSync(dbPath, queryPath);
3131
3277
  for (const suffix of ["-shm", "-wal"]) {
3132
3278
  const companion = `${dbPath}${suffix}`;
3133
- if (existsSync17(companion))
3279
+ if (existsSync18(companion))
3134
3280
  copyFileSync(companion, `${queryPath}${suffix}`);
3135
3281
  }
3136
3282
  try {
@@ -3182,7 +3328,7 @@ function buildModelTimeline(base) {
3182
3328
  }
3183
3329
  for (const entry of entries) {
3184
3330
  if (!entry.isDirectory() || entry.name === "dev_data") continue;
3185
- const dirPath = join18(base, entry.name);
3331
+ const dirPath = join19(base, entry.name);
3186
3332
  let files;
3187
3333
  try {
3188
3334
  files = readdirSync10(dirPath).filter((f) => f.endsWith(".chat"));
@@ -3191,7 +3337,7 @@ function buildModelTimeline(base) {
3191
3337
  }
3192
3338
  for (const file of files) {
3193
3339
  try {
3194
- const data = JSON.parse(readFileSync7(join18(dirPath, file), "utf-8"));
3340
+ const data = JSON.parse(readFileSync7(join19(dirPath, file), "utf-8"));
3195
3341
  const meta = data?.metadata;
3196
3342
  if (!meta?.modelId || !meta?.startTime) continue;
3197
3343
  const startMs = Number(meta.startTime);
@@ -3240,13 +3386,13 @@ var KiroParser = class {
3240
3386
  async parse() {
3241
3387
  const base = getKiroBasePath();
3242
3388
  if (!base) return { buckets: [], sessions: [] };
3243
- const dbPath = join18(base, "dev_data", "devdata.sqlite");
3244
- const jsonlPath = join18(base, "dev_data", "tokens_generated.jsonl");
3389
+ const dbPath = join19(base, "dev_data", "devdata.sqlite");
3390
+ const jsonlPath = join19(base, "dev_data", "tokens_generated.jsonl");
3245
3391
  let rows;
3246
3392
  try {
3247
- if (existsSync17(dbPath)) {
3393
+ if (existsSync18(dbPath)) {
3248
3394
  rows = readDb(dbPath);
3249
- } else if (existsSync17(jsonlPath)) {
3395
+ } else if (existsSync18(jsonlPath)) {
3250
3396
  rows = readJsonl(jsonlPath);
3251
3397
  } else {
3252
3398
  return { buckets: [], sessions: [] };
@@ -3297,8 +3443,8 @@ registerParser(new KiroParser());
3297
3443
 
3298
3444
  // src/parsers/roo-code.ts
3299
3445
  import { readdirSync as readdirSync11, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
3300
- import { homedir as homedir18 } from "os";
3301
- import { basename as basename7, join as join19 } from "path";
3446
+ import { homedir as homedir19 } from "os";
3447
+ import { basename as basename8, join as join20 } from "path";
3302
3448
  var EXTENSION_ID2 = "rooveterinaryinc.roo-cline";
3303
3449
  var HOSTS2 = [
3304
3450
  "Code",
@@ -3313,17 +3459,17 @@ function getHostRoots2() {
3313
3459
  const out = [];
3314
3460
  let roots;
3315
3461
  if (process.platform === "darwin") {
3316
- roots = [join19(homedir18(), "Library", "Application Support")];
3462
+ roots = [join20(homedir19(), "Library", "Application Support")];
3317
3463
  } else if (process.platform === "win32") {
3318
3464
  roots = [
3319
- process.env.APPDATA?.trim() || join19(homedir18(), "AppData", "Roaming")
3465
+ process.env.APPDATA?.trim() || join20(homedir19(), "AppData", "Roaming")
3320
3466
  ];
3321
3467
  } else {
3322
- roots = [process.env.XDG_CONFIG_HOME?.trim() || join19(homedir18(), ".config")];
3468
+ roots = [process.env.XDG_CONFIG_HOME?.trim() || join20(homedir19(), ".config")];
3323
3469
  }
3324
3470
  for (const root of roots) {
3325
3471
  for (const h of HOSTS2) {
3326
- out.push(join19(root, h));
3472
+ out.push(join20(root, h));
3327
3473
  }
3328
3474
  }
3329
3475
  return out;
@@ -3331,7 +3477,7 @@ function getHostRoots2() {
3331
3477
  function findExtensionDirs() {
3332
3478
  const dirs = [];
3333
3479
  for (const root of getHostRoots2()) {
3334
- const ext = join19(root, "User", "globalStorage", EXTENSION_ID2);
3480
+ const ext = join20(root, "User", "globalStorage", EXTENSION_ID2);
3335
3481
  try {
3336
3482
  if (statSync3(ext).isDirectory()) dirs.push(ext);
3337
3483
  } catch {
@@ -3349,13 +3495,13 @@ function readJsonSafe2(path) {
3349
3495
  function projectFromPath2(absPath) {
3350
3496
  if (!absPath || typeof absPath !== "string") return "unknown";
3351
3497
  const trimmed = absPath.replace(/[\\/]+$/, "");
3352
- const name = basename7(trimmed);
3498
+ const name = basename8(trimmed);
3353
3499
  return name || "unknown";
3354
3500
  }
3355
3501
  function readHistoryItems(extDir) {
3356
- const tasksDir = join19(extDir, "tasks");
3502
+ const tasksDir = join20(extDir, "tasks");
3357
3503
  const index = readJsonSafe2(
3358
- join19(tasksDir, "_index.json")
3504
+ join20(tasksDir, "_index.json")
3359
3505
  );
3360
3506
  if (index?.entries && Array.isArray(index.entries)) return index.entries;
3361
3507
  const items = [];
@@ -3369,7 +3515,7 @@ function readHistoryItems(extDir) {
3369
3515
  if (!entry.isDirectory() || entry.name.startsWith("_") || entry.name.startsWith("."))
3370
3516
  continue;
3371
3517
  const item = readJsonSafe2(
3372
- join19(tasksDir, entry.name, "history_item.json")
3518
+ join20(tasksDir, entry.name, "history_item.json")
3373
3519
  );
3374
3520
  if (item && typeof item === "object") items.push(item);
3375
3521
  }
@@ -3396,7 +3542,7 @@ var RooCodeParser = class {
3396
3542
  const project = projectFromPath2(item.workspace);
3397
3543
  const fallbackModel = item.apiConfigName && String(item.apiConfigName).trim() || "roo-unknown";
3398
3544
  const messages = readJsonSafe2(
3399
- join19(extDir, "tasks", taskId, "ui_messages.json")
3545
+ join20(extDir, "tasks", taskId, "ui_messages.json")
3400
3546
  );
3401
3547
  if (!Array.isArray(messages)) continue;
3402
3548
  for (const msg of messages) {
@@ -3460,11 +3606,11 @@ var RooCodeParser = class {
3460
3606
  registerParser(new RooCodeParser());
3461
3607
 
3462
3608
  // src/parsers/snow.ts
3463
- import { existsSync as existsSync18 } from "fs";
3464
- import { homedir as homedir19 } from "os";
3465
- import { join as join20 } from "path";
3466
- var DEFAULT_DATA_DIR6 = join20(homedir19(), ".snow", "usage");
3467
- function toNonNegativeNumber(value) {
3609
+ import { existsSync as existsSync19 } from "fs";
3610
+ import { homedir as homedir20 } from "os";
3611
+ import { join as join21 } from "path";
3612
+ var DEFAULT_DATA_DIR6 = join21(homedir20(), ".snow", "usage");
3613
+ function toNonNegativeNumber2(value) {
3468
3614
  const numberValue = Number(value);
3469
3615
  return Number.isFinite(numberValue) && numberValue >= 0 ? numberValue : 0;
3470
3616
  }
@@ -3487,10 +3633,10 @@ var SnowParser = class {
3487
3633
  if (typeof record.timestamp !== "string") continue;
3488
3634
  const timestamp = new Date(record.timestamp);
3489
3635
  if (Number.isNaN(timestamp.getTime())) continue;
3490
- const inputTokens = toNonNegativeNumber(record.inputTokens);
3491
- const outputTokens = toNonNegativeNumber(record.outputTokens);
3492
- const cachedTokens = toNonNegativeNumber(record.cacheReadInputTokens);
3493
- const reasoningTokens = toNonNegativeNumber(record.reasoningTokens);
3636
+ const inputTokens = toNonNegativeNumber2(record.inputTokens);
3637
+ const outputTokens = toNonNegativeNumber2(record.outputTokens);
3638
+ const cachedTokens = toNonNegativeNumber2(record.cacheReadInputTokens);
3639
+ const reasoningTokens = toNonNegativeNumber2(record.reasoningTokens);
3494
3640
  if (inputTokens + outputTokens + cachedTokens + reasoningTokens === 0)
3495
3641
  continue;
3496
3642
  entries.push({
@@ -3510,26 +3656,26 @@ var SnowParser = class {
3510
3656
  return { buckets: aggregateToBuckets(entries), sessions: [] };
3511
3657
  }
3512
3658
  isInstalled() {
3513
- return existsSync18(this.dataDir);
3659
+ return existsSync19(this.dataDir);
3514
3660
  }
3515
3661
  };
3516
3662
  registerParser(new SnowParser());
3517
3663
 
3518
3664
  // src/parsers/cursor.ts
3519
3665
  import { execFileSync as execFileSync4 } from "child_process";
3520
- import { copyFileSync as copyFileSync2, existsSync as existsSync19, mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
3521
- import { homedir as homedir20, tmpdir as tmpdir2 } from "os";
3522
- import { dirname as dirname4, join as join21, resolve as resolve2 } from "path";
3523
- var TOOL_ID12 = "cursor";
3524
- var TOOL_NAME12 = "Cursor";
3525
- var STATE_DB_RELATIVE = join21("User", "globalStorage", "state.vscdb");
3666
+ import { copyFileSync as copyFileSync2, existsSync as existsSync20, mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
3667
+ import { homedir as homedir21, tmpdir as tmpdir2 } from "os";
3668
+ import { dirname as dirname4, join as join22, resolve as resolve2 } from "path";
3669
+ var TOOL_ID13 = "cursor";
3670
+ var TOOL_NAME13 = "Cursor";
3671
+ var STATE_DB_RELATIVE = join22("User", "globalStorage", "state.vscdb");
3526
3672
  var ACCESS_TOKEN_KEY = "cursorAuth/accessToken";
3527
3673
  var SESSION_COOKIE = "WorkosCursorSessionToken";
3528
3674
  var FETCH_TIMEOUT_MS = 1e4;
3529
3675
  function getDefaultStateDbPath() {
3530
3676
  if (process.platform === "darwin") {
3531
- return join21(
3532
- homedir20(),
3677
+ return join22(
3678
+ homedir21(),
3533
3679
  "Library",
3534
3680
  "Application Support",
3535
3681
  "Cursor",
@@ -3537,25 +3683,25 @@ function getDefaultStateDbPath() {
3537
3683
  );
3538
3684
  }
3539
3685
  if (process.platform === "win32") {
3540
- const appData = process.env.APPDATA?.trim() || join21(homedir20(), "AppData", "Roaming");
3541
- return join21(appData, "Cursor", STATE_DB_RELATIVE);
3686
+ const appData = process.env.APPDATA?.trim() || join22(homedir21(), "AppData", "Roaming");
3687
+ return join22(appData, "Cursor", STATE_DB_RELATIVE);
3542
3688
  }
3543
- const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim() || join21(homedir20(), ".config");
3544
- return join21(xdgConfigHome, "Cursor", STATE_DB_RELATIVE);
3689
+ const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim() || join22(homedir21(), ".config");
3690
+ return join22(xdgConfigHome, "Cursor", STATE_DB_RELATIVE);
3545
3691
  }
3546
3692
  function getCursorStateDbPath() {
3547
3693
  const explicit = process.env.CURSOR_STATE_DB_PATH?.trim();
3548
3694
  if (explicit) {
3549
3695
  const resolved = resolve2(explicit);
3550
- return existsSync19(resolved) ? resolved : null;
3696
+ return existsSync20(resolved) ? resolved : null;
3551
3697
  }
3552
3698
  const configDirs = process.env.CURSOR_CONFIG_DIR?.trim();
3553
3699
  const candidates = configDirs ? configDirs.split(",").map((v) => v.trim()).filter(Boolean).map((v) => {
3554
3700
  const r = resolve2(v);
3555
- return r.endsWith(".vscdb") ? r : join21(r, STATE_DB_RELATIVE);
3701
+ return r.endsWith(".vscdb") ? r : join22(r, STATE_DB_RELATIVE);
3556
3702
  }) : [getDefaultStateDbPath()];
3557
3703
  for (const c of candidates) {
3558
- if (existsSync19(c)) return c;
3704
+ if (existsSync20(c)) return c;
3559
3705
  }
3560
3706
  return null;
3561
3707
  }
@@ -3595,13 +3741,13 @@ function readAccessToken(dbPath) {
3595
3741
  return queryAccessToken(dbPath);
3596
3742
  } catch (err) {
3597
3743
  if (!isLockError2(err)) throw err;
3598
- const snapshotDir = mkdtempSync2(join21(tmpdir2(), "tokenarena-cursor-"));
3599
- const queryPath = join21(snapshotDir, "state.vscdb");
3744
+ const snapshotDir = mkdtempSync2(join22(tmpdir2(), "tokenarena-cursor-"));
3745
+ const queryPath = join22(snapshotDir, "state.vscdb");
3600
3746
  try {
3601
3747
  copyFileSync2(dbPath, queryPath);
3602
3748
  for (const suffix of ["-shm", "-wal"]) {
3603
3749
  const companion = `${dbPath}${suffix}`;
3604
- if (existsSync19(companion))
3750
+ if (existsSync20(companion))
3605
3751
  copyFileSync2(companion, `${queryPath}${suffix}`);
3606
3752
  }
3607
3753
  return queryAccessToken(queryPath);
@@ -3729,8 +3875,8 @@ function parseInt0(value) {
3729
3875
  }
3730
3876
  function createToolDefinition10(dbPath) {
3731
3877
  return {
3732
- id: TOOL_ID12,
3733
- name: TOOL_NAME12,
3878
+ id: TOOL_ID13,
3879
+ name: TOOL_NAME13,
3734
3880
  dataDir: dirname4(dbPath)
3735
3881
  };
3736
3882
  }
@@ -3746,7 +3892,7 @@ var CursorParser = class {
3746
3892
  this.tool = createToolDefinition10(this.dbPath);
3747
3893
  }
3748
3894
  async parse() {
3749
- if (!this.dbPath || !existsSync19(this.dbPath)) {
3895
+ if (!this.dbPath || !existsSync20(this.dbPath)) {
3750
3896
  return { buckets: [], sessions: [] };
3751
3897
  }
3752
3898
  let token;
@@ -3793,7 +3939,7 @@ var CursorParser = class {
3793
3939
  const output = outputIdx >= 0 ? parseInt0(row[outputIdx]) : 0;
3794
3940
  if (inputCacheWrite + inputNoCache + cacheRead + output === 0) continue;
3795
3941
  entries.push({
3796
- source: TOOL_ID12,
3942
+ source: TOOL_ID13,
3797
3943
  model,
3798
3944
  project: "unknown",
3799
3945
  timestamp,
@@ -3809,19 +3955,19 @@ var CursorParser = class {
3809
3955
  };
3810
3956
  }
3811
3957
  isInstalled() {
3812
- return existsSync19(this.dbPath);
3958
+ return existsSync20(this.dbPath);
3813
3959
  }
3814
3960
  };
3815
3961
  registerParser(new CursorParser());
3816
3962
 
3817
3963
  // src/parsers/zcode.ts
3818
3964
  import { createHash as createHash2 } from "crypto";
3819
- import { existsSync as existsSync20 } from "fs";
3820
- import { homedir as homedir21, hostname as hostname4 } from "os";
3821
- import { dirname as dirname5, join as join22 } from "path";
3822
- var TOOL_ID13 = "zcode";
3823
- var TOOL_NAME13 = "ZCode";
3824
- var DEFAULT_DB_PATH3 = join22(homedir21(), ".zcode", "cli", "db", "db.sqlite");
3965
+ import { existsSync as existsSync21 } from "fs";
3966
+ import { homedir as homedir22, hostname as hostname4 } from "os";
3967
+ import { dirname as dirname5, join as join23 } from "path";
3968
+ var TOOL_ID14 = "zcode";
3969
+ var TOOL_NAME14 = "ZCode";
3970
+ var DEFAULT_DB_PATH3 = join23(homedir22(), ".zcode", "cli", "db", "db.sqlite");
3825
3971
  var MODEL_USAGE_QUERY = `SELECT
3826
3972
  model_usage.session_id as sessionId,
3827
3973
  session.directory as directory,
@@ -3856,8 +4002,8 @@ var TURN_USAGE_QUERY = `SELECT
3856
4002
  WHERE duration_ms IS NOT NULL`;
3857
4003
  function createToolDefinition11(dbPath) {
3858
4004
  return {
3859
- id: TOOL_ID13,
3860
- name: TOOL_NAME13,
4005
+ id: TOOL_ID14,
4006
+ name: TOOL_NAME14,
3861
4007
  dataDir: dirname5(dbPath)
3862
4008
  };
3863
4009
  }
@@ -3905,7 +4051,7 @@ function getOrCreateDraft(drafts, sessionId, project) {
3905
4051
  }
3906
4052
  const next = {
3907
4053
  sessionId,
3908
- source: TOOL_ID13,
4054
+ source: TOOL_ID14,
3909
4055
  project,
3910
4056
  firstMessageAt: null,
3911
4057
  lastMessageAt: null,
@@ -4073,7 +4219,7 @@ var ZCodeParser = class {
4073
4219
  this.tool = createToolDefinition11(this.dbPath);
4074
4220
  }
4075
4221
  async parse() {
4076
- if (!existsSync20(this.dbPath)) {
4222
+ if (!existsSync21(this.dbPath)) {
4077
4223
  return { buckets: [], sessions: [] };
4078
4224
  }
4079
4225
  const usageRows = await this.queryRows(
@@ -4095,7 +4241,7 @@ var ZCodeParser = class {
4095
4241
  }
4096
4242
  entries.push({
4097
4243
  sessionId: getString2(row.sessionId) ?? void 0,
4098
- source: TOOL_ID13,
4244
+ source: TOOL_ID14,
4099
4245
  model: getString2(row.model) ?? "unknown",
4100
4246
  project: getPathLeaf7(getString2(row.directory)),
4101
4247
  timestamp,
@@ -4131,7 +4277,7 @@ var ZCodeParser = class {
4131
4277
  };
4132
4278
  }
4133
4279
  isInstalled() {
4134
- return existsSync20(this.dbPath);
4280
+ return existsSync21(this.dbPath);
4135
4281
  }
4136
4282
  };
4137
4283
  registerParser(new ZCodeParser());
@@ -4142,31 +4288,31 @@ import { Command, Option } from "commander";
4142
4288
  // src/infrastructure/config/manager.ts
4143
4289
  import { randomUUID } from "crypto";
4144
4290
  import {
4145
- existsSync as existsSync21,
4291
+ existsSync as existsSync22,
4146
4292
  mkdirSync,
4147
4293
  readFileSync as readFileSync9,
4148
4294
  unlinkSync,
4149
4295
  writeFileSync
4150
4296
  } from "fs";
4151
- import { join as join24 } from "path";
4297
+ import { join as join25 } from "path";
4152
4298
 
4153
4299
  // src/infrastructure/xdg.ts
4154
- import { homedir as homedir22 } from "os";
4155
- import { join as join23 } from "path";
4300
+ import { homedir as homedir23 } from "os";
4301
+ import { join as join24 } from "path";
4156
4302
  function getConfigHome() {
4157
- return process.env.XDG_CONFIG_HOME || join23(homedir22(), ".config");
4303
+ return process.env.XDG_CONFIG_HOME || join24(homedir23(), ".config");
4158
4304
  }
4159
4305
  function getStateHome() {
4160
- return process.env.XDG_STATE_HOME || join23(homedir22(), ".local", "state");
4306
+ return process.env.XDG_STATE_HOME || join24(homedir23(), ".local", "state");
4161
4307
  }
4162
4308
  function getRuntimeDir() {
4163
4309
  return process.env.XDG_RUNTIME_DIR || getStateHome();
4164
4310
  }
4165
4311
 
4166
4312
  // src/infrastructure/config/manager.ts
4167
- var CONFIG_DIR = join24(getConfigHome(), "tokenarena");
4313
+ var CONFIG_DIR = join25(getConfigHome(), "tokenarena");
4168
4314
  var isDev = process.env.TOKEN_ARENA_DEV === "1";
4169
- var CONFIG_FILE = join24(CONFIG_DIR, isDev ? "config.dev.json" : "config.json");
4315
+ var CONFIG_FILE = join25(CONFIG_DIR, isDev ? "config.dev.json" : "config.json");
4170
4316
  var DEFAULT_API_URL = "https://token.guji.uno";
4171
4317
  var VALID_CONFIG_KEYS = [
4172
4318
  "apiKey",
@@ -4182,7 +4328,7 @@ function getConfigDir() {
4182
4328
  return CONFIG_DIR;
4183
4329
  }
4184
4330
  function loadConfig() {
4185
- if (!existsSync21(CONFIG_FILE)) return null;
4331
+ if (!existsSync22(CONFIG_FILE)) return null;
4186
4332
  try {
4187
4333
  const raw = readFileSync9(CONFIG_FILE, "utf-8");
4188
4334
  const config = JSON.parse(raw);
@@ -4200,7 +4346,7 @@ function saveConfig(config) {
4200
4346
  `, "utf-8");
4201
4347
  }
4202
4348
  function deleteConfig() {
4203
- if (existsSync21(CONFIG_FILE)) {
4349
+ if (existsSync22(CONFIG_FILE)) {
4204
4350
  unlinkSync(CONFIG_FILE);
4205
4351
  }
4206
4352
  }
@@ -4836,14 +4982,19 @@ import { URL as URL2 } from "url";
4836
4982
  var MAX_RETRIES = 3;
4837
4983
  var INITIAL_DELAY = 1e3;
4838
4984
  var TIMEOUT_MS = 6e4;
4839
- function getIngestPayloadSize(device, buckets, sessions) {
4840
- const payload = {
4985
+ function buildIngestPayload(device, buckets, sessions, options) {
4986
+ return {
4841
4987
  schemaVersion: 2,
4842
4988
  device,
4843
4989
  buckets,
4844
- sessions: sessions ?? []
4990
+ sessions: sessions ?? [],
4991
+ ...options?.syncAchievements === void 0 ? {} : { syncAchievements: options.syncAchievements }
4845
4992
  };
4846
- return Buffer.byteLength(JSON.stringify(payload));
4993
+ }
4994
+ function getIngestPayloadSize(device, buckets, sessions, options) {
4995
+ return Buffer.byteLength(
4996
+ JSON.stringify(buildIngestPayload(device, buckets, sessions, options))
4997
+ );
4847
4998
  }
4848
4999
  var ApiClient = class {
4849
5000
  constructor(apiUrl, apiKey) {
@@ -4855,11 +5006,17 @@ var ApiClient = class {
4855
5006
  /**
4856
5007
  * Ingest buckets and sessions to server
4857
5008
  */
4858
- async ingest(device, buckets, sessions, onProgress) {
5009
+ async ingest(device, buckets, sessions, onProgress, options) {
4859
5010
  let lastError;
4860
5011
  for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
4861
5012
  try {
4862
- return await this.sendIngest(device, buckets, sessions, onProgress);
5013
+ return await this.sendIngest(
5014
+ device,
5015
+ buckets,
5016
+ sessions,
5017
+ onProgress,
5018
+ options
5019
+ );
4863
5020
  } catch (err) {
4864
5021
  lastError = err;
4865
5022
  const httpErr = err;
@@ -4874,16 +5031,11 @@ var ApiClient = class {
4874
5031
  }
4875
5032
  throw lastError;
4876
5033
  }
4877
- sendIngest(device, buckets, sessions, onProgress) {
5034
+ sendIngest(device, buckets, sessions, onProgress, options) {
4878
5035
  return new Promise((resolve4, reject) => {
4879
5036
  const url = new URL2("/api/usage/ingest", this.apiUrl);
4880
5037
  const body = Buffer.from(
4881
- JSON.stringify({
4882
- schemaVersion: 2,
4883
- device,
4884
- buckets,
4885
- sessions: sessions ?? []
4886
- })
5038
+ JSON.stringify(buildIngestPayload(device, buckets, sessions, options))
4887
5039
  );
4888
5040
  const totalBytes = body.length;
4889
5041
  const mod = url.protocol === "https:" ? https : http;
@@ -5056,7 +5208,7 @@ var ApiClient = class {
5056
5208
  // src/infrastructure/runtime/lock.ts
5057
5209
  import {
5058
5210
  closeSync,
5059
- existsSync as existsSync22,
5211
+ existsSync as existsSync23,
5060
5212
  openSync,
5061
5213
  readFileSync as readFileSync10,
5062
5214
  rmSync as rmSync3,
@@ -5065,22 +5217,22 @@ import {
5065
5217
 
5066
5218
  // src/infrastructure/runtime/paths.ts
5067
5219
  import { mkdirSync as mkdirSync2 } from "fs";
5068
- import { join as join25 } from "path";
5220
+ import { join as join26 } from "path";
5069
5221
  var APP_NAME = "tokenarena";
5070
5222
  function getRuntimeDirPath() {
5071
- return join25(getRuntimeDir(), APP_NAME);
5223
+ return join26(getRuntimeDir(), APP_NAME);
5072
5224
  }
5073
5225
  function getStateDir() {
5074
- return join25(getStateHome(), APP_NAME);
5226
+ return join26(getStateHome(), APP_NAME);
5075
5227
  }
5076
5228
  function getSyncLockPath() {
5077
- return join25(getRuntimeDirPath(), "sync.lock");
5229
+ return join26(getRuntimeDirPath(), "sync.lock");
5078
5230
  }
5079
5231
  function getSyncStatePath() {
5080
- return join25(getStateDir(), "status.json");
5232
+ return join26(getStateDir(), "status.json");
5081
5233
  }
5082
5234
  function getUploadManifestPath() {
5083
- return join25(getStateDir(), "upload-manifest.json");
5235
+ return join26(getStateDir(), "upload-manifest.json");
5084
5236
  }
5085
5237
  function ensureAppDirs() {
5086
5238
  mkdirSync2(getRuntimeDirPath(), { recursive: true });
@@ -5098,7 +5250,7 @@ function isProcessAlive(pid) {
5098
5250
  }
5099
5251
  }
5100
5252
  function readLockMetadata(lockPath) {
5101
- if (!existsSync22(lockPath)) {
5253
+ if (!existsSync23(lockPath)) {
5102
5254
  return null;
5103
5255
  }
5104
5256
  try {
@@ -5172,13 +5324,13 @@ function describeExistingSyncLock() {
5172
5324
  }
5173
5325
 
5174
5326
  // src/infrastructure/runtime/state.ts
5175
- import { existsSync as existsSync23, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
5327
+ import { existsSync as existsSync24, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
5176
5328
  function getDefaultState() {
5177
5329
  return { status: "idle" };
5178
5330
  }
5179
5331
  function loadSyncState() {
5180
5332
  const path = getSyncStatePath();
5181
- if (!existsSync23(path)) {
5333
+ if (!existsSync24(path)) {
5182
5334
  return getDefaultState();
5183
5335
  }
5184
5336
  try {
@@ -5239,7 +5391,7 @@ function markSyncFailed(source, error, status) {
5239
5391
  }
5240
5392
 
5241
5393
  // src/infrastructure/runtime/upload-manifest.ts
5242
- import { existsSync as existsSync24, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "fs";
5394
+ import { existsSync as existsSync25, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "fs";
5243
5395
  function isRecordOfStrings(value) {
5244
5396
  if (!value || typeof value !== "object" || Array.isArray(value)) {
5245
5397
  return false;
@@ -5255,7 +5407,7 @@ function isUploadManifest(value) {
5255
5407
  }
5256
5408
  function loadUploadManifest() {
5257
5409
  const path = getUploadManifestPath();
5258
- if (!existsSync24(path)) {
5410
+ if (!existsSync25(path)) {
5259
5411
  return null;
5260
5412
  }
5261
5413
  try {
@@ -5316,6 +5468,7 @@ function getDetectedTools() {
5316
5468
  // src/services/sync-service.ts
5317
5469
  var BATCH_SIZE = 100;
5318
5470
  var SESSION_BATCH_SIZE = 500;
5471
+ var MAX_INGEST_PAYLOAD_BYTES = 8 * 1024 * 1024;
5319
5472
  var PROGRESS_BAR_WIDTH = 28;
5320
5473
  function formatBytes(bytes) {
5321
5474
  if (bytes < 1024) return `${bytes}B`;
@@ -5327,6 +5480,9 @@ function renderProgressBar(progress) {
5327
5480
  const filled = Math.round(safeProgress * PROGRESS_BAR_WIDTH);
5328
5481
  return `${"\u2588".repeat(filled)}${"\u2591".repeat(PROGRESS_BAR_WIDTH - filled)}`;
5329
5482
  }
5483
+ function shouldSyncAchievementsForBatch(batchIndex, totalBatches) {
5484
+ return batchIndex === totalBatches - 1;
5485
+ }
5330
5486
  function writeUploadProgress(sent, total, batchNum, totalBatches) {
5331
5487
  const pct = total > 0 ? Math.round(sent / total * 100) : 100;
5332
5488
  const progressBar = renderProgressBar(total > 0 ? sent / total : 1);
@@ -5456,6 +5612,64 @@ var SyncFailure = class extends Error {
5456
5612
  kind;
5457
5613
  causeError;
5458
5614
  };
5615
+ function splitOversizedUploadBatch(device, buckets, sessions) {
5616
+ const payloadSize = getIngestPayloadSize(device, buckets, sessions, {
5617
+ syncAchievements: true
5618
+ });
5619
+ if (payloadSize <= MAX_INGEST_PAYLOAD_BYTES) {
5620
+ return [{ buckets, sessions }];
5621
+ }
5622
+ if (buckets.length + sessions.length <= 1) {
5623
+ throw new SyncFailure(
5624
+ `A single usage record exceeds the ${formatBytes(MAX_INGEST_PAYLOAD_BYTES)} ingest payload limit. Reduce the record size before syncing.`,
5625
+ "error"
5626
+ );
5627
+ }
5628
+ if (sessions.length > 1 && sessions.length >= buckets.length) {
5629
+ const midpoint = Math.ceil(sessions.length / 2);
5630
+ return [
5631
+ ...splitOversizedUploadBatch(
5632
+ device,
5633
+ buckets,
5634
+ sessions.slice(0, midpoint)
5635
+ ),
5636
+ ...splitOversizedUploadBatch(device, [], sessions.slice(midpoint))
5637
+ ];
5638
+ }
5639
+ if (buckets.length > 1) {
5640
+ const midpoint = Math.ceil(buckets.length / 2);
5641
+ return [
5642
+ ...splitOversizedUploadBatch(
5643
+ device,
5644
+ buckets.slice(0, midpoint),
5645
+ sessions
5646
+ ),
5647
+ ...splitOversizedUploadBatch(device, buckets.slice(midpoint), [])
5648
+ ];
5649
+ }
5650
+ return [
5651
+ ...splitOversizedUploadBatch(device, buckets, []),
5652
+ ...splitOversizedUploadBatch(device, [], sessions)
5653
+ ];
5654
+ }
5655
+ function buildUploadBatches(device, buckets, sessions) {
5656
+ const batches = [];
5657
+ let bucketOffset = 0;
5658
+ let sessionOffset = 0;
5659
+ while (bucketOffset < buckets.length || sessionOffset < sessions.length) {
5660
+ const batchBuckets = buckets.slice(bucketOffset, bucketOffset + BATCH_SIZE);
5661
+ const batchSessions = sessions.slice(
5662
+ sessionOffset,
5663
+ sessionOffset + SESSION_BATCH_SIZE
5664
+ );
5665
+ batches.push(
5666
+ ...splitOversizedUploadBatch(device, batchBuckets, batchSessions)
5667
+ );
5668
+ bucketOffset += batchBuckets.length;
5669
+ sessionOffset += batchSessions.length;
5670
+ }
5671
+ return batches;
5672
+ }
5459
5673
  async function runSync(config, opts = {}) {
5460
5674
  const { quiet = false, source = "manual", throws = false } = opts;
5461
5675
  const lock = tryAcquireSyncLock(source);
@@ -5596,24 +5810,19 @@ async function runSync(config, opts = {}) {
5596
5810
  markSyncSucceeded(source, { buckets: 0, sessions: 0 });
5597
5811
  return { buckets: 0, sessions: 0 };
5598
5812
  }
5599
- const bucketBatches = Math.ceil(changedBuckets.length / BATCH_SIZE);
5600
- const sessionBatches = Math.ceil(
5601
- changedSessions.length / SESSION_BATCH_SIZE
5813
+ const uploadBatches = buildUploadBatches(
5814
+ device,
5815
+ changedBuckets,
5816
+ changedSessions
5602
5817
  );
5603
- const totalBatches = Math.max(bucketBatches, sessionBatches, 1);
5604
- const batchPayloadSizes = Array.from(
5605
- { length: totalBatches },
5606
- (_, batchIdx) => getIngestPayloadSize(
5607
- device,
5608
- changedBuckets.slice(
5609
- batchIdx * BATCH_SIZE,
5610
- (batchIdx + 1) * BATCH_SIZE
5611
- ),
5612
- changedSessions.slice(
5613
- batchIdx * SESSION_BATCH_SIZE,
5614
- (batchIdx + 1) * SESSION_BATCH_SIZE
5818
+ const totalBatches = uploadBatches.length;
5819
+ const batchPayloadSizes = uploadBatches.map(
5820
+ (batch, batchIdx) => getIngestPayloadSize(device, batch.buckets, batch.sessions, {
5821
+ syncAchievements: shouldSyncAchievementsForBatch(
5822
+ batchIdx,
5823
+ totalBatches
5615
5824
  )
5616
- )
5825
+ })
5617
5826
  );
5618
5827
  const totalPayloadBytes = batchPayloadSizes.reduce(
5619
5828
  (sum, size) => sum + size,
@@ -5640,14 +5849,11 @@ async function runSync(config, opts = {}) {
5640
5849
  );
5641
5850
  }
5642
5851
  for (let batchIdx = 0; batchIdx < totalBatches; batchIdx++) {
5643
- const batch = changedBuckets.slice(
5644
- batchIdx * BATCH_SIZE,
5645
- (batchIdx + 1) * BATCH_SIZE
5646
- );
5647
- const batchSessions = changedSessions.slice(
5648
- batchIdx * SESSION_BATCH_SIZE,
5649
- (batchIdx + 1) * SESSION_BATCH_SIZE
5650
- );
5852
+ const uploadBatch = uploadBatches[batchIdx];
5853
+ if (!uploadBatch) {
5854
+ throw new SyncFailure("Upload batch planning failed.", "error");
5855
+ }
5856
+ const { buckets: batch, sessions: batchSessions } = uploadBatch;
5651
5857
  const batchNum = batchIdx + 1;
5652
5858
  const result = await apiClient.ingest(
5653
5859
  device,
@@ -5660,6 +5866,12 @@ async function runSync(config, opts = {}) {
5660
5866
  batchNum,
5661
5867
  totalBatches
5662
5868
  );
5869
+ },
5870
+ {
5871
+ syncAchievements: shouldSyncAchievementsForBatch(
5872
+ batchIdx,
5873
+ totalBatches
5874
+ )
5663
5875
  }
5664
5876
  );
5665
5877
  totalIngested += result.ingested ?? batch.length;
@@ -5735,18 +5947,18 @@ View your dashboard at: ${apiUrl}/usage`);
5735
5947
 
5736
5948
  // src/commands/init.ts
5737
5949
  import { execFileSync as execFileSync7, spawn } from "child_process";
5738
- import { existsSync as existsSync27 } from "fs";
5950
+ import { existsSync as existsSync28 } from "fs";
5739
5951
  import { appendFile, mkdir, readFile } from "fs/promises";
5740
- import { homedir as homedir25, platform as platform5 } from "os";
5741
- import { dirname as dirname6, join as join26, posix as posix3, win32 } from "path";
5952
+ import { homedir as homedir26, platform as platform5 } from "os";
5953
+ import { dirname as dirname6, join as join27, posix as posix3, win32 } from "path";
5742
5954
 
5743
5955
  // src/infrastructure/service/index.ts
5744
5956
  import { platform as platform4 } from "os";
5745
5957
 
5746
5958
  // src/infrastructure/service/linux-systemd.ts
5747
5959
  import { execFileSync as execFileSync5 } from "child_process";
5748
- import { existsSync as existsSync25, mkdirSync as mkdirSync3, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
5749
- import { homedir as homedir23, platform as platform2 } from "os";
5960
+ import { existsSync as existsSync26, mkdirSync as mkdirSync3, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
5961
+ import { homedir as homedir24, platform as platform2 } from "os";
5750
5962
  import { posix } from "path";
5751
5963
 
5752
5964
  // src/utils/command.ts
@@ -5815,10 +6027,10 @@ function escapeXml(value) {
5815
6027
 
5816
6028
  // src/infrastructure/service/linux-systemd.ts
5817
6029
  var SYSTEMD_SERVICE_NAME = "tokenarena";
5818
- function getLinuxSystemdServiceDir(homePath = homedir23()) {
6030
+ function getLinuxSystemdServiceDir(homePath = homedir24()) {
5819
6031
  return posix.join(homePath, ".config", "systemd", "user");
5820
6032
  }
5821
- function getLinuxSystemdServiceFile(homePath = homedir23()) {
6033
+ function getLinuxSystemdServiceFile(homePath = homedir24()) {
5822
6034
  return posix.join(
5823
6035
  getLinuxSystemdServiceDir(homePath),
5824
6036
  `${SYSTEMD_SERVICE_NAME}.service`
@@ -5875,7 +6087,7 @@ function ensureSystemdAvailable() {
5875
6087
  }
5876
6088
  function createLinuxSystemdServiceBackend() {
5877
6089
  function isInstalled() {
5878
- return existsSync25(getLinuxSystemdServiceFile());
6090
+ return existsSync26(getLinuxSystemdServiceFile());
5879
6091
  }
5880
6092
  async function setup(skipPrompt = false) {
5881
6093
  if (!ensureSystemdAvailable()) {
@@ -6000,7 +6212,7 @@ function createLinuxSystemdServiceBackend() {
6000
6212
  }
6001
6213
  async function uninstall(skipPrompt = false) {
6002
6214
  const serviceFile = getLinuxSystemdServiceFile();
6003
- if (!existsSync25(serviceFile)) {
6215
+ if (!existsSync26(serviceFile)) {
6004
6216
  logger.info(formatBullet("\u670D\u52A1\u6587\u4EF6\u4E0D\u5B58\u5728\u3002", "warning"));
6005
6217
  return;
6006
6218
  }
@@ -6061,17 +6273,17 @@ function createLinuxSystemdServiceBackend() {
6061
6273
 
6062
6274
  // src/infrastructure/service/macos-launchd.ts
6063
6275
  import { execFileSync as execFileSync6 } from "child_process";
6064
- import { existsSync as existsSync26, mkdirSync as mkdirSync4, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "fs";
6065
- import { homedir as homedir24, platform as platform3 } from "os";
6276
+ import { existsSync as existsSync27, mkdirSync as mkdirSync4, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "fs";
6277
+ import { homedir as homedir25, platform as platform3 } from "os";
6066
6278
  import { posix as posix2 } from "path";
6067
6279
  var MACOS_LAUNCHD_LABEL = "com.guji.tokenarena";
6068
6280
  function getCurrentUid() {
6069
6281
  return typeof process.getuid === "function" ? process.getuid() : null;
6070
6282
  }
6071
- function getMacosLaunchAgentDir(homePath = homedir24()) {
6283
+ function getMacosLaunchAgentDir(homePath = homedir25()) {
6072
6284
  return posix2.join(homePath, "Library", "LaunchAgents");
6073
6285
  }
6074
- function getMacosLaunchAgentFile(homePath = homedir24()) {
6286
+ function getMacosLaunchAgentFile(homePath = homedir25()) {
6075
6287
  return posix2.join(
6076
6288
  getMacosLaunchAgentDir(homePath),
6077
6289
  `${MACOS_LAUNCHD_LABEL}.plist`
@@ -6198,7 +6410,7 @@ function writeLaunchAgentPlist() {
6198
6410
  label: MACOS_LAUNCHD_LABEL,
6199
6411
  programArguments: [command.execPath, ...command.args],
6200
6412
  environment: getManagedServiceEnvironment(),
6201
- workingDirectory: homedir24(),
6413
+ workingDirectory: homedir25(),
6202
6414
  standardOutPath: stdoutPath,
6203
6415
  standardErrorPath: stderrPath
6204
6416
  });
@@ -6223,7 +6435,7 @@ function bootstrapLaunchAgent() {
6223
6435
  }
6224
6436
  function createMacosLaunchdServiceBackend() {
6225
6437
  function isInstalled() {
6226
- return existsSync26(getMacosLaunchAgentFile());
6438
+ return existsSync27(getMacosLaunchAgentFile());
6227
6439
  }
6228
6440
  async function setup(skipPrompt = false) {
6229
6441
  if (!ensureLaunchctlAvailable()) {
@@ -6364,7 +6576,7 @@ function createMacosLaunchdServiceBackend() {
6364
6576
  }
6365
6577
  async function uninstall(skipPrompt = false) {
6366
6578
  const plistFile = getMacosLaunchAgentFile();
6367
- if (!existsSync26(plistFile)) {
6579
+ if (!existsSync27(plistFile)) {
6368
6580
  logger.info(formatBullet("\u670D\u52A1\u6587\u4EF6\u4E0D\u5B58\u5728\u3002", "warning"));
6369
6581
  return;
6370
6582
  }
@@ -6475,7 +6687,7 @@ function resolvePowerShellProfilePath() {
6475
6687
  const systemRoot = process.env.SYSTEMROOT || "C:\\Windows";
6476
6688
  const candidates = [
6477
6689
  "pwsh.exe",
6478
- join26(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
6690
+ join27(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
6479
6691
  ];
6480
6692
  for (const command of candidates) {
6481
6693
  try {
@@ -6504,8 +6716,8 @@ function resolvePowerShellProfilePath() {
6504
6716
  function resolveShellAliasSetup(options = {}) {
6505
6717
  const currentPlatform = options.currentPlatform ?? platform5();
6506
6718
  const env = options.env ?? process.env;
6507
- const homeDir = options.homeDir ?? homedir25();
6508
- const pathExists = options.exists ?? existsSync27;
6719
+ const homeDir = options.homeDir ?? homedir26();
6720
+ const pathExists = options.exists ?? existsSync28;
6509
6721
  const shellFromEnv = env.SHELL ? basenameLikeShell(env.SHELL).toLowerCase() : "";
6510
6722
  const shellName = shellFromEnv || (currentPlatform === "win32" ? "powershell" : "");
6511
6723
  const aliasName = "ta";
@@ -6702,7 +6914,7 @@ async function setupShellAlias() {
6702
6914
  try {
6703
6915
  await mkdir(dirname6(setup.configFile), { recursive: true });
6704
6916
  let existingContent = "";
6705
- if (existsSync27(setup.configFile)) {
6917
+ if (existsSync28(setup.configFile)) {
6706
6918
  existingContent = await readFile(setup.configFile, "utf-8");
6707
6919
  }
6708
6920
  const normalizedContent = existingContent.toLowerCase();
@@ -6961,7 +7173,7 @@ function buildLocalUsageDashboardData(input2) {
6961
7173
 
6962
7174
  // src/infrastructure/runtime/cli-version.ts
6963
7175
  import { readFileSync as readFileSync13 } from "fs";
6964
- import { dirname as dirname7, join as join27 } from "path";
7176
+ import { dirname as dirname7, join as join28 } from "path";
6965
7177
  import { fileURLToPath } from "url";
6966
7178
  var FALLBACK_VERSION = "0.0.0";
6967
7179
  var cachedVersion;
@@ -6969,7 +7181,7 @@ function getCliVersion(metaUrl = import.meta.url) {
6969
7181
  if (cachedVersion) {
6970
7182
  return cachedVersion;
6971
7183
  }
6972
- const packageJsonPath = join27(
7184
+ const packageJsonPath = join28(
6973
7185
  dirname7(fileURLToPath(metaUrl)),
6974
7186
  "..",
6975
7187
  "package.json"
@@ -7380,8 +7592,8 @@ async function runSyncCommand(opts = {}) {
7380
7592
  }
7381
7593
 
7382
7594
  // src/commands/uninstall.ts
7383
- import { existsSync as existsSync28, readFileSync as readFileSync14, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "fs";
7384
- import { homedir as homedir26, platform as platform6 } from "os";
7595
+ import { existsSync as existsSync29, readFileSync as readFileSync14, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "fs";
7596
+ import { homedir as homedir27, platform as platform6 } from "os";
7385
7597
  function removeShellAlias() {
7386
7598
  const shell = process.env.SHELL;
7387
7599
  if (!shell) return;
@@ -7390,22 +7602,22 @@ function removeShellAlias() {
7390
7602
  let configFile;
7391
7603
  switch (shellName) {
7392
7604
  case "zsh":
7393
- configFile = `${homedir26()}/.zshrc`;
7605
+ configFile = `${homedir27()}/.zshrc`;
7394
7606
  break;
7395
7607
  case "bash":
7396
- if (platform6() === "darwin" && existsSync28(`${homedir26()}/.bash_profile`)) {
7397
- configFile = `${homedir26()}/.bash_profile`;
7608
+ if (platform6() === "darwin" && existsSync29(`${homedir27()}/.bash_profile`)) {
7609
+ configFile = `${homedir27()}/.bash_profile`;
7398
7610
  } else {
7399
- configFile = `${homedir26()}/.bashrc`;
7611
+ configFile = `${homedir27()}/.bashrc`;
7400
7612
  }
7401
7613
  break;
7402
7614
  case "fish":
7403
- configFile = `${homedir26()}/.config/fish/config.fish`;
7615
+ configFile = `${homedir27()}/.config/fish/config.fish`;
7404
7616
  break;
7405
7617
  default:
7406
7618
  return;
7407
7619
  }
7408
- if (!existsSync28(configFile)) return;
7620
+ if (!existsSync29(configFile)) return;
7409
7621
  try {
7410
7622
  let content = readFileSync14(configFile, "utf-8");
7411
7623
  const aliasPatterns = [
@@ -7444,7 +7656,7 @@ async function runUninstall() {
7444
7656
  const runtimeDir = getRuntimeDirPath();
7445
7657
  const serviceBackend = getServiceBackend();
7446
7658
  const hasInstalledService = serviceBackend?.isInstalled() ?? false;
7447
- const hasLocalArtifacts = existsSync28(configPath) || existsSync28(configDir) || existsSync28(stateDir) || existsSync28(runtimeDir) || hasInstalledService;
7659
+ const hasLocalArtifacts = existsSync29(configPath) || existsSync29(configDir) || existsSync29(stateDir) || existsSync29(runtimeDir) || hasInstalledService;
7448
7660
  if (!hasLocalArtifacts) {
7449
7661
  logger.info(formatHeader("\u5378\u8F7D TokenArena"));
7450
7662
  logger.info(formatBullet("\u672A\u53D1\u73B0\u672C\u5730\u914D\u7F6E\uFF0C\u65E0\u9700\u5378\u8F7D\u3002"));
@@ -7488,22 +7700,22 @@ async function runUninstall() {
7488
7700
  }
7489
7701
  }
7490
7702
  logger.info(formatSection("\u6267\u884C\u7ED3\u679C"));
7491
- if (existsSync28(configPath)) {
7703
+ if (existsSync29(configPath)) {
7492
7704
  deleteConfig();
7493
7705
  logger.info(formatBullet("\u5DF2\u5220\u9664\u914D\u7F6E\u6587\u4EF6\u3002", "success"));
7494
7706
  }
7495
- if (existsSync28(configDir)) {
7707
+ if (existsSync29(configDir)) {
7496
7708
  try {
7497
7709
  rmSync6(configDir, { recursive: false, force: true });
7498
7710
  logger.info(formatBullet("\u5DF2\u5220\u9664\u914D\u7F6E\u76EE\u5F55\u3002", "success"));
7499
7711
  } catch {
7500
7712
  }
7501
7713
  }
7502
- if (existsSync28(stateDir)) {
7714
+ if (existsSync29(stateDir)) {
7503
7715
  rmSync6(stateDir, { recursive: true, force: true });
7504
7716
  logger.info(formatBullet("\u5DF2\u5220\u9664\u72B6\u6001\u6570\u636E\u3002", "success"));
7505
7717
  }
7506
- if (existsSync28(runtimeDir)) {
7718
+ if (existsSync29(runtimeDir)) {
7507
7719
  rmSync6(runtimeDir, { recursive: true, force: true });
7508
7720
  logger.info(formatBullet("\u5DF2\u5220\u9664\u8FD0\u884C\u65F6\u6570\u636E\u3002", "success"));
7509
7721
  }
@@ -7734,7 +7946,7 @@ function createCli() {
7734
7946
  }
7735
7947
 
7736
7948
  // src/infrastructure/runtime/main-module.ts
7737
- import { existsSync as existsSync29, realpathSync as realpathSync2 } from "fs";
7949
+ import { existsSync as existsSync30, realpathSync as realpathSync2 } from "fs";
7738
7950
  import { resolve as resolve3 } from "path";
7739
7951
  import { fileURLToPath as fileURLToPath2 } from "url";
7740
7952
  function isMainModule(argvEntry = process.argv[1], metaUrl = import.meta.url) {
@@ -7745,7 +7957,7 @@ function isMainModule(argvEntry = process.argv[1], metaUrl = import.meta.url) {
7745
7957
  try {
7746
7958
  return realpathSync2(argvEntry) === realpathSync2(currentModulePath);
7747
7959
  } catch {
7748
- if (!existsSync29(argvEntry)) {
7960
+ if (!existsSync30(argvEntry)) {
7749
7961
  return false;
7750
7962
  }
7751
7963
  return resolve3(argvEntry) === resolve3(currentModulePath);