tokentracker-cli 0.26.2 → 0.26.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-BX2q-PW5.js → ActivityHeatmap-BQmAWAi7.js} +1 -1
- package/dashboard/dist/assets/{Card-CKdycEDk.js → Card-Cq6JCZM2.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-T_BImOGr.js → DashboardPage-CUt6YnjQ.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-DqFrE2fo.js → DevicePage-BwZwER8Q.js} +1 -1
- package/dashboard/dist/assets/{DialogTitle-BPUtAz2U.js → DialogTitle-wuIq7mbf.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-ortmqOpl.js → FadeIn-CEOcEh_z.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-Cl4nchsC.js → HeaderGithubStar-BRQgDG4V.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-CxRBCdXW.js → IpCheckPage-BrOzm4SC.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-DoIw_oXY.js → LandingPage-vqu0eDLZ.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardAvatar-GMFZci05.js → LeaderboardAvatar-Am5bi8es.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-BzkrPpnp.js → LeaderboardPage-Bi-245fM.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfileModal-CAcqZ15e.js → LeaderboardProfileModal-C8LQXrnF.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfilePage-D7jyFQk5.js → LeaderboardProfilePage-31yIarGc.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-fdq2MG4P.js → LimitsPage-h3rjkQlR.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-XEv4_b68.js → LocalOnlyNotice-CebJL6tE.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-CJ31pW6G.js → LoginPage-sRWAttYf.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-cEzMmw1k.js → PopoverPopup-D5KBjI3r.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-CkKiJH4q.js → ProviderIcon-CSMEjb0n.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-D--hzVuV.js → SettingsPage-B9x5wKaH.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-BTxPA7dW.js → SkillsPage-Cl76d9F3.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-Bh4zs5u8.js → WidgetsPage-CfgHQjCv.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-COhqXKs7.js → WrappedPage--2jUGICx.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-B0tVW8XM.js → arrow-up-right-ru062Kz1.js} +1 -1
- package/dashboard/dist/assets/check-CG2_3azC.js +1 -0
- package/dashboard/dist/assets/{chevron-down-Cz58bkoU.js → chevron-down-Czb08wHc.js} +1 -1
- package/dashboard/dist/assets/{download-Gv8d-j81.js → download-_X5qj-Bp.js} +1 -1
- package/dashboard/dist/assets/{info-bDEPb8dB.js → info-DBw8ck72.js} +1 -1
- package/dashboard/dist/assets/{main-tm3dyfMf.js → main-CYNmFfGa.js} +2 -2
- package/dashboard/dist/assets/{use-limits-display-prefs-BvHWn0OQ.js → use-limits-display-prefs-DDOdet4v.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-CI_YvzEo.js → use-native-settings-Bz5dJHlV.js} +1 -1
- package/dashboard/dist/assets/{use-reduced-motion-pCeztzVC.js → use-reduced-motion-7VZbLGR1.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-CKWnCeca.js → use-usage-limits-CW7MLh5g.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-DLoIpDjL.js → useCurrency-BF2b0rl-.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 +8 -6
- package/src/lib/claude-categorizer.js +59 -18
- package/src/lib/pricing/matcher.js +20 -0
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +4 -7
- package/dashboard/dist/assets/check-Dhwj43_0.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as a}from"./main-
|
|
1
|
+
import{r as a}from"./main-CYNmFfGa.js";const d=["claude","codex","cursor","gemini","kimi","kiro","copilot","antigravity"],S={claude:"Claude",codex:"Codex",cursor:"Cursor",gemini:"Gemini",kimi:"Kimi",kiro:"Kiro",copilot:"GitHub Copilot",antigravity:"Antigravity"},v={claude:"/brand-logos/claude-code.svg",codex:"/brand-logos/codex.svg",cursor:"/brand-logos/cursor.svg",gemini:"/brand-logos/gemini.svg",kimi:"/brand-logos/kimi.svg",kiro:"/brand-logos/kiro.svg",copilot:"/brand-logos/copilot.svg",antigravity:"/brand-logos/antigravity.svg"},l="tt.limits.providerOrder",g="tt.limits.providerVisibility";function w(){if(typeof window>"u")return[...d];try{const i=window.localStorage.getItem(l);if(!i)return[...d];const s=JSON.parse(i);if(!Array.isArray(s))return[...d];const r=s.filter(c=>d.includes(c));for(const c of d)r.includes(c)||r.push(c);return r}catch{return[...d]}}function y(){const i=Object.fromEntries(d.map(s=>[s,!0]));if(typeof window>"u")return i;try{const s=window.localStorage.getItem(g);if(!s)return i;const r=JSON.parse(s);if(!r||typeof r!="object")return i;const c={...i};for(const u of d)typeof r[u]=="boolean"&&(c[u]=r[u]);return c}catch{return i}}function C(){const[i,s]=a.useState(w),[r,c]=a.useState(y);a.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(l,JSON.stringify(i))}catch{}},[i]),a.useEffect(()=>{if(!(typeof window>"u"))try{window.localStorage.setItem(g,JSON.stringify(r))}catch{}},[r]),a.useEffect(()=>{if(typeof window>"u")return;const o=t=>{t.key===l&&s(w()),t.key===g&&c(y())};return window.addEventListener("storage",o),()=>window.removeEventListener("storage",o)},[]);const u=a.useCallback(o=>{c(t=>({...t,[o]:!t[o]}))},[]),b=a.useCallback(o=>{s(t=>{const e=t.indexOf(o);if(e<=0)return t;const n=[...t];return[n[e-1],n[e]]=[n[e],n[e-1]],n})},[]),p=a.useCallback(o=>{s(t=>{const e=t.indexOf(o);if(e<0||e>=t.length-1)return t;const n=[...t];return[n[e],n[e+1]]=[n[e+1],n[e]],n})},[]),O=a.useCallback((o,t)=>{o!==t&&s(e=>{const n=e.indexOf(o),m=e.indexOf(t);if(n<0||m<0)return e;const f=[...e],[I]=f.splice(n,1);return f.splice(m,0,I),f})},[]),k=a.useCallback(()=>{s([...d]),c(Object.fromEntries(d.map(o=>[o,!0])))},[]),x=a.useMemo(()=>i.filter(o=>r[o]!==!1),[i,r]);return{order:i,visibility:r,visibleOrdered:x,toggle:u,moveUp:b,moveDown:p,moveToward:O,reset:k}}export{v as L,S as a,C as u};
|
package/dashboard/dist/assets/{use-native-settings-CI_YvzEo.js → use-native-settings-Bz5dJHlV.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{C as a,S as x,r as n,aG as g,aH as m,aI as b,aJ as u,aK as f,aE as h}from"./main-
|
|
1
|
+
import{C as a,S as x,r as n,aG as g,aH as m,aI as b,aJ as u,aK as f,aE as h}from"./main-CYNmFfGa.js";import{C as y}from"./Card-Cq6JCZM2.js";function p({checked:s,onChange:t,disabled:e,ariaLabel:i}){return a.jsx("button",{type:"button",role:"switch","aria-checked":s,"aria-label":i,onClick:t,disabled:e,className:x("relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-oai-brand-500 disabled:opacity-50 disabled:cursor-not-allowed",s?"bg-oai-brand-500":"bg-oai-gray-300 dark:bg-oai-gray-700"),children:a.jsx("span",{className:x("inline-block h-3.5 w-3.5 rounded-full bg-white transition-transform",s?"translate-x-[18px]":"translate-x-[3px]")})})}function j({label:s,hint:t,control:e}){return a.jsxs("div",{className:"flex items-center justify-between gap-4 py-3",children:[a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsx("div",{className:"text-sm text-oai-gray-900 dark:text-oai-gray-200",children:s}),t?a.jsx("div",{className:"mt-0.5 text-xs text-oai-gray-500 dark:text-oai-gray-400",children:t}):null]}),a.jsx("div",{className:"shrink-0",children:e})]})}function N({title:s,subtitle:t,action:e,children:i}){return a.jsxs(y,{children:[a.jsxs("div",{className:"mb-3 flex items-start justify-between gap-4",children:[a.jsxs("div",{className:"min-w-0 flex-1",children:[a.jsx("h2",{className:"text-sm font-medium text-oai-gray-500 dark:text-oai-gray-300 uppercase tracking-wide",children:s}),t?a.jsx("p",{className:"mt-1 truncate text-xs text-oai-gray-500 dark:text-oai-gray-400",children:t}):null]}),e?a.jsx("div",{className:"shrink-0",children:e}):null]}),a.jsx("div",{className:"-mb-3 divide-y divide-oai-gray-200/60 dark:divide-oai-gray-800/60",children:i})]})}function w({options:s,value:t,onChange:e}){return a.jsx("div",{className:"inline-flex items-center rounded-lg border border-oai-gray-200 bg-oai-gray-50 p-0.5 dark:border-oai-gray-800 dark:bg-oai-gray-900",children:s.map(({value:i,label:d,Icon:l})=>{const r=t===i;return a.jsxs("button",{type:"button",onClick:()=>e(i),"aria-pressed":r,className:x("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors",r?"bg-white text-oai-black shadow-sm dark:bg-oai-gray-800 dark:text-white":"text-oai-gray-500 hover:text-oai-black dark:text-oai-gray-400 dark:hover:text-white"),children:[l?a.jsx(l,{className:"h-3.5 w-3.5","aria-hidden":!0}):null,a.jsx("span",{children:d})]},i)})})}function S(){const[s,t]=n.useState(null),e=g()&&m();n.useEffect(()=>{if(!e)return;const r=b(o=>t(o));return u(),r},[e]);const i=n.useCallback((r,o)=>{e&&(t(c=>c&&{...c,[r]:o}),f(r,o))},[e]),d=n.useCallback(r=>{e&&h(r)},[e]),l=n.useCallback(()=>{e&&u()},[e]);return{available:e,settings:s,setSetting:i,runAction:d,refresh:l}}export{N as S,p as T,j as a,w as b,S as u};
|
package/dashboard/dist/assets/{use-reduced-motion-pCeztzVC.js → use-reduced-motion-7VZbLGR1.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{aL as t,aM as o,r,aN as s}from"./main-
|
|
1
|
+
import{aL as t,aM as o,r,aN as s}from"./main-CYNmFfGa.js";function u(){!t.current&&o();const[e]=r.useState(s.current);return e}export{u};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r,am as l}from"./main-
|
|
1
|
+
import{r,am as l}from"./main-CYNmFfGa.js";function m(i){const[o,a]=r.useState(null),[u,s]=r.useState(null),[c,f]=r.useState(!0),n=!!i?.initialRefresh,g=r.useCallback(async()=>{try{const e=await l({refresh:!0});a(e&&typeof e=="object"?e:null),s(null)}catch(e){s(e?.message||String(e))}},[]);return r.useEffect(()=>{let e=!1;return(async()=>{try{const t=await l(n?{refresh:!0}:{});if(e)return;a(t&&typeof t=="object"?t:null),s(null)}catch(t){if(e)return;s(t?.message||String(t))}finally{e||f(!1)}})(),()=>{e=!0}},[n]),{data:o,error:u,isLoading:c,refresh:g}}export{m as u};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e,aB as t,aC as r,H as c}from"./main-
|
|
1
|
+
import{r as e,aB as t,aC as r,H as c}from"./main-CYNmFfGa.js";const a=Object.freeze({currency:c,rate:1,symbol:"$",rates:{...r},rateSource:"default",rateFetchedAt:null,setCurrency:()=>{}});function u(){return e.useContext(t)??a}export{u};
|
|
@@ -210,7 +210,7 @@
|
|
|
210
210
|
]
|
|
211
211
|
}
|
|
212
212
|
</script>
|
|
213
|
-
<script type="module" crossorigin src="/assets/main-
|
|
213
|
+
<script type="module" crossorigin src="/assets/main-CYNmFfGa.js"></script>
|
|
214
214
|
<link rel="stylesheet" crossorigin href="/assets/main-smnMFIqE.css">
|
|
215
215
|
</head>
|
|
216
216
|
<body>
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"description": "Shareable Token Tracker dashboard snapshot."
|
|
52
52
|
}
|
|
53
53
|
</script>
|
|
54
|
-
<script type="module" crossorigin src="/assets/main-
|
|
54
|
+
<script type="module" crossorigin src="/assets/main-CYNmFfGa.js"></script>
|
|
55
55
|
<link rel="stylesheet" crossorigin href="/assets/main-smnMFIqE.css">
|
|
56
56
|
</head>
|
|
57
57
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokentracker-cli",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.3",
|
|
4
4
|
"description": "Token usage tracker for AI agent CLIs (Claude Code, Codex, Cursor, Gemini, Kiro, OpenCode, OpenClaw, Every Code, Hermes, GitHub Copilot, Kimi Code, CodeBuddy, Grok Build, oh-my-pi, pi, Craft Agents, Kilo CLI, Kilo Code, Roo Code, Zed Agent, Goose)",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
6
|
"bin": {
|
package/src/commands/sync.js
CHANGED
|
@@ -104,10 +104,12 @@ const CLAUDE_MEM_OBSERVER_PATH_SEGMENT = "--claude-mem-observer-sessions";
|
|
|
104
104
|
// (DeepSeek/Kimi/Mimo/MiniMax anthropic-compatible endpoints, plus Claude
|
|
105
105
|
// Code's sub-agent / thinking transport paths). The repaired ground truth
|
|
106
106
|
// was therefore inflated by 1.6–3.7x on those providers — v3 left it that
|
|
107
|
-
// way.
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
|
|
107
|
+
// way.
|
|
108
|
+
// v6 re-runs the same atomic repair after making Claude zero-usage snapshots
|
|
109
|
+
// ineligible for dedup state. Mimo reuses message.id across zero/partial
|
|
110
|
+
// streaming snapshots and the final token-bearing response, so v4/v5 could
|
|
111
|
+
// preserve a tiny scar total instead of the ccusage-aligned ground truth.
|
|
112
|
+
const CLAUDE_GROUND_TRUTH_REPAIR_KEY = "claudeGroundTruthRepair_2026_05_v6";
|
|
111
113
|
|
|
112
114
|
async function cmdSync(argv) {
|
|
113
115
|
const opts = parseArgs(argv);
|
|
@@ -2045,7 +2047,7 @@ async function repairClaudeQueueFromGroundTruth({
|
|
|
2045
2047
|
}
|
|
2046
2048
|
uploadState.offset = 0;
|
|
2047
2049
|
uploadState.updatedAt = new Date().toISOString();
|
|
2048
|
-
uploadState.note = "
|
|
2050
|
+
uploadState.note = "reset_after_claude_repair_2026_05_v6";
|
|
2049
2051
|
await fs.writeFile(queueStatePath, JSON.stringify(uploadState));
|
|
2050
2052
|
}
|
|
2051
2053
|
|
|
@@ -2112,7 +2114,7 @@ async function repairClaudeQueueFromGroundTruth({
|
|
|
2112
2114
|
}
|
|
2113
2115
|
st.offset = 0;
|
|
2114
2116
|
st.updatedAt = new Date().toISOString();
|
|
2115
|
-
st.note = "
|
|
2117
|
+
st.note = "reset_after_claude_repair_2026_05_v6";
|
|
2116
2118
|
await fs.writeFile(projectQueueStatePath, JSON.stringify(st));
|
|
2117
2119
|
}
|
|
2118
2120
|
}
|
|
@@ -54,6 +54,44 @@ function emptyCategoryMap() {
|
|
|
54
54
|
return out;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
function toNonNegativeNumber(value) {
|
|
58
|
+
const n = Number(value || 0);
|
|
59
|
+
return Number.isFinite(n) ? Math.max(0, n) : 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function readClaudeUsageTotals(usage) {
|
|
63
|
+
if (!usage || typeof usage !== "object") return null;
|
|
64
|
+
const input_tokens = toNonNegativeNumber(usage.input_tokens);
|
|
65
|
+
const cached_input_tokens = toNonNegativeNumber(usage.cache_read_input_tokens);
|
|
66
|
+
const cache_creation_input_tokens = toNonNegativeNumber(usage.cache_creation_input_tokens);
|
|
67
|
+
const output_tokens = toNonNegativeNumber(usage.output_tokens);
|
|
68
|
+
const reasoning_output_tokens = toNonNegativeNumber(usage.reasoning_output_tokens);
|
|
69
|
+
const total_tokens =
|
|
70
|
+
input_tokens + cached_input_tokens + cache_creation_input_tokens + output_tokens;
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
input_tokens,
|
|
74
|
+
cached_input_tokens,
|
|
75
|
+
cache_creation_input_tokens,
|
|
76
|
+
output_tokens,
|
|
77
|
+
reasoning_output_tokens,
|
|
78
|
+
total_tokens,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function hasNonZeroClaudeUsage(usage) {
|
|
83
|
+
const totals = readClaudeUsageTotals(usage);
|
|
84
|
+
if (!totals) return false;
|
|
85
|
+
return (
|
|
86
|
+
totals.input_tokens +
|
|
87
|
+
totals.cached_input_tokens +
|
|
88
|
+
totals.cache_creation_input_tokens +
|
|
89
|
+
totals.output_tokens +
|
|
90
|
+
totals.reasoning_output_tokens >
|
|
91
|
+
0
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
57
95
|
function extractExecCommands(content) {
|
|
58
96
|
const commands = [];
|
|
59
97
|
for (const block of Array.isArray(content) ? content : []) {
|
|
@@ -481,10 +519,19 @@ async function categorizeSessionFile(filePath, { fromIso, toIso, seenHashes }, b
|
|
|
481
519
|
if (toIso && ts > toIso) continue;
|
|
482
520
|
|
|
483
521
|
const hash = claudeMessageDedupKey(obj);
|
|
484
|
-
if (hash)
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
522
|
+
if (hash && seenHashes.has(hash)) continue;
|
|
523
|
+
|
|
524
|
+
// Defer the hash-add until we've confirmed this entry carries non-zero
|
|
525
|
+
// usage. Mirrors the same fix in rollout.js parseClaudeFile. Without it,
|
|
526
|
+
// a zero-token streaming snapshot (e.g. Mimo's pre-response thinking
|
|
527
|
+
// entry that shares the final response's message.id) would claim the
|
|
528
|
+
// bare-msgId dedup key — Mimo has no requestId, so claudeMessageDedupKey
|
|
529
|
+
// falls back to bare msgId — and pre-empt the real token-bearing entry,
|
|
530
|
+
// collapsing Mimo into a tiny fraction of its real total.
|
|
531
|
+
const usage = obj?.message?.usage;
|
|
532
|
+
if (!hasNonZeroClaudeUsage(usage)) continue;
|
|
533
|
+
|
|
534
|
+
if (hash) seenHashes.add(hash);
|
|
488
535
|
|
|
489
536
|
classifyOneMessage(obj, sessionState, breakdown, toolLedger, skillLedger, execLedger);
|
|
490
537
|
counted += 1;
|
|
@@ -1195,7 +1242,8 @@ async function computeClaudeGroundTruthBuckets({ rootDir = null } = {}) {
|
|
|
1195
1242
|
continue;
|
|
1196
1243
|
}
|
|
1197
1244
|
const usage = obj?.message?.usage;
|
|
1198
|
-
|
|
1245
|
+
const totals = readClaudeUsageTotals(usage);
|
|
1246
|
+
if (!totals || !hasNonZeroClaudeUsage(usage)) continue;
|
|
1199
1247
|
|
|
1200
1248
|
const hash = claudeMessageDedupKey(obj);
|
|
1201
1249
|
if (hash) {
|
|
@@ -1208,25 +1256,18 @@ async function computeClaudeGroundTruthBuckets({ rootDir = null } = {}) {
|
|
|
1208
1256
|
const hourStart = ts ? toUtcHalfHourStart(ts) : null;
|
|
1209
1257
|
if (!hourStart) continue;
|
|
1210
1258
|
|
|
1211
|
-
const inputTok = Math.max(0, Number(usage.input_tokens || 0));
|
|
1212
|
-
const cacheRead = Math.max(0, Number(usage.cache_read_input_tokens || 0));
|
|
1213
|
-
const cacheCreate = Math.max(0, Number(usage.cache_creation_input_tokens || 0));
|
|
1214
|
-
const outputTok = Math.max(0, Number(usage.output_tokens || 0));
|
|
1215
|
-
const reasoningTok = Math.max(0, Number(usage.reasoning_output_tokens || 0));
|
|
1216
|
-
const total = inputTok + cacheRead + cacheCreate + outputTok;
|
|
1217
|
-
|
|
1218
1259
|
const key = `${model}|${hourStart}`;
|
|
1219
1260
|
let acc = buckets.get(key);
|
|
1220
1261
|
if (!acc) {
|
|
1221
1262
|
acc = bucketAccumulator();
|
|
1222
1263
|
buckets.set(key, acc);
|
|
1223
1264
|
}
|
|
1224
|
-
acc.input_tokens +=
|
|
1225
|
-
acc.cached_input_tokens +=
|
|
1226
|
-
acc.cache_creation_input_tokens +=
|
|
1227
|
-
acc.output_tokens +=
|
|
1228
|
-
acc.reasoning_output_tokens +=
|
|
1229
|
-
acc.total_tokens +=
|
|
1265
|
+
acc.input_tokens += totals.input_tokens;
|
|
1266
|
+
acc.cached_input_tokens += totals.cached_input_tokens;
|
|
1267
|
+
acc.cache_creation_input_tokens += totals.cache_creation_input_tokens;
|
|
1268
|
+
acc.output_tokens += totals.output_tokens;
|
|
1269
|
+
acc.reasoning_output_tokens += totals.reasoning_output_tokens;
|
|
1270
|
+
acc.total_tokens += totals.total_tokens;
|
|
1230
1271
|
}
|
|
1231
1272
|
rl.close();
|
|
1232
1273
|
stream.close?.();
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// 3. CURATED alias (e.g. "auto" -> "composer-1")
|
|
7
7
|
// 4. CURATED fuzzy substring (e.g. "kiro-future-xyz" matches via "kiro")
|
|
8
8
|
// 5. LiteLLM suffix-strip (gpt-5-codex-high-fast -> gpt-5-codex)
|
|
9
|
+
// 5b. LiteLLM provider-prefix strip (mimo-v2.5-pro -> openrouter/xiaomi/mimo-v2.5-pro)
|
|
9
10
|
// 6. LiteLLM reverse substring (longest-key first)
|
|
10
11
|
// 7. null (caller decides what to do — typically zero-pricing + negative cache)
|
|
11
12
|
|
|
@@ -169,6 +170,25 @@ function lookupPricing(model, { curated, litellm, source } = {}) {
|
|
|
169
170
|
}
|
|
170
171
|
}
|
|
171
172
|
|
|
173
|
+
// 5b. LiteLLM provider-prefix strip. Queue rows store the bare model name
|
|
174
|
+
// (e.g. "mimo-v2.5-pro"), but LiteLLM keys are provider-qualified (e.g.
|
|
175
|
+
// "openrouter/xiaomi/mimo-v2.5-pro"), so the exact lookups above miss. Match
|
|
176
|
+
// any key whose path suffix equals the bare model. When several providers
|
|
177
|
+
// expose the same model, pick the lexicographically smallest key so the
|
|
178
|
+
// resolved price is deterministic and independent of JSON ordering. Runs
|
|
179
|
+
// AFTER curated alias/fuzzy so e.g. Cursor's "auto" still resolves to
|
|
180
|
+
// composer-1 rather than a LiteLLM "*/auto" entry.
|
|
181
|
+
if (litellm) {
|
|
182
|
+
const suffix = "/" + lower;
|
|
183
|
+
let best = null;
|
|
184
|
+
for (const key of Object.keys(litellm)) {
|
|
185
|
+
if (key.length > suffix.length && key.toLowerCase().endsWith(suffix)) {
|
|
186
|
+
if (best === null || key < best) best = key;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (best) return { hit: true, source: "litellm:prefix-strip", value: litellm[best] };
|
|
190
|
+
}
|
|
191
|
+
|
|
172
192
|
// 6. LiteLLM reverse substring (longest-key first)
|
|
173
193
|
if (litellm) {
|
|
174
194
|
const sorted = getSortedKeys(litellm);
|