@smthrs/platform-node 1.0.0-rc.3 → 1.0.0-rc.4
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/bin/darwin-arm64/smithers-jj-export +0 -0
- package/bin/darwin-x64/smithers-jj-export +0 -0
- package/bin/linux-arm64/smithers-jj-export +0 -0
- package/bin/linux-x64/smithers-jj-export +2 -2
- package/bin/win32-x64/smithers-jj-export.exe +0 -0
- package/dist/cjs/ProcessConfinement.d.ts.map +1 -1
- package/dist/cjs/ProcessConfinement.js +6 -1
- package/dist/cjs/ProcessConfinement.js.map +2 -2
- package/dist/cjs/ProcessSandbox.d.ts +16 -2
- package/dist/cjs/ProcessSandbox.d.ts.map +1 -1
- package/dist/cjs/ProcessSandbox.js +17 -2
- package/dist/cjs/ProcessSandbox.js.map +2 -2
- package/dist/cjs/ScopedProcess.d.ts +2 -1
- package/dist/cjs/ScopedProcess.d.ts.map +1 -1
- package/dist/cjs/ScopedProcess.js.map +2 -2
- package/dist/cjs/internal/ProcessSupervisor.d.ts +2 -0
- package/dist/cjs/internal/ProcessSupervisor.d.ts.map +1 -1
- package/dist/cjs/internal/ProcessSupervisor.js +5 -2
- package/dist/cjs/internal/ProcessSupervisor.js.map +2 -2
- package/dist/cjs/internal/SupervisorProgram.d.ts.map +1 -1
- package/dist/cjs/internal/SupervisorProgram.js +23 -3
- package/dist/cjs/internal/SupervisorProgram.js.map +2 -2
- package/dist/esm/ProcessConfinement.d.ts.map +1 -1
- package/dist/esm/ProcessConfinement.js.map +1 -1
- package/dist/esm/ProcessSandbox.d.ts +16 -2
- package/dist/esm/ProcessSandbox.d.ts.map +1 -1
- package/dist/esm/ProcessSandbox.js +33 -4
- package/dist/esm/ProcessSandbox.js.map +1 -1
- package/dist/esm/ScopedProcess.d.ts +2 -1
- package/dist/esm/ScopedProcess.d.ts.map +1 -1
- package/dist/esm/ScopedProcess.js +2 -1
- package/dist/esm/ScopedProcess.js.map +1 -1
- package/dist/esm/internal/ProcessSupervisor.d.ts +2 -0
- package/dist/esm/internal/ProcessSupervisor.d.ts.map +1 -1
- package/dist/esm/internal/ProcessSupervisor.js +9 -2
- package/dist/esm/internal/ProcessSupervisor.js.map +1 -1
- package/dist/esm/internal/SupervisorProgram.d.ts.map +1 -1
- package/dist/esm/internal/SupervisorProgram.js +23 -3
- package/dist/esm/internal/SupervisorProgram.js.map +1 -1
- package/docs/api.md +2 -1
- package/docs/concepts/process-containment.md +8 -0
- package/package.json +4 -4
- package/src/ProcessConfinement.ts +10 -3
- package/src/ProcessSandbox.ts +38 -4
- package/src/ScopedProcess.ts +2 -1
- package/src/internal/ProcessSupervisor.ts +8 -2
- package/src/internal/SupervisorProgram.ts +23 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/internal/ProcessSupervisor.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Private parent connection for a POSIX process owner. Effect retains ownership\n * of the caller's streams; separate sockets carry requests and lifetime status.\n * @since 1.0.0\n */\n\nimport type { Lifecycle } from \"@smthrs/kernel/ContainedSpawner\"\nimport * as Channel from \"effect/Channel\"\nimport * as Clock from \"effect/Clock\"\nimport * as Effect from \"effect/Effect\"\nimport * as PlatformError from \"effect/PlatformError\"\nimport * as Sink from \"effect/Sink\"\nimport * as Stream from \"effect/Stream\"\nimport * as ChildProcess from \"effect/unstable/process/ChildProcess\"\nimport { type ChildProcessHandle, ExitCode, makeHandle } from \"effect/unstable/process/ChildProcessSpawner\"\nimport { randomBytes } from \"node:crypto\"\nimport { chmodSync, mkdtempSync, rmdirSync, rmSync } from \"node:fs\"\nimport { createConnection, createServer, type Socket } from \"node:net\"\nimport { tmpdir } from \"node:os\"\nimport { join, parse, resolve } from \"node:path\"\nimport * as Tls from \"node:tls\"\nimport { standardFdsOf } from \"./PipedProcess.ts\"\nimport type { Policy, Snapshot, System } from \"./ProcessCleanup.ts\"\nimport { NativeProcessError, processFault } from \"./ProcessFault.ts\"\nimport { source } from \"./SupervisorProgram.ts\"\nimport { resolveJobExecutable, WindowsProcessJob } from \"./WindowsProcessJob.ts\"\n\nconst startupMs = 5000\nconst deliveryMs = 500\nconst exitAllowanceMs = 2500\n// Host-wide process observations can exceed a socket-delivery deadline on a\n// loaded machine. Keep verification bounded without mistaking that delay for\n// an unclean exit; an unknown observation still never counts as success.\nconst verificationMs = 2500\nconst targets = new WeakMap<ChildProcessHandle, Control>()\n// Native owners and sockets keep running when the caller freezes its Clock.\n// Their delivery bounds and cleanup retries must keep running with them.\nconst processClock = Clock.Clock.defaultValue()\n\n/**\n * Actual service pid, distinct from the owner recorded by the host.\n * @private\n * @since 1.0.0\n */\nexport const targetPidOf = (handle: ChildProcessHandle): number | undefined => targets.get(handle)?.targetPid\n\n/** Native error fields remain data on the public PlatformError cause. */\nconst nativeError = (cause: unknown): Error & { code?: string | undefined; syscall?: string | undefined } => {\n if (cause instanceof Error) return cause\n const value = (cause ?? {}) as Record<string, unknown>\n const text = (field: string): string | undefined => typeof value[field] === \"string\" ? value[field] : undefined\n return new NativeProcessError({\n message: text(\"message\") ?? \"The process supervisor failed\",\n code: text(\"code\"),\n errno: typeof value.errno === \"number\" ? value.errno : undefined,\n reason: text(\"reason\"),\n syscall: text(\"syscall\"),\n path: text(\"path\"),\n signal: text(\"signal\")\n })\n}\n\n/**\n * Preserve the platform's errno and signal vocabulary across the private wire.\n * @private\n * @since 1.0.0\n */\nexport const failure = (method: string, command: string, cause: unknown): PlatformError.PlatformError => {\n const error = nativeError(cause)\n const tags: Readonly<Record<string, PlatformError.SystemErrorTag>> = {\n ENOENT: \"NotFound\",\n EACCES: \"PermissionDenied\",\n EEXIST: \"AlreadyExists\",\n EISDIR: \"BadResource\",\n ENOTDIR: \"BadResource\",\n ELOOP: \"BadResource\",\n EBUSY: \"Busy\"\n }\n return PlatformError.systemError({\n _tag: tags[error.code ?? \"\"] ?? \"Unknown\",\n module: \"ChildProcess\",\n method,\n pathOrDescriptor: command,\n syscall: error.syscall,\n description: error.message,\n cause: error\n })\n}\n\n/**\n * The observation behind an unverified cleanup, in the message itself: a\n * failure report usually shows only the message, and whether the receipt, the\n * kernel or the snapshot was missing decides host load versus a live survivor.\n * @private\n * @since 1.0.0\n */\nexport const unverifiedCleanup = (\n control: {\n readonly fault: unknown\n readonly cleanupFailed: boolean\n readonly cleanupAcknowledged: boolean\n readonly targetDone: boolean\n },\n cleanupRequired: boolean,\n vacant: boolean,\n observed: Snapshot | undefined\n): string =>\n [\n `target exited: ${control.targetDone}`,\n `cleanup receipt: ${\n control.cleanupFailed\n ? \"failed\"\n : !cleanupRequired\n ? \"not required\"\n : control.cleanupAcknowledged\n ? \"acknowledged\"\n : \"missing\"\n }`,\n `group vacant: ${vacant}`,\n observed === undefined\n ? \"group snapshot: unavailable\"\n : `group members: ${\n observed.members.length === 0\n ? \"none\"\n : observed.members.map((member) => `${member.pid} ${member.zombie ? \"zombie\" : \"live\"}`).join(\", \")\n }; host group ${observed.ownGroup}`,\n ...(control.fault === undefined\n ? []\n : [`fault: ${String((control.fault as { message?: unknown })?.message ?? control.fault)}`])\n ].join(\"; \")\n\nconst promise = <A>() => {\n let resolve!: (value: A) => void\n let reject!: (cause: unknown) => void\n const promise = new Promise<A>((yes, no) => {\n resolve = yes\n reject = no\n })\n // A status may arrive before its Effect consumer starts waiting.\n void promise.catch(() => {})\n return { promise, resolve, reject }\n}\n\nconst wait = <A>(value: Promise<A>, method: string, command: string) =>\n Effect.tryPromise({ try: () => value, catch: (cause) => failure(method, command, cause) })\n\nconst bounded = <A, E, R>(effect: Effect.Effect<A, E, R>, millis: number, method: string, command: string) =>\n effect.pipe(\n Effect.timeoutOrElse({\n duration: millis,\n orElse: () =>\n Effect.fail(\n failure(method, command, processFault(\"supervisor_timeout\", `Process supervisor ${method} timed out`))\n )\n }),\n Effect.provideService(Clock.Clock, processClock)\n )\n\n/**\n * Standalone application executables do not implement the runtime's eval CLI.\n * Refuse them before spawning rather than recursively launching the application.\n * @private\n * @since 1.0.0\n */\nexport const bootstrapArguments = (runtime: { readonly bun: boolean; readonly main: string; readonly sea: boolean }) =>\n runtime.sea || runtime.main.startsWith(\"/$bunfs/\")\n ? Effect.fail(\n failure(\n \"spawn\",\n process.execPath,\n processFault(\n \"runtime_unsupported\",\n \"Process supervision requires a Node or Bun runtime executable, not a compiled application\"\n )\n )\n )\n : Effect.succeed(runtime.bun ? [\"--no-env-file\", \"--config=/dev/null\"] : [])\n\nconst channelEnvironment = \"SMITHERS_PROCESS_CHANNEL\"\nconst tlsOptions = { minVersion: \"TLSv1.3\", maxVersion: \"TLSv1.3\", ciphers: \"TLS_AES_128_GCM_SHA256\" } as const\n\n/**\n * Connect an owner to its authenticated private channel.\n * @private\n * @since 1.0.0\n */\nexport const connectOwner = (path: string, environment: Readonly<Record<string, string | undefined>>): Socket => {\n const encoded = environment[channelEnvironment]\n if (encoded === undefined) return createConnection(path)\n const channel = JSON.parse(encoded) as { key: string; status: number; requests: number }\n return Tls.connect({\n ...tlsOptions,\n host: \"127.0.0.1\",\n port: path.endsWith(\"/r\") ? channel.requests : channel.status,\n pskCallback: () => ({ identity: \"smithers-owner\", psk: Buffer.from(channel.key, \"hex\") })\n })\n}\n\n/**\n * Kept separate from Effect scopes so native socket callbacks only settle\n * promises; none can start an unowned fiber or signal an observed process id.\n * @private\n * @since 1.0.0\n */\nexport class Control {\n readonly ready = promise<number>()\n readonly started = promise<void>()\n readonly exited = promise<ExitCode>()\n readonly lost = promise<never>()\n readonly ended = promise<void>()\n readonly requestsReady = promise<void>()\n readonly directory: string\n readonly path: string\n readonly requestPath: string\n readonly server\n readonly requestServer\n readonly listening: Promise<void>\n private readonly key: Buffer | undefined\n private readonly connections = new Set<Socket>()\n socket: Socket | undefined\n requestSocket: Socket | undefined\n targetDone = false\n ownerCreation: unknown\n targetPid: number | undefined\n ownerDone = false\n spawnFailed = false\n cleanupFailed = false\n cleanupAcknowledged = false\n activationSent = false\n closeSent = false\n fault: unknown\n onTargetExit: () => void = () => {}\n onCleanup: () => void = () => {}\n private receivedReady = false\n private receivedStarted = false\n private withdrawn = false\n\n constructor(transport: \"native\" | \"tls\" = \"native\") {\n this.key = transport === \"tls\" || process.platform === \"win32\" ? randomBytes(32) : undefined\n this.directory = mkdtempSync(this.key === undefined ? \"/tmp/sm-p-\" : join(tmpdir(), \"sm-p-\"))\n this.path = `${this.directory}/s`\n this.requestPath = `${this.directory}/r`\n const servers: Array<ReturnType<typeof createServer>> = []\n try {\n chmodSync(this.directory, 0o700)\n this.server = this.makeServer((socket) => this.accept(socket))\n servers.push(this.server)\n this.requestServer = this.makeServer((socket) => this.acceptRequests(socket))\n servers.push(this.requestServer)\n this.listening = Promise.all([\n new Promise<void>((resolve, reject) => {\n this.server.once(\"error\", reject)\n if (this.key === undefined) this.server.listen(this.path, resolve)\n else this.server.listen(0, \"127.0.0.1\", resolve)\n }),\n new Promise<void>((resolve, reject) => {\n this.requestServer.once(\"error\", reject)\n if (this.key === undefined) this.requestServer.listen(this.requestPath, resolve)\n else this.requestServer.listen(0, \"127.0.0.1\", resolve)\n })\n ]).then(() => {})\n } catch (cause) {\n for (const server of servers) server.close()\n rmSync(this.directory, { recursive: true, force: true })\n throw cause\n }\n }\n\n private makeServer(accept: (socket: Socket) => void): ReturnType<typeof createServer> {\n if (this.key === undefined) return createServer(accept)\n const server = Tls.createServer({\n ...tlsOptions,\n handshakeTimeout: startupMs,\n pskCallback: (_socket, identity) => identity === \"smithers-owner\" ? this.key! : null\n }, accept)\n server.on(\"connection\", (socket) => {\n this.connections.add(socket)\n socket.once(\"close\", () => this.connections.delete(socket))\n })\n server.on(\"tlsClientError\", (_error, socket) => socket.destroy())\n return server\n }\n\n /** Only the trusted owner receives this environment; target configuration replaces it. */\n environment(): Readonly<Record<string, string>> {\n if (this.key === undefined) return {}\n const status = this.server.address() as { port: number }\n const requests = this.requestServer.address() as { port: number }\n return {\n [channelEnvironment]: JSON.stringify({\n key: this.key.toString(\"hex\"),\n status: status.port,\n requests: requests.port\n })\n }\n }\n\n /** The test peer uses the same authenticated transport as the isolated owner. */\n connect(requests = false): Socket {\n return connectOwner(requests ? this.requestPath : this.path, this.environment())\n }\n\n /** Validate the stored READY after the raw spawn effect has returned its pid. */\n withdraw(pid: number, actual: number): void {\n if (pid !== actual) throw processFault(\"identity_mismatch\", \"Wrong supervisor identity\")\n this.server.close()\n this.server.unref()\n this.requestServer.close()\n this.requestServer.unref()\n // Node may unlink the owned socket synchronously inside server.close().\n rmSync(this.path, { force: true })\n rmSync(this.requestPath, { force: true })\n rmdirSync(this.directory)\n this.withdrawn = true\n }\n\n /** Request EOF asks for cleanup; leave its independent status reader intact. */\n disconnect(): void {\n this.requestSocket?.destroy()\n }\n\n dispose(): void {\n for (const socket of this.connections) socket.destroy()\n this.disconnect()\n this.socket?.destroy()\n this.server.close()\n this.requestServer.close()\n if (!this.withdrawn) rmSync(this.directory, { recursive: true, force: true })\n }\n\n rawEnded(): void {\n this.ownerDone = true\n // An accepted socket may still have buffered status frames. Its close event\n // rejects missing outcomes only after Node has delivered those frames.\n if (this.socket === undefined) this.closed()\n }\n\n write(message: unknown): Promise<void> {\n return new Promise((resolve, reject) => {\n const socket = this.requestSocket\n if (socket === undefined || socket.destroyed || !socket.writable) {\n reject(processFault(\"control_channel_closed\", \"Private process control channel closed\"))\n return\n }\n const data = `${JSON.stringify(message)}\\n`\n if (Buffer.byteLength(data) > 4 * 1024 * 1024) {\n reject(processFault(\"configuration_too_large\", \"Process configuration exceeds the private frame limit\"))\n return\n }\n socket.write(data, (error) => error ? reject(error) : resolve())\n })\n }\n\n private closed(): void {\n this.disconnect()\n const cause = this.fault ??\n processFault(\"outcome_missing\", \"Process supervisor closed before reporting its outcome\")\n if (this.activationSent && !this.targetDone && !this.spawnFailed) this.lost.reject(cause)\n this.ready.reject(cause)\n this.requestsReady.reject(cause)\n this.started.reject(cause)\n this.exited.reject(cause)\n this.ended.resolve()\n }\n\n private acceptRequests(socket: Socket): void {\n if (this.requestSocket !== undefined) {\n socket.destroy()\n return\n }\n this.requestSocket = socket\n // A late write may fail after the owner has sent its last status frames.\n // Destroying this socket must never discard those frames on the reader.\n socket.on(\"error\", (cause) => {\n this.fault = cause\n })\n this.requestsReady.resolve()\n }\n\n private accept(socket: Socket): void {\n if (this.socket !== undefined) {\n socket.destroy()\n return\n }\n this.socket = socket\n let buffer = \"\"\n socket.setEncoding(\"utf8\")\n socket.on(\"data\", (data: string) => {\n try {\n buffer += data\n if (Buffer.byteLength(buffer) > 16 * 1024) {\n throw processFault(\"status_too_large\", \"Process status frame exceeds its limit\")\n }\n for (;;) {\n const end = buffer.indexOf(\"\\n\")\n if (end < 0) break\n const line = buffer.slice(0, end)\n buffer = buffer.slice(end + 1)\n this.receive(JSON.parse(line))\n }\n } catch (cause) {\n this.fault = cause\n socket.destroy()\n }\n })\n socket.on(\"error\", (cause) => {\n this.fault = cause\n })\n socket.once(\"close\", () => this.closed())\n }\n\n private receive(value: unknown): void {\n if (typeof value !== \"object\" || value === null) throw processFault(\"status_invalid\", \"Invalid process status\")\n const message = value as Record<string, unknown>\n switch (message.type) {\n case \"ready\":\n if (\n this.receivedReady || message.version !== 1 || !Number.isSafeInteger(message.pid) || Number(message.pid) <= 1\n ) {\n throw processFault(\"status_invalid\", \"Invalid process readiness\")\n }\n this.receivedReady = true\n this.ownerCreation = message.created\n this.ready.resolve(Number(message.pid))\n return\n case \"spawned\":\n if (\n !this.activationSent || this.receivedStarted || !Number.isSafeInteger(message.pid) || Number(message.pid) <= 1\n ) {\n throw processFault(\"status_invalid\", \"Invalid target startup\")\n }\n this.receivedStarted = true\n this.targetPid = Number(message.pid)\n this.started.resolve()\n return\n case \"spawn_error\":\n if (!this.activationSent || this.receivedStarted) {\n throw processFault(\"status_invalid\", \"Invalid target spawn failure\")\n }\n this.spawnFailed = true\n this.fault = message\n this.started.reject(message)\n this.exited.reject(message)\n return\n case \"fault\":\n this.fault = message\n this.started.reject(message)\n this.exited.reject(message)\n return\n case \"exit\":\n if (\n !this.receivedStarted || this.targetDone ||\n !(Number.isInteger(message.code) && Number(message.code) >= 0 && message.signal === null ||\n message.code === null && typeof message.signal === \"string\" && /^SIG[A-Z0-9]+$/.test(message.signal))\n ) {\n throw processFault(\"status_invalid\", \"Invalid target exit status\")\n }\n this.targetDone = true\n if (message.code === null) {\n this.exited.reject({\n message: `Process interrupted due to receipt of signal: '${message.signal}'`,\n signal: message.signal\n })\n } else this.exited.resolve(ExitCode(Number(message.code)))\n this.onTargetExit()\n return\n case \"cleanup_error\":\n this.cleanupFailed = true\n this.fault = message\n return\n case \"cleanup\":\n this.cleanupAcknowledged = true\n this.onCleanup()\n return\n default:\n throw processFault(\"status_unknown\", \"Unknown process status\")\n }\n }\n}\n\n/**\n * Prepare a private, independently bounded process owner before target launch.\n * @private\n * @since 1.0.0\n */\nexport const prepare = (\n system: System,\n policy: (\n options: ChildProcess.KillOptions,\n defaults?: ChildProcess.KillOptions\n ) => Effect.Effect<Policy, PlatformError.PlatformError>,\n transport: \"native\" | \"tls\" = \"native\"\n): Lifecycle =>\n(command, spawn) =>\n Effect.gen(function*() {\n const initial = yield* policy(command.options)\n const bun = process.versions.bun !== undefined\n const sea = bun ? false : (yield* Effect.tryPromise({\n try: () => import(\"node:sea\"),\n catch: (cause) => failure(\"spawn\", process.execPath, cause)\n })).isSea()\n const bootstrap = yield* bootstrapArguments({\n bun,\n sea,\n main: (globalThis as { readonly Bun?: { readonly main?: string } }).Bun?.main ?? \"\"\n })\n const windows = system.platform === \"win32\"\n const grouped = !windows && (command.options.detached ?? true)\n const jobExecutable = windows ?\n yield* Effect.try({\n try: resolveJobExecutable,\n catch: (cause) => failure(\"spawn\", command.command, cause)\n }) :\n undefined\n const control = yield* Effect.acquireRelease(\n Effect.try({ try: () => new Control(transport), catch: (cause) => failure(\"spawn\", command.command, cause) }),\n (control) => Effect.sync(() => control.dispose())\n )\n yield* bounded(wait(control.listening, \"spawn\", command.command), startupMs, \"spawn\", command.command)\n const raw = yield* spawn(ChildProcess.make(process.execPath, [\n ...bootstrap,\n \"-e\",\n source,\n control.path,\n grouped ? \"group\" : \"direct\"\n ], {\n ...command.options,\n cwd: parse(process.execPath).root,\n env: {\n PATH: \"/usr/bin:/bin\",\n HOME: \"/\",\n XDG_CONFIG_HOME: \"/\",\n BUN_RUNTIME_TRANSPILER_CACHE_PATH: \"0\",\n ...control.environment(),\n ...(jobExecutable === undefined ? {} : { SMITHERS_PROCESS_JOB_HELPER: jobExecutable })\n },\n extendEnv: false,\n shell: false,\n detached: grouped,\n killSignal: \"SIGTERM\",\n forceKillAfter: initial.graceMs\n }))\n yield* Effect.exit(raw.exitCode).pipe(Effect.andThen(Effect.sync(() => control.rawEnded())), Effect.forkScoped)\n let job: WindowsProcessJob | undefined\n let settled = false\n let referenced = true\n let reref: Effect.Effect<void, PlatformError.PlatformError> = Effect.void\n let selected = initial\n let requireCleanupReceipt = false\n const snapshot = () => system.snapshot(raw.pid)\n const alone = () => {\n if (!grouped) return control.targetDone\n const observed = snapshot()\n if (observed === undefined || observed.ownGroup === raw.pid) return false\n const running = observed.members.filter((member) => !member.zombie)\n return running.length === 1 && running[0]!.pid === raw.pid\n }\n // This observation only shortens the owner's deadline. The live owner makes\n // the signal, so neither a stale observation nor a reused pid grants a kill.\n control.onTargetExit = () => {\n // An explicit stop already owns its deadline and escaped-child sweep.\n // Natural exit must not replace that policy with a second fast stop.\n if (control.closeSent) return\n if (!alone()) return\n control.closeSent = true\n void control.write({ type: \"stop\", killSignal: \"SIGKILL\", fast: true }).catch(() => control.disconnect())\n }\n const finish = yield* Effect.cached(Effect.gen(function*() {\n yield* bounded(Effect.exit(raw.exitCode), selected.graceMs + exitAllowanceMs, \"kill\", command.command).pipe(\n Effect.catch((cause) =>\n job === undefined ? Effect.fail(cause) : Effect.gen(function*() {\n // The native guardian can terminate a stalled owner independently.\n job!.stop()\n yield* bounded(Effect.exit(raw.exitCode), exitAllowanceMs, \"kill\", command.command)\n })\n )\n )\n if (job !== undefined) {\n yield* bounded(wait(job.settled.promise, \"kill\", command.command), 5500, \"kill\", command.command)\n settled = true\n return\n }\n yield* bounded(wait(control.ended.promise, \"kill\", command.command), deliveryMs, \"kill\", command.command).pipe(\n Effect.ignore\n )\n const deadline = Date.now() + verificationMs\n for (;;) {\n // The kernel's ESRCH proves the group empty with no fork. A `ps`\n // snapshot can go unanswered on a loaded host, so it only decides what\n // the kernel cannot: a group still holding zombies, or an unknown one.\n // A live member keeps both answers false. This host is alive, so a\n // vacant group is never this host's own group.\n const vacant = grouped && system.vacant(raw.pid)\n const observed = grouped && !vacant ? snapshot() : undefined\n settled = !control.cleanupFailed && (!requireCleanupReceipt || control.cleanupAcknowledged) && (grouped\n ? vacant ||\n observed !== undefined && observed.ownGroup !== raw.pid && observed.members.every((member) => member.zombie)\n : control.targetDone || control.spawnFailed || !control.activationSent)\n if (settled) return\n if (Date.now() >= deadline) {\n return yield* Effect.fail(\n failure(\n \"kill\",\n command.command,\n processFault(\n \"cleanup_unverified\",\n \"Process cleanup could not be verified; its ledger record is retained \" +\n `(${unverifiedCleanup(control, requireCleanupReceipt, vacant, observed)})`,\n {\n cause: {\n fault: control.fault,\n cleanupFailed: control.cleanupFailed,\n cleanupRequired: requireCleanupReceipt,\n cleanupAcknowledged: control.cleanupAcknowledged,\n targetDone: control.targetDone,\n groupVacant: vacant,\n ownerObserved: observed !== undefined,\n members: observed?.members\n }\n }\n )\n )\n )\n }\n yield* Effect.sleep(10).pipe(Effect.provideService(Clock.Clock, processClock))\n }\n }))\n const kill = (options: ChildProcess.KillOptions = {}) =>\n Effect.gen(function*() {\n const requested = yield* policy(options, command.options)\n if (!referenced) {\n job?.reference(true)\n control.socket?.ref()\n control.requestSocket?.ref()\n yield* reref\n referenced = true\n }\n if (!control.closeSent) {\n control.closeSent = true\n selected = requested\n // An empty original group cannot prove an explicit escaped-descendant\n // sweep succeeded if its status frames were lost. Startup and natural\n // completion have no such additional contract.\n requireCleanupReceipt = grouped && control.activationSent && !control.spawnFailed && !control.targetDone\n yield* bounded(\n wait(\n control.write({\n type: \"stop\",\n explicit: true,\n ...requested,\n // A live target can move out of this group with setsid().\n // Only an owner whose target was never activated can replace\n // the requested signal; natural exit has its own fast path.\n killSignal: !control.activationSent && alone() ? \"SIGKILL\" : requested.killSignal\n }),\n \"kill\",\n command.command\n ),\n deliveryMs,\n \"kill\",\n command.command\n )\n .pipe(Effect.catch(() => Effect.sync(() => control.disconnect())))\n }\n yield* finish\n }).pipe(Effect.uninterruptible)\n yield* Effect.addFinalizer(() =>\n kill().pipe(\n // A stuck owner keeps its ledger entry. Disable the underlying spawner's\n // unconditional group-kill finalizer rather than signal a stale identity.\n Effect.ensuring(\n Effect.suspend(() =>\n settled ? Effect.void : Effect.andThen(\n raw.unref,\n Effect.sync(() => {\n job?.stop()\n job?.reference(false)\n control.disconnect()\n })\n )\n ).pipe(Effect.orDie)\n ),\n Effect.orDie\n )\n )\n const ready = yield* bounded(\n wait(control.ready.promise, \"spawn\", command.command),\n startupMs,\n \"spawn\",\n command.command\n )\n yield* bounded(wait(control.requestsReady.promise, \"spawn\", command.command), startupMs, \"spawn\", command.command)\n yield* Effect.try({\n try: () => control.withdraw(ready, raw.pid),\n catch: (cause) => failure(\"spawn\", command.command, cause)\n })\n if (windows) {\n job = yield* Effect.try({\n try: () => new WindowsProcessJob(raw.pid, control.ownerCreation, jobExecutable!),\n catch: (cause) => failure(\"spawn\", command.command, cause)\n })\n yield* bounded(wait(job.ready.promise, \"spawn\", command.command), startupMs, \"spawn\", command.command)\n }\n const acknowledgeCleanup = windows || transport === \"tls\"\n if (acknowledgeCleanup) {\n control.onCleanup = () => {\n if (!control.ownerDone) {\n void control.write({ type: \"cleanup_ack\" }).catch(() => control.disconnect())\n }\n }\n }\n const options = command.options\n // Match Effect/Node's undefined-vs-empty environment semantics in the host,\n // before replacing the helper's environment with its isolated bootstrap.\n const env = options.extendEnv ? { ...process.env, ...options.env } : options.env ?? { ...process.env }\n yield* bounded(\n wait(\n control.write({\n type: \"configure\",\n acknowledgeCleanup,\n command: command.command,\n args: command.args,\n cwd: resolve(options.cwd ?? process.cwd()),\n env,\n shell: options.shell,\n windowsHide: options.windowsHide ?? true,\n windowsVerbatimArguments:\n (options as ChildProcess.CommandOptions & { readonly windowsVerbatimArguments?: boolean })\n .windowsVerbatimArguments,\n standardFds: standardFdsOf(raw),\n userFds: [\n ...new Set(\n Object.keys(options.additionalFds ?? {}).map(ChildProcess.parseFdName)\n .filter((fd): fd is number => fd !== undefined)\n )\n ],\n ...initial\n }),\n \"spawn\",\n command.command\n ),\n deliveryMs,\n \"spawn\",\n command.command\n )\n const activate = yield* Effect.cached(Effect.gen(function*() {\n control.activationSent = true\n yield* bounded(\n wait(control.write({ type: \"start\" }), \"spawn\", command.command),\n deliveryMs,\n \"spawn\",\n command.command\n )\n yield* bounded(wait(control.started.promise, \"spawn\", command.command), startupMs, \"spawn\", command.command)\n }))\n const unref = Effect.gen(function*() {\n if (referenced) {\n reref = yield* raw.unref\n job?.reference(false)\n control.socket?.unref()\n control.requestSocket?.unref()\n referenced = false\n }\n return Effect.gen(function*() {\n if (!referenced) {\n job?.reference(true)\n control.socket?.ref()\n control.requestSocket?.ref()\n yield* reref\n referenced = true\n }\n })\n })\n const output = <A>(stream: Stream.Stream<A, PlatformError.PlatformError>) =>\n stream.pipe(\n Stream.interruptWhen(wait(control.lost.promise, \"read\", command.command))\n )\n const input = <A>(sink: Sink.Sink<A, Uint8Array, never, PlatformError.PlatformError>) =>\n Sink.fromChannel(\n Sink.toChannel(sink).pipe(Channel.interruptWhen(wait(control.lost.promise, \"write\", command.command)))\n )\n const handle = makeHandle({\n ...raw,\n exitCode: wait(control.exited.promise, \"exitCode\", command.command),\n isRunning: Effect.sync(() => !control.targetDone && !control.ownerDone),\n stdin: input(raw.stdin),\n getInputFd: (fd) => input(raw.getInputFd(fd)),\n stdout: output(raw.stdout),\n stderr: output(raw.stderr),\n all: output(raw.all),\n getOutputFd: (fd) => output(raw.getOutputFd(fd)),\n kill,\n unref\n })\n targets.set(handle, control)\n return {\n handle,\n activate,\n settled: Effect.sync(() => settled)\n }\n })\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Private parent connection for a POSIX process owner. Effect retains ownership\n * of the caller's streams; separate sockets carry requests and lifetime status.\n * @since 1.0.0\n */\n\nimport type { Lifecycle } from \"@smthrs/kernel/ContainedSpawner\"\nimport * as Channel from \"effect/Channel\"\nimport * as Clock from \"effect/Clock\"\nimport * as Effect from \"effect/Effect\"\nimport * as PlatformError from \"effect/PlatformError\"\nimport * as Sink from \"effect/Sink\"\nimport * as Stream from \"effect/Stream\"\nimport * as ChildProcess from \"effect/unstable/process/ChildProcess\"\nimport { type ChildProcessHandle, ExitCode, makeHandle } from \"effect/unstable/process/ChildProcessSpawner\"\nimport { randomBytes } from \"node:crypto\"\nimport { chmodSync, mkdtempSync, rmdirSync, rmSync } from \"node:fs\"\nimport { createConnection, createServer, type Socket } from \"node:net\"\nimport { tmpdir } from \"node:os\"\nimport { join, parse, resolve } from \"node:path\"\nimport * as Tls from \"node:tls\"\nimport { standardFdsOf } from \"./PipedProcess.ts\"\nimport type { Policy, Snapshot, System } from \"./ProcessCleanup.ts\"\nimport { NativeProcessError, processFault } from \"./ProcessFault.ts\"\nimport { source } from \"./SupervisorProgram.ts\"\nimport { resolveJobExecutable, WindowsProcessJob } from \"./WindowsProcessJob.ts\"\n\nconst startupMs = 5000\nconst deliveryMs = 500\nconst exitAllowanceMs = 2500\n// Host-wide process observations can exceed a socket-delivery deadline on a\n// loaded machine. Keep verification bounded without mistaking that delay for\n// an unclean exit; an unknown observation still never counts as success.\nconst verificationMs = 2500\nconst targets = new WeakMap<ChildProcessHandle, Control>()\n// Native owners and sockets keep running when the caller freezes its Clock.\n// Their delivery bounds and cleanup retries must keep running with them.\nconst processClock = Clock.Clock.defaultValue()\n\n/**\n * Actual service pid, distinct from the owner recorded by the host.\n * @private\n * @since 1.0.0\n */\nexport const targetPidOf = (handle: ChildProcessHandle): number | undefined => targets.get(handle)?.targetPid\n\n/** Native error fields remain data on the public PlatformError cause. */\nconst nativeError = (cause: unknown): Error & { code?: string | undefined; syscall?: string | undefined } => {\n if (cause instanceof Error) return cause\n const value = (cause ?? {}) as Record<string, unknown>\n const text = (field: string): string | undefined => typeof value[field] === \"string\" ? value[field] : undefined\n return new NativeProcessError({\n message: text(\"message\") ?? \"The process supervisor failed\",\n code: text(\"code\"),\n errno: typeof value.errno === \"number\" ? value.errno : undefined,\n reason: text(\"reason\"),\n syscall: text(\"syscall\"),\n path: text(\"path\"),\n signal: text(\"signal\")\n })\n}\n\n/**\n * Preserve the platform's errno and signal vocabulary across the private wire.\n * @private\n * @since 1.0.0\n */\nexport const failure = (method: string, command: string, cause: unknown): PlatformError.PlatformError => {\n const error = nativeError(cause)\n const tags: Readonly<Record<string, PlatformError.SystemErrorTag>> = {\n ENOENT: \"NotFound\",\n EACCES: \"PermissionDenied\",\n EEXIST: \"AlreadyExists\",\n EISDIR: \"BadResource\",\n ENOTDIR: \"BadResource\",\n ELOOP: \"BadResource\",\n EBUSY: \"Busy\"\n }\n return PlatformError.systemError({\n _tag: tags[error.code ?? \"\"] ?? \"Unknown\",\n module: \"ChildProcess\",\n method,\n pathOrDescriptor: command,\n syscall: error.syscall,\n description: error.message,\n cause: error\n })\n}\n\n/**\n * The observation behind an unverified cleanup, in the message itself: a\n * failure report usually shows only the message, and whether the receipt, the\n * kernel or the snapshot was missing decides host load versus a live survivor.\n * @private\n * @since 1.0.0\n */\nexport const unverifiedCleanup = (\n control: {\n readonly fault: unknown\n readonly cleanupFailed: boolean\n readonly cleanupAcknowledged: boolean\n readonly targetDone: boolean\n },\n cleanupRequired: boolean,\n vacant: boolean,\n observed: Snapshot | undefined\n): string =>\n [\n `target exited: ${control.targetDone}`,\n `cleanup receipt: ${\n control.cleanupFailed\n ? \"failed\"\n : !cleanupRequired\n ? \"not required\"\n : control.cleanupAcknowledged\n ? \"acknowledged\"\n : \"missing\"\n }`,\n `group vacant: ${vacant}`,\n observed === undefined\n ? \"group snapshot: unavailable\"\n : `group members: ${\n observed.members.length === 0\n ? \"none\"\n : observed.members.map((member) => `${member.pid} ${member.zombie ? \"zombie\" : \"live\"}`).join(\", \")\n }; host group ${observed.ownGroup}`,\n ...(control.fault === undefined\n ? []\n : [`fault: ${String((control.fault as { message?: unknown })?.message ?? control.fault)}`])\n ].join(\"; \")\n\nconst promise = <A>() => {\n let resolve!: (value: A) => void\n let reject!: (cause: unknown) => void\n const promise = new Promise<A>((yes, no) => {\n resolve = yes\n reject = no\n })\n // A status may arrive before its Effect consumer starts waiting.\n void promise.catch(() => {})\n return { promise, resolve, reject }\n}\n\nconst wait = <A>(value: Promise<A>, method: string, command: string) =>\n Effect.tryPromise({ try: () => value, catch: (cause) => failure(method, command, cause) })\n\nconst bounded = <A, E, R>(effect: Effect.Effect<A, E, R>, millis: number, method: string, command: string) =>\n effect.pipe(\n Effect.timeoutOrElse({\n duration: millis,\n orElse: () =>\n Effect.fail(\n failure(method, command, processFault(\"supervisor_timeout\", `Process supervisor ${method} timed out`))\n )\n }),\n Effect.provideService(Clock.Clock, processClock)\n )\n\n/**\n * Standalone application executables do not implement the runtime's eval CLI.\n * Refuse them before spawning rather than recursively launching the application.\n * @private\n * @since 1.0.0\n */\nexport const bootstrapArguments = (runtime: { readonly bun: boolean; readonly main: string; readonly sea: boolean }) =>\n runtime.sea || runtime.main.startsWith(\"/$bunfs/\")\n ? Effect.fail(\n failure(\n \"spawn\",\n process.execPath,\n processFault(\n \"runtime_unsupported\",\n \"Process supervision requires a Node or Bun runtime executable, not a compiled application\"\n )\n )\n )\n : Effect.succeed(runtime.bun ? [\"--no-env-file\", \"--config=/dev/null\"] : [])\n\nconst channelEnvironment = \"SMITHERS_PROCESS_CHANNEL\"\nconst tlsOptions = { minVersion: \"TLSv1.3\", maxVersion: \"TLSv1.3\", ciphers: \"TLS_AES_128_GCM_SHA256\" } as const\n\n/**\n * Connect an owner to its authenticated private channel.\n * @private\n * @since 1.0.0\n */\nexport const connectOwner = (path: string, environment: Readonly<Record<string, string | undefined>>): Socket => {\n const encoded = environment[channelEnvironment]\n if (encoded === undefined) return createConnection(path)\n const channel = JSON.parse(encoded) as { key: string; status: number; requests: number }\n return Tls.connect({\n ...tlsOptions,\n host: \"127.0.0.1\",\n port: path.endsWith(\"/r\") ? channel.requests : channel.status,\n pskCallback: () => ({ identity: \"smithers-owner\", psk: Buffer.from(channel.key, \"hex\") })\n })\n}\n\n/**\n * Kept separate from Effect scopes so native socket callbacks only settle\n * promises; none can start an unowned fiber or signal an observed process id.\n * @private\n * @since 1.0.0\n */\nexport class Control {\n readonly ready = promise<number>()\n readonly started = promise<void>()\n readonly exited = promise<ExitCode>()\n readonly lost = promise<never>()\n readonly ended = promise<void>()\n readonly requestsReady = promise<void>()\n readonly directory: string\n readonly path: string\n readonly requestPath: string\n readonly server\n readonly requestServer\n readonly listening: Promise<void>\n private readonly key: Buffer | undefined\n private readonly connections = new Set<Socket>()\n socket: Socket | undefined\n requestSocket: Socket | undefined\n targetDone = false\n ownerCreation: unknown\n targetPid: number | undefined\n /** The owner's group guardian: a member that is neither target nor owner. */\n guardianPid: number | undefined\n ownerDone = false\n spawnFailed = false\n cleanupFailed = false\n cleanupAcknowledged = false\n activationSent = false\n closeSent = false\n fault: unknown\n onTargetExit: () => void = () => {}\n onCleanup: () => void = () => {}\n private receivedReady = false\n private receivedStarted = false\n private withdrawn = false\n\n constructor(transport: \"native\" | \"tls\" = \"native\") {\n this.key = transport === \"tls\" || process.platform === \"win32\" ? randomBytes(32) : undefined\n this.directory = mkdtempSync(this.key === undefined ? \"/tmp/sm-p-\" : join(tmpdir(), \"sm-p-\"))\n this.path = `${this.directory}/s`\n this.requestPath = `${this.directory}/r`\n const servers: Array<ReturnType<typeof createServer>> = []\n try {\n chmodSync(this.directory, 0o700)\n this.server = this.makeServer((socket) => this.accept(socket))\n servers.push(this.server)\n this.requestServer = this.makeServer((socket) => this.acceptRequests(socket))\n servers.push(this.requestServer)\n this.listening = Promise.all([\n new Promise<void>((resolve, reject) => {\n this.server.once(\"error\", reject)\n if (this.key === undefined) this.server.listen(this.path, resolve)\n else this.server.listen(0, \"127.0.0.1\", resolve)\n }),\n new Promise<void>((resolve, reject) => {\n this.requestServer.once(\"error\", reject)\n if (this.key === undefined) this.requestServer.listen(this.requestPath, resolve)\n else this.requestServer.listen(0, \"127.0.0.1\", resolve)\n })\n ]).then(() => {})\n } catch (cause) {\n for (const server of servers) server.close()\n rmSync(this.directory, { recursive: true, force: true })\n throw cause\n }\n }\n\n private makeServer(accept: (socket: Socket) => void): ReturnType<typeof createServer> {\n if (this.key === undefined) return createServer(accept)\n const server = Tls.createServer({\n ...tlsOptions,\n handshakeTimeout: startupMs,\n pskCallback: (_socket, identity) => identity === \"smithers-owner\" ? this.key! : null\n }, accept)\n server.on(\"connection\", (socket) => {\n this.connections.add(socket)\n socket.once(\"close\", () => this.connections.delete(socket))\n })\n server.on(\"tlsClientError\", (_error, socket) => socket.destroy())\n return server\n }\n\n /** Only the trusted owner receives this environment; target configuration replaces it. */\n environment(): Readonly<Record<string, string>> {\n if (this.key === undefined) return {}\n const status = this.server.address() as { port: number }\n const requests = this.requestServer.address() as { port: number }\n return {\n [channelEnvironment]: JSON.stringify({\n key: this.key.toString(\"hex\"),\n status: status.port,\n requests: requests.port\n })\n }\n }\n\n /** The test peer uses the same authenticated transport as the isolated owner. */\n connect(requests = false): Socket {\n return connectOwner(requests ? this.requestPath : this.path, this.environment())\n }\n\n /** Validate the stored READY after the raw spawn effect has returned its pid. */\n withdraw(pid: number, actual: number): void {\n if (pid !== actual) throw processFault(\"identity_mismatch\", \"Wrong supervisor identity\")\n this.server.close()\n this.server.unref()\n this.requestServer.close()\n this.requestServer.unref()\n // Node may unlink the owned socket synchronously inside server.close().\n rmSync(this.path, { force: true })\n rmSync(this.requestPath, { force: true })\n rmdirSync(this.directory)\n this.withdrawn = true\n }\n\n /** Request EOF asks for cleanup; leave its independent status reader intact. */\n disconnect(): void {\n this.requestSocket?.destroy()\n }\n\n dispose(): void {\n for (const socket of this.connections) socket.destroy()\n this.disconnect()\n this.socket?.destroy()\n this.server.close()\n this.requestServer.close()\n if (!this.withdrawn) rmSync(this.directory, { recursive: true, force: true })\n }\n\n rawEnded(): void {\n this.ownerDone = true\n // An accepted socket may still have buffered status frames. Its close event\n // rejects missing outcomes only after Node has delivered those frames.\n if (this.socket === undefined) this.closed()\n }\n\n write(message: unknown): Promise<void> {\n return new Promise((resolve, reject) => {\n const socket = this.requestSocket\n if (socket === undefined || socket.destroyed || !socket.writable) {\n reject(processFault(\"control_channel_closed\", \"Private process control channel closed\"))\n return\n }\n const data = `${JSON.stringify(message)}\\n`\n if (Buffer.byteLength(data) > 4 * 1024 * 1024) {\n reject(processFault(\"configuration_too_large\", \"Process configuration exceeds the private frame limit\"))\n return\n }\n socket.write(data, (error) => error ? reject(error) : resolve())\n })\n }\n\n private closed(): void {\n this.disconnect()\n const cause = this.fault ??\n processFault(\"outcome_missing\", \"Process supervisor closed before reporting its outcome\")\n if (this.activationSent && !this.targetDone && !this.spawnFailed) this.lost.reject(cause)\n this.ready.reject(cause)\n this.requestsReady.reject(cause)\n this.started.reject(cause)\n this.exited.reject(cause)\n this.ended.resolve()\n }\n\n private acceptRequests(socket: Socket): void {\n if (this.requestSocket !== undefined) {\n socket.destroy()\n return\n }\n this.requestSocket = socket\n // A late write may fail after the owner has sent its last status frames.\n // Destroying this socket must never discard those frames on the reader.\n socket.on(\"error\", (cause) => {\n this.fault = cause\n })\n this.requestsReady.resolve()\n }\n\n private accept(socket: Socket): void {\n if (this.socket !== undefined) {\n socket.destroy()\n return\n }\n this.socket = socket\n let buffer = \"\"\n socket.setEncoding(\"utf8\")\n socket.on(\"data\", (data: string) => {\n try {\n buffer += data\n if (Buffer.byteLength(buffer) > 16 * 1024) {\n throw processFault(\"status_too_large\", \"Process status frame exceeds its limit\")\n }\n for (;;) {\n const end = buffer.indexOf(\"\\n\")\n if (end < 0) break\n const line = buffer.slice(0, end)\n buffer = buffer.slice(end + 1)\n this.receive(JSON.parse(line))\n }\n } catch (cause) {\n this.fault = cause\n socket.destroy()\n }\n })\n socket.on(\"error\", (cause) => {\n this.fault = cause\n })\n socket.once(\"close\", () => this.closed())\n }\n\n private receive(value: unknown): void {\n if (typeof value !== \"object\" || value === null) throw processFault(\"status_invalid\", \"Invalid process status\")\n const message = value as Record<string, unknown>\n switch (message.type) {\n case \"ready\":\n if (\n this.receivedReady || message.version !== 1 || !Number.isSafeInteger(message.pid) || Number(message.pid) <= 1\n ) {\n throw processFault(\"status_invalid\", \"Invalid process readiness\")\n }\n this.receivedReady = true\n this.ownerCreation = message.created\n this.ready.resolve(Number(message.pid))\n return\n case \"spawned\":\n if (\n !this.activationSent || this.receivedStarted || !Number.isSafeInteger(message.pid) ||\n Number(message.pid) <= 1 ||\n message.guardian !== undefined && (!Number.isSafeInteger(message.guardian) || Number(message.guardian) <= 1)\n ) {\n throw processFault(\"status_invalid\", \"Invalid target startup\")\n }\n this.receivedStarted = true\n this.targetPid = Number(message.pid)\n if (message.guardian !== undefined) this.guardianPid = Number(message.guardian)\n this.started.resolve()\n return\n case \"spawn_error\":\n if (!this.activationSent || this.receivedStarted) {\n throw processFault(\"status_invalid\", \"Invalid target spawn failure\")\n }\n this.spawnFailed = true\n this.fault = message\n this.started.reject(message)\n this.exited.reject(message)\n return\n case \"fault\":\n this.fault = message\n this.started.reject(message)\n this.exited.reject(message)\n return\n case \"exit\":\n if (\n !this.receivedStarted || this.targetDone ||\n !(Number.isInteger(message.code) && Number(message.code) >= 0 && message.signal === null ||\n message.code === null && typeof message.signal === \"string\" && /^SIG[A-Z0-9]+$/.test(message.signal))\n ) {\n throw processFault(\"status_invalid\", \"Invalid target exit status\")\n }\n this.targetDone = true\n if (message.code === null) {\n this.exited.reject({\n message: `Process interrupted due to receipt of signal: '${message.signal}'`,\n signal: message.signal\n })\n } else this.exited.resolve(ExitCode(Number(message.code)))\n this.onTargetExit()\n return\n case \"cleanup_error\":\n this.cleanupFailed = true\n this.fault = message\n return\n case \"cleanup\":\n this.cleanupAcknowledged = true\n this.onCleanup()\n return\n default:\n throw processFault(\"status_unknown\", \"Unknown process status\")\n }\n }\n}\n\n/**\n * Prepare a private, independently bounded process owner before target launch.\n * @private\n * @since 1.0.0\n */\nexport const prepare = (\n system: System,\n policy: (\n options: ChildProcess.KillOptions,\n defaults?: ChildProcess.KillOptions\n ) => Effect.Effect<Policy, PlatformError.PlatformError>,\n transport: \"native\" | \"tls\" = \"native\"\n): Lifecycle =>\n(command, spawn) =>\n Effect.gen(function*() {\n const initial = yield* policy(command.options)\n const bun = process.versions.bun !== undefined\n const sea = bun ? false : (yield* Effect.tryPromise({\n try: () => import(\"node:sea\"),\n catch: (cause) => failure(\"spawn\", process.execPath, cause)\n })).isSea()\n const bootstrap = yield* bootstrapArguments({\n bun,\n sea,\n main: (globalThis as { readonly Bun?: { readonly main?: string } }).Bun?.main ?? \"\"\n })\n const windows = system.platform === \"win32\"\n const grouped = !windows && (command.options.detached ?? true)\n const jobExecutable = windows ?\n yield* Effect.try({\n try: resolveJobExecutable,\n catch: (cause) => failure(\"spawn\", command.command, cause)\n }) :\n undefined\n const control = yield* Effect.acquireRelease(\n Effect.try({ try: () => new Control(transport), catch: (cause) => failure(\"spawn\", command.command, cause) }),\n (control) => Effect.sync(() => control.dispose())\n )\n yield* bounded(wait(control.listening, \"spawn\", command.command), startupMs, \"spawn\", command.command)\n const raw = yield* spawn(ChildProcess.make(process.execPath, [\n ...bootstrap,\n \"-e\",\n source,\n control.path,\n grouped ? \"group\" : \"direct\"\n ], {\n ...command.options,\n cwd: parse(process.execPath).root,\n env: {\n PATH: \"/usr/bin:/bin\",\n HOME: \"/\",\n XDG_CONFIG_HOME: \"/\",\n BUN_RUNTIME_TRANSPILER_CACHE_PATH: \"0\",\n ...control.environment(),\n ...(jobExecutable === undefined ? {} : { SMITHERS_PROCESS_JOB_HELPER: jobExecutable })\n },\n extendEnv: false,\n shell: false,\n detached: grouped,\n killSignal: \"SIGTERM\",\n forceKillAfter: initial.graceMs\n }))\n yield* Effect.exit(raw.exitCode).pipe(Effect.andThen(Effect.sync(() => control.rawEnded())), Effect.forkScoped)\n let job: WindowsProcessJob | undefined\n let settled = false\n let referenced = true\n let reref: Effect.Effect<void, PlatformError.PlatformError> = Effect.void\n let selected = initial\n let requireCleanupReceipt = false\n const snapshot = () => system.snapshot(raw.pid)\n const alone = () => {\n if (!grouped) return control.targetDone\n const observed = snapshot()\n if (observed === undefined || observed.ownGroup === raw.pid) return false\n // The guardian leaves with the owner's final group SIGKILL.\n const running = observed.members.filter((member) => !member.zombie && member.pid !== control.guardianPid)\n return running.length === 1 && running[0]!.pid === raw.pid\n }\n // This observation only shortens the owner's deadline. The live owner makes\n // the signal, so neither a stale observation nor a reused pid grants a kill.\n control.onTargetExit = () => {\n // An explicit stop already owns its deadline and escaped-child sweep.\n // Natural exit must not replace that policy with a second fast stop.\n if (control.closeSent) return\n if (!alone()) return\n control.closeSent = true\n void control.write({ type: \"stop\", killSignal: \"SIGKILL\", fast: true }).catch(() => control.disconnect())\n }\n const finish = yield* Effect.cached(Effect.gen(function*() {\n yield* bounded(Effect.exit(raw.exitCode), selected.graceMs + exitAllowanceMs, \"kill\", command.command).pipe(\n Effect.catch((cause) =>\n job === undefined ? Effect.fail(cause) : Effect.gen(function*() {\n // The native guardian can terminate a stalled owner independently.\n job!.stop()\n yield* bounded(Effect.exit(raw.exitCode), exitAllowanceMs, \"kill\", command.command)\n })\n )\n )\n if (job !== undefined) {\n yield* bounded(wait(job.settled.promise, \"kill\", command.command), 5500, \"kill\", command.command)\n settled = true\n return\n }\n yield* bounded(wait(control.ended.promise, \"kill\", command.command), deliveryMs, \"kill\", command.command).pipe(\n Effect.ignore\n )\n const deadline = Date.now() + verificationMs\n for (;;) {\n // The kernel's ESRCH proves the group empty with no fork. A `ps`\n // snapshot can go unanswered on a loaded host, so it only decides what\n // the kernel cannot: a group still holding zombies, or an unknown one.\n // A live member keeps both answers false. This host is alive, so a\n // vacant group is never this host's own group.\n const vacant = grouped && system.vacant(raw.pid)\n const observed = grouped && !vacant ? snapshot() : undefined\n settled = !control.cleanupFailed && (!requireCleanupReceipt || control.cleanupAcknowledged) && (grouped\n ? vacant ||\n observed !== undefined && observed.ownGroup !== raw.pid && observed.members.every((member) => member.zombie)\n : control.targetDone || control.spawnFailed || !control.activationSent)\n if (settled) return\n if (Date.now() >= deadline) {\n return yield* Effect.fail(\n failure(\n \"kill\",\n command.command,\n processFault(\n \"cleanup_unverified\",\n \"Process cleanup could not be verified; its ledger record is retained \" +\n `(${unverifiedCleanup(control, requireCleanupReceipt, vacant, observed)})`,\n {\n cause: {\n fault: control.fault,\n cleanupFailed: control.cleanupFailed,\n cleanupRequired: requireCleanupReceipt,\n cleanupAcknowledged: control.cleanupAcknowledged,\n targetDone: control.targetDone,\n groupVacant: vacant,\n ownerObserved: observed !== undefined,\n members: observed?.members\n }\n }\n )\n )\n )\n }\n yield* Effect.sleep(10).pipe(Effect.provideService(Clock.Clock, processClock))\n }\n }))\n const kill = (options: ChildProcess.KillOptions = {}) =>\n Effect.gen(function*() {\n const requested = yield* policy(options, command.options)\n if (!referenced) {\n job?.reference(true)\n control.socket?.ref()\n control.requestSocket?.ref()\n yield* reref\n referenced = true\n }\n if (!control.closeSent) {\n control.closeSent = true\n selected = requested\n // An empty original group cannot prove an explicit escaped-descendant\n // sweep succeeded if its status frames were lost. Startup and natural\n // completion have no such additional contract.\n requireCleanupReceipt = grouped && control.activationSent && !control.spawnFailed && !control.targetDone\n yield* bounded(\n wait(\n control.write({\n type: \"stop\",\n explicit: true,\n ...requested,\n // A live target can move out of this group with setsid().\n // Only an owner whose target was never activated can replace\n // the requested signal; natural exit has its own fast path.\n killSignal: !control.activationSent && alone() ? \"SIGKILL\" : requested.killSignal\n }),\n \"kill\",\n command.command\n ),\n deliveryMs,\n \"kill\",\n command.command\n )\n .pipe(Effect.catch(() => Effect.sync(() => control.disconnect())))\n }\n yield* finish\n }).pipe(Effect.uninterruptible)\n yield* Effect.addFinalizer(() =>\n kill().pipe(\n // A stuck owner keeps its ledger entry. Disable the underlying spawner's\n // unconditional group-kill finalizer rather than signal a stale identity.\n Effect.ensuring(\n Effect.suspend(() =>\n settled ? Effect.void : Effect.andThen(\n raw.unref,\n Effect.sync(() => {\n job?.stop()\n job?.reference(false)\n control.disconnect()\n })\n )\n ).pipe(Effect.orDie)\n ),\n Effect.orDie\n )\n )\n const ready = yield* bounded(\n wait(control.ready.promise, \"spawn\", command.command),\n startupMs,\n \"spawn\",\n command.command\n )\n yield* bounded(wait(control.requestsReady.promise, \"spawn\", command.command), startupMs, \"spawn\", command.command)\n yield* Effect.try({\n try: () => control.withdraw(ready, raw.pid),\n catch: (cause) => failure(\"spawn\", command.command, cause)\n })\n if (windows) {\n job = yield* Effect.try({\n try: () => new WindowsProcessJob(raw.pid, control.ownerCreation, jobExecutable!),\n catch: (cause) => failure(\"spawn\", command.command, cause)\n })\n yield* bounded(wait(job.ready.promise, \"spawn\", command.command), startupMs, \"spawn\", command.command)\n }\n const acknowledgeCleanup = windows || transport === \"tls\"\n if (acknowledgeCleanup) {\n control.onCleanup = () => {\n if (!control.ownerDone) {\n void control.write({ type: \"cleanup_ack\" }).catch(() => control.disconnect())\n }\n }\n }\n const options = command.options\n // Match Effect/Node's undefined-vs-empty environment semantics in the host,\n // before replacing the helper's environment with its isolated bootstrap.\n const env = options.extendEnv ? { ...process.env, ...options.env } : options.env ?? { ...process.env }\n yield* bounded(\n wait(\n control.write({\n type: \"configure\",\n acknowledgeCleanup,\n command: command.command,\n args: command.args,\n cwd: resolve(options.cwd ?? process.cwd()),\n env,\n shell: options.shell,\n windowsHide: options.windowsHide ?? true,\n windowsVerbatimArguments:\n (options as ChildProcess.CommandOptions & { readonly windowsVerbatimArguments?: boolean })\n .windowsVerbatimArguments,\n standardFds: standardFdsOf(raw),\n userFds: [\n ...new Set(\n Object.keys(options.additionalFds ?? {}).map(ChildProcess.parseFdName)\n .filter((fd): fd is number => fd !== undefined)\n )\n ],\n ...initial\n }),\n \"spawn\",\n command.command\n ),\n deliveryMs,\n \"spawn\",\n command.command\n )\n const activate = yield* Effect.cached(Effect.gen(function*() {\n control.activationSent = true\n yield* bounded(\n wait(control.write({ type: \"start\" }), \"spawn\", command.command),\n deliveryMs,\n \"spawn\",\n command.command\n )\n yield* bounded(wait(control.started.promise, \"spawn\", command.command), startupMs, \"spawn\", command.command)\n }))\n const unref = Effect.gen(function*() {\n if (referenced) {\n reref = yield* raw.unref\n job?.reference(false)\n control.socket?.unref()\n control.requestSocket?.unref()\n referenced = false\n }\n return Effect.gen(function*() {\n if (!referenced) {\n job?.reference(true)\n control.socket?.ref()\n control.requestSocket?.ref()\n yield* reref\n referenced = true\n }\n })\n })\n const output = <A>(stream: Stream.Stream<A, PlatformError.PlatformError>) =>\n stream.pipe(\n Stream.interruptWhen(wait(control.lost.promise, \"read\", command.command))\n )\n const input = <A>(sink: Sink.Sink<A, Uint8Array, never, PlatformError.PlatformError>) =>\n Sink.fromChannel(\n Sink.toChannel(sink).pipe(Channel.interruptWhen(wait(control.lost.promise, \"write\", command.command)))\n )\n const handle = makeHandle({\n ...raw,\n exitCode: wait(control.exited.promise, \"exitCode\", command.command),\n isRunning: Effect.sync(() => !control.targetDone && !control.ownerDone),\n stdin: input(raw.stdin),\n getInputFd: (fd) => input(raw.getInputFd(fd)),\n stdout: output(raw.stdout),\n stderr: output(raw.stderr),\n all: output(raw.all),\n getOutputFd: (fd) => output(raw.getOutputFd(fd)),\n kill,\n unref\n })\n targets.set(handle, control)\n return {\n handle,\n activate,\n settled: Effect.sync(() => settled)\n }\n })\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["resolve", "promise", "control", "options"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SupervisorProgram.d.ts","sourceRoot":"","sources":["../../../src/internal/SupervisorProgram.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"SupervisorProgram.d.ts","sourceRoot":"","sources":["../../../src/internal/SupervisorProgram.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,QA6SlB,CAAA"}
|
|
@@ -60,6 +60,13 @@ const control = connect(socketPath);
|
|
|
60
60
|
const requests = connect(socketPath.replace(/\/s$/, '/r'));
|
|
61
61
|
let config;
|
|
62
62
|
let target;
|
|
63
|
+
// An owner killed outright (SIGKILL, the OOM killer) runs no handler, so it
|
|
64
|
+
// cannot stop its group. Its guardian is a member of that group that waits on
|
|
65
|
+
// a pipe only the owner holds. At EOF it kills its own group: kill 0 names the
|
|
66
|
+
// caller's group, never a number that could be stale. It ignores the catchable
|
|
67
|
+
// signals a stop sends the group and dies in the owner's final SIGKILL.
|
|
68
|
+
let guardian;
|
|
69
|
+
const guardianScript = "command trap '' HUP INT QUIT ABRT ALRM TERM USR1 USR2 PIPE TSTP TTIN TTOU; read -r line; kill -s KILL 0";
|
|
63
70
|
let targetDone = false;
|
|
64
71
|
let stopping = false;
|
|
65
72
|
let explicitStop = false;
|
|
@@ -220,8 +227,9 @@ const settleVacantGroup = () => {
|
|
|
220
227
|
let rows;
|
|
221
228
|
try { rows = snapshot(); } catch { return; }
|
|
222
229
|
for (const row of rows.values()) {
|
|
223
|
-
// The owner's
|
|
224
|
-
// exited target is already reaped and its orphans have
|
|
230
|
+
// The owner's other children are its guardian and the ps answering this
|
|
231
|
+
// snapshot; the exited target is already reaped and its orphans have
|
|
232
|
+
// another parent.
|
|
225
233
|
if (row.group === process.pid && row.pid !== process.pid && row.parent !== process.pid && !row.zombie) return;
|
|
226
234
|
}
|
|
227
235
|
clearTimeout(timer);
|
|
@@ -267,6 +275,18 @@ requests.on('data', (data) => {
|
|
|
267
275
|
if (config.standardFds !== undefined) {
|
|
268
276
|
for (const fd of [0, 1, 2]) descriptors[fd] = config.standardFds[fd];
|
|
269
277
|
}
|
|
278
|
+
if (grouped) {
|
|
279
|
+
// Started before the target, so no target outlives an owner unguarded.
|
|
280
|
+
// Every caller descriptor slot is replaced: a guardian holding a copy
|
|
281
|
+
// of the caller's output would keep that pipe from reaching EOF.
|
|
282
|
+
const guarded = descriptors.map(() => 'ignore');
|
|
283
|
+
guarded[0] = 'pipe';
|
|
284
|
+
guardian = cp.spawn('/bin/sh', ['-c', guardianScript], { cwd: '/', env: {}, detached: false, stdio: guarded });
|
|
285
|
+
// A failed launch has no pid; its later error event is the same fault.
|
|
286
|
+
guardian.once('error', () => {});
|
|
287
|
+
if (guardian.pid === undefined) throw new SupervisorFault('guardian_unavailable', 'Process group guardian unavailable');
|
|
288
|
+
guardian.stdin.on('error', () => {});
|
|
289
|
+
}
|
|
270
290
|
try {
|
|
271
291
|
target = cp.spawn(config.command, config.args, {
|
|
272
292
|
cwd: config.cwd, env: config.env, shell: config.shell,
|
|
@@ -289,7 +309,7 @@ requests.on('data', (data) => {
|
|
|
289
309
|
}
|
|
290
310
|
}
|
|
291
311
|
for (const fd of config.userFds) fs.closeSync(fd);
|
|
292
|
-
send({ type: 'spawned', pid: target.pid });
|
|
312
|
+
send({ type: 'spawned', pid: target.pid, ...(guardian === undefined ? {} : { guardian: guardian.pid }) });
|
|
293
313
|
});
|
|
294
314
|
target.once('error', spawnError);
|
|
295
315
|
target.once('exit', (code, signal) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/internal/SupervisorProgram.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * The isolated POSIX group owner. Kept as source because the same fixed program\n * runs in Node and Bun without loading the caller's modules or configuration.\n * Its behavior is exercised through the real prepared lifecycle tests.\n * @private\n * @since 1.0.0\n */\nexport const source = String.raw`\n// A fault of the owner itself: reason is its stable code on the status wire.\nclass SupervisorFault extends Error {\n constructor(reason, message) { super(message); this.name = 'SupervisorFault'; this.reason = reason; }\n}\nconst fs = require('node:fs');\nconst net = require('node:net');\nconst cp = require('node:child_process');\nconst [socketPath, mode] = process.argv.slice(-2);\nconst grouped = mode === 'group';\nconst jobHelper = process.env.SMITHERS_PROCESS_JOB_HELPER;\ndelete process.env.SMITHERS_PROCESS_JOB_HELPER;\nlet created;\nif (jobHelper !== undefined) {\n // The still-live owner observes itself. A later PID reuse cannot replace this\n // identity with a different process before the guardian attaches its job.\n const result = cp.spawnSync(jobHelper, ['--process-identity', String(process.pid)], {\n encoding: 'utf8', timeout: 5000, killSignal: 'SIGKILL', maxBuffer: 4096, env: {}\n });\n if (result.error || result.signal !== null || result.status !== 0) throw new SupervisorFault('owner_identity_unavailable', 'Owner identity unavailable');\n const identity = JSON.parse(result.stdout);\n if (identity?.status !== 'started' || typeof identity.created !== 'string' || !/^[1-9][0-9]{0,19}$/.test(identity.created)) {\n throw new SupervisorFault('owner_identity_unavailable', 'Owner creation identity unavailable');\n }\n created = identity.created;\n}\nconst channel = process.env.SMITHERS_PROCESS_CHANNEL ? JSON.parse(process.env.SMITHERS_PROCESS_CHANNEL) : undefined;\ndelete process.env.SMITHERS_PROCESS_CHANNEL;\nconst connect = path => channel === undefined ? net.connect(path) : require('node:tls').connect({\n minVersion: 'TLSv1.3', maxVersion: 'TLSv1.3', ciphers: 'TLS_AES_128_GCM_SHA256',\n host: '127.0.0.1', port: path.endsWith('/r') ? channel.requests : channel.status,\n pskCallback: () => ({ identity: 'smithers-owner', psk: Buffer.from(channel.key, 'hex') })\n});\nconst control = connect(socketPath);\n// A request write racing our exit may get EPIPE. Keep status on its own\n// connection so the host still receives the exit and cleanup already sent.\nconst requests = connect(socketPath.replace(/\\/s$/, '/r'));\nlet config;\nlet target;\nlet targetDone = false;\nlet stopping = false;\nlet explicitStop = false;\nlet killing = false;\nlet buffer = '';\nlet timer;\nlet escaped = new Map();\n\n// DirectorySandbox's trusted local explicit-stop contract also covers children\n// that deliberately create another session. This is best-effort positive-pid\n// signalling with identity revalidation, not the atomic own-group guarantee.\n// Linux reads its kernel table because slim images ship no procps (and BusyBox\n// ps lacks these columns); other POSIX hosts ask their system ps.\nconst procSnapshot = () => {\n let names;\n try { names = fs.readdirSync('/proc'); }\n catch { throw new SupervisorFault('descendant_observation_unavailable', 'Descendant observation unavailable'); }\n const rows = new Map();\n for (const name of names) {\n if (!/^[0-9]+$/.test(name)) continue;\n let text;\n try { text = fs.readFileSync('/proc/' + name + '/stat', 'utf8'); }\n catch (error) {\n // A process that ended after the listing is not an unreadable table.\n if (error.code === 'ENOENT' || error.code === 'ESRCH') continue;\n throw new SupervisorFault('descendant_observation_unavailable', 'Descendant observation unavailable');\n }\n // comm may contain spaces and parentheses; fields follow its last paren.\n const close = text.lastIndexOf(')');\n const head = text.slice(0, text.indexOf('(')).trim();\n const fields = text.slice(close + 1).trim().split(/[ \\t]+/);\n if (close < 0 || head !== name || fields.length < 20) throw new SupervisorFault('descendant_observation_invalid', 'Invalid descendant observation');\n // Fields 4, 5 and 22: parent, group and start time in clock ticks since boot.\n const [pid, parent, group, start] = [head, fields[1], fields[2], fields[19]].map(Number);\n if (![pid, parent, group, start].every(Number.isSafeInteger)) throw new SupervisorFault('descendant_identity_invalid', 'Invalid descendant identity');\n rows.set(pid, { pid, parent, group, start, zombie: fields[0] === 'Z' });\n }\n return rows;\n};\nconst psSnapshot = () => {\n const result = cp.spawnSync('/bin/ps', ['-A', '-o', 'pid=,ppid=,pgid=,stat=,lstart='], {\n encoding: 'utf8', timeout: 500, killSignal: 'SIGKILL', maxBuffer: 4 * 1024 * 1024,\n env: { PATH: '/usr/bin:/bin', LC_ALL: 'C' }\n });\n if (result.error || result.status !== 0) throw new SupervisorFault('descendant_observation_unavailable', 'Descendant observation unavailable');\n const rows = new Map();\n for (const line of result.stdout.trim().split('\\n')) {\n const match = /^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\S+)\\s+(.+)$/.exec(line);\n if (!match) throw new SupervisorFault('descendant_observation_invalid', 'Invalid descendant observation');\n const [pid, parent, group] = match.slice(1, 4).map(Number);\n const start = Date.parse(match[5]);\n if (![pid, parent, group, start].every(Number.isSafeInteger)) throw new SupervisorFault('descendant_identity_invalid', 'Invalid descendant identity');\n rows.set(pid, { pid, parent, group, start, zombie: match[4].startsWith('Z') });\n }\n return rows;\n};\nconst snapshot = process.platform === 'linux' ? procSnapshot : psSnapshot;\nconst captureEscaped = () => {\n const rows = snapshot();\n const descendants = new Set([target.pid]);\n for (;;) {\n const previous = descendants.size;\n for (const row of rows.values()) if (descendants.has(row.parent)) descendants.add(row.pid);\n if (descendants.size === previous) break;\n }\n for (const pid of descendants) {\n const row = rows.get(pid);\n if (row && !row.zombie && row.group !== process.pid && pid !== process.pid && pid > 1) escaped.set(pid, row);\n }\n};\nconst signalEscaped = (signal) => {\n if (escaped.size === 0) return;\n const rows = snapshot();\n for (const [pid, recorded] of escaped) {\n const row = rows.get(pid);\n if (!row || row.zombie) { escaped.delete(pid); continue; }\n if (row.start !== recorded.start || row.group !== recorded.group) {\n throw new SupervisorFault('descendant_identity_changed', 'Escaped descendant identity changed before signalling');\n }\n try { process.kill(pid, signal); }\n catch (error) { if (error.code === 'ESRCH') escaped.delete(pid); else throw error; }\n }\n};\nconst cleanupError = (error) => send({ type: 'cleanup_error', reason: error.reason, message: String(error.message).slice(0, 2048) });\nconst escapedSettled = () => {\n if (escaped.size === 0) return true;\n const rows = snapshot();\n for (const [pid, recorded] of escaped) {\n const row = rows.get(pid);\n if (!row || row.zombie) { escaped.delete(pid); continue; }\n if (row.start !== recorded.start || row.group !== recorded.group) {\n throw new SupervisorFault('descendant_identity_changed', 'Escaped descendant identity changed before cleanup verification');\n }\n }\n return escaped.size === 0;\n};\n\nconst send = (message, done = () => {}) => {\n if (control.destroyed || !control.writable) return done();\n control.write(JSON.stringify(message) + '\\n', done);\n};\nconst selfKill = () => process.kill(grouped ? -process.pid : process.pid, 'SIGKILL');\nconst force = () => {\n if (killing) return;\n killing = true;\n if (!grouped && target?.pid !== undefined && !targetDone) {\n target.kill('SIGKILL');\n return;\n }\n try { signalEscaped('SIGKILL'); } catch (error) { cleanupError(error); }\n const deadline = Date.now() + 500;\n const complete = () => {\n try {\n if (!escapedSettled()) {\n if (Date.now() < deadline) { setTimeout(complete, 10); return; }\n throw new SupervisorFault('descendants_unsettled', 'Escaped descendants did not settle before the cleanup bound');\n }\n } catch (error) { cleanupError(error); }\n // TLS write completion does not prove the peer received terminal status.\n // Keep the owner alive for its receipt; an unresponsive host still cannot\n // delay cleanup beyond the existing delivery bound.\n setTimeout(selfKill, 100);\n send({ type: 'cleanup' }, config?.acknowledgeCleanup ? () => {} : selfKill);\n };\n complete();\n};\nconst stop = (options = {}) => {\n // The first explicit stop owns its signal and deadline, including after EOF\n // or target exit. Only natural cleanup can accept the host's fast shortcut.\n if (stopping && (explicitStop || !options.fast)) return;\n const signal = options.killSignal ?? config?.killSignal ?? 'SIGTERM';\n // A target's exit cannot shorten a captured escaped child's grace period.\n if (options.fast && escaped.size !== 0) return;\n // A grouped owner reaches its members with one group signal and captures\n // only children that left the group. A direct target shares the host's\n // group, so every descendant is captured and signalled by pid.\n if (options.explicit && !targetDone && target?.pid !== undefined) {\n try { captureEscaped(); } catch (error) { cleanupError(error); }\n }\n stopping = true;\n explicitStop = options.explicit === true;\n if (signal === 'SIGKILL' || target?.pid === undefined || (!grouped && targetDone)) return force();\n if (signal === 'SIGSTOP') return force();\n // Keep the group owner alive to enforce escalation, including when the caller\n // chooses a catchable signal other than TERM/INT. SIGSTOP is refused by host.\n process.on(signal, () => {});\n try { signalEscaped(signal); } catch (error) { cleanupError(error); }\n if (grouped) process.kill(-process.pid, signal);\n else target.kill(signal);\n timer = setTimeout(force, options.graceMs ?? config?.graceMs ?? 2000);\n};\n// The grace window exists for the group's remaining members. Once the target\n// has exited and a snapshot shows no other live member and no escaped child,\n// nobody is left to wait for: finish now instead of sleeping out the grace.\n// An unreadable table keeps the deadline.\nconst settleVacantGroup = () => {\n if (!stopping || killing || escaped.size !== 0) return;\n let rows;\n try { rows = snapshot(); } catch { return; }\n for (const row of rows.values()) {\n // The owner's only other child is the ps answering this snapshot; the\n // exited target is already reaped and its orphans have another parent.\n if (row.group === process.pid && row.pid !== process.pid && row.parent !== process.pid && !row.zombie) return;\n }\n clearTimeout(timer);\n force();\n};\nconst spawnError = (error) => {\n send({ type: 'spawn_error', code: error.code, errno: error.errno,\n syscall: error.syscall, path: error.path, message: String(error.message).slice(0, 2048) });\n stop();\n};\nprocess.on('SIGTERM', () => { if (!stopping) stop(); });\nprocess.on('SIGINT', () => { if (!stopping) stop(); });\nprocess.on('uncaughtException', (error) => {\n try { send({ type: 'fault', reason: error.reason, message: String(error.message).slice(0, 2048) }, force); }\n finally { force(); }\n});\nprocess.on('unhandledRejection', (error) => { throw error; });\ncontrol.on('end', () => stop({ explicit: true }));\ncontrol.on('error', () => stop({ explicit: true }));\nrequests.on('end', () => stop({ explicit: true }));\nrequests.on('error', () => stop({ explicit: true }));\nrequests.setEncoding('utf8');\nrequests.on('data', (data) => {\n buffer += data;\n if (Buffer.byteLength(buffer) > 4 * 1024 * 1024) throw new SupervisorFault('control_frame_too_large', 'Control frame too large');\n for (;;) {\n const end = buffer.indexOf('\\n');\n if (end < 0) break;\n const line = buffer.slice(0, end);\n buffer = buffer.slice(end + 1);\n const message = JSON.parse(line);\n if (message.type === 'configure') {\n if (config !== undefined || stopping) throw new SupervisorFault('configuration_invalid', 'Duplicate or late configuration');\n config = message;\n } else if (message.type === 'cleanup_ack') {\n if (!config?.acknowledgeCleanup || !killing) throw new SupervisorFault('cleanup_acknowledgment_unexpected', 'Unexpected cleanup acknowledgment');\n selfKill();\n } else if (message.type === 'stop') stop(message);\n else if (message.type === 'start') {\n if (config === undefined || target !== undefined || stopping) throw new SupervisorFault('activation_invalid', 'Invalid activation');\n const descriptors = Array.from({ length: Math.max(2, ...config.userFds) + 1 }, () => 'ignore');\n for (const fd of [0, 1, 2, ...config.userFds]) descriptors[fd] = fd;\n if (config.standardFds !== undefined) {\n for (const fd of [0, 1, 2]) descriptors[fd] = config.standardFds[fd];\n }\n try {\n target = cp.spawn(config.command, config.args, {\n cwd: config.cwd, env: config.env, shell: config.shell,\n windowsHide: config.windowsHide, windowsVerbatimArguments: config.windowsVerbatimArguments,\n detached: false, stdio: descriptors\n });\n } catch (error) { spawnError(error); continue; }\n target.once('spawn', () => {\n // Release every caller pipe copy. Keep the runtime's standard slots\n // valid so a later internal allocation cannot reuse a closed 0/1/2.\n if (config.standardFds !== undefined) {\n // libuv intentionally does not close Windows CRT descriptors 0..2.\n // Caller pipes arrive in separate slots; the reserved slots are NUL.\n for (const fd of config.standardFds) if (fd !== 'ignore') fs.closeSync(fd);\n } else {\n for (const fd of [0, 1, 2]) {\n fs.closeSync(fd);\n const replacement = fs.openSync(require('node:os').devNull, fd === 0 ? 'r' : 'w');\n if (replacement !== fd) throw new SupervisorFault('stream_detach_failed', 'Could not detach supervisor standard streams');\n }\n }\n for (const fd of config.userFds) fs.closeSync(fd);\n send({ type: 'spawned', pid: target.pid });\n });\n target.once('error', spawnError);\n target.once('exit', (code, signal) => {\n targetDone = true;\n send({ type: 'exit', code, signal });\n if (grouped) { stop(); settleVacantGroup(); }\n else { killing = false; clearTimeout(timer); force(); }\n });\n } else throw new SupervisorFault('control_message_invalid', 'Invalid control message');\n }\n});\ncontrol.once('connect', () => send({ type: 'ready', version: 1, pid: process.pid, created }));\n`\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOO,MAAM,SAAS,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
|
|
4
|
+
"sourcesContent": ["/**\n * The isolated POSIX group owner. Kept as source because the same fixed program\n * runs in Node and Bun without loading the caller's modules or configuration.\n * Its behavior is exercised through the real prepared lifecycle tests.\n * @private\n * @since 1.0.0\n */\nexport const source = String.raw`\n// A fault of the owner itself: reason is its stable code on the status wire.\nclass SupervisorFault extends Error {\n constructor(reason, message) { super(message); this.name = 'SupervisorFault'; this.reason = reason; }\n}\nconst fs = require('node:fs');\nconst net = require('node:net');\nconst cp = require('node:child_process');\nconst [socketPath, mode] = process.argv.slice(-2);\nconst grouped = mode === 'group';\nconst jobHelper = process.env.SMITHERS_PROCESS_JOB_HELPER;\ndelete process.env.SMITHERS_PROCESS_JOB_HELPER;\nlet created;\nif (jobHelper !== undefined) {\n // The still-live owner observes itself. A later PID reuse cannot replace this\n // identity with a different process before the guardian attaches its job.\n const result = cp.spawnSync(jobHelper, ['--process-identity', String(process.pid)], {\n encoding: 'utf8', timeout: 5000, killSignal: 'SIGKILL', maxBuffer: 4096, env: {}\n });\n if (result.error || result.signal !== null || result.status !== 0) throw new SupervisorFault('owner_identity_unavailable', 'Owner identity unavailable');\n const identity = JSON.parse(result.stdout);\n if (identity?.status !== 'started' || typeof identity.created !== 'string' || !/^[1-9][0-9]{0,19}$/.test(identity.created)) {\n throw new SupervisorFault('owner_identity_unavailable', 'Owner creation identity unavailable');\n }\n created = identity.created;\n}\nconst channel = process.env.SMITHERS_PROCESS_CHANNEL ? JSON.parse(process.env.SMITHERS_PROCESS_CHANNEL) : undefined;\ndelete process.env.SMITHERS_PROCESS_CHANNEL;\nconst connect = path => channel === undefined ? net.connect(path) : require('node:tls').connect({\n minVersion: 'TLSv1.3', maxVersion: 'TLSv1.3', ciphers: 'TLS_AES_128_GCM_SHA256',\n host: '127.0.0.1', port: path.endsWith('/r') ? channel.requests : channel.status,\n pskCallback: () => ({ identity: 'smithers-owner', psk: Buffer.from(channel.key, 'hex') })\n});\nconst control = connect(socketPath);\n// A request write racing our exit may get EPIPE. Keep status on its own\n// connection so the host still receives the exit and cleanup already sent.\nconst requests = connect(socketPath.replace(/\\/s$/, '/r'));\nlet config;\nlet target;\n// An owner killed outright (SIGKILL, the OOM killer) runs no handler, so it\n// cannot stop its group. Its guardian is a member of that group that waits on\n// a pipe only the owner holds. At EOF it kills its own group: kill 0 names the\n// caller's group, never a number that could be stale. It ignores the catchable\n// signals a stop sends the group and dies in the owner's final SIGKILL.\nlet guardian;\nconst guardianScript = \"command trap '' HUP INT QUIT ABRT ALRM TERM USR1 USR2 PIPE TSTP TTIN TTOU; read -r line; kill -s KILL 0\";\nlet targetDone = false;\nlet stopping = false;\nlet explicitStop = false;\nlet killing = false;\nlet buffer = '';\nlet timer;\nlet escaped = new Map();\n\n// DirectorySandbox's trusted local explicit-stop contract also covers children\n// that deliberately create another session. This is best-effort positive-pid\n// signalling with identity revalidation, not the atomic own-group guarantee.\n// Linux reads its kernel table because slim images ship no procps (and BusyBox\n// ps lacks these columns); other POSIX hosts ask their system ps.\nconst procSnapshot = () => {\n let names;\n try { names = fs.readdirSync('/proc'); }\n catch { throw new SupervisorFault('descendant_observation_unavailable', 'Descendant observation unavailable'); }\n const rows = new Map();\n for (const name of names) {\n if (!/^[0-9]+$/.test(name)) continue;\n let text;\n try { text = fs.readFileSync('/proc/' + name + '/stat', 'utf8'); }\n catch (error) {\n // A process that ended after the listing is not an unreadable table.\n if (error.code === 'ENOENT' || error.code === 'ESRCH') continue;\n throw new SupervisorFault('descendant_observation_unavailable', 'Descendant observation unavailable');\n }\n // comm may contain spaces and parentheses; fields follow its last paren.\n const close = text.lastIndexOf(')');\n const head = text.slice(0, text.indexOf('(')).trim();\n const fields = text.slice(close + 1).trim().split(/[ \\t]+/);\n if (close < 0 || head !== name || fields.length < 20) throw new SupervisorFault('descendant_observation_invalid', 'Invalid descendant observation');\n // Fields 4, 5 and 22: parent, group and start time in clock ticks since boot.\n const [pid, parent, group, start] = [head, fields[1], fields[2], fields[19]].map(Number);\n if (![pid, parent, group, start].every(Number.isSafeInteger)) throw new SupervisorFault('descendant_identity_invalid', 'Invalid descendant identity');\n rows.set(pid, { pid, parent, group, start, zombie: fields[0] === 'Z' });\n }\n return rows;\n};\nconst psSnapshot = () => {\n const result = cp.spawnSync('/bin/ps', ['-A', '-o', 'pid=,ppid=,pgid=,stat=,lstart='], {\n encoding: 'utf8', timeout: 500, killSignal: 'SIGKILL', maxBuffer: 4 * 1024 * 1024,\n env: { PATH: '/usr/bin:/bin', LC_ALL: 'C' }\n });\n if (result.error || result.status !== 0) throw new SupervisorFault('descendant_observation_unavailable', 'Descendant observation unavailable');\n const rows = new Map();\n for (const line of result.stdout.trim().split('\\n')) {\n const match = /^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\S+)\\s+(.+)$/.exec(line);\n if (!match) throw new SupervisorFault('descendant_observation_invalid', 'Invalid descendant observation');\n const [pid, parent, group] = match.slice(1, 4).map(Number);\n const start = Date.parse(match[5]);\n if (![pid, parent, group, start].every(Number.isSafeInteger)) throw new SupervisorFault('descendant_identity_invalid', 'Invalid descendant identity');\n rows.set(pid, { pid, parent, group, start, zombie: match[4].startsWith('Z') });\n }\n return rows;\n};\nconst snapshot = process.platform === 'linux' ? procSnapshot : psSnapshot;\nconst captureEscaped = () => {\n const rows = snapshot();\n const descendants = new Set([target.pid]);\n for (;;) {\n const previous = descendants.size;\n for (const row of rows.values()) if (descendants.has(row.parent)) descendants.add(row.pid);\n if (descendants.size === previous) break;\n }\n for (const pid of descendants) {\n const row = rows.get(pid);\n if (row && !row.zombie && row.group !== process.pid && pid !== process.pid && pid > 1) escaped.set(pid, row);\n }\n};\nconst signalEscaped = (signal) => {\n if (escaped.size === 0) return;\n const rows = snapshot();\n for (const [pid, recorded] of escaped) {\n const row = rows.get(pid);\n if (!row || row.zombie) { escaped.delete(pid); continue; }\n if (row.start !== recorded.start || row.group !== recorded.group) {\n throw new SupervisorFault('descendant_identity_changed', 'Escaped descendant identity changed before signalling');\n }\n try { process.kill(pid, signal); }\n catch (error) { if (error.code === 'ESRCH') escaped.delete(pid); else throw error; }\n }\n};\nconst cleanupError = (error) => send({ type: 'cleanup_error', reason: error.reason, message: String(error.message).slice(0, 2048) });\nconst escapedSettled = () => {\n if (escaped.size === 0) return true;\n const rows = snapshot();\n for (const [pid, recorded] of escaped) {\n const row = rows.get(pid);\n if (!row || row.zombie) { escaped.delete(pid); continue; }\n if (row.start !== recorded.start || row.group !== recorded.group) {\n throw new SupervisorFault('descendant_identity_changed', 'Escaped descendant identity changed before cleanup verification');\n }\n }\n return escaped.size === 0;\n};\n\nconst send = (message, done = () => {}) => {\n if (control.destroyed || !control.writable) return done();\n control.write(JSON.stringify(message) + '\\n', done);\n};\nconst selfKill = () => process.kill(grouped ? -process.pid : process.pid, 'SIGKILL');\nconst force = () => {\n if (killing) return;\n killing = true;\n if (!grouped && target?.pid !== undefined && !targetDone) {\n target.kill('SIGKILL');\n return;\n }\n try { signalEscaped('SIGKILL'); } catch (error) { cleanupError(error); }\n const deadline = Date.now() + 500;\n const complete = () => {\n try {\n if (!escapedSettled()) {\n if (Date.now() < deadline) { setTimeout(complete, 10); return; }\n throw new SupervisorFault('descendants_unsettled', 'Escaped descendants did not settle before the cleanup bound');\n }\n } catch (error) { cleanupError(error); }\n // TLS write completion does not prove the peer received terminal status.\n // Keep the owner alive for its receipt; an unresponsive host still cannot\n // delay cleanup beyond the existing delivery bound.\n setTimeout(selfKill, 100);\n send({ type: 'cleanup' }, config?.acknowledgeCleanup ? () => {} : selfKill);\n };\n complete();\n};\nconst stop = (options = {}) => {\n // The first explicit stop owns its signal and deadline, including after EOF\n // or target exit. Only natural cleanup can accept the host's fast shortcut.\n if (stopping && (explicitStop || !options.fast)) return;\n const signal = options.killSignal ?? config?.killSignal ?? 'SIGTERM';\n // A target's exit cannot shorten a captured escaped child's grace period.\n if (options.fast && escaped.size !== 0) return;\n // A grouped owner reaches its members with one group signal and captures\n // only children that left the group. A direct target shares the host's\n // group, so every descendant is captured and signalled by pid.\n if (options.explicit && !targetDone && target?.pid !== undefined) {\n try { captureEscaped(); } catch (error) { cleanupError(error); }\n }\n stopping = true;\n explicitStop = options.explicit === true;\n if (signal === 'SIGKILL' || target?.pid === undefined || (!grouped && targetDone)) return force();\n if (signal === 'SIGSTOP') return force();\n // Keep the group owner alive to enforce escalation, including when the caller\n // chooses a catchable signal other than TERM/INT. SIGSTOP is refused by host.\n process.on(signal, () => {});\n try { signalEscaped(signal); } catch (error) { cleanupError(error); }\n if (grouped) process.kill(-process.pid, signal);\n else target.kill(signal);\n timer = setTimeout(force, options.graceMs ?? config?.graceMs ?? 2000);\n};\n// The grace window exists for the group's remaining members. Once the target\n// has exited and a snapshot shows no other live member and no escaped child,\n// nobody is left to wait for: finish now instead of sleeping out the grace.\n// An unreadable table keeps the deadline.\nconst settleVacantGroup = () => {\n if (!stopping || killing || escaped.size !== 0) return;\n let rows;\n try { rows = snapshot(); } catch { return; }\n for (const row of rows.values()) {\n // The owner's other children are its guardian and the ps answering this\n // snapshot; the exited target is already reaped and its orphans have\n // another parent.\n if (row.group === process.pid && row.pid !== process.pid && row.parent !== process.pid && !row.zombie) return;\n }\n clearTimeout(timer);\n force();\n};\nconst spawnError = (error) => {\n send({ type: 'spawn_error', code: error.code, errno: error.errno,\n syscall: error.syscall, path: error.path, message: String(error.message).slice(0, 2048) });\n stop();\n};\nprocess.on('SIGTERM', () => { if (!stopping) stop(); });\nprocess.on('SIGINT', () => { if (!stopping) stop(); });\nprocess.on('uncaughtException', (error) => {\n try { send({ type: 'fault', reason: error.reason, message: String(error.message).slice(0, 2048) }, force); }\n finally { force(); }\n});\nprocess.on('unhandledRejection', (error) => { throw error; });\ncontrol.on('end', () => stop({ explicit: true }));\ncontrol.on('error', () => stop({ explicit: true }));\nrequests.on('end', () => stop({ explicit: true }));\nrequests.on('error', () => stop({ explicit: true }));\nrequests.setEncoding('utf8');\nrequests.on('data', (data) => {\n buffer += data;\n if (Buffer.byteLength(buffer) > 4 * 1024 * 1024) throw new SupervisorFault('control_frame_too_large', 'Control frame too large');\n for (;;) {\n const end = buffer.indexOf('\\n');\n if (end < 0) break;\n const line = buffer.slice(0, end);\n buffer = buffer.slice(end + 1);\n const message = JSON.parse(line);\n if (message.type === 'configure') {\n if (config !== undefined || stopping) throw new SupervisorFault('configuration_invalid', 'Duplicate or late configuration');\n config = message;\n } else if (message.type === 'cleanup_ack') {\n if (!config?.acknowledgeCleanup || !killing) throw new SupervisorFault('cleanup_acknowledgment_unexpected', 'Unexpected cleanup acknowledgment');\n selfKill();\n } else if (message.type === 'stop') stop(message);\n else if (message.type === 'start') {\n if (config === undefined || target !== undefined || stopping) throw new SupervisorFault('activation_invalid', 'Invalid activation');\n const descriptors = Array.from({ length: Math.max(2, ...config.userFds) + 1 }, () => 'ignore');\n for (const fd of [0, 1, 2, ...config.userFds]) descriptors[fd] = fd;\n if (config.standardFds !== undefined) {\n for (const fd of [0, 1, 2]) descriptors[fd] = config.standardFds[fd];\n }\n if (grouped) {\n // Started before the target, so no target outlives an owner unguarded.\n // Every caller descriptor slot is replaced: a guardian holding a copy\n // of the caller's output would keep that pipe from reaching EOF.\n const guarded = descriptors.map(() => 'ignore');\n guarded[0] = 'pipe';\n guardian = cp.spawn('/bin/sh', ['-c', guardianScript], { cwd: '/', env: {}, detached: false, stdio: guarded });\n // A failed launch has no pid; its later error event is the same fault.\n guardian.once('error', () => {});\n if (guardian.pid === undefined) throw new SupervisorFault('guardian_unavailable', 'Process group guardian unavailable');\n guardian.stdin.on('error', () => {});\n }\n try {\n target = cp.spawn(config.command, config.args, {\n cwd: config.cwd, env: config.env, shell: config.shell,\n windowsHide: config.windowsHide, windowsVerbatimArguments: config.windowsVerbatimArguments,\n detached: false, stdio: descriptors\n });\n } catch (error) { spawnError(error); continue; }\n target.once('spawn', () => {\n // Release every caller pipe copy. Keep the runtime's standard slots\n // valid so a later internal allocation cannot reuse a closed 0/1/2.\n if (config.standardFds !== undefined) {\n // libuv intentionally does not close Windows CRT descriptors 0..2.\n // Caller pipes arrive in separate slots; the reserved slots are NUL.\n for (const fd of config.standardFds) if (fd !== 'ignore') fs.closeSync(fd);\n } else {\n for (const fd of [0, 1, 2]) {\n fs.closeSync(fd);\n const replacement = fs.openSync(require('node:os').devNull, fd === 0 ? 'r' : 'w');\n if (replacement !== fd) throw new SupervisorFault('stream_detach_failed', 'Could not detach supervisor standard streams');\n }\n }\n for (const fd of config.userFds) fs.closeSync(fd);\n send({ type: 'spawned', pid: target.pid, ...(guardian === undefined ? {} : { guardian: guardian.pid }) });\n });\n target.once('error', spawnError);\n target.once('exit', (code, signal) => {\n targetDone = true;\n send({ type: 'exit', code, signal });\n if (grouped) { stop(); settleVacantGroup(); }\n else { killing = false; clearTimeout(timer); force(); }\n });\n } else throw new SupervisorFault('control_message_invalid', 'Invalid control message');\n }\n});\ncontrol.once('connect', () => send({ type: 'ready', version: 1, pid: process.pid, created }));\n`\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOO,MAAM,SAAS,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProcessConfinement.d.ts","sourceRoot":"","sources":["../../src/ProcessConfinement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,wBAAwB,MAAM,mCAAmC,CAAA;AAE7E,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAOrC,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AAErD;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAA;IAC1D,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,CAAC,EAAE,cAAc,CAAC,IAAI,GAAG,SAAS,CAAA;IAC/C,uGAAuG;IACvG,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACjD;AA0FD;;;;;GAKG;AACH,eAAO,MAAM,IAAI,GAAI,UAAS,OAAY,KAAG,wBAAwB,CAAC,
|
|
1
|
+
{"version":3,"file":"ProcessConfinement.d.ts","sourceRoot":"","sources":["../../src/ProcessConfinement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,wBAAwB,MAAM,mCAAmC,CAAA;AAE7E,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAOrC,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AAErD;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAA;IAC1D,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,CAAC,EAAE,cAAc,CAAC,IAAI,GAAG,SAAS,CAAA;IAC/C,uGAAuG;IACvG,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACjD;AA0FD;;;;;GAKG;AACH,eAAO,MAAM,IAAI,GAAI,UAAS,OAAY,KAAG,wBAAwB,CAAC,OA2MrE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,KAAK,GAAI,UAAS,OAAY,KAAG,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,kBAAkB,CAC1B,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProcessConfinement.js","sourceRoot":"","sources":["../../src/ProcessConfinement.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The kernel's `ProcessConfinement` seam, filled with this host's native\n * sandbox: bubblewrap on Linux, seatbelt on macOS.\n *\n * `@smthrs/kernel`'s `ChildProcessSpawner.layer` hands every stage that passed\n * its `proc:spawn` check to the confinement in its context, together with the\n * `ProcessConfinement.Profile` the grants in force admit. This module turns\n * that profile into a {@link ProcessSandbox.Request}, plans it against the\n * workspace, and wraps the stage's argv in the mechanism the host has, so an\n * approved `bash` can write only where an `fs:write` grant says and reach the\n * network only when a `net:*` grant says. A stage that asked for the platform\n * shell is spelled out as `/bin/sh -c <line>` first: wrapping a `shell: true`\n * command line would otherwise leave its redirections and pipes to a shell\n * outside the sandbox.\n *\n * A host with no mechanism is the one decision this module leaves to its\n * caller: {@link Options.unavailable} either refuses the spawn, so nothing\n * runs that the profile would not have confined, or runs it unconfined with\n * a warning, the way every host did before this seam existed. A write set\n * the mechanism cannot enforce safely, a symbolic link below the workspace\n * root, is always refused.\n *\n * @since 1.0.0-rc.1\n */\n\nimport * as KernelProcessConfinement from \"@smthrs/kernel/ProcessConfinement\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as PlatformError from \"effect/PlatformError\"\nimport type * as Scope from \"effect/Scope\"\nimport * as ChildProcess from \"effect/unstable/process/ChildProcess\"\nimport * as NodeFs from \"node:fs\"\nimport * as NodeOs from \"node:os\"\nimport * as NodePath from \"node:path\"\nimport * as ProcessSandbox from \"./ProcessSandbox.ts\"\n\n/**\n * How the confinement is built.\n *\n * @category models\n * @since 1.0.0-rc.1\n */\nexport interface Options {\n /**\n * What happens when this host has no mechanism to enforce a profile with.\n * `\"refuse\"` fails the spawn, so a command never runs with more than the\n * profile allows; `\"unconfined\"` runs it as given and logs a\n * warning naming what the host lacks.\n */\n readonly unavailable?: \"refuse\" | \"unconfined\" | undefined\n /** The host facts mechanism selection reads; defaults to the real host. */\n readonly host?: ProcessSandbox.Host | undefined\n /** Where each run's private temporary directory is created; defaults to the OS temporary directory. */\n readonly temporaryDirectory?: string | undefined\n}\n\nconst shellLine = (command: ChildProcess.StandardCommand): string => [command.command, ...command.args].join(\" \")\n\n/**\n * The argv a stage executes, with a shell request made explicit.\n *\n * Node runs a `shell: true` stage as `/bin/sh -c <line>` on POSIX, and a\n * string shell as `<shell> -c <line>`; the wrapper needs that program to be\n * the one it confines, so the shell becomes the argv's first token and the\n * spawned stage no longer asks for one.\n */\nconst argvOf = (command: ChildProcess.StandardCommand): ReadonlyArray<string> => {\n const shell = command.options.shell\n if (shell === undefined || shell === false) return [command.command, ...command.args]\n return [shell === true ? \"/bin/sh\" : shell, \"-c\", shellLine(command)]\n}\n\nconst failure = (\n tag: \"NotFound\" | \"PermissionDenied\" | \"Unknown\",\n command: ChildProcess.StandardCommand,\n description: string,\n cause: unknown\n): PlatformError.PlatformError =>\n PlatformError.systemError({\n _tag: tag,\n module: \"ProcessConfinement\",\n method: \"confine\",\n pathOrDescriptor: shellLine(command),\n description,\n cause\n })\n\nconst messageOf = (cause: unknown): string =>\n ProcessSandbox.isUnenforceable(cause)\n ? cause.message\n : cause instanceof Error\n ? cause.message\n : String(cause)\n\n/** Classic BPF for seccomp_data: deny host Unix sockets and io_uring, preserving stream socketpair.\n * Closed networking additionally denies every IP socket family.\n * Wrong ABI and x32 syscall encodings fail closed rather than bypassing socket(). */\nconst networkFilter = (architecture: string, network: \"none\" | \"open\"): Uint8Array => {\n const abi = architecture === \"x64\"\n ? { audit: 0xc000003e, socket: 41, socketpair: 53 }\n : architecture === \"arm64\"\n ? { audit: 0xc00000b7, socket: 198, socketpair: 199 }\n : undefined\n if (abi === undefined) throw new Error(`native-network seccomp is unavailable on ${architecture}`)\n const instructions: Array<readonly [number, number, number, number]> = [\n [0x20, 0, 0, 4], // load audit architecture\n [0x15, 1, 0, abi.audit],\n [0x06, 0, 0, 0x80000000], // kill unsupported ABI\n [0x20, 0, 0, 0], // load syscall number\n [0x35, 0, 1, 0x40000000],\n [0x06, 0, 0, 0x80000000], // kill x32 / invalid high syscall numbers\n [0x15, 0, 1, 425], // io_uring_setup on both supported ABIs\n [0x06, 0, 0, 0x00050001], // io_uring can otherwise create sockets without socket()\n [0x15, 0, 6, abi.socketpair],\n [0x20, 0, 0, 16], // socketpair domain\n [0x15, 0, 4, 1], // AF_UNIX\n [0x20, 0, 0, 24], // socketpair type, low 32 bits of args[1]\n [0x54, 0, 0, 0xf], // strip SOCK_NONBLOCK and SOCK_CLOEXEC\n [0x15, 1, 0, 1], // only SOCK_STREAM; Linux aliases SOCK_RAW to datagram operations\n [0x06, 0, 0, 0x00050001],\n [0x20, 0, 0, 0] // reload syscall number before the socket() policy\n ]\n if (network === \"none\") {\n instructions.push([0x15, 0, 1, abi.socket], [0x06, 0, 0, 0x00050001]) // EPERM for every socket family\n } else {\n instructions.push(\n [0x15, 0, 3, abi.socket],\n [0x20, 0, 0, 16], // socket domain, low 32 bits of args[0]\n [0x15, 0, 1, 1], // AF_UNIX; IP families remain available\n [0x06, 0, 0, 0x00050001]\n )\n }\n instructions.push([0x06, 0, 0, 0x7fff0000]) // SECCOMP_RET_ALLOW\n const bytes = new Uint8Array(instructions.length * 8)\n const view = new DataView(bytes.buffer)\n for (const [index, [code, jt, jf, value]] of instructions.entries()) {\n view.setUint16(index * 8, code, true)\n view.setUint8(index * 8 + 2, jt)\n view.setUint8(index * 8 + 3, jf)\n view.setUint32(index * 8 + 4, value, true)\n }\n return bytes\n}\n\n/**\n * Builds the native confinement.\n *\n * @category constructors\n * @since 1.0.0-rc.1\n */\nexport const make = (options: Options = {}): KernelProcessConfinement.Service => {\n const hostFacts = options.host ?? ProcessSandbox.host()\n const temporaryRoot = options.temporaryDirectory ?? NodeOs.tmpdir()\n const unavailable = options.unavailable ?? \"refuse\"\n const confine = Effect.fn(\"ProcessConfinement.confine\")(function*(\n command: ChildProcess.StandardCommand,\n profile: KernelProcessConfinement.Profile\n ): Effect.fn.Return<ChildProcess.StandardCommand, PlatformError.PlatformError, Scope.Scope> {\n const request: ProcessSandbox.Request = {\n network: profile.network,\n unixSockets: false,\n strict: true,\n reads: profile.reads,\n writes: profile.writes,\n readOnly: profile.readOnly\n }\n const selected = ProcessSandbox.select(request, hostFacts)\n if (ProcessSandbox.isUnenforceable(selected)) {\n if (unavailable === \"refuse\") {\n return yield* failure(\n \"NotFound\",\n command,\n `${selected.message} This host refuses to spawn unconfined.`,\n selected\n )\n }\n yield* Effect.logWarning(`${selected.message} Running unconfined.`)\n return command\n }\n // The stage's directory is absolute: the kernel spawner roots it first.\n const cwd = command.options.cwd ?? profile.workspaceRoot\n const within = (root: string, path: string): boolean => {\n const relative = NodePath.relative(root, path)\n return relative !== \"..\" && !relative.startsWith(`..${NodePath.sep}`) && !NodePath.isAbsolute(relative)\n }\n if (!within(profile.workspaceRoot, cwd)) {\n return yield* failure(\"PermissionDenied\", command, \"command directory is outside the workspace\", undefined)\n }\n // A lexical workspace path can enter an unrelated host directory through\n // a symlink. Check the real directory before granting or preparing a run.\n const canonical = yield* Effect.try({\n try: () => {\n const root = NodeFs.realpathSync(profile.workspaceRoot)\n const directory = NodeFs.realpathSync(cwd)\n if (!within(root, directory)) throw new Error(\"command directory is outside the workspace\")\n for (const relative of profile.reads) {\n const lexical = NodePath.resolve(root, relative)\n if (!within(root, lexical)) continue\n // A read bind follows its source symlink. Refuse a grant that would\n // therefore mount unrelated host bytes inside the workspace.\n if (NodeFs.existsSync(lexical)) {\n const real = NodeFs.realpathSync(lexical)\n if (!within(root, real)) throw new Error(\"read grant resolves outside the workspace\")\n if (real !== lexical) throw new Error(\"read grant is a symbolic link\")\n }\n }\n return { root, cwd: directory }\n },\n catch: (cause) => failure(\"PermissionDenied\", command, messageOf(cause), cause)\n })\n // The private tmp lives as long as the spawn's scope, however preparation\n // ends; the declared write directories are created so the mechanism has\n // something to bind, which the grant that opened them already permits.\n const allocatedTmp = yield* Effect.acquireRelease(\n Effect.try({\n try: () => NodeFs.mkdtempSync(NodePath.join(temporaryRoot, \"smithers-confinement-\")),\n catch: (cause) => failure(\"Unknown\", command, `could not prepare the confinement: ${messageOf(cause)}`, cause)\n }),\n (directory) =>\n Effect.sync(() => {\n try {\n NodeFs.rmSync(directory, { recursive: true, force: true })\n } catch {\n // Cleanup is best-effort when the host refuses directory removal.\n }\n })\n )\n const tmp = yield* Effect.try({\n try: () => NodeFs.realpathSync(allocatedTmp),\n catch: (cause) => failure(\"Unknown\", command, `could not prepare the confinement: ${messageOf(cause)}`, cause)\n })\n const planned = yield* Effect.try({\n try: () => ProcessSandbox.plan(request, { workspaceRoot: canonical.root, cwd: canonical.cwd, tmp }, hostFacts),\n catch: (cause) => failure(\"Unknown\", command, `could not plan the confinement: ${messageOf(cause)}`, cause)\n })\n if (ProcessSandbox.isUnenforceable(planned)) {\n return yield* failure(\"PermissionDenied\", command, planned.message, planned)\n }\n // Build outputs may grant their parent directory. An agent grant must\n // never inherit that widening when a requested tree is already a file.\n const exactWrites = profile.writes.map((relative) => NodePath.resolve(planned.workspaceRoot, relative))\n if (planned.writes.some((write) => !exactWrites.includes(write))) {\n return yield* failure(\"PermissionDenied\", command, \"write grant is not a directory tree\", undefined)\n }\n // A workspace link does not grant authority to its target on the host.\n const confinement: ProcessSandbox.Plan = { ...planned, externalReads: [] }\n yield* Effect.try({\n try: () => {\n ProcessSandbox.validateWrites(confinement, hostFacts)\n NodeFs.mkdirSync(NodePath.join(tmp, \"home\"), { recursive: true })\n NodeFs.mkdirSync(NodePath.join(tmp, \"cache\"), { recursive: true })\n for (const write of planned.writes) NodeFs.mkdirSync(write, { recursive: true })\n },\n catch: (cause) => failure(\"Unknown\", command, `could not prepare the confinement: ${messageOf(cause)}`, cause)\n })\n // Only the target receives its requested environment. Dynamic-loader and\n // other startup variables must never configure the host sandbox launcher.\n const targetEnvironment: Record<string, string> = {}\n const inherited = command.options.env === undefined || command.options.extendEnv === true\n for (const [name, value] of Object.entries(inherited ? process.env : {})) {\n // Bash exports functions as names such as BASH_FUNC_which%%. These\n // ambient entries cannot be exported by the portable inner launcher.\n if (typeof value === \"string\" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) targetEnvironment[name] = value\n }\n for (const [name, value] of Object.entries(command.options.env ?? {})) {\n if (typeof value === \"string\") targetEnvironment[name] = value\n else delete targetEnvironment[name]\n }\n const privateEnvironment = ProcessSandbox.environment(confinement)\n const script = yield* Effect.try({\n try: () => {\n const quote = (value: string): string => `'${value.replaceAll(\"'\", \"'\\\\''\")}'`\n const exports: Array<string> = []\n for (const [name, value] of Object.entries({ ...targetEnvironment, ...privateEnvironment })) {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) throw new Error(\"environment variable name is not portable\")\n if (value.includes(\"\\0\")) throw new Error(\"environment variable contains a null byte\")\n exports.push(`export ${name}=${quote(value)}`)\n }\n const file = NodePath.join(tmp, \"environment.sh\")\n // Values stay off argv and journal receipts; the script is private,\n // removed with the scope, and interpreted only inside confinement.\n NodeFs.writeFileSync(\n file,\n [\n \"#!/bin/sh\",\n \"unset ENV BASH_ENV CDPATH PWD OLDPWD SHLVL _\",\n ...exports,\n \"exec \\\"$@\\\"\",\n \"\"\n ].join(\"\\n\"),\n { flag: \"wx\", mode: 0o600 }\n )\n return file\n },\n catch: (cause) => failure(\"PermissionDenied\", command, messageOf(cause), cause)\n })\n // Resolve the actual program before wrapping it in the environment shell.\n // Otherwise the sandbox sees only /usr/bin/env and omits a native helper\n // installed outside its ordinary runtime paths.\n const originalArgv = argvOf(command)\n const launch = ProcessSandbox.launcher(\n originalArgv[0]!, targetEnvironment.PATH,\n [...confinement.reads, ...confinement.writes, ...confinement.externalReads],\n confinement.workspaceRoot, hostFacts\n )\n const targetArgv = [\"/usr/bin/env\", \"-i\", \"/bin/sh\", script, launch.program, ...originalArgv.slice(1)]\n const wrapped = yield* Effect.try({\n try: () =>\n ProcessSandbox.wrap({ ...confinement, externalReads: [...confinement.externalReads, ...launch.reads, script] }, targetArgv, targetEnvironment, hostFacts),\n catch: (cause) => failure(\"Unknown\", command, `could not render the confinement: ${messageOf(cause)}`, cause)\n })\n let [executable, ...args] = wrapped.argv\n if (selected._tag === \"bubblewrap\") {\n const filter = yield* Effect.try({\n try: () => {\n const file = NodePath.join(tmp, \"network.seccomp\")\n NodeFs.writeFileSync(file, networkFilter(process.arch, profile.network), { flag: \"wx\", mode: 0o600 })\n return file\n },\n catch: (cause) => failure(\"PermissionDenied\", command, messageOf(cause), cause)\n })\n let fd = 3\n while (command.options.additionalFds?.[`fd${fd}`] !== undefined) fd++\n // A regular file avoids a filter-input pipe whose early closure can\n // surface as an unhandled ECONNRESET before bwrap reports its refusal.\n args = [\n \"-c\",\n `exec ${fd}<\"$1\"; shift; exec \"$@\"`,\n \"smithers-seccomp\",\n filter,\n executable!,\n \"--seccomp\",\n String(fd),\n ...args\n ]\n executable = \"/bin/sh\"\n }\n return ChildProcess.make(executable!, args, {\n ...command.options,\n cwd: canonical.cwd,\n shell: false,\n env: { PATH: \"/usr/bin:/bin:/usr/sbin:/sbin\", LANG: \"C\", ...privateEnvironment },\n extendEnv: false\n })\n })\n return KernelProcessConfinement.ProcessConfinement.of({ confine })\n}\n\n/**\n * Provides the native confinement.\n *\n * @category layers\n * @since 1.0.0-rc.1\n */\nexport const layer = (options: Options = {}): Layer.Layer<KernelProcessConfinement.ProcessConfinement> =>\n Layer.succeed(KernelProcessConfinement.ProcessConfinement)(make(options))\n"]}
|
|
1
|
+
{"version":3,"file":"ProcessConfinement.js","sourceRoot":"","sources":["../../src/ProcessConfinement.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The kernel's `ProcessConfinement` seam, filled with this host's native\n * sandbox: bubblewrap on Linux, seatbelt on macOS.\n *\n * `@smthrs/kernel`'s `ChildProcessSpawner.layer` hands every stage that passed\n * its `proc:spawn` check to the confinement in its context, together with the\n * `ProcessConfinement.Profile` the grants in force admit. This module turns\n * that profile into a {@link ProcessSandbox.Request}, plans it against the\n * workspace, and wraps the stage's argv in the mechanism the host has, so an\n * approved `bash` can write only where an `fs:write` grant says and reach the\n * network only when a `net:*` grant says. A stage that asked for the platform\n * shell is spelled out as `/bin/sh -c <line>` first: wrapping a `shell: true`\n * command line would otherwise leave its redirections and pipes to a shell\n * outside the sandbox.\n *\n * A host with no mechanism is the one decision this module leaves to its\n * caller: {@link Options.unavailable} either refuses the spawn, so nothing\n * runs that the profile would not have confined, or runs it unconfined with\n * a warning, the way every host did before this seam existed. A write set\n * the mechanism cannot enforce safely, a symbolic link below the workspace\n * root, is always refused.\n *\n * @since 1.0.0-rc.1\n */\n\nimport * as KernelProcessConfinement from \"@smthrs/kernel/ProcessConfinement\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as PlatformError from \"effect/PlatformError\"\nimport type * as Scope from \"effect/Scope\"\nimport * as ChildProcess from \"effect/unstable/process/ChildProcess\"\nimport * as NodeFs from \"node:fs\"\nimport * as NodeOs from \"node:os\"\nimport * as NodePath from \"node:path\"\nimport * as ProcessSandbox from \"./ProcessSandbox.ts\"\n\n/**\n * How the confinement is built.\n *\n * @category models\n * @since 1.0.0-rc.1\n */\nexport interface Options {\n /**\n * What happens when this host has no mechanism to enforce a profile with.\n * `\"refuse\"` fails the spawn, so a command never runs with more than the\n * profile allows; `\"unconfined\"` runs it as given and logs a\n * warning naming what the host lacks.\n */\n readonly unavailable?: \"refuse\" | \"unconfined\" | undefined\n /** The host facts mechanism selection reads; defaults to the real host. */\n readonly host?: ProcessSandbox.Host | undefined\n /** Where each run's private temporary directory is created; defaults to the OS temporary directory. */\n readonly temporaryDirectory?: string | undefined\n}\n\nconst shellLine = (command: ChildProcess.StandardCommand): string => [command.command, ...command.args].join(\" \")\n\n/**\n * The argv a stage executes, with a shell request made explicit.\n *\n * Node runs a `shell: true` stage as `/bin/sh -c <line>` on POSIX, and a\n * string shell as `<shell> -c <line>`; the wrapper needs that program to be\n * the one it confines, so the shell becomes the argv's first token and the\n * spawned stage no longer asks for one.\n */\nconst argvOf = (command: ChildProcess.StandardCommand): ReadonlyArray<string> => {\n const shell = command.options.shell\n if (shell === undefined || shell === false) return [command.command, ...command.args]\n return [shell === true ? \"/bin/sh\" : shell, \"-c\", shellLine(command)]\n}\n\nconst failure = (\n tag: \"NotFound\" | \"PermissionDenied\" | \"Unknown\",\n command: ChildProcess.StandardCommand,\n description: string,\n cause: unknown\n): PlatformError.PlatformError =>\n PlatformError.systemError({\n _tag: tag,\n module: \"ProcessConfinement\",\n method: \"confine\",\n pathOrDescriptor: shellLine(command),\n description,\n cause\n })\n\nconst messageOf = (cause: unknown): string =>\n ProcessSandbox.isUnenforceable(cause)\n ? cause.message\n : cause instanceof Error\n ? cause.message\n : String(cause)\n\n/** Classic BPF for seccomp_data: deny host Unix sockets and io_uring, preserving stream socketpair.\n * Closed networking additionally denies every IP socket family.\n * Wrong ABI and x32 syscall encodings fail closed rather than bypassing socket(). */\nconst networkFilter = (architecture: string, network: \"none\" | \"open\"): Uint8Array => {\n const abi = architecture === \"x64\"\n ? { audit: 0xc000003e, socket: 41, socketpair: 53 }\n : architecture === \"arm64\"\n ? { audit: 0xc00000b7, socket: 198, socketpair: 199 }\n : undefined\n if (abi === undefined) throw new Error(`native-network seccomp is unavailable on ${architecture}`)\n const instructions: Array<readonly [number, number, number, number]> = [\n [0x20, 0, 0, 4], // load audit architecture\n [0x15, 1, 0, abi.audit],\n [0x06, 0, 0, 0x80000000], // kill unsupported ABI\n [0x20, 0, 0, 0], // load syscall number\n [0x35, 0, 1, 0x40000000],\n [0x06, 0, 0, 0x80000000], // kill x32 / invalid high syscall numbers\n [0x15, 0, 1, 425], // io_uring_setup on both supported ABIs\n [0x06, 0, 0, 0x00050001], // io_uring can otherwise create sockets without socket()\n [0x15, 0, 6, abi.socketpair],\n [0x20, 0, 0, 16], // socketpair domain\n [0x15, 0, 4, 1], // AF_UNIX\n [0x20, 0, 0, 24], // socketpair type, low 32 bits of args[1]\n [0x54, 0, 0, 0xf], // strip SOCK_NONBLOCK and SOCK_CLOEXEC\n [0x15, 1, 0, 1], // only SOCK_STREAM; Linux aliases SOCK_RAW to datagram operations\n [0x06, 0, 0, 0x00050001],\n [0x20, 0, 0, 0] // reload syscall number before the socket() policy\n ]\n if (network === \"none\") {\n instructions.push([0x15, 0, 1, abi.socket], [0x06, 0, 0, 0x00050001]) // EPERM for every socket family\n } else {\n instructions.push(\n [0x15, 0, 3, abi.socket],\n [0x20, 0, 0, 16], // socket domain, low 32 bits of args[0]\n [0x15, 0, 1, 1], // AF_UNIX; IP families remain available\n [0x06, 0, 0, 0x00050001]\n )\n }\n instructions.push([0x06, 0, 0, 0x7fff0000]) // SECCOMP_RET_ALLOW\n const bytes = new Uint8Array(instructions.length * 8)\n const view = new DataView(bytes.buffer)\n for (const [index, [code, jt, jf, value]] of instructions.entries()) {\n view.setUint16(index * 8, code, true)\n view.setUint8(index * 8 + 2, jt)\n view.setUint8(index * 8 + 3, jf)\n view.setUint32(index * 8 + 4, value, true)\n }\n return bytes\n}\n\n/**\n * Builds the native confinement.\n *\n * @category constructors\n * @since 1.0.0-rc.1\n */\nexport const make = (options: Options = {}): KernelProcessConfinement.Service => {\n const hostFacts = options.host ?? ProcessSandbox.host()\n const temporaryRoot = options.temporaryDirectory ?? NodeOs.tmpdir()\n const unavailable = options.unavailable ?? \"refuse\"\n const confine = Effect.fn(\"ProcessConfinement.confine\")(function*(\n command: ChildProcess.StandardCommand,\n profile: KernelProcessConfinement.Profile\n ): Effect.fn.Return<ChildProcess.StandardCommand, PlatformError.PlatformError, Scope.Scope> {\n const request: ProcessSandbox.Request = {\n network: profile.network,\n unixSockets: false,\n strict: true,\n reads: profile.reads,\n writes: profile.writes,\n readOnly: profile.readOnly\n }\n const selected = ProcessSandbox.select(request, hostFacts)\n if (ProcessSandbox.isUnenforceable(selected)) {\n if (unavailable === \"refuse\") {\n return yield* failure(\n \"NotFound\",\n command,\n `${selected.message} This host refuses to spawn unconfined.`,\n selected\n )\n }\n yield* Effect.logWarning(`${selected.message} Running unconfined.`)\n return command\n }\n // The stage's directory is absolute: the kernel spawner roots it first.\n const cwd = command.options.cwd ?? profile.workspaceRoot\n const within = (root: string, path: string): boolean => {\n const relative = NodePath.relative(root, path)\n return relative !== \"..\" && !relative.startsWith(`..${NodePath.sep}`) && !NodePath.isAbsolute(relative)\n }\n if (!within(profile.workspaceRoot, cwd)) {\n return yield* failure(\"PermissionDenied\", command, \"command directory is outside the workspace\", undefined)\n }\n // A lexical workspace path can enter an unrelated host directory through\n // a symlink. Check the real directory before granting or preparing a run.\n const canonical = yield* Effect.try({\n try: () => {\n const root = NodeFs.realpathSync(profile.workspaceRoot)\n const directory = NodeFs.realpathSync(cwd)\n if (!within(root, directory)) throw new Error(\"command directory is outside the workspace\")\n for (const relative of profile.reads) {\n const lexical = NodePath.resolve(root, relative)\n if (!within(root, lexical)) continue\n // A read bind follows its source symlink. Refuse a grant that would\n // therefore mount unrelated host bytes inside the workspace.\n if (NodeFs.existsSync(lexical)) {\n const real = NodeFs.realpathSync(lexical)\n if (!within(root, real)) throw new Error(\"read grant resolves outside the workspace\")\n if (real !== lexical) throw new Error(\"read grant is a symbolic link\")\n }\n }\n return { root, cwd: directory }\n },\n catch: (cause) => failure(\"PermissionDenied\", command, messageOf(cause), cause)\n })\n // The private tmp lives as long as the spawn's scope, however preparation\n // ends; the declared write directories are created so the mechanism has\n // something to bind, which the grant that opened them already permits.\n const allocatedTmp = yield* Effect.acquireRelease(\n Effect.try({\n try: () => NodeFs.mkdtempSync(NodePath.join(temporaryRoot, \"smithers-confinement-\")),\n catch: (cause) => failure(\"Unknown\", command, `could not prepare the confinement: ${messageOf(cause)}`, cause)\n }),\n (directory) =>\n Effect.sync(() => {\n try {\n NodeFs.rmSync(directory, { recursive: true, force: true })\n } catch {\n // Cleanup is best-effort when the host refuses directory removal.\n }\n })\n )\n const tmp = yield* Effect.try({\n try: () => NodeFs.realpathSync(allocatedTmp),\n catch: (cause) => failure(\"Unknown\", command, `could not prepare the confinement: ${messageOf(cause)}`, cause)\n })\n const planned = yield* Effect.try({\n try: () => ProcessSandbox.plan(request, { workspaceRoot: canonical.root, cwd: canonical.cwd, tmp }, hostFacts),\n catch: (cause) => failure(\"Unknown\", command, `could not plan the confinement: ${messageOf(cause)}`, cause)\n })\n if (ProcessSandbox.isUnenforceable(planned)) {\n return yield* failure(\"PermissionDenied\", command, planned.message, planned)\n }\n // Build outputs may grant their parent directory. An agent grant must\n // never inherit that widening when a requested tree is already a file.\n const exactWrites = profile.writes.map((relative) => NodePath.resolve(planned.workspaceRoot, relative))\n if (planned.writes.some((write) => !exactWrites.includes(write))) {\n return yield* failure(\"PermissionDenied\", command, \"write grant is not a directory tree\", undefined)\n }\n // A workspace link does not grant authority to its target on the host.\n const confinement: ProcessSandbox.Plan = { ...planned, externalReads: [] }\n yield* Effect.try({\n try: () => {\n ProcessSandbox.validateWrites(confinement, hostFacts)\n NodeFs.mkdirSync(NodePath.join(tmp, \"home\"), { recursive: true })\n NodeFs.mkdirSync(NodePath.join(tmp, \"cache\"), { recursive: true })\n for (const write of planned.writes) NodeFs.mkdirSync(write, { recursive: true })\n },\n catch: (cause) => failure(\"Unknown\", command, `could not prepare the confinement: ${messageOf(cause)}`, cause)\n })\n // Only the target receives its requested environment. Dynamic-loader and\n // other startup variables must never configure the host sandbox launcher.\n const targetEnvironment: Record<string, string> = {}\n const inherited = command.options.env === undefined || command.options.extendEnv === true\n for (const [name, value] of Object.entries(inherited ? process.env : {})) {\n // Bash exports functions as names such as BASH_FUNC_which%%. These\n // ambient entries cannot be exported by the portable inner launcher.\n if (typeof value === \"string\" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) targetEnvironment[name] = value\n }\n for (const [name, value] of Object.entries(command.options.env ?? {})) {\n if (typeof value === \"string\") targetEnvironment[name] = value\n else delete targetEnvironment[name]\n }\n const privateEnvironment = ProcessSandbox.environment(confinement)\n const script = yield* Effect.try({\n try: () => {\n const quote = (value: string): string => `'${value.replaceAll(\"'\", \"'\\\\''\")}'`\n const exports: Array<string> = []\n for (const [name, value] of Object.entries({ ...targetEnvironment, ...privateEnvironment })) {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) throw new Error(\"environment variable name is not portable\")\n if (value.includes(\"\\0\")) throw new Error(\"environment variable contains a null byte\")\n exports.push(`export ${name}=${quote(value)}`)\n }\n const file = NodePath.join(tmp, \"environment.sh\")\n // Values stay off argv and journal receipts; the script is private,\n // removed with the scope, and interpreted only inside confinement.\n NodeFs.writeFileSync(\n file,\n [\n \"#!/bin/sh\",\n \"unset ENV BASH_ENV CDPATH PWD OLDPWD SHLVL _\",\n ...exports,\n \"exec \\\"$@\\\"\",\n \"\"\n ].join(\"\\n\"),\n { flag: \"wx\", mode: 0o600 }\n )\n return file\n },\n catch: (cause) => failure(\"PermissionDenied\", command, messageOf(cause), cause)\n })\n // Resolve the actual program before wrapping it in the environment shell.\n // Otherwise the sandbox sees only /usr/bin/env and omits a native helper\n // installed outside its ordinary runtime paths.\n const originalArgv = argvOf(command)\n const launch = ProcessSandbox.launcher(\n originalArgv[0]!,\n targetEnvironment.PATH,\n [...confinement.reads, ...confinement.writes, ...confinement.externalReads],\n confinement.workspaceRoot,\n hostFacts\n )\n const targetArgv = [\"/usr/bin/env\", \"-i\", \"/bin/sh\", script, launch.program, ...originalArgv.slice(1)]\n const wrapped = yield* Effect.try({\n try: () =>\n ProcessSandbox.wrap(\n { ...confinement, externalReads: [...confinement.externalReads, ...launch.reads, script] },\n targetArgv,\n targetEnvironment,\n hostFacts\n ),\n catch: (cause) => failure(\"Unknown\", command, `could not render the confinement: ${messageOf(cause)}`, cause)\n })\n let [executable, ...args] = wrapped.argv\n if (selected._tag === \"bubblewrap\") {\n const filter = yield* Effect.try({\n try: () => {\n const file = NodePath.join(tmp, \"network.seccomp\")\n NodeFs.writeFileSync(file, networkFilter(process.arch, profile.network), { flag: \"wx\", mode: 0o600 })\n return file\n },\n catch: (cause) => failure(\"PermissionDenied\", command, messageOf(cause), cause)\n })\n let fd = 3\n while (command.options.additionalFds?.[`fd${fd}`] !== undefined) fd++\n // A regular file avoids a filter-input pipe whose early closure can\n // surface as an unhandled ECONNRESET before bwrap reports its refusal.\n args = [\n \"-c\",\n `exec ${fd}<\"$1\"; shift; exec \"$@\"`,\n \"smithers-seccomp\",\n filter,\n executable!,\n \"--seccomp\",\n String(fd),\n ...args\n ]\n executable = \"/bin/sh\"\n }\n return ChildProcess.make(executable!, args, {\n ...command.options,\n cwd: canonical.cwd,\n shell: false,\n env: { PATH: \"/usr/bin:/bin:/usr/sbin:/sbin\", LANG: \"C\", ...privateEnvironment },\n extendEnv: false\n })\n })\n return KernelProcessConfinement.ProcessConfinement.of({ confine })\n}\n\n/**\n * Provides the native confinement.\n *\n * @category layers\n * @since 1.0.0-rc.1\n */\nexport const layer = (options: Options = {}): Layer.Layer<KernelProcessConfinement.ProcessConfinement> =>\n Layer.succeed(KernelProcessConfinement.ProcessConfinement)(make(options))\n"]}
|
|
@@ -128,6 +128,12 @@ export interface Host {
|
|
|
128
128
|
* paths. Read aliases are only collected when this probe is supplied.
|
|
129
129
|
*/
|
|
130
130
|
readonly realpath?: ((path: string) => string | undefined) | undefined;
|
|
131
|
+
/**
|
|
132
|
+
* The program a package-manager shim script runs: the absolute path on the
|
|
133
|
+
* `# cmd-shim-target=` line pnpm's cmd-shim writes, or `undefined` for any
|
|
134
|
+
* other file. Optional: a host without it treats a shim as the program.
|
|
135
|
+
*/
|
|
136
|
+
readonly shimTarget?: ((path: string) => string | undefined) | undefined;
|
|
131
137
|
readonly uid: number | undefined;
|
|
132
138
|
readonly gid: number | undefined;
|
|
133
139
|
}
|
|
@@ -293,8 +299,6 @@ export declare const environment: (confinement: Plan, hostEnv?: Readonly<Record<
|
|
|
293
299
|
/**
|
|
294
300
|
* The program a confined run execs, made reachable inside its empty root.
|
|
295
301
|
*
|
|
296
|
-
* @internal
|
|
297
|
-
*
|
|
298
302
|
* Bubblewrap resolves a bare program name with `execvp` against `PATH` inside
|
|
299
303
|
* the new root, and that root holds only the runtime paths and the declared
|
|
300
304
|
* sets. A package manager installed outside them — pnpm under the
|
|
@@ -311,6 +315,16 @@ export declare const environment: (confinement: Plan, hostEnv?: Readonly<Record<
|
|
|
311
315
|
* is bound read-only, because a script like `pnpm.cjs` loads its siblings.
|
|
312
316
|
* With no package directory only the real file is bound. A name that resolves
|
|
313
317
|
* nowhere is left for `execvp` to report.
|
|
318
|
+
*
|
|
319
|
+
* A package-manager shim script is followed like a link. `pnpm self-update`
|
|
320
|
+
* under `pnpm/action-setup` installs pnpm as a cmd-shim in `$PNPM_HOME/bin`
|
|
321
|
+
* that runs node on a package in pnpm's global store, a path relative to the
|
|
322
|
+
* script; binding the script alone left that package missing. When the
|
|
323
|
+
* program the shim names is executable, the run execs its real file and binds
|
|
324
|
+
* its package as above.
|
|
325
|
+
*
|
|
326
|
+
* @since 1.0.0
|
|
327
|
+
* @private
|
|
314
328
|
*/
|
|
315
329
|
export declare const launcher: (program: string, searchPath: string | undefined, granted: ReadonlyArray<string>, workspaceRoot: string, hostFacts: Host) => {
|
|
316
330
|
readonly program: string;
|