# ============================================================================= # DEVELOP - Installer dell'intelligenza (Brain) su Windows # Uso (PowerShell): irm https://developcontroller.com/install.ps1 | iex # 100% locale: scarica il Brain, il motore AI e i modelli, imposta l'avvio # automatico (Scheduled Task) e verifica. Nessun dato esce dal PC. # ============================================================================= $ErrorActionPreference = "Stop" $PkgUrl = if ($env:DEVELOP_PKG_URL) { $env:DEVELOP_PKG_URL } else { "https://developcontroller.com/develop-brain.tar.gz" } $Dir = if ($env:DEVELOP_HOME) { $env:DEVELOP_HOME } else { "$env:USERPROFILE\DEVELOP_BRAIN" } $Port = if ($env:BRAIN_PORT) { $env:BRAIN_PORT } else { "8088" } function Say($m){ Write-Host "• $m" -ForegroundColor Yellow } function Ok ($m){ Write-Host "✓ $m" -ForegroundColor Green } function Err($m){ Write-Host "✗ $m" -ForegroundColor Red } # Programmi esterni (pip, ollama, tar): eseguiti con ErrorActionPreference=Continue e SENZA 2>$null. # In Windows PowerShell 5.1, con "Stop", ogni riga scritta su stderr da un .exe diventa un errore che # interrompe il comando: ollama pull scrive l'avanzamento su stderr e si fermerebbe subito. function RunExe($exe, [string[]]$argv) { $old = $ErrorActionPreference; $ErrorActionPreference = "Continue" try { & $exe @argv *> $null } catch {} finally { $ErrorActionPreference = $old } return $LASTEXITCODE } function AiLabel($m){ switch ($m) { "qwen3:14b" {"Genius 2.0"} "qwen3:8b" {"Brain 1.0"} "gemma3:4b" {"modulo visione"} "qwen3-vl:4b-instruct" {"modulo trova oggetti"} "nomic-embed-text" {"modulo memoria"} "qwen2.5-coder:7b" {"modulo sviluppo"} default {"componente AI"} } } Write-Host "===============================================" Write-Host " DEVELOP - Installa l'intelligenza (Windows)" Write-Host "===============================================" # 1) Requisiti ----------------------------------------------------------------- # NB: su Windows pulito "python" e' un finto alias dello Store (WindowsApps) che # apre il Microsoft Store invece di eseguire Python: va scartato. function RealPython { $p = (Get-Command python -ErrorAction SilentlyContinue).Source if ($p -and $p -notlike "*\Microsoft\WindowsApps\*") { return $p } $cand = Get-ChildItem "$env:LOCALAPPDATA\Programs\Python\Python3*\python.exe" -ErrorAction SilentlyContinue | Sort-Object FullName -Descending | Select-Object -First 1 if ($cand) { return $cand.FullName } return $null } $py = RealPython if (-not $py) { Say "Installo Python…" try { winget install -e --id Python.Python.3.12 --silent --accept-package-agreements --accept-source-agreements | Out-Null } catch {} # winget non aggiorna il PATH della sessione corrente: ricaricalo e cerca anche nel percorso standard $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") $py = RealPython } if (-not $py) { Err "Python non trovato. Installa Python da python.org e riprova."; return } Ok "Python OK ($py)" # Modello in base alla RAM: 14B solo se >=24GB, altrimenti 8B $ramGB = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory/1GB) $model = if ($ramGB -ge 24) { "qwen3:14b" } else { "qwen3:8b" } $modelLabel = if ($ramGB -ge 24) { "Genius 2.0" } else { "Brain 1.0" } Say "RAM $ramGB GB -> intelligenza $modelLabel" # 2) Pacchetto Brain: dalla CHIAVETTA se presente, altrimenti download -------- Say "Preparo l'intelligenza…" New-Item -ItemType Directory -Force -Path $Dir | Out-Null $tar = "$env:TEMP\develop-brain.tar.gz" $root = if ($PSScriptRoot) { $PSScriptRoot } else { "." } $bundle = $env:DEVELOP_BUNDLE_DIR if (-not $bundle) { if (Test-Path (Join-Path $root "..\motore_ai")) { $bundle = (Resolve-Path (Join-Path $root "..")).Path } elseif (Test-Path (Join-Path $root "app\develop-brain.tar.gz")) { $bundle = $root } } $local = if ($bundle -and (Test-Path (Join-Path $bundle "app\develop-brain.tar.gz"))) { Join-Path $bundle "app\develop-brain.tar.gz" } elseif ($PSScriptRoot) { Join-Path $PSScriptRoot "develop-brain.tar.gz" } else { "develop-brain.tar.gz" } if (Test-Path $local) { Copy-Item $local $tar -Force; Ok "Uso il pacchetto dalla chiavetta" } else { Invoke-WebRequest -Uri $PkgUrl -OutFile $tar -UseBasicParsing } $rc = RunExe "tar" @("-xzf", $tar, "-C", $Dir) # tar è incluso in Windows 10+ if (-not (Test-Path "$Dir\brain_server.py")) { Err "Pacchetto incompleto."; return } Ok "Intelligenza installata in $Dir" # 3) Dipendenze Python --------------------------------------------------------- Say "Preparo le dipendenze…" # (l'esito dei programmi esterni si legge da $LASTEXITCODE, non dal try/catch) $rc = RunExe $py @("-m", "pip", "install", "--user", "--quiet", "pillow") if ($rc -eq 0) { Ok "Dipendenze pronte" } else { Say "Pillow non installato: analisi immagini ridotta." } # 4) Motore AI locale + modelli ------------------------------------------------ $ollama = (Get-Command ollama -ErrorAction SilentlyContinue).Source if (-not $ollama) { Say "Installo il motore AI locale…" $olsetup = if ($bundle) { Get-ChildItem (Join-Path $bundle "motore_ai") -Filter "OllamaSetup*.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 } else { $null } if ($olsetup) { Say "Installo il motore AI dalla chiavetta…"; Start-Process -Wait $olsetup.FullName -ArgumentList "/VERYSILENT" -ErrorAction SilentlyContinue } else { try { winget install -e --id Ollama.Ollama --silent --accept-package-agreements --accept-source-agreements | Out-Null } catch { Say "Installa il motore AI da ollama.com se voce/visione locale non parte." } } $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") $ollama = (Get-Command ollama -ErrorAction SilentlyContinue).Source if (-not $ollama -and (Test-Path "$env:LOCALAPPDATA\Programs\Ollama\ollama.exe")) { $ollama = "$env:LOCALAPPDATA\Programs\Ollama\ollama.exe" } } if ($ollama) { Start-Process -WindowStyle Hidden $ollama -ArgumentList "serve" -ErrorAction SilentlyContinue Start-Sleep -Seconds 3 if ($bundle -and (Test-Path (Join-Path $bundle "modelli_ai\models"))) { Say "Copio i modelli dalla chiavetta (niente download)…" $dst = Join-Path $env:USERPROFILE ".ollama\models" New-Item -ItemType Directory -Force -Path $dst | Out-Null Copy-Item (Join-Path $bundle "modelli_ai\models\*") $dst -Recurse -Force -ErrorAction SilentlyContinue Ok "Modelli copiati dalla chiavetta" } Say "Preparo i modelli (se non gia' presenti, li scarico — solo la prima volta)…" foreach ($m in @($model,"gemma3:4b","nomic-embed-text","qwen2.5-coder:7b","qwen3-vl:4b-instruct")) { Say "Scarico $(AiLabel $m) (può richiedere diversi minuti)…" $rc = RunExe $ollama @("pull", $m) if ($rc -eq 0) { Ok "$(AiLabel $m) pronto" } else { Say "$(AiLabel $m) non scaricato (riprova più tardi)." } } } # 5) Configurazione locale (nessun segreto; voce = SAPI di Windows) ------------ if (-not (Test-Path "$Dir\.env")) { @" BRAIN_PORT=$Port BRAIN_LLM=local LOCAL_MODEL=$model BRAIN_VISION=local VISION_MODEL=gemma3:4b LOCATE_MODEL=qwen3-vl:4b-instruct STT_ENGINE=local OLLAMA_URL=http://127.0.0.1:11434 "@ | Set-Content -Encoding ASCII "$Dir\.env" } Ok "Configurazione locale pronta" # 5b) Voce locale: trascrizione sul PC (whisper.cpp, licenza MIT) -------------- try { $voceDir = "$Dir\models"; $voceModel = "$voceDir\ggml-base.bin" $voceSha = "60ed5bc3dd14eea856493d334349b405782ddcaf0028d4b5df4088345fba2efe" New-Item -ItemType Directory -Force -Path $voceDir | Out-Null $haModello = (Test-Path $voceModel) -and ((Get-FileHash $voceModel -Algorithm SHA256).Hash.ToLower() -eq $voceSha) if (-not $haModello) { Say "Scarico il modello della voce (141 MB, solo la prima volta)…" $tmpM = "$env:TEMP\ggml-base.bin" Invoke-WebRequest "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin" -OutFile $tmpM -UseBasicParsing if ((Get-FileHash $tmpM -Algorithm SHA256).Hash.ToLower() -eq $voceSha) { Move-Item -Force $tmpM $voceModel; Ok "Modello della voce pronto" } else { Remove-Item $tmpM -Force -ErrorAction SilentlyContinue; Say "Modello della voce non verificato: la scrittura a voce resta disattiva." } } $wDir = "$Dir\bin\whisper"; $wExe = "$wDir\whisper-cli.exe" if (-not (Test-Path $wExe)) { Say "Scarico il programma di trascrizione vocale (whisper.cpp, licenza MIT)…" $zip = "$env:TEMP\whisper-bin-x64.zip" Invoke-WebRequest "https://github.com/ggml-org/whisper.cpp/releases/download/b5454/whisper-bin-x64.zip" -OutFile $zip -UseBasicParsing if ((Get-FileHash $zip -Algorithm SHA256).Hash.ToLower() -eq "6ba69e3482d7826214f90a6a9c84ca07782aec1e1d0c6a7c30c994fd5d816ccb") { $tmpX = "$env:TEMP\whisper_x"; if (Test-Path $tmpX) { Remove-Item -Recurse -Force $tmpX } Expand-Archive -Path $zip -DestinationPath $tmpX -Force New-Item -ItemType Directory -Force -Path $wDir | Out-Null Copy-Item "$tmpX\Release\whisper-cli.exe" $wDir -Force Copy-Item "$tmpX\Release\*.dll" $wDir -Force @' MIT License Copyright (c) 2023-2026 The ggml authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Fonte: https://github.com/ggml-org/whisper.cpp (release b5454) '@ | Set-Content -Encoding ASCII "$wDir\LICENSE-whisper.cpp.txt" Remove-Item -Recurse -Force $tmpX -ErrorAction SilentlyContinue } else { Say "Programma di trascrizione non verificato: la scrittura a voce resta disattiva." } Remove-Item $zip -Force -ErrorAction SilentlyContinue } if (Test-Path $wExe) { $codice = RunExe $wExe @("--help") if ($codice -eq 0) { if (-not (Select-String -Path "$Dir\.env" -Pattern "^WHISPER_BIN=" -Quiet)) { Add-Content -Encoding ASCII "$Dir\.env" "WHISPER_BIN=$wExe" } Ok "Trascrizione vocale pronta" } else { Say "Il programma di trascrizione non parte su questo PC: la scrittura a voce resta disattiva." } } } catch { Say "Voce locale non preparata ($($_.Exception.Message)): la scrittura a voce resta disattiva." } # 6) Avvio automatico (Scheduled Task all'accesso) ----------------------------- Say "Imposto l'avvio automatico…" $pyw = $py -replace "python.exe","pythonw.exe"; if (-not (Test-Path $pyw)) { $pyw = $py } # Attività pianificata SOLO per l'utente corrente (non richiede diritti di amministratore); # se non riesce, collegamento nella cartella Esecuzione automatica dell'utente. $auto = $false try { $me = "$env:USERDOMAIN\$env:USERNAME" $action = New-ScheduledTaskAction -Execute $pyw -Argument "`"$Dir\brain_server.py`"" -WorkingDirectory $Dir $trigger = New-ScheduledTaskTrigger -AtLogOn -User $me $princ = New-ScheduledTaskPrincipal -UserId $me -LogonType Interactive -RunLevel Limited Register-ScheduledTask -TaskName "DEVELOP Brain" -Action $action -Trigger $trigger -Principal $princ -Force | Out-Null Start-ScheduledTask -TaskName "DEVELOP Brain" $auto = $true; Ok "Avvio automatico attivo" } catch { try { $startup = [Environment]::GetFolderPath("Startup") $ws = New-Object -ComObject WScript.Shell $sc = $ws.CreateShortcut((Join-Path $startup "DEVELOP Brain.lnk")) $sc.TargetPath = $pyw; $sc.Arguments = "`"$Dir\brain_server.py`""; $sc.WorkingDirectory = $Dir; $sc.WindowStyle = 7 $sc.Save(); $auto = $true; Ok "Avvio automatico attivo (cartella Esecuzione automatica)" } catch { Say "Avvio automatico non impostato." } } Start-Sleep -Seconds 2 # l'attività pianificata impiega un attimo a risultare "Running": evita una seconda istanza if (-not (Get-ScheduledTask -TaskName "DEVELOP Brain" -ErrorAction SilentlyContinue | Where-Object State -eq "Running")) { Start-Process -WindowStyle Hidden $pyw -ArgumentList "`"$Dir\brain_server.py`"" -WorkingDirectory $Dir } # 7) Verifica ------------------------------------------------------------------ Say "Avvio e verifica…" $up = $false for ($i=0; $i -lt 30; $i++) { try { Invoke-WebRequest -Uri "http://127.0.0.1:$Port/health" -UseBasicParsing -TimeoutSec 3 | Out-Null; $up=$true; break } catch { Start-Sleep -Seconds 2 } } Write-Host "" if ($up) { Ok "Intelligenza ATTIVA su http://127.0.0.1:$Port" $url = "http://127.0.0.1:$Port/setup-check" try { $desk = [Environment]::GetFolderPath("Desktop") # cartella Desktop reale (anche se spostata in OneDrive) $lnk = Join-Path $desk "DEVELOP.url" "[InternetShortcut]`r`nURL=$url" | Set-Content -Encoding ASCII $lnk Ok "Icona 'DEVELOP' creata sul Desktop" } catch { Say "Icona sul Desktop non creata: apri $url" } Write-Host " -> Pannello (TUTTO in locale, privacy): $url" Start-Process $url } else { Err "Avvio non confermato. Riavvia il PC e riprova, oppure controlla $Dir." } Write-Host "==============================================="