@juliantanx/aiusage 1.5.7 → 1.5.9

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
@@ -7311,53 +7413,33 @@ function parseSessionTags(repoPath) {
7311
7413
  try {
7312
7414
  const sessionId = basename3(basename3(repoPath) === "v2" ? join7(repoPath, "..") : repoPath);
7313
7415
  if (!/^[0-9a-f]{24,}$/.test(sessionId)) return null;
7314
- const tagResult = spawnSync("git", ["tag", "-l"], { cwd: repoPath, encoding: "utf-8", timeout: 5e3 });
7315
- if (tagResult.status !== 0 || !tagResult.stdout?.trim()) return null;
7316
- const tags = tagResult.stdout;
7317
- const lines = tags.trim().split("\n");
7416
+ const result = spawnSync(
7417
+ "git",
7418
+ ["for-each-ref", "--format=%(refname:short)%09%(creatordate:unix)", "refs/tags"],
7419
+ { cwd: repoPath, encoding: "utf-8", timeout: 1e4 }
7420
+ );
7421
+ if (result.status !== 0 || !result.stdout?.trim()) return null;
7318
7422
  let chatTurns = 0;
7319
7423
  let toolCalls = 0;
7320
7424
  let startTs = 0;
7321
- for (const tag of lines) {
7322
- const t = tag.trim();
7323
- if (!t) continue;
7324
- let tagTs = 0;
7325
- try {
7326
- const logResult = spawnSync("git", ["log", "-1", "--format=%at", t], {
7327
- cwd: repoPath,
7328
- encoding: "utf-8",
7329
- timeout: 3e3
7330
- });
7331
- if (logResult.status !== 0 || !logResult.stdout?.trim()) continue;
7332
- const tsStr = logResult.stdout.trim();
7333
- tagTs = parseInt(tsStr, 10) * 1e3;
7334
- } catch {
7335
- continue;
7336
- }
7337
- if (t.startsWith("chain-start-")) {
7425
+ let earliest = Infinity;
7426
+ for (const line of result.stdout.trim().split("\n")) {
7427
+ const tab = line.indexOf(" ");
7428
+ if (tab < 0) continue;
7429
+ const name = line.slice(0, tab).trim();
7430
+ if (!name) continue;
7431
+ const tagTs = parseInt(line.slice(tab + 1).trim(), 10) * 1e3;
7432
+ if (!Number.isFinite(tagTs) || tagTs <= 0) continue;
7433
+ if (tagTs < earliest) earliest = tagTs;
7434
+ if (name.startsWith("chain-start-")) {
7338
7435
  startTs = tagTs;
7339
- } else if (t.startsWith("before-chat-turn-")) {
7436
+ } else if (name.startsWith("before-chat-turn-")) {
7340
7437
  chatTurns++;
7341
- } else if (t.startsWith("toolcall-")) {
7438
+ } else if (name.startsWith("toolcall-")) {
7342
7439
  toolCalls++;
7343
7440
  }
7344
7441
  }
7345
- if (startTs === 0) {
7346
- let earliest = Infinity;
7347
- for (const tag of lines) {
7348
- const t = tag.trim();
7349
- if (!t) continue;
7350
- const logResult = spawnSync("git", ["log", "-1", "--format=%at", t], {
7351
- cwd: repoPath,
7352
- encoding: "utf-8",
7353
- timeout: 3e3
7354
- });
7355
- if (logResult.status !== 0 || !logResult.stdout?.trim()) continue;
7356
- const ts3 = parseInt(logResult.stdout.trim(), 10) * 1e3;
7357
- if (ts3 > 0 && ts3 < earliest) earliest = ts3;
7358
- }
7359
- if (earliest < Infinity) startTs = earliest;
7360
- }
7442
+ if (startTs === 0 && earliest < Infinity) startTs = earliest;
7361
7443
  if (chatTurns === 0 && toolCalls === 0) return null;
7362
7444
  if (startTs === 0) return null;
7363
7445
  return { sessionId, startTs, chatTurns, toolCalls };
@@ -7430,6 +7512,173 @@ function runParseTrae(options) {
7430
7512
  return { records, toolCalls, lastImportedAt: latestTs, errors };
7431
7513
  }
7432
7514
 
7515
+ // src/commands/parse-codebuddy.ts
7516
+ import { existsSync as existsSync9, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
7517
+ import { join as join8, basename as basename4, dirname as dirname4 } from "path";
7518
+ function num3(value) {
7519
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
7520
+ }
7521
+ function toMs(iso) {
7522
+ if (typeof iso !== "string") return 0;
7523
+ const ms = Date.parse(iso);
7524
+ return Number.isFinite(ms) ? ms : 0;
7525
+ }
7526
+ var WORKSPACE_FOLDER_RE = /Workspace Folder:\s*(.+)/;
7527
+ function userMessageText(raw) {
7528
+ try {
7529
+ const inner = JSON.parse(raw);
7530
+ if (Array.isArray(inner.content)) {
7531
+ return inner.content.filter((block) => block?.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n");
7532
+ }
7533
+ } catch {
7534
+ }
7535
+ return raw;
7536
+ }
7537
+ function findMessagesDirs(root, depth = 0) {
7538
+ if (depth > 12) return [];
7539
+ let entries;
7540
+ try {
7541
+ entries = readdirSync3(root, { withFileTypes: true });
7542
+ } catch {
7543
+ return [];
7544
+ }
7545
+ const out = [];
7546
+ for (const entry of entries) {
7547
+ if (!entry.isDirectory()) continue;
7548
+ const full = join8(root, entry.name);
7549
+ if (entry.name === "messages") {
7550
+ out.push(full);
7551
+ continue;
7552
+ }
7553
+ out.push(...findMessagesDirs(full, depth + 1));
7554
+ }
7555
+ return out;
7556
+ }
7557
+ function readConversation(messagesDir) {
7558
+ let files;
7559
+ try {
7560
+ files = readdirSync3(messagesDir).filter((f) => f.endsWith(".json"));
7561
+ } catch {
7562
+ return null;
7563
+ }
7564
+ if (files.length === 0) return null;
7565
+ const parsed = [];
7566
+ for (const file of files) {
7567
+ try {
7568
+ const data = JSON.parse(readFileSync9(join8(messagesDir, file), "utf-8"));
7569
+ if (data && typeof data === "object") parsed.push(data);
7570
+ } catch {
7571
+ }
7572
+ }
7573
+ if (parsed.length === 0) return null;
7574
+ let cwd;
7575
+ for (const msg of parsed) {
7576
+ if (msg.role !== "user" || typeof msg.message !== "string") continue;
7577
+ const match = WORKSPACE_FOLDER_RE.exec(userMessageText(msg.message));
7578
+ if (match) {
7579
+ cwd = match[1].trim();
7580
+ break;
7581
+ }
7582
+ }
7583
+ let latest = null;
7584
+ for (const msg of parsed) {
7585
+ if (msg.role !== "assistant" || typeof msg.extra !== "string") continue;
7586
+ let extra2;
7587
+ try {
7588
+ extra2 = JSON.parse(msg.extra);
7589
+ } catch {
7590
+ continue;
7591
+ }
7592
+ const hasStats = extra2.statsSnapshot != null || extra2.lastStepInputTokens != null;
7593
+ if (!hasStats) continue;
7594
+ const ts3 = toMs(msg.createdAt);
7595
+ if (!latest || ts3 >= latest.ts) latest = { ts: ts3, extra: extra2 };
7596
+ }
7597
+ if (!latest) return null;
7598
+ const extra = latest.extra;
7599
+ const snapshot = extra.statsSnapshot ?? {};
7600
+ const cacheReadTokens = num3(snapshot.cachedInputTokens ?? extra.lastStepCachedInputTokens);
7601
+ const inputTokens = snapshot.cacheMissTokens != null ? num3(snapshot.cacheMissTokens) : Math.max(0, num3(extra.lastStepInputTokens) - cacheReadTokens);
7602
+ const outputTokens = num3(snapshot.lastOutputTokens ?? extra.lastStepOutputTokens);
7603
+ const cacheWriteTokens = num3(snapshot.cacheWriteTokens);
7604
+ const thinkingTokens = num3(snapshot.thinkingTokens);
7605
+ const model = typeof extra.modelId === "string" && extra.modelId.trim() ? extra.modelId.trim() : "unknown";
7606
+ return {
7607
+ ts: latest.ts,
7608
+ model,
7609
+ inputTokens,
7610
+ outputTokens,
7611
+ cacheReadTokens,
7612
+ cacheWriteTokens,
7613
+ thinkingTokens,
7614
+ cwd
7615
+ };
7616
+ }
7617
+ function runParseCodeBuddy(options) {
7618
+ const { dataDir, device, deviceInstanceId, platform: platform5, now, cursor, exchangeRate } = options;
7619
+ const records = [];
7620
+ const errors = [];
7621
+ let nextCursor = cursor ?? 0;
7622
+ if (!existsSync9(dataDir)) {
7623
+ return { records, nextCursor, errors };
7624
+ }
7625
+ let messagesDirs;
7626
+ try {
7627
+ messagesDirs = findMessagesDirs(dataDir);
7628
+ } catch (e) {
7629
+ return { records, nextCursor, errors: [String(e)] };
7630
+ }
7631
+ for (const messagesDir of messagesDirs) {
7632
+ const conversationDir = dirname4(messagesDir);
7633
+ const sessionId = basename4(conversationDir);
7634
+ let usage;
7635
+ try {
7636
+ usage = readConversation(messagesDir);
7637
+ } catch (e) {
7638
+ errors.push(`${messagesDir}: ${e instanceof Error ? e.message : e}`);
7639
+ continue;
7640
+ }
7641
+ if (!usage) continue;
7642
+ if (usage.inputTokens + usage.outputTokens + usage.cacheReadTokens === 0) continue;
7643
+ if (cursor && usage.ts <= cursor) continue;
7644
+ if (usage.ts > nextCursor) nextCursor = usage.ts;
7645
+ const provider = inferProvider(usage.model);
7646
+ const tokenArgs = {
7647
+ inputTokens: usage.inputTokens,
7648
+ outputTokens: usage.outputTokens,
7649
+ cacheReadTokens: usage.cacheReadTokens,
7650
+ cacheWriteTokens: usage.cacheWriteTokens,
7651
+ thinkingTokens: usage.thinkingTokens
7652
+ };
7653
+ const cost = calculateCost(usage.model, tokenArgs, exchangeRate);
7654
+ const recordId = generateRecordId(deviceInstanceId, conversationDir, usage.ts);
7655
+ records.push({
7656
+ id: recordId,
7657
+ ts: usage.ts,
7658
+ ingestedAt: now,
7659
+ updatedAt: now,
7660
+ lineOffset: 0,
7661
+ tool: "codebuddy",
7662
+ model: usage.model,
7663
+ provider,
7664
+ inputTokens: usage.inputTokens,
7665
+ outputTokens: usage.outputTokens,
7666
+ cacheReadTokens: usage.cacheReadTokens,
7667
+ cacheWriteTokens: usage.cacheWriteTokens,
7668
+ thinkingTokens: usage.thinkingTokens,
7669
+ cost,
7670
+ costSource: cost > 0 ? "pricing" : "unknown",
7671
+ sessionId,
7672
+ sourceFile: conversationDir,
7673
+ cwd: usage.cwd,
7674
+ device,
7675
+ deviceInstanceId,
7676
+ platform: platform5
7677
+ });
7678
+ }
7679
+ return { records, nextCursor, errors };
7680
+ }
7681
+
7433
7682
  // src/commands/parse.ts
7434
7683
  function extractCwdFromJson(data) {
7435
7684
  if (typeof data.cwd === "string" && data.cwd) return data.cwd;
@@ -7484,7 +7733,7 @@ function parseUiMessagesFile(options) {
7484
7733
  const errors = [];
7485
7734
  let messages;
7486
7735
  try {
7487
- const parsed = JSON.parse(readFileSync9(filePath, "utf-8"));
7736
+ const parsed = JSON.parse(readFileSync10(filePath, "utf-8"));
7488
7737
  messages = Array.isArray(parsed) ? parsed : [];
7489
7738
  } catch (e) {
7490
7739
  return { records, errors: [`${filePath}: ${e instanceof Error ? e.message : e}`] };
@@ -7562,7 +7811,7 @@ function parseKiroSessionFile(options) {
7562
7811
  const errors = [];
7563
7812
  let parsed;
7564
7813
  try {
7565
- parsed = JSON.parse(readFileSync9(filePath, "utf-8"));
7814
+ parsed = JSON.parse(readFileSync10(filePath, "utf-8"));
7566
7815
  } catch (e) {
7567
7816
  return { records, errors: [`${filePath}: ${e instanceof Error ? e.message : e}`] };
7568
7817
  }
@@ -7617,8 +7866,8 @@ function parseKiroWorkspaceSession(options) {
7617
7866
  const sessionId = parsed?.sessionId ?? extractSessionId2(filePath, "kiro");
7618
7867
  let sessionTs = now;
7619
7868
  try {
7620
- const dir = dirname4(filePath);
7621
- const sessionsJson = readFileSync9(join8(dir, "sessions.json"), "utf-8");
7869
+ const dir = dirname5(filePath);
7870
+ const sessionsJson = readFileSync10(join9(dir, "sessions.json"), "utf-8");
7622
7871
  const sessions = JSON.parse(sessionsJson);
7623
7872
  const match = Array.isArray(sessions) && sessions.find((s) => s.sessionId === sessionId);
7624
7873
  if (match?.dateCreated) {
@@ -7694,7 +7943,7 @@ async function runParse(db, filterTool, options) {
7694
7943
  const device = config?.device || hostname2() || state?.deviceInstanceId?.slice(0, 8) || "unknown";
7695
7944
  const deviceInstanceId = state?.deviceInstanceId ?? "unknown";
7696
7945
  const devicePlatform = config?.platform;
7697
- const watermarkPath = join8(AIUSAGE_DIR, "watermark.json");
7946
+ const watermarkPath = join9(AIUSAGE_DIR, "watermark.json");
7698
7947
  const wm = new WatermarkManager(watermarkPath);
7699
7948
  const toolPaths = discoverLogFiles();
7700
7949
  const aggregator = new Aggregator();
@@ -7765,7 +8014,7 @@ async function runParse(db, filterTool, options) {
7765
8014
  continue;
7766
8015
  }
7767
8016
  if (tool === "kiro" && filePath.endsWith(".jsonl")) {
7768
- const content2 = readFileSync9(filePath, "utf-8");
8017
+ const content2 = readFileSync10(filePath, "utf-8");
7769
8018
  const lines2 = content2.split("\n");
7770
8019
  let byteOffset2 = 0;
7771
8020
  for (const line of lines2) {
@@ -7895,7 +8144,7 @@ async function runParse(db, filterTool, options) {
7895
8144
  onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
7896
8145
  continue;
7897
8146
  }
7898
- const content = readFileSync9(filePath, "utf-8");
8147
+ const content = readFileSync10(filePath, "utf-8");
7899
8148
  const lines = content.split("\n");
7900
8149
  let byteOffset = 0;
7901
8150
  let fileCwd;
@@ -7959,7 +8208,7 @@ async function runParse(db, filterTool, options) {
7959
8208
  }
7960
8209
  }
7961
8210
  if (fileCwd) {
7962
- db.prepare(`UPDATE records SET cwd = ? WHERE source_file = ? AND cwd = ''`).run(fileCwd, filePath);
8211
+ db.prepare(`UPDATE records SET cwd = ?, updated_at = ? WHERE source_file = ? AND cwd = ''`).run(fileCwd, Date.now(), filePath);
7963
8212
  }
7964
8213
  wm.setEntry(tool, filePath, {
7965
8214
  offset: stat2.size,
@@ -7974,7 +8223,7 @@ async function runParse(db, filterTool, options) {
7974
8223
  }
7975
8224
  }
7976
8225
  const openCodeDbPath = options?.openCodeDbPath ?? getDbPath("opencode") ?? "";
7977
- if ((!filterTool || filterTool === "opencode") && existsSync9(openCodeDbPath)) {
8226
+ if ((!filterTool || filterTool === "opencode") && existsSync10(openCodeDbPath)) {
7978
8227
  try {
7979
8228
  const openCodeDb = new Database2(openCodeDbPath, { readonly: true });
7980
8229
  try {
@@ -8005,7 +8254,7 @@ async function runParse(db, filterTool, options) {
8005
8254
  }
8006
8255
  }
8007
8256
  const hermesDbPath = options?.hermesDbPath ?? getDbPath("hermes") ?? "";
8008
- if ((!filterTool || filterTool === "hermes") && existsSync9(hermesDbPath)) {
8257
+ if ((!filterTool || filterTool === "hermes") && existsSync10(hermesDbPath)) {
8009
8258
  try {
8010
8259
  const hermesDb = new Database2(hermesDbPath, { readonly: true });
8011
8260
  try {
@@ -8036,7 +8285,7 @@ async function runParse(db, filterTool, options) {
8036
8285
  }
8037
8286
  }
8038
8287
  const qoderDbPath = options?.qoderDbPath ?? getDbPath("qoder-db") ?? "";
8039
- if ((!filterTool || filterTool === "qoder") && existsSync9(qoderDbPath)) {
8288
+ if ((!filterTool || filterTool === "qoder") && existsSync10(qoderDbPath)) {
8040
8289
  try {
8041
8290
  const qoderDb = new Database2(qoderDbPath, { readonly: true });
8042
8291
  try {
@@ -8067,7 +8316,7 @@ async function runParse(db, filterTool, options) {
8067
8316
  }
8068
8317
  }
8069
8318
  const cursorDbPath = options?.cursorDbPath ?? getDbPath("cursor") ?? "";
8070
- if ((!filterTool || filterTool === "cursor") && existsSync9(cursorDbPath)) {
8319
+ if ((!filterTool || filterTool === "cursor") && existsSync10(cursorDbPath)) {
8071
8320
  try {
8072
8321
  const cursorDb = new Database2(cursorDbPath, { readonly: true });
8073
8322
  try {
@@ -8097,7 +8346,7 @@ async function runParse(db, filterTool, options) {
8097
8346
  }
8098
8347
  }
8099
8348
  const kiloDbPath = getDbPath("kilocode-db") ?? "";
8100
- if ((!filterTool || filterTool === "kilocode") && existsSync9(kiloDbPath)) {
8349
+ if ((!filterTool || filterTool === "kilocode") && existsSync10(kiloDbPath)) {
8101
8350
  try {
8102
8351
  const kiloDb = new Database2(kiloDbPath, { readonly: true });
8103
8352
  try {
@@ -8137,7 +8386,7 @@ async function runParse(db, filterTool, options) {
8137
8386
  }
8138
8387
  }
8139
8388
  const gooseDbPath = getDbPath("goose") ?? "";
8140
- if ((!filterTool || filterTool === "goose") && existsSync9(gooseDbPath)) {
8389
+ if ((!filterTool || filterTool === "goose") && existsSync10(gooseDbPath)) {
8141
8390
  try {
8142
8391
  const gooseDb = new Database2(gooseDbPath, { readonly: true });
8143
8392
  try {
@@ -8195,7 +8444,7 @@ async function runParse(db, filterTool, options) {
8195
8444
  }
8196
8445
  }
8197
8446
  const zedDbPath = getDbPath("zed") ?? "";
8198
- if ((!filterTool || filterTool === "zed") && existsSync9(zedDbPath)) {
8447
+ if ((!filterTool || filterTool === "zed") && existsSync10(zedDbPath)) {
8199
8448
  try {
8200
8449
  const zedDb = new Database2(zedDbPath, { readonly: true });
8201
8450
  try {
@@ -8225,7 +8474,7 @@ async function runParse(db, filterTool, options) {
8225
8474
  }
8226
8475
  if (!filterTool || filterTool === "zcode") {
8227
8476
  const zcodeDbPath = getDbPath("zcode") ?? "";
8228
- if (existsSync9(zcodeDbPath)) {
8477
+ if (existsSync10(zcodeDbPath)) {
8229
8478
  try {
8230
8479
  const zcodeDb = new Database2(zcodeDbPath, { readonly: true });
8231
8480
  try {
@@ -8284,6 +8533,31 @@ async function runParse(db, filterTool, options) {
8284
8533
  }
8285
8534
  onProgress({ phase: "Parsing SQLite", tool: "trae", current: 1, total: 1, records: parsedCount, toolCalls: toolCallCount });
8286
8535
  }
8536
+ const codeBuddyIdeDir = getDbPath("codebuddy-ide") ?? "";
8537
+ if ((!filterTool || filterTool === "codebuddy") && existsSync10(codeBuddyIdeDir)) {
8538
+ try {
8539
+ const cbCursor = wm.getCodeBuddyIdeCursor();
8540
+ const result = runParseCodeBuddy({
8541
+ dataDir: codeBuddyIdeDir,
8542
+ device,
8543
+ deviceInstanceId,
8544
+ platform: devicePlatform,
8545
+ now: Date.now(),
8546
+ cursor: cbCursor,
8547
+ exchangeRate
8548
+ });
8549
+ for (const record of result.records) insertRecord(db, record);
8550
+ parsedCount += result.records.length;
8551
+ errors.push(...result.errors);
8552
+ if (result.nextCursor > cbCursor) {
8553
+ wm.setCodeBuddyIdeCursor(result.nextCursor);
8554
+ wm.save();
8555
+ }
8556
+ onProgress({ phase: "Parsing logs", tool: "codebuddy", current: 1, total: 1, records: parsedCount, toolCalls: toolCallCount });
8557
+ } catch (e) {
8558
+ errors.push(`${codeBuddyIdeDir}: ${e instanceof Error ? e.message : e}`);
8559
+ }
8560
+ }
8287
8561
  if (deviceInstanceId !== "unknown") {
8288
8562
  db.prepare(
8289
8563
  `UPDATE records SET device_instance_id = ?, device = ? WHERE device_instance_id = 'unknown'`
@@ -8295,9 +8569,19 @@ async function runParse(db, filterTool, options) {
8295
8569
  ).run(devicePlatform);
8296
8570
  }
8297
8571
  backfillHermesSourceFiles(db);
8572
+ backfillCwd(db);
8573
+ backfillSkillNames(db);
8574
+ backfillCodexModels(db);
8575
+ backfillMissingToolCalls(db, exchangeRate);
8576
+ return { parsedCount, toolCallCount, errors };
8577
+ }
8578
+ function backfillCwd(db) {
8298
8579
  const staleFiles = db.prepare(
8299
8580
  `SELECT DISTINCT source_file FROM records WHERE cwd = '' AND source_file NOT LIKE 'synced/%'`
8300
8581
  ).all();
8582
+ const updateStmt = db.prepare(
8583
+ `UPDATE records SET cwd = ?, updated_at = ? WHERE source_file = ? AND cwd = ''`
8584
+ );
8301
8585
  for (const { source_file } of staleFiles) {
8302
8586
  try {
8303
8587
  const fd = openSync(source_file, "r");
@@ -8308,15 +8592,11 @@ async function runParse(db, filterTool, options) {
8308
8592
  const data = JSON.parse(firstLine);
8309
8593
  const cwd = extractCwdFromJson(data);
8310
8594
  if (cwd) {
8311
- db.prepare(`UPDATE records SET cwd = ? WHERE source_file = ? AND cwd = ''`).run(cwd, source_file);
8595
+ updateStmt.run(cwd, Date.now(), source_file);
8312
8596
  }
8313
8597
  } catch {
8314
8598
  }
8315
8599
  }
8316
- backfillSkillNames(db);
8317
- backfillCodexModels(db);
8318
- backfillMissingToolCalls(db, exchangeRate);
8319
- return { parsedCount, toolCallCount, errors };
8320
8600
  }
8321
8601
  function backfillHermesSourceFiles(db) {
8322
8602
  const rows = db.prepare(`
@@ -8330,7 +8610,7 @@ function backfillHermesSourceFiles(db) {
8330
8610
  const dbPath = rows[0].source_file;
8331
8611
  let titleMap = /* @__PURE__ */ new Map();
8332
8612
  try {
8333
- if (existsSync9(dbPath)) {
8613
+ if (existsSync10(dbPath)) {
8334
8614
  const hermesDb = new Database2(dbPath, { readonly: true });
8335
8615
  try {
8336
8616
  const sessions = hermesDb.prepare(
@@ -8343,11 +8623,11 @@ function backfillHermesSourceFiles(db) {
8343
8623
  }
8344
8624
  } catch {
8345
8625
  }
8346
- const updateStmt = db.prepare(`UPDATE records SET source_file = ? WHERE tool = 'hermes' AND session_id = ? AND source_file = ?`);
8626
+ const updateStmt = db.prepare(`UPDATE records SET source_file = ?, updated_at = ? WHERE tool = 'hermes' AND session_id = ? AND source_file = ?`);
8347
8627
  for (const row of rows) {
8348
8628
  const title = (titleMap.get(row.session_id) || "").replace(/[/\\:]/g, "_").slice(0, 80);
8349
8629
  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);
8630
+ updateStmt.run(newSourceFile, Date.now(), row.session_id, row.source_file);
8351
8631
  }
8352
8632
  }
8353
8633
  function backfillSkillNames(db) {
@@ -8411,7 +8691,7 @@ function backfillCodexModels(db) {
8411
8691
  );
8412
8692
  for (const [sourceFile, fileRows] of byFile) {
8413
8693
  try {
8414
- const content = readFileSync9(sourceFile, "utf-8");
8694
+ const content = readFileSync10(sourceFile, "utf-8");
8415
8695
  const lines = content.split("\n");
8416
8696
  const offsetToModel = /* @__PURE__ */ new Map();
8417
8697
  let currentModel = "";
@@ -8459,7 +8739,7 @@ function backfillMissingToolCalls(db, exchangeRate) {
8459
8739
  }
8460
8740
  for (const [sourceFile, fileRows] of rowsByFile) {
8461
8741
  try {
8462
- const content = readFileSync9(sourceFile, "utf-8");
8742
+ const content = readFileSync10(sourceFile, "utf-8");
8463
8743
  const lines = content.split("\n");
8464
8744
  const aggregator = new Aggregator();
8465
8745
  const recordIdByOffset = /* @__PURE__ */ new Map();
@@ -8521,7 +8801,7 @@ function deriveSessionId(tool, sourceFile) {
8521
8801
  }
8522
8802
 
8523
8803
  // src/commands/sync.ts
8524
- import { join as join10 } from "path";
8804
+ import { join as join11 } from "path";
8525
8805
 
8526
8806
  // src/db/synced-records.ts
8527
8807
  function insertSyncedRecord(db, record) {
@@ -8954,10 +9234,10 @@ async function cloudClear() {
8954
9234
  }
8955
9235
  function getClientVersion() {
8956
9236
  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"));
9237
+ const { readFileSync: readFileSync14 } = __require("fs");
9238
+ const { join: join18 } = __require("path");
9239
+ const pkgPath = join18(__dirname, "../../package.json");
9240
+ const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
8961
9241
  return pkg.version || "0.0.0";
8962
9242
  } catch {
8963
9243
  return "0.0.0";
@@ -9048,7 +9328,7 @@ var CloudSyncOrchestrator = class {
9048
9328
  import { execFile } from "child_process";
9049
9329
  import { promisify } from "util";
9050
9330
  import { readFile, writeFile, mkdir, readdir, stat, unlink, rm } from "fs/promises";
9051
- import { join as join9 } from "path";
9331
+ import { join as join10 } from "path";
9052
9332
  var exec = promisify(execFile);
9053
9333
  var GitSyncBackend = class _GitSyncBackend {
9054
9334
  repo;
@@ -9060,7 +9340,7 @@ var GitSyncBackend = class _GitSyncBackend {
9060
9340
  this.repo = config.repo;
9061
9341
  this.token = config.token;
9062
9342
  this.cacheDir = config.cacheDir;
9063
- this.dataDir = join9(config.cacheDir, "data");
9343
+ this.dataDir = join10(config.cacheDir, "data");
9064
9344
  this.branch = config.branch ?? "main";
9065
9345
  }
9066
9346
  get remoteUrl() {
@@ -9092,7 +9372,7 @@ var GitSyncBackend = class _GitSyncBackend {
9092
9372
  /** Clone or pull the repo to get latest remote state */
9093
9373
  async prepare() {
9094
9374
  try {
9095
- await stat(join9(this.cacheDir, ".git"));
9375
+ await stat(join10(this.cacheDir, ".git"));
9096
9376
  await this.git(["fetch", "origin", this.branch, "--depth=1"]);
9097
9377
  await this.git(["reset", "--hard", `origin/${this.branch}`]);
9098
9378
  } catch {
@@ -9109,15 +9389,15 @@ var GitSyncBackend = class _GitSyncBackend {
9109
9389
  }
9110
9390
  async readFile(path3) {
9111
9391
  try {
9112
- const fullPath = join9(this.dataDir, path3);
9392
+ const fullPath = join10(this.dataDir, path3);
9113
9393
  return await readFile(fullPath, "utf-8");
9114
9394
  } catch {
9115
9395
  return null;
9116
9396
  }
9117
9397
  }
9118
9398
  async writeFile(path3, content) {
9119
- const fullPath = join9(this.dataDir, path3);
9120
- await mkdir(join9(fullPath, ".."), { recursive: true });
9399
+ const fullPath = join10(this.dataDir, path3);
9400
+ await mkdir(join10(fullPath, ".."), { recursive: true });
9121
9401
  await writeFile(fullPath, content, "utf-8");
9122
9402
  }
9123
9403
  async listFiles() {
@@ -9128,7 +9408,7 @@ var GitSyncBackend = class _GitSyncBackend {
9128
9408
  }
9129
9409
  }
9130
9410
  async deleteFile(path3) {
9131
- const fullPath = join9(this.dataDir, path3);
9411
+ const fullPath = join10(this.dataDir, path3);
9132
9412
  try {
9133
9413
  await unlink(fullPath);
9134
9414
  } catch {
@@ -9167,7 +9447,7 @@ var GitSyncBackend = class _GitSyncBackend {
9167
9447
  for (const entry of entries) {
9168
9448
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
9169
9449
  if (entry.isDirectory()) {
9170
- files.push(...await this.walkDir(join9(dir, entry.name), relPath));
9450
+ files.push(...await this.walkDir(join10(dir, entry.name), relPath));
9171
9451
  } else if (entry.name.endsWith(".ndjson")) {
9172
9452
  files.push(relPath);
9173
9453
  }
@@ -9301,7 +9581,7 @@ function createBackend(config) {
9301
9581
  return new GitSyncBackend({
9302
9582
  repo: sync.repo,
9303
9583
  token,
9304
- cacheDir: join10(AIUSAGE_DIR, "sync-repo")
9584
+ cacheDir: join11(AIUSAGE_DIR, "sync-repo")
9305
9585
  });
9306
9586
  }
9307
9587
  if (sync.backend === "s3") {
@@ -9396,8 +9676,8 @@ async function runSync(db, options) {
9396
9676
  }
9397
9677
 
9398
9678
  // src/commands/clean.ts
9399
- import { unlinkSync as unlinkSync3, existsSync as existsSync10 } from "fs";
9400
- import { join as join11 } from "path";
9679
+ import { unlinkSync as unlinkSync3, existsSync as existsSync11 } from "fs";
9680
+ import { join as join12 } from "path";
9401
9681
  function cleanOldData(db, days) {
9402
9682
  const cutoff = Date.now() - days * 864e5;
9403
9683
  const recordsResult = db.prepare("DELETE FROM records WHERE ts < ?").run(cutoff);
@@ -9418,9 +9698,9 @@ function cleanAll(db) {
9418
9698
  const syncedResult = db.prepare("DELETE FROM synced_records").run();
9419
9699
  const syncStateResult = db.prepare("DELETE FROM sync_record_state").run();
9420
9700
  const tombstonesResult = db.prepare("DELETE FROM sync_tombstones").run();
9421
- const watermarkPath = join11(AIUSAGE_DIR, "watermark.json");
9701
+ const watermarkPath = join12(AIUSAGE_DIR, "watermark.json");
9422
9702
  let watermarkRemoved = false;
9423
- if (existsSync10(watermarkPath)) {
9703
+ if (existsSync11(watermarkPath)) {
9424
9704
  unlinkSync3(watermarkPath);
9425
9705
  watermarkRemoved = true;
9426
9706
  }
@@ -9455,7 +9735,7 @@ function createBackend2(config) {
9455
9735
  return new GitSyncBackend({
9456
9736
  repo: config.sync.repo,
9457
9737
  token,
9458
- cacheDir: join11(AIUSAGE_DIR, "sync-repo")
9738
+ cacheDir: join12(AIUSAGE_DIR, "sync-repo")
9459
9739
  });
9460
9740
  }
9461
9741
  if (config.sync.backend === "s3") {
@@ -9788,7 +10068,7 @@ var RuntimeSettingsController = class {
9788
10068
 
9789
10069
  // src/commands/serve.ts
9790
10070
  var MAX_PORT_ATTEMPTS = 10;
9791
- var PORT_FILE = join12(AIUSAGE_DIR, ".serve-port");
10071
+ var PORT_FILE = join13(AIUSAGE_DIR, ".serve-port");
9792
10072
  var MIME_TYPES = {
9793
10073
  ".html": "text/html",
9794
10074
  ".js": "application/javascript",
@@ -9863,11 +10143,11 @@ function serve(options) {
9863
10143
  getDbWriteQueueStatus: () => dbWriteQueue.getStatus()
9864
10144
  });
9865
10145
  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");
10146
+ const prodDir = join13(dirname6(fileURLToPath(import.meta.url)), "web");
10147
+ if (existsSync12(prodDir)) return prodDir;
10148
+ return join13(dirname6(fileURLToPath(import.meta.url)), "..", "..", "..", "web", "build");
9869
10149
  })();
9870
- if (!existsSync11(webBuildDir)) {
10150
+ if (!existsSync12(webBuildDir)) {
9871
10151
  console.error("Web dashboard not found. Reinstall the package: npm install -g @juliantanx/aiusage");
9872
10152
  process.exit(1);
9873
10153
  }
@@ -9877,19 +10157,19 @@ function serve(options) {
9877
10157
  apiServer.emit("request", req, res);
9878
10158
  return;
9879
10159
  }
9880
- if (existsSync11(webBuildDir)) {
9881
- let filePath = join12(webBuildDir, url.pathname);
10160
+ if (existsSync12(webBuildDir)) {
10161
+ let filePath = join13(webBuildDir, url.pathname);
9882
10162
  try {
9883
10163
  if (statSync4(filePath).isDirectory()) {
9884
- filePath = join12(filePath, "index.html");
10164
+ filePath = join13(filePath, "index.html");
9885
10165
  }
9886
10166
  } catch {
9887
10167
  }
9888
- if (!existsSync11(filePath)) {
9889
- filePath = join12(webBuildDir, "index.html");
10168
+ if (!existsSync12(filePath)) {
10169
+ filePath = join13(webBuildDir, "index.html");
9890
10170
  }
9891
10171
  try {
9892
- const content = readFileSync10(filePath);
10172
+ const content = readFileSync11(filePath);
9893
10173
  const ext = extname2(filePath);
9894
10174
  const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
9895
10175
  res.writeHead(200, { "Content-Type": contentType });
@@ -10071,9 +10351,9 @@ Consent recorded for schema v1.`
10071
10351
  }
10072
10352
 
10073
10353
  // src/commands/status.ts
10074
- import { statSync as statSync5, existsSync as existsSync12 } from "fs";
10354
+ import { statSync as statSync5, existsSync as existsSync13 } from "fs";
10075
10355
  import { hostname as hostname3 } from "os";
10076
- import { join as join13 } from "path";
10356
+ import { join as join14 } from "path";
10077
10357
  function formatFileSize(bytes) {
10078
10358
  if (bytes < 1024) return `${bytes} B`;
10079
10359
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -10091,9 +10371,9 @@ function generateStatus(db) {
10091
10371
  const schemaVersion = db.prepare("SELECT version FROM schema_version ORDER BY version DESC LIMIT 1").get()?.version ?? 0;
10092
10372
  const tableCount = db.prepare("SELECT COUNT(*) as count FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'").get()?.count ?? 0;
10093
10373
  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");
10374
+ const sizePath = join14(AIUSAGE_DIR, "cache.db");
10095
10375
  let databaseSize = "0 B";
10096
- if (existsSync12(sizePath)) {
10376
+ if (existsSync13(sizePath)) {
10097
10377
  try {
10098
10378
  const stat2 = statSync5(sizePath);
10099
10379
  databaseSize = formatFileSize(stat2.size);
@@ -10316,10 +10596,10 @@ var SyncProgressReporter = class {
10316
10596
  };
10317
10597
 
10318
10598
  // src/commands/widget.ts
10319
- import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
10599
+ import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
10320
10600
  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");
10601
+ import { join as join15 } from "path";
10602
+ var WIDGET_PID_PATH = join15(AIUSAGE_DIR, "widget.pid");
10323
10603
  async function launchWidget() {
10324
10604
  if (isWidgetRunning()) {
10325
10605
  console.log("aiusage widget is already running in the system tray.");
@@ -10341,10 +10621,10 @@ async function launchWidget() {
10341
10621
  console.log("aiusage widget started.");
10342
10622
  }
10343
10623
  function isWidgetRunning() {
10344
- if (!existsSync13(WIDGET_PID_PATH)) return false;
10624
+ if (!existsSync14(WIDGET_PID_PATH)) return false;
10345
10625
  let pid;
10346
10626
  try {
10347
- pid = parseInt(readFileSync11(WIDGET_PID_PATH, "utf-8").trim(), 10);
10627
+ pid = parseInt(readFileSync12(WIDGET_PID_PATH, "utf-8").trim(), 10);
10348
10628
  } catch {
10349
10629
  return false;
10350
10630
  }
@@ -10388,10 +10668,10 @@ function getDeviceName2() {
10388
10668
  }
10389
10669
  function getCliVersion2() {
10390
10670
  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"));
10671
+ const { readFileSync: readFileSync14 } = __require("fs");
10672
+ const { join: join18 } = __require("path");
10673
+ const pkgPath = join18(__dirname, "../../package.json");
10674
+ const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
10395
10675
  return pkg.version || "0.0.0";
10396
10676
  } catch {
10397
10677
  return "0.0.0";
@@ -10560,8 +10840,8 @@ function formatTokens(value) {
10560
10840
  return Number(value).toLocaleString();
10561
10841
  }
10562
10842
  function formatCost(value) {
10563
- const num3 = Number(value);
10564
- return Number.isFinite(num3) ? `$${num3.toFixed(4)}` : "$0.0000";
10843
+ const num4 = Number(value);
10844
+ return Number.isFinite(num4) ? `$${num4.toFixed(4)}` : "$0.0000";
10565
10845
  }
10566
10846
  function formatUpdatedAt(value) {
10567
10847
  const date = new Date(value);
@@ -10635,16 +10915,16 @@ async function runLeaderboardView(options = {}) {
10635
10915
  // src/commands/menu.ts
10636
10916
  import { createInterface } from "readline";
10637
10917
  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";
10918
+ import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
10919
+ import { join as join16 } from "path";
10640
10920
  var DEFAULT_PORT = 3847;
10641
- var PORT_FILE2 = join15(AIUSAGE_DIR, ".serve-port");
10921
+ var PORT_FILE2 = join16(AIUSAGE_DIR, ".serve-port");
10642
10922
  function clearScreen() {
10643
10923
  process.stdout.write("\x1Bc");
10644
10924
  }
10645
10925
  function getPort() {
10646
- if (existsSync14(PORT_FILE2)) {
10647
- const raw = readFileSync12(PORT_FILE2, "utf-8").trim();
10926
+ if (existsSync15(PORT_FILE2)) {
10927
+ const raw = readFileSync13(PORT_FILE2, "utf-8").trim();
10648
10928
  const n = parseInt(raw, 10);
10649
10929
  if (!isNaN(n) && n > 0) return n;
10650
10930
  }
@@ -11133,10 +11413,10 @@ ${dashboardStatusLine()}
11133
11413
  }
11134
11414
 
11135
11415
  // src/cli.ts
11136
- import { join as join16 } from "path";
11137
- var DB_PATH2 = join16(AIUSAGE_DIR, "cache.db");
11416
+ import { join as join17 } from "path";
11417
+ var DB_PATH2 = join17(AIUSAGE_DIR, "cache.db");
11138
11418
  var program = new Command();
11139
- program.name("aiusage").version(true ? "1.5.7" : "dev").description("CLI tool for AI usage statistics");
11419
+ program.name("aiusage").version(true ? "1.5.9" : "dev").description("CLI tool for AI usage statistics");
11140
11420
  program.action(() => {
11141
11421
  const unknownCommand = program.args.find((arg) => typeof arg === "string");
11142
11422
  if (unknownCommand) {
@@ -11423,7 +11703,7 @@ function hasWidget() {
11423
11703
  }
11424
11704
  }
11425
11705
  function buildPm2Config(includeWidget) {
11426
- const wrapperPath = join16(AIUSAGE_DIR, "pm2-server.cjs");
11706
+ const wrapperPath = join17(AIUSAGE_DIR, "pm2-server.cjs");
11427
11707
  writeFileSync7(wrapperPath, [
11428
11708
  `// Auto-generated by aiusage pm2-start \u2014 do not edit`,
11429
11709
  `const { execSync, spawn } = require('child_process')`,
@@ -11470,7 +11750,7 @@ ${apps.join(",\n")}
11470
11750
  program.command("pm2-setup").description("Generate ecosystem.config.cjs for PM2 background services").option("--server-only", "Only include server, skip widget").action((options) => {
11471
11751
  const includeWidget = options.serverOnly ? false : hasWidget();
11472
11752
  const config = buildPm2Config(includeWidget);
11473
- const configPath = join16(AIUSAGE_DIR, "ecosystem.config.cjs");
11753
+ const configPath = join17(AIUSAGE_DIR, "ecosystem.config.cjs");
11474
11754
  writeFileSync7(configPath, config, "utf-8");
11475
11755
  console.log(`Config created: ${configPath}`);
11476
11756
  if (!includeWidget) {
@@ -11480,7 +11760,7 @@ program.command("pm2-setup").description("Generate ecosystem.config.cjs for PM2
11480
11760
  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
11761
  const includeWidget = options.serverOnly ? false : hasWidget();
11482
11762
  const config = buildPm2Config(includeWidget);
11483
- const configPath = join16(AIUSAGE_DIR, "ecosystem.config.cjs");
11763
+ const configPath = join17(AIUSAGE_DIR, "ecosystem.config.cjs");
11484
11764
  writeFileSync7(configPath, config, "utf-8");
11485
11765
  console.log(`Config: ${configPath}`);
11486
11766
  try {