> ## Documentation Index
> Fetch the complete documentation index at: https://opensandbox-oc-s-762ac928075c46d2828bcb22.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Exec

> Run commands and manage exec sessions

Accessed via `sandbox.exec`.

## `await sandbox.exec.run(command, ...)`

Run a command synchronously via `sh -c`. [HTTP API →](/api-reference/exec/run)

<ParamField body="command" type="str" required>
  Shell command
</ParamField>

<ParamField body="timeout" type="int" default="60">
  Timeout in seconds
</ParamField>

<ParamField body="env" type="dict[str, str]">
  Environment variables
</ParamField>

<ParamField body="cwd" type="str">
  Working directory
</ParamField>

**Returns:** `ProcessResult`

```python theme={null}
result = await sandbox.exec.run("npm test", cwd="/app")
```

***

## `await sandbox.exec.start(command, ...)`

Start a long-running command with streaming I/O. [HTTP API →](/api-reference/exec/create-session)

<ParamField body="command" type="str" required>
  Command
</ParamField>

<ParamField body="args" type="list[str]">
  Arguments
</ParamField>

<ParamField body="env" type="dict[str, str]">
  Environment variables
</ParamField>

<ParamField body="cwd" type="str">
  Working directory
</ParamField>

<ParamField body="timeout" type="int">
  Timeout in seconds
</ParamField>

<ParamField body="max_run_after_disconnect" type="int">
  Seconds to keep running after disconnect
</ParamField>

<ParamField body="on_stdout" type="Callable[[bytes], None]">
  Stdout callback
</ParamField>

<ParamField body="on_stderr" type="Callable[[bytes], None]">
  Stderr callback
</ParamField>

<ParamField body="on_exit" type="Callable[[int], None]">
  Exit callback
</ParamField>

**Returns:** `ExecSession`

```python theme={null}
session = await sandbox.exec.start(
    "node", args=["server.js"], cwd="/app",
    on_stdout=lambda b: print(b.decode(), end=""),
    on_exit=lambda code: print(f"exited {code}"),
)
exit_code = await session.done
```

***

## `await sandbox.exec.background(command, ...)`

Alias for [`start`](#await-sandbox-exec-start-command). Same signature, same return type. Use when the intent is "run this in the background and observe it".

```python theme={null}
server = await sandbox.exec.background("npm", args=["run", "dev"])
```

***

## `await sandbox.exec.shell(...)`

Open a stateful shell session. Subsequent `.run()` calls share the same bash process, so `cwd`, exported env vars, and shell functions persist — the ergonomics of a terminal tab.

Backed by a long-running `bash --noprofile --norc` session. Foreground-only: concurrent `.run()` raises `ShellBusyError`.

<ParamField body="cwd" type="str">
  Initial working directory
</ParamField>

<ParamField body="env" type="dict[str, str]">
  Initial environment variables
</ParamField>

**Returns:** `Shell`

```python theme={null}
sh = await sandbox.exec.shell(cwd="/app")

await sh.run("npm install")
await sh.run("export NODE_ENV=test")
r = await sh.run("npm test", on_stdout=lambda b: print(b.decode(), end=""))
print(r.exit_code)

await sh.close()
```

### Shell

| Member                               | Type            | Description                                 |
| ------------------------------------ | --------------- | ------------------------------------------- |
| `session_id`                         | `str`           | Underlying exec session ID                  |
| `run(cmd, on_stdout=?, on_stderr=?)` | `ProcessResult` | Run a command, blocking until it exits      |
| `close()`                            | `None`          | Write `exit` and wait for bash to terminate |

<Note>
  Per-call `cwd`, `env`, and `timeout` are intentionally not supported in v1. Use inline shell syntax (`cd /x && cmd`, `FOO=bar cmd`) — the shell state carries across calls.
</Note>

Errors:

* `ShellBusyError` — another `run()` is in flight (shell is foreground-only).
* `ShellClosedError` — bash has exited (explicit `close()`, a command ran `exit`, or the session dropped).

***

## `await sandbox.exec.attach(session_id, ...)`

Reconnect to a running exec session over WebSocket.

<ParamField body="session_id" type="str" required>
  Session ID
</ParamField>

<ParamField body="on_stdout" type="Callable[[bytes], None]">
  Stdout callback
</ParamField>

<ParamField body="on_stderr" type="Callable[[bytes], None]">
  Stderr callback
</ParamField>

<ParamField body="on_exit" type="Callable[[int], None]">
  Exit callback
</ParamField>

<ParamField body="on_scrollback_end" type="Callable[[], None]">
  Scrollback replay done
</ParamField>

**Returns:** `ExecSession`

***

## `await sandbox.exec.list()`

List all exec sessions. [HTTP API →](/api-reference/exec/list-sessions)

**Returns:** `list[ExecSessionInfo]`

***

## `await sandbox.exec.kill(session_id, signal=9)`

Kill an exec session. [HTTP API →](/api-reference/exec/kill-session)

**Returns:** `None`

***

## ExecSession

| Member             | Type                  | Description                              |
| ------------------ | --------------------- | ---------------------------------------- |
| `session_id`       | `str`                 | Session ID                               |
| `done`             | `asyncio.Future[int]` | Resolves with exit code                  |
| `send_stdin(data)` | coroutine             | Send input (`str` or `bytes`)            |
| `kill(signal=9)`   | coroutine             | Kill process                             |
| `close()`          | coroutine             | Detach WebSocket (process keeps running) |

***

## Types

<Accordion title="ProcessResult">
  ```python theme={null}
  @dataclass
  class ProcessResult:
      exit_code: int
      stdout: str
      stderr: str
  ```
</Accordion>

<Accordion title="ExecSessionInfo">
  ```python theme={null}
  @dataclass
  class ExecSessionInfo:
      session_id: str
      sandbox_id: str
      command: str
      args: list[str]
      running: bool
      exit_code: int | None
      started_at: str
      attached_clients: int
  ```
</Accordion>
