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,518 +0,0 @@
|
|
|
1
|
-
import { json } from "./helpers";
|
|
2
|
-
import { ProviderKeys } from "../../providers";
|
|
3
|
-
import { SubscriptionDB, SettingsDB } from "../../db";
|
|
4
|
-
import {
|
|
5
|
-
getTriggerProvider,
|
|
6
|
-
getTriggerProviderIds,
|
|
7
|
-
registerTriggerProvider,
|
|
8
|
-
} from "../../triggers";
|
|
9
|
-
import { ComposioTriggerProvider } from "../../triggers/composio";
|
|
10
|
-
import { AgentDojoTriggerProvider } from "../../triggers/agentdojo";
|
|
11
|
-
import type { AuthContext } from "../../auth/middleware";
|
|
12
|
-
|
|
13
|
-
// Register trigger providers on module load
|
|
14
|
-
registerTriggerProvider(ComposioTriggerProvider);
|
|
15
|
-
registerTriggerProvider(AgentDojoTriggerProvider);
|
|
16
|
-
|
|
17
|
-
export async function handleTriggerRoutes(
|
|
18
|
-
req: Request,
|
|
19
|
-
path: string,
|
|
20
|
-
method: string,
|
|
21
|
-
authContext?: AuthContext,
|
|
22
|
-
): Promise<Response | null> {
|
|
23
|
-
|
|
24
|
-
// GET /api/settings/instance-url
|
|
25
|
-
if (path === "/api/settings/instance-url" && method === "GET") {
|
|
26
|
-
const url = SettingsDB.get("instance_url") || "";
|
|
27
|
-
return json({ instance_url: url });
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// PUT /api/settings/instance-url
|
|
31
|
-
if (path === "/api/settings/instance-url" && method === "PUT") {
|
|
32
|
-
try {
|
|
33
|
-
const body = await req.json();
|
|
34
|
-
const { instance_url } = body;
|
|
35
|
-
if (instance_url) {
|
|
36
|
-
SettingsDB.set("instance_url", instance_url.replace(/\/+$/, "")); // strip trailing slash
|
|
37
|
-
} else {
|
|
38
|
-
SettingsDB.set("instance_url", "");
|
|
39
|
-
}
|
|
40
|
-
return json({ success: true, instance_url: SettingsDB.get("instance_url") });
|
|
41
|
-
} catch (e: any) {
|
|
42
|
-
return json({ error: e.message || "Failed to save instance URL" }, 500);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// GET /api/triggers/providers - List available trigger providers
|
|
47
|
-
if (path === "/api/triggers/providers" && method === "GET") {
|
|
48
|
-
const url = new URL(req.url);
|
|
49
|
-
const projectId = url.searchParams.get("project_id") || null;
|
|
50
|
-
const providerIds = getTriggerProviderIds();
|
|
51
|
-
const providers = providerIds.map(id => {
|
|
52
|
-
const provider = getTriggerProvider(id);
|
|
53
|
-
const hasKey = !!ProviderKeys.getDecryptedForProject(id, projectId);
|
|
54
|
-
return { id, name: provider?.name || id, connected: hasKey };
|
|
55
|
-
});
|
|
56
|
-
return json({ providers });
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ============ Trigger Type Browsing ============
|
|
60
|
-
|
|
61
|
-
// GET /api/triggers/types?provider=composio&toolkit_slugs=github,gmail
|
|
62
|
-
if (path === "/api/triggers/types" && method === "GET") {
|
|
63
|
-
const url = new URL(req.url);
|
|
64
|
-
const providerId = url.searchParams.get("provider") || "composio";
|
|
65
|
-
const toolkitSlugsParam = url.searchParams.get("toolkit_slugs");
|
|
66
|
-
const toolkitSlugs = toolkitSlugsParam ? toolkitSlugsParam.split(",") : undefined;
|
|
67
|
-
const projectId = url.searchParams.get("project_id") || null;
|
|
68
|
-
|
|
69
|
-
const provider = getTriggerProvider(providerId);
|
|
70
|
-
console.log(`[triggers/types] provider=${providerId}, toolkitSlugs=${toolkitSlugsParam}, projectId=${projectId}, providerFound=${!!provider}`);
|
|
71
|
-
if (!provider) {
|
|
72
|
-
return json({ error: `Unknown trigger provider: ${providerId}` }, 404);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
76
|
-
console.log(`[triggers/types] apiKey found: ${!!apiKey}, length: ${apiKey?.length || 0}, first4: ${apiKey?.substring(0, 4) || 'none'}`);
|
|
77
|
-
if (!apiKey) {
|
|
78
|
-
console.log(`[triggers/types] NO API KEY for ${providerId} (projectId=${projectId})`);
|
|
79
|
-
return json({ error: `${provider.name} API key not configured`, types: [] }, 200);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
const types = await provider.listTriggerTypes(apiKey, toolkitSlugs);
|
|
84
|
-
console.log(`[triggers/types] Got ${types.length} types from ${providerId}`);
|
|
85
|
-
return json({ types });
|
|
86
|
-
} catch (e) {
|
|
87
|
-
console.error(`[triggers/types] Failed to list trigger types from ${providerId}:`, e);
|
|
88
|
-
return json({ error: "Failed to fetch trigger types" }, 500);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// GET /api/triggers/types/:slug?provider=composio
|
|
93
|
-
const typeMatch = path.match(/^\/api\/triggers\/types\/([^/]+)$/);
|
|
94
|
-
if (typeMatch && method === "GET") {
|
|
95
|
-
const slug = typeMatch[1];
|
|
96
|
-
const url = new URL(req.url);
|
|
97
|
-
const providerId = url.searchParams.get("provider") || "composio";
|
|
98
|
-
const projectId = url.searchParams.get("project_id") || null;
|
|
99
|
-
|
|
100
|
-
const provider = getTriggerProvider(providerId);
|
|
101
|
-
if (!provider) {
|
|
102
|
-
return json({ error: `Unknown trigger provider: ${providerId}` }, 404);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
106
|
-
if (!apiKey) {
|
|
107
|
-
return json({ error: `${provider.name} API key not configured` }, 401);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
const triggerType = await provider.getTriggerType(apiKey, slug);
|
|
112
|
-
if (!triggerType) {
|
|
113
|
-
return json({ error: "Trigger type not found" }, 404);
|
|
114
|
-
}
|
|
115
|
-
return json({ type: triggerType });
|
|
116
|
-
} catch (e) {
|
|
117
|
-
console.error(`Failed to get trigger type ${slug}:`, e);
|
|
118
|
-
return json({ error: "Failed to fetch trigger type" }, 500);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// ============ Trigger Instance Management ============
|
|
123
|
-
|
|
124
|
-
// GET /api/triggers?provider=composio
|
|
125
|
-
if (path === "/api/triggers" && method === "GET") {
|
|
126
|
-
const url = new URL(req.url);
|
|
127
|
-
const providerId = url.searchParams.get("provider") || "composio";
|
|
128
|
-
const projectId = url.searchParams.get("project_id") || null;
|
|
129
|
-
|
|
130
|
-
const provider = getTriggerProvider(providerId);
|
|
131
|
-
if (!provider) {
|
|
132
|
-
return json({ error: `Unknown trigger provider: ${providerId}` }, 404);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
136
|
-
if (!apiKey) {
|
|
137
|
-
return json({ error: `${provider.name} API key not configured`, triggers: [] }, 200);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
const triggers = await provider.listTriggers(apiKey);
|
|
142
|
-
return json({ triggers });
|
|
143
|
-
} catch (e) {
|
|
144
|
-
console.error(`Failed to list triggers from ${providerId}:`, e);
|
|
145
|
-
return json({ error: "Failed to fetch triggers" }, 500);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// POST /api/triggers?provider=composio
|
|
150
|
-
if (path === "/api/triggers" && method === "POST") {
|
|
151
|
-
const url = new URL(req.url);
|
|
152
|
-
const providerId = url.searchParams.get("provider") || "composio";
|
|
153
|
-
const projectId = url.searchParams.get("project_id") || null;
|
|
154
|
-
|
|
155
|
-
const provider = getTriggerProvider(providerId);
|
|
156
|
-
if (!provider) {
|
|
157
|
-
return json({ error: `Unknown trigger provider: ${providerId}` }, 404);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
161
|
-
if (!apiKey) {
|
|
162
|
-
return json({ error: `${provider.name} API key not configured` }, 401);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
const body = await req.json();
|
|
167
|
-
const { slug, connectedAccountId, config } = body;
|
|
168
|
-
|
|
169
|
-
if (!slug || !connectedAccountId) {
|
|
170
|
-
return json({ error: "slug and connectedAccountId are required" }, 400);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const result = await provider.createTrigger(apiKey, slug, connectedAccountId, config);
|
|
174
|
-
|
|
175
|
-
// Also create a local subscription for webhook routing
|
|
176
|
-
if (config?.agent_id && result.triggerId) {
|
|
177
|
-
const triggerSlug = config.server ? `${config.server}:${slug.replace(/^.*?-/, '')}` : slug;
|
|
178
|
-
SubscriptionDB.create({
|
|
179
|
-
trigger_slug: slug,
|
|
180
|
-
trigger_instance_id: result.triggerId,
|
|
181
|
-
agent_id: config.agent_id,
|
|
182
|
-
enabled: true,
|
|
183
|
-
project_id: projectId,
|
|
184
|
-
});
|
|
185
|
-
console.log(`[triggers] Created local subscription: ${slug} (instance=${result.triggerId}) → agent ${config.agent_id}`);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return json(result, 201);
|
|
189
|
-
} catch (e: any) {
|
|
190
|
-
console.error(`Failed to create trigger:`, e);
|
|
191
|
-
return json({ error: e.message || "Failed to create trigger" }, 500);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// POST /api/triggers/:id/enable?provider=composio
|
|
196
|
-
const enableMatch = path.match(/^\/api\/triggers\/([^/]+)\/enable$/);
|
|
197
|
-
if (enableMatch && method === "POST") {
|
|
198
|
-
const triggerId = enableMatch[1];
|
|
199
|
-
const url = new URL(req.url);
|
|
200
|
-
const providerId = url.searchParams.get("provider") || "composio";
|
|
201
|
-
const projectId = url.searchParams.get("project_id") || null;
|
|
202
|
-
|
|
203
|
-
const provider = getTriggerProvider(providerId);
|
|
204
|
-
if (!provider) {
|
|
205
|
-
return json({ error: `Unknown trigger provider: ${providerId}` }, 404);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
209
|
-
if (!apiKey) {
|
|
210
|
-
return json({ error: `${provider.name} API key not configured` }, 401);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
try {
|
|
214
|
-
const success = await provider.enableTrigger(apiKey, triggerId);
|
|
215
|
-
return json({ success });
|
|
216
|
-
} catch (e) {
|
|
217
|
-
console.error(`Failed to enable trigger ${triggerId}:`, e);
|
|
218
|
-
return json({ error: "Failed to enable trigger" }, 500);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// POST /api/triggers/:id/disable?provider=composio
|
|
223
|
-
const disableMatch = path.match(/^\/api\/triggers\/([^/]+)\/disable$/);
|
|
224
|
-
if (disableMatch && method === "POST") {
|
|
225
|
-
const triggerId = disableMatch[1];
|
|
226
|
-
const url = new URL(req.url);
|
|
227
|
-
const providerId = url.searchParams.get("provider") || "composio";
|
|
228
|
-
const projectId = url.searchParams.get("project_id") || null;
|
|
229
|
-
|
|
230
|
-
const provider = getTriggerProvider(providerId);
|
|
231
|
-
if (!provider) {
|
|
232
|
-
return json({ error: `Unknown trigger provider: ${providerId}` }, 404);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
236
|
-
if (!apiKey) {
|
|
237
|
-
return json({ error: `${provider.name} API key not configured` }, 401);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
try {
|
|
241
|
-
const success = await provider.disableTrigger(apiKey, triggerId);
|
|
242
|
-
return json({ success });
|
|
243
|
-
} catch (e) {
|
|
244
|
-
console.error(`Failed to disable trigger ${triggerId}:`, e);
|
|
245
|
-
return json({ error: "Failed to disable trigger" }, 500);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// DELETE /api/triggers/:id?provider=composio
|
|
250
|
-
const deleteMatch = path.match(/^\/api\/triggers\/([^/]+)$/);
|
|
251
|
-
if (deleteMatch && method === "DELETE") {
|
|
252
|
-
const triggerId = deleteMatch[1];
|
|
253
|
-
const url = new URL(req.url);
|
|
254
|
-
const providerId = url.searchParams.get("provider") || "composio";
|
|
255
|
-
const projectId = url.searchParams.get("project_id") || null;
|
|
256
|
-
|
|
257
|
-
const provider = getTriggerProvider(providerId);
|
|
258
|
-
if (!provider) {
|
|
259
|
-
return json({ error: `Unknown trigger provider: ${providerId}` }, 404);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
263
|
-
if (!apiKey) {
|
|
264
|
-
return json({ error: `${provider.name} API key not configured` }, 401);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
try {
|
|
268
|
-
const success = await provider.deleteTrigger(apiKey, triggerId);
|
|
269
|
-
|
|
270
|
-
// Also clean up any local subscriptions referencing this trigger instance
|
|
271
|
-
const localSubs = SubscriptionDB.findByTriggerInstanceId(triggerId);
|
|
272
|
-
let localDeleted = 0;
|
|
273
|
-
for (const sub of localSubs) {
|
|
274
|
-
SubscriptionDB.delete(sub.id);
|
|
275
|
-
localDeleted++;
|
|
276
|
-
}
|
|
277
|
-
if (localDeleted > 0) {
|
|
278
|
-
console.log(`[triggers] Cleaned up ${localDeleted} local subscription(s) for trigger ${triggerId}`);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return json({ success, localSubscriptionsDeleted: localDeleted });
|
|
282
|
-
} catch (e) {
|
|
283
|
-
console.error(`Failed to delete trigger ${triggerId}:`, e);
|
|
284
|
-
return json({ error: "Failed to delete trigger" }, 500);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// ============ Webhook Configuration ============
|
|
289
|
-
|
|
290
|
-
// POST /api/triggers/webhook/setup?provider=composio
|
|
291
|
-
if (path === "/api/triggers/webhook/setup" && method === "POST") {
|
|
292
|
-
const url = new URL(req.url);
|
|
293
|
-
const providerId = url.searchParams.get("provider") || "composio";
|
|
294
|
-
const projectId = url.searchParams.get("project_id") || null;
|
|
295
|
-
|
|
296
|
-
const provider = getTriggerProvider(providerId);
|
|
297
|
-
if (!provider) {
|
|
298
|
-
return json({ error: `Unknown trigger provider: ${providerId}` }, 404);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
302
|
-
if (!apiKey) {
|
|
303
|
-
return json({ error: `${provider.name} API key not configured` }, 401);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
try {
|
|
307
|
-
const body = await req.json();
|
|
308
|
-
const { webhookUrl } = body;
|
|
309
|
-
|
|
310
|
-
if (!webhookUrl) {
|
|
311
|
-
return json({ error: "webhookUrl is required" }, 400);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
const result = await provider.setupWebhook(apiKey, webhookUrl);
|
|
315
|
-
|
|
316
|
-
// Store the webhook secret locally for HMAC verification
|
|
317
|
-
if (result.secret) {
|
|
318
|
-
SettingsDB.set(`${providerId}_webhook_secret`, result.secret);
|
|
319
|
-
}
|
|
320
|
-
// Store the webhook URL for reference
|
|
321
|
-
SettingsDB.set(`${providerId}_webhook_url`, webhookUrl);
|
|
322
|
-
|
|
323
|
-
return json({ success: true, ...result });
|
|
324
|
-
} catch (e: any) {
|
|
325
|
-
console.error(`Failed to setup webhook for ${providerId}:`, e);
|
|
326
|
-
return json({ error: e.message || "Failed to setup webhook" }, 500);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// GET /api/triggers/webhook/status?provider=composio
|
|
331
|
-
if (path === "/api/triggers/webhook/status" && method === "GET") {
|
|
332
|
-
const url = new URL(req.url);
|
|
333
|
-
const providerId = url.searchParams.get("provider") || "composio";
|
|
334
|
-
const projectId = url.searchParams.get("project_id") || null;
|
|
335
|
-
|
|
336
|
-
const provider = getTriggerProvider(providerId);
|
|
337
|
-
if (!provider) {
|
|
338
|
-
return json({ error: `Unknown trigger provider: ${providerId}` }, 404);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
const apiKey = ProviderKeys.getDecryptedForProject(providerId, projectId);
|
|
342
|
-
if (!apiKey) {
|
|
343
|
-
return json({ error: `${provider.name} API key not configured` }, 401);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
try {
|
|
347
|
-
const config = await provider.getWebhookConfig(apiKey);
|
|
348
|
-
return json(config);
|
|
349
|
-
} catch (e) {
|
|
350
|
-
console.error(`Failed to get webhook status for ${providerId}:`, e);
|
|
351
|
-
return json({ error: "Failed to get webhook status" }, 500);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// ============ Subscription Management (local routing) ============
|
|
356
|
-
|
|
357
|
-
// GET /api/subscriptions?agent_id=xxx&project_id=xxx
|
|
358
|
-
if (path === "/api/subscriptions" && method === "GET") {
|
|
359
|
-
const url = new URL(req.url);
|
|
360
|
-
const agentId = url.searchParams.get("agent_id") || null;
|
|
361
|
-
const projectId = url.searchParams.get("project_id") || null;
|
|
362
|
-
|
|
363
|
-
let subscriptions;
|
|
364
|
-
if (agentId) {
|
|
365
|
-
subscriptions = SubscriptionDB.findByAgentId(agentId);
|
|
366
|
-
} else {
|
|
367
|
-
subscriptions = SubscriptionDB.findAll(projectId);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
return json({ subscriptions });
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// POST /api/subscriptions
|
|
374
|
-
if (path === "/api/subscriptions" && method === "POST") {
|
|
375
|
-
try {
|
|
376
|
-
const body = await req.json();
|
|
377
|
-
const { trigger_slug, trigger_instance_id, agent_id, project_id, public_url, provider: providerParam } = body;
|
|
378
|
-
|
|
379
|
-
if (!trigger_slug || !agent_id) {
|
|
380
|
-
return json({ error: "trigger_slug and agent_id are required" }, 400);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// Determine provider (default to composio for backward compat)
|
|
384
|
-
const providerId = providerParam || "composio";
|
|
385
|
-
const projId = project_id || null;
|
|
386
|
-
const provider = getTriggerProvider(providerId);
|
|
387
|
-
const apiKey = provider ? ProviderKeys.getDecryptedForProject(providerId, projId) : null;
|
|
388
|
-
|
|
389
|
-
if (provider && apiKey) {
|
|
390
|
-
const existingWebhook = SettingsDB.get(`${providerId}_webhook_url`);
|
|
391
|
-
if (!existingWebhook) {
|
|
392
|
-
try {
|
|
393
|
-
// Use instance_url setting first, then provided value, then request origin
|
|
394
|
-
const instanceUrl = SettingsDB.get("instance_url");
|
|
395
|
-
const origin = instanceUrl || public_url || new URL(req.url).origin;
|
|
396
|
-
const webhookUrl = `${origin}/api/webhooks/${providerId}`;
|
|
397
|
-
const result = await provider.setupWebhook(apiKey, webhookUrl);
|
|
398
|
-
if (result.secret) {
|
|
399
|
-
SettingsDB.set(`${providerId}_webhook_secret`, result.secret);
|
|
400
|
-
}
|
|
401
|
-
SettingsDB.set(`${providerId}_webhook_url`, webhookUrl);
|
|
402
|
-
console.log(`[subscriptions] Auto-configured ${providerId} webhook: ${webhookUrl}`);
|
|
403
|
-
} catch (e) {
|
|
404
|
-
console.warn(`[subscriptions] Failed to auto-setup ${providerId} webhook:`, e);
|
|
405
|
-
// Continue creating subscription — webhook can be set up manually later
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
const subscription = SubscriptionDB.create({
|
|
411
|
-
trigger_slug,
|
|
412
|
-
trigger_instance_id: trigger_instance_id || null,
|
|
413
|
-
agent_id,
|
|
414
|
-
enabled: true,
|
|
415
|
-
project_id: projId,
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
return json({ subscription }, 201);
|
|
419
|
-
} catch (e: any) {
|
|
420
|
-
console.error("Failed to create subscription:", e);
|
|
421
|
-
return json({ error: e.message || "Failed to create subscription" }, 500);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// GET /api/subscriptions/:id
|
|
426
|
-
const subGetMatch = path.match(/^\/api\/subscriptions\/([^/]+)$/);
|
|
427
|
-
if (subGetMatch && method === "GET") {
|
|
428
|
-
const subscription = SubscriptionDB.findById(subGetMatch[1]);
|
|
429
|
-
if (!subscription) {
|
|
430
|
-
return json({ error: "Subscription not found" }, 404);
|
|
431
|
-
}
|
|
432
|
-
return json({ subscription });
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// PUT /api/subscriptions/:id
|
|
436
|
-
const subUpdateMatch = path.match(/^\/api\/subscriptions\/([^/]+)$/);
|
|
437
|
-
if (subUpdateMatch && method === "PUT") {
|
|
438
|
-
const id = subUpdateMatch[1];
|
|
439
|
-
const existing = SubscriptionDB.findById(id);
|
|
440
|
-
if (!existing) {
|
|
441
|
-
return json({ error: "Subscription not found" }, 404);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
try {
|
|
445
|
-
const body = await req.json();
|
|
446
|
-
const updates: Record<string, unknown> = {};
|
|
447
|
-
|
|
448
|
-
if (body.trigger_slug !== undefined) updates.trigger_slug = body.trigger_slug;
|
|
449
|
-
if (body.trigger_instance_id !== undefined) updates.trigger_instance_id = body.trigger_instance_id;
|
|
450
|
-
if (body.agent_id !== undefined) updates.agent_id = body.agent_id;
|
|
451
|
-
if (body.enabled !== undefined) updates.enabled = body.enabled;
|
|
452
|
-
|
|
453
|
-
const updated = SubscriptionDB.update(id, updates);
|
|
454
|
-
return json({ subscription: updated });
|
|
455
|
-
} catch (e: any) {
|
|
456
|
-
console.error("Failed to update subscription:", e);
|
|
457
|
-
return json({ error: e.message || "Failed to update subscription" }, 500);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// DELETE /api/subscriptions/:id?provider=composio&project_id=xxx
|
|
462
|
-
const subDeleteMatch = path.match(/^\/api\/subscriptions\/([^/]+)$/);
|
|
463
|
-
if (subDeleteMatch && method === "DELETE") {
|
|
464
|
-
const subId = subDeleteMatch[1];
|
|
465
|
-
const sub = SubscriptionDB.findById(subId);
|
|
466
|
-
if (!sub) {
|
|
467
|
-
return json({ error: "Subscription not found" }, 404);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Also delete the remote trigger if it exists
|
|
471
|
-
let remoteDeleted = false;
|
|
472
|
-
if (sub.trigger_instance_id) {
|
|
473
|
-
const url = new URL(req.url);
|
|
474
|
-
const providerId = url.searchParams.get("provider") || null;
|
|
475
|
-
const projectId = url.searchParams.get("project_id") || sub.project_id || null;
|
|
476
|
-
|
|
477
|
-
// Try to determine provider — check query param, or try all registered providers
|
|
478
|
-
const providerIds = providerId ? [providerId] : getTriggerProviderIds();
|
|
479
|
-
for (const pid of providerIds) {
|
|
480
|
-
const provider = getTriggerProvider(pid);
|
|
481
|
-
const apiKey = provider ? ProviderKeys.getDecryptedForProject(pid, projectId) : null;
|
|
482
|
-
if (provider && apiKey) {
|
|
483
|
-
try {
|
|
484
|
-
await provider.deleteTrigger(apiKey, sub.trigger_instance_id);
|
|
485
|
-
remoteDeleted = true;
|
|
486
|
-
console.log(`[subscriptions] Deleted remote trigger ${sub.trigger_instance_id} via ${pid}`);
|
|
487
|
-
break;
|
|
488
|
-
} catch (e) {
|
|
489
|
-
console.warn(`[subscriptions] Failed to delete remote trigger ${sub.trigger_instance_id} via ${pid}:`, e);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
const success = SubscriptionDB.delete(subId);
|
|
496
|
-
return json({ success, remoteDeleted });
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// POST /api/subscriptions/:id/enable
|
|
500
|
-
const subEnableMatch = path.match(/^\/api\/subscriptions\/([^/]+)\/enable$/);
|
|
501
|
-
if (subEnableMatch && method === "POST") {
|
|
502
|
-
const sub = SubscriptionDB.findById(subEnableMatch[1]);
|
|
503
|
-
if (!sub) return json({ error: "Subscription not found" }, 404);
|
|
504
|
-
const updated = SubscriptionDB.update(subEnableMatch[1], { enabled: true });
|
|
505
|
-
return json({ subscription: updated });
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
// POST /api/subscriptions/:id/disable
|
|
509
|
-
const subDisableMatch = path.match(/^\/api\/subscriptions\/([^/]+)\/disable$/);
|
|
510
|
-
if (subDisableMatch && method === "POST") {
|
|
511
|
-
const sub = SubscriptionDB.findById(subDisableMatch[1]);
|
|
512
|
-
if (!sub) return json({ error: "Subscription not found" }, 404);
|
|
513
|
-
const updated = SubscriptionDB.update(subDisableMatch[1], { enabled: false });
|
|
514
|
-
return json({ subscription: updated });
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
return null;
|
|
518
|
-
}
|
package/src/routes/api/users.ts
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { json } from "./helpers";
|
|
2
|
-
import { UserDB } from "../../db";
|
|
3
|
-
import { createUser, hashPassword, validatePassword } from "../../auth";
|
|
4
|
-
import type { AuthContext } from "../../auth/middleware";
|
|
5
|
-
|
|
6
|
-
export async function handleUserRoutes(
|
|
7
|
-
req: Request,
|
|
8
|
-
path: string,
|
|
9
|
-
method: string,
|
|
10
|
-
authContext?: AuthContext,
|
|
11
|
-
): Promise<Response | null> {
|
|
12
|
-
const user = authContext?.user;
|
|
13
|
-
|
|
14
|
-
// GET /api/users - List all users
|
|
15
|
-
if (path === "/api/users" && method === "GET") {
|
|
16
|
-
const users = UserDB.findAll().map(u => ({
|
|
17
|
-
id: u.id,
|
|
18
|
-
username: u.username,
|
|
19
|
-
email: u.email,
|
|
20
|
-
role: u.role,
|
|
21
|
-
createdAt: u.created_at,
|
|
22
|
-
lastLoginAt: u.last_login_at,
|
|
23
|
-
}));
|
|
24
|
-
return json({ users });
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// POST /api/users - Create a new user
|
|
28
|
-
if (path === "/api/users" && method === "POST") {
|
|
29
|
-
try {
|
|
30
|
-
const body = await req.json();
|
|
31
|
-
const { username, password, email, role } = body;
|
|
32
|
-
|
|
33
|
-
if (!username || !password) {
|
|
34
|
-
return json({ error: "Username and password are required" }, 400);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const result = await createUser({
|
|
38
|
-
username,
|
|
39
|
-
password,
|
|
40
|
-
email: email || undefined,
|
|
41
|
-
role: role || "user",
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
if (!result.success) {
|
|
45
|
-
return json({ error: result.error }, 400);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return json({
|
|
49
|
-
user: {
|
|
50
|
-
id: result.user!.id,
|
|
51
|
-
username: result.user!.username,
|
|
52
|
-
email: result.user!.email,
|
|
53
|
-
role: result.user!.role,
|
|
54
|
-
createdAt: result.user!.created_at,
|
|
55
|
-
},
|
|
56
|
-
}, 201);
|
|
57
|
-
} catch (e) {
|
|
58
|
-
return json({ error: "Invalid request body" }, 400);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// GET /api/users/:id - Get a specific user
|
|
63
|
-
const userMatch = path.match(/^\/api\/users\/([^/]+)$/);
|
|
64
|
-
if (userMatch && method === "GET") {
|
|
65
|
-
const targetUser = UserDB.findById(userMatch[1]);
|
|
66
|
-
if (!targetUser) {
|
|
67
|
-
return json({ error: "User not found" }, 404);
|
|
68
|
-
}
|
|
69
|
-
return json({
|
|
70
|
-
user: {
|
|
71
|
-
id: targetUser.id,
|
|
72
|
-
username: targetUser.username,
|
|
73
|
-
email: targetUser.email,
|
|
74
|
-
role: targetUser.role,
|
|
75
|
-
createdAt: targetUser.created_at,
|
|
76
|
-
lastLoginAt: targetUser.last_login_at,
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// PUT /api/users/:id - Update a user
|
|
82
|
-
if (userMatch && method === "PUT") {
|
|
83
|
-
const targetUser = UserDB.findById(userMatch[1]);
|
|
84
|
-
if (!targetUser) {
|
|
85
|
-
return json({ error: "User not found" }, 404);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
const body = await req.json();
|
|
90
|
-
const updates: Parameters<typeof UserDB.update>[1] = {};
|
|
91
|
-
|
|
92
|
-
if (body.email !== undefined) updates.email = body.email;
|
|
93
|
-
if (body.role !== undefined) {
|
|
94
|
-
// Prevent removing last admin
|
|
95
|
-
if (targetUser.role === "admin" && body.role !== "admin") {
|
|
96
|
-
if (UserDB.countAdmins() <= 1) {
|
|
97
|
-
return json({ error: "Cannot remove the last admin" }, 400);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
updates.role = body.role;
|
|
101
|
-
}
|
|
102
|
-
if (body.password !== undefined) {
|
|
103
|
-
const validation = validatePassword(body.password);
|
|
104
|
-
if (!validation.valid) {
|
|
105
|
-
return json({ error: validation.errors.join(". ") }, 400);
|
|
106
|
-
}
|
|
107
|
-
updates.password_hash = await hashPassword(body.password);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const updated = UserDB.update(userMatch[1], updates);
|
|
111
|
-
return json({
|
|
112
|
-
user: updated ? {
|
|
113
|
-
id: updated.id,
|
|
114
|
-
username: updated.username,
|
|
115
|
-
email: updated.email,
|
|
116
|
-
role: updated.role,
|
|
117
|
-
createdAt: updated.created_at,
|
|
118
|
-
lastLoginAt: updated.last_login_at,
|
|
119
|
-
} : null,
|
|
120
|
-
});
|
|
121
|
-
} catch (e) {
|
|
122
|
-
return json({ error: "Invalid request body" }, 400);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// DELETE /api/users/:id - Delete a user
|
|
127
|
-
if (userMatch && method === "DELETE") {
|
|
128
|
-
const targetUser = UserDB.findById(userMatch[1]);
|
|
129
|
-
if (!targetUser) {
|
|
130
|
-
return json({ error: "User not found" }, 404);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Prevent deleting yourself
|
|
134
|
-
if (user && targetUser.id === user.id) {
|
|
135
|
-
return json({ error: "Cannot delete your own account" }, 400);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Prevent deleting last admin
|
|
139
|
-
if (targetUser.role === "admin" && UserDB.countAdmins() <= 1) {
|
|
140
|
-
return json({ error: "Cannot delete the last admin" }, 400);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
UserDB.delete(userMatch[1]);
|
|
144
|
-
return json({ success: true });
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return null;
|
|
148
|
-
}
|