Skip to main content
The PTY (pseudo-terminal) API provides interactive terminal sessions with full TTY support, enabling complex terminal interactions, real-time input/output, and terminal control.

Overview

Daytona’s PTY sessions enable:
  • Interactive terminals - Full terminal emulation with command history
  • Real-time I/O - Stream terminal output as it happens
  • Terminal control - Resize, manage, and control terminal sessions
  • Process management - Monitor and control running processes

Creating PTY Sessions

Basic PTY Session

Create an interactive terminal session:
const ptyHandle = await sandbox.process.createPty({
  id: 'my-terminal',
  cols: 120,
  rows: 30,
  onData: (data) => {
    // Decode and display terminal output
    const text = new TextDecoder().decode(data);
    process.stdout.write(text);
  }
});

// Wait for connection to establish
await ptyHandle.waitForConnection();

// Send commands
await ptyHandle.sendInput('ls -la\n');
await ptyHandle.sendInput('pwd\n');
await ptyHandle.sendInput('exit\n');

// Wait for terminal to exit
const result = await ptyHandle.wait();
console.log(`Terminal exited with code: ${result.exitCode}`);

// Clean up
await ptyHandle.disconnect();

PTY with Custom Environment

const ptyHandle = await sandbox.process.createPty({
  id: 'env-session',
  cwd: '/workspace/project',
  envs: {
    TERM: 'xterm-256color',
    LANG: 'en_US.UTF-8',
    MY_VAR: 'custom-value'
  },
  cols: 100,
  rows: 40,
  onData: (data) => {
    const text = new TextDecoder().decode(data);
    process.stdout.write(text);
  }
});

await ptyHandle.waitForConnection();
options
PtyCreateOptions & PtyConnectOptions
required
PTY session configuration

Connecting to Existing PTY

Connect to PTY Session

Connect to a previously created PTY session:
const handle = await sandbox.process.connectPty('my-session', {
  onData: (data) => {
    const text = new TextDecoder().decode(data);
    process.stdout.write(text);
  }
});

await handle.waitForConnection();

// Interact with the existing session
await handle.sendInput('echo "Connected!"\n');
sessionId
string
required
ID of the PTY session to connect to
options
PtyConnectOptions
required
Connection options

Sending Input

Send Text Commands

// Send a command
await ptyHandle.sendInput('ls -la\n');

// Send multiple commands
await ptyHandle.sendInput('cd /workspace\n');
await ptyHandle.sendInput('git status\n');

// Send interactive input
await ptyHandle.sendInput('python3\n');
await new Promise(resolve => setTimeout(resolve, 500));
await ptyHandle.sendInput('print("Hello from Python")\n');
await ptyHandle.sendInput('exit()\n');

Send Raw Bytes

// Send Ctrl+C (interrupt)
await ptyHandle.sendInput(new Uint8Array([3]));

// Send Ctrl+D (EOF)
await ptyHandle.sendInput(new Uint8Array([4]));

// Send Ctrl+Z (suspend)
await ptyHandle.sendInput(new Uint8Array([26]));
data
string | Uint8Array
required
Input data to send. String for commands/text, Uint8Array for raw control sequences.

Terminal Control

Resize Terminal

Change terminal dimensions:
const info = await ptyHandle.resize(120, 50);
console.log(`Resized to: ${info.cols}x${info.rows}`);
Or using the process API:
const info = await sandbox.process.resizePtySession(
  'my-session',
  150,  // cols
  40    // rows
);
cols
number
required
New number of terminal columns
rows
number
required
New number of terminal rows

Wait for Connection

Ensure the PTY session is ready before sending input:
const ptyHandle = await sandbox.process.createPty(options);

// Wait for connection (10 second timeout)
await ptyHandle.waitForConnection();

// Now safe to send input
await ptyHandle.sendInput('echo "Ready!"\n');

Check Connection Status

if (ptyHandle.isConnected()) {
  await ptyHandle.sendInput('command\n');
} else {
  console.log('Not connected');
}

Process Management

Wait for Exit

Block until the PTY process terminates:
await ptyHandle.sendInput('exit\n');

const result = await ptyHandle.wait();

if (result.exitCode === 0) {
  console.log('Process completed successfully');
} else {
  console.log(`Process failed with code: ${result.exitCode}`);
  if (result.error) {
    console.log(`Error: ${result.error}`);
  }
}
PTY Result:
interface PtyResult {
  exitCode?: number;  // Exit code when process ends
  error?: string;     // Error message if PTY failed
}

