@geekbeer/minion 3.42.3 → 3.43.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/package.json +1 -1
- package/win/minion-cli.ps1 +123 -5
package/package.json
CHANGED
package/win/minion-cli.ps1
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# minion-cli-win setup # Install software & register services (admin required)
|
|
8
8
|
# minion-cli-win configure --hq-url https://... --minion-id <UUID> --api-token <TOKEN>
|
|
9
9
|
# minion-cli-win uninstall [--keep-data] # Remove agent (admin required)
|
|
10
|
-
# minion-cli-win start | stop | restart | status | health | daemons | diagnose | version | help
|
|
10
|
+
# minion-cli-win start | stop [--force] | restart | status | health | daemons | diagnose | version | help
|
|
11
11
|
|
|
12
12
|
# Parse arguments manually to avoid issues with npm wrapper passing $args as array
|
|
13
13
|
$Command = 'help'
|
|
@@ -16,6 +16,7 @@ $MinionId = ''
|
|
|
16
16
|
$ApiToken = ''
|
|
17
17
|
$SetupTunnel = $false
|
|
18
18
|
$KeepData = $false
|
|
19
|
+
$Force = $false
|
|
19
20
|
|
|
20
21
|
$i = 0
|
|
21
22
|
while ($i -lt $args.Count) {
|
|
@@ -28,6 +29,7 @@ while ($i -lt $args.Count) {
|
|
|
28
29
|
'^--api-token$' { $i++; if ($i -lt $args.Count) { $ApiToken = [string]$args[$i] } }
|
|
29
30
|
'^--setup-tunnel$' { $SetupTunnel = $true }
|
|
30
31
|
'^--keep-data$' { $KeepData = $true }
|
|
32
|
+
'^--force$' { $Force = $true }
|
|
31
33
|
'^(-h|--help)$' { $Command = 'help' }
|
|
32
34
|
}
|
|
33
35
|
$i++
|
|
@@ -40,10 +42,15 @@ $ErrorActionPreference = 'Stop'
|
|
|
40
42
|
# Require Administrator for service management commands
|
|
41
43
|
# ============================================================
|
|
42
44
|
$adminRequired = @('setup', 'uninstall')
|
|
43
|
-
|
|
45
|
+
$needsAdmin = $Command -in $adminRequired
|
|
46
|
+
# `stop --force` rewrites NSSM AppExit config and may need to kill processes
|
|
47
|
+
# owned by the service account → requires Administrator.
|
|
48
|
+
if ($Command -eq 'stop' -and $Force) { $needsAdmin = $true }
|
|
49
|
+
if ($needsAdmin) {
|
|
44
50
|
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
|
|
45
51
|
if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
|
46
|
-
|
|
52
|
+
$label = if ($Command -eq 'stop' -and $Force) { 'stop --force' } else { $Command }
|
|
53
|
+
Write-Host "ERROR: '$label' requires Administrator privileges." -ForegroundColor Red
|
|
47
54
|
Write-Host " Right-click PowerShell and select 'Run as administrator'." -ForegroundColor Yellow
|
|
48
55
|
exit 1
|
|
49
56
|
}
|
|
@@ -440,6 +447,108 @@ function Stop-MinionService {
|
|
|
440
447
|
Write-Host "minion-agent service stopped"
|
|
441
448
|
}
|
|
442
449
|
|
|
450
|
+
function Stop-MinionServiceForce {
|
|
451
|
+
# Force-stop path used when `stop` (graceful) is broken — typically after a
|
|
452
|
+
# corrupted update where .env is unreadable, the agent API is unresponsive,
|
|
453
|
+
# or NSSM keeps respawning node.exe and locking package files.
|
|
454
|
+
#
|
|
455
|
+
# Steps:
|
|
456
|
+
# 1. Disable NSSM auto-restart on every minion service (so sc.exe stop sticks)
|
|
457
|
+
# 2. sc.exe stop each service
|
|
458
|
+
# 3. Kill remaining helper processes (tvnserver/websockify/cloudflared)
|
|
459
|
+
# 4. Kill any node.exe whose CommandLine references a minion script
|
|
460
|
+
# 5. Restore NSSM AppExit Restart so subsequent `start` behaves normally
|
|
461
|
+
Write-Host ""
|
|
462
|
+
Write-Host "=========================================" -ForegroundColor Yellow
|
|
463
|
+
Write-Host " Stop --force: bypassing graceful shutdown" -ForegroundColor Yellow
|
|
464
|
+
Write-Host "=========================================" -ForegroundColor Yellow
|
|
465
|
+
Write-Host ""
|
|
466
|
+
|
|
467
|
+
$services = @('minion-agent', 'minion-websockify', 'minion-cloudflared', 'minion-vnc')
|
|
468
|
+
$nssmAvailable = $NssmPath -and (Test-Path $NssmPath)
|
|
469
|
+
|
|
470
|
+
# Step 1: Disable NSSM auto-restart so sc.exe stop is not undone.
|
|
471
|
+
if ($nssmAvailable) {
|
|
472
|
+
foreach ($svc in $services) {
|
|
473
|
+
$state = Get-ServiceState $svc
|
|
474
|
+
if (-not $state) { continue }
|
|
475
|
+
Invoke-Nssm set $svc AppExit Default Exit | Out-Null
|
|
476
|
+
Invoke-Nssm set $svc AppThrottle 0 | Out-Null
|
|
477
|
+
Write-Detail "Disabled auto-restart for $svc"
|
|
478
|
+
}
|
|
479
|
+
} else {
|
|
480
|
+
Write-Warn "NSSM not found — skipping AppExit override (services may auto-restart)"
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
# Step 2: Stop each service via sc.exe (works without admin if SDDL granted).
|
|
484
|
+
foreach ($svc in $services) {
|
|
485
|
+
$state = Get-ServiceState $svc
|
|
486
|
+
if (-not $state) { continue }
|
|
487
|
+
if ($state -eq 'STOPPED') {
|
|
488
|
+
Write-Detail "$svc already stopped"
|
|
489
|
+
continue
|
|
490
|
+
}
|
|
491
|
+
sc.exe stop $svc 2>&1 | Out-Null
|
|
492
|
+
for ($i = 0; $i -lt 10; $i++) {
|
|
493
|
+
$s = Get-ServiceState $svc
|
|
494
|
+
if ($s -eq 'STOPPED') { break }
|
|
495
|
+
Start-Sleep -Seconds 1
|
|
496
|
+
}
|
|
497
|
+
$final = Get-ServiceState $svc
|
|
498
|
+
if ($final -eq 'STOPPED') {
|
|
499
|
+
Write-Detail "$svc stopped"
|
|
500
|
+
} else {
|
|
501
|
+
Write-Warn "$svc state=$final (sc.exe stop did not complete in 10s)"
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
# Step 3: Kill helper processes that may have been spawned outside NSSM.
|
|
506
|
+
foreach ($name in @('tvnserver', 'websockify', 'cloudflared')) {
|
|
507
|
+
$procs = Get-Process -Name $name -ErrorAction SilentlyContinue
|
|
508
|
+
if ($procs) {
|
|
509
|
+
$procs | Stop-Process -Force -ErrorAction SilentlyContinue
|
|
510
|
+
Write-Detail "Killed $name ($($procs.Count) process(es))"
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
# Step 4: Kill node.exe processes that hold the package files. We match on
|
|
515
|
+
# CommandLine so we don't touch unrelated node processes the user is running.
|
|
516
|
+
$nodePattern = 'minion|@geekbeer\\minion|terminal-server|workflow-runner|routine-runner|wsl-session-server'
|
|
517
|
+
try {
|
|
518
|
+
$nodeProcs = Get-CimInstance Win32_Process -Filter "Name='node.exe'" -ErrorAction Stop |
|
|
519
|
+
Where-Object { $_.CommandLine -match $nodePattern }
|
|
520
|
+
foreach ($p in $nodeProcs) {
|
|
521
|
+
try {
|
|
522
|
+
Stop-Process -Id $p.ProcessId -Force -ErrorAction Stop
|
|
523
|
+
Write-Detail "Killed node.exe pid=$($p.ProcessId)"
|
|
524
|
+
} catch {
|
|
525
|
+
Write-Warn "Failed to kill node.exe pid=$($p.ProcessId): $($_.Exception.Message)"
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
if (-not $nodeProcs) {
|
|
529
|
+
Write-Detail "No matching node.exe processes"
|
|
530
|
+
}
|
|
531
|
+
} catch {
|
|
532
|
+
Write-Warn "Could not enumerate node processes: $($_.Exception.Message)"
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
# Step 5: Restore AppExit Restart so a subsequent `start` behaves normally.
|
|
536
|
+
if ($nssmAvailable) {
|
|
537
|
+
foreach ($svc in $services) {
|
|
538
|
+
$state = Get-ServiceState $svc
|
|
539
|
+
if (-not $state) { continue }
|
|
540
|
+
Invoke-Nssm set $svc AppExit Default Restart | Out-Null
|
|
541
|
+
Invoke-Nssm set $svc AppThrottle 1500 | Out-Null
|
|
542
|
+
}
|
|
543
|
+
Write-Detail "Restored NSSM auto-restart settings"
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
Write-Host ""
|
|
547
|
+
Write-Host "Force-stop complete. If files are still locked, run:" -ForegroundColor Cyan
|
|
548
|
+
Write-Host " Get-Process | Where-Object { `$_.Modules.FileName -like '*\@geekbeer\minion\*' } | Select Id,ProcessName" -ForegroundColor Gray
|
|
549
|
+
Write-Host ""
|
|
550
|
+
}
|
|
551
|
+
|
|
443
552
|
function Restart-MinionService {
|
|
444
553
|
Stop-MinionService
|
|
445
554
|
Start-Sleep -Seconds 2
|
|
@@ -1673,7 +1782,9 @@ switch ($Command) {
|
|
|
1673
1782
|
'reconfigure' { Invoke-Configure } # alias for backwards compatibility
|
|
1674
1783
|
'uninstall' { Invoke-Uninstall }
|
|
1675
1784
|
'start' { Start-MinionService }
|
|
1676
|
-
'stop' {
|
|
1785
|
+
'stop' {
|
|
1786
|
+
if ($Force) { Stop-MinionServiceForce } else { Stop-MinionService }
|
|
1787
|
+
}
|
|
1677
1788
|
'restart' { Restart-MinionService }
|
|
1678
1789
|
'status' { Show-Status }
|
|
1679
1790
|
'health' { Show-Health }
|
|
@@ -1692,7 +1803,8 @@ switch ($Command) {
|
|
|
1692
1803
|
Write-Host "Commands (no admin required):"
|
|
1693
1804
|
Write-Host " configure Connect to HQ, deploy skills, start services"
|
|
1694
1805
|
Write-Host " start Start the minion-agent service"
|
|
1695
|
-
Write-Host " stop Stop the minion-agent service"
|
|
1806
|
+
Write-Host " stop Stop the minion-agent service (graceful)"
|
|
1807
|
+
Write-Host " stop --force Force-stop all minion services & processes (admin required)"
|
|
1696
1808
|
Write-Host " restart Restart the minion-agent service"
|
|
1697
1809
|
Write-Host " status Show agent service status"
|
|
1698
1810
|
Write-Host " health Check agent health endpoint"
|
|
@@ -1709,5 +1821,11 @@ switch ($Command) {
|
|
|
1709
1821
|
Write-Host ""
|
|
1710
1822
|
Write-Host "Uninstall options:"
|
|
1711
1823
|
Write-Host " --keep-data Preserve .env file"
|
|
1824
|
+
Write-Host ""
|
|
1825
|
+
Write-Host "Stop options:"
|
|
1826
|
+
Write-Host " --force Disable NSSM auto-restart, sc.exe stop all services,"
|
|
1827
|
+
Write-Host " kill remaining helpers (tvnserver/websockify/cloudflared)"
|
|
1828
|
+
Write-Host " and node.exe processes that lock package files."
|
|
1829
|
+
Write-Host " Use when graceful stop fails (e.g. corrupted update)."
|
|
1712
1830
|
}
|
|
1713
1831
|
}
|