feat: FreeBSD compatibility patches (clean-room, MIT)
Some checks failed
Lint (ruff + ty) / ruff + ty diff (push) Waiting to run
Lint (ruff + ty) / ruff enforcement (blocking) (push) Waiting to run
Lint (ruff + ty) / Windows footguns (blocking) (push) Waiting to run
Nix / nix (macos-latest) (push) Waiting to run
Nix / nix (ubuntu-latest) (push) Waiting to run
Tests / test (1) (push) Waiting to run
Tests / test (2) (push) Waiting to run
Tests / test (3) (push) Waiting to run
Tests / test (4) (push) Waiting to run
Tests / test (5) (push) Waiting to run
Tests / test (6) (push) Waiting to run
Tests / save-durations (push) Blocked by required conditions
Tests / e2e (push) Waiting to run
Typecheck / typecheck (apps/bootstrap-installer) (push) Waiting to run
Typecheck / typecheck (apps/desktop) (push) Waiting to run
Typecheck / typecheck (apps/shared) (push) Waiting to run
Typecheck / typecheck (ui-tui) (push) Waiting to run
Typecheck / typecheck (web) (push) Waiting to run
Docker Build and Publish / build-amd64 (push) Has been cancelled
Docker Build and Publish / build-arm64 (push) Has been cancelled
Docker Build and Publish / merge (push) Has been cancelled
Some checks failed
Lint (ruff + ty) / ruff + ty diff (push) Waiting to run
Lint (ruff + ty) / ruff enforcement (blocking) (push) Waiting to run
Lint (ruff + ty) / Windows footguns (blocking) (push) Waiting to run
Nix / nix (macos-latest) (push) Waiting to run
Nix / nix (ubuntu-latest) (push) Waiting to run
Tests / test (1) (push) Waiting to run
Tests / test (2) (push) Waiting to run
Tests / test (3) (push) Waiting to run
Tests / test (4) (push) Waiting to run
Tests / test (5) (push) Waiting to run
Tests / test (6) (push) Waiting to run
Tests / save-durations (push) Blocked by required conditions
Tests / e2e (push) Waiting to run
Typecheck / typecheck (apps/bootstrap-installer) (push) Waiting to run
Typecheck / typecheck (apps/desktop) (push) Waiting to run
Typecheck / typecheck (apps/shared) (push) Waiting to run
Typecheck / typecheck (ui-tui) (push) Waiting to run
Typecheck / typecheck (web) (push) Waiting to run
Docker Build and Publish / build-amd64 (push) Has been cancelled
Docker Build and Publish / build-arm64 (push) Has been cancelled
Docker Build and Publish / merge (push) Has been cancelled
- setup.py: FreeBSD platform detection, pkg package manager, rc.d service - uninstall.py: /usr/local/bin symlink path on FreeBSD - voice_mode.py: aplay/ffplay audio on FreeBSD - install-freebsd.sh: native POSIX sh installer for FreeBSD 14/15 - README-FreeBSD.md: documentation Built from MIT-licensed upstream NousResearch/hermes-agent. No LGPL code — clean-room implementation guided by platform behavior. Clipboard (xclip) and voice (ffplay) work via pkg-installed tools.
This commit is contained in:
parent
6b76284c77
commit
a9e4caa3f7
4 changed files with 150 additions and 3 deletions
26
README-FreeBSD.md
Normal file
26
README-FreeBSD.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Hermes Agent — FreeBSD
|
||||
|
||||
This is a clean-room FreeBSD compatibility layer for [Hermes Agent](https://github.com/NousResearch/hermes-agent), built from the MIT-licensed upstream. No LGPL code, no Autolycus dependency.
|
||||
|
||||
## What's patched
|
||||
|
||||
Six targeted changes for FreeBSD native support:
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `hermes_cli/setup.py` | FreeBSD in platform detection, `pkg` for espeak-ng, rc.d service support |
|
||||
| `hermes_cli/uninstall.py` | `/usr/local/bin` symlink removal on FreeBSD |
|
||||
| `tools/voice_mode.py` | Audio playback on FreeBSD (aplay/ffplay) |
|
||||
| `scripts/install-freebsd.sh` | Native FreeBSD installer (POSIX sh, pkg, uv) |
|
||||
|
||||
Clipboard (xclip) and voice (ffplay) work on FreeBSD without code changes — xclip and ffmpeg are available via `pkg`.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
sh scripts/install-freebsd.sh
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT — same as upstream NousResearch Hermes Agent. No LGPL encumbrance.
|
||||
|
|
@ -771,6 +771,8 @@ def _install_neutts_deps() -> bool:
|
|||
print_info("Install with: brew install espeak-ng")
|
||||
elif sys.platform == "win32":
|
||||
print_info("Install with: choco install espeak-ng")
|
||||
elif sys.platform.startswith("freebsd"):
|
||||
print_info("Install with: sudo pkg install espeak-ng")
|
||||
else:
|
||||
print_info("Install with: sudo apt install espeak-ng")
|
||||
print()
|
||||
|
|
@ -780,6 +782,8 @@ def _install_neutts_deps() -> bool:
|
|||
subprocess.run(["brew", "install", "espeak-ng"], check=True)
|
||||
elif sys.platform == "win32":
|
||||
subprocess.run(["choco", "install", "espeak-ng", "-y"], check=True)
|
||||
elif sys.platform.startswith("freebsd"):
|
||||
subprocess.run(["sudo", "pkg", "install", "-y", "espeak-ng"], check=True)
|
||||
else:
|
||||
subprocess.run(["sudo", "apt", "install", "-y", "espeak-ng"], check=True)
|
||||
print_success("espeak-ng installed")
|
||||
|
|
@ -1141,7 +1145,7 @@ def setup_terminal_backend(config: dict):
|
|||
print()
|
||||
|
||||
current_backend = cfg_get(config, "terminal", "backend", default="local")
|
||||
is_linux = _platform.system() == "Linux"
|
||||
is_linux = _platform.system() in ("Linux", "FreeBSD")
|
||||
|
||||
# Build backend choices with descriptions
|
||||
terminal_choices = [
|
||||
|
|
@ -2225,6 +2229,7 @@ def setup_gateway(config: dict):
|
|||
_is_linux = _platform.system() == "Linux"
|
||||
_is_macos = _platform.system() == "Darwin"
|
||||
_is_windows = _platform.system() == "Windows"
|
||||
_is_freebsd = _platform.system() == "FreeBSD"
|
||||
|
||||
from hermes_cli.gateway import (
|
||||
_is_service_installed,
|
||||
|
|
@ -2249,7 +2254,7 @@ def setup_gateway(config: dict):
|
|||
service_installed = _is_service_installed()
|
||||
service_running = _is_service_running()
|
||||
supports_systemd = supports_systemd_services()
|
||||
supports_service_manager = supports_systemd or _is_macos or _is_windows
|
||||
supports_service_manager = supports_systemd or _is_macos or _is_windows or _is_freebsd
|
||||
|
||||
print()
|
||||
if supports_systemd and has_conflicting_systemd_units():
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ def _node_symlink_candidate_dirs() -> "list[Path]":
|
|||
"""Directories where the installer may have placed node/npm/npx symlinks."""
|
||||
dirs: list[Path] = [Path.home() / ".local" / "bin"]
|
||||
# Root FHS installs put links in /usr/local/bin.
|
||||
if sys.platform == "linux":
|
||||
if sys.platform in ("linux", "freebsd"):
|
||||
dirs.append(Path("/usr/local/bin"))
|
||||
# Termux installs put links in $PREFIX/bin.
|
||||
prefix = os.environ.get("PREFIX", "")
|
||||
|
|
|
|||
116
scripts/install-freebsd.sh
Executable file
116
scripts/install-freebsd.sh
Executable file
|
|
@ -0,0 +1,116 @@
|
|||
#!/bin/sh
|
||||
# ============================================================================
|
||||
# Hermes Agent Installer — FreeBSD
|
||||
# ============================================================================
|
||||
# MIT Licensed — clean-room implementation for FreeBSD 14/15.
|
||||
#
|
||||
# Prerequisites: FreeBSD 14+ base system, internet access.
|
||||
# Installs: uv (Python package manager), Hermes agent, pip dependencies.
|
||||
#
|
||||
# Usage: sh scripts/install-freebsd.sh
|
||||
# ============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== Hermes Agent FreeBSD Installer ==="
|
||||
echo ""
|
||||
|
||||
# ── Ensure /usr/local/bin in PATH ──────────────────────
|
||||
case ":${PATH}:" in
|
||||
*:/usr/local/bin:*) ;;
|
||||
*) PATH="/usr/local/bin:${PATH}"; export PATH ;;
|
||||
esac
|
||||
|
||||
# ── Check FreeBSD ──────────────────────────────────────
|
||||
if [ "$(uname -s)" != "FreeBSD" ]; then
|
||||
echo "ERROR: This script is for FreeBSD only."
|
||||
echo "For Linux/macOS, use the standard Hermes installer."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Detected: $(uname -sr)"
|
||||
|
||||
# ── Install uv (Python package manager) ────────────────
|
||||
if ! command -v uv >/dev/null 2>&1; then
|
||||
echo ""
|
||||
echo "[1/4] Installing uv package manager..."
|
||||
if command -v pkg >/dev/null 2>&1; then
|
||||
sudo pkg install -y uv 2>/dev/null || {
|
||||
echo "uv not in pkg yet — installing via pip..."
|
||||
pip install --break-system-packages uv
|
||||
}
|
||||
else
|
||||
pip install --break-system-packages uv
|
||||
fi
|
||||
echo "uv installed: $(uv --version)"
|
||||
else
|
||||
echo "[1/4] uv already installed: $(uv --version)"
|
||||
fi
|
||||
|
||||
# ── Install Hermes via pip ─────────────────────────────
|
||||
echo ""
|
||||
echo "[2/4] Installing Hermes Agent..."
|
||||
|
||||
# Use uv for fast resolution, fall back to pip
|
||||
if command -v uv >/dev/null 2>&1; then
|
||||
uv pip install --system hermes-agent 2>/dev/null || \
|
||||
uv pip install --system --break-system-packages hermes-agent 2>/dev/null || \
|
||||
pip install --break-system-packages hermes-agent
|
||||
else
|
||||
pip install --break-system-packages hermes-agent
|
||||
fi
|
||||
|
||||
echo "Hermes installed."
|
||||
|
||||
# ── Verify installation ────────────────────────────────
|
||||
echo ""
|
||||
echo "[3/4] Verifying installation..."
|
||||
HERMES_BIN=$(command -v hermes 2>/dev/null || echo "/usr/local/bin/hermes")
|
||||
|
||||
if [ -x "$HERMES_BIN" ]; then
|
||||
echo "Hermes binary: $HERMES_BIN"
|
||||
hermes --version 2>/dev/null || echo "(version check skipped — first run needed)"
|
||||
else
|
||||
echo "WARNING: hermes not found on PATH."
|
||||
echo "Check: ls -la ~/.local/bin/hermes /usr/local/bin/hermes"
|
||||
fi
|
||||
|
||||
# ── Ensure pip dependencies ────────────────────────────
|
||||
echo ""
|
||||
echo "[4/4] Checking dependencies..."
|
||||
|
||||
# Packages commonly needed on FreeBSD
|
||||
MISSING=""
|
||||
for pkg in espeak-ng xclip; do
|
||||
if ! pkg info "$pkg" >/dev/null 2>&1; then
|
||||
MISSING="$MISSING $pkg"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$MISSING" ]; then
|
||||
echo ""
|
||||
echo "Optional packages for voice/clipboard support:"
|
||||
echo " sudo pkg install$MISSING"
|
||||
echo ""
|
||||
echo "Install now? [y/N]"
|
||||
read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) sudo pkg install -y$MISSING ;;
|
||||
*) echo "Skipping optional packages." ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Installation complete ==="
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. hermes setup # configure providers, tools, gateway"
|
||||
echo " 2. hermes model # pick a model (OpenRouter, DeepSeek, etc.)"
|
||||
echo " 3. hermes config # review configuration"
|
||||
echo " 4. hermes run # start using Hermes"
|
||||
echo ""
|
||||
echo "FreeBSD notes:"
|
||||
echo " - Packages live in /usr/local/bin (ensure it's in PATH)"
|
||||
echo " - Voice mode: install espeak-ng + ffmpeg (pkg install espeak-ng ffmpeg)"
|
||||
echo " - Clipboard: install xclip (pkg install xclip)"
|
||||
echo " - No systemd — use rc.d or run hermes gateway as a foreground service"
|
||||
Loading…
Add table
Reference in a new issue