Compare commits

..

6 Commits

Author SHA1 Message Date
shawn 14cf8332a2 fix: correct broken batch file paths and update README
- start-llama-server.bat: fix LLAMA_DIR to bin\llama (was bin\ollama),
  fix model to qwen2.5-coder-1.5b (was Llama-3.2-1B), use correct
  GPU (llama-server.exe) and CPU (cpu\llama-server-cpu.exe) binaries
- start-windows-mcp.bat: remove dead python313\ tier, use single
  embedded Python 3.13 at python\python.exe
- run-hermes.bat: use Scripts\hermes.exe instead of -m hermes_cli
  (no __main__.py in package), fix offline mode to use single
  config.yaml instead of non-existent config.offline.yaml
- run-hermes.ps1: same hermes_cli → hermes.exe fix
- README.txt: update to match actual USB state (Python 3.13, 142
  wheels, Qwen2.5-Coder-1.5B 1117MB model, correct dir structure)
2026-07-12 12:37:52 -04:00
shawn 2b7a8290a5 feat: add Windows-MCP UI automation + full offline deps
- New start-windows-mcp.bat — starts Windows-MCP SSE server on :8000
- Windows-MCP auto-starts when Hermes CLI launches (options 1/2)
- Pre-downloaded wheels: hermes-agent (47), windows-mcp (95)
- Offline Python 3.13 embedded + get-pip.py bootstrapper
- Hermes config wired with mcp_servers.windows-mcp
- Updated Makefile with targets: wheels-hermes, wheels-windows-mcp,
  python-portable, get-pip, deps
- Recreated: run-hermes.ps1, start-hermes-dashboard.bat
- Pure ASCII .bat files with CRLF line endings
2026-07-11 18:11:06 -04:00
shawn 6623d4d1fd fix: correct ISO rebuild paths and update GPU detection for USB structure
- Fixed quick-rebuild-iso.sh paths from /hermes/ to /opt/hermes/
- Fixed ISO boot structure (uses GRUB eltorito/efi, not isolinux)
- Added MBR extraction fallback for xorriso
- Updated start-llama-server.bat to reference existing USB paths
  (bin/ollama/, Llama-3.2-1B model, llama-server-vulkan.exe)
- Works with existing USB structure — no restructure needed
- All bat files: ASCII text, CRLF line terminators
2026-07-10 01:41:26 -04:00
shawn 4e0ab94ee2 feat: GPU detection for LLM server (NVIDIA/AMD/Intel/Vulkan + CPU fallback)
- Added start-llama-server.bat with cross-brand GPU auto-detection:
  * Detection 1: nvidia-smi for NVIDIA
  * Detection 2: WMIC for AMD, Intel Arc, Intel HD/Iris/UHD
  * Detection 3: vulkaninfo as final fallback probe
  * Falls back to CPU-only build when no GPU found
- Downloaded llama-server b9947 builds:
  * Vulkan build (91 MB) - supports all GPU brands
  * CPU build (45 MB) - fallback in cpu/ subdir
- Downloaded Qwen2.5-Coder-1.5B Q4_K_M model (1.1 GB)
- Fixed run-hermes.bat polling: replaces fixed 5s timeout with
  curl-based readiness polling (up to 30 attempts, 1s apart)
- All .bat files verified: ASCII text, CRLF line terminators
- Added .gitignore for build artifacts and large binaries
2026-07-10 00:58:56 -04:00
shawn ecda528b90 build:hermes-runtime — Portable Hermes Agent package for USB
Build scripts, configuration, and launch scripts for the Hermes Portable
Rescue runtime.

Includes:
- build-hermes-runtime.sh — Main build script (venv, config, tools)
- bootstrap-venv.sh — Standalone Python venv builder
- config.yaml — Agent configuration for rescue mode
- launch.sh/launch.ps1 — Linux and Windows entry points
- agent/hermes — Entry point for autorun.sh compatibility
- tool-manifest.json — Tool registry for diagnostic binaries
- scripts/ — check-env.sh, mount-storage.sh helpers
- autorun.inf — Windows USB auto-launch
- README — Build and runtime documentation

