import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.js";
import { o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js";
import { r as isMissingPathError } from "./errno-CkbDOfLk.js";
import "./errors-Z0lU1pH3.js";
import { t as sanitizeForLog } from "./ansi-DjDeieuH.js";
import { S as readSystemctlDetail, _ as isSystemdUnitActive, f as execSystemctl, o as resolveSystemdServiceName, s as resolveSystemdUnitPath } from "./systemd-service-files-BgofM7Qa.js";
import path from "node:path";
import fs from "node:fs/promises";
//#region src/daemon/systemd-system.ts
/** Detects system-scope systemd ownership before mutating a user gateway unit. */
function formatUnknownError(error) {
	return truncateUtf16Safe(sanitizeForLog(error instanceof Error ? error.message : String(error)), 500);
}
function quotePosixArgument(value) {
	return /^[A-Za-z0-9_@%+=:,./-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
}
function unverifiableSystemOwnership(unitName, detail, operation = "systemctl") {
	return {
		status: "unverifiable",
		unitName,
		operation,
		detail
	};
}
async function querySystemManager(unitName, run = execSystemctl) {
	const result = await run([
		"show",
		"--property=LoadState",
		"--value",
		unitName
	]);
	const loadState = result.stdout.trim().toLowerCase();
	if (result.code === 0) {
		if (loadState === "not-found") return {
			status: "absent",
			unitName
		};
		if (loadState) return {
			status: "loaded",
			unitName
		};
		return unverifiableSystemOwnership(unitName, "systemctl returned no LoadState");
	}
	const detail = readSystemctlDetail(result) || `systemctl exited with code ${result.code}`;
	const normalizedDetail = detail.toLowerCase();
	if (result.termination === "exit" && normalizedDetail.includes(unitName.toLowerCase()) && /not[- ]found|could not be found/i.test(normalizedDetail)) return {
		status: "absent",
		unitName
	};
	return unverifiableSystemOwnership(unitName, detail);
}
async function findInstalledSystemUnit(unitName, run = execSystemctl) {
	const result = await run([
		"show",
		"--property=UnitPath",
		"--value"
	]);
	if (result.code !== 0) return unverifiableSystemOwnership(unitName, readSystemctlDetail(result) || `systemctl exited with code ${result.code}`);
	const loadPaths = [...new Set(result.stdout.split(/\s+/).filter(path.posix.isAbsolute))];
	if (loadPaths.length === 0) return unverifiableSystemOwnership(unitName, "systemctl returned no system manager unit load paths");
	for (const dir of loadPaths) {
		const unitPath = path.posix.join(dir, unitName);
		try {
			await fs.lstat(unitPath);
			return {
				status: "installed",
				unitName,
				unitPath
			};
		} catch (error) {
			if (isMissingPathError(error)) continue;
			return unverifiableSystemOwnership(unitName, `${unitPath}: ${formatUnknownError(error)}`, "filesystem");
		}
	}
	return {
		status: "absent",
		unitName
	};
}
async function inspectSystemSystemdOwnership(unitName, timeoutMs) {
	if (process.platform !== "linux") return {
		status: "absent",
		unitName
	};
	const deadlineAt = timeoutMs && timeoutMs > 0 ? Date.now() + timeoutMs : void 0;
	const run = (args) => execSystemctl(args, void 0, deadlineAt ? Math.max(1, deadlineAt - Date.now()) : void 0);
	const initialQuery = await querySystemManager(unitName, run);
	if (initialQuery.status !== "absent") return initialQuery;
	const installed = await findInstalledSystemUnit(unitName, run);
	if (installed.status !== "absent") return installed;
	return await querySystemManager(unitName, run);
}
function isRunningAsRoot() {
	if (typeof process.geteuid !== "function") return false;
	try {
		return process.geteuid() === 0;
	} catch {
		return false;
	}
}
function formatSystemSystemdOwnershipError(ownership) {
	const privilegePrefix = isRunningAsRoot() ? "" : "sudo ";
	const unitName = quotePosixArgument(ownership.unitName);
	const summary = ownership.status === "loaded" ? `System systemd unit ${ownership.unitName} already owns this gateway unit name.` : ownership.status === "installed" ? `System systemd unit ${ownership.unitPath} already owns this gateway unit name.` : `System systemd ownership for ${ownership.unitName} could not be verified: ${ownership.detail}`;
	const installedInAdministratorPath = ownership.status === "installed" && (ownership.unitPath.startsWith("/etc/systemd/system/") || ownership.unitPath.startsWith("/etc/systemd/system.control/"));
	return [
		summary,
		"Refusing to create or activate a user systemd unit with the same name because duplicate managers can restart-loop the gateway.",
		"OpenClaw does not manage system-scope units, and --force does not override system ownership.",
		ownership.status === "loaded" ? `Keep it as the sole gateway manager, or inspect it with \`${privilegePrefix}systemctl cat ${unitName}\`, then disable it and uninstall or reconfigure the package, generator, or administrator unit that owns it before retrying.` : installedInAdministratorPath ? `Keep it as the sole gateway manager, or run \`${privilegePrefix}systemctl disable --now ${unitName}\`, \`${privilegePrefix}rm ${quotePosixArgument(ownership.unitPath)}\`, and \`${privilegePrefix}systemctl daemon-reload\` before retrying.` : ownership.status === "installed" ? `Keep it as the sole gateway manager, or inspect it with \`${privilegePrefix}systemctl cat ${unitName}\`, then uninstall or reconfigure the package, generator, or runtime owner of ${quotePosixArgument(ownership.unitPath)} before retrying.` : "Fix the reported systemctl or filesystem access error, then retry."
	].join("\n");
}
var SystemSystemdOwnershipError = class extends Error {
	constructor(ownership) {
		super(formatSystemSystemdOwnershipError(ownership));
		this.ownership = ownership;
		this.code = "SYSTEM_SYSTEMD_OWNERSHIP";
		this.name = "SystemSystemdOwnershipError";
	}
};
function isSystemSystemdOwnershipError(error) {
	return error instanceof SystemSystemdOwnershipError;
}
async function assertNoSystemSystemdOwnership(unitName, timeoutMs) {
	const ownership = await inspectSystemSystemdOwnership(unitName, timeoutMs);
	if (ownership.status !== "absent") throw new SystemSystemdOwnershipError(ownership);
}
//#endregion
//#region src/daemon/systemd-scope.ts
/** Installed systemd scope discovery and dueling-manager diagnostics. */
const SYSTEM_SYSTEMD_UNIT_DIRS = [
	"/etc/systemd/system",
	"/usr/lib/systemd/system",
	"/lib/systemd/system"
];
async function findSystemSystemdUnitPath(env) {
	const serviceFile = `${resolveSystemdServiceName(env)}.service`;
	for (const dir of SYSTEM_SYSTEMD_UNIT_DIRS) {
		const candidate = path.posix.join(dir, serviceFile);
		try {
			await fs.access(candidate);
			return candidate;
		} catch {
			continue;
		}
	}
	return null;
}
async function assertNoSystemGatewayOwnership(env) {
	if (env.OPENCLAW_SERVICE_KIND?.trim() === "node") return;
	await assertNoSystemSystemdOwnership(`${resolveSystemdServiceName(env)}.service`);
}
async function findMarkerOwnedSystemSystemdUnit() {
	const { findSystemGatewayServices } = await import("./inspect-BmcvquQv.js");
	let services;
	try {
		services = await findSystemGatewayServices();
	} catch {
		return null;
	}
	for (const svc of services) {
		if (svc.platform !== "linux" || svc.scope !== "system" || svc.marker !== "openclaw" || !svc.label?.endsWith(".service")) continue;
		const unitPath = /^unit:\s*(.+)$/.exec(svc.detail.trim())?.[1]?.trim();
		if (unitPath) return {
			unitName: svc.label,
			unitPath
		};
	}
	return null;
}
async function findUserSystemdGatewayScope(env) {
	const canonicalUnitName = `${resolveSystemdServiceName(env)}.service`;
	let userPath;
	try {
		userPath = resolveSystemdUnitPath(env);
	} catch {
		userPath = null;
	}
	if (!userPath) return null;
	try {
		await fs.access(userPath);
		return {
			scope: "user",
			unitName: canonicalUnitName,
			unitPath: userPath
		};
	} catch {
		return null;
	}
}
async function findSystemSystemdGatewayScope(env) {
	const canonicalUnitName = `${resolveSystemdServiceName(env)}.service`;
	const systemPath = await findSystemSystemdUnitPath(env);
	if (systemPath) return {
		scope: "system",
		unitName: canonicalUnitName,
		unitPath: systemPath
	};
	const owned = await findMarkerOwnedSystemSystemdUnit();
	return owned ? {
		scope: "system",
		unitName: owned.unitName,
		unitPath: owned.unitPath
	} : null;
}
/**
* Canonical detector: reports every installed scope without early-returning,
* so a coexisting user + system unit surfaces as `dueling`.
*/
async function findSystemdGatewayInstallation(env) {
	const [user, system] = await Promise.all([findUserSystemdGatewayScope(env), findSystemSystemdGatewayScope(env)]);
	if (user && system) {
		if (user.unitName === system.unitName) return {
			kind: "dueling",
			user,
			system
		};
		return {
			kind: "user",
			user
		};
	}
	if (user) return {
		kind: "user",
		user
	};
	if (system) return {
		kind: "system",
		system
	};
	return { kind: "none" };
}
/**
* The single scope to act on, preserving the long-standing user-first
* preference its four lifecycle callers (stop/restart/is-enabled/runtime)
* rely on. Dueling resolution (removing the redundant user unit) is handled
* separately by doctor via {@link findSystemdGatewayInstallation}; this
* function intentionally does not change lifecycle semantics.
*/
async function findInstalledSystemdGatewayScope(env) {
	const installation = await findSystemdGatewayInstallation(env);
	if (installation.kind === "dueling" || installation.kind === "user") return installation.user;
	if (installation.kind === "system") return installation.system;
	return null;
}
/**
* True only when the system-scope unit is running now AND persistently enabled
* at boot. Doctor's dueling repair deletes the user unit behind this probe, so
* both halves are required: an enabled-but-failed unit would leave no gateway
* until the next boot, and an active-but-unenabled unit would leave none after
* it. Uncheckable (systemctl missing/erroring) reads as false so the repair
* fails closed to hints rather than removing a working user-scope gateway.
*/
async function isSystemUnitActiveAndEnabled(env, unitName) {
	const active = await isSystemdUnitActive(env, unitName, "system");
	if (!active.ok || !active.value) return false;
	const res = await execSystemctl(["is-enabled", unitName], env);
	if (res.code !== 0) return false;
	return normalizeLowercaseStringOrEmpty(res.stdout) === "enabled";
}
/**
* Builds the operator-facing warning for a `dueling` installation, or null for
* any other state. Pure (no I/O) so the startup guard's messaging is unit
* testable without faking the whole service-mode boot path.
*/
function formatDuelingScopesWarning(installation, port) {
	if (installation.kind !== "dueling") return null;
	const { user, system } = installation;
	return `detected BOTH a user-scope (${user.unitPath}) and a system-scope (${system.unitPath}) gateway unit bound to port ${port}; they will SIGTERM each other in a restart loop. Run \`openclaw doctor --fix\` to resolve which unit should own this gateway.`;
}
//#endregion
export { isSystemUnitActiveAndEnabled as a, formatDuelingScopesWarning as i, findInstalledSystemdGatewayScope as n, assertNoSystemSystemdOwnership as o, findSystemdGatewayInstallation as r, isSystemSystemdOwnershipError as s, assertNoSystemGatewayOwnership as t };