Kill PTY Session

Forcefully terminate the PTY session:
// Kill via handle
await ptyHandle.kill();

// Or kill via process API
await sandbox.process.killPtySession('my-session');

// Wait for termination
const result = await ptyHandle.wait();
console.log(`Terminated with exit code: ${result.exitCode}`);

Disconnect from PTY

Close the WebSocket connection and clean up:
await ptyHandle.disconnect();
Always disconnect from PTY sessions when done to release resources.

PTY Session Information

List All PTY Sessions

const sessions = await sandbox.process.listPtySessions();

sessions.forEach(session => {
  console.log(`Session ID: ${session.id}`);
  console.log(`Active: ${session.active}`);
  console.log(`Created: ${session.createdAt}`);
  console.log(`Cols x Rows: ${session.cols}x${session.rows}`);
  console.log(`Working Directory: ${session.cwd}`);
  if (session.processId) {
    console.log(`Process ID: ${session.processId}`);
  }
  console.log('---');
});

Get Session Details

const session = await sandbox.process.getPtySessionInfo('my-session');

console.log(`Session ID: ${session.id}`);
console.log(`Active: ${session.active}`);
console.log(`Working Directory: ${session.cwd}`);
console.log(`Terminal Size: ${session.cols}x${session.rows}`);

if (session.processId) {
  console.log(`Process ID: ${session.processId}`);
}

Interactive Commands Example

Handle Interactive Prompts

const ptyHandle = await sandbox.process.createPty({
  id: 'interactive-session',
  cols: 120,
  rows: 30,
  onData: (data) => {
    const text = new TextDecoder().decode(data);
    process.stdout.write(text);
  }
});

await ptyHandle.waitForConnection();

// Send interactive command
await ptyHandle.sendInput('read -p "Enter your name: " name && echo "Hello, $name"\n');

// Wait for prompt
await new Promise(resolve => setTimeout(resolve, 1000));

// Send input
await ptyHandle.sendInput('Alice\n');

// Wait and exit
await new Promise(resolve => setTimeout(resolve, 1000));
await ptyHandle.sendInput('exit\n');

const result = await ptyHandle.wait();
console.log(`Session completed with exit code: ${result.exitCode}`);

await ptyHandle.disconnect();

Complete Example

import { Daytona, Sandbox } from '@daytonaio/sdk';

async function runPtySession(sandbox: Sandbox) {
  console.log('=== Creating Interactive PTY Session ===');
  
  const ptySessionId = 'demo-session';
  
  // Create PTY session
  const ptyHandle = await sandbox.process.createPty({
    id: ptySessionId,
    cwd: '/workspace',
    envs: {
      TERM: 'xterm-256color',
      LANG: 'en_US.UTF-8'
    },
    cols: 120,
    rows: 30,
    onData: (data) => {
      const text = new TextDecoder().decode(data);
      process.stdout.write(text);
    }
  });
  
  // Wait for connection
  await ptyHandle.waitForConnection();
  console.log('PTY connection established');
  
  // Run some commands
  console.log('\nRunning commands...');
  
  await ptyHandle.sendInput('echo "Current directory:"\n');
  await new Promise(resolve => setTimeout(resolve, 500));
  
  await ptyHandle.sendInput('pwd\n');
  await new Promise(resolve => setTimeout(resolve, 500));
  
  await ptyHandle.sendInput('echo "List files:"\n');
  await new Promise(resolve => setTimeout(resolve, 500));
  
  await ptyHandle.sendInput('ls -la\n');
  await new Promise(resolve => setTimeout(resolve, 1000));
  
  // Interactive command
  console.log('\nSending interactive command...');
  await ptyHandle.sendInput('printf "Enter a number: " && read num && echo "You entered: $num"\n');
  await new Promise(resolve => setTimeout(resolve, 1000));
  await ptyHandle.sendInput('42\n');
  await new Promise(resolve => setTimeout(resolve, 1000));
  
  // Resize terminal
  console.log('\nResizing terminal...');
  const info = await ptyHandle.resize(80, 25);
  console.log(`Resized to ${info.cols}x${info.rows}`);
  await new Promise(resolve => setTimeout(resolve, 500));
  
  // Exit
  console.log('\nExiting...');
  await ptyHandle.sendInput('exit\n');
  
  // Wait for completion
  const result = await ptyHandle.wait();
  console.log(`\nPTY session completed with exit code: ${result.exitCode}`);
  
  // Clean up
  await ptyHandle.disconnect();
}

