@axiom-lattice/gateway 3.0.2 → 3.0.3

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 (155) hide show
  1. package/dist/index.js +31 -9
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +31 -9
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +9 -6
  6. package/.eslintrc.json +0 -22
  7. package/.turbo/turbo-build.log +0 -46
  8. package/AGENTS.md +0 -112
  9. package/CHANGELOG.md +0 -1190
  10. package/LOGGER_CONFIG.md +0 -135
  11. package/RESUME_STREAM_CONTENT_BASED.md +0 -326
  12. package/RESUME_STREAM_README.md +0 -388
  13. package/jest.config.js +0 -72
  14. package/public/sdk/README.md +0 -695
  15. package/public/sdk/dashboard-engine-skill.md +0 -1122
  16. package/public/sdk/dashboard-single-file-spec.md +0 -357
  17. package/public/sdk/data-query-sdk-skill.md +0 -307
  18. package/public/sdk/data-query-sdk.d.ts +0 -252
  19. package/public/sdk/data-query-sdk.js +0 -970
  20. package/public/sdk/occupancy-dashboard.html +0 -363
  21. package/public/sdk/test-dashboard.html +0 -690
  22. package/scripts/seed-lark-installation.ts +0 -54
  23. package/src/__tests__/__mocks__/e2b.ts +0 -1
  24. package/src/__tests__/a2a.test.ts +0 -428
  25. package/src/__tests__/channel-installations.test.ts +0 -199
  26. package/src/__tests__/data-query.test.ts +0 -77
  27. package/src/__tests__/sandbox-provider-registration.test.ts +0 -74
  28. package/src/__tests__/setup.ts +0 -5
  29. package/src/__tests__/workspace.test.ts +0 -400
  30. package/src/channels/__tests__/routes.test.ts +0 -50
  31. package/src/channels/lark/LarkChannelAdapter.ts +0 -217
  32. package/src/channels/lark/README.md +0 -187
  33. package/src/channels/lark/__tests__/controller.test.ts +0 -136
  34. package/src/channels/lark/__tests__/mapping-service.test.ts +0 -118
  35. package/src/channels/lark/__tests__/parser.test.ts +0 -72
  36. package/src/channels/lark/__tests__/sender.test.ts +0 -37
  37. package/src/channels/lark/__tests__/verification.test.ts +0 -157
  38. package/src/channels/lark/controller.ts +0 -48
  39. package/src/channels/lark/mapping-service.ts +0 -138
  40. package/src/channels/lark/parser.ts +0 -68
  41. package/src/channels/lark/routes.ts +0 -22
  42. package/src/channels/lark/sender.ts +0 -58
  43. package/src/channels/lark/types.ts +0 -20
  44. package/src/channels/lark/verification.ts +0 -32
  45. package/src/channels/registry.ts +0 -20
  46. package/src/channels/routes.ts +0 -30
  47. package/src/channels/wechat/WechatChannelAdapter.ts +0 -294
  48. package/src/channels/wechat/__tests__/context-store.test.ts +0 -148
  49. package/src/channels/wechat/context-store.ts +0 -54
  50. package/src/channels/wechat/controller.ts +0 -59
  51. package/src/channels/wechat/routes.ts +0 -7
  52. package/src/channels/wechat/types.ts +0 -62
  53. package/src/channels/wechat/wechat-client.ts +0 -162
  54. package/src/config/local-a2a-templates.ts +0 -59
  55. package/src/config.ts +0 -124
  56. package/src/controllers/__tests__/assistant-local-a2a.test.ts +0 -147
  57. package/src/controllers/__tests__/local-a2a-templates.test.ts +0 -112
  58. package/src/controllers/__tests__/mime.test.ts +0 -74
  59. package/src/controllers/__tests__/resources.test.ts +0 -122
  60. package/src/controllers/__tests__/run.test.ts +0 -115
  61. package/src/controllers/__tests__/tasks.test.ts +0 -215
  62. package/src/controllers/__tests__/workflow-tracking.test.ts +0 -113
  63. package/src/controllers/agent_task.ts +0 -72
  64. package/src/controllers/assistant.ts +0 -490
  65. package/src/controllers/auth.ts +0 -524
  66. package/src/controllers/channel-bindings.ts +0 -135
  67. package/src/controllers/channel-installations.ts +0 -409
  68. package/src/controllers/collections.ts +0 -176
  69. package/src/controllers/config.ts +0 -126
  70. package/src/controllers/connections.ts +0 -181
  71. package/src/controllers/data-query.ts +0 -236
  72. package/src/controllers/database-configs.ts +0 -440
  73. package/src/controllers/embeddings.ts +0 -22
  74. package/src/controllers/entries.ts +0 -167
  75. package/src/controllers/eval.ts +0 -513
  76. package/src/controllers/export-import.ts +0 -166
  77. package/src/controllers/health.ts +0 -36
  78. package/src/controllers/local-a2a-templates.ts +0 -18
  79. package/src/controllers/local-a2a.ts +0 -149
  80. package/src/controllers/mcp-configs.ts +0 -673
  81. package/src/controllers/memory.ts +0 -257
  82. package/src/controllers/menu-items.ts +0 -114
  83. package/src/controllers/metrics-configs.ts +0 -1061
  84. package/src/controllers/middleware-types.ts +0 -15
  85. package/src/controllers/models.ts +0 -199
  86. package/src/controllers/personal-assistant.ts +0 -191
  87. package/src/controllers/resources.ts +0 -477
  88. package/src/controllers/run.ts +0 -338
  89. package/src/controllers/sandbox.ts +0 -255
  90. package/src/controllers/schedules.ts +0 -336
  91. package/src/controllers/skills.ts +0 -256
  92. package/src/controllers/tasks.ts +0 -213
  93. package/src/controllers/tenants.ts +0 -121
  94. package/src/controllers/thread_status.ts +0 -66
  95. package/src/controllers/threads.ts +0 -260
  96. package/src/controllers/tools.ts +0 -139
  97. package/src/controllers/users.ts +0 -135
  98. package/src/controllers/workflow-tracking.ts +0 -693
  99. package/src/controllers/workspace.ts +0 -879
  100. package/src/export_registrations/a2a-api-key.registration.ts +0 -89
  101. package/src/export_registrations/agent.registration.ts +0 -80
  102. package/src/export_registrations/binding.registration.ts +0 -106
  103. package/src/export_registrations/channel-installation.registration.ts +0 -88
  104. package/src/export_registrations/collection.registration.ts +0 -85
  105. package/src/export_registrations/connection.registration.ts +0 -97
  106. package/src/export_registrations/database-config.registration.ts +0 -94
  107. package/src/export_registrations/eval.registration.ts +0 -334
  108. package/src/export_registrations/index.ts +0 -31
  109. package/src/export_registrations/mcp-config.registration.ts +0 -97
  110. package/src/export_registrations/menu.registration.ts +0 -91
  111. package/src/export_registrations/metrics-config.registration.ts +0 -94
  112. package/src/export_registrations/skill.registration.ts +0 -88
  113. package/src/export_registrations/task.registration.ts +0 -99
  114. package/src/index.ts +0 -497
  115. package/src/logger/Logger.ts +0 -186
  116. package/src/router/MessageContext.ts +0 -14
  117. package/src/router/MessageRouter.ts +0 -490
  118. package/src/router/middlewares/auditLogger.ts +0 -34
  119. package/src/router/middlewares/deduplication.ts +0 -38
  120. package/src/router/middlewares/index.ts +0 -3
  121. package/src/router/middlewares/rateLimit.ts +0 -46
  122. package/src/routes/__tests__/a2a-test-connection.test.ts +0 -177
  123. package/src/routes/a2a-bridge.ts +0 -266
  124. package/src/routes/a2a-test-connection.ts +0 -126
  125. package/src/routes/a2a.ts +0 -851
  126. package/src/routes/channel-bindings.ts +0 -11
  127. package/src/routes/channel-installations.ts +0 -33
  128. package/src/routes/index.ts +0 -640
  129. package/src/routes/menu-items.ts +0 -10
  130. package/src/routes/resource-routes.ts +0 -35
  131. package/src/routes/stt.ts +0 -102
  132. package/src/schemas/data-query.ts +0 -69
  133. package/src/schemas/index.ts +0 -852
  134. package/src/services/ExportImportService.ts +0 -296
  135. package/src/services/__tests__/ExportImportService.test.ts +0 -130
  136. package/src/services/__tests__/eval-runner.test.ts +0 -131
  137. package/src/services/__tests__/local-a2a.test.ts +0 -124
  138. package/src/services/agent_task_consumer.ts +0 -348
  139. package/src/services/collection_service.ts +0 -258
  140. package/src/services/eval-runner.ts +0 -283
  141. package/src/services/local-a2a.ts +0 -354
  142. package/src/services/queue_service.ts +0 -104
  143. package/src/services/sandbox_service.ts +0 -223
  144. package/src/services/skill_service.ts +0 -97
  145. package/src/services/supabase.ts +0 -60
  146. package/src/swagger.ts +0 -77
  147. package/src/types/index.ts +0 -118
  148. package/src/types/koa.d.ts +0 -7
  149. package/src/utils/__tests__/local-a2a-runtime.test.ts +0 -77
  150. package/src/utils/__tests__/path-containment.test.ts +0 -35
  151. package/src/utils/a2a-url.ts +0 -20
  152. package/src/utils/local-a2a-runtime.ts +0 -107
  153. package/src/utils/mime.ts +0 -44
  154. package/src/utils/path-containment.ts +0 -38
  155. package/tsconfig.json +0 -26
