sandboxedjs 0.1.73 → 0.1.74

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
@@ -165,6 +165,17 @@ await session.run("echo $TOKEN in $(pwd)"); // → abc in /app
165
165
 
166
166
  ### Long-running processes
167
167
 
168
+ A job put in the background with `&` keeps running after the command that started it returns,
169
+ even from a stateless `exec`, and stops with `kill %N` in a session or when the container is
170
+ disposed:
171
+
172
+ ```ts
173
+ await box.exec("node server.js > /tmp/server.log 2>&1 &", { cwd: "/app" });
174
+ await box.waitForPort(3000);
175
+ ```
176
+
177
+ To hold the process yourself, spawn it:
178
+
168
179
  ```ts
169
180
  const proc = box.spawn("node server.js", { cwd: "/app" });
170
181
 
@@ -545,7 +556,10 @@ The container has no access to your filesystem, environment, or network unless y
545
556
  - The filesystem is entirely in memory. Code inside cannot read or write a host path — there is
546
557
  no `/Users`, no `/home/you`, no way to reach one.
547
558
  - Outbound network access is **off by default**; `curl https://…` fails until you pass
548
- `network: { allowOutbound: true }`, optionally narrowed with `allowedHosts`.
559
+ `network: { allowOutbound: true }`, optionally narrowed with `allowedHosts`. The same policy
560
+ binds a program's own `fetch`, `http`, `https` and `WebSocket`: a refused request fails with
561
+ `ENETUNREACH`. `localhost` and `127.0.0.1` always mean the container's own servers — never the
562
+ host's.
549
563
  - Host files enter only through `files`, `mount()` or `copyIn()`, and leave only through
550
564
  `copyOut()` or `fs.readFile()`.
551
565
  - `timeoutMs` bounds runaway commands, and `exec` settles even when a process ignores its kill
@@ -1000,7 +1014,16 @@ Honest list of what does not work:
1000
1014
  through is to answer **No** to a prompt like `npm create vite`'s "Install with npm and start
1001
1015
  now?" and run `npm install && npm run dev` from the shell instead.
1002
1016
  - **No `net`, `tls`, `worker_threads` or `vm`.** `http` and `https` are served by a virtual stack
1003
- that `request()` talks to directly, so servers work; raw sockets do not.
1017
+ that `request()` talks to directly, so servers work; raw sockets do not. A program can reach
1018
+ servers anywhere in the container over HTTP (`http.get`, `fetch`), but not open a `WebSocket` to
1019
+ one.
1020
+ - **`node:test` covers what test files use** — `test`/`it`, `describe`, hooks, subtests, `skip`,
1021
+ `todo`, `only`, `mock.fn` and `mock.method`, with `spec` and `tap` reports — and `node --test`
1022
+ finds and runs test files as Node 22 does. `run()`, coverage and mock timers are not
1023
+ implemented. A test file exits when its tests finish, as under `--test-force-exit`.
1024
+ - **On the in-realm runtime, programs share one global object.** A global one program sets is
1025
+ visible to the next. The worker runtime (the default where shared memory is available) gives
1026
+ each program its own.
1004
1027
  - **Python is source-built CPython/WASM.** Each program gets its own interpreter
1005
1028
  process worker. Pure-Python wheels install normally; native extensions must
1006
1029
  be linked or published for Emscripten. The bundled wheel index includes the
package/dist/agent.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as Container } from './container-BxEoVOmX.cjs';
2
- import './contracts-CVgctitO.cjs';
1
+ import { C as Container } from './container-DAWPIkGG.cjs';
2
+ import './contracts-C2_dTgk3.cjs';
3
3
 
