@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/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +8 -0
- package/dist/index.js +134 -48
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +124 -38
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/src/controllers/sandbox.ts +235 -51
- package/src/index.ts +8 -0
- package/src/services/agent_service.ts +2 -0
- package/src/services/sandbox_service.ts +3 -8
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
|
-
|
|
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
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
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
|
-
|
|
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
|
|
2198
|
-
|
|
2199
|
-
|
|
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
|
|
2202
|
-
|
|
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/
|
|
2249
|
+
"/api/assistants/:assistantId/threads/:threadId/sandbox/downloadfile",
|
|
2213
2250
|
async (request, reply) => {
|
|
2214
|
-
const { assistantId, threadId
|
|
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
|
|
2226
|
-
const
|
|
2266
|
+
const sandboxManager = getSandBoxManager2("default");
|
|
2267
|
+
const sandbox = await sandboxManager.createSandbox(sandboxName);
|
|
2227
2268
|
try {
|
|
2228
|
-
const
|
|
2229
|
-
const
|
|
2230
|
-
const
|
|
2231
|
-
|
|
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
|
-
|
|
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) => {
|