tokentracker-cli 0.3.6 → 0.3.9
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/{DashboardPage-17t3I3SD.js → DashboardPage-4iRwUaHk.js} +1 -1
- package/dashboard/dist/assets/{LandingExtras-CbiVw_PB.js → LandingExtras-B9IdYCfA.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-aSeBNOPm.js → LeaderboardPage-DW7CMnjR.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfilePage-UWJ966S9.js → LeaderboardProfilePage-mAuhFhYv.js} +1 -1
- package/dashboard/dist/assets/{MatrixRain-BUeGFxO9.js → MatrixRain-Bfjc_8I0.js} +1 -1
- package/dashboard/dist/assets/{MatrixShell-LwSJpENu.js → MatrixShell-BpQQnI5A.js} +1 -1
- package/dashboard/dist/assets/{main-QWjmY4su.js → main-DFYFVRqg.js} +4 -4
- package/dashboard/dist/assets/{vibeusage-api-B5UrDqf4.js → vibeusage-api-BGa0zUOb.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +1 -1
- package/src/lib/local-api.js +128 -6
- package/src/lib/rollout.js +8 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{b as p,al as G,d as ie,am as j,an as U,ao as ce,ap as ue,aq as le,ar as fe,as as de,at as ge,au as ke,a6 as pe,av as me,aw as ye,ax as he}from"./main-QWjmY4su.js";const be="Backend runtime unavailable (InsForge). Please retry later.",g={usageSummary:"vibeusage-usage-summary",usageDaily:"vibeusage-usage-daily",usageHourly:"vibeusage-usage-hourly",usageMonthly:"vibeusage-usage-monthly",usageHeatmap:"vibeusage-usage-heatmap",usageModelBreakdown:"vibeusage-usage-model-breakdown",projectUsageSummary:"vibeusage-project-usage-summary",leaderboard:"vibeusage-leaderboard",leaderboardProfile:"vibeusage-leaderboard-profile",userStatus:"vibeusage-user-status",linkCodeInit:"vibeusage-link-code-init",publicViewProfile:"vibeusage-public-view-profile",publicVisibility:"vibeusage-public-visibility"},J="/functions",we="/api/functions",y={business:"business",probe:"probe"};let z=null;async function d(e){return await ie(e)}async function xe({baseUrl:e,accessToken:t,signal:s}={}){const a=await d(t),n=he(new Date);return await m({baseUrl:e,accessToken:a,slug:g.usageSummary,params:{from:n,to:n},fetchOptions:{cache:"no-store",signal:s},retry:!1,requestKind:y.probe}),{status:200}}async function Ee({baseUrl:e,accessToken:t,from:s,to:a,source:n,model:r,timeZone:l,tzOffsetMinutes:o,rolling:c=!1}={}){const u=await d(t);if(p())return ue({from:s,to:a,seed:u,rolling:c});const i=M({timeZone:l,tzOffsetMinutes:o}),f=A({source:n,model:r}),_=c?{rolling:"1"}:{};return m({baseUrl:e,accessToken:u,slug:g.usageSummary,params:{from:s,to:a,...f,...i,..._}})}async function Re({baseUrl:e,accessToken:t,from:s,to:a,source:n,limit:r,timeZone:l,tzOffsetMinutes:o}={}){const c=await d(t);if(p())return fe({seed:c,limit:r});const u=M({timeZone:l,tzOffsetMinutes:o}),f={...A({source:n}),...u};return s&&(f.from=s),a&&(f.to=a),r!=null&&(f.limit=String(r)),m({baseUrl:e,accessToken:c,slug:g.projectUsageSummary,params:f})}async function Ce({baseUrl:e,accessToken:t,period:s,metric:a,limit:n,offset:r}={}){const l=await d(t);if(p())return G({seed:l,period:s,metric:a,limit:n,offset:r});const c=(typeof s=="string"?s:"week").trim().toLowerCase(),i={period:c==="month"||c==="total"||c==="week"?c:"week"};return a&&(i.metric=String(a)),n!=null&&(i.limit=String(n)),r!=null&&(i.offset=String(r)),m({baseUrl:e,accessToken:l,slug:g.leaderboard,params:i})}async function De({baseUrl:e,accessToken:t}={}){const s=await d(t);return p()?{enabled:!1,updated_at:null,share_token:null}:m({baseUrl:e,accessToken:s,slug:g.publicVisibility})}async function ze({baseUrl:e,accessToken:t,enabled:s}={}){const a=await d(t);return p()?{enabled:!!s,updated_at:new Date().toISOString(),share_token:s?"pv1-mock-token":null}:X({baseUrl:e,accessToken:a,slug:g.publicVisibility,body:{enabled:!!s}})}async function Ue({baseUrl:e,accessToken:t,userId:s,period:a}={}){const n=await d(t);if(p()){const r=G({seed:n,period:a,metric:"all",limit:250,offset:0}),o=(Array.isArray(r?.entries)?r.entries:[]).find(c=>c?.user_id===s)||null;return{period:r?.period??"week",from:r?.from??null,to:r?.to??null,generated_at:r?.generated_at??new Date().toISOString(),entry:o?{user_id:o.user_id??null,display_name:o.display_name??null,avatar_url:o.avatar_url??null,rank:o.rank??null,gpt_tokens:o.gpt_tokens??"0",claude_tokens:o.claude_tokens??"0",other_tokens:o.other_tokens??"0",total_tokens:o.total_tokens??"0"}:null}}return m({baseUrl:e,accessToken:n,slug:g.leaderboardProfile,params:{user_id:String(s||""),period:String(a||"")}})}async function Ie({baseUrl:e,accessToken:t}={}){const s=await d(t);if(p()){const a=new Date().toISOString();return{user_id:"mock-user",created_at:a,pro:{active:!0,sources:["mock"],expires_at:null,partial:!1,as_of:a},subscriptions:{partial:!1,as_of:a,items:[{tool:"codex",provider:"openai",product:"chatgpt",plan_type:"pro",updated_at:a},{tool:"claude",provider:"anthropic",product:"subscription",plan_type:"max",rate_limit_tier:"default_claude_max_5x",updated_at:a}]},install:{partial:!1,as_of:a,has_active_device_token:!1,has_active_device:!1,active_device_tokens:0,active_devices:0,latest_token_activity_at:null,latest_device_seen_at:null}}}return m({baseUrl:e,accessToken:s,slug:g.userStatus})}async function je({signal:e}={}){const t=await fetch("/functions/vibeusage-local-sync",{method:"POST",headers:{Accept:"application/json"},cache:"no-store",signal:e}),s=await t.json().catch(()=>({ok:!1,error:`Local sync request failed with HTTP ${t.status}`}));if(!t.ok||s?.ok===!1){const a=s?.error||s?.message||`Local sync request failed with HTTP ${t.status}`,n=new Error(a);throw n.status=t.status,n.statusCode=t.status,n.payload=s,n}return s}async function He({baseUrl:e,accessToken:t,from:s,to:a,source:n,timeZone:r,tzOffsetMinutes:l}={}){const o=await d(t);if(p())return le({from:s,to:a,seed:o});const c=M({timeZone:r,tzOffsetMinutes:l}),u=A({source:n});return m({baseUrl:e,accessToken:o,slug:g.usageModelBreakdown,params:{from:s,to:a,...u,...c}})}async function Le({baseUrl:e,accessToken:t,from:s,to:a,source:n,model:r,timeZone:l,tzOffsetMinutes:o}={}){const c=await d(t);if(p())return ce({from:s,to:a,seed:c});const u=M({timeZone:l,tzOffsetMinutes:o}),i=A({source:n,model:r});return m({baseUrl:e,accessToken:c,slug:g.usageDaily,params:{from:s,to:a,...i,...u}})}async function qe({baseUrl:e,accessToken:t,day:s,source:a,model:n,timeZone:r,tzOffsetMinutes:l}={}){const o=await d(t);if(p())return de({day:s,seed:o});const c=M({timeZone:r,tzOffsetMinutes:l}),u=A({source:a,model:n});return m({baseUrl:e,accessToken:o,slug:g.usageHourly,params:s?{day:s,...u,...c}:{...u,...c}})}async function Be({baseUrl:e,accessToken:t,months:s,to:a,source:n,model:r,timeZone:l,tzOffsetMinutes:o}={}){const c=await d(t);if(p())return ge({months:s,to:a,seed:c});const u=M({timeZone:l,tzOffsetMinutes:o}),i=A({source:n,model:r});return m({baseUrl:e,accessToken:c,slug:g.usageMonthly,params:{...s?{months:String(s)}:{},...a?{to:a}:{},...i,...u}})}async function Fe({baseUrl:e,accessToken:t,weeks:s,to:a,weekStartsOn:n,source:r,model:l,timeZone:o,tzOffsetMinutes:c}={}){const u=await d(t);if(p())return ke({weeks:s,to:a,weekStartsOn:n,seed:u});const i=M({timeZone:o,tzOffsetMinutes:c}),f=A({source:r,model:l});return m({baseUrl:e,accessToken:u,slug:g.usageHeatmap,params:{weeks:String(s),to:a,week_starts_on:n,...f,...i}})}async function Ne({baseUrl:e,accessToken:t}={}){const s=await d(t);return p()?{link_code:"mock_link_code",expires_at:new Date(Date.now()+10*6e4).toISOString()}:X({baseUrl:e,accessToken:s,slug:g.linkCodeInit,body:{}})}async function Ve({baseUrl:e,accessToken:t}={}){const s=await d(t);return m({baseUrl:e,accessToken:s,slug:g.publicViewProfile})}function M({timeZone:e,tzOffsetMinutes:t}={}){const s={},a=typeof e=="string"?e.trim():"";return a&&(s.tz=a),Number.isFinite(t)&&(s.tz_offset_minutes=String(Math.trunc(t))),s}function A({source:e,model:t}={}){const s={},a=typeof e=="string"?e.trim().toLowerCase():"";a&&(s.source=a);const n=typeof t=="string"?t.trim():"";return n&&(s.model=n),s}async function m({baseUrl:e,accessToken:t,slug:s,params:a,fetchOptions:n,errorPrefix:r,retry:l,requestKind:o=y.business,skipSessionExpiry:c=!1,allowRefresh:u=!0}={}){let i=await d(t),f=x(i),_=j({baseUrl:e,accessToken:i??void 0}).getHttpClient();const E=te(l,"GET"),T=c?y.probe:o;let v=0;const{primaryPath:R,fallbackPath:C}=O(s);for(;;)try{const S=await $({http:_,primaryPath:R,fallbackPath:C,params:a,fetchOptions:n});return H({hadAccessToken:f,accessToken:i}),S}catch(S){const h=S;if(h?.name==="AbortError")throw S;let b=null;const L=h?.statusCode??h?.status;if(u&&Z({status:L,requestKind:T,hadAccessToken:f,accessToken:i})){const k=(await ee())?.accessToken??null;if(x(k)){const D=j({baseUrl:e,accessToken:k}).getHttpClient();i=k,f=!0,_=D;try{const w=await $({http:D,primaryPath:R,fallbackPath:C,params:a,fetchOptions:n});return H({hadAccessToken:!0,accessToken:k}),w}catch(w){const B=w?.statusCode??w?.status;V({status:B,hadAccessToken:!0,accessToken:k,skipSessionExpiry:T===y.probe})&&U(),b=P(w,{errorPrefix:r,hadAccessToken:!0,accessToken:k,skipSessionExpiry:!0})}}else I({hadAccessToken:f,accessToken:i,skipSessionExpiry:T===y.probe})&&U();b??=P(h,{errorPrefix:r,hadAccessToken:f,accessToken:i,skipSessionExpiry:!0})}if(b??=P(h,{errorPrefix:r,hadAccessToken:f,accessToken:i,skipSessionExpiry:T===y.probe}),!se({err:b,attempt:v,retryOptions:E}))throw b;const q=ae({retryOptions:E,attempt:v});await ne(q),v+=1}}async function X({baseUrl:e,accessToken:t,slug:s,body:a,fetchOptions:n,errorPrefix:r,retry:l,requestKind:o=y.business,skipSessionExpiry:c=!1,allowRefresh:u=!0}={}){let i=await d(t),f=x(i),_=j({baseUrl:e,accessToken:i??void 0}).getHttpClient();const E=te(l,"POST"),T=c?y.probe:o;let v=0;const{primaryPath:R,fallbackPath:C}=O(s);for(;;)try{const S=await K({http:_,primaryPath:R,fallbackPath:C,body:a,fetchOptions:n});return H({hadAccessToken:f,accessToken:i}),S}catch(S){const h=S;if(h?.name==="AbortError")throw S;let b=null;const L=h?.statusCode??h?.status;if(u&&Z({status:L,requestKind:T,hadAccessToken:f,accessToken:i})){const k=(await ee())?.accessToken??null;if(x(k)){const D=j({baseUrl:e,accessToken:k}).getHttpClient();i=k,f=!0,_=D;try{const w=await K({http:D,primaryPath:R,fallbackPath:C,body:a,fetchOptions:n});return H({hadAccessToken:!0,accessToken:k}),w}catch(w){const B=w?.statusCode??w?.status;V({status:B,hadAccessToken:!0,accessToken:k,skipSessionExpiry:T===y.probe})&&U(),b=P(w,{errorPrefix:r,hadAccessToken:!0,accessToken:k,skipSessionExpiry:!0})}}else I({hadAccessToken:f,accessToken:i,skipSessionExpiry:T===y.probe})&&U();b??=P(h,{errorPrefix:r,hadAccessToken:f,accessToken:i,skipSessionExpiry:!0})}if(b??=P(h,{errorPrefix:r,hadAccessToken:f,accessToken:i,skipSessionExpiry:T===y.probe}),!se({err:b,attempt:v,retryOptions:E}))throw b;const q=ae({retryOptions:E,attempt:v});await ne(q),v+=1}}function O(e){const t=Te(e),s=`${N(J)}/${t}`,a=`${N(we)}/${t}`;return{primaryPath:s,fallbackPath:a}}function Te(e){return(typeof e=="string"?e.trim():"").replace(/^\/+/,"")}function N(e){const t=typeof e=="string"?e.trim():"";return t.endsWith("/")?t.slice(0,-1):t}async function $({http:e,primaryPath:t,fallbackPath:s,params:a,fetchOptions:n}={}){try{return await e.get(t,{params:a,...n||{}})}catch(r){if(!Q(r,t))throw r;return await e.get(s,{params:a,...n||{}})}}async function K({http:e,primaryPath:t,fallbackPath:s,body:a,fetchOptions:n}={}){try{return await W({http:e,path:t,body:a,fetchOptions:n})}catch(r){if(!Q(r,t))throw r;return await W({http:e,path:s,body:a,fetchOptions:n})}}async function W({http:e,path:t,body:s,fetchOptions:a}={}){return await e.post(t,s,{...a||{}})}function Q(e,t){return!t||!t.startsWith(`${N(J)}/`)?!1:(e?.statusCode??e?.status)===404}function P(e,{errorPrefix:t,hadAccessToken:s,accessToken:a,skipSessionExpiry:n}={}){const r=typeof e?.message=="string"?e.message.trim():"",l=typeof e?.error=="string"?e.error.trim():"",o=r||l||String(e||"Unknown error"),c=_e(o),u=new Error(t?`${t}: ${c}`:c);u.cause=e;const i=e?.statusCode??e?.status;return V({status:i,hadAccessToken:s,accessToken:a,skipSessionExpiry:n})&&U(),typeof i=="number"&&(u.status=i,u.statusCode=i),u.retryable=ve(i)||Me(o),c!==o&&(u.originalMessage=o),e?.nextActions&&(u.nextActions=e.nextActions),e?.error&&(u.error=e.error),u}function I({hadAccessToken:e,accessToken:t,skipSessionExpiry:s}={}){return s||!e||!x(t)?!1:Ae(t)}function V({status:e,hadAccessToken:t,accessToken:s,skipSessionExpiry:a}={}){return e!==401?!1:I({hadAccessToken:t,accessToken:s,skipSessionExpiry:a})}function Se({hadAccessToken:e,accessToken:t}={}){return I({hadAccessToken:e,accessToken:t})}function H({hadAccessToken:e,accessToken:t}={}){Se({hadAccessToken:e,accessToken:t})&&me()}function _e(e){return Y(e)?be:String(e||"Unknown error")}function Y(e){const t=String(e||"").toLowerCase();return t?!!(t.includes("deno:")||t.includes("deno")||t.includes("econnreset")||t.includes("econnrefused")||t.includes("etimedout")||t.includes("timeout")&&t.includes("request")||t.includes("upstream")&&(t.includes("deno")||t.includes("connect"))):!1}function Z({status:e,requestKind:t,hadAccessToken:s,accessToken:a}={}){return e!==401||t!==y.business?!1:I({hadAccessToken:s,accessToken:a})}async function ee(){return z||(z=ye.auth.getCurrentSession().then(({data:e})=>e?.session??null).catch(()=>null).finally(()=>{z=null}),z)}function ve(e){return e===502||e===503||e===504}function Me(e){const t=String(e||"").toLowerCase();return t?!!(Y(t)||t.includes("econnreset")||t.includes("econnrefused")||t.includes("etimedout")||t.includes("timeout")||t.includes("networkerror")||t.includes("failed to fetch")||t.includes("socket hang up")||t.includes("connection reset")):!1}function te(e,t){const a=(t||"GET").toUpperCase()==="GET"?{maxRetries:2,baseDelayMs:300,maxDelayMs:1500,jitterRatio:.2}:{maxRetries:0,baseDelayMs:0,maxDelayMs:0,jitterRatio:0};if(e==null)return a;if(e===!1)return{maxRetries:0,baseDelayMs:0,maxDelayMs:0,jitterRatio:0};const n=F(e.maxRetries??a.maxRetries,0,10),r=F(e.baseDelayMs??a.baseDelayMs,50,6e4),l=F(e.maxDelayMs??a.maxDelayMs,r,12e4),o=typeof e.jitterRatio=="number"?Math.max(0,Math.min(.5,e.jitterRatio)):a.jitterRatio;return{maxRetries:n,baseDelayMs:r,maxDelayMs:l,jitterRatio:o}}function x(e){return!!pe(e)}function Ae(e){if(!x(e))return!1;const t=e.split(".");return t.length!==3?!1:t.every(s=>/^[A-Za-z0-9_-]+$/.test(s))}function se({err:e,attempt:t,retryOptions:s}={}){return!s||s.maxRetries<=0||t>=s.maxRetries?!1:!!(e&&e.retryable)}function ae({retryOptions:e,attempt:t}={}){if(!e||e.maxRetries<=0)return 0;const s=e.baseDelayMs*Math.pow(2,t),a=Math.min(e.maxDelayMs,s),n=a*e.jitterRatio*Math.random();return Math.round(a+n)}function F(e,t,s){const a=Number(e);return Number.isFinite(a)?Math.min(s,Math.max(t,Math.floor(a))):t}function ne(e){return!e||e<=0?Promise.resolve():new Promise(t=>setTimeout(t,e))}export{Le as a,Re as b,qe as c,Be as d,Ee as e,He as f,Fe as g,De as h,Ve as i,Ie as j,Ce as k,Ue as l,xe as p,Ne as r,ze as s,je as t};
|
|
1
|
+
import{b as p,al as G,d as ie,am as j,an as U,ao as ce,ap as ue,aq as le,ar as fe,as as de,at as ge,au as ke,a6 as pe,av as me,aw as ye,ax as he}from"./main-DFYFVRqg.js";const be="Backend runtime unavailable (InsForge). Please retry later.",g={usageSummary:"vibeusage-usage-summary",usageDaily:"vibeusage-usage-daily",usageHourly:"vibeusage-usage-hourly",usageMonthly:"vibeusage-usage-monthly",usageHeatmap:"vibeusage-usage-heatmap",usageModelBreakdown:"vibeusage-usage-model-breakdown",projectUsageSummary:"vibeusage-project-usage-summary",leaderboard:"vibeusage-leaderboard",leaderboardProfile:"vibeusage-leaderboard-profile",userStatus:"vibeusage-user-status",linkCodeInit:"vibeusage-link-code-init",publicViewProfile:"vibeusage-public-view-profile",publicVisibility:"vibeusage-public-visibility"},J="/functions",we="/api/functions",y={business:"business",probe:"probe"};let z=null;async function d(e){return await ie(e)}async function xe({baseUrl:e,accessToken:t,signal:s}={}){const a=await d(t),n=he(new Date);return await m({baseUrl:e,accessToken:a,slug:g.usageSummary,params:{from:n,to:n},fetchOptions:{cache:"no-store",signal:s},retry:!1,requestKind:y.probe}),{status:200}}async function Ee({baseUrl:e,accessToken:t,from:s,to:a,source:n,model:r,timeZone:l,tzOffsetMinutes:o,rolling:c=!1}={}){const u=await d(t);if(p())return ue({from:s,to:a,seed:u,rolling:c});const i=M({timeZone:l,tzOffsetMinutes:o}),f=A({source:n,model:r}),_=c?{rolling:"1"}:{};return m({baseUrl:e,accessToken:u,slug:g.usageSummary,params:{from:s,to:a,...f,...i,..._}})}async function Re({baseUrl:e,accessToken:t,from:s,to:a,source:n,limit:r,timeZone:l,tzOffsetMinutes:o}={}){const c=await d(t);if(p())return fe({seed:c,limit:r});const u=M({timeZone:l,tzOffsetMinutes:o}),f={...A({source:n}),...u};return s&&(f.from=s),a&&(f.to=a),r!=null&&(f.limit=String(r)),m({baseUrl:e,accessToken:c,slug:g.projectUsageSummary,params:f})}async function Ce({baseUrl:e,accessToken:t,period:s,metric:a,limit:n,offset:r}={}){const l=await d(t);if(p())return G({seed:l,period:s,metric:a,limit:n,offset:r});const c=(typeof s=="string"?s:"week").trim().toLowerCase(),i={period:c==="month"||c==="total"||c==="week"?c:"week"};return a&&(i.metric=String(a)),n!=null&&(i.limit=String(n)),r!=null&&(i.offset=String(r)),m({baseUrl:e,accessToken:l,slug:g.leaderboard,params:i})}async function De({baseUrl:e,accessToken:t}={}){const s=await d(t);return p()?{enabled:!1,updated_at:null,share_token:null}:m({baseUrl:e,accessToken:s,slug:g.publicVisibility})}async function ze({baseUrl:e,accessToken:t,enabled:s}={}){const a=await d(t);return p()?{enabled:!!s,updated_at:new Date().toISOString(),share_token:s?"pv1-mock-token":null}:X({baseUrl:e,accessToken:a,slug:g.publicVisibility,body:{enabled:!!s}})}async function Ue({baseUrl:e,accessToken:t,userId:s,period:a}={}){const n=await d(t);if(p()){const r=G({seed:n,period:a,metric:"all",limit:250,offset:0}),o=(Array.isArray(r?.entries)?r.entries:[]).find(c=>c?.user_id===s)||null;return{period:r?.period??"week",from:r?.from??null,to:r?.to??null,generated_at:r?.generated_at??new Date().toISOString(),entry:o?{user_id:o.user_id??null,display_name:o.display_name??null,avatar_url:o.avatar_url??null,rank:o.rank??null,gpt_tokens:o.gpt_tokens??"0",claude_tokens:o.claude_tokens??"0",other_tokens:o.other_tokens??"0",total_tokens:o.total_tokens??"0"}:null}}return m({baseUrl:e,accessToken:n,slug:g.leaderboardProfile,params:{user_id:String(s||""),period:String(a||"")}})}async function Ie({baseUrl:e,accessToken:t}={}){const s=await d(t);if(p()){const a=new Date().toISOString();return{user_id:"mock-user",created_at:a,pro:{active:!0,sources:["mock"],expires_at:null,partial:!1,as_of:a},subscriptions:{partial:!1,as_of:a,items:[{tool:"codex",provider:"openai",product:"chatgpt",plan_type:"pro",updated_at:a},{tool:"claude",provider:"anthropic",product:"subscription",plan_type:"max",rate_limit_tier:"default_claude_max_5x",updated_at:a}]},install:{partial:!1,as_of:a,has_active_device_token:!1,has_active_device:!1,active_device_tokens:0,active_devices:0,latest_token_activity_at:null,latest_device_seen_at:null}}}return m({baseUrl:e,accessToken:s,slug:g.userStatus})}async function je({signal:e}={}){const t=await fetch("/functions/vibeusage-local-sync",{method:"POST",headers:{Accept:"application/json"},cache:"no-store",signal:e}),s=await t.json().catch(()=>({ok:!1,error:`Local sync request failed with HTTP ${t.status}`}));if(!t.ok||s?.ok===!1){const a=s?.error||s?.message||`Local sync request failed with HTTP ${t.status}`,n=new Error(a);throw n.status=t.status,n.statusCode=t.status,n.payload=s,n}return s}async function He({baseUrl:e,accessToken:t,from:s,to:a,source:n,timeZone:r,tzOffsetMinutes:l}={}){const o=await d(t);if(p())return le({from:s,to:a,seed:o});const c=M({timeZone:r,tzOffsetMinutes:l}),u=A({source:n});return m({baseUrl:e,accessToken:o,slug:g.usageModelBreakdown,params:{from:s,to:a,...u,...c}})}async function Le({baseUrl:e,accessToken:t,from:s,to:a,source:n,model:r,timeZone:l,tzOffsetMinutes:o}={}){const c=await d(t);if(p())return ce({from:s,to:a,seed:c});const u=M({timeZone:l,tzOffsetMinutes:o}),i=A({source:n,model:r});return m({baseUrl:e,accessToken:c,slug:g.usageDaily,params:{from:s,to:a,...i,...u}})}async function qe({baseUrl:e,accessToken:t,day:s,source:a,model:n,timeZone:r,tzOffsetMinutes:l}={}){const o=await d(t);if(p())return de({day:s,seed:o});const c=M({timeZone:r,tzOffsetMinutes:l}),u=A({source:a,model:n});return m({baseUrl:e,accessToken:o,slug:g.usageHourly,params:s?{day:s,...u,...c}:{...u,...c}})}async function Be({baseUrl:e,accessToken:t,months:s,to:a,source:n,model:r,timeZone:l,tzOffsetMinutes:o}={}){const c=await d(t);if(p())return ge({months:s,to:a,seed:c});const u=M({timeZone:l,tzOffsetMinutes:o}),i=A({source:n,model:r});return m({baseUrl:e,accessToken:c,slug:g.usageMonthly,params:{...s?{months:String(s)}:{},...a?{to:a}:{},...i,...u}})}async function Fe({baseUrl:e,accessToken:t,weeks:s,to:a,weekStartsOn:n,source:r,model:l,timeZone:o,tzOffsetMinutes:c}={}){const u=await d(t);if(p())return ke({weeks:s,to:a,weekStartsOn:n,seed:u});const i=M({timeZone:o,tzOffsetMinutes:c}),f=A({source:r,model:l});return m({baseUrl:e,accessToken:u,slug:g.usageHeatmap,params:{weeks:String(s),to:a,week_starts_on:n,...f,...i}})}async function Ne({baseUrl:e,accessToken:t}={}){const s=await d(t);return p()?{link_code:"mock_link_code",expires_at:new Date(Date.now()+10*6e4).toISOString()}:X({baseUrl:e,accessToken:s,slug:g.linkCodeInit,body:{}})}async function Ve({baseUrl:e,accessToken:t}={}){const s=await d(t);return m({baseUrl:e,accessToken:s,slug:g.publicViewProfile})}function M({timeZone:e,tzOffsetMinutes:t}={}){const s={},a=typeof e=="string"?e.trim():"";return a&&(s.tz=a),Number.isFinite(t)&&(s.tz_offset_minutes=String(Math.trunc(t))),s}function A({source:e,model:t}={}){const s={},a=typeof e=="string"?e.trim().toLowerCase():"";a&&(s.source=a);const n=typeof t=="string"?t.trim():"";return n&&(s.model=n),s}async function m({baseUrl:e,accessToken:t,slug:s,params:a,fetchOptions:n,errorPrefix:r,retry:l,requestKind:o=y.business,skipSessionExpiry:c=!1,allowRefresh:u=!0}={}){let i=await d(t),f=x(i),_=j({baseUrl:e,accessToken:i??void 0}).getHttpClient();const E=te(l,"GET"),T=c?y.probe:o;let v=0;const{primaryPath:R,fallbackPath:C}=O(s);for(;;)try{const S=await $({http:_,primaryPath:R,fallbackPath:C,params:a,fetchOptions:n});return H({hadAccessToken:f,accessToken:i}),S}catch(S){const h=S;if(h?.name==="AbortError")throw S;let b=null;const L=h?.statusCode??h?.status;if(u&&Z({status:L,requestKind:T,hadAccessToken:f,accessToken:i})){const k=(await ee())?.accessToken??null;if(x(k)){const D=j({baseUrl:e,accessToken:k}).getHttpClient();i=k,f=!0,_=D;try{const w=await $({http:D,primaryPath:R,fallbackPath:C,params:a,fetchOptions:n});return H({hadAccessToken:!0,accessToken:k}),w}catch(w){const B=w?.statusCode??w?.status;V({status:B,hadAccessToken:!0,accessToken:k,skipSessionExpiry:T===y.probe})&&U(),b=P(w,{errorPrefix:r,hadAccessToken:!0,accessToken:k,skipSessionExpiry:!0})}}else I({hadAccessToken:f,accessToken:i,skipSessionExpiry:T===y.probe})&&U();b??=P(h,{errorPrefix:r,hadAccessToken:f,accessToken:i,skipSessionExpiry:!0})}if(b??=P(h,{errorPrefix:r,hadAccessToken:f,accessToken:i,skipSessionExpiry:T===y.probe}),!se({err:b,attempt:v,retryOptions:E}))throw b;const q=ae({retryOptions:E,attempt:v});await ne(q),v+=1}}async function X({baseUrl:e,accessToken:t,slug:s,body:a,fetchOptions:n,errorPrefix:r,retry:l,requestKind:o=y.business,skipSessionExpiry:c=!1,allowRefresh:u=!0}={}){let i=await d(t),f=x(i),_=j({baseUrl:e,accessToken:i??void 0}).getHttpClient();const E=te(l,"POST"),T=c?y.probe:o;let v=0;const{primaryPath:R,fallbackPath:C}=O(s);for(;;)try{const S=await K({http:_,primaryPath:R,fallbackPath:C,body:a,fetchOptions:n});return H({hadAccessToken:f,accessToken:i}),S}catch(S){const h=S;if(h?.name==="AbortError")throw S;let b=null;const L=h?.statusCode??h?.status;if(u&&Z({status:L,requestKind:T,hadAccessToken:f,accessToken:i})){const k=(await ee())?.accessToken??null;if(x(k)){const D=j({baseUrl:e,accessToken:k}).getHttpClient();i=k,f=!0,_=D;try{const w=await K({http:D,primaryPath:R,fallbackPath:C,body:a,fetchOptions:n});return H({hadAccessToken:!0,accessToken:k}),w}catch(w){const B=w?.statusCode??w?.status;V({status:B,hadAccessToken:!0,accessToken:k,skipSessionExpiry:T===y.probe})&&U(),b=P(w,{errorPrefix:r,hadAccessToken:!0,accessToken:k,skipSessionExpiry:!0})}}else I({hadAccessToken:f,accessToken:i,skipSessionExpiry:T===y.probe})&&U();b??=P(h,{errorPrefix:r,hadAccessToken:f,accessToken:i,skipSessionExpiry:!0})}if(b??=P(h,{errorPrefix:r,hadAccessToken:f,accessToken:i,skipSessionExpiry:T===y.probe}),!se({err:b,attempt:v,retryOptions:E}))throw b;const q=ae({retryOptions:E,attempt:v});await ne(q),v+=1}}function O(e){const t=Te(e),s=`${N(J)}/${t}`,a=`${N(we)}/${t}`;return{primaryPath:s,fallbackPath:a}}function Te(e){return(typeof e=="string"?e.trim():"").replace(/^\/+/,"")}function N(e){const t=typeof e=="string"?e.trim():"";return t.endsWith("/")?t.slice(0,-1):t}async function $({http:e,primaryPath:t,fallbackPath:s,params:a,fetchOptions:n}={}){try{return await e.get(t,{params:a,...n||{}})}catch(r){if(!Q(r,t))throw r;return await e.get(s,{params:a,...n||{}})}}async function K({http:e,primaryPath:t,fallbackPath:s,body:a,fetchOptions:n}={}){try{return await W({http:e,path:t,body:a,fetchOptions:n})}catch(r){if(!Q(r,t))throw r;return await W({http:e,path:s,body:a,fetchOptions:n})}}async function W({http:e,path:t,body:s,fetchOptions:a}={}){return await e.post(t,s,{...a||{}})}function Q(e,t){return!t||!t.startsWith(`${N(J)}/`)?!1:(e?.statusCode??e?.status)===404}function P(e,{errorPrefix:t,hadAccessToken:s,accessToken:a,skipSessionExpiry:n}={}){const r=typeof e?.message=="string"?e.message.trim():"",l=typeof e?.error=="string"?e.error.trim():"",o=r||l||String(e||"Unknown error"),c=_e(o),u=new Error(t?`${t}: ${c}`:c);u.cause=e;const i=e?.statusCode??e?.status;return V({status:i,hadAccessToken:s,accessToken:a,skipSessionExpiry:n})&&U(),typeof i=="number"&&(u.status=i,u.statusCode=i),u.retryable=ve(i)||Me(o),c!==o&&(u.originalMessage=o),e?.nextActions&&(u.nextActions=e.nextActions),e?.error&&(u.error=e.error),u}function I({hadAccessToken:e,accessToken:t,skipSessionExpiry:s}={}){return s||!e||!x(t)?!1:Ae(t)}function V({status:e,hadAccessToken:t,accessToken:s,skipSessionExpiry:a}={}){return e!==401?!1:I({hadAccessToken:t,accessToken:s,skipSessionExpiry:a})}function Se({hadAccessToken:e,accessToken:t}={}){return I({hadAccessToken:e,accessToken:t})}function H({hadAccessToken:e,accessToken:t}={}){Se({hadAccessToken:e,accessToken:t})&&me()}function _e(e){return Y(e)?be:String(e||"Unknown error")}function Y(e){const t=String(e||"").toLowerCase();return t?!!(t.includes("deno:")||t.includes("deno")||t.includes("econnreset")||t.includes("econnrefused")||t.includes("etimedout")||t.includes("timeout")&&t.includes("request")||t.includes("upstream")&&(t.includes("deno")||t.includes("connect"))):!1}function Z({status:e,requestKind:t,hadAccessToken:s,accessToken:a}={}){return e!==401||t!==y.business?!1:I({hadAccessToken:s,accessToken:a})}async function ee(){return z||(z=ye.auth.getCurrentSession().then(({data:e})=>e?.session??null).catch(()=>null).finally(()=>{z=null}),z)}function ve(e){return e===502||e===503||e===504}function Me(e){const t=String(e||"").toLowerCase();return t?!!(Y(t)||t.includes("econnreset")||t.includes("econnrefused")||t.includes("etimedout")||t.includes("timeout")||t.includes("networkerror")||t.includes("failed to fetch")||t.includes("socket hang up")||t.includes("connection reset")):!1}function te(e,t){const a=(t||"GET").toUpperCase()==="GET"?{maxRetries:2,baseDelayMs:300,maxDelayMs:1500,jitterRatio:.2}:{maxRetries:0,baseDelayMs:0,maxDelayMs:0,jitterRatio:0};if(e==null)return a;if(e===!1)return{maxRetries:0,baseDelayMs:0,maxDelayMs:0,jitterRatio:0};const n=F(e.maxRetries??a.maxRetries,0,10),r=F(e.baseDelayMs??a.baseDelayMs,50,6e4),l=F(e.maxDelayMs??a.maxDelayMs,r,12e4),o=typeof e.jitterRatio=="number"?Math.max(0,Math.min(.5,e.jitterRatio)):a.jitterRatio;return{maxRetries:n,baseDelayMs:r,maxDelayMs:l,jitterRatio:o}}function x(e){return!!pe(e)}function Ae(e){if(!x(e))return!1;const t=e.split(".");return t.length!==3?!1:t.every(s=>/^[A-Za-z0-9_-]+$/.test(s))}function se({err:e,attempt:t,retryOptions:s}={}){return!s||s.maxRetries<=0||t>=s.maxRetries?!1:!!(e&&e.retryable)}function ae({retryOptions:e,attempt:t}={}){if(!e||e.maxRetries<=0)return 0;const s=e.baseDelayMs*Math.pow(2,t),a=Math.min(e.maxDelayMs,s),n=a*e.jitterRatio*Math.random();return Math.round(a+n)}function F(e,t,s){const a=Number(e);return Number.isFinite(a)?Math.min(s,Math.max(t,Math.floor(a))):t}function ne(e){return!e||e<=0?Promise.resolve():new Promise(t=>setTimeout(t,e))}export{Le as a,Re as b,qe as c,Be as d,Ee as e,He as f,Fe as g,De as h,Ve as i,Ie as j,Ce as k,Ue as l,xe as p,Ne as r,ze as s,je as t};
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
]
|
|
108
108
|
}
|
|
109
109
|
</script>
|
|
110
|
-
<script type="module" crossorigin src="/assets/main-
|
|
110
|
+
<script type="module" crossorigin src="/assets/main-DFYFVRqg.js"></script>
|
|
111
111
|
<link rel="stylesheet" crossorigin href="/assets/main-ByQlanyj.css">
|
|
112
112
|
</head>
|
|
113
113
|
<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-DFYFVRqg.js"></script>
|
|
55
55
|
<link rel="stylesheet" crossorigin href="/assets/main-ByQlanyj.css">
|
|
56
56
|
</head>
|
|
57
57
|
<body>
|
package/package.json
CHANGED
package/src/lib/local-api.js
CHANGED
|
@@ -6,6 +6,106 @@ const { spawn } = require("node:child_process");
|
|
|
6
6
|
const SYNC_TIMEOUT_MS = 120_000;
|
|
7
7
|
const TRACKER_BIN = path.resolve(__dirname, "../../bin/tracker.js");
|
|
8
8
|
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Per-model pricing (USD per million tokens)
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
// Rates per million tokens (USD). Sources: LiteLLM, OpenAI, Google, OpenRouter.
|
|
14
|
+
const MODEL_PRICING = {
|
|
15
|
+
// ── Anthropic Claude ──
|
|
16
|
+
"claude-opus-4-6": { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 },
|
|
17
|
+
"claude-opus-4-5-20250414": { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 },
|
|
18
|
+
"claude-sonnet-4-6": { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 },
|
|
19
|
+
"claude-sonnet-4-5-20250514": { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 },
|
|
20
|
+
"claude-sonnet-4-20250514": { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 },
|
|
21
|
+
"claude-haiku-4-5-20251001": { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 },
|
|
22
|
+
"claude-3-5-sonnet-20241022": { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 },
|
|
23
|
+
"claude-3-5-haiku-20241022": { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 },
|
|
24
|
+
// ── OpenAI GPT / Codex ──
|
|
25
|
+
"gpt-5": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
26
|
+
"gpt-5-fast": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
27
|
+
"gpt-5-high": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
28
|
+
"gpt-5-high-fast": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
29
|
+
"gpt-5-codex": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
30
|
+
"gpt-5-codex-high-fast": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
31
|
+
"gpt-5.1-codex": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
32
|
+
"gpt-5.1-codex-mini": { input: 0.25, output: 2, cache_read: 0.025 },
|
|
33
|
+
"gpt-5.1-codex-max": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
34
|
+
"gpt-5.1-codex-max-high-fast": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
35
|
+
"gpt-5.1-codex-max-xhigh-fast": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
36
|
+
"gpt-5.1-codex-high": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
37
|
+
"gpt-5.1-codex-max-high": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
38
|
+
"gpt-5.2": { input: 1.75, output: 14, cache_read: 0.175 },
|
|
39
|
+
"gpt-5.2-high": { input: 1.75, output: 14, cache_read: 0.175 },
|
|
40
|
+
"gpt-5.2-high-fast": { input: 1.75, output: 14, cache_read: 0.175 },
|
|
41
|
+
"gpt-5.2-codex": { input: 1.75, output: 14, cache_read: 0.175 },
|
|
42
|
+
"gpt-5.2-codex-high": { input: 1.75, output: 14, cache_read: 0.175 },
|
|
43
|
+
"gpt-5.3-codex": { input: 1.75, output: 14, cache_read: 0.175 },
|
|
44
|
+
"gpt-5.3-codex-high": { input: 1.75, output: 14, cache_read: 0.175 },
|
|
45
|
+
"gpt-5.4": { input: 2.5, output: 15, cache_read: 0.25 },
|
|
46
|
+
"gpt-5.4-mini": { input: 0.75, output: 4.5, cache_read: 0.075 },
|
|
47
|
+
"gpt-5.4-medium": { input: 1.5, output: 10, cache_read: 0.15 },
|
|
48
|
+
"o3": { input: 2, output: 8, cache_read: 0.5 },
|
|
49
|
+
// ── Google Gemini (official: ai.google.dev/pricing) ──
|
|
50
|
+
"gemini-2.5-pro": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
51
|
+
"gemini-2.5-pro-preview-06-05": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
52
|
+
"gemini-2.5-pro-preview-05-06": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
53
|
+
"gemini-2.5-flash": { input: 0.3, output: 2.5, cache_read: 0.03 },
|
|
54
|
+
"gemini-3-flash-preview": { input: 0.5, output: 3, cache_read: 0.05 },
|
|
55
|
+
"gemini-3-pro-preview": { input: 2, output: 12, cache_read: 0.2 },
|
|
56
|
+
"gemini-3.1-pro-preview": { input: 2, output: 12, cache_read: 0.2 },
|
|
57
|
+
// ── Cursor Composer ──
|
|
58
|
+
"composer-1": { input: 1.25, output: 10, cache_read: 0.125 },
|
|
59
|
+
"composer-1.5": { input: 3.5, output: 17.5, cache_read: 0.35 },
|
|
60
|
+
"composer-2": { input: 0.5, output: 2.5, cache_read: 0.2 },
|
|
61
|
+
"composer-2-fast": { input: 1.5, output: 7.5, cache_read: 0.15 },
|
|
62
|
+
// ── Moonshot Kimi (official: platform.moonshot.ai) ──
|
|
63
|
+
"kimi-for-coding": { input: 0.6, output: 2, cache_read: 0.15 },
|
|
64
|
+
"kimi-k2.5": { input: 0.6, output: 2, cache_read: 0.15 },
|
|
65
|
+
"kimi-k2.5-free": { input: 0, output: 0, cache_read: 0 },
|
|
66
|
+
// ── Misc / Free ──
|
|
67
|
+
"glm-4.7-free": { input: 0, output: 0, cache_read: 0 },
|
|
68
|
+
"nemotron-3-super-free": { input: 0, output: 0, cache_read: 0 },
|
|
69
|
+
"mimo-v2-pro-free": { input: 0, output: 0, cache_read: 0 },
|
|
70
|
+
"minimax-m2.1-free": { input: 0, output: 0, cache_read: 0 },
|
|
71
|
+
"MiniMax-M2.1": { input: 0.5, output: 3, cache_read: 0.05 },
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const ZERO_PRICING = { input: 0, output: 0, cache_read: 0, cache_write: 0 };
|
|
75
|
+
|
|
76
|
+
function getModelPricing(model) {
|
|
77
|
+
if (!model) return ZERO_PRICING;
|
|
78
|
+
const exact = MODEL_PRICING[model];
|
|
79
|
+
if (exact) return exact;
|
|
80
|
+
// Fuzzy match for common model families
|
|
81
|
+
const lower = model.toLowerCase();
|
|
82
|
+
if (lower.includes("opus")) return MODEL_PRICING["claude-opus-4-6"];
|
|
83
|
+
if (lower.includes("haiku")) return MODEL_PRICING["claude-haiku-4-5-20251001"];
|
|
84
|
+
if (lower.includes("sonnet")) return MODEL_PRICING["claude-sonnet-4-6"];
|
|
85
|
+
if (lower.includes("gpt-5.4")) return MODEL_PRICING["gpt-5.4"];
|
|
86
|
+
if (lower.includes("gpt-5.3")) return MODEL_PRICING["gpt-5.3-codex"];
|
|
87
|
+
if (lower.includes("gpt-5.2")) return MODEL_PRICING["gpt-5.2"];
|
|
88
|
+
if (lower.includes("gpt-5.1")) return MODEL_PRICING["gpt-5.1-codex"];
|
|
89
|
+
if (lower.includes("gpt-5")) return MODEL_PRICING["gpt-5"];
|
|
90
|
+
if (lower.includes("gemini-3")) return MODEL_PRICING["gemini-3-flash-preview"];
|
|
91
|
+
if (lower.includes("gemini-2.5")) return MODEL_PRICING["gemini-2.5-pro"];
|
|
92
|
+
if (lower.includes("kimi")) return MODEL_PRICING["kimi-k2.5"];
|
|
93
|
+
if (lower.includes("composer")) return MODEL_PRICING["composer-1"];
|
|
94
|
+
return ZERO_PRICING;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function computeRowCost(row) {
|
|
98
|
+
const pricing = getModelPricing(row.model);
|
|
99
|
+
return (
|
|
100
|
+
((row.input_tokens || 0) * (pricing.input || 0) +
|
|
101
|
+
(row.output_tokens || 0) * (pricing.output || 0) +
|
|
102
|
+
(row.cached_input_tokens || 0) * (pricing.cache_read || 0) +
|
|
103
|
+
(row.cache_creation_input_tokens || 0) * (pricing.cache_write || 0) +
|
|
104
|
+
(row.reasoning_output_tokens || 0) * (pricing.output || 0)) /
|
|
105
|
+
1_000_000
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
9
109
|
// ---------------------------------------------------------------------------
|
|
10
110
|
// Queue data helpers
|
|
11
111
|
// ---------------------------------------------------------------------------
|
|
@@ -19,7 +119,15 @@ function readQueueData(queuePath) {
|
|
|
19
119
|
try {
|
|
20
120
|
const raw = fs.readFileSync(queuePath, "utf8");
|
|
21
121
|
const lines = raw.split("\n").filter((l) => l.trim());
|
|
22
|
-
|
|
122
|
+
const parsed = lines.map((l) => JSON.parse(l));
|
|
123
|
+
// Deduplicate: each sync appends cumulative totals per bucket, so for
|
|
124
|
+
// each (source, model, hour_start) keep only the latest (last) entry.
|
|
125
|
+
const seen = new Map();
|
|
126
|
+
for (const row of parsed) {
|
|
127
|
+
const key = `${row.source || ""}|${row.model || ""}|${row.hour_start || ""}`;
|
|
128
|
+
seen.set(key, row);
|
|
129
|
+
}
|
|
130
|
+
return Array.from(seen.values());
|
|
23
131
|
} catch (_e) {
|
|
24
132
|
return [];
|
|
25
133
|
}
|
|
@@ -36,6 +144,7 @@ function aggregateByDay(rows) {
|
|
|
36
144
|
day,
|
|
37
145
|
total_tokens: 0,
|
|
38
146
|
billable_total_tokens: 0,
|
|
147
|
+
total_cost_usd: 0,
|
|
39
148
|
input_tokens: 0,
|
|
40
149
|
output_tokens: 0,
|
|
41
150
|
cached_input_tokens: 0,
|
|
@@ -47,6 +156,7 @@ function aggregateByDay(rows) {
|
|
|
47
156
|
const a = byDay.get(day);
|
|
48
157
|
a.total_tokens += row.total_tokens || 0;
|
|
49
158
|
a.billable_total_tokens += row.total_tokens || 0;
|
|
159
|
+
a.total_cost_usd += computeRowCost(row);
|
|
50
160
|
a.input_tokens += row.input_tokens || 0;
|
|
51
161
|
a.output_tokens += row.output_tokens || 0;
|
|
52
162
|
a.cached_input_tokens += row.cached_input_tokens || 0;
|
|
@@ -358,6 +468,7 @@ function createLocalApiHandler({ queuePath }) {
|
|
|
358
468
|
(acc, r) => {
|
|
359
469
|
acc.total_tokens += r.total_tokens;
|
|
360
470
|
acc.billable_total_tokens += r.billable_total_tokens;
|
|
471
|
+
acc.total_cost_usd += r.total_cost_usd || 0;
|
|
361
472
|
acc.input_tokens += r.input_tokens;
|
|
362
473
|
acc.output_tokens += r.output_tokens;
|
|
363
474
|
acc.cached_input_tokens += r.cached_input_tokens;
|
|
@@ -366,9 +477,9 @@ function createLocalApiHandler({ queuePath }) {
|
|
|
366
477
|
acc.conversation_count += r.conversation_count;
|
|
367
478
|
return acc;
|
|
368
479
|
},
|
|
369
|
-
{ total_tokens: 0, billable_total_tokens: 0, input_tokens: 0, output_tokens: 0, cached_input_tokens: 0, cache_creation_input_tokens: 0, reasoning_output_tokens: 0, conversation_count: 0 },
|
|
480
|
+
{ total_tokens: 0, billable_total_tokens: 0, total_cost_usd: 0, input_tokens: 0, output_tokens: 0, cached_input_tokens: 0, cache_creation_input_tokens: 0, reasoning_output_tokens: 0, conversation_count: 0 },
|
|
370
481
|
);
|
|
371
|
-
const totalCost =
|
|
482
|
+
const totalCost = totals.total_cost_usd;
|
|
372
483
|
|
|
373
484
|
const today = new Date();
|
|
374
485
|
const todayStr = today.toISOString().slice(0, 10);
|
|
@@ -502,16 +613,27 @@ function createLocalApiHandler({ queuePath }) {
|
|
|
502
613
|
}
|
|
503
614
|
|
|
504
615
|
const sources = Array.from(bySource.values()).map((s) => {
|
|
505
|
-
s.totals.total_cost_usd = ((s.totals.total_tokens * 1.75) / 1_000_000).toFixed(6);
|
|
506
616
|
s.models = Array.from(s.models.values())
|
|
507
|
-
.map((m) =>
|
|
617
|
+
.map((m) => {
|
|
618
|
+
const p = getModelPricing(m.model);
|
|
619
|
+
const cost =
|
|
620
|
+
((m.totals.input_tokens || 0) * (p.input || 0) +
|
|
621
|
+
(m.totals.output_tokens || 0) * (p.output || 0) +
|
|
622
|
+
(m.totals.cached_input_tokens || 0) * (p.cache_read || 0) +
|
|
623
|
+
(m.totals.cache_creation_input_tokens || 0) * (p.cache_write || 0) +
|
|
624
|
+
(m.totals.reasoning_output_tokens || 0) * (p.output || 0)) /
|
|
625
|
+
1_000_000;
|
|
626
|
+
return { ...m, totals: { ...m.totals, total_cost_usd: cost.toFixed(6) } };
|
|
627
|
+
})
|
|
508
628
|
.sort((a, b) => b.totals.total_tokens - a.totals.total_tokens);
|
|
629
|
+
const sourceCost = s.models.reduce((sum, m) => sum + Number(m.totals.total_cost_usd), 0);
|
|
630
|
+
s.totals.total_cost_usd = sourceCost.toFixed(6);
|
|
509
631
|
return s;
|
|
510
632
|
});
|
|
511
633
|
|
|
512
634
|
json(res, {
|
|
513
635
|
from, to, days: 0, sources,
|
|
514
|
-
pricing: { model: "
|
|
636
|
+
pricing: { model: "per-model", pricing_mode: "per_token_type", source: "litellm", effective_from: new Date().toISOString().slice(0, 10) },
|
|
515
637
|
});
|
|
516
638
|
return true;
|
|
517
639
|
}
|
package/src/lib/rollout.js
CHANGED
|
@@ -206,7 +206,10 @@ async function parseClaudeIncremental({
|
|
|
206
206
|
const projectMetaCache = projectEnabled ? new Map() : null;
|
|
207
207
|
const publicRepoCache = projectEnabled ? new Map() : null;
|
|
208
208
|
const touchedBuckets = new Set();
|
|
209
|
-
|
|
209
|
+
// Persist seenMessageHashes across syncs to prevent cross-file duplicates
|
|
210
|
+
// (e.g. subagent file created after main session was already parsed).
|
|
211
|
+
const prevHashes = Array.isArray(cursors.claudeHashes) ? cursors.claudeHashes : [];
|
|
212
|
+
const seenMessageHashes = new Set(prevHashes);
|
|
210
213
|
const defaultSource = normalizeSourceInput(source) || "claude";
|
|
211
214
|
|
|
212
215
|
if (!cursors.files || typeof cursors.files !== "object") {
|
|
@@ -285,6 +288,10 @@ async function parseClaudeIncremental({
|
|
|
285
288
|
projectState.updatedAt = new Date().toISOString();
|
|
286
289
|
cursors.projectHourly = projectState;
|
|
287
290
|
}
|
|
291
|
+
// Persist message hashes for cross-sync dedup; cap at 100k entries to bound size.
|
|
292
|
+
const allHashes = Array.from(seenMessageHashes);
|
|
293
|
+
cursors.claudeHashes =
|
|
294
|
+
allHashes.length > 100_000 ? allHashes.slice(allHashes.length - 100_000) : allHashes;
|
|
288
295
|
|
|
289
296
|
return { filesProcessed, eventsAggregated, bucketsQueued, projectBucketsQueued };
|
|
290
297
|
}
|