apteva 0.4.12 → 0.4.15
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/dist/App.jdzxkzm1.js +228 -0
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/auth/middleware.ts +42 -26
- package/src/crypto.ts +2 -2
- package/src/db-tests.ts +174 -0
- package/src/db.ts +302 -5
- package/src/integrations/agentdojo.ts +168 -42
- package/src/mcp-client.ts +15 -9
- package/src/mcp-platform.ts +160 -0
- package/src/openapi.ts +416 -21
- package/src/routes/api/agent-utils.ts +2 -2
- package/src/routes/api/api-keys.ts +95 -0
- package/src/routes/api/integrations.ts +1 -1
- package/src/routes/api/mcp.ts +2 -2
- package/src/routes/api/system.ts +10 -1
- package/src/routes/api/tests.ts +148 -0
- package/src/routes/api.ts +4 -0
- package/src/server.ts +2 -1
- package/src/test-runner.ts +598 -0
- package/src/web/App.tsx +23 -10
- package/src/web/components/agents/AgentPanel.tsx +4 -8
- package/src/web/components/common/Icons.tsx +8 -0
- package/src/web/components/dashboard/Dashboard.tsx +2 -4
- package/src/web/components/index.ts +1 -0
- package/src/web/components/layout/Sidebar.tsx +7 -1
- package/src/web/components/settings/SettingsPage.tsx +288 -5
- package/src/web/components/skills/SkillsPage.tsx +1 -1
- package/src/web/components/tasks/TasksPage.tsx +8 -3
- package/src/web/components/telemetry/TelemetryPage.tsx +2 -5
- package/src/web/components/tests/TestsPage.tsx +580 -0
- package/src/web/context/index.ts +1 -1
- package/src/web/types.ts +1 -1
- package/dist/App.9ph8javh.js +0 -228
package/src/routes/api/mcp.ts
CHANGED
|
@@ -243,8 +243,8 @@ export async function handleMcpRoutes(
|
|
|
243
243
|
return json({ error: "No command or package specified" }, 400);
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
//
|
|
247
|
-
const port = await getNextPort();
|
|
246
|
+
// Use permanently assigned port from DB, fallback to dynamic
|
|
247
|
+
const port = server.port || await getNextPort();
|
|
248
248
|
|
|
249
249
|
console.log(`Starting MCP server ${server.name}...`);
|
|
250
250
|
console.log(` Command: ${cmd.join(" ")}`);
|
package/src/routes/api/system.ts
CHANGED
|
@@ -126,8 +126,17 @@ export async function handleSystemRoutes(
|
|
|
126
126
|
if (path === "/api/tasks" && method === "GET") {
|
|
127
127
|
const url = new URL(req.url);
|
|
128
128
|
const status = url.searchParams.get("status") || "all";
|
|
129
|
+
const projectId = url.searchParams.get("project_id");
|
|
130
|
+
|
|
131
|
+
let runningAgents = AgentDB.findAll().filter(a => a.status === "running" && a.port);
|
|
132
|
+
|
|
133
|
+
// Filter agents by project if requested
|
|
134
|
+
if (projectId === "unassigned") {
|
|
135
|
+
runningAgents = runningAgents.filter(a => !a.project_id);
|
|
136
|
+
} else if (projectId) {
|
|
137
|
+
runningAgents = runningAgents.filter(a => a.project_id === projectId);
|
|
138
|
+
}
|
|
129
139
|
|
|
130
|
-
const runningAgents = AgentDB.findAll().filter(a => a.status === "running" && a.port);
|
|
131
140
|
const allTasks: any[] = [];
|
|
132
141
|
|
|
133
142
|
for (const agent of runningAgents) {
|
|
@@ -0,0 +1,148 @@
|
|
|
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
|
+
}
|
package/src/routes/api.ts
CHANGED
|
@@ -10,6 +10,8 @@ import { handleSkillRoutes } from "./api/skills";
|
|
|
10
10
|
import { handleIntegrationRoutes } from "./api/integrations";
|
|
11
11
|
import { handleMetaAgentRoutes } from "./api/meta-agent";
|
|
12
12
|
import { handleTelemetryRoutes } from "./api/telemetry";
|
|
13
|
+
import { handleTestRoutes } from "./api/tests";
|
|
14
|
+
import { handleApiKeyRoutes } from "./api/api-keys";
|
|
13
15
|
import { handlePlatformMcpRequest } from "../mcp-platform";
|
|
14
16
|
|
|
15
17
|
// Re-export for backward compatibility (server.ts dynamic import)
|
|
@@ -29,6 +31,7 @@ export async function handleApiRequest(
|
|
|
29
31
|
|
|
30
32
|
return (
|
|
31
33
|
(await handleSystemRoutes(req, path, method, authContext)) ??
|
|
34
|
+
(await handleApiKeyRoutes(req, path, method, authContext)) ?? // Must be before provider routes to handle /api/keys/personal
|
|
32
35
|
(await handleProviderRoutes(req, path, method, authContext)) ??
|
|
33
36
|
(await handleUserRoutes(req, path, method, authContext)) ??
|
|
34
37
|
(await handleProjectRoutes(req, path, method, authContext)) ??
|
|
@@ -38,6 +41,7 @@ export async function handleApiRequest(
|
|
|
38
41
|
(await handleIntegrationRoutes(req, path, method)) ??
|
|
39
42
|
(await handleMetaAgentRoutes(req, path, method)) ??
|
|
40
43
|
(await handleTelemetryRoutes(req, path, method)) ??
|
|
44
|
+
(await handleTestRoutes(req, path, method)) ??
|
|
41
45
|
json({ error: "Not found" }, 404)
|
|
42
46
|
);
|
|
43
47
|
}
|
package/src/server.ts
CHANGED
|
@@ -456,7 +456,8 @@ if (hasRestarts) {
|
|
|
456
456
|
continue;
|
|
457
457
|
}
|
|
458
458
|
|
|
459
|
-
|
|
459
|
+
// Use permanently assigned port from DB, fallback to dynamic
|
|
460
|
+
const port = server.port || await getNextPort();
|
|
460
461
|
const result = await startMcpProcess(server.id, cmd, serverEnv, port);
|
|
461
462
|
|
|
462
463
|
if (result.success) {
|