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.
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 -2403
  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,171 +0,0 @@
1
- import { json } from "./helpers";
2
- import { AgentDB, SubscriptionDB, SettingsDB } from "../../db";
3
- import { getTriggerProvider } from "../../triggers";
4
- import { agentFetch } from "./agent-utils";
5
-
6
- /**
7
- * Central webhook receiver for trigger providers.
8
- * POST /api/webhooks/:provider — receives trigger events from any registered provider,
9
- * verifies HMAC, looks up local subscriptions, and dispatches to the appropriate agent(s).
10
- *
11
- * This endpoint is public (HMAC-verified, no JWT/API key auth).
12
- */
13
- export async function handleWebhookRoutes(
14
- req: Request,
15
- path: string,
16
- method: string,
17
- ): Promise<Response | null> {
18
-
19
- // POST /api/webhooks/composio
20
- if (path === "/api/webhooks/composio" && method === "POST") {
21
- return handleProviderWebhook(req, "composio");
22
- }
23
-
24
- // POST /api/webhooks/agentdojo
25
- if (path === "/api/webhooks/agentdojo" && method === "POST") {
26
- return handleProviderWebhook(req, "agentdojo");
27
- }
28
-
29
- return null;
30
- }
31
-
32
- async function handleProviderWebhook(req: Request, providerId: string): Promise<Response> {
33
- const provider = getTriggerProvider(providerId);
34
- if (!provider) {
35
- return json({ error: `${providerId} provider not registered` }, 500);
36
- }
37
-
38
- // Read raw body for HMAC verification
39
- let rawBody: string;
40
- try {
41
- rawBody = await req.text();
42
- } catch {
43
- return json({ error: "Failed to read request body" }, 400);
44
- }
45
-
46
- // Verify HMAC signature using stored webhook secret
47
- const webhookSecret = SettingsDB.get(`${providerId}_webhook_secret`);
48
- if (webhookSecret) {
49
- const valid = provider.verifyWebhook(req, rawBody, webhookSecret);
50
- if (!valid) {
51
- console.warn(`[webhook] Invalid HMAC signature for ${providerId} webhook`);
52
- return json({ error: "Invalid signature" }, 401);
53
- }
54
- } else {
55
- console.warn(`[webhook] No ${providerId} webhook secret configured — skipping HMAC verification`);
56
- }
57
-
58
- // Parse the payload
59
- let body: Record<string, unknown>;
60
- try {
61
- body = JSON.parse(rawBody);
62
- } catch {
63
- return json({ error: "Invalid JSON" }, 400);
64
- }
65
-
66
- // Log raw webhook for debugging
67
- console.log(`[webhook:${providerId}] Raw payload:`, JSON.stringify(body, null, 2));
68
-
69
- const { triggerSlug, triggerInstanceId, payload } = provider.parseWebhookPayload(body);
70
- console.log(`[webhook:${providerId}] Parsed:`, { triggerSlug, triggerInstanceId });
71
-
72
- // Respond 200 immediately — dispatch async
73
- const dispatchPromise = dispatchToSubscribers(providerId, triggerSlug, triggerInstanceId, payload);
74
-
75
- // Fire and forget — but log errors
76
- dispatchPromise.catch(err => {
77
- console.error(`[webhook:${providerId}] Dispatch error:`, err);
78
- });
79
-
80
- return json({ received: true, provider: providerId, trigger: triggerSlug });
81
- }
82
-
83
- async function dispatchToSubscribers(
84
- providerId: string,
85
- triggerSlug: string,
86
- triggerInstanceId: string | null,
87
- payload: Record<string, unknown>,
88
- ): Promise<void> {
89
- // Find matching subscriptions:
90
- // 1. Exact match by trigger_instance_id (most specific)
91
- // 2. Match by trigger_slug (broader)
92
- let subscriptions = triggerInstanceId
93
- ? SubscriptionDB.findByTriggerInstanceId(triggerInstanceId)
94
- : [];
95
-
96
- // If no instance-level matches, fall back to slug-level
97
- if (subscriptions.length === 0) {
98
- subscriptions = SubscriptionDB.findByTriggerSlug(triggerSlug);
99
- }
100
-
101
- // Filter to enabled only
102
- subscriptions = subscriptions.filter(s => s.enabled);
103
-
104
- if (subscriptions.length === 0) {
105
- console.log(`[webhook:${providerId}] No subscriptions for trigger ${triggerSlug} (instance: ${triggerInstanceId || "none"})`);
106
- return;
107
- }
108
-
109
- // Dispatch to each subscribed agent
110
- const results = await Promise.allSettled(
111
- subscriptions.map(sub => dispatchToAgent(sub.agent_id, triggerSlug, payload)),
112
- );
113
-
114
- for (let i = 0; i < results.length; i++) {
115
- const result = results[i];
116
- const sub = subscriptions[i];
117
- if (result.status === "rejected") {
118
- console.error(`[webhook:${providerId}] Failed to dispatch to agent ${sub.agent_id}:`, result.reason);
119
- } else {
120
- console.log(`[webhook:${providerId}] Dispatched ${triggerSlug} to agent ${sub.agent_id}: ${result.value}`);
121
- }
122
- }
123
- }
124
-
125
- async function dispatchToAgent(
126
- agentId: string,
127
- triggerSlug: string,
128
- payload: Record<string, unknown>,
129
- ): Promise<string> {
130
- const agent = AgentDB.findById(agentId);
131
- if (!agent) {
132
- return "agent_not_found";
133
- }
134
-
135
- if (agent.status !== "running" || !agent.port) {
136
- return "agent_not_running";
137
- }
138
-
139
- // Format the trigger event as a chat message
140
- const triggerName = triggerSlug.replace(/_/g, " ").replace(/:/g, " → ");
141
- const message = [
142
- `[Trigger: ${triggerName}]`,
143
- "",
144
- "```json",
145
- JSON.stringify(payload, null, 2),
146
- "```",
147
- "",
148
- "Process this event and take appropriate action.",
149
- ].join("\n");
150
-
151
- const response = await agentFetch(agent.id, agent.port, "/chat", {
152
- method: "POST",
153
- headers: { "Content-Type": "application/json" },
154
- body: JSON.stringify({ message }),
155
- });
156
-
157
- // Consume the streaming response
158
- if (response.body) {
159
- try {
160
- const reader = response.body.getReader();
161
- while (true) {
162
- const { done } = await reader.read();
163
- if (done) break;
164
- }
165
- } catch {
166
- // Ignore read errors
167
- }
168
- }
169
-
170
- return response.ok ? "sent" : "agent_error";
171
- }
package/src/routes/api.ts DELETED
@@ -1,53 +0,0 @@
1
- import type { AuthContext } from "../auth/middleware";
2
- import { json } from "./api/helpers";
3
- import { handleSystemRoutes } from "./api/system";
4
- import { handleProviderRoutes } from "./api/providers";
5
- import { handleUserRoutes } from "./api/users";
6
- import { handleProjectRoutes } from "./api/projects";
7
- import { handleAgentRoutes } from "./api/agents";
8
- import { handleMcpRoutes } from "./api/mcp";
9
- import { handleSkillRoutes } from "./api/skills";
10
- import { handleIntegrationRoutes } from "./api/integrations";
11
- import { handleTriggerRoutes } from "./api/triggers";
12
- import { handleWebhookRoutes } from "./api/webhooks";
13
- import { handleMetaAgentRoutes } from "./api/meta-agent";
14
- import { handleTelemetryRoutes } from "./api/telemetry";
15
- import { handleTestRoutes } from "./api/tests";
16
- import { handleApiKeyRoutes } from "./api/api-keys";
17
- import { handleChannelRoutes } from "./api/channels";
18
- import { handlePlatformMcpRequest } from "../mcp-platform";
19
-
20
- // Re-export for backward compatibility (server.ts dynamic import)
21
- export { startAgentProcess } from "./api/agent-utils";
22
-
23
- export async function handleApiRequest(
24
- req: Request,
25
- path: string,
26
- authContext?: AuthContext,
27
- ): Promise<Response> {
28
- const method = req.method;
29
-
30
- // Built-in platform MCP server (for meta agent)
31
- if (path === "/api/mcp/platform" && method === "POST") {
32
- return handlePlatformMcpRequest(req);
33
- }
34
-
35
- return (
36
- (await handleWebhookRoutes(req, path, method)) ?? // Public, HMAC-verified — before auth
37
- (await handleSystemRoutes(req, path, method, authContext)) ??
38
- (await handleApiKeyRoutes(req, path, method, authContext)) ?? // Must be before provider routes to handle /api/keys/personal
39
- (await handleProviderRoutes(req, path, method, authContext)) ??
40
- (await handleUserRoutes(req, path, method, authContext)) ??
41
- (await handleProjectRoutes(req, path, method, authContext)) ??
42
- (await handleAgentRoutes(req, path, method, authContext)) ??
43
- (await handleMcpRoutes(req, path, method)) ??
44
- (await handleSkillRoutes(req, path, method)) ??
45
- (await handleIntegrationRoutes(req, path, method, authContext)) ??
46
- (await handleTriggerRoutes(req, path, method, authContext)) ??
47
- (await handleChannelRoutes(req, path, method)) ??
48
- (await handleMetaAgentRoutes(req, path, method)) ??
49
- (await handleTelemetryRoutes(req, path, method)) ??
50
- (await handleTestRoutes(req, path, method)) ??
51
- json({ error: "Not found" }, 404)
52
- );
53
- }
@@ -1,251 +0,0 @@
1
- import { UserDB } from "../db";
2
- import {
3
- login,
4
- refreshSession,
5
- invalidateSession,
6
- getAuthStatus,
7
- verifyAccessToken,
8
- hashPassword,
9
- validatePassword,
10
- REFRESH_TOKEN_EXPIRY,
11
- } from "../auth";
12
- import { Onboarding } from "../providers";
13
- import {
14
- getTokenFromRequest,
15
- getRefreshTokenFromCookie,
16
- createRefreshTokenCookie,
17
- clearRefreshTokenCookie,
18
- } from "../auth/middleware";
19
-
20
- function json(data: unknown, status = 200, headers: Record<string, string> = {}): Response {
21
- return new Response(JSON.stringify(data), {
22
- status,
23
- headers: { "Content-Type": "application/json", ...headers },
24
- });
25
- }
26
-
27
- export async function handleAuthRequest(req: Request, path: string): Promise<Response> {
28
- const method = req.method;
29
-
30
- // GET /api/auth/check - Check authentication status (includes onboarding to avoid extra round trip)
31
- if (path === "/api/auth/check" && method === "GET") {
32
- const token = getTokenFromRequest(req);
33
- const status = getAuthStatus(token || undefined);
34
- const onboarding = Onboarding.getStatus();
35
- return json({ ...status, onboarding });
36
- }
37
-
38
- // POST /api/auth/login - Login with username and password
39
- if (path === "/api/auth/login" && method === "POST") {
40
- try {
41
- const body = await req.json();
42
- const { username, password } = body;
43
-
44
- if (!username || !password) {
45
- return json({ error: "Username and password are required" }, 400);
46
- }
47
-
48
- const result = await login(username, password);
49
-
50
- if (!result.success) {
51
- return json({ error: result.error }, 401);
52
- }
53
-
54
- // Set refresh token as httpOnly cookie
55
- const cookieHeader = createRefreshTokenCookie(result.tokens!.refreshToken, REFRESH_TOKEN_EXPIRY);
56
-
57
- return json(
58
- {
59
- user: result.user,
60
- accessToken: result.tokens!.accessToken,
61
- expiresIn: result.tokens!.expiresIn,
62
- },
63
- 200,
64
- { "Set-Cookie": cookieHeader }
65
- );
66
- } catch (e) {
67
- return json({ error: "Invalid request body" }, 400);
68
- }
69
- }
70
-
71
- // POST /api/auth/logout - Logout (invalidate refresh token)
72
- if (path === "/api/auth/logout" && method === "POST") {
73
- const refreshToken = getRefreshTokenFromCookie(req);
74
-
75
- if (refreshToken) {
76
- invalidateSession(refreshToken);
77
- }
78
-
79
- // Clear the cookie
80
- return json(
81
- { success: true },
82
- 200,
83
- { "Set-Cookie": clearRefreshTokenCookie() }
84
- );
85
- }
86
-
87
- // POST /api/auth/refresh - Refresh access token
88
- if (path === "/api/auth/refresh" && method === "POST") {
89
- // No users = no valid sessions possible
90
- if (!UserDB.hasUsers()) {
91
- return json({ error: "No users exist" }, 401, { "Set-Cookie": clearRefreshTokenCookie() });
92
- }
93
-
94
- const refreshToken = getRefreshTokenFromCookie(req);
95
-
96
- if (!refreshToken) {
97
- return json({ error: "No refresh token" }, 401);
98
- }
99
-
100
- const result = await refreshSession(refreshToken);
101
-
102
- if (!result) {
103
- return json(
104
- { error: "Invalid or expired refresh token" },
105
- 401,
106
- { "Set-Cookie": clearRefreshTokenCookie() }
107
- );
108
- }
109
-
110
- // Set new refresh token cookie
111
- const cookieHeader = createRefreshTokenCookie(result.refreshToken, REFRESH_TOKEN_EXPIRY);
112
-
113
- // Include user info + onboarding to avoid extra /api/auth/me round trip
114
- const payload = verifyAccessToken(result.accessToken);
115
- const user = payload ? UserDB.findById(payload.userId) : null;
116
- const onboarding = Onboarding.getStatus();
117
-
118
- return json(
119
- {
120
- accessToken: result.accessToken,
121
- expiresIn: result.expiresIn,
122
- user: user ? { id: user.id, username: user.username, role: user.role } : undefined,
123
- onboarding,
124
- },
125
- 200,
126
- { "Set-Cookie": cookieHeader }
127
- );
128
- }
129
-
130
- // GET /api/auth/me - Get current user
131
- if (path === "/api/auth/me" && method === "GET") {
132
- const token = getTokenFromRequest(req);
133
-
134
- if (!token) {
135
- return json({ error: "Unauthorized" }, 401);
136
- }
137
-
138
- const payload = verifyAccessToken(token);
139
- if (!payload) {
140
- return json({ error: "Invalid or expired token" }, 401);
141
- }
142
-
143
- const user = UserDB.findById(payload.userId);
144
- if (!user) {
145
- return json({ error: "User not found" }, 404);
146
- }
147
-
148
- return json({
149
- user: {
150
- id: user.id,
151
- username: user.username,
152
- email: user.email,
153
- role: user.role,
154
- createdAt: user.created_at,
155
- lastLoginAt: user.last_login_at,
156
- },
157
- });
158
- }
159
-
160
- // PUT /api/auth/me - Update current user profile
161
- if (path === "/api/auth/me" && method === "PUT") {
162
- const token = getTokenFromRequest(req);
163
-
164
- if (!token) {
165
- return json({ error: "Unauthorized" }, 401);
166
- }
167
-
168
- const payload = verifyAccessToken(token);
169
- if (!payload) {
170
- return json({ error: "Invalid or expired token" }, 401);
171
- }
172
-
173
- const user = UserDB.findById(payload.userId);
174
- if (!user) {
175
- return json({ error: "User not found" }, 404);
176
- }
177
-
178
- try {
179
- const body = await req.json();
180
- const updates: Parameters<typeof UserDB.update>[1] = {};
181
-
182
- if (body.email !== undefined) updates.email = body.email;
183
-
184
- const updated = UserDB.update(user.id, updates);
185
-
186
- return json({
187
- user: updated ? {
188
- id: updated.id,
189
- username: updated.username,
190
- email: updated.email,
191
- role: updated.role,
192
- createdAt: updated.created_at,
193
- lastLoginAt: updated.last_login_at,
194
- } : null,
195
- });
196
- } catch (e) {
197
- return json({ error: "Invalid request body" }, 400);
198
- }
199
- }
200
-
201
- // PUT /api/auth/password - Change password
202
- if (path === "/api/auth/password" && method === "PUT") {
203
- const token = getTokenFromRequest(req);
204
-
205
- if (!token) {
206
- return json({ error: "Unauthorized" }, 401);
207
- }
208
-
209
- const payload = verifyAccessToken(token);
210
- if (!payload) {
211
- return json({ error: "Invalid or expired token" }, 401);
212
- }
213
-
214
- const user = UserDB.findById(payload.userId);
215
- if (!user) {
216
- return json({ error: "User not found" }, 404);
217
- }
218
-
219
- try {
220
- const body = await req.json();
221
- const { currentPassword, newPassword } = body;
222
-
223
- if (!currentPassword || !newPassword) {
224
- return json({ error: "Current and new password are required" }, 400);
225
- }
226
-
227
- // Verify current password
228
- const { verifyPassword } = await import("../auth");
229
- const isValid = await verifyPassword(currentPassword, user.password_hash);
230
- if (!isValid) {
231
- return json({ error: "Current password is incorrect" }, 401);
232
- }
233
-
234
- // Validate new password
235
- const validation = validatePassword(newPassword);
236
- if (!validation.valid) {
237
- return json({ error: validation.errors.join(". ") }, 400);
238
- }
239
-
240
- // Update password
241
- const newHash = await hashPassword(newPassword);
242
- UserDB.update(user.id, { password_hash: newHash });
243
-
244
- return json({ success: true, message: "Password updated successfully" });
245
- } catch (e) {
246
- return json({ error: "Invalid request body" }, 400);
247
- }
248
- }
249
-
250
- return json({ error: "Not found" }, 404);
251
- }
@@ -1,86 +0,0 @@
1
- import { createHash } from "crypto";
2
- import { AgentDB, isRealtimeEnabled, type Agent } from "../db";
3
- import { agentFetch } from "./api/agent-utils";
4
-
5
- function deriveShareToken(apiKey: string, agentId: string): string {
6
- return createHash("sha256")
7
- .update(apiKey + ":" + agentId + ":share")
8
- .digest("hex")
9
- .substring(0, 32);
10
- }
11
-
12
- export function findAgentByShareToken(token: string): Agent | null {
13
- const agents = AgentDB.findAll();
14
- for (const agent of agents) {
15
- const apiKey = AgentDB.getApiKey(agent.id);
16
- if (!apiKey) continue;
17
- if (deriveShareToken(apiKey, agent.id) === token) return agent;
18
- }
19
- return null;
20
- }
21
-
22
- /** Get the share token for an agent (used by API route for the UI) */
23
- export function getShareToken(agentId: string): string | null {
24
- const apiKey = AgentDB.getApiKey(agentId);
25
- if (!apiKey) return null;
26
- return deriveShareToken(apiKey, agentId);
27
- }
28
-
29
- export async function handleShareRequest(req: Request, path: string): Promise<Response | null> {
30
- // Match /share/<32 hex chars> sub-paths for API calls
31
- const infoMatch = path.match(/^\/share\/([a-f0-9]{32})\/info$/);
32
- const chatMatch = path.match(/^\/share\/([a-f0-9]{32})\/chat$/);
33
-
34
- if (!infoMatch && !chatMatch) return null;
35
-
36
- const token = (infoMatch || chatMatch)![1];
37
- const agent = findAgentByShareToken(token);
38
-
39
- // Intentionally vague 404 — don't reveal whether token exists
40
- if (!agent) {
41
- return new Response("Not found", { status: 404 });
42
- }
43
-
44
- // GET /share/:token/info — agent info (no secrets)
45
- if (infoMatch && req.method === "GET") {
46
- return Response.json({
47
- name: agent.name,
48
- status: agent.status,
49
- voiceEnabled: isRealtimeEnabled(agent.features),
50
- });
51
- }
52
-
53
- // POST /share/:token/chat — proxy to agent
54
- if (chatMatch && req.method === "POST") {
55
- if (agent.status !== "running" || !agent.port) {
56
- return Response.json({ error: "Agent is currently offline" }, { status: 503 });
57
- }
58
-
59
- try {
60
- const body = await req.json();
61
- const response = await agentFetch(agent.id, agent.port, "/chat", {
62
- method: "POST",
63
- headers: { "Content-Type": "application/json" },
64
- body: JSON.stringify(body),
65
- });
66
-
67
- if (!response.ok) {
68
- const errorText = await response.text();
69
- return Response.json({ error: `Agent error: ${errorText}` }, { status: response.status });
70
- }
71
-
72
- return new Response(response.body, {
73
- status: 200,
74
- headers: {
75
- "Content-Type": response.headers.get("Content-Type") || "text/event-stream",
76
- "Cache-Control": "no-cache",
77
- "Connection": "keep-alive",
78
- },
79
- });
80
- } catch (err) {
81
- return Response.json({ error: "Failed to connect to agent" }, { status: 500 });
82
- }
83
- }
84
-
85
- return null;
86
- }
@@ -1,131 +0,0 @@
1
- import { join } from "path";
2
- import { existsSync, statSync } from "fs";
3
-
4
- // Find dist directory - handle development, npx, and compiled binary contexts
5
- function findDistDir(): string {
6
- const candidates = [
7
- join(import.meta.dir, "../../dist"),
8
- join(import.meta.dir, "../dist"),
9
- join(import.meta.dir, "dist"), // compiled binary: dist/ alongside the executable
10
- join(process.cwd(), "dist"),
11
- ];
12
-
13
- for (const dir of candidates) {
14
- try {
15
- if (existsSync(dir) && statSync(dir).isDirectory()) {
16
- const indexPath = join(dir, "index.html");
17
- if (existsSync(indexPath)) {
18
- return dir;
19
- }
20
- }
21
- } catch {
22
- continue;
23
- }
24
- }
25
-
26
- return candidates[0]; // Default to first candidate
27
- }
28
-
29
- const DIST_DIR = findDistDir();
30
-
31
- // MIME types for common file extensions
32
- const MIME_TYPES: Record<string, string> = {
33
- ".html": "text/html; charset=utf-8",
34
- ".js": "application/javascript; charset=utf-8",
35
- ".css": "text/css; charset=utf-8",
36
- ".json": "application/json; charset=utf-8",
37
- ".png": "image/png",
38
- ".jpg": "image/jpeg",
39
- ".jpeg": "image/jpeg",
40
- ".gif": "image/gif",
41
- ".svg": "image/svg+xml",
42
- ".ico": "image/x-icon",
43
- ".woff": "font/woff",
44
- ".woff2": "font/woff2",
45
- ".ttf": "font/ttf",
46
- ".eot": "application/vnd.ms-fontobject",
47
- };
48
-
49
- function getMimeType(path: string): string {
50
- const ext = path.substring(path.lastIndexOf(".")).toLowerCase();
51
- return MIME_TYPES[ext] || "application/octet-stream";
52
- }
53
-
54
- export async function serveStatic(req: Request, path: string): Promise<Response> {
55
- try {
56
- // Default to index.html for root
57
- let filePath = path === "/" ? "/index.html" : path;
58
-
59
- // Prevent directory traversal attacks
60
- if (filePath.includes("..")) {
61
- return new Response("Forbidden", { status: 403 });
62
- }
63
-
64
- const fullPath = join(DIST_DIR, filePath);
65
-
66
- // Check if file exists using sync API (more reliable)
67
- if (existsSync(fullPath)) {
68
- try {
69
- const stat = statSync(fullPath);
70
- if (stat.isFile()) {
71
- const file = Bun.file(fullPath);
72
- const mimeType = getMimeType(filePath);
73
- const headers: Record<string, string> = { "Content-Type": mimeType };
74
-
75
- // Hashed assets (e.g. App.fq4xbpcz.js) are immutable — cache aggressively
76
- // index.html must never be cached (it references the hashed assets)
77
- const hasHash = /\.[a-z0-9]{6,}\.(js|css|map)$/.test(filePath);
78
- if (hasHash) {
79
- headers["Cache-Control"] = "public, max-age=31536000, immutable";
80
- } else if (filePath !== "/index.html") {
81
- headers["Cache-Control"] = "public, max-age=3600";
82
- }
83
-
84
- return new Response(file, { headers });
85
- }
86
- } catch {
87
- // Fall through to SPA handling
88
- }
89
- }
90
-
91
- // For SPA: if file doesn't exist and it's not a static asset, serve index.html
92
- if (!path.includes(".")) {
93
- const indexPath = join(DIST_DIR, "index.html");
94
- if (existsSync(indexPath)) {
95
- const indexFile = Bun.file(indexPath);
96
- return new Response(indexFile, {
97
- headers: { "Content-Type": "text/html; charset=utf-8" },
98
- });
99
- }
100
- }
101
-
102
- // If dist doesn't exist, serve a development message
103
- return new Response(
104
- `<!DOCTYPE html>
105
- <html>
106
- <head>
107
- <title>apteva</title>
108
- <style>
109
- body { font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #0f172a; color: #e2e8f0; }
110
- .container { text-align: center; }
111
- code { background: #1e293b; padding: 4px 8px; border-radius: 4px; }
112
- </style>
113
- </head>
114
- <body>
115
- <div class="container">
116
- <h1>apteva</h1>
117
- <p>Run <code>bun run build</code> to build the frontend</p>
118
- <p>API available at <a href="/api/health" style="color: #60a5fa">/api/health</a></p>
119
- </div>
120
- </body>
121
- </html>`,
122
- {
123
- status: 200,
124
- headers: { "Content-Type": "text/html; charset=utf-8" }
125
- }
126
- );
127
- } catch (error) {
128
- console.error("Static file error:", error);
129
- return new Response("Internal Server Error", { status: 500 });
130
- }
131
- }