isol8 0.5.1 → 0.6.1

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
@@ -135,12 +135,19 @@ isol8 cleanup --force
135
135
 
136
136
  ### `isol8 serve`
137
137
 
138
- Start the isol8 remote HTTP server. **Requires Bun runtime.**
138
+ Start the isol8 remote HTTP server. Downloads a pre-compiled standalone binary the first time you run this command (no Bun runtime required).
139
139
 
140
140
  ```bash
141
141
  isol8 serve --port 3000 --key my-secret-key
142
+ isol8 serve --update # Force re-download the server binary
142
143
  ```
143
144
 
145
+ | Flag | Description | Default |
146
+ |------|-------------|---------|
147
+ | `-p, --port <port>` | Port to listen on | `3000` |
148
+ | `-k, --key <key>` | API key for Bearer token auth | `$ISOL8_API_KEY` |
149
+ | `--update` | Force re-download the server binary | `false` |
150
+
144
151
  ### `isol8 config`
145
152
 
146
153
  Display the resolved configuration (merged defaults + config file). Shows the source file, defaults, network rules, cleanup policy, and dependencies.
@@ -408,6 +415,10 @@ bunx tsc --noEmit
408
415
  # Lint
409
416
  bun run lint
410
417
 
418
+ # Build
419
+ bun run build # Bundle CLI for Node.js distribution
420
+ bun run build:server # Compile standalone server binary
421
+
411
422
  # Benchmarks
412
423
  bun run bench # Cold start
413
424
  bun run bench:pool # Warm pool
package/dist/cli.js CHANGED
@@ -6929,6 +6929,11 @@ 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
+
6932
6937
  // node_modules/ssh2/lib/protocol/crypto/poly1305.js
