> ## 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.

# Interactive Stdio

> Bidirectional stdin/stdout/stderr for sandboxed processes — send data incrementally, receive streaming output, and drive interactive command-line tools.

**Stdio** keeps stdin open on a process inside the sandbox so you can
send data incrementally and receive stdout/stderr as it's produced.
Unlike [`commands.run`](/features/commands), which waits for the process
to exit before returning output, stdio lets you:

* **Send input line by line** — pipe data into `cat`, `wc`, `jq`, a
  database CLI, or any process that reads from stdin interactively.
* **Receive output as it arrives** — via callbacks or an iterator, not
  as one blob at the end.
* **Separate stdout and stderr** — independent callbacks for each stream.
* **Close stdin to signal EOF** — the process sees end-of-file on its
  stdin, just like pressing Ctrl-D in a terminal.
* **Kill long-running processes** — terminate a process mid-execution
  and retrieve the exit code.

Stdio is the right tool for REPLs, MCP servers, database shells,
language servers, and any process that reads from stdin in a loop.

## When to use Stdio vs Commands vs PTY

| Use `commands.run` when...                | Use `stdio.start` when...                   | Use `pty.create` when...                       |
| ----------------------------------------- | ------------------------------------------- | ---------------------------------------------- |
| Command takes all input upfront and exits | Command reads from stdin interactively      | Command needs a real terminal (ANSI, raw mode) |
| You only need the final result            | You need to send data mid-execution         | You need keystroke-level input                 |
| `pip install`, `go build`, `pytest`       | `cat`, `wc`, `jq`, database CLI, MCP server | `vim`, `htop`, `ssh`, `gh auth login`          |

**Default to `commands.run`.** Use `stdio.start` when you need to pipe
data into a running process. Use `pty.create` only when the command
requires a real terminal.

## Architecture

`stdio.start` uses four REST endpoints plus one SSE stream:

| Operation       | Method / route                                                           |
| --------------- | ------------------------------------------------------------------------ |
| Start process   | `POST /sandboxes/{id}/stdio` — returns a `cmd_id`                        |
| Send stdin      | `POST /sandboxes/{id}/stdio/{cmd_id}/stdin`                              |
| Close stdin     | `POST /sandboxes/{id}/stdio/{cmd_id}/stdin/close`                        |
| Kill process    | `DELETE /sandboxes/{id}/stdio/{cmd_id}`                                  |
| **Live output** | `GET /sandboxes/{id}/stdio/{cmd_id}/stream` (SSE, base64-encoded frames) |

The SSE stream emits `event: stdout` and `event: stderr` frames with
base64-encoded data, plus a final `event: exit` frame with the exit code.

