fix(windows): чёрные окна консоли выскакивали каждую минуту
Хаб — оконное приложение без консоли, поэтому каждый запуск консольного 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 <noreply@anthropic.com>
This commit is contained in:
parent
d17360bc2f
commit
f0d06e4994
5 changed files with 32 additions and 1 deletions
|
|
@ -162,6 +162,7 @@ def discover_models(profile_id: str | None = None) -> dict[str, str]:
|
||||||
errors="replace",
|
errors="replace",
|
||||||
env=env,
|
env=env,
|
||||||
stdin=subprocess.DEVNULL,
|
stdin=subprocess.DEVNULL,
|
||||||
|
**hidden_process_kwargs(),
|
||||||
)
|
)
|
||||||
raw = result.stdout.strip()
|
raw = result.stdout.strip()
|
||||||
if not raw or result.returncode != 0:
|
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(
|
def build_safe_subprocess_env(
|
||||||
base_env: dict[str, str] | None = None,
|
base_env: dict[str, str] | None = None,
|
||||||
allow_extra_keys: set[str] | list[str] | None = None,
|
allow_extra_keys: set[str] | list[str] | None = None,
|
||||||
|
|
@ -748,6 +769,7 @@ def agy_generate(
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
errors="replace",
|
errors="replace",
|
||||||
env=custom_env if custom_env is not None else build_safe_subprocess_env(),
|
env=custom_env if custom_env is not None else build_safe_subprocess_env(),
|
||||||
|
**hidden_process_kwargs(),
|
||||||
)
|
)
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
return _error_completion(model_raw, "agy subprocess timed out")
|
return _error_completion(model_raw, "agy subprocess timed out")
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import tempfile
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable
|
from typing import Any, Callable
|
||||||
|
from antigravity_provider.agy_subprocess import hidden_process_kwargs
|
||||||
|
|
||||||
|
|
||||||
def _hermes_home() -> Path:
|
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"],
|
["security", "find-generic-password", "-a", "antigravity", "-s", "gemini", "-w"],
|
||||||
stderr=subprocess.DEVNULL,
|
stderr=subprocess.DEVNULL,
|
||||||
timeout=5,
|
timeout=5,
|
||||||
|
**hidden_process_kwargs(),
|
||||||
).decode("utf-8")
|
).decode("utf-8")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ from pathlib import Path
|
||||||
from typing import Any, Dict, Optional, Tuple
|
from typing import Any, Dict, Optional, Tuple
|
||||||
|
|
||||||
from antigravity_provider.router.profile_manager import ProfileAuthManager, mask_email
|
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")
|
logger = logging.getLogger("hermes.router.codex_oauth")
|
||||||
|
|
||||||
|
|
@ -380,6 +381,7 @@ def stop_running_codex_processes() -> list[int]:
|
||||||
out = subprocess.check_output(
|
out = subprocess.check_output(
|
||||||
["tasklist", "/FI", f"IMAGENAME eq {proc_name}", "/FO", "CSV", "/NH"],
|
["tasklist", "/FI", f"IMAGENAME eq {proc_name}", "/FO", "CSV", "/NH"],
|
||||||
stderr=subprocess.DEVNULL,
|
stderr=subprocess.DEVNULL,
|
||||||
|
**hidden_process_kwargs(),
|
||||||
text=True,
|
text=True,
|
||||||
)
|
)
|
||||||
for line in out.strip().splitlines():
|
for line in out.strip().splitlines():
|
||||||
|
|
@ -389,7 +391,7 @@ def stop_running_codex_processes() -> list[int]:
|
||||||
pid_str = parts[1].strip('"')
|
pid_str = parts[1].strip('"')
|
||||||
if pid_str.isdigit():
|
if pid_str.isdigit():
|
||||||
pid = int(pid_str)
|
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)
|
stopped_pids.append(pid)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import sys
|
||||||
import traceback
|
import traceback
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List, Tuple
|
from typing import Dict, List, Tuple
|
||||||
|
from antigravity_provider.agy_subprocess import hidden_process_kwargs
|
||||||
|
|
||||||
|
|
||||||
def get_startup_log_path() -> Path:
|
def get_startup_log_path() -> Path:
|
||||||
|
|
@ -96,6 +97,7 @@ def self_heal_dependencies(missing_packages: List[str]) -> Tuple[bool, str]:
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=90,
|
timeout=90,
|
||||||
|
**hidden_process_kwargs(),
|
||||||
)
|
)
|
||||||
if res.returncode == 0:
|
if res.returncode == 0:
|
||||||
log_startup("Self-healing successful. Re-verifying package imports...")
|
log_startup("Self-healing successful. Re-verifying package imports...")
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ from typing import Any, Callable, Dict, Optional, Tuple
|
||||||
|
|
||||||
from antigravity_provider import paths
|
from antigravity_provider import paths
|
||||||
from antigravity_provider.version import __version__, CHANNEL, MINIMUM_HERMES_VERSION
|
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")
|
logger = logging.getLogger("hermes.hub.updater")
|
||||||
|
|
||||||
|
|
@ -119,6 +120,7 @@ def get_installed_commit() -> str:
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=5,
|
timeout=5,
|
||||||
|
**hidden_process_kwargs(),
|
||||||
)
|
)
|
||||||
if res.returncode == 0 and res.stdout.strip():
|
if res.returncode == 0 and res.stdout.strip():
|
||||||
return res.stdout.strip()
|
return res.stdout.strip()
|
||||||
|
|
@ -778,6 +780,7 @@ class UpdateManager:
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=15,
|
timeout=15,
|
||||||
|
**hidden_process_kwargs(),
|
||||||
)
|
)
|
||||||
if res.returncode != 0 or "OK" not in res.stdout:
|
if res.returncode != 0 or "OK" not in res.stdout:
|
||||||
raise RuntimeError(f"Post-update verification failed: {res.stderr or res.stdout}")
|
raise RuntimeError(f"Post-update verification failed: {res.stderr or res.stdout}")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue