@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.
- package/dist/index.js +626 -113
- package/dist/web/_app/immutable/chunks/{ToolSelector.0iI1cXVj.js → ToolSelector.CO-QGau6.js} +1 -1
- package/dist/web/_app/immutable/chunks/entry.NbJb2eQR.js +3 -0
- package/dist/web/_app/immutable/chunks/{stores.DE60qEGb.js → stores.BYZRdBSl.js} +1 -1
- package/dist/web/_app/immutable/chunks/{stores.BSI_cVy6.js → stores.C6g63Ng7.js} +1 -1
- package/dist/web/_app/immutable/entry/{app.CSiP_kAD.js → app.CM2PdbpC.js} +2 -2
- package/dist/web/_app/immutable/entry/start.Cj8FkSB_.js +1 -0
- package/dist/web/_app/immutable/nodes/{0.dH2tW-7d.js → 0.DDrgmg6l.js} +1 -1
- package/dist/web/_app/immutable/nodes/{1.B8yHhyzy.js → 1.DTUl4JbV.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.DB5iERgm.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/chunks/entry.PMcg7HQL.js +0 -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 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 (
|
|
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 = {
|
|
@@ -1436,7 +1435,12 @@ function defaultCursorDbPath() {
|
|
|
1436
1435
|
return join3(xdgConfigHome, "Cursor", "User", "globalStorage", "state.vscdb");
|
|
1437
1436
|
}
|
|
1438
1437
|
function defaultKiloDbPath() {
|
|
1439
|
-
|
|
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
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
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:
|
|
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:
|
|
3918
|
-
const { join:
|
|
3919
|
-
const pkgPath =
|
|
3920
|
-
const pkg = JSON.parse(
|
|
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
|
-
|
|
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
|
|
6132
|
-
import { join as
|
|
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(
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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") &&
|
|
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") &&
|
|
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") &&
|
|
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") &&
|
|
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") &&
|
|
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") &&
|
|
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") &&
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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 =
|
|
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
|
|
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:
|
|
8454
|
-
const { join:
|
|
8455
|
-
const pkgPath =
|
|
8456
|
-
const pkg = JSON.parse(
|
|
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
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
8616
|
-
await mkdir(
|
|
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 =
|
|
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(
|
|
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:
|
|
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
|
|
8896
|
-
import { join as
|
|
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 =
|
|
9421
|
+
const watermarkPath = join11(AIUSAGE_DIR, "watermark.json");
|
|
8918
9422
|
let watermarkRemoved = false;
|
|
8919
|
-
if (
|
|
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:
|
|
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 =
|
|
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 =
|
|
9354
|
-
if (
|
|
9355
|
-
return
|
|
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 (!
|
|
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 (
|
|
9368
|
-
let filePath =
|
|
9880
|
+
if (existsSync11(webBuildDir)) {
|
|
9881
|
+
let filePath = join12(webBuildDir, url.pathname);
|
|
9369
9882
|
try {
|
|
9370
|
-
if (
|
|
9371
|
-
filePath =
|
|
9883
|
+
if (statSync4(filePath).isDirectory()) {
|
|
9884
|
+
filePath = join12(filePath, "index.html");
|
|
9372
9885
|
}
|
|
9373
9886
|
} catch {
|
|
9374
9887
|
}
|
|
9375
|
-
if (!
|
|
9376
|
-
filePath =
|
|
9888
|
+
if (!existsSync11(filePath)) {
|
|
9889
|
+
filePath = join12(webBuildDir, "index.html");
|
|
9377
9890
|
}
|
|
9378
9891
|
try {
|
|
9379
|
-
const content =
|
|
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
|
|
10074
|
+
import { statSync as statSync5, existsSync as existsSync12 } from "fs";
|
|
9562
10075
|
import { hostname as hostname3 } from "os";
|
|
9563
|
-
import { join as
|
|
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 =
|
|
10094
|
+
const sizePath = join13(AIUSAGE_DIR, "cache.db");
|
|
9582
10095
|
let databaseSize = "0 B";
|
|
9583
|
-
if (
|
|
10096
|
+
if (existsSync12(sizePath)) {
|
|
9584
10097
|
try {
|
|
9585
|
-
const stat2 =
|
|
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
|
-
|
|
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
|
|
10319
|
+
import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
|
|
9807
10320
|
import { spawn, execSync as execSync2 } from "child_process";
|
|
9808
|
-
import { join as
|
|
9809
|
-
var WIDGET_PID_PATH =
|
|
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 (!
|
|
10344
|
+
if (!existsSync13(WIDGET_PID_PATH)) return false;
|
|
9832
10345
|
let pid;
|
|
9833
10346
|
try {
|
|
9834
|
-
pid = parseInt(
|
|
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:
|
|
9879
|
-
const { join:
|
|
9880
|
-
const pkgPath =
|
|
9881
|
-
const pkg = JSON.parse(
|
|
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
|
|
10126
|
-
import { join as
|
|
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 =
|
|
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 (
|
|
10134
|
-
const raw =
|
|
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
|
-
|
|
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
|
|
10624
|
-
var DB_PATH2 =
|
|
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.
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 {
|