You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CLAUDE.md
+8Lines changed: 8 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,6 +12,9 @@
12
12
13
13
- every publishable package must include a `README.md` with the standard format: title, tagline, and links to website, docs, and GitHub
14
14
- if `package.json` has a `"files"` array, `"README.md"` must be listed in it
15
+
-**no hardcoded monorepo/pnpm paths** — NEVER resolve dependencies at runtime using hardcoded relative paths into `node_modules/.pnpm/` or monorepo-relative `../../../node_modules/` walks; use `createRequire(import.meta.url).resolve("pkg/path")` or standard Node module resolution instead
16
+
-**no phantom transitive dependencies** — if published runtime code calls `require.resolve("foo")` or `import("foo")`, `foo` MUST be declared in that package's `dependencies` (not just available transitively in the monorepo)
17
+
-**`files` array must cover all runtime references** — if compiled `dist/` code resolves paths outside `dist/` at runtime (e.g., `../src/polyfills/`), those directories MUST be listed in the `"files"` array; verify with `pnpm pack --json` or `npm pack --dry-run` before publishing
15
18
16
19
## Testing Policy
17
20
@@ -24,6 +27,11 @@
24
27
- real-provider NodeRuntime CLI/tool tests that need a mutable temp worktree must pair `moduleAccess` with a real host-backed base filesystem such as `new NodeFileSystem()`; `moduleAccess` alone makes projected packages readable but leaves sandbox tools unable to touch `/tmp` working files
25
28
- e2e-docker fixtures connect to real Docker containers (Postgres, MySQL, Redis, SSH/SFTP) — skip gracefully via `skipUnlessDocker()` when Docker is unavailable
26
29
- interactive/PTY tests must use `kernel.openShell()` with `@xterm/headless`, not host PTY via `script -qefc`
30
+
- before fixing a reported runtime, CLI, SDK, or PTY bug, first reproduce the broken state and capture the exact visible output (stdout, stderr, event payloads, or terminal screen) in a regression or work note; do not start by guessing at the fix
31
+
- terminal-output and PTY-rendering bugs must use snapshot-style assertions against exact strings or exact screen contents under fixed rows/cols, not loose substring checks
32
+
- if expected terminal behavior is unclear, run the same flow on the host as a control and compare the sandbox transcript/screen against that host output before deciding what to fix
33
+
- be liberal with structured debug logging for complex interactive or long-running sessions so later manual repros can be diagnosed from artifacts instead of memory
34
+
- debug logging for complex sessions should go to a separate sink that does not contaminate stdout/stderr protocol output; prefer structured `pino` logs with enough context to reconstruct process lifecycle, PTY events, command routing, and failures, while redacting secrets
27
35
- kernel blocking-I/O regressions should be proven through `packages/core/test/kernel/kernel-integration.test.ts` using real process-owned FDs via `KernelInterface` (`fdWrite`, `flock`, `fdPollWait`) rather than only manager-level unit tests
28
36
- inode-lifetime/deferred-unlink kernel integration tests must use `InMemoryFileSystem` (or another inode-aware VFS) and await the kernel's POSIX-dir bootstrap; the default `createTestKernel()``TestFileSystem` does not exercise inode-backed FD lifetime semantics
29
37
- kernel signal-handler regressions should use a real spawned PID plus `KernelInterface.processTable` / `KernelInterface.socketTable`; unit `ProcessTable` coverage alone does not prove pending delivery or `SA_RESTART` behavior through the live kernel
|`loopbackExemptPorts`|`number[]`| Loopback ports that bypass SSRF checks (with `useDefaultNetwork`). |
174
175
|`processConfig`|`ProcessConfig`| Process metadata (cwd, env, argv, etc.). |
175
176
|`osConfig`|`OSConfig`| OS metadata (platform, arch, homedir, etc.). |
176
177
@@ -268,6 +269,26 @@ Each field accepts a `PermissionCheck`, which is either a boolean or a function
268
269
269
270
---
270
271
272
+
## Execution Methods
273
+
274
+
### `runtime.exec()`
275
+
276
+
Process-style execution. Accepts per-call environment, working directory, stdin, and stdio hooks. Use for automation loops, output observation, and CLI-style integrations.
The default network adapter blocks all loopback/private-IP requests as SSRF protection. To allow sandbox code to call a host-side RPC server on specific loopback ports, use `loopbackExemptPorts`:
Copy file name to clipboardExpand all lines: docs/features/output-capture.mdx
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,6 +10,8 @@ icon: "message-lines"
10
10
11
11
Console output from sandboxed code is **not buffered** into result fields. `exec()` and `run()` do not return `stdout` or `stderr`. Use the `onStdio` hook to capture output.
12
12
13
+
The per-execution `onStdio` option is available on `exec()` only. To capture output from `run()` calls, set a runtime-level hook when creating the `NodeRuntime` (see [Default hook](#default-hook) below).
Copy file name to clipboardExpand all lines: docs/quickstart.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -43,7 +43,7 @@ icon: "rocket"
43
43
</Step>
44
44
45
45
<Steptitle="Run code">
46
-
Use `runtime.run()` to execute JavaScript and get back exported values. Use `runtime.exec()` for scripts that produce console output.
46
+
Use `runtime.run()` to execute JavaScript and get back exported values. Use `runtime.exec()` for process-style execution with stdout/stderr observation, per-call environment overrides, and automation loops.
|**Per-call `onStdio`**| Yes | No (use runtime-level hook) |
84
+
|**Per-call `env` / `cwd` / `stdin`**| Yes | No |
85
+
|**Best for**| Side effects, CLI-style output, automation loops | Getting computed values back into the host |
86
+
87
+
### When to use `exec()`
88
+
89
+
Use `exec()` when sandboxed code produces **output you need to observe** or when you need per-call control over the execution environment. This is the right choice for AI SDK tool loops, code interpreters, and any integration where the result is communicated through `console.log` rather than `export`.
90
+
91
+
```ts
92
+
// AI SDK tool loop — capture stdout from each step
93
+
for (const step oftoolSteps) {
94
+
const result =awaitruntime.exec(step.code, {
95
+
onStdio: (e) =>appendToToolResult(e.message),
96
+
env: { API_KEY: step.apiKey },
97
+
cwd: "/workspace",
98
+
});
99
+
if (result.code!==0) handleError(result);
100
+
}
75
101
```
76
102
77
-
Use `run()` when you need a value back. The sandboxed code should use `export default`.
103
+
### When to use `run()`
104
+
105
+
Use `run()` when sandboxed code **exports a value** you need in the host. The sandbox code uses `export default` or named exports, and the host reads them from `result.exports`.
78
106
79
107
```ts
108
+
// Evaluate a user-provided expression and get the result
80
109
const result =awaitruntime.run<{ default:number }>("export default 40 + 2");
81
110
console.log(result.exports?.default); // 42
82
111
```
83
112
113
+
<Tip>
114
+
If you find yourself parsing `console.log` output to extract a value, switch to `run()` with an `export`. If you need to watch a stream of output lines, switch to `exec()` with `onStdio`.
115
+
</Tip>
116
+
84
117
## Capturing output
85
118
86
119
Console output is not buffered into the result. Use the `onStdio` hook to capture it.
87
120
121
+
The per-execution `onStdio` option is available on `exec()` only. To capture output from `run()`, set a runtime-level hook:
A single `NodeRuntime` instance is designed to be reused across many `.exec()` and `.run()` calls. Each call creates a fresh V8 isolate session internally, so per-execution state (module cache, budgets) is automatically reset while the underlying V8 process is reused efficiently.
142
+
143
+
```ts
144
+
// Recommended: create once, call many times, dispose at the end
// AI SDK tool loop — each step reuses the same runtime
151
+
for (const step oftoolSteps) {
152
+
const result =awaitruntime.exec(step.code, {
153
+
onStdio: (e) =>log(e.message),
154
+
});
155
+
}
156
+
157
+
// Clean up when the session is over
158
+
runtime.dispose();
94
159
```
95
160
161
+
Do **not** dispose and recreate the runtime between sequential calls. Calling `.exec()` or `.run()` on a disposed runtime throws `"NodeExecutionDriver has been disposed"`.
162
+
163
+
`dispose()` is synchronous and immediate — it kills active child processes and clears timers. Use `terminate()` (async) when you need to wait for graceful HTTP server shutdown before cleanup.
164
+
96
165
## TypeScript workflows
97
166
98
167
`NodeRuntime` executes JavaScript only. For sandboxed TypeScript type checking or compilation, use the separate `@secure-exec/typescript` package. See [TypeScript support](#typescript-support).
Use `exec()` for automation loops, CLI-style output capture, and per-call environment overrides. Use `run()` when the sandbox should return a value via `export`. See [exec vs run](/runtimes/node#exec-vs-run) for the full comparison.
86
+
85
87
## Capture output
86
88
87
89
Console output is not buffered by default. Use the `onStdio` hook to capture it:
0 commit comments