apteva 0.4.57 → 0.7.1

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 (142) hide show
  1. package/README.md +216 -54
  2. package/cli.js +35 -0
  3. package/install.js +92 -0
  4. package/package.json +15 -76
  5. package/LICENSE +0 -63
  6. package/bin/apteva.js +0 -196
  7. package/dist/ActivityPage.kxzzb4yc.js +0 -3
  8. package/dist/ApiDocsPage.zq998hbm.js +0 -4
  9. package/dist/App.55rea8mn.js +0 -61
  10. package/dist/App.5ywb23z4.js +0 -53
  11. package/dist/App.6thds120.js +0 -4
  12. package/dist/App.9tctxzqm.js +0 -8
  13. package/dist/App.a8r8ttaz.js +0 -4
  14. package/dist/App.agsv5bje.js +0 -4
  15. package/dist/App.cepapqmx.js +0 -4
  16. package/dist/App.dp041gb3.js +0 -221
  17. package/dist/App.fds72zb5.js +0 -4
  18. package/dist/App.fg9qj2dq.js +0 -4
  19. package/dist/App.ndfejbm9.js +0 -4
  20. package/dist/App.nxmfmq1h.js +0 -13
  21. package/dist/App.qdfyt8ba.js +0 -4
  22. package/dist/App.x2d0ygt6.js +0 -4
  23. package/dist/App.yt9p4nr3.js +0 -20
  24. package/dist/App.zn4mw16t.js +0 -1
  25. package/dist/ConnectionsPage.8r96ryw7.js +0 -3
  26. package/dist/McpPage.3cwh0gnd.js +0 -3
  27. package/dist/SettingsPage.ykgdh5ev.js +0 -3
  28. package/dist/SkillsPage.4np1s65b.js +0 -3
  29. package/dist/TasksPage.4g08t7p6.js +0 -3
  30. package/dist/TelemetryPage.72w9pwcp.js +0 -3
  31. package/dist/TestsPage.z4fk3r7r.js +0 -3
  32. package/dist/ThreadsPage.63tcajeh.js +0 -3
  33. package/dist/apteva-kit.css +0 -1
  34. package/dist/icon.png +0 -0
  35. package/dist/index.html +0 -16
  36. package/dist/styles.css +0 -1
  37. package/scripts/postinstall.mjs +0 -102
  38. package/src/auth/index.ts +0 -394
  39. package/src/auth/middleware.ts +0 -213
  40. package/src/binary.ts +0 -536
  41. package/src/channels/index.ts +0 -40
  42. package/src/channels/telegram.ts +0 -311
  43. package/src/crypto.ts +0 -301
  44. package/src/db-tests.ts +0 -174
  45. package/src/db.ts +0 -3133
  46. package/src/integrations/agentdojo.ts +0 -559
  47. package/src/integrations/composio.ts +0 -437
  48. package/src/integrations/index.ts +0 -87
  49. package/src/integrations/skillsmp.ts +0 -318
  50. package/src/mcp-client.ts +0 -605
  51. package/src/mcp-handler.ts +0 -394
  52. package/src/mcp-platform.ts +0 -2403
  53. package/src/openapi.ts +0 -2410
  54. package/src/providers.ts +0 -597
  55. package/src/routes/api/agent-utils.ts +0 -890
  56. package/src/routes/api/agents.ts +0 -916
  57. package/src/routes/api/api-keys.ts +0 -95
  58. package/src/routes/api/channels.ts +0 -182
  59. package/src/routes/api/helpers.ts +0 -12
  60. package/src/routes/api/integrations.ts +0 -639
  61. package/src/routes/api/mcp.ts +0 -574
  62. package/src/routes/api/meta-agent.ts +0 -195
  63. package/src/routes/api/projects.ts +0 -112
  64. package/src/routes/api/providers.ts +0 -424
  65. package/src/routes/api/skills.ts +0 -537
  66. package/src/routes/api/system.ts +0 -333
  67. package/src/routes/api/telemetry.ts +0 -203
  68. package/src/routes/api/tests.ts +0 -148
  69. package/src/routes/api/triggers.ts +0 -518
  70. package/src/routes/api/users.ts +0 -148
  71. package/src/routes/api/webhooks.ts +0 -171
  72. package/src/routes/api.ts +0 -53
  73. package/src/routes/auth.ts +0 -251
  74. package/src/routes/share.ts +0 -86
  75. package/src/routes/static.ts +0 -131
  76. package/src/server.ts +0 -642
  77. package/src/test-runner.ts +0 -598
  78. package/src/triggers/agentdojo.ts +0 -253
  79. package/src/triggers/composio.ts +0 -264
  80. package/src/triggers/index.ts +0 -71
  81. package/src/tui/AgentList.tsx +0 -145
  82. package/src/tui/App.tsx +0 -102
  83. package/src/tui/Login.tsx +0 -104
  84. package/src/tui/api.ts +0 -72
  85. package/src/tui/index.tsx +0 -7
  86. package/src/web/App.tsx +0 -455
  87. package/src/web/components/activity/ActivityPage.tsx +0 -314
  88. package/src/web/components/activity/index.ts +0 -1
  89. package/src/web/components/agents/AgentCard.tsx +0 -189
  90. package/src/web/components/agents/AgentPanel.tsx +0 -2244
  91. package/src/web/components/agents/AgentsView.tsx +0 -180
  92. package/src/web/components/agents/CreateAgentModal.tsx +0 -475
  93. package/src/web/components/agents/index.ts +0 -4
  94. package/src/web/components/api/ApiDocsPage.tsx +0 -842
  95. package/src/web/components/auth/CreateAccountStep.tsx +0 -176
  96. package/src/web/components/auth/LoginPage.tsx +0 -91
  97. package/src/web/components/auth/index.ts +0 -2
  98. package/src/web/components/common/Icons.tsx +0 -250
  99. package/src/web/components/common/LoadingSpinner.tsx +0 -44
  100. package/src/web/components/common/Modal.tsx +0 -199
  101. package/src/web/components/common/Select.tsx +0 -97
  102. package/src/web/components/common/index.ts +0 -20
  103. package/src/web/components/connections/ConnectionsPage.tsx +0 -54
  104. package/src/web/components/connections/IntegrationsTab.tsx +0 -170
  105. package/src/web/components/connections/OverviewTab.tsx +0 -137
  106. package/src/web/components/connections/TriggersTab.tsx +0 -1346
  107. package/src/web/components/dashboard/Dashboard.tsx +0 -572
  108. package/src/web/components/dashboard/index.ts +0 -1
  109. package/src/web/components/index.ts +0 -21
  110. package/src/web/components/layout/ErrorBanner.tsx +0 -18
  111. package/src/web/components/layout/Header.tsx +0 -332
  112. package/src/web/components/layout/Sidebar.tsx +0 -231
  113. package/src/web/components/layout/index.ts +0 -3
  114. package/src/web/components/mcp/IntegrationsPanel.tsx +0 -857
  115. package/src/web/components/mcp/McpPage.tsx +0 -2515
  116. package/src/web/components/mcp/index.ts +0 -1
  117. package/src/web/components/meta-agent/MetaAgent.tsx +0 -245
  118. package/src/web/components/onboarding/OnboardingWizard.tsx +0 -404
  119. package/src/web/components/onboarding/index.ts +0 -1
  120. package/src/web/components/settings/SettingsPage.tsx +0 -2776
  121. package/src/web/components/settings/index.ts +0 -1
  122. package/src/web/components/skills/SkillsPage.tsx +0 -1200
  123. package/src/web/components/tasks/TasksPage.tsx +0 -1116
  124. package/src/web/components/tasks/index.ts +0 -1
  125. package/src/web/components/telemetry/TelemetryPage.tsx +0 -1129
  126. package/src/web/components/tests/TestsPage.tsx +0 -594
  127. package/src/web/components/threads/ThreadsPage.tsx +0 -315
  128. package/src/web/context/AuthContext.tsx +0 -242
  129. package/src/web/context/ProjectContext.tsx +0 -214
  130. package/src/web/context/TelemetryContext.tsx +0 -299
  131. package/src/web/context/ThemeContext.tsx +0 -90
  132. package/src/web/context/UIModeContext.tsx +0 -49
  133. package/src/web/context/index.ts +0 -12
  134. package/src/web/hooks/index.ts +0 -3
  135. package/src/web/hooks/useAgents.ts +0 -115
  136. package/src/web/hooks/useOnboarding.ts +0 -20
  137. package/src/web/hooks/useProviders.ts +0 -75
  138. package/src/web/icon.png +0 -0
  139. package/src/web/index.html +0 -16
  140. package/src/web/styles.css +0 -118
  141. package/src/web/themes.ts +0 -162
  142. package/src/web/types.ts +0 -298
