tokentracker-cli 0.17.2 → 0.18.0

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.
Files changed (36) hide show
  1. package/README.md +2 -1
  2. package/README.zh-CN.md +4 -3
  3. package/dashboard/dist/assets/{Card-CgMq55sY.js → Card-DaQruAEZ.js} +1 -1
  4. package/dashboard/dist/assets/DashboardPage-B1ZCjfgB.js +1 -0
  5. package/dashboard/dist/assets/{FadeIn-DLhKBgqY.js → FadeIn-BYn5mp_C.js} +1 -1
  6. package/dashboard/dist/assets/{HeaderGithubStar-C9HsEaL7.js → HeaderGithubStar-DnKHfm5F.js} +1 -1
  7. package/dashboard/dist/assets/{IpCheckPage-XRIx0rfG.js → IpCheckPage-BtYkItYr.js} +1 -1
  8. package/dashboard/dist/assets/{LandingPage-DXlrkwEx.js → LandingPage-C4Ukq_ho.js} +1 -1
  9. package/dashboard/dist/assets/{LeaderboardPage-CIqzV9cT.js → LeaderboardPage-yWE-j66g.js} +1 -1
  10. package/dashboard/dist/assets/{LeaderboardProfilePage-IUgRwHTr.js → LeaderboardProfilePage-C3UVE-YJ.js} +1 -1
  11. package/dashboard/dist/assets/{LimitsPage-OrcWqg7m.js → LimitsPage-DH2yQZwk.js} +1 -1
  12. package/dashboard/dist/assets/{LoginPage-BuSNuEnm.js → LoginPage-BbEBnz58.js} +1 -1
  13. package/dashboard/dist/assets/{PopoverPopup-D6I_CIFY.js → PopoverPopup-B6sNFXGZ.js} +1 -1
  14. package/dashboard/dist/assets/{ProviderIcon-Bp-wDvoj.js → ProviderIcon-CDVWc_TN.js} +1 -1
  15. package/dashboard/dist/assets/{SettingsPage-DiyJd4az.js → SettingsPage-CJ2KJuU5.js} +1 -1
  16. package/dashboard/dist/assets/{SkillsPage-d8zU8wsf.js → SkillsPage-BQHojjxy.js} +1 -1
  17. package/dashboard/dist/assets/{WidgetsPage-DJsxeeBY.js → WidgetsPage-DuUOC5LD.js} +1 -1
  18. package/dashboard/dist/assets/{chevron-down-CFtbeaPZ.js → chevron-down-CkMLZDuK.js} +1 -1
  19. package/dashboard/dist/assets/{download-HjhL2_eA.js → download-BjbAqWCN.js} +1 -1
  20. package/dashboard/dist/assets/{leaderboard-columns-CrSoLL2s.js → leaderboard-columns-m4UzCZhg.js} +1 -1
  21. package/dashboard/dist/assets/{main-CgJNueY2.js → main-Dgiw16jZ.js} +2 -2
  22. package/dashboard/dist/assets/{use-limits-display-prefs-BW2QZr21.js → use-limits-display-prefs-DvnaGeUj.js} +1 -1
  23. package/dashboard/dist/assets/{use-native-settings-Bd1d-CVd.js → use-native-settings-BPjPcvON.js} +1 -1
  24. package/dashboard/dist/assets/{use-reduced-motion-B0JB6MTu.js → use-reduced-motion-D1akEyuH.js} +1 -1
  25. package/dashboard/dist/assets/{use-usage-limits-OtRz0apS.js → use-usage-limits-BmK4zEAz.js} +1 -1
  26. package/dashboard/dist/index.html +1 -1
  27. package/dashboard/dist/share.html +1 -1
  28. package/package.json +1 -1
  29. package/src/commands/sync.js +30 -0
  30. package/src/lib/pricing/curated-overrides.json +2 -1
  31. package/src/lib/pricing/index.js +12 -4
  32. package/src/lib/pricing/matcher.js +45 -10
  33. package/src/lib/pricing/seed-snapshot.json +1 -1
  34. package/src/lib/rollout.js +374 -0
  35. package/src/lib/usage-limits.js +2 -1
  36. package/dashboard/dist/assets/DashboardPage-DfBWYjnA.js +0 -1
