← Sandbox SDK / sandbox / 1-0-preview / api
Процессы
Запускайте и отслеживайте управляемые процессы в текущем контейнере песочницы.
Общую модель работы см. в Выполнение процессов. Об интерактивном вводе PTY и терминалах в браузере см. Терминалы и Terminals API.
У дескрипторов процессов есть без стандартного ввода. Используйте cwd, env, и argv (или явный shell-скрипт) для неинтерактивной работы. Используйте терминал когда вам нужен интерактивный PTY.
exec()
Запустите процесс из argv (исполняемый файл, затем аргументы). Промис разрешается при успешном запуске, а не при завершении процесса. SDK не запускает shell и не экранирует argv: каждый элемент представляет собой один аргумент процесса.
exec(command: SandboxCommand, options?: ExecOptions): Promise<SandboxProcess>SandboxCommand
type SandboxCommand = readonly [executable: string, ...args: string[]];command[0]должен быть непустым путём или именем исполняемого файла.- Последующие аргументы могут быть пустыми строками.
- Записи передаются как есть (без экранирования оболочки для argv).
- Синтаксис shell требует явного указания shell, например
['/bin/bash', '-lc', script].
ExecOptions
| Поле | Тип | Описание |
|---|---|---|
cwd |
string |
Рабочий каталог для этого запуска. По умолчанию: /workspace если не установлено. |
env |
Record<string, string> |
Переопределение окружения для этого запуска. Не влияет на последующие запуски. env уровня sandbox по-прежнему применяется. |
timeout |
number |
Время жизни удалённого процесса в миллисекундах. Супервизор может остановить процесс; в этом случае завершение может сообщать timedOut: true. |
Возвращает
Promise<SandboxProcess>
const process = await sandbox.exec(["node", "--version"]);
const output = await process.output({ encoding: "utf8" });
console.log(process.id, process.pid, output.stdout, output.exitCode);const process = await sandbox.exec(["node", "--version"]);
const output = await process.output({ encoding: "utf8" });
console.log(process.id, process.pid, output.stdout, output.exitCode);getProcess()
Возвращает дескриптор для процесса, запущенного в текущий контейнер для этого сандбокса, или null.
Не запускает контейнер, если ни один не выполняется. Возвращает null когда контейнер не запущен, когда идентификатор процесса неизвестен в текущем контейнере или когда этот процесс принадлежал предыдущему контейнеру с тем же идентификатором песочницы.
getProcess(id: string): Promise<SandboxProcess | null>ID процессов не сохраняются при остановке или замене контейнера. См. Сколько живёт процесс.
listProcesses()
Выводит список процессов в текущем контейнере этой песочницы. Не запускает контейнер, если он не работает. Если контейнер не запущен, возвращает пустой список.
listProcesses(): Promise<ProcessStatus[]>Каждая запись представляет собой ProcessStatus значение (такой же структуры, как status()).
SandboxProcess
| Участник | Описание |
|---|---|
id |
ID процесса в текущем контейнере. |
pid |
PID контейнера при запуске. |
exitCode |
Promise<number> который разрешается, когда контролируемая группа процессов завершает работу. |
status() |
Текущий дискриминированный статус. |
logs(options?) |
Поток логов на основе курсора. |
output(options?) |
Буферизованные stdout и stderr вместе с метаданными завершения. |
waitForExit(options?) |
Дождитесь стабилизации отслеживаемой группы процессов. |
waitForLog(pattern, options?) |
Дождитесь совпадения stdout или stderr. |
waitForPort(port, options?) |
Дождитесь готовности порта или сбоя проверки готовности. |
kill(signal?) |
Отправьте числовой сигнал. По умолчанию 15 (SIGTERM). |
У этого дескриптора нет API stdin для процесса.
status()
status(): Promise<ProcessStatus>См. ProcessStatus. Процесс остается running пока контролируемый группа процессов устоялось, даже если корневой pid завершается, а потомки продолжают работу.
output()
Буферизует stdout и stderr до завершения процесса (или до окончания локального ожидания), а затем возвращает метаданные завершения.
output(options?: ProcessOutputOptions): Promise<ProcessOutput<Uint8Array>>
output(
options: ProcessOutputOptions & { encoding: "utf8" },
): Promise<ProcessOutput<string>>ProcessOutput
interface ProcessOutput<T = Uint8Array> {
stdout: T;
stderr: T;
exitCode: number;
signal?: number;
timedOut: boolean;
truncated: boolean;
}Кодировка тела по умолчанию: binary (Uint8Array) если только вы не передадите encoding: "utf8". Предпочитайте logs() когда объём вывода может превысить желаемый размер буфера.
ProcessOutputOptions
| Поле | Тип | Описание |
|---|---|---|
encoding |
"utf8" |
Декодируйте stdout/stderr в строки. |
maxBytes |
number |
Ограничивает количество буферизованных байт для каждой стороны потока результата. Можно задать truncated: true. Если параметр не указан, ограничение по умолчанию отсутствует. |
timeout |
number |
Только локальный предельный срок ожидания в миллисекундах; процесс не завершается. |
signal |
AbortSignal |
Отмените только это ожидание. Процесс не завершается. |
maxBytes при установке должен быть неотрицательным конечным числом.
const process = await sandbox.exec(["/bin/bash", "-lc", "npm test"], {
cwd: "/workspace/app",
});
const result = await process.output({ encoding: "utf8", timeout: 120_000 });
console.log(result.exitCode, result.stdout, result.timedOut, result.truncated);const process = await sandbox.exec(["/bin/bash", "-lc", "npm test"], {
cwd: "/workspace/app",
});
const result = await process.output({ encoding: "utf8", timeout: 120_000 });
console.log(result.exitCode, result.stdout, result.timedOut, result.truncated);logs()
Передаёт воспроизводимые события журнала с использованием непрозрачного курсора.
logs(options?: ProcessLogsOptions): Promise<ReadableStream<ProcessLogEvent>>ProcessLogsOptions
| Поле | Тип | Описание |
|---|---|---|
since |
string |
Непрозрачный курсор; возобновление после предыдущего события. |
replay |
boolean |
Включайте буферизованную историю при возобновлении. |
follow |
boolean |
Держите поток открытым для вывода в реальном времени. |
signal |
AbortSignal |
Отмените только эту подписку. Процесс продолжит работать. |
ProcessLogEvent
type ProcessLogEvent =
| {
type: "stdout" | "stderr";
cursor: string;
timestamp: string;
data: Uint8Array;
}
| {
type: "terminal";
state: "exited";
cursor: string;
timestamp: string;
exit: ProcessExit;
}
| {
type: "terminal";
state: "error";
cursor: string;
timestamp: string;
error: ProcessFailure;
}
| {
type: "truncated";
cursor?: string;
timestamp: string;
};Сохранение последних cursor из доставленных событий, если последующий запрос Worker возобновляется с logs({ since: cursor, replay: true, follow: true }) на одинаковый процесс в одинаковый контейнер.
const process = await sandbox.exec(["/bin/bash", "-lc", "npm run dev"], {
cwd: "/workspace/app",
});
const stream = await process.logs({ follow: true, replay: true });
const reader = stream.getReader();
const decoder = new TextDecoder();
for (;;) {
const { done, value } = await reader.read();
if (done) break;
if (value.type === "stdout" || value.type === "stderr") {
// Keep value.cursor if you will resume later
console.log(value.type, decoder.decode(value.data, { stream: true }));
continue;
}
if (value.type === "terminal") {
console.log("done", value.state);
break;
}
}const process = await sandbox.exec(["/bin/bash", "-lc", "npm run dev"], {
cwd: "/workspace/app",
});
const stream = await process.logs({ follow: true, replay: true });
const reader = stream.getReader();
const decoder = new TextDecoder();
for (;;) {
const { done, value } = await reader.read();
if (done) break;
if (value.type === "stdout" || value.type === "stderr") {
// Keep value.cursor if you will resume later
console.log(value.type, decoder.decode(value.data, { stream: true }));
continue;
}
if (value.type === "terminal") {
console.log("done", value.state);
break;
}
}waitForExit()
Дождитесь стабилизации отслеживаемой группы процессов.
waitForExit(options?: {
timeout?: number;
signal?: AbortSignal;
}): Promise<ProcessExit>| Поле | Тип | Описание |
|---|---|---|
timeout |
number |
Только локальный предельный срок ожидания; процесс не завершается. |
signal |
AbortSignal |
Отмените только это ожидание. Процесс не завершается. |
Возвращает ProcessExit. Локальный тайм-аут проявляется как ProcessWaitTimeoutError. Локальное прерывание проявляется как ProcessAbortedError.
const build = await sandbox.exec(["/bin/bash", "-lc", "npm run build"], {
cwd: "/workspace/app",
});
const exit = await build.waitForExit({ timeout: 600_000 });
console.log(exit.code, exit.signal, exit.timedOut);const build = await sandbox.exec(["/bin/bash", "-lc", "npm run build"], {
cwd: "/workspace/app",
});
const exit = await build.waitForExit({ timeout: 600_000 });
console.log(exit.code, exit.signal, exit.timedOut);waitForLog()
Дождитесь, пока stdout и (или) stderr совпадут с паттерном.
waitForLog(
pattern: string | RegExp,
options?: WaitForLogOptions,
): Promise<WaitForLogResult>WaitForLogOptions
| Поле | Тип | Описание |
|---|---|---|
stream |
"stdout" | "stderr" | "both" |
Какие потоки должны совпадать. По умолчанию: "both". |
timeout |
number |
Только локальный предельный срок ожидания; процесс не завершается. |
signal |
AbortSignal |
Отмените только это ожидание. Процесс не завершается. |
WaitForLogResult
interface WaitForLogResult {
stream: "stdout" | "stderr";
text: string;
match: string;
cursor?: string;
}textэто совпадающее окно декодированного вывода для этого потока.matchэто найденная подстрока.cursorэто курсор лога в точке совпадения, если он доступен.
Если процесс завершается до совпадения, SDK выбрасывает исключение ProcessExitedBeforeLogError. Локальный тайм-аут ожидания вызывает исключение ProcessWaitTimeoutError.
const server = await sandbox.exec(["/bin/bash", "-lc", "npm run dev"], {
cwd: "/workspace/app",
});
const ready = await server.waitForLog(/listening on/i, {
stream: "both",
timeout: 60_000,
});
console.log(ready.stream, ready.match);const server = await sandbox.exec(["/bin/bash", "-lc", "npm run dev"], {
cwd: "/workspace/app",
});
const ready = await server.waitForLog(/listening on/i, {
stream: "both",
timeout: 60_000,
});
console.log(ready.stream, ready.match);waitForPort()
Дождитесь готовности порта либо завершите с ошибкой, если процесс завершится раньше или закончится локальное ожидание.
waitForPort(port: number, options?: WaitForPortOptions): Promise<void>WaitForPortOptions
| Поле | Тип | Описание |
|---|---|---|
mode |
"tcp" | "http" |
Проверка готовности. По умолчанию: "tcp" (принимает TCP-соединение). |
path |
string |
HTTP-путь запроса, когда mode это "http". По умолчанию: "/". |
status |
number | { min: number; max: number } |
Ожидаемый статус HTTP или включающий диапазон, когда mode это "http". По умолчанию: { min: 200, max: 399 }. |
interval |
number |
Интервал между проверками в миллисекундах. По умолчанию: 500. |
timeout |
number |
Только локальный предельный срок ожидания; процесс не завершается. Если значение не указано, тайм-аут по умолчанию отсутствует. |
signal |
AbortSignal |
Отмените только это ожидание. Процесс не завершается. |
Режим TCP (по умолчанию) считается успешным, когда порт принимает соединение:
const db = await sandbox.exec(["redis-server"]);
await db.waitForPort(6379, {
mode: "tcp",
timeout: 10_000,
});const db = await sandbox.exec(["redis-server"]);
await db.waitForPort(6379, {
mode: "tcp",
timeout: 10_000,
});Режим HTTP отправляет HTTP запрос и проверяет статус ответа:
const server = await sandbox.exec(["/bin/bash", "-lc", "npm run dev"], {
cwd: "/workspace/app",
});
await server.waitForPort(3000, {
mode: "http",
path: "/health",
status: { min: 200, max: 299 },
timeout: 60_000,
});const server = await sandbox.exec(["/bin/bash", "-lc", "npm run dev"], {
cwd: "/workspace/app",
});
await server.waitForPort(3000, {
mode: "http",
path: "/health",
status: { min: 200, max: 299 },
timeout: 60_000,
});Типичные сбои:
ProcessReadyTimeoutError: порт не готов до истечения локального таймаутаProcessExitedBeforeReadyError: процесс завершился раньше, чем порт стал готовProcessAbortedError: локальныйAbortSignalотменил ожидание (процесс может всё ещё выполняться)
kill()
Отправьте процессу числовой сигнал.
kill(signal?: number): Promise<void>По умолчанию signal это 15 (SIGTERM). Передавайте только числовой сигнал (например, 9 для SIGKILL). Строковые имена сигналов не принимаются.
Остановка процесса не связана с отменой локального ожидания или подписки на логи.
exitCode
readonly exitCode: Promise<number>Возвращает код завершения после того, как контролируемая группа процессов завершит работу (та же граница завершения, что и у waitForExit()). Отдавайте предпочтение waitForExit() когда вам также нужен signal или timedOut.
ProcessStatus
type ProcessStatus =
| {
state: "running";
id: string;
pid: number;
command: SandboxCommand;
cwd?: string;
startedAt: string;
}
| {
state: "exited";
id: string;
pid: number;
command: SandboxCommand;
cwd?: string;
startedAt: string;
endedAt: string;
exit: ProcessExit;
}
| {
state: "error";
id: string;
pid: number;
command: SandboxCommand;
cwd?: string;
startedAt: string;
endedAt: string;
error: ProcessFailure;
};listProcesses() возвращает ProcessStatus[] используя такую структуру.
ProcessExit
Результат, наблюдаемый для корень подпроцесс, когда контролируемая группа завершает работу.
interface ProcessExit {
code: number;
signal?: number;
timedOut: boolean;
}Сигналы, доставленные только потомкам, не изменяют этот результат. См. Выполнение процессов.
ProcessFailure
interface ProcessFailure {
code: string;
message: string;
}Распространённые ошибки
getProcess и listProcesses не выбрасывают исключение при отсутствии задачи. Они возвращают null или [] когда контейнер не запущен, идентификатор неизвестен в текущем контейнере или процесс принадлежал предыдущему контейнеру. Следующие классы ошибок относятся к операциям с дескриптором процесса (и к запуску), а не к этим запросам.
| Ситуация | Класс / результат |
|---|---|
getProcess / listProcesses пока не запущен ни один контейнер |
null / [] (это не ошибка; контейнер при этом не запускается) |
getProcess для неизвестного ID или процесса из предыдущего контейнера |
null |
| Операция с дескриптором после замены контейнера | StaleProcessHandleError |
| Операция с дескриптором, когда процесс отсутствует в текущем контейнере | ProcessNotFoundError |
Истекло время локального ожидания (output / waitForExit / waitForLog) |
ProcessWaitTimeoutError |
Локальный AbortSignal при ожидании или потоковой передаче |
ProcessAbortedError |
| Порт не готов до истечения локального таймаута | ProcessReadyTimeoutError |
| Процесс завершился до готовности порта | ProcessExitedBeforeReadyError |
| Процесс завершился до появления совпадения в логе | ProcessExitedBeforeLogError |
| Недопустимый рабочий каталог при запуске | InvalidProcessCwdError |
| Недопустимое окружение при запуске | InvalidProcessEnvironmentError |
| Недопустимый курсор журнала | InvalidProcessCursorError |
| Не удалось запустить процесс | ProcessSpawnFailedError |
| Контейнер не готов, поэтому работа не началась | ContainerUnavailableError |
| Работа прервана, хотя могла уже начаться | OperationInterruptedError |
Рекомендации по восстановлению: Ошибки и восстановление. Полный каталог: API ошибок. Время жизни: Сколько живёт процесс.