tokentracker-cli 0.53.2 → 0.53.4
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-BJ980nAD.js} +1 -1
- package/dashboard/dist/assets/{Card-CJiPCvnj.js → Card-B6Lf_P1k.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-CrdiIz7q.js → DashboardPage-B15gBRRP.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-DBjdZA2N.js → DevicePage-CxIy3ahX.js} +1 -1
- package/dashboard/dist/assets/{DialogTitle--jWfmv_b.js → DialogTitle-Cw5xQZ_R.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-BrYhLQwK.js → FadeIn-DfuANxLc.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-DGtt-L8c.js → HeaderGithubStar-CoQWIKv4.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-ToZsVEOj.js → IpCheckPage-SVk0e_8x.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-DgB2CT3R.js → LandingPage-CPAmE-yI.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardAvatar-94z-hs6u.js → LeaderboardAvatar-DHTK-O9A.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-hXIlg-XS.js → LeaderboardPage-o8abeuhO.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfileModal-BcppEyBi.js → LeaderboardProfileModal-D7dt_DwK.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfilePage-BfZVW6cn.js → LeaderboardProfilePage-CVTBYIoO.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-CdcWyyh0.js → LimitsPage-C-yuMLq4.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-CBRDpg-i.js → LocalOnlyNotice-DcKDnOEU.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-DLkWQvC1.js → LoginPage-CVD0_gAd.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-DMl0dwMU.js → PopoverPopup-uE2yt3Ya.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-CwwTPCTr.js → ResetPasswordPage-CkLEg7Xj.js} +1 -1
- package/dashboard/dist/assets/{Select-D3gMIEdB.js → Select-T_lwTOia.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-GvEnESYK.js → SelectItemText-CBG4W9IV.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-B3v6IkYh.js → SettingsPage-CfVhlxOc.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-DX_mIIl8.js → SkillsPage-BI6ugQVd.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-DgqWQ9EA.js → WidgetsPage-C0eeE5n9.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-CR02q4cO.js → WrappedPage-M5xKJN8-.js} +1 -1
- package/dashboard/dist/assets/{agent-logos-B3gxKbbb.js → agent-logos-Ce9EWr5X.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-DJo_-PxX.js → arrow-up-right-Dm5l83hw.js} +1 -1
- package/dashboard/dist/assets/{download-BwsW6vug.js → download-Bi2Iqqa-.js} +1 -1
- package/dashboard/dist/assets/{info-C-aAjjA6.js → info-DQFlgzy1.js} +1 -1
- package/dashboard/dist/assets/{main-BbEXIjk8.js → main-MlcZt0XW.js} +15 -15
- package/dashboard/dist/assets/{use-limits-display-prefs-BUrvvpbW.js → use-limits-display-prefs-BH_wSPHR.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-SsQX6gzC.js → use-native-settings-Bpwi96vW.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-LOfhEW65.js → use-usage-limits-DxpPWR_A.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-CVVJYL2V.js → useCurrency-B4tP0bFn.js} +1 -1
- package/dashboard/dist/assets/{useScrollLock-Dm_66oFo.js → useScrollLock-BLDk4uMc.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +2 -2
- package/src/commands/sync.js +210 -1
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +75 -0
package/src/lib/rollout.js
CHANGED
|
@@ -42,6 +42,30 @@ async function listRolloutFiles(sessionsDir) {
|
|
|
42
42
|
return out;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// Collect rollout-*.jsonl at ANY depth under dir. listRolloutFiles requires the
|
|
46
|
+
// strict YYYY/MM/DD/ nesting Codex itself writes, but Codex-Manager archives
|
|
47
|
+
// sessions FLAT into ~/.codex/archived_sessions/ (issue #187), so the strict
|
|
48
|
+
// scanner misses them. This recursive variant handles both flat and nested
|
|
49
|
+
// layouts; safe because the codex event dedup keys on sessionUUID + timestamp,
|
|
50
|
+
// so an archived copy of an already-counted session re-reads as a no-op.
|
|
51
|
+
async function listRolloutFilesDeep(dir) {
|
|
52
|
+
const out = [];
|
|
53
|
+
async function walk(d) {
|
|
54
|
+
const entries = await safeReadDir(d);
|
|
55
|
+
for (const e of entries) {
|
|
56
|
+
const p = path.join(d, e.name);
|
|
57
|
+
if (e.isDirectory()) {
|
|
58
|
+
await walk(p);
|
|
59
|
+
} else if (e.isFile() && e.name.startsWith("rollout-") && e.name.endsWith(".jsonl")) {
|
|
60
|
+
out.push(p);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
await walk(dir);
|
|
65
|
+
out.sort((a, b) => a.localeCompare(b));
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
45
69
|
async function listClaudeProjectFiles(projectsDir) {
|
|
46
70
|
const out = [];
|
|
47
71
|
await walkClaudeProjects(projectsDir, out);
|
|
@@ -102,6 +126,13 @@ async function parseRolloutIncremental({
|
|
|
102
126
|
cursors.files = {};
|
|
103
127
|
}
|
|
104
128
|
|
|
129
|
+
// Persisted set of seen Codex event keys (sessionUUID:eventTimestamp). Mirrors
|
|
130
|
+
// the claudeHashes pattern: it makes an inode-changing re-scan idempotent so an
|
|
131
|
+
// external rewrite of a session file (Codex-Manager's atomic provider-patch on
|
|
132
|
+
// account switch, issue #187) cannot re-count already-counted events.
|
|
133
|
+
const prevCodexHashes = Array.isArray(cursors?.codexHashes) ? cursors.codexHashes : [];
|
|
134
|
+
const seenCodexEvents = new Set(prevCodexHashes);
|
|
135
|
+
|
|
105
136
|
for (let idx = 0; idx < rolloutFiles.length; idx++) {
|
|
106
137
|
const entry = rolloutFiles[idx];
|
|
107
138
|
const filePath = typeof entry === "string" ? entry : entry?.path;
|
|
@@ -147,6 +178,8 @@ async function parseRolloutIncremental({
|
|
|
147
178
|
projectMetaCache,
|
|
148
179
|
publicRepoCache,
|
|
149
180
|
publicRepoResolver,
|
|
181
|
+
seenCodexEvents,
|
|
182
|
+
sessionId: codexSessionIdFromPath(filePath),
|
|
150
183
|
});
|
|
151
184
|
|
|
152
185
|
cursors.files[key] = {
|
|
@@ -176,6 +209,7 @@ async function parseRolloutIncremental({
|
|
|
176
209
|
const projectBucketsQueued = projectEnabled
|
|
177
210
|
? await enqueueTouchedProjectBuckets({ projectQueuePath, projectState, projectTouchedBuckets })
|
|
178
211
|
: 0;
|
|
212
|
+
cursors.codexHashes = Array.from(seenCodexEvents);
|
|
179
213
|
hourlyState.updatedAt = new Date().toISOString();
|
|
180
214
|
cursors.hourly = hourlyState;
|
|
181
215
|
if (projectState) {
|
|
@@ -718,6 +752,22 @@ async function parseOpenclawSessionFile({
|
|
|
718
752
|
return { endOffset, eventsAggregated };
|
|
719
753
|
}
|
|
720
754
|
|
|
755
|
+
/**
|
|
756
|
+
* Extract the session UUID from a Codex rollout file path
|
|
757
|
+
* (`rollout-<datetime>-<uuid>.jsonl`). Used as the stable per-session scope for
|
|
758
|
+
* event dedup: it survives both an inode-changing rewrite (Codex-Manager
|
|
759
|
+
* atomically rewrites session files to patch the provider on account switch,
|
|
760
|
+
* issue #187) and a sessions/ -> archived_sessions/ move. Returns null when the
|
|
761
|
+
* name has no UUID, in which case the caller falls back to the full path.
|
|
762
|
+
*/
|
|
763
|
+
function codexSessionIdFromPath(filePath) {
|
|
764
|
+
if (typeof filePath !== "string") return null;
|
|
765
|
+
const m = filePath.match(
|
|
766
|
+
/([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$/,
|
|
767
|
+
);
|
|
768
|
+
return m ? m[1] : null;
|
|
769
|
+
}
|
|
770
|
+
|
|
721
771
|
async function parseRolloutFile({
|
|
722
772
|
filePath,
|
|
723
773
|
startOffset,
|
|
@@ -733,6 +783,8 @@ async function parseRolloutFile({
|
|
|
733
783
|
projectMetaCache,
|
|
734
784
|
publicRepoCache,
|
|
735
785
|
publicRepoResolver,
|
|
786
|
+
seenCodexEvents,
|
|
787
|
+
sessionId,
|
|
736
788
|
}) {
|
|
737
789
|
const st = await fs.stat(filePath);
|
|
738
790
|
const endOffset = st.size;
|
|
@@ -830,6 +882,28 @@ async function parseRolloutFile({
|
|
|
830
882
|
const bucketStart = toUtcHalfHourStart(tokenTimestamp);
|
|
831
883
|
if (!bucketStart) continue;
|
|
832
884
|
|
|
885
|
+
// Idempotent re-scan dedup (issue #187). Codex usage is parsed incrementally
|
|
886
|
+
// by (inode, offset): when the inode changes the file is re-scanned from
|
|
887
|
+
// offset 0 and every event's delta is re-added to the PERSISTENT hourly
|
|
888
|
+
// buckets. External tools rewrite session files without changing the token
|
|
889
|
+
// data — Codex-Manager atomically rewrites them (new inode) to patch the
|
|
890
|
+
// provider on every account/channel switch — so without dedup each switch
|
|
891
|
+
// double-counts the rewritten sessions. `totals` is already advanced above,
|
|
892
|
+
// so skipping an already-seen event keeps the cumulative-delta chain intact
|
|
893
|
+
// while preventing the re-add; genuinely new turns carry new timestamps and
|
|
894
|
+
// are still counted. Key = sessionUUID:eventTimestamp (both stable across the
|
|
895
|
+
// rewrite and across a sessions/ -> archived_sessions/ move).
|
|
896
|
+
//
|
|
897
|
+
// Scoped to the `codex` source: Codex-Manager (the tool that does the atomic
|
|
898
|
+
// rewrite) manages Codex. Other rollout-format sources (e.g. every-code) have
|
|
899
|
+
// their own model re-alignment that legitimately re-reads prior events, which
|
|
900
|
+
// this dedup would otherwise suppress.
|
|
901
|
+
if (seenCodexEvents && source === "codex") {
|
|
902
|
+
const dedupKey = `${sessionId || filePath}:${tokenTimestamp}`;
|
|
903
|
+
if (seenCodexEvents.has(dedupKey)) continue;
|
|
904
|
+
seenCodexEvents.add(dedupKey);
|
|
905
|
+
}
|
|
906
|
+
|
|
833
907
|
const bucket = getHourlyBucket(hourlyState, source, model, bucketStart);
|
|
834
908
|
addTotals(bucket.totals, delta);
|
|
835
909
|
touchedBuckets.add(bucketKey(source, model, bucketStart));
|
|
@@ -8557,6 +8631,7 @@ function isCjkCodePoint(code) {
|
|
|
8557
8631
|
|
|
8558
8632
|
module.exports = {
|
|
8559
8633
|
listRolloutFiles,
|
|
8634
|
+
listRolloutFilesDeep,
|
|
8560
8635
|
listClaudeProjectFiles,
|
|
8561
8636
|
listGeminiSessionFiles,
|
|
8562
8637
|
listOpencodeMessageFiles,
|