tokentracker-cli 0.88.7 → 0.88.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.
Files changed (109) hide show
  1. package/README.de.md +4 -4
  2. package/README.ja.md +3 -3
  3. package/README.ko.md +3 -3
  4. package/README.md +7 -5
  5. package/README.zh-CN.md +6 -4
  6. package/dashboard/dist/assets/{AchievementBadge-m7oaBLpH.js → AchievementBadge-htvdO1vU.js} +1 -1
  7. package/dashboard/dist/assets/{AchievementsPage-BHHh4VjM.js → AchievementsPage-DbKcNgQ6.js} +2 -2
  8. package/dashboard/dist/assets/{AchievementsSection-CGLNe_1p.js → AchievementsSection-BrkTpKcW.js} +1 -1
  9. package/dashboard/dist/assets/{BadgeDetailModal-v1DGF6EM.js → BadgeDetailModal-BQkPgd90.js} +1 -1
  10. package/dashboard/dist/assets/{Card-C16SqIR8.js → Card-B5EBKIDM.js} +1 -1
  11. package/dashboard/dist/assets/{ClawdAnimated-Bj3ptLJv.js → ClawdAnimated-DJ0tdNxt.js} +1 -1
  12. package/dashboard/dist/assets/{CommandPalette-BaegDLAM.js → CommandPalette-WaKLjX-2.js} +1 -1
  13. package/dashboard/dist/assets/{CommunityStatsModal-CThxVFG0.js → CommunityStatsModal-4hdMbaWz.js} +1 -1
  14. package/dashboard/dist/assets/{ConfirmModal-C0sP3Ktk.js → ConfirmModal-A3mMk8B_.js} +1 -1
  15. package/dashboard/dist/assets/{Controls-C8usK6RW.js → Controls-CwxIB8nh.js} +1 -1
  16. package/dashboard/dist/assets/DashboardPage-BRRSD7kr.js +1 -0
  17. package/dashboard/dist/assets/{DevicePage-Cu4qqRcB.js → DevicePage-Dbx_L74N.js} +1 -1
  18. package/dashboard/dist/assets/{DialogClose-DVo3jJb6.js → DialogClose-D-zGwDEb.js} +1 -1
  19. package/dashboard/dist/assets/DialogDescription-CoaUGezu.js +1 -0
  20. package/dashboard/dist/assets/{DialogTitle-C1UOMgTZ.js → DialogTitle-BeYcqlZE.js} +1 -1
  21. package/dashboard/dist/assets/{FadeIn-CuQlQrvO.js → FadeIn-C8ywt-6i.js} +1 -1
  22. package/dashboard/dist/assets/{HeaderGithubStar-BTt2612H.js → HeaderGithubStar-BqEMlBbu.js} +1 -1
  23. package/dashboard/dist/assets/{HoverTooltip-CHdLolcf.js → HoverTooltip-D19dSRQZ.js} +1 -1
  24. package/dashboard/dist/assets/{IpCheckPage-BFz8hr_3.js → IpCheckPage-rh4LK9G1.js} +1 -1
  25. package/dashboard/dist/assets/{LandingPage-CTyNSAYl.js → LandingPage-DNiX6jyV.js} +2 -2
  26. package/dashboard/dist/assets/{LeaderboardAvatar-CBdOXZWk.js → LeaderboardAvatar-BJ1LIuDg.js} +1 -1
  27. package/dashboard/dist/assets/{LeaderboardPage-DStx51Bj.js → LeaderboardPage-DgNREoia.js} +2 -2
  28. package/dashboard/dist/assets/{LeaderboardProfileModal-CtxgJR07.js → LeaderboardProfileModal-CvSxMoV9.js} +3 -3
  29. package/dashboard/dist/assets/{LeaderboardProfilePage-DrpsHTdZ.js → LeaderboardProfilePage-DhvNnM97.js} +1 -1
  30. package/dashboard/dist/assets/{LimitsPage-m_itA-7d.js → LimitsPage-B8rUA85S.js} +1 -1
  31. package/dashboard/dist/assets/{LocalOnlyNotice-A62wFwYT.js → LocalOnlyNotice-DSnu1sDP.js} +1 -1
  32. package/dashboard/dist/assets/{LoginCard-DFJugNa-.js → LoginCard-1hs5q14W.js} +1 -1
  33. package/dashboard/dist/assets/{LoginModal-DF4BOwS6.js → LoginModal-CjSQQVyp.js} +1 -1
  34. package/dashboard/dist/assets/{LoginPage-BxFhfitr.js → LoginPage-DErmgGEN.js} +1 -1
  35. package/dashboard/dist/assets/{PetPage-l4fkRsf1.js → PetPage-CKqbIP9y.js} +1 -1
  36. package/dashboard/dist/assets/{PopoverPopup-y-OAHy4v.js → PopoverPopup-Ca-Jal24.js} +1 -1
  37. package/dashboard/dist/assets/{ProviderIcon-DwoPHz27.js → ProviderIcon-CbU5pvlK.js} +1 -1
  38. package/dashboard/dist/assets/{ResetPasswordPage-CCDj94rl.js → ResetPasswordPage-CzoDR8nx.js} +1 -1
  39. package/dashboard/dist/assets/{SegmentedControl-lEKPzZlV.js → SegmentedControl-BaLNIY76.js} +1 -1
  40. package/dashboard/dist/assets/{Select-DoyUBoxn.js → Select-Cf5PZCcx.js} +1 -1
  41. package/dashboard/dist/assets/{SelectItemText-K8Vzn1Dd.js → SelectItemText-xBMGaj65.js} +1 -1
  42. package/dashboard/dist/assets/{ServiceStatusPage-Cqyo7OtW.js → ServiceStatusPage-B6EHXq3M.js} +1 -1
  43. package/dashboard/dist/assets/{SessionsPage-BELC-8aJ.js → SessionsPage-C56K0tfO.js} +1 -1
  44. package/dashboard/dist/assets/{SettingsPage-C6Og4tMS.js → SettingsPage-BjOWdr1m.js} +1 -1
  45. package/dashboard/dist/assets/{SkillsPage-BKKgpXc_.js → SkillsPage-CwW4jbCo.js} +1 -1
  46. package/dashboard/dist/assets/{TokenGalaxy-DoSCQSpY.js → TokenGalaxy-jMGva06s.js} +1 -1
  47. package/dashboard/dist/assets/{ToolbarRootContext-GoQP9LXi.js → ToolbarRootContext-CzVz_WzP.js} +1 -1
  48. package/dashboard/dist/assets/{TrendMonitor-BHE1KkJ7.js → TrendMonitor-Dv7x1M8j.js} +1 -1
  49. package/dashboard/dist/assets/{WidgetsPage-CHBzNb19.js → WidgetsPage-28tdIrtI.js} +1 -1
  50. package/dashboard/dist/assets/{WrappedPage-CW_kqVoY.js → WrappedPage-C4-yHi1A.js} +1 -1
  51. package/dashboard/dist/assets/{__vite-browser-external-BgeLzJ0q.js → __vite-browser-external-TfJNQh7R.js} +6 -3
  52. package/dashboard/dist/assets/{agent-logos-BWeWRlms.js → agent-logos-opc9TJxO.js} +1 -1
  53. package/dashboard/dist/assets/{arrow-up-right-5GGSSEKW.js → arrow-up-right-C5182MzM.js} +1 -1
  54. package/dashboard/dist/assets/{check-g6Fq4TSB.js → check-DQD3S-RC.js} +1 -1
  55. package/dashboard/dist/assets/{chevron-right-DjPuavEP.js → chevron-right-CbqyxgAL.js} +1 -1
  56. package/dashboard/dist/assets/{copy-BAMl60zT.js → copy-C8s781jQ.js} +1 -1
  57. package/dashboard/dist/assets/{download-BQPhL4h5.js → download-DeuZwnjh.js} +1 -1
  58. package/dashboard/dist/assets/{external-link-CU3XKMbZ.js → external-link-26DY1LXg.js} +1 -1
  59. package/dashboard/dist/assets/{flame-B85YY_rB.js → flame-2tDcDlsl.js} +1 -1
  60. package/dashboard/dist/assets/{format-tokens-DDxlnhNV.js → format-tokens-CKwrPboc.js} +1 -1
  61. package/dashboard/dist/assets/{icons-D1e1TWTe.js → icons-OywRGkTO.js} +1 -1
  62. package/dashboard/dist/assets/{index-BZ2hkHKk.js → index-BlkMk8QH.js} +1 -1
  63. package/dashboard/dist/assets/{index-CWMkVmon.js → index-CQfZrqyE.js} +1 -1
  64. package/dashboard/dist/assets/{index-sQl-P7u5.js → index-DDb7tTK9.js} +1 -1
  65. package/dashboard/dist/assets/{info-DlSYpCFm.js → info-BxOjubUJ.js} +1 -1
  66. package/dashboard/dist/assets/{limits-providers-DS5EIbJK.js → limits-providers-BJ5Lyy5b.js} +1 -1
  67. package/dashboard/dist/assets/{link-2-DbEVoNk-.js → link-2-DyNL4Yp2.js} +1 -1
  68. package/dashboard/dist/assets/{loader-circle-CqmzGIX-.js → loader-circle-C9htg-60.js} +1 -1
  69. package/dashboard/dist/assets/{maximize-2-Ce8X8kHQ.js → maximize-2-BhYLZI1z.js} +1 -1
  70. package/dashboard/dist/assets/{modifiers.esm-BQNRpVcA.js → modifiers.esm-LB3iCgCR.js} +1 -1
  71. package/dashboard/dist/assets/{monitor-smartphone-8drugZmh.js → monitor-smartphone-ByTcTeaS.js} +1 -1
  72. package/dashboard/dist/assets/{provider-display-BayTojFu.js → provider-display-Ct_W_QPI.js} +2 -2
  73. package/dashboard/dist/assets/{react-BfgySeLu.js → react-B9PoHC4J.js} +1 -1
  74. package/dashboard/dist/assets/{search-BkGUxlXI.js → search-BHkYCtEB.js} +1 -1
  75. package/dashboard/dist/assets/{skills-api-D80JxFc1.js → skills-api-vyU78tZp.js} +1 -1
  76. package/dashboard/dist/assets/{sortable.esm-SjRQ4Ehw.js → sortable.esm-CP26VeUr.js} +1 -1
  77. package/dashboard/dist/assets/{store-B3WkWBgn.js → store-DapNnhgd.js} +1 -1
  78. package/dashboard/dist/assets/{terminal-CIvEvMau.js → terminal-BSywtUbT.js} +1 -1
  79. package/dashboard/dist/assets/{trash-2-BZclE0mU.js → trash-2-CSZa8zd_.js} +1 -1
  80. package/dashboard/dist/assets/{use-community-stats-ChaFzJoW.js → use-community-stats-iYFKWvre.js} +1 -1
  81. package/dashboard/dist/assets/{use-limits-display-prefs-2LALdqvT.js → use-limits-display-prefs-DMgg1XJk.js} +1 -1
  82. package/dashboard/dist/assets/{use-ordered-list-10Nmxw1I.js → use-ordered-list-BEbD2eQK.js} +1 -1
  83. package/dashboard/dist/assets/use-reduced-motion-LjbTKoie.js +1 -0
  84. package/dashboard/dist/assets/{use-session-efficiency-pref-CqPK4jX1.js → use-session-efficiency-pref-6BUBtxow.js} +1 -1
  85. package/dashboard/dist/assets/{use-transform-s6og1tg9.js → use-transform-B8GljRD3.js} +1 -1
  86. package/dashboard/dist/assets/{use-usage-limits-jyRJ_kl9.js → use-usage-limits-CJEE9QI7.js} +1 -1
  87. package/dashboard/dist/assets/{useCurrency-hZfdhfqK.js → useCurrency-BmjmX56B.js} +1 -1
  88. package/dashboard/dist/assets/{useScrollLock-DFF4vpw2.js → useScrollLock-LyMOuZKa.js} +1 -1
  89. package/dashboard/dist/assets/{useTokenFormat-DaVdoRvh.js → useTokenFormat-BUl7vg2K.js} +1 -1
  90. package/dashboard/dist/assets/{zap-B1XMdyCV.js → zap-ChXwtsxo.js} +1 -1
  91. package/dashboard/dist/brand-logos/reasonix.png +0 -0
  92. package/dashboard/dist/index.html +28 -25
  93. package/dashboard/dist/ip-check.html +1 -1
  94. package/dashboard/dist/leaderboard.html +1 -1
  95. package/dashboard/dist/llms.txt +4 -2
  96. package/dashboard/dist/share.html +1 -1
  97. package/package.json +2 -2
  98. package/src/commands/init.js +11 -0
  99. package/src/commands/status.js +27 -5
  100. package/src/commands/sync.js +180 -6
  101. package/src/lib/install-resolver.js +35 -0
  102. package/src/lib/local-api.js +3 -0
  103. package/src/lib/pricing/seed-snapshot.json +1 -1
  104. package/src/lib/rollout.js +781 -0
  105. package/src/lib/subscriptions.js +1 -0
  106. package/src/lib/usage-limits.js +17 -2
  107. package/dashboard/dist/assets/DashboardPage-DbzbT_cY.js +0 -1
  108. package/dashboard/dist/assets/DialogDescription-zf3KGPP3.js +0 -1
  109. package/dashboard/dist/assets/use-reduced-motion-J12KnTgP.js +0 -1
