tokentracker-cli 0.79.9 → 0.80.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dashboard/dist/assets/{AchievementBadge-B1WOPlu0.js → AchievementBadge-D617bvS9.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-9kHEQicS.js → AchievementsPage-mzE76Ah9.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-Dw05PYPr.js → AchievementsSection-8CYGBoPJ.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-DaWgiijJ.js → BadgeDetailModal-DM3veVmW.js} +1 -1
- package/dashboard/dist/assets/{Card-DYhrLLM4.js → Card-DWONuizy.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-BLy2LUKz.js → ClawdAnimated-CGQXyWuH.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-CWYqfht3.js → CommunityStatsModal-B5yDmJ0o.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-DcE2C24Y.js → ConfirmModal-d4wpsMPW.js} +1 -1
- package/dashboard/dist/assets/{Controls-CI7qw0-b.js → Controls-CMSqknaB.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-CAlgaBa7.js → DashboardPage-Cw6lmVSg.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-DinOyaeN.js → DevicePage-CsPSjSLH.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-CjfSDrLo.js → DialogClose-Cbs-z783.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-B0fmBJxQ.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-f7gNmCca.js → DialogTitle-C4mJiIQW.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-D_5w0Qh0.js → FadeIn-xOJwCmZ8.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-DTZyNKyL.js → HeaderGithubStar-oRz4rfC8.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-D8CViF98.js → IpCheckPage-CTwL-Zmz.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-CqN5KEjV.js → LandingPage-CqYJ3sZh.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardAvatar-CwUVO4wn.js → LeaderboardAvatar-CuF6Kzkj.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-B12HJ9LA.js → LeaderboardPage-BvOPIj-b.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-DFzPpsnV.js → LeaderboardProfileModal-DVXuKkRN.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-Z0ZYpNFd.js → LeaderboardProfilePage-RE1CPqzM.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-BpwioIa-.js → LimitsPage-BovkRgac.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-Covi6zdt.js → LocalOnlyNotice-CTFFU_ip.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-CRqbhwCx.js → LoginPage-mguBCTBZ.js} +1 -1
- package/dashboard/dist/assets/{PetPage-BY_yObVj.js → PetPage-C5KLH6Fm.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-B2dE3Ary.js → PopoverPopup-CLDh5yIk.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-C8IEJbVc.js → ResetPasswordPage-DnhgHQrV.js} +1 -1
- package/dashboard/dist/assets/{Select-BmRLYSqA.js → Select-DPSaPIlz.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-FC46bk8b.js → SelectItemText-jIBbXpqi.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-DipzOQVg.js → SettingsPage-BK0IqSjg.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-CziypUTf.js → SkillsPage-BQNoMpL2.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-CJegJhVR.js → WidgetsPage-308d8icu.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-DcMiKvzf.js → WrappedPage-T720sQ9S.js} +1 -1
- package/dashboard/dist/assets/{agent-logos-S7bF1LwW.js → agent-logos-Blo5C96d.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-D0yeL56y.js → arrow-up-right-DKz2Ua6U.js} +1 -1
- package/dashboard/dist/assets/check-fF6lQGN-.js +1 -0
- package/dashboard/dist/assets/{copy-D-z9ZtIu.js → copy-DcUFG0PD.js} +1 -1
- package/dashboard/dist/assets/{download-Dwxnd3QG.js → download-CNK1wtxV.js} +1 -1
- package/dashboard/dist/assets/{flame-Bdqk9A75.js → flame-BEhJZRaZ.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-DNVcCRwm.js → format-tokens-Bsfbd8nj.js} +1 -1
- package/dashboard/dist/assets/{info-CMLT6oUI.js → info-DRJLv-aV.js} +1 -1
- package/dashboard/dist/assets/{link-2-BmiHu726.js → link-2-C0GdaANY.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-CXVUQtzm.js → loader-circle-F9ymYeor.js} +1 -1
- package/dashboard/dist/assets/{main-B9m79io-.js → main-CThKxvKx.js} +3 -3
- package/dashboard/dist/assets/{provider-display-BoAesdke.js → provider-display-D4NdFjHN.js} +1 -1
- package/dashboard/dist/assets/{trash-2-C9Rh2DAL.js → trash-2-D9vulGMW.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-aZwYxrXR.js → use-community-stats-BU4dXEOM.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-tJKR83RT.js → use-limits-display-prefs-Bx2VJhwl.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-PjfJPK5v.js → use-native-settings-BSXaxaTC.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-D1QFmZUY.js → use-ordered-list-OA0HwwEL.js} +1 -1
- package/dashboard/dist/assets/{use-quality-per-dollar-pref-4N3Su4wC.js → use-quality-per-dollar-pref-Bqooc4FA.js} +1 -1
- package/dashboard/dist/assets/{use-transform-D9nurInL.js → use-transform-CZ9FdRGw.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-DapTpi_6.js → use-usage-limits-Ct_A4wTr.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-B0DH9ZZA.js → useCurrency-D4bvdaFB.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-DijV6DYS.js → useScrollLock-ZsWO-GoL.js} +1 -1
- package/dashboard/dist/assets/useTokenFormat-B3huMEws.js +1 -0
- package/dashboard/dist/assets/{zap-BVfhR3Ck.js → zap-C9Mp-GNU.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +1 -1
- package/src/commands/init.js +607 -6
- package/src/commands/sync.js +176 -7
- package/src/lib/codex-config.js +135 -40
- package/src/lib/codex-context-breakdown.js +123 -43
- package/src/lib/codex-rollout-parser.js +329 -32
- package/src/lib/local-api.js +11 -5
- package/src/lib/openclaw-session-plugin.js +13 -0
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +230 -10
- package/src/lib/usage-limits.js +9 -5
- package/dashboard/dist/assets/DialogDescription-DWs-KIUA.js +0 -1
- package/dashboard/dist/assets/check-Dn9BLYEf.js +0 -1
- package/dashboard/dist/assets/useTokenFormat-nxdG6gi9.js +0 -1
package/src/lib/rollout.js
CHANGED
|
@@ -172,6 +172,7 @@ async function parseRolloutIncremental({
|
|
|
172
172
|
onProgress,
|
|
173
173
|
source,
|
|
174
174
|
publicRepoResolver,
|
|
175
|
+
diagnostics,
|
|
175
176
|
}) {
|
|
176
177
|
await ensureDir(path.dirname(queuePath));
|
|
177
178
|
let filesProcessed = 0;
|
|
@@ -189,6 +190,7 @@ async function parseRolloutIncremental({
|
|
|
189
190
|
const projectFreshnessNowMs = Date.now();
|
|
190
191
|
const touchedBuckets = new Set();
|
|
191
192
|
const defaultSource = normalizeSourceInput(source) || DEFAULT_SOURCE;
|
|
193
|
+
const syncDiagnostics = diagnostics && typeof diagnostics === "object" ? diagnostics : null;
|
|
192
194
|
|
|
193
195
|
if (!cursors.files || typeof cursors.files !== "object") {
|
|
194
196
|
cursors.files = {};
|
|
@@ -199,7 +201,108 @@ async function parseRolloutIncremental({
|
|
|
199
201
|
// external rewrite of a session file (Codex-Manager's atomic provider-patch on
|
|
200
202
|
// account switch, issue #187) cannot re-count already-counted events.
|
|
201
203
|
const prevCodexHashes = Array.isArray(cursors?.codexHashes) ? cursors.codexHashes : [];
|
|
202
|
-
|
|
204
|
+
if (!Array.isArray(cursors.codexHashes)) cursors.codexHashes = prevCodexHashes;
|
|
205
|
+
let seenCodexEvents = null;
|
|
206
|
+
let newCodexEventKeys = null;
|
|
207
|
+
let newCodexEventKeySet = null;
|
|
208
|
+
let appendOnlyCodexEvents = null;
|
|
209
|
+
let historicalCodexEvents = null;
|
|
210
|
+
let cursorSessionPaths = null;
|
|
211
|
+
if (syncDiagnostics) {
|
|
212
|
+
const codexParseCandidates = (Array.isArray(rolloutFiles) ? rolloutFiles : []).reduce((count, entry) => {
|
|
213
|
+
const entrySource = typeof entry === "string"
|
|
214
|
+
? defaultSource
|
|
215
|
+
: normalizeSourceInput(entry?.source) || defaultSource;
|
|
216
|
+
return count + (entrySource === DEFAULT_SOURCE ? 1 : 0);
|
|
217
|
+
}, 0);
|
|
218
|
+
const discoveredRollouts = Number(syncDiagnostics.discovered_rollouts);
|
|
219
|
+
const cursorKeys = Number(syncDiagnostics.cursor_keys);
|
|
220
|
+
const coldSkipped = Number(syncDiagnostics.cold_skipped);
|
|
221
|
+
const parseCandidates = Number(syncDiagnostics.parse_candidates);
|
|
222
|
+
Object.assign(syncDiagnostics, {
|
|
223
|
+
discovered_rollouts: Number.isFinite(discoveredRollouts) ? discoveredRollouts : codexParseCandidates,
|
|
224
|
+
cursor_keys: Number.isFinite(cursorKeys) ? cursorKeys : Object.keys(cursors.files).length,
|
|
225
|
+
parse_candidates: Number.isFinite(parseCandidates) ? parseCandidates : codexParseCandidates,
|
|
226
|
+
stat_candidates: 0,
|
|
227
|
+
cold_skipped: Number.isFinite(coldSkipped) ? coldSkipped : 0,
|
|
228
|
+
content_files_read: 0,
|
|
229
|
+
hash_set_constructions: 0,
|
|
230
|
+
hash_array_materializations: 0,
|
|
231
|
+
hash_array_materialized_items: 0,
|
|
232
|
+
codex_hash_count: prevCodexHashes.length,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
const ensureNewCodexEventKeys = () => {
|
|
236
|
+
if (!newCodexEventKeys) newCodexEventKeys = [];
|
|
237
|
+
if (!newCodexEventKeySet) newCodexEventKeySet = new Set();
|
|
238
|
+
};
|
|
239
|
+
const recordNewCodexEvent = (key) => {
|
|
240
|
+
ensureNewCodexEventKeys();
|
|
241
|
+
if (newCodexEventKeySet.has(key)) return;
|
|
242
|
+
newCodexEventKeySet.add(key);
|
|
243
|
+
newCodexEventKeys.push(key);
|
|
244
|
+
if (seenCodexEvents) seenCodexEvents.add(key);
|
|
245
|
+
};
|
|
246
|
+
const getAppendOnlyCodexEvents = () => {
|
|
247
|
+
if (!appendOnlyCodexEvents) {
|
|
248
|
+
appendOnlyCodexEvents = {
|
|
249
|
+
has(key) {
|
|
250
|
+
return Boolean(
|
|
251
|
+
newCodexEventKeySet?.has(key) ||
|
|
252
|
+
seenCodexEvents?.has(key)
|
|
253
|
+
);
|
|
254
|
+
},
|
|
255
|
+
add: recordNewCodexEvent,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
return appendOnlyCodexEvents;
|
|
259
|
+
};
|
|
260
|
+
const getHistoricalCodexEvents = () => {
|
|
261
|
+
if (!seenCodexEvents) {
|
|
262
|
+
seenCodexEvents = new Set(prevCodexHashes);
|
|
263
|
+
for (const key of newCodexEventKeys || []) seenCodexEvents.add(key);
|
|
264
|
+
if (syncDiagnostics) syncDiagnostics.hash_set_constructions += 1;
|
|
265
|
+
}
|
|
266
|
+
if (!historicalCodexEvents) {
|
|
267
|
+
historicalCodexEvents = {
|
|
268
|
+
has: (key) => seenCodexEvents.has(key),
|
|
269
|
+
add: recordNewCodexEvent,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
return historicalCodexEvents;
|
|
273
|
+
};
|
|
274
|
+
const getCursorSessionPaths = () => {
|
|
275
|
+
if (cursorSessionPaths) return cursorSessionPaths;
|
|
276
|
+
cursorSessionPaths = new Map();
|
|
277
|
+
for (const existingPath of Object.keys(cursors.files)) {
|
|
278
|
+
const sessionId = codexSessionIdFromPath(existingPath);
|
|
279
|
+
if (!sessionId) continue;
|
|
280
|
+
if (!cursorSessionPaths.has(sessionId)) cursorSessionPaths.set(sessionId, new Set());
|
|
281
|
+
cursorSessionPaths.get(sessionId).add(existingPath);
|
|
282
|
+
}
|
|
283
|
+
return cursorSessionPaths;
|
|
284
|
+
};
|
|
285
|
+
const needsHistoricalCodexDedup = ({
|
|
286
|
+
filePath,
|
|
287
|
+
prev,
|
|
288
|
+
sameInode,
|
|
289
|
+
truncated,
|
|
290
|
+
startOffset,
|
|
291
|
+
rebuildingBaseline,
|
|
292
|
+
}) => {
|
|
293
|
+
if (rebuildingBaseline) return true;
|
|
294
|
+
if (prev && (!sameInode || truncated)) return true;
|
|
295
|
+
if (sameInode && !truncated && startOffset > 0) return false;
|
|
296
|
+
if (prev?.lastTotal) return true;
|
|
297
|
+
const sessionId = codexSessionIdFromPath(filePath);
|
|
298
|
+
if (!sessionId) return true;
|
|
299
|
+
const knownPaths = getCursorSessionPaths().get(sessionId);
|
|
300
|
+
if (!knownPaths) return false;
|
|
301
|
+
for (const knownPath of knownPaths) {
|
|
302
|
+
if (knownPath !== filePath) return true;
|
|
303
|
+
}
|
|
304
|
+
return false;
|
|
305
|
+
};
|
|
203
306
|
|
|
204
307
|
for (let idx = 0; idx < rolloutFiles.length; idx++) {
|
|
205
308
|
const entry = rolloutFiles[idx];
|
|
@@ -209,6 +312,7 @@ async function parseRolloutIncremental({
|
|
|
209
312
|
typeof entry === "string"
|
|
210
313
|
? defaultSource
|
|
211
314
|
: normalizeSourceInput(entry?.source) || defaultSource;
|
|
315
|
+
if (syncDiagnostics && fileSource === DEFAULT_SOURCE) syncDiagnostics.stat_candidates += 1;
|
|
212
316
|
const st = await fs.stat(filePath).catch(() => null);
|
|
213
317
|
if (!st || !st.isFile()) continue;
|
|
214
318
|
|
|
@@ -218,8 +322,18 @@ async function parseRolloutIncremental({
|
|
|
218
322
|
const sameInode = prev && prev.inode === inode;
|
|
219
323
|
const prevOffset = sameInode ? prev.offset || 0 : 0;
|
|
220
324
|
const truncated = sameInode && prevOffset > st.size;
|
|
221
|
-
const
|
|
222
|
-
|
|
325
|
+
const rebuildingCodexBaseline = Boolean(
|
|
326
|
+
fileSource === DEFAULT_SOURCE &&
|
|
327
|
+
sameInode &&
|
|
328
|
+
!truncated &&
|
|
329
|
+
prevOffset > 0 &&
|
|
330
|
+
prevOffset < st.size &&
|
|
331
|
+
(!prev.lastTotal || typeof prev.lastTotal !== "object")
|
|
332
|
+
);
|
|
333
|
+
const startOffset = sameInode && !truncated && !rebuildingCodexBaseline ? prevOffset : 0;
|
|
334
|
+
const lastTotal = sameInode && !truncated && !rebuildingCodexBaseline
|
|
335
|
+
? prev.lastTotal || null
|
|
336
|
+
: null;
|
|
223
337
|
const lastModel = sameInode && !truncated ? prev.lastModel || null : null;
|
|
224
338
|
|
|
225
339
|
const codexProjectFastPath = projectEnabled && fileSource === DEFAULT_SOURCE;
|
|
@@ -270,6 +384,21 @@ async function parseRolloutIncremental({
|
|
|
270
384
|
const projectRef = projectContext?.projectRef || null;
|
|
271
385
|
const projectKey = projectContext?.projectKey || null;
|
|
272
386
|
|
|
387
|
+
if (syncDiagnostics && !projectContextOnlyScan && fileSource === DEFAULT_SOURCE) {
|
|
388
|
+
syncDiagnostics.content_files_read += 1;
|
|
389
|
+
}
|
|
390
|
+
const codexEventTracker = fileSource === DEFAULT_SOURCE
|
|
391
|
+
? (needsHistoricalCodexDedup({
|
|
392
|
+
filePath,
|
|
393
|
+
prev,
|
|
394
|
+
sameInode,
|
|
395
|
+
truncated,
|
|
396
|
+
startOffset,
|
|
397
|
+
rebuildingBaseline: rebuildingCodexBaseline,
|
|
398
|
+
})
|
|
399
|
+
? getHistoricalCodexEvents
|
|
400
|
+
: getAppendOnlyCodexEvents)
|
|
401
|
+
: null;
|
|
273
402
|
const result = projectContextOnlyScan
|
|
274
403
|
? await scanRolloutProjectFileContexts({
|
|
275
404
|
filePath,
|
|
@@ -299,7 +428,7 @@ async function parseRolloutIncremental({
|
|
|
299
428
|
publicRepoCache,
|
|
300
429
|
publicRepoResolver,
|
|
301
430
|
projectContext,
|
|
302
|
-
seenCodexEvents,
|
|
431
|
+
seenCodexEvents: codexEventTracker,
|
|
303
432
|
sessionId: codexSessionIdFromPath(filePath),
|
|
304
433
|
});
|
|
305
434
|
|
|
@@ -341,7 +470,12 @@ async function parseRolloutIncremental({
|
|
|
341
470
|
const projectBucketsQueued = projectEnabled
|
|
342
471
|
? await enqueueTouchedProjectBuckets({ projectQueuePath, projectState, projectTouchedBuckets })
|
|
343
472
|
: 0;
|
|
344
|
-
|
|
473
|
+
for (const key of newCodexEventKeys || []) prevCodexHashes.push(key);
|
|
474
|
+
if (syncDiagnostics) {
|
|
475
|
+
syncDiagnostics.codex_hash_count = Array.isArray(cursors.codexHashes)
|
|
476
|
+
? cursors.codexHashes.length
|
|
477
|
+
: prevCodexHashes.length;
|
|
478
|
+
}
|
|
345
479
|
hourlyState.updatedAt = new Date().toISOString();
|
|
346
480
|
cursors.hourly = hourlyState;
|
|
347
481
|
if (projectState) {
|
|
@@ -359,8 +493,23 @@ async function filterColdCodexRolloutFiles({
|
|
|
359
493
|
auditDue = false,
|
|
360
494
|
nowMs = Date.now(),
|
|
361
495
|
recentDays = DEFAULT_CODEX_COLD_SKIP_RECENT_DAYS,
|
|
496
|
+
diagnostics = null,
|
|
362
497
|
} = {}) {
|
|
363
498
|
const files = Array.isArray(rolloutFiles) ? rolloutFiles : [];
|
|
499
|
+
const syncDiagnostics = diagnostics && typeof diagnostics === "object" ? diagnostics : null;
|
|
500
|
+
const isCodexEntry = (entry) => (
|
|
501
|
+
typeof entry === "string" || (normalizeSourceInput(entry?.source) || DEFAULT_SOURCE) === DEFAULT_SOURCE
|
|
502
|
+
);
|
|
503
|
+
if (syncDiagnostics) {
|
|
504
|
+
const discoveredRollouts = files.reduce(
|
|
505
|
+
(count, entry) => count + (isCodexEntry(entry) ? 1 : 0),
|
|
506
|
+
0,
|
|
507
|
+
);
|
|
508
|
+
syncDiagnostics.discovered_rollouts = discoveredRollouts;
|
|
509
|
+
syncDiagnostics.cursor_keys = Object.keys(cursors?.files || {}).length;
|
|
510
|
+
syncDiagnostics.cold_skipped = 0;
|
|
511
|
+
syncDiagnostics.parse_candidates = discoveredRollouts;
|
|
512
|
+
}
|
|
364
513
|
if (auditDue || !cursors?.files || typeof cursors.files !== "object") {
|
|
365
514
|
return { rolloutFiles: files, skipped: 0 };
|
|
366
515
|
}
|
|
@@ -414,6 +563,13 @@ async function filterColdCodexRolloutFiles({
|
|
|
414
563
|
skipped += 1;
|
|
415
564
|
}
|
|
416
565
|
|
|
566
|
+
if (syncDiagnostics) {
|
|
567
|
+
syncDiagnostics.cold_skipped = skipped;
|
|
568
|
+
syncDiagnostics.parse_candidates = out.reduce(
|
|
569
|
+
(count, entry) => count + (isCodexEntry(entry) ? 1 : 0),
|
|
570
|
+
0,
|
|
571
|
+
);
|
|
572
|
+
}
|
|
417
573
|
return { rolloutFiles: out, skipped };
|
|
418
574
|
}
|
|
419
575
|
|
|
@@ -1514,6 +1670,16 @@ async function parseRolloutFile({
|
|
|
1514
1670
|
let currentCwd = null;
|
|
1515
1671
|
let currentDate = null;
|
|
1516
1672
|
let isForkedRollout = false;
|
|
1673
|
+
// The replay prefix only ever exists at the head of a freshly-forked file. A
|
|
1674
|
+
// resumed scan (startOffset > 0) can never see it — and never re-reads the
|
|
1675
|
+
// session_meta line, so isForkedRollout stays false there anyway — but gate
|
|
1676
|
+
// on the offset explicitly so correctness does not hinge on that distant
|
|
1677
|
+
// invariant. Known accepted limitation: if a sync's read races the fork's
|
|
1678
|
+
// single-flush write and persists a cursor INSIDE the burst, the resumed
|
|
1679
|
+
// scan counts the remaining burst tail (bounded over-count, ms-wide window;
|
|
1680
|
+
// self-heals on any inode-changing rescan and never drops genuine usage).
|
|
1681
|
+
let replayPrefixActive = startOffset === 0;
|
|
1682
|
+
let prevForkedTokenMs = null;
|
|
1517
1683
|
const rolloutDate = rolloutDateFromPath(filePath);
|
|
1518
1684
|
let currentProjectRef = projectRef || null;
|
|
1519
1685
|
let currentProjectKey = projectKey || null;
|
|
@@ -1591,8 +1757,50 @@ async function parseRolloutFile({
|
|
|
1591
1757
|
totals = totalUsage;
|
|
1592
1758
|
}
|
|
1593
1759
|
|
|
1594
|
-
//
|
|
1595
|
-
|
|
1760
|
+
// Forked Codex rollouts replay the parent session's entire token history
|
|
1761
|
+
// into the child file the moment the fork is created. The date guard below
|
|
1762
|
+
// (current_date < rolloutDate) catches cross-day forks, but same-day forks
|
|
1763
|
+
// share the parent's current_date and slip through it. The replay is written
|
|
1764
|
+
// in a single flush, so its rows carry near-identical timestamps (sub-ms to a
|
|
1765
|
+
// few ms apart across observed 0.129–0.137 samples), while the first genuine
|
|
1766
|
+
// live turn lands seconds later (≥11s in every sampled fork). We therefore
|
|
1767
|
+
// skip the *leading* run of densely-spaced token_count rows and latch off
|
|
1768
|
+
// permanently at the first multi-second gap: a lone fast turn (always
|
|
1769
|
+
// preceded by a large gap) is never dropped, and an arbitrarily large replay
|
|
1770
|
+
// is fully skipped regardless of length. The threshold sits far above the
|
|
1771
|
+
// flush spacing and well below genuine turn cadence (≥~4.6s observed). The
|
|
1772
|
+
// first replayed row cannot be identified without lookahead, so it is still
|
|
1773
|
+
// counted (a bounded, <1% residual over-count); dropping real usage is the
|
|
1774
|
+
// worse failure, so we bias against it. `totals` is already advanced above,
|
|
1775
|
+
// keeping the cumulative-delta baseline correct for the live turns we keep.
|
|
1776
|
+
// Scoped to forked codex rollouts. (issue #169 follow-up.)
|
|
1777
|
+
let forkedReplaySkip = false;
|
|
1778
|
+
if (isForkedRollout && source === DEFAULT_SOURCE && replayPrefixActive) {
|
|
1779
|
+
const tokenMs = Date.parse(tokenTimestamp);
|
|
1780
|
+
// Fail open on anything the burst heuristic was not measured against:
|
|
1781
|
+
// an unparseable timestamp or a backwards clock step permanently ends
|
|
1782
|
+
// the prefix, so no later row can be skipped off a stale baseline.
|
|
1783
|
+
// Replay flushes are monotonic in every sampled rollout (2,116 gaps,
|
|
1784
|
+
// min 0ms), so this never fires on genuine replays.
|
|
1785
|
+
if (!Number.isFinite(tokenMs) || (prevForkedTokenMs !== null && tokenMs < prevForkedTokenMs)) {
|
|
1786
|
+
replayPrefixActive = false;
|
|
1787
|
+
} else {
|
|
1788
|
+
if (prevForkedTokenMs !== null && tokenMs - prevForkedTokenMs >= CODEX_FORK_REPLAY_GAP_MS) {
|
|
1789
|
+
replayPrefixActive = false;
|
|
1790
|
+
}
|
|
1791
|
+
forkedReplaySkip =
|
|
1792
|
+
replayPrefixActive &&
|
|
1793
|
+
prevForkedTokenMs !== null &&
|
|
1794
|
+
tokenMs - prevForkedTokenMs < CODEX_FORK_REPLAY_GAP_MS;
|
|
1795
|
+
prevForkedTokenMs = tokenMs;
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
// date matching is conservative; same-day fork replays are caught by the
|
|
1800
|
+
// burst detector above rather than this cross-day date guard.
|
|
1801
|
+
if (forkedReplaySkip || isForkedReplayToken({ isForkedRollout, rolloutDate, currentDate })) {
|
|
1802
|
+
continue;
|
|
1803
|
+
}
|
|
1596
1804
|
|
|
1597
1805
|
const bucketStart = toUtcHalfHourStart(tokenTimestamp);
|
|
1598
1806
|
if (!bucketStart) continue;
|
|
@@ -1613,10 +1821,13 @@ async function parseRolloutFile({
|
|
|
1613
1821
|
// rewrite) manages Codex. Other rollout-format sources (e.g. every-code) have
|
|
1614
1822
|
// their own model re-alignment that legitimately re-reads prior events, which
|
|
1615
1823
|
// this dedup would otherwise suppress.
|
|
1616
|
-
|
|
1824
|
+
const codexEvents = typeof seenCodexEvents === "function"
|
|
1825
|
+
? seenCodexEvents()
|
|
1826
|
+
: seenCodexEvents;
|
|
1827
|
+
if (codexEvents && source === "codex") {
|
|
1617
1828
|
const dedupKey = `${sessionId || filePath}:${tokenTimestamp}`;
|
|
1618
|
-
if (
|
|
1619
|
-
|
|
1829
|
+
if (codexEvents.has(dedupKey)) continue;
|
|
1830
|
+
codexEvents.add(dedupKey);
|
|
1620
1831
|
}
|
|
1621
1832
|
|
|
1622
1833
|
const bucket = getHourlyBucket(hourlyState, source, model, bucketStart);
|
|
@@ -2694,6 +2905,15 @@ function normalizeIsoDate(value) {
|
|
|
2694
2905
|
return match ? match[1] : null;
|
|
2695
2906
|
}
|
|
2696
2907
|
|
|
2908
|
+
// Max inter-row gap (ms) still treated as part of a forked rollout's replay
|
|
2909
|
+
// burst. Empirically the replay flush spaces rows sub-ms to a few ms apart while
|
|
2910
|
+
// genuine live turns arrive ≥~4.6s apart and the replay→live break is ≥11s, so
|
|
2911
|
+
// any value in the ~0.25–2s band separates them with a wide margin; 500ms biases
|
|
2912
|
+
// toward the low end because merging a real fast turn (under-count) is worse than
|
|
2913
|
+
// leaving a slow replay counted (bounded over-count). See the skip site in
|
|
2914
|
+
// parseRolloutFile. (issue #169 follow-up.)
|
|
2915
|
+
const CODEX_FORK_REPLAY_GAP_MS = 500;
|
|
2916
|
+
|
|
2697
2917
|
function isForkedReplayToken({ isForkedRollout, rolloutDate, currentDate }) {
|
|
2698
2918
|
return Boolean(isForkedRollout && rolloutDate && currentDate && currentDate < rolloutDate);
|
|
2699
2919
|
}
|
package/src/lib/usage-limits.js
CHANGED
|
@@ -2823,6 +2823,7 @@ async function fetchUsageLimitsUncached({
|
|
|
2823
2823
|
requestFn,
|
|
2824
2824
|
now = new Date(),
|
|
2825
2825
|
providerTimeoutMs = DEFAULT_PROVIDER_TIMEOUT_MS,
|
|
2826
|
+
forceRefresh = false,
|
|
2826
2827
|
} = {}) {
|
|
2827
2828
|
const nowMs = Date.now();
|
|
2828
2829
|
|
|
@@ -2870,11 +2871,14 @@ async function fetchUsageLimitsUncached({
|
|
|
2870
2871
|
// Skip the upstream Claude call entirely while a 429 cooldown is active — calling again
|
|
2871
2872
|
// just renews the penalty. The result handling below serves cache or a cooldown message.
|
|
2872
2873
|
const claudeRetryAtMs = claudeToken ? readClaudeRateLimitRetryAtMs({ home, nowMs }) : null;
|
|
2873
|
-
// Also avoid cross-process hammering after a recent successful read
|
|
2874
|
-
//
|
|
2875
|
-
//
|
|
2876
|
-
//
|
|
2877
|
-
|
|
2874
|
+
// Also avoid cross-process hammering after a recent successful read: embedded-server
|
|
2875
|
+
// restarts and background polls read the disk cache instead of spending another Claude
|
|
2876
|
+
// OAuth usage request. An explicit user refresh (refresh=1 → forceRefresh) punches
|
|
2877
|
+
// through this cache — but never through the 429 cooldown above, which is exactly the
|
|
2878
|
+
// hammering the cooldown exists to prevent.
|
|
2879
|
+
const freshClaudeCache = claudeToken && !forceRefresh
|
|
2880
|
+
? readFreshClaudeLimitsCache({ home, nowMs })
|
|
2881
|
+
: null;
|
|
2878
2882
|
|
|
2879
2883
|
const providerFetch = withFetchTimeout(fetchImpl, providerTimeoutMs);
|
|
2880
2884
|
const [claudeResult, codexResult, cursor, kimi, gemini, kiro, antigravity, copilot, grok, zcode, opencodeGo] = await Promise.all([
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as n,bb as a}from"./main-B9m79io-.js";import{u as p}from"./DialogTitle-f7gNmCca.js";import{u as c}from"./useScrollLock-DijV6DYS.js";const x=n.forwardRef(function(e,r){const{render:l,className:u,style:m,id:s,...t}=e,{store:i}=p(),o=c(s);return i.useSyncedValueWithCleanup("descriptionElementId",o),a("p",e,{ref:r,props:[{id:o},t]})});export{x as D};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{c}from"./main-B9m79io-.js";const e=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],t=c("check",e);export{t as C};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e,b3 as r,ai as a,b4 as n,b5 as s}from"./main-B9m79io-.js";const T=Object.freeze({mode:a.COMPACT,setMode:()=>{},formatTokens:(o,t={})=>s(o,t),formatTokensTooltip:(o,t={})=>n(o,t)});function f(){return e.useContext(r)??T}export{f as u};
|