6933
6938
  var require_poly1305 = __commonJS((exports, module) => {
6934
6939
  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";
@@ -7415,7 +7420,7 @@ var require_crypto = __commonJS((exports, module) => {
7415
7420
  var ChaChaPolyDecipher;
7416
7421
  var GenericDecipher;
7417
7422
  try {
7418
- binding = (()=>{throw new Error("Cannot require module "+"./crypto/build/Release/sshcrypto.node");})();
7423
+ binding = require_sshcrypto();
7419
7424
  ({
7420
7425
  AESGCMCipher,
7421
7426
  ChaChaPolyCipher,
@@ -54955,6 +54960,15 @@ var init_python = __esm(() => {
54955
54960
  });
54956
54961
 
54957
54962
  // src/runtime/index.ts
54963
+ var exports_runtime = {};
54964
+ __export(exports_runtime, {
54965
+ bashAdapter: () => bashAdapter,
54966
+ RuntimeRegistry: () => RuntimeRegistry,
54967
+ PythonAdapter: () => PythonAdapter,
54968
+ NodeAdapter: () => NodeAdapter,
54969
+ DenoAdapter: () => DenoAdapter,
54970
+ BunAdapter: () => BunAdapter
54971
+ });
54958
54972
  var init_runtime = __esm(() => {
54959
54973
  init_adapter();
54960
54974
  init_bash();
@@ -55250,6 +55264,10 @@ function extractFromTar(tarBuffer, targetPath) {
55250
55264
  }
55251
55265
 
55252
55266
  // src/engine/docker.ts
55267
+ var exports_docker = {};
55268
+ __export(exports_docker, {
55269
+ DockerIsol8: () => DockerIsol8
55270
+ });
55253
55271
  import { randomUUID } from "node:crypto";
55254
55272
  import { PassThrough } from "node:stream";
55255
55273
  async function writeFileViaExec(container, filePath, content) {
@@ -55945,6 +55963,125 @@ var init_docker = __esm(() => {
55945
55963
  MAX_OUTPUT_BYTES = 1024 * 1024;
55946
55964
  });
55947
55965
 
55966
+ // package.json
55967
+ var package_default;
55968
+ var init_package = __esm(() => {
55969
+ package_default = {
55970
+ name: "isol8",
55971
+ version: "0.6.0",
55972
+ description: "Secure code execution engine for AI agents",
55973
+ author: "Illusion47586",
55974
+ license: "MIT",
55975
+ repository: {
55976
+ type: "git",
55977
+ url: "https://github.com/Illusion47586/isol8.git"
55978
+ },
55979
+ homepage: "https://github.com/Illusion47586/isol8",
55980
+ bugs: {
55981
+ url: "https://github.com/Illusion47586/isol8/issues"
55982
+ },
55983
+ keywords: [
55984
+ "sandbox",
55985
+ "docker",
55986
+ "code-execution",
55987
+ "isolation",
55988
+ "security",
55989
+ "ai-agents",
55990
+ "container",
55991
+ "runtime"
55992
+ ],
55993
+ type: "module",
55994
+ main: "./dist/index.js",
55995
+ types: "./dist/index.d.ts",
55996
+ exports: {
55997
+ ".": {
55998
+ import: "./dist/index.js",
55999
+ types: "./dist/index.d.ts"
56000
+ },
56001
+ "./schema": "./schema/isol8.config.schema.json"
56002
+ },
56003
+ bin: {
56004
+ isol8: "./dist/cli.js"
56005
+ },
56006
+ scripts: {
56007
+ dev: "bun run src/cli.ts",
56008
+ build: "bun run scripts/build.ts",
56009
+ "build:server": "bun run scripts/build-server.ts",
56010
+ "build:server:all": "bun run scripts/build-server.ts --all",
56011
+ test: "bun test",
56012
+ "lint:check": "ultracite check",
56013
+ "lint:fix": "ultracite fix",
56014
+ bench: "bunx tsx benchmarks/spawn.ts",
56015
+ "bench:pool": "bunx tsx benchmarks/spawn-pool.ts",
56016
+ "bench:detailed": "bunx tsx benchmarks/spawn-detailed.ts",
56017
+ "docs:dev": "cd docs && mint dev",
56018
+ "docs:validate": "cd docs && mint validate",
56019
+ "docs:broken-links": "cd docs && mint broken-links",
56020
+ schema: "ts-json-schema-generator --path src/types.ts --type Isol8UserConfig --tsconfig tsconfig.json -o schema/isol8.config.schema.json && ultracite fix schema/isol8.config.schema.json",
56021
+ "publish:alpha": "bun run build && bun publish --tag alpha --access public --ignore-scripts",
56022
+ prepare: "simple-git-hooks"
56023
+ },
56024
+ dependencies: {
56025
+ commander: "^14.0.3",
56026
+ dockerode: "^4.0.9",
56027
+ hono: "^4.11.9",
56028
+ ora: "^9.3.0"
56029
+ },
56030
+ devDependencies: {
56031
+ "@biomejs/biome": "^2.3.15",
56032
+ "@semantic-release/changelog": "^6.0.3",
56033
+ "@semantic-release/git": "^10.0.1",
56034
+ "@semantic-release/github": "^12.0.6",
56035
+ "@semantic-release/npm": "^13.1.4",
56036
+ "@types/bun": "latest",
56037
+ "@types/dockerode": "^4.0.1",
56038
+ "@types/node": "^25.2.3",
56039
+ "lint-staged": "^16.2.7",
56040
+ mint: "^4.2.348",
56041
+ "semantic-release": "^25.0.3",
56042
+ "simple-git-hooks": "^2.13.1",
56043
+ "ts-json-schema-generator": "^2.5.0",
56044
+ typescript: "^5.9.3",
56045
+ ultracite: "^7.2.0"
56046
+ },
56047
+ files: [
56048
+ "dist/cli.js",
56049
+ "dist/index.js",
56050
+ "dist/src",
56051
+ "dist/docker",
56052
+ "schema",
56053
+ "README.md",
56054
+ "LICENSE"
56055
+ ],
56056
+ jsonValidation: [
56057
+ {
56058
+ fileMatch: "isol8.config.json",
56059
+ url: "./schema/isol8.config.schema.json"
56060
+ }
56061
+ ],
56062
+ "simple-git-hooks": {
56063
+ "pre-commit": "bun run lint-staged"
56064
+ },
56065
+ "lint-staged": {
56066
+ "*.{ts,tsx}": [
56067
+ "ultracite fix",
56068
+ "bash -c 'bunx tsc --noEmit -p tsconfig.json'"
56069
+ ],
56070
+ "src/types.ts": [
56071
+ "bash -c 'bun run schema'",
56072
+ "git add schema/isol8.config.schema.json"
56073
+ ]
56074
+ }
56075
+ };
56076
+ });
56077
+
56078
+ // src/version.ts
56079
+ var VERSION;
56080
+ var init_version = __esm(() => {
56081
+ init_package();
56082
+ VERSION = package_default.version;
56083
+ });
56084
+
55948
56085
  // node_modules/hono/dist/compose.js
55949
56086
  var compose = (middleware, onError, onNotFound) => {
55950
56087
  return (context, next) => {
@@ -57556,12 +57693,14 @@ var exports_server = {};
57556
57693
  __export(exports_server, {
57557
57694
  createServer: () => createServer
57558
57695
  });
57559
- function createServer(options) {
57696
+ async function createServer(options) {
57697
+ const { DockerIsol8: DockerIsol82 } = await Promise.resolve().then(() => (init_docker(), exports_docker));
57698
+ await Promise.resolve().then(() => (init_runtime(), exports_runtime));
57560
57699
  const config = loadConfig();
57561
57700
  const app = new Hono2;
57562
57701
  const globalSemaphore = new Semaphore(config.maxConcurrent);
57563
57702
  app.use("*", authMiddleware(options.apiKey));
57564
- app.get("/health", (c) => c.json({ status: "ok", version: "0.1.0" }));
57703
+ app.get("/health", (c) => c.json({ status: "ok", version: VERSION }));
57565
57704
  app.post("/execute", async (c) => {
57566
57705
  const body = await c.req.json();
57567
57706
  const engineOptions = {
@@ -57581,12 +57720,12 @@ function createServer(options) {
57581
57720
  engine = session.engine;
57582
57721
  session.lastAccessedAt = Date.now();
57583
57722
  } else {
57584
- engine = new DockerIsol8(engineOptions, config.maxConcurrent);
57723
+ engine = new DockerIsol82(engineOptions, config.maxConcurrent);
57585
57724
  await engine.start();
57586
57725
  sessions.set(body.sessionId, { engine, lastAccessedAt: Date.now() });
57587
57726
  }
57588
57727
  } else {
57589
- engine = new DockerIsol8(engineOptions, config.maxConcurrent);
57728
+ engine = new DockerIsol82(engineOptions, config.maxConcurrent);
57590
57729
  await engine.start();
57591
57730
  }
57592
57731
  try {
@@ -57618,7 +57757,7 @@ function createServer(options) {
57618
57757
  ...body.options,
57619
57758
  mode: "ephemeral"
57620
57759
  };
57621
- const engine = new DockerIsol8(engineOptions, config.maxConcurrent);
57760
+ const engine = new DockerIsol82(engineOptions, config.maxConcurrent);
57622
57761
  await engine.start();
57623
57762
  const encoder = new TextEncoder;
57624
57763
  const stream = new ReadableStream({
@@ -57711,14 +57850,21 @@ var sessions;
57711
57850
  var init_server = __esm(() => {
57712
57851
  init_dist();
57713
57852
  init_config();
57714
- init_docker();
57715
- init_runtime();
57853
+ init_version();
57716
57854
  sessions = new Map;
57717
57855
  });
57718
57856
 
57719
57857
  // src/cli.ts
57720
- import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync } from "node:fs";
57721
- import { homedir as homedir2 } from "node:os";
57858
+ import {
57859
+ chmodSync,
57860
+ existsSync as existsSync3,
57861
+ mkdirSync,
57862
+ readFileSync as readFileSync2,
57863
+ renameSync,
57864
+ unlinkSync,
57865
+ writeFileSync
57866
+ } from "node:fs";
57867
+ import { arch, homedir as homedir2, platform } from "node:os";
57722
57868
  import { join as join2, resolve as resolve2 } from "node:path";
57723
57869
 
57724
57870
  // node_modules/commander/esm.mjs
@@ -61216,8 +61362,9 @@ ${installCmd}
61216
61362
 
61217
61363
  // src/cli.ts
61218
61364
  init_runtime();
61365
+ init_version();
61219
61366
  var program2 = new Command;
61220
- program2.name("isol8").description("Secure code execution engine").version("0.1.0");
61367
+ program2.name("isol8").description("Secure code execution engine").version(VERSION);
61221
61368
  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) => {
61222
61369
  const docker = new import_dockerode2.default;
61223
61370
  const spinner = ora("Checking Docker...").start();
@@ -61361,29 +61508,141 @@ program2.command("run").description("Execute code in isol8").argument("[file]",
61361
61508
  process.exit(exitCode);
61362
61509
  }
61363
61510
  });
61364
- 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").action(async (opts) => {
61365
- const isBun = typeof globalThis.Bun !== "undefined";
61366
- if (!isBun) {
61367
- console.error("[ERR] The serve command requires Bun runtime.");
61368
- console.error(" Install Bun: https://bun.sh");
61369
- console.error(" Then run: bun run src/cli.ts serve");
61370
- process.exit(1);
61371
- }
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) => {
61372
61512
  const apiKey = opts.key ?? process.env.ISOL8_API_KEY;
61373
61513
  if (!apiKey) {
61374
61514
  console.error("[ERR] API key required. Use --key or ISOL8_API_KEY env var.");
61375
61515
  process.exit(1);
61376
61516
  }
61377
61517
  const port = Number.parseInt(opts.port, 10);
61378
- const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), exports_server));
61379
- const server = createServer2({ port, apiKey });
61380
- console.log(`[INFO] isol8 server listening on http://localhost:${port}`);
61381
- console.log(" Auth: Bearer token required");
61382
- Bun.serve({
61383
- fetch: server.app.fetch,
61384
- port
61518
+ if (typeof globalThis.Bun !== "undefined") {
61519
+ const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), exports_server));
61520
+ const server = await createServer2({ port, apiKey });
61521
+ console.log(`[INFO] isol8 server v${VERSION} listening on http://localhost:${port}`);
61522
+ console.log(" Auth: Bearer token required");
61523
+ Bun.serve({ fetch: server.app.fetch, port });
61524
+ return;
61525
+ }
61526
+ const binaryPath = await ensureServerBinary(opts.update ?? false);
61527
+ const { spawn: spawnChild } = await import("node:child_process");
61528
+ const child = spawnChild(binaryPath, ["--port", String(port), "--key", apiKey], {
61529
+ stdio: "inherit"
61530
+ });
61531
+ const forwardSignal = (signal) => {
61532
+ child.kill(signal);
61533
+ };
61534
+ process.on("SIGINT", () => forwardSignal("SIGINT"));
61535
+ process.on("SIGTERM", () => forwardSignal("SIGTERM"));
61536
+ child.on("exit", (code) => {
61537
+ process.exit(code ?? 0);
61385
61538
  });
61386
61539
  });
61540
+ function getServerBinaryName() {
61541
+ const os2 = platform();
61542
+ const cpu = arch();
61543
+ const osMap = {
61544
+ darwin: "darwin",
61545
+ linux: "linux",
61546
+ win32: "windows"
61547
+ };
61548
+ const archMap = {
61549
+ arm64: "arm64",
61550
+ aarch64: "arm64",
61551
+ x64: "x64",
61552
+ x86_64: "x64"
61553
+ };
61554
+ const resolvedOs = osMap[os2];
61555
+ const resolvedArch = archMap[cpu];
61556
+ if (!(resolvedOs && resolvedArch)) {
61557
+ console.error(`[ERR] Unsupported platform: ${os2}-${cpu}`);
61558
+ process.exit(1);
61559
+ }
61560
+ return `isol8-server-${resolvedOs}-${resolvedArch}`;
61561
+ }
61562
+ async function getServerBinaryVersion(binaryPath) {
61563
+ if (!existsSync3(binaryPath)) {
61564
+ return null;
61565
+ }
61566
+ try {
61567
+ const { execFileSync } = await import("node:child_process");
61568
+ const output = execFileSync(binaryPath, ["--version"], {
61569
+ encoding: "utf-8",
61570
+ timeout: 5000
61571
+ });
61572
+ return output.trim();
61573
+ } catch {
61574
+ return null;
61575
+ }
61576
+ }
61577
+ async function downloadServerBinary(binaryPath) {
61578
+ const binaryName = getServerBinaryName();
61579
+ const url = `https://github.com/Illusion47586/isol8/releases/download/v${VERSION}/${binaryName}`;
61580
+ const spinner = ora(`Downloading isol8 server v${VERSION}...`).start();
61581
+ try {
61582
+ const response = await fetch(url, { redirect: "follow" });
61583
+ if (!response.ok) {
61584
+ spinner.fail(`Failed to download server binary (HTTP ${response.status})`);
61585
+ if (response.status === 404) {
61586
+ console.error(`[ERR] No server binary found for v${VERSION} (${binaryName}).`);
61587
+ console.error(" Server binaries may not be available for this version yet.");
61588
+ console.error(` URL: ${url}`);
61589
+ }
61590
+ process.exit(1);
61591
+ }
61592
+ const binDir = join2(homedir2(), ".isol8", "bin");
61593
+ mkdirSync(binDir, { recursive: true });
61594
+ const tmpPath = `${binaryPath}.tmp`;
61595
+ const buffer = Buffer.from(await response.arrayBuffer());
61596
+ writeFileSync(tmpPath, buffer);
61597
+ chmodSync(tmpPath, 493);
61598
+ renameSync(tmpPath, binaryPath);
61599
+ spinner.succeed(`Downloaded isol8 server v${VERSION}`);
61600
+ } catch (err) {
61601
+ spinner.fail("Failed to download server binary");
61602
+ const tmpPath = `${binaryPath}.tmp`;
61603
+ if (existsSync3(tmpPath)) {
61604
+ unlinkSync(tmpPath);
61605
+ }
61606
+ throw err;
61607
+ }
61608
+ }
61609
+ async function promptYesNo(question) {
61610
+ const readline = await import("node:readline");
61611
+ const rl = readline.createInterface({
61612
+ input: process.stdin,
61613
+ output: process.stdout
61614
+ });
61615
+ const answer = await new Promise((resolve3) => {
61616
+ rl.question(question, resolve3);
61617
+ });
61618
+ rl.close();
61619
+ const normalized = answer.trim().toLowerCase();
61620
+ return normalized === "" || normalized === "y" || normalized === "yes";
61621
+ }
61622
+ async function ensureServerBinary(forceUpdate) {
61623
+ const binDir = join2(homedir2(), ".isol8", "bin");
61624
+ const binaryPath = join2(binDir, "isol8-server");
61625
+ if (forceUpdate) {
61626
+ await downloadServerBinary(binaryPath);
61627
+ return binaryPath;
61628
+ }
61629
+ const existingVersion = await getServerBinaryVersion(binaryPath);
61630
+ if (existingVersion === null) {
61631
+ await downloadServerBinary(binaryPath);
61632
+ return binaryPath;
61633
+ }
61634
+ if (existingVersion === VERSION) {
61635
+ return binaryPath;
61636
+ }
61637
+ console.log(`Server binary v${existingVersion} found, but CLI is v${VERSION}.`);
61638
+ const shouldUpdate = await promptYesNo("Download updated binary? [Y/n] ");
61639
+ if (shouldUpdate) {
61640
+ await downloadServerBinary(binaryPath);
61641
+ } else {
61642
+ console.warn(`[WARN] Running server v${existingVersion} (CLI is v${VERSION})`);
61643
+ }
61644
+ return binaryPath;
61645
+ }
61387
61646
  program2.command("config").description("Show the resolved isol8 configuration").option("--json", "Output as raw JSON").action((opts) => {
61388
61647
  const config = loadConfig();
61389
61648
  const searchPaths = [
@@ -61600,4 +61859,4 @@ if (!process.argv.slice(2).length) {
61600
61859
  }
61601
61860
  program2.parse();
61602
61861
 
61603
- //# debugId=E350C5AB2EA12EB364756E2164756E21
61862
+ //# debugId=05A7CFDAF922346964756E2164756E21