4
4
  /**
5
5
  * Structural copies of the LangChain Deep Agents backend contract.
package/dist/agent.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as Container } from './container-B5X6stfx.js';
2
- import './contracts-CVgctitO.js';
1
+ import { C as Container } from './container-CsLNpeyi.js';
2
+ import './contracts-C2_dTgk3.js';
3
3
 
4
4
  /**
5
5
  * Structural copies of the LangChain Deep Agents backend contract.
@@ -1,4 +1,4 @@
1
- import { V as Vfs, C as Cred, f as RuntimePod, D as DirEntry, p as Stats } from './contracts-CVgctitO.js';
1
+ import { V as Vfs, C as Cred, f as RuntimePod, O as OutboundPolicy, D as DirEntry, p as Stats } from './contracts-C2_dTgk3.js';
2
2
 
3
3
  /**
4
4
  * Byte streams for stdin/stdout/stderr, pipelines and redirections.
@@ -487,6 +487,8 @@ declare class NetworkStack {
487
487
  timeoutMs?: number;
488
488
  intervalMs?: number;
489
489
  }): Promise<boolean>;
490
+ /** The policy every way out of the container applies, not only the shell's. */
491
+ get policy(): OutboundPolicy;
490
492
  outboundAllowed(url: string): boolean;
491
493
  procNetDev(): string;
492
494
  procNetRoute(): string;
@@ -979,8 +981,13 @@ declare class Shell {
979
981
  captureSubshell(command: string): Promise<string>;
980
982
  /** `<(cmd)` — run the command now and hand back a path holding its output. */
981
983
  private makeProcessSubstitution;
982
- /** A copy that shares nothing mutable with this shell. */
983
- fork(): Shell;
984
+ /**
985
+ * A copy that shares nothing mutable with this shell.
986
+ *
987
+ * Subshells and pipeline stages run as part of this shell's process. A
988
+ * background job passes a process of its own, the way fork(2) gives one.
989
+ */
990
+ fork(proc?: Process): Shell;
984
991
  private currentIO;
985
992
  run(node: Node, io: ShellIO): Promise<number>;
986
993
  private runList;
@@ -1543,6 +1550,14 @@ declare class Container {
1543
1550
  }): Session;
1544
1551
  /** The container-wide session used by `shell()` shorthand helpers. */
1545
1552
  get shell(): Session;
1553
+ /**
1554
+ * The streams one call collects into.
1555
+ *
1556
+ * They are detachable because a background job started by the command keeps
1557
+ * writing after the call has returned. Nothing reads the result any more by
1558
+ * then, so without `detach` a chatty server would grow the buffer for as long
1559
+ * as it ran; afterwards its output still reaches the container-wide taps.
1560
+ */
1546
1561
  private makeStdio;
1547
1562
  /** Send an HTTP request to a server running inside the container. */
