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
This commit is contained in:
2026-07-04 03:59:06 -04:00
parent 85d460e749
commit ecda528b90
12 changed files with 572 additions and 88 deletions
+56
View File
@@ -0,0 +1,56 @@
# Hermes Portable Rescue — Source & Build
This directory contains the build scripts and source files for the Hermes Portable Rescue USB runtime.
## Files
| File | Purpose |
|------|---------|
| `build-hermes-runtime.sh` | **Main build script** — creates the portable Hermes runtime package |
| `bootstrap-venv.sh` | Standalone venv bootstrap (can run on target machine) |
| `autorun.inf` | Windows USB autorun configuration |
| `hermes/config.yaml` | Hermes Agent configuration for rescue mode |
| `hermes/launch.sh` | Linux launch script |
| `hermes/launch.ps1` | Windows PowerShell launch script |
| `hermes/tool-manifest.json` | Tool registry for the agent |
| `hermes/scripts/check-env.sh` | Environment verification script |
| `hermes/scripts/mount-storage.sh` | Storage partition mounting helper |
| `hermes/README.md` | Runtime package documentation (for the USB) |
## Building
```bash
# From the project root:
./src/build-hermes-runtime.sh
# Or with custom output path:
./src/build-hermes-runtime.sh --output /mnt/usb/hermes
```
The build script:
1. Creates the directory structure
2. Copies configuration files from `src/hermes/`
3. Creates a Python 3.11 virtual environment with Hermes Agent
4. Installs dependencies (pyyaml, psutil, requests)
5. Prunes the venv to reduce size
6. Collects diagnostic tool binaries
7. Generates the tool manifest and launch scripts
## Output
After building, `src/hermes/` contains the complete portable runtime:
```
hermes/
├── venv/ ← Python 3.11 + Hermes Agent (60-80 MB)
├── config.yaml ← Agent configuration
├── launch.sh ← Linux entry point
├── launch.ps1 ← Windows entry point
├── tool-manifest.json ← Tool registry
├── tools/ ← Diagnostic binaries
├── scripts/ ← Helper scripts
├── pip-freeze.txt ← Installed package manifest
└── README.md ← Runtime docs
```
Copy the entire `hermes/` directory to your USB drive and run `./launch.sh`.
+3
View File
@@ -0,0 +1,3 @@
"""
Hermes Portable Rescue — diagnostic tool package.
"""
+10
View File
@@ -0,0 +1,10 @@
[Autorun]
Action=Start Hermes Portable Rescue
Label=Hermes Rescue USB
Icon=hermes\hermes.ico
Open=hermes\launch.ps1
UseAutoPlay=1
[Autorun.Deprecated]
shell\hermes\command=hermes\launch.ps1
shell\hermes=Start Hermes Rescue
+122
View File
@@ -0,0 +1,122 @@
#!/usr/bin/env bash
# =============================================================================
# Hermes Portable Rescue — Bootstrap Python Virtual Environment
# =============================================================================
# Creates and populates the portable Python 3.11 venv with Hermes Agent.
# Designed to run either:
# - On the build machine (before USB deployment)
# - On the target machine (as part of first-run setup)
#
# Usage:
# ./bootstrap-venv.sh [--target DIR] [--python PYTHON_BIN] [--no-prune]
# =============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TARGET_DIR="${1:-$SCRIPT_DIR/hermes}"
PYTHON_BIN="${2:-python3}"
PRUNE=true
# Parse args
while [[ $# -gt 0 ]]; do
case "$1" in
--target) TARGET_DIR="$2"; shift 2 ;;
--python) PYTHON_BIN="$2"; shift 2 ;;
--no-prune) PRUNE=false; shift ;;
*) break ;;
esac
done
VENV_DIR="$TARGET_DIR/venv"
REQUIREMENTS_FILE="$TARGET_DIR/requirements.txt"
# Colors
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
# --- Preflight ---------------------------------------------------------------
info "Hermes Portable Rescue — Venv Bootstrap"
info "Target: $TARGET_DIR"
info "Python: $PYTHON_BIN"
command -v "$PYTHON_BIN" >/dev/null 2>&1 || error "Python not found: $PYTHON_BIN"
PYVER=$("$PYTHON_BIN" --version 2>&1)
info "Detected: $PYVER"
if [[ ! "$PYVER" =~ Python\ 3\.(1[1-9]|2[0-9]|30) ]]; then
error "Python 3.11+ required (got: $PYVER)"
fi
# --- Create venv -------------------------------------------------------------
if [[ -f "$VENV_DIR/bin/python3" ]]; then
info "Venv already exists — recreating"
rm -rf "$VENV_DIR"
fi
info "Creating virtual environment…"
"$PYTHON_BIN" -m venv "$VENV_DIR" --clear
info "Upgrading pip, setuptools, wheel…"
"$VENV_DIR/bin/pip3" install --upgrade pip setuptools wheel 2>&1 | tail -1
# --- Install Hermes Agent ----------------------------------------------------
info "Installing Hermes Agent and dependencies…"
"$VENV_DIR/bin/pip3" install --no-cache-dir \
hermes-agent \
pyyaml \
psutil \
requests \
2>&1 | tail -3
# Record installed packages
"$VENV_DIR/bin/pip3" freeze > "$TARGET_DIR/pip-freeze.txt"
info "Installed packages recorded to pip-freeze.txt ($(wc -l < "$TARGET_DIR/pip-freeze.txt") packages)"
# --- Prune (optional) --------------------------------------------------------
if [[ "$PRUNE" = true ]]; then
info "Pruning venv to reduce size…"
REMOVED=0
# Remove __pycache__ dirs
while IFS= read -r d; do
rm -rf "$d" && ((REMOVED++))
done < <(find "$VENV_DIR" -type d -name '__pycache__' 2>/dev/null)
# Remove .pyc files
find "$VENV_DIR" -type f -name '*.pyc' -delete 2>/dev/null
# Remove test directories
for dir in tests test testing; do
while IFS= read -r d; do
rm -rf "$d" && ((REMOVED++))
done < <(find "$VENV_DIR" -type d -name "$dir" 2>/dev/null)
done
# Remove dist-info metadata (keep only latest versions)
find "$VENV_DIR" -type d -name '*.dist-info' -exec rm -rf {} + 2>/dev/null || true
# Remove share, man, doc
rm -rf "$VENV_DIR/share" 2>/dev/null || true
info "Removed $REMOVED directories during prune"
fi
# --- Report ------------------------------------------------------------------
VENV_SIZE=$(du -sh "$VENV_DIR" | cut -f1)
TOTAL_SIZE=$(du -sh "$TARGET_DIR" | cut -f1)
echo ""
echo "============================================"
echo " Bootstrap Complete"
echo "============================================"
echo " Venv size: $VENV_SIZE"
echo " Total target: $TOTAL_SIZE"
echo " Python: $("$VENV_DIR/bin/python3" --version 2>&1)"
echo " Pip packages: $(wc -l < "$TARGET_DIR/pip-freeze.txt")"
echo "============================================"
echo ""
info "Next: copy $TARGET_DIR to USB and run launch.sh"
Regular → Executable
+21
View File
@@ -97,6 +97,27 @@ find "$VENV_DIR" -type d -name 'tests' -exec rm -rf {} + 2>/dev/null || true
find "$VENV_DIR" -type f -name '*.pyc' -delete 2>/dev/null || true
rm -rf "$VENV_DIR/share" 2>/dev/null || true
# --- Step 3b: Create agent/hermes entry point ---------------------------------
# The architecture doc and usb-image.sh expect HERMES_DIR/agent/hermes.
# After venv build, symlink to the installed hermes CLI:
# agent/hermes → ../venv/bin/hermes
# Before venv build, a wrapper script (src/hermes/agent/hermes) provides
# a helpful message directing the user to build.
info "Creating agent entry point…"
mkdir -p "$OUTPUT_DIR/agent"
# Create the executable entry point for autorun.sh compatibility
if [[ -f "$VENV_DIR/bin/hermes" ]]; then
# After build: symlink to the venv binary
ln -sf "../venv/bin/hermes" "$OUTPUT_DIR/agent/hermes"
info " agent/hermes → venv/bin/hermes (symlink)"
elif [[ -f "$SCRIPT_DIR/hermes/agent/hermes" ]]; then
# Copy the static wrapper from source (pre-build state)
cp "$SCRIPT_DIR/hermes/agent/hermes" "$OUTPUT_DIR/agent/hermes"
chmod +x "$OUTPUT_DIR/agent/hermes"
info " agent/hermes = static wrapper (pre-build)"
fi
# --- Step 4: Collect portable diagnostic tools -------------------------------
if [[ -d "$TOOL_CACHE" ]]; then
info "Copying prebuilt diagnostic tools from $TOOL_CACHE"
+58
View File
@@ -0,0 +1,58 @@
# Hermes Portable Rescue — Runtime Package
This directory contains the portable Hermes Agent runtime.
It is designed to be copied onto a USB drive and booted on any Windows PC.
## Contents
| Path | Purpose |
|------|---------|
| `venv/` | Portable Python 3.11 virtual environment with Hermes Agent |
| `config.yaml` | Hermes Agent configuration for rescue mode |
| `launch.sh` | Linux / Linux-live boot entry point |
| `launch.ps1` | Windows / WinPE entry point |
| `tools/` | Portable diagnostic binaries (smartctl, stress-ng, etc.) |
| `scripts/` | Diagnostic helper scripts |
| `models/` | (optional) Local LLM model files |
| `tool-manifest.json` | Tool registry for Hermes Agent capabilities |
| `pip-freeze.txt` | Installed Python packages manifest |
## Quick Start
**Linux:**
```bash
cd /path/to/hermes
./launch.sh
```
**Windows (PowerShell):**
```powershell
powershell -ExecutionPolicy Bypass -File .\launch.ps1
```
## Setup Order
1. Run `bootstrap-venv.sh` or `build-hermes-runtime.sh` to create the venv
2. (Optional) Put a 4-bit quantized GGUF model in `models/` for local LLM inference
3. Copy the entire `hermes/` directory to a USB drive
4. Boot the target machine from a Linux live USB
5. Mount the Hermes USB drive
6. Run `./launch.sh`
## Size Budget
| Component | Approx Size |
|-----------|-------------|
| Python venv (pruned) | 60-80 MB |
| Hermes Agent + deps | 20-30 MB |
| Tools (smartctl, stress-ng) | 10-15 MB |
| Models (optional) | 2-5 GB |
| **Total (minimal)** | **~100 MB** |
## Notes
- The venv is created for the **host architecture** (x86_64).
- For ARM64 targets, build on an ARM machine or use QEMU user-mode.
- LLM model files go in `models/` if running local inference.
- Network access is optional — API-based LLM fallback works with internet.
- All diagnostic reports are saved to the same USB in `reports/` directory.
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# =============================================================================
# Hermes Portable Rescue — Agent Entry Point
# =============================================================================
# This is the main entry point that usb-image.sh and autorun.sh expect at
# HERMES_DIR/agent/hermes. It finds and runs the Hermes CLI from the portable
# venv, or prompts to build if the venv doesn't exist yet.
# =============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
HERMES_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# Try the venv binary first (post-build)
if [[ -x "$HERMES_ROOT/venv/bin/hermes" ]]; then
exec "$HERMES_ROOT/venv/bin/hermes" "$@"
fi
# Try installed system hermes (pre-build / development)
if command -v hermes &>/dev/null; then
exec hermes "$@"
fi
# Fallback: no hermes found
echo "╔══════════════════════════════════════════════════════╗" >&2
echo "║ Hermes Agent not found. ║" >&2
echo "║ Run the build script to create the portable venv: ║" >&2
echo "║ ║" >&2
echo "║ ./src/build-hermes-runtime.sh ║" >&2
echo "║ ║" >&2
echo "║ Or install hermes-agent via pip: ║" >&2
echo "║ pip install hermes-agent ║" >&2
echo "╚══════════════════════════════════════════════════════╝" >&2
exit 1
+58 -87
View File
@@ -1,94 +1,65 @@
# Hermes Portable Rescue — Agent Configuration
# =============================================
# This is the **source** config for the portable Hermes agent.
# It is copied into the USB image by build/usb-image.sh.
#
# LLM strategy determined by research:llm-strat task (t_9ba3b456).
# Boot environment determined by research:boot-env task (t_163b7bb2).
# This file is deployed onto the USB and read by Hermes Agent at startup.
# Generated by src/build-hermes-runtime.sh
agent:
name: "Hermes Portable Rescue"
name: "hermes-portable-rescue"
version: "1.0.0"
mode: "rescue"
description: "AI-driven Windows PC diagnostic & repair toolkit"
author: "OPLabs / Shawn"
repo: "https://gitea.ourpad.casa/shawn/hermes-portable-rescue"
llm:
# Strategy: hybrid (local for offline, API fallback when network available)
strategy: hybrid
local_model: "/hermes/models/qwen2.5-7b-q4.gguf"
local_model_url: "https://huggingface.co/Qwen/Qwen2.5-7B-Instruct-GGUF/resolve/main/qwen2.5-7b-instruct-q4_K_M.gguf"
api_fallback: true
api_provider: openai
api_endpoint: "https://api.openai.com/v1"
model: "gpt-4o-mini"
temperature: 0.2
max_tokens: 2048
diagnostics:
auto_run: true
timeout_seconds: 3600
log_level: info
save_reports: true
report_path: "/hermes/reports/"
output_format: markdown
modules:
hardware_inventory:
enabled: true
script: "/hermes/scripts/hardware.py"
description: "CPU, RAM, GPU, disk enumeration and health checks"
bsod_analysis:
enabled: true
script: "/hermes/scripts/bsod.py"
description: "Windows minidump parser with LLM interpretation"
scan_paths:
- "C:/Windows/Minidump/"
- "C:/Windows/memory.dmp"
- "/mnt/c/Windows/Minidump/"
driver_check:
enabled: true
script: "/hermes/scripts/drivers.py"
description: "Hardware-aware driver scanner and updater"
stress_test:
enabled: false # Manual activation only — can destabilise a failing system
script: "/hermes/scripts/stress.py"
description: "RAM, CPU, and GPU stress testing orchestration"
confirm_before_run: true
backup_restore:
enabled: false # Manual activation only
script: "/hermes/scripts/backup.py"
description: "Disk imaging and file-level backup/restore"
confirm_before_run: true
network:
dhcp: true
fallback_static: false
dns:
- "8.8.8.8"
- "1.1.1.1"
auto_connect_wifi: false
wifi_ssid: ""
wifi_password: ""
filesystem:
mount_windows: true
mount_point: "/mnt/c"
ntfs_driver: "ntfs-3g"
fallback_fuse: true
display:
mode: interactive # interactive | silent | report-only
color: true
refresh_interval: 5 # seconds for live diagnostic dashboard
mode: "rescue" # rescue mode: diagnostic-first, no persistent chat
logging:
file: "/hermes/reports/session.log"
level: info # debug | info | warn | error
level: "INFO"
file: "/tmp/hermes-rescue.log"
max_size_mb: 10
rotation: 3
backup_count: 3
# Provider: uses local LLM if available, otherwise API fallback
llm:
mode: "auto" # auto = prefer local, fallback to api
local:
model_path: "${HERMES_ROOT}/models/q4_0.gguf"
context_size: 4096
threads: 4
api:
provider: "openrouter"
model: "openai/gpt-4o-mini"
timeout_seconds: 30
health_check:
max_retries: 2
fallback_to_local: true
rescue:
# Diagnostic sequence — ordered by dependency
diagnostic_sequence:
- hardware_inventory # gather system specs first
- disk_health # SMART checks
- memory_test # RAM stress
- bsod_analyzer # minidump parsing
- cpu_gpu_stress # thermal/load tests
- driver_check # driver inventory
# Storage paths on the USB
paths:
tools: "${HERMES_ROOT}/tools"
scripts: "${HERMES_ROOT}/scripts"
models: "${HERMES_ROOT}/models"
diagnostics: "${HERMES_ROOT}/diagnostics"
reports: "/tmp/hermes-reports"
# Auto-proceed — run the diagnostic sequence on boot without user input
auto_diagnose: true
# Report output
report:
format: "markdown"
output_dir: "/tmp/hermes-reports"
filename_template: "diagnostic-report-{timestamp}.md"
tools:
manifest: "${HERMES_ROOT}/tool-manifest.json"
timeout_seconds: 300
network:
# Optional: API-based LLM fallback needs network
probe_on_start: true
timeout_seconds: 10
Regular → Executable
View File
Regular → Executable
+10 -1
View File
@@ -60,4 +60,13 @@ echo ""
echo "[*] Starting Hermes Agent in rescue mode…"
echo ""
exec "$HERMES_ROOT/venv/bin/hermes" --config "$HERMES_CONFIG"
# Try agent/hermes first (autorun.sh compatible), fall back to venv binary
if [[ -x "$HERMES_ROOT/agent/hermes" ]]; then
exec "$HERMES_ROOT/agent/hermes" --config "$HERMES_CONFIG"
elif [[ -f "$HERMES_ROOT/venv/bin/hermes" ]]; then
exec "$HERMES_ROOT/venv/bin/hermes" --config "$HERMES_CONFIG"
else
echo "[✗] Hermes Agent not found at agent/hermes or venv/bin/hermes"
echo " Run src/build-hermes-runtime.sh first."
exit 1
fi
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env bash
# =============================================================================
# Hermes Portable Rescue — Environment Check Script
# =============================================================================
# Verifies the runtime environment has everything needed.
# Run: ./scripts/check-env.sh
# =============================================================================
set -euo pipefail
HERMES_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PASS=0
FAIL=0
WARN=0
info() { echo -e " [INFO] $*"; }
pass() { echo -e " [PASS] $*"; ((PASS++)); }
fail() { echo -e " [FAIL] $*"; ((FAIL++)); }
warn() { echo -e " [WARN] $*"; ((WARN++)); }
echo "============================================"
echo " Hermes Environment Check"
echo "============================================"
echo " Root: $HERMES_ROOT"
echo ""
# --- Python ------------------------------------------------------------------
echo "--- Python ---"
if [[ -f "$HERMES_ROOT/venv/bin/python3" ]]; then
PYVER=$("$HERMES_ROOT/venv/bin/python3" --version 2>&1)
pass "Python venv: $PYVER"
else
fail "Python venv not found at $HERMES_ROOT/venv/bin/python3"
fi
if [[ -f "$HERMES_ROOT/pip-freeze.txt" ]]; then
PKG_COUNT=$(wc -l < "$HERMES_ROOT/pip-freeze.txt")
pass "Package manifest: $PKG_COUNT packages"
else
fail "pip-freeze.txt not found"
fi
# --- Hermes Agent ------------------------------------------------------------
echo ""
echo "--- Hermes Agent ---"
if [[ -f "$HERMES_ROOT/venv/bin/hermes" ]]; then
pass "Hermes binary found"
else
fail "Hermes binary not found"
fi
if [[ -f "$HERMES_ROOT/config.yaml" ]]; then
pass "Config: config.yaml"
else
fail "config.yaml missing"
fi
# --- Tools -------------------------------------------------------------------
echo ""
echo "--- Diagnostic Tools ---"
if [[ -f "$HERMES_ROOT/tool-manifest.json" ]]; then
pass "Tool manifest: tool-manifest.json"
# Check each tool in the manifest
for tool in smartctl stress-ng dmidecode lshw memtester; do
if command -v "$tool" &>/dev/null; then
pass "Tool available: $tool"
else
warn "Tool not found: $tool (may need install)"
fi
done
else
fail "Tool manifest missing"
fi
# --- Storage -----------------------------------------------------------------
echo ""
echo "--- Storage ---"
ROOT_FREE=$(df -m "$HERMES_ROOT" | tail -1 | awk '{print $4}')
pass "Free space on Hermes root: ${ROOT_FREE}MB"
if [[ -d "/mnt/hermes-data" ]]; then
MNT_FREE=$(df -m /mnt/hermes-data | tail -1 | awk '{print $4}')
pass "Data mount: /mnt/hermes-data (${MNT_FREE}MB free)"
else
warn "Data mount /mnt/hermes-data not found (reports go to /tmp)"
fi
TMP_FREE=$(df -m /tmp | tail -1 | awk '{print $4}')
pass "Temp space: ${TMP_FREE}MB in /tmp"
# --- Network -----------------------------------------------------------------
echo ""
echo "--- Network ---"
if command -v curl &>/dev/null; then
if curl -s --connect-timeout 3 -o /dev/null -w "%{http_code}" https://google.com 2>/dev/null | grep -qE '^[23]'; then
pass "Internet: connected"
else
warn "Internet: not reachable (offline mode)"
fi
else
warn "curl not available"
fi
# --- Summary ----------------------------------------------------------------
echo ""
echo "============================================"
echo " Results: $PASS passed, $FAIL failed, $WARN warnings"
echo "============================================"
if [[ $FAIL -gt 0 ]]; then
echo ""
echo "Fix failures before running the agent."
exit 1
fi
exit 0
+85
View File
@@ -0,0 +1,85 @@
#!/usr/bin/env bash
# =============================================================================
# Hermes Portable Rescue — Storage Mount Helper
# =============================================================================
# Mounts NTFS/BTRFS/ext4 partitions for diagnostic access.
# Usage: ./scripts/mount-storage.sh [--all | --probe]
# =============================================================================
set -euo pipefail
MOUNT_BASE="/mnt/hermes-data"
PROBE_ONLY=false
while [[ $# -gt 0 ]]; do
case "$1" in
--probe) PROBE_ONLY=true; shift ;;
--all) PROBE_ONLY=false; shift ;;
*) echo "Usage: $0 [--all | --probe]"; exit 1 ;;
esac
done
mkdir -p "$MOUNT_BASE"
echo "[hermes:mount] Probing storage devices…"
# List all block devices
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,LABEL 2>/dev/null | grep -v loop | head -20
if [[ "$PROBE_ONLY" = true ]]; then
echo ""
echo "[hermes:mount] Probe complete — use --all to mount"
exit 0
fi
# Mount all unmounted NTFS, ext4, btrfs partitions (excluding live system)
echo ""
echo "[hermes:mount] Mounting accessible partitions…"
echo ""
MOUNT_COUNT=0
while IFS= read -r line; do
DEVICE=$(echo "$line" | awk '{print $1}')
FSTYPE=$(echo "$line" | awk '{print $2}')
MOUNTPOINT=$(echo "$line" | awk '{print $3}')
LABEL=$(echo "$line" | awk '{print $4}')
# Skip: already mounted, swap, live system root
if [[ "$MOUNTPOINT" != "(not mounted)" ]]; then continue; fi
if [[ "$FSTYPE" = "swap" ]]; then continue; fi
if [[ "$DEVICE" =~ (sr|loop|ram) ]]; then continue; fi
MOUNT_PATH="$MOUNT_BASE/$DEVICE"
mkdir -p "$MOUNT_PATH"
case "$FSTYPE" in
ntfs)
if command -v ntfs-3g &>/dev/null; then
ntfs-3g -o ro,noatime "/dev/$DEVICE" "$MOUNT_PATH" 2>/dev/null && \
echo " [✓] /dev/$DEVICE (NTFS${LABEL:+: $LABEL}) → $MOUNT_PATH" && \
((MOUNT_COUNT++)) || \
echo " [✗] /dev/$DEVICE mount failed"
else
echo " [ ] /dev/$DEVICE (NTFS — ntfs-3g not available)"
fi
;;
ext4|ext3|ext2|xfs|btrfs)
mount -o ro,noatime "/dev/$DEVICE" "$MOUNT_PATH" 2>/dev/null && \
echo " [✓] /dev/$DEVICE (${FSTYPE}${LABEL:+: $LABEL}) → $MOUNT_PATH" && \
((MOUNT_COUNT++)) || \
echo " [✗] /dev/$DEVICE mount failed"
;;
vfat|exfat)
mount -o ro,noatime,uid=1000 "/dev/$DEVICE" "$MOUNT_PATH" 2>/dev/null && \
echo " [✓] /dev/$DEVICE (${FSTYPE}${LABEL:+: $LABEL}) → $MOUNT_PATH" && \
((MOUNT_COUNT++)) || \
echo " [✗] /dev/$DEVICE mount failed"
;;
*)
echo " [ ] /dev/$DEVICE ($FSTYPE — unsupported)"
;;
esac
done < <(lsblk -nlo NAME,FSTYPE,MOUNTPOINT,LABEL 2>/dev/null)
echo ""
echo "[hermes:mount] $MOUNT_COUNT partitions mounted under $MOUNT_BASE"
echo "[hermes:mount] Use 'umount $MOUNT_BASE/*' when done"