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.
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
export DECLAW_API_KEY="your-api-key"
export DECLAW_DOMAIN="your-declaw-instance.example.com:8080"
Install the SDK:
go get github.com/declaw-ai/declaw-go
1. Basic echo (cat)
Start cat, send a line, close stdin, and read the echoed output.
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
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();
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...) },
})
2. Stdout and stderr callbacks
Receive stdout and stderr on separate callbacks.
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"
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();
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...) },
})
3. Multi-round interactive conversation
Send multiple lines to a process that reads in a loop.
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
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();
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)
4. Line counting with EOF
Send lines to wc -l and close stdin so it sees EOF and reports.
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"
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();
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...) },
})
5. Environment variables and working directory
Pass env vars and set the working directory.
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"
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();
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)
6. Kill a long-running process
Start a process, wait briefly, kill it, and read the exit code.
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
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
handle, _ := sbx.Stdio.Start(ctx, "sleep 300", nil)
time.Sleep(time.Second)
handle.Kill(ctx)
result, _ := handle.Wait(ctx)
fmt.Println(result.ExitCode) // -1
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.