tokentracker-cli 0.4.1 → 0.4.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/{DashboardPage-CSL_afwz.js → DashboardPage-Z2qqLFsJ.js} +1 -1
- package/dashboard/dist/assets/{LandingExtras-C8i8_OxM.js → LandingExtras-6NtvaOYM.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-T8OluU3F.js → LeaderboardPage-BV3NE1hK.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardProfilePage-D5ICOqlT.js → LeaderboardProfilePage-DM1XAgY4.js} +1 -1
- package/dashboard/dist/assets/{MatrixRain-DNKwhCXo.js → MatrixRain-B9PMNiTr.js} +1 -1
- package/dashboard/dist/assets/{MatrixShell-BZu61EY7.js → MatrixShell-Bjpqen-T.js} +1 -1
- package/dashboard/dist/assets/{main-DGPBnxuf.js → main-Bo8e66-R.js} +4 -4
- package/dashboard/dist/assets/{vibeusage-api-BpxQVR5V.js → vibeusage-api-CXScOK2G.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dashboard/dist/share.html +1 -1
- package/package.json +24 -1
- package/src/commands/sync.js +5 -2
- package/src/lib/browser-auth.js +108 -4
- package/src/lib/rollout.js +102 -8
|
@@ -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-DGPBnxuf.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-Bo8e66-R.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-Bo8e66-R.js"></script>
|
|
111
111
|
<link rel="stylesheet" crossorigin href="/assets/main-CmTD1xm_.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-Bo8e66-R.js"></script>
|
|
55
55
|
<link rel="stylesheet" crossorigin href="/assets/main-CmTD1xm_.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.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Token usage tracker for AI agent CLIs (Claude Code, Codex, Cursor, Kiro, Gemini, OpenCode, OpenClaw)",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"copy:push": "node scripts/copy-sync.cjs push",
|
|
31
31
|
"dashboard:build": "npm --prefix dashboard run build",
|
|
32
32
|
"dashboard:dev": "npm --prefix dashboard run dev",
|
|
33
|
+
"dashboard:open": "bash scripts/open-dashboard.sh",
|
|
33
34
|
"dashboard:preview": "npm --prefix dashboard run preview",
|
|
34
35
|
"dev:shim": "node scripts/dev-bin-shim.cjs",
|
|
35
36
|
"graph:auto-index": "node scripts/graph/auto-index.cjs",
|
|
@@ -52,6 +53,28 @@
|
|
|
52
53
|
"bundledDependencies": [
|
|
53
54
|
"@insforge/sdk"
|
|
54
55
|
],
|
|
56
|
+
"repository": {
|
|
57
|
+
"type": "git",
|
|
58
|
+
"url": "git+https://github.com/mm7894215/TokenTracker.git"
|
|
59
|
+
},
|
|
60
|
+
"homepage": "https://github.com/mm7894215/TokenTracker#readme",
|
|
61
|
+
"bugs": {
|
|
62
|
+
"url": "https://github.com/mm7894215/TokenTracker/issues"
|
|
63
|
+
},
|
|
64
|
+
"keywords": [
|
|
65
|
+
"token",
|
|
66
|
+
"tracker",
|
|
67
|
+
"ai",
|
|
68
|
+
"claude",
|
|
69
|
+
"codex",
|
|
70
|
+
"cursor",
|
|
71
|
+
"gemini",
|
|
72
|
+
"kiro",
|
|
73
|
+
"opencode",
|
|
74
|
+
"usage",
|
|
75
|
+
"cost"
|
|
76
|
+
],
|
|
77
|
+
"license": "MIT",
|
|
55
78
|
"engines": {
|
|
56
79
|
"node": ">=20"
|
|
57
80
|
}
|
package/src/commands/sync.js
CHANGED
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
listOpencodeMessageFiles,
|
|
13
13
|
readOpencodeDbMessages,
|
|
14
14
|
resolveKiroDbPath,
|
|
15
|
+
resolveKiroJsonlPath,
|
|
15
16
|
parseRolloutIncremental,
|
|
16
17
|
parseClaudeIncremental,
|
|
17
18
|
parseGeminiIncremental,
|
|
@@ -302,15 +303,17 @@ async function cmdSync(argv) {
|
|
|
302
303
|
}
|
|
303
304
|
}
|
|
304
305
|
|
|
305
|
-
// ── Kiro (SQLite-based) ──
|
|
306
|
+
// ── Kiro (SQLite-based, with JSONL fallback) ──
|
|
306
307
|
let kiroResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
307
308
|
const kiroDbPath = resolveKiroDbPath();
|
|
308
|
-
|
|
309
|
+
const kiroJsonlPath = resolveKiroJsonlPath();
|
|
310
|
+
if (fssync.existsSync(kiroDbPath) || fssync.existsSync(kiroJsonlPath)) {
|
|
309
311
|
if (progress?.enabled) {
|
|
310
312
|
progress.start(`Parsing Kiro ${renderBar(0)} | buckets 0`);
|
|
311
313
|
}
|
|
312
314
|
kiroResult = await parseKiroIncremental({
|
|
313
315
|
dbPath: kiroDbPath,
|
|
316
|
+
jsonlPath: kiroJsonlPath,
|
|
314
317
|
cursors,
|
|
315
318
|
queuePath,
|
|
316
319
|
onProgress: (p) => {
|
package/src/lib/browser-auth.js
CHANGED
|
@@ -133,16 +133,120 @@ async function startLocalCallbackServer({ callbackPath, timeoutMs, redirectUrl }
|
|
|
133
133
|
return { callbackUrl, waitForCallback };
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
function detectDefaultBrowser() {
|
|
137
|
+
try {
|
|
138
|
+
const raw = cp.execFileSync("defaults", [
|
|
139
|
+
"read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers",
|
|
140
|
+
], { encoding: "utf8", timeout: 3000 });
|
|
141
|
+
const match = raw.match(/https[\s\S]*?LSHandlerRoleAll\s*=\s*"([^"]+)"/);
|
|
142
|
+
if (!match) return null;
|
|
143
|
+
const bundleId = match[1].toLowerCase();
|
|
144
|
+
if (bundleId.includes("chrome")) return "Google Chrome";
|
|
145
|
+
if (bundleId.includes("safari")) return "Safari";
|
|
146
|
+
if (bundleId.includes("edgemac")) return "Microsoft Edge";
|
|
147
|
+
if (bundleId.includes("thebrowser") || bundleId.includes("arc")) return "Arc";
|
|
148
|
+
return null;
|
|
149
|
+
} catch (_e) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function buildBrowserList() {
|
|
155
|
+
const all = ["Google Chrome", "Safari", "Microsoft Edge", "Arc"];
|
|
156
|
+
const def = detectDefaultBrowser();
|
|
157
|
+
if (!def) return all;
|
|
158
|
+
return [def, ...all.filter((b) => b !== def)];
|
|
159
|
+
}
|
|
160
|
+
|
|
136
161
|
function openInBrowser(url) {
|
|
137
162
|
const platform = process.platform;
|
|
138
163
|
|
|
164
|
+
if (platform === "darwin") {
|
|
165
|
+
// On macOS, prefer reusing a matching tab in a supported running browser.
|
|
166
|
+
// Supported browsers are checked with the user's default browser first.
|
|
167
|
+
const browsers = buildBrowserList();
|
|
168
|
+
const listLiteral = browsers.map((b) => `"${b}"`).join(", ");
|
|
169
|
+
const script = `
|
|
170
|
+
tell application "System Events"
|
|
171
|
+
set browserList to {${listLiteral}}
|
|
172
|
+
set runningBrowser to ""
|
|
173
|
+
repeat with b in browserList
|
|
174
|
+
if (exists process (b as text)) then
|
|
175
|
+
set runningBrowser to (b as text)
|
|
176
|
+
exit repeat
|
|
177
|
+
end if
|
|
178
|
+
end repeat
|
|
179
|
+
end tell
|
|
180
|
+
|
|
181
|
+
if runningBrowser is "" then
|
|
182
|
+
open location "${url}"
|
|
183
|
+
else if runningBrowser is "Google Chrome" then
|
|
184
|
+
tell application "Google Chrome"
|
|
185
|
+
set found to false
|
|
186
|
+
repeat with w in windows
|
|
187
|
+
set tabIndex to 0
|
|
188
|
+
repeat with t in tabs of w
|
|
189
|
+
set tabIndex to tabIndex + 1
|
|
190
|
+
if URL of t starts with "${url}" then
|
|
191
|
+
set active tab index of w to tabIndex
|
|
192
|
+
set index of w to 1
|
|
193
|
+
reload t
|
|
194
|
+
activate
|
|
195
|
+
set found to true
|
|
196
|
+
exit repeat
|
|
197
|
+
end if
|
|
198
|
+
end repeat
|
|
199
|
+
if found then exit repeat
|
|
200
|
+
end repeat
|
|
201
|
+
if not found then
|
|
202
|
+
open location "${url}"
|
|
203
|
+
activate
|
|
204
|
+
end if
|
|
205
|
+
end tell
|
|
206
|
+
else if runningBrowser is "Safari" then
|
|
207
|
+
tell application "Safari"
|
|
208
|
+
set found to false
|
|
209
|
+
repeat with w in windows
|
|
210
|
+
set tabIndex to 0
|
|
211
|
+
repeat with t in tabs of w
|
|
212
|
+
set tabIndex to tabIndex + 1
|
|
213
|
+
if URL of t starts with "${url}" then
|
|
214
|
+
set current tab of w to t
|
|
215
|
+
set index of w to 1
|
|
216
|
+
do JavaScript "location.reload()" in t
|
|
217
|
+
activate
|
|
218
|
+
set found to true
|
|
219
|
+
exit repeat
|
|
220
|
+
end if
|
|
221
|
+
end repeat
|
|
222
|
+
if found then exit repeat
|
|
223
|
+
end repeat
|
|
224
|
+
if not found then
|
|
225
|
+
open location "${url}"
|
|
226
|
+
activate
|
|
227
|
+
end if
|
|
228
|
+
end tell
|
|
229
|
+
else
|
|
230
|
+
open location "${url}"
|
|
231
|
+
end if
|
|
232
|
+
`;
|
|
233
|
+
try {
|
|
234
|
+
const child = cp.spawn("osascript", ["-e", script], { stdio: "ignore", detached: true });
|
|
235
|
+
child.unref();
|
|
236
|
+
} catch (_e) {
|
|
237
|
+
// Fallback to plain open
|
|
238
|
+
try {
|
|
239
|
+
const child = cp.spawn("open", [url], { stdio: "ignore", detached: true });
|
|
240
|
+
child.unref();
|
|
241
|
+
} catch (_e2) {}
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
139
246
|
let cmd = null;
|
|
140
247
|
let args = [];
|
|
141
248
|
|
|
142
|
-
if (platform === "
|
|
143
|
-
cmd = "open";
|
|
144
|
-
args = [url];
|
|
145
|
-
} else if (platform === "win32") {
|
|
249
|
+
if (platform === "win32") {
|
|
146
250
|
cmd = "cmd";
|
|
147
251
|
args = ["/c", "start", "", url];
|
|
148
252
|
} else {
|
package/src/lib/rollout.js
CHANGED
|
@@ -2604,7 +2604,7 @@ async function parseCursorApiIncremental({
|
|
|
2604
2604
|
}
|
|
2605
2605
|
|
|
2606
2606
|
// ---------------------------------------------------------------------------
|
|
2607
|
-
// Kiro token tracking (reads from devdata.sqlite)
|
|
2607
|
+
// Kiro token tracking (reads from devdata.sqlite or tokens_generated.jsonl)
|
|
2608
2608
|
// ---------------------------------------------------------------------------
|
|
2609
2609
|
|
|
2610
2610
|
function resolveKiroBasePath() {
|
|
@@ -2624,6 +2624,10 @@ function resolveKiroDbPath() {
|
|
|
2624
2624
|
return path.join(resolveKiroBasePath(), "dev_data", "devdata.sqlite");
|
|
2625
2625
|
}
|
|
2626
2626
|
|
|
2627
|
+
function resolveKiroJsonlPath() {
|
|
2628
|
+
return path.join(resolveKiroBasePath(), "dev_data", "tokens_generated.jsonl");
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2627
2631
|
function readKiroDbTokens(dbPath, sinceId) {
|
|
2628
2632
|
if (!dbPath || !fssync.existsSync(dbPath)) return [];
|
|
2629
2633
|
const minId = Number.isFinite(sinceId) && sinceId > 0 ? sinceId : 0;
|
|
@@ -2648,6 +2652,53 @@ function readKiroDbTokens(dbPath, sinceId) {
|
|
|
2648
2652
|
return Array.isArray(rows) ? rows : [];
|
|
2649
2653
|
}
|
|
2650
2654
|
|
|
2655
|
+
// Read Kiro token data from JSONL fallback (tokens_generated.jsonl).
|
|
2656
|
+
// Each line: {"model":"agent","provider":"kiro","promptTokens":N,"generatedTokens":N}
|
|
2657
|
+
// The fallback file does not include per-row timestamps, so newly appended rows are
|
|
2658
|
+
// bucketed using the file mtime observed during this sync. We track a separate JSONL
|
|
2659
|
+
// cursor so it never shares state with the SQLite path.
|
|
2660
|
+
function readKiroJsonlTokens(jsonlPath, sinceLineIndex) {
|
|
2661
|
+
if (!jsonlPath || !fssync.existsSync(jsonlPath)) {
|
|
2662
|
+
return { rows: [], lineCount: 0, reset: false };
|
|
2663
|
+
}
|
|
2664
|
+
const startLine = Number.isFinite(sinceLineIndex) && sinceLineIndex > 0 ? sinceLineIndex : 0;
|
|
2665
|
+
let raw;
|
|
2666
|
+
try {
|
|
2667
|
+
raw = fssync.readFileSync(jsonlPath, "utf8");
|
|
2668
|
+
} catch (_e) {
|
|
2669
|
+
return { rows: [], lineCount: 0, reset: false };
|
|
2670
|
+
}
|
|
2671
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
2672
|
+
const lineCount = lines.length;
|
|
2673
|
+
if (startLine > lineCount) {
|
|
2674
|
+
return { rows: [], lineCount, reset: true };
|
|
2675
|
+
}
|
|
2676
|
+
let mtime;
|
|
2677
|
+
try {
|
|
2678
|
+
mtime = fssync.statSync(jsonlPath).mtime.toISOString();
|
|
2679
|
+
} catch (_e) {
|
|
2680
|
+
mtime = new Date().toISOString();
|
|
2681
|
+
}
|
|
2682
|
+
const timestamp = mtime.replace("T", " ").replace("Z", "").slice(0, 19);
|
|
2683
|
+
const rows = [];
|
|
2684
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
2685
|
+
try {
|
|
2686
|
+
const obj = JSON.parse(lines[i]);
|
|
2687
|
+
rows.push({
|
|
2688
|
+
id: i + 1,
|
|
2689
|
+
model: obj.model || "agent",
|
|
2690
|
+
provider: obj.provider || "kiro",
|
|
2691
|
+
tokens_prompt: obj.promptTokens || 0,
|
|
2692
|
+
tokens_generated: obj.generatedTokens || 0,
|
|
2693
|
+
timestamp,
|
|
2694
|
+
});
|
|
2695
|
+
} catch (_e) {
|
|
2696
|
+
// skip malformed lines
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
return { rows, lineCount, reset: false };
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2651
2702
|
// Build a sorted timeline of model usage from Kiro .chat metadata files
|
|
2652
2703
|
function buildKiroModelTimeline(basePath) {
|
|
2653
2704
|
const timeline = []; // [{ startMs, endMs, model }]
|
|
@@ -2730,14 +2781,49 @@ function normalizeKiroModelName(raw) {
|
|
|
2730
2781
|
return name || null;
|
|
2731
2782
|
}
|
|
2732
2783
|
|
|
2733
|
-
async function parseKiroIncremental({ dbPath, cursors, queuePath, onProgress }) {
|
|
2784
|
+
async function parseKiroIncremental({ dbPath, jsonlPath, cursors, queuePath, onProgress }) {
|
|
2734
2785
|
await ensureDir(path.dirname(queuePath));
|
|
2735
2786
|
const kiroState = cursors.kiro && typeof cursors.kiro === "object" ? cursors.kiro : {};
|
|
2736
|
-
const
|
|
2787
|
+
const lastDbId = typeof kiroState.lastDbId === "number"
|
|
2788
|
+
? kiroState.lastDbId
|
|
2789
|
+
: (typeof kiroState.lastId === "number" ? kiroState.lastId : 0);
|
|
2790
|
+
const jsonlState = kiroState.jsonl && typeof kiroState.jsonl === "object" ? kiroState.jsonl : {};
|
|
2791
|
+
const lastJsonlLine = typeof jsonlState.lastLine === "number" ? jsonlState.lastLine : 0;
|
|
2737
2792
|
|
|
2738
2793
|
const resolvedDbPath = dbPath || resolveKiroDbPath();
|
|
2739
|
-
const
|
|
2794
|
+
const resolvedJsonlPath = jsonlPath || resolveKiroJsonlPath();
|
|
2795
|
+
|
|
2796
|
+
// Try SQLite first, fall back to JSONL.
|
|
2797
|
+
let rows = [];
|
|
2798
|
+
let nextDbId = lastDbId;
|
|
2799
|
+
let nextJsonlLine = lastJsonlLine;
|
|
2800
|
+
let usingDb = false;
|
|
2801
|
+
if (fssync.existsSync(resolvedDbPath)) {
|
|
2802
|
+
rows = readKiroDbTokens(resolvedDbPath, lastDbId);
|
|
2803
|
+
usingDb = true;
|
|
2804
|
+
} else if (fssync.existsSync(resolvedJsonlPath)) {
|
|
2805
|
+
const jsonlResult = readKiroJsonlTokens(resolvedJsonlPath, lastJsonlLine);
|
|
2806
|
+
rows = jsonlResult.rows;
|
|
2807
|
+
nextJsonlLine = jsonlResult.lineCount;
|
|
2808
|
+
if (jsonlResult.reset) {
|
|
2809
|
+
cursors.kiro = {
|
|
2810
|
+
...kiroState,
|
|
2811
|
+
lastDbId,
|
|
2812
|
+
jsonl: { lastLine: jsonlResult.lineCount, updatedAt: new Date().toISOString() },
|
|
2813
|
+
updatedAt: new Date().toISOString(),
|
|
2814
|
+
};
|
|
2815
|
+
return { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
2816
|
+
}
|
|
2817
|
+
} else {
|
|
2818
|
+
return { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
2819
|
+
}
|
|
2740
2820
|
if (rows.length === 0) {
|
|
2821
|
+
cursors.kiro = {
|
|
2822
|
+
...kiroState,
|
|
2823
|
+
lastDbId,
|
|
2824
|
+
jsonl: { lastLine: nextJsonlLine, updatedAt: new Date().toISOString() },
|
|
2825
|
+
updatedAt: new Date().toISOString(),
|
|
2826
|
+
};
|
|
2741
2827
|
return { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
2742
2828
|
}
|
|
2743
2829
|
|
|
@@ -2749,7 +2835,7 @@ async function parseKiroIncremental({ dbPath, cursors, queuePath, onProgress })
|
|
|
2749
2835
|
const touchedBuckets = new Set();
|
|
2750
2836
|
const cb = typeof onProgress === "function" ? onProgress : null;
|
|
2751
2837
|
let eventsAggregated = 0;
|
|
2752
|
-
let maxId =
|
|
2838
|
+
let maxId = lastDbId;
|
|
2753
2839
|
|
|
2754
2840
|
for (let i = 0; i < rows.length; i++) {
|
|
2755
2841
|
const row = rows[i];
|
|
@@ -2780,7 +2866,7 @@ async function parseKiroIncremental({ dbPath, cursors, queuePath, onProgress })
|
|
|
2780
2866
|
touchedBuckets.add(bucketKey("kiro", model, bucketStart));
|
|
2781
2867
|
eventsAggregated++;
|
|
2782
2868
|
|
|
2783
|
-
if (row.id && row.id > maxId) maxId = row.id;
|
|
2869
|
+
if (usingDb && row.id && row.id > maxId) maxId = row.id;
|
|
2784
2870
|
|
|
2785
2871
|
if (cb) {
|
|
2786
2872
|
cb({
|
|
@@ -2794,9 +2880,16 @@ async function parseKiroIncremental({ dbPath, cursors, queuePath, onProgress })
|
|
|
2794
2880
|
}
|
|
2795
2881
|
|
|
2796
2882
|
const bucketsQueued = await enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets });
|
|
2797
|
-
|
|
2883
|
+
const updatedAt = new Date().toISOString();
|
|
2884
|
+
hourlyState.updatedAt = updatedAt;
|
|
2798
2885
|
cursors.hourly = hourlyState;
|
|
2799
|
-
cursors.kiro = {
|
|
2886
|
+
cursors.kiro = {
|
|
2887
|
+
...kiroState,
|
|
2888
|
+
lastId: maxId,
|
|
2889
|
+
lastDbId: maxId,
|
|
2890
|
+
jsonl: { lastLine: nextJsonlLine, updatedAt },
|
|
2891
|
+
updatedAt,
|
|
2892
|
+
};
|
|
2800
2893
|
|
|
2801
2894
|
return { recordsProcessed: rows.length, eventsAggregated, bucketsQueued };
|
|
2802
2895
|
}
|
|
@@ -2808,6 +2901,7 @@ module.exports = {
|
|
|
2808
2901
|
listOpencodeMessageFiles,
|
|
2809
2902
|
readOpencodeDbMessages,
|
|
2810
2903
|
resolveKiroDbPath,
|
|
2904
|
+
resolveKiroJsonlPath,
|
|
2811
2905
|
parseRolloutIncremental,
|
|
2812
2906
|
parseClaudeIncremental,
|
|
2813
2907
|
parseGeminiIncremental,
|