#Interactive terminal (PTY)
<subtitle>Create an interactive terminal session in a sandbox with support for real-time output, input, window resizing and reconnection. </subtitle>

The PTY (pseudo-terminal) module allows you to create interactive terminal sessions in a sandbox and enable real-time two-way communication.

Unlike `commands.run()` which executes a command and returns output when finished, PTY provides:

- **Real-time streaming output**: Terminal output is returned in real-time through callbacks.
- **Bidirectional Input**: Input can continue to be sent while the terminal is running.
- **Interactive Shell**: Supports full terminal behavior, including ANSI colors and escape sequences.
- **Session Persistence**: Can disconnect and later reconnect to a still running session.

## Create PTY session

Use `sandbox.pty.create()` to start an interactive bash shell. The following example executes `echo 'hello world'`, then exits and prints the terminal output.

```python
from ucloud_sandbox import Sandbox, PtySize

sandbox = Sandbox.create()

terminal = sandbox.pty.create(
    size=PtySize(rows=24, cols=80), #Terminal character size
    envs={"MY_VAR": "hello"}, # Optional environment variable
    cwd="/home/user", # Optional working directory
    user="root", # Optional running user
)

# terminal.pid is the terminal process ID
print("Terminal PID:", terminal.pid)

# Send input to PTY. The data must be bytes, and a newline at the end is equivalent to pressing Enter.
sandbox.pty.send_stdin(terminal.pid, b"echo 'hello world'\n")

# PTY runs an interactive login shell and does not exit on its own, so exit needs to be sent explicitly.
# Otherwise the wait() below will always block.
sandbox.pty.send_stdin(terminal.pid, b"exit\n")

# Streaming output is received through the on_pty callback of wait().
# end='' can avoid extra newlines added by print.
terminal.wait(on_pty=lambda data: print(data.decode(), end=""))
```

> PTY will run `TERM=xterm-256color`'s interactive bash shell, supporting ANSI colors and escape sequences.

## time out

PTY sessions support configuring a timeout to control the session duration. The default timeout is 60 seconds. For interactive or long-running sessions, you can set `timeout=0` to keep the session open.

```python
from ucloud_sandbox import Sandbox, PtySize

sandbox = Sandbox.create()

terminal = sandbox.pty.create(
    PtySize(rows=24, cols=80),
    timeout=0, # Keep the session open
)

# end='' can avoid extra newlines added by print()
# PTY output itself usually already contains newlines
terminal.wait(on_pty=lambda data: print(data.decode(), end=""))
```

## Send input to PTY

Use `send_stdin()` to send data to the terminal. This method will complete the sending synchronously, and the actual output will be returned through the `on_pty` callback passed to `wait()`.

```python
from ucloud_sandbox import Sandbox, PtySize

sandbox = Sandbox.create()

terminal = sandbox.pty.create(PtySize(rows=24, cols=80))

# Send commands. The data needs to be bytes; don't forget the trailing newline.
sandbox.pty.send_stdin(terminal.pid, b'echo "Hello from PTY"\n')

# Receive output through the on_pty callback of wait().
# end='' can avoid extra newlines added by print.
terminal.wait(on_pty=lambda data: print(data.decode(), end=""))
```

## Adjust terminal size

When the user's terminal window changes size, `resize()` can be called to notify the PTY. `cols` and `rows` represent the number of characters, not pixels.

```python
from ucloud_sandbox import Sandbox, PtySize

sandbox = Sandbox.create()

terminal = sandbox.pty.create(PtySize(rows=24, cols=80))

# Adjust to the new terminal size, the unit is characters
sandbox.pty.resize(terminal.pid, PtySize(rows=40, cols=120))
```

## Disconnect and reconnect

You can disconnect from a PTY session while leaving the session running in the background; then reconnect with new data processing logic.

This applies to:

-Restore terminal session after network outage.
- Multiple clients share terminal access.
- Implement terminal session persistence.

```python
from ucloud_sandbox import Sandbox, PtySize

sandbox = Sandbox.create()

# Create PTY session
terminal = sandbox.pty.create(PtySize(rows=24, cols=80))

pid = terminal.pid

# Send command
sandbox.pty.send_stdin(pid, b"echo hello\n")

# Disconnect, PTY will continue to run in the background
terminal.disconnect()

# Reconnect to the same session later
reconnected = sandbox.pty.connect(pid)

# Continue using the session and then exit
sandbox.pty.send_stdin(pid, b"echo world\nexit\n")

# Wait for the terminal to exit and receive output via on_pty streaming
reconnected.wait(on_pty=lambda data: print("Handler:", data.decode()))
```

## Terminate PTY

Use `kill()` to terminate the PTY session.

```python
from ucloud_sandbox import Sandbox, PtySize

sandbox = Sandbox.create()

terminal = sandbox.pty.create(PtySize(rows=24, cols=80))

# Terminate PTY
killed = sandbox.pty.kill(terminal.pid)
print("Killed:", killed) # True when successful

# You can also use the handle method
# terminal.kill()
```

## Wait for PTY to exit

Use `wait()` to wait for the terminal session to end, such as after the user enters `exit`.

```python
from ucloud_sandbox import Sandbox, PtySize

sandbox = Sandbox.create()

terminal = sandbox.pty.create(PtySize(rows=24, cols=80))

# Send exit command
sandbox.pty.send_stdin(terminal.pid, b"exit\n")

# Wait for the terminal to exit and receive output via on_pty streaming
result = terminal.wait(on_pty=lambda data: print(data.decode(), end=""))
print("Exit code:", result.exit_code)
```

## Interactive terminal implementation

Building a complete interactive terminal like SSH requires handling raw mode, standard input forwarding, and terminal window size change events. In actual implementation, you can encapsulate your own terminal front-end or CLI interaction logic based on the `sandbox.pty` API introduced in this article.