async function killLongRunningSession(sandbox: Sandbox) {
  console.log('\n=== Kill Long-Running PTY Session ===');
  
  const ptyHandle = await sandbox.process.createPty({
    id: 'kill-session',
    cols: 120,
    rows: 30,
    onData: (data) => {
      const text = new TextDecoder().decode(data);
      process.stdout.write(text);
    }
  });
  
  await ptyHandle.waitForConnection();
  
  // Start infinite loop
  console.log('Starting long-running process...');
  await ptyHandle.sendInput('while true; do echo "Running... $(date)"; sleep 1; done\n');
  
  // Let it run briefly
  await new Promise(resolve => setTimeout(resolve, 3000));
  
  // Kill the session
  console.log('\nKilling PTY session...');
  await ptyHandle.kill();
  
  const result = await ptyHandle.wait();
  console.log(`Session terminated with exit code: ${result.exitCode}`);
  
  await ptyHandle.disconnect();
}

async function main() {
  const daytona = new Daytona();
  const sandbox = await daytona.create();
  
  try {
    await runPtySession(sandbox);
    await killLongRunningSession(sandbox);
  } catch (error) {
    console.error('Error:', error);
  } finally {
    console.log(`\nDeleting sandbox: ${sandbox.id}`);
    await daytona.delete(sandbox);
  }
}

main().catch(console.error);

Best Practices

Call waitForConnection() before sending input to ensure the PTY is ready:
const ptyHandle = await sandbox.process.createPty(options);
await ptyHandle.waitForConnection();
await ptyHandle.sendInput('command\n');
Give commands time to execute before sending the next one:
await ptyHandle.sendInput('ls -la\n');
await new Promise(resolve => setTimeout(resolve, 500));
await ptyHandle.sendInput('pwd\n');
Always disconnect when done:
try {
  // Use PTY
} finally {
  await ptyHandle.disconnect();
}
Check connection status and handle errors:
if (!ptyHandle.isConnected()) {
  console.error('PTY not connected');
  return;
}

const result = await ptyHandle.wait();
if (result.error) {
  console.error('PTY error:', result.error);
}

Use Cases

Run interactive shell commands with real-time feedback:
await ptyHandle.sendInput('python3\n');
await ptyHandle.sendInput('print("Hello")\n');
await ptyHandle.sendInput('exit()\n');
Monitor long-running processes with streaming output:
const ptyHandle = await sandbox.process.createPty({
  id: 'build-session',
  onData: (data) => {
    // Stream build output in real-time
    const text = new TextDecoder().decode(data);
    console.log(text);
  }
});

await ptyHandle.sendInput('npm run build\n');
Run full terminal applications like vim, htop, etc.:
await ptyHandle.sendInput('vim file.txt\n');
// Send vim commands
await ptyHandle.sendInput('i'); // Insert mode
await ptyHandle.sendInput('Hello World');
await ptyHandle.sendInput(new Uint8Array([27])); // ESC
await ptyHandle.sendInput(':wq\n'); // Save and quit
Test CLI applications with automated input:
await ptyHandle.sendInput('./my-cli-tool\n');
await ptyHandle.sendInput('option1\n');
await ptyHandle.sendInput('yes\n');

const result = await ptyHandle.wait();
console.log(`Test ${result.exitCode === 0 ? 'passed' : 'failed'}`);

PTY Handle Properties

interface PtyHandle {
  sessionId: string;              // PTY session ID
  exitCode?: number;              // Exit code (when terminated)
  error?: string;                 // Error message (if failed)
  
  // Methods
  sendInput(data: string | Uint8Array): Promise<void>;
  resize(cols: number, rows: number): Promise<PtySessionInfo>;
  wait(): Promise<PtyResult>;
  kill(): Promise<void>;
  disconnect(): Promise<void>;
  waitForConnection(): Promise<void>;
  isConnected(): boolean;
}

Process Execution

Execute commands and code in sandboxes

Computer Use

Desktop automation with GUI interaction

Build docs developers (and LLMs) love