Process & Code Execution
exec runs a command, buffers its full output, and returns when the command exits. Use it for short, non-interactive commands.
execStream opens a live WebSocket connection that streams stdout/stderr in real time, accepts stdin, and optionally allocates a pseudo-terminal (PTY) for interactive programs like bash, vim, or top.
When to use which
Section titled “When to use which”| Use case | Method |
|---|---|
ls, git status, anything short and non-interactive | exec |
npm install, make, anything where you want live output | execStream |
bash, python -i, vim, anything interactive | execStream with tty: true |
| Multi-gigabyte output | execStream |
One-shot exec
Section titled “One-shot exec”const result = await sandbox.exec({ command: 'echo hello' })console.log(result.stdout) // "hello\n"console.log(result.exitCode)
// With working directory and environment variablesconst result2 = await sandbox.exec({ command: 'node index.js', workDir: '/workspace', env: { NODE_ENV: 'production' }, timeoutSeconds: 30,})result = sandbox.exec_command('echo hello')print(result['stdout']) # "hello\n"print(result['exit_code'])
# With optionsresult2 = sandbox.exec_command('node index.js', workdir='/workspace', env={'NODE_ENV': 'production'})result, err := sandbox.ExecCommand(ctx, "echo hello")if err != nil { log.Fatal(err) }fmt.Println(result.Stdout)
// With full optionsresult2, err := sandbox.Exec(ctx, sdktypes.ExecOptions{ Command: "node index.js", Workdir: "/workspace", Env: map[string]string{"NODE_ENV": "production"}, TimeoutSeconds: 30,})let result = sandbox.exec(aerolvm_sdk::ExecRequest { command: "echo hello".to_string(), ..Default::default()})?;println!("{}", result.stdout);
// With optionslet result2 = sandbox.exec(aerolvm_sdk::ExecRequest { command: "node index.js".to_string(), work_dir: Some("/workspace".to_string()), timeout_seconds: Some(30), ..Default::default()})?;ExecResult result = sandbox.exec(new ExecOptions().setCommand("echo hello"));System.out.println(result.stdout); // "hello\n"System.out.println(result.exitCode);
// With optionsExecResult result2 = sandbox.exec( new ExecOptions() .setCommand("node index.js") .setWorkDir("/workspace") .setEnv(Map.of("NODE_ENV", "production")) .setTimeoutSeconds(30));Live streaming output
Section titled “Live streaming output”const handle = sandbox.execStream({ command: 'npm install', workdir: '/workspace', onStdout: chunk => process.stdout.write(chunk), onStderr: chunk => process.stderr.write(chunk),})
const { code } = await handle.doneconsole.log(`exited with ${code}`)import sys
handle = sandbox.exec_stream({ 'command': 'npm install', 'workdir': '/workspace', 'onStdout': lambda chunk: sys.stdout.buffer.write(chunk), 'onStderr': lambda chunk: sys.stderr.buffer.write(chunk),})
print(handle.wait()) # {'code': 0}handle, err := sandbox.ExecStream(ctx, sdktypes.ExecStreamOptions{ Command: "npm install", Workdir: "/workspace", OnStdout: func(chunk []byte) { os.Stdout.Write(chunk) }, OnStderr: func(chunk []byte) { os.Stderr.Write(chunk) },})if err != nil { log.Fatal(err) }
exit, err := handle.Wait()fmt.Println(exit.Code, exit.Signal)use std::sync::Arc;
let handle = sandbox.exec_stream(aerolvm_sdk::ExecStreamOptions { command: "npm install".to_string(), on_stdout: Some(Arc::new(|chunk| { print!("{}", String::from_utf8_lossy(&chunk)) })), ..Default::default()})?;
println!("{:?}", handle.wait()?);ExecStreamHandle handle = sandbox.execStream( new ExecStreamOptions() .setCommand("npm install") .setWorkDir("/workspace") .setOnStdout(chunk -> System.out.print(new String(chunk, StandardCharsets.UTF_8))));
ExecExitInfo exit = handle.waitForExit();System.out.println(exit.code);Interactive shell with PTY
Section titled “Interactive shell with PTY”In PTY mode, stdout and stderr are merged onto the terminal, so only onStdout fires.
const handle = sandbox.execStream({ command: 'bash', tty: true, cols: process.stdout.columns, rows: process.stdout.rows, onStdout: chunk => process.stdout.write(chunk),})
process.stdin.setRawMode?.(true)process.stdin.on('data', chunk => handle.write(chunk))
process.stdout.on('resize', () => { handle.resize(process.stdout.columns, process.stdout.rows)})
await handle.doneimport sys
handle = sandbox.exec_stream({ 'command': 'bash', 'tty': True, 'cols': 120, 'rows': 40, 'onStdout': lambda chunk: sys.stdout.buffer.write(chunk),})
handle.write('echo hello\n')print(handle.wait())handle, err := sandbox.ExecStream(ctx, sdktypes.ExecStreamOptions{ Command: "bash", TTY: true, Cols: 120, Rows: 40, OnStdout: func(chunk []byte) { fmt.Print(string(chunk)) },})if err != nil { log.Fatal(err) }
if err := handle.WriteString("echo hello\n"); err != nil { log.Fatal(err) }
exit, err := handle.Wait()fmt.Println(exit.Code, exit.Signal)use std::sync::Arc;
let handle = sandbox.exec_stream(aerolvm_sdk::ExecStreamOptions { command: "bash".to_string(), tty: true, cols: Some(120), rows: Some(40), on_stdout: Some(Arc::new(|chunk| { print!("{}", String::from_utf8_lossy(&chunk)) })), ..Default::default()})?;
handle.write_string("echo hello\n")?;println!("{:?}", handle.wait()?);ExecStreamHandle handle = sandbox.execStream( new ExecStreamOptions() .setCommand("bash") .setTty(true) .setCols(120) .setRows(40) .setOnStdout(chunk -> System.out.print(new String(chunk, StandardCharsets.UTF_8))));
handle.write("echo hello\n");ExecExitInfo exit = handle.waitForExit();System.out.println(exit.code);Sending signals
Section titled “Sending signals”handle.signal('INT') // Ctrl-Chandle.signal('TERM')handle.signal('KILL')handle.signal('INT')handle.signal('TERM')handle.Signal("INT")handle.Signal("TERM")handle.signal("INT")?;handle.signal("TERM")?;handle.signal("INT");handle.signal("TERM");Signals target the process group, so child processes receive them too.
For persistent interactive processes that survive reconnects, see Sessions.