#!/usr/bin/env bash # ============================================================================== # Hermes Portable Rescue — USB Image Assembler # ============================================================================== # # Builds the final USB boot image from upstream build artifacts. # # Usage: # ./build/usb-image.sh [options] # # Options: # --output PATH Output path for the disk image (default: build/hermes-rescue.img) # --iso-only Build only the ISO, not the full disk image # --write-device DEV Write directly to a USB device (e.g. /dev/sdb) # --force Skip all safety confirmations # --skip-validate Skip artifact validation (for development) # --verbose Verbose output # --clean Clean build dirs before starting # # Dependencies: # - build:hermes-runtime → src/hermes/ (Hermes agent runtime) # - build:hardware-inventory → src/diagnostics/hardware.py # - build:bsod-analyzer → src/diagnostics/bsod.py # - build:driver-updater → src/diagnostics/drivers.py # - build:backup-restore → src/diagnostics/backup.py # - build:stress-tests → src/diagnostics/stress.py # - research:boot-env → research/boot-env.md (defines the boot environment) # # Output: # - build/hermes-rescue.img — Full disk image (Ventoy + Rescue ISO + Tools) # - build/hermes-rescue.iso — Bootable rescue ISO # # ============================================================================== set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" # ─── Configuration ──────────────────────────────────────────────────────────── # These can be overridden via environment variables OUTPUT_IMAGE="${OUTPUT_IMAGE:-"$PROJECT_DIR/build/hermes-rescue.img"}" OUTPUT_ISO="${OUTPUT_ISO:-"$PROJECT_DIR/build/hermes-rescue.iso"}" BUILD_DIR="${BUILD_DIR:-"$PROJECT_DIR/build/usb-staging"}" HERMES_SRC="${HERMES_SRC:-"$PROJECT_DIR/src/hermes"}" DIAG_SRC="${DIAG_SRC:-"$PROJECT_DIR/src/diagnostics"}" TOOLS_SRC="${TOOLS_SRC:-"$PROJECT_DIR/src/tools"}" VENTOY_JSON="${VENTOY_JSON:-"$PROJECT_DIR/build/ventoy.json"}" # Expected artifact paths (upstream deliverables) declare -A ARTIFACTS=( ["Hermes runtime"]="$HERMES_SRC/agent/hermes" ["Hermes config"]="$HERMES_SRC/config.yaml" ["Autorun script"]="$HERMES_SRC/autorun.sh" ["Hardware inventory"]="$DIAG_SRC/hardware.py" ["BSOD analyzer"]="$DIAG_SRC/bsod.py" ["Driver updater"]="$DIAG_SRC/drivers.py" ["Backup/restore"]="$DIAG_SRC/backup.py" ["Stress tests"]="$DIAG_SRC/stress.py" ) # Boot environment configuration BOOT_ENV="${BOOT_ENV:-linux}" # 'linux' or 'winpe' — set by research:boot-env ISO_LABEL="HERMES_RESCUE" # Image sizes (can be overridden via env) IMAGE_SIZE_MB="${IMAGE_SIZE_MB:-8192}" # 8 GB default EFI_SIZE_MB="${EFI_SIZE_MB:-512}" # 512 MB EFI partition # ─── Colors ──────────────────────────────────────────────────────────────────── RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color info() { echo -e "${BLUE}[INFO]${NC} $*"; } ok() { echo -e "${GREEN}[OK]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*" >&2; } error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } die() { error "$*"; exit 1; } # ─── Flags ──────────────────────────────────────────────────────────────────── DO_CLEAN=false SKIP_VALIDATE=false FORCE=false VERBOSE=false ISO_ONLY=false WRITE_DEVICE="" while [[ $# -gt 0 ]]; do case "$1" in --output) OUTPUT_IMAGE="$2"; shift 2 ;; --iso-only) ISO_ONLY=true; shift ;; --write-device) WRITE_DEVICE="$2"; shift 2 ;; --force) FORCE=true; shift ;; --skip-validate) SKIP_VALIDATE=true; shift ;; --verbose) VERBOSE=true; shift ;; --clean) DO_CLEAN=true; shift ;; -h|--help) head -30 "$0"; exit 0 ;; *) die "Unknown option: $1 (use --help)" ;; esac done # ─── Prerequisite Checks ────────────────────────────────────────────────────── check_command() { if ! command -v "$1" &>/dev/null; then die "Required command '$1' not found. Install it first." fi $VERBOSE && info " ✓ $1 found at $(command -v "$1")" } prerequisites() { info "Checking prerequisites..." # Core build tools check_command mkisofs check_command xorriso check_command grub-mkrescue 2>/dev/null || check_command grub2-mkrescue check_command dd check_command parted check_command mkfs.vfat check_command mkfs.ext4 # Ventoy (optional — needed only for full disk image) if ! $ISO_ONLY; then if command -v Ventoy2Disk.sh &>/dev/null; then VENTOY_CMD="Ventoy2Disk.sh" elif [ -f ./ventoy/Ventoy2Disk.sh ]; then VENTOY_CMD="./ventoy/Ventoy2Disk.sh" else warn "Ventoy2Disk.sh not found — will build ISO only (no Ventoy disk image)." fi fi ok "All prerequisite commands available." } validate_artifacts() { $SKIP_VALIDATE && { warn "Skipping artifact validation."; return 0; } info "Validating upstream build artifacts..." local missing=0 for name in "${!ARTIFACTS[@]}"; do local path="${ARTIFACTS[$name]}" if [ -f "$path" ] || [ -d "$path" ] || [ -x "$path" ] 2>/dev/null; then $VERBOSE && ok " Found: $name → $path" else warn " Missing: $name → $path" missing=$((missing + 1)) fi done # Check boot environment research local boot_env_doc="$PROJECT_DIR/research/boot-env.md" if [ -f "$boot_env_doc" ]; then $VERBOSE && ok " Found: boot environment research → $boot_env_doc" else warn " Missing: boot environment research → $boot_env_doc (will use default: $BOOT_ENV)" fi if [ "$missing" -gt 0 ]; then warn "Found $missing missing artifact(s). Build may be incomplete." if ! $FORCE; then echo "" warn "Run with --force to continue anyway, or build upstream tasks first." echo "" info "Upstream tasks to complete:" echo " build:hermes-runtime (t_c816e37d)" echo " build:hardware-inventory (t_1215a5f0)" echo " build:bsod-analyzer (t_10103a48)" echo " build:driver-updater (t_2afd37ec)" echo " build:backup-restore (t_0b33ea13)" echo " build:stress-tests (t_f817392d)" die "Missing artifacts. Build cannot proceed." fi else ok "All upstream artifacts present." fi } # ─── Directory Setup ────────────────────────────────────────────────────────── setup_directories() { info "Setting up staging directories..." if $DO_CLEAN && [ -d "$BUILD_DIR" ]; then info "Cleaning existing build directory..." rm -rf "$BUILD_DIR" fi mkdir -p "$BUILD_DIR"/{ISO,hermes/{agent,tools,scripts},ventoy/theme,persistence,boot/grub} $VERBOSE && ok "Staging directory: $BUILD_DIR" ok "Staging directories created." } # ─── Copy Hermes Runtime ────────────────────────────────────────────────────── copy_hermes_runtime() { info "Copying Hermes agent runtime..." if [ -d "$HERMES_SRC/agent" ]; then cp -r "$HERMES_SRC/agent/"* "$BUILD_DIR/hermes/agent/" 2>/dev/null || \ warn "No files in Hermes agent directory — skipping." fi if [ -f "$HERMES_SRC/config.yaml" ]; then cp "$HERMES_SRC/config.yaml" "$BUILD_DIR/hermes/config.yaml" else warn "config.yaml not found — generating default." generate_default_config fi if [ -f "$HERMES_SRC/autorun.sh" ]; then cp "$HERMES_SRC/autorun.sh" "$BUILD_DIR/hermes/autorun.sh" chmod +x "$BUILD_DIR/hermes/autorun.sh" else warn "autorun.sh not found — generating default." generate_default_autorun fi ok "Hermes runtime staged." } # ─── Copy Diagnostic Scripts ────────────────────────────────────────────────── copy_diagnostics() { info "Copying diagnostic scripts..." local scripts=("hardware.py" "bsod.py" "drivers.py" "backup.py" "stress.py") for script in "${scripts[@]}"; do local src="$DIAG_SRC/$script" if [ -f "$src" ]; then cp "$src" "$BUILD_DIR/hermes/scripts/$script" chmod +x "$BUILD_DIR/hermes/scripts/$script" 2>/dev/null || true $VERBOSE && ok " Staged: $script" else warn " Missing diagnostic script: $script" # Generate a stub so the system boots but the component isn't active generate_diagnostic_stub "$script" fi done ok "Diagnostic scripts staged." } # ─── Copy Tools ──────────────────────────────────────────────────────────────── copy_tools() { info "Copying diagnostic tools..." local tool_dirs=() if [ -d "$TOOLS_SRC" ]; then IFS=$'\n' read -d '' -r -a tool_dirs < <(find "$TOOLS_SRC" -mindepth 1 -maxdepth 1 -type d 2>/dev/null || true) fi if [ ${#tool_dirs[@]} -eq 0 ]; then warn "No diagnostic tools found in $TOOLS_SRC — creating structure." mkdir -p "$BUILD_DIR/hermes/tools" # Create placeholder README for tools that need to be added cat > "$BUILD_DIR/hermes/tools/README.md" << 'TOOLREADME' # Diagnostic Tools — Placeholder Directory Populate this directory with portable diagnostic executables: - `smartmontools/` — `smartctl`, `smartd` (disk health) - `memtest86+/` — MemTest86+ binary (RAM testing) - `stress-ng/` — stress-ng binary (CPU/GPU stress) - `dmidecode/` — DMI table decoder (hardware enumeration) - `lshw/` — lshw binary (hardware listing) - `parted/` — parted, fdisk utilities - `ntfs-3g/` — NTFS read/write support See the build:hermes-runtime task (t_c816e37d) for the packaging scripts. TOOLREADME else for dir in "${tool_dirs[@]}"; do local dirname dirname="$(basename "$dir")" cp -r "$dir" "$BUILD_DIR/hermes/tools/$dirname" $VERBOSE && ok " Staged tools: $dirname" done fi ok "Diagnostic tools staged." } # ─── Configure Boot Environment ──────────────────────────────────────────────── configure_boot() { info "Configuring boot environment..." # Read boot environment research if available local boot_doc="$PROJECT_DIR/research/boot-env.md" if [ -f "$boot_doc" ]; then # Source any boot config variables defined in the research doc if grep -q "^BOOT_ENV=" "$boot_doc" 2>/dev/null; then # shellcheck source=/dev/null source "$boot_doc" fi info "Using boot environment from research: $BOOT_ENV" fi case "$BOOT_ENV" in linux) configure_linux_boot ;; winpe) configure_winpe_boot ;; hybrid) configure_hybrid_boot ;; *) die "Unknown boot environment: $BOOT_ENV" ;; esac } configure_linux_boot() { info "Configuring Linux-based boot environment..." # Generate GRUB config for the rescue ISO cat > "$BUILD_DIR/boot/grub/grub.cfg" << 'GRUB' set default=0 set timeout=10 menuentry "Hermes Portable Rescue — Linux" { linux /boot/vmlinuz quiet splash console=tty0 initrd /boot/initrd.img } menuentry "Hermes Rescue — Safe Mode (no HW accel)" { linux /boot/vmlinuz nomodeset quiet initrd /boot/initrd.img } menuentry "Boot from Local Disk" { chainloader (hd0)+1 } menuentry "UEFI Firmware Settings" { fwsetup } menuentry "System Memory Test (MemTest86+)" { linux /boot/memtest86+.bin } GRUB ok "GRUB configuration written." } configure_winpe_boot() { info "Configuring WinPE-based boot environment..." # WinPE boot configuration cat > "$BUILD_DIR/ISO/autounattend.xml" << 'WINAUTO' Never WINAUTO ok "WinPE boot configuration written." } configure_hybrid_boot() { info "Configuring hybrid (multi-boot) environment..." # Hybrid: place both Linux ISO and WinPE ISO with Ventoy handling the boot menu mkdir -p "$BUILD_DIR/ISO" ok "Hybrid boot structure ready (add ISOs to build/usb-staging/ISO/)." } # ─── Generate Stubs / Defaults ──────────────────────────────────────────────── generate_diagnostic_stub() { local script_name="$1" local module_name="${script_name%.py}" cat > "$BUILD_DIR/hermes/scripts/$script_name" << STUB #!/usr/bin/env python3 """ ${module_name} — STUB MODULE. This diagnostic component has not been built yet. Replace this stub with the actual implementation once the upstream build task completes. Upstream task reference: check the Hermes Portable Rescue board. """ import sys def diagnose() -> dict: """Return a stub indicating this module is unavailable.""" return { "component": "${module_name}", "status": "unavailable", "message": "This diagnostic module has not been built yet.", } def main(): result = diagnose() print(f"[{result['status'].upper()}] {result['component']}: {result['message']}") return 1 if result['status'] == 'error' else 0 if __name__ == "__main__": sys.exit(main()) STUB chmod +x "$BUILD_DIR/hermes/scripts/$script_name" warn " Generated stub: $script_name" } generate_default_config() { cat > "$BUILD_DIR/hermes/config.yaml" << 'CONF' # Hermes Portable Rescue — Agent Configuration # ============================================= # This config is auto-generated by build/usb-image.sh. # Customize for production use. agent: name: "Hermes Portable Rescue" version: "1.0.0" mode: "rescue" llm: # Strategy determined by research:llm-strat (t_9ba3b456) # Options: local, api, hybrid strategy: hybrid local_model: "/hermes/models/qwen2.5-7b-q4.gguf" api_fallback: true api_endpoint: "https://api.openai.com/v1" diagnostics: auto_run: true timeout_seconds: 3600 hardware_inventory: true bsod_analysis: true driver_check: true stress_test: false # Manual activation only backup_restore: false # Manual activation only reporting: log_level: info output_format: markdown save_reports: true report_path: "/hermes/reports/" network: dhcp: true fallback_static: false dns: ["8.8.8.8", "1.1.1.1"] CONF ok "Default config.yaml generated." } generate_default_autorun() { cat > "$BUILD_DIR/hermes/autorun.sh" << 'AUTORUN' #!/bin/sh # ============================================================================== # Hermes Portable Rescue — Auto-Launch Script # ============================================================================== # Runs when the rescue environment boots. # Detects environment, mounts necessary filesystems, and launches Hermes agent. set -e HERMES_DIR="/hermes" REPORT_DIR="${HERMES_DIR}/reports" CONFIG="${HERMES_DIR}/config.yaml" echo "=====================================================" echo " Hermes Portable Rescue v1.0.0" echo " AI-driven Windows PC diagnostic & repair toolkit" echo "=====================================================" echo "" # Mount the Hermes partition if not already if [ ! -f "${CONFIG}" ]; then echo "[*] Locating Hermes runtime..." for device in /dev/sd* /dev/nvme0n1* /dev/mmcblk*; do mount_point="/mnt/hermes" mkdir -p "${mount_point}" 2>/dev/null || true if mount -o ro "${device}" "${mount_point}" 2>/dev/null; then if [ -f "${mount_point}/hermes/config.yaml" ]; then HERMES_DIR="${mount_point}/hermes" CONFIG="${HERMES_DIR}/config.yaml" echo "[+] Found Hermes runtime on ${device}" break fi umount "${mount_point}" 2>/dev/null || true fi done fi # Create report directory mkdir -p "${REPORT_DIR}" # Run hardware inventory echo "[*] Running hardware inventory..." if [ -f "${HERMES_DIR}/scripts/hardware.py" ]; then python3 "${HERMES_DIR}/scripts/hardware.py" > "${REPORT_DIR}/hardware.txt" 2>&1 || true echo "[+] Hardware inventory complete" fi # Launch the agent echo "[*] Starting Hermes agent..." if [ -f "${HERMES_DIR}/agent/hermes" ]; then exec "${HERMES_DIR}/agent/hermes" --config "${CONFIG}" elif command -v hermes &>/dev/null; then exec hermes --config "${CONFIG}" else echo "[!] Hermes agent not found. Starting diagnostic shell..." echo " Run individual diagnostics:" echo " python3 ${HERMES_DIR}/scripts/hardware.py" echo " python3 ${HERMES_DIR}/scripts/bsod.py --dump /path/to/minidump.dmp" echo " python3 ${HERMES_DIR}/scripts/drivers.py --scan" echo "" exec /bin/sh fi AUTORUN chmod +x "$BUILD_DIR/hermes/autorun.sh" ok "Default autorun.sh generated." } # ─── Build ISO ───────────────────────────────────────────────────────────────── build_iso() { info "Building rescue ISO: ${OUTPUT_ISO}" # Create the ISO directory structure local iso_root="$BUILD_DIR/ISO" mkdir -p "$iso_root"/{boot/grub,hermes} # Copy boot files if [ -f "$BUILD_DIR/boot/vmlinuz" ] && [ -f "$BUILD_DIR/boot/initrd.img" ]; then cp "$BUILD_DIR/boot/vmlinuz" "$iso_root/boot/" cp "$BUILD_DIR/boot/initrd.img" "$iso_root/boot/" fi # Copy GRUB config if [ -f "$BUILD_DIR/boot/grub/grub.cfg" ]; then cp "$BUILD_DIR/boot/grub/grub.cfg" "$iso_root/boot/grub/" fi # Copy Hermes runtime cp -r "$BUILD_DIR/hermes" "$iso_root/" # Build the ISO xorriso -as mkisofs \ -iso-level 3 \ -full-iso9660-filenames \ -volid "${ISO_LABEL}" \ -eltorito-boot boot/grub/eltorito.img \ -no-emul-boot \ -boot-load-size 4 \ -boot-info-table \ -eltorito-alt-boot \ -e boot/grub/efi.img \ -no-emul-boot \ -isohybrid-gpt-basdat \ -o "${OUTPUT_ISO}" \ "${iso_root}" 2>&1 | tail -5 if [ -f "${OUTPUT_ISO}" ]; then local iso_size iso_size=$(du -h "${OUTPUT_ISO}" | cut -f1) ok "ISO built: ${OUTPUT_ISO} (${iso_size})" else die "ISO build failed." fi } # ─── Build Full Disk Image ──────────────────────────────────────────────────── build_disk_image() { info "Building full disk image: ${OUTPUT_IMAGE}" # Create a sparse disk image dd if=/dev/zero of="${OUTPUT_IMAGE}" bs=1M count=0 seek="${IMAGE_SIZE_MB}" status=progress 2>/dev/null # Partition: GPT with EFI system partition + data partition parted -s "${OUTPUT_IMAGE}" mklabel gpt parted -s "${OUTPUT_IMAGE}" mkpart ESP fat32 1MiB "${EFI_SIZE_MB}MiB" parted -s "${OUTPUT_IMAGE}" set 1 esp on parted -s "${OUTPUT_IMAGE}" mkpart primary ext4 "${EFI_SIZE_MB}MiB" 100% # Set up loop device LOOP_DEV=$(losetup --find --show --partscan "${OUTPUT_IMAGE}") trap 'losetup -d "${LOOP_DEV}" 2>/dev/null || true' EXIT # Format partitions mkfs.vfat -F 32 -n "HERMES-EFI" "${LOOP_DEV}p1" >/dev/null 2>&1 mkfs.ext4 -L "HERMES-DATA" "${LOOP_DEV}p2" >/dev/null 2>&1 # Mount and populate local mnt_efi=$(mktemp -d) local mnt_data=$(mktemp -d) mount "${LOOP_DEV}p1" "$mnt_efi" mount "${LOOP_DEV}p2" "$mnt_data" # Copy data partition contents if [ -d "$BUILD_DIR/hermes" ]; then cp -r "$BUILD_DIR/hermes" "$mnt_data/" fi if [ -d "$BUILD_DIR/ventoy" ]; then cp -r "$BUILD_DIR/ventoy" "$mnt_efi/" fi if [ -f "$VENTOY_JSON" ]; then cp "$VENTOY_JSON" "$mnt_efi/ventoy/ventoy.json" fi # Copy ISOs mkdir -p "$mnt_data/ISO" if [ -f "${OUTPUT_ISO}" ]; then cp "${OUTPUT_ISO}" "$mnt_data/ISO/" fi # Sync and clean up sync umount "$mnt_efi" umount "$mnt_data" rmdir "$mnt_efi" "$mnt_data" losetup -d "${LOOP_DEV}" trap - EXIT local img_size img_size=$(du -h "${OUTPUT_IMAGE}" | cut -f1) ok "Disk image built: ${OUTPUT_IMAGE} (${img_size})" } # ─── Write to USB Device ────────────────────────────────────────────────────── write_to_device() { local device="$1" if [ ! -b "$device" ]; then die "Not a block device: $device" fi echo "" warn "⚠️ ABOUT TO OVERWRITE ENTIRE DEVICE: ${device}" warn " This will DESTROY ALL DATA on ${device}." echo "" if ! $FORCE; then echo -n "Type the device path again to confirm: " read -r confirm if [ "$confirm" != "$device" ]; then die "Confirmation mismatch. Aborting." fi fi info "Writing image to ${device}..." dd if="${OUTPUT_IMAGE}" of="${device}" bs=4M status=progress conv=fsync sync ok "Device ${device} written successfully." } # ─── Verification ───────────────────────────────────────────────────────────── verify_image() { info "Verifying built image..." if [ -f "${OUTPUT_ISO}" ]; then local iso_size iso_size=$(stat -c%s "${OUTPUT_ISO}" 2>/dev/null || stat -f%z "${OUTPUT_ISO}") if [ "$iso_size" -gt 1048576 ]; then # > 1 MB ok " ISO: ${OUTPUT_ISO} ($(du -h "${OUTPUT_ISO}" | cut -f1))" else warn " ISO seems too small: $(du -h "${OUTPUT_ISO}" | cut -f1)" fi fi if [ -f "${OUTPUT_IMAGE}" ]; then local img_size img_size=$(stat -c%s "${OUTPUT_IMAGE}" 2>/dev/null || stat -f%z "${OUTPUT_IMAGE}") if [ "$img_size" -gt 52428800 ]; then # > 50 MB ok " Image: ${OUTPUT_IMAGE} ($(du -h "${OUTPUT_IMAGE}" | cut -f1))" else warn " Image seems too small: $(du -h "${OUTPUT_IMAGE}" | cut -f1)" fi fi # Verify directory structure local expected_dirs=( "$BUILD_DIR/hermes/agent" "$BUILD_DIR/hermes/scripts" "$BUILD_DIR/hermes/tools" ) for dir in "${expected_dirs[@]}"; do if [ -d "$dir" ]; then $VERBOSE && ok " Structure: $dir" else warn " Missing directory: $dir" fi done ok "Verification complete." } # ─── Summary ─────────────────────────────────────────────────────────────────── print_summary() { echo "" echo "═══════════════════════════════════════════════════════════════" echo " Hermes Portable Rescue — Build Summary" echo "═══════════════════════════════════════════════════════════════" echo "" echo " Boot environment: ${BOOT_ENV}" echo " Staging directory: ${BUILD_DIR}" echo " Staging size: $(du -sh "${BUILD_DIR}" 2>/dev/null | cut -f1 || echo 'N/A')" echo "" if [ -f "${OUTPUT_ISO}" ]; then echo " ISO: ${OUTPUT_ISO}" echo " ISO size: $(du -h "${OUTPUT_ISO}" | cut -f1)" fi if [ -f "${OUTPUT_IMAGE}" ]; then echo " Disk image: ${OUTPUT_IMAGE}" echo " Image size: $(du -h "${OUTPUT_IMAGE}" | cut -f1)" fi if [ -n "$WRITE_DEVICE" ]; then echo " Written to device: ${WRITE_DEVICE}" fi echo "" # Show placeholder/generated stubs local stub_count stub_count=$(find "$BUILD_DIR/hermes/scripts" -name "*.py" -exec grep -l "STUB MODULE" {} \; 2>/dev/null | wc -l) if [ "$stub_count" -gt 0 ]; then warn " ${stub_count} diagnostic module(s) are stubs (upstream tasks not yet complete)." echo "" echo " Upstream tasks still needed:" echo " build:hermes-runtime (t_c816e37d)" echo " build:hardware-inventory (t_1215a5f0)" echo " build:bsod-analyzer (t_10103a48)" echo " build:driver-updater (t_2afd37ec)" echo " build:backup-restore (t_0b33ea13)" echo " build:stress-tests (t_f817392d)" else ok " All diagnostic modules present." fi echo "═══════════════════════════════════════════════════════════════" echo "" } # ─── Main ───────────────────────────────────────────────────────────────────── main() { echo "" echo "╔══════════════════════════════════════════════╗" echo "║ Hermes Portable Rescue — USB Image Builder ║" echo "╚══════════════════════════════════════════════╝" echo "" prerequisites validate_artifacts setup_directories copy_hermes_runtime copy_diagnostics copy_tools configure_boot build_iso if ! $ISO_ONLY; then build_disk_image fi verify_image print_summary if [ -n "$WRITE_DEVICE" ]; then write_to_device "$WRITE_DEVICE" fi info "Build complete! 🚀" } main "$@"