apteva 0.4.57 → 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.
- package/README.md +216 -54
- package/cli.js +35 -0
- package/install.js +92 -0
- package/package.json +12 -79
- package/LICENSE +0 -63
- package/bin/apteva.js +0 -196
- package/dist/ActivityPage.kxzzb4yc.js +0 -3
- package/dist/ApiDocsPage.zq998hbm.js +0 -4
- package/dist/App.55rea8mn.js +0 -61
- package/dist/App.5ywb23z4.js +0 -53
- package/dist/App.6thds120.js +0 -4
- package/dist/App.9tctxzqm.js +0 -8
- package/dist/App.a8r8ttaz.js +0 -4
- package/dist/App.agsv5bje.js +0 -4
- package/dist/App.cepapqmx.js +0 -4
- package/dist/App.dp041gb3.js +0 -221
- package/dist/App.fds72zb5.js +0 -4
- package/dist/App.fg9qj2dq.js +0 -4
- package/dist/App.ndfejbm9.js +0 -4
- package/dist/App.nxmfmq1h.js +0 -13
- package/dist/App.qdfyt8ba.js +0 -4
- package/dist/App.x2d0ygt6.js +0 -4
- package/dist/App.yt9p4nr3.js +0 -20
- package/dist/App.zn4mw16t.js +0 -1
- package/dist/ConnectionsPage.8r96ryw7.js +0 -3
- package/dist/McpPage.3cwh0gnd.js +0 -3
- package/dist/SettingsPage.ykgdh5ev.js +0 -3
- package/dist/SkillsPage.4np1s65b.js +0 -3
- package/dist/TasksPage.4g08t7p6.js +0 -3
- package/dist/TelemetryPage.72w9pwcp.js +0 -3
- package/dist/TestsPage.z4fk3r7r.js +0 -3
- package/dist/ThreadsPage.63tcajeh.js +0 -3
- package/dist/apteva-kit.css +0 -1
- package/dist/icon.png +0 -0
- package/dist/index.html +0 -16
- package/dist/styles.css +0 -1
- package/scripts/postinstall.mjs +0 -102
- package/src/auth/index.ts +0 -394
- package/src/auth/middleware.ts +0 -213
- package/src/binary.ts +0 -536
- package/src/channels/index.ts +0 -40
- package/src/channels/telegram.ts +0 -311
- package/src/crypto.ts +0 -301
- package/src/db-tests.ts +0 -174
- package/src/db.ts +0 -3133
- package/src/integrations/agentdojo.ts +0 -559
- package/src/integrations/composio.ts +0 -437
- package/src/integrations/index.ts +0 -87
- package/src/integrations/skillsmp.ts +0 -318
- package/src/mcp-client.ts +0 -605
- package/src/mcp-handler.ts +0 -394
- package/src/mcp-platform.ts +0 -2403
- package/src/openapi.ts +0 -2410
- package/src/providers.ts +0 -597
- package/src/routes/api/agent-utils.ts +0 -890
- package/src/routes/api/agents.ts +0 -916
- package/src/routes/api/api-keys.ts +0 -95
- package/src/routes/api/channels.ts +0 -182
- package/src/routes/api/helpers.ts +0 -12
- package/src/routes/api/integrations.ts +0 -639
- package/src/routes/api/mcp.ts +0 -574
- package/src/routes/api/meta-agent.ts +0 -195
- package/src/routes/api/projects.ts +0 -112
- package/src/routes/api/providers.ts +0 -424
- package/src/routes/api/skills.ts +0 -537
- package/src/routes/api/system.ts +0 -333
- package/src/routes/api/telemetry.ts +0 -203
- package/src/routes/api/tests.ts +0 -148
- package/src/routes/api/triggers.ts +0 -518
- package/src/routes/api/users.ts +0 -148
- package/src/routes/api/webhooks.ts +0 -171
- package/src/routes/api.ts +0 -53
- package/src/routes/auth.ts +0 -251
- package/src/routes/share.ts +0 -86
- package/src/routes/static.ts +0 -131
- package/src/server.ts +0 -642
- package/src/test-runner.ts +0 -598
- package/src/triggers/agentdojo.ts +0 -253
- package/src/triggers/composio.ts +0 -264
- package/src/triggers/index.ts +0 -71
- package/src/tui/AgentList.tsx +0 -145
- package/src/tui/App.tsx +0 -102
- package/src/tui/Login.tsx +0 -104
- package/src/tui/api.ts +0 -72
- package/src/tui/index.tsx +0 -7
- package/src/web/App.tsx +0 -455
- package/src/web/components/activity/ActivityPage.tsx +0 -314
- package/src/web/components/activity/index.ts +0 -1
- package/src/web/components/agents/AgentCard.tsx +0 -189
- package/src/web/components/agents/AgentPanel.tsx +0 -2244
- package/src/web/components/agents/AgentsView.tsx +0 -180
- package/src/web/components/agents/CreateAgentModal.tsx +0 -475
- package/src/web/components/agents/index.ts +0 -4
- package/src/web/components/api/ApiDocsPage.tsx +0 -842
- package/src/web/components/auth/CreateAccountStep.tsx +0 -176
- package/src/web/components/auth/LoginPage.tsx +0 -91
- package/src/web/components/auth/index.ts +0 -2
- package/src/web/components/common/Icons.tsx +0 -250
- package/src/web/components/common/LoadingSpinner.tsx +0 -44
- package/src/web/components/common/Modal.tsx +0 -199
- package/src/web/components/common/Select.tsx +0 -97
- package/src/web/components/common/index.ts +0 -20
- package/src/web/components/connections/ConnectionsPage.tsx +0 -54
- package/src/web/components/connections/IntegrationsTab.tsx +0 -170
- package/src/web/components/connections/OverviewTab.tsx +0 -137
- package/src/web/components/connections/TriggersTab.tsx +0 -1346
- package/src/web/components/dashboard/Dashboard.tsx +0 -572
- package/src/web/components/dashboard/index.ts +0 -1
- package/src/web/components/index.ts +0 -21
- package/src/web/components/layout/ErrorBanner.tsx +0 -18
- package/src/web/components/layout/Header.tsx +0 -332
- package/src/web/components/layout/Sidebar.tsx +0 -231
- package/src/web/components/layout/index.ts +0 -3
- package/src/web/components/mcp/IntegrationsPanel.tsx +0 -857
- package/src/web/components/mcp/McpPage.tsx +0 -2515
- package/src/web/components/mcp/index.ts +0 -1
- package/src/web/components/meta-agent/MetaAgent.tsx +0 -245
- package/src/web/components/onboarding/OnboardingWizard.tsx +0 -404
- package/src/web/components/onboarding/index.ts +0 -1
- package/src/web/components/settings/SettingsPage.tsx +0 -2776
- package/src/web/components/settings/index.ts +0 -1
- package/src/web/components/skills/SkillsPage.tsx +0 -1200
- package/src/web/components/tasks/TasksPage.tsx +0 -1116
- package/src/web/components/tasks/index.ts +0 -1
- package/src/web/components/telemetry/TelemetryPage.tsx +0 -1129
- package/src/web/components/tests/TestsPage.tsx +0 -594
- package/src/web/components/threads/ThreadsPage.tsx +0 -315
- package/src/web/context/AuthContext.tsx +0 -242
- package/src/web/context/ProjectContext.tsx +0 -214
- package/src/web/context/TelemetryContext.tsx +0 -299
- package/src/web/context/ThemeContext.tsx +0 -90
- package/src/web/context/UIModeContext.tsx +0 -49
- package/src/web/context/index.ts +0 -12
- package/src/web/hooks/index.ts +0 -3
- package/src/web/hooks/useAgents.ts +0 -115
- package/src/web/hooks/useOnboarding.ts +0 -20
- package/src/web/hooks/useProviders.ts +0 -75
- package/src/web/icon.png +0 -0
- package/src/web/index.html +0 -16
- package/src/web/styles.css +0 -118
- package/src/web/themes.ts +0 -162
- package/src/web/types.ts +0 -298
package/src/routes/api/system.ts
DELETED
|
@@ -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
|
-
}
|
package/src/routes/api/tests.ts
DELETED
|
@@ -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
|
-
}
|