import { c as normalizeOptionalString } from "./string-coerce-Bje8XVt9.js";
import { i as formatErrorMessage } from "./errors-CSrrtEb1.js";
import { t as formatDocsLink } from "./links-BszRQhGa.js";
import { t as formatCliCommand } from "./command-format-BORwwHyH.js";
import { t as sanitizeTerminalText } from "./safe-text-BuwPGyoq.js";
import { n as CHAT_CHANNEL_ORDER } from "./ids-21ROHvEC.js";
import { n as normalizeAccountId, t as DEFAULT_ACCOUNT_ID } from "./account-id-CRE2SEcy.js";
import { S as resolveDefaultAgentId, x as resolveAgentWorkspaceDir } from "./agent-scope-DYPLTPla.js";
import { d as listBundledChannelSetupPlugins, i as getBundledChannelSetupPlugin } from "./bundled-CA22YdiE.js";
import { c as listChatChannels, n as formatChannelSelectionLine, t as formatChannelPrimerLine } from "./registry-BHAtUOc3.js";
import { t as isChannelConfigured } from "./channel-configured-Cy9S1B1O.js";
import { s as getActivePluginRegistryVersion, t as getActivePluginChannelRegistry, v as requireActivePluginRegistry } from "./runtime-DqdsAeuz.js";
import { i as resolveChannelDefaultAccountId } from "./helpers-D5_OgjA3.js";
import { t as buildChannelSetupWizardAdapterFromSetupWizard } from "./setup-wizard-DdoVfkhd.js";
import { t as enablePluginInConfig } from "./enable-D-okhheq.js";
import { n as loadChannelSetupPluginRegistrySnapshotForChannel, t as ensureChannelSetupPluginInstalled } from "./plugin-install-DAL3FRyi.js";
import { r as listTrustedChannelPluginCatalogEntries } from "./trusted-catalog-BbMMVYkc.js";
import { i as shouldShowChannelInSetup, r as resolveChannelSetupEntries } from "./discovery-DkZdukc7.js";
//#region src/channels/plugins/setup-registry.ts
let cachedChannelSetupPlugins = {
	registryVersion: -1,
	registryRef: null,
	sorted: [],
	byId: /* @__PURE__ */ new Map()
};
function dedupeSetupPlugins(plugins) {
	const seen = /* @__PURE__ */ new Set();
	const resolved = [];
	for (const plugin of plugins) {
		const id = normalizeOptionalString(plugin.id) ?? "";
		if (!id || seen.has(id)) continue;
		seen.add(id);
		resolved.push(plugin);
	}
	return resolved;
}
function sortChannelSetupPlugins(plugins) {
	return dedupeSetupPlugins(plugins).toSorted((a, b) => {
		const indexA = CHAT_CHANNEL_ORDER.indexOf(a.id);
		const indexB = CHAT_CHANNEL_ORDER.indexOf(b.id);
		const orderA = a.meta.order ?? (indexA === -1 ? 999 : indexA);
		const orderB = b.meta.order ?? (indexB === -1 ? 999 : indexB);
		if (orderA !== orderB) return orderA - orderB;
		return a.id.localeCompare(b.id);
	});
}
function resolveCachedChannelSetupPlugins() {
	const registry = requireActivePluginRegistry();
	const registryVersion = getActivePluginRegistryVersion();
	const cached = cachedChannelSetupPlugins;
	if (cached.registryVersion === registryVersion && cached.registryRef === registry) return cached;
	const registryPlugins = (registry.channelSetups ?? []).map((entry) => entry.plugin);
	const sorted = sortChannelSetupPlugins(registryPlugins.length > 0 ? registryPlugins : listBundledChannelSetupPlugins());
	const byId = /* @__PURE__ */ new Map();
	for (const plugin of sorted) byId.set(plugin.id, plugin);
	const next = {
		registryVersion,
		registryRef: registry,
		sorted,
		byId
	};
	cachedChannelSetupPlugins = next;
	return next;
}
function listChannelSetupPlugins() {
	return resolveCachedChannelSetupPlugins().sorted.slice();
}
function listActiveChannelSetupPlugins() {
	return sortChannelSetupPlugins((getActivePluginChannelRegistry()?.channelSetups ?? []).map((entry) => entry.plugin));
}
function getChannelSetupPlugin(id) {
	const resolvedId = normalizeOptionalString(id) ?? "";
	if (!resolvedId) return;
	return resolveCachedChannelSetupPlugins().byId.get(resolvedId);
}
//#endregion
//#region src/commands/channel-setup/registry.ts
const setupWizardAdapters = /* @__PURE__ */ new WeakMap();
function isChannelSetupWizardAdapter(setupWizard) {
	return Boolean(setupWizard && typeof setupWizard === "object" && "getStatus" in setupWizard && typeof setupWizard.getStatus === "function" && "configure" in setupWizard && typeof setupWizard.configure === "function");
}
function isDeclarativeChannelSetupWizard(setupWizard) {
	return Boolean(setupWizard && typeof setupWizard === "object" && "status" in setupWizard && "credentials" in setupWizard);
}
function resolveChannelSetupWizardAdapterForPlugin(plugin) {
	if (!plugin) return;
	const { setupWizard } = plugin;
	if (isChannelSetupWizardAdapter(setupWizard)) return setupWizard;
	if (isDeclarativeChannelSetupWizard(setupWizard)) {
		const cached = setupWizardAdapters.get(plugin);
		if (cached) return cached;
		const adapter = buildChannelSetupWizardAdapterFromSetupWizard({
			plugin,
			wizard: setupWizard
		});
		setupWizardAdapters.set(plugin, adapter);
		return adapter;
	}
}
//#endregion
//#region src/flows/channel-setup.prompts.ts
function formatAccountLabel(accountId) {
	return accountId === "default" ? "默认 (主账户)" : accountId;
}
async function promptConfiguredAction(params) {
	const { prompter, label, supportsDisable, supportsDelete } = params;
	const options = [
		{
			value: "update",
			label: "Modify settings"
		},
		...supportsDisable ? [{
			value: "disable",
			label: "Disable (keeps config)"
		}] : [],
		...supportsDelete ? [{
			value: "delete",
			label: "Delete config"
		}] : [],
		{
			value: "skip",
			label: "Skip (leave as-is)"
		}
	];
	return await prompter.select({
		message: `${label} already configured. What do you want to do?`,
		options,
		initialValue: "update"
	});
}
async function promptRemovalAccountId(params) {
	const { cfg, prompter, label, channel } = params;
	const plugin = params.plugin ?? getChannelSetupPlugin(channel);
	if (!plugin) return DEFAULT_ACCOUNT_ID;
	const accountIds = plugin.config.listAccountIds(cfg).filter(Boolean);
	const defaultAccountId = resolveChannelDefaultAccountId({
		plugin,
		cfg,
		accountIds
	});
	if (accountIds.length <= 1) return defaultAccountId;
	return normalizeAccountId(await prompter.select({
		message: `${label} account`,
		options: accountIds.map((accountId) => ({
			value: accountId,
			label: formatAccountLabel(accountId)
		})),
		initialValue: defaultAccountId
	})) ?? defaultAccountId;
}
async function maybeConfigureDmPolicies(params) {
	const { selection, prompter, accountIdsByChannel } = params;
	const resolve = params.resolveAdapter ?? (() => void 0);
	const dmPolicies = selection.map((channel) => resolve(channel)?.dmPolicy).filter(Boolean);
	if (dmPolicies.length === 0) return params.cfg;
	if (!await prompter.confirm({
		message: "Configure DM access policies now? (default: pairing)",
		initialValue: false
	})) return params.cfg;
	let cfg = params.cfg;
	for (const policy of dmPolicies) {
		const accountId = accountIdsByChannel?.get(policy.channel);
		const { policyKey, allowFromKey } = policy.resolveConfigKeys?.(cfg, accountId) ?? {
			policyKey: policy.policyKey,
			allowFromKey: policy.allowFromKey
		};
		await prompter.note([
			"Default: pairing (unknown DMs get a pairing code).",
			`Approve: ${formatCliCommand(`openclaw pairing approve ${policy.channel} <code>`)}`,
			`Allowlist DMs: ${policyKey}="allowlist" + ${allowFromKey} entries.`,
			`Public DMs: ${policyKey}="open" + ${allowFromKey} includes "*".`,
			"Multi-user DMs: run: " + formatCliCommand("openclaw config set session.dmScope \"per-channel-peer\"") + " (or \"per-account-channel-peer\" for multi-account channels) to isolate sessions.",
			`Docs: ${formatDocsLink("/channels/pairing", "channels/pairing")}`
		].join("\n"), `${policy.label} DM access`);
		const nextPolicy = await prompter.select({
			message: `${policy.label} DM policy`,
			options: [
				{
					value: "pairing",
					label: "Pairing (recommended)"
				},
				{
					value: "allowlist",
					label: "Allowlist (specific users only)"
				},
				{
					value: "open",
					label: "Open (public inbound DMs)"
				},
				{
					value: "已禁用",
					label: "Disabled (ignore DMs)"
				}
			]
		});
		if (nextPolicy !== policy.getCurrent(cfg, accountId)) cfg = policy.setPolicy(cfg, nextPolicy, accountId);
		if (nextPolicy === "allowlist" && policy.promptAllowFrom) cfg = await policy.promptAllowFrom({
			cfg,
			prompter,
			accountId
		});
	}
	return cfg;
}
//#endregion
//#region src/flows/channel-setup.status.ts
function buildChannelSetupSelectionContribution(params) {
	return {
		id: `channel:setup:${params.channel}`,
		kind: "channel",
		surface: "setup",
		channel: params.channel,
		option: {
			value: params.channel,
			label: params.label,
			...params.hint ? { hint: params.hint } : {}
		},
		source: params.source
	};
}
function formatSetupSelectionLabel(label, fallback) {
	return sanitizeTerminalText(label).trim() || sanitizeTerminalText(fallback).trim() || "<invalid channel>";
}
function formatSetupSelectionHint(hint) {
	if (!hint) return;
	return sanitizeTerminalText(hint) || void 0;
}
function formatSetupDisplayText(value, fallback = "") {
	return sanitizeTerminalText(value ?? "").trim() || sanitizeTerminalText(fallback).trim() || "<invalid channel>";
}
function formatSetupFreeText(value) {
	return sanitizeTerminalText(value ?? "").trim();
}
function formatSetupOptionalDisplayText(value) {
	return sanitizeTerminalText(value ?? "").trim() || void 0;
}
function formatSetupDisplayList(values) {
	const safe = (values ?? []).flatMap((value) => {
		const sanitized = formatSetupOptionalDisplayText(value);
		return sanitized ? [sanitized] : [];
	});
	return safe.length > 0 ? safe : void 0;
}
function formatSetupDisplayMeta(meta) {
	const safeId = formatSetupDisplayText(meta.id, "<invalid channel>");
	const safeLabel = formatSetupDisplayText(meta.label, safeId);
	const safeSelectionDocsPrefix = formatSetupOptionalDisplayText(meta.selectionDocsPrefix);
	const safeSelectionExtras = formatSetupDisplayList(meta.selectionExtras);
	return {
		...meta,
		id: safeId,
		label: safeLabel,
		selectionLabel: formatSetupDisplayText(meta.selectionLabel, safeLabel),
		docsPath: formatSetupDisplayText(meta.docsPath, "/"),
		...meta.docsLabel ? { docsLabel: formatSetupDisplayText(meta.docsLabel, safeId) } : {},
		blurb: formatSetupFreeText(meta.blurb),
		...safeSelectionDocsPrefix ? { selectionDocsPrefix: safeSelectionDocsPrefix } : {},
		...safeSelectionExtras ? { selectionExtras: safeSelectionExtras } : {}
	};
}
async function collectChannelStatus(params) {
	const installedPlugins = params.installedPlugins ?? listChannelSetupPlugins();
	const workspaceDir = resolveAgentWorkspaceDir(params.cfg, resolveDefaultAgentId(params.cfg));
	const { installedCatalogEntries, installableCatalogEntries } = resolveChannelSetupEntries({
		cfg: params.cfg,
		installedPlugins,
		workspaceDir
	});
	const resolveAdapter = params.resolveAdapter ?? ((channel) => resolveChannelSetupWizardAdapterForPlugin(installedPlugins.find((plugin) => plugin.id === channel)));
	const statusEntries = await Promise.all(installedPlugins.flatMap((plugin) => {
		if (!shouldShowChannelInSetup(plugin.meta)) return [];
		const adapter = resolveAdapter(plugin.id);
		if (!adapter) return [];
		return adapter.getStatus({
			cfg: params.cfg,
			options: params.options,
			accountOverrides: params.accountOverrides
		});
	}));
	const statusByChannel = new Map(statusEntries.map((entry) => [entry.channel, entry]));
	const fallbackStatuses = listChatChannels().filter((meta) => shouldShowChannelInSetup(meta)).filter((meta) => !statusByChannel.has(meta.id)).map((meta) => {
		const configured = isChannelConfigured(params.cfg, meta.id);
		const statusLabel = configured ? "configured (plugin disabled)" : "not configured";
		return {
			channel: meta.id,
			configured,
			statusLines: [`${formatSetupSelectionLabel(meta.label, meta.id)}: ${statusLabel}`],
			selectionHint: configured ? "configured (plugin disabled)" : "not configured",
			quickstartScore: 0
		};
	});
	const discoveredPluginStatuses = installedCatalogEntries.filter((entry) => !statusByChannel.has(entry.id)).map((entry) => {
		const configured = isChannelConfigured(params.cfg, entry.id);
		const pluginEnabled = params.cfg.plugins?.entries?.[entry.pluginId ?? entry.id]?.enabled !== false;
		const statusLabel = configured ? pluginEnabled ? "configured" : "configured (plugin disabled)" : pluginEnabled ? "installed" : "installed (plugin disabled)";
		return {
			channel: entry.id,
			configured,
			statusLines: [`${formatSetupSelectionLabel(entry.meta.label, entry.id)}: ${statusLabel}`],
			selectionHint: statusLabel,
			quickstartScore: 0
		};
	});
	const catalogStatuses = installableCatalogEntries.map((entry) => ({
		channel: entry.id,
		configured: false,
		statusLines: [`${formatSetupSelectionLabel(entry.meta.label, entry.id)}: install plugin to enable`],
		selectionHint: "plugin · install",
		quickstartScore: 0
	}));
	const combinedStatuses = [
		...statusEntries,
		...fallbackStatuses,
		...discoveredPluginStatuses,
		...catalogStatuses
	];
	return {
		installedPlugins,
		catalogEntries: installableCatalogEntries,
		installedCatalogEntries,
		statusByChannel: new Map(combinedStatuses.map((entry) => [entry.channel, entry])),
		statusLines: combinedStatuses.flatMap((entry) => entry.statusLines)
	};
}
async function noteChannelStatus(params) {
	const { statusLines } = await collectChannelStatus({
		cfg: params.cfg,
		options: params.options,
		accountOverrides: params.accountOverrides ?? {},
		installedPlugins: params.installedPlugins,
		resolveAdapter: params.resolveAdapter
	});
	if (statusLines.length > 0) await params.prompter.note(statusLines.join("\n"), "Channel status");
}
async function noteChannelPrimer(prompter, channels) {
	const channelLines = channels.map((channel) => formatChannelPrimerLine(formatSetupDisplayMeta({
		id: channel.id,
		label: channel.label,
		selectionLabel: channel.label,
		docsPath: "/",
		blurb: channel.blurb
	})));
	await prompter.note([
		"DM security: default is pairing; unknown DMs get a pairing code.",
		`Approve with: ${formatCliCommand("openclaw pairing approve <channel> <code>")}`,
		"Public DMs require dmPolicy=\"open\" + allowFrom=[\"*\"].",
		"Multi-user DMs: run: " + formatCliCommand("openclaw config set session.dmScope \"per-channel-peer\"") + " (or \"per-account-channel-peer\" for multi-account channels) to isolate sessions.",
		`Docs: ${formatDocsLink("/channels/pairing", "channels/pairing")}`,
		"",
		...channelLines
	].join("\n"), "How channels work");
}
function resolveQuickstartDefault(statusByChannel) {
	let best = null;
	for (const [channel, status] of statusByChannel) {
		if (status.quickstartScore == null) continue;
		if (!best || status.quickstartScore > best.score) best = {
			channel,
			score: status.quickstartScore
		};
	}
	return best?.channel;
}
function resolveChannelSelectionNoteLines(params) {
	const { entries } = resolveChannelSetupEntries({
		cfg: params.cfg,
		installedPlugins: params.installedPlugins,
		workspaceDir: resolveAgentWorkspaceDir(params.cfg, resolveDefaultAgentId(params.cfg))
	});
	const selectionNotes = /* @__PURE__ */ new Map();
	for (const entry of entries) selectionNotes.set(entry.id, formatChannelSelectionLine(formatSetupDisplayMeta(entry.meta), formatDocsLink));
	return params.selection.map((channel) => selectionNotes.get(channel)).filter((line) => Boolean(line));
}
function resolveChannelSetupSelectionContributions(params) {
	const bundledChannelIds = new Set(listChatChannels().map((channel) => channel.id));
	return params.entries.filter((entry) => shouldShowChannelInSetup(entry.meta)).toSorted((left, right) => compareChannelSetupSelectionEntries(left, right)).map((entry) => {
		const disabledHint = params.resolveDisabledHint(entry.id);
		const hint = [params.statusByChannel.get(entry.id)?.selectionHint, disabledHint].filter(Boolean).join(" · ") || void 0;
		return buildChannelSetupSelectionContribution({
			channel: entry.id,
			label: formatSetupSelectionLabel(entry.meta.selectionLabel ?? entry.meta.label, entry.id),
			hint: formatSetupSelectionHint(hint),
			source: bundledChannelIds.has(entry.id) ? "core" : "plugin"
		});
	});
}
function compareChannelSetupSelectionEntries(left, right) {
	const leftLabel = left.meta.selectionLabel ?? left.meta.label;
	const rightLabel = right.meta.selectionLabel ?? right.meta.label;
	return leftLabel.localeCompare(rightLabel, void 0, {
		numeric: true,
		sensitivity: "base"
	}) || left.id.localeCompare(right.id, void 0, {
		numeric: true,
		sensitivity: "base"
	});
}
//#endregion
//#region src/flows/channel-setup.ts
function createChannelOnboardingPostWriteHookCollector() {
	const hooks = /* @__PURE__ */ new Map();
	return {
		collect(hook) {
			hooks.set(`${hook.channel}:${hook.accountId}`, hook);
		},
		drain() {
			const next = [...hooks.values()];
			hooks.clear();
			return next;
		}
	};
}
async function runCollectedChannelOnboardingPostWriteHooks(params) {
	for (const hook of params.hooks) try {
		await hook.run({
			cfg: params.cfg,
			runtime: params.runtime
		});
	} catch (err) {
		const message = formatErrorMessage(err);
		params.runtime.error(`Channel ${hook.channel} post-setup warning for "${hook.accountId}": ${message}`);
	}
}
function createChannelOnboardingPostWriteHook(params) {
	if (!params.accountId || !params.adapter?.afterConfigWritten) return;
	return {
		channel: params.channel,
		accountId: params.accountId,
		run: async ({ cfg, runtime }) => await params.adapter?.afterConfigWritten?.({
			previousCfg: params.previousCfg,
			cfg,
			accountId: params.accountId,
			runtime
		})
	};
}
async function setupChannels(cfg, runtime, prompter, options) {
	let next = cfg;
	const deferStatusUntilSelection = options?.deferStatusUntilSelection === true;
	const forceAllowFromChannels = new Set(options?.forceAllowFromChannels ?? []);
	const accountOverrides = { ...options?.accountIds };
	const scopedPluginsById = /* @__PURE__ */ new Map();
	const resolveWorkspaceDir = () => resolveAgentWorkspaceDir(next, resolveDefaultAgentId(next));
	const rememberScopedPlugin = (plugin) => {
		const channel = plugin.id;
		scopedPluginsById.set(channel, plugin);
		options?.onResolvedPlugin?.(channel, plugin);
	};
	const activePluginsById = /* @__PURE__ */ new Map();
	const rememberActivePlugin = (plugin) => {
		activePluginsById.set(plugin.id, plugin);
		return plugin;
	};
	const getVisibleChannelPlugin = (channel) => scopedPluginsById.get(channel) ?? activePluginsById.get(channel);
	const listVisibleInstalledPlugins = () => {
		const merged = /* @__PURE__ */ new Map();
		const registryPlugins = listActiveChannelSetupPlugins().map(rememberActivePlugin);
		for (const plugin of registryPlugins) if (shouldShowChannelInSetup(plugin.meta)) merged.set(plugin.id, plugin);
		for (const plugin of scopedPluginsById.values()) if (shouldShowChannelInSetup(plugin.meta)) merged.set(plugin.id, plugin);
		return Array.from(merged.values());
	};
	const resolveVisibleChannelEntries = () => resolveChannelSetupEntries({
		cfg: next,
		installedPlugins: listVisibleInstalledPlugins(),
		workspaceDir: resolveWorkspaceDir()
	});
	const loadScopedChannelPlugin = async (channel, pluginId, setup) => {
		const existing = getVisibleChannelPlugin(channel);
		if (existing && setup?.forceReload !== true) return existing;
		const snapshot = loadChannelSetupPluginRegistrySnapshotForChannel({
			cfg: next,
			runtime,
			channel,
			...pluginId ? { pluginId } : {},
			workspaceDir: resolveWorkspaceDir(),
			installRuntimeDeps: setup?.installRuntimeDeps ?? false,
			forceSetupOnlyChannelPlugins: setup?.forceSetupOnlyChannelPlugins
		});
		const plugin = snapshot.channelSetups.find((entry) => entry.plugin.id === channel)?.plugin ?? snapshot.channels.find((entry) => entry.plugin.id === channel)?.plugin;
		if (plugin) {
			rememberScopedPlugin(plugin);
			return plugin;
		}
		const bundledPlugin = getBundledChannelSetupPlugin(channel);
		if (bundledPlugin) {
			rememberScopedPlugin(bundledPlugin);
			return bundledPlugin;
		}
	};
	const getVisibleSetupFlowAdapter = (channel) => {
		const scopedPlugin = scopedPluginsById.get(channel);
		if (scopedPlugin) return resolveChannelSetupWizardAdapterForPlugin(scopedPlugin);
		return resolveChannelSetupWizardAdapterForPlugin(getVisibleChannelPlugin(channel));
	};
	const preloadConfiguredExternalPlugins = async () => {
		listVisibleInstalledPlugins();
		const workspaceDir = resolveWorkspaceDir();
		const preloadTasks = [];
		for (const entry of listTrustedChannelPluginCatalogEntries({
			cfg: next,
			workspaceDir
		})) {
			const channel = entry.id;
			if (getVisibleChannelPlugin(channel)) continue;
			if (!(next.plugins?.entries?.[entry.pluginId ?? channel]?.enabled === true) && !isChannelConfigured(next, channel)) continue;
			preloadTasks.push(loadScopedChannelPlugin(channel, entry.pluginId));
		}
		await Promise.all(preloadTasks);
	};
	if (!deferStatusUntilSelection) await preloadConfiguredExternalPlugins();
	const { statusByChannel, statusLines } = deferStatusUntilSelection ? {
		statusByChannel: /* @__PURE__ */ new Map(),
		statusLines: []
	} : await collectChannelStatus({
		cfg: next,
		options,
		accountOverrides,
		installedPlugins: listVisibleInstalledPlugins(),
		resolveAdapter: getVisibleSetupFlowAdapter
	});
	if (!options?.skipStatusNote && statusLines.length > 0) await prompter.note(statusLines.join("\n"), "Channel status");
	if (!(options?.skipConfirm ? true : await prompter.confirm({
		message: "Configure chat channels now?",
		initialValue: true
	}))) return cfg;
	await noteChannelPrimer(prompter, resolveVisibleChannelEntries().entries.map((entry) => ({
		id: entry.id,
		label: entry.meta.label,
		blurb: entry.meta.blurb
	})));
	const quickstartDefault = options?.initialSelection?.[0] ?? (deferStatusUntilSelection ? void 0 : resolveQuickstartDefault(statusByChannel));
	const shouldPromptAccountIds = options?.promptAccountIds === true;
	const accountIdsByChannel = /* @__PURE__ */ new Map();
	const recordAccount = (channel, accountId) => {
		options?.onAccountId?.(channel, accountId);
		getVisibleSetupFlowAdapter(channel)?.onAccountRecorded?.(accountId, options);
		accountIdsByChannel.set(channel, accountId);
	};
	const selection = [];
	const addSelection = (channel) => {
		if (!selection.includes(channel)) selection.push(channel);
	};
	const resolveConfigDisabledHint = (channel) => {
		if (next.plugins?.enabled === false) return "plugins disabled";
		if (next.plugins?.entries?.[channel]?.enabled === false) return "插件已禁用";
		if (typeof next.channels?.[channel]?.enabled === "boolean") return next.channels[channel]?.enabled === false ? "disabled" : void 0;
	};
	const resolveDisabledHint = (channel) => {
		const configDisabledHint = resolveConfigDisabledHint(channel);
		if (configDisabledHint || deferStatusUntilSelection) return configDisabledHint;
		const plugin = getVisibleChannelPlugin(channel);
		if (!plugin) return;
		const accountId = resolveChannelDefaultAccountId({
			plugin,
			cfg: next
		});
		const account = plugin.config.resolveAccount(next, accountId);
		let enabled;
		if (plugin.config.isEnabled) enabled = plugin.config.isEnabled(account, next);
		else if (typeof account?.enabled === "boolean") enabled = account.enabled;
		return enabled === false ? "disabled" : void 0;
	};
	const getChannelEntries = () => {
		const resolved = resolveVisibleChannelEntries();
		return {
			entries: resolved.entries,
			catalogById: resolved.installableCatalogById,
			installedCatalogById: resolved.installedCatalogById
		};
	};
	const refreshStatus = async (channel) => {
		const adapter = getVisibleSetupFlowAdapter(channel);
		if (!adapter) return;
		const status = await adapter.getStatus({
			cfg: next,
			options,
			accountOverrides
		});
		statusByChannel.set(channel, status);
	};
	const enableBundledPluginForSetup = async (channel) => {
		if (getVisibleChannelPlugin(channel)) {
			await refreshStatus(channel);
			return true;
		}
		const disabledHint = resolveConfigDisabledHint(channel);
		if (disabledHint) {
			await prompter.note(`${channel} cannot be configured while ${disabledHint}. Enable it before setup.`, "Channel setup");
			return false;
		}
		const result = enablePluginInConfig(next, channel);
		next = result.config;
		if (!result.enabled) {
			await prompter.note(`Cannot enable ${channel}: ${result.reason ?? "插件已禁用"}.`, "Channel setup");
			return false;
		}
		const plugin = await loadScopedChannelPlugin(channel);
		const adapter = getVisibleSetupFlowAdapter(channel);
		if (!plugin) {
			if (adapter) {
				await prompter.note(`${channel} plugin not available (continuing with setup). If the channel still doesn't work after setup, run \`${formatCliCommand("openclaw plugins list")}\` and \`${formatCliCommand("openclaw plugins enable " + channel)}\`, then restart the gateway.`, "Channel setup");
				await refreshStatus(channel);
				return true;
			}
			await prompter.note(`${channel} plugin not available.`, "Channel setup");
			return false;
		}
		await refreshStatus(channel);
		return true;
	};
	const applySetupResult = async (channel, result) => {
		const previousCfg = next;
		next = result.cfg;
		const adapter = getVisibleSetupFlowAdapter(channel);
		if (result.accountId) {
			recordAccount(channel, result.accountId);
			const postWriteHook = createChannelOnboardingPostWriteHook({
				accountId: result.accountId,
				adapter,
				channel,
				previousCfg
			});
			if (postWriteHook) options?.onPostWriteHook?.(postWriteHook);
		}
		addSelection(channel);
		await refreshStatus(channel);
	};
	const applyCustomSetupResult = async (channel, result) => {
		if (result === "skip") return false;
		await applySetupResult(channel, result);
		return true;
	};
	const configureChannel = async (channel) => {
		if (scopedPluginsById.has(channel)) await loadScopedChannelPlugin(channel, void 0, {
			forceReload: true,
			forceSetupOnlyChannelPlugins: true,
			installRuntimeDeps: true
		});
		const adapter = getVisibleSetupFlowAdapter(channel);
		if (!adapter) {
			await prompter.note(`${channel} does not support guided setup yet.`, "Channel setup");
			return;
		}
		await applySetupResult(channel, await adapter.configure({
			cfg: next,
			runtime,
			prompter,
			options,
			accountOverrides,
			shouldPromptAccountIds,
			forceAllowFrom: forceAllowFromChannels.has(channel)
		}));
	};
	const handleConfiguredChannel = async (channel, label) => {
		const plugin = getVisibleChannelPlugin(channel);
		const adapter = getVisibleSetupFlowAdapter(channel);
		if (adapter?.configureWhenConfigured) {
			if (!await applyCustomSetupResult(channel, await adapter.configureWhenConfigured({
				cfg: next,
				runtime,
				prompter,
				options,
				accountOverrides,
				shouldPromptAccountIds,
				forceAllowFrom: forceAllowFromChannels.has(channel),
				configured: true,
				label
			}))) return;
			return;
		}
		const supportsDisable = Boolean(options?.allowDisable && (plugin?.config.setAccountEnabled || adapter?.disable));
		const supportsDelete = Boolean(options?.allowDisable && plugin?.config.deleteAccount);
		const action = await promptConfiguredAction({
			prompter,
			label,
			supportsDisable,
			supportsDelete
		});
		if (action === "skip") return;
		if (action === "update") {
			await configureChannel(channel);
			return;
		}
		if (!options?.allowDisable) return;
		if (action === "delete" && !supportsDelete) {
			await prompter.note(`${label} does not support deleting config entries.`, "Remove channel");
			return;
		}
		const resolvedAccountId = normalizeAccountId((action === "delete" ? Boolean(plugin?.config.deleteAccount) : Boolean(plugin?.config.setAccountEnabled)) ? await promptRemovalAccountId({
			cfg: next,
			prompter,
			label,
			channel,
			plugin
		}) : "default") ?? (plugin ? resolveChannelDefaultAccountId({
			plugin,
			cfg: next
		}) : "default");
		const accountLabel = formatAccountLabel(resolvedAccountId);
		if (action === "delete") {
			if (!await prompter.confirm({
				message: `Delete ${label} account "${accountLabel}"?`,
				initialValue: false
			})) return;
			if (plugin?.config.deleteAccount) next = plugin.config.deleteAccount({
				cfg: next,
				accountId: resolvedAccountId
			});
			await refreshStatus(channel);
			return;
		}
		if (plugin?.config.setAccountEnabled) next = plugin.config.setAccountEnabled({
			cfg: next,
			accountId: resolvedAccountId,
			enabled: false
		});
		else if (adapter?.disable) next = adapter.disable(next);
		await refreshStatus(channel);
	};
	const handleChannelChoice = async (channel) => {
		const { catalogById, installedCatalogById } = getChannelEntries();
		const catalogEntry = catalogById.get(channel);
		const installedCatalogEntry = installedCatalogById.get(channel);
		const deferredDisabledHint = deferStatusUntilSelection ? resolveConfigDisabledHint(channel) : void 0;
		if (deferredDisabledHint) {
			await prompter.note(`${channel} cannot be configured while ${deferredDisabledHint}. Enable it before setup.`, "Channel setup");
			return "done";
		}
		if (catalogEntry) {
			const workspaceDir = resolveWorkspaceDir();
			const result = await ensureChannelSetupPluginInstalled({
				cfg: next,
				entry: catalogEntry,
				prompter,
				runtime,
				workspaceDir
			});
			next = result.cfg;
			if (!result.installed) return "retry_selection";
			await loadScopedChannelPlugin(channel, result.pluginId ?? catalogEntry.pluginId);
			await refreshStatus(channel);
		} else if (installedCatalogEntry) {
			if (!await loadScopedChannelPlugin(channel, installedCatalogEntry.pluginId)) {
				await prompter.note(`${channel} plugin not available.`, "Channel setup");
				return "done";
			}
			await refreshStatus(channel);
		} else if (!await enableBundledPluginForSetup(channel)) return "done";
		const plugin = getVisibleChannelPlugin(channel);
		const adapter = getVisibleSetupFlowAdapter(channel);
		const label = plugin?.meta.label ?? catalogEntry?.meta.label ?? channel;
		const configured = statusByChannel.get(channel)?.configured ?? false;
		if (adapter?.configureInteractive) {
			if (!await applyCustomSetupResult(channel, await adapter.configureInteractive({
				cfg: next,
				runtime,
				prompter,
				options,
				accountOverrides,
				shouldPromptAccountIds,
				forceAllowFrom: forceAllowFromChannels.has(channel),
				configured,
				label
			}))) return "done";
			return "done";
		}
		if (configured) {
			await handleConfiguredChannel(channel, label);
			return "done";
		}
		await configureChannel(channel);
		return "done";
	};
	if (options?.quickstartDefaults) while (true) {
		const { entries } = getChannelEntries();
		const choice = await prompter.select({
			message: "Select channel (QuickStart)",
			options: [...resolveChannelSetupSelectionContributions({
				entries,
				statusByChannel,
				resolveDisabledHint
			}).map((contribution) => contribution.option), {
				value: "__skip__",
				label: "Skip for now",
				hint: `You can add channels later via \`${formatCliCommand("openclaw channels add")}\``
			}],
			initialValue: quickstartDefault,
			searchable: true
		});
		if (choice === "__skip__") break;
		if (await handleChannelChoice(choice) === "done") break;
	}
	else {
		const doneValue = "__done__";
		const initialValue = options?.initialSelection?.[0] ?? quickstartDefault;
		while (true) {
			const { entries } = getChannelEntries();
			const choice = await prompter.select({
				message: "Select a channel",
				options: [...resolveChannelSetupSelectionContributions({
					entries,
					statusByChannel,
					resolveDisabledHint
				}).map((contribution) => contribution.option), {
					value: doneValue,
					label: "Finished",
					hint: selection.length > 0 ? "Done" : "Skip for now"
				}],
				initialValue
			});
			if (choice === doneValue) break;
			await handleChannelChoice(choice);
		}
	}
	options?.onSelection?.(selection);
	const selectedLines = resolveChannelSelectionNoteLines({
		cfg: next,
		installedPlugins: listVisibleInstalledPlugins(),
		selection
	});
	if (selectedLines.length > 0) await prompter.note(selectedLines.join("\n"), "Selected channels");
	if (!options?.skipDmPolicyPrompt) next = await maybeConfigureDmPolicies({
		cfg: next,
		selection,
		prompter,
		accountIdsByChannel,
		resolveAdapter: getVisibleSetupFlowAdapter
	});
	return next;
}
//#endregion
export { noteChannelStatus as a, setupChannels as i, createChannelOnboardingPostWriteHookCollector as n, runCollectedChannelOnboardingPostWriteHooks as r, createChannelOnboardingPostWriteHook as t };
