refactor(hermes_cli): hug lone closing brackets (AST-neutral) in desktop/dashboard/web_build/tui_launch

This commit is contained in:
Teknium
2026-09-02 22:01:17 -07:00
parent 616d7c3c2a
commit 46786a8138
4 changed files with 39 additions and 78 deletions

View File

@@ -70,8 +70,7 @@ _SYSTEMCTL_ERRORS = (FileNotFoundError, subprocess.TimeoutExpired, OSError)
def _run_probe(cmd: list[str], *, timeout: int) -> subprocess.CompletedProcess:
"""Captured, text-decoded ``subprocess.run`` for short local probes (systemctl, ps)."""
return subprocess.run(
cmd, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=timeout
)
cmd, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=timeout)
def _restart_managed_dashboard_service(reason: str, unit: str = _DASHBOARD_SYSTEMD_UNIT) -> bool:
@@ -263,8 +262,7 @@ def _respawn_dashboard_processes(commands: list[list[str]]) -> list[list[str]]:
with open(log_path, "ab") as log_f:
subprocess.Popen(
command, stdin=subprocess.DEVNULL, stdout=log_f, stderr=subprocess.STDOUT,
start_new_session=True, close_fds=True,
)
start_new_session=True, close_fds=True)
respawned.append(command)
except (OSError, ValueError) as exc:
failed.append((command, str(exc)))
@@ -335,8 +333,7 @@ def _install_hangup_protection(gateway_mode: bool = False):
(stdio wrapped in ``_UpdateOutputStream``). SIGINT/SIGTERM are left alone — legitimate cancels.
No-op in gateway mode (already detached). Returns state for ``_finalize_update_output``."""
state = {
"prev_stdout": sys.stdout, "prev_stderr": sys.stderr, "log_file": None, "installed": False
}
"prev_stdout": sys.stdout, "prev_stderr": sys.stderr, "log_file": None, "installed": False}
if gateway_mode:
return state
@@ -700,8 +697,7 @@ def _route_named_profile_dashboard(args, _headless_backend: bool, _ssh_owner_non
"serve" if _headless_backend else "dashboard",
"--port", str(args.port),
"--host", args.host,
"--open-profile", _launch_profile,
]
"--open-profile", _launch_profile]
if _ssh_owner_nonce:
reexec_argv.extend(["--ssh-owner-nonce", _ssh_owner_nonce])
if _token_file:

View File

@@ -122,8 +122,7 @@ def _write_desktop_build_stamp(project_root: Path, *, source_mode: bool) -> None
from hermes_cli.main import _desktop_stamp_path
_write_build_stamp(
_desktop_stamp_path(), "desktop",
lambda: _compute_desktop_content_hash(project_root), sourceMode=source_mode,
)
lambda: _compute_desktop_content_hash(project_root), sourceMode=source_mode)
def _desktop_packaged_executable(desktop_dir: Path) -> Optional[Path]:
@@ -242,15 +241,13 @@ def _windows_native_machine_from_iswow64() -> Optional[str]:
kernel32.GetCurrentProcess.restype = wintypes.HANDLE
kernel32.GetCurrentProcess.argtypes = []
kernel32.IsWow64Process2.argtypes = [
wintypes.HANDLE, ctypes.POINTER(wintypes.USHORT), ctypes.POINTER(wintypes.USHORT),
]
wintypes.HANDLE, ctypes.POINTER(wintypes.USHORT), ctypes.POINTER(wintypes.USHORT)]
kernel32.IsWow64Process2.restype = wintypes.BOOL
process_machine = wintypes.USHORT(0)
native_machine = wintypes.USHORT(0)
if not kernel32.IsWow64Process2(
kernel32.GetCurrentProcess(), ctypes.byref(process_machine), ctypes.byref(native_machine)
):
kernel32.GetCurrentProcess(), ctypes.byref(process_machine), ctypes.byref(native_machine)):
return None
return _PE_MACHINE_TO_NAME.get(native_machine.value)
@@ -547,8 +544,7 @@ def _electron_pkg_staged_missing_dist(project_root: Path) -> bool:
return (
(electron_dir / "package.json").is_file()
and (electron_dir / "install.js").is_file()
and not _electron_dist_ok(project_root)
)
and not _electron_dist_ok(project_root))
def _redownload_electron_dist(project_root: Path, env: dict, *, mirror: Optional[str] = None) -> bool:
@@ -652,8 +648,7 @@ def _desktop_macos_bundle_id(bundle: Path) -> Optional[str]:
info = bundle / "Contents" / "Info.plist"
if not info.exists() and bundle.suffix == ".framework":
candidates = list(bundle.glob("Versions/*/Resources/Info.plist")) + list(
bundle.glob("Resources/Info.plist")
)
bundle.glob("Resources/Info.plist"))
if candidates:
info = candidates[0]
if not info.exists():
@@ -690,8 +685,7 @@ def _desktop_macos_local_signing_identity() -> Optional[str]:
def _codesign_verify(codesign: str, app: Path, **kwargs) -> subprocess.CompletedProcess:
return subprocess.run(
[codesign, "--verify", "--deep", "--strict", str(app)], capture_output=True, **kwargs
)
[codesign, "--verify", "--deep", "--strict", str(app)], capture_output=True, **kwargs)
def _desktop_macos_has_valid_real_signature(app: Path) -> bool:
@@ -702,8 +696,7 @@ def _desktop_macos_has_valid_real_signature(app: Path) -> bool:
return False
try:
info = subprocess.run(
[codesign, "-dv", str(app)], check=False, capture_output=True, text=True
)
[codesign, "-dv", str(app)], check=False, capture_output=True, text=True)
output = f"{info.stdout}\n{info.stderr}"
if info.returncode != 0 or "TeamIdentifier=" not in output or "TeamIdentifier=not set" in output:
return False
@@ -731,8 +724,7 @@ def _desktop_macos_local_codesign(app: Path, *, desktop_dir: Path, identity: str
def sign_path(
path: Path, *, entitlements: Optional[Path] = None, identifier: Optional[str] = None,
runtime: bool = True,
) -> None:
runtime: bool = True) -> None:
args = [codesign, "--force", "--sign", identity, "--timestamp=none"]
if runtime:
args += ["--options", "runtime"]
@@ -808,8 +800,7 @@ def _macos_legacy_adhoc_resign(codesign: str, app: Path) -> bool:
def _desktop_macos_relaunchable_fixup(
desktop_dir: Path, *, publisher_signing_configured: Optional[bool] = None,
release_dir: Optional[Path] = None,
) -> bool:
release_dir: Optional[Path] = None) -> bool:
"""Re-sign a locally-built macOS app so in-place self-update doesn't reset TCC grants.
A rebuilt ad-hoc bundle (new cdhash, no stable Designated Requirement) reports
@@ -825,8 +816,7 @@ def _desktop_macos_relaunchable_fixup(
return True
if publisher_signing_configured is None:
publisher_signing_configured = bool(
os.environ.get("CSC_LINK") or os.environ.get("APPLE_SIGNING_IDENTITY")
)
os.environ.get("CSC_LINK") or os.environ.get("APPLE_SIGNING_IDENTITY"))
if publisher_signing_configured:
return True
exe = _desktop_packaged_executable_in(release_dir or (desktop_dir / "release"))
@@ -872,8 +862,7 @@ def _macos_codesigning_identity_valid(security: str, identity: str) -> bool:
def _macos_create_signing_identity(
openssl: str, security: str, codesign: str, keychain: str, identity: str
) -> bool:
openssl: str, security: str, codesign: str, keychain: str, identity: str) -> bool:
"""Create a self-signed code-signing cert (10 years), import it with codesign access, trust it for codeSign."""
tmp_dir = Path(tempfile.mkdtemp(prefix="hermes-tcc-"))
try:
@@ -890,8 +879,7 @@ def _macos_create_signing_identity(
"-addext", "keyUsage=critical,digitalSignature,keyCertSign",
"-addext", "extendedKeyUsage=codeSigning",
],
capture_output=True, check=True,
)
capture_output=True, check=True)
# OpenSSL 3 defaults to AES/SHA-2 PKCS#12 that `security import` rejects
# with "MAC verification failed". `-legacy` restores the accepted
@@ -904,8 +892,7 @@ def _macos_create_signing_identity(
"-inkey", str(key), "-in", str(crt),
"-out", str(p12), "-passout", "pass:hermeslocal",
],
capture_output=True, check=True,
)
capture_output=True, check=True)
def _import_p12():
return subprocess.run(
@@ -914,8 +901,7 @@ def _macos_create_signing_identity(
"-P", "hermeslocal",
"-T", codesign, "-T", "/usr/bin/codesign_allocate",
],
capture_output=True, text=True, check=False,
)
capture_output=True, text=True, check=False)
_export_p12([])
imported = _import_p12()
@@ -935,8 +921,7 @@ def _macos_create_signing_identity(
# command exists to front-load.
trusted = subprocess.run(
[security, "add-trusted-cert", "-r", "trustRoot", "-p", "codeSign", "-k", keychain, str(crt)],
capture_output=True, text=True, check=False,
)
capture_output=True, text=True, check=False)
if trusted.returncode != 0:
print(
" (could not trust the certificate for code signing: "
@@ -1062,8 +1047,7 @@ def _desktop_linux_userns_sandbox_available() -> bool:
[unshare, "--user", "--map-root-user", "true"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=5, check=False,
).returncode
== 0
)
== 0)
except (OSError, subprocess.TimeoutExpired):
return False
@@ -1159,8 +1143,7 @@ def _detect_linux_password_store() -> str | None:
"org.freedesktop.DBus.Peer.Ping",
],
capture_output=True,
timeout=5,
)
timeout=5)
if result.returncode == 0:
return "gnome-libsecret"
except Exception:
@@ -1285,8 +1268,7 @@ def _run_desktop_pack_with_recovery(
build_result.returncode != 0
and staging_dir is not None
and not env.get("ELECTRON_MIRROR")
and _staged_exe() is None
):
and _staged_exe() is None):
print(" ⚠ Desktop build still failing; the Electron download from "
"GitHub looks blocked. Re-downloading via a public mirror "
"(npmmirror.com)... (set ELECTRON_MIRROR to use another mirror)")
@@ -1396,8 +1378,7 @@ def _desktop_launch_env(args: argparse.Namespace) -> tuple[dict, list[str]]:
env["HERMES_DESKTOP_CWD"] = os.getcwd()
config_electron_flags, config_disable_gpu, config_password_store, config_ozone_hint = (
_desktop_launch_options()
)
_desktop_launch_options())
if config_disable_gpu != "auto" and "HERMES_DESKTOP_DISABLE_GPU" not in os.environ:
env["HERMES_DESKTOP_DISABLE_GPU"] = config_disable_gpu
if config_ozone_hint != "auto" and "ELECTRON_OZONE_PLATFORM_HINT" not in os.environ:

