# Daemora installer for Windows — one-shot setup for non-technical users. # # What this does, in order: # 1. Detects Node.js; installs 22 LTS via winget if missing or too old. # 2. Installs the `daemora` npm package globally so the `daemora` # command is on PATH. # 3. Drops a small launcher .bat at %USERPROFILE%\.daemora\bin\ that # starts the agent and waits for the HTTP port before opening the # browser. # 4. Creates Desktop + Start Menu shortcuts pointing at the launcher. # 5. Launches Daemora so the dashboard opens immediately on first # install. # # First run: the launcher hits `daemora start`, which runs the existing # setup wizard if config isn't done. Subsequent runs: the launcher just # opens the browser if the agent is already running, or restarts it. # # Usage (PowerShell, run as user — winget will UAC-prompt if Node needs # installing): # irm https://raw.githubusercontent.com/CodeAndCanvasLabs/Daemora/main/scripts/install.ps1 | iex # Env: DAEMORA_PORT (default 8081) #Requires -Version 5 $ErrorActionPreference = 'Stop' $port = if ($env:DAEMORA_PORT) { $env:DAEMORA_PORT } else { '8081' } $daemoraHome = Join-Path $env:USERPROFILE '.daemora' $logDir = Join-Path $daemoraHome 'logs' $binDir = Join-Path $daemoraHome 'bin' $prefix = '[daemora-install]' function Write-Log($m) { Write-Host "$prefix $m" } New-Item -ItemType Directory -Force -Path $daemoraHome,$logDir,$binDir | Out-Null # ── 1. Node.js ────────────────────────────────────────────────────────── function Test-Node22 { try { $v = (& node -v) 2>$null if (-not $v) { return $false } $major = ($v -replace 'v','' -split '\.')[0] return ([int]$major -ge 22) } catch { return $false } } if (-not (Test-Node22)) { Write-Log 'Node.js 22+ not found — installing via winget.' if (-not (Get-Command winget -ErrorAction SilentlyContinue)) { Write-Error 'winget is not available on this machine. Install Node.js 22 LTS from https://nodejs.org and rerun this script.' exit 1 } winget install --id OpenJS.NodeJS.LTS -h --accept-source-agreements --accept-package-agreements # winget updates the registry PATH but the current PowerShell session # is stale; merge machine + user PATH so `npm` and `node` resolve below. $env:PATH = [System.Environment]::GetEnvironmentVariable('PATH', 'Machine') + ';' + [System.Environment]::GetEnvironmentVariable('PATH', 'User') } if (-not (Get-Command npm -ErrorAction SilentlyContinue)) { Write-Error 'npm is not on PATH after installing Node. Open a new PowerShell window and rerun the installer.' exit 1 } # ── 2. daemora ───────────────────────────────────────────────────────── Write-Log 'installing Daemora via npm (this may take a minute)…' & npm install -g daemora if ($LASTEXITCODE -ne 0) { Write-Error "npm install -g daemora failed (exit $LASTEXITCODE)."; exit 1 } if (-not (Get-Command daemora -ErrorAction SilentlyContinue)) { Write-Error "Daemora installed but the 'daemora' command isn't on PATH. Open a new PowerShell window." exit 1 } # ── 3. Launcher .bat ─────────────────────────────────────────────────── $launcher = Join-Path $binDir 'daemora-launcher.bat' $launcherBody = @" @echo off REM Daemora launcher — starts the agent (if not already running) and REM opens the dashboard in the default browser. Generated by install.ps1. setlocal set URL=http://localhost:$port set LOG=%USERPROFILE%\.daemora\logs\daemora.log REM If the dashboard is already responding, just open the browser. curl -sf %URL% >nul 2>&1 if %ERRORLEVEL%==0 ( start "" "%URL%" exit /b 0 ) REM Start daemora in the background, redirect output to the log file. start "" /B cmd /c "daemora start >> %LOG% 2>&1" REM Wait up to 30s for the HTTP port to come up. set /a tries=0 :wait curl -sf %URL% >nul 2>&1 if %ERRORLEVEL%==0 goto open set /a tries+=1 if %tries% lss 30 ( timeout /t 1 /nobreak >nul goto wait ) :open start "" "%URL%" "@ Set-Content -Path $launcher -Value $launcherBody -Encoding ASCII # ── 4. Desktop + Start Menu shortcuts ────────────────────────────────── $ws = New-Object -ComObject WScript.Shell $desktop = [Environment]::GetFolderPath('Desktop') $startMenu = [Environment]::GetFolderPath('StartMenu') foreach ($dir in @($desktop, $startMenu)) { if (-not $dir) { continue } $lnk = Join-Path $dir 'Daemora.lnk' $shortcut = $ws.CreateShortcut($lnk) $shortcut.TargetPath = $launcher $shortcut.WorkingDirectory = $env:USERPROFILE $shortcut.Description = 'Daemora — self-hosted AI agent' # Hide the launcher's console window when launched from the shortcut. $shortcut.WindowStyle = 7 $shortcut.Save() } # ── 5. Launch ────────────────────────────────────────────────────────── Write-Log 'all set. starting Daemora…' Start-Process -FilePath $launcher -WindowStyle Hidden Start-Sleep -Seconds 1 Write-Log '' Write-Log "Daemora is running at http://localhost:$port" Write-Log 'Open it again any time from your Desktop or Start Menu.'