@@ -1,7 +1,7 @@
1
1
  # Token Tracker LLMs Index
2
2
 
3
3
  Token Tracker is a free, open-source, local-first AI coding usage and cost
4
- tracker for 29 tools. Its distinctive desktop experience includes a pixel pet
4
+ tracker for 31 tools. Its distinctive desktop experience includes a pixel pet
5
5
  powered by real token activity, four desktop widgets, and achievements. The
6
6
  same data powers a cross-platform CLI, a native macOS menu bar app, a native
7
7
  Windows system tray app, and a dashboard. It records token counts and
@@ -23,7 +23,7 @@ timestamps, never prompts, responses, or source code.
23
23
  - [npm package](https://www.npmjs.com/package/tokentracker-cli)
24
24
  - [Latest release](https://github.com/xiufengsun/TokenTracker/releases/latest)
25
25
 
26
- ## Supported AI coding tools (29)
26
+ ## Supported AI coding tools (31)
27
27
  - Claude Code
28
28
  - Codex CLI
29
29
  - Cursor
@@ -42,6 +42,7 @@ timestamps, never prompts, responses, or source code.
42
42
  - oh-my-pi
43
43
  - pi
44
44
  - Craft Agents
45
+ - Reasonix
45
46
  - Kilo CLI
46
47
  - Kilo Code
47
48
  - Roo Code
@@ -53,6 +54,7 @@ timestamps, never prompts, responses, or source code.
53
54
  - Qoder
54
55
  - AnythingLLM Desktop
55
56
  - Claude Science
57
+ - DeepSeek Harness
56
58
 
57
59
  ## Capabilities
58
60
  - Desktop pet that reacts to real token burn, streaks, and rest
@@ -75,7 +75,7 @@
75
75
  "description": "Shareable Token Tracker dashboard snapshot."
76
76
  }
77
77
  </script>
78
- <script type="module" crossorigin src="/assets/__vite-browser-external-BgeLzJ0q.js"></script>
78
+ <script type="module" crossorigin src="/assets/__vite-browser-external-TfJNQh7R.js"></script>
79
79
  <link rel="stylesheet" crossorigin href="/assets/__vite-browser-external-CRYifSM4.css">
80
80
  </head>
81
81
  <body>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tokentracker-cli",
3
- "version": "0.88.7",
4
- "description": "Local-first AI coding token usage and cost tracker for 29 tools, with a dashboard, desktop pet, widgets, achievements, and native macOS/Windows apps.",
3
+ "version": "0.88.9",
4
+ "description": "Local-first AI coding token usage and cost tracker for 31 tools, with a dashboard, desktop pet, widgets, achievements, and native macOS/Windows apps.",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
7
7
  "tokentracker-cli": "bin/tracker.js",
@@ -66,6 +66,7 @@ const {
66
66
  resolvePiAgentDir,
67
67
  piAgentDirCollidesWithOmp,
68
68
  resolveAnythingllmDbPath,
69
+ resolveReasonixHome,
69
70
  resolveTraeStoragePath,
70
71
  } = require("../lib/rollout");
71
72
  const { resolveRuntimeConfig, DEFAULT_BASE_URL } = require("../lib/runtime-config");
@@ -121,6 +122,7 @@ const SUPPORTED_PROVIDERS = [
121
122
  "oh-my-pi",
122
123
  "pi",
123
124
  "Craft Agents",
125
+ "Reasonix",
124
126
  "Kilo CLI",
125
127
  "Kilo Code",
126
128
  "Roo Code",
@@ -132,6 +134,7 @@ const SUPPORTED_PROVIDERS = [
132
134
  "Qoder",
133
135
  "AnythingLLM Desktop",
134
136
  "Claude Science",
137
+ "DeepSeek Harness",
135
138
  ];
136
139
 
137
140
  async function cmdInit(argv) {
@@ -717,6 +720,14 @@ async function applyIntegrationSetup({
717
720
  }
718
721
  }
719
722
 
723
+ // Reasonix: passive reader of content-free *.jsonl.telemetry.json sidecars.
724
+ {
725
+ const reasonixHome = resolveReasonixHome(process.env);
726
+ if (fssync.existsSync(reasonixHome)) {
727
+ summary.push({ label: "Reasonix", status: "detected", detail: "Passive telemetry reader (no hook needed)" });
728
+ }
729
+ }
730
+
720
731
  // Trae SOLO (ByteDance AI IDE): plan snapshot only. Trae keeps its session
721
732
  // transcripts SQLCipher-encrypted and its plaintext summaries hold no token
722
733
  // counts, so there is no usage to read — the detail line must not promise
@@ -62,6 +62,8 @@ const {
62
62
  piAgentDirCollidesWithOmp,
63
63
  resolveCraftSessionFiles,
64
64
  resolveCraftConfigDir,
65
+ resolveReasonixHome,
66
+ resolveReasonixTelemetryFiles,
65
67
  resolveKilocodeTaskFiles,
66
68
  resolveRoocodeTaskFiles,
67
69
  resolveZedDbPath,
@@ -72,6 +74,8 @@ const {
72
74
  resolveGooseDbPath,
73
75
  listDroidSettingsFiles,
74
76
  resolveDroidSessionsDir,
77
+ resolveDshHome,
78
+ resolveDshSessionFiles,
75
79
  resolveTraeStoragePath,
76
80
  readTraeEntitlementFromStorage,
77
81
  resolveGrokBuildSessions,
@@ -85,7 +89,7 @@ const {
85
89
  } = require("../lib/rollout");
86
90
  const wsl = require("../lib/wsl-probe");
87
91
  const { getWslMode, isInvalidWslMode, shouldProbeWsl, discoverWslHome } = wsl;
88
- const { resolveInstallPaths } = require("../lib/install-resolver");
92
+ const { resolveInstallPaths, resolveZcodeNativeDbPath } = require("../lib/install-resolver");
89
93
  const { probeGrokHookState, resolveGrokHome } = require("../lib/grok-hook");
90
94
  const { probeOmpHookState } = require("../lib/omp-hook");
91
95
 
@@ -428,6 +432,11 @@ async function cmdStatus(argv = []) {
428
432
  const craftInstalled = Boolean(craftConfigDir && fssync.existsSync(craftConfigDir));
429
433
  const craftFiles = craftInstalled ? resolveCraftSessionFiles(process.env) : [];
430
434
 
435
+ // Reasonix — passive scan of content-free cumulative telemetry sidecars.
436
+ const reasonixHome = resolveReasonixHome(process.env);
437
+ const reasonixInstalled = Boolean(reasonixHome && fssync.existsSync(reasonixHome));
438
+ const reasonixFiles = reasonixInstalled ? resolveReasonixTelemetryFiles(process.env) : [];
439
+
431
440
  // Kilo CLI (kilo.ai @kilocode/plugin) — passive scan of kilo.db.
432
441
  const xdgDataHome = process.env.XDG_DATA_HOME || path.join(home, ".local", "share");
433
442
  const kiloHome = process.env.KILO_HOME || path.join(xdgDataHome, "kilo");
@@ -456,10 +465,7 @@ async function cmdStatus(argv = []) {
456
465
  const mimoDbPath = mimoActive.join(" | ");
457
466
 
458
467
  // ZCode (Z.ai's coding agent — OpenCode-fork SQLite) — passive scan of db.sqlite.
459
- const zcodeHome = process.env.ZCODE_HOME || path.join(home, ".zcode");
460
- const zcodeNativeValue = process.platform === "win32" && typeof process.env.APPDATA === "string"
461
- ? path.join(process.env.APPDATA.trim(), ".zcode", "cli", "db", "db.sqlite")
462
- : path.join(zcodeHome, "cli", "db", "db.sqlite");
468
+ const zcodeNativeValue = resolveZcodeNativeDbPath({ home });
463
469
  const wslZcodeDir = process.platform === "win32" && wsl.shouldProbeWsl(process.env)
464
470
  ? wsl.discoverWslHome(".zcode")
465
471
  : null;
@@ -613,6 +619,10 @@ async function cmdStatus(argv = []) {
613
619
  const droidSessionsDir = resolveDroidSessionsDir(process.env);
614
620
  const droidSettingsFiles = listDroidSettingsFiles(process.env);
615
621
  const droidInstalled = droidSettingsFiles.length > 0;
622
+ const dshHome = resolveDshHome(process.env);
623
+ const dshSessionsDir = path.join(dshHome, "sessions");
624
+ const dshSessionFiles = await resolveDshSessionFiles(process.env);
625
+ const dshInstalled = dshSessionFiles.length > 0;
616
626
 
617
627
  // Trae SOLO (ByteDance AI IDE) — passive entitlement snapshot reader.
618
628
  const traeStoragePath = resolveTraeStoragePath(process.env);
@@ -866,6 +876,9 @@ async function cmdStatus(argv = []) {
866
876
  craft: craftInstalled
867
877
  ? { installed: true, files: craftFiles.length }
868
878
  : { installed: false },
879
+ reasonix: reasonixInstalled
880
+ ? { installed: true, files: reasonixFiles.length }
881
+ : { installed: false },
869
882
  anythingllm: anythingllmInstalled
870
883
  ? { installed: true, detail: anythingllmDbPath }
871
884
  : { installed: false },
@@ -900,6 +913,9 @@ async function cmdStatus(argv = []) {
900
913
  droid: droidInstalled
901
914
  ? { installed: true, files: droidSettingsFiles.length, detail: droidSessionsDir }
902
915
  : { installed: false },
916
+ dsh: dshInstalled
917
+ ? { installed: true, files: dshSessionFiles.length, detail: dshSessionsDir }
918
+ : { installed: false },
903
919
  trae: traeInstalled
904
920
  ? {
905
921
  installed: true,
@@ -1001,6 +1017,9 @@ async function cmdStatus(argv = []) {
1001
1017
  craftInstalled
1002
1018
  ? `- Craft Agents: passive reader (${craftFiles.length} session jsonl file${craftFiles.length !== 1 ? "s" : ""} found)`
1003
1019
  : null,
1020
+ reasonixInstalled
1021
+ ? `- Reasonix: passive reader (${reasonixFiles.length} telemetry file${reasonixFiles.length !== 1 ? "s" : ""} found)`
1022
+ : null,
1004
1023
  anythingllmInstalled
1005
1024
  ? `- AnythingLLM Desktop: passive reader (${anythingllmDbPath})`
1006
1025
  : null,
@@ -1059,6 +1078,9 @@ async function cmdStatus(argv = []) {
1059
1078
  droidInstalled
1060
1079
  ? `- Droid (Factory): passive reader (${droidSettingsFiles.length} session${droidSettingsFiles.length !== 1 ? "s" : ""} in ${droidSessionsDir}, cumulative-delta)`
1061
1080
  : null,
1081
+ dshInstalled
1082
+ ? `- DeepSeek Harness: passive reader (${dshSessionFiles.length} session${dshSessionFiles.length !== 1 ? "s" : ""} in ${dshSessionsDir})`
1083
+ : null,
1062
1084
  traeInstalled
1063
1085
  // Deliberately NOT "passive reader": every other line with that wording
1064
1086
  // means tokens are being counted. Trae encrypts its session transcripts
@@ -5,7 +5,7 @@ const fssync = require("node:fs");
5
5
  const cp = require("node:child_process");
6
6
  const readline = require("node:readline");
7
7
 
8
- const { resolveInstallPaths, ensureFlatCursor } = require("../lib/install-resolver");
8
+ const { resolveInstallPaths, resolveZcodeNativeDbPath, ensureFlatCursor } = require("../lib/install-resolver");
9
9
  const { multiInstallParse, mergeBothFileSources } = require("../lib/multi-install-parser");
10
10
  const wsl = require("../lib/wsl-probe");
11
11
  const {
@@ -80,6 +80,8 @@ const {
80
80
  piAgentDirCollidesWithOmp,
81
81
  resolveCraftSessionFiles,
82
82
  parseCraftIncremental,
83
+ resolveReasonixTelemetryFiles,
84
+ parseReasonixIncremental,
83
85
  resolveGrokBuildSessions,
84
86
  parseGrokBuildIncremental,
85
87
  listAntigravityTranscripts,
@@ -106,6 +108,8 @@ const {
106
108
  parseDroidIncremental,
107
109
  droidSessionIdFromPath,
108
110
  resolveDroidModel,
111
+ resolveDshSessionFiles,
112
+ parseDshIncremental,
109
113
  bucketKey,
110
114
  toUtcHalfHourStart,
111
115
  totalsKey,
@@ -244,8 +248,10 @@ const CODEX_COLD_SCAN_AUDIT_MAX_SYNCS = 288;
244
248
  // one-time repair purges all source=mimo data from the local queues + cursor
245
249
  // state so the next sync (providerID-filtered reader) rebuilds it correctly.
246
250
  const MIMO_PROVIDER_REPAIR_KEY = "mimoClaudeMislabelRepair_2026_06";
251
+ const DSH_LEGACY_SOURCE_MIGRATION_KEY = "deepseekHarnessSourceMigration_2026_08";
247
252
  const AUTO_SYNC_SOURCE_ALIASES = new Map([
248
253
  ["code", "every-code"],
254
+ ["deepseek", "dsh"],
249
255
  ["everycode", "every-code"],
250
256
  ["kilo", "kilo-cli"],
251
257
  ["kilo-code", "kilocode"],
@@ -263,6 +269,7 @@ const AUTO_SYNC_SOURCES = new Set([
263
269
  "craft",
264
270
  "cursor",
265
271
  "droid",
272
+ "dsh",
266
273
  "every-code",
267
274
  "gemini",
268
275
  "goose",
@@ -280,6 +287,7 @@ const AUTO_SYNC_SOURCES = new Set([
280
287
  "pi",
281
288
  "qoder",
282
289
  "qoder-cn",
290
+ "reasonix",
283
291
  "roocode",
284
292
  "workbuddy",
285
293
  "zcode",
@@ -289,6 +297,7 @@ const BACKGROUND_AUTO_SYNC_SOURCES = new Set([
289
297
  // Keep unscoped native 5-minute syncs bounded to dated local session trees.
290
298
  "codex",
291
299
  "every-code",
300
+ "reasonix",
292
301
  ]);
293
302
 
294
303
  function warnProviderParseFailure(label, err, opts) {
@@ -354,7 +363,7 @@ async function acquireSyncLock(
354
363
  priorityPollMs = PRIORITY_LOCK_POLL_MS,
355
364
  } = {},
356
365
  ) {
357
- const waitsForPriority = Boolean(opts.drain || opts.publishAccount);
366
+ const waitsForPriority = Boolean(opts.waitForLock || opts.drain || opts.publishAccount);
358
367
  let lock = await openLock(lockPath, {
359
368
  quietIfLocked: opts.auto || waitsForPriority,
360
369
  });
@@ -576,7 +585,6 @@ async function cmdSync(argv, context = {}) {
576
585
  const xdgDataHome = process.env.XDG_DATA_HOME || path.join(home, ".local", "share");
577
586
  const kiloHome = process.env.KILO_HOME || path.join(xdgDataHome, "kilo");
578
587
  const mimoHome = process.env.MIMO_HOME || path.join(xdgDataHome, "mimocode");
579
- const zcodeHome = process.env.ZCODE_HOME || path.join(home, ".zcode");
580
588
 
581
589
  // OpenClaw session plugin integration: lifecycle hooks request an
582
590
  // OpenClaw-only auto sync so unrelated providers do not get walked.
@@ -1369,9 +1377,7 @@ async function cmdSync(argv, context = {}) {
1369
1377
  // ── ZCode (Z.ai's coding agent — OpenCode-fork SQLite) ──
1370
1378
  let zcodeResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
1371
1379
  if (sourceAllowed("zcode")) {
1372
- const zcodeNativeValue = process.platform === "win32" && typeof process.env.APPDATA === "string"
1373
- ? path.join(process.env.APPDATA.trim(), ".zcode", "cli", "db", "db.sqlite")
1374
- : path.join(zcodeHome, "cli", "db", "db.sqlite");
1380
+ const zcodeNativeValue = resolveZcodeNativeDbPath({ home });
1375
1381
  const wslZcodeDir = process.platform === "win32" && wsl.shouldProbeWsl(process.env)
1376
1382
  ? wsl.discoverWslHome(".zcode")
1377
1383
  : null;
@@ -1398,6 +1404,32 @@ async function cmdSync(argv, context = {}) {
1398
1404
  };
1399
1405
  }
1400
1406
 
1407
+ // ── DeepSeek Harness — passive read of ~/.dsh/sessions session logs ──
1408
+ let dshResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
1409
+ if (sourceAllowed("dsh")) {
1410
+ await migrateLegacyDeepseekHarnessSource({ cursors, queuePath, queueStatePath });
1411
+ const dshSessionFiles = await resolveDshSessionFiles(process.env);
1412
+ if (dshSessionFiles.length > 0) {
1413
+ if (progress?.enabled) {
1414
+ progress.start(
1415
+ `Parsing DeepSeek Harness ${renderBar(0)} 0/${formatNumber(
1416
+ dshSessionFiles.length,
1417
+ )} sessions | buckets 0`,
1418
+ );
1419
+ }
1420
+ try {
1421
+ dshResult = await parseDshIncremental({
1422
+ sessionFiles: dshSessionFiles,
1423
+ cursors,
1424
+ queuePath,
1425
+ onProgress: makeProviderProgress("DeepSeek Harness"),
1426
+ });
1427
+ } catch (err) {
1428
+ warnProviderParseFailure("DeepSeek Harness", err, opts);
1429
+ }
1430
+ }
1431
+ }
1432
+
1401
1433
  // ── AnythingLLM Desktop (workspace_chats.response.metrics) ──
1402
1434
  let anythingllmResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
1403
1435
  if (sourceAllowed("anythingllm")) {
@@ -2075,6 +2107,25 @@ async function cmdSync(argv, context = {}) {
2075
2107
  }
2076
2108
  }
2077
2109
 
2110
+ // Reasonix — content-free cumulative telemetry sidecars only.
2111
+ let reasonixResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
2112
+ const reasonixFiles = sourceAllowed("reasonix")
2113
+ ? mergeBothFileSources({ resolveFiles: resolveReasonixTelemetryFiles, env: process.env })
2114
+ : [];
2115
+ if (reasonixFiles.length > 0) {
2116
+ try {
2117
+ reasonixResult = await parseReasonixIncremental({
2118
+ telemetryFiles: reasonixFiles,
2119
+ cursors,
2120
+ queuePath,
2121
+ env: process.env,
2122
+ onProgress: makeProviderProgress("Reasonix"),
2123
+ });
2124
+ } catch (err) {
2125
+ warnProviderParseFailure("Reasonix", err, opts);
2126
+ }
2127
+ }
2128
+
2078
2129
  // ── Grok Build (xAI) ──
2079
2130
  let grokResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
2080
2131
  // Full passive scan of all Grok sessions (historical + any not covered by hook)
@@ -2513,6 +2564,7 @@ async function cmdSync(argv, context = {}) {
2513
2564
  ompResult.recordsProcessed +
2514
2565
  piResult.recordsProcessed +
2515
2566
  craftResult.recordsProcessed +
2567
+ reasonixResult.recordsProcessed +
2516
2568
  grokResult.recordsProcessed +
2517
2569
  copilotResult.recordsProcessed +
2518
2570
  anythingllmResult.recordsProcessed +
@@ -2523,6 +2575,7 @@ async function cmdSync(argv, context = {}) {
2523
2575
  roocodeResult.recordsProcessed +
2524
2576
  zedResult.recordsProcessed +
2525
2577
  gooseResult.recordsProcessed +
2578
+ dshResult.recordsProcessed +
2526
2579
  droidResult.recordsProcessed;
2527
2580
  const totalBuckets =
2528
2581
  parseResult.bucketsQueued +
@@ -2545,6 +2598,7 @@ async function cmdSync(argv, context = {}) {
2545
2598
  ompResult.bucketsQueued +
2546
2599
  piResult.bucketsQueued +
2547
2600
  craftResult.bucketsQueued +
2601
+ reasonixResult.bucketsQueued +
2548
2602
  grokResult.bucketsQueued +
2549
2603
  copilotResult.bucketsQueued +
2550
2604
  anythingllmResult.bucketsQueued +
@@ -2555,6 +2609,7 @@ async function cmdSync(argv, context = {}) {
2555
2609
  roocodeResult.bucketsQueued +
2556
2610
  zedResult.bucketsQueued +
2557
2611
  gooseResult.bucketsQueued +
2612
+ dshResult.bucketsQueued +
2558
2613
  droidResult.bucketsQueued;
2559
2614
  const skipNoOpCursorCommit =
2560
2615
  opts.auto &&
@@ -2780,6 +2835,7 @@ function parseArgs(argv) {
2780
2835
  fromOpenclaw: false,
2781
2836
  source: null,
2782
2837
  drain: false,
2838
+ waitForLock: false,
2783
2839
  background: false,
2784
2840
  publishAccount: false,
2785
2841
  allLocalSources: false,
@@ -2797,6 +2853,7 @@ function parseArgs(argv) {
2797
2853
  }
2798
2854
  else if (a.startsWith("--source=")) out.source = normalizeSyncSource(a.slice("--source=".length));
2799
2855
  else if (a === "--drain") out.drain = true;
2856
+ else if (a === "--wait-for-lock") out.waitForLock = true;
2800
2857
  else if (a === "--background" || a === "--lightweight") out.background = true;
2801
2858
  else if (a === "--publish-account") out.publishAccount = true;
2802
2859
  else if (a === "--all-local-sources") out.allLocalSources = true;
@@ -2888,6 +2945,8 @@ module.exports = {
2888
2945
  repairCodebuddyLogJsonlOverlap,
2889
2946
  repairWorkbuddyContextUsage,
2890
2947
  WORKBUDDY_CONTEXT_USAGE_REPAIR_KEY,
2948
+ migrateLegacyDeepseekHarnessSource,
2949
+ DSH_LEGACY_SOURCE_MIGRATION_KEY,
2891
2950
  repairCodexRescanInflation,
2892
2951
  repairCodexForkReplayInflation,
2893
2952
  repairCodexInterleavedUsageInflation,
@@ -2912,6 +2971,121 @@ module.exports = {
2912
2971
  CLOUD_CONVERSATIONS_BACKFILL_KEY,
2913
2972
  };
2914
2973
 
2974
+ // The pre-merge Harness prototype briefly emitted source="deepseek" before
2975
+ // settling on the official CLI id, source="dsh". Relabel the local history,
2976
+ // append explicit zero rows for every old cloud key, and replay from offset 0.
2977
+ // That preserves the latest canonical totals while making remote whole-row
2978
+ // upserts remove the stale alias instead of displaying both sources or summing
2979
+ // the same session twice.
2980
+ async function migrateLegacyDeepseekHarnessSource({ cursors, queuePath, queueStatePath } = {}) {
2981
+ if (!cursors || typeof cursors !== "object") return false;
2982
+ const migrations = (cursors.migrations ||= {});
2983
+ if (migrations[DSH_LEGACY_SOURCE_MIGRATION_KEY]) return false;
2984
+
2985
+ let raw = "";
2986
+ try {
2987
+ raw = await fs.readFile(queuePath, "utf8");
2988
+ } catch (error) {
2989
+ if (error?.code !== "ENOENT") throw error;
2990
+ }
2991
+
2992
+ const output = [];
2993
+ const legacyKeys = new Map();
2994
+ const latestMigratedRows = new Map();
2995
+ const latestExplicitCanonicalRows = new Map();
2996
+ for (const line of raw.split("\n")) {
2997
+ if (!line.trim()) continue;
2998
+ let row;
2999
+ try {
3000
+ row = JSON.parse(line);
3001
+ } catch (_error) {
3002
+ output.push(line);
3003
+ continue;
3004
+ }
3005
+
3006
+ const isLegacy =
3007
+ row?.source === "deepseek" &&
3008
+ typeof row.model === "string" &&
3009
+ typeof row.hour_start === "string";
3010
+ if (isLegacy) {
3011
+ const key = `${row.model}|${row.hour_start}`;
3012
+ legacyKeys.set(key, { model: row.model, hour_start: row.hour_start });
3013
+ row = {
3014
+ ...row,
3015
+ source: "dsh",
3016
+ billable_total_tokens: row.billable_total_tokens ?? row.total_tokens,
3017
+ };
3018
+ }
3019
+ const serialized = JSON.stringify(row);
3020
+ output.push(serialized);
3021
+ if (row?.source === "dsh" && typeof row.model === "string" && typeof row.hour_start === "string") {
3022
+ const key = `${row.model}|${row.hour_start}`;
3023
+ if (isLegacy) latestMigratedRows.set(key, serialized);
3024
+ else latestExplicitCanonicalRows.set(key, serialized);
3025
+ }
3026
+ }
3027
+
3028
+ if (legacyKeys.size === 0) {
3029
+ migrations[DSH_LEGACY_SOURCE_MIGRATION_KEY] = {
3030
+ status: "noop",
3031
+ appliedAt: new Date().toISOString(),
3032
+ reason: "no-legacy-deepseek-rows",
3033
+ };
3034
+ return false;
3035
+ }
3036
+
3037
+ // Re-append the last canonical row for each affected key so local readers'
3038
+ // last-row-wins contract remains correct even when an old alias line was
3039
+ // physically later than an already-emitted dsh row.
3040
+ for (const key of legacyKeys.keys()) {
3041
+ const canonical = latestExplicitCanonicalRows.get(key) || latestMigratedRows.get(key);
3042
+ if (canonical) output.push(canonical);
3043
+ }
3044
+ for (const { model, hour_start } of legacyKeys.values()) {
3045
+ output.push(JSON.stringify({
3046
+ source: "deepseek",
3047
+ model,
3048
+ hour_start,
3049
+ input_tokens: 0,
3050
+ cached_input_tokens: 0,
3051
+ cache_creation_input_tokens: 0,
3052
+ output_tokens: 0,
3053
+ reasoning_output_tokens: 0,
3054
+ total_tokens: 0,
3055
+ billable_total_tokens: 0,
3056
+ conversation_count: 0,
3057
+ }));
3058
+ }
3059
+
3060
+ await ensureDir(path.dirname(queuePath));
3061
+ const tmpPath = `${queuePath}.tmp.${process.pid}.${Date.now()}`;
3062
+ await fs.writeFile(tmpPath, `${output.join("\n")}\n`, "utf8");
3063
+ await fs.rename(tmpPath, queuePath);
3064
+
3065
+ if (typeof queueStatePath === "string" && queueStatePath) {
3066
+ let state = {};
3067
+ try {
3068
+ state = JSON.parse(await fs.readFile(queueStatePath, "utf8"));
3069
+ if (!state || typeof state !== "object") state = {};
3070
+ } catch (_error) {
3071
+ state = {};
3072
+ }
3073
+ state.offset = 0;
3074
+ state.updatedAt = new Date().toISOString();
3075
+ state.note = "reset_after_deepseek_harness_source_migration_2026_08";
3076
+ await ensureDir(path.dirname(queueStatePath));
3077
+ await fs.writeFile(queueStatePath, `${JSON.stringify(state, null, 2)}\n`, "utf8");
3078
+ }
3079
+
3080
+ migrations[DSH_LEGACY_SOURCE_MIGRATION_KEY] = {
3081
+ status: "applied",
3082
+ appliedAt: new Date().toISOString(),
3083
+ migratedBuckets: legacyKeys.size,
3084
+ retractedBuckets: legacyKeys.size,
3085
+ };
3086
+ return true;
3087
+ }
3088
+
2915
3089
  function normalizeString(value) {
2916
3090
  if (typeof value !== "string") return null;
2917
3091
  const trimmed = value.trim();
@@ -1,4 +1,5 @@
1
1
  const fssync = require("node:fs");
2
+ const os = require("node:os");
2
3
  const path = require("node:path");
3
4
  const wsl = require("./wsl-probe");
4
5
 
@@ -48,6 +49,39 @@ function pathExists(p, existsSync) {
48
49
  try { return (existsSync || fssync.existsSync)(p) ? p : null; } catch (_e) { return null; }
49
50
  }
50
51
 
52
+ // ZCode stores its CLI DB under the user's home dir on every platform
53
+ // (~/.zcode/cli/db/db.sqlite), Windows included. The win32 APPDATA guess
54
+ // (introduced with the WSL dual-install work) silently missed the native
55
+ // install, so probe home first and keep APPDATA only as a fallback for
56
+ // installs that do live under Roaming. Falls back to the home path when
57
+ // neither exists so resolveInstallPaths can null it uniformly.
58
+ function resolveZcodeNativeDbPath({
59
+ home = os.homedir(),
60
+ env = process.env,
61
+ platform = process.platform,
62
+ deps = {},
63
+ } = {}) {
64
+ const existsSync = deps.existsSync || fssync.existsSync;
65
+ const zcodeHomeOverride =
66
+ typeof env.ZCODE_HOME === "string" ? env.ZCODE_HOME.trim() : "";
67
+ const zcodeHome = zcodeHomeOverride
68
+ ? path.resolve(zcodeHomeOverride)
69
+ : path.join(home, ".zcode");
70
+ // An explicit ZCODE_HOME pins the install: when its database is missing we
71
+ // must not fall back to an APPDATA database, or sync could read a different
72
+ // ZCode installation than the one the user pointed at.
73
+ const candidates = [
74
+ path.join(zcodeHome, "cli", "db", "db.sqlite"),
75
+ ...(!zcodeHomeOverride &&
76
+ platform === "win32" &&
77
+ typeof env.APPDATA === "string" &&
78
+ env.APPDATA.trim()
79
+ ? [path.join(env.APPDATA.trim(), ".zcode", "cli", "db", "db.sqlite")]
80
+ : []),
81
+ ];
82
+ return candidates.find((p) => existsSync(p)) || candidates[0];
83
+ }
84
+
51
85
  function hasAnyChild(p, children, existsSync) {
52
86
  const ex = existsSync || fssync.existsSync;
53
87
  return children.some((c) => { try { return ex(path.join(p, c)); } catch (_e) { return false; } });
@@ -99,6 +133,7 @@ function ensureFlatCursor(cursors, providerName, env, preferredKey) {
99
133
 
100
134
  module.exports = {
101
135
  resolveInstallPaths,
136
+ resolveZcodeNativeDbPath,
102
137
  ensureNamespacedCursors,
103
138
  ensureFlatCursor,
104
139
  };
@@ -880,6 +880,7 @@ function runSyncCommand(extraEnv = {}, opts = {}) {
880
880
  if (opts.publishAccount === true) args.push("--publish-account");
881
881
  if (opts.allLocalSources === true) args.push("--all-local-sources");
882
882
  if (opts.drain === true) args.push("--drain");
883
+ if (opts.waitForLock === true) args.push("--wait-for-lock");
883
884
  const child = spawn(process.execPath, args, {
884
885
  env: { ...process.env, ...extraEnv },
885
886
  stdio: ["ignore", "pipe", "pipe"],
@@ -1971,6 +1972,8 @@ function createLocalApiHandler({ queuePath }) {
1971
1972
  background,
1972
1973
  publishAccount,
1973
1974
  allLocalSources,
1975
+ waitForLock:
1976
+ !drain && !background && Boolean(extraEnv.TOKENTRACKER_DEVICE_TOKEN),
1974
1977
  });
1975
1978
  try {
1976
1979
  const { resetUsageLimitsCache } = require("./usage-limits");