1548
1563
  request(port: number, init?: {
@@ -1,4 +1,4 @@
1
- import { V as Vfs, C as Cred, f as RuntimePod, D as DirEntry, p as Stats } from './contracts-CVgctitO.cjs';
1
+ import { V as Vfs, C as Cred, f as RuntimePod, O as OutboundPolicy, D as DirEntry, p as Stats } from './contracts-C2_dTgk3.cjs';
2
2
 
3
3
  /**
4
4
  * Byte streams for stdin/stdout/stderr, pipelines and redirections.
@@ -487,6 +487,8 @@ declare class NetworkStack {
487
487
  timeoutMs?: number;
488
488
  intervalMs?: number;
489
489
  }): Promise<boolean>;
490
+ /** The policy every way out of the container applies, not only the shell's. */
491
+ get policy(): OutboundPolicy;
490
492
  outboundAllowed(url: string): boolean;
491
493
  procNetDev(): string;
492
494
  procNetRoute(): string;
@@ -979,8 +981,13 @@ declare class Shell {
979
981
  captureSubshell(command: string): Promise<string>;
980
982
  /** `<(cmd)` — run the command now and hand back a path holding its output. */
981
983
  private makeProcessSubstitution;
982
- /** A copy that shares nothing mutable with this shell. */
983
- fork(): Shell;
984
+ /**
985
+ * A copy that shares nothing mutable with this shell.
986
+ *
987
+ * Subshells and pipeline stages run as part of this shell's process. A
988
+ * background job passes a process of its own, the way fork(2) gives one.
989
+ */
990
+ fork(proc?: Process): Shell;
984
991
  private currentIO;
985
992
  run(node: Node, io: ShellIO): Promise<number>;
986
993
  private runList;
@@ -1543,6 +1550,14 @@ declare class Container {
1543
1550
  }): Session;
1544
1551
  /** The container-wide session used by `shell()` shorthand helpers. */
1545
1552
  get shell(): Session;
1553
+ /**
1554
+ * The streams one call collects into.
1555
+ *
1556
+ * They are detachable because a background job started by the command keeps
1557
+ * writing after the call has returned. Nothing reads the result any more by
1558
+ * then, so without `detach` a chatty server would grow the buffer for as long
1559
+ * as it ran; afterwards its output still reaches the container-wide taps.
1560
+ */
1546
1561
  private makeStdio;
1547
1562
  /** Send an HTTP request to a server running inside the container. */
1548
1563
  request(port: number, init?: {
@@ -347,6 +347,27 @@ declare class VirtualTcpNetwork {
347
347
  closeAll(): void;
348
348
  }
349
349
 
350
+ /**
351
+ * The outbound network policy, as plain functions every client consults.
352
+ *
353
+ * The container has several ways out — `curl` and `wget` in the shell, `http`,
354
+ * `https`, `fetch` and `WebSocket` in a Node program, sockets in Python — and
355
+ * they used to decide separately. Only the shell asked: a Node program's
356
+ * `fetch("https://…")` reached the internet from a container booted with
357
+ * outbound access off. One policy, applied at each exit, is what makes
358
+ * `network: { allowOutbound: false }` mean what it says.
359
+ *
360
+ * Loopback is not "outbound" at all. `127.0.0.1` inside the container is the
361
+ * container, so those requests are routed to its own servers and never handed
362
+ * to the host's network stack, whatever the policy allows.
363
+ */
364
+ interface OutboundPolicy {
365
+ /** Whether requests may leave the container at all. */
366
+ allowOutbound: boolean;
367
+ /** When outbound is on, the hosts it may reach (subdomains included). `null` means any. */
368
+ allowedHosts: string[] | null;
369
+ }
370
+
350
371
  /**
351
372
  * Clean-room contracts between SandboxedJS and its JavaScript runtime.
352
373
  *
@@ -479,9 +500,17 @@ interface RuntimePod {
479
500
  headers: Record<string, string>;
480
501
  body: Uint8Array;
481
502
  }) => Promise<RuntimeHttpResponse>): () => void;
503
+ /**
504
+ * Apply the container's outbound policy to programs this pod runs.
505
+ *
506
+ * Optional so a pod written elsewhere still satisfies the contract, but a pod
507
+ * without it cannot keep a program's own `fetch` inside the policy — only
508
+ * the shell's `curl` would honour it.
509
+ */
510
+ setNetworkPolicy?(policy: OutboundPolicy): void;
482
511
  snapshot(options?: Record<string, unknown>): unknown;
483
512
  restore(snapshot: unknown, options?: Record<string, unknown>): Promise<void>;
484
513
  teardown(): void;
485
514
  }
486
515
 
487
- export { type Cred as C, type DirEntry as D, type RuntimeVolume as R, type SpawnChild as S, Vfs as V, type WriteOptions as W, VirtualTcpNetwork as a, type RuntimeHttpResponse as b, type SyncSpawn as c, type VolumeStat as d, type VolumeStats as e, type RuntimePod as f, type RuntimePackageInstaller as g, type ChildSpawnConfig as h, type ChildHandle as i, type RuntimeProcess as j, type RuntimeSocketPeer as k, type RuntimeConnection as l, ROOT_CRED as m, type RuntimeProcessManager as n, type RuntimeProcessResult as o, Stats as p, type VirtualNode as q, type VirtualProvider as r, applyChmod as s, createChildProcessModule as t, formatMode as u, makeCred as v, octalMode as w, parseUmask as x };
516
+ export { type Cred as C, type DirEntry as D, type OutboundPolicy as O, type RuntimeVolume as R, type SpawnChild as S, Vfs as V, type WriteOptions as W, VirtualTcpNetwork as a, type RuntimeHttpResponse as b, type SyncSpawn as c, type VolumeStat as d, type VolumeStats as e, type RuntimePod as f, type RuntimePackageInstaller as g, type ChildSpawnConfig as h, type ChildHandle as i, type RuntimeProcess as j, type RuntimeSocketPeer as k, type RuntimeConnection as l, ROOT_CRED as m, type RuntimeProcessManager as n, type RuntimeProcessResult as o, Stats as p, type VirtualNode as q, type VirtualProvider as r, applyChmod as s, createChildProcessModule as t, formatMode as u, makeCred as v, octalMode as w, parseUmask as x };
@@ -347,6 +347,27 @@ declare class VirtualTcpNetwork {
347
347
  closeAll(): void;
348
348
  }
349
349
 
350
+ /**
351
+ * The outbound network policy, as plain functions every client consults.
352
+ *
353
+ * The container has several ways out — `curl` and `wget` in the shell, `http`,
354
+ * `https`, `fetch` and `WebSocket` in a Node program, sockets in Python — and
355
+ * they used to decide separately. Only the shell asked: a Node program's
356
+ * `fetch("https://…")` reached the internet from a container booted with
357
+ * outbound access off. One policy, applied at each exit, is what makes
358
+ * `network: { allowOutbound: false }` mean what it says.
359
+ *
360
+ * Loopback is not "outbound" at all. `127.0.0.1` inside the container is the
361
+ * container, so those requests are routed to its own servers and never handed
362
+ * to the host's network stack, whatever the policy allows.
363
+ */
364
+ interface OutboundPolicy {
365
+ /** Whether requests may leave the container at all. */
366
+ allowOutbound: boolean;
367
+ /** When outbound is on, the hosts it may reach (subdomains included). `null` means any. */
368
+ allowedHosts: string[] | null;
369
+ }
370
+
350
371
  /**
351
372
  * Clean-room contracts between SandboxedJS and its JavaScript runtime.
352
373
  *
@@ -479,9 +500,17 @@ interface RuntimePod {
479
500
  headers: Record<string, string>;
480
501
  body: Uint8Array;
481
502
  }) => Promise<RuntimeHttpResponse>): () => void;
503
+ /**
504
+ * Apply the container's outbound policy to programs this pod runs.
505
+ *
506
+ * Optional so a pod written elsewhere still satisfies the contract, but a pod
507
+ * without it cannot keep a program's own `fetch` inside the policy — only
508
+ * the shell's `curl` would honour it.
509
+ */
510
+ setNetworkPolicy?(policy: OutboundPolicy): void;
482
511
  snapshot(options?: Record<string, unknown>): unknown;
483
512
  restore(snapshot: unknown, options?: Record<string, unknown>): Promise<void>;
484
513
  teardown(): void;
485
514
  }
486
515
 
487
- export { type Cred as C, type DirEntry as D, type RuntimeVolume as R, type SpawnChild as S, Vfs as V, type WriteOptions as W, VirtualTcpNetwork as a, type RuntimeHttpResponse as b, type SyncSpawn as c, type VolumeStat as d, type VolumeStats as e, type RuntimePod as f, type RuntimePackageInstaller as g, type ChildSpawnConfig as h, type ChildHandle as i, type RuntimeProcess as j, type RuntimeSocketPeer as k, type RuntimeConnection as l, ROOT_CRED as m, type RuntimeProcessManager as n, type RuntimeProcessResult as o, Stats as p, type VirtualNode as q, type VirtualProvider as r, applyChmod as s, createChildProcessModule as t, formatMode as u, makeCred as v, octalMode as w, parseUmask as x };
516
+ export { type Cred as C, type DirEntry as D, type OutboundPolicy as O, type RuntimeVolume as R, type SpawnChild as S, Vfs as V, type WriteOptions as W, VirtualTcpNetwork as a, type RuntimeHttpResponse as b, type SyncSpawn as c, type VolumeStat as d, type VolumeStats as e, type RuntimePod as f, type RuntimePackageInstaller as g, type ChildSpawnConfig as h, type ChildHandle as i, type RuntimeProcess as j, type RuntimeSocketPeer as k, type RuntimeConnection as l, ROOT_CRED as m, type RuntimeProcessManager as n, type RuntimeProcessResult as o, Stats as p, type VirtualNode as q, type VirtualProvider as r, applyChmod as s, createChildProcessModule as t, formatMode as u, makeCred as v, octalMode as w, parseUmask as x };