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}")