tokentracker-cli 1.1.3 → 1.1.5
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.de.md +5 -4
- package/README.ja.md +5 -4
- package/README.ko.md +5 -4
- package/README.md +20 -4
- package/README.zh-CN.md +5 -4
- package/dashboard/dist/assets/{AchievementBadge-dt_UDU_h.js → AchievementBadge-ClUmGxBJ.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-B5DJ3S9l.js → AchievementsPage-BB0DFE3m.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-CDxcyig2.js → AchievementsSection-BMCSk63s.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-Cp98zNn2.js → BadgeDetailModal-DzJtvuKO.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-CR7MiUpo.js → BotAnimated-BviEasW4.js} +1 -1
- package/dashboard/dist/assets/{Card-C8IdDvlg.js → Card-BoHr-wLc.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-COeTlW8z.js → ClawdAnimated-Bwm8BfqY.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-Co9k2CCc.js → CommandPalette-Rb0Gfcg9.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-C6XQeRgw.js → CommunityStatsModal-Ck7R7Q-d.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-Ai4182O-.js → ConfirmModal-52Fu9wOi.js} +1 -1
- package/dashboard/dist/assets/{Controls-BbKzN-Pe.js → Controls-YlGr187A.js} +1 -1
- package/dashboard/dist/assets/DashboardPage-BRim2Rt6.js +1 -0
- package/dashboard/dist/assets/{DevicePage-CZbrtJmb.js → DevicePage-3mmhzfgd.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-DMl8tAoX.js → DialogClose-D1KfBYUS.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-BhWwQSSg.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-BJxpp2t-.js → DialogTitle-DKye2nJK.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-hU_MXl6R.js → FadeIn-mJp_ZFst.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-CM5RSxxN.js → HeaderGithubStar-B2R_aqp9.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-BZKfQyZQ.js → HoverTooltip-ElY02914.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-BjDx-zja.js → IpCheckPage-CNzPUe3x.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-CAt8BCDv.js → LandingPage-L25-CxXg.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-DiCrakjI.js → LeaderboardAvatar-BT7x5wAP.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-Dj3pHv6U.js → LeaderboardPage-bPce9gOU.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-DEWbcP9p.js → LeaderboardProfileModal-DPrFGAp6.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-CTbWHQkY.js → LeaderboardProfilePage-j2D1pwYO.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-V-98OUhF.js → LimitsPage-CSED1kBx.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-a4PqtUZY.js → LocalOnlyNotice-D6OERWez.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-oD1A2Ru8.js → LoginCard-HgmRdIn8.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-D8aSmxok.js → LoginModal-BSlEmKOV.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-DIjhBv2X.js → LoginPage-HQAhowvh.js} +1 -1
- package/dashboard/dist/assets/{PetPage-D7601BTX.js → PetPage-DnbYfu-z.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-aPnmKopO.js → PopoverPopup-DnyVdfB8.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-rwyfcNlT.js → ProviderIcon-D8kgkZlR.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-AF3SBgW2.js → ResetPasswordPage-ChtXGykP.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-fnlScNMT.js → SegmentedControl-dHUpXqFk.js} +1 -1
- package/dashboard/dist/assets/{Select-DDjC3RjB.js → Select-C0tpjNiq.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-VhBWE2O8.js → SelectItemText-C3G_w14k.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-BL3qqEsp.js → ServiceStatusPage-D6x4e1r8.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-DXYiuP2x.js → SessionsPage-DZH1QMWd.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-DTeA_ahr.js → SettingsPage-DGmDb2R4.js} +2 -2
- package/dashboard/dist/assets/{SkillsPage-CtTRwVQH.js → SkillsPage-ChloCHPa.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-CifK91V7.js → TokenGalaxy-DOC9wlkv.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-ClGAIYoa.js → ToolbarRootContext-BB_d-qdY.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-D1evFHRM.js → TrendMonitor-DVVzYdc7.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-Cq4RYJDE.js → WidgetsPage-DHVSyU0i.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-CoC5RJH7.js → WrappedPage-BDi28nRt.js} +1 -1
- package/dashboard/dist/assets/__vite-browser-external-Bnc5aBhq.js +45 -0
- package/dashboard/dist/assets/{agent-logos-QLdA9OeA.js → agent-logos-BiGvZiLq.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-BYLgB3bH.js → arrow-up-right-SvD82rok.js} +1 -1
- package/dashboard/dist/assets/{check-DfnIvqZD.js → check-jL5XErEW.js} +1 -1
- package/dashboard/dist/assets/{composite--JllTpIX.js → composite-DM6geXPA.js} +1 -1
- package/dashboard/dist/assets/{copy-ClKVBIgD.js → copy-Dy_GIzj2.js} +1 -1
- package/dashboard/dist/assets/{download-Di1-UFJX.js → download-BW_FS7nX.js} +1 -1
- package/dashboard/dist/assets/{external-link-D7zPVFLO.js → external-link-2RIovvCT.js} +1 -1
- package/dashboard/dist/assets/{flame-BMJTxjVg.js → flame-CIRJIxxA.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-DCZM5Nfn.js → format-tokens-CT8ahEK4.js} +1 -1
- package/dashboard/dist/assets/{icons-I615W2IU.js → icons-7J1WD0C-.js} +1 -1
- package/dashboard/dist/assets/{index-BX3CXVdD.js → index-D3iSSyYr.js} +1 -1
- package/dashboard/dist/assets/{index-CwczM3BZ.js → index-Ddb2A6Mx.js} +1 -1
- package/dashboard/dist/assets/{index-BejnmdPe.js → index-_vJl6sF7.js} +1 -1
- package/dashboard/dist/assets/{info-CFJPny5Q.js → info-BjVWcOOC.js} +1 -1
- package/dashboard/dist/assets/{link-2-D1ydWh_w.js → link-2-CyRWnHWE.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-Cq-J-OFg.js → loader-circle-DUpG5zah.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-kIoSq57I.js → maximize-2-BVGCRkFZ.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-CLojthuF.js → modifiers.esm-CZI7rED4.js} +1 -1
- package/dashboard/dist/assets/module-WSx8viXK.js +3 -0
- package/dashboard/dist/assets/{monitor-smartphone-DX2qWHYV.js → monitor-smartphone-DfBtd01T.js} +1 -1
- package/dashboard/dist/assets/{plus-B-JEpo3V.js → plus-CvbLCvjV.js} +1 -1
- package/dashboard/dist/assets/{provider-display-DJDdrTAS.js → provider-display-DC46GLrv.js} +2 -2
- package/dashboard/dist/assets/{react-Ctoe8iLl.js → react-mYr3J8Vw.js} +1 -1
- package/dashboard/dist/assets/{search-BTSQrlW-.js → search-CFaS1rSj.js} +1 -1
- package/dashboard/dist/assets/{skills-api-DKQyM8h7.js → skills-api-B1N6seFa.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-pOEL13xU.js → sortable.esm-BE2GiQaC.js} +1 -1
- package/dashboard/dist/assets/{store-B2RplzvW.js → store-DwBbExah.js} +1 -1
- package/dashboard/dist/assets/{terminal-BH_6_EW_.js → terminal-Bl2BuO9x.js} +1 -1
- package/dashboard/dist/assets/{trash-CC444Mz6.js → trash-D8beb27Y.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-CBXLdD_j.js → use-community-stats-DJG1DsXR.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-DMuPfZQI.js → use-latest-request-guard-DYK8iyOM.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-CmsGTBA4.js → use-limits-display-prefs-BGmcFVX3.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-BNJNpXjB.js → use-ordered-list-Cqia_VIq.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-CmCk9Sb5.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-g_0ISxe1.js → use-session-efficiency-pref-Mj3OeVGZ.js} +1 -1
- package/dashboard/dist/assets/{use-transform-D36BHcXk.js → use-transform-D4BQIBHx.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-Dxw3vzsD.js → use-usage-limits-BFrO7Mtq.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-V2FE4TMo.js → useCurrency-CYJavR2h.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-CEOwvzMt.js → useTokenFormat-CKruI7SP.js} +1 -1
- package/dashboard/dist/assets/{zap-BS7ZU76I.js → zap-a82fG76c.js} +1 -1
- package/dashboard/dist/brand-logos/trae.svg +1 -0
- package/dashboard/dist/index.html +13 -11
- package/dashboard/dist/ip-check.html +1 -1
- package/dashboard/dist/leaderboard.html +1 -1
- package/dashboard/dist/llms.txt +3 -2
- package/dashboard/dist/share.html +1 -1
- package/package.json +6 -4
- package/src/commands/init.js +9 -7
- package/src/commands/status.js +66 -11
- package/src/commands/sync.js +115 -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/pricing/curated-overrides.json +7 -0
- package/src/lib/pricing/matcher.js +5 -0
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +197 -18
- package/src/lib/scan-roots.js +277 -0
- package/src/lib/session-analytics.js +98 -16
- package/src/lib/trae-db.js +127 -0
- package/src/lib/trae-snapshot.js +230 -0
- package/src/lib/trae-sqlite.js +143 -0
- package/src/lib/trae-usage.js +121 -0
- package/dashboard/dist/assets/DashboardPage-DTcVGG4e.js +0 -1
- package/dashboard/dist/assets/DialogDescription-haPz7CX9.js +0 -1
- package/dashboard/dist/assets/__vite-browser-external-BDugA6I0.js +0 -45
- package/dashboard/dist/assets/module-DtWfRKM4.js +0 -3
- package/dashboard/dist/assets/use-reduced-motion-CNpG-Yoi.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({
|
|
@@ -755,9 +800,10 @@ async function cmdStatus(argv = []) {
|
|
|
755
800
|
const devinDbPath = resolveDevinDbPath(process.env);
|
|
756
801
|
const devinInstalled = Boolean(devinDbPath && fssync.existsSync(devinDbPath));
|
|
757
802
|
|
|
758
|
-
|
|
803
|
+
const { resolveTraeDbPaths } = require("../lib/trae-db");
|
|
804
|
+
const traeDbPaths = resolveTraeDbPaths(process.env);
|
|
759
805
|
const traeStoragePath = resolveTraeStoragePath(process.env);
|
|
760
|
-
const traeInstalled = Boolean(traeStoragePath);
|
|
806
|
+
const traeInstalled = Boolean(traeStoragePath || traeDbPaths.length);
|
|
761
807
|
// Render path for the entitlement snapshot: read it straight from the
|
|
762
808
|
// Trae Local State storage.json via the shared parser. The queue stays
|
|
763
809
|
// token-count-only, so the status read path never depends on queue rows
|
|
@@ -982,6 +1028,10 @@ async function cmdStatus(argv = []) {
|
|
|
982
1028
|
last_upload_error: lastUploadError || null,
|
|
983
1029
|
last_sync_skipped: syncSkip?.at ? syncSkip : null,
|
|
984
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,
|
|
985
1035
|
hooks: {
|
|
986
1036
|
codex_notify: notifyConfigured,
|
|
987
1037
|
acode_notify: acodeConfigured,
|
|
@@ -1124,7 +1174,9 @@ async function cmdStatus(argv = []) {
|
|
|
1124
1174
|
trae: traeInstalled
|
|
1125
1175
|
? {
|
|
1126
1176
|
installed: true,
|
|
1127
|
-
detail: traeStoragePath,
|
|
1177
|
+
detail: traeDbPaths[0] || traeStoragePath,
|
|
1178
|
+
usage_databases: traeDbPaths.length,
|
|
1179
|
+
usage_key_source: process.env.TOKENTRACKER_TRAE_SQLCIPHER_KEY?.trim() ? "environment" : "application",
|
|
1128
1180
|
...(traeEntitlement ? { entitlement: traeEntitlement } : {}),
|
|
1129
1181
|
}
|
|
1130
1182
|
: { installed: false },
|
|
@@ -1195,6 +1247,7 @@ async function cmdStatus(argv = []) {
|
|
|
1195
1247
|
lastUploadError ? `- Last upload error: ${lastUploadError}` : null,
|
|
1196
1248
|
syncSkipLine,
|
|
1197
1249
|
autoRetryLine,
|
|
1250
|
+
scanRootsLine,
|
|
1198
1251
|
`- Codex notify: ${notifyConfigured ? JSON.stringify(codexNotify) : "unset"}`,
|
|
1199
1252
|
`- AStudio notify: ${acodeConfigured ? JSON.stringify(acodeNotify) : "unset"}`,
|
|
1200
1253
|
`- Every Code notify: ${everyCodeConfigured ? JSON.stringify(everyCodeNotify) : "unset"}`,
|
|
@@ -1328,12 +1381,9 @@ async function cmdStatus(argv = []) {
|
|
|
1328
1381
|
? `- Devin CLI: passive reader (${devinDbPath})`
|
|
1329
1382
|
: null,
|
|
1330
1383
|
traeInstalled
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
// provider contributes plan info and nothing else — say so, or users go
|
|
1335
|
-
// looking for Trae usage in the dashboard that will never appear.
|
|
1336
|
-
? `- Trae SOLO: plan info only, no token usage (${traeStoragePath})`
|
|
1384
|
+
? traeDbPaths.length
|
|
1385
|
+
? `- TRAE: local usage reader, ${traeDbPaths.length} database(s) (shared application key; optional key override)`
|
|
1386
|
+
: `- TRAE: plan info only, no local usage database found (${traeStoragePath})`
|
|
1337
1387
|
: null,
|
|
1338
1388
|
traeEntitlement
|
|
1339
1389
|
? `- Trae SOLO plan: ${formatTraeEntitlementLine(traeEntitlement)}`
|
|
@@ -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
|
@@ -138,6 +138,7 @@ const {
|
|
|
138
138
|
resolveCommandCodeSessionFiles,
|
|
139
139
|
parseCommandCodeIncremental,
|
|
140
140
|
parseTraeCnApiIncremental,
|
|
141
|
+
parseTraeIncremental,
|
|
141
142
|
bucketKey,
|
|
142
143
|
toUtcHalfHourStart,
|
|
143
144
|
totalsKey,
|
|
@@ -171,6 +172,14 @@ const {
|
|
|
171
172
|
openCursorStore,
|
|
172
173
|
} = require("../lib/cursor-store");
|
|
173
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");
|
|
174
183
|
const { resolveRuntimeConfig, isLegacyInsforgeBaseUrl } = require("../lib/runtime-config");
|
|
175
184
|
const { extractTokenCount } = require("../lib/codex-rollout-parser");
|
|
176
185
|
const {
|
|
@@ -333,6 +342,7 @@ const AUTO_SYNC_SOURCES = new Set([
|
|
|
333
342
|
"reasonix",
|
|
334
343
|
"roocode",
|
|
335
344
|
"trae-cn",
|
|
345
|
+
"trae",
|
|
336
346
|
"unsloth",
|
|
337
347
|
"workbuddy",
|
|
338
348
|
"zcode",
|
|
@@ -599,7 +609,24 @@ async function cmdSync(argv, context = {}) {
|
|
|
599
609
|
persistedAnonKey: config.anonKey,
|
|
600
610
|
};
|
|
601
611
|
}
|
|
602
|
-
|
|
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);
|
|
603
630
|
const cursorStore = await openCursorStore({
|
|
604
631
|
trackerDir,
|
|
605
632
|
cursorsPath,
|
|
@@ -641,7 +668,16 @@ async function cmdSync(argv, context = {}) {
|
|
|
641
668
|
claudeInstallHomes.push(claudeNativeHome);
|
|
642
669
|
}
|
|
643
670
|
if (wslClaudeHome) claudeInstallHomes.push(wslClaudeHome);
|
|
644
|
-
|
|
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
|
+
);
|
|
645
681
|
const xdgDataHome = process.env.XDG_DATA_HOME || path.join(home, ".local", "share");
|
|
646
682
|
const kiloHome = process.env.KILO_HOME || path.join(xdgDataHome, "kilo");
|
|
647
683
|
|
|
@@ -698,7 +734,6 @@ async function cmdSync(argv, context = {}) {
|
|
|
698
734
|
|
|
699
735
|
const sources = [];
|
|
700
736
|
if (sourceAllowed("codex")) {
|
|
701
|
-
const codexNativeValue = process.env.CODEX_HOME || path.join(home, ".codex");
|
|
702
737
|
// resolveInstallPaths stays the single authority for wsl-first /
|
|
703
738
|
// native-first / wsl-only / native-only / both selection; requireAnyChild
|
|
704
739
|
// makes it validate that a candidate actually holds sessions/ or
|
|
@@ -738,6 +773,17 @@ async function cmdSync(argv, context = {}) {
|
|
|
738
773
|
sources.push({ source: "codex", sessionsDir: path.join(codexPaths.wsl, "archived_sessions"), deep: true });
|
|
739
774
|
}
|
|
740
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
|
+
}
|
|
741
787
|
}
|
|
742
788
|
if (sourceAllowed("acode")) {
|
|
743
789
|
const acodeNativeValue =
|
|
@@ -1065,14 +1111,49 @@ async function cmdSync(argv, context = {}) {
|
|
|
1065
1111
|
}
|
|
1066
1112
|
if (isFullSourceScan) {
|
|
1067
1113
|
await reincludeClaudeMemObserverFiles({ cursors, claudeFiles, queuePath, queueStatePath });
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
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
|
+
}
|
|
1076
1157
|
}
|
|
1077
1158
|
let claudeResult = { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
1078
1159
|
if (claudeFiles.length > 0) {
|
|
@@ -2011,6 +2092,27 @@ async function cmdSync(argv, context = {}) {
|
|
|
2011
2092
|
}
|
|
2012
2093
|
}
|
|
2013
2094
|
|
|
2095
|
+
let traeResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
2096
|
+
if (sourceAllowed("trae")) {
|
|
2097
|
+
try {
|
|
2098
|
+
traeResult = await parseTraeIncremental({
|
|
2099
|
+
cursors, queuePath, onProgress: makeProviderProgress("TRAE"),
|
|
2100
|
+
});
|
|
2101
|
+
for (const { database, message } of traeResult.errors) {
|
|
2102
|
+
process.stderr.write(`TRAE sync: could not read ${database}: ${message}. Will retry on the next sync.\n`);
|
|
2103
|
+
}
|
|
2104
|
+
if (traeResult.recordsSkipped > 0 && !opts.auto) {
|
|
2105
|
+
process.stderr.write(`TRAE sync: skipped ${traeResult.recordsSkipped} records with unsupported usage metadata.\n`);
|
|
2106
|
+
}
|
|
2107
|
+
if (traeResult.estimatedRecords > 0 && !opts.auto) {
|
|
2108
|
+
process.stderr.write(`TRAE sync: ${traeResult.estimatedRecords} Gemini records have repaired thought or cache counters, marked as estimated.\n`);
|
|
2109
|
+
}
|
|
2110
|
+
if (traeResult.unpricedRecords > 0 && !opts.auto) {
|
|
2111
|
+
process.stderr.write(`TRAE sync: ${traeResult.unpricedRecords} multi-request turns include earlier input without a cache split; it is counted in token totals but left out of cost.\n`);
|
|
2112
|
+
}
|
|
2113
|
+
} catch (err) { warnProviderParseFailure("TRAE", err); }
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2014
2116
|
// ── Trae Work CN (国内版) — account-level usage API ──
|
|
2015
2117
|
// A simple explicit rolling 30-day window captured once per sync (no
|
|
2016
2118
|
// cursor checkpoints / full-history import / page retries / background
|
|
@@ -3101,6 +3203,7 @@ async function cmdSync(argv, context = {}) {
|
|
|
3101
3203
|
claudeScienceResult.recordsProcessed +
|
|
3102
3204
|
cursorResult.recordsProcessed +
|
|
3103
3205
|
traeCnResult.recordsProcessed +
|
|
3206
|
+
traeResult.recordsProcessed +
|
|
3104
3207
|
kiroResult.recordsProcessed +
|
|
3105
3208
|
kiroCliResult.recordsProcessed +
|
|
3106
3209
|
hermesResult.recordsProcessed +
|
|
@@ -3144,6 +3247,7 @@ async function cmdSync(argv, context = {}) {
|
|
|
3144
3247
|
claudeScienceResult.bucketsQueued +
|
|
3145
3248
|
cursorResult.bucketsQueued +
|
|
3146
3249
|
traeCnResult.bucketsQueued +
|
|
3250
|
+
traeResult.bucketsQueued +
|
|
3147
3251
|
kiroResult.bucketsQueued +
|
|
3148
3252
|
kiroCliResult.bucketsQueued +
|
|
3149
3253
|
hermesResult.bucketsQueued +
|
|
@@ -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
|
|
|
@@ -118,6 +118,13 @@
|
|
|
118
118
|
"cache_write": 0.167,
|
|
119
119
|
"note": "See hy3-preview-agent."
|
|
120
120
|
},
|
|
121
|
+
"qwen3.8-flash": {
|
|
122
|
+
"input": 0.15,
|
|
123
|
+
"output": 0.47,
|
|
124
|
+
"cache_read": 0.016,
|
|
125
|
+
"cache_write": 0.2,
|
|
126
|
+
"note": "Alibaba Model Studio Singapore (International), USD per MTok, verified 2026-09-29: https://www.alibabacloud.com/help/en/model-studio/qwen3-8-flash . Reference API estimate; other regions/providers can have different rates. Issue #715."
|
|
127
|
+
},
|
|
121
128
|
"hy4-preview": {
|
|
122
129
|
"input": 0.833,
|
|
123
130
|
"output": 2.5,
|
|
@@ -277,6 +277,11 @@ function lookupPricing(model, { curated, litellm, source } = {}) {
|
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
// 1. CURATED exact
|
|
280
|
+
// Qwen Flash's provider-qualified and dated IDs share the reference SKU.
|
|
281
|
+
// Keep the boundary strict: Flash-Next and Max have independent prices.
|
|
282
|
+
if (/(?:^|\/)qwen3[.-]8-flash(?:-\d{4}-\d{2}-\d{2})?$/.test(lower.trim()) && curated.exact?.["qwen3.8-flash"]) {
|
|
283
|
+
return { hit: true, source: "curated:exact", value: curated.exact["qwen3.8-flash"] };
|
|
284
|
+
}
|
|
280
285
|
if (curated.exact && curated.exact[lookupModel]) {
|
|
281
286
|
return { hit: true, source: "curated:exact", value: curated.exact[lookupModel] };
|
|
282
287
|
}
|