import { t as runCommandWithTimeout } from "./exec-XH65-wH1.js";
import { o as trimLogTail } from "./restart-sentinel-BqQzvk3k.js";
import { r as parseSemver } from "./runtime-guard-Btp4Sks0.js";
import { n as resolveControlUiDistIndexHealth, r as resolveControlUiDistIndexPathForRoot } from "./control-ui-assets-C6cSlkkc.js";
import path from "node:path";
import os from "node:os";
import fs from "node:fs/promises";

//#region src/infra/update-channels.ts
const DEFAULT_PACKAGE_CHANNEL = "stable";
const DEFAULT_GIT_CHANNEL = "dev";
const DEV_BRANCH = "main";
function normalizeUpdateChannel(value) {
	if (!value) return null;
	const normalized = value.trim().toLowerCase();
	if (normalized === "stable" || normalized === "beta" || normalized === "dev") return normalized;
	return null;
}
function channelToNpmTag(channel) {
	if (channel === "beta") return "beta";
	if (channel === "dev") return "dev";
	return "latest";
}
function isBetaTag(tag) {
	return tag.toLowerCase().includes("-beta");
}
function isStableTag(tag) {
	return !isBetaTag(tag);
}
function resolveEffectiveUpdateChannel(params) {
	if (params.configChannel) return {
		channel: params.configChannel,
		source: "config"
	};
	if (params.installKind === "git") {
		const tag = params.git?.tag;
		if (tag) return {
			channel: isBetaTag(tag) ? "beta" : "stable",
			source: "git-tag"
		};
		const branch = params.git?.branch;
		if (branch && branch !== "HEAD") return {
			channel: "dev",
			source: "git-branch"
		};
		return {
			channel: DEFAULT_GIT_CHANNEL,
			source: "default"
		};
	}
	if (params.installKind === "package") return {
		channel: DEFAULT_PACKAGE_CHANNEL,
		source: "default"
	};
	return {
		channel: DEFAULT_PACKAGE_CHANNEL,
		source: "default"
	};
}
function formatUpdateChannelLabel(params) {
	if (params.source === "config") return `${params.channel} (config)`;
	if (params.source === "git-tag") return params.gitTag ? `${params.channel} (${params.gitTag})` : `${params.channel} (tag)`;
	if (params.source === "git-branch") return params.gitBranch ? `${params.channel} (${params.gitBranch})` : `${params.channel} (branch)`;
	return `${params.channel} (default)`;
}

