Skip to content

Commit 0049830

Browse files
fix(self-host): keep Hermes webui alive after setup-agent exits
On macOS/IDE terminals, shell-spawned nohup children get SIGKILL'd when the process group tears down, so Agent tab dies right after setup reports ready. Prefer a LaunchAgent on Darwin, detach with setsid/disown elsewhere, and export HERMES_WEBUI_ALLOWED_ORIGINS for non-default frontend ports.
1 parent 4d0c523 commit 0049830

1 file changed

Lines changed: 63 additions & 3 deletions

File tree

scripts/self-host/setup-agent.sh

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,21 +306,81 @@ start_webui() {
306306
local certifi
307307
certifi="$("$py" -c 'import certifi; print(certifi.where())' 2>/dev/null || true)"
308308

309+
# CORS for browser → nginx → /agent-api (must include the frontend origin:port).
310+
local allowed_origins="${HERMES_WEBUI_ALLOWED_ORIGINS:-http://localhost:${FRONTEND_PORT},http://127.0.0.1:${FRONTEND_PORT}}"
311+
309312
echo "→ Starting Hermes webui on ${WEBUI_HOST}:${WEBUI_PORT}"
313+
314+
# On macOS, prefer a LaunchAgent. Shell-spawned nohup children get SIGKILL'd when
315+
# some IDE/agent terminals tear down the process group — the Agent tab then
316+
# immediately reports "runtime unavailable".
317+
if [[ "$(uname -s)" == "Darwin" ]] && command -v launchctl >/dev/null 2>&1; then
318+
local label="com.deepsql.hermes-webui"
319+
local plist="$HOME/Library/LaunchAgents/${label}.plist"
320+
local uid
321+
uid="$(id -u)"
322+
mkdir -p "$HOME/Library/LaunchAgents"
323+
launchctl bootout "gui/${uid}/${label}" 2>/dev/null || true
324+
pkill -f "${WEBUI_DIR}/server.py" 2>/dev/null || true
325+
cat >"$plist" <<PLIST
326+
<?xml version="1.0" encoding="UTF-8"?>
327+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
328+
<plist version="1.0">
329+
<dict>
330+
<key>Label</key><string>${label}</string>
331+
<key>ProgramArguments</key>
332+
<array>
333+
<string>${py}</string>
334+
<string>${WEBUI_DIR}/server.py</string>
335+
</array>
336+
<key>WorkingDirectory</key><string>${WEBUI_DIR}</string>
337+
<key>EnvironmentVariables</key>
338+
<dict>
339+
<key>HERMES_HOME</key><string>${HERMES_HOME}</string>
340+
<key>HERMES_WEBUI_HOST</key><string>${WEBUI_HOST}</string>
341+
<key>HERMES_WEBUI_PORT</key><string>${WEBUI_PORT}</string>
342+
<key>HERMES_WEBUI_ALLOWED_ORIGINS</key><string>${allowed_origins}</string>
343+
<key>PATH</key><string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>
344+
$(if [[ -n "$certifi" ]]; then
345+
printf ' <key>SSL_CERT_FILE</key><string>%s</string>\n' "$certifi"
346+
printf ' <key>REQUESTS_CA_BUNDLE</key><string>%s</string>\n' "$certifi"
347+
printf ' <key>CURL_CA_BUNDLE</key><string>%s</string>\n' "$certifi"
348+
fi)
349+
</dict>
350+
<key>RunAtLoad</key><true/>
351+
<key>KeepAlive</key><true/>
352+
<key>StandardOutPath</key><string>${HERMES_HOME}/logs/webui.launchd.out.log</string>
353+
<key>StandardErrorPath</key><string>${HERMES_HOME}/logs/webui.launchd.err.log</string>
354+
</dict>
355+
</plist>
356+
PLIST
357+
launchctl bootstrap "gui/${uid}" "$plist" 2>/dev/null \
358+
|| launchctl load -w "$plist"
359+
wait_for_http "http://127.0.0.1:${WEBUI_PORT}/api/mcp/servers" "Hermes webui" 30 1
360+
return 0
361+
fi
362+
363+
# Linux / non-launchd: detach from this script's process group.
364+
rm -f "$PID_FILE"
310365
(
366+
cd "$WEBUI_DIR" || exit 1
311367
export HERMES_HOME
312368
export HERMES_WEBUI_HOST="$WEBUI_HOST"
313369
export HERMES_WEBUI_PORT="$WEBUI_PORT"
370+
export HERMES_WEBUI_ALLOWED_ORIGINS="$allowed_origins"
314371
unset HERMES_WEBUI_PASSWORD
315372
if [[ -n "$certifi" ]]; then
316373
export SSL_CERT_FILE="$certifi"
317374
export REQUESTS_CA_BUNDLE="$certifi"
318375
export CURL_CA_BUNDLE="$certifi"
319376
fi
320-
cd "$WEBUI_DIR"
321-
# Prefer venv/python for the process itself (has MCP when installed there).
322-
nohup "$py" server.py >>"$LOG_FILE" 2>&1 &
377+
if command -v setsid >/dev/null 2>&1; then
378+
setsid nohup "$py" server.py >>"$LOG_FILE" 2>&1 </dev/null &
379+
else
380+
nohup "$py" server.py >>"$LOG_FILE" 2>&1 </dev/null &
381+
fi
323382
echo $! >"$PID_FILE"
383+
disown $! 2>/dev/null || true
324384
)
325385
wait_for_http "http://127.0.0.1:${WEBUI_PORT}/api/mcp/servers" "Hermes webui" 30 1
326386
}

0 commit comments

Comments
 (0)