isol8 0.11.1 → 0.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -105,7 +105,7 @@ isol8 run script.py --host http://server:3000 --key my-api-key
105
105
  |------|-------------|---------|
106
106
  | `-e, --eval <code>` | Execute inline code | — |
107
107
  | `-r, --runtime <rt>` | Force runtime: `python`, `node`, `bun`, `deno`, `bash` | auto-detect |
108
- | `--net <mode>` | Network mode: `none`, `host`, `filtered` | `none` |
108
+ | `--net <mode>` | Network mode: `none`, `host`, `filtered` | `none` (unless `--install` is used without explicit `--net`, then auto `filtered`) |
109
109
  | `--allow <regex>` | Whitelist regex (repeatable, for `filtered`) | — |
110
110
  | `--deny <regex>` | Blacklist regex (repeatable, for `filtered`) | — |
111
111
  | `--out <file>` | Write stdout to file | — |
@@ -113,7 +113,7 @@ isol8 run script.py --host http://server:3000 --key my-api-key
113
113
  | `--persistent` | Keep container alive between runs | `false` |
114
114
  | `--persist` | Keep container after execution for inspection/debugging | `false` |
115
115
  | `--debug` | Enable debug logging for internal engine operations | `false` |
116
- | `--timeout <ms>` | Execution timeout in milliseconds | `30000` |
116
+ | `--timeout <ms>` | Timeout in milliseconds for package install + execution phases | `30000` |
117
117
  | `--memory <limit>` | Memory limit (e.g. `512m`, `1g`) | `512m` |
118
118
  | `--cpu <limit>` | CPU limit as fraction (e.g. `0.5`, `2.0`) | `1.0` |
119
119
  | `--image <name>` | Override Docker image | — |
@@ -124,7 +124,7 @@ isol8 run script.py --host http://server:3000 --key my-api-key
124
124
  | `--sandbox-size <size>` | Sandbox tmpfs size (e.g. `512m`, `1g`) | `512m` |
125
125
  | `--tmp-size <size>` | Tmp tmpfs size (e.g. `256m`, `512m`) | `256m` |
126
126
  | `--stdin <data>` | Data to pipe to stdin | — |
127
- | `--install <pkg>` | Install package for runtime (repeatable) | — |
127
+ | `--install <pkg>` | Install package for runtime (repeatable) | — (auto-adds default runtime registry allowlist in `filtered` mode) |
128
128
  | `--url <url>` | Fetch code from URL (requires `remoteCode.enabled=true`) | — |
129
129
  | `--github <owner/repo/ref/path>` | GitHub shorthand for raw source | — |
130
130
  | `--gist <gistId/file.ext>` | Gist shorthand for raw source | — |
@@ -169,11 +169,15 @@ isol8 serve --update # Force re-download the server binary
169
169
 
170
170
  | Flag | Description | Default |
171
171
  |------|-------------|---------|
172
- | `-p, --port <port>` | Port to listen on | `3000` |
172
+ | `-p, --port <port>` | Port to listen on | `--port` > `$ISOL8_PORT` > `$PORT` > `3000` |
173
173
  | `-k, --key <key>` | API key for Bearer token auth | `$ISOL8_API_KEY` |
174
174
  | `--update` | Force re-download the server binary | `false` |
175
175
  | `--debug` | Enable debug logging for server operations | `false` |
176
176
 
177
+ If the selected port is already in use, `isol8 serve` now prompts to enter another port or auto-select an available one. In non-interactive environments, it auto-falls back to a free port.
178
+
179
+ On graceful shutdown (`SIGINT`/`SIGTERM`), the server now cleans up tracked sessions, isol8 containers, and isol8 images before exiting.
180
+
177
181
  ### `isol8 config`
178
182
 
179
183
  Display the resolved configuration (merged defaults + config file). Shows the source file, defaults, network rules, cleanup policy, and dependencies.
@@ -375,7 +379,7 @@ Add the `$schema` property to get autocompletion, validation, and inline documen
375
379
  "node": ["lodash"]
376
380
  },
377
381
  "security": {
378
- "seccomp": "safety"
382
+ "seccomp": "strict"
379
383
  }
380
384
  }