View File

@@ -54,8 +54,7 @@ def _print_tui_exit_summary(session_id: Optional[str], active_session_file: Opti
return # No real conversation — don't show resume info
tokens = {
k: int(session.get(f"{k}_tokens") or 0)
for k in ("input", "output", "cache_read", "cache_write", "reasoning")
}
for k in ("input", "output", "cache_read", "cache_write", "reasoning")}
except Exception:
return
finally:
@@ -99,8 +98,7 @@ def _workspace_root(dir: Path) -> Path:
if (
(dir / "package.json").is_file()
and not (dir / "package-lock.json").is_file()
and (dir.parent / "package-lock.json").is_file()
):
and (dir.parent / "package-lock.json").is_file()):
return dir.parent
return dir
@@ -340,8 +338,7 @@ def _ensure_tui_node() -> None:
result = subprocess.run(
["bash", "-c", f'source "{helper}" >&2 && ensure_node >&2 && command -v node'],
env={**os.environ, "HERMES_HOME": hermes_home},
capture_output=True, text=True, encoding="utf-8", errors="replace", check=False,
)
capture_output=True, text=True, encoding="utf-8", errors="replace", check=False)
except (OSError, subprocess.SubprocessError):
return
@@ -378,8 +375,7 @@ def _restore_tui_workspace(tui_dir: Path) -> bool:
try:
subprocess.run(
[git, "restore", "--", tui_dir.name], cwd=str(tui_dir.parent), capture_output=True,
text=True, encoding="utf-8", errors="replace", check=False,
)
text=True, encoding="utf-8", errors="replace", check=False)
except OSError:
return False
return tui_dir.is_dir()
@@ -405,8 +401,7 @@ def _ensure_tui_workspace(tui_dir: Path) -> None:
" 2. Run `npm install --silent --no-fund --no-audit --progress=false`\n"
" 3. Retry `hermes --tui`\n"
"If the checkout is still inconsistent, run `hermes update --force`.",
file=sys.stderr,
)
file=sys.stderr)
sys.exit(1)
@@ -457,8 +452,7 @@ def _run_tui_npm_build(npm: str, cwd: Path, failure_message: str) -> None:
"""``npm run build`` in *cwd*; exit with *failure_message* + output tail on failure."""
result = subprocess.run(
[npm, "run", "build"], cwd=str(cwd), capture_output=True, text=True, encoding="utf-8",
errors="replace", env=_npm_lifecycle_env(),
)
errors="replace", env=_npm_lifecycle_env())
_exit_on_npm_failure(result, failure_message, sep="")
@@ -489,8 +483,7 @@ def _install_tui_dependencies(tui_dir: Path, *, termux_startup: bool) -> None:
return subprocess.run(
npm_install_cmd, cwd=str(npm_cwd), stdout=subprocess.PIPE, stderr=subprocess.PIPE,
text=True, encoding="utf-8", errors="replace",
env=_npm_lifecycle_env(with_hermes_node_path()),
)
env=_npm_lifecycle_env(with_hermes_node_path()))
result = _run_tui_install()
if result.returncode != 0:
@@ -517,8 +510,7 @@ def _make_tui_argv(tui_dir: Path, tui_dev: bool) -> tuple[list[str], Path]:
f"Error: --dev is incompatible with HERMES_TUI_DIR={ext_dir}\n"
f"The prebuilt TUI has no source code to hot-reload.\n"
f"Unset HERMES_TUI_DIR (e.g. `unset HERMES_TUI_DIR`) to use --dev from a checkout.",
file=sys.stderr,
)
file=sys.stderr)
sys.exit(1)
# 1. Prebuilt bundle (nix / packaged release / Docker image): just run it.
@@ -700,8 +692,7 @@ def _launch_tui(
provider: Optional[str] = None, toolsets: object = None, skills: object = None,
verbose: Optional[bool] = None, quiet: bool = False, query: Optional[str] = None,
image: Optional[str] = None, worktree: bool = False, checkpoints: bool = False,
pass_session_id: bool = False, max_turns: Optional[int] = None, accept_hooks: bool = False,
):
pass_session_id: bool = False, max_turns: Optional[int] = None, accept_hooks: bool = False):
"""Replace current process with the TUI."""
from hermes_cli.main import PROJECT_ROOT, _apply_tui_python_env, _make_tui_argv, _resolve_tui_heap_mb
tui_dir = PROJECT_ROOT / "ui-tui"
@@ -748,8 +739,7 @@ def _launch_tui(
("HERMES_TUI_PASS_SESSION_ID", "1" if pass_session_id else None),
("HERMES_TUI_MAX_TURNS", str(max_turns) if max_turns is not None else None),
("HERMES_TUI_TOOL_PROGRESS", "verbose" if verbose else "off" if quiet else None),
("HERMES_ACCEPT_HOOKS", "1" if accept_hooks else None),
):
("HERMES_ACCEPT_HOOKS", "1" if accept_hooks else None)):
if value:
env[key] = value
# Generous V8 heap (8GB target; default cap can fatal-OOM on long sessions),

