-
Notifications
You must be signed in to change notification settings - Fork 60
Expand file tree
/
Copy pathstart.sh
More file actions
executable file
·260 lines (217 loc) · 9.77 KB
/
Copy pathstart.sh
File metadata and controls
executable file
·260 lines (217 loc) · 9.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
#!/usr/bin/env bash
# ============================================================
# Hermes Web UI -- portable bootstrap
# Usage: ./start.sh [port]
#
# One-command startup. Discovers your Hermes install, sets up
# a local virtualenv if needed, installs dependencies, then
# launches the server and prints everything you need to know.
#
# Override any step with environment variables:
# HERMES_WEBUI_AGENT_DIR path to hermes-agent checkout
# HERMES_WEBUI_PYTHON python executable to use
# HERMES_WEBUI_PORT port to listen on (default: 8787)
# HERMES_WEBUI_HOST bind address (default: 127.0.0.1)
# HERMES_HOME override ~/.hermes base
# HERMES_WEBUI_STATE_DIR override state directory
# ============================================================
set -euo pipefail
# ── Load .env if present (machine-local overrides, not committed) ─────────────
_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ -f "${_SCRIPT_DIR}/.env" ]]; then
set -a
# shellcheck source=/dev/null
source "${_SCRIPT_DIR}/.env"
set +a
fi
# ── Colours ──────────────────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
ok() { echo -e "${GREEN}[ok]${RESET} $*"; }
warn() { echo -e "${YELLOW}[!!]${RESET} $*"; }
die() { echo -e "${RED}[XX]${RESET} $*" >&2; exit 1; }
info() { echo -e "${CYAN}[--]${RESET} $*"; }
hdr() { echo -e "\n${BOLD}$*${RESET}"; }
# ── Resolve repo root (the directory this script lives in) ───────────────────
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
info "Repo root: ${REPO_ROOT}"
# ── Port ─────────────────────────────────────────────────────────────────────
PORT="${1:-${HERMES_WEBUI_PORT:-8787}}"
export HERMES_WEBUI_PORT="${PORT}"
# ── Python discovery ─────────────────────────────────────────────────────────
hdr "Discovering Python..."
_find_python() {
# 1. Explicit env var
if [[ -n "${HERMES_WEBUI_PYTHON:-}" ]]; then
echo "${HERMES_WEBUI_PYTHON}"; return
fi
# 2. Agent venv (discovered below -- call again after agent dir found)
# (handled after agent dir discovery)
# 3. Local .venv in repo
if [[ -x "${REPO_ROOT}/.venv/bin/python" ]]; then
echo "${REPO_ROOT}/.venv/bin/python"; return
fi
# 4. System python3
if command -v python3 &>/dev/null; then
echo "$(command -v python3)"; return
fi
echo ""
}
PYTHON="$(_find_python)"
# ── Hermes agent discovery ────────────────────────────────────────────────────
hdr "Discovering Hermes agent..."
HERMES_HOME="${HERMES_HOME:-${HOME}/.hermes}"
AGENT_DIR=""
_find_agent() {
local candidates=(
"${HERMES_WEBUI_AGENT_DIR:-}"
"${HERMES_HOME}/hermes-agent"
"${REPO_ROOT}/../hermes-agent"
"${HOME}/.hermes/hermes-agent"
"${HOME}/hermes-agent"
)
for d in "${candidates[@]}"; do
[[ -z "$d" ]] && continue
d="$(cd "${d}" 2>/dev/null && pwd || true)"
if [[ -n "$d" && -f "${d}/run_agent.py" ]]; then
echo "$d"; return
fi
done
echo ""
}
AGENT_DIR="$(_find_agent)"
if [[ -n "${AGENT_DIR}" ]]; then
ok "Hermes agent: ${AGENT_DIR}"
export HERMES_WEBUI_AGENT_DIR="${AGENT_DIR}"
# Now that we have agent dir, prefer its venv if we don't already have a python
if [[ -z "${HERMES_WEBUI_PYTHON:-}" && -x "${AGENT_DIR}/venv/bin/python" ]]; then
PYTHON="${AGENT_DIR}/venv/bin/python"
fi
else
warn "Hermes agent not found. Agent features will not work."
warn "Fix with: export HERMES_WEBUI_AGENT_DIR=/path/to/hermes-agent"
fi
if [[ -n "${PYTHON}" ]]; then
ok "Python: ${PYTHON} ($(${PYTHON} --version 2>&1))"
else
warn "No Python found. Attempting to install..."
if command -v apt-get &>/dev/null; then
sudo apt-get install -y python3 python3-venv python3-pip
elif command -v brew &>/dev/null; then
brew install python3
else
die "Could not find or install Python. Please install Python 3.8+ and re-run."
fi
PYTHON="$(command -v python3)"
ok "Python installed: ${PYTHON}"
fi
# ── Minimum Python version check ─────────────────────────────────────────────
PY_VER="$(${PYTHON} -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')"
PY_MAJOR="$(echo "${PY_VER}" | cut -d. -f1)"
PY_MINOR="$(echo "${PY_VER}" | cut -d. -f2)"
if [[ "${PY_MAJOR}" -lt 3 || ( "${PY_MAJOR}" -eq 3 && "${PY_MINOR}" -lt 8 ) ]]; then
die "Python 3.8+ required. Found: ${PY_VER}"
fi
# ── Dependency check / local venv setup ──────────────────────────────────────
hdr "Checking dependencies..."
VENV_NEEDED=false
VENV_PATH="${REPO_ROOT}/.venv"
# If the chosen python is already the agent venv, its deps are already installed.
# If it is a system python, check if we can import the webui deps, create a local
# .venv if not.
_check_deps() {
"${PYTHON}" -c "import yaml" 2>/dev/null
}
if ! _check_deps; then
info "PyYAML not found in ${PYTHON}. Creating local .venv..."
if [[ ! -d "${VENV_PATH}" ]]; then
"${PYTHON}" -m venv "${VENV_PATH}" || die "Failed to create virtualenv at ${VENV_PATH}"
fi
VENV_PY="${VENV_PATH}/bin/python"
"${VENV_PY}" -m pip install --quiet --upgrade pip
if [[ -f "${REPO_ROOT}/requirements.txt" ]]; then
info "Installing from requirements.txt..."
"${VENV_PY}" -m pip install --quiet -r "${REPO_ROOT}/requirements.txt"
else
info "Installing minimal deps (pyyaml)..."
"${VENV_PY}" -m pip install --quiet pyyaml
fi
PYTHON="${VENV_PY}"
ok "Local venv ready: ${VENV_PATH}"
else
ok "Dependencies satisfied."
fi
# ── Kill any stale instance on the same port ─────────────────────────────────
hdr "Checking for existing instances..."
EXISTING=$(lsof -ti tcp:"${PORT}" 2>/dev/null || true)
if [[ -n "${EXISTING}" ]]; then
warn "Killing existing process on port ${PORT} (PID ${EXISTING})"
kill "${EXISTING}" 2>/dev/null || true
sleep 0.5
fi
# Also kill any server.py process from this repo
pkill -f "${REPO_ROOT}/server.py" 2>/dev/null || true
# ── Set up working directory for Hermes imports ───────────────────────────────
# server.py / api/config.py inject agent dir into sys.path at import time,
# but we also cd into the agent dir so relative imports in run_agent work.
if [[ -n "${AGENT_DIR}" ]]; then
WORKDIR="${AGENT_DIR}"
else
WORKDIR="${REPO_ROOT}"
fi
# ── Launch ───────────────────────────────────────────────────────────────────
hdr "Starting Hermes Web UI..."
LOG="/tmp/hermes-webui-${PORT}.log"
export HERMES_WEBUI_HOST="${HERMES_WEBUI_HOST:-127.0.0.1}"
export HERMES_WEBUI_STATE_DIR="${HERMES_WEBUI_STATE_DIR:-${HERMES_HOME}/webui}"
nohup "${PYTHON}" "${REPO_ROOT}/server.py" \
> "${LOG}" 2>&1 &
PID=$!
echo -e "\n${CYAN} PID ${PID} starting...${RESET}"
sleep 1.5
# ── Health check ─────────────────────────────────────────────────────────────
HEALTH_URL="http://${HERMES_WEBUI_HOST:-127.0.0.1}:${PORT}/health"
MAX_WAIT=15
ELAPSED=0
while [[ $ELAPSED -lt $MAX_WAIT ]]; do
if curl -sf "${HEALTH_URL}" | grep -q '"status"' 2>/dev/null; then
break
fi
sleep 0.5
ELAPSED=$((ELAPSED + 1))
done
if ! curl -sf "${HEALTH_URL}" | grep -q '"status"' 2>/dev/null; then
warn "Health check did not pass within ${MAX_WAIT}s. Check log:"
tail -20 "${LOG}"
echo ""
warn "Server may still be starting. Try: curl ${HEALTH_URL}"
else
ok "Server is healthy."
fi
# ── Print access instructions ─────────────────────────────────────────────────
BIND_HOST="${HERMES_WEBUI_HOST:-127.0.0.1}"
echo ""
echo -e "${BOLD}========================================${RESET}"
echo -e "${GREEN} Hermes Web UI is running${RESET}"
echo -e "${BOLD}========================================${RESET}"
echo ""
if [[ "${BIND_HOST}" == "127.0.0.1" || "${BIND_HOST}" == "localhost" ]]; then
# Server is bound to loopback -- detect if we are on a remote machine
# by checking if $SSH_CLIENT or $SSH_TTY is set
if [[ -n "${SSH_CLIENT:-}" || -n "${SSH_TTY:-}" ]]; then
SERVER_IP="$(hostname -I 2>/dev/null | awk '{print $1}' || echo "<your-server-ip>")"
echo -e " You are on a remote machine. To access from your local browser:"
echo ""
echo -e " ${CYAN}ssh -N -L ${PORT}:127.0.0.1:${PORT} \$(whoami)@${SERVER_IP}${RESET}"
echo ""
echo -e " Then open: ${BOLD}http://localhost:${PORT}${RESET}"
else
echo -e " Open: ${BOLD}http://localhost:${PORT}${RESET}"
fi
else
echo -e " Open: ${BOLD}http://${BIND_HOST}:${PORT}${RESET}"
fi
echo ""
echo -e " Log: ${LOG}"
echo -e " PID: ${PID}"
echo ""