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
|
@@ -1,424 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "fs";
|
|
2
|
-
import { json, debug } from "./helpers";
|
|
3
|
-
|
|
4
|
-
// Detect if running inside a Docker container
|
|
5
|
-
async function isRunningInDocker(): Promise<boolean> {
|
|
6
|
-
try {
|
|
7
|
-
return existsSync("/.dockerenv") || existsSync("/run/.containerenv");
|
|
8
|
-
} catch {
|
|
9
|
-
return false;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
import { startAgentProcess, setAgentStatus } from "./agent-utils";
|
|
13
|
-
import { AgentDB, UserDB } from "../../db";
|
|
14
|
-
import { ProviderKeys, Onboarding, getProvidersWithStatus, PROVIDERS, type ProviderId } from "../../providers";
|
|
15
|
-
import { createUser } from "../../auth";
|
|
16
|
-
import { agentProcesses } from "../../server";
|
|
17
|
-
|
|
18
|
-
export async function handleProviderRoutes(
|
|
19
|
-
req: Request,
|
|
20
|
-
path: string,
|
|
21
|
-
method: string,
|
|
22
|
-
authContext?: unknown,
|
|
23
|
-
): Promise<Response | null> {
|
|
24
|
-
// ==================== PROVIDERS ====================
|
|
25
|
-
|
|
26
|
-
// GET /api/providers - List supported providers and models with key status
|
|
27
|
-
if (path === "/api/providers" && method === "GET") {
|
|
28
|
-
const providers = getProvidersWithStatus();
|
|
29
|
-
return json({ providers });
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// GET /api/providers/ollama/models - Fetch available models from Ollama
|
|
33
|
-
if (path === "/api/providers/ollama/models" && method === "GET") {
|
|
34
|
-
// Get configured Ollama base URL or use default
|
|
35
|
-
const ollamaUrl = ProviderKeys.getDecrypted("ollama") || "http://localhost:11434";
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
const response = await fetch(`${ollamaUrl}/api/tags`, {
|
|
39
|
-
method: "GET",
|
|
40
|
-
headers: { "Accept": "application/json" },
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
if (!response.ok) {
|
|
44
|
-
return json({ error: "Failed to connect to Ollama", models: [] }, 200);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const data = await response.json() as { models?: Array<{ name: string; size: number; modified_at: string }> };
|
|
48
|
-
const models = (data.models || []).map((m: { name: string; size: number }) => ({
|
|
49
|
-
value: m.name,
|
|
50
|
-
label: m.name,
|
|
51
|
-
size: m.size,
|
|
52
|
-
}));
|
|
53
|
-
|
|
54
|
-
return json({ models, connected: true });
|
|
55
|
-
} catch (err) {
|
|
56
|
-
// Ollama not running or not reachable
|
|
57
|
-
return json({
|
|
58
|
-
error: "Ollama not reachable. Make sure Ollama is running.",
|
|
59
|
-
models: [],
|
|
60
|
-
connected: false,
|
|
61
|
-
}, 200);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// GET /api/providers/ollama/status - Check if Ollama is running
|
|
66
|
-
if (path === "/api/providers/ollama/status" && method === "GET") {
|
|
67
|
-
const ollamaUrl = ProviderKeys.getDecrypted("ollama") || "http://localhost:11434";
|
|
68
|
-
const isDocker = await isRunningInDocker();
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
const response = await fetch(`${ollamaUrl}/api/tags`, {
|
|
72
|
-
method: "GET",
|
|
73
|
-
signal: AbortSignal.timeout(3000),
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
if (response.ok) {
|
|
77
|
-
const data = await response.json() as { models?: Array<{ name: string }> };
|
|
78
|
-
return json({
|
|
79
|
-
connected: true,
|
|
80
|
-
url: ollamaUrl,
|
|
81
|
-
modelCount: data.models?.length || 0,
|
|
82
|
-
isDocker,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
return json({ connected: false, url: ollamaUrl, error: "Ollama not responding", isDocker });
|
|
86
|
-
} catch {
|
|
87
|
-
return json({ connected: false, url: ollamaUrl, error: "Ollama not reachable", isDocker });
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// POST /api/providers/ollama/install - Install Ollama automatically
|
|
92
|
-
if (path === "/api/providers/ollama/install" && method === "POST") {
|
|
93
|
-
// Don't allow install inside Docker containers
|
|
94
|
-
if (await isRunningInDocker()) {
|
|
95
|
-
return json({ success: false, error: "Cannot install Ollama inside a Docker container. Configure an external Ollama URL instead." }, 400);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Only supported on Linux/macOS
|
|
99
|
-
const platform = process.platform;
|
|
100
|
-
if (platform !== "linux" && platform !== "darwin") {
|
|
101
|
-
return json({ success: false, error: "Auto-install is only supported on Linux and macOS. Please download from https://ollama.com/download" }, 400);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Check if already installed
|
|
105
|
-
try {
|
|
106
|
-
const which = Bun.spawnSync(["which", "ollama"]);
|
|
107
|
-
if (which.exitCode === 0) {
|
|
108
|
-
// Already installed, just make sure it's running
|
|
109
|
-
Bun.spawn(["ollama", "serve"], { stdout: "ignore", stderr: "ignore" });
|
|
110
|
-
// Wait a moment for it to start
|
|
111
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
112
|
-
return json({ success: true, message: "Ollama is already installed", alreadyInstalled: true });
|
|
113
|
-
}
|
|
114
|
-
} catch { /* not installed */ }
|
|
115
|
-
|
|
116
|
-
// Install Ollama using official install script
|
|
117
|
-
try {
|
|
118
|
-
const proc = Bun.spawnSync(["bash", "-c", "curl -fsSL https://ollama.com/install.sh | sh"], {
|
|
119
|
-
timeout: 120_000, // 2 minute timeout
|
|
120
|
-
stderr: "pipe",
|
|
121
|
-
stdout: "pipe",
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
if (proc.exitCode !== 0) {
|
|
125
|
-
const stderr = proc.stderr.toString().trim();
|
|
126
|
-
return json({ success: false, error: `Installation failed: ${stderr || "Unknown error"}` }, 500);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Start Ollama serve in background
|
|
130
|
-
Bun.spawn(["ollama", "serve"], { stdout: "ignore", stderr: "ignore" });
|
|
131
|
-
|
|
132
|
-
// Wait for it to come up
|
|
133
|
-
await new Promise(r => setTimeout(r, 3000));
|
|
134
|
-
|
|
135
|
-
// Verify it's running
|
|
136
|
-
try {
|
|
137
|
-
const check = await fetch("http://localhost:11434/api/tags", { signal: AbortSignal.timeout(3000) });
|
|
138
|
-
if (check.ok) {
|
|
139
|
-
return json({ success: true, message: "Ollama installed and running" });
|
|
140
|
-
}
|
|
141
|
-
} catch { /* may still be starting */ }
|
|
142
|
-
|
|
143
|
-
return json({ success: true, message: "Ollama installed. It may take a moment to start." });
|
|
144
|
-
} catch (err: any) {
|
|
145
|
-
return json({ success: false, error: `Installation failed: ${err.message}` }, 500);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// POST /api/providers/ollama/pull - Pull a model
|
|
150
|
-
if (path === "/api/providers/ollama/pull" && method === "POST") {
|
|
151
|
-
const body = await req.json() as { model?: string };
|
|
152
|
-
const model = body.model;
|
|
153
|
-
if (!model) {
|
|
154
|
-
return json({ error: "Model name required" }, 400);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
const ollamaUrl = ProviderKeys.getDecrypted("ollama") || "http://localhost:11434";
|
|
159
|
-
const response = await fetch(`${ollamaUrl}/api/pull`, {
|
|
160
|
-
method: "POST",
|
|
161
|
-
headers: { "Content-Type": "application/json" },
|
|
162
|
-
body: JSON.stringify({ name: model }),
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
if (!response.ok) {
|
|
166
|
-
return json({ success: false, error: "Failed to start model pull" }, 500);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return json({ success: true, message: `Pulling ${model}...` });
|
|
170
|
-
} catch (err: any) {
|
|
171
|
-
return json({ success: false, error: `Failed to pull model: ${err.message}` }, 500);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// ==================== LOCAL VOICE PROVIDERS ====================
|
|
176
|
-
|
|
177
|
-
// GET /api/providers/:id/status - Check if a local voice provider is running
|
|
178
|
-
const localVoiceStatusMatch = path.match(/^\/api\/providers\/(speaches|whisper_cpp|kokoro|piper|fish_speech)\/status$/);
|
|
179
|
-
if (localVoiceStatusMatch && method === "GET") {
|
|
180
|
-
const providerId = localVoiceStatusMatch[1];
|
|
181
|
-
const providerDef = PROVIDERS[providerId as ProviderId];
|
|
182
|
-
const baseUrl = ProviderKeys.getDecrypted(providerId) ||
|
|
183
|
-
("defaultBaseUrl" in providerDef ? (providerDef as any).defaultBaseUrl : "");
|
|
184
|
-
const isDocker = await isRunningInDocker();
|
|
185
|
-
|
|
186
|
-
const healthEndpoints: Record<string, string> = {
|
|
187
|
-
speaches: "/v1/models",
|
|
188
|
-
whisper_cpp: "/",
|
|
189
|
-
kokoro: "/v1/models",
|
|
190
|
-
piper: "/api/voices",
|
|
191
|
-
fish_speech: "/v1/models",
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
try {
|
|
195
|
-
const response = await fetch(`${baseUrl}${healthEndpoints[providerId]}`, {
|
|
196
|
-
method: "GET",
|
|
197
|
-
signal: AbortSignal.timeout(3000),
|
|
198
|
-
});
|
|
199
|
-
if (response.ok) {
|
|
200
|
-
return json({ connected: true, url: baseUrl, isDocker });
|
|
201
|
-
}
|
|
202
|
-
return json({ connected: false, url: baseUrl, error: "Not responding", isDocker });
|
|
203
|
-
} catch {
|
|
204
|
-
return json({ connected: false, url: baseUrl, error: "Not reachable", isDocker });
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// GET /api/providers/:id/models - Fetch available models from a local voice provider
|
|
209
|
-
const localVoiceModelsMatch = path.match(/^\/api\/providers\/(speaches|kokoro|fish_speech)\/models$/);
|
|
210
|
-
if (localVoiceModelsMatch && method === "GET") {
|
|
211
|
-
const providerId = localVoiceModelsMatch[1];
|
|
212
|
-
const providerDef = PROVIDERS[providerId as ProviderId];
|
|
213
|
-
const baseUrl = ProviderKeys.getDecrypted(providerId) ||
|
|
214
|
-
("defaultBaseUrl" in providerDef ? (providerDef as any).defaultBaseUrl : "");
|
|
215
|
-
|
|
216
|
-
try {
|
|
217
|
-
const response = await fetch(`${baseUrl}/v1/models`, {
|
|
218
|
-
method: "GET",
|
|
219
|
-
headers: { "Accept": "application/json" },
|
|
220
|
-
signal: AbortSignal.timeout(5000),
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
if (!response.ok) {
|
|
224
|
-
return json({ error: "Failed to connect", models: [], connected: false }, 200);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const data = await response.json() as { data?: Array<{ id: string }> };
|
|
228
|
-
const models = (data.data || []).map((m: { id: string }) => ({
|
|
229
|
-
value: m.id,
|
|
230
|
-
label: m.id,
|
|
231
|
-
}));
|
|
232
|
-
|
|
233
|
-
return json({ models, connected: true });
|
|
234
|
-
} catch {
|
|
235
|
-
return json({ error: "Not reachable", models: [], connected: false }, 200);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// ==================== ONBOARDING ====================
|
|
240
|
-
|
|
241
|
-
// GET /api/onboarding/status - Check onboarding status
|
|
242
|
-
if (path === "/api/onboarding/status" && method === "GET") {
|
|
243
|
-
return json(Onboarding.getStatus());
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// POST /api/onboarding/complete - Mark onboarding as complete
|
|
247
|
-
if (path === "/api/onboarding/complete" && method === "POST") {
|
|
248
|
-
Onboarding.complete();
|
|
249
|
-
return json({ success: true });
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// POST /api/onboarding/reset - Reset onboarding (for testing)
|
|
253
|
-
if (path === "/api/onboarding/reset" && method === "POST") {
|
|
254
|
-
Onboarding.reset();
|
|
255
|
-
return json({ success: true });
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// POST /api/onboarding/user - Create first user during onboarding
|
|
259
|
-
// This endpoint only works when no users exist (enforced by middleware)
|
|
260
|
-
if (path === "/api/onboarding/user" && method === "POST") {
|
|
261
|
-
debug("POST /api/onboarding/user");
|
|
262
|
-
// Double-check no users exist
|
|
263
|
-
if (UserDB.hasUsers()) {
|
|
264
|
-
debug("Users already exist");
|
|
265
|
-
return json({ error: "Users already exist" }, 403);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
try {
|
|
269
|
-
const body = await req.json();
|
|
270
|
-
debug("Onboarding body:", JSON.stringify(body));
|
|
271
|
-
const { username, password, email } = body;
|
|
272
|
-
|
|
273
|
-
if (!username || !password) {
|
|
274
|
-
debug("Missing username or password");
|
|
275
|
-
return json({ error: "Username and password are required" }, 400);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Create first user as admin
|
|
279
|
-
debug("Creating user:", username);
|
|
280
|
-
const result = await createUser({
|
|
281
|
-
username,
|
|
282
|
-
password,
|
|
283
|
-
email: email || undefined, // Optional, for password recovery
|
|
284
|
-
role: "admin",
|
|
285
|
-
});
|
|
286
|
-
debug("Create user result:", result.success, result.error);
|
|
287
|
-
|
|
288
|
-
if (!result.success) {
|
|
289
|
-
return json({ error: result.error }, 400);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return json({
|
|
293
|
-
success: true,
|
|
294
|
-
user: {
|
|
295
|
-
id: result.user!.id,
|
|
296
|
-
username: result.user!.username,
|
|
297
|
-
role: result.user!.role,
|
|
298
|
-
},
|
|
299
|
-
}, 201);
|
|
300
|
-
} catch (e) {
|
|
301
|
-
debug("Onboarding error:", e);
|
|
302
|
-
return json({ error: "Invalid request body" }, 400);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// ==================== API KEYS ====================
|
|
307
|
-
|
|
308
|
-
// GET /api/keys - List all configured provider keys (without actual keys)
|
|
309
|
-
if (path === "/api/keys" && method === "GET") {
|
|
310
|
-
return json({ keys: ProviderKeys.getAll() });
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// GET /api/keys/:provider - Get all keys for a provider
|
|
314
|
-
const getProviderKeysMatch = path.match(/^\/api\/keys\/([^/]+)$/);
|
|
315
|
-
|
|
316
|
-
// POST /api/keys/:provider/test - Test an API key (must match before generic :provider routes)
|
|
317
|
-
const testKeyMatch = path.match(/^\/api\/keys\/([^/]+)\/test$/);
|
|
318
|
-
if (testKeyMatch && method === "POST") {
|
|
319
|
-
const providerId = testKeyMatch[1];
|
|
320
|
-
|
|
321
|
-
// Validate provider exists
|
|
322
|
-
if (!PROVIDERS[providerId as ProviderId]) {
|
|
323
|
-
return json({ error: "Unknown provider" }, 400);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
try {
|
|
327
|
-
const body = await req.json().catch(() => ({}));
|
|
328
|
-
const { key } = body as { key?: string };
|
|
329
|
-
|
|
330
|
-
// Test with provided key or stored key
|
|
331
|
-
const result = await ProviderKeys.test(providerId, key);
|
|
332
|
-
return json(result);
|
|
333
|
-
} catch (e) {
|
|
334
|
-
return json({ error: "Test failed" }, 500);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// DELETE /api/keys/by-id/:id - Remove a specific API key by ID
|
|
339
|
-
const deleteKeyByIdMatch = path.match(/^\/api\/keys\/by-id\/([^/]+)$/);
|
|
340
|
-
if (deleteKeyByIdMatch && method === "DELETE") {
|
|
341
|
-
const keyId = deleteKeyByIdMatch[1];
|
|
342
|
-
const deleted = ProviderKeys.deleteById(keyId);
|
|
343
|
-
return json({ success: deleted });
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
if (getProviderKeysMatch && method === "GET") {
|
|
347
|
-
const providerId = getProviderKeysMatch[1];
|
|
348
|
-
const keys = ProviderKeys.getAllByProvider(providerId);
|
|
349
|
-
return json({ keys });
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// POST /api/keys/:provider - Save an API key for a provider
|
|
353
|
-
if (getProviderKeysMatch && method === "POST") {
|
|
354
|
-
const providerId = getProviderKeysMatch[1];
|
|
355
|
-
|
|
356
|
-
// Validate provider exists
|
|
357
|
-
if (!PROVIDERS[providerId as ProviderId]) {
|
|
358
|
-
return json({ error: "Unknown provider" }, 400);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
try {
|
|
362
|
-
const body = await req.json();
|
|
363
|
-
const { key, project_id, name } = body as { key?: string; project_id?: string | null; name?: string | null };
|
|
364
|
-
|
|
365
|
-
if (!key) {
|
|
366
|
-
return json({ error: "API key is required" }, 400);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
const result = await ProviderKeys.save(providerId, key, project_id || null, name || null);
|
|
370
|
-
if (!result.success) {
|
|
371
|
-
return json({ error: result.error }, 400);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// Restart any running agents that use this provider (including meta agent)
|
|
375
|
-
const runningAgents = AgentDB.findAll().filter(
|
|
376
|
-
a => a.status === "running" && a.provider === providerId
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
// Stop all agents first
|
|
380
|
-
for (const agent of runningAgents) {
|
|
381
|
-
const agentProc = agentProcesses.get(agent.id);
|
|
382
|
-
if (agentProc) {
|
|
383
|
-
agentProc.proc.kill();
|
|
384
|
-
agentProcesses.delete(agent.id);
|
|
385
|
-
}
|
|
386
|
-
setAgentStatus(agent.id, "stopped", "provider_restart");
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// Wait once for ports to be released
|
|
390
|
-
if (runningAgents.length > 0) {
|
|
391
|
-
await new Promise(r => setTimeout(r, 500));
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Restart all agents in parallel
|
|
395
|
-
const restartResults = await Promise.all(
|
|
396
|
-
runningAgents.map(async (agent) => {
|
|
397
|
-
try {
|
|
398
|
-
const startResult = await startAgentProcess(agent, { silent: true });
|
|
399
|
-
return { id: agent.id, name: agent.name, success: startResult.success, error: startResult.error };
|
|
400
|
-
} catch (e) {
|
|
401
|
-
return { id: agent.id, name: agent.name, success: false, error: String(e) };
|
|
402
|
-
}
|
|
403
|
-
})
|
|
404
|
-
);
|
|
405
|
-
|
|
406
|
-
return json({
|
|
407
|
-
success: true,
|
|
408
|
-
message: "API key saved successfully",
|
|
409
|
-
restartedAgents: restartResults.length > 0 ? restartResults : undefined,
|
|
410
|
-
});
|
|
411
|
-
} catch (e) {
|
|
412
|
-
return json({ error: "Invalid request body" }, 400);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// DELETE /api/keys/:provider - Remove a global API key
|
|
417
|
-
if (getProviderKeysMatch && method === "DELETE") {
|
|
418
|
-
const providerId = getProviderKeysMatch[1];
|
|
419
|
-
const deleted = ProviderKeys.delete(providerId);
|
|
420
|
-
return json({ success: deleted });
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
return null;
|
|
424
|
-
}
|