import { l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js";
import { S as parseStrictInteger, w as parseStrictPositiveInteger } from "./number-coercion-CLj0HTDM.js";
import "./utils-B2aozS-T.js";
import { t as sleep } from "./sleep-D7nua6TP.js";
import { h as resolveLegacyGatewayLaunchAgentLabels, l as resolveGatewayLaunchAgentLabel } from "./constants-ChqKLfPp.js";
import { n as parseTcpPort, r as parseTcpPortFromArgs } from "./tcp-port-C3gLZtJi.js";
import { t as resolveDaemonHomeDir } from "./paths-CzCbqt0l.js";
import { d as assertValidLaunchAgentLabel, f as resolveLaunchAgentLabel, h as isLaunchctlNotLoaded, i as isSystemLaunchDaemonOwnershipError, m as formatLaunchctlResultDetail, n as formatSystemLaunchDaemonOwnershipSummary, p as execLaunchctl, r as inspectSystemLaunchDaemonOwnership, t as assertNoSystemLaunchDaemonOwnership, u as readLaunchAgentProgramArgumentsFromFile } from "./launchd-system-D7GZ1tBS.js";
import { n as probePortUsage } from "./ports-probe-BdOOPz4d.js";
import { t as resolveGatewayServiceProbeHosts } from "./gateway-service-probe-hosts-B2Q9eVo_.js";
import { a as writeFormattedLines, i as normalizeWindowsPathSeparators, n as parseKeyValueOutput, r as formatLine, t as createGatewayLifecycleMutationReporter } from "./service-mutation-DyzHamq7.js";
import { a as resolveLaunchAgentEnvWrapperPath, c as rewriteLaunchAgentPlistForRestart, i as resolveLaunchAgentEnvFilePath, l as writeLaunchAgentPlist, n as readExistingLaunchAgentPlist, o as resolveLaunchAgentPlistPath, r as readLaunchAgentProgramArguments, s as resolveLaunchAgentPlistPathForLabel, t as publishLaunchAgentPlist } from "./launchd-service-files-IpzKuDfJ.js";
import { r as formatPortDiagnostics } from "./ports-format-FOKK5FaA.js";
import { n as inspectPortUsage } from "./ports-inspect-Dp7h9Jfe.js";
import { t as cleanStaleGatewayProcessesSync } from "./restart-stale-pids-DDxdPFhU.js";
import { n as scheduleDetachedLaunchdRestartHandoff, t as scheduleDetachedLaunchdMaintenancePark } from "./launchd-restart-handoff-CLPavHLl.js";
import path from "node:path";
import fs from "node:fs/promises";
import { randomUUID } from "node:crypto";
import { spawnSync } from "node:child_process";
//#region src/daemon/launchd-current-service.ts
/** Detects whether the current process is running inside a launchd service label. */
/** Checks whether the current process appears to be running under the requested launchd label. */
function isCurrentProcessLaunchdServiceLabel(label, env = process.env, options = {}) {
	const currentLabels = [
		env.LAUNCH_JOB_LABEL,
		env.LAUNCH_JOB_NAME,
		env.XPC_SERVICE_NAME
	].flatMap((value) => {
		const normalized = normalizeOptionalString(value);
		return normalized ? [normalized] : [];
	});
	for (const currentLabel of currentLabels) if (currentLabel === label) return true;
	const configuredLabel = normalizeOptionalString(env.OPENCLAW_LAUNCHD_LABEL);
	if (!configuredLabel || configuredLabel !== label) return false;
	if (normalizeOptionalString(env.OPENCLAW_SERVICE_MARKER) === "openclaw" && Boolean(normalizeOptionalString(env.OPENCLAW_SERVICE_KIND))) return true;
	return options.allowConfiguredLabelFallback !== false && currentLabels.length === 0;
}
//#endregion
//#region src/daemon/launchd-runtime.ts
/** launchctl state parsing, inspection, and bootstrap primitives. */
const LAUNCH_AGENT_BOOTSTRAP_TEARDOWN_TIMEOUT_MS = 30 * 1e3;
const LAUNCH_AGENT_BOOTSTRAP_TEARDOWN_POLL_MS = 500;
async function resolveLaunchAgentGatewayContext(env) {
	const serviceKind = env.OPENCLAW_SERVICE_KIND?.trim();
	if (serviceKind && serviceKind !== "gateway") return {
		port: null,
		probeHosts: []
	};
	const command = await readLaunchAgentProgramArguments(env).catch(() => null);
	const fromArgs = parseTcpPortFromArgs(command?.programArguments);
	if (fromArgs !== null) return {
		port: fromArgs,
		probeHosts: await resolveGatewayServiceProbeHosts({
			env,
			command
		})
	};
	const fromServiceEnv = parseTcpPort(command?.environment?.OPENCLAW_GATEWAY_PORT ?? "");
	if (fromServiceEnv !== null) return {
		port: fromServiceEnv,
		probeHosts: await resolveGatewayServiceProbeHosts({
			env,
			command
		})
	};
	return {
		port: parseTcpPort(env.OPENCLAW_GATEWAY_PORT ?? ""),
		probeHosts: await resolveGatewayServiceProbeHosts({
			env,
			command
		})
	};
}
function resolveLaunchAgentGuiDomain() {
	if (typeof process.getuid !== "function") return "gui/501";
	return `gui/${process.getuid()}`;
}
function throwBootstrapGuiSessionError(params) {
	throw new Error(formatLaunchAgentGuiSessionError(params));
}
function formatLaunchAgentGuiSessionError(params) {
	return [
		`launchctl bootstrap failed: ${params.detail}`,
		`LaunchAgent ${params.actionHint} requires a logged-in macOS GUI session for this user (${params.domain}).`,
		"This usually means you are running from SSH/headless context or as the wrong user (including sudo).",
		`Fix: sign in to the macOS desktop as the target user and rerun \`${params.actionHint}\`.`,
		"For headless VM setups, enable auto-login for the target user so macOS creates the GUI session after boot.",
		"Headless deployments should use a dedicated logged-in user session or a custom LaunchDaemon (not shipped): https://docs.openclaw.ai/gateway"
	].join("\n");
}
async function bootstrapLaunchAgentOrThrow(params) {
	if (!params.skipEnable) {
		if ((await execLaunchctl(["enable", params.serviceTarget])).code === 0) params.onMutation?.("enable");
	}
	const teardownDeadline = Date.now() + LAUNCH_AGENT_BOOTSTRAP_TEARDOWN_TIMEOUT_MS;
	for (;;) {
		const boot = await execLaunchctl([
			"bootstrap",
			params.domain,
			params.plistPath
		]);
		if (boot.code === 0) {
			params.onMutation?.("bootstrap");
			return;
		}
		const detail = (boot.stderr || boot.stdout).trim();
		if (isUnsupportedGuiDomain(detail)) throwBootstrapGuiSessionError({
			detail,
			domain: params.domain,
			actionHint: params.actionHint
		});
		if (boot.termination === "exit" && isLaunchctlOperationAlreadyInProgress(detail)) {
			const state = await probeLaunchAgentState(params.serviceTarget);
			if (state.state === "running" || state.state === "stopped") {
				params.onMutation?.("bootstrap");
				return;
			}
		}
		const remainingMs = teardownDeadline - Date.now();
		if (!params.retryPendingTeardown || !isLaunchctlBootstrapPendingTeardown(boot) || remainingMs <= 0) throw new Error(`launchctl bootstrap failed: ${detail}`);
		await sleep(Math.min(LAUNCH_AGENT_BOOTSTRAP_TEARDOWN_POLL_MS, remainingMs));
	}
}
function parseLaunchctlPrint(output) {
	const entries = parseKeyValueOutput(output, "=");
	const info = {};
	const state = entries.state;
	if (state) info.state = state;
	const pidValue = entries.pid;
	if (pidValue) {
		const pid = parseStrictPositiveInteger(pidValue);
		if (pid !== void 0) info.pid = pid;
	}
	const exitStatusValue = entries["last exit status"];
	if (exitStatusValue) {
		const status = parseStrictInteger(exitStatusValue);
		if (status !== void 0) info.lastExitStatus = status;
	}
	const exitReason = entries["last exit reason"];
	if (exitReason) info.lastExitReason = exitReason;
	return info;
}
function parseLaunchAgentEnabled(output, label) {
	const labelPrefix = `"${label}"`;
	for (const line of output.split("\n")) {
		const entry = line.trim();
		if (!entry.startsWith(labelPrefix)) continue;
		const state = entry.slice(labelPrefix.length).trim();
		if (state === "=> enabled") return true;
		if (state === "=> disabled") return false;
		throw new Error(`launchctl print-disabled returned an unrecognized state for ${label}`);
	}
	return true;
}
async function isLaunchAgentEnabled(args) {
	const domain = resolveLaunchAgentGuiDomain();
	const label = resolveLaunchAgentLabel(args.env);
	const res = await execLaunchctl(["print-disabled", domain]);
	if (res.code !== 0) throw new Error(`launchctl print-disabled failed: ${formatLaunchctlResultDetail(res)}`);
	return parseLaunchAgentEnabled(res.stdout || res.stderr || "", label);
}
async function isLaunchAgentLoaded(args) {
	const probe = await probeLaunchAgentState(`${resolveLaunchAgentGuiDomain()}/${resolveLaunchAgentLabel(args.env)}`, args.timeoutMs);
	if (probe.state === "running" || probe.state === "stopped") return true;
	if (probe.state === "not-loaded") return false;
	throw new Error(`launchctl print failed: ${probe.detail ?? "unknown error"}`);
}
async function launchAgentPlistExists(env) {
	try {
		const plistPath = resolveLaunchAgentPlistPath(env);
		await fs.access(plistPath);
		return true;
	} catch {
		return false;
	}
}
async function readLaunchAgentRuntime(env, opts) {
	const domain = resolveLaunchAgentGuiDomain();
	const label = resolveLaunchAgentLabel(env);
	const [probe, systemOwnership] = await Promise.all([probeLaunchAgentState(`${domain}/${label}`, opts?.timeoutMs), inspectSystemLaunchDaemonOwnership(label, {
		...opts,
		scanInstalledPlists: false
	})]);
	if (systemOwnership.status !== "absent") return {
		status: "unknown",
		detail: formatSystemLaunchDaemonOwnershipSummary(systemOwnership),
		systemLaunchDaemon: {
			status: systemOwnership.status,
			serviceTarget: systemOwnership.serviceTarget,
			...systemOwnership.status === "installed" ? { plistPath: systemOwnership.plistPath } : {}
		}
	};
	if (probe.state === "not-loaded") return await launchAgentPlistExists(env) ? { status: "stopped" } : {
		status: "unknown",
		missingUnit: true
	};
	if (probe.state === "unknown") {
		const plistExists = await launchAgentPlistExists(env);
		const missingGuiSession = plistExists && isUnsupportedGuiDomain(probe.detail ?? "");
		return {
			status: "unknown",
			detail: probe.detail,
			...plistExists ? missingGuiSession ? { missingGuiSession: true } : {} : { missingUnit: true }
		};
	}
	const parsed = probe.runtime;
	const plistExists = await launchAgentPlistExists(env);
	return {
		status: probe.state,
		state: parsed.state,
		pid: parsed.pid,
		lastExitStatus: parsed.lastExitStatus,
		lastExitReason: parsed.lastExitReason,
		cachedLabel: !plistExists
	};
}
function isLaunchctlAlreadyLoaded(res) {
	const detail = normalizeLowercaseStringOrEmpty(res.stderr || res.stdout);
	return res.termination === "exit" && (res.code === 130 || detail.includes("already exists in domain"));
}
function isUnsupportedGuiDomain(detail) {
	const normalized = normalizeLowercaseStringOrEmpty(detail);
	return normalized.includes("domain does not support specified action") || normalized.includes("could not find domain for user gui") || normalized.includes("bootstrap failed: 125");
}
function isLaunchctlOperationAlreadyInProgress(detail) {
	const normalized = normalizeLowercaseStringOrEmpty(detail);
	return normalized.includes("operation already in progress") || normalized.includes("bootstrap failed: 37");
}
function isLaunchctlBootstrapPendingTeardown(res) {
	if (res.termination !== "exit" || isLaunchctlAlreadyLoaded(res)) return false;
	const normalized = normalizeLowercaseStringOrEmpty(res.stderr || res.stdout);
	return normalized.includes("bootstrap failed: 5") || normalized.includes("input/output error");
}
async function probeLaunchAgentState(serviceTarget, timeoutMs) {
	const probe = await execLaunchctl(["print", serviceTarget], timeoutMs);
	if (probe.code !== 0) {
		if (isLaunchctlNotLoaded(probe)) return { state: "not-loaded" };
		return {
			state: "unknown",
			detail: formatLaunchctlResultDetail(probe) || void 0
		};
	}
	const runtime = parseLaunchctlPrint(probe.stdout || probe.stderr || "");
	if (normalizeLowercaseStringOrEmpty(runtime.state) === "running" || typeof runtime.pid === "number" && runtime.pid > 1) return {
		state: "running",
		runtime
	};
	return {
		state: "stopped",
		runtime
	};
}
async function waitForLaunchAgentStopped(serviceTarget) {
	let lastProbe = { state: "unknown" };
	for (let attempt = 0; attempt < 10; attempt += 1) {
		const probe = await probeLaunchAgentState(serviceTarget);
		lastProbe = probe;
		if (probe.state === "stopped" || probe.state === "not-loaded") return probe;
		await new Promise((resolve) => {
			setTimeout(resolve, 100);
		});
	}
	return lastProbe;
}
//#endregion
//#region src/daemon/launchd-install.ts
/** Transactional LaunchAgent installation, staging, rollback, and removal. */
async function uninstallLaunchAgent({ env, stdout }) {
	assertExternalLaunchAgentMutation(env, "uninstall");
	const domain = resolveLaunchAgentGuiDomain();
	const label = resolveLaunchAgentLabel(env);
	const plistPath = resolveLaunchAgentPlistPath(env);
	if ((await probeLaunchAgentState(`${domain}/${label}`)).state !== "not-loaded") {
		const bootout = await execLaunchctl([
			"bootout",
			domain,
			plistPath
		]);
		if (bootout.code !== 0 && !isLaunchctlNotLoaded(bootout)) throw new Error(`launchctl bootout failed: ${formatLaunchctlResultDetail(bootout)}`);
	}
	try {
		await fs.lstat(plistPath);
	} catch (error) {
		if (error.code !== "ENOENT") throw createLaunchAgentRemovalError(error);
		stdout.write(`LaunchAgent not found at ${plistPath}\n`);
		return;
	}
	const home = normalizeWindowsPathSeparators(resolveDaemonHomeDir(env));
	const trashDir = path.posix.join(home, ".Trash");
	const dest = path.join(trashDir, `${label}.plist`);
	try {
		await fs.mkdir(trashDir, { recursive: true });
		await fs.rename(plistPath, dest);
		stdout.write(`${formatLine("Moved LaunchAgent to Trash", dest)}\n`);
	} catch (error) {
		if (error.code === "ENOENT") try {
			await fs.lstat(plistPath);
		} catch (accessError) {
			if (accessError.code === "ENOENT") {
				stdout.write(`LaunchAgent not found at ${plistPath}\n`);
				return;
			}
			throw createLaunchAgentRemovalError(accessError);
		}
		throw createLaunchAgentRemovalError(error);
	}
}
function createLaunchAgentRemovalError(error) {
	const code = error.code;
	return /* @__PURE__ */ new Error(`LaunchAgent removal failed${code ? ` (${code})` : ""}. Check permissions and retry.`);
}
function currentGatewayLaunchAgentLabel(targetEnv) {
	const configuredCurrentLabel = process.env.OPENCLAW_LAUNCHD_LABEL?.trim();
	return [.../* @__PURE__ */ new Set([resolveLaunchAgentLabel(targetEnv), ...configuredCurrentLabel ? [assertValidLaunchAgentLabel(configuredCurrentLabel)] : []])].find((label) => isCurrentProcessLaunchdServiceLabel(label, process.env, { allowConfiguredLabelFallback: false }));
}
function assertExternalLaunchAgentMutation(env, action) {
	const currentLabel = currentGatewayLaunchAgentLabel(env);
	if (!currentLabel) return;
	throw new Error(`Refusing to ${action} LaunchAgent ${resolveLaunchAgentLabel(env)} from inside ${currentLabel}; run this command from an external shell.`);
}
async function stageLaunchAgent({ stdout, ...args }) {
	const { plistPath, stdoutPath } = await writeLaunchAgentPlist({
		...args,
		stdout
	});
	writeFormattedLines(stdout, [{
		label: "Staged LaunchAgent",
		value: plistPath
	}, {
		label: "Logs",
		value: stdoutPath
	}], { leadingBlankLine: true });
	return { plistPath };
}
async function snapshotLaunchAgentLoadedState(plistContents, serviceTarget) {
	const probe = await probeLaunchAgentState(serviceTarget);
	if (probe.state === "unknown") throw new Error(`launchctl print could not determine whether ${serviceTarget} is loaded: ${probe.detail ?? "unknown error"}`);
	const loaded = probe.state !== "not-loaded";
	if (loaded && plistContents === null) throw new Error(`LaunchAgent ${serviceTarget} is loaded but its plist is missing; refusing an install that cannot restore the current definition if activation fails.`);
	return loaded;
}
async function restoreLaunchAgentOwnedFile(params) {
	if (params.contents === null) {
		await fs.unlink(params.path).catch((error) => {
			if (error.code !== "ENOENT") throw error;
		});
		return;
	}
	const temporaryPath = `${params.path}.openclaw-${randomUUID()}.rollback`;
	try {
		await fs.writeFile(temporaryPath, params.contents.toString("utf8"), {
			flag: "wx",
			mode: params.mode
		});
		await fs.rename(temporaryPath, params.path);
		await fs.chmod(params.path, params.mode).catch(() => void 0);
	} finally {
		await fs.unlink(temporaryPath).catch(() => void 0);
	}
}
async function restoreLaunchAgentInstallArtifacts(params) {
	await restoreLaunchAgentOwnedFile({
		path: resolveLaunchAgentEnvFilePath(params.env, params.label),
		contents: params.snapshot.envFileContents,
		mode: 384
	});
	await restoreLaunchAgentOwnedFile({
		path: resolveLaunchAgentEnvWrapperPath(params.env, params.label),
		contents: params.snapshot.wrapperContents,
		mode: 448
	});
	for (const legacy of params.snapshot.legacy) await restoreLaunchAgentOwnedFile({
		path: legacy.plistPath,
		contents: legacy.contents,
		mode: 420
	});
	if (params.snapshot.plistContents === null) {
		await fs.unlink(params.plistPath).catch((error) => {
			if (error.code !== "ENOENT") throw error;
		});
		return;
	}
	await publishLaunchAgentPlist({
		label: params.label,
		plistPath: params.plistPath,
		contents: params.snapshot.plistContents.toString("utf8")
	});
}
async function restoreLaunchAgentInstall(params) {
	const serviceTarget = `${params.domain}/${params.label}`;
	const currentState = await probeLaunchAgentState(serviceTarget);
	if (currentState.state === "unknown") throw new Error(`launchctl print could not determine whether ${serviceTarget} is loaded during LaunchAgent rollback: ${currentState.detail ?? "unknown error"}`);
	if (currentState.state !== "not-loaded") {
		const bootout = await execLaunchctl(["bootout", serviceTarget]);
		if (bootout.code !== 0 && !isLaunchctlNotLoaded(bootout)) throw new Error(`launchctl bootout failed: ${formatLaunchctlResultDetail(bootout)}`);
	}
	await restoreLaunchAgentInstallArtifacts({
		env: params.env,
		label: params.label,
		plistPath: params.plistPath,
		snapshot: params.snapshot
	});
	if (params.snapshot.loaded && params.snapshot.plistContents !== null) await bootstrapLaunchAgentOrThrow({
		domain: params.domain,
		serviceTarget,
		plistPath: params.plistPath,
		actionHint: "openclaw gateway start",
		retryPendingTeardown: true
	});
	for (const legacy of params.snapshot.legacy) {
		if (!legacy.loaded || legacy.contents === null) continue;
		await bootstrapLaunchAgentOrThrow({
			domain: params.domain,
			serviceTarget: `${params.domain}/${legacy.label}`,
			plistPath: legacy.plistPath,
			actionHint: "openclaw gateway start",
			retryPendingTeardown: true
		});
	}
}
async function deactivateLaunchAgentDefinition(domain, plistPath) {
	for (const args of [[
		"bootout",
		domain,
		plistPath
	], ["unload", plistPath]]) {
		const result = await execLaunchctl(args);
		if (result.code !== 0 && !isLaunchctlNotLoaded(result)) throw new Error(`launchctl ${args[0]} failed during LaunchAgent install: ${formatLaunchctlResultDetail(result)}`);
	}
}
async function activateLaunchAgent(params) {
	const domain = resolveLaunchAgentGuiDomain();
	const label = resolveLaunchAgentLabel(params.env);
	try {
		await assertNoSystemLaunchDaemonOwnership(label);
		for (const legacy of params.snapshot.legacy) if (legacy.loaded) await deactivateLaunchAgentDefinition(domain, legacy.plistPath);
		if (params.snapshot.loaded) await deactivateLaunchAgentDefinition(domain, params.plistPath);
		await bootstrapLaunchAgentOrThrow({
			domain,
			serviceTarget: `${domain}/${label}`,
			plistPath: params.plistPath,
			actionHint: "openclaw gateway install --force",
			retryPendingTeardown: true
		});
		for (const legacy of params.snapshot.legacy) await fs.unlink(legacy.plistPath).catch((error) => {
			if (error.code !== "ENOENT") throw error;
		});
	} catch (error) {
		try {
			await restoreLaunchAgentInstall({
				domain,
				env: params.env,
				label,
				plistPath: params.plistPath,
				snapshot: params.snapshot
			});
		} catch (rollbackError) {
			const detail = error instanceof Error ? error.message : String(error);
			throw new Error(`${detail}\nThe previous LaunchAgent supervision could not be restored.`, { cause: rollbackError });
		}
		throw error;
	}
}
async function installLaunchAgent(args) {
	assertExternalLaunchAgentMutation(args.env, "install");
	const targetPlistPath = resolveLaunchAgentPlistPath(args.env);
	const previousContents = await readExistingLaunchAgentPlist(targetPlistPath);
	const label = resolveLaunchAgentLabel(args.env);
	const domain = resolveLaunchAgentGuiDomain();
	const legacy = await Promise.all(resolveLegacyGatewayLaunchAgentLabels(args.env.OPENCLAW_PROFILE).map(async (legacyLabel) => {
		const plistPath = resolveLaunchAgentPlistPathForLabel(args.env, legacyLabel);
		const contents = await readExistingLaunchAgentPlist(plistPath);
		return {
			label: legacyLabel,
			plistPath,
			contents,
			loaded: await snapshotLaunchAgentLoadedState(contents, `${domain}/${legacyLabel}`)
		};
	}));
	const snapshot = {
		plistContents: previousContents,
		envFileContents: await readExistingLaunchAgentPlist(resolveLaunchAgentEnvFilePath(args.env, label)),
		wrapperContents: await readExistingLaunchAgentPlist(resolveLaunchAgentEnvWrapperPath(args.env, label)),
		legacy,
		loaded: await snapshotLaunchAgentLoadedState(previousContents, `${domain}/${label}`)
	};
	let plistPath;
	let stdoutPath;
	try {
		({plistPath, stdoutPath} = await writeLaunchAgentPlist(args));
	} catch (error) {
		try {
			await restoreLaunchAgentInstallArtifacts({
				env: args.env,
				label,
				plistPath: targetPlistPath,
				snapshot
			});
		} catch (rollbackError) {
			const detail = error instanceof Error ? error.message : String(error);
			throw new Error(`${detail}\nThe previous LaunchAgent files could not be restored.`, { cause: rollbackError });
		}
		throw error;
	}
	await activateLaunchAgent({
		env: args.env,
		plistPath,
		snapshot
	});
	writeFormattedLines(args.stdout, [{
		label: "Installed LaunchAgent",
		value: plistPath
	}, {
		label: "Logs",
		value: stdoutPath
	}], { leadingBlankLine: true });
	return { plistPath };
}
//#endregion
//#region src/daemon/launchd-lifecycle.ts
/** LaunchAgent bootstrap recovery plus start and restart lifecycle controls. */
const LAUNCHCTL_PROTECTED_PID_TIMEOUT_MS = 2e3;
function readLaunchAgentPidForCleanupSync(serviceTarget) {
	const probe = spawnSync("launchctl", ["print", serviceTarget], {
		encoding: "utf8",
		timeout: LAUNCHCTL_PROTECTED_PID_TIMEOUT_MS
	});
	const result = {
		stdout: probe.stdout ?? "",
		stderr: probe.error?.message ?? probe.stderr ?? "",
		code: probe.error ? 1 : probe.status ?? 1
	};
	if (result.code !== 0) throw new Error(`launchctl print failed: ${formatLaunchctlResultDetail(result)}`);
	const pid = parseLaunchctlPrint(result.stdout || result.stderr || "").pid;
	if (pid === void 0) throw new Error("launchctl print did not report a running pid");
	return pid;
}
async function repairLaunchAgentBootstrap(args) {
	const env = args.env ?? process.env;
	const domain = resolveLaunchAgentGuiDomain();
	const label = resolveLaunchAgentLabel(env);
	const plistPath = resolveLaunchAgentPlistPath(env);
	const serviceTarget = `${domain}/${label}`;
	try {
		await assertNoSystemLaunchDaemonOwnership(label);
	} catch (error) {
		if (!isSystemLaunchDaemonOwnershipError(error)) throw error;
		return {
			ok: false,
			status: error.ownership.status === "unverifiable" ? "system-launchdaemon-unverifiable" : "system-launchdaemon-conflict",
			detail: error.message
		};
	}
	await rewriteLaunchAgentPlistForRestart({
		env,
		label,
		plistPath,
		warn: args.warn ?? ((message) => console.warn(formatLine("Warning", message)))
	});
	await execLaunchctl(["enable", serviceTarget]);
	const boot = await execLaunchctl([
		"bootstrap",
		domain,
		plistPath
	]);
	let repairStatus = "repaired";
	if (boot.code !== 0) {
		const detail = (boot.stderr || boot.stdout).trim();
		if (isUnsupportedGuiDomain(detail)) return {
			ok: false,
			status: "gui-session-unavailable",
			detail,
			domain
		};
		if (!isLaunchctlAlreadyLoaded(boot)) return {
			ok: false,
			status: "bootstrap-failed",
			detail: detail || void 0
		};
		repairStatus = "already-loaded";
	}
	if (repairStatus === "repaired") return {
		ok: true,
		status: repairStatus
	};
	if ((await readLaunchAgentRuntime(env)).status === "running") return {
		ok: true,
		status: repairStatus
	};
	const kick = await execLaunchctl(["kickstart", serviceTarget]);
	if (kick.code !== 0) return {
		ok: false,
		status: "kickstart-failed",
		detail: (kick.stderr || kick.stdout).trim() || void 0
	};
	return {
		ok: true,
		status: repairStatus
	};
}
function writeLaunchAgentActionLine(stdout, label, value) {
	try {
		stdout.write(`${formatLine(label, value)}\n`);
	} catch (err) {
		if (err?.code !== "EPIPE") throw err;
	}
}
async function ensureLaunchAgentLoadedAfterFailure(params) {
	if ((await execLaunchctl(["print", params.serviceTarget])).code === 0) return { loaded: true };
	try {
		await bootstrapLaunchAgentOrThrow({
			domain: params.domain,
			serviceTarget: params.serviceTarget,
			plistPath: params.plistPath,
			actionHint: "openclaw gateway start",
			onMutation: params.onMutation
		});
		return { loaded: true };
	} catch (error) {
		return {
			loaded: false,
			detail: error instanceof Error ? error.message : String(error)
		};
	}
}
function formatLaunchAgentLeftUnloadedError(params) {
	return [
		params.failure,
		`LaunchAgent ${params.serviceTarget} is not loaded and could not be restored: ${params.restoreDetail}`,
		"The gateway is down and launchd has no job left to respawn it.",
		`Fix: run \`openclaw gateway start\`, or \`launchctl bootstrap ${params.domain} ${params.plistPath}\`.`
	].join("\n");
}
async function startLaunchAgent({ stdout, env, onMutation }) {
	const serviceEnv = env ?? process.env;
	const domain = resolveLaunchAgentGuiDomain();
	const label = resolveLaunchAgentLabel(serviceEnv);
	const plistPath = resolveLaunchAgentPlistPath(serviceEnv);
	const serviceTarget = `${domain}/${label}`;
	const reportMutation = createGatewayLifecycleMutationReporter(onMutation);
	await assertNoSystemLaunchDaemonOwnership(label);
	const enabled = (await execLaunchctl(["enable", serviceTarget])).code === 0;
	if (enabled) reportMutation("enable");
	let start = await execLaunchctl(["kickstart", serviceTarget]);
	if (isLaunchctlNotLoaded(start)) {
		await bootstrapLaunchAgentOrThrow({
			domain,
			serviceTarget,
			plistPath,
			actionHint: "openclaw gateway start",
			onMutation: reportMutation,
			skipEnable: enabled
		});
		start = await execLaunchctl(["kickstart", serviceTarget]);
	}
	if (start.code !== 0) throw new Error(`launchctl kickstart failed: ${start.stderr || start.stdout}`.trim());
	reportMutation("kickstart");
	writeLaunchAgentActionLine(stdout, "Started LaunchAgent", serviceTarget);
}
async function restartLaunchAgent({ preserveDefinition, stdout, env, warn, onMutation }) {
	const serviceEnv = env ?? process.env;
	const domain = resolveLaunchAgentGuiDomain();
	const label = resolveLaunchAgentLabel(serviceEnv);
	const plistPath = resolveLaunchAgentPlistPath(serviceEnv);
	const serviceTarget = `${domain}/${label}`;
	const reportMutation = createGatewayLifecycleMutationReporter(onMutation);
	await assertNoSystemLaunchDaemonOwnership(label);
	const detached = isCurrentProcessLaunchdServiceLabel(label);
	if (!detached) {
		const { port: cleanupPort, probeHosts } = await resolveLaunchAgentGatewayContext(serviceEnv);
		if (cleanupPort !== null) {
			cleanStaleGatewayProcessesSync(cleanupPort, { resolveProtectedPid: () => readLaunchAgentPidForCleanupSync(serviceTarget) });
			const diagnostics = await inspectPortUsage(cleanupPort, { probeHosts }).catch(() => null);
			if (diagnostics?.status === "busy") {
				const managedPid = (await readLaunchAgentRuntime(serviceEnv)).pid;
				if (!(managedPid !== void 0 && diagnostics.listeners.length > 0 && diagnostics.listeners.every((listener) => listener.pid === managedPid))) throw new Error([`gateway port ${cleanupPort} is busy but is not verifiably owned by LaunchAgent ${label}`, ...formatPortDiagnostics(diagnostics)].join("\n"));
			}
		}
	}
	const plistReloadNeeded = !preserveDefinition && await rewriteLaunchAgentPlistForRestart({
		env: serviceEnv,
		label,
		plistPath,
		stdout,
		warn
	});
	if (detached) {
		const handoff = scheduleDetachedLaunchdRestartHandoff({
			env: serviceEnv,
			mode: plistReloadNeeded ? "reload" : "kickstart",
			waitForPid: process.pid
		});
		if (!handoff.ok) throw new Error(`launchd restart handoff failed: ${handoff.error}`);
		reportMutation(plistReloadNeeded ? "handoff-reload" : "handoff-kickstart");
		writeLaunchAgentActionLine(stdout, "Scheduled LaunchAgent restart", serviceTarget);
		return { outcome: "scheduled" };
	}
	if ((await execLaunchctl(["enable", serviceTarget])).code === 0) reportMutation("enable");
	if (plistReloadNeeded) {
		const bootout = await execLaunchctl(["bootout", serviceTarget]);
		if (bootout.code !== 0 && !isLaunchctlNotLoaded(bootout)) throw new Error(`launchctl bootout failed: ${formatLaunchctlResultDetail(bootout)}`);
		if (bootout.code === 0) reportMutation("bootout");
		try {
			await bootstrapLaunchAgentOrThrow({
				domain,
				serviceTarget,
				plistPath,
				actionHint: "openclaw gateway restart",
				onMutation: reportMutation,
				retryPendingTeardown: true
			});
		} catch (error) {
			const restored = await ensureLaunchAgentLoadedAfterFailure({
				domain,
				serviceTarget,
				plistPath,
				onMutation: reportMutation
			});
			if (restored.loaded) throw error;
			throw new Error(formatLaunchAgentLeftUnloadedError({
				domain,
				serviceTarget,
				plistPath,
				failure: error instanceof Error ? error.message : String(error),
				restoreDetail: restored.detail
			}), { cause: error });
		}
		writeLaunchAgentActionLine(stdout, "Restarted LaunchAgent", serviceTarget);
		return { outcome: "completed" };
	}
	const start = await execLaunchctl([
		"kickstart",
		"-k",
		serviceTarget
	]);
	if (start.code === 0) {
		reportMutation("kickstart");
		writeLaunchAgentActionLine(stdout, "Restarted LaunchAgent", serviceTarget);
		return { outcome: "completed" };
	}
	if (!isLaunchctlNotLoaded(start)) {
		const restored = await ensureLaunchAgentLoadedAfterFailure({
			domain,
			serviceTarget,
			plistPath,
			onMutation: reportMutation
		});
		const failure = `launchctl kickstart failed: ${start.stderr || start.stdout}`.trim();
		if (restored.loaded) throw new Error(failure);
		throw new Error(formatLaunchAgentLeftUnloadedError({
			domain,
			serviceTarget,
			plistPath,
			failure,
			restoreDetail: restored.detail
		}));
	}
	await bootstrapLaunchAgentOrThrow({
		domain,
		serviceTarget,
		plistPath,
		actionHint: "openclaw gateway restart",
		onMutation: reportMutation
	});
	if (preserveDefinition) {
		const kick = await execLaunchctl(["kickstart", serviceTarget]);
		if (kick.code !== 0) throw new Error(`launchctl kickstart failed: ${kick.stderr || kick.stdout}`.trim());
		reportMutation("kickstart");
	}
	writeLaunchAgentActionLine(stdout, "Restarted LaunchAgent", serviceTarget);
	return { outcome: "completed" };
}
//#endregion
//#region src/daemon/launchd-stop.ts
/** LaunchAgent stop semantics and in-service maintenance parking. */
const LAUNCH_AGENT_STOP_PORT_RELEASE_TIMEOUT_MS = 20 * 1e3;
const LAUNCH_AGENT_STOP_PORT_RELEASE_POLL_MS = 100;
async function bootoutLaunchAgentOrThrow(params) {
	const bootout = await execLaunchctl(["bootout", params.serviceTarget]);
	if (bootout.code !== 0 && !isLaunchctlNotLoaded(bootout)) throw new Error(`${params.warning}; launchctl bootout failed: ${formatLaunchctlResultDetail(bootout)}`);
	params.onMutation?.();
	params.stdout.write(`${formatLine("Warning", params.warning)}\n`);
}
async function waitForGatewayPortRelease(port, probeHosts) {
	const deadline = Date.now() + LAUNCH_AGENT_STOP_PORT_RELEASE_TIMEOUT_MS;
	while (Date.now() < deadline) {
		await sleep(Math.min(LAUNCH_AGENT_STOP_PORT_RELEASE_POLL_MS, deadline - Date.now()));
		if (await probePortUsage(port, probeHosts) === "free") return true;
	}
	return false;
}
async function assertGatewayPortReleasedAfterStop(env) {
	const { port, probeHosts } = await resolveLaunchAgentGatewayContext(env);
	if (port === null) return;
	cleanStaleGatewayProcessesSync(port);
	const diagnostics = await inspectPortUsage(port, { probeHosts }).catch(() => null);
	if (diagnostics?.status !== "busy") return;
	if (await waitForGatewayPortRelease(port, probeHosts)) return;
	throw new Error([`gateway port ${port} is still busy after LaunchAgent stop`, ...formatPortDiagnostics(diagnostics)].join("\n"));
}
async function stopLaunchAgent({ stdout, env, disable: persistDisable, onMutation }) {
	const serviceEnv = env ?? process.env;
	const domain = resolveLaunchAgentGuiDomain();
	const label = resolveLaunchAgentLabel(serviceEnv);
	const serviceTarget = `${domain}/${label}`;
	const reportMutation = createGatewayLifecycleMutationReporter(onMutation);
	if (isCurrentProcessLaunchdServiceLabel(label, process.env, { allowConfiguredLabelFallback: false })) throw new Error(`Refusing to stop LaunchAgent ${label} from inside the same launchd service; run this command from an external shell.`);
	if (!persistDisable) {
		const bootout = await execLaunchctl(["bootout", serviceTarget]);
		if (bootout.code !== 0 && !isLaunchctlNotLoaded(bootout)) throw new Error(`launchctl bootout failed: ${formatLaunchctlResultDetail(bootout)}`);
		reportMutation("bootout");
		await assertGatewayPortReleasedAfterStop(serviceEnv);
		stdout.write(`${formatLine("Stopped LaunchAgent", serviceTarget)}\n`);
		return;
	}
	const disableResult = await execLaunchctl(["disable", serviceTarget]);
	if (disableResult.code !== 0) {
		await bootoutLaunchAgentOrThrow({
			serviceTarget,
			stdout,
			warning: `launchctl disable failed; used bootout fallback and left service unloaded: ${formatLaunchctlResultDetail(disableResult)}`,
			onMutation: () => reportMutation("disable-bootout")
		});
		await assertGatewayPortReleasedAfterStop(serviceEnv);
		stdout.write(`${formatLine("Stopped LaunchAgent (degraded)", serviceTarget)}\n`);
		return;
	}
	reportMutation("disable");
	const stop = await execLaunchctl(["stop", label]);
	if (stop.code !== 0 && !isLaunchctlNotLoaded(stop)) {
		await bootoutLaunchAgentOrThrow({
			serviceTarget,
			stdout,
			warning: `launchctl stop failed; used bootout fallback and left service unloaded: ${formatLaunchctlResultDetail(stop)}`,
			onMutation: () => reportMutation("disable-bootout")
		});
		await assertGatewayPortReleasedAfterStop(serviceEnv);
		stdout.write(`${formatLine("Stopped LaunchAgent (degraded)", serviceTarget)}\n`);
		return;
	}
	reportMutation("disable-stop");
	const stopState = await waitForLaunchAgentStopped(serviceTarget);
	if (stopState.state !== "stopped" && stopState.state !== "not-loaded") {
		await bootoutLaunchAgentOrThrow({
			serviceTarget,
			stdout,
			warning: stopState.state === "unknown" ? `launchctl print could not confirm stop; used bootout fallback and left service unloaded: ${stopState.detail ?? "unknown error"}` : "launchctl stop did not fully stop the service; used bootout fallback and left service unloaded",
			onMutation: () => reportMutation("disable-bootout")
		});
		await assertGatewayPortReleasedAfterStop(serviceEnv);
		stdout.write(`${formatLine("Stopped LaunchAgent (degraded)", serviceTarget)}\n`);
		return;
	}
	await assertGatewayPortReleasedAfterStop(serviceEnv);
	stdout.write(`${formatLine("Stopped LaunchAgent", serviceTarget)}\n`);
}
async function parkCurrentLaunchAgentForMaintenance(params = {}) {
	const serviceEnv = params.env ?? process.env;
	const domain = resolveLaunchAgentGuiDomain();
	const label = resolveLaunchAgentLabel(serviceEnv);
	if (!isCurrentProcessLaunchdServiceLabel(label, process.env, { allowConfiguredLabelFallback: false })) return false;
	const serviceTarget = `${domain}/${label}`;
	const disable = await execLaunchctl(["disable", serviceTarget]);
	if (disable.code !== 0) throw new Error(`launchctl disable failed while parking ${serviceTarget}: ${formatLaunchctlResultDetail(disable)}`);
	const handoff = scheduleDetachedLaunchdMaintenancePark({
		env: serviceEnv,
		waitForPid: process.pid
	});
	const handoffError = !handoff.ok ? handoff.error : await handoff.value ? void 0 : "helper failed to spawn";
	if (handoffError) {
		const rollback = await execLaunchctl(["enable", serviceTarget]);
		const rollbackDetail = rollback.code === 0 ? "restored launchd enable state" : `launchctl enable rollback failed: ${formatLaunchctlResultDetail(rollback)}`;
		throw new Error(`launchd maintenance park handoff failed: ${handoffError}; ${rollbackDetail}`);
	}
	return true;
}
//#endregion
//#region src/daemon/launchd-update-jobs.ts
/** Discovery and shutdown of stale OpenClaw launchd updater jobs. */
const OPENCLAW_UPDATE_LAUNCHD_LABEL_PREFIX = "ai.openclaw.update.";
const MANUAL_UPDATE_LAUNCHD_LABEL_PATTERN = /^ai\.openclaw\.manual-update\.\d+$/;
const OPENCLAW_PROFILE_UPDATE_LAUNCHD_LABEL_PATTERN = /^ai\.openclaw\.[A-Za-z0-9._-]+\.update\.[A-Za-z0-9._-]+$/;
const OPENCLAW_DIRECT_CLI_NAMES = /* @__PURE__ */ new Set(["openclaw", "openclaw.mjs"]);
const OPENCLAW_NODE_RUNTIME_NAMES = /* @__PURE__ */ new Set([
	"bun",
	"bun.exe",
	"node",
	"node.exe"
]);
const OPENCLAW_SCRIPT_NAMES = /* @__PURE__ */ new Set(["openclaw.mjs"]);
function normalizeOpenClawUpdateLaunchdLabel(label) {
	if (typeof label !== "string") return null;
	const trimmed = label.trim();
	if (trimmed.startsWith(OPENCLAW_UPDATE_LAUNCHD_LABEL_PREFIX)) return trimmed;
	return MANUAL_UPDATE_LAUNCHD_LABEL_PATTERN.test(trimmed) ? trimmed : null;
}
function normalizeOpenClawUpdateLaunchdLabelCandidate(label) {
	const normalized = normalizeOpenClawUpdateLaunchdLabel(label);
	if (normalized) return {
		label: normalized,
		requiresMetadata: false
	};
	if (typeof label !== "string") return null;
	const trimmed = label.trim();
	return OPENCLAW_PROFILE_UPDATE_LAUNCHD_LABEL_PATTERN.test(trimmed) ? {
		label: trimmed,
		requiresMetadata: true
	} : null;
}
function isCurrentGatewayLaunchdLabel(label, env) {
	if (label === resolveGatewayLaunchAgentLabel(env.OPENCLAW_PROFILE)) return true;
	if (env.OPENCLAW_SERVICE_MARKER?.trim() !== "openclaw" || env.OPENCLAW_SERVICE_KIND?.trim() !== "gateway") return false;
	const configuredLabel = env.OPENCLAW_LAUNCHD_LABEL?.trim();
	return Boolean(configuredLabel && label === configuredLabel);
}
function resolveCurrentOpenClawUpdateLaunchdJobLabel(env = process.env) {
	for (const label of [
		env.LAUNCH_JOB_LABEL,
		env.LAUNCH_JOB_NAME,
		env.XPC_SERVICE_NAME,
		env.OPENCLAW_LAUNCHD_LABEL
	]) {
		const candidate = normalizeOpenClawUpdateLaunchdLabelCandidate(label);
		if (candidate) {
			if (isCurrentGatewayLaunchdLabel(candidate.label, env)) continue;
			return candidate;
		}
	}
	return null;
}
function parseLaunchctlListOpenClawUpdateJobs(output) {
	return parseLaunchctlListOpenClawUpdateJobCandidates(output).filter((job) => !job.requiresMetadata).map(({ requiresMetadata: _requiresMetadata, ...job }) => job);
}
function parseLaunchctlListOpenClawUpdateJobCandidates(output) {
	const jobs = [];
	for (const rawLine of output.split(/\r?\n/)) {
		const line = rawLine.trim();
		if (!line) continue;
		const [pidRaw, statusRaw, ...labelParts] = line.split(/\s+/);
		const candidate = normalizeOpenClawUpdateLaunchdLabelCandidate(labelParts.join(" "));
		if (!candidate) continue;
		const pid = pidRaw === "-" ? void 0 : parseStrictPositiveInteger(pidRaw ?? "");
		const lastExitStatus = parseStrictInteger(statusRaw ?? "");
		jobs.push({
			label: candidate.label,
			requiresMetadata: candidate.requiresMetadata,
			...pid !== void 0 ? { pid } : {},
			...lastExitStatus !== void 0 ? { lastExitStatus } : {}
		});
	}
	return jobs.toSorted((a, b) => a.label.localeCompare(b.label));
}
function hasOpenClawUpdateLaunchdMarker(env) {
	return env?.OPENCLAW_UPDATE_RUN_HANDOFF?.trim() === "1";
}
function isOpenClawUpdateCommandPrefix(programArguments, updateIndex) {
	if (updateIndex === 1) {
		const cliName = path.basename(programArguments[0] ?? "").toLowerCase();
		return OPENCLAW_DIRECT_CLI_NAMES.has(cliName);
	}
	if (updateIndex !== 2) return false;
	const runtimeName = path.basename(programArguments[0] ?? "").toLowerCase();
	const entryName = path.basename(programArguments[1] ?? "").toLowerCase();
	return OPENCLAW_NODE_RUNTIME_NAMES.has(runtimeName) && OPENCLAW_SCRIPT_NAMES.has(entryName);
}
function isOpenClawUpdateProgramArguments(programArguments) {
	if (!Array.isArray(programArguments) || programArguments.length === 0) return false;
	const updateIndex = programArguments.findIndex((arg) => arg.trim() === "update");
	if (updateIndex < 0 || !programArguments.slice(updateIndex + 1).includes("--yes")) return false;
	return isOpenClawUpdateCommandPrefix(programArguments, updateIndex) && !programArguments.some((arg) => arg.trim() === "gateway");
}
async function isLaunchdJobConfirmedOpenClawUpdater(params) {
	const command = await readLaunchAgentProgramArgumentsFromFile(resolveLaunchAgentPlistPathForLabel(params.env, params.label));
	return hasOpenClawUpdateLaunchdMarker(command?.environment) || isOpenClawUpdateProgramArguments(command?.programArguments);
}
async function findStaleOpenClawUpdateLaunchdJobs(env = process.env) {
	if (process.platform !== "darwin") return [];
	const result = await execLaunchctl(["list"]);
	if (result.code !== 0) return [];
	const jobs = [];
	for (const job of parseLaunchctlListOpenClawUpdateJobCandidates(result.stdout)) {
		if (isCurrentGatewayLaunchdLabel(job.label, env)) continue;
		if (job.requiresMetadata && !await isLaunchdJobConfirmedOpenClawUpdater({
			label: job.label,
			env
		})) continue;
		jobs.push({
			label: job.label,
			...job.pid !== void 0 ? { pid: job.pid } : {},
			...job.lastExitStatus !== void 0 ? { lastExitStatus: job.lastExitStatus } : {}
		});
	}
	return jobs;
}
async function disableOpenClawUpdateLaunchdJobCandidate(params) {
	if (process.platform !== "darwin") return false;
	if (params.candidate.requiresMetadata && !(params.trustCurrentEnvMarker && hasOpenClawUpdateLaunchdMarker(params.env) || await isLaunchdJobConfirmedOpenClawUpdater({
		label: params.candidate.label,
		env: params.env
	}))) return false;
	return (await execLaunchctl(["disable", `${resolveLaunchAgentGuiDomain()}/${assertValidLaunchAgentLabel(params.candidate.label)}`])).code === 0;
}
async function disableOpenClawUpdateLaunchdJob(label, env = process.env) {
	const candidate = normalizeOpenClawUpdateLaunchdLabelCandidate(label);
	if (!candidate) return false;
	return await disableOpenClawUpdateLaunchdJobCandidate({
		candidate,
		env,
		trustCurrentEnvMarker: false
	});
}
async function disableCurrentOpenClawUpdateLaunchdJob(env = process.env) {
	const candidate = resolveCurrentOpenClawUpdateLaunchdJobLabel(env);
	if (!candidate) return false;
	return await disableOpenClawUpdateLaunchdJobCandidate({
		candidate,
		env,
		trustCurrentEnvMarker: isCurrentProcessLaunchdServiceLabel(candidate.label, env, { allowConfiguredLabelFallback: false })
	});
}
//#endregion
export { parseLaunchAgentEnabled as _, parkCurrentLaunchAgentForMaintenance as a, restartLaunchAgent as c, stageLaunchAgent as d, uninstallLaunchAgent as f, launchAgentPlistExists as g, isLaunchAgentLoaded as h, parseLaunchctlListOpenClawUpdateJobs as i, startLaunchAgent as l, isLaunchAgentEnabled as m, disableOpenClawUpdateLaunchdJob as n, stopLaunchAgent as o, formatLaunchAgentGuiSessionError as p, findStaleOpenClawUpdateLaunchdJobs as r, repairLaunchAgentBootstrap as s, disableCurrentOpenClawUpdateLaunchdJob as t, installLaunchAgent as u, parseLaunchctlPrint as v, readLaunchAgentRuntime as y };
