Skip to content

Execute a command in a running sandbox and return how it ended.

sandbox.exec(command: string | string[], options?: ExecOptions): Promise<ExecResult>
sandbox.exec(command: string | string[], options: ExecOptions & { background: true }): Promise<Command>

Examples

const result = await sandbox.exec(["echo", "hello"]);
const shell = await sandbox.exec("echo $GREETING from $(pwd)", { env: { GREETING: "hello" }, workdir: "/tmp" });
const sum = await sandbox.exec("wc -l", { stdin: "one\ntwo\n" });
const command = await sandbox.exec("sleep 5; echo done", { background: true });

Arguments

Argument Description
command a string runs under /bin/sh -c; an array or list runs as it is, with no shell

Options

TypeScript Python Description
env env variables added to the sandbox’s environment for this command
workdir workdir working directory
user user user name or uid in the sandbox
stdin stdin text or bytes the command reads, after which its input ends; true keeps the input open for writeStdin
tty tty run on a terminal: { rows, cols } in TypeScript, True or TerminalSize(rows, cols) in Python; all output arrives as stdout
signal an AbortSignal that ends this client’s wait
onStdout, onStderr on_stdout, on_stderr called with each chunk of output, as bytes, as it arrives
outputLimitBytes output_limit_bytes how many of the newest bytes of stdout and stderr together the result keeps; 8 MiB by default, 0 keeps none
background background return a Command as soon as the command is running

Result

The result holds exitCode, signal, stdout, stderr and lostBytes (exit_code, signal, stdout, stderr and lost_bytes in Python). Python also has stdout_bytes and stderr_bytes with the raw bytes.

A non-zero exit code comes back in the result and raises nothing. When a signal ends a command, every provider reports the exit code as 128 plus the signal’s number, such as 137 for KILL. A command that never started, for example because its binary does not exist, raises CommandNotStartedError.

Output

A result keeps the newest 8 MiB of stdout and stderr together, so the result of a long run may hold only the end of its output. The callbacks still see every chunk, including the ones past the limit. lostBytes counts something else: the output the daemon dropped before any client read it.

Ending the wait

An abort, an asyncio cancel, a Python timeout or a dropped connection ends only this client’s wait and releases the stream. The command keeps running until it exits or until command.kill.

Input

In Python, a foreground command takes its whole input as text or bytes, and a background command takes a bool: True keeps the input open for write_stdin. Any other mix raises TypeError.

In TypeScript, sandbox.commands.run() and sandbox.commands.start() do the same as exec() without and with background: true.

Wait for a background command to end and return how it ended.

command.wait(options?: { signal?: AbortSignal }): Promise<ExecResult>

Examples

const result = await command.wait();
const bounded = await command.wait({ signal: AbortSignal.timeout(30_000) });

Options

TypeScript Python Description
signal an AbortSignal that ends the wait with the signal’s reason
timeout seconds to wait before raising the built-in TimeoutError

Ending the wait never ends the command.

Send a signal to a background command.

command.kill(signal?: string): Promise<void>

Examples

await command.kill();
await command.kill("KILL");

Arguments

Argument Description
signal TERM by default, or KILL; the daemon refuses any other signal

Send input to a background command.

command.writeStdin(data: string | Uint8Array): Promise<void>

Examples

const cat = await sandbox.exec("cat", { background: true, stdin: true });
await cat.writeStdin("hello\n");
await cat.closeStdin();

The command must start with stdin set to true.

End the command’s input, so a command that reads to the end of its input can finish.

command.closeStdin(): Promise<void>

Examples

await command.closeStdin();

Change the terminal size of a command that runs on a terminal.

command.resize(rows: number, cols: number): Promise<void>

Examples

const top = await sandbox.exec("top", { background: true, tty: { rows: 24, cols: 80 } });
await top.resize(40, 120);

Read the command’s record from the daemon.

command.inspect(): Promise<CommandInfo>

Examples

const { state, exitCode } = await command.inspect();

A CommandInfo holds id, the sandbox ID, command, state (running or exited), the exit code, signal, the start and exit times, and the lost bytes. The exit code is null while the command runs.

Close the command’s stream and leave the command running.

command.disconnect(): void

Examples

command.disconnect();

The daemon keeps the command after this client lets go of it. wait or reconnect attaches again.

Attach to the command again.

command.reconnect(options?: { signal?: AbortSignal }): Promise<void>

Examples

await command.reconnect();
const result = await command.wait();

The daemon replays the output it still holds, so the result and the callbacks start over.

List the commands the daemon holds for the sandbox, running or ended.

sandbox.commands.list(): Promise<CommandInfo[]>

Examples

for (const info of await sandbox.commands.list()) console.log(info.id, info.state);

The list includes the commands any client started.

Find a command by ID, from any client, and return a handle to it.

sandbox.commands.get(id: string, options?: OutputOptions): Promise<Command>

Examples

const command = await sandbox.commands.get(id, { onStdout: (chunk) => process.stdout.write(chunk) });
const result = await command.wait();

Options

TypeScript Python Description
onStdout, onStderr on_stdout, on_stderr called with each chunk of output as it arrives
outputLimitBytes output_limit_bytes how many of the newest bytes the result keeps

The handle attaches on wait, so the callbacks see the output the daemon replays.