@juliantanx/aiusage 1.5.6 → 1.5.7

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.
Files changed (25) hide show
  1. package/dist/index.js +626 -113
  2. package/dist/web/_app/immutable/chunks/{ToolSelector.0iI1cXVj.js → ToolSelector.CO-QGau6.js} +1 -1
  3. package/dist/web/_app/immutable/chunks/entry.NbJb2eQR.js +3 -0
  4. package/dist/web/_app/immutable/chunks/{stores.DE60qEGb.js → stores.BYZRdBSl.js} +1 -1
  5. package/dist/web/_app/immutable/chunks/{stores.BSI_cVy6.js → stores.C6g63Ng7.js} +1 -1
  6. package/dist/web/_app/immutable/entry/{app.CSiP_kAD.js → app.CM2PdbpC.js} +2 -2
  7. package/dist/web/_app/immutable/entry/start.Cj8FkSB_.js +1 -0
  8. package/dist/web/_app/immutable/nodes/{0.dH2tW-7d.js → 0.DDrgmg6l.js} +1 -1
  9. package/dist/web/_app/immutable/nodes/{1.B8yHhyzy.js → 1.DTUl4JbV.js} +1 -1
  10. package/dist/web/_app/immutable/nodes/{10.CnTuhaua.js → 10.CP1F4_ac.js} +1 -1
  11. package/dist/web/_app/immutable/nodes/{11.5p54X4Ps.js → 11.DB5iERgm.js} +1 -1
  12. package/dist/web/_app/immutable/nodes/{12.BCT8Dv1G.js → 12.RfrZJ-XB.js} +1 -1
  13. package/dist/web/_app/immutable/nodes/{14.u96Ucsue.js → 14.Cbg9FBSP.js} +1 -1
  14. package/dist/web/_app/immutable/nodes/{15.CMhy_NGl.js → 15.CQoUgLeT.js} +1 -1
  15. package/dist/web/_app/immutable/nodes/{2.CJH1Fs0K.js → 2.DLTFcQ9L.js} +1 -1
  16. package/dist/web/_app/immutable/nodes/{3.Chtgl9hh.js → 3.jLX2a-Ic.js} +1 -1
  17. package/dist/web/_app/immutable/nodes/{5.DtYG5yJy.js → 5.CVQN-3_l.js} +1 -1
  18. package/dist/web/_app/immutable/nodes/{6.B-ud4zCO.js → 6.C-WRTBwg.js} +1 -1
  19. package/dist/web/_app/immutable/nodes/{7.7Fm6yzqA.js → 7.DFkuOTpF.js} +1 -1
  20. package/dist/web/_app/immutable/nodes/{8.Chp3dlzK.js → 8.BNiBE_e2.js} +1 -1
  21. package/dist/web/_app/version.json +1 -1
  22. package/dist/web/index.html +6 -6
  23. package/package.json +3 -3
  24. package/dist/web/_app/immutable/chunks/entry.PMcg7HQL.js +0 -3
  25. package/dist/web/_app/immutable/entry/start.DnwUvynt.js +0 -1
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 readFileSync9, existsSync as existsSync10, statSync as statSync3, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4 } from "fs";
17
- import { join as join11, extname as extname2, dirname as dirname4 } from "path";
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";
18
18
  import { fileURLToPath } from "url";
19
19
 
20
20
  // src/api/server.ts
@@ -50,7 +50,8 @@ var TOOLS = [
50
50
  "pi",
51
51
  "craft",
52
52
  "droid",
53
- "zcode"
53
+ "zcode",
54
+ "trae"
54
55
  ];
