The WebSocket terminal provides interactive, real-time access to a PTY (pseudo-terminal) in your sandbox. This enables you to run interactive commands, monitor output in real-time, and handle terminal-specific features like resizing.
Prerequisites
Before you begin, make sure you have:
- Active sandbox - A running sandbox (see Creating Sandboxes)
- WebSocket library - For Python:
pip install websockets (for JavaScript, WebSocket is built-in)
- Understanding of async/await - Familiarity with asynchronous programming patterns
- Terminal access - Basic understanding of terminal/command-line operations
Overview
The WebSocket terminal is ideal for:
- Interactive command-line tools and REPLs
- Real-time monitoring of long-running processes
- Terminal applications that require PTY support
- Building terminal UIs or dashboards
- Debugging and troubleshooting
The WebSocket terminal requires the websockets library for Python. Install it with: pip install websockets
Connecting to Terminal
Connect to the terminal using the terminal resource:
from hopx_ai import Sandbox
import asyncio
async def connect_terminal():
sandbox = Sandbox.create(template="code-interpreter")
# Connect to terminal
async with await sandbox.terminal.connect() as ws:
# Terminal is ready for input
pass
# Clean up
sandbox.kill()
asyncio.run(connect_terminal())
Send commands and input to the terminal:
async def send_commands():
sandbox = Sandbox.create(template="code-interpreter")
async with await sandbox.terminal.connect() as ws:
# Send a command (include \n to execute)
await sandbox.terminal.send_input(ws, "ls -la\n")
# Send another command
await sandbox.terminal.send_input(ws, "cd /workspace\n")
# Send interactive input
await sandbox.terminal.send_input(ws, "python3\n")
await sandbox.terminal.send_input(ws, "print('Hello')\n")
await sandbox.terminal.send_input(ws, "exit()\n")
sandbox.kill()
asyncio.run(send_commands())
Receiving Output
Receive terminal output in real-time using async iteration:
async def receive_output():
sandbox = Sandbox.create(template="code-interpreter")
async with await sandbox.terminal.connect() as ws:
# Send command
await sandbox.terminal.send_input(ws, "echo 'Hello World'\n")
# Receive output
async for message in sandbox.terminal.iter_output(ws):
if message['type'] == 'output':
print(message['data'], end='')
elif message['type'] == 'exit':
print(f"\nProcess exited with code: {message['code']}")
break
sandbox.kill()
asyncio.run(receive_output())
Resizing Terminal
Resize the terminal window to match your display:
async def resize_terminal():
sandbox = Sandbox.create(template="code-interpreter")
async with await sandbox.terminal.connect() as ws:
# Resize to 120 columns, 40 rows
await sandbox.terminal.resize(ws, cols=120, rows=40)
# Send command that benefits from larger terminal
await sandbox.terminal.send_input(ws, "ls -la\n")
async for message in sandbox.terminal.iter_output(ws):
if message['type'] == 'output':
print(message['data'], end='')
elif message['type'] == 'exit':
break
sandbox.kill()
asyncio.run(resize_terminal())
Complete Example
Here’s a complete example that demonstrates interactive terminal usage:
from hopx_ai import Sandbox
import asyncio
async def interactive_terminal():
sandbox = Sandbox.create(template="code-interpreter")
try:
async with await sandbox.terminal.connect() as ws:
# Resize terminal
await sandbox.terminal.resize(ws, cols=80, rows=24)
# Run a series of commands
commands = [
"pwd\n",
"whoami\n",
"uname -a\n",
"python3 --version\n"
]
for cmd in commands:
await sandbox.terminal.send_input(ws, cmd)
# Receive output for this command
async for message in sandbox.terminal.iter_output(ws):
if message['type'] == 'output':
print(message['data'], end='')
elif message['type'] == 'exit':
# Command completed, move to next
break
print() # Blank line between commands
finally:
sandbox.kill()
asyncio.run(interactive_terminal())
Message Types
The terminal sends different message types:
-
output: Terminal output data
{"type": "output", "data": "command output text"}
-
exit: Process exit notification
{"type": "exit", "code": 0}
Error Handling
Handle connection errors and timeouts:
import asyncio
from hopx_ai import Sandbox
async def terminal_with_error_handling():
sandbox = Sandbox.create(template="code-interpreter")
try:
async with await sandbox.terminal.connect(timeout=30) as ws:
await sandbox.terminal.send_input(ws, "invalid-command-that-fails\n")
async for message in sandbox.terminal.iter_output(ws):
if message['type'] == 'output':
print(message['data'], end='')
elif message['type'] == 'exit':
exit_code = message['code']
if exit_code != 0:
print(f"\nCommand failed with exit code: {exit_code}")
break
except Exception as e:
print(f"Terminal error: {e}")
finally:
sandbox.kill()
asyncio.run(terminal_with_error_handling())
Best Practices
Use async context managers in Python to ensure proper WebSocket cleanup. The async with statement automatically closes the connection.
Include newline characters (\n) when sending commands to execute them immediately. Without \n, the command waits for more input.
Terminal connections are stateful. Each connection maintains its own shell session. If you need a fresh environment, create a new terminal connection.
Resize the terminal before running commands that format output based on terminal width (like ls -la or tree).
API Reference
Python SDK
sandbox.terminal.connect(*, timeout=30) - Connect to terminal WebSocket
sandbox.terminal.send_input(ws, data) - Send input to terminal
sandbox.terminal.resize(ws, cols, rows) - Resize terminal window
sandbox.terminal.iter_output(ws) - Async iterator for terminal messages
JavaScript SDK
sandbox.terminal.connect() - Connect to terminal WebSocket
sandbox.terminal.sendInput(ws, data) - Send input to terminal
sandbox.terminal.resize(ws, cols, rows) - Resize terminal window
sandbox.terminal.output(ws) - Async iterator for terminal messages
API Endpoint
- WebSocket:
/terminal - Connect to interactive terminal
- Message Types:
input, resize, output, exit
Next Steps