tokentracker-cli 1.3.13 → 1.3.15

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.
Files changed (121) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dashboard/dist/assets/{AchievementBadge-DkNsd5tj.js → AchievementBadge-CSXb26DC.js} +1 -1
  4. package/dashboard/dist/assets/{AchievementsPage-Dr72Hcog.js → AchievementsPage-Bs6vIpd0.js} +2 -2
  5. package/dashboard/dist/assets/{AchievementsSection-CkOdGlnD.js → AchievementsSection-BO4u-574.js} +1 -1
  6. package/dashboard/dist/assets/{BadgeDetailModal-DADLfBsK.js → BadgeDetailModal-ic60vrZX.js} +1 -1
  7. package/dashboard/dist/assets/{BotAnimated-k6u1GRzw.js → BotAnimated-BHHAUEQL.js} +1 -1
  8. package/dashboard/dist/assets/{Card-Dk6s3h-P.js → Card-8t2FYLJV.js} +1 -1
  9. package/dashboard/dist/assets/{CloudCheckoutPage-DL0pvok0.js → CloudCheckoutPage-CXaDDial.js} +1 -1
  10. package/dashboard/dist/assets/{CloudPage-BizN1MZL.js → CloudPage-DN6s_UrT.js} +2 -2
  11. package/dashboard/dist/assets/{CommandPalette-B2crDNlV.js → CommandPalette-D7dHMpHK.js} +1 -1
  12. package/dashboard/dist/assets/{CommunityStatsModal-C6Rc5NME.js → CommunityStatsModal-C55r20Vb.js} +1 -1
  13. package/dashboard/dist/assets/{ConfirmModal-BrxBQn4n.js → ConfirmModal-jSEqSSnW.js} +1 -1
  14. package/dashboard/dist/assets/{Controls-C2mEPI72.js → Controls-CqmoR37l.js} +1 -1
  15. package/dashboard/dist/assets/{DashboardPage-BomPd7nt.js → DashboardPage-BLr2XmyZ.js} +1 -1
  16. package/dashboard/dist/assets/{DateRangePopover-Gz-N_D7O.js → DateRangePopover-z89rKqjK.js} +1 -1
  17. package/dashboard/dist/assets/{DevicePage-wmnNx9bd.js → DevicePage-Ddm5uKOw.js} +1 -1
  18. package/dashboard/dist/assets/{DialogClose-D1dzj_on.js → DialogClose-imfpTM8a.js} +1 -1
  19. package/dashboard/dist/assets/DialogDescription-BE5CNMW0.js +1 -0
  20. package/dashboard/dist/assets/{DialogTitle-BBxNf3O8.js → DialogTitle-Ca1-hka8.js} +1 -1
  21. package/dashboard/dist/assets/{ElectricLogo-DCopeDc8.js → ElectricLogo-CrnDjhtG.js} +1 -1
  22. package/dashboard/dist/assets/{FadeIn-DSndojVd.js → FadeIn-CAr2EMcK.js} +1 -1
  23. package/dashboard/dist/assets/{HeaderGithubStar-BBAGbevf.js → HeaderGithubStar-UK7qFFNh.js} +1 -1
  24. package/dashboard/dist/assets/{HoverTooltip-COD3xE9E.js → HoverTooltip-fs1HBOXB.js} +1 -1
  25. package/dashboard/dist/assets/{IpCheckPage-BtMWefop.js → IpCheckPage-Bk-MlPWf.js} +1 -1
  26. package/dashboard/dist/assets/{LandingPage-DN2IVBw7.js → LandingPage-DJnPCdqP.js} +2 -2
  27. package/dashboard/dist/assets/{LeaderboardPage-DkJowsRI.js → LeaderboardPage-wVjwsqyc.js} +2 -2
  28. package/dashboard/dist/assets/{LeaderboardProBadge-CRRphJNs.js → LeaderboardProBadge-BaQdHu06.js} +1 -1
  29. package/dashboard/dist/assets/{LeaderboardProfileModal-WmZAsZx5.js → LeaderboardProfileModal-D5ZZtIPl.js} +3 -3
  30. package/dashboard/dist/assets/{LeaderboardProfilePage-DWHu-eOp.js → LeaderboardProfilePage-BZsBL2jT.js} +1 -1
  31. package/dashboard/dist/assets/{LimitsPage-BRFAWm-1.js → LimitsPage-Cks_mKSL.js} +1 -1
  32. package/dashboard/dist/assets/{LocalOnlyNotice-BjJ8N4Zc.js → LocalOnlyNotice-DLnC_Tq4.js} +1 -1
  33. package/dashboard/dist/assets/{LoginCard-CYmjHoYK.js → LoginCard-FXZbjmOY.js} +1 -1
  34. package/dashboard/dist/assets/{LoginModal-DX1D9F8b.js → LoginModal-_R-0ZtpQ.js} +1 -1
  35. package/dashboard/dist/assets/{LoginPage-D7Ok7HiZ.js → LoginPage-MzDRyv1Q.js} +1 -1
  36. package/dashboard/dist/assets/{PetPage-BYISPev2.js → PetPage-DJ58EiPT.js} +2 -2
  37. package/dashboard/dist/assets/{PopoverPopup-BlzqlKqF.js → PopoverPopup-uQa7G-IO.js} +1 -1
  38. package/dashboard/dist/assets/{ProviderIcon-BuLwtYjj.js → ProviderIcon-xSNRuia-.js} +1 -1
  39. package/dashboard/dist/assets/{ResetPasswordPage-BJRKclq9.js → ResetPasswordPage-BcVcWbsW.js} +1 -1
  40. package/dashboard/dist/assets/{Select-0YyBw95W.js → Select-Dwix4ZFd.js} +1 -1
  41. package/dashboard/dist/assets/{SelectItemText-CZKyxUjE.js → SelectItemText-Bz5cn5Gk.js} +1 -1
  42. package/dashboard/dist/assets/{SelfHostPage-C_Afofs-.js → SelfHostPage-BLocCkUo.js} +1 -1
  43. package/dashboard/dist/assets/{ServiceStatusPage-0KpLdpon.js → ServiceStatusPage-CzMjS7EQ.js} +1 -1
  44. package/dashboard/dist/assets/{SessionsPage-YtSyWMd0.js → SessionsPage-Dqzuthrd.js} +1 -1
  45. package/dashboard/dist/assets/{SettingsPage-Bm641_AH.js → SettingsPage-DEdNhzKR.js} +3 -3
  46. package/dashboard/dist/assets/{SkillsPage-DNg-I2g_.js → SkillsPage-CqocOQID.js} +1 -1
  47. package/dashboard/dist/assets/{TokenGalaxy-BMN7WiRH.js → TokenGalaxy-B2sMNsM0.js} +1 -1
  48. package/dashboard/dist/assets/{TooltipPopup-BF56BHfc.js → TooltipPopup-LzbtjRSA.js} +1 -1
  49. package/dashboard/dist/assets/{TrendMonitor-D0ea5xMA.js → TrendMonitor-DpMcMWVh.js} +1 -1
  50. package/dashboard/dist/assets/{WidgetsPage-CIAoHmJG.js → WidgetsPage-aKfztwsk.js} +1 -1
  51. package/dashboard/dist/assets/{WrappedPage-CC_57Zr2.js → WrappedPage-NoY2hqbF.js} +1 -1
  52. package/dashboard/dist/assets/__vite-browser-external-BnGukaND.js +45 -0
  53. package/dashboard/dist/assets/{agent-logos-CcJUaB75.js → agent-logos-Oh8CLAYk.js} +1 -1
  54. package/dashboard/dist/assets/{arrow-left-CUiT31ZF.js → arrow-left-Mwggxctm.js} +1 -1
  55. package/dashboard/dist/assets/{arrow-right-Tds6Mert.js → arrow-right-Crx-kuck.js} +1 -1
  56. package/dashboard/dist/assets/{arrow-up-right-CGSxdyzM.js → arrow-up-right-uwoo8I0B.js} +1 -1
  57. package/dashboard/dist/assets/{calendar-days-Cc8AtMa3.js → calendar-days-C6MeKaqh.js} +1 -1
  58. package/dashboard/dist/assets/{check-CzJylQ9j.js → check-dsWQRYNj.js} +1 -1
  59. package/dashboard/dist/assets/{chevron-down-DyG9JVDO.js → chevron-down-BIf5bhyB.js} +1 -1
  60. package/dashboard/dist/assets/{cloud-checkout-DP5zddOq.js → cloud-checkout-GnFRQdnr.js} +1 -1
  61. package/dashboard/dist/assets/{composite-I_1t6PMI.js → composite-lINFdwLI.js} +1 -1
  62. package/dashboard/dist/assets/{download-B_LvkErh.js → download-B7eyvOxn.js} +1 -1
  63. package/dashboard/dist/assets/{external-link-DDA3ObBn.js → external-link-RNTsFDG4.js} +1 -1
  64. package/dashboard/dist/assets/{flame-BqpVWH_1.js → flame-uwCJ6brb.js} +1 -1
  65. package/dashboard/dist/assets/{format-tokens-D6QEOnGq.js → format-tokens-Bf7JxB4E.js} +1 -1
  66. package/dashboard/dist/assets/{gift-BxbKo3W6.js → gift-BY7kCGBK.js} +1 -1
  67. package/dashboard/dist/assets/{host-mode-o3qQY7tz.js → host-mode-Bs-UHw-7.js} +1 -1
  68. package/dashboard/dist/assets/{icons-oGPZPj2n.js → icons-CrLu8vlW.js} +1 -1
  69. package/dashboard/dist/assets/{index-DX--5z1g.js → index-CXQag63s.js} +1 -1
  70. package/dashboard/dist/assets/{index-BxtvQ1J7.js → index-DYIIk93n.js} +1 -1
  71. package/dashboard/dist/assets/{index-Byukv2Nu.js → index-DieuzFHA.js} +1 -1
  72. package/dashboard/dist/assets/{info-BoTz1AGZ.js → info-BteHQN_N.js} +1 -1
  73. package/dashboard/dist/assets/{link-2-C09a122s.js → link-2-C1RSbOHv.js} +1 -1
  74. package/dashboard/dist/assets/{loader-circle--9U0axVe.js → loader-circle-BM3x6_YL.js} +1 -1
  75. package/dashboard/dist/assets/{modifiers.esm-Cra0I6RP.js → modifiers.esm-BHzOJfH3.js} +1 -1
  76. package/dashboard/dist/assets/{monitor-smartphone-C1ENjmOu.js → monitor-smartphone-Ps3VptSt.js} +1 -1
  77. package/dashboard/dist/assets/{play-NqPqbW59.js → play-7MpFYU7a.js} +1 -1
  78. package/dashboard/dist/assets/{plus-DZP82JeP.js → plus-Dk-p9OaZ.js} +1 -1
  79. package/dashboard/dist/assets/{provider-display-BLo4k0bi.js → provider-display-Czwv4ClR.js} +1 -1
  80. package/dashboard/dist/assets/{react-CTLavVze.js → react-D9vco0ZF.js} +1 -1
  81. package/dashboard/dist/assets/{safePolygon-Yw5w5Nw8.js → safePolygon-Ca2iBTNb.js} +1 -1
  82. package/dashboard/dist/assets/{search-DgV6mpMO.js → search-Z2Hfw99-.js} +1 -1
  83. package/dashboard/dist/assets/{server-BybwAQjW.js → server-Dh_OraRG.js} +1 -1
  84. package/dashboard/dist/assets/{shield-check-BiTYwWdT.js → shield-check-59NUSZvk.js} +1 -1
  85. package/dashboard/dist/assets/{skills-api-Dx_Z8_SO.js → skills-api-Bguk8iPD.js} +1 -1
  86. package/dashboard/dist/assets/{sortable.esm-CmJnBLcr.js → sortable.esm-C9M-1k5o.js} +1 -1
  87. package/dashboard/dist/assets/{store-DzNIKGzf.js → store-nR9Ah4fr.js} +1 -1
  88. package/dashboard/dist/assets/{terminal-DPly-UPW.js → terminal-DKTXh9tj.js} +1 -1
  89. package/dashboard/dist/assets/{trash-DsL2ZAN5.js → trash-CQRRKbCs.js} +1 -1
  90. package/dashboard/dist/assets/{use-community-stats-CSTkXFed.js → use-community-stats-BoOd2Hhq.js} +1 -1
  91. package/dashboard/dist/assets/{use-latest-request-guard-CbAXXNlU.js → use-latest-request-guard-vQtFEBIX.js} +1 -1
  92. package/dashboard/dist/assets/{use-limits-display-prefs-mDl6Yv_-.js → use-limits-display-prefs-BRTj9_xd.js} +1 -1
  93. package/dashboard/dist/assets/{use-native-settings-Bu2C3b20.js → use-native-settings-CC6uEn3j.js} +1 -1
  94. package/dashboard/dist/assets/{use-ordered-list-DkGSfMTv.js → use-ordered-list-BUx6eIN7.js} +1 -1
  95. package/dashboard/dist/assets/use-reduced-motion-BZBrXeyc.js +1 -0
  96. package/dashboard/dist/assets/{use-session-efficiency-pref-B2GDf83h.js → use-session-efficiency-pref-BsUypXPb.js} +1 -1
  97. package/dashboard/dist/assets/{use-transform-BVVduBPJ.js → use-transform-oomGXI86.js} +1 -1
  98. package/dashboard/dist/assets/{use-usage-limits-fuzpbbVh.js → use-usage-limits-CXeCKJZ_.js} +1 -1
  99. package/dashboard/dist/assets/{useCurrency-CdDQzroJ.js → useCurrency-DIZEXq5g.js} +1 -1
  100. package/dashboard/dist/assets/{usePositioner-b6XXJtxE.js → usePositioner-Drh2sy7w.js} +1 -1
  101. package/dashboard/dist/assets/{useTokenFormat-90RoMQgI.js → useTokenFormat-DKURSOqb.js} +1 -1
  102. package/dashboard/dist/assets/{zap-xEHzTW-T.js → zap-BQ8nGK7b.js} +1 -1
  103. package/dashboard/dist/index.html +1 -1
  104. package/dashboard/dist/ip-check.html +1 -1
  105. package/dashboard/dist/leaderboard.html +1 -1
  106. package/dashboard/dist/pricing.html +1 -1
  107. package/dashboard/dist/share.html +1 -1
  108. package/dashboard/dist/terms.html +1 -1
  109. package/package.json +1 -1
  110. package/src/commands/status.js +5 -1
  111. package/src/commands/sync.js +28 -1
  112. package/src/lib/jsonl-lines.js +34 -4
  113. package/src/lib/pricing/curated-overrides.json +47 -0
  114. package/src/lib/pricing/matcher.js +11 -3
  115. package/src/lib/pricing/seed-snapshot.json +1 -1
  116. package/src/lib/rollout.js +148 -52
  117. package/src/lib/session-analytics.js +11 -10
  118. package/src/lib/upload-queue.js +1 -1
  119. package/dashboard/dist/assets/DialogDescription-ChbOGLc_.js +0 -1
  120. package/dashboard/dist/assets/__vite-browser-external-CPskAZuH.js +0 -45
  121. package/dashboard/dist/assets/use-reduced-motion-6nBw3cR5.js +0 -1
