import { s as defaultRuntime } from "./subsystem-hATE68va.js";
import { t as formatCliCommand } from "./command-format-DOYW_19_.js";
import { A as collectConfigEnvVars, N as VERSION } from "./config-BOTdzMIB.js";
import { a as NODE_SERVICE_MARKER, d as resolveGatewaySystemdServiceName, g as resolveNodeWindowsTaskName, h as resolveNodeSystemdServiceName, i as NODE_SERVICE_KIND, l as resolveGatewayLaunchAgentLabel, m as resolveNodeLaunchAgentLabel, n as GATEWAY_SERVICE_KIND, o as NODE_WINDOWS_TASK_SCRIPT_NAME, r as GATEWAY_SERVICE_MARKER } from "./constants-CRHmdXpV.js";
import process$1 from "node:process";
import path from "node:path";
import { promisify } from "node:util";
import fs from "node:fs/promises";
import { execFile } from "node:child_process";

//#region src/infra/runtime-guard.ts
const MIN_NODE = {
	major: 22,
	minor: 12,
	patch: 0
};
const SEMVER_RE = /(\d+)\.(\d+)\.(\d+)/;
function parseSemver(version) {
	if (!version) return null;
	const match = version.match(SEMVER_RE);
	if (!match) return null;
	const [, major, minor, patch] = match;
	return {
		major: Number.parseInt(major, 10),
		minor: Number.parseInt(minor, 10),
		patch: Number.parseInt(patch, 10)
	};
}
function isAtLeast(version, minimum) {
	if (!version) return false;
	if (version.major !== minimum.major) return version.major > minimum.major;
	if (version.minor !== minimum.minor) return version.minor > minimum.minor;
	return version.patch >= minimum.patch;
}
function detectRuntime() {
	return {
		kind: process$1.versions?.node ? "node" : "unknown",
		version: process$1.versions?.node ?? null,
		execPath: process$1.execPath ?? null,
		pathEnv: process$1.env.PATH ?? "(not set)"
	};
}
function runtimeSatisfies(details) {
	const parsed = parseSemver(details.version);
	if (details.kind === "node") return isAtLeast(parsed, MIN_NODE);
	return false;
}
function isSupportedNodeVersion(version) {
	return isAtLeast(parseSemver(version), MIN_NODE);
}
function assertSupportedRuntime(runtime = defaultRuntime, details = detectRuntime()) {
	if (runtimeSatisfies(details)) return;
	const versionLabel = details.version ?? "unknown";
	const runtimeLabel = details.kind === "unknown" ? "unknown runtime" : `${details.kind} ${versionLabel}`;
	const execLabel = details.execPath ?? "unknown";
	runtime.error([
		"SKYKOI requires Node >=22.12.0.",
		`Detected: ${runtimeLabel} (exec: ${execLabel}).`,
		`PATH searched: ${details.pathEnv}`,
		"Install Node: https://nodejs.org/en/download",
		"Upgrade Node and re-run SKYKOI."
	].join("\n"));
	runtime.exit(1);
}

