@juliantanx/aiusage 1.5.5 → 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/README.md +28 -1
- package/dist/index.js +701 -133
- 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.YDlXfj0w.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.RcQYJvro.js → app.CM2PdbpC.js} +2 -2
- package/dist/web/_app/immutable/entry/start.Cj8FkSB_.js +1 -0
- package/dist/web/_app/immutable/nodes/{0.DssNB1JP.js → 0.DDrgmg6l.js} +1 -1
- package/dist/web/_app/immutable/nodes/{1.D4HMlRNK.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.8-cT1ns9.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.BtjCrbiQ.js +0 -3
- package/dist/web/_app/immutable/entry/start.DAyKzWev.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) {
|
|
@@ -2709,7 +2803,7 @@ async function fetchLeaderboardStatus(serverUrl) {
|
|
|
2709
2803
|
|
|
2710
2804
|
// src/db/index.ts
|
|
2711
2805
|
import Database from "better-sqlite3";
|
|
2712
|
-
import { mkdirSync as mkdirSync4 } from "fs";
|
|
2806
|
+
import { mkdirSync as mkdirSync4, unlinkSync as unlinkSync2 } from "fs";
|
|
2713
2807
|
import { dirname as dirname3 } from "path";
|
|
2714
2808
|
|
|
2715
2809
|
// src/db/schema.ts
|
|
@@ -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
|
|
@@ -3694,11 +3811,31 @@ function initializeDatabase(db) {
|
|
|
3694
3811
|
runMigrations(db);
|
|
3695
3812
|
loadPricingRuntime(db, loadConfig());
|
|
3696
3813
|
}
|
|
3814
|
+
function removeCorruptedDb(path3) {
|
|
3815
|
+
for (const suffix of ["", "-shm", "-wal"]) {
|
|
3816
|
+
try {
|
|
3817
|
+
unlinkSync2(path3 + suffix);
|
|
3818
|
+
} catch {
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3697
3822
|
function createDatabase(path3) {
|
|
3698
3823
|
mkdirSync4(dirname3(path3), { recursive: true, mode: 448 });
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3824
|
+
try {
|
|
3825
|
+
const db = new Database(path3);
|
|
3826
|
+
initializeDatabase(db);
|
|
3827
|
+
return db;
|
|
3828
|
+
} catch (err) {
|
|
3829
|
+
const code = err.code;
|
|
3830
|
+
if (code === "SQLITE_CORRUPT" || code === "SQLITE_NOTADB") {
|
|
3831
|
+
console.warn(`Database corrupted, recreating: ${path3}`);
|
|
3832
|
+
removeCorruptedDb(path3);
|
|
3833
|
+
const db = new Database(path3);
|
|
3834
|
+
initializeDatabase(db);
|
|
3835
|
+
return db;
|
|
3836
|
+
}
|
|
3837
|
+
throw err;
|
|
3838
|
+
}
|
|
3702
3839
|
}
|
|
3703
3840
|
|
|
3704
3841
|
// src/db/tool-calls.ts
|
|
@@ -3894,10 +4031,10 @@ function getClientPlatform() {
|
|
|
3894
4031
|
}
|
|
3895
4032
|
function getCliVersion() {
|
|
3896
4033
|
try {
|
|
3897
|
-
const { readFileSync:
|
|
3898
|
-
const { join:
|
|
3899
|
-
const pkgPath =
|
|
3900
|
-
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"));
|
|
3901
4038
|
return pkg.version || "0.0.0";
|
|
3902
4039
|
} catch {
|
|
3903
4040
|
return "0.0.0";
|
|
@@ -4509,12 +4646,12 @@ async function recalcCosts(db, onProgress) {
|
|
|
4509
4646
|
const tx = db.transaction((batch) => {
|
|
4510
4647
|
for (const r of batch) {
|
|
4511
4648
|
processed++;
|
|
4512
|
-
|
|
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)) {
|
|
4513
4652
|
skipped++;
|
|
4514
4653
|
continue;
|
|
4515
4654
|
}
|
|
4516
|
-
const rawModel = r.tool === "qoder" ? normalizeQoderModel(r.model) : r.model;
|
|
4517
|
-
const model = rawModel === "unknown" ? r.tool === "qoder" ? "qoder-auto" : r.model : rawModel;
|
|
4518
4655
|
const provider = model !== r.model ? inferProvider(model) : r.provider;
|
|
4519
4656
|
const price = resolveCachedPrice(model);
|
|
4520
4657
|
const cost = price ? calculateCostForPrice(price, {
|
|
@@ -6108,8 +6245,8 @@ function createApiServer(db, options) {
|
|
|
6108
6245
|
|
|
6109
6246
|
// src/commands/parse.ts
|
|
6110
6247
|
import Database2 from "better-sqlite3";
|
|
6111
|
-
import { readFileSync as
|
|
6112
|
-
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";
|
|
6113
6250
|
import { hostname as hostname2 } from "os";
|
|
6114
6251
|
|
|
6115
6252
|
// src/watermark.ts
|
|
@@ -6139,7 +6276,8 @@ function defaultFileData() {
|
|
|
6139
6276
|
"pi": {},
|
|
6140
6277
|
"craft": {},
|
|
6141
6278
|
"droid": {},
|
|
6142
|
-
"zcode": {}
|
|
6279
|
+
"zcode": {},
|
|
6280
|
+
"trae": {}
|
|
6143
6281
|
};
|
|
6144
6282
|
}
|
|
6145
6283
|
var WatermarkManager = class {
|
|
@@ -6159,7 +6297,7 @@ var WatermarkManager = class {
|
|
|
6159
6297
|
if (parsed && typeof parsed === "object" && !("files" in parsed)) {
|
|
6160
6298
|
return { files: { ...defaultFileData(), ...parsed } };
|
|
6161
6299
|
}
|
|
6162
|
-
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 };
|
|
6163
6301
|
} catch {
|
|
6164
6302
|
return { files: defaultFileData() };
|
|
6165
6303
|
}
|
|
@@ -6240,6 +6378,12 @@ var WatermarkManager = class {
|
|
|
6240
6378
|
setZcodeToolCursor(cursor) {
|
|
6241
6379
|
this.data.zcodeTools = cursor;
|
|
6242
6380
|
}
|
|
6381
|
+
getTraeLastImported() {
|
|
6382
|
+
return this.data.trae ?? 0;
|
|
6383
|
+
}
|
|
6384
|
+
setTraeLastImported(ts3) {
|
|
6385
|
+
this.data.trae = ts3;
|
|
6386
|
+
}
|
|
6243
6387
|
};
|
|
6244
6388
|
|
|
6245
6389
|
// src/commands/parse-opencode.ts
|
|
@@ -6534,6 +6678,42 @@ function runParseQoder(db, options) {
|
|
|
6534
6678
|
}
|
|
6535
6679
|
|
|
6536
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
|
+
}
|
|
6537
6717
|
function runParseCursor(db, options) {
|
|
6538
6718
|
const { dbPath, device, deviceInstanceId, platform: platform5, now, cursor } = options;
|
|
6539
6719
|
const records = [];
|
|
@@ -7117,6 +7297,139 @@ function runParseZcode(db, options) {
|
|
|
7117
7297
|
};
|
|
7118
7298
|
}
|
|
7119
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
|
+
|
|
7120
7433
|
// src/commands/parse.ts
|
|
7121
7434
|
function extractCwdFromJson(data) {
|
|
7122
7435
|
if (typeof data.cwd === "string" && data.cwd) return data.cwd;
|
|
@@ -7160,6 +7473,7 @@ function extractSessionId2(filePath, tool) {
|
|
|
7160
7473
|
}
|
|
7161
7474
|
return "unknown";
|
|
7162
7475
|
}
|
|
7476
|
+
var CHARS_PER_TOKEN2 = 4;
|
|
7163
7477
|
function toNumber2(value) {
|
|
7164
7478
|
const n = Number(value);
|
|
7165
7479
|
return Number.isFinite(n) && n > 0 ? Math.floor(n) : 0;
|
|
@@ -7170,7 +7484,7 @@ function parseUiMessagesFile(options) {
|
|
|
7170
7484
|
const errors = [];
|
|
7171
7485
|
let messages;
|
|
7172
7486
|
try {
|
|
7173
|
-
const parsed = JSON.parse(
|
|
7487
|
+
const parsed = JSON.parse(readFileSync9(filePath, "utf-8"));
|
|
7174
7488
|
messages = Array.isArray(parsed) ? parsed : [];
|
|
7175
7489
|
} catch (e) {
|
|
7176
7490
|
return { records, errors: [`${filePath}: ${e instanceof Error ? e.message : e}`] };
|
|
@@ -7237,7 +7551,7 @@ function parseTimestamp2(value, fallback) {
|
|
|
7237
7551
|
}
|
|
7238
7552
|
function pathIsFile(filePath) {
|
|
7239
7553
|
try {
|
|
7240
|
-
return
|
|
7554
|
+
return statSync3(filePath).isFile();
|
|
7241
7555
|
} catch {
|
|
7242
7556
|
return false;
|
|
7243
7557
|
}
|
|
@@ -7248,10 +7562,13 @@ function parseKiroSessionFile(options) {
|
|
|
7248
7562
|
const errors = [];
|
|
7249
7563
|
let parsed;
|
|
7250
7564
|
try {
|
|
7251
|
-
parsed = JSON.parse(
|
|
7565
|
+
parsed = JSON.parse(readFileSync9(filePath, "utf-8"));
|
|
7252
7566
|
} catch (e) {
|
|
7253
7567
|
return { records, errors: [`${filePath}: ${e instanceof Error ? e.message : e}`] };
|
|
7254
7568
|
}
|
|
7569
|
+
if (Array.isArray(parsed?.history) && !parsed?.session_state) {
|
|
7570
|
+
return parseKiroWorkspaceSession({ filePath, parsed, device, deviceInstanceId, platform: platform5, now, exchangeRate });
|
|
7571
|
+
}
|
|
7255
7572
|
const turns = parsed?.session_state?.conversation_metadata?.user_turn_metadatas;
|
|
7256
7573
|
if (!Array.isArray(turns)) return { records, errors };
|
|
7257
7574
|
const sessionId = typeof parsed?.session_id === "string" && parsed.session_id ? parsed.session_id : extractSessionId2(filePath, "kiro");
|
|
@@ -7293,6 +7610,83 @@ function parseKiroSessionFile(options) {
|
|
|
7293
7610
|
}
|
|
7294
7611
|
return { records, errors };
|
|
7295
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
|
+
}
|
|
7296
7690
|
async function runParse(db, filterTool, options) {
|
|
7297
7691
|
const state = getState(AIUSAGE_DIR);
|
|
7298
7692
|
const config = loadConfig();
|
|
@@ -7300,7 +7694,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7300
7694
|
const device = config?.device || hostname2() || state?.deviceInstanceId?.slice(0, 8) || "unknown";
|
|
7301
7695
|
const deviceInstanceId = state?.deviceInstanceId ?? "unknown";
|
|
7302
7696
|
const devicePlatform = config?.platform;
|
|
7303
|
-
const watermarkPath =
|
|
7697
|
+
const watermarkPath = join8(AIUSAGE_DIR, "watermark.json");
|
|
7304
7698
|
const wm = new WatermarkManager(watermarkPath);
|
|
7305
7699
|
const toolPaths = discoverLogFiles();
|
|
7306
7700
|
const aggregator = new Aggregator();
|
|
@@ -7309,20 +7703,18 @@ async function runParse(db, filterTool, options) {
|
|
|
7309
7703
|
const errors = [];
|
|
7310
7704
|
const onProgress = options?.onProgress ?? (() => {
|
|
7311
7705
|
});
|
|
7312
|
-
const totalFiles = toolPaths.filter(({ tool }) => !filterTool || tool === filterTool).reduce((sum, { paths }) => sum + paths.length, 0);
|
|
7313
|
-
let fileIndex = 0;
|
|
7314
|
-
let currentTool;
|
|
7315
7706
|
for (const { tool, paths } of toolPaths) {
|
|
7316
7707
|
if (filterTool && tool !== filterTool) continue;
|
|
7317
|
-
|
|
7708
|
+
const toolTotal = paths.length;
|
|
7709
|
+
let toolIndex = 0;
|
|
7318
7710
|
for (const filePath of paths) {
|
|
7319
|
-
|
|
7711
|
+
toolIndex++;
|
|
7320
7712
|
try {
|
|
7321
|
-
const stat2 =
|
|
7713
|
+
const stat2 = statSync3(filePath);
|
|
7322
7714
|
const entry = wm.getEntry(tool, filePath);
|
|
7323
7715
|
const offset = entry?.offset ?? 0;
|
|
7324
7716
|
if (offset >= stat2.size) {
|
|
7325
|
-
onProgress({ phase: "Parsing logs", tool, current:
|
|
7717
|
+
onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
|
|
7326
7718
|
continue;
|
|
7327
7719
|
}
|
|
7328
7720
|
if ((tool === "roocode" || tool === "kilocode") && filePath.endsWith("ui_messages.json")) {
|
|
@@ -7346,7 +7738,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7346
7738
|
mtime: stat2.mtimeMs
|
|
7347
7739
|
});
|
|
7348
7740
|
wm.save();
|
|
7349
|
-
onProgress({ phase: "Parsing logs", tool, current:
|
|
7741
|
+
onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
|
|
7350
7742
|
continue;
|
|
7351
7743
|
}
|
|
7352
7744
|
if (tool === "kiro" && filePath.endsWith(".json")) {
|
|
@@ -7369,7 +7761,67 @@ async function runParse(db, filterTool, options) {
|
|
|
7369
7761
|
mtime: stat2.mtimeMs
|
|
7370
7762
|
});
|
|
7371
7763
|
wm.save();
|
|
7372
|
-
onProgress({ phase: "Parsing logs", tool, current:
|
|
7764
|
+
onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
|
|
7765
|
+
continue;
|
|
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 });
|
|
7373
7825
|
continue;
|
|
7374
7826
|
}
|
|
7375
7827
|
if (tool === "kelivo" && (filePath.endsWith("chats.json") || filePath.endsWith(".zip"))) {
|
|
@@ -7392,10 +7844,58 @@ async function runParse(db, filterTool, options) {
|
|
|
7392
7844
|
mtime: stat2.mtimeMs
|
|
7393
7845
|
});
|
|
7394
7846
|
wm.save();
|
|
7395
|
-
onProgress({ phase: "Parsing logs", tool, current:
|
|
7847
|
+
onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
|
|
7848
|
+
continue;
|
|
7849
|
+
}
|
|
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 });
|
|
7396
7896
|
continue;
|
|
7397
7897
|
}
|
|
7398
|
-
const content =
|
|
7898
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
7399
7899
|
const lines = content.split("\n");
|
|
7400
7900
|
let byteOffset = 0;
|
|
7401
7901
|
let fileCwd;
|
|
@@ -7467,14 +7967,14 @@ async function runParse(db, filterTool, options) {
|
|
|
7467
7967
|
mtime: stat2.mtimeMs
|
|
7468
7968
|
});
|
|
7469
7969
|
wm.save();
|
|
7470
|
-
onProgress({ phase: "Parsing logs", tool, current:
|
|
7970
|
+
onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
|
|
7471
7971
|
} catch (e) {
|
|
7472
7972
|
errors.push(`${filePath}: ${e instanceof Error ? e.message : e}`);
|
|
7473
7973
|
}
|
|
7474
7974
|
}
|
|
7475
7975
|
}
|
|
7476
7976
|
const openCodeDbPath = options?.openCodeDbPath ?? getDbPath("opencode") ?? "";
|
|
7477
|
-
if ((!filterTool || filterTool === "opencode") &&
|
|
7977
|
+
if ((!filterTool || filterTool === "opencode") && existsSync9(openCodeDbPath)) {
|
|
7478
7978
|
try {
|
|
7479
7979
|
const openCodeDb = new Database2(openCodeDbPath, { readonly: true });
|
|
7480
7980
|
try {
|
|
@@ -7505,7 +8005,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7505
8005
|
}
|
|
7506
8006
|
}
|
|
7507
8007
|
const hermesDbPath = options?.hermesDbPath ?? getDbPath("hermes") ?? "";
|
|
7508
|
-
if ((!filterTool || filterTool === "hermes") &&
|
|
8008
|
+
if ((!filterTool || filterTool === "hermes") && existsSync9(hermesDbPath)) {
|
|
7509
8009
|
try {
|
|
7510
8010
|
const hermesDb = new Database2(hermesDbPath, { readonly: true });
|
|
7511
8011
|
try {
|
|
@@ -7536,7 +8036,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7536
8036
|
}
|
|
7537
8037
|
}
|
|
7538
8038
|
const qoderDbPath = options?.qoderDbPath ?? getDbPath("qoder-db") ?? "";
|
|
7539
|
-
if ((!filterTool || filterTool === "qoder") &&
|
|
8039
|
+
if ((!filterTool || filterTool === "qoder") && existsSync9(qoderDbPath)) {
|
|
7540
8040
|
try {
|
|
7541
8041
|
const qoderDb = new Database2(qoderDbPath, { readonly: true });
|
|
7542
8042
|
try {
|
|
@@ -7567,7 +8067,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7567
8067
|
}
|
|
7568
8068
|
}
|
|
7569
8069
|
const cursorDbPath = options?.cursorDbPath ?? getDbPath("cursor") ?? "";
|
|
7570
|
-
if ((!filterTool || filterTool === "cursor") &&
|
|
8070
|
+
if ((!filterTool || filterTool === "cursor") && existsSync9(cursorDbPath)) {
|
|
7571
8071
|
try {
|
|
7572
8072
|
const cursorDb = new Database2(cursorDbPath, { readonly: true });
|
|
7573
8073
|
try {
|
|
@@ -7597,7 +8097,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7597
8097
|
}
|
|
7598
8098
|
}
|
|
7599
8099
|
const kiloDbPath = getDbPath("kilocode-db") ?? "";
|
|
7600
|
-
if ((!filterTool || filterTool === "kilocode") &&
|
|
8100
|
+
if ((!filterTool || filterTool === "kilocode") && existsSync9(kiloDbPath)) {
|
|
7601
8101
|
try {
|
|
7602
8102
|
const kiloDb = new Database2(kiloDbPath, { readonly: true });
|
|
7603
8103
|
try {
|
|
@@ -7637,7 +8137,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7637
8137
|
}
|
|
7638
8138
|
}
|
|
7639
8139
|
const gooseDbPath = getDbPath("goose") ?? "";
|
|
7640
|
-
if ((!filterTool || filterTool === "goose") &&
|
|
8140
|
+
if ((!filterTool || filterTool === "goose") && existsSync9(gooseDbPath)) {
|
|
7641
8141
|
try {
|
|
7642
8142
|
const gooseDb = new Database2(gooseDbPath, { readonly: true });
|
|
7643
8143
|
try {
|
|
@@ -7695,7 +8195,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7695
8195
|
}
|
|
7696
8196
|
}
|
|
7697
8197
|
const zedDbPath = getDbPath("zed") ?? "";
|
|
7698
|
-
if ((!filterTool || filterTool === "zed") &&
|
|
8198
|
+
if ((!filterTool || filterTool === "zed") && existsSync9(zedDbPath)) {
|
|
7699
8199
|
try {
|
|
7700
8200
|
const zedDb = new Database2(zedDbPath, { readonly: true });
|
|
7701
8201
|
try {
|
|
@@ -7725,7 +8225,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7725
8225
|
}
|
|
7726
8226
|
if (!filterTool || filterTool === "zcode") {
|
|
7727
8227
|
const zcodeDbPath = getDbPath("zcode") ?? "";
|
|
7728
|
-
if (
|
|
8228
|
+
if (existsSync9(zcodeDbPath)) {
|
|
7729
8229
|
try {
|
|
7730
8230
|
const zcodeDb = new Database2(zcodeDbPath, { readonly: true });
|
|
7731
8231
|
try {
|
|
@@ -7762,6 +8262,28 @@ async function runParse(db, filterTool, options) {
|
|
|
7762
8262
|
}
|
|
7763
8263
|
}
|
|
7764
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
|
+
}
|
|
7765
8287
|
if (deviceInstanceId !== "unknown") {
|
|
7766
8288
|
db.prepare(
|
|
7767
8289
|
`UPDATE records SET device_instance_id = ?, device = ? WHERE device_instance_id = 'unknown'`
|
|
@@ -7808,7 +8330,7 @@ function backfillHermesSourceFiles(db) {
|
|
|
7808
8330
|
const dbPath = rows[0].source_file;
|
|
7809
8331
|
let titleMap = /* @__PURE__ */ new Map();
|
|
7810
8332
|
try {
|
|
7811
|
-
if (
|
|
8333
|
+
if (existsSync9(dbPath)) {
|
|
7812
8334
|
const hermesDb = new Database2(dbPath, { readonly: true });
|
|
7813
8335
|
try {
|
|
7814
8336
|
const sessions = hermesDb.prepare(
|
|
@@ -7889,7 +8411,7 @@ function backfillCodexModels(db) {
|
|
|
7889
8411
|
);
|
|
7890
8412
|
for (const [sourceFile, fileRows] of byFile) {
|
|
7891
8413
|
try {
|
|
7892
|
-
const content =
|
|
8414
|
+
const content = readFileSync9(sourceFile, "utf-8");
|
|
7893
8415
|
const lines = content.split("\n");
|
|
7894
8416
|
const offsetToModel = /* @__PURE__ */ new Map();
|
|
7895
8417
|
let currentModel = "";
|
|
@@ -7937,7 +8459,7 @@ function backfillMissingToolCalls(db, exchangeRate) {
|
|
|
7937
8459
|
}
|
|
7938
8460
|
for (const [sourceFile, fileRows] of rowsByFile) {
|
|
7939
8461
|
try {
|
|
7940
|
-
const content =
|
|
8462
|
+
const content = readFileSync9(sourceFile, "utf-8");
|
|
7941
8463
|
const lines = content.split("\n");
|
|
7942
8464
|
const aggregator = new Aggregator();
|
|
7943
8465
|
const recordIdByOffset = /* @__PURE__ */ new Map();
|
|
@@ -7999,7 +8521,7 @@ function deriveSessionId(tool, sourceFile) {
|
|
|
7999
8521
|
}
|
|
8000
8522
|
|
|
8001
8523
|
// src/commands/sync.ts
|
|
8002
|
-
import { join as
|
|
8524
|
+
import { join as join10 } from "path";
|
|
8003
8525
|
|
|
8004
8526
|
// src/db/synced-records.ts
|
|
8005
8527
|
function insertSyncedRecord(db, record) {
|
|
@@ -8432,10 +8954,10 @@ async function cloudClear() {
|
|
|
8432
8954
|
}
|
|
8433
8955
|
function getClientVersion() {
|
|
8434
8956
|
try {
|
|
8435
|
-
const { readFileSync:
|
|
8436
|
-
const { join:
|
|
8437
|
-
const pkgPath =
|
|
8438
|
-
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"));
|
|
8439
8961
|
return pkg.version || "0.0.0";
|
|
8440
8962
|
} catch {
|
|
8441
8963
|
return "0.0.0";
|
|
@@ -8526,7 +9048,7 @@ var CloudSyncOrchestrator = class {
|
|
|
8526
9048
|
import { execFile } from "child_process";
|
|
8527
9049
|
import { promisify } from "util";
|
|
8528
9050
|
import { readFile, writeFile, mkdir, readdir, stat, unlink, rm } from "fs/promises";
|
|
8529
|
-
import { join as
|
|
9051
|
+
import { join as join9 } from "path";
|
|
8530
9052
|
var exec = promisify(execFile);
|
|
8531
9053
|
var GitSyncBackend = class _GitSyncBackend {
|
|
8532
9054
|
repo;
|
|
@@ -8538,7 +9060,7 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
8538
9060
|
this.repo = config.repo;
|
|
8539
9061
|
this.token = config.token;
|
|
8540
9062
|
this.cacheDir = config.cacheDir;
|
|
8541
|
-
this.dataDir =
|
|
9063
|
+
this.dataDir = join9(config.cacheDir, "data");
|
|
8542
9064
|
this.branch = config.branch ?? "main";
|
|
8543
9065
|
}
|
|
8544
9066
|
get remoteUrl() {
|
|
@@ -8570,7 +9092,7 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
8570
9092
|
/** Clone or pull the repo to get latest remote state */
|
|
8571
9093
|
async prepare() {
|
|
8572
9094
|
try {
|
|
8573
|
-
await stat(
|
|
9095
|
+
await stat(join9(this.cacheDir, ".git"));
|
|
8574
9096
|
await this.git(["fetch", "origin", this.branch, "--depth=1"]);
|
|
8575
9097
|
await this.git(["reset", "--hard", `origin/${this.branch}`]);
|
|
8576
9098
|
} catch {
|
|
@@ -8587,15 +9109,15 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
8587
9109
|
}
|
|
8588
9110
|
async readFile(path3) {
|
|
8589
9111
|
try {
|
|
8590
|
-
const fullPath =
|
|
9112
|
+
const fullPath = join9(this.dataDir, path3);
|
|
8591
9113
|
return await readFile(fullPath, "utf-8");
|
|
8592
9114
|
} catch {
|
|
8593
9115
|
return null;
|
|
8594
9116
|
}
|
|
8595
9117
|
}
|
|
8596
9118
|
async writeFile(path3, content) {
|
|
8597
|
-
const fullPath =
|
|
8598
|
-
await mkdir(
|
|
9119
|
+
const fullPath = join9(this.dataDir, path3);
|
|
9120
|
+
await mkdir(join9(fullPath, ".."), { recursive: true });
|
|
8599
9121
|
await writeFile(fullPath, content, "utf-8");
|
|
8600
9122
|
}
|
|
8601
9123
|
async listFiles() {
|
|
@@ -8606,7 +9128,7 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
8606
9128
|
}
|
|
8607
9129
|
}
|
|
8608
9130
|
async deleteFile(path3) {
|
|
8609
|
-
const fullPath =
|
|
9131
|
+
const fullPath = join9(this.dataDir, path3);
|
|
8610
9132
|
try {
|
|
8611
9133
|
await unlink(fullPath);
|
|
8612
9134
|
} catch {
|
|
@@ -8645,7 +9167,7 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
8645
9167
|
for (const entry of entries) {
|
|
8646
9168
|
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
8647
9169
|
if (entry.isDirectory()) {
|
|
8648
|
-
files.push(...await this.walkDir(
|
|
9170
|
+
files.push(...await this.walkDir(join9(dir, entry.name), relPath));
|
|
8649
9171
|
} else if (entry.name.endsWith(".ndjson")) {
|
|
8650
9172
|
files.push(relPath);
|
|
8651
9173
|
}
|
|
@@ -8779,7 +9301,7 @@ function createBackend(config) {
|
|
|
8779
9301
|
return new GitSyncBackend({
|
|
8780
9302
|
repo: sync.repo,
|
|
8781
9303
|
token,
|
|
8782
|
-
cacheDir:
|
|
9304
|
+
cacheDir: join10(AIUSAGE_DIR, "sync-repo")
|
|
8783
9305
|
});
|
|
8784
9306
|
}
|
|
8785
9307
|
if (sync.backend === "s3") {
|
|
@@ -8874,8 +9396,8 @@ async function runSync(db, options) {
|
|
|
8874
9396
|
}
|
|
8875
9397
|
|
|
8876
9398
|
// src/commands/clean.ts
|
|
8877
|
-
import { unlinkSync as
|
|
8878
|
-
import { join as
|
|
9399
|
+
import { unlinkSync as unlinkSync3, existsSync as existsSync10 } from "fs";
|
|
9400
|
+
import { join as join11 } from "path";
|
|
8879
9401
|
function cleanOldData(db, days) {
|
|
8880
9402
|
const cutoff = Date.now() - days * 864e5;
|
|
8881
9403
|
const recordsResult = db.prepare("DELETE FROM records WHERE ts < ?").run(cutoff);
|
|
@@ -8896,10 +9418,10 @@ function cleanAll(db) {
|
|
|
8896
9418
|
const syncedResult = db.prepare("DELETE FROM synced_records").run();
|
|
8897
9419
|
const syncStateResult = db.prepare("DELETE FROM sync_record_state").run();
|
|
8898
9420
|
const tombstonesResult = db.prepare("DELETE FROM sync_tombstones").run();
|
|
8899
|
-
const watermarkPath =
|
|
9421
|
+
const watermarkPath = join11(AIUSAGE_DIR, "watermark.json");
|
|
8900
9422
|
let watermarkRemoved = false;
|
|
8901
|
-
if (
|
|
8902
|
-
|
|
9423
|
+
if (existsSync10(watermarkPath)) {
|
|
9424
|
+
unlinkSync3(watermarkPath);
|
|
8903
9425
|
watermarkRemoved = true;
|
|
8904
9426
|
}
|
|
8905
9427
|
return {
|
|
@@ -8933,7 +9455,7 @@ function createBackend2(config) {
|
|
|
8933
9455
|
return new GitSyncBackend({
|
|
8934
9456
|
repo: config.sync.repo,
|
|
8935
9457
|
token,
|
|
8936
|
-
cacheDir:
|
|
9458
|
+
cacheDir: join11(AIUSAGE_DIR, "sync-repo")
|
|
8937
9459
|
});
|
|
8938
9460
|
}
|
|
8939
9461
|
if (config.sync.backend === "s3") {
|
|
@@ -9266,7 +9788,7 @@ var RuntimeSettingsController = class {
|
|
|
9266
9788
|
|
|
9267
9789
|
// src/commands/serve.ts
|
|
9268
9790
|
var MAX_PORT_ATTEMPTS = 10;
|
|
9269
|
-
var PORT_FILE =
|
|
9791
|
+
var PORT_FILE = join12(AIUSAGE_DIR, ".serve-port");
|
|
9270
9792
|
var MIME_TYPES = {
|
|
9271
9793
|
".html": "text/html",
|
|
9272
9794
|
".js": "application/javascript",
|
|
@@ -9283,6 +9805,15 @@ function serve(options) {
|
|
|
9283
9805
|
const config = loadConfig();
|
|
9284
9806
|
const dbWriteQueue = new AsyncTaskQueue();
|
|
9285
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
|
+
}
|
|
9286
9817
|
if (config == null || config.exchangeRate == null) {
|
|
9287
9818
|
const cacheAge = config?.exchangeRateCache ? Date.now() - config.exchangeRateCache.fetchedAt : Infinity;
|
|
9288
9819
|
if (cacheAge >= CACHE_TTL_MS) {
|
|
@@ -9316,7 +9847,10 @@ function serve(options) {
|
|
|
9316
9847
|
onSyncScheduleChanged: (ts3) => syncRuntime.setNextSyncAt(ts3)
|
|
9317
9848
|
});
|
|
9318
9849
|
runtimeSettings.start();
|
|
9319
|
-
|
|
9850
|
+
console.log("[serve] parsing logs...");
|
|
9851
|
+
runDbWrite(() => runParse(options.db)).then((result) => {
|
|
9852
|
+
console.log(`[serve] parsed ${result.parsedCount} records, ${result.toolCallCount} tool calls.`);
|
|
9853
|
+
}).catch((err) => {
|
|
9320
9854
|
console.error("[serve] initial parse failed:", err);
|
|
9321
9855
|
});
|
|
9322
9856
|
const apiServer = createApiServer(options.db, {
|
|
@@ -9329,11 +9863,11 @@ function serve(options) {
|
|
|
9329
9863
|
getDbWriteQueueStatus: () => dbWriteQueue.getStatus()
|
|
9330
9864
|
});
|
|
9331
9865
|
const webBuildDir = (() => {
|
|
9332
|
-
const prodDir =
|
|
9333
|
-
if (
|
|
9334
|
-
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");
|
|
9335
9869
|
})();
|
|
9336
|
-
if (!
|
|
9870
|
+
if (!existsSync11(webBuildDir)) {
|
|
9337
9871
|
console.error("Web dashboard not found. Reinstall the package: npm install -g @juliantanx/aiusage");
|
|
9338
9872
|
process.exit(1);
|
|
9339
9873
|
}
|
|
@@ -9343,19 +9877,19 @@ function serve(options) {
|
|
|
9343
9877
|
apiServer.emit("request", req, res);
|
|
9344
9878
|
return;
|
|
9345
9879
|
}
|
|
9346
|
-
if (
|
|
9347
|
-
let filePath =
|
|
9880
|
+
if (existsSync11(webBuildDir)) {
|
|
9881
|
+
let filePath = join12(webBuildDir, url.pathname);
|
|
9348
9882
|
try {
|
|
9349
|
-
if (
|
|
9350
|
-
filePath =
|
|
9883
|
+
if (statSync4(filePath).isDirectory()) {
|
|
9884
|
+
filePath = join12(filePath, "index.html");
|
|
9351
9885
|
}
|
|
9352
9886
|
} catch {
|
|
9353
9887
|
}
|
|
9354
|
-
if (!
|
|
9355
|
-
filePath =
|
|
9888
|
+
if (!existsSync11(filePath)) {
|
|
9889
|
+
filePath = join12(webBuildDir, "index.html");
|
|
9356
9890
|
}
|
|
9357
9891
|
try {
|
|
9358
|
-
const content =
|
|
9892
|
+
const content = readFileSync10(filePath);
|
|
9359
9893
|
const ext = extname2(filePath);
|
|
9360
9894
|
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
9361
9895
|
res.writeHead(200, { "Content-Type": contentType });
|
|
@@ -9393,7 +9927,7 @@ function serve(options) {
|
|
|
9393
9927
|
listenOnPort(options.port);
|
|
9394
9928
|
const cleanup = () => {
|
|
9395
9929
|
try {
|
|
9396
|
-
|
|
9930
|
+
unlinkSync4(PORT_FILE);
|
|
9397
9931
|
} catch {
|
|
9398
9932
|
}
|
|
9399
9933
|
};
|
|
@@ -9537,9 +10071,9 @@ Consent recorded for schema v1.`
|
|
|
9537
10071
|
}
|
|
9538
10072
|
|
|
9539
10073
|
// src/commands/status.ts
|
|
9540
|
-
import { statSync as
|
|
10074
|
+
import { statSync as statSync5, existsSync as existsSync12 } from "fs";
|
|
9541
10075
|
import { hostname as hostname3 } from "os";
|
|
9542
|
-
import { join as
|
|
10076
|
+
import { join as join13 } from "path";
|
|
9543
10077
|
function formatFileSize(bytes) {
|
|
9544
10078
|
if (bytes < 1024) return `${bytes} B`;
|
|
9545
10079
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
@@ -9557,11 +10091,11 @@ function generateStatus(db) {
|
|
|
9557
10091
|
const schemaVersion = db.prepare("SELECT version FROM schema_version ORDER BY version DESC LIMIT 1").get()?.version ?? 0;
|
|
9558
10092
|
const tableCount = db.prepare("SELECT COUNT(*) as count FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'").get()?.count ?? 0;
|
|
9559
10093
|
const viewCount = db.prepare("SELECT COUNT(*) as count FROM sqlite_master WHERE type = 'view'").get()?.count ?? 0;
|
|
9560
|
-
const sizePath =
|
|
10094
|
+
const sizePath = join13(AIUSAGE_DIR, "cache.db");
|
|
9561
10095
|
let databaseSize = "0 B";
|
|
9562
|
-
if (
|
|
10096
|
+
if (existsSync12(sizePath)) {
|
|
9563
10097
|
try {
|
|
9564
|
-
const stat2 =
|
|
10098
|
+
const stat2 = statSync5(sizePath);
|
|
9565
10099
|
databaseSize = formatFileSize(stat2.size);
|
|
9566
10100
|
} catch {
|
|
9567
10101
|
}
|
|
@@ -9650,11 +10184,11 @@ function recalcPricing(db) {
|
|
|
9650
10184
|
).all(lastId, BATCH_SIZE);
|
|
9651
10185
|
if (records.length === 0) break;
|
|
9652
10186
|
for (const record of records) {
|
|
9653
|
-
|
|
10187
|
+
const model = record.tool === "qoder" ? normalizeQoderModel(record.model) : record.model;
|
|
10188
|
+
if (record.cost_source === "log" && record.cost > 0 && !hasUserPrice(db, model)) {
|
|
9654
10189
|
skippedCount++;
|
|
9655
10190
|
continue;
|
|
9656
10191
|
}
|
|
9657
|
-
const model = record.tool === "qoder" ? normalizeQoderModel(record.model) : record.model;
|
|
9658
10192
|
const provider = model !== record.model ? inferProvider(model) : record.provider;
|
|
9659
10193
|
const price = resolvePriceFromRegistry(db, model);
|
|
9660
10194
|
const newCost = price ? calculateCostForPrice(price, {
|
|
@@ -9685,15 +10219,40 @@ function formatBar(ratio, width = 20) {
|
|
|
9685
10219
|
var ProgressReporter = class {
|
|
9686
10220
|
lastWrite = 0;
|
|
9687
10221
|
lastLine = "";
|
|
10222
|
+
lastInfo = null;
|
|
10223
|
+
currentTool;
|
|
10224
|
+
toolRecords = /* @__PURE__ */ new Map();
|
|
10225
|
+
recordsAtToolStart = 0;
|
|
9688
10226
|
update(info) {
|
|
9689
10227
|
if (!isTTY) return;
|
|
10228
|
+
if (info.tool && info.tool !== this.currentTool) {
|
|
10229
|
+
if (this.currentTool && this.lastInfo) {
|
|
10230
|
+
const toolRecordCount = this.lastInfo.records - this.recordsAtToolStart;
|
|
10231
|
+
this.toolRecords.set(this.currentTool, toolRecordCount);
|
|
10232
|
+
if (toolRecordCount > 0) {
|
|
10233
|
+
this.writeLine(this.lastInfo);
|
|
10234
|
+
process.stderr.write("\n");
|
|
10235
|
+
} else {
|
|
10236
|
+
process.stderr.write("\r\x1B[K");
|
|
10237
|
+
}
|
|
10238
|
+
this.lastLine = "";
|
|
10239
|
+
}
|
|
10240
|
+
this.currentTool = info.tool;
|
|
10241
|
+
this.recordsAtToolStart = info.records;
|
|
10242
|
+
}
|
|
10243
|
+
this.lastInfo = info;
|
|
9690
10244
|
const now = Date.now();
|
|
9691
|
-
|
|
10245
|
+
const isComplete = info.total > 0 && info.current >= info.total;
|
|
10246
|
+
if (!isComplete && now - this.lastWrite < MIN_INTERVAL_MS) return;
|
|
9692
10247
|
this.lastWrite = now;
|
|
10248
|
+
this.writeLine(info);
|
|
10249
|
+
}
|
|
10250
|
+
writeLine(info) {
|
|
9693
10251
|
const pct = info.total > 0 ? info.current / info.total * 100 : 0;
|
|
9694
10252
|
const bar = formatBar(info.total > 0 ? info.current / info.total : 0);
|
|
9695
10253
|
const toolLabel = info.tool ? ` [${info.tool}]` : "";
|
|
9696
|
-
const
|
|
10254
|
+
const toolRecordCount = info.records - this.recordsAtToolStart;
|
|
10255
|
+
const line = `\r${info.phase}${toolLabel} ${bar} ${pct.toFixed(1)}% ${info.current}/${info.total} records: ${toolRecordCount} `;
|
|
9697
10256
|
if (line !== this.lastLine) {
|
|
9698
10257
|
process.stderr.write(line);
|
|
9699
10258
|
this.lastLine = line;
|
|
@@ -9701,7 +10260,16 @@ var ProgressReporter = class {
|
|
|
9701
10260
|
}
|
|
9702
10261
|
done() {
|
|
9703
10262
|
if (!isTTY) return;
|
|
9704
|
-
|
|
10263
|
+
if (this.lastInfo && this.currentTool) {
|
|
10264
|
+
const toolRecordCount = this.lastInfo.records - this.recordsAtToolStart;
|
|
10265
|
+
this.toolRecords.set(this.currentTool, toolRecordCount);
|
|
10266
|
+
if (toolRecordCount > 0) {
|
|
10267
|
+
this.writeLine(this.lastInfo);
|
|
10268
|
+
process.stderr.write("\n");
|
|
10269
|
+
} else {
|
|
10270
|
+
process.stderr.write("\r\x1B[K");
|
|
10271
|
+
}
|
|
10272
|
+
}
|
|
9705
10273
|
}
|
|
9706
10274
|
};
|
|
9707
10275
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
@@ -9748,10 +10316,10 @@ var SyncProgressReporter = class {
|
|
|
9748
10316
|
};
|
|
9749
10317
|
|
|
9750
10318
|
// src/commands/widget.ts
|
|
9751
|
-
import { existsSync as
|
|
10319
|
+
import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
|
|
9752
10320
|
import { spawn, execSync as execSync2 } from "child_process";
|
|
9753
|
-
import { join as
|
|
9754
|
-
var WIDGET_PID_PATH =
|
|
10321
|
+
import { join as join14 } from "path";
|
|
10322
|
+
var WIDGET_PID_PATH = join14(AIUSAGE_DIR, "widget.pid");
|
|
9755
10323
|
async function launchWidget() {
|
|
9756
10324
|
if (isWidgetRunning()) {
|
|
9757
10325
|
console.log("aiusage widget is already running in the system tray.");
|
|
@@ -9773,10 +10341,10 @@ async function launchWidget() {
|
|
|
9773
10341
|
console.log("aiusage widget started.");
|
|
9774
10342
|
}
|
|
9775
10343
|
function isWidgetRunning() {
|
|
9776
|
-
if (!
|
|
10344
|
+
if (!existsSync13(WIDGET_PID_PATH)) return false;
|
|
9777
10345
|
let pid;
|
|
9778
10346
|
try {
|
|
9779
|
-
pid = parseInt(
|
|
10347
|
+
pid = parseInt(readFileSync11(WIDGET_PID_PATH, "utf-8").trim(), 10);
|
|
9780
10348
|
} catch {
|
|
9781
10349
|
return false;
|
|
9782
10350
|
}
|
|
@@ -9820,10 +10388,10 @@ function getDeviceName2() {
|
|
|
9820
10388
|
}
|
|
9821
10389
|
function getCliVersion2() {
|
|
9822
10390
|
try {
|
|
9823
|
-
const { readFileSync:
|
|
9824
|
-
const { join:
|
|
9825
|
-
const pkgPath =
|
|
9826
|
-
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"));
|
|
9827
10395
|
return pkg.version || "0.0.0";
|
|
9828
10396
|
} catch {
|
|
9829
10397
|
return "0.0.0";
|
|
@@ -10066,17 +10634,17 @@ async function runLeaderboardView(options = {}) {
|
|
|
10066
10634
|
|
|
10067
10635
|
// src/commands/menu.ts
|
|
10068
10636
|
import { createInterface } from "readline";
|
|
10069
|
-
import { spawn as spawn2, spawnSync, execSync as execSync4 } from "child_process";
|
|
10070
|
-
import { existsSync as
|
|
10071
|
-
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";
|
|
10072
10640
|
var DEFAULT_PORT = 3847;
|
|
10073
|
-
var PORT_FILE2 =
|
|
10641
|
+
var PORT_FILE2 = join15(AIUSAGE_DIR, ".serve-port");
|
|
10074
10642
|
function clearScreen() {
|
|
10075
10643
|
process.stdout.write("\x1Bc");
|
|
10076
10644
|
}
|
|
10077
10645
|
function getPort() {
|
|
10078
|
-
if (
|
|
10079
|
-
const raw =
|
|
10646
|
+
if (existsSync14(PORT_FILE2)) {
|
|
10647
|
+
const raw = readFileSync12(PORT_FILE2, "utf-8").trim();
|
|
10080
10648
|
const n = parseInt(raw, 10);
|
|
10081
10649
|
if (!isNaN(n) && n > 0) return n;
|
|
10082
10650
|
}
|
|
@@ -10123,7 +10691,7 @@ function openBrowser2(url) {
|
|
|
10123
10691
|
}
|
|
10124
10692
|
function runCommand(args) {
|
|
10125
10693
|
try {
|
|
10126
|
-
|
|
10694
|
+
spawnSync2("aiusage", args, { stdio: "inherit" });
|
|
10127
10695
|
} catch {
|
|
10128
10696
|
}
|
|
10129
10697
|
}
|
|
@@ -10565,10 +11133,10 @@ ${dashboardStatusLine()}
|
|
|
10565
11133
|
}
|
|
10566
11134
|
|
|
10567
11135
|
// src/cli.ts
|
|
10568
|
-
import { join as
|
|
10569
|
-
var DB_PATH2 =
|
|
11136
|
+
import { join as join16 } from "path";
|
|
11137
|
+
var DB_PATH2 = join16(AIUSAGE_DIR, "cache.db");
|
|
10570
11138
|
var program = new Command();
|
|
10571
|
-
program.name("aiusage").version(true ? "1.5.
|
|
11139
|
+
program.name("aiusage").version(true ? "1.5.7" : "dev").description("CLI tool for AI usage statistics");
|
|
10572
11140
|
program.action(() => {
|
|
10573
11141
|
const unknownCommand = program.args.find((arg) => typeof arg === "string");
|
|
10574
11142
|
if (unknownCommand) {
|
|
@@ -10855,7 +11423,7 @@ function hasWidget() {
|
|
|
10855
11423
|
}
|
|
10856
11424
|
}
|
|
10857
11425
|
function buildPm2Config(includeWidget) {
|
|
10858
|
-
const wrapperPath =
|
|
11426
|
+
const wrapperPath = join16(AIUSAGE_DIR, "pm2-server.cjs");
|
|
10859
11427
|
writeFileSync7(wrapperPath, [
|
|
10860
11428
|
`// Auto-generated by aiusage pm2-start \u2014 do not edit`,
|
|
10861
11429
|
`const { execSync, spawn } = require('child_process')`,
|
|
@@ -10902,7 +11470,7 @@ ${apps.join(",\n")}
|
|
|
10902
11470
|
program.command("pm2-setup").description("Generate ecosystem.config.cjs for PM2 background services").option("--server-only", "Only include server, skip widget").action((options) => {
|
|
10903
11471
|
const includeWidget = options.serverOnly ? false : hasWidget();
|
|
10904
11472
|
const config = buildPm2Config(includeWidget);
|
|
10905
|
-
const configPath =
|
|
11473
|
+
const configPath = join16(AIUSAGE_DIR, "ecosystem.config.cjs");
|
|
10906
11474
|
writeFileSync7(configPath, config, "utf-8");
|
|
10907
11475
|
console.log(`Config created: ${configPath}`);
|
|
10908
11476
|
if (!includeWidget) {
|
|
@@ -10912,7 +11480,7 @@ program.command("pm2-setup").description("Generate ecosystem.config.cjs for PM2
|
|
|
10912
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) => {
|
|
10913
11481
|
const includeWidget = options.serverOnly ? false : hasWidget();
|
|
10914
11482
|
const config = buildPm2Config(includeWidget);
|
|
10915
|
-
const configPath =
|
|
11483
|
+
const configPath = join16(AIUSAGE_DIR, "ecosystem.config.cjs");
|
|
10916
11484
|
writeFileSync7(configPath, config, "utf-8");
|
|
10917
11485
|
console.log(`Config: ${configPath}`);
|
|
10918
11486
|
try {
|