build:usb-image — Final USB image assembly script, Ventoy config, and Hermes agent runtime structure

- build/usb-image.sh: Main USB image assembler with artifact validation,
  ISO building, disk image creation, and USB write support
- build/ventoy.json: Ventoy configuration for multi-boot USB
- src/hermes/config.yaml: Portable Hermes agent configuration
- src/hermes/autorun.sh: Auto-launch script with full diagnostic pipeline
- Makefile: Build system with iso/image/check/clean/write targets

Generates fallback stubs for any diagnostic modules whose upstream
build tasks haven't completed yet, ensuring the image boots even
during active development.
This commit is contained in:
2026-07-04 03:53:30 -04:00
parent 8949e67783
commit 1479e161ab
15 changed files with 4013 additions and 0 deletions
+250
View File
@@ -0,0 +1,250 @@
#!/bin/sh
# ==============================================================================
# Hermes Portable Rescue — Auto-Launch Script (Source)
# ==============================================================================
#
# This is the master auto-launch script for the Hermes Portable Rescue USB.
# It runs when the rescue environment boots and orchestrates the entire
# diagnostic workflow.
#
# Design:
# - Detects the boot filesystem and mounts the Hermes partition
# - Creates a report directory and starts logging
# - Runs diagnostics in dependency order (inventory → analysis → fix)
# - Launches the Hermes agent for LLM-powered interpretation
# - Falls back to a diagnostic shell if the agent isn't available
#
# Environment variables (set by build/usb-image.sh or the boot environment):
# HERMES_DIR — Root of the Hermes runtime filesystem
# CONFIG — Path to config.yaml
# REPORT_DIR — Where to save diagnostic reports
# BOOT_MODE — 'live' (default), 'safe', 'recovery'
#
# ==============================================================================
set -e
# ─── Configuration ────────────────────────────────────────────────────────────
HERMES_DIR="${HERMES_DIR:-/hermes}"
CONFIG="${CONFIG:-${HERMES_DIR}/config.yaml}"
REPORT_DIR="${REPORT_DIR:-${HERMES_DIR}/reports}"
LOG_FILE="${LOG_FILE:-${REPORT_DIR}/session.log}"
BOOT_MODE="${BOOT_MODE:-live}"
# ─── Utility Functions ────────────────────────────────────────────────────────
timestamp() {
date "+%Y-%m-%d %H:%M:%S"
}
log() {
local level="$1"
shift
echo "[$(timestamp)] [${level}] $*" | tee -a "${LOG_FILE}"
}
info() { log "INFO" "$@"; }
warn() { log "WARN" "$@" >&2; }
error() { log "ERROR" "$@" >&2; }
# ─── Banner ────────────────────────────────────────────────────────────────────
print_banner() {
cat << 'BANNER'
╔═══════════════════════════════════════════════════════════════╗
║ ║
║ ██╗ ██╗███████╗██████╗ ███╗ ███╗ ║
║ ██║ ██║██╔════╝██╔══██╗████╗ ████║ ║
║ ███████║█████╗ ██████╔╝██╔████╔██║ ║
║ ██╔══██║██╔══╝ ██╔══██╗██║╚██╔╝██║ ║
║ ██║ ██║███████╗██║ ██║██║ ╚═╝ ██║ ║
║ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ║
║ ║
║ ┌─────────┐ ┌──────────┐ ┌──────────────────┐ ║
║ │ SCAN │→ │ ANALYZE │→ │ RECOMMEND/FIX │ ║
║ └─────────┘ └──────────┘ └──────────────────┘ ║
║ ║
║ Hermes Portable Rescue v1.0.0 ║
║ AI-driven Windows PC diagnostic & repair toolkit ║
║ ║
╚═══════════════════════════════════════════════════════════════╝
BANNER
}
# ─── Filesystem Setup ─────────────────────────────────────────────────────────
mount_windows_partition() {
local windows_mount="/mnt/c"
mkdir -p "${windows_mount}" 2>/dev/null || true
# Try common Windows partitions
for device in /dev/sda1 /dev/sda2 /dev/nvme0n1p1 /dev/nvme0n1p2 /dev/mmcblk0p1; do
if [ -b "${device}" ]; then
local fstype
fstype="$(blkid -o value -s TYPE "${device}" 2>/dev/null || echo '')"
case "${fstype}" in
ntfs)
if mount -t ntfs-3g "${device}" "${windows_mount}" 2>/dev/null; then
info "Mounted Windows partition: ${device}${windows_mount}"
return 0
fi
;;
ext4|vfat)
# Not a Windows partition (or could be EFI system partition)
;;
esac
fi
done
warn "No Windows partition found."
return 1
}
# ─── Diagnostic Pipeline ──────────────────────────────────────────────────────
run_script() {
local script="$1"
local report_name="$2"
local report_file="${REPORT_DIR}/${report_name}"
if [ ! -f "${script}" ]; then
warn "Script not found: ${script}"
return 1
fi
info "Running: ${script}"
if python3 "${script}" > "${report_file}" 2>&1; then
info "${report_name} complete (exit 0)"
return 0
else
local rc=$?
if [ "${rc}" -eq 1 ]; then
# Component unavailable (stub)
info " ${report_name}: component unavailable"
elif [ "${rc}" -eq 2 ]; then
# Error during diagnosis
warn "${report_name}: diagnostic error (exit ${rc})"
else
warn "${report_name}: failed with exit code ${rc}"
fi
return ${rc}
fi
}
run_diagnostics() {
info "Starting diagnostic pipeline..."
echo "───────────────────────────────────────────────────────" >> "${LOG_FILE}"
# Phase 1: Hardware inventory (independent, runs first)
run_script "${HERMES_DIR}/scripts/hardware.py" "01-hardware.txt"
# Phase 2: BSOD analysis (needs hardware inventory context)
run_script "${HERMES_DIR}/scripts/bsod.py" "02-bsod-analysis.txt"
# Phase 3: Driver update check (needs hardware inventory)
run_script "${HERMES_DIR}/scripts/drivers.py" "03-drivers.txt"
# Phase 4: Stress testing (manual/safe mode only)
if [ "${BOOT_MODE}" = "recovery" ]; then
info "Skipping stress tests in recovery mode."
elif grep -q '"enabled": true' "${CONFIG}" 2>/dev/null || [ -n "${RUN_STRESS_TESTS}" ]; then
run_script "${HERMES_DIR}/scripts/stress.py" "04-stress.txt"
else
info "Stress tests disabled (enable in config or set RUN_STRESS_TESTS=1)"
fi
# Phase 5: Backup/restore (manual activation)
if [ -n "${RUN_BACKUP}" ]; then
run_script "${HERMES_DIR}/scripts/backup.py" "05-backup.txt"
fi
echo "───────────────────────────────────────────────────────" >> "${LOG_FILE}"
info "Diagnostic pipeline complete."
}
# ─── Agent Launch ─────────────────────────────────────────────────────────────
launch_agent() {
info "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
error "Hermes agent not found on PATH or in ${HERMES_DIR}/agent/."
return 1
fi
}
start_diagnostic_shell() {
cat << 'SHELL'
╔═══════════════════════════════════════════════════════════════╗
║ DIAGNOSTIC SHELL
║ Hermes agent not available — running in manual mode. ║
╚═══════════════════════════════════════════════════════════════╝
Available diagnostic scripts:
python3 /hermes/scripts/hardware.py — Hardware inventory
python3 /hermes/scripts/bsod.py — BSOD minidump analysis
python3 /hermes/scripts/drivers.py — Driver scanner
python3 /hermes/scripts/stress.py — Stress testing
python3 /hermes/scripts/backup.py — Backup/restore
Reports saved to: /hermes/reports/
Type 'exit' to shut down.
SHELL
exec /bin/sh
}
# ─── Main ─────────────────────────────────────────────────────────────────────
main() {
print_banner
# Ensure report dir exists
mkdir -p "${REPORT_DIR}"
info "Hermes Portable Rescue v1.0.0 — Boot mode: ${BOOT_MODE}"
info "Config: ${CONFIG}"
info "Reports: ${REPORT_DIR}"
info "Log: ${LOG_FILE}"
echo ""
# Optional: mount Windows partition for access to minidumps, etc.
if [ "${MOUNT_WINDOWS}" != "no" ]; then
mount_windows_partition || true
fi
# Run diagnostic pipeline
if [ "${SKIP_DIAGNOSTICS}" != "1" ]; then
run_diagnostics
fi
# Print report summary
if [ -d "${REPORT_DIR}" ]; then
echo ""
info "Diagnostic reports:"
for report in "${REPORT_DIR}"/*.txt; do
if [ -f "${report}" ]; then
local rname
rname=$(basename "${report}")
echo " ${rname} ($(wc -l < "${report}") lines)"
fi
done
echo ""
fi
# Launch agent or fall back to diagnostic shell
if ! launch_agent; then
start_diagnostic_shell
fi
}
main "$@"