Fix Comfy poke never starting after force-stop.

The host agent PowerShell process probe matched its own command line, so /start always returned already/booting and never launched Comfy.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-09-03 21:38:06 -05:00
co-authored by Cursor
parent 5df9e2d8ac
commit 7da9e66f43
+15 -7
View File
@@ -183,6 +183,7 @@ function markAsleep() {
lastHealthyPort = 0 lastHealthyPort = 0
lastProcessUp = false lastProcessUp = false
lastLaunchAt = 0 lastLaunchAt = 0
probeSkipUntil.clear()
setProxyTarget(0) setProxyTarget(0)
} }
@@ -199,6 +200,11 @@ async function syncProxy() {
return healthyPort return healthyPort
} }
// PowerShell -Command text used to contain the literal main.py path, so the
// probe process matched itself and /start forever returned already/booting.
const PS_COMFY_MAIN =
"Get-CimInstance Win32_Process | Where-Object { $_.Name -match '^python(w)?\\.exe$' -and $_.CommandLine -match ('ComfyUI' + '[\\\\/]main\\.py') } | Select-Object -First 1 -ExpandProperty ProcessId"
async function processUp() { async function processUp() {
try { try {
const { stdout } = await execFileAsync('tasklist', ['/FO', 'CSV', '/NH'], { windowsHide: true, timeout: 4000 }) const { stdout } = await execFileAsync('tasklist', ['/FO', 'CSV', '/NH'], { windowsHide: true, timeout: 4000 })
@@ -207,7 +213,7 @@ async function processUp() {
try { try {
const { stdout } = await execFileAsync('powershell.exe', [ const { stdout } = await execFileAsync('powershell.exe', [
'-NoProfile', '-Command', '-NoProfile', '-Command',
"Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match 'ComfyUI\\\\main.py|ComfyUI/main.py' } | Select-Object -First 1 -ExpandProperty ProcessId" PS_COMFY_MAIN
], { windowsHide: true, timeout: 6000 }) ], { windowsHide: true, timeout: 6000 })
return Boolean(String(stdout).trim()) return Boolean(String(stdout).trim())
} catch { } catch {
@@ -299,7 +305,7 @@ async function pythonMainUp() {
if (!/python(?:w)?\.exe/i.test(stdout)) return false if (!/python(?:w)?\.exe/i.test(stdout)) return false
const { stdout: verbose } = await execFileAsync('powershell.exe', [ const { stdout: verbose } = await execFileAsync('powershell.exe', [
'-NoProfile', '-Command', '-NoProfile', '-Command',
"Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match 'ComfyUI\\\\main.py|ComfyUI/main.py' } | Select-Object -First 1 -ExpandProperty ProcessId" PS_COMFY_MAIN
], { windowsHide: true, timeout: 2500 }) ], { windowsHide: true, timeout: 2500 })
return Boolean(String(verbose).trim()) return Boolean(String(verbose).trim())
} catch { } catch {
@@ -482,7 +488,7 @@ async function stopComfyProcesses() {
try { try {
const { stdout } = await execFileAsync('powershell.exe', [ const { stdout } = await execFileAsync('powershell.exe', [
'-NoProfile', '-Command', '-NoProfile', '-Command',
"Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match 'ComfyUI\\\\main.py|ComfyUI/main.py' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue; $_.ProcessId }" "Get-CimInstance Win32_Process | Where-Object { $_.Name -match '^python(w)?\\.exe$' -and $_.CommandLine -match ('ComfyUI' + '[\\\\/]main\\.py') } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue; $_.ProcessId }"
], { windowsHide: true, timeout: 8000 }) ], { windowsHide: true, timeout: 8000 })
if (String(stdout).trim()) killed.push('main.py') if (String(stdout).trim()) killed.push('main.py')
} catch { /* ignore */ } } catch { /* ignore */ }
@@ -836,10 +842,10 @@ const server = http.createServer(async (req, res) => {
return json(res, 200, { ok: true, started: false, already: true, asleep: false, port: healthyPort, proxyPort }) return json(res, 200, { ok: true, started: false, already: true, asleep: false, port: healthyPort, proxyPort })
} }
const listenPort = desktopListenPort() const listenPort = desktopListenPort()
const processAlive = lastProcessUp || await processUp().catch(() => false) || await pythonMainUp().catch(() => false) // Do not trust lastProcessUp alone — force-stop can leave it sticky while Comfy is dead.
const processAlive = await processUp().catch(() => false) || await pythonMainUp().catch(() => false)
const portBusy = await portInUse(listenPort) const portBusy = await portInUse(listenPort)
const recentlySlow = [...probeSkipUntil.entries()].some(([, until]) => Date.now() < until) if (processAlive || portBusy) {
if (processAlive || portBusy || recentlySlow) {
markWork() markWork()
lastProcessUp = true lastProcessUp = true
if (portBusy) setProxyTarget(listenPort) if (portBusy) setProxyTarget(listenPort)
@@ -853,10 +859,12 @@ const server = http.createServer(async (req, res) => {
proxyPort proxyPort
}) })
} }
if (lastLaunchAt && Date.now() - lastLaunchAt < 5 * 60_000) { // Only honor launch cooldown if we actually spawned recently; never block a real cold start.
if (lastLaunchAt && Date.now() - lastLaunchAt < 45_000 && await processUp().catch(() => false)) {
return json(res, 200, { ok: true, started: false, already: true, booting: true, asleep: false, proxyPort }) return json(res, 200, { ok: true, started: false, already: true, booting: true, asleep: false, proxyPort })
} }
stoppedByAgent = false stoppedByAgent = false
probeSkipUntil.clear()
lastLaunchAt = Date.now() lastLaunchAt = Date.now()
const launched = await startComfy() const launched = await startComfy()
if (launched.port) { if (launched.port) {