Spaces:
Paused
Paused
Commit Β·
dfbc738
1
Parent(s): 040c488
Initial Commit
Browse files- .env.example +534 -0
- .gitattributes +8 -0
- .gitignore +15 -0
- Dockerfile +113 -0
- docker-compose.yml +16 -0
- entrypoint.sh +234 -0
- nginx-proxy.conf +10 -0
- nginx.conf +33 -0
- requirements.txt +1 -0
.env.example
ADDED
|
@@ -0,0 +1,534 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hermes Agent Environment Configuration
|
| 2 |
+
# Copy this file to .env and fill in your API keys
|
| 3 |
+
|
| 4 |
+
# HERMES_DASHBOARD_PASSWORD=
|
| 5 |
+
# HERMES_DASHBOARD_USERNAME=
|
| 6 |
+
|
| 7 |
+
# =============================================================================
|
| 8 |
+
# LLM PROVIDER (Fireworks AI)
|
| 9 |
+
# =============================================================================
|
| 10 |
+
# Get your key at: https://app.fireworks.ai/settings/users/api-keys
|
| 11 |
+
# Address models directly by catalog ID, e.g.
|
| 12 |
+
# accounts/fireworks/models/kimi-k2p6, accounts/fireworks/models/glm-5p2
|
| 13 |
+
# FIREWORKS_API_KEY=
|
| 14 |
+
# =============================================================================
|
| 15 |
+
# LLM PROVIDER (OpenRouter)
|
| 16 |
+
# =============================================================================
|
| 17 |
+
# OpenRouter provides access to many models through one API
|
| 18 |
+
# All LLM calls go through OpenRouter - no direct provider keys needed
|
| 19 |
+
# Get your key at: https://openrouter.ai/keys
|
| 20 |
+
# OPENROUTER_API_KEY=
|
| 21 |
+
|
| 22 |
+
# Default model is configured in ~/.hermes/config.yaml (model.default).
|
| 23 |
+
# Use 'hermes model' or 'hermes setup' to change it.
|
| 24 |
+
# LLM_MODEL is no longer read from .env β this line is kept for reference only.
|
| 25 |
+
# LLM_MODEL=anthropic/claude-opus-4.6
|
| 26 |
+
|
| 27 |
+
# =============================================================================
|
| 28 |
+
# LLM PROVIDER (NovitaAI)
|
| 29 |
+
# =============================================================================
|
| 30 |
+
# NovitaAI β 90+ models, pay-per-use
|
| 31 |
+
# Get your key at: https://novita.ai/settings/key-management
|
| 32 |
+
# NOVITA_API_KEY=
|
| 33 |
+
# NOVITA_BASE_URL=https://api.novita.ai/openai/v1 # Override default base URL
|
| 34 |
+
|
| 35 |
+
# =============================================================================
|
| 36 |
+
# LLM PROVIDER (Google AI Studio / Gemini)
|
| 37 |
+
# =============================================================================
|
| 38 |
+
# Native Gemini API via Google's OpenAI-compatible endpoint.
|
| 39 |
+
# Get your key at: https://aistudio.google.com/app/apikey
|
| 40 |
+
# GOOGLE_API_KEY=your_google_ai_studio_key_here
|
| 41 |
+
# GEMINI_API_KEY=your_gemini_key_here # alias for GOOGLE_API_KEY
|
| 42 |
+
# Optional base URL override (default: Google's OpenAI-compatible endpoint)
|
| 43 |
+
# GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
|
| 44 |
+
|
| 45 |
+
# =============================================================================
|
| 46 |
+
# LLM PROVIDER (Ollama Cloud)
|
| 47 |
+
# =============================================================================
|
| 48 |
+
# Cloud-hosted open models via Ollama's OpenAI-compatible endpoint.
|
| 49 |
+
# Get your key at: https://ollama.com/settings
|
| 50 |
+
# OLLAMA_API_KEY=your_ollama_key_here
|
| 51 |
+
# Optional base URL override (default: https://ollama.com/v1)
|
| 52 |
+
# OLLAMA_BASE_URL=https://ollama.com/v1
|
| 53 |
+
|
| 54 |
+
# =============================================================================
|
| 55 |
+
# LLM PROVIDER (z.ai / GLM)
|
| 56 |
+
# =============================================================================
|
| 57 |
+
# z.ai provides access to ZhipuAI GLM models (GLM-4-Plus, etc.)
|
| 58 |
+
# Get your key at: https://z.ai or https://open.bigmodel.cn
|
| 59 |
+
# GLM_API_KEY=
|
| 60 |
+
# GLM_BASE_URL=https://api.z.ai/api/paas/v4 # Override default base URL
|
| 61 |
+
|
| 62 |
+
# =============================================================================
|
| 63 |
+
# LLM PROVIDER (Kimi / Moonshot)
|
| 64 |
+
# =============================================================================
|
| 65 |
+
# Kimi Code provides access to Moonshot AI coding models (kimi-k2.5, etc.)
|
| 66 |
+
# Get your key at: https://platform.kimi.ai (Kimi Code console)
|
| 67 |
+
# Keys prefixed sk-kimi- use the Kimi Code API (api.kimi.com) by default.
|
| 68 |
+
# Legacy keys from platform.moonshot.ai need KIMI_BASE_URL override below.
|
| 69 |
+
# KIMI_API_KEY=
|
| 70 |
+
# KIMI_BASE_URL=https://api.kimi.com/coding/v1 # Default for sk-kimi- keys
|
| 71 |
+
# KIMI_BASE_URL=https://api.moonshot.ai/v1 # For legacy Moonshot keys
|
| 72 |
+
# KIMI_BASE_URL=https://api.moonshot.cn/v1 # For Moonshot China keys
|
| 73 |
+
# KIMI_CN_API_KEY= # Dedicated Moonshot China key
|
| 74 |
+
|
| 75 |
+
# =============================================================================
|
| 76 |
+
# LLM PROVIDER (Arcee AI)
|
| 77 |
+
# =============================================================================
|
| 78 |
+
# Arcee AI provides access to Trinity models (trinity-mini, trinity-large-*)
|
| 79 |
+
# Get an Arcee key at: https://chat.arcee.ai/
|
| 80 |
+
# ARCEEAI_API_KEY=
|
| 81 |
+
# ARCEE_BASE_URL= # Override default base URL
|
| 82 |
+
|
| 83 |
+
# =============================================================================
|
| 84 |
+
# LLM PROVIDER (MiniMax)
|
| 85 |
+
# =============================================================================
|
| 86 |
+
# MiniMax provides access to MiniMax models (global endpoint)
|
| 87 |
+
# Get your key at: https://www.minimax.io
|
| 88 |
+
# MINIMAX_API_KEY=
|
| 89 |
+
# MINIMAX_BASE_URL=https://api.minimax.io/v1 # Override default base URL
|
| 90 |
+
|
| 91 |
+
# MiniMax China endpoint (for users in mainland China)
|
| 92 |
+
# MINIMAX_CN_API_KEY=
|
| 93 |
+
# MINIMAX_CN_BASE_URL=https://api.minimaxi.com/v1 # Override default base URL
|
| 94 |
+
|
| 95 |
+
# =============================================================================
|
| 96 |
+
# LLM PROVIDER (OpenCode Zen)
|
| 97 |
+
# =============================================================================
|
| 98 |
+
# OpenCode Zen provides curated, tested models (GPT, Claude, Gemini, MiniMax, GLM, Kimi)
|
| 99 |
+
# Pay-as-you-go pricing. Get your key at: https://opencode.ai/auth
|
| 100 |
+
# OPENCODE_ZEN_API_KEY=
|
| 101 |
+
# OPENCODE_ZEN_BASE_URL=https://opencode.ai/zen/v1 # Override default base URL
|
| 102 |
+
|
| 103 |
+
# =============================================================================
|
| 104 |
+
# LLM PROVIDER (OpenCode Go)
|
| 105 |
+
# =============================================================================
|
| 106 |
+
# OpenCode Go provides access to open models (GLM-5, Kimi K2.5, MiniMax M2.5)
|
| 107 |
+
# $10/month subscription. Get your key at: https://opencode.ai/auth
|
| 108 |
+
# OPENCODE_GO_API_KEY=
|
| 109 |
+
|
| 110 |
+
# =============================================================================
|
| 111 |
+
# LLM PROVIDER (Hugging Face Inference Providers)
|
| 112 |
+
# =============================================================================
|
| 113 |
+
# Hugging Face routes to 20+ open models via unified OpenAI-compatible endpoint.
|
| 114 |
+
# Free tier included ($0.10/month), no markup on provider rates.
|
| 115 |
+
# Get your token at: https://huggingface.co/settings/tokens
|
| 116 |
+
# Required permission: "Make calls to Inference Providers"
|
| 117 |
+
# HF_TOKEN=
|
| 118 |
+
# HF_BASE_URL=https://router.huggingface.co/v1 # Override default base URL
|
| 119 |
+
# OPENCODE_GO_BASE_URL=https://opencode.ai/zen/go/v1 # Override default base URL
|
| 120 |
+
|
| 121 |
+
# DeepInfra β 100+ top open models, pay-per-use.
|
| 122 |
+
# Get your key at: https://deepinfra.com/dash/api_keys
|
| 123 |
+
# DEEPINFRA_API_KEY=
|
| 124 |
+
|
| 125 |
+
# =============================================================================
|
| 126 |
+
# LLM PROVIDER (Qwen OAuth)
|
| 127 |
+
# =============================================================================
|
| 128 |
+
# Qwen OAuth reuses your local Qwen CLI login (qwen auth qwen-oauth).
|
| 129 |
+
# No API key needed β credentials come from ~/.qwen/oauth_creds.json.
|
| 130 |
+
# Optional base URL override:
|
| 131 |
+
# HERMES_QWEN_BASE_URL=https://portal.qwen.ai/v1
|
| 132 |
+
|
| 133 |
+
# =============================================================================
|
| 134 |
+
# LLM PROVIDER (Xiaomi MiMo)
|
| 135 |
+
# =============================================================================
|
| 136 |
+
# Xiaomi MiMo models (mimo-v2-pro, mimo-v2-omni, mimo-v2-flash).
|
| 137 |
+
# Get your key at: https://platform.xiaomimimo.com
|
| 138 |
+
# XIAOMI_API_KEY=your_key_here
|
| 139 |
+
# Optional base URL override:
|
| 140 |
+
# XIAOMI_BASE_URL=https://api.xiaomimimo.com/v1
|
| 141 |
+
|
| 142 |
+
# =============================================================================
|
| 143 |
+
# LLM PROVIDER (Upstage Solar)
|
| 144 |
+
# =============================================================================
|
| 145 |
+
# Upstage provides access to Upstage Solar models.
|
| 146 |
+
# Get your key at: https://console.upstage.ai/api-keys
|
| 147 |
+
# UPSTAGE_API_KEY=your_key_here
|
| 148 |
+
# Optional base URL override:
|
| 149 |
+
# UPSTAGE_BASE_URL=https://api.upstage.ai/v1
|
| 150 |
+
|
| 151 |
+
# =============================================================================
|
| 152 |
+
# TOOL API KEYS
|
| 153 |
+
# =============================================================================
|
| 154 |
+
|
| 155 |
+
# Exa API Key - AI-native web search and contents
|
| 156 |
+
# Get at: https://exa.ai
|
| 157 |
+
# EXA_API_KEY=
|
| 158 |
+
|
| 159 |
+
# Parallel API Key - AI-native web search and extract
|
| 160 |
+
# Get at: https://parallel.ai
|
| 161 |
+
# PARALLEL_API_KEY=
|
| 162 |
+
|
| 163 |
+
# Firecrawl API Key - Web search, extract, and crawl
|
| 164 |
+
# Get at: https://firecrawl.dev/
|
| 165 |
+
# FIRECRAWL_API_KEY=
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
# FAL.ai API Key - Image generation
|
| 169 |
+
# Get at: https://fal.ai/
|
| 170 |
+
# FAL_KEY=
|
| 171 |
+
|
| 172 |
+
# Honcho - Cross-session AI-native user modeling (optional)
|
| 173 |
+
# Builds a persistent understanding of the user across sessions and tools.
|
| 174 |
+
# Get at: https://app.honcho.dev
|
| 175 |
+
# Also requires ~/.honcho/config.json with enabled=true (see README).
|
| 176 |
+
# HONCHO_API_KEY=
|
| 177 |
+
|
| 178 |
+
# =============================================================================
|
| 179 |
+
# HYPERLIQUID OPTIONAL SKILL
|
| 180 |
+
# =============================================================================
|
| 181 |
+
# Optional defaults for the Hyperliquid skill in optional-skills/blockchain/hyperliquid
|
| 182 |
+
#
|
| 183 |
+
# Hyperliquid API base URL override
|
| 184 |
+
# Default: https://api.hyperliquid.xyz
|
| 185 |
+
# HYPERLIQUID_API_URL=https://api.hyperliquid-testnet.xyz
|
| 186 |
+
#
|
| 187 |
+
# Default address for account-level commands like state, fills, orders, and review
|
| 188 |
+
# HYPERLIQUID_USER_ADDRESS=0x0000000000000000000000000000000000000000
|
| 189 |
+
|
| 190 |
+
# =============================================================================
|
| 191 |
+
# TERMINAL TOOL CONFIGURATION
|
| 192 |
+
# =============================================================================
|
| 193 |
+
# Backend type: "local", "singularity", "docker", "modal", or "ssh"
|
| 194 |
+
# Terminal backend is configured in ~/.hermes/config.yaml (terminal.backend).
|
| 195 |
+
# Use 'hermes setup' or 'hermes config set terminal.backend docker' to change.
|
| 196 |
+
# Supported: local, docker, singularity, modal, ssh
|
| 197 |
+
#
|
| 198 |
+
# Only override here if you need to force a backend without touching config.yaml:
|
| 199 |
+
# TERMINAL_ENV=local
|
| 200 |
+
|
| 201 |
+
# Override the container runtime binary (e.g. to use Podman instead of Docker).
|
| 202 |
+
# Useful on systems where Docker's storage driver is broken or unavailable.
|
| 203 |
+
# HERMES_DOCKER_BINARY=/usr/local/bin/podman
|
| 204 |
+
|
| 205 |
+
# Container images (for singularity/docker/modal backends)
|
| 206 |
+
# TERMINAL_DOCKER_IMAGE=nikolaik/python-nodejs:python3.11-nodejs20
|
| 207 |
+
# TERMINAL_SINGULARITY_IMAGE=docker://nikolaik/python-nodejs:python3.11-nodejs20
|
| 208 |
+
TERMINAL_MODAL_IMAGE=nikolaik/python-nodejs:python3.11-nodejs20
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
# Working directory for terminal commands
|
| 212 |
+
# For local backend: "." means current directory (resolved automatically)
|
| 213 |
+
# For remote backends (ssh/docker/modal/singularity): use an absolute path
|
| 214 |
+
# INSIDE the target environment, or leave unset for the backend's default
|
| 215 |
+
# (/root for modal, / for docker, ~ for ssh). Do NOT use a host-local path.
|
| 216 |
+
# Usually managed by config.yaml (terminal.cwd) β uncomment to override
|
| 217 |
+
# TERMINAL_CWD=.
|
| 218 |
+
|
| 219 |
+
# Default command timeout in seconds
|
| 220 |
+
TERMINAL_TIMEOUT=60
|
| 221 |
+
|
| 222 |
+
# Cleanup inactive environments after this many seconds
|
| 223 |
+
TERMINAL_LIFETIME_SECONDS=300
|
| 224 |
+
|
| 225 |
+
# =============================================================================
|
| 226 |
+
# SSH REMOTE EXECUTION (for TERMINAL_ENV=ssh)
|
| 227 |
+
# =============================================================================
|
| 228 |
+
# Run terminal commands on a remote server via SSH.
|
| 229 |
+
# Agent code stays on your machine, commands execute remotely.
|
| 230 |
+
#
|
| 231 |
+
# SECURITY BENEFITS:
|
| 232 |
+
# - Agent cannot read your .env file (API keys protected)
|
| 233 |
+
# - Agent cannot modify its own code
|
| 234 |
+
# - Remote server acts as isolated sandbox
|
| 235 |
+
# - Can safely configure passwordless sudo on remote
|
| 236 |
+
#
|
| 237 |
+
# TERMINAL_SSH_HOST=192.168.1.100
|
| 238 |
+
# TERMINAL_SSH_USER=agent
|
| 239 |
+
# TERMINAL_SSH_PORT=22
|
| 240 |
+
# TERMINAL_SSH_KEY=~/.ssh/id_rsa
|
| 241 |
+
|
| 242 |
+
# =============================================================================
|
| 243 |
+
# SUDO SUPPORT (works with ALL terminal backends)
|
| 244 |
+
# =============================================================================
|
| 245 |
+
# If set, enables sudo commands by piping password via `sudo -S`.
|
| 246 |
+
# Works with: local, docker, singularity, modal, and ssh backends.
|
| 247 |
+
#
|
| 248 |
+
# SECURITY WARNING: Password stored in plaintext. Only use on trusted machines.
|
| 249 |
+
#
|
| 250 |
+
# ALTERNATIVES:
|
| 251 |
+
# - For SSH backend: Configure passwordless sudo on the remote server
|
| 252 |
+
# - For containers: Run as root inside the container (no sudo needed)
|
| 253 |
+
# - For local: Configure /etc/sudoers for specific commands
|
| 254 |
+
# - For CLI: Leave unset - you'll be prompted interactively with 45s timeout
|
| 255 |
+
#
|
| 256 |
+
# SUDO_PASSWORD=your_password_here
|
| 257 |
+
|
| 258 |
+
# =============================================================================
|
| 259 |
+
# MODAL CLOUD BACKEND (Optional - for TERMINAL_ENV=modal)
|
| 260 |
+
# =============================================================================
|
| 261 |
+
# Modal uses CLI authentication, not environment variables.
|
| 262 |
+
# Run: pip install modal && modal setup
|
| 263 |
+
# This will authenticate via browser and store credentials locally.
|
| 264 |
+
# No API key needed in .env - Modal handles auth automatically.
|
| 265 |
+
|
| 266 |
+
# =============================================================================
|
| 267 |
+
# BROWSER TOOL CONFIGURATION (agent-browser + Browserbase)
|
| 268 |
+
# =============================================================================
|
| 269 |
+
# Browser automation requires Browserbase cloud service for remote browser execution.
|
| 270 |
+
# This allows the agent to navigate websites, fill forms, and extract information.
|
| 271 |
+
#
|
| 272 |
+
# STEALTH MODES:
|
| 273 |
+
# - Basic Stealth: ALWAYS active (random fingerprints, auto CAPTCHA solving)
|
| 274 |
+
# - Advanced Stealth: Requires BROWSERBASE_ADVANCED_STEALTH=true (Scale Plan only)
|
| 275 |
+
|
| 276 |
+
# Browserbase API Key - Cloud browser execution
|
| 277 |
+
# Get at: https://browserbase.com/
|
| 278 |
+
# BROWSERBASE_API_KEY=
|
| 279 |
+
|
| 280 |
+
# Browserbase Project ID - From your Browserbase dashboard
|
| 281 |
+
# BROWSERBASE_PROJECT_ID=
|
| 282 |
+
|
| 283 |
+
# Enable residential proxies for better CAPTCHA solving (default: true)
|
| 284 |
+
# Routes traffic through residential IPs, significantly improves success rate
|
| 285 |
+
BROWSERBASE_PROXIES=true
|
| 286 |
+
|
| 287 |
+
# Enable advanced stealth mode (default: false, requires Scale Plan)
|
| 288 |
+
# Uses custom Chromium build to avoid bot detection altogether
|
| 289 |
+
BROWSERBASE_ADVANCED_STEALTH=false
|
| 290 |
+
|
| 291 |
+
# Browser engine for local mode (default: auto = Chrome)
|
| 292 |
+
# "auto" β use Chrome (don't pass --engine flag)
|
| 293 |
+
# "lightpanda" β use Lightpanda (1.3-5.8x faster navigation, no screenshots)
|
| 294 |
+
# "chrome" β explicitly request Chrome
|
| 295 |
+
# Requires agent-browser v0.25.3+. Lightpanda commands that fail or return
|
| 296 |
+
# empty results are automatically retried with Chrome.
|
| 297 |
+
# Also configurable via browser.engine in config.yaml.
|
| 298 |
+
# AGENT_BROWSER_ENGINE=auto
|
| 299 |
+
|
| 300 |
+
# Browser session timeout in seconds (default: 300)
|
| 301 |
+
# Sessions are cleaned up after this duration of inactivity
|
| 302 |
+
BROWSER_SESSION_TIMEOUT=300
|
| 303 |
+
|
| 304 |
+
# Browser inactivity timeout - auto-cleanup inactive sessions (default: 120 = 2 min)
|
| 305 |
+
# Browser sessions are automatically closed after this period of no activity
|
| 306 |
+
BROWSER_INACTIVITY_TIMEOUT=120
|
| 307 |
+
|
| 308 |
+
# Extra Chromium launch flags passed to agent-browser, comma- or newline-separated.
|
| 309 |
+
# Hermes auto-injects "--no-sandbox,--disable-dev-shm-usage" when it detects root
|
| 310 |
+
# or AppArmor-restricted unprivileged user namespaces (Ubuntu 23.10+, DGX Spark,
|
| 311 |
+
# many container images), so leave this unset unless you need extra flags.
|
| 312 |
+
# Setting this disables the auto-injection.
|
| 313 |
+
# AGENT_BROWSER_ARGS=--no-sandbox
|
| 314 |
+
|
| 315 |
+
# Camofox local anti-detection browser (Camoufox-based Firefox).
|
| 316 |
+
# Set CAMOFOX_URL to route the browser tools through a local Camofox server
|
| 317 |
+
# instead of agent-browser/Browserbase. See docs/user-guide/features/browser.md.
|
| 318 |
+
# CAMOFOX_URL=http://localhost:9377
|
| 319 |
+
|
| 320 |
+
# Externally managed Camofox sessions β when another app owns the visible
|
| 321 |
+
# Camofox browser, set these so Hermes shares the same userId/profile instead
|
| 322 |
+
# of creating its own isolated session.
|
| 323 |
+
# CAMOFOX_USER_ID=
|
| 324 |
+
# CAMOFOX_SESSION_KEY=
|
| 325 |
+
# Set to true to reuse an already-open Camofox tab for this identity before
|
| 326 |
+
# creating a new one (useful for gateway restarts).
|
| 327 |
+
# CAMOFOX_ADOPT_EXISTING_TAB=false
|
| 328 |
+
|
| 329 |
+
# =============================================================================
|
| 330 |
+
# SESSION LOGGING
|
| 331 |
+
# =============================================================================
|
| 332 |
+
# Session trajectories are automatically saved to logs/ directory
|
| 333 |
+
# Format: logs/session_YYYYMMDD_HHMMSS_UUID.json
|
| 334 |
+
# Contains full conversation history in trajectory format for debugging/replay
|
| 335 |
+
|
| 336 |
+
# =============================================================================
|
| 337 |
+
# VOICE TRANSCRIPTION & OPENAI TTS
|
| 338 |
+
# =============================================================================
|
| 339 |
+
# Required for voice message transcription (Whisper) and OpenAI TTS voices.
|
| 340 |
+
# Uses OpenAI's API directly (not via OpenRouter).
|
| 341 |
+
# Named VOICE_TOOLS_OPENAI_KEY to avoid interference with OpenRouter.
|
| 342 |
+
# Get at: https://platform.openai.com/api-keys
|
| 343 |
+
# VOICE_TOOLS_OPENAI_KEY=
|
| 344 |
+
|
| 345 |
+
# =============================================================================
|
| 346 |
+
# SLACK INTEGRATION
|
| 347 |
+
# =============================================================================
|
| 348 |
+
# Slack Bot Token - From Slack App settings (OAuth & Permissions)
|
| 349 |
+
# Get at: https://api.slack.com/apps
|
| 350 |
+
# SLACK_BOT_TOKEN=xoxb-...
|
| 351 |
+
|
| 352 |
+
# Slack App Token - For Socket Mode (App-Level Tokens in Slack App settings)
|
| 353 |
+
# SLACK_APP_TOKEN=xapp-...
|
| 354 |
+
|
| 355 |
+
# Slack allowed users (comma-separated Slack user IDs)
|
| 356 |
+
# SLACK_ALLOWED_USERS=
|
| 357 |
+
|
| 358 |
+
# =============================================================================
|
| 359 |
+
# TELEGRAM INTEGRATION
|
| 360 |
+
# =============================================================================
|
| 361 |
+
# Telegram Bot Token - From @BotFather (https://t.me/BotFather)
|
| 362 |
+
# TELEGRAM_BOT_TOKEN=
|
| 363 |
+
# TELEGRAM_ALLOWED_USERS= # Comma-separated user IDs
|
| 364 |
+
# TELEGRAM_HOME_CHANNEL= # Default chat for cron delivery
|
| 365 |
+
# TELEGRAM_HOME_CHANNEL_NAME= # Display name for home channel
|
| 366 |
+
# TELEGRAM_CRON_THREAD_ID= # Forum topic ID for cron deliveries; overrides TELEGRAM_HOME_CHANNEL_THREAD_ID for cron so replies work in topic mode
|
| 367 |
+
|
| 368 |
+
# Webhook mode (optional β for cloud deployments like Fly.io/Railway)
|
| 369 |
+
# Default is long polling. Setting TELEGRAM_WEBHOOK_URL switches to webhook mode.
|
| 370 |
+
# TELEGRAM_WEBHOOK_URL=https://my-app.fly.dev/telegram
|
| 371 |
+
# TELEGRAM_WEBHOOK_PORT=8443
|
| 372 |
+
# TELEGRAM_WEBHOOK_SECRET= # Recommended for production
|
| 373 |
+
|
| 374 |
+
# WhatsApp (built-in Baileys bridge β run `hermes whatsapp` to pair)
|
| 375 |
+
# WHATSAPP_ENABLED=false
|
| 376 |
+
# WHATSAPP_ALLOWED_USERS=15551234567
|
| 377 |
+
|
| 378 |
+
# Email (IMAP/SMTP β send and receive emails as Hermes)
|
| 379 |
+
# For Gmail: enable 2FA β create App Password at https://myaccount.google.com/apppasswords
|
| 380 |
+
# EMAIL_ADDRESS=hermes@gmail.com
|
| 381 |
+
# EMAIL_PASSWORD=xxxx xxxx xxxx xxxx
|
| 382 |
+
# EMAIL_IMAP_HOST=imap.gmail.com
|
| 383 |
+
# EMAIL_IMAP_PORT=993
|
| 384 |
+
# EMAIL_SMTP_HOST=smtp.gmail.com
|
| 385 |
+
# EMAIL_SMTP_PORT=587
|
| 386 |
+
# EMAIL_POLL_INTERVAL=15
|
| 387 |
+
# EMAIL_ALLOWED_USERS=your@email.com
|
| 388 |
+
# EMAIL_HOME_ADDRESS=your@email.com
|
| 389 |
+
|
| 390 |
+
# Gateway-wide: allow ALL users without an allowlist (default: false = deny)
|
| 391 |
+
# Only set to true if you intentionally want open access.
|
| 392 |
+
# GATEWAY_ALLOW_ALL_USERS=false
|
| 393 |
+
|
| 394 |
+
# =============================================================================
|
| 395 |
+
# RESPONSE PACING
|
| 396 |
+
# =============================================================================
|
| 397 |
+
# Human-like delays between message chunks on messaging platforms.
|
| 398 |
+
# Makes the bot feel less robotic.
|
| 399 |
+
# HERMES_HUMAN_DELAY_MODE=off # off | natural | custom
|
| 400 |
+
# HERMES_HUMAN_DELAY_MIN_MS=800 # Min delay in ms (custom mode)
|
| 401 |
+
# HERMES_HUMAN_DELAY_MAX_MS=2500 # Max delay in ms (custom mode)
|
| 402 |
+
|
| 403 |
+
# =============================================================================
|
| 404 |
+
# DEBUG OPTIONS
|
| 405 |
+
# =============================================================================
|
| 406 |
+
WEB_TOOLS_DEBUG=false
|
| 407 |
+
VISION_TOOLS_DEBUG=false
|
| 408 |
+
MOA_TOOLS_DEBUG=false
|
| 409 |
+
IMAGE_TOOLS_DEBUG=false
|
| 410 |
+
|
| 411 |
+
# =============================================================================
|
| 412 |
+
# CONTEXT COMPRESSION (Auto-shrinks long conversations)
|
| 413 |
+
# =============================================================================
|
| 414 |
+
# When conversation approaches model's context limit, middle turns are
|
| 415 |
+
# automatically summarized to free up space.
|
| 416 |
+
#
|
| 417 |
+
# Context compression is configured in ~/.hermes/config.yaml under compression:
|
| 418 |
+
# CONTEXT_COMPRESSION_ENABLED=true # Enable auto-compression (default: true)
|
| 419 |
+
# CONTEXT_COMPRESSION_THRESHOLD=0.85 # Compress at 85% of context limit
|
| 420 |
+
# Model is set via compression.summary_model in config.yaml (default: google/gemini-3-flash-preview)
|
| 421 |
+
|
| 422 |
+
# =============================================================================
|
| 423 |
+
# SKILLS HUB (GitHub integration for skill search/install/publish)
|
| 424 |
+
# =============================================================================
|
| 425 |
+
|
| 426 |
+
# GitHub Personal Access Token β for higher API rate limits on skill search/install
|
| 427 |
+
# Get at: https://github.com/settings/tokens (Fine-grained recommended)
|
| 428 |
+
# GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
|
| 429 |
+
|
| 430 |
+
# GitHub App credentials (optional β for bot identity on PRs)
|
| 431 |
+
# GITHUB_APP_ID=
|
| 432 |
+
# GITHUB_APP_PRIVATE_KEY_PATH=
|
| 433 |
+
# GITHUB_APP_INSTALLATION_ID=
|
| 434 |
+
|
| 435 |
+
# Groq API key (free tier β used for Whisper STT in voice mode)
|
| 436 |
+
# GROQ_API_KEY=
|
| 437 |
+
|
| 438 |
+
# ElevenLabs API key (cloud STT/TTS β Scribe transcription)
|
| 439 |
+
# ELEVENLABS_API_KEY=
|
| 440 |
+
|
| 441 |
+
# =============================================================================
|
| 442 |
+
# STT PROVIDER SELECTION
|
| 443 |
+
# =============================================================================
|
| 444 |
+
# Default STT provider is "local" (faster-whisper) β runs on your machine, no API key needed.
|
| 445 |
+
# Install with: pip install faster-whisper
|
| 446 |
+
# Model downloads automatically on first use (~150 MB for "base").
|
| 447 |
+
# To use cloud providers instead, set GROQ_API_KEY, VOICE_TOOLS_OPENAI_KEY, or ELEVENLABS_API_KEY above.
|
| 448 |
+
# Provider priority: local > groq > openai > mistral > xai > elevenlabs
|
| 449 |
+
# Configure in config.yaml: stt.provider: local | groq | openai | mistral | xai | elevenlabs
|
| 450 |
+
|
| 451 |
+
# =============================================================================
|
| 452 |
+
# STT ADVANCED OVERRIDES (optional)
|
| 453 |
+
# =============================================================================
|
| 454 |
+
# Override default STT models per provider (normally set via stt.model in config.yaml)
|
| 455 |
+
# STT_GROQ_MODEL=whisper-large-v3-turbo
|
| 456 |
+
# STT_OPENAI_MODEL=whisper-1
|
| 457 |
+
# STT_ELEVENLABS_MODEL=scribe_v2
|
| 458 |
+
|
| 459 |
+
# Override STT provider endpoints (for proxies or self-hosted instances)
|
| 460 |
+
# GROQ_BASE_URL=https://api.groq.com/openai/v1
|
| 461 |
+
# STT_OPENAI_BASE_URL=https://api.openai.com/v1
|
| 462 |
+
# ELEVENLABS_STT_BASE_URL=https://api.elevenlabs.io/v1
|
| 463 |
+
|
| 464 |
+
# =============================================================================
|
| 465 |
+
# MICROSOFT TEAMS INTEGRATION
|
| 466 |
+
# =============================================================================
|
| 467 |
+
# Register a Bot in Azure: https://dev.botframework.com/ β "Register a bot"
|
| 468 |
+
# Or use Azure Portal: Azure Active Directory β App registrations β New registration
|
| 469 |
+
# Then add the bot to Teams via the Bot Framework or App Studio.
|
| 470 |
+
#
|
| 471 |
+
# TEAMS_CLIENT_ID= # Azure AD App (client) ID
|
| 472 |
+
# TEAMS_CLIENT_SECRET= # Azure AD client secret value
|
| 473 |
+
# TEAMS_TENANT_ID= # Azure AD tenant ID (or "common" for multi-tenant)
|
| 474 |
+
# TEAMS_ALLOWED_USERS= # Comma-separated AAD object IDs or UPNs
|
| 475 |
+
# TEAMS_ALLOW_ALL_USERS=false # Set true to skip the allowlist
|
| 476 |
+
# TEAMS_HOME_CHANNEL= # Default channel/chat ID for cron delivery
|
| 477 |
+
# TEAMS_HOME_CHANNEL_NAME= # Display name for the home channel
|
| 478 |
+
# TEAMS_PORT=3978 # Webhook listen port (Bot Framework default)
|
| 479 |
+
|
| 480 |
+
# =============================================================================
|
| 481 |
+
# GOOGLE CHAT INTEGRATION
|
| 482 |
+
# =============================================================================
|
| 483 |
+
# Connects via Cloud Pub/Sub pull subscription (no public URL required).
|
| 484 |
+
# Setup walkthrough: website/docs/user-guide/messaging/google_chat.md.
|
| 485 |
+
# 1. Create a GCP project, enable the Google Chat API and Cloud Pub/Sub.
|
| 486 |
+
# 2. Create a Service Account with roles/pubsub.subscriber on the
|
| 487 |
+
# subscription (NOT project-wide); download the JSON key.
|
| 488 |
+
# 3. Configure your Chat app at console.cloud.google.com/apis/credentials
|
| 489 |
+
# β Google Chat API β Configuration β Cloud Pub/Sub topic.
|
| 490 |
+
# 4. (Optional, for native attachment delivery) Each user runs
|
| 491 |
+
# `/setup-files` once in their own DM after Pub/Sub is wired up.
|
| 492 |
+
#
|
| 493 |
+
# GOOGLE_CHAT_PROJECT_ID= # GCP project hosting the topic (or set GOOGLE_CLOUD_PROJECT)
|
| 494 |
+
# GOOGLE_CHAT_SUBSCRIPTION_NAME= # Full path: projects/<id>/subscriptions/<name>
|
| 495 |
+
# GOOGLE_CHAT_SERVICE_ACCOUNT_JSON= # Path to SA JSON (or set GOOGLE_APPLICATION_CREDENTIALS)
|
| 496 |
+
# GOOGLE_CHAT_ALLOWED_USERS= # Comma-separated emails allowed to talk to the bot
|
| 497 |
+
# GOOGLE_CHAT_ALLOW_ALL_USERS=false # Set true to skip the allowlist
|
| 498 |
+
# GOOGLE_CHAT_HOME_CHANNEL= # Default space (spaces/XXXX) for cron delivery
|
| 499 |
+
# GOOGLE_CHAT_HOME_CHANNEL_NAME= # Display name for the home channel
|
| 500 |
+
|
| 501 |
+
# =============================================================================
|
| 502 |
+
# A2A INTEGRATION
|
| 503 |
+
# =============================================================================
|
| 504 |
+
|
| 505 |
+
# A2A_AGENT_NAME=
|
| 506 |
+
# A2A_HOST=
|
| 507 |
+
# A2A_BEARER_TOKEN=
|
| 508 |
+
|
| 509 |
+
# =============================================================================
|
| 510 |
+
# A2A INTEGRATION
|
| 511 |
+
# =============================================================================
|
| 512 |
+
|
| 513 |
+
# API_SERVER_HOST=
|
| 514 |
+
# API_SERVER_ENABLED=
|
| 515 |
+
# API_SERVER_KEY=
|
| 516 |
+
|
| 517 |
+
# =============================================================================
|
| 518 |
+
# BUZZ INTEGRATION
|
| 519 |
+
# =============================================================================
|
| 520 |
+
|
| 521 |
+
# BUZZ_RELAY_URL=
|
| 522 |
+
# BUZZ_PRIVATE_KEY=
|
| 523 |
+
# BUZZ_HOME_CHANNEL_THREAD_ID=
|
| 524 |
+
# BUZZ_ALLOWED_USERS=123123123,1231231234
|
| 525 |
+
# BUZZ_DISPLAY_NAME=
|
| 526 |
+
# BUZZ_TRANSPORT=
|
| 527 |
+
|
| 528 |
+
# =============================================================================
|
| 529 |
+
# DISCORD INTEGRATION
|
| 530 |
+
# =============================================================================
|
| 531 |
+
|
| 532 |
+
# DISCORD_BOT_TOKEN=
|
| 533 |
+
# DISCORD_GUILD_ID=
|
| 534 |
+
# DISCORD_CHANNEL_ID=
|
.gitattributes
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
*.bin filter=lfs diff=lfs merge=lfs -text
|
|
@@ -33,3 +37,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Force LF line endings for shell/Python scripts
|
| 2 |
+
*.sh text eol=lf
|
| 3 |
+
*.py text eol=lf
|
| 4 |
+
|
| 5 |
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 6 |
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 7 |
*.bin filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 37 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 38 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 39 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
*.jpg filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
*.webp filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Environment and secrets
|
| 2 |
+
.env
|
| 3 |
+
.env.local
|
| 4 |
+
*.pem
|
| 5 |
+
|
| 6 |
+
# Dependencies and build
|
| 7 |
+
node_modules/
|
| 8 |
+
__pycache__/
|
| 9 |
+
*.pyc
|
| 10 |
+
|
| 11 |
+
# Logs and temp
|
| 12 |
+
*.log
|
| 13 |
+
.DS_Store
|
| 14 |
+
|
| 15 |
+
graphify-out/
|
Dockerfile
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ββ Stage 1: Build Hermes Agent from source ββββββββββββββββββββββββββββββ
|
| 2 |
+
FROM ghcr.io/astral-sh/uv:0.11.6-python3.13-trixie AS uv_source
|
| 3 |
+
FROM tianon/gosu:1.19-trixie AS gosu_source
|
| 4 |
+
|
| 5 |
+
FROM debian:13.4
|
| 6 |
+
SHELL ["/bin/bash", "-c"]
|
| 7 |
+
|
| 8 |
+
ENV PYTHONUNBUFFERED=1
|
| 9 |
+
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright
|
| 10 |
+
|
| 11 |
+
# ββ System dependencies ββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 12 |
+
RUN echo "[build] Installing system deps..." && START=$(date +%s) \
|
| 13 |
+
&& apt-get update && apt-get install -y --no-install-recommends \
|
| 14 |
+
build-essential python3 python3-pip python3-venv \
|
| 15 |
+
git curl wget ca-certificates \
|
| 16 |
+
ripgrep ffmpeg gcc python3-dev libffi-dev procps gnupg \
|
| 17 |
+
git git-lfs \
|
| 18 |
+
vim nano htop bash \
|
| 19 |
+
tar gzip xz-utils bzip2 rsync \
|
| 20 |
+
gettext-base openssh-client nginx \
|
| 21 |
+
&& rm -rf /var/lib/apt/lists/* \
|
| 22 |
+
&& pip3 install --no-cache-dir --break-system-packages huggingface_hub requests pyyaml \
|
| 23 |
+
&& echo "[build] System deps: $(($(date +%s) - START))s"
|
| 24 |
+
|
| 25 |
+
# Install Node.js 22 LTS (official binary). Debian 13's apt `nodejs` is v20.x with
|
| 26 |
+
# npm 9.2.0, which fails Hermes' `npm install` (EBADENGINE): @electron/rebuild@4.2.0
|
| 27 |
+
# requires node >=22.12.0 and react-router@8.3.0 requires node >=22.22.0 β the repo
|
| 28 |
+
# sets engine-strict=true, so we pin the latest 22.x (22.23.2) that satisfies both.
|
| 29 |
+
# The binary tarball is distro-agnostic and ships its own npm, avoiding apt lag.
|
| 30 |
+
RUN echo "[build] Installing Node.js 22 LTS..." && START=$(date +%s) \
|
| 31 |
+
&& curl -fsSL https://nodejs.org/dist/v22.23.2/node-v22.23.2-linux-x64.tar.xz -o /tmp/node.tar.xz \
|
| 32 |
+
&& tar -xJf /tmp/node.tar.xz -C /usr/local --strip-components=1 \
|
| 33 |
+
&& rm -f /tmp/node.tar.xz \
|
| 34 |
+
&& node --version && npm --version \
|
| 35 |
+
&& echo "[build] Node.js install: $(($(date +%s) - START))s"
|
| 36 |
+
|
| 37 |
+
# ββ Install buzz CLI (Rust, build only the buzz-cli crate) ββββββββββββββββ
|
| 38 |
+
# Provides the `buzz` binary needed by BUZZ_PRIVATE_KEY agent config.
|
| 39 |
+
RUN echo "[build] Installing Rust + building buzz-cli..." && START=$(date +%s) \
|
| 40 |
+
&& curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal \
|
| 41 |
+
&& . "$HOME/.cargo/env" \
|
| 42 |
+
&& git clone --depth 1 https://github.com/block/buzz.git /tmp/buzz-src \
|
| 43 |
+
&& cd /tmp/buzz-src \
|
| 44 |
+
&& cargo build --release -p buzz-cli \
|
| 45 |
+
&& cp target/release/buzz /usr/local/bin/buzz \
|
| 46 |
+
&& chmod +x /usr/local/bin/buzz \
|
| 47 |
+
&& cd / && rm -rf /tmp/buzz-src \
|
| 48 |
+
&& echo "[build] buzz-cli: $(buzz --version 2>/dev/null || echo installed)" \
|
| 49 |
+
&& echo "[build] Rust+buzz build: $(($(date +%s) - START))s"
|
| 50 |
+
|
| 51 |
+
# ββ Non-root user (uid 1000 β required for HF Spaces Dev Mode) βββββββββββ
|
| 52 |
+
RUN useradd -u 1000 -m -d /opt/data hermes
|
| 53 |
+
|
| 54 |
+
COPY --chmod=0755 --from=gosu_source /gosu /usr/local/bin/
|
| 55 |
+
COPY --chmod=0755 --from=uv_source /usr/local/bin/uv /usr/local/bin/uvx /usr/local/bin/
|
| 56 |
+
|
| 57 |
+
# ββ Clone and build Hermes Agent βββββββββββββββββββββββββββββββββββββββββ
|
| 58 |
+
RUN echo "[build] Cloning Hermes Agent..." && START=$(date +%s) \
|
| 59 |
+
&& git clone --depth 1 https://github.com/NousResearch/hermes-agent.git /opt/hermes \
|
| 60 |
+
&& echo "[build] Clone: $(($(date +%s) - START))s"
|
| 61 |
+
|
| 62 |
+
WORKDIR /opt/hermes
|
| 63 |
+
|
| 64 |
+
# ββ Node dependencies + Playwright + Web Dashboard build βββββββββββββββββ
|
| 65 |
+
RUN echo "[build] Installing Node deps + Playwright..." && START=$(date +%s) \
|
| 66 |
+
&& npm install --prefer-offline --no-audit \
|
| 67 |
+
&& npx playwright install --with-deps chromium --only-shell \
|
| 68 |
+
&& if [ -d /opt/hermes/scripts/whatsapp-bridge ]; then \
|
| 69 |
+
cd /opt/hermes/scripts/whatsapp-bridge && npm install --prefer-offline --no-audit && npm install https-proxy-agent; \
|
| 70 |
+
fi \
|
| 71 |
+
&& echo "[build] Building web dashboard..." \
|
| 72 |
+
&& cd /opt/hermes/web && npm install --prefer-offline --no-audit && npm run build \
|
| 73 |
+
&& cd /opt/hermes && npm cache clean --force \
|
| 74 |
+
&& echo "[build] Node deps + web dashboard: $(($(date +%s) - START))s"
|
| 75 |
+
|
| 76 |
+
# ββ Python dependencies ββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 77 |
+
RUN chown -R hermes:hermes /opt/hermes
|
| 78 |
+
USER hermes
|
| 79 |
+
|
| 80 |
+
RUN echo "[build] Installing Python deps..." && START=$(date +%s) \
|
| 81 |
+
&& cd /opt/hermes \
|
| 82 |
+
&& UV_CACHE_DIR=/tmp/uv-cache uv venv \
|
| 83 |
+
&& UV_CACHE_DIR=/tmp/uv-cache uv pip install --no-cache-dir -e ".[all]" \
|
| 84 |
+
&& UV_CACHE_DIR=/tmp/uv-cache uv pip install --no-cache-dir huggingface_hub requests pyyaml \
|
| 85 |
+
&& rm -rf /tmp/uv-cache \
|
| 86 |
+
&& echo "[build] Python deps: $(($(date +%s) - START))s"
|
| 87 |
+
|
| 88 |
+
USER root
|
| 89 |
+
RUN chmod +x /opt/hermes/docker/entrypoint.sh
|
| 90 |
+
|
| 91 |
+
# ββ Prepare runtime dirs βββββββββββββββββββββββββββββββββοΏ½οΏ½οΏ½ββββββββββββββ
|
| 92 |
+
RUN mkdir -p /opt/data/cron /opt/data/sessions /opt/data/logs /opt/data/hooks \
|
| 93 |
+
/opt/data/memories /opt/data/skills /opt/data/skins /opt/data/plans \
|
| 94 |
+
/opt/data/workspace /opt/data/home \
|
| 95 |
+
&& chown -R 1000:1000 /opt/data
|
| 96 |
+
|
| 97 |
+
USER root
|
| 98 |
+
|
| 99 |
+
ARG CACHE_BUST=2026-07-18-wa-proxy-patch-v3
|
| 100 |
+
RUN echo "Build: ${CACHE_BUST}"
|
| 101 |
+
COPY --chown=root:root entrypoint.sh /opt/scripts/entrypoint.sh
|
| 102 |
+
RUN chmod +x /opt/scripts/entrypoint.sh
|
| 103 |
+
|
| 104 |
+
ENV HERMES_HOME=/opt/data
|
| 105 |
+
ENV PATH="/opt/hermes/.venv/bin:$PATH"
|
| 106 |
+
|
| 107 |
+
WORKDIR /app
|
| 108 |
+
COPY --link ./ /app
|
| 109 |
+
|
| 110 |
+
COPY nginx-proxy.conf /etc/nginx/proxy.conf
|
| 111 |
+
COPY nginx.conf /etc/nginx/nginx.conf
|
| 112 |
+
|
| 113 |
+
CMD ["/opt/scripts/entrypoint.sh"]
|
docker-compose.yml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
services:
|
| 2 |
+
hermes:
|
| 3 |
+
build: .
|
| 4 |
+
container_name: hermes
|
| 5 |
+
ports:
|
| 6 |
+
- "7860:7860"
|
| 7 |
+
env_file:
|
| 8 |
+
- .env
|
| 9 |
+
environment:
|
| 10 |
+
- HERMES_DASHBOARD_PASSWORD=${HERMES_DASHBOARD_PASSWORD:-}
|
| 11 |
+
- HERMES_DASHBOARD_USERNAME=${HERMES_DASHBOARD_USERNAME:-admin}
|
| 12 |
+
volumes:
|
| 13 |
+
- hermes_data:/data/hermes
|
| 14 |
+
|
| 15 |
+
volumes:
|
| 16 |
+
hermes_data:
|
entrypoint.sh
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
set -e
|
| 3 |
+
|
| 4 |
+
BOOT_START=$(date +%s)
|
| 5 |
+
|
| 6 |
+
echo "[entrypoint] Hermes Agent on HuggingFace Spaces (Safe Sync Mode)"
|
| 7 |
+
echo "==============================================================================="
|
| 8 |
+
|
| 9 |
+
# ββ Configuration βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 10 |
+
HERMES_WORK="/opt/data" # Fast local filesystem (working dir)
|
| 11 |
+
HERMES_BACKUP="/data/hermes" # HF mounted bucket (backup only)
|
| 12 |
+
INSTALL_DIR="/opt/hermes"
|
| 13 |
+
HERMES_UID=1000
|
| 14 |
+
HERMES_GID=1000
|
| 15 |
+
SYNC_INTERVAL="${SYNC_INTERVAL:-60}" # Default: sync every 1 minute
|
| 16 |
+
|
| 17 |
+
# ββ Singleton guard ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 18 |
+
# Hanya satu instance yang menjalankan steps. Instance duplikat (dari HF rapid-
|
| 19 |
+
# restart) cukup sleep infinity β tidak exit (exit = HF restart lagi).
|
| 20 |
+
# HERMES_ENTRYPOINT_OWNER di-export sebelum exec gosu sehingga instance setelah
|
| 21 |
+
# privilege-drop tahu dia adalah kelanjutan sah, bukan duplikat.
|
| 22 |
+
ENTRYPOINT_PID_FILE="/tmp/hermes-entrypoint.pid"
|
| 23 |
+
ENTRYPOINT_LOG="/tmp/hermes-entrypoint.log"
|
| 24 |
+
|
| 25 |
+
if [ -z "${HERMES_ENTRYPOINT_OWNER:-}" ]; then
|
| 26 |
+
# Instance baru tanpa token β cek apakah ada instance lain yang sudah running
|
| 27 |
+
if [ -f "$ENTRYPOINT_PID_FILE" ]; then
|
| 28 |
+
OLD_PID=$(cat "$ENTRYPOINT_PID_FILE" 2>/dev/null || echo "")
|
| 29 |
+
if [ -n "$OLD_PID" ] && kill -0 "$OLD_PID" 2>/dev/null; then
|
| 30 |
+
echo "[entrypoint] Instance $OLD_PID already running β standing by silently."
|
| 31 |
+
sleep infinity
|
| 32 |
+
exit 0
|
| 33 |
+
else
|
| 34 |
+
echo "[entrypoint] Stale PID $OLD_PID (dead) β taking over..."
|
| 35 |
+
rm -f "$ENTRYPOINT_PID_FILE" "$ENTRYPOINT_LOG" 2>/dev/null || true
|
| 36 |
+
fi
|
| 37 |
+
fi
|
| 38 |
+
echo $$ > "$ENTRYPOINT_PID_FILE"
|
| 39 |
+
export HERMES_ENTRYPOINT_OWNER=$$
|
| 40 |
+
# Buat log world-writable sekarang (sebagai root) agar hermes bisa write setelah gosu
|
| 41 |
+
touch "$ENTRYPOINT_LOG" && chmod 666 "$ENTRYPOINT_LOG"
|
| 42 |
+
fi
|
| 43 |
+
|
| 44 |
+
exec > >(tee -a "$ENTRYPOINT_LOG") 2>&1
|
| 45 |
+
|
| 46 |
+
# ββ Helper: Safe Sync (EXCLUDE large/temp files) βββββββββββββββββββββββββββ
|
| 47 |
+
safe_sync() {
|
| 48 |
+
local SRC="$1"
|
| 49 |
+
local DST="$2"
|
| 50 |
+
|
| 51 |
+
if [ ! -d "$SRC" ]; then
|
| 52 |
+
echo " Source $SRC does not exist, skipping sync"
|
| 53 |
+
return 0
|
| 54 |
+
fi
|
| 55 |
+
|
| 56 |
+
mkdir -p "$DST"
|
| 57 |
+
|
| 58 |
+
echo " Syncing: $SRC -> $DST"
|
| 59 |
+
echo " Excluding: node_modules, .cache, logs, *.tmp, *.lock, *.pid, .local/share/uv"
|
| 60 |
+
|
| 61 |
+
rsync -a --checksum --delete \
|
| 62 |
+
--exclude='.local/share/uv' \
|
| 63 |
+
--exclude='node_modules' \
|
| 64 |
+
--exclude='.cache' \
|
| 65 |
+
--exclude='.playwright' \
|
| 66 |
+
--exclude='logs' \
|
| 67 |
+
--exclude='*.log' \
|
| 68 |
+
--exclude='*.log.*' \
|
| 69 |
+
--exclude='*.tmp' \
|
| 70 |
+
--exclude='*.lock' \
|
| 71 |
+
--exclude='*.pid' \
|
| 72 |
+
--exclude='__pycache__' \
|
| 73 |
+
--exclude='state.db-wal' \
|
| 74 |
+
--exclude='state.db-shm' \
|
| 75 |
+
"$SRC/" "$DST/"
|
| 76 |
+
|
| 77 |
+
echo " β Sync completed"
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
# ββ Stage 1: Restore from backup (as root, before privilege drop) ββββββββββ
|
| 81 |
+
if [ "$(id -u)" = "0" ]; then
|
| 82 |
+
echo ""
|
| 83 |
+
echo "=== 1. Restoring Hermes data from persistent storage ==="
|
| 84 |
+
|
| 85 |
+
# Create working directory on fast local filesystem
|
| 86 |
+
mkdir -p "$HERMES_WORK"/{cron,sessions,logs,hooks,memories,skills,skins,plans,workspace,home}
|
| 87 |
+
mkdir -p "$HERMES_BACKUP"/{cron,sessions,logs,hooks,memories,skills,skins,plans,workspace,home}
|
| 88 |
+
|
| 89 |
+
# Restore from backup if exists
|
| 90 |
+
if [ -d "$HERMES_BACKUP" ] && [ "$(ls -A $HERMES_BACKUP 2>/dev/null | wc -l)" -gt 0 ]; then
|
| 91 |
+
echo "Found existing data in $HERMES_BACKUP"
|
| 92 |
+
RESTORE_START=$(date +%s)
|
| 93 |
+
safe_sync "$HERMES_BACKUP" "$HERMES_WORK"
|
| 94 |
+
RESTORE_END=$(date +%s)
|
| 95 |
+
echo "[TIMER] Restore from backup: $((RESTORE_END - RESTORE_START))s"
|
| 96 |
+
echo "Restore completed (sessions, memories, workspace restored)"
|
| 97 |
+
else
|
| 98 |
+
echo "No existing backup found, starting fresh"
|
| 99 |
+
fi
|
| 100 |
+
|
| 101 |
+
# Fix ownership on working directory (fast - only local files)
|
| 102 |
+
chown -R ${HERMES_UID}:${HERMES_GID} "$HERMES_WORK"
|
| 103 |
+
echo "β Working directory ready: $HERMES_WORK (owned by hermes)"
|
| 104 |
+
|
| 105 |
+
# Ensure /tmp is world-writable
|
| 106 |
+
chmod 1777 /tmp 2>/dev/null || true
|
| 107 |
+
|
| 108 |
+
echo ""
|
| 109 |
+
echo "=== 2. Starting Scheduled Backup (Every ${SYNC_INTERVAL}s) ==="
|
| 110 |
+
# Background backup loop β uses safe_sync which already excludes .local/share/uv
|
| 111 |
+
(while true; do
|
| 112 |
+
sleep ${SYNC_INTERVAL}
|
| 113 |
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backing up $HERMES_WORK -> $HERMES_BACKUP"
|
| 114 |
+
safe_sync "$HERMES_WORK" "$HERMES_BACKUP"
|
| 115 |
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backup completed"
|
| 116 |
+
done) &
|
| 117 |
+
BACKUP_PID=$!
|
| 118 |
+
echo "Backup loop started with PID $BACKUP_PID"
|
| 119 |
+
|
| 120 |
+
echo ""
|
| 121 |
+
echo "=== 3. Starting nginx reverse proxy (port 7860 β 7861) ==="
|
| 122 |
+
nginx
|
| 123 |
+
echo " β nginx started (PID $(cat /run/nginx.pid 2>/dev/null || echo unknown))"
|
| 124 |
+
|
| 125 |
+
echo ""
|
| 126 |
+
echo "=== 4. Dropping privileges to hermes (uid ${HERMES_UID}) ==="
|
| 127 |
+
exec gosu ${HERMES_UID} "$0" "$@"
|
| 128 |
+
fi
|
| 129 |
+
|
| 130 |
+
# ββ Stage 2: Run as hermes user βββββββββββββββββββββββββββββββββββββββββββββ
|
| 131 |
+
echo ""
|
| 132 |
+
echo "=== 4. Activating Python virtual environment ==="
|
| 133 |
+
if [ -f "${INSTALL_DIR}/.venv/bin/activate" ]; then
|
| 134 |
+
source "${INSTALL_DIR}/.venv/bin/activate"
|
| 135 |
+
echo "β Activated venv: $(which python3)"
|
| 136 |
+
fi
|
| 137 |
+
|
| 138 |
+
# ββ Bootstrap config files (in working dir, not mounted bucket) βββββββββββββ
|
| 139 |
+
echo ""
|
| 140 |
+
echo "=== 5. Bootstrapping configuration files ==="
|
| 141 |
+
|
| 142 |
+
if [ ! -f "$HERMES_WORK/.env" ] && [ -f "$INSTALL_DIR/.env.example" ]; then
|
| 143 |
+
cp "$INSTALL_DIR/.env.example" "$HERMES_WORK/.env"
|
| 144 |
+
echo "β Created .env from example"
|
| 145 |
+
fi
|
| 146 |
+
|
| 147 |
+
if [ ! -f "$HERMES_WORK/config.yaml" ] && [ -f "$INSTALL_DIR/cli-config.yaml.example" ]; then
|
| 148 |
+
cp "$INSTALL_DIR/cli-config.yaml.example" "$HERMES_WORK/config.yaml"
|
| 149 |
+
echo "β Created config.yaml from example"
|
| 150 |
+
fi
|
| 151 |
+
|
| 152 |
+
if [ ! -f "$HERMES_WORK/SOUL.md" ] && [ -f "$INSTALL_DIR/docker/SOUL.md" ]; then
|
| 153 |
+
cp "$INSTALL_DIR/docker/SOUL.md" "$HERMES_WORK/SOUL.md"
|
| 154 |
+
echo "β Created SOUL.md from template"
|
| 155 |
+
fi
|
| 156 |
+
|
| 157 |
+
# ββ Dashboard basic auth ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 158 |
+
DASH_USER="${HERMES_DASHBOARD_USERNAME:-admin}"
|
| 159 |
+
DASH_PASS="${HERMES_DASHBOARD_PASSWORD:-}"
|
| 160 |
+
if [ -n "$DASH_PASS" ] && [ -f "$HERMES_WORK/config.yaml" ]; then
|
| 161 |
+
if ! grep -q 'basic_auth' "$HERMES_WORK/config.yaml"; then
|
| 162 |
+
cat >> "$HERMES_WORK/config.yaml" <<EOF
|
| 163 |
+
|
| 164 |
+
dashboard:
|
| 165 |
+
basic_auth:
|
| 166 |
+
username: ${DASH_USER}
|
| 167 |
+
password: ${DASH_PASS}
|
| 168 |
+
EOF
|
| 169 |
+
echo "β Injected dashboard.basic_auth into config.yaml"
|
| 170 |
+
fi
|
| 171 |
+
fi
|
| 172 |
+
|
| 173 |
+
if [ -n "$DASH_PASS" ]; then
|
| 174 |
+
export HERMES_DASHBOARD_BASIC_AUTH_USERNAME="$DASH_USER"
|
| 175 |
+
export HERMES_DASHBOARD_BASIC_AUTH_PASSWORD="$DASH_PASS"
|
| 176 |
+
fi
|
| 177 |
+
|
| 178 |
+
# ββ Sync bundled skills βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 179 |
+
if [ -d "$INSTALL_DIR/skills" ] && [ -f "$INSTALL_DIR/tools/skills_sync.py" ]; then
|
| 180 |
+
python3 "$INSTALL_DIR/tools/skills_sync.py" 2>&1 || echo "[entrypoint] Skills sync skipped"
|
| 181 |
+
fi
|
| 182 |
+
|
| 183 |
+
# ββ Build artifacts check βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 184 |
+
echo ""
|
| 185 |
+
echo "=== 6. Build artifacts check ==="
|
| 186 |
+
test -f "$INSTALL_DIR/run_agent.py" && echo " β run_agent.py" || echo " β run_agent.py not found"
|
| 187 |
+
test -f "$INSTALL_DIR/gateway/run.py" && echo " β gateway/run.py" || echo " β gateway/run.py not found"
|
| 188 |
+
test -d "$INSTALL_DIR/web" && echo " β web/ dashboard" || echo " β web/ not found"
|
| 189 |
+
command -v hermes >/dev/null 2>&1 && echo " β hermes CLI: $(which hermes)" || echo " β hermes CLI not in PATH"
|
| 190 |
+
|
| 191 |
+
# ββ Register Buzz profile βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 192 |
+
if [ -n "${BUZZ_RELAY_URL:-}" ] && [ -n "${BUZZ_PRIVATE_KEY:-}" ]; then
|
| 193 |
+
_BUZZ_NAME="${BUZZ_DISPLAY_NAME:-${AGENT_NAME:-$(hostname)}}"
|
| 194 |
+
echo ""
|
| 195 |
+
echo "=== 7. Setting Buzz profile name: ${_BUZZ_NAME} ==="
|
| 196 |
+
buzz users set-profile --name "${_BUZZ_NAME}" 2>&1 || \
|
| 197 |
+
echo "β buzz users set-profile failed (non-fatal)"
|
| 198 |
+
fi
|
| 199 |
+
|
| 200 |
+
# ββ Export working directory as HERMES_HOME βββββββββββββββββββββββββββββββββ
|
| 201 |
+
export HERMES_HOME="$HERMES_WORK"
|
| 202 |
+
echo ""
|
| 203 |
+
echo "=== 8. Environment Setup ==="
|
| 204 |
+
echo " Working directory (FAST): $HERMES_WORK"
|
| 205 |
+
echo " Backup directory (HF Bucket): $HERMES_BACKUP"
|
| 206 |
+
echo " Sync interval: ${SYNC_INTERVAL}s"
|
| 207 |
+
echo " HERMES_HOME: $HERMES_HOME"
|
| 208 |
+
|
| 209 |
+
ENTRYPOINT_END=$(date +%s)
|
| 210 |
+
echo ""
|
| 211 |
+
echo "[TIMER] Entrypoint setup: $((ENTRYPOINT_END - BOOT_START))s"
|
| 212 |
+
|
| 213 |
+
# ββ Start Hermes Gateway ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 214 |
+
echo ""
|
| 215 |
+
echo "=== 9. Starting Hermes Agent ==="
|
| 216 |
+
echo " Hermes running on local filesystem: $HERMES_WORK (FAST)"
|
| 217 |
+
echo " Persistent storage: $HERMES_BACKUP (synced every ${SYNC_INTERVAL}s)"
|
| 218 |
+
echo ""
|
| 219 |
+
|
| 220 |
+
cd "$HERMES_WORK"
|
| 221 |
+
|
| 222 |
+
# Trap SIGTERM untuk final backup sebelum shutdown (dipasang di Stage 2 karena exec gosu menggantikan Stage 1)
|
| 223 |
+
trap "echo ''; echo 'Shutting down... performing final backup'; safe_sync '$HERMES_WORK' '$HERMES_BACKUP'; echo 'Final backup completed'; exit 0" SIGTERM SIGINT
|
| 224 |
+
|
| 225 |
+
# # Initial backup segera setelah start (jangan tunggu interval pertama)
|
| 226 |
+
# (sleep 30 && echo "[$(date '+%Y-%m-%d %H:%M:%S')] Initial post-start backup" && safe_sync "$HERMES_WORK" "$HERMES_BACKUP" && echo "[$(date '+%Y-%m-%d %H:%M:%S')] Initial backup done") &
|
| 227 |
+
|
| 228 |
+
# Start dashboard di background (port 7861 untuk HF Spaces)
|
| 229 |
+
"${INSTALL_DIR}/.venv/bin/hermes" dashboard --host 0.0.0.0 --port 7861 --no-open --insecure &
|
| 230 |
+
DASHBOARD_PID=$!
|
| 231 |
+
echo " Dashboard started (PID $DASHBOARD_PID) on :7860"
|
| 232 |
+
sleep 2
|
| 233 |
+
|
| 234 |
+
exec "${INSTALL_DIR}/.venv/bin/python3" -u "${INSTALL_DIR}/gateway/run.py"
|
nginx-proxy.conf
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
proxy_set_header Host $host;
|
| 2 |
+
proxy_set_header X-Real-IP $remote_addr;
|
| 3 |
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
| 4 |
+
proxy_set_header X-Forwarded-Port $server_port;
|
| 5 |
+
proxy_set_header X-Forwarded-Host $host/a2a;
|
| 6 |
+
proxy_set_header Upgrade $http_upgrade;
|
| 7 |
+
proxy_set_header Connection "upgrade";
|
| 8 |
+
proxy_redirect off;
|
| 9 |
+
proxy_read_timeout 300s;
|
| 10 |
+
proxy_connect_timeout 10s;
|
nginx.conf
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
worker_processes auto;
|
| 2 |
+
events { worker_connections 1024; }
|
| 3 |
+
|
| 4 |
+
http {
|
| 5 |
+
include /etc/nginx/mime.types;
|
| 6 |
+
default_type application/octet-stream;
|
| 7 |
+
sendfile on;
|
| 8 |
+
|
| 9 |
+
server {
|
| 10 |
+
listen 7860;
|
| 11 |
+
server_name _;
|
| 12 |
+
|
| 13 |
+
location / {
|
| 14 |
+
proxy_pass http://127.0.0.1:7861;
|
| 15 |
+
include /etc/nginx/proxy.conf;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
location /8642/ {
|
| 19 |
+
proxy_pass http://127.0.0.1:8642/;
|
| 20 |
+
include /etc/nginx/proxy.conf;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
location /.well-known/agent-card.json {
|
| 24 |
+
proxy_pass http://127.0.0.1:9900/.well-known/agent-card.json;
|
| 25 |
+
include /etc/nginx/proxy.conf;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
location /a2a/ {
|
| 29 |
+
proxy_pass http://127.0.0.1:9900/;
|
| 30 |
+
include /etc/nginx/proxy.conf;
|
| 31 |
+
}
|
| 32 |
+
}
|
| 33 |
+
}
|
requirements.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
huggingface_hub>=0.24.5
|