tokentracker-cli 0.85.0 → 0.86.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/README.de.md +5 -4
- package/README.ja.md +5 -4
- package/README.ko.md +5 -4
- package/README.md +8 -7
- package/README.zh-CN.md +5 -4
- package/dashboard/dist/assets/{AchievementBadge-DEqbHlmn.js → AchievementBadge-BgbH_mX4.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-BOaEu1AQ.js → AchievementsPage-CQqkh1JS.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-C370ngO3.js → AchievementsSection-Dmkxpn0s.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-D7Wkef0K.js → BadgeDetailModal-7OQf37lk.js} +1 -1
- package/dashboard/dist/assets/{Card-mDEVjNdM.js → Card-1PmWZRTK.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-DdsX-z8n.js → ClawdAnimated-CMJFagpX.js} +1 -1
- package/dashboard/dist/assets/{CommandPalette-DhxRIuHC.js → CommandPalette-D4OrwF1w.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-CG-9JPS6.js → CommunityStatsModal-BDC9EcwZ.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-COhxErLm.js → ConfirmModal-22DL5C_9.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-tX0DKgFE.js → DashboardPage-CEXWzDtt.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-BsSeHx84.js → DevicePage-8uhHB7kX.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-eyOW2cbx.js → DialogClose-BcJx6VK-.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-D6zxtOM5.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-H8sGiH7c.js → DialogTitle-gRzunAqv.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-pq2ja50M.js → FadeIn-4rlPdBVe.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-DWgcpDHr.js → HeaderGithubStar-CR003aO3.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-CcJ_VR65.js → HoverTooltip-Dgz-dM8Q.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-CNUN346Q.js → IpCheckPage-B4PJRJP_.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-yaoRGsNI.js → LandingPage-3JQe-jOZ.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-Ddai4I12.js → LeaderboardAvatar-CL9LxiEf.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-Bajkxf63.js → LeaderboardPage-DhIzM2Z2.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-BGYKDgIi.js → LeaderboardProfileModal-bbf5uJR5.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-Ck5vPaeN.js → LeaderboardProfilePage-CzuPxscw.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-Bqobtsw2.js → LimitsPage-B59rb2gg.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-e9KSE3wj.js → LocalOnlyNotice-BkK-6n1R.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-BhTSm2tb.js → LoginCard-BFGCzEjE.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-CRwuThde.js → LoginModal-yhkyscFn.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-j1i74lcc.js → LoginPage-DwU5s0F7.js} +1 -1
- package/dashboard/dist/assets/{PetPage-r62ViU1X.js → PetPage-BPscYR6z.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-DopNTyD4.js → PopoverPopup-E7PyGTTB.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-c1LkPhYM.js → ProviderIcon-CvmnzwKU.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-DJdDNvtq.js → ResetPasswordPage-DD6qX616.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-DhWVOTPN.js → SegmentedControl-DjGBL_l1.js} +1 -1
- package/dashboard/dist/assets/{Select-DeUmTmsJ.js → Select-B2MGmsi2.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-pX0B5f7h.js → SelectItemText-BaTwA_ZF.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-ejIPz6oF.js → ServiceStatusPage-0K0y8A6m.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-WEbn90GW.js → SessionsPage-CxrtRm3B.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-B8t5gCeI.js → SettingsPage-CyvSWvDP.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-BWMlzeAp.js → SkillsPage-BVOU8t3t.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-DIBRBvMj.js → TokenGalaxy-w7bwGloC.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-C6KxLvnI.js → ToolbarRootContext-BAzpDZej.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-BaMNh67w.js → TrendMonitor-DyT7Q4xp.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-DDsy4M3x.js → WidgetsPage-eDDtopQT.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-CiNnUFRI.js → WrappedPage-CKkbdWHF.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-CM-H8-LW.js → __vite-browser-external-SU8sCTLB.js} +3 -3
- package/dashboard/dist/assets/{agent-logos-BjVa6oe7.js → agent-logos-bkh9XZJd.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-CCum1mqX.js → arrow-up-right-D16zCIw8.js} +1 -1
- package/dashboard/dist/assets/{check-ySRQc1YP.js → check-BFL17JWQ.js} +1 -1
- package/dashboard/dist/assets/{chevron-right-5vFloHew.js → chevron-right-CUJgrtkp.js} +1 -1
- package/dashboard/dist/assets/{copy-mhfKVMKo.js → copy-CRQiCjgs.js} +1 -1
- package/dashboard/dist/assets/{download-GdrXglSx.js → download-Cd12nDrQ.js} +1 -1
- package/dashboard/dist/assets/{external-link-KGFaKc8s.js → external-link-CvAfnsG-.js} +1 -1
- package/dashboard/dist/assets/{flame-BmNtNoS1.js → flame-CCi681Yl.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-CCpCNw3d.js → format-tokens-BAVt8uTZ.js} +1 -1
- package/dashboard/dist/assets/{icons-XLKZavTf.js → icons-CbKC96gR.js} +1 -1
- package/dashboard/dist/assets/{index-_xU5U5Ct.js → index--TDvaL1y.js} +1 -1
- package/dashboard/dist/assets/{index-CsywTeCQ.js → index-B5b6PCh5.js} +1 -1
- package/dashboard/dist/assets/{index-IKpmeZte.js → index-B9LWn-mo.js} +1 -1
- package/dashboard/dist/assets/{info-DUV5UpmT.js → info-BPFpD--M.js} +1 -1
- package/dashboard/dist/assets/{limits-providers-DFkCZCZj.js → limits-providers-Cyedt-WJ.js} +1 -1
- package/dashboard/dist/assets/{link-2-D7c64yLY.js → link-2-BkEPPdfC.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-sMONRK2M.js → loader-circle-CzASiylJ.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-CVWRgrk9.js → maximize-2-CkwnHUJn.js} +1 -1
- package/dashboard/dist/assets/{provider-display-DSKqVjgm.js → provider-display-BXBBedC-.js} +2 -2
- package/dashboard/dist/assets/{react-c1q4mGkS.js → react-BXSctDQj.js} +1 -1
- package/dashboard/dist/assets/{search-CwD3s-_C.js → search-_qGma9tM.js} +1 -1
- package/dashboard/dist/assets/{skills-api-BRXj8kjT.js → skills-api-DdmJjnJ9.js} +1 -1
- package/dashboard/dist/assets/{store-lqTW2gfK.js → store-owYCN530.js} +1 -1
- package/dashboard/dist/assets/{terminal-DU1_KbKo.js → terminal-Clzw-KEF.js} +1 -1
- package/dashboard/dist/assets/{trash-2-B8Y5Xc-K.js → trash-2-C5Giwp_E.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-DYwwimvp.js → use-community-stats-Dk8UjLEr.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-Bst6ybK3.js → use-limits-display-prefs-CArM595Q.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-Cqle4lgw.js → use-native-settings-Cpd69kiF.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-Dtze0ahb.js → use-ordered-list--CzwhBTx.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-CcILnpU1.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-gwbDMHXa.js → use-session-efficiency-pref-DUXO8cCg.js} +1 -1
- package/dashboard/dist/assets/{use-transform-CU0yxXpX.js → use-transform-Cg3kuMj_.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-5y1I1T5_.js → use-usage-limits-DY9_mV28.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-BfVNrywe.js → useCurrency-BIe86hMn.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-CF1m7AMJ.js → useScrollLock-7-hejkNC.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-DiRwVvRr.js → useTokenFormat-DEtLbRgQ.js} +1 -1
- package/dashboard/dist/assets/{zap-BZtlWtuN.js → zap-DUxY1d-O.js} +1 -1
- package/dashboard/dist/index.html +12 -10
- package/dashboard/dist/ip-check.html +1 -1
- package/dashboard/dist/leaderboard.html +1 -1
- package/dashboard/dist/llms.txt +3 -2
- package/dashboard/dist/share.html +1 -1
- package/package.json +2 -2
- package/src/commands/init.js +1 -0
- package/src/commands/status.js +15 -0
- package/src/commands/sync.js +114 -8
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +451 -0
- package/dashboard/dist/assets/DialogDescription-BwtE4lKl.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-CbZDR54N.js +0 -1
package/src/commands/status.js
CHANGED
|
@@ -66,6 +66,7 @@ const {
|
|
|
66
66
|
resolveRoocodeTaskFiles,
|
|
67
67
|
resolveZedDbPath,
|
|
68
68
|
resolveQoderDbPaths,
|
|
69
|
+
resolveClaudeScienceDbPaths,
|
|
69
70
|
resolveAnythingllmDbPath,
|
|
70
71
|
resolveGooseDbPath,
|
|
71
72
|
listDroidSettingsFiles,
|
|
@@ -425,6 +426,14 @@ async function cmdStatus(argv = []) {
|
|
|
425
426
|
const qoderInstalled = qoderActive.length > 0;
|
|
426
427
|
const qoderDbPath = qoderActive.join(" | ");
|
|
427
428
|
|
|
429
|
+
// Claude Science — token usage lives on the `frames` table of operon-cli.db.
|
|
430
|
+
// Unlike the native/WSL pair other providers resolve to, this is an open-ended
|
|
431
|
+
// list: multi-org installs keep one DB per org (and on Windows they all sit
|
|
432
|
+
// inside WSL), so the resolver already returns only paths that exist.
|
|
433
|
+
const claudeScienceActive = resolveClaudeScienceDbPaths({ home, env: process.env });
|
|
434
|
+
const claudeScienceInstalled = claudeScienceActive.length > 0;
|
|
435
|
+
const claudeScienceDbPath = claudeScienceActive.join(" | ");
|
|
436
|
+
|
|
428
437
|
// OpenCode (JSON files + SQLite DB) — passive scan of storage/message/ and opencode.db.
|
|
429
438
|
const opencodeStorageNativeValue = process.env.OPENCODE_HOME || path.join(xdgDataHome, "opencode");
|
|
430
439
|
const wslOpencodeStorageDir = process.platform === "win32" && shouldProbeWsl(process.env)
|
|
@@ -786,6 +795,9 @@ async function cmdStatus(argv = []) {
|
|
|
786
795
|
qoder: qoderInstalled
|
|
787
796
|
? { installed: true, detail: qoderDbPath }
|
|
788
797
|
: { installed: false },
|
|
798
|
+
"claude-science": claudeScienceInstalled
|
|
799
|
+
? { installed: true, detail: claudeScienceDbPath }
|
|
800
|
+
: { installed: false },
|
|
789
801
|
kilocode: kilocodeInstalled
|
|
790
802
|
? { installed: true, files: kilocodeTaskFiles.length }
|
|
791
803
|
: { installed: false },
|
|
@@ -907,6 +919,9 @@ async function cmdStatus(argv = []) {
|
|
|
907
919
|
qoderInstalled
|
|
908
920
|
? `- Qoder: passive reader (${qoderDbPath})`
|
|
909
921
|
: null,
|
|
922
|
+
claudeScienceInstalled
|
|
923
|
+
? `- Claude Science: passive reader (${claudeScienceDbPath})`
|
|
924
|
+
: null,
|
|
910
925
|
opencodeInstalled
|
|
911
926
|
? `- OpenCode: passive reader (storage: ${opencodeStorageActive.join(" | ") || "not found"}, DB: ${opencodeDbActive.join(" | ") || "not found"})`
|
|
912
927
|
: null,
|
package/src/commands/sync.js
CHANGED
|
@@ -40,6 +40,12 @@ const {
|
|
|
40
40
|
parseOpencodeDbIncremental,
|
|
41
41
|
parseQoderDbIncremental,
|
|
42
42
|
parseOpenclawIncremental,
|
|
43
|
+
resolveOpenclawSessionFiles,
|
|
44
|
+
resolveOpenclawHome,
|
|
45
|
+
openclawCursorKey,
|
|
46
|
+
resolveClaudeScienceDbPaths,
|
|
47
|
+
readClaudeScienceFrames,
|
|
48
|
+
parseClaudeScienceIncremental,
|
|
43
49
|
parseCursorApiIncremental,
|
|
44
50
|
parseKiroIncremental,
|
|
45
51
|
parseHermesIncremental,
|
|
@@ -237,6 +243,7 @@ const AUTO_SYNC_SOURCES = new Set([
|
|
|
237
243
|
"antigravity",
|
|
238
244
|
"anythingllm",
|
|
239
245
|
"claude",
|
|
246
|
+
"claude-science",
|
|
240
247
|
"codebuddy",
|
|
241
248
|
"codex",
|
|
242
249
|
"copilot",
|
|
@@ -473,7 +480,7 @@ async function cmdSync(argv, context = {}) {
|
|
|
473
480
|
// OpenClaw session plugin integration: lifecycle hooks request an
|
|
474
481
|
// OpenClaw-only auto sync so unrelated providers do not get walked.
|
|
475
482
|
const openclawSignal = opts.fromOpenclaw
|
|
476
|
-
? resolveOpenclawSignal({
|
|
483
|
+
? resolveOpenclawSignal({ env: process.env })
|
|
477
484
|
: null;
|
|
478
485
|
|
|
479
486
|
const autoSourceScope = resolveAutoSourceScope(opts);
|
|
@@ -668,9 +675,38 @@ async function cmdSync(argv, context = {}) {
|
|
|
668
675
|
codexCursorLoadRestarted = Boolean(loadResult?.restarted);
|
|
669
676
|
}
|
|
670
677
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
678
|
+
// Plugin-triggered sync points at one specific session file; a normal full
|
|
679
|
+
// sync also passively scans every on-disk OpenClaw transcript so usage is
|
|
680
|
+
// captured even when the session plugin never fires (issue #264). The scan
|
|
681
|
+
// is gated to full syncs so a scoped `--from-openclaw` hook still only
|
|
682
|
+
// touches its own file and the 5-minute background tick stays cheap. The
|
|
683
|
+
// event-identity dedup makes the plugin and passive paths idempotent, so
|
|
684
|
+
// overlap on the same file is safe.
|
|
685
|
+
//
|
|
686
|
+
// Keyed by openclawCursorKey, not the raw path: that is the same identity
|
|
687
|
+
// the parser assigns cursors by, so a plugin-supplied path and a scanned
|
|
688
|
+
// path that differ only in Windows casing collapse to one entry instead of
|
|
689
|
+
// being parsed twice.
|
|
690
|
+
const openclawSessionFiles = new Map();
|
|
691
|
+
if (openclawSignal?.sessionFile) {
|
|
692
|
+
openclawSessionFiles.set(openclawCursorKey(openclawSignal.sessionFile), {
|
|
693
|
+
path: openclawSignal.sessionFile,
|
|
694
|
+
source: "openclaw",
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
if (isFullSourceScan && sourceAllowed("openclaw")) {
|
|
698
|
+
try {
|
|
699
|
+
for (const f of await resolveOpenclawSessionFiles(process.env)) {
|
|
700
|
+
const key = openclawCursorKey(f);
|
|
701
|
+
if (!openclawSessionFiles.has(key)) {
|
|
702
|
+
openclawSessionFiles.set(key, { path: f, source: "openclaw" });
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
} catch (err) {
|
|
706
|
+
warnProviderParseFailure("OpenClaw", err, opts);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
const openclawFiles = Array.from(openclawSessionFiles.values());
|
|
674
710
|
|
|
675
711
|
if (progress?.enabled) {
|
|
676
712
|
progress.start(
|
|
@@ -740,7 +776,7 @@ async function cmdSync(argv, context = {}) {
|
|
|
740
776
|
|
|
741
777
|
let openclawResult = { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
742
778
|
if (sourceAllowed("openclaw") && openclawFiles.length > 0) {
|
|
743
|
-
//
|
|
779
|
+
// Parses plugin-triggered and/or passively discovered session files.
|
|
744
780
|
try {
|
|
745
781
|
openclawResult = await parseOpenclawIncremental({
|
|
746
782
|
sessionFiles: openclawFiles,
|
|
@@ -1055,6 +1091,45 @@ async function cmdSync(argv, context = {}) {
|
|
|
1055
1091
|
}
|
|
1056
1092
|
}
|
|
1057
1093
|
|
|
1094
|
+
// ── Claude Science (Anthropic's local research workbench, issue #246) ──
|
|
1095
|
+
// Per-frame token usage lives on the `frames` table of operon-cli.db.
|
|
1096
|
+
// Passive, incremental, subtract-on-change. There can be more than one DB:
|
|
1097
|
+
// multi-org installs keep one per org, and on Windows the app runs inside
|
|
1098
|
+
// WSL so the DB is on the distro home.
|
|
1099
|
+
let claudeScienceResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
1100
|
+
if (sourceAllowed("claude-science")) {
|
|
1101
|
+
// Resolution is inside the error isolation too: it touches the filesystem
|
|
1102
|
+
// and (on Windows) probes WSL, and a throw here would abort cmdSync before
|
|
1103
|
+
// any provider's buckets are written, not just this one's.
|
|
1104
|
+
let claudeScienceDbPaths = [];
|
|
1105
|
+
try {
|
|
1106
|
+
claudeScienceDbPaths = resolveClaudeScienceDbPaths({ home, env: process.env });
|
|
1107
|
+
} catch (err) {
|
|
1108
|
+
warnProviderParseFailure("Claude Science", err, opts);
|
|
1109
|
+
}
|
|
1110
|
+
if (claudeScienceDbPaths.length > 0 && progress?.enabled) {
|
|
1111
|
+
progress.start(`Parsing Claude Science ${renderBar(0)} | buckets 0`);
|
|
1112
|
+
}
|
|
1113
|
+
for (const claudeScienceDbPath of claudeScienceDbPaths) {
|
|
1114
|
+
try {
|
|
1115
|
+
const dbRows = await readClaudeScienceFrames(claudeScienceDbPath);
|
|
1116
|
+
const parsed = await parseClaudeScienceIncremental({
|
|
1117
|
+
dbRows,
|
|
1118
|
+
cursors,
|
|
1119
|
+
queuePath,
|
|
1120
|
+
onProgress: makeProviderProgress("Claude Science"),
|
|
1121
|
+
});
|
|
1122
|
+
claudeScienceResult = mergeParseResult(claudeScienceResult, {
|
|
1123
|
+
recordsProcessed: parsed.recordsProcessed || 0,
|
|
1124
|
+
eventsAggregated: parsed.eventsAggregated || 0,
|
|
1125
|
+
bucketsQueued: parsed.bucketsQueued || 0,
|
|
1126
|
+
});
|
|
1127
|
+
} catch (err) {
|
|
1128
|
+
warnProviderParseFailure("Claude Science", err, opts);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1058
1133
|
async function parseOpencodeDbForInstall({ dbPath, readFn, source, cursorKey, ...rest }) {
|
|
1059
1134
|
if (!dbPath || !fssync.existsSync(dbPath)) {
|
|
1060
1135
|
return { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
@@ -2215,6 +2290,7 @@ async function cmdSync(argv, context = {}) {
|
|
|
2215
2290
|
antigravityResult.filesProcessed +
|
|
2216
2291
|
opencodeResult.filesProcessed +
|
|
2217
2292
|
qoderResult.recordsProcessed +
|
|
2293
|
+
claudeScienceResult.recordsProcessed +
|
|
2218
2294
|
cursorResult.recordsProcessed +
|
|
2219
2295
|
kiroResult.recordsProcessed +
|
|
2220
2296
|
kiroCliResult.recordsProcessed +
|
|
@@ -2245,6 +2321,7 @@ async function cmdSync(argv, context = {}) {
|
|
|
2245
2321
|
antigravityResult.bucketsQueued +
|
|
2246
2322
|
opencodeResult.bucketsQueued +
|
|
2247
2323
|
qoderResult.bucketsQueued +
|
|
2324
|
+
claudeScienceResult.bucketsQueued +
|
|
2248
2325
|
cursorResult.bucketsQueued +
|
|
2249
2326
|
kiroResult.bucketsQueued +
|
|
2250
2327
|
kiroCliResult.bucketsQueued +
|
|
@@ -2586,15 +2663,18 @@ function normalizeString(value) {
|
|
|
2586
2663
|
return trimmed.length > 0 ? trimmed : null;
|
|
2587
2664
|
}
|
|
2588
2665
|
|
|
2589
|
-
function resolveOpenclawSignal({
|
|
2666
|
+
function resolveOpenclawSignal({ env } = {}) {
|
|
2590
2667
|
if (!env) return null;
|
|
2591
2668
|
|
|
2592
2669
|
const agentId = normalizeString(env.TOKENTRACKER_OPENCLAW_AGENT_ID);
|
|
2593
2670
|
const sessionId = normalizeString(env.TOKENTRACKER_OPENCLAW_PREV_SESSION_ID);
|
|
2594
2671
|
if (!agentId || !sessionId) return null;
|
|
2595
2672
|
|
|
2596
|
-
|
|
2597
|
-
|
|
2673
|
+
// Resolve the OpenClaw home identically to the passive scanner so the
|
|
2674
|
+
// plugin-triggered file and passively discovered files share one path
|
|
2675
|
+
// spelling — otherwise the same transcript could get two cursors and be
|
|
2676
|
+
// counted twice (issue #264 review).
|
|
2677
|
+
const openclawHome = resolveOpenclawHome(env);
|
|
2598
2678
|
const sessionFile = path.join(openclawHome, "agents", agentId, "sessions", `${sessionId}.jsonl`);
|
|
2599
2679
|
|
|
2600
2680
|
const prevTotals = {
|
|
@@ -2627,6 +2707,16 @@ async function applyOpenclawTotalsFallback({
|
|
|
2627
2707
|
return { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
2628
2708
|
}
|
|
2629
2709
|
|
|
2710
|
+
// The passive/plugin transcript parse is authoritative: if this session's
|
|
2711
|
+
// transcript has ever yielded real per-event usage, synthesizing sessions.json
|
|
2712
|
+
// totals on top would count the same tokens twice (issue #264 review). Defer
|
|
2713
|
+
// to the real events and advance the fallback baseline so no stale delta lingers.
|
|
2714
|
+
const transcriptCursor =
|
|
2715
|
+
signal.sessionFile && cursors?.files
|
|
2716
|
+
? cursors.files[openclawCursorKey(signal.sessionFile)]
|
|
2717
|
+
: null;
|
|
2718
|
+
const transcriptHasRealUsage = Boolean(transcriptCursor?.hasRealUsage);
|
|
2719
|
+
|
|
2630
2720
|
const sessionKey = `${signal.agentId}:${signal.sessionId}`;
|
|
2631
2721
|
const statePath = path.join(trackerDir, "openclaw.fallback.state.json");
|
|
2632
2722
|
const fallbackFilePath = path.join(trackerDir, "openclaw.fallback.jsonl");
|
|
@@ -2635,6 +2725,22 @@ async function applyOpenclawTotalsFallback({
|
|
|
2635
2725
|
const prev =
|
|
2636
2726
|
sessions[sessionKey] && typeof sessions[sessionKey] === "object" ? sessions[sessionKey] : null;
|
|
2637
2727
|
|
|
2728
|
+
if (transcriptHasRealUsage) {
|
|
2729
|
+
sessions[sessionKey] = {
|
|
2730
|
+
totalTokens: normalizeNonNegativeInt(signal?.prevTotals?.totalTokens) || 0,
|
|
2731
|
+
inputTokens: normalizeNonNegativeInt(signal?.prevTotals?.inputTokens) || 0,
|
|
2732
|
+
outputTokens: normalizeNonNegativeInt(signal?.prevTotals?.outputTokens) || 0,
|
|
2733
|
+
model: normalizeString(signal?.prevTotals?.model) || "unknown",
|
|
2734
|
+
updatedAt: normalizeIsoOrEpoch(signal?.prevTotals?.updatedAt) || new Date().toISOString(),
|
|
2735
|
+
seenAt: new Date().toISOString(),
|
|
2736
|
+
coveredByEvents: true,
|
|
2737
|
+
};
|
|
2738
|
+
state.version = 1;
|
|
2739
|
+
state.sessions = sessions;
|
|
2740
|
+
await writeJson(statePath, state);
|
|
2741
|
+
return { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2638
2744
|
const current = {
|
|
2639
2745
|
totalTokens: normalizeNonNegativeInt(signal?.prevTotals?.totalTokens) || 0,
|
|
2640
2746
|
inputTokens: normalizeNonNegativeInt(signal?.prevTotals?.inputTokens) || 0,
|