Interactive terminal (PTY)
The PTY (pseudo-terminal) module allows you to create interactive terminal sessions in the sandbox with real-time, bidirectional communication.
Unlike commands.run() which executes a command and returns output after completion, PTY provides:
- Real-time streaming - Output is streamed as it happens via callbacks
- Bidirectional input - Send input while the terminal is running
- Interactive shell - Full terminal support with ANSI colors and escape sequences
- Session persistence - Disconnect and reconnect to running sessions
Create a PTY session
Use sandbox.pty.create() to start an interactive bash shell. This example runs echo 'hello world', then exits and prints the terminal output.
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const terminal = await sandbox.pty.create({
cols: 80, // Terminal size in characters
rows: 24,
envs: { MY_VAR: 'hello' }, // Optional environment variables
cwd: '/home/user', // Optional working directory
user: 'root', // Optional user to run as
onData: (data) => {
// Called whenever the terminal outputs data
process.stdout.write(data)
},
})
// terminal.pid contains the process ID
console.log('Terminal PID:', terminal.pid)
// Send input to the PTY. Data must be bytes (Uint8Array), and the trailing newline "presses Enter".
await sandbox.pty.sendInput(terminal.pid, new TextEncoder().encode("echo 'hello world'\n"))
// The PTY runs an interactive login shell that won't exit on its own, so tell it
// to exit — otherwise wait() below blocks forever.
await sandbox.pty.sendInput(terminal.pid, new TextEncoder().encode('exit\n'))
// Wait for the terminal to exit (output is streamed to onData above)
await terminal.wait()The PTY runs an interactive bash shell with TERM=xterm-256color, which supports ANSI colors and escape sequences.
Timeout
PTY sessions have a configurable timeout that controls the session duration. The default is 60 seconds. For interactive or long-running sessions, set timeoutMs: 0 (JavaScript) or timeout=0 (Python) to keep the session open indefinitely.
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const terminal = await sandbox.pty.create({
cols: 80,
rows: 24,
onData: (data) => process.stdout.write(data),
timeoutMs: 0, // Keep the session open indefinitely
})Send input to PTY
Use sendInput() in JavaScript or send_stdin() in Python to send data to the terminal. These methods return a Promise (JavaScript) or complete synchronously (Python) - the actual output is delivered to your onData callback (JavaScript) or to the on_pty callback you pass to wait() (Python).
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const terminal = await sandbox.pty.create({
cols: 80,
rows: 24,
onData: (data) => process.stdout.write(data),
})
// Send a command (don't forget the newline!)
await sandbox.pty.sendInput(
terminal.pid,
new TextEncoder().encode('echo "Hello from PTY"\n')
)Resize the terminal
When the user’s terminal window changes size, notify the PTY with resize(). The cols and rows parameters are measured in characters, not pixels.
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const terminal = await sandbox.pty.create({
cols: 80,
rows: 24,
onData: (data) => process.stdout.write(data),
})
// Resize to new dimensions (in characters)
await sandbox.pty.resize(terminal.pid, {
cols: 120,
rows: 40,
})Disconnect and reconnect
You can disconnect from a PTY session while keeping it running, then reconnect later with a new data handler. This is useful for:
- Resuming terminal sessions after network interruptions
- Sharing terminal access between multiple clients
- Implementing terminal session persistence
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
// Create a PTY session
const terminal = await sandbox.pty.create({
cols: 80,
rows: 24,
onData: (data) => console.log('Handler 1:', new TextDecoder().decode(data)),
})
const pid = terminal.pid
// Send a command
await sandbox.pty.sendInput(pid, new TextEncoder().encode('echo hello\n'))
// Disconnect - PTY keeps running in the background
await terminal.disconnect()
// Later: reconnect with a new data handler
const reconnected = await sandbox.pty.connect(pid, {
onData: (data) => console.log('Handler 2:', new TextDecoder().decode(data)),
})
// Continue using the session
await sandbox.pty.sendInput(pid, new TextEncoder().encode('echo world\n'))
// Wait for the terminal to exit
await reconnected.wait()Kill the PTY
Terminate the PTY session with kill().
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const terminal = await sandbox.pty.create({
cols: 80,
rows: 24,
onData: (data) => process.stdout.write(data),
})
// Kill the PTY
const killed = await sandbox.pty.kill(terminal.pid)
console.log('Killed:', killed) // true if successful
// Or use the handle method
// await terminal.kill()Wait for PTY to exit
Use wait() to wait for the terminal session to end (e.g., when the user types exit).
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const terminal = await sandbox.pty.create({
cols: 80,
rows: 24,
onData: (data) => process.stdout.write(data),
})
// Send exit command
await sandbox.pty.sendInput(terminal.pid, new TextEncoder().encode('exit\n'))
// Wait for the terminal to exit
const result = await terminal.wait()
console.log('Exit code:', result.exitCode)Interactive terminal (SSH-like)
Building a fully interactive terminal (like SSH) requires handling raw mode, stdin forwarding, and terminal resize events. For a production implementation, see the E2B CLI source code which uses the same sandbox.pty API documented above.