tokentracker-cli 0.87.7 → 0.87.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/README.md +3 -2
- package/README.zh-CN.md +3 -2
- package/dashboard/dist/assets/{AchievementBadge-4SSFyzNE.js → AchievementBadge-D3_hERkQ.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-4ASCXja2.js → AchievementsPage-DZGjT92g.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-CyaMYMol.js → AchievementsSection-CEzq3U3A.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-CUWTCEru.js → BadgeDetailModal-D3_B8E--.js} +1 -1
- package/dashboard/dist/assets/{Card-C79yXijL.js → Card-CMXM3-lp.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-Cw6J-x_d.js → ClawdAnimated-lQxUKRzw.js} +1 -1
- package/dashboard/dist/assets/{CommandPalette-C1RQ58De.js → CommandPalette-BCGdAeFB.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-CycaCGsd.js → CommunityStatsModal-BwE5h2y5.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-hPuA2aup.js → ConfirmModal-8uhS2ccj.js} +1 -1
- package/dashboard/dist/assets/{Controls-DjunxsRS.js → Controls-CBEQTl2X.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-C5H5_EZN.js → DashboardPage-UE_3tGVy.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-a1TT-0Bd.js → DevicePage-B06ybHJI.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-CCRfnIbO.js → DialogClose-L_t-nrYm.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-B7FFtR7A.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-2Qa5pApU.js → DialogTitle-Bsft4VvL.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-C3mpVy83.js → FadeIn-BNDdLqTu.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-Dn_F7ByX.js → HeaderGithubStar-CvNaKDbl.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-DaywO0Zz.js → HoverTooltip-D5j5IpNx.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-oqGfnWb7.js → IpCheckPage-Csf8gMJW.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-CX7dYsEX.js → LandingPage-BVSSwM0D.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-Bl-OMQ2x.js → LeaderboardAvatar-DXxSHqmP.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-yeWcgiVo.js → LeaderboardPage-DXj21BBR.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-CDmZtQ_m.js → LeaderboardProfileModal-DbEzZGg4.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-BqJYxoN1.js → LeaderboardProfilePage-DoIm3jB_.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-D8NFgXbZ.js → LimitsPage-D_tpQrUM.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-DjdhDEiq.js → LocalOnlyNotice-DBi5iuPr.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-jQXAmSQb.js → LoginCard-qDIJgUX1.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-q4G6EN7b.js → LoginModal-D4b5p4u1.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-BklWSXzs.js → LoginPage-CsSgItFA.js} +1 -1
- package/dashboard/dist/assets/{PetPage-DyWuMUTA.js → PetPage-B-wElg5q.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-Dbxed_FL.js → PopoverPopup-9C_VarUQ.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-ChYuSSfj.js → ProviderIcon-CciEdEeR.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-DJww1dxo.js → ResetPasswordPage-lsI8qlzE.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-Dr73_QVm.js → SegmentedControl-BGonMV45.js} +1 -1
- package/dashboard/dist/assets/{Select-BMCy_8oY.js → Select-DhRrGrDT.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-BRU7mlBI.js → SelectItemText-ClMYgZsa.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-DDao-oTH.js → ServiceStatusPage-DuzttgL7.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-BjngkrsY.js → SessionsPage-gZbxx0f_.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-Dsftjg5j.js → SettingsPage-NhxTrVNW.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-igML0DOZ.js → SkillsPage-BeXojJqQ.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-BBETSYe7.js → TokenGalaxy-DM49zKzo.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-CzEAxfqj.js → ToolbarRootContext-BfvoVNMW.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-JV6lkw-s.js → TrendMonitor-Bu-dXcrV.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-GpUmv0hI.js → WidgetsPage-D2Wo7seE.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-SP2r4wf1.js → WrappedPage-BXAwic4D.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-AQgdBxou.js → __vite-browser-external-BDEQk--l.js} +4 -3
- package/dashboard/dist/assets/{agent-logos-DxwSc05k.js → agent-logos-DMEEHjG7.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-CDW5zn0Q.js → arrow-up-right-gNviKtY8.js} +1 -1
- package/dashboard/dist/assets/{check-CQfDkdqM.js → check-Cvg5pmYP.js} +1 -1
- package/dashboard/dist/assets/{chevron-right-DDFSY4w-.js → chevron-right-DxdaNTo8.js} +1 -1
- package/dashboard/dist/assets/{copy-hxnyMdBa.js → copy-BNIJL7bf.js} +1 -1
- package/dashboard/dist/assets/{download-DOUBKxcf.js → download-C9McJlPa.js} +1 -1
- package/dashboard/dist/assets/{external-link-CVipCZOa.js → external-link-C_IU6zDK.js} +1 -1
- package/dashboard/dist/assets/{flame-C-QU6mPf.js → flame-CII7X8Je.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-DRX15id1.js → format-tokens-DuQbGpPt.js} +1 -1
- package/dashboard/dist/assets/{icons-ajxtn_0O.js → icons-B1YTqnxG.js} +1 -1
- package/dashboard/dist/assets/{index-DnVXRsqb.js → index-BRMxraNK.js} +1 -1
- package/dashboard/dist/assets/{index-OJC-yROZ.js → index-Dc9xsIu2.js} +1 -1
- package/dashboard/dist/assets/{index-Bq5VZMkS.js → index-QtcYir6O.js} +1 -1
- package/dashboard/dist/assets/{info-eITfPd17.js → info-CIgVqJoj.js} +1 -1
- package/dashboard/dist/assets/{limits-providers-TMq74bRy.js → limits-providers-B31NsGar.js} +1 -1
- package/dashboard/dist/assets/{link-2-D778MOmL.js → link-2-DTPXzHuB.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-uN5rBNIF.js → loader-circle-CgQSyCz8.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-C4FBbMpN.js → maximize-2-BL5J4aHZ.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-DLeiQDTT.js → modifiers.esm-s_rzcEf2.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-C-obu2-u.js → monitor-smartphone-C9ei_xde.js} +1 -1
- package/dashboard/dist/assets/{provider-display-Bjl8tmYu.js → provider-display-ZgvoJi05.js} +2 -2
- package/dashboard/dist/assets/{react-Diah7uAT.js → react-DhfPVGOm.js} +1 -1
- package/dashboard/dist/assets/{search-B0bPOquo.js → search-Ba_MWO7q.js} +1 -1
- package/dashboard/dist/assets/{skills-api-6otf2Sdl.js → skills-api-BkmJmyg1.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-DcrJaOfF.js → sortable.esm-Bmd5t12E.js} +1 -1
- package/dashboard/dist/assets/{store-DOs4omJM.js → store-BxRQs9yc.js} +1 -1
- package/dashboard/dist/assets/{terminal-CpMZjaGk.js → terminal-BzUwAs4g.js} +1 -1
- package/dashboard/dist/assets/{trash-2-BQYnn3zf.js → trash-2-CCprY7cu.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-CdL4zXXV.js → use-community-stats-TCdPJ_ir.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-DUh4f8f4.js → use-limits-display-prefs-CNF9luO6.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-DVs0Ik3O.js → use-ordered-list-BlTuSYJy.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-CfiR91Eg.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-CGggPI6Y.js → use-session-efficiency-pref-iHS0O_Fo.js} +1 -1
- package/dashboard/dist/assets/{use-transform-DMj91FEL.js → use-transform-CcVfL3mi.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-xJOXzuZG.js → use-usage-limits-Xdy-Y_1Z.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-BQRBx-KO.js → useCurrency-IXccXbmx.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-DuTPOBl8.js → useScrollLock-BEVewfZm.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-Dd95ph6d.js → useTokenFormat-Cihv3Ghl.js} +1 -1
- package/dashboard/dist/assets/{zap-DJMdRI6y.js → zap-zG2Fbb5_.js} +1 -1
- package/dashboard/dist/brand-logos/omp.svg +33 -0
- package/dashboard/dist/brand-logos/pi.svg +16 -0
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/ip-check.html +1 -1
- package/dashboard/dist/leaderboard.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +1 -1
- package/src/commands/init.js +49 -8
- package/src/commands/status.js +12 -5
- package/src/commands/sync.js +245 -1
- package/src/commands/uninstall.js +36 -1
- package/src/lib/install-resolver.js +22 -6
- package/src/lib/omp-hook.js +589 -0
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +272 -32
- package/dashboard/dist/assets/DialogDescription-C99d5K4r.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion--_Yc7AWk.js +0 -1
package/src/lib/rollout.js
CHANGED
|
@@ -7840,9 +7840,45 @@ function resolveCodebuddyDefaultModel(env = process.env) {
|
|
|
7840
7840
|
return fallback;
|
|
7841
7841
|
}
|
|
7842
7842
|
|
|
7843
|
+
// Modern CodeBuddy writes token usage into providerData.rawUsage on JSONL
|
|
7844
|
+
// records. Older IDE installs may have no usable JSONL usage and rely on the
|
|
7845
|
+
// extension log fallback below. Keep the sniff bounded: it is used only to
|
|
7846
|
+
// decide whether enabling the legacy log source would create a second copy of
|
|
7847
|
+
// the same round trips.
|
|
7848
|
+
function codebuddyJsonlHasUsage(filePath) {
|
|
7849
|
+
let fd;
|
|
7850
|
+
try {
|
|
7851
|
+
fd = fssync.openSync(filePath, "r");
|
|
7852
|
+
const buffer = Buffer.alloc(64 * 1024);
|
|
7853
|
+
const bytesRead = fssync.readSync(fd, buffer, 0, buffer.length, 0);
|
|
7854
|
+
const lines = buffer.toString("utf8", 0, bytesRead).split("\n");
|
|
7855
|
+
const limit = Math.min(lines.length, 100);
|
|
7856
|
+
for (let i = 0; i < limit; i += 1) {
|
|
7857
|
+
const line = lines[i];
|
|
7858
|
+
if (!line || (!line.includes("rawUsage") && !line.includes("\"usage\""))) continue;
|
|
7859
|
+
let entry;
|
|
7860
|
+
try { entry = JSON.parse(line); } catch { continue; }
|
|
7861
|
+
if (!entry || typeof entry !== "object") continue;
|
|
7862
|
+
const provider = entry.providerData;
|
|
7863
|
+
if (provider && typeof provider === "object" && provider.rawUsage && typeof provider.rawUsage === "object") {
|
|
7864
|
+
return true;
|
|
7865
|
+
}
|
|
7866
|
+
if (entry.usage && typeof entry.usage === "object") return true;
|
|
7867
|
+
}
|
|
7868
|
+
} catch {
|
|
7869
|
+
return false;
|
|
7870
|
+
} finally {
|
|
7871
|
+
if (fd !== undefined) {
|
|
7872
|
+
try { fssync.closeSync(fd); } catch {}
|
|
7873
|
+
}
|
|
7874
|
+
}
|
|
7875
|
+
return false;
|
|
7876
|
+
}
|
|
7877
|
+
|
|
7843
7878
|
function resolveCodebuddyProjectFiles(env = process.env) {
|
|
7844
7879
|
const codebuddyHome = resolveCodebuddyHome(env);
|
|
7845
|
-
const
|
|
7880
|
+
const jsonlFiles = [];
|
|
7881
|
+
const logFiles = [];
|
|
7846
7882
|
|
|
7847
7883
|
// 1. Recursive JSONL scan in codebuddyHome/projects
|
|
7848
7884
|
if (codebuddyHome) {
|
|
@@ -7863,7 +7899,7 @@ function resolveCodebuddyProjectFiles(env = process.env) {
|
|
|
7863
7899
|
} catch { continue; }
|
|
7864
7900
|
}
|
|
7865
7901
|
if (isDir) walkJsonl(full);
|
|
7866
|
-
else if (isFile && entry.name.endsWith(".jsonl"))
|
|
7902
|
+
else if (isFile && entry.name.endsWith(".jsonl")) jsonlFiles.push(full);
|
|
7867
7903
|
}
|
|
7868
7904
|
};
|
|
7869
7905
|
walkJsonl(projectsDir);
|
|
@@ -7916,10 +7952,10 @@ function resolveCodebuddyProjectFiles(env = process.env) {
|
|
|
7916
7952
|
walkLogs(full);
|
|
7917
7953
|
} else if (isFile && entry.name.endsWith(".log")) {
|
|
7918
7954
|
if (root.pattern === "*.log") {
|
|
7919
|
-
|
|
7955
|
+
logFiles.push(full);
|
|
7920
7956
|
} else if (root.pattern === "codebuddy-extension-log") {
|
|
7921
7957
|
if (full.toLowerCase().includes("tencent-cloud.coding-copilot")) {
|
|
7922
|
-
|
|
7958
|
+
logFiles.push(full);
|
|
7923
7959
|
}
|
|
7924
7960
|
}
|
|
7925
7961
|
}
|
|
@@ -7928,6 +7964,11 @@ function resolveCodebuddyProjectFiles(env = process.env) {
|
|
|
7928
7964
|
walkLogs(root.dir);
|
|
7929
7965
|
}
|
|
7930
7966
|
|
|
7967
|
+
const fallbackMode = String(env.TOKENTRACKER_CODEBUDDY_LOG_FALLBACK || "").trim();
|
|
7968
|
+
const includeLogs =
|
|
7969
|
+
fallbackMode === "1" ||
|
|
7970
|
+
(fallbackMode !== "0" && !jsonlFiles.some((filePath) => codebuddyJsonlHasUsage(filePath)));
|
|
7971
|
+
const files = [...jsonlFiles, ...(includeLogs ? logFiles : [])];
|
|
7931
7972
|
files.sort((a, b) => a.localeCompare(b));
|
|
7932
7973
|
return files;
|
|
7933
7974
|
}
|
|
@@ -8179,9 +8220,15 @@ async function parseCodebuddyIncremental({
|
|
|
8179
8220
|
} else {
|
|
8180
8221
|
let entry;
|
|
8181
8222
|
try { entry = JSON.parse(line); } catch { continue; }
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
|
|
8223
|
+
if (!entry || typeof entry !== "object") continue;
|
|
8224
|
+
|
|
8225
|
+
// Usage is carried on ANY record with providerData.rawUsage — assistant
|
|
8226
|
+
// messages AND function_call records. Each LLM round-trip (whether it
|
|
8227
|
+
// ends in a text reply or a tool call) carries its own usage; on real
|
|
8228
|
+
// installs function_call records are ~93% of round-trips and ~14x the
|
|
8229
|
+
// assistant-message token volume, so filtering by record type drops
|
|
8230
|
+
// the majority of usage. Aggregate them all; dedup per round-trip.
|
|
8231
|
+
// (Same convention as the WorkBuddy reader — same transcript format.)
|
|
8185
8232
|
const provider = entry.providerData;
|
|
8186
8233
|
const rawUsage = provider && typeof provider === "object" ? provider.rawUsage : null;
|
|
8187
8234
|
if (!rawUsage || typeof rawUsage !== "object") continue;
|
|
@@ -8194,38 +8241,65 @@ async function parseCodebuddyIncremental({
|
|
|
8194
8241
|
Number.isFinite(Number(entry.timestamp)) && Number(entry.timestamp) > 0
|
|
8195
8242
|
? Number(entry.timestamp)
|
|
8196
8243
|
: null;
|
|
8244
|
+
// One usage record per LLM round-trip; providerData.messageId is the
|
|
8245
|
+
// response-level id shared by the function_call/message pair of the
|
|
8246
|
+
// same round-trip, so it is the most stable dedup key. entry.id is the
|
|
8247
|
+
// per-record append id (unique per line, NOT per round-trip) and must
|
|
8248
|
+
// NOT be preferred — preferring it would count a round-trip once per
|
|
8249
|
+
// record type instead of once total.
|
|
8197
8250
|
const messageId =
|
|
8198
|
-
typeof
|
|
8199
|
-
?
|
|
8200
|
-
: typeof entry.
|
|
8201
|
-
? entry.
|
|
8202
|
-
:
|
|
8203
|
-
?
|
|
8204
|
-
: null
|
|
8251
|
+
typeof provider?.messageId === "string" && provider?.messageId
|
|
8252
|
+
? provider.messageId
|
|
8253
|
+
: typeof entry.uuid === "string" && entry.uuid
|
|
8254
|
+
? entry.uuid
|
|
8255
|
+
: typeof entry.id === "string" && entry.id
|
|
8256
|
+
? entry.id
|
|
8257
|
+
: tsMs != null
|
|
8258
|
+
? `${sessionId}:${tsMs}`
|
|
8259
|
+
: null;
|
|
8205
8260
|
if (!messageId) continue;
|
|
8206
8261
|
if (seenIds.has(messageId)) continue;
|
|
8207
8262
|
|
|
8208
8263
|
recordsProcessed++;
|
|
8209
8264
|
|
|
8210
8265
|
const promptTokens = toNonNegativeInt(rawUsage.prompt_tokens);
|
|
8211
|
-
const
|
|
8266
|
+
const completionTokensRaw = toNonNegativeInt(rawUsage.completion_tokens);
|
|
8212
8267
|
const details =
|
|
8213
8268
|
rawUsage.prompt_tokens_details && typeof rawUsage.prompt_tokens_details === "object"
|
|
8214
8269
|
? rawUsage.prompt_tokens_details
|
|
8215
8270
|
: {};
|
|
8216
|
-
const
|
|
8271
|
+
const completionDetails =
|
|
8272
|
+
rawUsage.completion_tokens_details && typeof rawUsage.completion_tokens_details === "object"
|
|
8273
|
+
? rawUsage.completion_tokens_details
|
|
8274
|
+
: {};
|
|
8275
|
+
// Cache-read mirrors three ways depending on upstream: Anthropic-style
|
|
8276
|
+
// cache_read_input_tokens, OpenAI-style prompt_tokens_details.cached_tokens,
|
|
8277
|
+
// DeepSeek-style prompt_cache_hit_tokens. Take the max (they mirror the
|
|
8278
|
+
// same quantity; on real data exactly one is non-zero).
|
|
8279
|
+
const cachedTokens = Math.max(
|
|
8280
|
+
toNonNegativeInt(details.cached_tokens),
|
|
8281
|
+
toNonNegativeInt(rawUsage.prompt_cache_hit_tokens),
|
|
8282
|
+
);
|
|
8217
8283
|
const cacheReadAlt = toNonNegativeInt(rawUsage.cache_read_input_tokens);
|
|
8218
|
-
const cacheCreation =
|
|
8219
|
-
|
|
8284
|
+
const cacheCreation = Math.max(
|
|
8285
|
+
toNonNegativeInt(rawUsage.cache_creation_input_tokens),
|
|
8286
|
+
toNonNegativeInt(rawUsage.prompt_cache_write_tokens),
|
|
8287
|
+
);
|
|
8288
|
+
// reasoning_tokens lives in completion_tokens_details (verified on real
|
|
8289
|
+
// CodeBuddy data: prompt_tokens_details.reasoning_tokens is always 0).
|
|
8290
|
+
// completion_tokens INCLUDES reasoning, so subtract to avoid double-count.
|
|
8291
|
+
const reasoningTokens = Math.min(completionTokensRaw, toNonNegativeInt(completionDetails.reasoning_tokens));
|
|
8292
|
+
const completionTokens = Math.max(0, completionTokensRaw - reasoningTokens);
|
|
8220
8293
|
|
|
8221
8294
|
const cacheRead = Math.max(cachedTokens, cacheReadAlt);
|
|
8222
|
-
const inputTokens = Math.max(0, promptTokens - cacheRead);
|
|
8295
|
+
const inputTokens = Math.max(0, promptTokens - cacheRead - cacheCreation);
|
|
8223
8296
|
|
|
8224
8297
|
if (
|
|
8225
8298
|
inputTokens === 0 &&
|
|
8226
8299
|
completionTokens === 0 &&
|
|
8227
8300
|
cacheRead === 0 &&
|
|
8228
|
-
cacheCreation === 0
|
|
8301
|
+
cacheCreation === 0 &&
|
|
8302
|
+
reasoningTokens === 0
|
|
8229
8303
|
) {
|
|
8230
8304
|
seenIds.add(messageId);
|
|
8231
8305
|
continue;
|
|
@@ -8356,6 +8430,12 @@ async function parseCodebuddyIncremental({
|
|
|
8356
8430
|
//
|
|
8357
8431
|
// model is the auto-router placeholder ("auto") — WorkBuddy does not expose
|
|
8358
8432
|
// the underlying model in the log, so we emit it verbatim.
|
|
8433
|
+
//
|
|
8434
|
+
// 4. Completed ~/.workbuddy/traces/**/trace_*.json summaries expose aggregate
|
|
8435
|
+
// modelInfo input/output/cache totals. They are a fallback only when the
|
|
8436
|
+
// session has no detailed JSONL usage; SQLite's session_usage table has no
|
|
8437
|
+
// output/cache/reasoning columns, so SQLite-only rows intentionally remain
|
|
8438
|
+
// input/total-only instead of fabricating a token split.
|
|
8359
8439
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
8360
8440
|
|
|
8361
8441
|
function resolveWorkbuddyHome(env = process.env) {
|
|
@@ -8390,14 +8470,16 @@ function resolveWorkbuddyDefaultModel(env = process.env) {
|
|
|
8390
8470
|
|
|
8391
8471
|
// Recursively collect every *.jsonl under ~/.workbuddy/projects so that
|
|
8392
8472
|
// per-session conversation logs AND their nested subagents/agent-*.jsonl files
|
|
8393
|
-
// are both discovered.
|
|
8473
|
+
// are both discovered. WorkBuddy also writes completed trace summaries under
|
|
8474
|
+
// ~/.workbuddy/traces/<pid>/trace_*.json. Those summaries are useful only when
|
|
8475
|
+
// the detailed JSONL for a session is absent, so they are returned as typed
|
|
8476
|
+
// entries and parsed after JSONL (JSONL remains authoritative when both exist).
|
|
8394
8477
|
function resolveWorkbuddyProjectFiles(env = process.env) {
|
|
8395
8478
|
const workbuddyHome = resolveWorkbuddyHome(env);
|
|
8396
8479
|
if (!workbuddyHome) return [];
|
|
8397
8480
|
const projectsDir = path.join(workbuddyHome, "projects");
|
|
8398
|
-
if (!fssync.existsSync(projectsDir)) return [];
|
|
8399
8481
|
const files = [];
|
|
8400
|
-
const
|
|
8482
|
+
const walkJsonl = (dir) => {
|
|
8401
8483
|
let entries;
|
|
8402
8484
|
try { entries = fssync.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
8403
8485
|
for (const entry of entries) {
|
|
@@ -8412,12 +8494,43 @@ function resolveWorkbuddyProjectFiles(env = process.env) {
|
|
|
8412
8494
|
isFile = st.isFile();
|
|
8413
8495
|
} catch { continue; }
|
|
8414
8496
|
}
|
|
8415
|
-
if (isDir)
|
|
8497
|
+
if (isDir) walkJsonl(full);
|
|
8416
8498
|
else if (isFile && entry.name.endsWith(".jsonl")) files.push(full);
|
|
8417
8499
|
}
|
|
8418
8500
|
};
|
|
8419
|
-
|
|
8420
|
-
|
|
8501
|
+
if (fssync.existsSync(projectsDir)) walkJsonl(projectsDir);
|
|
8502
|
+
|
|
8503
|
+
const tracesDir = path.join(workbuddyHome, "traces");
|
|
8504
|
+
const walkTraces = (dir) => {
|
|
8505
|
+
let entries;
|
|
8506
|
+
try { entries = fssync.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
8507
|
+
for (const entry of entries) {
|
|
8508
|
+
const full = path.join(dir, entry.name);
|
|
8509
|
+
let isDir = entry.isDirectory();
|
|
8510
|
+
let isFile = entry.isFile();
|
|
8511
|
+
if (!isDir && !isFile) {
|
|
8512
|
+
try {
|
|
8513
|
+
const st = fssync.statSync(full);
|
|
8514
|
+
isDir = st.isDirectory();
|
|
8515
|
+
isFile = st.isFile();
|
|
8516
|
+
} catch { continue; }
|
|
8517
|
+
}
|
|
8518
|
+
if (isDir) walkTraces(full);
|
|
8519
|
+
else if (isFile && /^trace_[^/]+\.json$/i.test(entry.name)) {
|
|
8520
|
+
files.push({ path: full, kind: "trace" });
|
|
8521
|
+
}
|
|
8522
|
+
}
|
|
8523
|
+
};
|
|
8524
|
+
if (fssync.existsSync(tracesDir)) walkTraces(tracesDir);
|
|
8525
|
+
files.sort((a, b) => {
|
|
8526
|
+
const aPath = typeof a === "string" ? a : a?.path || "";
|
|
8527
|
+
const bPath = typeof b === "string" ? b : b?.path || "";
|
|
8528
|
+
// JSONL first is intentional: trace summaries are a lossy fallback and
|
|
8529
|
+
// must not win over a detailed rawUsage record on the same session.
|
|
8530
|
+
const aKind = typeof a === "string" ? 0 : 1;
|
|
8531
|
+
const bKind = typeof b === "string" ? 0 : 1;
|
|
8532
|
+
return aKind - bKind || aPath.localeCompare(bPath);
|
|
8533
|
+
});
|
|
8421
8534
|
return files;
|
|
8422
8535
|
}
|
|
8423
8536
|
|
|
@@ -8447,11 +8560,22 @@ async function parseWorkbuddyIncremental({
|
|
|
8447
8560
|
workbuddyState.detailedSessions && typeof workbuddyState.detailedSessions === "object"
|
|
8448
8561
|
? { ...workbuddyState.detailedSessions }
|
|
8449
8562
|
: {};
|
|
8563
|
+
const seenTraceIds = new Set(
|
|
8564
|
+
Array.isArray(workbuddyState.seenTraceIds) ? workbuddyState.seenTraceIds : [],
|
|
8565
|
+
);
|
|
8566
|
+
const tracedSessionIds = new Set(
|
|
8567
|
+
Array.isArray(workbuddyState.tracedSessionIds) ? workbuddyState.tracedSessionIds : [],
|
|
8568
|
+
);
|
|
8450
8569
|
const detailedSessionsWithUsage = new Set();
|
|
8451
8570
|
|
|
8452
|
-
const
|
|
8571
|
+
const allFiles = Array.isArray(projectFiles)
|
|
8453
8572
|
? projectFiles
|
|
8454
8573
|
: resolveWorkbuddyProjectFiles(env || process.env);
|
|
8574
|
+
const jsonlFiles = allFiles.filter((entry) => typeof entry === "string");
|
|
8575
|
+
const traceFiles = allFiles.filter(
|
|
8576
|
+
(entry) => entry && typeof entry === "object" && typeof entry.path === "string" && entry.kind === "trace",
|
|
8577
|
+
);
|
|
8578
|
+
const files = [...jsonlFiles, ...traceFiles];
|
|
8455
8579
|
const fallbackModel = defaultModel || resolveWorkbuddyDefaultModel(env || process.env);
|
|
8456
8580
|
|
|
8457
8581
|
const workbuddyHome = resolveWorkbuddyHome(env || process.env);
|
|
@@ -8462,6 +8586,8 @@ async function parseWorkbuddyIncremental({
|
|
|
8462
8586
|
cursors.workbuddy = {
|
|
8463
8587
|
...workbuddyState,
|
|
8464
8588
|
seenIds: Array.from(seenIds),
|
|
8589
|
+
seenTraceIds: Array.from(seenTraceIds),
|
|
8590
|
+
tracedSessionIds: Array.from(tracedSessionIds),
|
|
8465
8591
|
fileOffsets,
|
|
8466
8592
|
sqliteSessions,
|
|
8467
8593
|
detailedSessions,
|
|
@@ -8476,8 +8602,8 @@ async function parseWorkbuddyIncremental({
|
|
|
8476
8602
|
let recordsProcessed = 0;
|
|
8477
8603
|
let eventsAggregated = 0;
|
|
8478
8604
|
|
|
8479
|
-
for (let fileIdx = 0; fileIdx <
|
|
8480
|
-
const filePath =
|
|
8605
|
+
for (let fileIdx = 0; fileIdx < jsonlFiles.length; fileIdx++) {
|
|
8606
|
+
const filePath = jsonlFiles[fileIdx];
|
|
8481
8607
|
let stat;
|
|
8482
8608
|
try { stat = fssync.statSync(filePath); } catch { continue; }
|
|
8483
8609
|
|
|
@@ -8515,7 +8641,10 @@ async function parseWorkbuddyIncremental({
|
|
|
8515
8641
|
typeof entry.sessionId === "string" && entry.sessionId
|
|
8516
8642
|
? entry.sessionId
|
|
8517
8643
|
: path.basename(filePath, ".jsonl");
|
|
8518
|
-
if (
|
|
8644
|
+
if (
|
|
8645
|
+
Object.prototype.hasOwnProperty.call(sqliteSessions, sessionId) ||
|
|
8646
|
+
tracedSessionIds.has(sessionId)
|
|
8647
|
+
) {
|
|
8519
8648
|
continue;
|
|
8520
8649
|
}
|
|
8521
8650
|
const tsMs =
|
|
@@ -8627,6 +8756,99 @@ async function parseWorkbuddyIncremental({
|
|
|
8627
8756
|
};
|
|
8628
8757
|
}
|
|
8629
8758
|
|
|
8759
|
+
// Trace summaries are a fallback for sessions whose JSONL has no detailed
|
|
8760
|
+
// providerData.rawUsage. A trace's modelInfo totals include the cache read
|
|
8761
|
+
// portion of input, so split input into uncached input + cached input. Do
|
|
8762
|
+
// not merge a trace with the same session's JSONL (or SQLite) data: the
|
|
8763
|
+
// JSONL path has per-round detail and the trace summary can be partial on
|
|
8764
|
+
// older WorkBuddy versions.
|
|
8765
|
+
for (let fileIdx = 0; fileIdx < traceFiles.length; fileIdx++) {
|
|
8766
|
+
const entry = traceFiles[fileIdx];
|
|
8767
|
+
const filePath = entry.path;
|
|
8768
|
+
let stat;
|
|
8769
|
+
try { stat = fssync.statSync(filePath); } catch { continue; }
|
|
8770
|
+
const prevEntry = fileOffsets[filePath] || {};
|
|
8771
|
+
const prevSize = Number(prevEntry.size) || 0;
|
|
8772
|
+
const prevIno = prevEntry.ino;
|
|
8773
|
+
const inodeChanged = typeof prevIno === "number" && prevIno !== stat.ino;
|
|
8774
|
+
const startOffset = stat.size < prevSize || inodeChanged ? 0 : prevSize;
|
|
8775
|
+
if (stat.size <= startOffset) continue;
|
|
8776
|
+
|
|
8777
|
+
let traceDoc;
|
|
8778
|
+
try {
|
|
8779
|
+
traceDoc = JSON.parse(fssync.readFileSync(filePath, "utf8"));
|
|
8780
|
+
} catch {
|
|
8781
|
+
continue;
|
|
8782
|
+
}
|
|
8783
|
+
const trace = traceDoc && typeof traceDoc === "object" && traceDoc.trace && typeof traceDoc.trace === "object"
|
|
8784
|
+
? traceDoc.trace
|
|
8785
|
+
: null;
|
|
8786
|
+
const traceId = typeof trace?.traceId === "string" && trace.traceId
|
|
8787
|
+
? trace.traceId
|
|
8788
|
+
: path.basename(filePath, ".json");
|
|
8789
|
+
const metadata = trace?.metadata && typeof trace.metadata === "object" ? trace.metadata : {};
|
|
8790
|
+
const modelInfo = trace?.modelInfo && typeof trace.modelInfo === "object"
|
|
8791
|
+
? trace.modelInfo
|
|
8792
|
+
: metadata.modelInfo && typeof metadata.modelInfo === "object"
|
|
8793
|
+
? metadata.modelInfo
|
|
8794
|
+
: {};
|
|
8795
|
+
const sessionId =
|
|
8796
|
+
(typeof trace?.sessionId === "string" && trace.sessionId) ||
|
|
8797
|
+
(typeof metadata.sessionId === "string" && metadata.sessionId) ||
|
|
8798
|
+
traceId;
|
|
8799
|
+
const startedAtRaw = trace?.startedAt || metadata.startedAt;
|
|
8800
|
+
const tsMs = typeof startedAtRaw === "number" && Number.isFinite(startedAtRaw)
|
|
8801
|
+
? (startedAtRaw > 10000000000 ? startedAtRaw : startedAtRaw * 1000)
|
|
8802
|
+
: Date.parse(String(startedAtRaw || ""));
|
|
8803
|
+
const totalInput = toNonNegativeInt(modelInfo.totalInputTokens);
|
|
8804
|
+
const totalOutput = toNonNegativeInt(modelInfo.totalOutputTokens);
|
|
8805
|
+
const totalCached = Math.min(totalInput, toNonNegativeInt(modelInfo.totalCachedTokens));
|
|
8806
|
+
if (seenTraceIds.has(traceId) || detailedSessions[sessionId] || detailedSessionsWithUsage.has(sessionId) || tracedSessionIds.has(sessionId)) {
|
|
8807
|
+
seenTraceIds.add(traceId);
|
|
8808
|
+
fileOffsets[filePath] = { size: stat.size, mtimeMs: stat.mtimeMs, ino: stat.ino };
|
|
8809
|
+
continue;
|
|
8810
|
+
}
|
|
8811
|
+
if (!Number.isFinite(tsMs) || tsMs <= 0 || (totalInput <= 0 && totalOutput <= 0)) {
|
|
8812
|
+
// Keep the file offset but do not permanently suppress a trace that may
|
|
8813
|
+
// still be finalized in place by WorkBuddy.
|
|
8814
|
+
fileOffsets[filePath] = { size: stat.size, mtimeMs: stat.mtimeMs, ino: stat.ino };
|
|
8815
|
+
continue;
|
|
8816
|
+
}
|
|
8817
|
+
|
|
8818
|
+
const bucketStart = toUtcHalfHourStart(new Date(tsMs).toISOString());
|
|
8819
|
+
if (!bucketStart) continue;
|
|
8820
|
+
const model =
|
|
8821
|
+
normalizeModelInput(Array.isArray(modelInfo.models) ? modelInfo.models[0] : modelInfo.model) ||
|
|
8822
|
+
fallbackModel;
|
|
8823
|
+
const inputTokens = Math.max(0, totalInput - totalCached);
|
|
8824
|
+
const delta = {
|
|
8825
|
+
input_tokens: inputTokens,
|
|
8826
|
+
cached_input_tokens: totalCached,
|
|
8827
|
+
cache_creation_input_tokens: 0,
|
|
8828
|
+
output_tokens: totalOutput,
|
|
8829
|
+
reasoning_output_tokens: 0,
|
|
8830
|
+
total_tokens: inputTokens + totalCached + totalOutput,
|
|
8831
|
+
conversation_count: 1,
|
|
8832
|
+
};
|
|
8833
|
+
const bucket = getHourlyBucket(hourlyState, "workbuddy", model, bucketStart);
|
|
8834
|
+
addTotals(bucket.totals, delta);
|
|
8835
|
+
touchedBuckets.add(bucketKey("workbuddy", model, bucketStart));
|
|
8836
|
+
seenTraceIds.add(traceId);
|
|
8837
|
+
tracedSessionIds.add(sessionId);
|
|
8838
|
+
recordsProcessed++;
|
|
8839
|
+
eventsAggregated++;
|
|
8840
|
+
fileOffsets[filePath] = { size: stat.size, mtimeMs: stat.mtimeMs, ino: stat.ino };
|
|
8841
|
+
if (cb) {
|
|
8842
|
+
cb({
|
|
8843
|
+
index: jsonlFiles.length + fileIdx + 1,
|
|
8844
|
+
total: files.length,
|
|
8845
|
+
recordsProcessed,
|
|
8846
|
+
eventsAggregated,
|
|
8847
|
+
bucketsQueued: touchedBuckets.size,
|
|
8848
|
+
});
|
|
8849
|
+
}
|
|
8850
|
+
}
|
|
8851
|
+
|
|
8630
8852
|
if (dbExists) {
|
|
8631
8853
|
const query = `
|
|
8632
8854
|
SELECT
|
|
@@ -8659,7 +8881,11 @@ async function parseWorkbuddyIncremental({
|
|
|
8659
8881
|
if (!row || typeof row !== "object") continue;
|
|
8660
8882
|
const sessionId = typeof row.session_id === "string" ? row.session_id.trim() : "";
|
|
8661
8883
|
if (!sessionId) continue;
|
|
8662
|
-
if (
|
|
8884
|
+
if (
|
|
8885
|
+
detailedSessions[sessionId] ||
|
|
8886
|
+
detailedSessionsWithUsage.has(sessionId) ||
|
|
8887
|
+
tracedSessionIds.has(sessionId)
|
|
8888
|
+
) continue;
|
|
8663
8889
|
|
|
8664
8890
|
const usedNow = toNonNegativeInt(row.used);
|
|
8665
8891
|
const updatedAtRaw = toNonNegativeInt(row.updated_at);
|
|
@@ -8696,13 +8922,16 @@ async function parseWorkbuddyIncremental({
|
|
|
8696
8922
|
|
|
8697
8923
|
const model = normalizeModelInput(rawModel) || fallbackModel;
|
|
8698
8924
|
|
|
8925
|
+
// session_usage only exposes a cumulative `used` value. It has no
|
|
8926
|
+
// output/cache/reasoning columns, so SQLite-only history can safely
|
|
8927
|
+
// contribute input/total but must not invent a token split.
|
|
8699
8928
|
const delta = {
|
|
8700
8929
|
input_tokens: inputTokens,
|
|
8701
8930
|
cached_input_tokens: cacheRead,
|
|
8702
8931
|
cache_creation_input_tokens: cacheCreation,
|
|
8703
8932
|
output_tokens: completionTokens,
|
|
8704
8933
|
reasoning_output_tokens: reasoningTokens,
|
|
8705
|
-
|
|
8934
|
+
total_tokens: inputTokens + completionTokens + cacheRead + cacheCreation + reasoningTokens,
|
|
8706
8935
|
conversation_count: prevUsed === 0 || isReset ? 1 : 0,
|
|
8707
8936
|
};
|
|
8708
8937
|
|
|
@@ -8740,6 +8969,14 @@ async function parseWorkbuddyIncremental({
|
|
|
8740
8969
|
detailedSessionEntries.length > 10_000
|
|
8741
8970
|
? Object.fromEntries(detailedSessionEntries.slice(detailedSessionEntries.length - 10_000))
|
|
8742
8971
|
: detailedSessions;
|
|
8972
|
+
const traceArr = Array.from(seenTraceIds);
|
|
8973
|
+
const cappedSeenTraceIds =
|
|
8974
|
+
traceArr.length > 10_000 ? traceArr.slice(traceArr.length - 10_000) : traceArr;
|
|
8975
|
+
const tracedSessionArr = Array.from(tracedSessionIds);
|
|
8976
|
+
const cappedTracedSessionIds =
|
|
8977
|
+
tracedSessionArr.length > 10_000
|
|
8978
|
+
? tracedSessionArr.slice(tracedSessionArr.length - 10_000)
|
|
8979
|
+
: tracedSessionArr;
|
|
8743
8980
|
|
|
8744
8981
|
const bucketsQueued = await enqueueTouchedBuckets({
|
|
8745
8982
|
queuePath,
|
|
@@ -8752,6 +8989,8 @@ async function parseWorkbuddyIncremental({
|
|
|
8752
8989
|
cursors.workbuddy = {
|
|
8753
8990
|
...workbuddyState,
|
|
8754
8991
|
seenIds: cappedSeen,
|
|
8992
|
+
seenTraceIds: cappedSeenTraceIds,
|
|
8993
|
+
tracedSessionIds: cappedTracedSessionIds,
|
|
8755
8994
|
fileOffsets,
|
|
8756
8995
|
sqliteSessions: cappedSqliteSessions,
|
|
8757
8996
|
detailedSessions: cappedDetailedSessions,
|
|
@@ -15448,6 +15687,7 @@ module.exports = {
|
|
|
15448
15687
|
resolveKimiCodeDefaultModel,
|
|
15449
15688
|
parseKimiCodeIncremental,
|
|
15450
15689
|
resolveCodebuddyHome,
|
|
15690
|
+
codebuddyJsonlHasUsage,
|
|
15451
15691
|
resolveCodebuddyProjectFiles,
|
|
15452
15692
|
resolveCodebuddyDefaultModel,
|
|
15453
15693
|
parseCodebuddyIncremental,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as n,z as a}from"./__vite-browser-external-AQgdBxou.js";import{u as p}from"./DialogTitle-2Qa5pApU.js";import{u as c}from"./useScrollLock-DuTPOBl8.js";const x=n.forwardRef(function(e,r){const{render:l,className:u,style:m,id:s,...t}=e,{store:i}=p(),o=c(s);return i.useSyncedValueWithCleanup("descriptionElementId",o),a("p",e,{ref:r,props:[{id:o},t]})});export{x as D};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as o}from"./__vite-browser-external-AQgdBxou.js";import{h as r,i as t,p as s}from"./react-Diah7uAT.js";function i(){!r.current&&t();const[e]=o.useState(s.current);return e}export{i as u};
|