import { Af as PLUGIN_ADMIN_RPC_ACTION_METHODS, Lf as resolvePluginAdminRpcActionGrant, Mf as isPluginRuntimeSessionReadAllowed, Oa as loadPluginManifestRegistry, Pf as normalizePluginsConfig, Rf as resolvePluginAdminRpcActionSourceKeys, Xt as loadConfig, ll as validateRegistryNpmSpec } from "./env-Dlz8aQHP.js";
import { d as listChannelPluginCatalogEntries } from "./dangerous-name-matching-ob0tR02Y.js";
import { i as resolveBundledPluginSources } from "./update-C_BnpDpE.js";
import { n as buildPluginLifecycleReport } from "./lifecycle-B4RibYD4.js";
import path from "node:path";

//#region src/plugins/marketplace-update-review.ts
function dedupeSorted$2(values) {
	return [...new Set(values)].map((value) => value.trim()).filter(Boolean).toSorted((left, right) => left.localeCompare(right));
}
function diffLists(current, next) {
	const currentSet = new Set(current);
	const nextSet = new Set(next);
	return {
		added: dedupeSorted$2(next.filter((value) => !currentSet.has(value))),
		removed: dedupeSorted$2(current.filter((value) => !nextSet.has(value)))
	};
}
function normalizeClawHubOrigin(value) {
	if (!value?.trim()) return;
	try {
		return new URL(value).origin;
	} catch {
		return;
	}
}
function formatMissingClawHubMetadata(install) {
	const missing = [];
	if (!install.clawhubUrl) missing.push("clawhubUrl");
	if (!install.clawhubArtifactUrl) missing.push("clawhubArtifactUrl");
	if (!install.clawhubPackage) missing.push("clawhubPackage");
	if (!install.clawhubFamily) missing.push("clawhubFamily");
	if (!install.clawhubChannel) missing.push("clawhubChannel");
	if (!install.version) missing.push("version");
	if (!install.artifactKind) missing.push("artifactKind");
	if (!install.artifactFormat) missing.push("artifactFormat");
	if (install.artifactKind === "npm-pack") {
		if (!install.npmIntegrity && !install.npmShasum) missing.push("npmIntegrity");
		if (!install.npmTarballName) missing.push("npmTarballName");
	}
	if (install.artifactKind === "clawpack") {
		if (!install.clawpackSha256) missing.push("clawpackSha256");
		if (typeof install.clawpackSpecVersion !== "number") missing.push("clawpackSpecVersion");
		if (!install.clawpackManifestSha256) missing.push("clawpackManifestSha256");
		if (typeof install.clawpackSize !== "number") missing.push("clawpackSize");
	}
	return missing;
}
function isClawHubIntegrityPinned(install) {
	if (!install.integrity) return false;
	if (install.artifactKind === "clawpack") return Boolean(install.clawpackSha256);
	if (install.artifactKind === "npm-pack") return Boolean(install.npmIntegrity || install.npmShasum);
	return true;
}
function resolveClawHubSourceTrust(install, outcome) {
	const spec = install.clawhubPackage && install.version ? `${install.clawhubPackage}@${install.version}` : install.clawhubPackage;
	const origin = normalizeClawHubOrigin(install.clawhubUrl);
	const missingMetadata = formatMissingClawHubMetadata(install);
	const integrityPinned = isClawHubIntegrityPinned(install);
	let reason = "ClawHub source allowlisted, artifact pinned, and local review completed";
	let trusted = true;
	if (!origin) {
		trusted = false;
		reason = "ClawHub source is missing a valid registry URL";
	} else if (origin !== "https://clawhub.com") {
		trusted = false;
		reason = `ClawHub registry is not allowlisted: ${origin}`;
	} else if (missingMetadata.length > 0) {
		trusted = false;
		reason = `ClawHub source metadata is incomplete: ${missingMetadata.join(", ")}`;
	} else if (!integrityPinned) {
		trusted = false;
		reason = "ClawHub source is missing pinned artifact integrity";
	} else if (!outcome.packageReview) {
		trusted = false;
		reason = "ClawHub source requires local package review";
	} else if ((outcome.warnings ?? []).length > 0) {
		trusted = false;
		reason = "ClawHub source has local scan warnings";
	}
	return {
		source: "clawhub",
		spec,
		trusted,
		reason,
		integrityPinned,
		resolvedSpec: outcome.resolvedSpec,
		resolvedIntegrity: outcome.integrity
	};
}
function resolveSourceTrust(install, outcome) {
	if (!install) return {
		source: "archive",
		trusted: false,
		reason: "missing install record",
		integrityPinned: false
	};
	if (install.source === "clawhub") return resolveClawHubSourceTrust(install, outcome);
	if (install.source !== "npm") return {
		source: install.source,
		spec: install.spec,
		trusted: false,
		reason: `updates from ${install.source} sources require manual review`,
		integrityPinned: false,
		resolvedSpec: outcome.resolvedSpec,
		resolvedIntegrity: outcome.integrity
	};
	const spec = install.spec?.trim();
	const registrySpecError = spec ? validateRegistryNpmSpec(spec) : "missing npm spec";
	const integrityPinned = Boolean(install.integrity);
	return {
		source: "npm",
		spec,
		trusted: !registrySpecError,
		reason: !registrySpecError ? integrityPinned ? "npm registry source with pinned integrity" : "npm registry source without pinned integrity" : `untrusted npm spec: ${registrySpecError}`,
		integrityPinned,
		resolvedSpec: outcome.resolvedSpec,
		resolvedIntegrity: outcome.integrity
	};
}
function buildPermissionDiff(entry, outcome) {
	const review = outcome.packageReview;
	const channels = diffLists(entry.channels, review?.channels ?? []);
	const providers = diffLists(entry.providers, review?.providers ?? []);
	const tools = diffLists(entry.toolNames, review?.tools ?? []);
	const skills = diffLists([], review?.skills ?? []);
	const changed = [];
	if (entry.kind && review?.kind && entry.kind !== review.kind) changed.push(`kind: ${entry.kind} -> ${review.kind}`);
	return {
		added: {
			channels: channels.added,
			providers: providers.added,
			tools: tools.added,
			skills: skills.added
		},
		removed: {
			channels: channels.removed,
			providers: providers.removed,
			tools: tools.removed,
			skills: skills.removed
		},
		changed
	};
}
function hasAddedPermissionSurface(diff) {
	return diff.added.channels.length > 0 || diff.added.providers.length > 0 || diff.added.tools.length > 0 || diff.added.skills.length > 0 || diff.changed.length > 0;
}
function buildPluginMarketplaceUpdateReview(params) {
	const sourceTrust = resolveSourceTrust(params.entry.install, params.outcome);
	const dependencyWarnings = params.outcome.packageReview?.dependencyWarnings ?? [];
	const scriptWarnings = params.outcome.packageReview?.scriptWarnings ?? [];
	const runtimeWarnings = params.outcome.packageReview?.runtimeWarnings ?? [];
	const scanWarnings = params.outcome.warnings ?? [];
	const permissionDiff = buildPermissionDiff(params.entry, params.outcome);
	const reasons = [];
	if (!sourceTrust.trusted) reasons.push(sourceTrust.reason);
	if (!sourceTrust.integrityPinned) reasons.push("source integrity is not pinned");
	if (dependencyWarnings.length > 0) reasons.push("package dependency manifest changed or requires review");
	if (scriptWarnings.length > 0) reasons.push("package declares npm scripts");
	if (runtimeWarnings.length > 0) reasons.push("plugin package exposes source-only TypeScript entries");
	if (scanWarnings.length > 0) reasons.push("scan warnings were reported");
	if (hasAddedPermissionSurface(permissionDiff)) reasons.push("plugin manifest surface expands or changes");
	return {
		currentVersion: params.outcome.currentVersion,
		nextVersion: params.outcome.nextVersion,
		sourceTrust,
		dependencyWarnings,
		scriptWarnings,
		runtimeWarnings,
		scanWarnings,
		permissionDiff,
		approvalRequired: reasons.length > 0,
		reasons: dedupeSorted$2(reasons)
	};
}
function formatPluginMarketplaceUpdateReviewWarnings(review) {
	return [
		...review.reasons.map((reason) => `Update review: ${reason}`),
		...review.dependencyWarnings,
		...review.scriptWarnings,
		...review.runtimeWarnings,
		...review.scanWarnings
	];
}

