← Sandbox SDK / sandbox / 1-0-preview / api
Терминалы
Создавайте интерактивные PTY-терминалы в текущем контейнере песочницы и управляйте ими.
Общую модель работы и пошаговое руководство по подключению браузера см. в Терминалы.
createTerminal()
Запустите терминал из argv (обычно shell). Промис разрешается при создании ресурса терминала. Действуют те же правила, что и для процесса exec: без неявного оборачивания в shell, аргументы argv не экранируются для shell.
createTerminal(options: CreateTerminalOptions): Promise<Terminal>CreateTerminalOptions
| Поле | Тип | Описание |
|---|---|---|
command |
SandboxCommand |
Argv для запуска в PTY. Обязательно. Пример: ['bash'] или ['/bin/bash']. |
cwd |
string |
Рабочий каталог для процесса терминала. |
env |
Record<string, string> |
Переопределение окружения для этого терминала. Не влияет на последующие запуски. |
cols |
number |
Начальная ширина в столбцах. |
rows |
number |
Начальная высота в строках. |
bufferSize |
number |
Размер буфера вывода для воспроизведения (если поддерживается средой выполнения). |
SandboxCommand это тот же тип argv, что и у process exec: readonly [executable: string, ...args: string[]].
Возвращает
Promise<Terminal> : handle для терминала в текущий контейнер.
const terminal = await sandbox.createTerminal({
command: ["bash"],
cwd: "/workspace",
env: { TERM: "xterm-256color" },
cols: 120,
rows: 40,
});
console.log(terminal.id);const terminal = await sandbox.createTerminal({
command: ["bash"],
cwd: "/workspace",
env: { TERM: "xterm-256color" },
cols: 120,
rows: 40,
});
console.log(terminal.id);getTerminal()
Возвращает дескриптор терминала в текущий контейнер, или null.
Не запускает контейнер, если ни один не выполняется. Возвращает null когда контейнер не запущен, когда идентификатор терминала неизвестен в текущем контейнере или когда этот терминал принадлежал предыдущему контейнеру с тем же идентификатором песочницы.
getTerminal(id: string): Promise<Terminal | null>listTerminals()
Выводит список терминалов в текущем контейнере этой песочницы. Не запускает контейнер, если он не работает. Если контейнер не запущен, возвращает пустой список.
listTerminals(): Promise<Terminal[]>Terminal
| Участник | Описание |
|---|---|
id |
Идентификатор терминала в текущем контейнере. |
getSnapshot() |
Текущий снимок (running / exited / error). |
write(data) |
Запись байтов в PTY (stdin). |
resize(cols, rows) |
Изменяет размер PTY. |
output(options?) |
Поток событий вывода на основе курсора. |
waitForExit(options?) |
Дождитесь завершения терминала. |
interrupt() |
Отправьте прерывание сессии терминала (например, семантику Ctrl-C). |
terminate() |
Завершите ресурс терминала. |
connect(request, opts?) |
Примите обновление WebSocket от браузера и подключите его к этому терминалу. |
getSnapshot()
interface TerminalSnapshot {
id: string;
pid?: number;
command: SandboxCommand;
cwd?: string;
status: "running" | "exited" | "error";
exit?: ProcessExit;
error?: ProcessFailure;
}write()
write(data: Uint8Array): Promise<void>Запись байтов в PTY. Нажатия клавиш в браузере обычно поступают через connect() взамен.
resize()
resize(cols: number, rows: number): Promise<void>output()
output(options?: TerminalOutputOptions): Promise<ReadableStream<TerminalOutputEvent>>TerminalOutputOptions
| Поле | Тип | Описание |
|---|---|---|
since |
string |
Непрозрачный курсор; возобновление после предыдущего события. |
replay |
boolean |
Включайте буферизованную историю при возобновлении. |
follow |
boolean |
Держите поток открытым для вывода в реальном времени. |
signal |
AbortSignal |
Отмените только эту подписку. Терминал продолжит работать. |
TerminalOutputEvent
type TerminalOutputEvent =
| {
type: "data";
terminalId: string;
cursor: string;
timestamp: string;
data: Uint8Array;
}
| {
type: "terminal";
terminalId: string;
cursor: string;
timestamp: string;
state: "exited";
exit: ProcessExit;
}
| {
type: "terminal";
terminalId: string;
cursor: string;
timestamp: string;
state: "error";
error: ProcessFailure;
}
| {
type: "truncated";
terminalId: string;
cursor?: string;
timestamp: string;
};Сохранение последних cursor из доставленных событий при повторном подключении или вызове output({ since, replay: true }) позже в одинаковый терминал в одинаковый контейнер.
waitForExit()
waitForExit(options?: {
timeout?: number;
signal?: AbortSignal;
}): Promise<ProcessExit>Локальный timeout / signal отменяют только ожидание. Они не завершают терминал. Вызовите terminate() или interrupt() когда вы собираетесь остановить его.
interrupt() и terminate()
interrupt(): Promise<void>
terminate(): Promise<void>Это операции управления терминалом. Они отличаются от операций с процессом kill(signal) на exec хендл.
connect()
Подключите к этому терминалу запрос на обновление WebSocket от браузера (или другого источника).
connect(
request: Request,
options?: {
cursor?: string;
cols?: number;
rows?: number;
},
): Promise<Response>requestдолжен быть запросом на обновление до WebSocket.cursorвозобновляет воспроизведение вывода после предыдущего отключения, если он есть у клиента.cols/rowsзадаёт размер PTY для этого подключения, если указан.
Возвращает обновление до WebSocket Response ваш Worker должен вернуть клиенту.
const url = new URL(request.url);
const terminalId = url.searchParams.get("terminalId");
if (!terminalId) {
return new Response("terminalId is required", { status: 400 });
}
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,
cols: 120,
rows: 40,
});const url = new URL(request.url);
const terminalId = url.searchParams.get("terminalId");
if (!terminalId) {
return new Response("terminalId is required", { status: 400 });
}
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,
cols: 120,
rows: 40,
});Полный путь с Worker и xterm.js см. в Терминалы.
Клиентский помощник: @cloudflare/sandbox/xterm
SandboxAddon интегрирует xterm.js ↗ с preview-терминалами.
import { SandboxAddon } from "@cloudflare/sandbox/xterm";
const addon = 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,
onStateChange: (state, error) => {
/* update UI */
},
});import { SandboxAddon } from "@cloudflare/sandbox/xterm";
const addon = 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,
onStateChange: (state, error) => {
/* update UI */
},
});| Элемент | Детали предпросмотра |
|---|---|
| Цель подключения | { sandboxId, terminalId? } |
getWebSocketUrl параметры |
sandboxId, terminalId?, cursor?, origin |
| Свойства | state, sandboxId, terminalId |
@xterm/xterm это необязательная peer зависимость предварительного пакета.
Распространённые ошибки
| Ситуация | Класс / результат |
|---|---|
| Неизвестный идентификатор терминала в текущем контейнере | TerminalNotFoundError |
getTerminal / listTerminals пока не запущен ни один контейнер |
null / [] (это не ошибка; контейнер при этом не запускается) |
| Дескриптор или ID терминала от предыдущего контейнера | StaleTerminalHandleError |
| Недопустимый рабочий каталог при создании | InvalidTerminalCwdError |
| Недопустимый курсор вывода | InvalidTerminalCursorError |
| Не удалось выполнить управляющую операцию | TerminalControlError |
Рекомендации по восстановлению: Ошибки и восстановление. Полный каталог: API ошибок. Время жизни: Сколько живёт процесс.