#!/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 "$@"