@hydra-acp/budgeter 0.1.17 → 0.1.19

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{relative as H,resolve as A}from"node:path";import{homedir as L}from"node:os";import{realpathSync as $}from"node:fs";function V(l){const c=l.match(/^(\d+)\s*([dhmwy])$/i);if(c!==null){const T=c[1],f=c[2];if(T===void 0||f===void 0)throw new Error(`invalid since spec: ${l}`);const m=parseInt(T,10),a=f.toLowerCase(),r=new Date;return a==="d"?r.setDate(r.getDate()-m):a==="h"?r.setHours(r.getHours()-m):a==="w"?r.setDate(r.getDate()-m*7):a==="m"?r.setMonth(r.getMonth()-m):a==="y"&&r.setFullYear(r.getFullYear()-m),r}const t=new Date(l);if(Number.isNaN(t.getTime()))throw new Error(`invalid since spec: ${l}`);return t}function q(l){const c=l.locByFiletype;if(c===void 0)return 0;let t=0;for(const T of Object.keys(c)){const f=c[T];f!==void 0&&(t+=f.added-f.removed)}return t}const I=new Map;function D(l){if(I.has(l))return I.get(l);try{const c=$(l);return I.set(l,c),c}catch{return I.set(l,l),l}}function P(l,c){let t=l;if(c.since!==void 0){const m=c.since.getTime(),a=[];for(const r of t)new Date(r.updatedAt).getTime()<m||a.push(r);t=a}if(c.dir!==void 0){const m=D(A(c.dir)),a=[];for(const r of t)r.cwd===void 0||r.cwd===""||(r.cwd.startsWith(m+"/")||r.cwd===m)&&a.push(r);t=a}if(c.host!==void 0&&c.host!=="all"){const m=c.host,a=[];for(const r of t)m==="local"?(!r.importedFromMachine||r.upstreamSessionId)&&a.push(r):r.importedFromMachine===m&&!r.upstreamSessionId&&a.push(r);t=a}if(c.interactive!==void 0){const m=c.interactive,a=[];for(const r of t)r.interactive===m&&a.push(r);t=a}const T=c.min??0,f=[];for(const m of t){let a;c.minMetric==="tokens"?a=m.contextTokens:c.minMetric==="loc"?a=q(m):a=m.costAmount,a>T&&f.push(m)}return f}function Y(l){return l.dir!==void 0?A(l.dir):A(L())}function K(l,c,t){if(l===void 0||l==="")return"<unknown>";const T=D(A(l));if(c===void 0){const v=D(A(L()));return T===v?"~":T.startsWith(v+"/")?"~/"+T.slice(v.length+1):T}const f=D(A(t));let m=H(f,T);if(m===""||m===".")return"<root>";if(m.startsWith(".."))return"<unknown>";const a=m.split("/");if(a.length===0)return"<root>";const r=Math.min(c,a.length),S=a.slice(0,r);return S.length===0?"<root>":S.join("/")}function X(l,c,t={},T){let f=t.since;if(f===void 0&&t.bucket!==void 0){const e=new Date;t.bucket==="hour"?(e.setHours(e.getHours()-24),f=e):t.bucket==="day"||t.bucket==="weekday"?(e.setDate(e.getDate()-30),f=e):t.bucket==="week"?(e.setMonth(e.getMonth()-6),f=e):t.bucket==="month"&&(e.setFullYear(e.getFullYear()-2),f=e)}if(!(f!==void 0||t.bucket!==void 0||t.tokens===!0||t.loc===!0||t.by!==void 0||t.dir!==void 0||t.interactive!==void 0)){let e=0;for(const o of l)e+=o.costAmount;let s="";for(const o of l){s=o.costCurrency;break}return{kind:"total",row:{label:"All sessions",costAmount:e,sessionCount:l.length},currency:s}}const a={since:f,dir:t.dir,interactive:t.interactive,min:t.min,minMetric:t.loc===!0?"loc":t.tokens===!0?"tokens":"cost"},r=P(l,a),S=new Map;if(c!==void 0)for(const e of c){const s=S.get(e.sessionId);s===void 0?S.set(e.sessionId,[e]):s.push(e)}let v="";if(c!==void 0&&c.length>0)for(const e of c){v=e.currency;break}else if(r.length>0)for(const e of r){v=e.costCurrency;break}if(t.by==="filetype"&&t.bucket===void 0){const e=new Map,s=new Set;for(const u of r){const n=u.locByFiletype;if(n===void 0)continue;let g=!1;for(const d of Object.keys(n)){const h=n[d];if(h===void 0)continue;let w=e.get(d);w===void 0&&(w={added:0,removed:0,sessions:new Set},e.set(d,w)),w.added+=h.added,w.removed+=h.removed,(h.added>0||h.removed>0)&&(w.sessions.add(u.sessionId),g=!0)}g&&s.add(u.sessionId)}const o=[];for(const[u,n]of e){const g={label:u,costAmount:0,sessionCount:n.sessions.size,linesAdded:n.added,linesRemoved:n.removed};o.push({label:u,items:[g],sessionCount:n.sessions.size})}return{kind:"grouped",groups:o,currency:v,totalSessions:s.size}}const x=e=>t.by==="dir"?K(e.cwd,t.depth,Y(t)):t.by==="session"?e.sessionId.startsWith("hydra_session_")?e.sessionId.slice(14):e.sessionId:t.by==="model"?e.model===""?"<unknown>":e.model:t.by==="agent"?e.agentId===""?"<unknown>":e.agentId:"<all>",B=e=>{const s=new Date(e);if(Number.isNaN(s.getTime()))return"<invalid>";if(t.bucket==="hour"){const o=s.toLocaleDateString(void 0,{month:"2-digit",day:"2-digit"}),u=String(s.getHours()).padStart(2,"0");return`${o} ${u}:00`}if(t.bucket==="day")return s.toLocaleDateString(void 0,{year:"numeric",month:"2-digit",day:"2-digit"});if(t.bucket==="weekday")return s.toLocaleDateString(void 0,{weekday:"short"});if(t.bucket==="week"){const o=new Date(s),u=o.getDay(),n=o.getDate()-u+(u===0?-6:1);return o.setDate(n),o.toLocaleDateString(void 0,{year:"numeric",month:"2-digit",day:"2-digit"})}return t.bucket==="month"?s.toLocaleDateString(void 0,{year:"numeric",month:"2-digit"}):s.toLocaleDateString(void 0,{year:"numeric",month:"2-digit",day:"2-digit"})},E=new Map;if(t.bucket==="weekday"){const e=new Date(2024,0,1);for(let s=0;s<7;s++){const o=new Date(e);o.setDate(e.getDate()+s),E.set(o.toLocaleDateString(void 0,{weekday:"short"}),s)}}const R=(e,s)=>{if(t.bucket==="weekday"){const o=E.get(e.bucket)??7,u=E.get(s.bucket)??7;if(o!==u)return o-u}return e.bucket.localeCompare(s.bucket)};if(t.by===void 0&&t.bucket===void 0){let e=0,s=0,o=0,u=0,n=0,g=0;for(const h of r){e+=h.costAmount;const w=S.get(h.sessionId);if(w!==void 0)for(const i of w)s+=i.deltaCost,t.tokens===!0&&(i.inputTokens!==void 0&&(o+=i.inputTokens),i.outputTokens!==void 0&&(u+=i.outputTokens),i.cacheReadTokens!==void 0&&(n+=i.cacheReadTokens),i.cacheWriteTokens!==void 0&&(g+=i.cacheWriteTokens))}const d={label:"All sessions",costAmount:e,deltaCost:s,sessionCount:r.length};if(t.tokens===!0&&(d.inputTokens=o,d.outputTokens=u,d.cacheReadTokens=n,d.cacheWriteTokens=g),t.loc===!0){let h=0,w=0;for(const i of r){const b=i.locByFiletype;if(b!==void 0)for(const k of Object.keys(b)){const p=b[k];p!==void 0&&(h+=p.added,w+=p.removed)}}d.linesAdded=h,d.linesRemoved=w}return{kind:"total",row:d,currency:v}}if(t.by!==void 0&&t.bucket===void 0){const e=new Map;for(const o of r){const u=x(o);let n=e.get(u);if(n===void 0&&(n={label:u,rows:{label:u,costAmount:0,deltaCost:0,sessionCount:0}},e.set(u,n)),n.rows.costAmount+=o.costAmount,n.rows.sessionCount=(n.rows.sessionCount??0)+1,t.tokens===!0&&(n.rows.inputTokens===void 0&&(n.rows.inputTokens=0),n.rows.inputTokens+=o.contextTokens),t.loc===!0){const d=o.locByFiletype;if(d!==void 0){n.rows.linesAdded===void 0&&(n.rows.linesAdded=0),n.rows.linesRemoved===void 0&&(n.rows.linesRemoved=0);for(const h of Object.keys(d)){const w=d[h];w!==void 0&&(n.rows.linesAdded+=w.added,n.rows.linesRemoved+=w.removed)}}}const g=S.get(o.sessionId);if(g!==void 0)for(const d of g)n.rows.deltaCost+=d.deltaCost,t.tokens===!0&&(n.rows.inputTokens===void 0&&(n.rows.inputTokens=0),n.rows.outputTokens===void 0&&(n.rows.outputTokens=0),n.rows.cacheReadTokens===void 0&&(n.rows.cacheReadTokens=0),n.rows.cacheWriteTokens===void 0&&(n.rows.cacheWriteTokens=0),d.inputTokens!==void 0&&(n.rows.inputTokens+=d.inputTokens),d.outputTokens!==void 0&&(n.rows.outputTokens+=d.outputTokens),d.cacheReadTokens!==void 0&&(n.rows.cacheReadTokens+=d.cacheReadTokens),d.cacheWriteTokens!==void 0&&(n.rows.cacheWriteTokens+=d.cacheWriteTokens))}const s=[];for(const o of e.values())s.push({label:o.label,items:[o.rows],sessionCount:o.rows.sessionCount});return{kind:"grouped",groups:s,currency:v}}const F=new Map;for(const e of r)F.set(e.sessionId,e);const M=new Map;if(c!==void 0){for(const e of c){if(!F.has(e.sessionId))continue;const s=M.get(e.sessionId);s===void 0?M.set(e.sessionId,[e]):s.push(e)}for(const e of M.values())e.sort((s,o)=>s.ts.localeCompare(o.ts))}const j=(e,s)=>{e._sessions===void 0&&(e._sessions=new Set),e._sessions.add(s),e.sessionCount=e._sessions.size},G=e=>{delete e._sessions},W=(e,s,o)=>{const u=B(s.ts);let n=e.get(u);n===void 0&&(n={bucket:u,costAmount:0,deltaCost:0,sessionCount:0},e.set(u,n)),n.costAmount+=o,n.deltaCost+=o,j(n,s.sessionId),t.tokens===!0&&(n.inputTokens===void 0||(s.inputTokens??0)>n.inputTokens)&&(n.inputTokens=s.inputTokens??n.inputTokens??0)};if(t.loc===!0&&t.bucket!==void 0){const e=new Set;for(const i of r)e.add(i.sessionId);const s=new Map;for(const i of r)s.set(i.sessionId,i);const o=(i,b)=>{let k=i.get(b);return k===void 0&&(k={bucket:b,costAmount:0,deltaCost:0,sessionCount:0,linesAdded:0,linesRemoved:0},i.set(b,k)),(k.linesAdded??void 0)===void 0&&(k.linesAdded=0),(k.linesRemoved??void 0)===void 0&&(k.linesRemoved=0),k},u=(i,b)=>{i.linesAdded=(i.linesAdded??0)+b.linesAdded,i.linesRemoved=(i.linesRemoved??0)+b.linesRemoved,i._sessions===void 0&&(i._sessions=new Set),i._sessions.add(b.sessionId),i.sessionCount=i._sessions.size},n=i=>{delete i._sessions},g=T??[];if(t.by==="filetype"){const i=new Map,b=new Set;for(const p of g){if(!e.has(p.sessionId)||f!==void 0&&new Date(p.ts)<f)continue;let y=i.get(p.filetype);y===void 0&&(y={label:p.filetype,buckets:new Map,sessions:new Set},i.set(p.filetype,y));const C=o(y.buckets,B(p.ts));u(C,p),y.sessions.add(p.sessionId),b.add(p.sessionId)}const k=[];for(const p of i.values()){const y=Array.from(p.buckets.values());for(const C of y)n(C);y.sort(R),y.length>0&&k.push({label:p.label,items:y,sessionCount:p.sessions.size})}return{kind:"timeSeriesGrouped",groups:k,currency:v,totalSessions:b.size}}if(t.by!==void 0){const i=new Map;for(const k of g){if(!e.has(k.sessionId)||f!==void 0&&new Date(k.ts)<f)continue;const p=s.get(k.sessionId);if(p===void 0)continue;const y=x(p);let C=i.get(y);C===void 0&&(C={label:y,buckets:new Map,sessions:new Set},i.set(y,C));const N=o(C.buckets,B(k.ts));u(N,k),C.sessions.add(k.sessionId)}const b=[];for(const k of i.values()){const p=Array.from(k.buckets.values());for(const y of p)n(y);p.sort(R),p.length>0&&b.push({label:k.label,items:p,sessionCount:k.sessions.size})}return{kind:"timeSeriesGrouped",groups:b,currency:v}}const d=new Map,h=new Set;for(const i of g){if(!e.has(i.sessionId)||f!==void 0&&new Date(i.ts)<f)continue;const b=o(d,B(i.ts));u(b,i),h.add(i.sessionId)}const w=Array.from(d.values());for(const i of w)n(i);return w.sort(R),{kind:"timeSeries",timeSeries:w,currency:v,totalSessions:h.size}}if(t.by!==void 0&&t.bucket!==void 0){const e=new Map,s=u=>{const n=x(u);let g=e.get(n);return g===void 0&&(g={label:n,buckets:new Map,sessions:new Set},e.set(n,g)),g};for(const u of r){const n=M.get(u.sessionId);if(n===void 0||n.length===0)continue;const g=s(u),d=g.buckets;g.sessions.add(u.sessionId);const h=n[0];if(h===void 0)continue;let w=h.cumulativeCost;for(let i=1;i<n.length;i++){const b=n[i];if(b===void 0)continue;const k=Math.max(0,b.cumulativeCost-w);w=b.cumulativeCost,!(f!==void 0&&new Date(b.ts)<f)&&W(d,b,k)}}const o=[];for(const u of e.values()){const n=Array.from(u.buckets.values());for(const g of n)G(g);n.sort(R),n.length>0&&o.push({label:u.label,items:n,sessionCount:u.sessions.size})}return{kind:"timeSeriesGrouped",groups:o,currency:v}}const O=new Map,z=new Set;for(const e of r){const s=M.get(e.sessionId);if(s===void 0||s.length===0)continue;const o=s[0];if(o===void 0)continue;let u=o.cumulativeCost,n=!1;for(let g=1;g<s.length;g++){const d=s[g];if(d===void 0)continue;const h=Math.max(0,d.cumulativeCost-u);u=d.cumulativeCost,!(f!==void 0&&new Date(d.ts)<f)&&(W(O,d,h),n=!0)}n&&z.add(e.sessionId)}const _=Array.from(O.values());for(const e of _)G(e);return _.sort(R),{kind:"timeSeries",timeSeries:_,currency:v,totalSessions:z.size}}export{X as aggregate,P as applyFilters,V as parseSince};
1
+ import{relative as H,resolve as R}from"node:path";import{homedir as L}from"node:os";import{realpathSync as $}from"node:fs";function V(w){const c=w.match(/^(\d+)\s*([dhmwy])$/i);if(c!==null){const y=c[1],a=c[2];if(y===void 0||a===void 0)throw new Error(`invalid since spec: ${w}`);const h=parseInt(y,10),k=a.toLowerCase(),r=new Date;return k==="d"?r.setDate(r.getDate()-h):k==="h"?r.setHours(r.getHours()-h):k==="w"?r.setDate(r.getDate()-h*7):k==="m"?r.setMonth(r.getMonth()-h):k==="y"&&r.setFullYear(r.getFullYear()-h),r}const t=new Date(w);if(Number.isNaN(t.getTime()))throw new Error(`invalid since spec: ${w}`);return t}function q(w){const c=w.locByFiletype;if(c===void 0)return 0;let t=0;for(const y of Object.keys(c)){const a=c[y];a!==void 0&&(t+=a.added-a.removed)}return t}const I=new Map;function D(w){if(I.has(w))return I.get(w);try{const c=$(w);return I.set(w,c),c}catch{return I.set(w,w),w}}function P(w,c){let t=w;if(c.since!==void 0){const h=c.since.getTime(),k=[];for(const r of t)new Date(r.updatedAt).getTime()<h||k.push(r);t=k}if(c.dir!==void 0){const h=D(R(c.dir)),k=[];for(const r of t)r.cwd===void 0||r.cwd===""||(r.cwd.startsWith(h+"/")||r.cwd===h)&&k.push(r);t=k}if(c.host!==void 0&&c.host!=="all"){const h=c.host,k=[];for(const r of t)h==="local"?(!r.importedFromMachine||r.upstreamSessionId)&&k.push(r):r.importedFromMachine===h&&!r.upstreamSessionId&&k.push(r);t=k}if(c.interactive!==void 0){const h=c.interactive,k=[];for(const r of t)r.interactive===h&&k.push(r);t=k}const y=c.min??0,a=[];for(const h of t){let k;c.minMetric==="tokens"?k=h.contextTokens:c.minMetric==="loc"?k=q(h):k=h.costAmount,k>y&&a.push(h)}return a}function Y(w){return w.dir!==void 0?R(w.dir):R(L())}function K(w,c,t){if(w===void 0||w==="")return"<unknown>";const y=D(R(w));if(c===void 0){const S=D(R(L()));return y===S?"~":y.startsWith(S+"/")?"~/"+y.slice(S.length+1):y}const a=D(R(t));let h=H(a,y);if(h===""||h===".")return"<root>";if(h.startsWith(".."))return"<unknown>";const k=h.split("/");if(k.length===0)return"<root>";const r=Math.min(c,k.length),C=k.slice(0,r);return C.length===0?"<root>":C.join("/")}function X(w,c,t={},y){let a=t.since;if(a===void 0&&t.bucket!==void 0){const e=new Date;t.bucket==="hour"?(e.setHours(e.getHours()-24),a=e):t.bucket==="day"||t.bucket==="weekday"?(e.setDate(e.getDate()-30),a=e):t.bucket==="week"?(e.setMonth(e.getMonth()-6),a=e):t.bucket==="month"&&(e.setFullYear(e.getFullYear()-2),a=e)}if(!(a!==void 0||t.bucket!==void 0||t.tokens===!0||t.loc===!0||t.by!==void 0||t.dir!==void 0||t.interactive!==void 0)){let e=0;for(const i of w)e+=i.costAmount;let s="";for(const i of w){s=i.costCurrency;break}return{kind:"total",row:{label:"All sessions",costAmount:e,sessionCount:w.length},currency:s}}const k={since:a,dir:t.dir,interactive:t.interactive,min:t.min,minMetric:t.loc===!0?"loc":t.tokens===!0?"tokens":"cost"},r=P(w,k),C=new Map;if(c!==void 0)for(const e of c){const s=C.get(e.sessionId);s===void 0?C.set(e.sessionId,[e]):s.push(e)}let S="";if(c!==void 0&&c.length>0)for(const e of c){S=e.currency;break}else if(r.length>0)for(const e of r){S=e.costCurrency;break}if(t.by==="filetype"&&t.bucket===void 0){const e=new Map,s=new Set;for(const u of r){const n=u.locByFiletype;if(n===void 0)continue;let l=!1;for(const m of Object.keys(n)){const f=n[m];if(f===void 0)continue;let b=e.get(m);b===void 0&&(b={added:0,removed:0,sessions:new Set},e.set(m,b)),b.added+=f.added,b.removed+=f.removed,(f.added>0||f.removed>0)&&(b.sessions.add(u.sessionId),l=!0)}l&&s.add(u.sessionId)}const i=[];for(const[u,n]of e){const l={label:u,costAmount:0,sessionCount:n.sessions.size,linesAdded:n.added,linesRemoved:n.removed};i.push({label:u,items:[l],sessionCount:n.sessions.size})}return{kind:"grouped",groups:i,currency:S,totalSessions:s.size}}const E=e=>t.by==="dir"?K(e.cwd,t.depth,Y(t)):t.by==="session"?e.sessionId.startsWith("hydra_session_")?e.sessionId.slice(14):e.sessionId:t.by==="model"?e.model===""?"<unknown>":e.model:t.by==="agent"?e.agentId===""?"<unknown>":e.agentId:"<all>",B=e=>{const s=new Date(e);if(Number.isNaN(s.getTime()))return"<invalid>";if(t.bucket==="hour"){const i=s.toLocaleDateString(void 0,{month:"2-digit",day:"2-digit"}),u=String(s.getHours()).padStart(2,"0");return`${i} ${u}:00`}if(t.bucket==="day")return s.toLocaleDateString(void 0,{year:"numeric",month:"2-digit",day:"2-digit"});if(t.bucket==="weekday")return s.toLocaleDateString(void 0,{weekday:"short"});if(t.bucket==="week"){const i=new Date(s),u=i.getDay(),n=i.getDate()-u+(u===0?-6:1);return i.setDate(n),i.toLocaleDateString(void 0,{year:"numeric",month:"2-digit",day:"2-digit"})}return t.bucket==="month"?s.toLocaleDateString(void 0,{year:"numeric",month:"2-digit"}):s.toLocaleDateString(void 0,{year:"numeric",month:"2-digit",day:"2-digit"})},x=new Map;if(t.bucket==="weekday"){const e=new Date(2024,0,1);for(let s=0;s<7;s++){const i=new Date(e);i.setDate(e.getDate()+s),x.set(i.toLocaleDateString(void 0,{weekday:"short"}),s)}}const M=(e,s)=>{if(t.bucket==="weekday"){const i=x.get(e.bucket)??7,u=x.get(s.bucket)??7;if(i!==u)return i-u}return e.bucket.localeCompare(s.bucket)},F=new Map;for(const e of r)F.set(e.sessionId,e);const A=new Map;if(c!==void 0){for(const e of c){if(!F.has(e.sessionId))continue;const s=A.get(e.sessionId);s===void 0?A.set(e.sessionId,[e]):s.push(e)}for(const e of A.values())e.sort((s,i)=>s.ts.localeCompare(i.ts))}if(t.by===void 0&&t.bucket===void 0){let e=0,s=0,i=0,u=0,n=0,l=0;for(const f of r){const b=A.get(f.sessionId);if(b===void 0||b.length<2)e+=f.costAmount,l++;else{let d=b[0].cumulativeCost,g=!1;for(let p=1;p<b.length;p++){const v=b[p];if(v===void 0)continue;const T=Math.max(0,v.cumulativeCost-d);d=v.cumulativeCost,!(a!==void 0&&new Date(v.ts)<a)&&(e+=T,g=!0)}g&&l++}if(t.tokens===!0){const o=C.get(f.sessionId);if(o!==void 0)for(const d of o)d.inputTokens!==void 0&&(s+=d.inputTokens),d.outputTokens!==void 0&&(i+=d.outputTokens),d.cacheReadTokens!==void 0&&(u+=d.cacheReadTokens),d.cacheWriteTokens!==void 0&&(n+=d.cacheWriteTokens)}}const m={label:"All sessions",costAmount:e,deltaCost:e,sessionCount:l};if(t.tokens===!0&&(m.inputTokens=s,m.outputTokens=i,m.cacheReadTokens=u,m.cacheWriteTokens=n),t.loc===!0){let f=0,b=0;for(const o of r){const d=o.locByFiletype;if(d!==void 0)for(const g of Object.keys(d)){const p=d[g];p!==void 0&&(f+=p.added,b+=p.removed)}}m.linesAdded=f,m.linesRemoved=b}return{kind:"total",row:m,currency:S}}if(t.by!==void 0&&t.bucket===void 0){const e=new Map;for(const i of r){const u=E(i);let n=e.get(u);if(n===void 0&&(n={label:u,rows:{label:u,costAmount:0,deltaCost:0,sessionCount:0},sessions:new Set},e.set(u,n)),t.tokens===!0&&(n.rows.inputTokens===void 0&&(n.rows.inputTokens=0),n.rows.inputTokens+=i.contextTokens),t.loc===!0){const m=i.locByFiletype;if(m!==void 0){n.rows.linesAdded===void 0&&(n.rows.linesAdded=0),n.rows.linesRemoved===void 0&&(n.rows.linesRemoved=0);for(const f of Object.keys(m)){const b=m[f];b!==void 0&&(n.rows.linesAdded+=b.added,n.rows.linesRemoved+=b.removed)}}}const l=A.get(i.sessionId);if(l===void 0||l.length<2)n.rows.costAmount+=i.costAmount,n.rows.deltaCost+=i.costAmount,n.sessions.add(i.sessionId);else{let f=l[0].cumulativeCost,b=!1;for(let o=1;o<l.length;o++){const d=l[o];if(d===void 0)continue;const g=Math.max(0,d.cumulativeCost-f);f=d.cumulativeCost,!(a!==void 0&&new Date(d.ts)<a)&&(n.rows.costAmount+=g,n.rows.deltaCost+=g,b=!0)}b&&n.sessions.add(i.sessionId)}if(t.tokens===!0){const m=C.get(i.sessionId);if(m!==void 0)for(const f of m)n.rows.inputTokens===void 0&&(n.rows.inputTokens=0),n.rows.outputTokens===void 0&&(n.rows.outputTokens=0),n.rows.cacheReadTokens===void 0&&(n.rows.cacheReadTokens=0),n.rows.cacheWriteTokens===void 0&&(n.rows.cacheWriteTokens=0),f.inputTokens!==void 0&&(n.rows.inputTokens+=f.inputTokens),f.outputTokens!==void 0&&(n.rows.outputTokens+=f.outputTokens),f.cacheReadTokens!==void 0&&(n.rows.cacheReadTokens+=f.cacheReadTokens),f.cacheWriteTokens!==void 0&&(n.rows.cacheWriteTokens+=f.cacheWriteTokens)}}const s=[];for(const i of e.values())i.rows.sessionCount=i.sessions.size,s.push({label:i.label,items:[i.rows],sessionCount:i.sessions.size});return{kind:"grouped",groups:s,currency:S}}const j=(e,s)=>{e._sessions===void 0&&(e._sessions=new Set),e._sessions.add(s),e.sessionCount=e._sessions.size},G=e=>{delete e._sessions},W=(e,s,i)=>{const u=B(s.ts);let n=e.get(u);n===void 0&&(n={bucket:u,costAmount:0,deltaCost:0,sessionCount:0},e.set(u,n)),n.costAmount+=i,n.deltaCost+=i,j(n,s.sessionId),t.tokens===!0&&(n.inputTokens===void 0||(s.inputTokens??0)>n.inputTokens)&&(n.inputTokens=s.inputTokens??n.inputTokens??0)};if(t.loc===!0&&t.bucket!==void 0){const e=new Set;for(const o of r)e.add(o.sessionId);const s=new Map;for(const o of r)s.set(o.sessionId,o);const i=(o,d)=>{let g=o.get(d);return g===void 0&&(g={bucket:d,costAmount:0,deltaCost:0,sessionCount:0,linesAdded:0,linesRemoved:0},o.set(d,g)),(g.linesAdded??void 0)===void 0&&(g.linesAdded=0),(g.linesRemoved??void 0)===void 0&&(g.linesRemoved=0),g},u=(o,d)=>{o.linesAdded=(o.linesAdded??0)+d.linesAdded,o.linesRemoved=(o.linesRemoved??0)+d.linesRemoved,o._sessions===void 0&&(o._sessions=new Set),o._sessions.add(d.sessionId),o.sessionCount=o._sessions.size},n=o=>{delete o._sessions},l=y??[];if(t.by==="filetype"){const o=new Map,d=new Set;for(const p of l){if(!e.has(p.sessionId)||a!==void 0&&new Date(p.ts)<a)continue;let v=o.get(p.filetype);v===void 0&&(v={label:p.filetype,buckets:new Map,sessions:new Set},o.set(p.filetype,v));const T=i(v.buckets,B(p.ts));u(T,p),v.sessions.add(p.sessionId),d.add(p.sessionId)}const g=[];for(const p of o.values()){const v=Array.from(p.buckets.values());for(const T of v)n(T);v.sort(M),v.length>0&&g.push({label:p.label,items:v,sessionCount:p.sessions.size})}return{kind:"timeSeriesGrouped",groups:g,currency:S,totalSessions:d.size}}if(t.by!==void 0){const o=new Map;for(const g of l){if(!e.has(g.sessionId)||a!==void 0&&new Date(g.ts)<a)continue;const p=s.get(g.sessionId);if(p===void 0)continue;const v=E(p);let T=o.get(v);T===void 0&&(T={label:v,buckets:new Map,sessions:new Set},o.set(v,T));const N=i(T.buckets,B(g.ts));u(N,g),T.sessions.add(g.sessionId)}const d=[];for(const g of o.values()){const p=Array.from(g.buckets.values());for(const v of p)n(v);p.sort(M),p.length>0&&d.push({label:g.label,items:p,sessionCount:g.sessions.size})}return{kind:"timeSeriesGrouped",groups:d,currency:S}}const m=new Map,f=new Set;for(const o of l){if(!e.has(o.sessionId)||a!==void 0&&new Date(o.ts)<a)continue;const d=i(m,B(o.ts));u(d,o),f.add(o.sessionId)}const b=Array.from(m.values());for(const o of b)n(o);return b.sort(M),{kind:"timeSeries",timeSeries:b,currency:S,totalSessions:f.size}}if(t.by!==void 0&&t.bucket!==void 0){const e=new Map,s=u=>{const n=E(u);let l=e.get(n);return l===void 0&&(l={label:n,buckets:new Map,sessions:new Set},e.set(n,l)),l};for(const u of r){const n=A.get(u.sessionId);if(n===void 0||n.length===0)continue;const l=s(u),m=l.buckets;l.sessions.add(u.sessionId);const f=n[0];if(f===void 0)continue;let b=f.cumulativeCost;for(let o=1;o<n.length;o++){const d=n[o];if(d===void 0)continue;const g=Math.max(0,d.cumulativeCost-b);b=d.cumulativeCost,!(a!==void 0&&new Date(d.ts)<a)&&W(m,d,g)}}const i=[];for(const u of e.values()){const n=Array.from(u.buckets.values());for(const l of n)G(l);n.sort(M),n.length>0&&i.push({label:u.label,items:n,sessionCount:u.sessions.size})}return{kind:"timeSeriesGrouped",groups:i,currency:S}}const z=new Map,O=new Set;for(const e of r){const s=A.get(e.sessionId);if(s===void 0||s.length===0)continue;const i=s[0];if(i===void 0)continue;let u=i.cumulativeCost,n=!1;for(let l=1;l<s.length;l++){const m=s[l];if(m===void 0)continue;const f=Math.max(0,m.cumulativeCost-u);u=m.cumulativeCost,!(a!==void 0&&new Date(m.ts)<a)&&(W(z,m,f),n=!0)}n&&O.add(e.sessionId)}const _=Array.from(z.values());for(const e of _)G(e);return _.sort(M),{kind:"timeSeries",timeSeries:_,currency:S,totalSessions:O.size}}export{X as aggregate,P as applyFilters,V as parseSince};
@@ -1,17 +1,17 @@
1
- function k(e){return"label"in e?e.label:e.bucket}function w(e){let n=0,s=!1;for(const o of e){if(o===void 0)continue;const f=String(o);f.length>n&&(n=f.length),o!==1&&(s=!0)}const l=s?8:7,r=e.map(o=>{if(o===void 0)return"";const f=String(o).padStart(n),d=(o===1?"session":"sessions").padEnd(l);return`${f} ${d}`}),u=n+1+l;return{strs:r,width:u}}function b(e){if(e<1e3)return String(e);let n,s;return e<1e6?(n=e/1e3,s="k"):e<1e9?(n=e/1e6,s="M"):(n=e/1e9,s="B"),(n%1===0?n.toFixed(0):n.toFixed(2)).replace(/\.?0+$/,"")+s}function j(e,n){let s=0;for(const u of e)u>s&&(s=u);if(s<=0||n<=0)return e.map(()=>"");const l=Math.max(1,n),r=[];for(const u of e){const o=u/s,f=Math.round(o*l),d=l-f;r.push("\u2588".repeat(f)+"\u2591".repeat(d))}return r}function E(e,n={}){const s=process.stdout.columns??80,l=n.histogram===!0,r=n.tokens===!0,u=n.loc===!0;let o="";const f=t=>`${t} session${t===1?"":"s"}`,d=(t,i)=>{if(u){let a=0;for(const c of i)a+=R(c);return`${A(a)} lines`}return S(t)};if(e.kind==="total"){const t=e.row.sessionCount,i=t!==void 0?f(t):e.row.label??"all sessions";o+=`Total: ${d(e.row.costAmount,[e.row])} across ${i}
2
- `}else if(e.kind==="grouped"){const t=[];let i=0;for(const c of e.groups)for(const p of c.items)t.push(p),i+=p.sessionCount??0;e.totalSessions!==void 0&&(i=e.totalSessions);const a=_(e.groups);o+=`Total: ${d(a,t)} across ${f(i)}
3
- `}else if(e.kind==="timeSeries"){const t=e.timeSeries.reduce((a,c)=>a+c.costAmount,0),i=e.totalSessions??e.timeSeries.reduce((a,c)=>a+(c.sessionCount??0),0);o+=`Total: ${d(t,e.timeSeries)} across ${f(i)}
4
- `}else if(e.kind==="timeSeriesGrouped"){const t=[];let i=0;for(const c of e.groups)for(const p of c.items)t.push(p),i+=p.sessionCount??0;e.totalSessions!==void 0&&(i=e.totalSessions);const a=_(e.groups);o+=`Total: ${d(a,t)} across ${f(i)}
1
+ function b(e){return"label"in e?e.label:e.bucket}function w(e){let n=0,i=!1;for(const o of e){if(o===void 0)continue;const f=String(o);f.length>n&&(n=f.length),o!==1&&(i=!0)}const l=i?8:7,r=e.map(o=>{if(o===void 0)return"";const f=String(o).padStart(n),d=(o===1?"session":"sessions").padEnd(l);return`${f} ${d}`}),u=n+1+l;return{strs:r,width:u}}function h(e){if(e<1e3)return String(e);let n,i;return e<1e6?(n=e/1e3,i="k"):e<1e9?(n=e/1e6,i="M"):(n=e/1e9,i="B"),(n%1===0?n.toFixed(0):n.toFixed(2)).replace(/\.?0+$/,"")+i}function j(e,n){let i=0;for(const u of e)u>i&&(i=u);if(i<=0||n<=0)return e.map(()=>"");const l=Math.max(1,n),r=[];for(const u of e){const o=u/i,f=Math.round(o*l),d=l-f;r.push("\u2588".repeat(f)+"\u2591".repeat(d))}return r}function E(e,n={}){const i=process.stdout.columns??80,l=n.histogram===!0,r=n.tokens===!0,u=n.loc===!0;let o="";const f=t=>`${t} session${t===1?"":"s"}`,d=(t,s)=>{if(u){let a=0;for(const c of s)a+=R(c);return`${A(a)} lines`}if(r){let a=0;for(const c of s)a+=k(c);return`${h(a)} tokens`}return S(t)};if(e.kind==="total"){const t=e.row.sessionCount,s=t!==void 0?f(t):e.row.label??"all sessions";o+=`Total: ${d(e.row.costAmount,[e.row])} across ${s}
2
+ `}else if(e.kind==="grouped"){const t=[];let s=0;for(const c of e.groups)for(const p of c.items)t.push(p),s+=p.sessionCount??0;e.totalSessions!==void 0&&(s=e.totalSessions);const a=_(e.groups);o+=`Total: ${d(a,t)} across ${f(s)}
3
+ `}else if(e.kind==="timeSeries"){const t=e.timeSeries.reduce((a,c)=>a+c.costAmount,0),s=e.totalSessions??e.timeSeries.reduce((a,c)=>a+(c.sessionCount??0),0);o+=`Total: ${d(t,e.timeSeries)} across ${f(s)}
4
+ `}else if(e.kind==="timeSeriesGrouped"){const t=[];let s=0;for(const c of e.groups)for(const p of c.items)t.push(p),s+=p.sessionCount??0;e.totalSessions!==void 0&&(s=e.totalSessions);const a=_(e.groups);o+=`Total: ${d(a,t)} across ${f(s)}
5
5
  `}if(n.windowLabel!==void 0&&n.windowLabel.length>0&&(o+=`Window: ${n.windowLabel}
6
- `),e.kind==="grouped")if(e.groups.every(i=>i.items.length===1)){const i=[];for(const a of e.groups){const c=a.items[0];c!==void 0&&i.push({...c,label:a.label})}i.sort((a,c)=>{const p=$(a,r,u);return $(c,r,u)-p}),l?o+=T(i,s,r,u):o+=N(i,r,u)}else for(const i of e.groups){o+=`
7
- ${i.label}: ${x(i.items,i.sessionCount,r,u)}
8
- `;const a=i.items,c=a.some(p=>p.inputTokens!==void 0||p.outputTokens!==void 0);l?o+=T(a,s,r,u):o+=v(a,c,r,u)}else if(e.kind==="timeSeries"){const t=e.timeSeries.some(i=>i.inputTokens!==void 0||i.outputTokens!==void 0);l?o+=T(e.timeSeries,s,r,u):o+=v(e.timeSeries,t,r,u)}else if(e.kind==="timeSeriesGrouped")for(const t of e.groups){o+=`
6
+ `),e.kind==="grouped")if(e.groups.every(s=>s.items.length===1)){const s=[];for(const a of e.groups){const c=a.items[0];c!==void 0&&s.push({...c,label:a.label})}s.sort((a,c)=>{const p=$(a,r,u);return $(c,r,u)-p}),l?o+=T(s,i,r,u):o+=N(s,r,u)}else for(const s of e.groups){o+=`
7
+ ${s.label}: ${x(s.items,s.sessionCount,r,u)}
8
+ `;const a=s.items,c=a.some(p=>p.inputTokens!==void 0||p.outputTokens!==void 0);l?o+=T(a,i,r,u):o+=v(a,c,r,u)}else if(e.kind==="timeSeries"){const t=e.timeSeries.some(s=>s.inputTokens!==void 0||s.outputTokens!==void 0);l?o+=T(e.timeSeries,i,r,u):o+=v(e.timeSeries,t,r,u)}else if(e.kind==="timeSeriesGrouped")for(const t of e.groups){o+=`
9
9
  ${t.label}: ${x(t.items,t.sessionCount,r,u)}
10
- `;const i=t.items.some(a=>a.inputTokens!==void 0||a.outputTokens!==void 0);l?o+=T(t.items,s,r,u):o+=v(t.items,i,r,u)}return o}function $(e,n,s){return s?R(e):n?h(e):e.costAmount}function L(e,n,s){return s?A(R(e)):n?b(h(e)):S(e.costAmount)}function x(e,n,s,l){let r=0;for(const d of e)r+=l?R(d):s?h(d):d.costAmount;const u=l?`${A(r)} lines`:s?`${b(r)} tokens`:S(r),o=n??e.reduce((d,t)=>d+(t.sessionCount??0),0);return o===0?u:`${u} across ${o} ${o===1?"session":"sessions"}`}function N(e,n,s){if(e.length===0)return` (no data)
11
- `;let l=0;for(const t of e)t.label.length>l&&(l=t.label.length);const r=e.map(t=>L(t,n,s));let u=0;for(const t of r)t.length>u&&(u=t.length);const{strs:o,width:f}=w(e.map(t=>t.sessionCount)),d=[];for(let t=0;t<e.length;t++){const i=e[t];if(i===void 0)continue;const a=r[t]??"";let c=` ${i.label.padEnd(l)} ${a.padStart(u)}`;f>0&&(c+=` ${o[t]}`),d.push(c+`
12
- `)}return d.join("")}function T(e,n,s,l){if(e.length===0)return` (no data)
13
- `;let r=0;for(const m of e){const g=k(m);g.length>r&&(r=g.length)}const u=e.map(m=>{const g=$(m,s,l);return l?Math.abs(g):g}),o=e.map(m=>L(m,s,l));let f=0;for(const m of o)m.length>f&&(f=m.length);const{strs:d,width:t}=w(e.map(m=>m.sessionCount)),i=t>0?t+2:0;let a=n-r-f-i-8;a<1&&(a=e.length>1?1:0);const c=j(u,a),p=[];for(let m=0;m<e.length;m++){const g=e[m];if(g===void 0)continue;const G=k(g).padEnd(r),y=(o[m]??"").padStart(f);let C=` ${G} ${y} ${c[m]??""}`;t>0&&(C+=` ${d[m]}`),p.push(C+`
14
- `)}return p.join("")}function v(e,n,s,l){if(e.length===0)return` (no data)
15
- `;let r=28;for(const o of e){const f=k(o);f.length>r&&(r=f.length)}let u=" ";u+=z("",r),u+=" ",l?u+=" +Added -Removed Net":s?u+=" Tokens":(u+=" Cost",n&&(u+=" Tokens")),u+=`
16
- `;for(const o of e){const f=k(o).padEnd(r);let d=" ";if(d+=f,d+=" ",l){const t=o.linesAdded??0,i=o.linesRemoved??0;d+=t.toLocaleString().padStart(10),d+=" ",d+=i.toLocaleString().padStart(8),d+=" ",d+=A(t-i).padStart(8)}else if(s){const t=h(o);d+=b(t).padStart(12)}else{const t=o.costAmount??0;if(d+=S(t).padStart(10),n){const i=h(o);d+=" ".padStart(2),d+=b(i).padStart(12)}}u+=d+`
17
- `}return u}function F(e){const n={kind:e.kind,currency:e.currency};if(e.kind==="total")n.row=B(e.row);else if(e.kind==="grouped"){const s=e.groups.slice().sort((l,r)=>l.label.localeCompare(r.label));n.groups=s.map(l=>({label:l.label,items:l.items.map(B)}))}else if(e.kind==="timeSeries")n.timeSeries=e.timeSeries.map(W);else if(e.kind==="timeSeriesGrouped"){const s=e.groups.slice().sort((l,r)=>l.label.localeCompare(r.label));n.groups=s.map(l=>({label:l.label,items:l.items.map(W)}))}return JSON.stringify(n,null,2)}function B(e){const n={label:e.label,costAmount:e.costAmount};return e.deltaCost!==void 0&&(n.deltaCost=e.deltaCost),e.inputTokens!==void 0&&(n.inputTokens=e.inputTokens),e.outputTokens!==void 0&&(n.outputTokens=e.outputTokens),e.cacheReadTokens!==void 0&&(n.cacheReadTokens=e.cacheReadTokens),e.cacheWriteTokens!==void 0&&(n.cacheWriteTokens=e.cacheWriteTokens),e.linesAdded!==void 0&&(n.linesAdded=e.linesAdded),e.linesRemoved!==void 0&&(n.linesRemoved=e.linesRemoved),(e.linesAdded!==void 0||e.linesRemoved!==void 0)&&(n.linesNet=(e.linesAdded??0)-(e.linesRemoved??0)),n}function W(e){const n={bucket:e.bucket,costAmount:e.costAmount,deltaCost:e.deltaCost};return e.inputTokens!==void 0&&(n.inputTokens=e.inputTokens),e.outputTokens!==void 0&&(n.outputTokens=e.outputTokens),e.cacheReadTokens!==void 0&&(n.cacheReadTokens=e.cacheReadTokens),e.cacheWriteTokens!==void 0&&(n.cacheWriteTokens=e.cacheWriteTokens),e.linesAdded!==void 0&&(n.linesAdded=e.linesAdded),e.linesRemoved!==void 0&&(n.linesRemoved=e.linesRemoved),(e.linesAdded!==void 0||e.linesRemoved!==void 0)&&(n.linesNet=(e.linesAdded??0)-(e.linesRemoved??0)),n}function S(e){return`$${e.toFixed(2)}`}function R(e){const n=e.linesAdded??0,s=e.linesRemoved??0;return n-s}function A(e){return`${e>0?"+":""}${e.toLocaleString()}`}function h(e){let n=0;return e.inputTokens!==void 0&&(n+=e.inputTokens),e.outputTokens!==void 0&&(n+=e.outputTokens),n}function z(e,n){return e.length>=n?e:e+" ".repeat(n-e.length)}function _(e){let n=0;for(const s of e)for(const l of s.items)n+=l.costAmount;return n}export{F as renderJson,E as renderText};
10
+ `;const s=t.items.some(a=>a.inputTokens!==void 0||a.outputTokens!==void 0);l?o+=T(t.items,i,r,u):o+=v(t.items,s,r,u)}return o}function $(e,n,i){return i?R(e):n?k(e):e.costAmount}function L(e,n,i){return i?A(R(e)):n?h(k(e)):S(e.costAmount)}function x(e,n,i,l){let r=0;for(const d of e)r+=l?R(d):i?k(d):d.costAmount;const u=l?`${A(r)} lines`:i?`${h(r)} tokens`:S(r),o=n??e.reduce((d,t)=>d+(t.sessionCount??0),0);return o===0?u:`${u} across ${o} ${o===1?"session":"sessions"}`}function N(e,n,i){if(e.length===0)return` (no data)
11
+ `;let l=0;for(const t of e)t.label.length>l&&(l=t.label.length);const r=e.map(t=>L(t,n,i));let u=0;for(const t of r)t.length>u&&(u=t.length);const{strs:o,width:f}=w(e.map(t=>t.sessionCount)),d=[];for(let t=0;t<e.length;t++){const s=e[t];if(s===void 0)continue;const a=r[t]??"";let c=` ${s.label.padEnd(l)} ${a.padStart(u)}`;f>0&&(c+=` ${o[t]}`),d.push(c+`
12
+ `)}return d.join("")}function T(e,n,i,l){if(e.length===0)return` (no data)
13
+ `;let r=0;for(const m of e){const g=b(m);g.length>r&&(r=g.length)}const u=e.map(m=>{const g=$(m,i,l);return l?Math.abs(g):g}),o=e.map(m=>L(m,i,l));let f=0;for(const m of o)m.length>f&&(f=m.length);const{strs:d,width:t}=w(e.map(m=>m.sessionCount)),s=t>0?t+2:0;let a=n-r-f-s-8;a<1&&(a=e.length>1?1:0);const c=j(u,a),p=[];for(let m=0;m<e.length;m++){const g=e[m];if(g===void 0)continue;const G=b(g).padEnd(r),y=(o[m]??"").padStart(f);let C=` ${G} ${y} ${c[m]??""}`;t>0&&(C+=` ${d[m]}`),p.push(C+`
14
+ `)}return p.join("")}function v(e,n,i,l){if(e.length===0)return` (no data)
15
+ `;let r=28;for(const o of e){const f=b(o);f.length>r&&(r=f.length)}let u=" ";u+=z("",r),u+=" ",l?u+=" +Added -Removed Net":i?u+=" Tokens":(u+=" Cost",n&&(u+=" Tokens")),u+=`
16
+ `;for(const o of e){const f=b(o).padEnd(r);let d=" ";if(d+=f,d+=" ",l){const t=o.linesAdded??0,s=o.linesRemoved??0;d+=t.toLocaleString().padStart(10),d+=" ",d+=s.toLocaleString().padStart(8),d+=" ",d+=A(t-s).padStart(8)}else if(i){const t=k(o);d+=h(t).padStart(12)}else{const t=o.costAmount??0;if(d+=S(t).padStart(10),n){const s=k(o);d+=" ".padStart(2),d+=h(s).padStart(12)}}u+=d+`
17
+ `}return u}function M(e){const n={kind:e.kind,currency:e.currency};if(e.kind==="total")n.row=B(e.row);else if(e.kind==="grouped"){const i=e.groups.slice().sort((l,r)=>l.label.localeCompare(r.label));n.groups=i.map(l=>({label:l.label,items:l.items.map(B)}))}else if(e.kind==="timeSeries")n.timeSeries=e.timeSeries.map(W);else if(e.kind==="timeSeriesGrouped"){const i=e.groups.slice().sort((l,r)=>l.label.localeCompare(r.label));n.groups=i.map(l=>({label:l.label,items:l.items.map(W)}))}return JSON.stringify(n,null,2)}function B(e){const n={label:e.label,costAmount:e.costAmount};return e.deltaCost!==void 0&&(n.deltaCost=e.deltaCost),e.inputTokens!==void 0&&(n.inputTokens=e.inputTokens),e.outputTokens!==void 0&&(n.outputTokens=e.outputTokens),e.cacheReadTokens!==void 0&&(n.cacheReadTokens=e.cacheReadTokens),e.cacheWriteTokens!==void 0&&(n.cacheWriteTokens=e.cacheWriteTokens),e.linesAdded!==void 0&&(n.linesAdded=e.linesAdded),e.linesRemoved!==void 0&&(n.linesRemoved=e.linesRemoved),(e.linesAdded!==void 0||e.linesRemoved!==void 0)&&(n.linesNet=(e.linesAdded??0)-(e.linesRemoved??0)),n}function W(e){const n={bucket:e.bucket,costAmount:e.costAmount,deltaCost:e.deltaCost};return e.inputTokens!==void 0&&(n.inputTokens=e.inputTokens),e.outputTokens!==void 0&&(n.outputTokens=e.outputTokens),e.cacheReadTokens!==void 0&&(n.cacheReadTokens=e.cacheReadTokens),e.cacheWriteTokens!==void 0&&(n.cacheWriteTokens=e.cacheWriteTokens),e.linesAdded!==void 0&&(n.linesAdded=e.linesAdded),e.linesRemoved!==void 0&&(n.linesRemoved=e.linesRemoved),(e.linesAdded!==void 0||e.linesRemoved!==void 0)&&(n.linesNet=(e.linesAdded??0)-(e.linesRemoved??0)),n}function S(e){return`$${e.toFixed(2)}`}function R(e){const n=e.linesAdded??0,i=e.linesRemoved??0;return n-i}function A(e){return`${e>0?"+":""}${e.toLocaleString()}`}function k(e){let n=0;return e.inputTokens!==void 0&&(n+=e.inputTokens),e.outputTokens!==void 0&&(n+=e.outputTokens),n}function z(e,n){return e.length>=n?e:e+" ".repeat(n-e.length)}function _(e){let n=0;for(const i of e)for(const l of i.items)n+=l.costAmount;return n}export{M as renderJson,E as renderText};
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import{readFileSync as U}from"node:fs";import{dirname as F,resolve as N}from"node:path";import{fileURLToPath as M}from"node:url";import{loadConfig as P}from"./config.js";import{BudgeterBridge as _}from"./bridge.js";import{DEFAULT_RULE as H}from"./rule.js";import{logger as W,setDebug as j}from"./util/log.js";import{scanSessions as B,enrichSessionsWithLoc as G}from"./cost/session-store.js";import{listSessionsFromDaemon as A,fetchUsageEventsFromDaemon as J}from"./cost/daemon-client.js";import{streamHistoryEditEvents as Y}from"./cost/history-stream.js";import{aggregate as q,applyFilters as V,parseSince as z}from"./cost/aggregate.js";import{renderText as K,renderJson as Z}from"./cost/format.js";const L=W("main");function C(){try{const e=F(M(import.meta.url));return JSON.parse(U(N(e,"../package.json"),"utf8")).version??"unknown"}catch{return"unknown"}}function Q(){process.stderr.write(`hydra-acp-budgeter: 'reset' as a CLI subcommand is no longer supported.
2
+ import{readFileSync as F}from"node:fs";import{dirname as N,resolve as M}from"node:path";import{fileURLToPath as P}from"node:url";import{loadConfig as _}from"./config.js";import{BudgeterBridge as H}from"./bridge.js";import{DEFAULT_RULE as W}from"./rule.js";import{logger as j,setDebug as B}from"./util/log.js";import{scanSessions as G,enrichSessionsWithLoc as A}from"./cost/session-store.js";import{listSessionsFromDaemon as J,fetchUsageEventsFromDaemon as Y}from"./cost/daemon-client.js";import{streamHistoryEditEvents as q}from"./cost/history-stream.js";import{aggregate as V,applyFilters as z,parseSince as K}from"./cost/aggregate.js";import{renderText as Z,renderJson as Q}from"./cost/format.js";const C=j("main");function D(){try{const e=N(P(import.meta.url));return JSON.parse(F(M(e,"../package.json"),"utf8")).version??"unknown"}catch{return"unknown"}}function X(){process.stderr.write(`hydra-acp-budgeter: 'reset' as a CLI subcommand is no longer supported.
3
3
  Run \`/hydra hydra-acp-budgeter reset\` in a live hydra session instead.
4
4
  Cost state is now persisted per-session in meta.json (extension_state).
5
- `),process.exit(2)}const X=`Usage: hydra budgeter usage [OPTIONS]
5
+ `),process.exit(2)}const ee=`Usage: hydra budgeter usage [OPTIONS]
6
6
 
7
7
  Options:
8
8
  --since <date|duration> Only include sessions updated after this date (e.g. 7d, 2024-01-01)
@@ -23,14 +23,14 @@ Options:
23
23
  --metric <cost|tokens|loc> Display metric (default: cost). "loc" counts net lines of code
24
24
  from Edit/Write tool diffs in session history.
25
25
  --json Output as JSON
26
- --help Show this help message`;async function D(e){if(e.includes("--help")){process.stdout.write(X+`
27
- `);return}let i,s="hour",r,a,l,w=!1,f,u=!0,o,k=!1,h,p=!1,m=!1;for(let t=0;t<e.length;t++){const n=e[t];if(n!==void 0){if(n==="--since")t+=1,i=e[t];else if(n==="--bucket")t+=1,s=ee(e[t]),p=!0;else if(n==="--by")t+=1,r=e[t];else if(n==="--depth")t+=1,a=e[t];else if(n==="--dir")t+=1,l=e[t];else if(n==="--interactive")w=!0;else if(n==="--min")t+=1,f=e[t];else if(n==="--histogram")u=!0,m=!0;else if(n==="--no-histogram")u=!1,m=!0;else if(n==="--metric")t+=1,o=e[t];else if(n==="--json")k=!0;else if(n==="--host")t+=1,h=e[t];else if(n.startsWith("--host="))h=n.slice(7);else if(n.startsWith("--"))throw new Error(`Unknown option: ${n}
26
+ --help Show this help message`;async function T(e){if(e.includes("--help")){process.stdout.write(ee+`
27
+ `);return}let i,s="hour",r,a,l,w=!1,f,u=!0,o,k=!1,h,p=!1,m=!1;for(let t=0;t<e.length;t++){const n=e[t];if(n!==void 0){if(n==="--since")t+=1,i=e[t];else if(n==="--bucket")t+=1,s=te(e[t]),p=!0;else if(n==="--by")t+=1,r=e[t];else if(n==="--depth")t+=1,a=e[t];else if(n==="--dir")t+=1,l=e[t];else if(n==="--interactive")w=!0;else if(n==="--min")t+=1,f=e[t];else if(n==="--histogram")u=!0,m=!0;else if(n==="--no-histogram")u=!1,m=!0;else if(n==="--metric")t+=1,o=e[t];else if(n==="--json")k=!0;else if(n==="--host")t+=1,h=e[t];else if(n.startsWith("--host="))h=n.slice(7);else if(n.startsWith("--"))throw new Error(`Unknown option: ${n}
28
28
  Run "hydra budgeter usage --help" for usage.`)}}if(o!==void 0&&o!=="cost"&&o!=="tokens"&&o!=="loc")throw new Error(`Invalid --metric value. Must be 'cost', 'tokens', or 'loc'.
29
29
  Run "hydra budgeter usage --help" for usage.`);const d=o==="loc";if(r!==void 0&&!new Set(["dir","session","model","agent","filetype"]).has(r))throw new Error(`Invalid --by value: ${r}. Must be one of: dir, session, model, agent, filetype.
30
30
  Run "hydra budgeter usage --help" for usage.`);if(r==="filetype"&&!d)throw new Error(`--by filetype requires --metric loc.
31
- Run "hydra budgeter usage --help" for usage.`);let y;if(i!==void 0)try{y=z(i)}catch(t){const n=t;throw new Error(`Invalid --since value: ${n.message}
32
- Run "hydra budgeter usage --help" for usage.`)}r!==void 0&&(p||(s=void 0),m||(u=!1)),!u&&!p&&(s=void 0);let c;if(y!==void 0)c=y;else{const t=new Date;s==="day"||s==="weekday"?t.setDate(t.getDate()-30):s==="week"?t.setMonth(t.getMonth()-6):s==="month"?t.setFullYear(t.getFullYear()-2):t.setHours(t.getHours()-24),c=t}const b=w?!0:void 0,g=o==="tokens",v=f!==void 0?parseFloat(f):void 0,E=await A()??B();(d||r==="filetype")&&await G(E);const S=V(E,{since:c,dir:l,interactive:b,min:v,minMetric:d?"loc":g?"tokens":"cost",host:h??"local"});let R;if(s!==void 0){const t=await J();t!==void 0&&(R=t.map(n=>({sessionId:n.sessionId,ts:n.ts,deltaCost:0,cumulativeCost:n.costCumulative,currency:n.costCurrency,inputTokens:n.contextTokens})))}let $;if(d&&s!==void 0){const t=[];for await(const n of Y(S))t.push(n);$=t}const T=a!==void 0?parseInt(a,10):void 0,I=q(S,R,{by:r,depth:T,bucket:s,since:c,interactive:b,dir:l,tokens:g,loc:d,min:v},$),x=te({sinceRaw:i,effectiveSince:c,bucket:s});if(k)process.stdout.write(Z(I)+`
33
- `);else{const t=K(I,{histogram:u,tokens:g,loc:d,windowLabel:x});process.stdout.write(t)}}function ee(e){const i=(e??"").toLowerCase();switch(i){case"weekday":case"dayofweek":case"day-of-week":case"day_of_week":case"dow":return"weekday";case"hour":case"day":case"week":case"month":return i;default:throw new Error(`unknown --bucket value ${JSON.stringify(e??"")} (expected hour, day, week, month, or weekday)`)}}function te(e){if(e.sinceRaw!==void 0){const i=se(e.sinceRaw);return i!==void 0?`last ${i}`:`since ${e.sinceRaw}`}return e.bucket==="day"||e.bucket==="weekday"?"last 30 days":e.bucket==="week"?"last 6 months":e.bucket==="month"?"last 2 years":e.bucket==="hour"?"last 24 hours":"last 24 hours (default; pass --since to widen)"}function se(e){const i=e.match(/^(\d+)\s*([dhmwy])$/i);if(i===null)return;let s=parseInt(i[1]??"0",10),r=(i[2]??"").toLowerCase();r==="h"&&s%24===0&&(s=s/24,r="d"),r==="d"&&s%7===0&&s<30&&(s=s/7,r="w"),r==="m"&&s%12===0&&(s=s/12,r="y");const a={h:"hour",d:"day",w:"week",m:"month",y:"year"}[r];if(a!==void 0)return`${s} ${a}${s===1?"":"s"}`}async function ne(){const e=P();j(e.debug);const i=new _({daemonWsUrl:e.hydraWsUrl,daemonHttpBase:e.hydraDaemonUrl,token:e.hydraToken,softLimit:e.softLimit,hardLimit:e.hardLimit,currency:e.currency,rule:H});i.start();const s=r=>{L.info(`${r} received \u2014 shutting down`),i.stop(),setTimeout(()=>process.exit(0),200).unref()};process.on("SIGINT",()=>s("SIGINT")),process.on("SIGTERM",()=>s("SIGTERM")),L.info(`hydra-acp-budgeter up; daemon=${e.hydraDaemonUrl} soft=${e.softLimit} hard=${e.hardLimit} ${e.currency} state=per-session (extension_state)`)}function ie(){process.stdout.write(`hydra-acp-budgeter ${C()}
31
+ Run "hydra budgeter usage --help" for usage.`);let y;if(i!==void 0)try{y=K(i)}catch(t){const n=t;throw new Error(`Invalid --since value: ${n.message}
32
+ Run "hydra budgeter usage --help" for usage.`)}r!==void 0&&(p||(s=void 0),m||(u=!1)),!u&&!p&&(s=void 0);let c;if(y!==void 0)c=y;else{const t=new Date;s==="day"||s==="weekday"?t.setDate(t.getDate()-30):s==="week"?t.setMonth(t.getMonth()-6):s==="month"?t.setFullYear(t.getFullYear()-2):t.setHours(t.getHours()-24),c=t}const b=w?!0:void 0,g=o==="tokens",v=f!==void 0?parseFloat(f):void 0,E=await J()??G();(d||r==="filetype")&&await A(E);const S=z(E,{since:c,dir:l,interactive:b,min:v,minMetric:d?"loc":g?"tokens":"cost",host:h??"local"});let R;const $=await Y();$!==void 0&&(R=$.map(t=>({sessionId:t.sessionId,ts:t.ts,deltaCost:0,cumulativeCost:t.costCumulative,currency:t.costCurrency,inputTokens:t.contextTokens})));let I;if(d&&s!==void 0){const t=[];for await(const n of q(S))t.push(n);I=t}const x=a!==void 0?parseInt(a,10):void 0,L=V(S,R,{by:r,depth:x,bucket:s,since:c,interactive:b,dir:l,tokens:g,loc:d,min:v},I),O=se({sinceRaw:i,effectiveSince:c,bucket:s});if(k)process.stdout.write(Q(L)+`
33
+ `);else{const t=Z(L,{histogram:u,tokens:g,loc:d,windowLabel:O});process.stdout.write(t)}}function te(e){const i=(e??"").toLowerCase();switch(i){case"weekday":case"dayofweek":case"day-of-week":case"day_of_week":case"dow":return"weekday";case"hour":case"day":case"week":case"month":return i;default:throw new Error(`unknown --bucket value ${JSON.stringify(e??"")} (expected hour, day, week, month, or weekday)`)}}function se(e){if(e.sinceRaw!==void 0){const i=ne(e.sinceRaw);return i!==void 0?`last ${i}`:`since ${e.sinceRaw}`}return e.bucket==="day"||e.bucket==="weekday"?"last 30 days":e.bucket==="week"?"last 6 months":e.bucket==="month"?"last 2 years":e.bucket==="hour"?"last 24 hours":"last 24 hours (default; pass --since to widen)"}function ne(e){const i=e.match(/^(\d+)\s*([dhmwy])$/i);if(i===null)return;let s=parseInt(i[1]??"0",10),r=(i[2]??"").toLowerCase();r==="h"&&s%24===0&&(s=s/24,r="d"),r==="d"&&s%7===0&&s<30&&(s=s/7,r="w"),r==="m"&&s%12===0&&(s=s/12,r="y");const a={h:"hour",d:"day",w:"week",m:"month",y:"year"}[r];if(a!==void 0)return`${s} ${a}${s===1?"":"s"}`}async function ie(){const e=_();B(e.debug);const i=new H({daemonWsUrl:e.hydraWsUrl,daemonHttpBase:e.hydraDaemonUrl,token:e.hydraToken,softLimit:e.softLimit,hardLimit:e.hardLimit,currency:e.currency,rule:W});i.start();const s=r=>{C.info(`${r} received \u2014 shutting down`),i.stop(),setTimeout(()=>process.exit(0),200).unref()};process.on("SIGINT",()=>s("SIGINT")),process.on("SIGTERM",()=>s("SIGTERM")),C.info(`hydra-acp-budgeter up; daemon=${e.hydraDaemonUrl} soft=${e.softLimit} hard=${e.hardLimit} ${e.currency} state=per-session (extension_state)`)}function re(){process.stdout.write(`hydra-acp-budgeter ${D()}
34
34
 
35
35
  Usage:
36
36
  hydra budgeter [usage] <flags> Report historical cost/usage across sessions
@@ -40,6 +40,6 @@ Flags:
40
40
  -v, --version Print version and exit
41
41
  repair Reconcile recorded cost against the agent ledger
42
42
  -h, --help Show this help
43
- `)}async function re(){const e=process.argv.slice(2);if(e.includes("--version")||e.includes("-v")){process.stdout.write(`hydra-acp-budgeter ${C()}
44
- `);return}if((e[0]==="help"||e.includes("--help")||e.includes("-h"))&&e[0]!=="usage"&&e[0]!=="cost"&&e[0]!=="repair"){ie();return}if(e[0]==="reset"){Q();return}if(e[0]==="repair"){const{runRepair:i}=await import("./cost/repair-cmd.js");await i(e.slice(1));return}if(e[0]==="usage"||e[0]==="cost"){await D(e.slice(1));return}if(e[0]==="run"||process.env.HYDRA_ACP_TOKEN){await ne();return}await D(e)}re().catch(e=>{process.stderr.write(`hydra-acp-budgeter: ${e.message}
43
+ `)}async function oe(){const e=process.argv.slice(2);if(e.includes("--version")||e.includes("-v")){process.stdout.write(`hydra-acp-budgeter ${D()}
44
+ `);return}if((e[0]==="help"||e.includes("--help")||e.includes("-h"))&&e[0]!=="usage"&&e[0]!=="cost"&&e[0]!=="repair"){re();return}if(e[0]==="reset"){X();return}if(e[0]==="repair"){const{runRepair:i}=await import("./cost/repair-cmd.js");await i(e.slice(1));return}if(e[0]==="usage"||e[0]==="cost"){await T(e.slice(1));return}if(e[0]==="run"||process.env.HYDRA_ACP_TOKEN){await ie();return}await T(e)}oe().catch(e=>{process.stderr.write(`hydra-acp-budgeter: ${e.message}
45
45
  `),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydra-acp/budgeter",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "Cost-budget transformer extension for hydra-acp — warns on soft limit, rejects further prompts/sessions on hard limit.",
5
5
  "license": "MIT",
6
6
  "type": "module",