fix windows desktop terminal popups (#1199)

This commit is contained in:
ekko
2026-06-01 12:20:10 +08:00
committed by GitHub
parent aa7c1c4fbb
commit 022e18dc8f
5 changed files with 87 additions and 20 deletions
+1 -4
View File
@@ -231,9 +231,6 @@ export async function startWebUiServer(port = DEFAULT_PORT): Promise<string> {
const bundledPython = isWin
? join(pythonDir(), 'python.exe')
: join(pythonDir(), 'bin', 'python3')
const bundledPythonNoWindow = isWin
? join(pythonDir(), 'pythonw.exe')
: bundledPython
const bridgePort = await getFreeTcpPort()
const workerPortBase = await getFreeTcpPortInRange(20000, 59000)
const loginShellPath = await getLoginShellPath()
@@ -257,7 +254,7 @@ export async function startWebUiServer(port = DEFAULT_PORT): Promise<string> {
// ready handshakes. Use python.exe on Windows and hide windows at the
// process creation layer instead of switching the bridge to pythonw.exe.
HERMES_AGENT_BRIDGE_PYTHON: bundledPython,
HERMES_AGENT_CLI_PYTHON: existsSync(bundledPythonNoWindow) ? bundledPythonNoWindow : bundledPython,
HERMES_AGENT_CLI_PYTHON: bundledPython,
HERMES_AGENT_ROOT: pythonDir(),
// Force TCP loopback for the agent bridge. The default `ipc:///tmp/...`
// unix socket is rejected on macOS in some EDR/sandbox setups (silent
@@ -10,6 +10,7 @@ delimited JSON request/response protocol over a local socket.
from __future__ import annotations
import argparse
import asyncio
import atexit
import copy
import errno
@@ -71,6 +72,14 @@ def _hidden_subprocess_kwargs() -> dict[str, int]:
return {"creationflags": getattr(subprocess, "CREATE_NO_WINDOW", 0)}
def _add_hidden_creationflags(kwargs: dict[str, Any], create_no_window: int) -> None:
flags = kwargs.get("creationflags", 0) or 0
try:
kwargs["creationflags"] = int(flags) | create_no_window
except Exception:
kwargs["creationflags"] = create_no_window
def _install_windows_hidden_subprocess_defaults() -> None:
"""Hide console windows for subprocesses launched inside desktop bridge runs.
@@ -87,18 +96,26 @@ def _install_windows_hidden_subprocess_defaults() -> None:
return
original_popen = subprocess.Popen
original_create_subprocess_exec = asyncio.create_subprocess_exec
original_create_subprocess_shell = asyncio.create_subprocess_shell
create_no_window = getattr(subprocess, "CREATE_NO_WINDOW", 0) or 0x08000000
class HiddenPopen(original_popen): # type: ignore[misc, valid-type]
def __init__(self, *args: Any, **kwargs: Any) -> None:
flags = kwargs.get("creationflags", 0) or 0
try:
kwargs["creationflags"] = int(flags) | create_no_window
except Exception:
kwargs["creationflags"] = create_no_window
_add_hidden_creationflags(kwargs, create_no_window)
super().__init__(*args, **kwargs)
async def hidden_create_subprocess_exec(*args: Any, **kwargs: Any) -> Any:
_add_hidden_creationflags(kwargs, create_no_window)
return await original_create_subprocess_exec(*args, **kwargs)
async def hidden_create_subprocess_shell(*args: Any, **kwargs: Any) -> Any:
_add_hidden_creationflags(kwargs, create_no_window)
return await original_create_subprocess_shell(*args, **kwargs)
subprocess.Popen = HiddenPopen # type: ignore[assignment]
asyncio.create_subprocess_exec = hidden_create_subprocess_exec # type: ignore[assignment]
asyncio.create_subprocess_shell = hidden_create_subprocess_shell # type: ignore[assignment]
subprocess._hermes_hidden_defaults_installed = True # type: ignore[attr-defined]
@@ -22,12 +22,15 @@ function bundledCliPythonForWindows(hermesBin: string): string | null {
if (envPython) return envPython
if (basename(hermesBin).toLowerCase() !== 'hermes.exe') return null
const pythonw = resolve(dirname(hermesBin), '..', 'pythonw.exe')
if (existsSync(pythonw)) return pythonw
const python = resolve(dirname(hermesBin), '..', 'python.exe')
return existsSync(python) ? python : null
}
function withWindowsHide<T extends ExecFileOptions | SpawnOptions>(options?: T): T {
if (process.platform !== 'win32') return (options || {}) as T
return { windowsHide: true, ...(options || {}) } as T
}
export function resolveHermesInvocation(hermesBin = resolveHermesBin()): HermesInvocation {
if (process.platform === 'win32') {
const python = bundledCliPythonForWindows(hermesBin)
@@ -47,7 +50,7 @@ export function execHermesWithBin(
execFile(
invocation.command,
[...invocation.argsPrefix, ...args],
{ ...options, encoding: 'utf8' },
{ ...withWindowsHide(options), encoding: 'utf8' },
(error, stdout, stderr) => {
if (error) {
rejectExec(Object.assign(error, { stdout, stderr }))
@@ -69,7 +72,7 @@ export function spawnHermesWithBin(
options?: SpawnOptions,
): ChildProcess {
const invocation = resolveHermesInvocation(hermesBin)
return spawn(invocation.command, [...invocation.argsPrefix, ...args], options || {})
return spawn(invocation.command, [...invocation.argsPrefix, ...args], withWindowsHide(options))
}
export function spawnHermes(args: readonly string[], options?: SpawnOptions): ChildProcess {