import { a as root } from "./secure-temp-dir-XAWcZnE2.js";
import "./security-runtime-oZx6LY64.js";
import { i as statRequiredDirectory, n as readAbsolutePath, r as resolveCanonicalReadPath, t as classifyFsSafeReadError } from "./path-errors-DvHNIK7J.js";
import path from "node:path";
import { spawn } from "node:child_process";
import crypto from "node:crypto";
//#region extensions/file-transfer/src/node-host/dir-fetch.ts
const DIR_FETCH_HARD_MAX_BYTES = 16 * 1024 * 1024;
const DIR_FETCH_DEFAULT_MAX_BYTES = 8 * 1024 * 1024;
function clampMaxBytes(input) {
	if (typeof input !== "number" || !Number.isFinite(input) || input <= 0) return DIR_FETCH_DEFAULT_MAX_BYTES;
	return Math.min(Math.floor(input), DIR_FETCH_HARD_MAX_BYTES);
}
function classifyFsError(err) {
	const safeCode = classifyFsSafeReadError(err);
	if (safeCode) return safeCode;
	if (err?.code === "ENOENT") return "NOT_FOUND";
	return "READ_ERROR";
}
async function preflightDu(dirPath, maxBytes) {
	const heuristicKb = Math.ceil(maxBytes * 4 / 1024);
	return new Promise((resolve) => {
		const du = spawn("du", ["-sk", dirPath], { stdio: [
			"ignore",
			"pipe",
			"ignore"
		] });
		let output = "";
		du.stdout.on("data", (chunk) => {
			output += chunk.toString();
		});
		du.on("close", (code) => {
			if (code !== 0) {
				resolve(true);
				return;
			}
			const match = /^(\d+)/.exec(output.trim());
			if (!match) {
				resolve(true);
				return;
			}
			resolve(Number.parseInt(match[1], 10) <= heuristicKb);
		});
		du.on("error", () => {
			resolve(true);
		});
	});
}
async function listTarEntries(tarBuffer) {
	return new Promise((resolve) => {
		const child = spawn("tar", ["-tzf", "-"], { stdio: [
			"pipe",
			"pipe",
			"ignore"
		] });
		let stdoutBuf = "";
		let aborted = false;
		const watchdog = setTimeout(() => {
			aborted = true;
			try {
				child.kill("SIGKILL");
			} catch {}
			resolve([]);
		}, 1e4);
		child.stdout.on("data", (chunk) => {
			stdoutBuf += chunk.toString();
			if (stdoutBuf.length > 32 * 1024 * 1024) {
				aborted = true;
				try {
					child.kill("SIGKILL");
				} catch {}
				clearTimeout(watchdog);
				resolve([]);
			}
		});
		child.on("close", (code) => {
			clearTimeout(watchdog);
			if (aborted) return;
			if (code !== 0) {
				resolve([]);
				return;
			}
			resolve(stdoutBuf.split("\n").map((line) => line.replace(/\\/gu, "/").replace(/^\.\//u, "").replace(/\/$/u, "")).filter((line) => line.length > 0));
		});
		child.on("error", () => {
			clearTimeout(watchdog);
			if (!aborted) resolve([]);
		});
		child.stdin.end(tarBuffer);
	});
}
async function listTreeEntries(root$1, maxEntries) {
	const results = [];
	const rootHandle = await root(root$1);
	async function visit(relativeDir) {
		const entries = await rootHandle.list(relativeDir, { withFileTypes: true });
		entries.sort((left, right) => left.name.localeCompare(right.name));
		for (const entry of entries) {
			const rel = path.posix.join(relativeDir === "." ? "" : relativeDir, entry.name);
			results.push(rel);
			if (results.length > maxEntries) return false;
			if (entry.isDirectory) {
				if (!await visit(rel)) return false;
			}
		}
		return true;
	}
	return await visit(".") ? results : "TOO_MANY";
}
async function handleDirFetch(params) {
	const requestedPath = readAbsolutePath(params.path);
	if (typeof requestedPath !== "string") return requestedPath;
	const maxBytes = clampMaxBytes(params.maxBytes);
	params.includeDotfiles;
	const followSymlinks = params.followSymlinks === true;
	const preflightOnly = params.preflightOnly === true;
	const canonical = await resolveCanonicalReadPath({
		requestedPath,
		followSymlinks,
		classifyError: classifyFsError,
		notFoundMessage: "directory not found"
	});
	if (typeof canonical !== "string") return canonical;
	const directory = await statRequiredDirectory(canonical, classifyFsError);
	if (!directory.ok) return directory;
	if (preflightOnly) try {
		const entries = await listTreeEntries(canonical, 5e3);
		if (entries === "TOO_MANY") return {
			ok: false,
			code: "TREE_TOO_LARGE",
			message: "directory tree exceeds 5000 entries during preflight",
			canonicalPath: canonical
		};
		return {
			ok: true,
			path: canonical,
			tarBase64: "",
			tarBytes: 0,
			sha256: "",
			fileCount: entries.length,
			entries,
			preflightOnly: true
		};
	} catch (err) {
		return {
			ok: false,
			code: classifyFsError(err),
			message: `preflight readdir failed: ${String(err)}`,
			canonicalPath: canonical
		};
	}
	if (!await preflightDu(canonical, maxBytes)) return {
		ok: false,
		code: "TREE_TOO_LARGE",
		message: `directory tree exceeds estimated size limit (${maxBytes} bytes raw)`,
		canonicalPath: canonical
	};
	const tarArgs = [
		"-czf",
		"-",
		"-C",
		canonical,
		"."
	];
	const TAR_HARD_TIMEOUT_MS = 6e4;
	const tarBuffer = await new Promise((resolve) => {
		const child = spawn(process.platform !== "win32" ? "/usr/bin/tar" : "tar", tarArgs, { stdio: [
			"ignore",
			"pipe",
			"pipe"
		] });
		const chunks = [];
		let totalBytes = 0;
		let aborted = false;
		const watchdog = setTimeout(() => {
			if (aborted) return;
			aborted = true;
			try {
				child.kill("SIGKILL");
			} catch {}
			resolve("TIMEOUT");
		}, TAR_HARD_TIMEOUT_MS);
		child.stdout.on("data", (chunk) => {
			if (aborted) return;
			totalBytes += chunk.byteLength;
			if (totalBytes > maxBytes) {
				aborted = true;
				clearTimeout(watchdog);
				child.kill("SIGTERM");
				resolve("TOO_LARGE");
				return;
			}
			chunks.push(chunk);
		});
		child.on("close", (code) => {
			clearTimeout(watchdog);
			if (aborted) return;
			if (code !== 0) {
				resolve("ERROR");
				return;
			}
			resolve(Buffer.concat(chunks));
		});
		child.on("error", () => {
			clearTimeout(watchdog);
			if (!aborted) resolve("ERROR");
		});
	});
	if (tarBuffer === "TOO_LARGE") return {
		ok: false,
		code: "TREE_TOO_LARGE",
		message: `tarball exceeded ${maxBytes} byte limit mid-stream`,
		canonicalPath: canonical
	};
	if (tarBuffer === "TIMEOUT") return {
		ok: false,
		code: "READ_ERROR",
		message: "tar command exceeded 60s wall-clock timeout (slow filesystem or symlink loop?)",
		canonicalPath: canonical
	};
	if (tarBuffer === "ERROR") return {
		ok: false,
		code: "READ_ERROR",
		message: "tar command failed",
		canonicalPath: canonical
	};
	const sha256 = crypto.createHash("sha256").update(tarBuffer).digest("hex");
	const tarBase64 = tarBuffer.toString("base64");
	const tarBytes = tarBuffer.byteLength;
	const entries = await listTarEntries(tarBuffer);
	return {
		ok: true,
		path: canonical,
		tarBase64,
		tarBytes,
		sha256,
		fileCount: entries.length,
		entries
	};
}
//#endregion
export { handleDirFetch };
