Fix Comfy wake: launch when HTTP is down instead of treating leftover Python as already booting.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-09-02 07:58:50 -05:00
co-authored by Cursor
parent aa2cefbcf2
commit 2ae5f6fb2e
3 changed files with 28 additions and 17 deletions
+23 -12
View File
@@ -16,6 +16,7 @@ const trainingUrl = String(process.env.TRAINING_CONTROL_URL || 'http://127.0.0.1
let lastWorkAt = Date.now() let lastWorkAt = Date.now()
let lastQueueRunning = 0 let lastQueueRunning = 0
let lastLaunchAt = 0
let stoppedByAgent = false let stoppedByAgent = false
let lastHealthyPort = 0 let lastHealthyPort = 0
let lastProcessUp = false let lastProcessUp = false
@@ -162,12 +163,23 @@ function ensureProxy(targetPort) {
}) })
} }
function markAsleep() {
stoppedByAgent = true
lastQueueRunning = 0
lastHealthyPort = 0
lastProcessUp = false
lastLaunchAt = 0
proxyTarget = 0
}
async function syncProxy() { async function syncProxy() {
const healthyPort = await findHealthyPort() const healthyPort = await findHealthyPort()
lastHealthyPort = healthyPort || 0 lastHealthyPort = healthyPort || 0
if (healthyPort) { if (healthyPort) {
ensureProxy(healthyPort) ensureProxy(healthyPort)
lastProcessUp = true lastProcessUp = true
} else {
lastProcessUp = false
} }
return healthyPort return healthyPort
} }
@@ -493,10 +505,7 @@ async function maybeIdleStop(healthyPort) {
http: Boolean(healthyPort) http: Boolean(healthyPort)
})) }))
const killed = await stopComfyProcesses() const killed = await stopComfyProcesses()
stoppedByAgent = true markAsleep()
lastQueueRunning = 0
lastHealthyPort = 0
lastProcessUp = false
console.log(JSON.stringify({ src: 'comfy-host-agent', event: 'stopped', reason: 'idle', killed })) console.log(JSON.stringify({ src: 'comfy-host-agent', event: 'stopped', reason: 'idle', killed }))
} }
@@ -617,14 +626,18 @@ const server = http.createServer(async (req, res) => {
if (req.method === 'GET' && url.pathname === '/status') { if (req.method === 'GET' && url.pathname === '/status') {
// Answer immediately. MyMonitor allows ~500ms; probing ports + WMI here // Answer immediately. MyMonitor allows ~500ms; probing ports + WMI here
// made the A light stay red even while this process was running. // made the A light stay red even while this process was running.
const healthyPort = lastHealthyPort || proxyTarget // Do not treat a stale proxyTarget as proof Comfy is up — that made
const processUpNow = Boolean(lastProcessUp || healthyPort) // /status report asleep+http at once, so training skipped /stop.
const healthyPort = lastHealthyPort || 0
const processUpNow = Boolean(lastProcessUp && healthyPort)
const asleep = !healthyPort && !processUpNow
if (!asleep) stoppedByAgent = false
return json(res, 200, { return json(res, 200, {
ok: true, ok: true,
http: Boolean(healthyPort), http: Boolean(healthyPort),
process: processUpNow, process: processUpNow,
processUp: processUpNow, processUp: processUpNow,
asleep: Boolean(stoppedByAgent || (!healthyPort && !processUpNow)), asleep,
lastActivityAt: new Date(lastWorkAt).toISOString(), lastActivityAt: new Date(lastWorkAt).toISOString(),
idleMs, idleMs,
port: healthyPort || null, port: healthyPort || null,
@@ -647,11 +660,12 @@ const server = http.createServer(async (req, res) => {
markWork() markWork()
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 })
} }
if (await pythonMainUp()) { if (lastLaunchAt && Date.now() - lastLaunchAt < 60_000) {
return json(res, 200, { ok: true, started: false, already: true, booting: true, asleep: false }) return json(res, 200, { ok: true, started: false, already: true, booting: true, asleep: false })
} }
await taskkillImage('Comfy Desktop.exe') await taskkillImage('Comfy Desktop.exe')
stoppedByAgent = false stoppedByAgent = false
lastLaunchAt = Date.now()
const launched = await startComfy() const launched = await startComfy()
if (launched.port) { if (launched.port) {
lastProcessUp = true lastProcessUp = true
@@ -671,10 +685,7 @@ const server = http.createServer(async (req, res) => {
}) })
} }
const killed = await stopComfyProcesses() const killed = await stopComfyProcesses()
stoppedByAgent = true markAsleep()
lastQueueRunning = 0
lastHealthyPort = 0
lastProcessUp = false
console.log(JSON.stringify({ src: 'comfy-host-agent', event: 'stopped', reason: 'stop', killed })) console.log(JSON.stringify({ src: 'comfy-host-agent', event: 'stopped', reason: 'stop', killed }))
return json(res, 200, { ok: true, stopped: true, asleep: true, killed }) return json(res, 200, { ok: true, stopped: true, asleep: true, killed })
} }
+2 -2
View File
@@ -154,7 +154,7 @@ export async function requestComfyWake() {
statusMessage: 'COMFY_CONTROL_URL is not set. The studio cannot poke the Beast host agent.' statusMessage: 'COMFY_CONTROL_URL is not set. The studio cannot poke the Beast host agent.'
}) })
} }
const remote = await controlRequest('/start', 'POST') const remote = await controlRequest('/start', 'POST', 25_000)
if (!remote) { if (!remote) {
throw createError({ throw createError({
statusCode: 503, statusCode: 503,
@@ -263,7 +263,7 @@ async function startComfy() {
} }
lastStartAt = now lastStartAt = now
const remote = await controlRequest('/start', 'POST') const remote = await controlRequest('/start', 'POST', 25_000)
throwIfTrainingBusy(remote) throwIfTrainingBusy(remote)
if (remote?.ok || remote?.started) { if (remote?.ok || remote?.started) {
log('start-remote', { pid: remote.pid || null }) log('start-remote', { pid: remote.pid || null })
+3 -3
View File
@@ -88,7 +88,7 @@ export async function fetchImageQueue() {
return fetchQueueAt(getImageComfyHost()) return fetchQueueAt(getImageComfyHost())
} }
async function controlRequest(path: string, method = 'GET') { async function controlRequest(path: string, method = 'GET', timeoutMs = 5000) {
const { controlUrl, controlToken } = settings() const { controlUrl, controlToken } = settings()
if (!controlUrl) return null if (!controlUrl) return null
try { try {
@@ -98,7 +98,7 @@ async function controlRequest(path: string, method = 'GET') {
Accept: 'application/json', Accept: 'application/json',
...(controlToken ? { Authorization: `Bearer ${controlToken}` } : {}) ...(controlToken ? { Authorization: `Bearer ${controlToken}` } : {})
}, },
signal: AbortSignal.timeout(5000) signal: AbortSignal.timeout(timeoutMs)
}) })
if (!res.ok) return { ok: false, status: res.status } if (!res.ok) return { ok: false, status: res.status }
return await res.json() as { return await res.json() as {
@@ -154,7 +154,7 @@ async function startImageComfy() {
return { started: false, reason: 'recent-start' as const } return { started: false, reason: 'recent-start' as const }
} }
lastStartAt = now lastStartAt = now
const remote = await controlRequest('/start', 'POST') const remote = await controlRequest('/start', 'POST', 25_000)
if (remote?.ok || remote?.started) { if (remote?.ok || remote?.started) {
log('start-remote', { port: remote.port || null }) log('start-remote', { port: remote.port || null })
return { started: true, reason: 'remote' as const } return { started: true, reason: 'remote' as const }