patchwork-os 0.2.0-beta.4 → 0.2.0-beta.5.canary.1
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/dist/analyticsConfig.d.ts +29 -0
- package/dist/analyticsConfig.js +89 -0
- package/dist/analyticsConfig.js.map +1 -0
- package/dist/analyticsSend.d.ts +17 -1
- package/dist/analyticsSend.js +63 -5
- package/dist/analyticsSend.js.map +1 -1
- package/dist/commands/analytics.d.ts +8 -0
- package/dist/commands/analytics.js +134 -0
- package/dist/commands/analytics.js.map +1 -0
- package/dist/index.js +85 -40
- package/dist/index.js.map +1 -1
- package/dist/recipeOrchestration.js +58 -8
- package/dist/recipeOrchestration.js.map +1 -1
- package/dist/recipeRoutes.js +95 -14
- package/dist/recipeRoutes.js.map +1 -1
- package/dist/server.js +36 -18
- package/dist/server.js.map +1 -1
- package/package.json +2 -1
- package/scripts/start-all.ps1 +209 -209
- package/scripts/start-orchestrator.ps1 +158 -158
package/scripts/start-all.ps1
CHANGED
|
@@ -1,209 +1,209 @@
|
|
|
1
|
-
#Requires -Version 5.1
|
|
2
|
-
<#
|
|
3
|
-
.SYNOPSIS
|
|
4
|
-
Windows orchestrator for bridge + Claude + Patchwork dashboard.
|
|
5
|
-
|
|
6
|
-
.DESCRIPTION
|
|
7
|
-
Cross-platform alternative to start-all.sh for native Windows (PowerShell/cmd).
|
|
8
|
-
Starts the bridge, waits for the lock file, launches Claude --ide, and
|
|
9
|
-
optionally starts the Patchwork dashboard dev server and opens it in the browser.
|
|
10
|
-
|
|
11
|
-
Run via npm:
|
|
12
|
-
npm run start:bridge # bridge only (simplest)
|
|
13
|
-
npm run start-all:win # full orchestrator (bridge + claude + dashboard)
|
|
14
|
-
|
|
15
|
-
Or directly:
|
|
16
|
-
pwsh -File scripts\start-all.ps1
|
|
17
|
-
pwsh -File scripts\start-all.ps1 --no-dashboard
|
|
18
|
-
pwsh -File scripts\start-all.ps1 --workspace C:\my\project --dashboard-port 3200
|
|
19
|
-
|
|
20
|
-
.PARAMETER Workspace
|
|
21
|
-
Directory to open in Claude (default: current directory).
|
|
22
|
-
|
|
23
|
-
.PARAMETER Full
|
|
24
|
-
Pass --full to the bridge, registering all ~95 tools including git/terminal/file ops.
|
|
25
|
-
Default is slim mode (27 IDE-exclusive tools).
|
|
26
|
-
|
|
27
|
-
.PARAMETER NoDashboard
|
|
28
|
-
Skip starting the Patchwork dashboard.
|
|
29
|
-
|
|
30
|
-
.PARAMETER DashboardPort
|
|
31
|
-
Port for the Next.js dashboard dev server (default: 3200).
|
|
32
|
-
|
|
33
|
-
.PARAMETER BridgePort
|
|
34
|
-
Port for the bridge (default: auto-assigned via lock file).
|
|
35
|
-
|
|
36
|
-
.PARAMETER Notify
|
|
37
|
-
ntfy.sh topic for push notifications (optional).
|
|
38
|
-
#>
|
|
39
|
-
[CmdletBinding()]
|
|
40
|
-
param(
|
|
41
|
-
[string]$Workspace = ".",
|
|
42
|
-
[switch]$Full,
|
|
43
|
-
[switch]$NoDashboard,
|
|
44
|
-
[int] $DashboardPort = 3200,
|
|
45
|
-
[int] $BridgePort = 0,
|
|
46
|
-
[string]$Notify = ""
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
Set-StrictMode -Version Latest
|
|
50
|
-
$ErrorActionPreference = "Stop"
|
|
51
|
-
|
|
52
|
-
# ── Resolve paths ─────────────────────────────────────────────────────────────
|
|
53
|
-
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
54
|
-
$BridgeDir = Split-Path -Parent $ScriptDir
|
|
55
|
-
$DashboardDir = Join-Path $BridgeDir "dashboard"
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
$Workspace = (Resolve-Path $Workspace).Path
|
|
59
|
-
} catch {
|
|
60
|
-
Write-Error "Workspace directory not found: $Workspace"
|
|
61
|
-
exit 1
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
65
|
-
function Write-Status($msg) { Write-Host "[orchestrator] $msg" -ForegroundColor Cyan }
|
|
66
|
-
function Write-Ok($msg) { Write-Host "[ok] $msg" -ForegroundColor Green }
|
|
67
|
-
function Write-Warn($msg) { Write-Host "[warn] $msg" -ForegroundColor Yellow }
|
|
68
|
-
|
|
69
|
-
function Send-Notify($msg) {
|
|
70
|
-
if (-not $Notify) { return }
|
|
71
|
-
try {
|
|
72
|
-
Invoke-RestMethod -Uri "https://ntfy.sh/$Notify" -Method Post -Body $msg -TimeoutSec 5 | Out-Null
|
|
73
|
-
} catch { Write-Warn "ntfy notification failed: $_" }
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
# ── Track child processes for cleanup ─────────────────────────────────────────
|
|
77
|
-
$Jobs = [System.Collections.Generic.List[System.Diagnostics.Process]]::new()
|
|
78
|
-
|
|
79
|
-
function Stop-AllJobs {
|
|
80
|
-
foreach ($p in $Jobs) {
|
|
81
|
-
if (-not $p.HasExited) {
|
|
82
|
-
try { $p.Kill($true) } catch { }
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
# Clean up on Ctrl+C or exit
|
|
88
|
-
$null = Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action { Stop-AllJobs }
|
|
89
|
-
try { [Console]::TreatControlCAsInput = $false } catch { }
|
|
90
|
-
|
|
91
|
-
# ── Build bridge args ─────────────────────────────────────────────────────────
|
|
92
|
-
$BridgeArgs = @("--workspace", $Workspace)
|
|
93
|
-
if ($BridgePort -gt 0) { $BridgeArgs += @("--port", $BridgePort) }
|
|
94
|
-
if ($Full) { $BridgeArgs += "--full" }
|
|
95
|
-
|
|
96
|
-
# ── Start bridge ──────────────────────────────────────────────────────────────
|
|
97
|
-
Write-Status "Starting bridge (workspace: $Workspace)..."
|
|
98
|
-
|
|
99
|
-
$BridgeInfo = New-Object System.Diagnostics.ProcessStartInfo
|
|
100
|
-
$BridgeInfo.UseShellExecute = $false
|
|
101
|
-
# On Windows, npm global bins are .cmd wrappers — must invoke via cmd.exe.
|
|
102
|
-
# Quote each argument so workspace paths with spaces (e.g. "C:\Users\Jane Doe\...") survive.
|
|
103
|
-
$BridgeInfo.FileName = "cmd.exe"
|
|
104
|
-
$quotedArgs = $BridgeArgs | ForEach-Object { if ($_ -match '\s') { "`"$_`"" } else { $_ } }
|
|
105
|
-
$BridgeInfo.Arguments = "/c claude-ide-bridge " + ($quotedArgs -join " ")
|
|
106
|
-
|
|
107
|
-
$BridgeProc = [System.Diagnostics.Process]::Start($BridgeInfo)
|
|
108
|
-
$Jobs.Add($BridgeProc)
|
|
109
|
-
|
|
110
|
-
# ── Wait for lock file ────────────────────────────────────────────────────────
|
|
111
|
-
Write-Status "Waiting for bridge lock file..."
|
|
112
|
-
$ClaudeBase = if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $env:USERPROFILE ".claude" }
|
|
113
|
-
$IdeDir = Join-Path $ClaudeBase "ide"
|
|
114
|
-
$Deadline = (Get-Date).AddSeconds(30)
|
|
115
|
-
$LockFile = $null
|
|
116
|
-
|
|
117
|
-
while ((Get-Date) -lt $Deadline) {
|
|
118
|
-
$locks = Get-ChildItem -Path $IdeDir -Filter "*.lock" -ErrorAction SilentlyContinue |
|
|
119
|
-
Where-Object { $_.LastWriteTime -gt (Get-Date).AddSeconds(-60) }
|
|
120
|
-
if ($locks) {
|
|
121
|
-
# Find the lock that matches our workspace
|
|
122
|
-
foreach ($lf in $locks) {
|
|
123
|
-
try {
|
|
124
|
-
$content = Get-Content $lf.FullName -Raw | ConvertFrom-Json
|
|
125
|
-
if ($content.isBridge -and
|
|
126
|
-
($content.workspace -replace '\\','/' ) -eq ($Workspace -replace '\\','/')) {
|
|
127
|
-
$LockFile = $lf
|
|
128
|
-
$DetectedPort = [int][System.IO.Path]::GetFileNameWithoutExtension($lf.Name)
|
|
129
|
-
break
|
|
130
|
-
}
|
|
131
|
-
} catch { }
|
|
132
|
-
}
|
|
133
|
-
if ($LockFile) { break }
|
|
134
|
-
}
|
|
135
|
-
Start-Sleep -Milliseconds 200
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (-not $LockFile) {
|
|
139
|
-
Write-Error "Bridge lock file not written after 30s. Bridge may have failed to start."
|
|
140
|
-
Stop-AllJobs
|
|
141
|
-
exit 1
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
Write-Ok "Bridge ready on port $DetectedPort"
|
|
145
|
-
Send-Notify "Bridge started on port $DetectedPort"
|
|
146
|
-
|
|
147
|
-
# ── Start Claude --ide ────────────────────────────────────────────────────────
|
|
148
|
-
Write-Status "Starting claude --ide..."
|
|
149
|
-
$ClaudeInfo = New-Object System.Diagnostics.ProcessStartInfo
|
|
150
|
-
$ClaudeInfo.FileName = "cmd.exe"
|
|
151
|
-
$ClaudeInfo.Arguments = "/c claude --ide"
|
|
152
|
-
$ClaudeInfo.UseShellExecute = $false
|
|
153
|
-
$ClaudeProc = [System.Diagnostics.Process]::Start($ClaudeInfo)
|
|
154
|
-
$Jobs.Add($ClaudeProc)
|
|
155
|
-
|
|
156
|
-
# ── Start dashboard ───────────────────────────────────────────────────────────
|
|
157
|
-
$DashProc = $null
|
|
158
|
-
if (-not $NoDashboard) {
|
|
159
|
-
if (-not (Test-Path (Join-Path $DashboardDir "node_modules"))) {
|
|
160
|
-
Write-Warn "dashboard/node_modules not found. Run 'npm ci' in the dashboard directory first, or pass -NoDashboard."
|
|
161
|
-
} else {
|
|
162
|
-
Write-Status "Starting dashboard on http://localhost:$DashboardPort ..."
|
|
163
|
-
|
|
164
|
-
$env:PATCHWORK_BRIDGE_PORT = $DetectedPort
|
|
165
|
-
$DashInfo = New-Object System.Diagnostics.ProcessStartInfo
|
|
166
|
-
$DashInfo.FileName = "cmd.exe"
|
|
167
|
-
$DashInfo.Arguments = "/c npx next dev -p $DashboardPort"
|
|
168
|
-
$DashInfo.WorkingDirectory = $DashboardDir
|
|
169
|
-
$DashInfo.UseShellExecute = $false
|
|
170
|
-
$DashProc = [System.Diagnostics.Process]::Start($DashInfo)
|
|
171
|
-
$Jobs.Add($DashProc)
|
|
172
|
-
|
|
173
|
-
# Poll until Next.js answers, then open the browser
|
|
174
|
-
$DashUrl = "http://localhost:$DashboardPort"
|
|
175
|
-
$DashDeadline = (Get-Date).AddSeconds(60)
|
|
176
|
-
$Opened = $false
|
|
177
|
-
while ((Get-Date) -lt $DashDeadline) {
|
|
178
|
-
try {
|
|
179
|
-
$r = Invoke-WebRequest -Uri $DashUrl -UseBasicParsing -TimeoutSec 1 -ErrorAction Stop
|
|
180
|
-
if ($r.StatusCode -lt 500) {
|
|
181
|
-
Write-Ok "Dashboard ready — opening $DashUrl"
|
|
182
|
-
Start-Process $DashUrl # opens default browser on Windows
|
|
183
|
-
$Opened = $true
|
|
184
|
-
break
|
|
185
|
-
}
|
|
186
|
-
} catch { }
|
|
187
|
-
Start-Sleep -Milliseconds 1000
|
|
188
|
-
}
|
|
189
|
-
if (-not $Opened) {
|
|
190
|
-
Write-Warn "Dashboard did not respond within 60s — open $DashUrl manually."
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
# ── Wait ──────────────────────────────────────────────────────────────────────
|
|
196
|
-
Write-Host ""
|
|
197
|
-
Write-Ok "All processes started. Press Ctrl+C to stop."
|
|
198
|
-
Write-Host " Bridge PID : $($BridgeProc.Id)"
|
|
199
|
-
Write-Host " Claude PID : $($ClaudeProc.Id)"
|
|
200
|
-
if ($DashProc) { Write-Host " Dashboard : http://localhost:$DashboardPort (PID $($DashProc.Id))" }
|
|
201
|
-
Write-Host ""
|
|
202
|
-
|
|
203
|
-
try {
|
|
204
|
-
# Block until the bridge exits (primary process)
|
|
205
|
-
$BridgeProc.WaitForExit()
|
|
206
|
-
} finally {
|
|
207
|
-
Write-Status "Bridge exited — stopping all processes..."
|
|
208
|
-
Stop-AllJobs
|
|
209
|
-
}
|
|
1
|
+
#Requires -Version 5.1
|
|
2
|
+
<#
|
|
3
|
+
.SYNOPSIS
|
|
4
|
+
Windows orchestrator for bridge + Claude + Patchwork dashboard.
|
|
5
|
+
|
|
6
|
+
.DESCRIPTION
|
|
7
|
+
Cross-platform alternative to start-all.sh for native Windows (PowerShell/cmd).
|
|
8
|
+
Starts the bridge, waits for the lock file, launches Claude --ide, and
|
|
9
|
+
optionally starts the Patchwork dashboard dev server and opens it in the browser.
|
|
10
|
+
|
|
11
|
+
Run via npm:
|
|
12
|
+
npm run start:bridge # bridge only (simplest)
|
|
13
|
+
npm run start-all:win # full orchestrator (bridge + claude + dashboard)
|
|
14
|
+
|
|
15
|
+
Or directly:
|
|
16
|
+
pwsh -File scripts\start-all.ps1
|
|
17
|
+
pwsh -File scripts\start-all.ps1 --no-dashboard
|
|
18
|
+
pwsh -File scripts\start-all.ps1 --workspace C:\my\project --dashboard-port 3200
|
|
19
|
+
|
|
20
|
+
.PARAMETER Workspace
|
|
21
|
+
Directory to open in Claude (default: current directory).
|
|
22
|
+
|
|
23
|
+
.PARAMETER Full
|
|
24
|
+
Pass --full to the bridge, registering all ~95 tools including git/terminal/file ops.
|
|
25
|
+
Default is slim mode (27 IDE-exclusive tools).
|
|
26
|
+
|
|
27
|
+
.PARAMETER NoDashboard
|
|
28
|
+
Skip starting the Patchwork dashboard.
|
|
29
|
+
|
|
30
|
+
.PARAMETER DashboardPort
|
|
31
|
+
Port for the Next.js dashboard dev server (default: 3200).
|
|
32
|
+
|
|
33
|
+
.PARAMETER BridgePort
|
|
34
|
+
Port for the bridge (default: auto-assigned via lock file).
|
|
35
|
+
|
|
36
|
+
.PARAMETER Notify
|
|
37
|
+
ntfy.sh topic for push notifications (optional).
|
|
38
|
+
#>
|
|
39
|
+
[CmdletBinding()]
|
|
40
|
+
param(
|
|
41
|
+
[string]$Workspace = ".",
|
|
42
|
+
[switch]$Full,
|
|
43
|
+
[switch]$NoDashboard,
|
|
44
|
+
[int] $DashboardPort = 3200,
|
|
45
|
+
[int] $BridgePort = 0,
|
|
46
|
+
[string]$Notify = ""
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
Set-StrictMode -Version Latest
|
|
50
|
+
$ErrorActionPreference = "Stop"
|
|
51
|
+
|
|
52
|
+
# ── Resolve paths ─────────────────────────────────────────────────────────────
|
|
53
|
+
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
54
|
+
$BridgeDir = Split-Path -Parent $ScriptDir
|
|
55
|
+
$DashboardDir = Join-Path $BridgeDir "dashboard"
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
$Workspace = (Resolve-Path $Workspace).Path
|
|
59
|
+
} catch {
|
|
60
|
+
Write-Error "Workspace directory not found: $Workspace"
|
|
61
|
+
exit 1
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
65
|
+
function Write-Status($msg) { Write-Host "[orchestrator] $msg" -ForegroundColor Cyan }
|
|
66
|
+
function Write-Ok($msg) { Write-Host "[ok] $msg" -ForegroundColor Green }
|
|
67
|
+
function Write-Warn($msg) { Write-Host "[warn] $msg" -ForegroundColor Yellow }
|
|
68
|
+
|
|
69
|
+
function Send-Notify($msg) {
|
|
70
|
+
if (-not $Notify) { return }
|
|
71
|
+
try {
|
|
72
|
+
Invoke-RestMethod -Uri "https://ntfy.sh/$Notify" -Method Post -Body $msg -TimeoutSec 5 | Out-Null
|
|
73
|
+
} catch { Write-Warn "ntfy notification failed: $_" }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# ── Track child processes for cleanup ─────────────────────────────────────────
|
|
77
|
+
$Jobs = [System.Collections.Generic.List[System.Diagnostics.Process]]::new()
|
|
78
|
+
|
|
79
|
+
function Stop-AllJobs {
|
|
80
|
+
foreach ($p in $Jobs) {
|
|
81
|
+
if (-not $p.HasExited) {
|
|
82
|
+
try { $p.Kill($true) } catch { }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Clean up on Ctrl+C or exit
|
|
88
|
+
$null = Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action { Stop-AllJobs }
|
|
89
|
+
try { [Console]::TreatControlCAsInput = $false } catch { }
|
|
90
|
+
|
|
91
|
+
# ── Build bridge args ─────────────────────────────────────────────────────────
|
|
92
|
+
$BridgeArgs = @("--workspace", $Workspace)
|
|
93
|
+
if ($BridgePort -gt 0) { $BridgeArgs += @("--port", $BridgePort) }
|
|
94
|
+
if ($Full) { $BridgeArgs += "--full" }
|
|
95
|
+
|
|
96
|
+
# ── Start bridge ──────────────────────────────────────────────────────────────
|
|
97
|
+
Write-Status "Starting bridge (workspace: $Workspace)..."
|
|
98
|
+
|
|
99
|
+
$BridgeInfo = New-Object System.Diagnostics.ProcessStartInfo
|
|
100
|
+
$BridgeInfo.UseShellExecute = $false
|
|
101
|
+
# On Windows, npm global bins are .cmd wrappers — must invoke via cmd.exe.
|
|
102
|
+
# Quote each argument so workspace paths with spaces (e.g. "C:\Users\Jane Doe\...") survive.
|
|
103
|
+
$BridgeInfo.FileName = "cmd.exe"
|
|
104
|
+
$quotedArgs = $BridgeArgs | ForEach-Object { if ($_ -match '\s') { "`"$_`"" } else { $_ } }
|
|
105
|
+
$BridgeInfo.Arguments = "/c claude-ide-bridge " + ($quotedArgs -join " ")
|
|
106
|
+
|
|
107
|
+
$BridgeProc = [System.Diagnostics.Process]::Start($BridgeInfo)
|
|
108
|
+
$Jobs.Add($BridgeProc)
|
|
109
|
+
|
|
110
|
+
# ── Wait for lock file ────────────────────────────────────────────────────────
|
|
111
|
+
Write-Status "Waiting for bridge lock file..."
|
|
112
|
+
$ClaudeBase = if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $env:USERPROFILE ".claude" }
|
|
113
|
+
$IdeDir = Join-Path $ClaudeBase "ide"
|
|
114
|
+
$Deadline = (Get-Date).AddSeconds(30)
|
|
115
|
+
$LockFile = $null
|
|
116
|
+
|
|
117
|
+
while ((Get-Date) -lt $Deadline) {
|
|
118
|
+
$locks = Get-ChildItem -Path $IdeDir -Filter "*.lock" -ErrorAction SilentlyContinue |
|
|
119
|
+
Where-Object { $_.LastWriteTime -gt (Get-Date).AddSeconds(-60) }
|
|
120
|
+
if ($locks) {
|
|
121
|
+
# Find the lock that matches our workspace
|
|
122
|
+
foreach ($lf in $locks) {
|
|
123
|
+
try {
|
|
124
|
+
$content = Get-Content $lf.FullName -Raw | ConvertFrom-Json
|
|
125
|
+
if ($content.isBridge -and
|
|
126
|
+
($content.workspace -replace '\\','/' ) -eq ($Workspace -replace '\\','/')) {
|
|
127
|
+
$LockFile = $lf
|
|
128
|
+
$DetectedPort = [int][System.IO.Path]::GetFileNameWithoutExtension($lf.Name)
|
|
129
|
+
break
|
|
130
|
+
}
|
|
131
|
+
} catch { }
|
|
132
|
+
}
|
|
133
|
+
if ($LockFile) { break }
|
|
134
|
+
}
|
|
135
|
+
Start-Sleep -Milliseconds 200
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (-not $LockFile) {
|
|
139
|
+
Write-Error "Bridge lock file not written after 30s. Bridge may have failed to start."
|
|
140
|
+
Stop-AllJobs
|
|
141
|
+
exit 1
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
Write-Ok "Bridge ready on port $DetectedPort"
|
|
145
|
+
Send-Notify "Bridge started on port $DetectedPort"
|
|
146
|
+
|
|
147
|
+
# ── Start Claude --ide ────────────────────────────────────────────────────────
|
|
148
|
+
Write-Status "Starting claude --ide..."
|
|
149
|
+
$ClaudeInfo = New-Object System.Diagnostics.ProcessStartInfo
|
|
150
|
+
$ClaudeInfo.FileName = "cmd.exe"
|
|
151
|
+
$ClaudeInfo.Arguments = "/c claude --ide"
|
|
152
|
+
$ClaudeInfo.UseShellExecute = $false
|
|
153
|
+
$ClaudeProc = [System.Diagnostics.Process]::Start($ClaudeInfo)
|
|
154
|
+
$Jobs.Add($ClaudeProc)
|
|
155
|
+
|
|
156
|
+
# ── Start dashboard ───────────────────────────────────────────────────────────
|
|
157
|
+
$DashProc = $null
|
|
158
|
+
if (-not $NoDashboard) {
|
|
159
|
+
if (-not (Test-Path (Join-Path $DashboardDir "node_modules"))) {
|
|
160
|
+
Write-Warn "dashboard/node_modules not found. Run 'npm ci' in the dashboard directory first, or pass -NoDashboard."
|
|
161
|
+
} else {
|
|
162
|
+
Write-Status "Starting dashboard on http://localhost:$DashboardPort ..."
|
|
163
|
+
|
|
164
|
+
$env:PATCHWORK_BRIDGE_PORT = $DetectedPort
|
|
165
|
+
$DashInfo = New-Object System.Diagnostics.ProcessStartInfo
|
|
166
|
+
$DashInfo.FileName = "cmd.exe"
|
|
167
|
+
$DashInfo.Arguments = "/c npx next dev -p $DashboardPort"
|
|
168
|
+
$DashInfo.WorkingDirectory = $DashboardDir
|
|
169
|
+
$DashInfo.UseShellExecute = $false
|
|
170
|
+
$DashProc = [System.Diagnostics.Process]::Start($DashInfo)
|
|
171
|
+
$Jobs.Add($DashProc)
|
|
172
|
+
|
|
173
|
+
# Poll until Next.js answers, then open the browser
|
|
174
|
+
$DashUrl = "http://localhost:$DashboardPort"
|
|
175
|
+
$DashDeadline = (Get-Date).AddSeconds(60)
|
|
176
|
+
$Opened = $false
|
|
177
|
+
while ((Get-Date) -lt $DashDeadline) {
|
|
178
|
+
try {
|
|
179
|
+
$r = Invoke-WebRequest -Uri $DashUrl -UseBasicParsing -TimeoutSec 1 -ErrorAction Stop
|
|
180
|
+
if ($r.StatusCode -lt 500) {
|
|
181
|
+
Write-Ok "Dashboard ready — opening $DashUrl"
|
|
182
|
+
Start-Process $DashUrl # opens default browser on Windows
|
|
183
|
+
$Opened = $true
|
|
184
|
+
break
|
|
185
|
+
}
|
|
186
|
+
} catch { }
|
|
187
|
+
Start-Sleep -Milliseconds 1000
|
|
188
|
+
}
|
|
189
|
+
if (-not $Opened) {
|
|
190
|
+
Write-Warn "Dashboard did not respond within 60s — open $DashUrl manually."
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
# ── Wait ──────────────────────────────────────────────────────────────────────
|
|
196
|
+
Write-Host ""
|
|
197
|
+
Write-Ok "All processes started. Press Ctrl+C to stop."
|
|
198
|
+
Write-Host " Bridge PID : $($BridgeProc.Id)"
|
|
199
|
+
Write-Host " Claude PID : $($ClaudeProc.Id)"
|
|
200
|
+
if ($DashProc) { Write-Host " Dashboard : http://localhost:$DashboardPort (PID $($DashProc.Id))" }
|
|
201
|
+
Write-Host ""
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
# Block until the bridge exits (primary process)
|
|
205
|
+
$BridgeProc.WaitForExit()
|
|
206
|
+
} finally {
|
|
207
|
+
Write-Status "Bridge exited — stopping all processes..."
|
|
208
|
+
Stop-AllJobs
|
|
209
|
+
}
|