tokentracker-cli 0.81.2 → 0.81.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dashboard/dist/assets/AchievementBadge-DiSoAalB.js +1 -0
  2. package/dashboard/dist/assets/AchievementsPage-DI_cvzqy.js +2 -0
  3. package/dashboard/dist/assets/AchievementsSection-ClWZZNvc.js +1 -0
  4. package/dashboard/dist/assets/BadgeDetailModal-DpQeG_6H.js +1 -0
  5. package/dashboard/dist/assets/Card-B0hyofoy.js +1 -0
  6. package/dashboard/dist/assets/ClawdAnimated-DZJGQ3-9.js +1 -0
  7. package/dashboard/dist/assets/CommunityStatsModal-Dcikt0kT.js +1 -0
  8. package/dashboard/dist/assets/ConfirmModal-DCfRP4oI.js +1 -0
  9. package/dashboard/dist/assets/DashboardPage-DBajsWEp.js +1 -0
  10. package/dashboard/dist/assets/DevicePage-De6KuFaJ.js +1 -0
  11. package/dashboard/dist/assets/DialogDescription-uQ4gVIv9.js +1 -0
  12. package/dashboard/dist/assets/DialogTitle-BvElfi0r.js +1 -0
  13. package/dashboard/dist/assets/FadeIn-DZoN7vbQ.js +1 -0
  14. package/dashboard/dist/assets/HeaderGithubStar-C9HQnEsI.js +1 -0
  15. package/dashboard/dist/assets/{IpCheckPage-CMf9CHFr.js → IpCheckPage-82zniXwO.js} +3 -3
  16. package/dashboard/dist/assets/{LandingPage-BT61_TqT.js → LandingPage-Df9fdR0n.js} +168 -168
  17. package/dashboard/dist/assets/LeaderboardAvatar-CjfEgoR8.js +1 -0
  18. package/dashboard/dist/assets/LeaderboardPage-BdbE6Nhj.js +2 -0
  19. package/dashboard/dist/assets/{LeaderboardProfileModal-C_kxqOMn.js → LeaderboardProfileModal-CDTK21dp.js} +4 -4
  20. package/dashboard/dist/assets/{LeaderboardProfilePage-zgNsE0EQ.js → LeaderboardProfilePage-B6QWnwB1.js} +3 -3
  21. package/dashboard/dist/assets/{LimitsPage-BiQpqxj8.js → LimitsPage-f0L0d15j.js} +4 -4
  22. package/dashboard/dist/assets/LocalOnlyNotice-DOcSDrOy.js +1 -0
  23. package/dashboard/dist/assets/LoginPage-WY_sRA7I.js +1 -0
  24. package/dashboard/dist/assets/PetPage-CNbgNE6f.js +1 -0
  25. package/dashboard/dist/assets/PopoverPopup-Cv525-dF.js +1 -0
  26. package/dashboard/dist/assets/ResetPasswordPage-vrcj6bBP.js +1 -0
  27. package/dashboard/dist/assets/Select-3HkgkO8y.js +1 -0
  28. package/dashboard/dist/assets/SelectItemText-Bz8FAKEv.js +1 -0
  29. package/dashboard/dist/assets/SettingsPage-CVldAt69.js +1 -0
  30. package/dashboard/dist/assets/SkillsPage-Dnz3eGcE.js +1 -0
  31. package/dashboard/dist/assets/TrendMonitor-OopQ06B8.js +19 -0
  32. package/dashboard/dist/assets/WidgetsPage-4dWjZkc9.js +1 -0
  33. package/dashboard/dist/assets/WrappedPage-Dbd-8EKD.js +1 -0
  34. package/dashboard/dist/assets/__vite-browser-external-CQxW1DuO.css +1 -0
  35. package/dashboard/dist/assets/__vite-browser-external-CxN2E6q5.js +1312 -0
  36. package/dashboard/dist/assets/agent-logos-CT9fumzx.js +1 -0
  37. package/dashboard/dist/assets/arrow-up-right-CGnVz9Km.js +1 -0
  38. package/dashboard/dist/assets/check-CkI3WNti.js +1 -0
  39. package/dashboard/dist/assets/copy-Bh3g2_O-.js +1 -0
  40. package/dashboard/dist/assets/download-BITw7nKX.js +1 -0
  41. package/dashboard/dist/assets/flame-ByAsnNar.js +1 -0
  42. package/dashboard/dist/assets/format-tokens-Cu8JtuN4.js +1 -0
  43. package/dashboard/dist/assets/icons-D9OB0N43.js +1 -0
  44. package/dashboard/dist/assets/index-lKnLA6Pd.js +1 -0
  45. package/dashboard/dist/assets/info-Mu2CLf-A.js +1 -0
  46. package/dashboard/dist/assets/link-2-CSlVaUw9.js +1 -0
  47. package/dashboard/dist/assets/loader-circle-CYck0YaS.js +1 -0
  48. package/dashboard/dist/assets/provider-display-BB925tPa.js +42 -0
  49. package/dashboard/dist/assets/terminal-4YysOnTe.js +1 -0
  50. package/dashboard/dist/assets/trash-2-C8-ZfrZG.js +1 -0
  51. package/dashboard/dist/assets/use-community-stats-Cxt-otko.js +1 -0
  52. package/dashboard/dist/assets/use-limits-display-prefs-BUFZh6fe.js +1 -0
  53. package/dashboard/dist/assets/use-native-settings-DbdPXxGf.js +1 -0
  54. package/dashboard/dist/assets/{use-ordered-list-DPbiKnPc.js → use-ordered-list-BZMM-iIj.js} +2 -2
  55. package/dashboard/dist/assets/use-quality-per-dollar-pref-DXoW5A0_.js +1 -0
  56. package/dashboard/dist/assets/use-transform-CuAoUb8H.js +1 -0
  57. package/dashboard/dist/assets/use-usage-limits-BEc3QMzR.js +1 -0
  58. package/dashboard/dist/assets/useCurrency-CpDnFC0N.js +1 -0
  59. package/dashboard/dist/assets/useScrollLock-Vyaif-7C.js +1 -0
  60. package/dashboard/dist/assets/useTokenFormat-B8FrUarf.js +1 -0
  61. package/dashboard/dist/assets/zap-CdeKJ6HL.js +1 -0
  62. package/dashboard/dist/index.html +2 -2
  63. package/dashboard/dist/share.html +2 -2
  64. package/package.json +1 -1
  65. package/src/commands/serve.js +8 -6
  66. package/src/commands/status.js +2 -1
  67. package/src/commands/sync.js +164 -36
  68. package/src/lib/codex-context-breakdown.js +330 -13
  69. package/src/lib/codex-rollout-parser.js +323 -42
  70. package/src/lib/cursor-store.js +1289 -0
  71. package/src/lib/diagnostics.js +8 -5
  72. package/src/lib/fs.js +254 -16
  73. package/src/lib/local-api.js +106 -17
  74. package/src/lib/pricing/index.js +8 -0
  75. package/src/lib/pricing/seed-snapshot.json +1 -1
  76. package/src/lib/rollout.js +101 -9
  77. package/dashboard/dist/assets/AchievementBadge-DXWHZrAL.js +0 -1
  78. package/dashboard/dist/assets/AchievementsPage-BhrQXniL.js +0 -2
  79. package/dashboard/dist/assets/AchievementsSection-gzP3m1Ml.js +0 -1
  80. package/dashboard/dist/assets/BadgeDetailModal-DGKPuvDU.js +0 -1
  81. package/dashboard/dist/assets/Card-BRtYQ-Ot.js +0 -1
  82. package/dashboard/dist/assets/ClawdAnimated-UsNG-qDv.js +0 -1
  83. package/dashboard/dist/assets/CommunityStatsModal-CJQw-P4t.js +0 -1
  84. package/dashboard/dist/assets/ConfirmModal-ByY4GDsy.js +0 -1
  85. package/dashboard/dist/assets/DashboardPage-CuLVnW98.js +0 -19
  86. package/dashboard/dist/assets/DevicePage-D67CxpmR.js +0 -1
  87. package/dashboard/dist/assets/DialogClose-Ci8hkTw5.js +0 -1
  88. package/dashboard/dist/assets/DialogDescription-C9vXUZU0.js +0 -1
  89. package/dashboard/dist/assets/DialogTitle-sLJxKWjv.js +0 -1
  90. package/dashboard/dist/assets/FadeIn-xVds37w6.js +0 -1
  91. package/dashboard/dist/assets/HeaderGithubStar-kK3omgmB.js +0 -1
  92. package/dashboard/dist/assets/LeaderboardAvatar-DyKz6NdT.js +0 -1
  93. package/dashboard/dist/assets/LeaderboardPage-DJ4zihJJ.js +0 -2
  94. package/dashboard/dist/assets/LocalOnlyNotice-DTrrUVbZ.js +0 -1
  95. package/dashboard/dist/assets/LoginPage-BOM9Q2sI.js +0 -1
  96. package/dashboard/dist/assets/PetPage-BURk7RG-.js +0 -1
  97. package/dashboard/dist/assets/PopoverPopup-BV_aoNBd.js +0 -1
  98. package/dashboard/dist/assets/ResetPasswordPage-SBL2LvXC.js +0 -1
  99. package/dashboard/dist/assets/Select-BsRdHde3.js +0 -1
  100. package/dashboard/dist/assets/SelectItemText-DgkrZxIq.js +0 -1
  101. package/dashboard/dist/assets/SettingsPage-DqxVsfnB.js +0 -1
  102. package/dashboard/dist/assets/SkillsPage-B0VfWkOh.js +0 -1
  103. package/dashboard/dist/assets/WidgetsPage-4QQ8wI_g.js +0 -1
  104. package/dashboard/dist/assets/WrappedPage-BK53khVY.js +0 -1
  105. package/dashboard/dist/assets/agent-logos-DXpW_Wmz.js +0 -1
  106. package/dashboard/dist/assets/arrow-up-right-DCc--V5G.js +0 -1
  107. package/dashboard/dist/assets/check-DqxuosV2.js +0 -1
  108. package/dashboard/dist/assets/copy-qChAT7MY.js +0 -1
  109. package/dashboard/dist/assets/download-W9R3sBph.js +0 -1
  110. package/dashboard/dist/assets/flame-0eyhMnfU.js +0 -1
  111. package/dashboard/dist/assets/format-tokens-CkfBUcoa.js +0 -1
  112. package/dashboard/dist/assets/info-DY1O85-U.js +0 -1
  113. package/dashboard/dist/assets/link-2-B_Z66hFT.js +0 -1
  114. package/dashboard/dist/assets/loader-circle-xH40lon2.js +0 -1
  115. package/dashboard/dist/assets/main-CeaB5N36.js +0 -1259
  116. package/dashboard/dist/assets/main-DAc77vwF.css +0 -1
  117. package/dashboard/dist/assets/provider-display-C_XKZQcF.js +0 -42
  118. package/dashboard/dist/assets/trash-2-DVhNJ74r.js +0 -1
  119. package/dashboard/dist/assets/use-community-stats-CO-nr2Vf.js +0 -1
  120. package/dashboard/dist/assets/use-limits-display-prefs-RRqmyoxt.js +0 -1
  121. package/dashboard/dist/assets/use-native-settings-79piPeET.js +0 -1
  122. package/dashboard/dist/assets/use-quality-per-dollar-pref-DyF49QRF.js +0 -1
  123. package/dashboard/dist/assets/use-transform-BvXyGAtb.js +0 -1
  124. package/dashboard/dist/assets/use-usage-limits-BiZ_I-KE.js +0 -1
  125. package/dashboard/dist/assets/useCurrency-D-C1e7eE.js +0 -1
  126. package/dashboard/dist/assets/useScrollLock-BfU2EMvG.js +0 -1
  127. package/dashboard/dist/assets/useTokenFormat-Cm4EvrQp.js +0 -1
  128. package/dashboard/dist/assets/zap-DlAOTwFs.js +0 -1
