@jnst/cursor-usage 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -146,32 +146,27 @@ bun scripts/generate-dummy-csv.ts > dummy-usage.csv
146
146
 
147
147
  ### Release
148
148
 
149
- Release commands verify, version, publish, push commits/tags, and create a
150
- draft GitHub Release with generated notes:
149
+ The release command verifies, versions, publishes, pushes commits/tags, and creates a
150
+ GitHub Release with generated notes:
151
151
 
152
152
  ```bash
153
- bun run release:patch
154
- bun run release:minor
155
- bun run release:major
153
+ bun run release
156
154
  ```
157
155
 
158
- GitHub Releases are drafts by default so notes can be reviewed before publish.
159
- Use `--publish-release` to publish the GitHub Release immediately, or
160
- `--dry-run` to print mutating steps without running them:
156
+ Use `--dry-run` to print mutating steps without running them:
161
157
 
162
158
  ```bash
163
- bun run release:patch --dry-run
164
- bun run release:patch --publish-release
159
+ bun run release --dry-run
165
160
  ```
166
161
 
167
- Release commands are safe to rerun after a partial failure. The script checks
162
+ The release command is safe to rerun after a partial failure. The script checks
168
163
  the current tag, npm package version, and GitHub Release before each publishing
169
164
  step:
170
165
 
171
166
  ```bash
172
167
  # If npm publish, git push, or GitHub Release creation failed midway,
173
168
  # fix the problem and run the same command again.
174
- bun run release:patch
169
+ bun run release
175
170
  ```
176
171
 
177
172
  ## Architecture