//#endregion
//#region src/daemon/program-args.ts
function isNodeRuntime(execPath) {
	const base = path.basename(execPath).toLowerCase();
	return base === "node" || base === "node.exe";
}
function isBunRuntime(execPath) {
	const base = path.basename(execPath).toLowerCase();
	return base === "bun" || base === "bun.exe";
}
async function resolveCliEntrypointPathForService() {
	const argv1 = process.argv[1];
	if (!argv1) throw new Error("Unable to resolve CLI entrypoint path");
	const normalized = path.resolve(argv1);
	const resolvedPath = await resolveRealpathSafe(normalized);
	if (/[/\\]dist[/\\].+\.(cjs|js|mjs)$/.test(resolvedPath)) {
		await fs.access(resolvedPath);
		if (/[/\\]dist[/\\].+\.(cjs|js|mjs)$/.test(normalized) && normalized !== resolvedPath) try {
			await fs.access(normalized);
			return normalized;
		} catch {}
		return resolvedPath;
	}
	const distCandidates = buildDistCandidates(resolvedPath, normalized);
	for (const candidate of distCandidates) try {
		await fs.access(candidate);
		return candidate;
	} catch {}
	throw new Error(`Cannot find built CLI at ${distCandidates.join(" or ")}. Run "pnpm build" first, or use dev mode.`);
}
async function resolveRealpathSafe(inputPath) {
	try {
		return await fs.realpath(inputPath);
	} catch {
		return inputPath;
	}
}
function buildDistCandidates(...inputs) {
	const candidates = [];
	const seen = /* @__PURE__ */ new Set();
	for (const inputPath of inputs) {
		if (!inputPath) continue;
		const baseDir = path.dirname(inputPath);
		appendDistCandidates(candidates, seen, path.resolve(baseDir, ".."));
		appendDistCandidates(candidates, seen, baseDir);
		appendNodeModulesBinCandidates(candidates, seen, inputPath);
	}
	return candidates;
}
function appendDistCandidates(candidates, seen, baseDir) {
	const distDir = path.resolve(baseDir, "dist");
	const distEntries = [
		path.join(distDir, "index.js"),
		path.join(distDir, "index.mjs"),
		path.join(distDir, "entry.js"),
		path.join(distDir, "entry.mjs")
	];
	for (const entry of distEntries) {
		if (seen.has(entry)) continue;
		seen.add(entry);
		candidates.push(entry);
	}
}
function appendNodeModulesBinCandidates(candidates, seen, inputPath) {
	const parts = inputPath.split(path.sep);
	const binIndex = parts.lastIndexOf(".bin");
	if (binIndex <= 0) return;
	if (parts[binIndex - 1] !== "node_modules") return;
	const binName = path.basename(inputPath);
	const nodeModulesDir = parts.slice(0, binIndex).join(path.sep);
	appendDistCandidates(candidates, seen, path.join(nodeModulesDir, binName));
}
function resolveRepoRootForDev() {
	const argv1 = process.argv[1];
	if (!argv1) throw new Error("Unable to resolve repo root");
	const parts = path.resolve(argv1).split(path.sep);
	const srcIndex = parts.lastIndexOf("src");
	if (srcIndex === -1) throw new Error("Dev mode requires running from repo (src/index.ts)");
	return parts.slice(0, srcIndex).join(path.sep);
}
async function resolveBunPath() {
	return await resolveBinaryPath("bun");
}
async function resolveNodePath() {
	return await resolveBinaryPath("node");
}
async function resolveBinaryPath(binary) {
	const { execSync } = await import("node:child_process");
	const cmd = process.platform === "win32" ? "where" : "which";
	try {
		const resolved = execSync(`${cmd} ${binary}`, { encoding: "utf8" }).trim().split(/\r?\n/)[0]?.trim();
		if (!resolved) throw new Error("empty");
		await fs.access(resolved);
		return resolved;
	} catch {
		if (binary === "bun") throw new Error("Bun not found in PATH. Install bun: https://bun.sh");
		throw new Error("Node not found in PATH. Install Node 22+.");
	}
}
async function resolveCliProgramArguments(params) {
	const execPath = process.execPath;
	const runtime = params.runtime ?? "auto";
	if (runtime === "node") return { programArguments: [
		params.nodePath ?? (isNodeRuntime(execPath) ? execPath : await resolveNodePath()),
		await resolveCliEntrypointPathForService(),
		...params.args
	] };
	if (runtime === "bun") {
		if (params.dev) {
			const repoRoot = resolveRepoRootForDev();
			const devCliPath = path.join(repoRoot, "src", "index.ts");
			await fs.access(devCliPath);
			return {
				programArguments: [
					isBunRuntime(execPath) ? execPath : await resolveBunPath(),
					devCliPath,
					...params.args
				],
				workingDirectory: repoRoot
			};
		}
		return { programArguments: [
			isBunRuntime(execPath) ? execPath : await resolveBunPath(),
			await resolveCliEntrypointPathForService(),
			...params.args
		] };
	}
	if (!params.dev) try {
		return { programArguments: [
			execPath,
			await resolveCliEntrypointPathForService(),
			...params.args
		] };
	} catch (error) {
		if (!isNodeRuntime(execPath)) return { programArguments: [execPath, ...params.args] };
		throw error;
	}
	const repoRoot = resolveRepoRootForDev();
	const devCliPath = path.join(repoRoot, "src", "index.ts");
	await fs.access(devCliPath);
	if (isBunRuntime(execPath)) return {
		programArguments: [
			execPath,
			devCliPath,
			...params.args
		],
		workingDirectory: repoRoot
	};
	return {
		programArguments: [
			await resolveBunPath(),
			devCliPath,
			...params.args
		],
		workingDirectory: repoRoot
	};
}
async function resolveGatewayProgramArguments(params) {
	return resolveCliProgramArguments({
		args: [
			"gateway",
			"--port",
			String(params.port)
		],
		dev: params.dev,
		runtime: params.runtime,
		nodePath: params.nodePath
	});
}
async function resolveNodeProgramArguments(params) {
	const args = [
		"node",
		"run",
		"--host",
		params.host,
		"--port",
		String(params.port)
	];
	if (params.tls || params.tlsFingerprint) args.push("--tls");
	if (params.tlsFingerprint) args.push("--tls-fingerprint", params.tlsFingerprint);
	if (params.nodeId) args.push("--node-id", params.nodeId);
	if (params.displayName) args.push("--display-name", params.displayName);
	return resolveCliProgramArguments({
		args,
		dev: params.dev,
		runtime: params.runtime,
		nodePath: params.nodePath
	});
}