@@ -106,6 +106,11 @@ const {
106
106
  parseCursorCsv,
107
107
  } = require("../lib/cursor-config");
108
108
  const { purgeProjectUsage } = require("../lib/project-usage-purge");
109
+ const {
110
+ isCodexSessionCursorPath,
111
+ isCursorStoreRetry,
112
+ openCursorStore,
113
+ } = require("../lib/cursor-store");
109
114
  const { resolveTrackerPaths } = require("../lib/tracker-paths");
110
115
  const { resolveRuntimeConfig } = require("../lib/runtime-config");
111
116
 
@@ -186,6 +191,8 @@ const CODEX_RESCAN_DEDUP_REPAIR_KEY = "codexRescanDedupRepair_2026_06";
186
191
  const CODEX_FORK_REPLAY_REPAIR_KEY = "codexForkReplayRepair_2026_07";
187
192
  const DROID_DUP_SESSION_REPAIR_KEY = "droidDupSessionInflationRepair_2026_06";
188
193
  const CODEX_COLD_SCAN_AUDIT_INTERVAL_MS = 24 * 60 * 60 * 1000;
194
+ const NOTIFY_LOCK_WAIT_MS = 130_000;
195
+ const NOTIFY_LOCK_POLL_MS = 5_000;
189
196
  const CODEX_COLD_SCAN_AUDIT_MAX_SYNCS = 288;