@@ -1,916 +0,0 @@
1
- import { existsSync, rmSync } from "fs";
2
- import { join } from "path";
3
- import { json, isDev } from "./helpers";
4
- import {
5
- agentFetch,
6
- toApiAgent, toApiAgentsBatch,
7
- checkPortFree,
8
- startAgentProcess,
9
- buildAgentConfig,
10
- pushConfigToAgent,
11
- pushSkillsToAgent,
12
- fetchFromAgent,
13
- AGENTS_DATA_DIR,
14
- META_AGENT_ID,
15
- setAgentStatus,
16
- } from "./agent-utils";
17
- import { AgentDB, McpServerDB, SkillDB, TelemetryDB, generateId, getMultiAgentConfig, type Agent } from "../../db";
18
- import { ProviderKeys } from "../../providers";
19
- import { agentProcesses } from "../../server";
20
- import type { AuthContext } from "../../auth/middleware";
21
-
22
- export async function handleAgentRoutes(
23
- req: Request,
24
- path: string,
25
- method: string,
26
- authContext?: AuthContext,
27
- ): Promise<Response | null> {
28
- // ==================== AGENT CRUD ====================
29
-
30
- // GET /api/agents - List agents (excludes meta agent), optionally filtered by project
31
- if (path === "/api/agents" && method === "GET") {
32
- const url = new URL(req.url);
33
- const projectId = url.searchParams.get("project_id");
34
-
35
- let agents;
36
- if (projectId === "unassigned") {
37
- // Agents with no project
38
- agents = AgentDB.findByProject(null);
39
- } else if (projectId) {
40
- agents = AgentDB.findByProject(projectId);
41
- } else {
42
- agents = AgentDB.findAll();
43
- }
44
-
45
- agents = agents.filter(a => a.id !== META_AGENT_ID);
46
- return json({ agents: toApiAgentsBatch(agents) });
47
- }
48
-
49
- // POST /api/agents - Create a new agent
50
- if (path === "/api/agents" && method === "POST") {
51
- try {
52
- const body = await req.json();
53
- const { name, model, provider, systemPrompt, features, projectId } = body;
54
-
55
- if (!name) {
56
- return json({ error: "Name is required" }, 400);
57
- }
58
-
59
- // Import DEFAULT_FEATURES from db.ts
60
- const { DEFAULT_FEATURES } = await import("../../db");
61
-
62
- const agent = AgentDB.create({
63
- id: generateId(),
64
- name,
65
- model: model || "claude-sonnet-4-6",
66
- provider: provider || "anthropic",
67
- system_prompt: systemPrompt || "You are a helpful assistant.",
68
- features: features || DEFAULT_FEATURES,
69
- mcp_servers: body.mcpServers || [],
70
- skills: body.skills || [],
71
- project_id: projectId || null,
72
- } as any);
73
-
74
- return json({ agent: toApiAgent(agent) }, 201);
75
- } catch (e) {
76
- console.error("Create agent error:", e);
77
- return json({ error: "Invalid request body" }, 400);
78
- }
79
- }
80
-
81
- // GET /api/agents/:id - Get a specific agent
82
- const agentMatch = path.match(/^\/api\/agents\/([^/]+)$/);
83
- if (agentMatch && method === "GET") {
84
- const agent = AgentDB.findById(agentMatch[1]);
85
- if (!agent) {
86
- return json({ error: "Agent not found" }, 404);
87
- }
88
- return json({ agent: toApiAgent(agent) });
89
- }
90
-
91
- // PUT /api/agents/:id - Update an agent
92
- if (agentMatch && method === "PUT") {
93
- const agent = AgentDB.findById(agentMatch[1]);
94
- if (!agent) {
95
- return json({ error: "Agent not found" }, 404);
96
- }
97
-
98
- try {
99
- const body = await req.json();
100
- const updates: Partial<Agent> = {};
101
-
102
- if (body.name !== undefined) updates.name = body.name;
103
- if (body.model !== undefined) updates.model = body.model;
104
- if (body.provider !== undefined) updates.provider = body.provider;
105
- if (body.systemPrompt !== undefined) updates.system_prompt = body.systemPrompt;
106
- if (body.features !== undefined) updates.features = body.features;
107
- if (body.mcpServers !== undefined) updates.mcp_servers = body.mcpServers;
108
- if (body.skills !== undefined) updates.skills = body.skills;
109
- if (body.projectId !== undefined) updates.project_id = body.projectId;
110
-
111
- const updated = AgentDB.update(agentMatch[1], updates);
112
-
113
- // If agent is running, handle config update
114
- if (updated && updated.status === "running" && updated.port) {
115
- const providerChanged = body.provider !== undefined && body.provider !== agent.provider;
116
-
117
- if (providerChanged) {
118
- // Provider changed — must restart to get new API key in env
119
- console.log(`Provider changed for ${updated.name} (${agent.provider} -> ${updated.provider}), restarting...`);
120
- const agentProc = agentProcesses.get(updated.id);
121
- if (agentProc) {
122
- // Graceful shutdown
123
- try {
124
- await fetch(`http://localhost:${updated.port}/shutdown`, {
125
- method: "POST",
126
- signal: AbortSignal.timeout(2000),
127
- });
128
- await new Promise(r => setTimeout(r, 500));
129
- } catch {}
130
- try { agentProc.proc.kill(); } catch {}
131
- agentProcesses.delete(updated.id);
132
- }
133
- setAgentStatus(updated.id, "stopped", "provider_changed");
134
- // Start with new provider
135
- const startResult = await startAgentProcess(updated, { silent: true });
136
- if (!startResult.success) {
137
- console.error(`Failed to restart agent after provider change: ${startResult.error}`);
138
- }
139
- } else {
140
- // Same provider — just push updated config
141
- const providerKey = ProviderKeys.getDecrypted(updated.provider);
142
- if (providerKey) {
143
- const config = buildAgentConfig(updated, providerKey);
144
- const configResult = await pushConfigToAgent(updated.id, updated.port, config);
145
- if (!configResult.success) {
146
- console.error(`Failed to push config to running agent: ${configResult.error}`);
147
- }
148
- // Push skills via /skills endpoint
149
- if (config.skills?.definitions?.length > 0) {
150
- const skillsResult = await pushSkillsToAgent(updated.id, updated.port, config.skills.definitions);
151
- if (!skillsResult.success) {
152
- console.error(`Failed to push skills to running agent: ${skillsResult.error}`);
153
- }
154
- }
155
- }
156
- }
157
- }
158
-
159
- return json({ agent: updated ? toApiAgent(updated) : null });
160
- } catch (e) {
161
- return json({ error: "Invalid request body" }, 400);
162
- }
163
- }
164
-
165
- // DELETE /api/agents/:id - Delete an agent
166
- if (agentMatch && method === "DELETE") {
167
- const agentId = agentMatch[1];
168
- const agent = AgentDB.findById(agentId);
169
- if (!agent) {
170
- return json({ error: "Agent not found" }, 404);
171
- }
172
-
173
- // Stop the agent if running
174
- const agentProc = agentProcesses.get(agentId);
175
- const port = agent.port;
176
-
177
- if (agentProc) {
178
- // Try graceful shutdown first
179
- if (port) {
180
- try {
181
- await fetch(`http://localhost:${port}/shutdown`, {
182
- method: "POST",
183
- signal: AbortSignal.timeout(2000),
184
- });
185
- await new Promise(r => setTimeout(r, 500));
186
- } catch {
187
- // Graceful shutdown failed
188
- }
189
- }
190
-
191
- try {
192
- agentProc.proc.kill();
193
- } catch {
194
- // Already dead
195
- }
196
- agentProcesses.delete(agentId);
197
-
198
- // Ensure port is freed
199
- if (port) {
200
- const isFree = await checkPortFree(port);
201
- if (!isFree) {
202
- try {
203
- const { execSync } = await import("child_process");
204
- execSync(`lsof -ti :${port} | xargs -r kill -9 2>/dev/null || true`, { stdio: "ignore" });
205
- } catch {
206
- // Ignore
207
- }
208
- }
209
- }
210
- }
211
-
212
- // Delete agent's telemetry data
213
- TelemetryDB.deleteByAgent(agentId);
214
-
215
- // Delete agent's data directory (contains threads, messages, etc.)
216
- const agentDataDir = join(AGENTS_DATA_DIR, agentId);
217
- if (existsSync(agentDataDir)) {
218
- try {
219
- rmSync(agentDataDir, { recursive: true, force: true });
220
- console.log(`Deleted agent data directory: ${agentDataDir}`);
221
- } catch (err) {
222
- console.error(`Failed to delete agent data directory: ${err}`);
223
- }
224
- }
225
-
226
- AgentDB.delete(agentId);
227
- return json({ success: true });
228
- }
229
-
230
- // ==================== AGENT API KEY ====================
231
-
232
- // GET /api/agents/:id/api-key - Get the agent's API key (masked)
233
- const apiKeyMatch = path.match(/^\/api\/agents\/([^/]+)\/api-key$/);
234
- if (apiKeyMatch && method === "GET") {
235
- const agent = AgentDB.findById(apiKeyMatch[1]);
236
- if (!agent) {
237
- return json({ error: "Agent not found" }, 404);
238
- }
239
-
240
- const apiKey = AgentDB.getApiKey(agent.id);
241
- if (!apiKey) {
242
- return json({ error: "No API key found for this agent" }, 404);
243
- }
244
-
245
- // Return masked key + full key (full key only shown on demand by frontend)
246
- const masked = apiKey.substring(0, 8) + "..." + apiKey.substring(apiKey.length - 4);
247
- return json({
248
- apiKey: masked,
249
- fullKey: apiKey,
250
- hasKey: true,
251
- });
252
- }
253
-
254
- // POST /api/agents/:id/api-key - Regenerate the agent's API key
255
- if (apiKeyMatch && method === "POST") {
256
- const agent = AgentDB.findById(apiKeyMatch[1]);
257
- if (!agent) {
258
- return json({ error: "Agent not found" }, 404);
259
- }
260
-
261
- const newKey = AgentDB.regenerateApiKey(agent.id);
262
- if (!newKey) {
263
- return json({ error: "Failed to regenerate API key" }, 500);
264
- }
265
-
266
- // Return the full new key (only time it's fully visible)
267
- return json({
268
- apiKey: newKey,
269
- message: "API key regenerated. This is the only time the full key will be shown.",
270
- });
271
- }
272
-
273
- // ==================== SHARE LINK ====================
274
-
275
- // GET /api/agents/:id/share-token - Get the share token for this agent
276
- const shareTokenMatch = path.match(/^\/api\/agents\/([^/]+)\/share-token$/);
277
- if (shareTokenMatch && method === "GET") {
278
- const agent = AgentDB.findById(shareTokenMatch[1]);
279
- if (!agent) {
280
- return json({ error: "Agent not found" }, 404);
281
- }
282
-
283
- const { getShareToken } = await import("../share");
284
- const token = getShareToken(agent.id);
285
- if (!token) {
286
- return json({ error: "Could not generate share token" }, 500);
287
- }
288
-
289
- return json({ token });
290
- }
291
-
292
- // ==================== AGENT LIFECYCLE ====================
293
-
294
- // POST /api/agents/:id/start - Start an agent
295
- const startMatch = path.match(/^\/api\/agents\/([^/]+)\/start$/);
296
- if (startMatch && method === "POST") {
297
- const agent = AgentDB.findById(startMatch[1]);
298
- if (!agent) {
299
- return json({ error: "Agent not found" }, 404);
300
- }
301
-
302
- const result = await startAgentProcess(agent);
303
- if (!result.success) {
304
- return json({ error: result.error }, 400);
305
- }
306
-
307
- const updated = AgentDB.findById(agent.id);
308
- return json({ agent: updated ? toApiAgent(updated) : null, message: `Agent started on port ${result.port}` });
309
- }
310
-
311
- // POST /api/agents/:id/stop - Stop an agent
312
- const stopMatch = path.match(/^\/api\/agents\/([^/]+)\/stop$/);
313
- if (stopMatch && method === "POST") {
314
- const agent = AgentDB.findById(stopMatch[1]);
315
- if (!agent) {
316
- return json({ error: "Agent not found" }, 404);
317
- }
318
-
319
- const agentProc = agentProcesses.get(agent.id);
320
- const port = agent.port;
321
-
322
- if (agentProc) {
323
- console.log(`Stopping agent ${agent.name} (pid: ${agentProc.proc.pid})...`);
324
-
325
- // Try graceful shutdown first
326
- if (port) {
327
- try {
328
- await fetch(`http://localhost:${port}/shutdown`, {
329
- method: "POST",
330
- signal: AbortSignal.timeout(2000),
331
- });
332
- await new Promise(r => setTimeout(r, 500)); // Wait for graceful shutdown
333
- } catch {
334
- // Graceful shutdown failed or timed out
335
- }
336
- }
337
-
338
- // Force kill if still running
339
- try {
340
- agentProc.proc.kill();
341
- } catch {
342
- // Already dead
343
- }
344
- agentProcesses.delete(agent.id);
345
-
346
- // Ensure port is freed
347
- if (port) {
348
- const isFree = await checkPortFree(port);
349
- if (!isFree) {
350
- // Force kill by port
351
- try {
352
- const { execSync } = await import("child_process");
353
- execSync(`lsof -ti :${port} | xargs -r kill -9 2>/dev/null || true`, { stdio: "ignore" });
354
- } catch {
355
- // Ignore
356
- }
357
- }
358
- }
359
- }
360
-
361
- const updated = setAgentStatus(agent.id, "stopped", "user_stopped");
362
- return json({ agent: updated ? toApiAgent(updated) : null, message: "Agent stopped" });
363
- }
364
-
365
- // POST /api/agents/:id/chat - Proxy chat to agent binary with streaming
366
- const chatMatch = path.match(/^\/api\/agents\/([^/]+)\/chat$/);
367
- if (chatMatch && method === "POST") {
368
- const agent = AgentDB.findById(chatMatch[1]);
369
- if (!agent) {
370
- return json({ error: "Agent not found" }, 404);
371
- }
372
-
373
- if (agent.status !== "running" || !agent.port) {
374
- return json({ error: "Agent is not running" }, 400);
375
- }
376
-
377
- try {
378
- const body = await req.json();
379
-
380
-
381
- // Proxy to the agent's /chat endpoint with authentication
382
- const response = await agentFetch(agent.id, agent.port, "/chat", {
383
- method: "POST",
384
- headers: { "Content-Type": "application/json" },
385
- body: JSON.stringify(body),
386
- });
387
-
388
- // Stream the response back
389
- if (!response.ok) {
390
- const errorText = await response.text();
391
- return json({ error: `Agent error: ${errorText}` }, response.status);
392
- }
393
-
394
- // Return streaming response with proper headers
395
- return new Response(response.body, {
396
- status: 200,
397
- headers: {
398
- "Content-Type": response.headers.get("Content-Type") || "text/event-stream",
399
- "Cache-Control": "no-cache",
400
- "Connection": "keep-alive",
401
- },
402
- });
403
- } catch (err) {
404
- console.error(`Chat proxy error: ${err}`);
405
- return json({ error: `Failed to proxy chat: ${err}` }, 500);
406
- }
407
- }
408
-
409
- // ==================== WEBHOOK ENDPOINT ====================
410
-
411
- // POST /api/agents/:id/webhook - Receive external trigger events and forward to agent chat
412
- const webhookMatch = path.match(/^\/api\/agents\/([^/]+)\/webhook$/);
413
- if (webhookMatch && method === "POST") {
414
- const agent = AgentDB.findById(webhookMatch[1]);
415
- if (!agent) {
416
- return json({ error: "Agent not found" }, 404);
417
- }
418
-
419
- if (agent.status !== "running" || !agent.port) {
420
- return json({ error: "Agent is not running" }, 400);
421
- }
422
-
423
- try {
424
- const body = await req.json();
425
-
426
- // Format the webhook payload as a chat message
427
- const triggerSlug = body.trigger_name || body.type || "unknown_trigger";
428
- const eventPayload = body.payload || body.data || body;
429
-
430
- const triggerName = String(triggerSlug).replace(/_/g, " ");
431
- const message = [
432
- `[Trigger: ${triggerName}]`,
433
- "",
434
- "```json",
435
- JSON.stringify(eventPayload, null, 2),
436
- "```",
437
- "",
438
- "Process this event and take appropriate action.",
439
- ].join("\n");
440
-
441
- // Forward to agent's /chat endpoint
442
- const response = await agentFetch(agent.id, agent.port, "/chat", {
443
- method: "POST",
444
- headers: { "Content-Type": "application/json" },
445
- body: JSON.stringify({ message }),
446
- });
447
-
448
- // Consume the streaming response (we don't need the agent's reply)
449
- if (response.body) {
450
- try {
451
- const reader = response.body.getReader();
452
- while (true) {
453
- const { done } = await reader.read();
454
- if (done) break;
455
- }
456
- } catch {
457
- // Ignore read errors
458
- }
459
- }
460
-
461
- if (!response.ok) {
462
- return json({ error: "Agent failed to process webhook" }, 502);
463
- }
464
-
465
- return json({ received: true, agent_id: agent.id, trigger: triggerSlug });
466
- } catch (err) {
467
- console.error(`Webhook proxy error for agent ${webhookMatch[1]}:`, err);
468
- return json({ error: `Failed to process webhook: ${err}` }, 500);
469
- }
470
- }
471
-
472
- // ==================== THREAD & MESSAGE PROXY ====================
473
-
474
- // GET /api/threads - Consolidated threads from all running agents
475
- if (path === "/api/threads" && method === "GET") {
476
- const url = new URL(req.url);
477
- const projectId = url.searchParams.get("project_id");
478
-
479
- let agents;
480
- if (projectId === "unassigned") {
481
- agents = AgentDB.findByProject(null);
482
- } else if (projectId) {
483
- agents = AgentDB.findByProject(projectId);
484
- } else {
485
- agents = AgentDB.findAll();
486
- }
487
-
488
- // Only query running agents (excluding meta agent)
489
- const runningAgents = agents.filter(a => a.id !== META_AGENT_ID && a.status === "running" && a.port);
490
-
491
- const results = await Promise.allSettled(
492
- runningAgents.map(async (agent) => {
493
- const response = await agentFetch(agent.id, agent.port!, "/threads", {
494
- method: "GET",
495
- headers: { "Accept": "application/json" },
496
- signal: AbortSignal.timeout(3000),
497
- });
498
- if (!response.ok) return [];
499
- const data = await response.json() as any;
500
- const threads = Array.isArray(data) ? data : (data.threads ?? []);
501
- return threads.map((t: any) => ({
502
- ...t,
503
- agent_id: agent.id,
504
- agent_name: agent.name,
505
- }));
506
- })
507
- );
508
-
509
- const allThreads = results
510
- .filter((r): r is PromiseFulfilledResult<any[]> => r.status === "fulfilled")
511
- .flatMap(r => r.value)
512
- .filter((t: any) => !t.parent_id);
513
-
514
- // Sort by most recent first
515
- allThreads.sort((a, b) => {
516
- const ta = a.updated_at || a.created_at || "";
517
- const tb = b.updated_at || b.created_at || "";
518
- return tb.localeCompare(ta);
519
- });
520
-
521
- return json({ threads: allThreads });
522
- }
523
-
524
- // GET/POST /api/agents/:id/threads
525
- const threadsListMatch = path.match(/^\/api\/agents\/([^/]+)\/threads$/);
526
- if (threadsListMatch && method === "GET") {
527
- const agent = AgentDB.findById(threadsListMatch[1]);
528
- if (!agent) return json({ error: "Agent not found" }, 404);
529
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
530
-
531
- try {
532
- const response = await agentFetch(agent.id, agent.port, "/threads", {
533
- method: "GET",
534
- headers: { "Accept": "application/json" },
535
- });
536
- if (!response.ok) {
537
- const errorText = await response.text();
538
- return json({ error: `Agent error: ${errorText}` }, response.status);
539
- }
540
- const data = await response.json();
541
- return json(data);
542
- } catch (err) {
543
- console.error(`Threads list proxy error: ${err}`);
544
- return json({ error: `Failed to fetch threads: ${err}` }, 500);
545
- }
546
- }
547
-
548
- if (threadsListMatch && method === "POST") {
549
- const agent = AgentDB.findById(threadsListMatch[1]);
550
- if (!agent) return json({ error: "Agent not found" }, 404);
551
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
552
-
553
- try {
554
- const body = await req.json().catch(() => ({}));
555
- const response = await agentFetch(agent.id, agent.port, "/threads", {
556
- method: "POST",
557
- headers: { "Content-Type": "application/json" },
558
- body: JSON.stringify(body),
559
- });
560
- if (!response.ok) {
561
- const errorText = await response.text();
562
- return json({ error: `Agent error: ${errorText}` }, response.status);
563
- }
564
- const data = await response.json();
565
- return json(data, 201);
566
- } catch (err) {
567
- console.error(`Thread create proxy error: ${err}`);
568
- return json({ error: `Failed to create thread: ${err}` }, 500);
569
- }
570
- }
571
-
572
- // GET/DELETE /api/agents/:id/threads/:threadId
573
- const threadDetailMatch = path.match(/^\/api\/agents\/([^/]+)\/threads\/([^/]+)$/);
574
- if (threadDetailMatch && method === "GET") {
575
- const agent = AgentDB.findById(threadDetailMatch[1]);
576
- if (!agent) return json({ error: "Agent not found" }, 404);
577
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
578
-
579
- try {
580
- const threadId = threadDetailMatch[2];
581
- const response = await agentFetch(agent.id, agent.port, `/threads/${threadId}`, {
582
- method: "GET",
583
- headers: { "Accept": "application/json" },
584
- });
585
- if (!response.ok) {
586
- const errorText = await response.text();
587
- return json({ error: `Agent error: ${errorText}` }, response.status);
588
- }
589
- const data = await response.json();
590
- return json(data);
591
- } catch (err) {
592
- console.error(`Thread detail proxy error: ${err}`);
593
- return json({ error: `Failed to fetch thread: ${err}` }, 500);
594
- }
595
- }
596
-
597
- if (threadDetailMatch && method === "DELETE") {
598
- const agent = AgentDB.findById(threadDetailMatch[1]);
599
- if (!agent) return json({ error: "Agent not found" }, 404);
600
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
601
-
602
- try {
603
- const threadId = threadDetailMatch[2];
604
- const response = await agentFetch(agent.id, agent.port, `/threads/${threadId}`, { method: "DELETE" });
605
- if (!response.ok) {
606
- const errorText = await response.text();
607
- return json({ error: `Agent error: ${errorText}` }, response.status);
608
- }
609
- return json({ success: true });
610
- } catch (err) {
611
- console.error(`Thread delete proxy error: ${err}`);
612
- return json({ error: `Failed to delete thread: ${err}` }, 500);
613
- }
614
- }
615
-
616
- // GET /api/agents/:id/threads/:threadId/messages
617
- const threadMessagesMatch = path.match(/^\/api\/agents\/([^/]+)\/threads\/([^/]+)\/messages$/);
618
- if (threadMessagesMatch && method === "GET") {
619
- const agent = AgentDB.findById(threadMessagesMatch[1]);
620
- if (!agent) return json({ error: "Agent not found" }, 404);
621
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
622
-
623
- try {
624
- const threadId = threadMessagesMatch[2];
625
- const response = await agentFetch(agent.id, agent.port, `/threads/${threadId}/messages`, {
626
- method: "GET",
627
- headers: { "Accept": "application/json" },
628
- });
629
- if (!response.ok) {
630
- const errorText = await response.text();
631
- return json({ error: `Agent error: ${errorText}` }, response.status);
632
- }
633
- const data = await response.json();
634
- return json(data);
635
- } catch (err) {
636
- console.error(`Thread messages proxy error: ${err}`);
637
- return json({ error: `Failed to fetch messages: ${err}` }, 500);
638
- }
639
- }
640
-
641
- // ==================== MEMORY PROXY ====================
642
-
643
- const memoriesMatch = path.match(/^\/api\/agents\/([^/]+)\/memories$/);
644
- if (memoriesMatch && method === "GET") {
645
- const agent = AgentDB.findById(memoriesMatch[1]);
646
- if (!agent) return json({ error: "Agent not found" }, 404);
647
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
648
-
649
- try {
650
- const url = new URL(req.url);
651
- const threadId = url.searchParams.get("thread_id") || "";
652
- const endpoint = `/memories${threadId ? `?thread_id=${threadId}` : ""}`;
653
- const response = await agentFetch(agent.id, agent.port, endpoint, {
654
- method: "GET",
655
- headers: { "Accept": "application/json" },
656
- });
657
- if (!response.ok) {
658
- const errorText = await response.text();
659
- return json({ error: `Agent error: ${errorText}` }, response.status);
660
- }
661
- const data = await response.json();
662
- return json(data);
663
- } catch (err) {
664
- console.error(`Memories list proxy error: ${err}`);
665
- return json({ error: `Failed to fetch memories: ${err}` }, 500);
666
- }
667
- }
668
-
669
- if (memoriesMatch && method === "DELETE") {
670
- const agent = AgentDB.findById(memoriesMatch[1]);
671
- if (!agent) return json({ error: "Agent not found" }, 404);
672
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
673
-
674
- try {
675
- const response = await agentFetch(agent.id, agent.port, "/memories", { method: "DELETE" });
676
- if (!response.ok) {
677
- const errorText = await response.text();
678
- return json({ error: `Agent error: ${errorText}` }, response.status);
679
- }
680
- return json({ success: true });
681
- } catch (err) {
682
- console.error(`Memories clear proxy error: ${err}`);
683
- return json({ error: `Failed to clear memories: ${err}` }, 500);
684
- }
685
- }
686
-
687
- const memoryDeleteMatch = path.match(/^\/api\/agents\/([^/]+)\/memories\/([^/]+)$/);
688
- if (memoryDeleteMatch && method === "DELETE") {
689
- const agent = AgentDB.findById(memoryDeleteMatch[1]);
690
- if (!agent) return json({ error: "Agent not found" }, 404);
691
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
692
-
693
- try {
694
- const memoryId = memoryDeleteMatch[2];
695
- const response = await agentFetch(agent.id, agent.port, `/memories/${memoryId}`, { method: "DELETE" });
696
- if (!response.ok) {
697
- const errorText = await response.text();
698
- return json({ error: `Agent error: ${errorText}` }, response.status);
699
- }
700
- return json({ success: true });
701
- } catch (err) {
702
- console.error(`Memory delete proxy error: ${err}`);
703
- return json({ error: `Failed to delete memory: ${err}` }, 500);
704
- }
705
- }
706
-
707
- // ==================== FILES PROXY ====================
708
-
709
- const filesMatch = path.match(/^\/api\/agents\/([^/]+)\/files$/);
710
- if (filesMatch && method === "POST") {
711
- const agent = AgentDB.findById(filesMatch[1]);
712
- if (!agent) return json({ error: "Agent not found" }, 404);
713
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
714
-
715
- try {
716
- const contentType = req.headers.get("content-type") || "";
717
- const body = await req.arrayBuffer();
718
- const response = await agentFetch(agent.id, agent.port, "/files", {
719
- method: "POST",
720
- headers: { "Content-Type": contentType },
721
- body: body,
722
- });
723
- if (!response.ok) {
724
- const errorText = await response.text();
725
- return json({ error: `Agent error: ${errorText}` }, response.status);
726
- }
727
- const data = await response.json();
728
- return json(data);
729
- } catch (err) {
730
- console.error(`File upload proxy error: ${err}`);
731
- return json({ error: `Failed to upload file: ${err}` }, 500);
732
- }
733
- }
734
-
735
- if (filesMatch && method === "GET") {
736
- const agent = AgentDB.findById(filesMatch[1]);
737
- if (!agent) return json({ error: "Agent not found" }, 404);
738
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
739
-
740
- try {
741
- const url = new URL(req.url);
742
- const params = new URLSearchParams();
743
- if (url.searchParams.get("thread_id")) params.set("thread_id", url.searchParams.get("thread_id")!);
744
- if (url.searchParams.get("limit")) params.set("limit", url.searchParams.get("limit")!);
745
-
746
- const endpoint = `/files${params.toString() ? `?${params}` : ""}`;
747
- const response = await agentFetch(agent.id, agent.port, endpoint, {
748
- method: "GET",
749
- headers: { "Accept": "application/json" },
750
- });
751
- if (!response.ok) {
752
- const errorText = await response.text();
753
- return json({ error: `Agent error: ${errorText}` }, response.status);
754
- }
755
- const data = await response.json();
756
- return json(data);
757
- } catch (err) {
758
- console.error(`Files list proxy error: ${err}`);
759
- return json({ error: `Failed to fetch files: ${err}` }, 500);
760
- }
761
- }
762
-
763
- // GET/DELETE /api/agents/:id/files/:fileId/download and /api/agents/:id/files/:fileId
764
- const fileDownloadMatch = path.match(/^\/api\/agents\/([^/]+)\/files\/([^/]+)\/download$/);
765
- if (fileDownloadMatch && method === "GET") {
766
- const agent = AgentDB.findById(fileDownloadMatch[1]);
767
- if (!agent) return json({ error: "Agent not found" }, 404);
768
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
769
-
770
- try {
771
- const fileId = fileDownloadMatch[2];
772
- const response = await agentFetch(agent.id, agent.port, `/files/${fileId}/download`);
773
- if (!response.ok) {
774
- const errorText = await response.text();
775
- return json({ error: `Agent error: ${errorText}` }, response.status);
776
- }
777
- return new Response(response.body, {
778
- status: response.status,
779
- headers: {
780
- "Content-Type": response.headers.get("Content-Type") || "application/octet-stream",
781
- "Content-Disposition": response.headers.get("Content-Disposition") || "attachment",
782
- "Content-Length": response.headers.get("Content-Length") || "",
783
- },
784
- });
785
- } catch (err) {
786
- console.error(`File download proxy error: ${err}`);
787
- return json({ error: `Failed to download file: ${err}` }, 500);
788
- }
789
- }
790
-
791
- const fileGetMatch = path.match(/^\/api\/agents\/([^/]+)\/files\/([^/]+)$/);
792
- if (fileGetMatch && method === "GET") {
793
- const agent = AgentDB.findById(fileGetMatch[1]);
794
- if (!agent) return json({ error: "Agent not found" }, 404);
795
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
796
-
797
- try {
798
- const fileId = fileGetMatch[2];
799
- const response = await agentFetch(agent.id, agent.port, `/files/${fileId}`, {
800
- method: "GET",
801
- headers: { "Accept": "application/json" },
802
- });
803
- if (!response.ok) {
804
- const errorText = await response.text();
805
- return json({ error: `Agent error: ${errorText}` }, response.status);
806
- }
807
- const data = await response.json();
808
- return json(data);
809
- } catch (err) {
810
- console.error(`File get proxy error: ${err}`);
811
- return json({ error: `Failed to fetch file: ${err}` }, 500);
812
- }
813
- }
814
-
815
- if (fileGetMatch && method === "DELETE") {
816
- const agent = AgentDB.findById(fileGetMatch[1]);
817
- if (!agent) return json({ error: "Agent not found" }, 404);
818
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
819
-
820
- try {
821
- const fileId = fileGetMatch[2];
822
- const response = await agentFetch(agent.id, agent.port, `/files/${fileId}`, { method: "DELETE" });
823
- if (!response.ok) {
824
- const errorText = await response.text();
825
- return json({ error: `Agent error: ${errorText}` }, response.status);
826
- }
827
- return json({ success: true });
828
- } catch (err) {
829
- console.error(`File delete proxy error: ${err}`);
830
- return json({ error: `Failed to delete file: ${err}` }, 500);
831
- }
832
- }
833
-
834
- // ==================== DISCOVERY/PEERS PROXY ====================
835
-
836
- // GET /api/discovery/agents - Central discovery endpoint for agents to find peers
837
- if (path === "/api/discovery/agents" && method === "GET") {
838
- const url = new URL(req.url); // BUG FIX: was missing url declaration
839
- const group = url.searchParams.get("group");
840
- const excludeId = url.searchParams.get("exclude") || req.headers.get("X-Agent-ID");
841
-
842
- // Find all running agents in the same group
843
- const allAgents = AgentDB.findAll();
844
- const peers = allAgents
845
- .filter(a => {
846
- if (a.status !== "running" || !a.port) return false;
847
- if (excludeId && a.id === excludeId) return false;
848
- const agentConfig = getMultiAgentConfig(a.features, a.project_id);
849
- if (!agentConfig.enabled) return false;
850
- if (group) {
851
- const peerGroup = agentConfig.group || a.project_id;
852
- if (peerGroup !== group) return false;
853
- }
854
- return true;
855
- })
856
- .map(a => {
857
- const agentConfig = getMultiAgentConfig(a.features, a.project_id);
858
- return {
859
- id: a.id,
860
- name: a.name,
861
- url: `http://localhost:${a.port}`,
862
- group: agentConfig.group || a.project_id,
863
- };
864
- });
865
-
866
- return json({ agents: peers });
867
- }
868
-
869
- // GET /api/agents/:id/peers - Get discovered peer agents
870
- const peersMatch = path.match(/^\/api\/agents\/([^/]+)\/peers$/);
871
- if (peersMatch && method === "GET") {
872
- const agent = AgentDB.findById(peersMatch[1]);
873
- if (!agent) return json({ error: "Agent not found" }, 404);
874
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
875
-
876
- try {
877
- const response = await agentFetch(agent.id, agent.port, "/discovery/agents", {
878
- method: "GET",
879
- headers: { "Accept": "application/json" },
880
- });
881
- if (!response.ok) {
882
- const errorText = await response.text();
883
- return json({ error: `Agent error: ${errorText}` }, response.status);
884
- }
885
- const data = await response.json();
886
- return json(data);
887
- } catch (err) {
888
- console.error(`Peers list proxy error: ${err}`);
889
- return json({ error: `Failed to fetch peers: ${err}` }, 500);
890
- }
891
- }
892
-
893
- // ==================== AGENT TASKS ====================
894
-
895
- // GET /api/agents/:id/tasks - Get tasks from a specific agent
896
- const agentTasksMatch = path.match(/^\/api\/agents\/([^/]+)\/tasks$/);
897
- if (agentTasksMatch && method === "GET") {
898
- const agentId = agentTasksMatch[1];
899
- const agent = AgentDB.findById(agentId);
900
-
901
- if (!agent) return json({ error: "Agent not found" }, 404);
902
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
903
-
904
- const url = new URL(req.url);
905
- const status = url.searchParams.get("status") || "all";
906
-
907
- const data = await fetchFromAgent(agent.id, agent.port, `/tasks?status=${status}`);
908
- if (!data) {
909
- return json({ error: "Failed to fetch tasks from agent" }, 500);
910
- }
911
-
912
- return json(data);
913
- }
914
-
915
- return null;
916
- }