@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.
- package/README.md +213 -0
- package/bin/hermes-webui.mjs +588 -0
- package/package.json +25 -0
- package/scripts/sync-vendor.mjs +74 -0
- package/templates/launchd/com.bitseek.hermes-webui.plist +21 -0
- package/templates/systemd/hermes-webui.service +13 -0
- package/templates/windows/hermes-webui-task.ps1 +3 -0
- package/vendor/agent-frontend-shell/.bitseek-source.json +6 -0
- package/vendor/agent-frontend-shell/.dockerignore +7 -0
- package/vendor/agent-frontend-shell/.env.docker.example +89 -0
- package/vendor/agent-frontend-shell/.env.example +34 -0
- package/vendor/agent-frontend-shell/.github/FUNDING.yml +3 -0
- package/vendor/agent-frontend-shell/.github/workflows/browser-smoke.yml +42 -0
- package/vendor/agent-frontend-shell/.github/workflows/docker-smoke.yml +233 -0
- package/vendor/agent-frontend-shell/.github/workflows/native-windows-startup.yml +132 -0
- package/vendor/agent-frontend-shell/.github/workflows/release.yml +57 -0
- package/vendor/agent-frontend-shell/.github/workflows/tests.yml +88 -0
- package/vendor/agent-frontend-shell/.vscode/launch.json +59 -0
- package/vendor/agent-frontend-shell/.vscode/settings.json +13 -0
- package/vendor/agent-frontend-shell/AGENTS.md +80 -0
- package/vendor/agent-frontend-shell/ARCHITECTURE.md +1658 -0
- package/vendor/agent-frontend-shell/BUGS.md +52 -0
- package/vendor/agent-frontend-shell/CHANGELOG.md +7295 -0
- package/vendor/agent-frontend-shell/CONTRIBUTING.md +205 -0
- package/vendor/agent-frontend-shell/CONTRIBUTORS.md +107 -0
- package/vendor/agent-frontend-shell/DESIGN.md +173 -0
- package/vendor/agent-frontend-shell/Dockerfile +91 -0
- package/vendor/agent-frontend-shell/LICENSE +21 -0
- package/vendor/agent-frontend-shell/README-CUSTOM.md +76 -0
- package/vendor/agent-frontend-shell/README.md +705 -0
- package/vendor/agent-frontend-shell/ROADMAP.md +351 -0
- package/vendor/agent-frontend-shell/SPRINTS.md +147 -0
- package/vendor/agent-frontend-shell/TESTING.md +1932 -0
- package/vendor/agent-frontend-shell/THEMES.md +170 -0
- package/vendor/agent-frontend-shell/api/__init__.py +1 -0
- package/vendor/agent-frontend-shell/api/agent_health.py +392 -0
- package/vendor/agent-frontend-shell/api/agent_sessions.py +782 -0
- package/vendor/agent-frontend-shell/api/auth.py +592 -0
- package/vendor/agent-frontend-shell/api/background.py +87 -0
- package/vendor/agent-frontend-shell/api/clarify.py +238 -0
- package/vendor/agent-frontend-shell/api/commands.py +124 -0
- package/vendor/agent-frontend-shell/api/compression_anchor.py +134 -0
- package/vendor/agent-frontend-shell/api/config.py +5178 -0
- package/vendor/agent-frontend-shell/api/dashboard_probe.py +255 -0
- package/vendor/agent-frontend-shell/api/extensions.py +253 -0
- package/vendor/agent-frontend-shell/api/gateway_chat.py +435 -0
- package/vendor/agent-frontend-shell/api/gateway_watcher.py +230 -0
- package/vendor/agent-frontend-shell/api/goals.py +608 -0
- package/vendor/agent-frontend-shell/api/helpers.py +474 -0
- package/vendor/agent-frontend-shell/api/kanban_bridge.py +1255 -0
- package/vendor/agent-frontend-shell/api/metering.py +194 -0
- package/vendor/agent-frontend-shell/api/models.py +4210 -0
- package/vendor/agent-frontend-shell/api/oauth.py +770 -0
- package/vendor/agent-frontend-shell/api/onboarding.py +1046 -0
- package/vendor/agent-frontend-shell/api/passkeys.py +365 -0
- package/vendor/agent-frontend-shell/api/profiles.py +1499 -0
- package/vendor/agent-frontend-shell/api/providers.py +2175 -0
- package/vendor/agent-frontend-shell/api/request_diagnostics.py +160 -0
- package/vendor/agent-frontend-shell/api/rollback.py +320 -0
- package/vendor/agent-frontend-shell/api/routes.py +13990 -0
- package/vendor/agent-frontend-shell/api/run_journal.py +284 -0
- package/vendor/agent-frontend-shell/api/runner_client.py +156 -0
- package/vendor/agent-frontend-shell/api/runtime_adapter.py +431 -0
- package/vendor/agent-frontend-shell/api/session_discoverability.py +640 -0
- package/vendor/agent-frontend-shell/api/session_events.py +45 -0
- package/vendor/agent-frontend-shell/api/session_lifecycle.py +208 -0
- package/vendor/agent-frontend-shell/api/session_ops.py +207 -0
- package/vendor/agent-frontend-shell/api/session_recovery.py +655 -0
- package/vendor/agent-frontend-shell/api/skill_usage.py +32 -0
- package/vendor/agent-frontend-shell/api/startup.py +128 -0
- package/vendor/agent-frontend-shell/api/state_sync.py +187 -0
- package/vendor/agent-frontend-shell/api/streaming.py +7048 -0
- package/vendor/agent-frontend-shell/api/system_health.py +167 -0
- package/vendor/agent-frontend-shell/api/terminal.py +410 -0
- package/vendor/agent-frontend-shell/api/turn_journal.py +214 -0
- package/vendor/agent-frontend-shell/api/updates.py +1261 -0
- package/vendor/agent-frontend-shell/api/upload.py +322 -0
- package/vendor/agent-frontend-shell/api/usage.py +26 -0
- package/vendor/agent-frontend-shell/api/workspace.py +867 -0
- package/vendor/agent-frontend-shell/api/workspace_git.py +1261 -0
- package/vendor/agent-frontend-shell/api/worktrees.py +357 -0
- package/vendor/agent-frontend-shell/bootstrap.py +492 -0
- package/vendor/agent-frontend-shell/ctl.sh +427 -0
- package/vendor/agent-frontend-shell/docker-compose.custom.yml +26 -0
- package/vendor/agent-frontend-shell/docker-compose.three-container.yml +168 -0
- package/vendor/agent-frontend-shell/docker-compose.two-container.yml +147 -0
- package/vendor/agent-frontend-shell/docker-compose.yml +57 -0
- package/vendor/agent-frontend-shell/docker_init.bash +459 -0
- package/vendor/agent-frontend-shell/docs/CONTRACTS.md +207 -0
- package/vendor/agent-frontend-shell/docs/EXTENSIONS.md +212 -0
- package/vendor/agent-frontend-shell/docs/ISSUES.md +23 -0
- package/vendor/agent-frontend-shell/docs/UIUX-GUIDE.md +196 -0
- package/vendor/agent-frontend-shell/docs/advanced-chat-setup.md +83 -0
- package/vendor/agent-frontend-shell/docs/docker.md +337 -0
- package/vendor/agent-frontend-shell/docs/onboarding-agent-checklist.md +207 -0
- package/vendor/agent-frontend-shell/docs/onboarding.md +202 -0
- package/vendor/agent-frontend-shell/docs/remote-access.md +75 -0
- package/vendor/agent-frontend-shell/docs/rfcs/README.md +53 -0
- package/vendor/agent-frontend-shell/docs/rfcs/agent-source-boundary.md +70 -0
- package/vendor/agent-frontend-shell/docs/rfcs/canonical-session-resolution.md +124 -0
- package/vendor/agent-frontend-shell/docs/rfcs/hermes-run-adapter-contract.md +1079 -0
- package/vendor/agent-frontend-shell/docs/rfcs/turn-journal.md +195 -0
- package/vendor/agent-frontend-shell/docs/rfcs/webui-run-state-consistency-contract.md +157 -0
- package/vendor/agent-frontend-shell/docs/supervisor.md +280 -0
- package/vendor/agent-frontend-shell/docs/troubleshooting.md +132 -0
- package/vendor/agent-frontend-shell/docs/ui-ux/index.html +863 -0
- package/vendor/agent-frontend-shell/docs/ui-ux/two-stage-proposal.html +768 -0
- package/vendor/agent-frontend-shell/docs/why-hermes.md +489 -0
- package/vendor/agent-frontend-shell/docs/workspace-git.md +92 -0
- package/vendor/agent-frontend-shell/docs/wsl-autostart.md +126 -0
- package/vendor/agent-frontend-shell/eslint.runtime-guard.config.mjs +35 -0
- package/vendor/agent-frontend-shell/extensions/bitseek-design-system.md +330 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/apple-touch-icon.png +0 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/empty-logo.svg +739 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-192.png +0 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-32.png +0 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-512.png +0 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-512.svg +745 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/favicon.ico +0 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/favicon.svg +745 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/titlebar-icon-v2.svg +751 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/titlebar-icon-v3.svg +739 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/titlebar-icon.svg +745 -0
- package/vendor/agent-frontend-shell/extensions/branding/branding.js +112 -0
- package/vendor/agent-frontend-shell/extensions/branding/config.json +14 -0
- package/vendor/agent-frontend-shell/extensions/branding/manifest.json +53 -0
- package/vendor/agent-frontend-shell/extensions/index.js +67 -0
- package/vendor/agent-frontend-shell/extensions/loader/hermes-loader.js +77 -0
- package/vendor/agent-frontend-shell/extensions/manifest.json +16 -0
- package/vendor/agent-frontend-shell/extensions/pages/ai-teammates/page.css +333 -0
- package/vendor/agent-frontend-shell/extensions/pages/ai-teammates/page.js +487 -0
- package/vendor/agent-frontend-shell/extensions/pages/manifest.json +6 -0
- package/vendor/agent-frontend-shell/extensions/pages/registry.css +56 -0
- package/vendor/agent-frontend-shell/extensions/pages/registry.js +302 -0
- package/vendor/agent-frontend-shell/extensions/themes/bitseek/index.css +93 -0
- package/vendor/agent-frontend-shell/extensions/themes/bitseek/index.js +98 -0
- package/vendor/agent-frontend-shell/install.sh +63 -0
- package/vendor/agent-frontend-shell/mcp_server.py +567 -0
- package/vendor/agent-frontend-shell/package.json +12 -0
- package/vendor/agent-frontend-shell/pyproject.toml +56 -0
- package/vendor/agent-frontend-shell/pytest.ini +3 -0
- package/vendor/agent-frontend-shell/requirements.txt +5 -0
- package/vendor/agent-frontend-shell/server.py +624 -0
- package/vendor/agent-frontend-shell/start.ps1 +210 -0
- package/vendor/agent-frontend-shell/start.sh +65 -0
- package/vendor/agent-frontend-shell/static/apple-touch-icon.png +0 -0
- package/vendor/agent-frontend-shell/static/boot.js +1990 -0
- package/vendor/agent-frontend-shell/static/commands.js +1402 -0
- package/vendor/agent-frontend-shell/static/favicon-192.png +0 -0
- package/vendor/agent-frontend-shell/static/favicon-32.png +0 -0
- package/vendor/agent-frontend-shell/static/favicon-512.png +0 -0
- package/vendor/agent-frontend-shell/static/favicon-512.svg +18 -0
- package/vendor/agent-frontend-shell/static/favicon.ico +0 -0
- package/vendor/agent-frontend-shell/static/favicon.svg +20 -0
- package/vendor/agent-frontend-shell/static/i18n.js +15389 -0
- package/vendor/agent-frontend-shell/static/icons.js +92 -0
- package/vendor/agent-frontend-shell/static/index.html +1506 -0
- package/vendor/agent-frontend-shell/static/login.js +177 -0
- package/vendor/agent-frontend-shell/static/manifest.json +53 -0
- package/vendor/agent-frontend-shell/static/messages.js +3521 -0
- package/vendor/agent-frontend-shell/static/onboarding.js +800 -0
- package/vendor/agent-frontend-shell/static/panels.js +7995 -0
- package/vendor/agent-frontend-shell/static/pwa-startup.js +83 -0
- package/vendor/agent-frontend-shell/static/sessions.js +5165 -0
- package/vendor/agent-frontend-shell/static/style.css +4774 -0
- package/vendor/agent-frontend-shell/static/sw.js +173 -0
- package/vendor/agent-frontend-shell/static/terminal.js +632 -0
- package/vendor/agent-frontend-shell/static/ui.js +8997 -0
- package/vendor/agent-frontend-shell/static/vendor/js-yaml/4.1.0/js-yaml.min.js +2 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_AMS-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_AMS-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Bold.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Bold.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Italic.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Italic.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-Italic.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-Italic.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Script-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Script-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size1-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size1-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size2-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size2-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size3-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size3-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size4-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size4-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/katex.min.css +1 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/katex.min.js +1 -0
- package/vendor/agent-frontend-shell/static/vendor/smd.min.js +29 -0
- 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,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,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
|