@axiom-lattice/gateway 2.1.22 → 2.1.23

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/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/index.ts
2
2
  import fastify from "fastify";
3
3
  import cors from "@fastify/cors";
4
+ import multipart from "@fastify/multipart";
4
5
  import sensible from "@fastify/sensible";
5
6
  import websocket from "@fastify/websocket";
6
7
 
@@ -61,6 +62,7 @@ async function agent_invoke({
61
62
  "x-tenant-id": tenant_id,
62
63
  "x-request-id": run_id,
63
64
  "x-thread-id": thread_id,
65
+ "x-assistant-id": assistant_id,
64
66
  runConfig
65
67
  // Inject runConfig for tools to access
66
68
  },
@@ -115,6 +117,7 @@ async function agent_stream({
115
117
  "x-tenant-id": tenant_id,
116
118
  "x-request-id": run_id,
117
119
  "x-thread-id": thread_id,
120
+ "x-assistant-id": assistant_id,
118
121
  runConfig
119
122
  // Inject runConfig for tools to access
120
123
  },
@@ -1986,9 +1989,11 @@ var getHealthSchema = {
1986
1989
  }
1987
1990
  };
1988
1991
 
1992
+ // src/controllers/sandbox.ts
1993
+ import { Readable } from "stream";
1994
+
1989
1995
  // src/services/sandbox_service.ts
1990
- import { getAgentConfig, getAgentLattice as getAgentLattice2, normalizeSandboxName } from "@axiom-lattice/core";
1991
- var SANDBOX_BASE_URL = process.env.SANDBOX_BASE_URL || "http://localhost:8080";
1996
+ import { getAgentConfig, getAgentLattice as getAgentLattice2, getSandBoxManager, normalizeSandboxName } from "@axiom-lattice/core";
1992
1997
  var ERROR_HTML = `<!DOCTYPE html>
1993
1998
  <html lang="zh-CN">
1994
1999
  <head>
@@ -2099,9 +2104,6 @@ var ERROR_HTML = `<!DOCTYPE html>
2099
2104
  </body>
2100
2105
  </html>`;
2101
2106
  var SandboxService = class {
2102
- constructor(baseUrl) {
2103
- this.baseUrl = baseUrl || SANDBOX_BASE_URL;
2104
- }
2105
2107
  getSandboxConfig(assistantId) {
2106
2108
  const agentConfig = getAgentConfig(assistantId);
2107
2109
  if (!agentConfig) {
@@ -2127,7 +2129,8 @@ var SandboxService = class {
2127
2129
  return normalizeSandboxName(sandboxName);
2128
2130
  }
2129
2131
  getTargetUrl(sandboxName) {
2130
- return `${this.baseUrl}/sandbox/${sandboxName}`;
2132
+ const sandboxManager = getSandBoxManager("default");
2133
+ return `${sandboxManager.getBaseURL()}/sandbox/${sandboxName}`;
2131
2134
  }
2132
2135
  async getVncHtml(sandboxName) {
2133
2136
  const response = await fetch(`${this.getTargetUrl(sandboxName)}/vnc/index.html`);
@@ -2171,21 +2174,41 @@ var SandboxService = class {
2171
2174
  var sandboxService = new SandboxService();
2172
2175
 
2173
2176
  // src/controllers/sandbox.ts
2174
- var SANDBOX_BASE_URL2 = process.env.SANDBOX_BASE_URL || "http://localhost:8080";
2175
- async function registerSandboxProxyRoutes(app2) {
2176
- app2.get(
2177
- "/api/assistants/:assistantId/threads/:threadId/sandbox",
2177
+ import { getSandBoxManager as getSandBoxManager2 } from "@axiom-lattice/core";
2178
+ function getFilenameFromPath(path) {
2179
+ const segments = path.replace(/\/+$/, "").split("/");
2180
+ return segments[segments.length - 1] || "download";
2181
+ }
2182
+ var EXT_TO_MIME = {
2183
+ ".txt": "text/plain",
2184
+ ".html": "text/html",
2185
+ ".css": "text/css",
2186
+ ".js": "application/javascript",
2187
+ ".json": "application/json",
2188
+ ".pdf": "application/pdf",
2189
+ ".png": "image/png",
2190
+ ".jpg": "image/jpeg",
2191
+ ".jpeg": "image/jpeg",
2192
+ ".gif": "image/gif",
2193
+ ".webp": "image/webp",
2194
+ ".svg": "image/svg+xml",
2195
+ ".zip": "application/zip",
2196
+ ".csv": "text/csv",
2197
+ ".xml": "application/xml"
2198
+ };
2199
+ function getContentTypeFromFilename(filename) {
2200
+ const ext = filename.includes(".") ? filename.slice(filename.lastIndexOf(".")).toLowerCase() : "";
2201
+ return EXT_TO_MIME[ext] ?? "application/octet-stream";
2202
+ }
2203
+ function registerSandboxProxyRoutes(app2) {
2204
+ app2.post(
2205
+ "/api/assistants/:assistantId/threads/:threadId/sandbox/uploadfile",
2178
2206
  async (request, reply) => {
2207
+ console.log("[Sandbox Upload] Route matched:", request.url);
2179
2208
  const { assistantId, threadId } = request.params;
2180
2209
  const sandboxConfig = sandboxService.getSandboxConfig(assistantId);
2181
2210
  if (!sandboxConfig) {
2182
- const errorHtml = sandboxService.generateErrorHtml(
2183
- assistantId,
2184
- threadId,
2185
- "unknown",
2186
- `Assistant ${assistantId} not found`
2187
- );
2188
- return reply.status(404).type("text/html").send(errorHtml);
2211
+ return reply.status(500).send({ error: "Assistant sandbox config not found" });
2189
2212
  }
2190
2213
  const { isolatedLevel } = sandboxConfig;
2191
2214
  const sandboxName = sandboxService.computeSandboxName(
@@ -2193,28 +2216,46 @@ async function registerSandboxProxyRoutes(app2) {
2193
2216
  threadId,
2194
2217
  isolatedLevel
2195
2218
  );
2219
+ const sandboxManager = getSandBoxManager2("default");
2220
+ const sandbox = await sandboxManager.createSandbox(sandboxName);
2196
2221
  try {
2197
- const html = await sandboxService.getVncHtml(sandboxName);
2198
- const rewrittenHtml = sandboxService.rewriteHtml(html, assistantId, threadId);
2199
- return reply.type("text/html").send(rewrittenHtml);
2222
+ const data = await request.file();
2223
+ if (!data) {
2224
+ return reply.status(400).send({ error: "No file in request" });
2225
+ }
2226
+ const buffer = await data.toBuffer();
2227
+ const pathEntry = data.fields?.path;
2228
+ const pathValue = pathEntry && typeof pathEntry === "object" && "value" in pathEntry ? String(pathEntry.value) : typeof pathEntry === "string" ? pathEntry : void 0;
2229
+ const formData = new FormData();
2230
+ formData.append("file", new Blob([buffer]), data.filename ?? "file");
2231
+ const path = `/home/gem/uploads/${pathValue ? pathValue : ""}${data.filename}`;
2232
+ const uploadResult = await sandbox.file.uploadFile({
2233
+ file: buffer,
2234
+ path
2235
+ });
2236
+ if (!uploadResult.ok) {
2237
+ return reply.status(502).send({ error: `Upload error: ${uploadResult.error}` });
2238
+ }
2239
+ const relativePath = uploadResult.body?.data?.file_path.replace(`/home/gem`, "");
2240
+ const result = { id: relativePath, name: data.filename, size: buffer.length };
2241
+ return reply.status(200).send({ message: "File uploaded successfully", ...result });
2200
2242
  } catch (error) {
2201
- const errorHtml = sandboxService.generateErrorHtml(
2202
- assistantId,
2203
- threadId,
2204
- isolatedLevel,
2205
- error.message || "Failed to connect to sandbox"
2206
- );
2207
- return reply.status(502).type("text/html").send(errorHtml);
2243
+ const message = error instanceof Error ? error.message : String(error);
2244
+ return reply.status(502).send({ error: `Upload proxy error: ${message}` });
2208
2245
  }
2209
2246
  }
2210
2247
  );
2211
2248
  app2.get(
2212
- "/api/assistants/:assistantId/threads/:threadId/sandbox/vnc/*",
2249
+ "/api/assistants/:assistantId/threads/:threadId/sandbox/downloadfile",
2213
2250
  async (request, reply) => {
2214
- const { assistantId, threadId, "*": restPath } = request.params;
2251
+ const { assistantId, threadId } = request.params;
2252
+ const { path: filePath } = request.query;
2253
+ if (!filePath || typeof filePath !== "string") {
2254
+ return reply.status(400).send({ error: "Query parameter 'path' is required" });
2255
+ }
2215
2256
  const sandboxConfig = sandboxService.getSandboxConfig(assistantId);
2216
2257
  if (!sandboxConfig) {
2217
- return reply.status(404).send("Assistant not found");
2258
+ return reply.status(404).send({ error: "Assistant sandbox config not found" });
2218
2259
  }
2219
2260
  const { isolatedLevel } = sandboxConfig;
2220
2261
  const sandboxName = sandboxService.computeSandboxName(
@@ -2222,15 +2263,53 @@ async function registerSandboxProxyRoutes(app2) {
2222
2263
  threadId,
2223
2264
  isolatedLevel
2224
2265
  );
2225
- const targetPath = restPath ? `/vnc/${restPath}` : "/vnc/";
2226
- const targetUrl = `${sandboxService.getTargetUrl(sandboxName)}${targetPath}`;
2266
+ const sandboxManager = getSandBoxManager2("default");
2267
+ const sandbox = await sandboxManager.createSandbox(sandboxName);
2227
2268
  try {
2228
- const response = await fetch(targetUrl);
2229
- const contentType = response.headers.get("content-type") || "application/octet-stream";
2230
- const body = await response.arrayBuffer();
2231
- reply.status(response.status).type(contentType).send(Buffer.from(body));
2269
+ const resolvedPath = filePath.startsWith("/home/gem") ? filePath : `/home/gem/${filePath.replace(/^\//, "")}`;
2270
+ const filename = getFilenameFromPath(resolvedPath);
2271
+ const inferredContentType = getContentTypeFromFilename(filename);
2272
+ const downloadResult = await sandbox.file.downloadFile({
2273
+ path: resolvedPath
2274
+ });
2275
+ if (!downloadResult.ok) {
2276
+ return reply.status(502).send({
2277
+ error: `Download error: ${JSON.stringify(downloadResult.error)}`
2278
+ });
2279
+ }
2280
+ const body = downloadResult.body;
2281
+ if (typeof body?.stream === "function") {
2282
+ const webStream = body.stream();
2283
+ const nodeStream = Readable.fromWeb(webStream);
2284
+ const contentType2 = body.contentType ?? inferredContentType;
2285
+ const contentDisposition2 = body.contentDisposition ?? `inline; filename="${filename.replace(/"/g, '\\"')}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
2286
+ reply = reply.status(200).type(contentType2).header("Content-Disposition", contentDisposition2).send(nodeStream);
2287
+ return reply;
2288
+ }
2289
+ const bodyUnknown = downloadResult.body;
2290
+ let buf;
2291
+ let contentType = inferredContentType;
2292
+ let contentDisposition = `inline; filename="${filename.replace(/"/g, '\\"')}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
2293
+ if (bodyUnknown instanceof ArrayBuffer) {
2294
+ buf = Buffer.from(bodyUnknown);
2295
+ } else if (bodyUnknown instanceof Buffer) {
2296
+ buf = bodyUnknown;
2297
+ } else if (bodyUnknown && typeof bodyUnknown.arrayBuffer === "function") {
2298
+ const res = bodyUnknown;
2299
+ buf = Buffer.from(await res.arrayBuffer());
2300
+ if (res.headers?.get("content-type")) contentType = res.headers.get("content-type");
2301
+ if (res.headers?.get("content-disposition")) contentDisposition = res.headers.get("content-disposition");
2302
+ } else if (bodyUnknown && typeof bodyUnknown.blob === "function") {
2303
+ const blob = await bodyUnknown.blob();
2304
+ buf = Buffer.from(await blob.arrayBuffer());
2305
+ } else {
2306
+ return reply.status(502).send({ error: "Unexpected download response format" });
2307
+ }
2308
+ reply = reply.status(200).type(contentType).header("Content-Disposition", contentDisposition).send(buf);
2309
+ return reply;
2232
2310
  } catch (error) {
2233
- reply.status(502).send(`Proxy error: ${error.message}`);
2311
+ const message = error instanceof Error ? error.message : String(error);
2312
+ return reply.status(502).send({ error: `Download proxy error: ${message}` });
2234
2313
  }
2235
2314
  }
2236
2315
  );
@@ -2773,12 +2852,19 @@ app.register(cors, {
2773
2852
  "Authorization",
2774
2853
  "X-Requested-With",
2775
2854
  "x-tenant-id",
2776
- "x-request-id"
2855
+ "x-request-id",
2856
+ "x-assistant-id",
2857
+ "x-thread-id"
2777
2858
  ],
2778
2859
  exposedHeaders: ["Content-Type"],
2779
2860
  credentials: true
2780
2861
  });
2781
2862
  app.register(sensible);
2863
+ app.register(multipart, {
2864
+ limits: {
2865
+ fileSize: Number(process.env.BODY_LIMIT) || 50 * 1024 * 1024
2866
+ }
2867
+ });
2782
2868
  app.register(websocket);
2783
2869
  app.setErrorHandler((error, request, reply) => {
2784
2870
  const getHeaderValue = (header) => {