diff --git a/public_html/agent/install-windows.ps1 b/public_html/agent/install-windows.ps1 index 34fa0ed..0c67487 100644 --- a/public_html/agent/install-windows.ps1 +++ b/public_html/agent/install-windows.ps1 @@ -44,7 +44,7 @@ Write-Host "[1/6] Checking for Python 3..." -ForegroundColor Cyan $pythonPath = $null -# Search for a system-wide Python first (accessible by LocalSystem) +# System-wide paths — accessible by LocalSystem service account $systemPaths = @( "C:\Program Files\Python313\python.exe", "C:\Program Files\Python312\python.exe", @@ -56,6 +56,49 @@ $systemPaths = @( "C:\Python311\python.exe", "C:\Python310\python.exe" ) + +function Install-PythonSystemWide { + # Try winget first (Win 10 1709+ / Win 11) + $wingetOk = $false + try { + $null = Get-Command winget -ErrorAction Stop + Write-Host " Using winget (system-wide)..." -NoNewline + winget install Python.Python.3.12 --silent --scope machine ` + --accept-package-agreements --accept-source-agreements 2>&1 | Out-Null + if ($LASTEXITCODE -eq 0) { $wingetOk = $true; Write-Host " done." -ForegroundColor Green } + } catch {} + + if (-not $wingetOk) { + # Direct download — works on Win 8.1 without winget + # Python 3.11 explicitly supports Win 8.1+ + Write-Host " Downloading Python 3.11 (Win 8.1+ compatible)..." -NoNewline + $pyInstaller = "$env:TEMP\python-installer.exe" + $pyUrl = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-amd64.exe" + try { + [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 + $wc = New-Object System.Net.WebClient + $wc.DownloadFile($pyUrl, $pyInstaller) + Write-Host " downloaded." -ForegroundColor Green + } catch { + Write-Error "Could not download Python. Install from https://python.org choosing 'Install for all users', then re-run." + } + Write-Host " Installing system-wide (silent)..." -NoNewline + $proc = Start-Process -FilePath $pyInstaller ` + -ArgumentList "/quiet InstallAllUsers=1 PrependPath=1 Include_test=0" ` + -Wait -PassThru + if ($proc.ExitCode -ne 0) { + Write-Error "Python installer exited $($proc.ExitCode). Install manually from https://python.org then re-run." + } + Write-Host " done." -ForegroundColor Green + Remove-Item $pyInstaller -ErrorAction SilentlyContinue + } + + # Refresh PATH after install + $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH","Machine") + ";" + + [System.Environment]::GetEnvironmentVariable("PATH","User") +} + +# ── Search for system-wide Python first ─────────────────────────────────────── foreach ($p in $systemPaths) { if (Test-Path $p) { try { @@ -65,7 +108,7 @@ foreach ($p in $systemPaths) { } } -# Fall back to PATH +# ── Fall back to PATH — but flag if it's per-user ───────────────────────────── if (-not $pythonPath) { foreach ($cmd in @("python", "python3", "py")) { try { @@ -78,64 +121,31 @@ if (-not $pythonPath) { } } -if (-not $pythonPath) { +# ── If Python is per-user (AppData), install system-wide so LocalSystem can use it ── +$needsSystemPython = $false +if ($pythonPath -and ($pythonPath -match "AppData")) { + Write-Host " Found per-user Python: $pythonPath" -ForegroundColor Yellow + Write-Host " LocalSystem service needs system-wide Python. Installing..." -ForegroundColor Yellow + $needsSystemPython = $true +} elseif (-not $pythonPath) { Write-Host " Python 3 not found. Installing system-wide..." -ForegroundColor Yellow + $needsSystemPython = $true +} - # Try winget first (Win 10 1709+ / Win 11) - $wingetOk = $false - try { - $null = Get-Command winget -ErrorAction Stop - Write-Host " Using winget..." -NoNewline - winget install Python.Python.3.12 --silent --scope machine ` - --accept-package-agreements --accept-source-agreements 2>&1 | Out-Null - $wingetOk = $true - Write-Host " done." -ForegroundColor Green - } catch {} - - if (-not $wingetOk) { - # Direct download — works on Win 8.1 without winget - # Python 3.11 is explicitly documented to support Win 8.1+ - Write-Host " Downloading Python 3.11 installer (Win 8.1+ compatible)..." -NoNewline - $pyInstaller = "$env:TEMP\python-installer.exe" - $pyUrl = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-amd64.exe" - try { - [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 - $wc = New-Object System.Net.WebClient - $wc.DownloadFile($pyUrl, $pyInstaller) - Write-Host " downloaded." -ForegroundColor Green - } catch { - Write-Error "Could not download Python installer. Install Python 3.11+ from https://python.org (choose 'Install for all users') then re-run." - } - Write-Host " Running Python installer (system-wide, silent)..." -NoNewline - $proc = Start-Process -FilePath $pyInstaller ` - -ArgumentList "/quiet InstallAllUsers=1 PrependPath=1 Include_test=0" ` - -Wait -PassThru - if ($proc.ExitCode -ne 0) { - Write-Error "Python installer exited with code $($proc.ExitCode). Install manually from https://python.org then re-run." - } - Write-Host " done." -ForegroundColor Green - Remove-Item $pyInstaller -ErrorAction SilentlyContinue - } - - # Refresh PATH and locate installed Python - $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH","Machine") + ";" + - [System.Environment]::GetEnvironmentVariable("PATH","User") +if ($needsSystemPython) { + Install-PythonSystemWide + # Locate the newly installed system-wide Python + $pythonPath = $null foreach ($p in $systemPaths) { - if (Test-Path $p) { $pythonPath = $p; break } - } - if (-not $pythonPath) { - foreach ($cmd in @("python", "python3")) { + if (Test-Path $p) { try { - $ver = & $cmd --version 2>&1 - if ("$ver" -match "Python 3") { - $resolved = (Get-Command $cmd -ErrorAction SilentlyContinue) - if ($resolved) { $pythonPath = $resolved.Source; break } - } + $ver = & $p --version 2>&1 + if ("$ver" -match "Python 3") { $pythonPath = $p; break } } catch {} } } if (-not $pythonPath) { - Write-Error "Python 3 still not found after installation. Open a new Admin PowerShell and re-run this installer." + Write-Error "System-wide Python not found after install. Open a new Admin PowerShell and re-run." } } @@ -143,13 +153,19 @@ Write-Host " Python: $pythonPath" -ForegroundColor Green # ── Install pywin32 (required for Windows service support) ──────────────────── Write-Host "[2/6] Installing pywin32..." -ForegroundColor Cyan + +# pip install +$pipResult = & $pythonPath -m pip install --upgrade pywin32 2>&1 +if ($LASTEXITCODE -ne 0) { + Write-Error "pip install pywin32 failed (exit $LASTEXITCODE).`n$pipResult`nTry manually: $pythonPath -m pip install pywin32" +} + +# postinstall registers service runner DLLs — non-fatal if it fails try { - & $pythonPath -m pip install --quiet --upgrade pywin32 - # Run postinstall to register service runner DLLs system-wide - & $pythonPath -c "import pywin32_postinstall; pywin32_postinstall.install()" 2>$null + $postResult = & $pythonPath -c "import pywin32_postinstall; pywin32_postinstall.install()" 2>&1 Write-Host " pywin32 installed." -ForegroundColor Green } catch { - Write-Error "pip install pywin32 failed: $_`nEnsure pip is available: $pythonPath -m ensurepip" + Write-Host " pywin32 installed (postinstall skipped — service should still work)." -ForegroundColor Yellow } # ── Create install directory and download agent ────────────────────────────────