import { Sl as fileExists, al as scanDirectoryWithSummary, fl as resolveSafeInstallDir, hl as installPackageDir, yl as withExtractedArchiveRoot } from "./env-Dlz8aQHP.js";
import { tt as resolvePreferredFasedAgentTmpDir } from "./agent-paths-DUHGyU1s.js";
import { a as resolveFasedAgentManifestBlock, l as parseFrontmatterBlock, n as normalizeStringList } from "./frontmatter-DTw-J9er.js";
import os from "node:os";
import path from "node:path";
import fs from "node:fs/promises";
import { createHash } from "node:crypto";

//#region src/infra/fasedhub.ts
const DEFAULT_CLAWHUB_REGISTRY = "https://clawhub.com";
const DEFAULT_FETCH_TIMEOUT_MS = 3e4;
var FasedHubRequestError = class extends Error {
	constructor(params) {
		super(`ClawHub ${params.path} failed (${params.status}): ${params.body}`);
		this.name = "ClawHubRequestError";
		this.status = params.status;
		this.requestPath = params.path;
		this.responseBody = params.body;
	}
};
function normalizeOptionalString(value) {
	return typeof value === "string" && value.trim() ? value.trim() : void 0;
}
function normalizeBaseUrl(baseUrl) {
	const envValue = normalizeOptionalString(process.env.CLAWHUB_REGISTRY) || normalizeOptionalString(process.env.FASED_CLAWHUB_REGISTRY) || normalizeOptionalString(process.env.CLAWHUB_URL) || normalizeOptionalString(process.env.FASED_CLAWHUB_URL) || normalizeOptionalString(process.env.FASEDHUB_REGISTRY) || normalizeOptionalString(process.env.FASEDHUB_URL) || DEFAULT_CLAWHUB_REGISTRY;
	return (normalizeOptionalString(baseUrl) || envValue).replace(/\/+$/, "") || DEFAULT_CLAWHUB_REGISTRY;
}
function resolveFasedHubBaseUrl(baseUrl) {
	return normalizeBaseUrl(baseUrl);
}
function extractTokenFromFasedHubConfig(value) {
	if (!value || typeof value !== "object") return;
	const record = value;
	return normalizeOptionalString(record.accessToken) ?? normalizeOptionalString(record.authToken) ?? normalizeOptionalString(record.apiToken) ?? normalizeOptionalString(record.token) ?? extractTokenFromFasedHubConfig(record.auth) ?? extractTokenFromFasedHubConfig(record.session) ?? extractTokenFromFasedHubConfig(record.credentials) ?? extractTokenFromFasedHubConfig(record.user);
}
function resolveFasedHubConfigPaths() {
	const explicit = normalizeOptionalString(process.env.CLAWHUB_CONFIG_PATH) || normalizeOptionalString(process.env.FASED_CLAWHUB_CONFIG_PATH) || normalizeOptionalString(process.env.FASEDHUB_CONFIG_PATH) || normalizeOptionalString(process.env.CLAWDHUB_CONFIG_PATH);
	if (explicit) return [explicit];
	const xdgConfigHome = normalizeOptionalString(process.env.XDG_CONFIG_HOME);
	const configHome = xdgConfigHome && xdgConfigHome.length > 0 ? xdgConfigHome : path.join(os.homedir(), ".config");
	const legacyClawHubXdgPath = path.join(configHome, "clawhub", "config.json");
	const fasedHubXdgPath = path.join(configHome, "fasedhub", "config.json");
	if (process.platform === "darwin") return [
		path.join(os.homedir(), "Library", "Application Support", "clawhub", "config.json"),
		legacyClawHubXdgPath,
		path.join(os.homedir(), "Library", "Application Support", "fasedhub", "config.json"),
		fasedHubXdgPath
	];
	return [legacyClawHubXdgPath, fasedHubXdgPath];
}
async function resolveFasedHubAuthToken() {
	const envToken = normalizeOptionalString(process.env.CLAWHUB_TOKEN) || normalizeOptionalString(process.env.CLAWHUB_AUTH_TOKEN) || normalizeOptionalString(process.env.FASED_CLAWHUB_TOKEN) || normalizeOptionalString(process.env.FASEDHUB_TOKEN) || normalizeOptionalString(process.env.FASEDHUB_AUTH_TOKEN);
	if (envToken) return envToken;
	for (const configPath of resolveFasedHubConfigPaths()) try {
		const raw = await fs.readFile(configPath, "utf8");
		const token = extractTokenFromFasedHubConfig(JSON.parse(raw));
		if (token) return token;
	} catch {}
}
function buildUrl(params) {
	const url = new URL(params.path, `${normalizeBaseUrl(params.baseUrl)}/`);
	for (const [key, value] of Object.entries(params.search ?? {})) {
		if (!value) continue;
		url.searchParams.set(key, value);
	}
	return url;
}
async function fasedHubRequest(params) {
	const url = buildUrl(params);
	const token = normalizeOptionalString(params.token) || await resolveFasedHubAuthToken();
	const controller = new AbortController();
	const timeout = setTimeout(() => controller.abort(/* @__PURE__ */ new Error(`ClawHub request timed out after ${params.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS}ms`)), params.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS);
	try {
		return {
			response: await (params.fetchImpl ?? fetch)(url, {
				headers: token ? { Authorization: `Bearer ${token}` } : void 0,
				signal: controller.signal
			}),
			url
		};
	} finally {
		clearTimeout(timeout);
	}
}
async function readErrorBody(response) {
	try {
		return (await response.text()).trim() || response.statusText || `HTTP ${response.status}`;
	} catch {
		return response.statusText || `HTTP ${response.status}`;
	}
}
async function fetchJson(params) {
	const { response, url } = await fasedHubRequest(params);
	if (!response.ok) throw new FasedHubRequestError({
		path: url.pathname,
		status: response.status,
		body: await readErrorBody(response)
	});
	return await response.json();
}
function formatSha256Integrity(bytes) {
	return `sha256-${createHash("sha256").update(bytes).digest("base64")}`;
}
function sanitizeTempFileName(fileName) {
	return path.basename(fileName).replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "download.bin";
}
async function createTempDownloadTarget(params) {
	const dir = await fs.mkdtemp(path.join(resolvePreferredFasedAgentTmpDir(), `${params.prefix}-`));
	return {
		path: path.join(dir, sanitizeTempFileName(params.fileName)),
		cleanup: async () => {
			await fs.rm(dir, {
				recursive: true,
				force: true
			});
		}
	};
}
async function searchFasedHubSkills(params) {
	return (await fetchJson({
		baseUrl: params.baseUrl,
		path: "/api/v1/search",
		token: params.token,
		timeoutMs: params.timeoutMs,
		fetchImpl: params.fetchImpl,
		search: {
			q: params.query.trim(),
			limit: params.limit ? String(params.limit) : void 0
		}
	})).results ?? [];
}
async function fetchFasedHubSkillDetail(params) {
	return await fetchJson({
		baseUrl: params.baseUrl,
		path: `/api/v1/skills/${encodeURIComponent(params.slug)}`,
		token: params.token,
		timeoutMs: params.timeoutMs,
		fetchImpl: params.fetchImpl
	});
}
async function downloadFasedHubSkillArchive(params) {
	const { response, url } = await fasedHubRequest({
		baseUrl: params.baseUrl,
		path: "/api/v1/download",
		token: params.token,
		timeoutMs: params.timeoutMs,
		fetchImpl: params.fetchImpl,
		search: {
			slug: params.slug,
			version: params.version,
			tag: params.version ? void 0 : params.tag
		}
	});
	if (!response.ok) throw new FasedHubRequestError({
		path: url.pathname,
		status: response.status,
		body: await readErrorBody(response)
	});
	const bytes = new Uint8Array(await response.arrayBuffer());
	const target = await createTempDownloadTarget({
		prefix: "clawhub-skill",
		fileName: `${params.slug}.zip`
	});
	await fs.writeFile(target.path, bytes);
	return {
		archivePath: target.path,
		integrity: formatSha256Integrity(bytes),
		cleanup: target.cleanup
	};
}
const resolveClawHubBaseUrl = resolveFasedHubBaseUrl;
const searchClawHubSkills = searchFasedHubSkills;
const fetchClawHubSkillDetail = fetchFasedHubSkillDetail;
const downloadClawHubSkillArchive = downloadFasedHubSkillArchive;

