browser4-cli 4.12.0-rc.2 → 4.12.0
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 +21 -15
- 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 +79 -17
- package/scripts/install-browser4-cli.sh +31 -8
- package/scripts/tests/install-browser4-cli.tests.ps1 +151 -0
- package/scripts/tests/install-browser4-cli.tests.sh +135 -0
package/README.md
CHANGED
|
@@ -88,7 +88,7 @@ browser4-cli agent run "Search amazon for mechanical keyboards, compare the top
|
|
|
88
88
|
|
|
89
89
|
# Parallel scraping with swarm
|
|
90
90
|
browser4-cli swarm create --max-open-tabs 12 --display-mode HEADLESS
|
|
91
|
-
browser4-cli swarm submit --seed-file ./urls.txt --refresh
|
|
91
|
+
browser4-cli swarm submit --seed-file ./urls.txt --refresh
|
|
92
92
|
browser4-cli swarm result scrape-task-1
|
|
93
93
|
|
|
94
94
|
# Batch multiple commands
|
|
@@ -247,16 +247,19 @@ browser4-cli pdf --filename page.pdf
|
|
|
247
247
|
|
|
248
248
|
| Command | Description |
|
|
249
249
|
|---|---|
|
|
250
|
-
| `tab-list` | List all tabs with their zero-based index. |
|
|
250
|
+
| `tab-list` | List all tabs with their zero-based index and stable GUID. Use `--json` for full GUIDs. |
|
|
251
251
|
| `tab-new [url]` | Open a new tab. |
|
|
252
|
-
| `tab-close [index]` | Close a tab (current tab if no index). |
|
|
253
|
-
| `tab-select <index>` | Switch to a tab by index. |
|
|
252
|
+
| `tab-close [index]` | Close a tab (current tab if no index). Use `--guid <guid>` for GUID-based close. |
|
|
253
|
+
| `tab-select <index>` | Switch to a tab by index. Use `--guid <guid>` for GUID-based select. |
|
|
254
254
|
|
|
255
255
|
```bash
|
|
256
256
|
browser4-cli tab-list
|
|
257
|
+
browser4-cli tab-list --json # JSON output with full GUIDs
|
|
257
258
|
browser4-cli tab-select 1
|
|
259
|
+
browser4-cli tab-select --guid 1B46D74FB # select by stable GUID
|
|
258
260
|
browser4-cli tab-new https://example.com
|
|
259
261
|
browser4-cli tab-close 1
|
|
262
|
+
browser4-cli tab-close --guid 1B46D74FB # close by stable GUID
|
|
260
263
|
```
|
|
261
264
|
|
|
262
265
|
### Element inspection
|
|
@@ -382,17 +385,21 @@ instructions that always match the installed CLI version.
|
|
|
382
385
|
| `skills list` | List available bundled skills with file counts. |
|
|
383
386
|
| `skills get <name>` | Output a skill's SKILL.md content. `--full` includes references and templates. `--all` outputs every skill. |
|
|
384
387
|
| `skills path [name]` | Print the skills directory path. With a name, prints the path to that skill's subdirectory. |
|
|
388
|
+
| `skills unpack [dest]` | Unpack bundled skill files to a directory (defaults to the skills directory). |
|
|
385
389
|
|
|
386
390
|
Skill files are unpacked to the versioned installation directory during `browser4-cli install`.
|
|
391
|
+
Use `skills unpack` to refresh or relocate skill files without reinstalling.
|
|
387
392
|
Set `BROWSER4_SKILLS_DIR` to override the skills directory path.
|
|
388
393
|
|
|
389
394
|
```bash
|
|
390
|
-
browser4-cli skills
|
|
391
|
-
browser4-cli skills get browser4-cli
|
|
395
|
+
browser4-cli skills # List bundled skills
|
|
396
|
+
browser4-cli skills get browser4-cli # Get the CLI skill's main content
|
|
392
397
|
browser4-cli skills get browser4-cli --full # Include references and templates
|
|
393
|
-
browser4-cli skills get --all
|
|
394
|
-
browser4-cli skills path
|
|
395
|
-
browser4-cli skills path browser4-cli
|
|
398
|
+
browser4-cli skills get --all # Output every skill
|
|
399
|
+
browser4-cli skills path # Print skills root directory
|
|
400
|
+
browser4-cli skills path browser4-cli # Print path to a specific skill
|
|
401
|
+
browser4-cli skills unpack # Unpack to default skills directory
|
|
402
|
+
browser4-cli skills unpack /custom/path # Unpack to a custom directory
|
|
396
403
|
```
|
|
397
404
|
|
|
398
405
|
### Other
|
|
@@ -489,7 +496,7 @@ browser4-cli htmlsnapshot
|
|
|
489
496
|
browser4-cli htmlsnapshot get <field> [selector] [name]
|
|
490
497
|
browser4-cli htmlsnapshot get all <field> [selector] [name]
|
|
491
498
|
browser4-cli htmlsnapshot query [url] --sql <query>
|
|
492
|
-
browser4-cli htmlsnapshot export [--file <path>]
|
|
499
|
+
browser4-cli htmlsnapshot export [--file <path>] [--clean]
|
|
493
500
|
browser4-cli htmlsnapshot summary
|
|
494
501
|
browser4-cli htmlsnapshot grep [OPTIONS] <pattern>
|
|
495
502
|
browser4-cli htmlsnapshot inspect [selector] [--max N] [--depth D]
|
|
@@ -581,10 +588,11 @@ browser4-cli htmlsnapshot query --sql "
|
|
|
581
588
|
|
|
582
589
|
#### htmlsnapshot export
|
|
583
590
|
|
|
584
|
-
Save the full snapshot HTML to a local file.
|
|
591
|
+
Save the full snapshot HTML to a local file. Use `--clean` to strip `<script>`, `<style>`, comments, and non-standard attributes — producing minimal HTML ideal for LLM consumption.
|
|
585
592
|
|
|
586
593
|
```bash
|
|
587
594
|
browser4-cli htmlsnapshot export --file snapshot.html
|
|
595
|
+
browser4-cli htmlsnapshot export --file page.html --clean
|
|
588
596
|
```
|
|
589
597
|
|
|
590
598
|
#### htmlsnapshot summary
|
|
@@ -735,13 +743,12 @@ line; `#` comments and blank lines ignored), or both.
|
|
|
735
743
|
| `--expires <duration>` | Cache expiration (e.g. `1d`, `1h`) |
|
|
736
744
|
| `--refresh` | Force fresh fetch, ignore cache |
|
|
737
745
|
| `--parse` | Parse page immediately after fetching |
|
|
738
|
-
| `--store-content` | Persist page content to storage |
|
|
739
746
|
|
|
740
747
|
```bash
|
|
741
748
|
# Submit URLs from a file
|
|
742
749
|
browser4-cli swarm submit --seed-file ./urls.txt \
|
|
743
750
|
--deadline 2026-03-30T00:00:00Z \
|
|
744
|
-
--expires 1d --refresh
|
|
751
|
+
--expires 1d --refresh
|
|
745
752
|
|
|
746
753
|
# Submit with inline X-SQL
|
|
747
754
|
browser4-cli swarm submit "https://www.amazon.com/dp/B08PP5MSVB" --sql "
|
|
@@ -800,7 +807,6 @@ browser4-cli crawl <url> [options]
|
|
|
800
807
|
| `-a`, `--args` | — | Additional LoadOptions passthrough (e.g. `-a "-nMaxRetry 5"`) |
|
|
801
808
|
| `--refresh` | — | Force fresh fetch |
|
|
802
809
|
| `--parse` | — | Parse pages immediately after fetching |
|
|
803
|
-
| `--store-content` | — | Persist page content |
|
|
804
810
|
| `--expires` | — | Cache expiration (e.g. `1d`, `1h`, `30m`) |
|
|
805
811
|
| `-p`, `--priority` | — | Queue priority (lower = higher) |
|
|
806
812
|
| `--ignore-url-query` | — | Strip query params during URL normalization |
|
|
@@ -821,7 +827,7 @@ browser4-cli crawl "https://shop.example.com" \
|
|
|
821
827
|
--top-links 10
|
|
822
828
|
|
|
823
829
|
# Deep crawl with refresh and content storage
|
|
824
|
-
browser4-cli crawl "https://example.com" --depth 3 --refresh
|
|
830
|
+
browser4-cli crawl "https://example.com" --depth 3 --refresh
|
|
825
831
|
|
|
826
832
|
# Background crawl — submit and return immediately
|
|
827
833
|
browser4-cli crawl "https://example.com" -ol "a[href]" --background
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -429,39 +429,101 @@ function New-Symlinks {
|
|
|
429
429
|
if ($DryRun) {
|
|
430
430
|
Write-Step "[DRY-RUN] Would create link: $linkName -> $BinaryName"
|
|
431
431
|
} else {
|
|
432
|
-
New-PlatformLink -LinkPath $linkPath -TargetName $BinaryName -DisplayName $linkName
|
|
432
|
+
$null = New-PlatformLink -LinkPath $linkPath -TargetName $BinaryName -DisplayName $linkName
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
-
# 2)
|
|
435
|
+
# 2) b4 -> browser4-cli-<platform> (only if b4 is our tool or doesn't exist)
|
|
436
436
|
$shortName = "b4$ext"
|
|
437
437
|
$shortPath = Join-Path $InstallDir $shortName
|
|
438
438
|
|
|
439
|
-
|
|
440
|
-
$
|
|
441
|
-
if ($existingCmd) {
|
|
442
|
-
Write-WarnMsg "Skipping short link '$shortName': 'b4' already found on PATH ($($existingCmd.Source))"
|
|
443
|
-
return
|
|
444
|
-
}
|
|
439
|
+
$b4Exists = $false
|
|
440
|
+
$b4IsOurs = $false
|
|
445
441
|
|
|
446
|
-
# Check
|
|
442
|
+
# Check in install dir first --anything here is ours
|
|
447
443
|
if (Test-Path $shortPath) {
|
|
448
|
-
|
|
449
|
-
|
|
444
|
+
$b4Exists = $true
|
|
445
|
+
$b4IsOurs = $true
|
|
450
446
|
}
|
|
451
447
|
|
|
452
|
-
# Also check b4.cmd
|
|
453
|
-
if ($script:OSWin) {
|
|
448
|
+
# Also check b4.cmd wrapper on Windows
|
|
449
|
+
if ($script:OSWin -and -not $b4Exists) {
|
|
454
450
|
$shortCmdPath = Join-Path $InstallDir "b4.cmd"
|
|
455
451
|
if (Test-Path $shortCmdPath) {
|
|
456
|
-
|
|
457
|
-
|
|
452
|
+
$b4Exists = $true
|
|
453
|
+
$b4IsOurs = $true
|
|
458
454
|
}
|
|
459
455
|
}
|
|
460
456
|
|
|
457
|
+
# Check if b4 is on PATH from somewhere else.
|
|
458
|
+
# Try Get-Command first; fall back to scanning PATH directories manually
|
|
459
|
+
# because Get-Command may miss extensionless executables on Linux/macOS
|
|
460
|
+
# or may cache stale results in some PowerShell versions.
|
|
461
|
+
if (-not $b4Exists) {
|
|
462
|
+
$existingCmd = Get-Command b4 -ErrorAction SilentlyContinue
|
|
463
|
+
if ($existingCmd) {
|
|
464
|
+
$b4Exists = $true
|
|
465
|
+
# Check if it's ours by running --version
|
|
466
|
+
try {
|
|
467
|
+
$b4Version = & b4 --version 2>&1
|
|
468
|
+
if ($b4Version -match "browser4-cli") {
|
|
469
|
+
$b4IsOurs = $true
|
|
470
|
+
}
|
|
471
|
+
} catch {
|
|
472
|
+
# Can't determine --leave it alone
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
# Fallback: scan PATH manually when Get-Command didn't find anything.
|
|
478
|
+
# On Linux/macOS, PowerShell's Get-Command may miss scripts that are
|
|
479
|
+
# executable but not in its command cache, especially in CI containers.
|
|
480
|
+
if (-not $b4Exists) {
|
|
481
|
+
$separator = [System.IO.Path]::PathSeparator
|
|
482
|
+
$pathDirs = @($env:Path -split $separator | Where-Object { $_ })
|
|
483
|
+
foreach ($dir in $pathDirs) {
|
|
484
|
+
$candidatePath = Join-Path $dir $shortName
|
|
485
|
+
if (Test-Path $candidatePath -PathType Leaf) {
|
|
486
|
+
$b4Exists = $true
|
|
487
|
+
try {
|
|
488
|
+
$b4Version = & $candidatePath --version 2>&1
|
|
489
|
+
if ($b4Version -match "browser4-cli") {
|
|
490
|
+
$b4IsOurs = $true
|
|
491
|
+
}
|
|
492
|
+
} catch {
|
|
493
|
+
# Can't determine --leave it alone
|
|
494
|
+
}
|
|
495
|
+
break
|
|
496
|
+
}
|
|
497
|
+
# On Windows, also check for b4.cmd / b4.bat wrappers
|
|
498
|
+
if ($script:OSWin) {
|
|
499
|
+
foreach ($pext in @('.cmd', '.bat')) {
|
|
500
|
+
$candidateWin = Join-Path $dir "b4$pext"
|
|
501
|
+
if (Test-Path $candidateWin -PathType Leaf) {
|
|
502
|
+
$b4Exists = $true
|
|
503
|
+
try {
|
|
504
|
+
$b4Version = & $candidateWin --version 2>&1
|
|
505
|
+
if ($b4Version -match "browser4-cli") {
|
|
506
|
+
$b4IsOurs = $true
|
|
507
|
+
}
|
|
508
|
+
} catch { }
|
|
509
|
+
break
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
if ($b4Exists) { break }
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if ($b4Exists -and -not $b4IsOurs) {
|
|
518
|
+
Write-WarnMsg "Skipping short link '$shortName': 'b4' is not browser4-cli"
|
|
519
|
+
return
|
|
520
|
+
}
|
|
521
|
+
|
|
461
522
|
if ($DryRun) {
|
|
462
|
-
|
|
523
|
+
$action = if ($b4Exists) { "Would update link" } else { "Would create link" }
|
|
524
|
+
Write-Step "[DRY-RUN] $action`: $shortName -> $BinaryName"
|
|
463
525
|
} else {
|
|
464
|
-
New-PlatformLink -LinkPath $shortPath -TargetName $BinaryName -DisplayName $shortName
|
|
526
|
+
$null = New-PlatformLink -LinkPath $shortPath -TargetName $BinaryName -DisplayName $shortName
|
|
465
527
|
}
|
|
466
528
|
}
|
|
467
529
|
|
|
@@ -497,25 +497,48 @@ create_symlinks() {
|
|
|
497
497
|
ok "Created symlink: ${link_name} -> ${binary_name}"
|
|
498
498
|
fi
|
|
499
499
|
|
|
500
|
-
# 2)
|
|
500
|
+
# 2) b4 -> browser4-cli-<platform> (only if b4 is our tool or doesn't exist)
|
|
501
501
|
local short_name="b4${ext}"
|
|
502
502
|
local short_path="${install_dir}/${short_name}"
|
|
503
503
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
return
|
|
507
|
-
fi
|
|
504
|
+
local b4_exists=false
|
|
505
|
+
local b4_is_ours=false
|
|
508
506
|
|
|
507
|
+
# Check in install dir first — anything here is ours
|
|
509
508
|
if [[ -e "$short_path" ]] || [[ -L "$short_path" ]]; then
|
|
510
|
-
|
|
509
|
+
b4_exists=true
|
|
510
|
+
b4_is_ours=true
|
|
511
|
+
fi
|
|
512
|
+
|
|
513
|
+
# Check if b4 is on PATH from somewhere else
|
|
514
|
+
if [[ "$b4_exists" != true ]]; then
|
|
515
|
+
if command -v b4 >/dev/null 2>&1; then
|
|
516
|
+
b4_exists=true
|
|
517
|
+
# Check if it's ours by running --version
|
|
518
|
+
if b4 --version 2>&1 | grep -qi "browser4-cli"; then
|
|
519
|
+
b4_is_ours=true
|
|
520
|
+
fi
|
|
521
|
+
fi
|
|
522
|
+
fi
|
|
523
|
+
|
|
524
|
+
if [[ "$b4_exists" == true ]] && [[ "$b4_is_ours" != true ]]; then
|
|
525
|
+
warn "Skipping short link '${short_name}': 'b4' is not browser4-cli"
|
|
511
526
|
return
|
|
512
527
|
fi
|
|
513
528
|
|
|
514
529
|
if [[ "$DRY_RUN" == true ]]; then
|
|
515
|
-
|
|
530
|
+
if [[ "$b4_exists" == true ]]; then
|
|
531
|
+
step "[DRY-RUN] Would update symlink: ${short_name} -> ${binary_name}"
|
|
532
|
+
else
|
|
533
|
+
step "[DRY-RUN] Would create symlink: ${short_name} -> ${binary_name}"
|
|
534
|
+
fi
|
|
516
535
|
else
|
|
517
536
|
ln -sf "$binary_name" "$short_path"
|
|
518
|
-
|
|
537
|
+
if [[ "$b4_exists" == true ]]; then
|
|
538
|
+
ok "Updated symlink: ${short_name} -> ${binary_name}"
|
|
539
|
+
else
|
|
540
|
+
ok "Created symlink: ${short_name} -> ${binary_name}"
|
|
541
|
+
fi
|
|
519
542
|
fi
|
|
520
543
|
}
|
|
521
544
|
|
|
@@ -301,6 +301,157 @@ $sb = [ScriptBlock]::Create($scriptContent)
|
|
|
301
301
|
|
|
302
302
|
Write-Host ""
|
|
303
303
|
|
|
304
|
+
# ── New-Symlinks (b4 link logic) ──
|
|
305
|
+
Write-Host "--- New-Symlinks ---" -ForegroundColor Cyan
|
|
306
|
+
|
|
307
|
+
& {
|
|
308
|
+
$Silent = $true
|
|
309
|
+
$DryRun = $false
|
|
310
|
+
$SkipLocal = $false
|
|
311
|
+
$Locate = $false
|
|
312
|
+
$Source = ""
|
|
313
|
+
$Version = ""
|
|
314
|
+
$InstallDir = ""
|
|
315
|
+
$AddToPath = $true
|
|
316
|
+
|
|
317
|
+
. $sb
|
|
318
|
+
|
|
319
|
+
$testPlatformKey = if ($script:OSWin) { "win32-x64" } else { "linux-x64" }
|
|
320
|
+
$testExt = if ($testPlatformKey.StartsWith("win32")) { ".exe" } else { "" }
|
|
321
|
+
$testBinaryName = "browser4-cli-$testPlatformKey$testExt"
|
|
322
|
+
$testShortName = "b4$testExt"
|
|
323
|
+
|
|
324
|
+
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "b4-install-test-$(Get-Random)"
|
|
325
|
+
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
|
|
326
|
+
|
|
327
|
+
try {
|
|
328
|
+
# Create the dummy platform binary that New-PlatformLink needs
|
|
329
|
+
$dummyBinary = Join-Path $tempDir $testBinaryName
|
|
330
|
+
"dummy" | Out-File $dummyBinary
|
|
331
|
+
|
|
332
|
+
$testShortPath = Join-Path $tempDir $testShortName
|
|
333
|
+
$testCmdPath = Join-Path $tempDir "b4.cmd"
|
|
334
|
+
|
|
335
|
+
function Clear-B4Links {
|
|
336
|
+
if (Test-Path $testShortPath) { Remove-Item $testShortPath -Force -ErrorAction SilentlyContinue }
|
|
337
|
+
if (Test-Path $testCmdPath) { Remove-Item $testCmdPath -Force -ErrorAction SilentlyContinue }
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
# ── Scenario 1: b4 does not exist → should create ──
|
|
341
|
+
Clear-B4Links
|
|
342
|
+
|
|
343
|
+
New-Symlinks -BinaryName $testBinaryName -InstallDir $tempDir -PlatformKey $testPlatformKey
|
|
344
|
+
|
|
345
|
+
Test "New-Symlinks creates b4 when it does not exist" {
|
|
346
|
+
if (-not ((Test-Path $testShortPath) -or (Test-Path $testCmdPath))) {
|
|
347
|
+
throw "b4 link was not created (neither symlink nor .cmd wrapper found in $tempDir)"
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
# ── Scenario 2: b4 exists in install dir → should update ──
|
|
352
|
+
Clear-B4Links
|
|
353
|
+
|
|
354
|
+
# Create initial link
|
|
355
|
+
New-Symlinks -BinaryName $testBinaryName -InstallDir $tempDir -PlatformKey $testPlatformKey
|
|
356
|
+
|
|
357
|
+
$existingPath = if (Test-Path $testShortPath) { $testShortPath }
|
|
358
|
+
elseif (Test-Path $testCmdPath) { $testCmdPath }
|
|
359
|
+
else { $null }
|
|
360
|
+
|
|
361
|
+
if ($existingPath) {
|
|
362
|
+
$beforeTime = (Get-Item $existingPath).LastWriteTime
|
|
363
|
+
Start-Sleep -Milliseconds 200
|
|
364
|
+
|
|
365
|
+
New-Symlinks -BinaryName $testBinaryName -InstallDir $tempDir -PlatformKey $testPlatformKey
|
|
366
|
+
|
|
367
|
+
$afterPath = if (Test-Path $testShortPath) { $testShortPath }
|
|
368
|
+
elseif (Test-Path $testCmdPath) { $testCmdPath }
|
|
369
|
+
else { $null }
|
|
370
|
+
|
|
371
|
+
Test "New-Symlinks updates b4 when it already exists in install dir" {
|
|
372
|
+
if (-not $afterPath) {
|
|
373
|
+
throw "b4 link disappeared after update"
|
|
374
|
+
}
|
|
375
|
+
if ((Get-Item $afterPath).LastWriteTime -le $beforeTime) {
|
|
376
|
+
throw "b4 link was not updated (timestamp unchanged)"
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
Write-Host " SKIP New-Symlinks updates b4 (precondition: initial link creation failed)" -ForegroundColor Yellow
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
# ── Scenario 3: foreign b4 on PATH → should NOT create b4 in install dir ──
|
|
384
|
+
Clear-B4Links
|
|
385
|
+
|
|
386
|
+
$foreignDir = Join-Path ([System.IO.Path]::GetTempPath()) "b4-foreign-test-$(Get-Random)"
|
|
387
|
+
New-Item -ItemType Directory -Path $foreignDir -Force | Out-Null
|
|
388
|
+
try {
|
|
389
|
+
# Create fake b4 that is NOT browser4-cli
|
|
390
|
+
$foreignB4 = Join-Path $foreignDir $testShortName
|
|
391
|
+
if ($script:OSWin) {
|
|
392
|
+
# Use .cmd so Get-Command finds it via PATHEXT
|
|
393
|
+
$foreignCmd = Join-Path $foreignDir "b4.cmd"
|
|
394
|
+
"@echo off`r`necho NotBrowser4" | Out-File $foreignCmd -Encoding ASCII
|
|
395
|
+
} else {
|
|
396
|
+
"#!/bin/sh`necho NotBrowser4" | Out-File $foreignB4 -Encoding ASCII
|
|
397
|
+
& { chmod +x $foreignB4 2>$null } | Out-Null
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
$oldPath = $env:Path
|
|
401
|
+
$env:Path = "$foreignDir$([System.IO.Path]::PathSeparator)$oldPath"
|
|
402
|
+
try {
|
|
403
|
+
New-Symlinks -BinaryName $testBinaryName -InstallDir $tempDir -PlatformKey $testPlatformKey
|
|
404
|
+
|
|
405
|
+
Test "New-Symlinks skips b4 when foreign b4 is on PATH" {
|
|
406
|
+
if ((Test-Path $testShortPath) -or (Test-Path $testCmdPath)) {
|
|
407
|
+
throw "b4 link was created even though a non-browser4-cli b4 is on PATH"
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
} finally {
|
|
411
|
+
$env:Path = $oldPath
|
|
412
|
+
}
|
|
413
|
+
} finally {
|
|
414
|
+
Remove-Item $foreignDir -Recurse -Force -ErrorAction SilentlyContinue
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
# ── Scenario 4: b4 on PATH is browser4-cli → should create b4 link ──
|
|
418
|
+
Clear-B4Links
|
|
419
|
+
|
|
420
|
+
$oursDir = Join-Path ([System.IO.Path]::GetTempPath()) "b4-ours-test-$(Get-Random)"
|
|
421
|
+
New-Item -ItemType Directory -Path $oursDir -Force | Out-Null
|
|
422
|
+
try {
|
|
423
|
+
$oursB4 = Join-Path $oursDir $testShortName
|
|
424
|
+
if ($script:OSWin) {
|
|
425
|
+
$oursCmd = Join-Path $oursDir "b4.cmd"
|
|
426
|
+
"@echo off`r`necho browser4-cli v4.12.0" | Out-File $oursCmd -Encoding ASCII
|
|
427
|
+
} else {
|
|
428
|
+
"#!/bin/sh`necho browser4-cli v4.12.0" | Out-File $oursB4 -Encoding ASCII
|
|
429
|
+
& { chmod +x $oursB4 2>$null } | Out-Null
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
$oldPath = $env:Path
|
|
433
|
+
$env:Path = "$oursDir$([System.IO.Path]::PathSeparator)$oldPath"
|
|
434
|
+
try {
|
|
435
|
+
New-Symlinks -BinaryName $testBinaryName -InstallDir $tempDir -PlatformKey $testPlatformKey
|
|
436
|
+
|
|
437
|
+
Test "New-Symlinks creates b4 when b4 on PATH is browser4-cli" {
|
|
438
|
+
if (-not ((Test-Path $testShortPath) -or (Test-Path $testCmdPath))) {
|
|
439
|
+
throw "b4 link was not created even though PATH b4 is browser4-cli"
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
} finally {
|
|
443
|
+
$env:Path = $oldPath
|
|
444
|
+
}
|
|
445
|
+
} finally {
|
|
446
|
+
Remove-Item $oursDir -Recurse -Force -ErrorAction SilentlyContinue
|
|
447
|
+
}
|
|
448
|
+
} finally {
|
|
449
|
+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
Write-Host ""
|
|
454
|
+
|
|
304
455
|
# ── Summary ──
|
|
305
456
|
Write-Host "============================================" -ForegroundColor Cyan
|
|
306
457
|
$total = $pass + $fail
|
|
@@ -303,6 +303,141 @@ test "empty --version with dry-run doesn't download" bash -c "
|
|
|
303
303
|
|
|
304
304
|
echo ""
|
|
305
305
|
|
|
306
|
+
# ── create_symlinks (b4 link logic) ─────────────────────
|
|
307
|
+
|
|
308
|
+
echo "--- create_symlinks ---" | cyan
|
|
309
|
+
|
|
310
|
+
# Run symlink tests using the already-sourced FUNC_TEST_SCRIPT.
|
|
311
|
+
SYMLINK_TEST_SCRIPT="$(mktemp)"
|
|
312
|
+
SYMLINK_RESULT="$(mktemp)"
|
|
313
|
+
trap "rm -f '$FUNC_TEST_SCRIPT' '$SYMLINK_TEST_SCRIPT' '$SYMLINK_RESULT'" EXIT
|
|
314
|
+
|
|
315
|
+
cat > "$SYMLINK_TEST_SCRIPT" << 'SYMLINKEOF'
|
|
316
|
+
src="$1"
|
|
317
|
+
result_file="$2"
|
|
318
|
+
set -- # clear positional params so the sourced install script doesn't parse our args
|
|
319
|
+
source "$src" >/dev/null 2>&1
|
|
320
|
+
|
|
321
|
+
# Detect platform for test names
|
|
322
|
+
case "$(uname -s)" in
|
|
323
|
+
MINGW*|MSYS*|CYGWIN*) test_platform="win32-x64"; test_ext=".exe" ;;
|
|
324
|
+
*) test_platform="linux-x64"; test_ext="" ;;
|
|
325
|
+
esac
|
|
326
|
+
test_binary="browser4-cli-${test_platform}${test_ext}"
|
|
327
|
+
test_short="b4${test_ext}"
|
|
328
|
+
|
|
329
|
+
temp_dir="${TMPDIR:-/tmp}/b4-install-test-$$"
|
|
330
|
+
mkdir -p "$temp_dir"
|
|
331
|
+
cd "$temp_dir" || exit 1
|
|
332
|
+
|
|
333
|
+
pass=0
|
|
334
|
+
fail=0
|
|
335
|
+
|
|
336
|
+
do_test() {
|
|
337
|
+
local name="$1" fn="$2"
|
|
338
|
+
if bash -c "$fn" 2>/dev/null; then
|
|
339
|
+
pass=$((pass + 1))
|
|
340
|
+
printf '\033[0;32m PASS %s\033[0m\n' "$name"
|
|
341
|
+
else
|
|
342
|
+
fail=$((fail + 1))
|
|
343
|
+
printf '\033[0;31m FAIL %s\033[0m\n' "$name"
|
|
344
|
+
fi
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
# Create dummy platform binary (required by create_symlinks)
|
|
348
|
+
echo "dummy" > "$test_binary"
|
|
349
|
+
|
|
350
|
+
# ── Scenario 1: b4 does not exist → should create ──
|
|
351
|
+
rm -f "$test_short" "b4.cmd" 2>/dev/null || true
|
|
352
|
+
create_symlinks "$test_binary" "$temp_dir" >/dev/null 2>&1
|
|
353
|
+
|
|
354
|
+
do_test "create_symlinks creates b4 when it does not exist" \
|
|
355
|
+
"[[ -e '$test_short' || -L '$test_short' || -e '$temp_dir/b4.cmd' ]]"
|
|
356
|
+
|
|
357
|
+
# ── Scenario 2: b4 exists in install dir → should update ──
|
|
358
|
+
rm -f "$test_short" "$temp_dir/b4.cmd" 2>/dev/null || true
|
|
359
|
+
create_symlinks "$test_binary" "$temp_dir" >/dev/null 2>&1
|
|
360
|
+
|
|
361
|
+
existing=""
|
|
362
|
+
if [[ -e "$test_short" ]] || [[ -L "$test_short" ]]; then
|
|
363
|
+
existing="$test_short"
|
|
364
|
+
elif [[ -e "$temp_dir/b4.cmd" ]]; then
|
|
365
|
+
existing="$temp_dir/b4.cmd"
|
|
366
|
+
fi
|
|
367
|
+
|
|
368
|
+
if [[ -n "$existing" ]]; then
|
|
369
|
+
# Remove the link and verify create_symlinks recreates it
|
|
370
|
+
rm -f "$existing"
|
|
371
|
+
[[ ! -e "$test_short" ]] && [[ ! -L "$test_short" ]] && [[ ! -e "$temp_dir/b4.cmd" ]] || true
|
|
372
|
+
|
|
373
|
+
create_symlinks "$test_binary" "$temp_dir" >/dev/null 2>&1
|
|
374
|
+
|
|
375
|
+
after_path=""
|
|
376
|
+
if [[ -e "$test_short" ]] || [[ -L "$test_short" ]]; then
|
|
377
|
+
after_path="$test_short"
|
|
378
|
+
elif [[ -e "$temp_dir/b4.cmd" ]]; then
|
|
379
|
+
after_path="$temp_dir/b4.cmd"
|
|
380
|
+
fi
|
|
381
|
+
|
|
382
|
+
do_test "create_symlinks updates b4 when it already exists in install dir" \
|
|
383
|
+
"[[ -n '$after_path' ]]"
|
|
384
|
+
else
|
|
385
|
+
printf ' SKIP create_symlinks updates b4 (precondition: initial link creation failed)\n'
|
|
386
|
+
fi
|
|
387
|
+
|
|
388
|
+
# ── Scenario 3: foreign b4 on PATH → should NOT create b4 ──
|
|
389
|
+
rm -f "$test_short" "$temp_dir/b4.cmd" 2>/dev/null || true
|
|
390
|
+
|
|
391
|
+
foreign_dir="${TMPDIR:-/tmp}/b4-foreign-$$"
|
|
392
|
+
mkdir -p "$foreign_dir"
|
|
393
|
+
foreign_b4="${foreign_dir}/${test_short}"
|
|
394
|
+
printf '#!/bin/sh\necho NotBrowser4\n' > "$foreign_b4"
|
|
395
|
+
chmod +x "$foreign_b4" 2>/dev/null || true
|
|
396
|
+
|
|
397
|
+
OLD_PATH="$PATH"
|
|
398
|
+
PATH="${foreign_dir}:${PATH}"
|
|
399
|
+
create_symlinks "$test_binary" "$temp_dir" >/dev/null 2>&1
|
|
400
|
+
PATH="$OLD_PATH"
|
|
401
|
+
|
|
402
|
+
do_test "create_symlinks skips b4 when foreign b4 is on PATH" \
|
|
403
|
+
"! [[ -e '$test_short' || -L '$test_short' || -e '$temp_dir/b4.cmd' ]]"
|
|
404
|
+
|
|
405
|
+
rm -rf "$foreign_dir"
|
|
406
|
+
|
|
407
|
+
# ── Scenario 4: b4 on PATH is browser4-cli → should create b4 ──
|
|
408
|
+
rm -f "$test_short" "$temp_dir/b4.cmd" 2>/dev/null || true
|
|
409
|
+
|
|
410
|
+
ours_dir="${TMPDIR:-/tmp}/b4-ours-$$"
|
|
411
|
+
mkdir -p "$ours_dir"
|
|
412
|
+
ours_b4="${ours_dir}/${test_short}"
|
|
413
|
+
printf '#!/bin/sh\necho browser4-cli v4.12.0\n' > "$ours_b4"
|
|
414
|
+
chmod +x "$ours_b4" 2>/dev/null || true
|
|
415
|
+
|
|
416
|
+
OLD_PATH="$PATH"
|
|
417
|
+
PATH="${ours_dir}:${PATH}"
|
|
418
|
+
create_symlinks "$test_binary" "$temp_dir" >/dev/null 2>&1
|
|
419
|
+
PATH="$OLD_PATH"
|
|
420
|
+
|
|
421
|
+
do_test "create_symlinks creates b4 when b4 on PATH is browser4-cli" \
|
|
422
|
+
"[[ -e '$test_short' || -L '$test_short' || -e '$temp_dir/b4.cmd' ]]"
|
|
423
|
+
|
|
424
|
+
rm -rf "$ours_dir"
|
|
425
|
+
|
|
426
|
+
# Cleanup temp dir
|
|
427
|
+
rm -rf "$temp_dir"
|
|
428
|
+
|
|
429
|
+
# Write results to the result file for the parent to parse
|
|
430
|
+
printf '%d\n%d\n' "$pass" "$fail" > "$result_file"
|
|
431
|
+
SYMLINKEOF
|
|
432
|
+
|
|
433
|
+
bash "$SYMLINK_TEST_SCRIPT" "$FUNC_TEST_SCRIPT" "$SYMLINK_RESULT"
|
|
434
|
+
symlink_pass=$(head -1 "$SYMLINK_RESULT")
|
|
435
|
+
symlink_fail=$(tail -1 "$SYMLINK_RESULT")
|
|
436
|
+
PASS=$((PASS + ${symlink_pass:-0}))
|
|
437
|
+
FAIL=$((FAIL + ${symlink_fail:-0}))
|
|
438
|
+
|
|
439
|
+
echo ""
|
|
440
|
+
|
|
306
441
|
# ── Summary ────────────────────────────────────────────
|
|
307
442
|
|
|
308
443
|
cyan "============================================"
|