Files
hermes-agent/hermes_cli/subcommands/gui.py
T
CriptoGus eeb391d0b1 feat(desktop): add --setup-tcc-identity to keep macOS TCC grants across rebuilds
Adds a one-shot `hermes desktop --setup-tcc-identity` command that creates a
self-signed code-signing certificate in the login keychain (openssl +
security import), grants codesign access to it, writes
desktop.macos_signing_identity to config.yaml, and re-signs the packaged app
with a certificate-anchored Designated Requirement.

macOS persists permission grants (Full Disk Access, Accessibility, Files and
Folders, microphone) against the app's code-signing identity, not its path.
The default identifier-pinned ad-hoc signature is stable across rebuilds, but
a certificate-anchored identity is the strongest guarantee — the same
mechanism yabai/skhd rely on. Previously users had to create the certificate
manually in Keychain Access; this command automates the whole flow and is
idempotent (re-run after updates).

Docs: desktop.md TCC section now leads with the command, keeps manual steps.
Tests: 4 new — fresh cert creation path, idempotent reuse, non-macOS no-op,
cmd_gui early-exit before build.
2026-08-25 16:24:19 -07:00

81 lines
3.0 KiB
Python

"""``hermes gui`` subcommand parser.
Extracted verbatim from ``hermes_cli/main.py:main()`` (god-file Phase 2).
Handler injected to avoid importing ``main``.
"""
from __future__ import annotations
from typing import Callable
def build_gui_parser(subparsers, *, cmd_gui: Callable) -> None:
"""Attach the ``gui`` subcommand to ``subparsers``."""
# =========================================================================
gui_parser = subparsers.add_parser(
"desktop",
aliases=["gui"],
help="Build and launch the native desktop app",
description=(
"Launch the Hermes Electron desktop app. By default this installs "
"workspace Node dependencies, builds the current OS's unpacked "
"Electron app, then launches that packaged artifact."
),
)
gui_parser.add_argument(
"--source",
action="store_true",
help="Launch via `electron .` against apps/desktop/dist instead of the packaged app",
)
gui_parser.add_argument(
"--build-only",
action="store_true",
help="Build the desktop app but do not launch it (used by the installer's --update flow)",
)
gui_parser.add_argument(
"--fake-boot",
action="store_true",
help="Enable deterministic desktop boot delays for validating startup UI",
)
gui_parser.add_argument(
"--ignore-existing",
action="store_true",
help="Force Desktop to ignore any hermes CLI already on PATH during backend resolution",
)
gui_parser.add_argument(
"--hermes-root",
help="Override the Hermes source root used by Desktop (sets HERMES_DESKTOP_HERMES_ROOT)",
)
gui_parser.add_argument(
"--cwd",
help="Initial project directory for Desktop chat sessions (sets HERMES_DESKTOP_CWD)",
)
gui_parser.add_argument(
"--skip-build",
action="store_true",
help="Skip npm install/package and launch the existing unpacked app from apps/desktop/release",
)
gui_parser.add_argument(
"--force-build",
action="store_true",
help="Force a full rebuild even if the content stamp matches",
)
gui_parser.add_argument(
"--setup-tcc-identity",
action="store_true",
help=(
"macOS only: create/import a self-signed code-signing certificate "
"in the login keychain and point desktop.macos_signing_identity at "
"it, then re-sign the packaged app. Makes macOS TCC grants (Full "
"Disk Access, Accessibility, Files and Folders, microphone) survive "
"rebuilds with a certificate-anchored identity. Idempotent — safe "
"to re-run after updates."
),
)
gui_parser.add_argument(
"--identity",
default="Hermes Local Signing",
help="Certificate name to create/use for --setup-tcc-identity (default: Hermes Local Signing)",
)
gui_parser.set_defaults(func=cmd_gui)