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 doneIn 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);