{"id":50708468,"url":"https://github.com/privatenumber/pty-spawn","last_synced_at":"2026-06-09T13:31:52.880Z","repository":{"id":338157097,"uuid":"1156555188","full_name":"privatenumber/pty-spawn","owner":"privatenumber","description":"Tiny pseudo-terminal spawning for humans","archived":false,"fork":false,"pushed_at":"2026-04-27T11:40:02.000Z","size":47,"stargazers_count":6,"open_issues_count":11,"forks_count":0,"subscribers_count":0,"default_branch":"develop","last_synced_at":"2026-05-18T07:26:29.744Z","etag":null,"topics":["node-pty","pty","spawn","subprocess","terminal"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/privatenumber.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"privatenumber"}},"created_at":"2026-02-12T19:30:27.000Z","updated_at":"2026-03-05T23:28:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/privatenumber/pty-spawn","commit_stats":null,"previous_names":["privatenumber/pty-spawn"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/privatenumber/pty-spawn","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/privatenumber%2Fpty-spawn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/privatenumber%2Fpty-spawn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/privatenumber%2Fpty-spawn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/privatenumber%2Fpty-spawn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/privatenumber","download_url":"https://codeload.github.com/privatenumber/pty-spawn/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/privatenumber%2Fpty-spawn/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34110011,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["node-pty","pty","spawn","subprocess","terminal"],"created_at":"2026-06-09T13:31:52.809Z","updated_at":"2026-06-09T13:31:52.873Z","avatar_url":"https://github.com/privatenumber.png","language":"TypeScript","funding_links":["https://github.com/sponsors/privatenumber"],"categories":[],"sub_categories":[],"readme":"# pty-spawn\n\nSpawn a PTY process and `await` it like a promise. Built on [`node-pty`](https://github.com/microsoft/node-pty).\n\n## Install\n\n```sh\nnpm install pty-spawn\n```\n\n## Quick start\n\n```ts\nimport { spawn, waitFor } from 'pty-spawn'\n\nconst subprocess = spawn('node', ['server.js'])\n\n// Wait for specific output\nawait waitFor(subprocess, output =\u003e output.includes('Listening on port 3000'))\n\n// Interact\nsubprocess.stdin.write('quit\\n')\n\n// Await final result\nconst result = await subprocess\nconsole.log(result.output)\n```\n\n## Why?\n\n`node-pty` gives you low-level event wiring. `pty-spawn` wraps it into a single awaitable object:\n\n| Without `pty-spawn` | With `pty-spawn` |\n| --- | --- |\n| Wire up promise + event cleanup | `await subprocess` |\n| Buffer output + poll + handle exit/timeout | `waitFor(subprocess, predicate)` |\n| Abort/exit race conditions | Late abort never overrides a clean exit |\n| Manual output accumulation loop | `subprocess.output` (live string) |\n\n## API\n\n### `spawn(file, args?, options?)`\n\nSpawns a PTY process. Returns a [`Subprocess`](#subprocess).\n\n```ts\nconst subprocess = spawn('node', ['server.js'], { timeout: 5000 })\n\n// Without args\nconst subprocess = spawn('bash', { window: { cols: 120 } })\n```\n\n#### Options\n\nAll [`node-pty` options](https://github.com/microsoft/node-pty) are supported, plus:\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `window` | `{ cols?, rows? }` | — | PTY window size |\n| `timeout` | `number` | `0` (disabled) | Auto-abort after _N_ ms |\n| `signal` | `AbortSignal` | — | Abort control |\n| `reject` | `boolean` | `true` | Reject on non-zero exit, signal, or abort. When `false`, always resolves |\n\n### Subprocess\n\nA `Promise\u003cResult\u003e` with control properties attached.\n\n#### Properties\n\n| Property | Type | Description |\n| --- | --- | --- |\n| `pid` | `number` | Process ID |\n| `output` | `string` | Accumulated output (live — grows as the process writes) |\n\n#### `stdin.write(data)`\n\nWrite to the process stdin.\n\n```ts\nsubprocess.stdin.write('hello\\n')\n```\n\n#### `kill(signal?, options?)`\n\nTerminate the process and wait for exit.\n\n```ts\nawait subprocess.kill()\nawait subprocess.kill('SIGTERM')\nawait subprocess.kill('SIGTERM', { forceKill: 3000 })\nawait subprocess.kill({ forceKill: 3000 })\n```\n\n`forceKill` escalates to `SIGKILL` after the given milliseconds if the process traps the initial signal. Safe to call after exit.\n\n#### `resize(cols, rows)`\n\nResize the PTY window. Safe to call after exit.\n\n#### Async iteration\n\nThe subprocess itself is `AsyncIterable\u003cstring\u003e` — stream output chunks in real time:\n\n```ts\nfor await (const chunk of subprocess) {\n    process.stdout.write(chunk)\n}\n```\n\n#### Async disposal\n\nSupports [`await using`](https://github.com/tc39/proposal-explicit-resource-management) for automatic cleanup:\n\n```ts\n{\n    await using subprocess = spawn('node', ['server.js'])\n    // killed when scope exits\n}\n```\n\n### Result\n\n`await subprocess` resolves with:\n\n| Property | Type | Description |\n| --- | --- | --- |\n| `output` | `string` | All terminal output |\n| `exitCode` | `number` | Process exit code |\n| `signalName` | `string?` | Signal name if terminated (e.g. `'SIGTERM'`) |\n| `file` | `string` | Spawned file path |\n| `args` | `string[]` | Arguments passed |\n| `durationMs` | `number` | Wall-clock duration in ms |\n\n\u003e [!NOTE]\n\u003e PTYs combine stdout and stderr into a single stream. `output` contains everything the process wrote to the terminal.\n\n### SubprocessError\n\nNon-zero exit or signal termination rejects with `SubprocessError`, which extends `Error` and includes all `Result` fields.\n\n```ts\nimport { spawn, SubprocessError } from 'pty-spawn'\n\ntry {\n    await subprocess\n} catch (error) {\n    if (error instanceof SubprocessError) {\n        error.exitCode // e.g. 1\n        error.signalName // e.g. 'SIGTERM'\n        error.output // captured output\n    }\n}\n```\n\nAbort and timeout behavior:\n- `timeout` and `signal` abort the process, rejecting with `SubprocessError`\n- `error.cause` contains the underlying reason (e.g. `TimeoutError`)\n- If the process exits cleanly before the abort fires, success wins the race\n\n### `waitFor(subprocess, predicate, options?)`\n\nWait for output to satisfy a predicate.\n\n```ts\nawait waitFor(subprocess, output =\u003e output.includes('Ready'))\n\n// With timeout\nawait waitFor(subprocess, output =\u003e output.includes('Ready'), {\n    signal: AbortSignal.timeout(5000)\n})\n```\n\nThe predicate receives accumulated output (since `waitFor` was called) and can be async. Calls are serialized — a slow predicate won't run concurrently.\n\n| Option | Type | Description |\n| --- | --- | --- |\n| `signal` | `AbortSignal` | Abort control (use `AbortSignal.timeout()` for timeouts) |\n\n## Inspiration\n\nThanks to [execa](https://github.com/sindresorhus/execa) and [nano-spawn](https://github.com/sindresorhus/nano-spawn) for the inspiration. They proved that `await spawn(...)` is the right primitive for child processes — pty-spawn brings that model to pseudo-terminals.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprivatenumber%2Fpty-spawn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprivatenumber%2Fpty-spawn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprivatenumber%2Fpty-spawn/lists"}