//#endregion
//#region src/plugins/marketplace.ts
function dedupeSorted$1(values) {
	return [...new Set(values)].toSorted((left, right) => left.localeCompare(right));
}
function mergeDiagnostics(left, right) {
	const merged = /* @__PURE__ */ new Map();
	for (const diag of [...left, ...right]) {
		const key = `${diag.level}::${diag.pluginId ?? ""}::${diag.source ?? ""}::${diag.message}`;
		if (!merged.has(key)) merged.set(key, diag);
	}
	return [...merged.values()];
}
function resolveCatalogLocalPath(localPath, workspaceDir) {
	const trimmed = localPath?.trim();
	if (!trimmed) return;
	if (path.isAbsolute(trimmed)) return trimmed;
	return workspaceDir ? path.resolve(workspaceDir, trimmed) : void 0;
}
function createEntry(id) {
	return {
		id,
		name: id,
		status: "available",
		discovered: false,
		managed: false,
		loaded: false,
		enabled: false,
		hasInstallRecord: false,
		channels: [],
		providers: [],
		toolNames: [],
		hookNames: [],
		gatewayMethods: [],
		cliCommands: [],
		services: [],
		commands: [],
		httpHandlers: 0,
		hookCount: 0,
		installOptions: {}
	};
}
function mergeLifecycleEntry(target, plugin) {
	target.name = plugin.name || target.name;
	target.description = plugin.description ?? target.description;
	target.version = plugin.version ?? target.version;
	target.kind = plugin.kind ?? target.kind;
	target.origin = plugin.origin;
	target.source = plugin.source;
	target.status = plugin.status;
	target.discovered = true;
	target.managed = plugin.managed;
	target.loaded = plugin.status === "loaded";
	target.enabled = plugin.enabled;
	target.hasInstallRecord = Boolean(plugin.install);
	target.install = plugin.install ?? target.install;
	target.error = plugin.error ?? target.error;
	target.channels = dedupeSorted$1([...target.channels, ...plugin.channelIds]);
	target.providers = dedupeSorted$1([...target.providers, ...plugin.providerIds]);
	target.toolNames = dedupeSorted$1([...target.toolNames, ...plugin.toolNames]);
	target.hookNames = dedupeSorted$1([...target.hookNames, ...plugin.hookNames]);
	target.gatewayMethods = dedupeSorted$1([...target.gatewayMethods, ...plugin.gatewayMethods]);
	target.cliCommands = dedupeSorted$1([...target.cliCommands, ...plugin.cliCommands]);
	target.services = dedupeSorted$1([...target.services, ...plugin.services]);
	target.commands = dedupeSorted$1([...target.commands, ...plugin.commands]);
	target.httpHandlers = Math.max(target.httpHandlers, plugin.httpHandlers);
	target.hookCount = Math.max(target.hookCount, plugin.hookCount);
}
function mergeManifestRecord(target, manifest) {
	target.name = manifest.name || target.name;
	target.description = manifest.description ?? target.description;
	target.version = manifest.version ?? target.version;
	target.kind = manifest.kind ?? target.kind;
	target.origin = target.origin ?? manifest.origin;
	target.source = target.source ?? manifest.source;
	target.discovered = true;
	target.channels = dedupeSorted$1([...target.channels, ...manifest.channels]);
	target.providers = dedupeSorted$1([...target.providers, ...manifest.providers]);
}
function mergeCatalogEntry(target, catalogEntry, workspaceDir) {
	target.name = catalogEntry.meta.label || target.name;
	target.description = catalogEntry.meta.blurb || target.description;
	target.channelCatalog = catalogEntry.meta;
	target.channels = dedupeSorted$1([...target.channels, catalogEntry.id]);
	target.installOptions = {
		...target.installOptions,
		npmSpec: target.installOptions.npmSpec ?? catalogEntry.install.npmSpec,
		localPath: target.installOptions.localPath ?? catalogEntry.install.localPath,
		resolvedLocalPath: target.installOptions.resolvedLocalPath ?? resolveCatalogLocalPath(catalogEntry.install.localPath, workspaceDir),
		defaultChoice: target.installOptions.defaultChoice ?? catalogEntry.install.defaultChoice,
		expectedIntegrity: target.installOptions.expectedIntegrity ?? catalogEntry.install.expectedIntegrity
	};
}
function mergeBundledSource(target, source) {
	target.installOptions = {
		...target.installOptions,
		npmSpec: target.installOptions.npmSpec ?? source.npmSpec,
		bundledLocalPath: target.installOptions.bundledLocalPath ?? source.localPath
	};
}
function resolveRuntimeHelpers(config, entry) {
	const pluginsConfig = normalizePluginsConfig(config.plugins);
	const pluginId = entry.id;
	const source = {
		...entry.origin ? { origin: entry.origin } : {},
		...entry.source ? { source: entry.source } : {}
	};
	const sourceKeys = resolvePluginAdminRpcActionSourceKeys(source);
	const grants = pluginsConfig.entries[pluginId]?.runtime?.adminRpcActions.allow ?? [];
	return {
		sessions: { read: isPluginRuntimeSessionReadAllowed(pluginsConfig, pluginId) },
		adminRpcActions: {
			sourceKeys,
			methods: PLUGIN_ADMIN_RPC_ACTION_METHODS.map((method) => {
				const grant = grants.find((item) => item.method === method);
				const decision = resolvePluginAdminRpcActionGrant({
					config: pluginsConfig,
					pluginId,
					method,
					source
				});
				return {
					method,
					granted: Boolean(grant),
					effective: decision.allowed,
					sources: grant?.sources ?? [],
					requireOperatorApproval: grant?.requireOperatorApproval === true,
					...decision.allowed ? {} : { reason: decision.reason }
				};
			})
		}
	};
}
function resolveActions(entry) {
	const actions = ["status"];
	const hasInstallSource = Boolean(entry.installOptions.npmSpec || entry.installOptions.resolvedLocalPath || entry.installOptions.bundledLocalPath);
	if (!entry.managed && !entry.loaded && hasInstallSource && (entry.channelCatalog || !entry.discovered)) actions.push("install");
	if (entry.install?.source === "npm" && entry.install.spec) actions.push("update");
	if (entry.managed) actions.push("uninstall");
	return actions;
}
function sortPlugins(left, right) {
	const rankLeft = left.managed ? 0 : left.discovered ? 1 : 2;
	const rankRight = right.managed ? 0 : right.discovered ? 1 : 2;
	if (rankLeft !== rankRight) return rankLeft - rankRight;
	const orderLeft = left.channelCatalog?.order ?? 999;
	const orderRight = right.channelCatalog?.order ?? 999;
	if (orderLeft !== orderRight) return orderLeft - orderRight;
	return left.name.localeCompare(right.name);
}
function buildPluginMarketplaceReport(params) {
	const config = params?.config ?? loadConfig();
	const lifecycleReport = params?.lifecycleReport ?? buildPluginLifecycleReport({ config });
	const workspaceDir = params?.workspaceDir ?? lifecycleReport.workspaceDir;
	const manifestRegistry = params?.manifestRegistry ?? loadPluginManifestRegistry({
		config,
		...workspaceDir ? { workspaceDir } : {}
	});
	const channelCatalog = params?.channelCatalog ?? listChannelPluginCatalogEntries(workspaceDir ? { workspaceDir } : {});
	const bundledSources = params?.bundledSources ?? resolveBundledPluginSources(workspaceDir ? { workspaceDir } : {});
	const entries = /* @__PURE__ */ new Map();
	const getOrCreate = (id) => {
		const existing = entries.get(id);
		if (existing) return existing;
		const next = createEntry(id);
		entries.set(id, next);
		return next;
	};
	for (const plugin of lifecycleReport.plugins) mergeLifecycleEntry(getOrCreate(plugin.id), plugin);
	for (const plugin of manifestRegistry.plugins) mergeManifestRecord(getOrCreate(plugin.id), plugin);
	for (const entry of channelCatalog) mergeCatalogEntry(getOrCreate(entry.id), entry, workspaceDir);
	for (const source of bundledSources.values()) mergeBundledSource(getOrCreate(source.pluginId), source);
	return {
		workspaceDir,
		plugins: [...entries.values()].map((entry) => ({
			...entry,
			runtimeHelpers: resolveRuntimeHelpers(config, entry),
			actions: resolveActions(entry)
		})).toSorted(sortPlugins),
		diagnostics: mergeDiagnostics(lifecycleReport.diagnostics, manifestRegistry.diagnostics)
	};
}
function resolvePluginMarketplaceEntry(params) {
	return (params.report ?? buildPluginMarketplaceReport({
		config: params.config,
		...params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}
	})).plugins.find((plugin) => plugin.id === params.idOrName || plugin.name === params.idOrName);
}

