Spaces:
Running
Running
| set -eo pipefail | |
| export HOME="${HOME:-/root}" | |
| DATA="/data" | |
| # --- 0. Persistent data dir: ~/.hermes -> /data --------------------------------- | |
| # Hermes (baked into the image) defaults to $HOME/.hermes. We swap that for the | |
| # mounted /data bucket so config, skills, memory, sessions and cron survive | |
| # restarts. /root is ephemeral, so the symlink itself is recreated every boot. | |
| mkdir -p "${DATA}" | |
| rm -rf "${HOME}/.hermes" | |
| ln -sfn "${DATA}" "${HOME}/.hermes" | |
| # Heal mount permissions/ownership (bucket mounts can reset them between boots). | |
| if [ ! -w "${DATA}" ] && [ "$(id -u)" = "0" ]; then | |
| echo "[boot] repairing ownership on ${DATA}..." >&2 | |
| chown root:root "${DATA}" 2>/dev/null || true | |
| chmod u+rwx "${DATA}" 2>/dev/null || true | |
| fi | |
| # --- 1. Seed the volume from the image on first boot --------------------------------------- | |
| if [ ! -f "${DATA}/config.yaml" ]; then | |
| echo "[boot] first boot — seeding Hermes data (node, uv, bundled skills) into ${DATA}..." >&2 | |
| if [ -d /hermes-seed ]; then | |
| cp -a /hermes-seed/. "${DATA}/" 2>/dev/null || true | |
| fi | |
| fi | |
| # Storage Buckets are S3-like and may drop the executable bit on remount. Re-assert +x | |
| # on every boot for the binaries Hermes relies on (node, uv, skills scripts) so the | |
| # volume stays self-healing across restarts. | |
| if [ -d "${DATA}/node/bin" ]; then | |
| chmod +x "${DATA}/node/bin/"* 2>/dev/null || true | |
| fi | |
| find "${DATA}" -maxdepth 3 -type f \( -name "uv" -o -name "node" -o -name "npm" -o -name "npx" \) -exec chmod +x {} + 2>/dev/null || true | |
| # node/npm (managed by the installer under ~/.hermes/node) + uv on PATH. | |
| export PATH="${DATA}/node/bin:${HOME}/.local/bin:${PATH}" | |
| # Make sure `hermes` is reachable (it's baked at /usr/local/bin, fallback to venv). | |
| if ! command -v hermes >/dev/null 2>&1; then | |
| ln -sfn /usr/local/lib/hermes-agent/venv/bin/hermes /usr/local/bin/hermes 2>/dev/null || true | |
| fi | |
| # --- 2. Persist secrets (Space env secrets) into ~/.hermes/.env every boot ---- | |
| # (config.yaml lives in the .env directory too, so this file IS the volume) | |
| env_file="${DATA}/.env" | |
| touch "${env_file}" | |
| set_key() { | |
| local key="$1" value="${2:-}" | |
| if [ -n "${value}" ]; then | |
| if grep -qE "^${key}=" "${env_file}" 2>/dev/null; then | |
| sed -i "/^${key}=/d" "${env_file}" | |
| fi | |
| printf '%s=%s\n' "${key}" "${value}" >> "${env_file}" | |
| fi | |
| } | |
| set_key OPENROUTER_API_KEY "${OPENROUTER_API_KEY:-}" | |
| set_key HERMES_OPENROUTER_CACHE 1 | |
| # Dashboard basic auth (a non-loopback bind requires an auth provider). | |
| set_key HERMES_DASHBOARD_BASIC_AUTH_USERNAME "${HERMES_DASHBOARD_BASIC_AUTH_USERNAME:-admin}" | |
| set_key HERMES_DASHBOARD_BASIC_AUTH_PASSWORD "${HERMES_DASHBOARD_BASIC_AUTH_PASSWORD:-}" | |
| set_key HERMES_DASHBOARD_BASIC_AUTH_SECRET "${HERMES_DASHBOARD_BASIC_AUTH_SECRET:-}" | |
| # OpenAI-compatible API server (loopback only). | |
| set_key API_SERVER_ENABLED "${API_SERVER_ENABLED:-true}" | |
| set_key API_SERVER_PORT "${API_SERVER_PORT:-8642}" | |
| set_key API_SERVER_KEY "${API_SERVER_KEY:-}" | |
| set_key API_SERVER_HOST "127.0.0.1" | |
| # --- 3. Default config.yaml (OpenRouter + delegation/spawn), only if missing ---- | |
| if [ ! -f "${DATA}/config.yaml" ]; then | |
| cat > "${DATA}/config.yaml" <<'YAML' | |
| # Hermes Agent — persistent setup via OpenRouter with subagent spawning. | |
| model: | |
| provider: openrouter | |
| default: "~anthropic/claude-sonnet-latest" | |
| # Spawn subagents through OpenRouter (parallel + nested orchestration). | |
| delegation: | |
| provider: openrouter | |
| max_concurrent_children: 3 | |
| max_spawn_depth: 2 | |
| orchestrator_enabled: true | |
| max_iterations: 50 | |
| terminal: | |
| backend: local | |
| openrouter: | |
| response_cache: true | |
| YAML | |
| fi | |
| # --- 4. Start the Hermes web dashboard on the Hugging Face PORT ---------------- | |
| PORT="${PORT:-7860}" | |
| echo "[boot] Hermes dashboard → http://0.0.0.0:${PORT}/" >&2 | |
| # Use prebuilt web UI if present (baked at Docker build); otherwise build at runtime. | |
| DASH_FLAGS="--host 0.0.0.0 --port ${PORT}" | |
| if [ -d /usr/local/lib/hermes-agent/hermes_cli/web_dist ]; then | |
| DASH_FLAGS="${DASH_FLAGS} --skip-build" | |
| fi | |
| exec hermes dashboard ${DASH_FLAGS} |