Shadow HERMES_HOME pattern: .env stays local, bucket symlinks in
Browse files- bootstrap.sh +79 -29
bootstrap.sh
CHANGED
|
@@ -4,22 +4,30 @@
|
|
| 4 |
# ============================================================================
|
| 5 |
# Creates your own HF bucket from the merve/hermes-agent template, mounts it,
|
| 6 |
# installs Hermes, sets up Telegram, and launches the agent.
|
| 7 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
#
|
| 9 |
# bash <(curl -fsSL https://huggingface.co/merve/hermes-agent-bootstrap/resolve/main/bootstrap.sh)
|
| 10 |
#
|
| 11 |
-
#
|
| 12 |
-
# <YOUR_HF_USERNAME>/hermes-agent β your personal bucket (created if missing)
|
| 13 |
-
# ~/hermes-bucket β local mount of your bucket
|
| 14 |
-
# ~/.hermes-secrets.env β your local secrets (mode 600, NEVER uploaded)
|
| 15 |
-
# ~/.hermes/ β Hermes code + venv
|
| 16 |
-
# ~/.local/bin/{hf,hermes,hf-mount} β installed CLIs
|
| 17 |
# ============================================================================
|
| 18 |
set -euo pipefail
|
| 19 |
|
| 20 |
TEMPLATE="merve/hermes-agent"
|
| 21 |
MOUNT="$HOME/hermes-bucket"
|
| 22 |
-
|
|
|
|
|
|
|
| 23 |
|
| 24 |
G='\033[0;32m'; Y='\033[0;33m'; C='\033[0;36m'; R='\033[0;31m'; N='\033[0m'
|
| 25 |
say() { printf "${C}β${N} %s\n" "$*"; }
|
|
@@ -107,7 +115,7 @@ else
|
|
| 107 |
fi
|
| 108 |
|
| 109 |
# ----------------------------------------------------------------------------
|
| 110 |
-
# 6. Hermes Agent
|
| 111 |
# ----------------------------------------------------------------------------
|
| 112 |
if ! command -v hermes >/dev/null 2>&1; then
|
| 113 |
say "Installing Hermes Agent (this takes a minute)..."
|
|
@@ -117,7 +125,38 @@ fi
|
|
| 117 |
ok "Hermes: $(command -v hermes)"
|
| 118 |
|
| 119 |
# ----------------------------------------------------------------------------
|
| 120 |
-
# 7.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
# ----------------------------------------------------------------------------
|
| 122 |
printf "\n${C}Telegram setup${N}\n"
|
| 123 |
|
|
@@ -162,11 +201,11 @@ except Exception:
|
|
| 162 |
fi
|
| 163 |
|
| 164 |
# ----------------------------------------------------------------------------
|
| 165 |
-
#
|
| 166 |
# ----------------------------------------------------------------------------
|
| 167 |
umask 077
|
| 168 |
cat > "$SECRETS" <<EOF
|
| 169 |
-
# Hermes secrets β
|
| 170 |
HF_TOKEN=$HF_TOKEN_VAL
|
| 171 |
TELEGRAM_BOT_TOKEN=$TG_TOKEN
|
| 172 |
TELEGRAM_ALLOWED_USERS=$USER_ID
|
|
@@ -176,7 +215,7 @@ chmod 600 "$SECRETS"
|
|
| 176 |
ok "Secrets saved to $SECRETS (mode 600)"
|
| 177 |
|
| 178 |
# ----------------------------------------------------------------------------
|
| 179 |
-
#
|
| 180 |
# ----------------------------------------------------------------------------
|
| 181 |
SHELL_NAME="${SHELL##*/}"
|
| 182 |
case "$SHELL_NAME" in
|
|
@@ -185,29 +224,41 @@ case "$SHELL_NAME" in
|
|
| 185 |
*) SHELL_RC="$HOME/.profile" ;;
|
| 186 |
esac
|
| 187 |
|
| 188 |
-
|
| 189 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
|
| 191 |
-
# hermes-bucket aliases (added by bootstrap.sh)
|
| 192 |
-
alias hermes-here='HERMES_HOME=\$HOME/
|
| 193 |
-
alias hermes-gateway='HERMES_HOME=\$HOME/
|
| 194 |
EOF
|
| 195 |
-
|
| 196 |
-
else
|
| 197 |
-
ok "Shell aliases already present in $SHELL_RC"
|
| 198 |
-
fi
|
| 199 |
|
| 200 |
# ----------------------------------------------------------------------------
|
| 201 |
-
#
|
| 202 |
# ----------------------------------------------------------------------------
|
| 203 |
cat <<EOF
|
| 204 |
|
| 205 |
${G}β Ready.${N}
|
| 206 |
|
| 207 |
-
Your bucket:
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
Model:
|
| 211 |
|
| 212 |
${C}hermes-here${N} β chat in your terminal
|
| 213 |
${C}hermes-gateway${N} β start Telegram bot (talk from your phone)
|
|
@@ -217,8 +268,7 @@ EOF
|
|
| 217 |
if [ -t 0 ] && [ -t 1 ]; then
|
| 218 |
say "Launching Hermes now..."
|
| 219 |
sleep 1
|
| 220 |
-
export HERMES_HOME="$
|
| 221 |
-
set -a; source "$SECRETS"; set +a
|
| 222 |
exec hermes
|
| 223 |
else
|
| 224 |
say "Run ${C}source $SHELL_RC${N} then ${C}hermes-here${N} to start chatting."
|
|
|
|
| 4 |
# ============================================================================
|
| 5 |
# Creates your own HF bucket from the merve/hermes-agent template, mounts it,
|
| 6 |
# installs Hermes, sets up Telegram, and launches the agent.
|
| 7 |
+
#
|
| 8 |
+
# Layout:
|
| 9 |
+
# ~/hermes-bucket mount of YOUR bucket (config, SOUL, skills, memories, ...)
|
| 10 |
+
# ~/.hermes-home HERMES_HOME (LOCAL). Secrets live here, everything else
|
| 11 |
+
# is symlinked to ~/hermes-bucket so Hermes data syncs.
|
| 12 |
+
# ~/.hermes Hermes Python install (code + venv)
|
| 13 |
+
#
|
| 14 |
+
# Why the shadow dir: Hermes hardcodes its secrets file at $HERMES_HOME/.env
|
| 15 |
+
# and atomically rewrites it on `hermes setup` / sanitization. If HERMES_HOME
|
| 16 |
+
# were the mount, those writes would land in a public bucket. The shadow dir
|
| 17 |
+
# keeps .env local while every other Hermes write still passes through to the
|
| 18 |
+
# bucket via symlink.
|
| 19 |
#
|
| 20 |
# bash <(curl -fsSL https://huggingface.co/merve/hermes-agent-bootstrap/resolve/main/bootstrap.sh)
|
| 21 |
#
|
| 22 |
+
# Idempotent: re-runs are safe.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
# ============================================================================
|
| 24 |
set -euo pipefail
|
| 25 |
|
| 26 |
TEMPLATE="merve/hermes-agent"
|
| 27 |
MOUNT="$HOME/hermes-bucket"
|
| 28 |
+
HOME_DIR="$HOME/.hermes-home"
|
| 29 |
+
LEGACY_SECRETS="$HOME/.hermes-secrets.env"
|
| 30 |
+
SECRETS="$HOME_DIR/.env"
|
| 31 |
|
| 32 |
G='\033[0;32m'; Y='\033[0;33m'; C='\033[0;36m'; R='\033[0;31m'; N='\033[0m'
|
| 33 |
say() { printf "${C}β${N} %s\n" "$*"; }
|
|
|
|
| 115 |
fi
|
| 116 |
|
| 117 |
# ----------------------------------------------------------------------------
|
| 118 |
+
# 6. Hermes Agent install
|
| 119 |
# ----------------------------------------------------------------------------
|
| 120 |
if ! command -v hermes >/dev/null 2>&1; then
|
| 121 |
say "Installing Hermes Agent (this takes a minute)..."
|
|
|
|
| 125 |
ok "Hermes: $(command -v hermes)"
|
| 126 |
|
| 127 |
# ----------------------------------------------------------------------------
|
| 128 |
+
# 7. Shadow HERMES_HOME β keeps .env local, symlinks everything else to bucket
|
| 129 |
+
# ----------------------------------------------------------------------------
|
| 130 |
+
say "Setting up shadow HERMES_HOME at $HOME_DIR..."
|
| 131 |
+
mkdir -p "$HOME_DIR" "$HOME_DIR/audio_cache" "$HOME_DIR/image_cache" "$HOME_DIR/logs"
|
| 132 |
+
|
| 133 |
+
# Make sure bucket subdirs that Hermes writes to exist (so symlinks resolve)
|
| 134 |
+
for d in memories cron hooks pairing sessions; do
|
| 135 |
+
mkdir -p "$MOUNT/$d"
|
| 136 |
+
done
|
| 137 |
+
|
| 138 |
+
# Files mirrored from the bucket (read mostly; if Hermes ever rewrites one
|
| 139 |
+
# atomically the symlink severs and the local copy takes over β safe but no
|
| 140 |
+
# longer syncing for that file)
|
| 141 |
+
for f in config.yaml SOUL.md README.md .env.example; do
|
| 142 |
+
ln -sfn "$MOUNT/$f" "$HOME_DIR/$f"
|
| 143 |
+
done
|
| 144 |
+
# Directory trees that Hermes appends to: writes inside flow through to bucket
|
| 145 |
+
for d in skills memories cron hooks pairing sessions; do
|
| 146 |
+
ln -sfn "$MOUNT/$d" "$HOME_DIR/$d"
|
| 147 |
+
done
|
| 148 |
+
ok "Shadow dir wired: $HOME_DIR (.env stays local, data symlinks β $MOUNT)"
|
| 149 |
+
|
| 150 |
+
# Migrate legacy secrets file from a previous bootstrap, if present
|
| 151 |
+
if [ -f "$LEGACY_SECRETS" ] && [ ! -f "$SECRETS" ]; then
|
| 152 |
+
cp "$LEGACY_SECRETS" "$SECRETS"
|
| 153 |
+
chmod 600 "$SECRETS"
|
| 154 |
+
warn "Migrated legacy $LEGACY_SECRETS β $SECRETS"
|
| 155 |
+
warn " You can delete $LEGACY_SECRETS once you confirm things work."
|
| 156 |
+
fi
|
| 157 |
+
|
| 158 |
+
# ----------------------------------------------------------------------------
|
| 159 |
+
# 8. Telegram (required step)
|
| 160 |
# ----------------------------------------------------------------------------
|
| 161 |
printf "\n${C}Telegram setup${N}\n"
|
| 162 |
|
|
|
|
| 201 |
fi
|
| 202 |
|
| 203 |
# ----------------------------------------------------------------------------
|
| 204 |
+
# 9. Write secrets file (LOCAL, never reaches the bucket)
|
| 205 |
# ----------------------------------------------------------------------------
|
| 206 |
umask 077
|
| 207 |
cat > "$SECRETS" <<EOF
|
| 208 |
+
# Hermes secrets β local-only ($HOME_DIR is HERMES_HOME, not a mount)
|
| 209 |
HF_TOKEN=$HF_TOKEN_VAL
|
| 210 |
TELEGRAM_BOT_TOKEN=$TG_TOKEN
|
| 211 |
TELEGRAM_ALLOWED_USERS=$USER_ID
|
|
|
|
| 215 |
ok "Secrets saved to $SECRETS (mode 600)"
|
| 216 |
|
| 217 |
# ----------------------------------------------------------------------------
|
| 218 |
+
# 10. Shell aliases
|
| 219 |
# ----------------------------------------------------------------------------
|
| 220 |
SHELL_NAME="${SHELL##*/}"
|
| 221 |
case "$SHELL_NAME" in
|
|
|
|
| 224 |
*) SHELL_RC="$HOME/.profile" ;;
|
| 225 |
esac
|
| 226 |
|
| 227 |
+
# Rewrite the aliases block if we already added one (keeps users on the
|
| 228 |
+
# latest definition β old runs used a different layout)
|
| 229 |
+
if [ -f "$SHELL_RC" ] && grep -q "# hermes-bucket aliases" "$SHELL_RC"; then
|
| 230 |
+
python3 - "$SHELL_RC" <<'PY'
|
| 231 |
+
import sys, pathlib
|
| 232 |
+
p = pathlib.Path(sys.argv[1])
|
| 233 |
+
text = p.read_text()
|
| 234 |
+
start = text.find("# hermes-bucket aliases")
|
| 235 |
+
if start == -1:
|
| 236 |
+
sys.exit(0)
|
| 237 |
+
end = text.find("\n\n", start)
|
| 238 |
+
end = len(text) if end == -1 else end
|
| 239 |
+
p.write_text(text[:start].rstrip() + "\n" + text[end:].lstrip("\n"))
|
| 240 |
+
PY
|
| 241 |
+
fi
|
| 242 |
+
|
| 243 |
+
cat >> "$SHELL_RC" <<EOF
|
| 244 |
|
| 245 |
+
# hermes-bucket aliases (added by bootstrap.sh β safe to remove if you'd rather export HERMES_HOME yourself)
|
| 246 |
+
alias hermes-here='HERMES_HOME=\$HOME/.hermes-home hermes'
|
| 247 |
+
alias hermes-gateway='HERMES_HOME=\$HOME/.hermes-home hermes gateway start'
|
| 248 |
EOF
|
| 249 |
+
ok "Shell aliases written to $SHELL_RC"
|
|
|
|
|
|
|
|
|
|
| 250 |
|
| 251 |
# ----------------------------------------------------------------------------
|
| 252 |
+
# 11. Summary + launch
|
| 253 |
# ----------------------------------------------------------------------------
|
| 254 |
cat <<EOF
|
| 255 |
|
| 256 |
${G}β Ready.${N}
|
| 257 |
|
| 258 |
+
Your bucket: https://huggingface.co/buckets/$BUCKET
|
| 259 |
+
Bucket mount: $MOUNT
|
| 260 |
+
HERMES_HOME: $HOME_DIR (secrets local; everything else symlinks to mount)
|
| 261 |
+
Model: Qwen/Qwen3.6-35B-A3B (HF Inference Providers β deepinfra)
|
| 262 |
|
| 263 |
${C}hermes-here${N} β chat in your terminal
|
| 264 |
${C}hermes-gateway${N} β start Telegram bot (talk from your phone)
|
|
|
|
| 268 |
if [ -t 0 ] && [ -t 1 ]; then
|
| 269 |
say "Launching Hermes now..."
|
| 270 |
sleep 1
|
| 271 |
+
export HERMES_HOME="$HOME_DIR"
|
|
|
|
| 272 |
exec hermes
|
| 273 |
else
|
| 274 |
say "Run ${C}source $SHELL_RC${N} then ${C}hermes-here${N} to start chatting."
|