Skip to main content

Stream stdout and stderr

def on_stdout(data: str) -> None:
    print(data, end="")

def on_stderr(data: str) -> None:
    print(data, end="")

result = sandbox.commands.run_streaming(
    "for i in 1 2 3; do echo tick-$i; sleep 1; done",
    shell=True,
    on_stdout=on_stdout,
    on_stderr=on_stderr,
)
print(result.exit_code)
Passing callbacks to sandbox.commands.run(...) without background=True uses the same streaming path:
result = sandbox.commands.run(
    "python3 -u script.py",
    shell=True,
    on_stdout=on_stdout,
    on_stderr=on_stderr,
)

Connect to a running process

process = sandbox.commands.connect(
    pid=1234,
    on_stdout=on_stdout,
    on_stderr=on_stderr,
)
result = process.wait()
print(result.exit_code)
Use connect(pid) for background processes that are still running. It attaches to the exec WebSocket and waits for the process exit event when possible.

Live background streaming

worker = sandbox.commands.run(
    "python3 -u worker.py",
    shell=True,
    background=True,
    on_stdout=on_stdout,
    on_stderr=on_stderr,
)

worker.disconnect()
Combining background=True with callbacks starts the command through the exec WebSocket and returns a BackgroundCommand after an exec_started message. The CLI equivalent is nullspace sandbox process stream with --cwd, --env, and --timeout flags.

Behavior

Streaming uses GET /v1/sandboxes/{id}/ws?channel=exec. SDK callers send exec_start for new commands or exec_connect for existing PIDs and then read exec_started, exec_stdout, exec_stderr, exec_exit, and error messages. Use the REST get_logs endpoint when you only need logs after the process has started.