From cb5dc8289a71aaafa19637627015fca84d9fb33c Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Fri, 24 Jul 2026 22:53:23 +0800 Subject: [PATCH 01/18] update env --- .github/workflows/build-docker.yml | 4 ++-- docker_casdoor/demo/docker-compose.yml | 8 +++----- docker_gui/README.md | 2 +- docker_keycloak/demo/docker-compose.yml | 23 ++++++++++++----------- docker_nocobase/demo/docker-compose.yml | 2 +- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 7dba2af..8987fcf 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -21,8 +21,8 @@ permissions: env: TZ: Asia/Shanghai BUILDKIT_PROGRESS: "plain" # Full logs for CI build. - REGISTRY_SRC: ${{ vars.REGISTRY_SRC || 'docker.io' }} # For BASE_NAMESPACE of images: where to pull base images from, docker.io or other source registry URL. - REGISTRY_DST: ${{ vars.REGISTRY_DST || 'docker.io' }} # For tags of built images: where to push images to, docker.io or other destination registry URL. + REGISTRY_SRC: ${{ vars.REGISTRY_SRC || 'quay.io' }} # For BASE_NAMESPACE of images: where to pull base images from, docker.io or other source registry URL. + REGISTRY_DST: ${{ vars.REGISTRY_DST || 'quay.io' }} # For tags of built images: where to push images to, docker.io or other destination registry URL. # DOCKER_REGISTRY_USERNAME and DOCKER_REGISTRY_PASSWORD is required for docker image push, they should be set in CI secrets. DOCKER_REGISTRY_USERNAME: ${{ vars.DOCKER_REGISTRY_USERNAME }} DOCKER_REGISTRY_PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} diff --git a/docker_casdoor/demo/docker-compose.yml b/docker_casdoor/demo/docker-compose.yml index d052fdd..30c1709 100644 --- a/docker_casdoor/demo/docker-compose.yml +++ b/docker_casdoor/demo/docker-compose.yml @@ -13,7 +13,6 @@ services: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres ports: ["35432:5432"] - networks: ["net-moa"] volumes: - /data/storage/pg-common:/var/lib/postgresql/data # su postgres && psql -d casdoor @@ -21,7 +20,8 @@ services: svc-casdoor: image: quay.io/labnow/casdoor container_name: svc-casdoor - hostname: svc-nocobase + hostname: svc-casdoor + depends_on: ["db-postgres-common"] command: | /bin/bash -l -c ' ls -alh /opt/casdoor @@ -30,9 +30,7 @@ services: sleep 8s cat /opt/casdoor/conf/app.conf /opt/casdoor/server -createDatabase=true' - ports: - - 8000:8000 - depends_on: ["db-postgres-common"] + ports: ["8000:8000"] healthcheck: test: ["CMD-SHELL", "curl --head -fsSk http://localhost:8000/health/ready" ] interval: 30s diff --git a/docker_gui/README.md b/docker_gui/README.md index fccc896..b047497 100644 --- a/docker_gui/README.md +++ b/docker_gui/README.md @@ -99,7 +99,7 @@ docker run --rm -p 8080:8080 labnow/gui:selkies --encoder=vp8enc --enable_resize ```bash docker run --rm -it -p 8080:8080 \ - quay.io/labnow0dev/gui-linux bash + quay.io/labnow/gui-linux bash # optional env: -e SELKIES_TURN_PROTOCOL=tcp \ diff --git a/docker_keycloak/demo/docker-compose.yml b/docker_keycloak/demo/docker-compose.yml index 8c9ac60..41026d4 100644 --- a/docker_keycloak/demo/docker-compose.yml +++ b/docker_keycloak/demo/docker-compose.yml @@ -1,21 +1,26 @@ services: - db-postgres: + db-postgres-common: # su postgres && psql -d keycloak -U pg-keycloak-username - image: docker.io/library/postgres:16 - container_name: db-postgres + image: quay.io/labnow/postgres-17:latest + container_name: db-postgres-common + hostname: db-postgres-common + pull_policy: always + restart: unless-stopped environment: POSTGRES_DB: keycloak POSTGRES_USER: pg-keycloak-username POSTGRES_PASSWORD: pg-keycloak-password svc-keycloak: - image: docker.io/labnow/keycloak + image: quay.io/labnow/keycloak # build: # context: ../ # dockerfile: Dockerfile # args: - # BASE_NAMESPACE: labnow0dev + # BASE_NAMESPACE: labnow container_name: svc-keycloak + hostname: svc-keycloak + depends_on: ["db-postgres-common"] # command: ["/bin/bash", "--login", "bin/kc.sh", "start-dev", "--verbose"] command: | /bin/bash -l -c ' @@ -30,20 +35,16 @@ services: environment: KC_HOSTNAME: localhost KC_DB: postgres - KC_DB_URL: "jdbc:postgresql://db-postgres:5432/keycloak" + KC_DB_URL: "jdbc:postgresql://db-postgres-common:5432/keycloak" KC_DB_USERNAME: "pg-keycloak-username" KC_DB_PASSWORD: "pg-keycloak-password" KEYCLOAK_ADMIN: keycloak-admin KEYCLOAK_ADMIN_PASSWORD: keycloak-password PROXY_ADDRESS_FORWARDING: "true" - ports: - - 8080:8080 - depends_on: - - db-postgres + ports: ["8080:8080"] healthcheck: test: ["CMD-SHELL", "curl", "--head", "-fsSk", "https://localhost:8080/health/ready" ] interval: 30s timeout: 30s start_period: 5s retries: 3 - diff --git a/docker_nocobase/demo/docker-compose.yml b/docker_nocobase/demo/docker-compose.yml index 06dad8d..91e4f58 100644 --- a/docker_nocobase/demo/docker-compose.yml +++ b/docker_nocobase/demo/docker-compose.yml @@ -18,7 +18,7 @@ services: # su postgres && psql -d nocobase svc-nocobase: - image: quay.io/labnow0dev/nocobase + image: quay.io/labnow/nocobase container_name: svc-nocobase hostname: svc-nocobase depends_on: ["db-postgres-common"] From 62192d849847691592222a6f4a72125a8fe2dcf7 Mon Sep 17 00:00:00 2001 From: Trae Bot Date: Fri, 24 Jul 2026 15:53:15 +0000 Subject: [PATCH 02/18] fix(hermes): restore working docker build after upstream wheel drop - Switch from removed PyPI wheel flow to uv-based venv install matching the upstream NousResearch/hermes-agent Dockerfile (they dropped brew + pip/PyPI wheel support in commit d84e11a). - Split ARGs into BASE_IMG_BUILD (node, for npm + tsc) and BASE_IMG (base, for the script-setup-sys.sh / script-utils.sh helpers used by other sibling images like litellm / storebox / searxng). - Add a uv_source stage to copy a static uv binary into the builder, plus build-time APT toolchain (gcc/cmake/python3-dev/libolm-dev) so uv sync can compile native deps (matrix crypto, cryptography, ffi). - Builder: npm install + playwright chromium, uv sync with the production extras (all/messaging + major providers + hindsight + matrix), build web & ui-tui frontends, uv pip install -e ., and stamp /opt/hermes/.install_method + exec shim. - Runtime: copy the full /opt/hermes tree (venv + source + browsers + built frontends) into the base-image layer, install supervisord + runtime apt deps, seed hermes_cli paths into /etc/profile.d, and wire PATH/PYTHONPATH so python3 -m scripts.* / python3 -m tools.* resolves against the managed venv inside start-hermes.sh. - Bump healthcheck start-period & retries to account for cold-boot supervisord + gateway startup on slower nodes. Fixes hermes job in build-docker.yml CI/CD workflow. --- docker_hermes/hermes.Dockerfile | 99 ++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 21 deletions(-) diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index f4f5573..f8f4e84 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -1,30 +1,75 @@ # Distributed under the terms of the Modified BSD License. ARG BASE_NAMESPACE -ARG BASE_IMG="node" +ARG BASE_IMG_BUILD="node" +ARG BASE_IMG="base" + +# --- uv source stage (borrow the static uv binary) --- +FROM ghcr.io/astral-sh/uv:0.11.6-python3.13-trixie@sha256:b3c543b6c4f23a5f2df22866bd7857e5d304b67a564f4feab6ac22044dde719b AS uv_source # --- Building Stage --- -FROM ${BASE_NAMESPACE:+$BASE_NAMESPACE/}${BASE_IMG} AS builder +FROM ${BASE_NAMESPACE:+$BASE_NAMESPACE/}${BASE_IMG_BUILD} AS builder + +LABEL maintainer="postmaster@labnow.ai" # Build-time environment ENV NODE_ENV=development -WORKDIR /build +ENV PYTHONDONTWRITEBYTECODE=1 +ENV UV_LINK_MODE=copy +WORKDIR /opt/hermes + +# Install uv from uv_source stage +COPY --from=uv_source /usr/local/bin/uv /usr/local/bin/uv +COPY --from=uv_source /usr/local/bin/uvx /usr/local/bin/uvx + +# Install build-time system dependencies (compilers + native libs needed for Python extensions). +# Without these, `uv sync` fails when compiling packages like `matrix-*-crypto`, `cryptography`, +# or `ffi`-based wheels on cold builds. +RUN set -eux \ + && printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\n' > /etc/apt/apt.conf.d/80-retries \ + && apt-get -qq update -yq --fix-missing \ + && DEBIAN_FRONTEND=noninteractive apt-get -qq install -yq --no-install-recommends \ + ca-certificates curl git gcc g++ make cmake \ + python3 python3-dev python3-venv python-is-python3 \ + libffi-dev libolm-dev \ + && rm -rf /var/lib/apt/lists/* -# Clone source +# Clone source (full clone for reproducibility; depth 1 for speed) RUN git clone --depth 1 --branch main https://github.com/nousresearch/hermes-agent.git . -# Install Node dependencies and build frontend +# ---------- Node dependencies + Playwright (cached on manifests) ---------- +ENV npm_config_install_links=false +RUN set -eux \ + && npm install --prefer-offline --no-audit --fetch-retries=5 \ + && for i in 1 2 3; do \ + npx playwright install --with-deps chromium --only-shell && break || \ + { [ "$i" = 3 ] && exit 1; echo "playwright install failed (attempt $i); retrying in 10s"; sleep 10; }; \ + done \ + && npm cache clean --force + +# ---------- Python dependency install via uv (cached on manifests) ---------- +# README.md is referenced by pyproject.toml but excluded by .dockerignore in source; +# create a placeholder so uv's build frontend doesn't fail. +RUN touch ./README.md +RUN set -eux \ + && uv sync --frozen --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix || \ + uv sync --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix + +# ---------- Frontend build (web + ui-tui) ---------- RUN set -eux \ - && export npm_config_install_links=false \ - && npm install --include=dev --prefer-offline --no-audit \ && (cd web && npm run build) \ && (cd ui-tui && npm run build) \ && mkdir -p hermes_cli/tui_dist && cp ui-tui/dist/entry.js hermes_cli/tui_dist/ -# Build Python wheel +# ---------- Link hermes-agent itself (editable, no deps) + install-method stamp ---------- RUN set -eux \ - && python3 -m pip install --upgrade pip build \ - && python3 -m build --wheel --outdir dist + && uv pip install --no-cache-dir --no-deps -e "." \ + && mkdir -p /opt/hermes/bin \ + && cp /opt/hermes/docker/hermes-exec-shim.sh /opt/hermes/bin/hermes 2>/dev/null || { \ + printf '#!/usr/bin/env bash\nexec /opt/hermes/.venv/bin/hermes "$@"\n' > /opt/hermes/bin/hermes; \ + } \ + && chmod 0755 /opt/hermes/bin/hermes \ + && printf 'docker\n' > /opt/hermes/.install_method # --- Runtime Stage --- FROM ${BASE_NAMESPACE:+$BASE_NAMESPACE/}${BASE_IMG} @@ -33,32 +78,44 @@ LABEL maintainer="postmaster@labnow.ai" # Production environment ENV NODE_ENV=production +ENV PYTHONUNBUFFERED=1 +ENV PYTHONDONTWRITEBYTECODE=1 ENV HERMES_HOME=/root/workspace -ENV HERMES_WEB_DIST=/usr/local/lib/python3.12/dist-packages/hermes_cli/web_dist -ENV HERMES_TUI_DIR=/usr/local/lib/python3.12/dist-packages/hermes_cli/tui_dist ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright -ENV PATH="/opt/node/bin:/opt/conda/bin:/root/.local/bin:${PATH}" +# Put the hermes venv at the front so `python3`, `hermes`, `uv`, etc. resolve to the +# managed install. This also lets `python3 -m scripts.*` / `python3 -m tools.*` in +# the seeding scripts find the hermes source tree via the appended PYTHONPATH. +ENV VIRTUAL_ENV=/opt/hermes/.venv +ENV PYTHONPATH="/opt/hermes:${PYTHONPATH:-}" +ENV PATH="/opt/hermes/.venv/bin:/opt/hermes/bin:/opt/node/bin:/opt/conda/bin:/root/.local/bin:${PATH}" ENV HOME=/root/workspace WORKDIR /root/workspace - -# Copy build artifacts from builder -COPY --from=builder /build/dist/*.whl /tmp/ +# Copy the full hermes install tree from the builder (venv + source + browsers + built frontends) +COPY --from=builder /opt/hermes /opt/hermes # Copy utilities and tools COPY work /opt/utils/ +# Discover the real python site-packages so legacy env-var fallbacks point at the right tree. +# Keep explicit versioned fallbacks around in case detection runs before the first pip install. RUN set -eux \ && chmod +x /opt/utils/*.sh \ && ln -sf /opt/utils/start-hermes.sh /usr/local/bin/start-hermes.sh \ && ln -sf /opt/utils/healthcheck-hermes.sh /usr/local/bin/healthcheck-hermes.sh \ - ## Install Runtime dependencies + ## Runtime APT deps (hermes needs libolm for matrix, ffmpeg for voice, ripgrep for FTS, etc.) && printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\n' > /etc/apt/apt.conf.d/80-retries \ - && pip install --no-cache-dir /tmp/*.whl \ - && npx playwright install --with-deps chromium --only-shell \ - ## Install supervisord (Go version) && source /opt/utils/script-setup-sys.sh && setup_supervisord \ && source /opt/utils/script-utils.sh && install_apt /opt/utils/install_list_hermes.apt \ + ## Detect the real hermes_cli location inside the venv and record it in a profile.d snippet + ## so start-hermes.sh's auto-detect always finds the built frontends. + && VENV_PY=$(/opt/hermes/.venv/bin/python3 -c "import hermes_cli, pathlib; print(pathlib.Path(hermes_cli.__file__).resolve().parent)" 2>/dev/null || true) \ + && if [ -n "$VENV_PY" ]; then \ + echo "Detected hermes_cli at: $VENV_PY"; \ + mkdir -p /etc/profile.d; \ + printf 'export HERMES_WEB_DIST=%s/web_dist\nexport HERMES_TUI_DIR=%s/tui_dist\n' "$VENV_PY" "$VENV_PY" > /etc/profile.d/hermes-paths.sh; \ + chmod +x /etc/profile.d/hermes-paths.sh; \ + fi \ && install__clean # Data persistence is owned by the runtime orchestrator. @@ -68,5 +125,5 @@ RUN set -eux \ # The labnow-open wrapper calls start-hermes.sh with explicit gateway/dashboard modes and therefore does not use this CMD. CMD ["start-hermes.sh", "all"] EXPOSE 9119 -HEALTHCHECK --interval=10s --timeout=5s --start-period=20s --retries=3 \ +HEALTHCHECK --interval=10s --timeout=5s --start-period=40s --retries=5 \ CMD ["/usr/local/bin/healthcheck-hermes.sh"] From d70599d707c3c186a02d513b997e861a4271f645 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Sat, 25 Jul 2026 00:10:31 +0800 Subject: [PATCH 03/18] debug hermes build --- docker_hermes/hermes.Dockerfile | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index f8f4e84..9043c33 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -4,38 +4,24 @@ ARG BASE_NAMESPACE ARG BASE_IMG_BUILD="node" ARG BASE_IMG="base" -# --- uv source stage (borrow the static uv binary) --- -FROM ghcr.io/astral-sh/uv:0.11.6-python3.13-trixie@sha256:b3c543b6c4f23a5f2df22866bd7857e5d304b67a564f4feab6ac22044dde719b AS uv_source - # --- Building Stage --- FROM ${BASE_NAMESPACE:+$BASE_NAMESPACE/}${BASE_IMG_BUILD} AS builder -LABEL maintainer="postmaster@labnow.ai" - # Build-time environment ENV NODE_ENV=development -ENV PYTHONDONTWRITEBYTECODE=1 ENV UV_LINK_MODE=copy WORKDIR /opt/hermes -# Install uv from uv_source stage -COPY --from=uv_source /usr/local/bin/uv /usr/local/bin/uv -COPY --from=uv_source /usr/local/bin/uvx /usr/local/bin/uvx - # Install build-time system dependencies (compilers + native libs needed for Python extensions). # Without these, `uv sync` fails when compiling packages like `matrix-*-crypto`, `cryptography`, # or `ffi`-based wheels on cold builds. RUN set -eux \ && printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\n' > /etc/apt/apt.conf.d/80-retries \ && apt-get -qq update -yq --fix-missing \ - && DEBIAN_FRONTEND=noninteractive apt-get -qq install -yq --no-install-recommends \ - ca-certificates curl git gcc g++ make cmake \ - python3 python3-dev python3-venv python-is-python3 \ + && apt-get -qq install -yq --no-install-recommends \ libffi-dev libolm-dev \ - && rm -rf /var/lib/apt/lists/* - -# Clone source (full clone for reproducibility; depth 1 for speed) -RUN git clone --depth 1 --branch main https://github.com/nousresearch/hermes-agent.git . + ## Clone source (full clone for reproducibility; depth 1 for speed) + && git clone --depth 1 --branch main https://github.com/nousresearch/hermes-agent.git . # ---------- Node dependencies + Playwright (cached on manifests) ---------- ENV npm_config_install_links=false @@ -50,8 +36,8 @@ RUN set -eux \ # ---------- Python dependency install via uv (cached on manifests) ---------- # README.md is referenced by pyproject.toml but excluded by .dockerignore in source; # create a placeholder so uv's build frontend doesn't fail. -RUN touch ./README.md RUN set -eux \ + && touch ./README.md && uv sync --frozen --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix || \ uv sync --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix @@ -78,13 +64,10 @@ LABEL maintainer="postmaster@labnow.ai" # Production environment ENV NODE_ENV=production -ENV PYTHONUNBUFFERED=1 -ENV PYTHONDONTWRITEBYTECODE=1 ENV HERMES_HOME=/root/workspace ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright -# Put the hermes venv at the front so `python3`, `hermes`, `uv`, etc. resolve to the -# managed install. This also lets `python3 -m scripts.*` / `python3 -m tools.*` in -# the seeding scripts find the hermes source tree via the appended PYTHONPATH. +# Put the hermes venv at the front so `python3`, `hermes`, `uv`, etc. resolve to the managed install. +# This also lets `python3 -m scripts.*` / `python3 -m tools.*` in the seeding scripts find the hermes source tree via the appended PYTHONPATH. ENV VIRTUAL_ENV=/opt/hermes/.venv ENV PYTHONPATH="/opt/hermes:${PYTHONPATH:-}" ENV PATH="/opt/hermes/.venv/bin:/opt/hermes/bin:/opt/node/bin:/opt/conda/bin:/root/.local/bin:${PATH}" From b892d86e283adb548157c8ed472744e9a697a555 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Fri, 24 Jul 2026 16:25:46 +0000 Subject: [PATCH 04/18] simpilify --- docker_hermes/hermes.Dockerfile | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index 9043c33..4b1fe0b 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -10,34 +10,31 @@ FROM ${BASE_NAMESPACE:+$BASE_NAMESPACE/}${BASE_IMG_BUILD} AS builder # Build-time environment ENV NODE_ENV=development ENV UV_LINK_MODE=copy +ENV npm_config_install_links=false + + WORKDIR /opt/hermes # Install build-time system dependencies (compilers + native libs needed for Python extensions). -# Without these, `uv sync` fails when compiling packages like `matrix-*-crypto`, `cryptography`, -# or `ffi`-based wheels on cold builds. +# Without these, `uv sync` fails when compiling packages like `matrix-*-crypto`, `cryptography`, or `ffi`-based wheels on cold builds. RUN set -eux \ && printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\n' > /etc/apt/apt.conf.d/80-retries \ && apt-get -qq update -yq --fix-missing \ && apt-get -qq install -yq --no-install-recommends \ libffi-dev libolm-dev \ ## Clone source (full clone for reproducibility; depth 1 for speed) - && git clone --depth 1 --branch main https://github.com/nousresearch/hermes-agent.git . - -# ---------- Node dependencies + Playwright (cached on manifests) ---------- -ENV npm_config_install_links=false -RUN set -eux \ + && git clone --depth 1 --branch main https://github.com/nousresearch/hermes-agent.git . \ + ## ---------- Node dependencies + Playwright (cached on manifests) ---------- && npm install --prefer-offline --no-audit --fetch-retries=5 \ && for i in 1 2 3; do \ npx playwright install --with-deps chromium --only-shell && break || \ { [ "$i" = 3 ] && exit 1; echo "playwright install failed (attempt $i); retrying in 10s"; sleep 10; }; \ done \ - && npm cache clean --force - -# ---------- Python dependency install via uv (cached on manifests) ---------- -# README.md is referenced by pyproject.toml but excluded by .dockerignore in source; -# create a placeholder so uv's build frontend doesn't fail. -RUN set -eux \ - && touch ./README.md + && npm cache clean --force \ + ## ---------- Python dependency install via uv (cached on manifests) ---------- + ## README.md is referenced by pyproject.toml but excluded by .dockerignore in source; + ## create a placeholder so uv's build frontend doesn't fail. + && touch ./README.md \ && uv sync --frozen --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix || \ uv sync --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix @@ -45,10 +42,8 @@ RUN set -eux \ RUN set -eux \ && (cd web && npm run build) \ && (cd ui-tui && npm run build) \ - && mkdir -p hermes_cli/tui_dist && cp ui-tui/dist/entry.js hermes_cli/tui_dist/ - -# ---------- Link hermes-agent itself (editable, no deps) + install-method stamp ---------- -RUN set -eux \ + && mkdir -p hermes_cli/tui_dist && cp ui-tui/dist/entry.js hermes_cli/tui_dist/ \ + ## ---------- Link hermes-agent itself (editable, no deps) + install-method stamp ---------- && uv pip install --no-cache-dir --no-deps -e "." \ && mkdir -p /opt/hermes/bin \ && cp /opt/hermes/docker/hermes-exec-shim.sh /opt/hermes/bin/hermes 2>/dev/null || { \ From 0ee77e151ef813f15ca600fc0a5659c213798222 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Fri, 24 Jul 2026 17:10:47 +0000 Subject: [PATCH 05/18] debug hermes install --- docker_hermes/hermes.Dockerfile | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index 4b1fe0b..bb3c0cd 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -12,12 +12,15 @@ ENV NODE_ENV=development ENV UV_LINK_MODE=copy ENV npm_config_install_links=false +# Copy utilities and tools +COPY work /opt/utils/ WORKDIR /opt/hermes # Install build-time system dependencies (compilers + native libs needed for Python extensions). # Without these, `uv sync` fails when compiling packages like `matrix-*-crypto`, `cryptography`, or `ffi`-based wheels on cold builds. RUN set -eux \ + && chmod +x /opt/utils/*.sh && mv /opt/utils/*hermes*.sh /opt/hermes/ \ && printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\n' > /etc/apt/apt.conf.d/80-retries \ && apt-get -qq update -yq --fix-missing \ && apt-get -qq install -yq --no-install-recommends \ @@ -26,17 +29,27 @@ RUN set -eux \ && git clone --depth 1 --branch main https://github.com/nousresearch/hermes-agent.git . \ ## ---------- Node dependencies + Playwright (cached on manifests) ---------- && npm install --prefer-offline --no-audit --fetch-retries=5 \ - && for i in 1 2 3; do \ - npx playwright install --with-deps chromium --only-shell && break || \ - { [ "$i" = 3 ] && exit 1; echo "playwright install failed (attempt $i); retrying in 10s"; sleep 10; }; \ - done \ + && npm install -g playwright \ + && playwright install --with-deps chromium --only-shell \ && npm cache clean --force \ ## ---------- Python dependency install via uv (cached on manifests) ---------- ## README.md is referenced by pyproject.toml but excluded by .dockerignore in source; ## create a placeholder so uv's build frontend doesn't fail. && touch ./README.md \ + ## ---------- hack python-olm for building compatible wheels ---------- + && mkdir -p /tmp/olm && cd /tmp/olm \ + && curl -s https://pypi.org/pypi/python-olm/3.2.16/json \ + | jq -r '.urls[] | select(.packagetype=="sdist").url' \ + | xargs curl -L -o python-olm-3.2.16.tar.gz \ + && tar xf python-olm-3.2.16.tar.gz \ + && cd python-olm-3.2.16 \ + && sed -i 's/cmake_minimum_required(VERSION [0-9.]*)/cmake_minimum_required(VERSION 3.5)/' libolm/CMakeLists.txt \ + && pip wheel . --no-build-isolation -w /tmp/olm/wheels \ + && pip install /tmp/olm/wheels/*.whl \ + && cd /opt/hermes \ + ## ---------- uv sync (cached on manifests) ---------- && uv sync --frozen --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix || \ - uv sync --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix + uv sync --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix # ---------- Frontend build (web + ui-tui) ---------- RUN set -eux \ @@ -72,15 +85,12 @@ WORKDIR /root/workspace # Copy the full hermes install tree from the builder (venv + source + browsers + built frontends) COPY --from=builder /opt/hermes /opt/hermes -# Copy utilities and tools -COPY work /opt/utils/ # Discover the real python site-packages so legacy env-var fallbacks point at the right tree. # Keep explicit versioned fallbacks around in case detection runs before the first pip install. RUN set -eux \ - && chmod +x /opt/utils/*.sh \ - && ln -sf /opt/utils/start-hermes.sh /usr/local/bin/start-hermes.sh \ - && ln -sf /opt/utils/healthcheck-hermes.sh /usr/local/bin/healthcheck-hermes.sh \ + && ln -sf /opt/hermes/start-hermes.sh /usr/local/bin/start-hermes.sh \ + && ln -sf /opt/hermes/healthcheck-hermes.sh /usr/local/bin/healthcheck-hermes.sh \ ## Runtime APT deps (hermes needs libolm for matrix, ffmpeg for voice, ripgrep for FTS, etc.) && printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\n' > /etc/apt/apt.conf.d/80-retries \ && source /opt/utils/script-setup-sys.sh && setup_supervisord \ From 01c2ed20ab7c187d9371fe85a4baf7df1111743c Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Fri, 24 Jul 2026 17:21:18 +0000 Subject: [PATCH 06/18] debug --- docker_hermes/hermes.Dockerfile | 28 ++++++++++------------ docker_hermes/work/install_list_hermes.apt | 6 ++--- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index bb3c0cd..6661a1a 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -12,16 +12,11 @@ ENV NODE_ENV=development ENV UV_LINK_MODE=copy ENV npm_config_install_links=false -# Copy utilities and tools -COPY work /opt/utils/ - WORKDIR /opt/hermes # Install build-time system dependencies (compilers + native libs needed for Python extensions). # Without these, `uv sync` fails when compiling packages like `matrix-*-crypto`, `cryptography`, or `ffi`-based wheels on cold builds. RUN set -eux \ - && chmod +x /opt/utils/*.sh && mv /opt/utils/*hermes*.sh /opt/hermes/ \ - && printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\n' > /etc/apt/apt.conf.d/80-retries \ && apt-get -qq update -yq --fix-missing \ && apt-get -qq install -yq --no-install-recommends \ libffi-dev libolm-dev \ @@ -32,10 +27,6 @@ RUN set -eux \ && npm install -g playwright \ && playwright install --with-deps chromium --only-shell \ && npm cache clean --force \ - ## ---------- Python dependency install via uv (cached on manifests) ---------- - ## README.md is referenced by pyproject.toml but excluded by .dockerignore in source; - ## create a placeholder so uv's build frontend doesn't fail. - && touch ./README.md \ ## ---------- hack python-olm for building compatible wheels ---------- && mkdir -p /tmp/olm && cd /tmp/olm \ && curl -s https://pypi.org/pypi/python-olm/3.2.16/json \ @@ -45,17 +36,18 @@ RUN set -eux \ && cd python-olm-3.2.16 \ && sed -i 's/cmake_minimum_required(VERSION [0-9.]*)/cmake_minimum_required(VERSION 3.5)/' libolm/CMakeLists.txt \ && pip wheel . --no-build-isolation -w /tmp/olm/wheels \ - && pip install /tmp/olm/wheels/*.whl \ + && mv /tmp/olm/wheels/*olm*.whl /opt/hermes \ && cd /opt/hermes \ + && pip install ./*.whl \ ## ---------- uv sync (cached on manifests) ---------- && uv sync --frozen --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix || \ uv sync --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix -# ---------- Frontend build (web + ui-tui) ---------- +### ---------- Frontend build (web + ui-tui) ---------- RUN set -eux \ - && (cd web && npm run build) \ + && (cd web && npm run build) \ && (cd ui-tui && npm run build) \ - && mkdir -p hermes_cli/tui_dist && cp ui-tui/dist/entry.js hermes_cli/tui_dist/ \ + && mkdir -pv hermes_cli/tui_dist && cp ui-tui/dist/entry.js hermes_cli/tui_dist/ \ ## ---------- Link hermes-agent itself (editable, no deps) + install-method stamp ---------- && uv pip install --no-cache-dir --no-deps -e "." \ && mkdir -p /opt/hermes/bin \ @@ -65,7 +57,12 @@ RUN set -eux \ && chmod 0755 /opt/hermes/bin/hermes \ && printf 'docker\n' > /opt/hermes/.install_method -# --- Runtime Stage --- +# Copy utilities and tools +COPY work /opt/utils/ +RUN chmod +x /opt/utils/*.sh && mv /opt/utils/*hermes*.sh /opt/hermes/ + + +### --- Runtime Stage --- FROM ${BASE_NAMESPACE:+$BASE_NAMESPACE/}${BASE_IMG} LABEL maintainer="postmaster@labnow.ai" @@ -85,15 +82,14 @@ WORKDIR /root/workspace # Copy the full hermes install tree from the builder (venv + source + browsers + built frontends) COPY --from=builder /opt/hermes /opt/hermes - # Discover the real python site-packages so legacy env-var fallbacks point at the right tree. # Keep explicit versioned fallbacks around in case detection runs before the first pip install. RUN set -eux \ && ln -sf /opt/hermes/start-hermes.sh /usr/local/bin/start-hermes.sh \ && ln -sf /opt/hermes/healthcheck-hermes.sh /usr/local/bin/healthcheck-hermes.sh \ + && source /opt/utils/script-setup-sys.sh && setup_supervisord \ ## Runtime APT deps (hermes needs libolm for matrix, ffmpeg for voice, ripgrep for FTS, etc.) && printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\n' > /etc/apt/apt.conf.d/80-retries \ - && source /opt/utils/script-setup-sys.sh && setup_supervisord \ && source /opt/utils/script-utils.sh && install_apt /opt/utils/install_list_hermes.apt \ ## Detect the real hermes_cli location inside the venv and record it in a profile.d snippet ## so start-hermes.sh's auto-detect always finds the built frontends. diff --git a/docker_hermes/work/install_list_hermes.apt b/docker_hermes/work/install_list_hermes.apt index 48ac66a..021c621 100644 --- a/docker_hermes/work/install_list_hermes.apt +++ b/docker_hermes/work/install_list_hermes.apt @@ -1,9 +1,9 @@ +libffi-dev +libolm-dev iputils-ping +xz-utils ripgrep ffmpeg -libffi-dev -libolm-dev procps openssh-client docker-cli -xz-utils From 96b19e3228e1c35542613020480f258e9011b379 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Fri, 24 Jul 2026 17:27:24 +0000 Subject: [PATCH 07/18] debug uv install --- docker_hermes/hermes.Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index 6661a1a..c63894c 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -11,6 +11,7 @@ FROM ${BASE_NAMESPACE:+$BASE_NAMESPACE/}${BASE_IMG_BUILD} AS builder ENV NODE_ENV=development ENV UV_LINK_MODE=copy ENV npm_config_install_links=false +ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright WORKDIR /opt/hermes @@ -39,6 +40,7 @@ RUN set -eux \ && mv /tmp/olm/wheels/*olm*.whl /opt/hermes \ && cd /opt/hermes \ && pip install ./*.whl \ + && rm ./uv.lock \ ## ---------- uv sync (cached on manifests) ---------- && uv sync --frozen --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix || \ uv sync --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix From ae6639a3f4573d876dc8e733cc5299a696fbf058 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Fri, 24 Jul 2026 17:46:18 +0000 Subject: [PATCH 08/18] debug hermes install --- docker_hermes/hermes.Dockerfile | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index c63894c..fb07d5b 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -29,7 +29,8 @@ RUN set -eux \ && playwright install --with-deps chromium --only-shell \ && npm cache clean --force \ ## ---------- hack python-olm for building compatible wheels ---------- - && mkdir -p /tmp/olm && cd /tmp/olm \ + && mkdir -pv /opt/hermes/vendor \ + && mkdir -pv /tmp/olm && cd /tmp/olm \ && curl -s https://pypi.org/pypi/python-olm/3.2.16/json \ | jq -r '.urls[] | select(.packagetype=="sdist").url' \ | xargs curl -L -o python-olm-3.2.16.tar.gz \ @@ -37,14 +38,12 @@ RUN set -eux \ && cd python-olm-3.2.16 \ && sed -i 's/cmake_minimum_required(VERSION [0-9.]*)/cmake_minimum_required(VERSION 3.5)/' libolm/CMakeLists.txt \ && pip wheel . --no-build-isolation -w /tmp/olm/wheels \ - && mv /tmp/olm/wheels/*olm*.whl /opt/hermes \ + && mv /tmp/olm/wheels/*olm*.whl /opt/hermes/vendor/ \ && cd /opt/hermes \ - && pip install ./*.whl \ - && rm ./uv.lock \ - ## ---------- uv sync (cached on manifests) ---------- - && uv sync --frozen --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix || \ - uv sync --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix - + && pip install ./vendor/*.whl \ + && rm ./uv.lock + ## ---------- (hack finished) ---------- + ### ---------- Frontend build (web + ui-tui) ---------- RUN set -eux \ && (cd web && npm run build) \ @@ -52,6 +51,7 @@ RUN set -eux \ && mkdir -pv hermes_cli/tui_dist && cp ui-tui/dist/entry.js hermes_cli/tui_dist/ \ ## ---------- Link hermes-agent itself (editable, no deps) + install-method stamp ---------- && uv pip install --no-cache-dir --no-deps -e "." \ + --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix \ && mkdir -p /opt/hermes/bin \ && cp /opt/hermes/docker/hermes-exec-shim.sh /opt/hermes/bin/hermes 2>/dev/null || { \ printf '#!/usr/bin/env bash\nexec /opt/hermes/.venv/bin/hermes "$@"\n' > /opt/hermes/bin/hermes; \ @@ -73,11 +73,8 @@ LABEL maintainer="postmaster@labnow.ai" ENV NODE_ENV=production ENV HERMES_HOME=/root/workspace ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright -# Put the hermes venv at the front so `python3`, `hermes`, `uv`, etc. resolve to the managed install. -# This also lets `python3 -m scripts.*` / `python3 -m tools.*` in the seeding scripts find the hermes source tree via the appended PYTHONPATH. -ENV VIRTUAL_ENV=/opt/hermes/.venv ENV PYTHONPATH="/opt/hermes:${PYTHONPATH:-}" -ENV PATH="/opt/hermes/.venv/bin:/opt/hermes/bin:/opt/node/bin:/opt/conda/bin:/root/.local/bin:${PATH}" +ENV PATH="/opt/hermes/bin:/opt/node/bin:/opt/conda/bin:/root/.local/bin:${PATH}" ENV HOME=/root/workspace WORKDIR /root/workspace @@ -90,8 +87,6 @@ RUN set -eux \ && ln -sf /opt/hermes/start-hermes.sh /usr/local/bin/start-hermes.sh \ && ln -sf /opt/hermes/healthcheck-hermes.sh /usr/local/bin/healthcheck-hermes.sh \ && source /opt/utils/script-setup-sys.sh && setup_supervisord \ - ## Runtime APT deps (hermes needs libolm for matrix, ffmpeg for voice, ripgrep for FTS, etc.) - && printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\n' > /etc/apt/apt.conf.d/80-retries \ && source /opt/utils/script-utils.sh && install_apt /opt/utils/install_list_hermes.apt \ ## Detect the real hermes_cli location inside the venv and record it in a profile.d snippet ## so start-hermes.sh's auto-detect always finds the built frontends. From da7b7a1bc054704efc0cfc36b826378d117615af Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Fri, 24 Jul 2026 17:51:20 +0000 Subject: [PATCH 09/18] debug hermes build --- docker_hermes/hermes.Dockerfile | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index fb07d5b..10d3eff 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -39,9 +39,8 @@ RUN set -eux \ && sed -i 's/cmake_minimum_required(VERSION [0-9.]*)/cmake_minimum_required(VERSION 3.5)/' libolm/CMakeLists.txt \ && pip wheel . --no-build-isolation -w /tmp/olm/wheels \ && mv /tmp/olm/wheels/*olm*.whl /opt/hermes/vendor/ \ - && cd /opt/hermes \ - && pip install ./vendor/*.whl \ - && rm ./uv.lock + && cd /opt/hermes && rm ./uv.lock \ + && uv pip install ./vendor/*.whl ## ---------- (hack finished) ---------- ### ---------- Frontend build (web + ui-tui) ---------- @@ -50,6 +49,7 @@ RUN set -eux \ && (cd ui-tui && npm run build) \ && mkdir -pv hermes_cli/tui_dist && cp ui-tui/dist/entry.js hermes_cli/tui_dist/ \ ## ---------- Link hermes-agent itself (editable, no deps) + install-method stamp ---------- + && cd /opt/hermes \ && uv pip install --no-cache-dir --no-deps -e "." \ --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix \ && mkdir -p /opt/hermes/bin \ @@ -71,19 +71,22 @@ LABEL maintainer="postmaster@labnow.ai" # Production environment ENV NODE_ENV=production -ENV HERMES_HOME=/root/workspace +ENV HERMES_HOME=/opt/hermes ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright ENV PYTHONPATH="/opt/hermes:${PYTHONPATH:-}" ENV PATH="/opt/hermes/bin:/opt/node/bin:/opt/conda/bin:/root/.local/bin:${PATH}" -ENV HOME=/root/workspace -WORKDIR /root/workspace # Copy the full hermes install tree from the builder (venv + source + browsers + built frontends) COPY --from=builder /opt/hermes /opt/hermes +WORKDIR /opt/hermes + # Discover the real python site-packages so legacy env-var fallbacks point at the right tree. # Keep explicit versioned fallbacks around in case detection runs before the first pip install. RUN set -eux \ + && uv pip install ./vendor/*.whl \ + && uv pip install --no-cache-dir --no-deps -e "." \ + --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix \ && ln -sf /opt/hermes/start-hermes.sh /usr/local/bin/start-hermes.sh \ && ln -sf /opt/hermes/healthcheck-hermes.sh /usr/local/bin/healthcheck-hermes.sh \ && source /opt/utils/script-setup-sys.sh && setup_supervisord \ From a0c9f71ec18183f44e370c8ee86417d200a234a2 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Fri, 24 Jul 2026 18:16:14 +0000 Subject: [PATCH 10/18] debug hermes build --- docker_hermes/hermes.Dockerfile | 35 ++++++++++----------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index 10d3eff..a0200b0 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -23,11 +23,6 @@ RUN set -eux \ libffi-dev libolm-dev \ ## Clone source (full clone for reproducibility; depth 1 for speed) && git clone --depth 1 --branch main https://github.com/nousresearch/hermes-agent.git . \ - ## ---------- Node dependencies + Playwright (cached on manifests) ---------- - && npm install --prefer-offline --no-audit --fetch-retries=5 \ - && npm install -g playwright \ - && playwright install --with-deps chromium --only-shell \ - && npm cache clean --force \ ## ---------- hack python-olm for building compatible wheels ---------- && mkdir -pv /opt/hermes/vendor \ && mkdir -pv /tmp/olm && cd /tmp/olm \ @@ -40,22 +35,24 @@ RUN set -eux \ && pip wheel . --no-build-isolation -w /tmp/olm/wheels \ && mv /tmp/olm/wheels/*olm*.whl /opt/hermes/vendor/ \ && cd /opt/hermes && rm ./uv.lock \ + && printf '\n[tool.uv.sources]\npython-olm = { path = "vendor/python_olm-3.2.16-cp313-cp313-linux_x86_64.whl" }\n' >> pyproject.toml \ && uv pip install ./vendor/*.whl ## ---------- (hack finished) ---------- ### ---------- Frontend build (web + ui-tui) ---------- RUN set -eux \ + ## ---------- Node dependencies + Playwright (cached on manifests) ---------- + && npm install --prefer-offline --no-audit --fetch-retries=5 \ + && npm install -g playwright && playwright install --with-deps chromium --only-shell \ + && npm cache clean --force \ && (cd web && npm run build) \ && (cd ui-tui && npm run build) \ && mkdir -pv hermes_cli/tui_dist && cp ui-tui/dist/entry.js hermes_cli/tui_dist/ \ ## ---------- Link hermes-agent itself (editable, no deps) + install-method stamp ---------- && cd /opt/hermes \ - && uv pip install --no-cache-dir --no-deps -e "." \ - --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix \ - && mkdir -p /opt/hermes/bin \ - && cp /opt/hermes/docker/hermes-exec-shim.sh /opt/hermes/bin/hermes 2>/dev/null || { \ - printf '#!/usr/bin/env bash\nexec /opt/hermes/.venv/bin/hermes "$@"\n' > /opt/hermes/bin/hermes; \ - } \ + && uv pip install -e ".[all,messaging,anthropic,bedrock,azure-identity,hindsight,matrix]" \ + && mkdir -pv /opt/hermes/bin \ + && ln -sf /opt/hermes/docker/hermes-exec-shim.sh /opt/hermes/bin/hermes \ && chmod 0755 /opt/hermes/bin/hermes \ && printf 'docker\n' > /opt/hermes/.install_method @@ -63,7 +60,6 @@ RUN set -eux \ COPY work /opt/utils/ RUN chmod +x /opt/utils/*.sh && mv /opt/utils/*hermes*.sh /opt/hermes/ - ### --- Runtime Stage --- FROM ${BASE_NAMESPACE:+$BASE_NAMESPACE/}${BASE_IMG} @@ -76,7 +72,7 @@ ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright ENV PYTHONPATH="/opt/hermes:${PYTHONPATH:-}" ENV PATH="/opt/hermes/bin:/opt/node/bin:/opt/conda/bin:/root/.local/bin:${PATH}" -# Copy the full hermes install tree from the builder (venv + source + browsers + built frontends) +# Copy the full hermes install tree from the builder (source + browsers + built frontends) COPY --from=builder /opt/hermes /opt/hermes WORKDIR /opt/hermes @@ -84,22 +80,11 @@ WORKDIR /opt/hermes # Discover the real python site-packages so legacy env-var fallbacks point at the right tree. # Keep explicit versioned fallbacks around in case detection runs before the first pip install. RUN set -eux \ - && uv pip install ./vendor/*.whl \ - && uv pip install --no-cache-dir --no-deps -e "." \ - --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight --extra matrix \ + && uv pip install -e ".[all,messaging,anthropic,bedrock,azure-identity,hindsight,matrix]" \ && ln -sf /opt/hermes/start-hermes.sh /usr/local/bin/start-hermes.sh \ && ln -sf /opt/hermes/healthcheck-hermes.sh /usr/local/bin/healthcheck-hermes.sh \ && source /opt/utils/script-setup-sys.sh && setup_supervisord \ && source /opt/utils/script-utils.sh && install_apt /opt/utils/install_list_hermes.apt \ - ## Detect the real hermes_cli location inside the venv and record it in a profile.d snippet - ## so start-hermes.sh's auto-detect always finds the built frontends. - && VENV_PY=$(/opt/hermes/.venv/bin/python3 -c "import hermes_cli, pathlib; print(pathlib.Path(hermes_cli.__file__).resolve().parent)" 2>/dev/null || true) \ - && if [ -n "$VENV_PY" ]; then \ - echo "Detected hermes_cli at: $VENV_PY"; \ - mkdir -p /etc/profile.d; \ - printf 'export HERMES_WEB_DIST=%s/web_dist\nexport HERMES_TUI_DIR=%s/tui_dist\n' "$VENV_PY" "$VENV_PY" > /etc/profile.d/hermes-paths.sh; \ - chmod +x /etc/profile.d/hermes-paths.sh; \ - fi \ && install__clean # Data persistence is owned by the runtime orchestrator. From 75ac4aba9d5bfeb414e80586675f3f6853bc552c Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Fri, 24 Jul 2026 18:20:52 +0000 Subject: [PATCH 11/18] debug python-olm --- docker_hermes/hermes.Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index a0200b0..db3ce11 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -80,6 +80,7 @@ WORKDIR /opt/hermes # Discover the real python site-packages so legacy env-var fallbacks point at the right tree. # Keep explicit versioned fallbacks around in case detection runs before the first pip install. RUN set -eux \ + && uv pip install ./vendor/*.whl \ && uv pip install -e ".[all,messaging,anthropic,bedrock,azure-identity,hindsight,matrix]" \ && ln -sf /opt/hermes/start-hermes.sh /usr/local/bin/start-hermes.sh \ && ln -sf /opt/hermes/healthcheck-hermes.sh /usr/local/bin/healthcheck-hermes.sh \ From 3cf970ba27e01e810811930647d16356118b0273 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Fri, 24 Jul 2026 18:35:27 +0000 Subject: [PATCH 12/18] code clean --- docker_hermes/hermes.Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index db3ce11..514cc27 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -80,12 +80,12 @@ WORKDIR /opt/hermes # Discover the real python site-packages so legacy env-var fallbacks point at the right tree. # Keep explicit versioned fallbacks around in case detection runs before the first pip install. RUN set -eux \ + && source /opt/utils/script-utils.sh && install_apt /opt/utils/install_list_hermes.apt \ && uv pip install ./vendor/*.whl \ && uv pip install -e ".[all,messaging,anthropic,bedrock,azure-identity,hindsight,matrix]" \ - && ln -sf /opt/hermes/start-hermes.sh /usr/local/bin/start-hermes.sh \ - && ln -sf /opt/hermes/healthcheck-hermes.sh /usr/local/bin/healthcheck-hermes.sh \ + && ln -sf /opt/hermes/*hermes*.sh /usr/local/bin/start-hermes.sh \ + && ln -sf /opt/hermes/bin/*hermes*.sh /usr/local/bin/healthcheck-hermes.sh \ && source /opt/utils/script-setup-sys.sh && setup_supervisord \ - && source /opt/utils/script-utils.sh && install_apt /opt/utils/install_list_hermes.apt \ && install__clean # Data persistence is owned by the runtime orchestrator. From 91079c4709a6f1016ddf3d24cce8671d7f413414 Mon Sep 17 00:00:00 2001 From: haobibo <1.59735e+06+haobibo@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:59:48 +0000 Subject: [PATCH 13/18] fix(hermes): fix Docker build by adding missing scripts and update data persistence to /root/.hermes --- docker_hermes/README.md | 6 +- docker_hermes/demo/docker-compose.yml | 4 +- docker_hermes/hermes.Dockerfile | 13 ++-- docker_hermes/work/script-setup-sys.sh | 55 +++++++++++++++ docker_hermes/work/script-utils.sh | 94 ++++++++++++++++++++++++++ docker_hermes/work/start-hermes.sh | 6 +- docker_hermes/work/supervisord.sh | 2 +- 7 files changed, 164 insertions(+), 16 deletions(-) create mode 100644 docker_hermes/work/script-setup-sys.sh create mode 100644 docker_hermes/work/script-utils.sh diff --git a/docker_hermes/README.md b/docker_hermes/README.md index 83efb5f..f3c06ac 100644 --- a/docker_hermes/README.md +++ b/docker_hermes/README.md @@ -25,9 +25,9 @@ The Hermes Agent container hosts services on the following port: Hermes persists session data, memory, custom skills, and configurations under: -- **`/root/workspace`**: Sourced home directory for all agent states. Must be mounted via Docker Compose or a workspace volume. +- **`/root/.hermes`**: Sourced home directory for all agent states. Must be mounted via Docker Compose or a workspace volume. -### Subdirectories under `/root/workspace`: +### Subdirectories under `/root/.hermes`: - `sessions/`, `memories/` - Database and conversation history. - `skills/`, `plans/` - Executable custom agent skills and plan workflows. - `config.yaml`, `.env` - Main configuration and environment profiles. @@ -107,7 +107,7 @@ docker run -d \ --name svc-hermes \ --hostname hermes \ -p 9119:9119 \ - -v /path/to/your/data:/root/workspace \ + -v /path/to/your/data:/root/.hermes \ -e HERMES_DASHBOARD=true \ quay.io/labnow/hermes:local ``` diff --git a/docker_hermes/demo/docker-compose.yml b/docker_hermes/demo/docker-compose.yml index 600a39b..d17eacd 100644 --- a/docker_hermes/demo/docker-compose.yml +++ b/docker_hermes/demo/docker-compose.yml @@ -11,7 +11,7 @@ services: command: ["start-hermes.sh", "all"] environment: TZ: ${TZ:-Asia/Shanghai} - HERMES_HOME: /root/workspace + HERMES_HOME: /root/.hermes HERMES_DASHBOARD: ${HERMES_DASHBOARD:-true} HERMES_DASHBOARD_HOST: ${HERMES_DASHBOARD_HOST:-0.0.0.0} HERMES_DASHBOARD_PORT: ${HERMES_DASHBOARD_PORT:-9119} @@ -36,7 +36,7 @@ services: start_period: 20s retries: 3 volumes: - - ${HERMES_DATA_DIR:-../../.data/hermes}:/root/workspace + - ${HERMES_DATA_DIR:-../../.data/hermes}:/root/.hermes logging: driver: "json-file" options: diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index 514cc27..446f166 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -67,29 +67,28 @@ LABEL maintainer="postmaster@labnow.ai" # Production environment ENV NODE_ENV=production -ENV HERMES_HOME=/opt/hermes +ENV HERMES_HOME=/root/.hermes ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright ENV PYTHONPATH="/opt/hermes:${PYTHONPATH:-}" ENV PATH="/opt/hermes/bin:/opt/node/bin:/opt/conda/bin:/root/.local/bin:${PATH}" - +ENV HOME=/root/.hermes # Copy the full hermes install tree from the builder (source + browsers + built frontends) COPY --from=builder /opt/hermes /opt/hermes - -WORKDIR /opt/hermes +WORKDIR /root/.hermes # Discover the real python site-packages so legacy env-var fallbacks point at the right tree. # Keep explicit versioned fallbacks around in case detection runs before the first pip install. RUN set -eux \ - && source /opt/utils/script-utils.sh && install_apt /opt/utils/install_list_hermes.apt \ + && . /opt/utils/script-utils.sh && install_apt /opt/utils/install_list_hermes.apt \ && uv pip install ./vendor/*.whl \ && uv pip install -e ".[all,messaging,anthropic,bedrock,azure-identity,hindsight,matrix]" \ && ln -sf /opt/hermes/*hermes*.sh /usr/local/bin/start-hermes.sh \ && ln -sf /opt/hermes/bin/*hermes*.sh /usr/local/bin/healthcheck-hermes.sh \ - && source /opt/utils/script-setup-sys.sh && setup_supervisord \ + && . /opt/utils/script-setup-sys.sh && setup_supervisord \ && install__clean # Data persistence is owned by the runtime orchestrator. -# Compose and external workspace wrappers must provide the explicit `/root/workspace` mount. +# Compose and external workspace wrappers must provide the explicit `${HERMES_HOME}` mount. # Standalone containers keep the historical gateway+dashboard behavior. # The labnow-open wrapper calls start-hermes.sh with explicit gateway/dashboard modes and therefore does not use this CMD. diff --git a/docker_hermes/work/script-setup-sys.sh b/docker_hermes/work/script-setup-sys.sh new file mode 100644 index 0000000..5766768 --- /dev/null +++ b/docker_hermes/work/script-setup-sys.sh @@ -0,0 +1,55 @@ +source /opt/utils/script-utils.sh + + +setup_tini() { + ARCH=$(uname -m | sed -e 's/x86_64/amd64/' -e 's/aarch64/arm64/') \ + && local VER_TINI_REQ="${1:-}" \ + && local VERS_TINI=$(curl -sL "https://api.github.com/repos/krallin/tini/releases?per_page=50" | grep -Po '(?<="tag_name": ")[^"]+' | grep -Po '\d[\d.]+' | sort -rV) \ + && if [ -n "${VER_TINI_REQ}" ]; then + local VER_TINI_RE=${VER_TINI_REQ#v} \ + && VER_TINI_RE=${VER_TINI_RE//./\\.} \ + && local VER_TINI=$(echo "${VERS_TINI}" | grep -m1 -E "^${VER_TINI_RE}([.-]|$)") + else + local VER_TINI=$(echo "${VERS_TINI}" | head -1) + fi \ + && [ -n "${VER_TINI}" ] \ + && URL_TINI="https://github.com/krallin/tini/releases/download/v${VER_TINI}/tini-${ARCH}" \ + && echo "Downloading Tini ${VER_TINI} from ${URL_TINI}" \ + && curl -o /usr/bin/tini -sL $URL_TINI && chmod +x /usr/bin/tini ; + type tini && echo "@ Version of tini: $(tini --version)" || return -1 ; + # ref: https://cloud-atlas.readthedocs.io/zh-cn/latest/docker/init/docker_tini.html + # to run multi-proces with tini: use a bash script ends with the following code + # main() { *other code* /bin/bash -c "while true; do (echo 'Hello from tini'; date; sleep 120); done" } main +} + + +setup_supervisord() { + UNAME=$(uname | tr '[:upper:]' '[:lower:]') \ + && ARCH=$(uname -m | sed -e 's/x86_64/amd64/' -e 's/aarch64/arm64/') \ + && local VER_SUPERVISORD_REQ="${1:-}" \ + && local VERS_SUPERVISORD=$(curl -sL "https://api.github.com/repos/LabNow-ai/supervisord/releases?per_page=50" | grep -Po '(?<="tag_name": ")[^"]+' | grep -Po '(\d[\d|.]+)' | sort -rV) \ + && if [ -n "${VER_SUPERVISORD_REQ}" ]; then + local VER_SUPERVISORD_RE=${VER_SUPERVISORD_REQ#v} \ + && VER_SUPERVISORD_RE=${VER_SUPERVISORD_RE//./\\.} \ + && local VER_SUPERVISORD=$(echo "${VERS_SUPERVISORD}" | grep -m1 -E "^${VER_SUPERVISORD_RE}([.-]|$)") + else + local VER_SUPERVISORD=$(echo "${VERS_SUPERVISORD}" | head -1) + fi \ + && [ -n "${VER_SUPERVISORD}" ] \ + && URL_SUPERVISORD="https://github.com/LabNow-ai/supervisord/releases/download/v${VER_SUPERVISORD}/supervisord_${VER_SUPERVISORD}_${UNAME}_${ARCH}.tar.gz" \ + && echo "Downloading Supervisord ${VER_SUPERVISORD} from ${URL_SUPERVISORD}" \ + && curl -o /tmp/TMP.tgz -sL $URL_SUPERVISORD && tar -C /tmp/ -xzf /tmp/TMP.tgz && rm /tmp/TMP.tgz \ + && mkdir -pv /opt/bin/ && mv /tmp/supervisord /opt/bin/ && ln -sf /opt/bin/supervisord /usr/local/bin/ ; + type supervisord && echo "@ Version of supervisord: $(supervisord version)" || return -1 ; +} + + +setup_systemd() { + apt-get -qq update -yq --fix-missing \ + && apt-get -qq install -yq --no-install-recommends systemd systemd-cron \ + && rm -f /lib/systemd/system/systemd*udev* \ + && rm -f /lib/systemd/system/getty.target + # ref: https://cloud-atlas.readthedocs.io/zh_CN/latest/docker/init/docker_systemd.html + # ENTRYPOINT [ "/usr/lib/systemd/systemd" ] + # CMD [ "log-level=info", "unit=sysinit.target" ] +} diff --git a/docker_hermes/work/script-utils.sh b/docker_hermes/work/script-utils.sh new file mode 100644 index 0000000..fc91d08 --- /dev/null +++ b/docker_hermes/work/script-utils.sh @@ -0,0 +1,94 @@ +# shell util functions + +# function to debug, resolve package names from a text file and display. +install_echo() { cat $1 | cut -d "%" -f 1 | sed '/^$/d' | xargs -r -n1 printf '%s\n' ; } + +# function to install apt-get packages from a text file which lists package names (add comments with % char) +install_apt() { apt-get -qq update -yq --fix-missing && apt-get -qq install -yq --no-install-recommends $(cat "$1" | cut -d '%' -f 1) ; } + +# function to install conda packages from a text file which lists package names (add comments with % char) +install_conda() { cat $1 | cut -d "%" -f 1 | sed '/^$/d' | xargs -r -n1 conda install -yq ; } +install_mamba() { cat $1 | cut -d "%" -f 1 | sed '/^$/d' | xargs -r -n1 mamba install -yq --root-prefix="${CONDA_PREFIX}" --prefix="${CONDA_PREFIX}" ; } + +# function to install python packages with pip from a text file which lists package names (add comments with % char) +install_pip() { cat $1 | cut -d "%" -f 1 | sed '/^$/d' | xargs -r -n1 pip install --no-cache-dir --root-user-action=ignore -U --pre ; } + +# function to install R packages from a text file which lists package names (add comments with % char, use quiet=T to be less verbose) +install_R() { R -e "options(Ncpus=4);lapply(scan('$1','c',comment.char='%'),function(x){cat(x,system.time(install.packages(x,clean=T,quiet=T)),'\n')})"; } + +# function to install go packages with go from a text file which lists package names (add comments with % char) +install_go() { cat $1 | cut -d "%" -f 1 | sed '/^$/d' | xargs -r -n1 go get -u ; } + +# function to install julia packages from a text file which lists package names (add comments with % char) +install_julia() { julia -e "import Pkg; l=filter(x->length(x)>0, [split(i,r\"%| |\t\")[1] for i in readlines(\"$1\")]); Pkg.add(l)" ; } + +# function to install octave packages with go from a text file which lists package names (add comments with % char) +install_octave() { cat $1 | cut -d "%" -f 1 | sed '/^$/d' | xargs -r -n1 -I {} sh -c 'octave --eval "pkg install -forge {}"' ; } + +# function to download a ZIP file and unzip it to /opt/ +install_zip() { curl -o /tmp/TMP.zip -sL $1 && unzip -q -d /opt/ /tmp/TMP.zip && rm /tmp/TMP.zip ; } + +# function to download a .tar.gz file and unzip it to /opt/, add a second argument to extract only those file +install_tar_gz() { curl -o /tmp/TMP.tgz -sL $1 && tar -C /opt/ -xzf /tmp/TMP.tgz ${2:-} && rm /tmp/TMP.tgz ; } + +# function to download a .tar.bz file and unzip it to /opt/, add a second argument to extract only those file +install_tar_bz() { curl -o /tmp/TMP.tbz -sL $1 && tar -C /opt/ -xjf /tmp/TMP.tbz ${2:-} && rm /tmp/TMP.tbz ; } + +# function to download a .tar.xz file and unzip it to /opt/, add a second argument to extract only those file +install_tar_xz() { curl -o /tmp/TMP.txz -sL $1 && tar -C /opt/ -xJf /tmp/TMP.txz ${2:-} && rm /tmp/TMP.txz ; } + +# function to install java packages from a text file which lists JAR file maven full names (add comments with % char) +install_mvn() { cat $1 | cut -d "%" -f 1 | xargs -r -n1 -I {} mvn dependency:copy -DlocalRepositoryDirectory="/tmp/m2repo" -Djavax.net.ssl.trustStorePassword=changeit -Dartifact="{}" -DoutputDirectory="${2:-}" ; } + +# function to clean up +install__clean(){ + type apt-get && apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* ; + type npm && npm cache clean --force ; + type mamba && mamba clean -ya && rm -rf ~/micromamba ; + type conda && conda clean -ya && ( rm -rf "${CONDA_PREFIX:-/opt/conda}"/pkgs/* || true ) ; + sudo find "${CONDA_PREFIX:-/opt/conda}"/lib -type f -name "*.py[co]" -delete 2>/dev/null || true ; + sudo find "${CONDA_PREFIX:-/opt/conda}"/lib -type d -name "__pycache__" -prune -exec rm -rf {} + 2>/dev/null || true ; + + ( rm -rf /usr/share/doc /usr/share/man || true ) ; + ( rm -rf /tmp/.* /tmp/* /var/log/* /var/cache/* /root/.cache /root/.* || true ) && chmod ugo+rwXt /tmp ; + ls -alh /root /tmp ; + echo "@ System release info:" && cat /etc/*release* ; + echo "@ System environment variables:" && printenv | sort ; + echo "@ Version of image: building finished at:" $(date) ; + true +} + +# function to list installed packages +list_installed_packages() { + type pip && echo "@ Version of Python and packages:" && python --version && pip list + type conda && echo "@ Version of Conda and packages:" && conda info && conda list | grep -v "" + type mamba && echo "@ Version of Mamba and packages:" && mamba info && mamba list | grep -v "" + type node && echo "@ Version of NodeJS and packages:" && node --version && npm --version && npm list -g --depth 0 + type java && echo "@ Version of Java (JRE):" && java -version + type javac && echo "@ Version of Java (JDK):" && javac -version + type R && echo "@ Version of R and libraries:" && R --version && R -e "R.Version()\$version.string;installed.packages()[,c(3,10)]" + type julia && echo "@ Version of Julia and packages" && julia --version && julia -e "using Pkg; for(k,v) in Pkg.dependencies(); println(v.name,\"==\",v.version); end" + type go && echo "@ Version of golang and packages:" && go version && go list ... + type octave && echo "@ Version of Octave and packages:" && octave --version && octave --eval "pkg list" + true +} + +fix_permission() { + GROUP_ID=${1:-0}; shift 1; + for d in "$@"; do + find "${d}" \ + ! \( \ + -group "${GROUP_ID}" \ + -a -perm -g+rwX \ + \) \ + -exec chgrp "${GROUP_ID}" -- {} \+ \ + -exec chmod g+rwX -- {} \+ + # setuid, setgid *on directories only* + find "${d}" \ + \( \ + -type d \ + -a ! -perm -6000 \ + \) \ + -exec chmod +6000 -- {} \+; + done +} diff --git a/docker_hermes/work/start-hermes.sh b/docker_hermes/work/start-hermes.sh index b8e4086..f020793 100644 --- a/docker_hermes/work/start-hermes.sh +++ b/docker_hermes/work/start-hermes.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -eu -HERMES_HOME="${HERMES_HOME:-/root/workspace}" +HERMES_HOME="${HERMES_HOME:-/root/.hermes}" mkdir -p "$HERMES_HOME" bootstrap_lock_dir="$HERMES_HOME/.hermes-bootstrap.lock" @@ -124,8 +124,8 @@ if [ -z "${AGENT_BROWSER_EXECUTABLE_PATH:-}" ] && [ -n "${PLAYWRIGHT_BROWSERS_PA fi # Configure environments for command invocation -export HOME=/root/workspace -cd /root/workspace +export HOME="${HERMES_HOME}" +cd "${HERMES_HOME}" # Explicit service modes allow an outer supervisor (for example labnow-open) to # manage Hermes processes directly instead of starting a nested supervisor. diff --git a/docker_hermes/work/supervisord.sh b/docker_hermes/work/supervisord.sh index 87851d6..661e911 100644 --- a/docker_hermes/work/supervisord.sh +++ b/docker_hermes/work/supervisord.sh @@ -7,7 +7,7 @@ set -eu # Set up dashboard parameters dash_host="${HERMES_DASHBOARD_HOST:-0.0.0.0}" dash_port="${HERMES_DASHBOARD_PORT:-9119}" -hermes_home="${HERMES_HOME:-/root/workspace}" +hermes_home="${HERMES_HOME:-/root/.hermes}" insecure="" case "${HERMES_DASHBOARD_INSECURE:-}" in From 2ae093ed578afae41162355859b0fc55b743042d Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Fri, 24 Jul 2026 19:03:23 +0000 Subject: [PATCH 14/18] remove redundant files --- docker_hermes/work/script-setup-sys.sh | 55 --------------- docker_hermes/work/script-utils.sh | 94 -------------------------- 2 files changed, 149 deletions(-) delete mode 100644 docker_hermes/work/script-setup-sys.sh delete mode 100644 docker_hermes/work/script-utils.sh diff --git a/docker_hermes/work/script-setup-sys.sh b/docker_hermes/work/script-setup-sys.sh deleted file mode 100644 index 5766768..0000000 --- a/docker_hermes/work/script-setup-sys.sh +++ /dev/null @@ -1,55 +0,0 @@ -source /opt/utils/script-utils.sh - - -setup_tini() { - ARCH=$(uname -m | sed -e 's/x86_64/amd64/' -e 's/aarch64/arm64/') \ - && local VER_TINI_REQ="${1:-}" \ - && local VERS_TINI=$(curl -sL "https://api.github.com/repos/krallin/tini/releases?per_page=50" | grep -Po '(?<="tag_name": ")[^"]+' | grep -Po '\d[\d.]+' | sort -rV) \ - && if [ -n "${VER_TINI_REQ}" ]; then - local VER_TINI_RE=${VER_TINI_REQ#v} \ - && VER_TINI_RE=${VER_TINI_RE//./\\.} \ - && local VER_TINI=$(echo "${VERS_TINI}" | grep -m1 -E "^${VER_TINI_RE}([.-]|$)") - else - local VER_TINI=$(echo "${VERS_TINI}" | head -1) - fi \ - && [ -n "${VER_TINI}" ] \ - && URL_TINI="https://github.com/krallin/tini/releases/download/v${VER_TINI}/tini-${ARCH}" \ - && echo "Downloading Tini ${VER_TINI} from ${URL_TINI}" \ - && curl -o /usr/bin/tini -sL $URL_TINI && chmod +x /usr/bin/tini ; - type tini && echo "@ Version of tini: $(tini --version)" || return -1 ; - # ref: https://cloud-atlas.readthedocs.io/zh-cn/latest/docker/init/docker_tini.html - # to run multi-proces with tini: use a bash script ends with the following code - # main() { *other code* /bin/bash -c "while true; do (echo 'Hello from tini'; date; sleep 120); done" } main -} - - -setup_supervisord() { - UNAME=$(uname | tr '[:upper:]' '[:lower:]') \ - && ARCH=$(uname -m | sed -e 's/x86_64/amd64/' -e 's/aarch64/arm64/') \ - && local VER_SUPERVISORD_REQ="${1:-}" \ - && local VERS_SUPERVISORD=$(curl -sL "https://api.github.com/repos/LabNow-ai/supervisord/releases?per_page=50" | grep -Po '(?<="tag_name": ")[^"]+' | grep -Po '(\d[\d|.]+)' | sort -rV) \ - && if [ -n "${VER_SUPERVISORD_REQ}" ]; then - local VER_SUPERVISORD_RE=${VER_SUPERVISORD_REQ#v} \ - && VER_SUPERVISORD_RE=${VER_SUPERVISORD_RE//./\\.} \ - && local VER_SUPERVISORD=$(echo "${VERS_SUPERVISORD}" | grep -m1 -E "^${VER_SUPERVISORD_RE}([.-]|$)") - else - local VER_SUPERVISORD=$(echo "${VERS_SUPERVISORD}" | head -1) - fi \ - && [ -n "${VER_SUPERVISORD}" ] \ - && URL_SUPERVISORD="https://github.com/LabNow-ai/supervisord/releases/download/v${VER_SUPERVISORD}/supervisord_${VER_SUPERVISORD}_${UNAME}_${ARCH}.tar.gz" \ - && echo "Downloading Supervisord ${VER_SUPERVISORD} from ${URL_SUPERVISORD}" \ - && curl -o /tmp/TMP.tgz -sL $URL_SUPERVISORD && tar -C /tmp/ -xzf /tmp/TMP.tgz && rm /tmp/TMP.tgz \ - && mkdir -pv /opt/bin/ && mv /tmp/supervisord /opt/bin/ && ln -sf /opt/bin/supervisord /usr/local/bin/ ; - type supervisord && echo "@ Version of supervisord: $(supervisord version)" || return -1 ; -} - - -setup_systemd() { - apt-get -qq update -yq --fix-missing \ - && apt-get -qq install -yq --no-install-recommends systemd systemd-cron \ - && rm -f /lib/systemd/system/systemd*udev* \ - && rm -f /lib/systemd/system/getty.target - # ref: https://cloud-atlas.readthedocs.io/zh_CN/latest/docker/init/docker_systemd.html - # ENTRYPOINT [ "/usr/lib/systemd/systemd" ] - # CMD [ "log-level=info", "unit=sysinit.target" ] -} diff --git a/docker_hermes/work/script-utils.sh b/docker_hermes/work/script-utils.sh deleted file mode 100644 index fc91d08..0000000 --- a/docker_hermes/work/script-utils.sh +++ /dev/null @@ -1,94 +0,0 @@ -# shell util functions - -# function to debug, resolve package names from a text file and display. -install_echo() { cat $1 | cut -d "%" -f 1 | sed '/^$/d' | xargs -r -n1 printf '%s\n' ; } - -# function to install apt-get packages from a text file which lists package names (add comments with % char) -install_apt() { apt-get -qq update -yq --fix-missing && apt-get -qq install -yq --no-install-recommends $(cat "$1" | cut -d '%' -f 1) ; } - -# function to install conda packages from a text file which lists package names (add comments with % char) -install_conda() { cat $1 | cut -d "%" -f 1 | sed '/^$/d' | xargs -r -n1 conda install -yq ; } -install_mamba() { cat $1 | cut -d "%" -f 1 | sed '/^$/d' | xargs -r -n1 mamba install -yq --root-prefix="${CONDA_PREFIX}" --prefix="${CONDA_PREFIX}" ; } - -# function to install python packages with pip from a text file which lists package names (add comments with % char) -install_pip() { cat $1 | cut -d "%" -f 1 | sed '/^$/d' | xargs -r -n1 pip install --no-cache-dir --root-user-action=ignore -U --pre ; } - -# function to install R packages from a text file which lists package names (add comments with % char, use quiet=T to be less verbose) -install_R() { R -e "options(Ncpus=4);lapply(scan('$1','c',comment.char='%'),function(x){cat(x,system.time(install.packages(x,clean=T,quiet=T)),'\n')})"; } - -# function to install go packages with go from a text file which lists package names (add comments with % char) -install_go() { cat $1 | cut -d "%" -f 1 | sed '/^$/d' | xargs -r -n1 go get -u ; } - -# function to install julia packages from a text file which lists package names (add comments with % char) -install_julia() { julia -e "import Pkg; l=filter(x->length(x)>0, [split(i,r\"%| |\t\")[1] for i in readlines(\"$1\")]); Pkg.add(l)" ; } - -# function to install octave packages with go from a text file which lists package names (add comments with % char) -install_octave() { cat $1 | cut -d "%" -f 1 | sed '/^$/d' | xargs -r -n1 -I {} sh -c 'octave --eval "pkg install -forge {}"' ; } - -# function to download a ZIP file and unzip it to /opt/ -install_zip() { curl -o /tmp/TMP.zip -sL $1 && unzip -q -d /opt/ /tmp/TMP.zip && rm /tmp/TMP.zip ; } - -# function to download a .tar.gz file and unzip it to /opt/, add a second argument to extract only those file -install_tar_gz() { curl -o /tmp/TMP.tgz -sL $1 && tar -C /opt/ -xzf /tmp/TMP.tgz ${2:-} && rm /tmp/TMP.tgz ; } - -# function to download a .tar.bz file and unzip it to /opt/, add a second argument to extract only those file -install_tar_bz() { curl -o /tmp/TMP.tbz -sL $1 && tar -C /opt/ -xjf /tmp/TMP.tbz ${2:-} && rm /tmp/TMP.tbz ; } - -# function to download a .tar.xz file and unzip it to /opt/, add a second argument to extract only those file -install_tar_xz() { curl -o /tmp/TMP.txz -sL $1 && tar -C /opt/ -xJf /tmp/TMP.txz ${2:-} && rm /tmp/TMP.txz ; } - -# function to install java packages from a text file which lists JAR file maven full names (add comments with % char) -install_mvn() { cat $1 | cut -d "%" -f 1 | xargs -r -n1 -I {} mvn dependency:copy -DlocalRepositoryDirectory="/tmp/m2repo" -Djavax.net.ssl.trustStorePassword=changeit -Dartifact="{}" -DoutputDirectory="${2:-}" ; } - -# function to clean up -install__clean(){ - type apt-get && apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* ; - type npm && npm cache clean --force ; - type mamba && mamba clean -ya && rm -rf ~/micromamba ; - type conda && conda clean -ya && ( rm -rf "${CONDA_PREFIX:-/opt/conda}"/pkgs/* || true ) ; - sudo find "${CONDA_PREFIX:-/opt/conda}"/lib -type f -name "*.py[co]" -delete 2>/dev/null || true ; - sudo find "${CONDA_PREFIX:-/opt/conda}"/lib -type d -name "__pycache__" -prune -exec rm -rf {} + 2>/dev/null || true ; - - ( rm -rf /usr/share/doc /usr/share/man || true ) ; - ( rm -rf /tmp/.* /tmp/* /var/log/* /var/cache/* /root/.cache /root/.* || true ) && chmod ugo+rwXt /tmp ; - ls -alh /root /tmp ; - echo "@ System release info:" && cat /etc/*release* ; - echo "@ System environment variables:" && printenv | sort ; - echo "@ Version of image: building finished at:" $(date) ; - true -} - -# function to list installed packages -list_installed_packages() { - type pip && echo "@ Version of Python and packages:" && python --version && pip list - type conda && echo "@ Version of Conda and packages:" && conda info && conda list | grep -v "" - type mamba && echo "@ Version of Mamba and packages:" && mamba info && mamba list | grep -v "" - type node && echo "@ Version of NodeJS and packages:" && node --version && npm --version && npm list -g --depth 0 - type java && echo "@ Version of Java (JRE):" && java -version - type javac && echo "@ Version of Java (JDK):" && javac -version - type R && echo "@ Version of R and libraries:" && R --version && R -e "R.Version()\$version.string;installed.packages()[,c(3,10)]" - type julia && echo "@ Version of Julia and packages" && julia --version && julia -e "using Pkg; for(k,v) in Pkg.dependencies(); println(v.name,\"==\",v.version); end" - type go && echo "@ Version of golang and packages:" && go version && go list ... - type octave && echo "@ Version of Octave and packages:" && octave --version && octave --eval "pkg list" - true -} - -fix_permission() { - GROUP_ID=${1:-0}; shift 1; - for d in "$@"; do - find "${d}" \ - ! \( \ - -group "${GROUP_ID}" \ - -a -perm -g+rwX \ - \) \ - -exec chgrp "${GROUP_ID}" -- {} \+ \ - -exec chmod g+rwX -- {} \+ - # setuid, setgid *on directories only* - find "${d}" \ - \( \ - -type d \ - -a ! -perm -6000 \ - \) \ - -exec chmod +6000 -- {} \+; - done -} From 98f4d5955980872bea63236ea5f8b1b904e0b897 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Fri, 24 Jul 2026 19:04:10 +0000 Subject: [PATCH 15/18] typo --- docker_hermes/hermes.Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index 446f166..a62a45a 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -82,8 +82,8 @@ RUN set -eux \ && . /opt/utils/script-utils.sh && install_apt /opt/utils/install_list_hermes.apt \ && uv pip install ./vendor/*.whl \ && uv pip install -e ".[all,messaging,anthropic,bedrock,azure-identity,hindsight,matrix]" \ - && ln -sf /opt/hermes/*hermes*.sh /usr/local/bin/start-hermes.sh \ - && ln -sf /opt/hermes/bin/*hermes*.sh /usr/local/bin/healthcheck-hermes.sh \ + && ln -sf /opt/hermes/*hermes*.sh /usr/local/bin/ \ + && ln -sf /opt/hermes/bin/*hermes*.sh /usr/local/bin/ \ && . /opt/utils/script-setup-sys.sh && setup_supervisord \ && install__clean From cab1c627797c251a09b6fb7fef9747a11ca78fd9 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Sat, 25 Jul 2026 05:58:52 +0000 Subject: [PATCH 16/18] debug hermes install --- docker_hermes/hermes.Dockerfile | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index a62a45a..a319aa7 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -15,14 +15,16 @@ ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright WORKDIR /opt/hermes +# Copy utilities and tools +COPY work /opt/utils/ + # Install build-time system dependencies (compilers + native libs needed for Python extensions). # Without these, `uv sync` fails when compiling packages like `matrix-*-crypto`, `cryptography`, or `ffi`-based wheels on cold builds. RUN set -eux \ - && apt-get -qq update -yq --fix-missing \ - && apt-get -qq install -yq --no-install-recommends \ - libffi-dev libolm-dev \ + && . /opt/utils/script-utils.sh && install_apt /opt/utils/install_list_hermes.apt \ ## Clone source (full clone for reproducibility; depth 1 for speed) && git clone --depth 1 --branch main https://github.com/nousresearch/hermes-agent.git . \ + && chmod +x /opt/utils/*.sh && mv /opt/utils/*hermes*.sh /opt/utils/install_list_hermes.apt /opt/hermes/ \ ## ---------- hack python-olm for building compatible wheels ---------- && mkdir -pv /opt/hermes/vendor \ && mkdir -pv /tmp/olm && cd /tmp/olm \ @@ -56,10 +58,6 @@ RUN set -eux \ && chmod 0755 /opt/hermes/bin/hermes \ && printf 'docker\n' > /opt/hermes/.install_method -# Copy utilities and tools -COPY work /opt/utils/ -RUN chmod +x /opt/utils/*.sh && mv /opt/utils/*hermes*.sh /opt/hermes/ - ### --- Runtime Stage --- FROM ${BASE_NAMESPACE:+$BASE_NAMESPACE/}${BASE_IMG} @@ -79,7 +77,7 @@ WORKDIR /root/.hermes # Discover the real python site-packages so legacy env-var fallbacks point at the right tree. # Keep explicit versioned fallbacks around in case detection runs before the first pip install. RUN set -eux \ - && . /opt/utils/script-utils.sh && install_apt /opt/utils/install_list_hermes.apt \ + && . /opt/utils/script-utils.sh && install_apt /opt/hermes/install_list_hermes.apt \ && uv pip install ./vendor/*.whl \ && uv pip install -e ".[all,messaging,anthropic,bedrock,azure-identity,hindsight,matrix]" \ && ln -sf /opt/hermes/*hermes*.sh /usr/local/bin/ \ From 5f8751b484d2d2045f485d8e531f6772f7373340 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Sat, 25 Jul 2026 06:00:43 +0000 Subject: [PATCH 17/18] debug script names --- .../work/{supervisord.sh => start-hermes-supervisord.sh} | 0 docker_hermes/work/start-hermes.sh | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename docker_hermes/work/{supervisord.sh => start-hermes-supervisord.sh} (100%) diff --git a/docker_hermes/work/supervisord.sh b/docker_hermes/work/start-hermes-supervisord.sh similarity index 100% rename from docker_hermes/work/supervisord.sh rename to docker_hermes/work/start-hermes-supervisord.sh diff --git a/docker_hermes/work/start-hermes.sh b/docker_hermes/work/start-hermes.sh index f020793..8a0d26e 100644 --- a/docker_hermes/work/start-hermes.sh +++ b/docker_hermes/work/start-hermes.sh @@ -141,7 +141,7 @@ case "$1" in echo "[start-hermes] the all mode does not accept extra arguments" >&2 exit 2 fi - exec /opt/utils/supervisord.sh + exec /opt/hermes/start-hermes-supervisord.sh ;; gateway) shift From 4cdaa1a6daccf0841ebaec6bf7e5287b3ba9bbef Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Sat, 25 Jul 2026 06:13:54 +0000 Subject: [PATCH 18/18] fix workdir --- docker_hermes/hermes.Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker_hermes/hermes.Dockerfile b/docker_hermes/hermes.Dockerfile index a319aa7..5da15e9 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -72,11 +72,10 @@ ENV PATH="/opt/hermes/bin:/opt/node/bin:/opt/conda/bin:/root/.local/bin:${PATH}" ENV HOME=/root/.hermes # Copy the full hermes install tree from the builder (source + browsers + built frontends) COPY --from=builder /opt/hermes /opt/hermes -WORKDIR /root/.hermes # Discover the real python site-packages so legacy env-var fallbacks point at the right tree. # Keep explicit versioned fallbacks around in case detection runs before the first pip install. -RUN set -eux \ +RUN set -eux && cd /opt/hermes \ && . /opt/utils/script-utils.sh && install_apt /opt/hermes/install_list_hermes.apt \ && uv pip install ./vendor/*.whl \ && uv pip install -e ".[all,messaging,anthropic,bedrock,azure-identity,hindsight,matrix]" \ @@ -90,6 +89,7 @@ RUN set -eux \ # Standalone containers keep the historical gateway+dashboard behavior. # The labnow-open wrapper calls start-hermes.sh with explicit gateway/dashboard modes and therefore does not use this CMD. +WORKDIR /root/.hermes CMD ["start-hermes.sh", "all"] EXPOSE 9119 HEALTHCHECK --interval=10s --timeout=5s --start-period=40s --retries=5 \