agentix-cli 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/dist/agent-RDVGL2TW.js +2 -0
  2. package/dist/audit-Z47AQFFD.js +3 -0
  3. package/dist/audit-Z47AQFFD.js.map +1 -0
  4. package/dist/board-dashboard-KX2CNOSY.js +7282 -0
  5. package/dist/board-dashboard-KX2CNOSY.js.map +1 -0
  6. package/dist/chunk-34NG7UDX.js +11 -0
  7. package/dist/chunk-34NG7UDX.js.map +1 -0
  8. package/dist/{chunk-6MKAXEE5.js → chunk-4ITQP3XL.js} +2 -2
  9. package/dist/{chunk-6MKAXEE5.js.map → chunk-4ITQP3XL.js.map} +1 -1
  10. package/dist/chunk-53NRX2NA.js +2 -0
  11. package/dist/chunk-53NRX2NA.js.map +1 -0
  12. package/dist/chunk-6PPIFDOQ.js +1742 -0
  13. package/dist/chunk-6PPIFDOQ.js.map +1 -0
  14. package/dist/chunk-AFRHKA7B.js +2 -0
  15. package/dist/chunk-AFRHKA7B.js.map +1 -0
  16. package/dist/chunk-ASSJ4Z6Z.js +164 -0
  17. package/dist/chunk-ASSJ4Z6Z.js.map +1 -0
  18. package/dist/chunk-BLUXL6HR.js +854 -0
  19. package/dist/chunk-BLUXL6HR.js.map +1 -0
  20. package/dist/chunk-BTAEYL3P.js +72 -0
  21. package/dist/chunk-BTAEYL3P.js.map +1 -0
  22. package/dist/chunk-CDNWCYF7.js +2 -0
  23. package/dist/chunk-CDNWCYF7.js.map +1 -0
  24. package/dist/chunk-DJEZ2HVU.js +5 -0
  25. package/dist/chunk-DJEZ2HVU.js.map +1 -0
  26. package/dist/{chunk-FLOGK2U7.js → chunk-DSCMCVTV.js} +3 -3
  27. package/dist/chunk-DSCMCVTV.js.map +1 -0
  28. package/dist/chunk-EW27SCCC.js +2 -0
  29. package/dist/chunk-EW27SCCC.js.map +1 -0
  30. package/dist/chunk-HPDHNIA3.js +2 -0
  31. package/dist/chunk-IM376SD6.js +13 -0
  32. package/dist/chunk-IM376SD6.js.map +1 -0
  33. package/dist/chunk-J4WKMKTP.js +5 -0
  34. package/dist/{chunk-YI2T5QYB.js.map → chunk-J4WKMKTP.js.map} +1 -1
  35. package/dist/chunk-JGXE2YNV.js +46 -0
  36. package/dist/chunk-JGXE2YNV.js.map +1 -0
  37. package/dist/chunk-JXEPJ3YD.js +118 -0
  38. package/dist/chunk-JXEPJ3YD.js.map +1 -0
  39. package/dist/chunk-LY7JGDV7.js +6 -0
  40. package/dist/chunk-LY7JGDV7.js.map +1 -0
  41. package/dist/chunk-N67JZAXU.js +2 -0
  42. package/dist/chunk-N67JZAXU.js.map +1 -0
  43. package/dist/chunk-SR3X25TN.js +3 -0
  44. package/dist/chunk-SR3X25TN.js.map +1 -0
  45. package/dist/{chunk-7MBTMCKY.js → chunk-TRQSP67X.js} +2 -2
  46. package/dist/chunk-U364GNQX.js +719 -0
  47. package/dist/chunk-U364GNQX.js.map +1 -0
  48. package/dist/chunk-VJUTQAPJ.js +2 -0
  49. package/dist/chunk-VJUTQAPJ.js.map +1 -0
  50. package/dist/{chunk-HI24KAEY.js → chunk-WJXU5F4Z.js} +3 -4
  51. package/dist/{chunk-HI24KAEY.js.map → chunk-WJXU5F4Z.js.map} +1 -1
  52. package/dist/{chunk-QZP65I2H.js → chunk-ZE2YFX4K.js} +2 -2
  53. package/dist/cli.js +1 -320
  54. package/dist/cli.js.map +1 -1
  55. package/dist/{compaction-JBZZ7I7Y.js → compaction-S5H6DTOR.js} +6 -6
  56. package/dist/compaction-S5H6DTOR.js.map +1 -0
  57. package/dist/config-UOHEG4BW.js +2 -0
  58. package/dist/config-mutator-KSEBWKJH.js +2 -0
  59. package/dist/context-planner-2RGHJHSO.js +17 -0
  60. package/dist/context-planner-2RGHJHSO.js.map +1 -0
  61. package/dist/debug-TGAP7E7W.js +2 -0
  62. package/dist/discover-OATU7SHI.js +4 -0
  63. package/dist/discover-OATU7SHI.js.map +1 -0
  64. package/dist/editor-chat-FUBQLMAQ.js +137 -0
  65. package/dist/editor-chat-FUBQLMAQ.js.map +1 -0
  66. package/dist/heal-YP7QI7D3.js +2 -0
  67. package/dist/inbox-O5UB3YQN.js +315 -0
  68. package/dist/inbox-O5UB3YQN.js.map +1 -0
  69. package/dist/index.d.ts +3390 -1814
  70. package/dist/index.js +23 -53
  71. package/dist/index.js.map +1 -1
  72. package/dist/ingest-whatsapp-C3O5UPBX.js +6 -0
  73. package/dist/ingest-whatsapp-C3O5UPBX.js.map +1 -0
  74. package/dist/loader-BGWA7IRY.js +2 -0
  75. package/dist/loader-LBHKSXBK.js +2 -0
  76. package/dist/memory-extract-KKPKAOPH.js +2 -0
  77. package/dist/processes-7W27G3J3.js +256 -0
  78. package/dist/processes-7W27G3J3.js.map +1 -0
  79. package/dist/program-2MDELQLX.js +2 -0
  80. package/dist/providers-WCQSKTNP.js +2 -0
  81. package/dist/query-FZAL7AQD.js +2 -0
  82. package/dist/recipes-LZ56TA3X.js +2 -0
  83. package/dist/registry-ONJSILH4.js +2 -0
  84. package/dist/scheduler-XRM2RJIV.js +2 -0
  85. package/dist/slack-D27BE37Q.js +3 -0
  86. package/dist/slack-D27BE37Q.js.map +1 -0
  87. package/dist/store-4UCO56K2.js +2 -0
  88. package/dist/store-4UCO56K2.js.map +1 -0
  89. package/dist/{subagent-2SJHSSJS.js → subagent-EPOT3QDM.js} +2 -2
  90. package/dist/task-store-3NUWLYRY.js +2 -0
  91. package/dist/task-store-3NUWLYRY.js.map +1 -0
  92. package/dist/telegram-UWV67KWG.js +3 -0
  93. package/dist/telegram-UWV67KWG.js.map +1 -0
  94. package/dist/timers-QB4KYNAE.js +2 -0
  95. package/dist/timers-QB4KYNAE.js.map +1 -0
  96. package/dist/types-ECMEC5BZ.js +2 -0
  97. package/dist/types-ECMEC5BZ.js.map +1 -0
  98. package/dist/types-TTB6DJUE.js +2 -0
  99. package/dist/types-TTB6DJUE.js.map +1 -0
  100. package/dist/usage-dashboard-FQGPPABD.js +803 -0
  101. package/dist/usage-dashboard-FQGPPABD.js.map +1 -0
  102. package/dist/web/workflow-editor.global.js +1450 -0
  103. package/dist/web/workflow-editor.global.js.map +1 -0
  104. package/dist/whatsapp-KJ2NRKKQ.js +3 -0
  105. package/dist/whatsapp-KJ2NRKKQ.js.map +1 -0
  106. package/dist/{whatsapp-state-6D6FHHD2.js → whatsapp-state-3ZVQ6MQD.js} +2 -2
  107. package/dist/{wiki-UDPYFJBL.js → wiki-OUYKM7MB.js} +2 -2
  108. package/dist/wiki-OUYKM7MB.js.map +1 -0
  109. package/dist/workspace-setup-6GDBEORT.js +2 -0
  110. package/dist/workspace-setup-6GDBEORT.js.map +1 -0
  111. package/package.json +19 -2
  112. package/dist/agent-BCDPSPKU.js +0 -2
  113. package/dist/board-dashboard-LA7WW56M.js +0 -5668
  114. package/dist/board-dashboard-LA7WW56M.js.map +0 -1
  115. package/dist/chunk-6U2XOQHU.js +0 -7
  116. package/dist/chunk-6U2XOQHU.js.map +0 -1
  117. package/dist/chunk-7FJQGD6U.js +0 -6
  118. package/dist/chunk-7FJQGD6U.js.map +0 -1
  119. package/dist/chunk-ABDP7TDL.js +0 -71
  120. package/dist/chunk-ABDP7TDL.js.map +0 -1
  121. package/dist/chunk-CXY2RYNI.js +0 -11
  122. package/dist/chunk-CXY2RYNI.js.map +0 -1
  123. package/dist/chunk-FIOB3MEX.js +0 -2
  124. package/dist/chunk-FLOGK2U7.js.map +0 -1
  125. package/dist/chunk-HVC6FJNP.js +0 -108
  126. package/dist/chunk-HVC6FJNP.js.map +0 -1
  127. package/dist/chunk-K7BE4E35.js +0 -138
  128. package/dist/chunk-K7BE4E35.js.map +0 -1
  129. package/dist/chunk-KXJSLLSQ.js +0 -199
  130. package/dist/chunk-KXJSLLSQ.js.map +0 -1
  131. package/dist/chunk-KXZMYLHQ.js +0 -46
  132. package/dist/chunk-KXZMYLHQ.js.map +0 -1
  133. package/dist/chunk-M7HKBG3V.js +0 -2
  134. package/dist/chunk-M7HKBG3V.js.map +0 -1
  135. package/dist/chunk-MCJUAE6V.js +0 -6
  136. package/dist/chunk-MCJUAE6V.js.map +0 -1
  137. package/dist/chunk-RB7265LO.js +0 -12
  138. package/dist/chunk-RB7265LO.js.map +0 -1
  139. package/dist/chunk-YI2T5QYB.js +0 -5
  140. package/dist/compaction-JBZZ7I7Y.js.map +0 -1
  141. package/dist/config-LN6JEHMF.js +0 -2
  142. package/dist/config-mutator-24TH7PHM.js +0 -2
  143. package/dist/debug-3MEUO2P2.js +0 -2
  144. package/dist/heal-WKY5I4KB.js +0 -2
  145. package/dist/loader-PTCXLJEX.js +0 -2
  146. package/dist/memory-extract-AD7KMRPC.js +0 -2
  147. package/dist/providers-VUIWO3ZK.js +0 -2
  148. package/dist/query-PKGYE5HR.js +0 -2
  149. package/dist/registry-ASC2QS5K.js +0 -2
  150. package/dist/scheduler-A3TQOCKT.js +0 -2
  151. package/dist/types-OIFU3KSQ.js +0 -2
  152. package/dist/usage-dashboard-QX3CF6KK.js +0 -404
  153. package/dist/usage-dashboard-QX3CF6KK.js.map +0 -1
  154. package/dist/workspace-setup-ZWTT42XT.js +0 -2
  155. /package/dist/{agent-BCDPSPKU.js.map → agent-RDVGL2TW.js.map} +0 -0
  156. /package/dist/{chunk-FIOB3MEX.js.map → chunk-HPDHNIA3.js.map} +0 -0
  157. /package/dist/{chunk-7MBTMCKY.js.map → chunk-TRQSP67X.js.map} +0 -0
  158. /package/dist/{chunk-QZP65I2H.js.map → chunk-ZE2YFX4K.js.map} +0 -0
  159. /package/dist/{config-LN6JEHMF.js.map → config-UOHEG4BW.js.map} +0 -0
  160. /package/dist/{config-mutator-24TH7PHM.js.map → config-mutator-KSEBWKJH.js.map} +0 -0
  161. /package/dist/{debug-3MEUO2P2.js.map → debug-TGAP7E7W.js.map} +0 -0
  162. /package/dist/{heal-WKY5I4KB.js.map → heal-YP7QI7D3.js.map} +0 -0
  163. /package/dist/{loader-PTCXLJEX.js.map → loader-BGWA7IRY.js.map} +0 -0
  164. /package/dist/{memory-extract-AD7KMRPC.js.map → loader-LBHKSXBK.js.map} +0 -0
  165. /package/dist/{providers-VUIWO3ZK.js.map → memory-extract-KKPKAOPH.js.map} +0 -0
  166. /package/dist/{query-PKGYE5HR.js.map → program-2MDELQLX.js.map} +0 -0
  167. /package/dist/{registry-ASC2QS5K.js.map → providers-WCQSKTNP.js.map} +0 -0
  168. /package/dist/{scheduler-A3TQOCKT.js.map → query-FZAL7AQD.js.map} +0 -0
  169. /package/dist/{types-OIFU3KSQ.js.map → recipes-LZ56TA3X.js.map} +0 -0
  170. /package/dist/{wiki-UDPYFJBL.js.map → registry-ONJSILH4.js.map} +0 -0
  171. /package/dist/{workspace-setup-ZWTT42XT.js.map → scheduler-XRM2RJIV.js.map} +0 -0
  172. /package/dist/{subagent-2SJHSSJS.js.map → subagent-EPOT3QDM.js.map} +0 -0
  173. /package/dist/{whatsapp-state-6D6FHHD2.js.map → whatsapp-state-3ZVQ6MQD.js.map} +0 -0
