import { c as isRecord } from "./record-coerce-DItp3I4t.js";
import { u as normalizeStringEntries, v as uniqueStrings } from "./string-normalization-e_fvmxMf.js";
import { s as pathExists } from "./absolute-path-CYFPfAjt.js";
import "./fs-safe-gVoExnQ2.js";
import { t as hasErrnoCode } from "./errno-CkbDOfLk.js";
import { r as formatErrorMessage } from "./errors-Z0lU1pH3.js";
import { r as stripAnsi } from "./ansi-DjDeieuH.js";
import { t as movePathWithCopyFallback } from "./move-path-DwtBa1y6.js";
import "./replace-file-CLSCJ1qR.js";
import { i as resolveGatewayInstallEntrypoint } from "./gateway-entrypoint-D-dT8_Uv.js";
import { n as readPackageName, r as readPackageVersion, t as readPackageManagerSpec } from "./package-json-DDWYAL5c.js";
import { t as resolveStableNodePath } from "./stable-node-path-CysbL5Xo.js";
import { r as runCommandWithTimeout } from "./exec-B6wf9wmM.js";
import { g as updateInstallRootsMatch, h as resolveUpdateInstallRoot, p as trimLogTail } from "./restart-sentinel-DtNjsD5i.js";
import { a as resolveDevUpdateTargetRevision } from "./update-dev-target-BGSFRuDr.js";
import { t as applyPathPrepend } from "./path-prepend-Up5PqO7Z.js";
import { a as resolveControlUiAssetHealth, o as resolveControlUiDistIndexPathForRoot } from "./control-ui-assets-Bc4Rcpqu.js";
import { a as channelToNpmTag, c as isStableTag, n as DEV_BRANCH, r as EXTENDED_STABLE_TAG_UNSUPPORTED_REASON, s as isBetaTag, u as resolveDevUpstreamRefs } from "./update-channels-D2-WrHya.js";
import { a as resolveExtendedStablePackage, c as parsePackageOpenClawSchemaVersions, l as detectPackageManager, n as compareSemverStrings, u as resolveBunGlobalInstallOwner } from "./update-check-DrhFQonH.js";
import { _ as resolvePnpmGlobalDirFromGlobalRoot, b as normalizePackageTagInput, c as globalInstallFallbackArgs, d as resolveExpectedInstalledVersionFromSpec, f as resolveGlobalInstallSpec, g as resolveNpmLifecyclePolicyGate, h as resolveNpmGlobalPrefixLayoutFromPrefix, i as createGlobalInstallEnv, l as listActivePnpmIsolatedGlobalPackages, m as resolveNpmGlobalPrefixLayoutFromGlobalRoot, n as cleanupGlobalRenameDirs, o as detectGlobalInstallManagerForRoot, p as resolveGlobalInstallTarget, r as collectInstalledGlobalPackageErrors, s as globalInstallArgs, u as readPackageManagerProbeValue, v as resolvePnpmIsolatedInstallOwner, y as collectGitRuntimeErrors } from "./update-global-C5_r5uUt.js";
import { t as PACKAGE_POST_INSTALL_DOCTOR_ADVISORY } from "./update-doctor-result-D3HkeqjI.js";
import { pathToFileURL } from "node:url";
import os from "node:os";
import path from "node:path";
import fs from "node:fs/promises";
//#region src/infra/package-update-steps.ts
const PACKAGE_MANAGER_SWAP_SOURCE_HARDLINKS = "allow";
function packageUpdateFailure(failedStep, verifiedPackageRoot, steps = [failedStep]) {
	return {
		steps,
		verifiedPackageRoot,
		afterVersion: null,
		failedStep
	};
}
const NPM_PACK_QUIET_FLAGS = ["--json", "--loglevel=error"];
const PACKAGE_INSTALL_GUARD_PATH = path.join("dist", "openclaw-install-guard");
const PACKAGE_LIFECYCLE_PENDING_PATH = ".openclaw-lifecycle-pending";
const PACKAGE_PREINSTALL_SCRIPT_PATH = path.join("scripts", "preinstall-package-manager-warning.mjs");
async function resolveNpmUpdateLifecyclePolicy(params) {
	const gate = resolveNpmLifecyclePolicyGate(params.installTarget);
	if (!gate.error) return {
		policy: gate.policy,
		failedStep: null
	};
	const argv = [params.installTarget.command, "--version"];
	const version = params.installTarget.npmOwner?.version ?? "";
	return {
		policy: null,
		failedStep: {
			name: "npm lifecycle policy preflight",
			command: argv.join(" "),
			cwd: process.cwd(),
			durationMs: 0,
			exitCode: 1,
			stdoutTail: version || null,
			stderrTail: gate.error
		}
	};
}
async function resolveCanonicalPath(filePath) {
	return path.resolve(await fs.realpath(filePath).catch(() => filePath));
}
async function runPnpmPreflightProbe(params) {
	const startedAt = Date.now();
	const argv = [params.installTarget.command, ...params.args];
	const probeCwd = params.installTarget.globalRoot ?? void 0;
	try {
		const result = await params.runCommand(argv, {
			timeoutMs: params.timeoutMs,
			env: params.env,
			...probeCwd ? { cwd: probeCwd } : {}
		});
		if (result.code === 0) return {
			result,
			failedStep: null
		};
		return {
			result: null,
			failedStep: {
				name: "pnpm isolated install preflight",
				command: argv.join(" "),
				cwd: probeCwd ?? process.cwd(),
				durationMs: Date.now() - startedAt,
				exitCode: result.code ?? 1,
				stdoutTail: result.stdout || null,
				stderrTail: result.stderr || `Unable to run ${argv.join(" ")}.`
			}
		};
	} catch (error) {
		return {
			result: null,
			failedStep: {
				name: "pnpm isolated install preflight",
				command: argv.join(" "),
				cwd: probeCwd ?? process.cwd(),
				durationMs: Date.now() - startedAt,
				exitCode: 1,
				stdoutTail: null,
				stderrTail: formatErrorMessage(error)
			}
		};
	}
}
async function validatePnpmIsolatedUpdate(params) {
	const owner = params.installTarget.pnpmIsolated;
	if (!owner) return {
		globalBinDir: null,
		failedStep: null
	};
	const activePackages = await listActivePnpmIsolatedGlobalPackages({
		globalRoot: params.installTarget.globalRoot,
		packageName: params.packageName
	});
	const activePackageRoots = activePackages.map((entry) => entry.packageRoot);
	const siblingPackages = [...new Set(activePackages.flatMap((entry) => entry.packageNames.filter((name) => name !== params.packageName)))].toSorted((a, b) => a.localeCompare(b));
	if (siblingPackages.length > 0) return {
		globalBinDir: null,
		failedStep: {
			name: "pnpm isolated install preflight",
			command: `inspect ${params.installTarget.globalRoot ?? "pnpm install"}`,
			cwd: params.installTarget.globalRoot ?? process.cwd(),
			durationMs: 0,
			exitCode: 1,
			stdoutTail: null,
			stderrTail: `OpenClaw shares a pnpm ${owner.layoutVersion} global install group with ${siblingPackages.join(", ")}. Automatic update stopped before mutation; update the group manually to preserve its sibling packages.`
		}
	};
	const invokingPackageRoot = params.installTarget.packageRoot;
	const invokingInstallOwner = await resolvePnpmIsolatedInstallOwner(invokingPackageRoot);
	const activeInstallOwners = await Promise.all(activePackageRoots.map((packageRoot) => resolvePnpmIsolatedInstallOwner(packageRoot)));
	const ownerMatchCount = invokingInstallOwner ? activeInstallOwners.filter((installOwner) => installOwner === invokingInstallOwner).length : 0;
	if (!invokingPackageRoot || activePackageRoots.length !== 1 || ownerMatchCount !== 1) return {
		globalBinDir: null,
		failedStep: {
			name: "pnpm isolated install preflight",
			command: `inspect ${params.installTarget.globalRoot ?? "pnpm install"}`,
			cwd: params.installTarget.globalRoot ?? process.cwd(),
			durationMs: 0,
			exitCode: 1,
			stdoutTail: null,
			stderrTail: `Expected exactly one active pnpm ${owner.layoutVersion} OpenClaw install owned by the invoking project; found ${activePackageRoots.length} active installs and ${ownerMatchCount} owner matches. Automatic update stopped before mutation.`
		}
	};
	const rootProbe = await runPnpmPreflightProbe({
		...params,
		args: ["root", "-g"]
	});
	if (rootProbe.failedStep || !rootProbe.result) return {
		globalBinDir: null,
		failedStep: rootProbe.failedStep
	};
	const reportedGlobalRoot = readPackageManagerProbeValue(rootProbe.result.stdout);
	const expectedGlobalRoot = params.installTarget.globalRoot;
	if (!reportedGlobalRoot || !expectedGlobalRoot || await resolveCanonicalPath(reportedGlobalRoot) !== await resolveCanonicalPath(expectedGlobalRoot)) return {
		globalBinDir: null,
		failedStep: {
			name: "pnpm isolated install preflight",
			command: `${params.installTarget.command} root -g`,
			cwd: expectedGlobalRoot ?? process.cwd(),
			durationMs: 0,
			exitCode: 1,
			stdoutTail: rootProbe.result.stdout || null,
			stderrTail: `The active pnpm command owns ${reportedGlobalRoot || "an unknown global root"}, not the invoking OpenClaw install at ${expectedGlobalRoot ?? "an unknown root"}. Automatic update stopped before mutation.`
		}
	};
	const binProbe = await runPnpmPreflightProbe({
		...params,
		args: ["bin", "-g"]
	});
	const globalBinDir = binProbe.result ? readPackageManagerProbeValue(binProbe.result.stdout) || null : null;
	if (binProbe.failedStep || !globalBinDir) return {
		globalBinDir: null,
		failedStep: binProbe.failedStep ?? {
			name: "pnpm isolated install preflight",
			command: `${params.installTarget.command} bin -g`,
			cwd: expectedGlobalRoot,
			durationMs: 0,
			exitCode: 1,
			stdoutTail: null,
			stderrTail: "The owning pnpm command did not report its global bin directory."
		}
	};
	return {
		globalBinDir,
		failedStep: null
	};
}
const PACKAGE_POSTINSTALL_SCRIPT_PATH = path.join("scripts", "postinstall-bundled-plugins.mjs");
function isBlockingPackageUpdateStep(step) {
	return step.exitCode !== 0 && step.advisory === void 0;
}
function isNormalProcessExit(step) {
	return step.termination !== "timeout" && step.termination !== "no-output-timeout" && step.termination !== "signal" && step.killed !== true && (step.signal === void 0 || step.signal === null);
}
function markPackagePostInstallDoctorAdvisory(step, result) {
	if (step.exitCode !== 86 || result?.status !== "advisory" || !isNormalProcessExit(step)) return step;
	const advisoryTail = [
		step.stderrTail,
		...result.advisory.details,
		PACKAGE_POST_INSTALL_DOCTOR_ADVISORY.message
	].filter((line) => Boolean(line?.trim())).join("\n");
	return {
		...step,
		advisory: PACKAGE_POST_INSTALL_DOCTOR_ADVISORY,
		stderrTail: trimLogTail(advisoryTail) ?? step.stderrTail
	};
}
async function removePathBestEffort(targetPath) {
	try {
		await fs.rm(targetPath, {
			recursive: true,
			force: true,
			maxRetries: process.platform === "win32" ? 5 : 2,
			retryDelay: 100
		});
		return true;
	} catch {
		return false;
	}
}
async function readPackageVersionIfPresent(packageRoot) {
	if (!packageRoot) return null;
	try {
		return await readPackageVersion(packageRoot);
	} catch {
		return null;
	}
}
function isUnambiguousNpmPrefixGlobalRoot(globalRoot) {
	const trimmed = globalRoot?.trim();
	if (!trimmed) return false;
	const normalized = path.resolve(trimmed);
	if (path.basename(normalized) !== "node_modules") return false;
	const parentDir = path.dirname(normalized);
	if (path.basename(parentDir) === "lib") return true;
	return process.platform === "win32" && path.basename(parentDir).toLowerCase() === "npm";
}
function resolveStagedNpmTargetLayout(installTarget) {
	const targetLayout = resolveNpmGlobalPrefixLayoutFromGlobalRoot(installTarget.globalRoot, { allowDirectNodeModulesRoot: installTarget.directNodeModulesRoot === true });
	if (!targetLayout) return null;
	if (installTarget.manager === "npm" || isUnambiguousNpmPrefixGlobalRoot(installTarget.globalRoot)) return targetLayout;
	return null;
}
function stripPackageAlias(spec, packageName) {
	const trimmed = spec.trim();
	const prefix = `${packageName.trim()}@`;
	return trimmed.toLowerCase().startsWith(prefix.toLowerCase()) ? trimmed.slice(prefix.length).trim() : trimmed;
}
function isHttpGitUrlSpec(spec) {
	try {
		const url = new URL(spec);
		if (url.protocol !== "https:" && url.protocol !== "http:") return false;
		const pathname = url.pathname.replace(/\/+$/u, "");
		if (pathname.endsWith(".git")) return true;
		const parts = pathname.split("/").filter(Boolean);
		return url.hostname.toLowerCase() === "github.com" && parts.length === 2;
	} catch {
		return false;
	}
}
function isGitHubShorthandSpec(spec) {
	const [repo] = spec.split("#", 1);
	if (!repo || repo.startsWith(".") || repo.startsWith("/") || repo.startsWith("@")) return false;
	const parts = repo.split("/");
	return parts.length === 2 && parts.every((part) => /^[^\s/:@]+$/u.test(part));
}
function isNpmGitSourceInstallSpec(spec, packageName) {
	const target = stripPackageAlias(spec, packageName);
	return /^github:/i.test(target) || /^git\+(?:ssh|https|http|file):/i.test(target) || /^git:/i.test(target) || /^ssh:\/\//i.test(target) || /^[^@\s]+@[^:\s]+:[^#\s]+(?:#.*)?$/u.test(target) || isHttpGitUrlSpec(target) || isGitHubShorthandSpec(target);
}
function resolvePnpmInstallSpecFromCwd(spec, packageName, sourceCwd) {
	const trimmed = spec.trim();
	const aliasPrefix = `${packageName.trim()}@`;
	const hasAlias = trimmed.toLowerCase().startsWith(aliasPrefix.toLowerCase());
	const targetSpec = hasAlias ? trimmed.slice(aliasPrefix.length).trim() : trimmed;
	const windowsPath = /^[a-z]:[\\/]/iu.test(sourceCwd) || sourceCwd.startsWith("\\\\");
	const paths = windowsPath ? path.win32 : path;
	const localProtocol = /^(file:|git\+file:|link:)(.*)$/iu.exec(targetSpec);
	if (localProtocol) {
		const protocol = localProtocol[1] ?? "";
		const target = localProtocol[2]?.trim() ?? "";
		const fragmentIndex = protocol.toLowerCase() === "git+file:" ? target.indexOf("#") : -1;
		const targetPath = fragmentIndex >= 0 ? target.slice(0, fragmentIndex) : target;
		const fragment = fragmentIndex >= 0 ? target.slice(fragmentIndex) : "";
		const resolvedTarget = targetPath && !/^~[\\/]/u.test(targetPath) && !path.isAbsolute(targetPath) && !path.win32.isAbsolute(targetPath) ? paths.resolve(sourceCwd, targetPath) : targetPath;
		if (protocol.toLowerCase() === "git+file:") return resolvedTarget === targetPath ? spec : `${hasAlias ? aliasPrefix : ""}git+${pathToFileURL(resolvedTarget, { windows: windowsPath }).href}${fragment}`;
		return `${aliasPrefix}${protocol}${resolvedTarget}`;
	}
	const isPath = /^(?:\.{1,2}|~)(?:[\\/]|$)/u.test(targetSpec) || path.isAbsolute(targetSpec) || path.win32.isAbsolute(targetSpec);
	if (!isPath && (hasAlias || /[:@]/u.test(targetSpec) || !/\.(?:tgz|tar\.gz)$/iu.test(targetSpec))) return spec;
	const target = isPath && !/^\.{1,2}(?:[\\/]|$)/u.test(targetSpec) ? targetSpec : paths.resolve(sourceCwd, targetSpec);
	return `${aliasPrefix}${/\.(?:tgz|tar\.gz|tar)$/iu.test(target) ? "file" : "link"}:${target}`;
}
async function createStagedNpmInstall(installTarget, packageName) {
	const targetLayout = resolveStagedNpmTargetLayout(installTarget);
	if (!targetLayout) return null;
	await fs.mkdir(targetLayout.globalRoot, { recursive: true });
	const prefix = await fs.mkdtemp(path.join(targetLayout.globalRoot, ".openclaw-update-stage-"));
	const layout = resolveNpmGlobalPrefixLayoutFromPrefix(prefix);
	const command = installTarget.manager === "npm" ? installTarget.command : "npm";
	return {
		prefix,
		layout,
		packageRoot: path.join(layout.globalRoot, packageName),
		installTarget: {
			manager: "npm",
			command,
			globalRoot: layout.globalRoot,
			packageRoot: path.join(layout.globalRoot, packageName)
		}
	};
}
async function findPackedTarball(packDir) {
	const tarballs = (await fs.readdir(packDir).catch(() => [])).filter((entry) => entry.endsWith(".tgz"));
	if (tarballs.length !== 1) return null;
	return path.join(packDir, tarballs[0] ?? "");
}
async function prepareNpmGitSourceInstallSpec(params) {
	if (params.installTarget.manager !== "npm" || !isNpmGitSourceInstallSpec(params.installSpec, params.packageName)) return {
		installSpec: params.installSpec,
		installCwd: params.installCwd ?? null,
		packDir: null,
		steps: [],
		failedStep: null
	};
	const packDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-update-pack-"));
	const packStep = await params.runStep({
		name: "global update pack",
		argv: [
			params.installTarget.command,
			"pack",
			params.installSpec,
			"--pack-destination",
			packDir,
			...NPM_PACK_QUIET_FLAGS
		],
		cwd: params.installCwd,
		env: params.env,
		timeoutMs: params.timeoutMs
	});
	if (packStep.exitCode !== 0) return {
		installSpec: params.installSpec,
		installCwd: params.installCwd ?? null,
		packDir,
		steps: [packStep],
		failedStep: packStep
	};
	const tarball = await findPackedTarball(packDir);
	if (!tarball) {
		const failedStep = {
			name: "global update pack verify",
			command: `find packed tarball in ${packDir}`,
			cwd: packDir,
			durationMs: 0,
			exitCode: 1,
			stdoutTail: null,
			stderrTail: `expected exactly one .tgz from npm pack ${params.installSpec}`
		};
		return {
			installSpec: params.installSpec,
			installCwd: params.installCwd ?? null,
			packDir,
			steps: [packStep, failedStep],
			failedStep
		};
	}
	return {
		installSpec: tarball,
		installCwd: packDir,
		packDir,
		steps: [packStep],
		failedStep: null
	};
}
async function prepareStagedNpmInstall(installTarget, packageName) {
	const startedAt = Date.now();
	try {
		return {
			stagedInstall: await createStagedNpmInstall(installTarget, packageName),
			failedStep: null
		};
	} catch (err) {
		return {
			stagedInstall: null,
			failedStep: {
				name: "global install stage",
				command: "prepare staged npm install",
				cwd: (installTarget.manager === "npm" ? resolveNpmGlobalPrefixLayoutFromGlobalRoot(installTarget.globalRoot, { allowDirectNodeModulesRoot: installTarget.directNodeModulesRoot === true }) : null)?.prefix ?? installTarget.globalRoot ?? process.cwd(),
				durationMs: Date.now() - startedAt,
				exitCode: 1,
				stdoutTail: null,
				stderrTail: formatErrorMessage(err)
			}
		};
	}
}
async function cleanupStagedNpmInstall(stage) {
	if (stage) await removePathBestEffort(stage.prefix);
}
async function copyPathEntry(source, destination) {
	const stat = await fs.lstat(source);
	await removePathBestEffort(destination);
	if (stat.isSymbolicLink()) {
		await fs.symlink(await fs.readlink(source), destination);
		return;
	}
	if (stat.isDirectory()) {
		await fs.cp(source, destination, {
			recursive: true,
			force: true,
			preserveTimestamps: false
		});
		return;
	}
	await fs.copyFile(source, destination);
	await fs.chmod(destination, stat.mode).catch(() => void 0);
}
async function replaceNpmBinShims(params) {
	let entries;
	try {
		entries = await fs.readdir(params.stageLayout.binDir);
	} catch {
		return;
	}
	const names = /* @__PURE__ */ new Set([params.packageName, "openclaw"]);
	const shimEntries = entries.filter((entry) => {
		const parsed = path.parse(entry);
		return names.has(entry) || names.has(parsed.name);
	});
	if (shimEntries.length === 0) return;
	const backupDir = await fs.mkdtemp(path.join(params.targetLayout.globalRoot, ".openclaw-shim-backup-"));
	const backedUpEntries = [];
	try {
		await fs.mkdir(params.targetLayout.binDir, { recursive: true });
		for (const entry of shimEntries) {
			const destination = path.join(params.targetLayout.binDir, entry);
			const hadExisting = await pathExists(destination);
			backedUpEntries.push({
				name: entry,
				hadExisting
			});
			if (hadExisting) await copyPathEntry(destination, path.join(backupDir, entry));
		}
		for (const entry of shimEntries) await copyPathEntry(path.join(params.stageLayout.binDir, entry), path.join(params.targetLayout.binDir, entry));
	} catch (err) {
		await fs.mkdir(params.targetLayout.binDir, { recursive: true });
		for (const entry of backedUpEntries) {
			const destination = path.join(params.targetLayout.binDir, entry.name);
			await removePathBestEffort(destination);
			if (entry.hadExisting) await copyPathEntry(path.join(backupDir, entry.name), destination);
		}
		throw err;
	} finally {
		await removePathBestEffort(backupDir);
	}
}
async function activateStagedNpmPackageRoot(source, destination) {
	if (!(await fs.lstat(source)).isSymbolicLink()) {
		await movePathWithCopyFallback({
			from: source,
			sourceHardlinks: PACKAGE_MANAGER_SWAP_SOURCE_HARDLINKS,
			to: destination
		});
		return;
	}
	const canonicalSource = await fs.realpath(source);
	await fs.symlink(canonicalSource, destination, process.platform === "win32" ? "junction" : void 0);
}
async function swapStagedNpmInstall(params) {
	const startedAt = Date.now();
	const targetLayout = resolveNpmGlobalPrefixLayoutFromGlobalRoot(params.installTarget.globalRoot, { allowDirectNodeModulesRoot: params.installTarget.directNodeModulesRoot === true });
	const targetPackageRoot = params.installTarget.packageRoot;
	if (!targetLayout || !targetPackageRoot) return {
		name: "global install swap",
		command: "swap staged npm install",
		cwd: params.stage.prefix,
		durationMs: Date.now() - startedAt,
		exitCode: 1,
		stdoutTail: null,
		stderrTail: "cannot resolve npm global prefix layout"
	};
	const backupRoot = path.join(targetLayout.globalRoot, `.openclaw-${process.pid}-${Date.now()}`);
	let movedExisting = false;
	let movedStaged = false;
	let removedBackup = true;
	try {
		await fs.mkdir(targetLayout.globalRoot, { recursive: true });
		if (await pathExists(targetPackageRoot)) {
			await movePathWithCopyFallback({
				from: targetPackageRoot,
				sourceHardlinks: PACKAGE_MANAGER_SWAP_SOURCE_HARDLINKS,
				to: backupRoot
			});
			movedExisting = true;
		}
		await activateStagedNpmPackageRoot(params.stage.packageRoot, targetPackageRoot);
		movedStaged = true;
		if (params.installTarget.directNodeModulesRoot !== true) await replaceNpmBinShims({
			stageLayout: params.stage.layout,
			targetLayout,
			packageName: params.packageName
		});
		if (movedExisting) removedBackup = await removePathBestEffort(backupRoot);
		return {
			name: "global install swap",
			command: `swap ${params.stage.packageRoot} -> ${targetPackageRoot}`,
			cwd: targetLayout.globalRoot,
			durationMs: Date.now() - startedAt,
			exitCode: 0,
			stdoutTail: movedExisting ? removedBackup ? `replaced ${params.packageName}` : `replaced ${params.packageName}; preserved old package at ${backupRoot} for delayed cleanup` : `installed ${params.packageName}`,
			stderrTail: null
		};
	} catch (err) {
		if (movedStaged) await removePathBestEffort(targetPackageRoot);
		if (movedExisting) await movePathWithCopyFallback({
			from: backupRoot,
			sourceHardlinks: PACKAGE_MANAGER_SWAP_SOURCE_HARDLINKS,
			to: targetPackageRoot
		}).catch(() => void 0);
		return {
			name: "global install swap",
			command: `swap ${params.stage.packageRoot} -> ${targetPackageRoot}`,
			cwd: targetLayout.globalRoot,
			durationMs: Date.now() - startedAt,
			exitCode: 1,
			stdoutTail: null,
			stderrTail: formatErrorMessage(err)
		};
	}
}
/**
* Runs the global package update flow, including npm staging when possible,
* package verification, optional post-verification, and cleanup.
*/
async function runGlobalPackageUpdateSteps(params) {
	let stagedInstall = null;
	let packedInstallDir = null;
	try {
		const npmPreflight = await resolveNpmUpdateLifecyclePolicy({ installTarget: params.installTarget });
		if (npmPreflight.failedStep) return packageUpdateFailure(npmPreflight.failedStep, params.packageRoot ?? params.installTarget.packageRoot);
		const pnpmPreflight = await validatePnpmIsolatedUpdate({
			installTarget: params.installTarget,
			packageName: params.packageName,
			runCommand: params.runCommand,
			timeoutMs: params.timeoutMs,
			env: params.env
		});
		if (pnpmPreflight.failedStep) return packageUpdateFailure(pnpmPreflight.failedStep, params.packageRoot ?? params.installTarget.packageRoot);
		const packageRoot = params.packageRoot ?? params.installTarget.packageRoot;
		if (packageRoot) await cleanupGlobalRenameDirs({
			globalRoot: path.dirname(packageRoot),
			packageName: params.packageName
		});
		const bunOwner = params.installTarget.manager === "bun" ? resolveBunGlobalInstallOwner(params.installTarget.packageRoot ?? params.packageRoot, params.env ?? process.env) : null;
		let effectiveInstallEnv = params.installTarget.manager === "bun" && params.installTarget.globalRoot ? {
			...params.env ?? process.env,
			BUN_INSTALL_GLOBAL_DIR: path.dirname(params.installTarget.globalRoot),
			...bunOwner?.bunInstall ? { BUN_INSTALL: bunOwner.bunInstall } : {}
		} : params.env;
		if (params.installTarget.manager === "pnpm" && params.installTarget.globalRoot) {
			const globalDir = resolvePnpmGlobalDirFromGlobalRoot(params.installTarget.globalRoot);
			effectiveInstallEnv = {
				...params.env ?? process.env,
				...globalDir ? {
					pnpm_config_global_dir: globalDir,
					PNPM_CONFIG_GLOBAL_DIR: globalDir,
					npm_config_global_dir: globalDir,
					NPM_CONFIG_GLOBAL_DIR: globalDir
				} : {},
				...pnpmPreflight.globalBinDir ? {
					pnpm_config_global_bin_dir: pnpmPreflight.globalBinDir,
					PNPM_CONFIG_GLOBAL_BIN_DIR: pnpmPreflight.globalBinDir,
					npm_config_global_bin_dir: pnpmPreflight.globalBinDir,
					NPM_CONFIG_GLOBAL_BIN_DIR: pnpmPreflight.globalBinDir
				} : {}
			};
		}
		const installEnv = effectiveInstallEnv === void 0 ? {} : { env: effectiveInstallEnv };
		const preparedInstall = await prepareStagedNpmInstall(params.installTarget, params.packageName);
		stagedInstall = preparedInstall.stagedInstall;
		if (preparedInstall.failedStep) return packageUpdateFailure(preparedInstall.failedStep, params.packageRoot ?? null);
		const steps = [];
		const installCommandTarget = stagedInstall?.installTarget ?? params.installTarget;
		const preparedSpec = await prepareNpmGitSourceInstallSpec({
			installTarget: installCommandTarget,
			installSpec: params.installSpec,
			packageName: params.packageName,
			runStep: params.runStep,
			timeoutMs: params.timeoutMs,
			env: params.env,
			installCwd: params.installCwd
		});
		packedInstallDir = preparedSpec.packDir;
		steps.push(...preparedSpec.steps);
		if (preparedSpec.failedStep) return packageUpdateFailure(preparedSpec.failedStep, params.packageRoot ?? null, steps);
		const updateCwd = (installCommandTarget.manager === "pnpm" ? installCommandTarget.globalRoot : null) ?? preparedSpec.installCwd;
		const updateInstallSpec = installCommandTarget.manager === "pnpm" ? resolvePnpmInstallSpecFromCwd(preparedSpec.installSpec, params.packageName, preparedSpec.installCwd ?? process.cwd()) : preparedSpec.installSpec;
		const updateStep = await params.runStep({
			name: "global update",
			argv: globalInstallArgs(installCommandTarget, updateInstallSpec, void 0, stagedInstall?.prefix, preparedSpec.installCwd, npmPreflight.policy ?? void 0),
			...updateCwd ? { cwd: updateCwd } : {},
			...installEnv,
			timeoutMs: params.timeoutMs
		});
		steps.push(updateStep);
		let finalInstallStep = updateStep;
		if (updateStep.exitCode !== 0) {
			await cleanupStagedNpmInstall(stagedInstall);
			stagedInstall = null;
			const preparedFallbackInstall = await prepareStagedNpmInstall(params.installTarget, params.packageName);
			stagedInstall = preparedFallbackInstall.stagedInstall;
			if (preparedFallbackInstall.failedStep) {
				steps.push(preparedFallbackInstall.failedStep);
				return packageUpdateFailure(preparedFallbackInstall.failedStep, params.packageRoot ?? null, steps);
			}
			const fallbackArgv = globalInstallFallbackArgs(stagedInstall?.installTarget ?? params.installTarget, preparedSpec.installSpec, void 0, stagedInstall?.prefix, preparedSpec.installCwd, npmPreflight.policy ?? void 0);
			if (fallbackArgv) {
				const fallbackStep = await params.runStep({
					name: "global update (omit optional)",
					argv: fallbackArgv,
					...preparedSpec.installCwd ? { cwd: preparedSpec.installCwd } : {},
					...installEnv,
					timeoutMs: params.timeoutMs
				});
				steps.push(fallbackStep);
				finalInstallStep = fallbackStep;
			} else {
				await cleanupStagedNpmInstall(stagedInstall);
				stagedInstall = null;
			}
		}
		const refreshedPnpmPackageRoot = finalInstallStep.exitCode === 0 && !stagedInstall && params.installTarget.pnpmIsolated ? await (async () => {
			const activeRoots = (await listActivePnpmIsolatedGlobalPackages({
				globalRoot: params.installTarget.globalRoot,
				packageName: params.packageName
			})).map((entry) => entry.packageRoot);
			if (activeRoots.length !== 1 || !params.installTarget.packageRoot) return null;
			const replacementRoot = activeRoots[0];
			if (!replacementRoot) return null;
			const [replacementOwner, previousOwner] = await Promise.all([resolvePnpmIsolatedInstallOwner(replacementRoot), resolvePnpmIsolatedInstallOwner(params.installTarget.packageRoot)]);
			return replacementOwner && previousOwner && replacementOwner !== previousOwner ? replacementRoot : null;
		})() : null;
		if (finalInstallStep.exitCode === 0 && !stagedInstall && params.installTarget.manager === "pnpm" && params.installTarget.pnpmIsolated !== void 0 && params.installTarget.packageRoot !== null && refreshedPnpmPackageRoot === null) {
			const replacementStep = {
				name: "global install verify",
				command: `resolve pnpm replacement in ${params.installTarget.globalRoot ?? "unknown root"}`,
				cwd: params.installTarget.globalRoot ?? process.cwd(),
				durationMs: 0,
				exitCode: 1,
				stderrTail: "could not identify a unique active pnpm replacement package"
			};
			steps.push(replacementStep);
			return packageUpdateFailure(replacementStep, params.packageRoot ?? null, steps);
		}
		const livePackageRoot = refreshedPnpmPackageRoot ?? params.installTarget.packageRoot ?? params.packageRoot ?? (await resolveGlobalInstallTarget({
			manager: params.installTarget,
			runCommand: params.runCommand,
			timeoutMs: params.timeoutMs,
			packageName: params.packageName
		})).packageRoot ?? null;
		const verificationPackageRoot = stagedInstall?.packageRoot ?? livePackageRoot;
		let verifiedPackageRoot = livePackageRoot ?? verificationPackageRoot;
		if (finalInstallStep.exitCode === 0 && !verificationPackageRoot && params.expectedGitCheckout) {
			const failedStep = {
				name: "global install verify",
				command: "resolve installed checkout",
				cwd: updateCwd ?? process.cwd(),
				durationMs: 0,
				exitCode: 1,
				stderrTail: "could not identify the installed checkout root"
			};
			return packageUpdateFailure(failedStep, null, [...steps, failedStep]);
		}
		if (finalInstallStep.exitCode === 0 && !stagedInstall && params.installTarget.manager === "pnpm" && verificationPackageRoot) {
			const installGuardPath = path.join(verificationPackageRoot, PACKAGE_INSTALL_GUARD_PATH);
			const lifecyclePendingPath = path.join(verificationPackageRoot, PACKAGE_LIFECYCLE_PENDING_PATH);
			const hasInstallGuard = await pathExists(installGuardPath);
			const hasPendingLifecycle = await pathExists(lifecyclePendingPath);
			if (hasInstallGuard || hasPendingLifecycle) {
				if (!hasPendingLifecycle) try {
					await fs.writeFile(lifecyclePendingPath, "pending\n", "utf8");
				} catch (error) {
					const markerStep = {
						name: "pnpm package lifecycle marker",
						command: `write ${lifecyclePendingPath}`,
						cwd: verificationPackageRoot,
						durationMs: 0,
						exitCode: 1,
						stderrTail: formatErrorMessage(error)
					};
					steps.push(markerStep);
					return packageUpdateFailure(markerStep, verifiedPackageRoot, steps);
				}
				const lifecycleScripts = [...hasInstallGuard ? [["pnpm package preinstall", PACKAGE_PREINSTALL_SCRIPT_PATH]] : [], ["pnpm package postinstall", PACKAGE_POSTINSTALL_SCRIPT_PATH]];
				for (const [name, relativeScript] of lifecycleScripts) {
					const lifecycleStep = await params.runStep({
						name,
						argv: [process.execPath, path.join(verificationPackageRoot, relativeScript)],
						cwd: verificationPackageRoot,
						env: effectiveInstallEnv,
						timeoutMs: params.timeoutMs
					});
					steps.push(lifecycleStep);
					if (lifecycleStep.exitCode !== 0) return packageUpdateFailure(lifecycleStep, verifiedPackageRoot, steps);
				}
				try {
					await fs.rm(lifecyclePendingPath);
				} catch (error) {
					const finalizeStep = {
						name: "pnpm package lifecycle finalize",
						command: `remove ${lifecyclePendingPath}`,
						cwd: verificationPackageRoot,
						durationMs: 0,
						exitCode: 1,
						stderrTail: formatErrorMessage(error)
					};
					steps.push(finalizeStep);
					return packageUpdateFailure(finalizeStep, verifiedPackageRoot, steps);
				}
			}
		}
		let afterVersion = null;
		if (finalInstallStep.exitCode === 0 && verificationPackageRoot) {
			const candidateVersion = await readPackageVersion(verificationPackageRoot);
			if (!stagedInstall) afterVersion = candidateVersion;
			const verificationErrors = await collectInstalledGlobalPackageErrors({
				packageRoot: verificationPackageRoot,
				expectedVersion: resolveExpectedInstalledVersionFromSpec(params.packageName, params.installSpec),
				expectedGitCheckout: params.expectedGitCheckout
			});
			if (verificationErrors.length > 0) steps.push({
				name: "global install verify",
				command: `verify ${verificationPackageRoot}`,
				cwd: verificationPackageRoot,
				durationMs: 0,
				exitCode: 1,
				stderrTail: verificationErrors.join("\n"),
				stdoutTail: null
			});
			if (stagedInstall && verificationErrors.length === 0) {
				const swapStep = await swapStagedNpmInstall({
					stage: stagedInstall,
					installTarget: params.installTarget,
					packageName: params.packageName
				});
				steps.push(swapStep);
				if (swapStep.exitCode === 0) {
					verifiedPackageRoot = params.installTarget.packageRoot ?? verifiedPackageRoot;
					afterVersion = candidateVersion;
				}
			}
			const failedVerifyOrSwap = steps.find((step) => (step.name === "global install verify" || step.name === "global install swap") && step.exitCode !== 0);
			const postVerifyStep = failedVerifyOrSwap ? null : verifiedPackageRoot ? await params.postVerifyStep?.(verifiedPackageRoot) : null;
			if (postVerifyStep) steps.push(postVerifyStep);
			if (failedVerifyOrSwap && stagedInstall) afterVersion = await readPackageVersionIfPresent(livePackageRoot);
		}
		const failedStep = isBlockingPackageUpdateStep(finalInstallStep) ? finalInstallStep : steps.find((step) => step !== updateStep && isBlockingPackageUpdateStep(step)) ?? null;
		return {
			steps,
			verifiedPackageRoot,
			afterVersion,
			failedStep
		};
	} finally {
		await cleanupStagedNpmInstall(stagedInstall);
		if (packedInstallDir) await removePathBestEffort(packedInstallDir);
	}
}
//#endregion
//#region src/infra/update-runner-command.ts
const UPDATE_RUNNER_TIMEOUT_MS = 20 * 6e4;
const MAX_LOG_CHARS = 8e3;
function mergeCommandEnvironments(baseEnv, overrideEnv) {
	if (!baseEnv) return overrideEnv;
	if (!overrideEnv) return baseEnv;
	return {
		...baseEnv,
		...overrideEnv
	};
}
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,
		signal: result.signal,
		killed: result.killed,
		termination: result.termination
	});
	const stepResult = {
		name,
		command,
		cwd,
		durationMs,
		exitCode: result.code,
		stdoutTail: trimLogTail(result.stdout, MAX_LOG_CHARS),
		stderrTail,
		signal: result.signal,
		killed: result.killed,
		termination: result.termination
	};
	opts.results?.push(stepResult);
	return stepResult;
}
function normalizeFallbackFailureReason(stepName) {
	switch (stepName) {
		case "global update":
		case "global update (omit optional)":
		case "global install stage":
		case "global install verify":
		case "global install swap": return "global-install-failed";
		case "openclaw doctor": return "doctor-failed";
		case "ui:build (post-doctor repair)": return "ui-build-failed";
		default: return "unexpected-error";
	}
}
async function buildUpdateCommandRunner(runCommand) {
	const defaultCommandEnv = await createGlobalInstallEnv();
	if (runCommand) return {
		defaultCommandEnv,
		runCommand
	};
	return {
		defaultCommandEnv,
		runCommand: async (argv, options) => await runCommandWithTimeout(argv, {
			...options,
			env: mergeCommandEnvironments(defaultCommandEnv, options.env),
			killProcessTree: true
		})
	};
}
//#endregion
//#region src/infra/update-runner-doctor.ts
const UPDATE_DEFER_CONFIGURED_PLUGIN_INSTALL_REPAIR_ENV = "OPENCLAW_UPDATE_DEFER_CONFIGURED_PLUGIN_INSTALL_REPAIR";
const UPDATE_PARENT_SUPPORTS_DOCTOR_CONFIG_WRITE_ENV = "OPENCLAW_UPDATE_PARENT_SUPPORTS_DOCTOR_CONFIG_WRITE";
const UPDATE_PARENT_SUPPORTS_GATEWAY_RESTART_ENV = "OPENCLAW_UPDATE_PARENT_SUPPORTS_GATEWAY_RESTART";
const UPDATE_PARENT_ALLOWS_GATEWAY_SERVICE_REPAIR_ENV = "OPENCLAW_UPDATE_PARENT_ALLOWS_GATEWAY_SERVICE_REPAIR";
const UPDATE_PARENT_ALLOWS_GATEWAY_ACTIVATION_ENV = "OPENCLAW_UPDATE_PARENT_ALLOWS_GATEWAY_ACTIVATION";
const UPDATE_DOCTOR_SERVICE_REPAIR_POLICY_ENV = "OPENCLAW_SERVICE_REPAIR_POLICY";
const EXTERNAL_SERVICE_REPAIR_POLICY_MIN_VERSION = "2026.4.25-beta.1";
function resolveUpdateDoctorExecutionPolicy(params) {
	if (params.allowGatewayServiceRepair) return { fix: true };
	const support = compareSemverStrings(params.targetVersion, EXTERNAL_SERVICE_REPAIR_POLICY_MIN_VERSION);
	if (support !== null && support >= 0) return {
		fix: true,
		serviceRepairPolicy: "external"
	};
	return { fix: false };
}
function buildUpdateDoctorEnv(params) {
	return {
		OPENCLAW_UPDATE_IN_PROGRESS: "1",
		...params.deferConfiguredPluginInstallRepair ? { [UPDATE_DEFER_CONFIGURED_PLUGIN_INSTALL_REPAIR_ENV]: "1" } : {},
		[UPDATE_PARENT_SUPPORTS_DOCTOR_CONFIG_WRITE_ENV]: "1",
		[UPDATE_PARENT_SUPPORTS_GATEWAY_RESTART_ENV]: "1",
		[UPDATE_PARENT_ALLOWS_GATEWAY_SERVICE_REPAIR_ENV]: params.allowGatewayServiceRepair ? "1" : "0",
		[UPDATE_PARENT_ALLOWS_GATEWAY_ACTIVATION_ENV]: params.allowGatewayActivation ? "1" : "0",
		...params.serviceRepairPolicy ? { [UPDATE_DOCTOR_SERVICE_REPAIR_POLICY_ENV]: params.serviceRepairPolicy } : {},
		...params.compatibilityHostVersion ? { OPENCLAW_COMPATIBILITY_HOST_VERSION: params.compatibilityHostVersion } : {}
	};
}
//#endregion
//#region src/infra/update-package-manager.ts
async function detectBuildManager(root) {
	return await detectPackageManager(root) ?? "npm";
}
function managerPreferenceOrder(preferred) {
	if (preferred === "pnpm") return [
		"pnpm",
		"npm",
		"bun"
	];
	if (preferred === "bun") return [
		"bun",
		"npm",
		"pnpm"
	];
	return [
		"npm",
		"pnpm",
		"bun"
	];
}
async function isManagerAvailable(runCommand, manager, timeoutMs, env, expectedVersion) {
	try {
		const res = await runCommand([manager, "--version"], {
			timeoutMs,
			env
		});
		return res.code === 0 && (!expectedVersion || res.stdout.trim() === expectedVersion);
	} catch {
		return false;
	}
}
function cloneCommandEnv(env) {
	return Object.fromEntries(Object.entries(env ?? process.env).filter(([, value]) => value != null).map(([key, value]) => [key, String(value)]));
}
async function enablePnpmViaCorepack(runCommand, timeoutMs, env, expectedVersion) {
	if (!await isManagerAvailable(runCommand, "corepack", timeoutMs, env)) return "missing";
	try {
		if ((await runCommand(["corepack", "enable"], {
			timeoutMs,
			env
		})).code !== 0) return "failed";
	} catch {
		return "failed";
	}
	return await isManagerAvailable(runCommand, "pnpm", timeoutMs, env, expectedVersion) ? "enabled" : "failed";
}
async function bootstrapPnpmViaNpm(params) {
	const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-update-pnpm-"));
	const cleanup = async () => {
		await fs.rm(tempRoot, {
			recursive: true,
			force: true
		}).catch(() => {});
	};
	try {
		await fs.writeFile(path.join(tempRoot, "package.json"), JSON.stringify({
			private: true,
			allowScripts: { [`pnpm@${params.version}`]: true }
		}));
		if ((await params.runCommand([
			"npm",
			"install",
			"--prefix",
			tempRoot,
			`pnpm@${params.version}`
		], {
			timeoutMs: params.timeoutMs,
			env: params.baseEnv
		})).code !== 0) {
			await cleanup();
			return null;
		}
		const env = cloneCommandEnv(params.baseEnv);
		applyPathPrepend(env, [path.join(tempRoot, "node_modules", ".bin")]);
		if (!await isManagerAvailable(params.runCommand, "pnpm", params.timeoutMs, env, params.version)) {
			await cleanup();
			return null;
		}
		return {
			env,
			cleanup
		};
	} catch {
		await cleanup();
		return null;
	}
}
/** Resolve the package manager and environment to use for an update build. */
async function resolveUpdateBuildManager(commandRunner, root, timeoutMs, baseEnv, requirement = "allow-fallback") {
	const runCommand = (argv, options) => commandRunner(argv, {
		...options,
		cwd: root
	});
	const preferred = await detectBuildManager(root);
	const pin = await readPackageManagerSpec(root);
	const pnpmVersion = /^pnpm@(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)(?:\+.*)?$/u.exec(pin ?? "")?.[1];
	if (preferred === "pnpm") {
		if (await isManagerAvailable(runCommand, "pnpm", timeoutMs, baseEnv, pnpmVersion)) return {
			kind: "resolved",
			manager: "pnpm",
			preferred,
			fallback: false
		};
		const corepackStatus = await enablePnpmViaCorepack(runCommand, timeoutMs, baseEnv, pnpmVersion);
		if (corepackStatus === "enabled") return {
			kind: "resolved",
			manager: "pnpm",
			preferred,
			fallback: false
		};
		if (await isManagerAvailable(runCommand, "npm", timeoutMs, baseEnv) && pnpmVersion) {
			const pnpmBootstrap = await bootstrapPnpmViaNpm({
				version: pnpmVersion,
				runCommand,
				timeoutMs,
				baseEnv
			});
			if (pnpmBootstrap) return {
				kind: "resolved",
				manager: "pnpm",
				preferred,
				fallback: false,
				env: pnpmBootstrap.env,
				cleanup: pnpmBootstrap.cleanup
			};
			if (requirement === "require-preferred") return {
				kind: "missing-required",
				preferred,
				reason: "pnpm-npm-bootstrap-failed"
			};
		}
		if (requirement === "require-preferred") {
			if (corepackStatus === "missing") return {
				kind: "missing-required",
				preferred,
				reason: "pnpm-corepack-missing"
			};
			if (corepackStatus === "failed") return {
				kind: "missing-required",
				preferred,
				reason: "pnpm-corepack-enable-failed"
			};
			return {
				kind: "missing-required",
				preferred,
				reason: "preferred-manager-unavailable"
			};
		}
	}
	for (const manager of managerPreferenceOrder(preferred)) if (await isManagerAvailable(runCommand, manager, timeoutMs, baseEnv, manager === "pnpm" ? pnpmVersion : void 0)) return {
		kind: "resolved",
		manager,
		preferred,
		fallback: manager !== preferred
	};
	if (requirement === "require-preferred") return {
		kind: "missing-required",
		preferred,
		reason: "preferred-manager-unavailable"
	};
	return {
		kind: "resolved",
		manager: "npm",
		preferred,
		fallback: preferred !== "npm"
	};
}
/** Build argv for running a package-manager script. */
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
	];
}
/** Build argv for installing dependencies with a package manager. */
function managerInstallArgs(manager, opts) {
	if (manager === "pnpm") return ["pnpm", "install"];
	if (manager === "bun") return ["bun", "install"];
	if (opts?.compatFallback) return [
		"npm",
		"install",
		"--no-package-lock",
		"--legacy-peer-deps"
	];
	return ["npm", "install"];
}
/** Build argv for installing dependencies while skipping lifecycle scripts. */
function managerInstallIgnoreScriptsArgs(manager) {
	if (manager === "pnpm") return [
		"pnpm",
		"install",
		"--ignore-scripts"
	];
	if (manager === "bun") return [
		"bun",
		"install",
		"--ignore-scripts"
	];
	return [
		"npm",
		"install",
		"--ignore-scripts"
	];
}
//#endregion
//#region src/infra/update-runner-git-commands.ts
const BUILD_MAX_OLD_SPACE_MB = 8192;
const DEV_PREFLIGHT_LINT_ENV = {
	OPENCLAW_LOCAL_CHECK: "1",
	OPENCLAW_LOCAL_CHECK_MODE: "throttled"
};
const DEV_PREFLIGHT_LINT_OPT_IN_ENV = "OPENCLAW_UPDATE_PREFLIGHT_LINT";
function shouldInstallWithoutScriptsOnWindows(manager) {
	return process.platform === "win32" && manager === "pnpm";
}
function resolveBuildNodeOptions(baseOptions) {
	const current = baseOptions?.trim() ?? "";
	const desired = `--max-old-space-size=${BUILD_MAX_OLD_SPACE_MB}`;
	const existingMatch = /(?:^|\s)--max-old-space-size=(\d+)(?=\s|$)/.exec(current);
	if (!existingMatch) return current ? `${current} ${desired}` : desired;
	const existingValue = Number(existingMatch[1]);
	if (Number.isFinite(existingValue) && existingValue >= BUILD_MAX_OLD_SPACE_MB) return current;
	return current.replace(/(?:^|\s)--max-old-space-size=\d+(?=\s|$)/, ` ${desired}`).trim();
}
function resolveBuildEnv(env = process.env, buildCacheRoot) {
	return {
		...env,
		OPENCLAW_UPDATE_IN_PROGRESS: "1",
		NODE_OPTIONS: resolveBuildNodeOptions(env.NODE_OPTIONS ?? process.env.NODE_OPTIONS),
		...buildCacheRoot ? { BUILD_ALL_CACHE_ROOT: buildCacheRoot } : {}
	};
}
function gitCleanCheckArgs(gitRoot) {
	return [
		"git",
		"-C",
		gitRoot,
		"status",
		"--porcelain",
		"--",
		":!dist/control-ui/"
	];
}
async function hasExplicitPnpmPreferOfflineConfig(params) {
	try {
		const result = await params.runCommand([
			"pnpm",
			"config",
			"get",
			"prefer-offline"
		], {
			cwd: params.cwd,
			timeoutMs: params.timeoutMs,
			env: params.env
		});
		if (result.code !== 0) return true;
		const value = result.stdout.trim();
		return value !== "" && value !== "undefined" && value !== "null";
	} catch {
		return true;
	}
}
async function resolveInstallEnv(manager, env, cwd, runCommand, timeoutMs) {
	if (manager !== "pnpm") return env;
	const effectiveEnv = env ?? process.env;
	const hasExplicitPreferOffline = effectiveEnv.pnpm_config_prefer_offline !== void 0 || effectiveEnv.PNPM_CONFIG_PREFER_OFFLINE !== void 0;
	const hasConfigPreferOffline = hasExplicitPreferOffline ? false : await hasExplicitPnpmPreferOfflineConfig({
		runCommand,
		cwd,
		timeoutMs,
		env: effectiveEnv
	});
	const installEnv = {
		...env,
		PNPM_CONFIG_RESOLUTION_MODE: env?.PNPM_CONFIG_RESOLUTION_MODE ?? "highest",
		npm_config_resolution_mode: env?.npm_config_resolution_mode ?? "highest",
		pnpm_config_resolution_mode: env?.pnpm_config_resolution_mode ?? "highest"
	};
	if (!hasExplicitPreferOffline && !hasConfigPreferOffline) {
		installEnv.PNPM_CONFIG_PREFER_OFFLINE = "true";
		installEnv.pnpm_config_prefer_offline = "true";
	}
	return installEnv;
}
function isSupersededInstallFailure(step, steps) {
	return step.name === "deps install" && steps.some((candidate) => candidate.name === "deps install (ignore scripts)" && candidate.exitCode === 0);
}
function isPreflightCandidateFailure(step) {
	return /^preflight (?:reset|clean|checkout|package manager|deps install(?: \(ignore scripts\))?|build|config validate|lint) \(.+\)$/u.test(step.name);
}
function isSupersededTargetRefFailure(step, followingSteps) {
	const isTargetRefProbe = step.name.startsWith("git rev-parse ");
	const isTargetTagFetch = step.name.startsWith("git fetch ") && step.name.includes(" refs/tags/");
	const isUpstreamProbe = step.name === "upstream check";
	const isLocalDevBranchProbe = step.name === `git show-ref ${DEV_BRANCH}`;
	if (!isTargetRefProbe && !isTargetTagFetch && !isUpstreamProbe && !isLocalDevBranchProbe) return false;
	if (isLocalDevBranchProbe) return followingSteps.some((candidate) => candidate.name.startsWith(`git checkout -B main `) && candidate.exitCode === 0);
	return followingSteps.some((candidate) => candidate.name.startsWith("git rev-parse ") && candidate.exitCode === 0);
}
function findBlockingGitFailure(steps) {
	return steps.find((step, index) => step.exitCode !== 0 && !isPreflightCandidateFailure(step) && !isSupersededInstallFailure(step, steps) && !isSupersededTargetRefFailure(step, steps.slice(index + 1)));
}
function shouldRunDevPreflightLint(env = process.env) {
	const value = env[DEV_PREFLIGHT_LINT_OPT_IN_ENV]?.trim().toLowerCase();
	return value === "1" || value === "true";
}
function resolveDevPreflightLintEnv(env) {
	return {
		...env,
		...DEV_PREFLIGHT_LINT_ENV
	};
}
//#endregion
//#region src/infra/update-runner-git-preflight.ts
const PREFLIGHT_MAX_COMMITS = 10;
const PREFLIGHT_TEMP_PREFIX = process.platform === "win32" ? "ocu-pf-" : ".openclaw-update-preflight-";
const PREFLIGHT_WORKTREE_DIRNAME = process.platform === "win32" ? "wt" : "worktree";
const PREFLIGHT_CLEANUP_TIMEOUT_MS = 6e4;
const WINDOWS_PREFLIGHT_BASE_DIR = "ocu";
function normalizeDevTargetRef(value) {
	const trimmed = value?.trim();
	return trimmed ? trimmed : null;
}
function looksLikeFullCommitSha(value) {
	return /^[0-9a-f]{40}$/i.test(value.trim());
}
function resolveTagFetchRef(candidate) {
	const ref = candidate.endsWith("^{}") ? candidate.slice(0, -3) : candidate;
	return ref.startsWith("refs/tags/") ? ref : null;
}
function buildDevTargetRefResolutionCandidates(devTargetRef) {
	const trimmed = devTargetRef.trim();
	const candidates = [];
	const addCandidate = (candidate) => {
		if (candidate && !candidates.includes(candidate)) candidates.push(candidate);
	};
	if (looksLikeFullCommitSha(trimmed) || trimmed.startsWith("refs/remotes/")) {
		addCandidate(trimmed);
		return candidates;
	}
	if (trimmed.startsWith("refs/heads/")) {
		addCandidate(`refs/remotes/origin/${trimmed.slice(11)}`);
		return candidates;
	}
	if (trimmed.startsWith("origin/")) {
		addCandidate(`refs/remotes/${trimmed}`);
		return candidates;
	}
	if (trimmed.startsWith("refs/tags/")) {
		addCandidate(`${trimmed}^{}`);
		addCandidate(trimmed);
		return candidates;
	}
	addCandidate(`refs/remotes/origin/${trimmed}`);
	addCandidate(`refs/tags/${trimmed}^{}`);
	addCandidate(`refs/tags/${trimmed}`);
	return candidates;
}
function resolvePreflightWorktreeDir(preflightRoot) {
	return path.join(preflightRoot, PREFLIGHT_WORKTREE_DIRNAME);
}
async function createPreflightRoot(gitRoot) {
	const baseDir = process.platform === "win32" && path.sep === "\\" ? path.win32.join(process.env.SystemDrive ?? "C:", WINDOWS_PREFLIGHT_BASE_DIR) : path.join(await fs.realpath(gitRoot), ".artifacts");
	await fs.mkdir(baseDir, { recursive: true });
	return fs.mkdtemp(path.join(baseDir, PREFLIGHT_TEMP_PREFIX));
}
async function removePathRecursive(target) {
	await fs.rm(target, {
		recursive: true,
		force: true,
		maxRetries: 3,
		retryDelay: 200
	}).catch(() => {});
}
async function resetPreflightCandidateWorktree(worktreeDir, shortSha, step) {
	if ((await runStep(step(`preflight reset (${shortSha})`, [
		"git",
		"-C",
		worktreeDir,
		"reset",
		"--hard"
	], worktreeDir))).exitCode !== 0) return false;
	return (await runStep(step(`preflight clean (${shortSha})`, [
		"git",
		"-C",
		worktreeDir,
		"clean",
		"-fdx"
	], worktreeDir))).exitCode === 0;
}
async function repairPreflightCleanup(worktreeDir, preflightRoot) {
	try {
		await fs.rm(worktreeDir, {
			recursive: true,
			force: true,
			maxRetries: 3,
			retryDelay: 200
		});
		await fs.rm(preflightRoot, {
			recursive: true,
			force: true,
			maxRetries: 3,
			retryDelay: 200
		});
		return true;
	} catch {
		return false;
	}
}
async function resolveExplicitTarget(params) {
	for (const candidate of buildDevTargetRefResolutionCandidates(params.devTargetRef)) {
		const tagFetchRef = resolveTagFetchRef(candidate);
		if (tagFetchRef) {
			const remotes = normalizeStringEntries(((await runStep(params.step("git remote", [
				"git",
				"-C",
				params.gitRoot,
				"remote"
			], params.gitRoot))).stdoutTail ?? "").split("\n"));
			let fetchedTag = false;
			for (const remote of remotes) if ((await runStep(params.step(`git fetch ${remote} ${tagFetchRef}`, [
				"git",
				"-C",
				params.gitRoot,
				"fetch",
				remote,
				`+${tagFetchRef}:${tagFetchRef}`
			], params.gitRoot))).exitCode === 0) {
				fetchedTag = true;
				break;
			}
			if (remotes.length > 0 && !fetchedTag) continue;
		}
		const shaStep = await runStep(params.step(`git rev-parse ${candidate}`, [
			"git",
			"-C",
			params.gitRoot,
			"rev-parse",
			candidate
		], params.gitRoot));
		const sha = shaStep.stdoutTail?.trim();
		if (shaStep.exitCode === 0 && sha) return sha;
	}
	return null;
}
async function resolveUpstreamCandidates(params) {
	let localDevBranchExists = null;
	let remoteBranchRefs = [];
	if (params.needsCheckoutMain) localDevBranchExists = (await runStep(params.step(`git show-ref ${DEV_BRANCH}`, [
		"git",
		"-C",
		params.gitRoot,
		"show-ref",
		"--verify",
		`refs/heads/${DEV_BRANCH}`
	], params.gitRoot))).exitCode === 0;
	if (params.needsCheckoutMain && localDevBranchExists === false) {
		const remoteStep = await runStep(params.step("git remote", [
			"git",
			"-C",
			params.gitRoot,
			"remote"
		], params.gitRoot));
		if (remoteStep.exitCode === 0) remoteBranchRefs = normalizeStringEntries((remoteStep.stdoutTail ?? "").split("\n")).map((remote) => `refs/remotes/${remote}/${DEV_BRANCH}`);
	}
	const upstreamRefs = resolveDevUpstreamRefs(params.needsCheckoutMain, remoteBranchRefs);
	let upstreamSha = null;
	let selectedDevUpstream = null;
	let sawResolvableUpstreamRef = false;
	for (const upstreamRef of upstreamRefs) {
		if (upstreamRef.endsWith("@{upstream}")) {
			if ((await runStep(params.step("upstream check", [
				"git",
				"-C",
				params.gitRoot,
				"rev-parse",
				"--abbrev-ref",
				"--symbolic-full-name",
				upstreamRef
			], params.gitRoot))).exitCode !== 0) continue;
			sawResolvableUpstreamRef = true;
		}
		const shaStep = await runStep(params.step(`git rev-parse ${upstreamRef}`, [
			"git",
			"-C",
			params.gitRoot,
			"rev-parse",
			upstreamRef
		], params.gitRoot));
		const sha = shaStep.stdoutTail?.trim();
		if (shaStep.exitCode === 0 && sha) {
			upstreamSha = sha;
			selectedDevUpstream = /^refs\/remotes\/(.+)$/u.exec(upstreamRef)?.[1] ?? null;
			break;
		}
		if (shaStep.exitCode === 0) sawResolvableUpstreamRef = true;
	}
	if (!upstreamSha) return sawResolvableUpstreamRef ? {
		status: "error",
		reason: "no-upstream-sha"
	} : {
		status: "skipped",
		reason: "no-upstream"
	};
	const revListStep = await runStep(params.step("git rev-list", [
		"git",
		"-C",
		params.gitRoot,
		"rev-list",
		`--max-count=${PREFLIGHT_MAX_COMMITS}`,
		upstreamSha
	], params.gitRoot));
	if (revListStep.exitCode !== 0) return {
		status: "error",
		reason: "preflight-revlist-failed"
	};
	const candidates = normalizeStringEntries((revListStep.stdoutTail ?? "").split("\n"));
	if (candidates.length === 0) return {
		status: "error",
		reason: "preflight-no-candidates"
	};
	return {
		status: "ok",
		sha: upstreamSha,
		candidates,
		selectedDevUpstream,
		localDevBranchExists
	};
}
function classifyPreflightFailure(step) {
	const output = stripAnsi(`${step.stdoutTail ?? ""}\n${step.stderrTail ?? ""}`);
	const nodeNoSpace = /^\s*(?:\[(?:ERR_PNPM_)?ENOSPC\][^\r\n]*|(?:Error:\s*)?)ENOSPC: no space left on device(?:,|$)/m.test(output);
	const gitNoSpace = /^(?:fatal|error): (?:cannot|could not|unable to) [^\r\n]+: No space left on device$/m.test(output);
	return nodeNoSpace || gitNoSpace ? "insufficient-space" : "failed";
}
async function testPreflightCandidate(params) {
	const shortSha = params.sha.slice(0, 8);
	if (!await resetPreflightCandidateWorktree(params.worktreeDir, shortSha, params.step)) return { status: "failed" };
	const runCandidateCheck = async (name, argv, env) => {
		const result = await runStep(params.step(`preflight ${name} (${shortSha})`, argv, params.worktreeDir, env));
		return result.exitCode === 0 ? null : result;
	};
	const checkout = await runCandidateCheck("checkout", [
		"git",
		"-C",
		params.worktreeDir,
		"checkout",
		"--detach",
		params.sha
	]);
	if (checkout) return { status: classifyPreflightFailure(checkout) };
	const manager = await resolveUpdateBuildManager(params.runCommand, params.worktreeDir, params.timeoutMs, params.defaultCommandEnv, "require-preferred");
	if (manager.kind === "missing-required") {
		params.steps.push({
			name: `preflight package manager (${shortSha})`,
			command: `resolve ${manager.preferred} package manager`,
			cwd: params.worktreeDir,
			durationMs: 0,
			exitCode: 1,
			stderrTail: manager.reason
		});
		return {
			status: "manager-unavailable",
			reason: manager.reason
		};
	}
	try {
		const preferIgnoreScripts = shouldInstallWithoutScriptsOnWindows(manager.manager);
		const ignoreScriptsArgv = managerInstallIgnoreScriptsArgs(manager.manager);
		const installArgv = preferIgnoreScripts && ignoreScriptsArgv ? ignoreScriptsArgv : managerInstallArgs(manager.manager, { compatFallback: manager.fallback && manager.manager === "npm" });
		const installName = preferIgnoreScripts ? "deps install (ignore scripts)" : "deps install";
		const installEnv = await resolveInstallEnv(manager.manager, manager.env ?? params.defaultCommandEnv, params.worktreeDir, params.runCommand, params.timeoutMs);
		const buildArgs = managerScriptArgs(manager.manager, "build");
		const buildEnv = resolveBuildEnv(manager.env ?? params.defaultCommandEnv, path.join(params.gitRoot, ".artifacts", "build-all-cache"));
		const configArgs = managerScriptArgs(manager.manager, "openclaw", [
			"config",
			"validate",
			"--json"
		]);
		const lintArgs = managerScriptArgs(manager.manager, "lint");
		const failure = await runCandidateCheck(installName, installArgv, installEnv) ?? await runCandidateCheck("build", buildArgs, buildEnv) ?? await runCandidateCheck("config validate", configArgs, manager.env) ?? (shouldRunDevPreflightLint() ? await runCandidateCheck("lint", lintArgs, resolveDevPreflightLintEnv(manager.env)) : null);
		return failure ? { status: classifyPreflightFailure(failure) } : {
			status: "ok",
			selectedSha: params.sha
		};
	} finally {
		await manager.cleanup?.();
	}
}
async function runGitDevPreflight(params) {
	const devTargetRef = params.devTarget ? normalizeDevTargetRef(resolveDevUpdateTargetRevision(params.devTarget)) : null;
	let preflightBaseSha;
	let candidates;
	let selectedDevUpstream = null;
	let localDevBranchExists = null;
	if (devTargetRef) {
		const targetSha = await resolveExplicitTarget({
			...params,
			devTargetRef
		});
		if (!targetSha) return {
			status: "error",
			reason: "no-target-sha"
		};
		preflightBaseSha = targetSha;
		candidates = [targetSha];
		if (params.devTarget?.mode === "tracked") {
			if ((await runStep(params.step("tracked target ancestry", [
				"git",
				"-C",
				params.gitRoot,
				"merge-base",
				"--is-ancestor",
				targetSha,
				`${params.devTarget.upstreamRef}^{commit}`
			], params.gitRoot))).exitCode !== 0) return {
				status: "error",
				reason: "tracked-upstream-invalid"
			};
		}
	} else {
		const upstream = await resolveUpstreamCandidates(params);
		if (upstream.status !== "ok") return upstream;
		preflightBaseSha = upstream.sha;
		candidates = upstream.candidates;
		selectedDevUpstream = upstream.selectedDevUpstream;
		localDevBranchExists = upstream.localDevBranchExists;
	}
	let preflightRoot;
	try {
		preflightRoot = await createPreflightRoot(params.gitRoot);
	} catch (error) {
		return {
			status: "error",
			reason: hasErrnoCode(error, "ENOSPC") ? "preflight-insufficient-space" : "preflight-worktree-failed"
		};
	}
	const worktreeDir = resolvePreflightWorktreeDir(preflightRoot);
	const worktreeStep = await runStep(params.step("preflight worktree", [
		"git",
		"-C",
		params.gitRoot,
		"worktree",
		"add",
		"--detach",
		worktreeDir,
		preflightBaseSha
	], params.gitRoot));
	if (worktreeStep.exitCode !== 0) {
		await removePathRecursive(preflightRoot);
		return {
			status: "error",
			reason: classifyPreflightFailure(worktreeStep) === "insufficient-space" ? "preflight-insufficient-space" : "preflight-worktree-failed"
		};
	}
	let tested;
	try {
		for (const sha of candidates) {
			const candidate = await testPreflightCandidate({
				...params,
				worktreeDir,
				sha
			});
			if (candidate.status === "ok" || candidate.status === "insufficient-space") {
				tested = candidate;
				break;
			}
			if (tested?.status !== "failed") tested = candidate;
		}
	} finally {
		const removeStep = await runStep({
			...params.step("preflight cleanup", [
				"git",
				"-C",
				params.gitRoot,
				"worktree",
				"remove",
				"--force",
				worktreeDir
			], params.gitRoot),
			timeoutMs: Math.min(params.timeoutMs, PREFLIGHT_CLEANUP_TIMEOUT_MS)
		});
		if (removeStep.exitCode !== 0 && await repairPreflightCleanup(worktreeDir, preflightRoot)) {
			removeStep.exitCode = 0;
			const message = process.platform === "win32" ? "windows fallback cleanup removed preflight tree" : "fallback cleanup removed preflight tree";
			removeStep.stderrTail = trimLogTail([removeStep.stderrTail, message].filter(Boolean).join("\n"), MAX_LOG_CHARS);
		}
		await params.runCommand([
			"git",
			"-C",
			params.gitRoot,
			"worktree",
			"prune"
		], {
			cwd: params.gitRoot,
			timeoutMs: params.timeoutMs
		}).catch(() => null);
		await removePathRecursive(preflightRoot);
	}
	if (tested?.status !== "ok") return {
		status: "error",
		reason: tested?.status === "insufficient-space" ? "preflight-insufficient-space" : tested?.status === "manager-unavailable" ? tested.reason : "preflight-no-good-commit"
	};
	return {
		status: "ok",
		selectedSha: tested.selectedSha,
		selectedDevUpstream,
		localDevBranchExists
	};
}
//#endregion
//#region src/infra/update-runner-git-recovery.ts
async function rebuildRolledBackGitRuntime(params) {
	const appendStep = async (name, argv, env) => {
		return (await runStep({
			runCommand: params.runCommand,
			name,
			argv,
			cwd: params.gitRoot,
			timeoutMs: params.timeoutMs,
			env,
			stepIndex: 0,
			totalSteps: 1,
			results: params.steps
		})).exitCode === 0;
	};
	const appendFailure = (reason, detail) => {
		params.steps.push({
			name: "git rollback runtime verify",
			command: `verify rollback runtime ${params.expectedSha}`,
			cwd: params.gitRoot,
			durationMs: 0,
			exitCode: 1,
			stderrTail: detail
		});
		return {
			serviceRestartSafe: false,
			reason
		};
	};
	const manager = await resolveUpdateBuildManager(params.runCommand, params.gitRoot, params.timeoutMs, params.defaultCommandEnv, "require-preferred");
	if (manager.kind === "missing-required") return appendFailure("manager-unavailable", manager.reason);
	try {
		const installEnv = await resolveInstallEnv(manager.manager, manager.env ?? params.defaultCommandEnv, params.gitRoot, params.runCommand, params.timeoutMs);
		let installed = await appendStep("git rollback deps install", managerInstallArgs(manager.manager, { compatFallback: manager.fallback && manager.manager === "npm" }), installEnv);
		if (!installed && shouldInstallWithoutScriptsOnWindows(manager.manager)) {
			const retryArgv = managerInstallIgnoreScriptsArgs(manager.manager);
			installed = retryArgv ? await appendStep("git rollback deps install (ignore scripts)", retryArgv, installEnv) : false;
		}
		if (!installed) return appendFailure("deps-install-failed", "failed to restore dependencies");
		if (!await appendStep("git rollback build", managerScriptArgs(manager.manager, "build"), resolveBuildEnv(manager.env ?? params.defaultCommandEnv, params.channel === "dev" ? path.join(params.gitRoot, ".artifacts", "build-all-cache") : void 0))) return appendFailure("build-failed", "failed to rebuild the original checkout");
		const cleanCheck = await runStep({
			runCommand: params.runCommand,
			name: "git rollback build clean check",
			argv: gitCleanCheckArgs(params.gitRoot),
			cwd: params.gitRoot,
			timeoutMs: params.timeoutMs,
			stepIndex: 0,
			totalSteps: 1,
			results: params.steps
		});
		if (cleanCheck.exitCode !== 0) return appendFailure("runtime-verification-failed", "failed to verify rollback checkout cleanliness");
		if (cleanCheck.stdoutTail?.trim()) return appendFailure("rollback-checkout-dirty", `rollback build left checkout dirty: ${cleanCheck.stdoutTail.trim()}`);
		const runtimeErrors = await collectGitRuntimeErrors({
			root: params.gitRoot,
			sha: params.expectedSha
		});
		const verified = runtimeErrors.length === 0;
		params.steps.push({
			name: "git rollback runtime verify",
			command: `verify rollback runtime ${params.expectedSha}`,
			cwd: params.gitRoot,
			durationMs: 0,
			exitCode: verified ? 0 : 1,
			...verified ? {} : { stderrTail: runtimeErrors.join("\n") }
		});
		return verified ? { serviceRestartSafe: true } : {
			serviceRestartSafe: false,
			reason: "runtime-verification-failed"
		};
	} finally {
		await manager.cleanup?.();
	}
}
//#endregion
//#region src/infra/update-runner-git-target.ts
async function readGitTargetSchemaVersions(params) {
	let result;
	try {
		result = await params.runCommand([
			"git",
			"-C",
			params.root,
			"show",
			`${params.revision}:package.json`
		], {
			cwd: params.root,
			timeoutMs: params.timeoutMs
		});
	} catch (error) {
		return {
			status: "unreadable",
			reason: String(error)
		};
	}
	if (result.code !== 0) return {
		status: "unreadable",
		reason: `git show ${params.revision}:package.json exited ${result.code}`
	};
	try {
		const schemaVersions = parsePackageOpenClawSchemaVersions(JSON.parse(result.stdout));
		return {
			status: "ok",
			...schemaVersions ? { schemaVersions } : {}
		};
	} catch (error) {
		return {
			status: "unreadable",
			reason: `target package.json unparseable: ${String(error)}`
		};
	}
}
async function prepareGitMutation(params) {
	const target = await readGitTargetSchemaVersions(params);
	return await params.beforeGitMutation?.(target.status === "ok" ? target.schemaVersions ? { schemaVersions: target.schemaVersions } : {} : { metadataUnreadable: target.reason }) ?? {};
}
async function readBranchName(runCommand, root, timeoutMs) {
	const result = await runCommand([
		"git",
		"-C",
		root,
		"rev-parse",
		"--abbrev-ref",
		"HEAD"
	], { timeoutMs }).catch(() => null);
	return (result?.code === 0 ? result.stdout.trim() : "") || null;
}
async function listGitTags(runCommand, root, timeoutMs) {
	const result = await runCommand([
		"git",
		"-C",
		root,
		"tag",
		"--list",
		"v*",
		"--sort=-v:refname"
	], { timeoutMs }).catch(() => null);
	return result?.code === 0 ? normalizeStringEntries(result.stdout.split("\n")) : [];
}
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 comparison = compareSemverStrings(betaTag, stableTag);
		return comparison != null && comparison < 0 ? stableTag : betaTag;
	}
	return tags.find((tag) => isStableTag(tag)) ?? null;
}
//#endregion
//#region src/infra/update-runner-git.ts
async function readBuiltGatewayBuildId(gitRoot) {
	try {
		const raw = await fs.readFile(path.join(gitRoot, "dist", "build-info.json"), "utf8");
		const parsed = JSON.parse(raw);
		const buildId = isRecord(parsed) ? parsed.buildId : void 0;
		return typeof buildId === "string" && buildId.trim() ? buildId.trim() : null;
	} catch {
		return null;
	}
}
async function updateGitCheckout(params) {
	const { opts, gitRoot, runCommand, defaultCommandEnv, timeoutMs, startedAt } = params;
	const channel = opts.channel ?? "dev";
	if (channel === "extended-stable") return {
		status: "error",
		mode: "git",
		root: gitRoot,
		reason: "unsupported_git_channel",
		steps: [],
		durationMs: Date.now() - startedAt
	};
	const beforeSha = (await runCommand([
		"git",
		"-C",
		gitRoot,
		"rev-parse",
		"HEAD"
	], {
		cwd: gitRoot,
		timeoutMs
	})).stdout.trim() || null;
	const beforeVersion = await readPackageVersion(gitRoot);
	const branch = await readBranchName(runCommand, gitRoot, timeoutMs);
	const devTarget = channel === "dev" ? opts.devTarget : void 0;
	const hasDevTarget = devTarget !== void 0;
	const needsCheckoutMain = channel === "dev" && !hasDevTarget && branch !== "main";
	const totalSteps = channel === "dev" ? needsCheckoutMain ? 12 : 11 : 9;
	const steps = [];
	let stepIndex = 0;
	const step = (name, argv, cwd, env) => ({
		runCommand,
		name,
		argv,
		cwd,
		timeoutMs,
		env,
		progress: opts.progress,
		stepIndex: stepIndex++,
		totalSteps,
		results: steps
	});
	let allowGatewayServiceRepair = opts.allowGatewayServiceRepair !== false;
	let allowGatewayActivation = opts.allowGatewayActivation === true;
	let mutationPrepared = false;
	let createdDevBranchDuringUpdate = false;
	let devPreflight;
	let liveBuildStarted = false;
	let recovery;
	const prepareMutation = async (revision) => {
		const preparation = await prepareGitMutation({
			runCommand,
			root: gitRoot,
			revision,
			timeoutMs,
			beforeGitMutation: opts.beforeGitMutation
		});
		allowGatewayServiceRepair = preparation.allowGatewayServiceRepair ?? allowGatewayServiceRepair;
		allowGatewayActivation = preparation.allowGatewayActivation ?? allowGatewayActivation;
		mutationPrepared = true;
	};
	const buildError = (reason, status = "error") => ({
		status,
		mode: "git",
		root: gitRoot,
		reason,
		before: {
			sha: beforeSha,
			version: beforeVersion
		},
		...recovery ? { recovery } : {},
		steps,
		durationMs: Date.now() - startedAt
	});
	const runRequiredStep = async (name, argv, reason) => {
		return (await runStep(step(name, argv, gitRoot))).exitCode === 0 ? null : buildError(reason);
	};
	const appendRecoveryStep = async (name, argv) => {
		return (await runStep({
			runCommand,
			name,
			argv,
			cwd: gitRoot,
			timeoutMs,
			stepIndex: 0,
			totalSteps: 1,
			results: steps
		})).exitCode === 0;
	};
	const verifyRollbackHead = async () => {
		if (!beforeSha) return false;
		const result = await runStep({
			runCommand,
			name: "git rollback verify HEAD",
			argv: [
				"git",
				"-C",
				gitRoot,
				"rev-parse",
				"HEAD"
			],
			cwd: gitRoot,
			timeoutMs,
			stepIndex: 0,
			totalSteps: 1,
			results: steps
		});
		const verified = result.exitCode === 0 && result.stdoutTail?.trim() === beforeSha;
		result.exitCode = verified ? 0 : 1;
		if (!verified) result.stderrTail = `expected ${beforeSha}, found ${result.stdoutTail?.trim() || "unreadable HEAD"}`;
		return verified;
	};
	const rollback = async () => {
		if (!beforeSha) return false;
		let restored = await appendRecoveryStep("git rollback clean", [
			"git",
			"-C",
			gitRoot,
			"reset",
			"--hard"
		]);
		restored = await appendRecoveryStep("git rollback clean untracked", [
			"git",
			"-C",
			gitRoot,
			"clean",
			"-fd",
			"-e",
			"dist/control-ui/"
		]) && restored;
		if (branch && branch !== "HEAD") {
			const checkedOut = await appendRecoveryStep("git rollback checkout", [
				"git",
				"-C",
				gitRoot,
				"checkout",
				"--force",
				branch
			]);
			if (checkedOut) {
				restored = await appendRecoveryStep("git rollback reset", [
					"git",
					"-C",
					gitRoot,
					"reset",
					"--hard",
					beforeSha
				]) && restored;
				if (createdDevBranchDuringUpdate) await appendRecoveryStep(`git rollback delete ${DEV_BRANCH}`, [
					"git",
					"-C",
					gitRoot,
					"branch",
					"-D",
					DEV_BRANCH
				]);
			}
			const verified = await verifyRollbackHead();
			return restored && checkedOut && verified;
		}
		restored = await appendRecoveryStep("git rollback checkout", [
			"git",
			"-C",
			gitRoot,
			"checkout",
			"--detach",
			beforeSha
		]) && restored;
		if (createdDevBranchDuringUpdate) await appendRecoveryStep(`git rollback delete ${DEV_BRANCH}`, [
			"git",
			"-C",
			gitRoot,
			"branch",
			"-D",
			DEV_BRANCH
		]);
		const verified = await verifyRollbackHead();
		return restored && verified;
	};
	const rollbackError = async (reason) => {
		const sourceRestored = await rollback();
		if (mutationPrepared) recovery = sourceRestored ? { serviceRestartSafe: true } : {
			serviceRestartSafe: false,
			reason: "source-rollback-failed"
		};
		if (sourceRestored && liveBuildStarted && beforeSha) recovery = await rebuildRolledBackGitRuntime({
			gitRoot,
			expectedSha: beforeSha,
			channel,
			runCommand,
			defaultCommandEnv,
			timeoutMs,
			steps
		});
		return buildError(reason);
	};
	if ((await runStep(step("clean check", gitCleanCheckArgs(gitRoot), gitRoot))).stdoutTail?.trim()) return buildError("dirty", "skipped");
	if (channel === "dev") {
		const fetchFailure = await runRequiredStep("git fetch", [
			"git",
			"-C",
			gitRoot,
			"fetch",
			"--all",
			"--prune",
			"--no-tags"
		], "fetch-failed");
		if (fetchFailure) return fetchFailure;
		devPreflight = await runGitDevPreflight({
			gitRoot,
			devTarget,
			needsCheckoutMain,
			runCommand,
			timeoutMs,
			defaultCommandEnv,
			steps,
			step
		});
		const preflight = devPreflight;
		if (preflight.status !== "ok") return buildError(preflight.reason, preflight.status);
		await prepareMutation(preflight.selectedSha);
		if (hasDevTarget) {
			const failure = await runRequiredStep(`git checkout ${preflight.selectedSha}`, [
				"git",
				"-C",
				gitRoot,
				"checkout",
				"--detach",
				preflight.selectedSha
			], "checkout-failed");
			if (failure) return failure;
		} else {
			let createdAtSelectedSha = false;
			if (needsCheckoutMain) {
				const hasLocalMain = preflight.localDevBranchExists !== false;
				const failure = await runRequiredStep(hasLocalMain ? `git checkout ${DEV_BRANCH}` : `git checkout -B ${DEV_BRANCH} ${preflight.selectedSha}`, hasLocalMain ? [
					"git",
					"-C",
					gitRoot,
					"checkout",
					DEV_BRANCH
				] : [
					"git",
					"-C",
					gitRoot,
					"checkout",
					"-B",
					DEV_BRANCH,
					preflight.selectedSha
				], "checkout-failed");
				if (failure) return failure;
				createdAtSelectedSha = !hasLocalMain;
				createdDevBranchDuringUpdate = createdAtSelectedSha;
				if (createdAtSelectedSha && preflight.selectedDevUpstream) {
					if (await runRequiredStep(`git branch --set-upstream-to ${preflight.selectedDevUpstream} main`, [
						"git",
						"-C",
						gitRoot,
						"branch",
						"--set-upstream-to",
						preflight.selectedDevUpstream,
						"main"
					], "checkout-failed")) return await rollbackError("checkout-failed");
				}
			}
			if (createdAtSelectedSha) steps.push({
				name: "git rebase",
				command: `git rebase ${preflight.selectedSha}`,
				cwd: gitRoot,
				durationMs: 0,
				exitCode: 0,
				stdoutTail: `skipped; ${DEV_BRANCH} was created at selected preflight SHA`
			});
			else if ((await runStep(step("git rebase", [
				"git",
				"-C",
				gitRoot,
				"rebase",
				preflight.selectedSha
			], gitRoot))).exitCode !== 0) {
				await runStep({
					runCommand,
					name: "git rebase --abort",
					argv: [
						"git",
						"-C",
						gitRoot,
						"rebase",
						"--abort"
					],
					cwd: gitRoot,
					timeoutMs,
					stepIndex: 0,
					totalSteps: 1,
					results: steps
				});
				return buildError("rebase-failed");
			}
		}
	} else {
		const fetchFailure = await runRequiredStep("git fetch", [
			"git",
			"-C",
			gitRoot,
			"fetch",
			"--all",
			"--prune",
			"--tags"
		], "fetch-failed");
		if (fetchFailure) return fetchFailure;
		const tag = await resolveChannelTag(runCommand, gitRoot, timeoutMs, channel);
		if (!tag) return buildError("no-release-tag");
		await prepareMutation(tag);
		const failure = await runRequiredStep(`git checkout ${tag}`, [
			"git",
			"-C",
			gitRoot,
			"checkout",
			"--detach",
			tag
		], "checkout-failed");
		if (failure) return failure;
	}
	const manager = await resolveUpdateBuildManager(runCommand, gitRoot, timeoutMs, defaultCommandEnv, "require-preferred");
	if (manager.kind === "missing-required") return await rollbackError(manager.reason);
	try {
		const installEnv = await resolveInstallEnv(manager.manager, manager.env ?? defaultCommandEnv, gitRoot, runCommand, timeoutMs);
		let installStep = await runStep(step("deps install", managerInstallArgs(manager.manager, { compatFallback: manager.fallback && manager.manager === "npm" }), gitRoot, installEnv));
		if (installStep.exitCode !== 0 && shouldInstallWithoutScriptsOnWindows(manager.manager)) {
			const retryArgv = managerInstallIgnoreScriptsArgs(manager.manager);
			if (retryArgv) installStep = await runStep(step("deps install (ignore scripts)", retryArgv, gitRoot, installEnv));
		}
		if (installStep.exitCode !== 0) return await rollbackError("deps-install-failed");
		liveBuildStarted = true;
		if ((await runStep(step("build", managerScriptArgs(manager.manager, "build"), gitRoot, resolveBuildEnv(manager.env ?? defaultCommandEnv, channel === "dev" ? path.join(gitRoot, ".artifacts", "build-all-cache") : void 0)))).exitCode !== 0) return await rollbackError("build-failed");
		const buildCleanCheck = await runStep(step("build clean check", gitCleanCheckArgs(gitRoot), gitRoot));
		if (buildCleanCheck.exitCode !== 0) return await rollbackError("build-failed");
		if (buildCleanCheck.stdoutTail?.trim()) return await rollbackError("build-dirty");
		if ((await resolveControlUiAssetHealth({ root: gitRoot })).kind !== "ready") {
			if ((await runStep(step("ui:build (build fallback)", managerScriptArgs(manager.manager, "ui:build"), gitRoot, manager.env))).exitCode !== 0) return await rollbackError("ui-build-failed");
		}
		const doctorEntry = path.join(gitRoot, "openclaw.mjs");
		if (!await fs.stat(doctorEntry).then(() => true, () => false)) {
			steps.push({
				name: "openclaw doctor entry",
				command: `verify ${doctorEntry}`,
				cwd: gitRoot,
				durationMs: 0,
				exitCode: 1,
				stderrTail: `missing ${doctorEntry}`
			});
			return await rollbackError("doctor-entry-missing");
		}
		const doctorNodePath = await resolveStableNodePath(process.execPath);
		const doctorPolicy = resolveUpdateDoctorExecutionPolicy({
			targetVersion: await readPackageVersion(gitRoot),
			allowGatewayServiceRepair
		});
		if ((await runStep(step("openclaw doctor", [
			doctorNodePath,
			doctorEntry,
			"doctor",
			"--non-interactive",
			...doctorPolicy.fix ? ["--fix"] : []
		], gitRoot, buildUpdateDoctorEnv({
			allowGatewayServiceRepair,
			allowGatewayActivation,
			serviceRepairPolicy: doctorPolicy.serviceRepairPolicy,
			deferConfiguredPluginInstallRepair: opts.deferConfiguredPluginInstallRepair
		})))).exitCode !== 0) return await rollbackError("doctor-failed");
		if ((await resolveControlUiAssetHealth({ root: gitRoot })).kind !== "ready") {
			if ((await runStep({
				runCommand,
				name: "ui:build (post-doctor repair)",
				argv: managerScriptArgs(manager.manager, "ui:build"),
				cwd: gitRoot,
				timeoutMs,
				env: manager.env,
				stepIndex: 0,
				totalSteps: 1,
				results: steps
			})).exitCode !== 0) return await rollbackError("ui-build-failed");
			const repairedHealth = await resolveControlUiAssetHealth({ root: gitRoot });
			if (repairedHealth.kind !== "ready") {
				const uiIndexPath = repairedHealth.indexPath ?? resolveControlUiDistIndexPathForRoot(gitRoot);
				steps.push({
					name: "ui assets verify",
					command: `verify ${uiIndexPath}`,
					cwd: gitRoot,
					durationMs: 0,
					exitCode: 1,
					stderrTail: repairedHealth.kind === "incomplete" ? `missing startup asset ${repairedHealth.missingAsset} referenced by ${uiIndexPath}` : `missing ${uiIndexPath}`
				});
				return await rollbackError("ui-assets-missing");
			}
		}
		const failedStep = findBlockingGitFailure(steps);
		const afterBuildId = channel === "dev" ? await readBuiltGatewayBuildId(gitRoot) : null;
		const afterShaStep = await runStep(step("git rev-parse HEAD (after)", [
			"git",
			"-C",
			gitRoot,
			"rev-parse",
			"HEAD"
		], gitRoot));
		if (afterShaStep.exitCode !== 0) return await rollbackError("head-verification-failed");
		if (devTarget?.mode === "tracked" && devPreflight?.status === "ok" && afterShaStep.stdoutTail?.trim() !== devPreflight.selectedSha) return await rollbackError("target-sha-mismatch");
		return {
			status: failedStep ? "error" : "ok",
			mode: "git",
			root: gitRoot,
			reason: failedStep ? normalizeFallbackFailureReason(failedStep.name) : void 0,
			before: {
				sha: beforeSha,
				version: beforeVersion
			},
			after: {
				sha: afterShaStep.stdoutTail?.trim() ?? null,
				version: await readPackageVersion(gitRoot),
				...afterBuildId ? { buildId: afterBuildId } : {},
				...!failedStep && devTarget?.mode === "tracked" ? { upstreamRef: devTarget.upstreamRef } : {}
			},
			steps,
			durationMs: Date.now() - startedAt
		};
	} finally {
		await manager.cleanup?.();
	}
}
//#endregion
//#region src/infra/update-runner-global.ts
const DEFAULT_PACKAGE_NAME = "openclaw";
function normalizeTag(tag) {
	return normalizePackageTagInput(tag, ["openclaw", DEFAULT_PACKAGE_NAME]) ?? "latest";
}
async function runGlobalUpdate(params) {
	const { opts, pkgRoot, globalManager, runCommand, timeoutMs, startedAt, beforeVersion, allowGatewayServiceRepair, allowGatewayActivation } = params;
	const channel = opts.channel ?? "stable";
	if (channel === "extended-stable" && opts.tag !== void 0) return {
		status: "error",
		mode: globalManager,
		root: pkgRoot,
		reason: EXTENDED_STABLE_TAG_UNSUPPORTED_REASON,
		before: { version: beforeVersion },
		steps: [],
		durationMs: Date.now() - startedAt
	};
	const packageName = await readPackageName(pkgRoot) ?? DEFAULT_PACKAGE_NAME;
	const installTarget = await resolveGlobalInstallTarget({
		manager: globalManager,
		runCommand,
		timeoutMs,
		pkgRoot,
		packageName
	});
	const extendedStable = channel === "extended-stable" ? await resolveExtendedStablePackage({
		installKind: "package",
		timeoutMs,
		packageName
	}) : null;
	if (extendedStable?.status === "failed") return {
		status: "error",
		mode: globalManager,
		root: pkgRoot,
		reason: extendedStable.reason,
		before: { version: beforeVersion },
		steps: [],
		durationMs: Date.now() - startedAt
	};
	const tag = normalizeTag(extendedStable?.status === "resolved" ? extendedStable.version : opts.tag ?? channelToNpmTag(channel));
	const globalInstallEnv = await createGlobalInstallEnv();
	const packageUpdate = await runGlobalPackageUpdateSteps({
		installTarget,
		installSpec: extendedStable?.status === "resolved" ? extendedStable.packageSpec : resolveGlobalInstallSpec({
			packageName,
			tag,
			env: globalInstallEnv
		}),
		packageName,
		packageRoot: pkgRoot,
		runCommand,
		timeoutMs,
		...globalInstallEnv === void 0 ? {} : { env: globalInstallEnv },
		installCwd: pkgRoot,
		runStep: (stepParams) => runStep({
			runCommand,
			...stepParams,
			cwd: stepParams.cwd ?? pkgRoot,
			progress: opts.progress,
			stepIndex: 0,
			totalSteps: 1
		}),
		postVerifyStep: async (verifiedPackageRoot) => {
			const doctorEntry = await resolveGatewayInstallEntrypoint(verifiedPackageRoot);
			if (!doctorEntry) return null;
			const doctorNodePath = await resolveStableNodePath(process.execPath);
			const candidateHostVersion = await readPackageVersion(verifiedPackageRoot);
			const doctorPolicy = resolveUpdateDoctorExecutionPolicy({
				targetVersion: candidateHostVersion,
				allowGatewayServiceRepair
			});
			return await runStep({
				runCommand,
				name: "openclaw doctor",
				argv: [
					doctorNodePath,
					doctorEntry,
					"doctor",
					"--non-interactive",
					...doctorPolicy.fix ? ["--fix"] : []
				],
				cwd: verifiedPackageRoot,
				timeoutMs,
				env: buildUpdateDoctorEnv({
					allowGatewayServiceRepair,
					allowGatewayActivation,
					serviceRepairPolicy: doctorPolicy.serviceRepairPolicy,
					compatibilityHostVersion: candidateHostVersion
				}),
				progress: opts.progress,
				stepIndex: 0,
				totalSteps: 1
			});
		}
	});
	return {
		status: packageUpdate.failedStep ? "error" : "ok",
		mode: globalManager,
		root: packageUpdate.verifiedPackageRoot ?? pkgRoot,
		reason: packageUpdate.failedStep ? normalizeFallbackFailureReason(packageUpdate.failedStep.name) : void 0,
		before: { version: beforeVersion },
		after: { version: packageUpdate.afterVersion },
		steps: packageUpdate.steps,
		durationMs: Date.now() - startedAt
	};
}
//#endregion
//#region src/infra/update-runner-install-surface.ts
const CORE_PACKAGE_NAMES = /* @__PURE__ */ new Set(["openclaw"]);
function normalizeDir(value) {
	if (!value) return null;
	const trimmed = value.trim();
	return trimmed ? path.resolve(trimmed) : null;
}
function resolveNodeModulesBinPackageRoot(argv1) {
	const normalized = path.resolve(argv1);
	const parts = normalized.split(path.sep);
	const binIndex = parts.lastIndexOf(".bin");
	if (binIndex <= 0 || 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 argv1 = normalizeDir(opts.argv1);
	if (argv1) {
		dirs.push(path.dirname(argv1));
		const packageRoot = resolveNodeModulesBinPackageRoot(argv1);
		if (packageRoot) dirs.push(packageRoot);
	}
	const cwd = normalizeDir(opts.cwd);
	if (cwd) dirs.push(cwd);
	let processCwd;
	try {
		processCwd = normalizeDir(process.cwd());
	} catch {
		processCwd = null;
	}
	if (processCwd) dirs.push(processCwd);
	return uniqueStrings(dirs);
}
async function resolveGitRoot(runCommand, candidates, timeoutMs, packageRoot) {
	for (const dir of candidates) {
		const result = await runCommand([
			"git",
			"-C",
			dir,
			"rev-parse",
			"--show-toplevel"
		], { timeoutMs }).catch(() => null);
		const root = result?.code === 0 ? result.stdout.trim() : "";
		if (root && (!packageRoot || updateInstallRootsMatch(root, packageRoot))) return root;
	}
	return null;
}
async function findPackageRoot(candidates) {
	for (const dir of candidates) {
		let current = dir;
		for (let index = 0; index < 12; index += 1) {
			try {
				const raw = await fs.readFile(path.join(current, "package.json"), "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 looksLikeGitCheckout(root) {
	try {
		await fs.access(path.join(root, ".git"));
		return true;
	} catch {
		return false;
	}
}
async function resolveUpdateInstallSurface(opts) {
	const root = opts.root;
	if (!root || opts.installKind === "unknown") return {
		kind: "missing",
		mode: "unknown"
	};
	if (opts.installKind === "git") return {
		kind: "git",
		mode: "git",
		root,
		packageRoot: root
	};
	const { runCommand } = await buildUpdateCommandRunner(opts.runCommand);
	const globalManager = await detectGlobalInstallManagerForRoot(runCommand, root, opts.timeoutMs ?? 12e5);
	if (globalManager) return {
		kind: "global",
		mode: globalManager,
		root,
		packageRoot: root
	};
	return {
		kind: "package-root",
		mode: "unknown",
		root,
		packageRoot: root
	};
}
//#endregion
//#region src/infra/update-runner.ts
async function runGatewayUpdate(opts = {}) {
	const startedAt = Date.now();
	const { defaultCommandEnv, runCommand } = await buildUpdateCommandRunner(opts.runCommand);
	const timeoutMs = opts.timeoutMs ?? 12e5;
	const candidates = buildStartDirs(opts);
	const pkgRoot = await findPackageRoot(candidates);
	let gitRoot = await resolveGitRoot(runCommand, candidates, timeoutMs, pkgRoot);
	if (!gitRoot && pkgRoot) {
		const cwdRoot = normalizeDir(opts.cwd);
		if (cwdRoot && updateInstallRootsMatch(cwdRoot, pkgRoot) && await looksLikeGitCheckout(cwdRoot)) gitRoot = resolveUpdateInstallRoot(cwdRoot);
	}
	if (gitRoot && !pkgRoot) return {
		status: "error",
		mode: "unknown",
		root: gitRoot,
		reason: "not-openclaw-root",
		steps: [],
		durationMs: Date.now() - startedAt
	};
	if (gitRoot && pkgRoot) return await updateGitCheckout({
		opts,
		gitRoot,
		runCommand,
		defaultCommandEnv,
		timeoutMs,
		startedAt
	});
	if (!pkgRoot) return {
		status: "error",
		mode: "unknown",
		reason: "not-openclaw-root",
		steps: [],
		durationMs: Date.now() - startedAt
	};
	const beforeVersion = await readPackageVersion(pkgRoot);
	const globalManager = await detectGlobalInstallManagerForRoot(runCommand, pkgRoot, timeoutMs);
	if (globalManager) return await runGlobalUpdate({
		opts,
		pkgRoot,
		globalManager,
		runCommand,
		timeoutMs,
		startedAt,
		beforeVersion,
		allowGatewayServiceRepair: opts.allowGatewayServiceRepair !== false,
		allowGatewayActivation: opts.allowGatewayActivation === true
	});
	return {
		status: "skipped",
		mode: "unknown",
		root: pkgRoot,
		reason: "not-git-install",
		before: { version: beforeVersion },
		steps: [],
		durationMs: Date.now() - startedAt
	};
}
function runGatewayUpdatePreflight(cwd, timeoutMs, devTarget) {
	const complete = /* @__PURE__ */ new Error("update-preflight-complete");
	return runGatewayUpdate({
		cwd,
		timeoutMs,
		devTarget,
		beforeGitMutation: () => Promise.reject(complete)
	}).catch((error) => {
		if (error !== complete) throw error;
	});
}
//#endregion
export { UPDATE_RUNNER_TIMEOUT_MS as a, resolveUpdateDoctorExecutionPolicy as i, runGatewayUpdatePreflight as n, markPackagePostInstallDoctorAdvisory as o, resolveUpdateInstallSurface as r, runGlobalPackageUpdateSteps as s, runGatewayUpdate as t };
