nox-openclaw-hunter 1.0.1 → 1.0.3

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.
Files changed (41) hide show
  1. package/README.md +170 -10
  2. package/dist/commands/export.d.ts.map +1 -1
  3. package/dist/commands/export.js +529 -46
  4. package/dist/commands/export.js.map +1 -1
  5. package/dist/detector/cli-binary.d.ts.map +1 -1
  6. package/dist/detector/cli-binary.js +19 -0
  7. package/dist/detector/cli-binary.js.map +1 -1
  8. package/dist/detector/config.d.ts.map +1 -1
  9. package/dist/detector/config.js +135 -3
  10. package/dist/detector/config.js.map +1 -1
  11. package/dist/detector/process.d.ts.map +1 -1
  12. package/dist/detector/process.js +12 -1
  13. package/dist/detector/process.js.map +1 -1
  14. package/dist/enforcer/file-remover.d.ts +9 -0
  15. package/dist/enforcer/file-remover.d.ts.map +1 -1
  16. package/dist/enforcer/file-remover.js +76 -0
  17. package/dist/enforcer/file-remover.js.map +1 -1
  18. package/dist/enforcer/index.d.ts.map +1 -1
  19. package/dist/enforcer/index.js +19 -8
  20. package/dist/enforcer/index.js.map +1 -1
  21. package/dist/enforcer/service-stopper.d.ts +1 -0
  22. package/dist/enforcer/service-stopper.d.ts.map +1 -1
  23. package/dist/enforcer/service-stopper.js +7 -6
  24. package/dist/enforcer/service-stopper.js.map +1 -1
  25. package/dist/mdm/templates/detect.ps1.d.ts.map +1 -1
  26. package/dist/mdm/templates/detect.ps1.js +69 -38
  27. package/dist/mdm/templates/detect.ps1.js.map +1 -1
  28. package/dist/mdm/templates/enforce.ps1.d.ts.map +1 -1
  29. package/dist/mdm/templates/enforce.ps1.js +38 -25
  30. package/dist/mdm/templates/enforce.ps1.js.map +1 -1
  31. package/dist/platform/darwin.d.ts.map +1 -1
  32. package/dist/platform/darwin.js +117 -25
  33. package/dist/platform/darwin.js.map +1 -1
  34. package/dist/platform/index.d.ts +4 -0
  35. package/dist/platform/index.d.ts.map +1 -1
  36. package/dist/platform/index.js.map +1 -1
  37. package/dist/platform/windows.d.ts.map +1 -1
  38. package/dist/platform/windows.js +101 -0
  39. package/dist/platform/windows.js.map +1 -1
  40. package/docs/jumpcloud.jpeg +0 -0
  41. package/package.json +1 -1
@@ -432,26 +432,30 @@ $ErrorActionPreference = "SilentlyContinue"
432
432
  $OpenClawFound = $false
433
433
  $Details = @()
434
434
  ${webhookSection}
435
+ # All known variants: openclaw, moltbot, clawdbot, clawbot
436
+ $Variants = @("openclaw", "moltbot", "clawdbot", "clawbot")
437
+
435
438
  # Check common installation paths
436
- $CliPaths = @(
437
- "$env:LOCALAPPDATA\\Programs\\openclaw\\openclaw.exe",
438
- "$env:ProgramFiles\\OpenClaw\\openclaw.exe",
439
- "$env:ProgramFiles(x86)\\OpenClaw\\openclaw.exe"
440
- )
439
+ $CliPaths = @()
440
+ foreach ($variant in $Variants) {
441
+ $CliPaths += "$env:LOCALAPPDATA\\Programs\\$variant\\$variant.exe"
442
+ $CliPaths += "$env:ProgramFiles\\$variant\\$variant.exe"
443
+ $CliPaths += "$env:ProgramFiles(x86)\\$variant\\$variant.exe"
444
+ }
441
445
 
442
446
  foreach ($path in $CliPaths) {
443
447
  if (Test-Path $path) {
444
448
  $OpenClawFound = $true
445
449
  $Details += "CLI found at $path"
446
- break
447
450
  }
448
451
  }
449
452
 
