@ipv9/tokentracker-cli 0.39.46 → 0.39.47

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 (26) hide show
  1. package/dashboard/dist/assets/{Card-Bk5BMQ2P.js → Card-BMWFZ5k5.js} +1 -1
  2. package/dashboard/dist/assets/{DashboardPage-DPP44kwE.js → DashboardPage-HjWNRKq7.js} +1 -1
  3. package/dashboard/dist/assets/{FadeIn-CWvnEI0O.js → FadeIn-BgLVBi0I.js} +1 -1
  4. package/dashboard/dist/assets/{IpCheckPage-BmSn2zKd.js → IpCheckPage-CsS9rEbM.js} +1 -1
  5. package/dashboard/dist/assets/{LimitsPage-CDCccsH_.js → LimitsPage-BifpRDJ6.js} +1 -1
  6. package/dashboard/dist/assets/{LocalOnlyNotice-D19MhLRF.js → LocalOnlyNotice-D7tdrheS.js} +1 -1
  7. package/dashboard/dist/assets/{PopoverPopup-D78fYtAs.js → PopoverPopup-DjMB9uLV.js} +1 -1
  8. package/dashboard/dist/assets/{Select-CKV1Rvl0.js → Select-DeCgT4YH.js} +1 -1
  9. package/dashboard/dist/assets/{SelectItemText-5eDkx616.js → SelectItemText-DwZRVpPE.js} +1 -1
  10. package/dashboard/dist/assets/{SettingsPage-DIwe50pL.js → SettingsPage-B1l6jasL.js} +1 -1
  11. package/dashboard/dist/assets/{SkillsPage-zUzRrWak.js → SkillsPage-DP5bdeUE.js} +1 -1
  12. package/dashboard/dist/assets/{WidgetsPage-3yOTNqKD.js → WidgetsPage-BVOQlozx.js} +1 -1
  13. package/dashboard/dist/assets/{WrappedPage-DPAnbLdi.js → WrappedPage-ixZYG73C.js} +1 -1
  14. package/dashboard/dist/assets/{arrow-up-right-BGg2lx0L.js → arrow-up-right-D74FAyJC.js} +1 -1
  15. package/dashboard/dist/assets/{download-BU4HPPvG.js → download-sKFNy5AQ.js} +1 -1
  16. package/dashboard/dist/assets/{format-vw28c71d.js → format-Bl1W1y2T.js} +1 -1
  17. package/dashboard/dist/assets/{limitDisplay-CY5Qlw5s.js → limitDisplay-C2rw2OOb.js} +1 -1
  18. package/dashboard/dist/assets/{main-D9Jo7Pt-.js → main-NjvOmlYA.js} +3 -3
  19. package/dashboard/dist/assets/{mock-data-B0oPPFnn.js → mock-data-DJ2XGAUP.js} +1 -1
  20. package/dashboard/dist/assets/{use-limits-display-prefs-BAGMxrq7.js → use-limits-display-prefs-C7BBDoNV.js} +1 -1
  21. package/dashboard/dist/assets/{use-native-settings-C4lSH6qA.js → use-native-settings-BYIme-b6.js} +1 -1
  22. package/dashboard/dist/assets/{useCurrency-Dvz0184_.js → useCurrency-CJzAqnSI.js} +1 -1
  23. package/dashboard/dist/index.html +1 -1
  24. package/package.json +1 -1
  25. package/src/lib/pricing/seed-snapshot.json +1 -1
  26. package/src/lib/rollout.js +48 -7
@@ -3237,21 +3237,48 @@ function isUncPath(p) {
3237
3237
  return typeof p === "string" && (p.startsWith("\\\\") || p.startsWith("//"));
3238
3238
  }
3239
3239
 
