import { $c as installPluginFromNpmSpec, Wa as coerceSecretRef, pd as resolveFasedAgentPackageRootSync } from "./env-Dlz8aQHP.js";
import { i as CONFIG_DIR, p as isRecord, x as resolveUserPath } from "./agent-paths-DUHGyU1s.js";
import "./send-Ditv1VfR.js";
import "./send-CWM2yWF6.js";
import "./model-catalog-B2mRe6cZ.js";
import "./github-copilot-token-A9DiaCB0.js";
import "./reply-prefix-CCp0C8w4.js";
import "./memory-BpNgGSAl.js";
import "./memory-search-Bm46LIUQ.js";
import "./manager-DTf-5dRo.js";
import "./fetch-guard-DkRm4Ip1.js";
import "./ip-CLwwPh25.js";
import "./ssrf-CqV2hisj.js";
import "./query-expansion-CfOgA1KD.js";
import "./memory-cli-B7kMyaVL.js";
import "./ir-r4mczNAR.js";
import "./target-errors-CWVZTpXS.js";
import "./chunk-D9NQZPPD.js";
import "./markdown-tables-ylpJUTGZ.js";
import "./path-alias-guards-BerTsE5c.js";
import "./fs-safe-BrR3_fH1.js";
import "./render-BDQg502o.js";
import "./commands-registry-DQLLJg_T.js";
import "./diagnostic-BGFH8MIa.js";
import "./diagnostic-session-state-HO94DMou.js";
import "./send-C4oPrvgH.js";
import "./model-bpjlPTsB.js";
import "./pi-model-discovery-Do4Oa02J.js";
import "./dangerous-tools-CusV9bcr.js";
import "./chrome-CUkBgrOC.js";
import "./pairing-token-C2iKEhFc.js";
import "./ws-DiFJ3zRB.js";
import "./onboarding.wallet-CcoV0W6U.js";
import "./wallet-runtime-config-TjiNNY9x.js";
import "./wallet-provider-registry-CFNtQ2ZR.js";
import "./wallet-W8qsxi6H.js";
import "./wallet-approval-auth-BaVTLG5r.js";
import "./runtime-DXHXAPJA.js";
import "./device-identity-ZHUQ6a8N.js";
import "./solana-bond-signing-D9-GtRIf.js";
import "./federation-8sW60I4p.js";
import "./call-CjHZhgQk.js";
import "./send-Dlj2DghK.js";
import "./outbound-attachment-D2ImFzcm.js";
import "./fetch-BqA-KBI4.js";
import "./delivery-queue-CMGPhIoF.js";
import "./deps-4rCQ5SBd.js";
import "./frontmatter-DTw-J9er.js";
import "./skills-CzDvK09q.js";
import "./skills-remote-CM9uNuu4.js";
import "./dangerous-name-matching-ob0tR02Y.js";
import "./channel-selection-BSwWHrf5.js";
import "./session-cost-usage-Dzc2RiRn.js";
import "./trash--uInT2PE.js";
import "./paths-rsh1tVGc.js";
import "./exec-approvals-aJApzFwx.js";
import "./node-shell-CpYc0oKF.js";
import "./task-planner-CkWt6WhX.js";
import "./send-CekwB5g1.js";
import "./resolve-route-y4Ol4Qmc.js";
import "./pairing-store-qjH5R_BQ.js";
import "./skills-clawhub-CBB2AUv6.js";
import "./exec-approvals-allowlist-BZoClZ91.js";
import "./skill-commands-Cx0nCzUv.js";
import "./channel-activity-DrrSLe52.js";
import "./tables-8heM_uJo.js";
import "./proxy-mdETjik4.js";
import "./config-DfviqDy5.js";
import "./links-CPltkiov.js";
import "./cli-utils-XevKf-tr.js";
import "./help-format-D7mxVfC9.js";
import "./progress-BhMUiH4f.js";
import "./replies-BFsEK9FQ.js";
import "./prompt-style-CWdNyu_Q.js";
import "./pairing-labels-DBt_OA8C.js";
import { t as enablePluginInConfig } from "./enable-C7xTf-9S.js";
import { t as buildNpmResolutionInstallFields } from "./installs-PYgg3AvN.js";
import "./status-jb8w0SwX.js";
import "./update-C_BnpDpE.js";
import { o as finalizeInstalledPluginConfig } from "./lifecycle-B4RibYD4.js";
import { t as resolvePluginWebSearchProviders } from "./web-search-providers.runtime-TGCiBzhv.js";
import path from "node:path";
import fs from "node:fs";

