isol8 0.6.1 → 0.6.2
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/README.md +1 -0
- package/dist/cli.js +123 -23
- package/dist/index.js +36 -2
- package/dist/src/server/index.d.ts +2 -0
- package/dist/src/server/index.d.ts.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -147,6 +147,7 @@ isol8 serve --update # Force re-download the server binary
|
|
|
147
147
|
| `-p, --port <port>` | Port to listen on | `3000` |
|
|
148
148
|
| `-k, --key <key>` | API key for Bearer token auth | `$ISOL8_API_KEY` |
|
|
149
149
|
| `--update` | Force re-download the server binary | `false` |
|
|
150
|
+
| `--debug` | Enable debug logging for server operations | `false` |
|
|
150
151
|
|
|
151
152
|
### `isol8 config`
|
|
152
153
|
|
package/dist/cli.js
CHANGED
|
@@ -6929,11 +6929,6 @@ var require_utils2 = __commonJS((exports, module) => {
|
|
|
6929
6929
|
};
|
|
6930
6930
|
});
|
|
6931
6931
|
|
|
6932
|
-
// node_modules/ssh2/lib/protocol/crypto/build/Release/sshcrypto.node
|
|
6933
|
-
var require_sshcrypto = __commonJS((exports, module) => {
|
|
6934
|
-
module.exports = __require("./sshcrypto-0209sx47.node");
|
|
6935
|
-
});
|
|
6936
|
-
|
|
6937
6932
|
// node_modules/ssh2/lib/protocol/crypto/poly1305.js
|
|
6938
6933
|
var require_poly1305 = __commonJS((exports, module) => {
|
|
6939
6934
|
var __dirname = "/home/runner/work/isol8/isol8/node_modules/ssh2/lib/protocol/crypto", __filename = "/home/runner/work/isol8/isol8/node_modules/ssh2/lib/protocol/crypto/poly1305.js";
|
|
@@ -7420,7 +7415,7 @@ var require_crypto = __commonJS((exports, module) => {
|
|
|
7420
7415
|
var ChaChaPolyDecipher;
|
|
7421
7416
|
var GenericDecipher;
|
|
7422
7417
|
try {
|
|
7423
|
-
binding =
|
|
7418
|
+
binding = (()=>{throw new Error("Cannot require module "+"./crypto/build/Release/sshcrypto.node");})();
|
|
7424
7419
|
({
|
|
7425
7420
|
AESGCMCipher,
|
|
7426
7421
|
ChaChaPolyCipher,
|
|
@@ -54990,11 +54985,6 @@ var init_runtime = __esm(() => {
|
|
|
54990
54985
|
});
|
|
54991
54986
|
|
|
54992
54987
|
// src/utils/logger.ts
|
|
54993
|
-
var exports_logger = {};
|
|
54994
|
-
__export(exports_logger, {
|
|
54995
|
-
logger: () => logger
|
|
54996
|
-
});
|
|
54997
|
-
|
|
54998
54988
|
class Logger {
|
|
54999
54989
|
debugMode = false;
|
|
55000
54990
|
setDebug(enabled) {
|
|
@@ -55968,7 +55958,7 @@ var package_default;
|
|
|
55968
55958
|
var init_package = __esm(() => {
|
|
55969
55959
|
package_default = {
|
|
55970
55960
|
name: "isol8",
|
|
55971
|
-
version: "0.6.
|
|
55961
|
+
version: "0.6.1",
|
|
55972
55962
|
description: "Secure code execution engine for AI agents",
|
|
55973
55963
|
author: "Illusion47586",
|
|
55974
55964
|
license: "MIT",
|
|
@@ -56030,6 +56020,7 @@ var init_package = __esm(() => {
|
|
|
56030
56020
|
devDependencies: {
|
|
56031
56021
|
"@biomejs/biome": "^2.3.15",
|
|
56032
56022
|
"@semantic-release/changelog": "^6.0.3",
|
|
56023
|
+
"@semantic-release/exec": "^7.1.0",
|
|
56033
56024
|
"@semantic-release/git": "^10.0.1",
|
|
56034
56025
|
"@semantic-release/github": "^12.0.6",
|
|
56035
56026
|
"@semantic-release/npm": "^13.1.4",
|
|
@@ -57696,13 +57687,21 @@ __export(exports_server, {
|
|
|
57696
57687
|
async function createServer(options) {
|
|
57697
57688
|
const { DockerIsol8: DockerIsol82 } = await Promise.resolve().then(() => (init_docker(), exports_docker));
|
|
57698
57689
|
await Promise.resolve().then(() => (init_runtime(), exports_runtime));
|
|
57690
|
+
if (options.debug) {
|
|
57691
|
+
logger.setDebug(true);
|
|
57692
|
+
}
|
|
57699
57693
|
const config = loadConfig();
|
|
57694
|
+
logger.debug("[Server] Config loaded");
|
|
57695
|
+
logger.debug(`[Server] Max concurrent: ${config.maxConcurrent}`);
|
|
57696
|
+
logger.debug(`[Server] Auto-prune: ${config.cleanup.autoPrune}`);
|
|
57700
57697
|
const app = new Hono2;
|
|
57701
57698
|
const globalSemaphore = new Semaphore(config.maxConcurrent);
|
|
57702
57699
|
app.use("*", authMiddleware(options.apiKey));
|
|
57703
57700
|
app.get("/health", (c) => c.json({ status: "ok", version: VERSION }));
|
|
57704
57701
|
app.post("/execute", async (c) => {
|
|
57705
57702
|
const body = await c.req.json();
|
|
57703
|
+
logger.debug(`[Server] POST /execute runtime=${body.request.runtime} sessionId=${body.sessionId ?? "ephemeral"}`);
|
|
57704
|
+
logger.debug(`[Server] Code length: ${body.request.code.length} chars`);
|
|
57706
57705
|
const engineOptions = {
|
|
57707
57706
|
network: config.defaults.network,
|
|
57708
57707
|
memoryLimit: config.defaults.memoryLimit,
|
|
@@ -57717,36 +57716,45 @@ async function createServer(options) {
|
|
|
57717
57716
|
if (body.sessionId) {
|
|
57718
57717
|
const session = sessions.get(body.sessionId);
|
|
57719
57718
|
if (session) {
|
|
57719
|
+
logger.debug(`[Server] Reusing existing session: ${body.sessionId}`);
|
|
57720
57720
|
engine = session.engine;
|
|
57721
57721
|
session.lastAccessedAt = Date.now();
|
|
57722
57722
|
} else {
|
|
57723
|
+
logger.debug(`[Server] Creating new session: ${body.sessionId}`);
|
|
57723
57724
|
engine = new DockerIsol82(engineOptions, config.maxConcurrent);
|
|
57724
57725
|
await engine.start();
|
|
57725
57726
|
sessions.set(body.sessionId, { engine, lastAccessedAt: Date.now() });
|
|
57726
57727
|
}
|
|
57727
57728
|
} else {
|
|
57729
|
+
logger.debug("[Server] Creating ephemeral engine");
|
|
57728
57730
|
engine = new DockerIsol82(engineOptions, config.maxConcurrent);
|
|
57729
57731
|
await engine.start();
|
|
57730
57732
|
}
|
|
57731
57733
|
try {
|
|
57734
|
+
logger.debug("[Server] Acquiring semaphore for /execute");
|
|
57732
57735
|
await globalSemaphore.acquire();
|
|
57733
57736
|
try {
|
|
57734
57737
|
const result = await engine.execute(body.request);
|
|
57738
|
+
logger.debug(`[Server] Execution completed: exitCode=${result.exitCode} duration=${result.durationMs}ms`);
|
|
57735
57739
|
return c.json(result);
|
|
57736
57740
|
} finally {
|
|
57737
57741
|
globalSemaphore.release();
|
|
57738
57742
|
}
|
|
57739
57743
|
} catch (err) {
|
|
57740
57744
|
const message = err instanceof Error ? err.message : String(err);
|
|
57745
|
+
logger.debug(`[Server] Execution error: ${message}`);
|
|
57741
57746
|
return c.json({ error: message }, 500);
|
|
57742
57747
|
} finally {
|
|
57743
57748
|
if (!body.sessionId) {
|
|
57749
|
+
logger.debug("[Server] Cleaning up ephemeral engine");
|
|
57744
57750
|
await engine.stop();
|
|
57745
57751
|
}
|
|
57746
57752
|
}
|
|
57747
57753
|
});
|
|
57748
57754
|
app.post("/execute/stream", async (c) => {
|
|
57749
57755
|
const body = await c.req.json();
|
|
57756
|
+
logger.debug(`[Server] POST /execute/stream runtime=${body.request.runtime}`);
|
|
57757
|
+
logger.debug(`[Server] Code length: ${body.request.code.length} chars`);
|
|
57750
57758
|
const engineOptions = {
|
|
57751
57759
|
network: config.defaults.network,
|
|
57752
57760
|
memoryLimit: config.defaults.memoryLimit,
|
|
@@ -57763,6 +57771,7 @@ async function createServer(options) {
|
|
|
57763
57771
|
const stream = new ReadableStream({
|
|
57764
57772
|
async start(controller) {
|
|
57765
57773
|
try {
|
|
57774
|
+
logger.debug("[Server] Acquiring semaphore for /execute/stream");
|
|
57766
57775
|
await globalSemaphore.acquire();
|
|
57767
57776
|
try {
|
|
57768
57777
|
for await (const event of engine.executeStream(body.request)) {
|
|
@@ -57771,16 +57780,19 @@ async function createServer(options) {
|
|
|
57771
57780
|
`;
|
|
57772
57781
|
controller.enqueue(encoder.encode(line));
|
|
57773
57782
|
}
|
|
57783
|
+
logger.debug("[Server] Stream completed");
|
|
57774
57784
|
} finally {
|
|
57775
57785
|
globalSemaphore.release();
|
|
57776
57786
|
}
|
|
57777
57787
|
} catch (err) {
|
|
57778
57788
|
const message = err instanceof Error ? err.message : String(err);
|
|
57789
|
+
logger.debug(`[Server] Stream error: ${message}`);
|
|
57779
57790
|
const errorEvent = `data: ${JSON.stringify({ type: "error", data: message })}
|
|
57780
57791
|
|
|
57781
57792
|
`;
|
|
57782
57793
|
controller.enqueue(encoder.encode(errorEvent));
|
|
57783
57794
|
} finally {
|
|
57795
|
+
logger.debug("[Server] Cleaning up stream engine");
|
|
57784
57796
|
await engine.stop();
|
|
57785
57797
|
controller.close();
|
|
57786
57798
|
}
|
|
@@ -57796,35 +57808,45 @@ async function createServer(options) {
|
|
|
57796
57808
|
});
|
|
57797
57809
|
app.post("/file", async (c) => {
|
|
57798
57810
|
const body = await c.req.json();
|
|
57811
|
+
logger.debug(`[Server] POST /file sessionId=${body.sessionId} path=${body.path}`);
|
|
57799
57812
|
const session = sessions.get(body.sessionId);
|
|
57800
57813
|
if (!session) {
|
|
57814
|
+
logger.debug(`[Server] Session not found: ${body.sessionId}`);
|
|
57801
57815
|
return c.json({ error: "Session not found" }, 404);
|
|
57802
57816
|
}
|
|
57803
57817
|
session.lastAccessedAt = Date.now();
|
|
57804
57818
|
const content = Buffer.from(body.content, "base64");
|
|
57805
57819
|
await session.engine.putFile(body.path, content);
|
|
57820
|
+
logger.debug(`[Server] File uploaded: ${body.path} (${content.length} bytes)`);
|
|
57806
57821
|
return c.json({ ok: true });
|
|
57807
57822
|
});
|
|
57808
57823
|
app.get("/file", async (c) => {
|
|
57809
57824
|
const sessionId = c.req.query("sessionId");
|
|
57810
57825
|
const path = c.req.query("path");
|
|
57826
|
+
logger.debug(`[Server] GET /file sessionId=${sessionId} path=${path}`);
|
|
57811
57827
|
if (!(sessionId && path)) {
|
|
57812
57828
|
return c.json({ error: "Missing sessionId or path" }, 400);
|
|
57813
57829
|
}
|
|
57814
57830
|
const session = sessions.get(sessionId);
|
|
57815
57831
|
if (!session) {
|
|
57832
|
+
logger.debug(`[Server] Session not found: ${sessionId}`);
|
|
57816
57833
|
return c.json({ error: "Session not found" }, 404);
|
|
57817
57834
|
}
|
|
57818
57835
|
session.lastAccessedAt = Date.now();
|
|
57819
57836
|
const content = await session.engine.getFile(path);
|
|
57837
|
+
logger.debug(`[Server] File downloaded: ${path} (${content.length} bytes)`);
|
|
57820
57838
|
return c.json({ content: content.toString("base64") });
|
|
57821
57839
|
});
|
|
57822
57840
|
app.delete("/session/:id", async (c) => {
|
|
57823
57841
|
const id = c.req.param("id");
|
|
57842
|
+
logger.debug(`[Server] DELETE /session/${id}`);
|
|
57824
57843
|
const session = sessions.get(id);
|
|
57825
57844
|
if (session) {
|
|
57826
57845
|
await session.engine.stop();
|
|
57827
57846
|
sessions.delete(id);
|
|
57847
|
+
logger.debug(`[Server] Session destroyed: ${id}`);
|
|
57848
|
+
} else {
|
|
57849
|
+
logger.debug(`[Server] Session not found (already cleaned up): ${id}`);
|
|
57828
57850
|
}
|
|
57829
57851
|
return c.json({ ok: true });
|
|
57830
57852
|
});
|
|
@@ -57834,6 +57856,7 @@ async function createServer(options) {
|
|
|
57834
57856
|
const now = Date.now();
|
|
57835
57857
|
for (const [id, session] of sessions) {
|
|
57836
57858
|
if (now - session.lastAccessedAt > maxAge) {
|
|
57859
|
+
logger.debug(`[Server] Auto-pruning stale session: ${id}`);
|
|
57837
57860
|
await session.engine.stop();
|
|
57838
57861
|
sessions.delete(id);
|
|
57839
57862
|
}
|
|
@@ -57850,6 +57873,7 @@ var sessions;
|
|
|
57850
57873
|
var init_server = __esm(() => {
|
|
57851
57874
|
init_dist();
|
|
57852
57875
|
init_config();
|
|
57876
|
+
init_logger();
|
|
57853
57877
|
init_version();
|
|
57854
57878
|
sessions = new Map;
|
|
57855
57879
|
});
|
|
@@ -58462,7 +58486,7 @@ onetime.callCount = (function_) => {
|
|
|
58462
58486
|
};
|
|
58463
58487
|
var onetime_default = onetime;
|
|
58464
58488
|
|
|
58465
|
-
// node_modules/
|
|
58489
|
+
// node_modules/signal-exit/dist/mjs/signals.js
|
|
58466
58490
|
var signals = [];
|
|
58467
58491
|
signals.push("SIGHUP", "SIGINT", "SIGTERM");
|
|
58468
58492
|
if (process.platform !== "win32") {
|
|
@@ -58472,7 +58496,7 @@ if (process.platform === "linux") {
|
|
|
58472
58496
|
signals.push("SIGIO", "SIGPOLL", "SIGPWR", "SIGSTKFLT");
|
|
58473
58497
|
}
|
|
58474
58498
|
|
|
58475
|
-
// node_modules/
|
|
58499
|
+
// node_modules/signal-exit/dist/mjs/index.js
|
|
58476
58500
|
var processOk = (process3) => !!process3 && typeof process3 === "object" && typeof process3.removeListener === "function" && typeof process3.emit === "function" && typeof process3.reallyExit === "function" && typeof process3.listeners === "function" && typeof process3.kill === "function" && typeof process3.pid === "number" && typeof process3.on === "function";
|
|
58477
58501
|
var kExitEmitter = Symbol.for("signal-exit emitter");
|
|
58478
58502
|
var global2 = globalThis;
|
|
@@ -61362,15 +61386,26 @@ ${installCmd}
|
|
|
61362
61386
|
|
|
61363
61387
|
// src/cli.ts
|
|
61364
61388
|
init_runtime();
|
|
61389
|
+
init_logger();
|
|
61365
61390
|
init_version();
|
|
61366
61391
|
var program2 = new Command;
|
|
61367
|
-
program2.name("isol8").description("Secure code execution engine").version(VERSION)
|
|
61392
|
+
program2.name("isol8").description("Secure code execution engine").version(VERSION).option("--debug", "Enable debug logging").hook("preAction", (thisCommand) => {
|
|
61393
|
+
const opts = thisCommand.optsWithGlobals();
|
|
61394
|
+
if (opts.debug) {
|
|
61395
|
+
logger.setDebug(true);
|
|
61396
|
+
}
|
|
61397
|
+
logger.debug(`[CLI] Command: ${thisCommand.args?.[0] ?? thisCommand.name()}`);
|
|
61398
|
+
logger.debug(`[CLI] Version: ${VERSION}`);
|
|
61399
|
+
logger.debug(`[CLI] Platform: ${platform()} ${arch()}`);
|
|
61400
|
+
});
|
|
61368
61401
|
program2.command("setup").description("Check Docker and build isol8 images").option("--python <packages>", "Additional Python packages (comma-separated)").option("--node <packages>", "Additional Node.js packages (comma-separated)").option("--bun <packages>", "Additional Bun packages (comma-separated)").option("--deno <packages>", "Additional Deno packages (comma-separated)").option("--bash <packages>", "Additional Bash packages (comma-separated)").action(async (opts) => {
|
|
61369
61402
|
const docker = new import_dockerode2.default;
|
|
61403
|
+
logger.debug("[Setup] Connecting to Docker daemon");
|
|
61370
61404
|
const spinner = ora("Checking Docker...").start();
|
|
61371
61405
|
try {
|
|
61372
61406
|
await docker.ping();
|
|
61373
61407
|
spinner.stopAndPersist({ symbol: "[OK]", text: "Docker is running" });
|
|
61408
|
+
logger.debug("[Setup] Docker ping successful");
|
|
61374
61409
|
} catch {
|
|
61375
61410
|
spinner.stopAndPersist({ symbol: "[ERR]", text: "Docker is not running or not installed." });
|
|
61376
61411
|
console.error(" Install Docker: https://docs.docker.com/get-docker/");
|
|
@@ -61378,11 +61413,14 @@ program2.command("setup").description("Check Docker and build isol8 images").opt
|
|
|
61378
61413
|
process.exit(1);
|
|
61379
61414
|
}
|
|
61380
61415
|
spinner.start("Building isol8 images...");
|
|
61416
|
+
logger.debug("[Setup] Building base images");
|
|
61381
61417
|
await buildBaseImages(docker, (progress) => {
|
|
61382
61418
|
const status = progress.status === "error" ? "[ERR]" : progress.status === "done" ? "[OK]" : "[..]";
|
|
61383
61419
|
if (progress.status === "building") {
|
|
61384
61420
|
spinner.text = `Building ${progress.runtime}...`;
|
|
61421
|
+
logger.debug(`[Setup] Building base image for ${progress.runtime}`);
|
|
61385
61422
|
} else if (progress.status === "done" || progress.status === "error") {
|
|
61423
|
+
logger.debug(`[Setup] Base image ${progress.runtime}: ${progress.status}${progress.message ? ` (${progress.message})` : ""}`);
|
|
61386
61424
|
spinner.stopAndPersist({
|
|
61387
61425
|
symbol: status,
|
|
61388
61426
|
text: `${progress.runtime}${progress.message ? `: ${progress.message}` : ""}`
|
|
@@ -61396,32 +61434,41 @@ program2.command("setup").description("Check Docker and build isol8 images").opt
|
|
|
61396
61434
|
spinner.stop();
|
|
61397
61435
|
}
|
|
61398
61436
|
const config = loadConfig();
|
|
61437
|
+
logger.debug("[Setup] Config loaded");
|
|
61399
61438
|
if (opts.python) {
|
|
61439
|
+
logger.debug(`[Setup] Adding Python packages from CLI: ${opts.python}`);
|
|
61400
61440
|
config.dependencies.python = [
|
|
61401
61441
|
...config.dependencies.python ?? [],
|
|
61402
61442
|
...opts.python.split(",")
|
|
61403
61443
|
];
|
|
61404
61444
|
}
|
|
61405
61445
|
if (opts.node) {
|
|
61446
|
+
logger.debug(`[Setup] Adding Node.js packages from CLI: ${opts.node}`);
|
|
61406
61447
|
config.dependencies.node = [...config.dependencies.node ?? [], ...opts.node.split(",")];
|
|
61407
61448
|
}
|
|
61408
61449
|
if (opts.bun) {
|
|
61450
|
+
logger.debug(`[Setup] Adding Bun packages from CLI: ${opts.bun}`);
|
|
61409
61451
|
config.dependencies.bun = [...config.dependencies.bun ?? [], ...opts.bun.split(",")];
|
|
61410
61452
|
}
|
|
61411
61453
|
if (opts.deno) {
|
|
61454
|
+
logger.debug(`[Setup] Adding Deno packages from CLI: ${opts.deno}`);
|
|
61412
61455
|
config.dependencies.deno = [...config.dependencies.deno ?? [], ...opts.deno.split(",")];
|
|
61413
61456
|
}
|
|
61414
61457
|
if (opts.bash) {
|
|
61458
|
+
logger.debug(`[Setup] Adding Bash packages from CLI: ${opts.bash}`);
|
|
61415
61459
|
config.dependencies.bash = [...config.dependencies.bash ?? [], ...opts.bash.split(",")];
|
|
61416
61460
|
}
|
|
61417
61461
|
const hasDeps = Object.values(config.dependencies).some((pkgs) => pkgs && pkgs.length > 0);
|
|
61418
61462
|
if (hasDeps) {
|
|
61463
|
+
logger.debug("[Setup] Building custom images with dependencies:", JSON.stringify(config.dependencies));
|
|
61419
61464
|
spinner.start("Building custom images with dependencies...");
|
|
61420
61465
|
await buildCustomImages(docker, config, (progress) => {
|
|
61421
61466
|
const status = progress.status === "error" ? "[ERR]" : progress.status === "done" ? "[OK]" : "[..]";
|
|
61422
61467
|
if (progress.status === "building") {
|
|
61423
61468
|
spinner.text = `Building custom ${progress.runtime}...`;
|
|
61469
|
+
logger.debug(`[Setup] Building custom image for ${progress.runtime}`);
|
|
61424
61470
|
} else if (progress.status === "done" || progress.status === "error") {
|
|
61471
|
+
logger.debug(`[Setup] Custom image ${progress.runtime}: ${progress.status}${progress.message ? ` (${progress.message})` : ""}`);
|
|
61425
61472
|
spinner.stopAndPersist({
|
|
61426
61473
|
symbol: status,
|
|
61427
61474
|
text: `${progress.runtime}${progress.message ? ` (${progress.message})` : ""}`
|
|
@@ -61440,6 +61487,22 @@ program2.command("setup").description("Check Docker and build isol8 images").opt
|
|
|
61440
61487
|
});
|
|
61441
61488
|
program2.command("run").description("Execute code in isol8").argument("[file]", "Script file to execute").option("-e, --eval <code>", "Execute inline code string").option("-r, --runtime <name>", "Force runtime (python, node, bun, deno, bash)").option("--net <mode>", "Network mode: none, host, filtered", "none").option("--allow <regex>", "Whitelist regex for filtered mode (repeatable)", collect, []).option("--deny <regex>", "Blacklist regex for filtered mode (repeatable)", collect, []).option("--out <file>", "Write output to file").option("--persistent", "Use persistent container").option("--timeout <ms>", "Execution timeout in milliseconds").option("--memory <limit>", "Memory limit (e.g. 512m, 1g)").option("--cpu <limit>", "CPU limit as fraction (e.g. 0.5, 2.0)").option("--image <name>", "Override Docker image").option("--pids-limit <n>", "Maximum number of processes").option("--writable", "Disable read-only root filesystem").option("--max-output <bytes>", "Maximum output size in bytes").option("--secret <KEY=VALUE>", "Secret env var (repeatable, values masked)", collect, []).option("--sandbox-size <size>", "Sandbox tmpfs size (e.g. 128m)").option("--tmp-size <size>", "Tmp tmpfs size (e.g. 256m, 512m)").option("--stdin <data>", "Data to pipe to stdin").option("--install <package>", "Install package for runtime (repeatable)", collect, []).option("--host <url>", "Execute on remote server").option("--key <key>", "API key for remote server").option("--no-stream", "Disable real-time output streaming").option("--debug", "Enable debug logging").option("--persist", "Keep container running after execution for inspection").action(async (file, opts) => {
|
|
61442
61489
|
const { code, runtime, engineOptions, engine, stdinData, fileExtension } = await resolveRunInput(file, opts);
|
|
61490
|
+
logger.debug(`[Run] Runtime: ${runtime}, mode: ${engineOptions.mode}`);
|
|
61491
|
+
logger.debug(`[Run] Network: ${engineOptions.network}, timeout: ${engineOptions.timeoutMs}ms`);
|
|
61492
|
+
logger.debug(`[Run] Memory: ${engineOptions.memoryLimit}, CPU: ${engineOptions.cpuLimit}`);
|
|
61493
|
+
logger.debug(`[Run] Code length: ${code.length} chars`);
|
|
61494
|
+
if (stdinData) {
|
|
61495
|
+
logger.debug(`[Run] Stdin data provided (${stdinData.length} chars)`);
|
|
61496
|
+
}
|
|
61497
|
+
if (opts.install?.length > 0) {
|
|
61498
|
+
logger.debug(`[Run] Packages to install: ${opts.install.join(", ")}`);
|
|
61499
|
+
}
|
|
61500
|
+
if (opts.host) {
|
|
61501
|
+
logger.debug(`[Run] Remote execution on ${opts.host}`);
|
|
61502
|
+
}
|
|
61503
|
+
if (engineOptions.persist) {
|
|
61504
|
+
logger.debug("[Run] Persist mode enabled");
|
|
61505
|
+
}
|
|
61443
61506
|
const cleanup = async () => {
|
|
61444
61507
|
await engine.stop();
|
|
61445
61508
|
process.exit(0);
|
|
@@ -61450,6 +61513,7 @@ program2.command("run").description("Execute code in isol8").argument("[file]",
|
|
|
61450
61513
|
let exitCode = 0;
|
|
61451
61514
|
try {
|
|
61452
61515
|
await engine.start();
|
|
61516
|
+
logger.debug("[Run] Engine started");
|
|
61453
61517
|
spinner.text = "Running code...";
|
|
61454
61518
|
const req = {
|
|
61455
61519
|
code,
|
|
@@ -61460,6 +61524,7 @@ program2.command("run").description("Execute code in isol8").argument("[file]",
|
|
|
61460
61524
|
fileExtension
|
|
61461
61525
|
};
|
|
61462
61526
|
if (opts.stream !== false) {
|
|
61527
|
+
logger.debug("[Run] Using streaming mode");
|
|
61463
61528
|
spinner.stop();
|
|
61464
61529
|
const stream = engine.executeStream(req);
|
|
61465
61530
|
for await (const event of stream) {
|
|
@@ -61477,7 +61542,9 @@ program2.command("run").description("Execute code in isol8").argument("[file]",
|
|
|
61477
61542
|
}
|
|
61478
61543
|
}
|
|
61479
61544
|
} else {
|
|
61545
|
+
logger.debug("[Run] Using non-streaming mode");
|
|
61480
61546
|
const result = await engine.execute(req);
|
|
61547
|
+
logger.debug(`[Run] Execution completed: exitCode=${result.exitCode}, duration=${result.durationMs}ms, truncated=${result.truncated}`);
|
|
61481
61548
|
spinner.stop();
|
|
61482
61549
|
if (result.stdout) {
|
|
61483
61550
|
console.log(result.stdout);
|
|
@@ -61500,6 +61567,7 @@ program2.command("run").description("Execute code in isol8").argument("[file]",
|
|
|
61500
61567
|
spinner.stop();
|
|
61501
61568
|
throw err;
|
|
61502
61569
|
} finally {
|
|
61570
|
+
logger.debug("[Run] Stopping engine");
|
|
61503
61571
|
const cleanupPromise = engine.stop();
|
|
61504
61572
|
const timeoutPromise = new Promise((resolve3) => setTimeout(resolve3, 5000));
|
|
61505
61573
|
await Promise.race([cleanupPromise, timeoutPromise]);
|
|
@@ -61508,24 +61576,33 @@ program2.command("run").description("Execute code in isol8").argument("[file]",
|
|
|
61508
61576
|
process.exit(exitCode);
|
|
61509
61577
|
}
|
|
61510
61578
|
});
|
|
61511
|
-
program2.command("serve").description("Start the isol8 remote server").option("-p, --port <port>", "Port to listen on", "3000").option("-k, --key <key>", "API key for authentication").option("--update", "Force re-download the server binary").action(async (opts) => {
|
|
61579
|
+
program2.command("serve").description("Start the isol8 remote server").option("-p, --port <port>", "Port to listen on", "3000").option("-k, --key <key>", "API key for authentication").option("--update", "Force re-download the server binary").option("--debug", "Enable debug logging").action(async (opts) => {
|
|
61512
61580
|
const apiKey = opts.key ?? process.env.ISOL8_API_KEY;
|
|
61513
61581
|
if (!apiKey) {
|
|
61514
61582
|
console.error("[ERR] API key required. Use --key or ISOL8_API_KEY env var.");
|
|
61515
61583
|
process.exit(1);
|
|
61516
61584
|
}
|
|
61517
61585
|
const port = Number.parseInt(opts.port, 10);
|
|
61586
|
+
logger.debug(`[Serve] Port: ${port}`);
|
|
61587
|
+
logger.debug(`[Serve] API key: ${"*".repeat(apiKey.length)}`);
|
|
61518
61588
|
if (typeof globalThis.Bun !== "undefined") {
|
|
61589
|
+
logger.debug("[Serve] Running under Bun, starting server in-process");
|
|
61519
61590
|
const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), exports_server));
|
|
61520
|
-
const server = await createServer2({ port, apiKey });
|
|
61591
|
+
const server = await createServer2({ port, apiKey, debug: opts.debug ?? false });
|
|
61521
61592
|
console.log(`[INFO] isol8 server v${VERSION} listening on http://localhost:${port}`);
|
|
61522
61593
|
console.log(" Auth: Bearer token required");
|
|
61523
61594
|
Bun.serve({ fetch: server.app.fetch, port });
|
|
61524
61595
|
return;
|
|
61525
61596
|
}
|
|
61597
|
+
logger.debug("[Serve] Running under Node.js, launching standalone binary");
|
|
61526
61598
|
const binaryPath = await ensureServerBinary(opts.update ?? false);
|
|
61599
|
+
logger.debug(`[Serve] Binary path: ${binaryPath}`);
|
|
61527
61600
|
const { spawn: spawnChild } = await import("node:child_process");
|
|
61528
|
-
const
|
|
61601
|
+
const binaryArgs = ["--port", String(port), "--key", apiKey];
|
|
61602
|
+
if (opts.debug) {
|
|
61603
|
+
binaryArgs.push("--debug");
|
|
61604
|
+
}
|
|
61605
|
+
const child = spawnChild(binaryPath, binaryArgs, {
|
|
61529
61606
|
stdio: "inherit"
|
|
61530
61607
|
});
|
|
61531
61608
|
const forwardSignal = (signal) => {
|
|
@@ -61540,6 +61617,7 @@ program2.command("serve").description("Start the isol8 remote server").option("-
|
|
|
61540
61617
|
function getServerBinaryName() {
|
|
61541
61618
|
const os2 = platform();
|
|
61542
61619
|
const cpu = arch();
|
|
61620
|
+
logger.debug(`[Serve] Resolving binary name for ${os2}-${cpu}`);
|
|
61543
61621
|
const osMap = {
|
|
61544
61622
|
darwin: "darwin",
|
|
61545
61623
|
linux: "linux",
|
|
@@ -61561,6 +61639,7 @@ function getServerBinaryName() {
|
|
|
61561
61639
|
}
|
|
61562
61640
|
async function getServerBinaryVersion(binaryPath) {
|
|
61563
61641
|
if (!existsSync3(binaryPath)) {
|
|
61642
|
+
logger.debug(`[Serve] No binary found at ${binaryPath}`);
|
|
61564
61643
|
return null;
|
|
61565
61644
|
}
|
|
61566
61645
|
try {
|
|
@@ -61569,14 +61648,17 @@ async function getServerBinaryVersion(binaryPath) {
|
|
|
61569
61648
|
encoding: "utf-8",
|
|
61570
61649
|
timeout: 5000
|
|
61571
61650
|
});
|
|
61651
|
+
logger.debug(`[Serve] Existing binary version: ${output.trim()}`);
|
|
61572
61652
|
return output.trim();
|
|
61573
61653
|
} catch {
|
|
61654
|
+
logger.debug("[Serve] Failed to get binary version");
|
|
61574
61655
|
return null;
|
|
61575
61656
|
}
|
|
61576
61657
|
}
|
|
61577
61658
|
async function downloadServerBinary(binaryPath) {
|
|
61578
61659
|
const binaryName = getServerBinaryName();
|
|
61579
61660
|
const url = `https://github.com/Illusion47586/isol8/releases/download/v${VERSION}/${binaryName}`;
|
|
61661
|
+
logger.debug(`[Serve] Download URL: ${url}`);
|
|
61580
61662
|
const spinner = ora(`Downloading isol8 server v${VERSION}...`).start();
|
|
61581
61663
|
try {
|
|
61582
61664
|
const response = await fetch(url, { redirect: "follow" });
|
|
@@ -61596,6 +61678,7 @@ async function downloadServerBinary(binaryPath) {
|
|
|
61596
61678
|
writeFileSync(tmpPath, buffer);
|
|
61597
61679
|
chmodSync(tmpPath, 493);
|
|
61598
61680
|
renameSync(tmpPath, binaryPath);
|
|
61681
|
+
logger.debug(`[Serve] Binary saved to ${binaryPath} (${buffer.length} bytes)`);
|
|
61599
61682
|
spinner.succeed(`Downloaded isol8 server v${VERSION}`);
|
|
61600
61683
|
} catch (err) {
|
|
61601
61684
|
spinner.fail("Failed to download server binary");
|
|
@@ -61622,18 +61705,23 @@ async function promptYesNo(question) {
|
|
|
61622
61705
|
async function ensureServerBinary(forceUpdate) {
|
|
61623
61706
|
const binDir = join2(homedir2(), ".isol8", "bin");
|
|
61624
61707
|
const binaryPath = join2(binDir, "isol8-server");
|
|
61708
|
+
logger.debug(`[Serve] Binary path: ${binaryPath}, forceUpdate: ${forceUpdate}`);
|
|
61625
61709
|
if (forceUpdate) {
|
|
61710
|
+
logger.debug("[Serve] Force update requested");
|
|
61626
61711
|
await downloadServerBinary(binaryPath);
|
|
61627
61712
|
return binaryPath;
|
|
61628
61713
|
}
|
|
61629
61714
|
const existingVersion = await getServerBinaryVersion(binaryPath);
|
|
61630
61715
|
if (existingVersion === null) {
|
|
61716
|
+
logger.debug("[Serve] No existing binary, downloading");
|
|
61631
61717
|
await downloadServerBinary(binaryPath);
|
|
61632
61718
|
return binaryPath;
|
|
61633
61719
|
}
|
|
61634
61720
|
if (existingVersion === VERSION) {
|
|
61721
|
+
logger.debug(`[Serve] Binary version ${existingVersion} matches CLI`);
|
|
61635
61722
|
return binaryPath;
|
|
61636
61723
|
}
|
|
61724
|
+
logger.debug(`[Serve] Version mismatch: binary=${existingVersion}, CLI=${VERSION}`);
|
|
61637
61725
|
console.log(`Server binary v${existingVersion} found, but CLI is v${VERSION}.`);
|
|
61638
61726
|
const shouldUpdate = await promptYesNo("Download updated binary? [Y/n] ");
|
|
61639
61727
|
if (shouldUpdate) {
|
|
@@ -61650,6 +61738,8 @@ program2.command("config").description("Show the resolved isol8 configuration").
|
|
|
61650
61738
|
join2(homedir2(), ".isol8", "config.json")
|
|
61651
61739
|
];
|
|
61652
61740
|
const loadedFrom = searchPaths.find((p) => existsSync3(p));
|
|
61741
|
+
logger.debug(`[Config] Config source: ${loadedFrom ?? "defaults"}`);
|
|
61742
|
+
logger.debug(`[Config] Resolved config: ${JSON.stringify(config)}`);
|
|
61653
61743
|
if (opts.json) {
|
|
61654
61744
|
console.log(JSON.stringify(config, null, 2));
|
|
61655
61745
|
return;
|
|
@@ -61713,10 +61803,12 @@ Isol8 Configuration
|
|
|
61713
61803
|
});
|
|
61714
61804
|
program2.command("cleanup").description("Remove orphaned isol8 containers").option("--force", "Skip confirmation prompt").action(async (opts) => {
|
|
61715
61805
|
const docker = new import_dockerode2.default;
|
|
61806
|
+
logger.debug("[Cleanup] Connecting to Docker daemon");
|
|
61716
61807
|
const spinner = ora("Checking Docker...").start();
|
|
61717
61808
|
try {
|
|
61718
61809
|
await docker.ping();
|
|
61719
61810
|
spinner.succeed("Docker is running");
|
|
61811
|
+
logger.debug("[Cleanup] Docker ping successful");
|
|
61720
61812
|
} catch {
|
|
61721
61813
|
spinner.fail("Docker is not running or not installed.");
|
|
61722
61814
|
process.exit(1);
|
|
@@ -61724,6 +61816,7 @@ program2.command("cleanup").description("Remove orphaned isol8 containers").opti
|
|
|
61724
61816
|
spinner.start("Finding isol8 containers...");
|
|
61725
61817
|
const containers = await docker.listContainers({ all: true });
|
|
61726
61818
|
const isol8Containers = containers.filter((c) => c.Image.startsWith("isol8:") || c.Image.startsWith("isol8-custom:"));
|
|
61819
|
+
logger.debug(`[Cleanup] Found ${containers.length} total containers, ${isol8Containers.length} isol8 containers`);
|
|
61727
61820
|
if (isol8Containers.length === 0) {
|
|
61728
61821
|
spinner.info("No isol8 containers found");
|
|
61729
61822
|
return;
|
|
@@ -61752,7 +61845,9 @@ program2.command("cleanup").description("Remove orphaned isol8 containers").opti
|
|
|
61752
61845
|
}
|
|
61753
61846
|
}
|
|
61754
61847
|
spinner.start("Removing containers...");
|
|
61848
|
+
logger.debug("[Cleanup] Removing containers");
|
|
61755
61849
|
const result = await DockerIsol8.cleanup(docker);
|
|
61850
|
+
logger.debug(`[Cleanup] Removed: ${result.removed}, failed: ${result.failed}`);
|
|
61756
61851
|
if (result.errors.length > 0) {
|
|
61757
61852
|
console.log("");
|
|
61758
61853
|
for (const err of result.errors) {
|
|
@@ -61767,13 +61862,16 @@ program2.command("cleanup").description("Remove orphaned isol8 containers").opti
|
|
|
61767
61862
|
});
|
|
61768
61863
|
async function resolveRunInput(file, opts) {
|
|
61769
61864
|
const config = loadConfig();
|
|
61865
|
+
logger.debug("[Run] Config loaded");
|
|
61770
61866
|
let code;
|
|
61771
61867
|
let runtime;
|
|
61772
61868
|
if (opts.eval) {
|
|
61773
61869
|
code = opts.eval;
|
|
61774
61870
|
runtime = opts.runtime ?? "python";
|
|
61871
|
+
logger.debug(`[Run] Inline eval, runtime: ${runtime}`);
|
|
61775
61872
|
} else if (file) {
|
|
61776
61873
|
const filePath = resolve2(file);
|
|
61874
|
+
logger.debug(`[Run] Reading file: ${filePath}`);
|
|
61777
61875
|
if (!existsSync3(filePath)) {
|
|
61778
61876
|
console.error(`[ERR] File not found: ${file}`);
|
|
61779
61877
|
process.exit(1);
|
|
@@ -61781,15 +61879,18 @@ async function resolveRunInput(file, opts) {
|
|
|
61781
61879
|
code = readFileSync2(filePath, "utf-8");
|
|
61782
61880
|
if (opts.runtime) {
|
|
61783
61881
|
runtime = opts.runtime;
|
|
61882
|
+
logger.debug(`[Run] Runtime specified: ${runtime}`);
|
|
61784
61883
|
} else {
|
|
61785
61884
|
try {
|
|
61786
61885
|
runtime = RuntimeRegistry.detect(file).name;
|
|
61886
|
+
logger.debug(`[Run] Auto-detected runtime: ${runtime}`);
|
|
61787
61887
|
} catch {
|
|
61788
61888
|
console.error(`[ERR] Cannot detect runtime for ${file}. Use --runtime to specify.`);
|
|
61789
61889
|
process.exit(1);
|
|
61790
61890
|
}
|
|
61791
61891
|
}
|
|
61792
61892
|
} else {
|
|
61893
|
+
logger.debug("[Run] Reading code from stdin");
|
|
61793
61894
|
const chunks = [];
|
|
61794
61895
|
for await (const chunk of process.stdin) {
|
|
61795
61896
|
chunks.push(chunk);
|
|
@@ -61815,10 +61916,7 @@ async function resolveRunInput(file, opts) {
|
|
|
61815
61916
|
debug: opts.debug ?? config.debug,
|
|
61816
61917
|
persist: opts.persist ?? false
|
|
61817
61918
|
};
|
|
61818
|
-
|
|
61819
|
-
const { logger: logger2 } = await Promise.resolve().then(() => (init_logger(), exports_logger));
|
|
61820
|
-
logger2.setDebug(true);
|
|
61821
|
-
}
|
|
61919
|
+
logger.debug(`[Run] Engine options: mode=${engineOptions.mode}, network=${engineOptions.network}`);
|
|
61822
61920
|
let fileExtension;
|
|
61823
61921
|
if (file) {
|
|
61824
61922
|
const ext = file.substring(file.lastIndexOf("."));
|
|
@@ -61839,6 +61937,7 @@ async function resolveRunInput(file, opts) {
|
|
|
61839
61937
|
const stdinData = opts.stdin ?? undefined;
|
|
61840
61938
|
let engine;
|
|
61841
61939
|
if (opts.host) {
|
|
61940
|
+
logger.debug(`[Run] Using remote engine: ${opts.host}`);
|
|
61842
61941
|
const apiKey = opts.key ?? process.env.ISOL8_API_KEY;
|
|
61843
61942
|
if (!apiKey) {
|
|
61844
61943
|
console.error("[ERR] API key required. Use --key or ISOL8_API_KEY env var.");
|
|
@@ -61846,6 +61945,7 @@ async function resolveRunInput(file, opts) {
|
|
|
61846
61945
|
}
|
|
61847
61946
|
engine = new RemoteIsol8({ host: opts.host, apiKey, sessionId: opts.persistent ? `cli-${Date.now()}` : undefined }, engineOptions);
|
|
61848
61947
|
} else {
|
|
61948
|
+
logger.debug("[Run] Using local Docker engine");
|
|
61849
61949
|
engine = new DockerIsol8(engineOptions, config.maxConcurrent);
|
|
61850
61950
|
}
|
|
61851
61951
|
return { code, runtime, engineOptions, engine, stdinData, fileExtension };
|
|
@@ -61859,4 +61959,4 @@ if (!process.argv.slice(2).length) {
|
|
|
61859
61959
|
}
|
|
61860
61960
|
program2.parse();
|
|
61861
61961
|
|
|
61862
|
-
//# debugId=
|
|
61962
|
+
//# debugId=21709D38E9A7C3B264756E2164756E21
|
package/dist/index.js
CHANGED
|
@@ -1327,10 +1327,11 @@ init_runtime();
|
|
|
1327
1327
|
|
|
1328
1328
|
// src/server/index.ts
|
|
1329
1329
|
import { Hono } from "hono";
|
|
1330
|
+
init_logger();
|
|
1330
1331
|
// package.json
|
|
1331
1332
|
var package_default = {
|
|
1332
1333
|
name: "isol8",
|
|
1333
|
-
version: "0.6.
|
|
1334
|
+
version: "0.6.1",
|
|
1334
1335
|
description: "Secure code execution engine for AI agents",
|
|
1335
1336
|
author: "Illusion47586",
|
|
1336
1337
|
license: "MIT",
|
|
@@ -1392,6 +1393,7 @@ var package_default = {
|
|
|
1392
1393
|
devDependencies: {
|
|
1393
1394
|
"@biomejs/biome": "^2.3.15",
|
|
1394
1395
|
"@semantic-release/changelog": "^6.0.3",
|
|
1396
|
+
"@semantic-release/exec": "^7.1.0",
|
|
1395
1397
|
"@semantic-release/git": "^10.0.1",
|
|
1396
1398
|
"@semantic-release/github": "^12.0.6",
|
|
1397
1399
|
"@semantic-release/npm": "^13.1.4",
|
|
@@ -1462,13 +1464,21 @@ var sessions = new Map;
|
|
|
1462
1464
|
async function createServer(options) {
|
|
1463
1465
|
const { DockerIsol8: DockerIsol82 } = await Promise.resolve().then(() => (init_docker(), exports_docker));
|
|
1464
1466
|
await Promise.resolve().then(() => (init_runtime(), exports_runtime));
|
|
1467
|
+
if (options.debug) {
|
|
1468
|
+
logger.setDebug(true);
|
|
1469
|
+
}
|
|
1465
1470
|
const config = loadConfig();
|
|
1471
|
+
logger.debug("[Server] Config loaded");
|
|
1472
|
+
logger.debug(`[Server] Max concurrent: ${config.maxConcurrent}`);
|
|
1473
|
+
logger.debug(`[Server] Auto-prune: ${config.cleanup.autoPrune}`);
|
|
1466
1474
|
const app = new Hono;
|
|
1467
1475
|
const globalSemaphore = new Semaphore(config.maxConcurrent);
|
|
1468
1476
|
app.use("*", authMiddleware(options.apiKey));
|
|
1469
1477
|
app.get("/health", (c) => c.json({ status: "ok", version: VERSION }));
|
|
1470
1478
|
app.post("/execute", async (c) => {
|
|
1471
1479
|
const body = await c.req.json();
|
|
1480
|
+
logger.debug(`[Server] POST /execute runtime=${body.request.runtime} sessionId=${body.sessionId ?? "ephemeral"}`);
|
|
1481
|
+
logger.debug(`[Server] Code length: ${body.request.code.length} chars`);
|
|
1472
1482
|
const engineOptions = {
|
|
1473
1483
|
network: config.defaults.network,
|
|
1474
1484
|
memoryLimit: config.defaults.memoryLimit,
|
|
@@ -1483,36 +1493,45 @@ async function createServer(options) {
|
|
|
1483
1493
|
if (body.sessionId) {
|
|
1484
1494
|
const session = sessions.get(body.sessionId);
|
|
1485
1495
|
if (session) {
|
|
1496
|
+
logger.debug(`[Server] Reusing existing session: ${body.sessionId}`);
|
|
1486
1497
|
engine = session.engine;
|
|
1487
1498
|
session.lastAccessedAt = Date.now();
|
|
1488
1499
|
} else {
|
|
1500
|
+
logger.debug(`[Server] Creating new session: ${body.sessionId}`);
|
|
1489
1501
|
engine = new DockerIsol82(engineOptions, config.maxConcurrent);
|
|
1490
1502
|
await engine.start();
|
|
1491
1503
|
sessions.set(body.sessionId, { engine, lastAccessedAt: Date.now() });
|
|
1492
1504
|
}
|
|
1493
1505
|
} else {
|
|
1506
|
+
logger.debug("[Server] Creating ephemeral engine");
|
|
1494
1507
|
engine = new DockerIsol82(engineOptions, config.maxConcurrent);
|
|
1495
1508
|
await engine.start();
|
|
1496
1509
|
}
|
|
1497
1510
|
try {
|
|
1511
|
+
logger.debug("[Server] Acquiring semaphore for /execute");
|
|
1498
1512
|
await globalSemaphore.acquire();
|
|
1499
1513
|
try {
|
|
1500
1514
|
const result = await engine.execute(body.request);
|
|
1515
|
+
logger.debug(`[Server] Execution completed: exitCode=${result.exitCode} duration=${result.durationMs}ms`);
|
|
1501
1516
|
return c.json(result);
|
|
1502
1517
|
} finally {
|
|
1503
1518
|
globalSemaphore.release();
|
|
1504
1519
|
}
|
|
1505
1520
|
} catch (err) {
|
|
1506
1521
|
const message = err instanceof Error ? err.message : String(err);
|
|
1522
|
+
logger.debug(`[Server] Execution error: ${message}`);
|
|
1507
1523
|
return c.json({ error: message }, 500);
|
|
1508
1524
|
} finally {
|
|
1509
1525
|
if (!body.sessionId) {
|
|
1526
|
+
logger.debug("[Server] Cleaning up ephemeral engine");
|
|
1510
1527
|
await engine.stop();
|
|
1511
1528
|
}
|
|
1512
1529
|
}
|
|
1513
1530
|
});
|
|
1514
1531
|
app.post("/execute/stream", async (c) => {
|
|
1515
1532
|
const body = await c.req.json();
|
|
1533
|
+
logger.debug(`[Server] POST /execute/stream runtime=${body.request.runtime}`);
|
|
1534
|
+
logger.debug(`[Server] Code length: ${body.request.code.length} chars`);
|
|
1516
1535
|
const engineOptions = {
|
|
1517
1536
|
network: config.defaults.network,
|
|
1518
1537
|
memoryLimit: config.defaults.memoryLimit,
|
|
@@ -1529,6 +1548,7 @@ async function createServer(options) {
|
|
|
1529
1548
|
const stream = new ReadableStream({
|
|
1530
1549
|
async start(controller) {
|
|
1531
1550
|
try {
|
|
1551
|
+
logger.debug("[Server] Acquiring semaphore for /execute/stream");
|
|
1532
1552
|
await globalSemaphore.acquire();
|
|
1533
1553
|
try {
|
|
1534
1554
|
for await (const event of engine.executeStream(body.request)) {
|
|
@@ -1537,16 +1557,19 @@ async function createServer(options) {
|
|
|
1537
1557
|
`;
|
|
1538
1558
|
controller.enqueue(encoder.encode(line));
|
|
1539
1559
|
}
|
|
1560
|
+
logger.debug("[Server] Stream completed");
|
|
1540
1561
|
} finally {
|
|
1541
1562
|
globalSemaphore.release();
|
|
1542
1563
|
}
|
|
1543
1564
|
} catch (err) {
|
|
1544
1565
|
const message = err instanceof Error ? err.message : String(err);
|
|
1566
|
+
logger.debug(`[Server] Stream error: ${message}`);
|
|
1545
1567
|
const errorEvent = `data: ${JSON.stringify({ type: "error", data: message })}
|
|
1546
1568
|
|
|
1547
1569
|
`;
|
|
1548
1570
|
controller.enqueue(encoder.encode(errorEvent));
|
|
1549
1571
|
} finally {
|
|
1572
|
+
logger.debug("[Server] Cleaning up stream engine");
|
|
1550
1573
|
await engine.stop();
|
|
1551
1574
|
controller.close();
|
|
1552
1575
|
}
|
|
@@ -1562,35 +1585,45 @@ async function createServer(options) {
|
|
|
1562
1585
|
});
|
|
1563
1586
|
app.post("/file", async (c) => {
|
|
1564
1587
|
const body = await c.req.json();
|
|
1588
|
+
logger.debug(`[Server] POST /file sessionId=${body.sessionId} path=${body.path}`);
|
|
1565
1589
|
const session = sessions.get(body.sessionId);
|
|
1566
1590
|
if (!session) {
|
|
1591
|
+
logger.debug(`[Server] Session not found: ${body.sessionId}`);
|
|
1567
1592
|
return c.json({ error: "Session not found" }, 404);
|
|
1568
1593
|
}
|
|
1569
1594
|
session.lastAccessedAt = Date.now();
|
|
1570
1595
|
const content = Buffer.from(body.content, "base64");
|
|
1571
1596
|
await session.engine.putFile(body.path, content);
|
|
1597
|
+
logger.debug(`[Server] File uploaded: ${body.path} (${content.length} bytes)`);
|
|
1572
1598
|
return c.json({ ok: true });
|
|
1573
1599
|
});
|
|
1574
1600
|
app.get("/file", async (c) => {
|
|
1575
1601
|
const sessionId = c.req.query("sessionId");
|
|
1576
1602
|
const path = c.req.query("path");
|
|
1603
|
+
logger.debug(`[Server] GET /file sessionId=${sessionId} path=${path}`);
|
|
1577
1604
|
if (!(sessionId && path)) {
|
|
1578
1605
|
return c.json({ error: "Missing sessionId or path" }, 400);
|
|
1579
1606
|
}
|
|
1580
1607
|
const session = sessions.get(sessionId);
|
|
1581
1608
|
if (!session) {
|
|
1609
|
+
logger.debug(`[Server] Session not found: ${sessionId}`);
|
|
1582
1610
|
return c.json({ error: "Session not found" }, 404);
|
|
1583
1611
|
}
|
|
1584
1612
|
session.lastAccessedAt = Date.now();
|
|
1585
1613
|
const content = await session.engine.getFile(path);
|
|
1614
|
+
logger.debug(`[Server] File downloaded: ${path} (${content.length} bytes)`);
|
|
1586
1615
|
return c.json({ content: content.toString("base64") });
|
|
1587
1616
|
});
|
|
1588
1617
|
app.delete("/session/:id", async (c) => {
|
|
1589
1618
|
const id = c.req.param("id");
|
|
1619
|
+
logger.debug(`[Server] DELETE /session/${id}`);
|
|
1590
1620
|
const session = sessions.get(id);
|
|
1591
1621
|
if (session) {
|
|
1592
1622
|
await session.engine.stop();
|
|
1593
1623
|
sessions.delete(id);
|
|
1624
|
+
logger.debug(`[Server] Session destroyed: ${id}`);
|
|
1625
|
+
} else {
|
|
1626
|
+
logger.debug(`[Server] Session not found (already cleaned up): ${id}`);
|
|
1594
1627
|
}
|
|
1595
1628
|
return c.json({ ok: true });
|
|
1596
1629
|
});
|
|
@@ -1600,6 +1633,7 @@ async function createServer(options) {
|
|
|
1600
1633
|
const now = Date.now();
|
|
1601
1634
|
for (const [id, session] of sessions) {
|
|
1602
1635
|
if (now - session.lastAccessedAt > maxAge) {
|
|
1636
|
+
logger.debug(`[Server] Auto-pruning stale session: ${id}`);
|
|
1603
1637
|
await session.engine.stop();
|
|
1604
1638
|
sessions.delete(id);
|
|
1605
1639
|
}
|
|
@@ -1626,4 +1660,4 @@ export {
|
|
|
1626
1660
|
BunAdapter
|
|
1627
1661
|
};
|
|
1628
1662
|
|
|
1629
|
-
//# debugId=
|
|
1663
|
+
//# debugId=042AB36FB522926664756E2164756E21
|
|
@@ -12,6 +12,8 @@ export interface ServerOptions {
|
|
|
12
12
|
port: number;
|
|
13
13
|
/** API key required for Bearer token authentication. */
|
|
14
14
|
apiKey: string;
|
|
15
|
+
/** Enable debug logging for internal server operations. */
|
|
16
|
+
debug?: boolean;
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
17
19
|
* Creates and configures the isol8 HTTP server.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAS5B,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAWD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa;;;;GA+OxD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "isol8",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Secure code execution engine for AI agents",
|
|
5
5
|
"author": "Illusion47586",
|
|
6
6
|
"license": "MIT",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@biomejs/biome": "^2.3.15",
|
|
64
64
|
"@semantic-release/changelog": "^6.0.3",
|
|
65
|
+
"@semantic-release/exec": "^7.1.0",
|
|
65
66
|
"@semantic-release/git": "^10.0.1",
|
|
66
67
|
"@semantic-release/github": "^12.0.6",
|
|
67
68
|
"@semantic-release/npm": "^13.1.4",
|