* 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. <code> becoming &lt;code&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>
82 lines
2.4 KiB
Docker
82 lines
2.4 KiB
Docker
FROM python:3.12-slim
|
|
|
|
LABEL maintainer="nesquena"
|
|
LABEL description="Hermes Web UI — browser interface for Hermes Agent"
|
|
|
|
# Install system packages
|
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
|
|
# 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
|
|
|
|
RUN apt-get update -y --fix-missing --no-install-recommends \
|
|
&& apt-get install -y --no-install-recommends \
|
|
apt-utils \
|
|
locales \
|
|
ca-certificates \
|
|
sudo \
|
|
curl \
|
|
rsync \
|
|
&& 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
|
|
|
|
# Every sudo group user does not need a password
|
|
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
|
|
|
|
# Create a new group for the hermeswebui and hermeswebuitoo users
|
|
RUN groupadd -g 1024 hermeswebui \
|
|
&& groupadd -g 1025 hermeswebuitoo
|
|
|
|
# The hermeswebui (resp. hermeswebuitoo) user will have UID 1024 (resp. 1025),
|
|
# be part of the hermeswebui (resp. hermeswebuitoo) and users groups and be sudo capable (passwordless)
|
|
RUN useradd -u 1024 -d /home/hermeswebui -g hermeswebui -s /bin/bash -m hermeswebui \
|
|
&& usermod -G users hermeswebui \
|
|
&& adduser hermeswebui sudo
|
|
RUN useradd -u 1025 -d /home/hermeswebuitoo -g hermeswebuitoo -s /bin/bash -m hermeswebuitoo \
|
|
&& usermod -G users hermeswebuitoo \
|
|
&& adduser hermeswebuitoo sudo
|
|
RUN chown -R hermeswebuitoo:hermeswebuitoo /apptoo
|
|
|
|
USER root
|
|
|
|
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 hermeswebuitoo
|
|
|
|
COPY . /apptoo
|
|
|
|
# Default to binding all interfaces (required for container networking)
|
|
ENV HERMES_WEBUI_HOST=0.0.0.0
|
|
ENV HERMES_WEBUI_PORT=8787
|
|
|
|
EXPOSE 8787
|
|
|
|
CMD ["/hermeswebui_init.bash"]
|
|
|