381
385
  ```
@@ -439,7 +443,7 @@ bun run bench:detailed # Phase breakdown
439
443
  | **Network** | Disabled by default; optional proxy-based filtering |
440
444
  | **Output** | Truncated at 1MB; secrets masked from stdout/stderr |
441
445
  | **Isolation** | Each execution in its own container (ephemeral) or exec (persistent) |
442
- | **Seccomp** | Default "safety" profile blocks dangerous syscalls (mount, swap, ptrace) but allows others for compatibility; configurable via `security.seccomp` |
446
+ | **Seccomp** | Default `strict` mode applies the built-in profile that blocks dangerous syscalls (mount, swap, ptrace). In standalone server binaries, an embedded copy is used when profile files are not present. If strict/custom profile loading fails, execution fails. |
443
447
 
444
448
  ### Container Filesystem
445
449
 
@@ -468,6 +472,7 @@ When running `isol8 serve`, these endpoints are available:
468
472
  | `POST` | `/file` | Upload file (base64) |
469
473
  | `GET` | `/file?sessionId=&path=` | Download file (base64) |
470
474
  | `DELETE` | `/session/:id` | Destroy persistent session |
475
+ | `POST` | `/cleanup` | Run server-side cleanup for sessions/containers (and images by default) |
471
476
 
472
477
  All endpoints (except `/health`) require `Authorization: Bearer <key>`.
473
478
 
package/dist/cli.js CHANGED
@@ -6318,7 +6318,7 @@ var require_bcrypt_pbkdf = __commonJS((exports, module) => {
6318
6318
 
6319
6319
  // node_modules/cpu-features/build/Release/cpufeatures.node
6320
6320
  var require_cpufeatures = __commonJS((exports, module) => {
6321
- module.exports = __require("./cpufeatures-tjjrgpt7.node");
6321
+ module.exports = __require("./cpufeatures-1yrn0vtw.node");
6322
6322
  });
6323
6323
 
6324
6324
  // node_modules/cpu-features/lib/index.js
@@ -55412,6 +55412,73 @@ class Semaphore {
55412
55412
  }
55413
55413
  }
55414
55414
 
55415
+ // src/engine/default-seccomp-profile.ts
55416
+ var EMBEDDED_DEFAULT_SECCOMP_PROFILE;
55417
+ var init_default_seccomp_profile = __esm(() => {
55418
+ EMBEDDED_DEFAULT_SECCOMP_PROFILE = JSON.stringify({
55419
+ defaultAction: "SCMP_ACT_ALLOW",
55420
+ architectures: ["SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32", "SCMP_ARCH_AARCH64"],
55421
+ syscalls: [
55422
+ {
55423
+ names: [
55424
+ "acct",
55425
+ "add_key",
55426
+ "bpf",
55427
+ "clock_adjtime",
55428
+ "clock_settime",
55429
+ "create_module",
55430
+ "delete_module",
55431
+ "finit_module",
55432
+ "get_mempolicy",
55433
+ "init_module",
55434
+ "ioperm",
55435
+ "iopl",
55436
+ "kcmp",
55437
+ "kexec_file_load",
55438
+ "kexec_load",
55439
+ "keyctl",
55440
+ "lookup_dcookie",
55441
+ "mbind",
55442
+ "mount",
55443
+ "move_pages",
55444
+ "name_to_handle_at",
55445
+ "open_by_handle_at",
55446
+ "perf_event_open",
55447
+ "pivot_root",
55448
+ "process_vm_readv",
55449
+ "process_vm_writev",
55450
+ "ptrace",
55451
+ "query_module",
55452
+ "quotactl",
55453
+ "reboot",
55454
+ "request_key",
55455
+ "set_mempolicy",
55456
+ "setns",
55457
+ "settimeofday",
55458
+ "stime",
55459
+ "swapon",
55460
+ "swapoff",
55461
+ "sysfs",
55462
+ "syslog",
55463
+ "umount",
55464
+ "umount2",
55465
+ "unshare",
55466
+ "uselib",
55467
+ "userfaultfd",
55468
+ "ustat",
55469
+ "vm86",
55470
+ "vm86old"
55471
+ ],
55472
+ action: "SCMP_ACT_ERRNO",
55473
+ args: [],
55474
+ comment: "",
55475
+ includes: {},
55476
+ excludes: {}
55477
+ }
55478
+ ]
55479
+ });
55480
+ });
55481
+
55415
55482
  // src/engine/utils.ts
55416
55483
  var exports_utils = {};
55417
55484
  __export(exports_utils, {
@@ -56155,7 +56222,19 @@ function wrapWithTimeout(cmd, timeoutSec) {
56155
56222
  function getInstallCommand(runtime, packages) {
56156
56223
  switch (runtime) {
56157
56224
  case "python":
56158
- return ["pip", "install", "--user", "--no-cache-dir", "--break-system-packages", ...packages];
56225
+ return [
56226
+ "pip",
56227
+ "install",
56228
+ "--user",
56229
+ "--no-cache-dir",
56230
+ "--break-system-packages",
56231
+ "--disable-pip-version-check",
56232
+ "--retries",
56233
+ "0",
56234
+ "--timeout",
56235
+ "15",
56236
+ ...packages
56237
+ ];
56159
56238
  case "node":
56160
56239
  return ["npm", "install", "--prefix", "/sandbox", ...packages];
56161
56240
  case "bun":
@@ -56168,8 +56247,9 @@ function getInstallCommand(runtime, packages) {
56168
56247
  throw new Error(`Unknown runtime for package install: ${runtime}`);
56169
56248
  }
56170
56249
  }
56171
- async function installPackages(container, runtime, packages) {
56172
- const cmd = getInstallCommand(runtime, packages);
56250
+ async function installPackages(container, runtime, packages, timeoutMs) {
56251
+ const timeoutSec = Math.max(1, Math.ceil(timeoutMs / 1000));
56252
+ const cmd = wrapWithTimeout(getInstallCommand(runtime, packages), timeoutSec);
56173
56253
  logger.debug(`Installing packages: ${JSON.stringify(cmd)}`);
56174
56254
  const env2 = [
56175
56255
  "PATH=/sandbox/.local/bin:/sandbox/.npm-global/bin:/sandbox/.bun-global/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin"
@@ -56180,6 +56260,12 @@ async function installPackages(container, runtime, packages) {
56180
56260
  env2.push("NPM_CONFIG_PREFIX=/sandbox/.npm-global");
56181
56261
  env2.push("NPM_CONFIG_CACHE=/sandbox/.npm-cache");
56182
56262
  env2.push("npm_config_cache=/sandbox/.npm-cache");
56263
+ env2.push("NPM_CONFIG_FETCH_RETRIES=0");
56264
+ env2.push("npm_config_fetch_retries=0");
56265
+ env2.push("NPM_CONFIG_FETCH_RETRY_MINTIMEOUT=1000");
56266
+ env2.push("npm_config_fetch_retry_mintimeout=1000");
56267
+ env2.push("NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT=2000");
56268
+ env2.push("npm_config_fetch_retry_maxtimeout=2000");
56183
56269
  } else if (runtime === "bun") {
56184
56270
  env2.push("BUN_INSTALL_GLOBAL_DIR=/sandbox/.bun-global");
56185
56271
  env2.push("BUN_INSTALL_CACHE_DIR=/sandbox/.bun-cache");
@@ -56201,7 +56287,13 @@ async function installPackages(container, runtime, packages) {
56201
56287
  const stderrStream = new PassThrough;
56202
56288
  container.modem.demuxStream(stream, stdoutStream, stderrStream);
56203
56289
  stderrStream.on("data", (chunk) => {
56204
- stderr += chunk.toString();
56290
+ const text = chunk.toString();
56291
+ stderr += text;
56292
+ logger.debug(`[install:${runtime}:stderr] ${text.trimEnd()}`);
56293
+ });
56294
+ stdoutStream.on("data", (chunk) => {
56295
+ const text = chunk.toString();
56296
+ logger.debug(`[install:${runtime}:stdout] ${text.trimEnd()}`);
56205
56297
  });
56206
56298
  stream.on("end", async () => {
56207
56299
  try {
@@ -56531,7 +56623,7 @@ class DockerIsol8 {
56531
56623
  const filePath = `${SANDBOX_WORKDIR}/main${ext}`;
56532
56624
  await writeFileViaExec(container, filePath, request.code);
56533
56625
  if (request.installPackages?.length) {
56534
- await installPackages(container, request.runtime, request.installPackages);
56626
+ await installPackages(container, request.runtime, request.installPackages, timeoutMs);
56535
56627
  }
56536
56628
  if (request.files) {
56537
56629
  for (const [fPath, fContent] of Object.entries(request.files)) {
@@ -56660,7 +56752,7 @@ class DockerIsol8 {
56660
56752
  rawCmd = adapter.getCommand(req.code, filePath);
56661
56753
  }
56662
56754
  if (req.installPackages?.length) {
56663
- await installPackages(container, req.runtime, req.installPackages);
56755
+ await installPackages(container, req.runtime, req.installPackages, timeoutMs);
56664
56756
  }
56665
56757
  const timeoutSec = Math.ceil(timeoutMs / 1000);
56666
56758
  let cmd;
@@ -56768,7 +56860,7 @@ class DockerIsol8 {
56768
56860
  const rawCmd = adapter.getCommand(req.code, filePath);
56769
56861
  const timeoutSec = Math.ceil(timeoutMs / 1000);
56770
56862
  if (req.installPackages?.length) {
56771
- await installPackages(this.container, req.runtime, req.installPackages);
56863
+ await installPackages(this.container, req.runtime, req.installPackages, timeoutMs);
56772
56864
  }
56773
56865
  let cmd;
56774
56866
  if (req.stdin) {
@@ -56911,17 +57003,15 @@ class DockerIsol8 {
56911
57003
  const profile = readFileSync3(this.security.customProfilePath, "utf-8");
56912
57004
  opts.push(`seccomp=${profile}`);
56913
57005
  } catch (e) {
56914
- logger.error(`Failed to load custom seccomp profile: ${e}`);
57006
+ throw new Error(`Failed to load custom seccomp profile at ${this.security.customProfilePath}: ${e}`);
56915
57007
  }
56916
57008
  return opts;
56917
57009
  }
56918
57010
  try {
56919
57011
  const profile = this.loadDefaultSeccompProfile();
56920
- if (profile) {
56921
- opts.push(`seccomp=${profile}`);
56922
- }
57012
+ opts.push(`seccomp=${profile}`);
56923
57013
  } catch (e) {
56924
- logger.error(`Failed to load default seccomp profile: ${e}`);
57014
+ throw new Error(`Failed to load default seccomp profile: ${e}`);
56925
57015
  }
56926
57016
  return opts;
56927
57017
  }
@@ -56934,8 +57024,11 @@ class DockerIsol8 {
56934
57024
  if (existsSync4(prodPath)) {
56935
57025
  return readFileSync3(prodPath, "utf-8");
56936
57026
  }
56937
- logger.warn("Could not locate default seccomp profile. Running without seccomp filter.");
56938
- return null;
57027
+ if (EMBEDDED_DEFAULT_SECCOMP_PROFILE.length > 0) {
57028
+ logger.debug(`Default seccomp profile file not found. Using embedded profile. Tried: ${devPath.pathname}, ${prodPath.pathname}`);
57029
+ return EMBEDDED_DEFAULT_SECCOMP_PROFILE;
57030
+ }
57031
+ throw new Error("Embedded default seccomp profile is unavailable");
56939
57032
  }
56940
57033
  buildEnv(extra) {
56941
57034
  const env2 = [
@@ -57160,6 +57253,7 @@ var init_docker = __esm(() => {
57160
57253
  init_logger();
57161
57254
  init_audit();
57162
57255
  init_code_fetcher();
57256
+ init_default_seccomp_profile();
57163
57257
  init_image_builder();
57164
57258
  init_pool();
57165
57259
  import_dockerode = __toESM(require_docker(), 1);
@@ -57171,7 +57265,7 @@ var package_default;
57171
57265
  var init_package = __esm(() => {
57172
57266
  package_default = {
57173
57267
  name: "isol8",
57174
- version: "0.11.0",
57268
+ version: "0.11.2",
57175
57269
  description: "Secure code execution engine for AI agents",
57176
57270
  author: "Illusion47586",
57177
57271
  license: "MIT",
@@ -58919,6 +59013,50 @@ async function createServer(options) {
58919
59013
  logger.debug(`[Server] Auto-prune: ${config.cleanup.autoPrune}`);
58920
59014
  const app = new Hono2;
58921
59015
  const globalSemaphore = new Semaphore(config.maxConcurrent);
59016
+ let pruneInterval;
59017
+ let cleanupInFlight = null;
59018
+ const cleanupSessions = async () => {
59019
+ let removed = 0;
59020
+ let failed = 0;
59021
+ const errors = [];
59022
+ for (const [id, session] of sessions) {
59023
+ try {
59024
+ await session.engine.stop();
59025
+ removed++;
59026
+ } catch (err) {
59027
+ failed++;
59028
+ const errorMsg = err instanceof Error ? err.message : String(err);
59029
+ errors.push(`${id}: ${errorMsg}`);
59030
+ } finally {
59031
+ sessions.delete(id);
59032
+ }
59033
+ }
59034
+ return { removed, failed, errors };
59035
+ };
59036
+ const runCleanup = async (includeImages) => {
59037
+ if (cleanupInFlight) {
59038
+ return cleanupInFlight;
59039
+ }
59040
+ cleanupInFlight = (async () => {
59041
+ logger.info(`[Server] Starting cleanup (sessions=true containers=true images=${includeImages})`);
59042
+ const sessionsResult = await cleanupSessions();
59043
+ const containersResult = await DockerIsol82.cleanup();
59044
+ const result = {
59045
+ sessions: sessionsResult,
59046
+ containers: containersResult
59047
+ };
59048
+ if (includeImages) {
59049
+ result.images = await DockerIsol82.cleanupImages();
59050
+ }
59051
+ logger.info(`[Server] Cleanup complete: sessions=${result.sessions.removed}/${result.sessions.failed} containers=${result.containers.removed}/${result.containers.failed}${result.images ? ` images=${result.images.removed}/${result.images.failed}` : ""}`);
59052
+ return result;
59053
+ })();
59054
+ try {
59055
+ return await cleanupInFlight;
59056
+ } finally {
59057
+ cleanupInFlight = null;
59058
+ }
59059
+ };
58922
59060
  app.use("*", authMiddleware(options.apiKey));
58923
59061
  app.get("/health", (c) => c.json({ status: "ok", version: VERSION }));
58924
59062
  app.post("/execute", async (c) => {
@@ -59099,8 +59237,21 @@ async function createServer(options) {
59099
59237
  }
59100
59238
  return c.json({ ok: true });
59101
59239
  });
59240
+ app.post("/cleanup", async (c) => {
59241
+ const body = await c.req.json().catch(() => ({}));
59242
+ const includeImages = body.images ?? true;
59243
+ logger.debug(`[Server] POST /cleanup images=${includeImages}`);
59244
+ try {
59245
+ const result = await runCleanup(includeImages);
59246
+ return c.json({ ok: true, ...result });
59247
+ } catch (err) {
59248
+ const message = err instanceof Error ? err.message : String(err);
59249
+ logger.error(`[Server] Cleanup failed: ${message}`);
59250
+ return c.json({ error: message }, 500);
59251
+ }
59252
+ });
59102
59253
  if (config.cleanup.autoPrune) {
59103
- setInterval(async () => {
59254
+ pruneInterval = setInterval(async () => {
59104
59255
  const maxAge = config.cleanup.maxContainerAgeMs;
59105
59256
  const now = Date.now();
59106
59257
  for (const [id, session] of sessions) {
@@ -59118,7 +59269,15 @@ async function createServer(options) {
59118
59269
  return {
59119
59270
  app,
59120
59271
  fetch: app.fetch,
59121
- port: options.port
59272
+ port: options.port,
59273
+ cleanup: async (includeImages = true) => runCleanup(includeImages),
59274
+ shutdown: async (includeImages = true) => {
59275
+ if (pruneInterval) {
59276
+ clearInterval(pruneInterval);
59277
+ pruneInterval = undefined;
59278
+ }
59279
+ await runCleanup(includeImages);
59280
+ }
59122
59281
  };
59123
59282
  }
59124
59283
  var sessions;
@@ -62747,22 +62906,54 @@ program2.command("run").description("Execute code in isol8").argument("[file]",
62747
62906
  process.exit(exitCode);
62748
62907
  }
62749
62908
  });
62750
- 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) => {
62909
+ program2.command("serve").description("Start the isol8 remote server").option("-p, --port <port>", "Port to listen on").option("-k, --key <key>", "API key for authentication").option("--update", "Force re-download the server binary").option("--debug", "Enable debug logging").action(async (opts) => {
62751
62910
  const apiKey = opts.key ?? process.env.ISOL8_API_KEY;
62752
62911
  if (!apiKey) {
62753
62912
  console.error("[ERR] API key required. Use --key or ISOL8_API_KEY env var.");
62754
62913
  process.exit(1);
62755
62914
  }
62756
- const port = Number.parseInt(opts.port, 10);
62757
- logger.debug(`[Serve] Port: ${port}`);
62915
+ const requestedPort = resolveServePort(opts.port);
62916
+ const port = await resolveAvailableServePort(requestedPort);
62917
+ logger.debug(`[Serve] Requested port: ${requestedPort}`);
62918
+ logger.debug(`[Serve] Using port: ${port}`);
62758
62919
  logger.debug(`[Serve] API key: ${"*".repeat(apiKey.length)}`);
62759
62920
  if (typeof globalThis.Bun !== "undefined") {
62760
62921
  logger.debug("[Serve] Running under Bun, starting server in-process");
62761
62922
  const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), exports_server));
62762
62923
  const server = await createServer2({ port, apiKey, debug: opts.debug ?? false });
62924
+ let shuttingDown = false;
62925
+ const bunServer = Bun.serve({ fetch: server.app.fetch, port });
62926
+ const shutdown = async () => {
62927
+ if (shuttingDown) {
62928
+ return;
62929
+ }
62930
+ shuttingDown = true;
62931
+ logger.info("[Serve] Shutting down server and cleaning up resources...");
62932
+ bunServer.stop();
62933
+ try {
62934
+ await server.shutdown();
62935
+ logger.info("[Serve] Cleanup complete");
62936
+ process.exit(0);
62937
+ } catch (err) {
62938
+ const message = err instanceof Error ? err.message : String(err);
62939
+ logger.error(`[Serve] Cleanup failed: ${message}`);
62940
+ process.exit(1);
62941
+ }
62942
+ };
62943
+ process.on("SIGINT", () => {
62944
+ shutdown().catch((err) => {
62945
+ const message = err instanceof Error ? err.message : String(err);
62946
+ logger.error(`[Serve] Shutdown handler failed: ${message}`);
62947
+ });
62948
+ });
62949
+ process.on("SIGTERM", () => {
62950
+ shutdown().catch((err) => {
62951
+ const message = err instanceof Error ? err.message : String(err);
62952
+ logger.error(`[Serve] Shutdown handler failed: ${message}`);
62953
+ });
62954
+ });
62763
62955
  console.log(`[INFO] isol8 server v${VERSION} listening on http://localhost:${port}`);
