usage-board 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/index.mjs +18 -9
  2. package/dist/public/_nuxt/B-CIibMz.js +25 -0
  3. package/dist/public/_nuxt/BFLIXB9O.js +9 -0
  4. package/dist/public/_nuxt/{DcPhnO3V.js → BNPuv5_2.js} +18 -18
  5. package/dist/public/_nuxt/Brp07rVz.js +3 -0
  6. package/dist/public/_nuxt/Bvg0CuDs.js +258 -0
  7. package/dist/public/_nuxt/CvpNFEwm.js +1 -0
  8. package/dist/public/_nuxt/D3pogf62.js +1 -0
  9. package/dist/public/_nuxt/{DLRBRF52.js → D9KD20BD.js} +1 -1
  10. package/dist/public/_nuxt/DUfyK9T8.js +21 -0
  11. package/dist/public/_nuxt/{BKFuB2T3.js → DsWoOjFk.js} +2 -2
  12. package/dist/public/_nuxt/builds/latest.json +1 -1
  13. package/dist/public/_nuxt/builds/meta/38460625-fa40-4f13-86da-ddec4bfb4ab8.json +1 -0
  14. package/dist/public/_nuxt/entry.CJkVK2j6.css +1 -0
  15. package/dist/public/_nuxt/{CileeSf2.js → f_iPFAtL.js} +1 -1
  16. package/dist/server/chunks/_/error-500.mjs +14 -0
  17. package/dist/server/chunks/_/index.min.mjs +348 -0
  18. package/dist/server/chunks/_/shared.cjs.prod.mjs +1 -1
  19. package/dist/server/chunks/build/client.precomputed.mjs +1 -1
  20. package/dist/server/chunks/nitro/nitro.mjs +164 -815
  21. package/dist/server/chunks/routes/api/payload.json.mjs +10 -348
  22. package/dist/server/chunks/routes/renderer.mjs +1 -2
  23. package/dist/server/chunks/routes/ws.mjs +1063 -0
  24. package/dist/server/index.mjs +1 -2
  25. package/package.json +6 -2
  26. package/dist/public/_nuxt/BNf7fR7M.js +0 -282
  27. package/dist/public/_nuxt/DAg3mJyk.js +0 -1
  28. package/dist/public/_nuxt/DoxGa6D0.js +0 -9
  29. package/dist/public/_nuxt/DptRzj9K.js +0 -3
  30. package/dist/public/_nuxt/builds/meta/12cca5fc-d3df-4df1-b723-c944bdd0c587.json +0 -1
  31. package/dist/public/_nuxt/entry.DrzELaFO.css +0 -1
