HERMES-GIELIE/Dockerfile

92 lines
3.1 KiB
Docker
Raw Normal View History

feat: Sprint 21 — mobile responsive layout + Docker support Mobile responsive (Issue #21): - Hamburger sidebar: slide-in overlay on mobile (<640px) with backdrop. Tap hamburger in topbar to open, tap outside to close. Full session list, project chips, all panel content accessible. - Bottom navigation bar: 5-tab fixed bar (Chat, Tasks, Skills, Memory, Spaces) replaces sidebar nav tabs on mobile. iOS-style layout. Tapping a tab opens the sidebar overlay with that panel active. - Right panel slide-over: Files button in topbar chips opens workspace panel as a slide-over from the right on mobile/tablet. - Touch targets: all interactive elements get min 44x44px touch areas. Session items, approval buttons, composer buttons all sized for fingers. - Composer positioned above bottom nav bar with proper spacing. - Sidebar nav tabs and bottom section hidden on mobile (replaced by bottom nav + topbar chips). - Clicking a session auto-closes the sidebar overlay. - Desktop layout completely unchanged — all mobile elements are display:none by default, only shown inside @media(max-width:640px). Docker (Issue #7): - Dockerfile: python:3.12-slim, HERMES_WEBUI_HOST=0.0.0.0, port 8787. - docker-compose.yml: named volume for state persistence, optional ~/.hermes mount for agent features, password env var documented. - README: Docker quick start section with compose and manual commands. Tests: 392 passed, 23 pre-existing failures, 0 regressions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 17:09:36 +00:00
FROM python:3.12-slim
LABEL maintainer="nesquena"
LABEL description="Hermes Web UI — browser interface for Hermes Agent"
v0.46.0: security, Docker UID/GID, model discovery, i18n, cancel fix * fix: decode HTML entities before markdown processing + zh/zh-Hant translations (#239) Adds decode() helper in renderMd() to fix double-escaping of HTML entities from LLM output (e.g. &lt;code&gt; becoming &amp;lt;code&amp;gt; instead of rendering). XSS-safe: decode runs before esc(), only 5 entity patterns. Also adds 40+ missing zh (Simplified Chinese) translation keys and a new zh-Hant (Traditional Chinese) locale with 163 keys. Fix applied: removed duplicate settings_label_notifications key in both zh and zh-Hant locales. Fixes #240 * fix: restore custom model list discovery with config api key (#238) get_available_models() now reads api_key from config.yaml before env vars: 1. model.api_key 2. providers.<active>.api_key / providers.custom.api_key 3. env var fallbacks (HERMES_API_KEY, OPENAI_API_KEY, etc.) Also adds OpenAI/Python User-Agent header and a regression test covering authenticated /v1/models discovery. Fixes users with LM Studio / Ollama custom endpoints configured in config.yaml whose model picker silently collapsed to the default model. * feat: Docker UID/GID matching to avoid root-owned .hermes files (#237) Adds docker_init.bash with hermeswebuitoo/hermeswebui user pattern so container files match the host user UID/GID. Prevents .hermes volume mounts from being owned by root when using a non-root host user. Configure via WANTED_UID and WANTED_GID env vars (default 1000/1000). Readme updated with setup instructions. Fix applied: removed duplicate WANTED_GID=1000 line in docker-compose.yml that was overriding the ${GID:-1000} variable expansion. * security: redact credentials from API responses and fix credential file permissions (#243) Adds response-layer credential redaction to three endpoints: - GET /api/session — messages[], tool_calls[], and title - GET /api/session/export — download also redacted - SSE done event — session payload in stream - GET /api/memory — MEMORY.md and USER.md content Adds api/startup.py with fix_credential_permissions() at server startup. Adds 13 tests in tests/test_security_redaction.py. Merged with #237 container detection changes in server.py. * fix: cancel button now interrupts agent and cleans up UI state (#244) Wires agent.interrupt() into cancel_stream() so the backend actually stops tool execution when the user clicks Cancel, rather than only stopping the SSE stream while the agent keeps running. Changes: - api/config.py: adds AGENT_INSTANCES dict (stream_id -> AIAgent) - api/streaming.py: stores agent in AGENT_INSTANCES after creation, checks CANCEL_FLAGS immediately after store (race condition fix), calls agent.interrupt() in cancel_stream(), cleans up in finally block - static/boot.js: removes stale setStatus(cancelling) call - static/messages.js: setBusy(false)/setStatus('') unconditionally on cancel Race condition fix: after storing agent in AGENT_INSTANCES, immediately checks if CANCEL_FLAGS[stream_id] is already set (cancel arrived during agent init) and interrupts before starting. Check is inside the same STREAMS_LOCK acquisition, making it atomic. New test file: tests/test_cancel_interrupt.py with 6 unit tests. * docs: v0.46.0 release notes, bump version, update test counts --------- Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
2026-04-11 17:17:52 +00:00
# Install system packages
ENV DEBIAN_FRONTEND=noninteractive
feat: Sprint 21 — mobile responsive layout + Docker support Mobile responsive (Issue #21): - Hamburger sidebar: slide-in overlay on mobile (<640px) with backdrop. Tap hamburger in topbar to open, tap outside to close. Full session list, project chips, all panel content accessible. - Bottom navigation bar: 5-tab fixed bar (Chat, Tasks, Skills, Memory, Spaces) replaces sidebar nav tabs on mobile. iOS-style layout. Tapping a tab opens the sidebar overlay with that panel active. - Right panel slide-over: Files button in topbar chips opens workspace panel as a slide-over from the right on mobile/tablet. - Touch targets: all interactive elements get min 44x44px touch areas. Session items, approval buttons, composer buttons all sized for fingers. - Composer positioned above bottom nav bar with proper spacing. - Sidebar nav tabs and bottom section hidden on mobile (replaced by bottom nav + topbar chips). - Clicking a session auto-closes the sidebar overlay. - Desktop layout completely unchanged — all mobile elements are display:none by default, only shown inside @media(max-width:640px). Docker (Issue #7): - Dockerfile: python:3.12-slim, HERMES_WEBUI_HOST=0.0.0.0, port 8787. - docker-compose.yml: named volume for state persistence, optional ~/.hermes mount for agent features, password env var documented. - README: Docker quick start section with compose and manual commands. Tests: 392 passed, 23 pre-existing failures, 0 regressions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 17:09:36 +00:00
v0.46.0: security, Docker UID/GID, model discovery, i18n, cancel fix * fix: decode HTML entities before markdown processing + zh/zh-Hant translations (#239) Adds decode() helper in renderMd() to fix double-escaping of HTML entities from LLM output (e.g. &lt;code&gt; becoming &amp;lt;code&amp;gt; instead of rendering). XSS-safe: decode runs before esc(), only 5 entity patterns. Also adds 40+ missing zh (Simplified Chinese) translation keys and a new zh-Hant (Traditional Chinese) locale with 163 keys. Fix applied: removed duplicate settings_label_notifications key in both zh and zh-Hant locales. Fixes #240 * fix: restore custom model list discovery with config api key (#238) get_available_models() now reads api_key from config.yaml before env vars: 1. model.api_key 2. providers.<active>.api_key / providers.custom.api_key 3. env var fallbacks (HERMES_API_KEY, OPENAI_API_KEY, etc.) Also adds OpenAI/Python User-Agent header and a regression test covering authenticated /v1/models discovery. Fixes users with LM Studio / Ollama custom endpoints configured in config.yaml whose model picker silently collapsed to the default model. * feat: Docker UID/GID matching to avoid root-owned .hermes files (#237) Adds docker_init.bash with hermeswebuitoo/hermeswebui user pattern so container files match the host user UID/GID. Prevents .hermes volume mounts from being owned by root when using a non-root host user. Configure via WANTED_UID and WANTED_GID env vars (default 1000/1000). Readme updated with setup instructions. Fix applied: removed duplicate WANTED_GID=1000 line in docker-compose.yml that was overriding the ${GID:-1000} variable expansion. * security: redact credentials from API responses and fix credential file permissions (#243) Adds response-layer credential redaction to three endpoints: - GET /api/session — messages[], tool_calls[], and title - GET /api/session/export — download also redacted - SSE done event — session payload in stream - GET /api/memory — MEMORY.md and USER.md content Adds api/startup.py with fix_credential_permissions() at server startup. Adds 13 tests in tests/test_security_redaction.py. Merged with #237 container detection changes in server.py. * fix: cancel button now interrupts agent and cleans up UI state (#244) Wires agent.interrupt() into cancel_stream() so the backend actually stops tool execution when the user clicks Cancel, rather than only stopping the SSE stream while the agent keeps running. Changes: - api/config.py: adds AGENT_INSTANCES dict (stream_id -> AIAgent) - api/streaming.py: stores agent in AGENT_INSTANCES after creation, checks CANCEL_FLAGS immediately after store (race condition fix), calls agent.interrupt() in cancel_stream(), cleans up in finally block - static/boot.js: removes stale setStatus(cancelling) call - static/messages.js: setBusy(false)/setStatus('') unconditionally on cancel Race condition fix: after storing agent in AGENT_INSTANCES, immediately checks if CANCEL_FLAGS[stream_id] is already set (cancel arrived during agent init) and interrupts before starting. Check is inside the same STREAMS_LOCK acquisition, making it atomic. New test file: tests/test_cancel_interrupt.py with 6 unit tests. * docs: v0.46.0 release notes, bump version, update test counts --------- Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
2026-04-11 17:17:52 +00:00
# Make use of apt-cacher-ng if available
RUN if [ "A${BUILD_APT_PROXY:-}" != "A" ]; then \
echo "Using APT proxy: ${BUILD_APT_PROXY}"; \
printf 'Acquire::http::Proxy "%s";\n' "$BUILD_APT_PROXY" > /etc/apt/apt.conf.d/01proxy; \
fi \
&& apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates wget gnupg \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
feat: Sprint 21 — mobile responsive layout + Docker support Mobile responsive (Issue #21): - Hamburger sidebar: slide-in overlay on mobile (<640px) with backdrop. Tap hamburger in topbar to open, tap outside to close. Full session list, project chips, all panel content accessible. - Bottom navigation bar: 5-tab fixed bar (Chat, Tasks, Skills, Memory, Spaces) replaces sidebar nav tabs on mobile. iOS-style layout. Tapping a tab opens the sidebar overlay with that panel active. - Right panel slide-over: Files button in topbar chips opens workspace panel as a slide-over from the right on mobile/tablet. - Touch targets: all interactive elements get min 44x44px touch areas. Session items, approval buttons, composer buttons all sized for fingers. - Composer positioned above bottom nav bar with proper spacing. - Sidebar nav tabs and bottom section hidden on mobile (replaced by bottom nav + topbar chips). - Clicking a session auto-closes the sidebar overlay. - Desktop layout completely unchanged — all mobile elements are display:none by default, only shown inside @media(max-width:640px). Docker (Issue #7): - Dockerfile: python:3.12-slim, HERMES_WEBUI_HOST=0.0.0.0, port 8787. - docker-compose.yml: named volume for state persistence, optional ~/.hermes mount for agent features, password env var documented. - README: Docker quick start section with compose and manual commands. Tests: 392 passed, 23 pre-existing failures, 0 regressions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 17:09:36 +00:00
v0.46.0: security, Docker UID/GID, model discovery, i18n, cancel fix * fix: decode HTML entities before markdown processing + zh/zh-Hant translations (#239) Adds decode() helper in renderMd() to fix double-escaping of HTML entities from LLM output (e.g. &lt;code&gt; becoming &amp;lt;code&amp;gt; instead of rendering). XSS-safe: decode runs before esc(), only 5 entity patterns. Also adds 40+ missing zh (Simplified Chinese) translation keys and a new zh-Hant (Traditional Chinese) locale with 163 keys. Fix applied: removed duplicate settings_label_notifications key in both zh and zh-Hant locales. Fixes #240 * fix: restore custom model list discovery with config api key (#238) get_available_models() now reads api_key from config.yaml before env vars: 1. model.api_key 2. providers.<active>.api_key / providers.custom.api_key 3. env var fallbacks (HERMES_API_KEY, OPENAI_API_KEY, etc.) Also adds OpenAI/Python User-Agent header and a regression test covering authenticated /v1/models discovery. Fixes users with LM Studio / Ollama custom endpoints configured in config.yaml whose model picker silently collapsed to the default model. * feat: Docker UID/GID matching to avoid root-owned .hermes files (#237) Adds docker_init.bash with hermeswebuitoo/hermeswebui user pattern so container files match the host user UID/GID. Prevents .hermes volume mounts from being owned by root when using a non-root host user. Configure via WANTED_UID and WANTED_GID env vars (default 1000/1000). Readme updated with setup instructions. Fix applied: removed duplicate WANTED_GID=1000 line in docker-compose.yml that was overriding the ${GID:-1000} variable expansion. * security: redact credentials from API responses and fix credential file permissions (#243) Adds response-layer credential redaction to three endpoints: - GET /api/session — messages[], tool_calls[], and title - GET /api/session/export — download also redacted - SSE done event — session payload in stream - GET /api/memory — MEMORY.md and USER.md content Adds api/startup.py with fix_credential_permissions() at server startup. Adds 13 tests in tests/test_security_redaction.py. Merged with #237 container detection changes in server.py. * fix: cancel button now interrupts agent and cleans up UI state (#244) Wires agent.interrupt() into cancel_stream() so the backend actually stops tool execution when the user clicks Cancel, rather than only stopping the SSE stream while the agent keeps running. Changes: - api/config.py: adds AGENT_INSTANCES dict (stream_id -> AIAgent) - api/streaming.py: stores agent in AGENT_INSTANCES after creation, checks CANCEL_FLAGS immediately after store (race condition fix), calls agent.interrupt() in cancel_stream(), cleans up in finally block - static/boot.js: removes stale setStatus(cancelling) call - static/messages.js: setBusy(false)/setStatus('') unconditionally on cancel Race condition fix: after storing agent in AGENT_INSTANCES, immediately checks if CANCEL_FLAGS[stream_id] is already set (cancel arrived during agent init) and interrupts before starting. Check is inside the same STREAMS_LOCK acquisition, making it atomic. New test file: tests/test_cancel_interrupt.py with 6 unit tests. * docs: v0.46.0 release notes, bump version, update test counts --------- Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
2026-04-11 17:17:52 +00:00
RUN apt-get update -y --fix-missing --no-install-recommends \
&& apt-get install -y --no-install-recommends \
apt-utils \
locales \
ca-certificates \
curl \
rsync \
openssh-client \
fix(docker): salvage operational hardening from #1686 (env readonly + apt deps) Three independent operational hardening fixes salvaged from PR #1686 (@binhpt310) after the parent PR was deferred over a separate sibling-repo build-context concern unrelated to these fixes: 1. start.sh's .env loader now filters readonly bash vars (UID, GID, EUID, EGID, PPID) before `source`-ing. docker-compose.yml's macOS instructions document `echo "UID=$(id -u)" >> .env` to set host UID/GID for bind-mount permission fixing — that .env was crashing start.sh with `UID: readonly variable` when `set -a; source ...; set +a` tried to assign to those names. Replaced with `source <(grep -vE '^[[:space:]]*(export[[:space:]]+)?(UID|GID|EUID|EGID|PPID)=' "${REPO_ROOT}/.env")`. The bootstrap regression guard at tests/test_bootstrap_dotenv.py:181 still passes — both `source` and `.env` are still on the modified line. 2. start.sh now defensively re-execs as the unprivileged hermeswebui user when invoked as root. Fires only when EUID==0 AND a hermeswebui user actually exists AND sudo is on PATH — so it's a no-op on host machines without the container user setup. The production image's entrypoint (docker_init.bash) already drops to hermeswebui before invoking start.sh, so this is a no-op on the canonical container path; it only matters for `sudo ./start.sh` or accidental root shells inside the container during interactive debugging. 3. Dockerfile installs xz-utils + git apt packages. xz-utils is required to decompress .tar.xz archives (e.g. Node.js distribution tarballs); git is needed for `git describe` (powers WEBUI_VERSION resolution at api/updates.py:_detect_webui_version) and any clone-based agent install path. Both are tiny apt packages on top of python:3.12-slim with no measurable image-size impact. What's NOT in this commit (deferred from #1686): - Pre-baking hermes-agent source into the image via `COPY hermes-agent-desktop/hermes-agent /opt/hermes/` plus a build-context flip to `..`. Requires a sibling-repo layout that breaks the canonical `git clone hermes-webui && cd hermes-webui && docker compose build` flow. The right shape is a build arg gating the COPY behind --build-arg WITH_AGENT_SOURCE=1; left to a separate PR. - Pre-installing Node.js 22 LTS system-wide. Real motivation but worth evaluating the fix shape (full Node bake vs. opt-in vs. layer cache) separately from these three operational fixes. Tests: tests/test_docker_env_readonly_vars.py — 11 tests (4 source-grep on the start.sh filter pattern + 5 behavioral that actually run bash against synthetic .env files containing readonly vars + 2 Dockerfile package-presence tests). All 11 pass. Behavioral tests skip if bash is not on PATH. Full suite: 5028 → 5036 passing (+8 net new after pytest collection counted some behavioral tests under skip), 0 regressions, 147.84s. Closes the operational-hardening portion of #1686. Co-authored-by: binhpt310 <binhpt310@users.noreply.github.com>
2026-05-09 19:17:34 +00:00
git \
xz-utils \
v0.46.0: security, Docker UID/GID, model discovery, i18n, cancel fix * fix: decode HTML entities before markdown processing + zh/zh-Hant translations (#239) Adds decode() helper in renderMd() to fix double-escaping of HTML entities from LLM output (e.g. &lt;code&gt; becoming &amp;lt;code&amp;gt; instead of rendering). XSS-safe: decode runs before esc(), only 5 entity patterns. Also adds 40+ missing zh (Simplified Chinese) translation keys and a new zh-Hant (Traditional Chinese) locale with 163 keys. Fix applied: removed duplicate settings_label_notifications key in both zh and zh-Hant locales. Fixes #240 * fix: restore custom model list discovery with config api key (#238) get_available_models() now reads api_key from config.yaml before env vars: 1. model.api_key 2. providers.<active>.api_key / providers.custom.api_key 3. env var fallbacks (HERMES_API_KEY, OPENAI_API_KEY, etc.) Also adds OpenAI/Python User-Agent header and a regression test covering authenticated /v1/models discovery. Fixes users with LM Studio / Ollama custom endpoints configured in config.yaml whose model picker silently collapsed to the default model. * feat: Docker UID/GID matching to avoid root-owned .hermes files (#237) Adds docker_init.bash with hermeswebuitoo/hermeswebui user pattern so container files match the host user UID/GID. Prevents .hermes volume mounts from being owned by root when using a non-root host user. Configure via WANTED_UID and WANTED_GID env vars (default 1000/1000). Readme updated with setup instructions. Fix applied: removed duplicate WANTED_GID=1000 line in docker-compose.yml that was overriding the ${GID:-1000} variable expansion. * security: redact credentials from API responses and fix credential file permissions (#243) Adds response-layer credential redaction to three endpoints: - GET /api/session — messages[], tool_calls[], and title - GET /api/session/export — download also redacted - SSE done event — session payload in stream - GET /api/memory — MEMORY.md and USER.md content Adds api/startup.py with fix_credential_permissions() at server startup. Adds 13 tests in tests/test_security_redaction.py. Merged with #237 container detection changes in server.py. * fix: cancel button now interrupts agent and cleans up UI state (#244) Wires agent.interrupt() into cancel_stream() so the backend actually stops tool execution when the user clicks Cancel, rather than only stopping the SSE stream while the agent keeps running. Changes: - api/config.py: adds AGENT_INSTANCES dict (stream_id -> AIAgent) - api/streaming.py: stores agent in AGENT_INSTANCES after creation, checks CANCEL_FLAGS immediately after store (race condition fix), calls agent.interrupt() in cancel_stream(), cleans up in finally block - static/boot.js: removes stale setStatus(cancelling) call - static/messages.js: setBusy(false)/setStatus('') unconditionally on cancel Race condition fix: after storing agent in AGENT_INSTANCES, immediately checks if CANCEL_FLAGS[stream_id] is already set (cancel arrived during agent init) and interrupts before starting. Check is inside the same STREAMS_LOCK acquisition, making it atomic. New test file: tests/test_cancel_interrupt.py with 6 unit tests. * docs: v0.46.0 release notes, bump version, update test counts --------- Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
2026-04-11 17:17:52 +00:00
&& apt-get upgrade -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# UTF-8
RUN localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
ENV LANG=en_US.utf8
ENV LC_ALL=C
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONIOENCODING=utf-8
WORKDIR /apptoo
# Create the unprivileged runtime user. The entrypoint starts as root only for
# UID/GID alignment and filesystem preparation, then execs the server as this user.
RUN groupadd -g 1024 hermeswebui \
&& useradd -u 1024 -d /home/hermeswebui -g hermeswebui -G users -s /bin/bash -m hermeswebui \
&& mkdir -p /app /uv_cache /workspace \
&& chown -R hermeswebui:hermeswebui /home/hermeswebui /app /uv_cache /workspace \
&& chmod 0755 /home/hermeswebui \
&& chmod 1777 /app /uv_cache /workspace
v0.46.0: security, Docker UID/GID, model discovery, i18n, cancel fix * fix: decode HTML entities before markdown processing + zh/zh-Hant translations (#239) Adds decode() helper in renderMd() to fix double-escaping of HTML entities from LLM output (e.g. &lt;code&gt; becoming &amp;lt;code&amp;gt; instead of rendering). XSS-safe: decode runs before esc(), only 5 entity patterns. Also adds 40+ missing zh (Simplified Chinese) translation keys and a new zh-Hant (Traditional Chinese) locale with 163 keys. Fix applied: removed duplicate settings_label_notifications key in both zh and zh-Hant locales. Fixes #240 * fix: restore custom model list discovery with config api key (#238) get_available_models() now reads api_key from config.yaml before env vars: 1. model.api_key 2. providers.<active>.api_key / providers.custom.api_key 3. env var fallbacks (HERMES_API_KEY, OPENAI_API_KEY, etc.) Also adds OpenAI/Python User-Agent header and a regression test covering authenticated /v1/models discovery. Fixes users with LM Studio / Ollama custom endpoints configured in config.yaml whose model picker silently collapsed to the default model. * feat: Docker UID/GID matching to avoid root-owned .hermes files (#237) Adds docker_init.bash with hermeswebuitoo/hermeswebui user pattern so container files match the host user UID/GID. Prevents .hermes volume mounts from being owned by root when using a non-root host user. Configure via WANTED_UID and WANTED_GID env vars (default 1000/1000). Readme updated with setup instructions. Fix applied: removed duplicate WANTED_GID=1000 line in docker-compose.yml that was overriding the ${GID:-1000} variable expansion. * security: redact credentials from API responses and fix credential file permissions (#243) Adds response-layer credential redaction to three endpoints: - GET /api/session — messages[], tool_calls[], and title - GET /api/session/export — download also redacted - SSE done event — session payload in stream - GET /api/memory — MEMORY.md and USER.md content Adds api/startup.py with fix_credential_permissions() at server startup. Adds 13 tests in tests/test_security_redaction.py. Merged with #237 container detection changes in server.py. * fix: cancel button now interrupts agent and cleans up UI state (#244) Wires agent.interrupt() into cancel_stream() so the backend actually stops tool execution when the user clicks Cancel, rather than only stopping the SSE stream while the agent keeps running. Changes: - api/config.py: adds AGENT_INSTANCES dict (stream_id -> AIAgent) - api/streaming.py: stores agent in AGENT_INSTANCES after creation, checks CANCEL_FLAGS immediately after store (race condition fix), calls agent.interrupt() in cancel_stream(), cleans up in finally block - static/boot.js: removes stale setStatus(cancelling) call - static/messages.js: setBusy(false)/setStatus('') unconditionally on cancel Race condition fix: after storing agent in AGENT_INSTANCES, immediately checks if CANCEL_FLAGS[stream_id] is already set (cancel arrived during agent init) and interrupts before starting. Check is inside the same STREAMS_LOCK acquisition, making it atomic. New test file: tests/test_cancel_interrupt.py with 6 unit tests. * docs: v0.46.0 release notes, bump version, update test counts --------- Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
2026-04-11 17:17:52 +00:00
COPY --chmod=555 docker_init.bash /hermeswebui_init.bash
RUN touch /.within_container
# Remove APT proxy configuration and clean up APT downloaded files
RUN rm -rf /var/lib/apt/lists/* /etc/apt/apt.conf.d/01proxy \
&& apt-get clean
USER root
# Pre-install uv system-wide so the container doesn't need internet access at runtime.
# Installing as root places uv in /usr/local/bin, available to all users.
# The init script will skip the download when uv is already on PATH.
RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh
COPY --chown=root:root . /apptoo
fix: dynamic version badge — read from git tag, never hardcoded (#790) * fix: dynamic version badge — read from git tag, never hardcoded The settings panel showed v0.50.87 and the HTTP Server: header said HermesWebUI/0.50.38 — both hardcoded strings that drift further behind with every release because there was no mechanism to keep them in sync. Changes: - api/updates.py: add _run_git() (moved before _detect_webui_version), _detect_webui_version(), and WEBUI_VERSION module constant resolved once at import time via 'git describe --tags --always --dirty'. Fallback chain: git → api/_version.py → 'unknown'. - api/routes.py: inject webui_version into GET /api/settings response so the frontend can read it without a separate API call. - static/panels.js: loadSettingsPanel() populates .settings-version-badge from settings.webui_version — one line after the existing api() call. - static/index.html: replace stale hardcoded 'v0.50.87' with '—' placeholder; JS overwrites it as soon as the settings panel opens. - server.py: replace hardcoded 'HermesWebUI/0.50.38' server_version with 'HermesWebUI/' + WEBUI_VERSION.lstrip('v') — stays in sync automatically. - Dockerfile: add ARG HERMES_VERSION=unknown and write api/_version.py so Docker images (where .git is excluded) still show the correct tag. - .github/workflows/release.yml: pass build-args: HERMES_VERSION=${{ github.ref_name }} to the Docker build step on tag pushes. - .gitignore: exclude api/_version.py (generated by Docker/CI, never committed). No manual 'update the version badge' step is required going forward. Tagging is sufficient — the badge and HTTP header update automatically. Tests: 18 new tests in tests/test_version_badge.py covering the full resolution chain, /api/settings injection, HTML placeholder, JS wiring, and server.py import. 1596 tests pass total. * fix: address review feedback on PR #790 - api/updates.py: replace exec() with regex parse for api/_version.py (no supply-chain risk from build artifact; exec unnecessary for one assignment) - api/updates.py: cap git describe timeout at 3s (was 10s — import-time stall on NFS/.git would block server startup unnecessarily) - server.py: lstrip('v') → removeprefix('v') (lstrip strips chars not prefix) - server.py: emit bare 'HermesWebUI' when version is 'unknown' rather than 'HermesWebUI/unknown' (log aggregators expect semver-ish suffix or none) - CHANGELOG.md: add v0.50.124 entry for this user-visible change - tests: rename exec-error test to reflect regex behaviour; add tests for removeprefix usage and unknown-version header guard (1598 tests total) --------- Co-authored-by: nesquena-hermes <hermes@nesquena.com>
2026-04-21 03:36:53 +00:00
# Bake the git version tag into the image so the settings badge works even
# when .git is not present (it is excluded by .dockerignore).
# CI passes: --build-arg HERMES_VERSION=$(git describe --tags --always)
# Local builds that omit the arg get "unknown" as the fallback.
ARG HERMES_VERSION=unknown
RUN echo "__version__ = '${HERMES_VERSION}'" > /apptoo/api/_version.py
feat: Sprint 21 — mobile responsive layout + Docker support Mobile responsive (Issue #21): - Hamburger sidebar: slide-in overlay on mobile (<640px) with backdrop. Tap hamburger in topbar to open, tap outside to close. Full session list, project chips, all panel content accessible. - Bottom navigation bar: 5-tab fixed bar (Chat, Tasks, Skills, Memory, Spaces) replaces sidebar nav tabs on mobile. iOS-style layout. Tapping a tab opens the sidebar overlay with that panel active. - Right panel slide-over: Files button in topbar chips opens workspace panel as a slide-over from the right on mobile/tablet. - Touch targets: all interactive elements get min 44x44px touch areas. Session items, approval buttons, composer buttons all sized for fingers. - Composer positioned above bottom nav bar with proper spacing. - Sidebar nav tabs and bottom section hidden on mobile (replaced by bottom nav + topbar chips). - Clicking a session auto-closes the sidebar overlay. - Desktop layout completely unchanged — all mobile elements are display:none by default, only shown inside @media(max-width:640px). Docker (Issue #7): - Dockerfile: python:3.12-slim, HERMES_WEBUI_HOST=0.0.0.0, port 8787. - docker-compose.yml: named volume for state persistence, optional ~/.hermes mount for agent features, password env var documented. - README: Docker quick start section with compose and manual commands. Tests: 392 passed, 23 pre-existing failures, 0 regressions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 17:09:36 +00:00
# Default to binding all interfaces (required for container networking)
ENV HERMES_WEBUI_HOST=0.0.0.0
ENV HERMES_WEBUI_PORT=8787
EXPOSE 8787
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8787/health || exit 1
# docker_init.bash performs root-only bind-mount setup, then drops to hermeswebui
# before starting the WebUI server. The production image does not ship sudo.
USER root
v0.46.0: security, Docker UID/GID, model discovery, i18n, cancel fix * fix: decode HTML entities before markdown processing + zh/zh-Hant translations (#239) Adds decode() helper in renderMd() to fix double-escaping of HTML entities from LLM output (e.g. &lt;code&gt; becoming &amp;lt;code&amp;gt; instead of rendering). XSS-safe: decode runs before esc(), only 5 entity patterns. Also adds 40+ missing zh (Simplified Chinese) translation keys and a new zh-Hant (Traditional Chinese) locale with 163 keys. Fix applied: removed duplicate settings_label_notifications key in both zh and zh-Hant locales. Fixes #240 * fix: restore custom model list discovery with config api key (#238) get_available_models() now reads api_key from config.yaml before env vars: 1. model.api_key 2. providers.<active>.api_key / providers.custom.api_key 3. env var fallbacks (HERMES_API_KEY, OPENAI_API_KEY, etc.) Also adds OpenAI/Python User-Agent header and a regression test covering authenticated /v1/models discovery. Fixes users with LM Studio / Ollama custom endpoints configured in config.yaml whose model picker silently collapsed to the default model. * feat: Docker UID/GID matching to avoid root-owned .hermes files (#237) Adds docker_init.bash with hermeswebuitoo/hermeswebui user pattern so container files match the host user UID/GID. Prevents .hermes volume mounts from being owned by root when using a non-root host user. Configure via WANTED_UID and WANTED_GID env vars (default 1000/1000). Readme updated with setup instructions. Fix applied: removed duplicate WANTED_GID=1000 line in docker-compose.yml that was overriding the ${GID:-1000} variable expansion. * security: redact credentials from API responses and fix credential file permissions (#243) Adds response-layer credential redaction to three endpoints: - GET /api/session — messages[], tool_calls[], and title - GET /api/session/export — download also redacted - SSE done event — session payload in stream - GET /api/memory — MEMORY.md and USER.md content Adds api/startup.py with fix_credential_permissions() at server startup. Adds 13 tests in tests/test_security_redaction.py. Merged with #237 container detection changes in server.py. * fix: cancel button now interrupts agent and cleans up UI state (#244) Wires agent.interrupt() into cancel_stream() so the backend actually stops tool execution when the user clicks Cancel, rather than only stopping the SSE stream while the agent keeps running. Changes: - api/config.py: adds AGENT_INSTANCES dict (stream_id -> AIAgent) - api/streaming.py: stores agent in AGENT_INSTANCES after creation, checks CANCEL_FLAGS immediately after store (race condition fix), calls agent.interrupt() in cancel_stream(), cleans up in finally block - static/boot.js: removes stale setStatus(cancelling) call - static/messages.js: setBusy(false)/setStatus('') unconditionally on cancel Race condition fix: after storing agent in AGENT_INSTANCES, immediately checks if CANCEL_FLAGS[stream_id] is already set (cancel arrived during agent init) and interrupts before starting. Check is inside the same STREAMS_LOCK acquisition, making it atomic. New test file: tests/test_cancel_interrupt.py with 6 unit tests. * docs: v0.46.0 release notes, bump version, update test counts --------- Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
2026-04-11 17:17:52 +00:00
CMD ["/hermeswebui_init.bash"]