62764
62956
  console.log(" Auth: Bearer token required");
62765
- Bun.serve({ fetch: server.app.fetch, port });
62766
62957
  return;
62767
62958
  }
62768
62959
  logger.debug("[Serve] Running under Node.js, launching standalone binary");
@@ -62861,6 +63052,11 @@ async function downloadServerBinary(binaryPath) {
62861
63052
  }
62862
63053
  }
62863
63054
  async function promptYesNo(question) {
63055
+ const answer = await promptText(question);
63056
+ const normalized = answer.trim().toLowerCase();
63057
+ return normalized === "" || normalized === "y" || normalized === "yes";
63058
+ }
63059
+ async function promptText(question) {
62864
63060
  const readline = await import("node:readline");
62865
63061
  const rl = readline.createInterface({
62866
63062
  input: process.stdin,
@@ -62870,8 +63066,103 @@ async function promptYesNo(question) {
62870
63066
  rl.question(question, resolve3);
62871
63067
  });
62872
63068
  rl.close();
62873
- const normalized = answer.trim().toLowerCase();
62874
- return normalized === "" || normalized === "y" || normalized === "yes";
63069
+ return answer;
63070
+ }
63071
+ function parsePort(raw2, source) {
63072
+ const parsed = Number(raw2);
63073
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
63074
+ console.error(`[ERR] Invalid port from ${source}: ${raw2}. Expected 1-65535.`);
63075
+ process.exit(1);
63076
+ }
63077
+ return parsed;
63078
+ }
63079
+ function resolveServePort(portFlag) {
63080
+ if (typeof portFlag === "string") {
63081
+ return parsePort(portFlag, "--port");
63082
+ }
63083
+ if (process.env.ISOL8_PORT) {
63084
+ return parsePort(process.env.ISOL8_PORT, "ISOL8_PORT");
63085
+ }
63086
+ if (process.env.PORT) {
63087
+ return parsePort(process.env.PORT, "PORT");
63088
+ }
63089
+ return 3000;
63090
+ }
63091
+ async function isPortAvailable(port) {
63092
+ const { createServer: createServer2 } = await import("node:net");
63093
+ return await new Promise((resolve3) => {
63094
+ const server = createServer2();
63095
+ server.once("error", () => {
63096
+ resolve3(false);
63097
+ });
63098
+ server.once("listening", () => {
63099
+ server.close(() => resolve3(true));
63100
+ });
63101
+ server.listen(port);
63102
+ });
63103
+ }
63104
+ async function findAvailablePort() {
63105
+ const { createServer: createServer2 } = await import("node:net");
63106
+ return await new Promise((resolve3, reject) => {
63107
+ const server = createServer2();
63108
+ server.once("error", reject);
63109
+ server.once("listening", () => {
63110
+ const address = server.address();
63111
+ if (!address || typeof address === "string") {
63112
+ server.close(() => reject(new Error("Failed to determine available port")));
63113
+ return;
63114
+ }
63115
+ server.close((closeErr) => {
63116
+ if (closeErr) {
63117
+ reject(closeErr);
63118
+ return;
63119
+ }
63120
+ resolve3(address.port);
63121
+ });
63122
+ });
63123
+ server.listen(0);
63124
+ });
63125
+ }
63126
+ async function resolveAvailableServePort(port) {
63127
+ if (await isPortAvailable(port)) {
63128
+ return port;
63129
+ }
63130
+ if (!(process.stdin.isTTY && process.stdout.isTTY)) {
63131
+ const autoPort = await findAvailablePort();
63132
+ console.warn(`[WARN] Port ${port} is in use. Falling back to available port ${autoPort}.`);
63133
+ return autoPort;
63134
+ }
63135
+ let candidate = port;
63136
+ while (true) {
63137
+ console.warn(`[WARN] Port ${candidate} is already in use.`);
63138
+ const choice = (await promptText("Choose: [1] Enter another port [2] Find an available port [3] Exit (default: 2): ")).trim().toLowerCase();
63139
+ if (choice === "" || choice === "2") {
63140
+ const autoPort = await findAvailablePort();
63141
+ console.log(`[INFO] Using available port ${autoPort}`);
63142
+ return autoPort;
63143
+ }
63144
+ if (choice === "1") {
63145
+ const rawPort = (await promptText("Enter port (1-65535): ")).trim();
63146
+ if (!rawPort) {
63147
+ continue;
63148
+ }
63149
+ const parsed = Number(rawPort);
63150
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
63151
+ console.error(`[ERR] Invalid port: ${rawPort}. Expected 1-65535.`);
63152
+ continue;
63153
+ }
63154
+ candidate = parsed;
63155
+ if (await isPortAvailable(candidate)) {
63156
+ return candidate;
63157
+ }
63158
+ continue;
63159
+ }
63160
+ if (choice === "3") {
63161
+ console.error("[ERR] Server startup cancelled.");
63162
+ process.exit(1);
63163
+ }
63164
+ console.error("[ERR] Invalid selection. Enter 1, 2, or 3.");
63165
+ }
62875
63166
  }
