import { l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js";
import { c as isRecord } from "./record-coerce-DItp3I4t.js";
import { w as parseStrictPositiveInteger } from "./number-coercion-CLj0HTDM.js";
import { t as stableStringify } from "./stable-stringify-DoZ6Yalc.js";
import { D as resolveConfigIncludes, j as resolveConfigEnvVars } from "./redact-CONqVXTy.js";
import { o as resolveRequiredHomeDir } from "./home-dir-BFvskzn8.js";
import { l as pathExists } from "./utils-B2aozS-T.js";
import { t as hasErrnoCode } from "./errno-CkbDOfLk.js";
import { a as GATEWAY_SERVICE_SELECTOR_ENV_KEYS, c as isGatewayServiceEnv, d as resolveGatewayProfileSuffix, i as GATEWAY_SERVICE_RUNTIME_PID_ENV, m as resolveGatewayWindowsTaskName, p as resolveGatewaySystemdServiceName } from "./constants-ChqKLfPp.js";
import { r as parseTcpPortFromArgs } from "./tcp-port-C3gLZtJi.js";
import { _ as resolveGatewayPort, n as DEFAULT_GATEWAY_PORT, t as CONFIG_PATH, v as resolveIncludeRoots } from "./paths-BBSTUjD5.js";
import { r as formatErrorMessage } from "./errors-Z0lU1pH3.js";
import { n as resolveCliName, t as replaceCliName } from "./cli-name-CVj-3DWf.js";
import { t as formatCliCommand } from "./command-format-HwSAdvXB.js";
import { t as resolveOpenClawPackageRoot } from "./openclaw-root-cgXywthP.js";
import { r as isJsonOutputModeActive } from "./json-output-mode-Ma8rku9x.js";
import { r as defaultRuntime } from "./runtime-LRpY2Icg.js";
import { S as createConfigIO } from "./io.runtime-CNGn9TXj.js";
import { s as parsePluginInstallRecordMap } from "./plugin-install-record-map-BGcwGvXL.js";
import { t as getWindowsCmdExePath } from "./windows-install-roots-BdGcwph2.js";
import { a as sha256Hex } from "./crypto-digest-IGAbV2KW.js";
import { n as ok, t as err } from "./result-BQGgYouL.js";
import { r as assertGatewayServiceMutationAllowed } from "./gateway-supervision-C0L8fX98.js";
import { S as asRuntimeConfig, x as asResolvedSourceConfig } from "./io.types-IrfFC0Eb.js";
import { l as parseConfigJson5 } from "./io.read-helpers-BJhqVbGj.js";
import "./io-C1WzQdAN.js";
import { t as createPreUpdateConfigSnapshot } from "./backup-rotation-CZ8MkY1q.js";
import { n as mutateConfigFileWithRetry } from "./mutate-Cg2fvWmm.js";
import "./config-D_ojUgx2.js";
import { n as quoteCmdScriptArg } from "./cmd-argv-THigjgT-.js";
import { r as theme } from "./theme-vjDs9tao.js";
import "./daemon-cli-DUsUIwF0.js";
import { o as resolveSystemdServiceName } from "./systemd-service-files-BgofM7Qa.js";
import { r as resolveGatewayTaskScriptPath } from "./paths-CzCbqt0l.js";
import { i as resolveGatewayInstallEntrypoint } from "./gateway-entrypoint-D-dT8_Uv.js";
import { n as readPackageName } from "./package-json-DDWYAL5c.js";
import { r as summarizeGatewayServiceLayout } from "./service-layout-DN_7JM1I.js";
import { a as resolveManagedGatewayServiceCommand } from "./service-types-CK8rxZ9x.js";
import { a as nodeVersionSatisfiesEngine, o as parseSemver } from "./runtime-guard-BuM9W7Vd.js";
import { r as runCommandWithTimeout } from "./exec-B6wf9wmM.js";
import { f as resolveLaunchAgentLabel, o as renderSystemLaunchDaemonOwnershipShellProbe } from "./launchd-system-D7GZ1tBS.js";
import { a as resolveGatewayRestartLogPath, r as renderPosixRestartLogSetup, s as shellEscapeRestartLogValue } from "./restart-logs-B_slAEhu.js";
import { i as terminateStaleGatewayPids, r as getSelfAndAncestorPidsSync } from "./restart-stale-pids-DDxdPFhU.js";
import { a as readGatewayServiceState, o as resolveGatewayService } from "./service-CO5q96-E.js";
import { a as resumeScheduledTaskAutoStartAfterUpdate, c as suspendScheduledTaskAutoStartForUpdate, p as resolveTaskName } from "./schtasks-B2DeDaQo.js";
import { n as runDaemonInstall } from "./install-1ah_d_uk.js";
import { a as recoverInstalledLaunchAgent, t as runDaemonRestart } from "./lifecycle-B83HQlcM.js";
import { a as renderRestartDiagnostics, n as waitForGatewayHealthyRestart } from "./restart-health-CjAus9KO.js";
import { p as trimLogTail } from "./restart-sentinel-DtNjsD5i.js";
import { n as registerSignalExitGate, r as waitForSignalExitBarriers, t as registerSignalExitBarrier } from "./signal-exit-barrier-Bs6DKn5_.js";
import { n as stylePromptMessage } from "./prompt-style-BQVvtDcR.js";
import { n as COMPLETION_SKIP_PLUGIN_COMMANDS_ENV, o as installCompletion } from "./completion-runtime-DGXHLkWu.js";
import { r as ensureCompletionCacheExists, t as checkShellCompletionStatus } from "./doctor-completion-CJRH-8ll.js";
import { l as normalizeUpdateChannel } from "./update-channels-D2-WrHya.js";
import { r as fetchNpmTagVersion, s as fetchNpmPackageTargetStatus } from "./update-check-DrhFQonH.js";
import { a as detectGlobalInstallManagerByPresence, b as normalizePackageTagInput, i as createGlobalInstallEnv, o as detectGlobalInstallManagerForRoot, t as canResolveRegistryVersionForPackageTarget } from "./update-global-C5_r5uUt.js";
import { t as doctorCommand } from "./doctor-B3Jrqnc-.js";
import { a as UPDATE_PARENT_SUPPORTS_DOCTOR_CONFIG_WRITE_ENV } from "./update-phase-CQx-E9S3.js";
import { existsSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import fs$1 from "node:fs/promises";
import { spawn, spawnSync } from "node:child_process";
import { Writable } from "node:stream";
import { confirm, isCancel } from "@clack/prompts";
//#region src/cli/update-cli/shared.ts
const INVALID_TIMEOUT_ERROR = "--timeout must be a positive integer (seconds)";
const MAX_SAFE_TIMEOUT_SECONDS = Math.floor(Number.MAX_SAFE_INTEGER / 1e3);
/** Parse a CLI timeout in seconds, exiting through the runtime on invalid input. */
function parseTimeoutMsOrExit(timeout) {
	if (timeout === void 0) return;
	const seconds = parseStrictPositiveInteger(timeout.trim());
	if (seconds === void 0 || seconds > MAX_SAFE_TIMEOUT_SECONDS) {
		if (isJsonOutputModeActive(process.argv)) throw new Error(INVALID_TIMEOUT_ERROR);
		defaultRuntime.error(INVALID_TIMEOUT_ERROR);
		defaultRuntime.exit(1);
		return null;
	}
	return seconds * 1e3;
}
const OPENCLAW_REPO_URL = "https://github.com/openclaw/openclaw.git";
const GIT_CLONE_BLOB_FILTER = "--filter=blob:none";
const MAX_LOG_CHARS = 8e3;
const DEFAULT_PACKAGE_NAME = "openclaw";
const CORE_PACKAGE_NAMES = /* @__PURE__ */ new Set([DEFAULT_PACKAGE_NAME]);
/** Normalize a CLI tag/version/spec into the npm target form accepted by update flows. */
function normalizeTag(value) {
	return normalizePackageTagInput(value, ["openclaw", DEFAULT_PACKAGE_NAME]);
}
function normalizeVersionTag(tag) {
	const trimmed = tag.trim();
	if (!trimmed) return null;
	const cleaned = trimmed.startsWith("v") ? trimmed.slice(1) : trimmed;
	return parseSemver(cleaned) ? cleaned : null;
}
/** Resolve an npm dist-tag or explicit version into a concrete package version. */
async function resolveTargetVersion(tag, timeoutMs, options = {}) {
	if (!canResolveRegistryVersionForPackageTarget(tag)) return null;
	const direct = normalizeVersionTag(tag);
	if (direct) return direct;
	return (await fetchNpmTagVersion({
		tag,
		timeoutMs,
		spec: options.spec,
		command: options.command,
		cwd: options.cwd,
		env: options.env
	})).version ?? null;
}
/** Return true when `root` is a local git checkout directory. */
async function isGitCheckout(root) {
	try {
		await fs$1.stat(path.join(root, ".git"));
		return true;
	} catch {
		return false;
	}
}
async function isCorePackage(root) {
	const name = await readPackageName(root);
	return Boolean(name && CORE_PACKAGE_NAMES.has(name));
}
/** Return true only for existing directories with no entries. */
async function isEmptyDir(targetPath) {
	try {
		return (await fs$1.readdir(targetPath)).length === 0;
	} catch {
		return false;
	}
}
/** Resolve the checkout path used by source-based self-update. */
function resolveGitInstallDir() {
	const override = process.env.OPENCLAW_GIT_DIR?.trim();
	if (override) return path.resolve(override);
	return resolveDefaultGitDir();
}
function resolveDefaultGitDir() {
	const home = resolveRequiredHomeDir(process.env, os.homedir);
	if (home.startsWith("/")) return path.posix.join(home, "openclaw");
	return path.join(home, "openclaw");
}
/** Prefer the current Node executable, falling back to `node` when run through another shim. */
function resolveNodeRunner() {
	const base = normalizeLowercaseStringOrEmpty(path.basename(process.execPath));
	if (base === "node" || base === "node.exe") return process.execPath;
	return "node";
}
/** Locate the installed OpenClaw package root that should receive update operations. */
async function resolveUpdateRoot() {
	return (process.argv[1] ? await resolveOpenClawPackageRoot({ cwd: path.dirname(path.resolve(process.argv[1])) }) : null) ?? await resolveOpenClawPackageRoot({
		moduleUrl: import.meta.url,
		cwd: process.cwd()
	}) ?? process.cwd();
}
/** Run one update subprocess and report bounded stdout/stderr tails to progress listeners. */
async function runUpdateStep(params) {
	const command = params.argv.join(" ");
	params.progress?.onStepStart?.({
		name: params.name,
		command,
		index: 0,
		total: 0
	});
	const started = Date.now();
	const res = await runCommandWithTimeout(params.argv, {
		cwd: params.cwd,
		env: params.env,
		timeoutMs: params.timeoutMs
	});
	const durationMs = Date.now() - started;
	const stderrTail = trimLogTail(res.stderr, MAX_LOG_CHARS);
	params.progress?.onStepComplete?.({
		name: params.name,
		command,
		index: 0,
		total: 0,
		durationMs,
		exitCode: res.code,
		stderrTail,
		signal: res.signal,
		killed: res.killed,
		termination: res.termination
	});
	return {
		name: params.name,
		command,
		cwd: params.cwd ?? process.cwd(),
		durationMs,
		exitCode: res.code,
		stdoutTail: trimLogTail(res.stdout, MAX_LOG_CHARS),
		stderrTail,
		signal: res.signal,
		killed: res.killed,
		termination: res.termination
	};
}
async function cloneGitCheckoutTransactionally(params) {
	const parentDir = path.dirname(params.dir);
	await fs$1.mkdir(parentDir, { recursive: true });
	const canonicalParentDir = await fs$1.realpath(parentDir);
	const preserveDir = await pathExists(params.dir) && await isEmptyDir(params.dir);
	const targetDir = preserveDir ? await fs$1.realpath(params.dir) : path.join(canonicalParentDir, path.basename(params.dir));
	const stagingParent = preserveDir ? targetDir : canonicalParentDir;
	const stagingDir = await fs$1.mkdtemp(path.join(stagingParent, ".openclaw-clone-"));
	let cleanupStaging = true;
	try {
		const result = await runUpdateStep({
			name: "git clone",
			argv: [
				"git",
				"clone",
				GIT_CLONE_BLOB_FILTER,
				OPENCLAW_REPO_URL,
				stagingDir
			],
			env: params.env,
			timeoutMs: params.timeoutMs,
			progress: params.progress
		});
		if (result.exitCode !== 0) return {
			checkoutDir: targetDir,
			step: result
		};
		if (!preserveDir) try {
			await fs$1.lstat(targetDir);
		} catch (error) {
			if (!hasErrnoCode(error, "ENOENT")) throw error;
			await fs$1.rename(stagingDir, targetDir);
			return {
				checkoutDir: targetDir,
				step: result
			};
		}
		if (!preserveDir) throw new Error(`OPENCLAW_GIT_DIR appeared while cloning: ${params.dir}. The existing path was left unchanged; move it or choose another OPENCLAW_GIT_DIR, then retry.`);
		const expectedEntries = preserveDir ? [path.basename(stagingDir)] : [];
		if ((await fs$1.readdir(targetDir)).toSorted().join("\0") !== expectedEntries.toSorted().join("\0")) throw new Error(`OPENCLAW_GIT_DIR appeared while cloning: ${params.dir}. The existing path was left unchanged; move it or choose another OPENCLAW_GIT_DIR, then retry.`);
		const entries = (await fs$1.readdir(stagingDir)).toSorted((a, b) => a === ".git" ? 1 : b === ".git" ? -1 : 0);
		const moved = [];
		let publishError;
		try {
			for (const entry of entries) {
				await fs$1.rename(path.join(stagingDir, entry), path.join(targetDir, entry));
				moved.push(entry);
			}
		} catch (error) {
			publishError = { value: error };
		}
		if (publishError) {
			const rollbackErrors = [];
			for (const entry of moved.toReversed()) try {
				await fs$1.rename(path.join(targetDir, entry), path.join(stagingDir, entry));
			} catch (rollbackError) {
				rollbackErrors.push(rollbackError);
			}
			if (rollbackErrors.length > 0) {
				cleanupStaging = false;
				throw new AggregateError([publishError.value, ...rollbackErrors], `Could not publish or fully roll back the cloned checkout at ${targetDir}; recovery files remain at ${stagingDir}`);
			}
			throw publishError.value;
		}
		return {
			checkoutDir: targetDir,
			step: result
		};
	} finally {
		if (cleanupStaging) await fs$1.rm(stagingDir, {
			recursive: true,
			force: true
		});
	}
}
/** Ensure the configured source-update directory exists and points at an OpenClaw checkout. */
async function ensureGitCheckout(params) {
	const gitEnv = params.env ?? await createGlobalInstallEnv();
	if (!await pathExists(params.dir)) return await cloneGitCheckoutTransactionally({
		dir: params.dir,
		env: gitEnv,
		timeoutMs: params.timeoutMs,
		progress: params.progress
	});
	if (!await isGitCheckout(params.dir)) {
		if (!await isEmptyDir(params.dir)) throw new Error(`OPENCLAW_GIT_DIR points at a non-git directory: ${params.dir}. Set OPENCLAW_GIT_DIR to an empty folder or an openclaw checkout.`);
		return await cloneGitCheckoutTransactionally({
			dir: params.dir,
			env: gitEnv,
			timeoutMs: params.timeoutMs,
			progress: params.progress
		});
	}
	if (!await isCorePackage(params.dir)) throw new Error(`OPENCLAW_GIT_DIR does not look like a core checkout: ${params.dir}.`);
	return {
		checkoutDir: await fs$1.realpath(params.dir),
		step: null
	};
}
/** Detect the package manager that owns a global/package OpenClaw install. */
async function resolveGlobalManager(params) {
	const runCommand = createGlobalCommandRunner();
	if (params.installKind === "package") {
		const detected = await detectGlobalInstallManagerForRoot(runCommand, params.root, params.timeoutMs);
		if (detected) return detected;
	}
	return await detectGlobalInstallManagerByPresence(runCommand, params.timeoutMs) ?? "npm";
}
const COMPLETION_CACHE_WRITE_TIMEOUT_MS = 3e4;
const COMPLETION_CACHE_MANUAL_REFRESH_HINT = "Shell tab-completion may be stale; refresh manually with: openclaw completion --write-state";
/** Best-effort refresh of shell completion state after a successful update. */
async function tryWriteCompletionCache(root, jsonMode) {
	const binPath = path.join(root, "openclaw.mjs");
	if (!await pathExists(binPath)) return "skipped";
	const result = spawnSync(resolveNodeRunner(), [
		binPath,
		"completion",
		"--write-state"
	], {
		cwd: root,
		env: {
			...process.env,
			[COMPLETION_SKIP_PLUGIN_COMMANDS_ENV]: "1"
		},
		encoding: "utf-8",
		timeout: COMPLETION_CACHE_WRITE_TIMEOUT_MS
	});
	if (result.error) {
		if (!jsonMode) {
			const reason = result.error.code === "ETIMEDOUT" ? `timed out after ${COMPLETION_CACHE_WRITE_TIMEOUT_MS / 1e3}s` : String(result.error);
			defaultRuntime.log(theme.warn(`Completion cache update failed: ${reason}. ${COMPLETION_CACHE_MANUAL_REFRESH_HINT}`));
		}
		return "failed";
	}
	if (result.status !== 0) {
		if (!jsonMode) {
			const stderr = (result.stderr ?? "").trim();
			const detail = stderr ? ` (${stderr})` : "";
			defaultRuntime.log(theme.warn(`Completion cache update failed${detail}. ${COMPLETION_CACHE_MANUAL_REFRESH_HINT}`));
		}
		return "failed";
	}
	return "completed";
}
/** Adapter used by global-install detection helpers to execute bounded subprocess probes. */
function createGlobalCommandRunner() {
	return async (argv, options) => {
		const res = await runCommandWithTimeout(argv, options);
		return {
			stdout: res.stdout,
			stderr: res.stderr,
			code: res.code
		};
	};
}
//#endregion
//#region src/cli/update-cli/update-command-config.ts
const PRE_UPDATE_CONFIG_SNAPSHOT_MAX_AGE_MS = 360 * 60 * 1e3;
async function createUpdateConfigSnapshot() {
	await createPreUpdateConfigSnapshot({
		configPath: CONFIG_PATH,
		fs: {
			writeFile: fs$1.writeFile,
			readFile: fs$1.readFile,
			existsSync
		}
	});
}
function normalizePluginInstallRecordMap(value) {
	const records = parsePluginInstallRecordMap(value);
	if (!records) throw new Error("Invalid plugin install record map");
	return records;
}
function normalizeChannelConfigMap(value) {
	if (!isRecord(value)) return null;
	return value;
}
function normalizeDirectAuthoredChannelConfigMap(value) {
	const channels = normalizeChannelConfigMap(value);
	if (!channels || Object.hasOwn(channels, "$include")) return null;
	return channels;
}
function restorePreUpdateChannelModelOverrides(params) {
	if (params.restoredChannelIds.length === 0) return {
		channels: params.channels,
		changed: false
	};
	const preUpdateModelByChannel = normalizeChannelConfigMap(params.preUpdateChannels.modelByChannel);
	if (!preUpdateModelByChannel) return {
		channels: params.channels,
		changed: false
	};
	const currentModelByChannel = normalizeChannelConfigMap(params.channels.modelByChannel) ?? {};
	const restoredModelByChannel = structuredClone(currentModelByChannel);
	let changed = false;
	for (const [providerId, providerOverrides] of Object.entries(preUpdateModelByChannel)) {
		const preUpdateProviderOverrides = normalizeChannelConfigMap(providerOverrides);
		if (!preUpdateProviderOverrides) continue;
		const currentProviderOverrides = normalizeChannelConfigMap(restoredModelByChannel[providerId]) ?? {};
		let providerChanged = false;
		for (const channelId of params.restoredChannelIds) {
			if (currentProviderOverrides[channelId] !== void 0 || preUpdateProviderOverrides[channelId] === void 0) continue;
			currentProviderOverrides[channelId] = structuredClone(preUpdateProviderOverrides[channelId]);
			providerChanged = true;
		}
		if (providerChanged) {
			restoredModelByChannel[providerId] = currentProviderOverrides;
			changed = true;
		}
	}
	return changed ? {
		channels: {
			...params.channels,
			modelByChannel: restoredModelByChannel
		},
		changed: true
	} : {
		channels: params.channels,
		changed: false
	};
}
function restoreDroppedPreUpdateChannels(snapshot, preUpdateConfig) {
	if (!snapshot.valid || !preUpdateConfig) return {
		snapshot,
		changed: false
	};
	const preUpdateChannels = normalizeChannelConfigMap(preUpdateConfig.sourceConfig.channels);
	if (!preUpdateChannels) return {
		snapshot,
		changed: false
	};
	let restoredChannels = { ...normalizeChannelConfigMap(snapshot.sourceConfig.channels) ?? {} };
	const restoredChannelIds = [];
	let restored = false;
	for (const [channelId, channelConfig] of Object.entries(preUpdateChannels)) {
		if (restoredChannels[channelId] !== void 0) continue;
		restoredChannels[channelId] = structuredClone(channelConfig);
		if (channelId !== "modelByChannel") restoredChannelIds.push(channelId);
		restored = true;
	}
	if (!restored) return {
		snapshot,
		changed: false
	};
	restoredChannels = restorePreUpdateChannelModelOverrides({
		channels: restoredChannels,
		preUpdateChannels,
		restoredChannelIds
	}).channels;
	const authoredChannels = resolveRestoredAuthoredChannels({
		currentChannels: snapshot.sourceConfig.channels,
		currentAuthoredChannels: isRecord(snapshot.parsed) ? snapshot.parsed.channels : snapshot.sourceConfig.channels,
		preUpdateAuthoredChannels: preUpdateConfig.authoredConfig.channels,
		restoredChannelIds
	});
	return {
		snapshot: {
			...createUpdatedConfigSnapshot(snapshot, {
				...snapshot.sourceConfig,
				channels: restoredChannels
			}),
			hash: snapshot.hash
		},
		changed: true,
		...authoredChannels !== void 0 ? { authoredChannels } : {}
	};
}
function hasRestorablePreUpdateChannels(snapshot, preUpdateConfig) {
	if (!snapshot.valid) return false;
	const preUpdateChannels = normalizeChannelConfigMap(preUpdateConfig.sourceConfig.channels);
	if (!preUpdateChannels) return false;
	const postUpdateChannels = normalizeChannelConfigMap(snapshot.sourceConfig.channels) ?? {};
	return Object.keys(preUpdateChannels).some((channelId) => postUpdateChannels[channelId] === void 0);
}
function resolveRestoredAuthoredChannels(params) {
	if (params.preUpdateAuthoredChannels === void 0) return;
	const directAuthoredChannels = normalizeDirectAuthoredChannelConfigMap(params.preUpdateAuthoredChannels);
	if (!directAuthoredChannels) {
		const preUpdateAuthoredChannels = normalizeChannelConfigMap(params.preUpdateAuthoredChannels);
		if (!preUpdateAuthoredChannels) return;
		const currentDirectAuthoredChannels = normalizeDirectAuthoredChannelConfigMap(params.currentAuthoredChannels);
		if (currentDirectAuthoredChannels) return {
			...structuredClone(preUpdateAuthoredChannels),
			...structuredClone(currentDirectAuthoredChannels)
		};
		const currentAuthoredChannels = normalizeChannelConfigMap(params.currentAuthoredChannels);
		return !currentAuthoredChannels || Object.keys(currentAuthoredChannels).length === 0 ? structuredClone(preUpdateAuthoredChannels) : void 0;
	}
	const restoredChannels = { ...normalizeDirectAuthoredChannelConfigMap(params.currentAuthoredChannels) ?? normalizeDirectAuthoredChannelConfigMap(params.currentChannels) ?? {} };
	let changed = false;
	for (const channelId of params.restoredChannelIds) {
		if (restoredChannels[channelId] !== void 0 || directAuthoredChannels[channelId] === void 0) continue;
		restoredChannels[channelId] = structuredClone(directAuthoredChannels[channelId]);
		changed = true;
	}
	const restoredModelOverrides = restorePreUpdateChannelModelOverrides({
		channels: restoredChannels,
		preUpdateChannels: directAuthoredChannels,
		restoredChannelIds: params.restoredChannelIds
	});
	if (restoredModelOverrides.changed) return restoredModelOverrides.channels;
	return changed ? restoredChannels : void 0;
}
async function persistRequestedUpdateChannel(params) {
	if (!params.requestedChannel || !params.configSnapshot.valid) return params.configSnapshot;
	const storedChannel = normalizeUpdateChannel(params.configSnapshot.config.update?.channel);
	if (params.requestedChannel === storedChannel) return params.configSnapshot;
	const requestedChannel = params.requestedChannel;
	const mutation = await mutateConfigFileWithRetry({
		writeOptions: { skipPluginValidation: true },
		mutate: (draft) => {
			draft.update = {
				...draft.update,
				channel: requestedChannel
			};
		}
	});
	return createUpdatedConfigSnapshot(mutation.snapshot, mutation.nextConfig);
}
function createUpdatedConfigSnapshot(snapshot, next) {
	if (!snapshot.valid) return snapshot;
	return {
		...snapshot,
		hash: void 0,
		parsed: next,
		sourceConfig: asResolvedSourceConfig(next),
		resolved: asResolvedSourceConfig(next),
		runtimeConfig: asRuntimeConfig(next),
		config: asRuntimeConfig(next)
	};
}
async function maybeRepairLegacyConfigForUpdateChannel(params) {
	if (params.configSnapshot.valid || params.configSnapshot.legacyIssues.length === 0) return params.configSnapshot;
	const { repairLegacyConfigForUpdateChannel } = await import("./legacy-config-repair-y0xbL1iP.js");
	const { snapshot, repaired } = await repairLegacyConfigForUpdateChannel(params);
	if (!params.jsonMode && repaired) defaultRuntime.log(theme.muted("Migrated legacy config before changing update channel."));
	return snapshot;
}
async function writePostCoreSourceConfigFile(filePath, preUpdateConfig) {
	if (!preUpdateConfig) return;
	await fs$1.writeFile(filePath, `${JSON.stringify(preUpdateConfig)}\n`, "utf-8");
}
async function readPostCoreSourceConfigFile(filePath, options) {
	if (!filePath) return;
	try {
		const parsed = parseConfigJson5(await fs$1.readFile(filePath, "utf-8"));
		if (!parsed.ok || !isRecord(parsed.parsed)) return;
		return normalizePreUpdateConfigRestoreInput(parsed.parsed, options);
	} catch {
		return;
	}
}
function normalizePreUpdateConfigRestoreInput(parsed, options) {
	const sourceConfig = parsed.sourceConfig;
	const authoredConfig = parsed.authoredConfig;
	if (isRecord(sourceConfig) && isRecord(authoredConfig)) return {
		sourceConfig,
		authoredConfig
	};
	const authored = parsed;
	return {
		sourceConfig: options?.configPath ? resolvePreUpdateSourceConfigFromAuthored(authored, options.configPath) : authored,
		authoredConfig: authored
	};
}
function resolvePreUpdateSourceConfigFromAuthored(authoredConfig, configPath) {
	try {
		const resolved = resolveConfigEnvVars(resolveConfigIncludes(authoredConfig, configPath, void 0, { allowedRoots: resolveIncludeRoots(process.env) }), process.env, { onMissing: () => void 0 });
		return isRecord(resolved) ? resolved : authoredConfig;
	} catch {
		return authoredConfig;
	}
}
async function isFreshPreUpdateConfigSnapshot(params) {
	const snapshotStat = await fs$1.stat(params.snapshotPath).catch(() => null);
	if (!snapshotStat) return false;
	if (params.updateStartedAtMs !== void 0 && snapshotStat.mtimeMs + 1e3 < params.updateStartedAtMs) return false;
	if (Date.now() - snapshotStat.mtimeMs > PRE_UPDATE_CONFIG_SNAPSHOT_MAX_AGE_MS) return false;
	const currentStat = await fs$1.stat(params.currentConfigPath).catch(() => null);
	return !currentStat || snapshotStat.mtimeMs <= currentStat.mtimeMs + 1e3;
}
async function readPostCorePreUpdateSourceConfig(params) {
	const fromChildEnv = await readPostCoreSourceConfigFile(params.sourceConfigPath);
	if (fromChildEnv) return fromChildEnv;
	if (params.updateStartedAtMs === void 0) return;
	const explicitPreUpdatePath = `${params.currentSnapshot.path}.pre-update`;
	if (await isFreshPreUpdateConfigSnapshot({
		currentConfigPath: params.currentSnapshot.path,
		snapshotPath: explicitPreUpdatePath,
		updateStartedAtMs: params.updateStartedAtMs
	})) {
		const preUpdateConfig = await readPostCoreSourceConfigFile(explicitPreUpdatePath, { configPath: params.currentSnapshot.path });
		if (preUpdateConfig && hasRestorablePreUpdateChannels(params.currentSnapshot, preUpdateConfig)) return preUpdateConfig;
		return;
	}
	const backupPath = `${params.currentSnapshot.path}.bak`;
	if (await isFreshPreUpdateConfigSnapshot({
		currentConfigPath: params.currentSnapshot.path,
		snapshotPath: backupPath,
		updateStartedAtMs: params.updateStartedAtMs
	})) {
		const preUpdateConfig = await readPostCoreSourceConfigFile(backupPath, { configPath: params.currentSnapshot.path });
		if (preUpdateConfig && hasRestorablePreUpdateChannels(params.currentSnapshot, preUpdateConfig)) return preUpdateConfig;
	}
}
//#endregion
//#region src/cli/update-cli/restart-helper.ts
/**
* Shell-escape a string for embedding in single-quoted shell arguments.
* Replaces every `'` with `'\''` (end quote, escaped quote, resume quote).
* For batch scripts, validates against special characters instead.
*/
function shellEscape(value) {
	return value.replace(/'/g, "'\\''");
}
/** Validates a task name is safe for embedding in Windows restart scripts. */
function isWindowsTaskNameSafe(value) {
	return /^[A-Za-z0-9 _\-().]+$/.test(value);
}
function powerShellSingleQuote(value) {
	return `'${value.replace(/'/g, "''")}'`;
}
function resolveSystemdUnit(env) {
	const override = normalizeOptionalString(env.OPENCLAW_SYSTEMD_UNIT);
	if (override) return override.endsWith(".service") ? override : `${override}.service`;
	return `${resolveGatewaySystemdServiceName(env.OPENCLAW_PROFILE)}.service`;
}
function resolveWindowsTaskName(env) {
	const override = env.OPENCLAW_WINDOWS_TASK_NAME?.trim();
	if (override) return override;
	return resolveGatewayWindowsTaskName(env.OPENCLAW_PROFILE);
}
function resolveLinuxFilesystemBusUid(busAddress) {
	const encodedBusPath = (busAddress?.match(/^unix:([^;]+)$/u)?.[1])?.split(",").find((parameter) => parameter.startsWith("path="))?.slice(5);
	if (encodedBusPath === void 0) return;
	try {
		return decodeURIComponent(encodedBusPath).match(/^\/run\/user\/(\d+)\/bus$/u)?.[1];
	} catch {
		return;
	}
}
async function renderLinuxUserBusRepair(env) {
	const uid = typeof process.geteuid === "function" ? process.geteuid() : 0;
	if (uid <= 0) return "";
	const expectedRuntimeDir = `/run/user/${uid}`;
	const expectedBusAddress = `unix:path=${expectedRuntimeDir}/bus`;
	const runtimeDir = normalizeOptionalString(env.XDG_RUNTIME_DIR);
	const busAddress = normalizeOptionalString(env.DBUS_SESSION_BUS_ADDRESS);
	const runtimeUid = (runtimeDir ? path.posix.normalize(runtimeDir) : void 0)?.match(/^\/run\/user\/(\d+)\/?$/)?.[1];
	const busUid = resolveLinuxFilesystemBusUid(busAddress);
	const repairRuntimeDir = !runtimeDir || runtimeUid !== void 0 && runtimeUid !== String(uid);
	const preserveCustomRuntimeDir = Boolean(runtimeDir) && runtimeUid === void 0;
	const repairBusAddress = !preserveCustomRuntimeDir && (!busAddress || busUid !== void 0 && busUid !== String(uid));
	const clearEmptyCustomBusAddress = preserveCustomRuntimeDir && env.DBUS_SESSION_BUS_ADDRESS !== void 0 && !busAddress;
	if (!repairRuntimeDir && !repairBusAddress && !clearEmptyCustomBusAddress) return "";
	try {
		const socketRuntimeDir = clearEmptyCustomBusAddress && runtimeDir ? runtimeDir : expectedRuntimeDir;
		if (!(await fs$1.stat(path.join(socketRuntimeDir, "bus"))).isSocket()) return "";
	} catch {
		return "";
	}
	return `# Repair missing or cross-user D-Bus values inherited by the updater.
${[
		repairRuntimeDir ? `export XDG_RUNTIME_DIR='${shellEscape(expectedRuntimeDir)}'` : "",
		repairBusAddress ? `export DBUS_SESSION_BUS_ADDRESS='${shellEscape(expectedBusAddress)}'` : "",
		clearEmptyCustomBusAddress ? "unset DBUS_SESSION_BUS_ADDRESS" : ""
	].filter(Boolean).join("\n")}
`;
}
/**
* Prepares a standalone script to restart the gateway service.
* This script is written to a temporary directory and does not depend on
* the installed package files, ensuring restart capability even if the
* update process temporarily removes or corrupts installation files.
*/
async function prepareRestartScript(env = process.env, gatewayPort = DEFAULT_GATEWAY_PORT, windowsGatewayArgv = []) {
	const timestamp = Date.now();
	const platform = process.platform;
	let scriptContent;
	let filename;
	try {
		if (platform === "linux") {
			const escaped = shellEscape(resolveSystemdUnit(env));
			const logSetup = renderPosixRestartLogSetup({
				...process.env,
				...env
			});
			const userBusRepair = await renderLinuxUserBusRepair({
				...process.env,
				...env
			});
			filename = `openclaw-restart-${timestamp}.sh`;
			scriptContent = `#!/bin/sh
# Standalone restart script — survives parent process termination.
# Wait briefly to ensure file locks are released after update.
sleep 1
exec 3>&2
${logSetup}
${userBusRepair}
printf '[%s] openclaw restart attempt source=update target=%s\\n' "$(date -u +%FT%TZ)" '${escaped}' >&2
if systemctl --user is-active --quiet '${escaped}' || systemctl --user is-enabled --quiet '${escaped}'; then
  if systemctl --user restart '${escaped}'; then
    status=0
    printf '[%s] openclaw restart done source=update\\n' "$(date -u +%FT%TZ)" >&2
  else
    status=$?
    printf '[%s] openclaw restart failed source=update status=%s\\n' "$(date -u +%FT%TZ)" "$status" >&2
  fi
elif systemctl is-active --quiet '${escaped}' || systemctl is-enabled --quiet '${escaped}'; then
  status=78
  printf '[%s] system-scoped openclaw gateway unit detected; update cannot restart it without sudo. Run: sudo systemctl restart %s\\n' "$(date -u +%FT%TZ)" '${escaped}' >&2
  printf '[%s] system-scoped openclaw gateway unit detected; update cannot restart it without sudo. Run: sudo systemctl restart %s\\n' "$(date -u +%FT%TZ)" '${escaped}' >&3 2>/dev/null || true
else
  if systemctl --user restart '${escaped}'; then
    status=0
    printf '[%s] openclaw restart done source=update\\n' "$(date -u +%FT%TZ)" >&2
  else
    status=$?
    printf '[%s] openclaw restart failed source=update status=%s\\n' "$(date -u +%FT%TZ)" "$status" >&2
  fi
fi
# Self-cleanup
script_dir=$(dirname "$0")
exec 3>&-
rm -f "$0"
rmdir "$script_dir" 2>/dev/null || true
exit "$status"
`;
		} else if (platform === "darwin") {
			const label = resolveLaunchAgentLabel(env);
			const escaped = shellEscape(label);
			const uid = process.getuid ? process.getuid() : 501;
			const home = normalizeOptionalString(env.HOME) || process.env.HOME || os.homedir();
			const escapedPlistPath = shellEscape(path.join(home, "Library", "LaunchAgents", `${label}.plist`));
			const logSetup = renderPosixRestartLogSetup({
				...process.env,
				...env
			});
			const systemOwnershipProbe = renderSystemLaunchDaemonOwnershipShellProbe(label);
			filename = `openclaw-restart-${timestamp}.sh`;
			scriptContent = `#!/bin/sh
# Standalone restart script — survives parent process termination.
# Wait briefly to ensure file locks are released after update.
sleep 1
# Capture launchctl output so bootstrap/kickstart failures leave a durable
# audit trail. Log setup is best-effort: restart must still run if the log path
# is temporarily unavailable.
${logSetup}
printf '[%s] openclaw restart attempt source=update target=%s\\n' "$(date -u +%FT%TZ)" '${shellEscapeRestartLogValue(label)}' >&2
${systemOwnershipProbe}
# Try kickstart first (works when the service is still registered).
# If it fails (e.g. after bootout), clear any persisted disabled state,
# then re-register via bootstrap. Bootstrap loads RunAtLoad agents, so the
# fallback must not immediately kickstart -k the freshly spawned gateway.
# The final status is captured
# before self-cleanup so a genuine failure remains observable.
status=0
if [ -n "$openclaw_system_launchd_conflict" ]; then
  status=78
  printf '[%s] openclaw restart blocked source=update reason=%s\n' "$(date -u +%FT%TZ)" "$openclaw_system_launchd_detail" >&2
elif ! launchctl kickstart -k 'gui/${uid}/${escaped}'; then
  launchctl enable 'gui/${uid}/${escaped}'
  if launchctl bootstrap 'gui/${uid}' '${escapedPlistPath}'; then
    status=0
  else
    launchctl kickstart -k 'gui/${uid}/${escaped}'
    status=$?
  fi
fi
if [ "$status" -eq 0 ]; then
  printf '[%s] openclaw restart done source=update\\n' "$(date -u +%FT%TZ)" >&2
else
  printf '[%s] openclaw restart failed source=update status=%s\\n' "$(date -u +%FT%TZ)" "$status" >&2
fi
# Self-cleanup (log is retained under the OpenClaw state logs directory).
script_dir=$(dirname "$0")
rm -f "$0"
rmdir "$script_dir" 2>/dev/null || true
exit "$status"
`;
		} else if (platform === "win32") {
			const taskName = resolveWindowsTaskName(env);
			if (!isWindowsTaskNameSafe(taskName)) return null;
			const port = Number.isFinite(gatewayPort) && gatewayPort > 0 ? gatewayPort : DEFAULT_GATEWAY_PORT;
			const quotedLogPath = powerShellSingleQuote(resolveGatewayRestartLogPath({
				...process.env,
				...env
			}));
			const quotedTaskName = powerShellSingleQuote(taskName);
			const quotedGatewayScriptPath = powerShellSingleQuote(resolveGatewayTaskScriptPath({
				...process.env,
				...env
			}));
			const expectedGatewayArgv = windowsGatewayArgv.map(powerShellSingleQuote).join(", ");
			filename = `openclaw-restart-${timestamp}.cmd`;
			scriptContent = `@echo off
REM Standalone restart script - survives parent process termination.
REM Keep this as a cmd wrapper so Group Policy script execution policies
REM cannot block the update restart handoff before schtasks.exe runs.
setlocal
set "OPENCLAW_RESTART_SCRIPT=%~f0"
set "OPENCLAW_RESTART_SCRIPT_DIR=%~dp0."
powershell -NoProfile -ExecutionPolicy Bypass -Command "$p=$env:OPENCLAW_RESTART_SCRIPT; $s=Get-Content -Raw -LiteralPath $p; $m='# POWERSHELL'; $i=$s.IndexOf($m); if ($i -lt 0) { exit 1 }; Invoke-Expression $s.Substring($i)"
set "status=%ERRORLEVEL%"
del "%~f0" >nul 2>&1
rmdir "%OPENCLAW_RESTART_SCRIPT_DIR%" >nul 2>&1
exit /b %status%
# POWERSHELL
# Wait briefly to ensure file locks are released after update.
$ErrorActionPreference = "Continue"
Start-Sleep -Seconds 2

$logPath = ${quotedLogPath}
try {
  $logDir = Split-Path -Parent $logPath
  New-Item -ItemType Directory -Path $logDir -Force | Out-Null
  Add-Content -LiteralPath $logPath -Value "[$(Get-Date -Format o)] openclaw restart log initialized"
} catch {
  # Restart should still run if log setup is unavailable.
}

function Write-RestartLog {
  param([string]$Message)
  try {
    Add-Content -LiteralPath $logPath -Value "[$(Get-Date -Format o)] $Message"
  } catch {
  }
}

function Join-OpenClawProcessArguments {
  param([string[]]$Arguments)
  ($Arguments | ForEach-Object {
    if ($_ -match "\\s") {
      '"' + $_ + '"'
    } else {
      $_
    }
  }) -join " "
}

function Invoke-OpenClawSchtasksWithTimeout {
  param(
    [string[]]$Arguments,
    [int]$TimeoutSeconds
  )
  $process = $null
  try {
    $startInfo = [System.Diagnostics.ProcessStartInfo]::new()
    $startInfo.FileName = "schtasks.exe"
    $startInfo.Arguments = Join-OpenClawProcessArguments -Arguments $Arguments
    $startInfo.UseShellExecute = $false
    $startInfo.RedirectStandardOutput = $true
    $startInfo.RedirectStandardError = $true
    $process = [System.Diagnostics.Process]::Start($startInfo)
    if (-not $process.WaitForExit($TimeoutSeconds * 1000)) {
      try {
        $process.Kill()
      } catch {
      }
      Write-RestartLog "openclaw restart schtasks timeout source=update args=$($Arguments -join ' ')"
      return 124
    }
    $stdout = $process.StandardOutput.ReadToEnd()
    $stderr = $process.StandardError.ReadToEnd()
    if ($stdout) {
      Write-RestartLog $stdout.Trim()
    }
    if ($stderr) {
      Write-RestartLog $stderr.Trim()
    }
    return $process.ExitCode
  } catch {
    Write-RestartLog "openclaw restart schtasks failed source=update args=$($Arguments -join ' ') error=$($_.Exception.Message)"
    return 1
  }
}

function Get-OpenClawScheduledTaskState {
  param([string]$TaskName)
  try {
    $task = Get-ScheduledTask -TaskName $TaskName -ErrorAction Stop
    if ($task -and $task.State) {
      return [string]$task.State
    }
  } catch {
  }

  try {
    $queryOutput = & schtasks.exe /Query /TN $TaskName /FO LIST 2>$null
    foreach ($line in $queryOutput) {
      if ($line -match "^\\s*Status:\\s*(.+?)\\s*$") {
        return $Matches[1]
      }
    }
  } catch {
  }

  return "Unknown"
}

$nativeSource = @'
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace OpenClaw.Restart {
  public sealed class ProcessLease : IDisposable {
    private IntPtr handle;
    public long CreationTimeFileTime { get; private set; }

    internal ProcessLease(IntPtr handle, long creationTimeFileTime) {
      this.handle = handle;
      CreationTimeFileTime = creationTimeFileTime;
    }

    public bool Terminate() {
      return handle != IntPtr.Zero && NativeMethods.TerminateProcess(handle, 1);
    }

    public void Dispose() {
      if (handle != IntPtr.Zero) {
        NativeMethods.CloseHandle(handle);
        handle = IntPtr.Zero;
      }
    }
  }

  public static class NativeMethods {
    private const uint PROCESS_TERMINATE = 0x0001;
    private const uint PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;

    [StructLayout(LayoutKind.Sequential)]
    private struct FILETIME {
      public uint Low;
      public uint High;
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr OpenProcess(uint access, bool inheritHandle, int processId);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool GetProcessTimes(
      IntPtr process,
      out FILETIME creation,
      out FILETIME exit,
      out FILETIME kernel,
      out FILETIME user
    );

    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern bool TerminateProcess(IntPtr process, uint exitCode);

    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern bool CloseHandle(IntPtr handle);

    [DllImport("shell32.dll", SetLastError = true)]
    private static extern IntPtr CommandLineToArgvW(
      [MarshalAs(UnmanagedType.LPWStr)] string commandLine,
      out int argumentCount
    );

    [DllImport("kernel32.dll")]
    private static extern IntPtr LocalFree(IntPtr memory);

    public static ProcessLease TryOpenProcess(int processId) {
      IntPtr handle = OpenProcess(
        PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION,
        false,
        processId
      );
      if (handle == IntPtr.Zero) {
        return null;
      }

      FILETIME creation;
      FILETIME exit;
      FILETIME kernel;
      FILETIME user;
      if (!GetProcessTimes(handle, out creation, out exit, out kernel, out user)) {
        CloseHandle(handle);
        return null;
      }

      long creationTime = ((long)creation.High << 32) | creation.Low;
      // Win32_Process.CreationDate exposes microseconds. Truncate FILETIME's
      // finer 100-nanosecond digit so both identity sources compare exactly.
      long normalizedCreationTime = creationTime - (creationTime % 10);
      return new ProcessLease(handle, normalizedCreationTime);
    }

    public static string[] ParseCommandLine(string commandLine) {
      int argumentCount;
      IntPtr arguments = CommandLineToArgvW(commandLine, out argumentCount);
      if (arguments == IntPtr.Zero) {
        throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
      }
      try {
        var result = new List<string>(argumentCount);
        for (int index = 0; index < argumentCount; index++) {
          IntPtr argument = Marshal.ReadIntPtr(arguments, index * IntPtr.Size);
          result.Add(Marshal.PtrToStringUni(argument));
        }
        return result.ToArray();
      } finally {
        LocalFree(arguments);
      }
    }
  }
}
'@

try {
  Add-Type -TypeDefinition $nativeSource -Language CSharp -ErrorAction Stop
} catch {
  Write-RestartLog "openclaw restart native ownership helper unavailable source=update error=$($_.Exception.Message)"
}

# OPENCLAW_RESTART_KILL_POLICY_BEGIN
function Get-OpenClawListenerSnapshot {
  param([int]$Port)

  try {
    if (Get-Command Get-NetTCPConnection -ErrorAction SilentlyContinue) {
      $listenerPids = @(
        Get-NetTCPConnection -State Listen -ErrorAction Stop |
          Where-Object { [int]$_.LocalPort -eq $Port } |
          ForEach-Object { [int]$_.OwningProcess } |
          Sort-Object -Unique
      )
      return [pscustomobject]@{ Known = $true; Pids = $listenerPids }
    }
  } catch {
    Write-RestartLog "openclaw restart Get-NetTCPConnection query failed source=update error=$($_.Exception.Message)"
  }

  try {
    $netstatOutput = @(& netstat.exe -ano -p tcp 2>$null)
    if ($LASTEXITCODE -ne 0) {
      return [pscustomobject]@{ Known = $false; Pids = @() }
    }

    $listenerPids = @()
    $localPortPattern = ":" + [regex]::Escape([string]$Port) + '$'
    foreach ($line in $netstatOutput) {
      $tokens = @($line.Trim() -split '\\s+' | Where-Object { $_ })
      if ($tokens.Count -lt 5 -or $tokens[0] -ine "TCP") {
        continue
      }
      # Listening rows use a wildcard foreign endpoint with port zero. Avoid the
      # localized state column entirely; protocol/endpoints/PID stay numeric.
      if ($tokens[1] -notmatch $localPortPattern -or $tokens[2] -notmatch ':0$') {
        continue
      }
      $listenerPid = 0
      if ([int]::TryParse($tokens[-1], [ref]$listenerPid) -and $listenerPid -gt 0) {
        $listenerPids += $listenerPid
      }
    }
    return [pscustomobject]@{
      Known = $true
      Pids = @($listenerPids | Sort-Object -Unique)
    }
  } catch {
    Write-RestartLog "openclaw restart netstat query failed source=update error=$($_.Exception.Message)"
    return [pscustomobject]@{ Known = $false; Pids = @() }
  }
}

function Get-OpenClawProcessFacts {
  param([int]$ProcessId)

  try {
    $process = Get-CimInstance Win32_Process -Filter "ProcessId = $ProcessId" -ErrorAction Stop
    if (-not $process -or -not $process.CommandLine -or -not $process.CreationDate) {
      return $null
    }
    $creationDate = if ($process.CreationDate -is [datetime]) {
      [datetime]$process.CreationDate
    } else {
      [System.Management.ManagementDateTimeConverter]::ToDateTime([string]$process.CreationDate)
    }
    $creationTimeFileTime = [long]$creationDate.ToUniversalTime().ToFileTimeUtc()
    $creationTimeFileTime -= $creationTimeFileTime % 10
    return [pscustomobject]@{
      ProcessId = [int]$process.ProcessId
      CreationTimeFileTime = [string]$creationTimeFileTime
      Argv = @([OpenClaw.Restart.NativeMethods]::ParseCommandLine([string]$process.CommandLine))
    }
  } catch {
    Write-RestartLog "openclaw restart process query failed source=update pid=$ProcessId error=$($_.Exception.Message)"
    return $null
  }
}

function Test-OpenClawArgvEqual {
  param([string[]]$Actual, [string[]]$Expected)
  if ($Actual.Count -ne $Expected.Count) {
    return $false
  }
  for ($index = 0; $index -lt $Actual.Count; $index++) {
    $actualArg = $Actual[$index]
    $expectedArg = $Expected[$index]
    # Windows may expand a bare launcher executable in the process command line.
    # Qualified paths and every non-executable argument remain exact.
    if ($index -eq 0 -and $expectedArg -notmatch '[\\\\/]') {
      $actualArg = [IO.Path]::GetFileName($actualArg)
      if (-not $actualArg.EndsWith('.exe', [StringComparison]::OrdinalIgnoreCase)) {
        $actualArg += '.exe'
      }
      if (-not $expectedArg.EndsWith('.exe', [StringComparison]::OrdinalIgnoreCase)) {
        $expectedArg += '.exe'
      }
    }
    if (-not [string]::Equals($actualArg, $expectedArg, [StringComparison]::OrdinalIgnoreCase)) {
      return $false
    }
  }
  return $true
}

function Test-OpenClawSameProcess {
  param($Expected, $Actual)
  return (
    $null -ne $Actual -and
    $Actual.ProcessId -eq $Expected.ProcessId -and
    $Actual.CreationTimeFileTime -eq $Expected.CreationTimeFileTime -and
    (Test-OpenClawArgvEqual -Actual $Actual.Argv -Expected $Expected.Argv)
  )
}

function Get-OpenClawListenerKillDecision {
  param(
    [int]$CandidatePid,
    [string[]]$ExpectedArgv,
    $ObservedProcess,
    [string]$HeldProcessCreationTimeFileTime,
    $RecheckedListeners,
    $RecheckedProcess
  )
  if ($ExpectedArgv.Count -eq 0) {
    return "expected-command-unavailable"
  }
  if ($null -eq $ObservedProcess -or $ObservedProcess.ProcessId -ne $CandidatePid) {
    return "process-unavailable"
  }
  if (-not (Test-OpenClawArgvEqual -Actual $ObservedProcess.Argv -Expected $ExpectedArgv)) {
    return "command-mismatch"
  }
  if ($HeldProcessCreationTimeFileTime -ne $ObservedProcess.CreationTimeFileTime) {
    return "process-replaced"
  }
  if (-not $RecheckedListeners.Known) {
    return "listener-query-unavailable"
  }
  if ($RecheckedListeners.Pids -notcontains $CandidatePid) {
    return "no-longer-listening"
  }
  if (-not (Test-OpenClawSameProcess -Expected $ObservedProcess -Actual $RecheckedProcess)) {
    return "process-replaced"
  }
  return "kill"
}

function Invoke-OpenClawVerifiedListenerKill {
  param(
    [int]$ProcessId,
    [int]$Port,
    [string[]]$ExpectedArgv,
    [scriptblock]$ProcessQuery = { param([int]$QueryPid) Get-OpenClawProcessFacts -ProcessId $QueryPid },
    [scriptblock]$ListenerQuery = { param([int]$QueryPort) Get-OpenClawListenerSnapshot -Port $QueryPort },
    [scriptblock]$ProcessOpen = { param([int]$QueryPid) [OpenClaw.Restart.NativeMethods]::TryOpenProcess($QueryPid) }
  )

  $observedProcess = & $ProcessQuery $ProcessId
  if ($null -eq $observedProcess) {
    Write-RestartLog "openclaw restart skipped listener source=update pid=$ProcessId decision=process-unavailable"
    return
  }
  if ($ExpectedArgv.Count -eq 0) {
    Write-RestartLog "openclaw restart skipped listener source=update pid=$ProcessId decision=expected-command-unavailable"
    return
  }
  if (-not (Test-OpenClawArgvEqual -Actual $observedProcess.Argv -Expected $ExpectedArgv)) {
    Write-RestartLog "openclaw restart skipped listener source=update pid=$ProcessId decision=command-mismatch"
    return
  }

  $lease = $null
  try {
    $lease = & $ProcessOpen $ProcessId
    if ($null -eq $lease) {
      Write-RestartLog "openclaw restart skipped listener source=update pid=$ProcessId decision=process-handle-unavailable"
      return
    }

    $recheckedListeners = & $ListenerQuery $Port
    $recheckedProcess = & $ProcessQuery $ProcessId
    $decisionParams = @{
      CandidatePid = $ProcessId
      ExpectedArgv = $ExpectedArgv
      ObservedProcess = $observedProcess
      HeldProcessCreationTimeFileTime = [string]$lease.CreationTimeFileTime
      RecheckedListeners = $recheckedListeners
      RecheckedProcess = $recheckedProcess
    }
    $decision = Get-OpenClawListenerKillDecision @decisionParams
    if ($decision -ne "kill") {
      Write-RestartLog "openclaw restart skipped listener source=update pid=$ProcessId decision=$decision"
      return
    }

    if ($lease.Terminate()) {
      Write-RestartLog "openclaw restart killed stale listener source=update pid=$ProcessId"
    } else {
      Write-RestartLog "openclaw restart failed to kill stale listener source=update pid=$ProcessId"
    }
  } catch {
    Write-RestartLog "openclaw restart ownership verification failed source=update pid=$ProcessId error=$($_.Exception.Message)"
  } finally {
    if ($null -ne $lease) {
      $lease.Dispose()
    }
  }
}
# OPENCLAW_RESTART_KILL_POLICY_END

function Invoke-OpenClawStartupLauncher {
  param([string]$LauncherPath)
  $launcherPath = $LauncherPath
  if (-not (Test-Path -LiteralPath $launcherPath)) {
    Write-RestartLog "openclaw restart startup launcher missing source=update path=$launcherPath"
    return 1
  }

  try {
    Start-Process -FilePath $launcherPath -WindowStyle Hidden | Out-Null
    Write-RestartLog "openclaw restart launched startup fallback source=update path=$launcherPath"
    return 0
  } catch {
    Write-RestartLog "openclaw restart startup fallback failed source=update error=$($_.Exception.Message)"
    return 1
  }
}

$taskName = ${quotedTaskName}
$port = ${port}
$gatewayScriptPath = ${quotedGatewayScriptPath}
$expectedGatewayArgv = @(${expectedGatewayArgv})
Write-RestartLog "openclaw restart attempt source=update target=$taskName"

$taskState = Get-OpenClawScheduledTaskState -TaskName $taskName
if ($taskState -eq "Running") {
  $endStatus = Invoke-OpenClawSchtasksWithTimeout -Arguments @("/End", "/TN", $taskName) -TimeoutSeconds 10
  if ($endStatus -ne 0) {
    Write-RestartLog "openclaw restart schtasks end did not complete cleanly source=update status=$endStatus"
  }
} else {
  Write-RestartLog "openclaw restart skipped schtasks end source=update state=$taskState"
}

for ($attempt = 1; $attempt -le 10; $attempt++) {
  $listenerSnapshot = Get-OpenClawListenerSnapshot -Port $port
  if (-not $listenerSnapshot.Known) {
    if ($attempt -eq 10) {
      Write-RestartLog "openclaw restart listener ownership unavailable source=update; refusing force-kill"
      break
    }
    Start-Sleep -Seconds 1
    continue
  }

  $listeners = @($listenerSnapshot.Pids)
  if ($listeners.Count -eq 0) {
    break
  }

  if ($attempt -eq 10) {
    foreach ($listenerPid in $listeners) {
      Invoke-OpenClawVerifiedListenerKill -ProcessId $listenerPid -Port $port -ExpectedArgv $expectedGatewayArgv
    }
    break
  }

  Start-Sleep -Seconds 1
}

$status = Invoke-OpenClawSchtasksWithTimeout -Arguments @("/Run", "/TN", $taskName) -TimeoutSeconds 30
if ($status -ne 0) {
  $status = Invoke-OpenClawStartupLauncher -LauncherPath $gatewayScriptPath
}
if ($status -eq 0) {
  Write-RestartLog "openclaw restart done source=update"
} else {
  Write-RestartLog "openclaw restart failed source=update status=$status"
}

exit $status
`;
		} else return null;
		const scriptDir = await fs$1.mkdtemp(path.join(os.tmpdir(), "openclaw-restart-"));
		const scriptPath = path.join(scriptDir, filename);
		try {
			await fs$1.writeFile(scriptPath, scriptContent, {
				mode: 493,
				flag: "wx"
			});
		} catch (error) {
			await fs$1.rm(scriptDir, {
				recursive: true,
				force: true
			}).catch(() => {});
			throw error;
		}
		return scriptPath;
	} catch {
		return null;
	}
}
/**
* Executes the prepared restart script as a **detached** process.
*
* The script must outlive the CLI process because the CLI itself is part
* of the service being restarted — `systemctl restart` / `launchctl
* kickstart -k` will terminate the current process tree.  Using
* `spawn({ detached: true })` + `unref()` ensures the script survives
* the parent's exit.
*
* Resolves immediately after spawning; the script runs independently.
*/
async function runRestartScript(scriptPath) {
	const isWindows = process.platform === "win32";
	const file = isWindows ? getWindowsCmdExePath() : "/bin/sh";
	const args = isWindows ? [
		"/d",
		"/s",
		"/c",
		quoteCmdScriptArg(scriptPath)
	] : [scriptPath];
	try {
		const child = spawn(file, args, {
			detached: true,
			stdio: "ignore",
			windowsHide: true
		});
		child.on("error", () => {});
		child.unref();
	} catch {}
}
//#endregion
//#region src/cli/update-cli/update-command-service-env.ts
const SERVICE_REFRESH_PATH_ENV_KEYS = [
	"OPENCLAW_HOME",
	"OPENCLAW_STATE_DIR",
	"OPENCLAW_CONFIG_PATH"
];
const MANAGED_UPDATE_SELECTOR_ENV_KEYS = ["OPENCLAW_HOME", ...GATEWAY_SERVICE_SELECTOR_ENV_KEYS];
function applyManagedServiceSelectorEnv(params) {
	const resolved = { ...params.baseEnv };
	const selectorEnv = params.selectorEnv ?? params.serviceEnv;
	for (const key of MANAGED_UPDATE_SELECTOR_ENV_KEYS) if (selectorEnv[key]?.trim()) resolved[key] = params.serviceEnv[key];
	else delete resolved[key];
	return resolved;
}
function resolveServiceRefreshEnv(env, invocationCwd) {
	const resolvedEnv = { ...env };
	for (const key of SERVICE_REFRESH_PATH_ENV_KEYS) {
		const rawValue = resolvedEnv[key]?.trim();
		if (!rawValue) continue;
		if (rawValue.startsWith("~") || path.isAbsolute(rawValue) || path.win32.isAbsolute(rawValue)) {
			resolvedEnv[key] = rawValue;
			continue;
		}
		if (!invocationCwd) {
			resolvedEnv[key] = rawValue;
			continue;
		}
		resolvedEnv[key] = path.resolve(invocationCwd, rawValue);
	}
	return resolvedEnv;
}
function disableUpdatedPackageCompileCacheEnv(env) {
	return {
		...env,
		NODE_DISABLE_COMPILE_CACHE: "1"
	};
}
function resolveUpdatedInstallCommandEnv(params) {
	const processEnv = resolveServiceRefreshEnv(params?.processEnv ?? process.env, params?.invocationCwd);
	const serviceEnv = params?.serviceEnv ? resolveServiceRefreshEnv(params.serviceEnv, params.invocationCwd) : void 0;
	return disableUpdatedPackageCompileCacheEnv({
		...processEnv,
		...serviceEnv
	});
}
function resolveOwnedManagedUpdateEnv(params) {
	const resolved = resolveUpdatedInstallCommandEnv(params);
	return applyManagedServiceSelectorEnv({
		baseEnv: resolved,
		serviceEnv: resolved,
		selectorEnv: params.serviceDefinitionEnv ?? params.serviceEnv
	});
}
function resolvePostInstallDoctorEnv(params) {
	const resolvedEnv = disableUpdatedPackageCompileCacheEnv(params?.baseEnv ?? process.env);
	if (!params?.serviceEnv) return resolvedEnv;
	return applyManagedServiceSelectorEnv({
		baseEnv: resolvedEnv,
		serviceEnv: resolveServiceRefreshEnv(params.serviceEnv, params.invocationCwd)
	});
}
//#endregion
//#region src/cli/update-cli/update-command-launch-agent-recovery.ts
async function recoverInstalledLaunchAgentAfterUpdate(params) {
	if ((params.deps?.platform ?? process.platform) !== "darwin") return {
		attempted: false,
		recovered: false
	};
	const service = params.service ?? resolveGatewayService();
	const readState = params.deps?.readState ?? readGatewayServiceState;
	const recover = params.deps?.recover ?? recoverInstalledLaunchAgent;
	const state = await readState(service, { env: params.env }).catch(() => null);
	if (!state || state.loadState.status !== "not-loaded") return {
		attempted: false,
		recovered: false
	};
	if (state && !state.installed && !state.runtime?.missingSupervision) return {
		attempted: false,
		recovered: false
	};
	let recovered;
	try {
		recovered = await recover({
			result: "restarted",
			env: state?.env ?? params.env
		});
	} catch (error) {
		return {
			attempted: true,
			recovered: false,
			detail: error instanceof Error ? error.message : String(error)
		};
	}
	if (!recovered) return {
		attempted: true,
		recovered: false,
		detail: "LaunchAgent was installed but not loaded; automatic bootstrap/kickstart recovery failed."
	};
	return {
		attempted: true,
		recovered: true,
		message: recovered.message
	};
}
//#endregion
//#region src/cli/update-cli/update-command-service-recovery.ts
const CLI_NAME$1 = resolveCliName();
function isPackageManagerUpdateMode(mode) {
	return mode === "npm" || mode === "pnpm" || mode === "bun";
}
function shouldUseLegacyProcessRestartAfterUpdate(params) {
	return !isPackageManagerUpdateMode(params.updateMode);
}
async function recoverLaunchAgentAndRecheckGatewayHealth(params) {
	if (params.health.healthy || params.preserveDefinition) return {
		health: params.health,
		launchAgentRecovery: null
	};
	const launchAgentRecovery = await (params.deps?.recoverLaunchAgent ?? recoverInstalledLaunchAgentAfterUpdate)({
		service: params.service,
		env: params.env
	});
	if (!launchAgentRecovery.recovered) return {
		health: params.health,
		launchAgentRecovery
	};
	return {
		health: await (params.deps?.waitForHealthy ?? waitForGatewayHealthyRestart)({
			service: params.service,
			port: params.port,
			expectedVersion: params.expectedVersion,
			...params.expectedBuildId ? { expectedBuildId: params.expectedBuildId } : {},
			env: params.env,
			supervisorKeepsAlive: true
		}),
		launchAgentRecovery
	};
}
async function hasLoadedLaunchdKeepAliveSupervisor(params) {
	if (process.platform !== "darwin") return false;
	return await params.service.isLoaded({ env: params.env }).catch(() => false);
}
function formatPostUpdateGatewayRecoveryLine(platform) {
	const restartCommand = replaceCliName(formatCliCommand("openclaw gateway restart"), CLI_NAME$1);
	const installCommand = replaceCliName(formatCliCommand("openclaw gateway install --force"), CLI_NAME$1);
	const statusCommand = replaceCliName(formatCliCommand("openclaw gateway status --deep"), CLI_NAME$1);
	if (platform === "darwin") return `Recovery: run \`${restartCommand}\`; if the LaunchAgent is installed but not loaded, run \`${installCommand}\` from the logged-in macOS user session, then rerun \`${statusCommand}\`.`;
	if (platform === "linux") return `Recovery: run \`${restartCommand}\`; if the systemd user service is missing, stale, or not active, run \`${installCommand}\` from the same user account, then rerun \`${statusCommand}\`.`;
	if (platform === "win32") return `Recovery: run \`${restartCommand}\`; if the gateway Scheduled Task or Windows login item is missing, stale, or not running, run \`${installCommand}\` from the same user account, then rerun \`${statusCommand}\`.`;
	return `Recovery: run \`${restartCommand}\`; if the local service manager reports the gateway service is missing, stale, or not running, run \`${installCommand}\` from the same user account, then rerun \`${statusCommand}\`.`;
}
function formatPostUpdateGatewayRecoveryInstructions(result, platform = process.platform) {
	const lines = [formatPostUpdateGatewayRecoveryLine(platform)];
	const beforeVersion = normalizeOptionalString(result.before?.version);
	if (isPackageManagerUpdateMode(result.mode) && beforeVersion) lines.push(`Rollback: reinstall OpenClaw ${beforeVersion} with the same package manager, then rerun \`${replaceCliName(formatCliCommand("openclaw gateway install --force"), CLI_NAME$1)}\`.`);
	return lines;
}
//#endregion
//#region src/cli/update-cli/update-command-service.ts
const CLI_NAME = resolveCliName();
const SERVICE_REFRESH_TIMEOUT_MS = 6e4;
const DEFINITION_DENIAL = /\bSERVICE_DEFINITION_(?:SEALED|UNKNOWN):[^\n]*/;
const POST_REFRESH_ALREADY_HEALTHY_ATTEMPTS = 10;
const POST_REFRESH_ALREADY_HEALTHY_DELAY_MS = 500;
const JSON_MODE_SERVICE_STDOUT = new Writable({ write(_chunk, _encoding, callback) {
	callback();
} });
function shouldPrepareUpdatedInstallRestart(params) {
	return isPackageManagerUpdateMode(params.updateMode) || params.updateMode === "git" && params.serviceStoppedForUpdate ? params.serviceInstalled : params.serviceLoaded && (params.updateMode !== "git" || params.serviceMatchesUpdateRoot === true);
}
function resolvePreparedGatewayUpdatePolicy(stopState, shouldRestart) {
	const verdict = stopState?.serviceUpdateVerdict;
	return {
		allowGatewayServiceRepair: verdict?.kind === "owned" && verdict.refreshDefinition,
		allowGatewayActivation: shouldRestart && stopState?.stopped === true && verdict?.kind === "owned"
	};
}
async function inspectManagedGatewayServiceBeforeUpdate(params) {
	const { state, root } = params;
	const { command } = state;
	const unavailable = () => ({
		kind: "unavailable",
		message: "Gateway service management skipped: its owner or runtime could not be inspected. Code update can continue; run `openclaw gateway status --deep` and restart the gateway manually when service access is restored."
	});
	if (!command) return !state.installed && !state.running && state.runtime?.missingUnit ? { kind: "absent" } : unavailable();
	const ownsRoot = await gatewayServiceCommandUsesRoot({
		root,
		command
	});
	if (ownsRoot === false) return { kind: "foreign" };
	if (state.loadState.status === "unknown" || state.runtime?.status !== "running" && state.runtime?.status !== "stopped") return unavailable();
	const serialized = stableStringify(command);
	if (Buffer.byteLength(serialized) > 4 * 1024 * 1024) return unavailable();
	const fingerprint = sha256Hex(serialized);
	return ownsRoot ? {
		kind: "owned",
		root,
		fingerprint,
		refreshDefinition: (state.definitionMutationCapability?.kind ?? "writable") === "writable"
	} : {
		kind: "unresolved",
		root,
		fingerprint
	};
}
function matchesStoppedService(before, state, inspection) {
	const verdict = before.serviceUpdateVerdict;
	const refreshDefinition = verdict?.kind === "owned" && verdict.refreshDefinition;
	const resolveName = process.platform === "darwin" ? resolveLaunchAgentLabel : process.platform === "win32" ? resolveTaskName : resolveSystemdServiceName;
	return Boolean(before.serviceEnv && state.command && verdict && "fingerprint" in verdict && resolveGatewayProfileSuffix(before.serviceEnv.OPENCLAW_PROFILE) === resolveGatewayProfileSuffix(state.env.OPENCLAW_PROFILE) && resolveName(before.serviceEnv) === resolveName(state.env) && (refreshDefinition || "fingerprint" in inspection && inspection.fingerprint === verdict.fingerprint));
}
async function revalidateManagedGatewayServiceAfterUpdate(params) {
	const before = params.preManagedServiceStop;
	const verdict = before?.serviceUpdateVerdict;
	assertGatewayServiceManagementAllowedForUpdate(params.state.env);
	const inspection = await inspectManagedGatewayServiceBeforeUpdate(params);
	if (before && verdict && (verdict.kind === "owned" || verdict.kind === "unresolved") && (inspection.kind !== verdict.kind || !matchesStoppedService(before, params.state, inspection))) throw new GatewayServiceUpdateOwnershipError("Gateway service ownership or manager identity changed; inspect it before restarting manually.", void 0);
	return inspection.kind === "owned" && verdict?.kind === "owned" && !verdict.refreshDefinition ? {
		...inspection,
		refreshDefinition: false
	} : inspection;
}
var GatewayServiceUpdateOwnershipError = class extends Error {
	constructor(message, cause) {
		super(message, { cause });
		this.name = "GatewayServiceUpdateOwnershipError";
	}
};
function resolveGatewayServiceManagementBlockMessageForUpdate(env = process.env) {
	try {
		assertGatewayServiceManagementAllowedForUpdate(env);
		return;
	} catch (err) {
		return err instanceof Error ? err.message : String(err);
	}
}
function assertGatewayServiceManagementAllowedForUpdate(env = process.env) {
	try {
		assertGatewayServiceMutationAllowed("manage the gateway service during update", env);
	} catch (err) {
		throw new GatewayServiceUpdateOwnershipError(err instanceof Error ? err.message : String(err), err);
	}
}
function isGatewayServiceManagementAllowedForUpdate(env = process.env) {
	return resolveGatewayServiceManagementBlockMessageForUpdate(env) === void 0;
}
var UpdateCommandAbort = class extends Error {
	constructor() {
		super("openclaw-update-abort");
		this.name = "UpdateCommandAbort";
	}
};
function createAggregateErrorWithCause(errors, message, cause) {
	return new AggregateError(errors, message, { cause });
}
function parsePositivePid(value) {
	if (typeof value === "number") return Number.isFinite(value) && value > 0 ? Math.floor(value) : null;
	const trimmed = typeof value === "string" ? value.trim() : "";
	return /^\d+$/u.test(trimmed) ? parseStrictPositiveInteger(trimmed) ?? null : null;
}
function gatewayAncestryBlockMessage(pid) {
	const gatewayPid = parsePositivePid(pid);
	if (gatewayPid === null) return;
	if (!(isGatewayServiceEnv(process.env) && parsePositivePid(process.env["OPENCLAW_GATEWAY_SERVICE_PID"]) === gatewayPid) && !getSelfAndAncestorPidsSync().has(gatewayPid)) return;
	return `openclaw update detected it is running inside the gateway process tree.
Gateway PID ${gatewayPid} is an ancestor of this process, so this updater cannot safely stop or restart the gateway that owns it.
Run \`${replaceCliName(formatCliCommand("openclaw update"), CLI_NAME)}\` from a shell outside the gateway service, or stop the gateway service first and then update.`;
}
function serviceControlStdoutForMode(jsonMode) {
	return jsonMode ? JSON_MODE_SERVICE_STDOUT : process.stdout;
}
function armWindowsTaskAutoStartRecovery(serviceEnv) {
	let restorePromise;
	let unregisterSignalExitBarrier = () => {};
	let finishUpdate;
	let interrupted = false;
	const unregisterSignalExitGate = registerSignalExitGate(new Promise((resolve) => {
		finishUpdate = resolve;
	}));
	const onSignal = (exitCode) => {
		interrupted = true;
		waitForSignalExitBarriers().catch((err) => {
			defaultRuntime.error(`Failed to complete update shutdown cleanup: ${String(err)}`);
		}).finally(() => {
			process.exit(exitCode);
		});
	};
	const onSigint = () => onSignal(130);
	const onSigterm = () => onSignal(143);
	const onSigbreak = () => onSignal(130);
	const removeSignalHandlers = () => {
		process.off("SIGINT", onSigint);
		process.off("SIGTERM", onSigterm);
		process.off("SIGBREAK", onSigbreak);
		unregisterSignalExitBarrier();
	};
	const complete = () => {
		finishUpdate?.();
		finishUpdate = void 0;
		unregisterSignalExitGate();
	};
	const restore = () => {
		restorePromise ??= suspensionPromise.then(async (suspended) => {
			if (suspended) await resumeScheduledTaskAutoStartAfterUpdate(serviceEnv);
		}).finally(removeSignalHandlers);
		return restorePromise;
	};
	process.on("SIGINT", onSigint);
	process.on("SIGTERM", onSigterm);
	process.on("SIGBREAK", onSigbreak);
	unregisterSignalExitBarrier = registerSignalExitBarrier(restore);
	const suspensionPromise = suspendScheduledTaskAutoStartForUpdate(serviceEnv);
	return {
		suspended: suspensionPromise,
		restore,
		complete,
		interrupted: () => interrupted
	};
}
async function abortWindowsTaskUpdateIfInterrupted(recovery) {
	if (!recovery.interrupted()) return;
	try {
		await recovery.restore();
	} finally {
		recovery.complete();
	}
	throw new UpdateCommandAbort();
}
async function maybeSuspendWindowsTaskAutoStartForPackageUpdate(params) {
	if (params.updateInstallKind !== "package" || process.platform !== "win32" || !params.serviceEnv) return;
	const recovery = armWindowsTaskAutoStartRecovery(params.serviceEnv);
	let suspended;
	try {
		suspended = await recovery.suspended;
	} catch (err) {
		await recovery.restore().catch(() => void 0);
		recovery.complete();
		throw err;
	}
	await abortWindowsTaskUpdateIfInterrupted(recovery);
	if (!suspended) {
		try {
			await recovery.restore();
		} finally {
			recovery.complete();
		}
		return;
	}
	return recovery;
}
async function maybeResumeWindowsTaskAutoStartAfterPackageUpdate(stopState) {
	if (!stopState?.windowsTaskAutoStartRecovery) return;
	await stopState.windowsTaskAutoStartRecovery.restore();
	stopState.windowsTaskAutoStartRecovery = void 0;
}
async function restoreWindowsTaskAutoStartOrExit(stopState) {
	try {
		await maybeResumeWindowsTaskAutoStartAfterPackageUpdate(stopState);
		return true;
	} catch (err) {
		defaultRuntime.error(`Failed to restore Windows Scheduled Task autostart after package update: ${String(err)}`);
		defaultRuntime.exit(1);
		return false;
	}
}
async function maybeStopManagedServiceBeforeMutableUpdate(params) {
	const uninspected = {
		stopped: false,
		inspected: false,
		runtimeInspected: false,
		running: false
	};
	const serviceMutationSkipMessage = resolveGatewayServiceManagementBlockMessageForUpdate(process.env);
	if (serviceMutationSkipMessage) return {
		...uninspected,
		serviceMutationAllowed: false,
		serviceMutationSkipMessage
	};
	let service;
	let serviceState;
	try {
		service = resolveGatewayService();
		serviceState = await readGatewayServiceState(service, {
			env: process.env,
			requireEffective: true,
			validateEnvBeforeStatusRead: assertGatewayServiceManagementAllowedForUpdate,
			timeoutMs: params.timeoutMs
		});
	} catch (err) {
		if (err instanceof GatewayServiceUpdateOwnershipError) return {
			...uninspected,
			serviceMutationAllowed: false,
			blockMessage: err.message
		};
		return {
			...uninspected,
			serviceMutationAllowed: false,
			serviceMutationSkipMessage: "Gateway service management skipped: inspection is unavailable. Code update can continue; run `openclaw gateway status --deep` and restart the gateway manually when service access is restored."
		};
	}
	const serviceUpdateVerdict = await inspectManagedGatewayServiceBeforeUpdate({
		root: params.root,
		state: serviceState
	});
	const inspected = {
		stopped: false,
		inspected: true,
		runtimeInspected: ["running", "stopped"].includes(serviceState.runtime?.status ?? ""),
		running: serviceState.running,
		serviceEnv: serviceState.env,
		serviceUpdateVerdict
	};
	if (serviceUpdateVerdict.kind === "unavailable") return {
		...inspected,
		serviceMutationAllowed: false,
		serviceMutationSkipMessage: serviceUpdateVerdict.message
	};
	if (serviceUpdateVerdict.kind === "foreign") return {
		...inspected,
		serviceMutationAllowed: false,
		serviceMutationSkipMessage: "Gateway service management skipped: the service belongs to a different OpenClaw installation and was left untouched."
	};
	if (serviceUpdateVerdict.kind === "absent" || params.phase === "inspect") return inspected;
	const suspendTask = () => maybeSuspendWindowsTaskAutoStartForPackageUpdate({
		updateInstallKind: params.updateInstallKind,
		serviceEnv: serviceState.env
	});
	const supervisorMayRespawn = params.shouldRestart && serviceState.loadState.status === "loaded" && (process.platform === "darwin" ? await service.isEnabled?.({ env: serviceState.env }) === true : process.env.OPENCLAW_UPDATE_RUN_HANDOFF === "1");
	if (!params.shouldRestart || !serviceState.running && !supervisorMayRespawn) {
		if (!params.shouldRestart && !params.jsonMode && serviceState.running) {
			const warning = `--no-restart is set while the managed gateway service is running; the ${params.updateInstallKind} update will not stop or restart that process.`;
			defaultRuntime.log(theme.warn(warning));
		}
		const windowsTaskAutoStartRecovery = !params.shouldRestart && isGatewayServiceEnv(process.env) ? void 0 : await suspendTask();
		return {
			...inspected,
			...windowsTaskAutoStartRecovery ? { windowsTaskAutoStartRecovery } : {}
		};
	}
	const blockMessage = gatewayAncestryBlockMessage(serviceState.runtime?.pid);
	if (blockMessage) return {
		...inspected,
		running: true,
		blockMessage
	};
	if (!params.jsonMode) {
		const message = `Stopping managed gateway service before ${params.updateInstallKind} update...`;
		defaultRuntime.log(theme.muted(message));
	}
	const windowsTaskAutoStartRecovery = await suspendTask();
	try {
		await service.stop({
			env: serviceState.env,
			stdout: serviceControlStdoutForMode(params.jsonMode)
		});
		if (windowsTaskAutoStartRecovery) await abortWindowsTaskUpdateIfInterrupted(windowsTaskAutoStartRecovery);
	} catch (err) {
		if (err instanceof UpdateCommandAbort) throw err;
		if (windowsTaskAutoStartRecovery) {
			try {
				await windowsTaskAutoStartRecovery.restore();
			} catch (resumeErr) {
				throw createAggregateErrorWithCause([err, resumeErr], `Failed to stop the managed gateway (${String(err)}) and restore Windows Scheduled Task autostart (${String(resumeErr)})`, err);
			} finally {
				windowsTaskAutoStartRecovery.complete();
			}
			if (windowsTaskAutoStartRecovery.interrupted()) throw new UpdateCommandAbort();
		}
		throw err;
	}
	return {
		...inspected,
		stopped: true,
		serviceDefinitionEnv: resolveManagedGatewayServiceCommand(serviceState.command)?.environment ?? {},
		...windowsTaskAutoStartRecovery ? { windowsTaskAutoStartRecovery } : {}
	};
}
async function maybeRestartServiceAfterFailedMutableUpdate(params) {
	const before = params.preManagedServiceStop;
	if (!before?.stopped || !before.serviceEnv) return;
	try {
		const verdict = before.serviceUpdateVerdict;
		if (!verdict || !("root" in verdict)) throw new Error("Stopped service ownership is unknown; restart it manually after inspection.");
		const service = resolveGatewayService();
		const state = await readGatewayServiceState(service, {
			env: before.serviceEnv,
			requireEffective: true,
			validateEnvBeforeStatusRead: assertGatewayServiceManagementAllowedForUpdate
		});
		const revalidated = await revalidateManagedGatewayServiceAfterUpdate({
			state,
			root: params.root ?? verdict.root,
			preManagedServiceStop: before
		});
		await service.restart({
			env: state.env,
			preserveDefinition: revalidated.kind !== "owned" || !revalidated.refreshDefinition,
			stdout: serviceControlStdoutForMode(params.jsonMode)
		});
		if (!params.jsonMode) defaultRuntime.log(theme.muted("Restarted managed gateway service after failed update."));
	} catch (err) {
		defaultRuntime.error(`Failed to restart managed gateway service after failed update: ${String(err)}`);
	}
}
function shouldBlockMutableUpdateFromGatewayServiceEnv(params) {
	const stopState = params.preManagedServiceStop;
	return isGatewayServiceEnv(process.env) && (!stopState?.inspected || !stopState.stopped && (!stopState.runtimeInspected || stopState.running && !stopState.blockMessage));
}
function formatCommandFailure(stdout, stderr) {
	const detail = `${stderr}\n${stdout}`.match(DEFINITION_DENIAL)?.[0] ?? (stderr || stdout).trim();
	return detail ? detail.split("\n").slice(-3).join("\n") : "command returned a non-zero exit code";
}
function tryResolveInvocationCwd() {
	try {
		return process.cwd();
	} catch {
		return;
	}
}
async function resolvePackageRuntimePreflight(params) {
	const nodeRunner = normalizeOptionalString(params.nodeRunner);
	const unchanged = () => nodeRunner ? { nodeRunner } : {};
	const target = params.tag.trim();
	if (!target || !canResolveRegistryVersionForPackageTarget(params.tag) || params.spec && !canResolveRegistryVersionForPackageTarget(params.spec)) return ok(unchanged());
	const status = await fetchNpmPackageTargetStatus({
		target,
		spec: params.spec,
		timeoutMs: params.timeoutMs,
		command: params.command,
		cwd: params.cwd,
		env: params.env
	});
	if (status.error) return ok(unchanged());
	const runtime = await resolvePackageRuntimeForPreflight({
		nodeRunner,
		timeoutMs: params.timeoutMs
	});
	const satisfies = nodeVersionSatisfiesEngine(runtime.version, status.nodeEngine);
	const targetVersion = status.version ?? target;
	const unchangedRuntime = {
		...unchanged(),
		targetVersion
	};
	if (satisfies === true) return ok(unchangedRuntime);
	const fallbackNodeRunner = normalizeOptionalString(params.fallbackNodeRunner);
	if (nodeRunner && fallbackNodeRunner && fallbackNodeRunner !== nodeRunner) {
		if (nodeVersionSatisfiesEngine((await resolvePackageRuntimeForPreflight({
			nodeRunner: fallbackNodeRunner,
			timeoutMs: params.timeoutMs
		})).version, status.nodeEngine) === true) return ok({
			nodeRunner: fallbackNodeRunner,
			replacedNodeRunner: nodeRunner,
			targetVersion
		});
	}
	if (satisfies !== false) return ok(unchangedRuntime);
	return err([
		`${runtime.nodeRunner ? `Node ${runtime.version ?? "unknown"} at ${runtime.nodeRunner}` : `Node ${runtime.version ?? "unknown"}`} is too old for openclaw@${targetVersion}.`,
		`The requested package requires ${status.nodeEngine}.`,
		runtime.nodeRunner ? "Upgrade the Node runtime that owns the managed Gateway service, then rerun `openclaw update`." : "Upgrade to Node 22.22.3+, Node 24.15.0+, or Node 25.9.0+, then rerun `openclaw update`.",
		"Bare `npm i -g openclaw` can silently install an older compatible release.",
		"After upgrading Node, use `npm i -g openclaw@latest`."
	].join("\n"));
}
async function resolvePackageRuntimeForPreflight(params) {
	const nodeRunner = normalizeOptionalString(params.nodeRunner);
	if (!nodeRunner) return { version: process.versions.node ?? null };
	const res = await runCommandWithTimeout([nodeRunner, "--version"], { timeoutMs: Math.min(params.timeoutMs ?? 1e4, 1e4) }).catch(() => null);
	return {
		version: res?.code === 0 ? res.stdout.trim().replace(/^v/u, "") || null : null,
		nodeRunner
	};
}
function stripGatewayServiceMarkerEnv(env) {
	const resolvedEnv = { ...env };
	delete resolvedEnv.OPENCLAW_SERVICE_MARKER;
	delete resolvedEnv.OPENCLAW_SERVICE_KIND;
	delete resolvedEnv[GATEWAY_SERVICE_RUNTIME_PID_ENV];
	return resolvedEnv;
}
async function resolveUpdatedGatewayRestartPort(params) {
	const env = params.serviceEnv ?? params.processEnv ?? process.env;
	let config = params.config;
	if (params.serviceCommand) {
		const port = parseTcpPortFromArgs(params.serviceCommand.programArguments);
		if (port !== null) return port;
		config = await createConfigIO({
			env,
			observe: false,
			pluginValidation: "skip",
			suppressFutureVersionWarning: true
		}).readBestEffortConfig();
	}
	return resolveGatewayPort(config, env);
}
function resolvePostUpdateServiceStateReadEnv(params) {
	const fallbackEnv = params.processEnv ?? process.env;
	return params.updateMode === "git" || isPackageManagerUpdateMode(params.updateMode) ? params.preManagedServiceEnv ?? fallbackEnv : fallbackEnv;
}
async function runUpdatedInstallGatewayCommand(params, action, preserveDefinition = false) {
	const installing = action === "install";
	const entrypoint = await resolveGatewayInstallEntrypoint(params.result.root);
	if (!entrypoint) {
		if (installing && !isPackageManagerUpdateMode(params.result.mode)) {
			await runDaemonInstall({
				force: true,
				json: params.opts.json || void 0
			});
			return true;
		}
		throw new Error(`updated install entrypoint not found under ${params.result.root ?? "unknown"}`);
	}
	const args = ["gateway", action];
	if (installing) args.push("--force");
	else if (preserveDefinition) args.push("--preserve-definition");
	if (params.opts.json) args.push("--json");
	const res = await runCommandWithTimeout([
		params.nodeRunner ?? resolveNodeRunner(),
		entrypoint,
		...args
	], {
		cwd: params.result.root,
		env: resolveUpdatedInstallCommandEnv({
			processEnv: installing ? params.serviceInstallEnv ?? process.env : process.env,
			serviceEnv: installing ? void 0 : params.serviceEnv,
			invocationCwd: params.invocationCwd
		}),
		timeoutMs: installing ? SERVICE_REFRESH_TIMEOUT_MS : params.timeoutMs
	});
	if (res.code === 0) return true;
	throw new Error(`updated install ${installing ? "refresh" : "restart"} failed (${entrypoint}): ${formatCommandFailure(res.stdout, res.stderr)}`);
}
async function tryInstallShellCompletion(opts) {
	if (opts.jsonMode || !process.stdin.isTTY) return;
	try {
		const status = await checkShellCompletionStatus(CLI_NAME);
		const generationOptions = { generationMode: "core-only" };
		if (status.usesSlowPattern) {
			defaultRuntime.log(theme.muted("Upgrading shell completion to cached version..."));
			if (!await ensureCompletionCacheExists(CLI_NAME, generationOptions)) throw new Error("completion cache generation failed");
			await installCompletion(status.shell, true, CLI_NAME);
			return;
		}
		if (status.profileInstalled && !status.cacheExists) {
			defaultRuntime.log(theme.muted("Regenerating shell completion cache..."));
			if (!await ensureCompletionCacheExists(CLI_NAME, generationOptions)) throw new Error("completion cache generation failed");
			return;
		}
		if (!status.profileInstalled) {
			defaultRuntime.log("");
			defaultRuntime.log(theme.heading("Shell completion"));
			const shouldInstall = await confirm({
				message: stylePromptMessage(`Enable ${status.shell} shell completion for ${CLI_NAME}?`),
				initialValue: true
			});
			if (isCancel(shouldInstall) || !shouldInstall) {
				if (!opts.skipPrompt) defaultRuntime.log(theme.muted(`Skipped. Run \`${replaceCliName(formatCliCommand("openclaw completion --install"), CLI_NAME)}\` later to enable.`));
				return;
			}
			if (!await ensureCompletionCacheExists(CLI_NAME, generationOptions)) throw new Error("completion cache generation failed");
			await installCompletion(status.shell, opts.skipPrompt, CLI_NAME);
		}
	} catch (err) {
		const message = formatErrorMessage(err);
		defaultRuntime.log(theme.warn(`Shell completion refresh failed: ${message}. Update will continue; retry with: ${replaceCliName(formatCliCommand("openclaw completion --write-state --install"), CLI_NAME)}`));
	}
}
async function tryRealpathOrResolve(value) {
	return await fs$1.realpath(path.resolve(value)).catch(() => path.resolve(value));
}
function resolveManagedServiceNodeRunner(command) {
	const args = command?.programArguments ?? [];
	const runner = args.indexOf("gateway") > 1 ? args[0] : void 0;
	const executable = normalizeOptionalString(runner ? path.basename(runner) : void 0);
	return ["node", "node.exe"].includes(executable?.toLowerCase() ?? "") ? runner : void 0;
}
/**
* Resolve the node binary baked into the managed gateway service unit,
* independent of any package root redirect. This detects when the user's
* current PATH-resolved node differs from the service's baked node even
* when the package root is the same.
*/
async function resolveManagedServiceNodeRunnerOverride() {
	if (!isGatewayServiceManagementAllowedForUpdate(process.env)) return;
	const serviceNode = resolveManagedServiceNodeRunner(await resolveGatewayService().readCommand(process.env, { requireEffective: true }).catch(() => null));
	if (!serviceNode) return;
	const currentNode = resolveNodeRunner();
	const [serviceNodeReal, currentNodeReal] = await Promise.all([tryRealpathOrResolve(serviceNode), tryRealpathOrResolve(currentNode)]);
	return serviceNodeReal === currentNodeReal ? void 0 : serviceNode;
}
async function resolveManagedServicePackageUpdateRoot(params) {
	if (!isGatewayServiceManagementAllowedForUpdate(process.env)) return null;
	const command = await resolveGatewayService().readCommand(process.env, { requireEffective: true }).catch(() => null);
	const layout = await summarizeGatewayServiceLayout(command);
	const serviceRoot = layout?.packageRoot;
	if (!serviceRoot || layout.entrypointSourceCheckout === true) return null;
	const [currentRootReal, serviceRootReal] = await Promise.all([tryRealpathOrResolve(params.root), tryRealpathOrResolve(serviceRoot)]);
	if (currentRootReal === serviceRootReal) return null;
	const nodeRunner = resolveManagedServiceNodeRunner(command);
	return {
		root: serviceRoot,
		previousRoot: params.root,
		...nodeRunner ? { nodeRunner } : {}
	};
}
async function gatewayServiceCommandUsesRoot(params) {
	const expectedRoot = normalizeOptionalString(params.root);
	if (!expectedRoot) return null;
	const command = params.command === void 0 ? isGatewayServiceManagementAllowedForUpdate(params.env ?? process.env) ? await resolveGatewayService().readCommand(params.env ?? process.env, { requireEffective: true }).catch(() => null) : null : params.command;
	const layout = await summarizeGatewayServiceLayout(command);
	const serviceRoot = layout?.packageRoot;
	const serviceEntrypoint = layout?.entrypoint;
	if (!serviceRoot || !serviceEntrypoint || !path.isAbsolute(serviceEntrypoint) && !path.win32.isAbsolute(serviceEntrypoint)) return null;
	const [expectedRootReal, serviceRootReal] = await Promise.all([tryRealpathOrResolve(expectedRoot), tryRealpathOrResolve(serviceRoot)]);
	if (expectedRootReal === serviceRootReal) return true;
	const [expected, actual] = await Promise.all([expectedRootReal, serviceRootReal].map((root) => fs$1.stat(root).catch(() => null)));
	if (expected && actual && expected.dev === actual.dev && expected.ino === actual.ino) return true;
	const managed = command?.managedDefinition;
	if (!managed || await gatewayServiceCommandUsesRoot({
		root: expectedRoot,
		command: managed
	}) !== true) return false;
	const namespace = path.dirname(expectedRootReal);
	const managedLayout = await summarizeGatewayServiceLayout(managed);
	if (serviceEntrypoint !== path.join(namespace, "current", "dist", path.basename(managedLayout?.entrypoint ?? ""))) return false;
	const releases = path.join(namespace, "releases");
	if (serviceRootReal.startsWith(`${releases}${path.sep}`)) return true;
	try {
		for await (const entry of await fs$1.opendir(releases)) {
			const candidate = await fs$1.lstat(path.join(releases, entry.name));
			if (actual && candidate.dev === actual.dev && candidate.ino === actual.ino) return true;
		}
	} catch {}
	return false;
}
async function maybeRestartService(params) {
	if (params.shouldRestart && (!isGatewayServiceManagementAllowedForUpdate(process.env) || !isGatewayServiceManagementAllowedForUpdate(params.serviceEnv ?? process.env))) {
		const message = resolveGatewayServiceManagementBlockMessageForUpdate(process.env) ?? resolveGatewayServiceManagementBlockMessageForUpdate(params.serviceEnv ?? process.env);
		if (message) defaultRuntime.error(message);
		return false;
	}
	let activation = params;
	const verdict = activation.serviceUpdateVerdict;
	let preserveDefinition = verdict?.kind === "unresolved" || verdict?.kind === "owned" && !verdict.refreshDefinition;
	const isPackageUpdate = isPackageManagerUpdateMode(activation.result.mode);
	const requiresVerifiedRestart = () => preserveDefinition || isPackageUpdate || activation.requireRunningServiceAfterRestart;
	const canRestartUpdatedInstall = () => preserveDefinition || isPackageUpdate && (activation.refreshServiceEnv || activation.serviceInstallEnv === null || activation.requireRunningServiceAfterRestart);
	if (preserveDefinition) defaultRuntime.error("Gateway service definition left unchanged; ask its deployment owner to repair stale metadata if needed.");
	if (activation.serviceMutationSkipMessage) {
		defaultRuntime.error(activation.serviceMutationSkipMessage);
		return true;
	}
	const verifyRestartedGateway = async (expectedGatewayVersion, expectedGatewayBuildId, opts = {}) => {
		const service = resolveGatewayService();
		const waitForHealthy = async () => await waitForGatewayHealthyRestart({
			service,
			port: activation.gatewayPort,
			expectedVersion: expectedGatewayVersion,
			...expectedGatewayBuildId ? { expectedBuildId: expectedGatewayBuildId } : {},
			env: activation.serviceEnv,
			requireRunningService: opts.requireRunningService,
			supervisorKeepsAlive: await hasLoadedLaunchdKeepAliveSupervisor({
				service,
				env: activation.serviceEnv
			})
		});
		let health = await waitForHealthy();
		if (!health.healthy && health.staleGatewayPids.length > 0) {
			if (!activation.opts.json) defaultRuntime.log(theme.warn(`Found stale gateway process(es) after restart: ${health.staleGatewayPids.join(", ")}. Cleaning up...`));
			await terminateStaleGatewayPids(health.staleGatewayPids);
			if (canRestartUpdatedInstall()) await runUpdatedInstallGatewayCommand(activation, "restart", preserveDefinition);
			else if (shouldUseLegacyProcessRestartAfterUpdate({ updateMode: activation.result.mode })) await runDaemonRestart();
			health = await waitForHealthy();
		}
		const recoveryVerification = await recoverLaunchAgentAndRecheckGatewayHealth({
			preserveDefinition,
			health,
			service,
			port: activation.gatewayPort,
			expectedVersion: expectedGatewayVersion,
			...expectedGatewayBuildId ? { expectedBuildId: expectedGatewayBuildId } : {},
			env: activation.serviceEnv
		});
		health = recoveryVerification.health;
		const launchAgentRecovery = recoveryVerification.launchAgentRecovery;
		if (launchAgentRecovery?.attempted) defaultRuntime.error(launchAgentRecovery.recovered ? launchAgentRecovery.message : launchAgentRecovery.detail);
		const serviceRuntimeHealthy = !opts.requireRunningService || health.runtime.status === "running";
		if (health.healthy && serviceRuntimeHealthy) {
			if (!activation.opts.json) defaultRuntime.log(theme.success("Gateway: restarted and verified."));
			return true;
		}
		const diagnosticLines = [
			"Gateway did not become healthy after restart.",
			...health.healthy && opts.requireRunningService ? ["Gateway responded, but the managed service did not report running after restart."] : [],
			...renderRestartDiagnostics(health),
			...launchAgentRecovery?.attempted ? [launchAgentRecovery.recovered ? `LaunchAgent recovery: ${launchAgentRecovery.message}` : `LaunchAgent recovery failed: ${launchAgentRecovery.detail}`] : [],
			`Restart log: ${resolveGatewayRestartLogPath(activation.serviceEnv ?? process.env)}`,
			`Run \`${replaceCliName(formatCliCommand("openclaw gateway status --deep"), CLI_NAME)}\` for details.`,
			...formatPostUpdateGatewayRecoveryInstructions(activation.result)
		];
		if (activation.opts.json) defaultRuntime.error(diagnosticLines.join("\n"));
		else {
			defaultRuntime.log(theme.warn(diagnosticLines[0] ?? "Gateway did not become healthy."));
			for (const line of diagnosticLines.slice(1)) defaultRuntime.log(theme.muted(line));
		}
		if (requiresVerifiedRestart() || opts.requireRunningService || expectedGatewayBuildId) return false;
		return !(health.versionMismatch || health.buildIdMismatch || health.activatedPluginErrors?.length);
	};
	if (activation.shouldRestart) {
		if (!activation.opts.json) {
			defaultRuntime.log("");
			defaultRuntime.log(theme.heading("Restarting service..."));
		}
		try {
			let expectedGatewayVersion = requiresVerifiedRestart() ? normalizeOptionalString(activation.result.after?.version) : void 0;
			const expectedGatewayBuildId = activation.channel === "dev" && activation.result.mode === "git" ? normalizeOptionalString(activation.result.after?.buildId) : void 0;
			const canVerifyUpdatedGatewayByVersion = expectedGatewayVersion !== void 0 && expectedGatewayVersion !== normalizeOptionalString(activation.result.before?.version);
			let restarted = false;
			let restartInitiated = false;
			let refreshedGatewayAlreadyHealthy = false;
			let updatedInstallRestartNeedsServiceRootProof = false;
			let restartScriptPath = preserveDefinition ? null : activation.restartScriptPath;
			if (activation.refreshServiceEnv && activation.serviceInstallEnv !== null) try {
				await runUpdatedInstallGatewayCommand(activation, "install");
				if (isPackageUpdate && expectedGatewayVersion) {
					refreshedGatewayAlreadyHealthy = (await waitForGatewayHealthyRestart({
						service: resolveGatewayService(),
						port: activation.gatewayPort,
						expectedVersion: expectedGatewayVersion,
						env: activation.serviceEnv,
						attempts: POST_REFRESH_ALREADY_HEALTHY_ATTEMPTS,
						delayMs: POST_REFRESH_ALREADY_HEALTHY_DELAY_MS
					})).healthy;
					if (refreshedGatewayAlreadyHealthy && !activation.opts.json) defaultRuntime.log(theme.muted("Gateway already reports the updated version after service refresh; skipped redundant restart."));
				}
			} catch (err) {
				defaultRuntime.error(`Failed to refresh gateway service environment from updated install: ${String(err)}`);
				if (DEFINITION_DENIAL.test(String(err))) {
					preserveDefinition = true;
					if (verdict?.kind !== "owned") throw err;
					const state = await readGatewayServiceState(resolveGatewayService(), {
						env: activation.serviceEnv,
						requireEffective: true,
						validateEnvBeforeStatusRead: assertGatewayServiceManagementAllowedForUpdate,
						timeoutMs: activation.timeoutMs
					});
					await revalidateManagedGatewayServiceAfterUpdate({
						state,
						root: activation.result.root ?? verdict.root,
						preManagedServiceStop: {
							serviceEnv: activation.serviceEnv,
							serviceUpdateVerdict: {
								...verdict,
								refreshDefinition: false
							}
						}
					});
					activation = {
						...activation,
						serviceEnv: state.env,
						gatewayPort: await resolveUpdatedGatewayRestartPort({
							serviceEnv: state.env,
							serviceCommand: state.command
						})
					};
					expectedGatewayVersion = normalizeOptionalString(activation.result.after?.version);
					restartScriptPath = null;
				}
				if (isPackageUpdate) {
					restartScriptPath = null;
					updatedInstallRestartNeedsServiceRootProof = !canVerifyUpdatedGatewayByVersion;
				}
			}
			if (!refreshedGatewayAlreadyHealthy && restartScriptPath) {
				await createUpdateConfigSnapshot();
				await runRestartScript(restartScriptPath);
				restartInitiated = true;
			} else if (!refreshedGatewayAlreadyHealthy && canRestartUpdatedInstall()) {
				await createUpdateConfigSnapshot();
				restarted = await runUpdatedInstallGatewayCommand(activation, "restart", preserveDefinition);
				if (updatedInstallRestartNeedsServiceRootProof && await gatewayServiceCommandUsesRoot({
					root: activation.result.root,
					env: activation.serviceEnv
				}) !== true) {
					if (!activation.opts.json) defaultRuntime.log(theme.warn("Gateway service did not point at the updated install after restart."));
					return false;
				}
			} else if (!refreshedGatewayAlreadyHealthy && shouldUseLegacyProcessRestartAfterUpdate({ updateMode: activation.result.mode }) && !activation.skipLegacyServiceRestart) {
				await createUpdateConfigSnapshot();
				restarted = await runDaemonRestart();
			} else if (!refreshedGatewayAlreadyHealthy && !activation.opts.json) defaultRuntime.log(theme.muted("Gateway: restart skipped (no installed service found)."));
			if (refreshedGatewayAlreadyHealthy || restartInitiated || restarted && (preserveDefinition || expectedGatewayVersion !== void 0 || activation.result.mode === "git") || activation.requireRunningServiceAfterRestart) {
				const requireRunningService = updatedInstallRestartNeedsServiceRootProof || activation.requireRunningServiceAfterRestart;
				if (!await verifyRestartedGateway(expectedGatewayVersion, expectedGatewayBuildId, { requireRunningService })) {
					if (!activation.opts.json) defaultRuntime.log("");
					return false;
				}
				if (!activation.opts.json && restartInitiated) {
					defaultRuntime.log(theme.success("Daemon restart completed."));
					defaultRuntime.log("");
				}
			}
			if (!activation.opts.json && restarted && !preserveDefinition) {
				defaultRuntime.log(theme.success("Daemon restarted successfully."));
				defaultRuntime.log("");
				await createUpdateConfigSnapshot();
				process.env.OPENCLAW_UPDATE_IN_PROGRESS = "1";
				process.env[UPDATE_PARENT_SUPPORTS_DOCTOR_CONFIG_WRITE_ENV] = "1";
				try {
					await doctorCommand(defaultRuntime, { nonInteractive: !(process.stdin.isTTY && !activation.opts.json && activation.opts.yes !== true) });
				} catch (err) {
					defaultRuntime.log(theme.warn(`Doctor failed: ${String(err)}`));
				} finally {
					delete process.env.OPENCLAW_UPDATE_IN_PROGRESS;
					delete process.env[UPDATE_PARENT_SUPPORTS_DOCTOR_CONFIG_WRITE_ENV];
				}
			}
		} catch (err) {
			defaultRuntime.error(`Gateway: restart failed: ${String(err)}. Code update remains installed; a service stopped for update may still be stopped. Run \`openclaw gateway status --deep\` and ask its service owner to restart it manually.`);
			if (requiresVerifiedRestart()) return false;
		}
		return true;
	}
	if (!activation.opts.json) {
		defaultRuntime.log("");
		defaultRuntime.log(theme.muted("Gateway: restart skipped (--no-restart)."));
		if (activation.result.mode === "npm" || activation.result.mode === "pnpm") defaultRuntime.log(theme.muted(`Tip: Run \`${replaceCliName(formatCliCommand("openclaw doctor"), CLI_NAME)}\`, then \`${replaceCliName(formatCliCommand("openclaw gateway restart"), CLI_NAME)}\` to apply updates to a running gateway.`));
		else defaultRuntime.log(theme.muted(`Tip: Run \`${replaceCliName(formatCliCommand("openclaw gateway restart"), CLI_NAME)}\` to apply updates to a running gateway.`));
	}
	return true;
}
//#endregion
export { createUpdateConfigSnapshot as A, isEmptyDir as B, tryInstallShellCompletion as C, resolveOwnedManagedUpdateEnv as D, disableUpdatedPackageCompileCacheEnv as E, restoreDroppedPreUpdateChannels as F, resolveGlobalManager as G, normalizeTag as H, writePostCoreSourceConfigFile as I, resolveUpdateRoot as J, resolveNodeRunner as K, DEFAULT_PACKAGE_NAME as L, normalizePluginInstallRecordMap as M, persistRequestedUpdateChannel as N, resolvePostInstallDoctorEnv as O, readPostCorePreUpdateSourceConfig as P, createGlobalCommandRunner as R, stripGatewayServiceMarkerEnv as S, isPackageManagerUpdateMode as T, parseTimeoutMsOrExit as U, isGitCheckout as V, resolveGitInstallDir as W, tryWriteCompletionCache as X, runUpdateStep as Y, resolveUpdatedGatewayRestartPort as _, gatewayServiceCommandUsesRoot as a, shouldBlockMutableUpdateFromGatewayServiceEnv as b, maybeRestartServiceAfterFailedMutableUpdate as c, resolveGatewayServiceManagementBlockMessageForUpdate as d, resolveManagedServiceNodeRunnerOverride as f, resolvePreparedGatewayUpdatePolicy as g, resolvePostUpdateServiceStateReadEnv as h, createAggregateErrorWithCause as i, maybeRepairLegacyConfigForUpdateChannel as j, prepareRestartScript as k, maybeResumeWindowsTaskAutoStartAfterPackageUpdate as l, resolvePackageRuntimePreflight as m, UpdateCommandAbort as n, isGatewayServiceManagementAllowedForUpdate as o, resolveManagedServicePackageUpdateRoot as p, resolveTargetVersion as q, assertGatewayServiceManagementAllowedForUpdate as r, maybeRestartService as s, GatewayServiceUpdateOwnershipError as t, maybeStopManagedServiceBeforeMutableUpdate as u, restoreWindowsTaskAutoStartOrExit as v, tryResolveInvocationCwd as w, shouldPrepareUpdatedInstallRestart as x, revalidateManagedGatewayServiceAfterUpdate as y, ensureGitCheckout as z };
