@yishiguji/tokenarena 0.9.0 → 0.11.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
@@ -4282,37 +4282,616 @@ var ZCodeParser = class {
4282
4282
  };
4283
4283
  registerParser(new ZCodeParser());
4284
4284
 
4285
+ // src/parsers/qodercli.ts
4286
+ import { existsSync as existsSync22, readdirSync as readdirSync12 } from "fs";
4287
+ import { homedir as homedir23 } from "os";
4288
+ import { basename as basename9, join as join24 } from "path";
4289
+ var TOOL_ID15 = "qodercli";
4290
+ var TOOL_NAME15 = "Qoder CLI";
4291
+ var DEFAULT_PROJECTS_DIR = join24(homedir23(), ".qoder", "projects");
4292
+ var DEFAULT_LOGS_DIR = join24(homedir23(), ".qoder", "logs", "sessions");
4293
+ var DEFAULT_RUNS_DIR = join24(homedir23(), ".qoder", "logs", "runs");
4294
+ var CLI_ENTRYPOINT = "cli";
4295
+ var IDE_ENTRYPOINT = "acp";
4296
+ var CREDIT_MODEL_FALLBACK = "credits";
4297
+ function createToolDefinition12(projectsDir) {
4298
+ return {
4299
+ id: TOOL_ID15,
4300
+ name: TOOL_NAME15,
4301
+ dataDir: projectsDir
4302
+ };
4303
+ }
4304
+ function toSafeNumber12(value) {
4305
+ const numberValue = Number(value);
4306
+ return Number.isFinite(numberValue) ? numberValue : 0;
4307
+ }
4308
+ function normalizeForPrefix5(value) {
4309
+ return value.replace(/\\/g, "/").replace(/\/+$/, "");
4310
+ }
4311
+ function extractQoderProject(filePath, projectsDir) {
4312
+ const normalizedFilePath = normalizeForPrefix5(filePath);
4313
+ const normalizedProjectsDir = normalizeForPrefix5(projectsDir);
4314
+ const prefix = `${normalizedProjectsDir}/`;
4315
+ if (!normalizedFilePath.startsWith(prefix)) return "unknown";
4316
+ const relative = normalizedFilePath.slice(prefix.length);
4317
+ const firstSeg = relative.split("/")[0];
4318
+ if (!firstSeg) return "unknown";
4319
+ const parts = firstSeg.split("-").filter(Boolean);
4320
+ return parts.length > 0 ? parts[parts.length - 1] ?? "unknown" : "unknown";
4321
+ }
4322
+ function classifyQoderEntrypoint(content) {
4323
+ let hasCli = false;
4324
+ let hasAcp = false;
4325
+ for (const line of content.split("\n")) {
4326
+ if (!line.trim()) continue;
4327
+ try {
4328
+ const obj = JSON.parse(line);
4329
+ if (obj.entrypoint === CLI_ENTRYPOINT) hasCli = true;
4330
+ else if (obj.entrypoint === IDE_ENTRYPOINT) hasAcp = true;
4331
+ } catch {
4332
+ }
4333
+ if (hasCli) return "cli";
4334
+ }
4335
+ if (hasAcp) return "acp";
4336
+ return "unknown";
4337
+ }
4338
+ function isQodercliBinaryPresent() {
4339
+ const home = homedir23();
4340
+ const candidates = [
4341
+ join24(home, ".local", "bin", "qodercli"),
4342
+ join24(home, ".qoder", "bin", "qodercli"),
4343
+ join24(home, ".qoder-cli")
4344
+ ];
4345
+ return candidates.some((path) => existsSync22(path));
4346
+ }
4347
+ function projectFromEncodedSlug(slug) {
4348
+ const parts = slug.split("-").filter(Boolean);
4349
+ return parts.length > 0 ? parts[parts.length - 1] ?? "unknown" : "unknown";
4350
+ }
4351
+ function projectFromCwd(cwd) {
4352
+ if (!cwd) return "unknown";
4353
+ const leaf = basename9(cwd.replace(/[\\/]+$/, ""));
4354
+ return leaf || "unknown";
4355
+ }
4356
+ function totalCreditsUsed(quota) {
4357
+ return toSafeNumber12(quota.userQuota?.used) + toSafeNumber12(quota.orgResourcePackage?.used) + toSafeNumber12(quota.addOnQuota?.used);
4358
+ }
4359
+ function parseRunManifest(content) {
4360
+ if (!content) return null;
4361
+ try {
4362
+ return JSON.parse(content);
4363
+ } catch {
4364
+ return null;
4365
+ }
4366
+ }
4367
+ function extractCreditSnapshotsFromLog(logContent, meta) {
4368
+ const snapshots = [];
4369
+ let sessionId = meta.defaultSessionId || meta.runId;
4370
+ let model = meta.defaultModel || CREDIT_MODEL_FALLBACK;
4371
+ for (const line of logContent.split("\n")) {
4372
+ if (!line.trim()) continue;
4373
+ if (line.includes("session.config.loaded")) {
4374
+ const sessionMatch = line.match(/session=([^\s\]]+)/);
4375
+ if (sessionMatch?.[1]) sessionId = sessionMatch[1];
4376
+ const modelMatch = line.match(/model="([^"]+)"/);
4377
+ if (modelMatch?.[1]) model = modelMatch[1];
4378
+ }
4379
+ if (!line.includes("quota/usage response:")) continue;
4380
+ const tsMatch = line.match(/^(\d{4}-\d{2}-\d{2}T[^\s]+)/);
4381
+ const jsonMatch = line.match(/response:\s+(\{.*\})\s*$/);
4382
+ if (!tsMatch?.[1] || !jsonMatch?.[1]) continue;
4383
+ let quota;
4384
+ try {
4385
+ quota = JSON.parse(jsonMatch[1]);
4386
+ } catch {
4387
+ continue;
4388
+ }
4389
+ const timestamp = new Date(tsMatch[1]);
4390
+ if (Number.isNaN(timestamp.getTime())) continue;
4391
+ snapshots.push({
4392
+ timestamp,
4393
+ // credits, not tokens
4394
+ totalUsed: totalCreditsUsed(quota),
4395
+ project: meta.project,
4396
+ sessionId,
4397
+ model,
4398
+ runId: meta.runId
4399
+ });
4400
+ }
4401
+ return snapshots;
4402
+ }
4403
+ function creditSnapshotsToEntries(snapshots) {
4404
+ if (snapshots.length === 0) return [];
4405
+ const sorted = [...snapshots].sort(
4406
+ (a, b) => a.timestamp.getTime() - b.timestamp.getTime()
4407
+ );
4408
+ const entries = [];
4409
+ let previousUsed = null;
4410
+ for (const snap of sorted) {
4411
+ if (previousUsed !== null) {
4412
+ const creditDelta = snap.totalUsed - previousUsed;
4413
+ if (creditDelta > 0) {
4414
+ entries.push({
4415
+ sessionId: snap.sessionId,
4416
+ source: TOOL_ID15,
4417
+ model: snap.model || CREDIT_MODEL_FALLBACK,
4418
+ project: snap.project,
4419
+ timestamp: snap.timestamp,
4420
+ // CREDIT counts (not tokens) — see file header.
4421
+ inputTokens: creditDelta,
4422
+ outputTokens: 0,
4423
+ reasoningTokens: 0,
4424
+ cachedTokens: 0
4425
+ });
4426
+ }
4427
+ }
4428
+ previousUsed = snap.totalUsed;
4429
+ }
4430
+ return entries;
4431
+ }
4432
+ var QoderCliParser = class {
4433
+ constructor(projectsDir = DEFAULT_PROJECTS_DIR, logsDir = DEFAULT_LOGS_DIR, runsDir = DEFAULT_RUNS_DIR) {
4434
+ this.projectsDir = projectsDir;
4435
+ this.logsDir = logsDir;
4436
+ this.runsDir = runsDir;
4437
+ this.tool = createToolDefinition12(projectsDir);
4438
+ }
4439
+ projectsDir;
4440
+ logsDir;
4441
+ runsDir;
4442
+ tool;
4443
+ async parse() {
4444
+ const entries = [];
4445
+ const sessionEvents = [];
4446
+ this.parseProjectSessions(sessionEvents);
4447
+ entries.push(...this.parseCreditDeltas());
4448
+ return {
4449
+ buckets: aggregateToBuckets(entries),
4450
+ sessions: extractSessions(sessionEvents, entries)
4451
+ };
4452
+ }
4453
+ parseProjectSessions(sessionEvents) {
4454
+ if (!existsSync22(this.projectsDir)) return;
4455
+ for (const filePath of findJsonlFiles(this.projectsDir)) {
4456
+ if (basename9(filePath).startsWith("verified-")) continue;
4457
+ const content = readFileSafe(filePath);
4458
+ if (!content) continue;
4459
+ if (classifyQoderEntrypoint(content) !== CLI_ENTRYPOINT) continue;
4460
+ const project = extractQoderProject(filePath, this.projectsDir);
4461
+ const sessionId = extractSessionId(filePath);
4462
+ for (const line of content.split("\n")) {
4463
+ if (!line.trim()) continue;
4464
+ try {
4465
+ const obj = JSON.parse(line);
4466
+ if (obj.entrypoint === IDE_ENTRYPOINT) continue;
4467
+ if (obj.type !== "user" && obj.type !== "assistant") continue;
4468
+ const timestamp = obj.timestamp;
4469
+ if (timestamp == null) continue;
4470
+ const ts = new Date(
4471
+ typeof timestamp === "number" && timestamp < 1e12 ? timestamp * 1e3 : timestamp
4472
+ );
4473
+ if (Number.isNaN(ts.getTime())) continue;
4474
+ sessionEvents.push({
4475
+ sessionId,
4476
+ source: TOOL_ID15,
4477
+ project,
4478
+ timestamp: ts,
4479
+ role: obj.type === "user" ? "user" : "assistant"
4480
+ });
4481
+ } catch {
4482
+ }
4483
+ }
4484
+ }
4485
+ }
4486
+ /**
4487
+ * Parse credit consumption from run logs.
4488
+ * There is no token data here — only quota/usage credit totals.
4489
+ */
4490
+ parseCreditDeltas() {
4491
+ if (!existsSync22(this.runsDir)) return [];
4492
+ let runDirs;
4493
+ try {
4494
+ runDirs = readdirSync12(this.runsDir, { withFileTypes: true }).filter(
4495
+ (d) => d.isDirectory()
4496
+ );
4497
+ } catch {
4498
+ return [];
4499
+ }
4500
+ const allSnapshots = [];
4501
+ for (const dir of runDirs) {
4502
+ const runPath = join24(this.runsDir, dir.name);
4503
+ const logPath = join24(runPath, "qodercli.log");
4504
+ if (!existsSync22(logPath)) continue;
4505
+ const logContent = readFileSafe(logPath);
4506
+ if (!logContent?.includes("quota/usage response:")) {
4507
+ continue;
4508
+ }
4509
+ const manifest = parseRunManifest(
4510
+ readFileSafe(join24(runPath, "manifest.json"))
4511
+ );
4512
+ const project = manifest?.project_id ? projectFromEncodedSlug(manifest.project_id) : projectFromCwd(manifest?.cwd);
4513
+ const snapshots = extractCreditSnapshotsFromLog(logContent, {
4514
+ project,
4515
+ runId: manifest?.run_id || dir.name
4516
+ });
4517
+ allSnapshots.push(...snapshots);
4518
+ }
4519
+ return creditSnapshotsToEntries(allSnapshots);
4520
+ }
4521
+ isInstalled() {
4522
+ return isQodercliBinaryPresent() || existsSync22(this.runsDir) || existsSync22(this.logsDir) || existsSync22(this.projectsDir);
4523
+ }
4524
+ };
4525
+ registerParser(new QoderCliParser());
4526
+
4527
+ // src/parsers/grok-build.ts
4528
+ import { existsSync as existsSync23, readdirSync as readdirSync13 } from "fs";
4529
+ import { homedir as homedir24 } from "os";
4530
+ import { basename as basename10, join as join25 } from "path";
4531
+ var TOOL_ID16 = "grok-build";
4532
+ var TOOL_NAME16 = "Grok Build";
4533
+ var DEFAULT_DATA_DIR7 = join25(homedir24(), ".grok", "sessions");
4534
+ function createToolDefinition13(dataDir) {
4535
+ return {
4536
+ id: TOOL_ID16,
4537
+ name: TOOL_NAME16,
4538
+ dataDir
4539
+ };
4540
+ }
4541
+ function toSafeNumber13(value) {
4542
+ const numberValue = Number(value);
4543
+ return Number.isFinite(numberValue) ? numberValue : 0;
4544
+ }
4545
+ function projectFromEncodedCwd(encoded) {
4546
+ try {
4547
+ const decoded = decodeURIComponent(encoded);
4548
+ const leaf = basename10(decoded.replace(/[\\/]+$/, ""));
4549
+ return leaf || "unknown";
4550
+ } catch {
4551
+ const leaf = basename10(encoded);
4552
+ return leaf || "unknown";
4553
+ }
4554
+ }
4555
+ function resolveTimestamp(obj) {
4556
+ const ms = obj._meta?.agentTimestampMs ?? obj.params?.update?._meta?.agentTimestampMs;
4557
+ if (typeof ms === "number" && Number.isFinite(ms)) {
4558
+ return new Date(ms);
4559
+ }
4560
+ const raw = obj.timestamp;
4561
+ if (raw == null) return null;
4562
+ if (typeof raw === "number") {
4563
+ return new Date(raw < 1e12 ? raw * 1e3 : raw);
4564
+ }
4565
+ const ts = new Date(raw);
4566
+ return Number.isNaN(ts.getTime()) ? null : ts;
4567
+ }
4568
+ function pushUsageEntries(entries, args) {
4569
+ const { sessionId, project, timestamp, usage, fallbackModel } = args;
4570
+ const modelUsage = usage.modelUsage;
4571
+ const models = modelUsage && Object.keys(modelUsage).length > 0 ? Object.entries(modelUsage) : [[fallbackModel, usage]];
4572
+ for (const [model, mu] of models) {
4573
+ const cached = toSafeNumber13(mu.cachedReadTokens);
4574
+ const reasoning = toSafeNumber13(mu.reasoningTokens);
4575
+ const rawInput = toSafeNumber13(mu.inputTokens);
4576
+ const rawOutput = toSafeNumber13(mu.outputTokens);
4577
+ const inputTokens = Math.max(0, rawInput - cached);
4578
+ const outputTokens = Math.max(0, rawOutput - reasoning);
4579
+ if (inputTokens === 0 && outputTokens === 0 && cached === 0 && reasoning === 0) {
4580
+ continue;
4581
+ }
4582
+ entries.push({
4583
+ sessionId,
4584
+ source: TOOL_ID16,
4585
+ model: model || fallbackModel || "unknown",
4586
+ project,
4587
+ timestamp,
4588
+ inputTokens,
4589
+ outputTokens,
4590
+ reasoningTokens: reasoning,
4591
+ cachedTokens: cached
4592
+ });
4593
+ }
4594
+ }
4595
+ function findSessionDirs(dataDir) {
4596
+ const results = [];
4597
+ if (!existsSync23(dataDir)) return results;
4598
+ let projectDirs;
4599
+ try {
4600
+ projectDirs = readdirSync13(dataDir, { withFileTypes: true });
4601
+ } catch {
4602
+ return results;
4603
+ }
4604
+ for (const projectEntry of projectDirs) {
4605
+ if (!projectEntry.isDirectory()) continue;
4606
+ if (projectEntry.name.endsWith(".sqlite")) continue;
4607
+ const projectDir = join25(dataDir, projectEntry.name);
4608
+ const project = projectFromEncodedCwd(projectEntry.name);
4609
+ let sessionEntries;
4610
+ try {
4611
+ sessionEntries = readdirSync13(projectDir, { withFileTypes: true });
4612
+ } catch {
4613
+ continue;
4614
+ }
4615
+ for (const sessionEntry of sessionEntries) {
4616
+ if (!sessionEntry.isDirectory()) continue;
4617
+ const sessionDir = join25(projectDir, sessionEntry.name);
4618
+ if (!existsSync23(join25(sessionDir, "updates.jsonl"))) continue;
4619
+ results.push({
4620
+ sessionDir,
4621
+ project,
4622
+ sessionId: sessionEntry.name
4623
+ });
4624
+ }
4625
+ }
4626
+ return results;
4627
+ }
4628
+ function readFallbackModel(sessionDir) {
4629
+ const summaryPath = join25(sessionDir, "summary.json");
4630
+ const content = readFileSafe(summaryPath);
4631
+ if (!content) return "unknown";
4632
+ try {
4633
+ const summary = JSON.parse(content);
4634
+ return summary.current_model_id || "unknown";
4635
+ } catch {
4636
+ return "unknown";
4637
+ }
4638
+ }
4639
+ var GrokBuildParser = class {
4640
+ constructor(dataDir = DEFAULT_DATA_DIR7) {
4641
+ this.dataDir = dataDir;
4642
+ this.tool = createToolDefinition13(dataDir);
4643
+ }
4644
+ dataDir;
4645
+ tool;
4646
+ async parse() {
4647
+ const entries = [];
4648
+ const sessionEvents = [];
4649
+ const sessions = findSessionDirs(this.dataDir);
4650
+ for (const { sessionDir, project, sessionId } of sessions) {
4651
+ const updatesPath = join25(sessionDir, "updates.jsonl");
4652
+ const content = readFileSafe(updatesPath);
4653
+ if (!content) continue;
4654
+ const fallbackModel = readFallbackModel(sessionDir);
4655
+ const seenUserPrompts = /* @__PURE__ */ new Set();
4656
+ const ANON_USER_OPEN = "__anon_user_open";
4657
+ for (const line of content.split("\n")) {
4658
+ if (!line.trim()) continue;
4659
+ let obj;
4660
+ try {
4661
+ obj = JSON.parse(line);
4662
+ } catch {
4663
+ continue;
4664
+ }
4665
+ const update = obj.params?.update;
4666
+ if (!update?.sessionUpdate) continue;
4667
+ const ts = resolveTimestamp(obj);
4668
+ if (!ts) continue;
4669
+ const sid = obj.params?.sessionId || sessionId;
4670
+ const updateType = update.sessionUpdate;
4671
+ const promptId = update.prompt_id || update._meta?.promptId || null;
4672
+ if (updateType === "user_message_chunk") {
4673
+ const key = promptId ?? ANON_USER_OPEN;
4674
+ if (seenUserPrompts.has(key)) continue;
4675
+ seenUserPrompts.add(key);
4676
+ sessionEvents.push({
4677
+ sessionId: sid,
4678
+ source: TOOL_ID16,
4679
+ project,
4680
+ timestamp: ts,
4681
+ role: "user"
4682
+ });
4683
+ continue;
4684
+ }
4685
+ if (updateType !== "turn_completed") continue;
4686
+ seenUserPrompts.delete(ANON_USER_OPEN);
4687
+ sessionEvents.push({
4688
+ sessionId: sid,
4689
+ source: TOOL_ID16,
4690
+ project,
4691
+ timestamp: ts,
4692
+ role: "assistant"
4693
+ });
4694
+ const usage = update.usage;
4695
+ if (!usage) continue;
4696
+ pushUsageEntries(entries, {
4697
+ sessionId: sid,
4698
+ project,
4699
+ timestamp: ts,
4700
+ usage,
4701
+ fallbackModel: update.model_id || fallbackModel
4702
+ });
4703
+ }
4704
+ }
4705
+ return {
4706
+ buckets: aggregateToBuckets(entries),
4707
+ sessions: extractSessions(sessionEvents, entries)
4708
+ };
4709
+ }
4710
+ isInstalled() {
4711
+ return existsSync23(this.dataDir) || existsSync23(join25(homedir24(), ".grok")) || existsSync23(join25(homedir24(), ".local", "bin", "grok"));
4712
+ }
4713
+ };
4714
+ registerParser(new GrokBuildParser());
4715
+
4716
+ // src/parsers/atomcode.ts
4717
+ import { existsSync as existsSync24 } from "fs";
4718
+ import { homedir as homedir25 } from "os";
4719
+ import { basename as basename11, join as join26 } from "path";
4720
+ var TOOL_ID17 = "atomcode";
4721
+ var TOOL_NAME17 = "AtomCode";
4722
+ var DEFAULT_SESSIONS_DIR6 = join26(homedir25(), ".atomcode", "sessions");
4723
+ function getAtomCodeSessionsDirs(env = process.env) {
4724
+ const dirs = [
4725
+ env.TOKEN_ARENA_ATOMCODE_DIR,
4726
+ env.ATOMCODE_HOME ? join26(env.ATOMCODE_HOME, "sessions") : void 0,
4727
+ DEFAULT_SESSIONS_DIR6
4728
+ ].filter((value) => Boolean(value));
4729
+ return Array.from(new Set(dirs));
4730
+ }
4731
+ function toNonNegativeNumber3(value) {
4732
+ const numberValue = Number(value);
4733
+ return Number.isFinite(numberValue) && numberValue >= 0 ? numberValue : 0;
4734
+ }
4735
+ function parseTimestamp3(value) {
4736
+ if (typeof value === "number" && Number.isFinite(value)) {
4737
+ const timestamp = new Date(value);
4738
+ return Number.isNaN(timestamp.getTime()) ? null : timestamp;
4739
+ }
4740
+ if (typeof value === "string" && value.trim()) {
4741
+ const asNumber = Number(value);
4742
+ if (Number.isFinite(asNumber)) {
4743
+ const timestamp2 = new Date(asNumber);
4744
+ if (!Number.isNaN(timestamp2.getTime())) {
4745
+ return timestamp2;
4746
+ }
4747
+ }
4748
+ const timestamp = new Date(value);
4749
+ return Number.isNaN(timestamp.getTime()) ? null : timestamp;
4750
+ }
4751
+ return null;
4752
+ }
4753
+ function parseMeta(content) {
4754
+ if (!content) return null;
4755
+ try {
4756
+ const parsed = JSON.parse(content);
4757
+ return parsed && typeof parsed === "object" ? parsed : null;
4758
+ } catch {
4759
+ return null;
4760
+ }
4761
+ }
4762
+ function getMetaModel(meta) {
4763
+ for (const turn of meta?.turn_stats ?? []) {
4764
+ for (const usage of turn.model_usage ?? []) {
4765
+ if (typeof usage.model_id === "string" && usage.model_id) {
4766
+ return usage.model_id;
4767
+ }
4768
+ }
4769
+ }
4770
+ return "unknown";
4771
+ }
4772
+ function getMetaProject(meta) {
4773
+ if (typeof meta?.working_dir === "string" && meta.working_dir) {
4774
+ return basename11(meta.working_dir) || "unknown";
4775
+ }
4776
+ return "unknown";
4777
+ }
4778
+ var AtomCodeParser = class {
4779
+ tool;
4780
+ sessionsDirs;
4781
+ constructor(sessionsDir) {
4782
+ this.sessionsDirs = sessionsDir ? [sessionsDir] : getAtomCodeSessionsDirs();
4783
+ this.tool = {
4784
+ id: TOOL_ID17,
4785
+ name: TOOL_NAME17,
4786
+ dataDir: this.sessionsDirs[0] ?? DEFAULT_SESSIONS_DIR6
4787
+ };
4788
+ }
4789
+ async parse() {
4790
+ const entries = [];
4791
+ const sessionEvents = [];
4792
+ const seenEntryKeys = /* @__PURE__ */ new Set();
4793
+ for (const sessionsDir of this.sessionsDirs) {
4794
+ for (const filePath of findJsonlFiles(sessionsDir)) {
4795
+ const content = readFileSafe(filePath);
4796
+ if (!content) continue;
4797
+ const rows = parseJsonl(content);
4798
+ if (rows.length === 0) continue;
4799
+ const fallbackSessionId = extractSessionId(filePath);
4800
+ const meta = parseMeta(
4801
+ readFileSafe(filePath.replace(/\.jsonl$/, ".meta"))
4802
+ );
4803
+ const project = getMetaProject(meta);
4804
+ const model = getMetaModel(meta);
4805
+ for (const row of rows) {
4806
+ const sessionId = typeof row.session_id === "string" && row.session_id ? row.session_id : fallbackSessionId;
4807
+ const timestamp = parseTimestamp3(row.ts);
4808
+ if (!timestamp) continue;
4809
+ if (row.user !== void 0 || row.assistant !== void 0) {
4810
+ sessionEvents.push({
4811
+ sessionId,
4812
+ source: TOOL_ID17,
4813
+ project,
4814
+ timestamp,
4815
+ role: row.user !== void 0 ? "user" : "assistant"
4816
+ });
4817
+ }
4818
+ const usage = row.usage;
4819
+ if (!usage) continue;
4820
+ const prompt = toNonNegativeNumber3(usage.prompt);
4821
+ const completion = toNonNegativeNumber3(usage.completion);
4822
+ const cached = toNonNegativeNumber3(usage.cached);
4823
+ const inputTokens = Math.max(0, prompt - cached);
4824
+ if (inputTokens + completion + cached === 0) {
4825
+ continue;
4826
+ }
4827
+ const entryKey = [
4828
+ sessionId,
4829
+ timestamp.toISOString(),
4830
+ model,
4831
+ inputTokens,
4832
+ completion,
4833
+ cached
4834
+ ].join("|");
4835
+ if (seenEntryKeys.has(entryKey)) {
4836
+ continue;
4837
+ }
4838
+ seenEntryKeys.add(entryKey);
4839
+ entries.push({
4840
+ sessionId,
4841
+ source: TOOL_ID17,
4842
+ model,
4843
+ project,
4844
+ timestamp,
4845
+ inputTokens,
4846
+ outputTokens: completion,
4847
+ reasoningTokens: 0,
4848
+ cachedTokens: cached
4849
+ });
4850
+ }
4851
+ }
4852
+ }
4853
+ return {
4854
+ buckets: aggregateToBuckets(entries),
4855
+ sessions: extractSessions(sessionEvents, entries)
4856
+ };
4857
+ }
4858
+ isInstalled() {
4859
+ return this.sessionsDirs.some((dir) => existsSync24(dir));
4860
+ }
4861
+ };
4862
+ registerParser(new AtomCodeParser());
4863
+
4285
4864
  // src/cli.ts
