tokentracker-cli 1.1.4 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dashboard/dist/assets/{AchievementBadge-BGA-HT_B.js → AchievementBadge-CpK5rOMf.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-CtRh5AjH.js → AchievementsPage-BtcQkHZR.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-BarRcKaN.js → AchievementsSection-BT50hdFh.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-Bg0BDAN2.js → BadgeDetailModal-DFtz7FxR.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-By9J3-dn.js → BotAnimated-DuZSWD8p.js} +1 -1
- package/dashboard/dist/assets/{Card-CIoJYi2j.js → Card-BzGCR7t6.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-Dkvc2xe8.js → ClawdAnimated-tpie3z4W.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-BssDtHRl.js → CommandPalette-DxL_wTUY.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-CQrgLlDb.js → CommunityStatsModal-5FYbcq_Q.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-C3hBPgYc.js → ConfirmModal-Dd1z1ofU.js} +1 -1
- package/dashboard/dist/assets/{Controls-BSW6a1HS.js → Controls-DlImhUxL.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-Dhv7fejy.js → DashboardPage-DIPzwCc4.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-D5eZn0wo.js → DevicePage-CtdxXb6m.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-BltQpaJo.js → DialogClose-FTnu24dV.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-CqQpH51z.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-CVddcRPZ.js → DialogTitle-C8_lCmoh.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-goPv73MT.js → FadeIn-Cw9s9Vyn.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-BDBrrkzt.js → HeaderGithubStar-C8GHdUnS.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-qyMezXOs.js → HoverTooltip-BFgXXFLw.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-Ces8JIPb.js → IpCheckPage-C62rs4Sf.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-B2TmLGno.js → LandingPage-qa3wKk7Y.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-CVVRoNua.js → LeaderboardAvatar-BkznbeVC.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-xtt-bOY8.js → LeaderboardPage-DTNaWWUP.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-BM4d_I_r.js → LeaderboardProfileModal-DkP4Wttv.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-BWqhFLQX.js → LeaderboardProfilePage-C7K3PNq3.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-BZL4Pcl8.js → LimitsPage-CdQfWCxG.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-D4gnOEJj.js → LocalOnlyNotice-ClvY1_wG.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-CB17FQ5w.js → LoginCard-B1kZLJZd.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-BI-O1rH4.js → LoginModal-GiYCVrbT.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-DnGzsvJ4.js → LoginPage-INlKWFTQ.js} +1 -1
- package/dashboard/dist/assets/{PetPage-NRnIKMaJ.js → PetPage-uq1reCKp.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-DHoK6UJZ.js → PopoverPopup-Dc5Z66VI.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-BTkdx9o2.js → ProviderIcon-Qh2N0LTI.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-C6K0w318.js → ResetPasswordPage-BdlpShC8.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-gEoWioBE.js → SegmentedControl-DCmwiJYf.js} +1 -1
- package/dashboard/dist/assets/{Select-CI-qtEYu.js → Select-BIMHJc2D.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-q-yhzzjw.js → SelectItemText-CMkxqxvY.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-C4xD1CEe.js → ServiceStatusPage-C1ll8UfE.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-CS0Yun2z.js → SessionsPage-DVnwnAWa.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-GVY_ktz4.js → SettingsPage-CGs5NN_Z.js} +2 -2
- package/dashboard/dist/assets/{SkillsPage-COPRL95O.js → SkillsPage-5hrIvbe1.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-Czi0Ic48.js → TokenGalaxy-BfD0rv-M.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-BZkTiBJB.js → ToolbarRootContext-BlMLA5Ez.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-CnIsuxV-.js → TrendMonitor-DnJCeEQS.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-OdX_Fsul.js → WidgetsPage-DsKnuAcr.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-h9N63-F8.js → WrappedPage-CXwtk-hY.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-BFNWaGsk.js → __vite-browser-external-cuNuS_b-.js} +2 -2
- package/dashboard/dist/assets/{agent-logos-BDiPxrST.js → agent-logos-DxLnGJgJ.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-DtFQUeJG.js → arrow-up-right-Cj7u2wr7.js} +1 -1
- package/dashboard/dist/assets/{check-C35onaNL.js → check-DNFOvbbk.js} +1 -1
- package/dashboard/dist/assets/{composite-qxBzts0W.js → composite-DQR0hWa5.js} +1 -1
- package/dashboard/dist/assets/{copy-D_D71TLc.js → copy-D0SHZEpt.js} +1 -1
- package/dashboard/dist/assets/{download-DMQI2hQe.js → download-UlivGLVm.js} +1 -1
- package/dashboard/dist/assets/{external-link-CemM4dWf.js → external-link-rdZCNL_v.js} +1 -1
- package/dashboard/dist/assets/{flame-CIvk0SP5.js → flame-qfy_Ty_R.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-D3JSvcFl.js → format-tokens-DU-AJn2b.js} +1 -1
- package/dashboard/dist/assets/{icons-Ccf5W0AL.js → icons-CZ7P_cMG.js} +1 -1
- package/dashboard/dist/assets/{index-Brd2evyz.js → index-CXnOAYdM.js} +1 -1
- package/dashboard/dist/assets/{index-C8Wri_HI.js → index-DDfiGiEo.js} +1 -1
- package/dashboard/dist/assets/{index-CFXmD_KX.js → index-DKnHC0bC.js} +1 -1
- package/dashboard/dist/assets/{info-CafrKEG8.js → info-BY3dZuIN.js} +1 -1
- package/dashboard/dist/assets/{link-2-Ds-9Nzlp.js → link-2-CSTcMOFn.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-DA4I55-V.js → loader-circle-yIM6ih7L.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-CjMPt5kU.js → maximize-2-C1P3TlYj.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-DIxTS09h.js → modifiers.esm-BoQpSulH.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-IZpIOxn3.js → monitor-smartphone-DafIVSH1.js} +1 -1
- package/dashboard/dist/assets/{plus-vbLFNs70.js → plus-B4l58vrs.js} +1 -1
- package/dashboard/dist/assets/{provider-display-BAu_UIfV.js → provider-display-CN_xz_OA.js} +1 -1
- package/dashboard/dist/assets/{react-Djb4VGKb.js → react-B9dzohvC.js} +1 -1
- package/dashboard/dist/assets/{search-Ct6LY9Ke.js → search-B8ejosPU.js} +1 -1
- package/dashboard/dist/assets/{skills-api-BnGI2gEp.js → skills-api-BLC6ZUle.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-CjCGd6g5.js → sortable.esm-BLUZnPyk.js} +1 -1
- package/dashboard/dist/assets/{store-CBxkOqRe.js → store-3B91_nUE.js} +1 -1
- package/dashboard/dist/assets/{terminal-Dlpdp5Az.js → terminal-DccsqdRD.js} +1 -1
- package/dashboard/dist/assets/{trash-pxUSKv66.js → trash-DfkcSCtq.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-tHaixRIw.js → use-community-stats-CuIZHVoQ.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-Cs44WPfL.js → use-latest-request-guard-B_oV9ZfF.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-Dw56PXWy.js → use-limits-display-prefs-CXj8GHMY.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-DpLx8oLV.js → use-ordered-list-dAFHiuLi.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-UMqqLkcq.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-BN3FtBvr.js → use-session-efficiency-pref-B9Q45jnR.js} +1 -1
- package/dashboard/dist/assets/{use-transform-C4nTGgzb.js → use-transform-CWR6dZvr.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-Dmwdc1yo.js → use-usage-limits-BmwSEzJh.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-DSc3A_Ih.js → useCurrency-BjHTP-xy.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-BHjcMgm8.js → useTokenFormat-DMOiOFlN.js} +1 -1
- package/dashboard/dist/assets/{zap-BE8i_wu2.js → zap-v0h1PWbB.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/serve.js +147 -17
- package/src/commands/status.js +57 -2
- package/src/commands/sync.js +90 -11
- package/src/lib/codex-context-breakdown.js +3 -1
- package/src/lib/diagnostics.js +20 -1
- package/src/lib/doctor.js +30 -0
- package/src/lib/local-api.js +6 -2
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/scan-roots.js +277 -0
- package/src/lib/session-analytics.js +98 -16
- package/src/lib/usage-limits.js +27 -1
- package/dashboard/dist/assets/DialogDescription-Cof52Icj.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-DrlJ9X6p.js +0 -1
package/src/commands/status.js
CHANGED
|
@@ -116,6 +116,13 @@ const {
|
|
|
116
116
|
const wsl = require("../lib/wsl-probe");
|
|
117
117
|
const { getWslMode, isInvalidWslMode, shouldProbeWsl, discoverWslHome } = wsl;
|
|
118
118
|
const { resolveInstallPaths, resolveZcodeNativeDbPath, resolveMimoNativeDbPath } = require("../lib/install-resolver");
|
|
119
|
+
const {
|
|
120
|
+
describeScanRootOrigin,
|
|
121
|
+
describeScanRootState,
|
|
122
|
+
hasAnyScanChild,
|
|
123
|
+
resolveEnvRoot,
|
|
124
|
+
resolveScanRoots,
|
|
125
|
+
} = require("../lib/scan-roots");
|
|
119
126
|
const { probeGrokHookState, resolveGrokHome } = require("../lib/grok-hook");
|
|
120
127
|
const { probeOmpHookState } = require("../lib/omp-hook");
|
|
121
128
|
|
|
@@ -197,7 +204,9 @@ async function cmdStatus(argv = []) {
|
|
|
197
204
|
const uploadThrottlePath = path.join(trackerDir, "upload.throttle.json");
|
|
198
205
|
const autoRetryPath = path.join(trackerDir, "auto.retry.json");
|
|
199
206
|
const syncSkipPath = path.join(trackerDir, "sync.skip.json");
|
|
200
|
-
|
|
207
|
+
// Normalized like sync (a relative CODEX_HOME is anchored to home, not cwd)
|
|
208
|
+
// so status lists exactly the root sync walks.
|
|
209
|
+
const codexHome = resolveEnvRoot("codex", { env: process.env, home }) || path.join(home, ".codex");
|
|
201
210
|
const codexConfigPath = path.join(codexHome, "config.toml");
|
|
202
211
|
const acodeHome = process.env.TOKENTRACKER_ACODE_HOME || path.join(home, ".acode");
|
|
203
212
|
const acodeConfigPath = path.join(acodeHome, "config.toml");
|
|
@@ -229,6 +238,33 @@ async function cmdStatus(argv = []) {
|
|
|
229
238
|
const geminiHookCommand = buildGeminiHookCommand(notifyPath);
|
|
230
239
|
|
|
231
240
|
const config = await readJson(configPath);
|
|
241
|
+
// Extra scan roots (#657): CODEX_HOME / CLAUDE_CONFIG_DIR of this process
|
|
242
|
+
// plus config.scanRoots, resolved exactly as sync does, so status lists every
|
|
243
|
+
// root sync walks — this is the output users paste when usage looks wrong.
|
|
244
|
+
const scanRoots = resolveScanRoots({
|
|
245
|
+
home,
|
|
246
|
+
env: process.env,
|
|
247
|
+
config,
|
|
248
|
+
base: { codex: [codexHome], claude: [path.join(home, ".claude")] },
|
|
249
|
+
});
|
|
250
|
+
const extraScanRoots = [];
|
|
251
|
+
for (const provider of ["codex", "claude"]) {
|
|
252
|
+
for (const entry of scanRoots[provider]) {
|
|
253
|
+
if (entry.origin === "native") continue;
|
|
254
|
+
extraScanRoots.push({
|
|
255
|
+
provider,
|
|
256
|
+
origin: describeScanRootOrigin(entry, provider),
|
|
257
|
+
path: entry.path,
|
|
258
|
+
exists: entry.exists,
|
|
259
|
+
error: entry.error || null,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const describeExtraScanRoot = (root) =>
|
|
264
|
+
`${root.provider} ${root.origin}: ${root.path}${describeScanRootState(root)}`;
|
|
265
|
+
const scanRootsLine = extraScanRoots.length > 0
|
|
266
|
+
? `- Extra scan roots: ${extraScanRoots.map(describeExtraScanRoot).join(" | ")}`
|
|
267
|
+
: null;
|
|
232
268
|
const { cursors } = await readCursorStateSummary({ trackerDir, cursorsPath });
|
|
233
269
|
const codexRecordOnlyWarning = formatRecordOnlyWarning(countRecordOnlyFiles(cursors));
|
|
234
270
|
const queueState = (await readJson(queueStatePath)) || { offset: 0 };
|
|
@@ -419,6 +455,10 @@ async function cmdStatus(argv = []) {
|
|
|
419
455
|
? wsl.discoverWslHome(".claude")
|
|
420
456
|
: null;
|
|
421
457
|
if (wslClaudeHomeStatus) claudeHomesStatus.push({ dir: wslClaudeHomeStatus, label: "WSL" });
|
|
458
|
+
for (const entry of scanRoots.claude) {
|
|
459
|
+
if (entry.origin === "native" || !entry.exists) continue;
|
|
460
|
+
claudeHomesStatus.push({ dir: entry.path, label: describeScanRootOrigin(entry, "claude") });
|
|
461
|
+
}
|
|
422
462
|
for (const { dir, label } of claudeHomesStatus) {
|
|
423
463
|
const projects = path.join(dir, "projects");
|
|
424
464
|
try {
|
|
@@ -657,7 +697,7 @@ async function cmdStatus(argv = []) {
|
|
|
657
697
|
// users are asked to paste when Codex usage looks wrong, so it must list
|
|
658
698
|
// every root sync actually walks — and no empty shell sync would skip.
|
|
659
699
|
const codexPaths = resolveInstallPaths({
|
|
660
|
-
nativeValue:
|
|
700
|
+
nativeValue: codexHome,
|
|
661
701
|
wslDir: ".codex",
|
|
662
702
|
requireAnyChild: ["sessions", "archived_sessions"],
|
|
663
703
|
union: true,
|
|
@@ -665,6 +705,11 @@ async function cmdStatus(argv = []) {
|
|
|
665
705
|
// Both children, matching requireAnyChild above: an install holding only
|
|
666
706
|
// archived_sessions/ is counted by sync and must not read as "not detected".
|
|
667
707
|
const codexActive = formatResolvedPaths(codexPaths, ["sessions", "archived_sessions"]);
|
|
708
|
+
for (const entry of scanRoots.codex) {
|
|
709
|
+
if (entry.origin === "native" || !entry.exists) continue;
|
|
710
|
+
if (!hasAnyScanChild(entry.path, ["sessions", "archived_sessions"])) continue;
|
|
711
|
+
codexActive.push(`${describeScanRootOrigin(entry, "codex")}: ${entry.path}`);
|
|
712
|
+
}
|
|
668
713
|
const codexInstalledStatus = codexActive.length > 0;
|
|
669
714
|
|
|
670
715
|
const acodePaths = resolveInstallPaths({
|
|
@@ -983,6 +1028,10 @@ async function cmdStatus(argv = []) {
|
|
|
983
1028
|
last_upload_error: lastUploadError || null,
|
|
984
1029
|
last_sync_skipped: syncSkip?.at ? syncSkip : null,
|
|
985
1030
|
auto_retry: autoRetry || null,
|
|
1031
|
+
// Extra scan roots (#657): CODEX_HOME / CLAUDE_CONFIG_DIR of this process
|
|
1032
|
+
// plus config.scanRoots; `exists` false + `error` null means absent,
|
|
1033
|
+
// `error` set means present but unreadable.
|
|
1034
|
+
extra_scan_roots: extraScanRoots,
|
|
986
1035
|
hooks: {
|
|
987
1036
|
codex_notify: notifyConfigured,
|
|
988
1037
|
acode_notify: acodeConfigured,
|
|
@@ -1198,6 +1247,7 @@ async function cmdStatus(argv = []) {
|
|
|
1198
1247
|
lastUploadError ? `- Last upload error: ${lastUploadError}` : null,
|
|
1199
1248
|
syncSkipLine,
|
|
1200
1249
|
autoRetryLine,
|
|
1250
|
+
scanRootsLine,
|
|
1201
1251
|
`- Codex notify: ${notifyConfigured ? JSON.stringify(codexNotify) : "unset"}`,
|
|
1202
1252
|
`- AStudio notify: ${acodeConfigured ? JSON.stringify(acodeNotify) : "unset"}`,
|
|
1203
1253
|
`- Every Code notify: ${everyCodeConfigured ? JSON.stringify(everyCodeNotify) : "unset"}`,
|
|
@@ -1525,6 +1575,11 @@ function renderLightTable(summary) {
|
|
|
1525
1575
|
push(`Hook · ${name}`, state ? "set" : "unset");
|
|
1526
1576
|
}
|
|
1527
1577
|
|
|
1578
|
+
for (const root of summary.extra_scan_roots || []) {
|
|
1579
|
+
const state = root.exists ? "" : (root.error ? ` (unreadable: ${root.error})` : " (missing)");
|
|
1580
|
+
push(`Scan root · ${root.provider}`, `${root.origin}: ${root.path}${state}`);
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1528
1583
|
for (const [name, info] of Object.entries(summary.providers || {})) {
|
|
1529
1584
|
const detail = [];
|
|
1530
1585
|
if (typeof info.installed === "boolean") detail.push(info.installed ? "installed" : "not installed");
|
package/src/commands/sync.js
CHANGED
|
@@ -172,6 +172,14 @@ const {
|
|
|
172
172
|
openCursorStore,
|
|
173
173
|
} = require("../lib/cursor-store");
|
|
174
174
|
const { resolveTrackerPaths } = require("../lib/tracker-paths");
|
|
175
|
+
const {
|
|
176
|
+
appendUniqueDirs,
|
|
177
|
+
extraScanRootPaths,
|
|
178
|
+
hasAnyScanChild,
|
|
179
|
+
resolveEnvRoot,
|
|
180
|
+
resolveScanRoots,
|
|
181
|
+
scanRootDirState,
|
|
182
|
+
} = require("../lib/scan-roots");
|
|
175
183
|
const { resolveRuntimeConfig, isLegacyInsforgeBaseUrl } = require("../lib/runtime-config");
|
|
176
184
|
const { extractTokenCount } = require("../lib/codex-rollout-parser");
|
|
177
185
|
const {
|
|
@@ -601,7 +609,24 @@ async function cmdSync(argv, context = {}) {
|
|
|
601
609
|
persistedAnonKey: config.anonKey,
|
|
602
610
|
};
|
|
603
611
|
}
|
|
604
|
-
|
|
612
|
+
// Scan roots (#657). Every producer — hook-fired sync, native background
|
|
613
|
+
// refresh, CLI — must derive the SAME root list, and the cursor store's
|
|
614
|
+
// codexRoots must come from that list: a rollout under a root the store
|
|
615
|
+
// does not know is filed in core.json instead of its per-day shard, so two
|
|
616
|
+
// producers with different roots never see each other's cursor and re-parse
|
|
617
|
+
// the file from byte 0 on every alternation (#639). CODEX_HOME keeps its
|
|
618
|
+
// existing meaning (replaces ~/.codex for this process) but is normalized
|
|
619
|
+
// once by resolveEnvRoot — a relative value is anchored to home, not cwd —
|
|
620
|
+
// and that single value feeds discovery AND the cursor store below.
|
|
621
|
+
// config.scanRoots adds roots for every process.
|
|
622
|
+
const codexNativeValue = resolveEnvRoot("codex", { env: process.env, home }) || path.join(home, ".codex");
|
|
623
|
+
const scanRoots = resolveScanRoots({
|
|
624
|
+
home,
|
|
625
|
+
env: process.env,
|
|
626
|
+
config,
|
|
627
|
+
base: { codex: [codexNativeValue], claude: [path.join(home, ".claude")] },
|
|
628
|
+
});
|
|
629
|
+
const codexCursorRoots = scanRoots.codex.map((entry) => entry.path);
|
|
605
630
|
const cursorStore = await openCursorStore({
|
|
606
631
|
trackerDir,
|
|
607
632
|
cursorsPath,
|
|
@@ -643,7 +668,16 @@ async function cmdSync(argv, context = {}) {
|
|
|
643
668
|
claudeInstallHomes.push(claudeNativeHome);
|
|
644
669
|
}
|
|
645
670
|
if (wslClaudeHome) claudeInstallHomes.push(wslClaudeHome);
|
|
646
|
-
|
|
671
|
+
// Extra Claude roots (#657): CLAUDE_CONFIG_DIR of the spawning process and
|
|
672
|
+
// config.scanRoots.claude, additive to the homes above so coverage does not
|
|
673
|
+
// depend on which process spawned this sync. Deduped by realpath at the
|
|
674
|
+
// projects/ level: two profiles may symlink one projects/ dir, and reading
|
|
675
|
+
// it under both spellings would double-parse every file, leaving
|
|
676
|
+
// correctness to the bounded claudeHashes layer.
|
|
677
|
+
const claudeProjectsDirs = appendUniqueDirs(
|
|
678
|
+
claudeInstallHomes.map((h) => path.join(h, "projects")),
|
|
679
|
+
extraScanRootPaths(scanRoots.claude).map((h) => path.join(h, "projects")),
|
|
680
|
+
);
|
|
647
681
|
const xdgDataHome = process.env.XDG_DATA_HOME || path.join(home, ".local", "share");
|
|
648
682
|
const kiloHome = process.env.KILO_HOME || path.join(xdgDataHome, "kilo");
|
|
649
683
|
|
|
@@ -700,7 +734,6 @@ async function cmdSync(argv, context = {}) {
|
|
|
700
734
|
|
|
701
735
|
const sources = [];
|
|
702
736
|
if (sourceAllowed("codex")) {
|
|
703
|
-
const codexNativeValue = process.env.CODEX_HOME || path.join(home, ".codex");
|
|
704
737
|
// resolveInstallPaths stays the single authority for wsl-first /
|
|
705
738
|
// native-first / wsl-only / native-only / both selection; requireAnyChild
|
|
706
739
|
// makes it validate that a candidate actually holds sessions/ or
|
|
@@ -740,6 +773,17 @@ async function cmdSync(argv, context = {}) {
|
|
|
740
773
|
sources.push({ source: "codex", sessionsDir: path.join(codexPaths.wsl, "archived_sessions"), deep: true });
|
|
741
774
|
}
|
|
742
775
|
}
|
|
776
|
+
// Extra Codex roots (#657) from config.scanRoots.codex. Same populated-
|
|
777
|
+
// root rule as requireAnyChild above so an empty shell dir is not walked;
|
|
778
|
+
// the day-inventory cache is keyed by day directory, so sharing it across
|
|
779
|
+
// roots is safe.
|
|
780
|
+
for (const extraRoot of extraScanRootPaths(scanRoots.codex)) {
|
|
781
|
+
if (!hasAnyScanChild(extraRoot, ["sessions", "archived_sessions"])) continue;
|
|
782
|
+
sources.push({ source: "codex", sessionsDir: path.join(extraRoot, "sessions"), inventoryCacheKey: "codexDayInventoryCache" });
|
|
783
|
+
if (!isBackgroundLightweightSync || backgroundCodexUsageRepair) {
|
|
784
|
+
sources.push({ source: "codex", sessionsDir: path.join(extraRoot, "archived_sessions"), deep: true });
|
|
785
|
+
}
|
|
786
|
+
}
|
|
743
787
|
}
|
|
744
788
|
if (sourceAllowed("acode")) {
|
|
745
789
|
const acodeNativeValue =
|
|
@@ -1067,14 +1111,49 @@ async function cmdSync(argv, context = {}) {
|
|
|
1067
1111
|
}
|
|
1068
1112
|
if (isFullSourceScan) {
|
|
1069
1113
|
await reincludeClaudeMemObserverFiles({ cursors, claudeFiles, queuePath, queueStatePath });
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1114
|
+
// The ground-truth repair rebuilds every Claude queue row from the roots
|
|
1115
|
+
// it is given. A configured root that is absent or unreadable right now
|
|
1116
|
+
// (unmounted volume, permissions) may still hold history that an earlier
|
|
1117
|
+
// scoped sync queued, so rebuilding without it would erase that history.
|
|
1118
|
+
// The same applies one level down: listClaudeProjectFiles turns a read
|
|
1119
|
+
// error on projects/ into an empty listing, so an unreadable projects/
|
|
1120
|
+
// would let the repair run against nothing and mark itself complete. An
|
|
1121
|
+
// ABSENT projects/ is fine for a NEW root (a profile with no sessions
|
|
1122
|
+
// yet) but not for one the cursor store shows has supplied files before:
|
|
1123
|
+
// then the directory vanished and rebuilding without it would erase its
|
|
1124
|
+
// history. Defer in both cases: the migration key stays unset and the
|
|
1125
|
+
// repair runs on a later full scan once the root is back. Ordinary
|
|
1126
|
+
// scanning still proceeds.
|
|
1127
|
+
const cursorFilePaths = Object.keys(cursors.files || {});
|
|
1128
|
+
const rootPreviouslySuppliedFiles = (rootPath) => {
|
|
1129
|
+
const prefix = path.join(rootPath, "projects") + path.sep;
|
|
1130
|
+
return cursorFilePaths.some((filePath) => filePath.startsWith(prefix));
|
|
1131
|
+
};
|
|
1132
|
+
const unavailableClaudeRoots = scanRoots.claude
|
|
1133
|
+
.filter((entry) => {
|
|
1134
|
+
if (entry.origin === "native") return false;
|
|
1135
|
+
if (!entry.exists) return true;
|
|
1136
|
+
const projectsState = scanRootDirState(path.join(entry.path, "projects"));
|
|
1137
|
+
if (projectsState.error !== null) return true;
|
|
1138
|
+
return !projectsState.exists && rootPreviouslySuppliedFiles(entry.path);
|
|
1139
|
+
})
|
|
1140
|
+
.map((entry) => entry.path);
|
|
1141
|
+
if (unavailableClaudeRoots.length > 0) {
|
|
1142
|
+
if (!opts.auto) {
|
|
1143
|
+
process.stderr.write(
|
|
1144
|
+
`Claude ground-truth repair deferred: configured scan root(s) unavailable: ${unavailableClaudeRoots.join(", ")}\n`,
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
} else {
|
|
1148
|
+
await repairClaudeQueueFromGroundTruth({
|
|
1149
|
+
cursors,
|
|
1150
|
+
queuePath,
|
|
1151
|
+
queueStatePath,
|
|
1152
|
+
projectQueuePath,
|
|
1153
|
+
projectQueueStatePath,
|
|
1154
|
+
rootDirs: claudeProjectsDirs,
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1078
1157
|
}
|
|
1079
1158
|
let claudeResult = { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
1080
1159
|
if (claudeFiles.length > 0) {
|
|
@@ -15,6 +15,8 @@ const fs = require("node:fs");
|
|
|
15
15
|
const os = require("node:os");
|
|
16
16
|
const path = require("node:path");
|
|
17
17
|
|
|
18
|
+
const { resolveEnvRoot } = require("./scan-roots");
|
|
19
|
+
|
|
18
20
|
const {
|
|
19
21
|
emptyTotals,
|
|
20
22
|
addInto,
|
|
@@ -396,7 +398,7 @@ async function computeCodexContextBreakdown({
|
|
|
396
398
|
toKey = range?.to || null;
|
|
397
399
|
}
|
|
398
400
|
|
|
399
|
-
const codexHome = process.env.
|
|
401
|
+
const codexHome = resolveEnvRoot("codex", { env: process.env, home: os.homedir() }) || path.join(os.homedir(), ".codex");
|
|
400
402
|
const roots = codexDir
|
|
401
403
|
? [codexDir]
|
|
402
404
|
: [path.join(codexHome, "sessions"), path.join(codexHome, "archived_sessions")];
|
package/src/lib/diagnostics.js
CHANGED
|
@@ -3,6 +3,7 @@ const path = require("node:path");
|
|
|
3
3
|
const fs = require("node:fs/promises");
|
|
4
4
|
|
|
5
5
|
const { readJson } = require("./fs");
|
|
6
|
+
const { resolveEnvRoot, resolveScanRoots } = require("./scan-roots");
|
|
6
7
|
const { readCursorStateSummary } = require("./cursor-store");
|
|
7
8
|
const { readCodexNotify, readEveryCodeNotify } = require("./codex-config");
|
|
8
9
|
const { areClaudeUsageHooksConfigured, buildClaudeHookCommand } = require("./claude-config");
|
|
@@ -66,7 +67,7 @@ function resolveKiroCliDbPathInline(env, home) {
|
|
|
66
67
|
|
|
67
68
|
async function collectTrackerDiagnostics({
|
|
68
69
|
home = os.homedir(),
|
|
69
|
-
codexHome = process.env
|
|
70
|
+
codexHome = resolveEnvRoot("codex", { env: process.env, home }) || path.join(home, ".codex"),
|
|
70
71
|
codeHome = process.env.CODE_HOME || path.join(home, ".code"),
|
|
71
72
|
} = {}) {
|
|
72
73
|
const { trackerDir, binDir } = await resolveTrackerPaths({ home });
|
|
@@ -92,6 +93,20 @@ async function collectTrackerDiagnostics({
|
|
|
92
93
|
path.join(home, ".grok");
|
|
93
94
|
|
|
94
95
|
const config = await readJson(configPath);
|
|
96
|
+
// Extra scan roots (#657), resolved as sync does. Paths are home-redacted
|
|
97
|
+
// like every other entry in `paths`.
|
|
98
|
+
const scanRoots = resolveScanRoots({
|
|
99
|
+
home,
|
|
100
|
+
env: process.env,
|
|
101
|
+
config,
|
|
102
|
+
base: { codex: [codexHome], claude: [path.join(home, ".claude")] },
|
|
103
|
+
});
|
|
104
|
+
const describeScanRoots = (entries) => entries.map((entry) => ({
|
|
105
|
+
path: redactValue(entry.path, home),
|
|
106
|
+
origin: entry.origin,
|
|
107
|
+
exists: entry.exists,
|
|
108
|
+
error: entry.error || null,
|
|
109
|
+
}));
|
|
95
110
|
const cursorSummary = await readCursorStateSummary({ trackerDir, cursorsPath });
|
|
96
111
|
const cursors = cursorSummary.cursors;
|
|
97
112
|
const queueState = (await readJson(queueStatePath)) || { offset: 0 };
|
|
@@ -217,6 +232,10 @@ async function collectTrackerDiagnostics({
|
|
|
217
232
|
merge_policy:
|
|
218
233
|
"Kiro IDE and Kiro CLI both emit source='kiro' in queue.jsonl so token, cost, heatmap, and leaderboard aggregations merge transparently. Use this block to distinguish sub-path contributions.",
|
|
219
234
|
},
|
|
235
|
+
scan_roots: {
|
|
236
|
+
codex: describeScanRoots(scanRoots.codex),
|
|
237
|
+
claude: describeScanRoots(scanRoots.claude),
|
|
238
|
+
},
|
|
220
239
|
config: {
|
|
221
240
|
base_url: typeof config?.baseUrl === "string" ? config.baseUrl : null,
|
|
222
241
|
device_token: config?.deviceToken ? "set" : "unset",
|
package/src/lib/doctor.js
CHANGED
|
@@ -337,6 +337,36 @@ function buildDiagnosticsChecks(diagnostics) {
|
|
|
337
337
|
});
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
+
// Extra scan roots (#657): surface CODEX_HOME / CLAUDE_CONFIG_DIR / config
|
|
341
|
+
// roots in the human-readable report and warn when a configured root is not
|
|
342
|
+
// on disk — a silently missing root is the difference between "no usage"
|
|
343
|
+
// and "not scanned". Installs without extras get no additional line.
|
|
344
|
+
const scanRoots = diagnostics?.scan_roots;
|
|
345
|
+
if (scanRoots && typeof scanRoots === "object") {
|
|
346
|
+
const extras = [];
|
|
347
|
+
for (const provider of ["codex", "claude"]) {
|
|
348
|
+
for (const entry of Array.isArray(scanRoots[provider]) ? scanRoots[provider] : []) {
|
|
349
|
+
if (!entry || entry.origin === "native") continue;
|
|
350
|
+
extras.push({ provider, ...entry });
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (extras.length > 0) {
|
|
354
|
+
const missing = extras.filter((entry) => !entry.exists);
|
|
355
|
+
const describe = (list) => list
|
|
356
|
+
.map((entry) => `${entry.provider} ${entry.path}${entry.error ? ` (unreadable: ${entry.error})` : ""}`)
|
|
357
|
+
.join(", ");
|
|
358
|
+
checks.push({
|
|
359
|
+
id: "scan_roots.extra",
|
|
360
|
+
status: missing.length > 0 ? "warn" : "ok",
|
|
361
|
+
detail: missing.length > 0
|
|
362
|
+
? `${missing.length} of ${extras.length} extra scan root(s) unavailable: ${describe(missing)}`
|
|
363
|
+
: `${extras.length} extra scan root(s) present: ${describe(extras)}`,
|
|
364
|
+
critical: false,
|
|
365
|
+
meta: { roots: extras },
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
340
370
|
return checks;
|
|
341
371
|
}
|
|
342
372
|
|
package/src/lib/local-api.js
CHANGED
|
@@ -1131,8 +1131,9 @@ function buildProxyHeaders(headers) {
|
|
|
1131
1131
|
// Main handler factory
|
|
1132
1132
|
// ---------------------------------------------------------------------------
|
|
1133
1133
|
|
|
1134
|
-
function createLocalApiHandler({ queuePath }) {
|
|
1134
|
+
function createLocalApiHandler({ queuePath, serverVersion = null }) {
|
|
1135
1135
|
const qp = queuePath || resolveQueuePath();
|
|
1136
|
+
const normalizedServerVersion = typeof serverVersion === "string" ? serverVersion.trim() : "";
|
|
1136
1137
|
|
|
1137
1138
|
// Server-side cookie relay: captures auth cookies from InsForge cloud responses
|
|
1138
1139
|
// so that both browser and WKWebView share the same login session via the proxy.
|
|
@@ -1581,7 +1582,10 @@ function createLocalApiHandler({ queuePath }) {
|
|
|
1581
1582
|
"Content-Type": "application/json",
|
|
1582
1583
|
"Cache-Control": "no-store",
|
|
1583
1584
|
});
|
|
1584
|
-
res.end(JSON.stringify({
|
|
1585
|
+
res.end(JSON.stringify({
|
|
1586
|
+
token: localAuthToken,
|
|
1587
|
+
...(normalizedServerVersion ? { serverVersion: normalizedServerVersion } : {}),
|
|
1588
|
+
}));
|
|
1585
1589
|
return true;
|
|
1586
1590
|
}
|
|
1587
1591
|
|