55
56
  var MODEL_PROVIDER_MAP = [
56
57
  ["claude-", "anthropic"],
@@ -451,13 +452,11 @@ var OpenClawParser = class {
451
452
  const cacheReadTokens = usage.cacheRead ?? 0;
452
453
  const cacheWriteTokens = usage.cacheWrite ?? 0;
453
454
  const thinkingTokens = 0;
455
+ const loggedCost = usage.cost != null && typeof usage.cost === "object" ? Number(usage.cost.total ?? 0) : typeof usage.cost === "number" ? usage.cost : NaN;
454
456
  let cost;
455
457
  let costSource;
456
- if (usage.cost != null && typeof usage.cost === "object") {
457
- cost = usage.cost.total ?? 0;
458
- costSource = "log";
459
- } else if (typeof usage.cost === "number") {
460
- cost = usage.cost;
458
+ if (Number.isFinite(loggedCost) && loggedCost > 0) {
459
+ cost = loggedCost;
461
460
  costSource = "log";
462
461
  } else if (model === "unknown") {
463
462
  cost = 0;
@@ -470,7 +469,7 @@ var OpenClawParser = class {
470
469
  cacheWriteTokens,
471
470
  thinkingTokens
472
471
  }, context.exchangeRate);
473
- costSource = "pricing";
472
+ costSource = cost > 0 ? "pricing" : "unknown";
474
473
  }
475
474
  const provider = parsed.message.provider ?? inferProvider(model);
476
475
  const record = {
@@ -1436,7 +1435,12 @@ function defaultCursorDbPath() {
1436
1435
  return join3(xdgConfigHome, "Cursor", "User", "globalStorage", "state.vscdb");
1437
1436
  }
1438
1437
  function defaultKiloDbPath() {
1439
- return join3(xdgDataDir(homedir2(), "kilo"), "kilo.db");
1438
+ const home = homedir2();
1439
+ if (platform() === "win32") {
1440
+ const appData = process.env.APPDATA ?? join3(home, "AppData", "Roaming");
1441
+ return join3(appData, "kilo", "kilo.db");
1442
+ }
1443
+ return join3(xdgDataDir(home, "kilo"), "kilo.db");
1440
1444
  }
1441
1445
  function defaultGooseDbPath() {
1442
1446
  const home = homedir2();
@@ -1604,24 +1608,47 @@ function probeKiro(ctx) {
1604
1608
  if (override) return override;
1605
1609
  const legacy = ctx.legacySources?.["kiro"];
1606
1610
  if (legacy) return legacy;
1607
- const ideDb = join3(
1608
- ctx.home,
1609
- "Library",
1610
- "Application Support",
1611
- "Kiro",
1612
- "User",
1613
- "globalStorage",
1614
- "kiro.kiroagent",
1615
- "dev_data",
1616
- "devdata.sqlite"
1617
- );
1611
+ return probeKiroDefaultPath(ctx);
1612
+ }
1613
+ function probeKiroOverridePath(ctx) {
1614
+ const override = envOverride("kiro", ctx.env);
1615
+ if (override) return override;
1616
+ const legacy = ctx.legacySources?.["kiro"];
1617
+ if (legacy) return legacy;
1618
+ return null;
1619
+ }
1620
+ function probeKiroDefaultPath(ctx) {
1621
+ const devDataBase = kiroDevDataDir(ctx);
1622
+ const ideDb = join3(devDataBase, "devdata.sqlite");
1623
+ const tokensJsonl = join3(devDataBase, "tokens_generated.jsonl");
1618
1624
  const cliSessions = join3(ctx.env.KIRO_HOME ?? join3(ctx.home, ".kiro"), "sessions", "cli");
1619
- const appSupport = platform() === "darwin" ? join3(ctx.home, "Library", "Application Support", "kiro-cli", "data.sqlite3") : join3(ctx.env.XDG_DATA_HOME ?? join3(ctx.home, ".local", "share"), "kiro-cli", "data.sqlite3");
1620
- if (existsSync3(ideDb)) return ideDb;
1625
+ const appSupport = platform() === "darwin" ? join3(ctx.home, "Library", "Application Support", "kiro-cli", "data.sqlite3") : platform() === "win32" ? join3(ctx.env.APPDATA ?? join3(ctx.home, "AppData", "Roaming"), "kiro-cli", "data.sqlite3") : join3(ctx.env.XDG_DATA_HOME ?? join3(ctx.home, ".local", "share"), "kiro-cli", "data.sqlite3");
1626
+ if (existsSync3(ideDb) && statSync(ideDb).size > 0) return ideDb;
1621
1627
  if (existsSync3(appSupport)) return appSupport;
1622
1628
  if (existsSync3(cliSessions)) return cliSessions;
1629
+ if (existsSync3(tokensJsonl)) return devDataBase;
1623
1630
  return null;
1624
1631
  }
1632
+ function kiroDevDataDir(ctx) {
1633
+ if (platform() === "darwin") {
1634
+ return join3(ctx.home, "Library", "Application Support", "Kiro", "User", "globalStorage", "kiro.kiroagent", "dev_data");
1635
+ }
1636
+ if (platform() === "win32") {
1637
+ const appData = ctx.env.APPDATA ?? join3(ctx.home, "AppData", "Roaming");
1638
+ return join3(appData, "Kiro", "User", "globalStorage", "kiro.kiroagent", "dev_data");
1639
+ }
1640
+ return join3(ctx.env.XDG_DATA_HOME ?? join3(ctx.home, ".local", "share"), "Kiro", "User", "globalStorage", "kiro.kiroagent", "dev_data");
1641
+ }
1642
+ function kiroWorkspaceSessionsDir(ctx) {
1643
+ if (platform() === "darwin") {
1644
+ return join3(ctx.home, "Library", "Application Support", "Kiro", "User", "globalStorage", "kiro.kiroagent", "workspace-sessions");
1645
+ }
1646
+ if (platform() === "win32") {
1647
+ const appData = ctx.env.APPDATA ?? join3(ctx.home, "AppData", "Roaming");
1648
+ return join3(appData, "Kiro", "User", "globalStorage", "kiro.kiroagent", "workspace-sessions");
1649
+ }
1650
+ return join3(ctx.env.XDG_DATA_HOME ?? join3(ctx.home, ".local", "share"), "Kiro", "User", "globalStorage", "kiro.kiroagent", "workspace-sessions");
1651
+ }
1625
1652
  function probeGrok(ctx) {
1626
1653
  const override = envOverride("grok", ctx.env);
1627
1654
  if (override) return override;
@@ -1725,6 +1752,27 @@ function probeDroid(ctx) {
1725
1752
  const dir = join3(ctx.home, ".droid", "sessions");
1726
1753
  return existsSync3(dir) ? dir : null;
1727
1754
  }
1755
+ function probeTrae(ctx) {
1756
+ const override = envOverride("trae", ctx.env);
1757
+ if (override) return override;
1758
+ const legacy = ctx.legacySources?.["trae"];
1759
+ if (legacy) return legacy;
1760
+ const appNames = platform() === "darwin" ? ["Trae CN", "TRAE SOLO CN", "Trae"] : platform() === "win32" ? ["Trae CN", "TRAE SOLO CN", "Trae"] : ["Trae CN", "TRAE SOLO CN", "Trae"];
1761
+ for (const appName of appNames) {
1762
+ let dbPath;
1763
+ if (platform() === "darwin") {
1764
+ dbPath = join3(ctx.home, "Library", "Application Support", appName, "User", "globalStorage", "state.vscdb");
1765
+ } else if (platform() === "win32") {
1766
+ const appData = ctx.env.APPDATA ?? join3(ctx.home, "AppData", "Roaming");
1767
+ dbPath = join3(appData, appName, "User", "globalStorage", "state.vscdb");
1768
+ } else {
1769
+ const config = ctx.env.XDG_CONFIG_HOME ?? join3(ctx.home, ".config");
1770
+ dbPath = join3(config, appName, "User", "globalStorage", "state.vscdb");
1771
+ }
1772
+ if (existsSync3(dbPath)) return dbPath;
1773
+ }
1774
+ return null;
1775
+ }
1728
1776
  function defaultQoderSessionDirs(home) {
1729
1777
  const windowsHomes = [
1730
1778
  process.env.USERPROFILE,
@@ -1772,7 +1820,8 @@ var TOOL_REGISTRY = [
1772
1820
  { tool: "omp", sourceKey: "omp", label: "oh-my-pi", probe: probeOmp },
1773
1821
  { tool: "pi", sourceKey: "pi", label: "pi", probe: probePi },
1774
1822
  { tool: "craft", sourceKey: "craft", label: "Craft", probe: probeCraft },
1775
- { tool: "droid", sourceKey: "droid", label: "Droid", probe: probeDroid }
1823
+ { tool: "droid", sourceKey: "droid", label: "Droid", probe: probeDroid },
1824
+ { tool: "trae", sourceKey: "trae", label: "Trae", probe: probeTrae }
1776
1825
  ];
1777
1826
  function discoverTools(env = process.env) {
1778
1827
  const home = homedir2();
@@ -1889,7 +1938,9 @@ function discoverLogFiles(env = process.env) {
1889
1938
  { tool: "gemini", path: probeGemini(ctx) },
1890
1939
  { tool: "kimi", path: probeKimi(ctx), filter: (p) => basename(p) === "wire.jsonl" },
1891
1940
  { tool: "codebuddy", path: probeCodeBuddy(ctx) },
1892
- { tool: "kiro", path: probeKiro(ctx), filter: (p) => extname(p) === ".jsonl" || extname(p) === ".json" },
1941
+ { tool: "kiro", path: kiroDevDataDir(ctx), filter: (p) => extname(p) === ".jsonl" || extname(p) === ".json" },
1942
+ { tool: "kiro", path: probeKiroOverridePath(ctx), filter: (p) => extname(p) === ".jsonl" || extname(p) === ".json" },
1943
+ { tool: "kiro", path: kiroWorkspaceSessionsDir(ctx), filter: (p) => extname(p) === ".json" && basename(p) !== "sessions.json" },
1893
1944
  { tool: "grok", path: probeGrok(ctx), filter: (p) => extname(p) === ".jsonl" },
1894
1945
  { tool: "antigravity", path: probeAntigravity(ctx) },
1895
1946
  { tool: "omp", path: probeOmp(ctx) },
@@ -1918,6 +1969,49 @@ function discoverLogFiles(env = process.env) {
1918
1969
  const paths = statSync(kelivoPath).isDirectory() ? [...findJsonFiles(kelivoPath).filter((p) => basename(p) === "chats.json"), ...findZipFiles(kelivoPath)] : basename(kelivoPath) === "chats.json" || basename(kelivoPath).endsWith(".zip") ? [kelivoPath] : [];
1919
1970
  if (paths.length > 0) results.push({ tool: "kelivo", paths: unique(paths) });
1920
1971
  }
1972
+ const cursorProjectDirs = cursorProjectRoots(ctx);
1973
+ for (const cursorHome of cursorProjectDirs) {
1974
+ if (!existsSync3(cursorHome)) continue;
1975
+ const transcriptPaths = findCursorTranscriptFiles(cursorHome);
1976
+ if (transcriptPaths.length > 0) {
1977
+ const existing = results.find((r) => r.tool === "cursor");
1978
+ if (existing) {
1979
+ existing.paths = unique([...existing.paths, ...transcriptPaths]);
1980
+ } else {
1981
+ results.push({ tool: "cursor", paths: transcriptPaths });
1982
+ }
1983
+ }
1984
+ }
1985
+ return results;
1986
+ }
1987
+ function cursorProjectRoots(ctx) {
1988
+ const roots = [join3(ctx.home, ".cursor", "projects")];
1989
+ if (platform() === "win32") {
1990
+ const userProfile = ctx.env.USERPROFILE ?? ctx.home;
1991
+ roots.push(join3(userProfile, ".cursor", "projects"));
1992
+ }
1993
+ return unique(roots);
1994
+ }
1995
+ function findCursorTranscriptFiles(dir) {
1996
+ const results = [];
1997
+ try {
1998
+ const entries = readdirSync(dir, { withFileTypes: true });
1999
+ for (const entry of entries) {
2000
+ if (!entry.isDirectory()) continue;
2001
+ const agentDir = join3(dir, entry.name, "agent-transcripts");
2002
+ if (!existsSync3(agentDir)) continue;
2003
+ try {
2004
+ const sessions = readdirSync(agentDir, { withFileTypes: true });
2005
+ for (const session of sessions) {
2006
+ if (!session.isDirectory()) continue;
2007
+ const jsonlPath = join3(agentDir, session.name, `${session.name}.jsonl`);
2008
+ if (existsSync3(jsonlPath)) results.push(jsonlPath);
2009
+ }
2010
+ } catch {
2011
+ }
2012
+ }
2013
+ } catch {
2014
+ }
1921
2015
  return results;
1922
2016
  }
1923
2017
  function findJsonFiles(dir) {
@@ -3201,6 +3295,21 @@ function loadPricingRuntime(db, config) {
3201
3295
  for (const [model, entry] of Object.entries(configOverrides)) overrides[model] = entry;
3202
3296
  setRuntimePriceTable(builtin, overrides);
3203
3297
  }
3298
+ function importConfigPriceOverrides(db, overrides) {
3299
+ const imported = [];
3300
+ for (const [modelKey, entry] of Object.entries(overrides)) {
3301
+ if (!modelKey.trim()) continue;
3302
+ const existingUserPrice = db.prepare(`
3303
+ SELECT 1 FROM model_prices
3304
+ WHERE model_key = ? AND origin = 'user' AND status = 'active'
3305
+ LIMIT 1
3306
+ `).get(modelKey);
3307
+ if (existingUserPrice) continue;
3308
+ setUserPrice(db, modelKey, entry);
3309
+ imported.push(modelKey);
3310
+ }
3311
+ return imported;
3312
+ }
3204
3313
  function setUserPrice(db, modelKey, entry) {
3205
3314
  const now = Date.now();
3206
3315
  const baseline = db.prepare(`
@@ -3455,6 +3564,14 @@ function priceTableFromDb(db) {
3455
3564
  for (const row of rows) table[row.model_key] = rowToPrice(row);
3456
3565
  return table;
3457
3566
  }
3567
+ function hasUserPrice(db, model) {
3568
+ const row = db.prepare(`
3569
+ SELECT 1 FROM model_prices
3570
+ WHERE model_key = ? AND origin = 'user' AND status = 'active'
3571
+ LIMIT 1
3572
+ `).get(model);
3573
+ return Boolean(row);
3574
+ }
3458
3575
  function resolvePriceFromRegistry(db, model) {
3459
3576
  const directUserPrice = db.prepare(`
3460
3577
  SELECT input, output, cache_read, cache_write, currency
@@ -3914,10 +4031,10 @@ function getClientPlatform() {
3914
4031
  }
3915
4032
  function getCliVersion() {
3916
4033
  try {
3917
- const { readFileSync: readFileSync12 } = __require("fs");
3918
- const { join: join16 } = __require("path");
3919
- const pkgPath = join16(__dirname, "../../package.json");
3920
- const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
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"));
3921
4038
  return pkg.version || "0.0.0";
3922
4039
  } catch {
3923
4040
  return "0.0.0";
@@ -4529,12 +4646,12 @@ async function recalcCosts(db, onProgress) {
4529
4646
  const tx = db.transaction((batch) => {
4530
4647
  for (const r of batch) {
4531
4648
  processed++;
4532
- if (r.cost_source === "log") {
4649
+ const rawModel = r.tool === "qoder" ? normalizeQoderModel(r.model) : r.model;
4650
+ const model = rawModel === "unknown" ? r.tool === "qoder" ? "qoder-auto" : r.model : rawModel;
4651
+ if (r.cost_source === "log" && r.cost > 0 && !hasUserPrice(db, model)) {
4533
4652
  skipped++;
4534
4653
  continue;
4535
4654
  }
4536
- const rawModel = r.tool === "qoder" ? normalizeQoderModel(r.model) : r.model;
4537
- const model = rawModel === "unknown" ? r.tool === "qoder" ? "qoder-auto" : r.model : rawModel;
4538
4655
  const provider = model !== r.model ? inferProvider(model) : r.provider;
4539
4656
  const price = resolveCachedPrice(model);
4540
4657
  const cost = price ? calculateCostForPrice(price, {
@@ -6128,8 +6245,8 @@ function createApiServer(db, options) {
6128
6245
 
6129
6246
  // src/commands/parse.ts
6130
6247
  import Database2 from "better-sqlite3";
6131
- import { readFileSync as readFileSync8, statSync as statSync2, existsSync as existsSync8, openSync, readSync, closeSync } from "fs";
6132
- import { join as join7 } from "path";
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";
6133
6250
  import { hostname as hostname2 } from "os";
6134
6251
 
6135
6252
  // src/watermark.ts
@@ -6159,7 +6276,8 @@ function defaultFileData() {
6159
6276
  "pi": {},
6160
6277
  "craft": {},
6161
6278
  "droid": {},
6162
- "zcode": {}
6279
+ "zcode": {},
6280
+ "trae": {}
6163
6281
  };
6164
6282
  }
6165
6283
  var WatermarkManager = class {
@@ -6179,7 +6297,7 @@ var WatermarkManager = class {
6179
6297
  if (parsed && typeof parsed === "object" && !("files" in parsed)) {
6180
6298
  return { files: { ...defaultFileData(), ...parsed } };
6181
6299
  }
6182
- 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 };
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 };
6183
6301
  } catch {
6184
6302
  return { files: defaultFileData() };
6185
6303
  }
@@ -6260,6 +6378,12 @@ var WatermarkManager = class {
6260
6378
  setZcodeToolCursor(cursor) {
6261
6379
  this.data.zcodeTools = cursor;
6262
6380
  }
6381
+ getTraeLastImported() {
6382
+ return this.data.trae ?? 0;
6383
+ }
6384
+ setTraeLastImported(ts3) {
6385
+ this.data.trae = ts3;
6386
+ }
6263
6387
  };
6264
6388
 
6265
6389
  // src/commands/parse-opencode.ts
@@ -6554,6 +6678,42 @@ function runParseQoder(db, options) {
6554
6678
  }
6555
6679
 
6556
6680
  // src/commands/parse-cursor.ts
6681
+ import { readFileSync as readFileSync8 } from "fs";
6682
+ function runParseCursorTranscript(options) {
6683
+ const charsPerToken = 3;
6684
+ try {
6685
+ const content = readFileSync8(options.jsonlPath, "utf-8");
6686
+ const lines = content.split("\n");
6687
+ let userChars = 0;
6688
+ let assistChars = 0;
6689
+ for (const line of lines) {
6690
+ if (!line.trim()) continue;
6691
+ let data;
6692
+ try {
6693
+ data = JSON.parse(line);
6694
+ } catch {
6695
+ continue;
6696
+ }
6697
+ const role = data?.role ?? "";
6698
+ const blocks = data?.message?.content ?? [];
6699
+ if (!Array.isArray(blocks)) continue;
6700
+ for (const block of blocks) {
6701
+ if (typeof block !== "object" || block === null || block.type !== "text") continue;
6702
+ const text = block.text ?? "";
6703
+ const clean = text.replace(/\[REDACTED\]/g, "").trim();
6704
+ if (!clean) continue;
6705
+ if (role === "user") userChars += clean.length;
6706
+ else if (role === "assistant") assistChars += clean.length;
6707
+ }
6708
+ }
6709
+ return {
6710
+ inputTextChars: Math.floor(userChars / charsPerToken),
6711
+ outputTextChars: Math.floor(assistChars / charsPerToken)
6712
+ };
6713
+ } catch {
6714
+ return { inputTextChars: 0, outputTextChars: 0 };
6715
+ }
6716
+ }
6557
6717
  function runParseCursor(db, options) {
6558
6718
  const { dbPath, device, deviceInstanceId, platform: platform5, now, cursor } = options;
6559
6719
  const records = [];
@@ -7137,6 +7297,139 @@ function runParseZcode(db, options) {
7137
7297
  };
7138
7298
  }
7139
7299
 
7300
+ // src/commands/parse-trae.ts
7301
+ import { existsSync as existsSync8, readdirSync as readdirSync2 } from "fs";
7302
+ import { join as join7, basename as basename3 } from "path";
7303
+ import { spawnSync } from "child_process";
7304
+ function deriveDataDir(dbPath) {
7305
+ const normalized = dbPath.replace(/\\/g, "/");
7306
+ const appDir = normalized.replace(/\/User\/globalStorage\/state\.vscdb$/, "");
7307
+ const dataDir = join7(appDir, "ModularData", "ai-agent", "snapshot");
7308
+ return existsSync8(dataDir) ? dataDir : null;
7309
+ }
7310
+ function parseSessionTags(repoPath) {
7311
+ try {
7312
+ const sessionId = basename3(basename3(repoPath) === "v2" ? join7(repoPath, "..") : repoPath);
7313
+ 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");
7318
+ let chatTurns = 0;
7319
+ let toolCalls = 0;
7320
+ 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-")) {
7338
+ startTs = tagTs;
7339
+ } else if (t.startsWith("before-chat-turn-")) {
7340
+ chatTurns++;
7341
+ } else if (t.startsWith("toolcall-")) {
7342
+ toolCalls++;
7343
+ }
7344
+ }
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
+ }
7361
+ if (chatTurns === 0 && toolCalls === 0) return null;
7362
+ if (startTs === 0) return null;
7363
+ return { sessionId, startTs, chatTurns, toolCalls };
7364
+ } catch {
7365
+ return null;
7366
+ }
7367
+ }
7368
+ function runParseTrae(options) {
7369
+ const { dbPath, device, deviceInstanceId, platform: platform5, now, lastImportedAt } = options;
7370
+ const records = [];
7371
+ const toolCalls = [];
7372
+ const errors = [];
7373
+ let latestTs = lastImportedAt ?? 0;
7374
+ const dataDir = deriveDataDir(dbPath);
7375
+ if (!dataDir) {
7376
+ return { records, toolCalls, lastImportedAt: latestTs, errors: ["Trae ModularData not found"] };
7377
+ }
7378
+ let entries;
7379
+ try {
7380
+ entries = readdirSync2(dataDir, { withFileTypes: true });
7381
+ } catch (e) {
7382
+ return { records, toolCalls, lastImportedAt: latestTs, errors: [String(e)] };
7383
+ }
7384
+ for (const entry of entries) {
7385
+ if (!entry.isDirectory()) continue;
7386
+ const repoPath = join7(dataDir, entry.name, "v2");
7387
+ if (!existsSync8(join7(repoPath, ".git"))) continue;
7388
+ const session = parseSessionTags(repoPath);
7389
+ if (!session) continue;
7390
+ if (session.startTs < latestTs) continue;
7391
+ if (session.startTs > latestTs) latestTs = session.startTs;
7392
+ const estInputTokens = session.chatTurns * 200;
7393
+ const estOutputTokens = session.chatTurns * 800;
7394
+ const recordId = generateRecordId(deviceInstanceId, `${dbPath}:${session.sessionId}`, session.startTs);
7395
+ const record = {
7396
+ id: recordId,
7397
+ ts: session.startTs,
7398
+ ingestedAt: now,
7399
+ updatedAt: now,
7400
+ lineOffset: 0,
7401
+ tool: "trae",
7402
+ model: "trae-agent",
7403
+ provider: "trae",
7404
+ inputTokens: estInputTokens,
7405
+ outputTokens: estOutputTokens,
7406
+ cacheReadTokens: 0,
7407
+ cacheWriteTokens: 0,
7408
+ thinkingTokens: 0,
7409
+ cost: 0,
7410
+ costSource: "unknown",
7411
+ sessionId: session.sessionId,
7412
+ sourceFile: dbPath,
7413
+ device,
7414
+ deviceInstanceId,
7415
+ platform: platform5
7416
+ };
7417
+ records.push(record);
7418
+ let callIndex = 0;
7419
+ for (let i = 0; i < session.toolCalls; i++) {
7420
+ toolCalls.push({
7421
+ id: generateRecordId(recordId, "tool_call", session.startTs + callIndex),
7422
+ recordId,
7423
+ name: "trae-tool",
7424
+ ts: session.startTs + callIndex,
7425
+ callIndex
7426
+ });
7427
+ callIndex++;
7428
+ }
7429
+ }
7430
+ return { records, toolCalls, lastImportedAt: latestTs, errors };
7431
+ }
7432
+
7140
7433
  // src/commands/parse.ts
7141
7434
  function extractCwdFromJson(data) {
7142
7435
  if (typeof data.cwd === "string" && data.cwd) return data.cwd;
@@ -7180,6 +7473,7 @@ function extractSessionId2(filePath, tool) {
7180
7473
  }
7181
7474
  return "unknown";
7182
7475
  }
7476
+ var CHARS_PER_TOKEN2 = 4;
7183
7477
  function toNumber2(value) {
7184
7478
  const n = Number(value);
7185
7479
  return Number.isFinite(n) && n > 0 ? Math.floor(n) : 0;
@@ -7190,7 +7484,7 @@ function parseUiMessagesFile(options) {
7190
7484
  const errors = [];
7191
7485
  let messages;
7192
7486
  try {
7193
- const parsed = JSON.parse(readFileSync8(filePath, "utf-8"));
7487
+ const parsed = JSON.parse(readFileSync9(filePath, "utf-8"));
7194
7488
  messages = Array.isArray(parsed) ? parsed : [];
7195
7489
  } catch (e) {
7196
7490
  return { records, errors: [`${filePath}: ${e instanceof Error ? e.message : e}`] };
@@ -7257,7 +7551,7 @@ function parseTimestamp2(value, fallback) {
7257
7551
  }
7258
7552
  function pathIsFile(filePath) {
7259
7553
  try {
7260
- return statSync2(filePath).isFile();
7554
+ return statSync3(filePath).isFile();
7261
7555
  } catch {
7262
7556
  return false;
7263
7557
  }
@@ -7268,10 +7562,13 @@ function parseKiroSessionFile(options) {
7268
7562
  const errors = [];
7269
7563
  let parsed;
7270
7564
  try {
7271
- parsed = JSON.parse(readFileSync8(filePath, "utf-8"));
7565
+ parsed = JSON.parse(readFileSync9(filePath, "utf-8"));
7272
7566
  } catch (e) {
7273
7567
  return { records, errors: [`${filePath}: ${e instanceof Error ? e.message : e}`] };
7274
7568
  }
7569
+ if (Array.isArray(parsed?.history) && !parsed?.session_state) {
7570
+ return parseKiroWorkspaceSession({ filePath, parsed, device, deviceInstanceId, platform: platform5, now, exchangeRate });
7571
+ }
7275
7572
  const turns = parsed?.session_state?.conversation_metadata?.user_turn_metadatas;
7276
7573
  if (!Array.isArray(turns)) return { records, errors };
7277
7574
  const sessionId = typeof parsed?.session_id === "string" && parsed.session_id ? parsed.session_id : extractSessionId2(filePath, "kiro");
@@ -7313,6 +7610,83 @@ function parseKiroSessionFile(options) {
7313
7610
  }
7314
7611
  return { records, errors };
7315
7612
  }
7613
+ function parseKiroWorkspaceSession(options) {
7614
+ const { filePath, parsed, device, deviceInstanceId, platform: platform5, now, exchangeRate } = options;
7615
+ const records = [];
7616
+ const errors = [];
7617
+ const sessionId = parsed?.sessionId ?? extractSessionId2(filePath, "kiro");
7618
+ let sessionTs = now;
7619
+ try {
7620
+ const dir = dirname4(filePath);
7621
+ const sessionsJson = readFileSync9(join8(dir, "sessions.json"), "utf-8");
7622
+ const sessions = JSON.parse(sessionsJson);
7623
+ const match = Array.isArray(sessions) && sessions.find((s) => s.sessionId === sessionId);
7624
+ if (match?.dateCreated) {
7625
+ const dc = Number(match.dateCreated);
7626
+ if (Number.isFinite(dc) && dc > 0) sessionTs = dc < 1e12 ? dc * 1e3 : dc;
7627
+ }
7628
+ } catch {
7629
+ }
7630
+ const allLogs = [];
7631
+ let logIndex = 0;
7632
+ for (const entry of parsed.history ?? []) {
7633
+ for (const pl of entry?.promptLogs ?? []) {
7634
+ allLogs.push({ log: pl, index: logIndex++ });
7635
+ }
7636
+ }
7637
+ const contextLength = parsed?.config?.models?.[0]?.contextLength ?? 4e4;
7638
+ const maxTokens = parsed?.config?.models?.[0]?.completionOptions?.maxTokens ?? 4e3;
7639
+ const contextPct = Number(parsed?.contextUsagePercentageBySession?.[sessionId] ?? parsed?.contextUsagePercentage ?? 0) / 100;
7640
+ const totalInputEstimate = contextPct > 0 ? Math.floor(contextLength * contextPct) : 0;
7641
+ const hasNoTokenData = allLogs.every(({ log }) => {
7642
+ const pl = typeof log.prompt === "string" ? log.prompt.length : 0;
7643
+ const cl = typeof log.completion === "string" ? log.completion.length : 0;
7644
+ return pl < 100 && cl === 0;
7645
+ });
7646
+ for (const { log, index } of allLogs) {
7647
+ const promptLen = typeof log.prompt === "string" ? log.prompt.length : 0;
7648
+ const completionLen = typeof log.completion === "string" ? log.completion.length : 0;
7649
+ let inputTokens;
7650
+ let outputTokens;
7651
+ if (hasNoTokenData) {
7652
+ inputTokens = totalInputEstimate > 0 && allLogs.length > 0 ? Math.max(1, Math.floor(totalInputEstimate / allLogs.length)) : 200;
7653
+ outputTokens = Math.floor(maxTokens * 0.3);
7654
+ } else {
7655
+ inputTokens = totalInputEstimate > 0 && allLogs.length > 0 ? Math.max(1, Math.floor(totalInputEstimate / allLogs.length)) : Math.max(1, Math.floor(promptLen / CHARS_PER_TOKEN2));
7656
+ outputTokens = completionLen > 0 ? Math.floor(completionLen / CHARS_PER_TOKEN2) : Math.floor(maxTokens * 0.3);
7657
+ }
7658
+ if (inputTokens + outputTokens === 0) continue;
7659
+ const model = normalizeKiroModel(log.completionOptions?.model ?? log.modelTitle);
7660
+ const provider = typeof log.provider === "string" && log.provider.trim() ? log.provider.trim().toLowerCase() : inferProvider(model);
7661
+ const recordTs = sessionTs + index;
7662
+ const tokenArgs = { inputTokens, outputTokens, cacheReadTokens: 0, cacheWriteTokens: 0, thinkingTokens: 0 };
7663
+ const cost = calculateCost(model, tokenArgs, exchangeRate);
7664
+ const sourceId = `${sessionId}:${index}`;
7665
+ records.push({
7666
+ id: generateRecordId(deviceInstanceId, `${filePath}:${sourceId}`, recordTs),
7667
+ ts: recordTs,
7668
+ ingestedAt: now,
7669
+ updatedAt: now,
7670
+ lineOffset: index,
7671
+ tool: "kiro",
7672
+ model,
7673
+ provider,
7674
+ inputTokens,
7675
+ outputTokens,
7676
+ cacheReadTokens: 0,
7677
+ cacheWriteTokens: 0,
7678
+ thinkingTokens: 0,
7679
+ cost,
7680
+ costSource: cost > 0 ? "pricing" : "unknown",
7681
+ sessionId,
7682
+ sourceFile: filePath,
7683
+ device,
7684
+ deviceInstanceId,
7685
+ platform: platform5
7686
+ });
7687
+ }
7688
+ return { records, errors };
7689
+ }
7316
7690
  async function runParse(db, filterTool, options) {
7317
7691
  const state = getState(AIUSAGE_DIR);
7318
7692
  const config = loadConfig();
@@ -7320,7 +7694,7 @@ async function runParse(db, filterTool, options) {
7320
7694
  const device = config?.device || hostname2() || state?.deviceInstanceId?.slice(0, 8) || "unknown";
7321
7695
  const deviceInstanceId = state?.deviceInstanceId ?? "unknown";
7322
7696
  const devicePlatform = config?.platform;
7323
- const watermarkPath = join7(AIUSAGE_DIR, "watermark.json");
7697
+ const watermarkPath = join8(AIUSAGE_DIR, "watermark.json");
7324
7698
  const wm = new WatermarkManager(watermarkPath);
7325
7699
  const toolPaths = discoverLogFiles();
7326
7700
  const aggregator = new Aggregator();
@@ -7336,7 +7710,7 @@ async function runParse(db, filterTool, options) {
7336
7710
  for (const filePath of paths) {
7337
7711
  toolIndex++;
7338
7712
  try {
7339
- const stat2 = statSync2(filePath);
7713
+ const stat2 = statSync3(filePath);
7340
7714
  const entry = wm.getEntry(tool, filePath);
7341
7715
  const offset = entry?.offset ?? 0;
7342
7716
  if (offset >= stat2.size) {
@@ -7390,6 +7764,66 @@ async function runParse(db, filterTool, options) {
7390
7764
  onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
7391
7765
  continue;
7392
7766
  }
7767
+ if (tool === "kiro" && filePath.endsWith(".jsonl")) {
7768
+ const content2 = readFileSync9(filePath, "utf-8");
7769
+ const lines2 = content2.split("\n");
7770
+ let byteOffset2 = 0;
7771
+ for (const line of lines2) {
7772
+ if (!line.trim()) {
7773
+ byteOffset2 += Buffer.byteLength(line, "utf-8") + 1;
7774
+ continue;
7775
+ }
7776
+ try {
7777
+ const data = JSON.parse(line);
7778
+ const inputTokens = toNumber2(data.promptTokens ?? data.inputTokens ?? data.tokensIn);
7779
+ const outputTokens = toNumber2(data.generatedTokens ?? data.outputTokens ?? data.tokensOut);
7780
+ if (inputTokens + outputTokens === 0) {
7781
+ byteOffset2 += Buffer.byteLength(line, "utf-8") + 1;
7782
+ continue;
7783
+ }
7784
+ const model = normalizeKiroModel(data.model);
7785
+ const provider = typeof data.provider === "string" && data.provider.trim() ? data.provider.trim().toLowerCase() : inferProvider(model);
7786
+ const recordTs = stat2.mtimeMs;
7787
+ const tokenArgs = { inputTokens, outputTokens, cacheReadTokens: 0, cacheWriteTokens: 0, thinkingTokens: 0 };
7788
+ const cost = calculateCost(model, tokenArgs, exchangeRate);
7789
+ const recordId = generateRecordId(deviceInstanceId, `${filePath}:${byteOffset2}`, recordTs);
7790
+ const record = {
7791
+ id: recordId,
7792
+ ts: recordTs,
7793
+ ingestedAt: Date.now(),
7794
+ updatedAt: Date.now(),
7795
+ lineOffset: byteOffset2,
7796
+ tool: "kiro",
7797
+ model,
7798
+ provider,
7799
+ inputTokens,
7800
+ outputTokens,
7801
+ cacheReadTokens: 0,
7802
+ cacheWriteTokens: 0,
7803
+ thinkingTokens: 0,
7804
+ cost,
7805
+ costSource: cost > 0 ? "pricing" : "unknown",
7806
+ sessionId: filePath,
7807
+ sourceFile: filePath,
7808
+ device,
7809
+ deviceInstanceId,
7810
+ platform: devicePlatform
7811
+ };
7812
+ insertRecord(db, record);
7813
+ parsedCount++;
7814
+ } catch {
7815
+ }
7816
+ byteOffset2 += Buffer.byteLength(line, "utf-8") + 1;
7817
+ }
7818
+ wm.setEntry(tool, filePath, {
7819
+ offset: stat2.size,
7820
+ size: stat2.size,
7821
+ mtime: stat2.mtimeMs
7822
+ });
7823
+ wm.save();
7824
+ onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
7825
+ continue;
7826
+ }
7393
7827
  if (tool === "kelivo" && (filePath.endsWith("chats.json") || filePath.endsWith(".zip"))) {
7394
7828
  const result = await runParseKelivo({
7395
7829
  filePath,
@@ -7413,7 +7847,55 @@ async function runParse(db, filterTool, options) {
7413
7847
  onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
7414
7848
  continue;
7415
7849
  }
7416
- const content = readFileSync8(filePath, "utf-8");
7850
+ if (tool === "cursor" && filePath.endsWith(".jsonl") && filePath.includes("agent-transcripts")) {
7851
+ const est = runParseCursorTranscript({
7852
+ jsonlPath: filePath,
7853
+ device,
7854
+ deviceInstanceId,
7855
+ platform: devicePlatform,
7856
+ now: Date.now()
7857
+ });
7858
+ if (est.inputTextChars + est.outputTextChars > 0) {
7859
+ const sessionId2 = filePath.split("/").slice(-2, -1)[0] || "unknown";
7860
+ const recordTs = stat2.mtimeMs;
7861
+ const tokenArgs = { inputTokens: est.inputTextChars, outputTokens: est.outputTextChars, cacheReadTokens: 0, cacheWriteTokens: 0, thinkingTokens: 0 };
7862
+ const cost = calculateCost("cursor-composer", tokenArgs, exchangeRate);
7863
+ const recordId = generateRecordId(deviceInstanceId, filePath, recordTs);
7864
+ const record = {
7865
+ id: recordId,
7866
+ ts: recordTs,
7867
+ ingestedAt: Date.now(),
7868
+ updatedAt: Date.now(),
7869
+ lineOffset: 0,
7870
+ tool: "cursor",
7871
+ model: "cursor-composer",
7872
+ provider: "cursor",
7873
+ inputTokens: est.inputTextChars,
7874
+ outputTokens: est.outputTextChars,
7875
+ cacheReadTokens: 0,
7876
+ cacheWriteTokens: 0,
7877
+ thinkingTokens: 0,
7878
+ cost,
7879
+ costSource: cost > 0 ? "pricing" : "unknown",
7880
+ sessionId: sessionId2,
7881
+ sourceFile: filePath,
7882
+ device,
7883
+ deviceInstanceId,
7884
+ platform: devicePlatform
7885
+ };
7886
+ insertRecord(db, record);
7887
+ parsedCount++;
7888
+ }
7889
+ wm.setEntry(tool, filePath, {
7890
+ offset: stat2.size,
7891
+ size: stat2.size,
7892
+ mtime: stat2.mtimeMs
7893
+ });
7894
+ wm.save();
7895
+ onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
7896
+ continue;
7897
+ }
7898
+ const content = readFileSync9(filePath, "utf-8");
7417
7899
  const lines = content.split("\n");
7418
7900
  let byteOffset = 0;
7419
7901
  let fileCwd;
@@ -7492,7 +7974,7 @@ async function runParse(db, filterTool, options) {
7492
7974
  }
7493
7975
  }
7494
7976
  const openCodeDbPath = options?.openCodeDbPath ?? getDbPath("opencode") ?? "";
7495
- if ((!filterTool || filterTool === "opencode") && existsSync8(openCodeDbPath)) {
7977
+ if ((!filterTool || filterTool === "opencode") && existsSync9(openCodeDbPath)) {
7496
7978
  try {
7497
7979
  const openCodeDb = new Database2(openCodeDbPath, { readonly: true });
7498
7980
  try {
@@ -7523,7 +8005,7 @@ async function runParse(db, filterTool, options) {
7523
8005
  }
7524
8006
  }
7525
8007
  const hermesDbPath = options?.hermesDbPath ?? getDbPath("hermes") ?? "";
7526
- if ((!filterTool || filterTool === "hermes") && existsSync8(hermesDbPath)) {
8008
+ if ((!filterTool || filterTool === "hermes") && existsSync9(hermesDbPath)) {
7527
8009
  try {
7528
8010
  const hermesDb = new Database2(hermesDbPath, { readonly: true });
7529
8011
  try {
@@ -7554,7 +8036,7 @@ async function runParse(db, filterTool, options) {
7554
8036
  }
7555
8037
  }
7556
8038
  const qoderDbPath = options?.qoderDbPath ?? getDbPath("qoder-db") ?? "";
7557
- if ((!filterTool || filterTool === "qoder") && existsSync8(qoderDbPath)) {
8039
+ if ((!filterTool || filterTool === "qoder") && existsSync9(qoderDbPath)) {
7558
8040
  try {
7559
8041
  const qoderDb = new Database2(qoderDbPath, { readonly: true });
7560
8042
  try {
@@ -7585,7 +8067,7 @@ async function runParse(db, filterTool, options) {
7585
8067
  }
7586
8068
  }
7587
8069
  const cursorDbPath = options?.cursorDbPath ?? getDbPath("cursor") ?? "";
7588
- if ((!filterTool || filterTool === "cursor") && existsSync8(cursorDbPath)) {
8070
+ if ((!filterTool || filterTool === "cursor") && existsSync9(cursorDbPath)) {
7589
8071
  try {
7590
8072
  const cursorDb = new Database2(cursorDbPath, { readonly: true });
7591
8073
  try {
@@ -7615,7 +8097,7 @@ async function runParse(db, filterTool, options) {
7615
8097
  }
7616
8098
  }
7617
8099
  const kiloDbPath = getDbPath("kilocode-db") ?? "";
7618
- if ((!filterTool || filterTool === "kilocode") && existsSync8(kiloDbPath)) {
8100
+ if ((!filterTool || filterTool === "kilocode") && existsSync9(kiloDbPath)) {
7619
8101
  try {
7620
8102
  const kiloDb = new Database2(kiloDbPath, { readonly: true });
7621
8103
  try {
@@ -7655,7 +8137,7 @@ async function runParse(db, filterTool, options) {
7655
8137
  }
7656
8138
  }
7657
8139
  const gooseDbPath = getDbPath("goose") ?? "";
7658
- if ((!filterTool || filterTool === "goose") && existsSync8(gooseDbPath)) {
8140
+ if ((!filterTool || filterTool === "goose") && existsSync9(gooseDbPath)) {
7659
8141
  try {
7660
8142
  const gooseDb = new Database2(gooseDbPath, { readonly: true });
7661
8143
  try {
@@ -7713,7 +8195,7 @@ async function runParse(db, filterTool, options) {
7713
8195
  }
7714
8196
  }
7715
8197
  const zedDbPath = getDbPath("zed") ?? "";
7716
- if ((!filterTool || filterTool === "zed") && existsSync8(zedDbPath)) {
8198
+ if ((!filterTool || filterTool === "zed") && existsSync9(zedDbPath)) {
7717
8199
  try {
7718
8200
  const zedDb = new Database2(zedDbPath, { readonly: true });
7719
8201
  try {
@@ -7743,7 +8225,7 @@ async function runParse(db, filterTool, options) {
7743
8225
  }
7744
8226
  if (!filterTool || filterTool === "zcode") {
7745
8227
  const zcodeDbPath = getDbPath("zcode") ?? "";
7746
- if (existsSync8(zcodeDbPath)) {
8228
+ if (existsSync9(zcodeDbPath)) {
7747
8229
  try {
7748
8230
  const zcodeDb = new Database2(zcodeDbPath, { readonly: true });
7749
8231
  try {
@@ -7780,6 +8262,28 @@ async function runParse(db, filterTool, options) {
7780
8262
  }
7781
8263
  }
7782
8264
  }
8265
+ const traeDbPath = getDbPath("trae") ?? "";
8266
+ if ((!filterTool || filterTool === "trae") && pathIsFile(traeDbPath)) {
8267
+ const traeLastImported = wm.getTraeLastImported?.() ?? 0;
8268
+ const result = runParseTrae({
8269
+ dbPath: traeDbPath,
8270
+ device,
8271
+ deviceInstanceId,
8272
+ platform: devicePlatform,
8273
+ now: Date.now(),
8274
+ lastImportedAt: traeLastImported
8275
+ });
8276
+ for (const record of result.records) insertRecord(db, record);
8277
+ for (const tc of result.toolCalls) insertToolCall(db, tc);
8278
+ parsedCount += result.records.length;
8279
+ toolCallCount += result.toolCalls.length;
8280
+ errors.push(...result.errors);
8281
+ if (result.lastImportedAt > traeLastImported) {
8282
+ wm.setTraeLastImported?.(result.lastImportedAt);
8283
+ wm.save();
8284
+ }
8285
+ onProgress({ phase: "Parsing SQLite", tool: "trae", current: 1, total: 1, records: parsedCount, toolCalls: toolCallCount });
8286
+ }
7783
8287
  if (deviceInstanceId !== "unknown") {
7784
8288
  db.prepare(
7785
8289
  `UPDATE records SET device_instance_id = ?, device = ? WHERE device_instance_id = 'unknown'`
@@ -7826,7 +8330,7 @@ function backfillHermesSourceFiles(db) {
7826
8330
  const dbPath = rows[0].source_file;
7827
8331
  let titleMap = /* @__PURE__ */ new Map();
7828
8332
  try {
7829
- if (existsSync8(dbPath)) {
8333
+ if (existsSync9(dbPath)) {
7830
8334
  const hermesDb = new Database2(dbPath, { readonly: true });
7831
8335
  try {
7832
8336
  const sessions = hermesDb.prepare(
@@ -7907,7 +8411,7 @@ function backfillCodexModels(db) {
7907
8411
  );
7908
8412
  for (const [sourceFile, fileRows] of byFile) {
7909
8413
  try {
7910
- const content = readFileSync8(sourceFile, "utf-8");
8414
+ const content = readFileSync9(sourceFile, "utf-8");
7911
8415
  const lines = content.split("\n");
7912
8416
  const offsetToModel = /* @__PURE__ */ new Map();
7913
8417
  let currentModel = "";
@@ -7955,7 +8459,7 @@ function backfillMissingToolCalls(db, exchangeRate) {
7955
8459
  }
7956
8460
  for (const [sourceFile, fileRows] of rowsByFile) {
7957
8461
  try {
7958
- const content = readFileSync8(sourceFile, "utf-8");
8462
+ const content = readFileSync9(sourceFile, "utf-8");
7959
8463
  const lines = content.split("\n");
7960
8464
  const aggregator = new Aggregator();
7961
8465
  const recordIdByOffset = /* @__PURE__ */ new Map();
@@ -8017,7 +8521,7 @@ function deriveSessionId(tool, sourceFile) {
8017
8521
  }
8018
8522
 
8019
8523
  // src/commands/sync.ts
8020
- import { join as join9 } from "path";
8524
+ import { join as join10 } from "path";
8021
8525
 
8022
8526
  // src/db/synced-records.ts
8023
8527
  function insertSyncedRecord(db, record) {
@@ -8450,10 +8954,10 @@ async function cloudClear() {
8450
8954
  }
8451
8955
  function getClientVersion() {
8452
8956
  try {
8453
- const { readFileSync: readFileSync12 } = __require("fs");
8454
- const { join: join16 } = __require("path");
8455
- const pkgPath = join16(__dirname, "../../package.json");
8456
- const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
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"));
8457
8961
  return pkg.version || "0.0.0";
8458
8962
  } catch {
8459
8963
  return "0.0.0";
@@ -8544,7 +9048,7 @@ var CloudSyncOrchestrator = class {
8544
9048
  import { execFile } from "child_process";
8545
9049
  import { promisify } from "util";
8546
9050
  import { readFile, writeFile, mkdir, readdir, stat, unlink, rm } from "fs/promises";
8547
- import { join as join8 } from "path";
9051
+ import { join as join9 } from "path";
8548
9052
  var exec = promisify(execFile);
8549
9053
  var GitSyncBackend = class _GitSyncBackend {
8550
9054
  repo;
@@ -8556,7 +9060,7 @@ var GitSyncBackend = class _GitSyncBackend {
8556
9060
  this.repo = config.repo;
8557
9061
  this.token = config.token;
8558
9062
  this.cacheDir = config.cacheDir;
8559
- this.dataDir = join8(config.cacheDir, "data");
9063
+ this.dataDir = join9(config.cacheDir, "data");
8560
9064
  this.branch = config.branch ?? "main";
8561
9065
  }
8562
9066
  get remoteUrl() {
@@ -8588,7 +9092,7 @@ var GitSyncBackend = class _GitSyncBackend {
8588
9092
  /** Clone or pull the repo to get latest remote state */
8589
9093
  async prepare() {
8590
9094
  try {
8591
- await stat(join8(this.cacheDir, ".git"));
9095
+ await stat(join9(this.cacheDir, ".git"));
8592
9096
  await this.git(["fetch", "origin", this.branch, "--depth=1"]);
8593
9097
  await this.git(["reset", "--hard", `origin/${this.branch}`]);
8594
9098
  } catch {
@@ -8605,15 +9109,15 @@ var GitSyncBackend = class _GitSyncBackend {
8605
9109
  }
8606
9110
  async readFile(path3) {
8607
9111
  try {
8608
- const fullPath = join8(this.dataDir, path3);
9112
+ const fullPath = join9(this.dataDir, path3);
8609
9113
  return await readFile(fullPath, "utf-8");
8610
9114
  } catch {
8611
9115
  return null;
8612
9116
  }
8613
9117
  }
8614
9118
  async writeFile(path3, content) {
8615
- const fullPath = join8(this.dataDir, path3);
8616
- await mkdir(join8(fullPath, ".."), { recursive: true });
9119
+ const fullPath = join9(this.dataDir, path3);
9120
+ await mkdir(join9(fullPath, ".."), { recursive: true });
8617
9121
  await writeFile(fullPath, content, "utf-8");
8618
9122
  }
8619
9123
  async listFiles() {
@@ -8624,7 +9128,7 @@ var GitSyncBackend = class _GitSyncBackend {
8624
9128
  }
8625
9129
  }
8626
9130
  async deleteFile(path3) {
8627
- const fullPath = join8(this.dataDir, path3);
9131
+ const fullPath = join9(this.dataDir, path3);
8628
9132
  try {
8629
9133
  await unlink(fullPath);
8630
9134
  } catch {
@@ -8663,7 +9167,7 @@ var GitSyncBackend = class _GitSyncBackend {
8663
9167
  for (const entry of entries) {
8664
9168
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
8665
9169
  if (entry.isDirectory()) {
8666
- files.push(...await this.walkDir(join8(dir, entry.name), relPath));
9170
+ files.push(...await this.walkDir(join9(dir, entry.name), relPath));
8667
9171
  } else if (entry.name.endsWith(".ndjson")) {
8668
9172
  files.push(relPath);
8669
9173
  }
@@ -8797,7 +9301,7 @@ function createBackend(config) {
8797
9301
  return new GitSyncBackend({
8798
9302
  repo: sync.repo,
8799
9303
  token,
8800
- cacheDir: join9(AIUSAGE_DIR, "sync-repo")
9304
+ cacheDir: join10(AIUSAGE_DIR, "sync-repo")
8801
9305
  });
8802
9306
  }
8803
9307
  if (sync.backend === "s3") {
@@ -8892,8 +9396,8 @@ async function runSync(db, options) {
8892
9396
  }
8893
9397
 
8894
9398
  // src/commands/clean.ts
8895
- import { unlinkSync as unlinkSync3, existsSync as existsSync9 } from "fs";
8896
- import { join as join10 } from "path";
9399
+ import { unlinkSync as unlinkSync3, existsSync as existsSync10 } from "fs";
9400
+ import { join as join11 } from "path";
8897
9401
  function cleanOldData(db, days) {
8898
9402
  const cutoff = Date.now() - days * 864e5;
8899
9403
  const recordsResult = db.prepare("DELETE FROM records WHERE ts < ?").run(cutoff);
@@ -8914,9 +9418,9 @@ function cleanAll(db) {
8914
9418
  const syncedResult = db.prepare("DELETE FROM synced_records").run();
8915
9419
  const syncStateResult = db.prepare("DELETE FROM sync_record_state").run();
8916
9420
  const tombstonesResult = db.prepare("DELETE FROM sync_tombstones").run();
8917
- const watermarkPath = join10(AIUSAGE_DIR, "watermark.json");
9421
+ const watermarkPath = join11(AIUSAGE_DIR, "watermark.json");
8918
9422
  let watermarkRemoved = false;
8919
- if (existsSync9(watermarkPath)) {
9423
+ if (existsSync10(watermarkPath)) {
8920
9424
  unlinkSync3(watermarkPath);
8921
9425
  watermarkRemoved = true;
8922
9426
  }
@@ -8951,7 +9455,7 @@ function createBackend2(config) {
8951
9455
  return new GitSyncBackend({
8952
9456
  repo: config.sync.repo,
8953
9457
  token,
8954
- cacheDir: join10(AIUSAGE_DIR, "sync-repo")
9458
+ cacheDir: join11(AIUSAGE_DIR, "sync-repo")
8955
9459
  });
8956
9460
  }
8957
9461
  if (config.sync.backend === "s3") {
@@ -9284,7 +9788,7 @@ var RuntimeSettingsController = class {
9284
9788
 
9285
9789
  // src/commands/serve.ts
9286
9790
  var MAX_PORT_ATTEMPTS = 10;
9287
- var PORT_FILE = join11(AIUSAGE_DIR, ".serve-port");
9791
+ var PORT_FILE = join12(AIUSAGE_DIR, ".serve-port");
9288
9792
  var MIME_TYPES = {
9289
9793
  ".html": "text/html",
9290
9794
  ".js": "application/javascript",
@@ -9301,6 +9805,15 @@ function serve(options) {
9301
9805
  const config = loadConfig();
9302
9806
  const dbWriteQueue = new AsyncTaskQueue();
9303
9807
  const runDbWrite = (task) => dbWriteQueue.run(task);
9808
+ if (config?.priceOverrides && Object.keys(config.priceOverrides).length > 0) {
9809
+ const imported = importConfigPriceOverrides(options.db, config.priceOverrides);
9810
+ delete config.priceOverrides;
9811
+ saveConfig(config);
9812
+ loadPricingRuntime(options.db, config);
9813
+ if (imported.length > 0) {
9814
+ console.log(`[serve] migrated ${imported.length} config price override(s) into the registry: ${imported.join(", ")}`);
9815
+ }
9816
+ }
9304
9817
  if (config == null || config.exchangeRate == null) {
9305
9818
  const cacheAge = config?.exchangeRateCache ? Date.now() - config.exchangeRateCache.fetchedAt : Infinity;
9306
9819
  if (cacheAge >= CACHE_TTL_MS) {
@@ -9350,11 +9863,11 @@ function serve(options) {
9350
9863
  getDbWriteQueueStatus: () => dbWriteQueue.getStatus()
9351
9864
  });
9352
9865
  const webBuildDir = (() => {
9353
- const prodDir = join11(dirname4(fileURLToPath(import.meta.url)), "web");
9354
- if (existsSync10(prodDir)) return prodDir;
9355
- return join11(dirname4(fileURLToPath(import.meta.url)), "..", "..", "..", "web", "build");
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");
9356
9869
  })();
9357
- if (!existsSync10(webBuildDir)) {
9870
+ if (!existsSync11(webBuildDir)) {
9358
9871
  console.error("Web dashboard not found. Reinstall the package: npm install -g @juliantanx/aiusage");
9359
9872
  process.exit(1);
9360
9873
  }
@@ -9364,19 +9877,19 @@ function serve(options) {
9364
9877
  apiServer.emit("request", req, res);
9365
9878
  return;
9366
9879
  }
9367
- if (existsSync10(webBuildDir)) {
9368
- let filePath = join11(webBuildDir, url.pathname);
9880
+ if (existsSync11(webBuildDir)) {
9881
+ let filePath = join12(webBuildDir, url.pathname);
9369
9882
  try {
9370
- if (statSync3(filePath).isDirectory()) {
9371
- filePath = join11(filePath, "index.html");
9883
+ if (statSync4(filePath).isDirectory()) {
9884
+ filePath = join12(filePath, "index.html");
9372
9885
  }
9373
9886
  } catch {
9374
9887
  }
9375
- if (!existsSync10(filePath)) {
9376
- filePath = join11(webBuildDir, "index.html");
9888
+ if (!existsSync11(filePath)) {
9889
+ filePath = join12(webBuildDir, "index.html");
9377
9890
  }
9378
9891
  try {
9379
- const content = readFileSync9(filePath);
9892
+ const content = readFileSync10(filePath);
9380
9893
  const ext = extname2(filePath);
9381
9894
  const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
9382
9895
  res.writeHead(200, { "Content-Type": contentType });
@@ -9558,9 +10071,9 @@ Consent recorded for schema v1.`
9558
10071
  }
9559
10072
 
9560
10073
  // src/commands/status.ts
9561
- import { statSync as statSync4, existsSync as existsSync11 } from "fs";
10074
+ import { statSync as statSync5, existsSync as existsSync12 } from "fs";
9562
10075
  import { hostname as hostname3 } from "os";
9563
- import { join as join12 } from "path";
10076
+ import { join as join13 } from "path";
9564
10077
  function formatFileSize(bytes) {
9565
10078
  if (bytes < 1024) return `${bytes} B`;
9566
10079
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -9578,11 +10091,11 @@ function generateStatus(db) {
9578
10091
  const schemaVersion = db.prepare("SELECT version FROM schema_version ORDER BY version DESC LIMIT 1").get()?.version ?? 0;
9579
10092
  const tableCount = db.prepare("SELECT COUNT(*) as count FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'").get()?.count ?? 0;
9580
10093
  const viewCount = db.prepare("SELECT COUNT(*) as count FROM sqlite_master WHERE type = 'view'").get()?.count ?? 0;
9581
- const sizePath = join12(AIUSAGE_DIR, "cache.db");
10094
+ const sizePath = join13(AIUSAGE_DIR, "cache.db");
9582
10095
  let databaseSize = "0 B";
9583
- if (existsSync11(sizePath)) {
10096
+ if (existsSync12(sizePath)) {
9584
10097
  try {
9585
- const stat2 = statSync4(sizePath);
10098
+ const stat2 = statSync5(sizePath);
9586
10099
  databaseSize = formatFileSize(stat2.size);
9587
10100
  } catch {
9588
10101
  }
@@ -9671,11 +10184,11 @@ function recalcPricing(db) {
9671
10184
  ).all(lastId, BATCH_SIZE);
9672
10185
  if (records.length === 0) break;
9673
10186
  for (const record of records) {
9674
- if (record.cost_source === "log") {
10187
+ const model = record.tool === "qoder" ? normalizeQoderModel(record.model) : record.model;
10188
+ if (record.cost_source === "log" && record.cost > 0 && !hasUserPrice(db, model)) {
9675
10189
  skippedCount++;
9676
10190
  continue;
9677
10191
  }
9678
- const model = record.tool === "qoder" ? normalizeQoderModel(record.model) : record.model;
9679
10192
  const provider = model !== record.model ? inferProvider(model) : record.provider;
9680
10193
  const price = resolvePriceFromRegistry(db, model);
9681
10194
  const newCost = price ? calculateCostForPrice(price, {
@@ -9803,10 +10316,10 @@ var SyncProgressReporter = class {
9803
10316
  };
9804
10317
 
9805
10318
  // src/commands/widget.ts
9806
- import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
10319
+ import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
9807
10320
  import { spawn, execSync as execSync2 } from "child_process";
9808
- import { join as join13 } from "path";
9809
- var WIDGET_PID_PATH = join13(AIUSAGE_DIR, "widget.pid");
10321
+ import { join as join14 } from "path";
10322
+ var WIDGET_PID_PATH = join14(AIUSAGE_DIR, "widget.pid");
9810
10323
  async function launchWidget() {
9811
10324
  if (isWidgetRunning()) {
9812
10325
  console.log("aiusage widget is already running in the system tray.");
@@ -9828,10 +10341,10 @@ async function launchWidget() {
9828
10341
  console.log("aiusage widget started.");
9829
10342
  }
9830
10343
  function isWidgetRunning() {
9831
- if (!existsSync12(WIDGET_PID_PATH)) return false;
10344
+ if (!existsSync13(WIDGET_PID_PATH)) return false;
9832
10345
  let pid;
9833
10346
  try {
9834
- pid = parseInt(readFileSync10(WIDGET_PID_PATH, "utf-8").trim(), 10);
10347
+ pid = parseInt(readFileSync11(WIDGET_PID_PATH, "utf-8").trim(), 10);
9835
10348
  } catch {
9836
10349
  return false;
9837
10350
  }
@@ -9875,10 +10388,10 @@ function getDeviceName2() {
9875
10388
  }
9876
10389
  function getCliVersion2() {
9877
10390
  try {
9878
- const { readFileSync: readFileSync12 } = __require("fs");
9879
- const { join: join16 } = __require("path");
9880
- const pkgPath = join16(__dirname, "../../package.json");
9881
- const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
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"));
9882
10395
  return pkg.version || "0.0.0";
9883
10396
  } catch {
9884
10397
  return "0.0.0";
@@ -10121,17 +10634,17 @@ async function runLeaderboardView(options = {}) {
10121
10634
 
10122
10635
  // src/commands/menu.ts
10123
10636
  import { createInterface } from "readline";
10124
- import { spawn as spawn2, spawnSync, execSync as execSync4 } from "child_process";
10125
- import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
10126
- import { join as join14 } from "path";
10637
+ 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";
10127
10640
  var DEFAULT_PORT = 3847;
10128
- var PORT_FILE2 = join14(AIUSAGE_DIR, ".serve-port");
10641
+ var PORT_FILE2 = join15(AIUSAGE_DIR, ".serve-port");
10129
10642
  function clearScreen() {
10130
10643
  process.stdout.write("\x1Bc");
10131
10644
  }
10132
10645
  function getPort() {
10133
- if (existsSync13(PORT_FILE2)) {
10134
- const raw = readFileSync11(PORT_FILE2, "utf-8").trim();
10646
+ if (existsSync14(PORT_FILE2)) {
10647
+ const raw = readFileSync12(PORT_FILE2, "utf-8").trim();
10135
10648
  const n = parseInt(raw, 10);
10136
10649
  if (!isNaN(n) && n > 0) return n;
10137
10650
  }
@@ -10178,7 +10691,7 @@ function openBrowser2(url) {
10178
10691
  }
10179
10692
  function runCommand(args) {
10180
10693
  try {
10181
- spawnSync("aiusage", args, { stdio: "inherit" });
10694
+ spawnSync2("aiusage", args, { stdio: "inherit" });
10182
10695
  } catch {
10183
10696
  }
10184
10697
  }
@@ -10620,10 +11133,10 @@ ${dashboardStatusLine()}
10620
11133
  }
10621
11134
 
10622
11135
  // src/cli.ts
10623
- import { join as join15 } from "path";
10624
- var DB_PATH2 = join15(AIUSAGE_DIR, "cache.db");
11136
+ import { join as join16 } from "path";
11137
+ var DB_PATH2 = join16(AIUSAGE_DIR, "cache.db");
10625
11138
  var program = new Command();
10626
- program.name("aiusage").version(true ? "1.5.6" : "dev").description("CLI tool for AI usage statistics");
11139
+ program.name("aiusage").version(true ? "1.5.7" : "dev").description("CLI tool for AI usage statistics");
10627
11140
  program.action(() => {
10628
11141
  const unknownCommand = program.args.find((arg) => typeof arg === "string");
10629
11142
  if (unknownCommand) {
@@ -10910,7 +11423,7 @@ function hasWidget() {
10910
11423
  }
10911
11424
  }
10912
11425
  function buildPm2Config(includeWidget) {
10913
- const wrapperPath = join15(AIUSAGE_DIR, "pm2-server.cjs");
11426
+ const wrapperPath = join16(AIUSAGE_DIR, "pm2-server.cjs");
10914
11427
  writeFileSync7(wrapperPath, [
10915
11428
  `// Auto-generated by aiusage pm2-start \u2014 do not edit`,
10916
11429
  `const { execSync, spawn } = require('child_process')`,
@@ -10957,7 +11470,7 @@ ${apps.join(",\n")}
10957
11470
  program.command("pm2-setup").description("Generate ecosystem.config.cjs for PM2 background services").option("--server-only", "Only include server, skip widget").action((options) => {
10958
11471
  const includeWidget = options.serverOnly ? false : hasWidget();
10959
11472
  const config = buildPm2Config(includeWidget);
10960
- const configPath = join15(AIUSAGE_DIR, "ecosystem.config.cjs");
11473
+ const configPath = join16(AIUSAGE_DIR, "ecosystem.config.cjs");
10961
11474
  writeFileSync7(configPath, config, "utf-8");
10962
11475
  console.log(`Config created: ${configPath}`);
10963
11476
  if (!includeWidget) {
@@ -10967,7 +11480,7 @@ program.command("pm2-setup").description("Generate ecosystem.config.cjs for PM2
10967
11480
  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) => {
10968
11481
  const includeWidget = options.serverOnly ? false : hasWidget();
10969
11482
  const config = buildPm2Config(includeWidget);
10970
- const configPath = join15(AIUSAGE_DIR, "ecosystem.config.cjs");
11483
+ const configPath = join16(AIUSAGE_DIR, "ecosystem.config.cjs");
10971
11484
  writeFileSync7(configPath, config, "utf-8");
10972
11485
  console.log(`Config: ${configPath}`);
10973
11486
  try {