tokentracker-cli 0.84.8 → 0.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dashboard/dist/assets/{AchievementBadge-DAAenS6A.js → AchievementBadge-DEqbHlmn.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-DJ9GMKxL.js → AchievementsPage-BOaEu1AQ.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-DtpLrh2Q.js → AchievementsSection-C370ngO3.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-CkE1DwdN.js → BadgeDetailModal-D7Wkef0K.js} +1 -1
- package/dashboard/dist/assets/{Card-Cs2vVMHG.js → Card-mDEVjNdM.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-BCSNeob2.js → ClawdAnimated-DdsX-z8n.js} +1 -1
- package/dashboard/dist/assets/{CommandPalette-CFDTjp76.js → CommandPalette-DhxRIuHC.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-D2oJpI9i.js → CommunityStatsModal-CG-9JPS6.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-D0a_xdwF.js → ConfirmModal-COhxErLm.js} +1 -1
- package/dashboard/dist/assets/DashboardPage-tX0DKgFE.js +1 -0
- package/dashboard/dist/assets/{DevicePage-c9Fk13gJ.js → DevicePage-BsSeHx84.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-jsfDvF2n.js → DialogClose-eyOW2cbx.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-BwtE4lKl.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-Dt835L7T.js → DialogTitle-H8sGiH7c.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-DJssGrj8.js → FadeIn-pq2ja50M.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-B9MwwA-P.js → HeaderGithubStar-DWgcpDHr.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-DPLMJkai.js → HoverTooltip-CcJ_VR65.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-BXKjxEHe.js → IpCheckPage-CNUN346Q.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-BJNMQTec.js → LandingPage-yaoRGsNI.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-BNwbnOa3.js → LeaderboardAvatar-Ddai4I12.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-BOxejyMy.js → LeaderboardPage-Bajkxf63.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-BEqUZCOD.js → LeaderboardProfileModal-BGYKDgIi.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-CUDV5iyh.js → LeaderboardProfilePage-Ck5vPaeN.js} +1 -1
- package/dashboard/dist/assets/LimitsPage-Bqobtsw2.js +4 -0
- package/dashboard/dist/assets/{LocalOnlyNotice-B3BSeCHd.js → LocalOnlyNotice-e9KSE3wj.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-2mAb696j.js → LoginCard-BhTSm2tb.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-cacKhc6U.js → LoginModal-CRwuThde.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-rnBlie8p.js → LoginPage-j1i74lcc.js} +1 -1
- package/dashboard/dist/assets/{PetPage-D3DhuS6O.js → PetPage-r62ViU1X.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-CxATX7M9.js → PopoverPopup-DopNTyD4.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-B5b9LkmM.js → ProviderIcon-c1LkPhYM.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-BGKmQ_JW.js → ResetPasswordPage-DJdDNvtq.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-BMeYCm2b.js → SegmentedControl-DhWVOTPN.js} +1 -1
- package/dashboard/dist/assets/{Select-CYHXq8im.js → Select-DeUmTmsJ.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-DvI4i06h.js → SelectItemText-pX0B5f7h.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-dv26-VvI.js → ServiceStatusPage-ejIPz6oF.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-DBQruqkO.js → SessionsPage-WEbn90GW.js} +2 -2
- package/dashboard/dist/assets/{SettingsPage-BWRbQ3Iq.js → SettingsPage-B8t5gCeI.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-BonhKgAt.js → SkillsPage-BWMlzeAp.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-BBUD1kpU.js → TokenGalaxy-DIBRBvMj.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-DPMm8uYz.js → ToolbarRootContext-C6KxLvnI.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-CZVE6gID.js → TrendMonitor-BaMNh67w.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-Bz9cieeZ.js → WidgetsPage-DDsy4M3x.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-CruMolZd.js → WrappedPage-CiNnUFRI.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-C3VH6nS4.js → __vite-browser-external-CM-H8-LW.js} +24 -16
- package/dashboard/dist/assets/{agent-logos-HIeYwIqN.js → agent-logos-BjVa6oe7.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-n7Bi4Tvd.js → arrow-up-right-CCum1mqX.js} +1 -1
- package/dashboard/dist/assets/{check-C5Q44G5k.js → check-ySRQc1YP.js} +1 -1
- package/dashboard/dist/assets/{chevron-right-BsnptUU0.js → chevron-right-5vFloHew.js} +1 -1
- package/dashboard/dist/assets/{copy-BGuL9uFa.js → copy-mhfKVMKo.js} +1 -1
- package/dashboard/dist/assets/{download-BljgPwR2.js → download-GdrXglSx.js} +1 -1
- package/dashboard/dist/assets/{external-link-B4Oz8hUe.js → external-link-KGFaKc8s.js} +1 -1
- package/dashboard/dist/assets/{flame-ChLG_NZE.js → flame-BmNtNoS1.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-DjuNk13Z.js → format-tokens-CCpCNw3d.js} +1 -1
- package/dashboard/dist/assets/{icons-CkO42M-u.js → icons-XLKZavTf.js} +1 -1
- package/dashboard/dist/assets/{index-CEsvYBWY.js → index-CsywTeCQ.js} +1 -1
- package/dashboard/dist/assets/{index-CGxawuWx.js → index-IKpmeZte.js} +1 -1
- package/dashboard/dist/assets/{index-D87oE_rV.js → index-_xU5U5Ct.js} +1 -1
- package/dashboard/dist/assets/{info-et8xtJ4v.js → info-DUV5UpmT.js} +1 -1
- package/dashboard/dist/assets/{limits-providers-vO_86yRy.js → limits-providers-DFkCZCZj.js} +1 -1
- package/dashboard/dist/assets/{link-2-Dge2rBkr.js → link-2-D7c64yLY.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-Cs3wpqJe.js → loader-circle-sMONRK2M.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-zn71uZX3.js → maximize-2-CVWRgrk9.js} +1 -1
- package/dashboard/dist/assets/{provider-display-CTt6mzLe.js → provider-display-DSKqVjgm.js} +1 -1
- package/dashboard/dist/assets/{react-BbHDQe6Q.js → react-c1q4mGkS.js} +1 -1
- package/dashboard/dist/assets/{search-3CMhatWb.js → search-CwD3s-_C.js} +1 -1
- package/dashboard/dist/assets/{skills-api-Cm1aiqWL.js → skills-api-BRXj8kjT.js} +1 -1
- package/dashboard/dist/assets/{store-DclXV77t.js → store-lqTW2gfK.js} +1 -1
- package/dashboard/dist/assets/{terminal-BC7uFcA7.js → terminal-DU1_KbKo.js} +1 -1
- package/dashboard/dist/assets/{trash-2-BT8V531F.js → trash-2-B8Y5Xc-K.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-DJr59SGu.js → use-community-stats-DYwwimvp.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-DU8OOFVb.js → use-limits-display-prefs-Bst6ybK3.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-BvSwI4BL.js → use-native-settings-Cqle4lgw.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-Cq5AyV2x.js → use-ordered-list-Dtze0ahb.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-CbZDR54N.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-WS8NhXuW.js → use-session-efficiency-pref-gwbDMHXa.js} +1 -1
- package/dashboard/dist/assets/{use-transform-CLYsi8xc.js → use-transform-CU0yxXpX.js} +1 -1
- package/dashboard/dist/assets/use-usage-limits-5y1I1T5_.js +1 -0
- package/dashboard/dist/assets/{useCurrency-D0GtEoJD.js → useCurrency-BfVNrywe.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-TIUBSeIM.js → useScrollLock-CF1m7AMJ.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-B0TBL9La.js → useTokenFormat-DiRwVvRr.js} +1 -1
- package/dashboard/dist/assets/{zap-D89769CL.js → zap-BZtlWtuN.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/ip-check.html +1 -1
- package/dashboard/dist/leaderboard.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +1 -1
- package/src/commands/status.js +114 -9
- package/src/commands/sync.js +151 -46
- package/src/lib/claude-categorizer.js +36 -9
- package/src/lib/diagnostics.js +86 -13
- package/src/lib/doctor.js +17 -0
- package/src/lib/grok-context-breakdown.js +794 -0
- package/src/lib/install-resolver.js +10 -2
- package/src/lib/local-api.js +21 -3
- package/src/lib/multi-install-parser.js +9 -1
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +480 -53
- package/src/lib/session-analytics.js +332 -9
- package/src/lib/usage-limits.js +47 -2
- package/dashboard/dist/assets/DashboardPage-CMCqvpkA.js +0 -1
- package/dashboard/dist/assets/DialogDescription-Dwxj5h5w.js +0 -1
- package/dashboard/dist/assets/LimitsPage-ejlJN1vi.js +0 -4
- package/dashboard/dist/assets/use-reduced-motion-D1_q3aRK.js +0 -1
- package/dashboard/dist/assets/use-usage-limits-CGoQQDG-.js +0 -1
package/src/commands/sync.js
CHANGED
|
@@ -24,6 +24,7 @@ const {
|
|
|
24
24
|
readQoderDbMessages,
|
|
25
25
|
resolveKiroDbPath,
|
|
26
26
|
resolveKiroJsonlPath,
|
|
27
|
+
resolveKiroBasePath,
|
|
27
28
|
resolveHermesPath,
|
|
28
29
|
resolveCopilotOtelPaths,
|
|
29
30
|
normalizeCopilotDbPath,
|
|
@@ -45,6 +46,8 @@ const {
|
|
|
45
46
|
gooseInstallOwnsCursor,
|
|
46
47
|
zedInstallOwnsCursor,
|
|
47
48
|
hermesInstallOwnsCursor,
|
|
49
|
+
kiroInstallOwnsCursor,
|
|
50
|
+
kiroCliInstallOwnsCursor,
|
|
48
51
|
copilotOtelCursorHasLegacyCliUsage,
|
|
49
52
|
pruneCopilotUsageClaims,
|
|
50
53
|
parseCopilotIncremental,
|
|
@@ -443,7 +446,25 @@ async function cmdSync(argv, context = {}) {
|
|
|
443
446
|
}
|
|
444
447
|
let grokHookSignalConsumed = false;
|
|
445
448
|
|
|
446
|
-
|
|
449
|
+
// Claude Code home — dual-install aware (#307): a Windows host may carry
|
|
450
|
+
// a native ~/.claude plus a WSL install reachable over \\wsl$. Unlike the
|
|
451
|
+
// SQLite providers, Claude UNIONS every allowed install instead of taking
|
|
452
|
+
// resolveAllWin32Paths' single pick: under the default wsl-first mode a
|
|
453
|
+
// WSL ~/.claude would otherwise silently evict the native one from
|
|
454
|
+
// collection — and from the "disk is truth" ground-truth repair below,
|
|
455
|
+
// which would then erase the native history. The file-hash and
|
|
456
|
+
// message-hash dedup layers make the union safe. Native is listed first
|
|
457
|
+
// so the cross-environment file dedup keeps the native copy as primary.
|
|
458
|
+
const claudeNativeHome = path.join(home, ".claude");
|
|
459
|
+
const wslClaudeHome = process.platform === "win32" && wsl.shouldProbeWsl(process.env)
|
|
460
|
+
? wsl.discoverWslHome(".claude")
|
|
461
|
+
: null;
|
|
462
|
+
const claudeInstallHomes = [];
|
|
463
|
+
if (process.platform !== "win32" || wsl.shouldProbeNative(process.env)) {
|
|
464
|
+
claudeInstallHomes.push(claudeNativeHome);
|
|
465
|
+
}
|
|
466
|
+
if (wslClaudeHome) claudeInstallHomes.push(wslClaudeHome);
|
|
467
|
+
const claudeProjectsDirs = claudeInstallHomes.map((h) => path.join(h, "projects"));
|
|
447
468
|
const xdgDataHome = process.env.XDG_DATA_HOME || path.join(home, ".local", "share");
|
|
448
469
|
const kiloHome = process.env.KILO_HOME || path.join(xdgDataHome, "kilo");
|
|
449
470
|
const mimoHome = process.env.MIMO_HOME || path.join(xdgDataHome, "mimocode");
|
|
@@ -751,7 +772,18 @@ async function cmdSync(argv, context = {}) {
|
|
|
751
772
|
openclawResult.eventsAggregated += openclawFallback.eventsAggregated;
|
|
752
773
|
openclawResult.bucketsQueued += openclawFallback.bucketsQueued;
|
|
753
774
|
|
|
754
|
-
|
|
775
|
+
let claudeFiles = [];
|
|
776
|
+
if (sourceAllowed("claude")) {
|
|
777
|
+
const seenClaudeFiles = new Set();
|
|
778
|
+
for (const dir of claudeProjectsDirs) {
|
|
779
|
+
for (const f of await listClaudeProjectFiles(dir)) {
|
|
780
|
+
if (!seenClaudeFiles.has(f)) {
|
|
781
|
+
seenClaudeFiles.add(f);
|
|
782
|
+
claudeFiles.push(f);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
755
787
|
if (isFullSourceScan) {
|
|
756
788
|
await reincludeClaudeMemObserverFiles({ cursors, claudeFiles, queuePath, queueStatePath });
|
|
757
789
|
await repairClaudeQueueFromGroundTruth({
|
|
@@ -760,6 +792,7 @@ async function cmdSync(argv, context = {}) {
|
|
|
760
792
|
queueStatePath,
|
|
761
793
|
projectQueuePath,
|
|
762
794
|
projectQueueStatePath,
|
|
795
|
+
rootDirs: claudeProjectsDirs,
|
|
763
796
|
});
|
|
764
797
|
}
|
|
765
798
|
let claudeResult = { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
@@ -1344,30 +1377,44 @@ async function cmdSync(argv, context = {}) {
|
|
|
1344
1377
|
}
|
|
1345
1378
|
}
|
|
1346
1379
|
|
|
1347
|
-
// ── Kiro (SQLite-based, with JSONL fallback) ──
|
|
1380
|
+
// ── Kiro (SQLite-based, with JSONL fallback; dual-install aware) ──
|
|
1348
1381
|
let kiroResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1382
|
+
if (sourceAllowed("kiro")) {
|
|
1383
|
+
const kiroNativeBase = resolveKiroBasePath(process.env);
|
|
1384
|
+
const wslKiroBase = process.platform === "win32" && wsl.shouldProbeWsl(process.env)
|
|
1385
|
+
? wsl.discoverWslHome(".config/Kiro/User/globalStorage/kiro.kiroagent")
|
|
1386
|
+
: null;
|
|
1387
|
+
const kiroPaths = resolveInstallPaths({ nativeValue: kiroNativeBase, wslValue: wslKiroBase });
|
|
1388
|
+
// resolveInstallPaths only checks the base dir (and skips the check off
|
|
1389
|
+
// win32); keep the original per-install db/jsonl presence gate so empty
|
|
1390
|
+
// installs never spin up a parse or seed a cursor namespace.
|
|
1391
|
+
const kiroHasData = (base) => Boolean(base)
|
|
1392
|
+
&& (fssync.existsSync(resolveKiroDbPath(base)) || fssync.existsSync(resolveKiroJsonlPath(base)));
|
|
1393
|
+
if (!kiroHasData(kiroPaths.native)) kiroPaths.native = null;
|
|
1394
|
+
if (!kiroHasData(kiroPaths.wsl)) kiroPaths.wsl = null;
|
|
1395
|
+
if (kiroPaths.native || kiroPaths.wsl) {
|
|
1396
|
+
if (progress?.enabled) {
|
|
1397
|
+
progress.start(`Parsing Kiro ${renderBar(0)} | buckets 0`);
|
|
1398
|
+
}
|
|
1399
|
+
try {
|
|
1400
|
+
kiroResult = await multiInstallParse({
|
|
1401
|
+
paths: kiroPaths,
|
|
1402
|
+
parserFn: parseKiroIncremental,
|
|
1403
|
+
providerName: "kiro",
|
|
1404
|
+
cursors,
|
|
1405
|
+
getParams: (base) => ({
|
|
1406
|
+
basePath: base,
|
|
1407
|
+
dbPath: resolveKiroDbPath(base),
|
|
1408
|
+
jsonlPath: resolveKiroJsonlPath(base),
|
|
1409
|
+
}),
|
|
1410
|
+
queuePath,
|
|
1411
|
+
onProgress: makeProviderProgress("Kiro"),
|
|
1412
|
+
detectInstall: (base, flatState) =>
|
|
1413
|
+
kiroInstallOwnsCursor(resolveKiroDbPath(base), flatState),
|
|
1414
|
+
});
|
|
1415
|
+
} catch (err) {
|
|
1416
|
+
warnProviderParseFailure("Kiro", err, opts);
|
|
1417
|
+
}
|
|
1371
1418
|
}
|
|
1372
1419
|
}
|
|
1373
1420
|
|
|
@@ -1454,28 +1501,79 @@ async function cmdSync(argv, context = {}) {
|
|
|
1454
1501
|
// token counts (billing is credit-based on Bedrock); we approximate at
|
|
1455
1502
|
// 4 chars/token from user prompt chars and assistant response chars.
|
|
1456
1503
|
let kiroCliResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1504
|
+
if (sourceAllowed("kiro")) {
|
|
1505
|
+
const kiroCliDb = resolveKiroCliDbPath(process.env);
|
|
1506
|
+
const kiroCliSessionFiles = resolveKiroCliSessionFiles(process.env);
|
|
1507
|
+
const nativeCliPresent = fssync.existsSync(kiroCliDb) || kiroCliSessionFiles.length > 0;
|
|
1508
|
+
|
|
1509
|
+
// Explicit overrides pin a single install — never mix them with WSL
|
|
1510
|
+
// auto-discovery (mirrors the Hermes TOKENTRACKER_HERMES_HOME branch).
|
|
1511
|
+
const kiroCliOverride = Boolean(process.env.KIRO_CLI_DB_PATH || process.env.KIRO_HOME);
|
|
1512
|
+
let wslKiroCliEnv = null;
|
|
1513
|
+
let wslKiroCliMarker = null;
|
|
1514
|
+
if (!kiroCliOverride && process.platform === "win32" && wsl.shouldProbeWsl(process.env)) {
|
|
1515
|
+
// A WSL install owns BOTH a data dir (~/.local/share/kiro-cli) and a
|
|
1516
|
+
// sessions home (~/.kiro). Derive both from whichever probe hits so
|
|
1517
|
+
// the per-install env never falls back to native paths.
|
|
1518
|
+
const wslKiroHomeDir = wsl.discoverWslHome(".kiro");
|
|
1519
|
+
const wslCliDataDir = wsl.discoverWslHome(".local/share/kiro-cli");
|
|
1520
|
+
const wslHomeRoot = wslKiroHomeDir
|
|
1521
|
+
? path.dirname(wslKiroHomeDir)
|
|
1522
|
+
: (wslCliDataDir ? path.dirname(path.dirname(path.dirname(wslCliDataDir))) : null);
|
|
1523
|
+
if (wslHomeRoot) {
|
|
1524
|
+
const wslCliDb = path.join(wslHomeRoot, ".local", "share", "kiro-cli", "data.sqlite3");
|
|
1525
|
+
wslKiroCliEnv = {
|
|
1526
|
+
...process.env,
|
|
1527
|
+
KIRO_CLI_DB_PATH: wslCliDb,
|
|
1528
|
+
KIRO_HOME: path.join(wslHomeRoot, ".kiro"),
|
|
1529
|
+
};
|
|
1530
|
+
const wslCliPresent = fssync.existsSync(wslCliDb)
|
|
1531
|
+
|| resolveKiroCliSessionFiles(wslKiroCliEnv).length > 0;
|
|
1532
|
+
if (wslCliPresent) wslKiroCliMarker = wslCliDb;
|
|
1533
|
+
}
|
|
1462
1534
|
}
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1535
|
+
|
|
1536
|
+
// Paths here are install markers only — the parser resolves its DB and
|
|
1537
|
+
// session files from the per-install env (KIRO_CLI_DB_PATH/KIRO_HOME).
|
|
1538
|
+
const kiroCliPaths = process.platform === "win32"
|
|
1539
|
+
? wsl.resolveAllWin32Paths({
|
|
1540
|
+
nativeValue: nativeCliPresent ? kiroCliDb : null,
|
|
1541
|
+
wslValue: wslKiroCliMarker,
|
|
1467
1542
|
env: process.env,
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1543
|
+
platform: "win32",
|
|
1544
|
+
})
|
|
1545
|
+
: { native: nativeCliPresent ? kiroCliDb : null, wsl: null };
|
|
1546
|
+
const kiroCliEnvFor = (p) =>
|
|
1547
|
+
wslKiroCliEnv && p === wslKiroCliMarker ? wslKiroCliEnv : process.env;
|
|
1548
|
+
if (kiroCliPaths.native || kiroCliPaths.wsl) {
|
|
1549
|
+
if (progress?.enabled) {
|
|
1550
|
+
progress.start(`Parsing Kiro CLI ${renderBar(0)} | buckets 0`);
|
|
1551
|
+
}
|
|
1552
|
+
try {
|
|
1553
|
+
kiroCliResult = await multiInstallParse({
|
|
1554
|
+
paths: kiroCliPaths,
|
|
1555
|
+
parserFn: parseKiroCliIncremental,
|
|
1556
|
+
providerName: "kiroCli",
|
|
1557
|
+
cursors,
|
|
1558
|
+
// Per-install env MUST come from getParams: multiInstallParse
|
|
1559
|
+
// spreads shared params over it, so a top-level env would clobber
|
|
1560
|
+
// the per-install one.
|
|
1561
|
+
getParams: (p) => ({ env: kiroCliEnvFor(p) }),
|
|
1562
|
+
queuePath,
|
|
1563
|
+
onProgress: (p) => {
|
|
1564
|
+
if (!progress?.enabled) return;
|
|
1565
|
+
const pct = p.total > 0 ? p.index / p.total : 1;
|
|
1566
|
+
progress.update(
|
|
1567
|
+
`Parsing Kiro CLI ${renderBar(pct)} ${formatNumber(p.index)}/${formatNumber(p.total)} sessions | buckets ${formatNumber(p.bucketsQueued)}`,
|
|
1568
|
+
);
|
|
1569
|
+
},
|
|
1570
|
+
detectInstall: (p, flatState) =>
|
|
1571
|
+
kiroCliInstallOwnsCursor(kiroCliEnvFor(p).KIRO_CLI_DB_PATH || kiroCliDb, flatState),
|
|
1572
|
+
});
|
|
1573
|
+
} catch (err) {
|
|
1574
|
+
if (!opts.auto) {
|
|
1575
|
+
process.stderr.write(`Kiro CLI sync: ${err.message}\n`);
|
|
1576
|
+
}
|
|
1479
1577
|
}
|
|
1480
1578
|
}
|
|
1481
1579
|
}
|
|
@@ -4688,6 +4786,7 @@ async function repairClaudeQueueFromGroundTruth({
|
|
|
4688
4786
|
queueStatePath = null,
|
|
4689
4787
|
projectQueuePath = null,
|
|
4690
4788
|
projectQueueStatePath = null,
|
|
4789
|
+
rootDirs = null,
|
|
4691
4790
|
}) {
|
|
4692
4791
|
if (!cursors || typeof cursors !== "object") return false;
|
|
4693
4792
|
const migrations = (cursors.migrations ||= {});
|
|
@@ -4695,7 +4794,13 @@ async function repairClaudeQueueFromGroundTruth({
|
|
|
4695
4794
|
|
|
4696
4795
|
let result;
|
|
4697
4796
|
try {
|
|
4698
|
-
|
|
4797
|
+
// rootDirs must cover every install the incremental parser scans (native
|
|
4798
|
+
// AND WSL): this repair's semantics are "disk is truth" — it clears all
|
|
4799
|
+
// claude buckets and replaces claudeHashes wholesale, so scanning fewer
|
|
4800
|
+
// roots than sync would silently drop the missing install's history.
|
|
4801
|
+
result = await computeClaudeGroundTruthBuckets(
|
|
4802
|
+
Array.isArray(rootDirs) && rootDirs.length > 0 ? { rootDirs } : {},
|
|
4803
|
+
);
|
|
4699
4804
|
} catch (e) {
|
|
4700
4805
|
console.error("[sync] claude ground-truth repair: scan failed:", e?.message || e);
|
|
4701
4806
|
return false;
|
|
@@ -1189,9 +1189,24 @@ function toUtcHalfHourStart(ts) {
|
|
|
1189
1189
|
).toISOString();
|
|
1190
1190
|
}
|
|
1191
1191
|
|
|
1192
|
-
async function computeClaudeGroundTruthBuckets({ rootDir = null } = {}) {
|
|
1193
|
-
|
|
1194
|
-
|
|
1192
|
+
async function computeClaudeGroundTruthBuckets({ rootDir = null, rootDirs = null } = {}) {
|
|
1193
|
+
// Multi-root (#307): a Windows host may scan a native ~/.claude/projects
|
|
1194
|
+
// plus a WSL install over \\wsl$. `rootDirs` wins over the legacy single
|
|
1195
|
+
// `rootDir`; duplicated session files synced between environments collapse
|
|
1196
|
+
// via the msgId+reqId dedup below.
|
|
1197
|
+
const roots = Array.isArray(rootDirs) && rootDirs.length > 0
|
|
1198
|
+
? rootDirs
|
|
1199
|
+
: [rootDir || defaultClaudeProjectsDir()];
|
|
1200
|
+
const files = [];
|
|
1201
|
+
const seenFiles = new Set();
|
|
1202
|
+
for (const root of roots) {
|
|
1203
|
+
for (const fp of listSessionFiles(root)) {
|
|
1204
|
+
if (!seenFiles.has(fp)) {
|
|
1205
|
+
seenFiles.add(fp);
|
|
1206
|
+
files.push(fp);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1195
1210
|
const buckets = new Map(); // `${model}|${hourStart}` → totals
|
|
1196
1211
|
const seenHashes = new Set();
|
|
1197
1212
|
const userMessageBuckets = new Map(); // for conversation_count tracking
|
|
@@ -1204,7 +1219,9 @@ async function computeClaudeGroundTruthBuckets({ rootDir = null } = {}) {
|
|
|
1204
1219
|
continue;
|
|
1205
1220
|
}
|
|
1206
1221
|
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1207
|
-
|
|
1222
|
+
// Separator-agnostic: WSL roots are UNC paths where path.join produces
|
|
1223
|
+
// backslashes (mirrors the same check in rollout.js parseClaudeFile).
|
|
1224
|
+
const isMainSession = !/[\\/]subagents[\\/]/.test(fp);
|
|
1208
1225
|
|
|
1209
1226
|
for await (const line of rl) {
|
|
1210
1227
|
if (!line) continue;
|
|
@@ -1224,11 +1241,21 @@ async function computeClaudeGroundTruthBuckets({ rootDir = null } = {}) {
|
|
|
1224
1241
|
typeof content === "string" ||
|
|
1225
1242
|
(Array.isArray(content) && content.some((b) => b?.type === "text"));
|
|
1226
1243
|
if (hasText) {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1244
|
+
// Dedup by line uuid (mirrors parseClaudeFile): with multiple
|
|
1245
|
+
// roots the same session file exists under both the native and
|
|
1246
|
+
// the \\wsl$ path, and without this every user message — and so
|
|
1247
|
+
// conversation_count — would double. Usage rows are already
|
|
1248
|
+
// guarded by claudeMessageDedupKey below.
|
|
1249
|
+
const userKey =
|
|
1250
|
+
typeof userObj?.uuid === "string" && userObj.uuid ? `u:${userObj.uuid}` : null;
|
|
1251
|
+
if (!userKey || !seenHashes.has(userKey)) {
|
|
1252
|
+
if (userKey) seenHashes.add(userKey);
|
|
1253
|
+
const ts = typeof userObj?.timestamp === "string" ? userObj.timestamp : null;
|
|
1254
|
+
const hourStart = ts ? toUtcHalfHourStart(ts) : null;
|
|
1255
|
+
if (hourStart) {
|
|
1256
|
+
const k = `unknown|${hourStart}`;
|
|
1257
|
+
userMessageBuckets.set(k, (userMessageBuckets.get(k) || 0) + 1);
|
|
1258
|
+
}
|
|
1232
1259
|
}
|
|
1233
1260
|
}
|
|
1234
1261
|
}
|
package/src/lib/diagnostics.js
CHANGED
|
@@ -18,12 +18,43 @@ const { probeOpenclawHookState } = require("./openclaw-hook");
|
|
|
18
18
|
const { probeOpenclawSessionPluginState } = require("./openclaw-session-plugin");
|
|
19
19
|
const { probeGrokHookState } = require("./grok-hook");
|
|
20
20
|
const { resolveTrackerPaths } = require("./tracker-paths");
|
|
21
|
-
|
|
21
|
+
const wsl = require("./wsl-probe");
|
|
22
|
+
// TASK-011: Kiro paths inlined here to avoid pulling the ~4000-line
|
|
22
23
|
// rollout module on every `tokentracker status` / `diagnostics` call.
|
|
23
|
-
// rollout.js still exports resolveKiroCliDbPath
|
|
24
|
+
// rollout.js still exports resolveKiroCliDbPath / resolveKiroBasePath for
|
|
25
|
+
// external callers; keep the platform branches in lockstep.
|
|
26
|
+
function resolveKiroIdeBaseInline(env, home) {
|
|
27
|
+
const suffix = ["Kiro", "User", "globalStorage", "kiro.kiroagent"];
|
|
28
|
+
if (process.platform === "win32") {
|
|
29
|
+
const appData = typeof env.APPDATA === "string" && env.APPDATA.trim().length > 0
|
|
30
|
+
? env.APPDATA.trim()
|
|
31
|
+
: path.join(home, "AppData", "Roaming");
|
|
32
|
+
return path.join(appData, ...suffix);
|
|
33
|
+
}
|
|
34
|
+
if (process.platform === "linux") {
|
|
35
|
+
const configHome = typeof env.XDG_CONFIG_HOME === "string" && env.XDG_CONFIG_HOME.trim().length > 0
|
|
36
|
+
? env.XDG_CONFIG_HOME.trim()
|
|
37
|
+
: path.join(home, ".config");
|
|
38
|
+
return path.join(configHome, ...suffix);
|
|
39
|
+
}
|
|
40
|
+
return path.join(home, "Library", "Application Support", ...suffix);
|
|
41
|
+
}
|
|
42
|
+
|
|
24
43
|
function resolveKiroCliDbPathInline(env, home) {
|
|
25
44
|
if (env.KIRO_CLI_DB_PATH) return env.KIRO_CLI_DB_PATH;
|
|
26
45
|
const effectiveHome = env.HOME || home;
|
|
46
|
+
if (process.platform === "win32") {
|
|
47
|
+
const localAppData = typeof env.LOCALAPPDATA === "string" && env.LOCALAPPDATA.trim().length > 0
|
|
48
|
+
? env.LOCALAPPDATA.trim()
|
|
49
|
+
: path.join(effectiveHome, "AppData", "Local");
|
|
50
|
+
return path.join(localAppData, "kiro-cli", "data.sqlite3");
|
|
51
|
+
}
|
|
52
|
+
if (process.platform === "linux") {
|
|
53
|
+
const dataHome = typeof env.XDG_DATA_HOME === "string" && env.XDG_DATA_HOME.trim().length > 0
|
|
54
|
+
? env.XDG_DATA_HOME.trim()
|
|
55
|
+
: path.join(effectiveHome, ".local", "share");
|
|
56
|
+
return path.join(dataHome, "kiro-cli", "data.sqlite3");
|
|
57
|
+
}
|
|
27
58
|
return path.join(
|
|
28
59
|
effectiveHome,
|
|
29
60
|
"Library",
|
|
@@ -106,22 +137,42 @@ async function collectTrackerDiagnostics({
|
|
|
106
137
|
// Kiro IDE and Kiro CLI sub-path presence — merged under one "kiro" source
|
|
107
138
|
// at token/cost aggregation level; operators need visibility of both
|
|
108
139
|
// sub-paths here for debugging.
|
|
109
|
-
const kiroIdeDevDataDir = path.join(
|
|
110
|
-
home,
|
|
111
|
-
"Library",
|
|
112
|
-
"Application Support",
|
|
113
|
-
"Kiro",
|
|
114
|
-
"User",
|
|
115
|
-
"globalStorage",
|
|
116
|
-
"kiro.kiroagent",
|
|
117
|
-
"dev_data",
|
|
118
|
-
);
|
|
140
|
+
const kiroIdeDevDataDir = path.join(resolveKiroIdeBaseInline(process.env, home), "dev_data");
|
|
119
141
|
const kiroIdePresent =
|
|
120
142
|
(await safeStatSize(path.join(kiroIdeDevDataDir, "devdata.sqlite"))) > 0 ||
|
|
121
143
|
(await safeStatSize(path.join(kiroIdeDevDataDir, "tokens_generated.jsonl"))) > 0;
|
|
122
144
|
const kiroCliDbPath = resolveKiroCliDbPathInline(process.env, home);
|
|
123
145
|
const kiroCliPresent = require("node:fs").existsSync(kiroCliDbPath);
|
|
124
146
|
|
|
147
|
+
// WSL installs (win32 only) — surfaced so `tracker doctor --json` can
|
|
148
|
+
// confirm dual-install discovery without running a sync.
|
|
149
|
+
let kiroWslInstalls = null;
|
|
150
|
+
let claudeWslProjects = null;
|
|
151
|
+
if (process.platform === "win32" && wsl.shouldProbeWsl(process.env)) {
|
|
152
|
+
const wslClaudeHome = wsl.discoverWslHome(".claude");
|
|
153
|
+
if (wslClaudeHome) claudeWslProjects = redactWslUser(path.join(wslClaudeHome, "projects"));
|
|
154
|
+
const wslIdeBase = wsl.discoverWslHome(".config/Kiro/User/globalStorage/kiro.kiroagent");
|
|
155
|
+
const wslKiroHomeDir = wsl.discoverWslHome(".kiro");
|
|
156
|
+
const wslCliDataDir = wsl.discoverWslHome(".local/share/kiro-cli");
|
|
157
|
+
const wslHomeRoot = wslKiroHomeDir
|
|
158
|
+
? path.dirname(wslKiroHomeDir)
|
|
159
|
+
: (wslCliDataDir ? path.dirname(path.dirname(path.dirname(wslCliDataDir))) : null);
|
|
160
|
+
const wslCliDb = wslHomeRoot
|
|
161
|
+
? path.join(wslHomeRoot, ".local", "share", "kiro-cli", "data.sqlite3")
|
|
162
|
+
: null;
|
|
163
|
+
if (wslIdeBase || wslHomeRoot) {
|
|
164
|
+
kiroWslInstalls = {
|
|
165
|
+
ide_dev_data: wslIdeBase ? redactWslUser(path.join(wslIdeBase, "dev_data")) : null,
|
|
166
|
+
ide_present: Boolean(wslIdeBase) && (
|
|
167
|
+
(await safeStatSize(path.join(wslIdeBase, "dev_data", "devdata.sqlite"))) > 0 ||
|
|
168
|
+
(await safeStatSize(path.join(wslIdeBase, "dev_data", "tokens_generated.jsonl"))) > 0
|
|
169
|
+
),
|
|
170
|
+
cli_db: wslCliDb ? redactWslUser(wslCliDb) : null,
|
|
171
|
+
cli_present: Boolean(wslCliDb) && require("node:fs").existsSync(wslCliDb),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
125
176
|
const lastSuccessAt = uploadThrottle.lastSuccessMs
|
|
126
177
|
? new Date(uploadThrottle.lastSuccessMs).toISOString()
|
|
127
178
|
: null;
|
|
@@ -143,6 +194,8 @@ async function collectTrackerDiagnostics({
|
|
|
143
194
|
code_home: redactValue(codeHome, home),
|
|
144
195
|
code_config: redactValue(codeConfigPath, home),
|
|
145
196
|
claude_config: redactValue(claudeConfigPath, home),
|
|
197
|
+
claude_projects: redactValue(path.join(home, ".claude", "projects"), home),
|
|
198
|
+
...(process.platform === "win32" ? { claude_projects_wsl: claudeWslProjects } : {}),
|
|
146
199
|
gemini_config: redactValue(geminiSettingsPath, home),
|
|
147
200
|
opencode_config: redactValue(opencodeConfigDir, home),
|
|
148
201
|
grok_home: redactValue(grokHome, home),
|
|
@@ -154,6 +207,9 @@ async function collectTrackerDiagnostics({
|
|
|
154
207
|
kiro: {
|
|
155
208
|
ide_present: kiroIdePresent,
|
|
156
209
|
cli_present: kiroCliPresent,
|
|
210
|
+
...(process.platform === "win32"
|
|
211
|
+
? { wsl_mode: wsl.getWslMode(process.env), wsl_installs: kiroWslInstalls }
|
|
212
|
+
: {}),
|
|
157
213
|
cli_approximation:
|
|
158
214
|
"Kiro CLI does not persist explicit token counts (billing is credit-based on Bedrock). Tokens are approximated at 4 chars/token from user prompt chars and assistant response chars. Source rows that came through this path have model='kiro-cli-agent' when the underlying model is unknown (auto-routing); known Bedrock ARNs canonicalize to their short name (e.g. claude-sonnet-4).",
|
|
159
215
|
merge_policy:
|
|
@@ -243,6 +299,17 @@ function redactValue(value, home) {
|
|
|
243
299
|
return value.startsWith(homeNorm) ? `~${value.slice(homeNorm.length)}` : value;
|
|
244
300
|
}
|
|
245
301
|
|
|
302
|
+
// UNC WSL paths embed the distro name AND the Linux username; doctor reports
|
|
303
|
+
// are shared for support, so mask the user segment the same way redactValue
|
|
304
|
+
// masks $HOME (\\wsl$\Ubuntu\home\alice\... → \\wsl$\Ubuntu\home\~\...).
|
|
305
|
+
function redactWslUser(value) {
|
|
306
|
+
if (typeof value !== "string") return value;
|
|
307
|
+
return value.replace(
|
|
308
|
+
/^([\\/]{2}wsl(?:\$|\.localhost)[\\/][^\\/]+[\\/]home[\\/])[^\\/]+/i,
|
|
309
|
+
"$1~",
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
246
313
|
function redactError(message, home) {
|
|
247
314
|
if (typeof message !== "string") return message;
|
|
248
315
|
if (typeof home !== "string" || home.length === 0) return message;
|
|
@@ -275,4 +342,10 @@ function parseEpochMsToIso(v) {
|
|
|
275
342
|
return d.toISOString();
|
|
276
343
|
}
|
|
277
344
|
|
|
278
|
-
module.exports = {
|
|
345
|
+
module.exports = {
|
|
346
|
+
collectTrackerDiagnostics,
|
|
347
|
+
// Exported for the parity test that pins these inline copies to the
|
|
348
|
+
// canonical resolvers in rollout.js (see the lockstep note above).
|
|
349
|
+
resolveKiroIdeBaseInline,
|
|
350
|
+
resolveKiroCliDbPathInline,
|
|
351
|
+
};
|
package/src/lib/doctor.js
CHANGED
|
@@ -320,6 +320,23 @@ function buildDiagnosticsChecks(diagnostics) {
|
|
|
320
320
|
meta: { last_error: uploadError ? uploadError.message || null : null },
|
|
321
321
|
});
|
|
322
322
|
|
|
323
|
+
// Dual-install visibility (#306): surface resolved Kiro installs in the
|
|
324
|
+
// human-readable doctor output, not just the embedded diagnostics JSON.
|
|
325
|
+
const kiro = diagnostics?.kiro;
|
|
326
|
+
if (kiro && typeof kiro.wsl_mode === "string") {
|
|
327
|
+
const fmt = (present) => (present ? "yes" : "no");
|
|
328
|
+
const wslPart = kiro.wsl_installs
|
|
329
|
+
? `wsl ide=${fmt(kiro.wsl_installs.ide_present)} cli=${fmt(kiro.wsl_installs.cli_present)}`
|
|
330
|
+
: "wsl not detected";
|
|
331
|
+
checks.push({
|
|
332
|
+
id: "kiro.installs",
|
|
333
|
+
status: "ok",
|
|
334
|
+
detail: `native ide=${fmt(kiro.ide_present)} cli=${fmt(kiro.cli_present)}; ${wslPart} (mode ${kiro.wsl_mode})`,
|
|
335
|
+
critical: false,
|
|
336
|
+
meta: { wsl_mode: kiro.wsl_mode, wsl_installs: kiro.wsl_installs || null },
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
323
340
|
return checks;
|
|
324
341
|
}
|
|
325
342
|
|