tokentracker-cli 0.69.0 → 0.70.0
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/dashboard/dist/assets/{ActivityHeatmap-Bqy-meR1.js → ActivityHeatmap-HkjamKhQ.js} +1 -1
- package/dashboard/dist/assets/{Card-DbzltTXg.js → Card-BAu_q1P8.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-CumafhRo.js → CommunityStatsModal-Cvb5lxum.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-CfmWYFAl.js → ConfirmModal-Ckc0m2pF.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-beIJMVvO.js → DashboardPage-CdTpYDO0.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-Cb_cMnJj.js → DevicePage-BIQ25ul5.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-245t1wj3.js → DialogClose-DiOWMlOM.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-DNyMczI_.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-C_S_W-qN.js → DialogTitle-BrR7t4i0.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-9qSEABKW.js → FadeIn-CkFXh9qu.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-C42NExcO.js → HeaderGithubStar-BqKaM3ph.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-C_LVvRK3.js → IpCheckPage-k4RZKPiL.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-1ng5HTtU.js → LandingPage-Duj0yl_C.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-Dud2isij.js → LeaderboardAvatar-dILRiEW5.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-BVLHknpa.js → LeaderboardPage-yWly0uWz.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-creqSCBC.js → LeaderboardProfileModal-DyBDRAKE.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfilePage-CtCiW1ZR.js → LeaderboardProfilePage-CpXppYzc.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-DXhNfD6c.js → LimitsPage-BG86Jtvl.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-CupEXgLA.js → LocalOnlyNotice-CYCe0N_S.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-Bvb6khA_.js → LoginPage-D9XhznZH.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-1RwMOuZR.js → PopoverPopup-C8iZhys6.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-C1t2TVSo.js → ResetPasswordPage-B8mXGzFe.js} +1 -1
- package/dashboard/dist/assets/{Select-35dakfdf.js → Select-Wgv6XKdi.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-Bn80bCzm.js → SelectItemText-rxW6xT66.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-BtNI2EB5.js → SettingsPage-DRpIQZJx.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-DrtaOCt8.js → SkillsPage-Bl_FJwQu.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-DBP3CcAv.js → WidgetsPage-B0AlwiRQ.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-Dxzewd0b.js → WrappedPage-C-ojLydF.js} +1 -1
- package/dashboard/dist/assets/{agent-logos-DXWdLSyr.js → agent-logos-wKEmyIg8.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-CAv_ofGl.js → arrow-up-right-BwT8ykRA.js} +1 -1
- package/dashboard/dist/assets/check-gQkfPY06.js +1 -0
- package/dashboard/dist/assets/{copy-CkAl55Gc.js → copy-DvlBby5m.js} +1 -1
- package/dashboard/dist/assets/{download-CHuHPQRG.js → download-C7MsK41l.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-BhF7ktma.js → format-tokens-CtXNYfM2.js} +1 -1
- package/dashboard/dist/assets/{info-4RqgE-Sk.js → info-DXSmyEHB.js} +1 -1
- package/dashboard/dist/assets/{main-Cy1-zTjI.js → main-DPyNBrFb.js} +3 -3
- package/dashboard/dist/assets/main-DXM2UPde.css +1 -0
- package/dashboard/dist/assets/{use-community-stats-BHARwgjJ.js → use-community-stats-BC4Va1tk.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-D6TuBuXc.js → use-limits-display-prefs-BL-NCgDn.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-8aQ6BEe6.js → use-native-settings-D0x_4uCT.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-B0XApham.js → use-ordered-list-DEi9FKn9.js} +1 -1
- package/dashboard/dist/assets/{use-quality-per-dollar-pref-DNa5uaGP.js → use-quality-per-dollar-pref-BIf1mWJK.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-C1V3apqc.js → use-usage-limits-CosJF8AC.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-BEkNDMgc.js → useCurrency-4cyeApuY.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-BRpWM_0H.js → useScrollLock-D0Td7eQX.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/share.html +2 -2
- package/package.json +1 -1
- package/src/commands/status.js +8 -5
- package/src/commands/sync.js +1 -1
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +502 -110
- package/dashboard/dist/assets/DialogDescription-CMG2f0Xr.js +0 -1
- package/dashboard/dist/assets/check-BzPtb9x7.js +0 -1
- package/dashboard/dist/assets/main-CjU-7igC.css +0 -1
package/src/lib/rollout.js
CHANGED
|
@@ -5,7 +5,7 @@ const readline = require("node:readline");
|
|
|
5
5
|
|
|
6
6
|
const crypto = require("node:crypto");
|
|
7
7
|
const { ensureDir } = require("./fs");
|
|
8
|
-
const { readSqliteJsonRows } = require("./sqlite-reader");
|
|
8
|
+
const { readSqliteJsonRows, readSqliteJsonRowsAsync } = require("./sqlite-reader");
|
|
9
9
|
const wsl = require("./wsl-probe");
|
|
10
10
|
const { resolveInstallPaths } = require("./install-resolver");
|
|
11
11
|
|
|
@@ -3049,6 +3049,26 @@ function toNonNegativeInt(v) {
|
|
|
3049
3049
|
return Math.floor(n);
|
|
3050
3050
|
}
|
|
3051
3051
|
|
|
3052
|
+
function firstPresentNonNegativeInt(values) {
|
|
3053
|
+
for (const value of values) {
|
|
3054
|
+
if (value !== undefined && value !== null) {
|
|
3055
|
+
return toNonNegativeInt(value);
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
return 0;
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
function firstPositiveOrPresentNonNegativeInt(values) {
|
|
3062
|
+
let firstPresent = null;
|
|
3063
|
+
for (const value of values) {
|
|
3064
|
+
if (value === undefined || value === null) continue;
|
|
3065
|
+
const n = toNonNegativeInt(value);
|
|
3066
|
+
if (firstPresent === null) firstPresent = n;
|
|
3067
|
+
if (n > 0) return n;
|
|
3068
|
+
}
|
|
3069
|
+
return firstPresent === null ? 0 : firstPresent;
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3052
3072
|
function coerceEpochMs(v) {
|
|
3053
3073
|
const n = Number(v);
|
|
3054
3074
|
if (!Number.isFinite(n) || n <= 0) return 0;
|
|
@@ -5530,30 +5550,131 @@ function resolveCodebuddyDefaultModel(env = process.env) {
|
|
|
5530
5550
|
|
|
5531
5551
|
function resolveCodebuddyProjectFiles(env = process.env) {
|
|
5532
5552
|
const codebuddyHome = resolveCodebuddyHome(env);
|
|
5533
|
-
if (!codebuddyHome) return [];
|
|
5534
|
-
const projectsDir = path.join(codebuddyHome, "projects");
|
|
5535
|
-
if (!fssync.existsSync(projectsDir)) return [];
|
|
5536
5553
|
const files = [];
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5554
|
+
|
|
5555
|
+
// 1. Recursive JSONL scan in codebuddyHome/projects
|
|
5556
|
+
if (codebuddyHome) {
|
|
5557
|
+
const projectsDir = path.join(codebuddyHome, "projects");
|
|
5558
|
+
if (fssync.existsSync(projectsDir)) {
|
|
5559
|
+
const walkJsonl = (dir) => {
|
|
5560
|
+
let entries;
|
|
5561
|
+
try { entries = fssync.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
5562
|
+
for (const entry of entries) {
|
|
5563
|
+
const full = path.join(dir, entry.name);
|
|
5564
|
+
let isDir = entry.isDirectory();
|
|
5565
|
+
let isFile = entry.isFile();
|
|
5566
|
+
if (!isDir && !isFile) {
|
|
5567
|
+
try {
|
|
5568
|
+
const st = fssync.statSync(full);
|
|
5569
|
+
isDir = st.isDirectory();
|
|
5570
|
+
isFile = st.isFile();
|
|
5571
|
+
} catch { continue; }
|
|
5572
|
+
}
|
|
5573
|
+
if (isDir) walkJsonl(full);
|
|
5574
|
+
else if (isFile && entry.name.endsWith(".jsonl")) files.push(full);
|
|
5575
|
+
}
|
|
5576
|
+
};
|
|
5577
|
+
walkJsonl(projectsDir);
|
|
5578
|
+
}
|
|
5579
|
+
}
|
|
5580
|
+
|
|
5581
|
+
// 2. Active IDE extension logs scan
|
|
5582
|
+
const logRoots = [];
|
|
5583
|
+
const home = env.HOME || require("node:os").homedir();
|
|
5584
|
+
|
|
5585
|
+
if (process.platform === "darwin") {
|
|
5586
|
+
const appSupport = path.join(home, "Library", "Application Support");
|
|
5587
|
+
logRoots.push({ dir: path.join(appSupport, "CodeBuddy CN", "logs"), pattern: "codebuddy-extension-log" });
|
|
5588
|
+
logRoots.push({ dir: path.join(appSupport, "Code", "logs"), pattern: "codebuddy-extension-log" });
|
|
5589
|
+
logRoots.push({ dir: path.join(appSupport, "CodeBuddyExtension", "Logs", "CodeBuddyIDE"), pattern: "*.log" });
|
|
5590
|
+
logRoots.push({ dir: path.join(appSupport, "CodeBuddyExtension", "Logs", "VSCode"), pattern: "*.log" });
|
|
5591
|
+
} else if (process.platform === "win32") {
|
|
5592
|
+
const appData = env.APPDATA || path.join(home, "AppData", "Roaming");
|
|
5593
|
+
const localAppData = env.LOCALAPPDATA || path.join(home, "AppData", "Local");
|
|
5594
|
+
logRoots.push({ dir: path.join(appData, "CodeBuddy CN", "logs"), pattern: "codebuddy-extension-log" });
|
|
5595
|
+
logRoots.push({ dir: path.join(appData, "Code", "logs"), pattern: "codebuddy-extension-log" });
|
|
5596
|
+
logRoots.push({ dir: path.join(localAppData, "CodeBuddyExtension", "Logs", "CodeBuddyIDE"), pattern: "*.log" });
|
|
5597
|
+
logRoots.push({ dir: path.join(localAppData, "CodeBuddyExtension", "Logs", "VSCode"), pattern: "*.log" });
|
|
5598
|
+
} else {
|
|
5599
|
+
const xdgConfig = env.XDG_CONFIG_HOME || path.join(home, ".config");
|
|
5600
|
+
const xdgData = env.XDG_DATA_HOME || path.join(home, ".local", "share");
|
|
5601
|
+
logRoots.push({ dir: path.join(xdgConfig, "CodeBuddy CN", "logs"), pattern: "codebuddy-extension-log" });
|
|
5602
|
+
logRoots.push({ dir: path.join(xdgConfig, "Code", "logs"), pattern: "codebuddy-extension-log" });
|
|
5603
|
+
logRoots.push({ dir: path.join(xdgData, "CodeBuddyExtension", "Logs", "CodeBuddyIDE"), pattern: "*.log" });
|
|
5604
|
+
logRoots.push({ dir: path.join(xdgData, "CodeBuddyExtension", "Logs", "VSCode"), pattern: "*.log" });
|
|
5605
|
+
}
|
|
5606
|
+
|
|
5607
|
+
for (const root of logRoots) {
|
|
5608
|
+
if (!fssync.existsSync(root.dir)) continue;
|
|
5609
|
+
const walkLogs = (dir) => {
|
|
5543
5610
|
let entries;
|
|
5544
|
-
try { entries = fssync.readdirSync(
|
|
5611
|
+
try { entries = fssync.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
5545
5612
|
for (const entry of entries) {
|
|
5546
|
-
|
|
5547
|
-
|
|
5613
|
+
const full = path.join(dir, entry.name);
|
|
5614
|
+
let isDir = entry.isDirectory();
|
|
5615
|
+
let isFile = entry.isFile();
|
|
5616
|
+
if (!isDir && !isFile) {
|
|
5617
|
+
try {
|
|
5618
|
+
const st = fssync.statSync(full);
|
|
5619
|
+
isDir = st.isDirectory();
|
|
5620
|
+
isFile = st.isFile();
|
|
5621
|
+
} catch { continue; }
|
|
5622
|
+
}
|
|
5623
|
+
if (isDir) {
|
|
5624
|
+
walkLogs(full);
|
|
5625
|
+
} else if (isFile && entry.name.endsWith(".log")) {
|
|
5626
|
+
if (root.pattern === "*.log") {
|
|
5627
|
+
files.push(full);
|
|
5628
|
+
} else if (root.pattern === "codebuddy-extension-log") {
|
|
5629
|
+
if (full.toLowerCase().includes("tencent-cloud.coding-copilot")) {
|
|
5630
|
+
files.push(full);
|
|
5631
|
+
}
|
|
5632
|
+
}
|
|
5633
|
+
}
|
|
5548
5634
|
}
|
|
5549
|
-
}
|
|
5550
|
-
|
|
5551
|
-
// ignore — return what we have
|
|
5635
|
+
};
|
|
5636
|
+
walkLogs(root.dir);
|
|
5552
5637
|
}
|
|
5638
|
+
|
|
5553
5639
|
files.sort((a, b) => a.localeCompare(b));
|
|
5554
5640
|
return files;
|
|
5555
5641
|
}
|
|
5556
5642
|
|
|
5643
|
+
function getBracketValueAfter(line, marker) {
|
|
5644
|
+
const parts = line.split(marker);
|
|
5645
|
+
if (parts.length < 2) return null;
|
|
5646
|
+
const after = parts[1];
|
|
5647
|
+
const start = after.indexOf("[");
|
|
5648
|
+
if (start === -1) return null;
|
|
5649
|
+
const afterOpen = after.slice(start + 1);
|
|
5650
|
+
const end = afterOpen.indexOf("]");
|
|
5651
|
+
if (end === -1) return null;
|
|
5652
|
+
return afterOpen.slice(0, end).trim();
|
|
5653
|
+
}
|
|
5654
|
+
|
|
5655
|
+
function parseLogTimestampMs(line, fallbackTs) {
|
|
5656
|
+
let raw = "";
|
|
5657
|
+
if (line.startsWith("[")) {
|
|
5658
|
+
const endIdx = line.indexOf("]");
|
|
5659
|
+
if (endIdx !== -1) {
|
|
5660
|
+
raw = line.slice(1, endIdx).trim();
|
|
5661
|
+
}
|
|
5662
|
+
} else {
|
|
5663
|
+
const idx = line.indexOf(" [");
|
|
5664
|
+
if (idx !== -1) {
|
|
5665
|
+
raw = line.slice(0, idx).trim();
|
|
5666
|
+
} else {
|
|
5667
|
+
raw = line.slice(0, 23).trim();
|
|
5668
|
+
}
|
|
5669
|
+
}
|
|
5670
|
+
let ts = Date.parse(raw);
|
|
5671
|
+
if (isNaN(ts)) {
|
|
5672
|
+
const normalized = raw.replace(/\//g, '-');
|
|
5673
|
+
ts = Date.parse(normalized);
|
|
5674
|
+
}
|
|
5675
|
+
return isNaN(ts) ? fallbackTs : ts;
|
|
5676
|
+
}
|
|
5677
|
+
|
|
5557
5678
|
async function parseCodebuddyIncremental({
|
|
5558
5679
|
projectFiles,
|
|
5559
5680
|
cursors,
|
|
@@ -5572,6 +5693,10 @@ async function parseCodebuddyIncremental({
|
|
|
5572
5693
|
codebuddyState.fileOffsets && typeof codebuddyState.fileOffsets === "object"
|
|
5573
5694
|
? { ...codebuddyState.fileOffsets }
|
|
5574
5695
|
: {};
|
|
5696
|
+
const logModelsByAgent =
|
|
5697
|
+
codebuddyState.logModelsByAgent && typeof codebuddyState.logModelsByAgent === "object"
|
|
5698
|
+
? { ...codebuddyState.logModelsByAgent }
|
|
5699
|
+
: {};
|
|
5575
5700
|
|
|
5576
5701
|
const files = Array.isArray(projectFiles)
|
|
5577
5702
|
? projectFiles
|
|
@@ -5602,8 +5727,6 @@ async function parseCodebuddyIncremental({
|
|
|
5602
5727
|
const prevEntry = fileOffsets[filePath] || {};
|
|
5603
5728
|
const prevSize = Number(prevEntry.size) || 0;
|
|
5604
5729
|
const prevIno = prevEntry.ino;
|
|
5605
|
-
// Re-read from start if file shrunk (truncate/rewrite) or inode changed
|
|
5606
|
-
// (file deleted + recreated). Otherwise pick up after the last read offset.
|
|
5607
5730
|
const inodeChanged = typeof prevIno === "number" && prevIno !== stat.ino;
|
|
5608
5731
|
const startOffset = stat.size < prevSize || inodeChanged ? 0 : prevSize;
|
|
5609
5732
|
if (stat.size <= startOffset) continue;
|
|
@@ -5617,111 +5740,244 @@ async function parseCodebuddyIncremental({
|
|
|
5617
5740
|
} catch { continue; }
|
|
5618
5741
|
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
5619
5742
|
|
|
5743
|
+
const isLogFile = filePath.endsWith(".log");
|
|
5744
|
+
const modelsByAgent = new Map();
|
|
5745
|
+
const agentModelKey = (agentId) => `${filePath}::${agentId}`;
|
|
5746
|
+
|
|
5620
5747
|
for await (const line of rl) {
|
|
5621
5748
|
if (!line || !line.trim()) continue;
|
|
5622
|
-
let entry;
|
|
5623
|
-
try { entry = JSON.parse(line); } catch { continue; }
|
|
5624
5749
|
|
|
5625
|
-
|
|
5626
|
-
|
|
5750
|
+
if (isLogFile) {
|
|
5751
|
+
if (line.includes("[CraftInvokableAgent]") && line.includes("Model prepared:")) {
|
|
5752
|
+
const agentId = getBracketValueAfter(line, "[CraftInvokableAgent]");
|
|
5753
|
+
const marker = "Model prepared:";
|
|
5754
|
+
const idx = line.indexOf(marker);
|
|
5755
|
+
if (agentId && idx !== -1) {
|
|
5756
|
+
const afterMarker = line.slice(idx + marker.length).trim();
|
|
5757
|
+
let modelId = afterMarker;
|
|
5758
|
+
const openParen = afterMarker.lastIndexOf("(");
|
|
5759
|
+
if (openParen !== -1) {
|
|
5760
|
+
const tail = afterMarker.slice(openParen + 1);
|
|
5761
|
+
const closeParen = tail.indexOf(")");
|
|
5762
|
+
if (closeParen !== -1) {
|
|
5763
|
+
const inner = tail.slice(0, closeParen).trim();
|
|
5764
|
+
if (inner) modelId = inner;
|
|
5765
|
+
}
|
|
5766
|
+
}
|
|
5767
|
+
modelsByAgent.set(agentId, modelId);
|
|
5768
|
+
logModelsByAgent[agentModelKey(agentId)] = modelId;
|
|
5769
|
+
}
|
|
5770
|
+
continue;
|
|
5771
|
+
}
|
|
5627
5772
|
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5773
|
+
if (!line.includes("[AgentReporter]") || !line.includes("Agent execution successful with usage:")) {
|
|
5774
|
+
continue;
|
|
5775
|
+
}
|
|
5631
5776
|
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
const sessionId =
|
|
5635
|
-
typeof entry.sessionId === "string" && entry.sessionId
|
|
5636
|
-
? entry.sessionId
|
|
5637
|
-
: path.basename(filePath, ".jsonl");
|
|
5638
|
-
const tsMs =
|
|
5639
|
-
Number.isFinite(Number(entry.timestamp)) && Number(entry.timestamp) > 0
|
|
5640
|
-
? Number(entry.timestamp)
|
|
5641
|
-
: null;
|
|
5642
|
-
const messageId =
|
|
5643
|
-
typeof entry.uuid === "string" && entry.uuid
|
|
5644
|
-
? entry.uuid
|
|
5645
|
-
: typeof entry.id === "string" && entry.id
|
|
5646
|
-
? entry.id
|
|
5647
|
-
: tsMs != null
|
|
5648
|
-
? `${sessionId}:${tsMs}`
|
|
5649
|
-
: null;
|
|
5650
|
-
if (!messageId) continue;
|
|
5651
|
-
if (seenIds.has(messageId)) continue;
|
|
5777
|
+
const agentId = getBracketValueAfter(line, "[AgentReporter]");
|
|
5778
|
+
if (!agentId) continue;
|
|
5652
5779
|
|
|
5653
|
-
|
|
5780
|
+
const marker = "Agent execution successful with usage:";
|
|
5781
|
+
const usageParts = line.split(marker);
|
|
5782
|
+
if (usageParts.length < 2) continue;
|
|
5654
5783
|
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
rawUsage.prompt_tokens_details && typeof rawUsage.prompt_tokens_details === "object"
|
|
5659
|
-
? rawUsage.prompt_tokens_details
|
|
5660
|
-
: {};
|
|
5661
|
-
const cachedTokens = toNonNegativeInt(details.cached_tokens);
|
|
5662
|
-
// Anthropic-style mirror; CodeBuddy emits these too even if usually 0.
|
|
5663
|
-
const cacheReadAlt = toNonNegativeInt(rawUsage.cache_read_input_tokens);
|
|
5664
|
-
const cacheCreation = toNonNegativeInt(rawUsage.cache_creation_input_tokens);
|
|
5665
|
-
const reasoningTokens = toNonNegativeInt(details.reasoning_tokens);
|
|
5666
|
-
|
|
5667
|
-
// CRITICAL: prompt_tokens is OpenAI-style and INCLUDES cached.
|
|
5668
|
-
// Subtract cached so input_tokens is pure non-cached input — matches
|
|
5669
|
-
// the repo's normalization convention (see CLAUDE.md "Token
|
|
5670
|
-
// Normalization Convention"). cache_read takes the larger of the two
|
|
5671
|
-
// mirrored fields (rawUsage.cache_read_input_tokens vs
|
|
5672
|
-
// prompt_tokens_details.cached_tokens) since CodeBuddy populates one
|
|
5673
|
-
// or the other depending on upstream provider.
|
|
5674
|
-
const cacheRead = Math.max(cachedTokens, cacheReadAlt);
|
|
5675
|
-
const inputTokens = Math.max(0, promptTokens - cacheRead);
|
|
5784
|
+
const usageJsonRaw = usageParts[1].trim();
|
|
5785
|
+
const endBrace = usageJsonRaw.lastIndexOf("}");
|
|
5786
|
+
if (endBrace === -1) continue;
|
|
5676
5787
|
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
cacheCreation === 0
|
|
5682
|
-
) {
|
|
5683
|
-
seenIds.add(messageId);
|
|
5684
|
-
continue;
|
|
5685
|
-
}
|
|
5788
|
+
let usage;
|
|
5789
|
+
try {
|
|
5790
|
+
usage = JSON.parse(usageJsonRaw.slice(0, endBrace + 1));
|
|
5791
|
+
} catch { continue; }
|
|
5686
5792
|
|
|
5687
|
-
|
|
5793
|
+
if (!usage || typeof usage !== "object") continue;
|
|
5794
|
+
|
|
5795
|
+
const inputRaw = firstPresentNonNegativeInt([
|
|
5796
|
+
usage.cachedMissTokens,
|
|
5797
|
+
usage.cacheMissTokens,
|
|
5798
|
+
usage.input_tokens,
|
|
5799
|
+
usage.inputTokens,
|
|
5800
|
+
usage.prompt_tokens,
|
|
5801
|
+
]);
|
|
5802
|
+
const outputRaw = firstPresentNonNegativeInt([
|
|
5803
|
+
usage.output_tokens,
|
|
5804
|
+
usage.outputTokens,
|
|
5805
|
+
usage.completion_tokens,
|
|
5806
|
+
]);
|
|
5807
|
+
const cacheReadRaw = firstPositiveOrPresentNonNegativeInt([
|
|
5808
|
+
usage.cache_read_input_tokens,
|
|
5809
|
+
usage.cacheReadInputTokens,
|
|
5810
|
+
usage.cacheTokens,
|
|
5811
|
+
usage.prompt_cache_hit_tokens,
|
|
5812
|
+
usage.cached_tokens,
|
|
5813
|
+
]);
|
|
5814
|
+
const cacheCreationRaw = firstPositiveOrPresentNonNegativeInt([
|
|
5815
|
+
usage.cache_creation_input_tokens,
|
|
5816
|
+
usage.cacheCreationInputTokens,
|
|
5817
|
+
usage.cachedWriteTokens,
|
|
5818
|
+
usage.prompt_cache_write_tokens,
|
|
5819
|
+
]);
|
|
5820
|
+
const reasoningRaw = firstPresentNonNegativeInt([
|
|
5821
|
+
usage.completion_thinking_tokens,
|
|
5822
|
+
usage.completionThinkingTokens,
|
|
5823
|
+
usage.reasoningTokens,
|
|
5824
|
+
]);
|
|
5825
|
+
|
|
5826
|
+
const tsMs = parseLogTimestampMs(line, stat.mtimeMs);
|
|
5827
|
+
const dedupSecond = Math.floor(tsMs / 1000);
|
|
5828
|
+
const messageId = `codebuddy:extension-log:${agentId}:${dedupSecond}:${inputRaw}:${outputRaw}:${cacheReadRaw}:${cacheCreationRaw}:${reasoningRaw}`;
|
|
5829
|
+
|
|
5830
|
+
if (seenIds.has(messageId)) continue;
|
|
5831
|
+
recordsProcessed++;
|
|
5832
|
+
|
|
5833
|
+
let inputTokens = toNonNegativeInt(inputRaw);
|
|
5834
|
+
const completionTokens = toNonNegativeInt(outputRaw);
|
|
5835
|
+
const cacheRead = toNonNegativeInt(cacheReadRaw);
|
|
5836
|
+
const cacheCreation = toNonNegativeInt(cacheCreationRaw);
|
|
5837
|
+
const reasoningTokens = toNonNegativeInt(reasoningRaw);
|
|
5838
|
+
|
|
5839
|
+
const isFromTotalField =
|
|
5840
|
+
usage.cachedMissTokens === undefined &&
|
|
5841
|
+
usage.cacheMissTokens === undefined &&
|
|
5842
|
+
usage.prompt_cache_miss_tokens === undefined;
|
|
5843
|
+
if (isFromTotalField && cacheRead > 0) {
|
|
5844
|
+
inputTokens = Math.max(0, inputTokens - cacheRead);
|
|
5845
|
+
}
|
|
5846
|
+
|
|
5847
|
+
if (inputTokens === 0 && completionTokens === 0 && cacheRead === 0 && cacheCreation === 0) {
|
|
5848
|
+
seenIds.add(messageId);
|
|
5849
|
+
continue;
|
|
5850
|
+
}
|
|
5851
|
+
|
|
5852
|
+
const tsIso = new Date(tsMs).toISOString();
|
|
5853
|
+
const bucketStart = toUtcHalfHourStart(tsIso);
|
|
5854
|
+
if (!bucketStart) continue;
|
|
5855
|
+
|
|
5856
|
+
const rawModel =
|
|
5857
|
+
modelsByAgent.get(agentId) ||
|
|
5858
|
+
logModelsByAgent[agentModelKey(agentId)] ||
|
|
5859
|
+
fallbackModel;
|
|
5860
|
+
const model = normalizeModelInput(rawModel);
|
|
5861
|
+
|
|
5862
|
+
const delta = {
|
|
5863
|
+
input_tokens: inputTokens,
|
|
5864
|
+
cached_input_tokens: cacheRead,
|
|
5865
|
+
cache_creation_input_tokens: cacheCreation,
|
|
5866
|
+
output_tokens: completionTokens,
|
|
5867
|
+
reasoning_output_tokens: reasoningTokens,
|
|
5868
|
+
total_tokens: inputTokens + completionTokens + cacheRead + cacheCreation + reasoningTokens,
|
|
5869
|
+
conversation_count: 1,
|
|
5870
|
+
};
|
|
5871
|
+
|
|
5872
|
+
const bucket = getHourlyBucket(hourlyState, "codebuddy", model, bucketStart);
|
|
5873
|
+
addTotals(bucket.totals, delta);
|
|
5874
|
+
touchedBuckets.add(bucketKey("codebuddy", model, bucketStart));
|
|
5688
5875
|
seenIds.add(messageId);
|
|
5689
|
-
|
|
5690
|
-
}
|
|
5691
|
-
const tsIso = new Date(tsMs).toISOString();
|
|
5692
|
-
const bucketStart = toUtcHalfHourStart(tsIso);
|
|
5693
|
-
if (!bucketStart) continue;
|
|
5876
|
+
eventsAggregated++;
|
|
5694
5877
|
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5878
|
+
if (cb) {
|
|
5879
|
+
cb({
|
|
5880
|
+
index: fileIdx + 1,
|
|
5881
|
+
total: files.length,
|
|
5882
|
+
recordsProcessed,
|
|
5883
|
+
eventsAggregated,
|
|
5884
|
+
bucketsQueued: touchedBuckets.size,
|
|
5885
|
+
});
|
|
5886
|
+
}
|
|
5887
|
+
} else {
|
|
5888
|
+
let entry;
|
|
5889
|
+
try { entry = JSON.parse(line); } catch { continue; }
|
|
5890
|
+
|
|
5891
|
+
if (!entry || entry.type !== "message" || entry.role !== "assistant") continue;
|
|
5892
|
+
|
|
5893
|
+
const provider = entry.providerData;
|
|
5894
|
+
const rawUsage = provider && typeof provider === "object" ? provider.rawUsage : null;
|
|
5895
|
+
if (!rawUsage || typeof rawUsage !== "object") continue;
|
|
5896
|
+
|
|
5897
|
+
const sessionId =
|
|
5898
|
+
typeof entry.sessionId === "string" && entry.sessionId
|
|
5899
|
+
? entry.sessionId
|
|
5900
|
+
: path.basename(filePath, ".jsonl");
|
|
5901
|
+
const tsMs =
|
|
5902
|
+
Number.isFinite(Number(entry.timestamp)) && Number(entry.timestamp) > 0
|
|
5903
|
+
? Number(entry.timestamp)
|
|
5904
|
+
: null;
|
|
5905
|
+
const messageId =
|
|
5906
|
+
typeof entry.uuid === "string" && entry.uuid
|
|
5907
|
+
? entry.uuid
|
|
5908
|
+
: typeof entry.id === "string" && entry.id
|
|
5909
|
+
? entry.id
|
|
5910
|
+
: tsMs != null
|
|
5911
|
+
? `${sessionId}:${tsMs}`
|
|
5912
|
+
: null;
|
|
5913
|
+
if (!messageId) continue;
|
|
5914
|
+
if (seenIds.has(messageId)) continue;
|
|
5915
|
+
|
|
5916
|
+
recordsProcessed++;
|
|
5917
|
+
|
|
5918
|
+
const promptTokens = toNonNegativeInt(rawUsage.prompt_tokens);
|
|
5919
|
+
const completionTokens = toNonNegativeInt(rawUsage.completion_tokens);
|
|
5920
|
+
const details =
|
|
5921
|
+
rawUsage.prompt_tokens_details && typeof rawUsage.prompt_tokens_details === "object"
|
|
5922
|
+
? rawUsage.prompt_tokens_details
|
|
5923
|
+
: {};
|
|
5924
|
+
const cachedTokens = toNonNegativeInt(details.cached_tokens);
|
|
5925
|
+
const cacheReadAlt = toNonNegativeInt(rawUsage.cache_read_input_tokens);
|
|
5926
|
+
const cacheCreation = toNonNegativeInt(rawUsage.cache_creation_input_tokens);
|
|
5927
|
+
const reasoningTokens = toNonNegativeInt(details.reasoning_tokens);
|
|
5928
|
+
|
|
5929
|
+
const cacheRead = Math.max(cachedTokens, cacheReadAlt);
|
|
5930
|
+
const inputTokens = Math.max(0, promptTokens - cacheRead);
|
|
5931
|
+
|
|
5932
|
+
if (
|
|
5933
|
+
inputTokens === 0 &&
|
|
5934
|
+
completionTokens === 0 &&
|
|
5935
|
+
cacheRead === 0 &&
|
|
5936
|
+
cacheCreation === 0
|
|
5937
|
+
) {
|
|
5938
|
+
seenIds.add(messageId);
|
|
5939
|
+
continue;
|
|
5940
|
+
}
|
|
5699
5941
|
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5942
|
+
if (tsMs == null) {
|
|
5943
|
+
seenIds.add(messageId);
|
|
5944
|
+
continue;
|
|
5945
|
+
}
|
|
5946
|
+
const tsIso = new Date(tsMs).toISOString();
|
|
5947
|
+
const bucketStart = toUtcHalfHourStart(tsIso);
|
|
5948
|
+
if (!bucketStart) continue;
|
|
5949
|
+
|
|
5950
|
+
const model =
|
|
5951
|
+
normalizeModelInput(provider?.model) ||
|
|
5952
|
+
normalizeModelInput(entry.model) ||
|
|
5953
|
+
fallbackModel;
|
|
5954
|
+
|
|
5955
|
+
const delta = {
|
|
5956
|
+
input_tokens: inputTokens,
|
|
5957
|
+
cached_input_tokens: cacheRead,
|
|
5958
|
+
cache_creation_input_tokens: cacheCreation,
|
|
5959
|
+
output_tokens: completionTokens,
|
|
5960
|
+
reasoning_output_tokens: reasoningTokens,
|
|
5961
|
+
total_tokens:
|
|
5962
|
+
inputTokens + completionTokens + cacheRead + cacheCreation + reasoningTokens,
|
|
5963
|
+
conversation_count: 1,
|
|
5964
|
+
};
|
|
5710
5965
|
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5966
|
+
const bucket = getHourlyBucket(hourlyState, "codebuddy", model, bucketStart);
|
|
5967
|
+
addTotals(bucket.totals, delta);
|
|
5968
|
+
touchedBuckets.add(bucketKey("codebuddy", model, bucketStart));
|
|
5969
|
+
seenIds.add(messageId);
|
|
5970
|
+
eventsAggregated++;
|
|
5716
5971
|
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5972
|
+
if (cb) {
|
|
5973
|
+
cb({
|
|
5974
|
+
index: fileIdx + 1,
|
|
5975
|
+
total: files.length,
|
|
5976
|
+
recordsProcessed,
|
|
5977
|
+
eventsAggregated,
|
|
5978
|
+
bucketsQueued: touchedBuckets.size,
|
|
5979
|
+
});
|
|
5980
|
+
}
|
|
5725
5981
|
}
|
|
5726
5982
|
}
|
|
5727
5983
|
|
|
@@ -5739,6 +5995,11 @@ async function parseCodebuddyIncremental({
|
|
|
5739
5995
|
const seenArr = Array.from(seenIds);
|
|
5740
5996
|
const cappedSeen =
|
|
5741
5997
|
seenArr.length > 10_000 ? seenArr.slice(seenArr.length - 10_000) : seenArr;
|
|
5998
|
+
const logModelEntries = Object.entries(logModelsByAgent);
|
|
5999
|
+
const cappedLogModelsByAgent =
|
|
6000
|
+
logModelEntries.length > 10_000
|
|
6001
|
+
? Object.fromEntries(logModelEntries.slice(logModelEntries.length - 10_000))
|
|
6002
|
+
: logModelsByAgent;
|
|
5742
6003
|
|
|
5743
6004
|
const bucketsQueued = await enqueueTouchedBuckets({
|
|
5744
6005
|
queuePath,
|
|
@@ -5752,6 +6013,7 @@ async function parseCodebuddyIncremental({
|
|
|
5752
6013
|
...codebuddyState,
|
|
5753
6014
|
seenIds: cappedSeen,
|
|
5754
6015
|
fileOffsets,
|
|
6016
|
+
logModelsByAgent: cappedLogModelsByAgent,
|
|
5755
6017
|
updatedAt,
|
|
5756
6018
|
};
|
|
5757
6019
|
|
|
@@ -5885,17 +6147,32 @@ async function parseWorkbuddyIncremental({
|
|
|
5885
6147
|
workbuddyState.fileOffsets && typeof workbuddyState.fileOffsets === "object"
|
|
5886
6148
|
? { ...workbuddyState.fileOffsets }
|
|
5887
6149
|
: {};
|
|
6150
|
+
const sqliteSessions =
|
|
6151
|
+
workbuddyState.sqliteSessions && typeof workbuddyState.sqliteSessions === "object"
|
|
6152
|
+
? { ...workbuddyState.sqliteSessions }
|
|
6153
|
+
: {};
|
|
6154
|
+
const detailedSessions =
|
|
6155
|
+
workbuddyState.detailedSessions && typeof workbuddyState.detailedSessions === "object"
|
|
6156
|
+
? { ...workbuddyState.detailedSessions }
|
|
6157
|
+
: {};
|
|
6158
|
+
const detailedSessionsWithUsage = new Set();
|
|
5888
6159
|
|
|
5889
6160
|
const files = Array.isArray(projectFiles)
|
|
5890
6161
|
? projectFiles
|
|
5891
6162
|
: resolveWorkbuddyProjectFiles(env || process.env);
|
|
5892
6163
|
const fallbackModel = defaultModel || resolveWorkbuddyDefaultModel(env || process.env);
|
|
5893
6164
|
|
|
5894
|
-
|
|
6165
|
+
const workbuddyHome = resolveWorkbuddyHome(env || process.env);
|
|
6166
|
+
const dbPath = workbuddyHome ? path.join(workbuddyHome, "workbuddy.db") : null;
|
|
6167
|
+
const dbExists = Boolean(dbPath && fssync.existsSync(dbPath));
|
|
6168
|
+
|
|
6169
|
+
if (files.length === 0 && !dbExists) {
|
|
5895
6170
|
cursors.workbuddy = {
|
|
5896
6171
|
...workbuddyState,
|
|
5897
6172
|
seenIds: Array.from(seenIds),
|
|
5898
6173
|
fileOffsets,
|
|
6174
|
+
sqliteSessions,
|
|
6175
|
+
detailedSessions,
|
|
5899
6176
|
updatedAt: new Date().toISOString(),
|
|
5900
6177
|
};
|
|
5901
6178
|
return { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
@@ -5946,6 +6223,9 @@ async function parseWorkbuddyIncremental({
|
|
|
5946
6223
|
typeof entry.sessionId === "string" && entry.sessionId
|
|
5947
6224
|
? entry.sessionId
|
|
5948
6225
|
: path.basename(filePath, ".jsonl");
|
|
6226
|
+
if (Object.prototype.hasOwnProperty.call(sqliteSessions, sessionId)) {
|
|
6227
|
+
continue;
|
|
6228
|
+
}
|
|
5949
6229
|
const tsMs =
|
|
5950
6230
|
Number.isFinite(Number(entry.timestamp)) && Number(entry.timestamp) > 0
|
|
5951
6231
|
? Number(entry.timestamp)
|
|
@@ -6031,6 +6311,8 @@ async function parseWorkbuddyIncremental({
|
|
|
6031
6311
|
addTotals(bucket.totals, delta);
|
|
6032
6312
|
touchedBuckets.add(bucketKey("workbuddy", model, bucketStart));
|
|
6033
6313
|
seenIds.add(messageId);
|
|
6314
|
+
detailedSessions[sessionId] = true;
|
|
6315
|
+
detailedSessionsWithUsage.add(sessionId);
|
|
6034
6316
|
eventsAggregated++;
|
|
6035
6317
|
|
|
6036
6318
|
if (cb) {
|
|
@@ -6053,11 +6335,119 @@ async function parseWorkbuddyIncremental({
|
|
|
6053
6335
|
};
|
|
6054
6336
|
}
|
|
6055
6337
|
|
|
6338
|
+
if (dbExists) {
|
|
6339
|
+
const query = `
|
|
6340
|
+
SELECT
|
|
6341
|
+
su.session_id,
|
|
6342
|
+
su.used,
|
|
6343
|
+
su.updated_at,
|
|
6344
|
+
s.model,
|
|
6345
|
+
s.cwd
|
|
6346
|
+
FROM session_usage su
|
|
6347
|
+
LEFT JOIN sessions s ON s.id = su.session_id
|
|
6348
|
+
WHERE su.used IS NOT NULL
|
|
6349
|
+
AND su.used > 0
|
|
6350
|
+
AND su.updated_at IS NOT NULL
|
|
6351
|
+
AND su.updated_at > 0
|
|
6352
|
+
`.trim();
|
|
6353
|
+
let rows = [];
|
|
6354
|
+
const snap = snapshotSqliteDb(dbPath);
|
|
6355
|
+
try {
|
|
6356
|
+
rows = await readSqliteJsonRowsAsync(snap.path, query, {
|
|
6357
|
+
label: "WorkBuddy",
|
|
6358
|
+
timeout: 10_000,
|
|
6359
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
6360
|
+
});
|
|
6361
|
+
} finally {
|
|
6362
|
+
snap.cleanup();
|
|
6363
|
+
}
|
|
6364
|
+
|
|
6365
|
+
try {
|
|
6366
|
+
for (const row of rows) {
|
|
6367
|
+
if (!row || typeof row !== "object") continue;
|
|
6368
|
+
const sessionId = typeof row.session_id === "string" ? row.session_id.trim() : "";
|
|
6369
|
+
if (!sessionId) continue;
|
|
6370
|
+
if (detailedSessions[sessionId] || detailedSessionsWithUsage.has(sessionId)) continue;
|
|
6371
|
+
|
|
6372
|
+
const usedNow = toNonNegativeInt(row.used);
|
|
6373
|
+
const updatedAtRaw = toNonNegativeInt(row.updated_at);
|
|
6374
|
+
const rawModel = typeof row.model === "string" ? row.model.trim() : "";
|
|
6375
|
+
|
|
6376
|
+
if (usedNow <= 0 || updatedAtRaw <= 0) continue;
|
|
6377
|
+
|
|
6378
|
+
const prev = sqliteSessions[sessionId] || { used: 0 };
|
|
6379
|
+
const prevUsed = toNonNegativeInt(prev.used);
|
|
6380
|
+
const isReset = usedNow > 0 && prevUsed > 0 && usedNow < prevUsed;
|
|
6381
|
+
const inputDelta = isReset ? usedNow : Math.max(0, usedNow - prevUsed);
|
|
6382
|
+
if (inputDelta === 0) {
|
|
6383
|
+
sqliteSessions[sessionId] = {
|
|
6384
|
+
...prev,
|
|
6385
|
+
used: usedNow,
|
|
6386
|
+
updatedAt: updatedAtRaw,
|
|
6387
|
+
model: rawModel || prev.model || fallbackModel,
|
|
6388
|
+
};
|
|
6389
|
+
continue;
|
|
6390
|
+
}
|
|
6391
|
+
|
|
6392
|
+
recordsProcessed++;
|
|
6393
|
+
|
|
6394
|
+
const inputTokens = inputDelta;
|
|
6395
|
+
const completionTokens = 0;
|
|
6396
|
+
const cacheRead = 0;
|
|
6397
|
+
const cacheCreation = 0;
|
|
6398
|
+
const reasoningTokens = 0;
|
|
6399
|
+
|
|
6400
|
+
const tsMs = updatedAtRaw > 10000000000 ? updatedAtRaw : updatedAtRaw * 1000;
|
|
6401
|
+
const tsIso = new Date(tsMs).toISOString();
|
|
6402
|
+
const bucketStart = toUtcHalfHourStart(tsIso);
|
|
6403
|
+
if (!bucketStart) continue;
|
|
6404
|
+
|
|
6405
|
+
const model = normalizeModelInput(rawModel) || fallbackModel;
|
|
6406
|
+
|
|
6407
|
+
const delta = {
|
|
6408
|
+
input_tokens: inputTokens,
|
|
6409
|
+
cached_input_tokens: cacheRead,
|
|
6410
|
+
cache_creation_input_tokens: cacheCreation,
|
|
6411
|
+
output_tokens: completionTokens,
|
|
6412
|
+
reasoning_output_tokens: reasoningTokens,
|
|
6413
|
+
total_tokens: inputTokens,
|
|
6414
|
+
conversation_count: prevUsed === 0 || isReset ? 1 : 0,
|
|
6415
|
+
};
|
|
6416
|
+
|
|
6417
|
+
const bucket = getHourlyBucket(hourlyState, "workbuddy", model, bucketStart);
|
|
6418
|
+
addTotals(bucket.totals, delta);
|
|
6419
|
+
touchedBuckets.add(bucketKey("workbuddy", model, bucketStart));
|
|
6420
|
+
sqliteSessions[sessionId] = {
|
|
6421
|
+
used: usedNow,
|
|
6422
|
+
updatedAt: updatedAtRaw,
|
|
6423
|
+
model,
|
|
6424
|
+
};
|
|
6425
|
+
eventsAggregated++;
|
|
6426
|
+
}
|
|
6427
|
+
} catch (err) {
|
|
6428
|
+
// SQLite fallback is best effort; detailed JSONL remains authoritative.
|
|
6429
|
+
}
|
|
6430
|
+
}
|
|
6431
|
+
|
|
6056
6432
|
// Cap dedup set to last 10k IDs to bound cursor state size — same convention
|
|
6057
6433
|
// as CodeBuddy/Kimi/Copilot so cursors.json doesn't grow unbounded.
|
|
6058
6434
|
const seenArr = Array.from(seenIds);
|
|
6059
6435
|
const cappedSeen =
|
|
6060
6436
|
seenArr.length > 10_000 ? seenArr.slice(seenArr.length - 10_000) : seenArr;
|
|
6437
|
+
const sqliteSessionEntries = Object.entries(sqliteSessions);
|
|
6438
|
+
const cappedSqliteSessions =
|
|
6439
|
+
sqliteSessionEntries.length > 10_000
|
|
6440
|
+
? Object.fromEntries(
|
|
6441
|
+
sqliteSessionEntries
|
|
6442
|
+
.sort((a, b) => toNonNegativeInt(b[1]?.used) - toNonNegativeInt(a[1]?.used))
|
|
6443
|
+
.slice(0, 10_000),
|
|
6444
|
+
)
|
|
6445
|
+
: sqliteSessions;
|
|
6446
|
+
const detailedSessionEntries = Object.entries(detailedSessions);
|
|
6447
|
+
const cappedDetailedSessions =
|
|
6448
|
+
detailedSessionEntries.length > 10_000
|
|
6449
|
+
? Object.fromEntries(detailedSessionEntries.slice(detailedSessionEntries.length - 10_000))
|
|
6450
|
+
: detailedSessions;
|
|
6061
6451
|
|
|
6062
6452
|
const bucketsQueued = await enqueueTouchedBuckets({
|
|
6063
6453
|
queuePath,
|
|
@@ -6071,6 +6461,8 @@ async function parseWorkbuddyIncremental({
|
|
|
6071
6461
|
...workbuddyState,
|
|
6072
6462
|
seenIds: cappedSeen,
|
|
6073
6463
|
fileOffsets,
|
|
6464
|
+
sqliteSessions: cappedSqliteSessions,
|
|
6465
|
+
detailedSessions: cappedDetailedSessions,
|
|
6074
6466
|
updatedAt,
|
|
6075
6467
|
};
|
|
6076
6468
|
|