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

# 対話型ターミナル (PTY)

export const SandboxConfigHint = () => {
  if (typeof document === "undefined") {
    return null;
  } else {
    return <Note>Before running the example code in this document, please ensure you have properly configured environment variables. For details, please refer to <a href="/docs/ja/guides/sandbox-your-first-agent-sandbox#configure-environment-variables">Configure Environment Variables</a>.</Note>;
  }
};

PTY（pseudo-terminal、擬似ターミナル）モジュールを使用すると、サンドボックス内でリアルタイムの双方向通信を備えた対話型ターミナルセッションを実行できます。

PTY セッションは **リアルタイムストリーミング** をサポートし、生成されたターミナル出力をコールバックを通じて継続的に配信します。また、**双方向入力** により、セッションの実行中にデータを送信できます。さらに、ANSI カラーやエスケープシーケンスを含む完全なターミナル動作を備えた **対話型シェル** 体験を提供し、**セッションの永続化** もサポートしているため、実行中のセッションをデタッチして後から再接続できます。

<SandboxConfigHint />

## PTY セッションを作成する

`sandbox.pty.create()` を使用して、対話型の bash シェルを開始できます。

<CodeGroup>
  ```js JavaScript & TypeScript icon="js" theme={"system"}
  import { Sandbox } from 'novita-sandbox/code-interpreter'

  const sandbox = await Sandbox.create()

  const terminal = await sandbox.pty.create({
    cols: 80,              // Terminal width in characters
    rows: 24,              // Terminal height in characters
    onData: (data) => {
      // Called whenever terminal outputs data
      process.stdout.write(data)
    },
    envs: { MY_VAR: 'hello' },  // Optional environment variables
    cwd: '/home/user',          // Optional working directory
    user: 'root',               // Optional user to run as
  })

  // terminal.pid contains the process ID
  console.log('Terminal PID:', terminal.pid)
  ```

  ```python Python icon="python" theme={"system"}
  import threading

  from novita_sandbox.code_interpreter import Sandbox, PtySize

  sandbox = Sandbox.create()

  terminal = sandbox.pty.create(
      size=PtySize(rows=24, cols=80),  # PtySize is (rows, cols)
      envs={'MY_VAR': 'hello'},        # Optional environment variables
      cwd='/home/user',                # Optional working directory
      user='root',                     # Optional user to run as
  )

  # terminal.pid contains the process ID
  print('Terminal PID:', terminal.pid)

  # The Python SDK has no on_data param. Output is streamed via wait(on_pty=...),
  # which blocks, so run it in a background thread.
  threading.Thread(
      target=lambda: terminal.wait(on_pty=lambda data: print(data.decode(), end='')),
      daemon=True,
  ).start()
  ```
</CodeGroup>

<Note>
  PTY は `TERM=xterm-256color` を使用して対話型の bash シェルを起動するため、ANSI カラーやエスケープシーケンスは期待どおりに動作します。
</Note>

## タイムアウト

タイムアウト設定は構成可能で、PTY セッションがどのくらいの時間アクティブなままでいるかを決定します。JavaScript では `timeoutMs: 0`、Python では `timeout=0` を設定することで、PTY セッションを無期限に開いたままにできます。デフォルトでは、セッションは 60 秒のタイムアウトを使用します。

<CodeGroup>
  ```js JavaScript & TypeScript icon="js" theme={"system"}
  import { Sandbox } from 'novita-sandbox/code-interpreter'

  const sandbox = await Sandbox.create()

  const terminal = await sandbox.pty.create({
    cols: 80,
    rows: 24,
    onData: (data) => process.stdout.write(data),
    timeoutMs: 0,  // Keep the session open indefinitely
  })
  ```

  ```python Python icon="python" theme={"system"}
  from novita_sandbox.code_interpreter import Sandbox, PtySize

  sandbox = Sandbox.create()

  terminal = sandbox.pty.create(
      size=PtySize(rows=24, cols=80),
      timeout=0,  # Keep the session open indefinitely
  )
  ```
</CodeGroup>

## PTY に入力を送信する

JavaScript では `sendInput()`、Python では `send_stdin()` を使用して、ターミナルにデータを送信できます。

JavaScript では、`sendInput()` は Promise を返し、ターミナル出力は直接返されるのではなく、`onData` コールバックを通じて配信されます。
Python では、`send_stdin()` は同期的に完了し、ターミナル出力は `wait()` に渡された `on_pty` コールバックを通じて配信されます。

<CodeGroup>
  ```js JavaScript & TypeScript icon="js" theme={"system"}
  import { Sandbox } from 'novita-sandbox/code-interpreter'

  const sandbox = await Sandbox.create()

  const terminal = await sandbox.pty.create({
    cols: 80,
    rows: 24,
    onData: (data) => process.stdout.write(data),
  })

  // Send a command (don't forget the newline!)
  await sandbox.pty.sendInput(
    terminal.pid,
    new TextEncoder().encode('echo "Hello from PTY"\n')
  )
  ```

  ```python Python icon="python" theme={"system"}
  import threading

  from novita_sandbox.code_interpreter import Sandbox, PtySize

  sandbox = Sandbox.create()

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

  # Stream output in a background thread (Python uses wait(on_pty=...))
  threading.Thread(
      target=lambda: terminal.wait(on_pty=lambda data: print(data.decode(), end='')),
      daemon=True,
  ).start()

  # Send a command as bytes (b'...' is Python's byte string syntax)
  # Don't forget the newline!
  sandbox.pty.send_stdin(terminal.pid, b'echo "Hello from PTY"\n')
  ```
</CodeGroup>