package/dist/cli.js CHANGED
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
- import{readFile as vf}from"node:fs/promises";import{parseArgs as h}from"node:util";var l="Errored, No Charge";var Q="UTC";function _(){return Intl.DateTimeFormat().resolvedOptions().timeZone||Q}function E(f){try{return new Intl.DateTimeFormat("en-US",{timeZone:f}).format(new Date),!0}catch{return!1}}function R(f,g,$){return new Intl.DateTimeFormat("en-GB",{timeZone:g,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",hourCycle:"h23"}).formatToParts(f).find((V)=>V.type===$)?.value??""}function H(f){return Number.isInteger(f)&&f>=0&&f<=23}function y(f){if(!H(f))throw Error(`Invalid Daily Window start hour: ${f}`)}function Jf(f,g){return[R(f,g,"year"),R(f,g,"month"),R(f,g,"day")].join("-")}function Sf(f){let[g,$,Y]=f.split("-").map(Number);if(g===void 0||$===void 0||Y===void 0)throw Error(`Invalid Daily Window Key: ${f}`);return{year:g,month:$,date:Y}}function qf(f){return f.toISOString().slice(0,10)}function Gf(f,g){let{year:$,month:Y,date:V}=Sf(f);return qf(new Date(Date.UTC($,Y-1,V)+g*86400000))}function C(f,g=Q,$=0){y($);let Y=Jf(f,g);return Number(R(f,g,"hour"))<$?Gf(Y,-1):Y}function Lf(f,g=Q){return R(f,g,"hour")}function n(f=0){return y(f),Array.from({length:24},(g,$)=>String((f+$)%24).padStart(2,"0"))}function K(f,g,$=Q,Y=0){return f.filter((V)=>C(V.date,$,Y)===g)}function m(f,g=Q,$=0){let Y=[...f].sort((V,z)=>z.date.getTime()-V.date.getTime())[0];return Y?C(Y.date,g,$):null}function i(f){return f.filter((g)=>g.kind!==l)}function N(f,g=Q,$=0){let Y=0,V=0,z=0,c=new Set,A=new Set,J=new Set;for(let q of f){if(Y+=q.cost,V+=q.totalTokens,q.maxMode)z++;c.add(C(q.date,g,$)),A.add(q.user),J.add(q.model)}let S=[...c].sort();return{totalCost:Y,totalTokens:V,eventCount:f.length,firstDailyWindow:S[0]??null,lastDailyWindow:S[S.length-1]??null,dailyWindowCount:c.size,avgCostPerActiveDailyWindow:c.size>0?Y/c.size:0,maxModeRatio:f.length>0?z/f.length:0,userCount:A.size,modelCount:J.size}}function D(f,g){let $=new Map;for(let Y of f){let V=g(Y),z=$.get(V);if(!z)z={key:V,cost:0,totalTokens:0,inputTokens:0,outputTokens:0,cacheRead:0,eventCount:0},$.set(V,z);z.cost+=Y.cost,z.totalTokens+=Y.totalTokens,z.inputTokens+=Y.inputWithCacheWrite+Y.inputWithoutCacheWrite,z.outputTokens+=Y.outputTokens,z.cacheRead+=Y.cacheRead,z.eventCount++}return[...$.values()]}function k(f,g=Q,$=0){return D(f,(Y)=>C(Y.date,g,$)).sort((Y,V)=>Y.key.localeCompare(V.key))}function O(f){return D(f,(g)=>g.user).sort((g,$)=>$.cost-g.cost)}function U(f){return D(f,(g)=>g.model).sort((g,$)=>$.cost-g.cost)}function W(f){return D(f,(g)=>g.kind).sort((g,$)=>$.cost-g.cost)}function w(f,g=Q){return D(f,($)=>Lf($.date,g)).sort(($,Y)=>$.key.localeCompare(Y.key))}function d(f,g){return[...f].sort(($,Y)=>Y.cost-$.cost).slice(0,g)}function Xf(f){let g=[],$=[],Y="",V=!1,z=0,c=()=>{$.push(Y),Y=""},A=()=>{c(),g.push($),$=[]};while(z<f.length){let J=f[z];if(V){if(J==='"'){if(f[z+1]==='"'){Y+='"',z+=2;continue}V=!1,z++;continue}Y+=J,z++;continue}if(J==='"'){V=!0,z++;continue}if(J===","){c(),z++;continue}if(J==="\r"){z++;continue}if(J===`
3
- `){A(),z++;continue}Y+=J,z++}if(Y.length>0||$.length>0)A();return g}var Ff=["Date","User","Cloud Agent ID","Automation ID","Kind","Model","Max Mode","Input (w/ Cache Write)","Input (w/o Cache Write)","Cache Read","Output Tokens","Total Tokens","Cost"];function P(f){if(!f)return 0;let g=Number(f);return Number.isFinite(g)?g:0}function r(f){let g=Xf(f.trim());if(g.length===0)return[];let $=g[0],Y=new Map;$.forEach((c,A)=>Y.set(c.trim(),A));for(let c of["Date","User","Model","Cost"])if(!Y.has(c))throw Error(`Invalid CSV: missing column "${c}". Expected a Cursor usage-events export with columns: ${Ff.join(", ")}`);let V=(c,A)=>{let J=Y.get(A);return J===void 0?"":(c[J]??"").trim()},z=[];for(let c of g.slice(1)){if(c.length===1&&c[0]==="")continue;let A=V(c,"Date"),J=new Date(A);if(Number.isNaN(J.getTime()))continue;z.push({date:J,user:V(c,"User"),cloudAgentId:V(c,"Cloud Agent ID")||null,automationId:V(c,"Automation ID")||null,kind:V(c,"Kind"),model:V(c,"Model"),maxMode:V(c,"Max Mode").toLowerCase()==="yes",inputWithCacheWrite:P(V(c,"Input (w/ Cache Write)")),inputWithoutCacheWrite:P(V(c,"Input (w/o Cache Write)")),cacheRead:P(V(c,"Cache Read")),outputTokens:P(V(c,"Output Tokens")),totalTokens:P(V(c,"Total Tokens")),cost:P(V(c,"Cost"))})}return z}import{spawn as Qf}from"node:child_process";import{createReadStream as jf,existsSync as Nf}from"node:fs";import{stat as Pf}from"node:fs/promises";import{createServer as If}from"node:http";import{dirname as Bf,extname as Mf,join as T,normalize as Rf}from"node:path";import{fileURLToPath as Kf}from"node:url";var a=4321,Df={".html":"text/html; charset=utf-8",".js":"text/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".ico":"image/x-icon",".map":"application/json"};function Of(){let f=Bf(Kf(import.meta.url)),g=[T(f,"web"),T(f,"../../dist/web")];for(let $ of g)if(Nf(T($,"index.html")))return $;throw Error("Dashboard assets not found. Run `bun run build` first (expected dist/web/index.html).")}function Uf(f){let[g,...$]=process.platform==="darwin"?["open",f]:process.platform==="win32"?["cmd","/c","start","",f]:["xdg-open",f];try{Qf(g,$,{stdio:"ignore",detached:!0}).unref()}catch{}}function xf(f){return If(async(g,$)=>{let Y=decodeURIComponent(new URL(g.url??"/","http://localhost").pathname),V=Rf(T(f,Y==="/"?"/index.html":Y));if(!V.startsWith(f)){$.writeHead(403),$.end("Forbidden");return}try{let z=await Pf(V);if(!z.isFile())throw Error("not a file");$.writeHead(200,{"Content-Type":Df[Mf(V)]??"application/octet-stream","Content-Length":z.size}),jf(V).pipe($)}catch{$.writeHead(404),$.end("Not Found")}})}function Z(f={}){let g=Of(),$=xf(g);return new Promise((Y,V)=>{let z=()=>{let{port:c}=$.address();Y({server:$,url:`http://localhost:${c}`})};if(f.port!==void 0){$.once("error",V),$.listen(f.port,z);return}$.once("error",(c)=>{if(c.code!=="EADDRINUSE"){V(c);return}console.log(`port ${a} is in use, picked a free port instead`),$.listen(0)}),$.once("listening",z),$.listen(a)})}function s(f){Z({port:f.port}).then(({url:g})=>{if(console.log(`cursor-usage dashboard running at ${g}`),console.log("Drop a Cursor usage-events CSV onto the page. Ctrl+C to stop."),f.open)Uf(g)}).catch((g)=>{throw g})}var _f=process.stdout.isTTY&&!process.env.NO_COLOR,x=(f)=>(g)=>_f?`\x1B[${f}m${g}\x1B[0m`:g,j=x("1"),F=x("2"),o=x("36"),Vg=x("32"),zg=x("33");function B(f){return`$${f.toFixed(2)}`}function p(f){if(f>=1e9)return`${(f/1e9).toFixed(1)}B`;if(f>=1e6)return`${(f/1e6).toFixed(1)}M`;if(f>=1000)return`${(f/1000).toFixed(1)}K`;return String(f)}function t(f,g,$){if(g<=0||f<=0)return"";let Y=Math.round(f/g*$*8),V=Math.floor(Y/8),z=Y%8,c=["","▏","▎","▍","▌","▋","▊","▉"];return"█".repeat(V)+(c[z]??"")}function X(f,g){return f.length>=g?f:f+" ".repeat(g-f.length)}function b(f,g,$){return new Intl.DateTimeFormat("en-GB",{timeZone:g,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hourCycle:"h23"}).formatToParts(f).find((V)=>V.type===$)?.value??""}function Cf(f,g){return[b(f,g,"hour"),b(f,g,"minute"),b(f,g,"second")].join(":")}function kf(f,g,$,Y){let V=f.firstDailyWindow&&f.lastDailyWindow?`${f.firstDailyWindow} – ${f.lastDailyWindow}`:"no data",z=$?`${g}, start ${Y}:00, user ${$}`:`${g}, start ${Y}:00`,c=(J)=>F(X(J,14)),A=(J)=>j(X(J,12));return[`${j("Cursor Usage")} ${V} ${F(`(${f.eventCount} events, ${f.dailyWindowCount} daily windows, ${z})`)}`,"",` ${c("Total Cost")}${A(B(f.totalCost))} ${c("Total Tokens")}${A(p(f.totalTokens))}`,` ${c("Avg/Active")}${A(B(f.avgCostPerActiveDailyWindow))} ${c("Max Mode")}${A(`${Math.round(f.maxModeRatio*100)}%`)}`,` ${c("Users")}${A(String(f.userCount))} ${c("Models")}${A(String(f.modelCount))}`]}function I(f,g,$={totalCost:0}){let{totalCost:Y,barWidth:V=28,maxRows:z=15}=$,c=g.slice(0,z),A=Math.max(...c.map((q)=>q.key.length),4),J=Math.max(...c.map((q)=>q.cost),0),S=[j(f)];for(let q of c){let G=Y>0?` ${F(`${Math.round(q.cost/Y*100)}%`)}`:"";S.push(` ${X(q.key,A)} ${X(B(q.cost),8)} ${o(X(t(q.cost,J,V),V))}${G} ${F(`${p(q.totalTokens)} tok, ${q.eventCount} ev`)}`)}if(g.length>z)S.push(F(` … and ${g.length-z} more`));return S}function e(f,g,$,Y,V=0){let z=N(f,$,V),c=[kf(z,$,Y,V)],A={"daily-window":()=>I("Daily Window Cost",k(f,$,V),{totalCost:z.totalCost,maxRows:31}),model:()=>I("By Model",U(f),{totalCost:z.totalCost}),user:()=>I("By User",O(f),{totalCost:z.totalCost})};if(g)c.push(A[g]());else c.push(A["daily-window"](),A.model(),A.user());return c.map((J)=>J.join(`
2
+ import{readFile as yf}from"node:fs/promises";import{parseArgs as p}from"node:util";var u="Errored, No Charge";var F="UTC";function x(){return Intl.DateTimeFormat().resolvedOptions().timeZone||F}function h(f){try{return new Intl.DateTimeFormat("en-US",{timeZone:f}).format(new Date),!0}catch{return!1}}var l=new Map;function Af(f){let g=l.get(f);if(g)return g;let $=new Intl.DateTimeFormat("en-GB",{timeZone:f,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",hourCycle:"h23"});return l.set(f,$),$}function y(f,g){return new Map(Af(g).formatToParts(f).map(($)=>[$.type,$.value]))}function qf(f,g,$){return y(f,g).get($)??""}function E(f){return Number.isInteger(f)&&f>=0&&f<=23}function n(f){if(!E(f))throw Error(`Invalid Daily Window start hour: ${f}`)}function Gf(f,g){let $=y(f,g);return{dateKey:[$.get("year"),$.get("month"),$.get("day")].join("-"),hour:Number($.get("hour")??0)}}function Lf(f){let[g,$,Y]=f.split("-").map(Number);if(g===void 0||$===void 0||Y===void 0)throw Error(`Invalid Daily Window Key: ${f}`);return{year:g,month:$,date:Y}}function Xf(f){return f.toISOString().slice(0,10)}function Qf(f,g){let{year:$,month:Y,date:z}=Lf(f);return Xf(new Date(Date.UTC($,Y-1,z)+g*86400000))}function _(f,g=F,$=0){n($);let{dateKey:Y,hour:z}=Gf(f,g);return z<$?Qf(Y,-1):Y}function Ff(f,g=F){return qf(f,g,"hour")}function i(f=0){return n(f),Array.from({length:24},(g,$)=>String((f+$)%24).padStart(2,"0"))}function R(f,g,$=F,Y=0){return f.filter((z)=>_(z.date,$,Y)===g)}function m(f,g=F,$=0){let Y=[...f].sort((z,c)=>c.date.getTime()-z.date.getTime())[0];return Y?_(Y.date,g,$):null}function d(f){return f.filter((g)=>g.kind!==u)}function N(f,g=F,$=0){let Y=0,z=0,c=0,V=new Set,J=new Set,S=new Set;for(let q of f){if(Y+=q.cost,z+=q.totalTokens,q.maxMode)c++;V.add(_(q.date,g,$)),J.add(q.user),S.add(q.model)}let A=[...V].sort();return{totalCost:Y,totalTokens:z,eventCount:f.length,firstDailyWindow:A[0]??null,lastDailyWindow:A[A.length-1]??null,dailyWindowCount:V.size,avgCostPerActiveDailyWindow:V.size>0?Y/V.size:0,maxModeRatio:f.length>0?c/f.length:0,userCount:J.size,modelCount:S.size}}function K(f,g){let $=new Map;for(let Y of f){let z=g(Y),c=$.get(z);if(!c)c={key:z,cost:0,totalTokens:0,inputTokens:0,outputTokens:0,cacheRead:0,eventCount:0},$.set(z,c);c.cost+=Y.cost,c.totalTokens+=Y.totalTokens,c.inputTokens+=Y.inputWithCacheWrite+Y.inputWithoutCacheWrite,c.outputTokens+=Y.outputTokens,c.cacheRead+=Y.cacheRead,c.eventCount++}return[...$.values()]}function C(f,g=F,$=0){return K(f,(Y)=>_(Y.date,g,$)).sort((Y,z)=>Y.key.localeCompare(z.key))}function D(f){return K(f,(g)=>g.user).sort((g,$)=>$.cost-g.cost)}function O(f){return K(f,(g)=>g.model).sort((g,$)=>$.cost-g.cost)}function H(f){return K(f,(g)=>g.kind).sort((g,$)=>$.cost-g.cost)}function W(f,g=F){return K(f,($)=>Ff($.date,g)).sort(($,Y)=>$.key.localeCompare(Y.key))}function r(f,g){return[...f].sort(($,Y)=>Y.cost-$.cost).slice(0,g)}function jf(f){let g=[],$=[],Y="",z=!1,c=0,V=()=>{$.push(Y),Y=""},J=()=>{V(),g.push($),$=[]};while(c<f.length){let S=f[c];if(z){if(S==='"'){if(f[c+1]==='"'){Y+='"',c+=2;continue}z=!1,c++;continue}Y+=S,c++;continue}if(S==='"'){z=!0,c++;continue}if(S===","){V(),c++;continue}if(S==="\r"){c++;continue}if(S===`
3
+ `){J(),c++;continue}Y+=S,c++}if(Y.length>0||$.length>0)J();return g}var Nf=["Date","User","Cloud Agent ID","Automation ID","Kind","Model","Max Mode","Input (w/ Cache Write)","Input (w/o Cache Write)","Cache Read","Output Tokens","Total Tokens","Cost"];function P(f){if(!f)return 0;let g=Number(f);return Number.isFinite(g)?g:0}function a(f){let g=jf(f.trim());if(g.length===0)return[];let $=g[0],Y=new Map;$.forEach((V,J)=>Y.set(V.trim(),J));for(let V of["Date","User","Model","Cost"])if(!Y.has(V))throw Error(`Invalid CSV: missing column "${V}". Expected a Cursor usage-events export with columns: ${Nf.join(", ")}`);let z=(V,J)=>{let S=Y.get(J);return S===void 0?"":(V[S]??"").trim()},c=[];for(let V of g.slice(1)){if(V.length===1&&V[0]==="")continue;let J=z(V,"Date"),S=new Date(J);if(Number.isNaN(S.getTime()))continue;c.push({date:S,user:z(V,"User"),cloudAgentId:z(V,"Cloud Agent ID")||null,automationId:z(V,"Automation ID")||null,kind:z(V,"Kind"),model:z(V,"Model"),maxMode:z(V,"Max Mode").toLowerCase()==="yes",inputWithCacheWrite:P(z(V,"Input (w/ Cache Write)")),inputWithoutCacheWrite:P(z(V,"Input (w/o Cache Write)")),cacheRead:P(z(V,"Cache Read")),outputTokens:P(z(V,"Output Tokens")),totalTokens:P(z(V,"Total Tokens")),cost:P(z(V,"Cost"))})}return c}import{spawn as Pf}from"node:child_process";import{createReadStream as If,existsSync as Bf}from"node:fs";import{stat as Mf}from"node:fs/promises";import{createServer as Rf}from"node:http";import{dirname as Kf,extname as Df,join as k,normalize as Of}from"node:path";import{fileURLToPath as Uf}from"node:url";var s=4321,xf={".html":"text/html; charset=utf-8",".js":"text/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".ico":"image/x-icon",".map":"application/json"};function _f(){let f=Kf(Uf(import.meta.url)),g=[k(f,"web"),k(f,"../../dist/web")];for(let $ of g)if(Bf(k($,"index.html")))return $;throw Error("Dashboard assets not found. Run `bun run build` first (expected dist/web/index.html).")}function Cf(f){let[g,...$]=process.platform==="darwin"?["open",f]:process.platform==="win32"?["cmd","/c","start","",f]:["xdg-open",f];try{Pf(g,$,{stdio:"ignore",detached:!0}).unref()}catch{}}function kf(f){return Rf(async(g,$)=>{let Y=decodeURIComponent(new URL(g.url??"/","http://localhost").pathname),z=Of(k(f,Y==="/"?"/index.html":Y));if(!z.startsWith(f)){$.writeHead(403),$.end("Forbidden");return}try{let c=await Mf(z);if(!c.isFile())throw Error("not a file");$.writeHead(200,{"Content-Type":xf[Df(z)]??"application/octet-stream","Content-Length":c.size}),If(z).pipe($)}catch{$.writeHead(404),$.end("Not Found")}})}function w(f={}){let g=_f(),$=kf(g);return new Promise((Y,z)=>{let c=()=>{let{port:V}=$.address();Y({server:$,url:`http://localhost:${V}`})};if(f.port!==void 0){$.once("error",z),$.listen(f.port,c);return}$.once("error",(V)=>{if(V.code!=="EADDRINUSE"){z(V);return}console.log(`port ${s} is in use, picked a free port instead`),$.listen(0)}),$.once("listening",c),$.listen(s)})}function o(f){w({port:f.port}).then(({url:g})=>{if(console.log(`cursor-usage dashboard running at ${g}`),console.log("Drop a Cursor usage-events CSV onto the page. Ctrl+C to stop."),f.open)Cf(g)}).catch((g)=>{throw g})}var Tf=process.stdout.isTTY&&!process.env.NO_COLOR,U=(f)=>(g)=>Tf?`\x1B[${f}m${g}\x1B[0m`:g,j=U("1"),Q=U("2"),t=U("36"),Sg=U("32"),Ag=U("33");function B(f){return`$${f.toFixed(2)}`}function T(f){if(f>=1e9)return`${(f/1e9).toFixed(1)}B`;if(f>=1e6)return`${(f/1e6).toFixed(1)}M`;if(f>=1000)return`${(f/1000).toFixed(1)}K`;return String(f)}function e(f,g,$){if(g<=0||f<=0)return"";let Y=Math.round(f/g*$*8),z=Math.floor(Y/8),c=Y%8,V=["","▏","▎","▍","▌","▋","▊","▉"];return"█".repeat(z)+(V[c]??"")}function X(f,g){return f.length>=g?f:f+" ".repeat(g-f.length)}function b(f,g,$){return new Intl.DateTimeFormat("en-GB",{timeZone:g,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hourCycle:"h23"}).formatToParts(f).find((z)=>z.type===$)?.value??""}function pf(f,g){return[b(f,g,"hour"),b(f,g,"minute"),b(f,g,"second")].join(":")}function hf(f,g,$,Y){let z=f.firstDailyWindow&&f.lastDailyWindow?`${f.firstDailyWindow} – ${f.lastDailyWindow}`:"no data",c=$?`${g}, start ${Y}:00, user ${$}`:`${g}, start ${Y}:00`,V=(S)=>Q(X(S,14)),J=(S)=>j(X(S,12));return[`${j("Cursor Usage")} ${z} ${Q(`(${f.eventCount} events, ${f.dailyWindowCount} daily windows, ${c})`)}`,"",` ${V("Total Cost")}${J(B(f.totalCost))} ${V("Total Tokens")}${J(T(f.totalTokens))}`,` ${V("Avg/Active")}${J(B(f.avgCostPerActiveDailyWindow))} ${V("Max Mode")}${J(`${Math.round(f.maxModeRatio*100)}%`)}`,` ${V("Users")}${J(String(f.userCount))} ${V("Models")}${J(String(f.modelCount))}`]}function I(f,g,$={totalCost:0}){let{totalCost:Y,barWidth:z=28,maxRows:c=15}=$,V=g.slice(0,c),J=Math.max(...V.map((q)=>q.key.length),4),S=Math.max(...V.map((q)=>q.cost),0),A=[j(f)];for(let q of V){let G=Y>0?` ${Q(`${Math.round(q.cost/Y*100)}%`)}`:"";A.push(` ${X(q.key,J)} ${X(B(q.cost),8)} ${t(X(e(q.cost,S,z),z))}${G} ${Q(`${T(q.totalTokens)} tok, ${q.eventCount} ev`)}`)}if(g.length>c)A.push(Q(` … and ${g.length-c} more`));return A}function ff(f,g,$,Y,z=0){let c=N(f,$,z),V=[hf(c,$,Y,z)],J={"daily-window":()=>I("Daily Window Cost",C(f,$,z),{totalCost:c.totalCost,maxRows:31}),model:()=>I("By Model",O(f),{totalCost:c.totalCost}),user:()=>I("By User",D(f),{totalCost:c.totalCost})};if(g)V.push(J[g]());else V.push(J["daily-window"](),J.model(),J.user());return V.map((S)=>S.join(`
4
4
  `)).join(`
5
5
 
6
6
  `)+`
7
- `}function ff(f,g,$,Y=0){return JSON.stringify({timeZone:g,startHour:Y,filters:{user:$??null},summary:N(f,g,Y),byDailyWindow:k(f,g,Y),byModel:U(f),byUser:O(f)},null,2)}function Tf(f,g,$,Y,V,z,c){let A=N(g,$,Y),J=V>0?Math.round(A.totalCost/V*100):0,S=(G)=>F(X(G,14)),q=(G)=>j(X(G,12));return[`${j(`Daily Window ${f}`)} ${F(`(${A.eventCount} events, rank ${z}/${c} by cost, ${$}, start ${Y}:00)`)}`,"",` ${S("Cost")}${q(B(A.totalCost))} ${S("of period")}${q(`${J}%`)}`,` ${S("Total Tokens")}${q(p(A.totalTokens))} ${S("Max Mode")}${q(`${Math.round(A.maxModeRatio*100)}%`)}`,` ${S("Users")}${q(String(A.userCount))} ${S("Models")}${q(String(A.modelCount))}`]}function pf(f,g,$){let Y=new Map(w(f,g).map((c)=>[c.key,c])),V=Math.max(...[...Y.values()].map((c)=>c.cost),0),z=[j(`By Hour (${g})`)];for(let c of n($)){let A=Y.get(c),J=A?.cost??0,S=A?.eventCount??0,q=S>0?F(` ${S} ev`):"";z.push(` ${c} ${X(J>0?B(J):"",8)} ${o(X(t(J,V,24),24))}${q}`)}return z}function hf(f,g,$){let Y=d(f,g),V=[j(`Top Events (${Y.length} of ${f.length})`)],z=Math.max(...Y.map((A)=>A.user.length),4),c=Math.max(...Y.map((A)=>A.model.length),5);for(let A of Y){let J=Cf(A.date,$);V.push(` ${F(J)} ${X(A.user,z)} ${X(A.model,c)} ${X(B(A.cost),8)} ${F(`${p(A.totalTokens)} tok`)}`)}return V}function gf(f,g,$,Y,V=0){let z=k(f,$,V),c=K(f,g,$,V);if(c.length===0){let G=z.map((Af)=>Af.key),M=G.length>0?`
7
+ `}function gf(f,g,$,Y=0){return JSON.stringify({timeZone:g,startHour:Y,filters:{user:$??null},summary:N(f,g,Y),byDailyWindow:C(f,g,Y),byModel:O(f),byUser:D(f)},null,2)}function Ef(f,g,$,Y,z,c,V){let J=N(g,$,Y),S=z>0?Math.round(J.totalCost/z*100):0,A=(G)=>Q(X(G,14)),q=(G)=>j(X(G,12));return[`${j(`Daily Window ${f}`)} ${Q(`(${J.eventCount} events, rank ${c}/${V} by cost, ${$}, start ${Y}:00)`)}`,"",` ${A("Cost")}${q(B(J.totalCost))} ${A("of period")}${q(`${S}%`)}`,` ${A("Total Tokens")}${q(T(J.totalTokens))} ${A("Max Mode")}${q(`${Math.round(J.maxModeRatio*100)}%`)}`,` ${A("Users")}${q(String(J.userCount))} ${A("Models")}${q(String(J.modelCount))}`]}function Hf(f,g,$){let Y=new Map(W(f,g).map((V)=>[V.key,V])),z=Math.max(...[...Y.values()].map((V)=>V.cost),0),c=[j(`By Hour (${g})`)];for(let V of i($)){let J=Y.get(V),S=J?.cost??0,A=J?.eventCount??0,q=A>0?Q(` ${A} ev`):"";c.push(` ${V} ${X(S>0?B(S):"",8)} ${t(X(e(S,z,24),24))}${q}`)}return c}function Wf(f,g,$){let Y=r(f,g),z=[j(`Top Events (${Y.length} of ${f.length})`)],c=Math.max(...Y.map((J)=>J.user.length),4),V=Math.max(...Y.map((J)=>J.model.length),5);for(let J of Y){let S=pf(J.date,$);z.push(` ${Q(S)} ${X(J.user,c)} ${X(J.model,V)} ${X(B(J.cost),8)} ${Q(`${T(J.totalTokens)} tok`)}`)}return z}function $f(f,g,$,Y,z=0){let c=C(f,$,z),V=R(f,g,$,z);if(V.length===0){let G=c.map((Sf)=>Sf.key),M=G.length>0?`
8
8
  Available Daily Windows: ${G[0]} – ${G[G.length-1]}`:"";return`No billable events in Daily Window ${g}.${M}
9
- `}let A=f.reduce((G,M)=>G+M.cost,0),J=[...z].sort((G,M)=>M.cost-G.cost).findIndex((G)=>G.key===g)+1,S=N(c,$,V).totalCost;return[Tf(g,c,$,V,A,J,z.length),...Y?[[F(`Filtered to user: ${Y}`)]]:[],pf(c,$,V),I("By Model",U(c),{totalCost:S}),I("By User",O(c),{totalCost:S}),I("By Kind",W(c),{totalCost:S}),hf(c,20,$)].map((G)=>G.join(`
9
+ `}let J=f.reduce((G,M)=>G+M.cost,0),S=[...c].sort((G,M)=>M.cost-G.cost).findIndex((G)=>G.key===g)+1,A=N(V,$,z).totalCost;return[Ef(g,V,$,z,J,S,c.length),...Y?[[Q(`Filtered to user: ${Y}`)]]:[],Hf(V,$,z),I("By Model",O(V),{totalCost:A}),I("By User",D(V),{totalCost:A}),I("By Kind",H(V),{totalCost:A}),Wf(V,20,$)].map((G)=>G.join(`
10
10
  `)).join(`
11
11
 
12
12
  `)+`
13
- `}function $f(f,g,$,Y,V=0){let z=K(f,g,$,V);return JSON.stringify({dailyWindow:g,timeZone:$,startHour:V,filters:{user:Y??null},summary:N(z,$,V),byHour:w(z,$),byModel:U(z),byUser:O(z),byKind:W(z)},null,2)}import{basename as Ef,dirname as Hf,extname as Wf,join as Yf}from"node:path";function wf(f){let{csvPath:g,dailyWindow:$,dailyReport:Y,out:V}=f;if(V)return V;if(Y)return Yf(process.cwd(),"daily-report.png");let z=Wf(g),c=Ef(g,z);return Yf(Hf(g),`${c}${$?`-${$}-daily`:""}.png`)}async function Zf(){let f=Function("specifier","return import(specifier)");try{let g=await f("playwright-core");if(!g.chromium)throw Error("playwright-core did not expose chromium");return g.chromium}catch(g){throw Error(`Screenshot export requires playwright-core. Install dependencies and try again.
14
- ${String(g)}`)}}function bf(f){return f.map((g)=>({...g,date:g.date.toISOString()}))}async function v(f){if(f.events.length===0)throw Error("No billable usage events found in the Usage Export.");if(f.dailyWindow){if(K(f.events,f.dailyWindow,f.timeZone,f.startHour).length===0)throw Error(`No billable usage events found in Daily Window ${f.dailyWindow} (start hour ${f.startHour}, ${f.timeZone}).`)}let g=await Zf(),$=process.env.CHROME_PATH!==void 0?{headless:!0,executablePath:process.env.CHROME_PATH}:{headless:!0,channel:process.env.PLAYWRIGHT_CHROME_CHANNEL??"chrome"},Y;try{Y=await g.launch($)}catch(c){throw Error(["Could not launch Chrome for screenshot export.","Install Google Chrome, set CHROME_PATH, or set PLAYWRIGHT_CHROME_CHANNEL to an installed Chromium channel.",String(c)].join(`
15
- `))}let V=wf(f),z=await Z({port:0});try{let c=await Y.newPage({viewport:{width:1200,height:900}});await c.addInitScript({content:`window.__CURSOR_USAGE_EVENTS__ = ${JSON.stringify(bf(f.events))};`});let A=new URL(z.url);A.hash=new URLSearchParams({timezone:f.timeZone,...f.user?{user:f.user}:{},...f.dailyWindow?{"daily-window":f.dailyWindow}:{},...f.startHour!==0?{"start-hour":String(f.startHour)}:{},...f.eventLimit!==void 0?{"event-limit":String(f.eventLimit)}:{}}).toString(),await c.goto(A.href,{waitUntil:"networkidle"}),await c.waitForSelector(".grid",{timeout:1e4}),await c.screenshot({path:V,fullPage:!0})}finally{z.server.close(),await Y.close()}return V}var Vf=`cursor-usage — visualize Cursor usage-events CSV
13
+ `}function Yf(f,g,$,Y,z=0){let c=R(f,g,$,z);return JSON.stringify({dailyWindow:g,timeZone:$,startHour:z,filters:{user:Y??null},summary:N(c,$,z),byHour:W(c,$),byModel:O(c),byUser:D(c),byKind:H(c)},null,2)}import{basename as wf,dirname as bf,extname as Zf,join as Vf}from"node:path";function vf(f){let{csvPath:g,dailyWindow:$,dailyReport:Y,out:z}=f;if(z)return z;if(Y)return Vf(process.cwd(),"daily-report.png");let c=Zf(g),V=wf(g,c);return Vf(bf(g),`${V}${$?`-${$}-daily`:""}.png`)}async function uf(){let f=Function("specifier","return import(specifier)");try{let g=await f("playwright-core");if(!g.chromium)throw Error("playwright-core did not expose chromium");return g.chromium}catch(g){throw Error(`Screenshot export requires playwright-core. Install dependencies and try again.
14
+ ${String(g)}`)}}function lf(f){return f.map((g)=>({...g,date:g.date.toISOString()}))}async function Z(f){if(f.events.length===0)throw Error("No billable usage events found in the Usage Export.");if(f.dailyWindow){if(R(f.events,f.dailyWindow,f.timeZone,f.startHour).length===0)throw Error(`No billable usage events found in Daily Window ${f.dailyWindow} (start hour ${f.startHour}, ${f.timeZone}).`)}let g=await uf(),$=process.env.CHROME_PATH!==void 0?{headless:!0,executablePath:process.env.CHROME_PATH}:{headless:!0,channel:process.env.PLAYWRIGHT_CHROME_CHANNEL??"chrome"},Y;try{Y=await g.launch($)}catch(V){throw Error(["Could not launch Chrome for screenshot export.","Install Google Chrome, set CHROME_PATH, or set PLAYWRIGHT_CHROME_CHANNEL to an installed Chromium channel.",String(V)].join(`
15
+ `))}let z=vf(f),c=await w({port:0});try{let V=await Y.newPage({viewport:{width:1200,height:900}});await V.addInitScript({content:`window.__CURSOR_USAGE_EVENTS__ = ${JSON.stringify(lf(f.events))};`});let J=new URL(c.url);J.hash=new URLSearchParams({timezone:f.timeZone,...f.user?{user:f.user}:{},...f.dailyWindow?{"daily-window":f.dailyWindow}:{},...f.startHour!==0?{"start-hour":String(f.startHour)}:{},...f.eventLimit!==void 0?{"event-limit":String(f.eventLimit)}:{}}).toString(),await V.goto(J.href,{waitUntil:"networkidle"}),await V.waitForSelector(".grid",{timeout:1e4}),await V.screenshot({path:z,fullPage:!0})}finally{c.server.close(),await Y.close()}return z}var cf=`cursor-usage — visualize Cursor usage-events CSV
16
16
 
17
17
  Usage:
18
18
  cursor-usage [serve] [--port <n>] [--no-open] Start the drag & drop dashboard (default)
@@ -45,4 +45,4 @@ Serve options:
45
45
 
46
46
  -h, --help Show this help
47
47
  `;function L(f){console.error(`Error: ${f}
48
- `),console.error(Vf),process.exit(1)}function zf(f,g){if(f===void 0)return g;let $=Number(f);if(!H($))L(`invalid --start-hour value: ${f} (expected 0-23)`);return $}function uf(f){if(f===void 0)return;let g=Number(f);if(!Number.isInteger(g)||g<=0)L(`invalid --event-limit value: ${f} (expected a positive integer)`);return g}async function u(f,g,$,Y="No usage events found for the requested filters."){let V;try{V=await vf(f,"utf8")}catch{L(`file not found: ${f}`)}let z=r(V);if(!g)z=i(z);if($)z=z.filter((c)=>c.user===$);if(z.length===0)L(Y);return z}async function lf(f){let{values:g,positionals:$}=h({args:f,allowPositionals:!0,options:{by:{type:"string"},"daily-window":{type:"string"},"start-hour":{type:"string"},user:{type:"string"},timezone:{type:"string"},json:{type:"boolean",default:!1},"include-no-charge":{type:"boolean",default:!1}}}),Y=$[0];if(!Y)L("stats requires a path to a CSV file");let V=g.by;if(V&&!["daily-window","user","model"].includes(V))L(`invalid --by value: ${V} (expected daily-window, user or model)`);let z=g["daily-window"];if(z!==void 0&&!/^\d{4}-\d{2}-\d{2}$/.test(z))L(`invalid --daily-window value: ${z} (expected YYYY-MM-DD)`);let c=g.timezone??_();if(!E(c))L(`invalid --timezone value: ${c}`);let A=zf(g["start-hour"],0),J=g.user,S=await u(Y,g["include-no-charge"],J);if(z){console.log(g.json?$f(S,z,c,J,A):gf(S,z,c,J,A));return}console.log(g.json?ff(S,c,J,A):e(S,V,c,J,A))}async function yf(f){let{values:g,positionals:$}=h({args:f,allowPositionals:!0,options:{"daily-window":{type:"string"},"start-hour":{type:"string"},"event-limit":{type:"string"},out:{type:"string"},user:{type:"string"},timezone:{type:"string"},"include-no-charge":{type:"boolean",default:!1}}}),Y=$[0];if(!Y)L("screenshot requires a path to a CSV file");let V=g.timezone??_();if(!E(V))L(`invalid --timezone value: ${V}`);let z=g["daily-window"];if(z!==void 0&&!/^\d{4}-\d{2}-\d{2}$/.test(z))L(`invalid --daily-window value: ${z} (expected YYYY-MM-DD)`);let c=zf(g["start-hour"],0),A=uf(g["event-limit"]),J=await u(Y,g["include-no-charge"],g.user),S=await v({csvPath:Y,events:J,timeZone:V,dailyWindow:z,startHour:c,eventLimit:A,dailyReport:!1,out:g.out,user:g.user});console.log(`wrote ${S}`)}async function nf(f){let{positionals:g}=h({args:f,allowPositionals:!0,options:{}}),$=g[0];if(!$)L("daily-report requires a path to a CSV file");if(g.length>1)L(`unexpected argument: ${g[1]}`);let Y=_(),V=5,z=await u($,!1,void 0,"No billable usage events found in the Usage Export."),c=m(z,Y,V);if(!c)L("No billable usage events found in the Usage Export.");let A=await v({csvPath:$,events:z,timeZone:Y,dailyWindow:c,startHour:V,eventLimit:10,dailyReport:!0});console.log(`wrote ${A}`)}function cf(f){let{values:g}=h({args:f,allowPositionals:!1,options:{port:{type:"string"},"no-open":{type:"boolean",default:!1}}}),$;if(g.port!==void 0){if($=Number(g.port),!Number.isInteger($)||$<0||$>65535)L(`invalid --port value: ${g.port}`)}s({port:$,open:!g["no-open"]})}async function mf(){let f=process.argv.slice(2);if(f.includes("-h")||f.includes("--help")){console.log(Vf);return}let[g,...$]=f;switch(g){case"stats":await lf($);break;case"screenshot":await yf($);break;case"daily-report":await nf($);break;case"serve":cf($);break;case void 0:cf([]);break;default:L(`unknown command: ${g}`)}}await mf();
48
+ `),console.error(cf),process.exit(1)}function Jf(f,g){if(f===void 0)return g;let $=Number(f);if(!E($))L(`invalid --start-hour value: ${f} (expected 0-23)`);return $}function nf(f){if(f===void 0)return;let g=Number(f);if(!Number.isInteger(g)||g<=0)L(`invalid --event-limit value: ${f} (expected a positive integer)`);return g}async function v(f,g,$,Y="No usage events found for the requested filters."){let z;try{z=await yf(f,"utf8")}catch{L(`file not found: ${f}`)}let c=a(z);if(!g)c=d(c);if($)c=c.filter((V)=>V.user===$);if(c.length===0)L(Y);return c}async function mf(f){let{values:g,positionals:$}=p({args:f,allowPositionals:!0,options:{by:{type:"string"},"daily-window":{type:"string"},"start-hour":{type:"string"},user:{type:"string"},timezone:{type:"string"},json:{type:"boolean",default:!1},"include-no-charge":{type:"boolean",default:!1}}}),Y=$[0];if(!Y)L("stats requires a path to a CSV file");let z=g.by;if(z&&!["daily-window","user","model"].includes(z))L(`invalid --by value: ${z} (expected daily-window, user or model)`);let c=g["daily-window"];if(c!==void 0&&!/^\d{4}-\d{2}-\d{2}$/.test(c))L(`invalid --daily-window value: ${c} (expected YYYY-MM-DD)`);let V=g.timezone??x();if(!h(V))L(`invalid --timezone value: ${V}`);let J=Jf(g["start-hour"],0),S=g.user,A=await v(Y,g["include-no-charge"],S);if(c){console.log(g.json?Yf(A,c,V,S,J):$f(A,c,V,S,J));return}console.log(g.json?gf(A,V,S,J):ff(A,z,V,S,J))}async function df(f){let{values:g,positionals:$}=p({args:f,allowPositionals:!0,options:{"daily-window":{type:"string"},"start-hour":{type:"string"},"event-limit":{type:"string"},out:{type:"string"},user:{type:"string"},timezone:{type:"string"},"include-no-charge":{type:"boolean",default:!1}}}),Y=$[0];if(!Y)L("screenshot requires a path to a CSV file");let z=g.timezone??x();if(!h(z))L(`invalid --timezone value: ${z}`);let c=g["daily-window"];if(c!==void 0&&!/^\d{4}-\d{2}-\d{2}$/.test(c))L(`invalid --daily-window value: ${c} (expected YYYY-MM-DD)`);let V=Jf(g["start-hour"],0),J=nf(g["event-limit"]),S=await v(Y,g["include-no-charge"],g.user),A=await Z({csvPath:Y,events:S,timeZone:z,dailyWindow:c,startHour:V,eventLimit:J,dailyReport:!1,out:g.out,user:g.user});console.log(`wrote ${A}`)}async function rf(f){let{positionals:g}=p({args:f,allowPositionals:!0,options:{}}),$=g[0];if(!$)L("daily-report requires a path to a CSV file");if(g.length>1)L(`unexpected argument: ${g[1]}`);let Y=x(),z=5,c=await v($,!1,void 0,"No billable usage events found in the Usage Export."),V=m(c,Y,z);if(!V)L("No billable usage events found in the Usage Export.");let J=await Z({csvPath:$,events:c,timeZone:Y,dailyWindow:V,startHour:z,eventLimit:10,dailyReport:!0});console.log(`wrote ${J}`)}function zf(f){let{values:g}=p({args:f,allowPositionals:!1,options:{port:{type:"string"},"no-open":{type:"boolean",default:!1}}}),$;if(g.port!==void 0){if($=Number(g.port),!Number.isInteger($)||$<0||$>65535)L(`invalid --port value: ${g.port}`)}o({port:$,open:!g["no-open"]})}async function af(){let f=process.argv.slice(2);if(f.includes("-h")||f.includes("--help")){console.log(cf);return}let[g,...$]=f;switch(g){case"stats":await mf($);break;case"screenshot":await df($);break;case"daily-report":await rf($);break;case"serve":zf($);break;case void 0:zf([]);break;default:L(`unknown command: ${g}`)}}await af();