import { execFileSync } from "node:child_process"; import { existsSync, copyFileSync, mkdirSync, unlinkSync, } from "node:fs"; import { join } from "node:path"; import { randomBytes } from "node:crypto"; import { CONFIG } from "./config.js"; export function ensureBaseImage() { if (!existsSync(CONFIG.baseRootfs)) { throw new Error( `Base rootfs not found at ${CONFIG.baseRootfs}. Run 'fireclaw setup' first.` ); } if (!existsSync(CONFIG.kernelPath)) { throw new Error( `Kernel not found at ${CONFIG.kernelPath}. Run 'fireclaw setup' first.` ); } } export function ensureSshKeypair() { if (!existsSync(CONFIG.sshKeyPath)) { execFileSync("ssh-keygen", [ "-t", "ed25519", "-f", CONFIG.sshKeyPath, "-N", "", "-C", "fireclaw", ]); } } export function createRunCopy(vmId: string): string { mkdirSync(CONFIG.runsDir, { recursive: true }); const dest = join(CONFIG.runsDir, `${vmId}.ext4`); copyFileSync(CONFIG.baseRootfs, dest); return dest; } export function injectSshKey(rootfsPath: string) { const mountPoint = `/tmp/fireclaw-mount-${randomBytes(4).toString("hex")}`; mkdirSync(mountPoint, { recursive: true }); try { execFileSync("sudo", ["mount", "-o", "loop", rootfsPath, mountPoint], { stdio: "pipe", }); execFileSync("sudo", ["mkdir", "-p", join(mountPoint, "root/.ssh")], { stdio: "pipe", }); execFileSync( "sudo", [ "cp", CONFIG.sshPubKeyPath, join(mountPoint, "root/.ssh/authorized_keys"), ], { stdio: "pipe" } ); execFileSync( "sudo", ["chmod", "600", join(mountPoint, "root/.ssh/authorized_keys")], { stdio: "pipe" } ); execFileSync( "sudo", ["chmod", "700", join(mountPoint, "root/.ssh")], { stdio: "pipe" } ); } finally { try { execFileSync("sudo", ["umount", mountPoint], { stdio: "pipe" }); } catch { // Best effort } try { execFileSync("rmdir", [mountPoint], { stdio: "pipe" }); } catch { // Best effort } } } export function deleteRunCopy(rootfsPath: string) { try { unlinkSync(rootfsPath); } catch { // Already gone } }