INTEGRITY Документация

Процессы

Запускайте и отслеживайте управляемые процессы в текущем контейнере песочницы.

Общую модель работы см. в Выполнение процессов. Об интерактивном вводе 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[]];

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;
}

Если процесс завершается до совпадения, 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,
});

Типичные сбои:

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 ошибок. Время жизни: Сколько живёт процесс.