@bitseek/hermes-webui 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/README.md +213 -0
  2. package/bin/hermes-webui.mjs +588 -0
  3. package/package.json +25 -0
  4. package/scripts/sync-vendor.mjs +74 -0
  5. package/templates/launchd/com.bitseek.hermes-webui.plist +21 -0
  6. package/templates/systemd/hermes-webui.service +13 -0
  7. package/templates/windows/hermes-webui-task.ps1 +3 -0
  8. package/vendor/agent-frontend-shell/.bitseek-source.json +6 -0
  9. package/vendor/agent-frontend-shell/.dockerignore +7 -0
  10. package/vendor/agent-frontend-shell/.env.docker.example +89 -0
  11. package/vendor/agent-frontend-shell/.env.example +34 -0
  12. package/vendor/agent-frontend-shell/.github/FUNDING.yml +3 -0
  13. package/vendor/agent-frontend-shell/.github/workflows/browser-smoke.yml +42 -0
  14. package/vendor/agent-frontend-shell/.github/workflows/docker-smoke.yml +233 -0
  15. package/vendor/agent-frontend-shell/.github/workflows/native-windows-startup.yml +132 -0
  16. package/vendor/agent-frontend-shell/.github/workflows/release.yml +57 -0
  17. package/vendor/agent-frontend-shell/.github/workflows/tests.yml +88 -0
  18. package/vendor/agent-frontend-shell/.vscode/launch.json +59 -0
  19. package/vendor/agent-frontend-shell/.vscode/settings.json +13 -0
  20. package/vendor/agent-frontend-shell/AGENTS.md +80 -0
  21. package/vendor/agent-frontend-shell/ARCHITECTURE.md +1658 -0
  22. package/vendor/agent-frontend-shell/BUGS.md +52 -0
  23. package/vendor/agent-frontend-shell/CHANGELOG.md +7295 -0
  24. package/vendor/agent-frontend-shell/CONTRIBUTING.md +205 -0
  25. package/vendor/agent-frontend-shell/CONTRIBUTORS.md +107 -0
  26. package/vendor/agent-frontend-shell/DESIGN.md +173 -0
  27. package/vendor/agent-frontend-shell/Dockerfile +91 -0
  28. package/vendor/agent-frontend-shell/LICENSE +21 -0
  29. package/vendor/agent-frontend-shell/README-CUSTOM.md +76 -0
  30. package/vendor/agent-frontend-shell/README.md +705 -0
  31. package/vendor/agent-frontend-shell/ROADMAP.md +351 -0
  32. package/vendor/agent-frontend-shell/SPRINTS.md +147 -0
  33. package/vendor/agent-frontend-shell/TESTING.md +1932 -0
  34. package/vendor/agent-frontend-shell/THEMES.md +170 -0
  35. package/vendor/agent-frontend-shell/api/__init__.py +1 -0
  36. package/vendor/agent-frontend-shell/api/agent_health.py +392 -0
  37. package/vendor/agent-frontend-shell/api/agent_sessions.py +782 -0
  38. package/vendor/agent-frontend-shell/api/auth.py +592 -0
  39. package/vendor/agent-frontend-shell/api/background.py +87 -0
  40. package/vendor/agent-frontend-shell/api/clarify.py +238 -0
  41. package/vendor/agent-frontend-shell/api/commands.py +124 -0
  42. package/vendor/agent-frontend-shell/api/compression_anchor.py +134 -0
  43. package/vendor/agent-frontend-shell/api/config.py +5178 -0
  44. package/vendor/agent-frontend-shell/api/dashboard_probe.py +255 -0
  45. package/vendor/agent-frontend-shell/api/extensions.py +253 -0
  46. package/vendor/agent-frontend-shell/api/gateway_chat.py +435 -0
  47. package/vendor/agent-frontend-shell/api/gateway_watcher.py +230 -0
  48. package/vendor/agent-frontend-shell/api/goals.py +608 -0
  49. package/vendor/agent-frontend-shell/api/helpers.py +474 -0
  50. package/vendor/agent-frontend-shell/api/kanban_bridge.py +1255 -0
  51. package/vendor/agent-frontend-shell/api/metering.py +194 -0
  52. package/vendor/agent-frontend-shell/api/models.py +4210 -0
  53. package/vendor/agent-frontend-shell/api/oauth.py +770 -0
  54. package/vendor/agent-frontend-shell/api/onboarding.py +1046 -0
  55. package/vendor/agent-frontend-shell/api/passkeys.py +365 -0
  56. package/vendor/agent-frontend-shell/api/profiles.py +1499 -0
  57. package/vendor/agent-frontend-shell/api/providers.py +2175 -0
  58. package/vendor/agent-frontend-shell/api/request_diagnostics.py +160 -0
  59. package/vendor/agent-frontend-shell/api/rollback.py +320 -0
  60. package/vendor/agent-frontend-shell/api/routes.py +13990 -0
  61. package/vendor/agent-frontend-shell/api/run_journal.py +284 -0
  62. package/vendor/agent-frontend-shell/api/runner_client.py +156 -0
  63. package/vendor/agent-frontend-shell/api/runtime_adapter.py +431 -0
  64. package/vendor/agent-frontend-shell/api/session_discoverability.py +640 -0
  65. package/vendor/agent-frontend-shell/api/session_events.py +45 -0
  66. package/vendor/agent-frontend-shell/api/session_lifecycle.py +208 -0
  67. package/vendor/agent-frontend-shell/api/session_ops.py +207 -0
  68. package/vendor/agent-frontend-shell/api/session_recovery.py +655 -0
  69. package/vendor/agent-frontend-shell/api/skill_usage.py +32 -0
  70. package/vendor/agent-frontend-shell/api/startup.py +128 -0
  71. package/vendor/agent-frontend-shell/api/state_sync.py +187 -0
  72. package/vendor/agent-frontend-shell/api/streaming.py +7048 -0
  73. package/vendor/agent-frontend-shell/api/system_health.py +167 -0
  74. package/vendor/agent-frontend-shell/api/terminal.py +410 -0
  75. package/vendor/agent-frontend-shell/api/turn_journal.py +214 -0
  76. package/vendor/agent-frontend-shell/api/updates.py +1261 -0
  77. package/vendor/agent-frontend-shell/api/upload.py +322 -0
  78. package/vendor/agent-frontend-shell/api/usage.py +26 -0
  79. package/vendor/agent-frontend-shell/api/workspace.py +867 -0
  80. package/vendor/agent-frontend-shell/api/workspace_git.py +1261 -0
  81. package/vendor/agent-frontend-shell/api/worktrees.py +357 -0
  82. package/vendor/agent-frontend-shell/bootstrap.py +492 -0
  83. package/vendor/agent-frontend-shell/ctl.sh +427 -0
  84. package/vendor/agent-frontend-shell/docker-compose.custom.yml +26 -0
  85. package/vendor/agent-frontend-shell/docker-compose.three-container.yml +168 -0
  86. package/vendor/agent-frontend-shell/docker-compose.two-container.yml +147 -0
  87. package/vendor/agent-frontend-shell/docker-compose.yml +57 -0
  88. package/vendor/agent-frontend-shell/docker_init.bash +459 -0
  89. package/vendor/agent-frontend-shell/docs/CONTRACTS.md +207 -0
  90. package/vendor/agent-frontend-shell/docs/EXTENSIONS.md +212 -0
  91. package/vendor/agent-frontend-shell/docs/ISSUES.md +23 -0
  92. package/vendor/agent-frontend-shell/docs/UIUX-GUIDE.md +196 -0
  93. package/vendor/agent-frontend-shell/docs/advanced-chat-setup.md +83 -0
  94. package/vendor/agent-frontend-shell/docs/docker.md +337 -0
  95. package/vendor/agent-frontend-shell/docs/onboarding-agent-checklist.md +207 -0
  96. package/vendor/agent-frontend-shell/docs/onboarding.md +202 -0
  97. package/vendor/agent-frontend-shell/docs/remote-access.md +75 -0
  98. package/vendor/agent-frontend-shell/docs/rfcs/README.md +53 -0
  99. package/vendor/agent-frontend-shell/docs/rfcs/agent-source-boundary.md +70 -0
  100. package/vendor/agent-frontend-shell/docs/rfcs/canonical-session-resolution.md +124 -0
  101. package/vendor/agent-frontend-shell/docs/rfcs/hermes-run-adapter-contract.md +1079 -0
  102. package/vendor/agent-frontend-shell/docs/rfcs/turn-journal.md +195 -0
  103. package/vendor/agent-frontend-shell/docs/rfcs/webui-run-state-consistency-contract.md +157 -0
  104. package/vendor/agent-frontend-shell/docs/supervisor.md +280 -0
  105. package/vendor/agent-frontend-shell/docs/troubleshooting.md +132 -0
  106. package/vendor/agent-frontend-shell/docs/ui-ux/index.html +863 -0
  107. package/vendor/agent-frontend-shell/docs/ui-ux/two-stage-proposal.html +768 -0
  108. package/vendor/agent-frontend-shell/docs/why-hermes.md +489 -0
  109. package/vendor/agent-frontend-shell/docs/workspace-git.md +92 -0
  110. package/vendor/agent-frontend-shell/docs/wsl-autostart.md +126 -0
  111. package/vendor/agent-frontend-shell/eslint.runtime-guard.config.mjs +35 -0
  112. package/vendor/agent-frontend-shell/extensions/bitseek-design-system.md +330 -0
  113. package/vendor/agent-frontend-shell/extensions/branding/assets/apple-touch-icon.png +0 -0
  114. package/vendor/agent-frontend-shell/extensions/branding/assets/empty-logo.svg +739 -0
  115. package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-192.png +0 -0
  116. package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-32.png +0 -0
  117. package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-512.png +0 -0
  118. package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-512.svg +745 -0
  119. package/vendor/agent-frontend-shell/extensions/branding/assets/favicon.ico +0 -0
  120. package/vendor/agent-frontend-shell/extensions/branding/assets/favicon.svg +745 -0
  121. package/vendor/agent-frontend-shell/extensions/branding/assets/titlebar-icon-v2.svg +751 -0
  122. package/vendor/agent-frontend-shell/extensions/branding/assets/titlebar-icon-v3.svg +739 -0
  123. package/vendor/agent-frontend-shell/extensions/branding/assets/titlebar-icon.svg +745 -0
  124. package/vendor/agent-frontend-shell/extensions/branding/branding.js +112 -0
  125. package/vendor/agent-frontend-shell/extensions/branding/config.json +14 -0
  126. package/vendor/agent-frontend-shell/extensions/branding/manifest.json +53 -0
  127. package/vendor/agent-frontend-shell/extensions/index.js +67 -0
  128. package/vendor/agent-frontend-shell/extensions/loader/hermes-loader.js +77 -0
  129. package/vendor/agent-frontend-shell/extensions/manifest.json +16 -0
  130. package/vendor/agent-frontend-shell/extensions/pages/ai-teammates/page.css +333 -0
  131. package/vendor/agent-frontend-shell/extensions/pages/ai-teammates/page.js +487 -0
  132. package/vendor/agent-frontend-shell/extensions/pages/manifest.json +6 -0
  133. package/vendor/agent-frontend-shell/extensions/pages/registry.css +56 -0
  134. package/vendor/agent-frontend-shell/extensions/pages/registry.js +302 -0
  135. package/vendor/agent-frontend-shell/extensions/themes/bitseek/index.css +93 -0
  136. package/vendor/agent-frontend-shell/extensions/themes/bitseek/index.js +98 -0
  137. package/vendor/agent-frontend-shell/install.sh +63 -0
  138. package/vendor/agent-frontend-shell/mcp_server.py +567 -0
  139. package/vendor/agent-frontend-shell/package.json +12 -0
  140. package/vendor/agent-frontend-shell/pyproject.toml +56 -0
  141. package/vendor/agent-frontend-shell/pytest.ini +3 -0
  142. package/vendor/agent-frontend-shell/requirements.txt +5 -0
  143. package/vendor/agent-frontend-shell/server.py +624 -0
  144. package/vendor/agent-frontend-shell/start.ps1 +210 -0
  145. package/vendor/agent-frontend-shell/start.sh +65 -0
  146. package/vendor/agent-frontend-shell/static/apple-touch-icon.png +0 -0
  147. package/vendor/agent-frontend-shell/static/boot.js +1990 -0
  148. package/vendor/agent-frontend-shell/static/commands.js +1402 -0
  149. package/vendor/agent-frontend-shell/static/favicon-192.png +0 -0
  150. package/vendor/agent-frontend-shell/static/favicon-32.png +0 -0
  151. package/vendor/agent-frontend-shell/static/favicon-512.png +0 -0
  152. package/vendor/agent-frontend-shell/static/favicon-512.svg +18 -0
  153. package/vendor/agent-frontend-shell/static/favicon.ico +0 -0
  154. package/vendor/agent-frontend-shell/static/favicon.svg +20 -0
  155. package/vendor/agent-frontend-shell/static/i18n.js +15389 -0
  156. package/vendor/agent-frontend-shell/static/icons.js +92 -0
  157. package/vendor/agent-frontend-shell/static/index.html +1506 -0
  158. package/vendor/agent-frontend-shell/static/login.js +177 -0
  159. package/vendor/agent-frontend-shell/static/manifest.json +53 -0
  160. package/vendor/agent-frontend-shell/static/messages.js +3521 -0
  161. package/vendor/agent-frontend-shell/static/onboarding.js +800 -0
  162. package/vendor/agent-frontend-shell/static/panels.js +7995 -0
  163. package/vendor/agent-frontend-shell/static/pwa-startup.js +83 -0
  164. package/vendor/agent-frontend-shell/static/sessions.js +5165 -0
  165. package/vendor/agent-frontend-shell/static/style.css +4774 -0
  166. package/vendor/agent-frontend-shell/static/sw.js +173 -0
  167. package/vendor/agent-frontend-shell/static/terminal.js +632 -0
  168. package/vendor/agent-frontend-shell/static/ui.js +8997 -0
  169. package/vendor/agent-frontend-shell/static/vendor/js-yaml/4.1.0/js-yaml.min.js +2 -0
  170. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_AMS-Regular.ttf +0 -0
  171. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_AMS-Regular.woff +0 -0
  172. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  173. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  174. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  175. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  176. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  177. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  178. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  179. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  180. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  181. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  182. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  183. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  184. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  185. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Bold.ttf +0 -0
  186. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Bold.woff +0 -0
  187. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Bold.woff2 +0 -0
  188. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  189. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  190. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  191. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Italic.ttf +0 -0
  192. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Italic.woff +0 -0
  193. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Italic.woff2 +0 -0
  194. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Regular.ttf +0 -0
  195. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Regular.woff +0 -0
  196. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Regular.woff2 +0 -0
  197. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  198. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  199. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  200. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-Italic.ttf +0 -0
  201. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-Italic.woff +0 -0
  202. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-Italic.woff2 +0 -0
  203. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  204. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  205. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  206. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  207. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  208. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  209. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  210. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  211. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  212. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Script-Regular.ttf +0 -0
  213. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Script-Regular.woff +0 -0
  214. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Script-Regular.woff2 +0 -0
  215. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size1-Regular.ttf +0 -0
  216. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size1-Regular.woff +0 -0
  217. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  218. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size2-Regular.ttf +0 -0
  219. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size2-Regular.woff +0 -0
  220. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  221. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size3-Regular.ttf +0 -0
  222. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size3-Regular.woff +0 -0
  223. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  224. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size4-Regular.ttf +0 -0
  225. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size4-Regular.woff +0 -0
  226. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  227. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  228. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  229. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  230. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/katex.min.css +1 -0
  231. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/katex.min.js +1 -0
  232. package/vendor/agent-frontend-shell/static/vendor/smd.min.js +29 -0
  233. package/vendor/agent-frontend-shell/static/workspace.js +680 -0
