@juliantanx/aiusage 1.5.7 → 1.5.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +414 -134
- package/dist/web/_app/immutable/chunks/entry.sjMyScA0.js +3 -0
- package/dist/web/_app/immutable/chunks/{stores.BYZRdBSl.js → stores._Kt2_hfd.js} +1 -1
- package/dist/web/_app/immutable/entry/{app.CM2PdbpC.js → app.BQXavg_o.js} +2 -2
- package/dist/web/_app/immutable/entry/start.DQmY2FUm.js +1 -0
- package/dist/web/_app/immutable/nodes/{0.DDrgmg6l.js → 0.Z9rfrT26.js} +1 -1
- package/dist/web/_app/immutable/nodes/{1.DTUl4JbV.js → 1.Cr8pWYrd.js} +1 -1
- package/dist/web/_app/immutable/nodes/{11.DB5iERgm.js → 11.BTInmTls.js} +1 -1
- package/dist/web/_app/immutable/nodes/{2.DLTFcQ9L.js → 2.CB0PnCo0.js} +3 -3
- package/dist/web/_app/immutable/nodes/{6.C-WRTBwg.js → 6.CFsn5eeD.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.NbJb2eQR.js +0 -3
- package/dist/web/_app/immutable/entry/start.Cj8FkSB_.js +0 -1
package/dist/index.js
CHANGED
|
@@ -13,8 +13,8 @@ import { execSync as execSync5 } from "child_process";
|
|
|
13
13
|
|
|
14
14
|
// src/commands/serve.ts
|
|
15
15
|
import http2 from "http";
|
|
16
|
-
import { readFileSync as
|
|
17
|
-
import { join as
|
|
16
|
+
import { readFileSync as readFileSync11, existsSync as existsSync12, statSync as statSync4, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4 } from "fs";
|
|
17
|
+
import { join as join13, extname as extname2, dirname as dirname6 } from "path";
|
|
18
18
|
import { fileURLToPath } from "url";
|
|
19
19
|
|
|
20
20
|
// src/api/server.ts
|
|
@@ -872,7 +872,39 @@ function sanitizeModel(value, fallback) {
|
|
|
872
872
|
const model = lastPathSegment(value);
|
|
873
873
|
return model || fallback;
|
|
874
874
|
}
|
|
875
|
-
function
|
|
875
|
+
function codeBuddyUsage(parsed) {
|
|
876
|
+
const ru = parsed?.providerData?.rawUsage;
|
|
877
|
+
if (ru && typeof ru === "object") {
|
|
878
|
+
const hit = num(ru.prompt_cache_hit_tokens ?? ru.prompt_tokens_details?.cached_tokens);
|
|
879
|
+
const miss = ru.prompt_cache_miss_tokens;
|
|
880
|
+
const prompt2 = num(ru.prompt_tokens ?? ru.input_tokens);
|
|
881
|
+
return {
|
|
882
|
+
inputTokens: miss != null ? num(miss) : Math.max(0, prompt2 - hit),
|
|
883
|
+
outputTokens: num(ru.completion_tokens ?? ru.output_tokens),
|
|
884
|
+
cacheReadTokens: hit,
|
|
885
|
+
cacheWriteTokens: num(ru.prompt_cache_write_tokens ?? ru.cache_creation_input_tokens),
|
|
886
|
+
thinkingTokens: num(ru.completion_thinking_tokens ?? ru.completion_tokens_details?.reasoning_tokens)
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
const mu = parsed?.message?.usage;
|
|
890
|
+
if (mu && typeof mu === "object") {
|
|
891
|
+
const cacheRead = num(mu.cache_read_input_tokens ?? mu.prompt_tokens_details?.cached_tokens);
|
|
892
|
+
const rawInput = num(mu.input_tokens ?? mu.prompt_tokens);
|
|
893
|
+
return {
|
|
894
|
+
inputTokens: Math.max(0, rawInput - cacheRead),
|
|
895
|
+
outputTokens: num(mu.output_tokens ?? mu.completion_tokens),
|
|
896
|
+
cacheReadTokens: cacheRead,
|
|
897
|
+
cacheWriteTokens: num(mu.cache_creation_input_tokens ?? mu.cache_write_input_tokens),
|
|
898
|
+
thinkingTokens: num(mu.thinking_tokens ?? mu.reasoning_tokens)
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
return null;
|
|
902
|
+
}
|
|
903
|
+
function usageFromAny(parsed, tool) {
|
|
904
|
+
if (tool === "codebuddy") {
|
|
905
|
+
const cb = codeBuddyUsage(parsed);
|
|
906
|
+
if (cb) return cb;
|
|
907
|
+
}
|
|
876
908
|
const usage = parsed?.message?.usage ?? parsed?.usage ?? parsed?.event?.usage ?? parsed?.payload?.token_usage ?? parsed?.message?.payload?.token_usage ?? parsed?.providerData?.rawUsage ?? parsed?.data?.usage;
|
|
877
909
|
if (!usage || typeof usage !== "object") return null;
|
|
878
910
|
const details = usage.prompt_tokens_details ?? usage.input_tokens_details ?? {};
|
|
@@ -942,7 +974,7 @@ var GenericJsonlParser = class {
|
|
|
942
974
|
}
|
|
943
975
|
const normalized = parsed?.type === "context.append_loop_event" && parsed.event ? { ...parsed.event, time: parsed.time } : parsed;
|
|
944
976
|
if (!shouldAccept(this.tool, normalized)) return null;
|
|
945
|
-
const usage = usageFromAny(normalized);
|
|
977
|
+
const usage = usageFromAny(normalized, this.tool);
|
|
946
978
|
if (!usage) return null;
|
|
947
979
|
const total = usage.inputTokens + usage.outputTokens + usage.cacheReadTokens + usage.cacheWriteTokens + usage.thinkingTokens;
|
|
948
980
|
if (total === 0) return null;
|
|
@@ -1603,6 +1635,55 @@ function probeCodeBuddy(ctx) {
|
|
|
1603
1635
|
const dir = join3(ctx.env.CODEBUDDY_HOME ?? join3(ctx.home, ".codebuddy"), "projects");
|
|
1604
1636
|
return existsSync3(dir) ? dir : null;
|
|
1605
1637
|
}
|
|
1638
|
+
function codeBuddyIdeRoots(ctx) {
|
|
1639
|
+
const roots = [];
|
|
1640
|
+
if (platform() === "darwin") {
|
|
1641
|
+
roots.push(join3(ctx.home, "Library", "Application Support", "CodeBuddyExtension", "Data"));
|
|
1642
|
+
} else if (platform() === "win32") {
|
|
1643
|
+
const appData = ctx.env.APPDATA ?? join3(ctx.home, "AppData", "Roaming");
|
|
1644
|
+
roots.push(join3(appData, "CodeBuddyExtension", "Data"));
|
|
1645
|
+
} else {
|
|
1646
|
+
const config = ctx.env.XDG_CONFIG_HOME ?? join3(ctx.home, ".config");
|
|
1647
|
+
roots.push(join3(config, "CodeBuddyExtension", "Data"));
|
|
1648
|
+
}
|
|
1649
|
+
return unique(roots);
|
|
1650
|
+
}
|
|
1651
|
+
function probeCodeBuddyIde(ctx) {
|
|
1652
|
+
const override = envOverride("codebuddy-ide", ctx.env);
|
|
1653
|
+
if (override) return override;
|
|
1654
|
+
const legacy = ctx.legacySources?.["codebuddy-ide"];
|
|
1655
|
+
if (legacy) return legacy;
|
|
1656
|
+
for (const root of codeBuddyIdeRoots(ctx)) {
|
|
1657
|
+
if (existsSync3(root)) return root;
|
|
1658
|
+
}
|
|
1659
|
+
return null;
|
|
1660
|
+
}
|
|
1661
|
+
function countCodeBuddyIdeConversations(dir) {
|
|
1662
|
+
let count = 0;
|
|
1663
|
+
const walk = (root, depth) => {
|
|
1664
|
+
if (depth > 12) return;
|
|
1665
|
+
let entries;
|
|
1666
|
+
try {
|
|
1667
|
+
entries = readdirSync(root, { withFileTypes: true });
|
|
1668
|
+
} catch {
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
for (const entry of entries) {
|
|
1672
|
+
if (!entry.isDirectory()) continue;
|
|
1673
|
+
const full = join3(root, entry.name);
|
|
1674
|
+
if (entry.name === "messages") {
|
|
1675
|
+
try {
|
|
1676
|
+
if (readdirSync(full).some((f) => f.endsWith(".json"))) count++;
|
|
1677
|
+
} catch {
|
|
1678
|
+
}
|
|
1679
|
+
continue;
|
|
1680
|
+
}
|
|
1681
|
+
walk(full, depth + 1);
|
|
1682
|
+
}
|
|
1683
|
+
};
|
|
1684
|
+
walk(dir, 0);
|
|
1685
|
+
return count;
|
|
1686
|
+
}
|
|
1606
1687
|
function probeKiro(ctx) {
|
|
1607
1688
|
const override = envOverride("kiro", ctx.env);
|
|
1608
1689
|
if (override) return override;
|
|
@@ -1810,6 +1891,7 @@ var TOOL_REGISTRY = [
|
|
|
1810
1891
|
{ tool: "gemini", sourceKey: "gemini", label: "Gemini CLI", probe: probeGemini },
|
|
1811
1892
|
{ tool: "kimi", sourceKey: "kimi", label: "Kimi Code", probe: probeKimi },
|
|
1812
1893
|
{ tool: "codebuddy", sourceKey: "codebuddy", label: "CodeBuddy", probe: probeCodeBuddy },
|
|
1894
|
+
{ tool: "codebuddy", sourceKey: "codebuddy-ide", label: "CodeBuddy (IDE)", probe: probeCodeBuddyIde },
|
|
1813
1895
|
{ tool: "kiro", sourceKey: "kiro", label: "Kiro", probe: probeKiro },
|
|
1814
1896
|
{ tool: "grok", sourceKey: "grok", label: "Grok Build", probe: probeGrok },
|
|
1815
1897
|
{ tool: "antigravity", sourceKey: "antigravity", label: "Antigravity", probe: probeAntigravity },
|
|
@@ -1839,7 +1921,9 @@ function discoverTools(env = process.env) {
|
|
|
1839
1921
|
try {
|
|
1840
1922
|
const stat2 = statSync(detectedPath);
|
|
1841
1923
|
if (stat2.isDirectory()) {
|
|
1842
|
-
if (entry.sourceKey === "
|
|
1924
|
+
if (entry.sourceKey === "codebuddy-ide") {
|
|
1925
|
+
fileCount += countCodeBuddyIdeConversations(detectedPath);
|
|
1926
|
+
} else if (entry.sourceKey === "roocode" || entry.sourceKey === "kilocode") {
|
|
1843
1927
|
fileCount += findJsonFiles(detectedPath).filter((p) => basename(p) === "ui_messages.json").length;
|
|
1844
1928
|
} else if (entry.sourceKey === "kelivo") {
|
|
1845
1929
|
fileCount += findJsonFiles(detectedPath).filter((p) => basename(p) === "chats.json").length + findZipFiles(detectedPath).length;
|
|
@@ -3176,6 +3260,17 @@ function migrateV11(db) {
|
|
|
3176
3260
|
`);
|
|
3177
3261
|
}
|
|
3178
3262
|
|
|
3263
|
+
// src/db/migrations/v12.ts
|
|
3264
|
+
function migrateV12(db) {
|
|
3265
|
+
db.prepare(`
|
|
3266
|
+
UPDATE records
|
|
3267
|
+
SET updated_at = ?
|
|
3268
|
+
WHERE source_file NOT LIKE 'synced/%'
|
|
3269
|
+
AND (cwd != '' OR source_file LIKE '%:session:%')
|
|
3270
|
+
`).run(Date.now());
|
|
3271
|
+
db.prepare("INSERT INTO schema_version (version) VALUES (12)").run();
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3179
3274
|
// src/db/migrations/index.ts
|
|
3180
3275
|
var MIGRATIONS = [
|
|
3181
3276
|
{ version: 1, migrate: migrateV1 },
|
|
@@ -3188,7 +3283,8 @@ var MIGRATIONS = [
|
|
|
3188
3283
|
{ version: 8, migrate: migrateV8 },
|
|
3189
3284
|
{ version: 9, migrate: migrateV9 },
|
|
3190
3285
|
{ version: 10, migrate: migrateV10 },
|
|
3191
|
-
{ version: 11, migrate: migrateV11 }
|
|
3286
|
+
{ version: 11, migrate: migrateV11 },
|
|
3287
|
+
{ version: 12, migrate: migrateV12 }
|
|
3192
3288
|
];
|
|
3193
3289
|
function runMigrations(db) {
|
|
3194
3290
|
createSchemaVersionTable(db);
|
|
@@ -4031,10 +4127,10 @@ function getClientPlatform() {
|
|
|
4031
4127
|
}
|
|
4032
4128
|
function getCliVersion() {
|
|
4033
4129
|
try {
|
|
4034
|
-
const { readFileSync:
|
|
4035
|
-
const { join:
|
|
4036
|
-
const pkgPath =
|
|
4037
|
-
const pkg = JSON.parse(
|
|
4130
|
+
const { readFileSync: readFileSync14 } = __require("fs");
|
|
4131
|
+
const { join: join18 } = __require("path");
|
|
4132
|
+
const pkgPath = join18(__dirname, "../../package.json");
|
|
4133
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
4038
4134
|
return pkg.version || "0.0.0";
|
|
4039
4135
|
} catch {
|
|
4040
4136
|
return "0.0.0";
|
|
@@ -6245,8 +6341,8 @@ function createApiServer(db, options) {
|
|
|
6245
6341
|
|
|
6246
6342
|
// src/commands/parse.ts
|
|
6247
6343
|
import Database2 from "better-sqlite3";
|
|
6248
|
-
import { readFileSync as
|
|
6249
|
-
import { join as
|
|
6344
|
+
import { readFileSync as readFileSync10, statSync as statSync3, existsSync as existsSync10, openSync, readSync, closeSync } from "fs";
|
|
6345
|
+
import { join as join9, dirname as dirname5 } from "path";
|
|
6250
6346
|
import { hostname as hostname2 } from "os";
|
|
6251
6347
|
|
|
6252
6348
|
// src/watermark.ts
|
|
@@ -6297,7 +6393,7 @@ var WatermarkManager = class {
|
|
|
6297
6393
|
if (parsed && typeof parsed === "object" && !("files" in parsed)) {
|
|
6298
6394
|
return { files: { ...defaultFileData(), ...parsed } };
|
|
6299
6395
|
}
|
|
6300
|
-
return { files: { ...defaultFileData(), ...parsed.files ?? {} }, opencode: parsed.opencode ?? null, hermes: parsed.hermes ?? null, qoder: parsed.qoder ?? null, cursor: parsed.cursor ?? null, goose: parsed.goose ?? null, zed: parsed.zed ?? null, kiro: parsed.kiro ?? null, zcode: parsed.zcode ?? null, zcodeTools: parsed.zcodeTools ?? null, trae: parsed.trae ?? null };
|
|
6396
|
+
return { files: { ...defaultFileData(), ...parsed.files ?? {} }, opencode: parsed.opencode ?? null, hermes: parsed.hermes ?? null, qoder: parsed.qoder ?? null, cursor: parsed.cursor ?? null, goose: parsed.goose ?? null, zed: parsed.zed ?? null, kiro: parsed.kiro ?? null, zcode: parsed.zcode ?? null, zcodeTools: parsed.zcodeTools ?? null, trae: parsed.trae ?? null, codebuddyIde: parsed.codebuddyIde ?? null };
|
|
6301
6397
|
} catch {
|
|
6302
6398
|
return { files: defaultFileData() };
|
|
6303
6399
|
}
|
|
@@ -6384,6 +6480,12 @@ var WatermarkManager = class {
|
|
|
6384
6480
|
setTraeLastImported(ts3) {
|
|
6385
6481
|
this.data.trae = ts3;
|
|
6386
6482
|
}
|
|
6483
|
+
getCodeBuddyIdeCursor() {
|
|
6484
|
+
return this.data.codebuddyIde ?? 0;
|
|
6485
|
+
}
|
|
6486
|
+
setCodeBuddyIdeCursor(ts3) {
|
|
6487
|
+
this.data.codebuddyIde = ts3;
|
|
6488
|
+
}
|
|
6387
6489
|
};
|
|
6388
6490
|
|
|
6389
6491
|
// src/commands/parse-opencode.ts
|
|
@@ -7311,53 +7413,33 @@ function parseSessionTags(repoPath) {
|
|
|
7311
7413
|
try {
|
|
7312
7414
|
const sessionId = basename3(basename3(repoPath) === "v2" ? join7(repoPath, "..") : repoPath);
|
|
7313
7415
|
if (!/^[0-9a-f]{24,}$/.test(sessionId)) return null;
|
|
7314
|
-
const
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7416
|
+
const result = spawnSync(
|
|
7417
|
+
"git",
|
|
7418
|
+
["for-each-ref", "--format=%(refname:short)%09%(creatordate:unix)", "refs/tags"],
|
|
7419
|
+
{ cwd: repoPath, encoding: "utf-8", timeout: 1e4 }
|
|
7420
|
+
);
|
|
7421
|
+
if (result.status !== 0 || !result.stdout?.trim()) return null;
|
|
7318
7422
|
let chatTurns = 0;
|
|
7319
7423
|
let toolCalls = 0;
|
|
7320
7424
|
let startTs = 0;
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
if (logResult.status !== 0 || !logResult.stdout?.trim()) continue;
|
|
7332
|
-
const tsStr = logResult.stdout.trim();
|
|
7333
|
-
tagTs = parseInt(tsStr, 10) * 1e3;
|
|
7334
|
-
} catch {
|
|
7335
|
-
continue;
|
|
7336
|
-
}
|
|
7337
|
-
if (t.startsWith("chain-start-")) {
|
|
7425
|
+
let earliest = Infinity;
|
|
7426
|
+
for (const line of result.stdout.trim().split("\n")) {
|
|
7427
|
+
const tab = line.indexOf(" ");
|
|
7428
|
+
if (tab < 0) continue;
|
|
7429
|
+
const name = line.slice(0, tab).trim();
|
|
7430
|
+
if (!name) continue;
|
|
7431
|
+
const tagTs = parseInt(line.slice(tab + 1).trim(), 10) * 1e3;
|
|
7432
|
+
if (!Number.isFinite(tagTs) || tagTs <= 0) continue;
|
|
7433
|
+
if (tagTs < earliest) earliest = tagTs;
|
|
7434
|
+
if (name.startsWith("chain-start-")) {
|
|
7338
7435
|
startTs = tagTs;
|
|
7339
|
-
} else if (
|
|
7436
|
+
} else if (name.startsWith("before-chat-turn-")) {
|
|
7340
7437
|
chatTurns++;
|
|
7341
|
-
} else if (
|
|
7438
|
+
} else if (name.startsWith("toolcall-")) {
|
|
7342
7439
|
toolCalls++;
|
|
7343
7440
|
}
|
|
7344
7441
|
}
|
|
7345
|
-
if (startTs === 0)
|
|
7346
|
-
let earliest = Infinity;
|
|
7347
|
-
for (const tag of lines) {
|
|
7348
|
-
const t = tag.trim();
|
|
7349
|
-
if (!t) continue;
|
|
7350
|
-
const logResult = spawnSync("git", ["log", "-1", "--format=%at", t], {
|
|
7351
|
-
cwd: repoPath,
|
|
7352
|
-
encoding: "utf-8",
|
|
7353
|
-
timeout: 3e3
|
|
7354
|
-
});
|
|
7355
|
-
if (logResult.status !== 0 || !logResult.stdout?.trim()) continue;
|
|
7356
|
-
const ts3 = parseInt(logResult.stdout.trim(), 10) * 1e3;
|
|
7357
|
-
if (ts3 > 0 && ts3 < earliest) earliest = ts3;
|
|
7358
|
-
}
|
|
7359
|
-
if (earliest < Infinity) startTs = earliest;
|
|
7360
|
-
}
|
|
7442
|
+
if (startTs === 0 && earliest < Infinity) startTs = earliest;
|
|
7361
7443
|
if (chatTurns === 0 && toolCalls === 0) return null;
|
|
7362
7444
|
if (startTs === 0) return null;
|
|
7363
7445
|
return { sessionId, startTs, chatTurns, toolCalls };
|
|
@@ -7430,6 +7512,173 @@ function runParseTrae(options) {
|
|
|
7430
7512
|
return { records, toolCalls, lastImportedAt: latestTs, errors };
|
|
7431
7513
|
}
|
|
7432
7514
|
|
|
7515
|
+
// src/commands/parse-codebuddy.ts
|
|
7516
|
+
import { existsSync as existsSync9, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
|
|
7517
|
+
import { join as join8, basename as basename4, dirname as dirname4 } from "path";
|
|
7518
|
+
function num3(value) {
|
|
7519
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
7520
|
+
}
|
|
7521
|
+
function toMs(iso) {
|
|
7522
|
+
if (typeof iso !== "string") return 0;
|
|
7523
|
+
const ms = Date.parse(iso);
|
|
7524
|
+
return Number.isFinite(ms) ? ms : 0;
|
|
7525
|
+
}
|
|
7526
|
+
var WORKSPACE_FOLDER_RE = /Workspace Folder:\s*(.+)/;
|
|
7527
|
+
function userMessageText(raw) {
|
|
7528
|
+
try {
|
|
7529
|
+
const inner = JSON.parse(raw);
|
|
7530
|
+
if (Array.isArray(inner.content)) {
|
|
7531
|
+
return inner.content.filter((block) => block?.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n");
|
|
7532
|
+
}
|
|
7533
|
+
} catch {
|
|
7534
|
+
}
|
|
7535
|
+
return raw;
|
|
7536
|
+
}
|
|
7537
|
+
function findMessagesDirs(root, depth = 0) {
|
|
7538
|
+
if (depth > 12) return [];
|
|
7539
|
+
let entries;
|
|
7540
|
+
try {
|
|
7541
|
+
entries = readdirSync3(root, { withFileTypes: true });
|
|
7542
|
+
} catch {
|
|
7543
|
+
return [];
|
|
7544
|
+
}
|
|
7545
|
+
const out = [];
|
|
7546
|
+
for (const entry of entries) {
|
|
7547
|
+
if (!entry.isDirectory()) continue;
|
|
7548
|
+
const full = join8(root, entry.name);
|
|
7549
|
+
if (entry.name === "messages") {
|
|
7550
|
+
out.push(full);
|
|
7551
|
+
continue;
|
|
7552
|
+
}
|
|
7553
|
+
out.push(...findMessagesDirs(full, depth + 1));
|
|
7554
|
+
}
|
|
7555
|
+
return out;
|
|
7556
|
+
}
|
|
7557
|
+
function readConversation(messagesDir) {
|
|
7558
|
+
let files;
|
|
7559
|
+
try {
|
|
7560
|
+
files = readdirSync3(messagesDir).filter((f) => f.endsWith(".json"));
|
|
7561
|
+
} catch {
|
|
7562
|
+
return null;
|
|
7563
|
+
}
|
|
7564
|
+
if (files.length === 0) return null;
|
|
7565
|
+
const parsed = [];
|
|
7566
|
+
for (const file of files) {
|
|
7567
|
+
try {
|
|
7568
|
+
const data = JSON.parse(readFileSync9(join8(messagesDir, file), "utf-8"));
|
|
7569
|
+
if (data && typeof data === "object") parsed.push(data);
|
|
7570
|
+
} catch {
|
|
7571
|
+
}
|
|
7572
|
+
}
|
|
7573
|
+
if (parsed.length === 0) return null;
|
|
7574
|
+
let cwd;
|
|
7575
|
+
for (const msg of parsed) {
|
|
7576
|
+
if (msg.role !== "user" || typeof msg.message !== "string") continue;
|
|
7577
|
+
const match = WORKSPACE_FOLDER_RE.exec(userMessageText(msg.message));
|
|
7578
|
+
if (match) {
|
|
7579
|
+
cwd = match[1].trim();
|
|
7580
|
+
break;
|
|
7581
|
+
}
|
|
7582
|
+
}
|
|
7583
|
+
let latest = null;
|
|
7584
|
+
for (const msg of parsed) {
|
|
7585
|
+
if (msg.role !== "assistant" || typeof msg.extra !== "string") continue;
|
|
7586
|
+
let extra2;
|
|
7587
|
+
try {
|
|
7588
|
+
extra2 = JSON.parse(msg.extra);
|
|
7589
|
+
} catch {
|
|
7590
|
+
continue;
|
|
7591
|
+
}
|
|
7592
|
+
const hasStats = extra2.statsSnapshot != null || extra2.lastStepInputTokens != null;
|
|
7593
|
+
if (!hasStats) continue;
|
|
7594
|
+
const ts3 = toMs(msg.createdAt);
|
|
7595
|
+
if (!latest || ts3 >= latest.ts) latest = { ts: ts3, extra: extra2 };
|
|
7596
|
+
}
|
|
7597
|
+
if (!latest) return null;
|
|
7598
|
+
const extra = latest.extra;
|
|
7599
|
+
const snapshot = extra.statsSnapshot ?? {};
|
|
7600
|
+
const cacheReadTokens = num3(snapshot.cachedInputTokens ?? extra.lastStepCachedInputTokens);
|
|
7601
|
+
const inputTokens = snapshot.cacheMissTokens != null ? num3(snapshot.cacheMissTokens) : Math.max(0, num3(extra.lastStepInputTokens) - cacheReadTokens);
|
|
7602
|
+
const outputTokens = num3(snapshot.lastOutputTokens ?? extra.lastStepOutputTokens);
|
|
7603
|
+
const cacheWriteTokens = num3(snapshot.cacheWriteTokens);
|
|
7604
|
+
const thinkingTokens = num3(snapshot.thinkingTokens);
|
|
7605
|
+
const model = typeof extra.modelId === "string" && extra.modelId.trim() ? extra.modelId.trim() : "unknown";
|
|
7606
|
+
return {
|
|
7607
|
+
ts: latest.ts,
|
|
7608
|
+
model,
|
|
7609
|
+
inputTokens,
|
|
7610
|
+
outputTokens,
|
|
7611
|
+
cacheReadTokens,
|
|
7612
|
+
cacheWriteTokens,
|
|
7613
|
+
thinkingTokens,
|
|
7614
|
+
cwd
|
|
7615
|
+
};
|
|
7616
|
+
}
|
|
7617
|
+
function runParseCodeBuddy(options) {
|
|
7618
|
+
const { dataDir, device, deviceInstanceId, platform: platform5, now, cursor, exchangeRate } = options;
|
|
7619
|
+
const records = [];
|
|
7620
|
+
const errors = [];
|
|
7621
|
+
let nextCursor = cursor ?? 0;
|
|
7622
|
+
if (!existsSync9(dataDir)) {
|
|
7623
|
+
return { records, nextCursor, errors };
|
|
7624
|
+
}
|
|
7625
|
+
let messagesDirs;
|
|
7626
|
+
try {
|
|
7627
|
+
messagesDirs = findMessagesDirs(dataDir);
|
|
7628
|
+
} catch (e) {
|
|
7629
|
+
return { records, nextCursor, errors: [String(e)] };
|
|
7630
|
+
}
|
|
7631
|
+
for (const messagesDir of messagesDirs) {
|
|
7632
|
+
const conversationDir = dirname4(messagesDir);
|
|
7633
|
+
const sessionId = basename4(conversationDir);
|
|
7634
|
+
let usage;
|
|
7635
|
+
try {
|
|
7636
|
+
usage = readConversation(messagesDir);
|
|
7637
|
+
} catch (e) {
|
|
7638
|
+
errors.push(`${messagesDir}: ${e instanceof Error ? e.message : e}`);
|
|
7639
|
+
continue;
|
|
7640
|
+
}
|
|
7641
|
+
if (!usage) continue;
|
|
7642
|
+
if (usage.inputTokens + usage.outputTokens + usage.cacheReadTokens === 0) continue;
|
|
7643
|
+
if (cursor && usage.ts <= cursor) continue;
|
|
7644
|
+
if (usage.ts > nextCursor) nextCursor = usage.ts;
|
|
7645
|
+
const provider = inferProvider(usage.model);
|
|
7646
|
+
const tokenArgs = {
|
|
7647
|
+
inputTokens: usage.inputTokens,
|
|
7648
|
+
outputTokens: usage.outputTokens,
|
|
7649
|
+
cacheReadTokens: usage.cacheReadTokens,
|
|
7650
|
+
cacheWriteTokens: usage.cacheWriteTokens,
|
|
7651
|
+
thinkingTokens: usage.thinkingTokens
|
|
7652
|
+
};
|
|
7653
|
+
const cost = calculateCost(usage.model, tokenArgs, exchangeRate);
|
|
7654
|
+
const recordId = generateRecordId(deviceInstanceId, conversationDir, usage.ts);
|
|
7655
|
+
records.push({
|
|
7656
|
+
id: recordId,
|
|
7657
|
+
ts: usage.ts,
|
|
7658
|
+
ingestedAt: now,
|
|
7659
|
+
updatedAt: now,
|
|
7660
|
+
lineOffset: 0,
|
|
7661
|
+
tool: "codebuddy",
|
|
7662
|
+
model: usage.model,
|
|
7663
|
+
provider,
|
|
7664
|
+
inputTokens: usage.inputTokens,
|
|
7665
|
+
outputTokens: usage.outputTokens,
|
|
7666
|
+
cacheReadTokens: usage.cacheReadTokens,
|
|
7667
|
+
cacheWriteTokens: usage.cacheWriteTokens,
|
|
7668
|
+
thinkingTokens: usage.thinkingTokens,
|
|
7669
|
+
cost,
|
|
7670
|
+
costSource: cost > 0 ? "pricing" : "unknown",
|
|
7671
|
+
sessionId,
|
|
7672
|
+
sourceFile: conversationDir,
|
|
7673
|
+
cwd: usage.cwd,
|
|
7674
|
+
device,
|
|
7675
|
+
deviceInstanceId,
|
|
7676
|
+
platform: platform5
|
|
7677
|
+
});
|
|
7678
|
+
}
|
|
7679
|
+
return { records, nextCursor, errors };
|
|
7680
|
+
}
|
|
7681
|
+
|
|
7433
7682
|
// src/commands/parse.ts
|
|
7434
7683
|
function extractCwdFromJson(data) {
|
|
7435
7684
|
if (typeof data.cwd === "string" && data.cwd) return data.cwd;
|
|
@@ -7484,7 +7733,7 @@ function parseUiMessagesFile(options) {
|
|
|
7484
7733
|
const errors = [];
|
|
7485
7734
|
let messages;
|
|
7486
7735
|
try {
|
|
7487
|
-
const parsed = JSON.parse(
|
|
7736
|
+
const parsed = JSON.parse(readFileSync10(filePath, "utf-8"));
|
|
7488
7737
|
messages = Array.isArray(parsed) ? parsed : [];
|
|
7489
7738
|
} catch (e) {
|
|
7490
7739
|
return { records, errors: [`${filePath}: ${e instanceof Error ? e.message : e}`] };
|
|
@@ -7562,7 +7811,7 @@ function parseKiroSessionFile(options) {
|
|
|
7562
7811
|
const errors = [];
|
|
7563
7812
|
let parsed;
|
|
7564
7813
|
try {
|
|
7565
|
-
parsed = JSON.parse(
|
|
7814
|
+
parsed = JSON.parse(readFileSync10(filePath, "utf-8"));
|
|
7566
7815
|
} catch (e) {
|
|
7567
7816
|
return { records, errors: [`${filePath}: ${e instanceof Error ? e.message : e}`] };
|
|
7568
7817
|
}
|
|
@@ -7617,8 +7866,8 @@ function parseKiroWorkspaceSession(options) {
|
|
|
7617
7866
|
const sessionId = parsed?.sessionId ?? extractSessionId2(filePath, "kiro");
|
|
7618
7867
|
let sessionTs = now;
|
|
7619
7868
|
try {
|
|
7620
|
-
const dir =
|
|
7621
|
-
const sessionsJson =
|
|
7869
|
+
const dir = dirname5(filePath);
|
|
7870
|
+
const sessionsJson = readFileSync10(join9(dir, "sessions.json"), "utf-8");
|
|
7622
7871
|
const sessions = JSON.parse(sessionsJson);
|
|
7623
7872
|
const match = Array.isArray(sessions) && sessions.find((s) => s.sessionId === sessionId);
|
|
7624
7873
|
if (match?.dateCreated) {
|
|
@@ -7694,7 +7943,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7694
7943
|
const device = config?.device || hostname2() || state?.deviceInstanceId?.slice(0, 8) || "unknown";
|
|
7695
7944
|
const deviceInstanceId = state?.deviceInstanceId ?? "unknown";
|
|
7696
7945
|
const devicePlatform = config?.platform;
|
|
7697
|
-
const watermarkPath =
|
|
7946
|
+
const watermarkPath = join9(AIUSAGE_DIR, "watermark.json");
|
|
7698
7947
|
const wm = new WatermarkManager(watermarkPath);
|
|
7699
7948
|
const toolPaths = discoverLogFiles();
|
|
7700
7949
|
const aggregator = new Aggregator();
|
|
@@ -7765,7 +8014,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7765
8014
|
continue;
|
|
7766
8015
|
}
|
|
7767
8016
|
if (tool === "kiro" && filePath.endsWith(".jsonl")) {
|
|
7768
|
-
const content2 =
|
|
8017
|
+
const content2 = readFileSync10(filePath, "utf-8");
|
|
7769
8018
|
const lines2 = content2.split("\n");
|
|
7770
8019
|
let byteOffset2 = 0;
|
|
7771
8020
|
for (const line of lines2) {
|
|
@@ -7895,7 +8144,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7895
8144
|
onProgress({ phase: "Parsing logs", tool, current: toolIndex, total: toolTotal, records: parsedCount, toolCalls: toolCallCount });
|
|
7896
8145
|
continue;
|
|
7897
8146
|
}
|
|
7898
|
-
const content =
|
|
8147
|
+
const content = readFileSync10(filePath, "utf-8");
|
|
7899
8148
|
const lines = content.split("\n");
|
|
7900
8149
|
let byteOffset = 0;
|
|
7901
8150
|
let fileCwd;
|
|
@@ -7959,7 +8208,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7959
8208
|
}
|
|
7960
8209
|
}
|
|
7961
8210
|
if (fileCwd) {
|
|
7962
|
-
db.prepare(`UPDATE records SET cwd = ? WHERE source_file = ? AND cwd = ''`).run(fileCwd, filePath);
|
|
8211
|
+
db.prepare(`UPDATE records SET cwd = ?, updated_at = ? WHERE source_file = ? AND cwd = ''`).run(fileCwd, Date.now(), filePath);
|
|
7963
8212
|
}
|
|
7964
8213
|
wm.setEntry(tool, filePath, {
|
|
7965
8214
|
offset: stat2.size,
|
|
@@ -7974,7 +8223,7 @@ async function runParse(db, filterTool, options) {
|
|
|
7974
8223
|
}
|
|
7975
8224
|
}
|
|
7976
8225
|
const openCodeDbPath = options?.openCodeDbPath ?? getDbPath("opencode") ?? "";
|
|
7977
|
-
if ((!filterTool || filterTool === "opencode") &&
|
|
8226
|
+
if ((!filterTool || filterTool === "opencode") && existsSync10(openCodeDbPath)) {
|
|
7978
8227
|
try {
|
|
7979
8228
|
const openCodeDb = new Database2(openCodeDbPath, { readonly: true });
|
|
7980
8229
|
try {
|
|
@@ -8005,7 +8254,7 @@ async function runParse(db, filterTool, options) {
|
|
|
8005
8254
|
}
|
|
8006
8255
|
}
|
|
8007
8256
|
const hermesDbPath = options?.hermesDbPath ?? getDbPath("hermes") ?? "";
|
|
8008
|
-
if ((!filterTool || filterTool === "hermes") &&
|
|
8257
|
+
if ((!filterTool || filterTool === "hermes") && existsSync10(hermesDbPath)) {
|
|
8009
8258
|
try {
|
|
8010
8259
|
const hermesDb = new Database2(hermesDbPath, { readonly: true });
|
|
8011
8260
|
try {
|
|
@@ -8036,7 +8285,7 @@ async function runParse(db, filterTool, options) {
|
|
|
8036
8285
|
}
|
|
8037
8286
|
}
|
|
8038
8287
|
const qoderDbPath = options?.qoderDbPath ?? getDbPath("qoder-db") ?? "";
|
|
8039
|
-
if ((!filterTool || filterTool === "qoder") &&
|
|
8288
|
+
if ((!filterTool || filterTool === "qoder") && existsSync10(qoderDbPath)) {
|
|
8040
8289
|
try {
|
|
8041
8290
|
const qoderDb = new Database2(qoderDbPath, { readonly: true });
|
|
8042
8291
|
try {
|
|
@@ -8067,7 +8316,7 @@ async function runParse(db, filterTool, options) {
|
|
|
8067
8316
|
}
|
|
8068
8317
|
}
|
|
8069
8318
|
const cursorDbPath = options?.cursorDbPath ?? getDbPath("cursor") ?? "";
|
|
8070
|
-
if ((!filterTool || filterTool === "cursor") &&
|
|
8319
|
+
if ((!filterTool || filterTool === "cursor") && existsSync10(cursorDbPath)) {
|
|
8071
8320
|
try {
|
|
8072
8321
|
const cursorDb = new Database2(cursorDbPath, { readonly: true });
|
|
8073
8322
|
try {
|
|
@@ -8097,7 +8346,7 @@ async function runParse(db, filterTool, options) {
|
|
|
8097
8346
|
}
|
|
8098
8347
|
}
|
|
8099
8348
|
const kiloDbPath = getDbPath("kilocode-db") ?? "";
|
|
8100
|
-
if ((!filterTool || filterTool === "kilocode") &&
|
|
8349
|
+
if ((!filterTool || filterTool === "kilocode") && existsSync10(kiloDbPath)) {
|
|
8101
8350
|
try {
|
|
8102
8351
|
const kiloDb = new Database2(kiloDbPath, { readonly: true });
|
|
8103
8352
|
try {
|
|
@@ -8137,7 +8386,7 @@ async function runParse(db, filterTool, options) {
|
|
|
8137
8386
|
}
|
|
8138
8387
|
}
|
|
8139
8388
|
const gooseDbPath = getDbPath("goose") ?? "";
|
|
8140
|
-
if ((!filterTool || filterTool === "goose") &&
|
|
8389
|
+
if ((!filterTool || filterTool === "goose") && existsSync10(gooseDbPath)) {
|
|
8141
8390
|
try {
|
|
8142
8391
|
const gooseDb = new Database2(gooseDbPath, { readonly: true });
|
|
8143
8392
|
try {
|
|
@@ -8195,7 +8444,7 @@ async function runParse(db, filterTool, options) {
|
|
|
8195
8444
|
}
|
|
8196
8445
|
}
|
|
8197
8446
|
const zedDbPath = getDbPath("zed") ?? "";
|
|
8198
|
-
if ((!filterTool || filterTool === "zed") &&
|
|
8447
|
+
if ((!filterTool || filterTool === "zed") && existsSync10(zedDbPath)) {
|
|
8199
8448
|
try {
|
|
8200
8449
|
const zedDb = new Database2(zedDbPath, { readonly: true });
|
|
8201
8450
|
try {
|
|
@@ -8225,7 +8474,7 @@ async function runParse(db, filterTool, options) {
|
|
|
8225
8474
|
}
|
|
8226
8475
|
if (!filterTool || filterTool === "zcode") {
|
|
8227
8476
|
const zcodeDbPath = getDbPath("zcode") ?? "";
|
|
8228
|
-
if (
|
|
8477
|
+
if (existsSync10(zcodeDbPath)) {
|
|
8229
8478
|
try {
|
|
8230
8479
|
const zcodeDb = new Database2(zcodeDbPath, { readonly: true });
|
|
8231
8480
|
try {
|
|
@@ -8284,6 +8533,31 @@ async function runParse(db, filterTool, options) {
|
|
|
8284
8533
|
}
|
|
8285
8534
|
onProgress({ phase: "Parsing SQLite", tool: "trae", current: 1, total: 1, records: parsedCount, toolCalls: toolCallCount });
|
|
8286
8535
|
}
|
|
8536
|
+
const codeBuddyIdeDir = getDbPath("codebuddy-ide") ?? "";
|
|
8537
|
+
if ((!filterTool || filterTool === "codebuddy") && existsSync10(codeBuddyIdeDir)) {
|
|
8538
|
+
try {
|
|
8539
|
+
const cbCursor = wm.getCodeBuddyIdeCursor();
|
|
8540
|
+
const result = runParseCodeBuddy({
|
|
8541
|
+
dataDir: codeBuddyIdeDir,
|
|
8542
|
+
device,
|
|
8543
|
+
deviceInstanceId,
|
|
8544
|
+
platform: devicePlatform,
|
|
8545
|
+
now: Date.now(),
|
|
8546
|
+
cursor: cbCursor,
|
|
8547
|
+
exchangeRate
|
|
8548
|
+
});
|
|
8549
|
+
for (const record of result.records) insertRecord(db, record);
|
|
8550
|
+
parsedCount += result.records.length;
|
|
8551
|
+
errors.push(...result.errors);
|
|
8552
|
+
if (result.nextCursor > cbCursor) {
|
|
8553
|
+
wm.setCodeBuddyIdeCursor(result.nextCursor);
|
|
8554
|
+
wm.save();
|
|
8555
|
+
}
|
|
8556
|
+
onProgress({ phase: "Parsing logs", tool: "codebuddy", current: 1, total: 1, records: parsedCount, toolCalls: toolCallCount });
|
|
8557
|
+
} catch (e) {
|
|
8558
|
+
errors.push(`${codeBuddyIdeDir}: ${e instanceof Error ? e.message : e}`);
|
|
8559
|
+
}
|
|
8560
|
+
}
|
|
8287
8561
|
if (deviceInstanceId !== "unknown") {
|
|
8288
8562
|
db.prepare(
|
|
8289
8563
|
`UPDATE records SET device_instance_id = ?, device = ? WHERE device_instance_id = 'unknown'`
|
|
@@ -8295,9 +8569,19 @@ async function runParse(db, filterTool, options) {
|
|
|
8295
8569
|
).run(devicePlatform);
|
|
8296
8570
|
}
|
|
8297
8571
|
backfillHermesSourceFiles(db);
|
|
8572
|
+
backfillCwd(db);
|
|
8573
|
+
backfillSkillNames(db);
|
|
8574
|
+
backfillCodexModels(db);
|
|
8575
|
+
backfillMissingToolCalls(db, exchangeRate);
|
|
8576
|
+
return { parsedCount, toolCallCount, errors };
|
|
8577
|
+
}
|
|
8578
|
+
function backfillCwd(db) {
|
|
8298
8579
|
const staleFiles = db.prepare(
|
|
8299
8580
|
`SELECT DISTINCT source_file FROM records WHERE cwd = '' AND source_file NOT LIKE 'synced/%'`
|
|
8300
8581
|
).all();
|
|
8582
|
+
const updateStmt = db.prepare(
|
|
8583
|
+
`UPDATE records SET cwd = ?, updated_at = ? WHERE source_file = ? AND cwd = ''`
|
|
8584
|
+
);
|
|
8301
8585
|
for (const { source_file } of staleFiles) {
|
|
8302
8586
|
try {
|
|
8303
8587
|
const fd = openSync(source_file, "r");
|
|
@@ -8308,15 +8592,11 @@ async function runParse(db, filterTool, options) {
|
|
|
8308
8592
|
const data = JSON.parse(firstLine);
|
|
8309
8593
|
const cwd = extractCwdFromJson(data);
|
|
8310
8594
|
if (cwd) {
|
|
8311
|
-
|
|
8595
|
+
updateStmt.run(cwd, Date.now(), source_file);
|
|
8312
8596
|
}
|
|
8313
8597
|
} catch {
|
|
8314
8598
|
}
|
|
8315
8599
|
}
|
|
8316
|
-
backfillSkillNames(db);
|
|
8317
|
-
backfillCodexModels(db);
|
|
8318
|
-
backfillMissingToolCalls(db, exchangeRate);
|
|
8319
|
-
return { parsedCount, toolCallCount, errors };
|
|
8320
8600
|
}
|
|
8321
8601
|
function backfillHermesSourceFiles(db) {
|
|
8322
8602
|
const rows = db.prepare(`
|
|
@@ -8330,7 +8610,7 @@ function backfillHermesSourceFiles(db) {
|
|
|
8330
8610
|
const dbPath = rows[0].source_file;
|
|
8331
8611
|
let titleMap = /* @__PURE__ */ new Map();
|
|
8332
8612
|
try {
|
|
8333
|
-
if (
|
|
8613
|
+
if (existsSync10(dbPath)) {
|
|
8334
8614
|
const hermesDb = new Database2(dbPath, { readonly: true });
|
|
8335
8615
|
try {
|
|
8336
8616
|
const sessions = hermesDb.prepare(
|
|
@@ -8343,11 +8623,11 @@ function backfillHermesSourceFiles(db) {
|
|
|
8343
8623
|
}
|
|
8344
8624
|
} catch {
|
|
8345
8625
|
}
|
|
8346
|
-
const updateStmt = db.prepare(`UPDATE records SET source_file = ? WHERE tool = 'hermes' AND session_id = ? AND source_file = ?`);
|
|
8626
|
+
const updateStmt = db.prepare(`UPDATE records SET source_file = ?, updated_at = ? WHERE tool = 'hermes' AND session_id = ? AND source_file = ?`);
|
|
8347
8627
|
for (const row of rows) {
|
|
8348
8628
|
const title = (titleMap.get(row.session_id) || "").replace(/[/\\:]/g, "_").slice(0, 80);
|
|
8349
8629
|
const newSourceFile = title ? `${row.source_file}:session:${row.session_id}:${title}` : `${row.source_file}:session:${row.session_id}`;
|
|
8350
|
-
updateStmt.run(newSourceFile, row.session_id, row.source_file);
|
|
8630
|
+
updateStmt.run(newSourceFile, Date.now(), row.session_id, row.source_file);
|
|
8351
8631
|
}
|
|
8352
8632
|
}
|
|
8353
8633
|
function backfillSkillNames(db) {
|
|
@@ -8411,7 +8691,7 @@ function backfillCodexModels(db) {
|
|
|
8411
8691
|
);
|
|
8412
8692
|
for (const [sourceFile, fileRows] of byFile) {
|
|
8413
8693
|
try {
|
|
8414
|
-
const content =
|
|
8694
|
+
const content = readFileSync10(sourceFile, "utf-8");
|
|
8415
8695
|
const lines = content.split("\n");
|
|
8416
8696
|
const offsetToModel = /* @__PURE__ */ new Map();
|
|
8417
8697
|
let currentModel = "";
|
|
@@ -8459,7 +8739,7 @@ function backfillMissingToolCalls(db, exchangeRate) {
|
|
|
8459
8739
|
}
|
|
8460
8740
|
for (const [sourceFile, fileRows] of rowsByFile) {
|
|
8461
8741
|
try {
|
|
8462
|
-
const content =
|
|
8742
|
+
const content = readFileSync10(sourceFile, "utf-8");
|
|
8463
8743
|
const lines = content.split("\n");
|
|
8464
8744
|
const aggregator = new Aggregator();
|
|
8465
8745
|
const recordIdByOffset = /* @__PURE__ */ new Map();
|
|
@@ -8521,7 +8801,7 @@ function deriveSessionId(tool, sourceFile) {
|
|
|
8521
8801
|
}
|
|
8522
8802
|
|
|
8523
8803
|
// src/commands/sync.ts
|
|
8524
|
-
import { join as
|
|
8804
|
+
import { join as join11 } from "path";
|
|
8525
8805
|
|
|
8526
8806
|
// src/db/synced-records.ts
|
|
8527
8807
|
function insertSyncedRecord(db, record) {
|
|
@@ -8954,10 +9234,10 @@ async function cloudClear() {
|
|
|
8954
9234
|
}
|
|
8955
9235
|
function getClientVersion() {
|
|
8956
9236
|
try {
|
|
8957
|
-
const { readFileSync:
|
|
8958
|
-
const { join:
|
|
8959
|
-
const pkgPath =
|
|
8960
|
-
const pkg = JSON.parse(
|
|
9237
|
+
const { readFileSync: readFileSync14 } = __require("fs");
|
|
9238
|
+
const { join: join18 } = __require("path");
|
|
9239
|
+
const pkgPath = join18(__dirname, "../../package.json");
|
|
9240
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
8961
9241
|
return pkg.version || "0.0.0";
|
|
8962
9242
|
} catch {
|
|
8963
9243
|
return "0.0.0";
|
|
@@ -9048,7 +9328,7 @@ var CloudSyncOrchestrator = class {
|
|
|
9048
9328
|
import { execFile } from "child_process";
|
|
9049
9329
|
import { promisify } from "util";
|
|
9050
9330
|
import { readFile, writeFile, mkdir, readdir, stat, unlink, rm } from "fs/promises";
|
|
9051
|
-
import { join as
|
|
9331
|
+
import { join as join10 } from "path";
|
|
9052
9332
|
var exec = promisify(execFile);
|
|
9053
9333
|
var GitSyncBackend = class _GitSyncBackend {
|
|
9054
9334
|
repo;
|
|
@@ -9060,7 +9340,7 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
9060
9340
|
this.repo = config.repo;
|
|
9061
9341
|
this.token = config.token;
|
|
9062
9342
|
this.cacheDir = config.cacheDir;
|
|
9063
|
-
this.dataDir =
|
|
9343
|
+
this.dataDir = join10(config.cacheDir, "data");
|
|
9064
9344
|
this.branch = config.branch ?? "main";
|
|
9065
9345
|
}
|
|
9066
9346
|
get remoteUrl() {
|
|
@@ -9092,7 +9372,7 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
9092
9372
|
/** Clone or pull the repo to get latest remote state */
|
|
9093
9373
|
async prepare() {
|
|
9094
9374
|
try {
|
|
9095
|
-
await stat(
|
|
9375
|
+
await stat(join10(this.cacheDir, ".git"));
|
|
9096
9376
|
await this.git(["fetch", "origin", this.branch, "--depth=1"]);
|
|
9097
9377
|
await this.git(["reset", "--hard", `origin/${this.branch}`]);
|
|
9098
9378
|
} catch {
|
|
@@ -9109,15 +9389,15 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
9109
9389
|
}
|
|
9110
9390
|
async readFile(path3) {
|
|
9111
9391
|
try {
|
|
9112
|
-
const fullPath =
|
|
9392
|
+
const fullPath = join10(this.dataDir, path3);
|
|
9113
9393
|
return await readFile(fullPath, "utf-8");
|
|
9114
9394
|
} catch {
|
|
9115
9395
|
return null;
|
|
9116
9396
|
}
|
|
9117
9397
|
}
|
|
9118
9398
|
async writeFile(path3, content) {
|
|
9119
|
-
const fullPath =
|
|
9120
|
-
await mkdir(
|
|
9399
|
+
const fullPath = join10(this.dataDir, path3);
|
|
9400
|
+
await mkdir(join10(fullPath, ".."), { recursive: true });
|
|
9121
9401
|
await writeFile(fullPath, content, "utf-8");
|
|
9122
9402
|
}
|
|
9123
9403
|
async listFiles() {
|
|
@@ -9128,7 +9408,7 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
9128
9408
|
}
|
|
9129
9409
|
}
|
|
9130
9410
|
async deleteFile(path3) {
|
|
9131
|
-
const fullPath =
|
|
9411
|
+
const fullPath = join10(this.dataDir, path3);
|
|
9132
9412
|
try {
|
|
9133
9413
|
await unlink(fullPath);
|
|
9134
9414
|
} catch {
|
|
@@ -9167,7 +9447,7 @@ var GitSyncBackend = class _GitSyncBackend {
|
|
|
9167
9447
|
for (const entry of entries) {
|
|
9168
9448
|
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
9169
9449
|
if (entry.isDirectory()) {
|
|
9170
|
-
files.push(...await this.walkDir(
|
|
9450
|
+
files.push(...await this.walkDir(join10(dir, entry.name), relPath));
|
|
9171
9451
|
} else if (entry.name.endsWith(".ndjson")) {
|
|
9172
9452
|
files.push(relPath);
|
|
9173
9453
|
}
|
|
@@ -9301,7 +9581,7 @@ function createBackend(config) {
|
|
|
9301
9581
|
return new GitSyncBackend({
|
|
9302
9582
|
repo: sync.repo,
|
|
9303
9583
|
token,
|
|
9304
|
-
cacheDir:
|
|
9584
|
+
cacheDir: join11(AIUSAGE_DIR, "sync-repo")
|
|
9305
9585
|
});
|
|
9306
9586
|
}
|
|
9307
9587
|
if (sync.backend === "s3") {
|
|
@@ -9396,8 +9676,8 @@ async function runSync(db, options) {
|
|
|
9396
9676
|
}
|
|
9397
9677
|
|
|
9398
9678
|
// src/commands/clean.ts
|
|
9399
|
-
import { unlinkSync as unlinkSync3, existsSync as
|
|
9400
|
-
import { join as
|
|
9679
|
+
import { unlinkSync as unlinkSync3, existsSync as existsSync11 } from "fs";
|
|
9680
|
+
import { join as join12 } from "path";
|
|
9401
9681
|
function cleanOldData(db, days) {
|
|
9402
9682
|
const cutoff = Date.now() - days * 864e5;
|
|
9403
9683
|
const recordsResult = db.prepare("DELETE FROM records WHERE ts < ?").run(cutoff);
|
|
@@ -9418,9 +9698,9 @@ function cleanAll(db) {
|
|
|
9418
9698
|
const syncedResult = db.prepare("DELETE FROM synced_records").run();
|
|
9419
9699
|
const syncStateResult = db.prepare("DELETE FROM sync_record_state").run();
|
|
9420
9700
|
const tombstonesResult = db.prepare("DELETE FROM sync_tombstones").run();
|
|
9421
|
-
const watermarkPath =
|
|
9701
|
+
const watermarkPath = join12(AIUSAGE_DIR, "watermark.json");
|
|
9422
9702
|
let watermarkRemoved = false;
|
|
9423
|
-
if (
|
|
9703
|
+
if (existsSync11(watermarkPath)) {
|
|
9424
9704
|
unlinkSync3(watermarkPath);
|
|
9425
9705
|
watermarkRemoved = true;
|
|
9426
9706
|
}
|
|
@@ -9455,7 +9735,7 @@ function createBackend2(config) {
|
|
|
9455
9735
|
return new GitSyncBackend({
|
|
9456
9736
|
repo: config.sync.repo,
|
|
9457
9737
|
token,
|
|
9458
|
-
cacheDir:
|
|
9738
|
+
cacheDir: join12(AIUSAGE_DIR, "sync-repo")
|
|
9459
9739
|
});
|
|
9460
9740
|
}
|
|
9461
9741
|
if (config.sync.backend === "s3") {
|
|
@@ -9788,7 +10068,7 @@ var RuntimeSettingsController = class {
|
|
|
9788
10068
|
|
|
9789
10069
|
// src/commands/serve.ts
|
|
9790
10070
|
var MAX_PORT_ATTEMPTS = 10;
|
|
9791
|
-
var PORT_FILE =
|
|
10071
|
+
var PORT_FILE = join13(AIUSAGE_DIR, ".serve-port");
|
|
9792
10072
|
var MIME_TYPES = {
|
|
9793
10073
|
".html": "text/html",
|
|
9794
10074
|
".js": "application/javascript",
|
|
@@ -9863,11 +10143,11 @@ function serve(options) {
|
|
|
9863
10143
|
getDbWriteQueueStatus: () => dbWriteQueue.getStatus()
|
|
9864
10144
|
});
|
|
9865
10145
|
const webBuildDir = (() => {
|
|
9866
|
-
const prodDir =
|
|
9867
|
-
if (
|
|
9868
|
-
return
|
|
10146
|
+
const prodDir = join13(dirname6(fileURLToPath(import.meta.url)), "web");
|
|
10147
|
+
if (existsSync12(prodDir)) return prodDir;
|
|
10148
|
+
return join13(dirname6(fileURLToPath(import.meta.url)), "..", "..", "..", "web", "build");
|
|
9869
10149
|
})();
|
|
9870
|
-
if (!
|
|
10150
|
+
if (!existsSync12(webBuildDir)) {
|
|
9871
10151
|
console.error("Web dashboard not found. Reinstall the package: npm install -g @juliantanx/aiusage");
|
|
9872
10152
|
process.exit(1);
|
|
9873
10153
|
}
|
|
@@ -9877,19 +10157,19 @@ function serve(options) {
|
|
|
9877
10157
|
apiServer.emit("request", req, res);
|
|
9878
10158
|
return;
|
|
9879
10159
|
}
|
|
9880
|
-
if (
|
|
9881
|
-
let filePath =
|
|
10160
|
+
if (existsSync12(webBuildDir)) {
|
|
10161
|
+
let filePath = join13(webBuildDir, url.pathname);
|
|
9882
10162
|
try {
|
|
9883
10163
|
if (statSync4(filePath).isDirectory()) {
|
|
9884
|
-
filePath =
|
|
10164
|
+
filePath = join13(filePath, "index.html");
|
|
9885
10165
|
}
|
|
9886
10166
|
} catch {
|
|
9887
10167
|
}
|
|
9888
|
-
if (!
|
|
9889
|
-
filePath =
|
|
10168
|
+
if (!existsSync12(filePath)) {
|
|
10169
|
+
filePath = join13(webBuildDir, "index.html");
|
|
9890
10170
|
}
|
|
9891
10171
|
try {
|
|
9892
|
-
const content =
|
|
10172
|
+
const content = readFileSync11(filePath);
|
|
9893
10173
|
const ext = extname2(filePath);
|
|
9894
10174
|
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
9895
10175
|
res.writeHead(200, { "Content-Type": contentType });
|
|
@@ -10071,9 +10351,9 @@ Consent recorded for schema v1.`
|
|
|
10071
10351
|
}
|
|
10072
10352
|
|
|
10073
10353
|
// src/commands/status.ts
|
|
10074
|
-
import { statSync as statSync5, existsSync as
|
|
10354
|
+
import { statSync as statSync5, existsSync as existsSync13 } from "fs";
|
|
10075
10355
|
import { hostname as hostname3 } from "os";
|
|
10076
|
-
import { join as
|
|
10356
|
+
import { join as join14 } from "path";
|
|
10077
10357
|
function formatFileSize(bytes) {
|
|
10078
10358
|
if (bytes < 1024) return `${bytes} B`;
|
|
10079
10359
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
@@ -10091,9 +10371,9 @@ function generateStatus(db) {
|
|
|
10091
10371
|
const schemaVersion = db.prepare("SELECT version FROM schema_version ORDER BY version DESC LIMIT 1").get()?.version ?? 0;
|
|
10092
10372
|
const tableCount = db.prepare("SELECT COUNT(*) as count FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'").get()?.count ?? 0;
|
|
10093
10373
|
const viewCount = db.prepare("SELECT COUNT(*) as count FROM sqlite_master WHERE type = 'view'").get()?.count ?? 0;
|
|
10094
|
-
const sizePath =
|
|
10374
|
+
const sizePath = join14(AIUSAGE_DIR, "cache.db");
|
|
10095
10375
|
let databaseSize = "0 B";
|
|
10096
|
-
if (
|
|
10376
|
+
if (existsSync13(sizePath)) {
|
|
10097
10377
|
try {
|
|
10098
10378
|
const stat2 = statSync5(sizePath);
|
|
10099
10379
|
databaseSize = formatFileSize(stat2.size);
|
|
@@ -10316,10 +10596,10 @@ var SyncProgressReporter = class {
|
|
|
10316
10596
|
};
|
|
10317
10597
|
|
|
10318
10598
|
// src/commands/widget.ts
|
|
10319
|
-
import { existsSync as
|
|
10599
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
10320
10600
|
import { spawn, execSync as execSync2 } from "child_process";
|
|
10321
|
-
import { join as
|
|
10322
|
-
var WIDGET_PID_PATH =
|
|
10601
|
+
import { join as join15 } from "path";
|
|
10602
|
+
var WIDGET_PID_PATH = join15(AIUSAGE_DIR, "widget.pid");
|
|
10323
10603
|
async function launchWidget() {
|
|
10324
10604
|
if (isWidgetRunning()) {
|
|
10325
10605
|
console.log("aiusage widget is already running in the system tray.");
|
|
@@ -10341,10 +10621,10 @@ async function launchWidget() {
|
|
|
10341
10621
|
console.log("aiusage widget started.");
|
|
10342
10622
|
}
|
|
10343
10623
|
function isWidgetRunning() {
|
|
10344
|
-
if (!
|
|
10624
|
+
if (!existsSync14(WIDGET_PID_PATH)) return false;
|
|
10345
10625
|
let pid;
|
|
10346
10626
|
try {
|
|
10347
|
-
pid = parseInt(
|
|
10627
|
+
pid = parseInt(readFileSync12(WIDGET_PID_PATH, "utf-8").trim(), 10);
|
|
10348
10628
|
} catch {
|
|
10349
10629
|
return false;
|
|
10350
10630
|
}
|
|
@@ -10388,10 +10668,10 @@ function getDeviceName2() {
|
|
|
10388
10668
|
}
|
|
10389
10669
|
function getCliVersion2() {
|
|
10390
10670
|
try {
|
|
10391
|
-
const { readFileSync:
|
|
10392
|
-
const { join:
|
|
10393
|
-
const pkgPath =
|
|
10394
|
-
const pkg = JSON.parse(
|
|
10671
|
+
const { readFileSync: readFileSync14 } = __require("fs");
|
|
10672
|
+
const { join: join18 } = __require("path");
|
|
10673
|
+
const pkgPath = join18(__dirname, "../../package.json");
|
|
10674
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
10395
10675
|
return pkg.version || "0.0.0";
|
|
10396
10676
|
} catch {
|
|
10397
10677
|
return "0.0.0";
|
|
@@ -10560,8 +10840,8 @@ function formatTokens(value) {
|
|
|
10560
10840
|
return Number(value).toLocaleString();
|
|
10561
10841
|
}
|
|
10562
10842
|
function formatCost(value) {
|
|
10563
|
-
const
|
|
10564
|
-
return Number.isFinite(
|
|
10843
|
+
const num4 = Number(value);
|
|
10844
|
+
return Number.isFinite(num4) ? `$${num4.toFixed(4)}` : "$0.0000";
|
|
10565
10845
|
}
|
|
10566
10846
|
function formatUpdatedAt(value) {
|
|
10567
10847
|
const date = new Date(value);
|
|
@@ -10635,16 +10915,16 @@ async function runLeaderboardView(options = {}) {
|
|
|
10635
10915
|
// src/commands/menu.ts
|
|
10636
10916
|
import { createInterface } from "readline";
|
|
10637
10917
|
import { spawn as spawn2, spawnSync as spawnSync2, execSync as execSync4 } from "child_process";
|
|
10638
|
-
import { existsSync as
|
|
10639
|
-
import { join as
|
|
10918
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
10919
|
+
import { join as join16 } from "path";
|
|
10640
10920
|
var DEFAULT_PORT = 3847;
|
|
10641
|
-
var PORT_FILE2 =
|
|
10921
|
+
var PORT_FILE2 = join16(AIUSAGE_DIR, ".serve-port");
|
|
10642
10922
|
function clearScreen() {
|
|
10643
10923
|
process.stdout.write("\x1Bc");
|
|
10644
10924
|
}
|
|
10645
10925
|
function getPort() {
|
|
10646
|
-
if (
|
|
10647
|
-
const raw =
|
|
10926
|
+
if (existsSync15(PORT_FILE2)) {
|
|
10927
|
+
const raw = readFileSync13(PORT_FILE2, "utf-8").trim();
|
|
10648
10928
|
const n = parseInt(raw, 10);
|
|
10649
10929
|
if (!isNaN(n) && n > 0) return n;
|
|
10650
10930
|
}
|
|
@@ -11133,10 +11413,10 @@ ${dashboardStatusLine()}
|
|
|
11133
11413
|
}
|
|
11134
11414
|
|
|
11135
11415
|
// src/cli.ts
|
|
11136
|
-
import { join as
|
|
11137
|
-
var DB_PATH2 =
|
|
11416
|
+
import { join as join17 } from "path";
|
|
11417
|
+
var DB_PATH2 = join17(AIUSAGE_DIR, "cache.db");
|
|
11138
11418
|
var program = new Command();
|
|
11139
|
-
program.name("aiusage").version(true ? "1.5.
|
|
11419
|
+
program.name("aiusage").version(true ? "1.5.9" : "dev").description("CLI tool for AI usage statistics");
|
|
11140
11420
|
program.action(() => {
|
|
11141
11421
|
const unknownCommand = program.args.find((arg) => typeof arg === "string");
|
|
11142
11422
|
if (unknownCommand) {
|
|
@@ -11423,7 +11703,7 @@ function hasWidget() {
|
|
|
11423
11703
|
}
|
|
11424
11704
|
}
|
|
11425
11705
|
function buildPm2Config(includeWidget) {
|
|
11426
|
-
const wrapperPath =
|
|
11706
|
+
const wrapperPath = join17(AIUSAGE_DIR, "pm2-server.cjs");
|
|
11427
11707
|
writeFileSync7(wrapperPath, [
|
|
11428
11708
|
`// Auto-generated by aiusage pm2-start \u2014 do not edit`,
|
|
11429
11709
|
`const { execSync, spawn } = require('child_process')`,
|
|
@@ -11470,7 +11750,7 @@ ${apps.join(",\n")}
|
|
|
11470
11750
|
program.command("pm2-setup").description("Generate ecosystem.config.cjs for PM2 background services").option("--server-only", "Only include server, skip widget").action((options) => {
|
|
11471
11751
|
const includeWidget = options.serverOnly ? false : hasWidget();
|
|
11472
11752
|
const config = buildPm2Config(includeWidget);
|
|
11473
|
-
const configPath =
|
|
11753
|
+
const configPath = join17(AIUSAGE_DIR, "ecosystem.config.cjs");
|
|
11474
11754
|
writeFileSync7(configPath, config, "utf-8");
|
|
11475
11755
|
console.log(`Config created: ${configPath}`);
|
|
11476
11756
|
if (!includeWidget) {
|
|
@@ -11480,7 +11760,7 @@ program.command("pm2-setup").description("Generate ecosystem.config.cjs for PM2
|
|
|
11480
11760
|
program.command("pm2-start").description("Setup and start PM2 background services (requires pm2 installed globally)").option("--server-only", "Only start server, skip widget").action((options) => {
|
|
11481
11761
|
const includeWidget = options.serverOnly ? false : hasWidget();
|
|
11482
11762
|
const config = buildPm2Config(includeWidget);
|
|
11483
|
-
const configPath =
|
|
11763
|
+
const configPath = join17(AIUSAGE_DIR, "ecosystem.config.cjs");
|
|
11484
11764
|
writeFileSync7(configPath, config, "utf-8");
|
|
11485
11765
|
console.log(`Config: ${configPath}`);
|
|
11486
11766
|
try {
|