WebTerminal Integration

AIO Sandbox provides a built-in Web Terminal page and exposes a Shell WebSocket endpoint so you can embed a terminal in your own local web UI.

The examples below assume AIO Sandbox is running at http://localhost:8080.

Built-In Terminal Page

Open:

http://localhost:8080/terminal

The page loads the xterm.js assets bundled inside the sandbox:

/static/sandbox/xterm@5.3.0/
/static/sandbox/xterm-addon-fit@0.8.0/

Local previews do not depend on an external CDN. The page connects to ws://localhost:8080/v1/shell/ws, creates a new terminal session, and writes the returned session_id into the URL so the UI can identify the active session.

WebSocket Endpoint

Connect your custom UI to a new session:

ws://localhost:8080/v1/shell/ws

After the connection is established, the server returns a session_id so your UI can identify the active session. After connection failures, create a new terminal session instead of relying on ws?session_id=... for disconnect recovery or history restoration.

Message Types

Client to server:

{ "type": "input", "data": "ls -la\n" }
{ "type": "resize", "data": { "cols": 120, "rows": 40 } }
{ "type": "pong", "data": { "timestamp": 1710000000000 } }

Server to client:

{ "type": "session_id", "data": "SESSION_ID" }
{ "type": "ready", "data": "Terminal ready" }
{ "type": "output", "data": "command output" }
{ "type": "ping", "timestamp": 1710000000000 }

input is the raw terminal input stream, so include a newline when executing commands. Send resize when the terminal container changes size. Only reply with pong after receiving an application-level ping.

Minimal Browser Example

This example loads xterm.js from AIO's bundled static assets and connects a local page to a local sandbox.

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <link
      rel="stylesheet"
      href="http://localhost:8080/static/sandbox/xterm@5.3.0/css/xterm.css"
    />
    <style>
      html,
      body,
      #terminal {
        width: 100%;
        height: 100%;
        margin: 0;
      }
    </style>
  </head>
  <body>
    <div id="terminal"></div>

    <script src="http://localhost:8080/static/sandbox/xterm@5.3.0/lib/xterm.js"></script>
    <script src="http://localhost:8080/static/sandbox/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js"></script>
    <script>
      const term = new Terminal({ cursorBlink: true, convertEol: true });
      const fitAddon = new FitAddon.FitAddon();
      term.loadAddon(fitAddon);
      term.open(document.getElementById("terminal"));
      fitAddon.fit();

      const ws = new WebSocket("ws://localhost:8080/v1/shell/ws");

      ws.addEventListener("open", () => {
        ws.send(JSON.stringify({
          type: "resize",
          data: { cols: term.cols, rows: term.rows },
        }));
      });

      term.onData((data) => {
        ws.send(JSON.stringify({ type: "input", data }));
      });

      ws.addEventListener("message", (event) => {
        const message = JSON.parse(event.data);

        if (message.type === "output") {
          term.write(message.data);
        }

        if (message.type === "ping") {
          ws.send(JSON.stringify({
            type: "pong",
            data: { timestamp: message.timestamp ?? message.data },
          }));
        }
      });

      window.addEventListener("resize", () => {
        fitAddon.fit();
        ws.send(JSON.stringify({
          type: "resize",
          data: { cols: term.cols, rows: term.rows },
        }));
      });
    </script>
  </body>
</html>

For the complete Shell protocol, see Shell Terminal.