Daytona provides powerful pseudo terminal (PTY) capabilities through the process module in sandboxes. PTY sessions allow you to create interactive terminal sessions that can execute commands, handle user input, and manage terminal operations.
A PTY (Pseudo Terminal) is a virtual terminal interface that allows programs to interact with a shell as if they were connected to a real terminal. PTY sessions in Daytona enable:
- Interactive Development: REPLs, debuggers, and development tools
- Build Processes: Running and monitoring compilation, testing, or deployment
- System Administration: Remote server management and configuration
- User Interfaces: Terminal-based applications requiring user interaction
Create PTY session
Daytona provides methods to create an interactive terminal session that can execute commands and handle user input.
from daytona.common.pty import PtySize
pty_handle = sandbox.process.create_pty_session( id="my-session", cwd="/workspace", envs={"TERM": "xterm-256color"}, pty_size=PtySize(cols=120, rows=30))// Create a PTY session with custom configurationconst ptyHandle = await sandbox.process.createPty({ id: 'my-interactive-session', cwd: '/workspace', envs: { TERM: 'xterm-256color', LANG: 'en_US.UTF-8' }, cols: 120, rows: 30, onData: (data) => { // Handle terminal output const text = new TextDecoder().decode(data) process.stdout.write(text) },})
// Wait for connection to be establishedawait ptyHandle.waitForConnection()
// Send commands to the terminalawait ptyHandle.sendInput('ls -la\n')await ptyHandle.sendInput('echo "Hello, PTY!"\n')await ptyHandle.sendInput('exit\n')
// Wait for completion and get resultconst result = await ptyHandle.wait()console.log(`PTY session completed with exit code: ${result.exitCode}`)
// Clean upawait ptyHandle.disconnect()pty_size = Daytona::PtySize.new(rows: 30, cols: 120)pty_handle = sandbox.process.create_pty_session( id: 'my-interactive-session', cwd: '/workspace', envs: { 'TERM' => 'xterm-256color' }, pty_size: pty_size)
# Use the PTY sessionpty_handle.send_input("ls -la\n")pty_handle.send_input("echo 'Hello, PTY!'\n")pty_handle.send_input("exit\n")
# Handle outputpty_handle.each { |data| print data }
puts "PTY session completed with exit code: #{pty_handle.exit_code}"// Create a PTY session with custom configurationhandle, err := sandbox.Process.CreatePty(ctx, "my-interactive-session", options.WithCreatePtySize(types.PtySize{Cols: 120, Rows: 30}), options.WithCreatePtyEnv(map[string]string{"TERM": "xterm-256color"}),)if err != nil { log.Fatal(err)}defer handle.Disconnect()
// Wait for connection to be establishedif err := handle.WaitForConnection(ctx); err != nil { log.Fatal(err)}
// Send commands to the terminalhandle.SendInput([]byte("ls -la\n"))handle.SendInput([]byte("echo 'Hello, PTY!'\n"))handle.SendInput([]byte("exit\n"))
// Read output from channelfor data := range handle.DataChan() { fmt.Print(string(data))}
// Wait for completion and get resultresult, err := handle.Wait(ctx)if err != nil { log.Fatal(err)}fmt.Printf("PTY session completed with exit code: %d\n", *result.ExitCode)curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/pty' \ --request POST \ --header 'Content-Type: application/json' \ --data '{ "cols": 1, "cwd": "", "envs": { "additionalProperty": "" }, "id": "", "lazyStart": true, "rows": 1}'For more information, see the Python SDK, TypeScript SDK, Ruby SDK, Go SDK, and API references:
Connect to PTY session
Daytona provides methods to establish a connection to an existing PTY session, enabling interaction with a previously created terminal.
pty_handle = sandbox.process.connect_pty_session("my-session")// Connect to an existing PTY sessionconst handle = await sandbox.process.connectPty('my-session', { onData: (data) => { // Handle terminal output const text = new TextDecoder().decode(data) process.stdout.write(text) },})
// Wait for connection to be establishedawait handle.waitForConnection()
// Send commands to the existing sessionawait handle.sendInput('pwd\n')await handle.sendInput('ls -la\n')await handle.sendInput('exit\n')
// Wait for completionconst result = await handle.wait()console.log(`Session exited with code: ${result.exitCode}`)
// Clean upawait handle.disconnect()# Connect to an existing PTY sessionpty_handle = sandbox.process.connect_pty_session('my-session')pty_handle.send_input("echo 'Hello World'\n")pty_handle.send_input("exit\n")
# Handle outputpty_handle.each { |data| print data }
puts "Session exited with code: #{pty_handle.exit_code}"// Connect to an existing PTY sessionhandle, err := sandbox.Process.ConnectPty(ctx, "my-session")if err != nil { log.Fatal(err)}defer handle.Disconnect()
// Wait for connection to be establishedif err := handle.WaitForConnection(ctx); err != nil { log.Fatal(err)}
// Send commands to the existing sessionhandle.SendInput([]byte("pwd\n"))handle.SendInput([]byte("ls -la\n"))handle.SendInput([]byte("exit\n"))
// Read outputfor data := range handle.DataChan() { fmt.Print(string(data))}
// Wait for completionresult, err := handle.Wait(ctx)if err != nil { log.Fatal(err)}fmt.Printf("Session exited with code: %d\n", *result.ExitCode)curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/pty/{sessionId}/connect'For more information, see the Python SDK, TypeScript SDK, Ruby SDK, Go SDK, and API references:
connect_pty_session (Python SDK)
List PTY sessions
Daytona provides methods to list PTY sessions, allowing you to retrieve information about all PTY sessions, both active and inactive, that have been created in the sandbox.
# List all PTY sessionssessions = sandbox.process.list_pty_sessions()
for session in sessions: print(f"Session ID: {session.id}") print(f"Active: {session.active}") print(f"Created: {session.created_at}")// List all PTY sessionsconst sessions = await sandbox.process.listPtySessions()
for (const session of sessions) { console.log(`Session ID: ${session.id}`) console.log(`Active: ${session.active}`) console.log(`Created: ${session.createdAt}`) console.log('---')}# List all PTY sessionssessions = sandbox.process.list_pty_sessions
sessions.each do |session| puts "Session ID: #{session.id}" puts "Active: #{session.active}" puts "Terminal Size: #{session.cols}x#{session.rows}" puts '---'end// List all PTY sessionssessions, err := sandbox.Process.ListPtySessions(ctx)if err != nil { log.Fatal(err)}
for _, session := range sessions { fmt.Printf("Session ID: %s\n", session.Id) fmt.Printf("Active: %t\n", session.Active) fmt.Printf("Terminal Size: %dx%d\n", session.Cols, session.Rows) fmt.Println("---")}curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/pty'For more information, see the Python SDK, TypeScript SDK, Ruby SDK, Go SDK, and API references:
list_pty_sessions (Python SDK)
Get PTY session info
Daytona provides methods to get information about a specific PTY session, allowing you to retrieve comprehensive information about a specific PTY session including its current state, configuration, and metadata.
# Get details about a specific PTY sessionsession_info = sandbox.process.get_pty_session_info("my-session")
print(f"Session ID: {session_info.id}")print(f"Active: {session_info.active}")print(f"Working Directory: {session_info.cwd}")print(f"Terminal Size: {session_info.cols}x{session_info.rows}")// Get details about a specific PTY sessionconst 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}`)}# Get details about a specific PTY sessionsession_info = sandbox.process.get_pty_session_info('my-session')
puts "Session ID: #{session_info.id}"puts "Active: #{session_info.active}"puts "Working Directory: #{session_info.cwd}"puts "Terminal Size: #{session_info.cols}x#{session_info.rows}"// Get details about a specific PTY sessionsession, err := sandbox.Process.GetPtySessionInfo(ctx, "my-session")if err != nil { log.Fatal(err)}
fmt.Printf("Session ID: %s\n", session.Id)fmt.Printf("Active: %t\n", session.Active)fmt.Printf("Working Directory: %s\n", session.Cwd)fmt.Printf("Terminal Size: %dx%d\n", session.Cols, session.Rows)
if session.ProcessId != nil { fmt.Printf("Process ID: %d\n", *session.ProcessId)}curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/session/{sessionId}'For more information, see the Python SDK, TypeScript SDK, Ruby SDK, Go SDK, and API references:
get_pty_session_info (Python SDK)
getPtySessionInfo (TypeScript SDK)
Kill PTY session
Daytona provides methods to kill a PTY session, allowing you to forcefully terminate a PTY session and cleans up all associated resources.
# Kill a specific PTY sessionsandbox.process.kill_pty_session("my-session")
# Verify the session no longer existspty_sessions = sandbox.process.list_pty_sessions()for pty_session in pty_sessions: print(f"PTY session: {pty_session.id}")// Kill a specific PTY sessionawait sandbox.process.killPtySession('my-session')
// Verify the session is no longer activetry { const info = await sandbox.process.getPtySessionInfo('my-session') console.log(`Session still exists but active: ${info.active}`)} catch (error) { console.log('Session has been completely removed')}# Delete a specific PTY sessionsandbox.process.delete_pty_session('my-session')
# Verify the session no longer existssessions = sandbox.process.list_pty_sessionssessions.each do |session| puts "PTY session: #{session.id}"end// Kill a specific PTY sessionerr := sandbox.Process.KillPtySession(ctx, "my-session")if err != nil { log.Fatal(err)}
// Verify the session is no longer activesessions, err := sandbox.Process.ListPtySessions(ctx)if err != nil { log.Fatal(err)}
for _, session := range sessions { fmt.Printf("PTY session: %s\n", session.Id)}curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/session/{sessionId}' \ --request DELETEFor more information, see the Python SDK, TypeScript SDK, Ruby SDK, Go SDK, and API references:
Resize PTY session
Daytona provides methods to resize a PTY session, allowing you to change the terminal dimensions of an active PTY session. This sends a SIGWINCH signal to the shell process, allowing terminal applications to adapt to the new size.
from daytona.common.pty import PtySize
# Resize a PTY session to a larger terminalnew_size = PtySize(rows=40, cols=150)updated_info = sandbox.process.resize_pty_session("my-session", new_size)
print(f"Terminal resized to {updated_info.cols}x{updated_info.rows}")
# You can also use the PtyHandle's resize methodpty_handle.resize(new_size)// Resize a PTY session to a larger terminalconst updatedInfo = await sandbox.process.resizePtySession('my-session', 150, 40)console.log(`Terminal resized to ${updatedInfo.cols}x${updatedInfo.rows}`)
// You can also use the PtyHandle's resize methodawait ptyHandle.resize(150, 40) // cols, rows# Resize a PTY session to a larger terminalpty_size = Daytona::PtySize.new(rows: 40, cols: 150)session_info = sandbox.process.resize_pty_session('my-session', pty_size)
puts "Terminal resized to #{session_info.cols}x#{session_info.rows}"// Resize a PTY session to a larger terminalupdatedInfo, err := sandbox.Process.ResizePtySession(ctx, "my-session", types.PtySize{ Cols: 150, Rows: 40,})if err != nil { log.Fatal(err)}
fmt.Printf("Terminal resized to %dx%d\n", updatedInfo.Cols, updatedInfo.Rows)
// You can also use the PtyHandle's Resize methodinfo, err := handle.Resize(ctx, 150, 40)if err != nil { log.Fatal(err)}fmt.Printf("Terminal resized to %dx%d\n", info.Cols, info.Rows)curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/pty/{sessionId}/resize' \ --request POST \ --header 'Content-Type: application/json' \ --data '{ "cols": 1, "rows": 1}'For more information, see the Python SDK, TypeScript SDK, Ruby SDK, Go SDK, and API references:
resize_pty_session (Python SDK)
Interactive commands
Daytona provides methods to handle interactive commands with PTY sessions, allowing you to handle interactive commands that require user input and can be resized during execution.
import timefrom daytona import Daytona, Sandboxfrom daytona.common.pty import PtySize
def handle_pty_data(data: bytes): text = data.decode("utf-8", errors="replace") print(text, end="")
# Create PTY sessionpty_handle = sandbox.process.create_pty_session( id="interactive-session", pty_size=PtySize(cols=300, rows=100))
# Send interactive commandpty_handle.send_input('printf "Are you accepting the terms and conditions? (y/n): " && read confirm && if [ "$confirm" = "y" ]; then echo "You accepted"; else echo "You did not accept"; fi\n')time.sleep(1)pty_handle.send_input("y\n")
# Resize terminalpty_session_info = pty_handle.resize(PtySize(cols=210, rows=110))print(f"PTY session resized to {pty_session_info.cols}x{pty_session_info.rows}")
# Exit the sessionpty_handle.send_input('exit\n')
# Handle output using iteratorfor data in pty_handle: handle_pty_data(data)
print(f"Session completed with exit code: {pty_handle.exit_code}")import { Daytona, Sandbox } from '@daytonaio/sdk'
// Create PTY sessionconst ptyHandle = await sandbox.process.createPty({ id: 'interactive-session', cols: 300, rows: 100, onData: data => { const text = new TextDecoder().decode(data) process.stdout.write(text) },})
await ptyHandle.waitForConnection()
// Send interactive commandawait ptyHandle.sendInput( 'printf "Are you accepting the terms and conditions? (y/n): " && read confirm && if [ "$confirm" = "y" ]; then echo "You accepted"; else echo "You did not accept"; fi\n')await new Promise(resolve => setTimeout(resolve, 1000))await ptyHandle.sendInput('y\n')
// Resize terminalconst ptySessionInfo = await sandbox.process.resizePtySession( 'interactive-session', 210, 110)console.log( `\nPTY session resized to ${ptySessionInfo.cols}x${ptySessionInfo.rows}`)
// Exit the sessionawait ptyHandle.sendInput('exit\n')
// Wait for completionconst result = await ptyHandle.wait()console.log(`Session completed with exit code: ${result.exitCode}`)require 'daytona'
# Create PTY sessionpty_handle = sandbox.process.create_pty_session( id: 'interactive-session', pty_size: Daytona::PtySize.new(cols: 300, rows: 100))
# Handle output in a separate threadthread = Thread.new do pty_handle.each { |data| print data }end
# Send interactive commandpty_handle.send_input('printf "Are you accepting the terms and conditions? (y/n): " && read confirm && if [ "$confirm" = "y" ]; then echo "You accepted"; else echo "You did not accept"; fi' + "\n")sleep(1)pty_handle.send_input("y\n")
# Resize terminalpty_handle.resize(Daytona::PtySize.new(cols: 210, rows: 110))puts "\nPTY session resized"
# Exit the sessionpty_handle.send_input("exit\n")
# Wait for the thread to finishthread.join
puts "Session completed with exit code: #{pty_handle.exit_code}"// Create PTY sessionhandle, err := sandbox.Process.CreatePty(ctx, "interactive-session", options.WithCreatePtySize(types.PtySize{Cols: 300, Rows: 100}),)if err != nil { log.Fatal(err)}defer handle.Disconnect()
if err := handle.WaitForConnection(ctx); err != nil { log.Fatal(err)}
// Handle output in a goroutinego func() { for data := range handle.DataChan() { fmt.Print(string(data)) }}()
// Send interactive commandhandle.SendInput([]byte(`printf "Are you accepting the terms and conditions? (y/n): " && read confirm && if [ "$confirm" = "y" ]; then echo "You accepted"; else echo "You did not accept"; fi` + "\n"))time.Sleep(1 * time.Second)handle.SendInput([]byte("y\n"))
// Resize terminalinfo, err := handle.Resize(ctx, 210, 110)if err != nil { log.Fatal(err)}fmt.Printf("\nPTY session resized to %dx%d\n", info.Cols, info.Rows)
// Exit the sessionhandle.SendInput([]byte("exit\n"))
// Wait for completionresult, err := handle.Wait(ctx)if err != nil { log.Fatal(err)}fmt.Printf("Session completed with exit code: %d\n", *result.ExitCode)Long-running processes
Daytona provides methods to manage long-running processes with PTY sessions, allowing you to manage long-running processes that need to be monitored or terminated.
import timeimport threadingfrom daytona import Daytona, Sandboxfrom daytona.common.pty import PtySize
def handle_pty_data(data: bytes): text = data.decode("utf-8", errors="replace") print(text, end="")
# Create PTY sessionpty_handle = sandbox.process.create_pty_session( id="long-running-session", pty_size=PtySize(cols=120, rows=30))
# Start a long-running processpty_handle.send_input('while true; do echo "Running... $(date)"; sleep 1; done\n')
# Using thread and wait() method to handle PTY outputthread = threading.Thread(target=pty_handle.wait, args=(handle_pty_data, 10))thread.start()
time.sleep(3) # Let it run for a bit
print("Killing long-running process...")pty_handle.kill()
thread.join()
print(f"\nProcess terminated with exit code: {pty_handle.exit_code}")if pty_handle.error: print(f"Termination reason: {pty_handle.error}")import { Daytona, Sandbox } from '@daytonaio/sdk'
// Create PTY sessionconst ptyHandle = await sandbox.process.createPty({ id: 'long-running-session', cols: 120, rows: 30, onData: (data) => { const text = new TextDecoder().decode(data) process.stdout.write(text) },})
await ptyHandle.waitForConnection()
// Start a long-running processawait ptyHandle.sendInput('while true; do echo "Running... $(date)"; sleep 1; done\n')await new Promise(resolve => setTimeout(resolve, 3000)) // Let it run for a bit
console.log('Killing long-running process...')await ptyHandle.kill()
// Wait for terminationconst result = await ptyHandle.wait()console.log(`\nProcess terminated with exit code: ${result.exitCode}`)if (result.error) { console.log(`Termination reason: ${result.error}`)}require 'daytona'
# Create PTY sessionpty_handle = sandbox.process.create_pty_session( id: 'long-running-session', pty_size: Daytona::PtySize.new(cols: 120, rows: 30))
# Handle output in a separate threadthread = Thread.new do pty_handle.each { |data| print data }end
# Start a long-running processpty_handle.send_input("while true; do echo \"Running... $(date)\"; sleep 1; done\n")sleep(3) # Let it run for a bit
puts "Killing long-running process..."pty_handle.kill
thread.join
puts "\nProcess terminated with exit code: #{pty_handle.exit_code}"puts "Termination reason: #{pty_handle.error}" if pty_handle.error// Create PTY sessionhandle, err := sandbox.Process.CreatePty(ctx, "long-running-session", options.WithCreatePtySize(types.PtySize{Cols: 120, Rows: 30}),)if err != nil { log.Fatal(err)}defer handle.Disconnect()
if err := handle.WaitForConnection(ctx); err != nil { log.Fatal(err)}
// Handle output in a goroutinego func() { for data := range handle.DataChan() { fmt.Print(string(data)) }}()
// Start a long-running processhandle.SendInput([]byte(`while true; do echo "Running... $(date)"; sleep 1; done` + "\n"))time.Sleep(3 * time.Second) // Let it run for a bit
fmt.Println("Killing long-running process...")if err := handle.Kill(ctx); err != nil { log.Fatal(err)}
// Wait for terminationresult, err := handle.Wait(ctx)if err != nil { log.Fatal(err)}fmt.Printf("\nProcess terminated with exit code: %d\n", *result.ExitCode)if result.Error != nil { fmt.Printf("Termination reason: %s\n", *result.Error)}Resource management
Daytona provides methods to manage resource leaks with PTY sessions, allowing you to always clean up PTY sessions to prevent resource leaks.
# Python: Use try/finallypty_handle = Nonetry: pty_handle = sandbox.process.create_pty_session(id="session", pty_size=PtySize(cols=120, rows=30)) # Do work...finally: if pty_handle: pty_handle.kill()// TypeScript: Use try/finallylet ptyHandletry { ptyHandle = await sandbox.process.createPty({ id: 'session', cols: 120, rows: 30, }) // Do work...} finally { if (ptyHandle) await ptyHandle.kill()}# Ruby: Use begin/ensurepty_handle = nilbegin pty_handle = sandbox.process.create_pty_session( id: 'session', pty_size: Daytona::PtySize.new(cols: 120, rows: 30) ) # Do work...ensure pty_handle&.killend// Go: Use defer for cleanuphandle, err := sandbox.Process.CreatePty(ctx, "session", options.WithCreatePtySize(types.PtySize{Cols: 120, Rows: 30}),)if err != nil { log.Fatal(err)}defer handle.Disconnect()
// Do work...
// Or use Kill to terminate the processdefer handle.Kill(ctx)PtyHandle methods
Daytona provides methods to interact with PTY sessions, allowing you to send input, resize the terminal, wait for completion, and manage the WebSocket connection to a PTY session.
Send input
Daytona provides methods to send input to a PTY session, allowing you to send input data (keystrokes or commands) to the PTY session.
# Send a commandpty_handle.send_input("ls -la\n")
# Send user inputpty_handle.send_input("y\n")// Send a commandawait ptyHandle.sendInput('ls -la\n')
// Send raw bytesawait ptyHandle.sendInput(new Uint8Array([3])) // Ctrl+C# Send a commandpty_handle.send_input("ls -la\n")
# Send user inputpty_handle.send_input("y\n")// Send a commandhandle.SendInput([]byte("ls -la\n"))
// Send Ctrl+Chandle.SendInput([]byte{0x03})For more information, see the Python SDK, TypeScript SDK, Ruby SDK, and Go SDK references:
Wait for completion
Daytona provides methods to wait for a PTY process to exit and return the result, allowing you to wait for a PTY process to exit and return the result.
# Wait with a callback for output datadef handle_data(data: bytes): print(data.decode("utf-8", errors="replace"), end="")
result = pty_handle.wait(on_data=handle_data, timeout=30)print(f"Exit code: {result.exit_code}")// Wait for process to completeconst 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}`) }}# Wait by iterating over output (blocks until PTY session ends)pty_handle.each { |data| print data }
if pty_handle.exit_code == 0 puts 'Process completed successfully'else puts "Process failed with code: #{pty_handle.exit_code}" puts "Error: #{pty_handle.error}" if pty_handle.errorend// Wait for process to completeresult, err := handle.Wait(ctx)if err != nil { log.Fatal(err)}
if result.ExitCode != nil && *result.ExitCode == 0 { fmt.Println("Process completed successfully")} else { fmt.Printf("Process failed with code: %d\n", *result.ExitCode) if result.Error != nil { fmt.Printf("Error: %s\n", *result.Error) }}For more information, see the Python SDK, TypeScript SDK, Ruby SDK, and Go SDK references:
Wait for connection
Daytona provides methods to wait for the WebSocket connection to be established before sending input, allowing you to wait for the WebSocket connection to be established before sending input.
# Python handles connection internally during creation# No explicit wait needed// Wait for connection to be establishedawait ptyHandle.waitForConnection()
// Now safe to send inputawait ptyHandle.sendInput('echo "Connected!"\n')# Ruby handles connection internally during creation# No explicit wait needed - can send input immediately after creationpty_handle.send_input("echo 'Connected!'\n")// Wait for connection to be establishedif err := handle.WaitForConnection(ctx); err != nil { log.Fatal(err)}
// Now safe to send inputhandle.SendInput([]byte("echo 'Connected!'\n"))For more information, see the Python SDK, TypeScript SDK, Ruby SDK, and Go SDK references:
Kill PTY process
Daytona provides methods to kill a PTY process and terminate the session from the handle.
pty_handle.kill()// Kill a long-running processawait ptyHandle.kill()
// Wait to confirm terminationconst result = await ptyHandle.wait()console.log(`Process terminated with exit code: ${result.exitCode}`)# Kill a long-running processpty_handle.kill
puts "Process terminated with exit code: #{pty_handle.exit_code}"// Kill a long-running processif err := handle.Kill(ctx); err != nil { log.Fatal(err)}
// Wait to confirm terminationresult, err := handle.Wait(ctx)if err != nil { log.Fatal(err)}fmt.Printf("Process terminated with exit code: %d\n", *result.ExitCode)For more information, see the Python SDK, TypeScript SDK, Ruby SDK, and Go SDK references:
Resize from handle
Daytona provides methods to resize the PTY terminal dimensions directly from the handle.
from daytona.common.pty import PtySize
pty_handle.resize(PtySize(cols=120, rows=30))// Resize to 120x30await ptyHandle.resize(120, 30)# Resize to 120x30pty_handle.resize(Daytona::PtySize.new(cols: 120, rows: 30))// Resize to 120x30info, err := handle.Resize(ctx, 120, 30)if err != nil { log.Fatal(err)}fmt.Printf("Resized to %dx%d\n", info.Cols, info.Rows)For more information, see the Python SDK, TypeScript SDK, Ruby SDK, and Go SDK references:
Disconnect
Daytona provides methods to disconnect from a PTY session and clean up resources without killing the process.
# Python: Use kill() to terminate, or let the handle go out of scope// Always clean up when donetry { // ... use PTY session} finally { await ptyHandle.disconnect()}# Ruby: Use begin/ensure or kill the sessionbegin # ... use PTY sessionensure pty_handle.killend// Always clean up when done using deferhandle, err := sandbox.Process.CreatePty(ctx, "session")if err != nil { log.Fatal(err)}defer handle.Disconnect()
// ... use PTY sessionFor more information, see the Python SDK, TypeScript SDK, Ruby SDK, and Go SDK references:
Check connection status
Daytona provides methods to check if a PTY session is still connected.
# Python: Check by attempting operations or using session infosession_info = sandbox.process.get_pty_session_info("my-session")print(f"Session active: {session_info.active}")if (ptyHandle.isConnected()) { console.log('PTY session is active')}# Ruby: Check by using session infosession_info = sandbox.process.get_pty_session_info('my-session')puts 'PTY session is active' if session_info.activeif handle.IsConnected() { fmt.Println("PTY session is active")}For more information, see the Python SDK, TypeScript SDK, Ruby SDK, and Go SDK references:
Exit code and error
Daytona provides methods to access the exit code and error message after a PTY process terminates.
# After iteration or wait completesprint(f"Exit code: {pty_handle.exit_code}")if pty_handle.error: print(f"Error: {pty_handle.error}")// Access via getters after process terminatesconsole.log(`Exit code: ${ptyHandle.exitCode}`)if (ptyHandle.error) { console.log(`Error: ${ptyHandle.error}`)}# Access after process terminatesputs "Exit code: #{pty_handle.exit_code}"puts "Error: #{pty_handle.error}" if pty_handle.error// Access via methods after process terminatesif exitCode := handle.ExitCode(); exitCode != nil { fmt.Printf("Exit code: %d\n", *exitCode)}if errMsg := handle.Error(); errMsg != nil { fmt.Printf("Error: %s\n", *errMsg)}For more information, see the Python SDK, TypeScript SDK, Ruby SDK, and Go SDK references:
Iterate over output (Python)
Daytona provides methods to iterate over a PTY handle to receive output data.
# Iterate over PTY outputfor data in pty_handle: text = data.decode("utf-8", errors="replace") print(text, end="")
print(f"Session ended with exit code: {pty_handle.exit_code}")// TypeScript uses the onData callback insteadconst ptyHandle = await sandbox.process.createPty({ id: 'my-session', onData: (data) => { const text = new TextDecoder().decode(data) process.stdout.write(text) },})# Iterate over PTY outputpty_handle.each do |data| print dataend
puts "Session ended with exit code: #{pty_handle.exit_code}"// Go uses a channel to receive output datafor data := range handle.DataChan() { fmt.Print(string(data))}
// Or use as io.Readerio.Copy(os.Stdout, handle)
fmt.Printf("Session ended with exit code: %d\n", *handle.ExitCode())Error handling
Daytona provides methods to monitor exit codes and handle errors appropriately with PTY sessions.
# Python: Check exit codesresult = pty_handle.wait()if result.exit_code != 0: print(f"Command failed: {result.exit_code}") print(f"Error: {result.error}")// TypeScript: Check exit codesconst result = await ptyHandle.wait()if (result.exitCode !== 0) { console.log(`Command failed: ${result.exitCode}`) console.log(`Error: ${result.error}`)}# Ruby: Check exit codes# The handle blocks until the PTY session completespty_handle.each { |data| print data }
if pty_handle.exit_code != 0 puts "Command failed: #{pty_handle.exit_code}" puts "Error: #{pty_handle.error}"end// Go: Check exit codesresult, err := handle.Wait(ctx)if err != nil { log.Fatal(err)}
if result.ExitCode != nil && *result.ExitCode != 0 { fmt.Printf("Command failed: %d\n", *result.ExitCode) if result.Error != nil { fmt.Printf("Error: %s\n", *result.Error) }}Troubleshooting
- Connection issues: verify sandbox status, network connectivity, and proper session IDs
- Performance issues: use appropriate terminal dimensions and efficient data handlers
- Process management: use explicit
kill()calls and proper timeout handling for long-running processes