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 ────────────────────────────────