mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-25 05:52:34 +00:00
feat(computer-use): refresh cua-driver on hermes update + add install --upgrade (#24063)
cua-driver was only installed once on toolset enable: `_run_post_setup` early-returns when the binary is already on PATH, so upstream fixes (e.g. v0.1.6 Safari window-focus fix) never reached existing users without manual reinstall. Two refresh points now: - `hermes update` re-runs the upstream installer at the end of the update if cua-driver is on PATH (macOS-only, no-op otherwise). Ties driver freshness to the user-controlled update cadence — no startup latency, no per-launch GitHub API call. - `hermes computer-use install --upgrade` for manual force-refresh. The upstream `install.sh` always pulls the latest release, so re-running is the canonical upgrade path. No version-comparison logic needed. `hermes computer-use status` now shows the installed version, and points at `--upgrade` for refreshing.
This commit is contained in:
parent
97a0e69df0
commit
ced1990c1c
5 changed files with 308 additions and 50 deletions
|
|
@ -7801,6 +7801,22 @@ def _cmd_update_impl(args, gateway_mode: bool):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug("FHS PATH guard check failed: %s", e)
|
logger.debug("FHS PATH guard check failed: %s", e)
|
||||||
|
|
||||||
|
# Refresh the cua-driver binary used by the Computer Use toolset.
|
||||||
|
# The upstream installer is gated on macOS and on the binary already
|
||||||
|
# being on PATH, so this is a no-op for users who don't have it.
|
||||||
|
# Tying the refresh to ``hermes update`` gives users a predictable
|
||||||
|
# cadence (matches when they pull new agent code) without adding
|
||||||
|
# startup latency or a per-launch GitHub API call.
|
||||||
|
try:
|
||||||
|
if sys.platform == "darwin" and shutil.which("cua-driver"):
|
||||||
|
from hermes_cli.tools_config import install_cua_driver
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("→ Refreshing cua-driver (Computer Use)...")
|
||||||
|
install_cua_driver(upgrade=True)
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug("cua-driver refresh failed: %s", e)
|
||||||
|
|
||||||
# Write exit code *before* the gateway restart attempt.
|
# Write exit code *before* the gateway restart attempt.
|
||||||
# When running as ``hermes update --gateway`` (spawned by the gateway's
|
# When running as ``hermes update --gateway`` (spawned by the gateway's
|
||||||
# /update command), this process lives inside the gateway's systemd
|
# /update command), this process lives inside the gateway's systemd
|
||||||
|
|
@ -10801,10 +10817,19 @@ Examples:
|
||||||
)
|
)
|
||||||
computer_use_sub = computer_use_parser.add_subparsers(dest="computer_use_action")
|
computer_use_sub = computer_use_parser.add_subparsers(dest="computer_use_action")
|
||||||
|
|
||||||
computer_use_sub.add_parser(
|
computer_use_install = computer_use_sub.add_parser(
|
||||||
"install",
|
"install",
|
||||||
help="Install or repair the cua-driver binary (macOS)",
|
help="Install or repair the cua-driver binary (macOS)",
|
||||||
)
|
)
|
||||||
|
computer_use_install.add_argument(
|
||||||
|
"--upgrade",
|
||||||
|
action="store_true",
|
||||||
|
help=(
|
||||||
|
"Re-run the upstream installer even if cua-driver is already on "
|
||||||
|
"PATH. The upstream install.sh always pulls the latest release, "
|
||||||
|
"so this performs an in-place upgrade."
|
||||||
|
),
|
||||||
|
)
|
||||||
computer_use_sub.add_parser(
|
computer_use_sub.add_parser(
|
||||||
"status",
|
"status",
|
||||||
help="Print whether cua-driver is installed and on PATH",
|
help="Print whether cua-driver is installed and on PATH",
|
||||||
|
|
@ -10813,14 +10838,27 @@ Examples:
|
||||||
def cmd_computer_use(args):
|
def cmd_computer_use(args):
|
||||||
action = getattr(args, "computer_use_action", None)
|
action = getattr(args, "computer_use_action", None)
|
||||||
if action == "install":
|
if action == "install":
|
||||||
from hermes_cli.tools_config import _run_post_setup
|
from hermes_cli.tools_config import install_cua_driver
|
||||||
_run_post_setup("cua_driver")
|
install_cua_driver(upgrade=bool(getattr(args, "upgrade", False)))
|
||||||
return
|
return
|
||||||
if action == "status":
|
if action == "status":
|
||||||
import shutil
|
import shutil
|
||||||
|
import subprocess
|
||||||
path = shutil.which("cua-driver")
|
path = shutil.which("cua-driver")
|
||||||
if path:
|
if path:
|
||||||
print(f"cua-driver: installed at {path}")
|
version = ""
|
||||||
|
try:
|
||||||
|
version = subprocess.run(
|
||||||
|
["cua-driver", "--version"],
|
||||||
|
capture_output=True, text=True, timeout=5,
|
||||||
|
).stdout.strip()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if version:
|
||||||
|
print(f"cua-driver: installed at {path} ({version})")
|
||||||
|
else:
|
||||||
|
print(f"cua-driver: installed at {path}")
|
||||||
|
print(" Refresh to latest: hermes computer-use install --upgrade")
|
||||||
return
|
return
|
||||||
print("cua-driver: not installed")
|
print("cua-driver: not installed")
|
||||||
print(" Run: hermes computer-use install")
|
print(" Run: hermes computer-use install")
|
||||||
|
|
|
||||||
|
|
@ -591,6 +591,132 @@ def _pip_install(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def install_cua_driver(upgrade: bool = False) -> bool:
|
||||||
|
"""Install or refresh the cua-driver binary used by Computer Use.
|
||||||
|
|
||||||
|
The upstream installer always pulls the latest release tag, so re-running
|
||||||
|
it is the canonical way to upgrade. We expose two modes:
|
||||||
|
|
||||||
|
* ``upgrade=False`` — original post-setup behaviour: skip if already
|
||||||
|
installed, install otherwise. Used by the toolset enable flow where
|
||||||
|
we don't want to surprise the user with a network fetch.
|
||||||
|
* ``upgrade=True`` — always re-run the installer (or call ``cua-driver
|
||||||
|
update`` if the binary supports it). Used by ``hermes update`` and
|
||||||
|
by ``hermes computer-use install --upgrade``.
|
||||||
|
|
||||||
|
Returns True iff cua-driver is installed (or successfully refreshed)
|
||||||
|
when the function returns. macOS-only — silently returns False on
|
||||||
|
other platforms.
|
||||||
|
"""
|
||||||
|
import platform as _plat
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
if _plat.system() != "Darwin":
|
||||||
|
if upgrade:
|
||||||
|
# Silent on non-macOS — `hermes update` calls this for every
|
||||||
|
# user; only macOS users with cua-driver care.
|
||||||
|
return False
|
||||||
|
_print_warning(" Computer Use (cua-driver) is macOS-only; skipping.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
binary = shutil.which("cua-driver")
|
||||||
|
|
||||||
|
# Not installed → fresh install path (only when caller asked for it).
|
||||||
|
if not binary and not upgrade:
|
||||||
|
if not shutil.which("curl"):
|
||||||
|
_print_warning(" curl not found — install manually:")
|
||||||
|
_print_info(" https://github.com/trycua/cua/blob/main/libs/cua-driver/README.md")
|
||||||
|
return False
|
||||||
|
return _run_cua_driver_installer(label="Installing")
|
||||||
|
|
||||||
|
# Already installed and caller didn't ask to upgrade → just confirm.
|
||||||
|
if binary and not upgrade:
|
||||||
|
try:
|
||||||
|
version = subprocess.run(
|
||||||
|
["cua-driver", "--version"],
|
||||||
|
capture_output=True, text=True, timeout=5,
|
||||||
|
).stdout.strip()
|
||||||
|
_print_success(f" cua-driver already installed: {version or 'unknown version'}")
|
||||||
|
except Exception:
|
||||||
|
_print_success(" cua-driver already installed.")
|
||||||
|
_print_info(" Grant macOS permissions if not done yet:")
|
||||||
|
_print_info(" System Settings > Privacy & Security > Accessibility")
|
||||||
|
_print_info(" System Settings > Privacy & Security > Screen Recording")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# upgrade=True path — refresh to the latest upstream release.
|
||||||
|
if not shutil.which("curl"):
|
||||||
|
_print_warning(" curl not found — cannot refresh cua-driver.")
|
||||||
|
return bool(binary)
|
||||||
|
|
||||||
|
if binary:
|
||||||
|
# Show before/after version when we have a baseline. Best-effort.
|
||||||
|
try:
|
||||||
|
before = subprocess.run(
|
||||||
|
["cua-driver", "--version"],
|
||||||
|
capture_output=True, text=True, timeout=5,
|
||||||
|
).stdout.strip()
|
||||||
|
except Exception:
|
||||||
|
before = ""
|
||||||
|
else:
|
||||||
|
before = ""
|
||||||
|
|
||||||
|
ok = _run_cua_driver_installer(label="Refreshing", verbose=False)
|
||||||
|
if ok and before:
|
||||||
|
try:
|
||||||
|
after = subprocess.run(
|
||||||
|
["cua-driver", "--version"],
|
||||||
|
capture_output=True, text=True, timeout=5,
|
||||||
|
).stdout.strip()
|
||||||
|
if after and after != before:
|
||||||
|
_print_success(f" cua-driver upgraded: {before} → {after}")
|
||||||
|
elif after:
|
||||||
|
_print_info(f" cua-driver up to date: {after}")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return ok
|
||||||
|
|
||||||
|
|
||||||
|
def _run_cua_driver_installer(label: str = "Installing", verbose: bool = True) -> bool:
|
||||||
|
"""Run the upstream cua-driver install.sh. Returns True on success.
|
||||||
|
|
||||||
|
The script is idempotent: it always downloads the latest release, so
|
||||||
|
re-running it on an already-installed system performs an upgrade.
|
||||||
|
"""
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
install_cmd = (
|
||||||
|
"/bin/bash -c \"$(curl -fsSL "
|
||||||
|
"https://raw.githubusercontent.com/trycua/cua/main/"
|
||||||
|
"libs/cua-driver/scripts/install.sh)\""
|
||||||
|
)
|
||||||
|
if verbose:
|
||||||
|
_print_info(f" {label} cua-driver (macOS background computer-use)...")
|
||||||
|
else:
|
||||||
|
_print_info(f" {label} cua-driver...")
|
||||||
|
try:
|
||||||
|
result = subprocess.run(install_cmd, shell=True, timeout=300)
|
||||||
|
if result.returncode == 0 and shutil.which("cua-driver"):
|
||||||
|
if verbose:
|
||||||
|
_print_success(" cua-driver installed.")
|
||||||
|
_print_info(" IMPORTANT — grant macOS permissions now:")
|
||||||
|
_print_info(" System Settings > Privacy & Security > Accessibility")
|
||||||
|
_print_info(" System Settings > Privacy & Security > Screen Recording")
|
||||||
|
_print_info(" Both must allow the terminal / Hermes process.")
|
||||||
|
return True
|
||||||
|
_print_warning(f" cua-driver {label.lower()} did not complete. Re-run manually:")
|
||||||
|
_print_info(f" {install_cmd}")
|
||||||
|
return False
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
_print_warning(f" cua-driver {label.lower()} timed out. Re-run manually.")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
_print_warning(f" cua-driver {label.lower()} failed: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _run_post_setup(post_setup_key: str):
|
def _run_post_setup(post_setup_key: str):
|
||||||
"""Run post-setup hooks for tools that need extra installation steps."""
|
"""Run post-setup hooks for tools that need extra installation steps."""
|
||||||
import shutil
|
import shutil
|
||||||
|
|
@ -729,51 +855,7 @@ def _run_post_setup(post_setup_key: str):
|
||||||
_print_info(" docker run -p 9377:9377 -e CAMOFOX_PORT=9377 jo-inc/camofox-browser")
|
_print_info(" docker run -p 9377:9377 -e CAMOFOX_PORT=9377 jo-inc/camofox-browser")
|
||||||
|
|
||||||
elif post_setup_key == "cua_driver":
|
elif post_setup_key == "cua_driver":
|
||||||
# cua-driver provides macOS background computer-use (SkyLight SPIs).
|
install_cua_driver(upgrade=False)
|
||||||
# Install via upstream curl script if the binary isn't on $PATH yet.
|
|
||||||
import platform as _plat
|
|
||||||
import subprocess
|
|
||||||
if _plat.system() != "Darwin":
|
|
||||||
_print_warning(" Computer Use (cua-driver) is macOS-only; skipping.")
|
|
||||||
return
|
|
||||||
if shutil.which("cua-driver"):
|
|
||||||
try:
|
|
||||||
version = subprocess.run(
|
|
||||||
["cua-driver", "--version"],
|
|
||||||
capture_output=True, text=True, timeout=5,
|
|
||||||
).stdout.strip()
|
|
||||||
_print_success(f" cua-driver already installed: {version or 'unknown version'}")
|
|
||||||
except Exception:
|
|
||||||
_print_success(" cua-driver already installed.")
|
|
||||||
_print_info(" Grant macOS permissions if not done yet:")
|
|
||||||
_print_info(" System Settings > Privacy & Security > Accessibility")
|
|
||||||
_print_info(" System Settings > Privacy & Security > Screen Recording")
|
|
||||||
return
|
|
||||||
if not shutil.which("curl"):
|
|
||||||
_print_warning(" curl not found — install manually:")
|
|
||||||
_print_info(" https://github.com/trycua/cua/blob/main/libs/cua-driver/README.md")
|
|
||||||
return
|
|
||||||
_print_info(" Installing cua-driver (macOS background computer-use)...")
|
|
||||||
try:
|
|
||||||
install_cmd = (
|
|
||||||
"/bin/bash -c \"$(curl -fsSL "
|
|
||||||
"https://raw.githubusercontent.com/trycua/cua/main/"
|
|
||||||
"libs/cua-driver/scripts/install.sh)\""
|
|
||||||
)
|
|
||||||
result = subprocess.run(install_cmd, shell=True, timeout=300)
|
|
||||||
if result.returncode == 0 and shutil.which("cua-driver"):
|
|
||||||
_print_success(" cua-driver installed.")
|
|
||||||
_print_info(" IMPORTANT — grant macOS permissions now:")
|
|
||||||
_print_info(" System Settings > Privacy & Security > Accessibility")
|
|
||||||
_print_info(" System Settings > Privacy & Security > Screen Recording")
|
|
||||||
_print_info(" Both must allow the terminal / Hermes process.")
|
|
||||||
else:
|
|
||||||
_print_warning(" cua-driver install did not complete. Re-run manually:")
|
|
||||||
_print_info(f" {install_cmd}")
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
_print_warning(" cua-driver install timed out. Re-run manually.")
|
|
||||||
except Exception as e:
|
|
||||||
_print_warning(f" cua-driver install failed: {e}")
|
|
||||||
|
|
||||||
elif post_setup_key == "kittentts":
|
elif post_setup_key == "kittentts":
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
115
tests/hermes_cli/test_install_cua_driver.py
Normal file
115
tests/hermes_cli/test_install_cua_driver.py
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
"""Tests for ``install_cua_driver`` upgrade semantics.
|
||||||
|
|
||||||
|
The cua-driver upstream installer always pulls the latest release tag, so
|
||||||
|
re-running it is the canonical upgrade path. ``install_cua_driver(upgrade=True)``
|
||||||
|
must:
|
||||||
|
|
||||||
|
* Be macOS-only — no-op silently on Linux/Windows so ``hermes update`` can
|
||||||
|
call it unconditionally without warning every non-macOS user.
|
||||||
|
* Re-run the installer even when the binary is already on PATH (this is the
|
||||||
|
fix for the "we only pulled cua-driver once on enable" complaint).
|
||||||
|
* Preserve original ``upgrade=False`` behaviour for the toolset-enable flow:
|
||||||
|
skip if installed, install otherwise, warn on non-macOS.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
|
||||||
|
class TestInstallCuaDriverUpgrade:
|
||||||
|
def test_upgrade_on_non_macos_is_silent_noop(self):
|
||||||
|
"""``hermes update`` calls install_cua_driver(upgrade=True) for every
|
||||||
|
user. On Linux/Windows it must return False without printing the
|
||||||
|
"macOS-only; skipping" warning that the toolset-enable path emits."""
|
||||||
|
from hermes_cli import tools_config
|
||||||
|
|
||||||
|
with patch.object(tools_config, "_print_warning") as warn, \
|
||||||
|
patch("platform.system", return_value="Linux"):
|
||||||
|
assert tools_config.install_cua_driver(upgrade=True) is False
|
||||||
|
warn.assert_not_called()
|
||||||
|
|
||||||
|
def test_non_upgrade_on_non_macos_warns(self):
|
||||||
|
"""The toolset-enable path (upgrade=False) should still warn loudly
|
||||||
|
when the user tries to enable Computer Use on a non-macOS host."""
|
||||||
|
from hermes_cli import tools_config
|
||||||
|
|
||||||
|
with patch.object(tools_config, "_print_warning") as warn, \
|
||||||
|
patch("platform.system", return_value="Linux"):
|
||||||
|
assert tools_config.install_cua_driver(upgrade=False) is False
|
||||||
|
warn.assert_called()
|
||||||
|
|
||||||
|
def test_upgrade_on_macos_with_binary_runs_installer(self):
|
||||||
|
"""When cua-driver is already on PATH and upgrade=True, we must
|
||||||
|
re-run the upstream installer (this is the fix for the bug report).
|
||||||
|
"""
|
||||||
|
from hermes_cli import tools_config
|
||||||
|
|
||||||
|
with patch("platform.system", return_value="Darwin"), \
|
||||||
|
patch.object(tools_config.shutil, "which",
|
||||||
|
side_effect=lambda n: "/usr/local/bin/" + n
|
||||||
|
if n in ("cua-driver", "curl") else None), \
|
||||||
|
patch.object(tools_config, "_run_cua_driver_installer",
|
||||||
|
return_value=True) as runner, \
|
||||||
|
patch("subprocess.run"):
|
||||||
|
assert tools_config.install_cua_driver(upgrade=True) is True
|
||||||
|
runner.assert_called_once()
|
||||||
|
# Refresh path uses non-verbose mode so we don't re-print the
|
||||||
|
# "grant macOS permissions" block on every `hermes update`.
|
||||||
|
kwargs = runner.call_args.kwargs
|
||||||
|
assert kwargs.get("verbose") is False
|
||||||
|
|
||||||
|
def test_upgrade_on_macos_without_binary_runs_installer(self):
|
||||||
|
"""upgrade=True with cua-driver missing must still trigger an
|
||||||
|
install — equivalent to a fresh install. (Don't silently no-op.)"""
|
||||||
|
from hermes_cli import tools_config
|
||||||
|
|
||||||
|
with patch("platform.system", return_value="Darwin"), \
|
||||||
|
patch.object(tools_config.shutil, "which",
|
||||||
|
side_effect=lambda n: "/usr/bin/curl" if n == "curl" else None), \
|
||||||
|
patch.object(tools_config, "_run_cua_driver_installer",
|
||||||
|
return_value=True) as runner:
|
||||||
|
assert tools_config.install_cua_driver(upgrade=True) is True
|
||||||
|
runner.assert_called_once()
|
||||||
|
|
||||||
|
def test_non_upgrade_on_macos_with_binary_skips_install(self):
|
||||||
|
"""Original toolset-enable behaviour: cua-driver already installed
|
||||||
|
+ upgrade=False → confirm and return without re-running installer.
|
||||||
|
This is the behaviour that ``hermes tools`` (re)enable depends on,
|
||||||
|
so the new helper must not regress it."""
|
||||||
|
from hermes_cli import tools_config
|
||||||
|
|
||||||
|
with patch("platform.system", return_value="Darwin"), \
|
||||||
|
patch.object(tools_config.shutil, "which",
|
||||||
|
side_effect=lambda n: "/usr/local/bin/" + n
|
||||||
|
if n in ("cua-driver", "curl") else None), \
|
||||||
|
patch.object(tools_config, "_run_cua_driver_installer") as runner, \
|
||||||
|
patch("subprocess.run"):
|
||||||
|
assert tools_config.install_cua_driver(upgrade=False) is True
|
||||||
|
runner.assert_not_called()
|
||||||
|
|
||||||
|
def test_non_upgrade_on_macos_without_binary_runs_installer(self):
|
||||||
|
"""Original fresh-install path must still work."""
|
||||||
|
from hermes_cli import tools_config
|
||||||
|
|
||||||
|
with patch("platform.system", return_value="Darwin"), \
|
||||||
|
patch.object(tools_config.shutil, "which",
|
||||||
|
side_effect=lambda n: "/usr/bin/curl" if n == "curl" else None), \
|
||||||
|
patch.object(tools_config, "_run_cua_driver_installer",
|
||||||
|
return_value=True) as runner:
|
||||||
|
assert tools_config.install_cua_driver(upgrade=False) is True
|
||||||
|
runner.assert_called_once()
|
||||||
|
|
||||||
|
def test_upgrade_without_curl_does_not_crash(self):
|
||||||
|
"""If curl isn't on PATH we can't refresh — must warn and return
|
||||||
|
the current install state, not raise."""
|
||||||
|
from hermes_cli import tools_config
|
||||||
|
|
||||||
|
# cua-driver present, curl missing.
|
||||||
|
def _which(name):
|
||||||
|
return "/usr/local/bin/cua-driver" if name == "cua-driver" else None
|
||||||
|
|
||||||
|
with patch("platform.system", return_value="Darwin"), \
|
||||||
|
patch.object(tools_config.shutil, "which", side_effect=_which), \
|
||||||
|
patch.object(tools_config, "_print_warning"):
|
||||||
|
assert tools_config.install_cua_driver(upgrade=True) is True
|
||||||
|
|
@ -976,7 +976,8 @@ Subcommands:
|
||||||
| Subcommand | Description |
|
| Subcommand | Description |
|
||||||
|------------|-------------|
|
|------------|-------------|
|
||||||
| `install` | Run the upstream cua-driver installer (macOS only). |
|
| `install` | Run the upstream cua-driver installer (macOS only). |
|
||||||
| `status` | Print whether `cua-driver` is on `$PATH`. |
|
| `install --upgrade` | Re-run the installer even if cua-driver is already on PATH. The upstream script always pulls the latest release, so this performs an in-place upgrade. |
|
||||||
|
| `status` | Print whether `cua-driver` is on `$PATH` and which version is installed. |
|
||||||
|
|
||||||
`hermes computer-use install` is the stable entry point for installing the
|
`hermes computer-use install` is the stable entry point for installing the
|
||||||
[cua-driver](https://github.com/trycua/cua) binary used by the
|
[cua-driver](https://github.com/trycua/cua) binary used by the
|
||||||
|
|
@ -985,6 +986,11 @@ Subcommands:
|
||||||
to use for re-running the install if the toolset toggle didn't trigger
|
to use for re-running the install if the toolset toggle didn't trigger
|
||||||
it (for example, on returning-user setups).
|
it (for example, on returning-user setups).
|
||||||
|
|
||||||
|
`hermes update` automatically re-runs the upstream installer at the end
|
||||||
|
of the update if cua-driver is on PATH, so most users will not need to
|
||||||
|
call `--upgrade` manually. Use it when upstream ships a fix you want
|
||||||
|
right now without waiting for the next Hermes update.
|
||||||
|
|
||||||
## `hermes sessions`
|
## `hermes sessions`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,23 @@ After installing, regardless of which path you took:
|
||||||
```
|
```
|
||||||
or add `computer_use` to your enabled toolsets in `~/.hermes/config.yaml`.
|
or add `computer_use` to your enabled toolsets in `~/.hermes/config.yaml`.
|
||||||
|
|
||||||
|
## Keeping cua-driver up to date
|
||||||
|
|
||||||
|
The cua-driver project ships fixes regularly (e.g. v0.1.6 fixed a Safari
|
||||||
|
window-focus bug for UTM workflows). Hermes refreshes the binary in two
|
||||||
|
places so you don't get stuck on a stale release:
|
||||||
|
|
||||||
|
- **`hermes update`** — when you update Hermes itself, if `cua-driver` is
|
||||||
|
on PATH the upstream installer re-runs at the end of the update.
|
||||||
|
No-op for non-macOS users and for users without cua-driver installed.
|
||||||
|
- **`hermes computer-use install --upgrade`** — manual force-refresh.
|
||||||
|
Re-runs the upstream installer regardless of whether cua-driver is
|
||||||
|
already installed. Use this when you want the latest fix without
|
||||||
|
waiting for the next agent update.
|
||||||
|
|
||||||
|
`hermes computer-use status` shows the installed version next to the
|
||||||
|
binary path.
|
||||||
|
|
||||||
## Quick example
|
## Quick example
|
||||||
|
|
||||||
User prompt: *"Find my latest email from Stripe and summarise what they want me to do."*
|
User prompt: *"Find my latest email from Stripe and summarise what they want me to do."*
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue