@sooneocean/agw 1.4.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 (59) hide show
  1. package/README.md +116 -0
  2. package/bin/agw.ts +5 -0
  3. package/package.json +59 -0
  4. package/src/agents/base-adapter.ts +113 -0
  5. package/src/agents/claude-adapter.ts +29 -0
  6. package/src/agents/codex-adapter.ts +29 -0
  7. package/src/agents/gemini-adapter.ts +29 -0
  8. package/src/cli/commands/agents.ts +55 -0
  9. package/src/cli/commands/combo.ts +130 -0
  10. package/src/cli/commands/costs.ts +33 -0
  11. package/src/cli/commands/daemon.ts +110 -0
  12. package/src/cli/commands/history.ts +29 -0
  13. package/src/cli/commands/run.ts +59 -0
  14. package/src/cli/commands/status.ts +29 -0
  15. package/src/cli/commands/workflow.ts +73 -0
  16. package/src/cli/error-handler.ts +8 -0
  17. package/src/cli/http-client.ts +68 -0
  18. package/src/cli/index.ts +28 -0
  19. package/src/config.ts +68 -0
  20. package/src/daemon/middleware/auth.ts +35 -0
  21. package/src/daemon/middleware/rate-limiter.ts +63 -0
  22. package/src/daemon/middleware/tenant.ts +64 -0
  23. package/src/daemon/middleware/workspace.ts +40 -0
  24. package/src/daemon/routes/agents.ts +13 -0
  25. package/src/daemon/routes/combos.ts +103 -0
  26. package/src/daemon/routes/costs.ts +9 -0
  27. package/src/daemon/routes/health.ts +62 -0
  28. package/src/daemon/routes/memory.ts +32 -0
  29. package/src/daemon/routes/tasks.ts +157 -0
  30. package/src/daemon/routes/ui.ts +18 -0
  31. package/src/daemon/routes/workflows.ts +73 -0
  32. package/src/daemon/server.ts +91 -0
  33. package/src/daemon/services/agent-learning.ts +77 -0
  34. package/src/daemon/services/agent-manager.ts +71 -0
  35. package/src/daemon/services/auto-scaler.ts +77 -0
  36. package/src/daemon/services/circuit-breaker.ts +95 -0
  37. package/src/daemon/services/combo-executor.ts +300 -0
  38. package/src/daemon/services/dag-executor.ts +136 -0
  39. package/src/daemon/services/metrics.ts +35 -0
  40. package/src/daemon/services/stream-aggregator.ts +64 -0
  41. package/src/daemon/services/task-executor.ts +184 -0
  42. package/src/daemon/services/task-queue.ts +75 -0
  43. package/src/daemon/services/workflow-executor.ts +150 -0
  44. package/src/daemon/services/ws-manager.ts +90 -0
  45. package/src/dsl/parser.ts +124 -0
  46. package/src/plugins/plugin-loader.ts +72 -0
  47. package/src/router/keyword-router.ts +63 -0
  48. package/src/router/llm-router.ts +93 -0
  49. package/src/store/agent-repo.ts +57 -0
  50. package/src/store/audit-repo.ts +25 -0
  51. package/src/store/combo-repo.ts +99 -0
  52. package/src/store/cost-repo.ts +55 -0
  53. package/src/store/db.ts +137 -0
  54. package/src/store/memory-repo.ts +46 -0
  55. package/src/store/task-repo.ts +127 -0
  56. package/src/store/workflow-repo.ts +69 -0
  57. package/src/types.ts +208 -0
  58. package/tsconfig.json +17 -0
  59. package/ui/index.html +272 -0