@@ -1 +1 @@
1
- import{r as a}from"./main-CgJNueY2.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};
1
+ import{r as a}from"./main-Dgiw16jZ.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};
@@ -1 +1 @@
1
- import{D as a,a5 as x,r as n,aB as g,aC as m,aD as b,aE as u,aF as f,az as h}from"./main-CgJNueY2.js";import{C as y}from"./Card-CgMq55sY.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};
1
+ import{D as a,a5 as x,r as n,aB as g,aC as m,aD as b,aE as u,aF as f,az as h}from"./main-Dgiw16jZ.js";import{C as y}from"./Card-DaQruAEZ.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};
@@ -1 +1 @@
1
- import{aG as t,aH as o,r,aI as s}from"./main-CgJNueY2.js";function u(){!t.current&&o();const[e]=r.useState(s.current);return e}export{u};
1
+ import{aG as t,aH as o,r,aI as s}from"./main-Dgiw16jZ.js";function u(){!t.current&&o();const[e]=r.useState(s.current);return e}export{u};
@@ -1 +1 @@
1
- import{r,aj as l}from"./main-CgJNueY2.js";function y(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{y as u};
1
+ import{r,aj as l}from"./main-Dgiw16jZ.js";function y(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{y as u};
@@ -210,7 +210,7 @@
210
210
  ]
211
211
  }
212
212
  </script>
213
- <script type="module" crossorigin src="/assets/main-CgJNueY2.js"></script>
213
+ <script type="module" crossorigin src="/assets/main-Dgiw16jZ.js"></script>
214
214
  <link rel="stylesheet" crossorigin href="/assets/main-CITVpx5B.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-CgJNueY2.js"></script>
54
+ <script type="module" crossorigin src="/assets/main-Dgiw16jZ.js"></script>
55
55
  <link rel="stylesheet" crossorigin href="/assets/main-CITVpx5B.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.17.2",
3
+ "version": "0.18.0",
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)",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
@@ -37,6 +37,8 @@ const {
37
37
  parseCraftIncremental,
38
38
  resolveGrokBuildSessions,
39
39
  parseGrokBuildIncremental,
40
+ listAntigravityTranscripts,
41
+ parseAntigravityIncremental,
40
42
  resolveCodebuddyProjectFiles,
41
43
  parseCodebuddyIncremental,
42
44
  resolveKiroCliSessionFiles,
@@ -285,6 +287,32 @@ async function cmdSync(argv) {
285
287
  });
286
288
  }
287
289
 
