apteva 0.4.56 → 0.7.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 (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 +12 -79
  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 -2370
  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,333 +0,0 @@
1
- import { json } from "./helpers";
2
- import { META_AGENT_ENABLED, fetchFromAgent, agentFetch, startAgentProcess, setAgentStatus } from "./agent-utils";
3
- import { AgentDB } from "../../db";
4
- import { ProviderKeys } from "../../providers";
5
- import { agentProcesses, getBinaryStatus, BIN_DIR } from "../../server";
6
- import {
7
- checkForUpdates,
8
- getInstalledVersion,
9
- getAptevaVersion,
10
- downloadLatestBinary,
11
- installViaNpm,
12
- } from "../../binary";
13
- import { openApiSpec } from "../../openapi";
14
-
15
- export async function handleSystemRoutes(
16
- req: Request,
17
- path: string,
18
- method: string,
19
- authContext?: unknown,
20
- ): Promise<Response | null> {
21
- // GET /api/health - Health check endpoint (no auth required)
22
- if (path === "/api/health" && method === "GET") {
23
- const binaryStatus = getBinaryStatus(BIN_DIR);
24
- const installedVersion = getInstalledVersion();
25
- return json({
26
- status: "ok",
27
- version: getAptevaVersion(),
28
- timestamp: new Date().toISOString(),
29
- agents: {
30
- total: AgentDB.count(),
31
- running: AgentDB.countRunning(),
32
- },
33
- binary: {
34
- available: binaryStatus.exists,
35
- platform: binaryStatus.platform,
36
- arch: binaryStatus.arch,
37
- version: installedVersion,
38
- },
39
- });
40
- }
41
-
42
- // GET /api/features - Feature flags (no auth required)
43
- if (path === "/api/features" && method === "GET") {
44
- return json({
45
- projects: process.env.PROJECTS_ENABLED === "true",
46
- metaAgent: process.env.META_AGENT_ENABLED === "true",
47
- costTracking: process.env.COST_TRACKING_ENABLED !== "false",
48
- });
49
- }
50
-
51
- // GET /api/openapi - OpenAPI spec (no auth required)
52
- if (path === "/api/openapi" && method === "GET") {
53
- return json(openApiSpec);
54
- }
55
-
56
- // GET /api/stats - Get statistics
57
- if (path === "/api/stats" && method === "GET") {
58
- return json({
59
- totalAgents: AgentDB.count(),
60
- runningAgents: AgentDB.countRunning(),
61
- });
62
- }
63
-
64
- // GET /api/binary - Get binary status
65
- if (path === "/api/binary" && method === "GET") {
66
- return json(getBinaryStatus(BIN_DIR));
67
- }
68
-
69
- // GET /api/version - Check agent binary version info
70
- if (path === "/api/version" && method === "GET") {
71
- const versionInfo = await checkForUpdates();
72
- return json(versionInfo);
73
- }
74
-
75
- // POST /api/version/update - Download/install latest agent binary
76
- if (path === "/api/version/update" && method === "POST") {
77
- // Get all running agents to restart later
78
- const runningAgents = AgentDB.findAll().filter(a => a.status === "running");
79
- const agentsToRestart = runningAgents.map(a => a.id);
80
-
81
- // Stop all running agents
82
- for (const agent of runningAgents) {
83
- const agentProc = agentProcesses.get(agent.id);
84
- if (agentProc) {
85
- console.log(`Stopping agent ${agent.name} for update...`);
86
- agentProc.proc.kill();
87
- agentProcesses.delete(agent.id);
88
- }
89
- setAgentStatus(agent.id, "stopped", "binary_update");
90
- }
91
-
92
- // Try npm install first, fall back to direct download
93
- let result = await installViaNpm();
94
- if (!result.success) {
95
- // Fall back to direct download
96
- result = await downloadLatestBinary(BIN_DIR);
97
- }
98
-
99
- if (!result.success) {
100
- return json({ success: false, error: result.error }, 500);
101
- }
102
-
103
- // Restart agents that were running - in parallel
104
- const restartResults = await Promise.all(
105
- agentsToRestart.map(async (agentId) => {
106
- const agent = AgentDB.findById(agentId);
107
- if (!agent) return null;
108
- console.log(`Restarting agent ${agent.name} after update...`);
109
- const startResult = await startAgentProcess(agent);
110
- return { id: agent.id, name: agent.name, success: startResult.success, error: startResult.error };
111
- })
112
- ).then(r => r.filter(Boolean));
113
-
114
- return json({
115
- success: true,
116
- version: result.version,
117
- restarted: restartResults,
118
- });
119
- }
120
-
121
- // GET /api/tasks - Get all tasks from all running agents
122
- if (path === "/api/tasks" && method === "GET") {
123
- const url = new URL(req.url);
124
- const status = url.searchParams.get("status") || "all";
125
- const projectId = url.searchParams.get("project_id");
126
-
127
- let runningAgents = AgentDB.findAll().filter(a => a.status === "running" && a.port);
128
-
129
- // Filter agents by project if requested
130
- if (projectId === "unassigned") {
131
- runningAgents = runningAgents.filter(a => !a.project_id);
132
- } else if (projectId) {
133
- runningAgents = runningAgents.filter(a => a.project_id === projectId);
134
- }
135
-
136
- const allTasks: any[] = [];
137
-
138
- // Fetch tasks from all agents in parallel
139
- const results = await Promise.all(
140
- runningAgents.map(async (agent) => {
141
- try {
142
- const data = await fetchFromAgent(agent.id, agent.port!, `/tasks?status=${status}`);
143
- return { agent, tasks: data?.tasks || [] };
144
- } catch {
145
- return { agent, tasks: [] };
146
- }
147
- })
148
- );
149
-
150
- for (const { agent, tasks } of results) {
151
- for (const task of tasks) {
152
- allTasks.push({ ...task, agentId: agent.id, agentName: agent.name });
153
- }
154
- }
155
-
156
- // Sort by created_at descending
157
- allTasks.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
158
-
159
- return json({ tasks: allTasks, count: allTasks.length });
160
- }
161
-
162
- // GET /api/tasks/:agentId/:taskId - Get a single task with full details
163
- const singleTaskMatch = path.match(/^\/api\/tasks\/([^/]+)\/([^/]+)$/);
164
- if (singleTaskMatch && method === "GET") {
165
- const [, agentId, taskId] = singleTaskMatch;
166
- const agent = AgentDB.findById(agentId);
167
-
168
- if (!agent) {
169
- return json({ error: "Agent not found" }, 404);
170
- }
171
-
172
- if (agent.status !== "running" || !agent.port) {
173
- return json({ error: "Agent is not running" }, 400);
174
- }
175
-
176
- const data = await fetchFromAgent(agent.id, agent.port, `/tasks/${taskId}`);
177
- if (!data) {
178
- return json({ error: "Failed to fetch task from agent" }, 500);
179
- }
180
-
181
- return json({ task: { ...data, agentId: agent.id, agentName: agent.name } });
182
- }
183
-
184
- // POST /api/tasks/:agentId/:taskId/execute - Execute a task immediately
185
- const executeTaskMatch = path.match(/^\/api\/tasks\/([^/]+)\/([^/]+)\/execute$/);
186
- if (executeTaskMatch && method === "POST") {
187
- const [, agentId, taskId] = executeTaskMatch;
188
- const agent = AgentDB.findById(agentId);
189
- if (!agent) return json({ error: "Agent not found" }, 404);
190
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
191
-
192
- try {
193
- const res = await agentFetch(agentId, agent.port, `/tasks/${taskId}/execute`, {
194
- method: "POST",
195
- signal: AbortSignal.timeout(5000),
196
- });
197
- const data = await res.json();
198
- if (!res.ok) return json({ error: data.error || `HTTP ${res.status}` }, res.status);
199
- return json(data);
200
- } catch (err) {
201
- return json({ error: `Failed to execute task: ${err}` }, 500);
202
- }
203
- }
204
-
205
- // POST /api/tasks/:agentId - Create a task on an agent
206
- const createTaskMatch = path.match(/^\/api\/tasks\/([^/]+)$/);
207
- if (createTaskMatch && method === "POST") {
208
- const agentId = createTaskMatch[1];
209
- const agent = AgentDB.findById(agentId);
210
- if (!agent) return json({ error: "Agent not found" }, 404);
211
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
212
-
213
- try {
214
- const body = await req.json();
215
- const res = await agentFetch(agentId, agent.port, "/tasks", {
216
- method: "POST",
217
- headers: { "Content-Type": "application/json" },
218
- body: JSON.stringify(body),
219
- signal: AbortSignal.timeout(5000),
220
- });
221
- const data = await res.json();
222
- if (!res.ok) return json({ error: data.error || `HTTP ${res.status}` }, res.status);
223
- return json(data, 201);
224
- } catch (err) {
225
- return json({ error: `Failed to create task: ${err}` }, 500);
226
- }
227
- }
228
-
229
- // PUT /api/tasks/:agentId/:taskId - Update a task on an agent
230
- if (singleTaskMatch && method === "PUT") {
231
- const [, agentId, taskId] = singleTaskMatch;
232
- const agent = AgentDB.findById(agentId);
233
- if (!agent) return json({ error: "Agent not found" }, 404);
234
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
235
-
236
- try {
237
- const body = await req.json();
238
- const res = await agentFetch(agentId, agent.port, `/tasks/${taskId}`, {
239
- method: "PUT",
240
- headers: { "Content-Type": "application/json" },
241
- body: JSON.stringify(body),
242
- signal: AbortSignal.timeout(5000),
243
- });
244
- const data = await res.json();
245
- if (!res.ok) return json({ error: data.error || `HTTP ${res.status}` }, res.status);
246
- return json(data);
247
- } catch (err) {
248
- return json({ error: `Failed to update task: ${err}` }, 500);
249
- }
250
- }
251
-
252
- // DELETE /api/tasks/:agentId/:taskId - Delete a task on an agent
253
- if (singleTaskMatch && method === "DELETE") {
254
- const [, agentId, taskId] = singleTaskMatch;
255
- const agent = AgentDB.findById(agentId);
256
- if (!agent) return json({ error: "Agent not found" }, 404);
257
- if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
258
-
259
- try {
260
- const res = await agentFetch(agentId, agent.port, `/tasks/${taskId}`, {
261
- method: "DELETE",
262
- signal: AbortSignal.timeout(5000),
263
- });
264
- const data = await res.json();
265
- if (!res.ok) return json({ error: data.error || `HTTP ${res.status}` }, res.status);
266
- return json(data);
267
- } catch (err) {
268
- return json({ error: `Failed to delete task: ${err}` }, 500);
269
- }
270
- }
271
-
272
- // GET /api/dashboard - Get dashboard statistics
273
- if (path === "/api/dashboard" && method === "GET") {
274
- const url = new URL(req.url);
275
- const projectId = url.searchParams.get("project_id");
276
-
277
- let agents = AgentDB.findAll();
278
-
279
- // Filter agents by project if specified
280
- if (projectId === "unassigned") {
281
- agents = agents.filter(a => !a.project_id);
282
- } else if (projectId) {
283
- agents = agents.filter(a => a.project_id === projectId);
284
- }
285
-
286
- const runningAgents = agents.filter(a => a.status === "running" && a.port);
287
-
288
- let totalTasks = 0;
289
- let pendingTasks = 0;
290
- let completedTasks = 0;
291
- let runningTasks = 0;
292
-
293
- // Fetch task stats from all agents in parallel
294
- const taskResults = await Promise.all(
295
- runningAgents.map(async (agent) => {
296
- try {
297
- return await fetchFromAgent(agent.id, agent.port!, "/tasks?status=all");
298
- } catch {
299
- return null;
300
- }
301
- })
302
- );
303
-
304
- for (const data of taskResults) {
305
- if (data?.tasks) {
306
- totalTasks += data.tasks.length;
307
- for (const task of data.tasks) {
308
- if (task.status === "pending") pendingTasks++;
309
- else if (task.status === "completed") completedTasks++;
310
- else if (task.status === "running") runningTasks++;
311
- }
312
- }
313
- }
314
-
315
- return json({
316
- agents: {
317
- total: agents.length,
318
- running: runningAgents.length,
319
- },
320
- tasks: {
321
- total: totalTasks,
322
- pending: pendingTasks,
323
- running: runningTasks,
324
- completed: completedTasks,
325
- },
326
- providers: {
327
- configured: ProviderKeys.getConfiguredProviders().length,
328
- },
329
- });
330
- }
331
-
332
- return null;
333
- }
@@ -1,203 +0,0 @@
1
- import { json } from "./helpers";
2
- import { TelemetryDB, AgentDB } from "../../db";
3
- import { getModelCost } from "../../providers";
4
- import { telemetryBroadcaster, type TelemetryEvent } from "../../server";
5
-
6
- export async function handleTelemetryRoutes(
7
- req: Request,
8
- path: string,
9
- method: string,
10
- ): Promise<Response | null> {
11
- // POST /api/telemetry - Receive telemetry events from agents
12
- if (path === "/api/telemetry" && method === "POST") {
13
- try {
14
- const body = await req.json() as {
15
- agent_id: string;
16
- sent_at: string;
17
- events: Array<{
18
- id: string;
19
- timestamp: string;
20
- category: string;
21
- type: string;
22
- level: string;
23
- trace_id?: string;
24
- span_id?: string;
25
- thread_id?: string;
26
- data?: Record<string, unknown>;
27
- metadata?: Record<string, unknown>;
28
- duration_ms?: number;
29
- error?: string;
30
- }>;
31
- };
32
-
33
- if (!body.agent_id || !body.events) {
34
- return json({ error: "agent_id and events are required" }, 400);
35
- }
36
-
37
- // Filter out debug events - too noisy
38
- const filteredEvents = body.events.filter(e => e.level !== "debug");
39
-
40
- // Compute cost per LLM event if cost tracking is enabled
41
- const costTrackingEnabled = process.env.COST_TRACKING_ENABLED !== "false";
42
- if (costTrackingEnabled) {
43
- const agent = AgentDB.findById(body.agent_id);
44
- if (agent) {
45
- const pricing = getModelCost(agent.provider, agent.model);
46
- for (const event of filteredEvents) {
47
- if (event.category === "LLM" && event.data) {
48
- const inputTokens = (event.data.input_tokens as number) || 0;
49
- const outputTokens = (event.data.output_tokens as number) || 0;
50
- const cacheCreationTokens = (event.data.cache_creation_tokens as number) || 0;
51
- const cacheReadTokens = (event.data.cache_read_tokens as number) || 0;
52
- const reasoningTokens = (event.data.reasoning_tokens as number) || 0;
53
- (event as any).cost = (
54
- inputTokens * pricing.input_cost +
55
- outputTokens * pricing.output_cost +
56
- cacheCreationTokens * pricing.cache_creation_cost +
57
- cacheReadTokens * pricing.cache_read_cost +
58
- reasoningTokens * pricing.output_cost
59
- ) / 1_000_000;
60
- }
61
- }
62
- }
63
- }
64
-
65
- const inserted = TelemetryDB.insertBatch(body.agent_id, filteredEvents);
66
-
67
- // Broadcast to SSE clients
68
- if (filteredEvents.length > 0) {
69
- const broadcastEvents: TelemetryEvent[] = filteredEvents.map(e => ({
70
- id: e.id,
71
- agent_id: body.agent_id,
72
- timestamp: e.timestamp,
73
- category: e.category,
74
- type: e.type,
75
- level: e.level,
76
- trace_id: e.trace_id,
77
- thread_id: e.thread_id,
78
- data: e.data,
79
- duration_ms: e.duration_ms,
80
- error: e.error,
81
- cost: (e as any).cost as number | undefined,
82
- }));
83
- telemetryBroadcaster.broadcast(broadcastEvents);
84
- }
85
-
86
- return json({ received: body.events.length, inserted });
87
- } catch (e) {
88
- console.error("Telemetry error:", e);
89
- return json({ error: "Invalid telemetry payload" }, 400);
90
- }
91
- }
92
-
93
- // GET /api/telemetry/stream - SSE stream for real-time telemetry
94
- if (path === "/api/telemetry/stream" && method === "GET") {
95
- let controller: ReadableStreamDefaultController<string>;
96
-
97
- const stream = new ReadableStream<string>({
98
- start(c) {
99
- controller = c;
100
- telemetryBroadcaster.addClient(controller);
101
- // Send initial connection message
102
- controller.enqueue("data: {\"connected\":true}\n\n");
103
- },
104
- cancel() {
105
- telemetryBroadcaster.removeClient(controller);
106
- },
107
- });
108
-
109
- return new Response(stream, {
110
- headers: {
111
- "Content-Type": "text/event-stream",
112
- "Cache-Control": "no-cache, no-transform",
113
- "Connection": "keep-alive",
114
- "X-Accel-Buffering": "no",
115
- },
116
- });
117
- }
118
-
119
- // GET /api/telemetry/events - Query telemetry events
120
- if (path === "/api/telemetry/events" && method === "GET") {
121
- const url = new URL(req.url);
122
- const projectIdParam = url.searchParams.get("project_id");
123
- const events = TelemetryDB.query({
124
- agent_id: url.searchParams.get("agent_id") || undefined,
125
- project_id: projectIdParam === "null" ? null : projectIdParam || undefined,
126
- category: url.searchParams.get("category") || undefined,
127
- type: url.searchParams.get("type") || undefined,
128
- level: url.searchParams.get("level") || undefined,
129
- trace_id: url.searchParams.get("trace_id") || undefined,
130
- since: url.searchParams.get("since") || undefined,
131
- until: url.searchParams.get("until") || undefined,
132
- limit: parseInt(url.searchParams.get("limit") || "100"),
133
- offset: parseInt(url.searchParams.get("offset") || "0"),
134
- });
135
- return json({ events });
136
- }
137
-
138
- // GET /api/telemetry/usage - Get usage statistics
139
- if (path === "/api/telemetry/usage" && method === "GET") {
140
- const url = new URL(req.url);
141
- const projectIdParam = url.searchParams.get("project_id");
142
- const usage = TelemetryDB.getUsage({
143
- agent_id: url.searchParams.get("agent_id") || undefined,
144
- project_id: projectIdParam === "null" ? null : projectIdParam || undefined,
145
- since: url.searchParams.get("since") || undefined,
146
- until: url.searchParams.get("until") || undefined,
147
- group_by: (url.searchParams.get("group_by") as "agent" | "day" | "project") || undefined,
148
- });
149
- return json({ usage });
150
- }
151
-
152
- // GET /api/telemetry/stats - Get summary statistics
153
- if (path === "/api/telemetry/stats" && method === "GET") {
154
- const url = new URL(req.url);
155
- const agentId = url.searchParams.get("agent_id") || undefined;
156
- const projectIdParam = url.searchParams.get("project_id");
157
- const stats = TelemetryDB.getStats({
158
- agentId,
159
- projectId: projectIdParam === "null" ? null : projectIdParam || undefined,
160
- since: url.searchParams.get("since") || undefined,
161
- until: url.searchParams.get("until") || undefined,
162
- });
163
- return json({ stats });
164
- }
165
-
166
- // POST /api/telemetry/clear - Clear all telemetry data
167
- if (path === "/api/telemetry/clear" && method === "POST") {
168
- const deleted = TelemetryDB.deleteOlderThan(0); // Delete all
169
- return json({ deleted });
170
- }
171
-
172
- // --- Notification endpoints (piggyback on telemetry `seen` flag) ---
173
-
174
- // GET /api/notifications - Get notification-worthy events
175
- if (path === "/api/notifications" && method === "GET") {
176
- const url = new URL(req.url);
177
- const limit = parseInt(url.searchParams.get("limit") || "50");
178
- const notifications = TelemetryDB.getNotifications(limit);
179
- return json({ notifications });
180
- }
181
-
182
- // GET /api/notifications/count - Get unseen notification count
183
- if (path === "/api/notifications/count" && method === "GET") {
184
- const count = TelemetryDB.getUnseenCount();
185
- return json({ count });
186
- }
187
-
188
- // POST /api/notifications/mark-seen - Mark specific notifications as seen
189
- if (path === "/api/notifications/mark-seen" && method === "POST") {
190
- const body = await req.json() as { ids?: string[]; all?: boolean };
191
- if (body.all) {
192
- const updated = TelemetryDB.markAllSeen();
193
- return json({ updated });
194
- }
195
- if (body.ids && body.ids.length > 0) {
196
- const updated = TelemetryDB.markSeen(body.ids);
197
- return json({ updated });
198
- }
199
- return json({ error: "Provide ids array or all: true" }, 400);
200
- }
201
-
202
- return null;
203
- }
@@ -1,148 +0,0 @@
1
- import { json } from "./helpers";
2
- import { TestCaseDB, TestRunDB } from "../../db-tests";
3
- import { AgentDB } from "../../db";
4
- import { runTest, runAll } from "../../test-runner";
5
-
6
- export async function handleTestRoutes(
7
- req: Request,
8
- path: string,
9
- method: string,
10
- ): Promise<Response | null> {
11
- // GET /api/tests - List test cases
12
- if (path === "/api/tests" && method === "GET") {
13
- const url = new URL(req.url);
14
- const projectId = url.searchParams.get("project_id") || undefined;
15
- const tests = TestCaseDB.findAll(projectId);
16
-
17
- // Enrich with agent name and latest run
18
- const enriched = tests.map(tc => {
19
- const agent = tc.agent_id ? AgentDB.findById(tc.agent_id) : null;
20
- const lastRun = TestRunDB.getLatestByTestCase(tc.id);
21
- return {
22
- ...tc,
23
- agent_name: agent?.name || null,
24
- agent_status: agent?.status || null,
25
- last_run: lastRun ? {
26
- id: lastRun.id,
27
- status: lastRun.status,
28
- score: lastRun.score,
29
- duration_ms: lastRun.duration_ms,
30
- judge_reasoning: lastRun.judge_reasoning,
31
- generated_message: lastRun.generated_message,
32
- selected_agent_id: lastRun.selected_agent_id,
33
- selected_agent_name: lastRun.selected_agent_name,
34
- planner_reasoning: lastRun.planner_reasoning,
35
- created_at: lastRun.created_at,
36
- } : null,
37
- };
38
- });
39
-
40
- return json(enriched);
41
- }
42
-
43
- // POST /api/tests - Create test case
44
- if (path === "/api/tests" && method === "POST") {
45
- const body = await req.json() as any;
46
-
47
- // Behavior-driven: only name + behavior required
48
- // Legacy: name + agent_id + input_message + eval_criteria required
49
- if (!body.name) {
50
- return json({ error: "Missing required field: name" }, 400);
51
- }
52
-
53
- if (!body.behavior && (!body.agent_id || !body.input_message)) {
54
- return json({ error: "Either 'behavior' or both 'agent_id' and 'input_message' are required" }, 400);
55
- }
56
-
57
- // Validate agent if explicitly specified
58
- if (body.agent_id) {
59
- const agent = AgentDB.findById(body.agent_id);
60
- if (!agent) {
61
- return json({ error: "Agent not found" }, 404);
62
- }
63
- }
64
-
65
- const testCase = TestCaseDB.create({
66
- name: body.name,
67
- description: body.description,
68
- behavior: body.behavior,
69
- agent_id: body.agent_id || null,
70
- input_message: body.input_message || null,
71
- eval_criteria: body.eval_criteria,
72
- project_id: body.project_id,
73
- });
74
-
75
- return json(testCase, 201);
76
- }
77
-
78
- // PUT /api/tests/:id - Update test case
79
- const updateMatch = path.match(/^\/api\/tests\/([^/]+)$/);
80
- if (updateMatch && method === "PUT") {
81
- const body = await req.json() as any;
82
- const updated = TestCaseDB.update(updateMatch[1], body);
83
- if (!updated) {
84
- return json({ error: "Test case not found" }, 404);
85
- }
86
- return json(updated);
87
- }
88
-
89
- // DELETE /api/tests/:id - Delete test case
90
- if (updateMatch && method === "DELETE") {
91
- const deleted = TestCaseDB.delete(updateMatch[1]);
92
- if (!deleted) {
93
- return json({ error: "Test case not found" }, 404);
94
- }
95
- return json({ success: true });
96
- }
97
-
98
- // POST /api/tests/:id/run - Run single test
99
- const runSingleMatch = path.match(/^\/api\/tests\/([^/]+)\/run$/);
100
- if (runSingleMatch && method === "POST") {
101
- const testCase = TestCaseDB.findById(runSingleMatch[1]);
102
- if (!testCase) {
103
- return json({ error: "Test case not found" }, 404);
104
- }
105
-
106
- const result = await runTest(testCase);
107
- return json(result);
108
- }
109
-
110
- // POST /api/tests/run - Run all (or filtered) tests
111
- if (path === "/api/tests/run" && method === "POST") {
112
- const body = await req.json().catch(() => ({})) as any;
113
- const testCaseIds = body.test_case_ids as string[] | undefined;
114
-
115
- const results = await runAll(testCaseIds);
116
- const passed = results.filter(r => r.status === "passed").length;
117
- const failed = results.filter(r => r.status === "failed").length;
118
- const errors = results.filter(r => r.status === "error").length;
119
-
120
- return json({
121
- summary: { total: results.length, passed, failed, errors },
122
- results,
123
- });
124
- }
125
-
126
- // GET /api/tests/:id/runs - Get run history for a test
127
- const runsMatch = path.match(/^\/api\/tests\/([^/]+)\/runs$/);
128
- if (runsMatch && method === "GET") {
129
- const testCase = TestCaseDB.findById(runsMatch[1]);
130
- if (!testCase) {
131
- return json({ error: "Test case not found" }, 404);
132
- }
133
- const runs = TestRunDB.findByTestCase(runsMatch[1]);
134
- return json(runs);
135
- }
136
-
137
- // GET /api/tests/runs/:runId - Get single run details
138
- const runDetailMatch = path.match(/^\/api\/tests\/runs\/([^/]+)$/);
139
- if (runDetailMatch && method === "GET") {
140
- const run = TestRunDB.findById(runDetailMatch[1]);
141
- if (!run) {
142
- return json({ error: "Test run not found" }, 404);
143
- }
144
- return json(run);
145
- }
146
-
147
- return null;
148
- }