@@ -1,404 +0,0 @@
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
- Usage dashboard: http://localhost:${s}
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
- <html lang="en">
5
- <head>
6
- <meta charset="utf-8">
7
- <meta name="viewport" content="width=device-width, initial-scale=1">
8
- <title>AgentX Usage Dashboard</title>
9
- <style>${D}</style>
10
- </head>
11
- <body>
12
- <div class="header">
13
- <h1>AgentX Usage Dashboard</h1>
14
- <div class="filters">
15
- <label>From <input type="date" id="from"></label>
16
- <label>To <input type="date" id="to"></label>
17
- <button onclick="load()">Apply</button>
18
- <button onclick="setRange(7)">7d</button>
19
- <button onclick="setRange(14)">14d</button>
20
- <button onclick="setRange(30)">30d</button>
21
- <button onclick="setRange(0)">All</button>
22
- </div>
23
- </div>
24
-
25
- <div class="stats" id="stats"></div>
26
-
27
- <div class="grid">
28
- <div class="card">
29
- <h2>Cost Over Time</h2>
30
- <canvas id="costChart" height="260"></canvas>
31
- </div>
32
- <div class="card">
33
- <h2>Tokens Over Time</h2>
34
- <canvas id="tokenChart" height="260"></canvas>
35
- </div>
36
- </div>
37
-
38
- <div class="grid">
39
- <div class="card">
40
- <h2>Cost Per Agent</h2>
41
- <canvas id="agentChart" height="260"></canvas>
42
- </div>
43
- <div class="card">
44
- <h2>Token Breakdown</h2>
45
- <canvas id="breakdownChart" height="260"></canvas>
46
- </div>
47
- </div>
48
-
49
- <div class="card">
50
- <h2>Daily Detail</h2>
51
- <table id="table">
52
- <thead>
53
- <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>
54
- </thead>
55
- <tbody></tbody>
56
- </table>
57
- </div>
58
-
59
- <script>${O}</script>
60
- </body>
61
- </html>`}var D=`
62
- :root {
63
- --bg: #0f1117;
64
- --card: #1a1d27;
65
- --border: #2a2d3a;
66
- --text: #e1e4ed;
67
- --muted: #8b8fa3;
68
- --accent: #6366f1;
69
- --green: #22c55e;
70
- --orange: #f59e0b;
71
- --red: #ef4444;
72
- --blue: #3b82f6;
73
- --purple: #a855f7;
74
- --cyan: #06b6d4;
75
- }
76
- * { margin: 0; padding: 0; box-sizing: border-box; }
77
- body { background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; padding: 24px; }
78
- .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; flex-wrap: wrap; gap: 12px; }
79
- h1 { font-size: 20px; font-weight: 600; }
80
- h2 { font-size: 14px; font-weight: 500; color: var(--muted); margin-bottom: 16px; text-transform: uppercase; letter-spacing: 0.5px; }
81
- .filters { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
82
- .filters label { font-size: 13px; color: var(--muted); }
83
- .filters input { background: var(--card); border: 1px solid var(--border); color: var(--text); padding: 6px 10px; border-radius: 6px; font-size: 13px; }
84
- .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; }
85
- .filters button:hover { background: var(--accent); border-color: var(--accent); }
86
- .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 24px; }
87
- .stat { background: var(--card); border: 1px solid var(--border); border-radius: 10px; padding: 16px; }
88
- .stat .value { font-size: 24px; font-weight: 700; margin-bottom: 4px; }
89
- .stat .label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
90
- .stat .delta { font-size: 12px; margin-top: 4px; }
91
- .stat .delta.up { color: var(--red); }
92
- .stat .delta.down { color: var(--green); }
93
- .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }
94
- @media (max-width: 900px) { .grid { grid-template-columns: 1fr; } }
95
- .card { background: var(--card); border: 1px solid var(--border); border-radius: 10px; padding: 20px; }
96
- canvas { width: 100% !important; }
97
- table { width: 100%; border-collapse: collapse; font-size: 13px; }
98
- th { 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; }
99
- td { padding: 8px 12px; border-bottom: 1px solid var(--border); }
100
- tr:hover td { background: rgba(99, 102, 241, 0.05); }
101
- .cost { color: var(--accent); font-weight: 600; font-variant-numeric: tabular-nums; }
102
- .tok { font-variant-numeric: tabular-nums; }
103
- `,O=`
104
- let data = [];
105
-
106
- function fmtTok(n) {
107
- if (n >= 1e6) return (n/1e6).toFixed(1) + 'M';
108
- if (n >= 1e3) return (n/1e3).toFixed(0) + 'K';
109
- return String(n);
110
- }
111
- function fmtCost(n) { return n < 0.01 ? '$' + n.toFixed(4) : '$' + n.toFixed(2); }
112
-
113
- function setRange(days) {
114
- if (days === 0) {
115
- document.getElementById('from').value = '';
116
- document.getElementById('to').value = '';
117
- } else {
118
- const to = new Date();
119
- const from = new Date(Date.now() - days * 86400000);
120
- document.getElementById('from').value = from.toISOString().slice(0,10);
121
- document.getElementById('to').value = to.toISOString().slice(0,10);
122
- }
123
- load();
124
- }
125
-
126
- async function load() {
127
- const from = document.getElementById('from').value;
128
- const to = document.getElementById('to').value;
129
- const params = new URLSearchParams();
130
- if (from) params.set('from', from);
131
- if (to) params.set('to', to);
132
- const res = await fetch('/api/usage?' + params);
133
- data = await res.json();
134
- render();
135
- }
136
-
137
- function render() {
138
- renderStats();
139
- renderCostChart();
140
- renderTokenChart();
141
- renderAgentChart();
142
- renderBreakdownChart();
143
- renderTable();
144
- }
145
-
146
- function renderStats() {
147
- const el = document.getElementById('stats');
148
- const totalCost = data.reduce((s,d) => s + d.cost, 0);
149
- const totalTasks = data.reduce((s,d) => s + d.tasks, 0);
150
- const totalInput = data.reduce((s,d) => s + d.input, 0);
151
- const totalOutput = data.reduce((s,d) => s + d.output, 0);
152
- const totalCacheR = data.reduce((s,d) => s + d.cacheRead, 0);
153
- const totalCacheW = data.reduce((s,d) => s + d.cacheCreate, 0);
154
- const cacheRatio = (totalCacheR + totalCacheW) > 0 ? (totalCacheR / (totalCacheR + totalCacheW) * 100).toFixed(0) : '0';
155
- const avgDaily = data.length > 0 ? totalCost / data.length : 0;
156
-
157
- // Delta: compare last day vs previous
158
- let delta = '';
159
- if (data.length >= 2) {
160
- const last = data[data.length-1].cost;
161
- const prev = data[data.length-2].cost;
162
- if (prev > 0) {
163
- const pct = ((last - prev) / prev * 100).toFixed(0);
164
- const cls = last > prev ? 'up' : 'down';
165
- const arrow = last > prev ? '\\u2191' : '\\u2193';
166
- delta = '<div class="delta ' + cls + '">' + arrow + ' ' + Math.abs(pct) + '% vs prev day</div>';
167
- }
168
- }
169
-
170
- el.innerHTML =
171
- stat(fmtCost(totalCost), 'Total Cost', delta) +
172
- stat(fmtCost(avgDaily), 'Avg Daily Cost', '') +
173
- stat(totalTasks.toLocaleString(), 'Total Tasks', '') +
174
- stat(fmtTok(totalInput + totalOutput), 'Tokens (I/O)', '') +
175
- stat(fmtTok(totalCacheR), 'Cache Reads', '') +
176
- stat(cacheRatio + '%', 'Cache Hit Rate', '') +
177
- stat(data.length.toString(), 'Days Tracked', '');
178
- }
179
-
180
- function stat(value, label, extra) {
181
- return '<div class="stat"><div class="value">' + value + '</div><div class="label">' + label + '</div>' + extra + '</div>';
182
- }
183
-
184
- // --- Canvas chart helpers ---
185
- function drawLine(ctx, points, w, h, color, fill) {
186
- if (points.length < 2) return;
187
- const max = Math.max(...points, 0.001);
188
- const pad = { t: 10, r: 10, b: 30, l: 50 };
189
- const cw = w - pad.l - pad.r;
190
- const ch = h - pad.t - pad.b;
191
-
192
- ctx.strokeStyle = color;
193
- ctx.lineWidth = 2;
194
- ctx.beginPath();
195
- points.forEach((v, i) => {
196
- const x = pad.l + (i / (points.length - 1)) * cw;
197
- const y = pad.t + ch - (v / max) * ch;
198
- i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
199
- });
200
- ctx.stroke();
201
-
202
- if (fill) {
203
- ctx.lineTo(pad.l + cw, pad.t + ch);
204
- ctx.lineTo(pad.l, pad.t + ch);
205
- ctx.closePath();
206
- ctx.fillStyle = fill;
207
- ctx.fill();
208
- }
209
-
210
- // Y axis labels
211
- ctx.fillStyle = '#8b8fa3';
212
- ctx.font = '11px -apple-system, sans-serif';
213
- ctx.textAlign = 'right';
214
- for (let i = 0; i <= 4; i++) {
215
- const v = (max / 4) * i;
216
- const y = pad.t + ch - (i / 4) * ch;
217
- ctx.fillText(v >= 1000 ? fmtTok(v) : v < 0.01 ? v.toFixed(4) : v.toFixed(2), pad.l - 6, y + 4);
218
- ctx.strokeStyle = '#2a2d3a';
219
- ctx.lineWidth = 0.5;
220
- ctx.beginPath(); ctx.moveTo(pad.l, y); ctx.lineTo(pad.l + cw, y); ctx.stroke();
221
- }
222
-
223
- // X axis labels (first, middle, last)
224
- ctx.fillStyle = '#8b8fa3';
225
- ctx.textAlign = 'center';
226
- if (data.length > 0) {
227
- const labels = [0, Math.floor(data.length/2), data.length-1];
228
- labels.forEach(i => {
229
- if (i < data.length) {
230
- const x = pad.l + (i / (points.length - 1)) * cw;
231
- ctx.fillText(data[i].date.slice(5), x, pad.t + ch + 18);
232
- }
233
- });
234
- }
235
- }
236
-
237
- function drawBars(ctx, items, w, h, colors) {
238
- if (items.length === 0) return;
239
- const max = Math.max(...items.map(i => i.value), 0.001);
240
- const pad = { t: 10, r: 10, b: 60, l: 60 };
241
- const cw = w - pad.l - pad.r;
242
- const ch = h - pad.t - pad.b;
243
- const barW = Math.min(40, (cw / items.length) * 0.7);
244
- const gap = cw / items.length;
245
-
246
- items.forEach((item, i) => {
247
- const barH = (item.value / max) * ch;
248
- const x = pad.l + i * gap + (gap - barW) / 2;
249
- const y = pad.t + ch - barH;
250
- ctx.fillStyle = colors[i % colors.length];
251
- ctx.beginPath();
252
- ctx.roundRect(x, y, barW, barH, 3);
253
- ctx.fill();
254
-
255
- // Label
256
- ctx.fillStyle = '#8b8fa3';
257
- ctx.font = '11px -apple-system, sans-serif';
258
- ctx.textAlign = 'center';
259
- ctx.save();
260
- ctx.translate(x + barW/2, pad.t + ch + 12);
261
- ctx.rotate(-0.4);
262
- ctx.fillText(item.label.length > 14 ? item.label.slice(0,12) + '..' : item.label, 0, 0);
263
- ctx.restore();
264
-
265
- // Value on top
266
- ctx.fillStyle = '#e1e4ed';
267
- ctx.textAlign = 'center';
268
- ctx.fillText(item.fmt, x + barW/2, y - 6);
269
- });
270
- }
271
-
272
- function drawStacked(ctx, series, labels, w, h, colors) {
273
- if (labels.length === 0) return;
274
- const pad = { t: 10, r: 10, b: 30, l: 50 };
275
- const cw = w - pad.l - pad.r;
276
- const ch = h - pad.t - pad.b;
277
-
278
- // Calculate totals for max
279
- const totals = labels.map((_, i) => series.reduce((s, sr) => s + sr.values[i], 0));
280
- const max = Math.max(...totals, 1);
281
-
282
- const barW = Math.min(30, (cw / labels.length) * 0.7);
283
- const gap = cw / labels.length;
284
-
285
- labels.forEach((_, li) => {
286
- let yOffset = 0;
287
- series.forEach((sr, si) => {
288
- const val = sr.values[li];
289
- const barH = (val / max) * ch;
290
- const x = pad.l + li * gap + (gap - barW) / 2;
291
- const y = pad.t + ch - yOffset - barH;
292
- ctx.fillStyle = colors[si % colors.length];
293
- ctx.fillRect(x, y, barW, barH);
294
- yOffset += barH;
295
- });
296
- });
297
-
298
- // Legend
299
- ctx.font = '11px -apple-system, sans-serif';
300
- series.forEach((sr, i) => {
301
- const x = pad.l + i * 100;
302
- ctx.fillStyle = colors[i % colors.length];
303
- ctx.fillRect(x, h - 16, 10, 10);
304
- ctx.fillStyle = '#8b8fa3';
305
- ctx.textAlign = 'left';
306
- ctx.fillText(sr.name, x + 14, h - 7);
307
- });
308
-
309
- // X axis
310
- ctx.fillStyle = '#8b8fa3';
311
- ctx.textAlign = 'center';
312
- if (data.length > 0) {
313
- [0, Math.floor(labels.length/2), labels.length-1].forEach(i => {
314
- if (i < labels.length) {
315
- const x = pad.l + (i / Math.max(labels.length-1,1)) * cw;
316
- ctx.fillText(labels[i].slice(5), x, pad.t + ch + 18);
317
- }
318
- });
319
- }
320
- }
321
-
322
- function setupCanvas(id) {
323
- const canvas = document.getElementById(id);
324
- const dpr = window.devicePixelRatio || 1;
325
- const rect = canvas.parentElement.getBoundingClientRect();
326
- const w = rect.width - 40; // account for card padding
327
- const h = canvas.height;
328
- canvas.width = w * dpr;
329
- canvas.height = h * dpr;
330
- canvas.style.width = w + 'px';
331
- canvas.style.height = h + 'px';
332
- const ctx = canvas.getContext('2d');
333
- ctx.scale(dpr, dpr);
334
- ctx.clearRect(0, 0, w, h);
335
- return { ctx, w, h };
336
- }
337
-
338
- function renderCostChart() {
339
- const { ctx, w, h } = setupCanvas('costChart');
340
- const points = data.map(d => d.cost);
341
- drawLine(ctx, points, w, h, '#6366f1', 'rgba(99,102,241,0.1)');
342
- }
343
-
344
- function renderTokenChart() {
345
- const { ctx, w, h } = setupCanvas('tokenChart');
346
- const labels = data.map(d => d.date);
347
- drawStacked(ctx, [
348
- { name: 'Input', values: data.map(d => d.input) },
349
- { name: 'Output', values: data.map(d => d.output) },
350
- { name: 'Cache R', values: data.map(d => d.cacheRead) },
351
- { name: 'Cache W', values: data.map(d => d.cacheCreate) },
352
- ], labels, w, h, ['#3b82f6', '#22c55e', '#06b6d4', '#f59e0b']);
353
- }
354
-
355
- function renderAgentChart() {
356
- const { ctx, w, h } = setupCanvas('agentChart');
357
- const agentTotals = {};
358
- data.forEach(d => {
359
- for (const [id, a] of Object.entries(d.agents)) {
360
- agentTotals[id] = (agentTotals[id] || 0) + a.cost;
361
- }
362
- });
363
- const items = Object.entries(agentTotals)
364
- .sort(([,a],[,b]) => b - a)
365
- .slice(0, 10)
366
- .map(([id, cost]) => ({ label: id, value: cost, fmt: fmtCost(cost) }));
367
- drawBars(ctx, items, w, h, ['#6366f1','#3b82f6','#06b6d4','#22c55e','#f59e0b','#ef4444','#a855f7','#ec4899']);
368
- }
369
-
370
- function renderBreakdownChart() {
371
- const { ctx, w, h } = setupCanvas('breakdownChart');
372
- const totals = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 };
373
- data.forEach(d => { totals.input += d.input; totals.output += d.output; totals.cacheRead += d.cacheRead; totals.cacheCreate += d.cacheCreate; });
374
- const items = [
375
- { label: 'Input', value: totals.input, fmt: fmtTok(totals.input) },
376
- { label: 'Output', value: totals.output, fmt: fmtTok(totals.output) },
377
- { label: 'Cache Read', value: totals.cacheRead, fmt: fmtTok(totals.cacheRead) },
378
- { label: 'Cache Write', value: totals.cacheCreate, fmt: fmtTok(totals.cacheCreate) },
379
- ];
380
- drawBars(ctx, items, w, h, ['#3b82f6','#22c55e','#06b6d4','#f59e0b']);
381
- }
382
-
383
- function renderTable() {
384
- const tbody = document.querySelector('#table tbody');
385
- tbody.innerHTML = data.slice().reverse().map(d => {
386
- const top = Object.entries(d.agents).sort(([,a],[,b]) => b.cost - a.cost)[0];
387
- const topStr = top ? top[0] + ' (' + fmtCost(top[1].cost) + ')' : '-';
388
- return '<tr>' +
389
- '<td>' + d.date + '</td>' +
390
- '<td>' + d.tasks + '</td>' +
391
- '<td class="tok">' + fmtTok(d.input) + '</td>' +
392
- '<td class="tok">' + fmtTok(d.output) + '</td>' +
393
- '<td class="tok">' + fmtTok(d.cacheRead) + '</td>' +
394
- '<td class="tok">' + fmtTok(d.cacheCreate) + '</td>' +
395
- '<td class="cost">' + fmtCost(d.cost) + '</td>' +
396
- '<td>' + topStr + '</td>' +
397
- '</tr>';
398
- }).join('');
399
- }
400
-
401
- // Auto-load last 30 days
402
- setRange(30);
403
- `,W=T;export{W as default,T as startUsageDashboard};
404
- //# sourceMappingURL=usage-dashboard-QX3CF6KK.js.map
@@ -1 +0,0 @@
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"]}
@@ -1,2 +0,0 @@
1
- import{a,b}from"./chunk-QZP65I2H.js";import"./chunk-HI24KAEY.js";import"./chunk-FIOB3MEX.js";export{b as setupAllWorkspaces,a as setupWorkspace};
2
- //# sourceMappingURL=workspace-setup-ZWTT42XT.js.map