290
+ const antigravityFiles = await listAntigravityTranscripts(geminiHome);
291
+ let antigravityResult = { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
292
+ if (antigravityFiles.length > 0) {
293
+ if (progress?.enabled) {
294
+ progress.start(
295
+ `Parsing Antigravity ${renderBar(0)} 0/${formatNumber(antigravityFiles.length)} files | buckets 0`,
296
+ );
297
+ }
298
+ antigravityResult = await parseAntigravityIncremental({
299
+ sessionFiles: antigravityFiles,
300
+ cursors,
301
+ queuePath,
302
+ projectQueuePath,
303
+ onProgress: (p) => {
304
+ if (!progress?.enabled) return;
305
+ const pct = p.total > 0 ? p.index / p.total : 1;
306
+ progress.update(
307
+ `Parsing Antigravity ${renderBar(pct)} ${formatNumber(p.index)}/${formatNumber(p.total)} files | buckets ${formatNumber(
308
+ p.bucketsQueued,
309
+ )}`,
310
+ );
311
+ },
312
+ source: "antigravity",
313
+ });
314
+ }
315
+
288
316
  const opencodeFiles = await listOpencodeMessageFiles(opencodeStorageDir);
289
317
  let opencodeResult = { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
290
318
  if (opencodeFiles.length > 0) {
@@ -805,6 +833,7 @@ async function cmdSync(argv) {
805
833
  openclawResult.filesProcessed +
806
834
  claudeResult.filesProcessed +
807
835
  geminiResult.filesProcessed +
836
+ antigravityResult.filesProcessed +
808
837
  opencodeResult.filesProcessed +
809
838
  cursorResult.recordsProcessed +
810
839
  kiroResult.recordsProcessed +
@@ -824,6 +853,7 @@ async function cmdSync(argv) {
824
853
  openclawResult.bucketsQueued +
825
854
  claudeResult.bucketsQueued +
826
855
  geminiResult.bucketsQueued +
856
+ antigravityResult.bucketsQueued +
827
857
  opencodeResult.bucketsQueued +
828
858
  cursorResult.bucketsQueued +
829
859
  kiroResult.bucketsQueued +
@@ -34,7 +34,8 @@
34
34
  "nemotron-3-super-free":{ "input": 0, "output": 0, "cache_read": 0 },
35
35
  "mimo-v2-pro-free": { "input": 0, "output": 0, "cache_read": 0 },
36
36
  "minimax-m2.1-free":{ "input": 0, "output": 0, "cache_read": 0 },
37
- "MiniMax-M2.1": { "input": 0.5, "output": 3, "cache_read": 0.05 }
37
+ "MiniMax-M2.1": { "input": 0.5, "output": 3, "cache_read": 0.05 },
38
+ "antigravity-gpt-oss-120b": { "input": 2.5, "output": 10, "cache_read": 0, "note": "Antigravity bridge alias. Approximate with GPT-4o-class pricing until Google exposes route-specific billing metadata." }
38
39
  },
39
40
  "alias": {
40
41
  "auto": "composer-1"
@@ -82,17 +82,25 @@ function resetPricingForTests() {
82
82
  state.negativeCache.clear();
83
83
  }
84
84
 
85
- function getModelPricing(model) {
85
+ function getModelPricing(model, opts = {}) {
86
86
  if (!model) return ZERO_PRICING;
87
- if (state.negativeCache.has(model)) return ZERO_PRICING;
87
+ let lookupSource = null;
88
+ if (typeof opts === "string") {
89
+ lookupSource = opts.toLowerCase();
90
+ } else if (typeof opts.source === "string") {
91
+ lookupSource = opts.source.toLowerCase();
92
+ }
93
+ const cacheKey = lookupSource ? `${lookupSource}\0${model}` : model;
94
+ if (state.negativeCache.has(cacheKey)) return ZERO_PRICING;
88
95
 
89
96
  const result = lookupPricing(model, {
90
97
  curated: curatedOverrides,
91
98
  litellm: state.litellmPerMillionMap,
99
+ source: lookupSource,
92
100
  });
93
101
  if (result.hit) return result.value;
94
102
 
95
- state.negativeCache.add(model);
103
+ state.negativeCache.add(cacheKey);
96
104
  return ZERO_PRICING;
97
105
  }
98
106
 
@@ -100,7 +108,7 @@ function getModelPricing(model) {
100
108
  // computeRowCost in src/lib/local-api.js. Moved here so vite mock + local
101
109
  // server share one source of truth.
102
110
  function computeRowCost(row) {
103
- const pricing = getModelPricing(row.model);
111
+ const pricing = getModelPricing(row.model, { source: row.source });
104
112
  const reasoningIncludedInOutput = row.source === "codex" || row.source === "every-code";
105
113
  const reasoningCost = reasoningIncludedInOutput
106
114
  ? 0
@@ -28,6 +28,37 @@ function stripReasoningSuffix(model) {
28
28
  return model;
29
29
  }
30
30
 
31
+ function normalizeAntigravityModel(model) {
32
+ if (!model || typeof model !== "string") return model;
33
+ let lower = model
34
+ .trim()
35
+ .replace(/\([^)]*\)/g, " ")
36
+ .replace(/\b(thinking|xhigh|high|medium|low|fast)\b/gi, " ")
37
+ .toLowerCase()
38
+ .replace(/[^a-z0-9.]+/g, "-")
39
+ .replace(/^-+|-+$/g, "")
40
+ .replace(/-{2,}/g, "-");
41
+ lower = stripReasoningSuffix(lower);
42
+
43
+ if (lower.startsWith("gemini-claude-") || lower.startsWith("gemini-gpt-")) {
44
+ lower = lower.substring(7);
45
+ }
46
+
47
+ if (/^gemini-3\.\d+-flash-lite/.test(lower)) return "gemini-2.5-flash-lite";
48
+ if (/^gemini-3\.\d+-flash/.test(lower)) return "gemini-2.5-flash";
49
+ if (/^gemini-3\.\d+-pro/.test(lower)) return "gemini-2.5-pro";
50
+ if (/^claude-(sonnet|opus|haiku)-4\.\d+/.test(lower)) {
51
+ return lower.replace(/^claude-(sonnet|opus|haiku)-4\.(\d+)/, "claude-$1-4-$2");
52
+ }
53
+ if (lower.startsWith("gpt-oss-120b")) return "antigravity-gpt-oss-120b";
54
+
55
+ return lower;
56
+ }
57
+
58
+ function shouldNormalizeAntigravity(source) {
59
+ return typeof source === "string" && source.toLowerCase() === "antigravity";
60
+ }
61
+
31
62
  // Memoise the sorted-by-length LiteLLM key list. Reverse-substring scan walks
32
63
  // this once per uncached model; ~2k keys × negligible per-iteration cost, but
33
64
  // computing the sort on every call would add up across a sync.
@@ -41,28 +72,31 @@ function getSortedKeys(litellm) {
41
72
  return cached;
42
73
  }
43
74
 
44
- function lookupPricing(model, { curated, litellm }) {
75
+ function lookupPricing(model, { curated, litellm, source } = {}) {
45
76
  if (!model || typeof model !== "string") {
46
77
  return { hit: false, source: "empty", value: null };
47
78
  }
48
- const lower = model.toLowerCase();
79
+ const lookupModel = shouldNormalizeAntigravity(source)
80
+ ? normalizeAntigravityModel(model)
81
+ : model;
82
+ const lower = lookupModel.toLowerCase();
49
83
 
50
84
  // 1. CURATED exact
51
- if (curated.exact && curated.exact[model]) {
52
- return { hit: true, source: "curated:exact", value: curated.exact[model] };
85
+ if (curated.exact && curated.exact[lookupModel]) {
86
+ return { hit: true, source: "curated:exact", value: curated.exact[lookupModel] };
53
87
  }
54
88
 
55
89
  // 2. LiteLLM exact
56
- if (litellm && litellm[model]) {
57
- return { hit: true, source: "litellm:exact", value: litellm[model] };
90
+ if (litellm && litellm[lookupModel]) {
91
+ return { hit: true, source: "litellm:exact", value: litellm[lookupModel] };
58
92
  }
59
93
 
60
94
  // 3. CURATED alias (literal mapping like "auto" -> "composer-1")
61
- if (curated.alias && curated.alias[model] && curated.exact[curated.alias[model]]) {
95
+ if (curated.alias && curated.alias[lookupModel] && curated.exact[curated.alias[lookupModel]]) {
62
96
  return {
63
97
  hit: true,
64
98
  source: "curated:alias",
65
- value: curated.exact[curated.alias[model]],
99
+ value: curated.exact[curated.alias[lookupModel]],
66
100
  };
67
101
  }
68
102
 
@@ -78,8 +112,8 @@ function lookupPricing(model, { curated, litellm }) {
78
112
 
79
113
  // 5. LiteLLM suffix-strip
80
114
  if (litellm) {
81
- const stripped = stripReasoningSuffix(model);
82
- if (stripped !== model && litellm[stripped]) {
115
+ const stripped = stripReasoningSuffix(lookupModel);
116
+ if (stripped !== lookupModel && litellm[stripped]) {
83
117
  return { hit: true, source: "litellm:strip", value: litellm[stripped] };
84
118
  }
85
119
  }
@@ -144,6 +178,7 @@ function buildLitellmPerMillionMap(rawData) {
144
178
  module.exports = {
145
179
  lookupPricing,
146
180
  stripReasoningSuffix,
181
+ normalizeAntigravityModel,
147
182
  convertLitellmEntry,
148
183
  buildLitellmPerMillionMap,
149
184
  };