fix(termux): make setup-hermes use android path

This commit is contained in:
adybag14-cyber 2026-04-09 10:41:58 +02:00 committed by Teknium
parent 3878495972
commit 2194425918
2 changed files with 233 additions and 120 deletions

View file

@ -3,17 +3,17 @@
# Hermes Agent Setup Script # Hermes Agent Setup Script
# ============================================================================ # ============================================================================
# Quick setup for developers who cloned the repo manually. # Quick setup for developers who cloned the repo manually.
# Uses uv for fast Python provisioning and package management. # Uses uv for desktop/server setup and Python's stdlib venv + pip on Termux.
# #
# Usage: # Usage:
# ./setup-hermes.sh # ./setup-hermes.sh
# #
# This script: # This script:
# 1. Installs uv if not present # 1. Detects desktop/server vs Android/Termux setup path
# 2. Creates a virtual environment with Python 3.11 via uv # 2. Creates a Python 3.11 virtual environment
# 3. Installs all dependencies (main package + submodules) # 3. Installs the appropriate dependency set for the platform
# 4. Creates .env from template (if not exists) # 4. Creates .env from template (if not exists)
# 5. Symlinks the 'hermes' CLI command into ~/.local/bin # 5. Symlinks the 'hermes' CLI command into a user-facing bin dir
# 6. Runs the setup wizard (optional) # 6. Runs the setup wizard (optional)
# ============================================================================ # ============================================================================
@ -31,6 +31,26 @@ cd "$SCRIPT_DIR"
PYTHON_VERSION="3.11" PYTHON_VERSION="3.11"
is_termux() {
[ -n "${TERMUX_VERSION:-}" ] || [[ "${PREFIX:-}" == *"com.termux/files/usr"* ]]
}
get_command_link_dir() {
if is_termux && [ -n "${PREFIX:-}" ]; then
echo "$PREFIX/bin"
else
echo "$HOME/.local/bin"
fi
}
get_command_link_display_dir() {
if is_termux && [ -n "${PREFIX:-}" ]; then
echo '$PREFIX/bin'
else
echo '~/.local/bin'
fi
}
echo "" echo ""
echo -e "${CYAN}⚕ Hermes Agent Setup${NC}" echo -e "${CYAN}⚕ Hermes Agent Setup${NC}"
echo "" echo ""
@ -42,6 +62,9 @@ echo ""
echo -e "${CYAN}${NC} Checking for uv..." echo -e "${CYAN}${NC} Checking for uv..."
UV_CMD="" UV_CMD=""
if is_termux; then
echo -e "${CYAN}${NC} Termux detected — using Python's stdlib venv + pip instead of uv"
else
if command -v uv &> /dev/null; then if command -v uv &> /dev/null; then
UV_CMD="uv" UV_CMD="uv"
elif [ -x "$HOME/.local/bin/uv" ]; then elif [ -x "$HOME/.local/bin/uv" ]; then
@ -74,6 +97,7 @@ else
exit 1 exit 1
fi fi
fi fi
fi
# ============================================================================ # ============================================================================
# Python check (uv can provision it automatically) # Python check (uv can provision it automatically)
@ -81,6 +105,23 @@ fi
echo -e "${CYAN}${NC} Checking Python $PYTHON_VERSION..." echo -e "${CYAN}${NC} Checking Python $PYTHON_VERSION..."
if is_termux; then
if command -v python >/dev/null 2>&1; then
PYTHON_PATH="$(command -v python)"
if "$PYTHON_PATH" -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 11) else 1)' 2>/dev/null; then
PYTHON_FOUND_VERSION=$($PYTHON_PATH --version 2>/dev/null)
echo -e "${GREEN}${NC} $PYTHON_FOUND_VERSION found"
else
echo -e "${RED}${NC} Termux Python must be 3.11+"
echo " Run: pkg install python"
exit 1
fi
else
echo -e "${RED}${NC} Python not found in Termux"
echo " Run: pkg install python"
exit 1
fi
else
if $UV_CMD python find "$PYTHON_VERSION" &> /dev/null; then if $UV_CMD python find "$PYTHON_VERSION" &> /dev/null; then
PYTHON_PATH=$($UV_CMD python find "$PYTHON_VERSION") PYTHON_PATH=$($UV_CMD python find "$PYTHON_VERSION")
PYTHON_FOUND_VERSION=$($PYTHON_PATH --version 2>/dev/null) PYTHON_FOUND_VERSION=$($PYTHON_PATH --version 2>/dev/null)
@ -92,6 +133,7 @@ else
PYTHON_FOUND_VERSION=$($PYTHON_PATH --version 2>/dev/null) PYTHON_FOUND_VERSION=$($PYTHON_PATH --version 2>/dev/null)
echo -e "${GREEN}${NC} $PYTHON_FOUND_VERSION installed" echo -e "${GREEN}${NC} $PYTHON_FOUND_VERSION installed"
fi fi
fi
# ============================================================================ # ============================================================================
# Virtual environment # Virtual environment
@ -104,11 +146,16 @@ if [ -d "venv" ]; then
rm -rf venv rm -rf venv
fi fi
if is_termux; then
"$PYTHON_PATH" -m venv venv
echo -e "${GREEN}${NC} venv created with stdlib venv"
else
$UV_CMD venv venv --python "$PYTHON_VERSION" $UV_CMD venv venv --python "$PYTHON_VERSION"
echo -e "${GREEN}${NC} venv created (Python $PYTHON_VERSION)" echo -e "${GREEN}${NC} venv created (Python $PYTHON_VERSION)"
fi
# Tell uv to install into this venv (no activation needed for uv)
export VIRTUAL_ENV="$SCRIPT_DIR/venv" export VIRTUAL_ENV="$SCRIPT_DIR/venv"
SETUP_PYTHON="$SCRIPT_DIR/venv/bin/python"
# ============================================================================ # ============================================================================
# Dependencies # Dependencies
@ -116,6 +163,20 @@ export VIRTUAL_ENV="$SCRIPT_DIR/venv"
echo -e "${CYAN}${NC} Installing dependencies..." echo -e "${CYAN}${NC} Installing dependencies..."
if is_termux; then
export ANDROID_API_LEVEL="$(getprop ro.build.version.sdk 2>/dev/null || printf '%s' "${ANDROID_API_LEVEL:-}")"
echo -e "${CYAN}${NC} Termux detected — installing the tested Android bundle"
"$SETUP_PYTHON" -m pip install --upgrade pip setuptools wheel
if [ -f "constraints-termux.txt" ]; then
"$SETUP_PYTHON" -m pip install -e ".[termux]" -c constraints-termux.txt || {
echo -e "${YELLOW}${NC} Termux bundle install failed, falling back to base install..."
"$SETUP_PYTHON" -m pip install -e "." -c constraints-termux.txt
}
else
"$SETUP_PYTHON" -m pip install -e ".[termux]" || "$SETUP_PYTHON" -m pip install -e "."
fi
echo -e "${GREEN}${NC} Dependencies installed"
else
# Prefer uv sync with lockfile (hash-verified installs) when available, # Prefer uv sync with lockfile (hash-verified installs) when available,
# fall back to pip install for compatibility or when lockfile is stale. # fall back to pip install for compatibility or when lockfile is stale.
if [ -f "uv.lock" ]; then if [ -f "uv.lock" ]; then
@ -130,6 +191,7 @@ else
$UV_CMD pip install -e ".[all]" || $UV_CMD pip install -e "." $UV_CMD pip install -e ".[all]" || $UV_CMD pip install -e "."
echo -e "${GREEN}${NC} Dependencies installed" echo -e "${GREEN}${NC} Dependencies installed"
fi fi
fi
# ============================================================================ # ============================================================================
# Submodules (terminal backend + RL training) # Submodules (terminal backend + RL training)
@ -138,7 +200,9 @@ fi
echo -e "${CYAN}${NC} Installing optional submodules..." echo -e "${CYAN}${NC} Installing optional submodules..."
# tinker-atropos (RL training backend) # tinker-atropos (RL training backend)
if [ -d "tinker-atropos" ] && [ -f "tinker-atropos/pyproject.toml" ]; then if is_termux; then
echo -e "${CYAN}${NC} Skipping tinker-atropos on Termux (not part of the tested Android path)"
elif [ -d "tinker-atropos" ] && [ -f "tinker-atropos/pyproject.toml" ]; then
$UV_CMD pip install -e "./tinker-atropos" && \ $UV_CMD pip install -e "./tinker-atropos" && \
echo -e "${GREEN}${NC} tinker-atropos installed" || \ echo -e "${GREEN}${NC} tinker-atropos installed" || \
echo -e "${YELLOW}${NC} tinker-atropos install failed (RL tools may not work)" echo -e "${YELLOW}${NC} tinker-atropos install failed (RL tools may not work)"
@ -161,6 +225,9 @@ else
if [[ $REPLY =~ ^[Yy]$ ]] || [[ -z $REPLY ]]; then if [[ $REPLY =~ ^[Yy]$ ]] || [[ -z $REPLY ]]; then
INSTALLED=false INSTALLED=false
if is_termux; then
pkg install -y ripgrep && INSTALLED=true
else
# Check if sudo is available # Check if sudo is available
if command -v sudo &> /dev/null && sudo -n true 2>/dev/null; then if command -v sudo &> /dev/null && sudo -n true 2>/dev/null; then
if command -v apt &> /dev/null; then if command -v apt &> /dev/null; then
@ -180,14 +247,19 @@ else
echo -e "${CYAN}${NC} Trying cargo install (no sudo required)..." echo -e "${CYAN}${NC} Trying cargo install (no sudo required)..."
cargo install ripgrep && INSTALLED=true cargo install ripgrep && INSTALLED=true
fi fi
fi
if [ "$INSTALLED" = true ]; then if [ "$INSTALLED" = true ]; then
echo -e "${GREEN}${NC} ripgrep installed" echo -e "${GREEN}${NC} ripgrep installed"
else else
echo -e "${YELLOW}${NC} Auto-install failed. Install options:" echo -e "${YELLOW}${NC} Auto-install failed. Install options:"
if is_termux; then
echo " pkg install ripgrep # Termux / Android"
else
echo " sudo apt install ripgrep # Debian/Ubuntu" echo " sudo apt install ripgrep # Debian/Ubuntu"
echo " brew install ripgrep # macOS" echo " brew install ripgrep # macOS"
echo " cargo install ripgrep # With Rust (no sudo)" echo " cargo install ripgrep # With Rust (no sudo)"
fi
echo " https://github.com/BurntSushi/ripgrep#installation" echo " https://github.com/BurntSushi/ripgrep#installation"
fi fi
fi fi
@ -207,16 +279,22 @@ else
fi fi
# ============================================================================ # ============================================================================
# PATH setup — symlink hermes into ~/.local/bin # PATH setup — symlink hermes into a user-facing bin dir
# ============================================================================ # ============================================================================
echo -e "${CYAN}${NC} Setting up hermes command..." echo -e "${CYAN}${NC} Setting up hermes command..."
HERMES_BIN="$SCRIPT_DIR/venv/bin/hermes" HERMES_BIN="$SCRIPT_DIR/venv/bin/hermes"
mkdir -p "$HOME/.local/bin" COMMAND_LINK_DIR="$(get_command_link_dir)"
ln -sf "$HERMES_BIN" "$HOME/.local/bin/hermes" COMMAND_LINK_DISPLAY_DIR="$(get_command_link_display_dir)"
echo -e "${GREEN}${NC} Symlinked hermes → ~/.local/bin/hermes" mkdir -p "$COMMAND_LINK_DIR"
ln -sf "$HERMES_BIN" "$COMMAND_LINK_DIR/hermes"
echo -e "${GREEN}${NC} Symlinked hermes → $COMMAND_LINK_DISPLAY_DIR/hermes"
if is_termux; then
export PATH="$COMMAND_LINK_DIR:$PATH"
echo -e "${GREEN}${NC} $COMMAND_LINK_DISPLAY_DIR is already on PATH in Termux"
else
# Determine the appropriate shell config file # Determine the appropriate shell config file
SHELL_CONFIG="" SHELL_CONFIG=""
if [[ "$SHELL" == *"zsh"* ]]; then if [[ "$SHELL" == *"zsh"* ]]; then
@ -252,6 +330,7 @@ if [ -n "$SHELL_CONFIG" ]; then
echo -e "${GREEN}${NC} ~/.local/bin already on PATH" echo -e "${GREEN}${NC} ~/.local/bin already on PATH"
fi fi
fi fi
fi
# ============================================================================ # ============================================================================
# Seed bundled skills into ~/.hermes/skills/ # Seed bundled skills into ~/.hermes/skills/
@ -281,6 +360,14 @@ echo -e "${GREEN}✓ Setup complete!${NC}"
echo "" echo ""
echo "Next steps:" echo "Next steps:"
echo "" echo ""
if is_termux; then
echo " 1. Run the setup wizard to configure API keys:"
echo " hermes setup"
echo ""
echo " 2. Start chatting:"
echo " hermes"
echo ""
else
echo " 1. Reload your shell:" echo " 1. Reload your shell:"
echo " source $SHELL_CONFIG" echo " source $SHELL_CONFIG"
echo "" echo ""
@ -290,9 +377,14 @@ echo ""
echo " 3. Start chatting:" echo " 3. Start chatting:"
echo " hermes" echo " hermes"
echo "" echo ""
fi
echo "Other commands:" echo "Other commands:"
echo " hermes status # Check configuration" echo " hermes status # Check configuration"
if is_termux; then
echo " hermes gateway # Run gateway in foreground"
else
echo " hermes gateway install # Install gateway service (messaging + cron)" echo " hermes gateway install # Install gateway service (messaging + cron)"
fi
echo " hermes cron list # View scheduled jobs" echo " hermes cron list # View scheduled jobs"
echo " hermes doctor # Diagnose issues" echo " hermes doctor # Diagnose issues"
echo "" echo ""

View file

@ -0,0 +1,21 @@
from pathlib import Path
import subprocess
REPO_ROOT = Path(__file__).resolve().parents[2]
SETUP_SCRIPT = REPO_ROOT / "setup-hermes.sh"
def test_setup_hermes_script_is_valid_shell():
result = subprocess.run(["bash", "-n", str(SETUP_SCRIPT)], capture_output=True, text=True)
assert result.returncode == 0, result.stderr
def test_setup_hermes_script_has_termux_path():
content = SETUP_SCRIPT.read_text(encoding="utf-8")
assert "is_termux()" in content
assert ".[termux]" in content
assert "constraints-termux.txt" in content
assert "$PREFIX/bin" in content
assert "Skipping tinker-atropos on Termux" in content