patchwarden 0.6.1 → 0.6.4
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/PatchWarden-Control-Tray.cmd +11 -0
- package/PatchWarden-Control.cmd +6 -0
- package/PatchWarden-Desktop.cmd +5 -0
- package/PatchWarden.cmd +1 -1
- package/README.en.md +106 -18
- package/README.md +30 -19
- package/Restart-PatchWarden-Control.cmd +6 -0
- package/Stop-PatchWarden.cmd +11 -0
- package/dist/controlCenter.d.ts +14 -0
- package/dist/controlCenter.js +2002 -0
- package/dist/doctor.js +3 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/control-center/README.md +33 -0
- package/docs/control-center/control-center-daily-driver.md +211 -0
- package/docs/control-center/control-center-mvp.md +205 -0
- package/docs/control-center/control-center-phase2.md +159 -0
- package/docs/demo.md +3 -0
- package/docs/release-v0.6.4.md +45 -0
- package/examples/openai-tunnel/README.md +5 -5
- package/examples/openai-tunnel/tunnel-client.example.yaml +3 -3
- package/package.json +23 -15
- package/scripts/README.md +47 -0
- package/scripts/{brand-check.js → checks/brand-check.js} +2 -2
- package/scripts/checks/control-center-smoke.js +1098 -0
- package/scripts/{control-smoke.js → checks/control-smoke.js} +17 -3
- package/scripts/{doctor-smoke.js → checks/doctor-smoke.js} +1 -1
- package/scripts/{http-mcp-smoke.js → checks/http-mcp-smoke.js} +2 -2
- package/scripts/{lifecycle-smoke.js → checks/lifecycle-smoke.js} +12 -12
- package/scripts/{mcp-manifest-check.js → checks/mcp-manifest-check.js} +2 -2
- package/scripts/{mcp-smoke.js → checks/mcp-smoke.js} +4 -2
- package/scripts/{package-manifest-check.js → checks/package-manifest-check.js} +2 -1
- package/scripts/{tunnel-supervisor-smoke.js → checks/tunnel-supervisor-smoke.js} +2 -2
- package/scripts/{unit-tests.js → checks/unit-tests.js} +1 -1
- package/scripts/{watcher-supervisor-smoke.js → checks/watcher-supervisor-smoke.js} +11 -5
- package/scripts/control/control-center-tray.ps1 +281 -0
- package/scripts/{get-patchwarden-health.ps1 → control/get-patchwarden-health.ps1} +3 -3
- package/scripts/{manage-patchwarden.ps1 → control/manage-patchwarden.ps1} +30 -4
- package/scripts/control/restart-control-center.ps1 +173 -0
- package/scripts/{restart-patchwarden.ps1 → control/restart-patchwarden.ps1} +1 -1
- package/scripts/control/start-control-center.ps1 +263 -0
- package/scripts/{start-patchwarden-tunnel.ps1 → control/start-patchwarden-tunnel.ps1} +48 -6
- package/scripts/control/stop-patchwarden.ps1 +114 -0
- package/scripts/launchers/Check-PatchWarden-Health.cmd +1 -1
- package/scripts/launchers/Reset-PatchWarden-Tunnel-Key.cmd +1 -1
- package/scripts/launchers/Restart-PatchWarden.cmd +1 -1
- package/scripts/launchers/Start-PatchWarden-Direct-Tunnel.cmd +1 -1
- package/scripts/launchers/Start-PatchWarden-Tunnel.cmd +1 -1
- package/scripts/{patchwarden-mcp-direct.cmd → mcp/patchwarden-mcp-direct.cmd} +1 -1
- package/scripts/{patchwarden-mcp-stdio.cmd → mcp/patchwarden-mcp-stdio.cmd} +1 -1
- package/scripts/{pack-clean.js → release/pack-clean.js} +9 -1
- package/src/controlCenter.ts +2152 -0
- package/src/doctor.ts +4 -4
- package/src/version.ts +1 -1
- package/ui/colors_and_type.css +141 -0
- package/ui/pages/audit.html +743 -0
- package/ui/pages/dashboard.html +1154 -0
- package/ui/pages/direct-sessions.html +652 -0
- package/ui/pages/logs.html +502 -0
- package/ui/pages/task-detail.html +1229 -0
- package/ui/pages/tasks.html +702 -0
- package/ui/pages/workspace.html +947 -0
- package/ui/partials/project-shell.html +362 -0
- package/ui/vendor/lucide.js +12 -0
- package/ui/vendor/tailwindcss-browser.js +947 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#Requires -Version 5.1
|
|
2
|
+
param(
|
|
3
|
+
[switch]$NoBrowser,
|
|
4
|
+
[switch]$NoBuild
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
$ErrorActionPreference = "Stop"
|
|
8
|
+
|
|
9
|
+
$ProjectRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
|
10
|
+
$ControlCenterEntry = Join-Path $ProjectRoot "dist\controlCenter.js"
|
|
11
|
+
$ControlCenterEntryFull = [System.IO.Path]::GetFullPath($ControlCenterEntry)
|
|
12
|
+
$ProjectRootFull = [System.IO.Path]::GetFullPath($ProjectRoot).TrimEnd("\")
|
|
13
|
+
$ListenPort = 8090
|
|
14
|
+
$ListenUrl = "http://127.0.0.1:$ListenPort/"
|
|
15
|
+
$RuntimeDirectory = Join-Path $env:LOCALAPPDATA "patchwarden\control-center"
|
|
16
|
+
$StdoutLog = Join-Path $RuntimeDirectory "control-center.stdout.log"
|
|
17
|
+
$StderrLog = Join-Path $RuntimeDirectory "control-center.stderr.log"
|
|
18
|
+
$MaxWaitSeconds = 15
|
|
19
|
+
|
|
20
|
+
Set-Location -LiteralPath $ProjectRoot
|
|
21
|
+
|
|
22
|
+
function Test-ControlCenterProcess {
|
|
23
|
+
param([Parameter(Mandatory = $true)]$ProcessInfo)
|
|
24
|
+
|
|
25
|
+
$commandLine = [string]$ProcessInfo.CommandLine
|
|
26
|
+
if (-not $commandLine) { return $false }
|
|
27
|
+
|
|
28
|
+
$normalizedCommand = $commandLine.Replace("/", "\").ToLowerInvariant()
|
|
29
|
+
$normalizedEntry = $ControlCenterEntryFull.ToLowerInvariant()
|
|
30
|
+
$normalizedRoot = $ProjectRootFull.ToLowerInvariant()
|
|
31
|
+
|
|
32
|
+
return $normalizedCommand.Contains($normalizedEntry) -or (
|
|
33
|
+
$normalizedCommand.Contains("dist\controlcenter.js") -and
|
|
34
|
+
$normalizedCommand.Contains($normalizedRoot)
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function Get-ControlCenterProcesses {
|
|
39
|
+
Get-CimInstance Win32_Process -Filter "Name = 'node.exe'" |
|
|
40
|
+
Where-Object { Test-ControlCenterProcess $_ }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function Assert-PortIsNotForeign {
|
|
44
|
+
try {
|
|
45
|
+
$listeners = Get-NetTCPConnection -LocalPort $ListenPort -State Listen -ErrorAction Stop
|
|
46
|
+
} catch {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
foreach ($listener in $listeners) {
|
|
51
|
+
$owner = Get-CimInstance Win32_Process -Filter "ProcessId = $($listener.OwningProcess)" -ErrorAction SilentlyContinue
|
|
52
|
+
if (-not $owner) { continue }
|
|
53
|
+
if (-not (Test-ControlCenterProcess $owner)) {
|
|
54
|
+
Write-Host "[error] Port $ListenPort is used by a non-PatchWarden Control Center process." -ForegroundColor Red
|
|
55
|
+
Write-Host "[error] PID $($owner.ProcessId): $($owner.CommandLine)" -ForegroundColor DarkRed
|
|
56
|
+
Write-Host "[error] Close that process manually, then rerun this script." -ForegroundColor Yellow
|
|
57
|
+
exit 1
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function Stop-ControlCenterProcesses {
|
|
63
|
+
$processes = @(Get-ControlCenterProcesses)
|
|
64
|
+
if ($processes.Count -eq 0) {
|
|
65
|
+
Write-Host "[stop] No existing PatchWarden Control Center process found." -ForegroundColor DarkGray
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
foreach ($proc in $processes) {
|
|
70
|
+
Write-Host "[stop] Stopping Control Center PID $($proc.ProcessId)." -ForegroundColor Yellow
|
|
71
|
+
Stop-Process -Id $proc.ProcessId -ErrorAction SilentlyContinue
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Start-Sleep -Milliseconds 800
|
|
75
|
+
|
|
76
|
+
foreach ($proc in $processes) {
|
|
77
|
+
$stillRunning = Get-Process -Id $proc.ProcessId -ErrorAction SilentlyContinue
|
|
78
|
+
if ($stillRunning) {
|
|
79
|
+
Write-Host "[stop] PID $($proc.ProcessId) did not exit cleanly; forcing stop." -ForegroundColor Yellow
|
|
80
|
+
Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function Invoke-BuildIfNeeded {
|
|
86
|
+
if ($NoBuild) {
|
|
87
|
+
Write-Host "[build] Skipped by -NoBuild." -ForegroundColor DarkGray
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
Write-Host "[build] Running npm.cmd run build..." -ForegroundColor Cyan
|
|
92
|
+
npm.cmd run build
|
|
93
|
+
if ($LASTEXITCODE -ne 0) {
|
|
94
|
+
Write-Host "[error] npm.cmd run build exited with code $LASTEXITCODE." -ForegroundColor Red
|
|
95
|
+
exit $LASTEXITCODE
|
|
96
|
+
}
|
|
97
|
+
if (-not (Test-Path -LiteralPath $ControlCenterEntryFull)) {
|
|
98
|
+
Write-Host "[error] Build completed but dist\controlCenter.js is missing." -ForegroundColor Red
|
|
99
|
+
exit 1
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function Start-ControlCenter {
|
|
104
|
+
$nodeCommand = Get-Command node.exe -ErrorAction SilentlyContinue
|
|
105
|
+
if (-not $nodeCommand) {
|
|
106
|
+
Write-Host "[error] node.exe was not found on PATH." -ForegroundColor Red
|
|
107
|
+
exit 1
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
New-Item -ItemType Directory -Force -Path $RuntimeDirectory | Out-Null
|
|
111
|
+
Remove-Item -LiteralPath $StdoutLog, $StderrLog -Force -ErrorAction SilentlyContinue
|
|
112
|
+
|
|
113
|
+
Write-Host "[run] Starting PatchWarden Control Center on $ListenUrl" -ForegroundColor Cyan
|
|
114
|
+
$process = Start-Process -FilePath $nodeCommand.Source `
|
|
115
|
+
-ArgumentList @($ControlCenterEntryFull) `
|
|
116
|
+
-WorkingDirectory $ProjectRoot `
|
|
117
|
+
-RedirectStandardOutput $StdoutLog `
|
|
118
|
+
-RedirectStandardError $StderrLog `
|
|
119
|
+
-PassThru -WindowStyle Hidden
|
|
120
|
+
|
|
121
|
+
Start-Sleep -Milliseconds 500
|
|
122
|
+
if ($process.HasExited) {
|
|
123
|
+
Write-Host "[error] Control Center exited immediately with code $($process.ExitCode)." -ForegroundColor Red
|
|
124
|
+
if (Test-Path -LiteralPath $StderrLog) {
|
|
125
|
+
Get-Content -LiteralPath $StderrLog -Tail 20 -ErrorAction SilentlyContinue
|
|
126
|
+
}
|
|
127
|
+
exit 1
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
$ready = $false
|
|
131
|
+
for ($attempt = 1; $attempt -le $MaxWaitSeconds; $attempt++) {
|
|
132
|
+
try {
|
|
133
|
+
$process.Refresh()
|
|
134
|
+
if ($process.HasExited) {
|
|
135
|
+
Write-Host "[error] Control Center exited before readiness with code $($process.ExitCode)." -ForegroundColor Red
|
|
136
|
+
if (Test-Path -LiteralPath $StderrLog) {
|
|
137
|
+
Get-Content -LiteralPath $StderrLog -Tail 20 -ErrorAction SilentlyContinue
|
|
138
|
+
}
|
|
139
|
+
exit 1
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
$response = Invoke-WebRequest -Uri $ListenUrl -UseBasicParsing -TimeoutSec 2 -ErrorAction Stop
|
|
143
|
+
if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 500) {
|
|
144
|
+
$ready = $true
|
|
145
|
+
break
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
Start-Sleep -Seconds 1
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (-not $ready) {
|
|
153
|
+
Write-Host "[error] Control Center did not become ready on $ListenUrl within $MaxWaitSeconds seconds." -ForegroundColor Red
|
|
154
|
+
Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
|
|
155
|
+
exit 1
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
Write-Host "[ok] PatchWarden Control Center restarted at $ListenUrl" -ForegroundColor Green
|
|
159
|
+
Write-Host "[info] node PID $($process.Id)" -ForegroundColor DarkGray
|
|
160
|
+
Write-Host "[info] stdout log: $StdoutLog" -ForegroundColor DarkGray
|
|
161
|
+
Write-Host "[info] stderr log: $StderrLog" -ForegroundColor DarkGray
|
|
162
|
+
|
|
163
|
+
if (-not $NoBrowser) {
|
|
164
|
+
$cacheBuster = Get-Date -Format "yyyyMMddHHmmss"
|
|
165
|
+
Start-Process "$ListenUrl`?restarted=$cacheBuster"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
Assert-PortIsNotForeign
|
|
170
|
+
Stop-ControlCenterProcesses
|
|
171
|
+
Invoke-BuildIfNeeded
|
|
172
|
+
Assert-PortIsNotForeign
|
|
173
|
+
Start-ControlCenter
|
|
@@ -8,7 +8,7 @@ param(
|
|
|
8
8
|
$ErrorActionPreference = "Stop"
|
|
9
9
|
$ProgressPreference = "SilentlyContinue"
|
|
10
10
|
|
|
11
|
-
$ProjectRoot = Split-Path -Parent $PSScriptRoot
|
|
11
|
+
$ProjectRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
|
12
12
|
$ConfigPath = Join-Path $ProjectRoot "patchwarden.config.json"
|
|
13
13
|
$RuntimeDirectory = Join-Path $env:LOCALAPPDATA "patchwarden\runtime"
|
|
14
14
|
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#Requires -Version 5.1
|
|
2
|
+
param(
|
|
3
|
+
[switch]$NoBrowser,
|
|
4
|
+
[switch]$Foreground,
|
|
5
|
+
[switch]$Background
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
$ErrorActionPreference = "Stop"
|
|
9
|
+
|
|
10
|
+
# ── Argument reconciliation ──────────────────────────────────────
|
|
11
|
+
# -Foreground and -Background are mutually exclusive. When neither is
|
|
12
|
+
# supplied, default to -Background (detach node, exit after readiness).
|
|
13
|
+
if ($Foreground -and $Background) {
|
|
14
|
+
Write-Host "[error] -Foreground and -Background are mutually exclusive." -ForegroundColor Red
|
|
15
|
+
exit 1
|
|
16
|
+
}
|
|
17
|
+
if (-not $Foreground -and -not $Background) {
|
|
18
|
+
$Background = $true
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
$ProjectRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
|
22
|
+
$ControlCenterEntry = Join-Path $ProjectRoot "dist\controlCenter.js"
|
|
23
|
+
$ControlCenterEntryFull = [System.IO.Path]::GetFullPath($ControlCenterEntry)
|
|
24
|
+
$ProjectRootFull = [System.IO.Path]::GetFullPath($ProjectRoot).TrimEnd("\")
|
|
25
|
+
$ListenPort = 8090
|
|
26
|
+
$ListenUrl = "http://127.0.0.1:$ListenPort/"
|
|
27
|
+
$RuntimeDirectory = Join-Path $env:LOCALAPPDATA "patchwarden\control-center"
|
|
28
|
+
$StatusFile = Join-Path $RuntimeDirectory "control-center-status.json"
|
|
29
|
+
$StdoutLog = Join-Path $RuntimeDirectory "control-center.stdout.log"
|
|
30
|
+
$StderrLog = Join-Path $RuntimeDirectory "control-center.stderr.log"
|
|
31
|
+
$MaxWaitSeconds = 15
|
|
32
|
+
$PollIntervalSeconds = 1
|
|
33
|
+
|
|
34
|
+
Set-Location -LiteralPath $ProjectRoot
|
|
35
|
+
|
|
36
|
+
# ── Helpers ──────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
function Test-ControlCenterCommandLine {
|
|
39
|
+
param([string]$CommandLine)
|
|
40
|
+
if (-not $CommandLine) { return $false }
|
|
41
|
+
$normalizedCommand = $CommandLine.Replace("/", "\").ToLowerInvariant()
|
|
42
|
+
$normalizedEntry = $ControlCenterEntryFull.ToLowerInvariant()
|
|
43
|
+
$normalizedRoot = $ProjectRootFull.ToLowerInvariant()
|
|
44
|
+
return $normalizedCommand.Contains($normalizedEntry) -or (
|
|
45
|
+
$normalizedCommand.Contains("dist\controlcenter.js") -and
|
|
46
|
+
$normalizedCommand.Contains($normalizedRoot)
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function Get-ControlCenterProcesses {
|
|
51
|
+
Get-CimInstance Win32_Process -Filter "Name = 'node.exe'" -ErrorAction SilentlyContinue |
|
|
52
|
+
Where-Object { Test-ControlCenterCommandLine ([string]$_.CommandLine) }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function Read-StatusFile {
|
|
56
|
+
if (-not (Test-Path -LiteralPath $StatusFile)) { return $null }
|
|
57
|
+
try {
|
|
58
|
+
return Get-Content -LiteralPath $StatusFile -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
59
|
+
} catch {
|
|
60
|
+
return $null
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function Test-PortOwnedByControlCenter {
|
|
65
|
+
# Returns $true if a process is listening on $ListenPort AND it is our control
|
|
66
|
+
# center node process. Returns $false if nothing is listening. Throws a
|
|
67
|
+
# descriptive message if a foreign process owns the port.
|
|
68
|
+
$listeners = @()
|
|
69
|
+
try {
|
|
70
|
+
$listeners = Get-NetTCPConnection -LocalPort $ListenPort -State Listen -ErrorAction Stop |
|
|
71
|
+
Sort-Object OwningProcess -Unique
|
|
72
|
+
} catch {
|
|
73
|
+
return $false # nothing listening
|
|
74
|
+
}
|
|
75
|
+
foreach ($listener in $listeners) {
|
|
76
|
+
$owner = Get-CimInstance Win32_Process -Filter "ProcessId = $($listener.OwningProcess)" -ErrorAction SilentlyContinue
|
|
77
|
+
if (-not $owner) { continue }
|
|
78
|
+
if (Test-ControlCenterCommandLine ([string]$owner.CommandLine)) {
|
|
79
|
+
return $true
|
|
80
|
+
} else {
|
|
81
|
+
Write-Host "[error] Port $ListenPort is already used by a non-PatchWarden process." -ForegroundColor Red
|
|
82
|
+
Write-Host "[error] PID $($owner.ProcessId): $($owner.CommandLine)" -ForegroundColor DarkRed
|
|
83
|
+
Write-Host "[error] Close that process manually, then rerun this script." -ForegroundColor Yellow
|
|
84
|
+
exit 1
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return $false
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function Get-StderrTail {
|
|
91
|
+
if (-not (Test-Path -LiteralPath $StderrLog)) { return "" }
|
|
92
|
+
try {
|
|
93
|
+
$content = Get-Content -LiteralPath $StderrLog -Raw -Encoding UTF8 -ErrorAction Stop
|
|
94
|
+
if (-not $content) { return "" }
|
|
95
|
+
$trimmed = ($content -replace '[\r\n]+', ' ').Trim()
|
|
96
|
+
if ($trimmed.Length -gt 500) { $trimmed = $trimmed.Substring(0, 500) }
|
|
97
|
+
return $trimmed
|
|
98
|
+
} catch {
|
|
99
|
+
return ""
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# ── Single-instance detection ────────────────────────────────────
|
|
104
|
+
# 1. Read the status file. If it points at a live control center (port
|
|
105
|
+
# responds AND pid is alive AND command line matches), just open the
|
|
106
|
+
# browser and exit — do NOT spawn a second server.
|
|
107
|
+
# 2. If the port is occupied by a foreign process, error out.
|
|
108
|
+
# 3. Otherwise start a fresh instance.
|
|
109
|
+
|
|
110
|
+
$existing = Read-StatusFile
|
|
111
|
+
if ($existing) {
|
|
112
|
+
$existingPid = 0
|
|
113
|
+
[int]::TryParse([string]$existing.pid, [ref]$existingPid) | Out-Null
|
|
114
|
+
$existingPort = 0
|
|
115
|
+
[int]::TryParse([string]$existing.port, [ref]$existingPort) | Out-Null
|
|
116
|
+
|
|
117
|
+
$pidAlive = $false
|
|
118
|
+
if ($existingPid -gt 0) {
|
|
119
|
+
$proc = Get-CimInstance Win32_Process -Filter "ProcessId = $existingPid" -ErrorAction SilentlyContinue
|
|
120
|
+
if ($proc -and (Test-ControlCenterCommandLine ([string]$proc.CommandLine))) {
|
|
121
|
+
$pidAlive = $true
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if ($pidAlive -and $existingPort -gt 0) {
|
|
126
|
+
$probeUrl = "http://127.0.0.1:$existingPort/api/control-center-status"
|
|
127
|
+
try {
|
|
128
|
+
$resp = Invoke-WebRequest -Uri $probeUrl -UseBasicParsing -TimeoutSec 2 -ErrorAction Stop
|
|
129
|
+
if ($resp.StatusCode -eq 200) {
|
|
130
|
+
$body = $resp.Content | ConvertFrom-Json
|
|
131
|
+
if ($body.running -eq $true) {
|
|
132
|
+
$existingUrl = "http://127.0.0.1:$existingPort/"
|
|
133
|
+
Write-Host "[ok] PatchWarden Control Center is already running (PID $existingPid)." -ForegroundColor Green
|
|
134
|
+
Write-Host "[ok] URL: $existingUrl" -ForegroundColor Green
|
|
135
|
+
if (-not $NoBrowser) {
|
|
136
|
+
Write-Host "[open] Launching browser at $existingUrl"
|
|
137
|
+
Start-Process $existingUrl
|
|
138
|
+
}
|
|
139
|
+
exit 0
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} catch {
|
|
143
|
+
# Status file exists but the probe failed — stale entry. Fall through
|
|
144
|
+
# to a fresh start after cleaning up.
|
|
145
|
+
Write-Host "[warn] Status file exists but the server did not respond; starting a new instance." -ForegroundColor DarkYellow
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
Write-Host "[warn] Status file references a dead PID; starting a new instance." -ForegroundColor DarkYellow
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# Detect foreign port occupation before spawning.
|
|
153
|
+
Test-PortOwnedByControlCenter | Out-Null
|
|
154
|
+
# If we reach here, either nothing is listening or our own (now-dead) process
|
|
155
|
+
# was the listener. Clean up any orphaned node processes for this entry point.
|
|
156
|
+
$orphans = @(Get-ControlCenterProcesses)
|
|
157
|
+
if ($orphans.Count -gt 0) {
|
|
158
|
+
Write-Host "[cleanup] Stopping $($orphans.Count) orphaned Control Center node process(es)." -ForegroundColor DarkYellow
|
|
159
|
+
foreach ($p in $orphans) {
|
|
160
|
+
try { Stop-Process -Id $p.ProcessId -Force -ErrorAction Stop } catch {}
|
|
161
|
+
}
|
|
162
|
+
Start-Sleep -Milliseconds 500
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# ── Build if needed ──────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
$nodeCommand = Get-Command node.exe -ErrorAction SilentlyContinue
|
|
168
|
+
if (-not $nodeCommand) {
|
|
169
|
+
Write-Host "[error] node.exe was not found on PATH." -ForegroundColor Red
|
|
170
|
+
exit 1
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (-not (Test-Path -LiteralPath $ControlCenterEntryFull)) {
|
|
174
|
+
Write-Host "[build] dist/controlCenter.js not found. Running npm.cmd run build..." -ForegroundColor Yellow
|
|
175
|
+
npm.cmd run build
|
|
176
|
+
if ($LASTEXITCODE -ne 0) {
|
|
177
|
+
Write-Host "[error] npm.cmd run build exited with code $LASTEXITCODE." -ForegroundColor Red
|
|
178
|
+
exit 1
|
|
179
|
+
}
|
|
180
|
+
if (-not (Test-Path -LiteralPath $ControlCenterEntryFull)) {
|
|
181
|
+
Write-Host "[error] Build completed but dist/controlCenter.js is still missing." -ForegroundColor Red
|
|
182
|
+
exit 1
|
|
183
|
+
}
|
|
184
|
+
Write-Host "[build] Build complete." -ForegroundColor Green
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
New-Item -ItemType Directory -Force -Path $RuntimeDirectory | Out-Null
|
|
188
|
+
Remove-Item -LiteralPath $StdoutLog, $StderrLog -Force -ErrorAction SilentlyContinue
|
|
189
|
+
|
|
190
|
+
# ── Foreground mode: node attached to this console, logs stream live ──
|
|
191
|
+
if ($Foreground) {
|
|
192
|
+
Write-Host "[run] Starting PatchWarden Control Center on $ListenUrl (foreground)" -ForegroundColor Cyan
|
|
193
|
+
Write-Host "[run] Press Ctrl+C to stop. Logs stream below."
|
|
194
|
+
# In foreground mode the node process inherits this console; the status file
|
|
195
|
+
# is still written by the server itself on listen().
|
|
196
|
+
& $nodeCommand.Source $ControlCenterEntryFull
|
|
197
|
+
exit $LASTEXITCODE
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
# ── Background mode (default): detached node, this script exits after ready ──
|
|
201
|
+
Write-Host "[run] Starting PatchWarden Control Center on $ListenUrl (background)" -ForegroundColor Cyan
|
|
202
|
+
$process = Start-Process -FilePath $nodeCommand.Source `
|
|
203
|
+
-ArgumentList @($ControlCenterEntryFull) `
|
|
204
|
+
-WorkingDirectory $ProjectRoot `
|
|
205
|
+
-RedirectStandardOutput $StdoutLog `
|
|
206
|
+
-RedirectStandardError $StderrLog `
|
|
207
|
+
-PassThru -WindowStyle Hidden
|
|
208
|
+
|
|
209
|
+
Write-Host "[run] node PID $($process.Id)."
|
|
210
|
+
|
|
211
|
+
# Brief wait to detect truly immediate exits.
|
|
212
|
+
Start-Sleep -Milliseconds 500
|
|
213
|
+
if ($process.HasExited) {
|
|
214
|
+
$exitCode = $process.ExitCode
|
|
215
|
+
$stderrTail = Get-StderrTail
|
|
216
|
+
Write-Host "[error] node process exited immediately with code $exitCode." -ForegroundColor Red
|
|
217
|
+
if ($stderrTail) {
|
|
218
|
+
Write-Host "[error] stderr: $stderrTail" -ForegroundColor DarkRed
|
|
219
|
+
}
|
|
220
|
+
exit 1
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
# Poll for port readiness.
|
|
224
|
+
$ready = $false
|
|
225
|
+
for ($attempt = 1; $attempt -le $MaxWaitSeconds; $attempt++) {
|
|
226
|
+
try { $process.Refresh() } catch {}
|
|
227
|
+
if ($process.HasExited) {
|
|
228
|
+
$exitCode = $process.ExitCode
|
|
229
|
+
$stderrTail = Get-StderrTail
|
|
230
|
+
Write-Host "[error] node process exited with code $exitCode before the port became ready." -ForegroundColor Red
|
|
231
|
+
if ($stderrTail) {
|
|
232
|
+
Write-Host "[error] stderr: $stderrTail" -ForegroundColor DarkRed
|
|
233
|
+
}
|
|
234
|
+
exit 1
|
|
235
|
+
}
|
|
236
|
+
try {
|
|
237
|
+
$response = Invoke-WebRequest -Uri $ListenUrl -UseBasicParsing -TimeoutSec 2 -ErrorAction Stop
|
|
238
|
+
if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 500) {
|
|
239
|
+
$ready = $true
|
|
240
|
+
break
|
|
241
|
+
}
|
|
242
|
+
} catch {
|
|
243
|
+
# Not ready yet; keep polling.
|
|
244
|
+
}
|
|
245
|
+
Start-Sleep -Seconds $PollIntervalSeconds
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (-not $ready) {
|
|
249
|
+
Write-Host "[error] Control Center did not become ready on $ListenUrl within $MaxWaitSeconds seconds." -ForegroundColor Red
|
|
250
|
+
try { Stop-Process -Id $process.Id -Force -ErrorAction Stop } catch {}
|
|
251
|
+
exit 1
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
Write-Host "[ok] PatchWarden Control Center is ready at $ListenUrl" -ForegroundColor Green
|
|
255
|
+
|
|
256
|
+
if (-not $NoBrowser) {
|
|
257
|
+
Write-Host "[open] Launching default browser at $ListenUrl"
|
|
258
|
+
Start-Process $ListenUrl
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
Write-Host "[info] node PID $($process.Id); stdout log: $StdoutLog; stderr log: $StderrLog"
|
|
262
|
+
Write-Host "[info] Re-run this script to open the existing instance instead of starting a new one."
|
|
263
|
+
Write-Host "[info] Stop with: Stop-Process -Id $($process.Id)"
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
param(
|
|
1
|
+
param(
|
|
2
2
|
[string]$TunnelId = $env:PATCHWARDEN_TUNNEL_ID,
|
|
3
3
|
[string]$Profile = "patchwarden",
|
|
4
4
|
[ValidateSet("chatgpt_core", "chatgpt_direct")]
|
|
5
5
|
[string]$ToolProfile = "chatgpt_core",
|
|
6
6
|
[string]$ProxyUrl = $(if ($env:HTTPS_PROXY) { $env:HTTPS_PROXY } else { "http://127.0.0.1:7892" }),
|
|
7
|
-
[string]$TunnelClientExe = $env:TUNNEL_CLIENT_EXE,
|
|
7
|
+
[string]$TunnelClientExe = $(if ($env:TUNNEL_CLIENT_EXE) { $env:TUNNEL_CLIENT_EXE } else { $env:PATCHWARDEN_TUNNEL_CLIENT_EXE }),
|
|
8
8
|
[string]$OpencodeBin = $env:OPENCODE_BIN_DIR,
|
|
9
9
|
[string]$ConfigPath = $env:PATCHWARDEN_CONFIG,
|
|
10
10
|
[string]$CredentialPath = $(if ($env:PATCHWARDEN_CREDENTIAL_PATH) { $env:PATCHWARDEN_CREDENTIAL_PATH } else { Join-Path $env:APPDATA "patchwarden\control-plane-api-key.dpapi" }),
|
|
@@ -16,6 +16,7 @@ param(
|
|
|
16
16
|
[int]$WatcherHealthyResetSeconds = 60,
|
|
17
17
|
[switch]$SkipWatcher,
|
|
18
18
|
[switch]$ForgetSavedApiKey,
|
|
19
|
+
[switch]$NoTunnelWebUi,
|
|
19
20
|
[string]$HealthListenAddr = ""
|
|
20
21
|
)
|
|
21
22
|
|
|
@@ -29,12 +30,12 @@ if ([string]::IsNullOrWhiteSpace($HealthListenAddr)) {
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
$ProjectRoot = Split-Path -Parent $PSScriptRoot
|
|
33
|
+
$ProjectRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
|
33
34
|
if (-not $ConfigPath) {
|
|
34
35
|
$ConfigPath = Join-Path $ProjectRoot "patchwarden.config.json"
|
|
35
36
|
}
|
|
36
37
|
$McpLauncherName = if ($ToolProfile -eq "chatgpt_direct") { "patchwarden-mcp-direct.cmd" } else { "patchwarden-mcp-stdio.cmd" }
|
|
37
|
-
$McpStdioLauncher = Join-Path $ProjectRoot "scripts\$McpLauncherName"
|
|
38
|
+
$McpStdioLauncher = Join-Path $ProjectRoot "scripts\mcp\$McpLauncherName"
|
|
38
39
|
$McpStdioLauncherForTunnel = $McpStdioLauncher -replace "\\", "/"
|
|
39
40
|
$OpencodeConfigHome = Join-Path $env:LOCALAPPDATA "patchwarden\opencode-config"
|
|
40
41
|
$ProfilePath = Join-Path $env:APPDATA "tunnel-client\$Profile.yaml"
|
|
@@ -57,6 +58,7 @@ $script:WatcherManaged = $false
|
|
|
57
58
|
$script:WatcherRestartAttempts = 0
|
|
58
59
|
$script:WatcherHealthySince = $null
|
|
59
60
|
$script:WatcherRestartExhausted = $false
|
|
61
|
+
$DefaultTunnelClientPath = "D:\ai_agent\tunnel-client-v0.0.9--context-conduit-topaz-windows-amd64\tunnel-client.exe"
|
|
60
62
|
|
|
61
63
|
if ($ToolProfile -eq "chatgpt_direct") {
|
|
62
64
|
$SkipWatcher = $true
|
|
@@ -501,7 +503,34 @@ if (-not $TunnelClientExe) {
|
|
|
501
503
|
}
|
|
502
504
|
}
|
|
503
505
|
|
|
506
|
+
# Search common installation locations
|
|
507
|
+
$commonPaths = @(
|
|
508
|
+
$DefaultTunnelClientPath
|
|
509
|
+
Join-Path $env:LOCALAPPDATA "patchwarden\tunnel-client.exe"
|
|
510
|
+
Join-Path $env:APPDATA "tunnel-client\tunnel-client.exe"
|
|
511
|
+
Join-Path $env:USERPROFILE "tunnel-client\tunnel-client.exe"
|
|
512
|
+
)
|
|
513
|
+
if (-not $TunnelClientExe) {
|
|
514
|
+
foreach ($candidate in $commonPaths) {
|
|
515
|
+
if (Test-Path -LiteralPath $candidate) {
|
|
516
|
+
$TunnelClientExe = $candidate
|
|
517
|
+
Write-Host "[detect] Found tunnel-client.exe: $TunnelClientExe" -ForegroundColor Green
|
|
518
|
+
break
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
504
523
|
if (-not $TunnelClientExe) {
|
|
524
|
+
Write-Host ""
|
|
525
|
+
Write-Host "[input] tunnel-client.exe not found on PATH or common locations." -ForegroundColor Yellow
|
|
526
|
+
Write-Host " Please provide the full path."
|
|
527
|
+
Write-Host ""
|
|
528
|
+
Write-Host " To search for it manually, try one of these commands:" -ForegroundColor Cyan
|
|
529
|
+
Write-Host " Get-ChildItem -Path `$env:LOCALAPPDATA -Recurse -Filter tunnel-client.exe -ErrorAction SilentlyContinue" -ForegroundColor Gray
|
|
530
|
+
Write-Host " Get-ChildItem -Path `$env:APPDATA -Recurse -Filter tunnel-client.exe -ErrorAction SilentlyContinue" -ForegroundColor Gray
|
|
531
|
+
Write-Host " Get-ChildItem -Path C:\ -Recurse -Filter tunnel-client.exe -ErrorAction SilentlyContinue" -ForegroundColor Gray
|
|
532
|
+
Write-Host " Or download it from: https://platform.openai.com/docs/guides/mcp-connector" -ForegroundColor Cyan
|
|
533
|
+
Write-Host ""
|
|
505
534
|
$TunnelClientExe = Read-Host "Path to tunnel-client.exe"
|
|
506
535
|
}
|
|
507
536
|
|
|
@@ -521,6 +550,19 @@ Assert-File -Path $TunnelClientExe -Name "tunnel-client.exe"
|
|
|
521
550
|
Assert-File -Path $ConfigPath -Name "patchwarden.config.json"
|
|
522
551
|
Assert-File -Path $McpStdioLauncher -Name $McpLauncherName
|
|
523
552
|
|
|
553
|
+
# Auto-read Tunnel ID from existing profile YAML if not already set
|
|
554
|
+
if (-not $TunnelId -and (Test-Path -LiteralPath $ProfilePath)) {
|
|
555
|
+
try {
|
|
556
|
+
$profileContent = Get-Content -LiteralPath $ProfilePath -Raw -Encoding UTF8
|
|
557
|
+
if ($profileContent -match 'tunnel_id:\s*"([^"]+)"') {
|
|
558
|
+
$TunnelId = $matches[1]
|
|
559
|
+
Write-Host "[detect] Tunnel ID read from profile $Profile`: $TunnelId" -ForegroundColor Green
|
|
560
|
+
}
|
|
561
|
+
} catch {
|
|
562
|
+
Write-Host "[warn] Could not read Tunnel ID from $ProfilePath`: $($_.Exception.Message)" -ForegroundColor Yellow
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
524
566
|
if (-not $TunnelId) {
|
|
525
567
|
$TunnelId = Read-Host "Tunnel ID"
|
|
526
568
|
}
|
|
@@ -538,7 +580,7 @@ if (-not (Test-Path -LiteralPath (Join-Path $ProjectRoot "dist\index.js"))) {
|
|
|
538
580
|
New-Item -ItemType Directory -Force -Path $RuntimeDirectory | Out-Null
|
|
539
581
|
$env:PATCHWARDEN_CONFIG = $ConfigPath
|
|
540
582
|
Write-Host "[manifest] Verifying the exact $ToolProfile stdio MCP tool catalog..."
|
|
541
|
-
$manifestOutput = (& node (Join-Path $ProjectRoot "scripts\mcp-manifest-check.js") --profile $ToolProfile --json 2>&1 | Out-String).Trim()
|
|
583
|
+
$manifestOutput = (& node (Join-Path $ProjectRoot "scripts\checks\mcp-manifest-check.js") --profile $ToolProfile --json 2>&1 | Out-String).Trim()
|
|
542
584
|
if ($LASTEXITCODE -ne 0) {
|
|
543
585
|
Write-TunnelStatus -Status "stopped" -ReasonCode "tool_manifest_check_failed" -LastError "The tunnel MCP tool manifest preflight failed."
|
|
544
586
|
throw "Tool manifest preflight failed: $manifestOutput"
|
|
@@ -630,7 +672,7 @@ Write-Host "[run-config] cwd: $ProjectRoot"
|
|
|
630
672
|
Write-Host ""
|
|
631
673
|
|
|
632
674
|
$attempt = 0
|
|
633
|
-
$openUi = $
|
|
675
|
+
$openUi = -not $NoTunnelWebUi
|
|
634
676
|
try {
|
|
635
677
|
while ($true) {
|
|
636
678
|
$attempt++
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#Requires -Version 5.1
|
|
2
|
+
<#
|
|
3
|
+
.SYNOPSIS
|
|
4
|
+
One-click shutdown for PatchWarden desktop use.
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Stops Core/Direct through the consolidated manager, then closes the local
|
|
7
|
+
Control Center and tray processes that belong to this project path.
|
|
8
|
+
#>
|
|
9
|
+
param(
|
|
10
|
+
[switch]$KeepControlCenter,
|
|
11
|
+
[switch]$KeepTray
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
$ErrorActionPreference = "Stop"
|
|
15
|
+
$ProjectRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
|
16
|
+
$ProjectRootFull = [System.IO.Path]::GetFullPath($ProjectRoot).TrimEnd("\")
|
|
17
|
+
$Manager = Join-Path $PSScriptRoot "manage-patchwarden.ps1"
|
|
18
|
+
$ControlCenterEntry = Join-Path $ProjectRoot "dist\controlCenter.js"
|
|
19
|
+
$ControlCenterEntryFull = [System.IO.Path]::GetFullPath($ControlCenterEntry)
|
|
20
|
+
$TrayScript = Join-Path $PSScriptRoot "control-center-tray.ps1"
|
|
21
|
+
$TrayScriptFull = [System.IO.Path]::GetFullPath($TrayScript)
|
|
22
|
+
$RuntimeDirectory = Join-Path $env:LOCALAPPDATA "patchwarden\control-center"
|
|
23
|
+
$StatusFile = Join-Path $RuntimeDirectory "control-center-status.json"
|
|
24
|
+
|
|
25
|
+
Set-Location -LiteralPath $ProjectRoot
|
|
26
|
+
|
|
27
|
+
function Normalize-CommandLine {
|
|
28
|
+
param([string]$CommandLine)
|
|
29
|
+
if (-not $CommandLine) { return "" }
|
|
30
|
+
return $CommandLine.Replace("/", "\").ToLowerInvariant()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function Test-ControlCenterProcess {
|
|
34
|
+
param($ProcessInfo)
|
|
35
|
+
$normalizedCommand = Normalize-CommandLine ([string]$ProcessInfo.CommandLine)
|
|
36
|
+
$normalizedEntry = $ControlCenterEntryFull.ToLowerInvariant()
|
|
37
|
+
$normalizedRoot = $ProjectRootFull.ToLowerInvariant()
|
|
38
|
+
return $normalizedCommand.Contains($normalizedEntry) -or (
|
|
39
|
+
$normalizedCommand.Contains("dist\controlcenter.js") -and
|
|
40
|
+
$normalizedCommand.Contains($normalizedRoot)
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function Test-TrayProcess {
|
|
45
|
+
param($ProcessInfo)
|
|
46
|
+
$normalizedCommand = Normalize-CommandLine ([string]$ProcessInfo.CommandLine)
|
|
47
|
+
$normalizedTray = $TrayScriptFull.ToLowerInvariant()
|
|
48
|
+
$normalizedRoot = $ProjectRootFull.ToLowerInvariant()
|
|
49
|
+
return $normalizedCommand.Contains($normalizedTray) -or (
|
|
50
|
+
$normalizedCommand.Contains("control-center-tray.ps1") -and
|
|
51
|
+
$normalizedCommand.Contains($normalizedRoot)
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function Stop-MatchingProcesses {
|
|
56
|
+
param(
|
|
57
|
+
[Parameter(Mandatory = $true)][string]$Label,
|
|
58
|
+
[Parameter(Mandatory = $true)][scriptblock]$Predicate
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
$processes = @(
|
|
62
|
+
Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
|
|
63
|
+
Where-Object {
|
|
64
|
+
[int]$_.ProcessId -ne $PID -and (& $Predicate $_)
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if ($processes.Count -eq 0) {
|
|
69
|
+
Write-Host "[stop] No $Label process found." -ForegroundColor DarkGray
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
foreach ($proc in $processes) {
|
|
74
|
+
Write-Host "[stop] Closing $Label PID $($proc.ProcessId)." -ForegroundColor Yellow
|
|
75
|
+
try {
|
|
76
|
+
Stop-Process -Id $proc.ProcessId -ErrorAction SilentlyContinue
|
|
77
|
+
} catch {
|
|
78
|
+
Write-Host "[stop] PID $($proc.ProcessId) already exited or is inaccessible." -ForegroundColor DarkGray
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
Start-Sleep -Milliseconds 800
|
|
83
|
+
|
|
84
|
+
foreach ($proc in $processes) {
|
|
85
|
+
$stillRunning = Get-Process -Id $proc.ProcessId -ErrorAction SilentlyContinue
|
|
86
|
+
if ($stillRunning) {
|
|
87
|
+
Write-Host "[stop] PID $($proc.ProcessId) did not exit cleanly; forcing stop." -ForegroundColor Yellow
|
|
88
|
+
Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (-not (Test-Path -LiteralPath $Manager)) {
|
|
94
|
+
throw "Manager script not found: $Manager"
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
Write-Host "[stop] Stopping PatchWarden Core/Direct services..." -ForegroundColor Cyan
|
|
98
|
+
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $Manager stop all
|
|
99
|
+
if ($LASTEXITCODE -ne 0) {
|
|
100
|
+
exit $LASTEXITCODE
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (-not $KeepControlCenter) {
|
|
104
|
+
Stop-MatchingProcesses -Label "Control Center" -Predicate ${function:Test-ControlCenterProcess}
|
|
105
|
+
if (Test-Path -LiteralPath $StatusFile) {
|
|
106
|
+
Remove-Item -LiteralPath $StatusFile -Force -ErrorAction SilentlyContinue
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (-not $KeepTray) {
|
|
111
|
+
Stop-MatchingProcesses -Label "tray" -Predicate ${function:Test-TrayProcess}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
Write-Host "[ok] PatchWarden shutdown complete." -ForegroundColor Green
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
@echo off
|
|
2
2
|
setlocal
|
|
3
3
|
cd /d "%~dp0..\.."
|
|
4
|
-
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0..\manage-patchwarden.ps1" status all
|
|
4
|
+
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0..\control\manage-patchwarden.ps1" status all
|
|
5
5
|
echo.
|
|
6
6
|
pause
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
@echo off
|
|
2
2
|
setlocal
|
|
3
3
|
cd /d "%~dp0..\.."
|
|
4
|
-
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0..\start-patchwarden-tunnel.ps1" -ForgetSavedApiKey
|
|
4
|
+
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0..\control\start-patchwarden-tunnel.ps1" -ForgetSavedApiKey
|
|
5
5
|
echo.
|
|
6
6
|
pause
|