hermes-bot / Dockerfile
Z User
fix: webui auto-update silent failure + bump to v0.5.9
e10e850
FROM python:3.12-slim
# Fix timezone — HF Space defaults to UTC, we need Asia/Shanghai for log timestamps
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# System deps (add build tools for node-pty native compilation)
RUN apt-get update && apt-get install -y --no-install-recommends \
git curl gnupg2 fontconfig make g++ python3 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Clone hermes-agent (pinned to v2026.4.30 = v0.12.0)
# Runtime auto-update will pull newer releases transparently
RUN git clone --depth 1 --branch v2026.4.30 https://github.com/NousResearch/hermes-agent.git /app/hermes-agent && \
echo "v2026.4.30" > /app/hermes-agent.version
# Build venv
RUN python3 -m venv /app/venv
ENV PATH="/app/venv/bin:$PATH"
RUN pip install --quiet --upgrade pip && \
pip install --quiet psutil networkx duckduckgo-search && \
pip install --quiet -e "/app/hermes-agent[feishu,mcp,cron,pty]" && \
pip install --quiet aiohttp cryptography 2>&1 | tail -10
# Patch: add document file extensions to auto-detection for native delivery
COPY scripts/patch_file_delivery.py /tmp/patch_file_delivery.py
RUN python3 /tmp/patch_file_delivery.py; rm -f /tmp/patch_file_delivery.py
# Patch: Feishu media support in send_message_tool + anti-hallucination prompts
COPY patches/hermes-agent/agent/prompt_builder.py /app/hermes-agent/agent/prompt_builder.py
COPY patches/hermes-agent/tools/send_message_tool.py /app/hermes-agent/tools/send_message_tool.py
# Patch: Auto-inject MEDIA: tags from write_file tool calls
COPY scripts/patch_auto_media.py /tmp/patch_auto_media.py
RUN python3 /tmp/patch_auto_media.py; rm -f /tmp/patch_auto_media.py
# Patch: Auto-resolve relative media paths to absolute paths
COPY scripts/patch_resolve_media_paths.py /tmp/patch_resolve_media_paths.py
RUN python3 /tmp/patch_resolve_media_paths.py; rm -f /tmp/patch_resolve_media_paths.py
# Patch: Fix WeixinAdapter cross-event-loop session reuse
# Prevents "Timeout context manager should be used inside a task" when
# send_weixin_direct() reuses the gateway's aiohttp session from _run_async()
COPY scripts/patch_weixin_cross_loop.py /tmp/patch_weixin_cross_loop.py
RUN python3 /tmp/patch_weixin_cross_loop.py; rm -f /tmp/patch_weixin_cross_loop.py
# Patch: Strip <|channel>thinking / <channel|> model tags from user-visible output
COPY scripts/patch_strip_thinking_tags.py /tmp/patch_strip_thinking_tags.py
RUN python3 /tmp/patch_strip_thinking_tags.py; rm -f /tmp/patch_strip_thinking_tags.py
# Patch: Sandbox isolation for dangerous terminal commands (inspired by OpenAI Agents SDK)
# Installs bubblewrap if available, falls back to unshare + resource limits
RUN apt-get update && apt-get install -y --no-install-recommends bubblewrap 2>/dev/null || true; rm -rf /var/lib/apt/lists/*
COPY scripts/patch_sandbox_isolation.py /tmp/patch_sandbox_isolation.py
RUN python3 /tmp/patch_sandbox_isolation.py; rm -f /tmp/patch_sandbox_isolation.py
# Patch: DuckDuckGo free fallback for web_search (no API key needed)
COPY scripts/patch_web_search_fallback.py /tmp/patch_web_search_fallback.py
RUN python3 /tmp/patch_web_search_fallback.py; rm -f /tmp/patch_web_search_fallback.py
# Install Node.js 23
RUN ARCH=$(dpkg --print-architecture) \
&& if [ "$ARCH" = "amd64" ]; then NODE_ARCH="x64"; else NODE_ARCH="$ARCH"; fi \
&& echo "Installing Node.js v23 for ${NODE_ARCH}" \
&& curl -fsSL "https://nodejs.org/dist/v23.11.0/node-v23.11.0-linux-${NODE_ARCH}.tar.gz" \
-o /tmp/node.tar.gz \
&& tar -xzf /tmp/node.tar.gz -C /usr/local --strip-components=1 \
&& rm -f /tmp/node.tar.gz \
&& node --version && npm --version
# Chinese font (Noto Sans SC Regular + Bold)
RUN mkdir -p /usr/share/fonts/truetype/noto && \
curl -sL "https://github.com/googlefonts/noto-cjk/raw/main/Sans/SubsetOTF/SC/NotoSansSC-Regular.otf" \
-o /usr/share/fonts/truetype/noto/NotoSansSC-Regular.otf && \
curl -sL "https://github.com/googlefonts/noto-cjk/raw/main/Sans/SubsetOTF/SC/NotoSansSC-Bold.otf" \
-o /usr/share/fonts/truetype/noto/NotoSansSC-Bold.otf && \
fc-cache -f
# Clone agency-agents-zh (211 expert role prompts for instant role switching)
RUN git clone --depth 1 https://github.com/jnMetaCode/agency-agents-zh.git /app/agency-agents && \
echo "agency-agents-zh cloned ($(find /app/agency-agents -name '*.md' ! -name 'README*' ! -name 'CATALOG*' ! -name 'AGENT-LIST*' ! -name 'CONTRIBUTING*' ! -name 'LICENSE*' ! -name 'UPSTREAM*' | wc -l) agent files)"
# Build hermes-web-ui v0.5.9
# Aligned with upstream Dockerfile: NODE_OPTIONS + npm rebuild node-pty
RUN rm -rf /tmp/hermes-web-ui && \
git clone --depth 1 --branch v0.5.9 https://github.com/EKKOLearnAI/hermes-web-ui.git /tmp/hermes-web-ui && \
cd /tmp/hermes-web-ui && \
echo "build-v0.5.9-$(date +%Y%m%d)" > .buildstamp && \
npm install --ignore-scripts 2>&1 | tail -5 && \
npm rebuild node-pty 2>&1 | tail -5 && \
NODE_OPTIONS=--max-old-space-size=4096 npm run build 2>&1 | tail -10 && \
mkdir -p /app/webui-server && \
cp -r dist/server/* /app/webui-server/ && \
mkdir -p /app/webui-client && \
cp -r dist/client/* /app/webui-client/ && \
cp package.json /app/webui-server/package.json && \
npm prune --omit=dev --prefix /tmp/hermes-web-ui 2>&1 | tail -3 && \
cp -r node_modules /app/webui-server/node_modules && \
rm -rf /tmp/hermes-web-ui && \
echo "v0.5.9" > /app/webui.version && \
echo "hermes-web-ui v0.5.9 build done"
# Create hermes home
RUN mkdir -p /root/.hermes/plugins/image_gen/pollinations
# Copy config files (to both hermes home AND /app for persistence fallback)
COPY config.yaml /root/.hermes/config.yaml
COPY SOUL.md /root/.hermes/SOUL.md
# Keep repo copies in /app as fallback sources for persistent storage recovery
COPY config.yaml /app/config.yaml
COPY .env.example /app/.env.example
COPY entry.py /app/entry.py
COPY dashboard.html /app/dashboard.html
COPY plugins/pollinations/ /root/.hermes/plugins/image_gen/pollinations/
COPY scripts/ /app/scripts/
COPY custom-agents/ /app/custom-agents/
# Startup script
COPY start.sh /app/start.sh
RUN chmod +x /app/start.sh
EXPOSE 7860
ENV HERMES_ACCEPT_HOOKS=1
ENV NODE_ENV=production
ENV AUTH_TOKEN=hermes-bot-2026
CMD ["/app/start.sh"]