From f0d06e499449564b3fb19c80a8bcd862ea895594 Mon Sep 17 00:00:00 2001 From: Hermes Team Date: Mon, 31 Aug 2026 19:39:12 +0700 Subject: [PATCH] =?UTF-8?q?fix(windows):=20=D1=87=D1=91=D1=80=D0=BD=D1=8B?= =?UTF-8?q?=D0=B5=20=D0=BE=D0=BA=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=BD=D1=81?= =?UTF-8?q?=D0=BE=D0=BB=D0=B8=20=D0=B2=D1=8B=D1=81=D0=BA=D0=B0=D0=BA=D0=B8?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB=D0=B8=20=D0=BA=D0=B0=D0=B6=D0=B4=D1=83=D1=8E?= =?UTF-8?q?=20=D0=BC=D0=B8=D0=BD=D1=83=D1=82=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Хаб — оконное приложение без консоли, поэтому каждый запуск консольного exe открывал отдельное окно. Пока проверка аккаунтов шла по нажатию, это было незаметно. A50 сделал проверку автоматической раз в минуту, и окна agy.exe стали появляться постоянно, мешая работе. Добавлен hidden_process_kwargs(): CREATE_NO_WINDOW плюс STARTUPINFO с SW_HIDE, на не-Windows пусто. Применён ко всем ФОНОВЫМ вызовам: опрос моделей agy, выполнение запроса agy, чтение ключей, codex_oauth, launcher_bootstrap, git rev-parse и проверка версии в обновлении. Вызовы, где окно нужно видимым, не тронуты: вход по OAuth сознательно использует CREATE_NEW_CONSOLE, запуск установщика тоже должен быть виден. 574 passed, ruff clean. Co-Authored-By: Claude Opus 5 --- src/antigravity_provider/agy_subprocess.py | 22 +++++++++++++++++++ src/antigravity_provider/credentials.py | 2 ++ .../router/codex_oauth.py | 4 +++- .../router/launcher_bootstrap.py | 2 ++ .../updater/update_manager.py | 3 +++ 5 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/antigravity_provider/agy_subprocess.py b/src/antigravity_provider/agy_subprocess.py index 4fd1231..da1d12e 100644 --- a/src/antigravity_provider/agy_subprocess.py +++ b/src/antigravity_provider/agy_subprocess.py @@ -162,6 +162,7 @@ def discover_models(profile_id: str | None = None) -> dict[str, str]: errors="replace", env=env, stdin=subprocess.DEVNULL, + **hidden_process_kwargs(), ) raw = result.stdout.strip() if not raw or result.returncode != 0: @@ -618,6 +619,26 @@ BLOCKED_SECRET_PATTERNS: tuple[str, ...] = ( ) +def hidden_process_kwargs() -> dict: + """Флаги запуска подпроцесса без видимого окна консоли (только Windows). + + Хаб — оконное приложение без консоли, поэтому каждый запуск консольного + exe (agy.exe и прочие) открывал отдельное чёрное окно. Пока проверка шла + по нажатию, это было незаметно. После A50 проверка аккаунтов запускается + сама раз в минуту, и окна стали появляться постоянно, мешая работе. + + Применять ко всем ФОНОВЫМ вызовам. Для входа по OAuth окно нужно + видимым — там сознательно используется CREATE_NEW_CONSOLE. + """ + if os.name != "nt": + return {} + flags = getattr(subprocess, "CREATE_NO_WINDOW", 0x08000000) + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = subprocess.SW_HIDE + return {"creationflags": flags, "startupinfo": startupinfo} + + def build_safe_subprocess_env( base_env: dict[str, str] | None = None, allow_extra_keys: set[str] | list[str] | None = None, @@ -748,6 +769,7 @@ def agy_generate( encoding="utf-8", errors="replace", env=custom_env if custom_env is not None else build_safe_subprocess_env(), + **hidden_process_kwargs(), ) except subprocess.TimeoutExpired: return _error_completion(model_raw, "agy subprocess timed out") diff --git a/src/antigravity_provider/credentials.py b/src/antigravity_provider/credentials.py index 274cc8e..c565249 100644 --- a/src/antigravity_provider/credentials.py +++ b/src/antigravity_provider/credentials.py @@ -9,6 +9,7 @@ import tempfile from datetime import datetime from pathlib import Path from typing import Any, Callable +from antigravity_provider.agy_subprocess import hidden_process_kwargs def _hermes_home() -> Path: @@ -94,6 +95,7 @@ def load_agy_keychain_credentials(*, runner: Callable[[], str] | None = None) -> ["security", "find-generic-password", "-a", "antigravity", "-s", "gemini", "-w"], stderr=subprocess.DEVNULL, timeout=5, + **hidden_process_kwargs(), ).decode("utf-8") try: diff --git a/src/antigravity_provider/router/codex_oauth.py b/src/antigravity_provider/router/codex_oauth.py index 171006f..a7dc6bb 100644 --- a/src/antigravity_provider/router/codex_oauth.py +++ b/src/antigravity_provider/router/codex_oauth.py @@ -25,6 +25,7 @@ from pathlib import Path from typing import Any, Dict, Optional, Tuple from antigravity_provider.router.profile_manager import ProfileAuthManager, mask_email +from antigravity_provider.agy_subprocess import hidden_process_kwargs logger = logging.getLogger("hermes.router.codex_oauth") @@ -380,6 +381,7 @@ def stop_running_codex_processes() -> list[int]: out = subprocess.check_output( ["tasklist", "/FI", f"IMAGENAME eq {proc_name}", "/FO", "CSV", "/NH"], stderr=subprocess.DEVNULL, + **hidden_process_kwargs(), text=True, ) for line in out.strip().splitlines(): @@ -389,7 +391,7 @@ def stop_running_codex_processes() -> list[int]: pid_str = parts[1].strip('"') if pid_str.isdigit(): pid = int(pid_str) - subprocess.run(["taskkill", "/F", "/PID", str(pid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.run(["taskkill", "/F", "/PID", str(pid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, **hidden_process_kwargs()) stopped_pids.append(pid) except Exception: pass diff --git a/src/antigravity_provider/router/launcher_bootstrap.py b/src/antigravity_provider/router/launcher_bootstrap.py index 37619d2..8da2666 100644 --- a/src/antigravity_provider/router/launcher_bootstrap.py +++ b/src/antigravity_provider/router/launcher_bootstrap.py @@ -21,6 +21,7 @@ import sys import traceback from pathlib import Path from typing import Dict, List, Tuple +from antigravity_provider.agy_subprocess import hidden_process_kwargs def get_startup_log_path() -> Path: @@ -96,6 +97,7 @@ def self_heal_dependencies(missing_packages: List[str]) -> Tuple[bool, str]: capture_output=True, text=True, timeout=90, + **hidden_process_kwargs(), ) if res.returncode == 0: log_startup("Self-healing successful. Re-verifying package imports...") diff --git a/src/antigravity_provider/updater/update_manager.py b/src/antigravity_provider/updater/update_manager.py index 14aeb35..46cc4a8 100644 --- a/src/antigravity_provider/updater/update_manager.py +++ b/src/antigravity_provider/updater/update_manager.py @@ -28,6 +28,7 @@ from typing import Any, Callable, Dict, Optional, Tuple from antigravity_provider import paths from antigravity_provider.version import __version__, CHANNEL, MINIMUM_HERMES_VERSION +from antigravity_provider.agy_subprocess import hidden_process_kwargs logger = logging.getLogger("hermes.hub.updater") @@ -119,6 +120,7 @@ def get_installed_commit() -> str: capture_output=True, text=True, timeout=5, + **hidden_process_kwargs(), ) if res.returncode == 0 and res.stdout.strip(): return res.stdout.strip() @@ -778,6 +780,7 @@ class UpdateManager: capture_output=True, text=True, timeout=15, + **hidden_process_kwargs(), ) if res.returncode != 0 or "OK" not in res.stdout: raise RuntimeError(f"Post-update verification failed: {res.stderr or res.stdout}")