UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

2,938 lines 101 kB
import { a as normalizeLowercaseStringOrEmpty, c as normalizeOptionalString, o as normalizeNullableString } from "./string-coerce-mnp54Vah.js";
import { t as formatCliCommand } from "./command-format-CKGmlpAQ.js";
import { t as parseInlineOptionToken } from "./inline-option-token-Dqt7rKG4.js";
import { t as formatDocsLink } from "./links-CsLBrRff.js";
import { r as theme, t as colorize } from "./theme-vjDs9tao.js";
import { l as normalizeStringEntries } from "./string-normalization-WNUDCpXX.js";
import { l as sanitizeSystemRunEnvOverrides, s as sanitizeHostExecEnv, t as inspectHostExecEnvOverrides } from "./host-env-security-RnaFYKhk.js";
import { t as sameFileIdentity } from "./file-identity-BKNyWMFA.js";
import { _ as resolveNodeSystemdServiceName, g as resolveNodeLaunchAgentLabel, l as formatNodeServiceDescription, v as resolveNodeWindowsTaskName } from "./constants-e049XCW0.js";
import { n as resolveDaemonInstallRuntimeInputs, r as resolveDaemonNodeBinDir, s as resolveNodeProgramArguments, t as emitDaemonInstallRuntimeWarning } from "./daemon-install-plan.shared-Dr7I1qWf.js";
import { n as VERSION } from "./version-Crcn9X9T.js";
import { u as buildNodeServiceEnvironment } from "./runtime-paths-DS_J3TGI.js";
import { n as defaultRuntime } from "./runtime-B4lgFmsS.js";
import { u as normalizeAgentId } from "./session-key-B_NoIfpX.js";
import "./fs-safe-advanced-CBe_wA_B.js";
import { r as isGatewayDaemonRuntime, t as DEFAULT_GATEWAY_DAEMON_RUNTIME } from "./daemon-runtime-C76za6vm.js";
import { a as decodeWindowsOutputBuffer, o as resolveWindowsConsoleEncoding } from "./windows-command-CggaAJAY.js";
import { a as logWarn } from "./logger-lqqYRtFw.js";
import { i as getRuntimeConfig } from "./io-Gi7-pyU-.js";
import { _ as resolveExecutableFromPathEnv, a as resolveApprovalAuditTrustPath, s as resolveCommandResolutionFromArgv } from "./exec-command-resolution-CqhcxBz-.js";
import { O as normalizeExecutableToken, S as unwrapKnownDispatchWrapperInvocation, d as unwrapKnownShellMultiplexerInvocation, f as POSIX_INLINE_COMMAND_FLAGS, g as resolveInlineCommandMatch, i as extractShellWrapperCommand, k as splitShellArgs, l as isShellWrapperInvocation, p as advancePosixInlineOptionScan, s as isBlockedShellWrapperCommand, t as POSIX_SHELL_WRAPPERS, u as resolveShellWrapperTransportArgv, v as extractEnvAssignmentKeysFromDispatchWrappers } from "./shell-wrapper-resolution-CFL_Vekh.js";
import "./config-C9RxTsn1.js";
import { n as formatInvalidConfigPort, r as formatInvalidPortOption } from "./error-format-COoNML-C.js";
import { n as buildPlatformServiceStartHints, r as formatRuntimeStatus, t as buildPlatformRuntimeLogHints } from "./runtime-hints-BXTNKBW9.js";
import { d as resolveRuntimeStatusColor, g as installDaemonServiceAndEmit, n as createDaemonInstallActionContext, p as buildDaemonServiceSnapshot, r as failIfNixDaemonInstallMode, t as createCliStatusTextStyles } from "./shared-BEkveSUA.js";
import { t as parsePort } from "./parse-port-CbiRuE9n.js";
import { i as GATEWAY_CLIENT_NAMES, r as GATEWAY_CLIENT_MODES } from "./client-info-CcqJJIan.js";
import { n as GatewayClient } from "./client-C2g2lFC5.js";
import { r as loadOrCreateDeviceIdentity } from "./device-identity-CEPJolq9.js";
import { t as startGatewayClientWhenEventLoopReady } from "./client-start-readiness-BIoVQZze.js";
import { t as ConnectErrorDetailCodes } from "./connect-error-details-BXqba0zp.js";
import { i as runServiceUninstall, n as runServiceStart, r as runServiceStop, t as runServiceRestart } from "./lifecycle-core-BDdRclCf.js";
import { o as getActivePluginRegistry } from "./runtime-B2ROiq5l.js";
import { i as NODE_SYSTEM_RUN_COMMANDS, n as NODE_EXEC_APPROVALS_COMMANDS } from "./node-commands-SnI8Vs7F.js";
import { t as getMachineDisplayName } from "./machine-name-CBTz5R12.js";
import "./exec-wrapper-resolution-VZBsT_RG.js";
import { c as describeInterpreterInlineEval } from "./risks-DRxJ1pW5.js";
import { i as buildSafeBinsShellCommand, n as analyzeShellCommand, s as resolvePlannedSegmentArgv, t as analyzeArgvCommand } from "./exec-approvals-analysis-B4N2JXzl.js";
import { n as evaluateShellAllowlist, t as evaluateExecAllowlist } from "./exec-approvals-allowlist-VYEHBfj3.js";
import { B as resolveExecPolicyForMode, C as readExecApprovalsSnapshot, H as saveExecApprovals, M as resolveExecApprovals, O as requiresExecApproval, S as persistAllowAlwaysPatterns, U as requestJsonlSocket, c as ensureExecApprovals, g as normalizeExecApprovals, h as minSecurity, k as resolveAllowAlwaysPatternCoverage, l as hasDurableExecApproval, m as mergeExecApprovalsSocketDefaults, o as addDurableCommandApproval, p as maxAsk, s as commandRequiresSecurityAuditSuppressionApproval, w as recordAllowlistMatchesUse, z as resolveExecModePolicy } from "./exec-approvals-C6M6SGY3.js";
import { r as resolveExecSafeBinRuntimePolicy, t as isInterpreterLikeSafeBin } from "./exec-safe-bin-runtime-policy-DKOVy8lE.js";
import { t as ensureOpenClawCliOnPath } from "./path-env-CDAYsLgD.js";
import { t as resolveGatewayConnectionAuth } from "./connection-auth-zbD0QFhI.js";
import { t as formatHelpExamples } from "./help-format-CAcwboTs.js";
import { n as detectPolicyInlineEval } from "./policy-KTeYxilK.js";
import { c as normalizeSystemRunApprovalPlan, n as formatExecCommand, r as resolveSystemRunCommandRequest } from "./system-run-command-BO3cgBjR.js";
import { n as loadNodeHostConfig, r as saveNodeHostConfig, t as ensureNodeHostConfig } from "./config-QGn_oQSd.js";
import { t as resolveNodeService } from "./node-service-njT72UFj.js";
import fs from "node:fs";
import path from "node:path";
import { spawn } from "node:child_process";
import crypto from "node:crypto";
//#region src/infra/exec-host.ts
/** Send an authenticated exec request over the host JSONL socket. */
async function requestExecHostViaSocket(params) {
	const { socketPath, token, request } = params;
	if (!socketPath || !token) return null;
	const timeoutMs = params.timeoutMs ?? 2e4;
	const requestJson = JSON.stringify(request);
	const nonce = crypto.randomBytes(16).toString("hex");
	const ts = Date.now();
	const hmac = crypto.createHmac("sha256", token).update(`${nonce}:${ts}:${requestJson}`).digest("hex");
	return await requestJsonlSocket({
		socketPath,
		requestLine: JSON.stringify({
			type: "exec",
			id: crypto.randomUUID(),
			nonce,
			ts,
			hmac,
			requestJson
		}),
		timeoutMs,
		accept: (value) => {
			const msg = value;
			if (msg?.type !== "exec-res") return;
			if (msg.ok === true && msg.payload) return {
				ok: true,
				payload: msg.payload
			};
			if (msg.ok === false && msg.error) return {
				ok: false,
				error: msg.error
			};
			return null;
		}
	});
}
//#endregion
//#region src/node-host/exec-policy.ts
/** Evaluates node-host exec policy from security, approval, and allowlist context. */
/** Normalizes raw approval decisions from node-host payloads. */
function resolveExecApprovalDecision(value) {
	if (value === "allow-once" || value === "allow-always") return value;
	return null;
}
function formatSystemRunAllowlistMissMessage(params) {
	if (params?.windowsShellWrapperBlocked) return "SYSTEM_RUN_DENIED: allowlist miss (Windows shell wrappers like cmd.exe /c require approval; approve once/always or run with --ask on-miss|always)";
	if (params?.shellWrapperBlocked) return "SYSTEM_RUN_DENIED: allowlist miss (shell wrappers like sh/bash/zsh -c require approval; approve once/always or run with --ask on-miss|always)";
	return "SYSTEM_RUN_DENIED: allowlist miss";
}
/** Combines exec security, allowlist analysis, and approval state into an allow/deny decision. */
function evaluateSystemRunPolicy(params) {
	const windowsShellWrapperBlocked = params.security === "allowlist" && params.shellWrapperInvocation && params.isWindows && params.cmdInvocation;
	const shellWrapperBlocked = windowsShellWrapperBlocked;
	const analysisOk = shellWrapperBlocked ? false : params.analysisOk;
	const allowlistSatisfied = shellWrapperBlocked ? false : params.allowlistSatisfied;
	const approvedByAsk = params.approvalDecision !== null || params.approved === true;
	if (params.security === "deny") return {
		allowed: false,
		eventReason: "security=deny",
		errorMessage: "SYSTEM_RUN_DISABLED: security=deny",
		analysisOk,
		allowlistSatisfied,
		shellWrapperBlocked,
		windowsShellWrapperBlocked,
		requiresAsk: false,
		approvalDecision: params.approvalDecision,
		approvedByAsk
	};
	const requiresAsk = requiresExecApproval({
		ask: params.ask,
		security: params.security,
		analysisOk,
		allowlistSatisfied,
		durableApprovalSatisfied: params.durableApprovalSatisfied
	});
	if (requiresAsk && !approvedByAsk) return {
		allowed: false,
		eventReason: "approval-required",
		errorMessage: "SYSTEM_RUN_DENIED: approval required",
		analysisOk,
		allowlistSatisfied,
		shellWrapperBlocked,
		windowsShellWrapperBlocked,
		requiresAsk,
		approvalDecision: params.approvalDecision,
		approvedByAsk
	};
	if (params.security === "allowlist" && (!analysisOk || !allowlistSatisfied) && !approvedByAsk) {
		if (params.durableApprovalSatisfied) return {
			allowed: true,
			analysisOk,
			allowlistSatisfied,
			shellWrapperBlocked,
			windowsShellWrapperBlocked,
			requiresAsk,
			approvalDecision: params.approvalDecision,
			approvedByAsk
		};
		return {
			allowed: false,
			eventReason: "allowlist-miss",
			errorMessage: formatSystemRunAllowlistMissMessage({
				shellWrapperBlocked,
				windowsShellWrapperBlocked
			}),
			analysisOk,
			allowlistSatisfied,
			shellWrapperBlocked,
			windowsShellWrapperBlocked,
			requiresAsk,
			approvalDecision: params.approvalDecision,
			approvedByAsk
		};
	}
	return {
		allowed: true,
		analysisOk,
		allowlistSatisfied,
		shellWrapperBlocked,
		windowsShellWrapperBlocked,
		requiresAsk,
		approvalDecision: params.approvalDecision,
		approvedByAsk
	};
}
//#endregion
//#region src/node-host/invoke-system-run-allowlist.ts
/** Resolves system.run allowlist matches, argv plans, and truncated command output. */
/**
* Allowlist analysis and argv rewriting for node-host system.run.
*
* This module keeps command approval analysis separate from process execution,
* and only rewrites shell transports when the rebuilt command still satisfies policy.
*/
const POSIX_SHELL_WRAPPER_NAMES = POSIX_SHELL_WRAPPERS;
/** Evaluate system.run argv or shell command against the exec allowlist policy. */
/** Evaluates analyzed command segments against allowlist and trusted safe-bin policy. */
function evaluateSystemRunAllowlist(params) {
	if (params.shellCommand) {
		const allowlistEval = evaluateShellAllowlist({
			command: params.shellCommand,
			allowlist: params.approvals.allowlist,
			safeBins: params.safeBins,
			safeBinProfiles: params.safeBinProfiles,
			cwd: params.cwd,
			env: params.env,
			trustedSafeBinDirs: params.trustedSafeBinDirs,
			skillBins: params.skillBins,
			autoAllowSkills: params.autoAllowSkills,
			platform: process.platform
		});
		return {
			analysisOk: allowlistEval.analysisOk,
			allowlistMatches: allowlistEval.allowlistMatches,
			allowlistSatisfied: params.security === "allowlist" && allowlistEval.analysisOk ? allowlistEval.allowlistSatisfied : false,
			segments: allowlistEval.segments,
			segmentAllowlistEntries: allowlistEval.segmentAllowlistEntries,
			segmentSatisfiedBy: allowlistEval.segmentSatisfiedBy
		};
	}
	const analysis = analyzeArgvCommand({
		argv: params.argv,
		cwd: params.cwd,
		env: params.env
	});
	const allowlistEval = evaluateExecAllowlist({
		analysis,
		allowlist: params.approvals.allowlist,
		safeBins: params.safeBins,
		safeBinProfiles: params.safeBinProfiles,
		cwd: params.cwd,
		trustedSafeBinDirs: params.trustedSafeBinDirs,
		skillBins: params.skillBins,
		autoAllowSkills: params.autoAllowSkills
	});
	return {
		analysisOk: analysis.ok,
		allowlistMatches: allowlistEval.allowlistMatches,
		allowlistSatisfied: params.security === "allowlist" && analysis.ok ? allowlistEval.allowlistSatisfied : false,
		segments: analysis.segments,
		segmentAllowlistEntries: allowlistEval.segmentAllowlistEntries,
		segmentSatisfiedBy: allowlistEval.segmentSatisfiedBy
	};
}
/** Resolve the single planned argv that can replace the caller argv after allowlist approval. */
function resolvePlannedAllowlistArgv(params) {
	if (params.security !== "allowlist" || params.policy.approvedByAsk || params.shellCommand || !params.policy.analysisOk || !params.policy.allowlistSatisfied || params.segments.length !== 1) return;
	const plannedAllowlistArgv = resolvePlannedSegmentArgv(params.segments[0]);
	return plannedAllowlistArgv && plannedAllowlistArgv.length > 0 ? plannedAllowlistArgv : null;
}
/** Resolve final argv after safe-bin shell rewriting and allowlist revalidation. */
function resolveSystemRunExecArgv(params) {
	let execArgv = params.plannedAllowlistArgv ?? params.argv;
	if (params.security === "allowlist" && params.isWindows && !params.policy.approvedByAsk && params.shellCommand && params.policy.analysisOk && params.policy.allowlistSatisfied && params.segments.length === 1 && params.segments[0]?.argv.length > 0) execArgv = params.segments[0].argv;
	if (params.security === "allowlist" && !params.isWindows && !params.policy.approvedByAsk && params.shellCommand && params.policy.analysisOk && params.policy.allowlistSatisfied && params.segmentSatisfiedBy.some((entry) => entry === "safeBins" || entry === "inlineChain") && isPosixShellInlineCommandTransport(params.argv)) {
		const rebuilt = buildSafeBinsShellCommand({
			command: params.shellCommand,
			segments: params.segments,
			segmentSatisfiedBy: params.segmentSatisfiedBy,
			cwd: params.cwd,
			env: params.env,
			platform: process.platform
		});
		if (!rebuilt.ok || !rebuilt.command) return null;
		const rewrittenArgv = replacePosixShellInlineCommand({
			argv: params.argv,
			oldCommand: params.shellCommand,
			nextCommand: rebuilt.command
		});
		if (!rewrittenArgv) return null;
		const rebuiltAllowlist = evaluateSystemRunAllowlist({
			shellCommand: rebuilt.command,
			argv: rewrittenArgv,
			approvals: params.approvals,
			security: params.security,
			safeBins: params.safeBins,
			safeBinProfiles: params.safeBinProfiles,
			trustedSafeBinDirs: params.trustedSafeBinDirs,
			cwd: params.cwd,
			env: params.env,
			skillBins: params.skillBins,
			autoAllowSkills: params.autoAllowSkills
		});
		if (!rebuiltAllowlist.analysisOk || !rebuiltAllowlist.allowlistSatisfied) return null;
		execArgv = rewrittenArgv;
	}
	return execArgv;
}
function isPosixShellInlineCommandTransport(argv) {
	const transportArgv = resolveShellWrapperTransportArgv(argv);
	return Boolean(transportArgv && POSIX_SHELL_WRAPPER_NAMES.has(normalizeExecutableToken(transportArgv[0] ?? "")));
}
function findSubsequence(haystack, needle) {
	if (needle.length === 0 || needle.length > haystack.length) return -1;
	for (let start = 0; start <= haystack.length - needle.length; start += 1) {
		let matches = true;
		for (let offset = 0; offset < needle.length; offset += 1) if (haystack[start + offset] !== needle[offset]) {
			matches = false;
			break;
		}
		if (matches) return start;
	}
	return -1;
}
function replacePosixShellInlineCommand(params) {
	const transportArgv = resolveShellWrapperTransportArgv(params.argv);
	if (!transportArgv || !POSIX_SHELL_WRAPPER_NAMES.has(normalizeExecutableToken(transportArgv[0] ?? ""))) return null;
	const transportStart = findSubsequence(params.argv, transportArgv);
	if (transportStart < 0) return null;
	const match = resolveInlineCommandMatch(transportArgv, POSIX_INLINE_COMMAND_FLAGS, { allowCombinedC: true });
	if (match.valueTokenIndex === null) return null;
	const absoluteValueIndex = transportStart + match.valueTokenIndex;
	const token = params.argv[absoluteValueIndex];
	if (token === void 0) return null;
	const rewritten = [...params.argv];
	if (token === params.oldCommand) {
		rewritten[absoluteValueIndex] = params.nextCommand;
		return rewritten;
	}
	if (token.endsWith(params.oldCommand)) {
		rewritten[absoluteValueIndex] = token.slice(0, token.length - params.oldCommand.length) + params.nextCommand;
		return rewritten;
	}
	return null;
}
/** Mark truncated output in stderr when possible, otherwise stdout. */
/** Truncates captured stdout/stderr in place to the node-host output cap. */
function applyOutputTruncation(result) {
	if (!result.truncated) return;
	const suffix = "... (truncated)";
	if (result.stderr.trim().length > 0) result.stderr = `${result.stderr}\n${suffix}`;
	else result.stdout = `${result.stdout}\n${suffix}`;
}
//#endregion
//#region src/node-host/invoke-system-run-plan.ts
/** Builds and revalidates system.run approval plans for cwd and mutable executable operands. */
const MUTABLE_ARGV1_INTERPRETER_PATTERNS = [
	/^(?:node|nodejs)$/,
	/^perl$/,
	/^php$/,
	/^python(?:\d+(?:\.\d+)*)?$/,
	/^ruby$/
];
const GENERIC_MUTABLE_SCRIPT_RUNNERS = new Set([
	"esno",
	"jiti",
	"ts-node",
	"ts-node-esm",
	"tsx",
	"vite-node"
]);
const OPAQUE_MUTABLE_SCRIPT_RUNNERS = new Set(["busybox", "toybox"]);
const BUN_SUBCOMMANDS = new Set([
	"add",
	"audit",
	"completions",
	"create",
	"exec",
	"help",
	"init",
	"install",
	"link",
	"outdated",
	"patch",
	"pm",
	"publish",
	"remove",
	"repl",
	"run",
	"test",
	"unlink",
	"update",
	"upgrade",
	"x"
]);
const BUN_OPTIONS_WITH_VALUE = new Set([
	"--backend",
	"--bunfig",
	"--conditions",
	"--config",
	"--console-depth",
	"--cwd",
	"--define",
	"--elide-lines",
	"--env-file",
	"--extension-order",
	"--filter",
	"--hot",
	"--inspect",
	"--inspect-brk",
	"--inspect-wait",
	"--install",
	"--jsx-factory",
	"--jsx-fragment",
	"--jsx-import-source",
	"--loader",
	"--origin",
	"--port",
	"--preload",
	"--smol",
	"--tsconfig-override",
	"-c",
	"-e",
	"-p",
	"-r"
]);
const DENO_RUN_OPTIONS_WITH_VALUE = new Set([
	"--cached-only",
	"--cert",
	"--config",
	"--env-file",
	"--ext",
	"--harmony-import-attributes",
	"--import-map",
	"--inspect",
	"--inspect-brk",
	"--inspect-wait",
	"--location",
	"--log-level",
	"--lock",
	"--node-modules-dir",
	"--no-check",
	"--preload",
	"--reload",
	"--seed",
	"--strace-ops",
	"--unstable-bare-node-builtins",
	"--v8-flags",
	"--watch",
	"--watch-exclude",
	"-L"
]);
const NODE_OPTIONS_WITH_FILE_VALUE = new Set([
	"-r",
	"--experimental-loader",
	"--import",
	"--loader",
	"--require"
]);
const RUBY_UNSAFE_APPROVAL_FLAGS = new Set([
	"-I",
	"-r",
	"--require"
]);
const PERL_UNSAFE_APPROVAL_FLAGS = new Set([
	"-I",
	"-M",
	"-m"
]);
function normalizeOptionFlag(token) {
	return normalizeLowercaseStringOrEmpty(parseInlineOptionToken(token).name);
}
function readTrimmedArgToken(argv, index) {
	return normalizeNullableString(argv[index]) ?? "";
}
const POSIX_SHELL_OPTIONS_WITH_VALUE = new Set([
	"--init-file",
	"--rcfile",
	"--startup-script",
	"-O",
	"-o",
	"+O",
	"+o"
]);
const POSIX_SHELLS_WITH_PLUS_OPTIONS = new Set([
	"ash",
	"bash",
	"dash",
	"ksh",
	"sh",
	"zsh"
]);
function isPosixShellOptionToken(token, supportsPlusOptions) {
	return token.startsWith("-") || supportsPlusOptions && token.startsWith("+");
}
const NPM_EXEC_OPTIONS_WITH_VALUE = new Set([
	"--cache",
	"--package",
	"--prefix",
	"--script-shell",
	"--userconfig",
	"--workspace",
	"-p",
	"-w"
]);
const NPM_EXEC_FLAG_OPTIONS = new Set([
	"--no",
	"--quiet",
	"--ws",
	"--workspaces",
	"--yes",
	"-q",
	"-y"
]);
const PNPM_OPTIONS_WITH_VALUE = new Set([
	"--config",
	"--dir",
	"--filter",
	"--reporter",
	"--stream",
	"--test-pattern",
	"--workspace-concurrency",
	"-C"
]);
const PNPM_FLAG_OPTIONS = new Set([
	"--aggregate-output",
	"--color",
	"--parallel",
	"--recursive",
	"--silent",
	"--workspace-root",
	"-r",
	"-s",
	"-w"
]);
const PNPM_DLX_OPTIONS_WITH_VALUE = new Set([
	"--allow-build",
	"--package",
	"-p"
]);
function pathComponentsFromRootSync(targetPath) {
	const absolute = path.resolve(targetPath);
	const parts = [];
	let cursor = absolute;
	while (true) {
		parts.unshift(cursor);
		const parent = path.dirname(cursor);
		if (parent === cursor) return parts;
		cursor = parent;
	}
}
function isOwnedByCurrentProcessSync(candidate) {
	if (process.platform === "win32" || typeof process.getuid !== "function") return false;
	try {
		return fs.statSync(candidate).uid === process.getuid();
	} catch {
		return false;
	}
}
function isMutableByCurrentProcessSync(candidate) {
	try {
		fs.accessSync(candidate, fs.constants.W_OK);
		return true;
	} catch {
		return isOwnedByCurrentProcessSync(candidate);
	}
}
function hasMutableSymlinkPathComponentSync(targetPath) {
	for (const component of pathComponentsFromRootSync(targetPath)) try {
		if (!fs.lstatSync(component).isSymbolicLink()) continue;
		if (isMutableByCurrentProcessSync(path.dirname(component))) return true;
	} catch {
		return true;
	}
	return false;
}
function pathLooksMutableForShellPayloadSync(targetPath) {
	if (isMutableByCurrentProcessSync(targetPath) || isMutableByCurrentProcessSync(path.dirname(targetPath)) || hasMutableSymlinkPathComponentSync(targetPath)) return true;
	let realPath;
	try {
		realPath = fs.realpathSync(targetPath);
	} catch {
		return true;
	}
	return isMutableByCurrentProcessSync(realPath) || isMutableByCurrentProcessSync(path.dirname(realPath)) || hasMutableSymlinkPathComponentSync(realPath);
}
function shouldPinExecutableForApproval(params) {
	if (params.shellCommand !== null) return false;
	return (params.wrapperChain?.length ?? 0) === 0;
}
function hashFileContentsSync(filePath) {
	return crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
}
function looksLikePathToken(token) {
	return token.startsWith(".") || token.startsWith("/") || token.startsWith("\\") || token.includes("/") || token.includes("\\") || path.extname(token).length > 0;
}
function resolvesToExistingFileSync(rawOperand, cwd) {
	if (!rawOperand) return false;
	try {
		return fs.statSync(path.resolve(cwd ?? process.cwd(), rawOperand)).isFile();
	} catch {
		return false;
	}
}
function isKnownBinaryExecutableHeader(buffer) {
	if (buffer.length >= 4 && buffer.subarray(0, 4).equals(Buffer.from([
		127,
		69,
		76,
		70
	]))) return true;
	if (buffer.length >= 4 && (buffer.subarray(0, 4).equals(Buffer.from([
		254,
		237,
		250,
		206
	])) || buffer.subarray(0, 4).equals(Buffer.from([
		206,
		250,
		237,
		254
	])) || buffer.subarray(0, 4).equals(Buffer.from([
		254,
		237,
		250,
		207
	])) || buffer.subarray(0, 4).equals(Buffer.from([
		207,
		250,
		237,
		254
	])) || buffer.subarray(0, 4).equals(Buffer.from([
		202,
		254,
		186,
		190
	])) || buffer.subarray(0, 4).equals(Buffer.from([
		190,
		186,
		254,
		202
	])) || buffer.subarray(0, 4).equals(Buffer.from([
		202,
		254,
		186,
		191
	])) || buffer.subarray(0, 4).equals(Buffer.from([
		191,
		186,
		254,
		202
	])))) return true;
	if (buffer.length < 64 || !buffer.subarray(0, 2).equals(Buffer.from([77, 90]))) return false;
	const peOffset = buffer.readUInt32LE(60);
	return peOffset >= 0 && peOffset <= buffer.length - 4 && buffer.subarray(peOffset, peOffset + 4).equals(Buffer.from([
		80,
		69,
		0,
		0
	]));
}
function isLikelyScriptLikePathSync(targetPath) {
	let stat;
	try {
		stat = fs.statSync(targetPath);
	} catch {
		return true;
	}
	if (!stat.isFile()) return true;
	let header;
	try {
		const fd = fs.openSync(targetPath, "r");
		try {
			header = Buffer.alloc(1024);
			const bytesRead = fs.readSync(fd, header, 0, header.length, 0);
			header = header.subarray(0, bytesRead);
		} finally {
			fs.closeSync(fd);
		}
	} catch {
		return true;
	}
	if (header.length === 0) return true;
	if (header.subarray(0, 2).equals(Buffer.from("#!"))) return true;
	if (isKnownBinaryExecutableHeader(header)) return false;
	return true;
}
function unwrapArgvForMutableOperand(argv) {
	let current = argv;
	let baseIndex = 0;
	let opaqueMultiplexerSeen = false;
	while (true) {
		const dispatchUnwrap = unwrapKnownDispatchWrapperInvocation(current);
		if (dispatchUnwrap.kind === "unwrapped") {
			baseIndex += current.length - dispatchUnwrap.argv.length;
			current = dispatchUnwrap.argv;
			continue;
		}
		const shellMultiplexerUnwrap = unwrapKnownShellMultiplexerInvocation(current);
		if (shellMultiplexerUnwrap.kind === "unwrapped") {
			if (OPAQUE_MUTABLE_SCRIPT_RUNNERS.has(shellMultiplexerUnwrap.wrapper)) opaqueMultiplexerSeen = true;
			baseIndex += current.length - shellMultiplexerUnwrap.argv.length;
			current = shellMultiplexerUnwrap.argv;
			continue;
		}
		const packageManagerUnwrap = unwrapKnownPackageManagerExecInvocation(current);
		if (packageManagerUnwrap) {
			baseIndex += current.length - packageManagerUnwrap.length;
			current = packageManagerUnwrap;
			continue;
		}
		return {
			argv: current,
			baseIndex,
			opaqueMultiplexerSeen
		};
	}
}
function unwrapKnownPackageManagerExecInvocation(argv) {
	switch (normalizePackageManagerExecToken(argv[0] ?? "")) {
		case "npm": return unwrapNpmExecInvocation(argv);
		case "npx":
		case "bunx": return unwrapDirectPackageExecInvocation(argv);
		case "pnpm": return unwrapPnpmExecInvocation(argv);
		default: return null;
	}
}
function normalizePackageManagerExecToken(token) {
	const normalized = normalizeExecutableToken(token);
	if (!normalized) return normalized;
	return normalized.replace(/\.(?:c|m)?js$/i, "");
}
function unwrapPnpmExecInvocation(argv) {
	let idx = 1;
	while (idx < argv.length) {
		const token = readTrimmedArgToken(argv, idx);
		if (!token) {
			idx += 1;
			continue;
		}
		if (token === "--") {
			idx += 1;
			continue;
		}
		if (!token.startsWith("-")) {
			if (token === "exec") {
				if (idx + 1 >= argv.length) return null;
				const tail = argv.slice(idx + 1);
				return tail[0] === "--" ? tail.length > 1 ? tail.slice(1) : null : tail;
			}
			if (token === "dlx") return unwrapPnpmDlxInvocation(argv.slice(idx + 1));
			if (token === "node") {
				const tail = argv.slice(idx + 1);
				return ["node", ...tail[0] === "--" ? tail.slice(1) : tail];
			}
			return null;
		}
		const flag = normalizeOptionFlag(token);
		if (PNPM_OPTIONS_WITH_VALUE.has(flag) || PNPM_DLX_OPTIONS_WITH_VALUE.has(flag)) {
			idx += token.includes("=") ? 1 : 2;
			continue;
		}
		if (PNPM_FLAG_OPTIONS.has(flag)) {
			idx += 1;
			continue;
		}
		return null;
	}
	return null;
}
function unwrapPnpmDlxInvocation(argv) {
	let idx = 0;
	while (idx < argv.length) {
		const token = readTrimmedArgToken(argv, idx);
		if (!token) {
			idx += 1;
			continue;
		}
		if (token === "--") {
			const tail = argv.slice(idx + 1);
			return tail.length > 0 ? tail : null;
		}
		if (!token.startsWith("-")) return argv.slice(idx);
		const flag = normalizeOptionFlag(token);
		if (flag === "-c" || flag === "--shell-mode") return null;
		if (PNPM_OPTIONS_WITH_VALUE.has(flag) || PNPM_DLX_OPTIONS_WITH_VALUE.has(flag)) {
			idx += token.includes("=") ? 1 : 2;
			continue;
		}
		if (PNPM_FLAG_OPTIONS.has(flag)) {
			idx += 1;
			continue;
		}
		return null;
	}
	return null;
}
function unwrapDirectPackageExecInvocation(argv) {
	let idx = 1;
	while (idx < argv.length) {
		const token = readTrimmedArgToken(argv, idx);
		if (!token) {
			idx += 1;
			continue;
		}
		if (!token.startsWith("-")) return argv.slice(idx);
		const flag = normalizeOptionFlag(token);
		if (flag === "-c" || flag === "--call") return null;
		if (NPM_EXEC_OPTIONS_WITH_VALUE.has(flag)) {
			idx += token.includes("=") ? 1 : 2;
			continue;
		}
		if (NPM_EXEC_FLAG_OPTIONS.has(flag)) {
			idx += 1;
			continue;
		}
		return null;
	}
	return null;
}
function unwrapNpmExecInvocation(argv) {
	let idx = 1;
	while (idx < argv.length) {
		const token = readTrimmedArgToken(argv, idx);
		if (!token) {
			idx += 1;
			continue;
		}
		if (!token.startsWith("-")) {
			if (token !== "exec") return null;
			idx += 1;
			break;
		}
		if ((token === "-C" || token === "--prefix" || token === "--userconfig") && !token.includes("=")) {
			idx += 2;
			continue;
		}
		idx += 1;
	}
	if (idx >= argv.length) return null;
	const tail = argv.slice(idx);
	if (tail[0] === "--") return tail.length > 1 ? tail.slice(1) : null;
	return unwrapDirectPackageExecInvocation(["npx", ...tail]);
}
function resolvePosixShellScriptOperandIndex(argv, executable) {
	const supportsPlusOptions = POSIX_SHELLS_WITH_PLUS_OPTIONS.has(executable);
	if (resolveInlineCommandMatch(argv, POSIX_INLINE_COMMAND_FLAGS, {
		allowCombinedC: true,
		isOptionToken: (token) => isPosixShellOptionToken(token, supportsPlusOptions),
		stopAtFirstNonOption: true
	}).valueTokenIndex !== null) return null;
	let afterDoubleDash = false;
	for (let i = 1; i < argv.length; i += 1) {
		const token = readTrimmedArgToken(argv, i);
		if (!token) continue;
		if (token === "-") return null;
		if (!afterDoubleDash && token === "--") {
			afterDoubleDash = true;
			continue;
		}
		if (!afterDoubleDash && token === "-s") return null;
		if (!afterDoubleDash && isPosixShellOptionToken(token, supportsPlusOptions)) {
			const flag = normalizeOptionFlag(token);
			if (POSIX_SHELL_OPTIONS_WITH_VALUE.has(flag)) {
				if (!token.includes("=")) i += 1;
				continue;
			}
			i += advancePosixInlineOptionScan(token) - 1;
			continue;
		}
		return i;
	}
	return null;
}
function resolveOptionFilteredFileOperandIndex(params) {
	let afterDoubleDash = false;
	for (let i = params.startIndex; i < params.argv.length; i += 1) {
		const token = readTrimmedArgToken(params.argv, i);
		if (!token) continue;
		if (afterDoubleDash) return resolvesToExistingFileSync(token, params.cwd) ? i : null;
		if (token === "--") {
			afterDoubleDash = true;
			continue;
		}
		if (token === "-") return null;
		if (token.startsWith("-")) {
			if (!token.includes("=") && params.optionsWithValue?.has(token)) i += 1;
			continue;
		}
		return resolvesToExistingFileSync(token, params.cwd) ? i : null;
	}
	return null;
}
function resolveOptionFilteredPositionalIndex(params) {
	let afterDoubleDash = false;
	for (let i = params.startIndex; i < params.argv.length; i += 1) {
		const token = readTrimmedArgToken(params.argv, i);
		if (!token) continue;
		if (afterDoubleDash) return i;
		if (token === "--") {
			afterDoubleDash = true;
			continue;
		}
		if (token === "-") return null;
		if (token.startsWith("-")) {
			if (!token.includes("=") && params.optionsWithValue?.has(token)) i += 1;
			continue;
		}
		return i;
	}
	return null;
}
function collectExistingFileOperandIndexes(params) {
	let afterDoubleDash = false;
	const hits = [];
	for (let i = params.startIndex; i < params.argv.length; i += 1) {
		const token = readTrimmedArgToken(params.argv, i);
		if (!token) continue;
		if (afterDoubleDash) {
			if (resolvesToExistingFileSync(token, params.cwd)) hits.push(i);
			continue;
		}
		if (token === "--") {
			afterDoubleDash = true;
			continue;
		}
		if (token === "-") return {
			hits: [],
			sawOptionValueFile: false
		};
		if (token.startsWith("-")) {
			const option = parseInlineOptionToken(token);
			const flag = option.name;
			const inlineValue = option.hasInlineValue ? option.inlineValue : void 0;
			if (params.optionsWithFileValue?.has(normalizeLowercaseStringOrEmpty(flag))) {
				if (inlineValue && resolvesToExistingFileSync(inlineValue, params.cwd)) {
					hits.push(i);
					return {
						hits,
						sawOptionValueFile: true
					};
				}
				const nextToken = readTrimmedArgToken(params.argv, i + 1);
				if (!inlineValue && nextToken && resolvesToExistingFileSync(nextToken, params.cwd)) {
					hits.push(i + 1);
					return {
						hits,
						sawOptionValueFile: true
					};
				}
			}
			continue;
		}
		if (resolvesToExistingFileSync(token, params.cwd)) hits.push(i);
	}
	return {
		hits,
		sawOptionValueFile: false
	};
}
function resolveGenericInterpreterScriptOperandIndex(params) {
	const collection = collectExistingFileOperandIndexes({
		argv: params.argv,
		startIndex: 1,
		cwd: params.cwd,
		optionsWithFileValue: params.optionsWithFileValue
	});
	if (collection.sawOptionValueFile) return null;
	return collection.hits.length === 1 ? collection.hits[0] : null;
}
function resolveBunScriptOperandIndex(params) {
	const directIndex = resolveOptionFilteredPositionalIndex({
		argv: params.argv,
		startIndex: 1,
		optionsWithValue: BUN_OPTIONS_WITH_VALUE
	});
	if (directIndex === null) return null;
	const directToken = readTrimmedArgToken(params.argv, directIndex);
	if (directToken === "run") return resolveOptionFilteredFileOperandIndex({
		argv: params.argv,
		startIndex: directIndex + 1,
		cwd: params.cwd,
		optionsWithValue: BUN_OPTIONS_WITH_VALUE
	});
	if (BUN_SUBCOMMANDS.has(directToken)) return null;
	if (!looksLikePathToken(directToken)) return null;
	return directIndex;
}
function resolveDenoRunScriptOperandIndex(params) {
	if (readTrimmedArgToken(params.argv, 1) !== "run") return null;
	return resolveOptionFilteredFileOperandIndex({
		argv: params.argv,
		startIndex: 2,
		cwd: params.cwd,
		optionsWithValue: DENO_RUN_OPTIONS_WITH_VALUE
	});
}
function hasRubyUnsafeApprovalFlag(argv) {
	let afterDoubleDash = false;
	for (let i = 1; i < argv.length; i += 1) {
		const token = readTrimmedArgToken(argv, i);
		if (!token) continue;
		if (afterDoubleDash) return false;
		if (token === "--") {
			afterDoubleDash = true;
			continue;
		}
		if (token === "-I" || token === "-r") return true;
		if (token.startsWith("-I") || token.startsWith("-r")) return true;
		if (RUBY_UNSAFE_APPROVAL_FLAGS.has(normalizeLowercaseStringOrEmpty(token))) return true;
	}
	return false;
}
function hasPerlUnsafeApprovalFlag(argv) {
	let afterDoubleDash = false;
	for (let i = 1; i < argv.length; i += 1) {
		const token = readTrimmedArgToken(argv, i);
		if (!token) continue;
		if (afterDoubleDash) return false;
		if (token === "--") {
			afterDoubleDash = true;
			continue;
		}
		if (token === "-I" || token === "-M" || token === "-m") return true;
		if (token.startsWith("-I") || token.startsWith("-M") || token.startsWith("-m")) return true;
		if (PERL_UNSAFE_APPROVAL_FLAGS.has(token)) return true;
	}
	return false;
}
function isMutableScriptRunner(executable) {
	return GENERIC_MUTABLE_SCRIPT_RUNNERS.has(executable) || OPAQUE_MUTABLE_SCRIPT_RUNNERS.has(executable) || isInterpreterLikeSafeBin(executable);
}
function resolveMutableFileOperandIndex(argv, cwd) {
	const unwrapped = unwrapArgvForMutableOperand(argv);
	const executable = normalizeExecutableToken(unwrapped.argv[0] ?? "");
	if (!executable) return null;
	if (unwrapped.opaqueMultiplexerSeen || OPAQUE_MUTABLE_SCRIPT_RUNNERS.has(executable)) return null;
	if (POSIX_SHELL_WRAPPERS.has(executable)) {
		const shellIndex = resolvePosixShellScriptOperandIndex(unwrapped.argv, executable);
		return shellIndex === null ? null : unwrapped.baseIndex + shellIndex;
	}
	if (MUTABLE_ARGV1_INTERPRETER_PATTERNS.some((pattern) => pattern.test(executable))) {
		const operand = readTrimmedArgToken(unwrapped.argv, 1);
		if (operand && operand !== "-" && !operand.startsWith("-")) return unwrapped.baseIndex + 1;
	}
	if (executable === "bun") {
		const bunIndex = resolveBunScriptOperandIndex({
			argv: unwrapped.argv,
			cwd
		});
		if (bunIndex !== null) return unwrapped.baseIndex + bunIndex;
	}
	if (executable === "deno") {
		const denoIndex = resolveDenoRunScriptOperandIndex({
			argv: unwrapped.argv,
			cwd
		});
		if (denoIndex !== null) return unwrapped.baseIndex + denoIndex;
	}
	if (executable === "ruby" && hasRubyUnsafeApprovalFlag(unwrapped.argv)) return null;
	if (executable === "perl" && hasPerlUnsafeApprovalFlag(unwrapped.argv)) return null;
	if (!isMutableScriptRunner(executable)) return null;
	const genericIndex = resolveGenericInterpreterScriptOperandIndex({
		argv: unwrapped.argv,
		cwd,
		optionsWithFileValue: executable === "node" || executable === "nodejs" ? NODE_OPTIONS_WITH_FILE_VALUE : void 0
	});
	return genericIndex === null ? null : unwrapped.baseIndex + genericIndex;
}
function shellPayloadNeedsStableBinding(shellCommand, cwd) {
	const argv = splitShellArgs(shellCommand);
	if (!argv || argv.length === 0) return false;
	const snapshot = resolveMutableFileOperandSnapshotSync({
		argv,
		cwd,
		shellCommand: null
	});
	if (!snapshot.ok) return true;
	if (snapshot.snapshot) return true;
	const firstToken = readTrimmedArgToken(argv, 0);
	if (!resolvesToExistingFileSync(firstToken, cwd)) return false;
	if (!path.isAbsolute(firstToken)) return true;
	const resolvedPath = path.resolve(cwd ?? process.cwd(), firstToken);
	if (pathLooksMutableForShellPayloadSync(resolvedPath)) return true;
	return isLikelyScriptLikePathSync(resolvedPath);
}
function requiresStableInterpreterApprovalBindingWithShellCommand(params) {
	const unwrapped = unwrapArgvForMutableOperand(params.argv);
	if (unwrapped.opaqueMultiplexerSeen) return true;
	if (params.shellCommand !== null) return shellPayloadNeedsStableBinding(params.shellCommand, params.cwd);
	if (pnpmDlxInvocationNeedsFailClosedBinding(params.argv, params.cwd)) return true;
	const executable = normalizeExecutableToken(unwrapped.argv[0] ?? "");
	if (!executable) return false;
	if (POSIX_SHELL_WRAPPERS.has(executable)) return false;
	return isMutableScriptRunner(executable);
}
function pnpmDlxInvocationNeedsFailClosedBinding(argv, cwd) {
	if (normalizePackageManagerExecToken(argv[0] ?? "") !== "pnpm") return false;
	let idx = 1;
	while (idx < argv.length) {
		const token = readTrimmedArgToken(argv, idx);
		if (!token) {
			idx += 1;
			continue;
		}
		if (token === "--") {
			idx += 1;
			continue;
		}
		if (!token.startsWith("-")) {
			if (token !== "dlx") return false;
			return pnpmDlxTailNeedsFailClosedBinding(argv.slice(idx + 1), cwd);
		}
		const flag = normalizeOptionFlag(token);
		if (PNPM_OPTIONS_WITH_VALUE.has(flag) || PNPM_DLX_OPTIONS_WITH_VALUE.has(flag)) {
			idx += token.includes("=") ? 1 : 2;
			continue;
		}
		if (PNPM_FLAG_OPTIONS.has(flag)) {
			idx += 1;
			continue;
		}
		return true;
	}
	return false;
}
function pnpmDlxTailNeedsFailClosedBinding(argv, cwd) {
	let idx = 0;
	while (idx < argv.length) {
		const token = readTrimmedArgToken(argv, idx);
		if (!token) {
			idx += 1;
			continue;
		}
		if (token === "--") return pnpmDlxTailMayNeedStableBinding(argv.slice(idx + 1), cwd);
		if (!token.startsWith("-")) return pnpmDlxTailMayNeedStableBinding(argv.slice(idx), cwd);
		const flag = normalizeOptionFlag(token);
		if (flag === "-c" || flag === "--shell-mode") return false;
		if (PNPM_OPTIONS_WITH_VALUE.has(flag) || PNPM_DLX_OPTIONS_WITH_VALUE.has(flag)) {
			idx += token.includes("=") ? 1 : 2;
			continue;
		}
		if (PNPM_FLAG_OPTIONS.has(flag)) {
			idx += 1;
			continue;
		}
		return true;
	}
	return true;
}
function pnpmDlxTailMayNeedStableBinding(argv, cwd) {
	const snapshot = resolveMutableFileOperandSnapshotSync({
		argv,
		cwd,
		shellCommand: null
	});
	return snapshot.ok && snapshot.snapshot !== null;
}
/** Captures file identity for a mutable script operand that approval is bound to. */
function resolveMutableFileOperandSnapshotSync(params) {
	const argvIndex = resolveMutableFileOperandIndex(params.argv, params.cwd);
	if (argvIndex === null) {
		if (requiresStableInterpreterApprovalBindingWithShellCommand({
			argv: params.argv,
			shellCommand: params.shellCommand,
			cwd: params.cwd
		})) return {
			ok: false,
			message: "SYSTEM_RUN_DENIED: approval cannot safely bind this interpreter/runtime command"
		};
		return {
			ok: true,
			snapshot: null
		};
	}
	const rawOperand = readTrimmedArgToken(params.argv, argvIndex);
	if (!rawOperand) return {
		ok: false,
		message: "SYSTEM_RUN_DENIED: approval requires a stable script operand"
	};
	const resolvedPath = path.resolve(params.cwd ?? process.cwd(), rawOperand);
	let realPath;
	let stat;
	try {
		realPath = fs.realpathSync(resolvedPath);
		stat = fs.statSync(realPath);
	} catch {
		return {
			ok: false,
			message: "SYSTEM_RUN_DENIED: approval requires an existing script operand"
		};
	}
	if (!stat.isFile()) return {
		ok: false,
		message: "SYSTEM_RUN_DENIED: approval requires a file script operand"
	};
	return {
		ok: true,
		snapshot: {
			argvIndex,
			path: realPath,
			sha256: hashFileContentsSync(realPath)
		}
	};
}
function resolveCanonicalApprovalCwdSync(cwd) {
	const requestedCwd = path.resolve(cwd);
	let cwdLstat;
	let cwdStat;
	let cwdReal;
	let cwdRealStat;
	try {
		cwdLstat = fs.lstatSync(requestedCwd);
		cwdStat = fs.statSync(requestedCwd);
		cwdReal = fs.realpathSync(requestedCwd);
		cwdRealStat = fs.statSync(cwdReal);
	} catch {
		return {
			ok: false,
			message: "SYSTEM_RUN_DENIED: approval requires an existing canonical cwd"
		};
	}
	if (!cwdStat.isDirectory()) return {
		ok: false,
		message: "SYSTEM_RUN_DENIED: approval requires cwd to be a directory"
	};
	if (hasMutableSymlinkPathComponentSync(requestedCwd)) return {
		ok: false,
		message: "SYSTEM_RUN_DENIED: approval requires canonical cwd (no symlink path components)"
	};
	if (cwdLstat.isSymbolicLink()) return {
		ok: false,
		message: "SYSTEM_RUN_DENIED: approval requires canonical cwd (no symlink cwd)"
	};
	if (!sameFileIdentity(cwdStat, cwdLstat) || !sameFileIdentity(cwdStat, cwdRealStat) || !sameFileIdentity(cwdLstat, cwdRealStat)) return {
		ok: false,
		message: "SYSTEM_RUN_DENIED: approval cwd identity mismatch"
	};
	return {
		ok: true,
		snapshot: {
			cwd: cwdReal,
			stat: cwdStat
		}
	};
}
/** Rechecks that the approved cwd still points at the same directory identity. */
function revalidateApprovedCwdSnapshot(params) {
	const current = resolveCanonicalApprovalCwdSync(params.snapshot.cwd);
	if (!current.ok) return false;
	return sameFileIdentity(params.snapshot.stat, current.snapshot.stat);
}
function revalidateApprovedMutableFileOperand(params) {
	const operand = params.argv[params.snapshot.argvIndex]?.trim();
	if (!operand) return false;
	const resolvedPath = path.resolve(params.cwd ?? process.cwd(), operand);
	let realPath;
	try {
		realPath = fs.realpathSync(resolvedPath);
	} catch {
		return false;
	}
	if (realPath !== params.snapshot.path) return false;
	try {
		return hashFileContentsSync(realPath) === params.snapshot.sha256;
	} catch {
		return false;
	}
}
function hardenApprovedExecutionPaths(params) {
	if (!params.approvedByAsk) return {
		ok: true,
		argv: params.argv,
		argvChanged: false,
		cwd: params.cwd,
		approvedCwdSnapshot: void 0
	};
	let hardenedCwd = params.cwd;
	let approvedCwdSnapshot;
	if (hardenedCwd) {
		const canonicalCwd = resolveCanonicalApprovalCwdSync(hardenedCwd);
		if (!canonicalCwd.ok) return canonicalCwd;
		hardenedCwd = canonicalCwd.snapshot.cwd;
		approvedCwdSnapshot = canonicalCwd.snapshot;
	}
	if (params.argv.length === 0) return {
		ok: true,
		argv: params.argv,
		argvChanged: false,
		cwd: hardenedCwd,
		approvedCwdSnapshot
	};
	const resolution = resolveCommandResolutionFromArgv(params.argv, hardenedCwd);
	if (!shouldPinExecutableForApproval({
		shellCommand: params.shellCommand,
		wrapperChain: resolution?.wrapperChain
	})) return {
		ok: true,
		argv: params.argv,
		argvChanged: false,
		cwd: hardenedCwd,
		approvedCwdSnapshot
	};
	const pinnedExecutable = resolution?.execution.resolvedRealPath ?? resolution?.execution.resolvedPath;
	if (!pinnedExecutable) return {
		ok: false,
		message: "SYSTEM_RUN_DENIED: approval requires a stable executable path"
	};
	if (pinnedExecutable === params.argv[0]) return {
		ok: true,
		argv: params.argv,
		argvChanged: false,
		cwd: hardenedCwd,
		approvedCwdSnapshot
	};
	const argv = [...params.argv];
	argv[0] = pinnedExecutable;
	return {
		ok: true,
		argv,
		argvChanged: true,
		cwd: hardenedCwd,
		approvedCwdSnapshot
	};
}
function buildSystemRunApprovalPlan(params) {
	const command = resolveSystemRunCommandRequest({
		command: params.command,
		rawCommand: params.rawCommand
	});
	if (!command.ok) return {
		ok: false,
		message: command.message
	};
	if (command.argv.length === 0) return {
		ok: false,
		message: "command required"
	};
	if (command.shellPayload === null && isBlockedShellWrapperCommand(command.argv)) return {
		ok: false,
		message: "SYSTEM_RUN_DENIED: approval cannot safely bind this interpreter/runtime command"
	};
	const hardening = hardenApprovedExecutionPaths({
		approvedByAsk: true,
		argv: command.argv,
		shellCommand: command.shellPayload,
		cwd: normalizeNullableString(params.cwd) ?? void 0
	});
	if (!hardening.ok) return {
		ok: false,
		message: hardening.message
	};
	const commandText = formatExecCommand(hardening.argv);
	const commandPreview = command.previewText?.trim() && command.previewText.trim() !== commandText ? command.previewText.trim() : null;
	const mutableFileOperand = resolveMutableFileOperandSnapshotSync({
		argv: hardening.argv,
		cwd: hardening.cwd,
		shellCommand: command.shellPayload
	});
	if (!mutableFileOperand.ok) return {
		ok: false,
		message: mutableFileOperand.message
	};
	return {
		ok: true,
		plan: {
			argv: hardening.argv,
			cwd: hardening.cwd ?? null,
			commandText,
			commandPreview,
			agentId: normalizeNullableString(params.agentId),
			sessionKey: normalizeNullableString(params.sessionKey),
			mutableFileOperand: mutableFileOperand.snapshot ?? void 0
		}
	};
}
//#endregion
//#region src/node-host/invoke-system-run.ts
/** Policy and execution pipeline for approved node-host system.run requests. */
const safeBinTrustedDirWarningCache = /* @__PURE__ */ new Set();
const APPROVAL_CWD_DRIFT_DENIED_MESSAGE = "SYSTEM_RUN_DENIED: approval cwd changed before execution";
const APPROVAL_SCRIPT_OPERAND_BINDING_DENIED_MESSAGE = "SYSTEM_RUN_DENIED: approval missing script operand binding";
const APPROVAL_SCRIPT_OPERAND_DRIFT_DENIED_MESSAGE = "SYSTEM_RUN_DENIED: approval script operand changed before execution";
function hasLegacyExecPolicyOverride(exec) {
	return exec?.security !== void 0 || exec?.ask !== void 0;
}
function applyExecPolicyLayer(base, layer) {
	if (!layer) return base;
	if (layer.mode) return {
		mode: layer.mode,
		...resolveExecPolicyForMode(layer.mode)
	};
	if (hasLegacyExecPolicyOverride(layer)) return {
		security: layer.security ?? base.security,
		ask: layer.ask ?? base.ask
	};
	return base;
}
function warnWritableTrustedDirOnce(message) {
	if (safeBinTrustedDirWarningCache.has(message)) return;
	safeBinTrustedDirWarningCache.add(message);
	logWarn(message);
}
function normalizeDeniedReason(reason) {
	switch (reason) {
		case "security=deny":
		case "approval-required":
		case "allowlist-miss":
		case "execution-plan-miss":
		case "companion-unavailable":
		case "permission:screenRecording": return reason;
		default: return "approval-required";
	}
}
function resolveAgentExecConfig(cfg, agentId) {
	if (!agentId) return;
	const normalizedAgentId = normalizeAgentId(agentId);
	return (cfg.agents?.list?.find((candidate) => candidate !== null && typeof candidate === "object" && normalizeAgentId(candidate.id) === normalizedAgentId))?.tools?.exec;
}
/** Resolves the effective exec security/ask policy for one system.run request. */
function resolveEffectiveSystemRunExecPolicy(params) {
	const agentExec = resolveAgentExecConfig(params.cfg, params.agentId);
	const globalExec = params.cfg.tools?.exec;
	const layeredPolicy = applyExecPolicyLayer(applyExecPolicyLayer({
		security: params.defaultSecurity,
		ask: params.defaultAsk
	}, globalExec), agentExec);
	const modePolicy = resolveExecModePolicy({
		mode: layeredPolicy.mode,
		security: layeredPolicy.security,
		ask: layeredPolicy.ask
	});
	const approvals = resolveExecApprovals(params.agentId, {
		security: modePolicy.security,
		ask: modePolicy.ask,
		requireSocket: params.requireSocket
	});
	return {
		agentExec,
		globalExec,
		approvals,
		security: minSecurity(modePolicy.security, approvals.agent.security),
		ask: maxAsk(modePolicy.ask, approvals.agent.ask),
		autoReview: modePolicy.autoReview
	};
}
async function resolveSystemRunAutoReviewer(params) {
	if (params.opts.autoReviewer) return params.opts.autoReviewer;
	const { createModelExecAutoReviewer } = await import("./exec-auto-reviewer-Bs5cW_kH.js");
	return createModelExecAutoReviewer({
		cfg: params.cfg,
		agentId: params.agentId,
		reviewer: params.agentExec?.reviewer ?? params.globalExec?.reviewer
	});
}
async function loadSystemRunConfig(opts) {
	if (opts.getRuntimeConfig) return opts.getRuntimeConfig();
	const { getRuntimeConfig } = await import("./config/config.js");
	return getRuntimeConfig();
}
async function sendSystemRunDenied(opts, execution, params) {
	await opts.sendNodeEvent(opts.client, "exec.denied", opts.buildExecEventPayload({
		sessionKey: execution.sessionKey,
		runId: execution.runId,
		host: "node",
		command: execution.commandText,
		reason: params.reason,
		suppressNotifyOnExit: execution.suppressNotifyOnExit
	}));
	await opts.sendInvokeResult({
		ok: false,
		error: {
			code: "UNAVAILABLE",
			message: params.message
		}
	});
}
async function sendSystemRunCompleted(opts, execution, result, payloadJSON) {
	await opts.sendExecFinishedEvent({
		sessionKey: execution.sessionKey,
		runId: execution.runId,
		commandText: execution.commandText,
		result,
		suppressNotifyOnExit: execution.suppressNotifyOnExit
	});
	await opts.sendInvokeResult({
		ok: true,
		payloadJSON
	});
}
function argvArraysMatch(left, right) {
	return left !== void 0 && left.length === right.length && left.every((entry, index) => entry === right[index]);
}
async function parseSystemRunPhase(opts) {
	const command = resolveSystemRunCommandRequest({
		command: opts.params.command,
		rawCommand: opts.params.rawCommand
	});
	if (!command.ok) {
		await opts.sendInvokeResult({
			ok: false,
			error: {
				code: "INVALID_REQUEST",
				message: command.message
			}
		});
		return null;
	}
	if (command.argv.length === 0) {
		await opts.sendInvokeResult({
			ok: false,
			error: {
				code: "INVALID_REQUEST",
				message: "command required"
			}
		});
		return null;
	}
	const shellPayload = command.shellPayload;
	const shellWrapperInvocation = isShellWrapperInvocation(command.argv);
	const commandText = command.commandText;
	const approvalPlan = opts.params.systemRunPlan === void 0 ? null : normalizeSystemRunApprovalPlan(opts.params.systemRunPlan);
	if (opts.params.systemRunPlan !== void 0 && !approvalPlan) {
		await opts.sendInvokeResult({
			ok: false,
			error: {
				code: "INVALID_REQUEST",
				message: "systemRunPlan invalid"
			}
		});
		return null;
	}
	const agentId = normalizeOptionalString(opts.params.agentId);
	const sessionKey = normalizeOptionalString(opts.params.sessionKey) ?? "node";
	const runId = normalizeOptionalString(opts.params.runId) ?? crypto.randomUUID();
	const suppressNotifyOnExit = opts.params.suppressNotifyOnExit === true;
	const envAssignmentKeys = extractEnvAssignmentKeysFromDispatchWrappers(command.argv);
	const envAssignmentDiagnostics = inspectHostExecEnvOverrides({
		overrides: envAssignmentKeys.length > 0 ? Object.fromEntries(envAssignmentKeys.map((key) => [key, "1"])) : void 0,
		blockPathOverrides: true
	});
	if (envAssignmentDiagnostics.rejectedOverrideBlockedKeys.length > 0) {
		await opts.sendInvokeResult({
			ok: false,
			error: {
				code: "INVALID_REQUEST",
				message: `SYSTEM_RUN_DENIED: command env assignment rejected (blocked env assignment keys: ${envAssignmentDiagnostics.rejectedOverrideBlockedKeys.join(", ")})`
			}
		});
		return null;
	}
	const envOverrideDiagnostics = inspectHostExecEnvOverrides({
		overrides: opts.params.env ?? void 0,
		blockPathOverrides: true
	});
	if (envOverrideDiagnostics.rejectedOverrideBlockedKeys.length > 0 || envOverrideDiagnostics.rejectedOverrideInvalidKeys.length > 0) {
		const details = [];
		if (envOverrideDiagnostics.rejectedOverrideBlockedKeys.length > 0) details.push(`blocked override keys: ${envOverrideDiagnostics.rejectedOverrideBlockedKeys.join(", ")}`);
		if (envOverrideDiagnostics.rejectedOverrideInvalidKeys.length > 0) details.push(`invalid non-portable override keys: ${envOverrideDiagnostics.rejectedOverrideInvalidKeys.join(", ")}`);
		await opts.sendInvokeResult({
			ok: false,
			error: {
				code: "INVALID_REQUEST",
				message: `SYSTEM_RUN_DENIED: environment override rejected (${details.join("; ")})`
			}
		});
		return null;
	}
	const envOverrides = sanitizeSystemRunEnvOverrides({
		overrides: opts.params.env ?? void 0,
		shellWrapper: shellWrapperInvocation
	});
	return {
		argv: command.argv,
		shellPayload,
		shellWrapperInvocation,
		commandText,
		commandPreview: command.previewText,
		approvalPlan,
		agentId,
		sessionKey,
		runId,
		execution: {
			sessionKey,
			runId,
			commandText,
			suppressNotifyOnExit
		},
		approvalDecision: resolveExecApprovalDecision(opts.params.approvalDecision),
		envOverrides,
		env: opts.sanitizeEnv(envOverrides),
		cwd: normalizeOptionalString(opts.params.cwd),
		timeoutMs: opts.params.timeoutMs ?? void 0,
		needsScreenRecording: opts.params.needsScreenRecording === true,
		approved: opts.params.approved === true,
		suppressNotifyOnExit
	};
}
async function evaluateSystemRunPolicyPhase(opts, parsed) {
	const cfg = await loadSystemRunConfig(opts);
	const effectivePolicy = resolveEffectiveSystemRunExecPolicy({
		cfg,
		agentId: parsed.agentId,
		defaultSecurity: opts.resolveExecSecurity(void 0),
		defaultAsk: opts.resolveExecAsk(void 0),
		requireSocket: opts.preferMacAppExecHost
	});
	const { agentExec, globalExec, approvals, security, ask } = effectivePolicy;
	const autoAllowSkills = approvals.agent.autoAllowSkills;
	const { safeBins, safeBinProfiles, trustedSafeBinDirs } = resolveExecSafeBinRuntimePolicy({
		global: cfg.tools?.exec,
		local: agentExec,
		onWarning: warnWritableTrustedDirOnce
	});
	const bins = autoAllowSkills ? await opts.skillBins.current() : [];
	const allowlistEvaluation = evaluateSystemRunAllowlist({
		shellCommand: parsed.shellPayload,
		argv: parsed.argv,
		approvals,
		security,
		safeBins,
		safeBinProfiles,
		trustedSafeBinDirs,
		cwd: parsed.cwd,
		env: parsed.env,
		skillBins: bins,
		autoAllowSkills
	});
	const { allowlistMatches, segments, segmentAllowlistEntries, segmentSatisfiedBy } = allowlistEvaluation;
	let { analysisOk, allowlistSatisfied } = allowlistEvaluation;
	const strictInlineEval = agentExec?.strictInlineEval === true || cfg.tools?.exec?.strictInlineEval === true;
	const inlineEvalHit = strictInlineEval ? detectPolicyInlineEval(segments) : null;
	const isWindows = process.platform === "win32";
	const cmdDetectionArgv = resolveShellWrapperTransportArgv(parsed.argv) ?? parsed.argv;
	const cmdInvocation = opts.isCmdExeInvocation(cmdDetectionArgv);
	const durableApprovalSatisfied = hasDurableExecApproval({
		analysisOk,
		segmentAllowlistEntries,
		allowlist: approvals.allowlist,
		commandText: parsed.commandText
	});
	const inlineEvalExecutableTrusted = inlineEvalHit !== null && segmentAllowlistEntries.some((entry) => entry?.source === "allow-always");
	let approvalDecision = parsed.approvalDecision;
	let policy = evaluateSystemRunPolicy({
		security,
		ask,
		analysisOk,
		allowlistSatisfied,
		durableApprovalSatisfied: durableApprovalSatisfied || inlineEvalExecutableTrusted,
		approvalDecision,
		approved: parsed.approved,
		isWindows,
		cmdInvocation,
		shellWrapperInvocation: parsed.shellPayload !== null
	});
	const requiresSecurityAuditSuppressionApproval = commandRequiresSecurityAuditSuppressionApproval({
		command: parsed.commandText,
		cwd: parsed.cwd,
		env: parsed.env,
		segments
	}) && !(security === "full" && ask === "off");
	if (requiresSecurityAuditSuppressionApproval && !policy.approvedByAsk) policy = {
		allowed: false,
		eventReason: "approval-required",
		errorMessage: "SYSTEM_RUN_DENIED: approval required",
		analysisOk: policy.analysisOk,
		allowlistSatisfied: policy.allowlistSatisfied,
		shellWrapperBlocked: policy.shellWrapperBlocked,
		windowsShellWrapperBlocked: policy.windowsShellWrapperBlocked,
		requiresAsk: true,
		approvalDecision: policy.approvalDecision,
		approvedByAsk: policy.approvedByAsk
	};
	let autoReviewDeferredMessage;
	analysisOk = policy.analysisOk;
	allowlistSatisfied = policy.allowlistSatisfied;
	if (inlineEvalHit !== null && !policy.approvedByAsk && (policy.allowed ? true : policy.eventReason !== "security=deny")) {
		await sendSystemRunDenied(opts, parsed.execution, {
			reason: "approval-required",
			message: `SYSTEM_RUN_DENIED: approval required (${describeInterpreterInlineEval(inlineEvalHit)} requires explicit approval in strictInlineEval mode)`
		});
		return null;
	}
	if (!policy.allowed) {
		const [autoReviewSegment] = segments;
		const directAutoReviewArgvMatchesRequest = parsed.shellPayload !== null || argvArraysMatch(autoReviewSegment?.argv, parsed.argv);
		const autoReviewArgv = segments.length === 1 && directAutoReviewArgvMatchesRequest && (parsed.shellPayload === null || autoReviewSegment?.raw !== void 0 && autoReviewSegment.raw.trim() === parsed.shellPayload.trim()) ? autoReviewSegment?.argv : void 0;
		if (effectivePolicy.autoReview && ask !== "always" && analysisOk && autoReviewArgv !== void 0 && parsed.approvalPlan !== null && inlineEvalHit === null && !requiresSecurityAuditSuppressionApproval && policy.eventReason !== "security=deny") {
			const decision = await (await resolveSystemRunAutoReviewer({
				opts,
				cfg,
				agentId: parsed.agentId,
				agentExec,
				globalExec
			}))({
				command: parsed.commandText,
				argv: autoReviewArgv,
				cwd: parsed.cwd,
				envKeys: Object.keys(parsed.envOverrides ?? {}).toSorted(),
				host: "node",
				reason: policy.eventReason === "allowlist-miss" ? "allowlist-miss" : "approval-required",
				analysis: {
					parsed: analysisOk,
					allowlistMatched: allowlistSatisfied,
					durableApprovalMatched: durableApprovalSatisfied,
					inlineEval: false,
					shellWrapper: parsed.shellWrapperInvocation
				},
				agent: {
					id: parsed.agentId,
					sessionKey: parsed.sessionKey
				}
			});
			if (decision.decision === "allow-once") {
				approvalDecision = "allow-once";
				policy = evaluateSystemRunPolicy({
					security,
					ask,
					analysisOk,
					allowlistSatisfied,
					durableApprovalSatisfied: durableApprovalSatisfied || inlineEvalExecutableTrusted,
					approvalDecision,
					approved: true,
					isWindows,
					cmdInvocation,
					shellWrapperInvocation: parsed.shellPayload !== null
				});
			} else autoReviewDeferredMessage = `${policy.errorMessage} (exec auto-review deferred to human approval: ${decision.rationale})`;
		}
	}
	if (!policy.allowed) {
		await sendSystemRunDenied(opts, parsed.execution, {
			reason: policy.eventReason,
			message: autoReviewDeferredMessage ?? policy.errorMessage
		});
		return null;
	}
	if (policy.shellWrapperBlocked && !policy.approvedByAsk && !durableApprovalSatisfied) {
		await sendSystemRunDenied(opts, parsed.execution, {
			reason: "approval-required",
			message: "SYSTEM_RUN_DENIED: approval required"
		});
		return null;
	}
	const hardenedPaths = hardenApprovedExecutionPaths({
		approvedByAsk: policy.approvedByAsk,
		argv: parsed.argv,
		shellCommand: parsed.shellPayload,
		cwd: parsed.cwd
	});
	if (!hardenedPaths.ok) {
		await sendSystemRunDenied(opts, parsed.execution, {
			reason: "approval-required",
			message: hardenedPaths.message
		});
		return null;
	}
	const approvedCwdSnapshot = policy.approvedByAsk ? hardenedPaths.approvedCwdSnapshot : void 0;
	if (policy.approvedByAsk && hardenedPaths.cwd && !approvedCwdSnapshot) {
		await sendSystemRunDenied(opts, parsed.execution, {
			reason: "approval-required",
			message: APPROVAL_CWD_DRIFT_DENIED_MESSAGE
		});
		return null;
	}
	const plannedAllowlistArgv = resolvePlannedAllowlistArgv({
		security,
		shellCommand: parsed.shellPayload,
		policy,
		segments
	});
	if (plannedAllowlistArgv === null) {
		await sendSystemRunDenied(opts, parsed.execution, {
			reason: "execution-plan-miss",
			message: "SYSTEM_RUN_DENIED: execution plan mismatch"
		});
		return null;
	}
	return {
		...parsed,
		approvalDecision,
		argv: hardenedPaths.argv,
		cwd: hardenedPaths.cwd,
		approvals,
		security,
		ask,
		policy,
		durableApprovalSatisfied,
		strictInlineEval,
		inlineEvalHit,
		allowlistMatches,
		analysisOk,
		allowlistSatisfied,
		safeBins,
		safeBinProfiles,
		trustedSafeBinDirs,
		skillBins: bins,
		autoAllowSkills,
		segments,
		segmentSatisfiedBy,
		plannedAllowlistArgv: plannedAllowlistArgv ?? void 0,
		isWindows,
		approvedCwdSnapshot
	};
}
async function executeSystemRunPhase(opts, phase) {
	if (phase.approvedCwdSnapshot && !revalidateApprovedCwdSnapshot({ snapshot: phase.approvedCwdSnapshot })) {
		logWarn(`security: system.run approval cwd drift blocked (runId=${phase.runId})`);
		await sendSystemRunDenied(opts, phase.execution, {
			reason: "approval-required",
			message: APPROVAL_CWD_DRIFT_DENIED_MESSAGE
		});
		return;
	}
	const expectedMutableFileOperand = phase.approvalPlan ? resolveMutableFileOperandSnapshotSync({
		argv: phase.argv,
		cwd: phase.cwd,
		shellCommand: phase.shellPayload
	}) : null;
	if (expectedMutableFileOperand && !expectedMutableFileOperand.ok) {
		logWarn(`security: system.run approval script binding blocked (runId=${phase.runId})`);
		await sendSystemRunDenied(opts, phase.execution, {
			reason: "approval-required",
			message: expectedMutableFileOperand.message
		});
		return;
	}
	if (expectedMutableFileOperand?.snapshot && !phase.approvalPlan?.mutableFileOperand) {
		logWarn(`security: system.run approval script binding missing (runId=${phase.runId})`);
		await sendSystemRunDenied(opts, phase.execution, {
			reason: "approval-required",
			message: APPROVAL_SCRIPT_OPERAND_BINDING_DENIED_MESSAGE
		});
		return;
	}
	if (phase.approvalPlan?.mutableFileOperand && !revalidateApprovedMutableFileOperand({
		snapshot: phase.approvalPlan.mutableFileOperand,
		argv: phase.argv,
		cwd: phase.cwd
	})) {
		logWarn(`security: system.run approval script drift blocked (runId=${phase.runId})`);
		await sendSystemRunDenied(opts, phase.execution, {
			reason: "approval-required",
			message: APPROVAL_SCRIPT_OPERAND_DRIFT_DENIED_MESSAGE
		});
		return;
	}
	const execArgv = resolveSystemRunExecArgv({
		plannedAllowlistArgv: phase.plannedAllowlistArgv,
		argv: phase.argv,
		security: phase.security,
		approvals: phase.approvals,
		safeBins: phase.safeBins,
		safeBinProfiles: phase.safeBinProfiles,
		trustedSafeBinDirs: phase.trustedSafeBinDirs,
		skillBins: phase.skillBins,
		autoAllowSkills: phase.autoAllowSkills,
		isWindows: phase.isWindows,
		policy: phase.policy,
		shellCommand: phase.shellPayload,
		segments: phase.segments,
		segmentSatisfiedBy: phase.segmentSatisfiedBy,
		cwd: phase.cwd,
		env: phase.env
	});
	if (!execArgv) {
		await sendSystemRunDenied(opts, phase.execution, {
			reason: "execution-plan-miss",
			message: "SYSTEM_RUN_DENIED: execution plan mismatch"
		});
		return;
	}
	if (opts.preferMacAppExecHost) {
		const execRequest = {
			command: execArgv,
			rawCommand: execArgv === phase.argv ? phase.commandText || null : formatExecCommand(execArgv),
			cwd: phase.cwd ?? null,
			env: phase.envOverrides ?? null,
			timeoutMs: phase.timeoutMs ?? null,
			needsScreenRecording: phase.needsScreenRecording,
			agentId: phase.agentId ?? null,
			sessionKey: phase.sessionKey ?? null,
			approvalDecision: phase.approvalDecision
		};
		const response = await opts.runViaMacAppExecHost({
			approvals: phase.approvals,
			request: execRequest
		});
		if (!response) {
			if (opts.execHostEnforced || !opts.execHostFallbackAllowed) {
				await sendSystemRunDenied(opts, phase.execution, {
					reason: "companion-unavailable",
					message: "COMPANION_APP_UNAVAILABLE: macOS app exec host unreachable"
				});
				return;
			}
		} else if (!response.ok) {
			await sendSystemRunDenied(opts, phase.execution, {
				reason: normalizeDeniedReason(response.error.reason),
				message: response.error.message
			});
			return;
		} else {
			const result = response.payload;
			await sendSystemRunCompleted(opts, phase.execution, result, JSON.stringify(result));
			return;
		}
	}
	if (phase.policy.approvalDecision === "allow-always" && phase.inlineEvalHit === null) {
		if ((phase.policy.analysisOk ? persistAllowAlwaysPatterns({
			approvals: phase.approvals.file,
			agentId: phase.agentId,
			segments: phase.segments,
			cwd: phase.cwd,
			env: phase.env,
			platform: process.platform,
			commandText: phase.commandText,
			strictInlineEval: phase.strictInlineEval
		}) : []).length === 0) addDurableCommandApproval(phase.approvals.file, phase.agentId, phase.commandText);
	}
	recordAllowlistMatchesUse({
		approvals: phase.approvals.file,
		agentId: phase.agentId,
		matches: phase.allowlistMatches,
		command: phase.commandText,
		resolvedPath: resolveApprovalAuditTrustPath(phase.segments[0]?.resolution ?? null, phase.cwd)
	});
	if (phase.needsScreenRecording) {
		await sendSystemRunDenied(opts, phase.execution, {
			reason: "permission:screenRecording",
			message: "PERMISSION_MISSING: screenRecording"
		});
		return;
	}
	const result = await opts.runCommand(execArgv, phase.cwd, phase.env, phase.timeoutMs);
	applyOutputTruncation(result);
	await sendSystemRunCompleted(opts, phase.execution, result, JSON.stringify({
		exitCode: result.exitCode,
		timedOut: result.timedOut,
		success: result.success,
		stdout: result.stdout,
		stderr: result.stderr,
		error: result.error ?? null
	}));
}
/** Executes a validated system.run request, emitting lifecycle events and approvals. */
async function handleSystemRunInvoke(opts) {
	const parsed = await parseSystemRunPhase(opts);
	if (!parsed) return;
	const policyPhase = await evaluateSystemRunPolicyPhase(opts, parsed);
	if (!policyPhase) return;
	await executeSystemRunPhase(opts, policyPhase);
}
//#endregion
//#region src/node-host/plugin-node-host.ts
/**
* Plugin node-host command registry bridge.
*
* Node hosts load the active plugin registry, expose registered capabilities
* and commands, and dispatch incoming node-host commands by exact command id.
*/
let pluginRegistryLoaderModulePromise;
async function loadPluginRegistryLoaderModule() {
	pluginRegistryLoaderModulePromise ??= import("./runtime-registry-loader-PUy5EEon.js");
	return await pluginRegistryLoaderModulePromise;
}
/** Ensure plugin registry data is loaded before node-host command dispatch. */
async function ensureNodeHostPluginRegistry(params) {
	(await loadPluginRegistryLoaderModule()).ensurePluginRegistryLoaded({
		scope: "all",
		config: params.config,
		activationSourceConfig: params.config,
		env: params.env
	});
}
/** List registered node-host capabilities and command ids in deterministic order. */
function listRegisteredNodeHostCapsAndCommands() {
	const registry = getActivePluginRegistry();
	const caps = /* @__PURE__ */ new Set();
	const commands = /* @__PURE__ */ new Set();
	for (const entry of registry?.nodeHostCommands ?? []) {
		if (entry.command.cap) caps.add(entry.command.cap);
		commands.add(entry.command.command);
	}
	return {
		caps: [...caps].toSorted((left, right) => left.localeCompare(right)),
		commands: [...commands].toSorted((left, right) => left.localeCompare(right))
	};
}
/** Invoke a registered node-host plugin command, or return null for unknown commands. */
async function invokeRegisteredNodeHostCommand(command, paramsJSON) {
	const match = (getActivePluginRegistry()?.nodeHostCommands ?? []).find((entry) => entry.command.command === command);
	if (!match) return null;
	return await match.command.handle(paramsJSON);
}
//#endregion
//#region src/node-host/invoke.ts
/** Node-host command dispatcher for system commands, approvals, env policy, and plugin commands. */
const OUTPUT_CAP = 2e5;
const OUTPUT_EVENT_TAIL = 2e4;
const DEFAULT_NODE_PATH$1 = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
const execHostEnforced = normalizeLowercaseStringOrEmpty(process.env.OPENCLAW_NODE_EXEC_HOST ?? "") === "app";
const execHostFallbackAllowed = normalizeLowercaseStringOrEmpty(process.env.OPENCLAW_NODE_EXEC_FALLBACK ?? "") !== "0";
const preferMacAppExecHost = process.platform === "darwin" && execHostEnforced;
function buildEnvOverrideRejectionMessage(params) {
	const details = [];
	if (params.rejectedOverrideBlockedKeys.length > 0) details.push(`blocked override keys: ${params.rejectedOverrideBlockedKeys.join(", ")}`);
	if (params.rejectedOverrideInvalidKeys.length > 0) details.push(`invalid non-portable override keys: ${params.rejectedOverrideInvalidKeys.join(", ")}`);
	return `SYSTEM_RUN_DENIED: environment override rejected (${details.join("; ")})`;
}
function buildSystemRunPrepareCoverageEnv(params) {
	const diagnostics = inspectHostExecEnvOverrides({
		overrides: params.env ?? void 0,
		blockPathOverrides: true
	});
	if (diagnostics.rejectedOverrideBlockedKeys.length > 0 || diagnostics.rejectedOverrideInvalidKeys.length > 0) return {
		ok: false,
		message: buildEnvOverrideRejectionMessage(diagnostics)
	};
	return {
		ok: true,
		env: sanitizeEnv(sanitizeSystemRunEnvOverrides({
			overrides: params.env ?? void 0,
			shellWrapper: isShellWrapperInvocation(params.argv)
		}))
	};
}
function buildSystemRunAllowAlwaysCoverage(params) {
	const cwd = params.cwd ?? void 0;
	const shellWrapper = extractShellWrapperCommand(params.argv, params.rawCommand);
	if (shellWrapper.isWrapper) {
		if (!shellWrapper.command) return {
			complete: false,
			patterns: []
		};
		const analysis = analyzeShellCommand({
			command: shellWrapper.command,
			cwd,
			env: params.env,
			platform: process.platform
		});
		if (!analysis.ok) return {
			complete: false,
			patterns: []
		};
		return resolveAllowAlwaysPatternCoverage({
			segments: analysis.segments,
			cwd,
			env: params.env,
			platform: process.platform,
			strictInlineEval: params.strictInlineEval
		});
	}
	const analysis = analyzeArgvCommand({
		argv: params.argv,
		cwd,
		env: params.env
	});
	if (!analysis.ok) return {
		complete: false,
		patterns: []
	};
	return resolveAllowAlwaysPatternCoverage({
		segments: analysis.segments,
		cwd,
		env: params.env,
		platform: process.platform,
		strictInlineEval: params.strictInlineEval
	});
}
function resolveExecSecurity(value) {
	return value === "deny" || value === "allowlist" || value === "full" ? value : "allowlist";
}
function isCmdExeInvocation(argv) {
	const token = argv[0]?.trim();
	if (!token) return false;
	const base = normalizeLowercaseStringOrEmpty(path.win32.basename(token));
	return base === "cmd.exe" || base === "cmd";
}
function resolveExecAsk(value) {
	return value === "off" || value === "on-miss" || value === "always" ? value : "on-miss";
}
/** Builds a sanitized execution environment with controlled PATH and approved overrides. */
function sanitizeEnv(overrides) {
	return sanitizeHostExecEnv({
		overrides,
		blockPathOverrides: true
	});
}
function truncateOutput(raw, maxChars) {
	if (raw.length <= maxChars) return {
		text: raw,
		truncated: false
	};
	return {
		text: `... (truncated) ${raw.slice(raw.length - maxChars)}`,
		truncated: true
	};
}
function decodeCapturedOutputBuffer(params) {
	return decodeWindowsOutputBuffer(params);
}
function redactExecApprovals(file) {
	const socketPath = file.socket?.path?.trim();
	return {
		...file,
		socket: socketPath ? { path: socketPath } : void 0
	};
}
function requireExecApprovalsBaseHash(params, snapshot) {
	if (!snapshot.exists) return;
	if (!snapshot.hash) throw new Error("INVALID_REQUEST: exec approvals base hash unavailable; reload and retry");
	const baseHash = typeof params.baseHash === "string" ? params.baseHash.trim() : "";
	if (!baseHash) throw new Error("INVALID_REQUEST: exec approvals base hash required; reload and retry");
	if (baseHash !== snapshot.hash) throw new Error("INVALID_REQUEST: exec approvals changed; reload and retry");
}
async function runCommand(argv, cwd, env, timeoutMs) {
	return await new Promise((resolve) => {
		const stdoutChunks = [];
		const stderrChunks = [];
		let outputLen = 0;
		let truncated = false;
		let timedOut = false;
		let settled = false;
		const windowsEncoding = resolveWindowsConsoleEncoding();
		const child = spawn(argv[0], argv.slice(1), {
			cwd,
			env,
			stdio: [
				"ignore",
				"pipe",
				"pipe"
			],
			windowsHide: true
		});
		const onChunk = (chunk, target) => {
			if (outputLen >= OUTPUT_CAP) {
				truncated = true;
				return;
			}
			const remaining = OUTPUT_CAP - outputLen;
			const slice = chunk.length > remaining ? chunk.subarray(0, remaining) : chunk;
			outputLen += slice.length;
			if (target === "stdout") stdoutChunks.push(slice);
			else stderrChunks.push(slice);
			if (chunk.length > remaining) truncated = true;
		};
		child.stdout?.on("data", (chunk) => onChunk(chunk, "stdout"));
		child.stderr?.on("data", (chunk) => onChunk(chunk, "stderr"));
		let timer;
		if (timeoutMs && timeoutMs > 0) timer = setTimeout(() => {
			timedOut = true;
			try {
				child.kill("SIGKILL");
			} catch {}
		}, timeoutMs);
		const finalize = (exitCode, error) => {
			if (settled) return;
			settled = true;
			if (timer) clearTimeout(timer);
			const stdout = decodeCapturedOutputBuffer({
				buffer: Buffer.concat(stdoutChunks),
				windowsEncoding
			});
			const stderr = decodeCapturedOutputBuffer({
				buffer: Buffer.concat(stderrChunks),
				windowsEncoding
			});
			resolve({
				exitCode,
				timedOut,
				success: exitCode === 0 && !timedOut && !error,
				stdout,
				stderr,
				error: error ?? null,
				truncated
			});
		};
		child.on("error", (err) => {
			finalize(void 0, err.message);
		});
		child.on("exit", (code) => {
			finalize(code === null ? void 0 : code, null);
		});
	});
}
function resolveEnvPath(env) {
	return (env?.PATH ?? env?.Path ?? process.env.PATH ?? process.env.Path ?? DEFAULT_NODE_PATH$1).split(path.delimiter).filter(Boolean);
}
function resolveExecutable(bin, env) {
	if (bin.includes("/") || bin.includes("\\")) return null;
	const extensions = process.platform === "win32" ? (process.env.PATHEXT ?? process.env.PathExt ?? ".EXE;.CMD;.BAT;.COM").split(";").map((ext) => normalizeLowercaseStringOrEmpty(ext)) : [""];
	for (const dir of resolveEnvPath(env)) for (const ext of extensions) {
		const candidate = path.join(dir, bin + ext);
		if (fs.existsSync(candidate)) return candidate;
	}
	return null;
}
async function handleSystemWhich(params, env) {
	const bins = normalizeStringEntries(params.bins);
	const found = {};
	for (const bin of bins) {
		const pathLocal = resolveExecutable(bin, env);
		if (pathLocal) found[bin] = pathLocal;
	}
	return { bins: found };
}
function buildExecEventPayload(payload) {
	if (!payload.output) return payload;
	const trimmed = payload.output.trim();
	if (!trimmed) return payload;
	const { text } = truncateOutput(trimmed, OUTPUT_EVENT_TAIL);
	return {
		...payload,
		output: text
	};
}
async function sendExecFinishedEvent(params) {
	const combined = [
		params.result.stdout,
		params.result.stderr,
		params.result.error
	].filter(Boolean).join("\n");
	await sendNodeEvent(params.client, "exec.finished", buildExecEventPayload({
		sessionKey: params.sessionKey,
		runId: params.runId,
		host: "node",
		command: params.commandText,
		exitCode: params.result.exitCode ?? void 0,
		timedOut: params.result.timedOut,
		success: params.result.success,
		output: combined,
		suppressNotifyOnExit: params.suppressNotifyOnExit
	}));
}
async function runViaMacAppExecHost(params) {
	const { approvals, request } = params;
	return await requestExecHostViaSocket({
		socketPath: approvals.socketPath,
		token: approvals.token,
		request
	});
}
async function sendJsonPayloadResult(client, frame, payload) {
	await sendInvokeResult(client, frame, {
		ok: true,
		payloadJSON: JSON.stringify(payload)
	});
}
async function sendRawPayloadResult(client, frame, payloadJSON) {
	await sendInvokeResult(client, frame, {
		ok: true,
		payloadJSON
	});
}
async function sendErrorResult(client, frame, code, message) {
	await sendInvokeResult(client, frame, {
		ok: false,
		error: {
			code,
			message
		}
	});
}
async function sendInvalidRequestResult(client, frame, err) {
	await sendErrorResult(client, frame, "INVALID_REQUEST", String(err));
}
/** Handles one node-host command invocation payload and returns serialized results. */
async function handleInvoke(frame, client, skillBins) {
	const command = frame.command ?? "";
	if (command === "system.execApprovals.get") {
		try {
			ensureExecApprovals();
			const snapshot = readExecApprovalsSnapshot();
			await sendJsonPayloadResult(client, frame, {
				path: snapshot.path,
				exists: snapshot.exists,
				hash: snapshot.hash,
				file: redactExecApprovals(snapshot.file)
			});
		} catch (err) {
			const message = String(err);
			await sendErrorResult(client, frame, normalizeLowercaseStringOrEmpty(message).includes("timed out") ? "TIMEOUT" : "INVALID_REQUEST", message);
		}
		return;
	}
	if (command === "system.execApprovals.set") {
		try {
			const params = decodeParams(frame.paramsJSON);
			if (!params.file || typeof params.file !== "object") throw new Error("INVALID_REQUEST: exec approvals file required");
			ensureExecApprovals();
			const snapshot = readExecApprovalsSnapshot();
			requireExecApprovalsBaseHash(params, snapshot);
			saveExecApprovals(mergeExecApprovalsSocketDefaults({
				normalized: normalizeExecApprovals(params.file),
				current: snapshot.file
			}));
			const nextSnapshot = readExecApprovalsSnapshot();
			await sendJsonPayloadResult(client, frame, {
				path: nextSnapshot.path,
				exists: nextSnapshot.exists,
				hash: nextSnapshot.hash,
				file: redactExecApprovals(nextSnapshot.file)
			});
		} catch (err) {
			await sendInvalidRequestResult(client, frame, err);
		}
		return;
	}
	if (command === "system.which") {
		try {
			const params = decodeParams(frame.paramsJSON);
			if (!Array.isArray(params.bins)) throw new Error("INVALID_REQUEST: bins required");
			await sendJsonPayloadResult(client, frame, await handleSystemWhich(params, sanitizeEnv(void 0)));
		} catch (err) {
			await sendInvalidRequestResult(client, frame, err);
		}
		return;
	}
	try {
		const pluginNodeHostResult = await invokeRegisteredNodeHostCommand(command, frame.paramsJSON);
		if (pluginNodeHostResult !== null) {
			await sendRawPayloadResult(client, frame, pluginNodeHostResult);
			return;
		}
	} catch (err) {
		await sendInvalidRequestResult(client, frame, err);
		return;
	}
	if (command === "system.run.prepare") {
		try {
			const params = decodeParams(frame.paramsJSON);
			const prepared = buildSystemRunApprovalPlan(params);
			if (!prepared.ok) {
				await sendErrorResult(client, frame, "INVALID_REQUEST", prepared.message);
				return;
			}
			const prepareEnv = buildSystemRunPrepareCoverageEnv({
				argv: prepared.plan.argv,
				env: params.env ?? void 0
			});
			if (!prepareEnv.ok) {
				await sendErrorResult(client, frame, "INVALID_REQUEST", prepareEnv.message);
				return;
			}
			const { getRuntimeConfig } = await import("./config/config.js");
			const execPolicy = resolveEffectiveSystemRunExecPolicy({
				cfg: getRuntimeConfig(),
				agentId: prepared.plan.agentId ?? void 0,
				defaultSecurity: resolveExecSecurity(void 0),
				defaultAsk: resolveExecAsk(void 0),
				requireSocket: preferMacAppExecHost
			});
			await sendJsonPayloadResult(client, frame, {
				plan: prepared.plan,
				execPolicy: {
					security: execPolicy.security,
					ask: execPolicy.ask
				},
				allowAlwaysCoverage: buildSystemRunAllowAlwaysCoverage({
					argv: prepared.plan.argv,
					rawCommand: typeof params.rawCommand === "string" ? params.rawCommand : null,
					cwd: prepared.plan.cwd,
					env: prepareEnv.env,
					strictInlineEval: params.strictInlineEval === true
				})
			});
		} catch (err) {
			await sendInvalidRequestResult(client, frame, err);
		}
		return;
	}
	if (command !== "system.run") {
		await sendErrorResult(client, frame, "UNAVAILABLE", "command not supported");
		return;
	}
	let params;
	try {
		params = decodeParams(frame.paramsJSON);
	} catch (err) {
		await sendInvalidRequestResult(client, frame, err);
		return;
	}
	if (!Array.isArray(params.command) || params.command.length === 0) {
		await sendErrorResult(client, frame, "INVALID_REQUEST", "command required");
		return;
	}
	await handleSystemRunInvoke({
		client,
		params,
		skillBins,
		execHostEnforced,
		execHostFallbackAllowed,
		resolveExecSecurity,
		resolveExecAsk,
		isCmdExeInvocation,
		sanitizeEnv,
		runCommand,
		runViaMacAppExecHost,
		sendNodeEvent,
		buildExecEventPayload,
		sendInvokeResult: async (result) => {
			await sendInvokeResult(client, frame, result);
		},
		sendExecFinishedEvent: async ({ sessionKey, runId, commandText, result }) => {
			await sendExecFinishedEvent({
				client,
				sessionKey,
				runId,
				commandText,
				result
			});
		},
		preferMacAppExecHost
	});
}
function decodeParams(raw) {
	if (!raw) throw new Error("INVALID_REQUEST: paramsJSON required");
	try {
		return JSON.parse(raw);
	} catch {
		throw new Error("INVALID_REQUEST: paramsJSON malformed JSON");
	}
}
function coerceNodeInvokePayload(payload) {
	if (!payload || typeof payload !== "object") return null;
	const obj = payload;
	const id = typeof obj.id === "string" ? obj.id.trim() : "";
	const nodeId = typeof obj.nodeId === "string" ? obj.nodeId.trim() : "";
	const command = typeof obj.command === "string" ? obj.command.trim() : "";
	if (!id || !nodeId || !command) return null;
	return {
		id,
		nodeId,
		command,
		paramsJSON: typeof obj.paramsJSON === "string" ? obj.paramsJSON : obj.params !== void 0 ? JSON.stringify(obj.params) : null,
		timeoutMs: typeof obj.timeoutMs === "number" ? obj.timeoutMs : null,
		idempotencyKey: typeof obj.idempotencyKey === "string" ? obj.idempotencyKey : null
	};
}
async function sendInvokeResult(client, frame, result) {
	try {
		await client.request("node.invoke.result", buildNodeInvokeResultParams(frame, result));
	} catch {}
}
function buildNodeInvokeResultParams(frame, result) {
	const params = {
		id: frame.id,
		nodeId: frame.nodeId,
		ok: result.ok
	};
	if (result.payload !== void 0) params.payload = result.payload;
	if (typeof result.payloadJSON === "string") params.payloadJSON = result.payloadJSON;
	if (result.error) params.error = result.error;
	return params;
}
function buildNodeEventParams(event, payload) {
	const payloadJSON = payload === void 0 ? void 0 : JSON.stringify(payload);
	return {
		event,
		payloadJSON: typeof payloadJSON === "string" ? payloadJSON : null
	};
}
async function sendNodeEvent(client, event, payload) {
	try {
		await client.request("node.event", buildNodeEventParams(event, payload));
	} catch {}
}
//#endregion
//#region src/node-host/runner.ts
/** CLI runner for node-host stdin/stdout command dispatch. */
const DEFAULT_NODE_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
function resolveNodeHostGatewayPlatform(platform) {
	switch (platform) {
		case "darwin": return "macos";
		case "win32": return "windows";
		case "linux": return "linux";
		default: return "unknown";
	}
}
function resolveNodeHostGatewayDeviceFamily(platform) {
	switch (platform) {
		case "darwin": return "Mac";
		case "win32": return "Windows";
		case "linux": return "Linux";
		default: return;
	}
}
function writeStderrLine(message) {
	process.stderr.write(`${message}\n`);
}
const NODE_HOST_EXIT_ON_RECONNECT_PAUSE_CODES = new Set([
	ConnectErrorDetailCodes.AUTH_TOKEN_MISSING,
	ConnectErrorDetailCodes.AUTH_TOKEN_MISMATCH,
	ConnectErrorDetailCodes.AUTH_BOOTSTRAP_TOKEN_INVALID,
	ConnectErrorDetailCodes.AUTH_PASSWORD_MISSING,
	ConnectErrorDetailCodes.AUTH_PASSWORD_MISMATCH,
	ConnectErrorDetailCodes.CLIENT_VERSION_MISMATCH
]);
function shouldExitNodeHostOnReconnectPaused(detailCode) {
	return detailCode !== null && NODE_HOST_EXIT_ON_RECONNECT_PAUSE_CODES.has(detailCode);
}
function formatNodeHostReconnectPausedMessage(info, params) {
	const detail = info.detailCode ? ` detail=${info.detailCode}` : "";
	const reason = info.reason.trim() || "no close reason";
	const action = params?.exiting ? "exiting for supervisor restart" : "waiting for operator action";
	return `node host gateway reconnect paused after close (${info.code}): ${reason}${detail}; ${action}`;
}
function handleNodeHostReconnectPaused(info, deps = {}) {
	const shouldExit = shouldExitNodeHostOnReconnectPaused(info.detailCode);
	(deps.writeLine ?? writeStderrLine)(formatNodeHostReconnectPausedMessage(info, { exiting: shouldExit }));
	if (!shouldExit) return;
	(deps.exit ?? ((code) => process.exit(code)))(1);
}
function resolveExecutablePathFromEnv(bin, pathEnv) {
	if (bin.includes("/") || bin.includes("\\")) return null;
	return resolveExecutableFromPathEnv(bin, pathEnv) ?? null;
}
function resolveExecutableTrustPathFromEnv(bin, pathEnv) {
	const resolvedPath = resolveExecutablePathFromEnv(bin, pathEnv);
	if (!resolvedPath) return null;
	try {
		return fs.realpathSync(resolvedPath);
	} catch {
		return resolvedPath;
	}
}
function resolveSkillBinTrustEntries(bins, pathEnv) {
	const trustEntries = [];
	const seen = /* @__PURE__ */ new Set();
	for (const bin of bins) {
		const name = bin.trim();
		if (!name) continue;
		const resolvedPath = resolveExecutableTrustPathFromEnv(name, pathEnv);
		if (!resolvedPath) continue;
		const key = `${name}\u0000${resolvedPath}`;
		if (seen.has(key)) continue;
		seen.add(key);
		trustEntries.push({
			name,
			resolvedPath
		});
	}
	return trustEntries.toSorted((left, right) => left.name.localeCompare(right.name) || left.resolvedPath.localeCompare(right.resolvedPath));
}
var SkillBinsCache = class {
	constructor(fetch, pathEnv) {
		this.bins = [];
		this.lastRefresh = 0;
		this.ttlMs = 9e4;
		this.fetch = fetch;
		this.pathEnv = pathEnv;
	}
	async current(force = false) {
		if (force || Date.now() - this.lastRefresh > this.ttlMs) await this.refresh();
		return this.bins;
	}
	async refresh() {
		try {
			const bins = await this.fetch();
			this.bins = resolveSkillBinTrustEntries(bins, this.pathEnv);
			this.lastRefresh = Date.now();
		} catch {
			if (!this.lastRefresh) this.bins = [];
		}
	}
};
function ensureNodePathEnv() {
	ensureOpenClawCliOnPath({ pathEnv: process.env.PATH ?? "" });
	const current = process.env.PATH ?? "";
	if (current.trim()) return current;
	process.env.PATH = DEFAULT_NODE_PATH;
	return DEFAULT_NODE_PATH;
}
async function resolveNodeHostGatewayCredentials(params) {
	return await resolveGatewayConnectionAuth({
		config: (params.config.gateway?.mode === "remote" ? "remote" : "local") === "local" ? buildNodeHostLocalAuthConfig(params.config) : params.config,
		env: params.env,
		localTokenPrecedence: "env-first",
		localPasswordPrecedence: "env-first",
		remoteTokenPrecedence: "env-first",
		remotePasswordPrecedence: "env-first"
	});
}
function buildNodeHostLocalAuthConfig(config) {
	if (!config.gateway?.remote?.token && !config.gateway?.remote?.password) return config;
	const nextConfig = structuredClone(config);
	if (nextConfig.gateway?.remote) {
		nextConfig.gateway.remote.token = void 0;
		nextConfig.gateway.remote.password = void 0;
	}
	return nextConfig;
}
async function runNodeHost(opts) {
	const config = await ensureNodeHostConfig();
	const nodeId = opts.nodeId?.trim() || config.nodeId;
	if (nodeId !== config.nodeId) config.nodeId = nodeId;
	const displayName = opts.displayName?.trim() || config.displayName || await getMachineDisplayName();
	config.displayName = displayName;
	const gateway = {
		host: opts.gatewayHost,
		port: opts.gatewayPort,
		tls: opts.gatewayTls ?? getRuntimeConfig().gateway?.tls?.enabled ?? false,
		tlsFingerprint: opts.gatewayTlsFingerprint
	};
	config.gateway = gateway;
	await saveNodeHostConfig(config);
	const cfg = getRuntimeConfig();
	await ensureNodeHostPluginRegistry({
		config: cfg,
		env: process.env
	});
	const pluginNodeHost = listRegisteredNodeHostCapsAndCommands();
	const { token, password } = await resolveNodeHostGatewayCredentials({
		config: cfg,
		env: process.env
	});
	const host = gateway.host ?? "127.0.0.1";
	const port = gateway.port ?? 18789;
	const url = `${gateway.tls ? "wss" : "ws"}://${host}:${port}`;
	const pathEnv = ensureNodePathEnv();
	const client = new GatewayClient({
		url,
		token: token || void 0,
		password: password || void 0,
		preauthHandshakeTimeoutMs: cfg.gateway?.handshakeTimeoutMs,
		instanceId: nodeId,
		clientName: GATEWAY_CLIENT_NAMES.NODE_HOST,
		clientDisplayName: displayName,
		clientVersion: VERSION,
		platform: resolveNodeHostGatewayPlatform(process.platform),
		deviceFamily: resolveNodeHostGatewayDeviceFamily(process.platform),
		mode: GATEWAY_CLIENT_MODES.NODE,
		role: "node",
		scopes: [],
		caps: ["system", ...pluginNodeHost.caps],
		commands: [
			...NODE_SYSTEM_RUN_COMMANDS,
			...NODE_EXEC_APPROVALS_COMMANDS,
			...pluginNodeHost.commands
		],
		pathEnv,
		permissions: void 0,
		deviceIdentity: loadOrCreateDeviceIdentity(),
		tlsFingerprint: gateway.tlsFingerprint,
		onEvent: (evt) => {
			if (evt.event !== "node.invoke.request") return;
			const payload = coerceNodeInvokePayload(evt.payload);
			if (!payload) return;
			handleInvoke(payload, client, skillBins);
		},
		onConnectError: (err) => {
			writeStderrLine(`node host gateway connect failed: ${err.message}`);
		},
		onReconnectPaused: (info) => {
			handleNodeHostReconnectPaused(info);
		},
		onClose: (code, reason) => {
			writeStderrLine(`node host gateway closed (${code}): ${reason}`);
		}
	});
	const skillBins = new SkillBinsCache(async () => {
		const res = await client.request("skills.bins", {});
		return Array.isArray(res?.bins) ? res.bins.map((bin) => String(bin)) : [];
	}, pathEnv);
	if (!(await startGatewayClientWhenEventLoopReady(client, { clientOptions: { preauthHandshakeTimeoutMs: cfg.gateway?.handshakeTimeoutMs } })).ready) throw new Error("node host gateway event loop readiness timeout");
	await new Promise(() => {});
}
//#endregion
//#region src/commands/node-daemon-install-helpers.ts
/** Node-based daemon install plan builder for managed gateway services. */
function buildNodeInstallEnvironmentValueSources() {
	return { OPENCLAW_GATEWAY_TOKEN: "file" };
}
/** Builds launch arguments, environment, and metadata for a Node daemon service install. */
async function buildNodeInstallPlan(params) {
	const { devMode, nodePath } = await resolveDaemonInstallRuntimeInputs({
		env: params.env,
		runtime: params.runtime,
		devMode: params.devMode,
		nodePath: params.nodePath
	});
	const { programArguments, workingDirectory } = await resolveNodeProgramArguments({
		host: params.host,
		port: params.port,
		tls: params.tls,
		tlsFingerprint: params.tlsFingerprint,
		nodeId: params.nodeId,
		displayName: params.displayName,
		dev: devMode,
		runtime: params.runtime,
		nodePath
	});
	await emitDaemonInstallRuntimeWarning({
		env: params.env,
		runtime: params.runtime,
		programArguments,
		warn: params.warn,
		title: "Node daemon runtime"
	});
	const environment = buildNodeServiceEnvironment({
		env: params.env,
		extraPathDirs: resolveDaemonNodeBinDir(nodePath)
	});
	const description = formatNodeServiceDescription({ version: environment.OPENCLAW_SERVICE_VERSION });
	return {
		programArguments,
		workingDirectory,
		environment,
		environmentValueSources: buildNodeInstallEnvironmentValueSources(),
		description
	};
}
//#endregion
//#region src/commands/node-daemon-runtime.ts
/** Compatibility exports for the Node daemon runtime selector. */
/** Default Node daemon runtime, currently shared with the gateway daemon runtime. */
const DEFAULT_NODE_DAEMON_RUNTIME = DEFAULT_GATEWAY_DAEMON_RUNTIME;
/** Returns true when a string is a supported Node daemon runtime id. */
function isNodeDaemonRuntime(value) {
	return isGatewayDaemonRuntime(value);
}
//#endregion
//#region src/cli/node-cli/daemon.ts
function renderNodeServiceStartHints() {
	return buildPlatformServiceStartHints({
		installCommand: formatCliCommand("openclaw node install"),
		startCommand: formatCliCommand("openclaw node start"),
		launchAgentPlistPath: `~/Library/LaunchAgents/${resolveNodeLaunchAgentLabel()}.plist`,
		systemdServiceName: resolveNodeSystemdServiceName(),
		windowsTaskName: resolveNodeWindowsTaskName()
	});
}
function buildNodeRuntimeHints(env = process.env) {
	return buildPlatformRuntimeLogHints({
		env,
		systemdServiceName: resolveNodeSystemdServiceName(),
		windowsTaskName: resolveNodeWindowsTaskName()
	});
}
function resolveNodeDefaults(opts, config) {
	const host = normalizeOptionalString(opts.host) || config?.gateway?.host || "127.0.0.1";
	const portOverride = parsePort(opts.port);
	if (opts.port !== void 0 && portOverride === null) return {
		host,
		port: null
	};
	return {
		host,
		port: portOverride ?? config?.gateway?.port ?? 18789
	};
}
async function runNodeDaemonInstall(opts) {
	const { json, stdout, warnings, emit, fail } = createDaemonInstallActionContext(opts.json);
	if (failIfNixDaemonInstallMode(fail)) return;
	const config = await loadNodeHostConfig();
	const { host, port } = resolveNodeDefaults(opts, config);
	if (!Number.isFinite(port ?? NaN) || (port ?? 0) <= 0 || (port ?? 0) > 65535) {
		fail(opts.port !== void 0 ? formatInvalidPortOption("--port") : formatInvalidConfigPort("node.gateway.port"));
		return;
	}
	const runtimeRaw = opts.runtime ? opts.runtime : DEFAULT_NODE_DAEMON_RUNTIME;
	if (!isNodeDaemonRuntime(runtimeRaw)) {
		fail("Invalid --runtime (use \"node\" or \"bun\")");
		return;
	}
	const service = resolveNodeService();
	let loaded;
	try {
		loaded = await service.isLoaded({ env: process.env });
	} catch (err) {
		fail(`Node service check failed: ${String(err)}`);
		return;
	}
	if (loaded && !opts.force) {
		emit({
			ok: true,
			result: "already-installed",
			message: `Node service already ${service.loadedText}.`,
			service: buildDaemonServiceSnapshot(service, loaded),
			warnings: warnings.length ? warnings : void 0
		});
		if (!json) {
			defaultRuntime.log(`Node service already ${service.loadedText}.`);
			defaultRuntime.log(`Reinstall with: ${formatCliCommand("openclaw node install --force")}`);
		}
		return;
	}
	const tlsFingerprint = normalizeOptionalString(opts.tlsFingerprint) || config?.gateway?.tlsFingerprint;
	const tls = Boolean(opts.tls) || Boolean(tlsFingerprint) || Boolean(config?.gateway?.tls);
	const { programArguments, workingDirectory, environment, environmentValueSources, description } = await buildNodeInstallPlan({
		env: process.env,
		host,
		port: port ?? 18789,
		tls,
		tlsFingerprint: tlsFingerprint || void 0,
		nodeId: opts.nodeId,
		displayName: opts.displayName,
		runtime: runtimeRaw,
		warn: (message) => {
			if (json) warnings.push(message);
			else defaultRuntime.log(message);
		}
	});
	await installDaemonServiceAndEmit({
		serviceNoun: "Node",
		service,
		warnings,
		emit,
		fail,
		install: async () => {
			await service.install({
				env: process.env,
				stdout,
				programArguments,
				workingDirectory,
				environment,
				environmentValueSources,
				description
			});
		}
	});
}
async function runNodeDaemonUninstall(opts = {}) {
	return await runServiceUninstall({
		serviceNoun: "Node",
		service: resolveNodeService(),
		opts,
		stopBeforeUninstall: false,
		assertNotLoadedAfterUninstall: false
	});
}
async function runNodeDaemonStart(opts = {}) {
	return await runServiceStart({
		serviceNoun: "Node",
		service: resolveNodeService(),
		renderStartHints: renderNodeServiceStartHints,
		opts
	});
}
async function runNodeDaemonRestart(opts = {}) {
	await runServiceRestart({
		serviceNoun: "Node",
		service: resolveNodeService(),
		renderStartHints: renderNodeServiceStartHints,
		opts
	});
}
async function runNodeDaemonStop(opts = {}) {
	return await runServiceStop({
		serviceNoun: "Node",
		service: resolveNodeService(),
		opts
	});
}
async function runNodeDaemonStatus(opts = {}) {
	const json = Boolean(opts.json);
	const service = resolveNodeService();
	const [loaded, command, runtime] = await Promise.all([
		service.isLoaded({ env: process.env }).catch(() => false),
		service.readCommand(process.env).catch(() => null),
		service.readRuntime(process.env).catch((err) => ({
			status: "unknown",
			detail: String(err)
		}))
	]);
	const payload = { service: {
		...buildDaemonServiceSnapshot(service, loaded),
		command,
		runtime
	} };
	if (json) {
		defaultRuntime.writeJson(payload);
		return;
	}
	const { rich, label, accent, infoText, okText, warnText, errorText } = createCliStatusTextStyles();
	const serviceStatus = loaded ? okText(service.loadedText) : warnText(service.notLoadedText);
	defaultRuntime.log(`${label("Service:")} ${accent(service.label)} (${serviceStatus})`);
	if (command?.programArguments?.length) defaultRuntime.log(`${label("Command:")} ${infoText(command.programArguments.join(" "))}`);
	if (command?.sourcePath) defaultRuntime.log(`${label("Service file:")} ${infoText(command.sourcePath)}`);
	if (command?.workingDirectory) defaultRuntime.log(`${label("Working dir:")} ${infoText(command.workingDirectory)}`);
	const runtimeLine = formatRuntimeStatus(runtime);
	if (runtimeLine) {
		const runtimeColor = resolveRuntimeStatusColor(runtime?.status);
		defaultRuntime.log(`${label("Runtime:")} ${colorize(rich, runtimeColor, runtimeLine)}`);
	}
	if (!loaded) {
		defaultRuntime.log("");
		for (const hint of renderNodeServiceStartHints()) defaultRuntime.log(`${warnText("Start with:")} ${infoText(hint)}`);
		return;
	}
	const baseEnv = {
		...process.env,
		...command?.environment ?? void 0
	};
	const hintEnv = {
		...baseEnv,
		OPENCLAW_LOG_PREFIX: baseEnv.OPENCLAW_LOG_PREFIX ?? "node"
	};
	if (runtime?.missingUnit) {
		defaultRuntime.error(errorText("Service unit not found."));
		for (const hint of buildNodeRuntimeHints(hintEnv)) defaultRuntime.log(errorText(hint));
		return;
	}
	if (runtime?.status === "stopped") {
		defaultRuntime.error(errorText("Service is loaded but not running."));
		for (const hint of buildNodeRuntimeHints(hintEnv)) defaultRuntime.log(errorText(hint));
	}
}
//#endregion
//#region src/cli/node-cli/register.ts
function parsePortOption(value, fallback) {
	if (value === void 0) return fallback;
	return parsePort(value);
}
function registerNodeCli(program) {
	const node = program.command("node").description("Run and manage the headless node host service").addHelpText("after", () => `\n${theme.heading("Examples:")}\n${formatHelpExamples([
		["openclaw node run --host 127.0.0.1 --port 18789", "Run the node host in the foreground."],
		["openclaw node status", "Check node host service status."],
		["openclaw node install", "Install the node host service."],
		["openclaw node start", "Start the installed node host service."],
		["openclaw node restart", "Restart the installed node host service."]
	])}\n\n${theme.muted("Docs:")} ${formatDocsLink("/cli/node", "docs.openclaw.ai/cli/node")}\n`);
	node.command("run").description("Run the headless node host (foreground)").option("--host <host>", "Gateway host").option("--port <port>", "Gateway port").option("--tls", "Use TLS for the gateway connection").option("--tls-fingerprint <sha256>", "Expected TLS certificate fingerprint (sha256)").option("--node-id <id>", "Override node id (clears pairing token)").option("--display-name <name>", "Override node display name").action(async (opts) => {
		const existing = await loadNodeHostConfig();
		const host = normalizeOptionalString(opts.host) || existing?.gateway?.host || "127.0.0.1";
		const port = parsePortOption(opts.port, existing?.gateway?.port ?? 18789);
		if (port === null) {
			defaultRuntime.error(formatInvalidPortOption("--port"));
			defaultRuntime.exit(1);
			return;
		}
		const retargetedGateway = opts.host !== void 0 || opts.port !== void 0;
		const tlsFingerprint = opts.tlsFingerprint ?? (retargetedGateway ? void 0 : existing?.gateway?.tlsFingerprint);
		const inheritedTls = retargetedGateway ? void 0 : existing?.gateway?.tls;
		await runNodeHost({
			gatewayHost: host,
			gatewayPort: port,
			gatewayTls: typeof opts.tls === "boolean" ? opts.tls : Boolean(tlsFingerprint) || inheritedTls,
			gatewayTlsFingerprint: tlsFingerprint,
			nodeId: opts.nodeId,
			displayName: opts.displayName
		});
	});
	node.command("status").description("Show node host status").option("--json", "Output JSON", false).action(async (opts) => {
		await runNodeDaemonStatus(opts);
	});
	node.command("install").description("Install the node host service (launchd/systemd/schtasks)").option("--host <host>", "Gateway host").option("--port <port>", "Gateway port").option("--tls", "Use TLS for the gateway connection", false).option("--tls-fingerprint <sha256>", "Expected TLS certificate fingerprint (sha256)").option("--node-id <id>", "Override node id (clears pairing token)").option("--display-name <name>", "Override node display name").option("--runtime <runtime>", "Service runtime (node|bun). Default: node").option("--force", "Reinstall/overwrite if already installed", false).option("--json", "Output JSON", false).action(async (opts) => {
		await runNodeDaemonInstall(opts);
	});
	node.command("uninstall").description("Uninstall the node host service (launchd/systemd/schtasks)").option("--json", "Output JSON", false).action(async (opts) => {
		await runNodeDaemonUninstall(opts);
	});
	node.command("stop").description("Stop the node host service (launchd/systemd/schtasks)").option("--json", "Output JSON", false).action(async (opts) => {
		await runNodeDaemonStop(opts);
	});
	node.command("start").description("Start the node host service (launchd/systemd/schtasks)").option("--json", "Output JSON", false).action(async (opts) => {
		await runNodeDaemonStart(opts);
	});
	node.command("restart").description("Restart the node host service (launchd/systemd/schtasks)").option("--json", "Output JSON", false).action(async (opts) => {
		await runNodeDaemonRestart(opts);
	});
}
//#endregion
export { registerNodeCli };