> ## Documentation Index
> Fetch the complete documentation index at: https://bun-1dd33a4e-farm-e658f71f-system-wide-bunfig.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Spawn

> Spawn child processes with `Bun.spawn` or `Bun.spawnSync`

## Spawn a process (`Bun.spawn()`)

Provide a command as an array of strings. The result of `Bun.spawn()` is a `Bun.Subprocess` object.

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const proc = Bun.spawn(["bun", "--version"]);
console.log(await proc.exited); // 0
```

The second argument to `Bun.spawn` is a parameters object that configures the subprocess.

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const proc = Bun.spawn(["bun", "--version"], {
  cwd: "./path/to/subdir", // specify a working directory
  env: { ...process.env, FOO: "bar" }, // specify environment variables
  onExit(proc, exitCode, signalCode, error) {
    // exit handler
  },
});

proc.pid; // process ID of subprocess
```

## Input stream

By default, the input stream of the subprocess is undefined; configure it with the `stdin` parameter.

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const proc = Bun.spawn(["cat"], {
  stdin: await fetch("https://raw.githubusercontent.com/oven-sh/bun/main/examples/hashing.js"),
});

const text = await proc.stdout.text();
console.log(text); // "const input = "hello world".repeat(400); ..."
```

| Value                    | Description                                      |
| ------------------------ | ------------------------------------------------ |
| `null`                   | **Default.** Provide no input to the subprocess  |
| `"pipe"`                 | Return a `FileSink` for fast incremental writing |
| `"inherit"`              | Inherit the `stdin` of the parent process        |
| `Bun.file()`             | Read from the specified file                     |
| `TypedArray \| DataView` | Use a binary buffer as input                     |
| `Response`               | Use the response `body` as input                 |
| `Request`                | Use the request `body` as input                  |
| `ReadableStream`         | Use a readable stream as input                   |
| `Blob`                   | Use a blob as input                              |
| `number`                 | Read from the file with a given file descriptor  |

With `"pipe"`, the parent process can incrementally write to the subprocess's input stream.

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const proc = Bun.spawn(["cat"], {
  stdin: "pipe", // return a FileSink for writing
});

// enqueue string data
proc.stdin.write("hello");

// enqueue binary data
const enc = new TextEncoder();
proc.stdin.write(enc.encode(" world!"));

// send buffered data
proc.stdin.flush();

// close the input stream
proc.stdin.end();
```

Passing a `ReadableStream` to `stdin` pipes its data directly to the subprocess's input:

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const stream = new ReadableStream({
  start(controller) {
    controller.enqueue("Hello from ");
    controller.enqueue("ReadableStream!");
    controller.close();
  },
});

const proc = Bun.spawn(["cat"], {
  stdin: stream,
  stdout: "pipe",
});

const output = await proc.stdout.text();
console.log(output); // "Hello from ReadableStream!"
```

## Output streams

Read the subprocess's output from the `stdout` and `stderr` properties. By default these are instances of `ReadableStream`.

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const proc = Bun.spawn(["bun", "--version"]);
const text = await proc.stdout.text();
console.log(text); // => "1.3.3\n"
```

Configure the output stream by passing one of the following values to `stdout/stderr`:

| Value        | Description                                                                                         |
| ------------ | --------------------------------------------------------------------------------------------------- |
| `"pipe"`     | **Default for `stdout`.** Pipe the output to a `ReadableStream` on the returned `Subprocess` object |
| `"inherit"`  | **Default for `stderr`.** Inherit from the parent process                                           |
| `"ignore"`   | Discard the output                                                                                  |
| `Bun.file()` | Write to the specified file                                                                         |
| `number`     | Write to the file with the given file descriptor                                                    |

## Exit handling

Use the `onExit` callback to listen for the process exiting or being killed.

```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e-farm-e658f71f-system-wide-bunfig/NW8Sk37syP0KIejK/icons/typescript.svg?fit=max&auto=format&n=NW8Sk37syP0KIejK&q=85&s=a25d281cffed62521b09164567d2302e" theme={"theme":{"light":"github-light","dark":"dracula"}}
const proc = Bun.spawn(["bun", "--version"], {
  onExit(proc, exitCode, signalCode, error) {
    // exit handler
  },
});
```

The `exited` property is a `Promise` that resolves when the process exits.