//#endregion
//#region src/plugins/runtime-helper-grants.ts
function assertPluginAdminRpcActionMethod(method) {
	if (PLUGIN_ADMIN_RPC_ACTION_METHODS.includes(method)) return method;
	throw new Error(`unsupported plugin admin RPC method: ${method}`);
}
function dedupeSorted(values) {
	return [...new Set([...values].map((value) => value.trim()).filter(Boolean))].toSorted((a, b) => a.localeCompare(b));
}
function serializeGrants(grants) {
	return JSON.stringify(grants.map((grant) => ({
		method: grant.method,
		sources: dedupeSorted(grant.sources),
		requireOperatorApproval: grant.requireOperatorApproval
	})));
}
function getPluginRuntimeSessionReadGrant(config, pluginId) {
	const id = pluginId.trim();
	if (!id) return false;
	return isPluginRuntimeSessionReadAllowed(normalizePluginsConfig(config.plugins), id);
}
function setPluginRuntimeSessionReadGrant(config, pluginId, enabled) {
	const id = pluginId.trim();
	if (!id) throw new Error("plugin id is required");
	const existingEntry = config.plugins?.entries?.[id] ?? {};
	const existingRuntime = existingEntry.runtime ?? {};
	const existingHelpers = existingRuntime.helpers ?? {};
	const existingSessions = existingHelpers.sessions ?? {};
	const previous = getPluginRuntimeSessionReadGrant(config, id);
	return {
		config: {
			...config,
			plugins: {
				...config.plugins,
				entries: {
					...config.plugins?.entries,
					[id]: {
						...existingEntry,
						runtime: {
							...existingRuntime,
							helpers: {
								...existingHelpers,
								sessions: {
									...existingSessions,
									read: enabled
								}
							}
						}
					}
				}
			}
		},
		pluginId: id,
		enabled,
		changed: previous !== enabled
	};
}
function setPluginAdminRpcActionGrant(config, pluginId, method, enabled, sources) {
	const id = pluginId.trim();
	if (!id) throw new Error("plugin id is required");
	const adminRpcMethod = assertPluginAdminRpcActionMethod(method.trim());
	const normalizedSources = dedupeSorted(sources);
	if (enabled && normalizedSources.length === 0) throw new Error("at least one source key is required to grant plugin admin RPC access");
	const existingEntry = config.plugins?.entries?.[id] ?? {};
	const existingRuntime = existingEntry.runtime ?? {};
	const normalizedGrants = normalizePluginsConfig(config.plugins).entries[id]?.runtime?.adminRpcActions.allow ?? [];
	const preservedGrants = normalizedGrants.filter((grant) => grant.method !== adminRpcMethod);
	const nextGrants = enabled ? [...preservedGrants, {
		method: adminRpcMethod,
		sources: normalizedSources,
		requireOperatorApproval: true
	}] : preservedGrants;
	const changed = serializeGrants(normalizedGrants) !== serializeGrants(nextGrants);
	return {
		config: {
			...config,
			plugins: {
				...config.plugins,
				entries: {
					...config.plugins?.entries,
					[id]: {
						...existingEntry,
						runtime: {
							...existingRuntime,
							adminRpcActions: {
								...existingRuntime.adminRpcActions,
								allow: nextGrants
							}
						}
					}
				}
			}
		},
		pluginId: id,
		method: adminRpcMethod,
		enabled,
		sources: normalizedSources,
		changed
	};
}

//#endregion
export { resolvePluginMarketplaceEntry as a, buildPluginMarketplaceReport as i, setPluginAdminRpcActionGrant as n, buildPluginMarketplaceUpdateReview as o, setPluginRuntimeSessionReadGrant as r, formatPluginMarketplaceUpdateReviewWarnings as s, getPluginRuntimeSessionReadGrant as t };