Event stream
How ExecHandle streams stdout from the sandbox in real time — pipe, async generator, and await.
sb.exec() and sb.execCode() both return an ExecHandle. Under the hood, execd sends execution output as an SSE stream. ExecHandle wraps that stream and exposes three consumption modes.
Modes
Pipe
Send stdout directly to any writable with a write(chunk: string) method:
await sb.exec("npm run build").pipe(process.stdout);
await sb.exec("npm test").pipe(process.stderr);This is the most common mode. Output flows in real time as the command runs.
Async generator
Iterate stdout chunks one at a time:
for await (const chunk of sb.exec("npm run build").stdout()) {
process.stdout.write(chunk);
// or process each chunk (e.g. parse log lines)
}Use this when you want to inspect or transform output as it arrives.
Await
Await the handle directly to get the full result after the command completes:
const { stdout, stderr, exitCode } = await sb.exec("node --version");This buffers all stdout in memory and resolves once the command exits. Use when you need the full output as a string.
result()
.result() is an explicit promise form that resolves to { stdout, stderr, exitCode }:
const handle = sb.exec("npm test");
// ... do something else ...
const { exitCode } = await handle.result();ExecResult type
interface ExecResult {
stdout: string; // full stdout as a string
stderr: string; // full stderr as a string
exitCode: number; // process exit code
}Strict mode
By default, exec() throws CommandError if the exit code is non-zero (strict mode). Pass { strict: false } to get the ExecResult instead:
const { exitCode } = await sb.exec("test -f package.json", { strict: false });
if (exitCode !== 0) {
console.log("no package.json");
}What gets streamed
Only stdout is streamed via pipe() and the stdout() generator. Stderr is buffered separately and available via (await handle).stderr or .result().
The underlying SSE stream from execd also carries exit code information, which ExecHandle uses to populate exitCode in the result.