Start Comfy without a visible command prompt on the desktop.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-09-01 07:07:41 -05:00
co-authored by Cursor
parent 66f2db3738
commit fa32fb0a8e
2 changed files with 86 additions and 23 deletions
+74 -17
View File
@@ -2,8 +2,9 @@ import http from 'node:http'
import net from 'node:net'
import { execFile, spawn } from 'node:child_process'
import { promisify } from 'node:util'
import { readdirSync, existsSync, rmSync, readFileSync, openSync } from 'node:fs'
import { basename, join, resolve, relative, isAbsolute } from 'node:path'
import { readdirSync, existsSync, rmSync, readFileSync, openSync, mkdirSync, writeFileSync, unlinkSync } from 'node:fs'
import { basename, dirname, join, resolve, relative, isAbsolute } from 'node:path'
import { tmpdir } from 'node:os'
const execFileAsync = promisify(execFile)
const port = Number(process.env.COMFY_CONTROL_PORT || 8199)
@@ -235,10 +236,11 @@ function desktopShellCommand() {
async function pythonMainUp() {
try {
const { stdout } = await execFileAsync('wmic', [
'process', 'where', "name='python.exe'", 'get', 'commandline'
const { stdout } = await execFileAsync('powershell.exe', [
'-NoProfile', '-Command',
"Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match 'ComfyUI\\\\main.py|ComfyUI/main.py' } | Select-Object -First 1 -ExpandProperty ProcessId"
], { windowsHide: true, timeout: 6000 })
return /main\.py/i.test(String(stdout || ''))
return Boolean(String(stdout).trim())
} catch {
return false
}
@@ -268,6 +270,70 @@ async function pickListenPort() {
return preferred
}
function windowlessPython(pythonPath) {
if (process.platform !== 'win32') return pythonPath
if (!/python\.exe$/i.test(pythonPath)) return pythonPath
const pythonw = pythonPath.replace(/python\.exe$/i, 'pythonw.exe')
return existsSync(pythonw) ? pythonw : pythonPath
}
function vbsString(value) {
return `"${String(value).replace(/"/g, '""')}"`
}
function spawnViaWscript(file, args, opts = {}) {
const command = [file, ...args].map((part) => {
const text = String(part)
return /\s|"/.test(text) ? `"${text.replace(/"/g, '\\"')}"` : text
}).join(' ')
const lines = ['Set sh = CreateObject("WScript.Shell")']
if (opts.cwd) lines.push(`sh.CurrentDirectory = ${vbsString(opts.cwd)}`)
lines.push(`sh.Run ${vbsString(command)}, 0, False`)
const tmp = join(tmpdir(), `aigen-launch-${process.pid}-${Date.now()}.vbs`)
writeFileSync(tmp, `${lines.join('\r\n')}\r\n`)
const child = spawn('wscript.exe', ['//B', '//Nologo', tmp], {
detached: true,
stdio: 'ignore',
windowsHide: true
})
child.unref()
const cleanup = () => {
try { unlinkSync(tmp) } catch { /* ignore */ }
}
child.once('exit', cleanup)
setTimeout(cleanup, 15000)
return child
}
function spawnHidden(file, args, opts = {}) {
const exe = windowlessPython(file)
// python.exe + detached:true allocates an empty console on the active display.
// pythonw.exe is windowless. If it is missing, launch via hidden WScript.
if (process.platform === 'win32' && /python\.exe$/i.test(exe)) {
return spawnViaWscript(exe, args, opts)
}
const child = spawn(exe, args, {
detached: true,
stdio: opts.stdio || 'ignore',
windowsHide: true,
cwd: opts.cwd
})
child.unref()
return child
}
function spawnShellHidden(command, cwd) {
if (process.platform === 'win32') {
const match = String(command).match(/start\s+""\s+"([^"]+)"/i)
|| String(command).match(/^"([^"]+\.exe)"\s*$/i)
if (match) return spawnHidden(match[1], [], { cwd })
return spawnViaWscript(process.env.ComSpec || 'cmd.exe', ['/d', '/s', '/c', command], { cwd })
}
const child = spawn(command, { shell: true, detached: true, stdio: 'ignore', cwd })
child.unref()
return child
}
async function startComfy() {
markWork()
const launch = resolveHeadlessLaunch()
@@ -277,15 +343,13 @@ async function startComfy() {
if (portIdx >= 0) launch.args[portIdx + 1] = String(port)
let stdio = 'ignore'
try {
mkdirSync(dirname(launch.log), { recursive: true })
stdio = ['ignore', openSync(launch.log, 'a'), openSync(launch.log, 'a')]
} catch { /* ignore */ }
const child = spawn(launch.file, launch.args, {
detached: true,
const child = spawnHidden(launch.file, launch.args, {
stdio,
windowsHide: true,
cwd: launch.cwd
})
child.unref()
console.log(JSON.stringify({
src: 'comfy-host-agent',
event: 'start',
@@ -302,14 +366,7 @@ async function startComfy() {
cwd: launch?.cwd || process.env.COMFY_LAUNCH_CWD || undefined,
mode: launch?.mode || 'desktop-shell'
}
const child = spawn(fallback.command, {
shell: true,
detached: true,
stdio: 'ignore',
windowsHide: true,
cwd: fallback.cwd
})
child.unref()
spawnShellHidden(fallback.command, fallback.cwd)
console.log(JSON.stringify({
src: 'comfy-host-agent',
event: 'start',
+8 -2
View File
@@ -226,9 +226,15 @@ async function processRunning() {
function spawnLaunchCommand(command: string) {
log('spawn', { command: command.slice(0, 180) })
const child = spawn(command, {
const win = process.platform === 'win32'
const exe = win
? command.match(/start\s+""\s+"([^"]+)"/i)?.[1] || command.match(/^"([^"]+\.exe)"\s*$/i)?.[1]
: ''
const child = exe
? spawn(exe, [], { detached: true, stdio: 'ignore', windowsHide: true })
: spawn(command, {
shell: true,
detached: true,
detached: !win,
stdio: 'ignore',
windowsHide: true
})