File size: 5,829 Bytes
2e658e7
 
 
 
 
74c9c7c
2e658e7
74c9c7c
2e658e7
74c9c7c
2e658e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74c9c7c
 
2e658e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# HermesFace on Hugging Face Spaces β€” Source build
# Builds Hermes Agent from source since no pre-built Docker image is published
# Rebuild 2026-04-13: initial release

# ── Stage 1: Build Hermes Agent from source ──────────────────────────────
FROM ghcr.io/astral-sh/uv:0.11-python3.13-trixie AS uv_source
FROM tianon/gosu:1.19-trixie AS gosu_source
FROM node:22-trixie-slim AS node_source

FROM python:3.13-slim-trixie
SHELL ["/bin/bash", "-c"]

ENV PYTHONUNBUFFERED=1
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright

# ── System dependencies ──────────────────────────────────────────────────
RUN echo "[build] Installing system deps..." && START=$(date +%s) \
  && apt-get update \
  && apt-get install -y --no-install-recommends \
     build-essential \
     ripgrep ffmpeg gcc python3-dev libffi-dev procps \
     git ca-certificates curl \
  && rm -rf /var/lib/apt/lists/* \
  && echo "[build] System deps: $(($(date +%s) - START))s"

# ── Non-root user ────────────────────────────────────────────────────────
RUN useradd -u 10000 -m -d /opt/data hermes

COPY --chmod=0755 --from=gosu_source /gosu /usr/local/bin/
COPY --chmod=0755 --from=uv_source /usr/local/bin/uv /usr/local/bin/uvx /usr/local/bin/
COPY --chmod=0755 --from=node_source /usr/local/bin/node /usr/local/bin/node
COPY --from=node_source /usr/local/lib/node_modules /usr/local/lib/node_modules
RUN ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
  && ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \
  && [[ "$(python --version 2>&1)" == "Python 3.13."* ]] \
  && [[ "$(node --version)" == v22.* ]]

# ── Clone and build Hermes Agent (P1-T01 pinable upstream) ──────────────
ARG HERMES_UPSTREAM_REPO=https://github.com/NousResearch/hermes-agent.git
ARG HERMES_UPSTREAM_REF=8fc278207b0f5b25e567966f9615e1b1737f62af
RUN [[ "${HERMES_UPSTREAM_REF}" =~ ^[0-9a-fA-F]{40}$ ]] \
  || { echo "HERMES_UPSTREAM_REF must be an exact 40-character commit SHA" >&2; exit 2; } \
  && echo "[build] Fetching Hermes Agent commit ${HERMES_UPSTREAM_REF}..." && START=$(date +%s) \
  && git init /opt/hermes \
  && cd /opt/hermes \
  && git remote add origin "${HERMES_UPSTREAM_REPO}" \
  && git fetch --depth 1 origin "${HERMES_UPSTREAM_REF}" \
  && git checkout --detach FETCH_HEAD \
  && test "$(git rev-parse HEAD)" = "${HERMES_UPSTREAM_REF}" \
  && printf '%s\n' "${HERMES_UPSTREAM_REF}" > /opt/hermes/HERMES_UPSTREAM_COMMIT \
  && echo "[build] Verified upstream HEAD=$(git rev-parse HEAD)" \
  && echo "[build] Fetch: $(($(date +%s) - START))s"

COPY scripts/verify_upstream_integration.py /tmp/verify_upstream_integration.py
RUN python /tmp/verify_upstream_integration.py /opt/hermes

WORKDIR /opt/hermes

# ── Node dependencies + Playwright + Web Dashboard build ─────────────────
RUN echo "[build] Installing Node deps + Playwright..." && START=$(date +%s) \
  && test -f /opt/hermes/package-lock.json \
  && npm ci --prefer-offline --no-audit \
  && npx --yes playwright@1.52.0 install chromium --only-shell \
  && if [ -f /opt/hermes/scripts/whatsapp-bridge/package.json ]; then \
       test -f /opt/hermes/scripts/whatsapp-bridge/package-lock.json; \
       cd /opt/hermes/scripts/whatsapp-bridge && npm ci --prefer-offline --no-audit; \
     fi \
  && echo "[build] Building web dashboard..." \
  && cd /opt/hermes/web && npm run build \
  && cd /opt/hermes && npm cache clean --force \
  && echo "[build] Node deps + web dashboard: $(($(date +%s) - START))s"

# ── Python dependencies ──────────────────────────────────────────────────
COPY --chown=hermes:hermes requirements.lock /tmp/hermesface-requirements.lock
RUN chown -R hermes:hermes /opt/hermes
USER hermes

RUN echo "[build] Installing Python deps..." && START=$(date +%s) \
  && cd /opt/hermes \
  && test -f uv.lock \
  && uv sync --frozen --all-extras \
  && uv pip install --no-cache-dir -r /tmp/hermesface-requirements.lock \
  && echo "[build] Python deps: $(($(date +%s) - START))s"

USER root
RUN chmod +x /opt/hermes/docker/entrypoint.sh

# ── Prepare runtime dirs ────────────────────────────────────────────────
RUN mkdir -p /opt/data/cron /opt/data/sessions /opt/data/logs /opt/data/hooks \
             /opt/data/memories /opt/data/skills /opt/data/skins /opt/data/plans \
             /opt/data/workspace /opt/data/home \
  && chown -R hermes:hermes /opt/data

USER hermes

# ── HermesFace scripts (persistence + entrypoint + DNS + assets) ──────
ARG CACHE_BUST=2026-07-20-futures-v1
RUN echo "Build: ${CACHE_BUST}"
COPY --chown=hermes:hermes scripts /opt/data/scripts
COPY --chown=hermes:hermes assets /opt/data/assets
COPY --chown=hermes:hermes hermes_overlay /opt/data/hermes_overlay
COPY --chown=hermes:hermes hermes_overlay /opt/hermesface_overlay
RUN chmod +x /opt/data/scripts/entrypoint.sh \
             /opt/data/scripts/dns-resolve.py \
             /opt/data/scripts/hermes_persist.py \
             /opt/data/scripts/save_to_dataset.py \
             /opt/data/scripts/save_to_dataset_atomic.py \
             /opt/data/scripts/restore_from_dataset.py \
             /opt/data/scripts/restore_from_dataset_atomic.py

ENV HERMES_HOME=/opt/data
ENV PATH="/opt/hermes/.venv/bin:$PATH"
WORKDIR /opt/data

CMD ["/opt/data/scripts/entrypoint.sh"]