agentix-cli 0.13.0 → 0.15.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 +65 -478
- package/dist/agent-LRSSVFDR.js +2 -0
- package/dist/board-dashboard-DAPHL6W2.js +4508 -0
- package/dist/board-dashboard-DAPHL6W2.js.map +1 -0
- package/dist/chunk-3XULOWYB.js +199 -0
- package/dist/chunk-3XULOWYB.js.map +1 -0
- package/dist/chunk-6U2XOQHU.js +7 -0
- package/dist/chunk-6U2XOQHU.js.map +1 -0
- package/dist/{chunk-ZRYBSI5G.js → chunk-7MBTMCKY.js} +2 -2
- package/dist/{chunk-D6EDMQRV.js → chunk-AU37OREL.js} +5 -5
- package/dist/chunk-B4Q4X7D5.js +6 -0
- package/dist/chunk-B4Q4X7D5.js.map +1 -0
- package/dist/chunk-CXY2RYNI.js +11 -0
- package/dist/chunk-CXY2RYNI.js.map +1 -0
- package/dist/chunk-FIOB3MEX.js +2 -0
- package/dist/chunk-HLRVDCNH.js +5 -0
- package/dist/chunk-HLRVDCNH.js.map +1 -0
- package/dist/chunk-IEVEMKAI.js +168 -0
- package/dist/chunk-IEVEMKAI.js.map +1 -0
- package/dist/chunk-RB7265LO.js +12 -0
- package/dist/chunk-RB7265LO.js.map +1 -0
- package/dist/chunk-RGUYBH77.js +7 -0
- package/dist/chunk-RGUYBH77.js.map +1 -0
- package/dist/chunk-WVNSWBCU.js +138 -0
- package/dist/chunk-WVNSWBCU.js.map +1 -0
- package/dist/cli.js +64 -48
- package/dist/cli.js.map +1 -1
- package/dist/{compaction-7I3E5QUO.js → compaction-JBZZ7I7Y.js} +4 -4
- package/dist/config-DCJYECL4.js +2 -0
- package/dist/config-mutator-6EJHSP3Y.js +2 -0
- package/dist/debug-3MEUO2P2.js +2 -0
- package/dist/{heal-OTGT5HHJ.js → heal-WKY5I4KB.js} +2 -2
- package/dist/index.d.ts +894 -70
- package/dist/index.js +11 -11
- package/dist/index.js.map +1 -1
- package/dist/loader-PTCXLJEX.js +2 -0
- package/dist/memory-extract-AD7KMRPC.js +2 -0
- package/dist/{providers-G3WZ4RVJ.js → providers-VUIWO3ZK.js} +2 -2
- package/dist/registry-5ND7GLFJ.js +2 -0
- package/dist/scheduler-FCXOXCN5.js +2 -0
- package/dist/scheduler-FCXOXCN5.js.map +1 -0
- package/dist/{subagent-WV7QKQ3L.js → subagent-2SJHSSJS.js} +2 -2
- package/dist/{usage-dashboard-2TJQBFSH.js → usage-dashboard-QX3CF6KK.js} +8 -8
- package/dist/usage-dashboard-QX3CF6KK.js.map +1 -0
- package/dist/whatsapp-state-6D6FHHD2.js +2 -0
- package/dist/whatsapp-state-6D6FHHD2.js.map +1 -0
- package/dist/workspace-setup-ZWTT42XT.js +2 -0
- package/dist/workspace-setup-ZWTT42XT.js.map +1 -0
- package/package.json +10 -1
- package/dist/agent-5KIMJI3F.js +0 -2
- package/dist/chunk-34UAFPK4.js +0 -10
- package/dist/chunk-34UAFPK4.js.map +0 -1
- package/dist/chunk-4YCH6IZV.js +0 -2
- package/dist/chunk-CJ45Y2IR.js +0 -12
- package/dist/chunk-CJ45Y2IR.js.map +0 -1
- package/dist/chunk-T25UEXVK.js +0 -126
- package/dist/chunk-T25UEXVK.js.map +0 -1
- package/dist/chunk-VH23CDHL.js +0 -157
- package/dist/chunk-VH23CDHL.js.map +0 -1
- package/dist/chunk-WJ6ZF6EZ.js +0 -6
- package/dist/chunk-WJ6ZF6EZ.js.map +0 -1
- package/dist/config-HAD5E4YX.js +0 -2
- package/dist/debug-N3B5NVJU.js +0 -2
- package/dist/loader-YQE6XJBT.js +0 -2
- package/dist/memory-extract-RGPUPMKU.js +0 -2
- package/dist/registry-CDWY7CLL.js +0 -2
- package/dist/usage-dashboard-2TJQBFSH.js.map +0 -1
- package/dist/workspace-setup-QQTCJY4B.js +0 -2
- /package/dist/{agent-5KIMJI3F.js.map → agent-LRSSVFDR.js.map} +0 -0
- /package/dist/{chunk-ZRYBSI5G.js.map → chunk-7MBTMCKY.js.map} +0 -0
- /package/dist/{chunk-D6EDMQRV.js.map → chunk-AU37OREL.js.map} +0 -0
- /package/dist/{chunk-4YCH6IZV.js.map → chunk-FIOB3MEX.js.map} +0 -0
- /package/dist/{compaction-7I3E5QUO.js.map → compaction-JBZZ7I7Y.js.map} +0 -0
- /package/dist/{config-HAD5E4YX.js.map → config-DCJYECL4.js.map} +0 -0
- /package/dist/{debug-N3B5NVJU.js.map → config-mutator-6EJHSP3Y.js.map} +0 -0
- /package/dist/{heal-OTGT5HHJ.js.map → debug-3MEUO2P2.js.map} +0 -0
- /package/dist/{loader-YQE6XJBT.js.map → heal-WKY5I4KB.js.map} +0 -0
- /package/dist/{memory-extract-RGPUPMKU.js.map → loader-PTCXLJEX.js.map} +0 -0
- /package/dist/{providers-G3WZ4RVJ.js.map → memory-extract-AD7KMRPC.js.map} +0 -0
- /package/dist/{registry-CDWY7CLL.js.map → providers-VUIWO3ZK.js.map} +0 -0
- /package/dist/{workspace-setup-QQTCJY4B.js.map → registry-5ND7GLFJ.js.map} +0 -0
- /package/dist/{subagent-WV7QKQ3L.js.map → subagent-2SJHSSJS.js.map} +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{e as a,f as b,g as c}from"./chunk-KXZMYLHQ.js";import"./chunk-M7HKBG3V.js";import"./chunk-
|
|
2
|
-
//# sourceMappingURL=providers-
|
|
1
|
+
import{e as a,f as b,g as c}from"./chunk-KXZMYLHQ.js";import"./chunk-M7HKBG3V.js";import"./chunk-FIOB3MEX.js";export{b as ClaudeCodeProvider,a as ClaudeProvider,c as createProvider};
|
|
2
|
+
//# sourceMappingURL=providers-VUIWO3ZK.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{g as a,h as b,i as c}from"./chunk-IEVEMKAI.js";import"./chunk-CXY2RYNI.js";import"./chunk-HI24KAEY.js";import"./chunk-7MBTMCKY.js";import"./chunk-QOYAX2FT.js";import"./chunk-FIOB3MEX.js";export{c as AgentRegistry,b as getGlobalRegistry,a as setGlobalRegistry};
|
|
2
|
+
//# sourceMappingURL=registry-5ND7GLFJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import"./chunk-
|
|
1
|
+
import"./chunk-FIOB3MEX.js";var g=class{registry;activeRuns=new Map;runCounter=0;log;depthMap=new Map;constructor(t,e=console.error.bind(console,"[subagent]")){this.registry=t,this.log=e}async spawn(t){let e=this.depthMap.get(t.parentAgentId)||0;if(e>=3)return{runId:"",targetAgentId:t.targetAgentId,success:!1,content:"",error:`Sub-agent spawn depth exceeded (max: 3). Cannot spawn "${t.targetAgentId}" from "${t.parentAgentId}".`,duration:0};let n=`sub-${++this.runCounter}-${Date.now().toString(36)}`,r={request:t,runId:n,startedAt:Date.now()};this.activeRuns.set(n,r),this.depthMap.set(t.targetAgentId,e+1),this.log(`Spawning sub-agent: ${t.targetAgentId} (parent: ${t.parentAgentId}, depth: ${e+1}, blocking: ${t.blocking??!1})`);let o={message:this.buildSubAgentPrompt(t),agentId:t.targetAgentId,context:{channel:"subagent",sender:`agent:${t.parentAgentId}`,chatId:`subagent:${n}`}},i=t.timeout??3e5;try{let a=await Promise.race([this.registry.execute(o),this.timeoutPromise(i,t.targetAgentId)]),s={runId:n,targetAgentId:t.targetAgentId,success:!a.error,content:a.content,error:a.error,duration:Date.now()-r.startedAt};return this.log(`Sub-agent ${t.targetAgentId} completed in ${s.duration}ms (${s.success?"success":"error"})`),s}catch(a){return{runId:n,targetAgentId:t.targetAgentId,success:!1,content:"",error:a.message,duration:Date.now()-r.startedAt}}finally{this.activeRuns.delete(n),this.depthMap.delete(t.targetAgentId)}}async spawnParallel(t){return Promise.all(t.map(e=>this.spawn(e)))}listActive(){return Array.from(this.activeRuns.values()).map(t=>({runId:t.runId,targetAgentId:t.request.targetAgentId,parentAgentId:t.request.parentAgentId,duration:Date.now()-t.startedAt}))}buildSubAgentPrompt(t){return[`[Sub-agent task from ${t.parentAgentId}]`,"You have been spawned as a background sub-agent to handle a specific task.","Focus on completing the task below and provide a clear, actionable response.","",t.prompt].join(`
|
|
2
2
|
`)}timeoutPromise(t,e){return new Promise((n,r)=>{setTimeout(()=>{r(new Error(`Sub-agent "${e}" timed out after ${Math.round(t/1e3)}s`))},t)})}};export{g as SubAgentManager};
|
|
3
|
-
//# sourceMappingURL=subagent-
|
|
3
|
+
//# sourceMappingURL=subagent-2SJHSSJS.js.map
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import{a as p}from"./chunk-
|
|
1
|
+
import{a as p}from"./chunk-CXY2RYNI.js";import"./chunk-FIOB3MEX.js";import{createServer as w}from"http";import{readdirSync as k}from"fs";function T(s=4201){let n=new p;w(async(i,e)=>{let t=new URL(i.url||"/",`http://localhost:${s}`),o=t.pathname;if(e.setHeader("Access-Control-Allow-Origin","*"),o==="/api/usage"){let c=t.searchParams.get("from")||"",r=t.searchParams.get("to")||"",l=R(n,c,r);e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify(l));return}e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(S())}).listen(s,()=>{console.log(`
|
|
2
2
|
Usage dashboard: http://localhost:${s}
|
|
3
|
-
`)})}function
|
|
3
|
+
`)})}function R(s,n,d){let i=s.dir,e=[];try{for(let t of k(i)){if(!t.endsWith(".json"))continue;let o=t.replace(".json","");n&&o<n||d&&o>d||e.push(o)}}catch{return[]}return e.sort(),e.map(t=>{let o=s.getDate(t);if(!o)return null;let c=0,r=0,l=0,h=0,u=0,f=0,x={};for(let[C,a]of Object.entries(o.agents)){let m=p.calculateCost(a),g=(a.inputTokens||0)+(a.tier2InputTokens||0),b=(a.outputTokens||0)+(a.tier2OutputTokens||0),v=(a.cacheReadTokens||0)+(a.tier2CacheReadTokens||0),y=(a.cacheCreateTokens||0)+(a.tier2CacheCreateTokens||0);c+=a.tasks,r+=g,l+=b,h+=v,u+=y,f+=m,x[C]={tasks:a.tasks,cost:m,input:g,output:b,cacheRead:v,cacheCreate:y,model:a.model}}return{date:t,tasks:c,input:r,output:l,cacheRead:h,cacheCreate:u,cost:f,agents:x}}).filter(t=>t!==null&&t.tasks>0)}function S(){return`<!DOCTYPE html>
|
|
4
4
|
<html lang="en">
|
|
5
5
|
<head>
|
|
6
6
|
<meta charset="utf-8">
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
8
|
<title>AgentX Usage Dashboard</title>
|
|
9
|
-
<style>${
|
|
9
|
+
<style>${D}</style>
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div class="header">
|
|
@@ -56,9 +56,9 @@ import{a as p}from"./chunk-34UAFPK4.js";import"./chunk-4YCH6IZV.js";import{creat
|
|
|
56
56
|
</table>
|
|
57
57
|
</div>
|
|
58
58
|
|
|
59
|
-
<script>${
|
|
59
|
+
<script>${O}</script>
|
|
60
60
|
</body>
|
|
61
|
-
</html>`}var
|
|
61
|
+
</html>`}var D=`
|
|
62
62
|
:root {
|
|
63
63
|
--bg: #0f1117;
|
|
64
64
|
--card: #1a1d27;
|
|
@@ -100,7 +100,7 @@ td { padding: 8px 12px; border-bottom: 1px solid var(--border); }
|
|
|
100
100
|
tr:hover td { background: rgba(99, 102, 241, 0.05); }
|
|
101
101
|
.cost { color: var(--accent); font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
102
102
|
.tok { font-variant-numeric: tabular-nums; }
|
|
103
|
-
`,
|
|
103
|
+
`,O=`
|
|
104
104
|
let data = [];
|
|
105
105
|
|
|
106
106
|
function fmtTok(n) {
|
|
@@ -400,5 +400,5 @@ function renderTable() {
|
|
|
400
400
|
|
|
401
401
|
// Auto-load last 30 days
|
|
402
402
|
setRange(30);
|
|
403
|
-
`,
|
|
404
|
-
//# sourceMappingURL=usage-dashboard-
|
|
403
|
+
`,W=T;export{W as default,T as startUsageDashboard};
|
|
404
|
+
//# sourceMappingURL=usage-dashboard-QX3CF6KK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/daemon/usage-dashboard.ts"],"sourcesContent":["import { createServer } from \"http\"\nimport { readdirSync } from \"fs\"\nimport { TokenTracker, type DailyUsage, type AgentUsage, type DailyReport } from \"./token-tracker\"\n\n// --- Usage Dashboard: zero-dep web UI for token cost tracking ---\n\nexport function startUsageDashboard(port: number = 4201): void {\n const tracker = new TokenTracker()\n\n const server = createServer(async (req, res) => {\n const url = new URL(req.url || \"/\", `http://localhost:${port}`)\n const path = url.pathname\n\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\")\n\n if (path === \"/api/usage\") {\n const from = url.searchParams.get(\"from\") || \"\"\n const to = url.searchParams.get(\"to\") || \"\"\n const data = loadUsageRange(tracker, from, to)\n res.writeHead(200, { \"Content-Type\": \"application/json\" })\n res.end(JSON.stringify(data))\n return\n }\n\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" })\n res.end(renderDashboard())\n })\n\n server.listen(port, () => {\n console.log(`\\n Usage dashboard: http://localhost:${port}\\n`)\n })\n}\n\n// --- Data loading ---\n\ninterface UsageDay {\n date: string\n tasks: number\n input: number\n output: number\n cacheRead: number\n cacheCreate: number\n cost: number\n agents: Record<string, { tasks: number; cost: number; input: number; output: number; cacheRead: number; cacheCreate: number; model?: string }>\n}\n\nfunction loadUsageRange(tracker: TokenTracker, from: string, to: string): UsageDay[] {\n const dir = tracker[\"dir\"] // access private dir for file listing\n const files: string[] = []\n\n try {\n for (const f of readdirSync(dir)) {\n if (!f.endsWith(\".json\")) continue\n const date = f.replace(\".json\", \"\")\n if (from && date < from) continue\n if (to && date > to) continue\n files.push(date)\n }\n } catch {\n return []\n }\n\n files.sort()\n\n return files.map((date) => {\n const usage = tracker.getDate(date)\n if (!usage) return null\n\n let tasks = 0, input = 0, output = 0, cacheRead = 0, cacheCreate = 0, cost = 0\n const agents: UsageDay[\"agents\"] = {}\n\n for (const [id, agent] of Object.entries(usage.agents)) {\n const agentCost = TokenTracker.calculateCost(agent)\n // Token displays sum tier1 + tier2 so operators see the REAL volume\n // they paid for. Tier2 tokens (from requests >200K input) are billed\n // at 1.5×, but they're still tokens processed — hiding them made\n // low-display/high-cost days look inconsistent on the dashboard.\n const aInput = (agent.inputTokens || 0) + (agent.tier2InputTokens || 0)\n const aOutput = (agent.outputTokens || 0) + (agent.tier2OutputTokens || 0)\n const aCacheRead = (agent.cacheReadTokens || 0) + (agent.tier2CacheReadTokens || 0)\n const aCacheCreate = (agent.cacheCreateTokens || 0) + (agent.tier2CacheCreateTokens || 0)\n tasks += agent.tasks\n input += aInput\n output += aOutput\n cacheRead += aCacheRead\n cacheCreate += aCacheCreate\n cost += agentCost\n agents[id] = {\n tasks: agent.tasks,\n cost: agentCost,\n input: aInput,\n output: aOutput,\n cacheRead: aCacheRead,\n cacheCreate: aCacheCreate,\n model: agent.model,\n }\n }\n\n return { date, tasks, input, output, cacheRead, cacheCreate, cost, agents }\n }).filter((d): d is UsageDay => d !== null && d.tasks > 0)\n}\n\n// --- HTML rendering ---\n\nfunction renderDashboard(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>AgentX Usage Dashboard</title>\n<style>${CSS}</style>\n</head>\n<body>\n<div class=\"header\">\n <h1>AgentX Usage Dashboard</h1>\n <div class=\"filters\">\n <label>From <input type=\"date\" id=\"from\"></label>\n <label>To <input type=\"date\" id=\"to\"></label>\n <button onclick=\"load()\">Apply</button>\n <button onclick=\"setRange(7)\">7d</button>\n <button onclick=\"setRange(14)\">14d</button>\n <button onclick=\"setRange(30)\">30d</button>\n <button onclick=\"setRange(0)\">All</button>\n </div>\n</div>\n\n<div class=\"stats\" id=\"stats\"></div>\n\n<div class=\"grid\">\n <div class=\"card\">\n <h2>Cost Over Time</h2>\n <canvas id=\"costChart\" height=\"260\"></canvas>\n </div>\n <div class=\"card\">\n <h2>Tokens Over Time</h2>\n <canvas id=\"tokenChart\" height=\"260\"></canvas>\n </div>\n</div>\n\n<div class=\"grid\">\n <div class=\"card\">\n <h2>Cost Per Agent</h2>\n <canvas id=\"agentChart\" height=\"260\"></canvas>\n </div>\n <div class=\"card\">\n <h2>Token Breakdown</h2>\n <canvas id=\"breakdownChart\" height=\"260\"></canvas>\n </div>\n</div>\n\n<div class=\"card\">\n <h2>Daily Detail</h2>\n <table id=\"table\">\n <thead>\n <tr><th>Date</th><th>Tasks</th><th>Input</th><th>Output</th><th>Cache R</th><th>Cache W</th><th>Cost</th><th>Top Agent</th></tr>\n </thead>\n <tbody></tbody>\n </table>\n</div>\n\n<script>${JS}</script>\n</body>\n</html>`\n}\n\n// --- Embedded CSS ---\n\nconst CSS = `\n:root {\n --bg: #0f1117;\n --card: #1a1d27;\n --border: #2a2d3a;\n --text: #e1e4ed;\n --muted: #8b8fa3;\n --accent: #6366f1;\n --green: #22c55e;\n --orange: #f59e0b;\n --red: #ef4444;\n --blue: #3b82f6;\n --purple: #a855f7;\n --cyan: #06b6d4;\n}\n* { margin: 0; padding: 0; box-sizing: border-box; }\nbody { background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; padding: 24px; }\n.header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; flex-wrap: wrap; gap: 12px; }\nh1 { font-size: 20px; font-weight: 600; }\nh2 { font-size: 14px; font-weight: 500; color: var(--muted); margin-bottom: 16px; text-transform: uppercase; letter-spacing: 0.5px; }\n.filters { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }\n.filters label { font-size: 13px; color: var(--muted); }\n.filters input { background: var(--card); border: 1px solid var(--border); color: var(--text); padding: 6px 10px; border-radius: 6px; font-size: 13px; }\n.filters button { background: var(--card); border: 1px solid var(--border); color: var(--text); padding: 6px 14px; border-radius: 6px; cursor: pointer; font-size: 13px; transition: background 0.15s; }\n.filters button:hover { background: var(--accent); border-color: var(--accent); }\n.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 24px; }\n.stat { background: var(--card); border: 1px solid var(--border); border-radius: 10px; padding: 16px; }\n.stat .value { font-size: 24px; font-weight: 700; margin-bottom: 4px; }\n.stat .label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }\n.stat .delta { font-size: 12px; margin-top: 4px; }\n.stat .delta.up { color: var(--red); }\n.stat .delta.down { color: var(--green); }\n.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }\n@media (max-width: 900px) { .grid { grid-template-columns: 1fr; } }\n.card { background: var(--card); border: 1px solid var(--border); border-radius: 10px; padding: 20px; }\ncanvas { width: 100% !important; }\ntable { width: 100%; border-collapse: collapse; font-size: 13px; }\nth { text-align: left; padding: 8px 12px; border-bottom: 1px solid var(--border); color: var(--muted); font-weight: 500; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; }\ntd { padding: 8px 12px; border-bottom: 1px solid var(--border); }\ntr:hover td { background: rgba(99, 102, 241, 0.05); }\n.cost { color: var(--accent); font-weight: 600; font-variant-numeric: tabular-nums; }\n.tok { font-variant-numeric: tabular-nums; }\n`\n\n// --- Embedded JavaScript ---\n\nconst JS = `\nlet data = [];\n\nfunction fmtTok(n) {\n if (n >= 1e6) return (n/1e6).toFixed(1) + 'M';\n if (n >= 1e3) return (n/1e3).toFixed(0) + 'K';\n return String(n);\n}\nfunction fmtCost(n) { return n < 0.01 ? '$' + n.toFixed(4) : '$' + n.toFixed(2); }\n\nfunction setRange(days) {\n if (days === 0) {\n document.getElementById('from').value = '';\n document.getElementById('to').value = '';\n } else {\n const to = new Date();\n const from = new Date(Date.now() - days * 86400000);\n document.getElementById('from').value = from.toISOString().slice(0,10);\n document.getElementById('to').value = to.toISOString().slice(0,10);\n }\n load();\n}\n\nasync function load() {\n const from = document.getElementById('from').value;\n const to = document.getElementById('to').value;\n const params = new URLSearchParams();\n if (from) params.set('from', from);\n if (to) params.set('to', to);\n const res = await fetch('/api/usage?' + params);\n data = await res.json();\n render();\n}\n\nfunction render() {\n renderStats();\n renderCostChart();\n renderTokenChart();\n renderAgentChart();\n renderBreakdownChart();\n renderTable();\n}\n\nfunction renderStats() {\n const el = document.getElementById('stats');\n const totalCost = data.reduce((s,d) => s + d.cost, 0);\n const totalTasks = data.reduce((s,d) => s + d.tasks, 0);\n const totalInput = data.reduce((s,d) => s + d.input, 0);\n const totalOutput = data.reduce((s,d) => s + d.output, 0);\n const totalCacheR = data.reduce((s,d) => s + d.cacheRead, 0);\n const totalCacheW = data.reduce((s,d) => s + d.cacheCreate, 0);\n const cacheRatio = (totalCacheR + totalCacheW) > 0 ? (totalCacheR / (totalCacheR + totalCacheW) * 100).toFixed(0) : '0';\n const avgDaily = data.length > 0 ? totalCost / data.length : 0;\n\n // Delta: compare last day vs previous\n let delta = '';\n if (data.length >= 2) {\n const last = data[data.length-1].cost;\n const prev = data[data.length-2].cost;\n if (prev > 0) {\n const pct = ((last - prev) / prev * 100).toFixed(0);\n const cls = last > prev ? 'up' : 'down';\n const arrow = last > prev ? '\\\\u2191' : '\\\\u2193';\n delta = '<div class=\"delta ' + cls + '\">' + arrow + ' ' + Math.abs(pct) + '% vs prev day</div>';\n }\n }\n\n el.innerHTML =\n stat(fmtCost(totalCost), 'Total Cost', delta) +\n stat(fmtCost(avgDaily), 'Avg Daily Cost', '') +\n stat(totalTasks.toLocaleString(), 'Total Tasks', '') +\n stat(fmtTok(totalInput + totalOutput), 'Tokens (I/O)', '') +\n stat(fmtTok(totalCacheR), 'Cache Reads', '') +\n stat(cacheRatio + '%', 'Cache Hit Rate', '') +\n stat(data.length.toString(), 'Days Tracked', '');\n}\n\nfunction stat(value, label, extra) {\n return '<div class=\"stat\"><div class=\"value\">' + value + '</div><div class=\"label\">' + label + '</div>' + extra + '</div>';\n}\n\n// --- Canvas chart helpers ---\nfunction drawLine(ctx, points, w, h, color, fill) {\n if (points.length < 2) return;\n const max = Math.max(...points, 0.001);\n const pad = { t: 10, r: 10, b: 30, l: 50 };\n const cw = w - pad.l - pad.r;\n const ch = h - pad.t - pad.b;\n\n ctx.strokeStyle = color;\n ctx.lineWidth = 2;\n ctx.beginPath();\n points.forEach((v, i) => {\n const x = pad.l + (i / (points.length - 1)) * cw;\n const y = pad.t + ch - (v / max) * ch;\n i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);\n });\n ctx.stroke();\n\n if (fill) {\n ctx.lineTo(pad.l + cw, pad.t + ch);\n ctx.lineTo(pad.l, pad.t + ch);\n ctx.closePath();\n ctx.fillStyle = fill;\n ctx.fill();\n }\n\n // Y axis labels\n ctx.fillStyle = '#8b8fa3';\n ctx.font = '11px -apple-system, sans-serif';\n ctx.textAlign = 'right';\n for (let i = 0; i <= 4; i++) {\n const v = (max / 4) * i;\n const y = pad.t + ch - (i / 4) * ch;\n ctx.fillText(v >= 1000 ? fmtTok(v) : v < 0.01 ? v.toFixed(4) : v.toFixed(2), pad.l - 6, y + 4);\n ctx.strokeStyle = '#2a2d3a';\n ctx.lineWidth = 0.5;\n ctx.beginPath(); ctx.moveTo(pad.l, y); ctx.lineTo(pad.l + cw, y); ctx.stroke();\n }\n\n // X axis labels (first, middle, last)\n ctx.fillStyle = '#8b8fa3';\n ctx.textAlign = 'center';\n if (data.length > 0) {\n const labels = [0, Math.floor(data.length/2), data.length-1];\n labels.forEach(i => {\n if (i < data.length) {\n const x = pad.l + (i / (points.length - 1)) * cw;\n ctx.fillText(data[i].date.slice(5), x, pad.t + ch + 18);\n }\n });\n }\n}\n\nfunction drawBars(ctx, items, w, h, colors) {\n if (items.length === 0) return;\n const max = Math.max(...items.map(i => i.value), 0.001);\n const pad = { t: 10, r: 10, b: 60, l: 60 };\n const cw = w - pad.l - pad.r;\n const ch = h - pad.t - pad.b;\n const barW = Math.min(40, (cw / items.length) * 0.7);\n const gap = cw / items.length;\n\n items.forEach((item, i) => {\n const barH = (item.value / max) * ch;\n const x = pad.l + i * gap + (gap - barW) / 2;\n const y = pad.t + ch - barH;\n ctx.fillStyle = colors[i % colors.length];\n ctx.beginPath();\n ctx.roundRect(x, y, barW, barH, 3);\n ctx.fill();\n\n // Label\n ctx.fillStyle = '#8b8fa3';\n ctx.font = '11px -apple-system, sans-serif';\n ctx.textAlign = 'center';\n ctx.save();\n ctx.translate(x + barW/2, pad.t + ch + 12);\n ctx.rotate(-0.4);\n ctx.fillText(item.label.length > 14 ? item.label.slice(0,12) + '..' : item.label, 0, 0);\n ctx.restore();\n\n // Value on top\n ctx.fillStyle = '#e1e4ed';\n ctx.textAlign = 'center';\n ctx.fillText(item.fmt, x + barW/2, y - 6);\n });\n}\n\nfunction drawStacked(ctx, series, labels, w, h, colors) {\n if (labels.length === 0) return;\n const pad = { t: 10, r: 10, b: 30, l: 50 };\n const cw = w - pad.l - pad.r;\n const ch = h - pad.t - pad.b;\n\n // Calculate totals for max\n const totals = labels.map((_, i) => series.reduce((s, sr) => s + sr.values[i], 0));\n const max = Math.max(...totals, 1);\n\n const barW = Math.min(30, (cw / labels.length) * 0.7);\n const gap = cw / labels.length;\n\n labels.forEach((_, li) => {\n let yOffset = 0;\n series.forEach((sr, si) => {\n const val = sr.values[li];\n const barH = (val / max) * ch;\n const x = pad.l + li * gap + (gap - barW) / 2;\n const y = pad.t + ch - yOffset - barH;\n ctx.fillStyle = colors[si % colors.length];\n ctx.fillRect(x, y, barW, barH);\n yOffset += barH;\n });\n });\n\n // Legend\n ctx.font = '11px -apple-system, sans-serif';\n series.forEach((sr, i) => {\n const x = pad.l + i * 100;\n ctx.fillStyle = colors[i % colors.length];\n ctx.fillRect(x, h - 16, 10, 10);\n ctx.fillStyle = '#8b8fa3';\n ctx.textAlign = 'left';\n ctx.fillText(sr.name, x + 14, h - 7);\n });\n\n // X axis\n ctx.fillStyle = '#8b8fa3';\n ctx.textAlign = 'center';\n if (data.length > 0) {\n [0, Math.floor(labels.length/2), labels.length-1].forEach(i => {\n if (i < labels.length) {\n const x = pad.l + (i / Math.max(labels.length-1,1)) * cw;\n ctx.fillText(labels[i].slice(5), x, pad.t + ch + 18);\n }\n });\n }\n}\n\nfunction setupCanvas(id) {\n const canvas = document.getElementById(id);\n const dpr = window.devicePixelRatio || 1;\n const rect = canvas.parentElement.getBoundingClientRect();\n const w = rect.width - 40; // account for card padding\n const h = canvas.height;\n canvas.width = w * dpr;\n canvas.height = h * dpr;\n canvas.style.width = w + 'px';\n canvas.style.height = h + 'px';\n const ctx = canvas.getContext('2d');\n ctx.scale(dpr, dpr);\n ctx.clearRect(0, 0, w, h);\n return { ctx, w, h };\n}\n\nfunction renderCostChart() {\n const { ctx, w, h } = setupCanvas('costChart');\n const points = data.map(d => d.cost);\n drawLine(ctx, points, w, h, '#6366f1', 'rgba(99,102,241,0.1)');\n}\n\nfunction renderTokenChart() {\n const { ctx, w, h } = setupCanvas('tokenChart');\n const labels = data.map(d => d.date);\n drawStacked(ctx, [\n { name: 'Input', values: data.map(d => d.input) },\n { name: 'Output', values: data.map(d => d.output) },\n { name: 'Cache R', values: data.map(d => d.cacheRead) },\n { name: 'Cache W', values: data.map(d => d.cacheCreate) },\n ], labels, w, h, ['#3b82f6', '#22c55e', '#06b6d4', '#f59e0b']);\n}\n\nfunction renderAgentChart() {\n const { ctx, w, h } = setupCanvas('agentChart');\n const agentTotals = {};\n data.forEach(d => {\n for (const [id, a] of Object.entries(d.agents)) {\n agentTotals[id] = (agentTotals[id] || 0) + a.cost;\n }\n });\n const items = Object.entries(agentTotals)\n .sort(([,a],[,b]) => b - a)\n .slice(0, 10)\n .map(([id, cost]) => ({ label: id, value: cost, fmt: fmtCost(cost) }));\n drawBars(ctx, items, w, h, ['#6366f1','#3b82f6','#06b6d4','#22c55e','#f59e0b','#ef4444','#a855f7','#ec4899']);\n}\n\nfunction renderBreakdownChart() {\n const { ctx, w, h } = setupCanvas('breakdownChart');\n const totals = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 };\n data.forEach(d => { totals.input += d.input; totals.output += d.output; totals.cacheRead += d.cacheRead; totals.cacheCreate += d.cacheCreate; });\n const items = [\n { label: 'Input', value: totals.input, fmt: fmtTok(totals.input) },\n { label: 'Output', value: totals.output, fmt: fmtTok(totals.output) },\n { label: 'Cache Read', value: totals.cacheRead, fmt: fmtTok(totals.cacheRead) },\n { label: 'Cache Write', value: totals.cacheCreate, fmt: fmtTok(totals.cacheCreate) },\n ];\n drawBars(ctx, items, w, h, ['#3b82f6','#22c55e','#06b6d4','#f59e0b']);\n}\n\nfunction renderTable() {\n const tbody = document.querySelector('#table tbody');\n tbody.innerHTML = data.slice().reverse().map(d => {\n const top = Object.entries(d.agents).sort(([,a],[,b]) => b.cost - a.cost)[0];\n const topStr = top ? top[0] + ' (' + fmtCost(top[1].cost) + ')' : '-';\n return '<tr>' +\n '<td>' + d.date + '</td>' +\n '<td>' + d.tasks + '</td>' +\n '<td class=\"tok\">' + fmtTok(d.input) + '</td>' +\n '<td class=\"tok\">' + fmtTok(d.output) + '</td>' +\n '<td class=\"tok\">' + fmtTok(d.cacheRead) + '</td>' +\n '<td class=\"tok\">' + fmtTok(d.cacheCreate) + '</td>' +\n '<td class=\"cost\">' + fmtCost(d.cost) + '</td>' +\n '<td>' + topStr + '</td>' +\n '</tr>';\n }).join('');\n}\n\n// Auto-load last 30 days\nsetRange(30);\n`\n\nexport default startUsageDashboard\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8SJC,EAAQrC","names":["createServer","readdirSync","startUsageDashboard","port","tracker","TokenTracker","createServer","req","res","url","path","from","to","data","loadUsageRange","renderDashboard","dir","files","f","readdirSync","date","usage","tasks","input","output","cacheRead","cacheCreate","cost","agents","id","agent","agentCost","aInput","aOutput","aCacheRead","aCacheCreate","d","CSS","JS","usage_dashboard_default"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"./chunk-FIOB3MEX.js";var t={connection:"init",qr:null};function p(n){t.qr=n,t.qrUpdatedAt=new Date().toISOString()}function i(n,e){t.connection=n,t.detail=e,t.statusUpdatedAt=new Date().toISOString(),n==="open"&&(t.qr=null)}function o(){return{...t}}export{o as getWhatsAppState,p as setWhatsAppQR,i as setWhatsAppStatus};
|
|
2
|
+
//# sourceMappingURL=whatsapp-state-6D6FHHD2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/daemon/whatsapp-state.ts"],"sourcesContent":["// --- WhatsApp pairing state (daemon-local, in-memory) ---\n//\n// The WhatsApp adapter emits a QR string every time a fresh pairing is needed\n// (first run, after logout, after 440/408 timeouts). We stash the latest QR +\n// connection state here so the admin dashboard can poll for it and render an\n// inline pairing image without touching the daemon logs.\n//\n// All state is intentionally in-memory: if the daemon restarts, the adapter\n// reconnects and re-emits anyway. Nothing here is sensitive — the QR is only\n// useful to the person with physical access to the WhatsApp account.\n\nexport type WhatsAppConnection = \"init\" | \"connecting\" | \"open\" | \"close\"\n\nexport interface WhatsAppState {\n connection: WhatsAppConnection\n detail?: string\n qr: string | null\n qrUpdatedAt?: string\n statusUpdatedAt?: string\n}\n\nconst state: WhatsAppState = {\n connection: \"init\",\n qr: null,\n}\n\nexport function setWhatsAppQR(qr: string | null): void {\n state.qr = qr\n state.qrUpdatedAt = new Date().toISOString()\n}\n\nexport function setWhatsAppStatus(connection: WhatsAppConnection, detail?: string): void {\n state.connection = connection\n state.detail = detail\n state.statusUpdatedAt = new Date().toISOString()\n if (connection === \"open\") state.qr = null\n}\n\nexport function getWhatsAppState(): WhatsAppState {\n return { ...state }\n}\n"],"mappings":"4BAqBA,IAAMA,EAAuB,CAC3B,WAAY,OACZ,GAAI,IACN,EAEO,SAASC,EAAcC,EAAyB,CACrDF,EAAM,GAAKE,EACXF,EAAM,YAAc,IAAI,KAAK,EAAE,YAAY,CAC7C,CAEO,SAASG,EAAkBC,EAAgCC,EAAuB,CACvFL,EAAM,WAAaI,EACnBJ,EAAM,OAASK,EACfL,EAAM,gBAAkB,IAAI,KAAK,EAAE,YAAY,EAC3CI,IAAe,SAAQJ,EAAM,GAAK,KACxC,CAEO,SAASM,GAAkC,CAChD,MAAO,CAAE,GAAGN,CAAM,CACpB","names":["state","setWhatsAppQR","qr","setWhatsAppStatus","connection","detail","getWhatsAppState"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentix-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Self-hosted multi-agent orchestrator — routes Telegram, WhatsApp, crons, and A2A mesh tasks to Claude Code, OpenAI, or any LLM provider",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -55,6 +55,9 @@
|
|
|
55
55
|
"eval": "vitest run test/eval/",
|
|
56
56
|
"eval:track": "TRACK_SCORES=1 vitest run test/eval/real-data-eval.test.ts",
|
|
57
57
|
"usage:track": "tsx src/scripts/track-usage.ts",
|
|
58
|
+
"docs:dev": "vitepress dev docs",
|
|
59
|
+
"docs:build": "vitepress build docs",
|
|
60
|
+
"docs:preview": "vitepress preview docs",
|
|
58
61
|
"prepublishOnly": "npm run build"
|
|
59
62
|
},
|
|
60
63
|
"dependencies": {
|
|
@@ -62,24 +65,30 @@
|
|
|
62
65
|
"chalk": "5.2.0",
|
|
63
66
|
"commander": "^10.0.0",
|
|
64
67
|
"cosmiconfig": "^8.1.3",
|
|
68
|
+
"cronstrue": "^3.14.0",
|
|
65
69
|
"execa": "^7.0.0",
|
|
66
70
|
"fast-glob": "^3.3.2",
|
|
67
71
|
"fs-extra": "^11.2.0",
|
|
68
72
|
"node-fetch": "^3.3.0",
|
|
69
73
|
"ora": "^6.1.2",
|
|
70
74
|
"prompts": "^2.4.2",
|
|
75
|
+
"qrcode": "^1.5.4",
|
|
71
76
|
"qrcode-terminal": "^0.12.0",
|
|
72
77
|
"zod": "^3.20.2"
|
|
73
78
|
},
|
|
74
79
|
"devDependencies": {
|
|
75
80
|
"@types/fs-extra": "^11.0.1",
|
|
76
81
|
"@types/prompts": "^2.4.2",
|
|
82
|
+
"lucide-static": "^1.8.0",
|
|
83
|
+
"mermaid": "^11.14.0",
|
|
77
84
|
"prettier": "^3.0.0",
|
|
78
85
|
"rimraf": "^4.1.3",
|
|
79
86
|
"tsup": "^6.6.3",
|
|
80
87
|
"type-fest": "^3.8.0",
|
|
81
88
|
"typescript": "^4.9.3",
|
|
82
89
|
"vite-tsconfig-paths": "^4.3.1",
|
|
90
|
+
"vitepress": "^1.6.4",
|
|
91
|
+
"vitepress-plugin-mermaid": "^2.0.17",
|
|
83
92
|
"vitest": "^1.2.2"
|
|
84
93
|
},
|
|
85
94
|
"engines": {
|
package/dist/agent-5KIMJI3F.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{s as a,t as b,u as c}from"./chunk-D6EDMQRV.js";import"./chunk-SFQUP3BP.js";import"./chunk-CJ45Y2IR.js";import"./chunk-KXZMYLHQ.js";import"./chunk-M7HKBG3V.js";import"./chunk-MCJUAE6V.js";import"./chunk-DSYKYZMT.js";import"./chunk-QOYAX2FT.js";import"./chunk-4YCH6IZV.js";export{a as createAgentContext,b as generate,c as generateStream};
|
|
2
|
-
//# sourceMappingURL=agent-5KIMJI3F.js.map
|
package/dist/chunk-34UAFPK4.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import{readFileSync as R,writeFileSync as D,appendFileSync as S,existsSync as C,mkdirSync as _}from"fs";import{resolve as T}from"path";var y={"claude-opus":{input:15,output:75,cacheRead:1.5,cacheCreate:18.75},"claude-sonnet":{input:3,output:15,cacheRead:.3,cacheCreate:3.75},"claude-haiku":{input:.25,output:1.25,cacheRead:.025,cacheCreate:.3125}};function v(O){let t=O.toLowerCase();return t.includes("opus")?"claude-opus":t.includes("haiku")?"claude-haiku":"claude-sonnet"}var b=class{dir;cache=null;constructor(t=T(process.cwd(),".agentx/usage")){this.dir=t,_(this.dir,{recursive:!0})}static calculateCost(t){let o=v(t.model||"claude-sonnet"),n=y[o]||y["claude-sonnet"];return(t.inputTokens||0)/1e6*n.input+(t.outputTokens||0)/1e6*n.output+(t.cacheReadTokens||0)/1e6*n.cacheRead+(t.cacheCreateTokens||0)/1e6*n.cacheCreate}record(t,o,n,c,s,i,l){let e=this.today(),a=e.agents[t]||{tasks:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreateTokens:0,totalDuration:0,errors:0};a.tasks++,a.totalDuration+=o,l&&(a.model=l),n?(a.inputTokens+=n.inputTokens,a.outputTokens+=n.outputTokens,a.cacheReadTokens+=n.cacheReadTokens,a.cacheCreateTokens+=n.cacheCreateTokens):c!==void 0&&s!==void 0&&(a.inputTokens+=Math.ceil(c/4),a.outputTokens+=Math.ceil(s/4)),i&&a.errors++,e.agents[t]=a,this.save(e)}today(){let t=new Date().toISOString().slice(0,10);if(this.cache?.date===t)return this.cache;let o=this.filePath(t);if(C(o))try{return this.cache=JSON.parse(R(o,"utf-8")),this.cache}catch{}return this.cache={date:t,agents:{}},this.cache}getDate(t){let o=this.filePath(t);if(!C(o))return null;try{return JSON.parse(R(o,"utf-8"))}catch{return null}}generateDailyReport(t){let o=t||new Date().toISOString().slice(0,10),n=this.getDate(o);if(!n)return null;let c=0,s=0,i=0,l=0,e=0,a=0,g="",m=0,p={};for(let[k,h]of Object.entries(n.agents)){let d=b.calculateCost(h);p[k]=d,c+=h.tasks,s+=h.inputTokens||0,i+=h.outputTokens||0,l+=h.cacheReadTokens||0,e+=h.cacheCreateTokens||0,a+=d,d>m&&(m=d,g=k)}return{date:o,totalTasks:c,totalInput:s,totalOutput:i,totalCacheRead:l,totalCacheCreate:e,totalCost:a,topAgent:g,topCost:m,agentCosts:p}}appendToTokenCosts(t,o){let n=o||T(process.cwd(),".agentx/TOKEN_COSTS.md"),c=e=>e<.01?e.toFixed(4):e.toFixed(2),s=e=>e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(0)}K`:String(e),i=Object.entries(t.agentCosts).sort(([,e],[,a])=>a-e).map(([e,a])=>`${e}: $${c(a)}`).join(", "),l=`| ${t.date} | ${t.totalTasks} | ${s(t.totalInput)} | ${s(t.totalOutput)} | ${s(t.totalCacheRead)} | ${s(t.totalCacheCreate)} | $${c(t.totalCost)} | ${i} |`;C(n)?S(n,l+`
|
|
2
|
-
`):D(n,`# Token Costs
|
|
3
|
-
|
|
4
|
-
Daily token cost tracking. Auto-appended at midnight (Africa/Tunis).
|
|
5
|
-
|
|
6
|
-
| Date | Tasks | Input | Output | Cache R | Cache W | Cost | Per Agent |
|
|
7
|
-
|------|-------|-------|--------|---------|---------|------|-----------|
|
|
8
|
-
`+l+`
|
|
9
|
-
`)}summary(t=7){let o=0,n=0,c=0,s=0,i=0,l=0,e=0,a={};for(let p=0;p<t;p++){let k=new Date(Date.now()-p*864e5).toISOString().slice(0,10),h=this.getDate(k);if(h)for(let[d,u]of Object.entries(h.agents)){let f=b.calculateCost(u);o+=u.tasks,n+=u.inputTokens||0,c+=u.outputTokens||0,s+=u.cacheReadTokens||0,i+=u.cacheCreateTokens||0,l+=u.errors,e+=f;let r=a[d]||{tasks:0,input:0,output:0,cacheRead:0,cacheCreate:0,total:0,avgDuration:0,totalDuration:0,cost:0};r.tasks+=u.tasks,r.input+=u.inputTokens||0,r.output+=u.outputTokens||0,r.cacheRead+=u.cacheReadTokens||0,r.cacheCreate+=u.cacheCreateTokens||0,r.total=r.input+r.output+r.cacheRead+r.cacheCreate,r.totalDuration+=u.totalDuration,r.avgDuration=r.totalDuration/r.tasks,r.cost+=f,u.model&&(r.model=u.model),a[d]=r}}let g=n+c+s+i,m=s+i>0?s/(s+i):0;return{totalTasks:o,totalTokens:g,totalInput:n,totalOutput:c,totalCacheRead:s,totalCacheCreate:i,cacheHitRatio:m,totalErrors:l,totalCost:e,byAgent:a}}filePath(t){return T(this.dir,`${t}.json`)}save(t){try{D(this.filePath(t.date),JSON.stringify(t,null,2))}catch{}}};export{b as a};
|
|
10
|
-
//# sourceMappingURL=chunk-34UAFPK4.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/daemon/token-tracker.ts"],"sourcesContent":["import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// --- Token usage tracker ---\n// Tracks token usage per agent per day with cache-aware cost calculation.\n// Persisted to .agentx/usage/{YYYY-MM-DD}.json\n\n// Anthropic pricing per million tokens (as of April 2026)\nexport const CACHE_AWARE_PRICING: Record<string, { input: number; output: number; cacheRead: number; cacheCreate: number }> = {\n \"claude-opus\": { input: 15, output: 75, cacheRead: 1.5, cacheCreate: 18.75 },\n \"claude-sonnet\": { input: 3, output: 15, cacheRead: 0.3, cacheCreate: 3.75 },\n \"claude-haiku\": { input: 0.25, output: 1.25, cacheRead: 0.025, cacheCreate: 0.3125 },\n}\n\nexport function getModelFamily(model: string): string {\n const lower = model.toLowerCase()\n if (lower.includes(\"opus\")) return \"claude-opus\"\n if (lower.includes(\"haiku\")) return \"claude-haiku\"\n return \"claude-sonnet\"\n}\n\nexport interface DailyUsage {\n date: string\n agents: Record<string, AgentUsage>\n}\n\nexport interface AgentUsage {\n tasks: number\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheCreateTokens: number\n totalDuration: number\n errors: number\n model?: string\n}\n\nexport interface DailyReport {\n date: string\n totalTasks: number\n totalInput: number\n totalOutput: number\n totalCacheRead: number\n totalCacheCreate: number\n totalCost: number\n topAgent: string\n topCost: number\n agentCosts: Record<string, number>\n}\n\nexport class TokenTracker {\n private dir: string\n private cache: DailyUsage | null = null\n\n constructor(baseDir: string = resolve(process.cwd(), \".agentx/usage\")) {\n this.dir = baseDir\n mkdirSync(this.dir, { recursive: true })\n }\n\n /**\n * Calculate cost for an agent's usage with cache-aware pricing.\n */\n static calculateCost(usage: AgentUsage): number {\n const family = getModelFamily(usage.model || \"claude-sonnet\")\n const pricing = CACHE_AWARE_PRICING[family] || CACHE_AWARE_PRICING[\"claude-sonnet\"]\n\n return (\n ((usage.inputTokens || 0) / 1_000_000) * pricing.input +\n ((usage.outputTokens || 0) / 1_000_000) * pricing.output +\n ((usage.cacheReadTokens || 0) / 1_000_000) * pricing.cacheRead +\n ((usage.cacheCreateTokens || 0) / 1_000_000) * pricing.cacheCreate\n )\n }\n\n /**\n * Record a task execution with real or estimated token counts.\n */\n record(\n agentId: string,\n duration: number,\n realUsage?: { inputTokens: number; outputTokens: number; cacheReadTokens: number; cacheCreateTokens: number },\n messageLength?: number,\n responseLength?: number,\n error?: boolean,\n model?: string,\n ): void {\n const daily = this.today()\n const agent = daily.agents[agentId] || {\n tasks: 0, inputTokens: 0, outputTokens: 0,\n cacheReadTokens: 0, cacheCreateTokens: 0, totalDuration: 0, errors: 0,\n }\n\n agent.tasks++\n agent.totalDuration += duration\n if (model) agent.model = model\n\n if (realUsage) {\n agent.inputTokens += realUsage.inputTokens\n agent.outputTokens += realUsage.outputTokens\n agent.cacheReadTokens += realUsage.cacheReadTokens\n agent.cacheCreateTokens += realUsage.cacheCreateTokens\n } else if (messageLength !== undefined && responseLength !== undefined) {\n agent.inputTokens += Math.ceil(messageLength / 4)\n agent.outputTokens += Math.ceil(responseLength / 4)\n }\n\n if (error) agent.errors++\n\n daily.agents[agentId] = agent\n this.save(daily)\n }\n\n /**\n * Get today's usage summary.\n */\n today(): DailyUsage {\n const date = new Date().toISOString().slice(0, 10)\n\n if (this.cache?.date === date) return this.cache\n\n const path = this.filePath(date)\n if (existsSync(path)) {\n try {\n this.cache = JSON.parse(readFileSync(path, \"utf-8\"))\n return this.cache!\n } catch {}\n }\n\n this.cache = { date, agents: {} }\n return this.cache\n }\n\n /**\n * Get usage for a specific date.\n */\n getDate(date: string): DailyUsage | null {\n const path = this.filePath(date)\n if (!existsSync(path)) return null\n try {\n return JSON.parse(readFileSync(path, \"utf-8\"))\n } catch {\n return null\n }\n }\n\n /**\n * Generate a daily cost report for a given date.\n */\n generateDailyReport(date?: string): DailyReport | null {\n const targetDate = date || new Date().toISOString().slice(0, 10)\n const usage = this.getDate(targetDate)\n if (!usage) return null\n\n let totalTasks = 0, totalInput = 0, totalOutput = 0\n let totalCacheRead = 0, totalCacheCreate = 0, totalCost = 0\n let topAgent = \"\", topCost = 0\n const agentCosts: Record<string, number> = {}\n\n for (const [id, agent] of Object.entries(usage.agents)) {\n const cost = TokenTracker.calculateCost(agent)\n agentCosts[id] = cost\n totalTasks += agent.tasks\n totalInput += agent.inputTokens || 0\n totalOutput += agent.outputTokens || 0\n totalCacheRead += agent.cacheReadTokens || 0\n totalCacheCreate += agent.cacheCreateTokens || 0\n totalCost += cost\n if (cost > topCost) { topCost = cost; topAgent = id }\n }\n\n return {\n date: targetDate, totalTasks, totalInput, totalOutput,\n totalCacheRead, totalCacheCreate, totalCost, topAgent, topCost, agentCosts,\n }\n }\n\n /**\n * Append a daily report row to TOKEN_COSTS.md.\n */\n appendToTokenCosts(report: DailyReport, filePath?: string): void {\n const file = filePath || resolve(process.cwd(), \".agentx/TOKEN_COSTS.md\")\n const fmt = (n: number) => n < 0.01 ? n.toFixed(4) : n.toFixed(2)\n const fmtTok = (n: number) =>\n n >= 1_000_000 ? `${(n / 1_000_000).toFixed(1)}M` :\n n >= 1_000 ? `${(n / 1_000).toFixed(0)}K` :\n String(n)\n\n const agentBreakdown = Object.entries(report.agentCosts)\n .sort(([, a], [, b]) => b - a)\n .map(([id, cost]) => `${id}: $${fmt(cost)}`)\n .join(\", \")\n\n const row = `| ${report.date} | ${report.totalTasks} | ${fmtTok(report.totalInput)} | ${fmtTok(report.totalOutput)} | ${fmtTok(report.totalCacheRead)} | ${fmtTok(report.totalCacheCreate)} | $${fmt(report.totalCost)} | ${agentBreakdown} |`\n\n if (!existsSync(file)) {\n const header =\n \"# Token Costs\\n\\n\" +\n \"Daily token cost tracking. Auto-appended at midnight (Africa/Tunis).\\n\\n\" +\n \"| Date | Tasks | Input | Output | Cache R | Cache W | Cost | Per Agent |\\n\" +\n \"|------|-------|-------|--------|---------|---------|------|-----------|\\n\"\n writeFileSync(file, header + row + \"\\n\")\n } else {\n appendFileSync(file, row + \"\\n\")\n }\n }\n\n /**\n * Get summary across last N days.\n */\n summary(days: number = 7): {\n totalTasks: number\n totalTokens: number\n totalInput: number\n totalOutput: number\n totalCacheRead: number\n totalCacheCreate: number\n cacheHitRatio: number\n totalErrors: number\n totalCost: number\n byAgent: Record<string, { tasks: number; input: number; output: number; cacheRead: number; cacheCreate: number; total: number; avgDuration: number; cost: number }>\n } {\n let totalTasks = 0\n let totalInput = 0\n let totalOutput = 0\n let totalCacheRead = 0\n let totalCacheCreate = 0\n let totalErrors = 0\n let totalCost = 0\n const byAgent: Record<string, { tasks: number; input: number; output: number; cacheRead: number; cacheCreate: number; total: number; avgDuration: number; totalDuration: number; cost: number; model?: string }> = {}\n\n for (let i = 0; i < days; i++) {\n const date = new Date(Date.now() - i * 86400_000).toISOString().slice(0, 10)\n const usage = this.getDate(date)\n if (!usage) continue\n\n for (const [id, agent] of Object.entries(usage.agents)) {\n const cost = TokenTracker.calculateCost(agent)\n totalTasks += agent.tasks\n totalInput += agent.inputTokens || 0\n totalOutput += agent.outputTokens || 0\n totalCacheRead += agent.cacheReadTokens || 0\n totalCacheCreate += agent.cacheCreateTokens || 0\n totalErrors += agent.errors\n totalCost += cost\n\n const existing = byAgent[id] || { tasks: 0, input: 0, output: 0, cacheRead: 0, cacheCreate: 0, total: 0, avgDuration: 0, totalDuration: 0, cost: 0 }\n existing.tasks += agent.tasks\n existing.input += agent.inputTokens || 0\n existing.output += agent.outputTokens || 0\n existing.cacheRead += agent.cacheReadTokens || 0\n existing.cacheCreate += agent.cacheCreateTokens || 0\n existing.total = existing.input + existing.output + existing.cacheRead + existing.cacheCreate\n existing.totalDuration += agent.totalDuration\n existing.avgDuration = existing.totalDuration / existing.tasks\n existing.cost += cost\n if (agent.model) existing.model = agent.model\n byAgent[id] = existing\n }\n }\n\n const totalTokens = totalInput + totalOutput + totalCacheRead + totalCacheCreate\n const cacheHitRatio = (totalCacheRead + totalCacheCreate) > 0\n ? totalCacheRead / (totalCacheRead + totalCacheCreate)\n : 0\n\n return { totalTasks, totalTokens, totalInput, totalOutput, totalCacheRead, totalCacheCreate, cacheHitRatio, totalErrors, totalCost, byAgent }\n }\n\n private filePath(date: string): string {\n return resolve(this.dir, `${date}.json`)\n }\n\n private save(usage: DailyUsage): void {\n try {\n writeFileSync(this.filePath(usage.date), JSON.stringify(usage, null, 2))\n } catch {}\n }\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","appendFileSync","existsSync","mkdirSync","resolve","CACHE_AWARE_PRICING","getModelFamily","model","lower","TokenTracker","baseDir","usage","family","pricing","agentId","duration","realUsage","messageLength","responseLength","error","daily","agent","date","path","targetDate","totalTasks","totalInput","totalOutput","totalCacheRead","totalCacheCreate","totalCost","topAgent","topCost","agentCosts","id","cost","report","filePath","file","fmt","n","fmtTok","agentBreakdown","a","b","row","days","totalErrors","byAgent","i","existing","totalTokens","cacheHitRatio"]}
|
package/dist/chunk-4YCH6IZV.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
var d=Object.defineProperty;var e=(a,b,c)=>b in a?d(a,b,{enumerable:!0,configurable:!0,writable:!0,value:c}):a[b]=c;var f=(a=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(a,{get:(b,c)=>(typeof require<"u"?require:b)[c]}):a)(function(a){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+a+'" is not supported')});var g=(a,b,c)=>(e(a,typeof b!="symbol"?b+"":b,c),c);export{f as a,g as b};
|
|
2
|
-
//# sourceMappingURL=chunk-4YCH6IZV.js.map
|
package/dist/chunk-CJ45Y2IR.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import{a as c,b,d as w}from"./chunk-M7HKBG3V.js";import{e as m}from"./chunk-DSYKYZMT.js";import{z as l}from"zod";var d=["pre:generate","post:generate","pre:file-write","post:file-write","pre:prompt","post:response","pre:command","on:error","pre:tool-call","post:tool-call","pre:channel-message","post:channel-message","pre:a2a-task","pre:cron-run","post:cron-run"],H=["pre:generate","pre:file-write","pre:prompt","post:response","pre:command","pre:tool-call","pre:channel-message","post:channel-message","pre:a2a-task","pre:cron-run"],v=l.object({name:l.string(),type:l.enum(["command","prompt","script"]),command:l.string().optional(),prompt:l.string().optional(),provider:l.enum(["claude-code","claude","openai","ollama","custom"]).optional(),model:l.string().optional(),script:l.string().optional(),priority:l.number().default(100),enabled:l.boolean().default(!0)});var x={"claude-sonnet":{input:3,output:15},"claude-opus":{input:15,output:75},"claude-haiku":{input:.25,output:1.25}};function T(i){let o=i.toLowerCase();return o.includes("opus")?"claude-opus":o.includes("haiku")?"claude-haiku":"claude-sonnet"}var h=class{steps=[];models=new Map;recordStep(o,e,t,r){let n=T(e),s=x[n]||x["claude-sonnet"],p=t/1e6*s.input+r/1e6*s.output;this.steps.push({step:o,model:e,inputTokens:t,outputTokens:r,cost:p,timestamp:Date.now()});let a=this.models.get(e)||{inputTokens:0,outputTokens:0,cost:0,steps:0};a.inputTokens+=t,a.outputTokens+=r,a.cost+=p,a.steps++,this.models.set(e,a)}getSummary(){let o=0,e=0,t=0;for(let n of this.steps)o+=n.inputTokens,e+=n.outputTokens,t+=n.cost;let r={};for(let[n,s]of this.models)r[n]={...s};return{totalInputTokens:o,totalOutputTokens:e,totalTokens:o+e,totalCost:t,steps:[...this.steps],models:r}}reset(){this.steps=[],this.models.clear()}},E=new h;function C(i,o){let e=[];if(e.push(`# Session: ${i.id}`),e.push(""),e.push(`- **Created**: ${i.createdAt}`),e.push(`- **Updated**: ${i.updatedAt}`),e.push(`- **CWD**: ${i.cwd}`),e.push(`- **Messages**: ${i.messages.length}`),e.push(`- **Tokens**: ${i.tokensUsed.toLocaleString()}`),e.push(""),i.filesGenerated.length){e.push("## Files Generated"),e.push("");for(let t of i.filesGenerated)e.push(`- \`${t}\``);e.push("")}if(i.messages.length){e.push("## Conversation"),e.push("");for(let t of i.messages){if(t.role==="system")continue;let r=t.role==="user"?"**User**":"**Assistant**";e.push(`### ${r}`),e.push(""),t.role,e.push(t.content),e.push("")}}if(o){if(e.push("## Cost Summary"),e.push(""),e.push(`- **Total tokens**: ${o.totalTokens.toLocaleString()}`),e.push(` - Input: ${o.totalInputTokens.toLocaleString()}`),e.push(` - Output: ${o.totalOutputTokens.toLocaleString()}`),e.push(`- **Total cost**: $${o.totalCost.toFixed(4)}`),e.push(""),Object.keys(o.models).length){e.push("### Per Model"),e.push(""),e.push("| Model | Input | Output | Cost | Steps |"),e.push("|-------|-------|--------|------|-------|");for(let[t,r]of Object.entries(o.models))e.push(`| ${t} | ${r.inputTokens.toLocaleString()} | ${r.outputTokens.toLocaleString()} | $${r.cost.toFixed(4)} | ${r.steps} |`);e.push("")}if(o.steps.length){e.push("### Per Step"),e.push(""),e.push("| Step | Model | Input | Output | Cost |"),e.push("|------|-------|-------|--------|------|");for(let t of o.steps)e.push(`| ${t.step} | ${t.model} | ${t.inputTokens.toLocaleString()} | ${t.outputTokens.toLocaleString()} | $${t.cost.toFixed(4)} |`);e.push("")}}return e.join(`
|
|
2
|
-
`)}import{execa as O}from"execa";var k=class{hooks=new Map;register(o,e){if(!e.enabled)return;let t=this.createHandler(e),r={event:o,definition:e,handler:t},n=this.hooks.get(o)||[];n.push(r),n.sort((s,p)=>s.definition.priority-p.definition.priority),this.hooks.set(o,n)}async execute(o,e){let t=this.hooks.get(o);if(!t?.length)return{};let r=H.includes(o),n={};for(let s of t){let p=Date.now();try{let a=await s.handler({...e,...n}),u=Date.now()-p;if(a.modified&&(n={...n,...a.modified}),r&&a.blocked)return m.hook(s.definition.name,u,"blocked"),{blocked:!0,message:a.message||`Blocked by hook: ${s.definition.name}`,modified:n};m.hook(s.definition.name,u,"ok")}catch(a){let u=Date.now()-p;if(m.hook(s.definition.name,u,`error: ${a.message}`),c.warn(`Hook "${s.definition.name}" failed: ${a.message}`),r)return{blocked:!0,message:`Hook "${s.definition.name}" errored: ${a.message}`}}}return{modified:Object.keys(n).length?n:void 0}}has(o){return!!this.hooks.get(o)?.length}clear(){this.hooks.clear()}createHandler(o){switch(o.type){case"command":return this.createCommandHandler(o);case"script":return this.createScriptHandler(o);case"prompt":return this.createPromptHandler(o);default:throw new Error(`Unknown hook type: ${o.type}`)}}createCommandHandler(o){return async e=>{if(!o.command)return{};let t=o.command;t=t.replace(/\{\{(\w+)\}\}/g,(n,s)=>String(e[s]??""));let r=await O("sh",["-c",t],{cwd:e.cwd||process.cwd(),reject:!1,timeout:6e4,env:process.env});return r.exitCode!==0?{blocked:!0,message:r.stderr||r.stdout||`Command hook "${o.name}" failed`}:{}}}createScriptHandler(o){return async e=>{if(!o.script)return{};let r=await import(o.script.startsWith("/")?o.script:`${e.cwd||process.cwd()}/${o.script}`),n=r.default||r.handler||r;return typeof n=="function"?await n(e):{}}}createPromptHandler(o){return async e=>{if(!o.prompt)return{};let t=o.provider||b()?.provider||"claude-code",r=w();if(!r&&t!=="claude-code")return c.warn(`Prompt hook "${o.name}" skipped: no credentials available for provider "${t}"`),{};try{let{createProvider:n}=await import("./providers-G3WZ4RVJ.js"),s=n(t,r?.token),p=o.prompt;p=p.replace(/\{\{(\w+)\}\}/g,(M,$)=>String(e[$]??""));let a=`You are a hook validator for agentx. Analyze the following context and respond with ONLY a JSON object (no markdown, no code fences).
|
|
3
|
-
|
|
4
|
-
Response format:
|
|
5
|
-
{"action": "allow", "message": "optional explanation"}
|
|
6
|
-
OR
|
|
7
|
-
{"action": "block", "message": "reason for blocking"}
|
|
8
|
-
OR
|
|
9
|
-
{"action": "allow", "message": "optional", "modified": {"key": "new value"}}
|
|
10
|
-
|
|
11
|
-
The "modified" field is optional and can contain updated values to pass forward.`,y=(await s.generate([{role:"system",content:a},{role:"user",content:p}],{model:o.model,maxTokens:1024})).content.trim().match(/\{[\s\S]*\}/);if(!y)return c.warn(`Prompt hook "${o.name}": LLM response was not valid JSON, allowing`),{};let f=JSON.parse(y[0]);return f.action==="block"?{blocked:!0,message:f.message||`Blocked by prompt hook: ${o.name}`}:f.modified?{modified:f.modified}:{}}catch(n){return c.warn(`Prompt hook "${o.name}" failed: ${n.message}`),{}}}}};import{existsSync as S,readFileSync as R,readdirSync as D}from"fs";import g from"path";function U(i,o){j(i,o),N(i,o)}function j(i,o){let e=g.join(i,"agentx.config.json");if(S(e))try{let t=JSON.parse(R(e,"utf8"));if(!t.hooks)return;for(let[r,n]of Object.entries(t.hooks)){if(!d.includes(r)){c.warn(`Unknown hook event in config: ${r}`);continue}let s=r;if(Array.isArray(n))for(let p of n)try{let a=v.parse(p);o.register(s,a)}catch(a){c.warn(`Invalid hook definition for ${s}: ${a.message}`)}}}catch(t){c.warn(`Failed to load hooks from config: ${t.message}`)}}function N(i,o){let e=g.join(i,".agentx","hooks");if(S(e))try{let t=D(e).filter(r=>r.endsWith(".ts")||r.endsWith(".js")||r.endsWith(".mjs"));for(let r of t){let n=r.replace(/\.(ts|js|mjs)$/,"").split(".");if(n.length<2){c.warn(`Hook file "${r}" should be named <event>.<name>.<ext>`);continue}let s=n[0],p=n.slice(1).join("."),a=s.replace(/-/g,":");if(!d.includes(a)){let u=s.replace("-",":");if(!d.includes(u)){c.warn(`Unknown hook event in filename: ${r}`);continue}o.register(u,{name:p,type:"script",script:g.join(e,r),priority:100,enabled:!0});continue}o.register(a,{name:p,type:"script",script:g.join(e,r),priority:100,enabled:!0})}}catch(t){c.warn(`Failed to load hooks from directory: ${t.message}`)}}var se=new k;export{d as a,H as b,h as c,E as d,C as e,k as f,U as g,se as h};
|
|
12
|
-
//# sourceMappingURL=chunk-CJ45Y2IR.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/types.ts","../src/observability/tracker.ts","../src/observability/export.ts","../src/hooks/registry.ts","../src/hooks/loader.ts","../src/hooks/index.ts"],"sourcesContent":["import { z } from \"zod\"\n\n// --- Hook system types ---\n\nexport const HOOK_EVENTS = [\n \"pre:generate\",\n \"post:generate\",\n \"pre:file-write\",\n \"post:file-write\",\n \"pre:prompt\",\n \"post:response\",\n \"pre:command\",\n \"on:error\",\n \"pre:tool-call\",\n \"post:tool-call\",\n // Daemon events\n \"pre:channel-message\",\n \"post:channel-message\",\n \"pre:a2a-task\",\n \"pre:cron-run\",\n \"post:cron-run\",\n] as const\n\nexport type HookEvent = (typeof HOOK_EVENTS)[number]\n\n// Which hooks can block (return { blocked: true } to cancel the operation)\nexport const BLOCKING_EVENTS: HookEvent[] = [\n \"pre:generate\",\n \"pre:file-write\",\n \"pre:prompt\",\n \"post:response\",\n \"pre:command\",\n \"pre:tool-call\",\n // Daemon blocking events\n \"pre:channel-message\",\n \"post:channel-message\",\n \"pre:a2a-task\",\n \"pre:cron-run\",\n]\n\nexport type HookType = \"command\" | \"prompt\" | \"script\"\n\nexport const hookDefinitionSchema = z.object({\n name: z.string(),\n type: z.enum([\"command\", \"prompt\", \"script\"]),\n // For \"command\" type: shell command with {{variable}} interpolation\n command: z.string().optional(),\n // For \"prompt\" type: LLM prompt template\n prompt: z.string().optional(),\n // For \"prompt\" type: override provider/model for this hook\n provider: z.enum([\"claude-code\", \"claude\", \"openai\", \"ollama\", \"custom\"]).optional(),\n model: z.string().optional(),\n // For \"script\" type: path to JS/TS file exporting a handler\n script: z.string().optional(),\n // Hook priority (lower runs first, default 100)\n priority: z.number().default(100),\n // Whether this hook is enabled\n enabled: z.boolean().default(true),\n})\n\nexport type HookDefinition = z.infer<typeof hookDefinitionSchema>\n\nexport interface HookContext {\n event: HookEvent\n // Data varies by event\n task?: string\n file?: string\n fileContent?: string\n content?: string\n command?: string\n error?: Error\n cwd?: string\n [key: string]: unknown\n}\n\nexport interface HookResult {\n // If true, the operation is blocked/cancelled\n blocked?: boolean\n // Optional message explaining why it was blocked\n message?: string\n // Modified data to pass forward (e.g., modified file content)\n modified?: Record<string, unknown>\n}\n\nexport type HookHandler = (context: HookContext) => Promise<HookResult>\n","// --- Usage tracking: tokens, cost, per-step and per-model breakdown ---\n\nexport interface StepUsage {\n step: number\n model: string\n inputTokens: number\n outputTokens: number\n cost: number\n timestamp: number\n}\n\nexport interface ModelUsage {\n inputTokens: number\n outputTokens: number\n cost: number\n steps: number\n}\n\nexport interface UsageSummary {\n totalInputTokens: number\n totalOutputTokens: number\n totalTokens: number\n totalCost: number\n steps: StepUsage[]\n models: Record<string, ModelUsage>\n}\n\n// Cost per million tokens (input / output)\nconst MODEL_PRICING: Record<string, { input: number; output: number }> = {\n \"claude-sonnet\": { input: 3, output: 15 },\n \"claude-opus\": { input: 15, output: 75 },\n \"claude-haiku\": { input: 0.25, output: 1.25 },\n}\n\nfunction getModelFamily(model: string): string {\n const lower = model.toLowerCase()\n if (lower.includes(\"opus\")) return \"claude-opus\"\n if (lower.includes(\"haiku\")) return \"claude-haiku\"\n return \"claude-sonnet\" // default\n}\n\nexport class UsageTracker {\n private steps: StepUsage[] = []\n private models: Map<string, ModelUsage> = new Map()\n\n recordStep(\n step: number,\n model: string,\n inputTokens: number,\n outputTokens: number\n ): void {\n const family = getModelFamily(model)\n const pricing = MODEL_PRICING[family] || MODEL_PRICING[\"claude-sonnet\"]\n const cost =\n (inputTokens / 1_000_000) * pricing.input +\n (outputTokens / 1_000_000) * pricing.output\n\n this.steps.push({\n step,\n model,\n inputTokens,\n outputTokens,\n cost,\n timestamp: Date.now(),\n })\n\n const existing = this.models.get(model) || {\n inputTokens: 0,\n outputTokens: 0,\n cost: 0,\n steps: 0,\n }\n existing.inputTokens += inputTokens\n existing.outputTokens += outputTokens\n existing.cost += cost\n existing.steps++\n this.models.set(model, existing)\n }\n\n getSummary(): UsageSummary {\n let totalInputTokens = 0\n let totalOutputTokens = 0\n let totalCost = 0\n\n for (const step of this.steps) {\n totalInputTokens += step.inputTokens\n totalOutputTokens += step.outputTokens\n totalCost += step.cost\n }\n\n const models: Record<string, ModelUsage> = {}\n for (const [name, usage] of this.models) {\n models[name] = { ...usage }\n }\n\n return {\n totalInputTokens,\n totalOutputTokens,\n totalTokens: totalInputTokens + totalOutputTokens,\n totalCost,\n steps: [...this.steps],\n models,\n }\n }\n\n reset(): void {\n this.steps = []\n this.models.clear()\n }\n}\n\nexport const globalTracker = new UsageTracker()\n","// --- Session export: convert a session to readable markdown ---\n\nimport type { Session } from \"@/repl/session\"\nimport type { UsageSummary } from \"./tracker\"\n\nexport function exportSession(session: Session, usage?: UsageSummary): string {\n const lines: string[] = []\n\n // Header\n lines.push(`# Session: ${session.id}`)\n lines.push(\"\")\n lines.push(`- **Created**: ${session.createdAt}`)\n lines.push(`- **Updated**: ${session.updatedAt}`)\n lines.push(`- **CWD**: ${session.cwd}`)\n lines.push(`- **Messages**: ${session.messages.length}`)\n lines.push(`- **Tokens**: ${session.tokensUsed.toLocaleString()}`)\n lines.push(\"\")\n\n // Files generated\n if (session.filesGenerated.length) {\n lines.push(\"## Files Generated\")\n lines.push(\"\")\n for (const file of session.filesGenerated) {\n lines.push(`- \\`${file}\\``)\n }\n lines.push(\"\")\n }\n\n // Conversation\n if (session.messages.length) {\n lines.push(\"## Conversation\")\n lines.push(\"\")\n for (const msg of session.messages) {\n if (msg.role === \"system\") continue\n const label = msg.role === \"user\" ? \"**User**\" : \"**Assistant**\"\n lines.push(`### ${label}`)\n lines.push(\"\")\n if (msg.role === \"assistant\") {\n lines.push(msg.content)\n } else {\n lines.push(msg.content)\n }\n lines.push(\"\")\n }\n }\n\n // Cost summary\n if (usage) {\n lines.push(\"## Cost Summary\")\n lines.push(\"\")\n lines.push(`- **Total tokens**: ${usage.totalTokens.toLocaleString()}`)\n lines.push(` - Input: ${usage.totalInputTokens.toLocaleString()}`)\n lines.push(` - Output: ${usage.totalOutputTokens.toLocaleString()}`)\n lines.push(`- **Total cost**: $${usage.totalCost.toFixed(4)}`)\n lines.push(\"\")\n\n if (Object.keys(usage.models).length) {\n lines.push(\"### Per Model\")\n lines.push(\"\")\n lines.push(\"| Model | Input | Output | Cost | Steps |\")\n lines.push(\"|-------|-------|--------|------|-------|\")\n for (const [model, mu] of Object.entries(usage.models)) {\n lines.push(\n `| ${model} | ${mu.inputTokens.toLocaleString()} | ${mu.outputTokens.toLocaleString()} | $${mu.cost.toFixed(4)} | ${mu.steps} |`\n )\n }\n lines.push(\"\")\n }\n\n if (usage.steps.length) {\n lines.push(\"### Per Step\")\n lines.push(\"\")\n lines.push(\"| Step | Model | Input | Output | Cost |\")\n lines.push(\"|------|-------|-------|--------|------|\")\n for (const step of usage.steps) {\n lines.push(\n `| ${step.step} | ${step.model} | ${step.inputTokens.toLocaleString()} | ${step.outputTokens.toLocaleString()} | $${step.cost.toFixed(4)} |`\n )\n }\n lines.push(\"\")\n }\n }\n\n return lines.join(\"\\n\")\n}\n","import { execa } from \"execa\"\nimport type { HookEvent, HookDefinition, HookContext, HookResult, HookHandler } from \"./types\"\nimport { BLOCKING_EVENTS } from \"./types\"\nimport { logger } from \"@/utils/logger\"\nimport { debug } from \"@/observability\"\nimport { loadAuthConfig, resolveToken } from \"@/utils/auth-store\"\n\n// --- Hook Registry: registration, priority ordering, execution ---\n\ninterface RegisteredHook {\n event: HookEvent\n definition: HookDefinition\n handler: HookHandler\n}\n\nexport class HookRegistry {\n private hooks: Map<HookEvent, RegisteredHook[]> = new Map()\n\n /**\n * Register a hook for an event.\n */\n register(event: HookEvent, definition: HookDefinition): void {\n if (!definition.enabled) return\n\n const handler = this.createHandler(definition)\n const entry: RegisteredHook = { event, definition, handler }\n\n const existing = this.hooks.get(event) || []\n existing.push(entry)\n // Sort by priority (lower number = runs first)\n existing.sort((a, b) => a.definition.priority - b.definition.priority)\n this.hooks.set(event, existing)\n }\n\n /**\n * Execute all hooks for an event. Returns combined result.\n * For blocking events, stops at the first hook that returns { blocked: true }.\n */\n async execute(event: HookEvent, context: HookContext): Promise<HookResult> {\n const registered = this.hooks.get(event)\n if (!registered?.length) {\n return {}\n }\n\n const canBlock = BLOCKING_EVENTS.includes(event)\n let combinedModified: Record<string, unknown> = {}\n\n for (const hook of registered) {\n const start = Date.now()\n try {\n const result = await hook.handler({ ...context, ...combinedModified })\n const duration = Date.now() - start\n\n if (result.modified) {\n combinedModified = { ...combinedModified, ...result.modified }\n }\n\n if (canBlock && result.blocked) {\n debug.hook(hook.definition.name, duration, \"blocked\")\n return {\n blocked: true,\n message: result.message || `Blocked by hook: ${hook.definition.name}`,\n modified: combinedModified,\n }\n }\n\n debug.hook(hook.definition.name, duration, \"ok\")\n } catch (error: any) {\n const duration = Date.now() - start\n debug.hook(hook.definition.name, duration, `error: ${error.message}`)\n logger.warn(`Hook \"${hook.definition.name}\" failed: ${error.message}`)\n // Non-blocking hooks swallow errors; blocking hooks propagate\n if (canBlock) {\n return {\n blocked: true,\n message: `Hook \"${hook.definition.name}\" errored: ${error.message}`,\n }\n }\n }\n }\n\n return { modified: Object.keys(combinedModified).length ? combinedModified : undefined }\n }\n\n /**\n * Check if any hooks are registered for an event.\n */\n has(event: HookEvent): boolean {\n const registered = this.hooks.get(event)\n return !!registered?.length\n }\n\n /**\n * Clear all hooks (useful for testing).\n */\n clear(): void {\n this.hooks.clear()\n }\n\n /**\n * Create a HookHandler from a HookDefinition.\n */\n private createHandler(definition: HookDefinition): HookHandler {\n switch (definition.type) {\n case \"command\":\n return this.createCommandHandler(definition)\n case \"script\":\n return this.createScriptHandler(definition)\n case \"prompt\":\n return this.createPromptHandler(definition)\n default:\n throw new Error(`Unknown hook type: ${definition.type}`)\n }\n }\n\n /**\n * Command hook: runs a shell command with {{variable}} interpolation.\n * Exit code 0 = success, non-zero = blocked (for blocking events).\n */\n private createCommandHandler(definition: HookDefinition): HookHandler {\n return async (context: HookContext): Promise<HookResult> => {\n if (!definition.command) {\n return {}\n }\n\n // Interpolate {{variable}} placeholders\n let cmd = definition.command\n cmd = cmd.replace(/\\{\\{(\\w+)\\}\\}/g, (_match, key) => {\n return String(context[key] ?? \"\")\n })\n\n const result = await execa(\"sh\", [\"-c\", cmd], {\n cwd: context.cwd || process.cwd(),\n reject: false,\n timeout: 60_000,\n env: process.env,\n })\n\n if (result.exitCode !== 0) {\n return {\n blocked: true,\n message: result.stderr || result.stdout || `Command hook \"${definition.name}\" failed`,\n }\n }\n\n return {}\n }\n }\n\n /**\n * Script hook: imports and runs a JS/TS module that exports a handler function.\n */\n private createScriptHandler(definition: HookDefinition): HookHandler {\n return async (context: HookContext): Promise<HookResult> => {\n if (!definition.script) {\n return {}\n }\n\n const scriptPath = definition.script.startsWith(\"/\")\n ? definition.script\n : `${context.cwd || process.cwd()}/${definition.script}`\n\n const mod = await import(scriptPath)\n const handler = mod.default || mod.handler || mod\n\n if (typeof handler === \"function\") {\n return await handler(context)\n }\n\n return {}\n }\n }\n\n /**\n * Prompt hook: single-turn LLM call that can block or modify the operation.\n * The prompt template supports {{variable}} interpolation from hook context.\n * The LLM must respond with JSON: { \"action\": \"allow\"|\"block\", \"message\": \"...\", \"modified\": {...} }\n */\n private createPromptHandler(definition: HookDefinition): HookHandler {\n return async (context: HookContext): Promise<HookResult> => {\n if (!definition.prompt) {\n return {}\n }\n\n // Check credentials are available (don't prompt interactively)\n const providerName = definition.provider || loadAuthConfig()?.provider || \"claude-code\"\n const resolved = resolveToken()\n if (!resolved && providerName !== \"claude-code\") {\n logger.warn(\n `Prompt hook \"${definition.name}\" skipped: no credentials available for provider \"${providerName}\"`\n )\n return {}\n }\n\n try {\n // Lazy import to avoid circular deps\n const { createProvider } = await import(\"@/agent/providers\")\n const provider = createProvider(providerName as any, resolved?.token)\n\n // Interpolate {{variable}} placeholders in the prompt template\n let prompt = definition.prompt\n prompt = prompt.replace(/\\{\\{(\\w+)\\}\\}/g, (_match, key) => {\n return String(context[key] ?? \"\")\n })\n\n const systemPrompt = `You are a hook validator for agentx. Analyze the following context and respond with ONLY a JSON object (no markdown, no code fences).\n\nResponse format:\n{\"action\": \"allow\", \"message\": \"optional explanation\"}\nOR\n{\"action\": \"block\", \"message\": \"reason for blocking\"}\nOR\n{\"action\": \"allow\", \"message\": \"optional\", \"modified\": {\"key\": \"new value\"}}\n\nThe \"modified\" field is optional and can contain updated values to pass forward.`\n\n const result = await provider.generate(\n [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: prompt },\n ],\n {\n model: definition.model,\n maxTokens: 1024,\n }\n )\n\n // Parse the LLM response as JSON\n const content = result.content.trim()\n // Try to extract JSON from the response (handle markdown fences)\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/)\n if (!jsonMatch) {\n logger.warn(`Prompt hook \"${definition.name}\": LLM response was not valid JSON, allowing`)\n return {}\n }\n\n const parsed = JSON.parse(jsonMatch[0])\n\n if (parsed.action === \"block\") {\n return {\n blocked: true,\n message: parsed.message || `Blocked by prompt hook: ${definition.name}`,\n }\n }\n\n if (parsed.modified) {\n return { modified: parsed.modified }\n }\n\n return {}\n } catch (error: any) {\n // Don't block on hook failures — log and continue\n logger.warn(`Prompt hook \"${definition.name}\" failed: ${error.message}`)\n return {}\n }\n }\n }\n}\n","import { existsSync, readFileSync, readdirSync } from \"fs\"\nimport path from \"path\"\nimport type { HookEvent, HookDefinition } from \"./types\"\nimport { HOOK_EVENTS, hookDefinitionSchema } from \"./types\"\nimport { HookRegistry } from \"./registry\"\nimport { logger } from \"@/utils/logger\"\n\n// --- Hook Loader: load hooks from config and .agentx/hooks/ ---\n\ninterface HooksConfig {\n hooks?: Partial<Record<HookEvent, HookDefinition[]>>\n}\n\n/**\n * Load hooks from agentx.config.json and .agentx/hooks/ directory.\n */\nexport function loadHooks(cwd: string, registry: HookRegistry): void {\n loadHooksFromConfig(cwd, registry)\n loadHooksFromDirectory(cwd, registry)\n}\n\n/**\n * Load hook definitions from agentx.config.json.\n */\nfunction loadHooksFromConfig(cwd: string, registry: HookRegistry): void {\n const configPath = path.join(cwd, \"agentx.config.json\")\n if (!existsSync(configPath)) return\n\n try {\n const raw = JSON.parse(readFileSync(configPath, \"utf8\")) as HooksConfig\n if (!raw.hooks) return\n\n for (const [eventName, definitions] of Object.entries(raw.hooks)) {\n if (!HOOK_EVENTS.includes(eventName as HookEvent)) {\n logger.warn(`Unknown hook event in config: ${eventName}`)\n continue\n }\n\n const event = eventName as HookEvent\n if (!Array.isArray(definitions)) continue\n\n for (const def of definitions) {\n try {\n const parsed = hookDefinitionSchema.parse(def)\n registry.register(event, parsed)\n } catch (error: any) {\n logger.warn(`Invalid hook definition for ${event}: ${error.message}`)\n }\n }\n }\n } catch (error: any) {\n logger.warn(`Failed to load hooks from config: ${error.message}`)\n }\n}\n\n/**\n * Load hook scripts from .agentx/hooks/ directory.\n * Files are named <event>.<name>.ts (e.g., pre-file-write.format.ts).\n */\nfunction loadHooksFromDirectory(cwd: string, registry: HookRegistry): void {\n const hooksDir = path.join(cwd, \".agentx\", \"hooks\")\n if (!existsSync(hooksDir)) return\n\n try {\n const files = readdirSync(hooksDir).filter(\n (f) => f.endsWith(\".ts\") || f.endsWith(\".js\") || f.endsWith(\".mjs\")\n )\n\n for (const file of files) {\n // Parse filename: <event>.<name>.<ext>\n // Event uses dashes in filename: pre-file-write → pre:file-write\n const parts = file.replace(/\\.(ts|js|mjs)$/, \"\").split(\".\")\n if (parts.length < 2) {\n logger.warn(`Hook file \"${file}\" should be named <event>.<name>.<ext>`)\n continue\n }\n\n const eventSlug = parts[0]\n const name = parts.slice(1).join(\".\")\n\n // Convert slug to event name: pre-file-write → pre:file-write\n const eventName = eventSlug.replace(/-/g, \":\") as HookEvent\n if (!HOOK_EVENTS.includes(eventName)) {\n // Try replacing first dash only: pre-generate → pre:generate\n const altEvent = eventSlug.replace(\"-\", \":\") as HookEvent\n if (!HOOK_EVENTS.includes(altEvent)) {\n logger.warn(`Unknown hook event in filename: ${file}`)\n continue\n }\n registry.register(altEvent, {\n name,\n type: \"script\",\n script: path.join(hooksDir, file),\n priority: 100,\n enabled: true,\n })\n continue\n }\n\n registry.register(eventName, {\n name,\n type: \"script\",\n script: path.join(hooksDir, file),\n priority: 100,\n enabled: true,\n })\n }\n } catch (error: any) {\n logger.warn(`Failed to load hooks from directory: ${error.message}`)\n }\n}\n","export { HookRegistry } from \"./registry\"\nexport { loadHooks } from \"./loader\"\nexport type { HookEvent, HookDefinition, HookContext, HookResult, HookHandler } from \"./types\"\nexport { HOOK_EVENTS, BLOCKING_EVENTS } from \"./types\"\n\nimport { HookRegistry } from \"./registry\"\n\n// Singleton hook registry for the CLI process\nexport const globalHooks = new HookRegistry()\n"],"mappings":"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","names":["z","HOOK_EVENTS","BLOCKING_EVENTS","hookDefinitionSchema","MODEL_PRICING","getModelFamily","model","lower","UsageTracker","step","inputTokens","outputTokens","family","pricing","cost","existing","totalInputTokens","totalOutputTokens","totalCost","models","name","usage","globalTracker","exportSession","session","usage","lines","file","msg","label","model","mu","step","execa","HookRegistry","event","definition","handler","entry","existing","a","b","context","registered","canBlock","BLOCKING_EVENTS","combinedModified","hook","start","result","duration","debug","error","logger","cmd","_match","key","execa","mod","providerName","loadAuthConfig","resolved","resolveToken","createProvider","provider","prompt","systemPrompt","jsonMatch","parsed","existsSync","readFileSync","readdirSync","path","loadHooks","cwd","registry","loadHooksFromConfig","loadHooksFromDirectory","configPath","path","existsSync","raw","readFileSync","eventName","definitions","HOOK_EVENTS","logger","event","def","parsed","hookDefinitionSchema","error","hooksDir","files","readdirSync","f","file","parts","eventSlug","name","altEvent","globalHooks","HookRegistry"]}
|