Start Comfy without a visible command prompt on the desktop.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user