fix(update): skip cua-driver refresh when Applications is unwritable

This commit is contained in:
Sami Rusani 2026-06-17 15:20:26 +02:00 committed by Teknium
parent c13281ab57
commit d8b51269ca
5 changed files with 98 additions and 4 deletions

View file

@ -2952,6 +2952,11 @@ DEFAULT_CONFIG = {
# ignored paths — node_modules, venv, build outputs —
# are never touched.
"non_interactive_local_changes": "stash",
# Refresh an already-installed cua-driver during `hermes update`.
# The refresh is best-effort and macOS-only. Turn this off if the
# upstream installer is not appropriate for the machine, for example
# on non-admin accounts where `/Applications` is not writable.
"refresh_cua_driver": True,
},
# Language Server Protocol — semantic diagnostics from real

View file

@ -10243,7 +10243,23 @@ def _cmd_update_impl(args, gateway_mode: bool):
# predictable cadence (matches when they pull new agent code) without
# adding startup latency or a per-launch GitHub API call.
try:
if sys.platform in ("darwin", "win32", "linux") and shutil.which("cua-driver"):
refresh_cua_driver = True
try:
from hermes_cli.config import load_config
_update_cfg = (load_config() or {}).get("updates", {})
if isinstance(_update_cfg, dict):
refresh_cua_driver = bool(
_update_cfg.get("refresh_cua_driver", True)
)
except Exception as cfg_exc:
logger.debug("Could not read updates.refresh_cua_driver: %s", cfg_exc)
if (
refresh_cua_driver
and sys.platform in ("darwin", "win32", "linux")
and shutil.which("cua-driver")
):
from hermes_cli.tools_config import install_cua_driver
print()

View file

@ -705,6 +705,19 @@ def _pip_install(
# no Python-side duplication.
def _cua_install_target_writable() -> bool:
"""Return whether the upstream installer can write its app bundle target."""
if sys.platform != "darwin":
return True
applications_dir = "/Applications"
try:
if not os.path.isdir(applications_dir):
return True
return os.access(applications_dir, os.W_OK)
except Exception:
return True
def install_cua_driver(upgrade: bool = False) -> bool:
"""Install or refresh the cua-driver binary used by Computer Use.
@ -747,6 +760,14 @@ def install_cua_driver(upgrade: bool = False) -> bool:
# Not installed → fresh install path (only when caller asked for it).
if not binary and not upgrade:
if not _cua_install_target_writable():
_print_info(
" /Applications is not writable; skipping cua-driver install."
)
_print_info(
" Run from an admin account or install cua-driver manually."
)
return False
if not shutil.which(fetch_tool):
_print_warning(f" {fetch_tool} not found — install manually:")
_print_info(" https://github.com/trycua/cua/blob/main/libs/cua-driver/README.md")
@ -778,6 +799,15 @@ def install_cua_driver(upgrade: bool = False) -> bool:
return True
# upgrade=True path — refresh to the latest upstream release.
if not _cua_install_target_writable():
_print_info(
" /Applications is not writable; skipping cua-driver refresh."
)
_print_info(
" Run `hermes computer-use install --upgrade` from an admin account to update it."
)
return bool(binary)
if not shutil.which(fetch_tool):
_print_warning(f" {fetch_tool} not found — cannot refresh cua-driver.")
return bool(binary)

View file

@ -708,6 +708,14 @@ _SCHEMA_OVERRIDES: Dict[str, Dict[str, Any]] = {
),
"options": ["stash", "discard"],
},
"updates.refresh_cua_driver": {
"type": "bool",
"description": (
"Refresh an already-installed cua-driver during hermes update. "
"Disable this on non-admin macOS accounts where /Applications is "
"not writable."
),
},
}
# Categories with fewer fields get merged into "general" to avoid tab sprawl.