diff --git a/scripts/install.ps1 b/scripts/install.ps1 index fea73649ca5..7645af00da5 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -185,6 +185,35 @@ function Write-Err { Write-Host "[X] $Message" -ForegroundColor Red } +# Inspect npm output for a TLS-trust failure and, if found, print actionable +# remediation. npm/Node surface corporate MITM proxies and missing root CAs as +# "unable to get local issuer certificate" / "self-signed certificate in +# certificate chain" / UNABLE_TO_GET_ISSUER_CERT_LOCALLY -- most commonly while +# Electron's install.js postinstall downloads the Electron binary. The reporter +# usually misreads this as an admin-rights or generic install failure (see +# issue #38016), so detect it once here and route every npm stage through this +# hint. Returns $true when a cert error was detected (caller may adjust its own +# messaging), $false otherwise. +function Show-NpmCertHint { + param([string]$NpmOutput) + if (-not $NpmOutput) { return $false } + $isCertError = $NpmOutput -match "unable to get local issuer certificate" ` + -or $NpmOutput -match "self.signed certificate" ` + -or $NpmOutput -match "UNABLE_TO_GET_ISSUER_CERT_LOCALLY" ` + -or $NpmOutput -match "SELF_SIGNED_CERT_IN_CHAIN" ` + -or $NpmOutput -match "CERT_HAS_EXPIRED" + if (-not $isCertError) { return $false } + Write-Warn "This looks like a TLS certificate-trust failure, not a permissions problem." + Write-Info " A corporate proxy or antivirus is likely intercepting HTTPS and presenting a" + Write-Info " certificate Node.js doesn't trust. To fix, point Node at your org's root CA:" + Write-Info " 1. Get the corporate root CA as a .pem/.crt from your IT team." + Write-Info " 2. setx NODE_EXTRA_CA_CERTS `"C:\path\to\corp-ca.pem`"" + Write-Info " 3. Open a NEW terminal (so the env var takes effect) and re-run the installer." + Write-Info " Quick (less secure) alternative -- disable TLS verification just for the install:" + Write-Info " npm config set strict-ssl false (re-enable afterwards: npm config set strict-ssl true)" + return $true +} + # --- Ensure-mode helpers --- function Resolve-NpmCmd { @@ -252,6 +281,7 @@ function Install-AgentBrowser { $npmDetail = Get-Content $npmLog -Raw -ErrorAction SilentlyContinue Remove-Item $npmLog -Force -ErrorAction SilentlyContinue Write-Err "npm install -g failed (exit $npmExit): $npmDetail" + Show-NpmCertHint $npmDetail | Out-Null throw "npm install failed" } Remove-Item $npmLog -Force -ErrorAction SilentlyContinue @@ -1873,6 +1903,7 @@ function Install-NodeDeps { Write-Host " $line" -ForegroundColor DarkGray } Write-Info " Full log: $logPath" + Show-NpmCertHint $errText | Out-Null } } Write-Info "Run manually later: cd `"$installDir`"; npm install" @@ -2127,15 +2158,22 @@ function Install-Desktop { # tsc/typescript unresolved so `npm run pack`'s `tsc -b` dies with # no obvious cause. Fall back to `npm install` only if `npm ci` # fails (lockfile out of sync / very old npm without ci). - & $npmExe ci 2>&1 | ForEach-Object { "$_" } + # + # Tee the merged output into $npmOut while still emitting every line + # live. We don't need a side log file (the bootstrap streaming sink + # is the artifact), but on failure we scan $npmOut for the TLS-trust + # signature so corporate-proxy users get the NODE_EXTRA_CA_CERTS hint + # instead of an opaque "exit 1" (issue #38016). + & $npmExe ci 2>&1 | ForEach-Object { "$_" } | Tee-Object -Variable npmOut $code = $LASTEXITCODE if ($code -ne 0) { Write-Info " npm ci failed (exit $code) -- retrying with npm install..." - & $npmExe install 2>&1 | ForEach-Object { "$_" } + & $npmExe install 2>&1 | ForEach-Object { "$_" } | Tee-Object -Variable npmOut $code = $LASTEXITCODE } $ErrorActionPreference = $prevEAP if ($code -ne 0) { + Show-NpmCertHint ($npmOut -join "`n") | Out-Null throw "desktop workspace npm install failed (exit $code) -- see lines above for cause" } Write-Success "Desktop workspace dependencies installed"