kern-sandbox 0.2.6 → 0.2.7

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.
Files changed (2) hide show
  1. package/index.js +14 -2
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -36,7 +36,7 @@ const crypto = require("crypto");
36
36
  const zlib = require("zlib");
37
37
  const { spawn, spawnSync } = require("child_process");
38
38
 
39
- const VERSION = "0.2.6";
39
+ const VERSION = "0.2.7";
40
40
 
41
41
  const DEFAULT_IMAGE = "python:3.12-slim";
42
42
  const WORKSPACE = "/workspace"; // where the persistent workspace is mounted inside every box
@@ -2417,6 +2417,11 @@ class Kernel {
2417
2417
  this._waiters = []; // FIFO of { resolve, timer }; one reply per request keeps them in order
2418
2418
  this._stderr = Buffer.alloc(0);
2419
2419
  this._dead = false;
2420
+ // WHY THE CAUSE IS KEPT. Every death funnels through `_teardownResult`, which KNOWS what ended the
2421
+ // kernel, and the next cell then threw "a prior cell timed out, or the box exited" - two guesses
2422
+ // where the answer was in hand (MEASURED: a cell that blew the memory cap produced
2423
+ // `fault.type === "oom"`, and the very next cell blamed a timeout). If it is known, name it.
2424
+ this._death = null;
2420
2425
  // kern's KERN_STARTED_FD bytes for a RESIDENT box: the enforcement byte (2nd) and the OOM-outcome
2421
2426
  // byte (3rd). kern writes them only at box teardown (a cell kills the kernel), so they arrive
2422
2427
  // ~concurrent with the death we detect on stdout; read once, bounded, on death (`_readCapSignal`).
@@ -2522,7 +2527,13 @@ class Kernel {
2522
2527
 
2523
2528
  async runCode(code, { timeoutS } = {}) {
2524
2529
  if (!this._child) throw new SandboxError("kernel not started");
2525
- if (this._dead) throw new SandboxError("kernel is dead (a prior cell timed out, or the box exited)");
2530
+ if (this._dead) {
2531
+ const why = this._death ? `a prior cell ended it (${this._death})` : "it was closed";
2532
+ throw new SandboxError(
2533
+ `kernel is dead: ${why}. Files written to the workspace are still there; names and imports ` +
2534
+ "from the earlier cells are gone. Open a new one with `sbx.kernel()`"
2535
+ );
2536
+ }
2526
2537
  if (typeof code !== "string" || code.includes("\0"))
2527
2538
  throw new SandboxError("code must be a string with no NUL byte");
2528
2539
  const eff = timeoutS != null ? this._sbx._effTimeout(timeoutS) : this._timeout;
@@ -2681,6 +2692,7 @@ class Kernel {
2681
2692
  }
2682
2693
 
2683
2694
  _teardownResult(type, message, started, exitCode = -1) {
2695
+ this._death = type === null ? "the code crashed" : type;
2684
2696
  this._kill();
2685
2697
  // Same rule as the one-shot path: a box that never STARTED (the kernel failed to boot) throws, it
2686
2698
  // does not return a hollow result. timeout/killed stay as data on the returned result.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kern-sandbox",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "kern is a fast, rootless sandbox and virtual resource runtime for any workload, including untrusted and AI-generated code; kern-sandbox is its Node/TypeScript binding. Run untrusted or agent-generated code (Python/JS/Bash) in a real, kernel-enforced box in single-digit milliseconds, with no cloud, no account and no VM.",
5
5
  "keywords": [
6
6
  "sandbox",