@hydra-acp/budgeter 0.1.21 → 0.1.23
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/dist/cost/daemon-client.js +3 -3
- package/dist/index.js +6 -6
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{readFileSync as k}from"node:fs";import{homedir as
|
|
2
|
-
`)){const a=
|
|
3
|
-
`).filter(i=>i.length>0)}export{j as fetchSessionHistory,F as fetchToolBlob,O as fetchUsageEventsFromDaemon,H as listSessionsFromDaemon};
|
|
1
|
+
import{readFileSync as k}from"node:fs";import{homedir as b}from"node:os";import{resolve as m}from"node:path";import{logger as I}from"../util/log.js";const d=I("cost/daemon-client");function x(){if(process.env.HYDRA_ACP_TOKEN)return process.env.HYDRA_ACP_TOKEN;const e=m(process.env.HYDRA_ACP_HOME??m(b(),".hydra-acp"),"auth-token");try{const t=k(e,"utf8").trim();if(t.length>0)return t}catch(t){const n=t;n.code!=="ENOENT"&&d.debug(`auth-token read failed: ${n.message}`)}}function T(e=m(process.env.HYDRA_ACP_HOME??m(b(),".hydra-acp"),"daemon.pid")){if(process.env.HYDRA_ACP_DAEMON_URL)return process.env.HYDRA_ACP_DAEMON_URL;try{const t=k(e,"utf8"),n=JSON.parse(t);if(typeof n.loopbackPort=="number")return`http://127.0.0.1:${n.loopbackPort}`;if(typeof n.host=="string"&&typeof n.port=="number")return`http://${n.host}:${n.port}`}catch(t){const n=t;n.code!=="ENOENT"&&d.debug(`daemon.pid read failed: ${n.message}`)}return"http://127.0.0.1:8765"}function l(){const e=x();if(e!==void 0)return{daemonUrl:T(),token:e}}function P(e){const t=typeof e.sessionId=="string"?e.sessionId:void 0;if(t===void 0)return;const n=typeof e.updatedAt=="string"?e.updatedAt:"",o=(e._meta??{})["hydra-acp"]??{},i=typeof e.cwd=="string"?e.cwd:typeof o.cwd=="string"?o.cwd:void 0,r=typeof e.agentId=="string"?e.agentId:typeof o.agentId=="string"?o.agentId:"",u=typeof e.currentModel=="string"?e.currentModel:typeof o.currentModel=="string"?o.currentModel:"",a=typeof e.title=="string"?e.title:"",c=e.interactive===!0||o.interactive===!0,p=typeof e.importedFromMachine=="string"?e.importedFromMachine:typeof o.importedFromMachine=="string"?o.importedFromMachine:"",$=p.length>0?p:void 0,y=typeof e.upstreamSessionId=="string"?e.upstreamSessionId:typeof o.upstreamSessionId=="string"?o.upstreamSessionId:"",A=y.length>0?y:void 0,h=typeof e.remote=="string"?e.remote:"",v=h.length>0?h:void 0,R=e.currentUsage,E=o.currentUsage,f=R??E,C=typeof f?.costAmount=="number"?f.costAmount:0,S=typeof f?.costCurrency=="string"?f.costCurrency:"",U=typeof f?.used=="number"?f.used:0;return{sessionId:t,cwd:i,agentId:r,model:u,interactive:c,costAmount:C,costCurrency:S,contextTokens:U,title:a,createdAt:"",updatedAt:n,importedFromMachine:$,upstreamSessionId:A,remote:v}}async function H(){const e=l();if(e===void 0){d.debug("no daemon token resolved; skipping daemon fetch");return}const t=`${e.daemonUrl.replace(/\/$/,"")}/v1/sessions?includeNonInteractive=1`;let n;try{n=await fetch(t,{headers:{Authorization:`Bearer ${e.token}`}})}catch(r){d.debug(`daemon fetch failed (${t}): ${r.message}`);return}if(!n.ok){d.debug(`daemon returned HTTP ${n.status} for ${t}`);return}let s;try{s=await n.json()}catch(r){d.debug(`daemon response not JSON: ${r.message}`);return}const o=s;if(!Array.isArray(o.sessions)){d.debug("daemon response missing sessions[]");return}const i=[];for(const r of o.sessions)if(r&&typeof r=="object"&&!Array.isArray(r)){const u=P(r);u!==void 0&&i.push(u)}return i}function _(e){const t=typeof e.sessionId=="string"?e.sessionId:void 0,n=typeof e.ts=="string"?e.ts:void 0;if(t===void 0||n===void 0)return;const s=e.update??void 0,o=typeof s?.cost?.amount=="number"?s.cost.amount:0,i=typeof s?.cost?.currency=="string"?s.cost.currency:"USD",r=typeof s?.used=="number"?s.used:0;return{sessionId:t,ts:n,costCumulative:o,costCurrency:i,contextTokens:r}}async function O(e){const t=l();if(t===void 0)return;const n=new URLSearchParams({kinds:"usage_update"});e!==void 0&&n.set("since",e.toISOString());const s=`${t.daemonUrl.replace(/\/$/,"")}/v1/sessions/events?${n.toString()}`;let o;try{o=await fetch(s,{headers:{Authorization:`Bearer ${t.token}`}})}catch(u){d.debug(`events fetch failed: ${u.message}`);return}if(!o.ok){d.debug(`events endpoint HTTP ${o.status}`);return}const i=await o.text(),r=[];for(const u of i.split(`
|
|
2
|
+
`)){const a=u.trim();if(a.length===0)continue;let c;try{c=JSON.parse(a)}catch{continue}if(c&&typeof c=="object"&&!Array.isArray(c)){const p=_(c);p!==void 0&&r.push(p)}}return r}const g=new Map;async function F(e,t){const n=`${e}|${t}`,s=g.get(n);if(s!==void 0)return s;const o=l();if(o===void 0)return g.set(n,null),null;const i=`${o.daemonUrl.replace(/\/$/,"")}/v1/sessions/${encodeURIComponent(e)}/tools/${encodeURIComponent(t)}`;let r;try{r=await fetch(i,{headers:{Authorization:`Bearer ${o.token}`}})}catch(a){return d.debug(`tool blob fetch failed: ${a.message}`),g.set(n,null),null}if(!r.ok)return d.debug(`tool blob endpoint HTTP ${r.status} for ${t}`),g.set(n,null),null;const u=await r.text();return g.set(n,u),u}async function j(e){const t=l();if(t===void 0)return;const n=`${t.daemonUrl.replace(/\/$/,"")}/v1/sessions/${encodeURIComponent(e)}/history`;let s;try{s=await fetch(n,{headers:{Authorization:`Bearer ${t.token}`}})}catch(i){d.debug(`session history fetch failed (${n}): ${i.message}`);return}if(!s.ok){d.debug(`session history endpoint HTTP ${s.status} for ${e}`);return}return(await s.text()).split(`
|
|
3
|
+
`).filter(i=>i.length>0)}export{j as fetchSessionHistory,F as fetchToolBlob,O as fetchUsageEventsFromDaemon,H as listSessionsFromDaemon,T as resolveDaemonUrl};
|
package/dist/index.js
CHANGED
|
@@ -25,13 +25,13 @@ Options:
|
|
|
25
25
|
from Edit/Write tool diffs in session history.
|
|
26
26
|
--json Output as JSON
|
|
27
27
|
--help Show this help message`;async function x(e){if(e.includes("--help")){process.stdout.write(te+`
|
|
28
|
-
`);return}let i,s="hour",r,
|
|
28
|
+
`);return}let i,s="hour",r,d,h,w=!1,m,u=!0,o,k=!1,l,p=!1,y=!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=se(e[t]),p=!0;else if(n==="--by")t+=1,r=e[t];else if(n==="--depth")t+=1,d=e[t];else if(n==="--dir")t+=1,h=e[t];else if(n==="--interactive")w=!0;else if(n==="--min")t+=1,m=e[t];else if(n==="--histogram")u=!0,y=!0;else if(n==="--no-histogram")u=!1,y=!0;else if(n==="--metric")t+=1,o=e[t];else if(n==="--json")k=!0;else if(n==="--host")t+=1,l=e[t];else if(n.startsWith("--host="))l=n.slice(7);else if(n.startsWith("--"))throw new Error(`Unknown option: ${n}
|
|
29
29
|
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'.
|
|
30
|
-
Run "hydra budgeter usage --help" for usage.`);const
|
|
31
|
-
Run "hydra budgeter usage --help" for usage.`);if(r==="filetype"&&!
|
|
32
|
-
Run "hydra budgeter usage --help" for usage.`);let
|
|
33
|
-
Run "hydra budgeter usage --help" for usage.`)}r!==void 0&&(
|
|
34
|
-
`);else{const t=Q(L,{histogram:u,tokens:
|
|
30
|
+
Run "hydra budgeter usage --help" for usage.`);const a=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.
|
|
31
|
+
Run "hydra budgeter usage --help" for usage.`);if(r==="filetype"&&!a)throw new Error(`--by filetype requires --metric loc.
|
|
32
|
+
Run "hydra budgeter usage --help" for usage.`);let g;if(i!==void 0)try{g=Z(i)}catch(t){const n=t;throw new Error(`Invalid --since value: ${n.message}
|
|
33
|
+
Run "hydra budgeter usage --help" for usage.`)}r!==void 0&&(p||(s=void 0),y||(u=!1)),!u&&!p&&(s=void 0);let c;if(g!==void 0)c=g;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,f=o==="tokens",v=m!==void 0?parseFloat(m):void 0,O=await q()??J(),E=C(O,{host:l??"local",minMetric:a?"loc":f?"tokens":"cost"});(a||r==="filetype")&&await Y(E);const S=C(E,{since:c,dir:h,interactive:b,min:v,minMetric:a?"loc":f?"tokens":"cost",host:l??"local"});let R;const $=await V();$!==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(a&&s!==void 0){const t=[];for await(const n of z(S))t.push(n);I=t}const U=d!==void 0?parseInt(d,10):void 0,L=K(S,R,{by:r,depth:U,bucket:s,since:c,interactive:b,dir:h,tokens:f,loc:a,min:v},I),F=ne({sinceRaw:i,effectiveSince:c,bucket:s});if(k)process.stdout.write(X(L)+`
|
|
34
|
+
`);else{const t=Q(L,{histogram:u,tokens:f,loc:a,windowLabel:F});process.stdout.write(t)}}function se(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 ne(e){if(e.sinceRaw!==void 0){const i=ie(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 ie(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 d={h:"hour",d:"day",w:"week",m:"month",y:"year"}[r];if(d!==void 0)return`${s} ${d}${s===1?"":"s"}`}async function re(){const e=W();A(e.debug);const i=new j({daemonWsUrl:e.hydraWsUrl,daemonHttpBase:e.hydraDaemonUrl,token:e.hydraToken,softLimit:e.softLimit,hardLimit:e.hardLimit,currency:e.currency,rule:B});i.start();const s=r=>{D.info(`${r} received \u2014 shutting down`),i.stop(),setTimeout(()=>process.exit(0),200).unref()};process.on("SIGINT",()=>s("SIGINT")),process.on("SIGTERM",()=>s("SIGTERM")),D.info(`hydra-acp-budgeter up; daemon=${e.hydraDaemonUrl} soft=${e.softLimit} hard=${e.hardLimit} ${e.currency} state=per-session (extension_state)`)}function oe(){process.stdout.write(`hydra-acp-budgeter ${T()}
|
|
35
35
|
|
|
36
36
|
Usage:
|
|
37
37
|
hydra budgeter [usage] <flags> Report historical cost/usage across sessions
|
package/package.json
CHANGED