import { r as formatErrorMessage } from "./errors-CGMew-1w.js";
import { d as readStringValue, o as normalizeOptionalLowercaseString, s as normalizeOptionalString } from "./string-coerce-CIT1-v_a.js";
import { T as setVerbose } from "./logger-CGXjC0Hv.js";
import { n as defaultRuntime, r as writeRuntimeJson } from "./runtime-CgZZEqw7.js";
import { _ as shortenHomePath, m as resolveUserPath } from "./utils-CE5zxr9Z.js";
import { _ as GATEWAY_CLIENT_NAMES, g as GATEWAY_CLIENT_MODES } from "./message-channel-BIhV7ttg.js";
import { d as setConsoleTimestampPrefix, t as createSubsystemLogger, u as setConsoleSubsystemFilter } from "./subsystem-qDUB67UD.js";
import { t as formatDocsLink } from "./links-D3Ka0iWu.js";
import { n as isRich, r as theme, t as colorize } from "./theme-AvNURkOg.js";
import { n as inheritOptionFromParent } from "./command-options-BprOyVpY.js";
import { _ as resolveStateDir, t as CONFIG_PATH, u as resolveGatewayPort } from "./paths-CAiS8AEq.js";
import "./globals-D7B2Z7Cq.js";
import { g as resolveDefaultAgentWorkspaceDir } from "./workspace-CnnxQiol.js";
import { t as resolveWorkspaceTemplateDir } from "./workspace-templates-BBJakJ5Y.js";
import { a as loadConfig, c as readBestEffortConfig, h as writeConfigFile, l as readConfigFileSnapshot, r as createConfigIO } from "./io-B_JNqEVl.js";
import { a as hasConfiguredSecretInput } from "./types.secrets-CjPCMEo4.js";
import "./config-BR67jvKt.js";
import { t as formatCliCommand } from "./command-format-BeJUeZeT.js";
import { d as resolveGatewayLaunchAgentLabel, h as resolveGatewayWindowsTaskName, p as resolveGatewaySystemdServiceName } from "./paths-syJo8Z0T.js";
import { a as isLoopbackHost, n as isContainerEnvironment, t as defaultGatewayBindMode } from "./net-Dx7qKYeO.js";
import { o as resolveGatewayAuth } from "./auth-_WslrZgG.js";
import { n as inspectBestEffortPrimaryTailnetIPv4 } from "./control-ui-links-CwbPc9S7.js";
import { i as probeGateway } from "./probe-DfmxZ7Mk.js";
import { o as handleReset } from "./onboard-helpers-BQhH-jMj.js";
import { t as cleanStaleGatewayProcessesSync } from "./restart-stale-pids-CVv_-0fS.js";
import { a as inspectPortUsage, s as formatPortDiagnostics } from "./ports-B0tSxIc2.js";
import { r as resolveGatewayService } from "./service-CP2j6WNs.js";
import { p as parsePort } from "./shared-CREzpFYu.js";
import { n as addGatewayServiceCommands } from "./daemon-cli-j-nQfl8f.js";
import { o as writeGatewayRestartHandoffSync } from "./restart-handoff-B7VBseaV.js";
import { i as formatUsd, r as formatTokenCount } from "./usage-format-Bzaz0eTM.js";
import { a as getActiveTaskCount, c as markGatewayDraining, d as waitForActiveTasks, l as resetAllLanes } from "./command-queue-Bwkfmk9S.js";
import { r as callGateway } from "./call-BbnBxegf.js";
import { a as markGatewaySigusr1RestartHandled, i as isGatewaySigusr1RestartExternallyAllowed, l as triggerKaijiBotRestart, o as scheduleGatewaySigusr1Restart, t as consumeGatewaySigusr1RestartAuthorization } from "./restart-Ae-DTY0e.js";
import { d as waitForActiveEmbeddedRuns, r as getActiveEmbeddedRunCount, t as abortEmbeddedPiRun } from "./runs-gYao2sT0.js";
import { n as parseTimeoutMsWithFallback } from "./parse-timeout-OuBfUt0t.js";
import { n as runCommandWithRuntime } from "./cli-utils-CcAr2vZx.js";
import { n as setGatewayWsLogStyle } from "./ws-logging-DJu3n_3p.js";
import { t as formatHelpExamples } from "./help-format-CzGgkTGQ.js";
import { n as withProgress } from "./progress-BrVGmJbK.js";
import { o as resolveControlUiRootSync } from "./control-ui-assets-A53m8hYI.js";
import { t as readSecretFromFile } from "./secret-file-DfYxftim.js";
import { n as resolveWideAreaDiscoveryDomain } from "./widearea-dns-BWrAD9S6.js";
import { i as discoverGatewayBeacons, n as buildGatewayDiscoveryTarget, r as serializeGatewayDiscoveryBeacon } from "./gateway-discovery-targets-B_svEsZh.js";
import { n as resolveGatewayProbeSurfaceAuth } from "./auth-surface-resolution-BZLSPNTs.js";
import { t as pickGatewaySelfPresence } from "./gateway-presence-C7ZKme5I.js";
import { i as styleHealthChannelLine, t as formatHealthChannelLines } from "./health-DTfuAMKw.js";
import { n as acquireGatewayLock, t as GatewayLockError } from "./gateway-lock-BkhPbe1x.js";
import { n as forceFreePortAndWait, r as waitForPortBindable } from "./ports-BrD2WgKB.js";
import fs from "node:fs";
import path from "node:path";
import { spawn } from "node:child_process";
import os from "node:os";
//#region src/commands/gateway-status/discovery.ts
function inferSshTargetFromRemoteUrl(rawUrl) {
	if (typeof rawUrl !== "string") return null;
	const trimmed = normalizeOptionalString(rawUrl) ?? "";
	if (!trimmed) return null;
	let host = null;
	try {
		host = new URL(trimmed).hostname || null;
	} catch {
		return null;
	}
	if (!host) return null;
	const user = normalizeOptionalString(process.env.USER) ?? "";
	return user ? `${user}@${host}` : host;
}
function buildSshTarget(input) {
	const host = normalizeOptionalString(input.host) ?? "";
	if (!host) return null;
	const user = normalizeOptionalString(input.user) ?? "";
	const base = user ? `${user}@${host}` : host;
	const port = input.port ?? 22;
	if (port && port !== 22) return `${base}:${port}`;
	return base;
}
async function resolveSshTarget(params) {
	const [{ resolveSshConfig }, { parseSshTarget }] = await Promise.all([params.loadSshConfigModule(), params.loadSshTunnelModule()]);
	const parsed = parseSshTarget(params.rawTarget);
	if (!parsed) return null;
	const config = await resolveSshConfig(parsed, {
		identity: params.identity ?? void 0,
		timeoutMs: Math.min(800, params.overallTimeoutMs)
	});
	if (!config) return {
		target: params.rawTarget,
		identity: params.identity ?? void 0
	};
	const target = buildSshTarget({
		user: config.user ?? parsed.user,
		host: config.host ?? parsed.host,
		port: config.port ?? parsed.port
	});
	if (!target) return {
		target: params.rawTarget,
		identity: params.identity ?? void 0
	};
	return {
		target,
		identity: params.identity ?? config.identityFiles.find((entry) => normalizeOptionalString(entry)) ?? void 0
	};
}
function pickAutoSshTargetFromDiscovery(params) {
	for (const beacon of params.discovery) {
		const sshTarget = buildGatewayDiscoveryTarget(beacon, { sshUser: params.sshUser ?? void 0 }).sshTarget;
		if (!sshTarget) continue;
		if (params.parseSshTarget(sshTarget)) return sshTarget;
	}
	return null;
}
//#endregion
//#region src/commands/gateway-status/helpers.ts
const MISSING_SCOPE_PATTERN = /\bmissing scope:\s*[a-z0-9._-]+/i;
function parseIntOrNull(value) {
	const s = typeof value === "string" ? value.trim() : typeof value === "number" || typeof value === "bigint" ? String(value) : "";
	if (!s) return null;
	const n = Number.parseInt(s, 10);
	return Number.isFinite(n) ? n : null;
}
function parseTimeoutMs(raw, fallbackMs) {
	return parseTimeoutMsWithFallback(raw, fallbackMs);
}
function normalizeWsUrl(value) {
	const trimmed = value.trim();
	if (!trimmed) return null;
	if (!trimmed.startsWith("ws://") && !trimmed.startsWith("wss://")) return null;
	return trimmed;
}
function resolveTargets(cfg, explicitUrl) {
	const targets = [];
	const add = (t) => {
		if (!targets.some((x) => x.url === t.url)) targets.push(t);
	};
	const explicit = typeof explicitUrl === "string" ? normalizeWsUrl(explicitUrl) : null;
	if (explicit) add({
		id: "explicit",
		kind: "explicit",
		url: explicit,
		active: true
	});
	const remoteUrl = typeof cfg.gateway?.remote?.url === "string" ? normalizeWsUrl(cfg.gateway.remote.url) : null;
	if (remoteUrl) add({
		id: "configRemote",
		kind: "configRemote",
		url: remoteUrl,
		active: cfg.gateway?.mode === "remote"
	});
	const port = resolveGatewayPort(cfg);
	add({
		id: "localLoopback",
		kind: "localLoopback",
		url: `${cfg.gateway?.tls?.enabled === true ? "wss" : "ws"}://127.0.0.1:${port}`,
		active: cfg.gateway?.mode !== "remote"
	});
	return targets;
}
function isLoopbackProbeTarget(target) {
	if (target.kind === "localLoopback") return true;
	try {
		return isLoopbackHost(new URL(target.url).hostname);
	} catch {
		return false;
	}
}
function resolveProbeBudgetMs(overallMs, target) {
	if (target.kind === "sshTunnel") return Math.min(2e3, overallMs);
	if (!isLoopbackProbeTarget(target)) return Math.min(1500, overallMs);
	if (target.kind === "localLoopback" && !target.active) return Math.min(800, overallMs);
	return overallMs;
}
function sanitizeSshTarget(value) {
	if (typeof value !== "string") return null;
	const trimmed = value.trim();
	if (!trimmed) return null;
	return trimmed.replace(/^ssh\\s+/, "");
}
async function resolveAuthForTarget(cfg, target, overrides) {
	const tokenOverride = normalizeOptionalString(overrides.token);
	const passwordOverride = normalizeOptionalString(overrides.password);
	if (tokenOverride || passwordOverride) return {
		token: tokenOverride,
		password: passwordOverride
	};
	return resolveGatewayProbeSurfaceAuth({
		config: cfg,
		surface: target.kind === "configRemote" || target.kind === "sshTunnel" ? "remote" : "local"
	});
}
function extractConfigSummary(snapshotUnknown) {
	const snap = snapshotUnknown;
	const path = typeof snap?.path === "string" ? snap.path : null;
	const exists = Boolean(snap?.exists);
	const valid = Boolean(snap?.valid);
	const issuesRaw = Array.isArray(snap?.issues) ? snap.issues : [];
	const legacyRaw = Array.isArray(snap?.legacyIssues) ? snap.legacyIssues : [];
	const cfg = snap?.config ?? {};
	const gateway = cfg.gateway ?? {};
	const secretDefaults = (cfg.secrets ?? {}).defaults ?? void 0;
	const wideArea = (cfg.discovery ?? {}).wideArea ?? {};
	const remote = gateway.remote ?? {};
	const auth = gateway.auth ?? {};
	const controlUi = gateway.controlUi ?? {};
	const tailscale = gateway.tailscale ?? {};
	const authMode = typeof auth.mode === "string" ? auth.mode : null;
	const authTokenConfigured = hasConfiguredSecretInput(auth.token, secretDefaults);
	const authPasswordConfigured = hasConfiguredSecretInput(auth.password, secretDefaults);
	const remoteUrl = typeof remote.url === "string" ? normalizeWsUrl(remote.url) : null;
	const remoteTokenConfigured = hasConfiguredSecretInput(remote.token, secretDefaults);
	const remotePasswordConfigured = hasConfiguredSecretInput(remote.password, secretDefaults);
	const wideAreaEnabled = typeof wideArea.enabled === "boolean" ? wideArea.enabled : null;
	return {
		path,
		exists,
		valid,
		issues: issuesRaw.filter((i) => Boolean(i && typeof i.path === "string" && typeof i.message === "string")).map((i) => ({
			path: i.path,
			message: i.message
		})),
		legacyIssues: legacyRaw.filter((i) => Boolean(i && typeof i.path === "string" && typeof i.message === "string")).map((i) => ({
			path: i.path,
			message: i.message
		})),
		gateway: {
			mode: typeof gateway.mode === "string" ? gateway.mode : null,
			bind: typeof gateway.bind === "string" ? gateway.bind : null,
			port: parseIntOrNull(gateway.port),
			controlUiEnabled: typeof controlUi.enabled === "boolean" ? controlUi.enabled : null,
			controlUiBasePath: typeof controlUi.basePath === "string" ? controlUi.basePath : null,
			authMode,
			authTokenConfigured,
			authPasswordConfigured,
			remoteUrl,
			remoteTokenConfigured,
			remotePasswordConfigured,
			tailscaleMode: typeof tailscale.mode === "string" ? tailscale.mode : null
		},
		discovery: { wideAreaEnabled }
	};
}
function buildNetworkHints(cfg) {
	const { tailnetIPv4 } = inspectBestEffortPrimaryTailnetIPv4();
	const port = resolveGatewayPort(cfg);
	const localScheme = cfg.gateway?.tls?.enabled === true ? "wss" : "ws";
	return {
		localLoopbackUrl: `${localScheme}://127.0.0.1:${port}`,
		localTailnetUrl: tailnetIPv4 ? `${localScheme}://${tailnetIPv4}:${port}` : null,
		tailnetIPv4: tailnetIPv4 ?? null
	};
}
function renderTargetHeader(target, rich) {
	const kindLabel = target.kind === "localLoopback" ? "Local loopback" : target.kind === "sshTunnel" ? "Remote over SSH" : target.kind === "configRemote" ? target.active ? "Remote (configured)" : "Remote (configured, inactive)" : "URL (explicit)";
	return `${colorize(rich, theme.heading, kindLabel)} ${colorize(rich, theme.muted, target.url)}`;
}
function isScopeLimitedProbeFailure(probe) {
	if (probe.ok || probe.connectLatencyMs == null) return false;
	return MISSING_SCOPE_PATTERN.test(probe.error ?? "");
}
function isProbeReachable(probe) {
	return probe.ok || isScopeLimitedProbeFailure(probe);
}
function renderProbeSummaryLine(probe, rich) {
	if (probe.ok) {
		const latency = typeof probe.connectLatencyMs === "number" ? `${probe.connectLatencyMs}ms` : "unknown";
		return `${colorize(rich, theme.success, "Connect: ok")} (${latency}) · ${colorize(rich, theme.success, "RPC: ok")}`;
	}
	const detail = probe.error ? ` - ${probe.error}` : "";
	if (probe.connectLatencyMs != null) {
		const latency = typeof probe.connectLatencyMs === "number" ? `${probe.connectLatencyMs}ms` : "unknown";
		const rpcStatus = isScopeLimitedProbeFailure(probe) ? colorize(rich, theme.warn, "RPC: limited") : colorize(rich, theme.error, "RPC: failed");
		return `${colorize(rich, theme.success, "Connect: ok")} (${latency}) · ${rpcStatus}${detail}`;
	}
	return `${colorize(rich, theme.error, "Connect: failed")}${detail}`;
}
//#endregion
//#region src/commands/gateway-status/output.ts
function pickPrimaryProbedTarget(probed) {
	const reachable = probed.filter((entry) => isProbeReachable(entry.probe));
	return reachable.find((entry) => entry.target.kind === "explicit") ?? reachable.find((entry) => entry.target.kind === "sshTunnel") ?? reachable.find((entry) => entry.target.kind === "configRemote") ?? reachable.find((entry) => entry.target.kind === "localLoopback") ?? null;
}
function buildGatewayStatusWarnings(params) {
	const reachable = params.probed.filter((entry) => isProbeReachable(entry.probe));
	const degradedScopeLimited = params.probed.filter((entry) => isScopeLimitedProbeFailure(entry.probe));
	const warnings = [];
	if (params.sshTarget && !params.sshTunnelStarted) warnings.push({
		code: "ssh_tunnel_failed",
		message: params.sshTunnelError ? `SSH tunnel failed: ${String(params.sshTunnelError)}` : "SSH tunnel failed to start; falling back to direct probes."
	});
	if (params.localTlsLoadError) warnings.push({
		code: "local_tls_runtime_unavailable",
		message: `Local gateway TLS is enabled but KaijiBot could not load the local certificate fingerprint: ${params.localTlsLoadError}`,
		targetIds: ["localLoopback"]
	});
	if (reachable.length > 1) warnings.push({
		code: "multiple_gateways",
		message: "Unconventional setup: multiple reachable gateways detected. Usually one gateway per network is recommended unless you intentionally run isolated profiles, like a rescue bot (see docs: /gateway#multiple-gateways-same-host).",
		targetIds: reachable.map((entry) => entry.target.id)
	});
	for (const result of params.probed) {
		if (result.authDiagnostics.length === 0 || isProbeReachable(result.probe)) continue;
		for (const diagnostic of result.authDiagnostics) warnings.push({
			code: "auth_secretref_unresolved",
			message: diagnostic,
			targetIds: [result.target.id]
		});
	}
	for (const result of degradedScopeLimited) warnings.push({
		code: "probe_scope_limited",
		message: "Probe diagnostics are limited by gateway scopes (missing operator.read). Connection succeeded, but status details may be incomplete. Hint: pair device identity or use credentials with operator.read.",
		targetIds: [result.target.id]
	});
	return warnings;
}
function writeGatewayStatusJson(params) {
	const reachable = params.probed.filter((entry) => isProbeReachable(entry.probe));
	const degraded = params.probed.some((entry) => isScopeLimitedProbeFailure(entry.probe));
	writeRuntimeJson(params.runtime, {
		ok: reachable.length > 0,
		degraded,
		ts: Date.now(),
		durationMs: Date.now() - params.startedAt,
		timeoutMs: params.overallTimeoutMs,
		primaryTargetId: params.primaryTargetId,
		warnings: params.warnings,
		network: params.network,
		discovery: {
			timeoutMs: params.discoveryTimeoutMs,
			count: params.discovery.length,
			beacons: params.discovery.map((beacon) => serializeGatewayDiscoveryBeacon(beacon))
		},
		targets: params.probed.map((entry) => ({
			id: entry.target.id,
			kind: entry.target.kind,
			url: entry.target.url,
			active: entry.target.active,
			tunnel: entry.target.tunnel ?? null,
			connect: {
				ok: isProbeReachable(entry.probe),
				rpcOk: entry.probe.ok,
				scopeLimited: isScopeLimitedProbeFailure(entry.probe),
				latencyMs: entry.probe.connectLatencyMs,
				error: entry.probe.error,
				close: entry.probe.close
			},
			self: entry.self,
			config: entry.configSummary,
			health: entry.probe.health,
			summary: entry.probe.status,
			presence: entry.probe.presence
		}))
	});
	if (reachable.length === 0) params.runtime.exit(1);
}
function writeGatewayStatusText(params) {
	const ok = params.probed.filter((entry) => isProbeReachable(entry.probe)).length > 0;
	params.runtime.log(colorize(params.rich, theme.heading, "Gateway Status"));
	params.runtime.log(ok ? `${colorize(params.rich, theme.success, "Reachable")}: yes` : `${colorize(params.rich, theme.error, "Reachable")}: no`);
	params.runtime.log(colorize(params.rich, theme.muted, `Probe budget: ${params.overallTimeoutMs}ms`));
	if (params.warnings.length > 0) {
		params.runtime.log("");
		params.runtime.log(colorize(params.rich, theme.warn, "Warning:"));
		for (const warning of params.warnings) params.runtime.log(`- ${warning.message}`);
	}
	params.runtime.log("");
	params.runtime.log(colorize(params.rich, theme.heading, "Discovery (this machine)"));
	const discoveryDomains = params.wideAreaDomain ? `local. + ${params.wideAreaDomain}` : "local.";
	params.runtime.log(params.discovery.length > 0 ? `Found ${params.discovery.length} gateway(s) via Bonjour (${discoveryDomains})` : `Found 0 gateways via Bonjour (${discoveryDomains})`);
	if (params.discovery.length === 0) params.runtime.log(colorize(params.rich, theme.muted, "Tip: if the gateway is remote, mDNS won’t cross networks; use Wide-Area Bonjour (split DNS) or SSH tunnels."));
	params.runtime.log("");
	params.runtime.log(colorize(params.rich, theme.heading, "Targets"));
	for (const result of params.probed) {
		params.runtime.log(renderTargetHeader(result.target, params.rich));
		params.runtime.log(`  ${renderProbeSummaryLine(result.probe, params.rich)}`);
		if (result.target.tunnel?.kind === "ssh") params.runtime.log(`  ${colorize(params.rich, theme.muted, "ssh")}: ${colorize(params.rich, theme.command, result.target.tunnel.target)}`);
		if (result.probe.ok && result.self) {
			const host = result.self.host ?? "unknown";
			const ip = result.self.ip ? ` (${result.self.ip})` : "";
			const platform = result.self.platform ? ` · ${result.self.platform}` : "";
			const version = result.self.version ? ` · app ${result.self.version}` : "";
			params.runtime.log(`  ${colorize(params.rich, theme.info, "Gateway")}: ${host}${ip}${platform}${version}`);
		}
		if (result.configSummary) {
			const wideArea = result.configSummary.discovery.wideAreaEnabled === true ? "enabled" : result.configSummary.discovery.wideAreaEnabled === false ? "disabled" : "unknown";
			params.runtime.log(`  ${colorize(params.rich, theme.info, "Wide-area discovery")}: ${wideArea}`);
		}
		params.runtime.log("");
	}
	if (!ok) params.runtime.exit(1);
}
//#endregion
//#region src/commands/gateway-status/probe-run.ts
async function runGatewayStatusProbePass(params) {
	const discoveryPromise = discoverGatewayBeacons({
		timeoutMs: params.discoveryTimeoutMs,
		wideAreaDomain: params.wideAreaDomain
	});
	let sshTarget = params.sshTarget;
	let sshTunnelError = null;
	let sshTunnelStarted = false;
	const tryStartTunnel = async () => {
		if (!sshTarget) return null;
		try {
			const { startSshPortForward } = await params.loadSshTunnelModule();
			const tunnel = await startSshPortForward({
				target: sshTarget,
				identity: params.sshIdentity ?? void 0,
				localPortPreferred: params.remotePort,
				remotePort: params.remotePort,
				timeoutMs: Math.min(1500, params.overallTimeoutMs)
			});
			sshTunnelStarted = true;
			return tunnel;
		} catch (err) {
			sshTunnelError = formatErrorMessage(err);
			return null;
		}
	};
	const discoveryTask = discoveryPromise.catch(() => []);
	const tunnelTask = sshTarget ? tryStartTunnel() : Promise.resolve(null);
	const [discovery, tunnelFirst] = await Promise.all([discoveryTask, tunnelTask]);
	if (!sshTarget && params.opts.sshAuto) {
		const { parseSshTarget } = await params.loadSshTunnelModule();
		sshTarget = pickAutoSshTargetFromDiscovery({
			discovery,
			parseSshTarget,
			sshUser: normalizeOptionalString(process.env.USER) ?? ""
		});
	}
	const tunnel = tunnelFirst || (sshTarget && !sshTunnelStarted && !sshTunnelError ? await tryStartTunnel() : null);
	const tunnelTarget = tunnel ? {
		id: "sshTunnel",
		kind: "sshTunnel",
		url: `ws://127.0.0.1:${tunnel.localPort}`,
		active: true,
		tunnel: {
			kind: "ssh",
			target: sshTarget ?? "",
			localPort: tunnel.localPort,
			remotePort: params.remotePort,
			pid: tunnel.pid
		}
	} : null;
	const targets = tunnelTarget ? [tunnelTarget, ...params.baseTargets.filter((target) => target.url !== tunnelTarget.url)] : params.baseTargets;
	try {
		return {
			discovery,
			probed: await Promise.all(targets.map(async (target) => {
				const authResolution = await resolveAuthForTarget(params.cfg, target, {
					token: readStringValue(params.opts.token),
					password: readStringValue(params.opts.password)
				});
				const probe = await probeGateway({
					url: target.url,
					auth: {
						token: authResolution.token,
						password: authResolution.password
					},
					tlsFingerprint: target.kind === "localLoopback" && target.url.startsWith("wss://") ? params.localTlsFingerprint : void 0,
					timeoutMs: resolveProbeBudgetMs(params.overallTimeoutMs, target)
				});
				return {
					target,
					probe,
					configSummary: probe.configSnapshot ? extractConfigSummary(probe.configSnapshot) : null,
					self: pickGatewaySelfPresence(probe.presence),
					authDiagnostics: authResolution.diagnostics ?? []
				};
			})),
			sshTarget,
			sshTunnelStarted,
			sshTunnelError
		};
	} finally {
		if (tunnel) try {
			await tunnel.stop();
		} catch {}
	}
}
//#endregion
//#region src/commands/gateway-status.ts
let sshConfigModulePromise;
let sshTunnelModulePromise;
let gatewayTlsModulePromise;
function loadSshConfigModule() {
	sshConfigModulePromise ??= import("./ssh-config-BA1Y7a8V.js");
	return sshConfigModulePromise;
}
function loadSshTunnelModule() {
	sshTunnelModulePromise ??= import("./ssh-tunnel-CFeynLmj.js");
	return sshTunnelModulePromise;
}
function loadGatewayTlsModule() {
	gatewayTlsModulePromise ??= import("./gateway-BRmsKphk.js");
	return gatewayTlsModulePromise;
}
async function gatewayStatusCommand(opts, runtime) {
	const startedAt = Date.now();
	const cfg = await readBestEffortConfig();
	const rich = isRich() && opts.json !== true;
	const overallTimeoutMs = parseTimeoutMs(opts.timeout, 3e3);
	const wideAreaDomain = resolveWideAreaDiscoveryDomain({ configDomain: cfg.discovery?.wideArea?.domain });
	const baseTargets = resolveTargets(cfg, opts.url);
	const network = buildNetworkHints(cfg);
	const remotePort = resolveGatewayPort(cfg);
	const discoveryTimeoutMs = Math.min(1200, overallTimeoutMs);
	let sshTarget = sanitizeSshTarget(opts.ssh) ?? sanitizeSshTarget(cfg.gateway?.remote?.sshTarget);
	let sshIdentity = sanitizeSshTarget(opts.sshIdentity) ?? sanitizeSshTarget(cfg.gateway?.remote?.sshIdentity);
	if (!sshTarget) sshTarget = inferSshTargetFromRemoteUrl(cfg.gateway?.remote?.url);
	if (sshTarget) {
		const resolved = await resolveSshTarget({
			rawTarget: sshTarget,
			identity: sshIdentity,
			overallTimeoutMs,
			loadSshConfigModule,
			loadSshTunnelModule
		});
		if (resolved) {
			sshTarget = resolved.target;
			if (!sshIdentity && resolved.identity) sshIdentity = resolved.identity;
		}
	}
	const localTlsRuntime = cfg.gateway?.tls?.enabled === true ? await loadGatewayTlsModule().then(({ loadGatewayTlsRuntime }) => loadGatewayTlsRuntime(cfg.gateway?.tls)) : void 0;
	const probePass = await withProgress({
		label: "Inspecting gateways…",
		indeterminate: true,
		enabled: opts.json !== true
	}, async () => await runGatewayStatusProbePass({
		cfg,
		opts,
		overallTimeoutMs,
		discoveryTimeoutMs,
		wideAreaDomain,
		baseTargets,
		remotePort,
		sshTarget,
		sshIdentity,
		loadSshTunnelModule,
		localTlsFingerprint: localTlsRuntime?.enabled ? localTlsRuntime.fingerprintSha256 : void 0
	}));
	const warnings = buildGatewayStatusWarnings({
		probed: probePass.probed,
		sshTarget: probePass.sshTarget,
		sshTunnelStarted: probePass.sshTunnelStarted,
		sshTunnelError: probePass.sshTunnelError,
		localTlsLoadError: localTlsRuntime && !localTlsRuntime.enabled && localTlsRuntime.required ? localTlsRuntime.error ?? "gateway tls is enabled but local TLS runtime could not load" : null
	});
	const primary = pickPrimaryProbedTarget(probePass.probed);
	if (opts.json) {
		writeGatewayStatusJson({
			runtime,
			startedAt,
			overallTimeoutMs,
			discoveryTimeoutMs,
			network,
			discovery: probePass.discovery,
			probed: probePass.probed,
			warnings,
			primaryTargetId: primary?.target.id ?? null
		});
		return;
	}
	writeGatewayStatusText({
		runtime,
		rich,
		overallTimeoutMs,
		wideAreaDomain,
		discovery: probePass.discovery,
		probed: probePass.probed,
		warnings
	});
}
//#endregion
//#region src/cli/gateway-cli/call.ts
const gatewayCallOpts = (cmd) => cmd.option("--url <url>", "Gateway WebSocket URL (defaults to gateway.remote.url when configured)").option("--token <token>", "Gateway token (if required)").option("--password <password>", "Gateway password (password auth)").option("--timeout <ms>", "Timeout in ms", "10000").option("--expect-final", "Wait for final response (agent)", false).option("--json", "Output JSON", false);
const callGatewayCli = async (method, opts, params) => withProgress({
	label: `Gateway ${method}`,
	indeterminate: true,
	enabled: opts.json !== true
}, async () => await callGateway({
	config: opts.config,
	url: opts.url,
	token: opts.token,
	password: opts.password,
	method,
	params,
	expectFinal: Boolean(opts.expectFinal),
	timeoutMs: Number(opts.timeout ?? 1e4),
	clientName: GATEWAY_CLIENT_NAMES.CLI,
	mode: GATEWAY_CLIENT_MODES.CLI
}));
//#endregion
//#region src/cli/gateway-cli/discover.ts
function parseDiscoverTimeoutMs(raw, fallbackMs) {
	return parseTimeoutMsWithFallback(raw, fallbackMs, { invalidType: "error" });
}
function pickBeaconHost(beacon) {
	return buildGatewayDiscoveryTarget(beacon).endpoint?.host ?? null;
}
function pickGatewayPort(beacon) {
	return buildGatewayDiscoveryTarget(beacon).endpoint?.port ?? null;
}
function dedupeBeacons(beacons) {
	const out = [];
	const seen = /* @__PURE__ */ new Set();
	for (const b of beacons) {
		const host = pickBeaconHost(b) ?? "";
		const key = [
			b.domain ?? "",
			b.instanceName ?? "",
			b.displayName ?? "",
			host,
			String(b.port ?? ""),
			String(b.gatewayPort ?? "")
		].join("|");
		if (seen.has(key)) continue;
		seen.add(key);
		out.push(b);
	}
	return out;
}
function renderBeaconLines(beacon, rich) {
	const target = buildGatewayDiscoveryTarget(beacon);
	const lines = [`- ${colorize(rich, theme.accentBright, target.title)} ${colorize(rich, theme.muted, target.domain)}`];
	if (beacon.tailnetDns) lines.push(`  ${colorize(rich, theme.info, "tailnet")}: ${beacon.tailnetDns}`);
	if (beacon.lanHost) lines.push(`  ${colorize(rich, theme.info, "lan")}: ${beacon.lanHost}`);
	if (beacon.host) lines.push(`  ${colorize(rich, theme.info, "host")}: ${beacon.host}`);
	if (target.wsUrl) lines.push(`  ${colorize(rich, theme.muted, "ws")}: ${colorize(rich, theme.command, target.wsUrl)}`);
	if (beacon.role) lines.push(`  ${colorize(rich, theme.muted, "role")}: ${beacon.role}`);
	if (beacon.transport) lines.push(`  ${colorize(rich, theme.muted, "transport")}: ${beacon.transport}`);
	if (beacon.gatewayTls) {
		const fingerprint = beacon.gatewayTlsFingerprintSha256 ? `sha256 ${beacon.gatewayTlsFingerprintSha256}` : "enabled";
		lines.push(`  ${colorize(rich, theme.muted, "tls")}: ${fingerprint}`);
	}
	if (target.endpoint && target.sshPort) {
		const ssh = `ssh -N -L 18789:127.0.0.1:18789 <user>@${target.endpoint.host} -p ${target.sshPort}`;
		lines.push(`  ${colorize(rich, theme.muted, "ssh")}: ${colorize(rich, theme.command, ssh)}`);
	}
	return lines;
}
//#endregion
//#region src/infra/supervisor-markers.ts
const SUPERVISOR_HINTS = {
	launchd: [
		"LAUNCH_JOB_LABEL",
		"LAUNCH_JOB_NAME",
		"XPC_SERVICE_NAME",
		"KAIJIBOT_LAUNCHD_LABEL"
	],
	systemd: [
		"KAIJIBOT_SYSTEMD_UNIT",
		"INVOCATION_ID",
		"SYSTEMD_EXEC_PID",
		"JOURNAL_STREAM"
	],
	schtasks: ["KAIJIBOT_WINDOWS_TASK_NAME"]
};
[
	...SUPERVISOR_HINTS.launchd,
	...SUPERVISOR_HINTS.systemd,
	...SUPERVISOR_HINTS.schtasks
];
function hasAnyHint(env, keys) {
	return keys.some((key) => {
		const value = env[key];
		return typeof value === "string" && value.trim().length > 0;
	});
}
function detectRespawnSupervisor(env = process.env, platform = process.platform) {
	if (platform === "darwin") return hasAnyHint(env, SUPERVISOR_HINTS.launchd) ? "launchd" : null;
	if (platform === "linux") return hasAnyHint(env, SUPERVISOR_HINTS.systemd) ? "systemd" : null;
	if (platform === "win32") {
		if (hasAnyHint(env, SUPERVISOR_HINTS.schtasks)) return "schtasks";
		const marker = env.KAIJIBOT_SERVICE_MARKER?.trim();
		const serviceKind = env.KAIJIBOT_SERVICE_KIND?.trim();
		return marker && serviceKind === "gateway" ? "schtasks" : null;
	}
	return null;
}
//#endregion
//#region src/cli/gateway-cli/dev.ts
const DEV_IDENTITY_NAME = "C3-PO";
const DEV_IDENTITY_THEME = "protocol droid";
const DEV_IDENTITY_EMOJI = "🤖";
const DEV_AGENT_WORKSPACE_SUFFIX = "dev";
async function loadDevTemplate(name, fallback) {
	try {
		const templateDir = await resolveWorkspaceTemplateDir();
		const raw = await fs.promises.readFile(path.join(templateDir, name), "utf-8");
		if (!raw.startsWith("---")) return raw;
		const endIndex = raw.indexOf("\n---", 3);
		if (endIndex === -1) return raw;
		return raw.slice(endIndex + 4).replace(/^\s+/, "");
	} catch {
		return fallback;
	}
}
const resolveDevWorkspaceDir = (env = process.env) => {
	const baseDir = resolveDefaultAgentWorkspaceDir(env, os.homedir);
	if (normalizeOptionalLowercaseString(env.KAIJIBOT_PROFILE) === "dev") return baseDir;
	return `${baseDir}-${DEV_AGENT_WORKSPACE_SUFFIX}`;
};
async function writeFileIfMissing(filePath, content) {
	try {
		await fs.promises.writeFile(filePath, content, {
			encoding: "utf-8",
			flag: "wx"
		});
	} catch (err) {
		if (err.code !== "EEXIST") throw err;
	}
}
async function ensureDevWorkspace(dir) {
	const resolvedDir = resolveUserPath(dir);
	await fs.promises.mkdir(resolvedDir, { recursive: true });
	const [agents, soul, tools, identity, user] = await Promise.all([
		loadDevTemplate("AGENTS.dev.md", `# AGENTS.md - KaijiBot Dev Workspace\n\nDefault dev workspace for kaijibot gateway --dev.\n`),
		loadDevTemplate("SOUL.dev.md", `# SOUL.md - Dev Persona\n\nProtocol droid for debugging and operations.\n`),
		loadDevTemplate("TOOLS.dev.md", `# TOOLS.md - User Tool Notes (editable)\n\nAdd your local tool notes here.\n`),
		loadDevTemplate("IDENTITY.dev.md", `# IDENTITY.md - Agent Identity\n\n- Name: ${DEV_IDENTITY_NAME}\n- Creature: protocol droid\n- Vibe: ${DEV_IDENTITY_THEME}\n- Emoji: ${DEV_IDENTITY_EMOJI}\n`),
		loadDevTemplate("USER.dev.md", `# USER.md - User Profile\n\n- Name:\n- Preferred address:\n- Notes:\n`)
	]);
	await writeFileIfMissing(path.join(resolvedDir, "AGENTS.md"), agents);
	await writeFileIfMissing(path.join(resolvedDir, "SOUL.md"), soul);
	await writeFileIfMissing(path.join(resolvedDir, "TOOLS.md"), tools);
	await writeFileIfMissing(path.join(resolvedDir, "IDENTITY.md"), identity);
	await writeFileIfMissing(path.join(resolvedDir, "USER.md"), user);
}
async function ensureDevGatewayConfig(opts) {
	const workspace = resolveDevWorkspaceDir();
	if (opts.reset) await handleReset("full", workspace, defaultRuntime);
	const configPath = createConfigIO().configPath;
	const configExists = fs.existsSync(configPath);
	if (!opts.reset && configExists) return;
	await writeConfigFile({
		gateway: {
			mode: "local",
			bind: "loopback"
		},
		agents: {
			defaults: {
				workspace,
				skipBootstrap: true
			},
			list: [{
				id: "dev",
				default: true,
				workspace,
				identity: {
					name: DEV_IDENTITY_NAME,
					theme: DEV_IDENTITY_THEME,
					emoji: DEV_IDENTITY_EMOJI
				}
			}]
		}
	});
	await ensureDevWorkspace(workspace);
	defaultRuntime.log(`Dev config ready: ${shortenHomePath(configPath)}`);
	defaultRuntime.log(`Dev workspace ready: ${shortenHomePath(resolveUserPath(workspace))}`);
}
//#endregion
//#region src/infra/process-respawn.ts
function isTruthy(value) {
	const normalized = normalizeOptionalLowercaseString(value);
	return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
}
/**
* Attempt to restart this process with a fresh PID.
* - supervised environments (launchd/systemd/schtasks): caller should exit and let supervisor restart
* - KAIJIBOT_NO_RESPAWN=1: caller should keep in-process restart behavior (tests/dev)
* - otherwise: spawn detached child with current argv/execArgv, then caller exits
*/
function restartGatewayProcessWithFreshPid() {
	if (isTruthy(process.env.KAIJIBOT_NO_RESPAWN)) return { mode: "disabled" };
	const supervisor = detectRespawnSupervisor(process.env);
	if (supervisor) {
		if (supervisor === "schtasks") {
			const restart = triggerKaijiBotRestart();
			if (!restart.ok) return {
				mode: "failed",
				detail: restart.detail ?? `${restart.method} restart failed`
			};
		}
		return { mode: "supervised" };
	}
	if (process.platform === "win32") return {
		mode: "disabled",
		detail: "win32: detached respawn unsupported without Scheduled Task markers"
	};
	try {
		const args = [...process.execArgv, ...process.argv.slice(1)];
		const child = spawn(process.execPath, args, {
			env: process.env,
			detached: true,
			stdio: "inherit"
		});
		child.unref();
		return {
			mode: "spawned",
			pid: child.pid ?? void 0
		};
	} catch (err) {
		return {
			mode: "failed",
			detail: formatErrorMessage(err)
		};
	}
}
//#endregion
//#region src/process/restart-recovery.ts
/**
* Returns an iteration hook for in-process restart loops.
* The first call is considered initial startup and does nothing.
* Each subsequent call represents a restart iteration and invokes `onRestart`.
*/
function createRestartIterationHook(onRestart) {
	let isFirstIteration = true;
	return () => {
		if (isFirstIteration) {
			isFirstIteration = false;
			return false;
		}
		onRestart();
		return true;
	};
}
//#endregion
//#region src/cli/gateway-cli/run-loop.ts
const gatewayLog$1 = createSubsystemLogger("gateway");
const LAUNCHD_SUPERVISED_RESTART_EXIT_DELAY_MS = 1500;
const DEFAULT_RESTART_DRAIN_TIMEOUT_MS = 3e5;
async function runGatewayLoop(params) {
	let startupStartedAt = Date.now();
	let lock = await acquireGatewayLock({ port: params.lockPort });
	let server = null;
	let shuttingDown = false;
	let restartResolver = null;
	const cleanupSignals = () => {
		process.removeListener("SIGTERM", onSigterm);
		process.removeListener("SIGINT", onSigint);
		process.removeListener("SIGUSR1", onSigusr1);
	};
	const exitProcess = (code) => {
		cleanupSignals();
		params.runtime.exit(code);
	};
	const releaseLockIfHeld = async () => {
		if (!lock) return false;
		await lock.release();
		lock = null;
		return true;
	};
	const reacquireLockForInProcessRestart = async () => {
		try {
			startupStartedAt = Date.now();
			lock = await acquireGatewayLock({ port: params.lockPort });
			return true;
		} catch (err) {
			gatewayLog$1.error(`failed to reacquire gateway lock for in-process restart: ${String(err)}`);
			exitProcess(1);
			return false;
		}
	};
	const handleRestartAfterServerClose = async () => {
		const hadLock = await releaseLockIfHeld();
		const respawn = restartGatewayProcessWithFreshPid();
		if (respawn.mode === "spawned" || respawn.mode === "supervised") {
			const modeLabel = respawn.mode === "spawned" ? `spawned pid ${respawn.pid ?? "unknown"}` : "supervisor restart";
			gatewayLog$1.info(`restart mode: full process restart (${modeLabel})`);
			if (respawn.mode === "supervised" && detectRespawnSupervisor(process.env, process.platform) === "launchd") await new Promise((resolve) => {
				setTimeout(resolve, LAUNCHD_SUPERVISED_RESTART_EXIT_DELAY_MS);
			});
			if (respawn.mode === "supervised") writeGatewayRestartHandoffSync({
				restartKind: "full-process",
				supervisorMode: detectRespawnSupervisor(process.env, process.platform) ?? "external"
			});
			exitProcess(0);
			return;
		}
		if (respawn.mode === "failed") gatewayLog$1.warn(`full process restart failed (${respawn.detail ?? "unknown error"}); falling back to in-process restart`);
		else gatewayLog$1.info(`restart mode: in-process restart (${respawn.detail ?? "KAIJIBOT_NO_RESPAWN"})`);
		if (hadLock && !await reacquireLockForInProcessRestart()) return;
		shuttingDown = false;
		restartResolver?.();
	};
	const handleStopAfterServerClose = async () => {
		await releaseLockIfHeld();
		exitProcess(0);
	};
	const SHUTDOWN_TIMEOUT_MS = 25e3;
	const resolveRestartDrainTimeoutMs = () => {
		try {
			const timeoutMs = loadConfig().gateway?.reload?.deferralTimeoutMs;
			return typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs >= 0 ? timeoutMs : DEFAULT_RESTART_DRAIN_TIMEOUT_MS;
		} catch {
			return DEFAULT_RESTART_DRAIN_TIMEOUT_MS;
		}
	};
	const request = (action, signal) => {
		if (shuttingDown) {
			gatewayLog$1.info(`received ${signal} during shutdown; ignoring`);
			return;
		}
		shuttingDown = true;
		const isRestart = action === "restart";
		const restartDrainTimeoutMs = isRestart ? resolveRestartDrainTimeoutMs() : 0;
		gatewayLog$1.info(`received ${signal}; ${isRestart ? "restarting" : "shutting down"}`);
		const forceExitMs = isRestart ? restartDrainTimeoutMs + SHUTDOWN_TIMEOUT_MS : SHUTDOWN_TIMEOUT_MS;
		const forceExitTimer = setTimeout(() => {
			gatewayLog$1.error("shutdown timed out; exiting without full cleanup");
			exitProcess(1);
		}, forceExitMs);
		(async () => {
			try {
				if (isRestart) {
					markGatewayDraining();
					const activeTasks = getActiveTaskCount();
					const activeRuns = getActiveEmbeddedRunCount();
					if (activeRuns > 0) abortEmbeddedPiRun(void 0, { mode: "compacting" });
					if (activeTasks > 0 || activeRuns > 0) {
						gatewayLog$1.info(`draining ${activeTasks} active task(s) and ${activeRuns} active embedded run(s) before restart (timeout ${restartDrainTimeoutMs}ms)`);
						const [tasksDrain, runsDrain] = await Promise.all([activeTasks > 0 ? waitForActiveTasks(restartDrainTimeoutMs) : Promise.resolve({ drained: true }), activeRuns > 0 ? waitForActiveEmbeddedRuns(restartDrainTimeoutMs) : Promise.resolve({ drained: true })]);
						if (tasksDrain.drained && runsDrain.drained) gatewayLog$1.info("all active work drained");
						else {
							gatewayLog$1.warn("drain timeout reached; proceeding with restart");
							abortEmbeddedPiRun(void 0, { mode: "all" });
						}
					}
				}
				await server?.close({
					reason: isRestart ? "gateway restarting" : "gateway stopping",
					restartExpectedMs: isRestart ? 1500 : null
				});
			} catch (err) {
				gatewayLog$1.error(`shutdown error: ${String(err)}`);
			} finally {
				clearTimeout(forceExitTimer);
				server = null;
				if (isRestart) await handleRestartAfterServerClose();
				else await handleStopAfterServerClose();
			}
		})();
	};
	const onSigterm = () => {
		gatewayLog$1.info("signal SIGTERM received");
		request("stop", "SIGTERM");
	};
	const onSigint = () => {
		gatewayLog$1.info("signal SIGINT received");
		request("stop", "SIGINT");
	};
	const onSigusr1 = () => {
		gatewayLog$1.info("signal SIGUSR1 received");
		if (!consumeGatewaySigusr1RestartAuthorization()) {
			if (!isGatewaySigusr1RestartExternallyAllowed()) {
				gatewayLog$1.warn("SIGUSR1 restart ignored (not authorized; commands.restart=false or use gateway tool).");
				return;
			}
			if (shuttingDown) {
				gatewayLog$1.info("received SIGUSR1 during shutdown; ignoring");
				return;
			}
			scheduleGatewaySigusr1Restart({
				delayMs: 0,
				reason: "SIGUSR1"
			});
			return;
		}
		markGatewaySigusr1RestartHandled();
		request("restart", "SIGUSR1");
	};
	process.on("SIGTERM", onSigterm);
	process.on("SIGINT", onSigint);
	process.on("SIGUSR1", onSigusr1);
	try {
		const onIteration = createRestartIterationHook(() => {
			resetAllLanes();
		});
		let isFirstStart = true;
		for (;;) {
			onIteration();
			try {
				server = await params.start({ startupStartedAt });
				isFirstStart = false;
			} catch (err) {
				if (isFirstStart) throw err;
				server = null;
				await releaseLockIfHeld();
				const errMsg = formatErrorMessage(err);
				const errStack = err instanceof Error && err.stack ? `\n${err.stack}` : "";
				gatewayLog$1.error(`gateway startup failed: ${errMsg}. Process will stay alive; fix the issue and restart.${errStack}`);
			}
			await new Promise((resolve) => {
				restartResolver = resolve;
			});
		}
	} finally {
		await releaseLockIfHeld();
		cleanupSignals();
	}
}
//#endregion
//#region src/cli/gateway-cli/shared.ts
const toOptionString = (value) => {
	if (typeof value === "string") return value;
	if (typeof value === "number" || typeof value === "bigint") return value.toString();
};
function extractGatewayMiskeys(parsed) {
	if (!parsed || typeof parsed !== "object") return {
		hasGatewayToken: false,
		hasRemoteToken: false
	};
	const gateway = parsed.gateway;
	if (!gateway || typeof gateway !== "object") return {
		hasGatewayToken: false,
		hasRemoteToken: false
	};
	const hasGatewayToken = "token" in gateway;
	const remote = gateway.remote;
	return {
		hasGatewayToken,
		hasRemoteToken: remote && typeof remote === "object" ? "token" in remote : false
	};
}
function renderGatewayServiceStopHints(env = process.env) {
	const profile = env.KAIJIBOT_PROFILE;
	switch (process.platform) {
		case "darwin": return [`Tip: ${formatCliCommand("kaijibot gateway stop")}`, `Or: launchctl bootout gui/$UID/${resolveGatewayLaunchAgentLabel(profile)}`];
		case "linux": return [`Tip: ${formatCliCommand("kaijibot gateway stop")}`, `Or: systemctl --user stop ${resolveGatewaySystemdServiceName(profile)}.service`];
		case "win32": return [`Tip: ${formatCliCommand("kaijibot gateway stop")}`, `Or: schtasks /End /TN "${resolveGatewayWindowsTaskName(profile)}"`];
		default: return [`Tip: ${formatCliCommand("kaijibot gateway stop")}`];
	}
}
async function maybeExplainGatewayServiceStop() {
	const service = resolveGatewayService();
	let loaded = null;
	try {
		loaded = await service.isLoaded({ env: process.env });
	} catch {
		loaded = null;
	}
	if (loaded === false) return;
	defaultRuntime.error(loaded ? `Gateway service appears ${service.loadedText}. Stop it first.` : "Gateway service status unknown; if supervised, stop it first.");
	for (const hint of renderGatewayServiceStopHints()) defaultRuntime.error(hint);
}
//#endregion
//#region src/cli/gateway-cli/run.ts
const gatewayLog = createSubsystemLogger("gateway");
const GATEWAY_RUN_VALUE_KEYS = [
	"port",
	"bind",
	"token",
	"auth",
	"password",
	"passwordFile",
	"tailscale",
	"wsLog",
	"rawStreamPath"
];
const GATEWAY_RUN_BOOLEAN_KEYS = [
	"tailscaleResetOnExit",
	"allowUnconfigured",
	"dev",
	"reset",
	"force",
	"verbose",
	"cliBackendLogs",
	"claudeCliLogs",
	"compact",
	"rawStream"
];
const SUPERVISED_GATEWAY_LOCK_RETRY_MS = 5e3;
const GATEWAY_AUTH_MODES = [
	"none",
	"token",
	"password",
	"trusted-proxy"
];
const GATEWAY_TAILSCALE_MODES = [
	"off",
	"serve",
	"funnel"
];
function warnInlinePasswordFlag() {
	defaultRuntime.error("Warning: --password can be exposed via process listings. Prefer --password-file or KAIJIBOT_GATEWAY_PASSWORD.");
}
function resolveGatewayPasswordOption(opts) {
	const direct = toOptionString(opts.password);
	const file = toOptionString(opts.passwordFile);
	if (direct && file) throw new Error("Use either --password or --password-file.");
	if (file) return readSecretFromFile(file, "Gateway password");
	return direct;
}
function parseEnumOption(raw, allowed) {
	if (!raw) return null;
	return allowed.includes(raw) ? raw : null;
}
function formatModeChoices(modes) {
	return modes.map((mode) => `"${mode}"`).join("|");
}
function formatModeErrorList(modes) {
	const quoted = modes.map((mode) => `"${mode}"`);
	if (quoted.length === 0) return "";
	if (quoted.length === 1) return quoted[0];
	if (quoted.length === 2) return `${quoted[0]} or ${quoted[1]}`;
	return `${quoted.slice(0, -1).join(", ")}, or ${quoted[quoted.length - 1]}`;
}
function maybeLogPendingControlUiBuild(cfg) {
	if (cfg.gateway?.controlUi?.enabled === false) return;
	if (toOptionString(cfg.gateway?.controlUi?.root)) return;
	if (resolveControlUiRootSync({
		moduleUrl: import.meta.url,
		argv1: process.argv[1],
		cwd: process.cwd()
	})) return;
	gatewayLog.info("Control UI assets are missing; first startup may spend a few seconds building them before the gateway binds. Prebuild with `pnpm ui:build` for a faster first boot.");
}
function getGatewayStartGuardErrors(params) {
	if (params.allowUnconfigured || params.mode === "local") return [];
	if (!params.configExists) return [`Missing config. Run \`${formatCliCommand("kaijibot setup")}\` or set gateway.mode=local (or pass --allow-unconfigured).`];
	if (params.mode === void 0) return [[
		"Gateway start blocked: existing config is missing gateway.mode.",
		"Treat this as suspicious or clobbered config.",
		`Re-run \`${formatCliCommand("kaijibot onboard --mode local")}\` or \`${formatCliCommand("kaijibot setup")}\`, set gateway.mode=local manually, or pass --allow-unconfigured.`
	].join(" "), `Config write audit: ${params.configAuditPath}`];
	return [`Gateway start blocked: set gateway.mode=local (current: ${params.mode}) or pass --allow-unconfigured.`, `Config write audit: ${params.configAuditPath}`];
}
function resolveGatewayRunOptions(opts, command) {
	const resolved = { ...opts };
	for (const key of GATEWAY_RUN_VALUE_KEYS) {
		const inherited = inheritOptionFromParent(command, key);
		if (key === "wsLog") {
			resolved[key] = inherited ?? resolved[key];
			continue;
		}
		resolved[key] = resolved[key] ?? inherited;
	}
	for (const key of GATEWAY_RUN_BOOLEAN_KEYS) {
		const inherited = inheritOptionFromParent(command, key);
		resolved[key] = Boolean(resolved[key] || inherited);
	}
	return resolved;
}
function isGatewayLockError(err) {
	return err instanceof GatewayLockError || !!err && typeof err === "object" && err.name === "GatewayLockError";
}
function isHealthyGatewayLockError(err) {
	if (!isGatewayLockError(err) || typeof err.message !== "string") return false;
	return err.message.includes("gateway already running") || err.message.includes("another gateway instance is already listening");
}
async function runGatewayCommand$1(opts) {
	const isDevProfile = normalizeOptionalLowercaseString(process.env.KAIJIBOT_PROFILE) === "dev";
	const devMode = Boolean(opts.dev) || isDevProfile;
	if (opts.reset && !devMode) {
		defaultRuntime.error("Use --reset with --dev.");
		defaultRuntime.exit(1);
		return;
	}
	setVerbose(Boolean(opts.verbose));
	if (opts.cliBackendLogs || opts.claudeCliLogs) {
		setConsoleSubsystemFilter(["agent/cli-backend"]);
		process.env.KAIJIBOT_CLI_BACKEND_LOG_OUTPUT = "1";
	}
	const wsLogRaw = opts.compact ? "compact" : opts.wsLog;
	const wsLogStyle = wsLogRaw === "compact" ? "compact" : wsLogRaw === "full" ? "full" : "auto";
	if (wsLogRaw !== void 0 && wsLogRaw !== "auto" && wsLogRaw !== "compact" && wsLogRaw !== "full") {
		defaultRuntime.error("Invalid --ws-log (use \"auto\", \"full\", \"compact\")");
		defaultRuntime.exit(1);
	}
	setGatewayWsLogStyle(wsLogStyle);
	if (opts.rawStream) process.env.KAIJIBOT_RAW_STREAM = "1";
	const rawStreamPath = toOptionString(opts.rawStreamPath);
	if (rawStreamPath) process.env.KAIJIBOT_RAW_STREAM_PATH = rawStreamPath;
	const { startGatewayServer } = await withProgress({
		label: "Loading gateway modules…",
		indeterminate: true
	}, async () => import("./server-DNDCdh8f.js"));
	setConsoleTimestampPrefix(true);
	if (devMode) await ensureDevGatewayConfig({ reset: Boolean(opts.reset) });
	gatewayLog.info("loading configuration…");
	const cfg = loadConfig();
	maybeLogPendingControlUiBuild(cfg);
	const portOverride = parsePort(opts.port);
	if (opts.port !== void 0 && portOverride === null) {
		defaultRuntime.error("Invalid port");
		defaultRuntime.exit(1);
	}
	const port = portOverride ?? resolveGatewayPort(cfg);
	if (!Number.isFinite(port) || port <= 0) {
		defaultRuntime.error("Invalid port");
		defaultRuntime.exit(1);
	}
	const VALID_BIND_MODES = new Set([
		"loopback",
		"lan",
		"auto",
		"custom",
		"tailnet"
	]);
	const bindExplicitRawStr = normalizeOptionalString(toOptionString(opts.bind) ?? cfg.gateway?.bind);
	if (bindExplicitRawStr !== void 0 && !VALID_BIND_MODES.has(bindExplicitRawStr)) {
		defaultRuntime.error("Invalid --bind (use \"loopback\", \"lan\", \"tailnet\", \"auto\", or \"custom\")");
		defaultRuntime.exit(1);
		return;
	}
	const bindExplicitRaw = bindExplicitRawStr;
	if (process.env.KAIJIBOT_SERVICE_MARKER?.trim()) {
		const stale = cleanStaleGatewayProcessesSync(port);
		if (stale.length > 0) gatewayLog.info(`service-mode: cleared ${stale.length} stale gateway pid(s) before bind on port ${port}`);
	}
	if (opts.force) try {
		const { killed, waitedMs, escalatedToSigkill } = await forceFreePortAndWait(port, {
			timeoutMs: 2e3,
			intervalMs: 100,
			sigtermTimeoutMs: 700
		});
		if (killed.length === 0) gatewayLog.info(`force: no listeners on port ${port}`);
		else {
			for (const proc of killed) gatewayLog.info(`force: killed pid ${proc.pid}${proc.command ? ` (${proc.command})` : ""} on port ${port}`);
			if (escalatedToSigkill) gatewayLog.info(`force: escalated to SIGKILL while freeing port ${port}`);
			if (waitedMs > 0) gatewayLog.info(`force: waited ${waitedMs}ms for port ${port} to free`);
		}
		const bindWaitMs = await waitForPortBindable(port, {
			timeoutMs: 3e3,
			intervalMs: 150,
			host: bindExplicitRaw === "loopback" ? "127.0.0.1" : bindExplicitRaw === "lan" ? "0.0.0.0" : bindExplicitRaw === "custom" ? toOptionString(cfg.gateway?.customBindHost) : void 0
		});
		if (bindWaitMs > 0) gatewayLog.info(`force: waited ${bindWaitMs}ms for port ${port} to become bindable`);
	} catch (err) {
		defaultRuntime.error(`Force: ${String(err)}`);
		defaultRuntime.exit(1);
		return;
	}
	if (opts.token) {
		const token = toOptionString(opts.token);
		if (token) process.env.KAIJIBOT_GATEWAY_TOKEN = token;
	}
	const authModeRaw = toOptionString(opts.auth);
	const authMode = parseEnumOption(authModeRaw, GATEWAY_AUTH_MODES);
	if (authModeRaw && !authMode) {
		defaultRuntime.error(`Invalid --auth (use ${formatModeErrorList(GATEWAY_AUTH_MODES)})`);
		defaultRuntime.exit(1);
		return;
	}
	const tailscaleRaw = toOptionString(opts.tailscale);
	const tailscaleMode = parseEnumOption(tailscaleRaw, GATEWAY_TAILSCALE_MODES);
	if (tailscaleRaw && !tailscaleMode) {
		defaultRuntime.error(`Invalid --tailscale (use ${formatModeErrorList(GATEWAY_TAILSCALE_MODES)})`);
		defaultRuntime.exit(1);
		return;
	}
	const effectiveTailscaleMode = tailscaleMode ?? cfg.gateway?.tailscale?.mode ?? "off";
	const bind = bindExplicitRaw ?? defaultGatewayBindMode(effectiveTailscaleMode);
	let passwordRaw;
	try {
		passwordRaw = resolveGatewayPasswordOption(opts);
	} catch (err) {
		defaultRuntime.error(formatErrorMessage(err));
		defaultRuntime.exit(1);
		return;
	}
	if (toOptionString(opts.password)) warnInlinePasswordFlag();
	const tokenRaw = toOptionString(opts.token);
	gatewayLog.info("resolving authentication…");
	const snapshot = await readConfigFileSnapshot().catch(() => null);
	const configExists = snapshot?.exists ?? fs.existsSync(CONFIG_PATH);
	const configAuditPath = path.join(resolveStateDir(process.env), "logs", "config-audit.jsonl");
	const mode = (snapshot?.valid ? snapshot.config : cfg).gateway?.mode;
	const guardErrors = getGatewayStartGuardErrors({
		allowUnconfigured: opts.allowUnconfigured,
		configExists,
		configAuditPath,
		mode
	});
	if (guardErrors.length > 0) {
		for (const error of guardErrors) defaultRuntime.error(error);
		defaultRuntime.exit(1);
		return;
	}
	const miskeys = extractGatewayMiskeys(snapshot?.parsed);
	const authOverride = authMode || passwordRaw || tokenRaw || authModeRaw ? {
		...authMode ? { mode: authMode } : {},
		...tokenRaw ? { token: tokenRaw } : {},
		...passwordRaw ? { password: passwordRaw } : {}
	} : void 0;
	const resolvedAuth = resolveGatewayAuth({
		authConfig: cfg.gateway?.auth,
		authOverride,
		env: process.env,
		tailscaleMode: tailscaleMode ?? cfg.gateway?.tailscale?.mode ?? "off"
	});
	const resolvedAuthMode = resolvedAuth.mode;
	const tokenValue = resolvedAuth.token;
	const passwordValue = resolvedAuth.password;
	const hasToken = typeof tokenValue === "string" && tokenValue.trim().length > 0;
	const hasPassword = typeof passwordValue === "string" && passwordValue.trim().length > 0;
	const tokenConfigured = hasToken || hasConfiguredSecretInput(authOverride?.token ?? cfg.gateway?.auth?.token, cfg.secrets?.defaults);
	const passwordConfigured = hasPassword || hasConfiguredSecretInput(authOverride?.password ?? cfg.gateway?.auth?.password, cfg.secrets?.defaults);
	const hasSharedSecret = resolvedAuthMode === "token" && tokenConfigured || resolvedAuthMode === "password" && passwordConfigured;
	const canBootstrapToken = resolvedAuthMode === "token" && !tokenConfigured;
	const authHints = [];
	if (miskeys.hasGatewayToken) authHints.push("Found \"gateway.token\" in config. Use \"gateway.auth.token\" instead.");
	if (miskeys.hasRemoteToken) authHints.push("\"gateway.remote.token\" is for remote CLI calls; it does not enable local gateway auth.");
	if (resolvedAuthMode === "password" && !passwordConfigured) {
		defaultRuntime.error([
			"Gateway auth is set to password, but no password is configured.",
			"Set gateway.auth.password (or KAIJIBOT_GATEWAY_PASSWORD), or pass --password.",
			...authHints
		].filter(Boolean).join("\n"));
		defaultRuntime.exit(1);
		return;
	}
	if (resolvedAuthMode === "none") gatewayLog.warn("Gateway auth mode=none explicitly configured; all gateway connections are unauthenticated.");
	if (bind !== "loopback" && !hasSharedSecret && !canBootstrapToken && resolvedAuthMode !== "trusted-proxy") {
		defaultRuntime.error([
			`Refusing to bind gateway to ${bind} without auth.`,
			...isContainerEnvironment() ? ["Container environment detected — the gateway defaults to bind=auto (0.0.0.0) for port-forwarding compatibility.", "Set KAIJIBOT_GATEWAY_TOKEN or KAIJIBOT_GATEWAY_PASSWORD, or pass --token/--password to start with auth."] : ["Set gateway.auth.token/password (or KAIJIBOT_GATEWAY_TOKEN/KAIJIBOT_GATEWAY_PASSWORD) or pass --token/--password."],
			...authHints
		].filter(Boolean).join("\n"));
		defaultRuntime.exit(1);
		return;
	}
	const tailscaleOverride = tailscaleMode || opts.tailscaleResetOnExit ? {
		...tailscaleMode ? { mode: tailscaleMode } : {},
		...opts.tailscaleResetOnExit ? { resetOnExit: true } : {}
	} : void 0;
	gatewayLog.info("starting...");
	const startLoop = async () => await runGatewayLoop({
		runtime: defaultRuntime,
		lockPort: port,
		start: async ({ startupStartedAt } = {}) => await startGatewayServer(port, {
			bind,
			auth: authOverride,
			tailscale: tailscaleOverride,
			startupStartedAt
		})
	});
	try {
		const supervisor = detectRespawnSupervisor(process.env);
		while (true) try {
			await startLoop();
			break;
		} catch (err) {
			const isGatewayAlreadyRunning = err instanceof GatewayLockError && typeof err.message === "string" && err.message.includes("gateway already running");
			if (!supervisor || !isGatewayAlreadyRunning) throw err;
			gatewayLog.warn(`gateway already running under ${supervisor}; waiting ${SUPERVISED_GATEWAY_LOCK_RETRY_MS}ms before retrying startup`);
			await new Promise((resolve) => setTimeout(resolve, SUPERVISED_GATEWAY_LOCK_RETRY_MS));
		}
	} catch (err) {
		if (isGatewayLockError(err)) {
			const errMessage = formatErrorMessage(err);
			defaultRuntime.error(`Gateway failed to start: ${errMessage}\nIf the gateway is supervised, stop it with: ${formatCliCommand("kaijibot gateway stop")}`);
			try {
				const diagnostics = await inspectPortUsage(port);
				if (diagnostics.status === "busy") for (const line of formatPortDiagnostics(diagnostics)) defaultRuntime.error(line);
			} catch {}
			await maybeExplainGatewayServiceStop();
			defaultRuntime.exit(isHealthyGatewayLockError(err) ? 0 : 1);
			return;
		}
		defaultRuntime.error(`Gateway failed to start: ${String(err)}`);
		defaultRuntime.exit(1);
	}
}
function addGatewayRunCommand(cmd) {
	return cmd.option("--port <port>", "Port for the gateway WebSocket").option("--bind <mode>", "Bind mode (\"loopback\"|\"lan\"|\"tailnet\"|\"auto\"|\"custom\"). Defaults to config gateway.bind (or loopback).").option("--token <token>", "Shared token required in connect.params.auth.token (default: KAIJIBOT_GATEWAY_TOKEN env if set)").option("--auth <mode>", `Gateway auth mode (${formatModeChoices(GATEWAY_AUTH_MODES)})`).option("--password <password>", "Password for auth mode=password").option("--password-file <path>", "Read gateway password from file").option("--tailscale <mode>", `Tailscale exposure mode (${formatModeChoices(GATEWAY_TAILSCALE_MODES)})`).option("--tailscale-reset-on-exit", "Reset Tailscale serve/funnel configuration on shutdown", false).option("--allow-unconfigured", "Allow gateway start without enforcing gateway.mode=local in config (does not repair config)", false).option("--dev", "Create a dev config + workspace if missing (no BOOTSTRAP.md)", false).option("--reset", "Reset dev config + credentials + sessions + workspace (requires --dev)", false).option("--force", "Kill any existing listener on the target port before starting", false).option("--verbose", "Verbose logging to stdout/stderr", false).option("--cli-backend-logs", "Only show CLI backend logs in the console (includes stdout/stderr)", false).option("--claude-cli-logs", "Deprecated alias for --cli-backend-logs", false).option("--ws-log <style>", "WebSocket log style (\"auto\"|\"full\"|\"compact\")", "auto").option("--compact", "Alias for \"--ws-log compact\"", false).option("--raw-stream", "Log raw model stream events to jsonl", false).option("--raw-stream-path <path>", "Raw stream jsonl path").action(async (opts, command) => {
		await runGatewayCommand$1(resolveGatewayRunOptions(opts, command));
	});
}
//#endregion
//#region src/cli/gateway-cli/register.ts
function runGatewayCommand(action, label) {
	return runCommandWithRuntime(defaultRuntime, action, (err) => {
		const message = String(err);
		defaultRuntime.error(label ? `${label}: ${message}` : message);
		defaultRuntime.exit(1);
	});
}
function parseDaysOption(raw, fallback = 30) {
	if (typeof raw === "number" && Number.isFinite(raw)) return Math.max(1, Math.floor(raw));
	if (typeof raw === "string" && raw.trim() !== "") {
		const parsed = Number(raw);
		if (Number.isFinite(parsed)) return Math.max(1, Math.floor(parsed));
	}
	return fallback;
}
function resolveGatewayRpcOptions(opts, command) {
	const parentToken = inheritOptionFromParent(command, "token");
	const parentPassword = inheritOptionFromParent(command, "password");
	return {
		...opts,
		token: opts.token ?? parentToken,
		password: opts.password ?? parentPassword
	};
}
function renderCostUsageSummary(summary, days, rich) {
	const totalCost = formatUsd(summary.totals.totalCost) ?? "$0.00";
	const totalTokens = formatTokenCount(summary.totals.totalTokens) ?? "0";
	const lines = [colorize(rich, theme.heading, `Usage cost (${days} days)`), `${colorize(rich, theme.muted, "Total:")} ${totalCost} · ${totalTokens} tokens`];
	if (summary.totals.missingCostEntries > 0) lines.push(`${colorize(rich, theme.muted, "Missing entries:")} ${summary.totals.missingCostEntries}`);
	const latest = summary.daily.at(-1);
	if (latest) {
		const latestCost = formatUsd(latest.totalCost) ?? "$0.00";
		const latestTokens = formatTokenCount(latest.totalTokens) ?? "0";
		lines.push(`${colorize(rich, theme.muted, "Latest day:")} ${latest.date} · ${latestCost} · ${latestTokens} tokens`);
	}
	return lines;
}
function registerGatewayCli(program) {
	const gateway = addGatewayRunCommand(program.command("gateway").description("Run, inspect, and query the WebSocket Gateway").addHelpText("after", () => `\n${theme.heading("Examples:")}\n${formatHelpExamples([
		["kaijibot gateway run", "Run the gateway in the foreground."],
		["kaijibot gateway status", "Show service status and probe reachability."],
		["kaijibot gateway discover", "Find local and wide-area gateway beacons."],
		["kaijibot gateway call health", "Call a gateway RPC method directly."]
	])}\n\n${theme.muted("Docs:")} ${formatDocsLink("/cli/gateway", "docs.kaijibot.ai/cli/gateway")}\n`));
	addGatewayRunCommand(gateway.command("run").description("Run the WebSocket Gateway (foreground)"));
	addGatewayServiceCommands(gateway, { statusDescription: "Show gateway service status + probe the Gateway" });
	gatewayCallOpts(gateway.command("call").description("Call a Gateway method").argument("<method>", "Method name (health/status/system-presence/cron.*)").option("--params <json>", "JSON object string for params", "{}").action(async (method, opts, command) => {
		await runGatewayCommand(async () => {
			const rpcOpts = resolveGatewayRpcOptions(opts, command);
			const config = await readBestEffortConfig();
			const params = JSON.parse(String(opts.params ?? "{}"));
			const result = await callGatewayCli(method, {
				...rpcOpts,
				config
			}, params);
			if (rpcOpts.json) {
				defaultRuntime.writeJson(result);
				return;
			}
			const rich = isRich();
			defaultRuntime.log(`${colorize(rich, theme.heading, "Gateway call")}: ${colorize(rich, theme.muted, String(method))}`);
			defaultRuntime.writeJson(result);
		}, "Gateway call failed");
	}));
	gatewayCallOpts(gateway.command("usage-cost").description("Fetch usage cost summary from session logs").option("--days <days>", "Number of days to include", "30").action(async (opts, command) => {
		await runGatewayCommand(async () => {
			const rpcOpts = resolveGatewayRpcOptions(opts, command);
			const days = parseDaysOption(opts.days);
			const config = await readBestEffortConfig();
			const result = await callGatewayCli("usage.cost", {
				...rpcOpts,
				config
			}, { days });
			if (rpcOpts.json) {
				defaultRuntime.writeJson(result);
				return;
			}
			const rich = isRich();
			const summary = result;
			for (const line of renderCostUsageSummary(summary, days, rich)) defaultRuntime.log(line);
		}, "Gateway usage cost failed");
	}));
	gatewayCallOpts(gateway.command("health").description("Fetch Gateway health").action(async (opts, command) => {
		await runGatewayCommand(async () => {
			const rpcOpts = resolveGatewayRpcOptions(opts, command);
			const config = await readBestEffortConfig();
			const result = await callGatewayCli("health", {
				...rpcOpts,
				config
			});
			if (rpcOpts.json) {
				defaultRuntime.writeJson(result);
				return;
			}
			const rich = isRich();
			const obj = result && typeof result === "object" ? result : {};
			const durationMs = typeof obj.durationMs === "number" ? obj.durationMs : null;
			defaultRuntime.log(colorize(rich, theme.heading, "Gateway Health"));
			defaultRuntime.log(`${colorize(rich, theme.success, "OK")}${durationMs != null ? ` (${durationMs}ms)` : ""}`);
			if (obj.channels && typeof obj.channels === "object") for (const line of formatHealthChannelLines(obj)) defaultRuntime.log(styleHealthChannelLine(line, rich));
		});
	}));
	gateway.command("probe").description("Show gateway reachability + discovery + health + status summary (local + remote)").option("--url <url>", "Explicit Gateway WebSocket URL (still probes localhost)").option("--ssh <target>", "SSH target for remote gateway tunnel (user@host or user@host:port)").option("--ssh-identity <path>", "SSH identity file path").option("--ssh-auto", "Try to derive an SSH target from Bonjour discovery", false).option("--token <token>", "Gateway token (applies to all probes)").option("--password <password>", "Gateway password (applies to all probes)").option("--timeout <ms>", "Overall probe budget in ms", "3000").option("--json", "Output JSON", false).action(async (opts, command) => {
		await runGatewayCommand(async () => {
			await gatewayStatusCommand(resolveGatewayRpcOptions(opts, command), defaultRuntime);
		});
	});
	gateway.command("discover").description("Discover gateways via Bonjour (local + wide-area if configured)").option("--timeout <ms>", "Per-command timeout in ms", "2000").option("--json", "Output JSON", false).action(async (opts) => {
		await runGatewayCommand(async () => {
			const wideAreaDomain = resolveWideAreaDiscoveryDomain({ configDomain: (await readBestEffortConfig()).discovery?.wideArea?.domain });
			const timeoutMs = parseDiscoverTimeoutMs(opts.timeout, 2e3);
			const domains = ["local.", ...wideAreaDomain ? [wideAreaDomain] : []];
			const deduped = dedupeBeacons(await withProgress({
				label: "Scanning for gateways…",
				indeterminate: true,
				enabled: opts.json !== true,
				delayMs: 0
			}, async () => await discoverGatewayBeacons({
				timeoutMs,
				wideAreaDomain
			}))).toSorted((a, b) => String(a.displayName || a.instanceName).localeCompare(String(b.displayName || b.instanceName)));
			if (opts.json) {
				const enriched = deduped.map((b) => {
					const host = pickBeaconHost(b);
					const port = pickGatewayPort(b);
					return {
						...b,
						wsUrl: host ? `ws://${host}:${port}` : null
					};
				});
				defaultRuntime.writeJson({
					timeoutMs,
					domains,
					count: enriched.length,
					beacons: enriched
				});
				return;
			}
			const rich = isRich();
			defaultRuntime.log(colorize(rich, theme.heading, "Gateway Discovery"));
			defaultRuntime.log(colorize(rich, theme.muted, `Found ${deduped.length} gateway(s) · domains: ${domains.join(", ")}`));
			if (deduped.length === 0) return;
			for (const beacon of deduped) for (const line of renderBeaconLines(beacon, rich)) defaultRuntime.log(line);
		}, "gateway discover failed");
	});
}
//#endregion
export { registerGatewayCli };