```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e-farm-e658f71f-system-wide-bunfig/NW8Sk37syP0KIejK/icons/typescript.svg?fit=max&auto=format&n=NW8Sk37syP0KIejK&q=85&s=a25d281cffed62521b09164567d2302e" theme={"theme":{"light":"github-light","dark":"dracula"}}
const proc = Bun.spawn(["bun", "--version"]);

await proc.exited; // resolves when process exit
proc.killed; // boolean — was the process killed?
proc.exitCode; // null | number
proc.signalCode; // null | "SIGABRT" | "SIGALRM" | ...
```

To kill a process:

```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e-farm-e658f71f-system-wide-bunfig/NW8Sk37syP0KIejK/icons/typescript.svg?fit=max&auto=format&n=NW8Sk37syP0KIejK&q=85&s=a25d281cffed62521b09164567d2302e" theme={"theme":{"light":"github-light","dark":"dracula"}}
const proc = Bun.spawn(["bun", "--version"]);
proc.kill();
proc.killed; // true

proc.kill(15); // specify a signal code
proc.kill("SIGTERM"); // specify a signal name
```

The parent `bun` process does not terminate until all child processes have exited. Use `proc.unref()` to detach the child process from the parent.

```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e-farm-e658f71f-system-wide-bunfig/NW8Sk37syP0KIejK/icons/typescript.svg?fit=max&auto=format&n=NW8Sk37syP0KIejK&q=85&s=a25d281cffed62521b09164567d2302e" theme={"theme":{"light":"github-light","dark":"dracula"}}
const proc = Bun.spawn(["bun", "--version"]);
proc.unref();
```

## Resource usage

After the process exits, `resourceUsage()` reports its resource usage:

```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e-farm-e658f71f-system-wide-bunfig/NW8Sk37syP0KIejK/icons/typescript.svg?fit=max&auto=format&n=NW8Sk37syP0KIejK&q=85&s=a25d281cffed62521b09164567d2302e" theme={"theme":{"light":"github-light","dark":"dracula"}}
const proc = Bun.spawn(["bun", "--version"]);
await proc.exited;

const usage = proc.resourceUsage();
console.log(`Max memory used: ${usage.maxRSS} bytes`);
console.log(`CPU time (user): ${usage.cpuTime.user} µs`);
console.log(`CPU time (system): ${usage.cpuTime.system} µs`);
```

## Resource limits with cgroups (Linux)

