const require_rolldown_runtime = require("./rolldown-runtime-D_mwlA32.cjs");
const require_logger = require("./logger-DuG3amVa.cjs");
const require_esm = require("./esm-4Cu04ouJ.cjs");
const require_secureTempFiles = require("./secureTempFiles-CgQrWvRe.cjs");
let fs_promises = require("fs/promises");
fs_promises = require_rolldown_runtime.__toESM(fs_promises, 1);
let path = require("path");
path = require_rolldown_runtime.__toESM(path, 1);
let util = require("util");
let child_process = require("child_process");
//#region src/ruby/rubyUtils.ts
var rubyUtils_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
	getSysExecutable: () => getSysExecutable,
	runRuby: () => runRuby,
	state: () => state,
	tryPath: () => tryPath,
	validateRubyPath: () => validateRubyPath
});
const execFileAsync = (0, util.promisify)(child_process.execFile);
function logStderr(stderr) {
	for (const line of stderr.split(/\r?\n/)) {
		const message = line.trim();
		if (!message) continue;
		const levelMatch = /^(DEBUG|INFO|WARN|WARNING|ERROR|FATAL)\b[: ]?/i.exec(message);
		if (levelMatch) switch (levelMatch[1].toUpperCase()) {
			case "DEBUG":
				require_logger.logger.debug(line);
				continue;
			case "INFO":
				require_logger.logger.info(line);
				continue;
			case "WARN":
			case "WARNING":
				require_logger.logger.warn(line);
				continue;
			default:
				require_logger.logger.error(line);
				continue;
		}
		if (/\b(error|exception|fatal|failed|failure)\b/i.test(message) || /(?:Error|Exception)\b/.test(message) || /^from\s.+:\d+/.test(message)) require_logger.logger.error(line);
		else require_logger.logger.warn(line);
	}
}
/**
* Global state for Ruby executable path caching.
* Ensures consistent Ruby executable usage across multiple provider instances.
*/
const state = {
	cachedRubyPath: null,
	validationPromise: null,
	validatingPath: null
};
/**
* Attempts to find Ruby using Windows 'where' command.
* Only applicable on Windows platforms.
* @returns The validated Ruby executable path, or null if not found
*/
async function tryWindowsWhere() {
	try {
		const output = (await execFileAsync("where", ["ruby"])).stdout.trim();
		if (!output) {
			require_logger.logger.debug("Windows 'where ruby' returned empty output");
			return null;
		}
		const paths = output.split("\n").filter((path$2) => path$2.trim());
		for (const rubyPath of paths) {
			const trimmedPath = rubyPath.trim();
			if (!trimmedPath.endsWith(".exe")) continue;
			const validated = await tryPath(trimmedPath);
			if (validated) return validated;
		}
	} catch (error) {
		const errorMsg = error instanceof Error ? error.message : String(error);
		require_logger.logger.debug(`Windows 'where ruby' failed: ${errorMsg}`);
		if (errorMsg.includes("Access is denied") || errorMsg.includes("EACCES")) require_logger.logger.warn(`Permission denied when searching for Ruby: ${errorMsg}`);
	}
	return null;
}
/**
* Attempts to get Ruby executable path by running Ruby commands.
* Uses RbConfig.ruby to get the actual Ruby executable path.
* @param commands - Array of Ruby command names to try (e.g., ['ruby'])
* @returns The Ruby executable path, or null if all commands fail
*/
async function tryRubyCommands(commands) {
	for (const cmd of commands) try {
		const executablePath = (await execFileAsync(cmd, ["-e", "puts RbConfig.ruby"])).stdout.trim();
		if (executablePath && executablePath !== "None") return executablePath;
	} catch (error) {
		const errorMsg = error instanceof Error ? error.message : String(error);
		require_logger.logger.debug(`Ruby command "${cmd}" failed: ${errorMsg}`);
		if (errorMsg.includes("Access is denied") || errorMsg.includes("EACCES") || errorMsg.includes("EPERM")) require_logger.logger.warn(`Permission denied when trying Ruby command "${cmd}": ${errorMsg}`);
	}
	return null;
}
/**
* Attempts to validate Ruby commands directly as a final fallback.
* Validates each command by running it with --version.
* @param commands - Array of Ruby command names to try (e.g., ['ruby'])
* @returns The validated Ruby executable path, or null if all commands fail
*/
async function tryDirectCommands(commands) {
	for (const cmd of commands) try {
		const validated = await tryPath(cmd);
		if (validated) return validated;
	} catch (error) {
		const errorMsg = error instanceof Error ? error.message : String(error);
		require_logger.logger.debug(`Direct command "${cmd}" failed: ${errorMsg}`);
		if (errorMsg.includes("Access is denied") || errorMsg.includes("EACCES") || errorMsg.includes("EPERM")) require_logger.logger.warn(`Permission denied when trying Ruby command "${cmd}": ${errorMsg}`);
	}
	return null;
}
/**
* Attempts to get the Ruby executable path using platform-appropriate strategies.
* @returns The Ruby executable path if successful, or null if failed.
*/
async function getSysExecutable() {
	if (process.platform === "win32") {
		const whereResult = await tryWindowsWhere();
		if (whereResult) return whereResult;
		const sysResult = await tryRubyCommands(["ruby"]);
		if (sysResult) return sysResult;
		return await tryDirectCommands(["ruby"]);
	} else return await tryRubyCommands(["ruby"]);
}
/**
* Attempts to validate a Ruby executable path.
* @param path - The path to the Ruby executable to test.
* @returns The validated path if successful, or null if invalid.
*/
async function tryPath(path$3) {
	let timeoutId;
	try {
		const timeoutPromise = new Promise((_, reject) => {
			timeoutId = setTimeout(() => reject(/* @__PURE__ */ new Error("Command timed out")), 2500);
		});
		const result = await Promise.race([execFileAsync(path$3, ["--version"]), timeoutPromise]);
		if (timeoutId) clearTimeout(timeoutId);
		if (result.stdout.trim().toLowerCase().includes("ruby")) return path$3;
		return null;
	} catch {
		if (timeoutId) clearTimeout(timeoutId);
		return null;
	}
}
/**
* Validates and caches the Ruby executable path.
*
* @param rubyPath - Path to the Ruby executable.
* @param isExplicit - If true, only tries the provided path.
* @returns Validated Ruby executable path.
* @throws {Error} If no valid Ruby executable is found.
*/
async function validateRubyPath(rubyPath, isExplicit) {
	if (state.cachedRubyPath && state.validatingPath === rubyPath) return state.cachedRubyPath;
	if (state.validatingPath !== rubyPath) {
		state.cachedRubyPath = null;
		state.validationPromise = null;
		state.validatingPath = rubyPath;
	}
	if (!state.validationPromise) state.validationPromise = (async () => {
		try {
			const primaryPath = await tryPath(rubyPath);
			if (primaryPath) {
				state.cachedRubyPath = primaryPath;
				state.validationPromise = null;
				return primaryPath;
			}
			if (isExplicit) {
				const error = /* @__PURE__ */ new Error(`Ruby not found. Tried "${rubyPath}" Please ensure Ruby is installed and set the PROMPTFOO_RUBY environment variable to your Ruby executable path (e.g., '${process.platform === "win32" ? "C:\\Ruby32\\bin\\ruby.exe" : "/usr/bin/ruby"}').`);
				state.validationPromise = null;
				throw error;
			}
			const detectedPath = await getSysExecutable();
			if (detectedPath) {
				state.cachedRubyPath = detectedPath;
				state.validationPromise = null;
				return detectedPath;
			}
			const error = /* @__PURE__ */ new Error(`Ruby not found. Tried "${rubyPath}", ruby executable detection, and fallback commands. Please ensure Ruby is installed and set the PROMPTFOO_RUBY environment variable to your Ruby executable path (e.g., '${process.platform === "win32" ? "C:\\Ruby32\\bin\\ruby.exe" : "/usr/bin/ruby"}').`);
			state.validationPromise = null;
			throw error;
		} catch (error) {
			state.validationPromise = null;
			throw error;
		}
	})();
	return state.validationPromise;
}
/**
* Runs a Ruby script with the specified method and arguments.
*
* @param scriptPath - The path to the Ruby script to run.
* @param method - The name of the method to call in the Ruby script.
* @param args - An array of arguments to pass to the Ruby script.
* @param options - Optional settings for running the Ruby script.
* @param options.rubyExecutable - Optional path to the Ruby executable.
* @returns A promise that resolves to the output of the Ruby script.
* @throws An error if there's an issue running the Ruby script or parsing its output.
*/
async function runRuby(scriptPath, method, args, options = {}) {
	const absPath = path.default.resolve(scriptPath);
	const customPath = options.rubyExecutable || require_logger.getEnvString("PROMPTFOO_RUBY");
	let rubyPath = customPath || "ruby";
	let tempDirectory;
	rubyPath = await validateRubyPath(rubyPath, typeof customPath === "string");
	const wrapperPath = path.default.join(require_esm.getWrapperDir("ruby"), "wrapper.rb");
	try {
		tempDirectory = await require_secureTempFiles.createSecureTempDirectory("promptfoo-ruby-");
		const tempJsonPath = await require_secureTempFiles.writeSecureTempFile(tempDirectory, "input.json", require_logger.safeJsonStringify(args));
		const outputPath = await require_secureTempFiles.writeSecureTempFile(tempDirectory, "output.json", "");
		require_logger.logger.debug("[Ruby] Running script", {
			scriptPath: absPath,
			method
		});
		const { stdout, stderr } = await execFileAsync(rubyPath, [
			wrapperPath,
			absPath,
			method,
			tempJsonPath,
			outputPath
		]);
		if (stdout) require_logger.logger.debug(stdout.trim());
		if (stderr) logStderr(stderr);
		const output = await fs_promises.default.readFile(outputPath, "utf-8");
		require_logger.logger.debug("[Ruby] Script returned a result", { scriptPath: absPath });
		let result;
		try {
			result = JSON.parse(output);
		} catch (error) {
			throw new Error(`Invalid JSON returned by Ruby script: ${error.message}\nStack Trace: ${error.stack}`);
		}
		if (result?.type !== "final_result") throw new Error("The Ruby script `call_api` function must return a hash with an `output`");
		return result.data;
	} catch (error) {
		require_logger.logger.error(`Error running Ruby script: ${error.message}\nStack Trace: ${error.stack || "No Ruby traceback available"}`);
		throw new Error(`Error running Ruby script: ${error.message}\nStack Trace: ${error.stack || "No Ruby traceback available"}`);
	} finally {
		if (tempDirectory) try {
			await require_secureTempFiles.removeSecureTempDirectory(tempDirectory);
		} catch (error) {
			require_logger.logger.error(`Error removing temporary Ruby directory: ${error}`);
		}
	}
}
//#endregion
Object.defineProperty(exports, "rubyUtils_exports", {
	enumerable: true,
	get: function() {
		return rubyUtils_exports;
	}
});
Object.defineProperty(exports, "runRuby", {
	enumerable: true,
	get: function() {
		return runRuby;
	}
});

//# sourceMappingURL=rubyUtils-B04vwQhA.cjs.map