Spaces:
Running
Running
| 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"] | |