62876
63167
  async function ensureServerBinary(forceUpdate) {
62877
63168
  const binDir = join4(homedir2(), ".isol8", "bin");
@@ -63083,6 +63374,7 @@ program2.command("cleanup").description("Remove orphaned isol8 containers (and o
63083
63374
  async function resolveRunInput(file, opts) {
63084
63375
  const config = loadConfig();
63085
63376
  logger.debug("[Run] Config loaded");
63377
+ const hasExplicitNetFlag = process.argv.some((arg) => arg === "--net");
63086
63378
  let code;
63087
63379
  let codeUrl;
63088
63380
  let codeHash;
@@ -63156,6 +63448,20 @@ async function resolveRunInput(file, opts) {
63156
63448
  dependencies: config.dependencies,
63157
63449
  remoteCode: config.remoteCode
63158
63450
  };
63451
+ if (opts.install.length > 0 && !hasExplicitNetFlag) {
63452
+ engineOptions.network = "filtered";
63453
+ logger.debug("[Run] --install detected without explicit --net; using filtered network mode automatically");
63454
+ }
63455
+ if (opts.install.length > 0 && engineOptions.network === "filtered") {
63456
+ const runtimeRegistryAllowlist = getDefaultRegistryAllowPatterns(runtime);
63457
+ if (runtimeRegistryAllowlist.length > 0) {
63458
+ engineOptions.networkFilter = {
63459
+ whitelist: Array.from(new Set([...engineOptions.networkFilter?.whitelist ?? [], ...runtimeRegistryAllowlist])),
63460
+ blacklist: engineOptions.networkFilter?.blacklist ?? []
63461
+ };
63462
+ logger.debug(`[Run] Added default package registries for ${runtime}: ${runtimeRegistryAllowlist.join(", ")}`);
63463
+ }
63464
+ }
63159
63465
  logger.debug(`[Run] Engine options: mode=${engineOptions.mode}, network=${engineOptions.network}`);
63160
63466
  let fileExtension;
63161
63467
  if (file) {
@@ -63231,6 +63537,19 @@ function detectRuntimeFromPath(pathValue) {
63231
63537
  return;
63232
63538
  }
63233
63539
  }
63540
+ function getDefaultRegistryAllowPatterns(runtime) {
63541
+ switch (runtime) {
63542
+ case "python":
63543
+ return ["^pypi\\.org$", "^files\\.pythonhosted\\.org$"];
63544
+ case "node":
63545
+ case "bun":
63546
+ return ["^registry\\.npmjs\\.org$"];
63547
+ case "bash":
63548
+ return ["^dl-cdn\\.alpinelinux\\.org$"];
63549
+ default:
63550
+ return [];
63551
+ }
63552
+ }
63234
63553
  function collect(value, previous) {
63235
63554
  return previous.concat([value]);
63236
63555
  }
@@ -63240,4 +63559,4 @@ if (!process.argv.slice(2).length) {
63240
63559
  }
63241
63560
  program2.parse();
63242
63561
 
63243
- //# debugId=6B3AFECE6CC836BD64756E2164756E21
63562
+ //# debugId=DC4F72D2D8660BF264756E2164756E21
package/dist/index.js CHANGED
@@ -546,6 +546,73 @@ class Semaphore {
546
546
  }
547
547
  }
548
548
 
549
+ // src/engine/default-seccomp-profile.ts
550
+ var EMBEDDED_DEFAULT_SECCOMP_PROFILE;
551
+ var init_default_seccomp_profile = __esm(() => {
552
+ EMBEDDED_DEFAULT_SECCOMP_PROFILE = JSON.stringify({
553
+ defaultAction: "SCMP_ACT_ALLOW",
554
+ architectures: ["SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32", "SCMP_ARCH_AARCH64"],
555
+ syscalls: [
556
+ {
557
+ names: [
558
+ "acct",
559
+ "add_key",
560
+ "bpf",
561
+ "clock_adjtime",
562
+ "clock_settime",
563
+ "create_module",
564
+ "delete_module",
565
+ "finit_module",
566
+ "get_mempolicy",
567
+ "init_module",
568
+ "ioperm",
569
+ "iopl",
570
+ "kcmp",
571
+ "kexec_file_load",
572
+ "kexec_load",
573
+ "keyctl",
574
+ "lookup_dcookie",
575
+ "mbind",
576
+ "mount",
577
+ "move_pages",
578
+ "name_to_handle_at",
579
+ "open_by_handle_at",
580
+ "perf_event_open",
581
+ "pivot_root",
582
+ "process_vm_readv",
583
+ "process_vm_writev",
584
+ "ptrace",
585
+ "query_module",
586
+ "quotactl",
587
+ "reboot",
588
+ "request_key",
589
+ "set_mempolicy",
590
+ "setns",
591
+ "settimeofday",
592
+ "stime",
593
+ "swapon",
594
+ "swapoff",
595
+ "sysfs",
596
+ "syslog",
597
+ "umount",
598
+ "umount2",
599
+ "unshare",
600
+ "uselib",
601
+ "userfaultfd",
602
+ "ustat",
603
+ "vm86",
604
+ "vm86old"
605
+ ],
606
+ action: "SCMP_ACT_ERRNO",
607
+ args: [],
608
+ comment: "",
609
+ includes: {},
610
+ excludes: {}
611
+ }
612
+ ]
613
+ });
614
+ });
615
+
549
616
  // src/engine/image-builder.ts
550
617
  import { createHash as createHash2 } from "node:crypto";
551
618
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
@@ -1087,7 +1154,19 @@ function wrapWithTimeout(cmd, timeoutSec) {
1087
1154
  function getInstallCommand(runtime, packages) {
1088
1155
  switch (runtime) {
1089
1156
  case "python":
1090
- return ["pip", "install", "--user", "--no-cache-dir", "--break-system-packages", ...packages];
1157
+ return [
1158
+ "pip",
1159
+ "install",
1160
+ "--user",
1161
+ "--no-cache-dir",
1162
+ "--break-system-packages",
1163
+ "--disable-pip-version-check",
1164
+ "--retries",
1165
+ "0",
1166
+ "--timeout",
1167
+ "15",
1168
+ ...packages
1169
+ ];
1091
1170
  case "node":
1092
1171
  return ["npm", "install", "--prefix", "/sandbox", ...packages];
1093
1172
  case "bun":
@@ -1100,8 +1179,9 @@ function getInstallCommand(runtime, packages) {
1100
1179
  throw new Error(`Unknown runtime for package install: ${runtime}`);
1101
1180
  }
1102
1181
  }
1103
- async function installPackages(container, runtime, packages) {
1104
- const cmd = getInstallCommand(runtime, packages);
1182
+ async function installPackages(container, runtime, packages, timeoutMs) {
1183
+ const timeoutSec = Math.max(1, Math.ceil(timeoutMs / 1000));
1184
+ const cmd = wrapWithTimeout(getInstallCommand(runtime, packages), timeoutSec);
1105
1185
  logger.debug(`Installing packages: ${JSON.stringify(cmd)}`);
1106
1186
  const env = [
1107
1187
  "PATH=/sandbox/.local/bin:/sandbox/.npm-global/bin:/sandbox/.bun-global/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin"
@@ -1112,6 +1192,12 @@ async function installPackages(container, runtime, packages) {
1112
1192
  env.push("NPM_CONFIG_PREFIX=/sandbox/.npm-global");
1113
1193
  env.push("NPM_CONFIG_CACHE=/sandbox/.npm-cache");
1114
1194
  env.push("npm_config_cache=/sandbox/.npm-cache");
1195
+ env.push("NPM_CONFIG_FETCH_RETRIES=0");
1196
+ env.push("npm_config_fetch_retries=0");
1197
+ env.push("NPM_CONFIG_FETCH_RETRY_MINTIMEOUT=1000");
1198
+ env.push("npm_config_fetch_retry_mintimeout=1000");
1199
+ env.push("NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT=2000");
1200
+ env.push("npm_config_fetch_retry_maxtimeout=2000");
1115
1201
  } else if (runtime === "bun") {
1116
1202
  env.push("BUN_INSTALL_GLOBAL_DIR=/sandbox/.bun-global");
1117
1203
  env.push("BUN_INSTALL_CACHE_DIR=/sandbox/.bun-cache");
@@ -1133,7 +1219,13 @@ async function installPackages(container, runtime, packages) {
1133
1219
  const stderrStream = new PassThrough;
1134
1220
  container.modem.demuxStream(stream, stdoutStream, stderrStream);
1135
1221
  stderrStream.on("data", (chunk) => {
1136
- stderr += chunk.toString();
1222
+ const text = chunk.toString();
1223
+ stderr += text;
1224
+ logger.debug(`[install:${runtime}:stderr] ${text.trimEnd()}`);
1225
+ });
1226
+ stdoutStream.on("data", (chunk) => {
1227
+ const text = chunk.toString();
1228
+ logger.debug(`[install:${runtime}:stdout] ${text.trimEnd()}`);
1137
1229
  });
1138
1230
  stream.on("end", async () => {
1139
1231
  try {
@@ -1463,7 +1555,7 @@ class DockerIsol8 {
1463
1555
  const filePath = `${SANDBOX_WORKDIR}/main${ext}`;
1464
1556
  await writeFileViaExec(container, filePath, request.code);
1465
1557
  if (request.installPackages?.length) {
1466
- await installPackages(container, request.runtime, request.installPackages);
1558
+ await installPackages(container, request.runtime, request.installPackages, timeoutMs);
1467
1559
  }
1468
1560
  if (request.files) {
1469
1561
  for (const [fPath, fContent] of Object.entries(request.files)) {
@@ -1592,7 +1684,7 @@ class DockerIsol8 {
1592
1684
  rawCmd = adapter.getCommand(req.code, filePath);
1593
1685
  }
1594
1686
  if (req.installPackages?.length) {
1595
- await installPackages(container, req.runtime, req.installPackages);
1687
+ await installPackages(container, req.runtime, req.installPackages, timeoutMs);
1596
1688
  }
1597
1689
  const timeoutSec = Math.ceil(timeoutMs / 1000);
1598
1690
  let cmd;
@@ -1700,7 +1792,7 @@ class DockerIsol8 {
1700
1792
  const rawCmd = adapter.getCommand(req.code, filePath);
1701
1793
  const timeoutSec = Math.ceil(timeoutMs / 1000);
1702
1794
  if (req.installPackages?.length) {
1703
- await installPackages(this.container, req.runtime, req.installPackages);
1795
+ await installPackages(this.container, req.runtime, req.installPackages, timeoutMs);
1704
1796
  }
1705
1797
  let cmd;
1706
1798
  if (req.stdin) {
@@ -1843,17 +1935,15 @@ class DockerIsol8 {
1843
1935
  const profile = readFileSync3(this.security.customProfilePath, "utf-8");
1844
1936
  opts.push(`seccomp=${profile}`);
1845
1937
  } catch (e) {
1846
- logger.error(`Failed to load custom seccomp profile: ${e}`);
1938
+ throw new Error(`Failed to load custom seccomp profile at ${this.security.customProfilePath}: ${e}`);
1847
1939
  }
1848
1940
  return opts;
1849
1941
  }
1850
1942
  try {
1851
1943
  const profile = this.loadDefaultSeccompProfile();
1852
- if (profile) {
1853
- opts.push(`seccomp=${profile}`);
1854
- }
1944
+ opts.push(`seccomp=${profile}`);
1855
1945
  } catch (e) {
1856
- logger.error(`Failed to load default seccomp profile: ${e}`);
1946
+ throw new Error(`Failed to load default seccomp profile: ${e}`);
1857
1947
  }
1858
1948
  return opts;
1859
1949
  }
@@ -1866,8 +1956,11 @@ class DockerIsol8 {
1866
1956
  if (existsSync4(prodPath)) {
1867
1957
  return readFileSync3(prodPath, "utf-8");
1868
1958
  }
1869
- logger.warn("Could not locate default seccomp profile. Running without seccomp filter.");
1870
- return null;
1959
+ if (EMBEDDED_DEFAULT_SECCOMP_PROFILE.length > 0) {
1960
+ logger.debug(`Default seccomp profile file not found. Using embedded profile. Tried: ${devPath.pathname}, ${prodPath.pathname}`);
1961
+ return EMBEDDED_DEFAULT_SECCOMP_PROFILE;
1962
+ }
1963
+ throw new Error("Embedded default seccomp profile is unavailable");
1871
1964
  }
1872
1965
  buildEnv(extra) {
1873
1966
  const env = [
@@ -2092,6 +2185,7 @@ var init_docker = __esm(() => {
2092
2185
  init_logger();
2093
2186
  init_audit();
2094
2187
  init_code_fetcher();
2188
+ init_default_seccomp_profile();
2095
2189
  init_image_builder();
2096
2190
  init_pool();
2097
2191
  MAX_OUTPUT_BYTES = 1024 * 1024;
@@ -2349,7 +2443,7 @@ init_logger();
2349
2443
  // package.json
2350
2444
  var package_default = {
2351
2445
  name: "isol8",
2352
- version: "0.11.0",
2446
+ version: "0.11.2",
2353
2447
  description: "Secure code execution engine for AI agents",
2354
2448
  author: "Illusion47586",
2355
2449
  license: "MIT",
@@ -2501,6 +2595,50 @@ async function createServer(options) {
2501
2595
  logger.debug(`[Server] Auto-prune: ${config.cleanup.autoPrune}`);
2502
2596
  const app = new Hono;
2503
2597
  const globalSemaphore = new Semaphore(config.maxConcurrent);
2598
+ let pruneInterval;
2599
+ let cleanupInFlight = null;
2600
+ const cleanupSessions = async () => {
2601
+ let removed = 0;
2602
+ let failed = 0;
2603
+ const errors = [];
2604
+ for (const [id, session] of sessions) {
2605
+ try {
2606
+ await session.engine.stop();
2607
+ removed++;
2608
+ } catch (err) {
2609
+ failed++;
2610
+ const errorMsg = err instanceof Error ? err.message : String(err);
2611
+ errors.push(`${id}: ${errorMsg}`);
2612
+ } finally {
2613
+ sessions.delete(id);
2614
+ }
2615
+ }
2616
+ return { removed, failed, errors };
2617
+ };
2618
+ const runCleanup = async (includeImages) => {
2619
+ if (cleanupInFlight) {
2620
+ return cleanupInFlight;
2621
+ }
2622
+ cleanupInFlight = (async () => {
2623
+ logger.info(`[Server] Starting cleanup (sessions=true containers=true images=${includeImages})`);
2624
+ const sessionsResult = await cleanupSessions();
2625
+ const containersResult = await DockerIsol82.cleanup();
2626
+ const result = {
2627
+ sessions: sessionsResult,
2628
+ containers: containersResult
2629
+ };
2630
+ if (includeImages) {
2631
+ result.images = await DockerIsol82.cleanupImages();
2632
+ }
2633
+ logger.info(`[Server] Cleanup complete: sessions=${result.sessions.removed}/${result.sessions.failed} containers=${result.containers.removed}/${result.containers.failed}${result.images ? ` images=${result.images.removed}/${result.images.failed}` : ""}`);
2634
+ return result;
2635
+ })();
2636
+ try {
2637
+ return await cleanupInFlight;
2638
+ } finally {
2639
+ cleanupInFlight = null;
2640
+ }
2641
+ };
2504
2642
  app.use("*", authMiddleware(options.apiKey));
2505
2643
  app.get("/health", (c) => c.json({ status: "ok", version: VERSION }));
2506
2644
  app.post("/execute", async (c) => {
@@ -2681,8 +2819,21 @@ async function createServer(options) {
2681
2819
  }
2682
2820
  return c.json({ ok: true });
2683
2821
  });
2822
+ app.post("/cleanup", async (c) => {
2823
+ const body = await c.req.json().catch(() => ({}));
2824
+ const includeImages = body.images ?? true;
2825
+ logger.debug(`[Server] POST /cleanup images=${includeImages}`);
2826
+ try {
2827
+ const result = await runCleanup(includeImages);
2828
+ return c.json({ ok: true, ...result });
2829
+ } catch (err) {
2830
+ const message = err instanceof Error ? err.message : String(err);
2831
+ logger.error(`[Server] Cleanup failed: ${message}`);
2832
+ return c.json({ error: message }, 500);
2833
+ }
2834
+ });
2684
2835
  if (config.cleanup.autoPrune) {
2685
- setInterval(async () => {
2836
+ pruneInterval = setInterval(async () => {
2686
2837
  const maxAge = config.cleanup.maxContainerAgeMs;
2687
2838
  const now = Date.now();
2688
2839
  for (const [id, session] of sessions) {
@@ -2700,7 +2851,15 @@ async function createServer(options) {
2700
2851
  return {
2701
2852
  app,
2702
2853
  fetch: app.fetch,
2703
- port: options.port
2854
+ port: options.port,
2855
+ cleanup: async (includeImages = true) => runCleanup(includeImages),
2856
+ shutdown: async (includeImages = true) => {
2857
+ if (pruneInterval) {
2858
+ clearInterval(pruneInterval);
2859
+ pruneInterval = undefined;
2860
+ }
2861
+ await runCleanup(includeImages);
2862
+ }
2704
2863
  };
2705
2864
  }
2706
2865
  export {
@@ -2717,4 +2876,4 @@ export {
2717
2876
  BunAdapter
2718
2877
  };
2719
2878
 
2720
- //# debugId=C48E982CAC86EB5964756E2164756E21
2879
+ //# debugId=C10FBC887CAF691764756E2164756E21
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Embedded default seccomp profile.
3
+ *
4
+ * This keeps strict seccomp available in standalone compiled binaries where
5
+ * docker/seccomp-profile.json may not be present on disk.
6
+ */
7
+ export declare const EMBEDDED_DEFAULT_SECCOMP_PROFILE: string;
8
+ //# sourceMappingURL=default-seccomp-profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-seccomp-profile.d.ts","sourceRoot":"","sources":["../../../src/engine/default-seccomp-profile.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,QA6D3C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../../src/engine/docker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,MAAM,MAAM,WAAW,CAAC;AAG/B,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EAEf,WAAW,EAEX,YAAY,EAKZ,YAAY,EACZ,WAAW,EACZ,MAAM,UAAU,CAAC;AA2UlB,2HAA2H;AAC3H,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,WAAY,YAAW,WAAW;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAY;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAsB;IACrD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4C;IACrE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;IAEpD,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,IAAI,CAA8B;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;YAE1C,uBAAuB;IA6BrC;;;OAGG;gBACS,OAAO,GAAE,kBAAuB,EAAE,aAAa,SAAK;IA4ChE;;;;;OAKG;IACG,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAsCtD,kFAAkF;IAC5E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB3B;;;OAGG;IACG,OAAO,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAgB9D;;OAEG;YACW,WAAW;IAoDzB;;OAEG;YACW,qBAAqB;IA8CnC;;OAEG;YACW,kBAAkB;IA+DhC;;;;;;;OAOG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYpE;;;;;;OAMG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmB5C,6GAA6G;IAC7G,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAE/B;IAED;;;OAGG;IACI,aAAa,CAAC,GAAG,EAAE,gBAAgB,GAAG,aAAa,CAAC,WAAW,CAAC;YAuFzD,YAAY;IA0C1B,OAAO,CAAC,UAAU;YAsBJ,gBAAgB;YAgKhB,iBAAiB;YAwIjB,aAAa;YAkBb,oBAAoB;YASpB,wBAAwB;IA4BtC,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,eAAe;IA2BvB,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,yBAAyB;IAyBjC,OAAO,CAAC,QAAQ;YAwCD,gBAAgB;YA8EjB,iBAAiB;IAiG/B,OAAO,CAAC,iBAAiB;IAYzB;;;;;;;;;;;;;;;;;;;;OAoBG;WACU,OAAO,CAClB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IA0BjE;;;;;OAKG;WACU,aAAa,CACxB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CA2BlE"}
1
+ {"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../../src/engine/docker.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,23 @@ export interface ServerOptions {
15
15
  /** Enable debug logging for internal server operations. */
16
16
  debug?: boolean;
17
17
  }
18
+ interface CleanupResult {
19
+ sessions: {
20
+ removed: number;
21
+ failed: number;
22
+ errors: string[];
23
+ };
24
+ containers: {
25
+ removed: number;
26
+ failed: number;
27
+ errors: string[];
28
+ };
29
+ images?: {
30
+ removed: number;
31
+ failed: number;
32
+ errors: string[];
33
+ };
34
+ }
18
35
  /**
19
36
  * Creates and configures the isol8 HTTP server.
20
37
  *
@@ -36,5 +53,8 @@ export declare function createServer(options: ServerOptions): Promise<{
36
53
  app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
37
54
  fetch: (request: Request, Env?: unknown, executionCtx?: import("hono").ExecutionContext) => Response | Promise<Response>;
38
55
  port: number;
56
+ cleanup: (includeImages?: boolean) => Promise<CleanupResult>;
57
+ shutdown: (includeImages?: boolean) => Promise<void>;
39
58
  }>;
59
+ export {};
40
60
  //# sourceMappingURL=index.d.ts.map
@@ -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;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;AAaD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa;;;;GAmRxD"}
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;AAaD,UAAU,aAAa;IACrB,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAChE,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAClE,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAChE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa;;;;;;GAyWxD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "isol8",
3
- "version": "0.11.1",
3
+ "version": "0.11.3",
4
4
  "description": "Secure code execution engine for AI agents",
5
5
  "author": "Illusion47586",
6
6
  "license": "MIT",