#!/bin/bash # ============================================================================= # DEVELOP — Installer dell'intelligenza (Brain) sul Mac # Uso: doppio click su install.command oppure: # curl -fsSL https://developcontroller.com/install.command | bash # 100% locale: scarica il Brain, il motore AI e i modelli, imposta l'avvio # automatico e verifica. Nessun dato esce dal Mac, nessuna chiave richiesta. # ============================================================================= PKG_URL="${DEVELOP_PKG_URL:-https://developcontroller.com/develop-brain.tar.gz}" INSTALL_DIR="${DEVELOP_HOME:-$HOME/DEVELOP_BRAIN}" PY="/usr/bin/python3" PORT="${BRAIN_PORT:-8088}" PLIST="$HOME/Library/LaunchAgents/com.develop.brain.plist" TMP="$(mktemp -d)" c_gold="\033[1;33m"; c_ok="\033[1;32m"; c_err="\033[1;31m"; c_off="\033[0m" say(){ printf "${c_gold}• %s${c_off}\n" "$1"; } ok(){ printf "${c_ok}✓ %s${c_off}\n" "$1"; } err(){ printf "${c_err}✗ %s${c_off}\n" "$1" >&2; } cleanup(){ rm -rf "$TMP"; } trap cleanup EXIT echo "===============================================" echo " DEVELOP — Installa l'intelligenza sul Mac" echo "===============================================" # 1) Requisiti ----------------------------------------------------------------- [ "$(uname)" = "Darwin" ] || { err "Serve macOS."; exit 1; } ARCH="$(uname -m)" [ "$ARCH" = "arm64" ] || say "Consigliato Mac Apple Silicon (rilevato: $ARCH)." command -v "$PY" >/dev/null 2>&1 || { err "Python 3 di sistema non trovato (installa gli Strumenti da riga di comando Xcode)."; exit 1; } ok "macOS $ARCH · Python di sistema OK" # Modello in base alla RAM: 14B solo se >=24GB, altrimenti 8B (gira bene su 16GB) RAM_GB=$(( $(sysctl -n hw.memsize 2>/dev/null || echo 0) / 1073741824 )) if [ "$RAM_GB" -ge 24 ]; then MODEL="qwen3:14b"; MODEL_LABEL="Genius 2.0"; else MODEL="qwen3:8b"; MODEL_LABEL="Brain 1.0"; fi ai_label(){ case "$1" in qwen3:14b) echo "Genius 2.0";; qwen3:8b|qwen3:4b) echo "Brain 1.0";; gemma3:4b) echo "modulo visione";; qwen3-vl:4b-instruct) echo "modulo trova oggetti";; nomic-embed-text) echo "modulo memoria";; qwen2.5-coder:7b) echo "modulo sviluppo";; *) echo "componente AI";; esac; } say "RAM ${RAM_GB} GB → intelligenza $MODEL_LABEL" # 2) Pacchetto Brain: dalla CHIAVETTA se presente, altrimenti download -------- say "Preparo l'intelligenza…" mkdir -p "$INSTALL_DIR" SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" 2>/dev/null && pwd || echo .)" # BUNDLE = cartella con risorse offline (app + motore + modelli) su chiavetta USB. # La imposta il launcher (DEVELOP_BUNDLE_DIR) o si cerca accanto/sopra lo script. BUNDLE="${DEVELOP_BUNDLE_DIR:-}" [ -z "$BUNDLE" ] && [ -d "$SELF_DIR/motore_ai" -o -d "$SELF_DIR/modelli_ai" -o -f "$SELF_DIR/app/develop-brain.tar.gz" ] && BUNDLE="$SELF_DIR" [ -z "$BUNDLE" ] && [ -d "$SELF_DIR/../motore_ai" ] && BUNDLE="$(cd "$SELF_DIR/.." && pwd)" if [ -n "$BUNDLE" ] && [ -f "$BUNDLE/app/develop-brain.tar.gz" ]; then cp "$BUNDLE/app/develop-brain.tar.gz" "$TMP/brain.tar.gz" && ok "Uso il pacchetto dalla chiavetta" elif [ -f "$SELF_DIR/develop-brain.tar.gz" ]; then cp "$SELF_DIR/develop-brain.tar.gz" "$TMP/brain.tar.gz" && ok "Uso il pacchetto dalla chiavetta" elif ! curl -fsSL "$PKG_URL" -o "$TMP/brain.tar.gz"; then err "Pacchetto non trovato (né sulla chiavetta né da $PKG_URL)"; exit 1 fi tar -xzf "$TMP/brain.tar.gz" -C "$INSTALL_DIR" && ok "Intelligenza installata in $INSTALL_DIR" || { err "Estrazione fallita"; exit 1; } [ -f "$INSTALL_DIR/brain_server.py" ] || { err "Pacchetto incompleto (brain_server.py mancante)."; exit 1; } # 3) Dipendenze Python -------------------------------------------------------- say "Preparo le dipendenze…" "$PY" -m pip install --user --quiet --disable-pip-version-check pillow >/dev/null 2>&1 \ && ok "Dipendenze pronte" || say "Pillow non installato: l'analisi immagini sarà ridotta." # 4) Motore AI locale + modelli ---------------------------------------------- if ! command -v ollama >/dev/null 2>&1 && [ ! -d "/Applications/Ollama.app" ]; then say "Installo il motore AI locale…" if [ -n "$BUNDLE" ] && [ -f "$BUNDLE/motore_ai/Ollama-darwin.zip" ]; then /usr/bin/ditto -x -k "$BUNDLE/motore_ai/Ollama-darwin.zip" /Applications/ && ok "Motore AI installato (dalla chiavetta)" \ || say "Estrazione motore AI dalla chiavetta non riuscita." elif curl -fsSL https://ollama.com/download/Ollama-darwin.zip -o "$TMP/ollama.zip"; then /usr/bin/ditto -x -k "$TMP/ollama.zip" /Applications/ && ok "Motore AI installato" \ || say "Estrazione motore AI non riuscita: installa da ollama.com." else say "Scarica il motore AI da ollama.com se voce/visione locale non parte." fi fi OLLAMA_BIN="$(command -v ollama || echo /Applications/Ollama.app/Contents/Resources/ollama)" say "Avvio il motore AI…" ( "$OLLAMA_BIN" serve >/dev/null 2>&1 & ) 2>/dev/null sleep 3 if [ -n "$BUNDLE" ] && [ -d "$BUNDLE/modelli_ai/models" ]; then say "Copio i modelli dalla chiavetta (niente download)…" mkdir -p "$HOME/.ollama/models" cp -R "$BUNDLE/modelli_ai/models/." "$HOME/.ollama/models/" 2>/dev/null && ok "Modelli copiati dalla chiavetta" fi say "Preparo i modelli (se non gia' presenti, li scarico — solo la prima volta)…" for m in "$MODEL" gemma3:4b nomic-embed-text qwen2.5-coder:7b qwen3-vl:4b-instruct; do "$OLLAMA_BIN" pull "$m" >/dev/null 2>&1 && ok "$(ai_label "$m") pronto" || say "$(ai_label "$m") non scaricato (riprova più tardi)." done # 4b) Voce locale: trascrizione sul Mac (modello ggml-base, 141 MB) ----------- VOICE_DIR="$INSTALL_DIR/models"; VOICE_MODEL="$VOICE_DIR/ggml-base.bin" VOICE_SHA="60ed5bc3dd14eea856493d334349b405782ddcaf0028d4b5df4088345fba2efe" mkdir -p "$VOICE_DIR" if [ "$(shasum -a 256 "$VOICE_MODEL" 2>/dev/null | cut -d' ' -f1)" != "$VOICE_SHA" ]; then say "Scarico il modello della voce (141 MB, solo la prima volta)…" if curl -fL --retry 2 -s -o "$TMP/ggml-base.bin" "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin" \ && [ "$(shasum -a 256 "$TMP/ggml-base.bin" | cut -d' ' -f1)" = "$VOICE_SHA" ]; then mv "$TMP/ggml-base.bin" "$VOICE_MODEL" && ok "Modello della voce pronto" else say "Modello della voce non scaricato o non verificato: la scrittura a voce resta disattiva." fi fi if ! command -v whisper-cli >/dev/null 2>&1; then if command -v brew >/dev/null 2>&1; then brew install whisper-cpp >/dev/null 2>&1 && ok "Trascrizione vocale (whisper-cli) pronta" \ || say "whisper-cli non installato: riprova con brew install whisper-cpp" else say "Per scrivere a voce installa Homebrew (brew.sh) e poi: brew install whisper-cpp" fi fi # 5) Configurazione locale (nessun segreto) ----------------------------------- if [ ! -f "$INSTALL_DIR/.env" ]; then cat > "$INSTALL_DIR/.env" < "$PLIST" < Labelcom.develop.brain ProgramArguments$PY$INSTALL_DIR/brain_server.py WorkingDirectory$INSTALL_DIR RunAtLoad KeepAlive StandardOutPath$INSTALL_DIR/brain.out StandardErrorPath$INSTALL_DIR/brain.err EOF launchctl unload "$PLIST" >/dev/null 2>&1 launchctl load "$PLIST" >/dev/null 2>&1 && ok "Avvio automatico attivo" || say "Impostazione avvio automatico non riuscita." # 7) Verifica ----------------------------------------------------------------- say "Avvio e verifica dell'intelligenza…" UP="" for i in $(seq 1 30); do if curl -fsS "http://127.0.0.1:$PORT/health" >/dev/null 2>&1; then UP="1"; break; fi sleep 2 done echo if [ -n "$UP" ]; then ok "Intelligenza ATTIVA su http://127.0.0.1:$PORT" # Scorciatoia 'app': apre il PANNELLO LOCALE (full-local, niente internet) SC="$HOME/Desktop/DEVELOP.command" printf '#!/bin/bash\nopen "http://127.0.0.1:%s/app"\n' "$PORT" > "$SC" && chmod +x "$SC" && ok "Icona 'DEVELOP' creata sul Desktop" echo " → Setup e app: http://127.0.0.1:$PORT/setup-check" echo " oppure doppio click su 'DEVELOP' sul Desktop." open "http://127.0.0.1:$PORT/setup-check" 2>/dev/null || true else err "Non sono riuscito a confermare l'avvio. Riavvia il Mac e riprova, oppure controlla $INSTALL_DIR/brain.err" fi echo "==============================================="