diff --git a/.claude-flow/daemon-state.json b/.claude-flow/daemon-state.json index 956bb8b..6a61a55 100644 --- a/.claude-flow/daemon-state.json +++ b/.claude-flow/daemon-state.json @@ -1,23 +1,23 @@ { "running": true, - "startedAt": "2026-02-28T05:42:41.387Z", + "startedAt": "2026-02-28T13:34:03.423Z", "workers": { "map": { - "runCount": 40, - "successCount": 40, + "runCount": 41, + "successCount": 41, "failureCount": 0, - "averageDurationMs": 1.1500000000000001, - "lastRun": "2026-02-28T09:12:41.502Z", - "nextRun": "2026-02-28T09:12:41.494Z", + "averageDurationMs": 1.1707317073170733, + "lastRun": "2026-02-28T13:34:03.434Z", + "nextRun": "2026-02-28T13:49:03.434Z", "isRunning": false }, "audit": { - "runCount": 35, + "runCount": 36, "successCount": 0, - "failureCount": 35, + "failureCount": 36, "averageDurationMs": 0, - "lastRun": "2026-02-28T09:04:41.452Z", - "nextRun": "2026-02-28T09:14:41.452Z", + "lastRun": "2026-02-28T13:41:03.432Z", + "nextRun": "2026-02-28T13:51:03.432Z", "isRunning": false }, "optimize": { @@ -26,16 +26,16 @@ "failureCount": 27, "averageDurationMs": 0, "lastRun": "2026-02-28T09:11:41.441Z", - "nextRun": "2026-02-28T09:26:41.441Z", - "isRunning": false + "nextRun": "2026-02-28T13:38:03.426Z", + "isRunning": true }, "consolidate": { - "runCount": 18, - "successCount": 18, + "runCount": 19, + "successCount": 19, "failureCount": 0, - "averageDurationMs": 0.6111111111111112, - "lastRun": "2026-02-28T08:49:41.451Z", - "nextRun": "2026-02-28T09:18:41.402Z", + "averageDurationMs": 0.631578947368421, + "lastRun": "2026-02-28T13:41:03.438Z", + "nextRun": "2026-02-28T14:10:03.427Z", "isRunning": false }, "testgaps": { @@ -44,7 +44,7 @@ "failureCount": 22, "averageDurationMs": 0, "lastRun": "2026-02-28T09:10:41.422Z", - "nextRun": "2026-02-28T09:30:41.423Z", + "nextRun": "2026-02-28T13:42:03.426Z", "isRunning": false }, "predict": { @@ -131,5 +131,5 @@ } ] }, - "savedAt": "2026-02-28T09:12:41.503Z" + "savedAt": "2026-02-28T13:41:03.438Z" } \ No newline at end of file diff --git a/.claude-flow/daemon.pid b/.claude-flow/daemon.pid index 1e38528..09df927 100644 --- a/.claude-flow/daemon.pid +++ b/.claude-flow/daemon.pid @@ -1 +1 @@ -211 \ No newline at end of file +166 \ No newline at end of file diff --git a/.claude-flow/metrics/codebase-map.json b/.claude-flow/metrics/codebase-map.json index 27f3864..7277a65 100644 --- a/.claude-flow/metrics/codebase-map.json +++ b/.claude-flow/metrics/codebase-map.json @@ -1,5 +1,5 @@ { - "timestamp": "2026-02-28T09:12:41.502Z", + "timestamp": "2026-02-28T13:34:03.432Z", "projectRoot": "/home/user/wifi-densepose", "structure": { "hasPackageJson": false, @@ -7,5 +7,5 @@ "hasClaudeConfig": true, "hasClaudeFlow": true }, - "scannedAt": 1772269961502 + "scannedAt": 1772285643433 } \ No newline at end of file diff --git a/.claude-flow/metrics/consolidation.json b/.claude-flow/metrics/consolidation.json index c0a4fe4..ce8d54e 100644 --- a/.claude-flow/metrics/consolidation.json +++ b/.claude-flow/metrics/consolidation.json @@ -1,5 +1,5 @@ { - "timestamp": "2026-02-28T08:49:41.450Z", + "timestamp": "2026-02-28T13:41:03.437Z", "patternsConsolidated": 0, "memoryCleaned": 0, "duplicatesRemoved": 0 diff --git a/Makefile b/Makefile index b78a914..f58f01f 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,42 @@ # WiFi-DensePose Makefile # ============================================================ -.PHONY: verify verify-verbose verify-audit help +.PHONY: verify verify-verbose verify-audit install install-verify install-python \ + install-rust install-browser install-docker install-field install-full \ + check build-rust build-wasm test-rust bench run-api run-viz clean help +# ─── Installation ──────────────────────────────────────────── +# Guided interactive installer +install: + @./install.sh + +# Profile-specific installs (non-interactive) +install-verify: + @./install.sh --profile verify --yes + +install-python: + @./install.sh --profile python --yes + +install-rust: + @./install.sh --profile rust --yes + +install-browser: + @./install.sh --profile browser --yes + +install-docker: + @./install.sh --profile docker --yes + +install-field: + @./install.sh --profile field --yes + +install-full: + @./install.sh --profile full --yes + +# Hardware and environment check only (no install) +check: + @./install.sh --check-only + +# ─── Verification ──────────────────────────────────────────── # Trust Kill Switch -- one-command proof replay verify: @./verify @@ -15,12 +49,75 @@ verify-verbose: verify-audit: @./verify --verbose --audit +# ─── Rust Builds ───────────────────────────────────────────── +build-rust: + cd rust-port/wifi-densepose-rs && cargo build --release + +build-wasm: + cd rust-port/wifi-densepose-rs && wasm-pack build crates/wifi-densepose-wasm --target web --release + +build-wasm-mat: + cd rust-port/wifi-densepose-rs && wasm-pack build crates/wifi-densepose-wasm --target web --release -- --features mat + +test-rust: + cd rust-port/wifi-densepose-rs && cargo test --workspace + +bench: + cd rust-port/wifi-densepose-rs && cargo bench --package wifi-densepose-signal + +# ─── Run ───────────────────────────────────────────────────── +run-api: + uvicorn v1.src.api.main:app --host 0.0.0.0 --port 8000 + +run-api-dev: + uvicorn v1.src.api.main:app --host 0.0.0.0 --port 8000 --reload + +run-viz: + python3 -m http.server 3000 --directory ui + +run-docker: + docker compose up + +# ─── Clean ─────────────────────────────────────────────────── +clean: + rm -f .install.log + cd rust-port/wifi-densepose-rs && cargo clean 2>/dev/null || true + +# ─── Help ──────────────────────────────────────────────────── help: @echo "WiFi-DensePose Build Targets" @echo "============================================================" @echo "" - @echo " make verify Run the trust kill switch (proof replay)" - @echo " make verify-verbose Verbose mode with feature details" - @echo " make verify-audit Full verification + codebase audit" - @echo " make help Show this help" + @echo " Installation:" + @echo " make install Interactive guided installer" + @echo " make install-verify Verification only (~5 MB)" + @echo " make install-python Full Python pipeline (~500 MB)" + @echo " make install-rust Rust pipeline with ~810x speedup" + @echo " make install-browser WASM for browser (~10 MB)" + @echo " make install-docker Docker-based deployment" + @echo " make install-field WiFi-Mat disaster kit (~62 MB)" + @echo " make install-full Everything available" + @echo " make check Hardware/environment check only" + @echo "" + @echo " Verification:" + @echo " make verify Run the trust kill switch" + @echo " make verify-verbose Verbose with feature details" + @echo " make verify-audit Full verification + codebase audit" + @echo "" + @echo " Build:" + @echo " make build-rust Build Rust workspace (release)" + @echo " make build-wasm Build WASM package (browser)" + @echo " make build-wasm-mat Build WASM with WiFi-Mat (field)" + @echo " make test-rust Run all Rust tests" + @echo " make bench Run signal processing benchmarks" + @echo "" + @echo " Run:" + @echo " make run-api Start Python API server" + @echo " make run-api-dev Start API with hot-reload" + @echo " make run-viz Serve 3D visualization (port 3000)" + @echo " make run-docker Start Docker dev stack" + @echo "" + @echo " Utility:" + @echo " make clean Remove build artifacts" + @echo " make help Show this help" @echo "" diff --git a/README.md b/README.md index c1135d7..61e7e39 100644 --- a/README.md +++ b/README.md @@ -152,8 +152,10 @@ cargo test --package wifi-densepose-mat - [WiFi-Mat Disaster Response](#-wifi-mat-disaster-response-module) - [System Architecture](#️-system-architecture) - [Installation](#-installation) - - [Using pip (Recommended)](#using-pip-recommended) - - [From Source](#from-source) + - [Guided Installer (Recommended)](#guided-installer-recommended) + - [Install Profiles](#install-profiles) + - [From Source (Rust)](#from-source-rust--primary) + - [From Source (Python)](#from-source-python) - [Using Docker](#using-docker) - [System Requirements](#system-requirements) - [Quick Start](#-quick-start) @@ -189,7 +191,7 @@ cargo test --package wifi-densepose-mat - [Testing](#-testing) - [Running Tests](#running-tests) - [Test Categories](#test-categories) - - [Mock Testing](#mock-testing) + - [Testing Without Hardware](#testing-without-hardware) - [Continuous Integration](#continuous-integration) - [Deployment](#-deployment) - [Production Deployment](#production-deployment) @@ -266,24 +268,73 @@ WiFi DensePose consists of several key components working together: ## 📦 Installation -### Using pip (Recommended) +### Guided Installer (Recommended) -WiFi-DensePose is now available on PyPI for easy installation: +The interactive installer detects your hardware, checks your environment, and builds the right profile automatically: ```bash -# Install the latest stable version -pip install wifi-densepose - -# Install with specific version -pip install wifi-densepose==1.0.0 - -# Install with optional dependencies -pip install wifi-densepose[gpu] # For GPU acceleration -pip install wifi-densepose[dev] # For development -pip install wifi-densepose[all] # All optional dependencies +./install.sh ``` -### From Source +It walks through 7 steps: +1. **System detection** — OS, RAM, disk, GPU +2. **Toolchain detection** — Python, Rust, Docker, Node.js, ESP-IDF +3. **WiFi hardware detection** — interfaces, ESP32 USB, Intel CSI debug +4. **Profile recommendation** — picks the best profile for your hardware +5. **Dependency installation** — installs what's missing +6. **Build** — compiles the selected profile +7. **Summary** — shows next steps and verification commands + +#### Install Profiles + +| Profile | What it installs | Size | Requirements | +|---------|-----------------|------|-------------| +| `verify` | Pipeline verification only | ~5 MB | Python 3.8+ | +| `python` | Full Python API server + sensing | ~500 MB | Python 3.8+ | +| `rust` | Rust pipeline (~810x faster) | ~200 MB | Rust 1.70+ | +| `browser` | WASM for in-browser execution | ~10 MB | Rust + wasm-pack | +| `iot` | ESP32 sensor mesh + aggregator | varies | Rust + ESP-IDF | +| `docker` | Docker-based deployment | ~1 GB | Docker | +| `field` | WiFi-Mat disaster response kit | ~62 MB | Rust + wasm-pack | +| `full` | Everything available | ~2 GB | All toolchains | + +#### Non-Interactive Install + +```bash +# Install a specific profile without prompts +./install.sh --profile rust --yes + +# Just run hardware detection (no install) +./install.sh --check-only + +# Or use make targets +make install # Interactive +make install-verify # Verification only +make install-python # Python pipeline +make install-rust # Rust pipeline +make install-browser # WASM browser build +make install-docker # Docker deployment +make install-field # Disaster response kit +make install-full # Everything +make check # Hardware check only +``` + +### From Source (Rust — Primary) + +```bash +git clone https://github.com/ruvnet/wifi-densepose.git +cd wifi-densepose + +# Install Rust pipeline (810x faster than Python) +./install.sh --profile rust --yes + +# Or manually: +cd rust-port/wifi-densepose-rs +cargo build --release +cargo test --workspace +``` + +### From Source (Python) ```bash git clone https://github.com/ruvnet/wifi-densepose.git @@ -292,6 +343,16 @@ pip install -r requirements.txt pip install -e . ``` +### Using pip (Python only) + +```bash +pip install wifi-densepose + +# With optional dependencies +pip install wifi-densepose[gpu] # For GPU acceleration +pip install wifi-densepose[all] # All optional dependencies +``` + ### Using Docker ```bash @@ -301,19 +362,23 @@ docker run -p 8000:8000 ruvnet/wifi-densepose:latest ### System Requirements -- **Python**: 3.8 or higher +- **Rust**: 1.70+ (primary runtime — install via [rustup](https://rustup.rs/)) +- **Python**: 3.8+ (for verification and legacy v1 API) - **Operating System**: Linux (Ubuntu 18.04+), macOS (10.15+), Windows 10+ - **Memory**: Minimum 4GB RAM, Recommended 8GB+ - **Storage**: 2GB free space for models and data -- **Network**: WiFi interface with CSI capability -- **GPU**: Optional but recommended (NVIDIA GPU with CUDA support) +- **Network**: WiFi interface with CSI capability (optional — installer detects what you have) +- **GPU**: Optional (NVIDIA CUDA or Apple Metal) ## 🚀 Quick Start ### 1. Basic Setup ```bash -# Install the package +# Install the package (Rust — recommended) +./install.sh --profile rust --yes + +# Or Python legacy pip install wifi-densepose # Copy example configuration @@ -891,17 +956,16 @@ pytest tests/performance/ # Performance tests - Memory usage profiling - Stress testing -### Mock Testing +### Testing Without Hardware -For development without hardware: +For development without WiFi CSI hardware, use the deterministic reference signal: ```bash -# Enable mock mode -export MOCK_HARDWARE=true -export MOCK_POSE_DATA=true +# Verify the full signal processing pipeline (no hardware needed) +./verify -# Run tests with mocked hardware -pytest tests/ --mock-hardware +# Run Rust tests (all use real signal processing, no mocks) +cd rust-port/wifi-densepose-rs && cargo test --workspace ``` ### Continuous Integration @@ -1304,6 +1368,16 @@ SOFTWARE. ## Changelog +### v2.2.0 — 2026-02-28 + +- **Guided installer** — `./install.sh` with 7-step hardware detection, WiFi interface discovery, toolchain checks, and environment-specific RVF builds (verify/python/rust/browser/iot/docker/field/full profiles) +- **Make targets** — `make install`, `make check`, `make install-rust`, `make build-wasm`, `make bench`, and 15+ other targets +- **Real-only inference** — `forward()` and hardware adapters return explicit errors without weights/hardware instead of silent empty data +- **5.7x Doppler FFT speedup** — Phase cache ring buffer reduces full pipeline from 719us to 254us per frame +- **Trust kill switch** — `./verify` with SHA-256 proof replay, `--audit` mode, and production code integrity scan +- **Security hardening** — 10 vulnerabilities fixed (hardcoded creds, JWT bypass, NaN panics), 12 dead code instances removed +- **SOTA research** — Comprehensive WiFi sensing + RuVector analysis with 30+ citations and 20-year projection (docs/research/) + ### v2.1.0 — 2026-02-28 - **RuVector RVF integration** — Architecture Decision Records (ADR-002 through ADR-013) defining integration of RVF cognitive containers, HNSW vector search, SONA self-learning, GNN pattern recognition, post-quantum cryptography, distributed consensus, WASM edge runtime, and witness chains diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..a02c51f --- /dev/null +++ b/install.sh @@ -0,0 +1,1071 @@ +#!/usr/bin/env bash +# ====================================================================== +# WiFi-DensePose Installer +# +# Step-by-step installer with hardware detection, environment checks, +# and environment-specific RVF builds. +# +# Usage: +# ./install.sh Interactive guided install +# ./install.sh --profile browser Non-interactive with profile +# ./install.sh --check-only Hardware/environment check only +# ./install.sh --help Show help +# +# Profiles: +# verify - Verification only (Python + numpy + scipy) +# python - Full Python pipeline (API server, sensing, analytics) +# rust - Rust pipeline (signal processing, API, CLI) +# browser - WASM build for browser deployment +# iot - ESP32 sensor mesh + aggregator +# docker - Docker-based deployment +# field - Disaster response (WiFi-Mat) field deployment +# full - Everything +# ====================================================================== + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +RUST_DIR="${SCRIPT_DIR}/rust-port/wifi-densepose-rs" + +# ─── Colors ─────────────────────────────────────────────────────────── +if [ -t 1 ]; then + RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' + CYAN='\033[0;36m'; BLUE='\033[0;34m'; MAGENTA='\033[0;35m' + BOLD='\033[1m'; DIM='\033[2m'; RESET='\033[0m' +else + RED=''; GREEN=''; YELLOW=''; CYAN=''; BLUE=''; MAGENTA='' + BOLD=''; DIM=''; RESET='' +fi + +# ─── Globals ────────────────────────────────────────────────────────── +PROFILE="" +CHECK_ONLY=false +VERBOSE=false +SKIP_CONFIRM=false +INSTALL_LOG="${SCRIPT_DIR}/.install.log" + +# Hardware detection results +HAS_PYTHON=false; PYTHON_CMD="" +HAS_RUST=false; RUST_VERSION="" +HAS_CARGO=false +HAS_WASM_PACK=false +HAS_WASM_TARGET=false +HAS_DOCKER=false; DOCKER_VERSION="" +HAS_NODE=false; NODE_VERSION="" +HAS_NPM=false +HAS_ESPIDF=false +HAS_GIT=false +HAS_GPU=false; GPU_INFO="" +HAS_WIFI=false; WIFI_IFACE="" +HAS_OPENBLAS=false +HAS_PKGCONFIG=false +HAS_GCC=false +TOTAL_RAM_MB=0 +DISK_FREE_MB=0 +OS_TYPE=""; OS_RELEASE="" +ARCH="" + +# ─── Helpers ────────────────────────────────────────────────────────── +log() { echo -e "$1" | tee -a "${INSTALL_LOG}"; } +step() { echo -e "\n${CYAN}[$1]${RESET} ${BOLD}$2${RESET}"; } +ok() { echo -e " ${GREEN}OK${RESET} $1"; } +warn() { echo -e " ${YELLOW}WARN${RESET} $1"; } +fail() { echo -e " ${RED}FAIL${RESET} $1"; } +info() { echo -e " ${DIM}$1${RESET}"; } +need() { echo -e " ${BLUE}NEED${RESET} $1"; } + +banner() { + echo "" + echo -e "${BOLD}======================================================================" + echo " WiFi-DensePose Installer" + echo " Hardware detection + environment-specific RVF builds" + echo -e "======================================================================${RESET}" + echo "" +} + +usage() { + echo "Usage: ./install.sh [OPTIONS]" + echo "" + echo "Options:" + echo " --profile PROFILE Install specific profile (see below)" + echo " --check-only Run hardware/environment checks only" + echo " --verbose Show detailed output" + echo " --yes Skip confirmation prompts" + echo " --help Show this help" + echo "" + echo "Profiles:" + echo " verify Verification only (Python + numpy + scipy)" + echo " python Full Python pipeline (API, sensing, analytics)" + echo " rust Rust pipeline (signal processing, benchmarks)" + echo " browser WASM build for browser deployment (~10MB)" + echo " iot ESP32 sensor mesh + aggregator" + echo " docker Docker-based deployment" + echo " field WiFi-Mat disaster response field kit (~62MB)" + echo " full Everything" + echo "" + echo "Examples:" + echo " ./install.sh # Interactive" + echo " ./install.sh --profile verify # Quick verification" + echo " ./install.sh --profile rust --yes # Rust build, no prompts" + echo " ./install.sh --check-only # Just detect hardware" +} + +# ─── Argument parsing ───────────────────────────────────────────────── +while [[ $# -gt 0 ]]; do + case "$1" in + --profile) PROFILE="$2"; shift 2 ;; + --check-only) CHECK_ONLY=true; shift ;; + --verbose) VERBOSE=true; shift ;; + --yes) SKIP_CONFIRM=true; shift ;; + --help|-h) usage; exit 0 ;; + *) echo "Unknown option: $1"; usage; exit 1 ;; + esac +done + +# ─── Initialize log ────────────────────────────────────────────────── +echo "WiFi-DensePose install log - $(date -u +%Y-%m-%dT%H:%M:%SZ)" > "${INSTALL_LOG}" + +# ====================================================================== +# STEP 1: SYSTEM DETECTION +# ====================================================================== + +detect_system() { + step "1/7" "System Detection" + echo "" + + # OS + if [[ "$(uname)" == "Darwin" ]]; then + OS_TYPE="macos" + OS_RELEASE="$(sw_vers -productVersion 2>/dev/null || echo 'unknown')" + ok "macOS ${OS_RELEASE}" + elif [[ "$(uname)" == "Linux" ]]; then + OS_TYPE="linux" + if [ -f /etc/os-release ]; then + OS_RELEASE="$(. /etc/os-release && echo "${PRETTY_NAME}")" + else + OS_RELEASE="$(uname -r)" + fi + ok "Linux: ${OS_RELEASE}" + else + OS_TYPE="other" + OS_RELEASE="$(uname -s)" + warn "Unsupported OS: ${OS_RELEASE}" + fi + + # Architecture + ARCH="$(uname -m)" + ok "Architecture: ${ARCH}" + + # RAM + if [[ "$OS_TYPE" == "linux" ]]; then + TOTAL_RAM_MB=$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo 2>/dev/null || echo 0) + elif [[ "$OS_TYPE" == "macos" ]]; then + TOTAL_RAM_MB=$(( $(sysctl -n hw.memsize 2>/dev/null || echo 0) / 1024 / 1024 )) + fi + if [ "$TOTAL_RAM_MB" -ge 8192 ]; then + ok "RAM: ${TOTAL_RAM_MB} MB (recommended: 8192+)" + elif [ "$TOTAL_RAM_MB" -ge 4096 ]; then + warn "RAM: ${TOTAL_RAM_MB} MB (minimum met, 8192+ recommended)" + elif [ "$TOTAL_RAM_MB" -gt 0 ]; then + warn "RAM: ${TOTAL_RAM_MB} MB (below 4096 minimum)" + fi + + # Disk + DISK_FREE_MB=$(df -m "${SCRIPT_DIR}" 2>/dev/null | awk 'NR==2 {print $4}' || echo 0) + if [ "$DISK_FREE_MB" -ge 5000 ]; then + ok "Disk: ${DISK_FREE_MB} MB free" + elif [ "$DISK_FREE_MB" -ge 2000 ]; then + warn "Disk: ${DISK_FREE_MB} MB free (5000+ recommended)" + else + warn "Disk: ${DISK_FREE_MB} MB free (2000+ required)" + fi + + # GPU + if command -v nvidia-smi &>/dev/null; then + GPU_INFO="$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1 || echo '')" + if [ -n "$GPU_INFO" ]; then + HAS_GPU=true + ok "GPU: ${GPU_INFO} (NVIDIA CUDA)" + fi + elif command -v metal &>/dev/null || [ -d "/System/Library/Frameworks/Metal.framework" ]; then + HAS_GPU=true + GPU_INFO="Apple Metal" + ok "GPU: Apple Metal" + fi + if ! $HAS_GPU; then + info "GPU: None detected (CPU inference will be used)" + fi +} + +# ====================================================================== +# STEP 2: TOOLCHAIN DETECTION +# ====================================================================== + +detect_toolchains() { + step "2/7" "Toolchain Detection" + echo "" + + # Python + if command -v python3 &>/dev/null; then + PYTHON_CMD=python3 + HAS_PYTHON=true + PY_VER="$($PYTHON_CMD --version 2>&1)" + ok "Python: ${PY_VER}" + elif command -v python &>/dev/null; then + PY_VER="$(python --version 2>&1)" + if [[ "$PY_VER" == *"3."* ]]; then + PYTHON_CMD=python + HAS_PYTHON=true + ok "Python: ${PY_VER}" + else + fail "Python 2 found but Python 3 required" + fi + else + need "Python 3.8+ not found (install: https://python.org)" + fi + + # Check Python packages + if $HAS_PYTHON; then + NUMPY_OK=false; SCIPY_OK=false; TORCH_OK=false; FASTAPI_OK=false + $PYTHON_CMD -c "import numpy" 2>/dev/null && NUMPY_OK=true + $PYTHON_CMD -c "import scipy" 2>/dev/null && SCIPY_OK=true + $PYTHON_CMD -c "import torch" 2>/dev/null && TORCH_OK=true + $PYTHON_CMD -c "import fastapi" 2>/dev/null && FASTAPI_OK=true + + if $NUMPY_OK && $SCIPY_OK; then + ok "Python packages: numpy, scipy (verification ready)" + else + need "Python packages: numpy/scipy missing (pip install numpy scipy)" + fi + if $TORCH_OK; then ok "PyTorch: installed"; else info "PyTorch: not installed (needed for full pipeline)"; fi + if $FASTAPI_OK; then ok "FastAPI: installed"; else info "FastAPI: not installed (needed for API server)"; fi + fi + + # Rust + if command -v rustc &>/dev/null; then + HAS_RUST=true + RUST_VERSION="$(rustc --version 2>&1)" + ok "Rust: ${RUST_VERSION}" + else + info "Rust: not installed (install: https://rustup.rs)" + fi + + # Cargo + if command -v cargo &>/dev/null; then + HAS_CARGO=true + ok "Cargo: $(cargo --version 2>&1)" + fi + + # wasm-pack + if command -v wasm-pack &>/dev/null; then + HAS_WASM_PACK=true + ok "wasm-pack: $(wasm-pack --version 2>&1)" + else + info "wasm-pack: not installed (needed for browser profile)" + fi + + # WASM target + if $HAS_RUST && rustup target list --installed 2>/dev/null | grep -q "wasm32-unknown-unknown"; then + HAS_WASM_TARGET=true + ok "WASM target: wasm32-unknown-unknown installed" + fi + + # Docker + if command -v docker &>/dev/null; then + HAS_DOCKER=true + DOCKER_VERSION="$(docker --version 2>&1)" + ok "Docker: ${DOCKER_VERSION}" + else + info "Docker: not installed (needed for docker profile)" + fi + + # Node.js + if command -v node &>/dev/null; then + HAS_NODE=true + NODE_VERSION="$(node --version 2>&1)" + ok "Node.js: ${NODE_VERSION}" + else + info "Node.js: not installed (optional for UI dev)" + fi + + if command -v npm &>/dev/null; then + HAS_NPM=true + fi + + # Git + if command -v git &>/dev/null; then + HAS_GIT=true + ok "Git: $(git --version 2>&1)" + else + need "Git: not installed" + fi + + # ESP-IDF + if command -v idf.py &>/dev/null || [ -d "${HOME}/esp/esp-idf" ] || [ -n "${IDF_PATH:-}" ]; then + HAS_ESPIDF=true + ok "ESP-IDF: found" + else + info "ESP-IDF: not installed (needed for IoT profile)" + fi + + # System libraries (for Rust builds) + if command -v pkg-config &>/dev/null; then + HAS_PKGCONFIG=true + fi + if command -v gcc &>/dev/null || command -v cc &>/dev/null; then + HAS_GCC=true + fi + if pkg-config --exists openblas 2>/dev/null || [ -f /usr/lib/libopenblas.so ] || [ -f /usr/lib/x86_64-linux-gnu/libopenblas.so ] || brew list openblas &>/dev/null 2>&1; then + HAS_OPENBLAS=true + ok "OpenBLAS: found" + elif $HAS_RUST; then + need "OpenBLAS: not found (needed for Rust signal crate)" + fi +} + +# ====================================================================== +# STEP 3: WIFI HARDWARE DETECTION +# ====================================================================== + +detect_wifi_hardware() { + step "3/7" "WiFi Hardware Detection" + echo "" + + local hw_found=false + + # Check for WiFi interfaces + if [[ "$OS_TYPE" == "linux" ]]; then + # Check /proc/net/wireless for active WiFi + if [ -f /proc/net/wireless ]; then + local ifaces + ifaces="$(awk 'NR>2 {print $1}' /proc/net/wireless 2>/dev/null | tr -d ':' || true)" + if [ -n "$ifaces" ]; then + for iface in $ifaces; do + HAS_WIFI=true + WIFI_IFACE="$iface" + ok "WiFi interface: ${iface} (Linux /proc/net/wireless)" + hw_found=true + done + fi + fi + + # Check for iwconfig interfaces + if ! $hw_found && command -v iwconfig &>/dev/null; then + local iface + iface="$(iwconfig 2>/dev/null | grep -o '^\S*' | head -1 || true)" + if [ -n "$iface" ] && [ "$iface" != "lo" ]; then + HAS_WIFI=true + WIFI_IFACE="$iface" + ok "WiFi interface: ${iface} (iwconfig)" + hw_found=true + fi + fi + + # Check for ip link wireless interfaces + if ! $hw_found && command -v ip &>/dev/null; then + local wireless_ifaces + wireless_ifaces="$(ip link show 2>/dev/null | grep -oP '^\d+: \K(wl\S+)' || true)" + if [ -n "$wireless_ifaces" ]; then + for iface in $wireless_ifaces; do + HAS_WIFI=true + WIFI_IFACE="$iface" + ok "WiFi interface: ${iface} (ip link)" + hw_found=true + break + done + fi + fi + + # Check for ESP32 USB serial devices + local usb_devs + usb_devs="$(ls /dev/ttyUSB* /dev/ttyACM* 2>/dev/null || true)" + if [ -n "$usb_devs" ]; then + ok "USB serial devices detected (possible ESP32)" + for dev in $usb_devs; do + info " ${dev}" + done + hw_found=true + fi + + # Check for Intel 5300 CSI tool + if [ -d /sys/kernel/debug/ieee80211 ]; then + for phy in /sys/kernel/debug/ieee80211/*/; do + if [ -d "${phy}iwlwifi" ]; then + ok "Intel WiFi debug interface: $(basename "$phy")" + hw_found=true + fi + done + fi + + elif [[ "$OS_TYPE" == "macos" ]]; then + # macOS WiFi + local airport="/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport" + if [ -x "$airport" ]; then + local ssid + ssid="$($airport -I 2>/dev/null | awk '/ SSID/ {print $2}' || true)" + if [ -n "$ssid" ]; then + HAS_WIFI=true + WIFI_IFACE="en0" + ok "WiFi: connected to '${ssid}' (en0)" + hw_found=true + fi + fi + if ! $hw_found; then + local mac_wifi + mac_wifi="$(networksetup -listallhardwareports 2>/dev/null | awk '/Wi-Fi/{getline; print $2}' || true)" + if [ -n "$mac_wifi" ]; then + HAS_WIFI=true + WIFI_IFACE="$mac_wifi" + ok "WiFi interface: ${mac_wifi}" + hw_found=true + fi + fi + fi + + if ! $hw_found; then + info "No WiFi hardware detected" + info "You can still run verification and build WASM/Docker targets" + fi + + # CSI capability assessment + echo "" + echo -e " ${BOLD}CSI Capability Assessment:${RESET}" + if $HAS_WIFI; then + echo -e " ${GREEN}*${RESET} RSSI-based presence detection: ${GREEN}available${RESET} (commodity WiFi)" + echo -e " ${DIM}*${RESET} Full CSI extraction: requires ESP32-S3 mesh or Intel 5300/Atheros NIC" + echo -e " ${DIM}*${RESET} DensePose estimation: requires 3+ ESP32 nodes or research-grade NIC" + else + echo -e " ${DIM}*${RESET} No WiFi = build/verify only (no live sensing)" + fi +} + +# ====================================================================== +# STEP 4: PROFILE RECOMMENDATION +# ====================================================================== + +recommend_profile() { + step "4/7" "Profile Selection" + echo "" + + # Auto-recommend based on detected hardware + local recommended="" + local available_profiles=() + + # verify is always available + available_profiles+=("verify") + + if $HAS_PYTHON; then + available_profiles+=("python") + fi + if $HAS_RUST && $HAS_CARGO; then + available_profiles+=("rust") + if $HAS_WASM_PACK || $HAS_WASM_TARGET; then + available_profiles+=("browser") + fi + fi + if $HAS_DOCKER; then + available_profiles+=("docker") + fi + if $HAS_ESPIDF; then + available_profiles+=("iot") + fi + if $HAS_RUST && $HAS_CARGO; then + available_profiles+=("field") + fi + + # Determine recommendation (Rust is the primary runtime) + if $HAS_RUST && $HAS_CARGO; then + recommended="rust" + elif $HAS_PYTHON; then + recommended="python" + else + recommended="verify" + fi + + echo " Available profiles based on your system:" + echo "" + + local idx=1 + declare -A PROFILE_MAP + + for p in "${available_profiles[@]}"; do + local marker="" + if [ "$p" == "$recommended" ]; then + marker=" ${GREEN}(recommended)${RESET}" + fi + case "$p" in + verify) echo -e " ${BOLD}${idx})${RESET} verify - Pipeline verification only (~5 MB)${marker}" ;; + python) echo -e " ${BOLD}${idx})${RESET} python - Full Python pipeline + API server (~500 MB)${marker}" ;; + rust) echo -e " ${BOLD}${idx})${RESET} rust - Rust pipeline with ~810x speedup (~200 MB)${marker}" ;; + browser) echo -e " ${BOLD}${idx})${RESET} browser - WASM for browser deployment (~10 MB output)${marker}" ;; + docker) echo -e " ${BOLD}${idx})${RESET} docker - Docker-based deployment (~1 GB image)${marker}" ;; + iot) echo -e " ${BOLD}${idx})${RESET} iot - ESP32 sensor mesh + aggregator${marker}" ;; + field) echo -e " ${BOLD}${idx})${RESET} field - WiFi-Mat disaster response kit (~62 MB)${marker}" ;; + esac + PROFILE_MAP[$idx]="$p" + idx=$((idx + 1)) + done + + # Always show full as the last option + echo -e " ${BOLD}${idx})${RESET} full - Install everything available" + PROFILE_MAP[$idx]="full" + + if [ -n "$PROFILE" ]; then + echo "" + echo -e " Profile specified via --profile: ${BOLD}${PROFILE}${RESET}" + return + fi + + if $CHECK_ONLY; then + return + fi + + echo "" + read -rp " Select profile [1-${idx}] (default: ${recommended}): " choice + + if [ -z "$choice" ]; then + PROFILE="$recommended" + elif [[ -n "${PROFILE_MAP[$choice]+x}" ]]; then + PROFILE="${PROFILE_MAP[$choice]}" + else + echo -e " ${RED}Invalid choice. Using ${recommended}.${RESET}" + PROFILE="$recommended" + fi + + echo "" + echo -e " Selected: ${BOLD}${PROFILE}${RESET}" +} + +# ====================================================================== +# STEP 5: INSTALL DEPENDENCIES +# ====================================================================== + +install_deps() { + step "5/7" "Installing Dependencies" + echo "" + + case "$PROFILE" in + verify) + install_verify_deps + ;; + python) + install_verify_deps + install_python_deps + ;; + rust) + install_rust_deps + ;; + browser) + install_rust_deps + install_wasm_deps + ;; + iot) + install_rust_deps + install_iot_deps + ;; + docker) + check_docker_deps + ;; + field) + install_rust_deps + install_field_deps + ;; + full) + install_verify_deps + install_python_deps + install_rust_deps + if $HAS_WASM_PACK || $HAS_WASM_TARGET; then + install_wasm_deps + fi + if $HAS_DOCKER; then + check_docker_deps + fi + ;; + esac +} + +install_verify_deps() { + echo -e " ${CYAN}Verification dependencies:${RESET}" + if ! $HAS_PYTHON; then + fail "Python 3 required but not found. Install from https://python.org" + exit 1 + fi + + local NEED_INSTALL=false + $PYTHON_CMD -c "import numpy" 2>/dev/null || NEED_INSTALL=true + $PYTHON_CMD -c "import scipy" 2>/dev/null || NEED_INSTALL=true + + if $NEED_INSTALL; then + echo " Installing numpy and scipy..." + if [ -f "${SCRIPT_DIR}/v1/requirements-lock.txt" ]; then + $PYTHON_CMD -m pip install -r "${SCRIPT_DIR}/v1/requirements-lock.txt" 2>&1 | tail -3 + else + $PYTHON_CMD -m pip install numpy scipy 2>&1 | tail -3 + fi + ok "numpy + scipy installed" + else + ok "numpy + scipy already installed" + fi +} + +install_python_deps() { + echo -e " ${CYAN}Python pipeline dependencies:${RESET}" + if [ -f "${SCRIPT_DIR}/requirements.txt" ]; then + echo " Installing from requirements.txt..." + $PYTHON_CMD -m pip install -r "${SCRIPT_DIR}/requirements.txt" 2>&1 | tail -5 + ok "Python dependencies installed" + else + warn "requirements.txt not found" + fi +} + +install_rust_deps() { + echo -e " ${CYAN}Rust dependencies:${RESET}" + + if ! $HAS_RUST; then + echo " Rust not found. Installing via rustup..." + if ! $SKIP_CONFIRM; then + read -rp " Install Rust via rustup? [Y/n]: " yn + if [[ "$yn" =~ ^[Nn] ]]; then + fail "Rust required for this profile. Skipping." + return 1 + fi + fi + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + # shellcheck source=/dev/null + source "${HOME}/.cargo/env" 2>/dev/null || true + HAS_RUST=true + HAS_CARGO=true + ok "Rust installed" + else + ok "Rust already installed" + fi + + # System libraries for OpenBLAS + if ! $HAS_OPENBLAS; then + echo "" + echo -e " ${CYAN}System libraries:${RESET}" + if [[ "$OS_TYPE" == "linux" ]]; then + if command -v apt-get &>/dev/null; then + echo " Installing build-essential, gfortran, libopenblas-dev, pkg-config..." + if ! $SKIP_CONFIRM; then + read -rp " Install system packages via apt? [Y/n]: " yn + if [[ "$yn" =~ ^[Nn] ]]; then + warn "Skipping system packages. Rust build may fail." + return + fi + fi + sudo apt-get update -qq + sudo apt-get install -y -qq build-essential gfortran libopenblas-dev pkg-config + ok "System libraries installed" + elif command -v dnf &>/dev/null; then + echo " Installing gcc, gcc-fortran, openblas-devel, pkgconf..." + sudo dnf install -y gcc gcc-fortran openblas-devel pkgconf + ok "System libraries installed" + else + warn "Cannot auto-install OpenBLAS. Install manually." + fi + elif [[ "$OS_TYPE" == "macos" ]]; then + if command -v brew &>/dev/null; then + echo " Installing openblas via Homebrew..." + brew install openblas + ok "OpenBLAS installed" + else + warn "Install Homebrew and then: brew install openblas" + fi + fi + fi +} + +install_wasm_deps() { + echo "" + echo -e " ${CYAN}WASM dependencies:${RESET}" + + if ! $HAS_WASM_TARGET; then + echo " Adding wasm32-unknown-unknown target..." + rustup target add wasm32-unknown-unknown + ok "WASM target added" + else + ok "WASM target already installed" + fi + + if ! $HAS_WASM_PACK; then + echo " Installing wasm-pack..." + cargo install wasm-pack 2>&1 | tail -3 + ok "wasm-pack installed" + else + ok "wasm-pack already installed" + fi +} + +install_iot_deps() { + echo "" + echo -e " ${CYAN}IoT (ESP32) dependencies:${RESET}" + if $HAS_ESPIDF; then + ok "ESP-IDF already available" + else + echo "" + echo " ESP-IDF is required for ESP32 firmware builds." + echo " Install guide: https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/get-started/" + echo "" + echo " Quick install:" + echo " mkdir -p ~/esp && cd ~/esp" + echo " git clone --recursive https://github.com/espressif/esp-idf.git" + echo " cd esp-idf && git checkout v5.2" + echo " ./install.sh esp32s3" + echo " . ./export.sh" + warn "ESP-IDF not installed. Aggregator will be built but firmware flashing requires ESP-IDF." + fi +} + +install_field_deps() { + echo "" + echo -e " ${CYAN}Field deployment dependencies:${RESET}" + ok "Using Rust toolchain for WiFi-Mat build" + install_wasm_deps +} + +check_docker_deps() { + echo -e " ${CYAN}Docker dependencies:${RESET}" + if $HAS_DOCKER; then + ok "Docker available" + if docker compose version &>/dev/null; then + ok "Docker Compose available" + elif docker-compose --version &>/dev/null; then + ok "Docker Compose (standalone) available" + else + warn "Docker Compose not found" + fi + else + fail "Docker required for this profile" + exit 1 + fi +} + +# ====================================================================== +# STEP 6: BUILD +# ====================================================================== + +run_build() { + step "6/7" "Building (profile: ${PROFILE})" + echo "" + + case "$PROFILE" in + verify) + build_verify + ;; + python) + build_verify + build_python + ;; + rust) + build_rust + ;; + browser) + build_wasm + ;; + iot) + build_rust_crate "wifi-densepose-hardware" "ESP32 aggregator" + ;; + docker) + build_docker + ;; + field) + build_rust_crate "wifi-densepose-mat" "WiFi-Mat disaster module" + build_wasm_field + ;; + full) + build_verify + build_python + build_rust + if $HAS_WASM_PACK; then + build_wasm + fi + if $HAS_DOCKER; then + build_docker + fi + ;; + esac +} + +build_verify() { + echo -e " ${CYAN}Running pipeline verification...${RESET}" + echo "" + if "${SCRIPT_DIR}/verify" 2>&1; then + ok "Pipeline verification PASSED" + else + warn "Pipeline verification returned non-zero (see output above)" + fi +} + +build_python() { + echo "" + echo -e " ${CYAN}Setting up Python environment...${RESET}" + + # Create .env if it doesn't exist + if [ ! -f "${SCRIPT_DIR}/.env" ] && [ -f "${SCRIPT_DIR}/example.env" ]; then + cp "${SCRIPT_DIR}/example.env" "${SCRIPT_DIR}/.env" + ok "Created .env from example.env" + fi + + # Install package in development mode + if [ -f "${SCRIPT_DIR}/pyproject.toml" ]; then + echo " Installing wifi-densepose in development mode..." + (cd "${SCRIPT_DIR}" && $PYTHON_CMD -m pip install -e . 2>&1 | tail -3) + ok "Package installed in dev mode" + fi +} + +build_rust() { + echo -e " ${CYAN}Building Rust workspace (release)...${RESET}" + echo "" + + if [ ! -d "${RUST_DIR}" ]; then + fail "Rust workspace not found at ${RUST_DIR}" + return 1 + fi + + (cd "${RUST_DIR}" && cargo build --release 2>&1 | tail -10) + local exit_code=$? + + if [ $exit_code -eq 0 ]; then + ok "Rust workspace built successfully" + + # Show binary sizes + echo "" + echo -e " ${BOLD}Build artifacts:${RESET}" + local target_dir="${RUST_DIR}/target/release" + for bin in wifi-densepose-cli wifi-densepose-api; do + if [ -f "${target_dir}/${bin}" ]; then + local size + size=$(du -h "${target_dir}/${bin}" 2>/dev/null | cut -f1) + info " ${target_dir}/${bin} (${size})" + fi + done + + # Run tests + echo "" + echo -e " ${CYAN}Running Rust tests...${RESET}" + (cd "${RUST_DIR}" && cargo test --workspace 2>&1 | tail -5) + ok "Rust tests completed" + else + fail "Rust build failed (exit code: ${exit_code})" + fi +} + +build_rust_crate() { + local crate="$1" + local label="$2" + echo -e " ${CYAN}Building ${label}...${RESET}" + (cd "${RUST_DIR}" && cargo build --release --package "${crate}" 2>&1 | tail -5) + ok "${label} built" +} + +build_wasm() { + echo -e " ${CYAN}Building WASM package (browser profile ~10MB)...${RESET}" + echo "" + (cd "${RUST_DIR}" && wasm-pack build crates/wifi-densepose-wasm --target web --release 2>&1 | tail -10) + + if [ -d "${RUST_DIR}/crates/wifi-densepose-wasm/pkg" ]; then + local wasm_size + wasm_size=$(du -sh "${RUST_DIR}/crates/wifi-densepose-wasm/pkg" 2>/dev/null | cut -f1) + ok "WASM package built (${wasm_size})" + info "Output: ${RUST_DIR}/crates/wifi-densepose-wasm/pkg/" + else + warn "WASM package directory not found after build" + fi +} + +build_wasm_field() { + echo "" + echo -e " ${CYAN}Building WASM package with WiFi-Mat (field profile ~62MB)...${RESET}" + (cd "${RUST_DIR}" && wasm-pack build crates/wifi-densepose-wasm --target web --release -- --features mat 2>&1 | tail -10) + + if [ -d "${RUST_DIR}/crates/wifi-densepose-wasm/pkg" ]; then + local wasm_size + wasm_size=$(du -sh "${RUST_DIR}/crates/wifi-densepose-wasm/pkg" 2>/dev/null | cut -f1) + ok "Field WASM package built (${wasm_size})" + fi +} + +build_docker() { + echo -e " ${CYAN}Building Docker image...${RESET}" + echo "" + + local target="production" + if $VERBOSE; then + target="development" + fi + + (cd "${SCRIPT_DIR}" && docker build --target "${target}" -t wifi-densepose:latest . 2>&1 | tail -10) + + if docker images wifi-densepose:latest --format "{{.Size}}" 2>/dev/null | head -1; then + ok "Docker image built" + fi +} + +# ====================================================================== +# STEP 7: POST-INSTALL SUMMARY +# ====================================================================== + +post_install() { + step "7/7" "Installation Complete" + echo "" + + echo -e "${BOLD}======================================================================" + echo " WiFi-DensePose: Installation Summary" + echo -e "======================================================================${RESET}" + echo "" + + echo -e " ${BOLD}Profile:${RESET} ${PROFILE}" + echo -e " ${BOLD}OS:${RESET} ${OS_TYPE} (${ARCH})" + echo -e " ${BOLD}RAM:${RESET} ${TOTAL_RAM_MB} MB" + if $HAS_WIFI; then + echo -e " ${BOLD}WiFi:${RESET} ${WIFI_IFACE}" + fi + if $HAS_GPU; then + echo -e " ${BOLD}GPU:${RESET} ${GPU_INFO}" + fi + echo "" + + echo -e " ${BOLD}Next steps:${RESET}" + echo "" + + case "$PROFILE" in + verify) + echo " # Re-run verification at any time:" + echo " ./verify" + echo "" + echo " # Upgrade to a richer profile:" + echo " ./install.sh --profile python # Add API server" + echo " ./install.sh --profile rust # Add Rust performance" + ;; + python) + echo " # Start the API server:" + echo " uvicorn v1.src.api.main:app --host 0.0.0.0 --port 8000" + echo "" + echo " # Open API docs: http://localhost:8000/docs" + echo "" + if $HAS_WIFI; then + echo " # With WiFi detected (${WIFI_IFACE}), commodity sensing is available:" + echo " # The system can detect presence and motion via RSSI." + fi + ;; + rust) + echo " # Run benchmarks:" + echo " cd rust-port/wifi-densepose-rs" + echo " cargo bench --package wifi-densepose-signal" + echo "" + echo " # Start Rust API server:" + echo " cargo run --release --package wifi-densepose-api" + ;; + browser) + echo " # WASM package is at:" + echo " # rust-port/wifi-densepose-rs/crates/wifi-densepose-wasm/pkg/" + echo "" + echo " # Open the 3D visualization:" + echo " python3 -m http.server 3000 --directory ui" + echo " # Then open: http://localhost:3000/viz.html" + ;; + iot) + echo " # Flash ESP32-S3 nodes:" + echo " cd firmware/esp32-csi-node" + echo " idf.py set-target esp32s3" + echo " idf.py menuconfig # Set WiFi SSID, aggregator IP" + echo " idf.py build flash monitor" + echo "" + echo " # Start the aggregator:" + echo " cd rust-port/wifi-densepose-rs" + echo " cargo run --release --package wifi-densepose-hardware -- \\" + echo " --mode esp32-aggregator --port 5000" + ;; + docker) + echo " # Development (with Postgres, Redis, Prometheus, Grafana):" + echo " docker compose up" + echo "" + echo " # Production:" + echo " docker run -d -p 8000:8000 wifi-densepose:latest" + ;; + field) + echo " # WiFi-Mat disaster response module built." + echo "" + echo " # Run WiFi-Mat tests:" + echo " cd rust-port/wifi-densepose-rs" + echo " cargo test --package wifi-densepose-mat" + echo "" + echo " # Field deployment WASM package at:" + echo " # rust-port/wifi-densepose-rs/crates/wifi-densepose-wasm/pkg/" + ;; + full) + echo " # Verification: ./verify" + echo " # Python API: uvicorn v1.src.api.main:app --host 0.0.0.0 --port 8000" + echo " # Rust API: cd rust-port/wifi-densepose-rs && cargo run --release --package wifi-densepose-api" + echo " # Benchmarks: cd rust-port/wifi-densepose-rs && cargo bench" + echo " # Visualization: python3 -m http.server 3000 --directory ui" + echo " # Docker: docker compose up" + ;; + esac + + echo "" + echo -e " ${BOLD}RVF Container Sizes:${RESET}" + echo " IoT (ESP32): ~0.7 MB (int4 quantized)" + echo " Browser (Chrome): ~10 MB (int8 quantized)" + echo " Mobile (WebView): ~6 MB (int8 quantized)" + echo " Field (Disaster): ~62 MB (fp16 weights)" + echo "" + + echo -e " ${BOLD}Documentation:${RESET}" + echo " Build guide: docs/build-guide.md" + echo " Architecture: docs/adr/" + echo " SOTA research: docs/research/wifi-sensing-ruvector-sota-2026.md" + echo "" + + echo -e " ${BOLD}Trust verification:${RESET}" + echo " ./verify # One-command proof replay" + echo " make verify-audit # Full audit with mock scan" + echo "" + + echo -e " Install log saved to: ${INSTALL_LOG}" + echo "" + echo -e "${BOLD}======================================================================${RESET}" +} + +# ====================================================================== +# MAIN +# ====================================================================== + +main() { + banner + detect_system + detect_toolchains + detect_wifi_hardware + + if $CHECK_ONLY; then + echo "" + echo -e "${BOLD}Hardware check complete. Run without --check-only to install.${RESET}" + exit 0 + fi + + recommend_profile + + if [ -z "$PROFILE" ]; then + echo "No profile selected. Exiting." + exit 0 + fi + + # Confirm + if ! $SKIP_CONFIRM; then + echo "" + read -rp " Proceed with '${PROFILE}' installation? [Y/n]: " confirm + if [[ "$confirm" =~ ^[Nn] ]]; then + echo " Cancelled." + exit 0 + fi + fi + + install_deps + run_build + post_install +} + +main