@juliantanx/aiusage 1.5.7 → 1.5.8

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
@@ -13,8 +13,8 @@ import { execSync as execSync5 } from "child_process";
13
13
 
14
14
  // src/commands/serve.ts
15
15
  import http2 from "http";
16
- import { readFileSync as readFileSync10, existsSync as existsSync11, statSync as statSync4, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4 } from "fs";
17
- import { join as join12, extname as extname2, dirname as dirname5 } from "path";
16
+ import { readFileSync as readFileSync11, existsSync as existsSync12, statSync as statSync4, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4 } from "fs";
17
+ import { join as join13, extname as extname2, dirname as dirname6 } from "path";
18
18
  import { fileURLToPath } from "url";
19
19
 
20
20
  // src/api/server.ts
@@ -872,7 +872,39 @@ function sanitizeModel(value, fallback) {
872
872
  const model = lastPathSegment(value);
873
873
  return model || fallback;
874
874
  }
875
- function usageFromAny(parsed) {
875
+ function codeBuddyUsage(parsed) {
876
+ const ru = parsed?.providerData?.rawUsage;
877
+ if (ru && typeof ru === "object") {
878
+ const hit = num(ru.prompt_cache_hit_tokens ?? ru.prompt_tokens_details?.cached_tokens);
879
+ const miss = ru.prompt_cache_miss_tokens;
880
+ const prompt2 = num(ru.prompt_tokens ?? ru.input_tokens);
881
+ return {
882
+ inputTokens: miss != null ? num(miss) : Math.max(0, prompt2 - hit),
883
+ outputTokens: num(ru.completion_tokens ?? ru.output_tokens),
884
+ cacheReadTokens: hit,
885
+ cacheWriteTokens: num(ru.prompt_cache_write_tokens ?? ru.cache_creation_input_tokens),
886
+ thinkingTokens: num(ru.completion_thinking_tokens ?? ru.completion_tokens_details?.reasoning_tokens)
887
+ };
888
+ }
889
+ const mu = parsed?.message?.usage;
890
+ if (mu && typeof mu === "object") {
891
+ const cacheRead = num(mu.cache_read_input_tokens ?? mu.prompt_tokens_details?.cached_tokens);
892
+ const rawInput = num(mu.input_tokens ?? mu.prompt_tokens);
893
+ return {
894
+ inputTokens: Math.max(0, rawInput - cacheRead),
895
+ outputTokens: num(mu.output_tokens ?? mu.completion_tokens),
896
+ cacheReadTokens: cacheRead,
897
+ cacheWriteTokens: num(mu.cache_creation_input_tokens ?? mu.cache_write_input_tokens),
898
+ thinkingTokens: num(mu.thinking_tokens ?? mu.reasoning_tokens)
899
+ };
900
+ }
901
+ return null;
902
+ }
903
+ function usageFromAny(parsed, tool) {
904
+ if (tool === "codebuddy") {
905
+ const cb = codeBuddyUsage(parsed);
906
+ if (cb) return cb;
907
+ }
876
908
  const usage = parsed?.message?.usage ?? parsed?.usage ?? parsed?.event?.usage ?? parsed?.payload?.token_usage ?? parsed?.message?.payload?.token_usage ?? parsed?.providerData?.rawUsage ?? parsed?.data?.usage;
877
909
  if (!usage || typeof usage !== "object") return null;
878
910
  const details = usage.prompt_tokens_details ?? usage.input_tokens_details ?? {};
@@ -942,7 +974,7 @@ var GenericJsonlParser = class {
942
974
  }
943
975
  const normalized = parsed?.type === "context.append_loop_event" && parsed.event ? { ...parsed.event, time: parsed.time } : parsed;
944
976
  if (!shouldAccept(this.tool, normalized)) return null;
945
- const usage = usageFromAny(normalized);
977
+ const usage = usageFromAny(normalized, this.tool);
946
978
  if (!usage) return null;
947
979
  const total = usage.inputTokens + usage.outputTokens + usage.cacheReadTokens + usage.cacheWriteTokens + usage.thinkingTokens;
948
980
  if (total === 0) return null;
@@ -1603,6 +1635,55 @@ function probeCodeBuddy(ctx) {
1603
1635
  const dir = join3(ctx.env.CODEBUDDY_HOME ?? join3(ctx.home, ".codebuddy"), "projects");
1604
1636
  return existsSync3(dir) ? dir : null;
1605
1637
  }
1638
+ function codeBuddyIdeRoots(ctx) {
1639
+ const roots = [];
1640
+ if (platform() === "darwin") {
1641
+ roots.push(join3(ctx.home, "Library", "Application Support", "CodeBuddyExtension", "Data"));
1642
+ } else if (platform() === "win32") {
1643
+ const appData = ctx.env.APPDATA ?? join3(ctx.home, "AppData", "Roaming");
1644
+ roots.push(join3(appData, "CodeBuddyExtension", "Data"));
1645
+ } else {
1646
+ const config = ctx.env.XDG_CONFIG_HOME ?? join3(ctx.home, ".config");
1647
+ roots.push(join3(config, "CodeBuddyExtension", "Data"));
1648
+ }
1649
+ return unique(roots);
1650
+ }
1651
+ function probeCodeBuddyIde(ctx) {
1652
+ const override = envOverride("codebuddy-ide", ctx.env);
1653
+ if (override) return override;
1654
+ const legacy = ctx.legacySources?.["codebuddy-ide"];
1655
+ if (legacy) return legacy;
1656
+ for (const root of codeBuddyIdeRoots(ctx)) {
1657
+ if (existsSync3(root)) return root;
1658
+ }
1659
+ return null;
1660
+ }
1661
+ function countCodeBuddyIdeConversations(dir) {
1662
+ let count = 0;
1663
+ const walk = (root, depth) => {
1664
+ if (depth > 12) return;
1665
+ let entries;
1666
+ try {
1667
+ entries = readdirSync(root, { withFileTypes: true });
1668
+ } catch {
1669
+ return;
1670
+ }
1671
+ for (const entry of entries) {
1672
+ if (!entry.isDirectory()) continue;
1673
+ const full = join3(root, entry.name);
1674
+ if (entry.name === "messages") {
1675
+ try {
1676
+ if (readdirSync(full).some((f) => f.endsWith(".json"))) count++;
1677
+ } catch {
1678
+ }
1679
+ continue;
1680
+ }
1681
+ walk(full, depth + 1);
1682
+ }
1683
+ };
1684
+ walk(dir, 0);
1685
+ return count;
1686
+ }
1606
1687
  function probeKiro(ctx) {
1607
1688
  const override = envOverride("kiro", ctx.env);
1608
1689
  if (override) return override;
@@ -1810,6 +1891,7 @@ var TOOL_REGISTRY = [
1810
1891
  { tool: "gemini", sourceKey: "gemini", label: "Gemini CLI", probe: probeGemini },
1811
1892
  { tool: "kimi", sourceKey: "kimi", label: "Kimi Code", probe: probeKimi },
1812
1893
  { tool: "codebuddy", sourceKey: "codebuddy", label: "CodeBuddy", probe: probeCodeBuddy },
1894
+ { tool: "codebuddy", sourceKey: "codebuddy-ide", label: "CodeBuddy (IDE)", probe: probeCodeBuddyIde },
1813
1895
  { tool: "kiro", sourceKey: "kiro", label: "Kiro", probe: probeKiro },
1814
1896
  { tool: "grok", sourceKey: "grok", label: "Grok Build", probe: probeGrok },
1815
1897
  { tool: "antigravity", sourceKey: "antigravity", label: "Antigravity", probe: probeAntigravity },
@@ -1839,7 +1921,9 @@ function discoverTools(env = process.env) {
1839
1921
  try {
1840
1922
  const stat2 = statSync(detectedPath);
1841
1923
  if (stat2.isDirectory()) {
1842
- if (entry.sourceKey === "roocode" || entry.sourceKey === "kilocode") {
1924
+ if (entry.sourceKey === "codebuddy-ide") {
1925
+ fileCount += countCodeBuddyIdeConversations(detectedPath);
1926
+ } else if (entry.sourceKey === "roocode" || entry.sourceKey === "kilocode") {
1843
1927
  fileCount += findJsonFiles(detectedPath).filter((p) => basename(p) === "ui_messages.json").length;
1844
1928
  } else if (entry.sourceKey === "kelivo") {
1845
1929
  fileCount += findJsonFiles(detectedPath).filter((p) => basename(p) === "chats.json").length + findZipFiles(detectedPath).length;
@@ -3176,6 +3260,17 @@ function migrateV11(db) {
3176
3260
  `);
3177
3261
  }
3178
3262
 
3263
+ // src/db/migrations/v12.ts
3264
+ function migrateV12(db) {
3265
+ db.prepare(`
3266
+ UPDATE records
3267
+ SET updated_at = ?
3268
+ WHERE source_file NOT LIKE 'synced/%'
3269
+ AND (cwd != '' OR source_file LIKE '%:session:%')
3270
+ `).run(Date.now());
3271
+ db.prepare("INSERT INTO schema_version (version) VALUES (12)").run();
3272
+ }
3273
+
3179
3274
  // src/db/migrations/index.ts
3180
3275
  var MIGRATIONS = [
3181
3276
  { version: 1, migrate: migrateV1 },
@@ -3188,7 +3283,8 @@ var MIGRATIONS = [
3188
3283
  { version: 8, migrate: migrateV8 },
3189
3284
  { version: 9, migrate: migrateV9 },
3190
3285
  { version: 10, migrate: migrateV10 },
3191
- { version: 11, migrate: migrateV11 }
3286
+ { version: 11, migrate: migrateV11 },
3287
+ { version: 12, migrate: migrateV12 }
3192
3288
  ];
3193
3289
  function runMigrations(db) {
3194
3290
  createSchemaVersionTable(db);
@@ -4031,10 +4127,10 @@ function getClientPlatform() {
4031
4127
  }
4032
4128
  function getCliVersion() {
4033
4129
  try {
4034
- const { readFileSync: readFileSync13 } = __require("fs");
4035
- const { join: join17 } = __require("path");
4036
- const pkgPath = join17(__dirname, "../../package.json");
4037
- const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
4130
+ const { readFileSync: readFileSync14 } = __require("fs");
4131
+ const { join: join18 } = __require("path");
4132
+ const pkgPath = join18(__dirname, "../../package.json");
4133
+ const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
4038
4134
  return pkg.version || "0.0.0";
4039
4135
  } catch {
4040
4136
  return "0.0.0";
@@ -6245,8 +6341,8 @@ function createApiServer(db, options) {
6245
6341
 
6246
6342
  // src/commands/parse.ts
6247
6343
  import Database2 from "better-sqlite3";
6248
- import { readFileSync as readFileSync9, statSync as statSync3, existsSync as existsSync9, openSync, readSync, closeSync } from "fs";
6249
- import { join as join8, dirname as dirname4 } from "path";
6344
+ import { readFileSync as readFileSync10, statSync as statSync3, existsSync as existsSync10, openSync, readSync, closeSync } from "fs";
6345
+ import { join as join9, dirname as dirname5 } from "path";
6250
6346
  import { hostname as hostname2 } from "os";
6251
6347
 
6252
6348
  // src/watermark.ts
@@ -6297,7 +6393,7 @@ var WatermarkManager = class {
6297
6393
  if (parsed && typeof parsed === "object" && !("files" in parsed)) {
6298
6394
  return { files: { ...defaultFileData(), ...parsed } };
6299
6395
  }
6300
- return { files: { ...defaultFileData(), ...parsed.files ?? {} }, opencode: parsed.opencode ?? null, hermes: parsed.hermes ?? null, qoder: parsed.qoder ?? null, cursor: parsed.cursor ?? null, goose: parsed.goose ?? null, zed: parsed.zed ?? null, kiro: parsed.kiro ?? null, zcode: parsed.zcode ?? null, zcodeTools: parsed.zcodeTools ?? null, trae: parsed.trae ?? null };
6396
+ return { files: { ...defaultFileData(), ...parsed.files ?? {} }, opencode: parsed.opencode ?? null, hermes: parsed.hermes ?? null, qoder: parsed.qoder ?? null, cursor: parsed.cursor ?? null, goose: parsed.goose ?? null, zed: parsed.zed ?? null, kiro: parsed.kiro ?? null, zcode: parsed.zcode ?? null, zcodeTools: parsed.zcodeTools ?? null, trae: parsed.trae ?? null, codebuddyIde: parsed.codebuddyIde ?? null };
6301
6397
  } catch {
6302
6398
  return { files: defaultFileData() };
6303
6399
  }
@@ -6384,6 +6480,12 @@ var WatermarkManager = class {
6384
6480
  setTraeLastImported(ts3) {
6385
6481
  this.data.trae = ts3;
6386
6482
  }
6483
+ getCodeBuddyIdeCursor() {
6484
+ return this.data.codebuddyIde ?? 0;
6485
+ }
6486
+ setCodeBuddyIdeCursor(ts3) {
6487
+ this.data.codebuddyIde = ts3;
6488
+ }
6387
6489
  };
6388
6490
 
6389
6491
  // src/commands/parse-opencode.ts
@@ -7430,6 +7532,173 @@ function runParseTrae(options) {
7430
7532
  return { records, toolCalls, lastImportedAt: latestTs, errors };
7431
7533
  }
7432
7534
 
7535
+ // src/commands/parse-codebuddy.ts
7536
+ import { existsSync as existsSync9, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
7537
+ import { join as join8, basename as basename4, dirname as dirname4 } from "path";
7538
+ function num3(value) {
7539
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
7540
+ }
7541
+ function toMs(iso) {
7542
+ if (typeof iso !== "string") return 0;
7543
+ const ms = Date.parse(iso);
7544
+ return Number.isFinite(ms) ? ms : 0;
7545
+ }
7546
+ var WORKSPACE_FOLDER_RE = /Workspace Folder:\s*(.+)/;
7547
+ function userMessageText(raw) {
7548
+ try {
7549
+ const inner = JSON.parse(raw);
7550
+ if (Array.isArray(inner.content)) {
7551
+ return inner.content.filter((block) => block?.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n");
7552
+ }
7553
+ } catch {
7554
+ }
7555
+ return raw;
7556
+ }
7557
+ function findMessagesDirs(root, depth = 0) {
7558
+ if (depth > 12) return [];
7559
+ let entries;
7560
+ try {
7561
+ entries = readdirSync3(root, { withFileTypes: true });
7562
+ } catch {
7563
+ return [];
7564
+ }
7565
+ const out = [];
7566
+ for (const entry of entries) {
7567
+ if (!entry.isDirectory()) continue;
7568
+ const full = join8(root, entry.name);
7569
+ if (entry.name === "messages") {
7570
+ out.push(full);
7571
+ continue;
7572
+ }
7573
+ out.push(...findMessagesDirs(full, depth + 1));
7574
+ }
7575
+ return out;
7576
+ }
7577
+ function readConversation(messagesDir) {
7578
+ let files;
7579
+ try {
7580
+ files = readdirSync3(messagesDir).filter((f) => f.endsWith(".json"));
7581
+ } catch {
7582
+ return null;
7583
+ }
7584
+ if (files.length === 0) return null;
7585
+ const parsed = [];
7586
+ for (const file of files) {
7587
+ try {
7588
+ const data = JSON.parse(readFileSync9(join8(messagesDir, file), "utf-8"));
7589
+ if (data && typeof data === "object") parsed.push(data);
7590
+ } catch {
7591
+ }
7592
+ }
7593
+ if (parsed.length === 0) return null;
7594
+ let cwd;
7595
+ for (const msg of parsed) {
7596
+ if (msg.role !== "user" || typeof msg.message !== "string") continue;
7597
+ const match = WORKSPACE_FOLDER_RE.exec(userMessageText(msg.message));
7598
+ if (match) {
7599
+ cwd = match[1].trim();
7600
+ break;
7601
+ }
7602
+ }
7603
+ let latest = null;
7604
+ for (const msg of parsed) {
7605
+ if (msg.role !== "assistant" || typeof msg.extra !== "string") continue;
7606
+ let extra2;
7607
+ try {
7608
+ extra2 = JSON.parse(msg.extra);
7609
+ } catch {
7610
+ continue;
7611
+ }
7612
+ const hasStats = extra2.statsSnapshot != null || extra2.lastStepInputTokens != null;
7613
+ if (!hasStats) continue;
7614
+ const ts3 = toMs(msg.createdAt);
7615
+ if (!latest || ts3 >= latest.ts) latest = { ts: ts3, extra: extra2 };
7616
+ }
7617
+ if (!latest) return null;
7618
+ const extra = latest.extra;
7619
+ const snapshot = extra.statsSnapshot ?? {};
7620
+ const cacheReadTokens = num3(snapshot.cachedInputTokens ?? extra.lastStepCachedInputTokens);
7621
+ const inputTokens = snapshot.cacheMissTokens != null ? num3(snapshot.cacheMissTokens) : Math.max(0, num3(extra.lastStepInputTokens) - cacheReadTokens);
7622
+ const outputTokens = num3(snapshot.lastOutputTokens ?? extra.lastStepOutputTokens);
7623
+ const cacheWriteTokens = num3(snapshot.cacheWriteTokens);
7624
+ const thinkingTokens = num3(snapshot.thinkingTokens);
7625
+ const model = typeof extra.modelId === "string" && extra.modelId.trim() ? extra.modelId.trim() : "unknown";
7626
+ return {
7627
+ ts: latest.ts,
7628
+ model,
7629
+ inputTokens,
7630
+ outputTokens,
7631
+ cacheReadTokens,
7632
+ cacheWriteTokens,
7633
+ thinkingTokens,
7634
+ cwd
7635
+ };
7636
+ }
7637
+ function runParseCodeBuddy(options) {
7638
+ const { dataDir, device, deviceInstanceId, platform: platform5, now, cursor, exchangeRate } = options;
7639
+ const records = [];
7640
+ const errors = [];
7641
+ let nextCursor = cursor ?? 0;
7642
+ if (!existsSync9(dataDir)) {
7643
+ return { records, nextCursor, errors };
7644
+ }
7645
+ let messagesDirs;
7646
+ try {
7647
+ messagesDirs = findMessagesDirs(dataDir);
7648
+ } catch (e) {
7649
+ return { records, nextCursor, errors: [String(e)] };
7650
+ }
7651
+ for (const messagesDir of messagesDirs) {
7652
+ const conversationDir = dirname4(messagesDir);
7653
+ const sessionId = basename4(conversationDir);
7654
+ let usage;
7655
+ try {
7656
+ usage = readConversation(messagesDir);
7657
+ } catch (e) {
7658
+ errors.push(`${messagesDir}: ${e instanceof Error ? e.message : e}`);
7659
+ continue;
7660
+ }
7661
+ if (!usage) continue;
7662
+ if (usage.inputTokens + usage.outputTokens + usage.cacheReadTokens === 0) continue;
7663
+ if (cursor && usage.ts <= cursor) continue;
7664
+ if (usage.ts > nextCursor) nextCursor = usage.ts;
7665
+ const provider = inferProvider(usage.model);
7666
+ const tokenArgs = {
7667
+ inputTokens: usage.inputTokens,
7668
+ outputTokens: usage.outputTokens,
7669
+ cacheReadTokens: usage.cacheReadTokens,
7670
+ cacheWriteTokens: usage.cacheWriteTokens,
7671
+ thinkingTokens: usage.thinkingTokens
7672
+ };
7673
+ const cost = calculateCost(usage.model, tokenArgs, exchangeRate);
7674
+ const recordId = generateRecordId(deviceInstanceId, conversationDir, usage.ts);
7675
+ records.push({
7676
+ id: recordId,
7677
+ ts: usage.ts,
7678
+ ingestedAt: now,
7679
+ updatedAt: now,
7680
+ lineOffset: 0,
7681
+ tool: "codebuddy",
7682
+ model: usage.model,
7683
+ provider,
7684
+ inputTokens: usage.inputTokens,
7685
+ outputTokens: usage.outputTokens,
7686
+ cacheReadTokens: usage.cacheReadTokens,
7687
+ cacheWriteTokens: usage.cacheWriteTokens,
7688
+ thinkingTokens: usage.thinkingTokens,
7689
+ cost,
7690
+ costSource: cost > 0 ? "pricing" : "unknown",
7691
+ sessionId,
7692
+ sourceFile: conversationDir,
7693
+ cwd: usage.cwd,
7694
+ device,
7695
+ deviceInstanceId,
7696
+ platform: platform5
7697
+ });
7698
+ }
7699
+ return { records, nextCursor, errors };
7700
+ }
7701
+
7433
7702
  // src/commands/parse.ts
7434
7703
  function extractCwdFromJson(data) {
7435
7704
  if (typeof data.cwd === "string" && data.cwd) return data.cwd;
@@ -7484,7 +7753,7 @@ function parseUiMessagesFile(options) {
7484
7753
  const errors = [];
7485
7754
  let messages;
7486
7755
  try {
7487
- const parsed = JSON.parse(readFileSync9(filePath, "utf-8"));
7756
+ const parsed = JSON.parse(readFileSync10(filePath, "utf-8"));
7488
7757
  messages = Array.isArray(parsed) ? parsed : [];
7489
7758
  } catch (e) {
7490
7759
  return { records, errors: [`${filePath}: ${e instanceof Error ? e.message : e}`] };
@@ -7562,7 +7831,7 @@ function parseKiroSessionFile(options) {
7562
7831
  const errors = [];
7563
7832
  let parsed;
7564
7833
  try {
7565
- parsed = JSON.parse(readFileSync9(filePath, "utf-8"));
7834
+ parsed = JSON.parse(readFileSync10(filePath, "utf-8"));
7566
7835
  } catch (e) {
7567
7836
  return { records, errors: [`${filePath}: ${e instanceof Error ? e.message : e}`] };
7568
7837
  }
@@ -7617,8 +7886,8 @@ function parseKiroWorkspaceSession(options) {
7617
7886
  const sessionId = parsed?.sessionId ?? extractSessionId2(filePath, "kiro");
7618
7887
  let sessionTs = now;
7619
7888
  try {
7620
- const dir = dirname4(filePath);
7621
- const sessionsJson = readFileSync9(join8(dir, "sessions.json"), "utf-8");
7889
+ const dir = dirname5(filePath);
7890
+ const sessionsJson = readFileSync10(join9(dir, "sessions.json"), "utf-8");
7622
7891
  const sessions = JSON.parse(sessionsJson);
7623
7892
  const match = Array.isArray(sessions) && sessions.find((s) => s.sessionId === sessionId);
7624
7893
  if (match?.dateCreated) {
@@ -7694,7 +7963,7 @@ async function runParse(db, filterTool, options) {
7694
7963
  const device = config?.device || hostname2() || state?.deviceInstanceId?.slice(0, 8) || "unknown";
7695
7964
  const deviceInstanceId = state?.deviceInstanceId ?? "unknown";
7696
7965
  const devicePlatform = config?.platform;
7697
- const watermarkPath = join8(AIUSAGE_DIR, "watermark.json");
7966
+ const watermarkPath = join9(AIUSAGE_DIR, "watermark.json");
7698
7967
  const wm = new WatermarkManager(watermarkPath);
7699
7968
  const toolPaths = discoverLogFiles();
7700
7969
  const aggregator = new Aggregator();
@@ -7765,7 +8034,7 @@ async function runParse(db, filterTool, options) {
7765
8034
  continue;
7766
8035
  }
7767
8036
  if (tool === "kiro" && filePath.endsWith(".jsonl")) {
7768
- const content2 = readFileSync9(filePath, "utf-8");
8037
+ const content2 = readFileSync10(filePath, "utf-8");
7769
8038
  const lines2 = content2.split("\n");
7770
8039
  let byteOffset2 = 0;
7771
8040
  for (const line of lines2) {
@@ -7895,7 +8164,7 @@ async function runParse(db, filterTool, options) {
7895
8164
  onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
7896
8165
  continue;
7897
8166
  }
7898
- const content = readFileSync9(filePath, "utf-8");
8167
+ const content = readFileSync10(filePath, "utf-8");
7899
8168
  const lines = content.split("\n");
7900
8169
  let byteOffset = 0;
7901
8170
  let fileCwd;
@@ -7959,7 +8228,7 @@ async function runParse(db, filterTool, options) {
7959
8228
  }
7960
8229
  }
7961
8230
  if (fileCwd) {
7962
- db.prepare(`UPDATE records SET cwd = ? WHERE source_file = ? AND cwd = ''`).run(fileCwd, filePath);
8231
+ db.prepare(`UPDATE records SET cwd = ?, updated_at = ? WHERE source_file = ? AND cwd = ''`).run(fileCwd, Date.now(), filePath);
7963
8232
  }
7964
8233
  wm.setEntry(tool, filePath, {
7965
8234
  offset: stat2.size,
@@ -7974,7 +8243,7 @@ async function runParse(db, filterTool, options) {
7974
8243
  }
7975
8244
  }
7976
8245
  const openCodeDbPath = options?.openCodeDbPath ?? getDbPath("opencode") ?? "";
7977
- if ((!filterTool || filterTool === "opencode") && existsSync9(openCodeDbPath)) {
8246
+ if ((!filterTool || filterTool === "opencode") && existsSync10(openCodeDbPath)) {
7978
8247
  try {
7979
8248
  const openCodeDb = new Database2(openCodeDbPath, { readonly: true });
7980
8249
  try {
@@ -8005,7 +8274,7 @@ async function runParse(db, filterTool, options) {
8005
8274
  }
8006
8275
  }
8007
8276
  const hermesDbPath = options?.hermesDbPath ?? getDbPath("hermes") ?? "";
8008
- if ((!filterTool || filterTool === "hermes") && existsSync9(hermesDbPath)) {
8277
+ if ((!filterTool || filterTool === "hermes") && existsSync10(hermesDbPath)) {
8009
8278
  try {
8010
8279
  const hermesDb = new Database2(hermesDbPath, { readonly: true });
8011
8280
  try {
@@ -8036,7 +8305,7 @@ async function runParse(db, filterTool, options) {
8036
8305
  }
8037
8306
  }
8038
8307
  const qoderDbPath = options?.qoderDbPath ?? getDbPath("qoder-db") ?? "";
8039
- if ((!filterTool || filterTool === "qoder") && existsSync9(qoderDbPath)) {
8308
+ if ((!filterTool || filterTool === "qoder") && existsSync10(qoderDbPath)) {
8040
8309
  try {
8041
8310
  const qoderDb = new Database2(qoderDbPath, { readonly: true });
8042
8311
  try {
@@ -8067,7 +8336,7 @@ async function runParse(db, filterTool, options) {
8067
8336
  }
8068
8337
  }
8069
8338
  const cursorDbPath = options?.cursorDbPath ?? getDbPath("cursor") ?? "";
8070
- if ((!filterTool || filterTool === "cursor") && existsSync9(cursorDbPath)) {
8339
+ if ((!filterTool || filterTool === "cursor") && existsSync10(cursorDbPath)) {
8071
8340
  try {
8072
8341
  const cursorDb = new Database2(cursorDbPath, { readonly: true });
8073
8342
  try {
@@ -8097,7 +8366,7 @@ async function runParse(db, filterTool, options) {
8097
8366
  }
8098
8367
  }
8099
8368
  const kiloDbPath = getDbPath("kilocode-db") ?? "";
8100
- if ((!filterTool || filterTool === "kilocode") && existsSync9(kiloDbPath)) {
8369
+ if ((!filterTool || filterTool === "kilocode") && existsSync10(kiloDbPath)) {
8101
8370
  try {
8102
8371
  const kiloDb = new Database2(kiloDbPath, { readonly: true });
8103
8372
  try {
@@ -8137,7 +8406,7 @@ async function runParse(db, filterTool, options) {
8137
8406
  }
8138
8407
  }
8139
8408
  const gooseDbPath = getDbPath("goose") ?? "";
8140
- if ((!filterTool || filterTool === "goose") && existsSync9(gooseDbPath)) {
8409
+ if ((!filterTool || filterTool === "goose") && existsSync10(gooseDbPath)) {
8141
8410
  try {
8142
8411
  const gooseDb = new Database2(gooseDbPath, { readonly: true });
8143
8412
  try {
@@ -8195,7 +8464,7 @@ async function runParse(db, filterTool, options) {
8195
8464
  }
8196
8465
  }
8197
8466
  const zedDbPath = getDbPath("zed") ?? "";
8198
- if ((!filterTool || filterTool === "zed") && existsSync9(zedDbPath)) {
8467
+ if ((!filterTool || filterTool === "zed") && existsSync10(zedDbPath)) {
8199
8468
  try {
8200
8469
  const zedDb = new Database2(zedDbPath, { readonly: true });
8201
8470
  try {
@@ -8225,7 +8494,7 @@ async function runParse(db, filterTool, options) {
8225
8494
  }
8226
8495
  if (!filterTool || filterTool === "zcode") {
8227
8496
  const zcodeDbPath = getDbPath("zcode") ?? "";
8228
- if (existsSync9(zcodeDbPath)) {
8497
+ if (existsSync10(zcodeDbPath)) {
8229
8498
  try {
8230
8499
  const zcodeDb = new Database2(zcodeDbPath, { readonly: true });
8231
8500
  try {
@@ -8284,6 +8553,31 @@ async function runParse(db, filterTool, options) {
8284
8553
  }
8285
8554
  onProgress({ phase: "Parsing SQLite", tool: "trae", current: 1, total: 1, records: parsedCount, toolCalls: toolCallCount });
8286
8555
  }
8556
+ const codeBuddyIdeDir = getDbPath("codebuddy-ide") ?? "";
8557
+ if ((!filterTool || filterTool === "codebuddy") && existsSync10(codeBuddyIdeDir)) {
8558
+ try {
8559
+ const cbCursor = wm.getCodeBuddyIdeCursor();
8560
+ const result = runParseCodeBuddy({
8561
+ dataDir: codeBuddyIdeDir,
8562
+ device,
8563
+ deviceInstanceId,
8564
+ platform: devicePlatform,
8565
+ now: Date.now(),
8566
+ cursor: cbCursor,
8567
+ exchangeRate
8568
+ });
8569
+ for (const record of result.records) insertRecord(db, record);
8570
+ parsedCount += result.records.length;
8571
+ errors.push(...result.errors);
8572
+ if (result.nextCursor > cbCursor) {
8573
+ wm.setCodeBuddyIdeCursor(result.nextCursor);
8574
+ wm.save();
8575
+ }
8576
+ onProgress({ phase: "Parsing logs", tool: "codebuddy", current: 1, total: 1, records: parsedCount, toolCalls: toolCallCount });
8577
+ } catch (e) {
8578
+ errors.push(`${codeBuddyIdeDir}: ${e instanceof Error ? e.message : e}`);
8579
+ }
8580
+ }
8287
8581
  if (deviceInstanceId !== "unknown") {
8288
8582
  db.prepare(
8289
8583
  `UPDATE records SET device_instance_id = ?, device = ? WHERE device_instance_id = 'unknown'`
@@ -8295,9 +8589,19 @@ async function runParse(db, filterTool, options) {
8295
8589
  ).run(devicePlatform);
8296
8590
  }
8297
8591
  backfillHermesSourceFiles(db);
8592
+ backfillCwd(db);
8593
+ backfillSkillNames(db);
8594
+ backfillCodexModels(db);
8595
+ backfillMissingToolCalls(db, exchangeRate);
8596
+ return { parsedCount, toolCallCount, errors };
8597
+ }
8598
+ function backfillCwd(db) {
8298
8599
  const staleFiles = db.prepare(
8299
8600
  `SELECT DISTINCT source_file FROM records WHERE cwd = '' AND source_file NOT LIKE 'synced/%'`
8300
8601
  ).all();
8602
+ const updateStmt = db.prepare(
8603
+ `UPDATE records SET cwd = ?, updated_at = ? WHERE source_file = ? AND cwd = ''`
8604
+ );
8301
8605
  for (const { source_file } of staleFiles) {
8302
8606
  try {
8303
8607
  const fd = openSync(source_file, "r");
@@ -8308,15 +8612,11 @@ async function runParse(db, filterTool, options) {
8308
8612
  const data = JSON.parse(firstLine);
8309
8613
  const cwd = extractCwdFromJson(data);
8310
8614
  if (cwd) {
8311
- db.prepare(`UPDATE records SET cwd = ? WHERE source_file = ? AND cwd = ''`).run(cwd, source_file);
8615
+ updateStmt.run(cwd, Date.now(), source_file);
8312
8616
  }
8313
8617
  } catch {
8314
8618
  }
8315
8619
  }
8316
- backfillSkillNames(db);
8317
- backfillCodexModels(db);
8318
- backfillMissingToolCalls(db, exchangeRate);
8319
- return { parsedCount, toolCallCount, errors };
8320
8620
  }
8321
8621
  function backfillHermesSourceFiles(db) {
8322
8622
  const rows = db.prepare(`
@@ -8330,7 +8630,7 @@ function backfillHermesSourceFiles(db) {
8330
8630
  const dbPath = rows[0].source_file;
8331
8631
  let titleMap = /* @__PURE__ */ new Map();
8332
8632
  try {
8333
- if (existsSync9(dbPath)) {
8633
+ if (existsSync10(dbPath)) {
8334
8634
  const hermesDb = new Database2(dbPath, { readonly: true });
8335
8635
  try {
8336
8636
  const sessions = hermesDb.prepare(
@@ -8343,11 +8643,11 @@ function backfillHermesSourceFiles(db) {
8343
8643
  }
8344
8644
  } catch {
8345
8645
  }
8346
- const updateStmt = db.prepare(`UPDATE records SET source_file = ? WHERE tool = 'hermes' AND session_id = ? AND source_file = ?`);
8646
+ const updateStmt = db.prepare(`UPDATE records SET source_file = ?, updated_at = ? WHERE tool = 'hermes' AND session_id = ? AND source_file = ?`);
8347
8647
  for (const row of rows) {
8348
8648
  const title = (titleMap.get(row.session_id) || "").replace(/[/\\:]/g, "_").slice(0, 80);
8349
8649
  const newSourceFile = title ? `${row.source_file}:session:${row.session_id}:${title}` : `${row.source_file}:session:${row.session_id}`;
8350
- updateStmt.run(newSourceFile, row.session_id, row.source_file);
8650
+ updateStmt.run(newSourceFile, Date.now(), row.session_id, row.source_file);
8351
8651
  }
8352
8652
  }
8353
8653
  function backfillSkillNames(db) {
@@ -8411,7 +8711,7 @@ function backfillCodexModels(db) {
8411
8711
  );
8412
8712
  for (const [sourceFile, fileRows] of byFile) {
8413
8713
  try {
8414
- const content = readFileSync9(sourceFile, "utf-8");
8714
+ const content = readFileSync10(sourceFile, "utf-8");
8415
8715
  const lines = content.split("\n");
8416
8716
  const offsetToModel = /* @__PURE__ */ new Map();
8417
8717
  let currentModel = "";
@@ -8459,7 +8759,7 @@ function backfillMissingToolCalls(db, exchangeRate) {
8459
8759
  }
8460
8760
  for (const [sourceFile, fileRows] of rowsByFile) {
8461
8761
  try {
8462
- const content = readFileSync9(sourceFile, "utf-8");
8762
+ const content = readFileSync10(sourceFile, "utf-8");
8463
8763
  const lines = content.split("\n");
8464
8764
  const aggregator = new Aggregator();
8465
8765
  const recordIdByOffset = /* @__PURE__ */ new Map();
@@ -8521,7 +8821,7 @@ function deriveSessionId(tool, sourceFile) {
8521
8821
  }
8522
8822
 
8523
8823
  // src/commands/sync.ts
8524
- import { join as join10 } from "path";
8824
+ import { join as join11 } from "path";
8525
8825
 
8526
8826
  // src/db/synced-records.ts
8527
8827
  function insertSyncedRecord(db, record) {
@@ -8954,10 +9254,10 @@ async function cloudClear() {
8954
9254
  }
8955
9255
  function getClientVersion() {
8956
9256
  try {
8957
- const { readFileSync: readFileSync13 } = __require("fs");
8958
- const { join: join17 } = __require("path");
8959
- const pkgPath = join17(__dirname, "../../package.json");
8960
- const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
9257
+ const { readFileSync: readFileSync14 } = __require("fs");
9258
+ const { join: join18 } = __require("path");
9259
+ const pkgPath = join18(__dirname, "../../package.json");
9260
+ const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
8961
9261
  return pkg.version || "0.0.0";
8962
9262
  } catch {
8963
9263
  return "0.0.0";
@@ -9048,7 +9348,7 @@ var CloudSyncOrchestrator = class {
9048
9348
  import { execFile } from "child_process";
9049
9349
  import { promisify } from "util";
9050
9350
  import { readFile, writeFile, mkdir, readdir, stat, unlink, rm } from "fs/promises";
9051
- import { join as join9 } from "path";
9351
+ import { join as join10 } from "path";
9052
9352
  var exec = promisify(execFile);
9053
9353
  var GitSyncBackend = class _GitSyncBackend {
9054
9354
  repo;
@@ -9060,7 +9360,7 @@ var GitSyncBackend = class _GitSyncBackend {
9060
9360
  this.repo = config.repo;
9061
9361
  this.token = config.token;
9062
9362
  this.cacheDir = config.cacheDir;
9063
- this.dataDir = join9(config.cacheDir, "data");
9363
+ this.dataDir = join10(config.cacheDir, "data");
9064
9364
  this.branch = config.branch ?? "main";
9065
9365
  }
9066
9366
  get remoteUrl() {
@@ -9092,7 +9392,7 @@ var GitSyncBackend = class _GitSyncBackend {
9092
9392
  /** Clone or pull the repo to get latest remote state */
9093
9393
  async prepare() {
9094
9394
  try {
9095
- await stat(join9(this.cacheDir, ".git"));
9395
+ await stat(join10(this.cacheDir, ".git"));
9096
9396
  await this.git(["fetch", "origin", this.branch, "--depth=1"]);
9097
9397
  await this.git(["reset", "--hard", `origin/${this.branch}`]);
9098
9398
  } catch {
@@ -9109,15 +9409,15 @@ var GitSyncBackend = class _GitSyncBackend {
9109
9409
  }
9110
9410
  async readFile(path3) {
9111
9411
  try {
9112
- const fullPath = join9(this.dataDir, path3);
9412
+ const fullPath = join10(this.dataDir, path3);
9113
9413
  return await readFile(fullPath, "utf-8");
9114
9414
  } catch {
9115
9415
  return null;
9116
9416
  }
9117
9417
  }
9118
9418
  async writeFile(path3, content) {
9119
- const fullPath = join9(this.dataDir, path3);
9120
- await mkdir(join9(fullPath, ".."), { recursive: true });
9419
+ const fullPath = join10(this.dataDir, path3);
9420
+ await mkdir(join10(fullPath, ".."), { recursive: true });
9121
9421
  await writeFile(fullPath, content, "utf-8");
9122
9422
  }
9123
9423
  async listFiles() {
@@ -9128,7 +9428,7 @@ var GitSyncBackend = class _GitSyncBackend {
9128
9428
  }
9129
9429
  }
9130
9430
  async deleteFile(path3) {
9131
- const fullPath = join9(this.dataDir, path3);
9431
+ const fullPath = join10(this.dataDir, path3);
9132
9432
  try {
9133
9433
  await unlink(fullPath);
9134
9434
  } catch {
@@ -9167,7 +9467,7 @@ var GitSyncBackend = class _GitSyncBackend {
9167
9467
  for (const entry of entries) {
9168
9468
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
9169
9469
  if (entry.isDirectory()) {
9170
- files.push(...await this.walkDir(join9(dir, entry.name), relPath));
9470
+ files.push(...await this.walkDir(join10(dir, entry.name), relPath));
9171
9471
  } else if (entry.name.endsWith(".ndjson")) {
9172
9472
  files.push(relPath);
9173
9473
  }
@@ -9301,7 +9601,7 @@ function createBackend(config) {
9301
9601
  return new GitSyncBackend({
9302
9602
  repo: sync.repo,
9303
9603
  token,
9304
- cacheDir: join10(AIUSAGE_DIR, "sync-repo")
9604
+ cacheDir: join11(AIUSAGE_DIR, "sync-repo")
9305
9605
  });
9306
9606
  }
9307
9607
  if (sync.backend === "s3") {
@@ -9396,8 +9696,8 @@ async function runSync(db, options) {
9396
9696
  }
9397
9697
 
9398
9698
  // src/commands/clean.ts
9399
- import { unlinkSync as unlinkSync3, existsSync as existsSync10 } from "fs";
9400
- import { join as join11 } from "path";
9699
+ import { unlinkSync as unlinkSync3, existsSync as existsSync11 } from "fs";
9700
+ import { join as join12 } from "path";
9401
9701
  function cleanOldData(db, days) {
9402
9702
  const cutoff = Date.now() - days * 864e5;
9403
9703
  const recordsResult = db.prepare("DELETE FROM records WHERE ts < ?").run(cutoff);
@@ -9418,9 +9718,9 @@ function cleanAll(db) {
9418
9718
  const syncedResult = db.prepare("DELETE FROM synced_records").run();
9419
9719
  const syncStateResult = db.prepare("DELETE FROM sync_record_state").run();
9420
9720
  const tombstonesResult = db.prepare("DELETE FROM sync_tombstones").run();
9421
- const watermarkPath = join11(AIUSAGE_DIR, "watermark.json");
9721
+ const watermarkPath = join12(AIUSAGE_DIR, "watermark.json");
9422
9722
  let watermarkRemoved = false;
9423
- if (existsSync10(watermarkPath)) {
9723
+ if (existsSync11(watermarkPath)) {
9424
9724
  unlinkSync3(watermarkPath);
9425
9725
  watermarkRemoved = true;
9426
9726
  }
@@ -9455,7 +9755,7 @@ function createBackend2(config) {
9455
9755
  return new GitSyncBackend({
9456
9756
  repo: config.sync.repo,
9457
9757
  token,
9458
- cacheDir: join11(AIUSAGE_DIR, "sync-repo")
9758
+ cacheDir: join12(AIUSAGE_DIR, "sync-repo")
9459
9759
  });
9460
9760
  }
9461
9761
  if (config.sync.backend === "s3") {
@@ -9788,7 +10088,7 @@ var RuntimeSettingsController = class {
9788
10088
 
9789
10089
  // src/commands/serve.ts
9790
10090
  var MAX_PORT_ATTEMPTS = 10;
9791
- var PORT_FILE = join12(AIUSAGE_DIR, ".serve-port");
10091
+ var PORT_FILE = join13(AIUSAGE_DIR, ".serve-port");
9792
10092
  var MIME_TYPES = {
9793
10093
  ".html": "text/html",
9794
10094
  ".js": "application/javascript",
@@ -9863,11 +10163,11 @@ function serve(options) {
9863
10163
  getDbWriteQueueStatus: () => dbWriteQueue.getStatus()
9864
10164
  });
9865
10165
  const webBuildDir = (() => {
9866
- const prodDir = join12(dirname5(fileURLToPath(import.meta.url)), "web");
9867
- if (existsSync11(prodDir)) return prodDir;
9868
- return join12(dirname5(fileURLToPath(import.meta.url)), "..", "..", "..", "web", "build");
10166
+ const prodDir = join13(dirname6(fileURLToPath(import.meta.url)), "web");
10167
+ if (existsSync12(prodDir)) return prodDir;
10168
+ return join13(dirname6(fileURLToPath(import.meta.url)), "..", "..", "..", "web", "build");
9869
10169
  })();
9870
- if (!existsSync11(webBuildDir)) {
10170
+ if (!existsSync12(webBuildDir)) {
9871
10171
  console.error("Web dashboard not found. Reinstall the package: npm install -g @juliantanx/aiusage");
9872
10172
  process.exit(1);
9873
10173
  }
@@ -9877,19 +10177,19 @@ function serve(options) {
9877
10177
  apiServer.emit("request", req, res);
9878
10178
  return;
9879
10179
  }
9880
- if (existsSync11(webBuildDir)) {
9881
- let filePath = join12(webBuildDir, url.pathname);
10180
+ if (existsSync12(webBuildDir)) {
10181
+ let filePath = join13(webBuildDir, url.pathname);
9882
10182
  try {
9883
10183
  if (statSync4(filePath).isDirectory()) {
9884
- filePath = join12(filePath, "index.html");
10184
+ filePath = join13(filePath, "index.html");
9885
10185
  }
9886
10186
  } catch {
9887
10187
  }
9888
- if (!existsSync11(filePath)) {
9889
- filePath = join12(webBuildDir, "index.html");
10188
+ if (!existsSync12(filePath)) {
10189
+ filePath = join13(webBuildDir, "index.html");
9890
10190
  }
9891
10191
  try {
9892
- const content = readFileSync10(filePath);
10192
+ const content = readFileSync11(filePath);
9893
10193
  const ext = extname2(filePath);
9894
10194
  const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
9895
10195
  res.writeHead(200, { "Content-Type": contentType });
@@ -10071,9 +10371,9 @@ Consent recorded for schema v1.`
10071
10371
  }
10072
10372
 
10073
10373
  // src/commands/status.ts
10074
- import { statSync as statSync5, existsSync as existsSync12 } from "fs";
10374
+ import { statSync as statSync5, existsSync as existsSync13 } from "fs";
10075
10375
  import { hostname as hostname3 } from "os";
10076
- import { join as join13 } from "path";
10376
+ import { join as join14 } from "path";
10077
10377
  function formatFileSize(bytes) {
10078
10378
  if (bytes < 1024) return `${bytes} B`;
10079
10379
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -10091,9 +10391,9 @@ function generateStatus(db) {
10091
10391
  const schemaVersion = db.prepare("SELECT version FROM schema_version ORDER BY version DESC LIMIT 1").get()?.version ?? 0;
10092
10392
  const tableCount = db.prepare("SELECT COUNT(*) as count FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'").get()?.count ?? 0;
10093
10393
  const viewCount = db.prepare("SELECT COUNT(*) as count FROM sqlite_master WHERE type = 'view'").get()?.count ?? 0;
10094
- const sizePath = join13(AIUSAGE_DIR, "cache.db");
10394
+ const sizePath = join14(AIUSAGE_DIR, "cache.db");
10095
10395
  let databaseSize = "0 B";
10096
- if (existsSync12(sizePath)) {
10396
+ if (existsSync13(sizePath)) {
10097
10397
  try {
10098
10398
  const stat2 = statSync5(sizePath);
10099
10399
  databaseSize = formatFileSize(stat2.size);
@@ -10316,10 +10616,10 @@ var SyncProgressReporter = class {
10316
10616
  };
10317
10617
 
10318
10618
  // src/commands/widget.ts
10319
- import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
10619
+ import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
10320
10620
  import { spawn, execSync as execSync2 } from "child_process";
10321
- import { join as join14 } from "path";
10322
- var WIDGET_PID_PATH = join14(AIUSAGE_DIR, "widget.pid");
10621
+ import { join as join15 } from "path";
10622
+ var WIDGET_PID_PATH = join15(AIUSAGE_DIR, "widget.pid");
10323
10623
  async function launchWidget() {
10324
10624
  if (isWidgetRunning()) {
10325
10625
  console.log("aiusage widget is already running in the system tray.");
@@ -10341,10 +10641,10 @@ async function launchWidget() {
10341
10641
  console.log("aiusage widget started.");
10342
10642
  }
10343
10643
  function isWidgetRunning() {
10344
- if (!existsSync13(WIDGET_PID_PATH)) return false;
10644
+ if (!existsSync14(WIDGET_PID_PATH)) return false;
10345
10645
  let pid;
10346
10646
  try {
10347
- pid = parseInt(readFileSync11(WIDGET_PID_PATH, "utf-8").trim(), 10);
10647
+ pid = parseInt(readFileSync12(WIDGET_PID_PATH, "utf-8").trim(), 10);
10348
10648
  } catch {
10349
10649
  return false;
10350
10650
  }
@@ -10388,10 +10688,10 @@ function getDeviceName2() {
10388
10688
  }
10389
10689
  function getCliVersion2() {
10390
10690
  try {
10391
- const { readFileSync: readFileSync13 } = __require("fs");
10392
- const { join: join17 } = __require("path");
10393
- const pkgPath = join17(__dirname, "../../package.json");
10394
- const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
10691
+ const { readFileSync: readFileSync14 } = __require("fs");
10692
+ const { join: join18 } = __require("path");
10693
+ const pkgPath = join18(__dirname, "../../package.json");
10694
+ const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
10395
10695
  return pkg.version || "0.0.0";
10396
10696
  } catch {
10397
10697
  return "0.0.0";
@@ -10560,8 +10860,8 @@ function formatTokens(value) {
10560
10860
  return Number(value).toLocaleString();
10561
10861
  }
10562
10862
  function formatCost(value) {
10563
- const num3 = Number(value);
10564
- return Number.isFinite(num3) ? `$${num3.toFixed(4)}` : "$0.0000";
10863
+ const num4 = Number(value);
10864
+ return Number.isFinite(num4) ? `$${num4.toFixed(4)}` : "$0.0000";
10565
10865
  }
10566
10866
  function formatUpdatedAt(value) {
10567
10867
  const date = new Date(value);
@@ -10635,16 +10935,16 @@ async function runLeaderboardView(options = {}) {
10635
10935
  // src/commands/menu.ts
10636
10936
  import { createInterface } from "readline";
10637
10937
  import { spawn as spawn2, spawnSync as spawnSync2, execSync as execSync4 } from "child_process";
10638
- import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
10639
- import { join as join15 } from "path";
10938
+ import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
10939
+ import { join as join16 } from "path";
10640
10940
  var DEFAULT_PORT = 3847;
10641
- var PORT_FILE2 = join15(AIUSAGE_DIR, ".serve-port");
10941
+ var PORT_FILE2 = join16(AIUSAGE_DIR, ".serve-port");
10642
10942
  function clearScreen() {
10643
10943
  process.stdout.write("\x1Bc");
10644
10944
  }
10645
10945
  function getPort() {
10646
- if (existsSync14(PORT_FILE2)) {
10647
- const raw = readFileSync12(PORT_FILE2, "utf-8").trim();
10946
+ if (existsSync15(PORT_FILE2)) {
10947
+ const raw = readFileSync13(PORT_FILE2, "utf-8").trim();
10648
10948
  const n = parseInt(raw, 10);
10649
10949
  if (!isNaN(n) && n > 0) return n;
10650
10950
  }
@@ -11133,10 +11433,10 @@ ${dashboardStatusLine()}
11133
11433
  }
11134
11434
 
11135
11435
  // src/cli.ts
11136
- import { join as join16 } from "path";
11137
- var DB_PATH2 = join16(AIUSAGE_DIR, "cache.db");
11436
+ import { join as join17 } from "path";
11437
+ var DB_PATH2 = join17(AIUSAGE_DIR, "cache.db");
11138
11438
  var program = new Command();
11139
- program.name("aiusage").version(true ? "1.5.7" : "dev").description("CLI tool for AI usage statistics");
11439
+ program.name("aiusage").version(true ? "1.5.8" : "dev").description("CLI tool for AI usage statistics");
11140
11440
  program.action(() => {
11141
11441
  const unknownCommand = program.args.find((arg) => typeof arg === "string");
11142
11442
  if (unknownCommand) {
@@ -11423,7 +11723,7 @@ function hasWidget() {
11423
11723
  }
11424
11724
  }
11425
11725
  function buildPm2Config(includeWidget) {
11426
- const wrapperPath = join16(AIUSAGE_DIR, "pm2-server.cjs");
11726
+ const wrapperPath = join17(AIUSAGE_DIR, "pm2-server.cjs");
11427
11727
  writeFileSync7(wrapperPath, [
11428
11728
  `// Auto-generated by aiusage pm2-start \u2014 do not edit`,
11429
11729
  `const { execSync, spawn } = require('child_process')`,
@@ -11470,7 +11770,7 @@ ${apps.join(",\n")}
11470
11770
  program.command("pm2-setup").description("Generate ecosystem.config.cjs for PM2 background services").option("--server-only", "Only include server, skip widget").action((options) => {
11471
11771
  const includeWidget = options.serverOnly ? false : hasWidget();
11472
11772
  const config = buildPm2Config(includeWidget);
11473
- const configPath = join16(AIUSAGE_DIR, "ecosystem.config.cjs");
11773
+ const configPath = join17(AIUSAGE_DIR, "ecosystem.config.cjs");
11474
11774
  writeFileSync7(configPath, config, "utf-8");
11475
11775
  console.log(`Config created: ${configPath}`);
11476
11776
  if (!includeWidget) {
@@ -11480,7 +11780,7 @@ program.command("pm2-setup").description("Generate ecosystem.config.cjs for PM2
11480
11780
  program.command("pm2-start").description("Setup and start PM2 background services (requires pm2 installed globally)").option("--server-only", "Only start server, skip widget").action((options) => {
11481
11781
  const includeWidget = options.serverOnly ? false : hasWidget();
11482
11782
  const config = buildPm2Config(includeWidget);
11483
- const configPath = join16(AIUSAGE_DIR, "ecosystem.config.cjs");
11783
+ const configPath = join17(AIUSAGE_DIR, "ecosystem.config.cjs");
11484
11784
  writeFileSync7(configPath, config, "utf-8");
11485
11785
  console.log(`Config: ${configPath}`);
11486
11786
  try {