190
197
  // 0.57.0 mis-attributed mimocode's mirrored Claude/claude-mem history to
191
198
  // source=mimo (read the whole DB instead of only providerID=mimo rows). This
@@ -285,9 +292,51 @@ function mergeCopilotAppDbStates(primary = {}, alias = {}) {
285
292
  return { ...primary, ...newer, sessionTotals };
286
293
  }
287
294
 
295
+ function sleep(ms) {
296
+ return new Promise((resolve) => setTimeout(resolve, ms));
297
+ }
298
+
299
+ async function acquireSyncLock(
300
+ lockPath,
301
+ opts,
302
+ {
303
+ notifyWaitMs = NOTIFY_LOCK_WAIT_MS,
304
+ notifyPollMs = NOTIFY_LOCK_POLL_MS,
305
+ } = {},
306
+ ) {
307
+ let lock = await openLock(lockPath, { quietIfLocked: opts.auto });
308
+ if (lock || !opts.fromNotify || notifyWaitMs <= 0) return lock;
309
+
310
+ // One low-frequency waiter coalesces every notify that lands behind an
311
+ // active native/background sync. This avoids both losing the final Codex
312
+ // turn and spawning a fleet of idle retry processes during a long scan.
313
+ const waiter = await openLock(`${lockPath}.notify-wait`, {
314
+ quietIfLocked: true,
315
+ });
316
+ if (!waiter) return null;
317
+
318
+ try {
319
+ const deadline = Date.now() + notifyWaitMs;
320
+ while (!lock && Date.now() < deadline) {
321
+ const remaining = deadline - Date.now();
322
+ await sleep(Math.min(Math.max(1, notifyPollMs), remaining));
323
+ lock = await openLock(lockPath, { quietIfLocked: true });
324
+ }
325
+ return lock;
326
+ } finally {
327
+ await waiter.release();
328
+ }
329
+ }
330
+
288
331
  async function cmdSync(argv, context = {}) {
289
332
  const opts = parseArgs(argv);
290
333
  const diagnostics = context && typeof context === "object" ? context.diagnostics : null;
334
+ const cursorStoreOptions = context && typeof context === "object"
335
+ ? context.cursorStoreOptions
336
+ : null;
337
+ const lockWaitOptions = context && typeof context === "object"
338
+ ? context.lockWaitOptions
339
+ : undefined;
291
340
  const syncDiagnostics = diagnostics && typeof diagnostics === "object" ? diagnostics : null;
292
341
  const home = os.homedir();
293
342
  const { trackerDir } = await resolveTrackerPaths({ home });
@@ -298,7 +347,7 @@ async function cmdSync(argv, context = {}) {
298
347
  }
299
348
 
300
349
  const lockPath = path.join(trackerDir, "sync.lock");
301
- const lock = await openLock(lockPath, { quietIfLocked: opts.auto });
350
+ const lock = await acquireSyncLock(lockPath, opts, lockWaitOptions);
302
351
  if (!lock) return;
303
352
 
304
353
  let progress = null;
@@ -314,8 +363,24 @@ async function cmdSync(argv, context = {}) {
314
363
  const grokSignalPath = path.join(trackerDir, "grok-last-session.json");
315
364
  const legacyGrokSignalPath = path.join(trackerDir, "tracker", "grok-last-session.json");
316
365
 
366
+ // Native publication owns backlog and failure-backoff retries on its next
367
+ // five-minute tick. Remove any legacy detached retry marker immediately so
368
+ // an already-sleeping retry process observes the missing marker and exits.
369
+ if (opts.publishAccount) {
370
+ await clearAutoRetry(trackerDir);
371
+ }
372
+
317
373
  const config = await readJson(configPath);
318
- const cursors = (await readJson(cursorsPath)) || { version: 1, files: {}, updatedAt: null };
374
+ const codexCursorRoots = [process.env.CODEX_HOME || path.join(home, ".codex")];
375
+ const cursorStore = await openCursorStore({
376
+ trackerDir,
377
+ cursorsPath,
378
+ codexRoots: codexCursorRoots,
379
+ ...(cursorStoreOptions && typeof cursorStoreOptions === "object"
380
+ ? cursorStoreOptions
381
+ : {}),
382
+ });
383
+ const cursors = cursorStore.cursors;
319
384
  const uploadThrottle = normalizeUploadState(await readJson(uploadThrottlePath));
320
385
  let uploadThrottleState = uploadThrottle;
321
386
  let grokHookSignal = null;
@@ -418,6 +483,7 @@ async function cmdSync(argv, context = {}) {
418
483
  }
419
484
 
420
485
  if (isFullSourceScan) {
486
+ await cursorStore.materializeAllCodexState(cursors);
421
487
  await migrateRolloutCumulativeDeltaBuckets({ cursors, queuePath, rolloutFiles });
422
488
  const codexRescanRepairRan = await repairCodexRescanInflation({
423
489
  cursors,
@@ -446,20 +512,29 @@ async function cmdSync(argv, context = {}) {
446
512
  });
447
513
  }
448
514
 
449
- const codexColdSkipEnabled = opts.auto && (isFullSourceScan || isBackgroundLightweightSync) && sourceAllowed("codex");
515
+ const codexColdSkipEnabled = opts.auto && sourceAllowed("codex");
450
516
  const codexColdAuditDue = codexColdSkipEnabled
451
517
  ? isCodexColdScanAuditDue(cursors)
452
518
  : false;
453
- const codexColdFilter = codexColdSkipEnabled
519
+ let codexColdFilter = codexColdSkipEnabled
454
520
  ? await filterColdCodexRolloutFiles({
455
521
  rolloutFiles,
456
522
  cursors,
523
+ codexCursorStore: cursorStore,
457
524
  projectEnabled: true,
458
525
  auditDue: codexColdAuditDue,
459
526
  diagnostics: syncDiagnostics,
460
527
  })
461
528
  : { rolloutFiles, skipped: 0 };
462
529
  const rolloutFilesForParse = codexColdFilter.rolloutFiles;
530
+ let codexCursorLoadRestarted = false;
531
+ if (!codexColdSkipEnabled && sourceAllowed("codex")) {
532
+ const loadResult = await cursorStore.loadCodexFilesForPaths(
533
+ rolloutFilesForParse,
534
+ cursors,
535
+ );
536
+ codexCursorLoadRestarted = Boolean(loadResult?.restarted);
537
+ }
463
538
 
464
539
  const openclawFiles = openclawSignal?.sessionFile
465
540
  ? [{ path: openclawSignal.sessionFile, source: "openclaw" }]
@@ -473,30 +548,58 @@ async function cmdSync(argv, context = {}) {
473
548
 
474
549
  let parseResult = { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
475
550
  let codexParseSucceeded = false;
551
+ let codexFallbackRetryRan = Boolean(
552
+ codexColdFilter.restarted || codexCursorLoadRestarted,
553
+ );
554
+ const runCodexParse = (files) => parseRolloutIncremental({
555
+ rolloutFiles: files,
556
+ cursors,
557
+ codexEventStore: Array.isArray(cursors.codexHashes)
558
+ ? null
559
+ : cursorStore.codexEventStore,
560
+ queuePath,
561
+ projectQueuePath,
562
+ diagnostics: syncDiagnostics,
563
+ onProgress: (p) => {
564
+ if (!progress?.enabled) return;
565
+ const pct = p.total > 0 ? p.index / p.total : 1;
566
+ progress.update(
567
+ `Parsing ${renderBar(pct)} ${formatNumber(p.index)}/${formatNumber(p.total)} files | buckets ${formatNumber(
568
+ p.bucketsQueued,
569
+ )}`,
570
+ );
571
+ },
572
+ });
476
573
  try {
477
- parseResult = await parseRolloutIncremental({
478
- rolloutFiles: rolloutFilesForParse,
479
- cursors,
480
- queuePath,
481
- projectQueuePath,
482
- diagnostics: syncDiagnostics,
483
- onProgress: (p) => {
484
- if (!progress?.enabled) return;
485
- const pct = p.total > 0 ? p.index / p.total : 1;
486
- progress.update(
487
- `Parsing ${renderBar(pct)} ${formatNumber(p.index)}/${formatNumber(p.total)} files | buckets ${formatNumber(
488
- p.bucketsQueued,
489
- )}`,
490
- );
491
- },
492
- });
574
+ parseResult = await runCodexParse(rolloutFilesForParse);
493
575
  codexParseSucceeded = true;
494
576
  } catch (err) {
495
- warnProviderParseFailure("Codex", err, opts);
577
+ if (isCursorStoreRetry(err)) {
578
+ await cursorStore.loadCodexFilesForPaths(rolloutFiles, cursors);
579
+ codexColdFilter = { rolloutFiles, skipped: 0, restarted: true };
580
+ codexFallbackRetryRan = true;
581
+ if (syncDiagnostics) {
582
+ syncDiagnostics.cold_skipped = 0;
583
+ syncDiagnostics.parse_candidates = rolloutFiles.reduce(
584
+ (count, entry) => count + (
585
+ typeof entry === "string" || !entry?.source || entry.source === "codex"
586
+ ? 1
587
+ : 0
588
+ ),
589
+ 0,
590
+ );
591
+ }
592
+ parseResult = await runCodexParse(rolloutFiles);
593
+ codexParseSucceeded = true;
594
+ } else if (err?.code === "TOKENTRACKER_CURSOR_STORE_CORRUPT") {
595
+ throw err;
596
+ } else {
597
+ warnProviderParseFailure("Codex", err, opts);
598
+ }
496
599
  }
497
600
  if (codexColdSkipEnabled && codexParseSucceeded) {
498
601
  recordCodexColdScanAudit(cursors, {
499
- fullAudit: codexColdAuditDue,
602
+ fullAudit: codexColdAuditDue || codexFallbackRetryRan,
500
603
  skipped: codexColdFilter.skipped,
501
604
  });
502
605
  }
@@ -1851,12 +1954,12 @@ async function cmdSync(argv, context = {}) {
1851
1954
  }
1852
1955
 
1853
1956
  cursors.updatedAt = new Date().toISOString();
1854
- await writeJson(cursorsPath, cursors);
1957
+ await cursorStore.commit(cursors);
1855
1958
  if (syncDiagnostics) {
1856
- const cursorStat = await fs.stat(cursorsPath);
1959
+ const cursorStat = await fs.stat(cursorStore.currentCorePath);
1857
1960
  syncDiagnostics.cursor_commits = Number(syncDiagnostics.cursor_commits || 0) + 1;
1858
1961
  syncDiagnostics.cursor_bytes = Number(syncDiagnostics.cursor_bytes || 0) + cursorStat.size;
1859
- syncDiagnostics.cursor_path = cursorsPath;
1962
+ syncDiagnostics.cursor_path = cursorStore.currentCorePath;
1860
1963
  }
1861
1964
  if (grokHookSignalConsumed && grokHookSignalPath) {
1862
1965
  await fs.unlink(grokHookSignalPath).catch(() => {});
@@ -1868,8 +1971,37 @@ async function cmdSync(argv, context = {}) {
1868
1971
 
1869
1972
  let uploadResult = { inserted: 0, skipped: 0 };
1870
1973
  let uploadAttempted = false;
1974
+ let backgroundUploadDecision = null;
1975
+
1976
+ if (opts.publishAccount) {
1977
+ const uploadStateBefore = (await readJson(queueStatePath)) || { offset: 0 };
1978
+ const queueSizeBefore = await safeStatSize(queuePath);
1979
+ const pendingBytesBefore = Math.max(
1980
+ 0,
1981
+ queueSizeBefore - Number(uploadStateBefore.offset || 0),
1982
+ );
1983
+ // The native five-minute loop is already the success cadence. Reuse the
1984
+ // shared decision helper for pending/backoff handling, but intentionally
1985
+ // ignore the legacy 30-minute success throttle so account publication
1986
+ // does not become stale again. Failure backoff remains authoritative.
1987
+ backgroundUploadDecision = decideAutoUpload({
1988
+ nowMs: Date.now(),
1989
+ pendingBytes: pendingBytesBefore,
1990
+ state: {
1991
+ ...uploadThrottleState,
1992
+ nextAllowedAtMs: Number(uploadThrottleState.backoffUntilMs || 0),
1993
+ },
1994
+ config: {
1995
+ batchSize: 200,
1996
+ maxBatchesSmall: 5,
1997
+ maxBatchesLarge: 5,
1998
+ },
1999
+ });
2000
+ }
1871
2001
 
1872
- if (runtime.deviceToken && runtime.baseUrl && !isBackgroundLightweightSync) {
2002
+ if (runtime.deviceToken && runtime.baseUrl &&
2003
+ (!isBackgroundLightweightSync || opts.publishAccount) &&
2004
+ (!backgroundUploadDecision || backgroundUploadDecision.allowed)) {
1873
2005
  uploadAttempted = true;
1874
2006
  // Mirror the machine identity into the purge-surviving seed file so a
1875
2007
  // future `uninstall --purge` + reinstall recovers the same cloud device
@@ -1886,8 +2018,8 @@ async function cmdSync(argv, context = {}) {
1886
2018
  deviceToken: runtime.deviceToken,
1887
2019
  queuePath,
1888
2020
  queueStatePath,
1889
- maxBatches: opts.drain ? 100 : 5,
1890
- batchSize: 200,
2021
+ maxBatches: opts.drain ? 100 : (backgroundUploadDecision?.maxBatches || 5),
2022
+ batchSize: backgroundUploadDecision?.batchSize || 200,
1891
2023
  });
1892
2024
  // Record success so the exponential backoff step resets — otherwise
1893
2025
  // a single past failure keeps us pessimistically throttled forever.
@@ -1926,7 +2058,7 @@ async function cmdSync(argv, context = {}) {
1926
2058
 
1927
2059
  if (pendingBytes <= 0) {
1928
2060
  await clearAutoRetry(trackerDir);
1929
- } else if (opts.auto && uploadAttempted) {
2061
+ } else if (opts.auto && uploadAttempted && !opts.publishAccount) {
1930
2062
  const retryAtMs = Number(uploadThrottleState?.nextAllowedAtMs || 0);
1931
2063
  if (retryAtMs > Date.now()) {
1932
2064
  await scheduleAutoRetry({
@@ -2027,7 +2159,6 @@ async function cmdSync(argv, context = {}) {
2027
2159
  } finally {
2028
2160
  progress?.stop();
2029
2161
  await lock.release();
2030
- await fs.unlink(lockPath).catch(() => {});
2031
2162
  }
2032
2163
  }
2033
2164
 
@@ -2040,6 +2171,7 @@ function parseArgs(argv) {
2040
2171
  source: null,
2041
2172
  drain: false,
2042
2173
  background: false,
2174
+ publishAccount: false,
2043
2175
  allLocalSources: false,
2044
2176
  repairGrok: false,
2045
2177
  };
@@ -2056,6 +2188,7 @@ function parseArgs(argv) {
2056
2188
  else if (a.startsWith("--source=")) out.source = normalizeSyncSource(a.slice("--source=".length));
2057
2189
  else if (a === "--drain") out.drain = true;
2058
2190
  else if (a === "--background" || a === "--lightweight") out.background = true;
2191
+ else if (a === "--publish-account") out.publishAccount = true;
2059
2192
  else if (a === "--all-local-sources") out.allLocalSources = true;
2060
2193
  else if (a === "--repair-grok") out.repairGrok = true;
2061
2194
  else throw new Error(`Unknown option: ${a}`);
@@ -2129,6 +2262,7 @@ function recordCodexColdScanAudit(cursors, { fullAudit = false, skipped = 0 } =
2129
2262
 
2130
2263
  module.exports = {
2131
2264
  cmdSync,
2265
+ acquireSyncLock,
2132
2266
  migrateCursorUnknownBuckets,
2133
2267
  migrateRolloutCumulativeDeltaBuckets,
2134
2268
  repairCodexRescanInflation,
@@ -3655,12 +3789,6 @@ async function scanForForkedCodexRollout(rolloutFiles) {
3655
3789
  return { forked: false, indeterminate };
3656
3790
  }
3657
3791
 
3658
- function isCodexSessionCursorPath(filePath) {
3659
- if (typeof filePath !== "string") return false;
3660
- const normalized = filePath.replace(/\\/g, "/");
3661
- return /\/\.codex\/(?:archived_)?sessions\//.test(normalized);
3662
- }
3663
-
3664
3792
  async function projectUsageKeysFromQueuePath(queuePath, source) {
3665
3793
  if (typeof queuePath !== "string" || !queuePath) return [];
3666
3794
  let raw = "";