import "./paths-C9FmTW1x.js";
import { S as resolveUserPath, i as CONFIG_DIR, m as isRecord } from "./agent-paths-BySKyq9I.js";
import { Co as coerceSecretRef, mo as resolveFasedAgentPackageRootSync, wl as installPluginFromNpmSpec } from "./agent-scope-CLn_Ws3B.js";
import "./commands-registry-DfzoWf4b.js";
import "./reply-DQfPM5ia.js";
import "./errors-Dx3S3Nrp.js";
import "./send-DI5jsj-s.js";
import "./send-Dg2_aoK-.js";
import "./github-copilot-token-BA45-q56.js";
import "./chat-envelope-D_wSv26N.js";
import "./pi-model-discovery-BftA1L--.js";
import "./dangerous-tools-C88-uuUI.js";
import "./chrome-BFIqTpDi.js";
import "./pairing-token-BFGorDQo.js";
import "./ip-D_T5zbZS.js";
import "./ws-cvQhrVP3.js";
import "./onboarding.wallet-B12p7GUZ.js";
import "./wallet-runtime-config-BlRWvEER.js";
import "./wallet-provider-registry-ax0NJo0F.js";
import "./wallet-BPiknFdt.js";
import "./wallet-approval-auth-DT-vofLs.js";
import "./reply-prefix-BRvLVE6I.js";
import "./media-ZuS0crH3.js";
import "./path-alias-guards-DivKBvV8.js";
import "./fs-safe-Cp0MI7Oo.js";
import "./runtime-bsvPuEXH.js";
import "./device-identity-DzUZ06fL.js";
import "./solana-bond-signing-CgswTuFe.js";
import "./diagnostic-Cl18V9Cy.js";
import "./federation-BrNm7xtG.js";
import "./call-s6DNIELo.js";
import "./chunk-tBRG1JLR.js";
import "./markdown-tables-DvKl7MfL.js";
import "./send-aHqtlYYi.js";
import "./fetch-guard-hWUtz61M.js";
import "./ssrf-Blf4NrHS.js";
import "./outbound-attachment-BHTb_aF7.js";
import "./fetch-BJWQWfdf.js";
import "./delivery-queue-9lp1uujZ.js";
import "./deps-CBapZnsi.js";
import "./memory-e9aOXNx1.js";
import "./memory-search-B8y2GTeB.js";
import "./manager-DI2w66Ae.js";
import "./query-expansion-BdzehFyx.js";
import "./memory-cli-mMVfDz8Z.js";
import "./target-errors-_IirSOQ1.js";
import "./render-C2MRWrC0.js";
import "./diagnostic-session-state-89enG0Yp.js";
import "./send-B3O8oJLA.js";
import "./model-CfutahU0.js";
import "./exec-approvals-allowlist-C5bxRc7H.js";
import "./dangerous-name-matching-B2YH8EDF.js";
import "./skills-D1Qdj69-.js";
import "./node-match-BNTZ8TY5.js";
import "./system-run-command-BvFkDQae.js";
import "./channel-selection-EDP8q9uz.js";
import "./task-planner-BW6kdamv.js";
import "./trash-BiHtb5y8.js";
import "./paths-BA7z-HF9.js";
import "./send-CKgt65ud.js";
import "./resolve-route-CbII9dNc.js";
import "./pairing-store-CUPrh2Qy.js";
import "./session-cost-usage-DQSpjhkP.js";
import "./skills-clawhub-kuGAFKgL.js";
import "./config-eval-CuVkTCse.js";
import "./channel-activity-BkfQ8RFW.js";
import "./tables-apsvCDwc.js";
import "./proxy-DU7W9XSc.js";
import "./config-BKhx4paQ.js";
import "./links-BeUKckiL.js";
import "./cli-utils-DJ7Nfw8J.js";
import "./help-format-H4GNCFCs.js";
import "./progress-CDqdg8f8.js";
import "./replies-Xddrs-mt.js";
import "./skills-remote-DkdYN-8J.js";
import "./skill-commands-CxJHp3z6.js";
import "./prompt-style-Db5iJoaI.js";
import "./pairing-labels-sAC1Ty3_.js";
import { t as enablePluginInConfig } from "./enable-DYrDatZX.js";
import { t as buildNpmResolutionInstallFields } from "./installs-BS-BIvab.js";
import "./status-CTveTSuF.js";
import "./update-fKz5UGr6.js";
import { o as finalizeInstalledPluginConfig } from "./lifecycle-Bgh3V8Jt.js";
import { t as resolvePluginWebSearchProviders } from "./web-search-providers.runtime-gM-8LIxg.js";
import fs from "node:fs";
import path from "node:path";

//#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 };