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_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 f4f5573..5da15e9 100644 --- a/docker_hermes/hermes.Dockerfile +++ b/docker_hermes/hermes.Dockerfile @@ -1,72 +1,96 @@ # 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" # --- Building Stage --- -FROM ${BASE_NAMESPACE:+$BASE_NAMESPACE/}${BASE_IMG} AS builder +FROM ${BASE_NAMESPACE:+$BASE_NAMESPACE/}${BASE_IMG_BUILD} AS builder # Build-time environment ENV NODE_ENV=development -WORKDIR /build +ENV UV_LINK_MODE=copy +ENV npm_config_install_links=false +ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright -# Clone source -RUN git clone --depth 1 --branch main https://github.com/nousresearch/hermes-agent.git . +WORKDIR /opt/hermes -# Install Node dependencies and build frontend -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/ +# Copy utilities and tools +COPY work /opt/utils/ -# Build Python wheel +# 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 \ + && . /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 \ + && 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 \ + && 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 \ - && python3 -m pip install --upgrade pip build \ - && python3 -m build --wheel --outdir dist + ## ---------- 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 -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 -# --- Runtime Stage --- +### --- Runtime Stage --- FROM ${BASE_NAMESPACE:+$BASE_NAMESPACE/}${BASE_IMG} LABEL maintainer="postmaster@labnow.ai" # Production environment ENV NODE_ENV=production -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 HERMES_HOME=/root/.hermes ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright -ENV PATH="/opt/node/bin:/opt/conda/bin:/root/.local/bin:${PATH}" -ENV HOME=/root/workspace -WORKDIR /root/workspace +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 - -# Copy build artifacts from builder -COPY --from=builder /build/dist/*.whl /tmp/ - -# Copy utilities and tools -COPY work /opt/utils/ - -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 - && 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 \ +# 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 && 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]" \ + && 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 # 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. +WORKDIR /root/.hermes 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"] 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 diff --git a/docker_hermes/work/supervisord.sh b/docker_hermes/work/start-hermes-supervisord.sh similarity index 97% rename from docker_hermes/work/supervisord.sh rename to docker_hermes/work/start-hermes-supervisord.sh index 87851d6..661e911 100644 --- a/docker_hermes/work/supervisord.sh +++ b/docker_hermes/work/start-hermes-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 diff --git a/docker_hermes/work/start-hermes.sh b/docker_hermes/work/start-hermes.sh index b8e4086..8a0d26e 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. @@ -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 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"]