//#endregion
//#region src/daemon/runtime-paths.ts
const VERSION_MANAGER_MARKERS = [
	"/.nvm/",
	"/.fnm/",
	"/.volta/",
	"/.asdf/",
	"/.n/",
	"/.nodenv/",
	"/.nodebrew/",
	"/nvs/"
];
function getPathModule(platform) {
	return platform === "win32" ? path.win32 : path.posix;
}
function normalizeForCompare(input, platform) {
	const normalized = getPathModule(platform).normalize(input).replaceAll("\\", "/");
	if (platform === "win32") return normalized.toLowerCase();
	return normalized;
}
function buildSystemNodeCandidates(env, platform) {
	if (platform === "darwin") return [
		"/opt/homebrew/bin/node",
		"/usr/local/bin/node",
		"/usr/bin/node"
	];
	if (platform === "linux") return ["/usr/local/bin/node", "/usr/bin/node"];
	if (platform === "win32") {
		const pathModule = getPathModule(platform);
		const programFiles = env.ProgramFiles ?? "C:\\Program Files";
		const programFilesX86 = env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
		return [pathModule.join(programFiles, "nodejs", "node.exe"), pathModule.join(programFilesX86, "nodejs", "node.exe")];
	}
	return [];
}
const execFileAsync = promisify(execFile);
async function resolveNodeVersion(nodePath, execFileImpl) {
	try {
		const { stdout } = await execFileImpl(nodePath, ["-p", "process.versions.node"], { encoding: "utf8" });
		const value = stdout.trim();
		return value ? value : null;
	} catch {
		return null;
	}
}
function isVersionManagedNodePath(nodePath, platform = process.platform) {
	const normalized = normalizeForCompare(nodePath, platform);
	return VERSION_MANAGER_MARKERS.some((marker) => normalized.includes(marker));
}
function isSystemNodePath(nodePath, env = process.env, platform = process.platform) {
	const normalized = normalizeForCompare(nodePath, platform);
	return buildSystemNodeCandidates(env, platform).some((candidate) => {
		return normalized === normalizeForCompare(candidate, platform);
	});
}
async function resolveSystemNodePath(env = process.env, platform = process.platform) {
	const candidates = buildSystemNodeCandidates(env, platform);
	for (const candidate of candidates) try {
		await fs.access(candidate);
		return candidate;
	} catch {}
	return null;
}
async function resolveSystemNodeInfo(params) {
	const systemNode = await resolveSystemNodePath(params.env ?? process.env, params.platform ?? process.platform);
	if (!systemNode) return null;
	const version = await resolveNodeVersion(systemNode, params.execFile ?? execFileAsync);
	return {
		path: systemNode,
		version,
		supported: isSupportedNodeVersion(version)
	};
}
function renderSystemNodeWarning(systemNode, selectedNodePath) {
	if (!systemNode || systemNode.supported) return null;
	const versionLabel = systemNode.version ?? "unknown";
	const selectedLabel = selectedNodePath ? ` Using ${selectedNodePath} for the daemon.` : "";
	return `System Node ${versionLabel} at ${systemNode.path} is below the required Node 22+.${selectedLabel} Install Node 22+ from nodejs.org or Homebrew.`;
}
async function resolvePreferredNodePath(params) {
	if (params.runtime !== "node") return;
	const systemNode = await resolveSystemNodeInfo(params);
	if (!systemNode?.supported) return;
	return systemNode.path;
}