On Linux, pass `cgroup` to start the subprocess inside a [control group](https://docs.kernel.org/admin-guide/cgroup-v2.html). The child joins the cgroup before it begins executing, so limits configured on the cgroup (memory, pids, CPU) apply from the first instruction. They also apply to every process the child spawns in turn. When a memory limit is exceeded the kernel OOM-kills a process *inside* the cgroup instead of reclaiming memory from the parent.

A cgroup is a directory under `/sys/fs/cgroup`; create and configure it with ordinary file operations, then pass its path (or an open directory file descriptor):

```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e-farm-e658f71f-system-wide-bunfig/NW8Sk37syP0KIejK/icons/typescript.svg?fit=max&auto=format&n=NW8Sk37syP0KIejK&q=85&s=a25d281cffed62521b09164567d2302e" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { mkdirSync, writeFileSync } from "node:fs";

const dir = "/sys/fs/cgroup/build-jobs";
mkdirSync(dir, { recursive: true });
writeFileSync(`${dir}/memory.max`, String(2 * 1024 ** 3)); // cgroup v1: memory.limit_in_bytes

const proc = Bun.spawn({
  cmd: ["make", "-j8"],
  cgroup: dir,
});
```

You can pass the same directory to any number of spawns; the limit applies to their combined usage. Bun supports both cgroup v1 and v2 hierarchies. Creating cgroups typically requires root or a delegated subtree. Bun ignores the option on other platforms; on Linux, the spawn fails if the child cannot join the cgroup.

## Using AbortSignal

You can abort a subprocess using an `AbortSignal`:

```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e-farm-e658f71f-system-wide-bunfig/NW8Sk37syP0KIejK/icons/typescript.svg?fit=max&auto=format&n=NW8Sk37syP0KIejK&q=85&s=a25d281cffed62521b09164567d2302e" theme={"theme":{"light":"github-light","dark":"dracula"}}
const controller = new AbortController();
const { signal } = controller;

const proc = Bun.spawn({
  cmd: ["sleep", "100"],
  signal,
});

// Later, to abort the process:
controller.abort();
```

## Using timeout and killSignal

Set `timeout` to terminate a subprocess after a duration in milliseconds:

```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e-farm-e658f71f-system-wide-bunfig/NW8Sk37syP0KIejK/icons/typescript.svg?fit=max&auto=format&n=NW8Sk37syP0KIejK&q=85&s=a25d281cffed62521b09164567d2302e" theme={"theme":{"light":"github-light","dark":"dracula"}}
// Kill the process after 5 seconds
const proc = Bun.spawn({
  cmd: ["sleep", "10"],
  timeout: 5000, // 5 seconds in milliseconds
});

await proc.exited; // Will resolve after 5 seconds
```

By default, Bun kills timed-out processes with `SIGTERM`. Specify a different signal with the `killSignal` option:

```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e-farm-e658f71f-system-wide-bunfig/NW8Sk37syP0KIejK/icons/typescript.svg?fit=max&auto=format&n=NW8Sk37syP0KIejK&q=85&s=a25d281cffed62521b09164567d2302e" theme={"theme":{"light":"github-light","dark":"dracula"}}
// Kill the process with SIGKILL after 5 seconds
const proc = Bun.spawn({
  cmd: ["sleep", "10"],
  timeout: 5000,
  killSignal: "SIGKILL", // Can be string name or signal number
});
```

The `killSignal` option also controls which signal Bun sends when an AbortSignal is aborted.

## Using maxBuffer

For `Bun.spawnSync`, `maxBuffer` limits how many bytes of output the process can emit before Bun kills it:

```ts index.ts icon="https://mintcdn.com/bun-1dd33a4e-farm-e658f71f-system-wide-bunfig/NW8Sk37syP0KIejK/icons/typescript.svg?fit=max&auto=format&n=NW8Sk37syP0KIejK&q=85&s=a25d281cffed62521b09164567d2302e" theme={"theme":{"light":"github-light","dark":"dracula"}}
// Kill 'yes' after it emits over 100 bytes of output
const result = Bun.spawnSync({
  cmd: ["yes"], // or ["bun", "exec", "yes"] on Windows
  maxBuffer: 100,
});
// process exits
```

Bun stops reading as soon as the limit is passed. The returned output can
therefore exceed `maxBuffer` only by the single read that passed it, never by
whatever the process manages to write before the kill lands. This matches
Node.js.

## Inter-process communication (IPC)

Bun supports a direct inter-process communication channel between two `bun` processes. To receive messages from a spawned Bun subprocess, specify an `ipc` handler.

```ts parent.ts icon="https://mintcdn.com/bun-1dd33a4e-farm-e658f71f-system-wide-bunfig/NW8Sk37syP0KIejK/icons/typescript.svg?fit=max&auto=format&n=NW8Sk37syP0KIejK&q=85&s=a25d281cffed62521b09164567d2302e" theme={"theme":{"light":"github-light","dark":"dracula"}}
const child = Bun.spawn(["bun", "child.ts"], {
  ipc(message) {
    /**
     * The message received from the sub process
     **/
  },
});
```

The parent process sends messages to the subprocess with the `.send()` method on the returned `Subprocess` instance. The `ipc` handler also receives the sending subprocess as its second argument.

```ts parent.ts icon="https://mintcdn.com/bun-1dd33a4e-farm-e658f71f-system-wide-bunfig/NW8Sk37syP0KIejK/icons/typescript.svg?fit=max&auto=format&n=NW8Sk37syP0KIejK&q=85&s=a25d281cffed62521b09164567d2302e" theme={"theme":{"light":"github-light","dark":"dracula"}}
const childProc = Bun.spawn(["bun", "child.ts"], {
  ipc(message, childProc) {
    /**
     * The message received from the sub process
     **/
    childProc.send("Respond to child");
  },
});

childProc.send("I am your father"); // The parent can send messages to the child as well
```

The child process sends messages to its parent with `process.send()` and receives them with `process.on("message")`. Node.js uses the same API for `child_process.fork()`.

```ts child.ts theme={"theme":{"light":"github-light","dark":"dracula"}}
process.send("Hello from child as string");
process.send({ message: "Hello from child as object" });

process.on("message", message => {
  // print message from parent
  console.log(message);
});
```

```ts child.ts theme={"theme":{"light":"github-light","dark":"dracula"}}
// send a string
process.send("Hello from child as string");

// send an object
process.send({ message: "Hello from child as object" });
```

The `serialization` option controls the underlying communication format between the two processes:

* `advanced`: (default) Bun serializes messages using the JSC `serialize` API, which supports cloning [everything `structuredClone` supports](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). This does not support transferring ownership of objects.
* `json`: Bun serializes messages using `JSON.stringify` and `JSON.parse`, which does not support as many object types as `advanced` does.

To disconnect the IPC channel from the parent process, call:

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
childProc.disconnect();
```

### IPC between Bun & Node.js

To use IPC between a `bun` process and a Node.js process, set `serialization: "json"` in `Bun.spawn`. This is because Node.js and Bun use different JavaScript engines with different object serialization formats.

```js bun-node-ipc.js icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
if (typeof Bun !== "undefined") {
  const prefix = `[bun ${process.versions.bun} 🐇]`;
  const node = Bun.spawn({
    cmd: ["node", __filename],
    ipc({ message }) {
      console.log(message);
      node.send({ message: `${prefix} 👋 hey node` });
      node.kill();
    },
    stdio: ["inherit", "inherit", "inherit"],
    serialization: "json",
  });

  node.send({ message: `${prefix} 👋 hey node` });
} else {
  const prefix = `[node ${process.version}]`;
  process.on("message", ({ message }) => {
    console.log(message);
    process.send({ message: `${prefix} 👋 hey bun` });
  });
}
```

***

## Terminal (PTY) support

For interactive terminal applications, use the `terminal` option to spawn a subprocess with a pseudo-terminal (PTY) attached. The subprocess sees a real terminal, which enables colored output, cursor movement, and interactive prompts.

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const proc = Bun.spawn(["bash"], {
  terminal: {
    cols: 80,
    rows: 24,
    data(terminal, data) {
      // Called when data is received from the terminal
      process.stdout.write(data);
    },
  },
});

// Write to the terminal
proc.terminal.write("echo hello\n");

// Wait for the process to exit
await proc.exited;

// Close the terminal
proc.terminal.close();
```

When you pass the `terminal` option:

* The subprocess sees `process.stdout.isTTY` as `true`
* `stdin`, `stdout`, and `stderr` are all connected to the terminal
* `proc.stdin`, `proc.stdout`, and `proc.stderr` return `null` — use the terminal instead
* Access the terminal via `proc.terminal`

### Terminal options

| Option  | Description                                                                                                                                                        | Default            |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ |
| `cols`  | Number of columns                                                                                                                                                  | `80`               |
| `rows`  | Number of rows                                                                                                                                                     | `24`               |
| `name`  | Terminal type for PTY configuration (set the `TERM` env var separately with the `env` option)                                                                      | `"xterm-256color"` |
| `data`  | Callback when data is received `(terminal, data) => void`                                                                                                          | —                  |
| `exit`  | Callback when PTY stream closes (EOF or error). `exitCode` is PTY lifecycle status (0=EOF, 1=error), not subprocess exit code. Use `proc.exited` for process exit. | —                  |
| `drain` | Callback when ready for more data `(terminal) => void`                                                                                                             | —                  |

### Terminal methods

The `Terminal` object returned by `proc.terminal` has the following methods:

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
// Write data to the terminal
proc.terminal.write("echo hello\n");

// Resize the terminal
proc.terminal.resize(120, 40);

// Set raw mode (disable line buffering and echo)
proc.terminal.setRawMode(true);

// Keep event loop alive while terminal is open
proc.terminal.ref();
proc.terminal.unref();

// Close the terminal
proc.terminal.close();
```

### Reusable Terminal

To run multiple commands in sequence through the same terminal session, create a terminal independently and reuse it across subprocesses:

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
await using terminal = new Bun.Terminal({
  cols: 80,
  rows: 24,
  data(term, data) {
    process.stdout.write(data);
  },
});

// Spawn first process
const proc1 = Bun.spawn(["echo", "first"], { terminal });
await proc1.exited;

// Reuse terminal for another process
const proc2 = Bun.spawn(["echo", "second"], { terminal });
await proc2.exited;

// Terminal is closed automatically by `await using`
```

When passing an existing `Terminal` object:

* You can reuse the terminal across multiple spawns
* You control when to close the terminal
* The `exit` callback fires when you call `terminal.close()`, not when each subprocess exits
* Use `proc.exited` to detect individual subprocess exits

### Platform differences

`Bun.Terminal` uses `openpty()` on Linux and macOS, and ConPTY (`CreatePseudoConsole`) on Windows. The core behavior is the same on every platform: the child sees a TTY, `write()` reaches the child's stdin, child output reaches the `data` callback, and `resize()` updates the child's view. A few details differ:

* **No termios on Windows.** `inputFlags`, `outputFlags`, `localFlags`, and `controlFlags` always read as `0` and setting them is a no-op. `setRawMode()` records the flag but has no effect on the child; the child controls its own console mode.
* **No echo without a child process on Windows.** On POSIX, the kernel line discipline echoes `write()` input back to the `data` callback even with no process attached. ConPTY has no line discipline; it buffers input for the next reader. If you need echo, spawn a process that echoes.
* **ConPTY re-encodes output.** ConPTY renders the child's output to a virtual screen and emits whatever VT sequences describe the result. The `data` callback therefore receives semantically equivalent, but not byte-identical, escape sequences. ConPTY preserves colors and text; it may reorder or coalesce cursor-positioning and reset sequences. ConPTY also emits a short VT init sequence (`\x1b[?9001h\x1b[?1004h…`) before any child output.
* **Input `\r` is not translated to `\n` on Windows.** POSIX `ICRNL` maps carriage return to newline on input; ConPTY passes `\r` through unchanged.
* **`process.on('SIGWINCH')` in the child does not fire under ConPTY** unless the child is reading stdin in raw mode. `process.stdout.columns`/`rows` do update after `resize()`. The missing signal is a libuv limitation that affects any libuv-based child (Node.js included).
* On Windows before 11 24H2 (build 26100), `terminal.close()` may not terminate a still-running child promptly. The delay comes from [`ClosePseudoConsole`](https://learn.microsoft.com/en-us/windows/console/closepseudoconsole), which blocks on those versions until conhost has flushed its output through the pipe. Kill the attached process first if you need to tear down with a running child.

***

## Blocking API (`Bun.spawnSync()`)

`Bun.spawnSync` is the blocking equivalent of `Bun.spawn`. It supports the same inputs and parameters and returns a `SyncSubprocess` object, which differs from `Subprocess` in a few ways.

1. The returned object contains a `success` property that indicates whether the process exited with a zero exit code.
2. The `stdout` and `stderr` properties are instances of `Buffer` instead of `ReadableStream`.
3. There is no `stdin` property. Use `Bun.spawn` to incrementally write to the subprocess's input stream.

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const proc = Bun.spawnSync(["echo", "hello"]);

console.log(proc.stdout.toString());
// => "hello\n"
```

As a rule of thumb, the asynchronous `Bun.spawn` API is better for HTTP servers and apps, and `Bun.spawnSync` is better for building command-line tools.

***

## Benchmarks

<Note>
  ⚡️ `Bun.spawn` and `Bun.spawnSync` use [`posix_spawn(3)`](https://man7.org/linux/man-pages/man3/posix_spawn.3.html).
</Note>

Bun's `spawnSync` spawns processes 60% faster than the Node.js `child_process` module.

```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun spawn.mjs
```

```txt theme={"theme":{"light":"github-light","dark":"dracula"}}
cpu: Apple M1 Max
runtime: bun 1.x (arm64-darwin)

benchmark              time (avg)             (min … max)       p75       p99      p995
--------------------------------------------------------- -----------------------------
spawnSync echo hi  888.14 µs/iter    (821.83 µs … 1.2 ms) 905.92 µs      1 ms   1.03 ms
```

```sh terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
node spawn.node.mjs
```

```txt theme={"theme":{"light":"github-light","dark":"dracula"}}
cpu: Apple M1 Max
runtime: node v18.9.1 (arm64-darwin)

benchmark              time (avg)             (min … max)       p75       p99      p995
--------------------------------------------------------- -----------------------------
spawnSync echo hi    1.47 ms/iter     (1.14 ms … 2.64 ms)   1.57 ms   2.37 ms   2.52 ms
```

***

## Reference

The following is a reference of the Spawn API and types. The real types have complex generics to strongly type the `Subprocess` streams with the options passed to `Bun.spawn` and `Bun.spawnSync`. For full details, see [bun.d.ts](https://github.com/oven-sh/bun/blob/main/packages/bun-types/bun.d.ts).

```ts See Typescript Definitions expandable theme={"theme":{"light":"github-light","dark":"dracula"}}
interface Bun {
  spawn(command: string[], options?: SpawnOptions.OptionsObject): Subprocess;
  spawnSync(command: string[], options?: SpawnOptions.OptionsObject): SyncSubprocess;

  spawn(options: { cmd: string[] } & SpawnOptions.OptionsObject): Subprocess;
  spawnSync(options: { cmd: string[] } & SpawnOptions.OptionsObject): SyncSubprocess;
}

namespace SpawnOptions {
  interface OptionsObject {
    cwd?: string;
    env?: Record<string, string | undefined>;
    stdio?: [Writable, Readable, Readable];
    stdin?: Writable;
    stdout?: Readable;
    stderr?: Readable;
    onExit?(
      subprocess: Subprocess,
      exitCode: number | null,
      signalCode: number | null,
      error?: ErrorLike,
    ): void | Promise<void>;
    ipc?(message: any, subprocess: Subprocess): void;
    serialization?: "json" | "advanced";
    windowsHide?: boolean;
    windowsVerbatimArguments?: boolean;
    argv0?: string;
    signal?: AbortSignal;
    timeout?: number;
    killSignal?: string | number;
    maxBuffer?: number;
    terminal?: TerminalOptions; // PTY (POSIX) / ConPTY (Windows) support
  }

  type Readable =
    | "pipe"
    | "inherit"
    | "ignore"
    | null // equivalent to "ignore"
    | undefined // to use default
    | BunFile
    | ArrayBufferView
    | number;

  type Writable =
    | "pipe"
    | "inherit"
    | "ignore"
    | null // equivalent to "ignore"
    | undefined // to use default
    | BunFile
    | ArrayBufferView
    | number
    | ReadableStream
    | Blob
    | Response
    | Request;
}

interface Subprocess extends AsyncDisposable {
  readonly stdin: FileSink | number | undefined | null;
  readonly stdout: ReadableStream<Uint8Array<ArrayBuffer>> | number | undefined | null;
  readonly stderr: ReadableStream<Uint8Array<ArrayBuffer>> | number | undefined | null;
  readonly readable: ReadableStream<Uint8Array<ArrayBuffer>> | number | undefined | null;
  readonly terminal: Terminal | undefined;
  readonly pid: number;
  readonly exited: Promise<number>;
  readonly exitCode: number | null;
  readonly signalCode: NodeJS.Signals | null;
  readonly killed: boolean;

  kill(exitCode?: number | NodeJS.Signals): void;
  ref(): void;
  unref(): void;

  send(message: any): void;
  disconnect(): void;
  resourceUsage(): ResourceUsage | undefined;
}

interface SyncSubprocess {
  stdout: Buffer | undefined;
  stderr: Buffer | undefined;
  exitCode: number;
  success: boolean;
  resourceUsage: ResourceUsage;
  signalCode?: string;
  exitedDueToTimeout?: true;
  pid: number;
}

interface TerminalOptions {
  cols?: number;
  rows?: number;
  name?: string;
  data?: (terminal: Terminal, data: Uint8Array<ArrayBuffer>) => void;
  /** Called when PTY stream closes (EOF or error). exitCode is PTY lifecycle status (0=EOF, 1=error), not subprocess exit code. */
  exit?: (terminal: Terminal, exitCode: number, signal: string | null) => void;
  drain?: (terminal: Terminal) => void;
}

interface Terminal extends AsyncDisposable {
  readonly closed: boolean;
  write(data: string | BufferSource): number;
  resize(cols: number, rows: number): void;
  setRawMode(enabled: boolean): void;
  ref(): void;
  unref(): void;
  close(): void;
}

interface ResourceUsage {
  contextSwitches: {
    voluntary: number;
    involuntary: number;
  };

  cpuTime: {
    user: number;
    system: number;
    total: number;
  };
  maxRSS: number;

  messages: {
    sent: number;
    received: number;
  };
  ops: {
    in: number;
    out: number;
  };
  shmSize: number;
  signalCount: number;
  swapCount: number;
}

type Signal =
  | "SIGABRT"
  | "SIGALRM"
  | "SIGBUS"
  | "SIGCHLD"
  | "SIGCONT"
  | "SIGFPE"
  | "SIGHUP"
  | "SIGILL"
  | "SIGINT"
  | "SIGIO"
  | "SIGIOT"
  | "SIGKILL"
  | "SIGPIPE"
  | "SIGPOLL"
  | "SIGPROF"
  | "SIGPWR"
  | "SIGQUIT"
  | "SIGSEGV"
  | "SIGSTKFLT"
  | "SIGSTOP"
  | "SIGSYS"
  | "SIGTERM"
  | "SIGTRAP"
  | "SIGTSTP"
  | "SIGTTIN"
  | "SIGTTOU"
  | "SIGUNUSED"
  | "SIGURG"
  | "SIGUSR1"
  | "SIGUSR2"
  | "SIGVTALRM"
  | "SIGWINCH"
  | "SIGXCPU"
  | "SIGXFSZ"
  | "SIGBREAK"
  | "SIGLOST"
  | "SIGINFO";
```
