Files
hermes-agent-packer/install-hermes.sh
2026-04-23 21:07:49 +00:00

568 lines
19 KiB
Bash
Executable File

#!/bin/bash
# ============================================================================
# Hermes Agent Full Installer for Ubuntu 24.04
# ============================================================================
# Installs Hermes Agent with Vultr Inference provider pre-configured,
# plus Docker, zsh, Oh My Zsh, Homebrew, code-server, Caddy, ttyd, and UFW.
#
# Usage:
# ./install-hermes.sh [DOMAIN]
#
# If DOMAIN is provided, Caddy will be configured with HTTPS for that domain.
#
# ============================================================================
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
NC='\033[0m'
BOLD='\033[1m'
# Configuration - Vultr Inference
VULTR_API_KEY="55AIOTBABUBU7PAZRZEKORCG4T2H2VVZQ3XQ"
VULTR_BASE_URL="https://api.vultrinference.com/v1"
DEFAULT_MODEL="zai-org/GLM-5-FP8"
TTYD_PORT="${TTYD_PORT:-7681}"
CODE_SERVER_PORT="${CODE_SERVER_PORT:-8080}"
# Domain for Caddy (optional)
DOMAIN="${1:-}"
# ============================================================================
# Helper functions
# ============================================================================
log_info() {
echo -e "${CYAN}${NC} $1"
}
log_success() {
echo -e "${GREEN}${NC} $1"
}
log_warn() {
echo -e "${YELLOW}${NC} $1"
}
log_error() {
echo -e "${RED}${NC} $1"
}
print_banner() {
echo ""
echo -e "${CYAN}${BOLD}"
echo "┌─────────────────────────────────────────────────────────┐"
echo "│ Hermes Agent + Dev Environment Installer │"
echo "└─────────────────────────────────────────────────────────┘"
echo -e "${NC}"
}
# ============================================================================
# Main installation
# ============================================================================
main() {
print_banner
# Check for Ubuntu/Debian
if [ -f /etc/os-release ]; then
. /etc/os-release
if [ "$ID" != "ubuntu" ] && [ "$ID" != "debian" ]; then
log_warn "This script is designed for Ubuntu/Debian. Proceeding anyway..."
fi
fi
# Ensure we're running as root or have sudo
if [ "$(id -u)" -ne 0 ] && ! command -v sudo &> /dev/null; then
log_error "This script requires root access or sudo"
exit 1
fi
# Run as root if we're not already
if [ "$(id -u)" -ne 0 ]; then
log_info "Requesting sudo for system package installation..."
SUDO="sudo"
else
SUDO=""
fi
export DEBIAN_FRONTEND=noninteractive
# -------------------------------------------------------------------------
# Step 1: Install system dependencies
# -------------------------------------------------------------------------
log_info "Installing system packages..."
$SUDO apt-get update -qq
$SUDO apt-get install -y -qq \
build-essential procps curl file git ca-certificates \
gnupg software-properties-common ufw jq python3 python3-venv \
zsh docker.io ttyd ri rgrep ffmpeg > /dev/null 2>&1 || true
log_success "System packages installed"
# -------------------------------------------------------------------------
# Step 2: Install Docker (official repo)
# -------------------------------------------------------------------------
log_info "Installing Docker..."
if ! command -v docker &> /dev/null; then
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| tee /etc/apt/sources.list.d/docker.list > /dev/null
$SUDO apt-get update -qq
$SUDO apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
systemctl enable docker
systemctl start docker
fi
log_success "Docker installed ($(docker --version))"
# -------------------------------------------------------------------------
# Step 3: Install Caddy
# -------------------------------------------------------------------------
log_info "Installing Caddy..."
if ! command -v caddy &> /dev/null; then
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
| tee /etc/apt/sources.list.d/caddy-stable.list
chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
chmod o+r /etc/apt/sources.list.d/caddy-stable.list
$SUDO apt-get update -qq
$SUDO apt-get install -y -qq caddy
fi
log_success "Caddy installed ($(caddy version | head -1))"
# -------------------------------------------------------------------------
# Step 4: Install code-server
# -------------------------------------------------------------------------
log_info "Installing code-server..."
if ! command -v code-server &> /dev/null; then
curl -fsSL https://code-server.dev/install.sh | sh
fi
log_success "code-server installed ($(code-server --version))"
# -------------------------------------------------------------------------
# Step 5: Create hermes user
# -------------------------------------------------------------------------
log_info "Creating hermes user..."
if ! id hermes &>/dev/null; then
useradd -m -s /usr/bin/zsh hermes
else
chsh -s /usr/bin/zsh hermes 2>/dev/null || true
fi
# Create ttyd group and add hermes to it
groupadd ttyd 2>/dev/null || true
usermod -aG ttyd hermes
usermod -aG docker hermes
# Allow hermes user's systemd services to run without an active session
loginctl enable-linger hermes 2>/dev/null || true
log_success "Hermes user created (uid=$(id -u hermes))"
# -------------------------------------------------------------------------
# Step 6: Install Oh My Zsh (as hermes user)
# -------------------------------------------------------------------------
log_info "Installing Oh My Zsh..."
if [ ! -d /home/hermes/.oh-my-zsh ]; then
su - hermes -c 'sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended'
fi
log_success "Oh My Zsh installed"
# -------------------------------------------------------------------------
# Step 7: Install Homebrew (as hermes user)
# -------------------------------------------------------------------------
log_info "Installing Homebrew..."
if [ ! -d /home/linuxbrew/.linuxbrew ]; then
# Allow hermes to sudo without password during brew install
echo 'hermes ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/hermes
su - hermes -c 'NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"'
# Persist Homebrew in all shells
cat >> /home/hermes/.bashrc <<'BREWEOF'
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
BREWEOF
cat >> /home/hermes/.profile <<'BREWEOF'
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
BREWEOF
cat >> /home/hermes/.zshrc <<'BREWEOF'
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
BREWEOF
# Turn off analytics
su - hermes -c 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" && brew analytics off'
fi
log_success "Homebrew installed"
# -------------------------------------------------------------------------
# Step 8: Clone and install Hermes Agent
# -------------------------------------------------------------------------
log_info "Installing Hermes Agent..."
HERMES_HOME="/home/hermes/.hermes"
HERMES_LOCAL="/home/hermes/.local"
# Ensure directories exist with correct ownership
mkdir -p "$HERMES_LOCAL/bin"
chown -R hermes:hermes /home/hermes
if [ ! -d "$HERMES_HOME/hermes-agent" ]; then
su - hermes -c "git clone https://github.com/NousResearch/hermes-agent.git $HERMES_HOME/hermes-agent"
fi
log_success "Hermes Agent source cloned"
# -------------------------------------------------------------------------
# Step 9: Set up Python venv for hermes user
# -------------------------------------------------------------------------
log_info "Setting up Python venv for hermes user..."
# Remove old venv if exists
rm -rf "$HERMES_HOME/hermes-agent/venv"
# Create venv and install as hermes user
su - hermes -c "cd $HERMES_HOME/hermes-agent && python3 -m venv venv && ./venv/bin/pip install --upgrade pip --quiet && ./venv/bin/pip install -e '.[all]' --quiet"
# Create symlink
su - hermes -c "ln -sf $HERMES_HOME/hermes-agent/venv/bin/hermes $HERMES_LOCAL/bin/hermes"
# Add to hermes user's PATH
grep -q 'export PATH.*.local/bin' /home/hermes/.bashrc 2>/dev/null || \
echo 'export PATH="$HOME/.local/bin:$PATH"' >> /home/hermes/.bashrc
grep -q 'export PATH.*.local/bin' /home/hermes/.zshrc 2>/dev/null || \
echo 'export PATH="$HOME/.local/bin:$PATH"' >> /home/hermes/.zshrc
log_success "Hermes venv configured"
# -------------------------------------------------------------------------
# Step 10: Configure Vultr Inference provider
# -------------------------------------------------------------------------
log_info "Configuring Vultr Inference provider..."
mkdir -p "$HERMES_HOME"/{cron,sessions,logs,pairing,hooks,image_cache,audio_cache,memories,skills}
# Create .env with Vultr API key
cat > "$HERMES_HOME/.env" << ENV_EOF
# =============================================================================
# VULTR INFERENCE PROVIDER (Pre-configured)
# =============================================================================
VULTR_API_KEY=$VULTR_API_KEY
VULTR_BASE_URL=$VULTR_BASE_URL
# For OpenAI-compatible custom endpoint mode:
OPENAI_API_KEY=$VULTR_API_KEY
OPENAI_BASE_URL=$VULTR_BASE_URL
ENV_EOF
# Create config.yaml with Vultr model configured
cat > "$HERMES_HOME/config.yaml" << 'CONFIG_EOF'
# Hermes Agent Configuration - Vultr Inference
# =============================================================================
# Pre-configured for Vultr Inference API (OpenAI-compatible)
model:
# Default model (GLM-5-FP8 - 200K context, reasoning-capable)
default: "zai-org/GLM-5-FP8"
# Use custom provider for Vultr Inference (OpenAI-compatible)
provider: "custom"
base_url: "https://api.vultrinference.com/v1"
api_key: "55AIOTBABUBU7PAZRZEKORCG4T2H2VVZQ3XQ"
# Terminal backend (local execution)
terminal:
backend: "local"
cwd: "."
timeout: 180
lifetime_seconds: 300
# Enable persistent memory
memory:
memory_enabled: true
user_profile_enabled: true
memory_char_limit: 2200
user_char_limit: 1375
nudge_interval: 10
# Context compression for long conversations
compression:
enabled: true
threshold: 0.50
target_ratio: 0.20
protect_last_n: 20
summary_model: "zai-org/GLM-5-FP8"
# Agent settings
agent:
max_turns: 60
verbose: false
reasoning_effort: "medium"
# Toolsets for CLI
platform_toolsets:
cli: [hermes-cli]
# Display settings
display:
compact: false
tool_progress: all
streaming: true
CONFIG_EOF
# Set ownership
chown -R hermes:hermes "$HERMES_HOME" "$HERMES_LOCAL"
log_success "Vultr Inference configured"
# -------------------------------------------------------------------------
# Step 11: Set up ttyd systemd service
# -------------------------------------------------------------------------
log_info "Setting up ttyd service..."
HERMES_UID=$(id -u hermes)
HERMES_GID=$(id -g hermes)
# Disable default ttyd if enabled
systemctl disable ttyd 2>/dev/null || true
systemctl stop ttyd 2>/dev/null || true
# Create systemd service with Homebrew in PATH
cat > /etc/systemd/system/ttyd-hermes.service << SERVICE
[Unit]
Description=ttyd terminal for Hermes Agent
After=network.target
[Service]
Type=simple
Environment="HOME=/home/hermes"
Environment="PATH=/home/linuxbrew/.linuxbrew/bin:/home/hermes/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ExecStart=/usr/bin/ttyd -p $TTYD_PORT -u $HERMES_UID -g $HERMES_GID -W -t fontSize=14 -t theme='{"background":"#1a1a2e","foreground":"#eee"}' /home/hermes/.local/bin/hermes
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
SERVICE
systemctl daemon-reload
systemctl enable ttyd-hermes
systemctl start ttyd-hermes
log_success "ttyd service started on port $TTYD_PORT"
# -------------------------------------------------------------------------
# Step 12: Set up code-server for hermes user
# -------------------------------------------------------------------------
log_info "Setting up code-server..."
mkdir -p /home/hermes/.config/code-server
# Create code-server config
cat > /home/hermes/.config/code-server/config.yaml << CODESERVER_EOF
bind-addr: 0.0.0.0:$CODE_SERVER_PORT
auth: password
password: hermes-dev
cert: false
CODESERVER_EOF
chown -R hermes:hermes /home/hermes/.config
# Create systemd service for code-server
cat > /etc/systemd/system/code-server-hermes.service << CODESERVICE
[Unit]
Description=code-server for Hermes
After=network.target
[Service]
Type=simple
User=hermes
Environment="HOME=/home/hermes"
Environment="PATH=/home/linuxbrew/.linuxbrew/bin:/home/hermes/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
WorkingDirectory=/home/hermes
ExecStart=/usr/bin/code-server --config /home/hermes/.config/code-server/config.yaml
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
CODESERVICE
systemctl daemon-reload
systemctl enable code-server-hermes
systemctl start code-server-hermes
log_success "code-server started on port $CODE_SERVER_PORT"
# -------------------------------------------------------------------------
# Step 13: Configure Caddy (if domain provided)
# -------------------------------------------------------------------------
if [ -n "$DOMAIN" ]; then
log_info "Configuring Caddy for $DOMAIN..."
cat > /etc/caddy/Caddyfile << CADDYFILE
# Hermes Dev Environment - $DOMAIN
# Main domain - reverse proxy to services
$DOMAIN {
# Hermes terminal (ttyd)
handle /terminal/* {
reverse_proxy localhost:$TTYD_PORT
}
# code-server
handle /code/* {
reverse_proxy localhost:$CODE_SERVER_PORT
}
# Default: serve a landing page
handle / {
respond "Hermes Dev Environment
Routes:
/terminal/ - Hermes Agent (ttyd)
/code/ - VS Code (code-server)
" 200
}
}
# Also handle www redirect
www.$DOMAIN {
redir https://$DOMAIN{uri}
}
CADDYFILE
systemctl enable caddy
systemctl restart caddy
log_success "Caddy configured for https://$DOMAIN"
else
log_info "No domain provided, skipping Caddy HTTPS setup"
systemctl disable caddy 2>/dev/null || true
systemctl stop caddy 2>/dev/null || true
fi
# -------------------------------------------------------------------------
# Step 14: Add Homebrew to system PATH
# -------------------------------------------------------------------------
log_info "Adding Homebrew to system PATH..."
cat > /etc/profile.d/homebrew.sh << 'BREWPROFILE'
# Homebrew
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
BREWPROFILE
chmod +x /etc/profile.d/homebrew.sh
log_success "Homebrew added to system PATH"
# -------------------------------------------------------------------------
# Step 15: Configure UFW firewall
# -------------------------------------------------------------------------
log_info "Configuring UFW firewall..."
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow http
ufw allow https
ufw allow "$TTYD_PORT/tcp" comment "ttyd"
ufw allow "$CODE_SERVER_PORT/tcp" comment "code-server"
ufw --force enable
log_success "UFW configured"
# -------------------------------------------------------------------------
# Step 15: Cleanup
# -------------------------------------------------------------------------
log_info "Cleaning up..."
apt-get autoremove -y -qq 2>/dev/null || true
apt-get clean
rm -rf /var/lib/apt/lists/*
# -------------------------------------------------------------------------
# Done!
# -------------------------------------------------------------------------
# Get server IP
SERVER_IP=$(curl -s ifconfig.me 2>/dev/null || curl -s icanhazip.com 2>/dev/null || echo "YOUR_SERVER_IP")
echo ""
echo -e "${GREEN}${BOLD}"
echo "┌─────────────────────────────────────────────────────────┐"
echo "│ ✓ Installation Complete! │"
echo "└─────────────────────────────────────────────────────────┘"
echo -e "${NC}"
echo ""
echo -e "${CYAN}${BOLD}📁 Hermes Configuration:${NC}"
echo ""
echo " Config: ~/.hermes/config.yaml"
echo " API Keys: ~/.hermes/.env"
echo " Data: ~/.hermes/cron/, sessions/, logs/"
echo ""
echo -e "${CYAN}${BOLD}🌐 Access URLs:${NC}"
echo ""
if [ -n "$DOMAIN" ]; then
echo " Hermes: https://$DOMAIN/terminal/"
echo " VS Code: https://$DOMAIN/code/"
else
echo " Hermes: http://$SERVER_IP:$TTYD_PORT"
echo " VS Code: http://$SERVER_IP:$CODE_SERVER_PORT"
fi
echo ""
echo -e "${CYAN}${BOLD}🔧 Services:${NC}"
echo ""
echo " ttyd-hermes - Hermes terminal (port $TTYD_PORT)"
echo " code-server-hermes - VS Code (port $CODE_SERVER_PORT)"
echo " caddy - Reverse proxy (if domain set)"
echo " docker - Container runtime"
echo ""
echo -e "${CYAN}${BOLD}🛠️ Tools Installed:${NC}"
echo ""
echo " Docker: $(docker --version | cut -d' ' -f3 | tr -d ',')"
echo " Caddy: $(caddy version 2>/dev/null | head -1 | cut -d' ' -f1 || echo 'installed')"
echo " code-server: $(code-server --version 2>/dev/null | head -1 || echo 'installed')"
echo " Homebrew: /home/linuxbrew/.linuxbrew/bin/brew"
echo " Oh My Zsh: ~/.oh-my-zsh"
echo ""
echo -e "${CYAN}${BOLD}🤖 Available Models (Vultr Inference):${NC}"
echo ""
echo " zai-org/GLM-5-FP8 (200K context, reasoning)"
echo " deepseek-ai/DeepSeek-V3.2 (128K context, reasoning)"
echo " Qwen/Qwen2.5-Coder-32B-Instruct (131K context, code)"
echo ""
if [ -n "$DOMAIN" ]; then
echo -e "${YELLOW}⚡ Open https://$DOMAIN/terminal/ to start chatting with Hermes!${NC}"
else
echo -e "${YELLOW}⚡ Open http://$SERVER_IP:$TTYD_PORT to start chatting with Hermes!${NC}"
fi
echo ""
}
main "$@"