> ## Documentation Index
> Fetch the complete documentation index at: https://docs.declaw.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Commands

> Run, stream, list, kill, and interact with processes inside a Declaw sandbox using sbx.commands.

```python theme={null}
from declaw import Sandbox, CommandResult, CommandHandle, ProcessInfo
```

`sbx.commands` is the `Commands` sub-module available on every `Sandbox` instance. It provides methods to run foreground commands, launch background processes, stream real-time output, and interact with running processes via stdin.

## `sbx.commands.run()`

Run a command and block until it completes, returning its stdout, stderr, and exit code.

```python theme={null}
result = sbx.commands.run("echo hello")
print(result.stdout)   # "hello\n"
print(result.exit_code)  # 0
```

Pass `background=True` to launch the command in the background and receive a `CommandHandle` immediately.

```python theme={null}
handle: CommandHandle = sbx.commands.run("sleep 30", background=True)
print("PID:", handle.pid)
```

<ParamField body="cmd" type="str" required>
  Shell command to execute inside the sandbox.
</ParamField>

<ParamField body="background" type="bool" default="False">
  When `True`, the API returns immediately and the method returns a
  `CommandHandle`. When `False` (default), the method blocks and returns a
  `CommandResult`.
</ParamField>

<ParamField body="envs" type="dict[str, str] | None" default="None">
  Additional environment variables for this command.
</ParamField>

<ParamField body="user" type="str" default="'user'">
  Unix user to run the command as inside the sandbox.
</ParamField>

<ParamField body="cwd" type="str | None" default="None">
  Working directory. Defaults to the user's home directory.
</ParamField>

<ParamField body="on_stdout" type="Callable[[str], None] | None" default="None">
  Callback invoked for each line of stdout **after** the command completes.
  For real-time streaming, use `run_stream()` instead.
</ParamField>

<ParamField body="on_stderr" type="Callable[[str], None] | None" default="None">
  Callback invoked for each line of stderr after the command completes.
</ParamField>

<ParamField body="stdin" type="bool | None" default="None">
  Whether to attach a stdin pipe to the process.
</ParamField>

<ParamField body="timeout" type="float | None" default="60">
  Maximum time in seconds to wait for the command to complete.
</ParamField>

<ParamField body="request_timeout" type="float | None" default="None">
  Per-request HTTP timeout in seconds.
</ParamField>

**Returns** `CommandResult` when `background=False`, `CommandHandle` when `background=True`.

***

## `sbx.commands.run_stream()`

Run a command with real-time SSE streaming. Callbacks are invoked as each chunk of output arrives, before the command completes.

```python theme={null}
def on_line(line: str) -> None:
    print(line, end="", flush=True)

result = sbx.commands.run_stream(
    "python3 -u long_script.py",
    on_stdout=on_line,
    on_stderr=on_line,
)
print("Exit code:", result.exit_code)
```

<ParamField body="cmd" type="str" required>
  Shell command to execute.
</ParamField>

<ParamField body="on_stdout" type="Callable[[str], None] | None" default="None">
  Called in real-time for each stdout chunk as it arrives from the SSE stream.
</ParamField>

<ParamField body="on_stderr" type="Callable[[str], None] | None" default="None">
  Called in real-time for each stderr chunk as it arrives.
</ParamField>

<ParamField body="envs" type="dict[str, str] | None" default="None">
  Environment variables for the command.
</ParamField>

<ParamField body="user" type="str" default="'user'">
  Unix user to run as.
</ParamField>

<ParamField body="cwd" type="str | None" default="None">
  Working directory.
</ParamField>

<ParamField body="timeout" type="float | None" default="60">
  Command execution timeout in seconds.
</ParamField>

**Returns** `CommandResult` with the accumulated stdout and stderr after the command finishes.

***

## `sbx.commands.list()`

List all running (background) processes in the sandbox.

```python theme={null}
processes: list[ProcessInfo] = sbx.commands.list()
for p in processes:
    print(p.pid, p.cmd)
```

<ParamField body="request_timeout" type="float | None" default="None">
  Per-request HTTP timeout in seconds.
</ParamField>

**Returns** `list[ProcessInfo]`

