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,18 +62,21 @@ echo ""
echo -e "${CYAN}${NC} Checking for uv..." echo -e "${CYAN}${NC} Checking for uv..."
UV_CMD="" UV_CMD=""
if command -v uv &> /dev/null; then 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
UV_CMD="uv" UV_CMD="uv"
elif [ -x "$HOME/.local/bin/uv" ]; then elif [ -x "$HOME/.local/bin/uv" ]; then
UV_CMD="$HOME/.local/bin/uv" UV_CMD="$HOME/.local/bin/uv"
elif [ -x "$HOME/.cargo/bin/uv" ]; then elif [ -x "$HOME/.cargo/bin/uv" ]; then
UV_CMD="$HOME/.cargo/bin/uv" UV_CMD="$HOME/.cargo/bin/uv"
fi fi
if [ -n "$UV_CMD" ]; then if [ -n "$UV_CMD" ]; then
UV_VERSION=$($UV_CMD --version 2>/dev/null) UV_VERSION=$($UV_CMD --version 2>/dev/null)
echo -e "${GREEN}${NC} uv found ($UV_VERSION)" echo -e "${GREEN}${NC} uv found ($UV_VERSION)"
else else
echo -e "${CYAN}${NC} Installing uv..." echo -e "${CYAN}${NC} Installing uv..."
if curl -LsSf https://astral.sh/uv/install.sh | sh 2>/dev/null; then if curl -LsSf https://astral.sh/uv/install.sh | sh 2>/dev/null; then
if [ -x "$HOME/.local/bin/uv" ]; then if [ -x "$HOME/.local/bin/uv" ]; then
@ -73,6 +96,7 @@ else
echo -e "${RED}${NC} Failed to install uv. Visit https://docs.astral.sh/uv/" echo -e "${RED}${NC} Failed to install uv. Visit https://docs.astral.sh/uv/"
exit 1 exit 1
fi fi
fi
fi fi
# ============================================================================ # ============================================================================
@ -81,16 +105,34 @@ fi
echo -e "${CYAN}${NC} Checking Python $PYTHON_VERSION..." echo -e "${CYAN}${NC} Checking Python $PYTHON_VERSION..."
if $UV_CMD python find "$PYTHON_VERSION" &> /dev/null; then 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
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)
echo -e "${GREEN}${NC} $PYTHON_FOUND_VERSION found" echo -e "${GREEN}${NC} $PYTHON_FOUND_VERSION found"
else else
echo -e "${CYAN}${NC} Python $PYTHON_VERSION not found, installing via uv..." echo -e "${CYAN}${NC} Python $PYTHON_VERSION not found, installing via uv..."
$UV_CMD python install "$PYTHON_VERSION" $UV_CMD python install "$PYTHON_VERSION"
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)
echo -e "${GREEN}${NC} $PYTHON_FOUND_VERSION installed" echo -e "${GREEN}${NC} $PYTHON_FOUND_VERSION installed"
fi
fi fi
# ============================================================================ # ============================================================================
@ -104,11 +146,16 @@ if [ -d "venv" ]; then
rm -rf venv rm -rf venv
fi fi
$UV_CMD venv venv --python "$PYTHON_VERSION" if is_termux; then
echo -e "${GREEN}${NC} venv created (Python $PYTHON_VERSION)" "$PYTHON_PATH" -m venv venv
echo -e "${GREEN}${NC} venv created with stdlib venv"
else
$UV_CMD venv venv --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,9 +163,23 @@ export VIRTUAL_ENV="$SCRIPT_DIR/venv"
echo -e "${CYAN}${NC} Installing dependencies..." echo -e "${CYAN}${NC} Installing dependencies..."
# Prefer uv sync with lockfile (hash-verified installs) when available, if is_termux; then
# fall back to pip install for compatibility or when lockfile is stale. export ANDROID_API_LEVEL="$(getprop ro.build.version.sdk 2>/dev/null || printf '%s' "${ANDROID_API_LEVEL:-}")"
if [ -f "uv.lock" ]; then 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,
# fall back to pip install for compatibility or when lockfile is stale.
if [ -f "uv.lock" ]; then
echo -e "${CYAN}${NC} Using uv.lock for hash-verified installation..." echo -e "${CYAN}${NC} Using uv.lock for hash-verified installation..."
UV_PROJECT_ENVIRONMENT="$SCRIPT_DIR/venv" $UV_CMD sync --all-extras --locked 2>/dev/null && \ UV_PROJECT_ENVIRONMENT="$SCRIPT_DIR/venv" $UV_CMD sync --all-extras --locked 2>/dev/null && \
echo -e "${GREEN}${NC} Dependencies installed (lockfile verified)" || { echo -e "${GREEN}${NC} Dependencies installed (lockfile verified)" || {
@ -126,9 +187,10 @@ if [ -f "uv.lock" ]; then
$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"
} }
else 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
# ============================================================================ # ============================================================================
@ -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,24 +279,30 @@ 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"
# Determine the appropriate shell config file if is_termux; then
SHELL_CONFIG="" export PATH="$COMMAND_LINK_DIR:$PATH"
if [[ "$SHELL" == *"zsh"* ]]; then echo -e "${GREEN}${NC} $COMMAND_LINK_DISPLAY_DIR is already on PATH in Termux"
else
# Determine the appropriate shell config file
SHELL_CONFIG=""
if [[ "$SHELL" == *"zsh"* ]]; then
SHELL_CONFIG="$HOME/.zshrc" SHELL_CONFIG="$HOME/.zshrc"
elif [[ "$SHELL" == *"bash"* ]]; then elif [[ "$SHELL" == *"bash"* ]]; then
SHELL_CONFIG="$HOME/.bashrc" SHELL_CONFIG="$HOME/.bashrc"
[ ! -f "$SHELL_CONFIG" ] && SHELL_CONFIG="$HOME/.bash_profile" [ ! -f "$SHELL_CONFIG" ] && SHELL_CONFIG="$HOME/.bash_profile"
else else
# Fallback to checking existing files # Fallback to checking existing files
if [ -f "$HOME/.zshrc" ]; then if [ -f "$HOME/.zshrc" ]; then
SHELL_CONFIG="$HOME/.zshrc" SHELL_CONFIG="$HOME/.zshrc"
@ -233,9 +311,9 @@ else
elif [ -f "$HOME/.bash_profile" ]; then elif [ -f "$HOME/.bash_profile" ]; then
SHELL_CONFIG="$HOME/.bash_profile" SHELL_CONFIG="$HOME/.bash_profile"
fi fi
fi fi
if [ -n "$SHELL_CONFIG" ]; then if [ -n "$SHELL_CONFIG" ]; then
# Touch the file just in case it doesn't exist yet but was selected # Touch the file just in case it doesn't exist yet but was selected
touch "$SHELL_CONFIG" 2>/dev/null || true touch "$SHELL_CONFIG" 2>/dev/null || true
@ -251,6 +329,7 @@ if [ -n "$SHELL_CONFIG" ]; then
else else
echo -e "${GREEN}${NC} ~/.local/bin already on PATH" echo -e "${GREEN}${NC} ~/.local/bin already on PATH"
fi fi
fi
fi fi
# ============================================================================ # ============================================================================
@ -281,18 +360,31 @@ echo -e "${GREEN}✓ Setup complete!${NC}"
echo "" echo ""
echo "Next steps:" echo "Next steps:"
echo "" echo ""
echo " 1. Reload your shell:" if is_termux; then
echo " source $SHELL_CONFIG" echo " 1. Run the setup wizard to configure API keys:"
echo "" echo " hermes setup"
echo " 2. Run the setup wizard to configure API keys:" echo ""
echo " hermes setup" echo " 2. Start chatting:"
echo "" echo " hermes"
echo " 3. Start chatting:" echo ""
echo " hermes" else
echo "" echo " 1. Reload your shell:"
echo " source $SHELL_CONFIG"
echo ""
echo " 2. Run the setup wizard to configure API keys:"
echo " hermes setup"
echo ""
echo " 3. Start chatting:"
echo " hermes"
echo ""
fi
echo "Other commands:" echo "Other commands:"
echo " hermes status # Check configuration" echo " hermes status # Check configuration"
echo " hermes gateway install # Install gateway service (messaging + cron)" if is_termux; then
echo " hermes gateway # Run gateway in foreground"
else
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