import { H as isRich, M as info, U as theme } from "./agent-paths-BySKyq9I.js";
import { Bf as APPROVALS_SCOPE, Cd as isTruthyEnvValue, Dn as loadConfig, Et as loadSessionStore, Hf as PAIRING_SCOPE, Mu as getChannelPlugin, Nu as listChannelPlugins, Sd as resolvePreferredAccountId, bd as buildChannelAccountBindings, d as resolveDefaultAgentId, dd as formatCliCommand, eu as runExec, mf as GATEWAY_CLIENT_NAMES, pf as GATEWAY_CLIENT_MODES, zf as ADMIN_SCOPE } from "./agent-scope-CLn_Ws3B.js";
import { $o as getTailnetHostname, es as getTailscaleBinary } from "./reply-DQfPM5ia.js";
import { s as normalizeAgentId } from "./session-key-LmCHAJTQ.js";
import { n as formatErrorMessage } from "./errors-Dx3S3Nrp.js";
import { g as resolveStorePath } from "./chat-envelope-D_wSv26N.js";
import { t as rawDataToString } from "./ws-cvQhrVP3.js";
import { a as publicKeyRawBase64UrlFromPem, o as signDevicePayload, t as createEphemeralDeviceIdentity } from "./device-identity-DzUZ06fL.js";
import { Wn as buildDeviceAuthPayload, n as callGateway, t as buildGatewayConnectionDetails, zn as PROTOCOL_VERSION } from "./call-s6DNIELo.js";
import { n as withProgress } from "./progress-CDqdg8f8.js";
import { t as resolveChannelDefaultAccountId } from "./helpers-BvDnvwsE.js";
import { t as projectSafeChannelAccountSnapshotFields } from "./account-snapshot-fields-BgmEQ0wd.js";
import { t as resolveHeartbeatSummaryForAgent } from "./heartbeat-runner-DXh84Wuy.js";
import { WebSocket as WebSocket$1 } from "ws";

//#region src/gateway/server-startup-readiness.ts
const SERVICE_DEFS = [
	{
		id: "config",
		label: "Config",
		matches: (entry) => entry.name.startsWith("config.")
	},
	{
		id: "plugins",
		label: "Plugins",
		matches: (entry) => entry.name.startsWith("plugins.")
	},
	{
		id: "runtime",
		label: "Runtime",
		matches: (entry) => entry.name.startsWith("runtime.")
	},
	{
		id: "control-ui",
		label: "Control UI",
		matches: (entry) => entry.name.startsWith("control-ui.")
	},
	{
		id: "transport",
		label: "Transport",
		matches: (entry) => entry.name.startsWith("tls.") || entry.name.startsWith("ws.") || entry.name.startsWith("tailscale.") || entry.name.startsWith("discovery.")
	},
	{
		id: "automation",
		label: "Automation",
		matches: (entry) => entry.name.startsWith("cron.") || entry.name.startsWith("maintenance.") || entry.name.startsWith("config-reload.")
	},
	{
		id: "signer",
		label: "Signer",
		matches: (entry) => entry.name.startsWith("local-signer.")
	},
	{
		id: "sidecars",
		label: "Sidecars",
		matches: (entry) => entry.name.startsWith("sidecars.")
	}
];
function clonePhase(entry) {
	return {
		name: entry.name,
		durationMs: Math.max(0, Math.round(entry.durationMs))
	};
}
function createService(def, entries) {
	const phases = entries.map(clonePhase);
	return {
		id: def.id,
		label: def.label,
		status: phases.length > 0 ? "ready" : "unknown",
		durationMs: phases.reduce((sum, entry) => sum + entry.durationMs, 0),
		phases
	};
}
function buildGatewayStartupReadinessSnapshot(trace) {
	if (!trace) return {
		status: "unknown",
		services: SERVICE_DEFS.map((def) => createService(def, []))
	};
	const services = SERVICE_DEFS.map((def) => createService(def, trace.entries.filter((entry) => def.matches(entry))));
	const matched = new Set(services.flatMap((service) => service.phases.map((phase) => phase.name)));
	const otherPhases = trace.entries.filter((entry) => !matched.has(entry.name));
	if (otherPhases.length > 0) services.push({
		id: "other",
		label: "Other",
		status: "ready",
		durationMs: otherPhases.reduce((sum, entry) => sum + Math.max(0, entry.durationMs), 0),
		phases: otherPhases.map(clonePhase)
	});
	return {
		status: "ready",
		recordedAtMs: trace.recordedAtMs,
		totalMs: trace.totalMs,
		summary: trace.summary,
		services
	};
}

//#endregion
//#region src/gateway/server-startup-trace.ts
function formatDuration(ms) {
	return `${Math.max(0, Math.round(ms))}ms`;
}
let lastGatewayStartupTraceSnapshot = null;
function getLastGatewayStartupTraceSnapshot() {
	if (!lastGatewayStartupTraceSnapshot) return null;
	return {
		...lastGatewayStartupTraceSnapshot,
		entries: lastGatewayStartupTraceSnapshot.entries.map((entry) => ({ ...entry }))
	};
}
function createGatewayStartupTrace(opts = {}) {
	const now = opts.now ?? (() => Date.now());
	const startedAt = now();
	const entries = [];
	const live = opts.live === true;
	const liveLogger = opts.liveLogger ?? null;
	const record = (name, started) => {
		entries.push({
			name,
			durationMs: Math.max(0, Math.round(now() - started))
		});
	};
	return {
		measureSync(name, run) {
			const started = now();
			if (live && liveLogger) liveLogger.info(`gateway startup: ${name} start`);
			try {
				return run();
			} finally {
				record(name, started);
				if (live && liveLogger) liveLogger.info(`gateway startup: ${name} done (${formatDuration(now() - started)})`);
			}
		},
		async measure(name, run) {
			const started = now();
			if (live && liveLogger) liveLogger.info(`gateway startup: ${name} start`);
			try {
				return await run();
			} finally {
				record(name, started);
				if (live && liveLogger) liveLogger.info(`gateway startup: ${name} done (${formatDuration(now() - started)})`);
			}
		},
		entries() {
			return entries.map((entry) => ({ ...entry }));
		},
		totalMs() {
			return Math.max(0, Math.round(now() - startedAt));
		},
		summary() {
			const parts = entries.map((entry) => `${entry.name}=${formatDuration(entry.durationMs)}`);
			parts.push(`total=${formatDuration(this.totalMs())}`);
			return parts.join(", ");
		},
		logSummary(logger) {
			if (entries.length === 0) return;
			const totalMs = this.totalMs();
			const summary = this.summary();
			const startupTimings = this.entries();
			lastGatewayStartupTraceSnapshot = {
				entries: startupTimings,
				totalMs,
				summary,
				recordedAtMs: Math.round(now())
			};
			logger.info(`gateway startup timings: ${summary}`, {
				startupTimings,
				totalMs
			});
		}
	};
}

