hermes-persistent / entrypoint.sh
zk-Armor's picture
Upload folder using huggingface_hub
6fd41e0 verified
Raw
History Blame Contribute Delete
4.12 kB
#!/usr/bin/env bash
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}