## ターミナルのサイズを変更する

ユーザーがターミナルウィンドウのサイズを変更したときに、`resize()` を使用して PTY に通知できます。
cols と rows の値は、ピクセルではなく文字数で表されるターミナルの寸法を示します。

<CodeGroup>
  ```js JavaScript & TypeScript icon="js" theme={"system"}
  import { Sandbox } from 'novita-sandbox/code-interpreter'

  const sandbox = await Sandbox.create()

  const terminal = await sandbox.pty.create({
    cols: 80,
    rows: 24,
    onData: (data) => process.stdout.write(data),
  })

  // Resize to new dimensions (in characters)
  await sandbox.pty.resize(terminal.pid, {
    cols: 120,
    rows: 40,
  })
  ```

  ```python Python icon="python" theme={"system"}
  from novita_sandbox.code_interpreter import Sandbox, PtySize

  sandbox = Sandbox.create()

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

  # Resize to new dimensions (in characters)
  sandbox.pty.resize(terminal.pid, PtySize(rows=40, cols=120))
  ```
</CodeGroup>

## 切断と再接続

PTY セッションは、クライアントが切断した後もアクティブなままにできます。セッションからデタッチし、後で新しいデータハンドラーを使って再接続できます。

これは、ネットワーク中断からの復旧、複数クライアントからのターミナルアクセスのサポート、再接続をまたいだセッション状態の保持に使用できます。

<CodeGroup>
  ```js JavaScript & TypeScript icon="js" theme={"system"}
  import { Sandbox } from 'novita-sandbox/code-interpreter'

  const sandbox = await Sandbox.create()

  // Create a PTY session
  const terminal = await sandbox.pty.create({
    cols: 80,
    rows: 24,
    onData: (data) => console.log('Handler 1:', new TextDecoder().decode(data)),
  })

  const pid = terminal.pid

  // Send a command
  await sandbox.pty.sendInput(pid, new TextEncoder().encode('echo hello\n'))

  // Disconnect - PTY keeps running in the background
  await terminal.disconnect()

  // Later: reconnect with a new data handler
  const reconnected = await sandbox.pty.connect(pid, {
    onData: (data) => console.log('Handler 2:', new TextDecoder().decode(data)),
  })

  // Continue using the session
  await sandbox.pty.sendInput(pid, new TextEncoder().encode('echo world\n'))

  // Wait for the terminal to exit
  await reconnected.wait()
  ```

  ```python Python icon="python" theme={"system"}
  import threading
  import time

  from novita_sandbox.code_interpreter import Sandbox, PtySize

  sandbox = Sandbox.create()

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

  # Send a command
  sandbox.pty.send_stdin(pid, b'echo hello\n')
  time.sleep(0.5)

  # Disconnect - PTY keeps running in the background.
  # Don't disconnect while a wait() is iterating the same handle.
  terminal.disconnect()

  # Later: reconnect with a new handle and stream its output
  reconnected = sandbox.pty.connect(pid)
  threading.Thread(
      target=lambda: reconnected.wait(on_pty=lambda data: print('Handler 2:', data.decode())),
      daemon=True,
  ).start()

  # Continue using the session
  sandbox.pty.send_stdin(pid, b'echo world\n')
  time.sleep(1.5)
  ```
</CodeGroup>

## PTY を終了する

`kill()` を使用して、PTY セッションを終了できます。

<CodeGroup>
  ```js JavaScript & TypeScript icon="js" theme={"system"}
  import { Sandbox } from 'novita-sandbox/code-interpreter'

  const sandbox = await Sandbox.create()

  const terminal = await sandbox.pty.create({
    cols: 80,
    rows: 24,
    onData: (data) => process.stdout.write(data),
  })

  // Kill the PTY
  const killed = await sandbox.pty.kill(terminal.pid)
  console.log('Killed:', killed)  // true if successful

  // Or use the handle method
  // await terminal.kill()
  ```

  ```python Python icon="python" theme={"system"}
  from novita_sandbox.code_interpreter import Sandbox, PtySize

  sandbox = Sandbox.create()

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

  # Kill the PTY
  killed = sandbox.pty.kill(terminal.pid)
  print('Killed:', killed)  # True if successful

  # Or use the handle method
  # terminal.kill()
  ```
</CodeGroup>

## PTY の終了を待機する

ユーザーが `exit` と入力した場合など、ターミナルセッションが終了するまでブロックするには、`wait()` を使用できます。

<CodeGroup>
  ```js JavaScript & TypeScript icon="js" theme={"system"}
  import { Sandbox } from 'novita-sandbox/code-interpreter'

  const sandbox = await Sandbox.create()

  const terminal = await sandbox.pty.create({
    cols: 80,
    rows: 24,
    onData: (data) => process.stdout.write(data),
  })

  // Send exit command
  await sandbox.pty.sendInput(terminal.pid, new TextEncoder().encode('exit\n'))

  // Wait for the terminal to exit
  const result = await terminal.wait()
  console.log('Exit code:', result.exitCode)
  ```

  ```python Python icon="python" theme={"system"}
  from novita_sandbox.code_interpreter import Sandbox, PtySize

  sandbox = Sandbox.create()

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

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

  # wait() blocks until the terminal exits; pass on_pty to stream output
  result = terminal.wait(on_pty=lambda data: print(data.decode(), end=''))
  print('Exit code:', result.exit_code)
  ```
</CodeGroup>

## 対話型ターミナル（SSH のような）

上記で説明した同じ `sandbox.pty` API を使用して、raw mode、stdin の転送、ターミナルのリサイズイベントを処理することで、SSH のような完全に対話的なターミナルを作成できます。
