API reference

ExecHandle

The object returned by sb.exec() and sb.execCode() — PromiseLike<ExecResult> with pipe(), stdout(), and result().

import type { ExecHandle, ExecResult } from "@alineo-labs/sandbox";

ExecHandle is returned by sb.exec() and sb.execCode(). It implements PromiseLike<ExecResult> — you can await it directly, pipe it, or consume it as an async generator.

Consumption modes

await (direct)

const { stdout, stderr, exitCode } = await sb.exec("node --version");

Resolves after the command completes. stdout is the full buffered stdout string.

pipe()

await sb.exec("npm run build").pipe(process.stdout);

Streams stdout chunks to any writable with a write(chunk: string) method. Resolves when the command completes.

stdout()

for await (const chunk of sb.exec("npm test").stdout()) {
  process.stdout.write(chunk);
}

Async generator yielding stdout chunks as they arrive.

result()

const handle = sb.exec("npm test");
// ... other work ...
const { stdout, stderr, exitCode } = await handle.result();

Explicit promise form. Equivalent to await handle.

ExecResult

interface ExecResult {
  stdout: string; // full stdout as a string
  stderr: string; // full stderr as a string
  exitCode: number; // process exit code (0 = success)
}

Strict mode

By default, exec() throws CommandError if exitCode !== 0 after the handle resolves. Pass { strict: false } to get the result instead:

const { exitCode } = await sb.exec("test -f /etc/hosts", { strict: false });

See Error handling for more on CommandError.

Multiple consumers

An ExecHandle can only be consumed once — the underlying stream starts draining as soon as sb.exec()/sb.execCode() is called, in the ExecHandle constructor, whether or not anything ever consumes it. All consumption modes (pipe, stdout, await, result) share the same internal chunk buffer.

const handle = sb.exec("long-running-command");

// These share the same stream — start them before any awaiting
void handle.pipe(process.stdout); // stream as it runs
const { exitCode } = await handle.result(); // get result when done

In the workflow builder

The SandboxBuilder in @alineo-labs/workflow queues exec() calls but doesn't return ExecHandle instances — ops are executed during flush. Use readFile(path, as) to capture values into vars:

const { vars } = await workflow(client)
  .sandbox({ image: "node:22", resources: { cpu: "500m", memory: "256Mi" } }, (sb) => {
    sb.exec("node --version > /tmp/ver.txt");
    sb.readFile("/tmp/ver.txt", "version");
  })
  .result();

console.log(vars.version);