//#endregion
//#region src/infra/update-check.ts
async function exists(p) {
	try {
		await fs.access(p);
		return true;
	} catch {
		return false;
	}
}
async function detectPackageManager$1(root) {
	try {
		const raw = await fs.readFile(path.join(root, "package.json"), "utf-8");
		const pm = JSON.parse(raw)?.packageManager?.split("@")[0]?.trim();
		if (pm === "pnpm" || pm === "bun" || pm === "npm") return pm;
	} catch {}
	const files = await fs.readdir(root).catch(() => []);
	if (files.includes("pnpm-lock.yaml")) return "pnpm";
	if (files.includes("bun.lockb")) return "bun";
	if (files.includes("package-lock.json")) return "npm";
	return "unknown";
}
async function detectGitRoot(root) {
	const res = await runCommandWithTimeout([
		"git",
		"-C",
		root,
		"rev-parse",
		"--show-toplevel"
	], { timeoutMs: 4e3 }).catch(() => null);
	if (!res || res.code !== 0) return null;
	const top = res.stdout.trim();
	return top ? path.resolve(top) : null;
}
async function checkGitUpdateStatus(params) {
	const timeoutMs = params.timeoutMs ?? 6e3;
	const root = path.resolve(params.root);
	const base = {
		root,
		sha: null,
		tag: null,
		branch: null,
		upstream: null,
		dirty: null,
		ahead: null,
		behind: null,
		fetchOk: null
	};
	const branchRes = await runCommandWithTimeout([
		"git",
		"-C",
		root,
		"rev-parse",
		"--abbrev-ref",
		"HEAD"
	], { timeoutMs }).catch(() => null);
	if (!branchRes || branchRes.code !== 0) return {
		...base,
		error: branchRes?.stderr?.trim() || "git unavailable"
	};
	const branch = branchRes.stdout.trim() || null;
	const shaRes = await runCommandWithTimeout([
		"git",
		"-C",
		root,
		"rev-parse",
		"HEAD"
	], { timeoutMs }).catch(() => null);
	const sha = shaRes && shaRes.code === 0 ? shaRes.stdout.trim() : null;
	const tagRes = await runCommandWithTimeout([
		"git",
		"-C",
		root,
		"describe",
		"--tags",
		"--exact-match"
	], { timeoutMs }).catch(() => null);
	const tag = tagRes && tagRes.code === 0 ? tagRes.stdout.trim() : null;
	const upstreamRes = await runCommandWithTimeout([
		"git",
		"-C",
		root,
		"rev-parse",
		"--abbrev-ref",
		"@{upstream}"
	], { timeoutMs }).catch(() => null);
	const upstream = upstreamRes && upstreamRes.code === 0 ? upstreamRes.stdout.trim() : null;
	const dirtyRes = await runCommandWithTimeout([
		"git",
		"-C",
		root,
		"status",
		"--porcelain",
		"--",
		":!dist/control-ui/"
	], { timeoutMs }).catch(() => null);
	const dirty = dirtyRes && dirtyRes.code === 0 ? dirtyRes.stdout.trim().length > 0 : null;
	const fetchOk = params.fetch ? await runCommandWithTimeout([
		"git",
		"-C",
		root,
		"fetch",
		"--quiet",
		"--prune"
	], { timeoutMs }).then((r) => r.code === 0).catch(() => false) : null;
	const counts = upstream && upstream.length > 0 ? await runCommandWithTimeout([
		"git",
		"-C",
		root,
		"rev-list",
		"--left-right",
		"--count",
		`HEAD...${upstream}`
	], { timeoutMs }).catch(() => null) : null;
	const parseCounts = (raw) => {
		const parts = raw.trim().split(/\s+/);
		if (parts.length < 2) return null;
		const ahead = Number.parseInt(parts[0] ?? "", 10);
		const behind = Number.parseInt(parts[1] ?? "", 10);
		if (!Number.isFinite(ahead) || !Number.isFinite(behind)) return null;
		return {
			ahead,
			behind
		};
	};
	const parsed = counts && counts.code === 0 ? parseCounts(counts.stdout) : null;
	return {
		root,
		sha,
		tag,
		branch,
		upstream,
		dirty,
		ahead: parsed?.ahead ?? null,
		behind: parsed?.behind ?? null,
		fetchOk
	};
}
async function statMtimeMs(p) {
	try {
		return (await fs.stat(p)).mtimeMs;
	} catch {
		return null;
	}
}
function resolveDepsMarker(params) {
	const root = params.root;
	if (params.manager === "pnpm") return {
		lockfilePath: path.join(root, "pnpm-lock.yaml"),
		markerPath: path.join(root, "node_modules", ".modules.yaml")
	};
	if (params.manager === "bun") return {
		lockfilePath: path.join(root, "bun.lockb"),
		markerPath: path.join(root, "node_modules")
	};
	if (params.manager === "npm") return {
		lockfilePath: path.join(root, "package-lock.json"),
		markerPath: path.join(root, "node_modules")
	};
	return {
		lockfilePath: null,
		markerPath: null
	};
}
async function checkDepsStatus(params) {
	const { lockfilePath, markerPath } = resolveDepsMarker({
		root: path.resolve(params.root),
		manager: params.manager
	});
	if (!lockfilePath || !markerPath) return {
		manager: params.manager,
		status: "unknown",
		lockfilePath,
		markerPath,
		reason: "unknown package manager"
	};
	const lockExists = await exists(lockfilePath);
	const markerExists = await exists(markerPath);
	if (!lockExists) return {
		manager: params.manager,
		status: "unknown",
		lockfilePath,
		markerPath,
		reason: "lockfile missing"
	};
	if (!markerExists) return {
		manager: params.manager,
		status: "missing",
		lockfilePath,
		markerPath,
		reason: "node_modules marker missing"
	};
	const lockMtime = await statMtimeMs(lockfilePath);
	const markerMtime = await statMtimeMs(markerPath);
	if (!lockMtime || !markerMtime) return {
		manager: params.manager,
		status: "unknown",
		lockfilePath,
		markerPath
	};
	if (lockMtime > markerMtime + 1e3) return {
		manager: params.manager,
		status: "stale",
		lockfilePath,
		markerPath,
		reason: "lockfile newer than install marker"
	};
	return {
		manager: params.manager,
		status: "ok",
		lockfilePath,
		markerPath
	};
}
async function fetchWithTimeout(url, timeoutMs) {
	const ctrl = new AbortController();
	const t = setTimeout(() => ctrl.abort(), Math.max(250, timeoutMs));
	try {
		return await fetch(url, { signal: ctrl.signal });
	} finally {
		clearTimeout(t);
	}
}
async function fetchNpmLatestVersion(params) {
	const res = await fetchNpmTagVersion({
		tag: "latest",
		timeoutMs: params?.timeoutMs
	});
	return {
		latestVersion: res.version,
		error: res.error
	};
}
async function fetchNpmTagVersion(params) {
	const timeoutMs = params?.timeoutMs ?? 3500;
	const tag = params.tag;
	try {
		const res = await fetchWithTimeout(`https://registry.npmjs.org/SKYKOI/${encodeURIComponent(tag)}`, timeoutMs);
		if (!res.ok) return {
			tag,
			version: null,
			error: `HTTP ${res.status}`
		};
		const json = await res.json();
		return {
			tag,
			version: typeof json?.version === "string" ? json.version : null
		};
	} catch (err) {
		return {
			tag,
			version: null,
			error: String(err)
		};
	}
}
async function resolveNpmChannelTag(params) {
	const channelTag = channelToNpmTag(params.channel);
	const channelStatus = await fetchNpmTagVersion({
		tag: channelTag,
		timeoutMs: params.timeoutMs
	});
	if (params.channel !== "beta") return {
		tag: channelTag,
		version: channelStatus.version
	};
	const latestStatus = await fetchNpmTagVersion({
		tag: "latest",
		timeoutMs: params.timeoutMs
	});
	if (!latestStatus.version) return {
		tag: channelTag,
		version: channelStatus.version
	};
	if (!channelStatus.version) return {
		tag: "latest",
		version: latestStatus.version
	};
	const cmp = compareSemverStrings(channelStatus.version, latestStatus.version);
	if (cmp != null && cmp < 0) return {
		tag: "latest",
		version: latestStatus.version
	};
	return {
		tag: channelTag,
		version: channelStatus.version
	};
}
function compareSemverStrings(a, b) {
	const pa = parseSemver(a);
	const pb = parseSemver(b);
	if (!pa || !pb) return null;
	if (pa.major !== pb.major) return pa.major < pb.major ? -1 : 1;
	if (pa.minor !== pb.minor) return pa.minor < pb.minor ? -1 : 1;
	if (pa.patch !== pb.patch) return pa.patch < pb.patch ? -1 : 1;
	return 0;
}
async function checkUpdateStatus(params) {
	const timeoutMs = params.timeoutMs ?? 6e3;
	const root = params.root ? path.resolve(params.root) : null;
	if (!root) return {
		root: null,
		installKind: "unknown",
		packageManager: "unknown",
		registry: params.includeRegistry ? await fetchNpmLatestVersion({ timeoutMs }) : void 0
	};
	const pm = await detectPackageManager$1(root);
	const gitRoot = await detectGitRoot(root);
	const isGit = gitRoot && path.resolve(gitRoot) === root;
	return {
		root,
		installKind: isGit ? "git" : "package",
		packageManager: pm,
		git: isGit ? await checkGitUpdateStatus({
			root,
			timeoutMs,
			fetch: Boolean(params.fetchGit)
		}) : void 0,
		deps: await checkDepsStatus({
			root,
			manager: pm
		}),
		registry: params.includeRegistry ? await fetchNpmLatestVersion({ timeoutMs }) : void 0
	};
}

