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 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 = require_sshcrypto();
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.0",
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/restore-cursor/node_modules/signal-exit/dist/mjs/signals.js
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/restore-cursor/node_modules/signal-exit/dist/mjs/index.js
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 child = spawnChild(binaryPath, ["--port", String(port), "--key", apiKey], {
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
- if (engineOptions.debug) {
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=05A7CFDAF922346964756E2164756E21
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.0",
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=BE9F771BA94691E364756E2164756E21
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;AAQ5B,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;CAChB;AAWD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa;;;;GAqMxD"}
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.1",
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",