tokentracker-cli 0.95.0 → 0.95.2

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 (99) hide show
  1. package/dashboard/dist/assets/{AchievementBadge-Pi3Gvbm8.js → AchievementBadge-DtB1MwFe.js} +1 -1
  2. package/dashboard/dist/assets/{AchievementsPage-C4WLT8Rl.js → AchievementsPage--vddu0dC.js} +2 -2
  3. package/dashboard/dist/assets/{AchievementsSection-ilvR2ssr.js → AchievementsSection-CDl9hsbj.js} +1 -1
  4. package/dashboard/dist/assets/{BadgeDetailModal-CjEeToxa.js → BadgeDetailModal-C7WsIRy9.js} +1 -1
  5. package/dashboard/dist/assets/{BotAnimated-DDf9pBuK.js → BotAnimated-CmXyzIhX.js} +1 -1
  6. package/dashboard/dist/assets/{Card-DNous84-.js → Card-SUbC3VNS.js} +1 -1
  7. package/dashboard/dist/assets/{ClawdAnimated-BLMIRntb.js → ClawdAnimated-BdEbs8Me.js} +2 -2
  8. package/dashboard/dist/assets/{CommandPalette-vVekhoFO.js → CommandPalette-D1RLLeHB.js} +1 -1
  9. package/dashboard/dist/assets/{CommunityStatsModal-C0Kp_J8B.js → CommunityStatsModal-5ESGJgZQ.js} +1 -1
  10. package/dashboard/dist/assets/{ConfirmModal-DFdpcx12.js → ConfirmModal-DsCd51b2.js} +1 -1
  11. package/dashboard/dist/assets/{Controls-RNhqybmE.js → Controls-BVrLC_QD.js} +1 -1
  12. package/dashboard/dist/assets/{DashboardPage-BQjQh1IQ.js → DashboardPage-DtUOG6og.js} +1 -1
  13. package/dashboard/dist/assets/{DevicePage-DIhh-YAk.js → DevicePage-zYjj-m5V.js} +1 -1
  14. package/dashboard/dist/assets/{DialogClose-ZqxYVIzl.js → DialogClose-D66yBqkd.js} +1 -1
  15. package/dashboard/dist/assets/DialogDescription-BVlaoajd.js +1 -0
  16. package/dashboard/dist/assets/{DialogTitle-BiRcejlu.js → DialogTitle-C4ia00lc.js} +1 -1
  17. package/dashboard/dist/assets/{FadeIn-BDBQpeGe.js → FadeIn-DF2c8XaR.js} +1 -1
  18. package/dashboard/dist/assets/{HeaderGithubStar-CztIwtKT.js → HeaderGithubStar-Cnr3aqcX.js} +1 -1
  19. package/dashboard/dist/assets/{HoverTooltip-Cfso7LTC.js → HoverTooltip-DBOpxlH7.js} +1 -1
  20. package/dashboard/dist/assets/{IpCheckPage-CNE7dLs3.js → IpCheckPage-CUY6KBaV.js} +1 -1
  21. package/dashboard/dist/assets/{LandingPage-B2a3cweM.js → LandingPage-DKL8hZEq.js} +2 -2
  22. package/dashboard/dist/assets/{LeaderboardAvatar-CwmZnIji.js → LeaderboardAvatar-DRYeMNlq.js} +1 -1
  23. package/dashboard/dist/assets/{LeaderboardPage-DkTHj3fB.js → LeaderboardPage-DpD9Az3k.js} +2 -2
  24. package/dashboard/dist/assets/{LeaderboardProfileModal-CefTO3Zh.js → LeaderboardProfileModal-Dfg0V13F.js} +3 -3
  25. package/dashboard/dist/assets/{LeaderboardProfilePage-CnOXlQ3L.js → LeaderboardProfilePage-DH-jcVDu.js} +1 -1
  26. package/dashboard/dist/assets/{LimitsPage-BIZ1-6BB.js → LimitsPage-CVCKPScf.js} +1 -1
  27. package/dashboard/dist/assets/{LocalOnlyNotice-CdHQiPhy.js → LocalOnlyNotice-Bj_m0hw7.js} +1 -1
  28. package/dashboard/dist/assets/{LoginCard-yXLA9geu.js → LoginCard-BVKvtx60.js} +1 -1
  29. package/dashboard/dist/assets/{LoginModal-BF2Pfkqa.js → LoginModal-mUGiSIJN.js} +1 -1
  30. package/dashboard/dist/assets/{LoginPage-DuOypZap.js → LoginPage-BORCuCUt.js} +1 -1
  31. package/dashboard/dist/assets/{PetPage-BH3xDVV9.js → PetPage-D1JfEOVA.js} +1 -1
  32. package/dashboard/dist/assets/{PopoverPopup-CewF_cGz.js → PopoverPopup-DK3et-HG.js} +1 -1
  33. package/dashboard/dist/assets/{ProviderIcon-Dg0K0RPn.js → ProviderIcon-By8FAjxR.js} +1 -1
  34. package/dashboard/dist/assets/{ResetPasswordPage-CYwt85O3.js → ResetPasswordPage-DPa81jqa.js} +1 -1
  35. package/dashboard/dist/assets/{SegmentedControl-BJkXXomQ.js → SegmentedControl-DLzS-2eZ.js} +1 -1
  36. package/dashboard/dist/assets/{Select-B3_WH0Qe.js → Select-C05W6XEM.js} +1 -1
  37. package/dashboard/dist/assets/{SelectItemText-BUby_DUu.js → SelectItemText-CcfB3blP.js} +1 -1
  38. package/dashboard/dist/assets/{ServiceStatusPage-BDlVZk4i.js → ServiceStatusPage-ByqLbHv1.js} +1 -1
  39. package/dashboard/dist/assets/{SessionsPage-TaC9nahK.js → SessionsPage-CEDv1thj.js} +1 -1
  40. package/dashboard/dist/assets/{SettingsPage-CudcQh6m.js → SettingsPage-DAcndJZQ.js} +2 -2
  41. package/dashboard/dist/assets/{SkillsPage-iDLQ8lpZ.js → SkillsPage-BuuqQuNU.js} +1 -1
  42. package/dashboard/dist/assets/{TokenGalaxy-DtYzUsXU.js → TokenGalaxy-DzccxkXW.js} +1 -1
  43. package/dashboard/dist/assets/{ToolbarRootContext-PC8Tg1Jz.js → ToolbarRootContext-XUDMp5uy.js} +1 -1
  44. package/dashboard/dist/assets/{TrendMonitor-r8n8YO_9.js → TrendMonitor-B-yYabbz.js} +1 -1
  45. package/dashboard/dist/assets/{WidgetsPage-vzfGccWb.js → WidgetsPage-CwUDjEFo.js} +1 -1
  46. package/dashboard/dist/assets/{WrappedPage-DaKuIkYE.js → WrappedPage-Dr9ow3gH.js} +1 -1
  47. package/dashboard/dist/assets/{__vite-browser-external-Dnxl50k1.js → __vite-browser-external-DPOlUkcY.js} +2 -2
  48. package/dashboard/dist/assets/{agent-logos-B7nzn3n2.js → agent-logos-Cqc_suYk.js} +1 -1
  49. package/dashboard/dist/assets/{arrow-up-right-CsxyvCER.js → arrow-up-right-DSsR3Ntr.js} +1 -1
  50. package/dashboard/dist/assets/{check-hYcC7MdH.js → check-Cy35lAKh.js} +1 -1
  51. package/dashboard/dist/assets/{composite-C79LNbex.js → composite-BDwOlhhX.js} +1 -1
  52. package/dashboard/dist/assets/{copy-Cz4qzE62.js → copy-4szu5cYh.js} +1 -1
  53. package/dashboard/dist/assets/{download-CUaSNE2E.js → download-DdGW6Krz.js} +1 -1
  54. package/dashboard/dist/assets/{external-link-vq7kIyKK.js → external-link-DwSt83cq.js} +1 -1
  55. package/dashboard/dist/assets/{flame-CB6K4eR6.js → flame-DubukWqK.js} +1 -1
  56. package/dashboard/dist/assets/{format-tokens-KI-e_JQ-.js → format-tokens-B70EdQcN.js} +1 -1
  57. package/dashboard/dist/assets/{icons-3maItkrp.js → icons-B86TLdAj.js} +1 -1
  58. package/dashboard/dist/assets/{index-BRXoZ7Jj.js → index-B_KMoBeK.js} +1 -1
  59. package/dashboard/dist/assets/{index-CUbC50Zp.js → index-DI-W-pun.js} +1 -1
  60. package/dashboard/dist/assets/{index-C5WrfmLB.js → index-DzEMcUwI.js} +1 -1
  61. package/dashboard/dist/assets/{info-r0yGihUe.js → info-BzFpLbl6.js} +1 -1
  62. package/dashboard/dist/assets/{limits-providers-BN_94oe9.js → limits-providers-DxZOjiXQ.js} +1 -1
  63. package/dashboard/dist/assets/{link-2-J9t0VXWM.js → link-2-BQp7-3T7.js} +1 -1
  64. package/dashboard/dist/assets/{loader-circle-DCvOsPze.js → loader-circle-lrDmbIg5.js} +1 -1
  65. package/dashboard/dist/assets/{maximize-2-CZ9JhGjf.js → maximize-2-Cnd_Oth8.js} +1 -1
  66. package/dashboard/dist/assets/{modifiers.esm-DJ0_WKg0.js → modifiers.esm-CPTpZdHU.js} +1 -1
  67. package/dashboard/dist/assets/{monitor-smartphone-nqbWjJx_.js → monitor-smartphone-C7UY1zRx.js} +1 -1
  68. package/dashboard/dist/assets/{plus-Cg7wd2jK.js → plus-Bglp9N_7.js} +1 -1
  69. package/dashboard/dist/assets/{provider-display-B9L1nRY9.js → provider-display-CGsFySs6.js} +1 -1
  70. package/dashboard/dist/assets/{react-D2AD6Pk2.js → react-DKXriBSF.js} +1 -1
  71. package/dashboard/dist/assets/{search-uNiWba1h.js → search-D_LLDRdU.js} +1 -1
  72. package/dashboard/dist/assets/{skills-api-BBYuAhiL.js → skills-api-CJWjKIco.js} +1 -1
  73. package/dashboard/dist/assets/{sortable.esm-CzPxc949.js → sortable.esm-zlY09tye.js} +1 -1
  74. package/dashboard/dist/assets/{store-D4A7EUwE.js → store-8e16Ac2h.js} +1 -1
  75. package/dashboard/dist/assets/{terminal-Crmm_yKF.js → terminal-CR228ekj.js} +1 -1
  76. package/dashboard/dist/assets/{trash-2-ia9VjRnU.js → trash-2-BjLWewOp.js} +1 -1
  77. package/dashboard/dist/assets/{use-community-stats-KaGCAxaF.js → use-community-stats-DhOJPaVg.js} +1 -1
  78. package/dashboard/dist/assets/{use-latest-request-guard-Dy8gSDkk.js → use-latest-request-guard-lcGhb-NC.js} +1 -1
  79. package/dashboard/dist/assets/{use-limits-display-prefs-JSTYlbTD.js → use-limits-display-prefs-DJIImtLN.js} +1 -1
  80. package/dashboard/dist/assets/{use-ordered-list-C4JnHdE0.js → use-ordered-list-BJZfZuoJ.js} +1 -1
  81. package/dashboard/dist/assets/use-reduced-motion-DdpxZQoh.js +1 -0
  82. package/dashboard/dist/assets/{use-session-efficiency-pref-CgyUz5Gc.js → use-session-efficiency-pref-C-l4b1cD.js} +1 -1
  83. package/dashboard/dist/assets/{use-transform-C7-jSJ9k.js → use-transform-UjIt477N.js} +1 -1
  84. package/dashboard/dist/assets/{use-usage-limits-B3YYqn-v.js → use-usage-limits-DqoTZrqT.js} +1 -1
  85. package/dashboard/dist/assets/{useCurrency-CK_gcb3J.js → useCurrency-CnM8aB4v.js} +1 -1
  86. package/dashboard/dist/assets/{useTokenFormat-C_a-UkCA.js → useTokenFormat-D4pAK2ld.js} +1 -1
  87. package/dashboard/dist/assets/{zap-BIJNn-Ic.js → zap-Du1IyEtj.js} +1 -1
  88. package/dashboard/dist/index.html +1 -1
  89. package/dashboard/dist/ip-check.html +1 -1
  90. package/dashboard/dist/leaderboard.html +1 -1
  91. package/dashboard/dist/share.html +1 -1
  92. package/package.json +1 -1
  93. package/src/commands/status.js +51 -8
  94. package/src/commands/sync.js +75 -0
  95. package/src/lib/pricing/curated-overrides.json +18 -0
  96. package/src/lib/pricing/seed-snapshot.json +1 -1
  97. package/src/lib/rollout.js +383 -0
  98. package/dashboard/dist/assets/DialogDescription-DDM4aanX.js +0 -1
  99. package/dashboard/dist/assets/use-reduced-motion-AQE0ummK.js +0 -1