//#endregion
//#region src/daemon/service-env.ts
function resolveSystemPathDirs(platform) {
	if (platform === "darwin") return [
		"/opt/homebrew/bin",
		"/usr/local/bin",
		"/usr/bin",
		"/bin"
	];
	if (platform === "linux") return [
		"/usr/local/bin",
		"/usr/bin",
		"/bin"
	];
	return [];
}
/**
* Resolve common user bin directories for Linux.
* These are paths where npm global installs and node version managers typically place binaries.
*/
function resolveLinuxUserBinDirs(home, env) {
	if (!home) return [];
	const dirs = [];
	const add = (dir) => {
		if (dir) dirs.push(dir);
	};
	const appendSubdir = (base, subdir) => {
		if (!base) return;
		return base.endsWith(`/${subdir}`) ? base : path.posix.join(base, subdir);
	};
	add(env?.PNPM_HOME);
	add(appendSubdir(env?.NPM_CONFIG_PREFIX, "bin"));
	add(appendSubdir(env?.BUN_INSTALL, "bin"));
	add(appendSubdir(env?.VOLTA_HOME, "bin"));
	add(appendSubdir(env?.ASDF_DATA_DIR, "shims"));
	add(appendSubdir(env?.NVM_DIR, "current/bin"));
	add(appendSubdir(env?.FNM_DIR, "current/bin"));
	dirs.push(`${home}/.local/bin`);
	dirs.push(`${home}/.npm-global/bin`);
	dirs.push(`${home}/bin`);
	dirs.push(`${home}/.nvm/current/bin`);
	dirs.push(`${home}/.fnm/current/bin`);
	dirs.push(`${home}/.volta/bin`);
	dirs.push(`${home}/.asdf/shims`);
	dirs.push(`${home}/.local/share/pnpm`);
	dirs.push(`${home}/.bun/bin`);
	return dirs;
}
function getMinimalServicePathParts(options = {}) {
	const platform = options.platform ?? process.platform;
	if (platform === "win32") return [];
	const parts = [];
	const extraDirs = options.extraDirs ?? [];
	const systemDirs = resolveSystemPathDirs(platform);
	const linuxUserDirs = platform === "linux" ? resolveLinuxUserBinDirs(options.home, options.env) : [];
	const add = (dir) => {
		if (!dir) return;
		if (!parts.includes(dir)) parts.push(dir);
	};
	for (const dir of extraDirs) add(dir);
	for (const dir of linuxUserDirs) add(dir);
	for (const dir of systemDirs) add(dir);
	return parts;
}
function getMinimalServicePathPartsFromEnv(options = {}) {
	const env = options.env ?? process.env;
	return getMinimalServicePathParts({
		...options,
		home: options.home ?? env.HOME,
		env
	});
}
function buildMinimalServicePath(options = {}) {
	const env = options.env ?? process.env;
	if ((options.platform ?? process.platform) === "win32") return env.PATH ?? "";
	return getMinimalServicePathPartsFromEnv({
		...options,
		env
	}).join(path.posix.delimiter);
}
function buildServiceEnvironment(params) {
	const { env, port, token, launchdLabel } = params;
	const profile = env.SKYKOI_PROFILE;
	const resolvedLaunchdLabel = launchdLabel || (process.platform === "darwin" ? resolveGatewayLaunchAgentLabel(profile) : void 0);
	const systemdUnit = `${resolveGatewaySystemdServiceName(profile)}.service`;
	const stateDir = env.SKYKOI_STATE_DIR;
	const configPath = env.SKYKOI_CONFIG_PATH;
	return {
		HOME: env.HOME,
		PATH: buildMinimalServicePath({ env }),
		SKYKOI_PROFILE: profile,
		SKYKOI_STATE_DIR: stateDir,
		SKYKOI_CONFIG_PATH: configPath,
		SKYKOI_HOST: env.SKYKOI_HOST,
		SKYKOI_PORT: env.SKYKOI_PORT,
		SKYKOI_GATEWAY_PORT: String(port),
		SKYKOI_GATEWAY_TOKEN: token,
		SKYKOI_LAUNCHD_LABEL: resolvedLaunchdLabel,
		SKYKOI_SYSTEMD_UNIT: systemdUnit,
		SKYKOI_SERVICE_MARKER: GATEWAY_SERVICE_MARKER,
		SKYKOI_SERVICE_KIND: GATEWAY_SERVICE_KIND,
		SKYKOI_SERVICE_VERSION: VERSION,
		SKYKOI_APP_URL: env.SKYKOI_APP_URL,
		SKYKOI_KOI_ID: env.SKYKOI_KOI_ID
	};
}
function buildNodeServiceEnvironment(params) {
	const { env } = params;
	const stateDir = env.SKYKOI_STATE_DIR;
	const configPath = env.SKYKOI_CONFIG_PATH;
	return {
		HOME: env.HOME,
		PATH: buildMinimalServicePath({ env }),
		SKYKOI_GATEWAY_TOKEN: env.SKYKOI_GATEWAY_TOKEN,
		SKYKOI_STATE_DIR: stateDir,
		SKYKOI_CONFIG_PATH: configPath,
		SKYKOI_LAUNCHD_LABEL: resolveNodeLaunchAgentLabel(),
		SKYKOI_SYSTEMD_UNIT: resolveNodeSystemdServiceName(),
		SKYKOI_WINDOWS_TASK_NAME: resolveNodeWindowsTaskName(),
		SKYKOI_TASK_SCRIPT_NAME: NODE_WINDOWS_TASK_SCRIPT_NAME,
		SKYKOI_LOG_PREFIX: "node",
		SKYKOI_SERVICE_MARKER: NODE_SERVICE_MARKER,
		SKYKOI_SERVICE_KIND: NODE_SERVICE_KIND,
		SKYKOI_SERVICE_VERSION: VERSION,
		SKYKOI_APP_URL: env.SKYKOI_APP_URL,
		SKYKOI_KOI_ID: env.SKYKOI_KOI_ID
	};
}

