@warlock.js/process 5.33.0 → 5.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/cjs/index.cjs +5 -2
- package/cjs/index.cjs.map +1 -1
- package/esm/guardian.mjs +4 -1
- package/esm/guardian.mjs.map +1 -1
- package/esm/supervisor.mjs +1 -1
- package/esm/supervisor.mjs.map +1 -1
- package/llms-full.txt +22 -0
- package/package.json +1 -1
- package/skills/supervise-processes/SKILL.md +22 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog — @warlock.js/process
|
|
2
2
|
|
|
3
|
+
## 5.34.0 - 2026-10-19
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Validate supervisor configuration before accessing options; errors name the missing or invalid `owner` or `registry` option.
|
|
8
|
+
|
|
9
|
+
### Security
|
|
10
|
+
|
|
11
|
+
- Strip guardian ownership and launch variables from macOS/Windows workload environments, including Windows casing aliases. Guardian proof remains intact; Linux retains its `/proc` ownership token.
|
|
12
|
+
|
|
3
13
|
## 5.33.0 - 2026-10-19
|
|
4
14
|
|
|
5
15
|
### Added
|
package/cjs/index.cjs
CHANGED
|
@@ -140,7 +140,10 @@ const start = () => {
|
|
|
140
140
|
gated = false;
|
|
141
141
|
const [command, ...args] = JSON.parse(process.env.WARLOCK_PROCESS_LAUNCH);
|
|
142
142
|
const env = { ...process.env };
|
|
143
|
-
|
|
143
|
+
for (const key of Object.keys(env)) {
|
|
144
|
+
const name = process.platform === "win32" ? key.toUpperCase() : key;
|
|
145
|
+
if (name === "WARLOCK_PROCESS_TOKEN" || name === "WARLOCK_PROCESS_LAUNCH") delete env[key];
|
|
146
|
+
}
|
|
144
147
|
const child = spawn(command, args, { env, stdio: "inherit", windowsHide: true });
|
|
145
148
|
child.on("error", (error) => { console.error(error.message); finish(127); });
|
|
146
149
|
child.on("exit", (code, signal) => {
|
|
@@ -597,7 +600,7 @@ function matches(entry, filter) {
|
|
|
597
600
|
return Object.entries(filter.labels ?? {}).every(([key, value]) => entry.labels[key] === value);
|
|
598
601
|
}
|
|
599
602
|
function supervisor(options) {
|
|
600
|
-
if (!options
|
|
603
|
+
for (const option of ["owner", "registry"]) if (!options || typeof options[option] !== "string" || !options[option].trim()) throw new TypeError(`Invalid supervisor configuration: ${option} must be a nonempty string`);
|
|
601
604
|
validateLimits(options.defaults ?? {});
|
|
602
605
|
options = {
|
|
603
606
|
...options,
|
package/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["path","readFile","mkdir","writeFile","rmdir","randomBytes","createHmac","connect","promisify","execFile","readFile","mkdir","path","readFile","randomBytes","rename","rm","writeFile","path","delay","PassThrough","randomBytes","Writable","spawnChild"],"sources":["../../../../../../../packages/process/src/resources.ts","../../../../../../../packages/process/src/guardian.ts","../../../../../../../packages/process/src/platform.ts","../../../../../../../packages/process/src/identity.ts","../../../../../../../packages/process/src/registry.ts","../../../../../../../packages/process/src/supervisor.ts"],"sourcesContent":["import { mkdir, readFile, rmdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { ProcessLimits } from \"./types\";\n\nfunction mountedCgroup(membership: string): string | undefined {\n const mount = \"/sys/fs/cgroup\";\n const directory = path.resolve(mount, `.${membership}`);\n // A cgroup namespace can report ancestors using /../ components. Never\n // normalize those into writes outside the standard cgroup mount.\n return directory === mount || directory.startsWith(`${mount}/`) ? directory : undefined;\n}\n\nexport function validateLimits(limits: ProcessLimits, platform: string = process.platform): void {\n for (const [key, value] of Object.entries(limits)) {\n if (\n ![\"timeoutMs\", \"memoryBytes\", \"cpuSeconds\", \"virtualMemoryBytes\"].includes(key) ||\n !Number.isSafeInteger(value) ||\n value <= 0\n )\n throw new Error(`Invalid process limit: ${key}`);\n }\n if (platform !== \"linux\" && limits.virtualMemoryBytes !== undefined)\n throw new Error(\"virtualMemoryBytes is supported only on Linux\");\n if (platform === \"win32\" && limits.cpuSeconds !== undefined)\n throw new Error(\"cpuSeconds is supported only on POSIX platforms\");\n}\n\n/**\n * POSIX shell applies inherited limits. Linux stops here; macOS execs a gated guardian.\n * Flags/units: Apple's bash-3.2/builtins/ulimit.def and installed Bash manual.\n * https://github.com/apple-oss-distributions/bash/blob/main/bash-3.2/builtins/ulimit.def\n */\nexport function launcher(limits: ProcessLimits, platform: string = process.platform): string {\n const commands = [\"set -e\"];\n if (limits.cpuSeconds) commands.push(`ulimit -t ${limits.cpuSeconds}`);\n if (limits.virtualMemoryBytes)\n commands.push(`ulimit -v ${Math.ceil(limits.virtualMemoryBytes / 1024)}`);\n // Linux ignores RLIMIT_RSS; polling remains the fallback resident-memory cap.\n // Darwin's RLIMIT_DATA restricts heap growth, not total resident memory.\n if (limits.memoryBytes)\n commands.push(\n `ulimit -${platform === \"darwin\" ? \"d\" : \"m\"} ${Math.ceil(limits.memoryBytes / 1024)}`,\n );\n if (platform === \"linux\") commands.push('kill -STOP \"$$\"');\n commands.push('exec \"$@\"');\n return commands.join(\"\\n\");\n}\n\n/** https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html */\nexport async function attachCgroup(\n pid: number,\n token: string,\n limits: ProcessLimits,\n root?: string,\n): Promise<string | undefined> {\n if (process.platform !== \"linux\") return undefined;\n let directory: string | undefined;\n try {\n const own = (await readFile(\"/proc/self/cgroup\", \"utf8\"))\n .split(\"\\n\")\n .find((line) => line.startsWith(\"0::\"))\n ?.slice(3);\n if (!root && own === undefined) return undefined;\n const parent = root ?? mountedCgroup(own ?? \"/\");\n if (!parent) return undefined;\n // Do not enable controllers or move the app: delegation belongs to the service manager.\n await readFile(path.join(parent, \"cgroup.controllers\"), \"utf8\");\n directory = path.join(parent, `warlock-${token}`);\n await mkdir(directory);\n if (limits.memoryBytes) {\n await writeFile(path.join(directory, \"memory.max\"), String(limits.memoryBytes));\n await writeFile(path.join(directory, \"memory.oom.group\"), \"1\");\n }\n await writeFile(path.join(directory, \"cgroup.procs\"), String(pid));\n return directory;\n } catch {\n if (directory) await rmdir(directory).catch(() => undefined);\n return undefined;\n }\n}\n\nexport async function cgroupMemory(directory: string): Promise<{ peak: number; oom: boolean }> {\n const [peak, events] = await Promise.all([\n readFile(path.join(directory, \"memory.peak\"), \"utf8\").catch(() => \"0\"),\n readFile(path.join(directory, \"memory.events\"), \"utf8\").catch(() => \"\"),\n ]);\n return { peak: Number(peak) || 0, oom: Number(/^oom_kill (\\d+)/m.exec(events)?.[1] ?? 0) > 0 };\n}\n\n/** Only reattach the token-specific cgroup confirmed by the kernel membership file. */\nexport async function recoveredCgroup(pid: number, token: string): Promise<string | undefined> {\n if (process.platform !== \"linux\") return undefined;\n let source: string;\n try {\n source = await readFile(`/proc/${pid}/cgroup`, \"utf8\");\n } catch (error) {\n if ([\"ENOENT\", \"ESRCH\"].includes(String((error as NodeJS.ErrnoException).code)))\n return undefined;\n throw error;\n }\n const membership = source\n .split(\"\\n\")\n .find((line) => line.startsWith(\"0::\"))\n ?.slice(3);\n if (!membership || path.basename(membership) !== `warlock-${token}`) return undefined;\n return mountedCgroup(membership);\n}\n\nexport async function cleanCgroup(directory: string): Promise<void> {\n // This unique cgroup was created for this token, or recovered after identity proof.\n // cgroup.kill also covers descendants that created their own POSIX session.\n await writeFile(path.join(directory, \"cgroup.kill\"), \"1\").catch(() => undefined);\n await rmdir(directory).catch(() => undefined);\n}\n","import { createHmac, randomBytes } from \"node:crypto\";\nimport { connect } from \"node:net\";\nimport type { Command, OwnershipProof } from \"./platform\";\nimport { launcher } from \"./resources\";\nimport type { ProcessLimits } from \"./types\";\n\nexport function guardianAddress(platform: string, pid: number, uid: number): string {\n return platform === \"win32\"\n ? `\\\\\\\\.\\\\pipe\\\\warlock-process-${pid}`\n : `/tmp/warlock-process-${uid}-${pid}.sock`;\n}\n\n/** A live guardian reads its own environment, never argv, to prove ownership.\n * Node IPC is native-free: https://nodejs.org/api/net.html#ipc-support\n * The stable root PID stays alive for the workload and survives supervisor restarts.\n */\nexport const guardianScript = String.raw`\nconst { createServer } = require(\"node:net\");\nconst { createHmac } = require(\"node:crypto\");\nconst { spawn } = require(\"node:child_process\");\nconst { chmodSync, unlinkSync } = require(\"node:fs\");\nconst token = process.env.WARLOCK_PROCESS_TOKEN;\nconst address = process.platform === \"win32\"\n ? \"\\\\\\\\.\\\\pipe\\\\warlock-process-\" + process.pid\n : \"/tmp/warlock-process-\" + process.getuid() + \"-\" + process.pid + \".sock\";\nlet gated = true;\nconst finish = (code) => { server.close(); process.exit(code); };\nconst start = () => {\n if (!gated) return;\n gated = false;\n const [command, ...args] = JSON.parse(process.env.WARLOCK_PROCESS_LAUNCH);\n const env = { ...process.env };\n delete env.WARLOCK_PROCESS_LAUNCH;\n const child = spawn(command, args, { env, stdio: \"inherit\", windowsHide: true });\n child.on(\"error\", (error) => { console.error(error.message); finish(127); });\n child.on(\"exit\", (code, signal) => {\n if (signal && process.platform !== \"win32\") process.kill(process.pid, signal);\n else finish(code ?? 1);\n });\n};\nconst server = createServer((socket) => {\n socket.setTimeout(2000, () => socket.destroy());\n socket.on(\"error\", () => {});\n let input = \"\";\n socket.on(\"data\", (chunk) => {\n input += chunk;\n if (input.length > 1024) { socket.destroy(); return; }\n if (!input.endsWith(\"\\n\")) return;\n try {\n const request = JSON.parse(input);\n if (!/^[a-f0-9]{64}$/.test(request.nonce)) { socket.destroy(); return; }\n const sign = (value) => createHmac(\"sha256\", token).update(value).digest(\"hex\");\n if (request.resume === sign(request.nonce + \":resume\")) start();\n socket.end(JSON.stringify({ pid: process.pid, digest: sign(request.nonce + \":\" + process.pid + \":\" + gated), gated }) + \"\\n\");\n } catch { socket.destroy(); }\n });\n});\nserver.on(\"error\", (error) => { console.error(error.message); process.exit(126); });\nif (process.platform !== \"win32\") {\n try { unlinkSync(address); } catch (error) { if (error.code !== \"ENOENT\") throw error; }\n process.on(\"exit\", () => { try { unlinkSync(address); } catch {} });\n}\nserver.listen(address, () => { if (process.platform !== \"win32\") chmodSync(address, 0o600); });\n`;\n\nexport async function guardianProof(\n pid: number,\n resume?: string,\n expectedToken: string | undefined = resume,\n): Promise<OwnershipProof | undefined> {\n if (!expectedToken) return undefined;\n const token = expectedToken;\n const nonce = randomBytes(32).toString(\"hex\");\n const sign = (value: string) => createHmac(\"sha256\", token).update(value).digest(\"hex\");\n const address = guardianAddress(process.platform, pid, process.getuid?.() ?? 0);\n return new Promise((resolve, reject) => {\n const socket = connect(address);\n let data = \"\";\n const fail = (error: Error) => {\n socket.destroy();\n reject(error);\n };\n socket.setTimeout(2000, () => fail(new Error(\"Process ownership observation timed out\")));\n socket.on(\"connect\", () =>\n socket.write(\n JSON.stringify({ nonce, resume: resume ? sign(nonce + \":resume\") : undefined }) + \"\\n\",\n ),\n );\n socket.on(\"data\", (chunk: Buffer) => {\n data += chunk.toString();\n if (data.length > 1024) fail(new Error(\"Invalid process ownership response\"));\n });\n socket.on(\"error\", (error: NodeJS.ErrnoException) => {\n if ([\"ENOENT\", \"ECONNREFUSED\"].includes(error.code ?? \"\")) resolve(undefined);\n else reject(error);\n });\n socket.on(\"end\", () => {\n try {\n resolve(parseGuardianProof(data, pid, nonce, token));\n } catch (error) {\n reject(error);\n }\n });\n });\n}\n\n/** Malformed observation is an error; an authenticated identity mismatch is unowned. */\nexport function parseGuardianProof(\n source: string,\n pid: number,\n nonce: string,\n token: string,\n): OwnershipProof | undefined {\n const proof: unknown = JSON.parse(source);\n if (\n !proof ||\n typeof proof !== \"object\" ||\n !(\"pid\" in proof) ||\n typeof proof.pid !== \"number\" ||\n !(\"gated\" in proof) ||\n typeof proof.gated !== \"boolean\" ||\n !(\"digest\" in proof) ||\n typeof proof.digest !== \"string\"\n )\n throw new Error(\"Invalid process ownership response\");\n const expected = createHmac(\"sha256\", token)\n .update(nonce + \":\" + pid + \":\" + proof.gated)\n .digest(\"hex\");\n if (proof.pid !== pid || proof.digest !== expected) return undefined;\n return { pid, token, gated: proof.gated };\n}\n\n/** Pure launch construction, including case-insensitive Windows environment keys. */\nexport function launchCommand(\n platform: string,\n node: string,\n command: string,\n args: string[],\n limits: ProcessLimits,\n environment: NodeJS.ProcessEnv,\n token: string,\n): Omit<Command, \"env\"> & { env: NodeJS.ProcessEnv } {\n const portable = platform !== \"linux\";\n const env: NodeJS.ProcessEnv = Object.fromEntries(\n Object.entries(environment).filter(\n ([key]) =>\n ![\"WARLOCK_PROCESS_TOKEN\", \"WARLOCK_PROCESS_LAUNCH\"].includes(\n platform === \"win32\" ? key.toUpperCase() : key,\n ),\n ),\n );\n env.WARLOCK_PROCESS_TOKEN = token;\n if (portable) env.WARLOCK_PROCESS_LAUNCH = JSON.stringify([command, ...args]);\n const executable = portable ? node : command;\n const arguments_ = portable ? [\"-e\", guardianScript] : args;\n return {\n file: platform === \"win32\" ? executable : \"/bin/sh\",\n args:\n platform === \"win32\"\n ? arguments_\n : [\"-c\", launcher(limits, platform), \"warlock-process\", executable, ...arguments_],\n env,\n };\n}\n","import type { Identity } from \"./identity\";\n\nexport type PortablePlatform = \"darwin\" | \"win32\";\nexport type Command = { file: string; args: string[]; env?: Record<string, string> };\nexport type Execute = (command: Command) => Promise<string>;\nexport type OwnershipProof = { pid: number; token: string; gated: boolean };\nexport type ReadProof = (pid: number) => Promise<OwnershipProof | undefined>;\ntype Row = {\n pid: number;\n parent: number;\n start: string;\n rss: number;\n group: number;\n state: string;\n};\n\nexport class UnsupportedProcessPlatformError extends Error {\n constructor(public readonly platform: string) {\n super(`Process supervision is unsupported on ${platform}`);\n this.name = \"UnsupportedProcessPlatformError\";\n }\n}\nexport function supported(platform: string): void {\n if (![\"linux\", \"darwin\", \"win32\"].includes(platform))\n throw new UnsupportedProcessPlatformError(platform);\n}\nfunction validPid(pid: number): void {\n if (!Number.isSafeInteger(pid) || pid <= 1) throw new Error(\"Invalid process PID\");\n}\n\n/** Apple ps(1): lstart uses strftime %c. LC_ALL=C gives %a %b %e %H:%M:%S %Y.\n * https://github.com/apple-oss-distributions/adv_cmds/blob/main/ps/ps.1\n * https://github.com/apple-oss-distributions/adv_cmds/blob/main/ps/print.c (lstarted)\n * https://github.com/apple-oss-distributions/Libc/blob/main/stdtime/FreeBSD/timelocal.c (_C_time_locale)\n */\nexport function parseDarwinStart(source: string): string {\n const match =\n /^(Sun|Mon|Tue|Wed|Thu|Fri|Sat) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s+(\\d{1,2}) (\\d{2}):(\\d{2}):(\\d{2}) (\\d{4})$/.exec(\n source.trim(),\n );\n if (!match) throw new Error(\"Invalid macOS process start time\");\n const [, weekday, month, day, hour, minute, second, year] = match;\n const monthIndex = \"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec\"\n .split(\" \")\n .indexOf(month ?? \"\");\n const date = new Date(\n Date.UTC(Number(year), monthIndex, Number(day), Number(hour), Number(minute), Number(second)),\n );\n if (\n date.getUTCFullYear() !== Number(year) ||\n date.getUTCMonth() !== monthIndex ||\n date.getUTCDate() !== Number(day) ||\n date.getUTCHours() !== Number(hour) ||\n date.getUTCMinutes() !== Number(minute) ||\n date.getUTCSeconds() !== Number(second) ||\n \"Sun Mon Tue Wed Thu Fri Sat\".split(\" \")[date.getUTCDay()] !== weekday\n )\n throw new Error(\"Invalid macOS process start time\");\n return String(date.getTime() / 1000);\n}\nexport function darwinCommand(pid: number, tree = false): Command {\n validPid(pid);\n return {\n file: \"/bin/ps\",\n args: [...(tree ? [\"-ax\"] : [\"-p\", String(pid)]), \"-o\", \"pid=,ppid=,pgid=,state=,rss=,lstart=\"],\n env: { LC_ALL: \"C\", TZ: \"UTC\" },\n };\n}\nexport function parseDarwinRows(source: string): Row[] {\n return source.trim()\n ? source\n .trim()\n .split(\"\\n\")\n .map((line) => {\n const match = /^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\S+)\\s+(\\d+)\\s+(.+?)\\s*$/.exec(line);\n if (!match) throw new Error(\"Invalid macOS process observation\");\n return {\n pid: Number(match[1]),\n parent: Number(match[2]),\n group: Number(match[3]),\n state: match[4] ?? \"\",\n rss: Number(match[5]) * 1024,\n start: parseDarwinStart(match[6] ?? \"\"),\n };\n })\n : [];\n}\n\n/** CIM CreationDate is a DateTime; serialize UTC ticks as text, never a locale date\n * or JSON number (100ns ticks exceed Number's precision). WorkingSetSize is bytes.\n * https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-process\n */\nexport function windowsCommand(pid: number, tree = false): Command {\n validPid(pid);\n const query = `Get-CimInstance Win32_Process${tree ? \"\" : ` -Filter 'ProcessId=${pid}'`}`;\n const script = [\n \"$ErrorActionPreference = 'Stop'\",\n `$rows = @(${query} | ForEach-Object {`,\n `if ([int]$_.ProcessId -eq ${pid} -and $null -eq $_.CreationDate) { throw 'Cannot read process creation time' }`,\n \"if ($null -ne $_.CreationDate) {\",\n \"[pscustomobject]@{ pid = [int]$_.ProcessId; parent = [int]$_.ParentProcessId; start = $_.CreationDate.ToUniversalTime().Ticks.ToString([Globalization.CultureInfo]::InvariantCulture); rss = [double]$_.WorkingSetSize }\",\n \"}\",\n \"})\",\n \"ConvertTo-Json -InputObject $rows -Compress\",\n ].join(\"\\n\");\n return {\n file: \"powershell.exe\",\n args: [\"-NoLogo\", \"-NoProfile\", \"-NonInteractive\", \"-Command\", script],\n };\n}\nexport function parseWindowsRows(source: string): Row[] {\n const value: unknown = JSON.parse(source.replace(/^\\uFEFF/, \"\"));\n if (!Array.isArray(value)) throw new Error(\"Invalid Windows process observation\");\n return value.map((row: unknown) => {\n if (\n !row ||\n typeof row !== \"object\" ||\n !(\"pid\" in row) ||\n typeof row.pid !== \"number\" ||\n !Number.isSafeInteger(row.pid) ||\n row.pid < 0 ||\n !(\"parent\" in row) ||\n typeof row.parent !== \"number\" ||\n !Number.isSafeInteger(row.parent) ||\n row.parent < 0 ||\n !(\"start\" in row) ||\n typeof row.start !== \"string\" ||\n !/^\\d{18}$/.test(row.start) ||\n !(\"rss\" in row) ||\n typeof row.rss !== \"number\" ||\n !Number.isFinite(row.rss) ||\n row.rss < 0\n )\n throw new Error(\"Invalid Windows process observation\");\n return {\n pid: row.pid,\n parent: row.parent,\n start: row.start,\n rss: row.rss,\n group: row.pid,\n state: \"R\",\n };\n });\n}\nexport function taskkillCommand(pid: number): Command {\n validPid(pid);\n // Never accept an image name, filter, wildcard or shell interpolation.\n return { file: \"taskkill.exe\", args: [\"/PID\", String(pid), \"/T\", \"/F\"] };\n}\nexport function treeMemory(rows: Row[], pid: number): number {\n const root = rows.find((row) => row.pid === pid);\n if (!root) return 0;\n const included = new Map<number, Row>([[pid, root]]);\n let changed = true;\n while (changed) {\n changed = false;\n for (const row of rows) {\n const parent = included.get(row.parent);\n // Reject a reused parent PID whose creation time is later than the child.\n if (!included.has(row.pid) && parent && BigInt(row.start) >= BigInt(parent.start)) {\n included.set(row.pid, row);\n changed = true;\n }\n }\n }\n return [...included.values()].reduce((total, row) => total + row.rss, 0);\n}\n\n/** All OS effects are injected; foreign-platform specs never execute real commands. */\nexport async function portableIdentity(\n platform: PortablePlatform,\n pid: number,\n token: boolean,\n execute: Execute,\n readProof: ReadProof,\n): Promise<Identity | undefined> {\n validPid(pid);\n const command = platform === \"darwin\" ? darwinCommand : windowsCommand;\n const parse = platform === \"darwin\" ? parseDarwinRows : parseWindowsRows;\n const rows = parse(await execute(command(pid, token)));\n const before = rows.find((row) => row.pid === pid);\n if (!before || /^[ZX]/.test(before.state)) return undefined;\n if (!token) return before;\n const proof = await readProof(pid);\n const after = parse(await execute(command(pid))).find((row) => row.pid === pid);\n if (\n !after ||\n after.start !== before.start ||\n after.group !== before.group ||\n /^[ZX]/.test(after.state)\n )\n return undefined;\n return {\n start: after.start,\n group: after.group,\n state: proof?.pid === pid && proof.gated ? \"T\" : after.state,\n rss: treeMemory(rows, pid),\n token: proof?.pid === pid ? proof.token : undefined,\n };\n}\n","import { execFile } from \"node:child_process\";\nimport { readFile } from \"node:fs/promises\";\nimport { promisify } from \"node:util\";\n\nimport { guardianProof } from \"./guardian\";\nimport { portableIdentity, type Command } from \"./platform\";\n\nconst execute = promisify(execFile);\nexport async function executeCommand(command: Command): Promise<string> {\n const { stdout } = await execute(command.file, command.args, {\n env: { ...process.env, ...command.env },\n timeout: 5000,\n maxBuffer: 4 * 1024 * 1024,\n windowsHide: true,\n });\n return stdout;\n}\nexport const TOKEN_ENV = \"WARLOCK_PROCESS_TOKEN\";\nexport type Identity = { start: string; group: number; state: string; rss: number; token?: string };\n\nfunction statFields(stat: string): string[] {\n return stat\n .slice(stat.lastIndexOf(\")\") + 2)\n .trim()\n .split(/\\s+/);\n}\n\n/**\n * A task that is exiting has already released its memory map (stat field 23,\n * vsize, reads 0) before it turns into a zombie. Linux then reports its /proc\n * files as root-owned, so it is never a live child to verify or signal.\n */\nfunction exiting(fields: string[]): boolean {\n return [\"Z\", \"X\"].includes(fields[0] ?? \"\") || fields[20] === \"0\";\n}\n\n/** /proc fields: https://man7.org/linux/man-pages/man5/proc_pid_stat.5.html */\nexport async function identity(\n pid: number,\n token = false,\n expectedToken?: string,\n): Promise<Identity | undefined> {\n if (!Number.isSafeInteger(pid) || pid <= 1) return undefined;\n try {\n if (process.platform === \"linux\") {\n const stat = await readFile(`/proc/${pid}/stat`, \"utf8\");\n const fields = statFields(stat);\n const state = fields[0];\n const start = fields[19];\n if (!state || !start || exiting(fields)) return undefined;\n const boot = (await readFile(\"/proc/sys/kernel/random/boot_id\", \"utf8\")).trim();\n const status = await readFile(`/proc/${pid}/status`, \"utf8\");\n let environment = \"\";\n if (token) {\n try {\n environment = await readFile(`/proc/${pid}/environ`, \"utf8\");\n } catch (error) {\n // Without root, the environment of a task that started exiting after\n // the first stat read is unreadable. Only that case counts as gone;\n // any other unreadable environment still fails closed.\n if ((error as { code?: unknown }).code !== \"EACCES\") throw error;\n const now = statFields(await readFile(`/proc/${pid}/stat`, \"utf8\"));\n if (now[19] !== start || exiting(now)) return undefined;\n throw error;\n }\n }\n // /proc reads are separate operations. Reject PID reuse or a group change\n // during observation before combining a token with a start identity.\n const after = statFields(await readFile(`/proc/${pid}/stat`, \"utf8\"));\n if (after[19] !== start || after[2] !== fields[2] || exiting(after)) return undefined;\n return {\n start: `${boot}:${start}`,\n group: Number(fields[2]),\n state: after[0] ?? state,\n rss: Number(/^VmRSS:\\s+(\\d+)/m.exec(status)?.[1] ?? 0) * 1024,\n token: environment\n .split(\"\\0\")\n .find((entry) => entry.startsWith(`${TOKEN_ENV}=`))\n ?.slice(TOKEN_ENV.length + 1),\n };\n }\n if (process.platform === \"darwin\" || process.platform === \"win32\")\n return await portableIdentity(process.platform, pid, token, executeCommand, (selected) =>\n guardianProof(selected, undefined, expectedToken),\n );\n return undefined;\n } catch (error) {\n const code = (error as { code?: unknown }).code;\n if (\n (process.platform === \"linux\" && [\"ENOENT\", \"ESRCH\"].includes(String(code))) ||\n (process.platform === \"darwin\" &&\n code === 1 &&\n !(error as { stdout?: string }).stdout?.trim())\n )\n return undefined;\n throw error;\n }\n}\n\nexport class ProcessNotOwnedError extends Error {\n constructor(public readonly pid: number) {\n super(`Process ${pid} does not have a verified supervisor identity`);\n this.name = \"ProcessNotOwnedError\";\n }\n}\n\nexport async function owns(entry: { pid: number; start: string; token: string }): Promise<boolean> {\n const current = await identity(entry.pid, true, entry.token);\n return (\n current?.start === entry.start && current.token === entry.token && current.group === entry.pid\n );\n}\n","import { randomBytes } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { identity } from \"./identity\";\nimport type { ProcessInfo } from \"./types\";\n\nexport type RegistryEntry = ProcessInfo & {\n token: string;\n start: string;\n cgroup?: string;\n /** Persisted launch gate: recovery may resume an adopted, interrupted launch. */\n gated?: boolean;\n};\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction strings(value: unknown): value is Record<string, string> {\n return record(value) && Object.values(value).every((item) => typeof item === \"string\");\n}\nexport function parseRegistry(source: string, owner: string): RegistryEntry[] {\n const value: unknown = JSON.parse(source);\n if (\n !record(value) ||\n value.version !== 1 ||\n value.owner !== owner ||\n !Array.isArray(value.entries)\n )\n throw new Error(\"Invalid process registry or different owner\");\n const entries: RegistryEntry[] = [];\n for (const entry of value.entries) {\n if (\n !record(entry) ||\n entry.owner !== owner ||\n typeof entry.pid !== \"number\" ||\n !Number.isSafeInteger(entry.pid) ||\n Number(entry.pid) <= 1 ||\n typeof entry.token !== \"string\" ||\n !/^[a-f0-9]{64}$/.test(entry.token) ||\n typeof entry.start !== \"string\" ||\n typeof entry.type !== \"string\" ||\n typeof entry.command !== \"string\" ||\n !Array.isArray(entry.args) ||\n !entry.args.every((arg) => typeof arg === \"string\") ||\n !strings(entry.labels) ||\n typeof entry.startedAt !== \"number\" ||\n !Number.isFinite(entry.startedAt) ||\n entry.startedAt < 0 ||\n !record(entry.limits) ||\n !Object.entries(entry.limits).every(\n ([key, limit]) =>\n [\"timeoutMs\", \"memoryBytes\", \"cpuSeconds\", \"virtualMemoryBytes\"].includes(key) &&\n typeof limit === \"number\" &&\n Number.isSafeInteger(limit) &&\n limit > 0,\n ) ||\n ![\"cgroup-v2\", \"rlimit-polling\", \"rss-polling\"].includes(String(entry.enforcement)) ||\n typeof entry.peakMemoryBytes !== \"number\" ||\n !Number.isFinite(entry.peakMemoryBytes) ||\n entry.peakMemoryBytes < 0 ||\n (entry.cgroup !== undefined && typeof entry.cgroup !== \"string\") ||\n (entry.gated !== undefined && typeof entry.gated !== \"boolean\")\n )\n throw new Error(\"Invalid process registry entry\");\n entries.push(entry as RegistryEntry);\n }\n if (new Set(entries.map((entry) => entry.pid)).size !== entries.length)\n throw new Error(\"Duplicate registry PID\");\n return entries;\n}\n\n/** One writer per registry. A crash leaves a lease which only a dead identity can release. */\nexport class Registry {\n private queue: Promise<void> = Promise.resolve();\n private locked = false;\n constructor(\n public readonly file: string,\n private readonly owner: string,\n ) {}\n async open(): Promise<RegistryEntry[]> {\n await mkdir(path.dirname(this.file), { recursive: true, mode: 0o700 });\n const self = await identity(process.pid);\n if (!self) throw new Error(\"Cannot verify registry writer identity\");\n const lock = `${this.file}.lock`;\n try {\n await mkdir(lock, { mode: 0o700 });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n const reclaim = `${lock}.reclaim`;\n await mkdir(reclaim, { mode: 0o700 });\n try {\n const lease: unknown = JSON.parse(await readFile(path.join(lock, \"writer.json\"), \"utf8\"));\n if (!record(lease) || typeof lease.pid !== \"number\" || typeof lease.start !== \"string\")\n throw new Error(\"Invalid registry writer lease; inspect it manually\");\n const writer = await identity(lease.pid);\n if (writer?.start === lease.start) throw new Error(\"Process registry is already in use\");\n const stale = `${lock}.${randomBytes(8).toString(\"hex\")}`;\n await rename(lock, stale);\n await rm(stale, { recursive: true });\n await mkdir(lock, { mode: 0o700 });\n } finally {\n await rm(reclaim, { recursive: true });\n }\n }\n this.locked = true;\n try {\n await writeFile(\n path.join(lock, \"writer.json\"),\n JSON.stringify({ pid: process.pid, start: self.start }),\n { mode: 0o600 },\n );\n try {\n return parseRegistry(await readFile(this.file, \"utf8\"), this.owner);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw error;\n }\n } catch (error) {\n await this.close();\n throw error;\n }\n }\n save(entries: RegistryEntry[]): Promise<void> {\n const source = JSON.stringify({ version: 1, owner: this.owner, entries }, null, 2) + \"\\n\";\n const next = this.queue.then(async () => {\n const temporary = `${this.file}.${randomBytes(8).toString(\"hex\")}.tmp`;\n try {\n await writeFile(temporary, source, { mode: 0o600, flag: \"wx\" });\n await rename(temporary, this.file);\n } finally {\n await rm(temporary, { force: true });\n }\n });\n this.queue = next.catch(() => undefined);\n return next;\n }\n async close(): Promise<void> {\n await this.queue;\n if (this.locked) {\n await rm(`${this.file}.lock`, { recursive: true });\n this.locked = false;\n }\n }\n}\n","import { spawn as spawnChild } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport path from \"node:path\";\nimport { PassThrough, Writable } from \"node:stream\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { identity, owns, ProcessNotOwnedError, executeCommand } from \"./identity\";\nimport { guardianProof, launchCommand } from \"./guardian\";\nimport { supported, taskkillCommand } from \"./platform\";\nimport { Registry, type RegistryEntry } from \"./registry\";\nimport {\n attachCgroup,\n cgroupMemory,\n cleanCgroup,\n recoveredCgroup,\n validateLimits,\n} from \"./resources\";\nimport type {\n ProcessExit,\n ProcessFilter,\n ProcessHandle,\n ProcessInfo,\n ProcessSupervisor,\n PtyHandle,\n RecoveryResult,\n SpawnOptions,\n SupervisorOptions,\n} from \"./types\";\n\ntype Pty = {\n pid: number;\n onData(listener: (data: string) => void): { dispose(): void };\n onExit(listener: (event: { exitCode: number; signal?: number }) => void): { dispose(): void };\n write(data: string): void;\n resize(columns: number, rows: number): void;\n};\ntype PtyModule = {\n spawn(\n command: string,\n args: string[],\n options: {\n cwd?: string;\n env: Record<string, string>;\n cols: number;\n rows: number;\n name: string;\n },\n ): Pty;\n};\ntype Active = {\n entry: RegistryEntry;\n handle: ProcessHandle;\n reason?: ProcessExit[\"reason\"];\n finish(code: number | null, signal: string | null): Promise<void>;\n stopping?: Promise<void>;\n timer?: ReturnType<typeof setInterval>;\n finished: boolean;\n adopted: boolean;\n fail(error: unknown): void;\n};\n\nfunction info(entry: RegistryEntry): ProcessInfo {\n const { token: _token, start: _start, cgroup: _cgroup, gated: _gated, ...visible } = entry;\n return structuredClone(visible);\n}\nfunction validateFilter(filter: ProcessFilter): void {\n if (\n !filter ||\n typeof filter !== \"object\" ||\n Array.isArray(filter) ||\n Object.keys(filter).some((key) => key !== \"labels\")\n )\n throw new Error(\"Only label filters are supported\");\n if (\n filter.labels !== undefined &&\n (filter.labels === null ||\n typeof filter.labels !== \"object\" ||\n Array.isArray(filter.labels) ||\n !Object.values(filter.labels).every((value) => typeof value === \"string\"))\n )\n throw new Error(\"Labels must be string key/value pairs\");\n}\nfunction matches(entry: RegistryEntry, filter: ProcessFilter): boolean {\n return Object.entries(filter.labels ?? {}).every(([key, value]) => entry.labels[key] === value);\n}\n\nexport function supervisor(options: SupervisorOptions): ProcessSupervisor {\n if (!options.owner.trim() || !options.registry.trim())\n throw new Error(\"owner and registry are required\");\n validateLimits(options.defaults ?? {});\n // Caller-owned configuration must not change an active supervisor's identity.\n options = { ...options, defaults: { ...options.defaults } };\n const pollMs = options.pollIntervalMs ?? 100;\n const graceMs = options.stopGraceMs ?? 1000;\n if (!Number.isSafeInteger(pollMs) || pollMs < 10 || !Number.isSafeInteger(graceMs) || graceMs < 0)\n throw new Error(\"Invalid polling interval or stop grace period\");\n const registry = new Registry(path.resolve(options.registry), options.owner);\n const entries = new Map<number, RegistryEntry>();\n const active = new Map<number, Active>();\n let ready: Promise<void> | undefined;\n let closing = false;\n let shutdown: Promise<void> | undefined;\n // Serialize public operations, including shutdown, so a gated spawn cannot escape stopAll.\n let queue: Promise<unknown> = Promise.resolve();\n function serial<T>(operation: () => Promise<T>): Promise<T> {\n const next = queue.then(operation);\n queue = next.catch(() => undefined);\n return next;\n }\n async function open(): Promise<void> {\n supported(process.platform);\n if (closing) throw new Error(\"Supervisor has shut down\");\n ready ??= registry.open().then((saved) => {\n for (const entry of saved) {\n delete entry.cgroup;\n entries.set(entry.pid, entry);\n }\n });\n await ready;\n }\n const save = () => registry.save([...entries.values()]);\n\n async function signal(entry: RegistryEntry, value: NodeJS.Signals): Promise<void> {\n if (!(await owns(entry))) throw new ProcessNotOwnedError(entry.pid);\n if (process.platform !== \"linux\" && value === \"SIGCONT\") {\n const proof = await guardianProof(entry.pid, entry.token);\n if (!proof || proof.token !== entry.token || proof.gated)\n throw new ProcessNotOwnedError(entry.pid);\n return;\n }\n if (process.platform === \"win32\") {\n try {\n await executeCommand(taskkillCommand(entry.pid));\n } catch (error) {\n if (await owns(entry)) throw error;\n }\n return;\n }\n // Negative PID targets only the session/group established for this child.\n // Recheck for every signal, including escalation; never match a command line.\n try {\n process.kill(-entry.pid, value);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ESRCH\") throw error;\n }\n }\n async function stopEntry(\n entry: RegistryEntry,\n reason: ProcessExit[\"reason\"] = \"stopped\",\n ): Promise<void> {\n const tracked = active.get(entry.pid);\n let running: Active;\n if (!tracked) {\n if (!(await owns(entry))) throw new ProcessNotOwnedError(entry.pid);\n entry.cgroup = await recoveredCgroup(entry.pid, entry.token);\n running = track(entry);\n } else running = tracked;\n if (running.finished) return running.finish(null, null);\n if (running.stopping) return running.stopping;\n const stopping = (async () => {\n if (!(await owns(entry))) throw new ProcessNotOwnedError(entry.pid);\n running.reason ??= reason;\n await signal(entry, \"SIGTERM\");\n const deadline = Date.now() + graceMs;\n while (await owns(entry)) {\n if (Date.now() >= deadline) {\n await signal(entry, \"SIGKILL\");\n break;\n }\n await delay(20);\n }\n const killedDeadline = Date.now() + 5000;\n while (await owns(entry)) {\n if (Date.now() >= killedDeadline)\n throw new Error(`Process ${entry.pid} did not exit after SIGKILL`);\n await delay(20);\n }\n await running.finish(null, null);\n })();\n running.stopping = stopping;\n try {\n await stopping;\n } catch (error) {\n running.stopping = undefined;\n // The leader may exit between identity verification and a signal. A\n // missing identity is harmless for a tracked handle; a live mismatch is\n // still an ownership failure and must never receive another signal.\n if (error instanceof ProcessNotOwnedError && !(await identity(entry.pid))) {\n await running.finish(null, null);\n return;\n }\n throw error;\n }\n }\n\n function track(\n entry: RegistryEntry,\n streams?: Pick<ProcessHandle, \"stdin\" | \"stdout\" | \"stderr\">,\n ): Active {\n let resolveExit: (exit: ProcessExit) => void = () => undefined;\n let rejectExit: (error: unknown) => void = () => undefined;\n const exited = new Promise<ProcessExit>((resolve, reject) => {\n resolveExit = resolve;\n rejectExit = reject;\n });\n // A rejected persistence operation is still observable through handle.exited.\n void exited.catch(() => undefined);\n const stdout = streams?.stdout ?? new PassThrough();\n const stderr = streams?.stderr ?? new PassThrough();\n const stdin = streams?.stdin ?? new PassThrough();\n let finishing: Promise<void> | undefined;\n const running: Active = {\n entry,\n finished: false,\n adopted: !streams,\n fail: (error) => rejectExit(error),\n handle: {\n pid: entry.pid,\n stdout,\n stderr,\n stdin,\n exited,\n stop: () =>\n serial(async () => {\n if (!running.finished) await stopEntry(entry);\n }),\n },\n finish(code, sig) {\n finishing ??= (async () => {\n running.finished = true;\n clearInterval(running.timer);\n if (entry.cgroup) {\n const memory = await cgroupMemory(entry.cgroup);\n entry.peakMemoryBytes = Math.max(entry.peakMemoryBytes, memory.peak);\n if (memory.oom) running.reason = \"memory-cap\";\n await cleanCgroup(entry.cgroup);\n }\n active.delete(entry.pid);\n entries.delete(entry.pid);\n try {\n await save();\n resolveExit({\n reason: running.reason ?? \"exit\",\n code,\n signal: sig,\n peakMemoryBytes: entry.peakMemoryBytes,\n });\n } catch (error) {\n rejectExit(error);\n throw error;\n }\n if (!streams) {\n (stdout as PassThrough).end();\n (stderr as PassThrough).end();\n stdin.end();\n }\n })();\n return finishing;\n },\n };\n active.set(entry.pid, running);\n return running;\n }\n\n function monitor(running: Active): void {\n if (running.finished) return;\n let sampling = false;\n const sample = async () => {\n if (sampling || running.finished) return;\n sampling = true;\n try {\n const entry = running.entry;\n const current = await identity(entry.pid, true, entry.token);\n if (running.finished) return;\n if (\n current?.start !== entry.start ||\n current.token !== entry.token ||\n current.group !== entry.pid\n ) {\n if (!current) {\n if (running.adopted) await running.finish(null, null);\n } else {\n // A live child that changes its environment or group can no longer\n // be supervised safely. Preserve the entry and surface the failure.\n throw new ProcessNotOwnedError(entry.pid);\n }\n return;\n }\n entry.peakMemoryBytes = Math.max(entry.peakMemoryBytes, current.rss);\n if (entry.cgroup) {\n const memory = await cgroupMemory(entry.cgroup);\n if (running.finished) return;\n entry.peakMemoryBytes = Math.max(entry.peakMemoryBytes, memory.peak);\n if (memory.oom) running.reason = \"memory-cap\";\n }\n if (entry.limits.memoryBytes && entry.peakMemoryBytes > entry.limits.memoryBytes)\n await stopEntry(entry, \"memory-cap\");\n else if (entry.limits.timeoutMs && Date.now() - entry.startedAt >= entry.limits.timeoutMs)\n await stopEntry(entry, \"timeout\");\n } finally {\n sampling = false;\n }\n };\n running.timer = setInterval(() => {\n void sample().catch((error: unknown) => {\n // Never signal on an observation error. Surface it to the caller and keep the registry.\n clearInterval(running.timer);\n if (!running.finished) running.fail(error);\n });\n }, pollMs);\n running.timer.unref();\n }\n\n async function launch(\n input: SpawnOptions & { columns?: number; rows?: number },\n terminal: boolean,\n ): Promise<ProcessHandle | PtyHandle> {\n await open();\n if ([...entries.keys()].some((pid) => !active.has(pid)))\n throw new Error(\"Call recover() before spawning with an existing registry\");\n if (\n typeof input.command !== \"string\" ||\n !input.command ||\n typeof input.kind !== \"string\" ||\n !input.kind\n )\n throw new Error(\"kind and command are required\");\n validateFilter({ labels: input.labels });\n if (\n terminal &&\n [input.columns ?? 80, input.rows ?? 24].some(\n (size) => !Number.isSafeInteger(size) || size <= 0,\n )\n )\n throw new Error(\"Terminal dimensions must be positive safe integers\");\n const limits = { ...options.defaults, ...input.limits };\n validateLimits(limits);\n const token = randomBytes(32).toString(\"hex\");\n const {\n file: executable,\n args,\n env,\n } = launchCommand(\n process.platform,\n process.execPath,\n input.command,\n input.args ?? [],\n limits,\n { ...process.env, ...input.env },\n token,\n );\n let pid: number;\n let streams: Pick<ProcessHandle, \"stdin\" | \"stdout\" | \"stderr\">;\n let terminalProcess: Pty | undefined;\n let exit: { code: number | null; signal: string | null } | undefined;\n let spawnError: Error | undefined;\n let running: Active | undefined;\n const onExit = (code: number | null, sig: string | null) => {\n exit = { code, signal: sig };\n if (running) void running.finish(code, sig).catch(() => undefined);\n };\n if (terminal) {\n // Optional peer loaded only here. Structural types follow node-pty's official typings.\n const peer = \"node-pty\";\n const pty = (await import(peer)) as PtyModule;\n terminalProcess = pty.spawn(executable, args, {\n cwd: input.cwd,\n env: Object.fromEntries(\n Object.entries(env).filter((pair): pair is [string, string] => pair[1] !== undefined),\n ),\n cols: input.columns ?? 80,\n rows: input.rows ?? 24,\n name: \"xterm-256color\",\n });\n pid = terminalProcess.pid;\n const output = new PassThrough();\n const errors = new PassThrough();\n const instance = terminalProcess;\n const dataSubscription = instance.onData((data) => output.write(data));\n const inputStream = new Writable({\n write(chunk: Buffer, _encoding, callback) {\n instance.write(chunk.toString());\n callback();\n },\n });\n const exitSubscription = instance.onExit((event) => {\n dataSubscription.dispose();\n exitSubscription.dispose();\n output.end();\n errors.end();\n inputStream.end();\n onExit(event.exitCode, event.signal ? String(event.signal) : null);\n });\n streams = { stdout: output, stderr: errors, stdin: inputStream };\n } else {\n const child = spawnChild(executable, args, {\n cwd: input.cwd,\n env,\n detached: process.platform !== \"win32\",\n windowsHide: true,\n stdio: \"pipe\",\n });\n child.on(\"error\", (error) => {\n spawnError = error;\n });\n // Finish on leader exit so cgroup cleanup can stop remaining members;\n // waiting for close could deadlock on a descendant holding a pipe open.\n child.on(\"exit\", onExit);\n if (!child.pid) throw new Error(\"Could not start process\");\n pid = child.pid;\n streams = { stdout: child.stdout, stderr: child.stderr, stdin: child.stdin };\n }\n let entry: RegistryEntry | undefined;\n try {\n const deadline = Date.now() + 5000;\n let current = await identity(pid, true, token);\n while (!current?.state.startsWith(\"T\") || current.token !== token) {\n if (spawnError) throw spawnError;\n if (exit || Date.now() >= deadline)\n throw new Error(\"Child failed to reach the resource setup gate\");\n await delay(10);\n current = await identity(pid, true, token);\n }\n if (current.group !== pid) throw new Error(\"Child did not create an isolated process group\");\n entry = {\n pid,\n owner: options.owner,\n type: input.kind,\n command: input.command,\n args: [...(input.args ?? [])],\n labels: { ...input.labels },\n startedAt: Date.now(),\n limits,\n enforcement: process.platform === \"win32\" ? \"rss-polling\" : \"rlimit-polling\",\n peakMemoryBytes: current.rss,\n token,\n start: current.start,\n gated: true,\n };\n entry.cgroup = await attachCgroup(pid, token, limits, options.cgroupRoot);\n if (entry.cgroup) entry.enforcement = \"cgroup-v2\";\n entries.set(pid, entry);\n await save();\n running = track(entry, streams);\n await signal(entry, \"SIGCONT\");\n delete entry.gated;\n await save();\n monitor(running);\n if (terminalProcess) {\n const instance = terminalProcess;\n return {\n ...running.handle,\n write: (data: string) => instance.write(data),\n resize: (columns: number, rows: number) => instance.resize(columns, rows),\n };\n }\n return running.handle;\n } catch (error) {\n // This PID was created by this call. Kill only after its random token is verified.\n const current = await identity(pid, true, token);\n if (current?.token === token) {\n const failed = { pid, token, start: current.start };\n if (await owns(failed)) {\n try {\n if (process.platform === \"win32\") await executeCommand(taskkillCommand(pid));\n else process.kill(-pid, \"SIGKILL\");\n } catch {\n /* Already exited. */\n }\n }\n }\n if (entry && !running) {\n entries.delete(pid);\n await save();\n }\n if (entry?.cgroup) await cleanCgroup(entry.cgroup);\n throw error;\n }\n }\n\n const api: ProcessSupervisor = {\n spawn: (input) => serial(() => launch(input, false)),\n spawnPty: (input) => serial(async () => (await launch(input, true)) as PtyHandle),\n list: (filter = {}) =>\n serial(async () => {\n validateFilter(filter);\n await open();\n return [...entries.values()].filter((entry) => matches(entry, filter)).map(info);\n }),\n stop: (filter = {}) =>\n serial(async () => {\n validateFilter(filter);\n await open();\n for (const entry of [...entries.values()].filter((entry) => matches(entry, filter)))\n await stopEntry(entry);\n }),\n stopPid: (pid) =>\n serial(async () => {\n await open();\n const entry = entries.get(pid);\n if (!entry) throw new ProcessNotOwnedError(pid);\n await stopEntry(entry);\n }),\n recover: (recovery = {}) =>\n serial(async () => {\n await open();\n const result: RecoveryResult = { adopted: [], stopped: [], cleared: [] };\n for (const entry of [...entries.values()]) {\n if (active.has(entry.pid)) continue;\n if (!(await owns(entry))) {\n entries.delete(entry.pid);\n result.cleared.push(entry.pid);\n continue;\n }\n // Never trust a persisted filesystem path for cgroup I/O after a restart.\n entry.cgroup = await recoveredCgroup(entry.pid, entry.token);\n entry.enforcement = entry.cgroup\n ? \"cgroup-v2\"\n : process.platform === \"win32\"\n ? \"rss-polling\"\n : \"rlimit-polling\";\n const keep = await recovery.keep?.(info(entry));\n if (keep) {\n const running = track(entry);\n monitor(running);\n if (entry.gated) {\n await signal(entry, \"SIGCONT\");\n delete entry.gated;\n }\n result.adopted.push(entry.pid);\n } else {\n await stopEntry(entry);\n result.stopped.push(entry.pid);\n }\n }\n await save();\n return result;\n }),\n stopAll() {\n shutdown ??= serial(async () => {\n await open();\n closing = true;\n try {\n const errors: unknown[] = [];\n for (const entry of [...entries.values()]) {\n try {\n if (await owns(entry)) await stopEntry(entry);\n else {\n const running = active.get(entry.pid);\n if (running) await running.finish(null, null);\n else entries.delete(entry.pid);\n }\n } catch (error) {\n errors.push(error);\n }\n }\n await save();\n if (errors.length)\n throw new AggregateError(errors, \"Some owned processes could not be stopped\");\n } finally {\n await registry.close();\n }\n });\n return shutdown;\n },\n };\n return api;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["path","readFile","mkdir","writeFile","rmdir","randomBytes","createHmac","connect","promisify","execFile","readFile","mkdir","path","readFile","randomBytes","rename","rm","writeFile","path","delay","PassThrough","randomBytes","Writable","spawnChild"],"sources":["../../../../../../../packages/process/src/resources.ts","../../../../../../../packages/process/src/guardian.ts","../../../../../../../packages/process/src/platform.ts","../../../../../../../packages/process/src/identity.ts","../../../../../../../packages/process/src/registry.ts","../../../../../../../packages/process/src/supervisor.ts"],"sourcesContent":["import { mkdir, readFile, rmdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { ProcessLimits } from \"./types\";\n\nfunction mountedCgroup(membership: string): string | undefined {\n const mount = \"/sys/fs/cgroup\";\n const directory = path.resolve(mount, `.${membership}`);\n // A cgroup namespace can report ancestors using /../ components. Never\n // normalize those into writes outside the standard cgroup mount.\n return directory === mount || directory.startsWith(`${mount}/`) ? directory : undefined;\n}\n\nexport function validateLimits(limits: ProcessLimits, platform: string = process.platform): void {\n for (const [key, value] of Object.entries(limits)) {\n if (\n ![\"timeoutMs\", \"memoryBytes\", \"cpuSeconds\", \"virtualMemoryBytes\"].includes(key) ||\n !Number.isSafeInteger(value) ||\n value <= 0\n )\n throw new Error(`Invalid process limit: ${key}`);\n }\n if (platform !== \"linux\" && limits.virtualMemoryBytes !== undefined)\n throw new Error(\"virtualMemoryBytes is supported only on Linux\");\n if (platform === \"win32\" && limits.cpuSeconds !== undefined)\n throw new Error(\"cpuSeconds is supported only on POSIX platforms\");\n}\n\n/**\n * POSIX shell applies inherited limits. Linux stops here; macOS execs a gated guardian.\n * Flags/units: Apple's bash-3.2/builtins/ulimit.def and installed Bash manual.\n * https://github.com/apple-oss-distributions/bash/blob/main/bash-3.2/builtins/ulimit.def\n */\nexport function launcher(limits: ProcessLimits, platform: string = process.platform): string {\n const commands = [\"set -e\"];\n if (limits.cpuSeconds) commands.push(`ulimit -t ${limits.cpuSeconds}`);\n if (limits.virtualMemoryBytes)\n commands.push(`ulimit -v ${Math.ceil(limits.virtualMemoryBytes / 1024)}`);\n // Linux ignores RLIMIT_RSS; polling remains the fallback resident-memory cap.\n // Darwin's RLIMIT_DATA restricts heap growth, not total resident memory.\n if (limits.memoryBytes)\n commands.push(\n `ulimit -${platform === \"darwin\" ? \"d\" : \"m\"} ${Math.ceil(limits.memoryBytes / 1024)}`,\n );\n if (platform === \"linux\") commands.push('kill -STOP \"$$\"');\n commands.push('exec \"$@\"');\n return commands.join(\"\\n\");\n}\n\n/** https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html */\nexport async function attachCgroup(\n pid: number,\n token: string,\n limits: ProcessLimits,\n root?: string,\n): Promise<string | undefined> {\n if (process.platform !== \"linux\") return undefined;\n let directory: string | undefined;\n try {\n const own = (await readFile(\"/proc/self/cgroup\", \"utf8\"))\n .split(\"\\n\")\n .find((line) => line.startsWith(\"0::\"))\n ?.slice(3);\n if (!root && own === undefined) return undefined;\n const parent = root ?? mountedCgroup(own ?? \"/\");\n if (!parent) return undefined;\n // Do not enable controllers or move the app: delegation belongs to the service manager.\n await readFile(path.join(parent, \"cgroup.controllers\"), \"utf8\");\n directory = path.join(parent, `warlock-${token}`);\n await mkdir(directory);\n if (limits.memoryBytes) {\n await writeFile(path.join(directory, \"memory.max\"), String(limits.memoryBytes));\n await writeFile(path.join(directory, \"memory.oom.group\"), \"1\");\n }\n await writeFile(path.join(directory, \"cgroup.procs\"), String(pid));\n return directory;\n } catch {\n if (directory) await rmdir(directory).catch(() => undefined);\n return undefined;\n }\n}\n\nexport async function cgroupMemory(directory: string): Promise<{ peak: number; oom: boolean }> {\n const [peak, events] = await Promise.all([\n readFile(path.join(directory, \"memory.peak\"), \"utf8\").catch(() => \"0\"),\n readFile(path.join(directory, \"memory.events\"), \"utf8\").catch(() => \"\"),\n ]);\n return { peak: Number(peak) || 0, oom: Number(/^oom_kill (\\d+)/m.exec(events)?.[1] ?? 0) > 0 };\n}\n\n/** Only reattach the token-specific cgroup confirmed by the kernel membership file. */\nexport async function recoveredCgroup(pid: number, token: string): Promise<string | undefined> {\n if (process.platform !== \"linux\") return undefined;\n let source: string;\n try {\n source = await readFile(`/proc/${pid}/cgroup`, \"utf8\");\n } catch (error) {\n if ([\"ENOENT\", \"ESRCH\"].includes(String((error as NodeJS.ErrnoException).code)))\n return undefined;\n throw error;\n }\n const membership = source\n .split(\"\\n\")\n .find((line) => line.startsWith(\"0::\"))\n ?.slice(3);\n if (!membership || path.basename(membership) !== `warlock-${token}`) return undefined;\n return mountedCgroup(membership);\n}\n\nexport async function cleanCgroup(directory: string): Promise<void> {\n // This unique cgroup was created for this token, or recovered after identity proof.\n // cgroup.kill also covers descendants that created their own POSIX session.\n await writeFile(path.join(directory, \"cgroup.kill\"), \"1\").catch(() => undefined);\n await rmdir(directory).catch(() => undefined);\n}\n","import { createHmac, randomBytes } from \"node:crypto\";\nimport { connect } from \"node:net\";\nimport type { Command, OwnershipProof } from \"./platform\";\nimport { launcher } from \"./resources\";\nimport type { ProcessLimits } from \"./types\";\n\nexport function guardianAddress(platform: string, pid: number, uid: number): string {\n return platform === \"win32\"\n ? `\\\\\\\\.\\\\pipe\\\\warlock-process-${pid}`\n : `/tmp/warlock-process-${uid}-${pid}.sock`;\n}\n\n/** A live guardian reads its own environment, never argv, to prove ownership.\n * Node IPC is native-free: https://nodejs.org/api/net.html#ipc-support\n * The stable root PID stays alive for the workload and survives supervisor restarts.\n */\nexport const guardianScript = String.raw`\nconst { createServer } = require(\"node:net\");\nconst { createHmac } = require(\"node:crypto\");\nconst { spawn } = require(\"node:child_process\");\nconst { chmodSync, unlinkSync } = require(\"node:fs\");\nconst token = process.env.WARLOCK_PROCESS_TOKEN;\nconst address = process.platform === \"win32\"\n ? \"\\\\\\\\.\\\\pipe\\\\warlock-process-\" + process.pid\n : \"/tmp/warlock-process-\" + process.getuid() + \"-\" + process.pid + \".sock\";\nlet gated = true;\nconst finish = (code) => { server.close(); process.exit(code); };\nconst start = () => {\n if (!gated) return;\n gated = false;\n const [command, ...args] = JSON.parse(process.env.WARLOCK_PROCESS_LAUNCH);\n const env = { ...process.env };\n for (const key of Object.keys(env)) {\n const name = process.platform === \"win32\" ? key.toUpperCase() : key;\n if (name === \"WARLOCK_PROCESS_TOKEN\" || name === \"WARLOCK_PROCESS_LAUNCH\") delete env[key];\n }\n const child = spawn(command, args, { env, stdio: \"inherit\", windowsHide: true });\n child.on(\"error\", (error) => { console.error(error.message); finish(127); });\n child.on(\"exit\", (code, signal) => {\n if (signal && process.platform !== \"win32\") process.kill(process.pid, signal);\n else finish(code ?? 1);\n });\n};\nconst server = createServer((socket) => {\n socket.setTimeout(2000, () => socket.destroy());\n socket.on(\"error\", () => {});\n let input = \"\";\n socket.on(\"data\", (chunk) => {\n input += chunk;\n if (input.length > 1024) { socket.destroy(); return; }\n if (!input.endsWith(\"\\n\")) return;\n try {\n const request = JSON.parse(input);\n if (!/^[a-f0-9]{64}$/.test(request.nonce)) { socket.destroy(); return; }\n const sign = (value) => createHmac(\"sha256\", token).update(value).digest(\"hex\");\n if (request.resume === sign(request.nonce + \":resume\")) start();\n socket.end(JSON.stringify({ pid: process.pid, digest: sign(request.nonce + \":\" + process.pid + \":\" + gated), gated }) + \"\\n\");\n } catch { socket.destroy(); }\n });\n});\nserver.on(\"error\", (error) => { console.error(error.message); process.exit(126); });\nif (process.platform !== \"win32\") {\n try { unlinkSync(address); } catch (error) { if (error.code !== \"ENOENT\") throw error; }\n process.on(\"exit\", () => { try { unlinkSync(address); } catch {} });\n}\nserver.listen(address, () => { if (process.platform !== \"win32\") chmodSync(address, 0o600); });\n`;\n\nexport async function guardianProof(\n pid: number,\n resume?: string,\n expectedToken: string | undefined = resume,\n): Promise<OwnershipProof | undefined> {\n if (!expectedToken) return undefined;\n const token = expectedToken;\n const nonce = randomBytes(32).toString(\"hex\");\n const sign = (value: string) => createHmac(\"sha256\", token).update(value).digest(\"hex\");\n const address = guardianAddress(process.platform, pid, process.getuid?.() ?? 0);\n return new Promise((resolve, reject) => {\n const socket = connect(address);\n let data = \"\";\n const fail = (error: Error) => {\n socket.destroy();\n reject(error);\n };\n socket.setTimeout(2000, () => fail(new Error(\"Process ownership observation timed out\")));\n socket.on(\"connect\", () =>\n socket.write(\n JSON.stringify({ nonce, resume: resume ? sign(nonce + \":resume\") : undefined }) + \"\\n\",\n ),\n );\n socket.on(\"data\", (chunk: Buffer) => {\n data += chunk.toString();\n if (data.length > 1024) fail(new Error(\"Invalid process ownership response\"));\n });\n socket.on(\"error\", (error: NodeJS.ErrnoException) => {\n if ([\"ENOENT\", \"ECONNREFUSED\"].includes(error.code ?? \"\")) resolve(undefined);\n else reject(error);\n });\n socket.on(\"end\", () => {\n try {\n resolve(parseGuardianProof(data, pid, nonce, token));\n } catch (error) {\n reject(error);\n }\n });\n });\n}\n\n/** Malformed observation is an error; an authenticated identity mismatch is unowned. */\nexport function parseGuardianProof(\n source: string,\n pid: number,\n nonce: string,\n token: string,\n): OwnershipProof | undefined {\n const proof: unknown = JSON.parse(source);\n if (\n !proof ||\n typeof proof !== \"object\" ||\n !(\"pid\" in proof) ||\n typeof proof.pid !== \"number\" ||\n !(\"gated\" in proof) ||\n typeof proof.gated !== \"boolean\" ||\n !(\"digest\" in proof) ||\n typeof proof.digest !== \"string\"\n )\n throw new Error(\"Invalid process ownership response\");\n const expected = createHmac(\"sha256\", token)\n .update(nonce + \":\" + pid + \":\" + proof.gated)\n .digest(\"hex\");\n if (proof.pid !== pid || proof.digest !== expected) return undefined;\n return { pid, token, gated: proof.gated };\n}\n\n/** Pure launch construction, including case-insensitive Windows environment keys. */\nexport function launchCommand(\n platform: string,\n node: string,\n command: string,\n args: string[],\n limits: ProcessLimits,\n environment: NodeJS.ProcessEnv,\n token: string,\n): Omit<Command, \"env\"> & { env: NodeJS.ProcessEnv } {\n const portable = platform !== \"linux\";\n const env: NodeJS.ProcessEnv = Object.fromEntries(\n Object.entries(environment).filter(\n ([key]) =>\n ![\"WARLOCK_PROCESS_TOKEN\", \"WARLOCK_PROCESS_LAUNCH\"].includes(\n platform === \"win32\" ? key.toUpperCase() : key,\n ),\n ),\n );\n env.WARLOCK_PROCESS_TOKEN = token;\n if (portable) env.WARLOCK_PROCESS_LAUNCH = JSON.stringify([command, ...args]);\n const executable = portable ? node : command;\n const arguments_ = portable ? [\"-e\", guardianScript] : args;\n return {\n file: platform === \"win32\" ? executable : \"/bin/sh\",\n args:\n platform === \"win32\"\n ? arguments_\n : [\"-c\", launcher(limits, platform), \"warlock-process\", executable, ...arguments_],\n env,\n };\n}\n","import type { Identity } from \"./identity\";\n\nexport type PortablePlatform = \"darwin\" | \"win32\";\nexport type Command = { file: string; args: string[]; env?: Record<string, string> };\nexport type Execute = (command: Command) => Promise<string>;\nexport type OwnershipProof = { pid: number; token: string; gated: boolean };\nexport type ReadProof = (pid: number) => Promise<OwnershipProof | undefined>;\ntype Row = {\n pid: number;\n parent: number;\n start: string;\n rss: number;\n group: number;\n state: string;\n};\n\nexport class UnsupportedProcessPlatformError extends Error {\n constructor(public readonly platform: string) {\n super(`Process supervision is unsupported on ${platform}`);\n this.name = \"UnsupportedProcessPlatformError\";\n }\n}\nexport function supported(platform: string): void {\n if (![\"linux\", \"darwin\", \"win32\"].includes(platform))\n throw new UnsupportedProcessPlatformError(platform);\n}\nfunction validPid(pid: number): void {\n if (!Number.isSafeInteger(pid) || pid <= 1) throw new Error(\"Invalid process PID\");\n}\n\n/** Apple ps(1): lstart uses strftime %c. LC_ALL=C gives %a %b %e %H:%M:%S %Y.\n * https://github.com/apple-oss-distributions/adv_cmds/blob/main/ps/ps.1\n * https://github.com/apple-oss-distributions/adv_cmds/blob/main/ps/print.c (lstarted)\n * https://github.com/apple-oss-distributions/Libc/blob/main/stdtime/FreeBSD/timelocal.c (_C_time_locale)\n */\nexport function parseDarwinStart(source: string): string {\n const match =\n /^(Sun|Mon|Tue|Wed|Thu|Fri|Sat) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s+(\\d{1,2}) (\\d{2}):(\\d{2}):(\\d{2}) (\\d{4})$/.exec(\n source.trim(),\n );\n if (!match) throw new Error(\"Invalid macOS process start time\");\n const [, weekday, month, day, hour, minute, second, year] = match;\n const monthIndex = \"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec\"\n .split(\" \")\n .indexOf(month ?? \"\");\n const date = new Date(\n Date.UTC(Number(year), monthIndex, Number(day), Number(hour), Number(minute), Number(second)),\n );\n if (\n date.getUTCFullYear() !== Number(year) ||\n date.getUTCMonth() !== monthIndex ||\n date.getUTCDate() !== Number(day) ||\n date.getUTCHours() !== Number(hour) ||\n date.getUTCMinutes() !== Number(minute) ||\n date.getUTCSeconds() !== Number(second) ||\n \"Sun Mon Tue Wed Thu Fri Sat\".split(\" \")[date.getUTCDay()] !== weekday\n )\n throw new Error(\"Invalid macOS process start time\");\n return String(date.getTime() / 1000);\n}\nexport function darwinCommand(pid: number, tree = false): Command {\n validPid(pid);\n return {\n file: \"/bin/ps\",\n args: [...(tree ? [\"-ax\"] : [\"-p\", String(pid)]), \"-o\", \"pid=,ppid=,pgid=,state=,rss=,lstart=\"],\n env: { LC_ALL: \"C\", TZ: \"UTC\" },\n };\n}\nexport function parseDarwinRows(source: string): Row[] {\n return source.trim()\n ? source\n .trim()\n .split(\"\\n\")\n .map((line) => {\n const match = /^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\S+)\\s+(\\d+)\\s+(.+?)\\s*$/.exec(line);\n if (!match) throw new Error(\"Invalid macOS process observation\");\n return {\n pid: Number(match[1]),\n parent: Number(match[2]),\n group: Number(match[3]),\n state: match[4] ?? \"\",\n rss: Number(match[5]) * 1024,\n start: parseDarwinStart(match[6] ?? \"\"),\n };\n })\n : [];\n}\n\n/** CIM CreationDate is a DateTime; serialize UTC ticks as text, never a locale date\n * or JSON number (100ns ticks exceed Number's precision). WorkingSetSize is bytes.\n * https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-process\n */\nexport function windowsCommand(pid: number, tree = false): Command {\n validPid(pid);\n const query = `Get-CimInstance Win32_Process${tree ? \"\" : ` -Filter 'ProcessId=${pid}'`}`;\n const script = [\n \"$ErrorActionPreference = 'Stop'\",\n `$rows = @(${query} | ForEach-Object {`,\n `if ([int]$_.ProcessId -eq ${pid} -and $null -eq $_.CreationDate) { throw 'Cannot read process creation time' }`,\n \"if ($null -ne $_.CreationDate) {\",\n \"[pscustomobject]@{ pid = [int]$_.ProcessId; parent = [int]$_.ParentProcessId; start = $_.CreationDate.ToUniversalTime().Ticks.ToString([Globalization.CultureInfo]::InvariantCulture); rss = [double]$_.WorkingSetSize }\",\n \"}\",\n \"})\",\n \"ConvertTo-Json -InputObject $rows -Compress\",\n ].join(\"\\n\");\n return {\n file: \"powershell.exe\",\n args: [\"-NoLogo\", \"-NoProfile\", \"-NonInteractive\", \"-Command\", script],\n };\n}\nexport function parseWindowsRows(source: string): Row[] {\n const value: unknown = JSON.parse(source.replace(/^\\uFEFF/, \"\"));\n if (!Array.isArray(value)) throw new Error(\"Invalid Windows process observation\");\n return value.map((row: unknown) => {\n if (\n !row ||\n typeof row !== \"object\" ||\n !(\"pid\" in row) ||\n typeof row.pid !== \"number\" ||\n !Number.isSafeInteger(row.pid) ||\n row.pid < 0 ||\n !(\"parent\" in row) ||\n typeof row.parent !== \"number\" ||\n !Number.isSafeInteger(row.parent) ||\n row.parent < 0 ||\n !(\"start\" in row) ||\n typeof row.start !== \"string\" ||\n !/^\\d{18}$/.test(row.start) ||\n !(\"rss\" in row) ||\n typeof row.rss !== \"number\" ||\n !Number.isFinite(row.rss) ||\n row.rss < 0\n )\n throw new Error(\"Invalid Windows process observation\");\n return {\n pid: row.pid,\n parent: row.parent,\n start: row.start,\n rss: row.rss,\n group: row.pid,\n state: \"R\",\n };\n });\n}\nexport function taskkillCommand(pid: number): Command {\n validPid(pid);\n // Never accept an image name, filter, wildcard or shell interpolation.\n return { file: \"taskkill.exe\", args: [\"/PID\", String(pid), \"/T\", \"/F\"] };\n}\nexport function treeMemory(rows: Row[], pid: number): number {\n const root = rows.find((row) => row.pid === pid);\n if (!root) return 0;\n const included = new Map<number, Row>([[pid, root]]);\n let changed = true;\n while (changed) {\n changed = false;\n for (const row of rows) {\n const parent = included.get(row.parent);\n // Reject a reused parent PID whose creation time is later than the child.\n if (!included.has(row.pid) && parent && BigInt(row.start) >= BigInt(parent.start)) {\n included.set(row.pid, row);\n changed = true;\n }\n }\n }\n return [...included.values()].reduce((total, row) => total + row.rss, 0);\n}\n\n/** All OS effects are injected; foreign-platform specs never execute real commands. */\nexport async function portableIdentity(\n platform: PortablePlatform,\n pid: number,\n token: boolean,\n execute: Execute,\n readProof: ReadProof,\n): Promise<Identity | undefined> {\n validPid(pid);\n const command = platform === \"darwin\" ? darwinCommand : windowsCommand;\n const parse = platform === \"darwin\" ? parseDarwinRows : parseWindowsRows;\n const rows = parse(await execute(command(pid, token)));\n const before = rows.find((row) => row.pid === pid);\n if (!before || /^[ZX]/.test(before.state)) return undefined;\n if (!token) return before;\n const proof = await readProof(pid);\n const after = parse(await execute(command(pid))).find((row) => row.pid === pid);\n if (\n !after ||\n after.start !== before.start ||\n after.group !== before.group ||\n /^[ZX]/.test(after.state)\n )\n return undefined;\n return {\n start: after.start,\n group: after.group,\n state: proof?.pid === pid && proof.gated ? \"T\" : after.state,\n rss: treeMemory(rows, pid),\n token: proof?.pid === pid ? proof.token : undefined,\n };\n}\n","import { execFile } from \"node:child_process\";\nimport { readFile } from \"node:fs/promises\";\nimport { promisify } from \"node:util\";\n\nimport { guardianProof } from \"./guardian\";\nimport { portableIdentity, type Command } from \"./platform\";\n\nconst execute = promisify(execFile);\nexport async function executeCommand(command: Command): Promise<string> {\n const { stdout } = await execute(command.file, command.args, {\n env: { ...process.env, ...command.env },\n timeout: 5000,\n maxBuffer: 4 * 1024 * 1024,\n windowsHide: true,\n });\n return stdout;\n}\nexport const TOKEN_ENV = \"WARLOCK_PROCESS_TOKEN\";\nexport type Identity = { start: string; group: number; state: string; rss: number; token?: string };\n\nfunction statFields(stat: string): string[] {\n return stat\n .slice(stat.lastIndexOf(\")\") + 2)\n .trim()\n .split(/\\s+/);\n}\n\n/**\n * A task that is exiting has already released its memory map (stat field 23,\n * vsize, reads 0) before it turns into a zombie. Linux then reports its /proc\n * files as root-owned, so it is never a live child to verify or signal.\n */\nfunction exiting(fields: string[]): boolean {\n return [\"Z\", \"X\"].includes(fields[0] ?? \"\") || fields[20] === \"0\";\n}\n\n/** /proc fields: https://man7.org/linux/man-pages/man5/proc_pid_stat.5.html */\nexport async function identity(\n pid: number,\n token = false,\n expectedToken?: string,\n): Promise<Identity | undefined> {\n if (!Number.isSafeInteger(pid) || pid <= 1) return undefined;\n try {\n if (process.platform === \"linux\") {\n const stat = await readFile(`/proc/${pid}/stat`, \"utf8\");\n const fields = statFields(stat);\n const state = fields[0];\n const start = fields[19];\n if (!state || !start || exiting(fields)) return undefined;\n const boot = (await readFile(\"/proc/sys/kernel/random/boot_id\", \"utf8\")).trim();\n const status = await readFile(`/proc/${pid}/status`, \"utf8\");\n let environment = \"\";\n if (token) {\n try {\n environment = await readFile(`/proc/${pid}/environ`, \"utf8\");\n } catch (error) {\n // Without root, the environment of a task that started exiting after\n // the first stat read is unreadable. Only that case counts as gone;\n // any other unreadable environment still fails closed.\n if ((error as { code?: unknown }).code !== \"EACCES\") throw error;\n const now = statFields(await readFile(`/proc/${pid}/stat`, \"utf8\"));\n if (now[19] !== start || exiting(now)) return undefined;\n throw error;\n }\n }\n // /proc reads are separate operations. Reject PID reuse or a group change\n // during observation before combining a token with a start identity.\n const after = statFields(await readFile(`/proc/${pid}/stat`, \"utf8\"));\n if (after[19] !== start || after[2] !== fields[2] || exiting(after)) return undefined;\n return {\n start: `${boot}:${start}`,\n group: Number(fields[2]),\n state: after[0] ?? state,\n rss: Number(/^VmRSS:\\s+(\\d+)/m.exec(status)?.[1] ?? 0) * 1024,\n token: environment\n .split(\"\\0\")\n .find((entry) => entry.startsWith(`${TOKEN_ENV}=`))\n ?.slice(TOKEN_ENV.length + 1),\n };\n }\n if (process.platform === \"darwin\" || process.platform === \"win32\")\n return await portableIdentity(process.platform, pid, token, executeCommand, (selected) =>\n guardianProof(selected, undefined, expectedToken),\n );\n return undefined;\n } catch (error) {\n const code = (error as { code?: unknown }).code;\n if (\n (process.platform === \"linux\" && [\"ENOENT\", \"ESRCH\"].includes(String(code))) ||\n (process.platform === \"darwin\" &&\n code === 1 &&\n !(error as { stdout?: string }).stdout?.trim())\n )\n return undefined;\n throw error;\n }\n}\n\nexport class ProcessNotOwnedError extends Error {\n constructor(public readonly pid: number) {\n super(`Process ${pid} does not have a verified supervisor identity`);\n this.name = \"ProcessNotOwnedError\";\n }\n}\n\nexport async function owns(entry: { pid: number; start: string; token: string }): Promise<boolean> {\n const current = await identity(entry.pid, true, entry.token);\n return (\n current?.start === entry.start && current.token === entry.token && current.group === entry.pid\n );\n}\n","import { randomBytes } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { identity } from \"./identity\";\nimport type { ProcessInfo } from \"./types\";\n\nexport type RegistryEntry = ProcessInfo & {\n token: string;\n start: string;\n cgroup?: string;\n /** Persisted launch gate: recovery may resume an adopted, interrupted launch. */\n gated?: boolean;\n};\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction strings(value: unknown): value is Record<string, string> {\n return record(value) && Object.values(value).every((item) => typeof item === \"string\");\n}\nexport function parseRegistry(source: string, owner: string): RegistryEntry[] {\n const value: unknown = JSON.parse(source);\n if (\n !record(value) ||\n value.version !== 1 ||\n value.owner !== owner ||\n !Array.isArray(value.entries)\n )\n throw new Error(\"Invalid process registry or different owner\");\n const entries: RegistryEntry[] = [];\n for (const entry of value.entries) {\n if (\n !record(entry) ||\n entry.owner !== owner ||\n typeof entry.pid !== \"number\" ||\n !Number.isSafeInteger(entry.pid) ||\n Number(entry.pid) <= 1 ||\n typeof entry.token !== \"string\" ||\n !/^[a-f0-9]{64}$/.test(entry.token) ||\n typeof entry.start !== \"string\" ||\n typeof entry.type !== \"string\" ||\n typeof entry.command !== \"string\" ||\n !Array.isArray(entry.args) ||\n !entry.args.every((arg) => typeof arg === \"string\") ||\n !strings(entry.labels) ||\n typeof entry.startedAt !== \"number\" ||\n !Number.isFinite(entry.startedAt) ||\n entry.startedAt < 0 ||\n !record(entry.limits) ||\n !Object.entries(entry.limits).every(\n ([key, limit]) =>\n [\"timeoutMs\", \"memoryBytes\", \"cpuSeconds\", \"virtualMemoryBytes\"].includes(key) &&\n typeof limit === \"number\" &&\n Number.isSafeInteger(limit) &&\n limit > 0,\n ) ||\n ![\"cgroup-v2\", \"rlimit-polling\", \"rss-polling\"].includes(String(entry.enforcement)) ||\n typeof entry.peakMemoryBytes !== \"number\" ||\n !Number.isFinite(entry.peakMemoryBytes) ||\n entry.peakMemoryBytes < 0 ||\n (entry.cgroup !== undefined && typeof entry.cgroup !== \"string\") ||\n (entry.gated !== undefined && typeof entry.gated !== \"boolean\")\n )\n throw new Error(\"Invalid process registry entry\");\n entries.push(entry as RegistryEntry);\n }\n if (new Set(entries.map((entry) => entry.pid)).size !== entries.length)\n throw new Error(\"Duplicate registry PID\");\n return entries;\n}\n\n/** One writer per registry. A crash leaves a lease which only a dead identity can release. */\nexport class Registry {\n private queue: Promise<void> = Promise.resolve();\n private locked = false;\n constructor(\n public readonly file: string,\n private readonly owner: string,\n ) {}\n async open(): Promise<RegistryEntry[]> {\n await mkdir(path.dirname(this.file), { recursive: true, mode: 0o700 });\n const self = await identity(process.pid);\n if (!self) throw new Error(\"Cannot verify registry writer identity\");\n const lock = `${this.file}.lock`;\n try {\n await mkdir(lock, { mode: 0o700 });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n const reclaim = `${lock}.reclaim`;\n await mkdir(reclaim, { mode: 0o700 });\n try {\n const lease: unknown = JSON.parse(await readFile(path.join(lock, \"writer.json\"), \"utf8\"));\n if (!record(lease) || typeof lease.pid !== \"number\" || typeof lease.start !== \"string\")\n throw new Error(\"Invalid registry writer lease; inspect it manually\");\n const writer = await identity(lease.pid);\n if (writer?.start === lease.start) throw new Error(\"Process registry is already in use\");\n const stale = `${lock}.${randomBytes(8).toString(\"hex\")}`;\n await rename(lock, stale);\n await rm(stale, { recursive: true });\n await mkdir(lock, { mode: 0o700 });\n } finally {\n await rm(reclaim, { recursive: true });\n }\n }\n this.locked = true;\n try {\n await writeFile(\n path.join(lock, \"writer.json\"),\n JSON.stringify({ pid: process.pid, start: self.start }),\n { mode: 0o600 },\n );\n try {\n return parseRegistry(await readFile(this.file, \"utf8\"), this.owner);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw error;\n }\n } catch (error) {\n await this.close();\n throw error;\n }\n }\n save(entries: RegistryEntry[]): Promise<void> {\n const source = JSON.stringify({ version: 1, owner: this.owner, entries }, null, 2) + \"\\n\";\n const next = this.queue.then(async () => {\n const temporary = `${this.file}.${randomBytes(8).toString(\"hex\")}.tmp`;\n try {\n await writeFile(temporary, source, { mode: 0o600, flag: \"wx\" });\n await rename(temporary, this.file);\n } finally {\n await rm(temporary, { force: true });\n }\n });\n this.queue = next.catch(() => undefined);\n return next;\n }\n async close(): Promise<void> {\n await this.queue;\n if (this.locked) {\n await rm(`${this.file}.lock`, { recursive: true });\n this.locked = false;\n }\n }\n}\n","import { spawn as spawnChild } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport path from \"node:path\";\nimport { PassThrough, Writable } from \"node:stream\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { identity, owns, ProcessNotOwnedError, executeCommand } from \"./identity\";\nimport { guardianProof, launchCommand } from \"./guardian\";\nimport { supported, taskkillCommand } from \"./platform\";\nimport { Registry, type RegistryEntry } from \"./registry\";\nimport {\n attachCgroup,\n cgroupMemory,\n cleanCgroup,\n recoveredCgroup,\n validateLimits,\n} from \"./resources\";\nimport type {\n ProcessExit,\n ProcessFilter,\n ProcessHandle,\n ProcessInfo,\n ProcessSupervisor,\n PtyHandle,\n RecoveryResult,\n SpawnOptions,\n SupervisorOptions,\n} from \"./types\";\n\ntype Pty = {\n pid: number;\n onData(listener: (data: string) => void): { dispose(): void };\n onExit(listener: (event: { exitCode: number; signal?: number }) => void): { dispose(): void };\n write(data: string): void;\n resize(columns: number, rows: number): void;\n};\ntype PtyModule = {\n spawn(\n command: string,\n args: string[],\n options: {\n cwd?: string;\n env: Record<string, string>;\n cols: number;\n rows: number;\n name: string;\n },\n ): Pty;\n};\ntype Active = {\n entry: RegistryEntry;\n handle: ProcessHandle;\n reason?: ProcessExit[\"reason\"];\n finish(code: number | null, signal: string | null): Promise<void>;\n stopping?: Promise<void>;\n timer?: ReturnType<typeof setInterval>;\n finished: boolean;\n adopted: boolean;\n fail(error: unknown): void;\n};\n\nfunction info(entry: RegistryEntry): ProcessInfo {\n const { token: _token, start: _start, cgroup: _cgroup, gated: _gated, ...visible } = entry;\n return structuredClone(visible);\n}\nfunction validateFilter(filter: ProcessFilter): void {\n if (\n !filter ||\n typeof filter !== \"object\" ||\n Array.isArray(filter) ||\n Object.keys(filter).some((key) => key !== \"labels\")\n )\n throw new Error(\"Only label filters are supported\");\n if (\n filter.labels !== undefined &&\n (filter.labels === null ||\n typeof filter.labels !== \"object\" ||\n Array.isArray(filter.labels) ||\n !Object.values(filter.labels).every((value) => typeof value === \"string\"))\n )\n throw new Error(\"Labels must be string key/value pairs\");\n}\nfunction matches(entry: RegistryEntry, filter: ProcessFilter): boolean {\n return Object.entries(filter.labels ?? {}).every(([key, value]) => entry.labels[key] === value);\n}\n\nexport function supervisor(options: SupervisorOptions): ProcessSupervisor {\n for (const option of [\"owner\", \"registry\"] as const) {\n if (!options || typeof options[option] !== \"string\" || !options[option].trim()) {\n throw new TypeError(`Invalid supervisor configuration: ${option} must be a nonempty string`);\n }\n }\n validateLimits(options.defaults ?? {});\n // Caller-owned configuration must not change an active supervisor's identity.\n options = { ...options, defaults: { ...options.defaults } };\n const pollMs = options.pollIntervalMs ?? 100;\n const graceMs = options.stopGraceMs ?? 1000;\n if (!Number.isSafeInteger(pollMs) || pollMs < 10 || !Number.isSafeInteger(graceMs) || graceMs < 0)\n throw new Error(\"Invalid polling interval or stop grace period\");\n const registry = new Registry(path.resolve(options.registry), options.owner);\n const entries = new Map<number, RegistryEntry>();\n const active = new Map<number, Active>();\n let ready: Promise<void> | undefined;\n let closing = false;\n let shutdown: Promise<void> | undefined;\n // Serialize public operations, including shutdown, so a gated spawn cannot escape stopAll.\n let queue: Promise<unknown> = Promise.resolve();\n function serial<T>(operation: () => Promise<T>): Promise<T> {\n const next = queue.then(operation);\n queue = next.catch(() => undefined);\n return next;\n }\n async function open(): Promise<void> {\n supported(process.platform);\n if (closing) throw new Error(\"Supervisor has shut down\");\n ready ??= registry.open().then((saved) => {\n for (const entry of saved) {\n delete entry.cgroup;\n entries.set(entry.pid, entry);\n }\n });\n await ready;\n }\n const save = () => registry.save([...entries.values()]);\n\n async function signal(entry: RegistryEntry, value: NodeJS.Signals): Promise<void> {\n if (!(await owns(entry))) throw new ProcessNotOwnedError(entry.pid);\n if (process.platform !== \"linux\" && value === \"SIGCONT\") {\n const proof = await guardianProof(entry.pid, entry.token);\n if (!proof || proof.token !== entry.token || proof.gated)\n throw new ProcessNotOwnedError(entry.pid);\n return;\n }\n if (process.platform === \"win32\") {\n try {\n await executeCommand(taskkillCommand(entry.pid));\n } catch (error) {\n if (await owns(entry)) throw error;\n }\n return;\n }\n // Negative PID targets only the session/group established for this child.\n // Recheck for every signal, including escalation; never match a command line.\n try {\n process.kill(-entry.pid, value);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ESRCH\") throw error;\n }\n }\n async function stopEntry(\n entry: RegistryEntry,\n reason: ProcessExit[\"reason\"] = \"stopped\",\n ): Promise<void> {\n const tracked = active.get(entry.pid);\n let running: Active;\n if (!tracked) {\n if (!(await owns(entry))) throw new ProcessNotOwnedError(entry.pid);\n entry.cgroup = await recoveredCgroup(entry.pid, entry.token);\n running = track(entry);\n } else running = tracked;\n if (running.finished) return running.finish(null, null);\n if (running.stopping) return running.stopping;\n const stopping = (async () => {\n if (!(await owns(entry))) throw new ProcessNotOwnedError(entry.pid);\n running.reason ??= reason;\n await signal(entry, \"SIGTERM\");\n const deadline = Date.now() + graceMs;\n while (await owns(entry)) {\n if (Date.now() >= deadline) {\n await signal(entry, \"SIGKILL\");\n break;\n }\n await delay(20);\n }\n const killedDeadline = Date.now() + 5000;\n while (await owns(entry)) {\n if (Date.now() >= killedDeadline)\n throw new Error(`Process ${entry.pid} did not exit after SIGKILL`);\n await delay(20);\n }\n await running.finish(null, null);\n })();\n running.stopping = stopping;\n try {\n await stopping;\n } catch (error) {\n running.stopping = undefined;\n // The leader may exit between identity verification and a signal. A\n // missing identity is harmless for a tracked handle; a live mismatch is\n // still an ownership failure and must never receive another signal.\n if (error instanceof ProcessNotOwnedError && !(await identity(entry.pid))) {\n await running.finish(null, null);\n return;\n }\n throw error;\n }\n }\n\n function track(\n entry: RegistryEntry,\n streams?: Pick<ProcessHandle, \"stdin\" | \"stdout\" | \"stderr\">,\n ): Active {\n let resolveExit: (exit: ProcessExit) => void = () => undefined;\n let rejectExit: (error: unknown) => void = () => undefined;\n const exited = new Promise<ProcessExit>((resolve, reject) => {\n resolveExit = resolve;\n rejectExit = reject;\n });\n // A rejected persistence operation is still observable through handle.exited.\n void exited.catch(() => undefined);\n const stdout = streams?.stdout ?? new PassThrough();\n const stderr = streams?.stderr ?? new PassThrough();\n const stdin = streams?.stdin ?? new PassThrough();\n let finishing: Promise<void> | undefined;\n const running: Active = {\n entry,\n finished: false,\n adopted: !streams,\n fail: (error) => rejectExit(error),\n handle: {\n pid: entry.pid,\n stdout,\n stderr,\n stdin,\n exited,\n stop: () =>\n serial(async () => {\n if (!running.finished) await stopEntry(entry);\n }),\n },\n finish(code, sig) {\n finishing ??= (async () => {\n running.finished = true;\n clearInterval(running.timer);\n if (entry.cgroup) {\n const memory = await cgroupMemory(entry.cgroup);\n entry.peakMemoryBytes = Math.max(entry.peakMemoryBytes, memory.peak);\n if (memory.oom) running.reason = \"memory-cap\";\n await cleanCgroup(entry.cgroup);\n }\n active.delete(entry.pid);\n entries.delete(entry.pid);\n try {\n await save();\n resolveExit({\n reason: running.reason ?? \"exit\",\n code,\n signal: sig,\n peakMemoryBytes: entry.peakMemoryBytes,\n });\n } catch (error) {\n rejectExit(error);\n throw error;\n }\n if (!streams) {\n (stdout as PassThrough).end();\n (stderr as PassThrough).end();\n stdin.end();\n }\n })();\n return finishing;\n },\n };\n active.set(entry.pid, running);\n return running;\n }\n\n function monitor(running: Active): void {\n if (running.finished) return;\n let sampling = false;\n const sample = async () => {\n if (sampling || running.finished) return;\n sampling = true;\n try {\n const entry = running.entry;\n const current = await identity(entry.pid, true, entry.token);\n if (running.finished) return;\n if (\n current?.start !== entry.start ||\n current.token !== entry.token ||\n current.group !== entry.pid\n ) {\n if (!current) {\n if (running.adopted) await running.finish(null, null);\n } else {\n // A live child that changes its environment or group can no longer\n // be supervised safely. Preserve the entry and surface the failure.\n throw new ProcessNotOwnedError(entry.pid);\n }\n return;\n }\n entry.peakMemoryBytes = Math.max(entry.peakMemoryBytes, current.rss);\n if (entry.cgroup) {\n const memory = await cgroupMemory(entry.cgroup);\n if (running.finished) return;\n entry.peakMemoryBytes = Math.max(entry.peakMemoryBytes, memory.peak);\n if (memory.oom) running.reason = \"memory-cap\";\n }\n if (entry.limits.memoryBytes && entry.peakMemoryBytes > entry.limits.memoryBytes)\n await stopEntry(entry, \"memory-cap\");\n else if (entry.limits.timeoutMs && Date.now() - entry.startedAt >= entry.limits.timeoutMs)\n await stopEntry(entry, \"timeout\");\n } finally {\n sampling = false;\n }\n };\n running.timer = setInterval(() => {\n void sample().catch((error: unknown) => {\n // Never signal on an observation error. Surface it to the caller and keep the registry.\n clearInterval(running.timer);\n if (!running.finished) running.fail(error);\n });\n }, pollMs);\n running.timer.unref();\n }\n\n async function launch(\n input: SpawnOptions & { columns?: number; rows?: number },\n terminal: boolean,\n ): Promise<ProcessHandle | PtyHandle> {\n await open();\n if ([...entries.keys()].some((pid) => !active.has(pid)))\n throw new Error(\"Call recover() before spawning with an existing registry\");\n if (\n typeof input.command !== \"string\" ||\n !input.command ||\n typeof input.kind !== \"string\" ||\n !input.kind\n )\n throw new Error(\"kind and command are required\");\n validateFilter({ labels: input.labels });\n if (\n terminal &&\n [input.columns ?? 80, input.rows ?? 24].some(\n (size) => !Number.isSafeInteger(size) || size <= 0,\n )\n )\n throw new Error(\"Terminal dimensions must be positive safe integers\");\n const limits = { ...options.defaults, ...input.limits };\n validateLimits(limits);\n const token = randomBytes(32).toString(\"hex\");\n const {\n file: executable,\n args,\n env,\n } = launchCommand(\n process.platform,\n process.execPath,\n input.command,\n input.args ?? [],\n limits,\n { ...process.env, ...input.env },\n token,\n );\n let pid: number;\n let streams: Pick<ProcessHandle, \"stdin\" | \"stdout\" | \"stderr\">;\n let terminalProcess: Pty | undefined;\n let exit: { code: number | null; signal: string | null } | undefined;\n let spawnError: Error | undefined;\n let running: Active | undefined;\n const onExit = (code: number | null, sig: string | null) => {\n exit = { code, signal: sig };\n if (running) void running.finish(code, sig).catch(() => undefined);\n };\n if (terminal) {\n // Optional peer loaded only here. Structural types follow node-pty's official typings.\n const peer = \"node-pty\";\n const pty = (await import(peer)) as PtyModule;\n terminalProcess = pty.spawn(executable, args, {\n cwd: input.cwd,\n env: Object.fromEntries(\n Object.entries(env).filter((pair): pair is [string, string] => pair[1] !== undefined),\n ),\n cols: input.columns ?? 80,\n rows: input.rows ?? 24,\n name: \"xterm-256color\",\n });\n pid = terminalProcess.pid;\n const output = new PassThrough();\n const errors = new PassThrough();\n const instance = terminalProcess;\n const dataSubscription = instance.onData((data) => output.write(data));\n const inputStream = new Writable({\n write(chunk: Buffer, _encoding, callback) {\n instance.write(chunk.toString());\n callback();\n },\n });\n const exitSubscription = instance.onExit((event) => {\n dataSubscription.dispose();\n exitSubscription.dispose();\n output.end();\n errors.end();\n inputStream.end();\n onExit(event.exitCode, event.signal ? String(event.signal) : null);\n });\n streams = { stdout: output, stderr: errors, stdin: inputStream };\n } else {\n const child = spawnChild(executable, args, {\n cwd: input.cwd,\n env,\n detached: process.platform !== \"win32\",\n windowsHide: true,\n stdio: \"pipe\",\n });\n child.on(\"error\", (error) => {\n spawnError = error;\n });\n // Finish on leader exit so cgroup cleanup can stop remaining members;\n // waiting for close could deadlock on a descendant holding a pipe open.\n child.on(\"exit\", onExit);\n if (!child.pid) throw new Error(\"Could not start process\");\n pid = child.pid;\n streams = { stdout: child.stdout, stderr: child.stderr, stdin: child.stdin };\n }\n let entry: RegistryEntry | undefined;\n try {\n const deadline = Date.now() + 5000;\n let current = await identity(pid, true, token);\n while (!current?.state.startsWith(\"T\") || current.token !== token) {\n if (spawnError) throw spawnError;\n if (exit || Date.now() >= deadline)\n throw new Error(\"Child failed to reach the resource setup gate\");\n await delay(10);\n current = await identity(pid, true, token);\n }\n if (current.group !== pid) throw new Error(\"Child did not create an isolated process group\");\n entry = {\n pid,\n owner: options.owner,\n type: input.kind,\n command: input.command,\n args: [...(input.args ?? [])],\n labels: { ...input.labels },\n startedAt: Date.now(),\n limits,\n enforcement: process.platform === \"win32\" ? \"rss-polling\" : \"rlimit-polling\",\n peakMemoryBytes: current.rss,\n token,\n start: current.start,\n gated: true,\n };\n entry.cgroup = await attachCgroup(pid, token, limits, options.cgroupRoot);\n if (entry.cgroup) entry.enforcement = \"cgroup-v2\";\n entries.set(pid, entry);\n await save();\n running = track(entry, streams);\n await signal(entry, \"SIGCONT\");\n delete entry.gated;\n await save();\n monitor(running);\n if (terminalProcess) {\n const instance = terminalProcess;\n return {\n ...running.handle,\n write: (data: string) => instance.write(data),\n resize: (columns: number, rows: number) => instance.resize(columns, rows),\n };\n }\n return running.handle;\n } catch (error) {\n // This PID was created by this call. Kill only after its random token is verified.\n const current = await identity(pid, true, token);\n if (current?.token === token) {\n const failed = { pid, token, start: current.start };\n if (await owns(failed)) {\n try {\n if (process.platform === \"win32\") await executeCommand(taskkillCommand(pid));\n else process.kill(-pid, \"SIGKILL\");\n } catch {\n /* Already exited. */\n }\n }\n }\n if (entry && !running) {\n entries.delete(pid);\n await save();\n }\n if (entry?.cgroup) await cleanCgroup(entry.cgroup);\n throw error;\n }\n }\n\n const api: ProcessSupervisor = {\n spawn: (input) => serial(() => launch(input, false)),\n spawnPty: (input) => serial(async () => (await launch(input, true)) as PtyHandle),\n list: (filter = {}) =>\n serial(async () => {\n validateFilter(filter);\n await open();\n return [...entries.values()].filter((entry) => matches(entry, filter)).map(info);\n }),\n stop: (filter = {}) =>\n serial(async () => {\n validateFilter(filter);\n await open();\n for (const entry of [...entries.values()].filter((entry) => matches(entry, filter)))\n await stopEntry(entry);\n }),\n stopPid: (pid) =>\n serial(async () => {\n await open();\n const entry = entries.get(pid);\n if (!entry) throw new ProcessNotOwnedError(pid);\n await stopEntry(entry);\n }),\n recover: (recovery = {}) =>\n serial(async () => {\n await open();\n const result: RecoveryResult = { adopted: [], stopped: [], cleared: [] };\n for (const entry of [...entries.values()]) {\n if (active.has(entry.pid)) continue;\n if (!(await owns(entry))) {\n entries.delete(entry.pid);\n result.cleared.push(entry.pid);\n continue;\n }\n // Never trust a persisted filesystem path for cgroup I/O after a restart.\n entry.cgroup = await recoveredCgroup(entry.pid, entry.token);\n entry.enforcement = entry.cgroup\n ? \"cgroup-v2\"\n : process.platform === \"win32\"\n ? \"rss-polling\"\n : \"rlimit-polling\";\n const keep = await recovery.keep?.(info(entry));\n if (keep) {\n const running = track(entry);\n monitor(running);\n if (entry.gated) {\n await signal(entry, \"SIGCONT\");\n delete entry.gated;\n }\n result.adopted.push(entry.pid);\n } else {\n await stopEntry(entry);\n result.stopped.push(entry.pid);\n }\n }\n await save();\n return result;\n }),\n stopAll() {\n shutdown ??= serial(async () => {\n await open();\n closing = true;\n try {\n const errors: unknown[] = [];\n for (const entry of [...entries.values()]) {\n try {\n if (await owns(entry)) await stopEntry(entry);\n else {\n const running = active.get(entry.pid);\n if (running) await running.finish(null, null);\n else entries.delete(entry.pid);\n }\n } catch (error) {\n errors.push(error);\n }\n }\n await save();\n if (errors.length)\n throw new AggregateError(errors, \"Some owned processes could not be stopped\");\n } finally {\n await registry.close();\n }\n });\n return shutdown;\n },\n };\n return api;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,SAAS,cAAc,YAAwC;CAC7D,MAAM,QAAQ;CACd,MAAM,YAAYA,kBAAK,QAAQ,OAAO,IAAI,YAAY;CAGtD,OAAO,cAAc,SAAS,UAAU,WAAW,GAAG,MAAM,EAAE,IAAI,YAAY;AAChF;AAEA,SAAgB,eAAe,QAAuB,WAAmB,QAAQ,UAAgB;CAC/F,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAC9C,IACE,CAAC;EAAC;EAAa;EAAe;EAAc;CAAoB,CAAC,CAAC,SAAS,GAAG,KAC9E,CAAC,OAAO,cAAc,KAAK,KAC3B,SAAS,GAET,MAAM,IAAI,MAAM,0BAA0B,KAAK;CAEnD,IAAI,aAAa,WAAW,OAAO,uBAAuB,QACxD,MAAM,IAAI,MAAM,+CAA+C;CACjE,IAAI,aAAa,WAAW,OAAO,eAAe,QAChD,MAAM,IAAI,MAAM,iDAAiD;AACrE;;;;;;AAOA,SAAgB,SAAS,QAAuB,WAAmB,QAAQ,UAAkB;CAC3F,MAAM,WAAW,CAAC,QAAQ;CAC1B,IAAI,OAAO,YAAY,SAAS,KAAK,aAAa,OAAO,YAAY;CACrE,IAAI,OAAO,oBACT,SAAS,KAAK,aAAa,KAAK,KAAK,OAAO,qBAAqB,IAAI,GAAG;CAG1E,IAAI,OAAO,aACT,SAAS,KACP,WAAW,aAAa,WAAW,MAAM,IAAI,GAAG,KAAK,KAAK,OAAO,cAAc,IAAI,GACrF;CACF,IAAI,aAAa,SAAS,SAAS,KAAK,mBAAiB;CACzD,SAAS,KAAK,aAAW;CACzB,OAAO,SAAS,KAAK,IAAI;AAC3B;;AAGA,eAAsB,aACpB,KACA,OACA,QACA,MAC6B;CAC7B,IAAI,QAAQ,aAAa,SAAS,OAAO;CACzC,IAAI;CACJ,IAAI;EACF,MAAM,OAAO,UAAMC,2BAAS,qBAAqB,MAAM,EAAC,CACrD,MAAM,IAAI,CAAC,CACX,MAAM,SAAS,KAAK,WAAW,KAAK,CAAC,CAAC,EACrC,MAAM,CAAC;EACX,IAAI,CAAC,QAAQ,QAAQ,QAAW,OAAO;EACvC,MAAM,SAAS,QAAQ,cAAc,OAAO,GAAG;EAC/C,IAAI,CAAC,QAAQ,OAAO;EAEpB,UAAMA,2BAASD,kBAAK,KAAK,QAAQ,oBAAoB,GAAG,MAAM;EAC9D,YAAYA,kBAAK,KAAK,QAAQ,WAAW,OAAO;EAChD,UAAME,wBAAM,SAAS;EACrB,IAAI,OAAO,aAAa;GACtB,UAAMC,4BAAUH,kBAAK,KAAK,WAAW,YAAY,GAAG,OAAO,OAAO,WAAW,CAAC;GAC9E,UAAMG,4BAAUH,kBAAK,KAAK,WAAW,kBAAkB,GAAG,GAAG;EAC/D;EACA,UAAMG,4BAAUH,kBAAK,KAAK,WAAW,cAAc,GAAG,OAAO,GAAG,CAAC;EACjE,OAAO;CACT,QAAQ;EACN,IAAI,WAAW,UAAMI,wBAAM,SAAS,CAAC,CAAC,YAAY,MAAS;EAC3D;CACF;AACF;AAEA,eAAsB,aAAa,WAA4D;CAC7F,MAAM,CAAC,MAAM,UAAU,MAAM,QAAQ,IAAI,KACvCH,2BAASD,kBAAK,KAAK,WAAW,aAAa,GAAG,MAAM,CAAC,CAAC,YAAY,GAAG,OACrEC,2BAASD,kBAAK,KAAK,WAAW,eAAe,GAAG,MAAM,CAAC,CAAC,YAAY,EAAE,CACxE,CAAC;CACD,OAAO;EAAE,MAAM,OAAO,IAAI,KAAK;EAAG,KAAK,OAAO,mBAAmB,KAAK,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI;CAAE;AAC/F;;AAGA,eAAsB,gBAAgB,KAAa,OAA4C;CAC7F,IAAI,QAAQ,aAAa,SAAS,OAAO;CACzC,IAAI;CACJ,IAAI;EACF,SAAS,UAAMC,2BAAS,SAAS,IAAI,UAAU,MAAM;CACvD,SAAS,OAAO;EACd,IAAI,CAAC,UAAU,OAAO,CAAC,CAAC,SAAS,OAAQ,MAAgC,IAAI,CAAC,GAC5E,OAAO;EACT,MAAM;CACR;CACA,MAAM,aAAa,OAChB,MAAM,IAAI,CAAC,CACX,MAAM,SAAS,KAAK,WAAW,KAAK,CAAC,CAAC,EACrC,MAAM,CAAC;CACX,IAAI,CAAC,cAAcD,kBAAK,SAAS,UAAU,MAAM,WAAW,SAAS,OAAO;CAC5E,OAAO,cAAc,UAAU;AACjC;AAEA,eAAsB,YAAY,WAAkC;CAGlE,UAAMG,4BAAUH,kBAAK,KAAK,WAAW,aAAa,GAAG,GAAG,CAAC,CAAC,YAAY,MAAS;CAC/E,UAAMI,wBAAM,SAAS,CAAC,CAAC,YAAY,MAAS;AAC9C;;;;AC3GA,SAAgB,gBAAgB,UAAkB,KAAa,KAAqB;CAClF,OAAO,aAAa,UAChB,gCAAgC,QAChC,wBAAwB,IAAI,GAAG,IAAI;AACzC;;;;;AAMA,MAAa,iBAAiB,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDxC,eAAsB,cACpB,KACA,QACA,gBAAoC,QACC;CACrC,IAAI,CAAC,eAAe,OAAO;CAC3B,MAAM,QAAQ;CACd,MAAM,YAAQC,yBAAY,EAAE,CAAC,CAAC,SAAS,KAAK;CAC5C,MAAM,QAAQ,cAAkBC,wBAAW,UAAU,KAAK,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;CACtF,MAAM,UAAU,gBAAgB,QAAQ,UAAU,KAAK,QAAQ,SAAS,KAAK,CAAC;CAC9E,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,aAASC,kBAAQ,OAAO;EAC9B,IAAI,OAAO;EACX,MAAM,QAAQ,UAAiB;GAC7B,OAAO,QAAQ;GACf,OAAO,KAAK;EACd;EACA,OAAO,WAAW,WAAY,qBAAK,IAAI,MAAM,yCAAyC,CAAC,CAAC;EACxF,OAAO,GAAG,iBACR,OAAO,MACL,KAAK,UAAU;GAAE;GAAO,QAAQ,SAAS,KAAK,QAAQ,SAAS,IAAI;EAAU,CAAC,IAAI,IACpF,CACF;EACA,OAAO,GAAG,SAAS,UAAkB;GACnC,QAAQ,MAAM,SAAS;GACvB,IAAI,KAAK,SAAS,MAAM,qBAAK,IAAI,MAAM,oCAAoC,CAAC;EAC9E,CAAC;EACD,OAAO,GAAG,UAAU,UAAiC;GACnD,IAAI,CAAC,UAAU,cAAc,CAAC,CAAC,SAAS,MAAM,QAAQ,EAAE,GAAG,QAAQ,MAAS;QACvE,OAAO,KAAK;EACnB,CAAC;EACD,OAAO,GAAG,aAAa;GACrB,IAAI;IACF,QAAQ,mBAAmB,MAAM,KAAK,OAAO,KAAK,CAAC;GACrD,SAAS,OAAO;IACd,OAAO,KAAK;GACd;EACF,CAAC;CACH,CAAC;AACH;;AAGA,SAAgB,mBACd,QACA,KACA,OACA,OAC4B;CAC5B,MAAM,QAAiB,KAAK,MAAM,MAAM;CACxC,IACE,CAAC,SACD,OAAO,UAAU,YACjB,EAAE,SAAS,UACX,OAAO,MAAM,QAAQ,YACrB,EAAE,WAAW,UACb,OAAO,MAAM,UAAU,aACvB,EAAE,YAAY,UACd,OAAO,MAAM,WAAW,UAExB,MAAM,IAAI,MAAM,oCAAoC;CACtD,MAAM,eAAWD,wBAAW,UAAU,KAAK,CAAC,CACzC,OAAO,QAAQ,MAAM,MAAM,MAAM,MAAM,KAAK,CAAC,CAC7C,OAAO,KAAK;CACf,IAAI,MAAM,QAAQ,OAAO,MAAM,WAAW,UAAU,OAAO;CAC3D,OAAO;EAAE;EAAK;EAAO,OAAO,MAAM;CAAM;AAC1C;;AAGA,SAAgB,cACd,UACA,MACA,SACA,MACA,QACA,aACA,OACmD;CACnD,MAAM,WAAW,aAAa;CAC9B,MAAM,MAAyB,OAAO,YACpC,OAAO,QAAQ,WAAW,CAAC,CAAC,QACzB,CAAC,SACA,CAAC,CAAC,yBAAyB,wBAAwB,CAAC,CAAC,SACnD,aAAa,UAAU,IAAI,YAAY,IAAI,GAC7C,CACJ,CACF;CACA,IAAI,wBAAwB;CAC5B,IAAI,UAAU,IAAI,yBAAyB,KAAK,UAAU,CAAC,SAAS,GAAG,IAAI,CAAC;CAC5E,MAAM,aAAa,WAAW,OAAO;CACrC,MAAM,aAAa,WAAW,CAAC,MAAM,cAAc,IAAI;CACvD,OAAO;EACL,MAAM,aAAa,UAAU,aAAa;EAC1C,MACE,aAAa,UACT,aACA;GAAC;GAAM,SAAS,QAAQ,QAAQ;GAAG;GAAmB;GAAY,GAAG;EAAU;EACrF;CACF;AACF;;;;ACtJA,IAAa,kCAAb,cAAqD,MAAM;CAC7B;CAA5B,YAAY,AAAgB,UAAkB;EAC5C,MAAM,yCAAyC,UAAU;EAD/B;EAE1B,KAAK,OAAO;CACd;AACF;AACA,SAAgB,UAAU,UAAwB;CAChD,IAAI,CAAC;EAAC;EAAS;EAAU;CAAO,CAAC,CAAC,SAAS,QAAQ,GACjD,MAAM,IAAI,gCAAgC,QAAQ;AACtD;AACA,SAAS,SAAS,KAAmB;CACnC,IAAI,CAAC,OAAO,cAAc,GAAG,KAAK,OAAO,GAAG,MAAM,IAAI,MAAM,qBAAqB;AACnF;;;;;;AAOA,SAAgB,iBAAiB,QAAwB;CACvD,MAAM,QACJ,gIAAgI,KAC9H,OAAO,KAAK,CACd;CACF,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,kCAAkC;CAC9D,MAAM,GAAG,SAAS,OAAO,KAAK,MAAM,QAAQ,QAAQ,QAAQ;CAC5D,MAAM,aAAa,kDAChB,MAAM,GAAG,CAAC,CACV,QAAQ,SAAS,EAAE;CACtB,MAAM,OAAO,IAAI,KACf,KAAK,IAAI,OAAO,IAAI,GAAG,YAAY,OAAO,GAAG,GAAG,OAAO,IAAI,GAAG,OAAO,MAAM,GAAG,OAAO,MAAM,CAAC,CAC9F;CACA,IACE,KAAK,eAAe,MAAM,OAAO,IAAI,KACrC,KAAK,YAAY,MAAM,cACvB,KAAK,WAAW,MAAM,OAAO,GAAG,KAChC,KAAK,YAAY,MAAM,OAAO,IAAI,KAClC,KAAK,cAAc,MAAM,OAAO,MAAM,KACtC,KAAK,cAAc,MAAM,OAAO,MAAM,KACtC,8BAA8B,MAAM,GAAG,CAAC,CAAC,KAAK,UAAU,OAAO,SAE/D,MAAM,IAAI,MAAM,kCAAkC;CACpD,OAAO,OAAO,KAAK,QAAQ,IAAI,GAAI;AACrC;AACA,SAAgB,cAAc,KAAa,OAAO,OAAgB;CAChE,SAAS,GAAG;CACZ,OAAO;EACL,MAAM;EACN,MAAM;GAAC,GAAI,OAAO,CAAC,KAAK,IAAI,CAAC,MAAM,OAAO,GAAG,CAAC;GAAI;GAAM;EAAsC;EAC9F,KAAK;GAAE,QAAQ;GAAK,IAAI;EAAM;CAChC;AACF;AACA,SAAgB,gBAAgB,QAAuB;CACrD,OAAO,OAAO,KAAK,IACf,OACG,KAAK,CAAC,CACN,MAAM,IAAI,CAAC,CACX,KAAK,SAAS;EACb,MAAM,QAAQ,wDAAwD,KAAK,IAAI;EAC/E,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,mCAAmC;EAC/D,OAAO;GACL,KAAK,OAAO,MAAM,EAAE;GACpB,QAAQ,OAAO,MAAM,EAAE;GACvB,OAAO,OAAO,MAAM,EAAE;GACtB,OAAO,MAAM,MAAM;GACnB,KAAK,OAAO,MAAM,EAAE,IAAI;GACxB,OAAO,iBAAiB,MAAM,MAAM,EAAE;EACxC;CACF,CAAC,IACH,CAAC;AACP;;;;;AAMA,SAAgB,eAAe,KAAa,OAAO,OAAgB;CACjE,SAAS,GAAG;CAYZ,OAAO;EACL,MAAM;EACN,MAAM;GAAC;GAAW;GAAc;GAAmB;GAZtC;IACb;IACA,aAAa,gCAH+B,OAAO,KAAK,uBAAuB,IAAI,KAGhE;IACnB,6BAA6B,IAAI;IACjC;IACA;IACA;IACA;IACA;GACF,CAAC,CAAC,KAAK,IAG+D;EAAC;CACvE;AACF;AACA,SAAgB,iBAAiB,QAAuB;CACtD,MAAM,QAAiB,KAAK,MAAM,OAAO,QAAQ,WAAW,EAAE,CAAC;CAC/D,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,MAAM,IAAI,MAAM,qCAAqC;CAChF,OAAO,MAAM,KAAK,QAAiB;EACjC,IACE,CAAC,OACD,OAAO,QAAQ,YACf,EAAE,SAAS,QACX,OAAO,IAAI,QAAQ,YACnB,CAAC,OAAO,cAAc,IAAI,GAAG,KAC7B,IAAI,MAAM,KACV,EAAE,YAAY,QACd,OAAO,IAAI,WAAW,YACtB,CAAC,OAAO,cAAc,IAAI,MAAM,KAChC,IAAI,SAAS,KACb,EAAE,WAAW,QACb,OAAO,IAAI,UAAU,YACrB,CAAC,WAAW,KAAK,IAAI,KAAK,KAC1B,EAAE,SAAS,QACX,OAAO,IAAI,QAAQ,YACnB,CAAC,OAAO,SAAS,IAAI,GAAG,KACxB,IAAI,MAAM,GAEV,MAAM,IAAI,MAAM,qCAAqC;EACvD,OAAO;GACL,KAAK,IAAI;GACT,QAAQ,IAAI;GACZ,OAAO,IAAI;GACX,KAAK,IAAI;GACT,OAAO,IAAI;GACX,OAAO;EACT;CACF,CAAC;AACH;AACA,SAAgB,gBAAgB,KAAsB;CACpD,SAAS,GAAG;CAEZ,OAAO;EAAE,MAAM;EAAgB,MAAM;GAAC;GAAQ,OAAO,GAAG;GAAG;GAAM;EAAI;CAAE;AACzE;AACA,SAAgB,WAAW,MAAa,KAAqB;CAC3D,MAAM,OAAO,KAAK,MAAM,QAAQ,IAAI,QAAQ,GAAG;CAC/C,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,2BAAW,IAAI,IAAiB,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;CACnD,IAAI,UAAU;CACd,OAAO,SAAS;EACd,UAAU;EACV,KAAK,MAAM,OAAO,MAAM;GACtB,MAAM,SAAS,SAAS,IAAI,IAAI,MAAM;GAEtC,IAAI,CAAC,SAAS,IAAI,IAAI,GAAG,KAAK,UAAU,OAAO,IAAI,KAAK,KAAK,OAAO,OAAO,KAAK,GAAG;IACjF,SAAS,IAAI,IAAI,KAAK,GAAG;IACzB,UAAU;GACZ;EACF;CACF;CACA,OAAO,CAAC,GAAG,SAAS,OAAO,CAAC,CAAC,CAAC,QAAQ,OAAO,QAAQ,QAAQ,IAAI,KAAK,CAAC;AACzE;;AAGA,eAAsB,iBACpB,UACA,KACA,OACA,SACA,WAC+B;CAC/B,SAAS,GAAG;CACZ,MAAM,UAAU,aAAa,WAAW,gBAAgB;CACxD,MAAM,QAAQ,aAAa,WAAW,kBAAkB;CACxD,MAAM,OAAO,MAAM,MAAM,QAAQ,QAAQ,KAAK,KAAK,CAAC,CAAC;CACrD,MAAM,SAAS,KAAK,MAAM,QAAQ,IAAI,QAAQ,GAAG;CACjD,IAAI,CAAC,UAAU,QAAQ,KAAK,OAAO,KAAK,GAAG,OAAO;CAClD,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,QAAQ,MAAM,UAAU,GAAG;CACjC,MAAM,QAAQ,MAAM,MAAM,QAAQ,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,QAAQ,IAAI,QAAQ,GAAG;CAC9E,IACE,CAAC,SACD,MAAM,UAAU,OAAO,SACvB,MAAM,UAAU,OAAO,SACvB,QAAQ,KAAK,MAAM,KAAK,GAExB,OAAO;CACT,OAAO;EACL,OAAO,MAAM;EACb,OAAO,MAAM;EACb,OAAO,OAAO,QAAQ,OAAO,MAAM,QAAQ,MAAM,MAAM;EACvD,KAAK,WAAW,MAAM,GAAG;EACzB,OAAO,OAAO,QAAQ,MAAM,MAAM,QAAQ;CAC5C;AACF;;;;AChMA,MAAM,cAAUE,qBAAUC,2BAAQ;AAClC,eAAsB,eAAe,SAAmC;CACtE,MAAM,EAAE,WAAW,MAAM,QAAQ,QAAQ,MAAM,QAAQ,MAAM;EAC3D,KAAK;GAAE,GAAG,QAAQ;GAAK,GAAG,QAAQ;EAAI;EACtC,SAAS;EACT,WAAW;EACX,aAAa;CACf,CAAC;CACD,OAAO;AACT;AACA,MAAa,YAAY;AAGzB,SAAS,WAAW,MAAwB;CAC1C,OAAO,KACJ,MAAM,KAAK,YAAY,GAAG,IAAI,CAAC,CAAC,CAChC,KAAK,CAAC,CACN,MAAM,KAAK;AAChB;;;;;;AAOA,SAAS,QAAQ,QAA2B;CAC1C,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,SAAS,OAAO,MAAM,EAAE,KAAK,OAAO,QAAQ;AAChE;;AAGA,eAAsB,SACpB,KACA,QAAQ,OACR,eAC+B;CAC/B,IAAI,CAAC,OAAO,cAAc,GAAG,KAAK,OAAO,GAAG,OAAO;CACnD,IAAI;EACF,IAAI,QAAQ,aAAa,SAAS;GAEhC,MAAM,SAAS,WAAW,UADPC,2BAAS,SAAS,IAAI,QAAQ,MAAM,CACzB;GAC9B,MAAM,QAAQ,OAAO;GACrB,MAAM,QAAQ,OAAO;GACrB,IAAI,CAAC,SAAS,CAAC,SAAS,QAAQ,MAAM,GAAG,OAAO;GAChD,MAAM,QAAQ,UAAMA,2BAAS,mCAAmC,MAAM,EAAC,CAAE,KAAK;GAC9E,MAAM,SAAS,UAAMA,2BAAS,SAAS,IAAI,UAAU,MAAM;GAC3D,IAAI,cAAc;GAClB,IAAI,OACF,IAAI;IACF,cAAc,UAAMA,2BAAS,SAAS,IAAI,WAAW,MAAM;GAC7D,SAAS,OAAO;IAId,IAAK,MAA6B,SAAS,UAAU,MAAM;IAC3D,MAAM,MAAM,WAAW,UAAMA,2BAAS,SAAS,IAAI,QAAQ,MAAM,CAAC;IAClE,IAAI,IAAI,QAAQ,SAAS,QAAQ,GAAG,GAAG,OAAO;IAC9C,MAAM;GACR;GAIF,MAAM,QAAQ,WAAW,UAAMA,2BAAS,SAAS,IAAI,QAAQ,MAAM,CAAC;GACpE,IAAI,MAAM,QAAQ,SAAS,MAAM,OAAO,OAAO,MAAM,QAAQ,KAAK,GAAG,OAAO;GAC5E,OAAO;IACL,OAAO,GAAG,KAAK,GAAG;IAClB,OAAO,OAAO,OAAO,EAAE;IACvB,OAAO,MAAM,MAAM;IACnB,KAAK,OAAO,mBAAmB,KAAK,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI;IACzD,OAAO,YACJ,MAAM,IAAI,CAAC,CACX,MAAM,UAAU,MAAM,WAAW,GAAG,UAAU,EAAE,CAAC,CAAC,EACjD,MAAM,EAAoB;GAChC;EACF;EACA,IAAI,QAAQ,aAAa,YAAY,QAAQ,aAAa,SACxD,OAAO,MAAM,iBAAiB,QAAQ,UAAU,KAAK,OAAO,iBAAiB,aAC3E,cAAc,UAAU,QAAW,aAAa,CAClD;EACF;CACF,SAAS,OAAO;EACd,MAAM,OAAQ,MAA6B;EAC3C,IACG,QAAQ,aAAa,WAAW,CAAC,UAAU,OAAO,CAAC,CAAC,SAAS,OAAO,IAAI,CAAC,KACzE,QAAQ,aAAa,YACpB,SAAS,KACT,CAAE,MAA8B,QAAQ,KAAK,GAE/C,OAAO;EACT,MAAM;CACR;AACF;AAEA,IAAa,uBAAb,cAA0C,MAAM;CAClB;CAA5B,YAAY,AAAgB,KAAa;EACvC,MAAM,WAAW,IAAI,8CAA8C;EADzC;EAE1B,KAAK,OAAO;CACd;AACF;AAEA,eAAsB,KAAK,OAAwE;CACjG,MAAM,UAAU,MAAM,SAAS,MAAM,KAAK,MAAM,MAAM,KAAK;CAC3D,OACE,SAAS,UAAU,MAAM,SAAS,QAAQ,UAAU,MAAM,SAAS,QAAQ,UAAU,MAAM;AAE/F;;;;ACjGA,SAAS,OAAO,OAAkD;CAChE,OAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AACA,SAAS,QAAQ,OAAiD;CAChE,OAAO,OAAO,KAAK,KAAK,OAAO,OAAO,KAAK,CAAC,CAAC,OAAO,SAAS,OAAO,SAAS,QAAQ;AACvF;AACA,SAAgB,cAAc,QAAgB,OAAgC;CAC5E,MAAM,QAAiB,KAAK,MAAM,MAAM;CACxC,IACE,CAAC,OAAO,KAAK,KACb,MAAM,YAAY,KAClB,MAAM,UAAU,SAChB,CAAC,MAAM,QAAQ,MAAM,OAAO,GAE5B,MAAM,IAAI,MAAM,6CAA6C;CAC/D,MAAM,UAA2B,CAAC;CAClC,KAAK,MAAM,SAAS,MAAM,SAAS;EACjC,IACE,CAAC,OAAO,KAAK,KACb,MAAM,UAAU,SAChB,OAAO,MAAM,QAAQ,YACrB,CAAC,OAAO,cAAc,MAAM,GAAG,KAC/B,OAAO,MAAM,GAAG,KAAK,KACrB,OAAO,MAAM,UAAU,YACvB,CAAC,iBAAiB,KAAK,MAAM,KAAK,KAClC,OAAO,MAAM,UAAU,YACvB,OAAO,MAAM,SAAS,YACtB,OAAO,MAAM,YAAY,YACzB,CAAC,MAAM,QAAQ,MAAM,IAAI,KACzB,CAAC,MAAM,KAAK,OAAO,QAAQ,OAAO,QAAQ,QAAQ,KAClD,CAAC,QAAQ,MAAM,MAAM,KACrB,OAAO,MAAM,cAAc,YAC3B,CAAC,OAAO,SAAS,MAAM,SAAS,KAChC,MAAM,YAAY,KAClB,CAAC,OAAO,MAAM,MAAM,KACpB,CAAC,OAAO,QAAQ,MAAM,MAAM,CAAC,CAAC,OAC3B,CAAC,KAAK,WACL;GAAC;GAAa;GAAe;GAAc;EAAoB,CAAC,CAAC,SAAS,GAAG,KAC7E,OAAO,UAAU,YACjB,OAAO,cAAc,KAAK,KAC1B,QAAQ,CACZ,KACA,CAAC;GAAC;GAAa;GAAkB;EAAa,CAAC,CAAC,SAAS,OAAO,MAAM,WAAW,CAAC,KAClF,OAAO,MAAM,oBAAoB,YACjC,CAAC,OAAO,SAAS,MAAM,eAAe,KACtC,MAAM,kBAAkB,KACvB,MAAM,WAAW,UAAa,OAAO,MAAM,WAAW,YACtD,MAAM,UAAU,UAAa,OAAO,MAAM,UAAU,WAErD,MAAM,IAAI,MAAM,gCAAgC;EAClD,QAAQ,KAAK,KAAsB;CACrC;CACA,IAAI,IAAI,IAAI,QAAQ,KAAK,UAAU,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,QAAQ,QAC9D,MAAM,IAAI,MAAM,wBAAwB;CAC1C,OAAO;AACT;;AAGA,IAAa,WAAb,MAAsB;CAIF;CACC;CAJnB,AAAQ,QAAuB,QAAQ,QAAQ;CAC/C,AAAQ,SAAS;CACjB,YACE,AAAgB,MAChB,AAAiB,OACjB;EAFgB;EACC;CAChB;CACH,MAAM,OAAiC;EACrC,UAAMC,wBAAMC,kBAAK,QAAQ,KAAK,IAAI,GAAG;GAAE,WAAW;GAAM,MAAM;EAAM,CAAC;EACrE,MAAM,OAAO,MAAM,SAAS,QAAQ,GAAG;EACvC,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,wCAAwC;EACnE,MAAM,OAAO,GAAG,KAAK,KAAK;EAC1B,IAAI;GACF,UAAMD,wBAAM,MAAM,EAAE,MAAM,IAAM,CAAC;EACnC,SAAS,OAAO;GACd,IAAK,MAAgC,SAAS,UAAU,MAAM;GAC9D,MAAM,UAAU,GAAG,KAAK;GACxB,UAAMA,wBAAM,SAAS,EAAE,MAAM,IAAM,CAAC;GACpC,IAAI;IACF,MAAM,QAAiB,KAAK,MAAM,UAAME,2BAASD,kBAAK,KAAK,MAAM,aAAa,GAAG,MAAM,CAAC;IACxF,IAAI,CAAC,OAAO,KAAK,KAAK,OAAO,MAAM,QAAQ,YAAY,OAAO,MAAM,UAAU,UAC5E,MAAM,IAAI,MAAM,oDAAoD;IAEtE,KAAI,MADiB,SAAS,MAAM,GAAG,EAC7B,EAAE,UAAU,MAAM,OAAO,MAAM,IAAI,MAAM,oCAAoC;IACvF,MAAM,QAAQ,GAAG,KAAK,OAAGE,yBAAY,CAAC,CAAC,CAAC,SAAS,KAAK;IACtD,UAAMC,yBAAO,MAAM,KAAK;IACxB,UAAMC,qBAAG,OAAO,EAAE,WAAW,KAAK,CAAC;IACnC,UAAML,wBAAM,MAAM,EAAE,MAAM,IAAM,CAAC;GACnC,UAAU;IACR,UAAMK,qBAAG,SAAS,EAAE,WAAW,KAAK,CAAC;GACvC;EACF;EACA,KAAK,SAAS;EACd,IAAI;GACF,UAAMC,4BACJL,kBAAK,KAAK,MAAM,aAAa,GAC7B,KAAK,UAAU;IAAE,KAAK,QAAQ;IAAK,OAAO,KAAK;GAAM,CAAC,GACtD,EAAE,MAAM,IAAM,CAChB;GACA,IAAI;IACF,OAAO,cAAc,UAAMC,2BAAS,KAAK,MAAM,MAAM,GAAG,KAAK,KAAK;GACpE,SAAS,OAAO;IACd,IAAK,MAAgC,SAAS,UAAU,OAAO,CAAC;IAChE,MAAM;GACR;EACF,SAAS,OAAO;GACd,MAAM,KAAK,MAAM;GACjB,MAAM;EACR;CACF;CACA,KAAK,SAAyC;EAC5C,MAAM,SAAS,KAAK,UAAU;GAAE,SAAS;GAAG,OAAO,KAAK;GAAO;EAAQ,GAAG,MAAM,CAAC,IAAI;EACrF,MAAM,OAAO,KAAK,MAAM,KAAK,YAAY;GACvC,MAAM,YAAY,GAAG,KAAK,KAAK,OAAGC,yBAAY,CAAC,CAAC,CAAC,SAAS,KAAK,EAAE;GACjE,IAAI;IACF,UAAMG,4BAAU,WAAW,QAAQ;KAAE,MAAM;KAAO,MAAM;IAAK,CAAC;IAC9D,UAAMF,yBAAO,WAAW,KAAK,IAAI;GACnC,UAAU;IACR,UAAMC,qBAAG,WAAW,EAAE,OAAO,KAAK,CAAC;GACrC;EACF,CAAC;EACD,KAAK,QAAQ,KAAK,YAAY,MAAS;EACvC,OAAO;CACT;CACA,MAAM,QAAuB;EAC3B,MAAM,KAAK;EACX,IAAI,KAAK,QAAQ;GACf,UAAMA,qBAAG,GAAG,KAAK,KAAK,QAAQ,EAAE,WAAW,KAAK,CAAC;GACjD,KAAK,SAAS;EAChB;CACF;AACF;;;;ACnFA,SAAS,KAAK,OAAmC;CAC/C,MAAM,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,SAAS,OAAO,QAAQ,GAAG,YAAY;CACrF,OAAO,gBAAgB,OAAO;AAChC;AACA,SAAS,eAAe,QAA6B;CACnD,IACE,CAAC,UACD,OAAO,WAAW,YAClB,MAAM,QAAQ,MAAM,KACpB,OAAO,KAAK,MAAM,CAAC,CAAC,MAAM,QAAQ,QAAQ,QAAQ,GAElD,MAAM,IAAI,MAAM,kCAAkC;CACpD,IACE,OAAO,WAAW,WACjB,OAAO,WAAW,QACjB,OAAO,OAAO,WAAW,YACzB,MAAM,QAAQ,OAAO,MAAM,KAC3B,CAAC,OAAO,OAAO,OAAO,MAAM,CAAC,CAAC,OAAO,UAAU,OAAO,UAAU,QAAQ,IAE1E,MAAM,IAAI,MAAM,uCAAuC;AAC3D;AACA,SAAS,QAAQ,OAAsB,QAAgC;CACrE,OAAO,OAAO,QAAQ,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,WAAW,MAAM,OAAO,SAAS,KAAK;AAChG;AAEA,SAAgB,WAAW,SAA+C;CACxE,KAAK,MAAM,UAAU,CAAC,SAAS,UAAU,GACvC,IAAI,CAAC,WAAW,OAAO,QAAQ,YAAY,YAAY,CAAC,QAAQ,OAAO,CAAC,KAAK,GAC3E,MAAM,IAAI,UAAU,qCAAqC,OAAO,2BAA2B;CAG/F,eAAe,QAAQ,YAAY,CAAC,CAAC;CAErC,UAAU;EAAE,GAAG;EAAS,UAAU,EAAE,GAAG,QAAQ,SAAS;CAAE;CAC1D,MAAM,SAAS,QAAQ,kBAAkB;CACzC,MAAM,UAAU,QAAQ,eAAe;CACvC,IAAI,CAAC,OAAO,cAAc,MAAM,KAAK,SAAS,MAAM,CAAC,OAAO,cAAc,OAAO,KAAK,UAAU,GAC9F,MAAM,IAAI,MAAM,+CAA+C;CACjE,MAAM,WAAW,IAAI,SAASE,kBAAK,QAAQ,QAAQ,QAAQ,GAAG,QAAQ,KAAK;CAC3E,MAAM,0BAAU,IAAI,IAA2B;CAC/C,MAAM,yBAAS,IAAI,IAAoB;CACvC,IAAI;CACJ,IAAI,UAAU;CACd,IAAI;CAEJ,IAAI,QAA0B,QAAQ,QAAQ;CAC9C,SAAS,OAAU,WAAyC;EAC1D,MAAM,OAAO,MAAM,KAAK,SAAS;EACjC,QAAQ,KAAK,YAAY,MAAS;EAClC,OAAO;CACT;CACA,eAAe,OAAsB;EACnC,UAAU,QAAQ,QAAQ;EAC1B,IAAI,SAAS,MAAM,IAAI,MAAM,0BAA0B;EACvD,UAAU,SAAS,KAAK,CAAC,CAAC,MAAM,UAAU;GACxC,KAAK,MAAM,SAAS,OAAO;IACzB,OAAO,MAAM;IACb,QAAQ,IAAI,MAAM,KAAK,KAAK;GAC9B;EACF,CAAC;EACD,MAAM;CACR;CACA,MAAM,aAAa,SAAS,KAAK,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC;CAEtD,eAAe,OAAO,OAAsB,OAAsC;EAChF,IAAI,CAAE,MAAM,KAAK,KAAK,GAAI,MAAM,IAAI,qBAAqB,MAAM,GAAG;EAClE,IAAI,QAAQ,aAAa,WAAW,UAAU,WAAW;GACvD,MAAM,QAAQ,MAAM,cAAc,MAAM,KAAK,MAAM,KAAK;GACxD,IAAI,CAAC,SAAS,MAAM,UAAU,MAAM,SAAS,MAAM,OACjD,MAAM,IAAI,qBAAqB,MAAM,GAAG;GAC1C;EACF;EACA,IAAI,QAAQ,aAAa,SAAS;GAChC,IAAI;IACF,MAAM,eAAe,gBAAgB,MAAM,GAAG,CAAC;GACjD,SAAS,OAAO;IACd,IAAI,MAAM,KAAK,KAAK,GAAG,MAAM;GAC/B;GACA;EACF;EAGA,IAAI;GACF,QAAQ,KAAK,CAAC,MAAM,KAAK,KAAK;EAChC,SAAS,OAAO;GACd,IAAK,MAAgC,SAAS,SAAS,MAAM;EAC/D;CACF;CACA,eAAe,UACb,OACA,SAAgC,WACjB;EACf,MAAM,UAAU,OAAO,IAAI,MAAM,GAAG;EACpC,IAAI;EACJ,IAAI,CAAC,SAAS;GACZ,IAAI,CAAE,MAAM,KAAK,KAAK,GAAI,MAAM,IAAI,qBAAqB,MAAM,GAAG;GAClE,MAAM,SAAS,MAAM,gBAAgB,MAAM,KAAK,MAAM,KAAK;GAC3D,UAAU,MAAM,KAAK;EACvB,OAAO,UAAU;EACjB,IAAI,QAAQ,UAAU,OAAO,QAAQ,OAAO,MAAM,IAAI;EACtD,IAAI,QAAQ,UAAU,OAAO,QAAQ;EACrC,MAAM,YAAY,YAAY;GAC5B,IAAI,CAAE,MAAM,KAAK,KAAK,GAAI,MAAM,IAAI,qBAAqB,MAAM,GAAG;GAClE,QAAQ,WAAW;GACnB,MAAM,OAAO,OAAO,SAAS;GAC7B,MAAM,WAAW,KAAK,IAAI,IAAI;GAC9B,OAAO,MAAM,KAAK,KAAK,GAAG;IACxB,IAAI,KAAK,IAAI,KAAK,UAAU;KAC1B,MAAM,OAAO,OAAO,SAAS;KAC7B;IACF;IACA,UAAMC,iCAAM,EAAE;GAChB;GACA,MAAM,iBAAiB,KAAK,IAAI,IAAI;GACpC,OAAO,MAAM,KAAK,KAAK,GAAG;IACxB,IAAI,KAAK,IAAI,KAAK,gBAChB,MAAM,IAAI,MAAM,WAAW,MAAM,IAAI,4BAA4B;IACnE,UAAMA,iCAAM,EAAE;GAChB;GACA,MAAM,QAAQ,OAAO,MAAM,IAAI;EACjC,EAAC,CAAE;EACH,QAAQ,WAAW;EACnB,IAAI;GACF,MAAM;EACR,SAAS,OAAO;GACd,QAAQ,WAAW;GAInB,IAAI,iBAAiB,wBAAwB,CAAE,MAAM,SAAS,MAAM,GAAG,GAAI;IACzE,MAAM,QAAQ,OAAO,MAAM,IAAI;IAC/B;GACF;GACA,MAAM;EACR;CACF;CAEA,SAAS,MACP,OACA,SACQ;EACR,IAAI,oBAAiD;EACrD,IAAI,mBAA6C;EACjD,MAAM,SAAS,IAAI,SAAsB,SAAS,WAAW;GAC3D,cAAc;GACd,aAAa;EACf,CAAC;EAED,AAAK,OAAO,YAAY,MAAS;EACjC,MAAM,SAAS,SAAS,UAAU,IAAIC,wBAAY;EAClD,MAAM,SAAS,SAAS,UAAU,IAAIA,wBAAY;EAClD,MAAM,QAAQ,SAAS,SAAS,IAAIA,wBAAY;EAChD,IAAI;EACJ,MAAM,UAAkB;GACtB;GACA,UAAU;GACV,SAAS,CAAC;GACV,OAAO,UAAU,WAAW,KAAK;GACjC,QAAQ;IACN,KAAK,MAAM;IACX;IACA;IACA;IACA;IACA,YACE,OAAO,YAAY;KACjB,IAAI,CAAC,QAAQ,UAAU,MAAM,UAAU,KAAK;IAC9C,CAAC;GACL;GACA,OAAO,MAAM,KAAK;IAChB,eAAe,YAAY;KACzB,QAAQ,WAAW;KACnB,cAAc,QAAQ,KAAK;KAC3B,IAAI,MAAM,QAAQ;MAChB,MAAM,SAAS,MAAM,aAAa,MAAM,MAAM;MAC9C,MAAM,kBAAkB,KAAK,IAAI,MAAM,iBAAiB,OAAO,IAAI;MACnE,IAAI,OAAO,KAAK,QAAQ,SAAS;MACjC,MAAM,YAAY,MAAM,MAAM;KAChC;KACA,OAAO,OAAO,MAAM,GAAG;KACvB,QAAQ,OAAO,MAAM,GAAG;KACxB,IAAI;MACF,MAAM,KAAK;MACX,YAAY;OACV,QAAQ,QAAQ,UAAU;OAC1B;OACA,QAAQ;OACR,iBAAiB,MAAM;MACzB,CAAC;KACH,SAAS,OAAO;MACd,WAAW,KAAK;MAChB,MAAM;KACR;KACA,IAAI,CAAC,SAAS;MACZ,AAAC,OAAuB,IAAI;MAC5B,AAAC,OAAuB,IAAI;MAC5B,MAAM,IAAI;KACZ;IACF,EAAC,CAAE;IACH,OAAO;GACT;EACF;EACA,OAAO,IAAI,MAAM,KAAK,OAAO;EAC7B,OAAO;CACT;CAEA,SAAS,QAAQ,SAAuB;EACtC,IAAI,QAAQ,UAAU;EACtB,IAAI,WAAW;EACf,MAAM,SAAS,YAAY;GACzB,IAAI,YAAY,QAAQ,UAAU;GAClC,WAAW;GACX,IAAI;IACF,MAAM,QAAQ,QAAQ;IACtB,MAAM,UAAU,MAAM,SAAS,MAAM,KAAK,MAAM,MAAM,KAAK;IAC3D,IAAI,QAAQ,UAAU;IACtB,IACE,SAAS,UAAU,MAAM,SACzB,QAAQ,UAAU,MAAM,SACxB,QAAQ,UAAU,MAAM,KACxB;KACA,IAAI,CAAC,SACH;UAAI,QAAQ,SAAS,MAAM,QAAQ,OAAO,MAAM,IAAI;KAAC,OAIrD,MAAM,IAAI,qBAAqB,MAAM,GAAG;KAE1C;IACF;IACA,MAAM,kBAAkB,KAAK,IAAI,MAAM,iBAAiB,QAAQ,GAAG;IACnE,IAAI,MAAM,QAAQ;KAChB,MAAM,SAAS,MAAM,aAAa,MAAM,MAAM;KAC9C,IAAI,QAAQ,UAAU;KACtB,MAAM,kBAAkB,KAAK,IAAI,MAAM,iBAAiB,OAAO,IAAI;KACnE,IAAI,OAAO,KAAK,QAAQ,SAAS;IACnC;IACA,IAAI,MAAM,OAAO,eAAe,MAAM,kBAAkB,MAAM,OAAO,aACnE,MAAM,UAAU,OAAO,YAAY;SAChC,IAAI,MAAM,OAAO,aAAa,KAAK,IAAI,IAAI,MAAM,aAAa,MAAM,OAAO,WAC9E,MAAM,UAAU,OAAO,SAAS;GACpC,UAAU;IACR,WAAW;GACb;EACF;EACA,QAAQ,QAAQ,kBAAkB;GAChC,AAAK,OAAO,CAAC,CAAC,OAAO,UAAmB;IAEtC,cAAc,QAAQ,KAAK;IAC3B,IAAI,CAAC,QAAQ,UAAU,QAAQ,KAAK,KAAK;GAC3C,CAAC;EACH,GAAG,MAAM;EACT,QAAQ,MAAM,MAAM;CACtB;CAEA,eAAe,OACb,OACA,UACoC;EACpC,MAAM,KAAK;EACX,IAAI,CAAC,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,OAAO,IAAI,GAAG,CAAC,GACpD,MAAM,IAAI,MAAM,0DAA0D;EAC5E,IACE,OAAO,MAAM,YAAY,YACzB,CAAC,MAAM,WACP,OAAO,MAAM,SAAS,YACtB,CAAC,MAAM,MAEP,MAAM,IAAI,MAAM,+BAA+B;EACjD,eAAe,EAAE,QAAQ,MAAM,OAAO,CAAC;EACvC,IACE,YACA,CAAC,MAAM,WAAW,IAAI,MAAM,QAAQ,EAAE,CAAC,CAAC,MACrC,SAAS,CAAC,OAAO,cAAc,IAAI,KAAK,QAAQ,CACnD,GAEA,MAAM,IAAI,MAAM,oDAAoD;EACtE,MAAM,SAAS;GAAE,GAAG,QAAQ;GAAU,GAAG,MAAM;EAAO;EACtD,eAAe,MAAM;EACrB,MAAM,YAAQC,yBAAY,EAAE,CAAC,CAAC,SAAS,KAAK;EAC5C,MAAM,EACJ,MAAM,YACN,MACA,QACE,cACF,QAAQ,UACR,QAAQ,UACR,MAAM,SACN,MAAM,QAAQ,CAAC,GACf,QACA;GAAE,GAAG,QAAQ;GAAK,GAAG,MAAM;EAAI,GAC/B,KACF;EACA,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,MAAM,UAAU,MAAqB,QAAuB;GAC1D,OAAO;IAAE;IAAM,QAAQ;GAAI;GAC3B,IAAI,SAAS,AAAK,QAAQ,OAAO,MAAM,GAAG,CAAC,CAAC,YAAY,MAAS;EACnE;EACA,IAAI,UAAU;GAIZ,mBAAkB,MADC,OAAO,YACL,CAAC,MAAM,YAAY,MAAM;IAC5C,KAAK,MAAM;IACX,KAAK,OAAO,YACV,OAAO,QAAQ,GAAG,CAAC,CAAC,QAAQ,SAAmC,KAAK,OAAO,MAAS,CACtF;IACA,MAAM,MAAM,WAAW;IACvB,MAAM,MAAM,QAAQ;IACpB,MAAM;GACR,CAAC;GACD,MAAM,gBAAgB;GACtB,MAAM,SAAS,IAAID,wBAAY;GAC/B,MAAM,SAAS,IAAIA,wBAAY;GAC/B,MAAM,WAAW;GACjB,MAAM,mBAAmB,SAAS,QAAQ,SAAS,OAAO,MAAM,IAAI,CAAC;GACrE,MAAM,cAAc,IAAIE,qBAAS,EAC/B,MAAM,OAAe,WAAW,UAAU;IACxC,SAAS,MAAM,MAAM,SAAS,CAAC;IAC/B,SAAS;GACX,EACF,CAAC;GACD,MAAM,mBAAmB,SAAS,QAAQ,UAAU;IAClD,iBAAiB,QAAQ;IACzB,iBAAiB,QAAQ;IACzB,OAAO,IAAI;IACX,OAAO,IAAI;IACX,YAAY,IAAI;IAChB,OAAO,MAAM,UAAU,MAAM,SAAS,OAAO,MAAM,MAAM,IAAI,IAAI;GACnE,CAAC;GACD,UAAU;IAAE,QAAQ;IAAQ,QAAQ;IAAQ,OAAO;GAAY;EACjE,OAAO;GACL,MAAM,YAAQC,0BAAW,YAAY,MAAM;IACzC,KAAK,MAAM;IACX;IACA,UAAU,QAAQ,aAAa;IAC/B,aAAa;IACb,OAAO;GACT,CAAC;GACD,MAAM,GAAG,UAAU,UAAU;IAC3B,aAAa;GACf,CAAC;GAGD,MAAM,GAAG,QAAQ,MAAM;GACvB,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,MAAM,yBAAyB;GACzD,MAAM,MAAM;GACZ,UAAU;IAAE,QAAQ,MAAM;IAAQ,QAAQ,MAAM;IAAQ,OAAO,MAAM;GAAM;EAC7E;EACA,IAAI;EACJ,IAAI;GACF,MAAM,WAAW,KAAK,IAAI,IAAI;GAC9B,IAAI,UAAU,MAAM,SAAS,KAAK,MAAM,KAAK;GAC7C,OAAO,CAAC,SAAS,MAAM,WAAW,GAAG,KAAK,QAAQ,UAAU,OAAO;IACjE,IAAI,YAAY,MAAM;IACtB,IAAI,QAAQ,KAAK,IAAI,KAAK,UACxB,MAAM,IAAI,MAAM,+CAA+C;IACjE,UAAMJ,iCAAM,EAAE;IACd,UAAU,MAAM,SAAS,KAAK,MAAM,KAAK;GAC3C;GACA,IAAI,QAAQ,UAAU,KAAK,MAAM,IAAI,MAAM,gDAAgD;GAC3F,QAAQ;IACN;IACA,OAAO,QAAQ;IACf,MAAM,MAAM;IACZ,SAAS,MAAM;IACf,MAAM,CAAC,GAAI,MAAM,QAAQ,CAAC,CAAE;IAC5B,QAAQ,EAAE,GAAG,MAAM,OAAO;IAC1B,WAAW,KAAK,IAAI;IACpB;IACA,aAAa,QAAQ,aAAa,UAAU,gBAAgB;IAC5D,iBAAiB,QAAQ;IACzB;IACA,OAAO,QAAQ;IACf,OAAO;GACT;GACA,MAAM,SAAS,MAAM,aAAa,KAAK,OAAO,QAAQ,QAAQ,UAAU;GACxE,IAAI,MAAM,QAAQ,MAAM,cAAc;GACtC,QAAQ,IAAI,KAAK,KAAK;GACtB,MAAM,KAAK;GACX,UAAU,MAAM,OAAO,OAAO;GAC9B,MAAM,OAAO,OAAO,SAAS;GAC7B,OAAO,MAAM;GACb,MAAM,KAAK;GACX,QAAQ,OAAO;GACf,IAAI,iBAAiB;IACnB,MAAM,WAAW;IACjB,OAAO;KACL,GAAG,QAAQ;KACX,QAAQ,SAAiB,SAAS,MAAM,IAAI;KAC5C,SAAS,SAAiB,SAAiB,SAAS,OAAO,SAAS,IAAI;IAC1E;GACF;GACA,OAAO,QAAQ;EACjB,SAAS,OAAO;GAEd,MAAM,UAAU,MAAM,SAAS,KAAK,MAAM,KAAK;GAC/C,IAAI,SAAS,UAAU,OAAO;IAC5B,MAAM,SAAS;KAAE;KAAK;KAAO,OAAO,QAAQ;IAAM;IAClD,IAAI,MAAM,KAAK,MAAM,GACnB,IAAI;KACF,IAAI,QAAQ,aAAa,SAAS,MAAM,eAAe,gBAAgB,GAAG,CAAC;UACtE,QAAQ,KAAK,CAAC,KAAK,SAAS;IACnC,QAAQ,CAER;GAEJ;GACA,IAAI,SAAS,CAAC,SAAS;IACrB,QAAQ,OAAO,GAAG;IAClB,MAAM,KAAK;GACb;GACA,IAAI,OAAO,QAAQ,MAAM,YAAY,MAAM,MAAM;GACjD,MAAM;EACR;CACF;CAwFA,OAAO;EArFL,QAAQ,UAAU,aAAa,OAAO,OAAO,KAAK,CAAC;EACnD,WAAW,UAAU,OAAO,YAAa,MAAM,OAAO,OAAO,IAAI,CAAe;EAChF,OAAO,SAAS,CAAC,MACf,OAAO,YAAY;GACjB,eAAe,MAAM;GACrB,MAAM,KAAK;GACX,OAAO,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,QAAQ,UAAU,QAAQ,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI;EACjF,CAAC;EACH,OAAO,SAAS,CAAC,MACf,OAAO,YAAY;GACjB,eAAe,MAAM;GACrB,MAAM,KAAK;GACX,KAAK,MAAM,SAAS,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,QAAQ,UAAU,QAAQ,OAAO,MAAM,CAAC,GAChF,MAAM,UAAU,KAAK;EACzB,CAAC;EACH,UAAU,QACR,OAAO,YAAY;GACjB,MAAM,KAAK;GACX,MAAM,QAAQ,QAAQ,IAAI,GAAG;GAC7B,IAAI,CAAC,OAAO,MAAM,IAAI,qBAAqB,GAAG;GAC9C,MAAM,UAAU,KAAK;EACvB,CAAC;EACH,UAAU,WAAW,CAAC,MACpB,OAAO,YAAY;GACjB,MAAM,KAAK;GACX,MAAM,SAAyB;IAAE,SAAS,CAAC;IAAG,SAAS,CAAC;IAAG,SAAS,CAAC;GAAE;GACvE,KAAK,MAAM,SAAS,CAAC,GAAG,QAAQ,OAAO,CAAC,GAAG;IACzC,IAAI,OAAO,IAAI,MAAM,GAAG,GAAG;IAC3B,IAAI,CAAE,MAAM,KAAK,KAAK,GAAI;KACxB,QAAQ,OAAO,MAAM,GAAG;KACxB,OAAO,QAAQ,KAAK,MAAM,GAAG;KAC7B;IACF;IAEA,MAAM,SAAS,MAAM,gBAAgB,MAAM,KAAK,MAAM,KAAK;IAC3D,MAAM,cAAc,MAAM,SACtB,cACA,QAAQ,aAAa,UACnB,gBACA;IAEN,IAAI,MADe,SAAS,OAAO,KAAK,KAAK,CAAC,GACpC;KAER,QADgB,MAAM,KACR,CAAC;KACf,IAAI,MAAM,OAAO;MACf,MAAM,OAAO,OAAO,SAAS;MAC7B,OAAO,MAAM;KACf;KACA,OAAO,QAAQ,KAAK,MAAM,GAAG;IAC/B,OAAO;KACL,MAAM,UAAU,KAAK;KACrB,OAAO,QAAQ,KAAK,MAAM,GAAG;IAC/B;GACF;GACA,MAAM,KAAK;GACX,OAAO;EACT,CAAC;EACH,UAAU;GACR,aAAa,OAAO,YAAY;IAC9B,MAAM,KAAK;IACX,UAAU;IACV,IAAI;KACF,MAAM,SAAoB,CAAC;KAC3B,KAAK,MAAM,SAAS,CAAC,GAAG,QAAQ,OAAO,CAAC,GACtC,IAAI;MACF,IAAI,MAAM,KAAK,KAAK,GAAG,MAAM,UAAU,KAAK;WACvC;OACH,MAAM,UAAU,OAAO,IAAI,MAAM,GAAG;OACpC,IAAI,SAAS,MAAM,QAAQ,OAAO,MAAM,IAAI;YACvC,QAAQ,OAAO,MAAM,GAAG;MAC/B;KACF,SAAS,OAAO;MACd,OAAO,KAAK,KAAK;KACnB;KAEF,MAAM,KAAK;KACX,IAAI,OAAO,QACT,MAAM,IAAI,eAAe,QAAQ,2CAA2C;IAChF,UAAU;KACR,MAAM,SAAS,MAAM;IACvB;GACF,CAAC;GACD,OAAO;EACT;CAEO;AACX"}
|
package/esm/guardian.mjs
CHANGED
|
@@ -26,7 +26,10 @@ const start = () => {
|
|
|
26
26
|
gated = false;
|
|
27
27
|
const [command, ...args] = JSON.parse(process.env.WARLOCK_PROCESS_LAUNCH);
|
|
28
28
|
const env = { ...process.env };
|
|
29
|
-
|
|
29
|
+
for (const key of Object.keys(env)) {
|
|
30
|
+
const name = process.platform === "win32" ? key.toUpperCase() : key;
|
|
31
|
+
if (name === "WARLOCK_PROCESS_TOKEN" || name === "WARLOCK_PROCESS_LAUNCH") delete env[key];
|
|
32
|
+
}
|
|
30
33
|
const child = spawn(command, args, { env, stdio: "inherit", windowsHide: true });
|
|
31
34
|
child.on("error", (error) => { console.error(error.message); finish(127); });
|
|
32
35
|
child.on("exit", (code, signal) => {
|
package/esm/guardian.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guardian.mjs","names":[],"sources":["../../../../../../../packages/process/src/guardian.ts"],"sourcesContent":["import { createHmac, randomBytes } from \"node:crypto\";\nimport { connect } from \"node:net\";\nimport type { Command, OwnershipProof } from \"./platform\";\nimport { launcher } from \"./resources\";\nimport type { ProcessLimits } from \"./types\";\n\nexport function guardianAddress(platform: string, pid: number, uid: number): string {\n return platform === \"win32\"\n ? `\\\\\\\\.\\\\pipe\\\\warlock-process-${pid}`\n : `/tmp/warlock-process-${uid}-${pid}.sock`;\n}\n\n/** A live guardian reads its own environment, never argv, to prove ownership.\n * Node IPC is native-free: https://nodejs.org/api/net.html#ipc-support\n * The stable root PID stays alive for the workload and survives supervisor restarts.\n */\nexport const guardianScript = String.raw`\nconst { createServer } = require(\"node:net\");\nconst { createHmac } = require(\"node:crypto\");\nconst { spawn } = require(\"node:child_process\");\nconst { chmodSync, unlinkSync } = require(\"node:fs\");\nconst token = process.env.WARLOCK_PROCESS_TOKEN;\nconst address = process.platform === \"win32\"\n ? \"\\\\\\\\.\\\\pipe\\\\warlock-process-\" + process.pid\n : \"/tmp/warlock-process-\" + process.getuid() + \"-\" + process.pid + \".sock\";\nlet gated = true;\nconst finish = (code) => { server.close(); process.exit(code); };\nconst start = () => {\n if (!gated) return;\n gated = false;\n const [command, ...args] = JSON.parse(process.env.WARLOCK_PROCESS_LAUNCH);\n const env = { ...process.env };\n
|
|
1
|
+
{"version":3,"file":"guardian.mjs","names":[],"sources":["../../../../../../../packages/process/src/guardian.ts"],"sourcesContent":["import { createHmac, randomBytes } from \"node:crypto\";\nimport { connect } from \"node:net\";\nimport type { Command, OwnershipProof } from \"./platform\";\nimport { launcher } from \"./resources\";\nimport type { ProcessLimits } from \"./types\";\n\nexport function guardianAddress(platform: string, pid: number, uid: number): string {\n return platform === \"win32\"\n ? `\\\\\\\\.\\\\pipe\\\\warlock-process-${pid}`\n : `/tmp/warlock-process-${uid}-${pid}.sock`;\n}\n\n/** A live guardian reads its own environment, never argv, to prove ownership.\n * Node IPC is native-free: https://nodejs.org/api/net.html#ipc-support\n * The stable root PID stays alive for the workload and survives supervisor restarts.\n */\nexport const guardianScript = String.raw`\nconst { createServer } = require(\"node:net\");\nconst { createHmac } = require(\"node:crypto\");\nconst { spawn } = require(\"node:child_process\");\nconst { chmodSync, unlinkSync } = require(\"node:fs\");\nconst token = process.env.WARLOCK_PROCESS_TOKEN;\nconst address = process.platform === \"win32\"\n ? \"\\\\\\\\.\\\\pipe\\\\warlock-process-\" + process.pid\n : \"/tmp/warlock-process-\" + process.getuid() + \"-\" + process.pid + \".sock\";\nlet gated = true;\nconst finish = (code) => { server.close(); process.exit(code); };\nconst start = () => {\n if (!gated) return;\n gated = false;\n const [command, ...args] = JSON.parse(process.env.WARLOCK_PROCESS_LAUNCH);\n const env = { ...process.env };\n for (const key of Object.keys(env)) {\n const name = process.platform === \"win32\" ? key.toUpperCase() : key;\n if (name === \"WARLOCK_PROCESS_TOKEN\" || name === \"WARLOCK_PROCESS_LAUNCH\") delete env[key];\n }\n const child = spawn(command, args, { env, stdio: \"inherit\", windowsHide: true });\n child.on(\"error\", (error) => { console.error(error.message); finish(127); });\n child.on(\"exit\", (code, signal) => {\n if (signal && process.platform !== \"win32\") process.kill(process.pid, signal);\n else finish(code ?? 1);\n });\n};\nconst server = createServer((socket) => {\n socket.setTimeout(2000, () => socket.destroy());\n socket.on(\"error\", () => {});\n let input = \"\";\n socket.on(\"data\", (chunk) => {\n input += chunk;\n if (input.length > 1024) { socket.destroy(); return; }\n if (!input.endsWith(\"\\n\")) return;\n try {\n const request = JSON.parse(input);\n if (!/^[a-f0-9]{64}$/.test(request.nonce)) { socket.destroy(); return; }\n const sign = (value) => createHmac(\"sha256\", token).update(value).digest(\"hex\");\n if (request.resume === sign(request.nonce + \":resume\")) start();\n socket.end(JSON.stringify({ pid: process.pid, digest: sign(request.nonce + \":\" + process.pid + \":\" + gated), gated }) + \"\\n\");\n } catch { socket.destroy(); }\n });\n});\nserver.on(\"error\", (error) => { console.error(error.message); process.exit(126); });\nif (process.platform !== \"win32\") {\n try { unlinkSync(address); } catch (error) { if (error.code !== \"ENOENT\") throw error; }\n process.on(\"exit\", () => { try { unlinkSync(address); } catch {} });\n}\nserver.listen(address, () => { if (process.platform !== \"win32\") chmodSync(address, 0o600); });\n`;\n\nexport async function guardianProof(\n pid: number,\n resume?: string,\n expectedToken: string | undefined = resume,\n): Promise<OwnershipProof | undefined> {\n if (!expectedToken) return undefined;\n const token = expectedToken;\n const nonce = randomBytes(32).toString(\"hex\");\n const sign = (value: string) => createHmac(\"sha256\", token).update(value).digest(\"hex\");\n const address = guardianAddress(process.platform, pid, process.getuid?.() ?? 0);\n return new Promise((resolve, reject) => {\n const socket = connect(address);\n let data = \"\";\n const fail = (error: Error) => {\n socket.destroy();\n reject(error);\n };\n socket.setTimeout(2000, () => fail(new Error(\"Process ownership observation timed out\")));\n socket.on(\"connect\", () =>\n socket.write(\n JSON.stringify({ nonce, resume: resume ? sign(nonce + \":resume\") : undefined }) + \"\\n\",\n ),\n );\n socket.on(\"data\", (chunk: Buffer) => {\n data += chunk.toString();\n if (data.length > 1024) fail(new Error(\"Invalid process ownership response\"));\n });\n socket.on(\"error\", (error: NodeJS.ErrnoException) => {\n if ([\"ENOENT\", \"ECONNREFUSED\"].includes(error.code ?? \"\")) resolve(undefined);\n else reject(error);\n });\n socket.on(\"end\", () => {\n try {\n resolve(parseGuardianProof(data, pid, nonce, token));\n } catch (error) {\n reject(error);\n }\n });\n });\n}\n\n/** Malformed observation is an error; an authenticated identity mismatch is unowned. */\nexport function parseGuardianProof(\n source: string,\n pid: number,\n nonce: string,\n token: string,\n): OwnershipProof | undefined {\n const proof: unknown = JSON.parse(source);\n if (\n !proof ||\n typeof proof !== \"object\" ||\n !(\"pid\" in proof) ||\n typeof proof.pid !== \"number\" ||\n !(\"gated\" in proof) ||\n typeof proof.gated !== \"boolean\" ||\n !(\"digest\" in proof) ||\n typeof proof.digest !== \"string\"\n )\n throw new Error(\"Invalid process ownership response\");\n const expected = createHmac(\"sha256\", token)\n .update(nonce + \":\" + pid + \":\" + proof.gated)\n .digest(\"hex\");\n if (proof.pid !== pid || proof.digest !== expected) return undefined;\n return { pid, token, gated: proof.gated };\n}\n\n/** Pure launch construction, including case-insensitive Windows environment keys. */\nexport function launchCommand(\n platform: string,\n node: string,\n command: string,\n args: string[],\n limits: ProcessLimits,\n environment: NodeJS.ProcessEnv,\n token: string,\n): Omit<Command, \"env\"> & { env: NodeJS.ProcessEnv } {\n const portable = platform !== \"linux\";\n const env: NodeJS.ProcessEnv = Object.fromEntries(\n Object.entries(environment).filter(\n ([key]) =>\n ![\"WARLOCK_PROCESS_TOKEN\", \"WARLOCK_PROCESS_LAUNCH\"].includes(\n platform === \"win32\" ? key.toUpperCase() : key,\n ),\n ),\n );\n env.WARLOCK_PROCESS_TOKEN = token;\n if (portable) env.WARLOCK_PROCESS_LAUNCH = JSON.stringify([command, ...args]);\n const executable = portable ? node : command;\n const arguments_ = portable ? [\"-e\", guardianScript] : args;\n return {\n file: platform === \"win32\" ? executable : \"/bin/sh\",\n args:\n platform === \"win32\"\n ? arguments_\n : [\"-c\", launcher(limits, platform), \"warlock-process\", executable, ...arguments_],\n env,\n };\n}\n"],"mappings":"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"}
|
package/esm/supervisor.mjs
CHANGED
|
@@ -22,7 +22,7 @@ function matches(entry, filter) {
|
|
|
22
22
|
return Object.entries(filter.labels ?? {}).every(([key, value]) => entry.labels[key] === value);
|
|
23
23
|
}
|
|
24
24
|
function supervisor(options) {
|
|
25
|
-
if (!options
|
|
25
|
+
for (const option of ["owner", "registry"]) if (!options || typeof options[option] !== "string" || !options[option].trim()) throw new TypeError(`Invalid supervisor configuration: ${option} must be a nonempty string`);
|
|
26
26
|
validateLimits(options.defaults ?? {});
|
|
27
27
|
options = {
|
|
28
28
|
...options,
|
package/esm/supervisor.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supervisor.mjs","names":["delay","spawnChild"],"sources":["../../../../../../../packages/process/src/supervisor.ts"],"sourcesContent":["import { spawn as spawnChild } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport path from \"node:path\";\nimport { PassThrough, Writable } from \"node:stream\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { identity, owns, ProcessNotOwnedError, executeCommand } from \"./identity\";\nimport { guardianProof, launchCommand } from \"./guardian\";\nimport { supported, taskkillCommand } from \"./platform\";\nimport { Registry, type RegistryEntry } from \"./registry\";\nimport {\n attachCgroup,\n cgroupMemory,\n cleanCgroup,\n recoveredCgroup,\n validateLimits,\n} from \"./resources\";\nimport type {\n ProcessExit,\n ProcessFilter,\n ProcessHandle,\n ProcessInfo,\n ProcessSupervisor,\n PtyHandle,\n RecoveryResult,\n SpawnOptions,\n SupervisorOptions,\n} from \"./types\";\n\ntype Pty = {\n pid: number;\n onData(listener: (data: string) => void): { dispose(): void };\n onExit(listener: (event: { exitCode: number; signal?: number }) => void): { dispose(): void };\n write(data: string): void;\n resize(columns: number, rows: number): void;\n};\ntype PtyModule = {\n spawn(\n command: string,\n args: string[],\n options: {\n cwd?: string;\n env: Record<string, string>;\n cols: number;\n rows: number;\n name: string;\n },\n ): Pty;\n};\ntype Active = {\n entry: RegistryEntry;\n handle: ProcessHandle;\n reason?: ProcessExit[\"reason\"];\n finish(code: number | null, signal: string | null): Promise<void>;\n stopping?: Promise<void>;\n timer?: ReturnType<typeof setInterval>;\n finished: boolean;\n adopted: boolean;\n fail(error: unknown): void;\n};\n\nfunction info(entry: RegistryEntry): ProcessInfo {\n const { token: _token, start: _start, cgroup: _cgroup, gated: _gated, ...visible } = entry;\n return structuredClone(visible);\n}\nfunction validateFilter(filter: ProcessFilter): void {\n if (\n !filter ||\n typeof filter !== \"object\" ||\n Array.isArray(filter) ||\n Object.keys(filter).some((key) => key !== \"labels\")\n )\n throw new Error(\"Only label filters are supported\");\n if (\n filter.labels !== undefined &&\n (filter.labels === null ||\n typeof filter.labels !== \"object\" ||\n Array.isArray(filter.labels) ||\n !Object.values(filter.labels).every((value) => typeof value === \"string\"))\n )\n throw new Error(\"Labels must be string key/value pairs\");\n}\nfunction matches(entry: RegistryEntry, filter: ProcessFilter): boolean {\n return Object.entries(filter.labels ?? {}).every(([key, value]) => entry.labels[key] === value);\n}\n\nexport function supervisor(options: SupervisorOptions): ProcessSupervisor {\n if (!options.owner.trim() || !options.registry.trim())\n throw new Error(\"owner and registry are required\");\n validateLimits(options.defaults ?? {});\n // Caller-owned configuration must not change an active supervisor's identity.\n options = { ...options, defaults: { ...options.defaults } };\n const pollMs = options.pollIntervalMs ?? 100;\n const graceMs = options.stopGraceMs ?? 1000;\n if (!Number.isSafeInteger(pollMs) || pollMs < 10 || !Number.isSafeInteger(graceMs) || graceMs < 0)\n throw new Error(\"Invalid polling interval or stop grace period\");\n const registry = new Registry(path.resolve(options.registry), options.owner);\n const entries = new Map<number, RegistryEntry>();\n const active = new Map<number, Active>();\n let ready: Promise<void> | undefined;\n let closing = false;\n let shutdown: Promise<void> | undefined;\n // Serialize public operations, including shutdown, so a gated spawn cannot escape stopAll.\n let queue: Promise<unknown> = Promise.resolve();\n function serial<T>(operation: () => Promise<T>): Promise<T> {\n const next = queue.then(operation);\n queue = next.catch(() => undefined);\n return next;\n }\n async function open(): Promise<void> {\n supported(process.platform);\n if (closing) throw new Error(\"Supervisor has shut down\");\n ready ??= registry.open().then((saved) => {\n for (const entry of saved) {\n delete entry.cgroup;\n entries.set(entry.pid, entry);\n }\n });\n await ready;\n }\n const save = () => registry.save([...entries.values()]);\n\n async function signal(entry: RegistryEntry, value: NodeJS.Signals): Promise<void> {\n if (!(await owns(entry))) throw new ProcessNotOwnedError(entry.pid);\n if (process.platform !== \"linux\" && value === \"SIGCONT\") {\n const proof = await guardianProof(entry.pid, entry.token);\n if (!proof || proof.token !== entry.token || proof.gated)\n throw new ProcessNotOwnedError(entry.pid);\n return;\n }\n if (process.platform === \"win32\") {\n try {\n await executeCommand(taskkillCommand(entry.pid));\n } catch (error) {\n if (await owns(entry)) throw error;\n }\n return;\n }\n // Negative PID targets only the session/group established for this child.\n // Recheck for every signal, including escalation; never match a command line.\n try {\n process.kill(-entry.pid, value);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ESRCH\") throw error;\n }\n }\n async function stopEntry(\n entry: RegistryEntry,\n reason: ProcessExit[\"reason\"] = \"stopped\",\n ): Promise<void> {\n const tracked = active.get(entry.pid);\n let running: Active;\n if (!tracked) {\n if (!(await owns(entry))) throw new ProcessNotOwnedError(entry.pid);\n entry.cgroup = await recoveredCgroup(entry.pid, entry.token);\n running = track(entry);\n } else running = tracked;\n if (running.finished) return running.finish(null, null);\n if (running.stopping) return running.stopping;\n const stopping = (async () => {\n if (!(await owns(entry))) throw new ProcessNotOwnedError(entry.pid);\n running.reason ??= reason;\n await signal(entry, \"SIGTERM\");\n const deadline = Date.now() + graceMs;\n while (await owns(entry)) {\n if (Date.now() >= deadline) {\n await signal(entry, \"SIGKILL\");\n break;\n }\n await delay(20);\n }\n const killedDeadline = Date.now() + 5000;\n while (await owns(entry)) {\n if (Date.now() >= killedDeadline)\n throw new Error(`Process ${entry.pid} did not exit after SIGKILL`);\n await delay(20);\n }\n await running.finish(null, null);\n })();\n running.stopping = stopping;\n try {\n await stopping;\n } catch (error) {\n running.stopping = undefined;\n // The leader may exit between identity verification and a signal. A\n // missing identity is harmless for a tracked handle; a live mismatch is\n // still an ownership failure and must never receive another signal.\n if (error instanceof ProcessNotOwnedError && !(await identity(entry.pid))) {\n await running.finish(null, null);\n return;\n }\n throw error;\n }\n }\n\n function track(\n entry: RegistryEntry,\n streams?: Pick<ProcessHandle, \"stdin\" | \"stdout\" | \"stderr\">,\n ): Active {\n let resolveExit: (exit: ProcessExit) => void = () => undefined;\n let rejectExit: (error: unknown) => void = () => undefined;\n const exited = new Promise<ProcessExit>((resolve, reject) => {\n resolveExit = resolve;\n rejectExit = reject;\n });\n // A rejected persistence operation is still observable through handle.exited.\n void exited.catch(() => undefined);\n const stdout = streams?.stdout ?? new PassThrough();\n const stderr = streams?.stderr ?? new PassThrough();\n const stdin = streams?.stdin ?? new PassThrough();\n let finishing: Promise<void> | undefined;\n const running: Active = {\n entry,\n finished: false,\n adopted: !streams,\n fail: (error) => rejectExit(error),\n handle: {\n pid: entry.pid,\n stdout,\n stderr,\n stdin,\n exited,\n stop: () =>\n serial(async () => {\n if (!running.finished) await stopEntry(entry);\n }),\n },\n finish(code, sig) {\n finishing ??= (async () => {\n running.finished = true;\n clearInterval(running.timer);\n if (entry.cgroup) {\n const memory = await cgroupMemory(entry.cgroup);\n entry.peakMemoryBytes = Math.max(entry.peakMemoryBytes, memory.peak);\n if (memory.oom) running.reason = \"memory-cap\";\n await cleanCgroup(entry.cgroup);\n }\n active.delete(entry.pid);\n entries.delete(entry.pid);\n try {\n await save();\n resolveExit({\n reason: running.reason ?? \"exit\",\n code,\n signal: sig,\n peakMemoryBytes: entry.peakMemoryBytes,\n });\n } catch (error) {\n rejectExit(error);\n throw error;\n }\n if (!streams) {\n (stdout as PassThrough).end();\n (stderr as PassThrough).end();\n stdin.end();\n }\n })();\n return finishing;\n },\n };\n active.set(entry.pid, running);\n return running;\n }\n\n function monitor(running: Active): void {\n if (running.finished) return;\n let sampling = false;\n const sample = async () => {\n if (sampling || running.finished) return;\n sampling = true;\n try {\n const entry = running.entry;\n const current = await identity(entry.pid, true, entry.token);\n if (running.finished) return;\n if (\n current?.start !== entry.start ||\n current.token !== entry.token ||\n current.group !== entry.pid\n ) {\n if (!current) {\n if (running.adopted) await running.finish(null, null);\n } else {\n // A live child that changes its environment or group can no longer\n // be supervised safely. Preserve the entry and surface the failure.\n throw new ProcessNotOwnedError(entry.pid);\n }\n return;\n }\n entry.peakMemoryBytes = Math.max(entry.peakMemoryBytes, current.rss);\n if (entry.cgroup) {\n const memory = await cgroupMemory(entry.cgroup);\n if (running.finished) return;\n entry.peakMemoryBytes = Math.max(entry.peakMemoryBytes, memory.peak);\n if (memory.oom) running.reason = \"memory-cap\";\n }\n if (entry.limits.memoryBytes && entry.peakMemoryBytes > entry.limits.memoryBytes)\n await stopEntry(entry, \"memory-cap\");\n else if (entry.limits.timeoutMs && Date.now() - entry.startedAt >= entry.limits.timeoutMs)\n await stopEntry(entry, \"timeout\");\n } finally {\n sampling = false;\n }\n };\n running.timer = setInterval(() => {\n void sample().catch((error: unknown) => {\n // Never signal on an observation error. Surface it to the caller and keep the registry.\n clearInterval(running.timer);\n if (!running.finished) running.fail(error);\n });\n }, pollMs);\n running.timer.unref();\n }\n\n async function launch(\n input: SpawnOptions & { columns?: number; rows?: number },\n terminal: boolean,\n ): Promise<ProcessHandle | PtyHandle> {\n await open();\n if ([...entries.keys()].some((pid) => !active.has(pid)))\n throw new Error(\"Call recover() before spawning with an existing registry\");\n if (\n typeof input.command !== \"string\" ||\n !input.command ||\n typeof input.kind !== \"string\" ||\n !input.kind\n )\n throw new Error(\"kind and command are required\");\n validateFilter({ labels: input.labels });\n if (\n terminal &&\n [input.columns ?? 80, input.rows ?? 24].some(\n (size) => !Number.isSafeInteger(size) || size <= 0,\n )\n )\n throw new Error(\"Terminal dimensions must be positive safe integers\");\n const limits = { ...options.defaults, ...input.limits };\n validateLimits(limits);\n const token = randomBytes(32).toString(\"hex\");\n const {\n file: executable,\n args,\n env,\n } = launchCommand(\n process.platform,\n process.execPath,\n input.command,\n input.args ?? [],\n limits,\n { ...process.env, ...input.env },\n token,\n );\n let pid: number;\n let streams: Pick<ProcessHandle, \"stdin\" | \"stdout\" | \"stderr\">;\n let terminalProcess: Pty | undefined;\n let exit: { code: number | null; signal: string | null } | undefined;\n let spawnError: Error | undefined;\n let running: Active | undefined;\n const onExit = (code: number | null, sig: string | null) => {\n exit = { code, signal: sig };\n if (running) void running.finish(code, sig).catch(() => undefined);\n };\n if (terminal) {\n // Optional peer loaded only here. Structural types follow node-pty's official typings.\n const peer = \"node-pty\";\n const pty = (await import(peer)) as PtyModule;\n terminalProcess = pty.spawn(executable, args, {\n cwd: input.cwd,\n env: Object.fromEntries(\n Object.entries(env).filter((pair): pair is [string, string] => pair[1] !== undefined),\n ),\n cols: input.columns ?? 80,\n rows: input.rows ?? 24,\n name: \"xterm-256color\",\n });\n pid = terminalProcess.pid;\n const output = new PassThrough();\n const errors = new PassThrough();\n const instance = terminalProcess;\n const dataSubscription = instance.onData((data) => output.write(data));\n const inputStream = new Writable({\n write(chunk: Buffer, _encoding, callback) {\n instance.write(chunk.toString());\n callback();\n },\n });\n const exitSubscription = instance.onExit((event) => {\n dataSubscription.dispose();\n exitSubscription.dispose();\n output.end();\n errors.end();\n inputStream.end();\n onExit(event.exitCode, event.signal ? String(event.signal) : null);\n });\n streams = { stdout: output, stderr: errors, stdin: inputStream };\n } else {\n const child = spawnChild(executable, args, {\n cwd: input.cwd,\n env,\n detached: process.platform !== \"win32\",\n windowsHide: true,\n stdio: \"pipe\",\n });\n child.on(\"error\", (error) => {\n spawnError = error;\n });\n // Finish on leader exit so cgroup cleanup can stop remaining members;\n // waiting for close could deadlock on a descendant holding a pipe open.\n child.on(\"exit\", onExit);\n if (!child.pid) throw new Error(\"Could not start process\");\n pid = child.pid;\n streams = { stdout: child.stdout, stderr: child.stderr, stdin: child.stdin };\n }\n let entry: RegistryEntry | undefined;\n try {\n const deadline = Date.now() + 5000;\n let current = await identity(pid, true, token);\n while (!current?.state.startsWith(\"T\") || current.token !== token) {\n if (spawnError) throw spawnError;\n if (exit || Date.now() >= deadline)\n throw new Error(\"Child failed to reach the resource setup gate\");\n await delay(10);\n current = await identity(pid, true, token);\n }\n if (current.group !== pid) throw new Error(\"Child did not create an isolated process group\");\n entry = {\n pid,\n owner: options.owner,\n type: input.kind,\n command: input.command,\n args: [...(input.args ?? [])],\n labels: { ...input.labels },\n startedAt: Date.now(),\n limits,\n enforcement: process.platform === \"win32\" ? \"rss-polling\" : \"rlimit-polling\",\n peakMemoryBytes: current.rss,\n token,\n start: current.start,\n gated: true,\n };\n entry.cgroup = await attachCgroup(pid, token, limits, options.cgroupRoot);\n if (entry.cgroup) entry.enforcement = \"cgroup-v2\";\n entries.set(pid, entry);\n await save();\n running = track(entry, streams);\n await signal(entry, \"SIGCONT\");\n delete entry.gated;\n await save();\n monitor(running);\n if (terminalProcess) {\n const instance = terminalProcess;\n return {\n ...running.handle,\n write: (data: string) => instance.write(data),\n resize: (columns: number, rows: number) => instance.resize(columns, rows),\n };\n }\n return running.handle;\n } catch (error) {\n // This PID was created by this call. Kill only after its random token is verified.\n const current = await identity(pid, true, token);\n if (current?.token === token) {\n const failed = { pid, token, start: current.start };\n if (await owns(failed)) {\n try {\n if (process.platform === \"win32\") await executeCommand(taskkillCommand(pid));\n else process.kill(-pid, \"SIGKILL\");\n } catch {\n /* Already exited. */\n }\n }\n }\n if (entry && !running) {\n entries.delete(pid);\n await save();\n }\n if (entry?.cgroup) await cleanCgroup(entry.cgroup);\n throw error;\n }\n }\n\n const api: ProcessSupervisor = {\n spawn: (input) => serial(() => launch(input, false)),\n spawnPty: (input) => serial(async () => (await launch(input, true)) as PtyHandle),\n list: (filter = {}) =>\n serial(async () => {\n validateFilter(filter);\n await open();\n return [...entries.values()].filter((entry) => matches(entry, filter)).map(info);\n }),\n stop: (filter = {}) =>\n serial(async () => {\n validateFilter(filter);\n await open();\n for (const entry of [...entries.values()].filter((entry) => matches(entry, filter)))\n await stopEntry(entry);\n }),\n stopPid: (pid) =>\n serial(async () => {\n await open();\n const entry = entries.get(pid);\n if (!entry) throw new ProcessNotOwnedError(pid);\n await stopEntry(entry);\n }),\n recover: (recovery = {}) =>\n serial(async () => {\n await open();\n const result: RecoveryResult = { adopted: [], stopped: [], cleared: [] };\n for (const entry of [...entries.values()]) {\n if (active.has(entry.pid)) continue;\n if (!(await owns(entry))) {\n entries.delete(entry.pid);\n result.cleared.push(entry.pid);\n continue;\n }\n // Never trust a persisted filesystem path for cgroup I/O after a restart.\n entry.cgroup = await recoveredCgroup(entry.pid, entry.token);\n entry.enforcement = entry.cgroup\n ? \"cgroup-v2\"\n : process.platform === \"win32\"\n ? \"rss-polling\"\n : \"rlimit-polling\";\n const keep = await recovery.keep?.(info(entry));\n if (keep) {\n const running = track(entry);\n monitor(running);\n if (entry.gated) {\n await signal(entry, \"SIGCONT\");\n delete entry.gated;\n }\n result.adopted.push(entry.pid);\n } else {\n await stopEntry(entry);\n result.stopped.push(entry.pid);\n }\n }\n await save();\n return result;\n }),\n stopAll() {\n shutdown ??= serial(async () => {\n await open();\n closing = true;\n try {\n const errors: unknown[] = [];\n for (const entry of [...entries.values()]) {\n try {\n if (await owns(entry)) await stopEntry(entry);\n else {\n const running = active.get(entry.pid);\n if (running) await running.finish(null, null);\n else entries.delete(entry.pid);\n }\n } catch (error) {\n errors.push(error);\n }\n }\n await save();\n if (errors.length)\n throw new AggregateError(errors, \"Some owned processes could not be stopped\");\n } finally {\n await registry.close();\n }\n });\n return shutdown;\n },\n };\n return api;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"supervisor.mjs","names":["delay","spawnChild"],"sources":["../../../../../../../packages/process/src/supervisor.ts"],"sourcesContent":["import { spawn as spawnChild } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport path from \"node:path\";\nimport { PassThrough, Writable } from \"node:stream\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { identity, owns, ProcessNotOwnedError, executeCommand } from \"./identity\";\nimport { guardianProof, launchCommand } from \"./guardian\";\nimport { supported, taskkillCommand } from \"./platform\";\nimport { Registry, type RegistryEntry } from \"./registry\";\nimport {\n attachCgroup,\n cgroupMemory,\n cleanCgroup,\n recoveredCgroup,\n validateLimits,\n} from \"./resources\";\nimport type {\n ProcessExit,\n ProcessFilter,\n ProcessHandle,\n ProcessInfo,\n ProcessSupervisor,\n PtyHandle,\n RecoveryResult,\n SpawnOptions,\n SupervisorOptions,\n} from \"./types\";\n\ntype Pty = {\n pid: number;\n onData(listener: (data: string) => void): { dispose(): void };\n onExit(listener: (event: { exitCode: number; signal?: number }) => void): { dispose(): void };\n write(data: string): void;\n resize(columns: number, rows: number): void;\n};\ntype PtyModule = {\n spawn(\n command: string,\n args: string[],\n options: {\n cwd?: string;\n env: Record<string, string>;\n cols: number;\n rows: number;\n name: string;\n },\n ): Pty;\n};\ntype Active = {\n entry: RegistryEntry;\n handle: ProcessHandle;\n reason?: ProcessExit[\"reason\"];\n finish(code: number | null, signal: string | null): Promise<void>;\n stopping?: Promise<void>;\n timer?: ReturnType<typeof setInterval>;\n finished: boolean;\n adopted: boolean;\n fail(error: unknown): void;\n};\n\nfunction info(entry: RegistryEntry): ProcessInfo {\n const { token: _token, start: _start, cgroup: _cgroup, gated: _gated, ...visible } = entry;\n return structuredClone(visible);\n}\nfunction validateFilter(filter: ProcessFilter): void {\n if (\n !filter ||\n typeof filter !== \"object\" ||\n Array.isArray(filter) ||\n Object.keys(filter).some((key) => key !== \"labels\")\n )\n throw new Error(\"Only label filters are supported\");\n if (\n filter.labels !== undefined &&\n (filter.labels === null ||\n typeof filter.labels !== \"object\" ||\n Array.isArray(filter.labels) ||\n !Object.values(filter.labels).every((value) => typeof value === \"string\"))\n )\n throw new Error(\"Labels must be string key/value pairs\");\n}\nfunction matches(entry: RegistryEntry, filter: ProcessFilter): boolean {\n return Object.entries(filter.labels ?? {}).every(([key, value]) => entry.labels[key] === value);\n}\n\nexport function supervisor(options: SupervisorOptions): ProcessSupervisor {\n for (const option of [\"owner\", \"registry\"] as const) {\n if (!options || typeof options[option] !== \"string\" || !options[option].trim()) {\n throw new TypeError(`Invalid supervisor configuration: ${option} must be a nonempty string`);\n }\n }\n validateLimits(options.defaults ?? {});\n // Caller-owned configuration must not change an active supervisor's identity.\n options = { ...options, defaults: { ...options.defaults } };\n const pollMs = options.pollIntervalMs ?? 100;\n const graceMs = options.stopGraceMs ?? 1000;\n if (!Number.isSafeInteger(pollMs) || pollMs < 10 || !Number.isSafeInteger(graceMs) || graceMs < 0)\n throw new Error(\"Invalid polling interval or stop grace period\");\n const registry = new Registry(path.resolve(options.registry), options.owner);\n const entries = new Map<number, RegistryEntry>();\n const active = new Map<number, Active>();\n let ready: Promise<void> | undefined;\n let closing = false;\n let shutdown: Promise<void> | undefined;\n // Serialize public operations, including shutdown, so a gated spawn cannot escape stopAll.\n let queue: Promise<unknown> = Promise.resolve();\n function serial<T>(operation: () => Promise<T>): Promise<T> {\n const next = queue.then(operation);\n queue = next.catch(() => undefined);\n return next;\n }\n async function open(): Promise<void> {\n supported(process.platform);\n if (closing) throw new Error(\"Supervisor has shut down\");\n ready ??= registry.open().then((saved) => {\n for (const entry of saved) {\n delete entry.cgroup;\n entries.set(entry.pid, entry);\n }\n });\n await ready;\n }\n const save = () => registry.save([...entries.values()]);\n\n async function signal(entry: RegistryEntry, value: NodeJS.Signals): Promise<void> {\n if (!(await owns(entry))) throw new ProcessNotOwnedError(entry.pid);\n if (process.platform !== \"linux\" && value === \"SIGCONT\") {\n const proof = await guardianProof(entry.pid, entry.token);\n if (!proof || proof.token !== entry.token || proof.gated)\n throw new ProcessNotOwnedError(entry.pid);\n return;\n }\n if (process.platform === \"win32\") {\n try {\n await executeCommand(taskkillCommand(entry.pid));\n } catch (error) {\n if (await owns(entry)) throw error;\n }\n return;\n }\n // Negative PID targets only the session/group established for this child.\n // Recheck for every signal, including escalation; never match a command line.\n try {\n process.kill(-entry.pid, value);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ESRCH\") throw error;\n }\n }\n async function stopEntry(\n entry: RegistryEntry,\n reason: ProcessExit[\"reason\"] = \"stopped\",\n ): Promise<void> {\n const tracked = active.get(entry.pid);\n let running: Active;\n if (!tracked) {\n if (!(await owns(entry))) throw new ProcessNotOwnedError(entry.pid);\n entry.cgroup = await recoveredCgroup(entry.pid, entry.token);\n running = track(entry);\n } else running = tracked;\n if (running.finished) return running.finish(null, null);\n if (running.stopping) return running.stopping;\n const stopping = (async () => {\n if (!(await owns(entry))) throw new ProcessNotOwnedError(entry.pid);\n running.reason ??= reason;\n await signal(entry, \"SIGTERM\");\n const deadline = Date.now() + graceMs;\n while (await owns(entry)) {\n if (Date.now() >= deadline) {\n await signal(entry, \"SIGKILL\");\n break;\n }\n await delay(20);\n }\n const killedDeadline = Date.now() + 5000;\n while (await owns(entry)) {\n if (Date.now() >= killedDeadline)\n throw new Error(`Process ${entry.pid} did not exit after SIGKILL`);\n await delay(20);\n }\n await running.finish(null, null);\n })();\n running.stopping = stopping;\n try {\n await stopping;\n } catch (error) {\n running.stopping = undefined;\n // The leader may exit between identity verification and a signal. A\n // missing identity is harmless for a tracked handle; a live mismatch is\n // still an ownership failure and must never receive another signal.\n if (error instanceof ProcessNotOwnedError && !(await identity(entry.pid))) {\n await running.finish(null, null);\n return;\n }\n throw error;\n }\n }\n\n function track(\n entry: RegistryEntry,\n streams?: Pick<ProcessHandle, \"stdin\" | \"stdout\" | \"stderr\">,\n ): Active {\n let resolveExit: (exit: ProcessExit) => void = () => undefined;\n let rejectExit: (error: unknown) => void = () => undefined;\n const exited = new Promise<ProcessExit>((resolve, reject) => {\n resolveExit = resolve;\n rejectExit = reject;\n });\n // A rejected persistence operation is still observable through handle.exited.\n void exited.catch(() => undefined);\n const stdout = streams?.stdout ?? new PassThrough();\n const stderr = streams?.stderr ?? new PassThrough();\n const stdin = streams?.stdin ?? new PassThrough();\n let finishing: Promise<void> | undefined;\n const running: Active = {\n entry,\n finished: false,\n adopted: !streams,\n fail: (error) => rejectExit(error),\n handle: {\n pid: entry.pid,\n stdout,\n stderr,\n stdin,\n exited,\n stop: () =>\n serial(async () => {\n if (!running.finished) await stopEntry(entry);\n }),\n },\n finish(code, sig) {\n finishing ??= (async () => {\n running.finished = true;\n clearInterval(running.timer);\n if (entry.cgroup) {\n const memory = await cgroupMemory(entry.cgroup);\n entry.peakMemoryBytes = Math.max(entry.peakMemoryBytes, memory.peak);\n if (memory.oom) running.reason = \"memory-cap\";\n await cleanCgroup(entry.cgroup);\n }\n active.delete(entry.pid);\n entries.delete(entry.pid);\n try {\n await save();\n resolveExit({\n reason: running.reason ?? \"exit\",\n code,\n signal: sig,\n peakMemoryBytes: entry.peakMemoryBytes,\n });\n } catch (error) {\n rejectExit(error);\n throw error;\n }\n if (!streams) {\n (stdout as PassThrough).end();\n (stderr as PassThrough).end();\n stdin.end();\n }\n })();\n return finishing;\n },\n };\n active.set(entry.pid, running);\n return running;\n }\n\n function monitor(running: Active): void {\n if (running.finished) return;\n let sampling = false;\n const sample = async () => {\n if (sampling || running.finished) return;\n sampling = true;\n try {\n const entry = running.entry;\n const current = await identity(entry.pid, true, entry.token);\n if (running.finished) return;\n if (\n current?.start !== entry.start ||\n current.token !== entry.token ||\n current.group !== entry.pid\n ) {\n if (!current) {\n if (running.adopted) await running.finish(null, null);\n } else {\n // A live child that changes its environment or group can no longer\n // be supervised safely. Preserve the entry and surface the failure.\n throw new ProcessNotOwnedError(entry.pid);\n }\n return;\n }\n entry.peakMemoryBytes = Math.max(entry.peakMemoryBytes, current.rss);\n if (entry.cgroup) {\n const memory = await cgroupMemory(entry.cgroup);\n if (running.finished) return;\n entry.peakMemoryBytes = Math.max(entry.peakMemoryBytes, memory.peak);\n if (memory.oom) running.reason = \"memory-cap\";\n }\n if (entry.limits.memoryBytes && entry.peakMemoryBytes > entry.limits.memoryBytes)\n await stopEntry(entry, \"memory-cap\");\n else if (entry.limits.timeoutMs && Date.now() - entry.startedAt >= entry.limits.timeoutMs)\n await stopEntry(entry, \"timeout\");\n } finally {\n sampling = false;\n }\n };\n running.timer = setInterval(() => {\n void sample().catch((error: unknown) => {\n // Never signal on an observation error. Surface it to the caller and keep the registry.\n clearInterval(running.timer);\n if (!running.finished) running.fail(error);\n });\n }, pollMs);\n running.timer.unref();\n }\n\n async function launch(\n input: SpawnOptions & { columns?: number; rows?: number },\n terminal: boolean,\n ): Promise<ProcessHandle | PtyHandle> {\n await open();\n if ([...entries.keys()].some((pid) => !active.has(pid)))\n throw new Error(\"Call recover() before spawning with an existing registry\");\n if (\n typeof input.command !== \"string\" ||\n !input.command ||\n typeof input.kind !== \"string\" ||\n !input.kind\n )\n throw new Error(\"kind and command are required\");\n validateFilter({ labels: input.labels });\n if (\n terminal &&\n [input.columns ?? 80, input.rows ?? 24].some(\n (size) => !Number.isSafeInteger(size) || size <= 0,\n )\n )\n throw new Error(\"Terminal dimensions must be positive safe integers\");\n const limits = { ...options.defaults, ...input.limits };\n validateLimits(limits);\n const token = randomBytes(32).toString(\"hex\");\n const {\n file: executable,\n args,\n env,\n } = launchCommand(\n process.platform,\n process.execPath,\n input.command,\n input.args ?? [],\n limits,\n { ...process.env, ...input.env },\n token,\n );\n let pid: number;\n let streams: Pick<ProcessHandle, \"stdin\" | \"stdout\" | \"stderr\">;\n let terminalProcess: Pty | undefined;\n let exit: { code: number | null; signal: string | null } | undefined;\n let spawnError: Error | undefined;\n let running: Active | undefined;\n const onExit = (code: number | null, sig: string | null) => {\n exit = { code, signal: sig };\n if (running) void running.finish(code, sig).catch(() => undefined);\n };\n if (terminal) {\n // Optional peer loaded only here. Structural types follow node-pty's official typings.\n const peer = \"node-pty\";\n const pty = (await import(peer)) as PtyModule;\n terminalProcess = pty.spawn(executable, args, {\n cwd: input.cwd,\n env: Object.fromEntries(\n Object.entries(env).filter((pair): pair is [string, string] => pair[1] !== undefined),\n ),\n cols: input.columns ?? 80,\n rows: input.rows ?? 24,\n name: \"xterm-256color\",\n });\n pid = terminalProcess.pid;\n const output = new PassThrough();\n const errors = new PassThrough();\n const instance = terminalProcess;\n const dataSubscription = instance.onData((data) => output.write(data));\n const inputStream = new Writable({\n write(chunk: Buffer, _encoding, callback) {\n instance.write(chunk.toString());\n callback();\n },\n });\n const exitSubscription = instance.onExit((event) => {\n dataSubscription.dispose();\n exitSubscription.dispose();\n output.end();\n errors.end();\n inputStream.end();\n onExit(event.exitCode, event.signal ? String(event.signal) : null);\n });\n streams = { stdout: output, stderr: errors, stdin: inputStream };\n } else {\n const child = spawnChild(executable, args, {\n cwd: input.cwd,\n env,\n detached: process.platform !== \"win32\",\n windowsHide: true,\n stdio: \"pipe\",\n });\n child.on(\"error\", (error) => {\n spawnError = error;\n });\n // Finish on leader exit so cgroup cleanup can stop remaining members;\n // waiting for close could deadlock on a descendant holding a pipe open.\n child.on(\"exit\", onExit);\n if (!child.pid) throw new Error(\"Could not start process\");\n pid = child.pid;\n streams = { stdout: child.stdout, stderr: child.stderr, stdin: child.stdin };\n }\n let entry: RegistryEntry | undefined;\n try {\n const deadline = Date.now() + 5000;\n let current = await identity(pid, true, token);\n while (!current?.state.startsWith(\"T\") || current.token !== token) {\n if (spawnError) throw spawnError;\n if (exit || Date.now() >= deadline)\n throw new Error(\"Child failed to reach the resource setup gate\");\n await delay(10);\n current = await identity(pid, true, token);\n }\n if (current.group !== pid) throw new Error(\"Child did not create an isolated process group\");\n entry = {\n pid,\n owner: options.owner,\n type: input.kind,\n command: input.command,\n args: [...(input.args ?? [])],\n labels: { ...input.labels },\n startedAt: Date.now(),\n limits,\n enforcement: process.platform === \"win32\" ? \"rss-polling\" : \"rlimit-polling\",\n peakMemoryBytes: current.rss,\n token,\n start: current.start,\n gated: true,\n };\n entry.cgroup = await attachCgroup(pid, token, limits, options.cgroupRoot);\n if (entry.cgroup) entry.enforcement = \"cgroup-v2\";\n entries.set(pid, entry);\n await save();\n running = track(entry, streams);\n await signal(entry, \"SIGCONT\");\n delete entry.gated;\n await save();\n monitor(running);\n if (terminalProcess) {\n const instance = terminalProcess;\n return {\n ...running.handle,\n write: (data: string) => instance.write(data),\n resize: (columns: number, rows: number) => instance.resize(columns, rows),\n };\n }\n return running.handle;\n } catch (error) {\n // This PID was created by this call. Kill only after its random token is verified.\n const current = await identity(pid, true, token);\n if (current?.token === token) {\n const failed = { pid, token, start: current.start };\n if (await owns(failed)) {\n try {\n if (process.platform === \"win32\") await executeCommand(taskkillCommand(pid));\n else process.kill(-pid, \"SIGKILL\");\n } catch {\n /* Already exited. */\n }\n }\n }\n if (entry && !running) {\n entries.delete(pid);\n await save();\n }\n if (entry?.cgroup) await cleanCgroup(entry.cgroup);\n throw error;\n }\n }\n\n const api: ProcessSupervisor = {\n spawn: (input) => serial(() => launch(input, false)),\n spawnPty: (input) => serial(async () => (await launch(input, true)) as PtyHandle),\n list: (filter = {}) =>\n serial(async () => {\n validateFilter(filter);\n await open();\n return [...entries.values()].filter((entry) => matches(entry, filter)).map(info);\n }),\n stop: (filter = {}) =>\n serial(async () => {\n validateFilter(filter);\n await open();\n for (const entry of [...entries.values()].filter((entry) => matches(entry, filter)))\n await stopEntry(entry);\n }),\n stopPid: (pid) =>\n serial(async () => {\n await open();\n const entry = entries.get(pid);\n if (!entry) throw new ProcessNotOwnedError(pid);\n await stopEntry(entry);\n }),\n recover: (recovery = {}) =>\n serial(async () => {\n await open();\n const result: RecoveryResult = { adopted: [], stopped: [], cleared: [] };\n for (const entry of [...entries.values()]) {\n if (active.has(entry.pid)) continue;\n if (!(await owns(entry))) {\n entries.delete(entry.pid);\n result.cleared.push(entry.pid);\n continue;\n }\n // Never trust a persisted filesystem path for cgroup I/O after a restart.\n entry.cgroup = await recoveredCgroup(entry.pid, entry.token);\n entry.enforcement = entry.cgroup\n ? \"cgroup-v2\"\n : process.platform === \"win32\"\n ? \"rss-polling\"\n : \"rlimit-polling\";\n const keep = await recovery.keep?.(info(entry));\n if (keep) {\n const running = track(entry);\n monitor(running);\n if (entry.gated) {\n await signal(entry, \"SIGCONT\");\n delete entry.gated;\n }\n result.adopted.push(entry.pid);\n } else {\n await stopEntry(entry);\n result.stopped.push(entry.pid);\n }\n }\n await save();\n return result;\n }),\n stopAll() {\n shutdown ??= serial(async () => {\n await open();\n closing = true;\n try {\n const errors: unknown[] = [];\n for (const entry of [...entries.values()]) {\n try {\n if (await owns(entry)) await stopEntry(entry);\n else {\n const running = active.get(entry.pid);\n if (running) await running.finish(null, null);\n else entries.delete(entry.pid);\n }\n } catch (error) {\n errors.push(error);\n }\n }\n await save();\n if (errors.length)\n throw new AggregateError(errors, \"Some owned processes could not be stopped\");\n } finally {\n await registry.close();\n }\n });\n return shutdown;\n },\n };\n return api;\n}\n"],"mappings":"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"}
|
package/llms-full.txt
CHANGED
|
@@ -132,4 +132,26 @@ PTY stdout combines terminal output; stderr is empty. `stdin` is writable too.
|
|
|
132
132
|
Platform source references and delegation details:
|
|
133
133
|
https://warlock.js.org/v/latest/process/.
|
|
134
134
|
|
|
135
|
+
## Configuration validation and guardian credentials
|
|
136
|
+
|
|
137
|
+
`supervisor()` requires nonempty string `owner` and `registry` options. Missing,
|
|
138
|
+
null or invalid configuration throws `TypeError` immediately, naming the first
|
|
139
|
+
invalid option (owner before registry). Validate deployment configuration before
|
|
140
|
+
starting work:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import { supervisor } from "@warlock.js/process";
|
|
144
|
+
|
|
145
|
+
const processes = supervisor({
|
|
146
|
+
owner: "test-workers",
|
|
147
|
+
registry: "storage/test-workers.json",
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
On macOS and Windows, only the guardian receives `WARLOCK_PROCESS_TOKEN` and
|
|
152
|
+
`WARLOCK_PROCESS_LAUNCH`. It authenticates ownership over its socket/named pipe;
|
|
153
|
+
both private variables are removed before spawning the workload (case-insensitively
|
|
154
|
+
on Windows). Ordinary environment entries reach the workload. Linux continues
|
|
155
|
+
to forward the token because ownership is checked through `/proc/<pid>/environ`.
|
|
156
|
+
|
|
135
157
|
|
package/package.json
CHANGED
|
@@ -123,3 +123,25 @@ PTY stdout combines terminal output; stderr is empty. `stdin` is writable too.
|
|
|
123
123
|
|
|
124
124
|
Platform source references and delegation details:
|
|
125
125
|
https://warlock.js.org/v/latest/process/.
|
|
126
|
+
|
|
127
|
+
## Configuration validation and guardian credentials
|
|
128
|
+
|
|
129
|
+
`supervisor()` requires nonempty string `owner` and `registry` options. Missing,
|
|
130
|
+
null or invalid configuration throws `TypeError` immediately, naming the first
|
|
131
|
+
invalid option (owner before registry). Validate deployment configuration before
|
|
132
|
+
starting work:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import { supervisor } from "@warlock.js/process";
|
|
136
|
+
|
|
137
|
+
const processes = supervisor({
|
|
138
|
+
owner: "test-workers",
|
|
139
|
+
registry: "storage/test-workers.json",
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
On macOS and Windows, only the guardian receives `WARLOCK_PROCESS_TOKEN` and
|
|
144
|
+
`WARLOCK_PROCESS_LAUNCH`. It authenticates ownership over its socket/named pipe;
|
|
145
|
+
both private variables are removed before spawning the workload (case-insensitively
|
|
146
|
+
on Windows). Ordinary environment entries reach the workload. Linux continues
|
|
147
|
+
to forward the token because ownership is checked through `/proc/<pid>/environ`.
|