Shell WebSocket API
The Shell WebSocket API provides real-time terminal access with session management, command execution, and output streaming.
Connection
WebSocket Endpoint
ws://localhost:8080/v1/shell/ws
Connection Options
Parameter |
Type |
Required |
Description |
Example |
session_id |
string |
No |
Connect to existing session, creates new if omitted |
abc123-def456 |
Examples
Create new session:
const ws = new WebSocket('ws://localhost:8080/v1/shell/ws');
Connect to existing session:
const ws = new WebSocket('ws://localhost:8080/v1/shell/ws?session_id=abc123');
Message Protocol
All messages are JSON-formatted with type
and data
fields.
Client Messages (Send to Server)
Input Command
Send user input to terminal:
{
"type": "input",
"data": "ls -la\n"
}
Resize Terminal
Adjust terminal dimensions:
{
"type": "resize",
"data": {
"cols": 80,
"rows": 24
}
}
Heartbeat Response
Respond to server ping:
{
"type": "pong",
"timestamp": 1703123456789
}
Server Messages (Receive from Server)
Session ID Notification
Sent when new session is created:
{
"type": "session_id",
"data": "c4058a14-148f-4bcc-b608-a092245c6f16"
}
Terminal Output
Real-time command output:
{
"type": "output",
"data": "total 8\ndrwxr-xr-x 2 root root 4096 Jan 1 12:00 .\n"
}
Session Restored
When connecting to existing session:
{
"type": "terminal_restored",
"data": "Session abc123 restored"
}
Terminal Ready
New session is ready:
{
"type": "ready",
"data": "Terminal ready - Session: abc123"
}
Heartbeat Ping
Server keepalive (every 30 seconds):
{
"type": "ping",
"data": 1703123456789
}
Error Messages
Various error conditions:
{
"type": "error",
"data": "Session not found"
}
Complete Integration Example
Here's a full working example with xterm.js:
<!DOCTYPE html>
<html>
<head>
<title>AIO Sandbox Terminal</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css" />
<style>
body { margin: 0; padding: 20px; background: #1e1e1e; color: white; }
.terminal-container { width: 100%; height: 500px; background: #000; }
.controls { margin-bottom: 20px; }
button { padding: 8px 16px; margin-right: 10px; }
</style>
</head>
<body>
<div class="controls">
<button onclick="terminal.connect()">Connect</button>
<button onclick="terminal.disconnect()">Disconnect</button>
<button onclick="terminal.clear()">Clear</button>
</div>
<div id="terminal" class="terminal-container"></div>
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js"></script>
<script>
class AIOTerminal {
constructor() {
this.terminal = new Terminal({
cursorBlink: true,
fontSize: 14,
fontFamily: 'Monaco, Menlo, "Ubuntu Mono", monospace'
});
this.fitAddon = new FitAddon.FitAddon();
this.terminal.loadAddon(this.fitAddon);
this.terminal.open(document.getElementById('terminal'));
this.fitAddon.fit();
this.websocket = null;
this.sessionId = null;
// Handle user input
this.terminal.onData(data => this.sendInput(data));
this.terminal.onResize(({ cols, rows }) => this.sendResize(cols, rows));
}
connect(sessionId = null) {
const wsUrl = `ws://localhost:8080/v1/shell/ws${sessionId ? `?session_id=${sessionId}` : ''}`;
this.websocket = new WebSocket(wsUrl);
this.websocket.onopen = () => {
console.log('Connected to AIO Sandbox terminal');
this.sendResize(this.terminal.cols, this.terminal.rows);
};
this.websocket.onmessage = (event) => {
const message = JSON.parse(event.data);
this.handleMessage(message);
};
this.websocket.onclose = () => {
console.log('Terminal connection closed');
};
this.websocket.onerror = (error) => {
console.error('Terminal connection error:', error);
};
}
handleMessage(message) {
const { type, data } = message;
switch (type) {
case 'session_id':
this.sessionId = data;
console.log('Session ID:', data);
break;
case 'output':
case 'restore_output':
this.terminal.write(data);
break;
case 'ready':
case 'terminal_restored':
this.terminal.write(`\\x1b[32m${data}\\x1b[0m\\r\\n`);
break;
case 'ping':
this.sendMessage({ type: 'pong', timestamp: data });
break;
case 'error':
this.terminal.write(`\\r\\n\\x1b[31mError: ${data}\\x1b[0m\\r\\n`);
break;
default:
console.log('Unknown message type:', type, data);
}
}
sendMessage(message) {
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
this.websocket.send(JSON.stringify(message));
}
}
sendInput(data) {
this.sendMessage({ type: 'input', data });
}
sendResize(cols, rows) {
this.sendMessage({ type: 'resize', data: { cols, rows } });
}
disconnect() {
if (this.websocket) {
this.websocket.close();
}
}
clear() {
this.terminal.clear();
}
}
// Initialize terminal
const terminal = new AIOTerminal();
// Auto-connect on page load
window.addEventListener('load', () => {
terminal.connect();
});
</script>
</body>
</html>
Session Management
Session Features
- Persistence: Sessions survive WebSocket disconnections
- Restoration: Reconnect to existing sessions with command history
- Timeout: Sessions automatically expire after 1 hour of inactivity
- Concurrent: Multiple sessions can run simultaneously
Best Practices
- Store Session IDs: Save session IDs for reconnection
- Handle Disconnections: Implement automatic reconnection logic
- Heartbeat Handling: Always respond to ping messages
- Error Recovery: Handle connection errors gracefully
Error Handling
Common error scenarios:
Error |
Cause |
Solution |
Session not found |
Invalid session_id |
Create new session |
Connection refused |
Service unavailable |
Check AIO Sandbox status |
Rate limited |
Too many connections |
Implement connection pooling |
Next Steps
- File Operations: Learn about file system APIs → File API
- Integration Examples: See practical implementations → Terminal Examples
- Browser CDP: Combine with browser automation → Browser API