@@ -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