//#endregion
//#region src/commands/daemon-install-helpers.ts
function resolveGatewayDevMode(argv = process.argv) {
	const normalizedEntry = argv[1]?.replaceAll("\\", "/");
	return Boolean(normalizedEntry?.includes("/src/") && normalizedEntry.endsWith(".ts"));
}
async function buildGatewayInstallPlan(params) {
	const devMode = params.devMode ?? resolveGatewayDevMode();
	const nodePath = params.nodePath ?? await resolvePreferredNodePath({
		env: params.env,
		runtime: params.runtime
	});
	const { programArguments, workingDirectory } = await resolveGatewayProgramArguments({
		port: params.port,
		dev: devMode,
		runtime: params.runtime,
		nodePath
	});
	if (params.runtime === "node") {
		const warning = renderSystemNodeWarning(await resolveSystemNodeInfo({ env: params.env }), programArguments[0]);
		if (warning) params.warn?.(warning, "Gateway runtime");
	}
	const serviceEnvironment = buildServiceEnvironment({
		env: params.env,
		port: params.port,
		token: params.token,
		launchdLabel: process.platform === "darwin" ? resolveGatewayLaunchAgentLabel(params.env.SKYKOI_PROFILE) : void 0
	});
	const environment = { ...collectConfigEnvVars(params.config) };
	Object.assign(environment, serviceEnvironment);
	return {
		programArguments,
		workingDirectory,
		environment
	};
}
function gatewayInstallErrorHint(platform = process.platform) {
	return platform === "win32" ? "Tip: rerun from an elevated PowerShell (Start → type PowerShell → right-click → Run as administrator) or skip service install." : `Tip: rerun \`${formatCliCommand("skykoi gateway install")}\` after fixing the error.`;
}

//#endregion
//#region src/commands/daemon-runtime.ts
const DEFAULT_GATEWAY_DAEMON_RUNTIME = "node";
const GATEWAY_DAEMON_RUNTIME_OPTIONS = [{
	value: "node",
	label: "Node (recommended)",
	hint: "Required for WhatsApp + Telegram. Bun can corrupt memory on reconnect."
}];
function isGatewayDaemonRuntime(value) {
	return value === "node" || value === "bun";
}

//#endregion
export { parseSemver as _, gatewayInstallErrorHint as a, getMinimalServicePathPartsFromEnv as c, renderSystemNodeWarning as d, resolvePreferredNodePath as f, assertSupportedRuntime as g, resolveNodeProgramArguments as h, buildGatewayInstallPlan as i, isSystemNodePath as l, resolveSystemNodePath as m, GATEWAY_DAEMON_RUNTIME_OPTIONS as n, resolveGatewayDevMode as o, resolveSystemNodeInfo as p, isGatewayDaemonRuntime as r, buildNodeServiceEnvironment as s, DEFAULT_GATEWAY_DAEMON_RUNTIME as t, isVersionManagedNodePath as u };