#!/bin/bash # HOT-Step CPP — macOS Build-from-Source Installer # # One command to go from fresh clone to working app: # ./install.sh # # What this does: # 1. Checks prerequisites (Xcode, Metal Toolchain, Node.js, CMake) # 2. Initializes git submodules # 3. Builds the C++ engine with Metal GPU acceleration # 4. Installs server + UI dependencies # 5. Builds the production UI # # After install completes, run ./launch.sh to start the app. set -e cd "$(dirname "$0")" ROOT_DIR="$(pwd)" # ── Colors ────────────────────────────────────────────────────────── RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' step() { echo -e "\n${CYAN}${BOLD}[$1/$TOTAL_STEPS]${NC} ${BOLD}$2${NC}"; } ok() { echo -e " ${GREEN}✓${NC} $1"; } warn() { echo -e " ${YELLOW}⚠${NC} $1"; } fail() { echo -e " ${RED}✗ $1${NC}"; exit 1; } TOTAL_STEPS=6 echo "" echo -e "${BOLD}╔══════════════════════════════════════════╗${NC}" echo -e "${BOLD}║ HOT-Step CPP — macOS Installer ║${NC}" echo -e "${BOLD}║ Build from Source (Apple Silicon) ║${NC}" echo -e "${BOLD}╚══════════════════════════════════════════╝${NC}" echo "" # ── Step 1: Check Prerequisites ───────────────────────────────────── step 1 "Checking prerequisites..." # macOS check if [[ "$(uname)" != "Darwin" ]]; then fail "This script is for macOS only." fi ok "macOS $(sw_vers -productVersion) ($(uname -m))" # Xcode or Command Line Tools if xcode-select -p &>/dev/null; then XCODE_PATH="$(xcode-select -p)" if [[ "$XCODE_PATH" == *"Xcode.app"* ]]; then ok "Xcode installed at ${XCODE_PATH}" else ok "Command Line Tools installed" fi else fail "Xcode or Command Line Tools required.\n Install: xcode-select --install" fi # Metal compiler if xcrun metal --version &>/dev/null; then ok "Metal compiler available" else warn "Metal compiler not found." echo " This is required for GPU acceleration." echo "" echo " If you have Xcode installed, download the Metal Toolchain:" echo " sudo xcodebuild -runFirstLaunch" echo " xcodebuild -downloadComponent MetalToolchain" echo "" echo " Without Metal, the engine will still build but will use CPU only." echo "" read -p " Continue without Metal? [y/N] " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1 fi NO_METAL=1 fi # CMake if command -v cmake &>/dev/null; then ok "CMake $(cmake --version | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" else fail "CMake not found.\n Install: brew install cmake" fi # Git if command -v git &>/dev/null; then ok "Git $(git --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" else fail "Git not found." fi # ── Step 2: Find or install Node.js ────────────────────────────────── step 2 "Checking Node.js..." find_compatible_node() { # Priority: brew node@22 > system node (if compatible) local candidates=( "/opt/homebrew/opt/node@22/bin/node" "/usr/local/opt/node@22/bin/node" ) for candidate in "${candidates[@]}"; do if [ -x "$candidate" ]; then local ver ver="$("$candidate" --version 2>/dev/null | tr -d 'v')" local major="${ver%%.*}" if [ "$major" -ge 18 ] && [ "$major" -lt 24 ] 2>/dev/null; then echo "$candidate" return 0 fi fi done # Check system node if command -v node &>/dev/null; then local ver ver="$(node --version 2>/dev/null | tr -d 'v')" local major="${ver%%.*}" if [ "$major" -ge 18 ] && [ "$major" -lt 24 ] 2>/dev/null; then echo "$(command -v node)" return 0 fi fi return 1 } NODE_BIN="" if NODE_BIN=$(find_compatible_node); then NODE_VER="$("$NODE_BIN" --version)" NODE_DIR="$(dirname "$NODE_BIN")" ok "Node.js ${NODE_VER} at ${NODE_BIN}" else # Try to install via brew echo " Node.js 18-22 required (Node 24+ is not supported)." if command -v brew &>/dev/null; then echo "" read -p " Install Node.js 22 via Homebrew? [Y/n] " -n 1 -r echo if [[ ! $REPLY =~ ^[Nn]$ ]]; then echo " Installing node@22..." brew install node@22 NODE_BIN="/opt/homebrew/opt/node@22/bin/node" if [ ! -x "$NODE_BIN" ]; then NODE_BIN="/usr/local/opt/node@22/bin/node" fi if [ -x "$NODE_BIN" ]; then NODE_DIR="$(dirname "$NODE_BIN")" ok "Node.js $("$NODE_BIN" --version) installed" else fail "Node.js installation failed." fi else fail "Node.js 18-22 is required. Install manually:\n brew install node@22" fi else fail "Node.js 18-22 required.\n Install Homebrew first: https://brew.sh\n Then: brew install node@22" fi fi # Export so npm/npx use the right Node export PATH="${NODE_DIR}:${PATH}" # ── Step 3: Initialize submodules ──────────────────────────────────── step 3 "Initializing git submodules..." if [ -f "engine/ggml/CMakeLists.txt" ] && [ -f "engine/vendor/vst3sdk/CMakeLists.txt" ]; then ok "Submodules already initialized" else git submodule update --init --recursive ok "Submodules initialized" fi # ── Step 4: Build the C++ engine ───────────────────────────────────── step 4 "Building C++ engine (this may take a few minutes)..." cd engine mkdir -p build cd build CMAKE_FLAGS=( -DCMAKE_BUILD_TYPE=Release -DGGML_BACKEND_DL=OFF ) if [ -z "${NO_METAL}" ]; then CMAKE_FLAGS+=(-DGGML_METAL=ON -DGGML_METAL_EMBED_LIBRARY=ON) echo " GPU: Metal (Apple Silicon)" else echo " GPU: None (CPU-only build)" fi # Detect Ninja for faster builds if command -v ninja &>/dev/null; then CMAKE_FLAGS+=(-G Ninja) echo " Generator: Ninja" else echo " Generator: Make (install ninja for faster builds: brew install ninja)" fi cmake .. "${CMAKE_FLAGS[@]}" 2>&1 | tail -5 echo "" NCPU=$(sysctl -n hw.ncpu 2>/dev/null || echo 4) cmake --build . --config Release -j "${NCPU}" 2>&1 | grep -E '^\[|Built target|Linking|error:' echo "" [ -f ace-server ] && ok "ace-server" || warn "ace-server not built" [ -f mastering ] && ok "mastering" || warn "mastering not built" [ -f mp3-codec ] && ok "mp3-codec" || warn "mp3-codec not built" [ -f vst-host ] && ok "vst-host" || warn "vst-host not built" cd "${ROOT_DIR}" # ── Step 5: Install Node.js dependencies ───────────────────────────── step 5 "Installing Node.js dependencies..." echo " Server..." cd server && npm install --loglevel=warn 2>&1 | tail -3 && cd .. ok "Server dependencies installed" echo " UI..." cd ui && npm install --loglevel=warn 2>&1 | tail -3 && cd .. ok "UI dependencies installed" # ── Step 6: Build the UI ───────────────────────────────────────────── step 6 "Building production UI..." cd ui npx vite build 2>&1 | tail -5 cd .. ok "UI built → ui/dist/" # ── Done! ───────────────────────────────────────────────────────────── echo "" echo -e "${GREEN}${BOLD}╔══════════════════════════════════════════╗${NC}" echo -e "${GREEN}${BOLD}║ Installation complete! 🎉 ║${NC}" echo -e "${GREEN}${BOLD}╚══════════════════════════════════════════╝${NC}" echo "" echo " Next steps:" echo "" echo " 1. Start the app:" echo -e " ${CYAN}./launch.sh${NC}" echo "" echo " 2. Open in your browser:" echo -e " ${CYAN}http://localhost:3001${NC}" echo "" echo " 3. Download AI models (~7 GB):" echo " Click Models → Get More Models in the app" echo ""