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.
@@ -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-DGPBnxuf.js"></script>
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-DGPBnxuf.js"></script>
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.1",
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
  }
@@ -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
- if (fssync.existsSync(kiroDbPath)) {
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) => {
@@ -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 === "darwin") {
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 {
@@ -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 lastId = typeof kiroState.lastId === "number" ? kiroState.lastId : 0;
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 rows = readKiroDbTokens(resolvedDbPath, lastId);
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 = lastId;
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
- hourlyState.updatedAt = new Date().toISOString();
2883
+ const updatedAt = new Date().toISOString();
2884
+ hourlyState.updatedAt = updatedAt;
2798
2885
  cursors.hourly = hourlyState;
2799
- cursors.kiro = { lastId: maxId, updatedAt: new Date().toISOString() };
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,