//#endregion
//#region src/infra/update-global.ts
const PRIMARY_PACKAGE_NAME = "skykoi";
const ALL_PACKAGE_NAMES = [PRIMARY_PACKAGE_NAME];
const GLOBAL_RENAME_PREFIX = ".";
async function pathExists(targetPath) {
	try {
		await fs.access(targetPath);
		return true;
	} catch {
		return false;
	}
}
async function tryRealpath(targetPath) {
	try {
		return await fs.realpath(targetPath);
	} catch {
		return path.resolve(targetPath);
	}
}
function resolveBunGlobalRoot() {
	const bunInstall = process.env.BUN_INSTALL?.trim() || path.join(os.homedir(), ".bun");
	return path.join(bunInstall, "install", "global", "node_modules");
}
async function resolveGlobalRoot(manager, runCommand, timeoutMs) {
	if (manager === "bun") return resolveBunGlobalRoot();
	const res = await runCommand(manager === "pnpm" ? [
		"pnpm",
		"root",
		"-g"
	] : [
		"npm",
		"root",
		"-g"
	], { timeoutMs }).catch(() => null);
	if (!res || res.code !== 0) return null;
	return res.stdout.trim() || null;
}
async function resolveGlobalPackageRoot(manager, runCommand, timeoutMs) {
	const root = await resolveGlobalRoot(manager, runCommand, timeoutMs);
	if (!root) return null;
	return path.join(root, PRIMARY_PACKAGE_NAME);
}
async function detectGlobalInstallManagerForRoot(runCommand, pkgRoot, timeoutMs) {
	const pkgReal = await tryRealpath(pkgRoot);
	for (const { manager, argv } of [{
		manager: "npm",
		argv: [
			"npm",
			"root",
			"-g"
		]
	}, {
		manager: "pnpm",
		argv: [
			"pnpm",
			"root",
			"-g"
		]
	}]) {
		const res = await runCommand(argv, { timeoutMs }).catch(() => null);
		if (!res || res.code !== 0) continue;
		const globalRoot = res.stdout.trim();
		if (!globalRoot) continue;
		const globalReal = await tryRealpath(globalRoot);
		for (const name of ALL_PACKAGE_NAMES) {
			const expected = path.join(globalReal, name);
			if (path.resolve(expected) === path.resolve(pkgReal)) return manager;
		}
	}
	const bunGlobalReal = await tryRealpath(resolveBunGlobalRoot());
	for (const name of ALL_PACKAGE_NAMES) {
		const bunExpected = path.join(bunGlobalReal, name);
		if (path.resolve(bunExpected) === path.resolve(pkgReal)) return "bun";
	}
	return null;
}
async function detectGlobalInstallManagerByPresence(runCommand, timeoutMs) {
	for (const manager of ["npm", "pnpm"]) {
		const root = await resolveGlobalRoot(manager, runCommand, timeoutMs);
		if (!root) continue;
		for (const name of ALL_PACKAGE_NAMES) if (await pathExists(path.join(root, name))) return manager;
	}
	const bunRoot = resolveBunGlobalRoot();
	for (const name of ALL_PACKAGE_NAMES) if (await pathExists(path.join(bunRoot, name))) return "bun";
	return null;
}
function globalInstallArgs(manager, spec) {
	if (manager === "pnpm") return [
		"pnpm",
		"add",
		"-g",
		spec,
		"--reporter=silent"
	];
	if (manager === "bun") return [
		"bun",
		"add",
		"-g",
		spec,
		"--silent"
	];
	return [
		"npm",
		"i",
		"-g",
		spec,
		"--no-fund",
		"--no-audit",
		"--loglevel=error"
	];
}
async function cleanupGlobalRenameDirs(params) {
	const removed = [];
	const root = params.globalRoot.trim();
	const name = params.packageName.trim();
	if (!root || !name) return { removed };
	const prefix = `${GLOBAL_RENAME_PREFIX}${name}-`;
	let entries = [];
	try {
		entries = await fs.readdir(root);
	} catch {
		return { removed };
	}
	for (const entry of entries) {
		if (!entry.startsWith(prefix)) continue;
		const target = path.join(root, entry);
		try {
			if (!(await fs.lstat(target)).isDirectory()) continue;
			await fs.rm(target, {
				recursive: true,
				force: true
			});
			removed.push(entry);
		} catch {}
	}
	return { removed };
}

