import { execSync } from "node:child_process";
export type Language =
| "javascript"
| "typescript"
| "python"
| "shell"
| "ruby"
| "go"
| "rust"
| "php"
| "perl"
| "r";
export interface RuntimeInfo {
command: string;
available: boolean;
version: string;
preferred: boolean;
}
export interface RuntimeMap {
javascript: string;
typescript: string | null;
python: string | null;
shell: string;
ruby: string | null;
go: string | null;
rust: string | null;
php: string | null;
perl: string | null;
r: string | null;
}
function commandExists(cmd: string): boolean {
try {
execSync(`command -v ${cmd} 2>/dev/null`, { stdio: "pipe" });
return true;
} catch {
return false;
}
}
function getVersion(cmd: string): string {
try {
return execSync(`${cmd} --version 2>/dev/null`, {
encoding: "utf-8",
timeout: 5000,
})
.trim()
.split("\n")[0];
} catch {
return "unknown";
}
}
export function detectRuntimes(): RuntimeMap {
const hasBun = commandExists("bun");
return {
javascript: hasBun ? "bun" : "node",
typescript: hasBun
? "bun"
: commandExists("tsx")
? "tsx"
: commandExists("ts-node")
? "ts-node"
: null,
python: commandExists("python3")
? "python3"
: commandExists("python")
? "python"
: null,
shell: commandExists("bash") ? "bash" : "sh",
ruby: commandExists("ruby") ? "ruby" : null,
go: commandExists("go") ? "go" : null,
rust: commandExists("rustc") ? "rustc" : null,
php: commandExists("php") ? "php" : null,
perl: commandExists("perl") ? "perl" : null,
r: commandExists("Rscript")
? "Rscript"
: commandExists("r")
? "r"
: null,
};
}
export function hasBunRuntime(): boolean {
return commandExists("bun");
}
export function getRuntimeSummary(runtimes: RuntimeMap): string {
const lines: string[] = [];
const bunPreferred = runtimes.javascript === "bun";
lines.push(
` JavaScript: ${runtimes.javascript} (${getVersion(runtimes.javascript)})${bunPreferred ? " ⚡" : ""}`,
);
if (runtimes.typescript) {
lines.push(
` TypeScript: ${runtimes.typescript} (${getVersion(runtimes.typescript)})`,
);
} else {
lines.push(
` TypeScript: not available (install bun, tsx, or ts-node)`,
);
}
if (runtimes.python) {
lines.push(
` Python: ${runtimes.python} (${getVersion(runtimes.python)})`,
);
} else {
lines.push(` Python: not available`);
}
lines.push(
` Shell: ${runtimes.shell} (${getVersion(runtimes.shell)})`,
);
// Optional runtimes — only show if available
if (runtimes.ruby)
lines.push(
` Ruby: ${runtimes.ruby} (${getVersion(runtimes.ruby)})`,
);
if (runtimes.go)
lines.push(` Go: ${runtimes.go} (${getVersion(runtimes.go)})`);
if (runtimes.rust)
lines.push(
` Rust: ${runtimes.rust} (${getVersion(runtimes.rust)})`,
);
if (runtimes.php)
lines.push(
` PHP: ${runtimes.php} (${getVersion(runtimes.php)})`,
);
if (runtimes.perl)
lines.push(
` Perl: ${runtimes.perl} (${getVersion(runtimes.perl)})`,
);
if (runtimes.r)
lines.push(` R: ${runtimes.r} (${getVersion(runtimes.r)})`);
if (!bunPreferred) {
lines.push("");
lines.push(
" Tip: Install Bun for 3-5x faster JS/TS execution → https://bun.sh",
);
}
return lines.join("\n");
}
export function getAvailableLanguages(runtimes: RuntimeMap): Language[] {
const langs: Language[] = ["javascript", "shell"];
if (runtimes.typescript) langs.push("typescript");
if (runtimes.python) langs.push("python");
if (runtimes.ruby) langs.push("ruby");
if (runtimes.go) langs.push("go");
if (runtimes.rust) langs.push("rust");
if (runtimes.php) langs.push("php");
if (runtimes.perl) langs.push("perl");
if (runtimes.r) langs.push("r");
return langs;
}
export function buildCommand(
runtimes: RuntimeMap,
language: Language,
filePath: string,
): string[] {
switch (language) {
case "javascript":
return runtimes.javascript === "bun"
? ["bun", "run", filePath]
: ["node", filePath];
case "typescript":
if (!runtimes.typescript) {
throw new Error(
"No TypeScript runtime available. Install one of: bun (recommended), tsx (npm i -g tsx), or ts-node.",
);
}
if (runtimes.typescript === "bun") return ["bun", "run", filePath];
if (runtimes.typescript === "tsx") return ["tsx", filePath];
return ["ts-node", filePath];
case "python":
if (!runtimes.python) {
throw new Error(
"No Python runtime available. Install python3 or python.",
);
}
return [runtimes.python, filePath];
case "shell":
return [runtimes.shell, filePath];
case "ruby":
if (!runtimes.ruby) {
throw new Error("Ruby not available. Install ruby.");
}
return [runtimes.ruby, filePath];
case "go":
if (!runtimes.go) {
throw new Error("Go not available. Install go.");
}
return ["go", "run", filePath];
case "rust": {
if (!runtimes.rust) {
throw new Error(
"Rust not available. Install rustc via https://rustup.rs",
);
}
// Rust needs compile + run — handled specially in executor
return ["__rust_compile_run__", filePath];
}
case "php":
if (!runtimes.php) {
throw new Error("PHP not available. Install php.");
}
return ["php", filePath];
case "perl":
if (!runtimes.perl) {
throw new Error("Perl not available. Install perl.");
}
return ["perl", filePath];
case "r":
if (!runtimes.r) {
throw new Error("R not available. Install R / Rscript.");
}
return [runtimes.r, filePath];
}
}