//#endregion
//#region src/agents/skills-marketplace-policy.ts
const VALID_WALLET_ACTIONS = new Set([
	"prepare",
	"send",
	"plan",
	"quote",
	"swap",
	"schedule_plan",
	"limit_order",
	"limit_cancel",
	"limit_history"
]);
const VALID_WALLET_ROLES = new Set(["agent"]);
const VALID_WALLET_CHAINS = new Set(["solana"]);
const RISKY_WALLET_ACTIONS = new Set([
	"send",
	"swap",
	"schedule_plan",
	"limit_order"
]);
async function findSkillFile(rootDir) {
	for (const candidate of [
		"SKILL.md",
		"skill.md",
		"skills.md",
		"SKILL.MD"
	]) {
		const file = path.join(rootDir, candidate);
		try {
			if ((await fs.stat(file)).isFile()) return file;
		} catch {}
	}
	throw new Error("downloaded archive is missing SKILL.md");
}
function uniqueSorted(values) {
	if (!values || values.length === 0) return;
	return [...new Set(values.map((value) => value.trim()).filter(Boolean))].toSorted();
}
function normalizeNumber(value, field) {
	if (value === void 0) return;
	const parsed = typeof value === "number" ? value : Number(value);
	if (!Number.isFinite(parsed) || parsed < 0) throw new Error(`invalid marketplace walletActions.${field}`);
	return Math.floor(parsed);
}
function normalizeBoolean(value) {
	return typeof value === "boolean" ? value : void 0;
}
function normalizeWalletActionsRequest(raw) {
	if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error("invalid marketplace walletActions manifest");
	const input = raw;
	const actions = uniqueSorted(normalizeStringList(input.actions));
	const roles = uniqueSorted(normalizeStringList(input.roles));
	const chains = uniqueSorted(normalizeStringList(input.chains));
	for (const action of actions ?? []) if (!VALID_WALLET_ACTIONS.has(action)) throw new Error(`invalid marketplace wallet action: ${action}`);
	for (const role of roles ?? []) if (!VALID_WALLET_ROLES.has(role)) throw new Error(`invalid marketplace wallet role: ${role}`);
	for (const chain of chains ?? []) if (!VALID_WALLET_CHAINS.has(chain)) throw new Error(`invalid marketplace wallet chain: ${chain}`);
	const maxAmount = typeof input.maxAmount === "string" ? input.maxAmount.trim() : "";
	const request = {
		actions,
		roles,
		chains,
		inputMints: uniqueSorted(normalizeStringList(input.inputMints)),
		outputMints: uniqueSorted(normalizeStringList(input.outputMints)),
		maxAmount: maxAmount || void 0,
		maxSlippageBps: normalizeNumber(input.maxSlippageBps, "maxSlippageBps"),
		autonomous: normalizeBoolean(input.autonomous),
		cron: normalizeBoolean(input.cron)
	};
	return Object.fromEntries(Object.entries(request).filter(([, value]) => Array.isArray(value) ? value.length > 0 : value !== void 0));
}
function readWalletActionsManifest(metadata) {
	if (!metadata) return;
	if (metadata.walletActions !== void 0) return metadata.walletActions;
	return (metadata.permissions && typeof metadata.permissions === "object" && !Array.isArray(metadata.permissions) ? metadata.permissions : void 0)?.walletActions;
}
function readToolAccessManifest(metadata) {
	if (!metadata) return;
	const permissions = metadata.permissions && typeof metadata.permissions === "object" && !Array.isArray(metadata.permissions) ? metadata.permissions : void 0;
	const tools = uniqueSorted([
		...normalizeStringList(metadata.tools),
		...normalizeStringList(metadata.toolAccess),
		...normalizeStringList(permissions?.tools),
		...normalizeStringList(permissions?.toolAccess)
	]);
	return tools && tools.length > 0 ? tools : void 0;
}
function readInstallManifest(metadata) {
	const raw = metadata?.install;
	if (!Array.isArray(raw)) return;
	const kinds = [];
	const bins = [];
	for (const entry of raw) {
		if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
		const item = entry;
		const kind = typeof item.kind === "string" ? item.kind : typeof item.type === "string" ? item.type : "";
		if (kind.trim()) kinds.push(kind.trim().toLowerCase());
		bins.push(...normalizeStringList(item.bins));
	}
	const request = {
		kinds: uniqueSorted(kinds),
		bins: uniqueSorted(bins)
	};
	return Object.fromEntries(Object.entries(request).filter(([, value]) => Array.isArray(value) && value.length > 0));
}
function stableJson(value) {
	if (Array.isArray(value)) return `[${value.map((item) => stableJson(item)).join(",")}]`;
	if (value && typeof value === "object") return `{${Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(",")}}`;
	return JSON.stringify(value);
}
function digestPermissions(value) {
	return createHash("sha256").update(stableJson(value)).digest("hex");
}
function hasRiskyWalletActions(request) {
	if (!request) return false;
	if (request.autonomous === true || request.cron === true) return true;
	return (request.actions ?? []).some((action) => RISKY_WALLET_ACTIONS.has(action));
}
function hasRequestedInstallOrToolAccess(params) {
	return Boolean(params.toolAccess?.length || params.install?.kinds?.length || params.install?.bins?.length);
}
async function inspectSkillMarketplaceManifest(rootDir) {
	const skillFile = await findSkillFile(rootDir);
	const metadata = resolveFasedAgentManifestBlock({ frontmatter: parseFrontmatterBlock(await fs.readFile(skillFile, "utf8")) });
	const walletActionsRaw = readWalletActionsManifest(metadata);
	const walletActions = walletActionsRaw === void 0 ? void 0 : normalizeWalletActionsRequest(walletActionsRaw);
	const toolAccess = readToolAccessManifest(metadata);
	const install = readInstallManifest(metadata);
	const payload = {
		install,
		toolAccess,
		walletActions
	};
	return {
		skillFile,
		permissions: {
			version: 1,
			walletActions,
			toolAccess,
			install,
			risky: hasRiskyWalletActions(walletActions) || hasRequestedInstallOrToolAccess({
				install,
				toolAccess
			}),
			digest: digestPermissions(payload)
		}
	};
}
function formatMarketplacePermissionSummary(summary) {
	const wallet = summary?.walletActions;
	if (!wallet) {
		const parts = ["wallet actions: none requested"];
		if (summary?.toolAccess?.length) parts.push(`tools: ${summary.toolAccess.join(",")}`);
		if (summary?.install?.kinds?.length || summary?.install?.bins?.length) parts.push(`install: ${summary.install.kinds?.join(",") || "unspecified"}${summary.install.bins?.length ? `; bins: ${summary.install.bins.join(",")}` : ""}`);
		return parts.join("; ");
	}
	const actions = wallet.actions?.join(",") || "unspecified";
	const autonomous = wallet.autonomous === true ? "yes" : "no";
	const cron = wallet.cron === true ? "yes" : "no";
	const parts = [
		`wallet actions: ${actions}`,
		`autonomous: ${autonomous}`,
		`cron: ${cron}`
	];
	if (summary?.toolAccess?.length) parts.push(`tools: ${summary.toolAccess.join(",")}`);
	if (summary?.install?.kinds?.length || summary?.install?.bins?.length) parts.push(`install: ${summary.install.kinds?.join(",") || "unspecified"}${summary.install.bins?.length ? `; bins: ${summary.install.bins.join(",")}` : ""}`);
	return parts.join("; ");
}

