> ## 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 — basic echo, multi-round conversations, separate stream callbacks, line counting, environment variables, and process lifecycle.

This cookbook demonstrates the full `stdio` API surface through six
self-contained demos. Each one starts a process with an open stdin pipe,
sends data, and reads the response.

## What you'll learn

* Sending stdin and receiving stdout via `sandbox.stdio.start(cmd)`
* Separate `on_stdout` and `on_stderr` callbacks
* Multi-round interactive conversations (read-echo loop)
* Iterator / stream protocol for consuming output
* Closing stdin to signal EOF (`proc.close_stdin()`)
* Custom environment variables and working directory
* Killing a long-running process (`proc.kill()`)

## Prerequisites

<Snippet file="snippets/env-setup.mdx" />

Install the SDK:

<Tabs>
  <Tab title="Python">
    ```bash theme={null}
    pip install declaw
    ```
  </Tab>

  <Tab title="TypeScript">
    ```bash theme={null}
    npm install @declaw/sdk
    ```
  </Tab>

  <Tab title="Go">
    ```bash theme={null}
    go get github.com/declaw-ai/declaw-go
    ```
  </Tab>
</Tabs>

## 1. Basic echo (cat)

Start `cat`, send a line, close stdin, and read the echoed output.

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

    sbx = Sandbox.create(timeout=300)
    chunks = []
    proc = sbx.stdio.start("cat", on_stdout=lambda d: chunks.append(d))
    proc.send_stdin("hello from stdio!\n")
    proc.close_stdin()
    result = proc.wait(timeout=10)
    print(b"".join(chunks).decode().strip())  # "hello from stdio!"
    print(result.exit_code)                    # 0
    ```
  </Tab>

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

    const sbx = await Sandbox.create({ timeout: 300 });
    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();
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    sbx, _ := declaw.Create(ctx, declaw.WithTimeout(300))
    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...) },
    })
    ```
  </Tab>
</Tabs>

## 2. Stdout and stderr callbacks

Receive stdout and stderr on separate callbacks.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    out, err = [], []
    proc = sbx.stdio.start(
        "sh -c 'echo this-is-stdout; echo this-is-stderr >&2'",
        on_stdout=lambda d: out.append(d),
        on_stderr=lambda d: err.append(d),
    )
    result = proc.wait(timeout=10)
    print(b"".join(out).decode().strip())  # "this-is-stdout"
    print(b"".join(err).decode().strip())  # "this-is-stderr"
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const out: Uint8Array[] = [];
    const err: Uint8Array[] = [];
    const proc = await sbx.stdio.start(
      "sh -c 'echo this-is-stdout; echo this-is-stderr >&2'",
      {
        onStdout: (d) => out.push(d),
        onStderr: (d) => err.push(d),
      },
    );
    await proc.wait();
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    handle, _ := sbx.Stdio.Start(ctx,
        "sh -c 'echo this-is-stdout; echo this-is-stderr >&2'", nil)
    var stdout, stderr []byte
    handle.Stream(ctx, &declaw.StdioStreamOpts{
        OnStdout: func(d []byte) { stdout = append(stdout, d...) },
        OnStderr: func(d []byte) { stderr = append(stderr, d...) },
    })
    ```
  </Tab>
</Tabs>

## 3. Multi-round interactive conversation

Send multiple lines 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()
    proc.wait(timeout=10)
    # reply: message 0 ... reply: message 4
    ```
  </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>

## 4. Line counting with EOF

Send lines to `wc -l` and close stdin so it sees EOF and reports.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    out = []
    proc = sbx.stdio.start("wc -l", on_stdout=lambda d: out.append(d))
    proc.send_stdin("line one\nline two\nline three\n")
    proc.close_stdin()
    result = proc.wait(timeout=10)
    print(b"".join(out).decode().strip())  # "3"
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const out: Uint8Array[] = [];
    const proc = await sbx.stdio.start("wc -l", {
      onStdout: (d) => out.push(d),
    });
    await proc.sendStdin("line one\nline two\nline three\n");
    await proc.closeStdin();
    await proc.wait();
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    handle, _ := sbx.Stdio.Start(ctx, "wc -l", nil)
    handle.SendStdin(ctx, []byte("line one\nline two\nline three\n"))
    handle.CloseStdin(ctx)
    var count []byte
    handle.Stream(ctx, &declaw.StdioStreamOpts{
        OnStdout: func(d []byte) { count = append(count, d...) },
    })
    ```
  </Tab>
</Tabs>

## 5. Environment variables and working directory

Pass env vars and set the working directory.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    out = []
    proc = sbx.stdio.start(
        "sh -c 'echo $GREETING from $(pwd)'",
        envs={"GREETING": "hello-env"},
        cwd="/tmp",
        on_stdout=lambda d: out.append(d),
    )
    proc.wait(timeout=10)
    print(b"".join(out).decode().strip())  # "hello-env 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-env" },
        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-env"},
            Cwd:  "/tmp",
        })
    handle.Wait(ctx)
    ```
  </Tab>
</Tabs>

## 6. Kill a long-running process

Start a process, wait briefly, kill it, and read the exit code.

<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(timeout=10)
    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>

## Full runnable examples

Complete self-contained scripts with all six demos and cleanup are
available in the SDK repositories. Install the SDK (`pip install declaw`
or `npm install @declaw/sdk`) and run the example directly.