## Quick start

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from declaw import Sandbox

    sbx = Sandbox.create()
    try:
        # Start a process with an open stdin pipe
        chunks = []
        proc = sbx.stdio.start("cat", on_stdout=lambda d: chunks.append(d))

        # Send data and close stdin
        proc.send_stdin("hello from stdio!\n")
        proc.close_stdin()

        # Wait for the process to exit
        result = proc.wait(timeout=10)
        print(b"".join(chunks).decode().strip())  # "hello from stdio!"
        print(result.exit_code)                    # 0
    finally:
        sbx.kill()
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    import { Sandbox } from "@declaw/sdk";

    const sbx = await Sandbox.create();
    try {
      const chunks: Uint8Array[] = [];
      const proc = await sbx.stdio.start("cat", {
        onStdout: (d) => chunks.push(d),
      });

      await proc.sendStdin("hello from stdio!\n");
      await proc.closeStdin();

      const result = await proc.wait();
      const dec = new TextDecoder();
      console.log(chunks.map((c) => dec.decode(c)).join("").trim());
      console.log(result.exitCode);
    } finally {
      await sbx.kill();
    }
    ```
  </Tab>

  <Tab title="Go">
    In Go, output callbacks are provided when calling `Stream()`, not at
    start time.

    ```go theme={null}
    ctx := context.Background()
    sbx, _ := declaw.Create(ctx)
    defer sbx.Kill(ctx)

    handle, _ := sbx.Stdio.Start(ctx, "cat", nil)
    handle.SendStdin(ctx, []byte("hello from stdio!\n"))
    handle.CloseStdin(ctx)

    var output []byte
    result, _ := handle.Stream(ctx, &declaw.StdioStreamOpts{
        OnStdout: func(data []byte) { output = append(output, data...) },
    })
    fmt.Println(string(output))     // "hello from stdio!"
    fmt.Println(result.ExitCode)    // 0
    ```
  </Tab>
</Tabs>

## Multi-round interaction

Send multiple lines of input to a process that reads in a loop:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    replies = []
    proc = sbx.stdio.start(
        "sh -c 'while read line; do echo \"reply: $line\"; done'",
        on_stdout=lambda d: replies.append(d),
    )

    for i in range(5):
        proc.send_stdin(f"message {i}\n")

    proc.close_stdin()
    result = proc.wait(timeout=10)
    print(b"".join(replies).decode())
    # reply: message 0
    # reply: message 1
    # ...
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const replies: Uint8Array[] = [];
    const proc = await sbx.stdio.start(
      "sh -c 'while read line; do echo \"reply: $line\"; done'",
      { onStdout: (d) => replies.push(d) },
    );

    for (let i = 0; i < 5; i++) {
      await proc.sendStdin(`message ${i}\n`);
    }

    await proc.closeStdin();
    await proc.wait();
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    handle, _ := sbx.Stdio.Start(ctx,
        `sh -c 'while read line; do echo "reply: $line"; done'`, nil)

    for i := 0; i < 5; i++ {
        handle.SendStdin(ctx, []byte(fmt.Sprintf("message %d\n", i)))
    }
    handle.CloseStdin(ctx)
    handle.Wait(ctx)
    ```
  </Tab>
</Tabs>

## Environment variables and working directory

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    proc = sbx.stdio.start(
        "sh -c 'echo $GREETING from $(pwd)'",
        envs={"GREETING": "hello"},
        cwd="/tmp",
        on_stdout=lambda d: print(d.decode().strip()),
    )
    proc.wait()
    # hello from /tmp
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const out: Uint8Array[] = [];
    const proc = await sbx.stdio.start(
      "sh -c 'echo $GREETING from $(pwd)'",
      {
        envs: { GREETING: "hello" },
        cwd: "/tmp",
        onStdout: (d) => out.push(d),
      },
    );
    await proc.wait();
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    handle, _ := sbx.Stdio.Start(ctx,
        "sh -c 'echo $GREETING from $(pwd)'",
        &declaw.StdioStartOpts{
            Envs: map[string]string{"GREETING": "hello"},
            Cwd:  "/tmp",
        })
    handle.Wait(ctx)
    ```
  </Tab>
</Tabs>

## Killing a process

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import time

    proc = sbx.stdio.start("sleep 300")
    time.sleep(1)
    killed = proc.kill()          # True
    result = proc.wait()
    print(result.exit_code)       # -1
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const proc = await sbx.stdio.start("sleep 300");
    await new Promise((r) => setTimeout(r, 1000));
    const killed = await proc.kill();   // true
    const result = await proc.wait();
    console.log(result.exitCode);       // -1
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    handle, _ := sbx.Stdio.Start(ctx, "sleep 300", nil)
    time.Sleep(time.Second)
    handle.Kill(ctx)
    result, _ := handle.Wait(ctx)
    fmt.Println(result.ExitCode)  // -1
    ```
  </Tab>
</Tabs>

## Next steps

* [Python SDK: Stdio reference](/sdks/python/stdio) — `StdioProcess`, iterator protocol, threading notes
* [TypeScript SDK: Stdio reference](/sdks/typescript/stdio) — TypeScript equivalent
* [Go SDK: Stdio reference](/sdks/go/stdio) — Go equivalent
* [Cookbook: interactive stdio](/cookbook/patterns/stdio-interactive) — 6 runnable demos
