@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,13 @@
1
+ [Unit]
2
+ Description=Bitseek Hermes WebUI
3
+ After=network.target
4
+
5
+ [Service]
6
+ Type=simple
7
+ Environment=HERMES_HOME=%h/.hermes
8
+ ExecStart=%h/.hermes/local-bundle/runtime/node/bin/node %h/.hermes/local-bundle/node-global/bin/hermes-webui start --foreground --port 8787
9
+ Restart=on-failure
10
+ RestartSec=5
11
+
12
+ [Install]
13
+ WantedBy=default.target
@@ -0,0 +1,3 @@
1
+ $Node = "$env:LOCALAPPDATA\hermes\local-bundle\runtime\node\node.exe"
2
+ $Cli = "$env:LOCALAPPDATA\hermes\local-bundle\node-global\bin\hermes-webui.mjs"
3
+ & $Node $Cli start --foreground --port 8787
@@ -0,0 +1,6 @@
1
+ {
2
+ "repo": "adv-org/agent-frontend-shell",
3
+ "branch": "dev-zkp",
4
+ "commit": "36860bfd509554ac8e1a6b56c0dba36ba6692758",
5
+ "synced_at": "2026-06-02T04:40:48.989Z"
6
+ }
@@ -0,0 +1,7 @@
1
+ .git
2
+ .pytest_cache
3
+ __pycache__
4
+ *.pyc
5
+ *.pyo
6
+ tests/
7
+ .env*
@@ -0,0 +1,89 @@
1
+ # Hermes Web UI — Docker Compose configuration template
2
+ #
3
+ # Copy this file to `.env` next to your docker-compose.yml.
4
+ # All variables are optional — Docker Compose substitutes defaults if unset.
5
+ #
6
+ # cp .env.docker.example .env
7
+ # # edit values you care about, then:
8
+ # docker compose up -d
9
+
10
+ # ──────────────────────────────────────────────────────────────────────────
11
+ # UID / GID — host user mapping
12
+ # ──────────────────────────────────────────────────────────────────────────
13
+ # Critical when bind-mounting an EXISTING host directory (e.g. ~/.hermes).
14
+ # The container runs as UID/GID and must match your host file ownership,
15
+ # otherwise the container can't read your config.yaml or write sessions.
16
+ #
17
+ # Find yours: id -u (UID) | id -g (GID)
18
+ #
19
+ # On macOS, UIDs start at 501 (not 1000), so you MUST set these.
20
+ # On Linux, the default of 1000 usually matches the first interactive user.
21
+ #
22
+ # REPLACE THESE WITH YOUR ACTUAL VALUES (run `id -u` and `id -g`):
23
+ UID=1000
24
+ GID=1000
25
+
26
+ # ──────────────────────────────────────────────────────────────────────────
27
+ # Hermes home directory — single-container compose only
28
+ # ──────────────────────────────────────────────────────────────────────────
29
+ # Where on the host your config, sessions, skills, and state live.
30
+ # Default: ~/.hermes (works for everyone with a standard install)
31
+ # Override if your .hermes is elsewhere:
32
+ # HERMES_HOME=/opt/hermes-data
33
+
34
+ # ──────────────────────────────────────────────────────────────────────────
35
+ # Workspace directory — single-container compose
36
+ # ──────────────────────────────────────────────────────────────────────────
37
+ # Path to your code/project directory. The WebUI's file browser shows
38
+ # this at /workspace inside the container.
39
+ # Default: ~/workspace
40
+ # HERMES_WORKSPACE=/home/me/dev
41
+
42
+ # ──────────────────────────────────────────────────────────────────────────
43
+ # Password — protect remote access
44
+ # ──────────────────────────────────────────────────────────────────────────
45
+ # REQUIRED if you expose the container on anything other than 127.0.0.1.
46
+ # Without a password, anyone who can reach the port can run commands as
47
+ # the agent.
48
+ # HERMES_WEBUI_PASSWORD=change-me-to-something-strong
49
+
50
+ # ──────────────────────────────────────────────────────────────────────────
51
+ # Permission handling for bind-mounted .hermes — advanced
52
+ # ──────────────────────────────────────────────────────────────────────────
53
+ # By default, the WebUI's startup credential-permission fixer enforces
54
+ # 0600 mode on .env, auth.json, and similar credential files in HERMES_HOME.
55
+ # This is the right behavior on a clean install, but it can clash with:
56
+ #
57
+ # - Bind-mounting an EXISTING ~/.hermes whose .env is intentionally 0640
58
+ # (e.g. group-readable for a Docker user group)
59
+ # - HERMES_HOME_MODE configured at the agent level for a multi-user setup
60
+ #
61
+ # To bypass the WebUI's fixer entirely:
62
+ # HERMES_SKIP_CHMOD=1
63
+ #
64
+ # OR to allow group bits while still stripping world-readable:
65
+ # HERMES_HOME_MODE=0640
66
+ #
67
+ # ⚠️ MULTI-CONTAINER WARNING: HERMES_HOME_MODE has DIFFERENT semantics in
68
+ # the WebUI vs. the agent image:
69
+ # - WebUI: credential FILE mode threshold (0640 = allow group bits)
70
+ # - Agent: HERMES_HOME *directory* mode (default 0700)
71
+ # 0640 on a directory has NO execute bit, so the agent can't enter its own
72
+ # home → broken. If you set HERMES_HOME_MODE for a multi-container setup,
73
+ # use 0750 (group-traversable) or 0701 (x-only for non-owner traversal).
74
+ # The compose files document both correctly per-service.
75
+
76
+ # ──────────────────────────────────────────────────────────────────────────
77
+ # Multi-container only — used by docker-compose.two-container.yml and
78
+ # docker-compose.three-container.yml
79
+ # ──────────────────────────────────────────────────────────────────────────
80
+ # These compose files use named Docker volumes by default (recommended).
81
+ # Set the variables above to your host UID/GID — both the agent container
82
+ # (HERMES_UID/HERMES_GID) and the webui container (WANTED_UID/WANTED_GID)
83
+ # are derived from $UID/$GID so files written by one are readable by the
84
+ # other.
85
+ #
86
+ # If you switch to bind mounts (replacing `hermes-home: {}` with a `device:`
87
+ # bind), ALL THREE containers must mount the SAME host path and run as the
88
+ # SAME UID/GID. Mismatched UIDs → "Permission denied" → the WebUI crashes
89
+ # on every HTTP request because it can't read its own auth signing key.
@@ -0,0 +1,34 @@
1
+ # Hermes Web UI -- local machine config template
2
+ # Copy this to .env and fill in your values.
3
+ # start.sh sources .env automatically if present.
4
+ # All values are optional -- auto-discovery will fill in anything left blank.
5
+
6
+ # Path to your hermes-agent checkout (the repo that contains run_agent.py)
7
+ # HERMES_WEBUI_AGENT_DIR=/path/to/hermes-agent
8
+
9
+ # Python executable to use (defaults to the agent venv if found)
10
+ # HERMES_WEBUI_PYTHON=/path/to/python
11
+
12
+ # Bind address (default: 127.0.0.1 -- loopback only, safe default)
13
+ # HERMES_WEBUI_HOST=127.0.0.1
14
+
15
+ # Port to listen on (default: 8787)
16
+ # HERMES_WEBUI_PORT=8787
17
+
18
+ # Where to store sessions, workspaces, and other state (default: ~/.hermes/webui)
19
+ # HERMES_WEBUI_STATE_DIR=~/.hermes/webui
20
+
21
+ # Default workspace directory shown on first launch
22
+ # HERMES_WEBUI_DEFAULT_WORKSPACE=~/workspace
23
+
24
+ # Optional model override. Leave unset to use the active Hermes provider default.
25
+ # HERMES_WEBUI_DEFAULT_MODEL=
26
+
27
+ # Base directory for all Hermes state (affects all paths above if set)
28
+ # HERMES_HOME=~/.hermes
29
+
30
+ # Path to your Hermes config.yaml (for toolsets and model config)
31
+ # HERMES_CONFIG_PATH=~/.hermes/config.yaml
32
+
33
+ # Display name for the assistant in the UI (default: Hermes)
34
+ # HERMES_WEBUI_BOT_NAME=Hermes
@@ -0,0 +1,3 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: nesquena
@@ -0,0 +1,42 @@
1
+ name: Browser smoke
2
+
3
+ # Headless page-load smoke: boots the real server.py (agent-free) and loads the
4
+ # key pages in Chromium, failing on any console error or uncaught JS exception.
5
+ # This catches the runtime-JS brick class (const-reassign, function/window
6
+ # collision) that `node --check`, ESLint, and the mocked pytest suite cannot
7
+ # see — they only manifest when a real browser executes the page.
8
+ #
9
+ # No secrets, no credentials: the server boots agent-free and the smoke script
10
+ # strips every *_API_KEY from the environment before launch.
11
+
12
+ on:
13
+ pull_request:
14
+ branches: [master]
15
+ push:
16
+ branches: [master]
17
+
18
+ jobs:
19
+ browser-smoke:
20
+ runs-on: ubuntu-latest
21
+ timeout-minutes: 10
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - name: Set up Python
26
+ uses: actions/setup-python@v5
27
+ with:
28
+ python-version: '3.12'
29
+ cache: 'pip'
30
+ cache-dependency-path: |
31
+ **/requirements*.txt
32
+ **/pyproject.toml
33
+
34
+ - name: Install dependencies
35
+ run: |
36
+ python -m pip install --upgrade pip
37
+ pip install "pyyaml>=6.0" playwright
38
+ # Install only the Chromium browser + its system deps.
39
+ python -m playwright install --with-deps chromium
40
+
41
+ - name: Run browser smoke
42
+ run: python tests/browser_smoke.py
@@ -0,0 +1,233 @@
1
+ name: Docker smoke
2
+
3
+ # Runtime smoke gate for Docker init logic.
4
+ #
5
+ # Background: v0.51.84 (PR #2470) shipped a startup-killing :ro mount + chown
6
+ # interaction (EROFS under `set -e`) that 9 source-level pytest invariants +
7
+ # 5800+ existing tests all passed. The independent reviewer caught it by eye.
8
+ # This workflow closes that class of gap by actually `docker compose up`-ing
9
+ # each variant against a real Docker daemon on the GHA runner.
10
+ #
11
+ # Scope (intentionally small for v1):
12
+ # - 3 compose variants (single, two-container, three-container)
13
+ # - For multi-container variants, rebuild the local Dockerfile and re-tag
14
+ # it as ghcr.io/nesquena/hermes-webui:latest BEFORE `up` so the PR's
15
+ # changes to docker_init.bash / Dockerfile actually execute. Without this
16
+ # the multi-container variants would pull the previous release from GHCR
17
+ # and silently miss every PR-level regression.
18
+ # - Pre-flight `docker compose config` job to catch schema/interpolation drift.
19
+ # - Reaper before each smoke run + trap on EXIT for orphan defence.
20
+ #
21
+ # Out of scope for v1 (per design review):
22
+ # - HERMES_WEBUI_SMOKE_TEST env flag in docker_init.bash (production-code footgun)
23
+ # - --user 60000:60000 (skips the chown branch we're protecting against)
24
+ # - Hadolint / yamllint (separate lint workflow, follow-up PR)
25
+ # - Local-runnable scripts/docker-smoke-test.sh (ship CI first, then iterate)
26
+ # - Podman runtime smoke (defer until a podman-specific bug ships)
27
+
28
+ on:
29
+ pull_request:
30
+ branches: [master]
31
+ paths:
32
+ - 'Dockerfile'
33
+ - 'docker_init.bash'
34
+ - 'docker-compose*.yml'
35
+ - '.dockerignore'
36
+ - '.env.docker.example'
37
+ - '.github/workflows/docker-smoke.yml'
38
+ push:
39
+ branches: [master]
40
+ paths:
41
+ - 'Dockerfile'
42
+ - 'docker_init.bash'
43
+ - 'docker-compose*.yml'
44
+ - '.dockerignore'
45
+ - '.env.docker.example'
46
+ - '.github/workflows/docker-smoke.yml'
47
+ workflow_dispatch:
48
+
49
+ # Fork PRs run with no secrets — that's the right model. Pin to least privilege.
50
+ permissions:
51
+ contents: read
52
+
53
+ jobs:
54
+ compose-config:
55
+ name: Compose config validation
56
+ runs-on: ubuntu-latest
57
+ steps:
58
+ - uses: actions/checkout@v4
59
+
60
+ - name: Validate every compose file parses
61
+ run: |
62
+ set -euo pipefail
63
+ for f in docker-compose.yml docker-compose.two-container.yml docker-compose.three-container.yml; do
64
+ echo "::group::compose config: $f"
65
+ docker compose -f "$f" config > /dev/null
66
+ echo "::endgroup::"
67
+ done
68
+
69
+ # Build the Docker image once and cache layers via GHA cache.
70
+ # The smoke matrix jobs then pull from this cache instead of rebuilding
71
+ # from scratch, saving ~1-3 minutes per variant.
72
+ build-image:
73
+ name: Build Docker image (cache layers)
74
+ runs-on: ubuntu-latest
75
+ needs: compose-config
76
+ steps:
77
+ - uses: actions/checkout@v4
78
+
79
+ - uses: docker/setup-buildx-action@v3
80
+
81
+ - name: Build and cache Docker image
82
+ uses: docker/build-push-action@v6
83
+ with:
84
+ context: .
85
+ load: true
86
+ tags: ghcr.io/nesquena/hermes-webui:latest
87
+ cache-from: type=gha
88
+ cache-to: type=gha,mode=max
89
+
90
+ smoke:
91
+ name: Smoke ${{ matrix.variant }}
92
+ runs-on: ubuntu-latest
93
+ needs: [compose-config, build-image]
94
+ timeout-minutes: 15
95
+ strategy:
96
+ fail-fast: false
97
+ matrix:
98
+ variant:
99
+ - single
100
+ - two-container
101
+ - three-container
102
+ steps:
103
+ - uses: actions/checkout@v4
104
+
105
+ - uses: docker/setup-buildx-action@v3
106
+
107
+ # Restore the cached Docker image from the build-image job.
108
+ # Read-only: the build-image job already populated the GHA cache, so the
109
+ # smoke variants only need cache-from (no redundant cache-to re-export).
110
+ - name: Restore Docker image from cache
111
+ uses: docker/build-push-action@v6
112
+ with:
113
+ context: .
114
+ load: true
115
+ tags: ghcr.io/nesquena/hermes-webui:latest
116
+ cache-from: type=gha
117
+
118
+ - name: Resolve compose file + project name
119
+ id: vars
120
+ run: |
121
+ set -euo pipefail
122
+ case "${{ matrix.variant }}" in
123
+ single)
124
+ echo "compose_file=docker-compose.yml" >> "$GITHUB_OUTPUT"
125
+ ;;
126
+ two-container)
127
+ echo "compose_file=docker-compose.two-container.yml" >> "$GITHUB_OUTPUT"
128
+ ;;
129
+ three-container)
130
+ echo "compose_file=docker-compose.three-container.yml" >> "$GITHUB_OUTPUT"
131
+ ;;
132
+ esac
133
+ # Per-run project name so concurrent jobs / reruns can't clobber each other.
134
+ echo "project=hermes-smoke-${{ matrix.variant }}-${{ github.run_id }}-${{ github.run_attempt }}" >> "$GITHUB_OUTPUT"
135
+
136
+ - name: Reap any prior hermes-smoke resources on this runner
137
+ run: |
138
+ set -euo pipefail
139
+ # Hosted GHA runners are fresh, so this is mostly defence-in-depth for
140
+ # self-hosted runner re-use. We rely primarily on the unique per-run
141
+ # project name + `compose down -v --remove-orphans` in the EXIT trap
142
+ # to clean up the resources THIS run creates; this step only sweeps
143
+ # leftovers from prior runs that crashed before their trap fired.
144
+ # Match by project-name prefix instead of labels (the compose files
145
+ # don't carry hermes-smoke labels on their resources).
146
+ for c in $(docker ps -aq --filter "name=hermes-smoke-"); do
147
+ docker rm -f "$c" || true
148
+ done
149
+ for v in $(docker volume ls -q | grep "^hermes-smoke-" || true); do
150
+ docker volume rm -f "$v" || true
151
+ done
152
+ for n in $(docker network ls --format '{{.Name}}' | grep "^hermes-smoke-" || true); do
153
+ docker network rm "$n" || true
154
+ done
155
+
156
+ - name: Prepare ephemeral host paths
157
+ id: paths
158
+ run: |
159
+ set -euo pipefail
160
+ STATE_DIR="$(mktemp -d -t hermes-smoke-state-XXXXXX)"
161
+ WORK_DIR="$(mktemp -d -t hermes-smoke-work-XXXXXX)"
162
+ echo "state_dir=$STATE_DIR" >> "$GITHUB_OUTPUT"
163
+ echo "work_dir=$WORK_DIR" >> "$GITHUB_OUTPUT"
164
+ echo "Allocated:"
165
+ echo " HERMES_HOME = $STATE_DIR"
166
+ echo " HERMES_WORKSPACE = $WORK_DIR"
167
+
168
+ - name: Smoke (up + health + log scan + down)
169
+ env:
170
+ COMPOSE_FILE: ${{ steps.vars.outputs.compose_file }}
171
+ PROJECT: ${{ steps.vars.outputs.project }}
172
+ HERMES_HOME: ${{ steps.paths.outputs.state_dir }}
173
+ HERMES_WORKSPACE: ${{ steps.paths.outputs.work_dir }}
174
+ run: |
175
+ set -euo pipefail
176
+
177
+ # ----- Trap-guaranteed cleanup, regardless of exit reason -----
178
+ cleanup() {
179
+ local rc=$?
180
+ echo "::group::Cleanup (rc=$rc)"
181
+ docker compose -p "$PROJECT" -f "$COMPOSE_FILE" logs --no-color --tail=200 || true
182
+ docker compose -p "$PROJECT" -f "$COMPOSE_FILE" down -v --remove-orphans || true
183
+ rm -rf "$HERMES_HOME" "$HERMES_WORKSPACE" || true
184
+ echo "::endgroup::"
185
+ return $rc
186
+ }
187
+ trap cleanup EXIT
188
+
189
+ echo "::group::docker compose up"
190
+ # --wait blocks until all services report healthy OR --wait-timeout fires.
191
+ # Compose v2 returns nonzero on either failure mode.
192
+ docker compose -p "$PROJECT" -f "$COMPOSE_FILE" up -d --wait --wait-timeout 120
193
+ echo "::endgroup::"
194
+
195
+ echo "::group::container roster"
196
+ docker compose -p "$PROJECT" -f "$COMPOSE_FILE" ps
197
+ echo "::endgroup::"
198
+
199
+ # ----- WebUI /health probe -----
200
+ # Single-container: WebUI is on the host on 127.0.0.1:8787.
201
+ # Two/three-container: same — both compose files publish 127.0.0.1:8787.
202
+ echo "::group::Probe /health"
203
+ attempts=0
204
+ max_attempts=30
205
+ until curl --fail --silent --max-time 5 http://127.0.0.1:8787/health > /dev/null; do
206
+ attempts=$((attempts + 1))
207
+ if [ "$attempts" -ge "$max_attempts" ]; then
208
+ echo "❌ WebUI /health never returned 200 after $max_attempts attempts (~60s)"
209
+ exit 1
210
+ fi
211
+ sleep 2
212
+ done
213
+ echo "✅ /health = 200 after $attempts attempts"
214
+ echo "::endgroup::"
215
+
216
+ # ----- Startup log scan: must not contain any known-bad signatures -----
217
+ # These are the exact patterns that would have flagged #2470 in real time.
218
+ # The grep -i is anchored to actual error tokens; benign log lines that
219
+ # contain the substring 'error' in a stack-friendly context (e.g.
220
+ # "errorless", URL paths) are improbable for these specific tokens.
221
+ echo "::group::Startup log scan"
222
+ LOGS="$(docker compose -p "$PROJECT" -f "$COMPOSE_FILE" logs --no-color)"
223
+ # `!! ERROR` + `!! Exiting script` are the actual strings emitted by
224
+ # docker_init.bash's error_exit() helper — the function name itself
225
+ # never appears in output. The literal token `error_exit` is kept as
226
+ # a belt-and-suspenders catch for any stray debug/echo of the name.
227
+ BAD_PATTERNS='EROFS|Read-only file system|Traceback|PermissionError|!! ERROR|!! Exiting script|error_exit|groupmod: cannot|usermod: cannot|Failed to set (UID|GID|owner|permissions|ownership)'
228
+ if echo "$LOGS" | grep -E -i "$BAD_PATTERNS"; then
229
+ echo "❌ Startup logs contain known-bad pattern (see above)"
230
+ exit 1
231
+ fi
232
+ echo "✅ No known-bad patterns in startup logs"
233
+ echo "::endgroup::"
@@ -0,0 +1,132 @@
1
+ name: Native Windows startup
2
+
3
+ # Runs on PRs that touch start.ps1 (or this workflow). Validates the
4
+ # native-Windows launch script catches the bug classes the recent
5
+ # Windows-only batch caught manually (#2805 WOW64 ProgramFiles redirect,
6
+ # #2806 venv-portability claim, #2807 port-parse + finally-cleanup).
7
+ #
8
+ # Scope (per nesquena-hermes comment on #2811 — option 1, mock-only):
9
+ # hermes-agent is not published to PyPI, so we cannot pip-install it on
10
+ # the runner. Instead we stub a hermes_cli/ directory next to a sibling
11
+ # hermes-agent/ folder — just enough for start.ps1's existence guard to
12
+ # pass. The workflow then runs start.ps1 for a few seconds and asserts
13
+ # that none of start.ps1's own Write-Error guards fired. Server-boot
14
+ # regressions remain covered by the Linux jobs and docker-smoke.yml.
15
+
16
+ on:
17
+ pull_request:
18
+ paths:
19
+ - 'start.ps1'
20
+ - '.github/workflows/native-windows-startup.yml'
21
+ workflow_dispatch:
22
+
23
+ jobs:
24
+ native-windows-startup:
25
+ name: start.ps1 path discovery (mock hermes-agent)
26
+ runs-on: windows-latest
27
+ timeout-minutes: 8
28
+
29
+ steps:
30
+ - name: Checkout
31
+ uses: actions/checkout@v4
32
+
33
+ - name: Setup Python 3.11
34
+ uses: actions/setup-python@v5
35
+ with:
36
+ python-version: '3.11'
37
+
38
+ # Create the WebUI venv. start.ps1 prefers $AgentDir\venv if it
39
+ # exists, then falls back to the python on PATH. We create a
40
+ # WebUI-local venv to mirror the README's documented native path
41
+ # and to give start.ps1 a real python.exe to invoke.
42
+ - name: Create venv (README path)
43
+ shell: pwsh
44
+ run: |
45
+ python -m venv venv
46
+ if (-not (Test-Path venv\Scripts\python.exe)) {
47
+ throw "venv\Scripts\python.exe missing after venv create"
48
+ }
49
+
50
+ # Mock-only hermes-agent provisioning. We can't pip-install
51
+ # hermes-agent (not on PyPI), so we stub the minimum that
52
+ # start.ps1's `Test-Path hermes_cli -PathType Container` guard
53
+ # needs to pass. server.py would crash on this stub at import
54
+ # time — we deliberately do NOT probe /health below.
55
+ - name: Stub hermes-agent (mock hermes_cli only)
56
+ shell: pwsh
57
+ run: |
58
+ $agentDir = Join-Path (Split-Path -Parent $PWD) 'hermes-agent'
59
+ $cliDir = Join-Path $agentDir 'hermes_cli'
60
+ New-Item -ItemType Directory -Force -Path $cliDir | Out-Null
61
+ Set-Content -Path (Join-Path $cliDir '__init__.py') -Value '# stub for CI path-discovery test only'
62
+ "HERMES_WEBUI_AGENT_DIR=$agentDir" >> $env:GITHUB_ENV
63
+ Write-Host "Stub hermes-agent provisioned at $agentDir"
64
+
65
+ # Run start.ps1 and verify it passes its own discovery guards
66
+ # without erroring out. server.py will exit non-zero on the stub
67
+ # (no real CLI code) — that's expected and not asserted against.
68
+ # We only fail if start.ps1's own Write-Error guards fire.
69
+ - name: Run start.ps1 + verify path discovery
70
+ shell: pwsh
71
+ run: |
72
+ $stdout = Join-Path $env:RUNNER_TEMP 'start-ps1.out'
73
+ $stderr = Join-Path $env:RUNNER_TEMP 'start-ps1.err'
74
+ $proc = Start-Process -FilePath 'pwsh' `
75
+ -ArgumentList '-NoLogo','-File','.\start.ps1' `
76
+ -WorkingDirectory $PWD `
77
+ -PassThru `
78
+ -RedirectStandardOutput $stdout `
79
+ -RedirectStandardError $stderr
80
+ "SERVER_PID=$($proc.Id)" >> $env:GITHUB_ENV
81
+ Write-Host "Spawned start.ps1 wrapper PID $($proc.Id)"
82
+
83
+ # Path discovery is sub-second; the 8s buffer lets the python
84
+ # launch land in the logs (and immediately exit on the stub).
85
+ Start-Sleep -Seconds 8
86
+
87
+ Write-Host "===== start.ps1 stdout ====="
88
+ $stdoutContent = if (Test-Path $stdout) { Get-Content $stdout -Raw } else { '<empty>' }
89
+ Write-Host $stdoutContent
90
+ Write-Host "===== start.ps1 stderr ====="
91
+ $stderrContent = if (Test-Path $stderr) { Get-Content $stderr -Raw } else { '<empty>' }
92
+ Write-Host $stderrContent
93
+
94
+ # Pattern set: every Write-Error message start.ps1 can emit on
95
+ # its own discovery path. If any of these appear in stderr,
96
+ # path discovery regressed and the job must fail.
97
+ $guardErrors = @(
98
+ 'Python 3 is required',
99
+ 'hermes-agent not found',
100
+ 'HERMES_WEBUI_AGENT_DIR is set to',
101
+ 'is not a valid integer port',
102
+ 'is out of TCP-port range',
103
+ 'server.py not found'
104
+ )
105
+ foreach ($msg in $guardErrors) {
106
+ if ($stderrContent -and $stderrContent -match [regex]::Escape($msg)) {
107
+ throw "REGRESSION: start.ps1 errored on guard '$msg' - path discovery failed."
108
+ }
109
+ }
110
+ Write-Host "OK: start.ps1 path discovery - all guards passed."
111
+
112
+ # taskkill /T walks the process tree, /F forces. taskkill returns
113
+ # 128 ("process not found") if the PID is already gone — that's
114
+ # the expected steady state for this mock-only workflow because
115
+ # server.py exits immediately on the stub hermes_cli. Reset
116
+ # $LASTEXITCODE so the step never fails on the cleanup itself.
117
+ - name: Stop background server (tree-kill)
118
+ if: always()
119
+ shell: pwsh
120
+ run: |
121
+ if ($env:SERVER_PID) {
122
+ & taskkill /PID $env:SERVER_PID /T /F 2>&1 | Out-Host
123
+ $global:LASTEXITCODE = 0
124
+ }
125
+ # Belt-and-suspenders: kill anything still bound to 8787.
126
+ $hanging = Get-NetTCPConnection -LocalPort 8787 -State Listen -ErrorAction SilentlyContinue
127
+ if ($hanging) {
128
+ foreach ($c in $hanging) {
129
+ try { Stop-Process -Id $c.OwningProcess -Force -ErrorAction Stop } catch {}
130
+ }
131
+ }
132
+ exit 0
@@ -0,0 +1,57 @@
1
+ name: Release & Docker
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ jobs:
9
+ release:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: write # required: create GitHub Release
13
+ packages: write # required: push to ghcr.io
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ # Create GitHub Release from tag with auto-generated notes
19
+ - name: Create GitHub Release
20
+ uses: softprops/action-gh-release@v2
21
+ with:
22
+ generate_release_notes: true
23
+
24
+ # Set up multi-arch build (QEMU + Buildx)
25
+ - uses: docker/setup-qemu-action@v3
26
+ - uses: docker/setup-buildx-action@v3
27
+
28
+ # Log in to GitHub Container Registry
29
+ - name: Log in to GitHub Container Registry
30
+ uses: docker/login-action@v3
31
+ with:
32
+ registry: ghcr.io
33
+ username: ${{ github.actor }}
34
+ password: ${{ secrets.GITHUB_TOKEN }}
35
+
36
+ # Extract tags from the git ref (supports vX.Y and vX.Y.Z formats)
37
+ - name: Extract metadata
38
+ id: meta
39
+ uses: docker/metadata-action@v5
40
+ with:
41
+ images: ghcr.io/${{ github.repository }}
42
+ tags: |
43
+ type=match,pattern=v(\d+\.\d+(?:\.\d+)?),group=1
44
+ type=raw,value=latest
45
+
46
+ # Build and push multi-arch image (amd64 + arm64)
47
+ - name: Build and push Docker image
48
+ uses: docker/build-push-action@v6
49
+ with:
50
+ context: .
51
+ platforms: linux/amd64,linux/arm64
52
+ push: true
53
+ tags: ${{ steps.meta.outputs.tags }}
54
+ labels: ${{ steps.meta.outputs.labels }}
55
+ build-args: HERMES_VERSION=${{ github.ref_name }}
56
+ cache-from: type=gha
57
+ cache-to: type=gha,mode=max