package/ui/index.html ADDED
@@ -0,0 +1,272 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AGW Dashboard</title>
7
+ <style>
8
+ :root { --bg: #0d1117; --card: #161b22; --border: #30363d; --text: #c9d1d9; --dim: #8b949e; --accent: #58a6ff; --green: #3fb950; --red: #f85149; --orange: #d29922; --purple: #bc8cff; }
9
+ * { margin: 0; padding: 0; box-sizing: border-box; }
10
+ body { background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 14px; }
11
+ .header { background: var(--card); border-bottom: 1px solid var(--border); padding: 16px 24px; display: flex; justify-content: space-between; align-items: center; }
12
+ .header h1 { font-size: 20px; color: var(--accent); }
13
+ .header .status { display: flex; gap: 16px; align-items: center; }
14
+ .badge { padding: 2px 8px; border-radius: 12px; font-size: 12px; font-weight: 600; }
15
+ .badge-green { background: rgba(63,185,80,0.2); color: var(--green); }
16
+ .badge-red { background: rgba(248,81,73,0.2); color: var(--red); }
17
+ .badge-orange { background: rgba(210,153,34,0.2); color: var(--orange); }
18
+ .badge-blue { background: rgba(88,166,255,0.2); color: var(--accent); }
19
+ .badge-purple { background: rgba(188,140,255,0.2); color: var(--purple); }
20
+ .container { max-width: 1200px; margin: 0 auto; padding: 24px; }
21
+ .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin-bottom: 24px; }
22
+ .card { background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 20px; }
23
+ .card h2 { font-size: 14px; color: var(--dim); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; }
24
+ .card .value { font-size: 28px; font-weight: 700; }
25
+ .card .sub { font-size: 12px; color: var(--dim); margin-top: 4px; }
26
+ table { width: 100%; border-collapse: collapse; }
27
+ th { text-align: left; padding: 8px 12px; border-bottom: 2px solid var(--border); color: var(--dim); font-size: 12px; text-transform: uppercase; }
28
+ td { padding: 10px 12px; border-bottom: 1px solid var(--border); }
29
+ tr:hover td { background: rgba(88,166,255,0.05); }
30
+ .mono { font-family: 'SFMono-Regular', Consolas, monospace; font-size: 13px; }
31
+ .prompt { max-width: 400px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
32
+ .tabs { display: flex; gap: 0; margin-bottom: 24px; border-bottom: 1px solid var(--border); }
33
+ .tab { padding: 10px 20px; cursor: pointer; color: var(--dim); border-bottom: 2px solid transparent; }
34
+ .tab:hover { color: var(--text); }
35
+ .tab.active { color: var(--accent); border-bottom-color: var(--accent); }
36
+ .panel { display: none; }
37
+ .panel.active { display: block; }
38
+ .agent-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; }
39
+ .agent-card { padding: 16px; background: var(--card); border: 1px solid var(--border); border-radius: 8px; text-align: center; }
40
+ .agent-card .name { font-size: 16px; font-weight: 600; margin-bottom: 8px; }
41
+ .agent-card .indicator { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-right: 6px; }
42
+ .indicator-green { background: var(--green); }
43
+ .indicator-red { background: var(--red); }
44
+ .indicator-grey { background: var(--dim); }
45
+ .submit-form { display: flex; gap: 8px; margin-bottom: 24px; }
46
+ .submit-form input, .submit-form select { background: var(--card); border: 1px solid var(--border); color: var(--text); padding: 8px 12px; border-radius: 6px; font-size: 14px; }
47
+ .submit-form input { flex: 1; }
48
+ .submit-form button { background: var(--accent); color: #fff; border: none; padding: 8px 20px; border-radius: 6px; cursor: pointer; font-weight: 600; }
49
+ .submit-form button:hover { opacity: 0.9; }
50
+ .refresh-btn { background: none; border: 1px solid var(--border); color: var(--dim); padding: 4px 12px; border-radius: 6px; cursor: pointer; font-size: 12px; }
51
+ .refresh-btn:hover { color: var(--text); border-color: var(--text); }
52
+ .cost-bar { height: 6px; background: var(--border); border-radius: 3px; margin-top: 8px; overflow: hidden; }
53
+ .cost-bar-fill { height: 100%; border-radius: 3px; transition: width 0.3s; }
54
+ </style>
55
+ </head>
56
+ <body>
57
+
58
+ <div class="header">
59
+ <h1>AGW Dashboard</h1>
60
+ <div class="status">
61
+ <span id="conn-status" class="badge badge-green">Connected</span>
62
+ <button class="refresh-btn" onclick="refreshAll()">Refresh</button>
63
+ </div>
64
+ </div>
65
+
66
+ <div class="container">
67
+ <!-- Stats cards -->
68
+ <div class="grid" id="stats-grid">
69
+ <div class="card"><h2>Total Tasks</h2><div class="value" id="stat-total">-</div></div>
70
+ <div class="card"><h2>Running</h2><div class="value" id="stat-running">-</div></div>
71
+ <div class="card"><h2>Daily Cost</h2><div class="value" id="stat-daily-cost">-</div><div class="sub" id="stat-daily-limit"></div><div class="cost-bar"><div class="cost-bar-fill" id="daily-bar" style="width:0%;background:var(--green)"></div></div></div>
72
+ <div class="card"><h2>Monthly Cost</h2><div class="value" id="stat-monthly-cost">-</div><div class="sub" id="stat-monthly-limit"></div><div class="cost-bar"><div class="cost-bar-fill" id="monthly-bar" style="width:0%;background:var(--green)"></div></div></div>
73
+ </div>
74
+
75
+ <!-- Tabs -->
76
+ <div class="tabs">
77
+ <div class="tab active" onclick="switchTab('tasks', this)">Tasks</div>
78
+ <div class="tab" onclick="switchTab('agents', this)">Agents</div>
79
+ <div class="tab" onclick="switchTab('workflows', this)">Workflows</div>
80
+ <div class="tab" onclick="switchTab('costs', this)">Cost Breakdown</div>
81
+ </div>
82
+
83
+ <!-- Tasks panel -->
84
+ <div class="panel active" id="panel-tasks">
85
+ <div class="submit-form">
86
+ <input type="text" id="task-prompt" placeholder="Enter task prompt...">
87
+ <select id="task-agent">
88
+ <option value="">Auto-route</option>
89
+ <option value="claude">Claude</option>
90
+ <option value="codex">Codex</option>
91
+ <option value="gemini">Gemini</option>
92
+ </select>
93
+ <select id="task-priority">
94
+ <option value="1">P1 (Low)</option>
95
+ <option value="2">P2</option>
96
+ <option value="3" selected>P3 (Normal)</option>
97
+ <option value="4">P4</option>
98
+ <option value="5">P5 (Critical)</option>
99
+ </select>
100
+ <button onclick="submitTask()">Run</button>
101
+ </div>
102
+ <div class="card">
103
+ <table>
104
+ <thead><tr><th>ID</th><th>Status</th><th>Priority</th><th>Agent</th><th>Prompt</th><th>Duration</th><th>Cost</th></tr></thead>
105
+ <tbody id="tasks-tbody"></tbody>
106
+ </table>
107
+ </div>
108
+ </div>
109
+
110
+ <!-- Agents panel -->
111
+ <div class="panel" id="panel-agents">
112
+ <div class="agent-grid" id="agents-grid"></div>
113
+ </div>
114
+
115
+ <!-- Workflows panel -->
116
+ <div class="panel" id="panel-workflows">
117
+ <div class="card">
118
+ <table>
119
+ <thead><tr><th>ID</th><th>Name</th><th>Mode</th><th>Status</th><th>Steps</th><th>Progress</th></tr></thead>
120
+ <tbody id="workflows-tbody"></tbody>
121
+ </table>
122
+ </div>
123
+ </div>
124
+
125
+ <!-- Cost panel -->
126
+ <div class="panel" id="panel-costs">
127
+ <div class="grid" id="cost-agent-grid"></div>
128
+ </div>
129
+ </div>
130
+
131
+ <script>
132
+ const BASE = window.location.origin;
133
+ const headers = {};
134
+ const authToken = new URLSearchParams(window.location.search).get('token');
135
+ if (authToken) headers['Authorization'] = `Bearer ${authToken}`;
136
+
137
+ async function api(path) {
138
+ try {
139
+ const res = await fetch(BASE + path, { headers });
140
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
141
+ document.getElementById('conn-status').className = 'badge badge-green';
142
+ document.getElementById('conn-status').textContent = 'Connected';
143
+ return res.json();
144
+ } catch (e) {
145
+ document.getElementById('conn-status').className = 'badge badge-red';
146
+ document.getElementById('conn-status').textContent = 'Disconnected';
147
+ return null;
148
+ }
149
+ }
150
+
151
+ async function apiPost(path, body) {
152
+ const res = await fetch(BASE + path, { method: 'POST', headers: { ...headers, 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
153
+ return res.json();
154
+ }
155
+
156
+ function esc(s) {
157
+ const d = document.createElement('div');
158
+ d.textContent = s;
159
+ return d.innerHTML;
160
+ }
161
+
162
+ function statusBadge(status) {
163
+ const map = { completed: 'green', running: 'blue', failed: 'red', pending: 'orange', routing: 'purple', queued: 'orange' };
164
+ return `<span class="badge badge-${map[status] || 'orange'}">${status}</span>`;
165
+ }
166
+
167
+ function priorityBadge(p) {
168
+ const labels = { 1: 'P1', 2: 'P2', 3: 'P3', 4: 'P4', 5: 'P5' };
169
+ const colors = { 1: 'orange', 2: 'orange', 3: 'blue', 4: 'purple', 5: 'red' };
170
+ return `<span class="badge badge-${colors[p] || 'blue'}">${labels[p] || p}</span>`;
171
+ }
172
+
173
+ async function loadTasks() {
174
+ const tasks = await api('/tasks?limit=50');
175
+ if (!tasks) return;
176
+ const tbody = document.getElementById('tasks-tbody');
177
+ const running = tasks.filter(t => t.status === 'running').length;
178
+ document.getElementById('stat-total').textContent = tasks.length;
179
+ document.getElementById('stat-running').textContent = running;
180
+ tbody.innerHTML = tasks.map(t => `<tr>
181
+ <td class="mono">${t.taskId}</td>
182
+ <td>${statusBadge(t.status)}</td>
183
+ <td>${priorityBadge(t.priority || 3)}</td>
184
+ <td>${t.assignedAgent || '-'}</td>
185
+ <td class="prompt" title="${esc(t.prompt)}">${esc(t.prompt)}</td>
186
+ <td class="mono">${t.result ? (t.result.durationMs / 1000).toFixed(1) + 's' : '-'}</td>
187
+ <td class="mono">${t.result?.costEstimate ? '$' + t.result.costEstimate.toFixed(3) : '-'}</td>
188
+ </tr>`).join('');
189
+ }
190
+
191
+ async function loadAgents() {
192
+ const agents = await api('/agents');
193
+ if (!agents) return;
194
+ const grid = document.getElementById('agents-grid');
195
+ grid.innerHTML = agents.map(a => `<div class="agent-card">
196
+ <div class="name">${a.name}</div>
197
+ <div><span class="indicator ${a.available ? 'indicator-green' : a.enabled ? 'indicator-red' : 'indicator-grey'}"></span>
198
+ ${a.available ? 'Ready' : a.enabled ? 'Unavailable' : 'Disabled'}</div>
199
+ <div class="sub" style="margin-top:8px">${a.command} ${JSON.parse(a.args || '[]').join(' ')}</div>
200
+ <div class="sub">${a.lastHealthCheck ? 'Checked: ' + new Date(a.lastHealthCheck).toLocaleTimeString() : 'Never checked'}</div>
201
+ </div>`).join('');
202
+ }
203
+
204
+ async function loadCosts() {
205
+ const costs = await api('/costs');
206
+ if (!costs) return;
207
+ document.getElementById('stat-daily-cost').textContent = '$' + costs.daily.toFixed(2);
208
+ document.getElementById('stat-monthly-cost').textContent = '$' + costs.monthly.toFixed(2);
209
+
210
+ if (costs.dailyLimit) {
211
+ document.getElementById('stat-daily-limit').textContent = `Limit: $${costs.dailyLimit.toFixed(2)}`;
212
+ const pct = Math.min(100, (costs.daily / costs.dailyLimit) * 100);
213
+ const bar = document.getElementById('daily-bar');
214
+ bar.style.width = pct + '%';
215
+ bar.style.background = pct > 80 ? 'var(--red)' : pct > 50 ? 'var(--orange)' : 'var(--green)';
216
+ }
217
+ if (costs.monthlyLimit) {
218
+ document.getElementById('stat-monthly-limit').textContent = `Limit: $${costs.monthlyLimit.toFixed(2)}`;
219
+ const pct = Math.min(100, (costs.monthly / costs.monthlyLimit) * 100);
220
+ const bar = document.getElementById('monthly-bar');
221
+ bar.style.width = pct + '%';
222
+ bar.style.background = pct > 80 ? 'var(--red)' : pct > 50 ? 'var(--orange)' : 'var(--green)';
223
+ }
224
+
225
+ const grid = document.getElementById('cost-agent-grid');
226
+ grid.innerHTML = Object.entries(costs.byAgent).map(([id, cost]) =>
227
+ `<div class="card"><h2>${id}</h2><div class="value">$${cost.toFixed(2)}</div><div class="sub">All time</div></div>`
228
+ ).join('') || '<div class="card"><h2>No cost data yet</h2></div>';
229
+ }
230
+
231
+ async function loadWorkflows() {
232
+ const wfs = await api('/workflows?limit=50');
233
+ if (!wfs) return;
234
+ const tbody = document.getElementById('workflows-tbody');
235
+ tbody.innerHTML = wfs.map(w => `<tr>
236
+ <td class="mono">${w.workflowId}</td>
237
+ <td>${w.name}</td>
238
+ <td><span class="badge badge-blue">${w.mode}</span></td>
239
+ <td>${statusBadge(w.status)}</td>
240
+ <td class="mono">${w.steps.length}</td>
241
+ <td class="mono">${w.currentStep + 1}/${w.steps.length}</td>
242
+ </tr>`).join('');
243
+ }
244
+
245
+ function switchTab(name, el) {
246
+ document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
247
+ document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
248
+ el.classList.add('active');
249
+ document.getElementById('panel-' + name).classList.add('active');
250
+ }
251
+
252
+ async function submitTask() {
253
+ const prompt = document.getElementById('task-prompt').value.trim();
254
+ if (!prompt) return;
255
+ const agent = document.getElementById('task-agent').value || undefined;
256
+ const priority = parseInt(document.getElementById('task-priority').value);
257
+ await apiPost('/tasks', { prompt, preferredAgent: agent, priority });
258
+ document.getElementById('task-prompt').value = '';
259
+ loadTasks();
260
+ }
261
+
262
+ async function refreshAll() {
263
+ await Promise.all([loadTasks(), loadAgents(), loadCosts(), loadWorkflows()]);
264
+ }
265
+
266
+ // Initial load
267
+ refreshAll();
268
+ // Auto-refresh every 5s
269
+ setInterval(refreshAll, 5000);
270
+ </script>
271
+ </body>
272
+ </html>