//#region src/plugins/web-search-install-catalog.ts
const OFFICIAL_EXTERNAL_PLUGIN_CATALOG_RELATIVE_PATH = path.join("config", "official-external-plugin-catalog.json");
const DEFAULT_EXTERNAL_CATALOG_PATHS = [
	path.join(CONFIG_DIR, "mpm", "plugins.json"),
	path.join(CONFIG_DIR, "mpm", "catalog.json"),
	path.join(CONFIG_DIR, "plugins", "catalog.json")
];
const ENV_CATALOG_PATHS = ["FASED_PLUGIN_CATALOG_PATHS", "FASED_MPM_CATALOG_PATHS"];
function normalizeString(value) {
	return typeof value === "string" && value.trim() ? value.trim() : void 0;
}
function normalizeStringList(value) {
	return Array.isArray(value) ? value.map(normalizeString).filter((entry) => Boolean(entry)) : [];
}
function normalizeOnboardingScopes(value) {
	if (!Array.isArray(value)) return;
	const scopes = value.filter((entry) => entry === "text-inference");
	return scopes.length > 0 ? scopes : void 0;
}
function splitEnvPaths(value) {
	const trimmed = value.trim();
	if (!trimmed) return [];
	return trimmed.split(/[;,]/g).flatMap((chunk) => chunk.split(path.delimiter)).map((entry) => entry.trim()).filter(Boolean);
}
function parseCatalogEntries(raw) {
	if (Array.isArray(raw)) return raw.filter((entry) => isRecord(entry));
	if (!isRecord(raw)) return [];
	const list = raw.entries ?? raw.packages ?? raw.plugins;
	if (!Array.isArray(list)) return [];
	return list.filter((entry) => isRecord(entry));
}
function loadCatalogEntriesFromPaths(paths) {
	const entries = [];
	for (const resolved of paths) {
		if (!fs.existsSync(resolved)) continue;
		try {
			const payload = JSON.parse(fs.readFileSync(resolved, "utf-8"));
			entries.push(...parseCatalogEntries(payload));
		} catch {}
	}
	return entries;
}
function resolveOfficialCatalogPaths(options) {
	if (options?.officialCatalogPaths !== void 0) return options.officialCatalogPaths.map((entry) => entry.trim()).filter(Boolean);
	return [
		resolveFasedAgentPackageRootSync({ cwd: process.cwd() }),
		resolveFasedAgentPackageRootSync({ moduleUrl: import.meta.url }),
		process.cwd()
	].filter((entry, index, all) => Boolean(entry) && all.indexOf(entry) === index).map((packageRoot) => path.join(packageRoot, OFFICIAL_EXTERNAL_PLUGIN_CATALOG_RELATIVE_PATH));
}
function resolveExternalCatalogPaths(options) {
	if (options?.catalogPaths !== void 0) return options.catalogPaths.map((entry) => entry.trim()).filter(Boolean);
	for (const key of ENV_CATALOG_PATHS) {
		const raw = process.env[key];
		if (raw && raw.trim()) return splitEnvPaths(raw).map((entry) => resolveUserPath(entry));
	}
	return DEFAULT_EXTERNAL_CATALOG_PATHS;
}
function resolveManifest(entry) {
	return entry.fased ?? null;
}
function pathSegments(pathValue) {
	return pathValue.split(".").map((segment) => segment.trim()).filter((segment) => segment.length > 0);
}
function getConfigPath(config, pathValue) {
	let current = config;
	for (const segment of pathSegments(pathValue)) {
		if (!isRecord(current)) return;
		current = current[segment];
	}
	return current;
}
function setConfigPath(target, pathValue, value) {
	const segments = pathSegments(pathValue);
	let current = target;
	for (const segment of segments.slice(0, -1)) {
		const next = current[segment];
		if (!isRecord(next)) current[segment] = {};
		current = current[segment];
	}
	const leaf = segments.at(-1);
	if (leaf) current[leaf] = value;
}
function buildProviderEntry(params) {
	const providerId = normalizeString(params.provider.id);
	const label = normalizeString(params.provider.label);
	const hint = normalizeString(params.provider.hint);
	const credentialPath = normalizeString(params.provider.credentialPath) ?? `plugins.entries.${params.pluginId}.config.webSearch.apiKey`;
	const envVars = normalizeStringList(params.provider.envVars);
	const placeholder = normalizeString(params.provider.placeholder);
	const signupUrl = normalizeString(params.provider.signupUrl);
	if (!providerId || !label || !hint || envVars.length === 0 || !placeholder || !signupUrl) return null;
	const onboardingScopes = normalizeOnboardingScopes(params.provider.onboardingScopes);
	return {
		id: providerId,
		pluginId: params.pluginId,
		label,
		hint,
		envVars,
		placeholder,
		signupUrl,
		credentialPath,
		...onboardingScopes ? { onboardingScopes } : {},
		...params.provider.requiresCredential === false ? { requiresCredential: false } : {},
		...normalizeString(params.provider.credentialLabel) ? { credentialLabel: normalizeString(params.provider.credentialLabel) } : {},
		...normalizeString(params.provider.docsUrl) ? { docsUrl: normalizeString(params.provider.docsUrl) } : {},
		...typeof params.provider.autoDetectOrder === "number" ? { autoDetectOrder: params.provider.autoDetectOrder } : {},
		getCredentialValue: (searchConfig) => searchConfig?.apiKey,
		setCredentialValue: (value, searchConfig) => {
			if (searchConfig) searchConfig.apiKey = value;
		},
		getConfiguredCredentialValue: (config) => getConfigPath(config, credentialPath),
		setConfiguredCredentialValue: (configTarget, value) => {
			setConfigPath(configTarget, credentialPath, value);
		},
		applySelectionConfig: (config) => enablePluginInConfig(config, params.pluginId).config,
		createTool: () => null
	};
}
function buildInstallCatalogEntry(entry, trustedSourceLinkedOfficialInstall) {
	const manifest = resolveManifest(entry);
	const pluginId = normalizeString(manifest?.plugin?.id);
	const install = manifest?.install;
	if (!manifest || !pluginId || !install) return [];
	const label = normalizeString(manifest.plugin?.label) ?? pluginId;
	return (manifest.webSearchProviders ?? []).map((provider) => buildProviderEntry({
		pluginId,
		provider
	})).filter((provider) => Boolean(provider)).map((provider) => ({
		pluginId,
		label,
		install,
		provider,
		...trustedSourceLinkedOfficialInstall ? { trustedSourceLinkedOfficialInstall } : {}
	}));
}
function resolveWebSearchInstallCatalogEntries(options) {
	const officialEntries = loadCatalogEntriesFromPaths(resolveOfficialCatalogPaths(options)).flatMap((entry) => buildInstallCatalogEntry(entry, true));
	const externalEntries = loadCatalogEntriesFromPaths(resolveExternalCatalogPaths(options)).flatMap((entry) => buildInstallCatalogEntry(entry, false));
	const byProvider = /* @__PURE__ */ new Map();
	for (const entry of [...externalEntries, ...officialEntries]) if (!byProvider.has(entry.provider.id)) byProvider.set(entry.provider.id, entry);
	return [...byProvider.values()].toSorted((left, right) => left.provider.label.localeCompare(right.provider.label) || left.provider.id.localeCompare(right.provider.id));
}