***

## `sbx.commands.kill()`

Send SIGKILL to a running process by PID.

```python theme={null}
killed = sbx.commands.kill(pid=1234)
```

<ParamField body="pid" type="int" required>
  Process ID of the command to kill.
</ParamField>

<ParamField body="request_timeout" type="float | None" default="None">
  Per-request HTTP timeout in seconds.
</ParamField>

**Returns** `bool` — `True` if the process was killed, `False` if it was already dead.

***

## `sbx.commands.send_stdin()`

Write data to the stdin of a running background process.

```python theme={null}
handle = sbx.commands.run("cat", background=True)
sbx.commands.send_stdin(handle.pid, "hello\n")
sbx.commands.send_stdin(handle.pid, "world\n")
```

<ParamField body="pid" type="int" required>
  Process ID of the running command.
</ParamField>

<ParamField body="data" type="str" required>
  Data to write to stdin. Include `\n` for newlines.
</ParamField>

<ParamField body="request_timeout" type="float | None" default="None">
  Per-request HTTP timeout in seconds.
</ParamField>

**Returns** `None`

***

## `sbx.commands.connect()`

Create a `CommandHandle` for an already-running process by PID without making
an API call. Useful when you have a PID from `list()` and want to wait on it.

```python theme={null}
handle = sbx.commands.connect(pid=5678)
result = handle.wait()
```

<ParamField body="pid" type="int" required>
  Process ID of the running command.
</ParamField>

**Returns** `CommandHandle`

***

## CommandHandle

`CommandHandle` is returned by `sbx.commands.run(background=True)` and `sbx.commands.connect()`. It provides methods to wait for or kill the process.

```python theme={null}
handle = sbx.commands.run("python3 script.py", background=True)

# Later, wait for it to finish
result = handle.wait(
    on_stdout=lambda line: print("OUT:", line),
    on_stderr=lambda line: print("ERR:", line),
)
```

### `handle.wait()`

Wait for the background command to complete.

<ParamField body="on_stdout" type="Callable[[str], None] | None" default="None">
  Called for each stdout line after the command completes.
</ParamField>

<ParamField body="on_stderr" type="Callable[[str], None] | None" default="None">
  Called for each stderr line after the command completes.
</ParamField>

**Returns** `CommandResult`. Raises `CommandExitException` if the exit code is non-zero.

### `handle.kill()`

Kill the process.

**Returns** `bool`

### `handle.pid`

**Type** `int` — the process ID.

***

## Data models

### `CommandResult`

```python theme={null}
@dataclass
class CommandResult:
    stdout: str = ""
    stderr: str = ""
    exit_code: int = 0
```

### `ProcessInfo`

```python theme={null}
@dataclass
class ProcessInfo:
    pid: int
    cmd: str
    is_pty: bool = False
    envs: dict[str, str] = field(default_factory=dict)
```

### `Stdout` / `Stderr`

Line-level output models used when streaming output with timestamps:

```python theme={null}
@dataclass
class Stdout:
    line: str
    timestamp: float | None = None

@dataclass
class Stderr:
    line: str
    timestamp: float | None = None
```

***

## Examples

### Run a Python script

```python theme={null}
result = sbx.commands.run(
    "python3 -c \"import sys; print(sys.version)\"",
    timeout=10,
)
print(result.stdout)
```

### Run with environment variables

```python theme={null}
result = sbx.commands.run(
    "echo $MY_SECRET",
    envs={"MY_SECRET": "s3cr3t"},
)
```

### Stream long-running output

```python theme={null}
lines = []

result = sbx.commands.run_stream(
    "for i in $(seq 1 100); do echo $i; sleep 0.05; done",
    on_stdout=lines.append,
)
print(f"Received {len(lines)} lines")
```

### Background process with stdin

```python theme={null}
import time

handle = sbx.commands.run("python3 -c \"import sys; [print(l.strip()+'!') for l in sys.stdin]\"", background=True)
time.sleep(0.1)
sbx.commands.send_stdin(handle.pid, "hello\nworld\n")
result = handle.wait()
print(result.stdout)  # "hello!\nworld!\n"
```
