#!/bin/bash # NomadArch Installer for macOS # Version: 0.6.0 - Robust Edition # Exit on undefined variables set -u # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' BOLD='\033[1m' # Script directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" TARGET_DIR="$SCRIPT_DIR" BIN_DIR="$TARGET_DIR/bin" LOG_FILE="$TARGET_DIR/install.log" ERRORS=0 WARNINGS=0 BINARY_FREE_MODE=1 # Logging function log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE" } print_header() { echo "" echo -e "${CYAN}╔═══════════════════════════════════════════════════════════════╗${NC}" echo -e "${CYAN}║${NC} ${BOLD}NomadArch Installer for macOS${NC} ${CYAN}║${NC}" echo -e "${CYAN}║${NC} Version: 0.6.0 - Robust Edition ${CYAN}║${NC}" echo -e "${CYAN}╚═══════════════════════════════════════════════════════════════╝${NC}" echo "" } print_header log "========== Installer started ==========" # ═══════════════════════════════════════════════════════════════ # STEP 1: OS and Architecture Detection # ═══════════════════════════════════════════════════════════════ echo "[STEP 1/8] Detecting System..." OS_TYPE=$(uname -s) ARCH_TYPE=$(uname -m) log "OS: $OS_TYPE, Arch: $ARCH_TYPE" if [[ "$OS_TYPE" != "Darwin" ]]; then echo -e "${RED}[ERROR]${NC} This installer is for macOS. Current OS: $OS_TYPE" echo " Use Install-Linux.sh for Linux or Install-Windows.bat for Windows." log "ERROR: Not macOS ($OS_TYPE)" exit 1 fi case "$ARCH_TYPE" in arm64) ARCH="arm64" ;; x86_64) ARCH="x64" ;; *) echo -e "${YELLOW}[WARN]${NC} Unusual architecture: $ARCH_TYPE (proceeding anyway)" ARCH="$ARCH_TYPE" ((WARNINGS++)) || true ;; esac # Get macOS version MACOS_VERSION=$(sw_vers -productVersion 2>/dev/null || echo "unknown") echo -e "${GREEN}[OK]${NC} OS: macOS $MACOS_VERSION" echo -e "${GREEN}[OK]${NC} Architecture: $ARCH_TYPE ($ARCH)" log "macOS $MACOS_VERSION, Arch: $ARCH_TYPE" # ═══════════════════════════════════════════════════════════════ # STEP 2: Check Write Permissions # ═══════════════════════════════════════════════════════════════ echo "" echo "[STEP 2/8] Checking Write Permissions..." mkdir -p "$BIN_DIR" 2>/dev/null || true if ! touch "$SCRIPT_DIR/.install-write-test" 2>/dev/null; then echo -e "${YELLOW}[WARN]${NC} No write access to $SCRIPT_DIR" TARGET_DIR="$HOME/.nomadarch" BIN_DIR="$TARGET_DIR/bin" LOG_FILE="$TARGET_DIR/install.log" mkdir -p "$BIN_DIR" if ! touch "$TARGET_DIR/.install-write-test" 2>/dev/null; then echo -e "${RED}[ERROR]${NC} Cannot write to $TARGET_DIR" log "ERROR: Write permission denied" exit 1 fi rm -f "$TARGET_DIR/.install-write-test" echo -e "${GREEN}[OK]${NC} Using fallback: $TARGET_DIR" else rm -f "$SCRIPT_DIR/.install-write-test" echo -e "${GREEN}[OK]${NC} Write access verified" fi log "Install target: $TARGET_DIR" # ═══════════════════════════════════════════════════════════════ # STEP 3: Check Xcode Command Line Tools # ═══════════════════════════════════════════════════════════════ echo "" echo "[STEP 3/8] Checking Xcode Command Line Tools..." if xcode-select -p >/dev/null 2>&1; then echo -e "${GREEN}[OK]${NC} Xcode Command Line Tools installed" else echo -e "${BLUE}[INFO]${NC} Installing Xcode Command Line Tools..." echo -e "${YELLOW}[NOTE]${NC} A dialog may appear - click 'Install' to proceed" xcode-select --install 2>/dev/null || true # Wait for installation echo -e "${BLUE}[INFO]${NC} Waiting for Xcode tools installation..." while ! xcode-select -p >/dev/null 2>&1; do sleep 5 done echo -e "${GREEN}[OK]${NC} Xcode Command Line Tools installed" fi # ═══════════════════════════════════════════════════════════════ # STEP 4: Check and Install Homebrew + Node.js # ═══════════════════════════════════════════════════════════════ echo "" echo "[STEP 4/8] Checking Homebrew and Node.js..." BREW_OK=0 NODE_OK=0 NPM_OK=0 # Check Homebrew if command -v brew >/dev/null 2>&1; then echo -e "${GREEN}[OK]${NC} Homebrew: $(brew --version | head -1)" BREW_OK=1 else echo -e "${BLUE}[INFO]${NC} Homebrew not found. Installing..." log "Installing Homebrew" if /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"; then # Add Homebrew to PATH for Apple Silicon if [[ "$ARCH_TYPE" == "arm64" ]]; then eval "$(/opt/homebrew/bin/brew shellenv)" 2>/dev/null || true echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile 2>/dev/null || true fi # Verify if command -v brew >/dev/null 2>&1; then echo -e "${GREEN}[OK]${NC} Homebrew installed successfully" BREW_OK=1 else echo -e "${YELLOW}[WARN]${NC} Homebrew installed but not in PATH" echo -e "${YELLOW}[INFO]${NC} You may need to restart your terminal" ((WARNINGS++)) || true # Try to find it if [[ -f "/opt/homebrew/bin/brew" ]]; then eval "$(/opt/homebrew/bin/brew shellenv)" BREW_OK=1 elif [[ -f "/usr/local/bin/brew" ]]; then eval "$(/usr/local/bin/brew shellenv)" BREW_OK=1 fi fi else echo -e "${RED}[ERROR]${NC} Failed to install Homebrew" log "ERROR: Homebrew installation failed" ((WARNINGS++)) || true fi fi # Check Node.js if command -v node >/dev/null 2>&1; then NODE_VERSION=$(node --version 2>/dev/null || echo "unknown") if [[ "$NODE_VERSION" != "unknown" ]]; then NODE_MAJOR=$(echo "$NODE_VERSION" | sed 's/v//' | cut -d'.' -f1) echo -e "${GREEN}[OK]${NC} Node.js: $NODE_VERSION" NODE_OK=1 if [[ $NODE_MAJOR -lt 18 ]]; then echo -e "${YELLOW}[WARN]${NC} Node.js 18+ recommended (current: $NODE_VERSION)" ((WARNINGS++)) || true fi fi fi # Install Node.js if needed if [[ $NODE_OK -eq 0 ]]; then echo -e "${BLUE}[INFO]${NC} Node.js not found. Installing..." log "Installing Node.js" INSTALL_SUCCESS=0 # Try Homebrew first if [[ $BREW_OK -eq 1 ]]; then if brew install node 2>&1; then INSTALL_SUCCESS=1 fi fi # Try nvm as fallback if [[ $INSTALL_SUCCESS -eq 0 ]]; then echo -e "${BLUE}[INFO]${NC} Trying nvm installation..." export NVM_DIR="$HOME/.nvm" if curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh 2>/dev/null | bash >/dev/null 2>&1; then [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" if nvm install 20 2>&1; then INSTALL_SUCCESS=1 fi fi fi # Try official pkg installer as last resort if [[ $INSTALL_SUCCESS -eq 0 ]]; then echo -e "${BLUE}[INFO]${NC} Downloading Node.js installer..." NODE_PKG="/tmp/node-installer.pkg" if curl -fsSL "https://nodejs.org/dist/v20.10.0/node-v20.10.0.pkg" -o "$NODE_PKG" 2>/dev/null; then echo -e "${BLUE}[INFO]${NC} Running Node.js installer (may require password)..." sudo installer -pkg "$NODE_PKG" -target / && INSTALL_SUCCESS=1 rm -f "$NODE_PKG" fi fi if [[ $INSTALL_SUCCESS -eq 1 ]]; then # Verify installation hash -r 2>/dev/null || true if command -v node >/dev/null 2>&1; then NODE_VERSION=$(node --version) echo -e "${GREEN}[OK]${NC} Node.js installed: $NODE_VERSION" NODE_OK=1 fi fi if [[ $NODE_OK -eq 0 ]]; then echo -e "${RED}[ERROR]${NC} Could not install Node.js automatically" echo "" echo "Please install Node.js manually:" echo " 1. Install via Homebrew: brew install node" echo " 2. Or download from: https://nodejs.org/" echo "" log "ERROR: Node.js installation failed" ERRORS=$((ERRORS+1)) fi fi # Check npm if command -v npm >/dev/null 2>&1; then NPM_VERSION=$(npm --version 2>/dev/null || echo "unknown") if [[ "$NPM_VERSION" != "unknown" ]]; then echo -e "${GREEN}[OK]${NC} npm: $NPM_VERSION" NPM_OK=1 fi fi if [[ $NPM_OK -eq 0 && $NODE_OK -eq 1 ]]; then echo -e "${RED}[ERROR]${NC} npm not found (should come with Node.js)" ERRORS=$((ERRORS+1)) fi # ═══════════════════════════════════════════════════════════════ # STEP 5: Check Git (optional) # ═══════════════════════════════════════════════════════════════ echo "" echo "[STEP 5/8] Checking Optional Dependencies..." if command -v git >/dev/null 2>&1; then echo -e "${GREEN}[OK]${NC} Git: $(git --version)" else echo -e "${YELLOW}[INFO]${NC} Git not found (optional - installing via Homebrew)" if [[ $BREW_OK -eq 1 ]]; then brew install git 2>/dev/null || true fi fi if command -v curl >/dev/null 2>&1; then echo -e "${GREEN}[OK]${NC} curl: available" else echo -e "${RED}[ERROR]${NC} curl not found (required)" ERRORS=$((ERRORS+1)) fi # ═══════════════════════════════════════════════════════════════ # STEP 6: Install npm Dependencies # ═══════════════════════════════════════════════════════════════ echo "" echo "[STEP 6/8] Installing Dependencies..." if [[ $NODE_OK -eq 0 || $NPM_OK -eq 0 ]]; then echo -e "${YELLOW}[SKIP]${NC} Skipping npm install (Node.js/npm not available)" else cd "$SCRIPT_DIR" if [[ ! -f "package.json" ]]; then echo -e "${RED}[ERROR]${NC} package.json not found in $SCRIPT_DIR" echo "Make sure you extracted the full NomadArch package." log "ERROR: package.json missing" ERRORS=$((ERRORS+1)) else echo -e "${BLUE}[INFO]${NC} Running npm install (this may take a few minutes)..." log "Running npm install" if npm install --no-audit --no-fund 2>&1; then echo -e "${GREEN}[OK]${NC} Dependencies installed" else echo -e "${YELLOW}[WARN]${NC} npm install had issues, trying with legacy peer deps..." if npm install --legacy-peer-deps --no-audit --no-fund 2>&1; then echo -e "${GREEN}[OK]${NC} Dependencies installed (with legacy peer deps)" else echo -e "${RED}[ERROR]${NC} npm install failed" log "ERROR: npm install failed" ERRORS=$((ERRORS+1)) fi fi fi fi # ═══════════════════════════════════════════════════════════════ # STEP 7: Build UI Assets # ═══════════════════════════════════════════════════════════════ echo "" echo "[STEP 7/8] Building UI Assets..." if [[ $NODE_OK -eq 0 || $NPM_OK -eq 0 ]]; then echo -e "${YELLOW}[SKIP]${NC} Skipping UI build (Node.js/npm not available)" elif [[ -f "$SCRIPT_DIR/packages/ui/dist/index.html" ]]; then echo -e "${GREEN}[OK]${NC} UI build already exists" else echo -e "${BLUE}[INFO]${NC} Building UI (this may take 1-2 minutes)..." cd "$SCRIPT_DIR/packages/ui" if npm run build 2>&1; then echo -e "${GREEN}[OK]${NC} UI assets built successfully" else echo -e "${RED}[ERROR]${NC} UI build failed" log "ERROR: UI build failed" ERRORS=$((ERRORS+1)) fi cd "$SCRIPT_DIR" fi # ═══════════════════════════════════════════════════════════════ # STEP 8: Health Check and Summary # ═══════════════════════════════════════════════════════════════ echo "" echo "[STEP 8/8] Running Health Check..." HEALTH_ERRORS=0 [[ -f "$SCRIPT_DIR/package.json" ]] || { echo -e "${RED}[FAIL]${NC} package.json missing"; HEALTH_ERRORS=$((HEALTH_ERRORS+1)); } [[ -d "$SCRIPT_DIR/packages/ui" ]] || { echo -e "${RED}[FAIL]${NC} packages/ui missing"; HEALTH_ERRORS=$((HEALTH_ERRORS+1)); } [[ -d "$SCRIPT_DIR/packages/server" ]] || { echo -e "${RED}[FAIL]${NC} packages/server missing"; HEALTH_ERRORS=$((HEALTH_ERRORS+1)); } if [[ $NODE_OK -eq 1 && $NPM_OK -eq 1 ]]; then [[ -d "$SCRIPT_DIR/node_modules" ]] || { echo -e "${RED}[FAIL]${NC} node_modules missing"; HEALTH_ERRORS=$((HEALTH_ERRORS+1)); } [[ -f "$SCRIPT_DIR/packages/ui/dist/index.html" ]] || { echo -e "${YELLOW}[WARN]${NC} UI build missing"; ((WARNINGS++)) || true; } fi if [[ $HEALTH_ERRORS -eq 0 ]]; then echo -e "${GREEN}[OK]${NC} Health checks passed" else ERRORS=$((ERRORS+HEALTH_ERRORS)) fi # Summary echo "" echo -e "${CYAN}╔═══════════════════════════════════════════════════════════════╗${NC}" echo -e "${CYAN}║${NC} ${BOLD}INSTALLATION SUMMARY${NC} ${CYAN}║${NC}" echo -e "${CYAN}╚═══════════════════════════════════════════════════════════════╝${NC}" echo "" echo " Install Directory: $TARGET_DIR" echo " Architecture: $ARCH ($ARCH_TYPE)" echo " macOS Version: $MACOS_VERSION" [[ -n "${NODE_VERSION:-}" ]] && echo " Node.js: $NODE_VERSION" [[ -n "${NPM_VERSION:-}" ]] && echo " npm: $NPM_VERSION" echo " Mode: Binary-Free Mode" echo " Errors: $ERRORS" echo " Warnings: $WARNINGS" echo " Log File: $LOG_FILE" echo "" if [[ $ERRORS -gt 0 ]]; then echo -e "${RED}╔═══════════════════════════════════════════════════════════════╗${NC}" echo -e "${RED}║${NC} ${BOLD}INSTALLATION COMPLETED WITH ERRORS${NC} ${RED}║${NC}" echo -e "${RED}╚═══════════════════════════════════════════════════════════════╝${NC}" echo "" echo "Review the errors above and check: $LOG_FILE" echo "" echo "Common fixes:" echo " 1. Install Homebrew: /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"" echo " 2. Install Node.js: brew install node" echo " 3. Restart terminal after Homebrew installation" echo "" log "Installation FAILED with $ERRORS errors" else echo -e "${GREEN}╔═══════════════════════════════════════════════════════════════╗${NC}" echo -e "${GREEN}║${NC} ${BOLD}INSTALLATION SUCCESSFUL!${NC} ${GREEN}║${NC}" echo -e "${GREEN}╚═══════════════════════════════════════════════════════════════╝${NC}" echo "" echo "To start NomadArch, run:" echo " ./Launch-Unix.sh" echo "" echo "Available Free Models:" echo " - GPT-5 Nano (fast)" echo " - Grok Code (coding)" echo " - GLM-4.7 (general)" echo " - Doubao (creative)" echo "" log "Installation SUCCESSFUL" fi exit $ERRORS