//#endregion
//#region src/flows/search-setup.ts
const WEB_SEARCH_DOCS_URL = "https://docs.fased.ai/tools/web";
function normalizeOptionalString(value) {
	if (typeof value !== "string") return;
	return value.trim() || void 0;
}
function hasConfiguredSecretInput(value) {
	if (normalizeOptionalString(value)) return true;
	return Boolean(coerceSecretRef(value));
}
function readSearchConfig(config) {
	const search = config?.tools?.web?.search;
	return search && typeof search === "object" ? search : {};
}
function readProviderConfig(config, provider) {
	const nested = readSearchConfig(config)[provider];
	return nested && typeof nested === "object" && !Array.isArray(nested) ? nested : {};
}
function writeSearchConfig(config, search) {
	return {
		...config,
		tools: {
			...config.tools,
			web: {
				...config.tools?.web,
				search
			}
		}
	};
}
function cloneConfigForMutation(config) {
	return structuredClone(config);
}
function resolveProviderEntry(provider, config) {
	return resolvePluginWebSearchProviders({ config }).find((entry) => entry.id === provider);
}
function resolveSearchProviderCredentialLabel(entry) {
	if (entry.requiresCredential === false) return `${entry.label} setup`;
	if (entry.id === "searxng") return "SearXNG base URL";
	return `${entry.label} API key`;
}
function providerNeedsCredential(entry) {
	return entry.requiresCredential !== false;
}
function hasKeyInEnv(entry) {
	return entry.envVars.some((key) => Boolean(normalizeOptionalString(process.env[key])));
}
function resolveExistingKey(config, provider) {
	const entry = resolveProviderEntry(provider, config);
	const configuredValue = entry?.getConfiguredCredentialValue?.(config);
	const configured = normalizeOptionalString(configuredValue);
	if (configured) return configured;
	const search = readSearchConfig(config);
	const entryValue = entry?.getCredentialValue?.(search);
	const fromEntry = normalizeOptionalString(entryValue);
	if (fromEntry) return fromEntry;
	if (provider === "brave") return normalizeOptionalString(search.apiKey);
	return normalizeOptionalString(readProviderConfig(config, provider).apiKey);
}
function hasExistingKey(config, provider) {
	if (resolveExistingKey(config, provider)) return true;
	const entry = resolveProviderEntry(provider, config);
	if (hasConfiguredSecretInput(entry?.getConfiguredCredentialValue?.(config))) return true;
	return hasConfiguredSecretInput(entry?.getCredentialValue?.(readSearchConfig(config)));
}
function providerIsReady(config, entry) {
	if (!providerNeedsCredential(entry)) return true;
	return hasExistingKey(config, entry.id) || hasKeyInEnv(entry);
}
function applySearchProviderSelection(config, provider) {
	const selected = resolveProviderEntry(provider, config)?.applySelectionConfig?.(config) ?? config;
	return writeSearchConfig(selected, {
		...selected.tools?.web?.search,
		provider,
		enabled: true
	});
}
function applySearchKey(config, provider, key) {
	const selected = applySearchProviderSelection(config, provider);
	const search = { ...selected.tools?.web?.search };
	const entry = resolveProviderEntry(provider, selected);
	if (entry?.setConfiguredCredentialValue) {
		const target = cloneConfigForMutation(selected);
		entry.setConfiguredCredentialValue(target, key);
		return target;
	}
	if (entry?.setCredentialValue) {
		entry.setCredentialValue(key, search);
		return writeSearchConfig(selected, search);
	}
	if (provider === "brave") search.apiKey = key;
	else search[provider] = {
		...readProviderConfig(selected, provider),
		apiKey: key
	};
	return writeSearchConfig(selected, search);
}
function applyCatalogSearchProviderSelection(config, entry) {
	const selected = entry.provider.applySelectionConfig?.(config) ?? config;
	return writeSearchConfig(selected, {
		...selected.tools?.web?.search,
		provider: entry.provider.id,
		enabled: true
	});
}
function applyCatalogSearchKey(config, entry, key) {
	const selected = applyCatalogSearchProviderSelection(config, entry);
	if (entry.provider.setConfiguredCredentialValue) {
		const target = cloneConfigForMutation(selected);
		entry.provider.setConfiguredCredentialValue(target, key);
		return target;
	}
	return applySearchKey(selected, entry.provider.id, key);
}
async function installCatalogProvider(params) {
	const npmSpec = params.entry.install.npmSpec?.trim();
	if (!npmSpec) {
		await params.prompter.note(`${params.entry.label} has no npm install source available.`, "Web search plugin");
		return null;
	}
	if (!await params.prompter.confirm({
		message: `Install ${params.entry.label} web search plugin?`,
		initialValue: true
	})) return null;
	const result = await installPluginFromNpmSpec({
		spec: npmSpec,
		expectedPluginId: params.entry.pluginId,
		logger: {
			info: (message) => params.runtime.log?.(message),
			warn: (message) => params.runtime.log?.(message)
		}
	});
	if (!result.ok) {
		await params.prompter.note(`Failed to install ${npmSpec}: ${result.error}`, "Web search plugin");
		params.runtime.error?.(`Web search plugin install failed: ${result.error}`);
		return null;
	}
	return finalizeInstalledPluginConfig({
		config: params.config,
		pluginId: result.pluginId,
		refreshManifestRegistry: true,
		installRecord: {
			source: "npm",
			spec: npmSpec,
			installPath: result.targetDir,
			version: result.version,
			...buildNpmResolutionInstallFields(result.npmResolution)
		}
	}).config;
}
async function runSearchSetupFlow(config, runtime, prompter) {
	const providerOptions = resolvePluginWebSearchProviders({ config });
	const providerIds = new Set(providerOptions.map((entry) => entry.id));
	const installOptions = resolveWebSearchInstallCatalogEntries().filter((entry) => !providerIds.has(entry.provider.id));
	if (providerOptions.length === 0 && installOptions.length === 0) {
		await prompter.note([
			"No web search providers are available.",
			"Enable a web search provider plugin or use raw config.",
			`Docs: ${WEB_SEARCH_DOCS_URL}`
		].join("\n"), "Web search");
		return config;
	}
	await prompter.note([
		"Web search lets your agent look things up online using the `web_search` tool.",
		"Choose a provider and store its API key, or leave the key empty to use an environment variable.",
		`Docs: ${WEB_SEARCH_DOCS_URL}`
	].join("\n"), "Web search");
	const existingProvider = normalizeOptionalString(config.tools?.web?.search?.provider);
	const defaultProvider = providerOptions.find((entry) => entry.id === existingProvider)?.id ?? providerOptions.find((entry) => providerIsReady(config, entry))?.id ?? providerOptions[0]?.id ?? (installOptions[0] ? `__install__:${installOptions[0].provider.id}` : void 0);
	if (!defaultProvider) return config;
	const choice = await prompter.select({
		message: "Web search provider",
		options: [
			...providerOptions.map((entry) => {
				const status = entry.requiresCredential === false ? "no key required" : providerIsReady(config, entry) ? "configured" : entry.envVars.length > 0 ? `env: ${entry.envVars.join(" or ")}` : void 0;
				return {
					value: entry.id,
					label: entry.label,
					hint: [entry.hint, status].filter(Boolean).join(" · ")
				};
			}),
			...installOptions.map((entry) => ({
				value: `__install__:${entry.provider.id}`,
				label: entry.provider.label,
				hint: `${entry.provider.hint} · install ${entry.label}`
			})),
			{
				value: "__skip__",
				label: "Skip for now",
				hint: "Keep current search settings"
			}
		],
		initialValue: defaultProvider
	});
	if (choice === "__skip__") return config;
	let provider = choice;
	let entry = resolveProviderEntry(provider, config);
	let catalogEntry;
	if (typeof choice === "string" && choice.startsWith("__install__:")) {
		const providerId = choice.slice(12);
		catalogEntry = installOptions.find((item) => item.provider.id === providerId);
		if (!catalogEntry) return config;
		const installedConfig = await installCatalogProvider({
			config,
			entry: catalogEntry,
			runtime,
			prompter
		});
		if (!installedConfig) return config;
		config = installedConfig;
		provider = catalogEntry.provider.id;
		entry = catalogEntry.provider;
	}
	if (!entry) return config;
	if (!providerNeedsCredential(entry)) {
		if (catalogEntry) return applyCatalogSearchProviderSelection(config, catalogEntry);
		return applySearchProviderSelection(config, provider);
	}
	const existingKey = resolveExistingKey(config, provider);
	const envAvailable = hasKeyInEnv(entry);
	const credentialLabel = resolveSearchProviderCredentialLabel(entry);
	const key = normalizeOptionalString(await (prompter.secret ?? prompter.text)({
		message: existingKey ? `${credentialLabel} (leave blank to keep current)` : envAvailable ? `${credentialLabel} (leave blank to use env var)` : credentialLabel,
		placeholder: existingKey ? "Leave blank to keep current" : entry.placeholder
	}));
	if (key) {
		if (catalogEntry) return applyCatalogSearchKey(config, catalogEntry, key);
		return applySearchKey(config, provider, key);
	}
	if (existingKey) {
		if (catalogEntry) return applyCatalogSearchKey(config, catalogEntry, existingKey);
		return applySearchKey(config, provider, existingKey);
	}
	if (envAvailable) {
		if (catalogEntry) return applyCatalogSearchProviderSelection(config, catalogEntry);
		return applySearchProviderSelection(config, provider);
	}
	await prompter.note([
		`No ${credentialLabel} stored yet, so web_search will stay unavailable until a key exists.`,
		...entry.signupUrl ? [`Get a key: ${entry.signupUrl}`] : [],
		`Docs: ${WEB_SEARCH_DOCS_URL}`
	].join("\n"), "Web search");
	return {
		...config,
		tools: {
			...config.tools,
			web: {
				...config.tools?.web,
				search: {
					...config.tools?.web?.search,
					provider
				}
			}
		}
	};
}

//#endregion
export { runSearchSetupFlow as setupSearch };