450
- # Check config directory
451
- $ConfigPaths = @(
452
- "$env:USERPROFILE\\.openclaw",
453
- "$env:APPDATA\\OpenClaw"
454
- )
453
+ # Check config directories (all variants)
454
+ $ConfigPaths = @()
455
+ foreach ($variant in $Variants) {
456
+ $ConfigPaths += "$env:USERPROFILE\\.$variant"
457
+ $ConfigPaths += "$env:APPDATA\\$variant"
458
+ }
455
459
 
456
460
  foreach ($path in $ConfigPaths) {
457
461
  if (Test-Path $path) {
@@ -460,11 +464,13 @@ foreach ($path in $ConfigPaths) {
460
464
  }
461
465
  }
462
466
 
463
- # Check running processes
464
- $processes = Get-Process -Name "*openclaw*" -ErrorAction SilentlyContinue
465
- if ($processes) {
466
- $OpenClawFound = $true
467
- $Details += "Process running: $($processes.Name -join ', ')"
467
+ # Check running processes (all variants)
468
+ foreach ($variant in $Variants) {
469
+ $processes = Get-Process -Name "*$variant*" -ErrorAction SilentlyContinue
470
+ if ($processes) {
471
+ $OpenClawFound = $true
472
+ $Details += "Process running: $($processes.Name -join ', ')"
473
+ }
468
474
  }
469
475
 
470
476
  # Check gateway port
@@ -474,11 +480,163 @@ if ($portCheck.TcpTestSucceeded) {
474
480
  $Details += "Gateway port 18789 listening"
475
481
  }
476
482
 
477
- # Check Windows service
478
- $service = Get-Service -Name "*openclaw*" -ErrorAction SilentlyContinue
479
- if ($service) {
480
- $OpenClawFound = $true
481
- $Details += "Service found: $($service.Name) ($($service.Status))"
483
+ # Check Windows services (all variants)
484
+ foreach ($variant in $Variants) {
485
+ $service = Get-Service -Name "*$variant*" -ErrorAction SilentlyContinue
486
+ if ($service) {
487
+ $OpenClawFound = $true
488
+ $Details += "Service found: $($service.Name) ($($service.Status))"
489
+ }
490
+ }
491
+
492
+ # Check WSL2 installations
493
+ function Get-WslDistros {
494
+ $distroList = @()
495
+
496
+ # Method 1: Registry Lookup (Most Reliable)
497
+ $lxssRoot = "HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Lxss"
498
+ if (Test-Path $lxssRoot) {
499
+ Get-ChildItem -Path $lxssRoot -ErrorAction SilentlyContinue | ForEach-Object {
500
+ $distroName = (Get-ItemProperty -Path $_.PSPath -Name DistributionName -ErrorAction SilentlyContinue).DistributionName
501
+ if ($distroName) {
502
+ $distroList += $distroName
503
+ }
504
+ }
505
+ }
506
+
507
+ # Method 2: Fallback to wsl.exe CLI
508
+ if ($distroList.Count -eq 0) {
509
+ try {
510
+ $consoleEnc = [Console]::OutputEncoding
511
+ [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
512
+ $output = wsl --list --quiet 2>$null
513
+ [Console]::OutputEncoding = $consoleEnc
514
+
515
+ if ($output) {
516
+ $cleaned = $output -replace '\\x00', ''
517
+ $lines = $cleaned -split '[\\r\\n]+' | Where-Object { $_.Trim() -and $_ -notmatch 'Windows Subsystem' }
518
+ foreach ($line in $lines) {
519
+ $d = $line.Trim()
520
+ if ($d) { $distroList += $d }
521
+ }
522
+ }
523
+ } catch {}
524
+ }
525
+
526
+ return $distroList | Select-Object -Unique
527
+ }
528
+
529
+ function Get-Wsl2UserHomes {
530
+ param([string]$Distro)
531
+ $homes = @()
532
+
533
+ $basePaths = @(
534
+ "\\\\wsl.localhost\\$Distro",
535
+ "\\\\wsl\$\\$Distro"
536
+ )
537
+
538
+ foreach ($base in $basePaths) {
539
+ $homePath = "$base\\home"
540
+ if (Test-Path $homePath) {
541
+ Get-ChildItem -Path $homePath -Directory -ErrorAction SilentlyContinue | ForEach-Object {
542
+ $homes += $_.FullName
543
+ }
544
+ $rootPath = "$base\\root"
545
+ if (Test-Path $rootPath) {
546
+ $homes += $rootPath
547
+ }
548
+ break
549
+ }
550
+ }
551
+ return $homes
552
+ }
553
+
554
+ # Scan all WSL2 distros
555
+ $WslDistros = Get-WslDistros
556
+ foreach ($distro in $WslDistros) {
557
+ $userHomes = Get-Wsl2UserHomes -Distro $distro
558
+
559
+ foreach ($home in $userHomes) {
560
+ # Check config directories (.openclaw, .clawdbot, etc.)
561
+ foreach ($variant in $Variants) {
562
+ $configDir = Join-Path $home ".$variant"
563
+ if (Test-Path $configDir) {
564
+ $OpenClawFound = $true
565
+ $Details += "WSL2 [$distro] config dir: $configDir"
566
+ }
567
+ }
568
+
569
+ # Check CLI binaries
570
+ foreach ($variant in $Variants) {
571
+ $binPaths = @(
572
+ (Join-Path $home ".local\\bin\\$variant"),
573
+ (Join-Path $home "bin\\$variant")
574
+ )
575
+ foreach ($binPath in $binPaths) {
576
+ if (Test-Path $binPath) {
577
+ $OpenClawFound = $true
578
+ $Details += "WSL2 [$distro] binary: $binPath"
579
+ }
580
+ }
581
+ }
582
+
583
+ # Check nvm node_modules
584
+ $nvmDir = Join-Path $home ".nvm\\versions\\node"
585
+ if (Test-Path $nvmDir) {
586
+ Get-ChildItem -Path $nvmDir -Directory -ErrorAction SilentlyContinue | ForEach-Object {
587
+ foreach ($variant in $Variants) {
588
+ $npmPkg = Join-Path $_.FullName "lib\\node_modules\\$variant"
589
+ if (Test-Path $npmPkg) {
590
+ $OpenClawFound = $true
591
+ $Details += "WSL2 [$distro] npm (nvm): $npmPkg"
592
+ }
593
+ }
594
+ }
595
+ }
596
+
597
+ # Check all npm/yarn/pnpm package locations
598
+ foreach ($variant in $Variants) {
599
+ $npmPaths = @(
600
+ (Join-Path $home ".local\\lib\\node_modules\\$variant"),
601
+ (Join-Path $home ".npm-global\\lib\\node_modules\\$variant"),
602
+ (Join-Path $home ".npm-packages\\lib\\node_modules\\$variant"),
603
+ (Join-Path $home ".npm\\node_modules\\$variant"),
604
+ (Join-Path $home "npm\\lib\\node_modules\\$variant"),
605
+ (Join-Path $home ".node_modules\\$variant"),
606
+ (Join-Path $home "node_modules\\$variant"),
607
+ (Join-Path $home ".yarn\\global\\node_modules\\$variant"),
608
+ (Join-Path $home ".config\\yarn\\global\\node_modules\\$variant"),
609
+ (Join-Path $home ".pnpm-global\\node_modules\\$variant")
610
+ )
611
+
612
+ foreach ($npmPath in $npmPaths) {
613
+ if (Test-Path $npmPath) {
614
+ $OpenClawFound = $true
615
+ $Details += "WSL2 [$distro] package: $npmPath"
616
+ }
617
+ }
618
+ }
619
+ }
620
+
621
+ # Check system-wide paths for this distro
622
+ $sysBase = "\\\\wsl.localhost\\$distro"
623
+ if (-not (Test-Path $sysBase)) { $sysBase = "\\\\wsl\$\\$distro" }
624
+
625
+ foreach ($variant in $Variants) {
626
+ $sysPaths = @(
627
+ "$sysBase\\usr\\local\\bin\\$variant",
628
+ "$sysBase\\usr\\bin\\$variant",
629
+ "$sysBase\\opt\\$variant\\$variant",
630
+ "$sysBase\\usr\\local\\lib\\node_modules\\$variant",
631
+ "$sysBase\\usr\\lib\\node_modules\\$variant"
632
+ )
633
+ foreach ($sysPath in $sysPaths) {
634
+ if (Test-Path $sysPath) {
635
+ $OpenClawFound = $true
636
+ $Details += "WSL2 [$distro] system: $sysPath"
637
+ }
638
+ }
639
+ }
482
640
  }
483
641
 
484
642
  $DetailsString = $Details -join "; "
@@ -969,6 +1127,66 @@ if command -v docker &>/dev/null; then
969
1127
  log_action "Cleaned Docker artifacts"
970
1128
  fi
971
1129
 
1130
+ # Remove npm global packages (all variants)
1131
+ log_action "Removing npm global packages..."
1132
+ NPM_REMOVED=0
1133
+ NPM_FAILED=0
1134
+
1135
+ # Get npm global directory
1136
+ if command -v npm &>/dev/null; then
1137
+ NPM_ROOT=$(npm root -g 2>/dev/null || echo "")
1138
+ if [[ -n "$NPM_ROOT" && -d "$NPM_ROOT" ]]; then
1139
+ for pkg_name in openclaw clawbot clawdbot moltbot; do
1140
+ pkg_path="$NPM_ROOT/$pkg_name"
1141
+ if [[ -d "$pkg_path" ]]; then
1142
+ rm -rf "$pkg_path" 2>/dev/null
1143
+ if [[ -d "$pkg_path" ]]; then
1144
+ log_action " FAILED to remove npm package: $pkg_path"
1145
+ NPM_FAILED=$((NPM_FAILED + 1))
1146
+ else
1147
+ log_action " Removed npm package: $pkg_path"
1148
+ NPM_REMOVED=$((NPM_REMOVED + 1))
1149
+ fi
1150
+ fi
1151
+ done
1152
+ fi
1153
+ fi
1154
+
1155
+ # Also check nvm directories for all users
1156
+ for user_home in $(get_all_user_homes); do
1157
+ nvm_dir="$user_home/.nvm/versions/node"
1158
+ if [[ -d "$nvm_dir" ]]; then
1159
+ for node_version in "$nvm_dir"/*; do
1160
+ if [[ -d "$node_version/lib/node_modules" ]]; then
1161
+ for pkg_name in openclaw clawbot clawdbot moltbot; do
1162
+ pkg_path="$node_version/lib/node_modules/$pkg_name"
1163
+ if [[ -d "$pkg_path" ]]; then
1164
+ rm -rf "$pkg_path" 2>/dev/null
1165
+ # Also remove the symlinked binary
1166
+ bin_path="$node_version/bin/$pkg_name"
1167
+ rm -f "$bin_path" 2>/dev/null
1168
+ if [[ -d "$pkg_path" ]]; then
1169
+ log_action " FAILED to remove npm package: $pkg_path"
1170
+ NPM_FAILED=$((NPM_FAILED + 1))
1171
+ else
1172
+ log_action " Removed npm package: $pkg_path"
1173
+ NPM_REMOVED=$((NPM_REMOVED + 1))
1174
+ fi
1175
+ fi
1176
+ done
1177
+ fi
1178
+ done
1179
+ fi
1180
+ done
1181
+
1182
+ if [[ $NPM_FAILED -gt 0 ]]; then
1183
+ log_action "npm removal: $NPM_REMOVED removed, $NPM_FAILED failed"
1184
+ elif [[ $NPM_REMOVED -gt 0 ]]; then
1185
+ log_action "npm removal completed ($NPM_REMOVED packages removed)"
1186
+ else
1187
+ log_action "npm removal completed (no npm packages found)"
1188
+ fi
1189
+
972
1190
  # FINAL CLEANUP PASS - catch any respawned processes and recreated files
973
1191
  log_action "Running final cleanup pass..."
974
1192
  sleep 2
@@ -1089,31 +1307,39 @@ function Send-Webhook {
1089
1307
  $ErrorActionPreference = "SilentlyContinue"
1090
1308
  $PurgeResults = @()
1091
1309
  ${webhookSection}
1310
+ # All known variants: openclaw, moltbot, clawdbot, clawbot
1311
+ $Variants = @("openclaw", "moltbot", "clawdbot", "clawbot")
1312
+
1092
1313
  function Log-Action {
1093
1314
  param([string]$Message)
1094
1315
  Write-Host "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $Message"
1095
1316
  $script:PurgeResults += $Message
1096
1317
  }
1097
1318
 
1098
- # Kill running processes
1099
- Log-Action "Killing OpenClaw processes..."
1100
- Get-Process -Name "*openclaw*" | Stop-Process -Force -ErrorAction SilentlyContinue
1319
+ # Kill running processes (all variants)
1320
+ Log-Action "Killing processes (all variants)..."
1321
+ foreach ($variant in $Variants) {
1322
+ Get-Process -Name "*$variant*" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
1323
+ }
1101
1324
  Log-Action "Processes terminated"
1102
1325
 
1103
- # Stop and remove Windows service
1104
- $service = Get-Service -Name "*openclaw*" -ErrorAction SilentlyContinue
1105
- if ($service) {
1106
- Stop-Service -Name $service.Name -Force -ErrorAction SilentlyContinue
1107
- sc.exe delete $service.Name 2>$null
1108
- Log-Action "Stopped and removed service: $($service.Name)"
1326
+ # Stop and remove Windows services (all variants)
1327
+ foreach ($variant in $Variants) {
1328
+ $services = Get-Service -Name "*$variant*" -ErrorAction SilentlyContinue
1329
+ foreach ($service in $services) {
1330
+ Stop-Service -Name $service.Name -Force -ErrorAction SilentlyContinue
1331
+ sc.exe delete $service.Name 2>$null
1332
+ Log-Action "Stopped and removed service: $($service.Name)"
1333
+ }
1109
1334
  }
1110
1335
 
1111
- # Remove CLI binaries
1112
- $CliPaths = @(
1113
- "$env:LOCALAPPDATA\\Programs\\openclaw",
1114
- "$env:ProgramFiles\\OpenClaw",
1115
- "$env:ProgramFiles(x86)\\OpenClaw"
1116
- )
1336
+ # Remove CLI binaries (all variants)
1337
+ $CliPaths = @()
1338
+ foreach ($variant in $Variants) {
1339
+ $CliPaths += "$env:LOCALAPPDATA\\Programs\\$variant"
1340
+ $CliPaths += "$env:ProgramFiles\\$variant"
1341
+ $CliPaths += "$env:ProgramFiles(x86)\\$variant"
1342
+ }
1117
1343
 
1118
1344
  foreach ($path in $CliPaths) {
1119
1345
  if (Test-Path $path) {
@@ -1122,11 +1348,12 @@ foreach ($path in $CliPaths) {
1122
1348
  }
1123
1349
  }
1124
1350
 
1125
- # Remove config directories
1126
- $ConfigPaths = @(
1127
- "$env:USERPROFILE\\.openclaw",
1128
- "$env:APPDATA\\OpenClaw"
1129
- )
1351
+ # Remove config directories (all variants)
1352
+ $ConfigPaths = @()
1353
+ foreach ($variant in $Variants) {
1354
+ $ConfigPaths += "$env:USERPROFILE\\.$variant"
1355
+ $ConfigPaths += "$env:APPDATA\\$variant"
1356
+ }
1130
1357
 
1131
1358
  foreach ($path in $ConfigPaths) {
1132
1359
  if (Test-Path $path) {
@@ -1135,17 +1362,273 @@ foreach ($path in $ConfigPaths) {
1135
1362
  }
1136
1363
  }
1137
1364
 
1138
- # Remove scheduled tasks
1139
- Get-ScheduledTask -TaskName "*openclaw*" | Unregister-ScheduledTask -Confirm:$false -ErrorAction SilentlyContinue
1365
+ # Remove npm global packages (all variants)
1366
+ Log-Action "Removing npm global packages..."
1367
+ if (Get-Command npm -ErrorAction SilentlyContinue) {
1368
+ $npmRoot = (npm root -g 2>$null)
1369
+ if ($npmRoot -and (Test-Path $npmRoot)) {
1370
+ foreach ($variant in $Variants) {
1371
+ $pkgPath = Join-Path $npmRoot $variant
1372
+ if (Test-Path $pkgPath) {
1373
+ Remove-Item -Path $pkgPath -Recurse -Force -ErrorAction SilentlyContinue
1374
+ Log-Action "Removed npm package: $pkgPath"
1375
+ }
1376
+ }
1377
+ }
1378
+ }
1379
+
1380
+ # Also check nvm directories for all users (Windows nvm-windows)
1381
+ $userProfiles = Get-ChildItem "C:\\Users" -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -notin @("Public", "Default", "Default User", "All Users") }
1382
+ foreach ($profile in $userProfiles) {
1383
+ $nvmDir = Join-Path $profile.FullName ".nvm"
1384
+ if (Test-Path $nvmDir) {
1385
+ Get-ChildItem $nvmDir -Directory -ErrorAction SilentlyContinue | ForEach-Object {
1386
+ $nodeModules = Join-Path $_.FullName "node_modules"
1387
+ if (Test-Path $nodeModules) {
1388
+ foreach ($variant in $Variants) {
1389
+ $pkgPath = Join-Path $nodeModules $variant
1390
+ if (Test-Path $pkgPath) {
1391
+ Remove-Item -Path $pkgPath -Recurse -Force -ErrorAction SilentlyContinue
1392
+ Log-Action "Removed npm package: $pkgPath"
1393
+ }
1394
+ }
1395
+ }
1396
+ }
1397
+ }
1398
+ # Also check AppData for nvm-windows
1399
+ $nvmWinDir = Join-Path $profile.FullName "AppData\\Roaming\\nvm"
1400
+ if (Test-Path $nvmWinDir) {
1401
+ Get-ChildItem $nvmWinDir -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -match "^v\\d" } | ForEach-Object {
1402
+ $nodeModules = Join-Path $_.FullName "node_modules"
1403
+ if (Test-Path $nodeModules) {
1404
+ foreach ($variant in $Variants) {
1405
+ $pkgPath = Join-Path $nodeModules $variant
1406
+ if (Test-Path $pkgPath) {
1407
+ Remove-Item -Path $pkgPath -Recurse -Force -ErrorAction SilentlyContinue
1408
+ Log-Action "Removed npm package: $pkgPath"
1409
+ }
1410
+ }
1411
+ }
1412
+ }
1413
+ }
1414
+ }
1415
+ Log-Action "npm package removal completed"
1416
+
1417
+ # Remove scheduled tasks (all variants)
1418
+ foreach ($variant in $Variants) {
1419
+ Get-ScheduledTask -TaskName "*$variant*" -ErrorAction SilentlyContinue | Unregister-ScheduledTask -Confirm:$false -ErrorAction SilentlyContinue
1420
+ }
1140
1421
  Log-Action "Removed scheduled tasks"
1141
1422
 
1142
- # Clean Docker (if installed)
1423
+ # Clean Docker (if installed) - all variants
1143
1424
  if (Get-Command docker -ErrorAction SilentlyContinue) {
1144
- docker ps -a --filter "name=openclaw" --format "{{.ID}}" | ForEach-Object { docker rm -f $_ 2>$null }
1145
- docker images --filter "reference=*openclaw*" --format "{{.ID}}" | ForEach-Object { docker rmi -f $_ 2>$null }
1425
+ foreach ($variant in $Variants) {
1426
+ docker ps -a --filter "name=$variant" --format "{{.ID}}" 2>$null | ForEach-Object { docker rm -f $_ 2>$null }
1427
+ docker images --filter "reference=*$variant*" --format "{{.ID}}" 2>$null | ForEach-Object { docker rmi -f $_ 2>$null }
1428
+ }
1146
1429
  Log-Action "Cleaned Docker artifacts"
1147
1430
  }
1148
1431
 
1432
+ # Remove WSL2 installations
1433
+ function Get-WslDistros {
1434
+ $distroList = @()
1435
+
1436
+ # Method 1: Registry Lookup (Most Reliable)
1437
+ $lxssRoot = "HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Lxss"
1438
+ if (Test-Path $lxssRoot) {
1439
+ Get-ChildItem -Path $lxssRoot -ErrorAction SilentlyContinue | ForEach-Object {
1440
+ $distroName = (Get-ItemProperty -Path $_.PSPath -Name DistributionName -ErrorAction SilentlyContinue).DistributionName
1441
+ if ($distroName) {
1442
+ $distroList += $distroName
1443
+ }
1444
+ }
1445
+ }
1446
+
1447
+ # Method 2: Fallback to wsl.exe CLI
1448
+ if ($distroList.Count -eq 0) {
1449
+ try {
1450
+ $consoleEnc = [Console]::OutputEncoding
1451
+ [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
1452
+ $output = wsl --list --quiet 2>$null
1453
+ [Console]::OutputEncoding = $consoleEnc
1454
+
1455
+ if ($output) {
1456
+ $cleaned = $output -replace '\\x00', ''
1457
+ $lines = $cleaned -split '[\\r\\n]+' | Where-Object { $_.Trim() -and $_ -notmatch 'Windows Subsystem' }
1458
+ foreach ($line in $lines) {
1459
+ $d = $line.Trim()
1460
+ if ($d) { $distroList += $d }
1461
+ }
1462
+ }
1463
+ } catch {}
1464
+ }
1465
+
1466
+ return $distroList | Select-Object -Unique
1467
+ }
1468
+
1469
+ function Get-Wsl2UserHomes {
1470
+ param([string]$Distro)
1471
+ $homes = @()
1472
+
1473
+ $basePaths = @(
1474
+ "\\\\wsl.localhost\\$Distro",
1475
+ "\\\\wsl\$\\$Distro"
1476
+ )
1477
+
1478
+ foreach ($base in $basePaths) {
1479
+ $homePath = "$base\\home"
1480
+ if (Test-Path $homePath) {
1481
+ Get-ChildItem -Path $homePath -Directory -ErrorAction SilentlyContinue | ForEach-Object {
1482
+ $homes += $_.FullName
1483
+ }
1484
+ $rootPath = "$base\\root"
1485
+ if (Test-Path $rootPath) {
1486
+ $homes += $rootPath
1487
+ }
1488
+ break
1489
+ }
1490
+ }
1491
+ return $homes
1492
+ }
1493
+
1494
+ function Remove-Wsl2Item {
1495
+ param([string]$Path, [string]$Description)
1496
+ if (-not (Test-Path $Path)) { return $false }
1497
+
1498
+ # Try PowerShell removal first
1499
+ Remove-Item -Path $Path -Recurse -Force -ErrorAction SilentlyContinue
1500
+
1501
+ if (-not (Test-Path $Path)) {
1502
+ Log-Action "Removed WSL2 $Description"
1503
+ return $true
1504
+ }
1505
+
1506
+ # If failed, try via wsl command
1507
+ try {
1508
+ # Convert Windows path to WSL path (Handle both localhost and wsl$ formats)
1509
+ $wslPath = $Path -replace '^\\\\\\\\wsl\\.localhost\\\\[^\\\\]+\\\\', '/' -replace '^\\\\\\\\wsl\$\\\\[^\\\\]+\\\\', '/' -replace '\\\\', '/'
1510
+
1511
+ # Extract distro name
1512
+ $distro = if ($Path -match '\\\\wsl\\.localhost\\\\([^\\\\]+)\\\\') { $matches[1] }
1513
+ elseif ($Path -match '\\\\wsl\$\\\\([^\\\\]+)\\\\') { $matches[1] }
1514
+ else { $null }
1515
+
1516
+ if ($distro) {
1517
+ wsl -d $distro rm -rf "$wslPath" 2>$null
1518
+ if (-not (Test-Path $Path)) {
1519
+ Log-Action "Removed WSL2 $Description (via wsl)"
1520
+ return $true
1521
+ }
1522
+ }
1523
+ } catch {}
1524
+
1525
+ Log-Action "WARNING: Failed to remove WSL2 $Description"
1526
+ return $false
1527
+ }
1528
+
1529
+ Log-Action "Checking for WSL2 installations..."
1530
+ $WslDistros = Get-WslDistros
1531
+ $Wsl2Removed = 0
1532
+
1533
+ if ($WslDistros.Count -gt 0) {
1534
+ Log-Action "Found WSL2 distros: $($WslDistros -join ', ')"
1535
+
1536
+ foreach ($distro in $WslDistros) {
1537
+ # KILL SWITCH: Stop Linux processes first
1538
+ Log-Action "Killing WSL2 processes in [$distro]..."
1539
+ foreach ($variant in $Variants) {
1540
+ # Kill processes matching the variant name or node processes running it
1541
+ wsl -d $distro bash -c "pkill -f $variant 2>/dev/null; pkill -f 'node.*$variant' 2>/dev/null"
1542
+ }
1543
+
1544
+ $userHomes = Get-Wsl2UserHomes -Distro $distro
1545
+ if ($userHomes.Count -gt 0) {
1546
+ Log-Action "Scanning WSL2 [$distro] users: $($userHomes.Count) found"
1547
+ }
1548
+
1549
+ foreach ($home in $userHomes) {
1550
+ # Remove config directories (.openclaw, .clawdbot, etc.)
1551
+ foreach ($variant in $Variants) {
1552
+ $configDir = Join-Path $home ".$variant"
1553
+ if (Remove-Wsl2Item -Path $configDir -Description "[$distro] config: $configDir") {
1554
+ $Wsl2Removed++
1555
+ }
1556
+ }
1557
+
1558
+ # Remove CLI binaries
1559
+ foreach ($variant in $Variants) {
1560
+ $binPaths = @(
1561
+ (Join-Path $home ".local\\bin\\$variant"),
1562
+ (Join-Path $home "bin\\$variant")
1563
+ )
1564
+ foreach ($binPath in $binPaths) {
1565
+ if (Remove-Wsl2Item -Path $binPath -Description "[$distro] binary: $binPath") {
1566
+ $Wsl2Removed++
1567
+ }
1568
+ }
1569
+ }
1570
+
1571
+ # Remove nvm node_modules
1572
+ $nvmDir = Join-Path $home ".nvm\\versions\\node"
1573
+ if (Test-Path $nvmDir) {
1574
+ Get-ChildItem -Path $nvmDir -Directory -ErrorAction SilentlyContinue | ForEach-Object {
1575
+ foreach ($variant in $Variants) {
1576
+ $npmPkg = Join-Path $_.FullName "lib\\node_modules\\$variant"
1577
+ if (Remove-Wsl2Item -Path $npmPkg -Description "[$distro] npm (nvm): $npmPkg") {
1578
+ $Wsl2Removed++
1579
+ }
1580
+ }
1581
+ }
1582
+ }
1583
+
1584
+ # Remove all npm/yarn/pnpm package locations
1585
+ foreach ($variant in $Variants) {
1586
+ $npmPaths = @(
1587
+ (Join-Path $home ".local\\lib\\node_modules\\$variant"),
1588
+ (Join-Path $home ".npm-global\\lib\\node_modules\\$variant"),
1589
+ (Join-Path $home ".npm-packages\\lib\\node_modules\\$variant"),
1590
+ (Join-Path $home ".npm\\node_modules\\$variant"),
1591
+ (Join-Path $home "npm\\lib\\node_modules\\$variant"),
1592
+ (Join-Path $home ".node_modules\\$variant"),
1593
+ (Join-Path $home "node_modules\\$variant"),
1594
+ (Join-Path $home ".yarn\\global\\node_modules\\$variant"),
1595
+ (Join-Path $home ".config\\yarn\\global\\node_modules\\$variant"),
1596
+ (Join-Path $home ".pnpm-global\\node_modules\\$variant")
1597
+ )
1598
+
1599
+ foreach ($npmPath in $npmPaths) {
1600
+ if (Remove-Wsl2Item -Path $npmPath -Description "[$distro] package: $npmPath") {
1601
+ $Wsl2Removed++
1602
+ }
1603
+ }
1604
+ }
1605
+ }
1606
+
1607
+ # Remove system-wide paths for this distro
1608
+ $sysBase = "\\\\wsl.localhost\\$distro"
1609
+ if (-not (Test-Path $sysBase)) { $sysBase = "\\\\wsl\$\\$distro" }
1610
+
1611
+ foreach ($variant in $Variants) {
1612
+ $sysPaths = @(
1613
+ "$sysBase\\usr\\local\\bin\\$variant",
1614
+ "$sysBase\\usr\\bin\\$variant",
1615
+ "$sysBase\\opt\\$variant",
1616
+ "$sysBase\\usr\\local\\lib\\node_modules\\$variant",
1617
+ "$sysBase\\usr\\lib\\node_modules\\$variant"
1618
+ )
1619
+ foreach ($sysPath in $sysPaths) {
1620
+ if (Remove-Wsl2Item -Path $sysPath -Description "[$distro] system: $sysPath") {
1621
+ $Wsl2Removed++
1622
+ }
1623
+ }
1624
+ }
1625
+ }
1626
+
1627
+ Log-Action "WSL2 cleanup completed ($Wsl2Removed items removed)"
1628
+ } else {
1629
+ Log-Action "No WSL2 distros found"
1630
+ }
1631
+
1149
1632
  ${webhookUrl ? '$ResultsString = $PurgeResults -join "; "\nSend-Webhook -Status "purged" -Details $ResultsString\n' : ''}
1150
1633
  Log-Action "Purge complete"
1151
1634
  exit 0