FROM python:3.11-slim # 基础环境变量,主目录改为 /home/hermes ENV HERMES_HOME=/home/hermes ENV PATH="${HERMES_HOME}/.local/bin:${HERMES_HOME}/.npm-global/bin:${PATH}" ENV NPM_CONFIG_PREFIX="${HERMES_HOME}/.npm-global" ENV PIP_USER=1 ENV PYTHONUSERBASE="${HERMES_HOME}/.local" ENV CAMOUFOX_PORT=9377 ENV TZ=Asia/Shanghai # 系统依赖(完整) RUN apt-get update && apt-get install -y --no-install-recommends \ curl wget git ca-certificates gnupg \ build-essential gcc g++ make cmake pkg-config \ python3-dev libffi-dev libssl-dev \ ffmpeg ripgrep \ portaudio19-dev libsndfile1-dev libpulse-dev \ libjpeg-dev libpng-dev libtiff-dev libwebp-dev \ libmagic-dev \ libnss3 libnspr4 libatk1.0-0t64 libatk-bridge2.0-0t64 \ libcups2t64 libdrm2 libdbus-1-3 libxkbcommon0 \ libxcomposite1 libxdamage1 libxfixes3 libxrandr2 \ libgbm1 libpango-1.0-0 libcairo2 libasound2t64 \ libatspi2.0-0t64 libwayland-client0 libgtk-3-0 \ libdbus-glib-1-2 libxss1 \ xterm fonts-noto-color-emoji fonts-wqy-microhei \ fonts-dejavu-core fontconfig \ tzdata procps htop vim less netcat-openbsd \ && rm -rf /var/lib/apt/lists/* # 时区 RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && echo "${TZ}" > /etc/timezone # Node.js 24 RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \ && apt-get install -y nodejs \ && rm -rf /var/lib/apt/lists/* # 创建 hermes 用户(家目录为 /home/hermes)及所有子目录 RUN useradd -m -d ${HERMES_HOME} -s /bin/bash hermes \ && mkdir -p ${HERMES_HOME}/projects \ && mkdir -p ${HERMES_HOME}/.local/bin \ && mkdir -p ${HERMES_HOME}/.npm-global \ && mkdir -p ${HERMES_HOME}/.cache/camoufox \ && mkdir -p ${HERMES_HOME}/.hermes \ && mkdir -p ${HERMES_HOME}/.hermes-web-ui \ && chown -R hermes:hermes ${HERMES_HOME} # 切换到 hermes 用户 USER hermes WORKDIR ${HERMES_HOME} # 安装 uv,克隆并安装 hermes-agent RUN pip install --user uv RUN git clone https://github.com/NousResearch/hermes-agent.git ${HERMES_HOME}/projects/hermes-agent WORKDIR ${HERMES_HOME}/projects/hermes-agent RUN pip install --user -e . RUN pip install --user -e ".[all]" || \ (echo "=== [all] install failed, trying subsets ===" && \ pip install --user -e ".[browser]" && \ pip install --user -e ".[voice]" && \ pip install --user -e ".[search]" && \ pip install --user -e ".[messaging]" && \ echo "=== Subset install completed ===") WORKDIR ${HERMES_HOME} RUN pip install --user camoufox[geoip] RUN npm install -g npm@latest hermes-web-ui # 软链接 RUN ln -sf ${HERMES_HOME}/.local/bin/hermes ${HERMES_HOME}/.local/bin/hermes-agent \ && ln -sf ${HERMES_HOME}/.npm-global/bin/hermes-web-ui ${HERMES_HOME}/.local/bin/hermes-web-ui # 切回 root 打包并放置启动脚本 USER root # 打包 /home/hermes 目录,压缩包放在根下 RUN tar czf /hermes.tar.gz -C /home hermes # 启动脚本(路径全部使用 /home/hermes) RUN cat > /usr/local/bin/start.sh << 'SCRIPT_EOF' #!/bin/bash set -e HERMES_HOME=/home/hermes # 动态时区 if [ -n "${TZ}" ]; then echo "Setting timezone to ${TZ}..." ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime 2>/dev/null || \ echo "Warning: Cannot change timezone (permission denied). Using default." echo "${TZ}" > /etc/timezone 2>/dev/null || true fi echo "=== Hermes Full Stack Container ===" echo "Timezone: $(cat /etc/timezone 2>/dev/null || echo 'unknown')" echo "Node.js version: $(node --version)" echo "npm version: $(npm --version)" # 首次挂载检测:若 /home/hermes/.local 目录为空则解压 if [ ! -d "${HERMES_HOME}/.local" ] || [ -z "$(ls -A ${HERMES_HOME}/.local 2>/dev/null)" ]; then echo ">>> Initializing ${HERMES_HOME} from built-in archive..." tar xzf /hermes.tar.gz -C /home chown -R hermes:hermes ${HERMES_HOME} echo ">>> Extraction complete." fi # 确保必要目录 mkdir -p ${HERMES_HOME}/.hermes ${HERMES_HOME}/.hermes-web-ui ${HERMES_HOME}/.cache/camoufox # 环境 export PATH="${HERMES_HOME}/.local/bin:${HERMES_HOME}/.npm-global/bin:${PATH}" # 确定 hermes 命令 if [ -x "${HERMES_HOME}/projects/hermes-agent/hermes" ]; then HERMES_CMD="${HERMES_HOME}/projects/hermes-agent/hermes" else HERMES_CMD="hermes" fi # 首次设置 Agent if [ ! -f "${HERMES_HOME}/.hermes/config.yaml" ]; then echo "First startup — initializing Hermes Agent..." $HERMES_CMD setup --non-interactive || true fi # 提示 Camoufox 浏览器 if [ ! -f "${HERMES_HOME}/.local/bin/camoufox" ]; then echo "==========================================" echo " Camoufox browser not found." echo " Install manually in container:" echo " python -m camoufox fetch" echo "==========================================" fi echo "Starting Hermes Web UI on port ${PORT:-8648}..." # 后台启动(不退出) hermes-web-ui start --port ${PORT:-8648} sleep 2 echo "Dashboard URL: http://localhost:${PORT:-8648}" # 保活 echo "Container stay-alive mode..." exec tail -f /dev/null SCRIPT_EOF RUN chmod +x /usr/local/bin/start.sh EXPOSE 8648 9377 VOLUME ["/home/hermes"] HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ CMD curl -f http://localhost:8648/health || exit 1 ENTRYPOINT ["/usr/local/bin/start.sh"]