INTEGRITY Dokumentace

Terminály v prohlížeči

Tato příručka ukazuje, jak připojit terminál v prohlížeči k shellu sandboxu. Můžete použít SandboxAddon s xterm.js, nebo se připojte přímo přes WebSockets.

Předpoklady

Potřebujete existující Cloudflare Worker s vazbou sandbox. Viz Začínáme pokud ho nemáte.

Nainstalujte závislosti terminálu do svého frontendového projektu:

npm install @xterm/xterm @xterm/addon-fit @cloudflare/sandbox

Pokud nepoužíváte xterm.js, potřebujete pouze @cloudflare/sandbox pro typy.

Zpracování upgradů WebSocket ve Workeru

Přidejte route, která přesměrovává WebSocket spojení na terminál sandboxu. Následující příklad podporuje jak výchozí, tak pojmenované relace prostřednictvím parametru dotazu:

import { getSandbox } from "@cloudflare/sandbox";

export { Sandbox } from "@cloudflare/sandbox";

export default {
	async fetch(request, env) {
		const url = new URL(request.url);

		if (
			url.pathname === "/ws/terminal" &&
			request.headers.get("Upgrade") === "websocket"
		) {
			const sandbox = getSandbox(env.Sandbox, "my-sandbox");
			const sessionId = url.searchParams.get("session");

			if (sessionId) {
				const session = await sandbox.getSession(sessionId);
				return await session.terminal(request);
			}

			return await sandbox.terminal(request, { cols: 80, rows: 24 });
		}

		return new Response("Not found", { status: 404 });
	},
};
import { getSandbox } from '@cloudflare/sandbox';

export { Sandbox } from '@cloudflare/sandbox';

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === '/ws/terminal' && request.headers.get('Upgrade') === 'websocket') {
      const sandbox = getSandbox(env.Sandbox, 'my-sandbox');
      const sessionId = url.searchParams.get('session');

      if (sessionId) {
        const session = await sandbox.getSession(sessionId);
        return await session.terminal(request);
      }

      return await sandbox.terminal(request, { cols: 80, rows: 24 });
    }

    return new Response('Not found', { status: 404 });
  }
};

Připojte se pomocí xterm.js a SandboxAddon

Vytvořte terminál v kódu prohlížeče a připojte SandboxAddon. Doplněk spravuje WebSocket připojení, automatické opětovné připojení a přeposílání změn velikosti.

import { Terminal } from "@xterm/xterm";
import { FitAddon } from "@xterm/addon-fit";
import { SandboxAddon } from "@cloudflare/sandbox/xterm";
import "@xterm/xterm/css/xterm.css";

const terminal = new Terminal({ cursorBlink: true });
const fitAddon = new FitAddon();
terminal.loadAddon(fitAddon);

const addon = new SandboxAddon({
	getWebSocketUrl: ({ sandboxId, sessionId, origin }) => {
		const params = new URLSearchParams({ id: sandboxId });
		if (sessionId) params.set("session", sessionId);
		return `${origin}/ws/terminal?${params}`;
	},
	onStateChange: (state, error) => {
		console.log(`Terminal ${state}`, error ?? "");
	},
});

terminal.loadAddon(addon);
terminal.open(document.getElementById("terminal"));
fitAddon.fit();

// Connect to the default session
addon.connect({ sandboxId: "my-sandbox" });

// Or connect to a specific session
// addon.connect({ sandboxId: 'my-sandbox', sessionId: 'development' });

window.addEventListener("resize", () => fitAddon.fit());
import { Terminal } from '@xterm/xterm';
import { FitAddon } from '@xterm/addon-fit';
import { SandboxAddon } from '@cloudflare/sandbox/xterm';
import '@xterm/xterm/css/xterm.css';

const terminal = new Terminal({ cursorBlink: true });
const fitAddon = new FitAddon();
terminal.loadAddon(fitAddon);

const addon = new SandboxAddon({
  getWebSocketUrl: ({ sandboxId, sessionId, origin }) => {
    const params = new URLSearchParams({ id: sandboxId });
    if (sessionId) params.set('session', sessionId);
    return `${origin}/ws/terminal?${params}`;
  },
  onStateChange: (state, error) => {
    console.log(`Terminal ${state}`, error ?? '');
  }
});

terminal.loadAddon(addon);
terminal.open(document.getElementById('terminal'));
fitAddon.fit();

// Connect to the default session
addon.connect({ sandboxId: 'my-sandbox' });

// Or connect to a specific session
// addon.connect({ sandboxId: 'my-sandbox', sessionId: 'development' });

window.addEventListener('resize', () => fitAddon.fit());

Úplné addon API najdete v Referenční dokumentace k Terminal API.

Připojte se bez xterm.js

Pokud vytváříte vlastní uživatelské rozhraní terminálu nebo pracujete v prostředí bez xterm.js, připojte se přímo přes WebSockets. Protokol používá binární rámce pro data terminálu a textové rámce JSON pro řídicí zprávy.

const ws = new WebSocket("wss://example.com/ws/terminal?id=my-sandbox");
ws.binaryType = "arraybuffer";

const decoder = new TextDecoder();
const encoder = new TextEncoder();

ws.addEventListener("message", (event) => {
	if (event.data instanceof ArrayBuffer) {
		// Terminal output (binary) — includes ANSI escape sequences
		const text = decoder.decode(event.data);
		appendToDisplay(text);
		return;
	}

	// Control message (JSON text)
	const msg = JSON.parse(event.data);

	switch (msg.type) {
		case "ready":
			// Terminal is accepting input — send initial resize
			ws.send(JSON.stringify({ type: "resize", cols: 80, rows: 24 }));
			break;

		case "exit":
			console.log(`Shell exited: code ${msg.code}`);
			break;

		case "error":
			console.error("Terminal error:", msg.message);
			break;
	}
});

// Send keystrokes as binary
function sendInput(text) {
	if (ws.readyState === WebSocket.OPEN) {
		ws.send(encoder.encode(text));
	}
}
const ws = new WebSocket('wss://example.com/ws/terminal?id=my-sandbox');
ws.binaryType = 'arraybuffer';

const decoder = new TextDecoder();
const encoder = new TextEncoder();

ws.addEventListener('message', (event) => {
  if (event.data instanceof ArrayBuffer) {
    // Terminal output (binary) — includes ANSI escape sequences
    const text = decoder.decode(event.data);
    appendToDisplay(text);
    return;
  }

  // Control message (JSON text)
  const msg = JSON.parse(event.data);

  switch (msg.type) {
    case 'ready':
      // Terminal is accepting input — send initial resize
      ws.send(JSON.stringify({ type: 'resize', cols: 80, rows: 24 }));
      break;

    case 'exit':
      console.log(`Shell exited: code ${msg.code}`);
      break;

    case 'error':
      console.error('Terminal error:', msg.message);
      break;
  }
});

// Send keystrokes as binary
function sendInput(text: string): void {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(encoder.encode(text));
  }
}

Klíčové podrobnosti protokolu:

Úplnou specifikaci protokolu najdete v Sekce o protokolu WebSocket v referenci API.

Osvědčené postupy