Create and Attach to a Terminal
Create a terminal and attach to its websocket stream:const terminal = await sandbox.terminal.create({
command: "bash",
args: ["-l"],
rows: 24,
cols: 80,
});
const connection = await terminal.attach();
for await (const event of connection.events()) {
if (event.type === "output") {
process.stdout.write(event.data);
continue;
}
console.log("Exit code:", event.status.exitCode);
break;
}
terminal = sandbox.terminal.create(
{
"command": "bash",
"args": ["-l"],
"rows": 24,
"cols": 80,
}
)
connection = terminal.attach()
for event in connection.events():
if event.type == "output":
print(event.data, end="")
continue
print(f"Exit code: {event.status.exit_code}")
break
from hyperbrowser.models import SandboxTerminalCreateParams
terminal = sandbox.terminal.create(
SandboxTerminalCreateParams(
command="bash",
args=["-l"],
rows=24,
cols=80,
)
)
connection = terminal.attach()
for event in connection.events():
if event.type == "output":
print(event.data, end="")
continue
print(f"Exit code: {event.status.exit_code}")
break
Write Input and Resize the PTY
Send input through the attached connection and resize the terminal:const terminal = await sandbox.terminal.create({
command: "bash",
args: ["-l"],
});
const connection = await terminal.attach();
await connection.resize(32, 110);
await connection.write("pwd\n");
await connection.write("echo terminal-ok\n");
await connection.write("exit\n");
await connection.close();
terminal = sandbox.terminal.create(
{
"command": "bash",
"args": ["-l"],
}
)
connection = terminal.attach()
connection.resize(32, 110)
connection.write("pwd\n")
connection.write("echo terminal-ok\n")
connection.write("exit\n")
connection.close()
from hyperbrowser.models import SandboxTerminalCreateParams
terminal = sandbox.terminal.create(
SandboxTerminalCreateParams(
command="bash",
args=["-l"],
)
)
connection = terminal.attach()
connection.resize(32, 110)
connection.write("pwd\n")
connection.write("echo terminal-ok\n")
connection.write("exit\n")
connection.close()
Get, Refresh, and Wait
Fetch an existing terminal or wait for it to complete:const terminal = await sandbox.terminal.create({
command: "bash",
args: ["-lc", "echo hello-from-terminal"],
});
const fetched = await sandbox.terminal.get(terminal.id, true);
console.log(fetched.current.output?.map((chunk) => chunk.data).join(""));
const status = await terminal.wait({
timeoutMs: 2_000,
includeOutput: true,
});
console.log(status.running, status.exitCode);
terminal = sandbox.terminal.create(
{
"command": "bash",
"args": ["-lc", "echo hello-from-terminal"],
}
)
fetched = sandbox.terminal.get(terminal.id, include_output=True)
print("".join(chunk.data for chunk in fetched.current.output or []))
status = terminal.wait(
timeout_ms=2000,
include_output=True,
)
print(status.running, status.exit_code)
from hyperbrowser.models import SandboxTerminalCreateParams
terminal = sandbox.terminal.create(
SandboxTerminalCreateParams(
command="bash",
args=["-lc", "echo hello-from-terminal"],
)
)
fetched = sandbox.terminal.get(terminal.id, include_output=True)
print("".join(chunk.data for chunk in fetched.current.output or []))
status = terminal.wait(
timeout_ms=2000,
include_output=True,
)
print(status.running, status.exit_code)
Signal and Kill
You can signal or kill a terminal-backed process:const terminal = await sandbox.terminal.create({
command: "bash",
args: ["-lc", "sleep 30"],
});
await terminal.signal("TERM");
const status = await terminal.wait({ timeoutMs: 5_000 });
console.log("Still running:", status.running);
console.log("Exit code:", status.exitCode);
terminal = sandbox.terminal.create(
{
"command": "bash",
"args": ["-lc", "sleep 30"],
}
)
terminal.signal("TERM")
status = terminal.wait(timeout_ms=5000)
print("Still running:", status.running)
print("Exit code:", status.exit_code)
from hyperbrowser.models import SandboxTerminalCreateParams
terminal = sandbox.terminal.create(
SandboxTerminalCreateParams(
command="bash",
args=["-lc", "sleep 30"],
)
)
terminal.signal("TERM")
status = terminal.wait(timeout_ms=5000)
print("Still running:", status.running)
print("Exit code:", status.exit_code)
status.running tells you whether the terminal session is still alive after the signal.
After signal("TERM") and a successful wait(...), you would usually expect
status.running to be false.
Common Terminal Parameters
string
required
Command to launch inside the terminal session.
string[]
Optional command arguments.
string
Working directory for the terminal process.
object
Environment variables to set for the terminal process.
number
Initial terminal row count.
number
Initial terminal column count.
number
Optional PTY runtime limit in milliseconds.
sandbox.pty is an alias for sandbox.terminal.Create Interactive Terminal
Run an interactive sandbox shell from your backend process and bridge local terminal input/output to the sandbox PTY stream.const terminal = await sandbox.terminal.create({
command: "bash",
args: ["-l"],
rows: process.stdout.rows ?? 24,
cols: process.stdout.columns ?? 80,
});
console.log("Sandbox PTY ID:", terminal.id);
const connection = await terminal.attach();
const onStdinData = (chunk: Buffer) => {
void connection.write(chunk);
};
const onResize = () => {
void connection.resize(process.stdout.rows ?? 24, process.stdout.columns ?? 80);
};
if (process.stdin.isTTY) {
process.stdin.setRawMode(true);
}
process.stdin.resume();
process.stdin.on("data", onStdinData);
if (process.stdout.isTTY) {
process.stdout.on("resize", onResize);
}
try {
for await (const event of connection.events()) {
if (event.type === "output") {
process.stdout.write(event.data);
continue;
}
process.stdout.write(`\n[remote exited: ${event.status.exitCode ?? "unknown"}]\n`);
break;
}
} finally {
process.stdin.off("data", onStdinData);
process.stdin.pause();
process.stdout.off("resize", onResize);
if (process.stdin.isTTY) {
process.stdin.setRawMode(false);
}
await connection.close();
await sandbox.stop();
}
import os
import sys
import termios
import threading
import tty
terminal = sandbox.terminal.create(
{
"command": "bash",
"args": ["-l"],
"rows": os.get_terminal_size().lines if sys.stdout.isatty() else 24,
"cols": os.get_terminal_size().columns if sys.stdout.isatty() else 80,
}
)
print(f"Sandbox PTY ID: {terminal.id}")
connection = terminal.attach()
stop_input = threading.Event()
def stdin_pump():
while not stop_input.is_set():
chunk = os.read(sys.stdin.fileno(), 1024)
if not chunk:
break
connection.write(chunk)
input_thread = threading.Thread(target=stdin_pump, daemon=True)
input_thread.start()
original_tty = None
if sys.stdin.isatty():
original_tty = termios.tcgetattr(sys.stdin.fileno())
tty.setraw(sys.stdin.fileno())
try:
for event in connection.events():
if event.type == "output":
sys.stdout.write(event.data)
sys.stdout.flush()
continue
print(f"\n[remote exited: {event.status.exit_code}]")
break
finally:
stop_input.set()
connection.close()
if original_tty is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, original_tty)
sandbox.stop()
import os
import sys
import termios
import threading
import tty
from hyperbrowser.models import SandboxTerminalCreateParams
terminal = sandbox.terminal.create(
SandboxTerminalCreateParams(
command="bash",
args=["-l"],
rows=os.get_terminal_size().lines if sys.stdout.isatty() else 24,
cols=os.get_terminal_size().columns if sys.stdout.isatty() else 80,
)
)
print(f"Sandbox PTY ID: {terminal.id}")
connection = terminal.attach()
stop_input = threading.Event()
def stdin_pump():
while not stop_input.is_set():
chunk = os.read(sys.stdin.fileno(), 1024)
if not chunk:
break
connection.write(chunk)
input_thread = threading.Thread(target=stdin_pump, daemon=True)
input_thread.start()
original_tty = None
if sys.stdin.isatty():
original_tty = termios.tcgetattr(sys.stdin.fileno())
tty.setraw(sys.stdin.fileno())
try:
for event in connection.events():
if event.type == "output":
sys.stdout.write(event.data)
sys.stdout.flush()
continue
print(f"\n[remote exited: {event.status.exit_code}]")
break
finally:
stop_input.set()
connection.close()
if original_tty is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, original_tty)
sandbox.stop()
The Python raw-mode bridge uses
termios and tty, so it is intended for POSIX terminals (Linux/macOS).