tokentracker-cli 1.0.0 → 1.0.2
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-Dpf3VBcS.js → AchievementBadge-CBGtv8Gf.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-BSGbLI5V.js → AchievementsPage-DBNXAqHB.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-C10HXoq1.js → AchievementsSection-D9ndOgjA.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-DajrEc3e.js → BadgeDetailModal-B_xo1k4z.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-BpbTCJML.js → BotAnimated-OaYL-NrW.js} +1 -1
- package/dashboard/dist/assets/{Card-CDCj927L.js → Card-BprDyWQy.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-CiTkm04x.js → ClawdAnimated-DKzj5hnk.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-CYHUUX7M.js → CommandPalette-Y7y8uy9B.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-BHZehk5P.js → CommunityStatsModal-DtJjD6Gk.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-DZlT_xe_.js → ConfirmModal-Dnq9wDSB.js} +1 -1
- package/dashboard/dist/assets/{Controls-Bcijikco.js → Controls-B3LyuY9G.js} +1 -1
- package/dashboard/dist/assets/DashboardPage-uyz3GHPG.js +1 -0
- package/dashboard/dist/assets/{DevicePage-DzisRlts.js → DevicePage-TBvBfX-E.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-CQ9hFUNv.js → DialogClose-C0xfVNxI.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-B1_lWQbi.js +1 -0
- package/dashboard/dist/assets/{DialogTitle--bItxq3K.js → DialogTitle-h96NnlwF.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-v5sAHZBu.js → FadeIn-BlT9LOhg.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-Bg8R51fN.js → HeaderGithubStar-BC8PlJFf.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-DToNFKkJ.js → HoverTooltip-D2H0rvOx.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-BmFXYXZm.js → IpCheckPage-Bf9pTUyL.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-COpRacX0.js → LandingPage-CqFZZcgl.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-D0l8_L_d.js → LeaderboardAvatar-Bglw3mWk.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-BJHS44Fq.js → LeaderboardPage-Ba4rstvv.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-C1K9rSGn.js → LeaderboardProfileModal-DITfDxdj.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-CvR48D_y.js → LeaderboardProfilePage-9DgMqaVi.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-BN79yMkb.js → LimitsPage-Ct5IA5Ny.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-BwTJpkDW.js → LocalOnlyNotice-DCgOvXBZ.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-BMXAg1rZ.js → LoginCard-Bo3St7wp.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-Bf0F_UPK.js → LoginModal-CqN9Lhx0.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-BylH2waa.js → LoginPage-CLvn_ZAo.js} +1 -1
- package/dashboard/dist/assets/{PetPage-X4v_J-Uj.js → PetPage-B-tmGNaA.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-yEQVU5C_.js → PopoverPopup-D0KIhAfw.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-KRXIghW0.js → ProviderIcon-DLytDN3-.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-DQQF2rsl.js → ResetPasswordPage-CGVTHdB9.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-VkVwcoXk.js → SegmentedControl-B7gk-3QK.js} +1 -1
- package/dashboard/dist/assets/{Select-CxW2C8OG.js → Select-qbqB_qtp.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-D6vhaI0Q.js → SelectItemText-Qzhp3UFE.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-CCKqUZhI.js → ServiceStatusPage-DPe_EeVu.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-Huqt77Qt.js → SessionsPage-Dza7qrmT.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-CpX8GYce.js → SettingsPage-C_Am0uFM.js} +2 -2
- package/dashboard/dist/assets/{SkillsPage-S1dtu_Cd.js → SkillsPage-Bsu5m-xp.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-BayM0FOG.js → TokenGalaxy-COH6RLu3.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-B1KG8B5q.js → ToolbarRootContext-B_XUhyzz.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-DYqz9nLw.js → TrendMonitor-DMgbTVNO.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-WuzVeCBG.js → WidgetsPage-gQTmgsN6.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-Bpn9GYHz.js → WrappedPage-B5nLQG1F.js} +1 -1
- package/dashboard/dist/assets/__vite-browser-external-DUAYhIIg.js +45 -0
- package/dashboard/dist/assets/{agent-logos-DVE956SY.js → agent-logos-B92se84f.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-B7aZy0wJ.js → arrow-up-right-THgpMHTL.js} +1 -1
- package/dashboard/dist/assets/{check-55YI8PGo.js → check-CZ5GlBuF.js} +1 -1
- package/dashboard/dist/assets/{composite--O2XKSlW.js → composite-pohmvEEN.js} +1 -1
- package/dashboard/dist/assets/{copy-DDVFmqF5.js → copy-BEu6UqzK.js} +1 -1
- package/dashboard/dist/assets/{download-DmIFrL1S.js → download-580FSMzx.js} +1 -1
- package/dashboard/dist/assets/{external-link-DwG_AAOo.js → external-link-DSl6rKSu.js} +1 -1
- package/dashboard/dist/assets/{flame-ANwvantJ.js → flame-CuoqhgO0.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-BzZ_DfX2.js → format-tokens-yngxPaJs.js} +1 -1
- package/dashboard/dist/assets/{icons-CdyVgzlu.js → icons-C5P7F226.js} +1 -1
- package/dashboard/dist/assets/{index-D8g1p_Ee.js → index-1CFe9s91.js} +1 -1
- package/dashboard/dist/assets/{index-B-1uJU_R.js → index-CVccZBhd.js} +1 -1
- package/dashboard/dist/assets/{index-D0VhCSlC.js → index-DkLVCz9v.js} +1 -1
- package/dashboard/dist/assets/{info-n2XpcOlm.js → info-BtYRYa79.js} +1 -1
- package/dashboard/dist/assets/{link-2-Bq9GUX22.js → link-2-D4bPs6lx.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-fkYFxXuJ.js → loader-circle-CAcXf7Ue.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-C5v08iPD.js → maximize-2-B3xxE3HC.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-AG_bzlWZ.js → modifiers.esm-DvoWZTDt.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-BEjlJhuw.js → monitor-smartphone-CkyNQoTv.js} +1 -1
- package/dashboard/dist/assets/{plus-BfdtMxnD.js → plus-BCXK4rAO.js} +1 -1
- package/dashboard/dist/assets/{provider-display-BvjuoQty.js → provider-display-Y5ggva-H.js} +1 -1
- package/dashboard/dist/assets/{react-f77D-LSn.js → react-CjlEHr8p.js} +1 -1
- package/dashboard/dist/assets/{search-Dmczow8i.js → search-Dh5kBuL2.js} +1 -1
- package/dashboard/dist/assets/{skills-api-BuqPQei_.js → skills-api-XhL8INjh.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-CDAVQaFQ.js → sortable.esm-CFa-CzqV.js} +1 -1
- package/dashboard/dist/assets/{store-C2inKcM-.js → store-Bt47laU0.js} +1 -1
- package/dashboard/dist/assets/{terminal-C1O422UZ.js → terminal-BXW29s08.js} +1 -1
- package/dashboard/dist/assets/{trash-CXOKPZfQ.js → trash-CYw5PqQr.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-CKfyxLvl.js → use-community-stats-C5t_95Jj.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-C3rQR4BN.js → use-latest-request-guard-CcrXitYE.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-Ic4GUPRY.js → use-limits-display-prefs-DktuWI-1.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-Cvbz_wcj.js → use-ordered-list-DUHxxfXc.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-dYTH0OxK.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-u_qW99gD.js → use-session-efficiency-pref-CDSUns2p.js} +1 -1
- package/dashboard/dist/assets/{use-transform-CVGiBxJK.js → use-transform-DkLOu1mx.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-MPu696v0.js → use-usage-limits-BTbbhOUs.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-BOBz18If.js → useCurrency-C7QiSZUH.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-BooCzJJZ.js → useTokenFormat-Kv5h6RPd.js} +1 -1
- package/dashboard/dist/assets/{zap-M_x9dAwo.js → zap-DOdxB6Nn.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/ip-check.html +1 -1
- package/dashboard/dist/leaderboard.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +1 -1
- package/src/commands/status.js +3 -0
- package/src/commands/sync.js +10 -1
- package/src/lib/codex-rollout-parser.js +86 -8
- package/src/lib/codex-usage-record.js +195 -0
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +154 -1
- package/dashboard/dist/assets/DashboardPage-yREqx-tx.js +0 -1
- package/dashboard/dist/assets/DialogDescription-CLXWH3lG.js +0 -1
- package/dashboard/dist/assets/__vite-browser-external-B1XxVtUQ.js +0 -45
- package/dashboard/dist/assets/use-reduced-motion-DewlF5tk.js +0 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Codex rollouts written since CLI 0.151 carry a top-level
|
|
4
|
+
// {"type":"token_usage_record","payload":{usage,turn_token_usage,thread_token_usage,...}}
|
|
5
|
+
// next to the event_msg token_count that has always carried usage (issue
|
|
6
|
+
// #652). While both are written they describe the same model response, so
|
|
7
|
+
// token_count stays the only source of normal usage and a record is counted
|
|
8
|
+
// in exactly one case:
|
|
9
|
+
//
|
|
10
|
+
// Compaction. Codex bills the compaction model call in a record that is
|
|
11
|
+
// immediately followed by a top-level `compacted` line, and the next
|
|
12
|
+
// token_count repeats the previous cumulative total (its last_token_usage is
|
|
13
|
+
// the post-compaction context size, not a response). The cumulative delta is
|
|
14
|
+
// zero, so without the record the call is lost. Inside a file that has
|
|
15
|
+
// already shown a token_count, such a record is held as `compaction` and
|
|
16
|
+
// counted when the next token_count arrives with a total identical to the
|
|
17
|
+
// previous one. If that total advanced, the token_count already covers the
|
|
18
|
+
// call and the record is dropped. Each compaction is counted once per
|
|
19
|
+
// payload.response_id: a fork replays its parent's records and a copied
|
|
20
|
+
// rollout repeats them, both with the same id. A record without one is never
|
|
21
|
+
// counted.
|
|
22
|
+
//
|
|
23
|
+
// A file whose records are never matched by a token_count is not counted from
|
|
24
|
+
// its records. It is flagged `recordOnly` instead, and sync surfaces a
|
|
25
|
+
// warning. The proof is the same ordering Codex keeps for every response: its
|
|
26
|
+
// token_count is written after its record and before the next record or the
|
|
27
|
+
// turn end. So a record followed by another record or a turn end with no
|
|
28
|
+
// token_count in between flags the file; a sync that stops between a record
|
|
29
|
+
// and its token_count does not.
|
|
30
|
+
//
|
|
31
|
+
// payload.usage is the per-response delta; turn_token_usage and
|
|
32
|
+
// thread_token_usage are cumulative and are never read. All state below is
|
|
33
|
+
// persisted in the sync cursor and the session parser resume state, so the
|
|
34
|
+
// record, the `compacted` line and the token_count may land in different
|
|
35
|
+
// reads.
|
|
36
|
+
|
|
37
|
+
const { canonicalUsage, sameUsage } = require("./codex-token-usage");
|
|
38
|
+
|
|
39
|
+
function extractTokenUsageRecord(obj) {
|
|
40
|
+
if (obj?.type !== "token_usage_record") return null;
|
|
41
|
+
const usage = obj.payload?.usage;
|
|
42
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage)) return null;
|
|
43
|
+
const timestamp = typeof obj.timestamp === "string" ? obj.timestamp : null;
|
|
44
|
+
if (!timestamp) return null;
|
|
45
|
+
// canonicalUsage folds cache_write_input_tokens into
|
|
46
|
+
// cache_creation_input_tokens exactly as the token_count path does.
|
|
47
|
+
const canonical = canonicalUsage(usage);
|
|
48
|
+
if (!canonical) return null;
|
|
49
|
+
const responseId = typeof obj.payload.response_id === "string" && obj.payload.response_id
|
|
50
|
+
? obj.payload.response_id
|
|
51
|
+
: null;
|
|
52
|
+
return { usage: canonical, timestamp, responseId };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Compaction calls already counted by sync, by response_id, kept in core
|
|
56
|
+
// cursor state as cursors.codexCompactionResponseIds (oldest first). The id
|
|
57
|
+
// is unique per API response and survives fork replays and file copies, so
|
|
58
|
+
// it is the only dedup key a compaction needs. Compactions are rare (19 on
|
|
59
|
+
// the maintainer's corpus), so the cap is far above any real history.
|
|
60
|
+
const MAX_COMPACTION_RESPONSE_IDS = 20000;
|
|
61
|
+
|
|
62
|
+
function createCompactionResponseIds(cursors) {
|
|
63
|
+
const stored = Array.isArray(cursors.codexCompactionResponseIds)
|
|
64
|
+
? cursors.codexCompactionResponseIds
|
|
65
|
+
: [];
|
|
66
|
+
const ids = new Set(stored);
|
|
67
|
+
const added = [];
|
|
68
|
+
return {
|
|
69
|
+
has: (id) => ids.has(id),
|
|
70
|
+
add(id) {
|
|
71
|
+
if (ids.has(id)) return;
|
|
72
|
+
ids.add(id);
|
|
73
|
+
added.push(id);
|
|
74
|
+
},
|
|
75
|
+
persist() {
|
|
76
|
+
if (added.length === 0) return;
|
|
77
|
+
cursors.codexCompactionResponseIds = [...stored, ...added].slice(-MAX_COMPACTION_RESPONSE_IDS);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isCodexTurnEndEvent(obj) {
|
|
83
|
+
if (obj?.type !== "event_msg") return false;
|
|
84
|
+
const type = obj.payload?.type;
|
|
85
|
+
return type === "task_complete" || type === "turn_aborted";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isPlainObject(value) {
|
|
89
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function createUsageRecordState(snapshot) {
|
|
93
|
+
const compaction = snapshot?.compaction;
|
|
94
|
+
return {
|
|
95
|
+
sawTokenCount: Boolean(snapshot?.sawTokenCount),
|
|
96
|
+
unmatchedRecord: Boolean(snapshot?.unmatchedRecord),
|
|
97
|
+
recordOnly: Boolean(snapshot?.recordOnly),
|
|
98
|
+
compaction: isPlainObject(compaction) && isPlainObject(compaction.candidate)
|
|
99
|
+
? { candidate: compaction.candidate, compacted: Boolean(compaction.compacted) }
|
|
100
|
+
: null,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Called for every token_count carrying an info object.
|
|
105
|
+
function noteTokenCount(state) {
|
|
106
|
+
state.sawTokenCount = true;
|
|
107
|
+
state.unmatchedRecord = false;
|
|
108
|
+
state.recordOnly = false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Called for every record. `candidate` is what the caller would count if the
|
|
112
|
+
// record turns out to be a compaction call (null when it cannot be counted).
|
|
113
|
+
function noteUsageRecord(state, candidate) {
|
|
114
|
+
if (state.sawTokenCount) {
|
|
115
|
+
state.compaction = candidate ? { candidate, compacted: false } : null;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (state.unmatchedRecord) state.recordOnly = true;
|
|
119
|
+
state.unmatchedRecord = true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Turn end: a record still waiting for its token_count will not get one.
|
|
123
|
+
function noteTurnEnd(state) {
|
|
124
|
+
if (state.sawTokenCount) return;
|
|
125
|
+
if (state.unmatchedRecord) state.recordOnly = true;
|
|
126
|
+
state.unmatchedRecord = false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// True while the line right after a record decides whether it was a
|
|
130
|
+
// compaction call.
|
|
131
|
+
function awaitsCompactedLine(state) {
|
|
132
|
+
return Boolean(state?.compaction && !state.compaction.compacted);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Called for each complete line after a record, other than a record. Only a
|
|
136
|
+
// `compacted` line directly after the record keeps it as a candidate.
|
|
137
|
+
function noteLineAfterUsageRecord(state, isCompactedLine) {
|
|
138
|
+
if (!awaitsCompactedLine(state)) return;
|
|
139
|
+
if (isCompactedLine) state.compaction.compacted = true;
|
|
140
|
+
else state.compaction = null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Called for every token_count carrying an info object, with the cumulative
|
|
144
|
+
// total that was active before it. Returns the compaction candidate to count,
|
|
145
|
+
// or null.
|
|
146
|
+
function takeCompactionOnTokenCount(state, previousTotal, totalUsage) {
|
|
147
|
+
const compaction = state.compaction;
|
|
148
|
+
state.compaction = null;
|
|
149
|
+
if (!compaction?.compacted) return null;
|
|
150
|
+
if (!canonicalUsage(previousTotal) || !sameUsage(previousTotal, totalUsage)) return null;
|
|
151
|
+
return compaction.candidate;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function isRecordOnly(state) {
|
|
155
|
+
return Boolean(state?.recordOnly && !state.sawTokenCount);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function snapshotUsageRecordState(state) {
|
|
159
|
+
if (!state || (!state.sawTokenCount && !state.unmatchedRecord && !state.recordOnly)) return null;
|
|
160
|
+
const out = { sawTokenCount: state.sawTokenCount };
|
|
161
|
+
if (state.unmatchedRecord) out.unmatchedRecord = true;
|
|
162
|
+
if (state.recordOnly) out.recordOnly = true;
|
|
163
|
+
if (state.compaction) out.compaction = state.compaction;
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Sync keeps the flagged rollouts in core cursor state:
|
|
168
|
+
// cursors.codexUsageRecordOnlyFiles = { [rolloutPath]: true }.
|
|
169
|
+
function countRecordOnlyFiles(cursors) {
|
|
170
|
+
const flagged = cursors?.codexUsageRecordOnlyFiles;
|
|
171
|
+
return isPlainObject(flagged) ? Object.keys(flagged).length : 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function formatRecordOnlyWarning(count) {
|
|
175
|
+
if (!count) return null;
|
|
176
|
+
return `${count} Codex session(s) report usage only via token_usage_record; not counted yet (see #652)`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
module.exports = {
|
|
180
|
+
MAX_COMPACTION_RESPONSE_IDS,
|
|
181
|
+
createCompactionResponseIds,
|
|
182
|
+
countRecordOnlyFiles,
|
|
183
|
+
formatRecordOnlyWarning,
|
|
184
|
+
awaitsCompactedLine,
|
|
185
|
+
createUsageRecordState,
|
|
186
|
+
extractTokenUsageRecord,
|
|
187
|
+
isCodexTurnEndEvent,
|
|
188
|
+
isRecordOnly,
|
|
189
|
+
noteLineAfterUsageRecord,
|
|
190
|
+
noteTokenCount,
|
|
191
|
+
noteTurnEnd,
|
|
192
|
+
noteUsageRecord,
|
|
193
|
+
snapshotUsageRecordState,
|
|
194
|
+
takeCompactionOnTokenCount,
|
|
195
|
+
};
|