@@ -5761,6 +5761,384 @@ async function parseQoderDbIncremental({
5761
5761
  return { messagesProcessed, eventsAggregated, bucketsQueued, projectBucketsQueued };
5762
5762
  }
5763
5763
 
5764
+ // ── Qoder (new) — ~/.qoder/projects JSONL (com.qoder.app.stable, 2026-08+) ──
5765
+ //
5766
+ // Qoder 2026-08 (app 0.1.2+) migrated from SharedClientCache/cache/db/local.db
5767
+ // to Electron main.sqlite + ~/.qoder/projects/<slug>/<sessionId>.jsonl.
5768
+ // The new transcript's message.usage no longer carries prompt_tokens — it is a
5769
+ // credit-billed SDK: {input_tokens:0, output_tokens:0, credits:3.2, billable:true}.
5770
+ // Only rows with authoritative token fields are counted: credit-only usage
5771
+ // without tokens is intentionally not counted (usage stays unsupported, no
5772
+ // token delta) because there is no first-party evidence for a credit→token
5773
+ // rate. Cost was never estimated — no authoritative credit→USD rate is
5774
+ // published, so total_cost_usd stays 0.
5775
+ // Old local.db is kept as a legacy fallback; both sources now use distinct
5776
+ // cursor namespaces (qoder vs qoderNew) via disjoint messageKey prefixes
5777
+ // (row: vs jsonl:) but upload under the same source="qoder".
5778
+
5779
+ function resolveQoderProjectsDir({ home = os.homedir(), env = process.env, platform = process.platform, deps = {} } = {}) {
5780
+ const override = typeof env.QODER_PROJECTS_DIR === "string" && env.QODER_PROJECTS_DIR.trim()
5781
+ ? path.resolve(env.QODER_PROJECTS_DIR.trim())
5782
+ : null;
5783
+ if (override) return override;
5784
+ // QODER_HOME points at the app support dir for the legacy DB; the new
5785
+ // projects dir is always ~/.qoder regardless of QODER_HOME.
5786
+ // On Windows also probe WSL distro home (same pattern as other providers).
5787
+ if (platform === "win32" && !env.QODER_PROJECTS_DIR) {
5788
+ const discoverWslHome = deps.discoverWslHome || wsl.discoverWslHome;
5789
+ const wslRoot = wsl.shouldProbeWsl(env) ? discoverWslHome(".qoder", { ...deps, env }) : null;
5790
+ if (wslRoot) {
5791
+ const wslProjects = path.join(wslRoot, "projects");
5792
+ if ((deps.existsSync || fssync.existsSync)(wslProjects)) return wslProjects;
5793
+ }
5794
+ }
5795
+ return path.join(home, ".qoder", "projects");
5796
+ }
5797
+
5798
+ function resolveQoderCnProjectsDir({ home = os.homedir(), env = process.env, platform = process.platform, deps = {} } = {}) {
5799
+ const override = typeof env.QODER_CN_PROJECTS_DIR === "string" && env.QODER_CN_PROJECTS_DIR.trim()
5800
+ ? path.resolve(env.QODER_CN_PROJECTS_DIR.trim())
5801
+ : null;
5802
+ if (override) return override;
5803
+ // CN and international currently share ~/.qoder; keep a distinct override for
5804
+ // future split without double-counting by default.
5805
+ if (platform === "win32" && !env.QODER_CN_PROJECTS_DIR) {
5806
+ const discoverWslHome = deps.discoverWslHome || wsl.discoverWslHome;
5807
+ const wslRoot = wsl.shouldProbeWsl(env) ? discoverWslHome(".qoder", { ...deps, env }) : null;
5808
+ if (wslRoot) {
5809
+ const wslProjects = path.join(wslRoot, "projects");
5810
+ if ((deps.existsSync || fssync.existsSync)(wslProjects)) return wslProjects;
5811
+ }
5812
+ }
5813
+ return path.join(home, ".qoder", "projects");
5814
+ }
5815
+
5816
+ async function listQoderNewSessionFiles(projectsDir) {
5817
+ const out = [];
5818
+ if (!projectsDir || !fssync.existsSync(projectsDir)) return out;
5819
+ async function walk(dir) {
5820
+ const entries = await safeReadDir(dir);
5821
+ for (const e of entries) {
5822
+ const p = path.join(dir, e.name);
5823
+ if (e.isDirectory()) {
5824
+ await walk(p);
5825
+ } else if (e.isFile() && e.name.endsWith(".jsonl")) {
5826
+ out.push(p);
5827
+ }
5828
+ }
5829
+ }
5830
+ await walk(projectsDir);
5831
+ out.sort((a, b) => a.localeCompare(b));
5832
+ return out;
5833
+ }
5834
+
5835
+ function qoderNewModelFromRecord(record) {
5836
+ const msgModel = record?.message?.model;
5837
+ const direct = typeof msgModel === "string" ? msgModel.trim() : "";
5838
+ return normalizeModelInput(direct) || "qoder-agent";
5839
+ }
5840
+
5841
+ function qoderNewMessageKey(record, filePath, lineIndex = 0) {
5842
+ const msgId = normalizeMessageKeyPart(record?.message?.id)
5843
+ || normalizeMessageKeyPart(record?.uuid)
5844
+ || normalizeMessageKeyPart(record?.id)
5845
+ || null;
5846
+ const sessionId = normalizeMessageKeyPart(record?.sessionId || record?.session_id);
5847
+ // Prefix to avoid collision with legacy row: keys; fallback includes line index
5848
+ // so multiple no-id records in the same file remain distinct.
5849
+ const fallbackSuffix = Number.isFinite(lineIndex) ? `${filePath}:${lineIndex}` : filePath;
5850
+ if (sessionId && msgId) return `jsonl:${sessionId}|${msgId}`;
5851
+ if (msgId) return `jsonl:${msgId}`;
5852
+ if (sessionId) return `jsonl:${sessionId}|${record?.uuid || fallbackSuffix}`;
5853
+ return `jsonl:${fallbackSuffix}|${record?.uuid || ""}`;
5854
+ }
5855
+
5856
+ function qoderNewTimestampMs(record) {
5857
+ return coerceEpochMs(record?.timestamp)
5858
+ || coerceEpochMs(record?.message?.timestamp)
5859
+ || parseIsoTimestampMs(record?.timestamp)
5860
+ || parseIsoTimestampMs(record?.message?.timestamp)
5861
+ || 0;
5862
+ }
5863
+
5864
+ function normalizeQoderNewTokens(usage) {
5865
+ if (!usage || typeof usage !== "object") return null;
5866
+ const credits = Number(usage.credits ?? usage.original_credits ?? 0);
5867
+ let input = Number(usage.input_tokens ?? 0);
5868
+ let cached = Number(usage.cache_read_input_tokens ?? usage.cached_tokens ?? 0);
5869
+ let cacheCreation = Number(usage.cache_creation_input_tokens ?? 0);
5870
+ let output = Number(usage.output_tokens ?? 0);
5871
+ // Guard against malformed numbers (NaN/Infinity/negative) — align with
5872
+ // legacy normalizeQoderTokens which returns null on such input.
5873
+ if (!Number.isFinite(input) || input < 0) input = 0;
5874
+ if (!Number.isFinite(cached) || cached < 0) cached = 0;
5875
+ if (!Number.isFinite(cacheCreation) || cacheCreation < 0) cacheCreation = 0;
5876
+ if (!Number.isFinite(output) || output < 0) output = 0;
5877
+ // Only rows with authoritative token fields are counted; anything else
5878
+ // falls through to null (unsupported, no token delta).
5879
+ if (input > 0 || cached > 0 || cacheCreation > 0 || output > 0) {
5880
+ const inp = Math.max(0, Math.trunc(input));
5881
+ const cach = Math.max(0, Math.trunc(cached));
5882
+ const out = Math.max(0, Math.trunc(output));
5883
+ const cc = Math.max(0, Math.trunc(cacheCreation));
5884
+ return {
5885
+ input_tokens: inp,
5886
+ cached_input_tokens: cach,
5887
+ cache_creation_input_tokens: cc,
5888
+ output_tokens: out,
5889
+ reasoning_output_tokens: 0,
5890
+ total_tokens: inp + cach + cc + out,
5891
+ billable_total_tokens: inp + cach + cc + out,
5892
+ credits: Number.isFinite(credits) && credits > 0 ? credits : 0,
5893
+ usage_precision: null,
5894
+ };
5895
+ }
5896
+ // Credit-only usage without authoritative token fields is intentionally
5897
+ // not counted (no token delta): there is no first-party evidence for a
5898
+ // credit→token rate. The caller still counts billable messages as
5899
+ // conversation activity.
5900
+ return null;
5901
+ }
5902
+
5903
+ async function parseQoderNewIncremental({
5904
+ sessionFiles,
5905
+ cursors,
5906
+ queuePath,
5907
+ projectQueuePath,
5908
+ onProgress,
5909
+ sourceKey = "qoder",
5910
+ cursorKey = "qoderNew",
5911
+ publicRepoResolver,
5912
+ } = {}) {
5913
+ await ensureDir(path.dirname(queuePath));
5914
+ const files = Array.isArray(sessionFiles) ? sessionFiles : [];
5915
+ // One-time migration: pre-#549 stored JSONL keys under the legacy "qoder"
5916
+ // cursor (same namespace as SQLite). Move them to the new isolated namespace
5917
+ // so history is not double-counted and legacy multi-install state is preserved.
5918
+ if (cursorKey === "qoderNew" && cursors?.qoder && !cursors?.qoderNew) {
5919
+ const legacy = normalizeQoderState(cursors.qoder);
5920
+ const jsonlEntries = Object.entries(legacy.messages).filter(([k]) => k.startsWith("jsonl:"));
5921
+ if (jsonlEntries.length > 0) {
5922
+ const migrated = {};
5923
+ for (const [k, v] of jsonlEntries) {
5924
+ migrated[k] = v;
5925
+ delete legacy.messages[k];
5926
+ }
5927
+ cursors.qoderNew = { messages: migrated, updatedAt: legacy.updatedAt || new Date().toISOString() };
5928
+ }
5929
+ }
5930
+ if (cursorKey === "qoderCnNew" && cursors?.["qoder-cn"] && !cursors?.["qoderCnNew"]) {
5931
+ const legacy = normalizeQoderState(cursors["qoder-cn"]);
5932
+ const jsonlEntries = Object.entries(legacy.messages).filter(([k]) => k.startsWith("jsonl:"));
5933
+ if (jsonlEntries.length > 0) {
5934
+ const migrated = {};
5935
+ for (const [k, v] of jsonlEntries) {
5936
+ migrated[k] = v;
5937
+ delete legacy.messages[k];
5938
+ }
5939
+ cursors["qoderCnNew"] = { messages: migrated, updatedAt: legacy.updatedAt || new Date().toISOString() };
5940
+ }
5941
+ }
5942
+ const hourlyState = normalizeHourlyState(cursors?.hourly);
5943
+ const qoderState = normalizeQoderState(cursors?.[cursorKey]);
5944
+ const projectEnabled = typeof projectQueuePath === "string" && projectQueuePath.length > 0;
5945
+ const projectState = projectEnabled ? normalizeProjectState(cursors?.projectHourly) : null;
5946
+ const projectTouchedBuckets = projectEnabled ? new Set() : null;
5947
+ const projectMetaCache = projectEnabled ? new Map() : null;
5948
+ const publicRepoCache = projectEnabled ? new Map() : null;
5949
+ const touchedBuckets = new Set();
5950
+ const cb = typeof onProgress === "function" ? onProgress : null;
5951
+
5952
+ // Build current snapshot from all JSONL files
5953
+ const currentByKey = new Map();
5954
+ const fileCount = files.length;
5955
+ for (let fileIdx = 0; fileIdx < files.length; fileIdx++) {
5956
+ const filePath = files[fileIdx];
5957
+ let raw;
5958
+ try {
5959
+ raw = await fs.readFile(filePath, "utf8");
5960
+ } catch (_e) {
5961
+ continue;
5962
+ }
5963
+ const lines = raw.split("\n");
5964
+ for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
5965
+ const line = lines[lineIdx];
5966
+ if (!line.trim()) continue;
5967
+ let record;
5968
+ try {
5969
+ record = JSON.parse(line);
5970
+ } catch (_e) {
5971
+ continue;
5972
+ }
5973
+ if (record?.type !== "assistant") continue;
5974
+ const msg = record?.message;
5975
+ if (!msg || msg.role !== "assistant") continue;
5976
+ // Skip synthetic sub-agent streaming chunks that carry no usage
5977
+ const usage = msg.usage;
5978
+ if (!usage || typeof usage !== "object") continue;
5979
+ const base = normalizeQoderNewTokens(usage);
5980
+ // Allow billable zero-token messages to still count conversation (no token delta)
5981
+ const isBillable = usage.billable !== false;
5982
+ if (!base && !isBillable) continue;
5983
+ const timestampMs = qoderNewTimestampMs(record);
5984
+ if (!timestampMs) continue;
5985
+ const bucketStart = toUtcHalfHourStart(new Date(timestampMs).toISOString());
5986
+ if (!bucketStart) continue;
5987
+ const messageKey = qoderNewMessageKey(record, filePath, lineIdx);
5988
+ if (!messageKey) continue;
5989
+ const model = qoderNewModelFromRecord(record);
5990
+ const totals = base ? {
5991
+ input_tokens: base.input_tokens,
5992
+ cached_input_tokens: base.cached_input_tokens,
5993
+ cache_creation_input_tokens: base.cache_creation_input_tokens,
5994
+ output_tokens: base.output_tokens,
5995
+ reasoning_output_tokens: 0,
5996
+ total_tokens: base.total_tokens,
5997
+ billable_total_tokens: base.billable_total_tokens,
5998
+ total_cost_usd: 0,
5999
+ usage_precision: base.usage_precision || undefined,
6000
+ conversation_count: 1,
6001
+ } : {
6002
+ input_tokens: 0,
6003
+ cached_input_tokens: 0,
6004
+ cache_creation_input_tokens: 0,
6005
+ output_tokens: 0,
6006
+ reasoning_output_tokens: 0,
6007
+ total_tokens: 0,
6008
+ billable_total_tokens: 0,
6009
+ total_cost_usd: 0,
6010
+ conversation_count: 1,
6011
+ };
6012
+ let projectKey = null;
6013
+ let projectRef = null;
6014
+ if (projectEnabled) {
6015
+ const rawCwd = typeof record?.cwd === "string" ? record.cwd.trim() : "";
6016
+ if (rawCwd) {
6017
+ const startDir = wsl.mapWslCwdToUnc(rawCwd, filePath);
6018
+ const context = await resolveProjectContextForPath({
6019
+ startDir,
6020
+ projectMetaCache,
6021
+ publicRepoCache,
6022
+ publicRepoResolver,
6023
+ projectState,
6024
+ });
6025
+ projectKey = context?.projectKey || null;
6026
+ projectRef = context?.projectRef || null;
6027
+ }
6028
+ }
6029
+ currentByKey.set(messageKey, {
6030
+ totals,
6031
+ bucketStart,
6032
+ model,
6033
+ projectKey,
6034
+ projectRef,
6035
+ filePath,
6036
+ });
6037
+ }
6038
+ if (cb && (fileIdx % 50 === 0 || fileIdx === files.length - 1)) {
6039
+ cb({
6040
+ index: fileIdx + 1,
6041
+ total: fileCount,
6042
+ messagesProcessed: currentByKey.size,
6043
+ eventsAggregated: 0,
6044
+ bucketsQueued: 0,
6045
+ });
6046
+ }
6047
+ }
6048
+
6049
+ let messagesProcessed = currentByKey.size;
6050
+ let eventsAggregated = 0;
6051
+
6052
+ // Subtract contributions that disappeared or changed
6053
+ for (const [key, prev] of Object.entries(qoderState.messages)) {
6054
+ if (!key.startsWith("jsonl:")) continue;
6055
+ const cur = currentByKey.get(key);
6056
+ const unchanged = cur
6057
+ && totalsKey(prev.totals) === totalsKey(cur.totals)
6058
+ && prev.bucketStart === cur.bucketStart
6059
+ && prev.model === cur.model
6060
+ && (prev.projectKey || null) === (cur.projectKey || null)
6061
+ && (prev.totals?.total_cost_usd || 0) === (cur.totals.total_cost_usd || 0);
6062
+ if (unchanged) continue;
6063
+ if (prev.totals && prev.bucketStart && prev.model) {
6064
+ const oldBucket = getHourlyBucket(hourlyState, sourceKey, prev.model, prev.bucketStart);
6065
+ subtractTotals(oldBucket.totals, prev.totals);
6066
+ touchedBuckets.add(bucketKey(sourceKey, prev.model, prev.bucketStart));
6067
+ if (projectEnabled && prev.projectKey) {
6068
+ const oldProjectBucket = getProjectBucket(projectState, prev.projectKey, sourceKey, prev.bucketStart, prev.projectRef || null);
6069
+ subtractTotals(oldProjectBucket.totals, prev.totals);
6070
+ projectTouchedBuckets.add(projectBucketKey(prev.projectKey, sourceKey, prev.bucketStart));
6071
+ }
6072
+ }
6073
+ if (cur) {
6074
+ const bucket = getHourlyBucket(hourlyState, sourceKey, cur.model, cur.bucketStart);
6075
+ addTotals(bucket.totals, cur.totals);
6076
+ if (cur.totals.usage_precision) bucket.usage_precision = cur.totals.usage_precision;
6077
+ // addTotals handles total_cost_usd via USD_TICKS, but credits cost is small; ensure it accumulates
6078
+ touchedBuckets.add(bucketKey(sourceKey, cur.model, cur.bucketStart));
6079
+ if (projectEnabled && cur.projectKey) {
6080
+ const projectBucket = getProjectBucket(projectState, cur.projectKey, sourceKey, cur.bucketStart, cur.projectRef);
6081
+ addTotals(projectBucket.totals, cur.totals);
6082
+ if (cur.totals.usage_precision) projectBucket.usage_precision = cur.totals.usage_precision;
6083
+ projectTouchedBuckets.add(projectBucketKey(cur.projectKey, sourceKey, cur.bucketStart));
6084
+ }
6085
+ qoderState.messages[key] = {
6086
+ totals: cur.totals,
6087
+ conversationCount: cur.totals.conversation_count,
6088
+ bucketStart: cur.bucketStart,
6089
+ model: cur.model,
6090
+ projectKey: cur.projectKey,
6091
+ projectRef: cur.projectRef,
6092
+ updatedAt: new Date().toISOString(),
6093
+ };
6094
+ eventsAggregated += 1;
6095
+ } else {
6096
+ delete qoderState.messages[key];
6097
+ eventsAggregated += 1;
6098
+ }
6099
+ }
6100
+
6101
+ // Add brand-new keys
6102
+ for (const [key, cur] of currentByKey.entries()) {
6103
+ if (qoderState.messages[key]) continue;
6104
+ const bucket = getHourlyBucket(hourlyState, sourceKey, cur.model, cur.bucketStart);
6105
+ addTotals(bucket.totals, cur.totals);
6106
+ if (cur.totals.usage_precision) bucket.usage_precision = cur.totals.usage_precision;
6107
+ touchedBuckets.add(bucketKey(sourceKey, cur.model, cur.bucketStart));
6108
+ if (projectEnabled && cur.projectKey) {
6109
+ const projectBucket = getProjectBucket(projectState, cur.projectKey, sourceKey, cur.bucketStart, cur.projectRef);
6110
+ addTotals(projectBucket.totals, cur.totals);
6111
+ if (cur.totals.usage_precision) projectBucket.usage_precision = cur.totals.usage_precision;
6112
+ projectTouchedBuckets.add(projectBucketKey(cur.projectKey, sourceKey, cur.bucketStart));
6113
+ }
6114
+ qoderState.messages[key] = {
6115
+ totals: cur.totals,
6116
+ conversationCount: cur.totals.conversation_count,
6117
+ bucketStart: cur.bucketStart,
6118
+ model: cur.model,
6119
+ projectKey: cur.projectKey,
6120
+ projectRef: cur.projectRef,
6121
+ updatedAt: new Date().toISOString(),
6122
+ };
6123
+ eventsAggregated += 1;
6124
+ }
6125
+
6126
+ const bucketsQueued = await enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets });
6127
+ const projectBucketsQueued = projectEnabled
6128
+ ? await enqueueTouchedProjectBuckets({ projectQueuePath, projectState, projectTouchedBuckets })
6129
+ : 0;
6130
+ const updatedAt = new Date().toISOString();
6131
+ hourlyState.updatedAt = updatedAt;
6132
+ qoderState.updatedAt = updatedAt;
6133
+ cursors.hourly = hourlyState;
6134
+ cursors[cursorKey] = qoderState;
6135
+ if (projectState) {
6136
+ projectState.updatedAt = updatedAt;
6137
+ cursors.projectHourly = projectState;
6138
+ }
6139
+ return { messagesProcessed, eventsAggregated, bucketsQueued, projectBucketsQueued };
6140
+ }
6141
+
5764
6142
  // ── Claude Science (Anthropic's local research workbench, issue #246) ──