Part of the Hermes Portable Rescue project board.
Task: t_c816e37d
2026-07-04 03:59:06 -04:00
shawn 85d460e749 research: LLM strategy deliverable — hybrid local-first recommendation 2026-07-04 03:54:02 -04:00
176 changed files with 33675 additions and 116 deletions
+33
View File
@@ -0,0 +1,33 @@
# Build artifacts (large binaries, not tracked in git)
build/output/hermes-rescue.iso
build/output/casper-rw
build/output/hermes-data.ext4
build/output/fix-line-endings.ps1
build/output/python/ # Portable Python (download at build time)
build/iso-staging/
build/xubuntu-24.04*.iso
build/SHA256SUMS
build/hermes-build.fs
# Model and native binaries (large, downloaded at build time)
*.gguf
*.dll
*.exe
# Python cache
__pycache__/
*.pyc
*.pyo
# IDE/editor
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Secrets
src/hermes/auth.json
+41
View File
@@ -78,6 +78,47 @@ check:
fi; \
done
## wheels-windows-mcp — Download Windows-MCP wheels for offline install
wheels-windows-mcp:
@printf "$(BOLD)Downloading Windows-MCP wheels...$(NC)\n"
@mkdir -p $(OUTPUT_DIR)/bin/wheels-windows-mcp
@pip3 download --platform win_amd64 --python-version 3.13 --only-binary=:all: \
--dest $(OUTPUT_DIR)/bin/wheels-windows-mcp --quiet windows-mcp 2>&1
@printf "$(GREEN)$(NC) Downloaded $$(ls $(OUTPUT_DIR)/bin/wheels-windows-mcp/ | wc -l) wheels\n"
@du -sh $(OUTPUT_DIR)/bin/wheels-windows-mcp/
## wheels-hermes — Download Hermes Agent wheels for offline Windows install
wheels-hermes:
@printf "$(BOLD)Downloading Hermes Agent wheels...$(NC)\n"
@mkdir -p $(OUTPUT_DIR)/bin/wheels
@pip3 download --platform win_amd64 --python-version 3.11 --only-binary=:all: \
--dest $(OUTPUT_DIR)/bin/wheels --quiet hermes-agent zeroconf ifaddr 2>&1
@printf "$(GREEN)$(NC) Downloaded $$(ls $(OUTPUT_DIR)/bin/wheels/ | wc -l) wheels\n"
@du -sh $(OUTPUT_DIR)/bin/wheels/
## get-pip — Download get-pip.py bootstrapper
get-pip:
@printf "$(BOLD)Downloading get-pip.py...$(NC)\n"
@curl -sL https://bootstrap.pypa.io/get-pip.py -o $(OUTPUT_DIR)/bin/get-pip.py
@printf "$(GREEN)$(NC) Downloaded get-pip.py ($$(wc -c < $(OUTPUT_DIR)/bin/get-pip.py) bytes)\n"
## python-portable — Download portable Python 3.13 for Windows
python-portable:
@printf "$(BOLD)Downloading portable Python 3.13 for Windows...$(NC)\n"
@mkdir -p /tmp/python-portable
@cd /tmp/python-portable && curl -sL "https://www.python.org/ftp/python/3.13.4/python-3.13.4-embed-amd64.zip" -o python-embed.zip
@mkdir -p $(OUTPUT_DIR)/python
@cd /tmp/python-portable && unzip -q -o python-embed.zip -d $(OUTPUT_DIR)/python/
@rm -rf /tmp/python-portable
@# Remove the _pth file so pip can be installed
@rm -f $(OUTPUT_DIR)/python/python*._pth
@printf "$(GREEN)$(NC) Portable Python 3.13 extracted to $(OUTPUT_DIR)/python/\n"
@du -sh $(OUTPUT_DIR)/python/
## deps — Download all dependencies for offline USB build
deps: get-pip wheels-hermes wheels-windows-mcp
@printf "$(GREEN)$(NC) All dependencies downloaded.\n"
clean:
@printf "$(BOLD)Cleaning build artifacts...$(NC)\n"
@rm -rf $(BUILD_DIR)/usb-staging/
+1
View File
@@ -13,6 +13,7 @@ An **autonomous AI-driven Windows PC diagnostic & repair toolkit** that lives on
| ⬆️ **Driver Updates** | Hardware inventory → vendor API lookup → guided download/install |
| 📦 **Backup/Restore** | Disk imaging, file-level backup, guided restore with AI oversight |
| 💬 **Natural Language** | Describes what it found and what it's doing — no manual tool hunting |
| 🖱️ **Windows UI Automation** | Hermes can click, type, take screenshots, and navigate Windows UI via Windows-MCP (option 6) |
## Why this is different from existing tools
+734
View File
@@ -0,0 +1,734 @@
#!/usr/bin/env bash
# ==============================================================================
# Hermes Portable Rescue — Custom Live ISO Builder
# ==============================================================================
#
# Builds a custom Xubuntu 24.04 LTS live ISO with Hermes Agent pre-installed,
# diagnostic tools, autorun setup, and persistent storage support.
#
# Usage:
# sudo ./build/build-custom-iso.sh [options]
#
# Options:
# --input-iso PATH Path to Xubuntu 24.04 ISO (default: auto-detect)
# --output-dir DIR Output directory (default: ./build/output)
# --work-dir DIR Working directory for build (default: /tmp/hermes-iso-build)
# --skip-download Don't download ISO if missing
# --keep-workdir Don't clean up workdir after build
# --help Show this message
#
# Requirements:
# - squashfs-tools (mksquashfs, unsquashfs)
# - xorriso
# - syslinux-common, isolinux
# - sudo access for mount operations
#
# ==============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
BUILD_DIR="$PROJECT_DIR/build"
# ─── Defaults ──────────────────────────────────────────────────────────────────
DEFAULT_INPUT_ISO="$BUILD_DIR/xubuntu-24.04.4-desktop-amd64.iso"
OUTPUT_DIR="${OUTPUT_DIR:-$BUILD_DIR/output}"
WORKDIR="${WORKDIR:-/tmp/hermes-iso-build}"
SKIP_DOWNLOAD=false
KEEP_WORKDIR=false
# ─── Colors ────────────────────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
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; }
# ─── Parse Arguments ───────────────────────────────────────────────────────────
INPUT_ISO="$DEFAULT_INPUT_ISO"
while [[ $# -gt 0 ]]; do
case "$1" in
--input-iso) INPUT_ISO="$2"; shift 2 ;;
--output-dir) OUTPUT_DIR="$2"; shift 2 ;;
--work-dir) WORKDIR="$2"; shift 2 ;;
--skip-download) SKIP_DOWNLOAD=true; shift ;;
--keep-workdir) KEEP_WORKDIR=true; shift ;;
-h|--help) head -50 "$0"; exit 0 ;;
*) die "Unknown option: $1" ;;
esac
done
# ─── Prerequisites ─────────────────────────────────────────────────────────────
check_command() {
if ! command -v "$1" &>/dev/null; then
die "Required command '$1' not found"
fi
}
prerequisites() {
info "Checking prerequisites..."
check_command unsquashfs
check_command mksquashfs
check_command xorriso
check_command dd
check_command mount
check_command rsync
check_command chroot
ok "All prerequisites available."
}
# ─── Phase 1: Download / Verify ISO ────────────────────────────────────────────
phase_download_iso() {
if [[ -f "$INPUT_ISO" ]]; then
local iso_size
iso_size=$(stat -c%s "$INPUT_ISO" 2>/dev/null || echo 0)
if [[ "$iso_size" -gt 1000000000 ]]; then
ok "Input ISO found: $INPUT_ISO ($(du -h "$INPUT_ISO" | cut -f1))"
# Verify SHA256
local expected_sha="fc2e995bb05c41ea19f1dbfd91f6deea7b2aed7a83b9934d98fc9d9cac527d97"
local actual_sha
actual_sha=$(sha256sum "$INPUT_ISO" | awk '{print $1}')
if [[ "$actual_sha" == "$expected_sha" ]]; then
ok "SHA256 verified."
else
warn "SHA256 mismatch! Expected $expected_sha, got $actual_sha"
warn "Continuing anyway — ISO may be incomplete or different build."
fi
return 0
fi
fi
if $SKIP_DOWNLOAD; then
die "ISO not found at $INPUT_ISO and --skip-download is set."
fi
info "Downloading Xubuntu 24.04.4 LTS Desktop ISO (~4.1 GB)..."
wget -c "https://cdimage.ubuntu.com/xubuntu/releases/24.04/release/xubuntu-24.04.4-desktop-amd64.iso" \
-O "$INPUT_ISO" 2>&1
ok "Download complete."
}
# ─── Phase 2: Extract ISO ─────────────────────────────────────────────────────
phase_extract_iso() {
info "Extracting ISO contents..."
local iso_mount="/mnt/hermes-iso-source"
mkdir -p "$iso_mount" "$WORKDIR/iso-contents"
# Mount ISO (handle both .iso and .img)
mount -o loop "$INPUT_ISO" "$iso_mount" 2>/dev/null || \
die "Failed to mount ISO. Is it a valid ISO file?"
# List contents for debugging
info "ISO contents:"
ls -la "$iso_mount/" 2>/dev/null
echo ""
# Determine boot structure
if [[ -d "$iso_mount/isolinux" ]]; then
ok "BIOS boot: isolinux detected"
fi
if [[ -f "$iso_mount/boot/grub/efi.img" ]] || [[ -f "$iso_mount/boot/grub/eltorito.img" ]]; then
ok "UEFI boot: GRUB efi.img detected"
fi
# Check squashfs location
local squashfs_src=""
if [[ -f "$iso_mount/casper/filesystem.squashfs" ]]; then
squashfs_src="$iso_mount/casper/filesystem.squashfs"
ok "Squashfs: casper/filesystem.squashfs"
elif [[ -f "$iso_mount/casper/filesystem.squashfs.xz" ]]; then
squashfs_src="$iso_mount/casper/filesystem.squashfs.xz"
ok "Squashfs: casper/filesystem.squashfs.xz (compressed)"
else
ls -la "$iso_mount/" "$iso_mount/casper/" 2>/dev/null
umount "$iso_mount"
die "Cannot locate filesystem.squashfs in the ISO."
fi
# Copy everything EXCEPT the squashfs (we'll replace it)
info "Copying ISO contents (excluding squashfs)..."
rsync -a --exclude="casper/filesystem.squashfs*" \
"$iso_mount/" "$WORKDIR/iso-contents/" 2>&1 | tail -5
# Copy the squashfs for extraction
cp "$squashfs_src" "$WORKDIR/filesystem.squashfs"
# Extract the MBR from the ISO for hybrid boot
info "Extracting MBR boot sector..."
dd if="$INPUT_ISO" of="$WORKDIR/iso-contents/isohdpfx.bin" bs=440 count=1 2>/dev/null
ok "MBR extracted."
umount "$iso_mount"
rmdir "$iso_mount"
ok "ISO contents extracted to $WORKDIR/iso-contents"
}
# ─── Phase 3: Unsquash Filesystem ──────────────────────────────────────────────
phase_unsquash() {
info "Unsquashing filesystem (this may take a few minutes)..."
mkdir -p "$WORKDIR/squashfs-root"
if [[ "$WORKDIR/filesystem.squashfs" == *.xz ]]; then
xz -d "$WORKDIR/filesystem.squashfs" -c > "$WORKDIR/filesystem.squashfs.unxz" 2>/dev/null
unsquashfs -d "$WORKDIR/squashfs-root" -f "$WORKDIR/filesystem.squashfs.unxz" 2>&1 | tail -3
else
unsquashfs -d "$WORKDIR/squashfs-root" -f "$WORKDIR/filesystem.squashfs" 2>&1 | tail -3
fi
local root_size
root_size=$(du -sh "$WORKDIR/squashfs-root" | cut -f1)
ok "Filesystem unsquashed: $root_size"
}
# ─── Phase 4: Customize Filesystem (Chroot) ────────────────────────────────────
# This function writes the customization script that runs INSIDE the chroot
generate_chroot_script() {
cat > "$WORKDIR/chroot-customize.sh" << 'CHROOTSCRIPT'
#!/bin/bash
# ==============================================================================
# Hermes Portable Rescue — Chroot Customization Script
# ==============================================================================
# Runs inside the live filesystem chroot to install everything.
# ==============================================================================
set -euo pipefail
# Redirect output to log
exec > /tmp/chroot-build.log 2>&1
echo "=== Hermes Portable Rescue — Chroot Customization ==="
echo "Date: $(date)"
echo ""
# ─── 1. Mount virtual filesystems ──────────────────────────────────────────────
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev || mount -t tmpfs none /dev
mkdir -p /dev/pts
mount -t devpts none /dev/pts
# ─── 2. Configure package sources ──────────────────────────────────────────────
echo "=== Configuring package sources ==="
apt-get update -qq 2>/dev/null || true
# ─── 3. Install essential packages ─────────────────────────────────────────────
echo "=== Installing packages ==="
PACKAGES=(
# Python + dev
python3-pip
python3-venv
python3-setuptools
python3-wheel
git
# Diagnostic tools
smartmontools
dmidecode
lshw
testdisk
ddrescue
ntfs-3g
stress-ng
# Network tools
nmap
net-tools
curl
wget
openssh-client
iperf3
traceroute
# Storage tools
parted
rsync
htop
iotop
exfatprogs
btrfs-progs
xfsprogs
# Filesystem tools
dosfstools
e2fsprogs
# Windows-specific tools
hivex # Windows registry access
)
apt-get install -y -qq "${PACKAGES[@]}" 2>&1 | tail -5
echo "Package installation complete."
# ─── 4. Install Hermes Agent ───────────────────────────────────────────────────
echo "=== Installing Hermes Agent ==="
# Create Hermes directory structure
mkdir -p /opt/hermes/{agent,tools,scripts,models,diagnostics,config}
# Install from pip
pip3 install --no-cache-dir \
hermes-agent \
pyyaml \
psutil \
requests \
2>&1 | tail -3
# Freeze package list for provenance
pip3 freeze > /opt/hermes/pip-freeze.txt
echo "Hermes Agent installed."
# ─── 5. Configure Hermes autorun service ───────────────────────────────────────
echo "=== Configuring autorun ==="
# Systemd service that runs the Hermes rescue autorun on boot
cat > /etc/systemd/system/hermes-autorun.service << 'SERVICEEOF'
[Unit]
Description=Hermes Portable Rescue — Auto-Diagnostic Agent
After=network.target multi-user.target
ConditionPathExists=/opt/hermes/autorun.sh
[Service]
Type=oneshot
ExecStart=/opt/hermes/autorun.sh
RemainAfterExit=yes
StandardOutput=journal+console
StandardError=journal+console
Environment=HERMES_DIR=/opt/hermes
Environment=CONFIG=/opt/hermes/config.yaml
Environment=REPORT_DIR=/opt/hermes/reports
Environment=BOOT_MODE=live
[Install]
WantedBy=multi-user.target
SERVICEEOF
# Desktop autostart for GUI mode — opens a terminal running Hermes
mkdir -p /etc/xdg/autostart
cat > /etc/xdg/autostart/hermes-rescue.desktop << 'DESKTOPDF'
[Desktop Entry]
Type=Application
Name=Hermes Portable Rescue
Comment=AI-driven PC diagnostic & repair toolkit
Exec=xfce4-terminal --title="Hermes Rescue" -e "/opt/hermes/autorun.sh" --hold
Terminal=false
Categories=System;Utility;
StartupNotify=true
X-GNOME-Autostart-enabled=true
DESKTOPDF
# Also create a desktop shortcut on the user desktop for manual launch
mkdir -p /etc/skel/Desktop
cat > /etc/skel/Desktop/hermes-rescue.desktop << 'DESKTOPEOF'
[Desktop Entry]
Type=Application
Name=Hermes Rescue
Comment=AI-driven PC diagnostic & repair toolkit
Exec=xfce4-terminal --title="Hermes Rescue" -e "/opt/hermes/autorun.sh" --hold
Terminal=false
Icon=system-run
Categories=System;Utility;
StartupNotify=true
DESKTOPEOF
chmod +x /etc/skel/Desktop/hermes-rescue.desktop
# Enable the service for live boot
systemctl enable hermes-autorun.service 2>/dev/null || true
echo "Autorun service configured."
# ─── 6. Create symlinks for diagnostic scripts ─────────────────────────────────
echo "=== Setting up diagnostic scripts ==="
# The scripts will be copied from the project source via bind mount.
# Create placeholder structure here — actual files come from the build host.
mkdir -p /opt/hermes/scripts
cat > /opt/hermes/scripts/README.txt << 'README'
Diagnostic scripts are copied from the build host at ISO build time.
Place your scripts in /opt/hermes/scripts/ on the running system.
README
echo "Diagnostic scripts directory ready."
# ─── 7. Configure auto-login (lightdm for XFCE) ────────────────────────────────
echo "=== Configuring auto-login ==="
# LightDM auto-login for the live session user (xubuntu)
mkdir -p /etc/lightdm/lightdm.conf.d
cat > /etc/lightdm/lightdm.conf.d/50-hermes-autologin.conf << 'LIGHTDMEOF'
[Seat:*]
autologin-user=xubuntu
autologin-user-timeout=0
autologin-session=xubuntu
LIGHTDMEOF
echo "Auto-login configured for 'xubuntu'."
# ─── 8. Create persistent storage hook ─────────────────────────────────────────
echo "=== Setting up persistent storage hook ==="
# Script to mount persistent storage if available
mkdir -p /opt/hermes/scripts
cat > /opt/hermes/scripts/mount-persistent.sh << 'MOUNTEOF'
#!/bin/bash
# Mount persistent storage partition (NTFS/exFAT labeled HERMES_DATA)
set -e
PERSISTENT_LABEL="HERMES_DATA"
MOUNT_POINT="/mnt/hermes-data"
mkdir -p "$MOUNT_POINT"
# Find and mount by label
for dev in /dev/sd* /dev/nvme* /dev/mmcblk*; do
if [ -b "$dev" ]; then
label=$(blkid -o value -s LABEL "$dev" 2>/dev/null || echo "")
if [ "$label" = "$PERSISTENT_LABEL" ]; then
fstype=$(blkid -o value -s TYPE "$dev" 2>/dev/null || echo "")
case "$fstype" in
ntfs) mount -t ntfs-3g "$dev" "$MOUNT_POINT" 2>/dev/null && break ;;
ext4) mount "$dev" "$MOUNT_POINT" 2>/dev/null && break ;;
vfat|exfat) mount "$dev" "$MOUNT_POINT" 2>/dev/null && break ;;
esac
fi
fi
done
if mountpoint -q "$MOUNT_POINT"; then
echo "Persistent storage mounted at $MOUNT_POINT"
# Symlink persistent config
if [ -f "$MOUNT_POINT/hermes/config.yaml" ]; then
ln -sf "$MOUNT_POINT/hermes/config.yaml" /opt/hermes/config.yaml
fi
else
echo "No persistent storage found. Running in temporary mode."
fi
MOUNTEOF
chmod +x /opt/hermes/scripts/mount-persistent.sh
echo "Persistent storage hook created."
# ─── 9. Clean up package caches ─────────────────────────────────────────────────
echo "=== Cleaning up ==="
apt-get clean -qq 2>/dev/null || true
apt-get autoremove --purge -qq 2>/dev/null || true
rm -rf /var/lib/apt/lists/* 2>/dev/null || true
rm -rf /root/.cache 2>/dev/null || true
rm -rf /tmp/* 2>/dev/null || true
# Clean Python caches
find /usr -type d -name '__pycache__' -exec rm -rf {} + 2>/dev/null || true
find /usr -type f -name '*.pyc' -delete 2>/dev/null || true
find /opt/hermes -type d -name '__pycache__' -exec rm -rf {} + 2>/dev/null || true
find /opt/hermes -type f -name '*.pyc' -delete 2>/dev/null || true
echo ""
echo "=== Chroot customization complete! ==="
echo "Packages installed: $(dpkg-query -W -f='${Package}\n' 2>/dev/null | wc -l)"
echo "Hermes config at: /opt/hermes/"
echo "Autorun service: hermes-autorun.service"
echo ""
# Unmount virtual filesystems
umount /dev/pts 2>/dev/null || true
umount /sys 2>/dev/null || true
umount /proc 2>/dev/null || true
exit 0
CHROOTSCRIPT
chmod +x "$WORKDIR/chroot-customize.sh"
}
phase_customize() {
info "Preparing chroot environment..."
SQUASHFS_ROOT="$WORKDIR/squashfs-root"
# Bind mount necessary filesystems
mount --bind /dev "$SQUASHFS_ROOT/dev"
mount --bind /proc "$SQUASHFS_ROOT/proc"
mount --bind /sys "$SQUASHFS_ROOT/sys"
mount -t devpts none "$SQUASHFS_ROOT/dev/pts" 2>/dev/null || true
# Network connectivity inside chroot
cp /etc/resolv.conf "$SQUASHFS_ROOT/etc/resolv.conf"
# Copy the project's diagnostic scripts and config into the chroot
mkdir -p "$SQUASHFS_ROOT/tmp/hermes-project"
if [[ -d "$PROJECT_DIR/src/diagnostics" ]]; then
cp -r "$PROJECT_DIR/src/diagnostics/"* "$SQUASHFS_ROOT/tmp/hermes-project/" 2>/dev/null || true
fi
if [[ -f "$PROJECT_DIR/src/hermes/config.yaml" ]]; then
cp "$PROJECT_DIR/src/hermes/config.yaml" "$SQUASHFS_ROOT/tmp/hermes-project/" 2>/dev/null || true
fi
if [[ -f "$PROJECT_DIR/src/hermes/autorun.sh" ]]; then
cp "$PROJECT_DIR/src/hermes/autorun.sh" "$SQUASHFS_ROOT/tmp/hermes-project/" 2>/dev/null || true
fi
if [[ -f "$PROJECT_DIR/src/hermes/launch.sh" ]]; then
cp "$PROJECT_DIR/src/hermes/launch.sh" "$SQUASHFS_ROOT/tmp/hermes-project/" 2>/dev/null || true
fi
if [[ -d "$PROJECT_DIR/src/hermes/scripts" ]]; then
cp -r "$PROJECT_DIR/src/hermes/scripts/"* "$SQUASHFS_ROOT/tmp/hermes-project/scripts/" 2>/dev/null || true
fi
# Copy the chroot script in
cp "$WORKDIR/chroot-customize.sh" "$SQUASHFS_ROOT/tmp/chroot-customize.sh"
chmod +x "$SQUASHFS_ROOT/tmp/chroot-customize.sh"
info "Running chroot customization (this may take 10-20 minutes)..."
echo ""
if chroot "$SQUASHFS_ROOT" /bin/bash /tmp/chroot-customize.sh; then
ok "Chroot customization completed successfully."
else
warn "Chroot script exited with code $? — checking log..."
fi
# Check the build log
if [[ -f "$SQUASHFS_ROOT/tmp/chroot-build.log" ]]; then
echo ""
info "Chroot build log (last 30 lines):"
tail -30 "$SQUASHFS_ROOT/tmp/chroot-build.log"
fi
# Copy project files from temp to /opt/hermes
info "Installing project files..."
if [[ -f "$SQUASHFS_ROOT/tmp/hermes-project/config.yaml" ]]; then
cp "$SQUASHFS_ROOT/tmp/hermes-project/config.yaml" "$SQUASHFS_ROOT/opt/hermes/config.yaml"
fi
if [[ -f "$SQUASHFS_ROOT/tmp/hermes-project/autorun.sh" ]]; then
cp "$SQUASHFS_ROOT/tmp/hermes-project/autorun.sh" "$SQUASHFS_ROOT/opt/hermes/autorun.sh"
chmod +x "$SQUASHFS_ROOT/opt/hermes/autorun.sh"
fi
if [[ -f "$SQUASHFS_ROOT/tmp/hermes-project/launch.sh" ]]; then
cp "$SQUASHFS_ROOT/tmp/hermes-project/launch.sh" "$SQUASHFS_ROOT/opt/hermes/launch.sh"
chmod +x "$SQUASHFS_ROOT/opt/hermes/launch.sh"
fi
if [[ -d "$SQUASHFS_ROOT/tmp/hermes-project/scripts" ]]; then
cp -r "$SQUASHFS_ROOT/tmp/hermes-project/scripts/"* "$SQUASHFS_ROOT/opt/hermes/scripts/" 2>/dev/null || true
fi
if [[ -d "$SQUASHFS_ROOT/tmp/hermes-project/diagnostics" ]]; then
cp -r "$SQUASHFS_ROOT/tmp/hermes-project/diagnostics/"* "$SQUASHFS_ROOT/opt/hermes/scripts/" 2>/dev/null || true
fi
if [[ -d "$SQUASHFS_ROOT/tmp/hermes-project/__init__.py" ]]; then
cp "$SQUASHFS_ROOT/tmp/hermes-project/__init__.py" "$SQUASHFS_ROOT/opt/hermes/scripts/" 2>/dev/null || true
fi
if [[ -f "$SQUASHFS_ROOT/tmp/hermes-project/__main__.py" ]]; then
cp "$SQUASHFS_ROOT/tmp/hermes-project/__main__.py" "$SQUASHFS_ROOT/opt/hermes/scripts/" 2>/dev/null || true
fi
# Clean up
rm -rf "$SQUASHFS_ROOT/tmp/hermes-project" "$SQUASHFS_ROOT/tmp/chroot-customize.sh" 2>/dev/null || true
}
# ─── Phase 5: Clean Chroot ─────────────────────────────────────────────────────
phase_clean_chroot() {
info "Cleaning chroot..."
SQUASHFS_ROOT="$WORKDIR/squashfs-root"
# Remove resolv.conf
rm -f "$SQUASHFS_ROOT/etc/resolv.conf"
# Unmount virtual filesystems
umount "$SQUASHFS_ROOT/dev/pts" 2>/dev/null || true
umount "$SQUASHFS_ROOT/sys" 2>/dev/null || true
umount "$SQUASHFS_ROOT/proc" 2>/dev/null || true
umount "$SQUASHFS_ROOT/dev" 2>/dev/null || true
ok "Chroot cleaned."
}
# ─── Phase 6: Re-pack Squashfs ─────────────────────────────────────────────────
phase_repack_squashfs() {
info "Re-packing squashfs filesystem (this takes a while)..."
local target_squashfs="$WORKDIR/iso-contents/casper/filesystem.squashfs"
mkdir -p "$(dirname "$target_squashfs")"
# Rebuild with high compression
mksquashfs "$WORKDIR/squashfs-root" "$target_squashfs" \
-comp xz -b 1M -Xdict-size 100% \
-noappend \
2>&1 | tail -5
local squash_size
squash_size=$(du -h "$target_squashfs" | cut -f1)
ok "Squashfs re-packed: $squash_size"
# Update filesystem.size for casper
info "Updating filesystem.size..."
local fs_size
fs_size=$(du -sx --block-size=1 "$WORKDIR/squashfs-root" 2>/dev/null | cut -f1)
echo "$fs_size" > "$WORKDIR/iso-contents/casper/filesystem.size"
ok "filesystem.size updated: $fs_size bytes"
}
# ─── Phase 7: Generate ISO ─────────────────────────────────────────────────────
phase_generate_iso() {
info "Generating bootable ISO..."
mkdir -p "$OUTPUT_DIR"
local output_iso="$OUTPUT_DIR/hermes-rescue.iso"
local iso_contents="$WORKDIR/iso-contents"
# Build the ISO with hybrid MBR (BIOS + UEFI)
cd "$iso_contents"
# Determine which efi.img to use
local efi_img=""
if [[ -f "boot/grub/efi.img" ]]; then
efi_img="boot/grub/efi.img"
elif [[ -f "boot/grub/eltorito.img" ]]; then
efi_img="boot/grub/eltorito.img"
else
# Search for any efi boot image
efi_img=$(find . -name "efi.img" -o -name "eltorito.img" 2>/dev/null | head -1)
if [[ -z "$efi_img" ]]; then
warn "No EFI boot image found — ISO may not boot on UEFI systems."
fi
fi
if [[ -n "$efi_img" ]]; then
info "Using EFI image: $efi_img"
xorriso -as mkisofs -r -V "HERMES_RESCUE" \
-J -joliet-long \
-isohybrid-mbr "$iso_contents/isohdpfx.bin" \
-b isolinux/isolinux.bin \
-c isolinux/boot.cat \
-no-emul-boot -boot-load-size 4 -boot-info-table \
-eltorito-alt-boot -e "$efi_img" -no-emul-boot \
-isohybrid-gpt-basdat \
-isohybrid-apm-hfsplus \
-o "$output_iso" \
"$iso_contents" 2>&1 | tail -10
else
# BIOS-only fallback
warn "Building BIOS-only ISO (no UEFI support)."
xorriso -as mkisofs -r -V "HERMES_RESCUE" \
-J -joliet-long \
-isohybrid-mbr "$iso_contents/isohdpfx.bin" \
-b isolinux/isolinux.bin \
-c isolinux/boot.cat \
-no-emul-boot -boot-load-size 4 -boot-info-table \
-o "$output_iso" \
"$iso_contents" 2>&1 | tail -10
fi
local iso_size
iso_size=$(du -h "$output_iso" | cut -f1)
ok "ISO generated: $output_iso ($iso_size)"
cd "$PROJECT_DIR"
}
# ─── Phase 8: Create Persistent Storage Overlay ────────────────────────────────
phase_create_persistence() {
info "Creating persistent storage files..."
# Create a casper-rw file for persistence (1GB by default)
local persist_size="${PERSIST_SIZE:-1024}"
local persist_file="$OUTPUT_DIR/casper-rw"
if [[ -f "$persist_file" ]]; then
warn "casper-rw already exists — skipping."
return 0
fi
dd if=/dev/zero of="$persist_file" bs=1M count="$persist_size" status=progress 2>/dev/null
mkfs.ext4 -F -L casper-rw "$persist_file" 2>/dev/null
ok "Persistent storage created: $persist_file (${persist_size}MB)"
# Create a writeable partition image for Windows-accessible data
local data_img="$OUTPUT_DIR/hermes-data.ext4"
if [[ ! -f "$data_img" ]]; then
dd if=/dev/zero of="$data_img" bs=1M count=512 status=progress 2>/dev/null
mkfs.ext4 -F -L HERMES_DATA "$data_img" 2>/dev/null
ok "Data partition image: $data_img (512MB)"
fi
}
# ─── Phase 9: Cleanup ──────────────────────────────────────────────────────────
phase_cleanup() {
if $KEEP_WORKDIR; then
info "Keeping workdir at $WORKDIR"
return 0
fi
info "Cleaning up build artifacts..."
rm -rf "$WORKDIR/squashfs-root" 2>/dev/null || true
rm -f "$WORKDIR/filesystem.squashfs" "$WORKDIR/filesystem.squashfs.unxz" 2>/dev/null || true
rm -rf "$WORKDIR/iso-contents" 2>/dev/null || true
rm -f "$WORKDIR/chroot-customize.sh" 2>/dev/null || true
ok "Cleanup complete."
}
# ─── Main ──────────────────────────────────────────────────────────────────────
main() {
echo ""
echo "╔════════════════════════════════════════════════════════╗"
echo "║ Hermes Portable Rescue — Live ISO Builder ║"
echo "╚════════════════════════════════════════════════════════╝"
echo ""
echo " Input ISO: $INPUT_ISO"
echo " Output: $OUTPUT_DIR/hermes-rescue.iso"
echo " Workdir: $WORKDIR"
echo " Project: $PROJECT_DIR"
echo ""
# Phase count
TOTAL=8
CURRENT=0
CURRENT=$((CURRENT + 1)); echo ""; echo "[${CURRENT}/${TOTAL}] Prerequisites"; prerequisites
CURRENT=$((CURRENT + 1)); echo ""; echo "[${CURRENT}/${TOTAL}] Download/Verify ISO"; phase_download_iso
CURRENT=$((CURRENT + 1)); echo ""; echo "[${CURRENT}/${TOTAL}] Extract ISO"; phase_extract_iso
CURRENT=$((CURRENT + 1)); echo ""; echo "[${CURRENT}/${TOTAL}] Unsquash filesystem"; phase_unsquash
CURRENT=$((CURRENT + 1)); echo ""; echo "[${CURRENT}/${TOTAL}] Generate chroot script"; generate_chroot_script
CURRENT=$((CURRENT + 1)); echo ""; echo "[${CURRENT}/${TOTAL}] Customize (chroot)"; phase_customize
CURRENT=$((CURRENT + 1)); echo ""; echo "[${CURRENT}/${TOTAL}] Clean chroot"; phase_clean_chroot
CURRENT=$((CURRENT + 1)); echo ""; echo "[${CURRENT}/${TOTAL}] Re-pack squashfs"; phase_repack_squashfs
CURRENT=$((CURRENT + 1)); echo ""; echo "[${CURRENT}/${TOTAL}] Generate ISO"; phase_generate_iso
CURRENT=$((CURRENT + 1)); echo ""; echo "[${CURRENT}/${TOTAL}] Create persistence"; phase_create_persistence
CURRENT=$((CURRENT + 1)); echo ""; echo "[${CURRENT}/${TOTAL}] Cleanup"; phase_cleanup
echo ""
echo "╔════════════════════════════════════════════════════════╗"
echo "║ BUILD COMPLETE ║"
echo "╚════════════════════════════════════════════════════════╝"
echo ""
echo " ISO: $OUTPUT_DIR/hermes-rescue.iso"
echo " Persist: $OUTPUT_DIR/casper-rw"
echo " Data img: $OUTPUT_DIR/hermes-data.ext4"
echo ""
echo "Write to USB:"
echo " # Copy ISO to a Ventoy USB, or write directly:"
echo " sudo dd if=$OUTPUT_DIR/hermes-rescue.iso of=/dev/sdX bs=4M status=progress"
echo ""
echo "For persistence, copy casper-rw to the USB root alongside the ISO,"
echo "or the live system will auto-detect a partition labeled 'HERMES_DATA'."
echo ""
}
main "$@"
+50
View File
@@ -0,0 +1,50 @@
Hermes Portable Rescue USB
===========================
Multi-boot IT rescue toolkit — boot from ISO or use on a running Windows PC.
🥾 BOOT MODE (Ventoy)
Restart PC -> F12/F2/ESC -> select USB -> pick ISO/hermes-rescue.iso
Boots Xubuntu with auto-launching hardware diagnostics + Hermes AI.
🪟 WINDOWS PORTABLE MODE
Plug USB into any Windows PC, open windows-hermes/, double-click:
run-hermes.bat
MENU:
1) Hermes CLI — auto: online (opencode-go), falls back to local LLM
2) Hermes CLI — force offline (pre-starts local llama.cpp)
3) Web Dashboard + AI Chat — http://rescue.local:8080 / http://localhost:9090
4) Hardware Diagnostics — CPU, RAM, disks, GPU, BSOD, drivers
5) Disk Tools — CrystalDiskInfo (SMART health check on Discrete)
6) Windows MCP Server — UI Automation (click, type, screenshot)
7) Exit
📂 USB STRUCTURE
/ISO/ — Bootable ISO images (Ventoy)
/hermes/ — Config, scripts, auth, web dashboard
config.yaml — Online + offline providers (auto-fallback)
auth.json — Your opencode-go credentials
scripts/diagnose_windows.py — Windows-native hardware diagnostics
web/server.py — Hardware dashboard on :8080
tools/disk-info/ — CrystalDiskInfo portable
/windows-hermes/ — Windows portable agent (embedded Python 3.13)
python/ — Embedded Python 3.13 + Hermes Agent 0.15.x
bin/
wheels/ — 47 pre-downloaded Hermes Agent .whl files
wheels-windows-mcp/ — 95 pre-downloaded Windows-MCP .whl files
llama/ — llama-server.exe + CPU-optimized builds
ollama/ — Qwen2.5-Coder-1.5B GGUF model (1,117 MB)
run-hermes.bat — Main launcher (double-click)
start-llama-server.bat — Starts local LLM (auto-detects GPU)
start-windows-mcp.bat — Starts Windows-MCP UI Automation
start-hermes-dashboard.bat — Starts Hermes AI Chat on :9090
/ventoy/ — Ventoy bootloader config
🔑 NOTES
- Online mode: opencode-go (deepseek-v4-flash) — uses your credentials
- Offline mode: llama.cpp (Qwen2.5-Coder-1.5B Q4_K_M) — auto GPU detection, ~2 GB RAM
- All modes share the same memory/session data
- Diagnostics and dashboard work fully offline
- CrystalDiskInfo requires admin for SMART data on some systems
- Windows-MCP (UI Automation) requires admin for screenshots/clicking
- Hermes AI Chat at http://localhost:9090 (requires llama-server running)
File diff suppressed because it is too large Load Diff
+9
View File
@@ -0,0 +1,9 @@
# Diagnostic Tools — Placeholder Directory
#
# Populate this directory with portable Windows diagnostic executables:
#
# - smartmontools/ — smartctl.exe (disk health)
# - stress-ng/ — stress.exe (CPU/GPU stress)
# - hwinfo/ — hardware info tools
#
# Run: make tools-download

Some files were not shown because too many files have changed in this diff Show More