browser4-cli 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -1
- package/bin/browser4-cli-darwin-arm64 +0 -0
- package/bin/browser4-cli-darwin-x64 +0 -0
- package/bin/browser4-cli-linux-arm64 +0 -0
- package/bin/browser4-cli-linux-musl-arm64 +0 -0
- package/bin/browser4-cli-linux-musl-x64 +0 -0
- package/bin/browser4-cli-linux-x64 +0 -0
- package/bin/browser4-cli-win32-x64.exe +0 -0
- package/package.json +1 -1
- package/scripts/install-browser4-cli.ps1 +379 -0
- package/scripts/install-browser4-cli.sh +433 -0
- package/scripts/sync-version.js +29 -1
package/README.md
CHANGED
|
@@ -37,6 +37,28 @@ brew install browser4-cli
|
|
|
37
37
|
cargo install browser4-cli
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
### Standalone Installer Scripts (no npm / Rust / Homebrew needed)
|
|
41
|
+
|
|
42
|
+
Bootstrap the native binary directly with a single command:
|
|
43
|
+
|
|
44
|
+
**Windows (PowerShell):**
|
|
45
|
+
```powershell
|
|
46
|
+
Invoke-WebRequest -Uri "https://browser4.oss-cn-beijing.aliyuncs.com/scripts/install-browser4-cli.ps1" -OutFile "$env:TEMP\install-browser4-cli.ps1"
|
|
47
|
+
powershell -ExecutionPolicy Bypass -File "$env:TEMP\install-browser4-cli.ps1"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Linux / macOS (bash):**
|
|
51
|
+
```bash
|
|
52
|
+
curl -fsSL https://browser4.oss-cn-beijing.aliyuncs.com/scripts/install-browser4-cli.sh | bash
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or run the scripts locally from a cloned repo:
|
|
56
|
+
- `cli/scripts/install-browser4-cli.ps1` (Windows)
|
|
57
|
+
- `cli/scripts/install-browser4-cli.sh` (Linux / macOS / Git Bash)
|
|
58
|
+
|
|
59
|
+
See [Standalone CLI Installer Scripts](../docs/cli-standalone-install.md) for
|
|
60
|
+
the full option reference, supported platforms, and examples.
|
|
61
|
+
|
|
40
62
|
### From Source
|
|
41
63
|
|
|
42
64
|
```bash
|
|
@@ -242,7 +264,7 @@ Like other advanced commands, they are intentionally omitted from the global
|
|
|
242
264
|
Use the spaced `agent <subcommand>` form:
|
|
243
265
|
|
|
244
266
|
```shell
|
|
245
|
-
browser4-cli agent run "Open
|
|
267
|
+
browser4-cli agent run "Open browser4.io and summarize the hero section"
|
|
246
268
|
browser4-cli agent status agent-task-1
|
|
247
269
|
browser4-cli agent result agent-task-1
|
|
248
270
|
```
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Install browser4-cli — download the native binary and set it up on your PATH.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Detects your OS and CPU architecture, downloads the matching native binary from
|
|
7
|
+
GitHub Releases or Alibaba Cloud OSS, installs it to a user-local directory, and
|
|
8
|
+
optionally adds it to your PATH.
|
|
9
|
+
|
|
10
|
+
Default install location:
|
|
11
|
+
Windows: $env:LOCALAPPDATA\Programs\browser4-cli
|
|
12
|
+
(override with -InstallDir)
|
|
13
|
+
|
|
14
|
+
Download sources (tried in order unless -Source is specified):
|
|
15
|
+
1. GitHub Releases — https://github.com/platonai/Browser4
|
|
16
|
+
2. Aliyun OSS — https://browser4.oss-cn-beijing.aliyuncs.com
|
|
17
|
+
|
|
18
|
+
.PARAMETER Version
|
|
19
|
+
Release version tag to download (e.g. "v4.11.0" or "v0.1.12-cli").
|
|
20
|
+
Defaults to "latest" which resolves to the most recent stable release.
|
|
21
|
+
|
|
22
|
+
.PARAMETER InstallDir
|
|
23
|
+
Directory to install the binary into.
|
|
24
|
+
Default: $env:LOCALAPPDATA\Programs\browser4-cli
|
|
25
|
+
|
|
26
|
+
.PARAMETER Source
|
|
27
|
+
Force a specific download source: "github" or "oss".
|
|
28
|
+
Default: try GitHub first, fall back to OSS.
|
|
29
|
+
|
|
30
|
+
.PARAMETER AddToPath
|
|
31
|
+
Add the install directory to the current user's PATH environment variable.
|
|
32
|
+
Default: true.
|
|
33
|
+
|
|
34
|
+
.PARAMETER Silent
|
|
35
|
+
Suppress all non-error output.
|
|
36
|
+
|
|
37
|
+
.PARAMETER DryRun
|
|
38
|
+
Print what would be done without actually doing it.
|
|
39
|
+
|
|
40
|
+
.EXAMPLE
|
|
41
|
+
# Quick install — default location, latest version, add to PATH
|
|
42
|
+
powershell -ExecutionPolicy Bypass -File install-browser4-cli.ps1
|
|
43
|
+
|
|
44
|
+
.EXAMPLE
|
|
45
|
+
# Silent install with a specific version
|
|
46
|
+
powershell -ExecutionPolicy Bypass -File install-browser4-cli.ps1 -Version "v4.11.0" -Silent
|
|
47
|
+
|
|
48
|
+
.EXAMPLE
|
|
49
|
+
# Install from OSS only, custom directory
|
|
50
|
+
powershell -ExecutionPolicy Bypass -File install-browser4-cli.ps1 -Source oss -InstallDir "C:\tools\browser4"
|
|
51
|
+
#>
|
|
52
|
+
|
|
53
|
+
[CmdletBinding()]
|
|
54
|
+
param(
|
|
55
|
+
[string]$Version = "",
|
|
56
|
+
[string]$InstallDir = "",
|
|
57
|
+
[ValidateSet("github", "oss")]
|
|
58
|
+
[string]$Source = "",
|
|
59
|
+
[bool]$AddToPath = $true,
|
|
60
|
+
[switch]$Silent,
|
|
61
|
+
[switch]$DryRun
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
$ErrorActionPreference = "Stop"
|
|
65
|
+
|
|
66
|
+
# ──────────────────────────────────────────────
|
|
67
|
+
# OS detection (compatible with PS 5.1+)
|
|
68
|
+
# ──────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
$script:IsWin = [System.Environment]::OSVersion.Platform -eq "Win32NT"
|
|
71
|
+
$script:IsMac = [System.Environment]::OSVersion.Platform -eq "Unix" -and (uname -s 2>$null) -eq "Darwin"
|
|
72
|
+
$script:IsLinux = [System.Environment]::OSVersion.Platform -eq "Unix" -and (uname -s 2>$null) -ne "Darwin"
|
|
73
|
+
|
|
74
|
+
# ──────────────────────────────────────────────
|
|
75
|
+
# Helpers
|
|
76
|
+
# ──────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
function Write-Summary {
|
|
79
|
+
param([string]$Message, [string]$Color = "White")
|
|
80
|
+
if (-not $Silent) { Write-Host $Message -ForegroundColor $Color }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function Write-Step {
|
|
84
|
+
param([string]$Message)
|
|
85
|
+
if (-not $Silent) { Write-Host " » $Message" -ForegroundColor Gray }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function Write-Check {
|
|
89
|
+
param([string]$Message)
|
|
90
|
+
if (-not $Silent) { Write-Host " ✓ $Message" -ForegroundColor Green }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function Write-WarnMsg {
|
|
94
|
+
param([string]$Message)
|
|
95
|
+
if (-not $Silent) { Write-Host " ⚠ $Message" -ForegroundColor Yellow }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# ──────────────────────────────────────────────
|
|
99
|
+
# Detection
|
|
100
|
+
# ──────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
function Get-PlatformKey {
|
|
103
|
+
$arch = if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq [System.Runtime.InteropServices.Architecture]::Arm64) { "arm64" } else { "x64" }
|
|
104
|
+
|
|
105
|
+
if ($script:IsWin) {
|
|
106
|
+
return "win32-$arch"
|
|
107
|
+
}
|
|
108
|
+
elseif ($script:IsMac) {
|
|
109
|
+
return "darwin-$arch"
|
|
110
|
+
}
|
|
111
|
+
elseif ($script:IsLinux) {
|
|
112
|
+
# Detect musl
|
|
113
|
+
$isMusl = $false
|
|
114
|
+
try {
|
|
115
|
+
$lddOutput = ldd --version 2>&1
|
|
116
|
+
if ($lddOutput -match "musl") { $isMusl = $true }
|
|
117
|
+
} catch {
|
|
118
|
+
if ((Test-Path "/lib/ld-musl-x86_64.so.1") -or (Test-Path "/lib/ld-musl-aarch64.so.1")) {
|
|
119
|
+
$isMusl = $true
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
$libc = if ($isMusl) { "musl" } else { "" }
|
|
123
|
+
if ($libc) { return "linux-$libc-$arch" } else { return "linux-$arch" }
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
throw "Unsupported OS. Browser4 CLI supports Windows, macOS, and Linux."
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function Get-BinaryName {
|
|
131
|
+
param([string]$PlatformKey)
|
|
132
|
+
return "browser4-cli-$PlatformKey" + $(if ($PlatformKey.StartsWith("win32")) { ".exe" } else { "" })
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function Get-DefaultInstallDir {
|
|
136
|
+
if ($script:IsWin) {
|
|
137
|
+
return Join-Path $env:LOCALAPPDATA "Programs\browser4-cli"
|
|
138
|
+
}
|
|
139
|
+
elseif ($script:IsLinux -or $script:IsMac) {
|
|
140
|
+
# Prefer ~/.local/bin for user installs
|
|
141
|
+
if (Test-Path "$env:HOME/.local/bin") {
|
|
142
|
+
return "$env:HOME/.local/bin"
|
|
143
|
+
}
|
|
144
|
+
return "$env:HOME/.local/bin"
|
|
145
|
+
}
|
|
146
|
+
throw "Unsupported OS"
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# ──────────────────────────────────────────────
|
|
150
|
+
# Download URLs
|
|
151
|
+
# ──────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
$GITHUB_REPO = "platonai/Browser4"
|
|
154
|
+
$OSS_BASE = "https://browser4.oss-cn-beijing.aliyuncs.com"
|
|
155
|
+
|
|
156
|
+
function Get-DownloadUrls {
|
|
157
|
+
param([string]$BinaryName, [string]$VersionTag)
|
|
158
|
+
|
|
159
|
+
$urls = @()
|
|
160
|
+
|
|
161
|
+
$ghBase = "https://github.com/$GITHUB_REPO/releases/download"
|
|
162
|
+
if ($VersionTag) {
|
|
163
|
+
$ghUrl = "$ghBase/$VersionTag/$BinaryName"
|
|
164
|
+
$ossUrl = "$OSS_BASE/releases/download/$VersionTag/$BinaryName"
|
|
165
|
+
} else {
|
|
166
|
+
# Use 'latest' redirect for GitHub, 'latest' symlink for OSS
|
|
167
|
+
$ghUrl = "https://github.com/$GITHUB_REPO/releases/latest/download/$BinaryName"
|
|
168
|
+
$ossUrl = "$OSS_BASE/releases/download/latest/$BinaryName"
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if ($Source -eq "github") {
|
|
172
|
+
$urls += @{ Url = $ghUrl; Label = "GitHub Releases" }
|
|
173
|
+
} elseif ($Source -eq "oss") {
|
|
174
|
+
$urls += @{ Url = $ossUrl; Label = "Aliyun OSS" }
|
|
175
|
+
} else {
|
|
176
|
+
$urls += @{ Url = $ghUrl; Label = "GitHub Releases" }
|
|
177
|
+
$urls += @{ Url = $ossUrl; Label = "Aliyun OSS" }
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return $urls
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function Invoke-Download {
|
|
184
|
+
param([string]$Url, [string]$OutFile, [string]$Label)
|
|
185
|
+
|
|
186
|
+
Write-Step "Trying $Label..."
|
|
187
|
+
Write-Step "URL: $Url"
|
|
188
|
+
|
|
189
|
+
if ($DryRun) {
|
|
190
|
+
Write-Check "[DRY-RUN] Would download to: $OutFile"
|
|
191
|
+
return $true
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
$ProgressPreference = if ($Silent) { "SilentlyContinue" } else { "Continue" }
|
|
196
|
+
|
|
197
|
+
# Use Invoke-WebRequest with progress bar
|
|
198
|
+
Invoke-WebRequest -Uri $Url -OutFile $OutFile -UseBasicParsing -ErrorAction Stop
|
|
199
|
+
|
|
200
|
+
if (Test-Path $OutFile) {
|
|
201
|
+
$size = (Get-Item $OutFile).Length
|
|
202
|
+
if ($size -gt 102400) { # > 100 KB minimum
|
|
203
|
+
Write-Check "Downloaded $( [math]::Round($size / 1MB, 1) ) MB"
|
|
204
|
+
return $true
|
|
205
|
+
} else {
|
|
206
|
+
Write-WarnMsg "Downloaded file too small ($size bytes) — may be an error page"
|
|
207
|
+
Remove-Item $OutFile -Force -ErrorAction SilentlyContinue
|
|
208
|
+
return $false
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
Write-WarnMsg "Download appeared to succeed but file not found"
|
|
212
|
+
return $false
|
|
213
|
+
} catch {
|
|
214
|
+
Write-WarnMsg "Failed: $($_.Exception.Message)"
|
|
215
|
+
return $false
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
# ──────────────────────────────────────────────
|
|
220
|
+
# PATH management
|
|
221
|
+
# ──────────────────────────────────────────────
|
|
222
|
+
|
|
223
|
+
function Add-DirectoryToUserPath {
|
|
224
|
+
param([string]$Dir)
|
|
225
|
+
|
|
226
|
+
$dirResolved = (Resolve-Path $Dir -ErrorAction SilentlyContinue).Path
|
|
227
|
+
if (-not $dirResolved) { $dirResolved = $Dir }
|
|
228
|
+
|
|
229
|
+
# Read current user PATH
|
|
230
|
+
$currentUserPath = [System.Environment]::GetEnvironmentVariable("Path", [System.EnvironmentVariableTarget]::User)
|
|
231
|
+
$paths = if ($currentUserPath) { $currentUserPath -split ";" | Where-Object { $_ } } else { @() }
|
|
232
|
+
|
|
233
|
+
# Check if already present
|
|
234
|
+
$normalized = $paths | ForEach-Object { $rp = Resolve-Path $_ -ErrorAction SilentlyContinue; if ($rp) { $rp.Path } else { $_ } }
|
|
235
|
+
if ($normalized -contains $dirResolved) {
|
|
236
|
+
Write-Check "Already in user PATH: $Dir"
|
|
237
|
+
return
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if ($DryRun) {
|
|
241
|
+
Write-Check "[DRY-RUN] Would add to user PATH: $Dir"
|
|
242
|
+
return
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
$newPath = if ($currentUserPath) { "$currentUserPath;$Dir" } else { $Dir }
|
|
246
|
+
[System.Environment]::SetEnvironmentVariable("Path", $newPath, [System.EnvironmentVariableTarget]::User)
|
|
247
|
+
Write-Check "Added to user PATH: $Dir"
|
|
248
|
+
|
|
249
|
+
# Also update current session
|
|
250
|
+
$env:Path = "$env:Path;$Dir"
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
# ──────────────────────────────────────────────
|
|
254
|
+
# Main
|
|
255
|
+
# ──────────────────────────────────────────────
|
|
256
|
+
|
|
257
|
+
function Main {
|
|
258
|
+
Write-Summary "╔════════════════════════════════════════╗" -Color Cyan
|
|
259
|
+
Write-Summary "║ browser4-cli Installer ║" -Color Cyan
|
|
260
|
+
Write-Summary "╚════════════════════════════════════════╝" -Color Cyan
|
|
261
|
+
Write-Summary ""
|
|
262
|
+
|
|
263
|
+
# Detect platform
|
|
264
|
+
$platformKey = Get-PlatformKey
|
|
265
|
+
$binaryName = Get-BinaryName -PlatformKey $platformKey
|
|
266
|
+
Write-Step "Platform: $platformKey"
|
|
267
|
+
Write-Step "Binary: $binaryName"
|
|
268
|
+
|
|
269
|
+
# Determine install directory
|
|
270
|
+
$installDir = if ($InstallDir) { $InstallDir } else { Get-DefaultInstallDir }
|
|
271
|
+
Write-Step "Install: $installDir"
|
|
272
|
+
Write-Summary ""
|
|
273
|
+
|
|
274
|
+
if (-not (Test-Path $installDir)) {
|
|
275
|
+
if (-not $DryRun) {
|
|
276
|
+
New-Item -ItemType Directory -Path $installDir -Force | Out-Null
|
|
277
|
+
}
|
|
278
|
+
Write-Step "Created directory: $installDir"
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
$binaryPath = Join-Path $installDir $binaryName
|
|
282
|
+
|
|
283
|
+
# If binary already exists and no version override, skip download
|
|
284
|
+
if ((Test-Path $binaryPath) -and (-not $Version)) {
|
|
285
|
+
Write-Check "Binary already installed: $binaryPath"
|
|
286
|
+
} else {
|
|
287
|
+
# Build download URLs
|
|
288
|
+
$urls = Get-DownloadUrls -BinaryName $binaryName -VersionTag $Version
|
|
289
|
+
if (-not $urls -or $urls.Count -eq 0) {
|
|
290
|
+
throw "No download URLs configured"
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
$downloaded = $false
|
|
294
|
+
$tempFile = [System.IO.Path]::GetTempFileName()
|
|
295
|
+
|
|
296
|
+
foreach ($entry in $urls) {
|
|
297
|
+
if (Invoke-Download -Url $entry.Url -OutFile $tempFile -Label $entry.Label) {
|
|
298
|
+
$downloaded = $true
|
|
299
|
+
break
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (-not $downloaded) {
|
|
304
|
+
if (Test-Path $tempFile) { Remove-Item $tempFile -Force }
|
|
305
|
+
throw @"
|
|
306
|
+
Could not download browser4-cli binary.
|
|
307
|
+
|
|
308
|
+
Tried:
|
|
309
|
+
$($urls | ForEach-Object { " - $($_.Label): $($_.Url)" } | Out-String)
|
|
310
|
+
|
|
311
|
+
Please check:
|
|
312
|
+
- Network connectivity
|
|
313
|
+
- The version/tag exists: $Version
|
|
314
|
+
- For GitHub rate limits, set GITHUB_TOKEN environment variable
|
|
315
|
+
"@
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
# Move from temp to install dir
|
|
319
|
+
if (-not $DryRun) {
|
|
320
|
+
if (Test-Path $binaryPath) { Remove-Item $binaryPath -Force }
|
|
321
|
+
Move-Item $tempFile $binaryPath -Force
|
|
322
|
+
}
|
|
323
|
+
Write-Check "Installed: $binaryPath"
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
# On Unix, ensure executable bit
|
|
327
|
+
if (-not $script:IsWin) {
|
|
328
|
+
if (-not $DryRun) {
|
|
329
|
+
try { chmod +x $binaryPath 2>$null } catch { }
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
# Add to PATH
|
|
334
|
+
if ($AddToPath -and $script:IsWin) {
|
|
335
|
+
Write-Summary ""
|
|
336
|
+
Add-DirectoryToUserPath -Dir $installDir
|
|
337
|
+
} elseif ($AddToPath -and -not $script:IsWin) {
|
|
338
|
+
Write-Summary ""
|
|
339
|
+
$shellRc = if (Test-Path "$env:HOME/.zshrc") { "$env:HOME/.zshrc" } elseif (Test-Path "$env:HOME/.bashrc") { "$env:HOME/.bashrc" } elseif (Test-Path "$env:HOME/.bash_profile") { "$env:HOME/.bash_profile" } else { "$env:HOME/.profile" }
|
|
340
|
+
$pathLine = "export PATH=""$installDir`:`$PATH"""
|
|
341
|
+
if (-not $DryRun) {
|
|
342
|
+
if (-not (Select-String -Path $shellRc -Pattern [regex]::Escape($installDir) -ErrorAction SilentlyContinue)) {
|
|
343
|
+
Add-Content -Path $shellRc -Value ""
|
|
344
|
+
Add-Content -Path $shellRc -Value "# browser4-cli"
|
|
345
|
+
Add-Content -Path $shellRc -Value $pathLine
|
|
346
|
+
Write-Check "Added to PATH in $shellRc"
|
|
347
|
+
} else {
|
|
348
|
+
Write-Check "PATH entry already in $shellRc"
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
Write-Check "[DRY-RUN] Would add to $shellRc"
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
# Verify
|
|
356
|
+
Write-Summary ""
|
|
357
|
+
if (-not $DryRun) {
|
|
358
|
+
try {
|
|
359
|
+
$versionOutput = & $binaryPath --version 2>&1
|
|
360
|
+
Write-Summary "✓ browser4-cli installed successfully" -Color Green
|
|
361
|
+
Write-Summary " Version: $versionOutput"
|
|
362
|
+
} catch {
|
|
363
|
+
Write-Summary "✓ Binary installed at: $binaryPath" -Color Green
|
|
364
|
+
Write-WarnMsg "Could not verify --version (this is normal on first install)"
|
|
365
|
+
}
|
|
366
|
+
} else {
|
|
367
|
+
Write-Summary "[DRY-RUN] Installation plan complete" -Color Yellow
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
Write-Summary ""
|
|
371
|
+
Write-Summary "Run 'browser4-cli --help' to get started." -Color Cyan
|
|
372
|
+
|
|
373
|
+
if ($script:IsWin) {
|
|
374
|
+
Write-Summary "If the command isn't found, restart your terminal or run:"
|
|
375
|
+
Write-Summary " `$env:Path = [System.Environment]::GetEnvironmentVariable('Path','User') + ';' + [System.Environment]::GetEnvironmentVariable('Path','Machine')"
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
Main
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# install-browser4-cli.sh
|
|
3
|
+
# Download and install the browser4-cli native binary.
|
|
4
|
+
#
|
|
5
|
+
# Detects OS, CPU architecture, and libc variant (glibc / musl), downloads
|
|
6
|
+
# the matching binary from GitHub Releases or Alibaba Cloud OSS, and installs
|
|
7
|
+
# it to a user-local directory.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# curl -fsSL https://.../install-browser4-cli.sh | bash
|
|
11
|
+
# curl -fsSL https://.../install-browser4-cli.sh | bash -s -- --silent
|
|
12
|
+
#
|
|
13
|
+
# ./install-browser4-cli.sh [OPTIONS]
|
|
14
|
+
#
|
|
15
|
+
# Options:
|
|
16
|
+
# --version, -v TAG Release tag (e.g. "v4.11.0"). Default: latest.
|
|
17
|
+
# --install-dir, -d DIR Install directory (default: ~/.local/bin).
|
|
18
|
+
# --source SRC Force download source: "github" or "oss".
|
|
19
|
+
# --no-path Skip adding install dir to PATH.
|
|
20
|
+
# --silent, -s Suppress non-error output.
|
|
21
|
+
# --dry-run Print what would be done without doing it.
|
|
22
|
+
# --help, -h Show this message.
|
|
23
|
+
|
|
24
|
+
set -euo pipefail
|
|
25
|
+
|
|
26
|
+
# ──────────────────────────────────────────────
|
|
27
|
+
# Globals
|
|
28
|
+
# ──────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
GITHUB_REPO="platonai/Browser4"
|
|
31
|
+
OSS_BASE="https://browser4.oss-cn-beijing.aliyuncs.com"
|
|
32
|
+
|
|
33
|
+
VERSION=""
|
|
34
|
+
INSTALL_DIR=""
|
|
35
|
+
SOURCE=""
|
|
36
|
+
ADD_TO_PATH=true
|
|
37
|
+
SILENT=false
|
|
38
|
+
DRY_RUN=false
|
|
39
|
+
|
|
40
|
+
# ──────────────────────────────────────────────
|
|
41
|
+
# Helpers
|
|
42
|
+
# ──────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
say() { if [[ "$SILENT" != true ]]; then echo -e "$*"; fi; }
|
|
45
|
+
step() { say " → $*"; }
|
|
46
|
+
ok() { say " ✓ $*"; }
|
|
47
|
+
warn() { say " ⚠ $*" >&2; }
|
|
48
|
+
die() { echo "ERROR: $*" >&2; exit 1; }
|
|
49
|
+
|
|
50
|
+
color_cyan='\033[0;36m'
|
|
51
|
+
color_green='\033[0;32m'
|
|
52
|
+
color_yellow='\033[0;33m'
|
|
53
|
+
color_reset='\033[0m'
|
|
54
|
+
|
|
55
|
+
header() {
|
|
56
|
+
if [[ "$SILENT" != true ]]; then
|
|
57
|
+
echo -e "${color_cyan}╔════════════════════════════════════════╗${color_reset}"
|
|
58
|
+
echo -e "${color_cyan}║ browser4-cli Installer ║${color_reset}"
|
|
59
|
+
echo -e "${color_cyan}╚════════════════════════════════════════╝${color_reset}"
|
|
60
|
+
echo ""
|
|
61
|
+
fi
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
usage() {
|
|
65
|
+
cat <<EOF
|
|
66
|
+
Usage: $(basename "$0") [OPTIONS]
|
|
67
|
+
|
|
68
|
+
Download and install the browser4-cli native binary.
|
|
69
|
+
|
|
70
|
+
Options:
|
|
71
|
+
--version, -v TAG Release tag (e.g. "v4.11.0"). Default: latest.
|
|
72
|
+
--install-dir, -d DIR Install directory (default: ~/.local/bin).
|
|
73
|
+
--source SRC Force source: "github" or "oss" (default: try both).
|
|
74
|
+
--no-path Skip adding install dir to shell rc file.
|
|
75
|
+
--silent, -s Suppress non-error output.
|
|
76
|
+
--dry-run Print what would be done without doing it.
|
|
77
|
+
--help, -h Show this message.
|
|
78
|
+
|
|
79
|
+
Examples:
|
|
80
|
+
$(basename "$0") # Install latest to ~/.local/bin
|
|
81
|
+
$(basename "$0") --version v4.11.0 # Install specific version
|
|
82
|
+
$(basename "$0") --source oss --silent # Silent install from Aliyun OSS
|
|
83
|
+
$(basename "$0") --install-dir /usr/local/bin # System-wide install (needs sudo)
|
|
84
|
+
EOF
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# ──────────────────────────────────────────────
|
|
88
|
+
# Argument parsing
|
|
89
|
+
# ──────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
while [[ $# -gt 0 ]]; do
|
|
92
|
+
case "$1" in
|
|
93
|
+
--version|-v)
|
|
94
|
+
shift; [[ -z "${1:-}" ]] && die "--version requires a value"
|
|
95
|
+
VERSION="$1"; shift ;;
|
|
96
|
+
--install-dir|-d)
|
|
97
|
+
shift; [[ -z "${1:-}" ]] && die "--install-dir requires a value"
|
|
98
|
+
INSTALL_DIR="$1"; shift ;;
|
|
99
|
+
--source)
|
|
100
|
+
shift; [[ -z "${1:-}" ]] && die "--source requires a value"
|
|
101
|
+
if [[ "$1" != "github" && "$1" != "oss" ]]; then
|
|
102
|
+
die "--source must be 'github' or 'oss'"
|
|
103
|
+
fi
|
|
104
|
+
SOURCE="$1"; shift ;;
|
|
105
|
+
--no-path) ADD_TO_PATH=false; shift ;;
|
|
106
|
+
--silent|-s) SILENT=true; shift ;;
|
|
107
|
+
--dry-run) DRY_RUN=true; shift ;;
|
|
108
|
+
--help|-h) usage; exit 0 ;;
|
|
109
|
+
*) die "Unknown argument: $1 (use --help)";;
|
|
110
|
+
esac
|
|
111
|
+
done
|
|
112
|
+
|
|
113
|
+
# ──────────────────────────────────────────────
|
|
114
|
+
# Platform detection
|
|
115
|
+
# ──────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
detect_os() {
|
|
118
|
+
case "$(uname -s)" in
|
|
119
|
+
Linux) echo "linux" ;;
|
|
120
|
+
Darwin) echo "darwin" ;;
|
|
121
|
+
MINGW*|MSYS*|CYGWIN*) echo "win32" ;;
|
|
122
|
+
*) die "Unsupported OS: $(uname -s)" ;;
|
|
123
|
+
esac
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
detect_arch() {
|
|
127
|
+
local arch
|
|
128
|
+
arch=$(uname -m)
|
|
129
|
+
case "$arch" in
|
|
130
|
+
x86_64|amd64) echo "x64" ;;
|
|
131
|
+
aarch64|arm64) echo "arm64" ;;
|
|
132
|
+
*) die "Unsupported architecture: $arch" ;;
|
|
133
|
+
esac
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
detect_libc() {
|
|
137
|
+
# Only relevant on Linux
|
|
138
|
+
if [[ "$(uname -s)" != "Linux" ]]; then
|
|
139
|
+
echo ""
|
|
140
|
+
return
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
# Check for musl
|
|
144
|
+
if ldd --version 2>&1 | grep -qi musl; then
|
|
145
|
+
echo "musl"
|
|
146
|
+
elif [[ -f /lib/ld-musl-x86_64.so.1 ]] || [[ -f /lib/ld-musl-aarch64.so.1 ]]; then
|
|
147
|
+
echo "musl"
|
|
148
|
+
else
|
|
149
|
+
echo ""
|
|
150
|
+
fi
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
get_platform_key() {
|
|
154
|
+
local os arch libc
|
|
155
|
+
os=$(detect_os)
|
|
156
|
+
arch=$(detect_arch)
|
|
157
|
+
|
|
158
|
+
if [[ "$os" == "linux" ]]; then
|
|
159
|
+
libc=$(detect_libc)
|
|
160
|
+
if [[ -n "$libc" ]]; then
|
|
161
|
+
echo "linux-${libc}-${arch}"
|
|
162
|
+
else
|
|
163
|
+
echo "linux-${arch}"
|
|
164
|
+
fi
|
|
165
|
+
else
|
|
166
|
+
echo "${os}-${arch}"
|
|
167
|
+
fi
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
get_binary_name() {
|
|
171
|
+
local platform_key="$1"
|
|
172
|
+
local ext=""
|
|
173
|
+
if [[ "$platform_key" == win32-* ]]; then
|
|
174
|
+
ext=".exe"
|
|
175
|
+
fi
|
|
176
|
+
echo "browser4-cli-${platform_key}${ext}"
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
get_default_install_dir() {
|
|
180
|
+
local dir="${HOME}/.local/bin"
|
|
181
|
+
# Prefer existing dir
|
|
182
|
+
if [[ -d "$dir" ]]; then
|
|
183
|
+
echo "$dir"
|
|
184
|
+
return
|
|
185
|
+
fi
|
|
186
|
+
# Check if ~/bin exists
|
|
187
|
+
if [[ -d "${HOME}/bin" ]]; then
|
|
188
|
+
echo "${HOME}/bin"
|
|
189
|
+
return
|
|
190
|
+
fi
|
|
191
|
+
echo "$dir"
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
# ──────────────────────────────────────────────
|
|
195
|
+
# Download
|
|
196
|
+
# ──────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
check_commands() {
|
|
199
|
+
if ! command -v curl >/dev/null 2>&1; then
|
|
200
|
+
die "'curl' is required but not found. Install curl and retry."
|
|
201
|
+
fi
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
get_download_urls() {
|
|
205
|
+
local binary_name="$1"
|
|
206
|
+
local version_tag="$2"
|
|
207
|
+
local urls=()
|
|
208
|
+
|
|
209
|
+
if [[ -n "$version_tag" ]]; then
|
|
210
|
+
local gh_url="https://github.com/${GITHUB_REPO}/releases/download/${version_tag}/${binary_name}"
|
|
211
|
+
local oss_url="${OSS_BASE}/releases/download/${version_tag}/${binary_name}"
|
|
212
|
+
else
|
|
213
|
+
# Use 'latest' redirect for GitHub, 'latest' symlink for OSS
|
|
214
|
+
local gh_url="https://github.com/${GITHUB_REPO}/releases/latest/download/${binary_name}"
|
|
215
|
+
local oss_url="${OSS_BASE}/releases/download/latest/${binary_name}"
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
case "$SOURCE" in
|
|
219
|
+
github) urls+=("GitHub Releases|${gh_url}") ;;
|
|
220
|
+
oss) urls+=("Aliyun OSS|${oss_url}") ;;
|
|
221
|
+
*)
|
|
222
|
+
urls+=("GitHub Releases|${gh_url}")
|
|
223
|
+
urls+=("Aliyun OSS|${oss_url}")
|
|
224
|
+
;;
|
|
225
|
+
esac
|
|
226
|
+
|
|
227
|
+
printf '%s\n' "${urls[@]}"
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
download_file() {
|
|
231
|
+
local url="$1"
|
|
232
|
+
local dest="$2"
|
|
233
|
+
local label="$3"
|
|
234
|
+
|
|
235
|
+
step "Trying ${label}..."
|
|
236
|
+
step "URL: $url"
|
|
237
|
+
|
|
238
|
+
if [[ "$DRY_RUN" == true ]]; then
|
|
239
|
+
ok "[DRY-RUN] Would download to: $dest"
|
|
240
|
+
return 0
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
local http_code
|
|
244
|
+
http_code=$(curl -sSfL -w "%{http_code}" -o "$dest" \
|
|
245
|
+
${GITHUB_TOKEN:+-H "Authorization: Bearer $GITHUB_TOKEN"} \
|
|
246
|
+
"$url" 2>/dev/null) || true
|
|
247
|
+
|
|
248
|
+
if [[ "$http_code" == "200" ]] || [[ "$http_code" == "302" ]]; then
|
|
249
|
+
local size
|
|
250
|
+
size=$(stat -c%s "$dest" 2>/dev/null || stat -f%z "$dest" 2>/dev/null || echo 0)
|
|
251
|
+
|
|
252
|
+
# Sanity check: binary must be > 100 KB
|
|
253
|
+
if [[ "$size" -gt 102400 ]]; then
|
|
254
|
+
local size_mb
|
|
255
|
+
size_mb=$(awk "BEGIN { printf \"%.1f\", $size / 1048576 }")
|
|
256
|
+
ok "Downloaded ${size_mb} MB"
|
|
257
|
+
return 0
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
warn "Downloaded file too small (${size} bytes) — may be an error page"
|
|
261
|
+
rm -f "$dest"
|
|
262
|
+
return 1
|
|
263
|
+
fi
|
|
264
|
+
|
|
265
|
+
warn "Failed: HTTP ${http_code}"
|
|
266
|
+
rm -f "$dest"
|
|
267
|
+
return 1
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# ──────────────────────────────────────────────
|
|
271
|
+
# PATH management
|
|
272
|
+
# ──────────────────────────────────────────────
|
|
273
|
+
|
|
274
|
+
add_to_shell_rc() {
|
|
275
|
+
local dir="$1"
|
|
276
|
+
|
|
277
|
+
if [[ "$DRY_RUN" == true ]]; then
|
|
278
|
+
ok "[DRY-RUN] Would add to shell rc: $dir"
|
|
279
|
+
return
|
|
280
|
+
fi
|
|
281
|
+
|
|
282
|
+
# Pick the best rc file
|
|
283
|
+
local rc_file=""
|
|
284
|
+
for candidate in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile"; do
|
|
285
|
+
if [[ -f "$candidate" ]]; then
|
|
286
|
+
rc_file="$candidate"
|
|
287
|
+
break
|
|
288
|
+
fi
|
|
289
|
+
done
|
|
290
|
+
|
|
291
|
+
if [[ -z "$rc_file" ]]; then
|
|
292
|
+
rc_file="$HOME/.profile"
|
|
293
|
+
fi
|
|
294
|
+
|
|
295
|
+
if grep -qF "$dir" "$rc_file" 2>/dev/null; then
|
|
296
|
+
ok "PATH entry already in $rc_file"
|
|
297
|
+
return
|
|
298
|
+
fi
|
|
299
|
+
|
|
300
|
+
{
|
|
301
|
+
echo ""
|
|
302
|
+
echo "# browser4-cli"
|
|
303
|
+
echo "export PATH=\"$dir:\$PATH\""
|
|
304
|
+
} >> "$rc_file"
|
|
305
|
+
|
|
306
|
+
ok "Added to PATH in $rc_file"
|
|
307
|
+
say " Reload with: source $rc_file"
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
# ──────────────────────────────────────────────
|
|
311
|
+
# Main
|
|
312
|
+
# ──────────────────────────────────────────────
|
|
313
|
+
|
|
314
|
+
main() {
|
|
315
|
+
check_commands
|
|
316
|
+
header
|
|
317
|
+
|
|
318
|
+
# Detect platform
|
|
319
|
+
local platform_key binary_name
|
|
320
|
+
platform_key=$(get_platform_key)
|
|
321
|
+
binary_name=$(get_binary_name "$platform_key")
|
|
322
|
+
|
|
323
|
+
step "Platform: $platform_key"
|
|
324
|
+
step "Binary: $binary_name"
|
|
325
|
+
|
|
326
|
+
# Install directory
|
|
327
|
+
if [[ -z "$INSTALL_DIR" ]]; then
|
|
328
|
+
INSTALL_DIR=$(get_default_install_dir)
|
|
329
|
+
fi
|
|
330
|
+
step "Install: $INSTALL_DIR"
|
|
331
|
+
say ""
|
|
332
|
+
|
|
333
|
+
# Ensure install directory exists
|
|
334
|
+
if [[ ! -d "$INSTALL_DIR" ]]; then
|
|
335
|
+
if [[ "$DRY_RUN" != true ]]; then
|
|
336
|
+
mkdir -p "$INSTALL_DIR"
|
|
337
|
+
fi
|
|
338
|
+
step "Created directory: $INSTALL_DIR"
|
|
339
|
+
fi
|
|
340
|
+
|
|
341
|
+
local binary_path="${INSTALL_DIR}/${binary_name}"
|
|
342
|
+
|
|
343
|
+
# Download if needed
|
|
344
|
+
if [[ -f "$binary_path" ]] && [[ -z "$VERSION" ]]; then
|
|
345
|
+
ok "Binary already installed: $binary_path"
|
|
346
|
+
else
|
|
347
|
+
local urls
|
|
348
|
+
IFS=$'\n' read -r -d '' -a urls < <(get_download_urls "$binary_name" "$VERSION" && printf '\0')
|
|
349
|
+
|
|
350
|
+
if [[ ${#urls[@]} -eq 0 ]]; then
|
|
351
|
+
die "No download URLs configured"
|
|
352
|
+
fi
|
|
353
|
+
|
|
354
|
+
local downloaded=false
|
|
355
|
+
local tmpfile
|
|
356
|
+
tmpfile=$(mktemp)
|
|
357
|
+
|
|
358
|
+
# Ensure cleanup
|
|
359
|
+
cleanup() { rm -f "$tmpfile"; }
|
|
360
|
+
trap cleanup EXIT
|
|
361
|
+
|
|
362
|
+
for entry in "${urls[@]}"; do
|
|
363
|
+
local label url
|
|
364
|
+
label="${entry%%|*}"
|
|
365
|
+
url="${entry#*|}"
|
|
366
|
+
|
|
367
|
+
if download_file "$url" "$tmpfile" "$label"; then
|
|
368
|
+
downloaded=true
|
|
369
|
+
break
|
|
370
|
+
fi
|
|
371
|
+
done
|
|
372
|
+
|
|
373
|
+
if [[ "$downloaded" != true ]]; then
|
|
374
|
+
local tried_msg="Could not download browser4-cli binary."$'\n'$'\n'"Tried:"$'\n'
|
|
375
|
+
for entry in "${urls[@]}"; do
|
|
376
|
+
local l="${entry%%|*}" u="${entry#*|}"
|
|
377
|
+
tried_msg+=" - ${l}: ${u}"$'\n'
|
|
378
|
+
done
|
|
379
|
+
tried_msg+=$'\n'"Please check:"$'\n'
|
|
380
|
+
tried_msg+=" - Network connectivity"$'\n'
|
|
381
|
+
tried_msg+=" - The version/tag exists: ${VERSION:-latest}"$'\n'
|
|
382
|
+
tried_msg+=" - For GitHub rate limits, set GITHUB_TOKEN environment variable"
|
|
383
|
+
die "$tried_msg"
|
|
384
|
+
fi
|
|
385
|
+
|
|
386
|
+
# Move from temp to install dir
|
|
387
|
+
if [[ "$DRY_RUN" != true ]]; then
|
|
388
|
+
rm -f "$binary_path"
|
|
389
|
+
mv "$tmpfile" "$binary_path"
|
|
390
|
+
chmod +x "$binary_path"
|
|
391
|
+
fi
|
|
392
|
+
ok "Installed: $binary_path"
|
|
393
|
+
|
|
394
|
+
trap - EXIT
|
|
395
|
+
fi
|
|
396
|
+
|
|
397
|
+
# Ensure executable
|
|
398
|
+
if [[ "$DRY_RUN" != true ]] && [[ -f "$binary_path" ]]; then
|
|
399
|
+
chmod +x "$binary_path" 2>/dev/null || true
|
|
400
|
+
fi
|
|
401
|
+
|
|
402
|
+
# Add to PATH
|
|
403
|
+
if [[ "$ADD_TO_PATH" == true ]]; then
|
|
404
|
+
say ""
|
|
405
|
+
add_to_shell_rc "$INSTALL_DIR"
|
|
406
|
+
fi
|
|
407
|
+
|
|
408
|
+
# Verify
|
|
409
|
+
say ""
|
|
410
|
+
if [[ "$DRY_RUN" != true ]]; then
|
|
411
|
+
if version_output=$("$binary_path" --version 2>&1); then
|
|
412
|
+
echo -e "${color_green}✓ browser4-cli installed successfully${color_reset}"
|
|
413
|
+
say " Version: $version_output"
|
|
414
|
+
else
|
|
415
|
+
echo -e "${color_green}✓ Binary installed at: $binary_path${color_reset}"
|
|
416
|
+
warn "Could not verify --version (this is normal on first install)"
|
|
417
|
+
fi
|
|
418
|
+
else
|
|
419
|
+
echo -e "${color_yellow}[DRY-RUN] Installation plan complete${color_reset}"
|
|
420
|
+
fi
|
|
421
|
+
|
|
422
|
+
say ""
|
|
423
|
+
echo -e "${color_cyan}Run 'browser4-cli --help' to get started.${color_reset}"
|
|
424
|
+
|
|
425
|
+
# If ADD_TO_PATH was used, remind about sourcing
|
|
426
|
+
if [[ "$ADD_TO_PATH" == true ]] && [[ "$DRY_RUN" != true ]]; then
|
|
427
|
+
say ""
|
|
428
|
+
say "To use immediately, run:"
|
|
429
|
+
say " export PATH=\"$INSTALL_DIR:\$PATH\""
|
|
430
|
+
fi
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
main
|
package/scripts/sync-version.js
CHANGED
|
@@ -181,12 +181,14 @@ if (cargoVersion !== version) {
|
|
|
181
181
|
|
|
182
182
|
// 4. Update Cargo.lock (only in sync mode)
|
|
183
183
|
if (!checkOnly && cargoVersion !== version) {
|
|
184
|
+
let lockUpdated = false;
|
|
184
185
|
try {
|
|
185
186
|
execSync("cargo update -p browser4-cli --offline", {
|
|
186
187
|
cwd: cargoDir,
|
|
187
188
|
stdio: "pipe",
|
|
188
189
|
});
|
|
189
190
|
console.log(" Updated Cargo.lock");
|
|
191
|
+
lockUpdated = true;
|
|
190
192
|
} catch {
|
|
191
193
|
try {
|
|
192
194
|
execSync("cargo update -p browser4-cli", {
|
|
@@ -194,8 +196,34 @@ if (!checkOnly && cargoVersion !== version) {
|
|
|
194
196
|
stdio: "pipe",
|
|
195
197
|
});
|
|
196
198
|
console.log(" Updated Cargo.lock");
|
|
199
|
+
lockUpdated = true;
|
|
197
200
|
} catch (e) {
|
|
198
|
-
console.error(` Warning: Could not update Cargo.lock: ${e.message}`);
|
|
201
|
+
console.error(` Warning: Could not update Cargo.lock via cargo: ${e.message}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Fallback: edit Cargo.lock directly when cargo update fails (e.g. due to
|
|
206
|
+
// lock file version mismatch between the installed cargo and the lock file).
|
|
207
|
+
if (!lockUpdated) {
|
|
208
|
+
const cargoLockPath = join(cargoDir, "Cargo.lock");
|
|
209
|
+
try {
|
|
210
|
+
let lockContent = readFileSync(cargoLockPath, "utf-8");
|
|
211
|
+
// Match the [[package]] block for browser4-cli and replace its version.
|
|
212
|
+
// Works for both v3 and v4 lock file formats.
|
|
213
|
+
const lockPkgRegex = new RegExp(
|
|
214
|
+
`(\\[\\[package\\]\\][\\r\\n]+\\s*name\\s*=\\s*"browser4-cli"[\\r\\n]+\\s*version\\s*=\\s*)"[^"]*"`,
|
|
215
|
+
"m"
|
|
216
|
+
);
|
|
217
|
+
if (lockPkgRegex.test(lockContent)) {
|
|
218
|
+
lockContent = lockContent.replace(lockPkgRegex, `$1"${version}"`);
|
|
219
|
+
writeFileSync(cargoLockPath, lockContent);
|
|
220
|
+
console.log(` Updated Cargo.lock directly: ${cargoVersion} -> ${version}`);
|
|
221
|
+
lockUpdated = true;
|
|
222
|
+
} else {
|
|
223
|
+
console.error(" Warning: Could not find browser4-cli entry in Cargo.lock");
|
|
224
|
+
}
|
|
225
|
+
} catch (e2) {
|
|
226
|
+
console.error(` Warning: Could not update Cargo.lock directly: ${e2.message}`);
|
|
199
227
|
}
|
|
200
228
|
}
|
|
201
229
|
}
|