@juliantanx/aiusage 1.5.6 → 1.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +940 -127
- package/dist/web/_app/immutable/chunks/{ToolSelector.0iI1cXVj.js → ToolSelector.CO-QGau6.js} +1 -1
- package/dist/web/_app/immutable/chunks/{entry.PMcg7HQL.js → entry.DJZO-wBk.js} +1 -1
- package/dist/web/_app/immutable/chunks/{stores.BSI_cVy6.js → stores.C6g63Ng7.js} +1 -1
- package/dist/web/_app/immutable/chunks/{stores.DE60qEGb.js → stores.CDuvMHgD.js} +1 -1
- package/dist/web/_app/immutable/entry/{app.CSiP_kAD.js → app.WQCJU-_e.js} +2 -2
- package/dist/web/_app/immutable/entry/start.COue4Eiw.js +1 -0
- package/dist/web/_app/immutable/nodes/{0.dH2tW-7d.js → 0.Fq-nYlaS.js} +1 -1
- package/dist/web/_app/immutable/nodes/{1.B8yHhyzy.js → 1.DrSfUVzz.js} +1 -1
- package/dist/web/_app/immutable/nodes/{10.CnTuhaua.js → 10.CP1F4_ac.js} +1 -1
- package/dist/web/_app/immutable/nodes/{11.5p54X4Ps.js → 11.YqYsFEzd.js} +1 -1
- package/dist/web/_app/immutable/nodes/{12.BCT8Dv1G.js → 12.RfrZJ-XB.js} +1 -1
- package/dist/web/_app/immutable/nodes/{14.u96Ucsue.js → 14.Cbg9FBSP.js} +1 -1
- package/dist/web/_app/immutable/nodes/{15.CMhy_NGl.js → 15.CQoUgLeT.js} +1 -1
- package/dist/web/_app/immutable/nodes/{2.CJH1Fs0K.js → 2.DLTFcQ9L.js} +1 -1
- package/dist/web/_app/immutable/nodes/{3.Chtgl9hh.js → 3.jLX2a-Ic.js} +1 -1
- package/dist/web/_app/immutable/nodes/{5.DtYG5yJy.js → 5.CVQN-3_l.js} +1 -1
- package/dist/web/_app/immutable/nodes/{6.B-ud4zCO.js → 6.C-WRTBwg.js} +1 -1
- package/dist/web/_app/immutable/nodes/{7.7Fm6yzqA.js → 7.DFkuOTpF.js} +1 -1
- package/dist/web/_app/immutable/nodes/{8.Chp3dlzK.js → 8.BNiBE_e2.js} +1 -1
- package/dist/web/_app/version.json +1 -1
- package/dist/web/index.html +6 -6
- package/package.json +3 -3
- 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
|
|
17
|
-
import { join as
|
|
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
|
|
@@ -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 (
|
|
457
|
-
cost =
|
|
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 = {
|
|
@@ -873,7 +872,39 @@ function sanitizeModel(value, fallback) {
|
|
|
873
872
|
const model = lastPathSegment(value);
|
|
874
873
|
return model || fallback;
|
|
875
874
|
}
|
|
876
|
-
function
|
|
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
|
+
}
|
|
877
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;
|
|
878
909
|
if (!usage || typeof usage !== "object") return null;
|
|
879
910
|
const details = usage.prompt_tokens_details ?? usage.input_tokens_details ?? {};
|
|
@@ -943,7 +974,7 @@ var GenericJsonlParser = class {
|
|
|
943
974
|
}
|
|
944
975
|
const normalized = parsed?.type === "context.append_loop_event" && parsed.event ? { ...parsed.event, time: parsed.time } : parsed;
|
|
945
976
|
if (!shouldAccept(this.tool, normalized)) return null;
|
|
946
|
-
const usage = usageFromAny(normalized);
|
|
977
|
+
const usage = usageFromAny(normalized, this.tool);
|
|
947
978
|
if (!usage) return null;
|
|
948
979
|
const total = usage.inputTokens + usage.outputTokens + usage.cacheReadTokens + usage.cacheWriteTokens + usage.thinkingTokens;
|
|
949
980
|
if (total === 0) return null;
|
|
@@ -1436,7 +1467,12 @@ function defaultCursorDbPath() {
|
|
|
1436
1467
|
return join3(xdgConfigHome, "Cursor", "User", "globalStorage", "state.vscdb");
|
|
1437
1468
|
}
|
|
1438
1469
|
function defaultKiloDbPath() {
|
|
1439
|
-
|
|
1470
|
+
const home = homedir2();
|
|
1471
|
+
if (platform() === "win32") {
|
|
1472
|
+
const appData = process.env.APPDATA ?? join3(home, "AppData", "Roaming");
|
|
1473
|
+
return join3(appData, "kilo", "kilo.db");
|
|
1474
|
+
}
|
|
1475
|
+
return join3(xdgDataDir(home, "kilo"), "kilo.db");
|
|
1440
1476
|
}
|
|
1441
1477
|
function defaultGooseDbPath() {
|
|
1442
1478
|
const home = homedir2();
|
|
@@ -1599,29 +1635,101 @@ function probeCodeBuddy(ctx) {
|
|
|
1599
1635
|
const dir = join3(ctx.env.CODEBUDDY_HOME ?? join3(ctx.home, ".codebuddy"), "projects");
|
|
1600
1636
|
return existsSync3(dir) ? dir : null;
|
|
1601
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
|
+
}
|
|
1602
1687
|
function probeKiro(ctx) {
|
|
1603
1688
|
const override = envOverride("kiro", ctx.env);
|
|
1604
1689
|
if (override) return override;
|
|
1605
1690
|
const legacy = ctx.legacySources?.["kiro"];
|
|
1606
1691
|
if (legacy) return legacy;
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
);
|
|
1692
|
+
return probeKiroDefaultPath(ctx);
|
|
1693
|
+
}
|
|
1694
|
+
function probeKiroOverridePath(ctx) {
|
|
1695
|
+
const override = envOverride("kiro", ctx.env);
|
|
1696
|
+
if (override) return override;
|
|
1697
|
+
const legacy = ctx.legacySources?.["kiro"];
|
|
1698
|
+
if (legacy) return legacy;
|
|
1699
|
+
return null;
|
|
1700
|
+
}
|
|
1701
|
+
function probeKiroDefaultPath(ctx) {
|
|
1702
|
+
const devDataBase = kiroDevDataDir(ctx);
|
|
1703
|
+
const ideDb = join3(devDataBase, "devdata.sqlite");
|
|
1704
|
+
const tokensJsonl = join3(devDataBase, "tokens_generated.jsonl");
|
|
1618
1705
|
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;
|
|
1706
|
+
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");
|
|
1707
|
+
if (existsSync3(ideDb) && statSync(ideDb).size > 0) return ideDb;
|
|
1621
1708
|
if (existsSync3(appSupport)) return appSupport;
|
|
1622
1709
|
if (existsSync3(cliSessions)) return cliSessions;
|
|
1710
|
+
if (existsSync3(tokensJsonl)) return devDataBase;
|
|
1623
1711
|
return null;
|
|
1624
1712
|
}
|
|
1713
|
+
function kiroDevDataDir(ctx) {
|
|
1714
|
+
if (platform() === "darwin") {
|
|
1715
|
+
return join3(ctx.home, "Library", "Application Support", "Kiro", "User", "globalStorage", "kiro.kiroagent", "dev_data");
|
|
1716
|
+
}
|
|
1717
|
+
if (platform() === "win32") {
|
|
1718
|
+
const appData = ctx.env.APPDATA ?? join3(ctx.home, "AppData", "Roaming");
|
|
1719
|
+
return join3(appData, "Kiro", "User", "globalStorage", "kiro.kiroagent", "dev_data");
|
|
1720
|
+
}
|
|
1721
|
+
return join3(ctx.env.XDG_DATA_HOME ?? join3(ctx.home, ".local", "share"), "Kiro", "User", "globalStorage", "kiro.kiroagent", "dev_data");
|
|
1722
|
+
}
|
|
1723
|
+
function kiroWorkspaceSessionsDir(ctx) {
|
|
1724
|
+
if (platform() === "darwin") {
|
|
1725
|
+
return join3(ctx.home, "Library", "Application Support", "Kiro", "User", "globalStorage", "kiro.kiroagent", "workspace-sessions");
|
|
1726
|
+
}
|
|
1727
|
+
if (platform() === "win32") {
|
|
1728
|
+
const appData = ctx.env.APPDATA ?? join3(ctx.home, "AppData", "Roaming");
|
|
1729
|
+
return join3(appData, "Kiro", "User", "globalStorage", "kiro.kiroagent", "workspace-sessions");
|
|
1730
|
+
}
|
|
1731
|
+
return join3(ctx.env.XDG_DATA_HOME ?? join3(ctx.home, ".local", "share"), "Kiro", "User", "globalStorage", "kiro.kiroagent", "workspace-sessions");
|
|
1732
|
+
}
|
|
1625
1733
|
function probeGrok(ctx) {
|
|
1626
1734
|
const override = envOverride("grok", ctx.env);
|
|
1627
1735
|
if (override) return override;
|
|
@@ -1725,6 +1833,27 @@ function probeDroid(ctx) {
|
|
|
1725
1833
|
const dir = join3(ctx.home, ".droid", "sessions");
|
|
1726
1834
|
return existsSync3(dir) ? dir : null;
|
|
1727
1835
|
}
|
|
1836
|
+
function probeTrae(ctx) {
|
|
1837
|
+
const override = envOverride("trae", ctx.env);
|
|
1838
|
+
if (override) return override;
|
|
1839
|
+
const legacy = ctx.legacySources?.["trae"];
|
|
1840
|
+
if (legacy) return legacy;
|
|
1841
|
+
const appNames = platform() === "darwin" ? ["Trae CN", "TRAE SOLO CN", "Trae"] : platform() === "win32" ? ["Trae CN", "TRAE SOLO CN", "Trae"] : ["Trae CN", "TRAE SOLO CN", "Trae"];
|
|
1842
|
+
for (const appName of appNames) {
|
|
1843
|
+
let dbPath;
|
|
1844
|
+
if (platform() === "darwin") {
|
|
1845
|
+
dbPath = join3(ctx.home, "Library", "Application Support", appName, "User", "globalStorage", "state.vscdb");
|
|
1846
|
+
} else if (platform() === "win32") {
|
|
1847
|
+
const appData = ctx.env.APPDATA ?? join3(ctx.home, "AppData", "Roaming");
|
|
1848
|
+
dbPath = join3(appData, appName, "User", "globalStorage", "state.vscdb");
|
|
1849
|
+
} else {
|
|
1850
|
+
const config = ctx.env.XDG_CONFIG_HOME ?? join3(ctx.home, ".config");
|
|
1851
|
+
dbPath = join3(config, appName, "User", "globalStorage", "state.vscdb");
|
|
1852
|
+
}
|
|
1853
|
+
if (existsSync3(dbPath)) return dbPath;
|
|
1854
|
+
}
|
|
1855
|
+
return null;
|
|
1856
|
+
}
|
|
1728
1857
|
function defaultQoderSessionDirs(home) {
|
|
1729
1858
|
const windowsHomes = [
|
|
1730
1859
|
process.env.USERPROFILE,
|
|
@@ -1762,6 +1891,7 @@ var TOOL_REGISTRY = [
|
|
|
1762
1891
|
{ tool: "gemini", sourceKey: "gemini", label: "Gemini CLI", probe: probeGemini },
|
|
1763
1892
|
{ tool: "kimi", sourceKey: "kimi", label: "Kimi Code", probe: probeKimi },
|
|
1764
1893
|
{ tool: "codebuddy", sourceKey: "codebuddy", label: "CodeBuddy", probe: probeCodeBuddy },
|
|
1894
|
+
{ tool: "codebuddy", sourceKey: "codebuddy-ide", label: "CodeBuddy (IDE)", probe: probeCodeBuddyIde },
|
|
1765
1895
|
{ tool: "kiro", sourceKey: "kiro", label: "Kiro", probe: probeKiro },
|
|
1766
1896
|
{ tool: "grok", sourceKey: "grok", label: "Grok Build", probe: probeGrok },
|
|
1767
1897
|
{ tool: "antigravity", sourceKey: "antigravity", label: "Antigravity", probe: probeAntigravity },
|
|
@@ -1772,7 +1902,8 @@ var TOOL_REGISTRY = [
|
|
|
1772
1902
|
{ tool: "omp", sourceKey: "omp", label: "oh-my-pi", probe: probeOmp },
|
|
1773
1903
|
{ tool: "pi", sourceKey: "pi", label: "pi", probe: probePi },
|
|
1774
1904
|
{ tool: "craft", sourceKey: "craft", label: "Craft", probe: probeCraft },
|
|
1775
|
-
{ tool: "droid", sourceKey: "droid", label: "Droid", probe: probeDroid }
|
|
1905
|
+
{ tool: "droid", sourceKey: "droid", label: "Droid", probe: probeDroid },
|
|
1906
|
+
{ tool: "trae", sourceKey: "trae", label: "Trae", probe: probeTrae }
|
|
1776
1907
|
];
|
|
1777
1908
|
function discoverTools(env = process.env) {
|
|
1778
1909
|
const home = homedir2();
|
|
@@ -1790,7 +1921,9 @@ function discoverTools(env = process.env) {
|
|
|
1790
1921
|
try {
|
|
1791
1922
|
const stat2 = statSync(detectedPath);
|
|
1792
1923
|
if (stat2.isDirectory()) {
|
|
1793
|
-
if (entry.sourceKey === "
|
|
1924
|
+
if (entry.sourceKey === "codebuddy-ide") {
|
|
1925
|
+
fileCount += countCodeBuddyIdeConversations(detectedPath);
|
|
1926
|
+
} else if (entry.sourceKey === "roocode" || entry.sourceKey === "kilocode") {
|
|
1794
1927
|
fileCount += findJsonFiles(detectedPath).filter((p) => basename(p) === "ui_messages.json").length;
|
|
1795
1928
|
} else if (entry.sourceKey === "kelivo") {
|
|
1796
1929
|
fileCount += findJsonFiles(detectedPath).filter((p) => basename(p) === "chats.json").length + findZipFiles(detectedPath).length;
|
|
@@ -1889,7 +2022,9 @@ function discoverLogFiles(env = process.env) {
|
|
|
1889
2022
|
{ tool: "gemini", path: probeGemini(ctx) },
|
|
1890
2023
|
{ tool: "kimi", path: probeKimi(ctx), filter: (p) => basename(p) === "wire.jsonl" },
|
|
1891
2024
|
{ tool: "codebuddy", path: probeCodeBuddy(ctx) },
|
|
1892
|
-
{ tool: "kiro", path:
|
|
2025
|
+
{ tool: "kiro", path: kiroDevDataDir(ctx), filter: (p) => extname(p) === ".jsonl" || extname(p) === ".json" },
|
|
2026
|
+
{ tool: "kiro", path: probeKiroOverridePath(ctx), filter: (p) => extname(p) === ".jsonl" || extname(p) === ".json" },
|
|
2027
|
+
{ tool: "kiro", path: kiroWorkspaceSessionsDir(ctx), filter: (p) => extname(p) === ".json" && basename(p) !== "sessions.json" },
|
|
1893
2028
|
{ tool: "grok", path: probeGrok(ctx), filter: (p) => extname(p) === ".jsonl" },
|
|
1894
2029
|
{ tool: "antigravity", path: probeAntigravity(ctx) },
|
|
1895
2030
|
{ tool: "omp", path: probeOmp(ctx) },
|
|
@@ -1918,6 +2053,49 @@ function discoverLogFiles(env = process.env) {
|
|
|
1918
2053
|
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
2054
|
if (paths.length > 0) results.push({ tool: "kelivo", paths: unique(paths) });
|
|
1920
2055
|
}
|
|
2056
|
+
const cursorProjectDirs = cursorProjectRoots(ctx);
|
|
2057
|
+
for (const cursorHome of cursorProjectDirs) {
|
|
2058
|
+
if (!existsSync3(cursorHome)) continue;
|
|
2059
|
+
const transcriptPaths = findCursorTranscriptFiles(cursorHome);
|
|
2060
|
+
if (transcriptPaths.length > 0) {
|
|
2061
|
+
const existing = results.find((r) => r.tool === "cursor");
|
|
2062
|
+
if (existing) {
|
|
2063
|
+
existing.paths = unique([...existing.paths, ...transcriptPaths]);
|
|
2064
|
+
} else {
|
|
2065
|
+
results.push({ tool: "cursor", paths: transcriptPaths });
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
return results;
|
|
2070
|
+
}
|
|
2071
|
+
function cursorProjectRoots(ctx) {
|
|
2072
|
+
const roots = [join3(ctx.home, ".cursor", "projects")];
|
|
2073
|
+
if (platform() === "win32") {
|
|
2074
|
+
const userProfile = ctx.env.USERPROFILE ?? ctx.home;
|
|
2075
|
+
roots.push(join3(userProfile, ".cursor", "projects"));
|
|
2076
|
+
}
|
|
2077
|
+
return unique(roots);
|
|
2078
|
+
}
|
|
2079
|
+
function findCursorTranscriptFiles(dir) {
|
|
2080
|
+
const results = [];
|
|
2081
|
+
try {
|
|
2082
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
2083
|
+
for (const entry of entries) {
|
|
2084
|
+
if (!entry.isDirectory()) continue;
|
|
2085
|
+
const agentDir = join3(dir, entry.name, "agent-transcripts");
|
|
2086
|
+
if (!existsSync3(agentDir)) continue;
|
|
2087
|
+
try {
|
|
2088
|
+
const sessions = readdirSync(agentDir, { withFileTypes: true });
|
|
2089
|
+
for (const session of sessions) {
|
|
2090
|
+
if (!session.isDirectory()) continue;
|
|
2091
|
+
const jsonlPath = join3(agentDir, session.name, `${session.name}.jsonl`);
|
|
2092
|
+
if (existsSync3(jsonlPath)) results.push(jsonlPath);
|
|
2093
|
+
}
|
|
2094
|
+
} catch {
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
} catch {
|
|
2098
|
+
}
|
|
1921
2099
|
return results;
|
|
1922
2100
|
}
|
|
1923
2101
|
function findJsonFiles(dir) {
|
|
@@ -3082,6 +3260,17 @@ function migrateV11(db) {
|
|
|
3082
3260
|
`);
|
|
3083
3261
|
}
|
|
3084
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
|
+
|
|
3085
3274
|
// src/db/migrations/index.ts
|
|
3086
3275
|
var MIGRATIONS = [
|
|
3087
3276
|
{ version: 1, migrate: migrateV1 },
|
|
@@ -3094,7 +3283,8 @@ var MIGRATIONS = [
|
|
|
3094
3283
|
{ version: 8, migrate: migrateV8 },
|
|
3095
3284
|
{ version: 9, migrate: migrateV9 },
|
|
3096
3285
|
{ version: 10, migrate: migrateV10 },
|
|
3097
|
-
{ version: 11, migrate: migrateV11 }
|
|
3286
|
+
{ version: 11, migrate: migrateV11 },
|
|
3287
|
+
{ version: 12, migrate: migrateV12 }
|
|
3098
3288
|
];
|
|
3099
3289
|
function runMigrations(db) {
|
|
3100
3290
|
createSchemaVersionTable(db);
|
|
@@ -3201,6 +3391,21 @@ function loadPricingRuntime(db, config) {
|
|
|
3201
3391
|
for (const [model, entry] of Object.entries(configOverrides)) overrides[model] = entry;
|
|
3202
3392
|
setRuntimePriceTable(builtin, overrides);
|
|
3203
3393
|
}
|
|
3394
|
+
function importConfigPriceOverrides(db, overrides) {
|
|
3395
|
+
const imported = [];
|
|
3396
|
+
for (const [modelKey, entry] of Object.entries(overrides)) {
|
|
3397
|
+
if (!modelKey.trim()) continue;
|
|
3398
|
+
const existingUserPrice = db.prepare(`
|
|
3399
|
+
SELECT 1 FROM model_prices
|
|
3400
|
+
WHERE model_key = ? AND origin = 'user' AND status = 'active'
|
|
3401
|
+
LIMIT 1
|
|
3402
|
+
`).get(modelKey);
|
|
3403
|
+
if (existingUserPrice) continue;
|
|
3404
|
+
setUserPrice(db, modelKey, entry);
|
|
3405
|
+
imported.push(modelKey);
|
|
3406
|
+
}
|
|
3407
|
+
return imported;
|
|
3408
|
+
}
|
|
3204
3409
|
function setUserPrice(db, modelKey, entry) {
|
|
3205
3410
|
const now = Date.now();
|
|
3206
3411
|
const baseline = db.prepare(`
|
|
@@ -3455,6 +3660,14 @@ function priceTableFromDb(db) {
|
|
|
3455
3660
|
for (const row of rows) table[row.model_key] = rowToPrice(row);
|
|
3456
3661
|
return table;
|
|
3457
3662
|
}
|
|
3663
|
+
function hasUserPrice(db, model) {
|
|
3664
|
+
const row = db.prepare(`
|
|
3665
|
+
SELECT 1 FROM model_prices
|
|
3666
|
+
WHERE model_key = ? AND origin = 'user' AND status = 'active'
|
|
3667
|
+
LIMIT 1
|
|
3668
|
+
`).get(model);
|
|
3669
|
+
return Boolean(row);
|
|
3670
|
+
}
|
|
3458
3671
|
function resolvePriceFromRegistry(db, model) {
|
|
3459
3672
|
const directUserPrice = db.prepare(`
|
|
3460
3673
|
SELECT input, output, cache_read, cache_write, currency
|
|
@@ -3914,10 +4127,10 @@ function getClientPlatform() {
|
|
|
3914
4127
|
}
|
|
3915
4128
|
function getCliVersion() {
|
|
3916
4129
|
try {
|
|
3917
|
-
const { readFileSync:
|
|
3918
|
-
const { join:
|
|
3919
|
-
const pkgPath =
|
|
3920
|
-
const pkg = JSON.parse(
|
|
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"));
|
|
3921
4134
|
return pkg.version || "0.0.0";
|
|
3922
4135
|
} catch {
|
|
3923
4136
|
return "0.0.0";
|
|
@@ -4529,12 +4742,12 @@ async function recalcCosts(db, onProgress) {
|
|
|
4529
4742
|
const tx = db.transaction((batch) => {
|
|
4530
4743
|
for (const r of batch) {
|
|
4531
4744
|
processed++;
|
|
4532
|
-
|
|
4745
|
+
const rawModel = r.tool === "qoder" ? normalizeQoderModel(r.model) : r.model;
|
|
4746
|
+
const model = rawModel === "unknown" ? r.tool === "qoder" ? "qoder-auto" : r.model : rawModel;
|
|
4747
|
+
if (r.cost_source === "log" && r.cost > 0 && !hasUserPrice(db, model)) {
|
|
4533
4748
|
skipped++;
|
|
4534
4749
|
continue;
|
|
4535
4750
|
}
|
|
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
4751
|
const provider = model !== r.model ? inferProvider(model) : r.provider;
|
|
4539
4752
|
const price = resolveCachedPrice(model);
|
|
4540
4753
|
const cost = price ? calculateCostForPrice(price, {
|
|
@@ -6128,8 +6341,8 @@ function createApiServer(db, options) {
|
|
|
6128
6341
|
|
|
6129
6342
|
// src/commands/parse.ts
|
|
6130
6343
|
import Database2 from "better-sqlite3";
|
|
6131
|
-
import { readFileSync as
|
|
6132
|
-
import { join as
|
|
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";
|
|
6133
6346
|
import { hostname as hostname2 } from "os";
|
|
6134
6347
|
|
|
6135
6348
|
// src/watermark.ts
|
|
@@ -6159,7 +6372,8 @@ function defaultFileData() {
|
|
|
6159
6372
|
"pi": {},
|
|
6160
6373
|
"craft": {},
|
|
6161
6374
|
"droid": {},
|
|
6162
|
-
"zcode": {}
|
|
6375
|
+
"zcode": {},
|
|
6376
|
+
"trae": {}
|
|
6163
6377
|
};
|
|
6164
6378
|
}
|
|
6165
6379
|
var WatermarkManager = class {
|
|
@@ -6179,7 +6393,7 @@ var WatermarkManager = class {
|
|
|
6179
6393
|
if (parsed && typeof parsed === "object" && !("files" in parsed)) {
|
|
6180
6394
|
return { files: { ...defaultFileData(), ...parsed } };
|
|
6181
6395
|
}
|
|
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 };
|
|
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 };
|
|
6183
6397
|
} catch {
|
|
6184
6398
|
return { files: defaultFileData() };
|
|
6185
6399
|
}
|
|
@@ -6260,6 +6474,18 @@ var WatermarkManager = class {
|
|
|
6260
6474
|
setZcodeToolCursor(cursor) {
|
|
6261
6475
|
this.data.zcodeTools = cursor;
|
|
6262
6476
|
}
|
|
6477
|
+
getTraeLastImported() {
|
|
6478
|
+
return this.data.trae ?? 0;
|
|
6479
|
+
}
|
|
6480
|
+
setTraeLastImported(ts3) {
|
|
6481
|
+
this.data.trae = ts3;
|
|
6482
|
+
}
|
|
6483
|
+
getCodeBuddyIdeCursor() {
|
|
6484
|
+
return this.data.codebuddyIde ?? 0;
|
|
6485
|
+
}
|
|
6486
|
+
setCodeBuddyIdeCursor(ts3) {
|
|
6487
|
+
this.data.codebuddyIde = ts3;
|
|
6488
|
+
}
|
|
6263
6489
|
};
|
|
6264
6490
|
|
|
6265
6491
|
// src/commands/parse-opencode.ts
|
|
@@ -6554,6 +6780,42 @@ function runParseQoder(db, options) {
|
|
|
6554
6780
|
}
|
|
6555
6781
|
|
|
6556
6782
|
// src/commands/parse-cursor.ts
|
|
6783
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
6784
|
+
function runParseCursorTranscript(options) {
|
|
6785
|
+
const charsPerToken = 3;
|
|
6786
|
+
try {
|
|
6787
|
+
const content = readFileSync8(options.jsonlPath, "utf-8");
|
|
6788
|
+
const lines = content.split("\n");
|
|
6789
|
+
let userChars = 0;
|
|
6790
|
+
let assistChars = 0;
|
|
6791
|
+
for (const line of lines) {
|
|
6792
|
+
if (!line.trim()) continue;
|
|
6793
|
+
let data;
|
|
6794
|
+
try {
|
|
6795
|
+
data = JSON.parse(line);
|
|
6796
|
+
} catch {
|
|
6797
|
+
continue;
|
|
6798
|
+
}
|
|
6799
|
+
const role = data?.role ?? "";
|
|
6800
|
+
const blocks = data?.message?.content ?? [];
|
|
6801
|
+
if (!Array.isArray(blocks)) continue;
|
|
6802
|
+
for (const block of blocks) {
|
|
6803
|
+
if (typeof block !== "object" || block === null || block.type !== "text") continue;
|
|
6804
|
+
const text = block.text ?? "";
|
|
6805
|
+
const clean = text.replace(/\[REDACTED\]/g, "").trim();
|
|
6806
|
+
if (!clean) continue;
|
|
6807
|
+
if (role === "user") userChars += clean.length;
|
|
6808
|
+
else if (role === "assistant") assistChars += clean.length;
|
|
6809
|
+
}
|
|
6810
|
+
}
|
|
6811
|
+
return {
|
|
6812
|
+
inputTextChars: Math.floor(userChars / charsPerToken),
|
|
6813
|
+
outputTextChars: Math.floor(assistChars / charsPerToken)
|
|
6814
|
+
};
|
|
6815
|
+
} catch {
|
|
6816
|
+
return { inputTextChars: 0, outputTextChars: 0 };
|
|
6817
|
+
}
|
|
6818
|
+
}
|
|
6557
6819
|
function runParseCursor(db, options) {
|
|
6558
6820
|
const { dbPath, device, deviceInstanceId, platform: platform5, now, cursor } = options;
|
|
6559
6821
|
const records = [];
|
|
@@ -7137,6 +7399,306 @@ function runParseZcode(db, options) {
|
|
|
7137
7399
|
};
|
|
7138
7400
|
}
|
|
7139
7401
|
|
|
7402
|
+
// src/commands/parse-trae.ts
|
|
7403
|
+
import { existsSync as existsSync8, readdirSync as readdirSync2 } from "fs";
|
|
7404
|
+
import { join as join7, basename as basename3 } from "path";
|
|
7405
|
+
import { spawnSync } from "child_process";
|
|
7406
|
+
function deriveDataDir(dbPath) {
|
|
7407
|
+
const normalized = dbPath.replace(/\\/g, "/");
|
|
7408
|
+
const appDir = normalized.replace(/\/User\/globalStorage\/state\.vscdb$/, "");
|
|
7409
|
+
const dataDir = join7(appDir, "ModularData", "ai-agent", "snapshot");
|
|
7410
|
+
return existsSync8(dataDir) ? dataDir : null;
|
|
7411
|
+
}
|
|
7412
|
+
function parseSessionTags(repoPath) {
|
|
7413
|
+
try {
|
|
7414
|
+
const sessionId = basename3(basename3(repoPath) === "v2" ? join7(repoPath, "..") : repoPath);
|
|
7415
|
+
if (!/^[0-9a-f]{24,}$/.test(sessionId)) return null;
|
|
7416
|
+
const tagResult = spawnSync("git", ["tag", "-l"], { cwd: repoPath, encoding: "utf-8", timeout: 5e3 });
|
|
7417
|
+
if (tagResult.status !== 0 || !tagResult.stdout?.trim()) return null;
|
|
7418
|
+
const tags = tagResult.stdout;
|
|
7419
|
+
const lines = tags.trim().split("\n");
|
|
7420
|
+
let chatTurns = 0;
|
|
7421
|
+
let toolCalls = 0;
|
|
7422
|
+
let startTs = 0;
|
|
7423
|
+
for (const tag of lines) {
|
|
7424
|
+
const t = tag.trim();
|
|
7425
|
+
if (!t) continue;
|
|
7426
|
+
let tagTs = 0;
|
|
7427
|
+
try {
|
|
7428
|
+
const logResult = spawnSync("git", ["log", "-1", "--format=%at", t], {
|
|
7429
|
+
cwd: repoPath,
|
|
7430
|
+
encoding: "utf-8",
|
|
7431
|
+
timeout: 3e3
|
|
7432
|
+
});
|
|
7433
|
+
if (logResult.status !== 0 || !logResult.stdout?.trim()) continue;
|
|
7434
|
+
const tsStr = logResult.stdout.trim();
|
|
7435
|
+
tagTs = parseInt(tsStr, 10) * 1e3;
|
|
7436
|
+
} catch {
|
|
7437
|
+
continue;
|
|
7438
|
+
}
|
|
7439
|
+
if (t.startsWith("chain-start-")) {
|
|
7440
|
+
startTs = tagTs;
|
|
7441
|
+
} else if (t.startsWith("before-chat-turn-")) {
|
|
7442
|
+
chatTurns++;
|
|
7443
|
+
} else if (t.startsWith("toolcall-")) {
|
|
7444
|
+
toolCalls++;
|
|
7445
|
+
}
|
|
7446
|
+
}
|
|
7447
|
+
if (startTs === 0) {
|
|
7448
|
+
let earliest = Infinity;
|
|
7449
|
+
for (const tag of lines) {
|
|
7450
|
+
const t = tag.trim();
|
|
7451
|
+
if (!t) continue;
|
|
7452
|
+
const logResult = spawnSync("git", ["log", "-1", "--format=%at", t], {
|
|
7453
|
+
cwd: repoPath,
|
|
7454
|
+
encoding: "utf-8",
|
|
7455
|
+
timeout: 3e3
|
|
7456
|
+
});
|
|
7457
|
+
if (logResult.status !== 0 || !logResult.stdout?.trim()) continue;
|
|
7458
|
+
const ts3 = parseInt(logResult.stdout.trim(), 10) * 1e3;
|
|
7459
|
+
if (ts3 > 0 && ts3 < earliest) earliest = ts3;
|
|
7460
|
+
}
|
|
7461
|
+
if (earliest < Infinity) startTs = earliest;
|
|
7462
|
+
}
|
|
7463
|
+
if (chatTurns === 0 && toolCalls === 0) return null;
|
|
7464
|
+
if (startTs === 0) return null;
|
|
7465
|
+
return { sessionId, startTs, chatTurns, toolCalls };
|
|
7466
|
+
} catch {
|
|
7467
|
+
return null;
|
|
7468
|
+
}
|
|
7469
|
+
}
|
|
7470
|
+
function runParseTrae(options) {
|
|
7471
|
+
const { dbPath, device, deviceInstanceId, platform: platform5, now, lastImportedAt } = options;
|
|
7472
|
+
const records = [];
|
|
7473
|
+
const toolCalls = [];
|
|
7474
|
+
const errors = [];
|
|
7475
|
+
let latestTs = lastImportedAt ?? 0;
|
|
7476
|
+
const dataDir = deriveDataDir(dbPath);
|
|
7477
|
+
if (!dataDir) {
|
|
7478
|
+
return { records, toolCalls, lastImportedAt: latestTs, errors: ["Trae ModularData not found"] };
|
|
7479
|
+
}
|
|
7480
|
+
let entries;
|
|
7481
|
+
try {
|
|
7482
|
+
entries = readdirSync2(dataDir, { withFileTypes: true });
|
|
7483
|
+
} catch (e) {
|
|
7484
|
+
return { records, toolCalls, lastImportedAt: latestTs, errors: [String(e)] };
|
|
7485
|
+
}
|
|
7486
|
+
for (const entry of entries) {
|
|
7487
|
+
if (!entry.isDirectory()) continue;
|
|
7488
|
+
const repoPath = join7(dataDir, entry.name, "v2");
|
|
7489
|
+
if (!existsSync8(join7(repoPath, ".git"))) continue;
|
|
7490
|
+
const session = parseSessionTags(repoPath);
|
|
7491
|
+
if (!session) continue;
|
|
7492
|
+
if (session.startTs < latestTs) continue;
|
|
7493
|
+
if (session.startTs > latestTs) latestTs = session.startTs;
|
|
7494
|
+
const estInputTokens = session.chatTurns * 200;
|
|
7495
|
+
const estOutputTokens = session.chatTurns * 800;
|
|
7496
|
+
const recordId = generateRecordId(deviceInstanceId, `${dbPath}:${session.sessionId}`, session.startTs);
|
|
7497
|
+
const record = {
|
|
7498
|
+
id: recordId,
|
|
7499
|
+
ts: session.startTs,
|
|
7500
|
+
ingestedAt: now,
|
|
7501
|
+
updatedAt: now,
|
|
7502
|
+
lineOffset: 0,
|
|
7503
|
+
tool: "trae",
|
|
7504
|
+
model: "trae-agent",
|
|
7505
|
+
provider: "trae",
|
|
7506
|
+
inputTokens: estInputTokens,
|
|
7507
|
+
outputTokens: estOutputTokens,
|
|
7508
|
+
cacheReadTokens: 0,
|
|
7509
|
+
cacheWriteTokens: 0,
|
|
7510
|
+
thinkingTokens: 0,
|
|
7511
|
+
cost: 0,
|
|
7512
|
+
costSource: "unknown",
|
|
7513
|
+
sessionId: session.sessionId,
|
|
7514
|
+
sourceFile: dbPath,
|
|
7515
|
+
device,
|
|
7516
|
+
deviceInstanceId,
|
|
7517
|
+
platform: platform5
|
|
7518
|
+
};
|
|
7519
|
+
records.push(record);
|
|
7520
|
+
let callIndex = 0;
|
|
7521
|
+
for (let i = 0; i < session.toolCalls; i++) {
|
|
7522
|
+
toolCalls.push({
|
|
7523
|
+
id: generateRecordId(recordId, "tool_call", session.startTs + callIndex),
|
|
7524
|
+
recordId,
|
|
7525
|
+
name: "trae-tool",
|
|
7526
|
+
ts: session.startTs + callIndex,
|
|
7527
|
+
callIndex
|
|
7528
|
+
});
|
|
7529
|
+
callIndex++;
|
|
7530
|
+
}
|
|
7531
|
+
}
|
|
7532
|
+
return { records, toolCalls, lastImportedAt: latestTs, errors };
|
|
7533
|
+
}
|
|
7534
|
+
|
|
7535
|
+
// src/commands/parse-codebuddy.ts
|
|
7536
|
+
import { existsSync as existsSync9, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
|
|
7537
|
+
import { join as join8, basename as basename4, dirname as dirname4 } from "path";
|
|
7538
|
+
function num3(value) {
|
|
7539
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
7540
|
+
}
|
|
7541
|
+
function toMs(iso) {
|
|
7542
|
+
if (typeof iso !== "string") return 0;
|
|
7543
|
+
const ms = Date.parse(iso);
|
|
7544
|
+
return Number.isFinite(ms) ? ms : 0;
|
|
7545
|
+
}
|
|
7546
|
+
var WORKSPACE_FOLDER_RE = /Workspace Folder:\s*(.+)/;
|
|
7547
|
+
function userMessageText(raw) {
|
|
7548
|
+
try {
|
|
7549
|
+
const inner = JSON.parse(raw);
|
|
7550
|
+
if (Array.isArray(inner.content)) {
|
|
7551
|
+
return inner.content.filter((block) => block?.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n");
|
|
7552
|
+
}
|
|
7553
|
+
} catch {
|
|
7554
|
+
}
|
|
7555
|
+
return raw;
|
|
7556
|
+
}
|
|
7557
|
+
function findMessagesDirs(root, depth = 0) {
|
|
7558
|
+
if (depth > 12) return [];
|
|
7559
|
+
let entries;
|
|
7560
|
+
try {
|
|
7561
|
+
entries = readdirSync3(root, { withFileTypes: true });
|
|
7562
|
+
} catch {
|
|
7563
|
+
return [];
|
|
7564
|
+
}
|
|
7565
|
+
const out = [];
|
|
7566
|
+
for (const entry of entries) {
|
|
7567
|
+
if (!entry.isDirectory()) continue;
|
|
7568
|
+
const full = join8(root, entry.name);
|
|
7569
|
+
if (entry.name === "messages") {
|
|
7570
|
+
out.push(full);
|
|
7571
|
+
continue;
|
|
7572
|
+
}
|
|
7573
|
+
out.push(...findMessagesDirs(full, depth + 1));
|
|
7574
|
+
}
|
|
7575
|
+
return out;
|
|
7576
|
+
}
|
|
7577
|
+
function readConversation(messagesDir) {
|
|
7578
|
+
let files;
|
|
7579
|
+
try {
|
|
7580
|
+
files = readdirSync3(messagesDir).filter((f) => f.endsWith(".json"));
|
|
7581
|
+
} catch {
|
|
7582
|
+
return null;
|
|
7583
|
+
}
|
|
7584
|
+
if (files.length === 0) return null;
|
|
7585
|
+
const parsed = [];
|
|
7586
|
+
for (const file of files) {
|
|
7587
|
+
try {
|
|
7588
|
+
const data = JSON.parse(readFileSync9(join8(messagesDir, file), "utf-8"));
|
|
7589
|
+
if (data && typeof data === "object") parsed.push(data);
|
|
7590
|
+
} catch {
|
|
7591
|
+
}
|
|
7592
|
+
}
|
|
7593
|
+
if (parsed.length === 0) return null;
|
|
7594
|
+
let cwd;
|
|
7595
|
+
for (const msg of parsed) {
|
|
7596
|
+
if (msg.role !== "user" || typeof msg.message !== "string") continue;
|
|
7597
|
+
const match = WORKSPACE_FOLDER_RE.exec(userMessageText(msg.message));
|
|
7598
|
+
if (match) {
|
|
7599
|
+
cwd = match[1].trim();
|
|
7600
|
+
break;
|
|
7601
|
+
}
|
|
7602
|
+
}
|
|
7603
|
+
let latest = null;
|
|
7604
|
+
for (const msg of parsed) {
|
|
7605
|
+
if (msg.role !== "assistant" || typeof msg.extra !== "string") continue;
|
|
7606
|
+
let extra2;
|
|
7607
|
+
try {
|
|
7608
|
+
extra2 = JSON.parse(msg.extra);
|
|
7609
|
+
} catch {
|
|
7610
|
+
continue;
|
|
7611
|
+
}
|
|
7612
|
+
const hasStats = extra2.statsSnapshot != null || extra2.lastStepInputTokens != null;
|
|
7613
|
+
if (!hasStats) continue;
|
|
7614
|
+
const ts3 = toMs(msg.createdAt);
|
|
7615
|
+
if (!latest || ts3 >= latest.ts) latest = { ts: ts3, extra: extra2 };
|
|
7616
|
+
}
|
|
7617
|
+
if (!latest) return null;
|
|
7618
|
+
const extra = latest.extra;
|
|
7619
|
+
const snapshot = extra.statsSnapshot ?? {};
|
|
7620
|
+
const cacheReadTokens = num3(snapshot.cachedInputTokens ?? extra.lastStepCachedInputTokens);
|
|
7621
|
+
const inputTokens = snapshot.cacheMissTokens != null ? num3(snapshot.cacheMissTokens) : Math.max(0, num3(extra.lastStepInputTokens) - cacheReadTokens);
|
|
7622
|
+
const outputTokens = num3(snapshot.lastOutputTokens ?? extra.lastStepOutputTokens);
|
|
7623
|
+
const cacheWriteTokens = num3(snapshot.cacheWriteTokens);
|
|
7624
|
+
const thinkingTokens = num3(snapshot.thinkingTokens);
|
|
7625
|
+
const model = typeof extra.modelId === "string" && extra.modelId.trim() ? extra.modelId.trim() : "unknown";
|
|
7626
|
+
return {
|
|
7627
|
+
ts: latest.ts,
|
|
7628
|
+
model,
|
|
7629
|
+
inputTokens,
|
|
7630
|
+
outputTokens,
|
|
7631
|
+
cacheReadTokens,
|
|
7632
|
+
cacheWriteTokens,
|
|
7633
|
+
thinkingTokens,
|
|
7634
|
+
cwd
|
|
7635
|
+
};
|
|
7636
|
+
}
|
|
7637
|
+
function runParseCodeBuddy(options) {
|
|
7638
|
+
const { dataDir, device, deviceInstanceId, platform: platform5, now, cursor, exchangeRate } = options;
|
|
7639
|
+
const records = [];
|
|
7640
|
+
const errors = [];
|
|
7641
|
+
let nextCursor = cursor ?? 0;
|
|
7642
|
+
if (!existsSync9(dataDir)) {
|
|
7643
|
+
return { records, nextCursor, errors };
|
|
7644
|
+
}
|
|
7645
|
+
let messagesDirs;
|
|
7646
|
+
try {
|
|
7647
|
+
messagesDirs = findMessagesDirs(dataDir);
|
|
7648
|
+
} catch (e) {
|
|
7649
|
+
return { records, nextCursor, errors: [String(e)] };
|
|
7650
|
+
}
|
|
7651
|
+
for (const messagesDir of messagesDirs) {
|
|
7652
|
+
const conversationDir = dirname4(messagesDir);
|
|
7653
|
+
const sessionId = basename4(conversationDir);
|
|
7654
|
+
let usage;
|
|
7655
|
+
try {
|
|
7656
|
+
usage = readConversation(messagesDir);
|
|
7657
|
+
} catch (e) {
|
|
7658
|
+
errors.push(`${messagesDir}: ${e instanceof Error ? e.message : e}`);
|
|
7659
|
+
continue;
|
|
7660
|
+
}
|
|
7661
|
+
if (!usage) continue;
|
|
7662
|
+
if (usage.inputTokens + usage.outputTokens + usage.cacheReadTokens === 0) continue;
|
|
7663
|
+
if (cursor && usage.ts <= cursor) continue;
|
|
7664
|
+
if (usage.ts > nextCursor) nextCursor = usage.ts;
|
|
7665
|
+
const provider = inferProvider(usage.model);
|
|
7666
|
+
const tokenArgs = {
|
|
7667
|
+
inputTokens: usage.inputTokens,
|
|
7668
|
+
outputTokens: usage.outputTokens,
|
|
7669
|
+
cacheReadTokens: usage.cacheReadTokens,
|
|
7670
|
+
cacheWriteTokens: usage.cacheWriteTokens,
|
|
7671
|
+
thinkingTokens: usage.thinkingTokens
|
|
7672
|
+
};
|
|
7673
|
+
const cost = calculateCost(usage.model, tokenArgs, exchangeRate);
|
|
7674
|
+
const recordId = generateRecordId(deviceInstanceId, conversationDir, usage.ts);
|
|
7675
|
+
records.push({
|
|
7676
|
+
id: recordId,
|
|
7677
|
+
ts: usage.ts,
|
|
7678
|
+
ingestedAt: now,
|
|
7679
|
+
updatedAt: now,
|
|
7680
|
+
lineOffset: 0,
|
|
7681
|
+
tool: "codebuddy",
|
|
7682
|
+
model: usage.model,
|
|
7683
|
+
provider,
|
|
7684
|
+
inputTokens: usage.inputTokens,
|
|
7685
|
+
outputTokens: usage.outputTokens,
|
|
7686
|
+
cacheReadTokens: usage.cacheReadTokens,
|
|
7687
|
+
cacheWriteTokens: usage.cacheWriteTokens,
|
|
7688
|
+
thinkingTokens: usage.thinkingTokens,
|
|
7689
|
+
cost,
|
|
7690
|
+
costSource: cost > 0 ? "pricing" : "unknown",
|
|
7691
|
+
sessionId,
|
|
7692
|
+
sourceFile: conversationDir,
|
|
7693
|
+
cwd: usage.cwd,
|
|
7694
|
+
device,
|
|
7695
|
+
deviceInstanceId,
|
|
7696
|
+
platform: platform5
|
|
7697
|
+
});
|
|
7698
|
+
}
|
|
7699
|
+
return { records, nextCursor, errors };
|
|
7700
|
+
}
|
|
7701
|
+
|
|
7140
7702
|
// src/commands/parse.ts
|
|
7141
7703
|
function extractCwdFromJson(data) {
|
|
7142
7704
|
if (typeof data.cwd === "string" && data.cwd) return data.cwd;
|
|
@@ -7180,6 +7742,7 @@ function extractSessionId2(filePath, tool) {
|
|
|
7180
7742
|
}
|
|
7181
7743
|
return "unknown";
|
|
7182
7744
|
}
|
|
7745
|
+
var CHARS_PER_TOKEN2 = 4;
|
|
7183
7746
|
function toNumber2(value) {
|
|
7184
7747
|
const n = Number(value);
|
|
7185
7748
|
return Number.isFinite(n) && n > 0 ? Math.floor(n) : 0;
|
|
@@ -7190,7 +7753,7 @@ function parseUiMessagesFile(options) {
|
|
|
7190
7753
|
const errors = [];
|
|
7191
7754
|
let messages;
|
|
7192
7755
|
try {
|
|
7193
|
-
const parsed = JSON.parse(
|
|
7756
|
+
const parsed = JSON.parse(readFileSync10(filePath, "utf-8"));
|
|
7194
7757
|
messages = Array.isArray(parsed) ? parsed : [];
|
|
7195
7758
|
} catch (e) {
|
|
7196
7759
|
return { records, errors: [`${filePath}: ${e instanceof Error ? e.message : e}`] };
|
|
@@ -7257,7 +7820,7 @@ function parseTimestamp2(value, fallback) {
|
|
|
7257
7820
|
}
|
|
7258
7821
|
function pathIsFile(filePath) {
|
|
7259
7822
|
try {
|
|
7260
|
-
return
|
|
7823
|
+
return statSync3(filePath).isFile();
|
|
7261
7824
|
} catch {
|
|
7262
7825
|
return false;
|
|
7263
7826
|
}
|
|
@@ -7268,10 +7831,13 @@ function parseKiroSessionFile(options) {
|
|
|
7268
7831
|
const errors = [];
|
|
7269
7832
|
let parsed;
|
|
7270
7833
|
try {
|
|
7271
|
-
parsed = JSON.parse(
|
|
7834
|
+
parsed = JSON.parse(readFileSync10(filePath, "utf-8"));
|
|
7272
7835
|
} catch (e) {
|
|
7273
7836
|
return { records, errors: [`${filePath}: ${e instanceof Error ? e.message : e}`] };
|
|
7274
7837
|
}
|
|
7838
|
+
if (Array.isArray(parsed?.history) && !parsed?.session_state) {
|
|
7839
|
+
return parseKiroWorkspaceSession({ filePath, parsed, device, deviceInstanceId, platform: platform5, now, exchangeRate });
|
|
7840
|
+
}
|
|
7275
7841
|
const turns = parsed?.session_state?.conversation_metadata?.user_turn_metadatas;
|
|
7276
7842
|
if (!Array.isArray(turns)) return { records, errors };
|
|
7277
7843
|
const sessionId = typeof parsed?.session_id === "string" && parsed.session_id ? parsed.session_id : extractSessionId2(filePath, "kiro");
|
|
@@ -7313,6 +7879,83 @@ function parseKiroSessionFile(options) {
|
|
|
7313
7879
|
}
|
|
7314
7880
|
return { records, errors };
|
|
7315
7881
|
}
|
|
7882
|
+
function parseKiroWorkspaceSession(options) {
|
|
7883
|
+
const { filePath, parsed, device, deviceInstanceId, platform: platform5, now, exchangeRate } = options;
|
|
7884
|
+
const records = [];
|
|
7885
|
+
const errors = [];
|
|
7886
|
+
const sessionId = parsed?.sessionId ?? extractSessionId2(filePath, "kiro");
|
|
7887
|
+
let sessionTs = now;
|
|
7888
|
+
try {
|
|
7889
|
+
const dir = dirname5(filePath);
|
|
7890
|
+
const sessionsJson = readFileSync10(join9(dir, "sessions.json"), "utf-8");
|
|
7891
|
+
const sessions = JSON.parse(sessionsJson);
|
|
7892
|
+
const match = Array.isArray(sessions) && sessions.find((s) => s.sessionId === sessionId);
|
|
7893
|
+
if (match?.dateCreated) {
|
|
7894
|
+
const dc = Number(match.dateCreated);
|
|
7895
|
+
if (Number.isFinite(dc) && dc > 0) sessionTs = dc < 1e12 ? dc * 1e3 : dc;
|
|
7896
|
+
}
|
|
7897
|
+
} catch {
|
|
7898
|
+
}
|
|
7899
|
+
const allLogs = [];
|
|
7900
|
+
let logIndex = 0;
|
|
7901
|
+
for (const entry of parsed.history ?? []) {
|
|
7902
|
+
for (const pl of entry?.promptLogs ?? []) {
|
|
7903
|
+
allLogs.push({ log: pl, index: logIndex++ });
|
|
7904
|
+
}
|
|
7905
|
+
}
|
|
7906
|
+
const contextLength = parsed?.config?.models?.[0]?.contextLength ?? 4e4;
|
|
7907
|
+
const maxTokens = parsed?.config?.models?.[0]?.completionOptions?.maxTokens ?? 4e3;
|
|
7908
|
+
const contextPct = Number(parsed?.contextUsagePercentageBySession?.[sessionId] ?? parsed?.contextUsagePercentage ?? 0) / 100;
|
|
7909
|
+
const totalInputEstimate = contextPct > 0 ? Math.floor(contextLength * contextPct) : 0;
|
|
7910
|
+
const hasNoTokenData = allLogs.every(({ log }) => {
|
|
7911
|
+
const pl = typeof log.prompt === "string" ? log.prompt.length : 0;
|
|
7912
|
+
const cl = typeof log.completion === "string" ? log.completion.length : 0;
|
|
7913
|
+
return pl < 100 && cl === 0;
|
|
7914
|
+
});
|
|
7915
|
+
for (const { log, index } of allLogs) {
|
|
7916
|
+
const promptLen = typeof log.prompt === "string" ? log.prompt.length : 0;
|
|
7917
|
+
const completionLen = typeof log.completion === "string" ? log.completion.length : 0;
|
|
7918
|
+
let inputTokens;
|
|
7919
|
+
let outputTokens;
|
|
7920
|
+
if (hasNoTokenData) {
|
|
7921
|
+
inputTokens = totalInputEstimate > 0 && allLogs.length > 0 ? Math.max(1, Math.floor(totalInputEstimate / allLogs.length)) : 200;
|
|
7922
|
+
outputTokens = Math.floor(maxTokens * 0.3);
|
|
7923
|
+
} else {
|
|
7924
|
+
inputTokens = totalInputEstimate > 0 && allLogs.length > 0 ? Math.max(1, Math.floor(totalInputEstimate / allLogs.length)) : Math.max(1, Math.floor(promptLen / CHARS_PER_TOKEN2));
|
|
7925
|
+
outputTokens = completionLen > 0 ? Math.floor(completionLen / CHARS_PER_TOKEN2) : Math.floor(maxTokens * 0.3);
|
|
7926
|
+
}
|
|
7927
|
+
if (inputTokens + outputTokens === 0) continue;
|
|
7928
|
+
const model = normalizeKiroModel(log.completionOptions?.model ?? log.modelTitle);
|
|
7929
|
+
const provider = typeof log.provider === "string" && log.provider.trim() ? log.provider.trim().toLowerCase() : inferProvider(model);
|
|
7930
|
+
const recordTs = sessionTs + index;
|
|
7931
|
+
const tokenArgs = { inputTokens, outputTokens, cacheReadTokens: 0, cacheWriteTokens: 0, thinkingTokens: 0 };
|
|
7932
|
+
const cost = calculateCost(model, tokenArgs, exchangeRate);
|
|
7933
|
+
const sourceId = `${sessionId}:${index}`;
|
|
7934
|
+
records.push({
|
|
7935
|
+
id: generateRecordId(deviceInstanceId, `${filePath}:${sourceId}`, recordTs),
|
|
7936
|
+
ts: recordTs,
|
|
7937
|
+
ingestedAt: now,
|
|
7938
|
+
updatedAt: now,
|
|
7939
|
+
lineOffset: index,
|
|
7940
|
+
tool: "kiro",
|
|
7941
|
+
model,
|
|
7942
|
+
provider,
|
|
7943
|
+
inputTokens,
|
|
7944
|
+
outputTokens,
|
|
7945
|
+
cacheReadTokens: 0,
|
|
7946
|
+
cacheWriteTokens: 0,
|
|
7947
|
+
thinkingTokens: 0,
|
|
7948
|
+
cost,
|
|
7949
|
+
costSource: cost > 0 ? "pricing" : "unknown",
|
|
7950
|
+
sessionId,
|
|
7951
|
+
sourceFile: filePath,
|
|
7952
|
+
device,
|
|
7953
|
+
deviceInstanceId,
|
|
7954
|
+
platform: platform5
|
|
7955
|
+
});
|
|
7956
|
+
}
|
|
7957
|
+
return { records, errors };
|
|
7958
|
+
}
|
|
7316
7959
|
async function runParse(db, filterTool, options) {
|
|
7317
7960
|
const state = getState(AIUSAGE_DIR);
|
|
7318
7961
|
const config = loadConfig();
|
|
@@ -7320,7 +7963,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7320
7963
|
const device = config?.device || hostname2() || state?.deviceInstanceId?.slice(0, 8) || "unknown";
|
|
7321
7964
|
const deviceInstanceId = state?.deviceInstanceId ?? "unknown";
|
|
7322
7965
|
const devicePlatform = config?.platform;
|
|
7323
|
-
const watermarkPath =
|
|
7966
|
+
const watermarkPath = join9(AIUSAGE_DIR, "watermark.json");
|
|
7324
7967
|
const wm = new WatermarkManager(watermarkPath);
|
|
7325
7968
|
const toolPaths = discoverLogFiles();
|
|
7326
7969
|
const aggregator = new Aggregator();
|
|
@@ -7336,7 +7979,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7336
7979
|
for (const filePath of paths) {
|
|
7337
7980
|
toolIndex++;
|
|
7338
7981
|
try {
|
|
7339
|
-
const stat2 =
|
|
7982
|
+
const stat2 = statSync3(filePath);
|
|
7340
7983
|
const entry = wm.getEntry(tool, filePath);
|
|
7341
7984
|
const offset = entry?.offset ?? 0;
|
|
7342
7985
|
if (offset >= stat2.size) {
|
|
@@ -7390,6 +8033,66 @@ async function runParse(db, filterTool, options) {
|
|
|
7390
8033
|
onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
|
|
7391
8034
|
continue;
|
|
7392
8035
|
}
|
|
8036
|
+
if (tool === "kiro" && filePath.endsWith(".jsonl")) {
|
|
8037
|
+
const content2 = readFileSync10(filePath, "utf-8");
|
|
8038
|
+
const lines2 = content2.split("\n");
|
|
8039
|
+
let byteOffset2 = 0;
|
|
8040
|
+
for (const line of lines2) {
|
|
8041
|
+
if (!line.trim()) {
|
|
8042
|
+
byteOffset2 += Buffer.byteLength(line, "utf-8") + 1;
|
|
8043
|
+
continue;
|
|
8044
|
+
}
|
|
8045
|
+
try {
|
|
8046
|
+
const data = JSON.parse(line);
|
|
8047
|
+
const inputTokens = toNumber2(data.promptTokens ?? data.inputTokens ?? data.tokensIn);
|
|
8048
|
+
const outputTokens = toNumber2(data.generatedTokens ?? data.outputTokens ?? data.tokensOut);
|
|
8049
|
+
if (inputTokens + outputTokens === 0) {
|
|
8050
|
+
byteOffset2 += Buffer.byteLength(line, "utf-8") + 1;
|
|
8051
|
+
continue;
|
|
8052
|
+
}
|
|
8053
|
+
const model = normalizeKiroModel(data.model);
|
|
8054
|
+
const provider = typeof data.provider === "string" && data.provider.trim() ? data.provider.trim().toLowerCase() : inferProvider(model);
|
|
8055
|
+
const recordTs = stat2.mtimeMs;
|
|
8056
|
+
const tokenArgs = { inputTokens, outputTokens, cacheReadTokens: 0, cacheWriteTokens: 0, thinkingTokens: 0 };
|
|
8057
|
+
const cost = calculateCost(model, tokenArgs, exchangeRate);
|
|
8058
|
+
const recordId = generateRecordId(deviceInstanceId, `${filePath}:${byteOffset2}`, recordTs);
|
|
8059
|
+
const record = {
|
|
8060
|
+
id: recordId,
|
|
8061
|
+
ts: recordTs,
|
|
8062
|
+
ingestedAt: Date.now(),
|
|
8063
|
+
updatedAt: Date.now(),
|
|
8064
|
+
lineOffset: byteOffset2,
|
|
8065
|
+
tool: "kiro",
|
|
8066
|
+
model,
|
|
8067
|
+
provider,
|
|
8068
|
+
inputTokens,
|
|
8069
|
+
outputTokens,
|
|
8070
|
+
cacheReadTokens: 0,
|
|
8071
|
+
cacheWriteTokens: 0,
|
|
8072
|
+
thinkingTokens: 0,
|
|
8073
|
+
cost,
|
|
8074
|
+
costSource: cost > 0 ? "pricing" : "unknown",
|
|
8075
|
+
sessionId: filePath,
|
|
8076
|
+
sourceFile: filePath,
|
|
8077
|
+
device,
|
|
8078
|
+
deviceInstanceId,
|
|
8079
|
+
platform: devicePlatform
|
|
8080
|
+
};
|
|
8081
|
+
insertRecord(db, record);
|
|
8082
|
+
parsedCount++;
|
|
8083
|
+
} catch {
|
|
8084
|
+
}
|
|
8085
|
+
byteOffset2 += Buffer.byteLength(line, "utf-8") + 1;
|
|
8086
|
+
}
|
|
8087
|
+
wm.setEntry(tool, filePath, {
|
|
8088
|
+
offset: stat2.size,
|
|
8089
|
+
size: stat2.size,
|
|
8090
|
+
mtime: stat2.mtimeMs
|
|
8091
|
+
});
|
|
8092
|
+
wm.save();
|
|
8093
|
+
onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
|
|
8094
|
+
continue;
|
|
8095
|
+
}
|
|
7393
8096
|
if (tool === "kelivo" && (filePath.endsWith("chats.json") || filePath.endsWith(".zip"))) {
|
|
7394
8097
|
const result = await runParseKelivo({
|
|
7395
8098
|
filePath,
|
|
@@ -7413,7 +8116,55 @@ async function runParse(db, filterTool, options) {
|
|
|
7413
8116
|
onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
|
|
7414
8117
|
continue;
|
|
7415
8118
|
}
|
|
7416
|
-
|
|
8119
|
+
if (tool === "cursor" && filePath.endsWith(".jsonl") && filePath.includes("agent-transcripts")) {
|
|
8120
|
+
const est = runParseCursorTranscript({
|
|
8121
|
+
jsonlPath: filePath,
|
|
8122
|
+
device,
|
|
8123
|
+
deviceInstanceId,
|
|
8124
|
+
platform: devicePlatform,
|
|
8125
|
+
now: Date.now()
|
|
8126
|
+
});
|
|
8127
|
+
if (est.inputTextChars + est.outputTextChars > 0) {
|
|
8128
|
+
const sessionId2 = filePath.split("/").slice(-2, -1)[0] || "unknown";
|
|
8129
|
+
const recordTs = stat2.mtimeMs;
|
|
8130
|
+
const tokenArgs = { inputTokens: est.inputTextChars, outputTokens: est.outputTextChars, cacheReadTokens: 0, cacheWriteTokens: 0, thinkingTokens: 0 };
|
|
8131
|
+
const cost = calculateCost("cursor-composer", tokenArgs, exchangeRate);
|
|
8132
|
+
const recordId = generateRecordId(deviceInstanceId, filePath, recordTs);
|
|
8133
|
+
const record = {
|
|
8134
|
+
id: recordId,
|
|
8135
|
+
ts: recordTs,
|
|
8136
|
+
ingestedAt: Date.now(),
|
|
8137
|
+
updatedAt: Date.now(),
|
|
8138
|
+
lineOffset: 0,
|
|
8139
|
+
tool: "cursor",
|
|
8140
|
+
model: "cursor-composer",
|
|
8141
|
+
provider: "cursor",
|
|
8142
|
+
inputTokens: est.inputTextChars,
|
|
8143
|
+
outputTokens: est.outputTextChars,
|
|
8144
|
+
cacheReadTokens: 0,
|
|
8145
|
+
cacheWriteTokens: 0,
|
|
8146
|
+
thinkingTokens: 0,
|
|
8147
|
+
cost,
|
|
8148
|
+
costSource: cost > 0 ? "pricing" : "unknown",
|
|
8149
|
+
sessionId: sessionId2,
|
|
8150
|
+
sourceFile: filePath,
|
|
8151
|
+
device,
|
|
8152
|
+
deviceInstanceId,
|
|
8153
|
+
platform: devicePlatform
|
|
8154
|
+
};
|
|
8155
|
+
insertRecord(db, record);
|
|
8156
|
+
parsedCount++;
|
|
8157
|
+
}
|
|
8158
|
+
wm.setEntry(tool, filePath, {
|
|
8159
|
+
offset: stat2.size,
|
|
8160
|
+
size: stat2.size,
|
|
8161
|
+
mtime: stat2.mtimeMs
|
|
8162
|
+
});
|
|
8163
|
+
wm.save();
|
|
8164
|
+
onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
|
|
8165
|
+
continue;
|
|
8166
|
+
}
|
|
8167
|
+
const content = readFileSync10(filePath, "utf-8");
|
|
7417
8168
|
const lines = content.split("\n");
|
|
7418
8169
|
let byteOffset = 0;
|
|
7419
8170
|
let fileCwd;
|
|
@@ -7477,7 +8228,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7477
8228
|
}
|
|
7478
8229
|
}
|
|
7479
8230
|
if (fileCwd) {
|
|
7480
|
-
db.prepare(`UPDATE records SET cwd = ? WHERE source_file = ? AND cwd = ''`).run(fileCwd, filePath);
|
|
8231
|
+
db.prepare(`UPDATE records SET cwd = ?, updated_at = ? WHERE source_file = ? AND cwd = ''`).run(fileCwd, Date.now(), filePath);
|
|
7481
8232
|
}
|
|
7482
8233
|
wm.setEntry(tool, filePath, {
|
|
7483
8234
|
offset: stat2.size,
|
|
@@ -7492,7 +8243,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7492
8243
|
}
|
|
7493
8244
|
}
|
|
7494
8245
|
const openCodeDbPath = options?.openCodeDbPath ?? getDbPath("opencode") ?? "";
|
|
7495
|
-
if ((!filterTool || filterTool === "opencode") &&
|
|
8246
|
+
if ((!filterTool || filterTool === "opencode") && existsSync10(openCodeDbPath)) {
|
|
7496
8247
|
try {
|
|
7497
8248
|
const openCodeDb = new Database2(openCodeDbPath, { readonly: true });
|
|
7498
8249
|
try {
|
|
@@ -7523,7 +8274,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7523
8274
|
}
|
|
7524
8275
|
}
|
|
7525
8276
|
const hermesDbPath = options?.hermesDbPath ?? getDbPath("hermes") ?? "";
|
|
7526
|
-
if ((!filterTool || filterTool === "hermes") &&
|
|
8277
|
+
if ((!filterTool || filterTool === "hermes") && existsSync10(hermesDbPath)) {
|
|
7527
8278
|
try {
|
|
7528
8279
|
const hermesDb = new Database2(hermesDbPath, { readonly: true });
|
|
7529
8280
|
try {
|
|
@@ -7554,7 +8305,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7554
8305
|
}
|
|
7555
8306
|
}
|
|
7556
8307
|
const qoderDbPath = options?.qoderDbPath ?? getDbPath("qoder-db") ?? "";
|
|
7557
|
-
if ((!filterTool || filterTool === "qoder") &&
|
|
8308
|
+
if ((!filterTool || filterTool === "qoder") && existsSync10(qoderDbPath)) {
|
|
7558
8309
|
try {
|
|
7559
8310
|
const qoderDb = new Database2(qoderDbPath, { readonly: true });
|
|
7560
8311
|
try {
|
|
@@ -7585,7 +8336,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7585
8336
|
}
|
|
7586
8337
|
}
|
|
7587
8338
|
const cursorDbPath = options?.cursorDbPath ?? getDbPath("cursor") ?? "";
|
|
7588
|
-
if ((!filterTool || filterTool === "cursor") &&
|
|
8339
|
+
if ((!filterTool || filterTool === "cursor") && existsSync10(cursorDbPath)) {
|
|
7589
8340
|
try {
|
|
7590
8341
|
const cursorDb = new Database2(cursorDbPath, { readonly: true });
|
|
7591
8342
|
try {
|
|
@@ -7615,7 +8366,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7615
8366
|
}
|
|
7616
8367
|
}
|
|
7617
8368
|
const kiloDbPath = getDbPath("kilocode-db") ?? "";
|
|
7618
|
-
if ((!filterTool || filterTool === "kilocode") &&
|
|
8369
|
+
if ((!filterTool || filterTool === "kilocode") && existsSync10(kiloDbPath)) {
|
|
7619
8370
|
try {
|
|
7620
8371
|
const kiloDb = new Database2(kiloDbPath, { readonly: true });
|
|
7621
8372
|
try {
|
|
@@ -7655,7 +8406,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7655
8406
|
}
|
|
7656
8407
|
}
|
|
7657
8408
|
const gooseDbPath = getDbPath("goose") ?? "";
|
|
7658
|
-
if ((!filterTool || filterTool === "goose") &&
|
|
8409
|
+
if ((!filterTool || filterTool === "goose") && existsSync10(gooseDbPath)) {
|
|
7659
8410
|
try {
|
|
7660
8411
|
const gooseDb = new Database2(gooseDbPath, { readonly: true });
|
|
7661
8412
|
try {
|
|
@@ -7713,7 +8464,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7713
8464
|
}
|
|
7714
8465
|
}
|
|
7715
8466
|
const zedDbPath = getDbPath("zed") ?? "";
|
|
7716
|
-
if ((!filterTool || filterTool === "zed") &&
|
|
8467
|
+
if ((!filterTool || filterTool === "zed") && existsSync10(zedDbPath)) {
|
|
7717
8468
|
try {
|
|
7718
8469
|
const zedDb = new Database2(zedDbPath, { readonly: true });
|
|
7719
8470
|
try {
|
|
@@ -7743,7 +8494,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7743
8494
|
}
|
|
7744
8495
|
if (!filterTool || filterTool === "zcode") {
|
|
7745
8496
|
const zcodeDbPath = getDbPath("zcode") ?? "";
|
|
7746
|
-
if (
|
|
8497
|
+
if (existsSync10(zcodeDbPath)) {
|
|
7747
8498
|
try {
|
|
7748
8499
|
const zcodeDb = new Database2(zcodeDbPath, { readonly: true });
|
|
7749
8500
|
try {
|
|
@@ -7780,6 +8531,53 @@ async function runParse(db, filterTool, options) {
|
|
|
7780
8531
|
}
|
|
7781
8532
|
}
|
|
7782
8533
|
}
|
|
8534
|
+
const traeDbPath = getDbPath("trae") ?? "";
|
|
8535
|
+
if ((!filterTool || filterTool === "trae") && pathIsFile(traeDbPath)) {
|
|
8536
|
+
const traeLastImported = wm.getTraeLastImported?.() ?? 0;
|
|
8537
|
+
const result = runParseTrae({
|
|
8538
|
+
dbPath: traeDbPath,
|
|
8539
|
+
device,
|
|
8540
|
+
deviceInstanceId,
|
|
8541
|
+
platform: devicePlatform,
|
|
8542
|
+
now: Date.now(),
|
|
8543
|
+
lastImportedAt: traeLastImported
|
|
8544
|
+
});
|
|
8545
|
+
for (const record of result.records) insertRecord(db, record);
|
|
8546
|
+
for (const tc of result.toolCalls) insertToolCall(db, tc);
|
|
8547
|
+
parsedCount += result.records.length;
|
|
8548
|
+
toolCallCount += result.toolCalls.length;
|
|
8549
|
+
errors.push(...result.errors);
|
|
8550
|
+
if (result.lastImportedAt > traeLastImported) {
|
|
8551
|
+
wm.setTraeLastImported?.(result.lastImportedAt);
|
|
8552
|
+
wm.save();
|
|
8553
|
+
}
|
|
8554
|
+
onProgress({ phase: "Parsing SQLite", tool: "trae", current: 1, total: 1, records: parsedCount, toolCalls: toolCallCount });
|
|
8555
|
+
}
|
|
8556
|
+
const codeBuddyIdeDir = getDbPath("codebuddy-ide") ?? "";
|
|
8557
|
+
if ((!filterTool || filterTool === "codebuddy") && existsSync10(codeBuddyIdeDir)) {
|
|
8558
|
+
try {
|
|
8559
|
+
const cbCursor = wm.getCodeBuddyIdeCursor();
|
|
8560
|
+
const result = runParseCodeBuddy({
|
|
8561
|
+
dataDir: codeBuddyIdeDir,
|
|
8562
|
+
device,
|
|
8563
|
+
deviceInstanceId,
|
|
8564
|
+
platform: devicePlatform,
|
|
8565
|
+
now: Date.now(),
|
|
8566
|
+
cursor: cbCursor,
|
|
8567
|
+
exchangeRate
|
|
8568
|
+
});
|
|
8569
|
+
for (const record of result.records) insertRecord(db, record);
|
|
8570
|
+
parsedCount += result.records.length;
|
|
8571
|
+
errors.push(...result.errors);
|
|
8572
|
+
if (result.nextCursor > cbCursor) {
|
|
8573
|
+
wm.setCodeBuddyIdeCursor(result.nextCursor);
|
|
8574
|
+
wm.save();
|
|
8575
|
+
}
|
|
8576
|
+
onProgress({ phase: "Parsing logs", tool: "codebuddy", current: 1, total: 1, records: parsedCount, toolCalls: toolCallCount });
|
|
8577
|
+
} catch (e) {
|
|
8578
|
+
errors.push(`${codeBuddyIdeDir}: ${e instanceof Error ? e.message : e}`);
|
|
8579
|
+
}
|
|
8580
|
+
}
|
|
7783
8581
|
if (deviceInstanceId !== "unknown") {
|
|
7784
8582
|
db.prepare(
|
|
7785
8583
|
`UPDATE records SET device_instance_id = ?, device = ? WHERE device_instance_id = 'unknown'`
|
|
@@ -7791,9 +8589,19 @@ async function runParse(db, filterTool, options) {
|
|
|
7791
8589
|
).run(devicePlatform);
|
|
7792
8590
|
}
|
|
7793
8591
|
backfillHermesSourceFiles(db);
|
|
8592
|
+
backfillCwd(db);
|
|
8593
|
+
backfillSkillNames(db);
|
|
8594
|
+
backfillCodexModels(db);
|
|
8595
|
+
backfillMissingToolCalls(db, exchangeRate);
|
|
8596
|
+
return { parsedCount, toolCallCount, errors };
|
|
8597
|
+
}
|
|
8598
|
+
function backfillCwd(db) {
|
|
7794
8599
|
const staleFiles = db.prepare(
|
|
7795
8600
|
`SELECT DISTINCT source_file FROM records WHERE cwd = '' AND source_file NOT LIKE 'synced/%'`
|
|
7796
8601
|
).all();
|
|
8602
|
+
const updateStmt = db.prepare(
|
|
8603
|
+
`UPDATE records SET cwd = ?, updated_at = ? WHERE source_file = ? AND cwd = ''`
|
|
8604
|
+
);
|
|
7797
8605
|
for (const { source_file } of staleFiles) {
|
|
7798
8606
|
try {
|
|
7799
8607
|
const fd = openSync(source_file, "r");
|
|
@@ -7804,15 +8612,11 @@ async function runParse(db, filterTool, options) {
|
|
|
7804
8612
|
const data = JSON.parse(firstLine);
|
|
7805
8613
|
const cwd = extractCwdFromJson(data);
|
|
7806
8614
|
if (cwd) {
|
|
7807
|
-
|
|
8615
|
+
updateStmt.run(cwd, Date.now(), source_file);
|
|
7808
8616
|
}
|
|
7809
8617
|
} catch {
|
|
7810
8618
|
}
|
|
7811
8619
|
}
|
|
7812
|
-
backfillSkillNames(db);
|
|
7813
|
-
backfillCodexModels(db);
|
|
7814
|
-
backfillMissingToolCalls(db, exchangeRate);
|
|
7815
|
-
return { parsedCount, toolCallCount, errors };
|
|
7816
8620
|
}
|
|
7817
8621
|
function backfillHermesSourceFiles(db) {
|
|
7818
8622
|
const rows = db.prepare(`
|
|
@@ -7826,7 +8630,7 @@ function backfillHermesSourceFiles(db) {
|
|
|
7826
8630
|
const dbPath = rows[0].source_file;
|
|
7827
8631
|
let titleMap = /* @__PURE__ */ new Map();
|
|
7828
8632
|
try {
|
|
7829
|
-
if (
|
|
8633
|
+
if (existsSync10(dbPath)) {
|
|
7830
8634
|
const hermesDb = new Database2(dbPath, { readonly: true });
|
|
7831
8635
|
try {
|
|
7832
8636
|
const sessions = hermesDb.prepare(
|
|
@@ -7839,11 +8643,11 @@ function backfillHermesSourceFiles(db) {
|
|
|
7839
8643
|
}
|
|
7840
8644
|
} catch {
|
|
7841
8645
|
}
|
|
7842
|
-
const updateStmt = db.prepare(`UPDATE records SET source_file = ? WHERE tool = 'hermes' AND session_id = ? AND source_file = ?`);
|
|
8646
|
+
const updateStmt = db.prepare(`UPDATE records SET source_file = ?, updated_at = ? WHERE tool = 'hermes' AND session_id = ? AND source_file = ?`);
|
|
7843
8647
|
for (const row of rows) {
|
|
7844
8648
|
const title = (titleMap.get(row.session_id) || "").replace(/[/\\:]/g, "_").slice(0, 80);
|
|
7845
8649
|
const newSourceFile = title ? `${row.source_file}:session:${row.session_id}:${title}` : `${row.source_file}:session:${row.session_id}`;
|
|
7846
|
-
updateStmt.run(newSourceFile, row.session_id, row.source_file);
|
|
8650
|
+
updateStmt.run(newSourceFile, Date.now(), row.session_id, row.source_file);
|
|
7847
8651
|
}
|
|
7848
8652
|
}
|
|
7849
8653
|
function backfillSkillNames(db) {
|
|
@@ -7907,7 +8711,7 @@ function backfillCodexModels(db) {
|
|
|
7907
8711
|
);
|
|
7908
8712
|
for (const [sourceFile, fileRows] of byFile) {
|
|
7909
8713
|
try {
|
|
7910
|
-
const content =
|
|
8714
|
+
const content = readFileSync10(sourceFile, "utf-8");
|
|
7911
8715
|
const lines = content.split("\n");
|
|
7912
8716
|
const offsetToModel = /* @__PURE__ */ new Map();
|
|
7913
8717
|
let currentModel = "";
|
|
@@ -7955,7 +8759,7 @@ function backfillMissingToolCalls(db, exchangeRate) {
|
|
|
7955
8759
|
}
|
|
7956
8760
|
for (const [sourceFile, fileRows] of rowsByFile) {
|
|
7957
8761
|
try {
|
|
7958
|
-
const content =
|
|
8762
|
+
const content = readFileSync10(sourceFile, "utf-8");
|
|
7959
8763
|
const lines = content.split("\n");
|
|
7960
8764
|
const aggregator = new Aggregator();
|
|
7961
8765
|
const recordIdByOffset = /* @__PURE__ */ new Map();
|
|
@@ -8017,7 +8821,7 @@ function deriveSessionId(tool, sourceFile) {
|
|
|
8017
8821
|
}
|
|
8018
8822
|
|
|
8019
8823
|
// src/commands/sync.ts
|
|
8020
|
-
import { join as
|
|
8824
|
+
import { join as join11 } from "path";
|
|
8021
8825
|
|
|
8022
8826
|
// src/db/synced-records.ts
|
|
8023
8827
|
function insertSyncedRecord(db, record) {
|
|
@@ -8450,10 +9254,10 @@ async function cloudClear() {
|
|
|
8450
9254
|
}
|
|
8451
9255
|
function getClientVersion() {
|
|
8452
9256
|
try {
|
|
8453
|
-
const { readFileSync:
|
|
8454
|
-
const { join:
|
|
8455
|
-
const pkgPath =
|
|
8456
|
-
const pkg = JSON.parse(
|
|
9257
|
+
const { readFileSync: readFileSync14 } = __require("fs");
|
|
9258
|
+
const { join: join18 } = __require("path");
|
|
9259
|
+
const pkgPath = join18(__dirname, "../../package.json");
|
|
9260
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
8457
9261
|
return pkg.version || "0.0.0";
|
|
8458
9262
|
} catch {
|
|
8459
9263
|
return "0.0.0";
|
|
@@ -8544,7 +9348,7 @@ var CloudSyncOrchestrator = class {
|
|
|
8544
9348
|
import { execFile } from "child_process";
|
|
8545
9349
|
import { promisify } from "util";
|
|
8546
9350
|
import { readFile, writeFile, mkdir, readdir, stat, unlink, rm } from "fs/promises";
|
|
8547
|
-
import { join as
|
|
9351
|
+
import { join as join10 } from "path";
|
|
8548
9352
|
var exec = promisify(execFile);
|
|
8549
9353
|
var GitSyncBackend = class _GitSyncBackend {
|
|
8550
9354
|
repo;
|
|
@@ -8556,7 +9360,7 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
8556
9360
|
this.repo = config.repo;
|
|
8557
9361
|
this.token = config.token;
|
|
8558
9362
|
this.cacheDir = config.cacheDir;
|
|
8559
|
-
this.dataDir =
|
|
9363
|
+
this.dataDir = join10(config.cacheDir, "data");
|
|
8560
9364
|
this.branch = config.branch ?? "main";
|
|
8561
9365
|
}
|
|
8562
9366
|
get remoteUrl() {
|
|
@@ -8588,7 +9392,7 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
8588
9392
|
/** Clone or pull the repo to get latest remote state */
|
|
8589
9393
|
async prepare() {
|
|
8590
9394
|
try {
|
|
8591
|
-
await stat(
|
|
9395
|
+
await stat(join10(this.cacheDir, ".git"));
|
|
8592
9396
|
await this.git(["fetch", "origin", this.branch, "--depth=1"]);
|
|
8593
9397
|
await this.git(["reset", "--hard", `origin/${this.branch}`]);
|
|
8594
9398
|
} catch {
|
|
@@ -8605,15 +9409,15 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
8605
9409
|
}
|
|
8606
9410
|
async readFile(path3) {
|
|
8607
9411
|
try {
|
|
8608
|
-
const fullPath =
|
|
9412
|
+
const fullPath = join10(this.dataDir, path3);
|
|
8609
9413
|
return await readFile(fullPath, "utf-8");
|
|
8610
9414
|
} catch {
|
|
8611
9415
|
return null;
|
|
8612
9416
|
}
|
|
8613
9417
|
}
|
|
8614
9418
|
async writeFile(path3, content) {
|
|
8615
|
-
const fullPath =
|
|
8616
|
-
await mkdir(
|
|
9419
|
+
const fullPath = join10(this.dataDir, path3);
|
|
9420
|
+
await mkdir(join10(fullPath, ".."), { recursive: true });
|
|
8617
9421
|
await writeFile(fullPath, content, "utf-8");
|
|
8618
9422
|
}
|
|
8619
9423
|
async listFiles() {
|
|
@@ -8624,7 +9428,7 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
8624
9428
|
}
|
|
8625
9429
|
}
|
|
8626
9430
|
async deleteFile(path3) {
|
|
8627
|
-
const fullPath =
|
|
9431
|
+
const fullPath = join10(this.dataDir, path3);
|
|
8628
9432
|
try {
|
|
8629
9433
|
await unlink(fullPath);
|
|
8630
9434
|
} catch {
|
|
@@ -8663,7 +9467,7 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
8663
9467
|
for (const entry of entries) {
|
|
8664
9468
|
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
8665
9469
|
if (entry.isDirectory()) {
|
|
8666
|
-
files.push(...await this.walkDir(
|
|
9470
|
+
files.push(...await this.walkDir(join10(dir, entry.name), relPath));
|
|
8667
9471
|
} else if (entry.name.endsWith(".ndjson")) {
|
|
8668
9472
|
files.push(relPath);
|
|
8669
9473
|
}
|
|
@@ -8797,7 +9601,7 @@ function createBackend(config) {
|
|
|
8797
9601
|
return new GitSyncBackend({
|
|
8798
9602
|
repo: sync.repo,
|
|
8799
9603
|
token,
|
|
8800
|
-
cacheDir:
|
|
9604
|
+
cacheDir: join11(AIUSAGE_DIR, "sync-repo")
|
|
8801
9605
|
});
|
|
8802
9606
|
}
|
|
8803
9607
|
if (sync.backend === "s3") {
|
|
@@ -8892,8 +9696,8 @@ async function runSync(db, options) {
|
|
|
8892
9696
|
}
|
|
8893
9697
|
|
|
8894
9698
|
// src/commands/clean.ts
|
|
8895
|
-
import { unlinkSync as unlinkSync3, existsSync as
|
|
8896
|
-
import { join as
|
|
9699
|
+
import { unlinkSync as unlinkSync3, existsSync as existsSync11 } from "fs";
|
|
9700
|
+
import { join as join12 } from "path";
|
|
8897
9701
|
function cleanOldData(db, days) {
|
|
8898
9702
|
const cutoff = Date.now() - days * 864e5;
|
|
8899
9703
|
const recordsResult = db.prepare("DELETE FROM records WHERE ts < ?").run(cutoff);
|
|
@@ -8914,9 +9718,9 @@ function cleanAll(db) {
|
|
|
8914
9718
|
const syncedResult = db.prepare("DELETE FROM synced_records").run();
|
|
8915
9719
|
const syncStateResult = db.prepare("DELETE FROM sync_record_state").run();
|
|
8916
9720
|
const tombstonesResult = db.prepare("DELETE FROM sync_tombstones").run();
|
|
8917
|
-
const watermarkPath =
|
|
9721
|
+
const watermarkPath = join12(AIUSAGE_DIR, "watermark.json");
|
|
8918
9722
|
let watermarkRemoved = false;
|
|
8919
|
-
if (
|
|
9723
|
+
if (existsSync11(watermarkPath)) {
|
|
8920
9724
|
unlinkSync3(watermarkPath);
|
|
8921
9725
|
watermarkRemoved = true;
|
|
8922
9726
|
}
|
|
@@ -8951,7 +9755,7 @@ function createBackend2(config) {
|
|
|
8951
9755
|
return new GitSyncBackend({
|
|
8952
9756
|
repo: config.sync.repo,
|
|
8953
9757
|
token,
|
|
8954
|
-
cacheDir:
|
|
9758
|
+
cacheDir: join12(AIUSAGE_DIR, "sync-repo")
|
|
8955
9759
|
});
|
|
8956
9760
|
}
|
|
8957
9761
|
if (config.sync.backend === "s3") {
|
|
@@ -9284,7 +10088,7 @@ var RuntimeSettingsController = class {
|
|
|
9284
10088
|
|
|
9285
10089
|
// src/commands/serve.ts
|
|
9286
10090
|
var MAX_PORT_ATTEMPTS = 10;
|
|
9287
|
-
var PORT_FILE =
|
|
10091
|
+
var PORT_FILE = join13(AIUSAGE_DIR, ".serve-port");
|
|
9288
10092
|
var MIME_TYPES = {
|
|
9289
10093
|
".html": "text/html",
|
|
9290
10094
|
".js": "application/javascript",
|
|
@@ -9301,6 +10105,15 @@ function serve(options) {
|
|
|
9301
10105
|
const config = loadConfig();
|
|
9302
10106
|
const dbWriteQueue = new AsyncTaskQueue();
|
|
9303
10107
|
const runDbWrite = (task) => dbWriteQueue.run(task);
|
|
10108
|
+
if (config?.priceOverrides && Object.keys(config.priceOverrides).length > 0) {
|
|
10109
|
+
const imported = importConfigPriceOverrides(options.db, config.priceOverrides);
|
|
10110
|
+
delete config.priceOverrides;
|
|
10111
|
+
saveConfig(config);
|
|
10112
|
+
loadPricingRuntime(options.db, config);
|
|
10113
|
+
if (imported.length > 0) {
|
|
10114
|
+
console.log(`[serve] migrated ${imported.length} config price override(s) into the registry: ${imported.join(", ")}`);
|
|
10115
|
+
}
|
|
10116
|
+
}
|
|
9304
10117
|
if (config == null || config.exchangeRate == null) {
|
|
9305
10118
|
const cacheAge = config?.exchangeRateCache ? Date.now() - config.exchangeRateCache.fetchedAt : Infinity;
|
|
9306
10119
|
if (cacheAge >= CACHE_TTL_MS) {
|
|
@@ -9350,11 +10163,11 @@ function serve(options) {
|
|
|
9350
10163
|
getDbWriteQueueStatus: () => dbWriteQueue.getStatus()
|
|
9351
10164
|
});
|
|
9352
10165
|
const webBuildDir = (() => {
|
|
9353
|
-
const prodDir =
|
|
9354
|
-
if (
|
|
9355
|
-
return
|
|
10166
|
+
const prodDir = join13(dirname6(fileURLToPath(import.meta.url)), "web");
|
|
10167
|
+
if (existsSync12(prodDir)) return prodDir;
|
|
10168
|
+
return join13(dirname6(fileURLToPath(import.meta.url)), "..", "..", "..", "web", "build");
|
|
9356
10169
|
})();
|
|
9357
|
-
if (!
|
|
10170
|
+
if (!existsSync12(webBuildDir)) {
|
|
9358
10171
|
console.error("Web dashboard not found. Reinstall the package: npm install -g @juliantanx/aiusage");
|
|
9359
10172
|
process.exit(1);
|
|
9360
10173
|
}
|
|
@@ -9364,19 +10177,19 @@ function serve(options) {
|
|
|
9364
10177
|
apiServer.emit("request", req, res);
|
|
9365
10178
|
return;
|
|
9366
10179
|
}
|
|
9367
|
-
if (
|
|
9368
|
-
let filePath =
|
|
10180
|
+
if (existsSync12(webBuildDir)) {
|
|
10181
|
+
let filePath = join13(webBuildDir, url.pathname);
|
|
9369
10182
|
try {
|
|
9370
|
-
if (
|
|
9371
|
-
filePath =
|
|
10183
|
+
if (statSync4(filePath).isDirectory()) {
|
|
10184
|
+
filePath = join13(filePath, "index.html");
|
|
9372
10185
|
}
|
|
9373
10186
|
} catch {
|
|
9374
10187
|
}
|
|
9375
|
-
if (!
|
|
9376
|
-
filePath =
|
|
10188
|
+
if (!existsSync12(filePath)) {
|
|
10189
|
+
filePath = join13(webBuildDir, "index.html");
|
|
9377
10190
|
}
|
|
9378
10191
|
try {
|
|
9379
|
-
const content =
|
|
10192
|
+
const content = readFileSync11(filePath);
|
|
9380
10193
|
const ext = extname2(filePath);
|
|
9381
10194
|
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
9382
10195
|
res.writeHead(200, { "Content-Type": contentType });
|
|
@@ -9558,9 +10371,9 @@ Consent recorded for schema v1.`
|
|
|
9558
10371
|
}
|
|
9559
10372
|
|
|
9560
10373
|
// src/commands/status.ts
|
|
9561
|
-
import { statSync as
|
|
10374
|
+
import { statSync as statSync5, existsSync as existsSync13 } from "fs";
|
|
9562
10375
|
import { hostname as hostname3 } from "os";
|
|
9563
|
-
import { join as
|
|
10376
|
+
import { join as join14 } from "path";
|
|
9564
10377
|
function formatFileSize(bytes) {
|
|
9565
10378
|
if (bytes < 1024) return `${bytes} B`;
|
|
9566
10379
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
@@ -9578,11 +10391,11 @@ function generateStatus(db) {
|
|
|
9578
10391
|
const schemaVersion = db.prepare("SELECT version FROM schema_version ORDER BY version DESC LIMIT 1").get()?.version ?? 0;
|
|
9579
10392
|
const tableCount = db.prepare("SELECT COUNT(*) as count FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'").get()?.count ?? 0;
|
|
9580
10393
|
const viewCount = db.prepare("SELECT COUNT(*) as count FROM sqlite_master WHERE type = 'view'").get()?.count ?? 0;
|
|
9581
|
-
const sizePath =
|
|
10394
|
+
const sizePath = join14(AIUSAGE_DIR, "cache.db");
|
|
9582
10395
|
let databaseSize = "0 B";
|
|
9583
|
-
if (
|
|
10396
|
+
if (existsSync13(sizePath)) {
|
|
9584
10397
|
try {
|
|
9585
|
-
const stat2 =
|
|
10398
|
+
const stat2 = statSync5(sizePath);
|
|
9586
10399
|
databaseSize = formatFileSize(stat2.size);
|
|
9587
10400
|
} catch {
|
|
9588
10401
|
}
|
|
@@ -9671,11 +10484,11 @@ function recalcPricing(db) {
|
|
|
9671
10484
|
).all(lastId, BATCH_SIZE);
|
|
9672
10485
|
if (records.length === 0) break;
|
|
9673
10486
|
for (const record of records) {
|
|
9674
|
-
|
|
10487
|
+
const model = record.tool === "qoder" ? normalizeQoderModel(record.model) : record.model;
|
|
10488
|
+
if (record.cost_source === "log" && record.cost > 0 && !hasUserPrice(db, model)) {
|
|
9675
10489
|
skippedCount++;
|
|
9676
10490
|
continue;
|
|
9677
10491
|
}
|
|
9678
|
-
const model = record.tool === "qoder" ? normalizeQoderModel(record.model) : record.model;
|
|
9679
10492
|
const provider = model !== record.model ? inferProvider(model) : record.provider;
|
|
9680
10493
|
const price = resolvePriceFromRegistry(db, model);
|
|
9681
10494
|
const newCost = price ? calculateCostForPrice(price, {
|
|
@@ -9803,10 +10616,10 @@ var SyncProgressReporter = class {
|
|
|
9803
10616
|
};
|
|
9804
10617
|
|
|
9805
10618
|
// src/commands/widget.ts
|
|
9806
|
-
import { existsSync as
|
|
10619
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
9807
10620
|
import { spawn, execSync as execSync2 } from "child_process";
|
|
9808
|
-
import { join as
|
|
9809
|
-
var WIDGET_PID_PATH =
|
|
10621
|
+
import { join as join15 } from "path";
|
|
10622
|
+
var WIDGET_PID_PATH = join15(AIUSAGE_DIR, "widget.pid");
|
|
9810
10623
|
async function launchWidget() {
|
|
9811
10624
|
if (isWidgetRunning()) {
|
|
9812
10625
|
console.log("aiusage widget is already running in the system tray.");
|
|
@@ -9828,10 +10641,10 @@ async function launchWidget() {
|
|
|
9828
10641
|
console.log("aiusage widget started.");
|
|
9829
10642
|
}
|
|
9830
10643
|
function isWidgetRunning() {
|
|
9831
|
-
if (!
|
|
10644
|
+
if (!existsSync14(WIDGET_PID_PATH)) return false;
|
|
9832
10645
|
let pid;
|
|
9833
10646
|
try {
|
|
9834
|
-
pid = parseInt(
|
|
10647
|
+
pid = parseInt(readFileSync12(WIDGET_PID_PATH, "utf-8").trim(), 10);
|
|
9835
10648
|
} catch {
|
|
9836
10649
|
return false;
|
|
9837
10650
|
}
|
|
@@ -9875,10 +10688,10 @@ function getDeviceName2() {
|
|
|
9875
10688
|
}
|
|
9876
10689
|
function getCliVersion2() {
|
|
9877
10690
|
try {
|
|
9878
|
-
const { readFileSync:
|
|
9879
|
-
const { join:
|
|
9880
|
-
const pkgPath =
|
|
9881
|
-
const pkg = JSON.parse(
|
|
10691
|
+
const { readFileSync: readFileSync14 } = __require("fs");
|
|
10692
|
+
const { join: join18 } = __require("path");
|
|
10693
|
+
const pkgPath = join18(__dirname, "../../package.json");
|
|
10694
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
9882
10695
|
return pkg.version || "0.0.0";
|
|
9883
10696
|
} catch {
|
|
9884
10697
|
return "0.0.0";
|
|
@@ -10047,8 +10860,8 @@ function formatTokens(value) {
|
|
|
10047
10860
|
return Number(value).toLocaleString();
|
|
10048
10861
|
}
|
|
10049
10862
|
function formatCost(value) {
|
|
10050
|
-
const
|
|
10051
|
-
return Number.isFinite(
|
|
10863
|
+
const num4 = Number(value);
|
|
10864
|
+
return Number.isFinite(num4) ? `$${num4.toFixed(4)}` : "$0.0000";
|
|
10052
10865
|
}
|
|
10053
10866
|
function formatUpdatedAt(value) {
|
|
10054
10867
|
const date = new Date(value);
|
|
@@ -10121,17 +10934,17 @@ async function runLeaderboardView(options = {}) {
|
|
|
10121
10934
|
|
|
10122
10935
|
// src/commands/menu.ts
|
|
10123
10936
|
import { createInterface } from "readline";
|
|
10124
|
-
import { spawn as spawn2, spawnSync, execSync as execSync4 } from "child_process";
|
|
10125
|
-
import { existsSync as
|
|
10126
|
-
import { join as
|
|
10937
|
+
import { spawn as spawn2, spawnSync as spawnSync2, execSync as execSync4 } from "child_process";
|
|
10938
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
10939
|
+
import { join as join16 } from "path";
|
|
10127
10940
|
var DEFAULT_PORT = 3847;
|
|
10128
|
-
var PORT_FILE2 =
|
|
10941
|
+
var PORT_FILE2 = join16(AIUSAGE_DIR, ".serve-port");
|
|
10129
10942
|
function clearScreen() {
|
|
10130
10943
|
process.stdout.write("\x1Bc");
|
|
10131
10944
|
}
|
|
10132
10945
|
function getPort() {
|
|
10133
|
-
if (
|
|
10134
|
-
const raw =
|
|
10946
|
+
if (existsSync15(PORT_FILE2)) {
|
|
10947
|
+
const raw = readFileSync13(PORT_FILE2, "utf-8").trim();
|
|
10135
10948
|
const n = parseInt(raw, 10);
|
|
10136
10949
|
if (!isNaN(n) && n > 0) return n;
|
|
10137
10950
|
}
|
|
@@ -10178,7 +10991,7 @@ function openBrowser2(url) {
|
|
|
10178
10991
|
}
|
|
10179
10992
|
function runCommand(args) {
|
|
10180
10993
|
try {
|
|
10181
|
-
|
|
10994
|
+
spawnSync2("aiusage", args, { stdio: "inherit" });
|
|
10182
10995
|
} catch {
|
|
10183
10996
|
}
|
|
10184
10997
|
}
|
|
@@ -10620,10 +11433,10 @@ ${dashboardStatusLine()}
|
|
|
10620
11433
|
}
|
|
10621
11434
|
|
|
10622
11435
|
// src/cli.ts
|
|
10623
|
-
import { join as
|
|
10624
|
-
var DB_PATH2 =
|
|
11436
|
+
import { join as join17 } from "path";
|
|
11437
|
+
var DB_PATH2 = join17(AIUSAGE_DIR, "cache.db");
|
|
10625
11438
|
var program = new Command();
|
|
10626
|
-
program.name("aiusage").version(true ? "1.5.
|
|
11439
|
+
program.name("aiusage").version(true ? "1.5.8" : "dev").description("CLI tool for AI usage statistics");
|
|
10627
11440
|
program.action(() => {
|
|
10628
11441
|
const unknownCommand = program.args.find((arg) => typeof arg === "string");
|
|
10629
11442
|
if (unknownCommand) {
|
|
@@ -10910,7 +11723,7 @@ function hasWidget() {
|
|
|
10910
11723
|
}
|
|
10911
11724
|
}
|
|
10912
11725
|
function buildPm2Config(includeWidget) {
|
|
10913
|
-
const wrapperPath =
|
|
11726
|
+
const wrapperPath = join17(AIUSAGE_DIR, "pm2-server.cjs");
|
|
10914
11727
|
writeFileSync7(wrapperPath, [
|
|
10915
11728
|
`// Auto-generated by aiusage pm2-start \u2014 do not edit`,
|
|
10916
11729
|
`const { execSync, spawn } = require('child_process')`,
|
|
@@ -10957,7 +11770,7 @@ ${apps.join(",\n")}
|
|
|
10957
11770
|
program.command("pm2-setup").description("Generate ecosystem.config.cjs for PM2 background services").option("--server-only", "Only include server, skip widget").action((options) => {
|
|
10958
11771
|
const includeWidget = options.serverOnly ? false : hasWidget();
|
|
10959
11772
|
const config = buildPm2Config(includeWidget);
|
|
10960
|
-
const configPath =
|
|
11773
|
+
const configPath = join17(AIUSAGE_DIR, "ecosystem.config.cjs");
|
|
10961
11774
|
writeFileSync7(configPath, config, "utf-8");
|
|
10962
11775
|
console.log(`Config created: ${configPath}`);
|
|
10963
11776
|
if (!includeWidget) {
|
|
@@ -10967,7 +11780,7 @@ program.command("pm2-setup").description("Generate ecosystem.config.cjs for PM2
|
|
|
10967
11780
|
program.command("pm2-start").description("Setup and start PM2 background services (requires pm2 installed globally)").option("--server-only", "Only start server, skip widget").action((options) => {
|
|
10968
11781
|
const includeWidget = options.serverOnly ? false : hasWidget();
|
|
10969
11782
|
const config = buildPm2Config(includeWidget);
|
|
10970
|
-
const configPath =
|
|
11783
|
+
const configPath = join17(AIUSAGE_DIR, "ecosystem.config.cjs");
|
|
10971
11784
|
writeFileSync7(configPath, config, "utf-8");
|
|
10972
11785
|
console.log(`Config: ${configPath}`);
|
|
10973
11786
|
try {
|