//#endregion
//#region src/infra/update-runner.ts
/**
* Update runner — handles self-update of the SKYKOI runtime and dependencies.
*
* Manages npm/git-based updates: checks for new versions, downloads updates,
* applies them, and triggers gateway restart. Supports both npm registry updates
* and git-based updates for development installs. Includes rollback on failure.
*
* @module
*/
const DEFAULT_TIMEOUT_MS = 20 * 6e4;
const MAX_LOG_CHARS = 8e3;
const PREFLIGHT_MAX_COMMITS = 10;
const START_DIRS = [
	"cwd",
	"argv1",
	"process"
];
const DEFAULT_PACKAGE_NAME = "skykoi";
const CORE_PACKAGE_NAMES = new Set([DEFAULT_PACKAGE_NAME]);
function normalizeDir(value) {
	if (!value) return null;
	const trimmed = value.trim();
	if (!trimmed) return null;
	return path.resolve(trimmed);
}
function resolveNodeModulesBinPackageRoot(argv1) {
	const normalized = path.resolve(argv1);
	const parts = normalized.split(path.sep);
	const binIndex = parts.lastIndexOf(".bin");
	if (binIndex <= 0) return null;
	if (parts[binIndex - 1] !== "node_modules") return null;
	const binName = path.basename(normalized);
	const nodeModulesDir = parts.slice(0, binIndex).join(path.sep);
	return path.join(nodeModulesDir, binName);
}
function buildStartDirs(opts) {
	const dirs = [];
	const cwd = normalizeDir(opts.cwd);
	if (cwd) dirs.push(cwd);
	const argv1 = normalizeDir(opts.argv1);
	if (argv1) {
		dirs.push(path.dirname(argv1));
		const packageRoot = resolveNodeModulesBinPackageRoot(argv1);
		if (packageRoot) dirs.push(packageRoot);
	}
	const proc = normalizeDir(process.cwd());
	if (proc) dirs.push(proc);
	return Array.from(new Set(dirs));
}
async function readPackageVersion(root) {
	try {
		const raw = await fs.readFile(path.join(root, "package.json"), "utf-8");
		const parsed = JSON.parse(raw);
		return typeof parsed?.version === "string" ? parsed.version : null;
	} catch {
		return null;
	}
}
async function readPackageName(root) {
	try {
		const raw = await fs.readFile(path.join(root, "package.json"), "utf-8");
		const name = JSON.parse(raw)?.name?.trim();
		return name ? name : null;
	} catch {
		return null;
	}
}
async function readBranchName(runCommand, root, timeoutMs) {
	const res = await runCommand([
		"git",
		"-C",
		root,
		"rev-parse",
		"--abbrev-ref",
		"HEAD"
	], { timeoutMs }).catch(() => null);
	if (!res || res.code !== 0) return null;
	return res.stdout.trim() || null;
}
async function listGitTags(runCommand, root, timeoutMs, pattern = "v*") {
	const res = await runCommand([
		"git",
		"-C",
		root,
		"tag",
		"--list",
		pattern,
		"--sort=-v:refname"
	], { timeoutMs }).catch(() => null);
	if (!res || res.code !== 0) return [];
	return res.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
}
async function resolveChannelTag(runCommand, root, timeoutMs, channel) {
	const tags = await listGitTags(runCommand, root, timeoutMs);
	if (channel === "beta") {
		const betaTag = tags.find((tag) => isBetaTag(tag)) ?? null;
		const stableTag = tags.find((tag) => isStableTag(tag)) ?? null;
		if (!betaTag) return stableTag;
		if (!stableTag) return betaTag;
		const cmp = compareSemverStrings(betaTag, stableTag);
		if (cmp != null && cmp < 0) return stableTag;
		return betaTag;
	}
	return tags.find((tag) => isStableTag(tag)) ?? null;
}
async function resolveGitRoot(runCommand, candidates, timeoutMs) {
	for (const dir of candidates) {
		const res = await runCommand([
			"git",
			"-C",
			dir,
			"rev-parse",
			"--show-toplevel"
		], { timeoutMs });
		if (res.code === 0) {
			const root = res.stdout.trim();
			if (root) return root;
		}
	}
	return null;
}
async function findPackageRoot(candidates) {
	for (const dir of candidates) {
		let current = dir;
		for (let i = 0; i < 12; i += 1) {
			const pkgPath = path.join(current, "package.json");
			try {
				const raw = await fs.readFile(pkgPath, "utf-8");
				const name = JSON.parse(raw)?.name?.trim();
				if (name && CORE_PACKAGE_NAMES.has(name)) return current;
			} catch {}
			const parent = path.dirname(current);
			if (parent === current) break;
			current = parent;
		}
	}
	return null;
}
async function detectPackageManager(root) {
	try {
		const raw = await fs.readFile(path.join(root, "package.json"), "utf-8");
		const pm = JSON.parse(raw)?.packageManager?.split("@")[0]?.trim();
		if (pm === "pnpm" || pm === "bun" || pm === "npm") return pm;
	} catch {}
	const files = await fs.readdir(root).catch(() => []);
	if (files.includes("pnpm-lock.yaml")) return "pnpm";
	if (files.includes("bun.lockb")) return "bun";
	if (files.includes("package-lock.json")) return "npm";
	return "npm";
}
async function runStep(opts) {
	const { runCommand, name, argv, cwd, timeoutMs, env, progress, stepIndex, totalSteps } = opts;
	const command = argv.join(" ");
	const stepInfo = {
		name,
		command,
		index: stepIndex,
		total: totalSteps
	};
	progress?.onStepStart?.(stepInfo);
	const started = Date.now();
	const result = await runCommand(argv, {
		cwd,
		timeoutMs,
		env
	});
	const durationMs = Date.now() - started;
	const stderrTail = trimLogTail(result.stderr, MAX_LOG_CHARS);
	progress?.onStepComplete?.({
		...stepInfo,
		durationMs,
		exitCode: result.code,
		stderrTail
	});
	return {
		name,
		command,
		cwd,
		durationMs,
		exitCode: result.code,
		stdoutTail: trimLogTail(result.stdout, MAX_LOG_CHARS),
		stderrTail: trimLogTail(result.stderr, MAX_LOG_CHARS)
	};
}
function managerScriptArgs(manager, script, args = []) {
	if (manager === "pnpm") return [
		"pnpm",
		script,
		...args
	];
	if (manager === "bun") return [
		"bun",
		"run",
		script,
		...args
	];
	if (args.length > 0) return [
		"npm",
		"run",
		script,
		"--",
		...args
	];
	return [
		"npm",
		"run",
		script
	];
}
function managerInstallArgs(manager) {
	if (manager === "pnpm") return ["pnpm", "install"];
	if (manager === "bun") return ["bun", "install"];
	return ["npm", "install"];
}
function normalizeTag(tag) {
	const trimmed = tag?.trim();
	if (!trimmed) return "latest";
	if (trimmed.startsWith("SKYKOI@")) return trimmed.slice(7);
	if (trimmed.startsWith(`${DEFAULT_PACKAGE_NAME}@`)) return trimmed.slice(`${DEFAULT_PACKAGE_NAME}@`.length);
	return trimmed;
}
async function runGatewayUpdate(opts = {}) {
	const startedAt = Date.now();
	const runCommand = opts.runCommand ?? (async (argv, options) => {
		const res = await runCommandWithTimeout(argv, options);
		return {
			stdout: res.stdout,
			stderr: res.stderr,
			code: res.code
		};
	});
	const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
	const progress = opts.progress;
	const steps = [];
	const candidates = buildStartDirs(opts);
	let stepIndex = 0;
	let gitTotalSteps = 0;
	const step = (name, argv, cwd, env) => {
		const currentIndex = stepIndex;
		stepIndex += 1;
		return {
			runCommand,
			name,
			argv,
			cwd,
			timeoutMs,
			env,
			progress,
			stepIndex: currentIndex,
			totalSteps: gitTotalSteps
		};
	};
	const pkgRoot = await findPackageRoot(candidates);
	let gitRoot = await resolveGitRoot(runCommand, candidates, timeoutMs);
	if (gitRoot && pkgRoot && path.resolve(gitRoot) !== path.resolve(pkgRoot)) gitRoot = null;
	if (gitRoot && !pkgRoot) return {
		status: "error",
		mode: "unknown",
		root: gitRoot,
		reason: "not-SKYKOI-root",
		steps: [],
		durationMs: Date.now() - startedAt
	};
	if (gitRoot && pkgRoot && path.resolve(gitRoot) === path.resolve(pkgRoot)) {
		const beforeSha = (await runCommand([
			"git",
			"-C",
			gitRoot,
			"rev-parse",
			"HEAD"
		], {
			cwd: gitRoot,
			timeoutMs
		})).stdout.trim() || null;
		const beforeVersion = await readPackageVersion(gitRoot);
		const channel = opts.channel ?? "dev";
		const branch = channel === "dev" ? await readBranchName(runCommand, gitRoot, timeoutMs) : null;
		const needsCheckoutMain = channel === "dev" && branch !== DEV_BRANCH;
		gitTotalSteps = channel === "dev" ? needsCheckoutMain ? 11 : 10 : 9;
		const statusCheck = await runStep(step("clean check", [
			"git",
			"-C",
			gitRoot,
			"status",
			"--porcelain",
			"--",
			":!dist/control-ui/"
		], gitRoot));
		steps.push(statusCheck);
		if (statusCheck.stdoutTail && statusCheck.stdoutTail.trim().length > 0) return {
			status: "skipped",
			mode: "git",
			root: gitRoot,
			reason: "dirty",
			before: {
				sha: beforeSha,
				version: beforeVersion
			},
			steps,
			durationMs: Date.now() - startedAt
		};
		if (channel === "dev") {
			if (needsCheckoutMain) {
				const checkoutStep = await runStep(step(`git checkout ${DEV_BRANCH}`, [
					"git",
					"-C",
					gitRoot,
					"checkout",
					DEV_BRANCH
				], gitRoot));
				steps.push(checkoutStep);
				if (checkoutStep.exitCode !== 0) return {
					status: "error",
					mode: "git",
					root: gitRoot,
					reason: "checkout-failed",
					before: {
						sha: beforeSha,
						version: beforeVersion
					},
					steps,
					durationMs: Date.now() - startedAt
				};
			}
			const upstreamStep = await runStep(step("upstream check", [
				"git",
				"-C",
				gitRoot,
				"rev-parse",
				"--abbrev-ref",
				"--symbolic-full-name",
				"@{upstream}"
			], gitRoot));
			steps.push(upstreamStep);
			if (upstreamStep.exitCode !== 0) return {
				status: "skipped",
				mode: "git",
				root: gitRoot,
				reason: "no-upstream",
				before: {
					sha: beforeSha,
					version: beforeVersion
				},
				steps,
				durationMs: Date.now() - startedAt
			};
			const fetchStep = await runStep(step("git fetch", [
				"git",
				"-C",
				gitRoot,
				"fetch",
				"--all",
				"--prune",
				"--tags"
			], gitRoot));
			steps.push(fetchStep);
			const upstreamShaStep = await runStep(step("git rev-parse @{upstream}", [
				"git",
				"-C",
				gitRoot,
				"rev-parse",
				"@{upstream}"
			], gitRoot));
			steps.push(upstreamShaStep);
			const upstreamSha = upstreamShaStep.stdoutTail?.trim();
			if (!upstreamShaStep.stdoutTail || !upstreamSha) return {
				status: "error",
				mode: "git",
				root: gitRoot,
				reason: "no-upstream-sha",
				before: {
					sha: beforeSha,
					version: beforeVersion
				},
				steps,
				durationMs: Date.now() - startedAt
			};
			const revListStep = await runStep(step("git rev-list", [
				"git",
				"-C",
				gitRoot,
				"rev-list",
				`--max-count=${PREFLIGHT_MAX_COMMITS}`,
				upstreamSha
			], gitRoot));
			steps.push(revListStep);
			if (revListStep.exitCode !== 0) return {
				status: "error",
				mode: "git",
				root: gitRoot,
				reason: "preflight-revlist-failed",
				before: {
					sha: beforeSha,
					version: beforeVersion
				},
				steps,
				durationMs: Date.now() - startedAt
			};
			const candidates = (revListStep.stdoutTail ?? "").split("\n").map((line) => line.trim()).filter(Boolean);
			if (candidates.length === 0) return {
				status: "error",
				mode: "git",
				root: gitRoot,
				reason: "preflight-no-candidates",
				before: {
					sha: beforeSha,
					version: beforeVersion
				},
				steps,
				durationMs: Date.now() - startedAt
			};
			const manager = await detectPackageManager(gitRoot);
			const preflightRoot = await fs.mkdtemp(path.join(os.tmpdir(), "SKYKOI-update-preflight-"));
			const worktreeDir = path.join(preflightRoot, "worktree");
			const worktreeStep = await runStep(step("preflight worktree", [
				"git",
				"-C",
				gitRoot,
				"worktree",
				"add",
				"--detach",
				worktreeDir,
				upstreamSha
			], gitRoot));
			steps.push(worktreeStep);
			if (worktreeStep.exitCode !== 0) {
				await fs.rm(preflightRoot, {
					recursive: true,
					force: true
				}).catch(() => {});
				return {
					status: "error",
					mode: "git",
					root: gitRoot,
					reason: "preflight-worktree-failed",
					before: {
						sha: beforeSha,
						version: beforeVersion
					},
					steps,
					durationMs: Date.now() - startedAt
				};
			}
			let selectedSha = null;
			try {
				for (const sha of candidates) {
					const shortSha = sha.slice(0, 8);
					const checkoutStep = await runStep(step(`preflight checkout (${shortSha})`, [
						"git",
						"-C",
						worktreeDir,
						"checkout",
						"--detach",
						sha
					], worktreeDir));
					steps.push(checkoutStep);
					if (checkoutStep.exitCode !== 0) continue;
					const depsStep = await runStep(step(`preflight deps install (${shortSha})`, managerInstallArgs(manager), worktreeDir));
					steps.push(depsStep);
					if (depsStep.exitCode !== 0) continue;
					const buildStep = await runStep(step(`preflight build (${shortSha})`, managerScriptArgs(manager, "build"), worktreeDir));
					steps.push(buildStep);
					if (buildStep.exitCode !== 0) continue;
					const lintStep = await runStep(step(`preflight lint (${shortSha})`, managerScriptArgs(manager, "lint"), worktreeDir));
					steps.push(lintStep);
					if (lintStep.exitCode !== 0) continue;
					selectedSha = sha;
					break;
				}
			} finally {
				const removeStep = await runStep(step("preflight cleanup", [
					"git",
					"-C",
					gitRoot,
					"worktree",
					"remove",
					"--force",
					worktreeDir
				], gitRoot));
				steps.push(removeStep);
				await runCommand([
					"git",
					"-C",
					gitRoot,
					"worktree",
					"prune"
				], {
					cwd: gitRoot,
					timeoutMs
				}).catch(() => null);
				await fs.rm(preflightRoot, {
					recursive: true,
					force: true
				}).catch(() => {});
			}
			if (!selectedSha) return {
				status: "error",
				mode: "git",
				root: gitRoot,
				reason: "preflight-no-good-commit",
				before: {
					sha: beforeSha,
					version: beforeVersion
				},
				steps,
				durationMs: Date.now() - startedAt
			};
			const rebaseStep = await runStep(step("git rebase", [
				"git",
				"-C",
				gitRoot,
				"rebase",
				selectedSha
			], gitRoot));
			steps.push(rebaseStep);
			if (rebaseStep.exitCode !== 0) {
				const abortResult = await runCommand([
					"git",
					"-C",
					gitRoot,
					"rebase",
					"--abort"
				], {
					cwd: gitRoot,
					timeoutMs
				});
				steps.push({
					name: "git rebase --abort",
					command: "git rebase --abort",
					cwd: gitRoot,
					durationMs: 0,
					exitCode: abortResult.code,
					stdoutTail: trimLogTail(abortResult.stdout, MAX_LOG_CHARS),
					stderrTail: trimLogTail(abortResult.stderr, MAX_LOG_CHARS)
				});
				return {
					status: "error",
					mode: "git",
					root: gitRoot,
					reason: "rebase-failed",
					before: {
						sha: beforeSha,
						version: beforeVersion
					},
					steps,
					durationMs: Date.now() - startedAt
				};
			}
		} else {
			const fetchStep = await runStep(step("git fetch", [
				"git",
				"-C",
				gitRoot,
				"fetch",
				"--all",
				"--prune",
				"--tags"
			], gitRoot));
			steps.push(fetchStep);
			if (fetchStep.exitCode !== 0) return {
				status: "error",
				mode: "git",
				root: gitRoot,
				reason: "fetch-failed",
				before: {
					sha: beforeSha,
					version: beforeVersion
				},
				steps,
				durationMs: Date.now() - startedAt
			};
			const tag = await resolveChannelTag(runCommand, gitRoot, timeoutMs, channel);
			if (!tag) return {
				status: "error",
				mode: "git",
				root: gitRoot,
				reason: "no-release-tag",
				before: {
					sha: beforeSha,
					version: beforeVersion
				},
				steps,
				durationMs: Date.now() - startedAt
			};
			const checkoutStep = await runStep(step(`git checkout ${tag}`, [
				"git",
				"-C",
				gitRoot,
				"checkout",
				"--detach",
				tag
			], gitRoot));
			steps.push(checkoutStep);
			if (checkoutStep.exitCode !== 0) return {
				status: "error",
				mode: "git",
				root: gitRoot,
				reason: "checkout-failed",
				before: {
					sha: beforeSha,
					version: beforeVersion
				},
				steps,
				durationMs: Date.now() - startedAt
			};
		}
		const manager = await detectPackageManager(gitRoot);
		const depsStep = await runStep(step("deps install", managerInstallArgs(manager), gitRoot));
		steps.push(depsStep);
		const buildStep = await runStep(step("build", managerScriptArgs(manager, "build"), gitRoot));
		steps.push(buildStep);
		const uiBuildStep = await runStep(step("ui:build", managerScriptArgs(manager, "ui:build"), gitRoot));
		steps.push(uiBuildStep);
		const doctorEntry = path.join(gitRoot, "skykoi.mjs");
		if (!await fs.stat(doctorEntry).then(() => true).catch(() => false)) {
			steps.push({
				name: "SKYKOI doctor entry",
				command: `verify ${doctorEntry}`,
				cwd: gitRoot,
				durationMs: 0,
				exitCode: 1,
				stderrTail: `missing ${doctorEntry}`
			});
			return {
				status: "error",
				mode: "git",
				root: gitRoot,
				reason: "doctor-entry-missing",
				before: {
					sha: beforeSha,
					version: beforeVersion
				},
				steps,
				durationMs: Date.now() - startedAt
			};
		}
		const doctorStep = await runStep(step("SKYKOI doctor", [
			process.execPath,
			doctorEntry,
			"doctor",
			"--non-interactive"
		], gitRoot, { SKYKOI_UPDATE_IN_PROGRESS: "1" }));
		steps.push(doctorStep);
		if (!(await resolveControlUiDistIndexHealth({ root: gitRoot })).exists) {
			const repairArgv = managerScriptArgs(manager, "ui:build");
			const started = Date.now();
			const repairResult = await runCommand(repairArgv, {
				cwd: gitRoot,
				timeoutMs
			});
			const repairStep = {
				name: "ui:build (post-doctor repair)",
				command: repairArgv.join(" "),
				cwd: gitRoot,
				durationMs: Date.now() - started,
				exitCode: repairResult.code,
				stdoutTail: trimLogTail(repairResult.stdout, MAX_LOG_CHARS),
				stderrTail: trimLogTail(repairResult.stderr, MAX_LOG_CHARS)
			};
			steps.push(repairStep);
			if (repairResult.code !== 0) return {
				status: "error",
				mode: "git",
				root: gitRoot,
				reason: repairStep.name,
				before: {
					sha: beforeSha,
					version: beforeVersion
				},
				steps,
				durationMs: Date.now() - startedAt
			};
			const repairedUiIndexHealth = await resolveControlUiDistIndexHealth({ root: gitRoot });
			if (!repairedUiIndexHealth.exists) {
				const uiIndexPath = repairedUiIndexHealth.indexPath ?? resolveControlUiDistIndexPathForRoot(gitRoot);
				steps.push({
					name: "ui assets verify",
					command: `verify ${uiIndexPath}`,
					cwd: gitRoot,
					durationMs: 0,
					exitCode: 1,
					stderrTail: `missing ${uiIndexPath}`
				});
				return {
					status: "error",
					mode: "git",
					root: gitRoot,
					reason: "ui-assets-missing",
					before: {
						sha: beforeSha,
						version: beforeVersion
					},
					steps,
					durationMs: Date.now() - startedAt
				};
			}
		}
		const failedStep = steps.find((s) => s.exitCode !== 0);
		const afterShaStep = await runStep(step("git rev-parse HEAD (after)", [
			"git",
			"-C",
			gitRoot,
			"rev-parse",
			"HEAD"
		], gitRoot));
		steps.push(afterShaStep);
		const afterVersion = await readPackageVersion(gitRoot);
		return {
			status: failedStep ? "error" : "ok",
			mode: "git",
			root: gitRoot,
			reason: failedStep ? failedStep.name : void 0,
			before: {
				sha: beforeSha,
				version: beforeVersion
			},
			after: {
				sha: afterShaStep.stdoutTail?.trim() ?? null,
				version: afterVersion
			},
			steps,
			durationMs: Date.now() - startedAt
		};
	}
	if (!pkgRoot) return {
		status: "error",
		mode: "unknown",
		reason: `no root (${START_DIRS.join(",")})`,
		steps: [],
		durationMs: Date.now() - startedAt
	};
	const beforeVersion = await readPackageVersion(pkgRoot);
	const globalManager = await detectGlobalInstallManagerForRoot(runCommand, pkgRoot, timeoutMs);
	if (globalManager) {
		const packageName = await readPackageName(pkgRoot) ?? DEFAULT_PACKAGE_NAME;
		await cleanupGlobalRenameDirs({
			globalRoot: path.dirname(pkgRoot),
			packageName
		});
		const channel = opts.channel ?? DEFAULT_PACKAGE_CHANNEL;
		const spec = `${packageName}@${normalizeTag(opts.tag ?? channelToNpmTag(channel))}`;
		const updateStep = await runStep({
			runCommand,
			name: "global update",
			argv: globalInstallArgs(globalManager, spec),
			cwd: pkgRoot,
			timeoutMs,
			progress,
			stepIndex: 0,
			totalSteps: 1
		});
		let finalStep = updateStep;
		if (updateStep.exitCode !== 0 && (updateStep.stderrTail?.includes("EACCES") || updateStep.stderrTail?.includes("permission denied") || updateStep.stderrTail?.includes("Missing write access"))) finalStep = await runStep({
			runCommand,
			name: "global update (sudo)",
			argv: ["sudo", ...globalInstallArgs(globalManager, spec)],
			cwd: pkgRoot,
			timeoutMs,
			progress,
			stepIndex: 0,
			totalSteps: 1
		});
		const steps = finalStep === updateStep ? [updateStep] : [updateStep, finalStep];
		const afterVersion = await readPackageVersion(pkgRoot);
		return {
			status: finalStep.exitCode === 0 ? "ok" : "error",
			mode: globalManager,
			root: pkgRoot,
			reason: finalStep.exitCode === 0 ? void 0 : finalStep.name,
			before: { version: beforeVersion },
			after: { version: afterVersion },
			steps,
			durationMs: Date.now() - startedAt
		};
	}
	return {
		status: "skipped",
		mode: "unknown",
		root: pkgRoot,
		reason: "not-git-install",
		before: { version: beforeVersion },
		steps: [],
		durationMs: Date.now() - startedAt
	};
}

//#endregion
export { resolveEffectiveUpdateChannel as _, globalInstallArgs as a, compareSemverStrings as c, resolveNpmChannelTag as d, DEFAULT_GIT_CHANNEL as f, normalizeUpdateChannel as g, formatUpdateChannelLabel as h, detectGlobalInstallManagerForRoot as i, fetchNpmLatestVersion as l, channelToNpmTag as m, cleanupGlobalRenameDirs as n, resolveGlobalPackageRoot as o, DEFAULT_PACKAGE_CHANNEL as p, detectGlobalInstallManagerByPresence as r, checkUpdateStatus as s, runGatewayUpdate as t, fetchNpmTagVersion as u };