


















@@ -104,6 +104,144 @@ function Check-Node {
104104return $false
105105}
106106107+function Get-WindowsNodeArchitecture {
108+foreach ($architecture in @($env:PROCESSOR_ARCHITEW6432, $env:PROCESSOR_ARCHITECTURE)) {
109+if ($architecture -match "ARM64") {
110+return "arm64"
111+ }
112+ }
113+return "x64"
114+}
115+116+function Get-OpenClawDepsRoot {
117+$localAppData = $env:LOCALAPPDATA
118+if ([string]::IsNullOrWhiteSpace($localAppData)) {
119+$localAppData = [Environment]::GetFolderPath("LocalApplicationData")
120+ }
121+if ([string]::IsNullOrWhiteSpace($localAppData)) {
122+$localAppData = Join-Path ([Environment]::GetFolderPath("UserProfile")) "AppData\Local"
123+ }
124+return (Join-Path $localAppData "OpenClaw\deps")
125+}
126+127+function Get-PortableNodeRoot {
128+return (Join-Path (Get-OpenClawDepsRoot) "portable-node")
129+}
130+131+function Get-PortableNodeCommandPath {
132+$root = Get-PortableNodeRoot
133+$candidate = Join-Path $root "node.exe"
134+if (Test-Path $candidate) {
135+return $candidate
136+ }
137+return $null
138+}
139+140+function Use-PortableNodeIfPresent {
141+$nodeExe = Get-PortableNodeCommandPath
142+if (-not $nodeExe) {
143+return $false
144+ }
145+146+Add-ToProcessPath (Split-Path -Parent $nodeExe)
147+return (Check-Node)
148+}
149+150+function Ensure-PortableNodeOnUserPath {
151+$nodeExe = Get-PortableNodeCommandPath
152+if (-not $nodeExe) {
153+return
154+ }
155+156+$nodeDir = Split-Path -Parent $nodeExe
157+if (Add-ToUserPath $nodeDir) {
158+Write-Host "[!] Added $nodeDir to user PATH (restart terminal if node or openclaw is not found)" -ForegroundColor Yellow
159+ }
160+}
161+162+function Resolve-PortableNodeDownload {
163+$architecture = Get-WindowsNodeArchitecture
164+$index = Invoke-RestMethod -Uri "https://nodejs.org/dist/index.json"
165+$release = $index |
166+Where-Object { $_.version -match '^v24\.' } |
167+Select-Object -First 1
168+169+if (-not $release -or -not $release.version) {
170+throw "Could not resolve latest Node.js 24 release metadata."
171+ }
172+173+$fileKey = "win-$architecture-zip"
174+if ($release.files -and -not ($release.files -contains $fileKey)) {
175+throw "Node.js $($release.version) does not publish $fileKey."
176+ }
177+178+$name = "node-$($release.version)-win-$architecture.zip"
179+return @{
180+Version = $release.version
181+Name = $name
182+Url = "https://nodejs.org/dist/$($release.version)/$name"
183+ }
184+}
185+186+function Install-PortableNode {
187+if (Use-PortableNodeIfPresent) {
188+ Ensure-PortableNodeOnUserPath
189+$nodeVersion = (& node -v 2>$null)
190+if ($nodeVersion) {
191+Write-Host "[OK] User-local Node.js already available: $nodeVersion" -ForegroundColor Green
192+ }
193+return
194+ }
195+196+Write-Host " No package manager found; bootstrapping user-local portable Node.js..." -ForegroundColor Gray
197+198+$download = Resolve-PortableNodeDownload
199+$portableRoot = Get-PortableNodeRoot
200+$portableParent = Split-Path -Parent $portableRoot
201+$tmpZip = Join-Path $env:TEMP $download.Name
202+$tmpExtract = Join-Path $env:TEMP ("openclaw-portable-node-" + [guid]::NewGuid().ToString("N"))
203+204+New-Item -ItemType Directory -Force -Path $portableParent | Out-Null
205+if (Test-Path $portableRoot) {
206+Remove-Item -Recurse -Force $portableRoot
207+ }
208+if (Test-Path $tmpExtract) {
209+Remove-Item -Recurse -Force $tmpExtract
210+ }
211+New-Item -ItemType Directory -Force -Path $tmpExtract | Out-Null
212+213+try {
214+Write-Host " Downloading Node.js $($download.Version)..." -ForegroundColor Gray
215+Invoke-WebRequest -UseBasicParsing -Uri $download.Url -OutFile $tmpZip
216+Expand-Archive -Path $tmpZip -DestinationPath $tmpExtract -Force
217+218+$nodeDir = Get-ChildItem -Path $tmpExtract -Directory |
219+Where-Object { Test-Path (Join-Path $_.FullName "node.exe") } |
220+Select-Object -First 1
221+if (-not $nodeDir) {
222+throw "Node.js archive did not contain node.exe."
223+ }
224+225+New-Item -ItemType Directory -Force -Path $portableRoot | Out-Null
226+Move-Item -Path (Join-Path $nodeDir.FullName "*") -Destination $portableRoot -Force
227+ } finally {
228+if (Test-Path $tmpZip) {
229+Remove-Item -Force $tmpZip
230+ }
231+if (Test-Path $tmpExtract) {
232+Remove-Item -Recurse -Force $tmpExtract
233+ }
234+ }
235+236+if (-not (Use-PortableNodeIfPresent)) {
237+throw "Portable Node.js bootstrap completed, but node is still unavailable."
238+ }
239+ Ensure-PortableNodeOnUserPath
240+241+$nodeVersion = (& node -v 2>$null)
242+Write-Host "[OK] User-local Node.js ready: $nodeVersion" -ForegroundColor Green
243+}
244+107245# Install Node.js
108246function Install-Node {
109247Write-Host "[*] Installing Node.js..." -ForegroundColor Yellow
@@ -143,9 +281,18 @@ function Install-Node {
143281return $true
144282 }
145283284+try {
285+Install-PortableNode
286+if (Check-Node) {
287+return $true
288+ }
289+ } catch {
290+Write-Host "[!] Portable Node.js bootstrap failed: $($_.Exception.Message)" -ForegroundColor Yellow
291+ }
292+146293# Manual download fallback
147294Write-Host ""
148-Write-Host "Error: Could not find a package manager (winget, choco, or scoop)" -ForegroundColor Red
295+Write-Host "Error: Could not install Node.js automatically." -ForegroundColor Red
149296Write-Host ""
150297Write-Host "Please install Node.js 22+ manually:" -ForegroundColor Yellow
151298Write-Host " https://nodejs.org/en/download/" -ForegroundColor Cyan
@@ -190,6 +337,33 @@ function Add-ToProcessPath {
190337$env:Path = "$PathEntry;$env:Path"
191338}
192339340+function Add-ToUserPath {
341+param(
342+ [Parameter(Mandatory = $true)]
343+ [string]$PathEntry
344+ )
345+346+if ([string]::IsNullOrWhiteSpace($PathEntry)) {
347+return $false
348+ }
349+350+Add-ToProcessPath $PathEntry
351+352+$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
353+$userEntries = @($userPath -split ";" | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
354+if ($userEntries | Where-Object { $_ -ieq $PathEntry }) {
355+return $false
356+ }
357+358+$newUserPath = if ([string]::IsNullOrWhiteSpace($userPath)) {
359+$PathEntry
360+ } else {
361+"$userPath;$PathEntry"
362+ }
363+ [Environment]::SetEnvironmentVariable("Path", $newUserPath, "User")
364+return $true
365+}
366+193367function Get-PortableGitRoot {
194368$base = Join-Path $env:LOCALAPPDATA "OpenClaw\deps"
195369return (Join-Path $base "portable-git")
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。