View File

@@ -20,8 +20,7 @@ import time as _time
from pathlib import Path
from typing import Callable
from hermes_cli.main_tui_launch import (
_npm_lifecycle_env, _termux_workspace_install_context, _workspace_root
)
_npm_lifecycle_env, _termux_workspace_install_context, _workspace_root)
# Log-record parity with the origin module.
logger = logging.getLogger("hermes_cli.main")
@@ -178,8 +177,7 @@ def _web_ui_build_needed(web_dir: Path) -> bool:
if not sentinel.exists():
return True
return not _stamp_is_current(
_web_ui_stamp_path(), lambda: _compute_web_ui_content_hash(project_root, web_dir)
)
_web_ui_stamp_path(), lambda: _compute_web_ui_content_hash(project_root, web_dir))
def _compute_web_ui_content_hash(project_root: Path, web_dir: Path) -> str:
@@ -196,8 +194,7 @@ def _web_ui_stamp_path() -> Path:
def _write_web_ui_build_stamp(project_root: Path, web_dir: Path) -> None:
"""Write the web UI build stamp after a successful build."""
_write_build_stamp(
_web_ui_stamp_path(), "web UI", lambda: _compute_web_ui_content_hash(project_root, web_dir)
)
_web_ui_stamp_path(), "web UI", lambda: _compute_web_ui_content_hash(project_root, web_dir))
def _console_print(text: str) -> None:
@@ -211,8 +208,7 @@ def _console_print(text: str) -> None:
def _run_with_idle_timeout(
cmd: list[str], cwd: Path, *, idle_timeout_seconds: int = 180, indent: str = " ",
env: dict[str, str] | None = None,
) -> subprocess.CompletedProcess:
env: dict[str, str] | None = None) -> subprocess.CompletedProcess:
"""Stream a subprocess, killing it after *idle_timeout_seconds* of silence (a silent captured
Vite build on a low-memory host looks like a hang and users reboot mid-install). Returns merged
stdout, empty stderr, rc 124 if terminate raced a clean exit; never raises on idle timeout."""
@@ -223,8 +219,7 @@ def _run_with_idle_timeout(
try:
proc = subprocess.Popen(
cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True, encoding="utf-8", errors="replace", bufsize=1, env=env,
)
text=True, encoding="utf-8", errors="replace", bufsize=1, env=env)
except OSError as exc:
# E.g. npm not on PATH between the which() check and now.
return subprocess.CompletedProcess(cmd, 127, stdout="", stderr=str(exc))
@@ -309,8 +304,7 @@ def _nixos_build_env() -> dict[str, str] | None:
def _run_npm_install_deterministic(
npm: str, cwd: Path, *, extra_args: tuple[str, ...] = (), capture_output: bool = True,
env: dict[str, str] | None = None,
) -> subprocess.CompletedProcess:
env: dict[str, str] | None = None) -> subprocess.CompletedProcess:
"""Deterministic npm install that never mutates ``package-lock.json``.
``npm ci`` when a lockfile exists, else/on failure ``npm install --no-save``