4286
4865
  import { Command, Option } from "commander";
4287
4866
 
4288
4867
  // src/infrastructure/config/manager.ts
4289
4868
  import { randomUUID } from "crypto";
4290
4869
  import {
4291
- existsSync as existsSync22,
4870
+ existsSync as existsSync25,
4292
4871
  mkdirSync,
4293
4872
  readFileSync as readFileSync9,
4294
4873
  unlinkSync,
4295
4874
  writeFileSync
4296
4875
  } from "fs";
4297
- import { join as join25 } from "path";
4876
+ import { join as join28 } from "path";
4298
4877
 
4299
4878
  // src/infrastructure/xdg.ts
4300
- import { homedir as homedir23 } from "os";
4301
- import { join as join24 } from "path";
4879
+ import { homedir as homedir26 } from "os";
4880
+ import { join as join27 } from "path";
4302
4881
  function getConfigHome() {
4303
- return process.env.XDG_CONFIG_HOME || join24(homedir23(), ".config");
4882
+ return process.env.XDG_CONFIG_HOME || join27(homedir26(), ".config");
4304
4883
  }
4305
4884
  function getStateHome() {
4306
- return process.env.XDG_STATE_HOME || join24(homedir23(), ".local", "state");
4885
+ return process.env.XDG_STATE_HOME || join27(homedir26(), ".local", "state");
4307
4886
  }