3240
+ function sqliteDbChangeFingerprint(dbPath) {
3241
+ const parts = [];
3242
+ for (const suffix of ["", "-wal", "-shm", "-journal"]) {
3243
+ const filePath = dbPath + suffix;
3244
+ const label = suffix || "main";
3245
+ try {
3246
+ const stat = fssync.statSync(filePath, { bigint: true });
3247
+ parts.push(`${label}:${stat.size}:${stat.mtimeNs}`);
3248
+ } catch (error) {
3249
+ if (error && error.code === "ENOENT") {
3250
+ parts.push(`${label}:missing`);
3251
+ continue;
3252
+ }
3253
+ // A partial fingerprint must never authorize the no-change fast path.
3254
+ // Retry the read instead of treating repeated stat failures as stable.
3255
+ return null;
3256
+ }
3257
+ }
3258
+ return parts.join("|");
3259
+ }
3260
+
3240
3261
  function snapshotSqliteDb(dbPath) {
3241
3262
  const tmpRoot = fssync.mkdtempSync(
3242
3263
  path.join(require("node:os").tmpdir(), "tokentracker-hermes-snap-"),
3243
3264
  );
3244
3265
  const target = path.join(tmpRoot, path.basename(dbPath));
3245
3266
  fssync.copyFileSync(dbPath, target);
3246
- // Best-effort copy of SQLite sidecars; missing -wal/-shm/-journal is fine.
3267
+ // Copy SQLite sidecars as one logical snapshot. A missing sidecar is normal,
3268
+ // but any other copy failure makes the snapshot incomplete and must prevent
3269
+ // callers from advancing a change fingerprint based on stale main-DB data.
3270
+ let sidecarsComplete = true;
3247
3271
  for (const suffix of ["-wal", "-shm", "-journal"]) {
3248
3272
  const src = dbPath + suffix;
3249
3273
  try {
3250
- if (fssync.existsSync(src)) fssync.copyFileSync(src, target + suffix);
3251
- } catch (_e) { }
3274
+ fssync.copyFileSync(src, target + suffix);
3275
+ } catch (error) {
3276
+ if (!error || error.code !== "ENOENT") sidecarsComplete = false;
3277
+ }
3252
3278
  }
3253
3279
  return {
3254
3280
  path: target,
3281
+ sidecarsComplete,
3255
3282
  cleanup() {
3256
3283
  try { fssync.rmSync(tmpRoot, { recursive: true, force: true }); } catch (_e) { }
3257
3284
  },
@@ -6128,7 +6155,8 @@ async function parseGooseIncremental({
6128
6155
  ? { ...gooseState.sessionTotals }
6129
6156
  : {};
6130
6157
 
6131
- const cursorDbMtime = Number.isFinite(gooseState.lastDbMtimeMs) ? gooseState.lastDbMtimeMs : 0;
6158
+ const cursorDbFingerprint =
6159
+ typeof gooseState.lastDbFingerprint === "string" ? gooseState.lastDbFingerprint : null;
6132
6160
  let currentMtime = 0;
6133
6161
  try {
6134
6162
  currentMtime = fssync.statSync(resolvedDb).mtimeMs;
@@ -6139,9 +6167,12 @@ async function parseGooseIncremental({
6139
6167
  }
6140
6168
  throw e;
6141
6169
  }
6142
- // mtime short-circuit: skip the full sessions table scan when the DB
6143
- // hasn't been touched since the last sync.
6144
- if (currentMtime > 0 && currentMtime === cursorDbMtime) {
6170
+ const currentDbFingerprint = sqliteDbChangeFingerprint(resolvedDb);
6171
+ // Goose commonly commits active-session writes only to SQLite's WAL. Skip
6172
+ // the table scan only when the main DB and every sidecar are unchanged.
6173
+ // Legacy cursors without a fingerprint intentionally perform one read to
6174
+ // establish the sidecar-aware baseline.
6175
+ if (cursorDbFingerprint && currentDbFingerprint === cursorDbFingerprint) {
6145
6176
  cursors.goose = { ...gooseState, sessionTotals, updatedAt: observedAt };
6146
6177
  return { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
6147
6178
  }
@@ -6151,6 +6182,15 @@ async function parseGooseIncremental({
6151
6182
  const snap = snapshotSqliteDb(resolvedDb);
6152
6183
  let rows = [];
6153
6184
  try {
6185
+ if (!snap.sidecarsComplete) {
6186
+ cursors.goose = {
6187
+ ...gooseState,
6188
+ sessionTotals,
6189
+ lastDbFingerprint: null,
6190
+ updatedAt: observedAt,
6191
+ };
6192
+ return { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
6193
+ }
6154
6194
  rows = readGooseSessionsFromSqlite(snap.path, sqliteOptions);
6155
6195
  } finally {
6156
6196
  snap.cleanup();
@@ -6271,6 +6311,7 @@ async function parseGooseIncremental({
6271
6311
  ...gooseState,
6272
6312
  sessionTotals,
6273
6313
  lastDbMtimeMs: currentMtime,
6314
+ lastDbFingerprint: currentDbFingerprint,
6274
6315
  updatedAt: observedAt,
6275
6316
  };
6276
6317