@@ -1,338 +0,0 @@
1
- import { FastifyRequest, FastifyReply } from "fastify";
2
- import { CreateRunRequest } from "../types";
3
- import { v4 } from "uuid";
4
- import {
5
- Agent,
6
- ThreadStatus,
7
- agentLatticeManager,
8
- agentInstanceManager,
9
- QueueMode,
10
- } from "@axiom-lattice/core";
11
- import { MessageChunkTypes } from "@axiom-lattice/protocols";
12
-
13
- function getUserId(request: FastifyRequest): string | undefined {
14
- const authUser = (request as any).user;
15
- if (authUser?.id) return authUser.id;
16
- return (request.headers["x-user-id"] as string) || undefined;
17
- }
18
-
19
- interface ResumeStreamRequest {
20
- thread_id: string;
21
- assistant_id: string
22
- message_id: string;
23
- known_content: string;
24
- poll_interval?: number;
25
- }
26
-
27
- // Create run
28
- export const createRun = async (
29
- request: FastifyRequest,
30
- reply: FastifyReply
31
- ): Promise<void> => {
32
- try {
33
- const {
34
- assistant_id,
35
- thread_id,
36
- message_id,
37
- command,
38
- streaming,
39
- background,
40
- custom_run_config,
41
- mode,
42
- ...input
43
- } = request.body as CreateRunRequest;
44
-
45
- const tenant_id = request.headers["x-tenant-id"] as string;
46
- const workspace_id = request.headers["x-workspace-id"] as string;
47
- const project_id = request.headers["x-project-id"] as string;
48
- const x_request_id = (request.headers["x-request-id"] as string) || v4();
49
- const user_id = getUserId(request);
50
-
51
- // Merge user_id, project_id, workspace_id into custom_run_config for downstream propagation
52
- const mergedConfig = {
53
- ...custom_run_config,
54
- ...(user_id && { user_id }),
55
- };
56
-
57
- // Validate request
58
- if (!assistant_id) {
59
- reply.status(400).send({
60
- success: false,
61
- error: "助手ID是必需的",
62
- });
63
- return;
64
- }
65
-
66
- // Get or create Agent instance (ensures single instance per thread)
67
- const agent = agentInstanceManager.getAgent({
68
- assistant_id,
69
- thread_id,
70
- tenant_id,
71
- workspace_id,
72
- project_id,
73
- custom_run_config: mergedConfig,
74
- });
75
-
76
-
77
- // ADR-026: background mode — enqueue only, returns 202.
78
- // Chunk delivery is handled by the thread-level resume_stream SSE.
79
- if (background) {
80
- const messageInput = message_id
81
- ? { ...input, id: message_id }
82
- : input;
83
-
84
- const result = await agent.addMessage({
85
- input: messageInput,
86
- command,
87
- custom_run_config: mergedConfig,
88
- }, mode as QueueMode);
89
-
90
- reply.status(202).send({
91
- success: true,
92
- messageId: result.messageId,
93
- queued: true,
94
- });
95
- return;
96
- }
97
-
98
- // Handle streaming requests
99
- if (streaming) {
100
- // Setup SSE
101
- reply.hijack();
102
- reply.raw.writeHead(200, {
103
- "Content-Type": "text/event-stream",
104
- "Cache-Control": "no-cache",
105
- Connection: "keep-alive",
106
- "Access-Control-Allow-Origin": "*",
107
- });
108
-
109
- try {
110
-
111
- // Execute agent with streaming
112
- // Only pass id if message_id is provided (for backward compatibility)
113
- const messageInput = message_id
114
- ? { ...input, id: message_id }
115
- : input;
116
-
117
- const result = await agent.addMessage({
118
- input: messageInput,
119
- command,
120
- custom_run_config: mergedConfig,
121
- }, mode as QueueMode);
122
-
123
- // const agentStatus = await agent.getRunStatus()
124
- // console.log(agentStatus)
125
- // if (agentStatus === "busy") {
126
- // reply.status(200).send({
127
- // success: true,
128
-
129
- // });
130
- // return
131
- // }
132
-
133
- const stream = agent.chunkStream((result).messageId, [MessageChunkTypes.MESSAGE_COMPLETED, MessageChunkTypes.INTERRUPT, MessageChunkTypes.MESSAGE_FAILED])
134
-
135
- // Forward all chunks to SSE
136
- for await (const chunk of stream) {
137
- const success = reply.raw.write(`data: ${JSON.stringify(chunk)}\n\n`);
138
- if (!success) {
139
- await new Promise(resolve => reply.raw.once('drain', resolve));
140
- }
141
- }
142
- } catch (error: any) {
143
- // Send error as SSE event
144
- const errorEvent = {
145
- type: 'error',
146
- data: {
147
- id: v4(),
148
- content: error.message || 'Stream processing error',
149
- },
150
- };
151
- reply.raw.write(`data: ${JSON.stringify(errorEvent)}\n\n`);
152
- } finally {
153
- reply.raw.end();
154
- }
155
- } else {
156
- // Non-streaming: use agentExecutor
157
- const { message: msg, ...restInputNonStream } = input;
158
- const result = await agent.invoke({
159
- input: { message: msg, ...restInputNonStream },
160
- command,
161
- custom_run_config: mergedConfig,
162
- });
163
- reply.status(200).send({
164
- success: true,
165
- ...result,
166
- });
167
- }
168
- } catch (error: any) {
169
- reply.status(500).send({
170
- success: false,
171
- error: `创建运行时发生错误: ${error.message}`,
172
- });
173
- }
174
- };
175
-
176
- // Resume stream from known position
177
- export const resumeStream = async (
178
- request: FastifyRequest,
179
- reply: FastifyReply
180
- ): Promise<void> => {
181
- try {
182
-
183
-
184
-
185
-
186
- const { thread_id, message_id, assistant_id, known_content, poll_interval } =
187
- request.body as ResumeStreamRequest;
188
- const tenant_id = request.headers["x-tenant-id"] as string;
189
- const workspace_id = request.headers["x-workspace-id"] as string;
190
- const project_id = request.headers["x-project-id"] as string;
191
-
192
- // Validate request data
193
- if (!thread_id || !message_id || !assistant_id || known_content === undefined) {
194
- reply.status(400).send({
195
- success: false,
196
- error: "thread_id, message_id, assistant_id, and known_content are required",
197
- });
198
- return;
199
- }
200
-
201
- // Notify Fastify that we will manually handle the response
202
- reply.hijack();
203
-
204
- // Set SSE response headers
205
- reply.raw.writeHead(200, {
206
- "Content-Type": "text/event-stream",
207
- "Cache-Control": "no-cache",
208
- Connection: "keep-alive",
209
- "Access-Control-Allow-Origin": "*",
210
- });
211
-
212
- try {
213
- // Create Agent instance for accessing chunk buffer
214
- // Get or create Agent instance (ensures single instance per thread)
215
- const agent = agentInstanceManager.getAgent({
216
- assistant_id,
217
- thread_id,
218
- tenant_id,
219
- workspace_id,
220
- project_id,
221
- });
222
-
223
- // ADR-026: Thread-level SSE, lives until client disconnect.
224
- // Empty stopTypes means the iterator never stops on its own —
225
- // MESSAGE_COMPLETED, THREAD_IDLE etc. pass through as data.
226
- // Client disconnect (close event) or client abort (reply.raw.destroyed)
227
- // terminate the loop.
228
- const stream = agent.chunkStream(message_id, []);
229
- let closed = false;
230
- request.raw.on('close', () => { closed = true; });
231
-
232
- for await (const chunk of stream) {
233
- if (closed || reply.raw.destroyed) break;
234
- reply.raw.write(`data: ${JSON.stringify(chunk)}\n\n`);
235
- }
236
- } catch (error: any) {
237
- // Send error as SSE event before closing (following MessageChunk format)
238
- const errorEvent = {
239
- type: 'error',
240
- data: {
241
- id: v4(),
242
- content: error.message || 'Resume stream processing error'
243
- }
244
- };
245
- reply.raw.write(`data: ${JSON.stringify(errorEvent)}\n\n`);
246
- } finally {
247
- reply.raw.end();
248
- }
249
- } catch (error: any) {
250
- reply.status(500).send({
251
- success: false,
252
- error: `Error resuming stream: ${error.message}`,
253
- });
254
- }
255
- };
256
-
257
- // Abort agent execution for a thread
258
- export const abortRun = async (
259
- request: FastifyRequest,
260
- reply: FastifyReply
261
- ): Promise<void> => {
262
- try {
263
- const { assistantId, threadId } = request.params as {
264
- assistantId: string;
265
- threadId: string;
266
- };
267
- const tenant_id = request.headers["x-tenant-id"] as string;
268
- const workspace_id = (request.headers["x-workspace-id"] as string) || "default";
269
- const project_id = (request.headers["x-project-id"] as string) || "default";
270
-
271
- const agent = agentInstanceManager.getAgent({
272
- assistant_id: assistantId,
273
- thread_id: threadId,
274
- tenant_id,
275
- workspace_id,
276
- project_id,
277
- });
278
-
279
- await agent.abort();
280
-
281
- reply.status(200).send({
282
- success: true,
283
- message: "Agent aborted",
284
- });
285
- } catch (error: any) {
286
- reply.status(500).send({
287
- success: false,
288
- error: `Abort failed: ${error.message}`,
289
- });
290
- }
291
- };
292
-
293
- /**
294
- * Recover agent execution after a crash or manual abort.
295
- *
296
- * Resumes the LangGraph from its last checkpoint if the graph was
297
- * mid-execution (BUSY). Otherwise cleans up stuck queue messages
298
- * and restarts the queue processor.
299
- *
300
- * Pairs with {@link abortRun} — use abort to stop, recover to continue.
301
- */
302
- export const recoverRun = async (
303
- request: FastifyRequest,
304
- reply: FastifyReply
305
- ): Promise<void> => {
306
- try {
307
- const { assistantId, threadId } = request.params as {
308
- assistantId: string;
309
- threadId: string;
310
- };
311
- const tenant_id = request.headers["x-tenant-id"] as string;
312
- const workspace_id = (request.headers["x-workspace-id"] as string) || "default";
313
- const project_id = (request.headers["x-project-id"] as string) || "default";
314
-
315
- const agent = agentInstanceManager.getAgent({
316
- assistant_id: assistantId,
317
- thread_id: threadId,
318
- tenant_id,
319
- workspace_id,
320
- project_id,
321
- });
322
-
323
- await agent.resumeTask();
324
-
325
- const status = await agent.getRunStatus();
326
-
327
- reply.status(200).send({
328
- success: true,
329
- threadId,
330
- status,
331
- });
332
- } catch (error: any) {
333
- reply.status(500).send({
334
- success: false,
335
- error: `Recover failed: ${error.message}`,
336
- });
337
- }
338
- };
@@ -1,255 +0,0 @@
1
- import { FastifyInstance } from "fastify";
2
- import { sandboxService } from "../services/sandbox_service";
3
- import { getSandBoxManager } from "@axiom-lattice/core";
4
- import { getContentTypeFromFilename, getFilenameFromPath } from "../utils/mime";
5
-
6
- interface SandboxParams {
7
- assistantId: string;
8
- threadId: string;
9
- }
10
-
11
- export function registerSandboxProxyRoutes(app: FastifyInstance): void {
12
- // Register uploadfile route FIRST before wildcard routes to ensure it matches
13
- app.post<{ Params: SandboxParams }>(
14
- "/api/assistants/:assistantId/threads/:threadId/sandbox/uploadfile",
15
- async (request, reply) => {
16
- console.log("[Sandbox Upload] Route matched:", request.url);
17
- const { assistantId, threadId } = request.params;
18
- const tenantId = (request.headers["x-tenant-id"] as string) || "default";
19
-
20
- const vmIsolation = sandboxService.getFilesystemVmIsolation(tenantId, assistantId);
21
- if (!vmIsolation) {
22
- return reply.status(500).send({ error: "Assistant sandbox config not found" });
23
- }
24
-
25
- const workspaceId = request.headers["x-workspace-id"] as string;
26
- const projectId = request.headers["x-project-id"] as string;
27
-
28
- const sandboxManager = getSandBoxManager();
29
- const sandbox = await sandboxManager.getSandboxFromConfig({
30
- assistant_id: assistantId,
31
- thread_id: threadId,
32
- tenantId,
33
- workspaceId,
34
- projectId,
35
- vmIsolation,
36
- });
37
-
38
- try {
39
- const data = await request.file();
40
- if (!data) {
41
- return reply.status(400).send({ error: "No file in request" });
42
- }
43
-
44
- const buffer = await data.toBuffer();
45
- const pathEntry = data.fields?.path;
46
- const pathValue =
47
- pathEntry && typeof pathEntry === "object" && "value" in pathEntry
48
- ? String((pathEntry as { value: unknown }).value)
49
- : typeof pathEntry === "string"
50
- ? pathEntry
51
- : undefined;
52
-
53
- const filePath = `/uploads/${pathValue ? pathValue : ""}${data.filename}`;
54
- try {
55
- await sandbox.file.uploadFile({ file: filePath, data: buffer });
56
- } catch (err) {
57
- reply.status(500).send({ error: String(err) });
58
- return;
59
- }
60
-
61
- const relativePath = filePath.replace(`~/`, "");
62
- const result = { id: relativePath, name: data.filename, size: buffer.length };
63
-
64
- return reply.status(200).send({ message: "File uploaded successfully", ...result });
65
- } catch (error: unknown) {
66
- const message = error instanceof Error ? error.message : String(error);
67
- return reply.status(502).send({ error: `Upload proxy error: ${message}` });
68
- }
69
- }
70
- );
71
-
72
- // Download file from sandbox: GET with query param path (file path in sandbox, e.g. /home/gem/uploads/foo.txt)
73
- app.get<{
74
- Params: SandboxParams;
75
- Querystring: { path: string };
76
- }>(
77
- "/api/assistants/:assistantId/threads/:threadId/sandbox/downloadfile",
78
- async (request, reply) => {
79
- const { assistantId, threadId } = request.params;
80
- const { path: filePath } = request.query;
81
- const tenantId = (request.headers["x-tenant-id"] as string) || "default";
82
-
83
- if (!filePath || typeof filePath !== "string") {
84
- return reply.status(400).send({ error: "Query parameter 'path' is required" });
85
- }
86
-
87
- const vmIsolation = sandboxService.getFilesystemVmIsolation(tenantId, assistantId);
88
- if (!vmIsolation) {
89
- return reply.status(500).send({ error: "Assistant filesystem vmIsolation not found" });
90
- }
91
-
92
- const workspaceId = request.headers["x-workspace-id"] as string;
93
- const projectId = request.headers["x-project-id"] as string;
94
-
95
- const sandboxManager = getSandBoxManager();
96
- const sandbox = await sandboxManager.getSandboxFromConfig({
97
- assistant_id: assistantId,
98
- thread_id: threadId,
99
- tenantId,
100
- workspaceId,
101
- projectId,
102
- vmIsolation,
103
- });
104
-
105
- try {
106
- // Normalize path to /-prefixed absolute path
107
- const resolvedPath = filePath.startsWith("/") ? filePath : `/${filePath}`;
108
- const filename = getFilenameFromPath(resolvedPath);
109
- const inferredContentType = getContentTypeFromFilename(filename);
110
-
111
- try {
112
- const buf = await sandbox.file.downloadFile({ file: resolvedPath });
113
- const contentDisposition = `inline; filename="${filename.replace(/"/g, '\\"')}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
114
- reply = reply.status(200).type(inferredContentType).header("Content-Disposition", contentDisposition).send(buf);
115
- return reply;
116
- } catch (err) {
117
- reply.status(500).send({ error: String(err) });
118
- return;
119
- }
120
- } catch (error: unknown) {
121
- const message = error instanceof Error ? error.message : String(error);
122
- return reply.status(502).send({ error: `Download proxy error: ${message}` });
123
- }
124
- }
125
- );
126
-
127
- // app.get<{ Params: SandboxParams }>(
128
- // "/api/assistants/:assistantId/threads/:threadId/sandbox/browser",
129
- // async (request, reply) => {
130
- // const { assistantId, threadId } = request.params;
131
-
132
- // const sandboxConfig = sandboxService.getSandboxConfig(assistantId);
133
- // if (!sandboxConfig) {
134
- // const errorHtml = sandboxService.generateErrorHtml(
135
- // assistantId,
136
- // threadId,
137
- // "unknown",
138
- // `Assistant ${assistantId} not found`
139
- // );
140
- // return reply.status(404).type("text/html").send(errorHtml);
141
- // }
142
-
143
- // const { vmIsolation } = sandboxConfig;
144
- // const sandboxName = sandboxService.computeSandboxName(
145
- // assistantId,
146
- // threadId,
147
- // vmIsolation
148
- // );
149
-
150
- // try {
151
- // const html = await sandboxService.getVncHtml(sandboxName);
152
- // const rewrittenHtml = sandboxService.rewriteHtml(html, assistantId, threadId);
153
- // return reply.type("text/html").send(rewrittenHtml);
154
- // } catch (error: any) {
155
- // const errorHtml = sandboxService.generateErrorHtml(
156
- // assistantId,
157
- // threadId,
158
- // vmIsolation,
159
- // error.message || "Failed to connect to sandbox"
160
- // );
161
- // return reply.status(502).type("text/html").send(errorHtml);
162
- // }
163
- // }
164
- // );
165
-
166
-
167
- // app.get<{ Params: SandboxParams }>(
168
- // "/api/assistants/:assistantId/threads/:threadId/sandbox/websockify",
169
- // { websocket: true },
170
-
171
- // (connection, request) => {
172
-
173
- // const url = (connection?.url) as string;
174
- // console.log(`[WebSocket] Received connection from URL: ${url}`);
175
-
176
- // const urlMatch = url.match(/\/api\/assistants\/([^/]+)\/threads\/([^/]+)\/sandbox\/websockify/);
177
- // if (!urlMatch) {
178
- // console.error(`[WebSocket] Failed to parse params from URL: ${url}`);
179
- // connection.close(1008, "Invalid URL format");
180
- // return;
181
- // }
182
-
183
- // const assistantId = urlMatch[1];
184
- // const threadId = urlMatch[2];
185
- // console.log(`[WebSocket] Parsed params - assistantId: ${assistantId}, threadId: ${threadId}`);
186
-
187
- // const sandboxConfig = sandboxService.getSandboxConfig(assistantId);
188
- // if (!sandboxConfig) {
189
- // console.error(`[WebSocket] Assistant ${assistantId} not found`);
190
- // connection.close(1008, "Assistant not found");
191
- // return;
192
- // }
193
-
194
- // const { vmIsolation } = sandboxConfig;
195
- // const sandboxName = sandboxService.computeSandboxName(
196
- // assistantId,
197
- // threadId,
198
- // vmIsolation
199
- // );
200
-
201
- // const targetUrl = sandboxService.getTargetUrl(sandboxName);
202
- // const targetWsUrl = targetUrl.replace(/^http/, "ws").replace(/^https/, "wss") + "/websockify";
203
-
204
- // console.log(`[WebSocket] Connecting to target: ${targetWsUrl}`);
205
-
206
- // const targetSocket = new WebSocket(targetWsUrl);
207
- // const clientStream = createWebSocketStream(connection, { encoding: "utf8" });
208
-
209
- // const targetStream = createWebSocketStream(targetSocket, { encoding: "utf8" });
210
-
211
- // const forward = pipeline(clientStream, targetStream);
212
- // const backward = pipeline(targetStream, clientStream);
213
-
214
- // Promise.all([forward, backward]).catch((err) => {
215
- // console.error(`[WebSocket] Proxy pipeline failed:`, err.message);
216
- // targetSocket.terminate();
217
- // connection.terminate();
218
- // });
219
- // }
220
- // );
221
-
222
- // app.get<{ Params: ProxyParams }>(
223
- // "/api/assistants/:assistantId/threads/:threadId/sandbox/browser/vnc/*",
224
- // async (request, reply) => {
225
- // const { assistantId, threadId, "*": restPath } = request.params;
226
-
227
- // const sandboxConfig = sandboxService.getSandboxConfig(assistantId);
228
- // if (!sandboxConfig) {
229
- // return reply.status(404).send("Assistant not found");
230
- // }
231
-
232
- // const { vmIsolation } = sandboxConfig;
233
- // const sandboxName = sandboxService.computeSandboxName(
234
- // assistantId,
235
- // threadId,
236
- // vmIsolation
237
- // );
238
-
239
- // const targetPath = restPath ? `/vnc/${restPath}` : "/vnc/";
240
- // const targetUrl = `${sandboxService.getTargetUrl(sandboxName)}${targetPath}`;
241
-
242
- // try {
243
- // const response = await fetch(targetUrl);
244
- // const contentType = response.headers.get("content-type") || "application/octet-stream";
245
-
246
- // const body = await response.arrayBuffer();
247
- // reply.status(response.status).type(contentType).send(Buffer.from(body));
248
- // } catch (error: any) {
249
- // reply.status(502).send(`Proxy error: ${error.message}`);
250
- // }
251
- // }
252
- // );
253
-
254
-
255
- }