@@ -0,0 +1,427 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ HERMES_HOME="${HERMES_HOME:-${HOME}/.hermes}"
6
+ PID_FILE="${HERMES_WEBUI_PID_FILE:-${HERMES_HOME}/webui.pid}"
7
+ LOG_FILE="${HERMES_WEBUI_LOG_FILE:-${HERMES_HOME}/webui.log}"
8
+ STATE_FILE="${HERMES_WEBUI_CTL_STATE_FILE:-${HERMES_HOME}/webui.ctl.env}"
9
+ DEFAULT_STATE_DIR="${HERMES_WEBUI_STATE_DIR:-${HERMES_HOME}/webui}"
10
+ DEFAULT_LAUNCHD_LABEL="${HERMES_WEBUI_LAUNCHD_LABEL:-com.parantoux.hermes-webui}"
11
+
12
+ usage() {
13
+ cat <<'EOF'
14
+ Usage: ./ctl.sh <command> [args]
15
+
16
+ Commands:
17
+ start [bootstrap args...] Start Hermes WebUI as a background daemon
18
+ stop Stop the daemon started by ctl.sh
19
+ restart [bootstrap args...] Stop, then start again
20
+ status Show daemon, host/port, log, and health status
21
+ logs [--lines N] [--follow|--no-follow]
22
+ Show the daemon log (defaults to tail -n 100 -f)
23
+ EOF
24
+ }
25
+
26
+ ensure_home() {
27
+ mkdir -p "${HERMES_HOME}" "${DEFAULT_STATE_DIR}"
28
+ }
29
+
30
+ _load_repo_dotenv_preserving_env() {
31
+ local env_file="${REPO_ROOT}/.env"
32
+ [[ -f "${env_file}" ]] || return 0
33
+
34
+ local -a preserved=()
35
+ local line key value
36
+ while IFS= read -r line || [[ -n "${line}" ]]; do
37
+ line="${line#${line%%[![:space:]]*}}"
38
+ [[ -z "${line}" || "${line}" == \#* || "${line}" != *=* ]] && continue
39
+ key="${line%%=*}"
40
+ key="${key#export }"
41
+ key="${key//[[:space:]]/}"
42
+ [[ "${key}" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || continue
43
+ if [[ -n "${!key+x}" ]]; then
44
+ value="${!key}"
45
+ preserved+=("${key}=${value}")
46
+ fi
47
+ done < "${env_file}"
48
+
49
+ set -a
50
+ # shellcheck source=/dev/null
51
+ source "${env_file}"
52
+ set +a
53
+
54
+ local assignment
55
+ if [[ ${#preserved[@]} -gt 0 ]]; then
56
+ for assignment in "${preserved[@]}"; do
57
+ export "${assignment}"
58
+ done
59
+ fi
60
+ }
61
+
62
+ _find_python() {
63
+ if [[ -n "${HERMES_WEBUI_PYTHON:-}" ]]; then
64
+ printf '%s\n' "${HERMES_WEBUI_PYTHON}"
65
+ elif command -v python3 >/dev/null 2>&1; then
66
+ command -v python3
67
+ elif command -v python >/dev/null 2>&1; then
68
+ command -v python
69
+ else
70
+ echo "[ctl] Python 3 is required to run bootstrap.py" >&2
71
+ return 1
72
+ fi
73
+ }
74
+
75
+ _parse_launch_binding() {
76
+ CTL_HOST="${HERMES_WEBUI_HOST:-127.0.0.1}"
77
+ CTL_PORT="${HERMES_WEBUI_PORT:-8787}"
78
+ local arg next_is_host=0 saw_port=0
79
+ for arg in "$@"; do
80
+ if (( next_is_host )); then
81
+ CTL_HOST="${arg}"
82
+ next_is_host=0
83
+ continue
84
+ fi
85
+ case "${arg}" in
86
+ --host)
87
+ next_is_host=1
88
+ ;;
89
+ --host=*)
90
+ CTL_HOST="${arg#--host=}"
91
+ ;;
92
+ --*)
93
+ ;;
94
+ *)
95
+ if (( ! saw_port )) && [[ "${arg}" =~ ^[0-9]+$ ]]; then
96
+ CTL_PORT="${arg}"
97
+ saw_port=1
98
+ fi
99
+ ;;
100
+ esac
101
+ done
102
+ }
103
+
104
+ _build_bootstrap_args() {
105
+ CTL_BOOTSTRAP_ARGS=()
106
+ local arg next_is_host=0 saw_port=0
107
+ for arg in "$@"; do
108
+ if (( next_is_host )); then
109
+ next_is_host=0
110
+ continue
111
+ fi
112
+ case "${arg}" in
113
+ --host)
114
+ next_is_host=1
115
+ ;;
116
+ --host=*)
117
+ ;;
118
+ --*)
119
+ CTL_BOOTSTRAP_ARGS+=("${arg}")
120
+ ;;
121
+ *)
122
+ if (( ! saw_port )) && [[ "${arg}" =~ ^[0-9]+$ ]]; then
123
+ saw_port=1
124
+ else
125
+ CTL_BOOTSTRAP_ARGS+=("${arg}")
126
+ fi
127
+ ;;
128
+ esac
129
+ done
130
+ }
131
+
132
+ _write_state() {
133
+ local pid="$1" host="$2" port="$3" python_exe="${4:-}"
134
+ local state_dir="${HERMES_WEBUI_STATE_DIR:-${DEFAULT_STATE_DIR}}"
135
+ {
136
+ printf 'PID=%q\n' "${pid}"
137
+ printf 'REPO_ROOT=%q\n' "${REPO_ROOT}"
138
+ printf 'PYTHON_EXE=%q\n' "${python_exe}"
139
+ printf 'HOST=%q\n' "${host}"
140
+ printf 'PORT=%q\n' "${port}"
141
+ printf 'LOG_FILE=%q\n' "${LOG_FILE}"
142
+ printf 'STATE_DIR=%q\n' "${state_dir}"
143
+ printf 'STARTED_AT=%q\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
144
+ } > "${STATE_FILE}"
145
+ }
146
+
147
+ _load_state_if_present() {
148
+ if [[ -f "${STATE_FILE}" ]]; then
149
+ # shellcheck source=/dev/null
150
+ source "${STATE_FILE}"
151
+ fi
152
+ }
153
+
154
+ _pid_from_file() {
155
+ [[ -f "${PID_FILE}" ]] || return 1
156
+ local pid
157
+ pid="$(tr -d '[:space:]' < "${PID_FILE}")"
158
+ [[ "${pid}" =~ ^[0-9]+$ ]] || return 1
159
+ printf '%s\n' "${pid}"
160
+ }
161
+
162
+ _is_alive() {
163
+ local pid="$1"
164
+ kill -0 "${pid}" >/dev/null 2>&1
165
+ }
166
+
167
+ _proc_args() {
168
+ local pid="$1"
169
+ ps -p "${pid}" -o args= 2>/dev/null || true
170
+ }
171
+
172
+ _is_owned_webui_pid() {
173
+ local pid="$1" args state_repo="" state_python=""
174
+ [[ -f "${STATE_FILE}" ]] || return 1
175
+ _load_state_if_present
176
+ state_repo="${REPO_ROOT:-}"
177
+ state_python="${PYTHON_EXE:-}"
178
+ [[ "${state_repo}" == "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ]] || return 1
179
+ args="$(_proc_args "${pid}")"
180
+ [[ -n "${args}" ]] || return 1
181
+ [[ "${args}" == *"${state_repo}/bootstrap.py"* || "${args}" == *"${state_repo}/server.py"* || "${args}" == *"${state_repo}/start.sh"* || ( -n "${state_python}" && "${args}" == *"${state_python}"* ) ]]
182
+ }
183
+
184
+ _current_pid() {
185
+ local pid
186
+ pid="$(_pid_from_file)" || return 1
187
+ if _is_alive "${pid}" && _is_owned_webui_pid "${pid}"; then
188
+ printf '%s\n' "${pid}"
189
+ return 0
190
+ fi
191
+ return 1
192
+ }
193
+
194
+ _clear_stale_pid() {
195
+ if [[ -f "${PID_FILE}" ]]; then
196
+ rm -f "${PID_FILE}" "${STATE_FILE}"
197
+ echo "[ctl] Removed stale PID file: ${PID_FILE}"
198
+ fi
199
+ }
200
+
201
+ _pid_listens_on_port() {
202
+ # Best-effort check that PID $1 has a listening socket on TCP port $2.
203
+ # macOS (where launchd exists) ships lsof; if we can't determine ownership we
204
+ # return 2 ("unknown") so the caller can fall back conservatively rather than
205
+ # guess. Never blocks on a hard failure.
206
+ local pid="$1" port="$2"
207
+ [[ "${pid}" =~ ^[0-9]+$ && "${port}" =~ ^[0-9]+$ ]] || return 2
208
+ if command -v lsof >/dev/null 2>&1; then
209
+ if lsof -nP -p "${pid}" -iTCP:"${port}" -sTCP:LISTEN >/dev/null 2>&1; then
210
+ return 0 # PID is listening on that port → real conflict
211
+ fi
212
+ return 1 # PID is alive but NOT listening on that port → no conflict
213
+ fi
214
+ return 2 # can't determine
215
+ }
216
+
217
+ _launchd_webui_pid() {
218
+ [[ "${HERMES_WEBUI_CTL_ALLOW_LAUNCHD_CONFLICT:-0}" == "1" ]] && return 1
219
+ command -v launchctl >/dev/null 2>&1 || return 1
220
+ local label="${HERMES_WEBUI_LAUNCHD_LABEL:-${DEFAULT_LAUNCHD_LABEL}}"
221
+ [[ -n "${label}" ]] || return 1
222
+ local uid launchd_out pid
223
+ uid="$(id -u)"
224
+ launchd_out="$(launchctl print "gui/${uid}/${label}" 2>/dev/null)" || return 1
225
+ pid="$(printf '%s\n' "${launchd_out}" | awk '/^[[:space:]]*pid = / {print $3; exit}')"
226
+ [[ "${pid}" =~ ^[0-9]+$ ]] || return 1
227
+ (( pid > 0 )) || return 1
228
+ _is_alive "${pid}" || return 1
229
+ # Only treat the launchd job as a conflict for the port we are about to bind.
230
+ # A second instance on a DIFFERENT port (e.g. HERMES_WEBUI_PORT=8788 for a
231
+ # test build) does not collide with the launchd-managed default and must be
232
+ # allowed to start (#3291 over-block fix). When port ownership can't be
233
+ # determined (no lsof), fall back to the conservative previous behavior of
234
+ # only guarding the default port so non-default ports are never wrongly blocked.
235
+ local want_port="${CTL_PORT:-${HERMES_WEBUI_PORT:-8787}}"
236
+ _pid_listens_on_port "${pid}" "${want_port}"
237
+ case "$?" in
238
+ 0) printf '%s\n' "${pid}"; return 0 ;; # launchd job listens on our port → block
239
+ 1) return 1 ;; # launchd job on a different port → allow
240
+ *) # unknown: only guard the default port
241
+ if [[ "${want_port}" == "8787" ]]; then
242
+ printf '%s\n' "${pid}"; return 0
243
+ fi
244
+ return 1 ;;
245
+ esac
246
+ }
247
+
248
+ start_cmd() {
249
+ ensure_home
250
+ _load_repo_dotenv_preserving_env
251
+ export HERMES_WEBUI_STATE_DIR="${HERMES_WEBUI_STATE_DIR:-${DEFAULT_STATE_DIR}}"
252
+ mkdir -p "${HERMES_WEBUI_STATE_DIR}"
253
+ _parse_launch_binding "$@"
254
+ _build_bootstrap_args "$@"
255
+ export HERMES_WEBUI_HOST="${CTL_HOST}"
256
+ export HERMES_WEBUI_PORT="${CTL_PORT}"
257
+
258
+ local existing_pid
259
+ if existing_pid="$(_current_pid 2>/dev/null)"; then
260
+ echo "[ctl] Hermes WebUI is already running (PID ${existing_pid})"
261
+ return 0
262
+ fi
263
+ local launchd_pid
264
+ if launchd_pid="$(_launchd_webui_pid 2>/dev/null)"; then
265
+ echo "[ctl] Refusing to start a second Hermes WebUI while launchd job ${HERMES_WEBUI_LAUNCHD_LABEL:-${DEFAULT_LAUNCHD_LABEL}} is running (PID ${launchd_pid})." >&2
266
+ echo "[ctl] Use launchctl kickstart -k gui/$(id -u)/${HERMES_WEBUI_LAUNCHD_LABEL:-${DEFAULT_LAUNCHD_LABEL}} or disable the launchd job before using ctl.sh start." >&2
267
+ return 2
268
+ fi
269
+ _clear_stale_pid >/dev/null 2>&1 || true
270
+
271
+ local python_exe pid
272
+ python_exe="$(_find_python)"
273
+ : >> "${LOG_FILE}"
274
+ (
275
+ cd "${REPO_ROOT}"
276
+ trap '' HUP
277
+ export HERMES_WEBUI_PRESERVE_ENV=1
278
+ exec nohup "${python_exe}" "${REPO_ROOT}/bootstrap.py" --no-browser --foreground --host "${CTL_HOST}" "${CTL_PORT}" ${CTL_BOOTSTRAP_ARGS[@]+"${CTL_BOOTSTRAP_ARGS[@]}"}
279
+ ) >> "${LOG_FILE}" 2>&1 &
280
+ pid=$!
281
+
282
+ printf '%s\n' "${pid}" > "${PID_FILE}"
283
+ _write_state "${pid}" "${CTL_HOST}" "${CTL_PORT}" "${python_exe}"
284
+ sleep 0.15
285
+ if ! _is_alive "${pid}"; then
286
+ echo "[ctl] Hermes WebUI failed to stay running. Log: ${LOG_FILE}" >&2
287
+ rm -f "${PID_FILE}" "${STATE_FILE}"
288
+ return 1
289
+ fi
290
+ echo "[ctl] Started Hermes WebUI (PID ${pid})"
291
+ echo "[ctl] Bound: ${CTL_HOST}:${CTL_PORT}"
292
+ echo "[ctl] Log: ${LOG_FILE}"
293
+ }
294
+
295
+ stop_cmd() {
296
+ ensure_home
297
+ local pid
298
+ if ! pid="$(_pid_from_file 2>/dev/null)"; then
299
+ echo "[ctl] Hermes WebUI is stopped"
300
+ rm -f "${PID_FILE}" "${STATE_FILE}"
301
+ return 0
302
+ fi
303
+
304
+ if ! _is_alive "${pid}" || ! _is_owned_webui_pid "${pid}"; then
305
+ _clear_stale_pid
306
+ return 0
307
+ fi
308
+
309
+ echo "[ctl] Stopping Hermes WebUI (PID ${pid})"
310
+ kill "${pid}" >/dev/null 2>&1 || true
311
+ local i
312
+ for i in {1..50}; do
313
+ if ! _is_alive "${pid}"; then
314
+ rm -f "${PID_FILE}" "${STATE_FILE}"
315
+ echo "[ctl] Stopped"
316
+ return 0
317
+ fi
318
+ sleep 0.1
319
+ done
320
+
321
+ echo "[ctl] Process did not exit after SIGTERM; sending SIGKILL" >&2
322
+ kill -KILL "${pid}" >/dev/null 2>&1 || true
323
+ rm -f "${PID_FILE}" "${STATE_FILE}"
324
+ }
325
+
326
+ _health_line() {
327
+ local host="$1" port="$2" url result
328
+ url="http://${host}:${port}/health"
329
+ if command -v curl >/dev/null 2>&1; then
330
+ if result="$(curl -fsS --max-time 2 "${url}" 2>/dev/null)"; then
331
+ if command -v python3 >/dev/null 2>&1; then
332
+ printf '%s' "${result}" | python3 -c 'import json,sys
333
+ try:
334
+ data=json.load(sys.stdin)
335
+ sessions=data.get("sessions", data.get("session_count", "?"))
336
+ active=data.get("active_streams", "?")
337
+ status=data.get("status", "ok")
338
+ print(f"ok ({sessions} sessions, {active} active streams)" if status == "ok" else status)
339
+ except Exception:
340
+ print("ok")'
341
+ else
342
+ echo "ok"
343
+ fi
344
+ else
345
+ echo "unreachable (${url})"
346
+ fi
347
+ else
348
+ echo "unknown (curl not found; ${url})"
349
+ fi
350
+ }
351
+
352
+ status_cmd() {
353
+ ensure_home
354
+ _load_state_if_present
355
+ local host="${HOST:-${HERMES_WEBUI_HOST:-127.0.0.1}}"
356
+ local port="${PORT:-${HERMES_WEBUI_PORT:-8787}}"
357
+ local log_path="${LOG_FILE}"
358
+ local pid uptime health
359
+
360
+ if pid="$(_current_pid 2>/dev/null)"; then
361
+ uptime="$(ps -p "${pid}" -o etime= 2>/dev/null | sed 's/^ *//' || true)"
362
+ health="$(_health_line "${host}" "${port}")"
363
+ echo "● hermes-webui — running"
364
+ echo " PID: ${pid}"
365
+ echo " Uptime: ${uptime:-unknown}"
366
+ echo " Bound: ${host}:${port}"
367
+ echo " Log: ${log_path}"
368
+ echo " Health: ${health}"
369
+ else
370
+ [[ -f "${PID_FILE}" ]] && _clear_stale_pid >/dev/null 2>&1 || true
371
+ echo "● hermes-webui — stopped"
372
+ echo " PID: -"
373
+ echo " Bound: ${host}:${port}"
374
+ echo " Log: ${log_path}"
375
+ echo " Health: not checked"
376
+ fi
377
+ }
378
+
379
+ logs_cmd() {
380
+ ensure_home
381
+ local lines=100 follow=1
382
+ while [[ $# -gt 0 ]]; do
383
+ case "$1" in
384
+ --lines)
385
+ shift
386
+ lines="${1:-}"
387
+ [[ "${lines}" =~ ^[0-9]+$ ]] || { echo "[ctl] --lines requires a number" >&2; return 2; }
388
+ ;;
389
+ --lines=*)
390
+ lines="${1#--lines=}"
391
+ [[ "${lines}" =~ ^[0-9]+$ ]] || { echo "[ctl] --lines requires a number" >&2; return 2; }
392
+ ;;
393
+ --follow|-f)
394
+ follow=1
395
+ ;;
396
+ --no-follow)
397
+ follow=0
398
+ ;;
399
+ *)
400
+ echo "[ctl] Unknown logs option: $1" >&2
401
+ return 2
402
+ ;;
403
+ esac
404
+ shift
405
+ done
406
+ touch "${LOG_FILE}"
407
+ if (( follow )); then
408
+ tail -n "${lines}" -f "${LOG_FILE}"
409
+ else
410
+ tail -n "${lines}" "${LOG_FILE}"
411
+ fi
412
+ }
413
+
414
+ cmd="${1:-}"
415
+ if [[ $# -gt 0 ]]; then
416
+ shift
417
+ fi
418
+
419
+ case "${cmd}" in
420
+ start) start_cmd "$@" ;;
421
+ stop) stop_cmd ;;
422
+ restart) stop_cmd; start_cmd "$@" ;;
423
+ status) status_cmd ;;
424
+ logs) logs_cmd "$@" ;;
425
+ -h|--help|help|"") usage ;;
426
+ *) echo "[ctl] Unknown command: ${cmd}" >&2; usage >&2; exit 2 ;;
427
+ esac
@@ -0,0 +1,26 @@
1
+ version: '3.8'
2
+
3
+ services:
4
+ hermes-webui-custom:
5
+ image: ghcr.io/yourusername/hermes-webui:custom
6
+ container_name: hermes-webui-custom
7
+ restart: unless-stopped
8
+ ports:
9
+ - "127.0.0.1:8787:8787"
10
+ environment:
11
+ - HERMES_WEBUI_HOST=0.0.0.0
12
+ - HERMES_WEBUI_PASSWORD=${HERMES_WEBUI_PASSWORD:-}
13
+ - WANTED_UID=${UID:-1000}
14
+ - WANTED_GID=${GID:-1000}
15
+ volumes:
16
+ - hermes-data:/home/hermeswebui/.hermes
17
+ - ${HOME}/workspace:/workspace
18
+ healthcheck:
19
+ test: ["CMD", "curl", "-f", "http://127.0.0.1:8787/health"]
20
+ interval: 30s
21
+ timeout: 10s
22
+ retries: 3
23
+
24
+ volumes:
25
+ hermes-data:
26
+ driver: local
@@ -0,0 +1,168 @@
1
+ # Three-container Docker Compose: Hermes Agent + Dashboard + WebUI
2
+ #
3
+ # QUICK START:
4
+ # docker compose -f docker-compose.three-container.yml up -d
5
+ # Open http://localhost:8787 (chat) and http://localhost:9119 (dashboard)
6
+ #
7
+ # This extends the two-container setup with the Hermes Dashboard for
8
+ # monitoring agent activity, sessions, and resource usage.
9
+ #
10
+ # Services:
11
+ # hermes-agent — gateway API on port 8642 (CLI, Telegram, cron, tools)
12
+ # hermes-dashboard — monitoring dashboard on port 9119
13
+ # hermes-webui — browser chat interface on port 8787
14
+ #
15
+ # All three share the same hermes-home volume so config, sessions,
16
+ # skills, and memory are consistent across all surfaces.
17
+ #
18
+ # WHEN NOT TO USE THIS:
19
+ # - You hit "Permission denied" trying to share an existing ~/.hermes directory
20
+ # → use docker-compose.yml (single-container) instead, OR
21
+ # → keep this file but switch to NAMED VOLUMES (the default) instead of bind mounts
22
+ # - You're on Podman 3.4 or older without keep-id namespace support
23
+ # → see https://github.com/sunnysktsang/hermes-suite for an all-in-one image
24
+ #
25
+ # KNOWN LIMITATION (#681): tools triggered from the WebUI run in the WebUI
26
+ # container, not the agent container. See docker-compose.two-container.yml
27
+ # for context.
28
+ #
29
+ # NOTE ON VOLUMES:
30
+ # This file uses named Docker volumes (hermes-home, hermes-agent-src) which
31
+ # work out of the box. If you prefer bind mounts (e.g. to an existing
32
+ # directory), see docker-compose.two-container.yml for a bind-mount example.
33
+ # When using bind mounts, ALL THREE containers must mount the same host path
34
+ # AND run as the same UID/GID (set via the UID/GID env vars below).
35
+
36
+ services:
37
+ hermes-agent:
38
+ image: nousresearch/hermes-agent:latest
39
+ container_name: hermes-agent
40
+ command: gateway run
41
+ ports:
42
+ - "127.0.0.1:8642:8642"
43
+ volumes:
44
+ # Persist config, state, sessions, skills, memory across restarts
45
+ - hermes-home:/home/hermes/.hermes
46
+ # Expose agent source so the WebUI can install dependencies from it
47
+ - hermes-agent-src:/opt/hermes
48
+ environment:
49
+ - HERMES_HOME=/home/hermes/.hermes
50
+ # Align UID/GID across containers sharing the hermes-home volume.
51
+ # Defaults to 1000 to match WANTED_UID/WANTED_GID in the webui service.
52
+ - HERMES_UID=${UID:-1000}
53
+ - HERMES_GID=${GID:-1000}
54
+ # Bind-mount permission handling for the agent — narrow set of overrides.
55
+ # NOTE: The agent's HERMES_HOME_MODE applies to the HERMES_HOME *directory*
56
+ # mode (default 0700) — NOT to credential files like the WebUI's variant.
57
+ # If you set this, you MUST keep the owner-execute bit so the agent can
58
+ # traverse its own home directory. 0640 BREAKS the agent (no x bit → no
59
+ # traversal). Use 0750 for group-traversable or 0701 for x-only.
60
+ # The agent's container detection (/.dockerenv) already auto-skips
61
+ # credential chmod inside Docker, so HERMES_SKIP_CHMOD is redundant here.
62
+ # - HERMES_HOME_MODE=0750
63
+ restart: unless-stopped
64
+ deploy:
65
+ resources:
66
+ limits:
67
+ memory: 4G
68
+ cpus: "2.0"
69
+ networks:
70
+ - hermes-net
71
+
72
+ hermes-dashboard:
73
+ image: nousresearch/hermes-agent:latest
74
+ container_name: hermes-dashboard
75
+ command: dashboard --host 0.0.0.0 --insecure
76
+ ports:
77
+ - "127.0.0.1:9119:9119"
78
+ volumes:
79
+ - hermes-home:/home/hermes/.hermes
80
+ environment:
81
+ - HERMES_HOME=/home/hermes/.hermes
82
+ # Align UID/GID across containers sharing the hermes-home volume.
83
+ # Defaults to 1000 to match WANTED_UID/WANTED_GID in the webui service.
84
+ - HERMES_UID=${UID:-1000}
85
+ - HERMES_GID=${GID:-1000}
86
+ # Dashboard connects to the gateway for health/session data
87
+ - GATEWAY_HEALTH_URL=http://hermes-agent:8642
88
+ depends_on:
89
+ - hermes-agent
90
+ restart: unless-stopped
91
+ deploy:
92
+ resources:
93
+ limits:
94
+ memory: 512M
95
+ cpus: "0.5"
96
+ networks:
97
+ - hermes-net
98
+
99
+ hermes-webui:
100
+ image: ghcr.io/nesquena/hermes-webui:latest
101
+ container_name: hermes-webui
102
+ depends_on:
103
+ - hermes-agent
104
+ ports:
105
+ # Expose on localhost only. Remove 127.0.0.1: to expose on all interfaces
106
+ # (set HERMES_WEBUI_PASSWORD if doing so).
107
+ - "127.0.0.1:8787:8787"
108
+ volumes:
109
+ # Same hermes home as the agent — shares config, sessions, state
110
+ - hermes-home:/home/hermeswebui/.hermes
111
+ # Agent source mounted where docker_init.bash expects it.
112
+ # Mounted read-only — the WebUI only reads this volume to install
113
+ # the agent's Python dependencies at startup (`uv pip install`).
114
+ # Read-only enforces that defence-in-depth at the kernel layer.
115
+ - hermes-agent-src:/home/hermeswebui/.hermes/hermes-agent:ro
116
+ # Workspace directory — browse and edit files from the WebUI.
117
+ # Adapt the host path to your project directory.
118
+ # ${HOME} is used rather than `~` so the default resolves the same way
119
+ # across Linux, macOS, WSL2, and Docker Desktop on Windows.
120
+ - ${HERMES_WORKSPACE:-${HOME}/workspace}:/workspace
121
+ environment:
122
+ - HERMES_WEBUI_HOST=0.0.0.0
123
+ - HERMES_WEBUI_PORT=8787
124
+ - HERMES_WEBUI_STATE_DIR=/home/hermeswebui/.hermes/webui
125
+ # Match your host user's UID/GID for correct file permissions.
126
+ # Run `id -u` and `id -g` to find your values.
127
+ # On macOS, UIDs start at 501 (not 1000) — set these in a .env file:
128
+ # echo "UID=$(id -u)" >> .env && echo "GID=$(id -g)" >> .env
129
+ - WANTED_UID=${UID:-1000}
130
+ - WANTED_GID=${GID:-1000}
131
+ # NOTE: When using bind-mount volumes shared across containers, ALL containers
132
+ # that write to the same host directory must run as the same UID/GID.
133
+ # If hermes-agent initialises the state dir as root (UID 0), hermes-webui
134
+ # will get a PermissionError accessing those paths — including a crash on every
135
+ # HTTP request if the auth signing-key file is unreadable. Either set WANTED_UID
136
+ # to match the agent container's UID, or use a named Docker volume (preferred).
137
+ # Optional: set a password for remote access
138
+ # - HERMES_WEBUI_PASSWORD=your-secret-password
139
+ # Bind-mount permission handling for the WebUI (fixes #1389, #1399).
140
+ # NOTE: WebUI's HERMES_HOME_MODE is a credential-file threshold (allow
141
+ # group bits on .env/.signing_key/etc.), DIFFERENT from the agent's
142
+ # which applies to the HERMES_HOME directory itself. 0640 is correct
143
+ # for the WebUI; do NOT copy this value to the agent service block.
144
+ # - HERMES_SKIP_CHMOD=1
145
+ # - HERMES_HOME_MODE=0640
146
+ restart: unless-stopped
147
+ networks:
148
+ - hermes-net
149
+
150
+ networks:
151
+ hermes-net:
152
+ driver: bridge
153
+
154
+ volumes:
155
+ # IMPORTANT — upgrading the agent image:
156
+ # The `hermes-agent-src` volume is initialised from the agent image's
157
+ # `/opt/hermes` on first `up`, and Docker reuses the volume verbatim on
158
+ # later runs — even after `docker pull` of a newer agent image. After
159
+ # upgrading the agent image, run:
160
+ #
161
+ # docker compose -f docker-compose.three-container.yml down
162
+ # docker volume rm <project>_hermes-agent-src
163
+ # docker compose -f docker-compose.three-container.yml pull
164
+ # docker compose -f docker-compose.three-container.yml up -d
165
+ #
166
+ # The full procedure (and why) is documented in docs/docker.md.
167
+ hermes-home:
168
+ hermes-agent-src: