> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hopx.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# WebSocket Terminal

> Connect to an interactive terminal via WebSocket for real-time command execution in HopX sandboxes. Access interactive shell sessions, execute commands in real-time, and stream terminal output. Perfect for interactive development, debugging, and system administration. Includes Python and JavaScript SDK examples and WebSocket API endpoints.

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](/core-concepts/sandboxes/creating))
* **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

<Note>
  The WebSocket terminal requires the `websockets` library for Python. Install it with: `pip install websockets`
</Note>

## Connecting to Terminal

Connect to the terminal using the `terminal` resource:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    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())
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    import { Sandbox } from '@hopx-ai/sdk';

    const sandbox = await Sandbox.create({
      template: 'code-interpreter'
    });

    // Connect to terminal
    const ws = await sandbox.terminal.connect();

    // Terminal is ready for input
    // ... use terminal ...

    await sandbox.kill();
    ```
  </Tab>
</Tabs>

## Sending Input

Send commands and input to the terminal:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    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())
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const sandbox = await Sandbox.create({
      template: 'code-interpreter'
    });

    const ws = await sandbox.terminal.connect();

    // Send commands
    sandbox.terminal.sendInput(ws, 'ls -la\n');
    sandbox.terminal.sendInput(ws, 'cd /workspace\n');

    // Send interactive input
    sandbox.terminal.sendInput(ws, 'python3\n');
    sandbox.terminal.sendInput(ws, "print('Hello')\n");
    sandbox.terminal.sendInput(ws, 'exit()\n');

    await sandbox.kill();
    ```
  </Tab>
</Tabs>

## Receiving Output

Receive terminal output in real-time using async iteration:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    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())
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const sandbox = await Sandbox.create({
      template: 'code-interpreter'
    });

    const ws = await sandbox.terminal.connect();

    // Send command
    sandbox.terminal.sendInput(ws, "echo 'Hello World'\n");

    // Receive output
    for await (const message of sandbox.terminal.output(ws)) {
      if (message.type === 'output') {
        process.stdout.write(message.data);
      } else if (message.type === 'exit') {
        console.log(`\nProcess exited with code: ${message.code}`);
        break;
      }
    }

    await sandbox.kill();
    ```
  </Tab>
</Tabs>

## Resizing Terminal

Resize the terminal window to match your display:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    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())
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const sandbox = await Sandbox.create({
      template: 'code-interpreter'
    });

    const ws = await sandbox.terminal.connect();

    // Resize terminal
    sandbox.terminal.resize(ws, 120, 40);

    // Send command
    sandbox.terminal.sendInput(ws, 'ls -la\n');

    // Receive output
    for await (const message of sandbox.terminal.output(ws)) {
      if (message.type === 'output') {
        process.stdout.write(message.data);
      } else if (message.type === 'exit') {
        break;
      }
    }

    await sandbox.kill();
    ```
  </Tab>
</Tabs>

## Complete Example

Here's a complete example that demonstrates interactive terminal usage:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    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())
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    import { Sandbox } from '@hopx-ai/sdk';

    async function interactiveTerminal() {
      const sandbox = await Sandbox.create({
        template: 'code-interpreter'
      });
      
      try {
        const ws = await sandbox.terminal.connect();
        
        // Resize terminal
        sandbox.terminal.resize(ws, 80, 24);
        
        // Run a series of commands
        const commands = [
          'pwd\n',
          'whoami\n',
          'uname -a\n',
          'python3 --version\n'
        ];
        
        for (const cmd of commands) {
          sandbox.terminal.sendInput(ws, cmd);
          
          // Receive output for this command
          for await (const message of sandbox.terminal.output(ws)) {
            if (message.type === 'output') {
              process.stdout.write(message.data);
            } else if (message.type === 'exit') {
              // Command completed, move to next
              break;
            }
          }
          
          console.log(); // Blank line between commands
        }
      } finally {
        await sandbox.kill();
      }
    }

    interactiveTerminal();
    ```
  </Tab>
</Tabs>

## Message Types

The terminal sends different message types:

* **`output`**: Terminal output data
  ```json theme={null}
  {"type": "output", "data": "command output text"}
  ```

* **`exit`**: Process exit notification
  ```json theme={null}
  {"type": "exit", "code": 0}
  ```

## Error Handling

Handle connection errors and timeouts:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    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())
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    import { Sandbox } from '@hopx-ai/sdk';

    async function terminalWithErrorHandling() {
      const sandbox = await Sandbox.create({
        template: 'code-interpreter'
      });
      
      try {
        const ws = await sandbox.terminal.connect();
        
        sandbox.terminal.sendInput(ws, 'invalid-command-that-fails\n');
        
        for await (const message of sandbox.terminal.output(ws)) {
          if (message.type === 'output') {
            process.stdout.write(message.data);
          } else if (message.type === 'exit') {
            const exitCode = message.code;
            if (exitCode !== 0) {
              console.log(`\nCommand failed with exit code: ${exitCode}`);
            }
            break;
          }
        }
      } catch (error) {
        console.error(`Terminal error: ${error.message}`);
      } finally {
        await sandbox.kill();
      }
    }

    terminalWithErrorHandling();
    ```
  </Tab>
</Tabs>

## Best Practices

<Tip>
  **Use async context managers** in Python to ensure proper WebSocket cleanup. The `async with` statement automatically closes the connection.
</Tip>

<Tip>
  **Include newline characters** (`\n`) when sending commands to execute them immediately. Without `\n`, the command waits for more input.
</Tip>

<Warning>
  **Terminal connections are stateful**. Each connection maintains its own shell session. If you need a fresh environment, create a new terminal connection.
</Warning>

<Tip>
  **Resize the terminal** before running commands that format output based on terminal width (like `ls -la` or `tree`).
</Tip>

## 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`

## Related

* **[Running Commands](/core-concepts/commands/running)** - Execute shell commands synchronously
* **[Background Commands](/core-concepts/commands/background)** - Run commands in background
* **SDK**: [sandbox.terminal.connect()](/sdk/python/terminal#connect) - Python SDK method
* [CLI Terminal](/cli/commands/terminal) - Terminal access from CLI

## Next Steps

* Learn about [Running Commands](/core-concepts/commands/running) for synchronous execution
* Explore [Background Commands](/core-concepts/commands/background) for non-blocking tasks
* Review [Code Execution](/core-concepts/code-execution/synchronous) for programmatic execution