5765
6143
  //
5766
6144
  // Claude Science keeps all of its state in a SQLite database named
@@ -19864,6 +20242,10 @@ module.exports = {
19864
20242
  resolveQoderDbPaths,
19865
20243
  resolveQoderCnDbPaths,
19866
20244
  readQoderDbMessages,
20245
+ resolveQoderProjectsDir,
20246
+ resolveQoderCnProjectsDir,
20247
+ listQoderNewSessionFiles,
20248
+ parseQoderNewIncremental,
19867
20249
  resolveKiroBasePath,
19868
20250
  resolveKiroDbPath,
19869
20251
  resolveKiroJsonlPath,
@@ -20007,6 +20389,7 @@ module.exports = {
20007
20389
  normalizeGeminiTokens,
20008
20390
  normalizeOpencodeTokens,
20009
20391
  normalizeQoderTokens,
20392
+ normalizeQoderNewTokens,
20010
20393
  sameGeminiTotals,
20011
20394
  diffGeminiTotals,
20012
20395
  // Exposed so the queue-repair migration can mutate cursors state in the
@@ -1 +0,0 @@
1
- import{r as n,z as a}from"./__vite-browser-external-Dnxl50k1.js";import{u as p}from"./DialogTitle-BiRcejlu.js";import{u as c}from"./composite-C79LNbex.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};
@@ -1 +0,0 @@
1
- import{r as o}from"./__vite-browser-external-Dnxl50k1.js";import{h as r,i as t,p as s}from"./react-D2AD6Pk2.js";function i(){!r.current&&t();const[e]=o.useState(s.current);return e}export{i as u};