@@ -2846,6 +2846,8 @@ async function parseClaudeFile({
2846
2846
  continue;
2847
2847
  }
2848
2848
 
2849
+ if (isOmOClaudeSdkMirror(obj)) continue;
2850
+
2849
2851
  const usage = obj?.message?.usage || obj?.usage;
2850
2852
  if (!usage || typeof usage !== "object") continue;
2851
2853
 
@@ -4901,6 +4903,14 @@ function isCodexTotalOnlyResetSentinel(lastUsage, totalUsage) {
4901
4903
  // backward compatibility for the (msgId, reqId) format already persisted in
4902
4904
  // cursors.claudeHashes (msgId strings don't contain `:`, so the two formats
4903
4905
  // share the same Set without collision).
4906
+ // OmO Native routes Anthropic-subscription turns through the Claude Agent SDK,
4907
+ // which mirrors the same usage into ~/.claude/projects with entrypoint "sdk-ts"
4908
+ // and userType "external". OmO's passive reader already counts those turns
4909
+ // under source=omo — skip the mirrored Claude transcript copy (#726).
4910
+ function isOmOClaudeSdkMirror(obj) {
4911
+ return obj?.entrypoint === "sdk-ts" && obj?.userType === "external";
4912
+ }
4913
+
4904
4914
  function claudeMessageDedupKey(obj) {
4905
4915
  const msgId = typeof obj?.message?.id === "string" && obj.message.id ? obj.message.id : null;
4906
4916
  if (!msgId) return null;
@@ -5542,6 +5552,79 @@ function hasZcodeNativeUsageSchema(dbPath, sqliteOptions = {}) {
5542
5552
  }
5543
5553
  }
5544
5554
 
5555
+ // true: readable with content; false: confirmed empty; null: unavailable or
5556
+ // unreadable. Only a confirmed empty database is safe to replace during discovery.
5557
+ function zcodeDbHasContent(dbPath, sqliteOptions = {}) {
5558
+ if (!dbPath || !fssync.existsSync(dbPath)) return null;
5559
+ let snapshot = null;
5560
+ let effectiveDbPath = dbPath;
5561
+ if (isUncPath(dbPath)) {
5562
+ try {
5563
+ snapshot = snapshotSqliteDb(dbPath);
5564
+ effectiveDbPath = snapshot.path;
5565
+ } catch (_error) {
5566
+ // Preserve the existing direct-read fallback for transient snapshot
5567
+ // failures and UNC implementations that support SQLite locking.
5568
+ }
5569
+ }
5570
+ try {
5571
+ const rows = readSqliteJsonRows(
5572
+ effectiveDbPath,
5573
+ "SELECT (SELECT COUNT(*) FROM session) AS sessions, (SELECT COUNT(*) FROM model_usage) AS requests",
5574
+ {
5575
+ label: "ZCode",
5576
+ timeout: 10_000,
5577
+ maxBuffer: 1024 * 1024,
5578
+ ...sqliteOptions,
5579
+ throwOnReadFailure: true,
5580
+ },
5581
+ );
5582
+ const row = Array.isArray(rows) ? rows[0] : null;
5583
+ if (!row) return null;
5584
+ return (Number(row.sessions) || 0) > 0 || (Number(row.requests) || 0) > 0;
5585
+ } catch (_error) {
5586
+ return null;
5587
+ } finally {
5588
+ if (snapshot) snapshot.cleanup();
5589
+ }
5590
+ }
5591
+
5592
+ function hasZcodeCursorState(cursors) {
5593
+ return [cursors?.zcode, cursors?.zcode?.native, cursors?.zcode?.wsl]
5594
+ .some((state) => Object.keys(state?.messages || {}).length > 0) ||
5595
+ Object.keys(cursors?.hourly?.buckets || {}).some((key) => key.startsWith("zcode|")) ||
5596
+ Object.keys(cursors?.hourly?.groupQueued || {}).some((key) => key.startsWith("zcode|")) ||
5597
+ Object.keys(cursors?.projectHourly?.buckets || {}).some((key) => key.includes("|zcode|"));
5598
+ }
5599
+
5600
+ // Sync, session discovery and status must select the same installs. Once sync
5601
+ // records a priority-mode choice, retain it: switching databases underneath a
5602
+ // flat cursor would accumulate both installs in the same usage ledger.
5603
+ function resolveZcodeInstallPaths({ nativeValue, wslValue } = {}, env = process.env, deps = {}) {
5604
+ const mode = wsl.getWslMode(env);
5605
+ const priorityMode = (deps.platform || process.platform) === "win32" && ["native-first", "wsl-first"].includes(mode);
5606
+ const selection = deps.selection || deps.cursors?.zcodeInstallSelection;
5607
+ if (priorityMode && selection?.mode === mode &&
5608
+ selection.zcodeHome === (env.ZCODE_HOME?.trim() || null) &&
5609
+ typeof selection.dbPath === "string" && selection.dbPath) {
5610
+ // A newly discovered or temporarily unavailable install must not change
5611
+ // the cursor's owner. Only an explicit mode/home override releases it.
5612
+ return { native: selection.dbPath, wsl: null };
5613
+ }
5614
+ const paths = resolveInstallPaths({ nativeValue, wslValue }, env, deps);
5615
+ if (!priorityMode || (!selection && hasZcodeCursorState(deps.cursors))) {
5616
+ // Legacy cursors followed the original install resolver. Keep that choice
5617
+ // rather than feeding another database into an already-owned flat ledger.
5618
+ return paths;
5619
+ }
5620
+ const picked = paths.native || paths.wsl;
5621
+ const alternate = picked === nativeValue ? wslValue : nativeValue;
5622
+ if (picked && alternate && zcodeDbHasContent(picked) === false && zcodeDbHasContent(alternate) === true) {
5623
+ return { native: alternate, wsl: null };
5624
+ }
5625
+ return paths;
5626
+ }
5627
+
5545
5628
  function buildZcodeNativeUsageSql({ hasSessionDirectory }) {
5546
5629
  const directorySelect = hasSessionDirectory ? ", s.directory AS directory" : "";
5547
5630
  const directoryJoin = hasSessionDirectory
@@ -15626,6 +15709,44 @@ function ompEntryTimestampMs(msg, entry) {
15626
15709
  return null;
15627
15710
  }
15628
15711
 
15712
+ // Tool results can be hundreds of MB, exceeding V8's ~2^29-character string
15713
+ // limit without carrying usage. Skip them without decoding; assistant usage
15714
+ // records are orders of magnitude smaller.
15715
+ const OMP_LIKE_MAX_RECORD_BYTES = 64 * 1024 * 1024;
15716
+
15717
+ async function scanOmpLikeTranscript(filePath, startOffset, maxRecordBytes, onEntry) {
15718
+ let stream;
15719
+ try {
15720
+ stream = fssync.createReadStream(filePath, { start: startOffset });
15721
+ } catch {
15722
+ return startOffset;
15723
+ }
15724
+ let scannedEndOffset = startOffset;
15725
+ let committedEndOffset = startOffset;
15726
+ for await (const record of physicalJsonlRecords(stream, {
15727
+ invalidUtf8: "record",
15728
+ maxRecordBytes,
15729
+ })) {
15730
+ scannedEndOffset += record.physicalBytes;
15731
+ if (record.terminated) {
15732
+ committedEndOffset = scannedEndOffset;
15733
+ if (record.oversized || !record.utf8Valid || !record.line || !record.line.trim()) continue;
15734
+ let entry;
15735
+ try { entry = JSON.parse(record.line); } catch { continue; }
15736
+ onEntry(entry);
15737
+ continue;
15738
+ }
15739
+ // A final incomplete record may still be in the process of being written.
15740
+ if (record.oversized || !record.utf8Valid || !record.line || !record.line.trim()) break;
15741
+ let entry;
15742
+ try { entry = JSON.parse(record.line); } catch { break; }
15743
+ onEntry(entry);
15744
+ committedEndOffset = scannedEndOffset;
15745
+ break;
15746
+ }
15747
+ return committedEndOffset;
15748
+ }
15749
+
15629
15750
  // Shared implementation for the oh-my-pi session format. omp and omo both
15630
15751
  // persist it verbatim, so they differ only in where the sessions live, which
15631
15752
  // cursor namespace they own, and how reasoning tokens are spelled.
@@ -15647,6 +15768,7 @@ async function parseOmpLikeIncremental({
15647
15768
  resolveFileCwd,
15648
15769
  reasoningFields,
15649
15770
  reasoningIncludedInOutput = false,
15771
+ maxRecordBytes = OMP_LIKE_MAX_RECORD_BYTES,
15650
15772
  } = {}) {
15651
15773
  await ensureDir(path.dirname(queuePath));
15652
15774
  const projectEnabled = typeof projectQueuePath === "string" && projectQueuePath.length > 0;
@@ -15724,35 +15846,22 @@ async function parseOmpLikeIncremental({
15724
15846
  const startOffset = stat.size < prevSize || inodeChanged ? 0 : prevSize;
15725
15847
  if (stat.size <= startOffset) continue;
15726
15848
 
15727
- let stream;
15728
- try {
15729
- stream = fssync.createReadStream(filePath, {
15730
- encoding: "utf8",
15731
- start: startOffset,
15732
- });
15733
- } catch { continue; }
15734
- const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
15735
-
15736
- for await (const line of rl) {
15737
- if (!line || !line.trim()) continue;
15738
- let entry;
15739
- try { entry = JSON.parse(line); } catch { continue; }
15740
-
15849
+ const committedEndOffset = await scanOmpLikeTranscript(filePath, startOffset, maxRecordBytes, (entry) => {
15741
15850
  // First line of each file is type:"session" (header) — skip all
15742
15851
  // non-message records.
15743
- if (!entry || entry.type !== "message") continue;
15852
+ if (!entry || entry.type !== "message") return;
15744
15853
 
15745
15854
  // Only assistant messages carry token usage.
15746
15855
  const msg = entry.message;
15747
- if (!msg || msg.role !== "assistant") continue;
15856
+ if (!msg || msg.role !== "assistant") return;
15748
15857
 
15749
15858
  const usage = msg.usage;
15750
- if (!usage || typeof usage !== "object") continue;
15859
+ if (!usage || typeof usage !== "object") return;
15751
15860
 
15752
15861
  // Dedup by top-level entry id (8-char string assigned by oh-my-pi).
15753
15862
  const entryId = typeof entry.id === "string" && entry.id ? entry.id : null;
15754
- if (!entryId) continue;
15755
- if (seenIds.has(entryId)) continue;
15863
+ if (!entryId) return;
15864
+ if (seenIds.has(entryId)) return;
15756
15865
 
15757
15866
  recordsProcessed++;
15758
15867
 
@@ -15770,7 +15879,7 @@ async function parseOmpLikeIncremental({
15770
15879
  reasoningTokens === 0
15771
15880
  ) {
15772
15881
  seenIds.add(entryId);
15773
- continue;
15882
+ return;
15774
15883
  }
15775
15884
 
15776
15885
  // Entries with no resolvable timestamp are skipped — they cannot be
@@ -15778,12 +15887,12 @@ async function parseOmpLikeIncremental({
15778
15887
  const tsMs = ompEntryTimestampMs(msg, entry);
15779
15888
  if (tsMs == null) {
15780
15889
  seenIds.add(entryId);
15781
- continue;
15890
+ return;
15782
15891
  }
15783
15892
 
15784
15893
  const tsIso = new Date(tsMs).toISOString();
15785
15894
  const bucketStart = toUtcHalfHourStart(tsIso);
15786
- if (!bucketStart) continue;
15895
+ if (!bucketStart) return;
15787
15896
 
15788
15897
  // Use provided totalTokens when available; otherwise sum all components.
15789
15898
  // OmO folds reasoning into output, so the fallback must not add it again.
@@ -15820,14 +15929,12 @@ async function parseOmpLikeIncremental({
15820
15929
  bucketsQueued: touchedBuckets.size,
15821
15930
  });
15822
15931
  }
15823
- }
15932
+ });
15824
15933
 
15825
- let postStat = stat;
15826
- try { postStat = fssync.statSync(filePath); } catch {}
15827
15934
  fileOffsets[filePath] = {
15828
- size: postStat.size,
15829
- mtimeMs: postStat.mtimeMs,
15830
- ino: postStat.ino,
15935
+ size: committedEndOffset,
15936
+ mtimeMs: stat.mtimeMs,
15937
+ ino: stat.ino,
15831
15938
  };
15832
15939
  }
15833
15940
 
@@ -15860,28 +15967,16 @@ async function parseOmpLikeIncremental({
15860
15967
  : null;
15861
15968
  const projectRef = projectContext?.projectRef || null;
15862
15969
  const projectKey = projectContext?.projectKey || null;
15970
+ let committedEndOffset = stat.size;
15863
15971
 
15864
15972
  if (projectKey && projectRef) {
15865
- let stream;
15866
- try {
15867
- stream = fssync.createReadStream(filePath, {
15868
- encoding: "utf8",
15869
- start: startOffset,
15870
- });
15871
- } catch {
15872
- continue;
15873
- }
15874
- const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
15875
- for await (const line of rl) {
15876
- if (!line || !line.trim()) continue;
15877
- let entry;
15878
- try { entry = JSON.parse(line); } catch { continue; }
15973
+ committedEndOffset = await scanOmpLikeTranscript(filePath, startOffset, maxRecordBytes, (entry) => {
15879
15974
  const msg = entry?.type === "message" ? entry.message : null;
15880
15975
  const usage = msg?.role === "assistant" ? msg.usage : null;
15881
- if (!usage || typeof usage !== "object") continue;
15976
+ if (!usage || typeof usage !== "object") return;
15882
15977
 
15883
15978
  const entryId = typeof entry.id === "string" && entry.id ? entry.id : null;
15884
- if (!entryId || projectSeenIds.has(entryId)) continue;
15979
+ if (!entryId || projectSeenIds.has(entryId)) return;
15885
15980
 
15886
15981
  const input = toNonNegativeInt(usage.input);
15887
15982
  const output = toNonNegativeInt(usage.output);
@@ -15896,7 +15991,7 @@ async function parseOmpLikeIncremental({
15896
15991
  reasoningTokens === 0
15897
15992
  ) {
15898
15993
  projectSeenIds.add(entryId);
15899
- continue;
15994
+ return;
15900
15995
  }
15901
15996
 
15902
15997
  const tsMs = ompEntryTimestampMs(msg, entry);
@@ -15905,7 +16000,7 @@ async function parseOmpLikeIncremental({
15905
16000
  : toUtcHalfHourStart(new Date(tsMs).toISOString());
15906
16001
  if (!bucketStart) {
15907
16002
  projectSeenIds.add(entryId);
15908
- continue;
16003
+ return;
15909
16004
  }
15910
16005
 
15911
16006
  const totalTokens =
@@ -15932,15 +16027,13 @@ async function parseOmpLikeIncremental({
15932
16027
  addTotals(projectBucket.totals, delta);
15933
16028
  projectTouchedBuckets.add(projectBucketKey(projectKey, source, bucketStart));
15934
16029
  projectSeenIds.add(entryId);
15935
- }
16030
+ });
15936
16031
  }
15937
16032
 
15938
- let postStat = stat;
15939
- try { postStat = fssync.statSync(filePath); } catch {}
15940
16033
  projectFileOffsets[filePath] = {
15941
- size: postStat.size,
15942
- mtimeMs: postStat.mtimeMs,
15943
- ino: postStat.ino,
16034
+ size: committedEndOffset,
16035
+ mtimeMs: stat.mtimeMs,
16036
+ ino: stat.ino,
15944
16037
  };
15945
16038
  }
15946
16039
  }
@@ -24595,6 +24688,9 @@ module.exports = {
24595
24688
  coerceEpochMs,
24596
24689
  normalizeOpencodeModelFields,
24597
24690
  hasZcodeNativeUsageSchema,
24691
+ zcodeDbHasContent,
24692
+ hasZcodeCursorState,
24693
+ resolveZcodeInstallPaths,
24598
24694
  resolveQoderDbPath,
24599
24695
  resolveQoderDbPaths,
24600
24696
  resolveQoderCnDbPaths,
@@ -28,12 +28,13 @@ const fsp = require("node:fs/promises");
28
28
  const os = require("node:os");
29
29
  const path = require("node:path");
30
30
  const readline = require("node:readline");
31
- const { listClaudeProjectFiles, listRolloutFilesDeep, claudeMessageDedupKey, normalizeClaudeUsage, readZcodeDbMessages, coerceEpochMs, normalizeOpencodeModelFields, normalizeOpencodeTokens, deriveOpencodeMessageFingerprint } = require("./rollout");
31
+ const { listClaudeProjectFiles, listRolloutFilesDeep, claudeMessageDedupKey, normalizeClaudeUsage, readZcodeDbMessages, coerceEpochMs, resolveZcodeInstallPaths, normalizeOpencodeModelFields, normalizeOpencodeTokens, deriveOpencodeMessageFingerprint } = require("./rollout");
32
32
  const { parseCodexRolloutFile } = require("./codex-rollout-parser");
33
33
  const { computeRowCost, getModelPricingInfo } = require("./pricing");
34
34
  const { normalizePerformance, mergePerformance, createClaudePerformanceCollector, createCodexPerformanceCollector, createZcodePerformanceCollector } = require("./session-performance");
35
35
  const { readSqliteJsonRows, readSqliteJsonRowsAsync } = require("./sqlite-reader");
36
36
  const { resolveZcodeNativeDbPath, resolveInstallPaths } = require("./install-resolver");
37
+ const { readCursorStateSummary } = require("./cursor-store");
37
38
  const { USD_TICKS_PER_USD, normalizeGrokUsage } = require("./grok-usage");
38
39
  const wsl = require("./wsl-probe");
39
40
  const {
@@ -1975,7 +1976,7 @@ async function discoverSessionFiles(home, env = process.env, deps = {}) {
1975
1976
  // it like the usage sync does (src/commands/sync.js): native candidate first
1976
1977
  // (ZCODE_HOME, home, win32 APPDATA fallback), then the WSL install when the
1977
1978
  // WSL mode probes it, picked with the same mode priority (default wsl-first)
1978
- // so the browser reads the same installs the usage card counts. "both"
1979
+ // so the browser reads the same install the usage card counts. "both"
1979
1980
  // preserves both paths; the scanner merges shared session/event identities.
1980
1981
  const zcodePlatform = deps.platform || process.platform;
1981
1982
  const zcodeNativeDbPath = (deps.resolveZcodeNativeDbPath || resolveZcodeNativeDbPath)({ home, env, platform: zcodePlatform });
@@ -1991,14 +1992,14 @@ async function discoverSessionFiles(home, env = process.env, deps = {}) {
1991
1992
  : null;
1992
1993
  const zcodeExisting = (dbPath) => (dbPath && fs.existsSync(dbPath) ? dbPath : null);
1993
1994
  const zcodeWslDbPath = zcodeWslHome ? path.join(zcodeWslHome, "cli", "db", "db.sqlite") : null;
1994
- const zcodePaths = zcodePlatform === "win32"
1995
- ? wsl.resolveAllWin32Paths({
1996
- nativeValue: zcodeExisting(zcodeNativeDbPath),
1997
- wslValue: zcodeExisting(zcodeWslDbPath),
1998
- env,
1999
- platform: zcodePlatform,
2000
- })
2001
- : { native: zcodeExisting(zcodeNativeDbPath), wsl: null };
1995
+ const zcodeNativeExisting = zcodeExisting(zcodeNativeDbPath);
1996
+ const { cursors } = zcodePlatform === "win32"
1997
+ ? await readCursorStateSummary({ trackerDir: path.join(home, ".tokentracker", "tracker") })
1998
+ : {};
1999
+ const zcodePaths = resolveZcodeInstallPaths({
2000
+ nativeValue: zcodePlatform === "win32" ? zcodeNativeDbPath : zcodeNativeExisting,
2001
+ wslValue: zcodeWslDbPath,
2002
+ }, env, { platform: zcodePlatform, cursors });
2002
2003
  const zcode = [...new Set(Object.values(zcodePaths).filter(Boolean))];
2003
2004
  const opencodeNativeDir = env.OPENCODE_HOME || path.join(env.XDG_DATA_HOME || path.join(home, ".local", "share"), "opencode");
2004
2005
  const opencodeWslDir = zcodePlatform === "win32" && zcodeProbeWsl && wsl.shouldProbeWsl(env)
@@ -122,7 +122,7 @@ async function compactPendingQueue({ queuePath, offset = 0, minPendingBytes = BA
122
122
  temporary = null;
123
123
  return { compacted: true, removedRecords, pendingBytesBefore, pendingBytesAfter };
124
124
  } catch (error) {
125
- if (error.code === "ENOENT" && !input) return { compacted: false, removedRecords: 0, pendingBytesBefore: 0, pendingBytesAfter: 0 };
125
+ if (error.code === "ENOENT" && !input && !temporary) return { compacted: false, removedRecords: 0, pendingBytesBefore: 0, pendingBytesAfter: 0 };
126
126
  throw error;
127
127
  } finally {
128
128
  await input?.close();
@@ -1 +0,0 @@
1
- import{r as n,K as a}from"./__vite-browser-external-CPskAZuH.js";import{u as p}from"./DialogTitle-BBxNf3O8.js";import{u as c}from"./composite-I_1t6PMI.js";const x=n.forwardRef(function(e,r){const{render:l,className:u,style:m,id:s,...t}=e,i=p(),o=c(s);return i.useSyncedValueWithCleanup("descriptionElementId",o),a("p",e,{ref:r,props:[{id:o},t]})});export{x as D};