4308
4887
  function getRuntimeDir() {
4309
4888
  return process.env.XDG_RUNTIME_DIR || getStateHome();
4310
4889
  }
4311
4890
 
4312
4891
  // src/infrastructure/config/manager.ts
4313
- var CONFIG_DIR = join25(getConfigHome(), "tokenarena");
4892
+ var CONFIG_DIR = join28(getConfigHome(), "tokenarena");
4314
4893
  var isDev = process.env.TOKEN_ARENA_DEV === "1";
4315
- var CONFIG_FILE = join25(CONFIG_DIR, isDev ? "config.dev.json" : "config.json");
4894
+ var CONFIG_FILE = join28(CONFIG_DIR, isDev ? "config.dev.json" : "config.json");
4316
4895
  var DEFAULT_API_URL = "https://token.guji.uno";
4317
4896
  var VALID_CONFIG_KEYS = [
4318
4897
  "apiKey",
@@ -4328,7 +4907,7 @@ function getConfigDir() {
4328
4907
  return CONFIG_DIR;
4329
4908
  }
4330
4909
  function loadConfig() {
4331
- if (!existsSync22(CONFIG_FILE)) return null;
4910
+ if (!existsSync25(CONFIG_FILE)) return null;
4332
4911
  try {
4333
4912
  const raw = readFileSync9(CONFIG_FILE, "utf-8");
4334
4913
  const config = JSON.parse(raw);
@@ -4346,7 +4925,7 @@ function saveConfig(config) {
4346
4925
  `, "utf-8");
4347
4926
  }
4348
4927
  function deleteConfig() {
4349
- if (existsSync22(CONFIG_FILE)) {
4928
+ if (existsSync25(CONFIG_FILE)) {
4350
4929
  unlinkSync(CONFIG_FILE);
4351
4930
  }
4352
4931
  }
@@ -5208,7 +5787,7 @@ var ApiClient = class {
5208
5787
  // src/infrastructure/runtime/lock.ts
5209
5788
  import {
5210
5789
  closeSync,
5211
- existsSync as existsSync23,
5790
+ existsSync as existsSync26,
5212
5791
  openSync,
5213
5792
  readFileSync as readFileSync10,
5214
5793
  rmSync as rmSync3,
@@ -5217,22 +5796,22 @@ import {
5217
5796
 
5218
5797
  // src/infrastructure/runtime/paths.ts
5219
5798
  import { mkdirSync as mkdirSync2 } from "fs";
5220
- import { join as join26 } from "path";
5799
+ import { join as join29 } from "path";
5221
5800
  var APP_NAME = "tokenarena";
5222
5801
  function getRuntimeDirPath() {
5223
- return join26(getRuntimeDir(), APP_NAME);
5802
+ return join29(getRuntimeDir(), APP_NAME);
5224
5803
  }
5225
5804
  function getStateDir() {
5226
- return join26(getStateHome(), APP_NAME);
5805
+ return join29(getStateHome(), APP_NAME);
5227
5806
  }
5228
5807
  function getSyncLockPath() {
5229
- return join26(getRuntimeDirPath(), "sync.lock");
5808
+ return join29(getRuntimeDirPath(), "sync.lock");
5230
5809
  }
5231
5810
  function getSyncStatePath() {
5232
- return join26(getStateDir(), "status.json");
5811
+ return join29(getStateDir(), "status.json");
5233
5812
  }
5234
5813
  function getUploadManifestPath() {
5235
- return join26(getStateDir(), "upload-manifest.json");
5814
+ return join29(getStateDir(), "upload-manifest.json");
5236
5815
  }
5237
5816
  function ensureAppDirs() {
5238
5817
  mkdirSync2(getRuntimeDirPath(), { recursive: true });
@@ -5250,7 +5829,7 @@ function isProcessAlive(pid) {
5250
5829
  }
5251
5830
  }
5252
5831
  function readLockMetadata(lockPath) {
5253
- if (!existsSync23(lockPath)) {
5832
+ if (!existsSync26(lockPath)) {
5254
5833
  return null;
5255
5834
  }
5256
5835
  try {
@@ -5324,13 +5903,13 @@ function describeExistingSyncLock() {
5324
5903
  }
5325
5904
 
5326
5905
  // src/infrastructure/runtime/state.ts
5327
- import { existsSync as existsSync24, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
5906
+ import { existsSync as existsSync27, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
5328
5907
  function getDefaultState() {
5329
5908
  return { status: "idle" };
5330
5909
  }
5331
5910
  function loadSyncState() {
5332
5911
  const path = getSyncStatePath();
5333
- if (!existsSync24(path)) {
5912
+ if (!existsSync27(path)) {
5334
5913
  return getDefaultState();
5335
5914
  }
5336
5915
  try {
@@ -5391,7 +5970,7 @@ function markSyncFailed(source, error, status) {
5391
5970
  }
5392
5971
 
5393
5972
  // src/infrastructure/runtime/upload-manifest.ts
5394
- import { existsSync as existsSync25, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "fs";
5973
+ import { existsSync as existsSync28, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "fs";
5395
5974
  function isRecordOfStrings(value) {
5396
5975
  if (!value || typeof value !== "object" || Array.isArray(value)) {
5397
5976
  return false;
@@ -5407,7 +5986,7 @@ function isUploadManifest(value) {
5407
5986
  }
5408
5987
  function loadUploadManifest() {
5409
5988
  const path = getUploadManifestPath();
5410
- if (!existsSync25(path)) {
5989
+ if (!existsSync28(path)) {
5411
5990
  return null;
5412
5991
  }
5413
5992
  try {
@@ -5947,18 +6526,18 @@ View your dashboard at: ${apiUrl}/usage`);
5947
6526
 
5948
6527
  // src/commands/init.ts
5949
6528
  import { execFileSync as execFileSync7, spawn } from "child_process";
5950
- import { existsSync as existsSync28 } from "fs";
6529
+ import { existsSync as existsSync31 } from "fs";
5951
6530
  import { appendFile, mkdir, readFile } from "fs/promises";
5952
- import { homedir as homedir26, platform as platform5 } from "os";
5953
- import { dirname as dirname6, join as join27, posix as posix3, win32 } from "path";
6531
+ import { homedir as homedir29, platform as platform5 } from "os";
6532
+ import { dirname as dirname6, join as join30, posix as posix3, win32 } from "path";
5954
6533
 
5955
6534
  // src/infrastructure/service/index.ts
5956
6535
  import { platform as platform4 } from "os";
5957
6536
 
5958
6537
  // src/infrastructure/service/linux-systemd.ts
5959
6538
  import { execFileSync as execFileSync5 } from "child_process";
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";
6539
+ import { existsSync as existsSync29, mkdirSync as mkdirSync3, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
6540
+ import { homedir as homedir27, platform as platform2 } from "os";
5962
6541
  import { posix } from "path";
5963
6542
 
5964
6543
  // src/utils/command.ts
@@ -6027,10 +6606,10 @@ function escapeXml(value) {
6027
6606
 
6028
6607
  // src/infrastructure/service/linux-systemd.ts
6029
6608
  var SYSTEMD_SERVICE_NAME = "tokenarena";
6030
- function getLinuxSystemdServiceDir(homePath = homedir24()) {
6609
+ function getLinuxSystemdServiceDir(homePath = homedir27()) {
6031
6610
  return posix.join(homePath, ".config", "systemd", "user");
6032
6611
  }
6033
- function getLinuxSystemdServiceFile(homePath = homedir24()) {
6612
+ function getLinuxSystemdServiceFile(homePath = homedir27()) {
6034
6613
  return posix.join(
6035
6614
  getLinuxSystemdServiceDir(homePath),
6036
6615
  `${SYSTEMD_SERVICE_NAME}.service`
@@ -6087,7 +6666,7 @@ function ensureSystemdAvailable() {
6087
6666
  }
6088
6667
  function createLinuxSystemdServiceBackend() {
6089
6668
  function isInstalled() {
6090
- return existsSync26(getLinuxSystemdServiceFile());
6669
+ return existsSync29(getLinuxSystemdServiceFile());
6091
6670
  }
6092
6671
  async function setup(skipPrompt = false) {
6093
6672
  if (!ensureSystemdAvailable()) {
@@ -6212,7 +6791,7 @@ function createLinuxSystemdServiceBackend() {
6212
6791
  }
6213
6792
  async function uninstall(skipPrompt = false) {
6214
6793
  const serviceFile = getLinuxSystemdServiceFile();
6215
- if (!existsSync26(serviceFile)) {
6794
+ if (!existsSync29(serviceFile)) {
6216
6795
  logger.info(formatBullet("\u670D\u52A1\u6587\u4EF6\u4E0D\u5B58\u5728\u3002", "warning"));
6217
6796
  return;
6218
6797
  }
@@ -6273,17 +6852,17 @@ function createLinuxSystemdServiceBackend() {
6273
6852
 
6274
6853
  // src/infrastructure/service/macos-launchd.ts
6275
6854
  import { execFileSync as execFileSync6 } from "child_process";
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";
6855
+ import { existsSync as existsSync30, mkdirSync as mkdirSync4, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "fs";
6856
+ import { homedir as homedir28, platform as platform3 } from "os";
6278
6857
  import { posix as posix2 } from "path";
6279
6858
  var MACOS_LAUNCHD_LABEL = "com.guji.tokenarena";
6280
6859
  function getCurrentUid() {
6281
6860
  return typeof process.getuid === "function" ? process.getuid() : null;
6282
6861
  }
6283
- function getMacosLaunchAgentDir(homePath = homedir25()) {
6862
+ function getMacosLaunchAgentDir(homePath = homedir28()) {
6284
6863
  return posix2.join(homePath, "Library", "LaunchAgents");
6285
6864
  }
6286
- function getMacosLaunchAgentFile(homePath = homedir25()) {
6865
+ function getMacosLaunchAgentFile(homePath = homedir28()) {
6287
6866
  return posix2.join(
6288
6867
  getMacosLaunchAgentDir(homePath),
6289
6868
  `${MACOS_LAUNCHD_LABEL}.plist`
@@ -6410,7 +6989,7 @@ function writeLaunchAgentPlist() {
6410
6989
  label: MACOS_LAUNCHD_LABEL,
6411
6990
  programArguments: [command.execPath, ...command.args],
6412
6991
  environment: getManagedServiceEnvironment(),
6413
- workingDirectory: homedir25(),
6992
+ workingDirectory: homedir28(),
6414
6993
  standardOutPath: stdoutPath,
6415
6994
  standardErrorPath: stderrPath
6416
6995
  });
@@ -6435,7 +7014,7 @@ function bootstrapLaunchAgent() {
6435
7014
  }
6436
7015
  function createMacosLaunchdServiceBackend() {
6437
7016
  function isInstalled() {
6438
- return existsSync27(getMacosLaunchAgentFile());
7017
+ return existsSync30(getMacosLaunchAgentFile());
6439
7018
  }
6440
7019
  async function setup(skipPrompt = false) {
6441
7020
  if (!ensureLaunchctlAvailable()) {
@@ -6576,7 +7155,7 @@ function createMacosLaunchdServiceBackend() {
6576
7155
  }
6577
7156
  async function uninstall(skipPrompt = false) {
6578
7157
  const plistFile = getMacosLaunchAgentFile();
6579
- if (!existsSync27(plistFile)) {
7158
+ if (!existsSync30(plistFile)) {
6580
7159
  logger.info(formatBullet("\u670D\u52A1\u6587\u4EF6\u4E0D\u5B58\u5728\u3002", "warning"));
6581
7160
  return;
6582
7161
  }
@@ -6687,7 +7266,7 @@ function resolvePowerShellProfilePath() {
6687
7266
  const systemRoot = process.env.SYSTEMROOT || "C:\\Windows";
6688
7267
  const candidates = [
6689
7268
  "pwsh.exe",
6690
- join27(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
7269
+ join30(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
6691
7270
  ];
6692
7271
  for (const command of candidates) {
6693
7272
  try {
@@ -6716,8 +7295,8 @@ function resolvePowerShellProfilePath() {
6716
7295
  function resolveShellAliasSetup(options = {}) {
6717
7296
  const currentPlatform = options.currentPlatform ?? platform5();
6718
7297
  const env = options.env ?? process.env;
6719
- const homeDir = options.homeDir ?? homedir26();
6720
- const pathExists = options.exists ?? existsSync28;
7298
+ const homeDir = options.homeDir ?? homedir29();
7299
+ const pathExists = options.exists ?? existsSync31;
6721
7300
  const shellFromEnv = env.SHELL ? basenameLikeShell(env.SHELL).toLowerCase() : "";
6722
7301
  const shellName = shellFromEnv || (currentPlatform === "win32" ? "powershell" : "");
6723
7302
  const aliasName = "ta";
@@ -6914,7 +7493,7 @@ async function setupShellAlias() {
6914
7493
  try {
6915
7494
  await mkdir(dirname6(setup.configFile), { recursive: true });
6916
7495
  let existingContent = "";
6917
- if (existsSync28(setup.configFile)) {
7496
+ if (existsSync31(setup.configFile)) {
6918
7497
  existingContent = await readFile(setup.configFile, "utf-8");
6919
7498
  }
6920
7499
  const normalizedContent = existingContent.toLowerCase();
@@ -7173,7 +7752,7 @@ function buildLocalUsageDashboardData(input2) {
7173
7752
 
7174
7753
  // src/infrastructure/runtime/cli-version.ts
7175
7754
  import { readFileSync as readFileSync13 } from "fs";
7176
- import { dirname as dirname7, join as join28 } from "path";
7755
+ import { dirname as dirname7, join as join31 } from "path";
7177
7756
  import { fileURLToPath } from "url";
7178
7757
  var FALLBACK_VERSION = "0.0.0";
7179
7758
  var cachedVersion;
@@ -7181,7 +7760,7 @@ function getCliVersion(metaUrl = import.meta.url) {
7181
7760
  if (cachedVersion) {
7182
7761
  return cachedVersion;
7183
7762
  }
7184
- const packageJsonPath = join28(
7763
+ const packageJsonPath = join31(
7185
7764
  dirname7(fileURLToPath(metaUrl)),
7186
7765
  "..",
7187
7766
  "package.json"
@@ -7592,8 +8171,8 @@ async function runSyncCommand(opts = {}) {
7592
8171
  }
7593
8172
 
7594
8173
  // src/commands/uninstall.ts
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";
8174
+ import { existsSync as existsSync32, readFileSync as readFileSync14, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "fs";
8175
+ import { homedir as homedir30, platform as platform6 } from "os";
7597
8176
  function removeShellAlias() {
7598
8177
  const shell = process.env.SHELL;
7599
8178
  if (!shell) return;
@@ -7602,22 +8181,22 @@ function removeShellAlias() {
7602
8181
  let configFile;
7603
8182
  switch (shellName) {
7604
8183
  case "zsh":
7605
- configFile = `${homedir27()}/.zshrc`;
8184
+ configFile = `${homedir30()}/.zshrc`;
7606
8185
  break;
7607
8186
  case "bash":
7608
- if (platform6() === "darwin" && existsSync29(`${homedir27()}/.bash_profile`)) {
7609
- configFile = `${homedir27()}/.bash_profile`;
8187
+ if (platform6() === "darwin" && existsSync32(`${homedir30()}/.bash_profile`)) {
8188
+ configFile = `${homedir30()}/.bash_profile`;
7610
8189
  } else {
7611
- configFile = `${homedir27()}/.bashrc`;
8190
+ configFile = `${homedir30()}/.bashrc`;
7612
8191
  }
7613
8192
  break;
7614
8193
  case "fish":
7615
- configFile = `${homedir27()}/.config/fish/config.fish`;
8194
+ configFile = `${homedir30()}/.config/fish/config.fish`;
7616
8195
  break;
7617
8196
  default:
7618
8197
  return;
7619
8198
  }
7620
- if (!existsSync29(configFile)) return;
8199
+ if (!existsSync32(configFile)) return;
7621
8200
  try {
7622
8201
  let content = readFileSync14(configFile, "utf-8");
7623
8202
  const aliasPatterns = [
@@ -7656,7 +8235,7 @@ async function runUninstall() {
7656
8235
  const runtimeDir = getRuntimeDirPath();
7657
8236
  const serviceBackend = getServiceBackend();
7658
8237
  const hasInstalledService = serviceBackend?.isInstalled() ?? false;
7659
- const hasLocalArtifacts = existsSync29(configPath) || existsSync29(configDir) || existsSync29(stateDir) || existsSync29(runtimeDir) || hasInstalledService;
8238
+ const hasLocalArtifacts = existsSync32(configPath) || existsSync32(configDir) || existsSync32(stateDir) || existsSync32(runtimeDir) || hasInstalledService;
7660
8239
  if (!hasLocalArtifacts) {
7661
8240
  logger.info(formatHeader("\u5378\u8F7D TokenArena"));
7662
8241
  logger.info(formatBullet("\u672A\u53D1\u73B0\u672C\u5730\u914D\u7F6E\uFF0C\u65E0\u9700\u5378\u8F7D\u3002"));
@@ -7700,22 +8279,22 @@ async function runUninstall() {
7700
8279
  }
7701
8280
  }
7702
8281
  logger.info(formatSection("\u6267\u884C\u7ED3\u679C"));
7703
- if (existsSync29(configPath)) {
8282
+ if (existsSync32(configPath)) {
7704
8283
  deleteConfig();
7705
8284
  logger.info(formatBullet("\u5DF2\u5220\u9664\u914D\u7F6E\u6587\u4EF6\u3002", "success"));
7706
8285
  }
7707
- if (existsSync29(configDir)) {
8286
+ if (existsSync32(configDir)) {
7708
8287
  try {
7709
8288
  rmSync6(configDir, { recursive: false, force: true });
7710
8289
  logger.info(formatBullet("\u5DF2\u5220\u9664\u914D\u7F6E\u76EE\u5F55\u3002", "success"));
7711
8290
  } catch {
7712
8291
  }
7713
8292
  }
7714
- if (existsSync29(stateDir)) {
8293
+ if (existsSync32(stateDir)) {
7715
8294
  rmSync6(stateDir, { recursive: true, force: true });
7716
8295
  logger.info(formatBullet("\u5DF2\u5220\u9664\u72B6\u6001\u6570\u636E\u3002", "success"));
7717
8296
  }
7718
- if (existsSync29(runtimeDir)) {
8297
+ if (existsSync32(runtimeDir)) {
7719
8298
  rmSync6(runtimeDir, { recursive: true, force: true });
7720
8299
  logger.info(formatBullet("\u5DF2\u5220\u9664\u8FD0\u884C\u65F6\u6570\u636E\u3002", "success"));
7721
8300
  }
@@ -7946,7 +8525,7 @@ function createCli() {
7946
8525
  }
7947
8526
 
7948
8527
  // src/infrastructure/runtime/main-module.ts
7949
- import { existsSync as existsSync30, realpathSync as realpathSync2 } from "fs";
8528
+ import { existsSync as existsSync33, realpathSync as realpathSync2 } from "fs";
7950
8529
  import { resolve as resolve3 } from "path";
7951
8530
  import { fileURLToPath as fileURLToPath2 } from "url";
7952
8531
  function isMainModule(argvEntry = process.argv[1], metaUrl = import.meta.url) {
@@ -7957,7 +8536,7 @@ function isMainModule(argvEntry = process.argv[1], metaUrl = import.meta.url) {
7957
8536
  try {
7958
8537
  return realpathSync2(argvEntry) === realpathSync2(currentModulePath);
7959
8538
  } catch {
7960
- if (!existsSync30(argvEntry)) {
8539
+ if (!existsSync33(argvEntry)) {
7961
8540
  return false;
7962
8541
  }
7963
8542
  return resolve3(argvEntry) === resolve3(currentModulePath);