package/dist/index.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  import { createServer } from "node:http";
2
- import path, { resolve } from "node:path";
2
+ import path, { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
3
4
  import cac from "cac";
4
5
  import { createServer as createServer$1 } from "node:net";
5
6
  import os, { networkInterfaces } from "node:os";
6
7
  import fs, { constants } from "node:fs/promises";
7
8
  import process$1 from "node:process";
8
- import { fileURLToPath } from "node:url";
9
9
  import childProcess, { execFile } from "node:child_process";
10
10
  import { promisify } from "node:util";
11
11
  import fs$1 from "node:fs";
@@ -756,23 +756,32 @@ defineLazyProperty(apps, "safari", () => detectPlatformBinary({ darwin: "Safari"
756
756
  //#endregion
757
757
  //#region package.json
758
758
  var name = "usage-board";
759
- var version = "1.0.0";
759
+ var version = "2.0.0";
760
760
  //#endregion
761
761
  //#region src/index.ts
762
762
  const cli = cac(name);
763
- async function loadNitroListener(outputDir) {
763
+ async function loadNitroEntrypoint(outputDir) {
764
764
  const mod = await import(resolve(outputDir, "server/index.mjs"));
765
- return mod.listener ?? mod.middleware ?? mod.handler ?? mod.default ?? mod;
765
+ return {
766
+ listener: mod.listener ?? mod.middleware ?? mod.handler ?? mod.default,
767
+ websocket: mod.websocket
768
+ };
766
769
  }
767
770
  cli.command("", "Start tokens usage analysis").option("--host <host>", "Host", { default: "127.0.0.1" }).option("--port <port>", "Port", { default: 7777 }).option("--open", "Open browser", { default: true }).action(async (option) => {
768
771
  const port = await getPort({
769
772
  port: option.port,
770
773
  portRange: [7777, 9e3]
771
774
  });
772
- const lister = await loadNitroListener(resolve(process.cwd(), "dist"));
773
- createServer(async (req, res) => {
774
- await lister(req, res);
775
- }).listen(port, option.host, async () => {
775
+ const nitro = await loadNitroEntrypoint(resolve(dirname(fileURLToPath(import.meta.url)), "./"));
776
+ const app = createServer(async (req, res) => {
777
+ await nitro.listener(req, res);
778
+ });
779
+ if (nitro.websocket) {
780
+ const { default: wsAdapter } = await import("crossws/adapters/node");
781
+ const { handleUpgrade } = wsAdapter(nitro.websocket);
782
+ app.on("upgrade", handleUpgrade);
783
+ }
784
+ app.listen(port, option.host, async () => {
776
785
  if (option.open) {
777
786
  const url = `http://${option.host}:${port}`;
778
787
  console.log(`Usage board is running at ${url}`);
@@ -0,0 +1,25 @@
1
+ import{w as W,S as Z,C as ee,a as J,z as te,A as ne,_ as oe,f as y,d as C}from"./Bvg0CuDs.js";import{z as K,o as u,B as X,w as P,b as _,D as i,a as o,c as f,Y as T,X as $,h as r,E as H,t as m,ae,a5 as O,H as q}from"./BFLIXB9O.js";const se={class:"mt-4 flex flex-wrap items-center justify-center gap-3 text-xs text-muted-foreground"},re={class:"font-medium text-foreground tabular-nums"},le=K({name:"StatisticalAnalysisModelUsagePanel",__name:"ModelUsagePanel",props:{monthlyItems:{},year:{}},setup(D){const g=D,v=["#2563eb","#f97316","#0891b2","#8b5cf6","#059669","#f43f5e"],M={bottom:32,left:56,right:12,top:8},S=[0,void 0],x=r(()=>g.year??A(g.monthlyItems)??new Date().getFullYear()),b=r(()=>Array.from({length:12},(t,e)=>{const s=`${e+1}`.padStart(2,"0");return`${x.value}-${s}`})),U=r(()=>[0,Math.max(b.value.length-1,0)]),p=r(()=>b.value.map((t,e)=>e)),k=r(()=>g.monthlyItems.filter(t=>t.month.startsWith(`${x.value}-`))),h=r(()=>Array.from(new Set(k.value.map(t=>t.model)))),w=r(()=>b.value.map((t,e)=>{const s=Object.fromEntries(h.value.map(c=>[c,k.value.find(d=>d.month===t&&d.model===c)?.tokenTotal??0]));return{month:t,monthIndex:e,tokensByModel:s,totalTokens:Object.values(s).reduce((c,d)=>c+d,0)}})),l=r(()=>h.value.map((t,e)=>{const s=n(e),c=w.value.reduce((d,Q)=>d+(Q.tokensByModel[t]??0),0);return{color:s,model:t,totalLabel:y(c),totalTokens:c}}).sort((t,e)=>e.totalTokens-t.totalTokens)),L=r(()=>l.value.map(t=>e=>e.tokensByModel[t.model]??0)),E=r(()=>Object.fromEntries(l.value.map(t=>[t.model,{color:t.color,label:t.model}]))),G=r(()=>l.value.map(t=>`
2
+ <linearGradient id="${F(t.model)}" x1="0" y1="0" x2="0" y2="1">
3
+ <stop offset="0%" stop-color="${t.color}" stop-opacity="0.45" />
4
+ <stop offset="100%" stop-color="${t.color}" stop-opacity="0.08" />
5
+ </linearGradient>
6
+ `).join(""));function N(t){return t.monthIndex}function I(t,e){const s=l.value[e];return s?`url(#${F(s.model)})`:n(0)}function z(t,e){return l.value[e]?.color??n(0)}function Y(t,e){return l.value[e]?.color??n(0)}function R(t){const e=l.value.map(s=>({...s,tokenTotal:t.tokensByModel[s.model]??0})).sort((s,c)=>c.tokenTotal-s.tokenTotal);return`
7
+ <div class="grid min-w-48 gap-2 rounded-md border bg-background px-3 py-2 text-xs shadow-lg">
8
+ <div class="font-medium text-foreground">${j(t.month)} ${t.month.slice(0,4)}</div>
9
+ <div class="grid gap-1 text-muted-foreground">
10
+ ${e.map(s=>`
11
+ <div class="flex items-center justify-between gap-4">
12
+ <span class="flex items-center gap-2">
13
+ <span class="size-2 rounded-sm" style="background-color: ${s.color}"></span>
14
+ ${a(s.model)}
15
+ </span>
16
+ <span class="font-mono font-medium text-foreground">${y(s.tokenTotal)}</span>
17
+ </div>
18
+ `).join("")}
19
+ </div>
20
+ <div class="flex justify-between gap-4 border-t pt-2 text-muted-foreground">
21
+ <span>Total</span>
22
+ <span class="font-mono font-semibold text-foreground">${y(t.totalTokens)}</span>
23
+ </div>
24
+ </div>
25
+ `}function V(t){if(t instanceof Date)return"";const e=b.value[t];return e?j(e):""}function B(t){return t instanceof Date?"":y(t)}function j(t){const[e,s]=t.split("-"),c=new Date(Number(e),Number(s)-1,1);return new Intl.DateTimeFormat("en-US",{month:"short"}).format(c)}function A(t){const s=[...t].sort((c,d)=>d.month.localeCompare(c.month))[0]?.month?.split("-")[0];return s?Number(s):null}function F(t){return`model-usage-${t.replace(/[^a-z0-9]+/gi,"-").toLowerCase()}`}function n(t){return v[t%v.length]??"#2563eb"}function a(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}return(t,e)=>{const s=oe,c=W;return u(),X(c,{description:"Monthly token trends by model",icon:"solar:cpu-line-duotone",title:"Model Usage"},{default:P(()=>[_(s,{class:"h-72 w-full",config:i(E)},{default:P(()=>[_(i(Z),{"auto-margin":!1,data:i(w),height:288,margin:M,"svg-defs":i(G),"x-domain":i(U),"y-domain":S},{default:P(()=>[_(i(ee),{color:I,line:!0,"line-color":z,"line-width":2.5,opacity:.82,x:N,y:i(L)},null,8,["y"]),_(i(J),{"grid-line":!1,"tick-format":V,"tick-values":i(p),type:"x"},null,8,["tick-values"]),_(i(J),{"num-ticks":4,"tick-format":B,type:"y"}),_(i(te)),_(i(ne),{color:Y,template:R,x:N,"y-stacked":i(L)},null,8,["y-stacked"])]),_:1},8,["data","svg-defs","x-domain"])]),_:1},8,["config"]),o("div",se,[(u(!0),f($,null,T(i(l),d=>(u(),f("div",{key:d.model,class:"flex items-center gap-2"},[o("span",{class:"size-2.5 rounded-sm",style:H({backgroundColor:d.color})},null,4),o("span",null,m(d.model),1),o("span",re,m(d.totalLabel),1)]))),128))])]),_:1})}}}),He=Object.assign(le,{__name:"StatisticalAnalysisModelUsagePanel"}),ce={class:"space-y-3"},de={class:"flex flex-col flex-1"},ie={class:"truncate text-sm font-medium tracking-tight"},ue={class:"mt-1 truncate text-xs text-muted-foreground"},me={class:"text-right"},ge={class:"text-xs font-medium tracking-tight tabular-nums"},pe={class:"mt-1 flex items-center justify-end gap-1"},fe=K({name:"StatisticalAnalysisProjectUsagePanel",__name:"ProjectUsagePanel",props:{items:{}},setup(D){const g=D,v=[{trend:"+12.4%",trendTone:"up"},{trend:"-8.2%",trendTone:"down"},{trend:"+22.1%",trendTone:"up"},{trend:"+15.8%",trendTone:"up"},{trend:"-3.4%",trendTone:"down"},{trend:"+9.6%",trendTone:"up"},{trend:"-5.1%",trendTone:"down"},{trend:"+4.7%",trendTone:"up"}],M=r(()=>g.items.slice(0,6).map((p,k)=>{const h=v[k]??{trend:"+0.0%",trendTone:"up"};return{...p,shortName:U(p.label),trend:h.trend,trendTone:h.trendTone}}));function S(p){return p==="down"?"lucide:trending-down":"lucide:trending-up"}function x(p){return p==="down"?"size-3 text-red-500":"size-3 text-emerald-600 dark:text-emerald-400"}function b(p){return p==="down"?"text-sm font-medium text-red-500 tabular-nums":"text-sm font-medium text-emerald-600 dark:text-emerald-400 tabular-nums"}function U(p){return p.split(/[-_\s]+/).filter(Boolean).slice(0,2).map(k=>k[0]?.toUpperCase()??"").join("")}return(p,k)=>{const h=ae,w=W;return u(),X(w,{description:"Best performers by spend",icon:"lucide:folder-git-2",title:"Top Projects"},{default:P(()=>[o("div",ce,[(u(!0),f($,null,T(i(M),l=>(u(),f("div",{key:l.repository,class:"flex justify-between items-center gap-3 rounded-md transition-colors"},[o("div",de,[o("p",ie,m(l.label),1),o("p",ue,m(l.detail),1)]),o("div",me,[o("p",ge,m(l.value),1),o("div",pe,[_(h,{class:O(x(l.trendTone)),mode:"svg",name:S(l.trendTone)},null,8,["class","name"]),o("span",{class:O(b(l.trendTone))},m(l.trend),3)])])]))),128))])]),_:1})}}}),Oe=Object.assign(fe,{__name:"StatisticalAnalysisProjectUsagePanel"}),be={class:"mb-4 grid grid-cols-1 gap-3 sm:grid-cols-3"},ye={class:"text-xs text-muted-foreground"},ke={class:"mt-1 text-lg font-semibold tabular-nums"},he={class:"border-b pb-4"},_e=["aria-label","tabindex"],ve={class:"sr-only"},xe={key:0,class:"pointer-events-none absolute bottom-full left-1/2 z-30 mb-2 hidden min-w-40 -translate-x-1/2 gap-1 rounded-md border bg-popover px-2.5 py-1.5 text-xs text-popover-foreground shadow-md group-hover:grid group-focus-visible:grid",role:"tooltip"},we={class:"font-medium"},Te={key:0,class:"flex items-center justify-between gap-4 text-muted-foreground"},$e={class:"font-mono font-medium text-foreground"},Ce={class:"flex items-center justify-between gap-4 text-muted-foreground"},De={class:"font-mono font-medium text-foreground"},Me={key:1,class:"flex items-center justify-between gap-4 text-muted-foreground"},Se={class:"font-mono font-medium text-foreground"},Ue={class:"mt-4 flex items-center justify-between gap-4"},Le={class:"text-xs text-muted-foreground"},Be={class:"flex items-center gap-1 text-xs text-muted-foreground"},je={class:"mt-4 grid grid-cols-1 gap-3 sm:grid-cols-3"},Ae={class:"text-xs text-muted-foreground"},Pe={class:"mt-1 flex items-center justify-between gap-2"},Ne={class:"text-sm font-semibold"},Ie={class:"text-xs text-muted-foreground"},ze=K({name:"UsageHeatmapPanel",__name:"UsageHeatmapPanel",props:{heatMetric:{default:"tokens"},items:{},title:{}},setup(D){const g=D,v=["bg-zinc-100 dark:bg-zinc-800/80","bg-emerald-50 dark:bg-emerald-950/70","bg-emerald-100 dark:bg-emerald-900/70","bg-teal-100 dark:bg-teal-900/75","bg-teal-200 dark:bg-teal-800/80","bg-cyan-200 dark:bg-cyan-800/85","bg-cyan-300 dark:bg-cyan-700/90","bg-sky-300 dark:bg-sky-600/90","bg-sky-400 dark:bg-sky-500/95","bg-blue-500 dark:bg-blue-400"],M=[...v],S=[{key:"sun",label:"S",row:"1"},{key:"mon",label:"M",row:"2"},{key:"tue",label:"T",row:"3"},{key:"wed",label:"W",row:"4"},{key:"thu",label:"T",row:"5"},{key:"fri",label:"F",row:"6"},{key:"sat",label:"S",row:"7"}],x=r(()=>B(new Date)),b=r(()=>{const n=B(x.value);return n.setDate(n.getDate()-364),n}),U=r(()=>`${A(b.value)} - ${A(x.value)}`),p=r(()=>g.heatMetric==="cost"?"spend":"tokens"),k=r(()=>`${U.value} ${p.value} heatmap. Darker cells mean higher daily ${p.value}.`),h=r(()=>`Colored by daily ${p.value}`),w=r(()=>{const n=new Map(g.items.map(a=>[j(V(a.date)),a]));return Array.from({length:365},(a,t)=>{const e=B(b.value);return e.setDate(e.getDate()+t),{date:e,usage:n.get(j(e))}})}),l=r(()=>{const n=Math.max(...w.value.map(a=>g.heatMetric==="cost"?a.usage?.costUSD??0:a.usage?.totalTokens??0));return w.value.map(a=>{const t=a.usage?.costUSD??0,e=a.usage?.totalTokens??0,s=g.heatMetric==="cost"?t:e;return{colorClass:v[F(s,n)],costLabel:C(t),costUSD:t,date:A(a.date),hasUsage:!!a.usage,tokenLabel:y(e),totalTokens:e}})}),L=r(()=>{const n=b.value.getDay(),a=(7-(n+l.value.length)%7)%7,t=Array.from({length:n},(c,d)=>({column:String(Math.floor(d/7)+2),colorClass:"bg-transparent",costLabel:"",date:"",isBlank:!0,key:`blank-${d}`,row:String(d%7+1),title:"No date",tokenLabel:""})),e=l.value.map((c,d)=>({...c,column:String(Math.floor((n+d)/7)+2),isBlank:!1,key:c.date,row:String((n+d)%7+1),title:g.heatMetric==="cost"?`${c.date}: ${C(c.costUSD)} / ${y(c.totalTokens)} tokens`:`${c.date}: ${y(c.totalTokens)} tokens / ${C(c.costUSD)}`})),s=Array.from({length:a},(c,d)=>({column:String(Math.floor((n+l.value.length+d)/7)+2),colorClass:"bg-transparent",costLabel:"",date:"",isBlank:!0,key:`trailing-blank-${d}`,row:String((n+l.value.length+d)%7+1),title:"No date",tokenLabel:""}));return[...t,...e,...s]}),E=r(()=>Math.ceil(L.value.length/7)),G=r(()=>({gridTemplateColumns:`max-content repeat(${E.value}, minmax(0, 1fr))`,gridTemplateRows:"repeat(7, minmax(0, 1fr))"})),N=r(()=>l.value.filter(n=>n.hasUsage).slice(-3)),I=r(()=>l.value.filter(n=>n.hasUsage).length),z=r(()=>C(l.value.reduce((n,a)=>n+a.costUSD,0))),Y=r(()=>y(l.value.reduce((n,a)=>n+a.totalTokens,0))),R=r(()=>g.heatMetric==="cost"?[{key:"year-cost",label:"Year Spend",value:z.value},{key:"year-tokens",label:"Year Tokens",value:Y.value},{key:"active-days",label:"Active Days",value:String(I.value)}]:[{key:"year-tokens",label:"Year Tokens",value:Y.value},{key:"year-cost",label:"Year Spend",value:z.value},{key:"active-days",label:"Active Days",value:String(I.value)}]);function V(n){return new Date(n)}function B(n){return new Date(n.getFullYear(),n.getMonth(),n.getDate())}function j(n){const a=n.getFullYear(),t=`${n.getMonth()+1}`.padStart(2,"0"),e=`${n.getDate()}`.padStart(2,"0");return`${a}-${t}-${e}`}function A(n){return new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short",year:"numeric"}).format(n)}function F(n,a){return n<=0||a<=0?0:Math.min(9,Math.max(1,Math.ceil(n/a*9)))}return(n,a)=>{const t=W;return u(),X(t,{description:k.value,icon:"lucide:calendar-days",title:g.title},{default:P(()=>[o("div",be,[(u(!0),f($,null,T(R.value,e=>(u(),f("div",{key:e.key,class:"rounded-md border px-3 py-2"},[o("p",ye,m(e.label),1),o("p",ke,m(e.value),1)]))),128))]),o("div",he,[o("div",{class:"grid w-full gap-1",style:H(G.value)},[(u(),f($,null,T(S,e=>o("span",{key:e.key,class:"flex items-center justify-end pr-1 text-[10px] text-muted-foreground",style:H({gridColumn:"1",gridRow:e.row})},m(e.label),5)),64)),(u(!0),f($,null,T(L.value,e=>(u(),f("div",{key:e.key,class:O(["group relative aspect-square w-full max-w-3 justify-self-center rounded-sm border border-black/5 outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring/50 dark:border-white/10",[e.colorClass,{"border-transparent opacity-0":e.isBlank}]]),"aria-label":e.title,style:H({gridColumn:e.column,gridRow:e.row}),tabindex:e.isBlank?-1:0},[o("span",ve,m(e.title),1),e.isBlank?q("",!0):(u(),f("div",xe,[o("span",we,m(e.date),1),g.heatMetric==="tokens"?(u(),f("span",Te,[a[0]||(a[0]=o("span",null,"Tokens",-1)),o("span",$e,m(e.tokenLabel),1)])):q("",!0),o("span",Ce,[a[1]||(a[1]=o("span",null,"Cost",-1)),o("span",De,m(e.costLabel),1)]),g.heatMetric==="cost"?(u(),f("span",Me,[a[2]||(a[2]=o("span",null,"Tokens",-1)),o("span",Se,m(e.tokenLabel),1)])):q("",!0),a[3]||(a[3]=o("span",{class:"absolute top-full left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rotate-45 border-r border-b border-border bg-popover"},null,-1))]))],14,_e))),128))],4),o("div",Ue,[o("p",Le,m(h.value),1),o("div",Be,[a[4]||(a[4]=o("span",null,"Less",-1)),(u(),f($,null,T(M,e=>o("span",{key:e,class:O(["size-3 rounded-[2px] border border-black/5 dark:border-white/10",e])},null,2)),64)),a[5]||(a[5]=o("span",null,"More",-1))])])]),o("div",je,[(u(!0),f($,null,T(N.value,e=>(u(),f("div",{key:e.date,class:"rounded-md border px-3 py-2"},[o("p",Ae,m(e.date),1),o("div",Pe,[o("span",Ne,m(g.heatMetric==="cost"?("formatCurrency"in n?n.formatCurrency:i(C))(e.costUSD):("formatCompactNumber"in n?n.formatCompactNumber:i(y))(e.totalTokens)),1),o("span",Ie,m(g.heatMetric==="cost"?("formatCompactNumber"in n?n.formatCompactNumber:i(y))(e.totalTokens):("formatCurrency"in n?n.formatCurrency:i(C))(e.costUSD)),1)])]))),128))])]),_:1},8,["description","title"])}}}),Ee=Object.assign(ze,{__name:"UsageHeatmapPanel"});export{He as _,Oe as a,Ee as b};