@yishiguji/tokenarena 0.17.0 → 0.18.1

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
@@ -646,11 +646,11 @@ function readRollout(path) {
646
646
  let minLineTimestampOrder = null;
647
647
  let line = 0;
648
648
  let maxOrdinal = null;
649
- for (const text of content.split("\n")) {
649
+ for (const text2 of content.split("\n")) {
650
650
  line++;
651
- if (!text.trim()) continue;
651
+ if (!text2.trim()) continue;
652
652
  try {
653
- const event = JSON.parse(text);
653
+ const event = JSON.parse(text2);
654
654
  if (typeof event.ordinal === "number" && Number.isSafeInteger(event.ordinal) && event.ordinal >= 0 && (maxOrdinal === null || event.ordinal > maxOrdinal)) {
655
655
  maxOrdinal = event.ordinal;
656
656
  }
@@ -1358,7 +1358,7 @@ async function readSqliteRowsWithBuiltin(dbPath, query) {
1358
1358
  throw err;
1359
1359
  }
1360
1360
  }
1361
- function readSqliteRowsWithCli(dbPath, query) {
1361
+ function readSqliteRowsWithCli(dbPath, query, readOnly = false) {
1362
1362
  const candidates = [
1363
1363
  process.env.TOKEN_ARENA_SQLITE3,
1364
1364
  "sqlite3",
@@ -1367,7 +1367,8 @@ function readSqliteRowsWithCli(dbPath, query) {
1367
1367
  let lastError = null;
1368
1368
  for (const command of candidates) {
1369
1369
  try {
1370
- const output = execFileSync(command, ["-json", dbPath, query], {
1370
+ const args = [...readOnly ? ["-readonly"] : [], "-json", dbPath, query];
1371
+ const output = execFileSync(command, args, {
1371
1372
  encoding: "utf-8",
1372
1373
  maxBuffer: 100 * 1024 * 1024,
1373
1374
  timeout: 3e4,
@@ -1380,7 +1381,7 @@ function readSqliteRowsWithCli(dbPath, query) {
1380
1381
  } catch (err) {
1381
1382
  lastError = err;
1382
1383
  const nodeError = err;
1383
- if (nodeError.status === 127 || nodeError.message?.includes("ENOENT")) {
1384
+ if (nodeError.status === 127 || nodeError.code === "ENOENT") {
1384
1385
  continue;
1385
1386
  }
1386
1387
  throw err;
@@ -1390,6 +1391,37 @@ function readSqliteRowsWithCli(dbPath, query) {
1390
1391
  `sqlite3 CLI not found. Install sqlite3 or set TOKEN_ARENA_SQLITE3 to its full path. Last error: ${lastError?.message || "not found"}`
1391
1392
  );
1392
1393
  }
1394
+ async function readSqliteRowsReadonly(dbPath, query) {
1395
+ return withSuppressedSqliteWarning(async () => {
1396
+ const sqliteModuleId = "node:sqlite";
1397
+ let sqlite;
1398
+ try {
1399
+ sqlite = await import(sqliteModuleId);
1400
+ } catch (error) {
1401
+ if (error.code !== "ERR_UNKNOWN_BUILTIN_MODULE") {
1402
+ throw error;
1403
+ }
1404
+ return readSqliteRowsWithCli(dbPath, query, true);
1405
+ }
1406
+ let supportsReadOnly = false;
1407
+ const probe = new sqlite.DatabaseSync(":memory:", {
1408
+ get readOnly() {
1409
+ supportsReadOnly = true;
1410
+ return false;
1411
+ }
1412
+ });
1413
+ probe.close();
1414
+ if (!supportsReadOnly) {
1415
+ return readSqliteRowsWithCli(dbPath, query, true);
1416
+ }
1417
+ const db = new sqlite.DatabaseSync(dbPath, { readOnly: true });
1418
+ try {
1419
+ return db.prepare(query).all();
1420
+ } finally {
1421
+ db.close();
1422
+ }
1423
+ });
1424
+ }
1393
1425
  async function readSqliteRows(dbPath, query) {
1394
1426
  const builtinRows = await readSqliteRowsWithBuiltin(dbPath, query);
1395
1427
  return builtinRows ?? readSqliteRowsWithCli(dbPath, query);
@@ -4039,8 +4071,8 @@ var ClineParser = class {
4039
4071
  if (msgType === "say" && msgSay === "api_req_started") {
4040
4072
  let info = null;
4041
4073
  try {
4042
- const text = msg.text;
4043
- if (typeof text === "string") info = JSON.parse(text);
4074
+ const text2 = msg.text;
4075
+ if (typeof text2 === "string") info = JSON.parse(text2);
4044
4076
  } catch {
4045
4077
  }
4046
4078
  if (!info) continue;
@@ -4806,21 +4838,228 @@ var SnowParser = class {
4806
4838
  };
4807
4839
  registerParser(new SnowParser());
4808
4840
 
4841
+ // src/parsers/snow-app.ts
4842
+ import { statSync as statSync7 } from "fs";
4843
+ import { homedir as homedir23 } from "os";
4844
+ import { dirname as dirname4, join as join24 } from "path";
4845
+ var TOOL_ID14 = "snow-app";
4846
+ var DEFAULT_DB_PATH3 = join24(homedir23(), ".snowapp", "snowapp.db");
4847
+ var SNAPSHOT_QUERY = `SELECT 'usage' AS kind, u.id, u.conversation_id AS sessionId,
4848
+ u.directory_id AS directoryId,
4849
+ u.model, u.created_at AS timestamp, '' AS role,
4850
+ u.input_tokens AS inputTokens, u.output_tokens AS outputTokens,
4851
+ u.cache_read_input_tokens AS cacheReadTokens,
4852
+ u.cache_creation_input_tokens AS cacheCreationTokens,
4853
+ '' AS forkedFrom, 0 AS forkMessageCount
4854
+ FROM usage_records u
4855
+ UNION ALL
4856
+ SELECT 'message' AS kind, m.id, m.conversation_id AS sessionId,
4857
+ (SELECT u.directory_id FROM usage_records u
4858
+ WHERE u.conversation_id = m.conversation_id
4859
+ ORDER BY u.created_at, u.id LIMIT 1) AS directoryId,
4860
+ '' AS model, m.created_at AS timestamp, m.role,
4861
+ 0 AS inputTokens, 0 AS outputTokens, 0 AS cacheReadTokens, 0 AS cacheCreationTokens,
4862
+ '' AS forkedFrom, 0 AS forkMessageCount
4863
+ FROM chat_messages m
4864
+ WHERE m.role IN ('user', 'assistant')
4865
+ AND EXISTS (SELECT 1 FROM usage_records u WHERE u.conversation_id = m.conversation_id)
4866
+ UNION ALL
4867
+ SELECT 'conversation' AS kind, c.conversation_id AS id, c.conversation_id AS sessionId,
4868
+ '' AS directoryId, '' AS model, '' AS timestamp, '' AS role,
4869
+ 0 AS inputTokens, 0 AS outputTokens, 0 AS cacheReadTokens, 0 AS cacheCreationTokens,
4870
+ c.forked_from_conversation_id AS forkedFrom, c.fork_message_count AS forkMessageCount
4871
+ FROM chat_conversations c
4872
+ WHERE EXISTS (SELECT 1 FROM usage_records u WHERE u.conversation_id = c.conversation_id)
4873
+ ORDER BY timestamp, kind, id`;
4874
+ function text(value) {
4875
+ return typeof value === "string" ? value.trim() : "";
4876
+ }
4877
+ function tokenCount4(value) {
4878
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
4879
+ throw new Error("Snow App contains invalid token counts; scan deferred.");
4880
+ }
4881
+ return value;
4882
+ }
4883
+ function parseTimestamp4(value) {
4884
+ const raw = text(value);
4885
+ const match = /^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?(Z|[+-]\d{2}:\d{2})?$/.exec(
4886
+ raw
4887
+ );
4888
+ if (!match)
4889
+ throw new Error("Snow App contains an invalid timestamp; scan deferred.");
4890
+ const [year, month, day, hour, minute, second] = match.slice(1, 7).map(Number);
4891
+ const calendar = new Date(
4892
+ Date.UTC(year, month - 1, day, hour, minute, second)
4893
+ );
4894
+ if (year < 1e3 || calendar.getUTCFullYear() !== year || calendar.getUTCMonth() !== month - 1 || calendar.getUTCDate() !== day || hour > 23 || minute > 59 || second > 59) {
4895
+ throw new Error(
4896
+ "Snow App contains an invalid calendar date; scan deferred."
4897
+ );
4898
+ }
4899
+ const date = new Date(raw.replace(" ", "T"));
4900
+ if (!Number.isFinite(date.getTime()) || !match[8] && (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day || date.getHours() !== hour || date.getMinutes() !== minute)) {
4901
+ throw new Error(
4902
+ "Snow App contains an invalid local timestamp; scan deferred."
4903
+ );
4904
+ }
4905
+ if (date.getTime() < Date.UTC(2020, 0, 1) || date.getTime() >= Date.UTC(2080, 0, 1)) {
4906
+ throw new Error(
4907
+ "Snow App timestamp is outside the supported range; scan deferred."
4908
+ );
4909
+ }
4910
+ if (!match[8]) {
4911
+ const nextDay = new Date(date);
4912
+ nextDay.setDate(nextDay.getDate() + 1);
4913
+ const overlapMinutes = nextDay.getTimezoneOffset() - date.getTimezoneOffset();
4914
+ if (overlapMinutes > 0) {
4915
+ const later = new Date(date.getTime() + overlapMinutes * 6e4);
4916
+ if (later.getHours() === hour && later.getMinutes() === minute && later.getDate() === day) {
4917
+ throw new Error(
4918
+ "Snow App contains an ambiguous local timestamp; scan deferred."
4919
+ );
4920
+ }
4921
+ }
4922
+ }
4923
+ const elapsed = date.getTime() - roundToHalfHour(date).getTime();
4924
+ if (elapsed < 0 || elapsed >= 30 * 6e4) {
4925
+ throw new Error(
4926
+ "Snow App timestamp cannot be bucketed safely across DST; scan deferred."
4927
+ );
4928
+ }
4929
+ return date;
4930
+ }
4931
+ function projectName(value) {
4932
+ if (value === "") return "unknown";
4933
+ if (typeof value !== "string" || !/^local:(?:[A-Za-z]:[\\/]|\/|\\\\)/.test(value)) {
4934
+ throw new Error(
4935
+ "Snow App contains an unsupported directory identity; scan deferred."
4936
+ );
4937
+ }
4938
+ return value.slice("local:".length).split(/[\\/]+/).filter(Boolean).at(-1) || "unknown";
4939
+ }
4940
+ var SnowAppParser = class {
4941
+ tool;
4942
+ dbPath;
4943
+ queryRows;
4944
+ constructor(options = {}) {
4945
+ this.dbPath = options.dbPath ?? DEFAULT_DB_PATH3;
4946
+ this.queryRows = options.queryRows ?? readSqliteRowsReadonly;
4947
+ this.tool = {
4948
+ id: TOOL_ID14,
4949
+ name: "Snow App",
4950
+ dataDir: dirname4(this.dbPath)
4951
+ };
4952
+ }
4953
+ isInstalled() {
4954
+ try {
4955
+ return statSync7(this.dbPath).isFile();
4956
+ } catch (error) {
4957
+ if (error.code === "ENOENT") return false;
4958
+ throw error;
4959
+ }
4960
+ }
4961
+ // Intentionally no parse cache: DB mtime alone misses uncheckpointed WAL writes.
4962
+ async parse() {
4963
+ if (!this.isInstalled()) return { buckets: [], sessions: [] };
4964
+ const rows = await this.queryRows(this.dbPath, SNAPSHOT_QUERY);
4965
+ const entries = [];
4966
+ const events = [];
4967
+ const seen = /* @__PURE__ */ new Map();
4968
+ for (const row of rows) {
4969
+ const id = text(row.id);
4970
+ if (!id || row.kind !== "usage" && row.kind !== "message" && row.kind !== "conversation") {
4971
+ throw new Error(
4972
+ "Snow App contains an invalid record identity; scan deferred."
4973
+ );
4974
+ }
4975
+ const key = JSON.stringify([row.kind, id]);
4976
+ const signature = JSON.stringify(row);
4977
+ const previous = seen.get(key);
4978
+ if (previous !== void 0) {
4979
+ if (previous !== signature)
4980
+ throw new Error("Conflicting Snow App record IDs; scan deferred.");
4981
+ continue;
4982
+ }
4983
+ seen.set(key, signature);
4984
+ if (row.kind === "conversation") {
4985
+ if (typeof row.forkedFrom !== "string" || row.forkedFrom !== "" || row.forkMessageCount !== 0) {
4986
+ throw new Error(
4987
+ "Snow App fork metadata is unsupported or invalid; scan deferred."
4988
+ );
4989
+ }
4990
+ continue;
4991
+ }
4992
+ const sessionId = text(row.sessionId);
4993
+ const project = projectName(row.directoryId);
4994
+ if (row.kind === "message") {
4995
+ if (!sessionId || row.role !== "user" && row.role !== "assistant") {
4996
+ throw new Error(
4997
+ "Snow App contains invalid message metadata; scan deferred."
4998
+ );
4999
+ }
5000
+ events.push({
5001
+ sessionId,
5002
+ source: TOOL_ID14,
5003
+ project,
5004
+ role: row.role,
5005
+ timestamp: parseTimestamp4(row.timestamp)
5006
+ });
5007
+ continue;
5008
+ }
5009
+ const input2 = tokenCount4(row.inputTokens);
5010
+ const outputTokens = tokenCount4(row.outputTokens);
5011
+ const cachedTokens = Math.min(input2, tokenCount4(row.cacheReadTokens));
5012
+ const cacheCreationTokens = tokenCount4(row.cacheCreationTokens);
5013
+ if (cacheCreationTokens > input2 - cachedTokens || !Number.isSafeInteger(input2 + outputTokens)) {
5014
+ throw new Error(
5015
+ "Snow App cache counts exceed inclusive input; scan deferred."
5016
+ );
5017
+ }
5018
+ if (input2 + outputTokens === 0) continue;
5019
+ entries.push({
5020
+ sessionId: sessionId || void 0,
5021
+ source: TOOL_ID14,
5022
+ project,
5023
+ model: text(row.model) || "unknown",
5024
+ timestamp: parseTimestamp4(row.timestamp),
5025
+ inputTokens: input2 - cachedTokens - cacheCreationTokens,
5026
+ outputTokens,
5027
+ cachedTokens,
5028
+ cacheCreationTokens,
5029
+ reasoningTokens: 0
5030
+ });
5031
+ }
5032
+ const buckets = aggregateToBuckets(entries);
5033
+ const sessions = extractSessions(events, entries);
5034
+ if ([...buckets, ...sessions].some(
5035
+ (row) => !Number.isSafeInteger(row.totalTokens)
5036
+ ) || sessions.some(
5037
+ (row) => row.durationSeconds > 2147483647 || row.activeSeconds > 2147483647
5038
+ )) {
5039
+ throw new Error(
5040
+ "Snow App aggregate exceeds supported counts or duration; scan deferred."
5041
+ );
5042
+ }
5043
+ return { buckets, sessions };
5044
+ }
5045
+ };
5046
+ registerParser(new SnowAppParser());
5047
+
4809
5048
  // src/parsers/cursor.ts
4810
5049
  import { execFileSync as execFileSync4 } from "child_process";
4811
5050
  import { copyFileSync as copyFileSync2, existsSync as existsSync23, mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
4812
- import { homedir as homedir23, tmpdir as tmpdir2 } from "os";
4813
- import { dirname as dirname4, join as join24, resolve as resolve2 } from "path";
4814
- var TOOL_ID14 = "cursor";
5051
+ import { homedir as homedir24, tmpdir as tmpdir2 } from "os";
5052
+ import { dirname as dirname5, join as join25, resolve as resolve2 } from "path";
5053
+ var TOOL_ID15 = "cursor";
4815
5054
  var TOOL_NAME13 = "Cursor";
4816
- var STATE_DB_RELATIVE = join24("User", "globalStorage", "state.vscdb");
5055
+ var STATE_DB_RELATIVE = join25("User", "globalStorage", "state.vscdb");
4817
5056
  var ACCESS_TOKEN_KEY = "cursorAuth/accessToken";
4818
5057
  var SESSION_COOKIE = "WorkosCursorSessionToken";
4819
5058
  var FETCH_TIMEOUT_MS = 1e4;
4820
5059
  function getDefaultStateDbPath() {
4821
5060
  if (process.platform === "darwin") {
4822
- return join24(
4823
- homedir23(),
5061
+ return join25(
5062
+ homedir24(),
4824
5063
  "Library",
4825
5064
  "Application Support",
4826
5065
  "Cursor",
@@ -4828,11 +5067,11 @@ function getDefaultStateDbPath() {
4828
5067
  );
4829
5068
  }
4830
5069
  if (process.platform === "win32") {
4831
- const appData = process.env.APPDATA?.trim() || join24(homedir23(), "AppData", "Roaming");
4832
- return join24(appData, "Cursor", STATE_DB_RELATIVE);
5070
+ const appData = process.env.APPDATA?.trim() || join25(homedir24(), "AppData", "Roaming");
5071
+ return join25(appData, "Cursor", STATE_DB_RELATIVE);
4833
5072
  }
4834
- const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim() || join24(homedir23(), ".config");
4835
- return join24(xdgConfigHome, "Cursor", STATE_DB_RELATIVE);
5073
+ const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim() || join25(homedir24(), ".config");
5074
+ return join25(xdgConfigHome, "Cursor", STATE_DB_RELATIVE);
4836
5075
  }
4837
5076
  function getCursorStateDbPath() {
4838
5077
  const explicit = process.env.CURSOR_STATE_DB_PATH?.trim();
@@ -4843,7 +5082,7 @@ function getCursorStateDbPath() {
4843
5082
  const configDirs = process.env.CURSOR_CONFIG_DIR?.trim();
4844
5083
  const candidates = configDirs ? configDirs.split(",").map((v) => v.trim()).filter(Boolean).map((v) => {
4845
5084
  const r = resolve2(v);
4846
- return r.endsWith(".vscdb") ? r : join24(r, STATE_DB_RELATIVE);
5085
+ return r.endsWith(".vscdb") ? r : join25(r, STATE_DB_RELATIVE);
4847
5086
  }) : [getDefaultStateDbPath()];
4848
5087
  for (const c of candidates) {
4849
5088
  if (existsSync23(c)) return c;
@@ -4886,8 +5125,8 @@ function readAccessToken(dbPath) {
4886
5125
  return queryAccessToken(dbPath);
4887
5126
  } catch (err) {
4888
5127
  if (!isLockError2(err)) throw err;
4889
- const snapshotDir = mkdtempSync2(join24(tmpdir2(), "tokenarena-cursor-"));
4890
- const queryPath = join24(snapshotDir, "state.vscdb");
5128
+ const snapshotDir = mkdtempSync2(join25(tmpdir2(), "tokenarena-cursor-"));
5129
+ const queryPath = join25(snapshotDir, "state.vscdb");
4891
5130
  try {
4892
5131
  copyFileSync2(dbPath, queryPath);
4893
5132
  for (const suffix of ["-shm", "-wal"]) {
@@ -4950,17 +5189,17 @@ async function fetchUsageCsv(token) {
4950
5189
  }
4951
5190
  throw new Error(`Cursor usage export auth failed (${failures.join("; ")})`);
4952
5191
  }
4953
- function parseCsv(text) {
5192
+ function parseCsv(text2) {
4954
5193
  const rows = [];
4955
5194
  let field = "";
4956
5195
  let row = [];
4957
5196
  let inQuotes = false;
4958
5197
  let i = 0;
4959
- while (i < text.length) {
4960
- const c = text[i];
5198
+ while (i < text2.length) {
5199
+ const c = text2[i];
4961
5200
  if (inQuotes) {
4962
5201
  if (c === '"') {
4963
- if (text[i + 1] === '"') {
5202
+ if (text2[i + 1] === '"') {
4964
5203
  field += '"';
4965
5204
  i += 2;
4966
5205
  continue;
@@ -5020,9 +5259,9 @@ function parseInt0(value) {
5020
5259
  }
5021
5260
  function createToolDefinition9(dbPath) {
5022
5261
  return {
5023
- id: TOOL_ID14,
5262
+ id: TOOL_ID15,
5024
5263
  name: TOOL_NAME13,
5025
- dataDir: dirname4(dbPath)
5264
+ dataDir: dirname5(dbPath)
5026
5265
  };
5027
5266
  }
5028
5267
  var CursorParser = class {
@@ -5084,7 +5323,7 @@ var CursorParser = class {
5084
5323
  const output = outputIdx >= 0 ? parseInt0(row[outputIdx]) : 0;
5085
5324
  if (inputCacheWrite + inputNoCache + cacheRead + output === 0) continue;
5086
5325
  entries.push({
5087
- source: TOOL_ID14,
5326
+ source: TOOL_ID15,
5088
5327
  model,
5089
5328
  project: "unknown",
5090
5329
  timestamp,
@@ -5109,11 +5348,11 @@ registerParser(new CursorParser());
5109
5348
  // src/parsers/zcode.ts
5110
5349
  import { createHash as createHash5 } from "crypto";
5111
5350
  import { existsSync as existsSync24 } from "fs";
5112
- import { homedir as homedir24, hostname as hostname5 } from "os";
5113
- import { dirname as dirname5, join as join25 } from "path";
5114
- var TOOL_ID15 = "zcode";
5351
+ import { homedir as homedir25, hostname as hostname5 } from "os";
5352
+ import { dirname as dirname6, join as join26 } from "path";
5353
+ var TOOL_ID16 = "zcode";
5115
5354
  var TOOL_NAME14 = "ZCode";
5116
- var DEFAULT_DB_PATH3 = join25(homedir24(), ".zcode", "cli", "db", "db.sqlite");
5355
+ var DEFAULT_DB_PATH4 = join26(homedir25(), ".zcode", "cli", "db", "db.sqlite");
5117
5356
  var MODEL_USAGE_QUERY = `SELECT
5118
5357
  model_usage.session_id as sessionId,
5119
5358
  session.directory as directory,
@@ -5122,13 +5361,15 @@ var MODEL_USAGE_QUERY = `SELECT
5122
5361
  model_usage.input_tokens as inputTokens,
5123
5362
  model_usage.output_tokens as outputTokens,
5124
5363
  model_usage.reasoning_tokens as reasoningTokens,
5125
- model_usage.cache_read_input_tokens as cacheReadInputTokens
5364
+ model_usage.cache_read_input_tokens as cacheReadInputTokens,
5365
+ model_usage.cache_creation_input_tokens as cacheCreationInputTokens
5126
5366
  FROM model_usage
5127
5367
  LEFT JOIN session ON session.id = model_usage.session_id
5128
5368
  WHERE model_usage.input_tokens > 0
5129
5369
  OR model_usage.output_tokens > 0
5130
5370
  OR model_usage.reasoning_tokens > 0
5131
- OR model_usage.cache_read_input_tokens > 0`;
5371
+ OR model_usage.cache_read_input_tokens > 0
5372
+ OR model_usage.cache_creation_input_tokens > 0`;
5132
5373
  var SESSION_QUERY = `SELECT
5133
5374
  id,
5134
5375
  directory,
@@ -5148,9 +5389,9 @@ var TURN_USAGE_QUERY = `SELECT
5148
5389
  WHERE duration_ms IS NOT NULL`;
5149
5390
  function createToolDefinition10(dbPath) {
5150
5391
  return {
5151
- id: TOOL_ID15,
5392
+ id: TOOL_ID16,
5152
5393
  name: TOOL_NAME14,
5153
- dataDir: dirname5(dbPath)
5394
+ dataDir: dirname6(dbPath)
5154
5395
  };
5155
5396
  }
5156
5397
  function toSafeNumber10(value) {
@@ -5197,7 +5438,7 @@ function getOrCreateDraft(drafts, sessionId, project) {
5197
5438
  }
5198
5439
  const next = {
5199
5440
  sessionId,
5200
- source: TOOL_ID15,
5441
+ source: TOOL_ID16,
5201
5442
  project,
5202
5443
  firstMessageAt: null,
5203
5444
  lastMessageAt: null,
@@ -5222,13 +5463,15 @@ function buildSessionUsage3(entries) {
5222
5463
  byModel = /* @__PURE__ */ new Map();
5223
5464
  usageBySession.set(entry.sessionId, byModel);
5224
5465
  }
5225
- const totalTokens = entry.inputTokens + entry.outputTokens + entry.reasoningTokens + entry.cachedTokens;
5466
+ const cacheCreationTokens = entry.cacheCreationTokens ?? 0;
5467
+ const totalTokens = entry.inputTokens + entry.outputTokens + entry.reasoningTokens + entry.cachedTokens + cacheCreationTokens;
5226
5468
  const existing = byModel.get(entry.model);
5227
5469
  if (existing) {
5228
5470
  existing.inputTokens += entry.inputTokens;
5229
5471
  existing.outputTokens += entry.outputTokens;
5230
5472
  existing.reasoningTokens += entry.reasoningTokens;
5231
5473
  existing.cachedTokens += entry.cachedTokens;
5474
+ existing.cacheCreationTokens = (existing.cacheCreationTokens ?? 0) + cacheCreationTokens;
5232
5475
  existing.totalTokens += totalTokens;
5233
5476
  continue;
5234
5477
  }
@@ -5238,6 +5481,7 @@ function buildSessionUsage3(entries) {
5238
5481
  outputTokens: entry.outputTokens,
5239
5482
  reasoningTokens: entry.reasoningTokens,
5240
5483
  cachedTokens: entry.cachedTokens,
5484
+ cacheCreationTokens,
5241
5485
  totalTokens
5242
5486
  });
5243
5487
  }
@@ -5325,6 +5569,10 @@ function buildSessions2(input2) {
5325
5569
  (sum, usage) => sum + usage.cachedTokens,
5326
5570
  0
5327
5571
  );
5572
+ const cacheCreationTokens = modelUsages.reduce(
5573
+ (sum, usage) => sum + (usage.cacheCreationTokens ?? 0),
5574
+ 0
5575
+ );
5328
5576
  const totalTokens = modelUsages.reduce(
5329
5577
  (sum, usage) => sum + usage.totalTokens,
5330
5578
  0
@@ -5351,6 +5599,7 @@ function buildSessions2(input2) {
5351
5599
  outputTokens,
5352
5600
  reasoningTokens,
5353
5601
  cachedTokens,
5602
+ cacheCreationTokens,
5354
5603
  totalTokens,
5355
5604
  primaryModel: modelUsages[0]?.model ?? "",
5356
5605
  modelUsages
@@ -5362,7 +5611,7 @@ var ZCodeParser = class {
5362
5611
  dbPath;
5363
5612
  queryRows;
5364
5613
  constructor(options = {}) {
5365
- this.dbPath = options.dbPath || DEFAULT_DB_PATH3;
5614
+ this.dbPath = options.dbPath || DEFAULT_DB_PATH4;
5366
5615
  this.queryRows = options.queryRows || readSqliteRows;
5367
5616
  this.tool = createToolDefinition10(this.dbPath);
5368
5617
  }
@@ -5380,23 +5629,31 @@ var ZCodeParser = class {
5380
5629
  if (!timestamp) {
5381
5630
  continue;
5382
5631
  }
5383
- const inputTokens = toSafeNumber10(row.inputTokens);
5384
- const outputTokens = toSafeNumber10(row.outputTokens);
5385
- const reasoningTokens = toSafeNumber10(row.reasoningTokens);
5386
5632
  const cachedTokens = toSafeNumber10(row.cacheReadInputTokens);
5387
- if (inputTokens === 0 && outputTokens === 0 && reasoningTokens === 0 && cachedTokens === 0) {
5633
+ const cacheCreationTokens = toSafeNumber10(row.cacheCreationInputTokens);
5634
+ const inputTokens = Math.max(
5635
+ 0,
5636
+ toSafeNumber10(row.inputTokens) - cachedTokens - cacheCreationTokens
5637
+ );
5638
+ const reasoningTokens = toSafeNumber10(row.reasoningTokens);
5639
+ const outputTokens = Math.max(
5640
+ 0,
5641
+ toSafeNumber10(row.outputTokens) - reasoningTokens
5642
+ );
5643
+ if (inputTokens + outputTokens + reasoningTokens + cachedTokens + cacheCreationTokens === 0) {
5388
5644
  continue;
5389
5645
  }
5390
5646
  entries.push({
5391
5647
  sessionId: getString3(row.sessionId) ?? void 0,
5392
- source: TOOL_ID15,
5648
+ source: TOOL_ID16,
5393
5649
  model: getString3(row.model) ?? "unknown",
5394
5650
  project: getPathLeaf7(getString3(row.directory)),
5395
5651
  timestamp,
5396
5652
  inputTokens,
5397
5653
  outputTokens,
5398
5654
  reasoningTokens,
5399
- cachedTokens
5655
+ cachedTokens,
5656
+ cacheCreationTokens
5400
5657
  });
5401
5658
  }
5402
5659
  let sessionRows = [];
@@ -5432,19 +5689,19 @@ registerParser(new ZCodeParser());
5432
5689
 
5433
5690
  // src/parsers/qodercli.ts
5434
5691
  import { existsSync as existsSync25, readdirSync as readdirSync13 } from "fs";
5435
- import { homedir as homedir25 } from "os";
5436
- import { basename as basename11, join as join26 } from "path";
5437
- var TOOL_ID16 = "qodercli";
5692
+ import { homedir as homedir26 } from "os";
5693
+ import { basename as basename11, join as join27 } from "path";
5694
+ var TOOL_ID17 = "qodercli";
5438
5695
  var TOOL_NAME15 = "Qoder CLI";
5439
- var DEFAULT_PROJECTS_DIR = join26(homedir25(), ".qoder", "projects");
5440
- var DEFAULT_LOGS_DIR = join26(homedir25(), ".qoder", "logs", "sessions");
5441
- var DEFAULT_RUNS_DIR = join26(homedir25(), ".qoder", "logs", "runs");
5696
+ var DEFAULT_PROJECTS_DIR = join27(homedir26(), ".qoder", "projects");
5697
+ var DEFAULT_LOGS_DIR = join27(homedir26(), ".qoder", "logs", "sessions");
5698
+ var DEFAULT_RUNS_DIR = join27(homedir26(), ".qoder", "logs", "runs");
5442
5699
  var CLI_ENTRYPOINT = "cli";
5443
5700
  var IDE_ENTRYPOINT = "acp";
5444
5701
  var CREDIT_MODEL_FALLBACK = "credits";
5445
5702
  function createToolDefinition11(projectsDir) {
5446
5703
  return {
5447
- id: TOOL_ID16,
5704
+ id: TOOL_ID17,
5448
5705
  name: TOOL_NAME15,
5449
5706
  dataDir: projectsDir
5450
5707
  };
@@ -5484,11 +5741,11 @@ function classifyQoderEntrypoint(content) {
5484
5741
  return "unknown";
5485
5742
  }
5486
5743
  function isQodercliBinaryPresent() {
5487
- const home = homedir25();
5744
+ const home = homedir26();
5488
5745
  const candidates = [
5489
- join26(home, ".local", "bin", "qodercli"),
5490
- join26(home, ".qoder", "bin", "qodercli"),
5491
- join26(home, ".qoder-cli")
5746
+ join27(home, ".local", "bin", "qodercli"),
5747
+ join27(home, ".qoder", "bin", "qodercli"),
5748
+ join27(home, ".qoder-cli")
5492
5749
  ];
5493
5750
  return candidates.some((path) => existsSync25(path));
5494
5751
  }
@@ -5561,7 +5818,7 @@ function creditSnapshotsToEntries(snapshots) {
5561
5818
  if (creditDelta > 0) {
5562
5819
  entries.push({
5563
5820
  sessionId: snap.sessionId,
5564
- source: TOOL_ID16,
5821
+ source: TOOL_ID17,
5565
5822
  model: snap.model || CREDIT_MODEL_FALLBACK,
5566
5823
  project: snap.project,
5567
5824
  timestamp: snap.timestamp,
@@ -5621,7 +5878,7 @@ var QoderCliParser = class {
5621
5878
  if (Number.isNaN(ts.getTime())) continue;
5622
5879
  sessionEvents.push({
5623
5880
  sessionId,
5624
- source: TOOL_ID16,
5881
+ source: TOOL_ID17,
5625
5882
  project,
5626
5883
  timestamp: ts,
5627
5884
  role: obj.type === "user" ? "user" : "assistant"
@@ -5647,15 +5904,15 @@ var QoderCliParser = class {
5647
5904
  }
5648
5905
  const allSnapshots = [];
5649
5906
  for (const dir of runDirs) {
5650
- const runPath = join26(this.runsDir, dir.name);
5651
- const logPath = join26(runPath, "qodercli.log");
5907
+ const runPath = join27(this.runsDir, dir.name);
5908
+ const logPath = join27(runPath, "qodercli.log");
5652
5909
  if (!existsSync25(logPath)) continue;
5653
5910
  const logContent = readFileSafe(logPath);
5654
5911
  if (!logContent?.includes("quota/usage response:")) {
5655
5912
  continue;
5656
5913
  }
5657
5914
  const manifest = parseRunManifest(
5658
- readFileSafe(join26(runPath, "manifest.json"))
5915
+ readFileSafe(join27(runPath, "manifest.json"))
5659
5916
  );
5660
5917
  const project = manifest?.project_id ? projectFromEncodedSlug(manifest.project_id) : projectFromCwd(manifest?.cwd);
5661
5918
  const snapshots = extractCreditSnapshotsFromLog(logContent, {
@@ -5674,15 +5931,15 @@ registerParser(new QoderCliParser());
5674
5931
 
5675
5932
  // src/parsers/grok-build.ts
5676
5933
  import { existsSync as existsSync26, readdirSync as readdirSync14 } from "fs";
5677
- import { homedir as homedir26 } from "os";
5678
- import { basename as basename12, join as join27 } from "path";
5679
- var TOOL_ID17 = "grok-build";
5934
+ import { homedir as homedir27 } from "os";
5935
+ import { basename as basename12, join as join28 } from "path";
5936
+ var TOOL_ID18 = "grok-build";
5680
5937
  var TOOL_NAME16 = "Grok Build";
5681
- var DEFAULT_DATA_DIR6 = join27(homedir26(), ".grok", "sessions");
5682
- var DEFAULT_ARCHIVE_DIR2 = join27(homedir26(), ".grok", "archived_sessions");
5938
+ var DEFAULT_DATA_DIR6 = join28(homedir27(), ".grok", "sessions");
5939
+ var DEFAULT_ARCHIVE_DIR2 = join28(homedir27(), ".grok", "archived_sessions");
5683
5940
  function createToolDefinition12(dataDir) {
5684
5941
  return {
5685
- id: TOOL_ID17,
5942
+ id: TOOL_ID18,
5686
5943
  name: TOOL_NAME16,
5687
5944
  dataDir
5688
5945
  };
@@ -5734,7 +5991,7 @@ function pushUsageEntries(entries, args) {
5734
5991
  }
5735
5992
  modelEntries.push({
5736
5993
  sessionId,
5737
- source: TOOL_ID17,
5994
+ source: TOOL_ID18,
5738
5995
  model: model || fallbackModel || "unknown",
5739
5996
  project,
5740
5997
  timestamp,
@@ -5758,7 +6015,7 @@ function findSessionDirs(dataDir) {
5758
6015
  for (const projectEntry of projectDirs) {
5759
6016
  if (!projectEntry.isDirectory()) continue;
5760
6017
  if (projectEntry.name.endsWith(".sqlite")) continue;
5761
- const projectDir = join27(dataDir, projectEntry.name);
6018
+ const projectDir = join28(dataDir, projectEntry.name);
5762
6019
  const project = projectFromEncodedCwd(projectEntry.name);
5763
6020
  let sessionEntries;
5764
6021
  try {
@@ -5768,8 +6025,8 @@ function findSessionDirs(dataDir) {
5768
6025
  }
5769
6026
  for (const sessionEntry of sessionEntries) {
5770
6027
  if (!sessionEntry.isDirectory()) continue;
5771
- const sessionDir = join27(projectDir, sessionEntry.name);
5772
- if (!existsSync26(join27(sessionDir, "updates.jsonl"))) continue;
6028
+ const sessionDir = join28(projectDir, sessionEntry.name);
6029
+ if (!existsSync26(join28(sessionDir, "updates.jsonl"))) continue;
5773
6030
  results.push({
5774
6031
  sessionDir,
5775
6032
  project,
@@ -5780,7 +6037,7 @@ function findSessionDirs(dataDir) {
5780
6037
  return results;
5781
6038
  }
5782
6039
  function readFallbackModel(sessionDir) {
5783
- const summaryPath = join27(sessionDir, "summary.json");
6040
+ const summaryPath = join28(sessionDir, "summary.json");
5784
6041
  const content = readFileSafe(summaryPath);
5785
6042
  if (!content) return "unknown";
5786
6043
  try {
@@ -5807,7 +6064,7 @@ var GrokBuildParser = class {
5807
6064
  ...findSessionDirs(this.dataDir)
5808
6065
  ];
5809
6066
  for (const { sessionDir, project, sessionId } of sessions) {
5810
- const updatesPath = join27(sessionDir, "updates.jsonl");
6067
+ const updatesPath = join28(sessionDir, "updates.jsonl");
5811
6068
  const content = readFileSafe(updatesPath);
5812
6069
  if (!content) continue;
5813
6070
  const fallbackModel = readFallbackModel(sessionDir);
@@ -5844,7 +6101,7 @@ var GrokBuildParser = class {
5844
6101
  ]),
5845
6102
  {
5846
6103
  sessionId: sid,
5847
- source: TOOL_ID17,
6104
+ source: TOOL_ID18,
5848
6105
  project,
5849
6106
  timestamp: ts,
5850
6107
  role: "user"
@@ -5860,7 +6117,7 @@ var GrokBuildParser = class {
5860
6117
  continue;
5861
6118
  sessionEvents.set(eventKey, {
5862
6119
  sessionId: sid,
5863
- source: TOOL_ID17,
6120
+ source: TOOL_ID18,
5864
6121
  project,
5865
6122
  timestamp: ts,
5866
6123
  role: "assistant"
@@ -5884,22 +6141,22 @@ var GrokBuildParser = class {
5884
6141
  };
5885
6142
  }
5886
6143
  isInstalled() {
5887
- return existsSync26(this.dataDir) || Boolean(this.archiveDir && existsSync26(this.archiveDir)) || existsSync26(join27(homedir26(), ".grok")) || existsSync26(join27(homedir26(), ".local", "bin", "grok"));
6144
+ return existsSync26(this.dataDir) || Boolean(this.archiveDir && existsSync26(this.archiveDir)) || existsSync26(join28(homedir27(), ".grok")) || existsSync26(join28(homedir27(), ".local", "bin", "grok"));
5888
6145
  }
5889
6146
  };
5890
6147
  registerParser(new GrokBuildParser());
5891
6148
 
5892
6149
  // src/parsers/atomcode.ts
5893
6150
  import { existsSync as existsSync27 } from "fs";
5894
- import { homedir as homedir27 } from "os";
5895
- import { basename as basename13, join as join28 } from "path";
5896
- var TOOL_ID18 = "atomcode";
6151
+ import { homedir as homedir28 } from "os";
6152
+ import { basename as basename13, join as join29 } from "path";
6153
+ var TOOL_ID19 = "atomcode";
5897
6154
  var TOOL_NAME17 = "AtomCode";
5898
- var DEFAULT_SESSIONS_DIR6 = join28(homedir27(), ".atomcode", "sessions");
6155
+ var DEFAULT_SESSIONS_DIR6 = join29(homedir28(), ".atomcode", "sessions");
5899
6156
  function getAtomCodeSessionsDirs(env = process.env) {
5900
6157
  const dirs = [
5901
6158
  env.TOKEN_ARENA_ATOMCODE_DIR,
5902
- env.ATOMCODE_HOME ? join28(env.ATOMCODE_HOME, "sessions") : void 0,
6159
+ env.ATOMCODE_HOME ? join29(env.ATOMCODE_HOME, "sessions") : void 0,
5903
6160
  DEFAULT_SESSIONS_DIR6
5904
6161
  ].filter((value) => Boolean(value));
5905
6162
  return Array.from(new Set(dirs));
@@ -5908,7 +6165,7 @@ function toNonNegativeNumber3(value) {
5908
6165
  const numberValue = Number(value);
5909
6166
  return Number.isFinite(numberValue) && numberValue >= 0 ? numberValue : 0;
5910
6167
  }
5911
- function parseTimestamp4(value) {
6168
+ function parseTimestamp5(value) {
5912
6169
  if (typeof value === "number" && Number.isFinite(value)) {
5913
6170
  const timestamp = new Date(value);
5914
6171
  return Number.isNaN(timestamp.getTime()) ? null : timestamp;
@@ -5957,7 +6214,7 @@ var AtomCodeParser = class {
5957
6214
  constructor(sessionsDir) {
5958
6215
  this.sessionsDirs = sessionsDir ? [sessionsDir] : getAtomCodeSessionsDirs();
5959
6216
  this.tool = {
5960
- id: TOOL_ID18,
6217
+ id: TOOL_ID19,
5961
6218
  name: TOOL_NAME17,
5962
6219
  dataDir: this.sessionsDirs[0] ?? DEFAULT_SESSIONS_DIR6
5963
6220
  };
@@ -5980,12 +6237,12 @@ var AtomCodeParser = class {
5980
6237
  const model = getMetaModel(meta);
5981
6238
  for (const row of rows) {
5982
6239
  const sessionId = typeof row.session_id === "string" && row.session_id ? row.session_id : fallbackSessionId;
5983
- const timestamp = parseTimestamp4(row.ts);
6240
+ const timestamp = parseTimestamp5(row.ts);
5984
6241
  if (!timestamp) continue;
5985
6242
  if (row.user !== void 0 || row.assistant !== void 0) {
5986
6243
  sessionEvents.push({
5987
6244
  sessionId,
5988
- source: TOOL_ID18,
6245
+ source: TOOL_ID19,
5989
6246
  project,
5990
6247
  timestamp,
5991
6248
  role: row.user !== void 0 ? "user" : "assistant"
@@ -6014,7 +6271,7 @@ var AtomCodeParser = class {
6014
6271
  seenEntryKeys.add(entryKey);
6015
6272
  entries.push({
6016
6273
  sessionId,
6017
- source: TOOL_ID18,
6274
+ source: TOOL_ID19,
6018
6275
  model,
6019
6276
  project,
6020
6277
  timestamp,
@@ -6039,17 +6296,17 @@ registerParser(new AtomCodeParser());
6039
6296
 
6040
6297
  // src/parsers/dsh.ts
6041
6298
  import { existsSync as existsSync28, readdirSync as readdirSync15, readFileSync as readFileSync9 } from "fs";
6042
- import { homedir as homedir28 } from "os";
6043
- import { basename as basename14, join as join29 } from "path";
6299
+ import { homedir as homedir29 } from "os";
6300
+ import { basename as basename14, join as join30 } from "path";
6044
6301
  import * as zlib from "zlib";
6045
- var TOOL_ID19 = "dsh";
6302
+ var TOOL_ID20 = "dsh";
6046
6303
  var TOOL_NAME18 = "DeepSeek Harness";
6047
- var DEFAULT_SESSIONS_DIR7 = join29(homedir28(), ".dsh", "sessions");
6304
+ var DEFAULT_SESSIONS_DIR7 = join30(homedir29(), ".dsh", "sessions");
6048
6305
  var LOG_BASENAME = "session";
6049
6306
  function getDshSessionsDirs(env = process.env) {
6050
6307
  const dirs = [
6051
6308
  env.TOKEN_ARENA_DSH_DIR,
6052
- env.DSH_HOME ? join29(env.DSH_HOME, "sessions") : void 0,
6309
+ env.DSH_HOME ? join30(env.DSH_HOME, "sessions") : void 0,
6053
6310
  DEFAULT_SESSIONS_DIR7
6054
6311
  ].filter((value) => Boolean(value));
6055
6312
  return Array.from(new Set(dirs));
@@ -6141,7 +6398,7 @@ function findSessionLogs(dir) {
6141
6398
  if (!existsSync28(dir)) return results;
6142
6399
  try {
6143
6400
  for (const entry of readdirSync15(dir, { withFileTypes: true })) {
6144
- const fullPath = join29(dir, entry.name);
6401
+ const fullPath = join30(dir, entry.name);
6145
6402
  if (entry.isDirectory()) {
6146
6403
  results.push(...findSessionLogs(fullPath));
6147
6404
  } else if (entry.name === `${LOG_BASENAME}.jsonl` || entry.name === `${LOG_BASENAME}.jsonl.zstd`) {
@@ -6172,7 +6429,7 @@ var DshParser = class {
6172
6429
  constructor(sessionsDir) {
6173
6430
  this.sessionsDirs = sessionsDir ? [sessionsDir] : getDshSessionsDirs();
6174
6431
  this.tool = {
6175
- id: TOOL_ID19,
6432
+ id: TOOL_ID20,
6176
6433
  name: TOOL_NAME18,
6177
6434
  dataDir: this.sessionsDirs[0] ?? DEFAULT_SESSIONS_DIR7
6178
6435
  };
@@ -6210,7 +6467,7 @@ var DshParser = class {
6210
6467
  if (timestamp) {
6211
6468
  sessionEvents.push({
6212
6469
  sessionId,
6213
- source: TOOL_ID19,
6470
+ source: TOOL_ID20,
6214
6471
  project,
6215
6472
  timestamp,
6216
6473
  role: "user"
@@ -6223,7 +6480,7 @@ var DshParser = class {
6223
6480
  if (timestamp && !isCompaction) {
6224
6481
  sessionEvents.push({
6225
6482
  sessionId,
6226
- source: TOOL_ID19,
6483
+ source: TOOL_ID20,
6227
6484
  project,
6228
6485
  timestamp,
6229
6486
  role: "assistant"
@@ -6258,7 +6515,7 @@ var DshParser = class {
6258
6515
  seenEntryKeys.add(entryKey);
6259
6516
  entries.push({
6260
6517
  sessionId,
6261
- source: TOOL_ID19,
6518
+ source: TOOL_ID20,
6262
6519
  model,
6263
6520
  project,
6264
6521
  timestamp,
@@ -6284,11 +6541,11 @@ registerParser(new DshParser());
6284
6541
 
6285
6542
  // src/parsers/cherry-studio.ts
6286
6543
  import { existsSync as existsSync29 } from "fs";
6287
- import { homedir as homedir29 } from "os";
6288
- import { dirname as dirname6, join as join30, resolve as resolve3 } from "path";
6289
- var TOOL_ID20 = "cherry-studio";
6544
+ import { homedir as homedir30 } from "os";
6545
+ import { dirname as dirname7, join as join31, resolve as resolve3 } from "path";
6546
+ var TOOL_ID21 = "cherry-studio";
6290
6547
  var TOOL_NAME19 = "Cherry Studio";
6291
- var DB_RELATIVE = join30("Data", "cherrystudio.sqlite");
6548
+ var DB_RELATIVE = join31("Data", "cherrystudio.sqlite");
6292
6549
  var USAGE_QUERY = `SELECT
6293
6550
  u.model_id as modelId,
6294
6551
  u.no_cache_tokens as noCacheTokens,
@@ -6311,14 +6568,14 @@ var MESSAGES_QUERY3 = `SELECT
6311
6568
  ORDER BY created_at`;
6312
6569
  function getDefaultUserDataDir(env = process.env) {
6313
6570
  if (process.platform === "darwin") {
6314
- return join30(homedir29(), "Library", "Application Support", "CherryStudio");
6571
+ return join31(homedir30(), "Library", "Application Support", "CherryStudio");
6315
6572
  }
6316
6573
  if (process.platform === "win32") {
6317
- const appData = env.APPDATA?.trim() || join30(homedir29(), "AppData", "Roaming");
6318
- return join30(appData, "CherryStudio");
6574
+ const appData = env.APPDATA?.trim() || join31(homedir30(), "AppData", "Roaming");
6575
+ return join31(appData, "CherryStudio");
6319
6576
  }
6320
- const xdgConfigHome = env.XDG_CONFIG_HOME?.trim() || join30(homedir29(), ".config");
6321
- return join30(xdgConfigHome, "CherryStudio");
6577
+ const xdgConfigHome = env.XDG_CONFIG_HOME?.trim() || join31(homedir30(), ".config");
6578
+ return join31(xdgConfigHome, "CherryStudio");
6322
6579
  }
6323
6580
  function getCherryStudioDbPaths(env = process.env) {
6324
6581
  const paths = [];
@@ -6326,10 +6583,10 @@ function getCherryStudioDbPaths(env = process.env) {
6326
6583
  if (explicit) {
6327
6584
  const resolved = resolve3(explicit);
6328
6585
  paths.push(
6329
- resolved.endsWith(".sqlite") ? resolved : join30(resolved, DB_RELATIVE)
6586
+ resolved.endsWith(".sqlite") ? resolved : join31(resolved, DB_RELATIVE)
6330
6587
  );
6331
6588
  }
6332
- paths.push(join30(getDefaultUserDataDir(env), DB_RELATIVE));
6589
+ paths.push(join31(getDefaultUserDataDir(env), DB_RELATIVE));
6333
6590
  return Array.from(new Set(paths));
6334
6591
  }
6335
6592
  function resolveCherryStudioDbPath() {
@@ -6338,9 +6595,9 @@ function resolveCherryStudioDbPath() {
6338
6595
  }
6339
6596
  function createToolDefinition13(dbPath) {
6340
6597
  return {
6341
- id: TOOL_ID20,
6598
+ id: TOOL_ID21,
6342
6599
  name: TOOL_NAME19,
6343
- dataDir: dbPath ? dirname6(dbPath) : join30(getDefaultUserDataDir(), "Data")
6600
+ dataDir: dbPath ? dirname7(dbPath) : join31(getDefaultUserDataDir(), "Data")
6344
6601
  };
6345
6602
  }
6346
6603
  function toSafeCount(value) {
@@ -6405,7 +6662,7 @@ var CherryStudioParser = class {
6405
6662
  // Rows the topic join could not resolve still count toward buckets,
6406
6663
  // they just cannot take part in session timing.
6407
6664
  sessionId: getNonEmptyString(row.sessionId) ?? void 0,
6408
- source: TOOL_ID20,
6665
+ source: TOOL_ID21,
6409
6666
  model: getNonEmptyString(row.modelId) ?? "unknown",
6410
6667
  project: "unknown",
6411
6668
  timestamp,
@@ -6438,7 +6695,7 @@ var CherryStudioParser = class {
6438
6695
  if (!timestamp) continue;
6439
6696
  sessionEvents.push({
6440
6697
  sessionId,
6441
- source: TOOL_ID20,
6698
+ source: TOOL_ID21,
6442
6699
  project: "unknown",
6443
6700
  timestamp,
6444
6701
  role
@@ -6467,25 +6724,25 @@ import {
6467
6724
  unlinkSync,
6468
6725
  writeFileSync
6469
6726
  } from "fs";
6470
- import { join as join32 } from "path";
6727
+ import { join as join33 } from "path";
6471
6728
 
6472
6729
  // src/infrastructure/xdg.ts
6473
- import { homedir as homedir30 } from "os";
6474
- import { join as join31 } from "path";
6730
+ import { homedir as homedir31 } from "os";
6731
+ import { join as join32 } from "path";
6475
6732
  function getConfigHome() {
6476
- return process.env.XDG_CONFIG_HOME || join31(homedir30(), ".config");
6733
+ return process.env.XDG_CONFIG_HOME || join32(homedir31(), ".config");
6477
6734
  }
6478
6735
  function getStateHome() {
6479
- return process.env.XDG_STATE_HOME || join31(homedir30(), ".local", "state");
6736
+ return process.env.XDG_STATE_HOME || join32(homedir31(), ".local", "state");
6480
6737
  }
6481
6738
  function getRuntimeDir() {
6482
6739
  return process.env.XDG_RUNTIME_DIR || getStateHome();
6483
6740
  }
6484
6741
 
6485
6742
  // src/infrastructure/config/manager.ts
6486
- var CONFIG_DIR = join32(getConfigHome(), "tokenarena");
6743
+ var CONFIG_DIR = join33(getConfigHome(), "tokenarena");
6487
6744
  var isDev = process.env.TOKEN_ARENA_DEV === "1";
6488
- var CONFIG_FILE = join32(CONFIG_DIR, isDev ? "config.dev.json" : "config.json");
6745
+ var CONFIG_FILE = join33(CONFIG_DIR, isDev ? "config.dev.json" : "config.json");
6489
6746
  var DEFAULT_API_URL = "https://token.guji.uno";
6490
6747
  var VALID_CONFIG_KEYS = [
6491
6748
  "apiKey",
@@ -7347,22 +7604,22 @@ import {
7347
7604
 
7348
7605
  // src/infrastructure/runtime/paths.ts
7349
7606
  import { mkdirSync as mkdirSync2 } from "fs";
7350
- import { join as join33 } from "path";
7607
+ import { join as join34 } from "path";
7351
7608
  var APP_NAME = "tokenarena";
7352
7609
  function getRuntimeDirPath() {
7353
- return join33(getRuntimeDir(), APP_NAME);
7610
+ return join34(getRuntimeDir(), APP_NAME);
7354
7611
  }
7355
7612
  function getStateDir() {
7356
- return join33(getStateHome(), APP_NAME);
7613
+ return join34(getStateHome(), APP_NAME);
7357
7614
  }
7358
7615
  function getSyncLockPath() {
7359
- return join33(getRuntimeDirPath(), "sync.lock");
7616
+ return join34(getRuntimeDirPath(), "sync.lock");
7360
7617
  }
7361
7618
  function getSyncStatePath() {
7362
- return join33(getStateDir(), "status.json");
7619
+ return join34(getStateDir(), "status.json");
7363
7620
  }
7364
7621
  function getUploadManifestPath() {
7365
- return join33(getStateDir(), "upload-manifest.json");
7622
+ return join34(getStateDir(), "upload-manifest.json");
7366
7623
  }
7367
7624
  function ensureAppDirs() {
7368
7625
  mkdirSync2(getRuntimeDirPath(), { recursive: true });
@@ -7568,19 +7825,19 @@ function saveUploadManifest(manifest) {
7568
7825
 
7569
7826
  // src/services/parse-cache.ts
7570
7827
  import { createHash as createHash7 } from "crypto";
7571
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync14, statSync as statSync7, writeFileSync as writeFileSync5 } from "fs";
7828
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync14, statSync as statSync8, writeFileSync as writeFileSync5 } from "fs";
7572
7829
  import { hostname as hostname6 } from "os";
7573
- import { join as join35 } from "path";
7830
+ import { join as join36 } from "path";
7574
7831
 
7575
7832
  // src/infrastructure/runtime/cli-version.ts
7576
- import { dirname as dirname7, join as join34 } from "path";
7833
+ import { dirname as dirname8, join as join35 } from "path";
7577
7834
  import { fileURLToPath } from "url";
7578
7835
  var FALLBACK_VERSION = "0.0.0";
7579
7836
  var versionCache = /* @__PURE__ */ new Map();
7580
7837
  function readPackageVersion(startDir) {
7581
7838
  let dir = startDir;
7582
7839
  while (true) {
7583
- const contents = readFileSafe(join34(dir, "package.json"));
7840
+ const contents = readFileSafe(join35(dir, "package.json"));
7584
7841
  if (contents !== null) {
7585
7842
  try {
7586
7843
  const { version } = JSON.parse(contents);
@@ -7589,7 +7846,7 @@ function readPackageVersion(startDir) {
7589
7846
  return void 0;
7590
7847
  }
7591
7848
  }
7592
- const parent = dirname7(dir);
7849
+ const parent = dirname8(dir);
7593
7850
  if (parent === dir) {
7594
7851
  return void 0;
7595
7852
  }
@@ -7597,7 +7854,7 @@ function readPackageVersion(startDir) {
7597
7854
  }
7598
7855
  }
7599
7856
  function getCliVersion(metaUrl = import.meta.url) {
7600
- const startDir = dirname7(fileURLToPath(metaUrl));
7857
+ const startDir = dirname8(fileURLToPath(metaUrl));
7601
7858
  const cached = versionCache.get(startDir);
7602
7859
  if (cached !== void 0) {
7603
7860
  return cached;
@@ -7611,10 +7868,10 @@ function getCliVersion(metaUrl = import.meta.url) {
7611
7868
  var CACHE_FORMAT_VERSION = 1;
7612
7869
  var SETTLE_MS = 5e3;
7613
7870
  function getParseCacheDir() {
7614
- return join35(getStateDir(), "parse-cache");
7871
+ return join36(getStateDir(), "parse-cache");
7615
7872
  }
7616
7873
  function getParseCachePath(toolId) {
7617
- return join35(getParseCacheDir(), `${toolId}.json`);
7874
+ return join36(getParseCacheDir(), `${toolId}.json`);
7618
7875
  }
7619
7876
  function computeScanFingerprint(files, now = Date.now()) {
7620
7877
  const hash = createHash7("sha256");
@@ -7624,7 +7881,7 @@ function computeScanFingerprint(files, now = Date.now()) {
7624
7881
  for (const file of [...files].sort()) {
7625
7882
  let stats;
7626
7883
  try {
7627
- stats = statSync7(file);
7884
+ stats = statSync8(file);
7628
7885
  } catch {
7629
7886
  return null;
7630
7887
  }
@@ -8289,8 +8546,8 @@ View your dashboard at: ${apiUrl}/usage`);
8289
8546
  import { execFileSync as execFileSync8, spawn } from "child_process";
8290
8547
  import { existsSync as existsSync36 } from "fs";
8291
8548
  import { appendFile, mkdir, readFile } from "fs/promises";
8292
- import { homedir as homedir33, platform as platform5 } from "os";
8293
- import { dirname as dirname8, join as join36, posix as posix3, win32 } from "path";
8549
+ import { homedir as homedir34, platform as platform5 } from "os";
8550
+ import { dirname as dirname9, join as join37, posix as posix3, win32 } from "path";
8294
8551
 
8295
8552
  // src/infrastructure/service/index.ts
8296
8553
  import { platform as platform4 } from "os";
@@ -8298,7 +8555,7 @@ import { platform as platform4 } from "os";
8298
8555
  // src/infrastructure/service/linux-systemd.ts
8299
8556
  import { execFileSync as execFileSync6 } from "child_process";
8300
8557
  import { existsSync as existsSync34, mkdirSync as mkdirSync4, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
8301
- import { homedir as homedir31, platform as platform2 } from "os";
8558
+ import { homedir as homedir32, platform as platform2 } from "os";
8302
8559
  import { posix } from "path";
8303
8560
 
8304
8561
  // src/utils/command.ts
@@ -8379,10 +8636,10 @@ function escapeXml(value) {
8379
8636
 
8380
8637
  // src/infrastructure/service/linux-systemd.ts
8381
8638
  var SYSTEMD_SERVICE_NAME = "tokenarena";
8382
- function getLinuxSystemdServiceDir(homePath = homedir31()) {
8639
+ function getLinuxSystemdServiceDir(homePath = homedir32()) {
8383
8640
  return posix.join(homePath, ".config", "systemd", "user");
8384
8641
  }
8385
- function getLinuxSystemdServiceFile(homePath = homedir31()) {
8642
+ function getLinuxSystemdServiceFile(homePath = homedir32()) {
8386
8643
  return posix.join(
8387
8644
  getLinuxSystemdServiceDir(homePath),
8388
8645
  `${SYSTEMD_SERVICE_NAME}.service`
@@ -8626,16 +8883,16 @@ function createLinuxSystemdServiceBackend() {
8626
8883
  // src/infrastructure/service/macos-launchd.ts
8627
8884
  import { execFileSync as execFileSync7 } from "child_process";
8628
8885
  import { existsSync as existsSync35, mkdirSync as mkdirSync5, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "fs";
8629
- import { homedir as homedir32, platform as platform3 } from "os";
8886
+ import { homedir as homedir33, platform as platform3 } from "os";
8630
8887
  import { posix as posix2 } from "path";
8631
8888
  var MACOS_LAUNCHD_LABEL = "com.guji.tokenarena";
8632
8889
  function getCurrentUid() {
8633
8890
  return typeof process.getuid === "function" ? process.getuid() : null;
8634
8891
  }
8635
- function getMacosLaunchAgentDir(homePath = homedir32()) {
8892
+ function getMacosLaunchAgentDir(homePath = homedir33()) {
8636
8893
  return posix2.join(homePath, "Library", "LaunchAgents");
8637
8894
  }
8638
- function getMacosLaunchAgentFile(homePath = homedir32()) {
8895
+ function getMacosLaunchAgentFile(homePath = homedir33()) {
8639
8896
  return posix2.join(
8640
8897
  getMacosLaunchAgentDir(homePath),
8641
8898
  `${MACOS_LAUNCHD_LABEL}.plist`
@@ -8762,7 +9019,7 @@ function writeLaunchAgentPlist() {
8762
9019
  label: MACOS_LAUNCHD_LABEL,
8763
9020
  programArguments: [command.execPath, ...command.args],
8764
9021
  environment: getManagedServiceEnvironment(),
8765
- workingDirectory: homedir32(),
9022
+ workingDirectory: homedir33(),
8766
9023
  standardOutPath: stdoutPath,
8767
9024
  standardErrorPath: stderrPath
8768
9025
  });
@@ -9039,7 +9296,7 @@ function resolvePowerShellProfilePath() {
9039
9296
  const systemRoot = process.env.SYSTEMROOT || "C:\\Windows";
9040
9297
  const candidates = [
9041
9298
  "pwsh.exe",
9042
- join36(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
9299
+ join37(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
9043
9300
  ];
9044
9301
  for (const command of candidates) {
9045
9302
  try {
@@ -9068,7 +9325,7 @@ function resolvePowerShellProfilePath() {
9068
9325
  function resolveShellAliasSetup(options = {}) {
9069
9326
  const currentPlatform = options.currentPlatform ?? platform5();
9070
9327
  const env = options.env ?? process.env;
9071
- const homeDir = options.homeDir ?? homedir33();
9328
+ const homeDir = options.homeDir ?? homedir34();
9072
9329
  const pathExists = options.exists ?? existsSync36;
9073
9330
  const shellFromEnv = env.SHELL ? basenameLikeShell(env.SHELL).toLowerCase() : "";
9074
9331
  const shellName = shellFromEnv || (currentPlatform === "win32" ? "powershell" : "");
@@ -9264,7 +9521,7 @@ async function setupShellAlias() {
9264
9521
  return;
9265
9522
  }
9266
9523
  try {
9267
- await mkdir(dirname8(setup.configFile), { recursive: true });
9524
+ await mkdir(dirname9(setup.configFile), { recursive: true });
9268
9525
  let existingContent = "";
9269
9526
  if (existsSync36(setup.configFile)) {
9270
9527
  existingContent = await readFile(setup.configFile, "utf-8");
@@ -9428,16 +9685,16 @@ function buildToolSummary(input2) {
9428
9685
  models.add(bucket.model || "unknown");
9429
9686
  }
9430
9687
  for (const session of input2.sessions) {
9431
- const projectName = session.project || "unknown";
9688
+ const projectName2 = session.project || "unknown";
9432
9689
  const modelName = session.primaryModel || "unknown";
9433
- projects.add(projectName);
9690
+ projects.add(projectName2);
9434
9691
  models.add(modelName);
9435
- const project = projectTotals.get(projectName);
9692
+ const project = projectTotals.get(projectName2);
9436
9693
  if (project) {
9437
9694
  project.sessions += 1;
9438
9695
  } else {
9439
- projectTotals.set(projectName, {
9440
- name: projectName,
9696
+ projectTotals.set(projectName2, {
9697
+ name: projectName2,
9441
9698
  inputTokens: 0,
9442
9699
  outputTokens: 0,
9443
9700
  cachedTokens: 0,
@@ -9928,7 +10185,7 @@ async function runSyncCommand(opts = {}) {
9928
10185
 
9929
10186
  // src/commands/uninstall.ts
9930
10187
  import { existsSync as existsSync37, readFileSync as readFileSync15, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
9931
- import { homedir as homedir34, platform as platform6 } from "os";
10188
+ import { homedir as homedir35, platform as platform6 } from "os";
9932
10189
  function removeShellAlias() {
9933
10190
  const shell = process.env.SHELL;
9934
10191
  if (!shell) return;
@@ -9937,17 +10194,17 @@ function removeShellAlias() {
9937
10194
  let configFile;
9938
10195
  switch (shellName) {
9939
10196
  case "zsh":
9940
- configFile = `${homedir34()}/.zshrc`;
10197
+ configFile = `${homedir35()}/.zshrc`;
9941
10198
  break;
9942
10199
  case "bash":
9943
- if (platform6() === "darwin" && existsSync37(`${homedir34()}/.bash_profile`)) {
9944
- configFile = `${homedir34()}/.bash_profile`;
10200
+ if (platform6() === "darwin" && existsSync37(`${homedir35()}/.bash_profile`)) {
10201
+ configFile = `${homedir35()}/.bash_profile`;
9945
10202
  } else {
9946
- configFile = `${homedir34()}/.bashrc`;
10203
+ configFile = `${homedir35()}/.bashrc`;
9947
10204
  }
9948
10205
  break;
9949
10206
  case "fish":
9950
- configFile = `${homedir34()}/.config/fish/config.fish`;
10207
+ configFile = `${homedir35()}/.config/fish/config.fish`;
9951
10208
  break;
9952
10209
  default:
9953
10210
  return;