← Sandbox SDK / sandbox / 1-0-preview
Terminály
A terminál je interaktivní PTY v aktuálním kontejneru pro daný sandbox. Použijte ho pro obousměrné I/O terminálu: shell v prohlížeči, změnu velikosti, přerušení a opětovné připojení.
Spouštění příkazů využívá exec a handly procesů. Terminály jsou samostatný typ prostředku. Referenční dokumentace API: Terminals API.
Procesy a terminály
Proces (exec) |
Terminál | |
|---|---|---|
| Role | Sledovaný proces argv | Interaktivní PTY |
| Vstup | argv při spuštění (a vše, co si program načte sám) | Vstup PTY přes write() nebo prohlížeč connect() |
| Output | logs(), output(), čeká |
output(), snímek, waitForExit() |
| Zastavit | kill(signal?) |
interrupt() / terminate() |
| Vyhledání | getProcess / listProcesses |
getTerminal / listTerminals |
Oba typy prostředků existují pouze v aktuálním kontejneru pro dané ID sandboxu. Vyhledávací metody kontejner nespouštějí. Viz Životní cyklus sandboxu a Jak dlouho proces žije.
Vytvoření terminálu
const terminal = await sandbox.createTerminal({
command: ["bash"],
cwd: "/workspace",
cols: 120,
rows: 40,
});
console.log(terminal.id);const terminal = await sandbox.createTerminal({
command: ["bash"],
cwd: "/workspace",
cols: 120,
rows: 40,
});
console.log(terminal.id);Do PTY můžete zapisovat z Workeru, měnit jeho velikost, streamovat výstup nebo ho ukončit:
await terminal.write(new TextEncoder().encode("uname -a\n"));
await terminal.resize(100, 30);
await terminal.terminate();await terminal.write(new TextEncoder().encode("uname -a\n"));
await terminal.resize(100, 30);
await terminal.terminate();Lifetime
- Terminál existuje pouze v aktuální kontejner pro dané ID sandboxu.
getTerminal/listTerminalsvrátitnull/[]když neběží žádný kontejner. Žádný kontejner tím nespouštějí.- Jakmile se kontejner zastaví nebo je nahrazen, stará ID terminálů přestanou platit. Pokud terminál znovu potřebujete, vytvořte nový.
- Aktivní terminál dokáže udržet kontejner naživu napříč požadavky Worker, stejně jako to dokáže aktivní proces.
Uložte terminal.id k obnovení stejného PTY, dokud je daný kontejner stále spuštěný.
Připojení prohlížeče
- Vytvořte terminál a uchovejte si
terminal.ids ID sandboxu. - Při každém upgradu na WebSocket vyřešte terminál pomocí
getTerminal, pak se vraťteterminal.connect(request). - V prohlížeči použijte
@cloudflare/sandbox/xtermhodnotouterminalId.
Worker
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")?.toLowerCase() === "websocket"
) {
const sandboxId = url.searchParams.get("sandboxId");
const terminalId = url.searchParams.get("terminalId");
if (!sandboxId || !terminalId) {
return new Response("sandboxId and terminalId are required", {
status: 400,
});
}
const sandbox = getSandbox(env.Sandbox, sandboxId);
const terminal = await sandbox.getTerminal(terminalId);
if (!terminal) {
return new Response("Terminal not found", { status: 404 });
}
return terminal.connect(request, {
cursor: url.searchParams.get("cursor") ?? undefined,
});
}
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")?.toLowerCase() === "websocket"
) {
const sandboxId = url.searchParams.get("sandboxId");
const terminalId = url.searchParams.get("terminalId");
if (!sandboxId || !terminalId) {
return new Response("sandboxId and terminalId are required", {
status: 400,
});
}
const sandbox = getSandbox(env.Sandbox, sandboxId);
const terminal = await sandbox.getTerminal(terminalId);
if (!terminal) {
return new Response("Terminal not found", { status: 404 });
}
return terminal.connect(request, {
cursor: url.searchParams.get("cursor") ?? undefined,
});
}
return new Response("Not found", { status: 404 });
},
};Vytvářejte terminál z trasy aplikace ve chvíli, kdy jej uživatelské rozhraní potřebuje:
const sandboxId = "user-123";
const sandbox = getSandbox(env.Sandbox, sandboxId);
const terminal = await sandbox.createTerminal({ command: ["bash"] });
return Response.json({ sandboxId, terminalId: terminal.id });const sandboxId = "user-123";
const sandbox = getSandbox(env.Sandbox, sandboxId);
const terminal = await sandbox.createTerminal({ command: ["bash"] });
return Response.json({ sandboxId, terminalId: terminal.id });Prohlížeč (xterm.js)
npm install @xterm/xterm @xterm/addon-fit @cloudflare/sandbox@nextimport { Terminal } from "@xterm/xterm";
import { FitAddon } from "@xterm/addon-fit";
import { SandboxAddon } from "@cloudflare/sandbox/xterm";
import "@xterm/xterm/css/xterm.css";
const term = new Terminal({ cursorBlink: true });
const fitAddon = new FitAddon();
const sandboxAddon = new SandboxAddon({
// `origin` is already a WebSocket origin (`wss://` or `ws://`).
getWebSocketUrl: ({ sandboxId, terminalId, cursor, origin }) => {
const params = new URLSearchParams({ sandboxId });
if (terminalId) params.set("terminalId", terminalId);
if (cursor) params.set("cursor", cursor);
return `${origin}/ws/terminal?${params}`;
},
reconnect: true,
});
term.loadAddon(fitAddon);
term.loadAddon(sandboxAddon);
term.open(document.getElementById("terminal"));
fitAddon.fit();
// Values returned by your create-terminal route
const sandboxId = "user-123";
const terminalId = "term_...";
sandboxAddon.connect({ sandboxId, terminalId });import { Terminal } from "@xterm/xterm";
import { FitAddon } from "@xterm/addon-fit";
import { SandboxAddon } from "@cloudflare/sandbox/xterm";
import "@xterm/xterm/css/xterm.css";
const term = new Terminal({ cursorBlink: true });
const fitAddon = new FitAddon();
const sandboxAddon = new SandboxAddon({
// `origin` is already a WebSocket origin (`wss://` or `ws://`).
getWebSocketUrl: ({ sandboxId, terminalId, cursor, origin }) => {
const params = new URLSearchParams({ sandboxId });
if (terminalId) params.set("terminalId", terminalId);
if (cursor) params.set("cursor", cursor);
return `${origin}/ws/terminal?${params}`;
},
reconnect: true,
});
term.loadAddon(fitAddon);
term.loadAddon(sandboxAddon);
term.open(document.getElementById("terminal")!);
fitAddon.fit();
// Values returned by your create-terminal route
const sandboxId = "user-123";
const terminalId = "term_...";
sandboxAddon.connect({ sandboxId, terminalId });| Stabilní balíček | Náhled |
|---|---|
sandbox.terminal(request) |
createTerminal + getTerminal + connect |
xterm / URL sessionId |
terminalId (a volitelně cursor) |