tokentracker-cli 0.53.2 → 0.53.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.
- package/dashboard/dist/assets/{ActivityHeatmap-CoVxkSjr.js → ActivityHeatmap-CZasqY2H.js} +1 -1
- package/dashboard/dist/assets/{Card-CJiPCvnj.js → Card-Cjd-ZKzN.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-CrdiIz7q.js → DashboardPage-BCa11vC2.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-DBjdZA2N.js → DevicePage-BFCRnDcz.js} +1 -1
- package/dashboard/dist/assets/{DialogTitle--jWfmv_b.js → DialogTitle-Btt5G5wB.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-BrYhLQwK.js → FadeIn-gIH1bR94.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-DGtt-L8c.js → HeaderGithubStar-BuzgYTZ5.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-ToZsVEOj.js → IpCheckPage-Cn4cjpf4.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-DgB2CT3R.js → LandingPage-BaPLcNme.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardAvatar-94z-hs6u.js → LeaderboardAvatar-DMaYoU7B.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-hXIlg-XS.js → LeaderboardPage-D-qMCc2C.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfileModal-BcppEyBi.js → LeaderboardProfileModal-CYzcdwRb.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfilePage-BfZVW6cn.js → LeaderboardProfilePage-CQCqbWZE.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-CdcWyyh0.js → LimitsPage-M7a-TKW5.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-CBRDpg-i.js → LocalOnlyNotice-k-0-ORjj.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-DLkWQvC1.js → LoginPage-BxAbRsn_.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-DMl0dwMU.js → PopoverPopup-C4YO4_N-.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-CwwTPCTr.js → ResetPasswordPage-Dqd9baMc.js} +1 -1
- package/dashboard/dist/assets/{Select-D3gMIEdB.js → Select-DXD3JLWK.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-GvEnESYK.js → SelectItemText-CTgRQ8ND.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-B3v6IkYh.js → SettingsPage-B2HxneYg.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-DX_mIIl8.js → SkillsPage-DEFIbkAX.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-DgqWQ9EA.js → WidgetsPage-DObILWDk.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-CR02q4cO.js → WrappedPage-CHKlz7Tc.js} +1 -1
- package/dashboard/dist/assets/{agent-logos-B3gxKbbb.js → agent-logos-CO0k-UGZ.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-DJo_-PxX.js → arrow-up-right-B_p-ZPxR.js} +1 -1
- package/dashboard/dist/assets/{download-BwsW6vug.js → download-zHPmUqi6.js} +1 -1
- package/dashboard/dist/assets/{info-C-aAjjA6.js → info-Cje0RF1w.js} +1 -1
- package/dashboard/dist/assets/{main-BbEXIjk8.js → main-DADupgLA.js} +2 -2
- package/dashboard/dist/assets/{use-limits-display-prefs-BUrvvpbW.js → use-limits-display-prefs-DIfRSzm0.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-SsQX6gzC.js → use-native-settings-BuBDLdTx.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-LOfhEW65.js → use-usage-limits-CFd1PQLO.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-CVVJYL2V.js → useCurrency-CIJjQNSk.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-Dm_66oFo.js → useScrollLock-DO1uWkvz.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/sync.js +198 -0
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +50 -0
package/src/lib/rollout.js
CHANGED
|
@@ -102,6 +102,13 @@ async function parseRolloutIncremental({
|
|
|
102
102
|
cursors.files = {};
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
// Persisted set of seen Codex event keys (sessionUUID:eventTimestamp). Mirrors
|
|
106
|
+
// the claudeHashes pattern: it makes an inode-changing re-scan idempotent so an
|
|
107
|
+
// external rewrite of a session file (Codex-Manager's atomic provider-patch on
|
|
108
|
+
// account switch, issue #187) cannot re-count already-counted events.
|
|
109
|
+
const prevCodexHashes = Array.isArray(cursors?.codexHashes) ? cursors.codexHashes : [];
|
|
110
|
+
const seenCodexEvents = new Set(prevCodexHashes);
|
|
111
|
+
|
|
105
112
|
for (let idx = 0; idx < rolloutFiles.length; idx++) {
|
|
106
113
|
const entry = rolloutFiles[idx];
|
|
107
114
|
const filePath = typeof entry === "string" ? entry : entry?.path;
|
|
@@ -147,6 +154,8 @@ async function parseRolloutIncremental({
|
|
|
147
154
|
projectMetaCache,
|
|
148
155
|
publicRepoCache,
|
|
149
156
|
publicRepoResolver,
|
|
157
|
+
seenCodexEvents,
|
|
158
|
+
sessionId: codexSessionIdFromPath(filePath),
|
|
150
159
|
});
|
|
151
160
|
|
|
152
161
|
cursors.files[key] = {
|
|
@@ -176,6 +185,7 @@ async function parseRolloutIncremental({
|
|
|
176
185
|
const projectBucketsQueued = projectEnabled
|
|
177
186
|
? await enqueueTouchedProjectBuckets({ projectQueuePath, projectState, projectTouchedBuckets })
|
|
178
187
|
: 0;
|
|
188
|
+
cursors.codexHashes = Array.from(seenCodexEvents);
|
|
179
189
|
hourlyState.updatedAt = new Date().toISOString();
|
|
180
190
|
cursors.hourly = hourlyState;
|
|
181
191
|
if (projectState) {
|
|
@@ -718,6 +728,22 @@ async function parseOpenclawSessionFile({
|
|
|
718
728
|
return { endOffset, eventsAggregated };
|
|
719
729
|
}
|
|
720
730
|
|
|
731
|
+
/**
|
|
732
|
+
* Extract the session UUID from a Codex rollout file path
|
|
733
|
+
* (`rollout-<datetime>-<uuid>.jsonl`). Used as the stable per-session scope for
|
|
734
|
+
* event dedup: it survives both an inode-changing rewrite (Codex-Manager
|
|
735
|
+
* atomically rewrites session files to patch the provider on account switch,
|
|
736
|
+
* issue #187) and a sessions/ -> archived_sessions/ move. Returns null when the
|
|
737
|
+
* name has no UUID, in which case the caller falls back to the full path.
|
|
738
|
+
*/
|
|
739
|
+
function codexSessionIdFromPath(filePath) {
|
|
740
|
+
if (typeof filePath !== "string") return null;
|
|
741
|
+
const m = filePath.match(
|
|
742
|
+
/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})\.jsonl$/,
|
|
743
|
+
);
|
|
744
|
+
return m ? m[1] : null;
|
|
745
|
+
}
|
|
746
|
+
|
|
721
747
|
async function parseRolloutFile({
|
|
722
748
|
filePath,
|
|
723
749
|
startOffset,
|
|
@@ -733,6 +759,8 @@ async function parseRolloutFile({
|
|
|
733
759
|
projectMetaCache,
|
|
734
760
|
publicRepoCache,
|
|
735
761
|
publicRepoResolver,
|
|
762
|
+
seenCodexEvents,
|
|
763
|
+
sessionId,
|
|
736
764
|
}) {
|
|
737
765
|
const st = await fs.stat(filePath);
|
|
738
766
|
const endOffset = st.size;
|
|
@@ -830,6 +858,28 @@ async function parseRolloutFile({
|
|
|
830
858
|
const bucketStart = toUtcHalfHourStart(tokenTimestamp);
|
|
831
859
|
if (!bucketStart) continue;
|
|
832
860
|
|
|
861
|
+
// Idempotent re-scan dedup (issue #187). Codex usage is parsed incrementally
|
|
862
|
+
// by (inode, offset): when the inode changes the file is re-scanned from
|
|
863
|
+
// offset 0 and every event's delta is re-added to the PERSISTENT hourly
|
|
864
|
+
// buckets. External tools rewrite session files without changing the token
|
|
865
|
+
// data — Codex-Manager atomically rewrites them (new inode) to patch the
|
|
866
|
+
// provider on every account/channel switch — so without dedup each switch
|
|
867
|
+
// double-counts the rewritten sessions. `totals` is already advanced above,
|
|
868
|
+
// so skipping an already-seen event keeps the cumulative-delta chain intact
|
|
869
|
+
// while preventing the re-add; genuinely new turns carry new timestamps and
|
|
870
|
+
// are still counted. Key = sessionUUID:eventTimestamp (both stable across the
|
|
871
|
+
// rewrite and across a sessions/ -> archived_sessions/ move).
|
|
872
|
+
//
|
|
873
|
+
// Scoped to the `codex` source: Codex-Manager (the tool that does the atomic
|
|
874
|
+
// rewrite) manages Codex. Other rollout-format sources (e.g. every-code) have
|
|
875
|
+
// their own model re-alignment that legitimately re-reads prior events, which
|
|
876
|
+
// this dedup would otherwise suppress.
|
|
877
|
+
if (seenCodexEvents && source === "codex") {
|
|
878
|
+
const dedupKey = `${sessionId || filePath}:${tokenTimestamp}`;
|
|
879
|
+
if (seenCodexEvents.has(dedupKey)) continue;
|
|
880
|
+
seenCodexEvents.add(dedupKey);
|
|
881
|
+
}
|
|
882
|
+
|
|
833
883
|
const bucket = getHourlyBucket(hourlyState, source, model, bucketStart);
|
|
834
884
|
addTotals(bucket.totals, delta);
|
|
835
885
|
touchedBuckets.add(bucketKey(source, model, bucketStart));
|