//#endregion
//#region src/terminal/health-style.ts
function styleHealthChannelLine(line, rich) {
	if (!rich) return line;
	const colon = line.indexOf(":");
	if (colon === -1) return line;
	const label = line.slice(0, colon + 1);
	const detail = line.slice(colon + 1).trimStart();
	const normalized = detail.toLowerCase();
	const applyPrefix = (prefix, color) => `${label} ${color(detail.slice(0, prefix.length))}${detail.slice(prefix.length)}`;
	if (normalized.startsWith("failed")) return applyPrefix("failed", theme.error);
	if (normalized.startsWith("ok")) return applyPrefix("ok", theme.success);
	if (normalized.startsWith("linked")) return applyPrefix("linked", theme.success);
	if (normalized.startsWith("configured")) return applyPrefix("configured", theme.success);
	if (normalized.startsWith("not linked")) return applyPrefix("not linked", theme.warn);
	if (normalized.startsWith("not configured")) return applyPrefix("not configured", theme.muted);
	if (normalized.startsWith("unknown")) return applyPrefix("unknown", theme.warn);
	return line;
}

//#endregion
//#region src/commands/hosted-dashboard-probe.ts
function sanitizeErrorMessage(value) {
	return (value instanceof Error ? value.message : String(value)).replace(/\s+/g, " ").trim().slice(0, 300) || "unknown error";
}
function buildOrigin(httpUrl) {
	return new URL(httpUrl).origin;
}
function buildLoginUrl(httpUrl) {
	return new URL("/api/control-ui/login/token", httpUrl).toString();
}
function buildWsUrl(httpUrl) {
	const url = new URL(httpUrl);
	url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
	url.search = "";
	url.hash = "";
	return url.toString();
}
function resolveAssetUrl(src, baseUrl) {
	const trimmed = src.trim();
	if (!trimmed) return null;
	try {
		return new URL(trimmed, baseUrl).toString();
	} catch {
		return null;
	}
}
function extractHtmlAssetUrls(html, baseUrl) {
	const urls = /* @__PURE__ */ new Set();
	for (const match of html.matchAll(/<(?:script|link)\b[^>]*\b(?:src|href)=["']([^"']+)["']/gi)) {
		const resolved = resolveAssetUrl(match[1] ?? "", baseUrl);
		if (resolved) urls.add(resolved);
	}
	return [...urls];
}
function extractHtmlScriptAssetUrls(html, baseUrl) {
	const urls = /* @__PURE__ */ new Set();
	for (const match of html.matchAll(/<script\b[^>]*>/gi)) {
		const tag = match[0] ?? "";
		if (/\bdata-fased-boot-watchdog\b/i.test(tag)) continue;
		const resolved = resolveAssetUrl(tag.match(/\bsrc=["']([^"']+)["']/i)?.[1] ?? "", baseUrl);
		if (resolved && /\.js(?:[?#].*)?$/i.test(resolved)) urls.add(resolved);
	}
	return [...urls];
}
function extractDynamicImportUrls(js, baseUrl) {
	const urls = /* @__PURE__ */ new Set();
	for (const match of js.matchAll(/\bimport\(\s*["']([^"']+\.js)["']\s*\)/g)) {
		const resolved = resolveAssetUrl(match[1] ?? "", baseUrl);
		if (resolved) urls.add(resolved);
	}
	return [...urls];
}
async function fetchTextWithTimeout(params) {
	const controller = new AbortController();
	const timer = setTimeout(() => controller.abort(), params.timeoutMs);
	try {
		const response = await fetch(params.url, {
			method: "GET",
			headers: params.headers,
			cache: "no-store",
			redirect: "follow",
			signal: controller.signal
		});
		const text = await response.text().catch(() => "");
		return {
			ok: true,
			url: response.url || params.url,
			status: response.status,
			contentType: response.headers.get("content-type") ?? "",
			text
		};
	} catch (err) {
		return {
			ok: false,
			message: sanitizeErrorMessage(err)
		};
	} finally {
		clearTimeout(timer);
	}
}
function inferServe(httpUrl) {
	try {
		return new URL(httpUrl).protocol === "https:" ? "tailscale" : "direct";
	} catch {
		return "direct";
	}
}
function urlPathnameLower(url) {
	try {
		return new URL(url).pathname.toLowerCase();
	} catch {
		return url.toLowerCase();
	}
}
function isLikelyAppJsUrl(url) {
	const basename = urlPathnameLower(url).split("/").pop() ?? "";
	return /^app-[^.]+\.js$/.test(basename);
}
function makeAssetCheck(params) {
	const valid = validateAssetResponse(params);
	return valid.ok ? {
		url: params.url,
		ok: true,
		status: params.status,
		contentType: params.contentType
	} : {
		url: params.url,
		ok: false,
		status: params.status,
		contentType: params.contentType,
		message: valid.message
	};
}
function validateAssetResponse(params) {
	if (params.status < 200 || params.status >= 300) return {
		ok: false,
		message: `${params.url} returned HTTP ${params.status}`
	};
	const head = params.text.slice(0, 80).trimStart().toLowerCase();
	if (head.startsWith("<!doctype") || head.startsWith("<html")) return {
		ok: false,
		message: `${params.url} returned HTML instead of an asset`
	};
	const lowerUrl = urlPathnameLower(params.url);
	const lowerContentType = params.contentType.toLowerCase();
	if (lowerUrl.endsWith(".js") && !lowerContentType.includes("javascript")) return {
		ok: false,
		message: `${params.url} returned ${params.contentType || "missing content-type"} instead of JavaScript`
	};
	if (lowerUrl.endsWith(".css") && !lowerContentType.includes("text/css")) return {
		ok: false,
		message: `${params.url} returned ${params.contentType || "missing content-type"} instead of CSS`
	};
	return { ok: true };
}
async function probeHostedControlUiAssets(params) {
	const bootCheck = {
		serve: inferServe(params.httpUrl),
		index: "failed",
		indexResponse: {
			url: params.httpUrl,
			ok: false
		},
		assets: []
	};
	const cookieHeader = `fased_ui_session=${encodeURIComponent(params.sessionToken)}`;
	const index = await fetchTextWithTimeout({
		url: params.httpUrl,
		timeoutMs: params.timeoutMs,
		headers: {
			accept: "text/html",
			cookie: cookieHeader
		}
	});
	if (!index.ok) {
		bootCheck.indexResponse.message = `index fetch failed: ${index.message}`;
		return {
			ok: false,
			message: bootCheck.indexResponse.message,
			bootCheck
		};
	}
	bootCheck.indexResponse = {
		url: index.url,
		ok: false,
		status: index.status,
		contentType: index.contentType
	};
	if (index.status < 200 || index.status >= 300) {
		bootCheck.indexResponse.message = `index returned HTTP ${index.status}`;
		return {
			ok: false,
			message: bootCheck.indexResponse.message,
			bootCheck
		};
	}
	if (!index.contentType.toLowerCase().includes("text/html")) {
		bootCheck.indexResponse.message = `index returned ${index.contentType || "missing content-type"} instead of HTML`;
		return {
			ok: false,
			message: bootCheck.indexResponse.message,
			bootCheck
		};
	}
	bootCheck.index = "ok";
	bootCheck.indexResponse.ok = true;
	const htmlAssets = extractHtmlAssetUrls(index.text, index.url).filter((url) => /\.(?:js|css)(?:[?#].*)?$/i.test(url));
	const entryJsUrl = extractHtmlScriptAssetUrls(index.text, index.url)[0];
	if (htmlAssets.length === 0) {
		const message = "index did not reference dashboard JS/CSS assets";
		bootCheck.indexResponse.message = message;
		return {
			ok: false,
			message,
			bootCheck
		};
	}
	const checked = /* @__PURE__ */ new Set();
	const pending = [...htmlAssets];
	let appJsUrl;
	while (pending.length > 0) {
		const assetUrl = pending.shift();
		if (!assetUrl || checked.has(assetUrl)) continue;
		checked.add(assetUrl);
		const asset = await fetchTextWithTimeout({
			url: assetUrl,
			timeoutMs: params.timeoutMs,
			headers: { accept: "*/*" }
		});
		if (!asset.ok) {
			const failedAsset = {
				url: assetUrl,
				ok: false,
				message: `${assetUrl} fetch failed: ${asset.message}`
			};
			bootCheck.assets.push(failedAsset);
			if (assetUrl === entryJsUrl) bootCheck.entryJs = failedAsset;
			if (assetUrl === appJsUrl) bootCheck.appJs = failedAsset;
			return {
				ok: false,
				message: failedAsset.message ?? "asset fetch failed",
				bootCheck
			};
		}
		const assetCheck = makeAssetCheck({
			url: assetUrl,
			status: asset.status,
			contentType: asset.contentType,
			text: asset.text
		});
		bootCheck.assets.push(assetCheck);
		if (assetUrl === entryJsUrl) bootCheck.entryJs = assetCheck;
		if (assetUrl === appJsUrl) bootCheck.appJs = assetCheck;
		if (!assetCheck.ok) return {
			ok: false,
			message: assetCheck.message ?? "asset validation failed",
			bootCheck
		};
		if (/\.js(?:[?#].*)?$/i.test(assetUrl)) {
			const dynamicUrls = extractDynamicImportUrls(asset.text, assetUrl);
			if (assetUrl === entryJsUrl && !appJsUrl) appJsUrl = dynamicUrls.find(isLikelyAppJsUrl) ?? dynamicUrls[0];
			for (const dynamicUrl of dynamicUrls) if (!checked.has(dynamicUrl)) pending.push(dynamicUrl);
		}
	}
	return {
		ok: true,
		bootCheck
	};
}
async function readLoginJson(response) {
	const text = await response.text().catch(() => "");
	if (!text.trim()) return {};
	try {
		return JSON.parse(text);
	} catch {
		return {
			ok: false,
			error: {
				code: "invalid_json",
				message: text.slice(0, 160)
			}
		};
	}
}
async function exchangeGatewayTokenForSession(params) {
	const controller = new AbortController();
	const timer = setTimeout(() => controller.abort(), params.timeoutMs);
	try {
		const response = await fetch(buildLoginUrl(params.httpUrl), {
			method: "POST",
			headers: {
				"content-type": "application/json",
				origin: buildOrigin(params.httpUrl)
			},
			cache: "no-store",
			body: JSON.stringify({ token: params.token }),
			signal: controller.signal
		});
		const body = await readLoginJson(response);
		if (!response.ok || body.ok !== true || !body.sessionToken?.trim()) return {
			ok: false,
			message: `${body.error?.code ? `${body.error.code}: ` : ""}${body.error?.message || `HTTP ${response.status}`}`
		};
		return {
			ok: true,
			sessionToken: body.sessionToken.trim()
		};
	} catch (err) {
		return {
			ok: false,
			message: sanitizeErrorMessage(err)
		};
	} finally {
		clearTimeout(timer);
	}
}
function summarizeGatewayConnectError(frame) {
	const error = frame && typeof frame === "object" && "error" in frame ? frame.error : void 0;
	if (!error || typeof error !== "object") return "connect rejected";
	const code = typeof error.code === "string" ? error.code : "connect_rejected";
	const message = typeof error.message === "string" ? error.message : "connect rejected";
	const details = error.details && typeof error.details === "object" ? error.details : null;
	return `${code}: ${message}${typeof details?.code === "string" ? ` (${details.code})` : ""}${typeof details?.authReason === "string" ? ` auth=${details.authReason}` : ""}`;
}
function probeHostedWs(params) {
	return new Promise((resolve) => {
		let settled = false;
		let connectSent = false;
		let ws = null;
		let connectNonce = "";
		const role = "operator";
		const scopes = [
			ADMIN_SCOPE,
			APPROVALS_SCOPE,
			PAIRING_SCOPE
		];
		const client = {
			id: GATEWAY_CLIENT_NAMES.CONTROL_UI,
			version: "dashboard-probe",
			platform: "node",
			mode: GATEWAY_CLIENT_MODES.UI
		};
		const deviceIdentity = createEphemeralDeviceIdentity();
		const finish = (result) => {
			if (settled) return;
			settled = true;
			clearTimeout(timer);
			try {
				ws?.close();
			} catch {}
			resolve(result);
		};
		const sendConnect = () => {
			if (!ws || ws.readyState !== WebSocket$1.OPEN || connectSent) return;
			connectSent = true;
			const signedAtMs = Date.now();
			const payload = buildDeviceAuthPayload({
				deviceId: deviceIdentity.deviceId,
				clientId: client.id,
				clientMode: client.mode,
				role,
				scopes,
				signedAtMs,
				token: params.sessionToken,
				nonce: connectNonce
			});
			ws.send(JSON.stringify({
				type: "req",
				id: "dashboard-browser-probe-connect",
				method: "connect",
				params: {
					minProtocol: PROTOCOL_VERSION,
					maxProtocol: PROTOCOL_VERSION,
					client,
					role,
					scopes,
					device: {
						id: deviceIdentity.deviceId,
						publicKey: publicKeyRawBase64UrlFromPem(deviceIdentity.publicKeyPem),
						signature: signDevicePayload(deviceIdentity.privateKeyPem, payload),
						signedAt: signedAtMs,
						nonce: connectNonce
					},
					caps: [],
					auth: { token: params.sessionToken },
					userAgent: "fased-dashboard-probe",
					locale: "en-US"
				}
			}));
		};
		const timer = setTimeout(() => {
			finish({
				ok: false,
				message: `timeout after ${params.timeoutMs}ms`
			});
		}, params.timeoutMs);
		try {
			ws = new WebSocket$1(params.wsUrl, {
				headers: { Origin: params.origin },
				handshakeTimeout: Math.max(1e3, Math.floor(params.timeoutMs)),
				maxPayload: 1024 * 1024
			});
		} catch (err) {
			finish({
				ok: false,
				message: sanitizeErrorMessage(err)
			});
			return;
		}
		ws.on("open", () => {});
		ws.on("message", (data) => {
			let parsed;
			try {
				parsed = JSON.parse(rawDataToString(data));
			} catch {
				return;
			}
			const frame = parsed;
			if (frame.type === "event" && frame.event === "connect.challenge") {
				const nonce = parsed && typeof parsed === "object" && "payload" in parsed ? parsed.payload?.nonce ?? "" : "";
				connectNonce = typeof nonce === "string" ? nonce : "";
				sendConnect();
				return;
			}
			if (frame.type !== "res" || frame.id !== "dashboard-browser-probe-connect") return;
			if (frame.ok === true) {
				finish({ ok: true });
				return;
			}
			finish({
				ok: false,
				message: summarizeGatewayConnectError(parsed)
			});
		});
		ws.on("error", (err) => {
			if (!settled) finish({
				ok: false,
				message: sanitizeErrorMessage(err)
			});
		});
		ws.on("close", (code, reason) => {
			if (!settled) {
				const reasonText = String(reason || "").trim();
				finish({
					ok: false,
					message: `closed before connect response (${code}${reasonText ? `: ${reasonText}` : ""})`
				});
			}
		});
	});
}
async function probeHostedDashboardBrowserPath(params) {
	const startedAt = Date.now();
	const timeoutMs = Math.max(1e3, Math.floor(params.timeoutMs ?? 6e3));
	const token = params.token.trim();
	const wsUrl = buildWsUrl(params.httpUrl);
	if (!token) return {
		ok: false,
		durationMs: Date.now() - startedAt,
		stage: "login",
		message: "missing gateway token",
		wsUrl
	};
	const login = await exchangeGatewayTokenForSession({
		httpUrl: params.httpUrl,
		token,
		timeoutMs
	});
	if (!login.ok) return {
		ok: false,
		durationMs: Date.now() - startedAt,
		stage: "login",
		message: login.message,
		wsUrl
	};
	const assets = await probeHostedControlUiAssets({
		httpUrl: params.httpUrl,
		sessionToken: login.sessionToken,
		timeoutMs
	});
	if (!assets.ok) return {
		ok: false,
		durationMs: Date.now() - startedAt,
		stage: "assets",
		message: assets.message,
		bootCheck: assets.bootCheck,
		wsUrl
	};
	const websocket = await probeHostedWs({
		wsUrl,
		origin: buildOrigin(params.httpUrl),
		sessionToken: login.sessionToken,
		timeoutMs
	});
	if (!websocket.ok) return {
		ok: false,
		durationMs: Date.now() - startedAt,
		stage: "websocket",
		message: websocket.message,
		bootCheck: assets.bootCheck,
		wsUrl
	};
	return {
		ok: true,
		durationMs: Date.now() - startedAt,
		bootCheck: assets.bootCheck,
		wsUrl
	};
}
async function waitForHostedDashboardBrowserPath(params) {
	const deadlineMs = Math.max(1, Math.floor(params.deadlineMs ?? 15e3));
	const probeTimeoutMs = Math.max(1, Math.floor(params.probeTimeoutMs ?? 6e3));
	const pollMs = Math.max(250, Math.floor(params.pollMs ?? 1e3));
	const startedAt = Date.now();
	let lastResult = null;
	while (Date.now() - startedAt < deadlineMs) {
		lastResult = await probeHostedDashboardBrowserPath({
			httpUrl: params.httpUrl,
			token: params.token,
			timeoutMs: probeTimeoutMs
		});
		if (lastResult.ok) return {
			...lastResult,
			durationMs: Date.now() - startedAt
		};
		await new Promise((resolve) => setTimeout(resolve, pollMs));
	}
	if (lastResult) return {
		...lastResult,
		durationMs: Date.now() - startedAt
	};
	return {
		ok: false,
		durationMs: Date.now() - startedAt,
		stage: "websocket",
		message: "timeout before first hosted dashboard probe completed"
	};
}

//#endregion
//#region src/commands/health.ts
const LOCAL_HEALTH_GATEWAY_TIMEOUT_MS = 3e4;
const HOSTED_HEALTH_GATEWAY_TIMEOUT_MS = 12e4;
const DEFAULT_TIMEOUT_MS = 1e4;
const debugHealth = (...args) => {
	if (isTruthyEnvValue(process.env.FASED_DEBUG_HEALTH)) console.warn("[health:debug]", ...args);
};
const formatDurationParts = (ms) => {
	if (!Number.isFinite(ms)) return "unknown";
	if (ms < 1e3) return `${Math.max(0, Math.round(ms))}ms`;
	const units = [
		{
			label: "w",
			size: 10080 * 60 * 1e3
		},
		{
			label: "d",
			size: 1440 * 60 * 1e3
		},
		{
			label: "h",
			size: 3600 * 1e3
		},
		{
			label: "m",
			size: 60 * 1e3
		},
		{
			label: "s",
			size: 1e3
		}
	];
	let remaining = Math.max(0, Math.floor(ms));
	const parts = [];
	for (const unit of units) {
		const value = Math.floor(remaining / unit.size);
		if (value > 0) {
			parts.push(`${value}${unit.label}`);
			remaining -= value * unit.size;
		}
	}
	if (parts.length === 0) return "0s";
	return parts.join(" ");
};
const resolveHeartbeatSummary = (cfg, agentId) => resolveHeartbeatSummaryForAgent(cfg, agentId);
async function runTailscaleServeStatus() {
	const tailscaleBin = await getTailscaleBinary();
	try {
		const { stdout } = await runExec(tailscaleBin, ["serve", "status"], {
			timeoutMs: 3e3,
			maxBuffer: 2e5
		});
		return {
			ok: true,
			stdout
		};
	} catch (err) {
		try {
			const { stdout } = await runExec("sudo", [
				"-n",
				tailscaleBin,
				"serve",
				"status"
			], {
				timeoutMs: 3e3,
				maxBuffer: 2e5
			});
			return {
				ok: true,
				stdout
			};
		} catch (sudoErr) {
			return {
				ok: false,
				detail: sudoErr instanceof Error ? sudoErr.message : err instanceof Error ? err.message : String(sudoErr || err)
			};
		}
	}
}
async function resolveHostedDashboardRouteStatus(cfg) {
	const mode = cfg.gateway?.tailscale?.mode;
	if (mode !== "serve" && mode !== "funnel") return null;
	const port = cfg.gateway?.port ?? 18789;
	const [status, host] = await Promise.all([runTailscaleServeStatus(), getTailnetHostname((cmd, args, opts) => runExec(cmd, args, opts)).catch(() => "")]);
	const url = host ? `https://${host}` : void 0;
	if (!status.ok) return {
		mode,
		ok: false,
		url,
		detail: `tailscale serve status unavailable (${status.detail})`
	};
	const stdout = status.stdout;
	const ready = stdout.includes(`127.0.0.1:${port}`) || stdout.includes(`localhost:${port}`) || stdout.includes(`:${port}`);
	return {
		mode,
		ok: ready,
		url,
		detail: ready ? `tailscale ${mode} routes to 127.0.0.1:${port}` : `tailscale ${mode} is not routing to 127.0.0.1:${port}`
	};
}
function isHostedHealthConfig(cfg) {
	const mode = cfg.gateway?.tailscale?.mode;
	return mode === "serve" || mode === "funnel";
}
function resolveHealthGatewayTimeoutMs(cfg) {
	return isHostedHealthConfig(cfg) ? HOSTED_HEALTH_GATEWAY_TIMEOUT_MS : LOCAL_HEALTH_GATEWAY_TIMEOUT_MS;
}
function getFirstErrorLine(err) {
	return (err instanceof Error ? err.message : String(err)).split("\n")[0]?.trim() || "unknown error";
}
function isGatewayWarmupTimeout(message) {
	return /gateway timeout|operation was aborted|aborted due to timeout/i.test(message);
}
function logHandledGatewayFailure(params) {
	const message = getFirstErrorLine(params.err);
	const hosted = isHostedHealthConfig(params.cfg);
	const warmup = hosted && isGatewayWarmupTimeout(message);
	if (params.json) {
		params.runtime.log(JSON.stringify({
			ok: false,
			gateway: warmup ? "warming" : "offline",
			error: message,
			hosted,
			timeoutMs: params.timeoutMs
		}, null, 2));
		return;
	}
	params.runtime.log(info(`Gateway: ${warmup ? "warming" : "offline"} (${message})`));
	if (warmup) params.runtime.log(info("Hosted Gateway is still warming after restart; retry health after it settles."));
	else params.runtime.log(info("The Gateway is not accepting health requests yet."));
	params.runtime.log(info(`Dashboard: ${formatCliCommand("fased dashboard --no-open")}`));
	if (params.verbose) {
		const details = buildGatewayConnectionDetails({ config: params.cfg });
		params.runtime.log(info("Gateway connection:"));
		for (const line of details.message.split("\n")) params.runtime.log(`  ${line}`);
	}
}
const resolveAgentOrder = (cfg) => {
	const defaultAgentId = resolveDefaultAgentId(cfg);
	const entries = Array.isArray(cfg.agents?.list) ? cfg.agents.list : [];
	const seen = /* @__PURE__ */ new Set();
	const ordered = [];
	for (const entry of entries) {
		if (!entry || typeof entry !== "object") continue;
		if (typeof entry.id !== "string" || !entry.id.trim()) continue;
		const id = normalizeAgentId(entry.id);
		if (!id || seen.has(id)) continue;
		seen.add(id);
		ordered.push({
			id,
			name: typeof entry.name === "string" ? entry.name : void 0
		});
	}
	if (!seen.has(defaultAgentId)) ordered.unshift({ id: defaultAgentId });
	if (ordered.length === 0) ordered.push({ id: defaultAgentId });
	return {
		defaultAgentId,
		ordered
	};
};
const buildSessionSummary = (storePath) => {
	const store = loadSessionStore(storePath);
	const sessions = Object.entries(store).filter(([key]) => key !== "global" && key !== "unknown").map(([key, entry]) => ({
		key,
		updatedAt: entry?.updatedAt ?? 0
	})).toSorted((a, b) => b.updatedAt - a.updatedAt);
	const recent = sessions.slice(0, 5).map((s) => ({
		key: s.key,
		updatedAt: s.updatedAt || null,
		age: s.updatedAt ? Date.now() - s.updatedAt : null
	}));
	return {
		path: storePath,
		count: sessions.length,
		recent
	};
};
function getGatewayLivenessHealthSnapshot() {
	const start = Date.now();
	const cfg = loadConfig();
	const { defaultAgentId, ordered } = resolveAgentOrder(cfg);
	const agents = ordered.map((entry) => {
		const storePath = resolveStorePath(cfg.session?.store, { agentId: entry.id });
		return {
			agentId: entry.id,
			name: entry.name,
			isDefault: entry.id === defaultAgentId,
			heartbeat: resolveHeartbeatSummary(cfg, entry.id),
			sessions: buildSessionSummary(storePath)
		};
	});
	const defaultAgent = agents.find((agent) => agent.isDefault) ?? agents[0];
	const sessions = defaultAgent?.sessions ?? buildSessionSummary(resolveStorePath(cfg.session?.store, { agentId: defaultAgentId }));
	const channelOrder = listChannelPlugins().map((plugin) => plugin.id);
	const channelLabels = Object.fromEntries(listChannelPlugins().map((plugin) => [plugin.id, plugin.meta.label ?? plugin.id]));
	return {
		ok: true,
		ts: Date.now(),
		durationMs: Date.now() - start,
		channels: {},
		channelOrder,
		channelLabels,
		heartbeatSeconds: defaultAgent?.heartbeat.everyMs ? Math.round(defaultAgent.heartbeat.everyMs / 1e3) : 0,
		defaultAgentId,
		agents,
		sessions,
		startup: buildGatewayStartupReadinessSnapshot(getLastGatewayStartupTraceSnapshot())
	};
}
const isAccountEnabled = (account) => {
	if (!account || typeof account !== "object") return true;
	return account.enabled !== false;
};
const asRecord = (value) => value && typeof value === "object" ? value : null;
const formatProbeLine = (probe, opts = {}) => {
	const record = asRecord(probe);
	if (!record) return null;
	const ok = typeof record.ok === "boolean" ? record.ok : void 0;
	if (ok === void 0) return null;
	const elapsedMs = typeof record.elapsedMs === "number" ? record.elapsedMs : null;
	const status = typeof record.status === "number" ? record.status : null;
	const error = typeof record.error === "string" ? record.error : null;
	const bot = asRecord(record.bot);
	const botUsername = bot && typeof bot.username === "string" ? bot.username : null;
	const webhook = asRecord(record.webhook);
	const webhookUrl = webhook && typeof webhook.url === "string" ? webhook.url : null;
	const usernames = /* @__PURE__ */ new Set();
	if (botUsername) usernames.add(botUsername);
	for (const extra of opts.botUsernames ?? []) if (extra) usernames.add(extra);
	if (ok) {
		let label = "ok";
		if (usernames.size > 0) label += ` (@${Array.from(usernames).join(", @")})`;
		if (elapsedMs != null) label += ` (${elapsedMs}ms)`;
		if (webhookUrl) label += ` - webhook ${webhookUrl}`;
		return label;
	}
	let label = `failed (${status ?? "unknown"})`;
	if (error) label += ` - ${error}`;
	return label;
};
const formatAccountProbeTiming = (summary) => {
	const probe = asRecord(summary.probe);
	if (!probe) return null;
	const elapsedMs = typeof probe.elapsedMs === "number" ? Math.round(probe.elapsedMs) : null;
	const ok = typeof probe.ok === "boolean" ? probe.ok : null;
	if (elapsedMs == null && ok !== true) return null;
	const accountId = summary.accountId || "default";
	const botRecord = asRecord(probe.bot);
	const botUsername = botRecord && typeof botRecord.username === "string" ? botRecord.username : null;
	return `${botUsername ? `@${botUsername}` : accountId}:${accountId}:${elapsedMs != null ? `${elapsedMs}ms` : "ok"}`;
};
const isProbeFailure = (summary) => {
	const probe = asRecord(summary.probe);
	if (!probe) return false;
	return (typeof probe.ok === "boolean" ? probe.ok : null) === false;
};
const formatHealthChannelLines = (summary, opts = {}) => {
	const channels = summary.channels ?? {};
	const channelOrder = summary.channelOrder?.length > 0 ? summary.channelOrder : Object.keys(channels);
	const accountMode = opts.accountMode ?? "default";
	const includeInactive = opts.includeInactive ?? true;
	const lines = [];
	for (const channelId of channelOrder) {
		const channelSummary = channels[channelId];
		if (!channelSummary) continue;
		const plugin = getChannelPlugin(channelId);
		const label = summary.channelLabels?.[channelId] ?? plugin?.meta.label ?? channelId;
		const accountSummaries = channelSummary.accounts ?? {};
		const accountIds = opts.accountIdsByChannel?.[channelId];
		const filteredSummaries = accountIds && accountIds.length > 0 ? accountIds.map((accountId) => accountSummaries[accountId]).filter((entry) => Boolean(entry)) : void 0;
		const listSummaries = accountMode === "all" ? Object.values(accountSummaries) : filteredSummaries ?? (channelSummary.accounts ? Object.values(accountSummaries) : []);
		const baseSummary = filteredSummaries && filteredSummaries.length > 0 ? filteredSummaries[0] : channelSummary;
		const botUsernames = listSummaries ? listSummaries.map((account) => {
			const probeRecord = asRecord(account.probe);
			const bot = probeRecord ? asRecord(probeRecord.bot) : null;
			return bot && typeof bot.username === "string" ? bot.username : null;
		}).filter((value) => Boolean(value)) : [];
		const linked = typeof baseSummary.linked === "boolean" ? baseSummary.linked : null;
		if (linked !== null) {
			if (linked) {
				const authAgeMs = typeof baseSummary.authAgeMs === "number" ? baseSummary.authAgeMs : null;
				const authLabel = authAgeMs != null ? ` (auth age ${Math.round(authAgeMs / 6e4)}m)` : "";
				lines.push(`${label}: linked${authLabel}`);
			} else if (includeInactive) lines.push(`${label}: not linked`);
			continue;
		}
		const configured = typeof baseSummary.configured === "boolean" ? baseSummary.configured : null;
		if (configured === false) {
			if (includeInactive) lines.push(`${label}: not configured`);
			continue;
		}
		const accountTimings = accountMode === "all" ? listSummaries.map((account) => formatAccountProbeTiming(account)).filter((value) => Boolean(value)) : [];
		const failedSummary = listSummaries.find((summary) => isProbeFailure(summary));
		if (failedSummary) {
			const failureLine = formatProbeLine(failedSummary.probe, { botUsernames });
			if (failureLine) {
				lines.push(`${label}: ${failureLine}`);
				continue;
			}
		}
		if (accountTimings.length > 0) {
			lines.push(`${label}: ok (${accountTimings.join(", ")})`);
			continue;
		}
		const probeLine = formatProbeLine(baseSummary.probe, { botUsernames });
		if (probeLine) {
			lines.push(`${label}: ${probeLine}`);
			continue;
		}
		if (configured === true) {
			lines.push(`${label}: configured`);
			continue;
		}
		if (includeInactive) lines.push(`${label}: unknown`);
	}
	return lines;
};
async function getHealthSnapshot(params) {
	const timeoutMs = params?.timeoutMs;
	const cfg = loadConfig();
	const { defaultAgentId, ordered } = resolveAgentOrder(cfg);
	const channelBindings = buildChannelAccountBindings(cfg);
	const sessionCache = /* @__PURE__ */ new Map();
	const agents = ordered.map((entry) => {
		const storePath = resolveStorePath(cfg.session?.store, { agentId: entry.id });
		const sessions = sessionCache.get(storePath) ?? buildSessionSummary(storePath);
		sessionCache.set(storePath, sessions);
		return {
			agentId: entry.id,
			name: entry.name,
			isDefault: entry.id === defaultAgentId,
			heartbeat: resolveHeartbeatSummary(cfg, entry.id),
			sessions
		};
	});
	const defaultAgent = agents.find((agent) => agent.isDefault) ?? agents[0];
	const heartbeatSeconds = defaultAgent?.heartbeat.everyMs ? Math.round(defaultAgent.heartbeat.everyMs / 1e3) : 0;
	const sessions = defaultAgent?.sessions ?? buildSessionSummary(resolveStorePath(cfg.session?.store, { agentId: defaultAgentId }));
	const start = Date.now();
	const cappedTimeout = timeoutMs === void 0 ? DEFAULT_TIMEOUT_MS : Math.max(50, timeoutMs);
	const doProbe = params?.probe !== false;
	const includeSensitive = params?.includeSensitive !== false;
	const channels = {};
	const channelOrder = listChannelPlugins().map((plugin) => plugin.id);
	const channelLabels = {};
	for (const plugin of listChannelPlugins()) {
		channelLabels[plugin.id] = plugin.meta.label ?? plugin.id;
		const accountIds = plugin.config.listAccountIds(cfg);
		const defaultAccountId = resolveChannelDefaultAccountId({
			plugin,
			cfg,
			accountIds
		});
		const boundAccounts = channelBindings.get(plugin.id)?.get(defaultAgentId) ?? [];
		const preferredAccountId = resolvePreferredAccountId({
			accountIds,
			defaultAccountId,
			boundAccounts
		});
		const boundAccountIdsAll = Array.from(new Set(Array.from(channelBindings.get(plugin.id)?.values() ?? []).flatMap((ids) => ids)));
		const accountIdsToProbe = Array.from(new Set([
			preferredAccountId,
			defaultAccountId,
			...accountIds,
			...boundAccountIdsAll
		].filter((value) => value && value.trim())));
		debugHealth("channel", {
			id: plugin.id,
			accountIds,
			defaultAccountId,
			boundAccounts,
			preferredAccountId,
			accountIdsToProbe
		});
		const accountSummaries = {};
		for (const accountId of accountIdsToProbe) {
			const account = plugin.config.resolveAccount(cfg, accountId);
			const enabled = plugin.config.isEnabled ? plugin.config.isEnabled(account, cfg) : isAccountEnabled(account);
			const configured = plugin.config.isConfigured ? await plugin.config.isConfigured(account, cfg) : true;
			let probe;
			let lastProbeAt = null;
			if (enabled && configured && doProbe && plugin.status?.probeAccount) try {
				probe = await plugin.status.probeAccount({
					account,
					timeoutMs: cappedTimeout,
					cfg
				});
				lastProbeAt = Date.now();
			} catch (err) {
				probe = {
					ok: false,
					error: formatErrorMessage(err)
				};
				lastProbeAt = Date.now();
			}
			const probeRecord = probe && typeof probe === "object" ? probe : null;
			const bot = probeRecord && typeof probeRecord.bot === "object" ? probeRecord.bot : null;
			if (bot?.username) debugHealth("probe.bot", {
				channel: plugin.id,
				accountId,
				username: bot.username
			});
			const snapshot = {
				...projectSafeChannelAccountSnapshotFields(params?.runtimeSnapshot?.channelAccounts[plugin.id]?.[accountId] ?? (accountId === defaultAccountId ? params?.runtimeSnapshot?.channels[plugin.id] : void 0)),
				accountId,
				enabled,
				configured
			};
			if (includeSensitive && probe !== void 0) snapshot.probe = probe;
			if (lastProbeAt) snapshot.lastProbeAt = lastProbeAt;
			const summary = plugin.status?.buildChannelSummary ? await plugin.status.buildChannelSummary({
				account,
				cfg,
				defaultAccountId: accountId,
				snapshot
			}) : void 0;
			const record = summary && typeof summary === "object" ? {
				...snapshot,
				...summary
			} : {
				...snapshot,
				accountId,
				configured
			};
			if (record.configured === void 0) record.configured = configured;
			if (includeSensitive && record.probe === void 0 && probe !== void 0) record.probe = probe;
			if (!includeSensitive) delete record.probe;
			if (record.lastProbeAt === void 0 && lastProbeAt) record.lastProbeAt = lastProbeAt;
			record.accountId = accountId;
			accountSummaries[accountId] = record;
		}
		const fallbackSummary = accountSummaries[preferredAccountId] ?? accountSummaries[defaultAccountId] ?? accountSummaries[accountIdsToProbe[0] ?? preferredAccountId] ?? accountSummaries[Object.keys(accountSummaries)[0]];
		if (fallbackSummary) channels[plugin.id] = {
			...fallbackSummary,
			accounts: accountSummaries
		};
	}
	return {
		ok: true,
		ts: Date.now(),
		durationMs: Date.now() - start,
		channels,
		channelOrder,
		channelLabels,
		heartbeatSeconds,
		defaultAgentId,
		agents,
		sessions: {
			path: sessions.path,
			count: sessions.count,
			recent: sessions.recent
		},
		startup: buildGatewayStartupReadinessSnapshot(getLastGatewayStartupTraceSnapshot())
	};
}
async function healthCommand(opts, runtime) {
	const cfg = opts.config ?? loadConfig();
	const gatewayTimeoutMs = opts.timeoutMs ?? resolveHealthGatewayTimeoutMs(cfg);
	let summary;
	try {
		summary = await withProgress({
			label: "Checking gateway health…",
			indeterminate: true,
			enabled: opts.json !== true
		}, async () => await callGateway({
			method: "health",
			params: opts.verbose ? { probe: true } : void 0,
			timeoutMs: gatewayTimeoutMs,
			config: cfg
		}));
	} catch (err) {
		if (opts.gatewayFailureMode !== "log-and-exit") throw err;
		logHandledGatewayFailure({
			runtime,
			cfg,
			err,
			json: opts.json,
			verbose: opts.verbose,
			timeoutMs: gatewayTimeoutMs
		});
		runtime.exit(1);
		return;
	}
	if (opts.json) runtime.log(JSON.stringify(summary, null, 2));
	else {
		const debugEnabled = isTruthyEnvValue(process.env.FASED_DEBUG_HEALTH);
		const rich = isRich();
		runtime.log(info(`Gateway: online (${Math.max(0, Math.round(summary.durationMs ?? 0))}ms)`));
		runtime.log(info(`Dashboard: ${formatCliCommand("fased dashboard")}`));
		const hostedRouteStatus = await resolveHostedDashboardRouteStatus(cfg);
		if (hostedRouteStatus) {
			const routeLabel = hostedRouteStatus.url ? ` ${hostedRouteStatus.url}` : "";
			runtime.log(info(hostedRouteStatus.ok ? `Dashboard route: online via Tailscale ${hostedRouteStatus.mode}${routeLabel}` : `Dashboard route: not verified via Tailscale ${hostedRouteStatus.mode} (${hostedRouteStatus.detail})`));
			const token = cfg.gateway?.auth?.token ?? process.env.FASED_GATEWAY_TOKEN ?? "";
			if (hostedRouteStatus.ok && hostedRouteStatus.url && token.trim() && opts.includeHostedBrowserPath !== false) {
				const hostedProbe = await probeHostedDashboardBrowserPath({
					httpUrl: hostedRouteStatus.url,
					token,
					timeoutMs: gatewayTimeoutMs
				});
				runtime.log(info(hostedProbe.ok ? `Dashboard browser path: online via Tailscale (${Math.max(0, Math.round(hostedProbe.durationMs))}ms)` : `Dashboard browser path: offline via Tailscale (${hostedProbe.stage}: ${hostedProbe.message})`));
			} else if (hostedRouteStatus.ok && hostedRouteStatus.url && !token.trim()) runtime.log(info("Dashboard browser path: not checked (missing gateway token)"));
			else if (hostedRouteStatus.ok && hostedRouteStatus.url) runtime.log(info("Dashboard browser path: not checked"));
		}
		if (opts.verbose) {
			const details = buildGatewayConnectionDetails({ config: cfg });
			runtime.log(info("Gateway connection:"));
			for (const line of details.message.split("\n")) runtime.log(`  ${line}`);
		}
		const localAgents = resolveAgentOrder(cfg);
		const defaultAgentId = summary.defaultAgentId ?? localAgents.defaultAgentId;
		const agents = Array.isArray(summary.agents) ? summary.agents : [];
		const fallbackAgents = localAgents.ordered.map((entry) => {
			const storePath = resolveStorePath(cfg.session?.store, { agentId: entry.id });
			return {
				agentId: entry.id,
				name: entry.name,
				isDefault: entry.id === localAgents.defaultAgentId,
				heartbeat: resolveHeartbeatSummary(cfg, entry.id),
				sessions: buildSessionSummary(storePath)
			};
		});
		const resolvedAgents = agents.length > 0 ? agents : fallbackAgents;
		const displayAgents = opts.verbose ? resolvedAgents : resolvedAgents.filter((agent) => agent.agentId === defaultAgentId);
		const channelBindings = buildChannelAccountBindings(cfg);
		if (debugEnabled) {
			runtime.log(info("[debug] local channel accounts"));
			for (const plugin of listChannelPlugins()) {
				const accountIds = plugin.config.listAccountIds(cfg);
				const defaultAccountId = resolveChannelDefaultAccountId({
					plugin,
					cfg,
					accountIds
				});
				runtime.log(`  ${plugin.id}: accounts=${accountIds.join(", ") || "(none)"} default=${defaultAccountId}`);
				for (const accountId of accountIds) {
					const account = plugin.config.resolveAccount(cfg, accountId);
					const record = asRecord(account);
					const tokenSource = record && typeof record.tokenSource === "string" ? record.tokenSource : void 0;
					const configured = plugin.config.isConfigured ? await plugin.config.isConfigured(account, cfg) : true;
					runtime.log(`    - ${accountId}: configured=${configured}${tokenSource ? ` tokenSource=${tokenSource}` : ""}`);
				}
			}
			runtime.log(info("[debug] bindings map"));
			for (const [channelId, byAgent] of channelBindings.entries()) {
				const entries = Array.from(byAgent.entries()).map(([agentId, ids]) => `${agentId}=[${ids.join(", ")}]`);
				runtime.log(`  ${channelId}: ${entries.join(" ")}`);
			}
			runtime.log(info("[debug] gateway channel probes"));
			for (const [channelId, channelSummary] of Object.entries(summary.channels ?? {})) {
				const accounts = channelSummary.accounts ?? {};
				const probes = Object.entries(accounts).map(([accountId, accountSummary]) => {
					const probe = asRecord(accountSummary.probe);
					const bot = probe ? asRecord(probe.bot) : null;
					return `${accountId}=${(bot && typeof bot.username === "string" ? bot.username : null) ?? "(no bot)"}`;
				});
				runtime.log(`  ${channelId}: ${probes.join(", ") || "(none)"}`);
			}
		}
		const channelAccountFallbacks = Object.fromEntries(listChannelPlugins().map((plugin) => {
			const accountIds = plugin.config.listAccountIds(cfg);
			const preferred = resolvePreferredAccountId({
				accountIds,
				defaultAccountId: resolveChannelDefaultAccountId({
					plugin,
					cfg,
					accountIds
				}),
				boundAccounts: channelBindings.get(plugin.id)?.get(defaultAgentId) ?? []
			});
			return [plugin.id, [preferred]];
		}));
		const accountIdsByChannel = (() => {
			const entries = displayAgents.length > 0 ? displayAgents : resolvedAgents;
			const byChannel = {};
			for (const [channelId, byAgent] of channelBindings.entries()) {
				const accountIds = [];
				for (const agent of entries) {
					const ids = byAgent.get(agent.agentId) ?? [];
					for (const id of ids) if (!accountIds.includes(id)) accountIds.push(id);
				}
				if (accountIds.length > 0) byChannel[channelId] = accountIds;
			}
			for (const [channelId, fallbackIds] of Object.entries(channelAccountFallbacks)) if (!byChannel[channelId] || byChannel[channelId].length === 0) byChannel[channelId] = fallbackIds;
			return byChannel;
		})();
		const channelLines = Object.keys(accountIdsByChannel).length > 0 ? formatHealthChannelLines(summary, {
			accountMode: opts.verbose ? "all" : "default",
			accountIdsByChannel,
			includeInactive: Boolean(opts.verbose)
		}) : formatHealthChannelLines(summary, {
			accountMode: opts.verbose ? "all" : "default",
			includeInactive: Boolean(opts.verbose)
		});
		if (channelLines.length > 0) for (const line of channelLines) runtime.log(styleHealthChannelLine(line, rich));
		else if (!opts.verbose) runtime.log(info("Channels: none connected yet (optional)."));
		for (const plugin of listChannelPlugins()) {
			const channelSummary = summary.channels?.[plugin.id];
			if (!channelSummary || channelSummary.linked !== true) continue;
			if (!plugin.status?.logSelfId) continue;
			const boundAccounts = channelBindings.get(plugin.id)?.get(defaultAgentId) ?? [];
			const accountIds = plugin.config.listAccountIds(cfg);
			const accountId = resolvePreferredAccountId({
				accountIds,
				defaultAccountId: resolveChannelDefaultAccountId({
					plugin,
					cfg,
					accountIds
				}),
				boundAccounts
			});
			const account = plugin.config.resolveAccount(cfg, accountId);
			plugin.status.logSelfId({
				account,
				cfg,
				runtime,
				includeChannelPrefix: true
			});
		}
		if (resolvedAgents.length > 0) {
			const agentLabels = resolvedAgents.map((agent) => agent.isDefault ? `${agent.agentId} (default)` : agent.agentId);
			runtime.log(info(`Agents: ${agentLabels.join(", ")}`));
		}
		const heartbeatParts = displayAgents.map((agent) => {
			const everyMs = agent.heartbeat?.everyMs;
			return `${everyMs ? formatDurationParts(everyMs) : "disabled"} (${agent.agentId})`;
		}).filter(Boolean);
		if (heartbeatParts.length > 0) runtime.log(info(`Heartbeat interval: ${heartbeatParts.join(", ")}`));
		if (displayAgents.length === 0) {
			runtime.log(info(`Session store: ${summary.sessions.path} (${summary.sessions.count} entries)`));
			if (summary.sessions.recent.length > 0) for (const r of summary.sessions.recent) runtime.log(`- ${r.key} (${r.updatedAt ? `${Math.round((Date.now() - r.updatedAt) / 6e4)}m ago` : "no activity"})`);
		} else for (const agent of displayAgents) {
			runtime.log(info(`Session store (${agent.agentId}): ${agent.sessions.path} (${agent.sessions.count} entries)`));
			if (agent.sessions.recent.length > 0) for (const r of agent.sessions.recent) runtime.log(`- ${r.key} (${r.updatedAt ? `${Math.round((Date.now() - r.updatedAt) / 6e4)}m ago` : "no activity"})`);
		}
	}
}

//#endregion
export { probeHostedDashboardBrowserPath as a, createGatewayStartupTrace as c, healthCommand as i, getLastGatewayStartupTraceSnapshot as l, getGatewayLivenessHealthSnapshot as n, waitForHostedDashboardBrowserPath as o, getHealthSnapshot as r, styleHealthChannelLine as s, formatHealthChannelLines as t };