UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

1,984 lines • 66.2 kB
import { Socket, createConnection, createServer } from "node:net";
import { spawn, spawnSync } from "node:child_process";
import fs, { opendirSync, readFileSync, readdirSync } from "node:fs";
import path from "node:path";
import { tmpdir } from "node:os";
import { isDeepStrictEqual, stripVTControlCharacters } from "node:util";
import { execa } from "execa";
import { EventEmitter, once } from "node:events";
import { mkdtemp, rm } from "node:fs/promises";
import { Transform } from "node:stream";
import { deserialize, serialize } from "node:v8";
//#region packages/normalization-core/src/error-coercion.ts
function readProperty(value, key) {
	try {
		return value[key];
	} catch {
		return;
	}
}
/**
* Normalizes an unknown thrown value into an Error. Non-Error objects become
* the `cause` and have their enumerable fields copied so structured details
* (codes, statuses) survive the coercion.
*/
function toErrorObject(value, fallbackMessage) {
	if (value instanceof Error) return value;
	if (typeof value === "string") return new Error(value);
	const error = new Error(fallbackMessage, { cause: value });
	if (typeof value === "object" && value !== null || typeof value === "function") Object.assign(error, value);
	return error;
}
function extractErrorCode(err) {
	if (!err || typeof err !== "object") return;
	const code = readProperty(err, "code");
	return typeof code === "string" || typeof code === "number" ? String(code) : void 0;
}
//#endregion
//#region packages/normalization-core/src/record-coerce.ts
/** Type guard for non-array object records at browser-safe boundaries. */
function isRecord(value) {
	return value !== null && typeof value === "object" && !Array.isArray(value);
}
//#endregion
//#region packages/agent-core/src/harness/env/kill-tree.ts
const DEFAULT_GRACE_MS = 3e3;
const MAX_GRACE_MS = 6e4;
const TASKKILL_COMPLETION_TIMEOUT_MS = 3e3;
const UNIX_PROCESS_TREE_TIMEOUT_MS = 500;
const MAX_UNIX_PROCESS_TREE_PIDS = 4096;
const MAX_UNIX_PROCESS_TREE_DEPTH = 128;
/**
* Best-effort process-tree termination with graceful shutdown.
* - Windows: use taskkill /T to include descendants. Sends SIGTERM-equivalent
*   first (without /F), then force-kills if taskkill refuses or the process
*   survives the grace period.
* - Unix: send SIGTERM to the process group when ownership is known, wait the
*   grace period, then SIGKILL. Linux attached children are enumerated and
*   signaled descendants-first because their process group belongs to the gateway.
*
* Group kill (`process.kill(-pid, ...)`) is only used when the PID is verified
* as its own process group leader, unless `detached: true` is explicitly passed.
* This prevents accidentally signaling the gateway's process group when the
* child shares its parent's group.
*
* - `detached: false`: skip group kill unconditionally.
* - `detached: true`: use group kill unconditionally (trust caller).
* - `detached` omitted: use group kill only when PID is the group leader.
*/
function killProcessTree(pid, opts) {
	if (!Number.isFinite(pid) || pid <= 0) return;
	if (process.platform === "win32") {
		if (opts?.force === true) {
			signalProcessTreeWindowsAndWait(pid, "SIGKILL");
			return;
		}
		killProcessTreeWindows(pid, normalizeGraceMs(opts?.graceMs));
		return;
	}
	const useGroupKill = opts?.detached === true || opts?.detached !== false && isProcessGroupLeader(pid);
	const attachedLinuxTree = opts?.detached === false && !useGroupKill && process.platform === "linux";
	const processTree = attachedLinuxTree ? collectUnixProcessTree(pid) : void 0;
	if (attachedLinuxTree && !processTree) {
		signalProcessTreeUnix(pid, opts?.force === true ? "SIGKILL" : "SIGTERM", false);
		return;
	}
	let forceRequested = false;
	const force = () => {
		if (forceRequested) return;
		forceRequested = true;
		const liveProcessTree = processTree?.filter(verifiedProcessInstanceAlive) ?? [];
		if (!(useGroupKill ? isProcessAlive(-pid) : attachedLinuxTree ? liveProcessTree.length > 0 : isProcessAlive(pid))) return;
		signalProcessTreeUnix(pid, "SIGKILL", useGroupKill, processTree ? liveProcessTree : void 0);
	};
	if (opts?.force === true) {
		if (processTree) force();
		else signalProcessTreeUnix(pid, "SIGKILL", useGroupKill);
		return;
	}
	signalProcessTreeUnix(pid, "SIGTERM", useGroupKill, processTree);
	const graceMs = normalizeGraceMs(opts?.graceMs);
	const forceTimer = setTimeout(force, graceMs);
	if (!attachedLinuxTree) forceTimer.unref();
	return { force: () => {
		clearTimeout(forceTimer);
		force();
	} };
}
function normalizeGraceMs(value) {
	if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_GRACE_MS;
	return Math.max(0, Math.min(MAX_GRACE_MS, Math.floor(value)));
}
function isProcessAlive(pid) {
	try {
		process.kill(pid, 0);
	} catch (error) {
		if ((typeof error === "object" && error !== null && "code" in error ? error.code : void 0) !== "EPERM") return false;
	}
	if (pid < 0 || process.platform !== "linux") return true;
	try {
		const status = readFileSync(`/proc/${pid}/status`, "utf8");
		return !(status.match(/^State:\s+(\S)/m)?.[1] === "Z" && /^Threads:[ \t]+1[ \t]*$/m.test(status));
	} catch {
		return true;
	}
}
function parseProcessGroupId(value) {
	if (typeof value !== "string" || !/^\d+$/.test(value.trim())) return;
	const pgid = Number(value.trim());
	return Number.isSafeInteger(pgid) && pgid > 0 ? pgid : void 0;
}
function readPsOutput(args) {
	try {
		const result = spawnSync("ps", args, {
			encoding: "utf8",
			timeout: 500
		});
		return result.error || result.status !== 0 ? void 0 : result.stdout;
	} catch {
		return;
	}
}
function readProcStatFields(pid) {
	try {
		const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
		const commEnd = stat.lastIndexOf(")");
		if (commEnd < 0) return;
		return stat.slice(commEnd + 1).trim().split(/\s+/);
	} catch {
		return;
	}
}
/** Fail closed to direct-PID signaling when group ownership cannot be proved. */
function isProcessGroupLeader(pid) {
	return ((process.platform === "linux" ? parseProcessGroupId(readProcStatFields(pid)?.[2]) : void 0) ?? parseProcessGroupId(readPsOutput([
		"-p",
		String(pid),
		"-o",
		"pgid="
	]))) === pid;
}
function parsePositivePids(value) {
	if (typeof value !== "string") return [];
	return value.trim().split(/\s+/u).map((entry) => Number(entry)).filter((entry) => Number.isSafeInteger(entry) && entry > 0);
}
function readUnixProcessIdentity(pid, deadlineMs) {
	return readUnixProcessInstance(pid, deadlineMs)?.identity;
}
/** Capture identity and parent together so a recycled child PID cannot authorize a foreign tree. */
function readUnixProcessInstance(pid, deadlineMs) {
	if (deadlineMs !== void 0 && Date.now() >= deadlineMs) return;
	if (process.platform !== "linux") return;
	const fields = readProcStatFields(pid);
	const ppid = Number(fields?.[1]);
	const starttime = fields?.[19];
	if (!starttime || !/^\d+$/u.test(starttime) || !Number.isSafeInteger(ppid)) return;
	return {
		identity: `${pid}:${starttime}`,
		ppid
	};
}
/** Missing identities are never eligible for an attached-tree signal. */
function verifiedProcessInstanceAlive(entry) {
	if (!entry.identity || !isProcessAlive(entry.pid)) return false;
	return readUnixProcessIdentity(entry.pid) === entry.identity;
}
function* readUnixProcessChildren(pid, canReadTask) {
	let tasks;
	try {
		tasks = opendirSync(`/proc/${pid}/task`);
		while (canReadTask()) {
			const task = tasks.readSync();
			if (!task) return;
			const tid = parseProcessGroupId(task.name);
			if (!tid) continue;
			try {
				yield* parsePositivePids(readFileSync(`/proc/${pid}/task/${tid}/children`, "utf8"));
			} catch {}
		}
	} catch {} finally {
		try {
			tasks?.closeSync();
		} catch {}
	}
}
/** Capture verified descendants before their parents; an unverified subtree is omitted. */
function collectUnixProcessTree(rootPid) {
	if (process.platform !== "linux") return;
	const descendants = [];
	const seen = /* @__PURE__ */ new Set([rootPid]);
	let taskReads = 0;
	const deadline = Date.now() + UNIX_PROCESS_TREE_TIMEOUT_MS;
	const rootIdentity = readUnixProcessIdentity(rootPid, deadline);
	if (!rootIdentity || Date.now() >= deadline) return;
	const withinBounds = (depth) => Date.now() < deadline && depth <= MAX_UNIX_PROCESS_TREE_DEPTH;
	const hasCapacity = () => seen.size < MAX_UNIX_PROCESS_TREE_PIDS;
	const visit = (parentPid, parentIdentity, depth) => {
		if (!withinBounds(depth) || !hasCapacity()) return;
		if (readUnixProcessIdentity(parentPid, deadline) !== parentIdentity) return;
		const children = readUnixProcessChildren(parentPid, () => withinBounds(depth) && hasCapacity() && taskReads++ < MAX_UNIX_PROCESS_TREE_PIDS && readUnixProcessIdentity(parentPid, deadline) === parentIdentity);
		for (const childPid of children) {
			if (!withinBounds(depth + 1) || !hasCapacity()) return;
			if (seen.has(childPid) || childPid === process.pid) continue;
			seen.add(childPid);
			const instance = readUnixProcessInstance(childPid, deadline);
			if (!instance || instance.ppid !== parentPid || !withinBounds(depth + 1)) continue;
			visit(childPid, instance.identity, depth + 1);
			descendants.push({
				pid: childPid,
				identity: instance.identity
			});
		}
	};
	visit(rootPid, rootIdentity, 0);
	return [...descendants, {
		pid: rootPid,
		identity: rootIdentity
	}];
}
function signalProcessTreeUnix(pid, signal, useGroupKill, processTree) {
	if (useGroupKill) {
		signalUnixTarget(-pid, signal);
		return;
	}
	const targets = processTree ?? [{ pid }];
	for (const entry of targets) {
		if (processTree && !verifiedProcessInstanceAlive(entry)) continue;
		signalUnixTarget(entry.pid, signal);
	}
}
function signalUnixTarget(pid, signal) {
	try {
		process.kill(pid, signal);
	} catch {}
}
function runTaskkill(args, onExit) {
	return new Promise((resolve) => {
		let settled = false;
		const finish = (code) => {
			if (settled) return;
			settled = true;
			clearTimeout(completionTimer);
			onExit?.(code);
			resolve();
		};
		const completionTimer = setTimeout(() => finish(null), TASKKILL_COMPLETION_TIMEOUT_MS);
		completionTimer.unref?.();
		try {
			const child = spawn("taskkill", args, {
				stdio: "ignore",
				detached: true,
				windowsHide: true
			});
			child.once("error", () => finish(null));
			child.once("close", (code) => finish(code));
		} catch {
			finish(null);
		}
	});
}
function killProcessTreeWindows(pid, graceMs) {
	let forced = false;
	let graceTimer;
	const forceKill = () => {
		if (forced) return;
		forced = true;
		if (graceTimer !== void 0) {
			clearTimeout(graceTimer);
			graceTimer = void 0;
		}
		if (!isProcessAlive(pid)) return;
		signalProcessTreeWindowsAndWait(pid, "SIGKILL");
	};
	signalProcessTreeWindowsAndWait(pid, "SIGTERM", (code) => {
		if (code !== null && code !== 0) forceKill();
	});
	graceTimer = setTimeout(forceKill, graceMs);
	graceTimer.unref();
}
function signalProcessTreeWindowsAndWait(pid, signal, onExit) {
	return runTaskkill(signal === "SIGKILL" ? [
		"/F",
		"/T",
		"/PID",
		String(pid)
	] : [
		"/T",
		"/PID",
		String(pid)
	], onExit);
}
//#endregion
//#region src/process/linux-oom-score.ts
const OOM_SCORE_WRAP_SHELL = "/bin/sh";
const OOM_SCORE_WRAP_SCRIPT = "echo 1000 > /proc/self/oom_score_adj 2>/dev/null; exec \"$0\" \"$@\"";
[
	"echo 1000 > /proc/self/oom_score_adj 2>/dev/null; if [ \"${OC_INTERNAL_OOM_EXEC_BASH_ENV+x}\" = x ]; then BASH_ENV=\"$OC_INTERNAL_OOM_EXEC_BASH_ENV\"; export BASH_ENV; fi; unset OC_INTERNAL_OOM_EXEC_BASH_ENV",
	"if [ \"${OC_INTERNAL_OOM_EXEC_ENV+x}\" = x ]; then ENV=\"$OC_INTERNAL_OOM_EXEC_ENV\"; export ENV; fi; unset OC_INTERNAL_OOM_EXEC_ENV",
	"if [ \"${OC_INTERNAL_OOM_EXEC_CDPATH+x}\" = x ]; then CDPATH=\"$OC_INTERNAL_OOM_EXEC_CDPATH\"; export CDPATH; fi; unset OC_INTERNAL_OOM_EXEC_CDPATH",
	"if [ \"${OC_INTERNAL_OOM_EXEC_PS4+x}\" = x ]; then PS4=\"$OC_INTERNAL_OOM_EXEC_PS4\"; export PS4; fi; unset OC_INTERNAL_OOM_EXEC_PS4; exec \"$0\" \"$@\""
].join("; ");
function isWrapped(command, args) {
	return command === OOM_SCORE_WRAP_SHELL && args[0] === "-c" && args[1] === OOM_SCORE_WRAP_SCRIPT;
}
/** Only small spawn owners may temporarily raise their score; never call from the Gateway. */
function spawnWithInheritedOomScore(command, args, options) {
	if (process.platform !== "linux" || !isWrapped(command, args) || !args[2] || options.shell || options.argv0 !== void 0) return spawn(command, args, options);
	let scoreFd;
	let original;
	try {
		scoreFd = fs.openSync("/proc/self/oom_score_adj", "r+");
		original = fs.readFileSync(scoreFd, "utf8").trim();
		fs.writeSync(scoreFd, "1000", 0, "utf8");
	} catch {
		if (scoreFd !== void 0) fs.closeSync(scoreFd);
		return spawn(command, args, options);
	}
	try {
		return spawn(args[2], args.slice(3), options);
	} finally {
		try {
			fs.writeSync(scoreFd, original, 0, "utf8");
		} catch {
			process.emitWarning("Could not restore spawn owner's Linux OOM score");
		} finally {
			fs.closeSync(scoreFd);
		}
	}
}
//#endregion
//#region src/process/supervisor/cancellation-policy.ts
const GRACEFUL_CANCEL_TIMEOUT_MS = 5e3;
//#endregion
//#region src/shared/pid-alive.ts
function isValidPid(pid) {
	return Number.isInteger(pid) && pid > 0;
}
/**
* Check whether a Linux task is terminal and has no live sibling threads.
* Missing or reaped procfs snapshots require a fresh existence probe.
*/
function isExitedLinuxProcess(pid) {
	if (process.platform !== "linux") return false;
	try {
		const status = fs.readFileSync(`/proc/${pid}/status`, "utf8");
		const state = status.match(/^State:\s+(\S)/m)?.[1];
		const threads = status.match(/^Threads:[ \t]+(\d+)[ \t]*$/m)?.[1];
		if (threads !== "0") return (state === "Z" || state === "X") && threads === "1";
	} catch {}
	try {
		process.kill(pid, 0);
	} catch (error) {
		return error.code === "ESRCH";
	}
	return false;
}
/** Returns true only when the PID is invalid, missing, or a known terminal Linux task. */
function isPidDefinitelyDead(pid) {
	if (!isValidPid(pid)) return true;
	try {
		process.kill(pid, 0);
	} catch (err) {
		return err.code === "ESRCH";
	}
	return isExitedLinuxProcess(pid);
}
//#endregion
//#region src/process/supervisor/service-child-group-ownership.ts
function readLinuxProcessUid(pid) {
	try {
		const lines = readFileSync(`/proc/${pid}/status`, "utf8").split("\n").filter((line) => line.startsWith("Uid:"));
		const ids = (lines.length === 1 ? /^Uid:[ \t]+(\d+)[ \t]+(\d+)[ \t]+(\d+)[ \t]+(\d+)[ \t]*$/.exec(lines[0]) : null)?.slice(1).map(Number);
		const inspectorUid = process.getuid?.();
		return ids?.every((uid) => Number.isSafeInteger(uid) && uid <= 4294967295) ? ids.find((uid) => uid === inspectorUid) ?? ids[0] : void 0;
	} catch {
		return;
	}
}
/** The caller supplies native command inspection; the standalone group worker stays dependency-free. */
function* readProcessGroupMembers(timeoutMs, commandInspection) {
	const includeCommand = commandInspection !== void 0;
	if (process.platform === "linux") {
		const deadline = Date.now() + timeoutMs;
		for (const name of readdirSync("/proc")) {
			if (Date.now() >= deadline) throw new Error("Process group census exceeded its deadline");
			if (!/^\d+$/.test(name)) continue;
			const pid = Number(name);
			let stat;
			let argv;
			let uid;
			let opaqueForeignOwner = false;
			try {
				stat = readFileSync(`/proc/${name}/stat`, "utf8");
				if (includeCommand) {
					uid = readLinuxProcessUid(pid);
					try {
						argv = readFileSync(`/proc/${name}/cmdline`, "utf8").split("\0").filter(Boolean);
					} catch (error) {
						const inspectorUid = process.getuid?.();
						opaqueForeignOwner = uid !== void 0 && inspectorUid !== void 0 && uid !== inspectorUid && ["EACCES", "EPERM"].includes(extractErrorCode(error) ?? "");
						if (!opaqueForeignOwner) throw error;
					}
				}
			} catch (error) {
				if (pid !== process.pid && ["ENOENT", "ESRCH"].includes(extractErrorCode(error) ?? "")) continue;
				throw new Error(`Could not classify PID ${pid}: process command inspection failed (${extractErrorCode(error) ?? "unavailable"}).`, { cause: error });
			}
			const match = /^(\d+) \([\s\S]*\) (\S) (\d+) (\d+)(?:\s|$)/.exec(stat);
			if (!match || Number(match[1]) !== pid || Date.now() >= deadline) throw new Error("Process group census is unavailable");
			if (argv?.length === 0) {
				const flags = Number(stat.slice(stat.lastIndexOf(")") + 2).split(/\s+/)[6]);
				if (!(Number.isInteger(flags) && (flags & 2097152) !== 0) && !isPidDefinitelyDead(pid)) {
					const inspectorUid = process.getuid?.();
					if (uid !== void 0 && inspectorUid !== void 0 && uid !== inspectorUid) {
						opaqueForeignOwner = true;
						argv = void 0;
					} else throw new Error(`Could not classify PID ${pid}: live userspace process has no readable arguments.`);
				}
			}
			yield {
				pid,
				pgid: Number(match[4]),
				state: match[2],
				...argv ? { command: {
					ppid: Number(match[3]),
					argv,
					...uid === void 0 ? {} : { uid }
				} } : opaqueForeignOwner && uid !== void 0 ? { command: {
					ppid: Number(match[3]),
					argvUnavailable: true,
					uid
				} } : {}
			};
		}
		if (Date.now() >= deadline) throw new Error("Process group census exceeded its deadline");
		return;
	}
	if (includeCommand && process.platform !== "darwin") throw new Error(`Exact process command census is unavailable on ${process.platform}.`);
	const deadline = Date.now() + timeoutMs;
	const census = spawnSync("/bin/ps", [
		"-A",
		"-o",
		includeCommand ? "pid=,pgid=,stat=,ppid=,uid=" : "pid=,pgid=,stat="
	], {
		encoding: "utf8",
		timeout: timeoutMs,
		maxBuffer: 4194304
	});
	if (census.error || census.status !== 0) throw new Error("Process group census is unavailable");
	for (const line of census.stdout.split("\n")) {
		if (!line.trim()) continue;
		const match = includeCommand ? /^\s*(\d+)\s+(\d+)\s+(\S+)\s+(\d+)\s+(-?\d+)\s*$/.exec(line) : /^\s*(\d+)\s+(\d+)\s+(\S+)\s*$/.exec(line);
		if (!match) throw new Error("Process group census is unavailable");
		const pid = Number(match[1]);
		if (pid !== census.pid) {
			if (includeCommand && Date.now() >= deadline) throw new Error("Process group census exceeded its deadline");
			const command = includeCommand ? match[3].startsWith("Z") || pid === 0 ? { argv: [] } : commandInspection?.readDarwinCommand(pid, Number(match[5]) >>> 0) : void 0;
			if (includeCommand && !command) continue;
			yield {
				pid,
				pgid: Number(match[2]),
				state: match[3],
				...command ? { command: {
					ppid: Number(match[4]),
					...command,
					uid: Number(match[5]) >>> 0
				} } : {}
			};
		}
	}
	if (includeCommand && Date.now() >= deadline) throw new Error("Process group census exceeded its deadline");
}
/** Advisory retirement timing only; the host owns kernel group-disappearance proof. */
function hasLiveOwnedProcessGroupMembers(timeoutMs = 1e3) {
	let observedOwner = false;
	try {
		for (const { pid, pgid, state } of readProcessGroupMembers(Math.max(1, Math.min(1e3, timeoutMs)))) if (pid === process.pid) {
			if (pgid !== process.pid) return;
			observedOwner = true;
		} else if (pgid === process.pid && (!state.startsWith("Z") || process.platform === "linux" && !isPidDefinitelyDead(pid))) return true;
	} catch {
		return;
	}
	return observedOwner ? false : void 0;
}
//#endregion
//#region src/process/spawn-broker/execa-message.ts
const decoder = new TextDecoder();
const commonEscapes = /* @__PURE__ */ new Map([
	[" ", " "],
	["\n", "\n"],
	["\b", "\\b"],
	["\f", "\\f"],
	["\r", "\\r"],
	["	", "\\t"]
]);
function renderOutput(output) {
	let text = typeof output === "string" ? output : decoder.decode(output);
	if (text.endsWith("\n")) text = text.slice(0, text.endsWith("\r\n") ? -2 : -1);
	return stripVTControlCharacters(text).replace(/[\p{Separator}\p{Other}]/gu, (character) => {
		const common = commonEscapes.get(character);
		if (common !== void 0) return common;
		const codepoint = character.codePointAt(0);
		const hex = codepoint.toString(16);
		return codepoint <= 65535 ? `\\u${hex.padStart(4, "0")}` : `\\U${hex}`;
	});
}
/** Keep the authoritative prefix and replace only exactly matching output suffixes. */
function encodeExecaMessage(message, output) {
	const outputs = [];
	let end = message.length;
	for (const stream of ["stdout", "stderr"]) {
		const rendered = renderOutput(output[stream]);
		if (!rendered) continue;
		const start = end - rendered.length;
		if (start < 2 || !message.endsWith(rendered, end) || message.slice(start - 2, start) !== "\n\n") return { message };
		outputs.unshift(stream);
		end = start - 2;
	}
	return outputs.length > 0 ? {
		message: message.slice(0, end),
		messageOutputs: outputs
	} : { message };
}
//#endregion
//#region src/process/spawn-broker/execa-protocol.ts
function serializeExecaError(error, output = {}) {
	return {
		name: error.name,
		...encodeExecaMessage(error.message, output),
		..."code" in error && typeof error.code === "string" ? { code: error.code } : {},
		...error.cause instanceof Error ? { cause: serializeExecaError(error.cause) } : {}
	};
}
//#endregion
//#region src/process/process-deadline.ts
/** Let pending child exit and output notifications run before accepting a deadline. */
function setProcessTimeout(callback, delayMs) {
	let decision;
	const timer = setTimeout(() => {
		decision = setTimeout(callback, 0);
	}, delayMs);
	return {
		clear() {
			clearTimeout(timer);
			clearTimeout(decision);
		},
		refresh() {
			clearTimeout(decision);
			decision = void 0;
			timer.refresh();
		}
	};
}
//#endregion
//#region src/process/spawn-broker/execa-output.ts
/** Execa retains its reader; the Gateway receives a separate native output socket. */
async function createExecaOutput() {
	const directory = await mkdtemp(path.join(tmpdir(), "oc-spawn-"));
	const server = createServer({ pauseOnConnect: true });
	let writer;
	let receiver;
	try {
		const socketPath = path.join(directory, "out");
		server.listen(socketPath);
		await once(server, "listening");
		const accepted = new Promise((resolve) => {
			server.once("connection", resolve);
		});
		receiver = createConnection(socketPath);
		[writer] = await Promise.all([accepted, once(receiver, "connect")]);
		const destination = writer;
		const transform = new Transform({
			transform(chunk, _encoding, callback) {
				destination.write(chunk, (error) => callback(error, chunk));
			},
			flush(callback) {
				destination.end(callback);
			},
			destroy(error, callback) {
				destination.destroy();
				callback(error);
			}
		});
		destination.on("error", (error) => transform.destroy(error));
		destination.once("close", () => {
			if (!destination.writableFinished) transform.destroy();
		});
		return {
			receiver,
			transform
		};
	} catch (error) {
		writer?.destroy();
		receiver?.destroy();
		throw error;
	} finally {
		server.close();
		await rm(directory, {
			recursive: true,
			force: true
		});
	}
}
//#endregion
//#region src/process/spawn-broker/protocol.ts
var SpawnBrokerError = class extends Error {
	constructor(message, options) {
		super(message, options);
		this.code = "ERR_SPAWN_BROKER_UNAVAILABLE";
		this.name = "SpawnBrokerError";
	}
};
function serializeBrokerError(error) {
	return {
		message: error.message,
		code: error.code,
		errno: error.errno,
		syscall: error.syscall,
		path: error.path
	};
}
//#endregion
//#region src/process/spawn-broker/pipe.ts
const readers = /* @__PURE__ */ new WeakMap();
const holdReadable = () => {};
/** Defer consumption and EOF until the transferred socket is ready for its caller. */
function holdPipe(socket) {
	const held = {
		read: socket.read.bind(socket),
		resumed: false,
		onResume: () => {
			held.resumed = true;
		}
	};
	readers.set(socket, held);
	socket.on("resume", held.onResume);
	socket.on("readable", holdReadable);
	socket.read = () => null;
}
/** Stop the sender's libuv reader before Node detaches the socket for IPC. */
function holdPipeForTransfer(socket) {
	stopPipeReads(socket);
	holdPipe(socket);
}
function stopPipeReads(socket) {
	const handle = Reflect.get(socket, "_handle");
	if (!handle || typeof handle !== "object" || !("readStop" in handle) || typeof handle.readStop !== "function" || !("reading" in handle) || typeof handle.reading !== "boolean") throw new SpawnBrokerError("Spawn broker requires a transferable Node pipe handle");
	if (handle.readStop() !== 0) throw new SpawnBrokerError("Spawn broker could not stop pipe reads");
	handle.reading = false;
}
/** Called after send's callback, when keepOpen:false has detached the native handle. */
function takePipePrefix(socket) {
	const held = readers.get(socket);
	if (!held) throw new Error("Spawn broker pipe was not held for handoff");
	const buffered = held.read.call(socket, socket.readableLength);
	restoreReader(socket);
	if (buffered === null) return Buffer.alloc(0);
	if (!Buffer.isBuffer(buffered)) throw new Error("Spawn broker pipe must contain bytes");
	return buffered;
}
function restoreReader(socket) {
	const held = readers.get(socket);
	if (!held) return;
	socket.read = held.read;
	readers.delete(socket);
	socket.removeListener("resume", held.onResume);
	socket.removeListener("readable", holdReadable);
	return held;
}
//#endregion
//#region src/process/spawn-broker/execa-worker.ts
function outputOption(options, fd) {
	const explicit = fd === 1 ? options.stdout : options.stderr;
	if (explicit !== void 0) return explicit;
	return typeof options.stdio === "string" ? options.stdio : options.stdio?.[fd] ?? "pipe";
}
/** Spawn only after preparing bounded output paths, before the command can produce bytes. */
async function startBrokerExeca(argv, options, assertCurrent) {
	const controller = new AbortController();
	const outputs = /* @__PURE__ */ new Map();
	try {
		for (const fd of [1, 2]) {
			const name = fd === 1 ? "stdout" : "stderr";
			const buffered = typeof options.buffer === "boolean" ? options.buffer : options.buffer?.[name];
			if (outputOption(options, fd) === "pipe" && buffered !== false) outputs.set(fd, await createExecaOutput());
		}
		assertCurrent();
		const { encoding, executionDeadlineMs, ...processOptions } = options;
		const spawnOptions = {
			...processOptions,
			...outputs.has(1) ? { stdout: {
				transform: outputs.get(1).transform,
				objectMode: false
			} } : {},
			...outputs.has(2) ? { stderr: {
				transform: outputs.get(2).transform,
				objectMode: false
			} } : {},
			cancelSignal: controller.signal
		};
		const start = () => encoding === void 0 || encoding === "utf8" || encoding === "utf16le" ? execa(argv[0], argv.slice(1), {
			...spawnOptions,
			encoding
		}) : execa(argv[0], argv.slice(1), {
			...spawnOptions,
			encoding
		});
		let subprocess;
		try {
			subprocess = start();
		} catch (error) {
			throw await adoptAbandonedSpawnError(error);
		}
		const child = subprocess.nodeChildProcess;
		const stdio = [
			0,
			1,
			2
		].map((fd) => {
			if (fd === 0 && options.input !== void 0) return null;
			if (fd > 0 && outputOption(options, fd === 1 ? 1 : 2) !== "pipe") return null;
			const stream = outputs.get(fd)?.receiver ?? child.stdio[fd];
			if (!(stream instanceof Socket)) return null;
			if (fd > 0) holdPipeForTransfer(stream);
			return stream;
		});
		let executionTimedOut = false;
		const deadline = executionDeadlineMs === void 0 ? void 0 : setProcessTimeout(() => {
			if (child.exitCode === null && child.signalCode === null) {
				executionTimedOut = true;
				subprocess.kill();
			}
		}, executionDeadlineMs);
		const clearDeadline = () => deadline?.clear();
		child.once("exit", clearDeadline);
		const result = subprocess.then((value) => serializeResult(value, executionTimedOut), (error) => {
			if (error instanceof Error && "failed" in error && error.failed === true) return serializeResult(error, executionTimedOut);
			throw error;
		}).finally(() => {
			clearDeadline();
			child.removeListener("exit", clearDeadline);
		});
		result.catch(() => {});
		return {
			child,
			stdio,
			result,
			cancel() {
				clearDeadline();
				controller.abort();
			},
			kill(signal) {
				clearDeadline();
				return subprocess.kill(signal);
			},
			outputDrained(fd, error) {
				const output = outputs.get(fd);
				if (output) {
					output.receiver.destroy();
					if (error) output.transform.destroy(error);
					return;
				}
				if (fd === 0 && !error) {
					child.stdin?.end();
					return;
				}
				child.stdio[fd]?.destroy(error);
			}
		};
	} catch (error) {
		for (const output of outputs.values()) {
			output.transform.destroy();
			output.receiver.destroy();
		}
		throw error;
	}
}
function isDescriptorSpawnError(error) {
	if (!(error instanceof Error)) return false;
	const syscall = "syscall" in error ? error.syscall : void 0;
	const code = "code" in error ? error.code : void 0;
	return typeof syscall === "string" && syscall.startsWith("spawn") && (code === "EMFILE" || code === "ENFILE");
}
async function adoptAbandonedSpawnError(thrown) {
	if (!(thrown instanceof TypeError)) return thrown;
	return await new Promise((resolve) => {
		const settle = (error) => {
			process.off("uncaughtException", onUncaught);
			clearImmediate(deadline);
			resolve(error);
		};
		const onUncaught = (error) => {
			if (isDescriptorSpawnError(error)) {
				settle(error);
				return;
			}
			settle(thrown);
			process.nextTick(() => {
				throw error;
			});
		};
		process.on("uncaughtException", onUncaught);
		const deadline = setImmediate(() => settle(thrown));
	});
}
function serializeResult(result, executionTimedOut) {
	const output = {
		stdout: byteOrTextOutput(result.stdout),
		stderr: byteOrTextOutput(result.stderr)
	};
	const error = result instanceof Error ? result : executionTimedOut ? /* @__PURE__ */ new Error("Command timed out") : void 0;
	return {
		...output,
		exitCode: result.exitCode,
		signal: result.signal,
		failed: result.failed || executionTimedOut,
		timedOut: result.timedOut || executionTimedOut,
		isCanceled: result.isCanceled,
		isGracefullyCanceled: result.isGracefullyCanceled,
		isMaxBuffer: result.isMaxBuffer,
		isTerminated: result.isTerminated,
		isForcefullyTerminated: result.isForcefullyTerminated,
		shortMessage: result.shortMessage,
		originalMessage: result.originalMessage,
		code: result.code,
		command: result.command,
		escapedCommand: result.escapedCommand,
		cwd: result.cwd,
		durationMs: result.durationMs,
		signalDescription: result.signalDescription,
		...error ? { error: serializeExecaError(error, output) } : {}
	};
}
function byteOrTextOutput(output) {
	if (output === void 0 || typeof output === "string" || output instanceof Uint8Array) return output;
	throw new TypeError("Spawn broker execa output must contain bytes or text");
}
//#endregion
//#region src/process/spawn-broker/ipc.ts
const FRAME_BYTES = 1048576;
const FRAME_KIND = "openclaw-spawn-broker-frame";
/** Frame large inputs/results while bounding both native writes and retained payloads. */
function createBrokerSender(send) {
	let bytes = 0;
	let messages = 0;
	let sequence = 0;
	let previous = Promise.resolve();
	const write = (message, handle) => new Promise((resolve, reject) => {
		try {
			send(message, handle, (error) => error ? reject(error) : resolve());
		} catch (error) {
			reject(toErrorObject(error, "Spawn broker IPC write failed"));
		}
	});
	const enqueue = (size, operation) => {
		if (bytes + size > 268435456 || messages >= 1024) return Promise.reject(new SpawnBrokerError("Spawn broker IPC capacity exceeded"));
		bytes += size;
		messages += 1;
		const completion = previous.then(operation);
		previous = completion.then(() => {}, () => {});
		return completion.finally(() => {
			bytes -= size;
			messages -= 1;
		});
	};
	const sender = (message, handle) => {
		const payload = serialize(message);
		const size = payload.byteLength;
		if (size > FRAME_BYTES && handle) return Promise.reject(new SpawnBrokerError("Spawn broker handle metadata exceeds one IPC frame"));
		const id = ++sequence;
		const operation = async () => {
			if (size <= FRAME_BYTES) {
				await write(message, handle);
				return;
			}
			for (let offset = 0; offset < size; offset += FRAME_BYTES) {
				const frame = {
					kind: FRAME_KIND,
					id,
					total: size,
					offset,
					bytes: payload.subarray(offset, offset + FRAME_BYTES)
				};
				await write(frame);
			}
		};
		return enqueue(size, operation);
	};
	return Object.assign(sender, { reserve(run) {
		return enqueue(FRAME_BYTES, async () => {
			let active = true;
			let publication;
			const publish = (message) => {
				if (!active || publication) return Promise.reject(new SpawnBrokerError("Spawn broker publication reservation is closed"));
				publication = (async () => {
					if (serialize(message).byteLength > FRAME_BYTES) throw new SpawnBrokerError("Spawn broker reserved publication exceeds one IPC frame");
					await write(message);
				})();
				publication.catch(() => {});
				return publication;
			};
			try {
				return await run(publish);
			} finally {
				active = false;
				await publication;
			}
		});
	} });
}
/** Only the version-matched private peer can supply frames on this channel. */
function createBrokerReceiver() {
	const pending = /* @__PURE__ */ new Map();
	let reserved = 0;
	return {
		receive(message) {
			if (!message || typeof message !== "object" || !("kind" in message) || message.kind !== FRAME_KIND) return message;
			const frame = message;
			if (!Number.isSafeInteger(frame.id) || !Number.isSafeInteger(frame.total) || frame.total <= FRAME_BYTES || frame.total > 268435456 || !Number.isSafeInteger(frame.offset) || !Buffer.isBuffer(frame.bytes) || frame.bytes.length === 0 || frame.bytes.length > FRAME_BYTES) throw new SpawnBrokerError("Invalid spawn broker IPC frame");
			let assembly = pending.get(frame.id);
			if (!assembly) {
				if (frame.offset !== 0 || pending.size >= 1024 || reserved + frame.total > 268435456) throw new SpawnBrokerError("Spawn broker receive capacity exceeded");
				assembly = {
					total: frame.total,
					received: 0,
					chunks: []
				};
				pending.set(frame.id, assembly);
				reserved += frame.total;
			}
			if (assembly.total !== frame.total || assembly.received !== frame.offset || assembly.received + frame.bytes.length > assembly.total) throw new SpawnBrokerError("Out-of-order spawn broker IPC frame");
			assembly.chunks.push(frame.bytes);
			assembly.received += frame.bytes.length;
			if (assembly.received < assembly.total) return;
			pending.delete(frame.id);
			reserved -= assembly.total;
			return deserialize(Buffer.concat(assembly.chunks, assembly.total));
		},
		clear() {
			pending.clear();
			reserved = 0;
		}
	};
}
//#endregion
//#region src/infra/worker-native-error.ts
const errorConstructors = new Map([
	EvalError,
	RangeError,
	ReferenceError,
	SyntaxError,
	TypeError,
	URIError,
	Error
].map((constructor) => [constructor.name, constructor]));
/** Node's Worker error events use prototype-only Errors that a second clone would erase. */
function encodeNativeWorkerFailure(value) {
	const seen = /* @__PURE__ */ new Map();
	const encode = (current) => {
		if (!(current instanceof Error)) return {
			kind: "value",
			value: current
		};
		const prior = seen.get(current);
		if (prior) return prior;
		const node = {
			kind: "error",
			constructorName: current instanceof AggregateError ? "AggregateError" : [...errorConstructors].find(([, Constructor]) => current instanceof Constructor)?.[0] ?? "Error",
			name: current.name,
			message: current.message,
			stack: current.stack
		};
		seen.set(current, node);
		const code = Object.getOwnPropertyDescriptor(current, "code")?.value;
		if (typeof code === "string" || typeof code === "number") node.code = code;
		for (const key of ["errcode", "errno"]) {
			const field = Object.getOwnPropertyDescriptor(current, key)?.value;
			if (typeof field === "number") node[key] = field;
		}
		if (Object.hasOwn(current, "cause")) node.cause = encode(current.cause);
		const errors = Object.getOwnPropertyDescriptor(current, "errors")?.value;
		if (Array.isArray(errors)) node.errors = errors.map(encode);
		return node;
	};
	return encode(value);
}
//#endregion
//#region src/process/spawn-broker/resource-port.ts
/** A logical resource port; closing it does not assert anything about a native child. */
var BrokerResourcePort = class extends EventEmitter {
	constructor(send) {
		super();
		this.send = send;
		this.closed = false;
	}
	postMessage(value) {
		if (this.closed) throw new Error("Native resource message port is closed");
		this.send(value);
	}
	receive(value) {
		if (!this.closed) this.emit("message", value);
	}
	close() {
		if (!this.closed) {
			this.closed = true;
			this.emit("close");
		}
	}
};
//#endregion
//#region src/process/spawn-broker/resource-protocol.ts
const SPAWN_BROKER_STARTUP_TIMEOUT_MS = 15e3;
//#endregion
//#region src/process/spawn-broker/resource-socket.ts
/** Byte transport only: socket closure says nothing about a resource's native lifetime. */
function createBrokerResourceSocket(socket, handlers) {
	const receiver = createBrokerReceiver();
	const header = Buffer.alloc(4);
	let headerBytes = 0;
	let body;
	let bodyBytes = 0;
	let stopped = false;
	let failure;
	let pendingWrite;
	const stop = (error) => {
		if (stopped) return;
		stopped = true;
		failure = error;
		receiver.clear();
		body = void 0;
		pendingWrite?.(error ?? new SpawnBrokerError("Spawn broker resource socket closed"));
		socket.destroy();
	};
	const sender = createBrokerSender((message, _handle, callback) => {
		if (stopped || socket.destroyed) {
			callback(failure ?? new SpawnBrokerError("Spawn broker resource socket closed"));
			return;
		}
		const payload = serialize(message);
		if (payload.length > 268435456) {
			callback(new SpawnBrokerError("Spawn broker resource frame exceeds capacity"));
			return;
		}
		const packet = Buffer.allocUnsafe(4 + payload.length);
		packet.writeUInt32BE(payload.length);
		payload.copy(packet, 4);
		const complete = (error) => {
			if (pendingWrite !== complete) return;
			pendingWrite = void 0;
			callback(error);
			if (error) stop(error);
		};
		pendingWrite = complete;
		try {
			socket.write(packet, (error) => complete(error ?? null));
		} catch (error) {
			complete(toErrorObject(error, "Spawn broker resource write failed"));
		}
	});
	socket.on("data", (chunk) => {
		try {
			let offset = 0;
			while (offset < chunk.length) {
				if (stopped) break;
				if (!body) {
					const count = Math.min(4 - headerBytes, chunk.length - offset);
					chunk.copy(header, headerBytes, offset, offset + count);
					headerBytes += count;
					offset += count;
					if (headerBytes !== 4) continue;
					const length = header.readUInt32BE();
					if (length === 0 || length > 268435456) throw new SpawnBrokerError("Invalid spawn broker resource frame length");
					body = Buffer.allocUnsafe(length);
					headerBytes = 0;
				}
				const count = Math.min(body.length - bodyBytes, chunk.length - offset);
				chunk.copy(body, bodyBytes, offset, offset + count);
				bodyBytes += count;
				offset += count;
				if (bodyBytes !== body.length) continue;
				const decoded = receiver.receive(deserialize(body));
				body = void 0;
				bodyBytes = 0;
				if (decoded !== void 0) {
					if (!decoded || typeof decoded !== "object" || !("value" in decoded)) throw new SpawnBrokerError("Invalid spawn broker resource message");
					handlers.message(decoded.value);
				}
			}
		} catch (error) {
			stop(toErrorObject(error, "Spawn broker resource read failed"));
		}
	});
	socket.once("end", () => stop(body || headerBytes ? new SpawnBrokerError("Spawn broker resource socket ended during a frame") : void 0));
	socket.once("error", stop);
	socket.once("close", () => {
		stop();
		handlers.close(failure);
	});
	return {
		async send(value) {
			if (stopped || socket.destroyed) throw failure ?? new SpawnBrokerError("Spawn broker resource socket closed");
			await sender({ value });
		},
		close: () => stop()
	};
}
//#endregion
//#region src/process/spawn-broker/resource-server.ts
/** The broker retains resource owners independently of their submitting Worker's socket. */
async function createBrokerNativeResourceServer(options) {
	const resources = /* @__PURE__ */ new Map();
	const connections = /* @__PURE__ */ new Set();
	let available = true;
	let sourceSealed = false;
	let ownerBufferedBytes = 0;
	let ownerBufferedMessages = 0;
	const live = () => available && process.connected;
	const publishParent = (message) => {
		options.reportParent(message).catch(() => disconnect());
	};
	const publishSocket = (resource, message) => {
		const socket = resource.socket;
		if (socket) socket.send(message).catch(() => socket.close());
	};
	const publish = (resource, message) => {
		publishSocket(resource, message);
		publishParent(message);
	};
	const failed = (resource, error) => {
		publish(resource, {
			type: "resource-failed",
			id: resource.attachment.id,
			error: encodeNativeWorkerFailure(error)
		});
	};
	const seal = (resource, ports) => {
		for (const port of ports) try {
			port?.close();
		} catch (error) {
			if (live()) failed(resource, error);
		}
	};
	const closeError = (resource, requestId, error) => {
		const fields = {
			type: "resource-close-error",
			id: resource.attachment.id,
			requestId
		};
		let reply;
		try {
			if (resource.owner?.encodeCloseError) reply = {
				...fields,
				error: resource.owner.encodeCloseError(error),
				resourceError: true
			};
			else reply = {
				...fields,
				error: encodeNativeWorkerFailure(error),
				resourceError: false
			};
		} catch (encodingError) {
			reply = {
				...fields,
				error: encodeNativeWorkerFailure(new AggregateError([error, encodingError], "Native resource cleanup error encoding failed", { cause: error })),
				resourceError: false
			};
		}
		publish(resource, reply);
	};
	const closeResource = async (resource, requestId) => {
		if (resource.closed) {
			publish(resource, {
				type: "resource-closed",
				id: resource.attachment.id,
				requestId
			});
			return;
		}
		const origin = requestId > 0 ? "worker" : "parent";
		if (resource.closing) {
			const existing = resource.closeObservers[origin];
			if (existing !== void 0 && existing !== requestId) closeError(resource, requestId, /* @__PURE__ */ new Error("Native resource close observer capacity exceeded"));
			else resource.closeObservers[origin] = requestId;
			return;
		}
		resource.closeObservers[origin] = requestId;
		resource.closing = true;
		let outcome;
		try {
			await new Promise((resolve) => {
				if (sourceSealed) {
					resolve();
					return;
				}
				resource.finishInitializationWait = resolve;
				resource.initialized.then(resolve);
			});
			resource.finishInitializationWait = void 0;
			if (!live()) return;
			if (!resource.closed) {
				await resource.owner?.close();
				resource.closed = true;
				seal(resource, [resource.target, resource.ownerPort]);
			}
			outcome = { ok: true };
		} catch (error) {
			outcome = {
				ok: false,
				error
			};
		} finally {
			const observers = resource.closeObservers;
			resource.closeObservers = {};
			resource.finishInitializationWait = void 0;
			resource.closing = false;
			if (live() && outcome) for (const observer of [observers.worker, observers.parent]) {
				if (observer === void 0) continue;
				if (outcome.ok) publish(resource, {
					type: "resource-closed",
					id: resource.attachment.id,
					requestId: observer
				});
				else closeError(resource, observer, outcome.error);
			}
		}
	};
	const receiveOwner = (resource, request) => {
		const acknowledge = (sequence) => publish(resource, resource.ownerFailures.get(sequence)?.response ?? {
			type: "resource-owner-received",
			id: resource.attachment.id,
			sequence
		});
		if (!Number.isSafeInteger(request.sequence) || request.sequence <= 0) throw new Error("Invalid native resource owner sequence");
		if (request.sequence <= resource.ownerSequence) {
			acknowledge(request.sequence);
			return;
		}
		if (!resource.ownerPort || resource.closed || !resource.created) throw new Error("Native resource owner port is unavailable");
		const ownerPort = resource.ownerPort;
		const apply = (sequence, value) => {
			resource.ownerSequence = sequence;
			try {
				ownerPort.receive(value);
			} catch (error) {
				try {
					const response = {
						type: "resource-owner-rejected",
						id: resource.attachment.id,
						sequence,
						error: encodeNativeWorkerFailure(error)
					};
					const size = serialize(response).length;
					if (ownerBufferedBytes + size > 268435456 || ownerBufferedMessages >= 1024) throw new Error("Native resource owner receipt capacity exceeded", { cause: error });
					resource.ownerFailures.set(sequence, {
						response,
						size
					});
					ownerBufferedBytes += size;
					ownerBufferedMessages++;
				} catch (receiptError) {
					failed(resource, receiptError);
					disconnect();
					return false;
				}
			}
			acknowledge(sequence);
			return true;
		};
		if (request.sequence !== resource.ownerSequence + 1) {
			if (resource.ownerMessages.has(request.sequence)) return;
			const size = serialize(request).length;
			if (ownerBufferedBytes + size > 268435456 || ownerBufferedMessages >= 1024) throw new Error("Native resource owner receive capacity exceeded");
			resource.ownerMessages.set(request.sequence, {
				value: request.value,
				size
			});
			ownerBufferedBytes += size;
			ownerBufferedMessages++;
			return;
		}
		if (!apply(request.sequence, request.value)) return;
		for (;;) {
			const next = resource.ownerMessages.get(resource.ownerSequence + 1);
			if (!next) return;
			resource.ownerMessages.delete(++resource.ownerSequence);
			ownerBufferedBytes -= next.size;
			ownerBufferedMessages--;
			if (!apply(resource.ownerSequence, next.value)) return;
		}
	};
	const receive = (request) => {
		if (!live()) return;
		if (request.type === "resource-seal") {
			sourceSealed = true;
			for (const resource of resources.values()) resource.finishInitializationWait?.();
			return;
		}
		if (request.type === "resource-close" && (!Number.isSafeInteger(request.requestId) || request.requestId === 0)) {
			publishParent({
				type: "resource-failed",
				id: request.id,
				error: encodeNativeWorkerFailure(/* @__PURE__ */ new Error("Invalid native resource close request ID"))
			});
			return;
		}
		const resource = resources.get(request.id);
		if (!resource) {
			if (sourceSealed && request.type === "resource-close") {
				publishParent({
					type: "resource-closed",
					id: request.id,
					requestId: request.requestId
				});
				return;
			}
			publishParent({
				type: "resource-failed",
				id: request.id,
				error: encodeNativeWorkerFailure(/* @__PURE__ */ new Error("Native resource owner is unavailable"))
			});
			return;
		}
		if (request.type === "resource-owner") try {
			receiveOwner(resource, request);
		} catch (error) {
			failed(resource, error);
		}
		else if (request.type === "resource-close") closeResource(resource, request.requestId);
		else if (request.type === "resource-release") {
			if (!resource.closed) {
				failed(resource, /* @__PURE__ */ new Error("Native resource must close before release"));
				return;
			}
			resources.delete(request.id);
			for (const pending of resource.ownerMessages.values()) {
				ownerBufferedBytes -= pending.size;
				ownerBufferedMessages--;
			}
			resource.ownerMessages.clear();
			for (const outcome of resource.ownerFailures.values()) {
				ownerBufferedBytes -= outcome.size;
				ownerBufferedMessages--;
			}
			resource.ownerFailures.clear();
			resource.socket?.close();
		} else if (!resource.created || resource.closed || (resource.closing || sourceSealed) && request.type === "resource-target") failed(resource, /* @__PURE__ */ new Error("Native resource is not accepting messages"));
		else try {
			if (resource.targetSealed) throw new Error("Native resource target port is closed");
			resource.target.receive(request.value);
		} catch (error) {
			failed(resource, error);
		}
	};
	const server = createServer((socket) => {
		let attached;
		const transport = createBrokerResourceSocket(socket, {
			message(value) {
				if (!live() || !value || typeof value !== "object" || !("type" in value)) {
					transport.close();
					return;
				}
				const request = value;
				if (request.type === "resource-seal") {
					transport.close();
					return;
				}
				if (request.type !== "resource-attach") {
					if (!attached || request.id !== attached.attachment.id || ![
						"resource-target",
						"resource-owner",
						"resource-close",
						"resource-release"
					].includes(request.type)) {
						transport.close();
						return;
					}
					receive(request);
					return;
				}
				const attachment = request.attachment;
				if (sourceSealed || attached || !attachment || attachment.secret !== options.secret || attachment.generation !== options.generation || attachment.endpoint !== options.endpoint || !Number.isSafeInteger(attachment.id) || attachment.id <= 0 || typeof attachment.moduleUrl !== "string" || typeof attachment.ownerPort !== "boolean") {
					transport.close();
					return;
				}
				let resource = resources.get(attachment.id);
				const fresh = resource === void 0;
				if (resource) {
					if (resource.socket || resource.attachment.moduleUrl !== attachment.moduleUrl || resource.attachment.ownerPort !== attachment.ownerPort || !isDeepStrictEqual(resource.attachment.input, attachment.input)) {
						transport.close();
						return;
					}
				} else {
					if (!options.canAdmit()) {
						transport.send({
							type: "resource-failed",
							id: attachment.id,
							error: encodeNativeWorkerFailure(/* @__PURE__ */ new Error("Spawn broker request capacity exceeded"))
						}).finally(() => transport.close()).catch(() => {});
						return;
					}
					const current = {
						attachment,
						target: new BrokerResourcePort((message) => {
							publishSocket(current, {
								type: "resource-target",
								id: attachment.id,
								value: message
							});
						}),
						initialized: Promise.resolve(),
						created: false,
						targetSealed: false,
						closing: false,
						closeObservers: {},
						closed: false,
						sequence: 0,
						ownerSequence: 0,
						ownerMessages: /* @__PURE__ */ new Map(),
						ownerFailures: /* @__PURE__ */ new Map()
					};
					if (attachment.ownerPort) current.ownerPort = new BrokerResourcePort((message) => {
						publish(current, {
							type: "resource-owner",
							id: attachment.id,
							sequence: ++current.sequence,
							value: message
						});
					});
					resource = current;
					resources.set(attachment.id, current);
				}
				attached = resource;
				resource.socket = transport;
				socket.setTimeout(0);
				const ready = {
					type: "resource-ready",
					id: attachment.id,
					pid: process.pid,
					generation: options.generation
				};
				const readySent = transport.send(ready);
				publishParent(ready);
				if (resource.created) readySent.then(() => transport.send({
					type: "resource-created",
					id: attachment.id
				})).catch(() => transport.close());
				else if (fresh) {
					const current = resource;
					current.initialized = (async () => {
						await readySent;
						if (!live() || sourceSealed) return;
						const module = await import(attachment.moduleUrl);
						if (!live() || sourceSealed) return;
						current.owner = module.createNativeWorkerResource(current.target, attachment.input, current.ownerPort);
						current.created = true;
						publish(current, {
							type: "resource-created",
							id: attachment.id
						});
					})().catch((error) => {
						current.initializationFailure = encodeNativeWorkerFailure(error);
						failed(current, error);
					});
				} else {
					const current = resource;
					readySent.then(async () => {
						await current.initialized;
						if (current.initializationFailure) await transport.send({
							type: "resource-failed",
							id: attachment.id,
							error: current.initializationFailure
						});
					}).catch(() => transport.close());
				}
			},
			close() {
				connections.delete(transport);
				if (attached?.socket === transport) {
					attached.socket = void 0;
					attached.targetSealed = true;
					seal(attached, [attached.target]);
				}
			}
		});
		connections.add(transport);
		socket.setTimeout(SPAWN_BROKER_STARTUP_TIMEOUT_MS, () => transport.close());
		if (!live()) transport.close();
	});
	server.maxConnections = 256;
	function disconnect() {
		if (!available) return;
		available = false;
		server.close();
		for (const connection of connections) connection.close();
		for (const resource of resources.values()) seal(resource, [resource.target, resource.ownerPort]);
	}
	await new Promise((resolve, reject) => {
		server.once("error", reject);
		server.listen(options.endpoint, () => {
			server.off("error", reject);
			server.on("error", disconnect);
			resolve();
		});
	});
	return {
		receive,
		disconnect,
		get size() {
			return resources.size;
		}
	};
}
//#endregion
//#region src/process/spawn-broker/worker-sender.ts
/** A receipt keeps Node's internal handle queue empty before inspecting the next socket. */
function createWorkerSender(send) {
	let pending;
	let closed;
	const finish = (pipe, error) => {
		if (pending !== pipe) return;
		pending = void 0;
		pipe.callback(error);
	};
	const sender = createBrokerSender((message, handle, callback) => {
		if (closed) {
			callback(closed);
			return;
		}
		if (!(handle instanceof Socket) || !message || typeof message !== "object" || !("type" in message) || message.type !== "pipe" || !("id" in message) || typeof message.id !== "number" || !("fd" in message) || typeof message.fd !== "number") {
			send(message, handle, callback);
			return;
		}
		const pipe = {
			id: message.id,
			fd: message.fd,
			written: false,
			received: false,
			callback
		};
		pending = pipe;
		const stdinClosed = message.fd === 0 && handle.destroyed;
		try {
			send(stdinClosed ? {
				...message,
				closed: true
			} : message, stdinClosed ? void 0 : handle, (error) => {
				if (error) {
					finish(pipe, error);
					return;
				}
				pipe.written = true;
				if (pipe.received) finish(pipe, null);
			});
		} catch (error) {
			pending = void 0;
			throw error;
		}
	});
	return {
		send: sender,
		reserve(run) {
			return sender.reserve(async (publish) => {
				if (closed) throw closed;
				return await run(publish);
			});
		},
		acknowledge(id, fd) {
			if (!pending || pending.id !== id || pending.fd !== fd) return;
			pending.received = true;
			if (pending.written) finish(pending, null);
		},
		close(error) {
			closed = error;
			if (pending) finish(pending, error);
		}
	};
}
//#endregion
//#region src/process/spawn-broker/worker.ts
const owned = /* @__PURE__ */ new Map();
const receiver = createBrokerReceiver();
let stopping = false;
const starting = /* @__PURE__ */ new Map();
let resources;
let startup = "waiting";
const sender = createWorkerSender((message, handle, callback) => {
	if (!process.send || !process.connected) {
		callback(/* @__PURE__ */ new Error("Spawn broker parent disconnected"));
		return;
	}
	process.send(message, handle, { keepOpen: false }, callback);
});
function report(message, handle) {
	return sender.send(message, handle).catch((error) => {
		shutdown();
		throw error;
	});
}
function forget(id, entry) {
	if (entry.announced && entry.exited && entry.resultSettled && entry.openPipes.size === 0) owned.delete(id);
}
function shutdown() {
	if (stopping) return;
	stopping = true;
	resources?.disconnect();
	sender.close(/* @__PURE__ */ new Error("Spawn broker parent disconnected"));
	receiver.clear();
	const terminations = [];
	for (const entry of owned.values()) {
		if (entry.child.connected) entry.child.disconnect();
		if (entry.child.pid) terminations.push(killProcessTree(entry.child.pid, {
			detached: entry.detached,
			graceMs: GRACEFUL_CANCEL_TIMEOUT_MS
		}));
	}
	const signalGroup = (signal) => {
		try {
			process.kill(-process.pid, signal);
		} catch {}
	};
	const finish = () => {
		for (const termination of terminations) termination?.force();
		signalGroup("SIGKILL");
		process.exit(0);
	};
	signalGroup("SIGTERM");
	if (owned.size === 0 && starting.size === 0 && !resources?.size && hasLiveOwnedProcessGroupMembers() === false) {
		finish();
		return;
	}
	setTimeout(finish, 5250);
}
function disposeFailedChild(child) {
	if (!child) return;
	child.once("error", () => {});
	if (child.pid) child.kill("SIGKILL");
	if (child.connected) child.disconnect();
	for (const stream of child.stdio ?? []) stream?.destroy();
}
async function launch(message) {
	if (stopping || owned.size + starting.size + (resources?.size ?? 0) >= 256) {
		const error = new SpawnBrokerError("Spawn broker request capacity exceeded");
		await report({
			type: "execa-result",
			id: message.id,
			result: {
				failed: true,
				code: error.code,
				timedOut: false,
				isCanceled: false,
				isGracefullyCanceled: false,
				isMaxBuffer: false,
				isTerminated: false,
				isForcefullyTerminated: false,
				command: "",
				escapedCommand: "",
				cwd: process.cwd(),
				durationMs: 0,
				error: serializeExecaError(error)
			}
		});
		await report({
			type: "error",
			id: message.id,
			error: serializeBrokerError(error),
			resultUnavailable: true
		});
		return;
	}
	const pending = {};
	starting.set(message.id, pending);
	let spawnedChild;
	const assertActive = () => {
		if (stopping || !process.connected) throw new Error("Spawn broker is stopping");
	};
	try {
		const admission = await sender.reserve(async (publish) => {
			assertActive();
			const execa = message.type === "spawn-execa" ? await startBrokerExeca(message.argv, message.options, assertActive) : void 0;
			const child = execa?.child ?? (message.type === "spawn" ? spawnWithInheritedOomScore(message.argv[0], message.argv.slice(1), message.options) : void 0);
			spawnedChild = child;
			if (!child) throw new Error("Spawn broker command did not start");
			if (!execa) {
				if (child.stdio === void 0) {
					const closed = new Promise((resolve) => {
						child.once("close", () => resolve());
					});
					const error = await new Promise((resolve) => {
						child.once("error", resolve);
					});
					await closed;
					throw error;
				}
				for (const [fd, stream] of child.stdio.entries()) if (fd > 0 && stream instanceof Socket) holdPipeForTransfer(stream);
			}
			if (execa && !child.pid) {
				for (const [fd] of execa.stdio.entries()) execa.outputDrained(fd);
				disposeFailedChild(child);
				return {
					type: "failed",
					execa
				};
			}
			const current = {
				child,
				detached: message.options.detached === true || message.type === "spawn-execa" && message.options.killDescendants === true,
				execa,
				announced: false,
				events: [],
				exited: false,
				resultSettled: !execa,
				openPipes: new Set((execa?.stdio ?? child.stdio).flatMap((stream, fd) => stream instanceof Socket ? [fd] : []))
			};
			owned.set(message.id, current);
			if (pending.canceled) execa?.cancel();
			if (pending.signal !== void 0) (execa?.kill ?? child.kill.bind(child))(pending.signal);
			const event = (value) => {
				if (current.announced) report(value).catch(() => {});
				else if (current.events.length < 32) current.events.push(value);
				else {
					child.kill("SIGKILL");
					shutdown();
				}
			};
			child.on("error", (error) => event({
				type: "error",
				id: message.id,
				error: serializeBrokerError(error)
			}));
			child.on("message", (payload) => event({
				type: "ipc",
				id: message.id,
				message: payload
			}));
			child.once("disconnect", () => event({
				type: "disconnect",
				id: message.id
			}));
			child.once("exit", (code, signal) => event({
				type: "exit",
				id: message.id,
				code,
				signal
			}));
			child.once("close", () => {
				event({
					type: "closed",
					id: message.id
				});
				current.exited = true;
				forget(message.id, current);
			});
			if (execa) execa.result.then(async (result) => {
				event({
					type: "execa-result",
					id: message.id,
					result
				});
				current.resultSettled = true;
				forget(message.id, current);
			}).catch((error) => {
				event({
					type: "error",
					id: message.id,
					error: serializeBrokerError(toErrorObject(error, "Spawn broker execa result failed")),
					resultUnavailable: true
				});
				current.resultSettled = true;
				forget(message.id, current);
			});
			if (!execa) await new Promise((resolve, reject) => {
				child.once("spawn", resolve);
				child.once("error", reject);
			});
			if (!child.pid) throw new Error("Spawn broker command has no process identifier");
			await publish({
				type: "owned",
				id: message.id,
				pid: child.pid
			});
			return {
				type: "started",
				entry: current
			};
		});
		if (admission.type === "failed") {
			const result = await admission.execa.result;
			await report({
				type: "execa-result",
				id: message.id,
				result
			});
			await report({
				type: "error",
				id: message.id,
				error: {
					message: result.error?.message ?? result.shortMessage ?? "Command could not be spawned",
					code: result.code
				}
			});
			return;
		}
		const current = admission.entry;
		const { child, execa } = current;
		if (stopping) {
			if (child.connected) child.disconnect();
			if (child.pid) killProcessTree(child.pid, {
				detached: current.detached,
				graceMs: GRACEFUL_CANCEL_TIMEOUT_MS
			});
			return;
		}
		const streams = execa?.stdio ?? child.stdio;
		for (const [fd, stream] of streams.entries()) {
			if (!(stream instanceof Socket)) continue;
			stream.pause();
			await report({
				type: "pipe",
				id: message.id,
				fd
			}, stream);
			if (fd > 0) await report({
				type: "pipe-prefix",
				id: message.id,
				fd,
				bytes: takePipePrefix(stream)
			});
			if (!execa) stream.destroy();
		}
		if (!child.pid) throw new Error("Spawn broker command has no process identifier");
		await report({
			type: "spawned",
			id: message.id,
			pid: child.pid,
			spawnfile: child.spawnfile,
			spawnargs: child.spawnargs,
			connected: child.connected,
			stdioLength: streams.length
		});
		for (;;) {
			const value = current.events.shift();
			if (value === void 0) break;
			await report(value);
		}
		current.announced = true;
		forget(message.id, current);
	} catch (error) {
		disposeFailedChild(spawnedChild);
		owned.delete(message.id);
		if (!stopping) await report({
			type: "error",
			id: message.id,
			error: serializeBrokerError(error instanceof Error ? error : new Error(String(error))),
			resultUnavailable: true
		});
	} finally {
		starting.delete(message.id);
	}
}
process.once("disconnect", shutdown);
const onSupervisorSignal = () => {
	if (!process.connected) shutdown();
};
process.on("SIGTERM", onSupervisorSignal);
process.on("SIGINT", onSupervisorSignal);
process.on("message", (raw, handle) => {
	if (startup === "waiting") {
		startup = "initializing";
		initialize(raw).catch(shutdown);
		return;
	}
	if (startup !== "ready") {
		shutdown();
		return;
	}
	let decoded;
	try {
		decoded = receiver.receive(raw);
	} catch {
		shutdown();
		return;
	}
	if (decoded === void 0) return;
	const message = decoded;
	if (message.type === "shutdown") {
		shutdown();
		return;
	}
	if (message.type === "pipe-received") {
		sender.acknowledge(message.id, message.fd);
		return;
	}
	if (message.type === "spawn" || message.type === "spawn-execa") {
		launch(message).catch(shutdown);
		return;
	}
	if (message.type === "resource-seal" || message.type === "resource-target" || message.type === "resource-owner" || message.type === "resource-close" || message.type === "resource-release") {
		resources?.receive(message);
		return;
	}
	const entry = owned.get(message.id);
	if (!entry) {
		const pending = starting.get(message.id);
		if (pending && message.type === "cancel") pending.canceled = true;
		if (pending && message.type === "kill") pending.signal = message.signal;
		if (message.type === "ipc") report({
			type: "ipc-sent",
			id: message.id,
			sequence: message.sequence,
			error: { message: "Child process IPC channel is closed" }
		}).catch(() => {});
		return;
	}
	if (message.type === "kill") (entry.execa?.kill ?? entry.child.kill.bind(entry.child))(message.signal);
	else if (message.type === "cancel") entry.execa?.cancel();
	else if (message.type === "output-drained") {
		entry.execa?.outputDrained(message.fd, message.error ? Object.assign(new Error(message.error.message), message.error) : void 0);
		entry.openPipes.delete(message.fd);
		forget(message.id, entry);
	} else if (message.type === "disconnect" && entry.child.connected) entry.child.disconnect();
	else if (message.type === "ipc") {
		const acknowledge = (error) => {
			report({
				type: "ipc-sent",
				id: message.id,
				sequence: message.sequence,
				...error ? { error: serializeBrokerError(error) } : {}
			}).catch(() => {});
		};
		if (!entry.child.connected) acknowledge(/* @__PURE__ */ new Error("Child process IPC channel is closed"));
		else try {
			entry.child.send(message.message, handle, { keepOpen: true }, acknowledge);
		} catch (error) {
			acknowledge(error instanceof Error ? error : new Error(String(error)));
		}
	}
});
async function initialize(raw) {
	if (!isRecord(raw) || raw.type !== "bootstrap") throw new Error("Invalid spawn broker bootstrap");
	if (stopping || !process.connected) return;
	if (raw.nativeResource !== void 0) {
		const authority = raw.nativeResource;
		if (!isRecord(authority) || typeof authority.endpoint !== "string" || !authority.endpoint || typeof authority.secret !== "string" || !authority.secret || typeof authority.generation !== "number" || !Number.isSafeInteger(authority.generation) || authority.generation < 0) throw new Error("Invalid native resource bootstrap authority");
		resources = await createBrokerNativeResourceServer({
			endpoint: authority.endpoint,
			secret: authority.secret,
			generation: authority.generation,
			reportParent: report,
			canAdmit: () => !stopping && owned.size + starting.size + (resources?.size ?? 0) < 256
		});
		if (stopping) {
			resources.disconnect();
			return;
		}
	}
	startup = "ready";
	await report({
		type: "ready",
		pid: process.pid
	});
}
//#endregion
export {};