apteva 0.4.56 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +216 -54
  2. package/cli.js +35 -0
  3. package/install.js +92 -0
  4. package/package.json +12 -79
  5. package/LICENSE +0 -63
  6. package/bin/apteva.js +0 -196
  7. package/dist/ActivityPage.kxzzb4yc.js +0 -3
  8. package/dist/ApiDocsPage.zq998hbm.js +0 -4
  9. package/dist/App.55rea8mn.js +0 -61
  10. package/dist/App.5ywb23z4.js +0 -53
  11. package/dist/App.6thds120.js +0 -4
  12. package/dist/App.9tctxzqm.js +0 -8
  13. package/dist/App.a8r8ttaz.js +0 -4
  14. package/dist/App.agsv5bje.js +0 -4
  15. package/dist/App.cepapqmx.js +0 -4
  16. package/dist/App.dp041gb3.js +0 -221
  17. package/dist/App.fds72zb5.js +0 -4
  18. package/dist/App.fg9qj2dq.js +0 -4
  19. package/dist/App.ndfejbm9.js +0 -4
  20. package/dist/App.nxmfmq1h.js +0 -13
  21. package/dist/App.qdfyt8ba.js +0 -4
  22. package/dist/App.x2d0ygt6.js +0 -4
  23. package/dist/App.yt9p4nr3.js +0 -20
  24. package/dist/App.zn4mw16t.js +0 -1
  25. package/dist/ConnectionsPage.8r96ryw7.js +0 -3
  26. package/dist/McpPage.3cwh0gnd.js +0 -3
  27. package/dist/SettingsPage.ykgdh5ev.js +0 -3
  28. package/dist/SkillsPage.4np1s65b.js +0 -3
  29. package/dist/TasksPage.4g08t7p6.js +0 -3
  30. package/dist/TelemetryPage.72w9pwcp.js +0 -3
  31. package/dist/TestsPage.z4fk3r7r.js +0 -3
  32. package/dist/ThreadsPage.63tcajeh.js +0 -3
  33. package/dist/apteva-kit.css +0 -1
  34. package/dist/icon.png +0 -0
  35. package/dist/index.html +0 -16
  36. package/dist/styles.css +0 -1
  37. package/scripts/postinstall.mjs +0 -102
  38. package/src/auth/index.ts +0 -394
  39. package/src/auth/middleware.ts +0 -213
  40. package/src/binary.ts +0 -536
  41. package/src/channels/index.ts +0 -40
  42. package/src/channels/telegram.ts +0 -311
  43. package/src/crypto.ts +0 -301
  44. package/src/db-tests.ts +0 -174
  45. package/src/db.ts +0 -3133
  46. package/src/integrations/agentdojo.ts +0 -559
  47. package/src/integrations/composio.ts +0 -437
  48. package/src/integrations/index.ts +0 -87
  49. package/src/integrations/skillsmp.ts +0 -318
  50. package/src/mcp-client.ts +0 -605
  51. package/src/mcp-handler.ts +0 -394
  52. package/src/mcp-platform.ts +0 -2370
  53. package/src/openapi.ts +0 -2410
  54. package/src/providers.ts +0 -597
  55. package/src/routes/api/agent-utils.ts +0 -890
  56. package/src/routes/api/agents.ts +0 -916
  57. package/src/routes/api/api-keys.ts +0 -95
  58. package/src/routes/api/channels.ts +0 -182
  59. package/src/routes/api/helpers.ts +0 -12
  60. package/src/routes/api/integrations.ts +0 -639
  61. package/src/routes/api/mcp.ts +0 -574
  62. package/src/routes/api/meta-agent.ts +0 -195
  63. package/src/routes/api/projects.ts +0 -112
  64. package/src/routes/api/providers.ts +0 -424
  65. package/src/routes/api/skills.ts +0 -537
  66. package/src/routes/api/system.ts +0 -333
  67. package/src/routes/api/telemetry.ts +0 -203
  68. package/src/routes/api/tests.ts +0 -148
  69. package/src/routes/api/triggers.ts +0 -518
  70. package/src/routes/api/users.ts +0 -148
  71. package/src/routes/api/webhooks.ts +0 -171
  72. package/src/routes/api.ts +0 -53
  73. package/src/routes/auth.ts +0 -251
  74. package/src/routes/share.ts +0 -86
  75. package/src/routes/static.ts +0 -131
  76. package/src/server.ts +0 -642
  77. package/src/test-runner.ts +0 -598
  78. package/src/triggers/agentdojo.ts +0 -253
  79. package/src/triggers/composio.ts +0 -264
  80. package/src/triggers/index.ts +0 -71
  81. package/src/tui/AgentList.tsx +0 -145
  82. package/src/tui/App.tsx +0 -102
  83. package/src/tui/Login.tsx +0 -104
  84. package/src/tui/api.ts +0 -72
  85. package/src/tui/index.tsx +0 -7
  86. package/src/web/App.tsx +0 -455
  87. package/src/web/components/activity/ActivityPage.tsx +0 -314
  88. package/src/web/components/activity/index.ts +0 -1
  89. package/src/web/components/agents/AgentCard.tsx +0 -189
  90. package/src/web/components/agents/AgentPanel.tsx +0 -2244
  91. package/src/web/components/agents/AgentsView.tsx +0 -180
  92. package/src/web/components/agents/CreateAgentModal.tsx +0 -475
  93. package/src/web/components/agents/index.ts +0 -4
  94. package/src/web/components/api/ApiDocsPage.tsx +0 -842
  95. package/src/web/components/auth/CreateAccountStep.tsx +0 -176
  96. package/src/web/components/auth/LoginPage.tsx +0 -91
  97. package/src/web/components/auth/index.ts +0 -2
  98. package/src/web/components/common/Icons.tsx +0 -250
  99. package/src/web/components/common/LoadingSpinner.tsx +0 -44
  100. package/src/web/components/common/Modal.tsx +0 -199
  101. package/src/web/components/common/Select.tsx +0 -97
  102. package/src/web/components/common/index.ts +0 -20
  103. package/src/web/components/connections/ConnectionsPage.tsx +0 -54
  104. package/src/web/components/connections/IntegrationsTab.tsx +0 -170
  105. package/src/web/components/connections/OverviewTab.tsx +0 -137
  106. package/src/web/components/connections/TriggersTab.tsx +0 -1346
  107. package/src/web/components/dashboard/Dashboard.tsx +0 -572
  108. package/src/web/components/dashboard/index.ts +0 -1
  109. package/src/web/components/index.ts +0 -21
  110. package/src/web/components/layout/ErrorBanner.tsx +0 -18
  111. package/src/web/components/layout/Header.tsx +0 -332
  112. package/src/web/components/layout/Sidebar.tsx +0 -231
  113. package/src/web/components/layout/index.ts +0 -3
  114. package/src/web/components/mcp/IntegrationsPanel.tsx +0 -857
  115. package/src/web/components/mcp/McpPage.tsx +0 -2515
  116. package/src/web/components/mcp/index.ts +0 -1
  117. package/src/web/components/meta-agent/MetaAgent.tsx +0 -245
  118. package/src/web/components/onboarding/OnboardingWizard.tsx +0 -404
  119. package/src/web/components/onboarding/index.ts +0 -1
  120. package/src/web/components/settings/SettingsPage.tsx +0 -2776
  121. package/src/web/components/settings/index.ts +0 -1
  122. package/src/web/components/skills/SkillsPage.tsx +0 -1200
  123. package/src/web/components/tasks/TasksPage.tsx +0 -1116
  124. package/src/web/components/tasks/index.ts +0 -1
  125. package/src/web/components/telemetry/TelemetryPage.tsx +0 -1129
  126. package/src/web/components/tests/TestsPage.tsx +0 -594
  127. package/src/web/components/threads/ThreadsPage.tsx +0 -315
  128. package/src/web/context/AuthContext.tsx +0 -242
  129. package/src/web/context/ProjectContext.tsx +0 -214
  130. package/src/web/context/TelemetryContext.tsx +0 -299
  131. package/src/web/context/ThemeContext.tsx +0 -90
  132. package/src/web/context/UIModeContext.tsx +0 -49
  133. package/src/web/context/index.ts +0 -12
  134. package/src/web/hooks/index.ts +0 -3
  135. package/src/web/hooks/useAgents.ts +0 -115
  136. package/src/web/hooks/useOnboarding.ts +0 -20
  137. package/src/web/hooks/useProviders.ts +0 -75
  138. package/src/web/icon.png +0 -0
  139. package/src/web/index.html +0 -16
  140. package/src/web/styles.css +0 -118
  141. package/src/web/themes.ts +0 -162
  142. package/src/web/types.ts +0 -298
@@ -1,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
- }
@@ -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
- }