//#endregion
//#region src/agents/skills-marketplace-scan.ts
const BLOCKED_DIR_NAMES = new Set([
	".git",
	".hg",
	".svn",
	"node_modules"
]);
const BLOCKED_PACKAGE_SCRIPTS = new Set([
	"preinstall",
	"install",
	"postinstall",
	"prepare",
	"prepack",
	"postpack"
]);
const DEPENDENCY_FIELDS = new Set([
	"dependencies",
	"devDependencies",
	"optionalDependencies",
	"peerDependencies",
	"bundleDependencies",
	"bundledDependencies"
]);
const MAX_FILES = 500;
const MAX_TOTAL_BYTES = 20 * 1024 * 1024;
const MAX_FILE_BYTES = 2 * 1024 * 1024;
const BLOCKED_FILE_NAMES = new Set([
	".env",
	".env.local",
	".env.production",
	".npmrc",
	".pypirc",
	".netrc",
	"credentials",
	"credentials.json",
	"id_dsa",
	"id_ecdsa",
	"id_ed25519",
	"id_rsa",
	"secrets.json",
	"service-account.json"
]);
const BLOCKED_FILE_EXTENSIONS = new Set([
	".dll",
	".dylib",
	".exe",
	".key",
	".node",
	".p12",
	".pem",
	".pfx",
	".so"
]);
const BLOCKED_INSTALL_SCRIPT_NAMES = new Set([
	"bootstrap.sh",
	"install.bat",
	"install.cmd",
	"install.ps1",
	"install.sh",
	"postinstall.sh",
	"setup.bat",
	"setup.cmd",
	"setup.ps1",
	"setup.sh"
]);
const SUSPICIOUS_SCRIPT_EXTENSIONS = new Set([
	".bash",
	".bat",
	".cmd",
	".fish",
	".ps1",
	".sh",
	".zsh"
]);
const DEPENDENCY_MANIFEST_FILE_NAMES = new Set([
	"Cargo.toml",
	"Gemfile",
	"Pipfile",
	"composer.json",
	"go.mod",
	"package-lock.json",
	"pnpm-lock.yaml",
	"poetry.lock",
	"pyproject.toml",
	"requirements.txt",
	"uv.lock",
	"yarn.lock"
].map((name) => name.toLowerCase()));
function toPosix(relativePath) {
	return relativePath.split(path.sep).join("/");
}
function isObjectRecord(value) {
	return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
function addFinding(findings, finding) {
	findings.push(finding);
}
function inspectMarketplaceFilePath(params) {
	const basename = path.posix.basename(params.path).toLowerCase();
	const ext = path.posix.extname(basename);
	if (BLOCKED_FILE_NAMES.has(basename)) addFinding(params.findings, {
		severity: "block",
		code: "sensitive_file",
		path: params.path,
		message: `skill archive contains sensitive file "${basename}"`
	});
	if (BLOCKED_FILE_EXTENSIONS.has(ext)) addFinding(params.findings, {
		severity: "block",
		code: "blocked_binary_or_secret_file",
		path: params.path,
		message: `skill archive contains blocked file type "${ext}"`
	});
	if (BLOCKED_INSTALL_SCRIPT_NAMES.has(basename)) addFinding(params.findings, {
		severity: "block",
		code: "install_script_file",
		path: params.path,
		message: `skill archive contains installer script "${basename}"`
	});
	else if (SUSPICIOUS_SCRIPT_EXTENSIONS.has(ext)) addFinding(params.findings, {
		severity: "warn",
		code: "script_file",
		path: params.path,
		message: `skill archive contains script file "${basename}"`
	});
	if (DEPENDENCY_MANIFEST_FILE_NAMES.has(basename)) addFinding(params.findings, {
		severity: "warn",
		code: "dependency_manifest",
		path: params.path,
		message: "skill archive contains a dependency manifest; marketplace installs do not run dependency install steps automatically"
	});
}
async function inspectMarketplaceSourceCode(params) {
	try {
		const summary = await scanDirectoryWithSummary(params.rootDir, {
			maxFiles: MAX_FILES,
			maxFileBytes: MAX_FILE_BYTES
		});
		for (const finding of summary.findings) {
			const relativePath = toPosix(path.relative(params.rootDir, finding.file));
			addFinding(params.findings, {
				severity: finding.severity === "critical" ? "block" : "warn",
				code: finding.severity === "critical" ? "dangerous_code" : "suspicious_code",
				path: relativePath,
				message: `${finding.message} (${finding.ruleId}, line ${finding.line})`
			});
		}
	} catch (err) {
		addFinding(params.findings, {
			severity: "block",
			code: "code_scan_failed",
			path: ".",
			message: `failed to scan marketplace skill code: ${err instanceof Error ? err.message : String(err)}`
		});
	}
}
async function inspectPackageJson(params) {
	let parsed;
	try {
		parsed = JSON.parse(await fs.readFile(path.join(params.rootDir, params.relativePath), "utf8"));
	} catch {
		addFinding(params.findings, {
			severity: "block",
			code: "invalid_package_json",
			path: toPosix(params.relativePath),
			message: "package.json is not valid JSON"
		});
		return;
	}
	if (!isObjectRecord(parsed)) return;
	const scripts = isObjectRecord(parsed.scripts) ? parsed.scripts : {};
	for (const scriptName of Object.keys(scripts)) if (BLOCKED_PACKAGE_SCRIPTS.has(scriptName)) addFinding(params.findings, {
		severity: "block",
		code: "package_lifecycle_script",
		path: toPosix(params.relativePath),
		message: `package.json contains lifecycle script "${scriptName}"`
	});
	for (const field of Object.keys(parsed)) if (DEPENDENCY_FIELDS.has(field) && isObjectRecord(parsed[field])) addFinding(params.findings, {
		severity: "block",
		code: "package_dependencies",
		path: toPosix(params.relativePath),
		message: `package.json contains ${field}; declare skill installers in metadata.fased.install instead`
	});
}
async function scanSkillMarketplaceArchive(rootDir) {
	const findings = [];
	const queue = [""];
	const files = [];
	let fileCount = 0;
	let totalBytes = 0;
	while (queue.length > 0) {
		const relativeDir = queue.shift() ?? "";
		const fullDir = path.join(rootDir, relativeDir);
		const entries = await fs.readdir(fullDir, { withFileTypes: true });
		for (const entry of entries) {
			const relativePath = path.join(relativeDir, entry.name);
			const posixPath = toPosix(relativePath);
			if (entry.isSymbolicLink()) {
				addFinding(findings, {
					severity: "block",
					code: "symlink",
					path: posixPath,
					message: "skill archives may not contain symlinks"
				});
				continue;
			}
			if (entry.isDirectory()) {
				if (BLOCKED_DIR_NAMES.has(entry.name)) {
					addFinding(findings, {
						severity: "block",
						code: "blocked_directory",
						path: posixPath,
						message: `skill archive contains blocked directory "${entry.name}"`
					});
					continue;
				}
				queue.push(relativePath);
				continue;
			}
			if (!entry.isFile()) {
				addFinding(findings, {
					severity: "block",
					code: "special_file",
					path: posixPath,
					message: "skill archives may contain only regular files and directories"
				});
				continue;
			}
			fileCount += 1;
			if (files.length < 80) files.push(posixPath);
			if (fileCount > MAX_FILES) addFinding(findings, {
				severity: "block",
				code: "too_many_files",
				path: posixPath,
				message: `skill archive exceeds ${MAX_FILES} files`
			});
			const stat = await fs.stat(path.join(rootDir, relativePath));
			totalBytes += stat.size;
			if (stat.size > MAX_FILE_BYTES) addFinding(findings, {
				severity: "block",
				code: "file_too_large",
				path: posixPath,
				message: `file exceeds ${MAX_FILE_BYTES} bytes`
			});
			if (totalBytes > MAX_TOTAL_BYTES) addFinding(findings, {
				severity: "block",
				code: "archive_too_large",
				path: posixPath,
				message: `skill archive exceeds ${MAX_TOTAL_BYTES} bytes`
			});
			if (entry.name === "package.json") await inspectPackageJson({
				rootDir,
				relativePath,
				findings
			});
			inspectMarketplaceFilePath({
				path: posixPath,
				findings
			});
		}
	}
	await inspectMarketplaceSourceCode({
		rootDir,
		findings
	});
	return {
		version: 1,
		fileCount,
		totalBytes,
		files: files.toSorted(),
		filesTruncated: fileCount > files.length,
		findings,
		blocked: findings.some((finding) => finding.severity === "block")
	};
}
function formatArchiveScanFindings(scan) {
	if (scan.findings.length === 0) return `archive scan: ${scan.fileCount} files, ${scan.totalBytes} bytes, no issues`;
	return scan.findings.map((finding) => `${finding.severity}: ${finding.code} at ${finding.path}: ${finding.message}`).join("; ");
}

//#endregion
//#region src/agents/skills-clawhub.ts
const DOT_DIR = ".clawhub";
const LEGACY_DOT_DIRS = [".clawdhub"];
const SKILL_ORIGIN_RELATIVE_PATH = path.join(DOT_DIR, "origin.json");
const VALID_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
const NON_ASCII_PATTERN = /[^\x00-\x7F]/;
function normalizeTrackedSlug(raw) {
	const slug = raw.trim();
	if (!slug || slug.includes("/") || slug.includes("\\") || slug.includes("..")) throw new Error(`Invalid skill slug: ${raw}`);
	return slug;
}
function validateRequestedSlug(raw) {
	const slug = normalizeTrackedSlug(raw);
	if (NON_ASCII_PATTERN.test(slug) || !VALID_SLUG_PATTERN.test(slug)) throw new Error(`Invalid skill slug: ${raw}`);
	return slug;
}
function normalizeRegistryUrl(value) {
	const raw = value?.trim();
	if (!raw) return null;
	try {
		const url = new URL(raw);
		url.hash = "";
		url.search = "";
		url.pathname = url.pathname.replace(/\/+$/, "");
		return url.toString().replace(/\/+$/, "");
	} catch {
		return raw.replace(/\/+$/, "");
	}
}
function normalizeRegistryAllowlist(values) {
	const rawList = values ?? ["https://clawhub.com"];
	return [...new Set(rawList.map((entry) => normalizeRegistryUrl(entry)).filter(Boolean))];
}
function resolveRegistryTrust(params) {
	const registry = normalizeRegistryUrl(params.registry);
	const allowed = normalizeRegistryAllowlist(params.allowRegistries);
	if (registry && allowed.includes(registry)) return {
		registry,
		trusted: true,
		mode: "allowlist",
		allowlist: allowed
	};
	if (params.allowTrackedLegacyOrigin && !params.allowRegistries && registry) return {
		registry,
		trusted: true,
		mode: "tracked-legacy",
		allowlist: allowed
	};
	throw new Error(`ClawHub registry is not allowlisted: ${params.registry}`);
}
async function resolveRequestedUpdateSlug(params) {
	const trackedSlug = normalizeTrackedSlug(params.requestedSlug);
	if (await readClawHubSkillOrigin(resolveSkillInstallDir(params.workspaceDir, trackedSlug)) || params.lock.skills[trackedSlug]) return trackedSlug;
	return validateRequestedSlug(params.requestedSlug);
}
function resolveSkillInstallDir(workspaceDir, slug) {
	const target = resolveSafeInstallDir({
		baseDir: path.join(path.resolve(workspaceDir), "skills"),
		id: slug,
		invalidNameMessage: "invalid skill target path"
	});
	if (!target.ok) throw new Error(target.error);
	return target.path;
}
async function ensureSkillRoot(rootDir) {
	for (const candidate of [
		"SKILL.md",
		"skill.md",
		"skills.md",
		"SKILL.MD"
	]) if (await fileExists(path.join(rootDir, candidate))) return;
	throw new Error("downloaded archive is missing SKILL.md");
}
async function readClawHubSkillsLockfile(workspaceDir) {
	const candidates = [path.join(workspaceDir, DOT_DIR, "lock.json"), ...LEGACY_DOT_DIRS.map((dir) => path.join(workspaceDir, dir, "lock.json"))];
	for (const candidate of candidates) try {
		const raw = JSON.parse(await fs.readFile(candidate, "utf8"));
		if (raw.version === 1 && raw.skills && typeof raw.skills === "object") return {
			version: 1,
			skills: raw.skills
		};
	} catch {}
	return {
		version: 1,
		skills: {}
	};
}
async function writeClawHubSkillsLockfile(workspaceDir, lockfile) {
	const targetPath = path.join(workspaceDir, DOT_DIR, "lock.json");
	await fs.mkdir(path.dirname(targetPath), { recursive: true });
	await fs.writeFile(targetPath, `${JSON.stringify(lockfile, null, 2)}\n`, "utf8");
}
async function readClawHubSkillOrigin(skillDir) {
	const candidates = [path.join(skillDir, DOT_DIR, "origin.json"), ...LEGACY_DOT_DIRS.map((dir) => path.join(skillDir, dir, "origin.json"))];
	for (const candidate of candidates) try {
		const raw = JSON.parse(await fs.readFile(candidate, "utf8"));
		if (raw.version === 1 && typeof raw.registry === "string" && typeof raw.slug === "string" && typeof raw.installedVersion === "string" && typeof raw.installedAt === "number") return raw;
	} catch {}
	return null;
}
async function writeClawHubSkillOrigin(skillDir, origin) {
	const targetPath = path.join(skillDir, SKILL_ORIGIN_RELATIVE_PATH);
	await fs.mkdir(path.dirname(targetPath), { recursive: true });
	await fs.writeFile(targetPath, `${JSON.stringify(origin, null, 2)}\n`, "utf8");
}
async function searchSkillsFromClawHub(params) {
	return await searchClawHubSkills({
		query: params.query?.trim() || "*",
		limit: params.limit,
		baseUrl: params.baseUrl
	});
}
async function resolveInstallVersion(params) {
	const detail = await fetchClawHubSkillDetail({
		slug: params.slug,
		baseUrl: params.baseUrl
	});
	if (!detail.skill) throw new Error(`Skill "${params.slug}" not found on ClawHub.`);
	const resolvedVersion = params.version ?? detail.latestVersion?.version;
	if (!resolvedVersion) throw new Error(`Skill "${params.slug}" has no installable version.`);
	return {
		detail,
		version: resolvedVersion
	};
}
async function installExtractedSkill(params) {
	await ensureSkillRoot(params.extractedRoot);
	const targetDir = resolveSkillInstallDir(params.workspaceDir, params.slug);
	const install = await installPackageDir({
		sourceDir: params.extractedRoot,
		targetDir,
		mode: params.mode,
		timeoutMs: 12e4,
		logger: params.logger,
		copyErrorPrefix: "failed to install skill",
		hasDeps: false,
		depsLogMessage: ""
	});
	if (!install.ok) return install;
	return {
		ok: true,
		targetDir
	};
}
function scanFindingKey(finding) {
	return `${finding.severity}\u0000${finding.code}\u0000${finding.path}\u0000${finding.message}`;
}
function collectPermissionLabels(permissions) {
	if (!permissions) return [];
	const labels = [];
	const wallet = permissions.walletActions;
	for (const action of wallet?.actions ?? []) labels.push(`wallet action: ${action}`);
	for (const role of wallet?.roles ?? []) labels.push(`wallet role: ${role}`);
	for (const chain of wallet?.chains ?? []) labels.push(`wallet chain: ${chain}`);
	for (const mint of wallet?.inputMints ?? []) labels.push(`wallet input mint: ${mint}`);
	for (const mint of wallet?.outputMints ?? []) labels.push(`wallet output mint: ${mint}`);
	if (wallet?.maxAmount) labels.push(`wallet max amount: ${wallet.maxAmount}`);
	if (wallet?.maxSlippageBps !== void 0) labels.push(`wallet max slippage: ${wallet.maxSlippageBps} bps`);
	if (wallet?.autonomous === true) labels.push("wallet autonomous actions");
	if (wallet?.cron === true) labels.push("wallet scheduled actions");
	for (const tool of permissions.toolAccess ?? []) labels.push(`tool access: ${tool}`);
	for (const kind of permissions.install?.kinds ?? []) labels.push(`install kind: ${kind}`);
	for (const bin of permissions.install?.bins ?? []) labels.push(`install binary: ${bin}`);
	return [...new Set(labels)].toSorted();
}
function diffPermissionLabels(params) {
	const previous = new Set(collectPermissionLabels(params.previous));
	const next = new Set(collectPermissionLabels(params.next));
	return {
		added: [...next].filter((label) => !previous.has(label)),
		removed: [...previous].filter((label) => !next.has(label))
	};
}
function buildMarketplaceUpdateReview(params) {
	const previousDigest = params.previousOrigin?.permissions?.digest;
	const permissionDigestChanged = previousDigest !== params.permissions.digest;
	const permissionDiff = diffPermissionLabels({
		previous: params.previousOrigin?.permissions,
		next: params.permissions
	});
	const previousFindings = new Set((params.previousOrigin?.installScan?.findings ?? []).filter((finding) => finding.severity === "warn").map((finding) => scanFindingKey(finding)));
	const addedScanFindings = params.installScan.findings.filter((finding) => finding.severity === "warn").filter((finding) => !previousFindings.has(scanFindingKey(finding))).map((finding) => ({
		severity: "warn",
		code: finding.code,
		path: finding.path,
		message: finding.message
	}));
	const reasons = [];
	if (params.isUpdate && params.permissions.risky && permissionDigestChanged) reasons.push("requested permissions changed");
	if (params.isUpdate && addedScanFindings.length > 0) reasons.push("archive scan added reviewable findings");
	return {
		version: 1,
		approvalRequired: reasons.length > 0,
		reasons,
		permissionDigestChanged,
		...previousDigest ? { previousPermissionDigest: previousDigest } : {},
		nextPermissionDigest: params.permissions.digest,
		permissionDiff,
		addedScanFindings
	};
}
function assertUpdateReviewAllowed(params) {
	if (params.review.approvalRequired && params.allowPermissionChanges !== true) throw new Error(`ClawHub skill update requires permission review (${params.review.reasons.join(", ")}); rerun with explicit permission-change approval.`);
}
async function performClawHubSkillInstall(params) {
	try {
		const registry = resolveClawHubBaseUrl(params.baseUrl);
		const sourceTrust = resolveRegistryTrust({
			registry,
			allowRegistries: params.allowRegistries,
			allowTrackedLegacyOrigin: params.force && Boolean(params.previousOrigin)
		});
		const { detail, version } = await resolveInstallVersion({
			slug: params.slug,
			version: params.version,
			baseUrl: params.baseUrl
		});
		const targetDir = resolveSkillInstallDir(params.workspaceDir, params.slug);
		if (!params.force && await fileExists(targetDir)) return {
			ok: false,
			error: `Skill already exists at ${targetDir}. Re-run with force/update.`
		};
		params.logger?.info?.(`Downloading ${params.slug}@${version} from ClawHub…`);
		const archive = await downloadClawHubSkillArchive({
			slug: params.slug,
			version,
			baseUrl: params.baseUrl
		});
		try {
			let extractedPermissions = null;
			let archiveScan = null;
			let updateReview = null;
			const install = await withExtractedArchiveRoot({
				archivePath: archive.archivePath,
				tempDirPrefix: "fased-skill-clawhub-",
				timeoutMs: 12e4,
				rootMarkers: ["SKILL.md"],
				onExtracted: async (rootDir) => {
					const scan = await scanSkillMarketplaceArchive(rootDir);
					archiveScan = scan;
					if (scan.blocked) return {
						ok: false,
						error: `ClawHub skill archive rejected: ${formatArchiveScanFindings(scan)}`
					};
					params.logger?.info?.(`Marketplace archive scan for ${params.slug}: ${formatArchiveScanFindings(scan)}`);
					const inspection = await inspectSkillMarketplaceManifest(rootDir);
					extractedPermissions = inspection.permissions;
					updateReview = buildMarketplaceUpdateReview({
						previousOrigin: params.previousOrigin,
						permissions: inspection.permissions,
						installScan: scan,
						isUpdate: params.force === true
					});
					assertUpdateReviewAllowed({
						review: updateReview,
						allowPermissionChanges: params.allowPermissionChanges
					});
					params.logger?.info?.(`Marketplace permissions for ${params.slug}: ${formatMarketplacePermissionSummary(inspection.permissions)}`);
					return await installExtractedSkill({
						workspaceDir: params.workspaceDir,
						slug: params.slug,
						extractedRoot: rootDir,
						mode: params.force ? "update" : "install",
						logger: params.logger
					});
				}
			});
			if (!install.ok) return install;
			if (!extractedPermissions) throw new Error("failed to inspect marketplace skill permissions");
			if (!archiveScan) throw new Error("failed to scan marketplace skill archive");
			if (!updateReview) throw new Error("failed to review marketplace skill update");
			const installedAt = Date.now();
			await writeClawHubSkillOrigin(install.targetDir, {
				version: 1,
				registry,
				slug: params.slug,
				installedVersion: version,
				installedAt,
				permissions: extractedPermissions,
				installScan: archiveScan,
				lastUpdateReview: updateReview
			});
			const lock = await readClawHubSkillsLockfile(params.workspaceDir);
			lock.skills[params.slug] = {
				version,
				installedAt
			};
			await writeClawHubSkillsLockfile(params.workspaceDir, lock);
			return {
				ok: true,
				slug: params.slug,
				version,
				targetDir: install.targetDir,
				sourceTrust,
				detail,
				permissions: extractedPermissions,
				installScan: archiveScan,
				updateReview
			};
		} finally {
			await archive.cleanup().catch(() => void 0);
		}
	} catch (err) {
		return {
			ok: false,
			error: err instanceof Error ? err.message : String(err)
		};
	}
}
async function installRequestedSkillFromClawHub(params) {
	try {
		return await performClawHubSkillInstall({
			...params,
			slug: validateRequestedSlug(params.slug)
		});
	} catch (err) {
		return {
			ok: false,
			error: err instanceof Error ? err.message : String(err)
		};
	}
}
async function installTrackedSkillFromClawHub(params) {
	try {
		return await performClawHubSkillInstall({
			...params,
			slug: normalizeTrackedSlug(params.slug)
		});
	} catch (err) {
		return {
			ok: false,
			error: err instanceof Error ? err.message : String(err)
		};
	}
}
async function previewTrackedSkillUpdateFromClawHub(params) {
	try {
		const sourceTrust = resolveRegistryTrust({
			registry: resolveClawHubBaseUrl(params.baseUrl),
			allowRegistries: params.allowRegistries,
			allowTrackedLegacyOrigin: Boolean(params.previousOrigin)
		});
		const { version } = await resolveInstallVersion({
			slug: params.slug,
			baseUrl: params.baseUrl
		});
		params.logger?.info?.(`Previewing ${params.slug}@${version} from ClawHub...`);
		const archive = await downloadClawHubSkillArchive({
			slug: params.slug,
			version,
			baseUrl: params.baseUrl
		});
		try {
			const targetDir = resolveSkillInstallDir(params.workspaceDir, params.slug);
			let permissions = null;
			let installScan = null;
			let updateReview = null;
			const extraction = await withExtractedArchiveRoot({
				archivePath: archive.archivePath,
				tempDirPrefix: "fased-skill-clawhub-preview-",
				timeoutMs: 12e4,
				rootMarkers: ["SKILL.md"],
				onExtracted: async (rootDir) => {
					const scan = await scanSkillMarketplaceArchive(rootDir);
					installScan = scan;
					const inspection = await inspectSkillMarketplaceManifest(rootDir);
					permissions = inspection.permissions;
					updateReview = buildMarketplaceUpdateReview({
						previousOrigin: params.previousOrigin,
						permissions: inspection.permissions,
						installScan: scan,
						isUpdate: true
					});
					return {
						ok: true,
						targetDir
					};
				}
			});
			if (!extraction.ok) return extraction;
			if (!permissions || !installScan || !updateReview) throw new Error("failed to preview marketplace skill update");
			return {
				ok: true,
				slug: params.slug,
				previousVersion: params.previousVersion,
				version,
				changed: params.previousVersion !== version,
				targetDir,
				sourceTrust,
				permissions,
				installScan,
				updateReview
			};
		} finally {
			await archive.cleanup().catch(() => void 0);
		}
	} catch (err) {
		return {
			ok: false,
			error: err instanceof Error ? err.message : String(err)
		};
	}
}
async function previewSkillInstallFromClawHub(params) {
	try {
		const slug = validateRequestedSlug(params.slug);
		const sourceTrust = resolveRegistryTrust({
			registry: resolveClawHubBaseUrl(params.baseUrl),
			allowRegistries: params.allowRegistries
		});
		const { detail, version } = await resolveInstallVersion({
			slug,
			version: params.version,
			baseUrl: params.baseUrl
		});
		const targetDir = resolveSkillInstallDir(params.workspaceDir, slug);
		if (await fileExists(targetDir)) return {
			ok: false,
			error: `Skill already exists at ${targetDir}. Use update for tracked ClawHub skills.`
		};
		params.logger?.info?.(`Previewing ${slug}@${version} from ClawHub...`);
		const archive = await downloadClawHubSkillArchive({
			slug,
			version,
			baseUrl: params.baseUrl
		});
		try {
			let permissions = null;
			let installScan = null;
			let updateReview = null;
			const extraction = await withExtractedArchiveRoot({
				archivePath: archive.archivePath,
				tempDirPrefix: "fased-skill-clawhub-install-preview-",
				timeoutMs: 12e4,
				rootMarkers: ["SKILL.md"],
				onExtracted: async (rootDir) => {
					const scan = await scanSkillMarketplaceArchive(rootDir);
					installScan = scan;
					const inspection = await inspectSkillMarketplaceManifest(rootDir);
					permissions = inspection.permissions;
					updateReview = buildMarketplaceUpdateReview({
						previousOrigin: null,
						permissions: inspection.permissions,
						installScan: scan,
						isUpdate: false
					});
					return {
						ok: true,
						targetDir
					};
				}
			});
			if (!extraction.ok) return extraction;
			if (!permissions || !installScan || !updateReview) throw new Error("failed to preview marketplace skill install");
			return {
				ok: true,
				slug,
				version,
				targetDir,
				sourceTrust,
				detail,
				permissions,
				installScan,
				updateReview
			};
		} finally {
			await archive.cleanup().catch(() => void 0);
		}
	} catch (err) {
		return {
			ok: false,
			error: err instanceof Error ? err.message : String(err)
		};
	}
}
async function resolveTrackedUpdateTarget(params) {
	const origin = await readClawHubSkillOrigin(resolveSkillInstallDir(params.workspaceDir, params.slug)) ?? null;
	if (!origin && !params.lock.skills[params.slug]) return {
		ok: false,
		slug: params.slug,
		error: `Skill "${params.slug}" is not tracked as a ClawHub install.`
	};
	return {
		ok: true,
		slug: params.slug,
		baseUrl: origin?.registry ?? params.baseUrl,
		previousVersion: origin?.installedVersion ?? params.lock.skills[params.slug]?.version ?? null,
		origin
	};
}
async function installSkillFromClawHub(params) {
	return await installRequestedSkillFromClawHub(params);
}
async function updateSkillsFromClawHub(params) {
	const lock = await readClawHubSkillsLockfile(params.workspaceDir);
	const slugs = params.slug ? [await resolveRequestedUpdateSlug({
		workspaceDir: params.workspaceDir,
		requestedSlug: params.slug,
		lock
	})] : Object.keys(lock.skills).map((slug) => normalizeTrackedSlug(slug));
	const results = [];
	for (const slug of slugs) {
		const tracked = await resolveTrackedUpdateTarget({
			workspaceDir: params.workspaceDir,
			slug,
			lock,
			baseUrl: params.baseUrl
		});
		if (!tracked.ok) {
			results.push({
				ok: false,
				error: tracked.error
			});
			continue;
		}
		const install = await installTrackedSkillFromClawHub({
			workspaceDir: params.workspaceDir,
			slug: tracked.slug,
			baseUrl: tracked.baseUrl,
			allowRegistries: params.allowRegistries,
			allowPermissionChanges: params.allowPermissionChanges,
			force: true,
			previousOrigin: tracked.origin,
			logger: params.logger
		});
		if (!install.ok) {
			results.push(install);
			continue;
		}
		results.push({
			ok: true,
			slug: tracked.slug,
			previousVersion: tracked.previousVersion,
			version: install.version,
			changed: tracked.previousVersion !== install.version,
			targetDir: install.targetDir,
			sourceTrust: install.sourceTrust,
			permissions: install.permissions,
			installScan: install.installScan,
			updateReview: install.updateReview
		});
	}
	return results;
}
async function previewSkillsUpdateFromClawHub(params) {
	const lock = await readClawHubSkillsLockfile(params.workspaceDir);
	const slugs = params.slug ? [await resolveRequestedUpdateSlug({
		workspaceDir: params.workspaceDir,
		requestedSlug: params.slug,
		lock
	})] : Object.keys(lock.skills).map((slug) => normalizeTrackedSlug(slug));
	const results = [];
	for (const slug of slugs) {
		const tracked = await resolveTrackedUpdateTarget({
			workspaceDir: params.workspaceDir,
			slug,
			lock,
			baseUrl: params.baseUrl
		});
		if (!tracked.ok) {
			results.push({
				ok: false,
				error: tracked.error
			});
			continue;
		}
		results.push(await previewTrackedSkillUpdateFromClawHub({
			workspaceDir: params.workspaceDir,
			slug: tracked.slug,
			baseUrl: tracked.baseUrl,
			allowRegistries: params.allowRegistries,
			previousVersion: tracked.previousVersion,
			previousOrigin: tracked.origin,
			logger: params.logger
		}));
	}
	return results;
}
async function readTrackedClawHubSkillSlugs(workspaceDir) {
	const lock = await readClawHubSkillsLockfile(workspaceDir);
	return Object.keys(lock.skills).toSorted();
}

//#endregion
export { readTrackedClawHubSkillSlugs as a, formatArchiveScanFindings as c, readClawHubSkillOrigin as i, formatMarketplacePermissionSummary as l, previewSkillInstallFromClawHub as n, searchSkillsFromClawHub as o, previewSkillsUpdateFromClawHub as r, updateSkillsFromClawHub as s, installSkillFromClawHub as t, fetchClawHubSkillDetail as u };