From 4a8dee1d68b380ec233c838422869f6e6566ed4c Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Thu, 28 May 2026 19:50:52 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20hermes=200.14.?= =?UTF-8?q?0=20=E6=A1=86=E6=9E=B6=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frameworks/hermes/0.14.0/Dockerfile | 38 +++++ frameworks/hermes/0.14.0/README.md | 138 +++++++++++++++ frameworks/hermes/0.14.0/build.conf | 4 + frameworks/hermes/0.14.0/docker/.env.example | 3 + frameworks/hermes/0.14.0/docker/SOUL.md | 15 ++ .../0.14.0/docker/cli-config.yaml.example | 4 + frameworks/hermes/0.14.0/docker/entrypoint.sh | 160 ++++++++++++++++++ frameworks/hermes/0.14.0/test.sh | 131 ++++++++++++++ 8 files changed, 493 insertions(+) create mode 100644 frameworks/hermes/0.14.0/Dockerfile create mode 100644 frameworks/hermes/0.14.0/README.md create mode 100644 frameworks/hermes/0.14.0/build.conf create mode 100644 frameworks/hermes/0.14.0/docker/.env.example create mode 100644 frameworks/hermes/0.14.0/docker/SOUL.md create mode 100644 frameworks/hermes/0.14.0/docker/cli-config.yaml.example create mode 100755 frameworks/hermes/0.14.0/docker/entrypoint.sh create mode 100755 frameworks/hermes/0.14.0/test.sh diff --git a/frameworks/hermes/0.14.0/Dockerfile b/frameworks/hermes/0.14.0/Dockerfile new file mode 100644 index 0000000..2f41ac8 --- /dev/null +++ b/frameworks/hermes/0.14.0/Dockerfile @@ -0,0 +1,38 @@ +FROM ghcr.io/astral-sh/uv:0.11.6-python3.13-trixie@sha256:b3c543b6c4f23a5f2df22866bd7857e5d304b67a564f4feab6ac22044dde719b AS uv_source +FROM tianon/gosu:1.19-trixie@sha256:3b176695959c71e123eb390d427efc665eeb561b1540e82679c15e992006b8b9 AS gosu_source +FROM opencloudos/opencloudos9-minimal:latest + +LABEL maintainer="OpenCloudOS Community" +LABEL org.opencontainers.image.source="https://gitee.com/OpenCloudOS/ai-agent-container" +LABEL org.opencontainers.image.description="Hermes Agent 0.14.0 on OpenCloudOS 9" + +ENV PYTHONUNBUFFERED=1 +ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright + +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/ + +# Install runtime + build deps, install Python packages, then strip build tools. +# Build-only RPMs (gcc, -devel, etc.) compile native extensions during pip install +# but are unused at runtime. Removing them within the same layer saves ~200 MB. +RUN dnf install -y \ + gcc gcc-c++ make python3-devel \ + curl nodejs20 nodejs20-npm ripgrep ffmpeg procps git openssh-clients tini xz \ + && uv pip install --no-cache-dir --system \ + "hermes-agent==0.14.0" \ + && dnf remove -y gcc gcc-c++ make python3-devel nodejs20-docs \ + && dnf clean all \ + && rm -rf /var/cache/yum/* /root/.cache/uv + +RUN useradd -u 10000 -m -d /opt/data hermes + +WORKDIR /opt/hermes + +COPY docker/entrypoint.sh docker/SOUL.md /opt/hermes/docker/ +COPY docker/.env.example docker/cli-config.yaml.example /opt/hermes/ + +ENV HERMES_HOME=/opt/data +ENV PATH="/opt/data/.local/bin:${PATH}" +VOLUME [ "/opt/data" ] + +ENTRYPOINT ["/usr/bin/tini", "-g", "--", "/opt/hermes/docker/entrypoint.sh"] diff --git a/frameworks/hermes/0.14.0/README.md b/frameworks/hermes/0.14.0/README.md new file mode 100644 index 0000000..d9af0a6 --- /dev/null +++ b/frameworks/hermes/0.14.0/README.md @@ -0,0 +1,138 @@ +# Hermes Agent 0.14.0 on OpenCloudOS 9 + +## 基本信息 +- **框架版本**:v0.14.0(上游 tag: v2026.5.16) +- **基础镜像**:opencloudos9-minimal:latest +- **Python 版本**:3.11.6 +- **CUDA 版本**:N/A +- **镜像大小**:~835MB + +## 简介 + +Hermes Agent 是 NousResearch 开发的自我改进 AI Agent,能够从经验中创建技能并在使用过程中改进,支持多种 LLM 提供商和 22+ 种消息平台。 + +本镜像基于 OpenCloudOS 9 minimal 构建,适用于 CPU 环境。 + +## 构建 + +```bash +docker build -t oc9-hermes:0.14.0 . +``` + +## 使用说明 + +详细的官方 Docker 使用文档请参考: +https://hermes-agent.nousresearch.com/docs/zh-Hans/user-guide/docker + +### 快速开始(首次运行设置) + +创建数据目录并运行设置向导(只需执行一次): + +```bash +mkdir -p ~/.hermes +docker run -it --rm \ + -v ~/.hermes:/opt/data \ + oc9-hermes:0.14.0 \ + python3 -m hermes_cli.main setup +``` + +设置向导会提示输入 API 密钥并将其写入 `~/.hermes/.env`。 + +### 交互式 CLI 聊天 + +对已有数据目录打开交互式聊天会话: + +```bash +docker run -it --rm \ + -v ~/.hermes:/opt/data \ + oc9-hermes:0.14.0 +``` + +### 以 Gateway 模式运行 + +配置完成后,将容器作为持久化 gateway(Telegram、Discord、Slack、WhatsApp 等)在后台运行: + +```bash +docker run -d \ + --name hermes \ + --restart unless-stopped \ + -v ~/.hermes:/opt/data \ + -p 8642:8642 \ + oc9-hermes:0.14.0 \ + python3 -m hermes_cli.main gateway run +``` + +端口 8642 暴露 gateway 的 OpenAI 兼容 API 服务器和健康检查端点。 + +启用 API 服务器(需设置 `API_SERVER_KEY`): + +```bash +docker run -d \ + --name hermes \ + --restart unless-stopped \ + -v ~/.hermes:/opt/data \ + -p 8642:8642 \ + -e API_SERVER_ENABLED=true \ + -e API_SERVER_HOST=0.0.0.0 \ + -e API_SERVER_KEY="$(openssl rand -hex 32)" \ + -e API_SERVER_CORS_ORIGINS='*' \ + oc9-hermes:0.14.0 \ + python3 -m hermes_cli.main gateway run +``` + +查看 gateway 状态: + +```bash +docker exec hermes python3 -m hermes_cli.main gateway status +``` + +### 运行 Dashboard + +内置 Web dashboard 作为可选的子进程运行。设置 `HERMES_DASHBOARD=1` 可在启动 gateway 时同时启动 dashboard: + +```bash +docker run -d \ + --name hermes \ + --restart unless-stopped \ + -v ~/.hermes:/opt/data \ + -p 8642:8642 \ + -e HERMES_DASHBOARD=1 \ + oc9-hermes:0.14.0 \ + python3 -m hermes_cli.main gateway run +``` + +Dashboard 环境变量: + +| 变量 | 描述 | 默认值 | +|------|------|--------| +| `HERMES_DASHBOARD` | 设为 `1` 以启动 dashboard | 未设置 | +| `HERMES_DASHBOARD_HOST` | 绑定地址 | 127.0.0.1 | +| `HERMES_DASHBOARD_PORT` | HTTP 端口 | 9119 | +| `HERMES_DASHBOARD_TUI` | 设为 `1` 启用浏览器内 Chat 标签页 | 未设置 | + +查看 dashboard 状态: + +```bash +docker exec hermes python3 -m hermes_cli.main dashboard --status +``` + +### 验证安装 + +```bash +# 查看版本 +docker run --rm oc9-hermes:0.14.0 python3 -m hermes_cli.main version + +# 运行诊断 +docker run --rm -e HERMES_HOME=/tmp/hermes_test oc9-hermes:0.14.0 \ + python3 -m hermes_cli.main doctor + +# 查看整体状态 +docker run --rm oc9-hermes:0.14.0 python3 -m hermes_cli.main status +``` + +## 数据持久化 + +容器将所有用户数据(配置、API 密钥、会话、技能、记忆)存储在 `/opt/data` 目录中。通过 `-v ~/.hermes:/opt/data` 挂载宿主机目录实现数据持久化。镜像本身是无状态的,可通过拉取新版本进行升级而不会丢失任何配置。 + +## 已知问题 +- (暂无) diff --git a/frameworks/hermes/0.14.0/build.conf b/frameworks/hermes/0.14.0/build.conf new file mode 100644 index 0000000..cd4cd16 --- /dev/null +++ b/frameworks/hermes/0.14.0/build.conf @@ -0,0 +1,4 @@ +# hermes-agent 0.14.0 on OpenCloudOS 9 +IMAGE_NAME=oc9-hermes +IMAGE_TAG=0.14.0 +GPU_TEST=false diff --git a/frameworks/hermes/0.14.0/docker/.env.example b/frameworks/hermes/0.14.0/docker/.env.example new file mode 100644 index 0000000..e654782 --- /dev/null +++ b/frameworks/hermes/0.14.0/docker/.env.example @@ -0,0 +1,3 @@ +# Hermes Agent Configuration +# Copy this file to ~/.hermes/.env and fill in your API keys. +# See https://hermes-agent.nousresearch.com/docs/zh-Hans/user-guide/docker diff --git a/frameworks/hermes/0.14.0/docker/SOUL.md b/frameworks/hermes/0.14.0/docker/SOUL.md new file mode 100644 index 0000000..9103a61 --- /dev/null +++ b/frameworks/hermes/0.14.0/docker/SOUL.md @@ -0,0 +1,15 @@ +# Hermes Agent Persona + + \ No newline at end of file diff --git a/frameworks/hermes/0.14.0/docker/cli-config.yaml.example b/frameworks/hermes/0.14.0/docker/cli-config.yaml.example new file mode 100644 index 0000000..604513e --- /dev/null +++ b/frameworks/hermes/0.14.0/docker/cli-config.yaml.example @@ -0,0 +1,4 @@ +# Hermes Agent CLI Configuration +# See https://hermes-agent.nousresearch.com/docs/zh-Hans/user-guide/configuration +model: "auto" +provider: "auto" diff --git a/frameworks/hermes/0.14.0/docker/entrypoint.sh b/frameworks/hermes/0.14.0/docker/entrypoint.sh new file mode 100755 index 0000000..7864b9b --- /dev/null +++ b/frameworks/hermes/0.14.0/docker/entrypoint.sh @@ -0,0 +1,160 @@ +#!/bin/bash +# Docker/Podman entrypoint: bootstrap config files into the mounted volume, then run hermes. +set -e + +HERMES_HOME="${HERMES_HOME:-/opt/data}" +INSTALL_DIR="/opt/hermes" + +# --- Privilege dropping via gosu --- +# When started as root (the default for Docker, or fakeroot in rootless Podman), +# optionally remap the hermes user/group to match host-side ownership, fix volume +# permissions, then re-exec as hermes. +if [ "$(id -u)" = "0" ]; then + if [ -n "$HERMES_UID" ] && [ "$HERMES_UID" != "$(id -u hermes)" ]; then + echo "Changing hermes UID to $HERMES_UID" + usermod -u "$HERMES_UID" hermes + fi + + if [ -n "$HERMES_GID" ] && [ "$HERMES_GID" != "$(id -g hermes)" ]; then + echo "Changing hermes GID to $HERMES_GID" + # -o allows non-unique GID (e.g. macOS GID 20 "staff" may already exist + # as "dialout" in the Debian-based container image) + groupmod -o -g "$HERMES_GID" hermes 2>/dev/null || true + fi + + # Fix ownership of the data volume. When HERMES_UID remaps the hermes user, + # files created by previous runs (under the old UID) become inaccessible. + # Always chown -R when UID was remapped; otherwise only if top-level is wrong. + actual_hermes_uid=$(id -u hermes) + needs_chown=false + if [ -n "$HERMES_UID" ] && [ "$HERMES_UID" != "10000" ]; then + needs_chown=true + elif [ "$(stat -c %u "$HERMES_HOME" 2>/dev/null)" != "$actual_hermes_uid" ]; then + needs_chown=true + fi + if [ "$needs_chown" = true ]; then + echo "Fixing ownership of $HERMES_HOME to hermes ($actual_hermes_uid)" + # In rootless Podman the container's "root" is mapped to an unprivileged + # host UID — chown will fail. That's fine: the volume is already owned + # by the mapped user on the host side. + chown -R hermes:hermes "$HERMES_HOME" 2>/dev/null || \ + echo "Warning: chown failed (rootless container?) — continuing anyway" + # The .venv must also be re-chowned when UID is remapped, otherwise + # lazy_deps.py cannot install platform packages (discord.py, etc.). + chown -R hermes:hermes "$INSTALL_DIR/.venv" 2>/dev/null || \ + echo "Warning: chown .venv failed (rootless container?) — continuing anyway" + fi + + # Ensure config.yaml is readable by the hermes runtime user even if it was + # edited on the host after initial ownership setup. Must run here (as root) + # rather than after the gosu drop, otherwise a non-root caller like + # `docker run -u $(id -u):$(id -g)` hits "Operation not permitted" (#15865). + if [ -f "$HERMES_HOME/config.yaml" ]; then + chown hermes:hermes "$HERMES_HOME/config.yaml" 2>/dev/null || true + chmod 640 "$HERMES_HOME/config.yaml" 2>/dev/null || true + fi + + echo "Dropping root privileges" + exec gosu hermes "$0" "$@" +fi + +# --- Running as hermes from here --- +# Activate venv if it exists (system-wide install uses --system, no venv) +if [ -f "${INSTALL_DIR}/.venv/bin/activate" ]; then + source "${INSTALL_DIR}/.venv/bin/activate" +fi + +# Create essential directory structure. Cache and platform directories +# (cache/images, cache/audio, platforms/whatsapp, etc.) are created on +# demand by the application — don't pre-create them here so new installs +# get the consolidated layout from get_hermes_dir(). +# The "home/" subdirectory is a per-profile HOME for subprocesses (git, +# ssh, gh, npm …). Without it those tools write to /root which is +# ephemeral and shared across profiles. See issue #4426. +mkdir -p "$HERMES_HOME"/{cron,sessions,logs,hooks,memories,skills,skins,plans,workspace,home} + +# .env +if [ ! -f "$HERMES_HOME/.env" ]; then + cp "$INSTALL_DIR/.env.example" "$HERMES_HOME/.env" +fi + +# config.yaml +if [ ! -f "$HERMES_HOME/config.yaml" ]; then + cp "$INSTALL_DIR/cli-config.yaml.example" "$HERMES_HOME/config.yaml" +fi + +# SOUL.md +if [ ! -f "$HERMES_HOME/SOUL.md" ]; then + cp "$INSTALL_DIR/docker/SOUL.md" "$HERMES_HOME/SOUL.md" +fi + +# auth.json: bootstrap from env on first boot only. Used by orchestrators +# (e.g. provisioning a Hermes VPS from an account-management service) that +# need to seed the OAuth refresh credential non-interactively, instead of +# walking the user through `hermes setup` + the device-flow login dance. +# Subsequent token rotations write back to the same file, which lives on a +# persistent volume — so this env var is consumed exactly once at first +# boot. The `[ ! -f ... ]` guard is critical: without it, a container +# restart would clobber a rotated refresh token with the now-stale value +# the orchestrator originally seeded. +if [ ! -f "$HERMES_HOME/auth.json" ] && [ -n "$HERMES_AUTH_JSON_BOOTSTRAP" ]; then + printf '%s' "$HERMES_AUTH_JSON_BOOTSTRAP" > "$HERMES_HOME/auth.json" + chmod 600 "$HERMES_HOME/auth.json" +fi + +# Sync bundled skills (manifest-based so user edits are preserved) +if [ -d "$INSTALL_DIR/skills" ]; then + python3 "$INSTALL_DIR/tools/skills_sync.py" +fi + +# Optionally start `hermes dashboard` as a side-process. +# +# Toggled by HERMES_DASHBOARD=1 (also accepts "true"/"yes", case-insensitive). +# Host/port/TUI can be overridden via: +# HERMES_DASHBOARD_HOST (default 0.0.0.0 — exposed outside the container) +# HERMES_DASHBOARD_PORT (default 9119, matches `hermes dashboard` default) +# HERMES_DASHBOARD_TUI (already honored by `hermes dashboard` itself) +# +# The dashboard is a long-lived server. We background it *before* the final +# `exec hermes "$@"` so the user's chosen foreground command (chat, gateway, +# sleep infinity, …) remains PID-of-interest for the container runtime. When +# the container stops the whole process tree is torn down, so no explicit +# cleanup is needed. +case "${HERMES_DASHBOARD:-}" in + 1|true|TRUE|True|yes|YES|Yes) + dash_host="${HERMES_DASHBOARD_HOST:-0.0.0.0}" + dash_port="${HERMES_DASHBOARD_PORT:-9119}" + dash_args=(--host "$dash_host" --port "$dash_port" --no-open) + # Binding to anything other than localhost requires --insecure — the + # dashboard refuses otherwise because it exposes API keys. Inside a + # container this is the expected deployment (host reaches it via + # published port), so opt in automatically. + if [ "$dash_host" != "127.0.0.1" ] && [ "$dash_host" != "localhost" ]; then + dash_args+=(--insecure) + fi + echo "Starting hermes dashboard on ${dash_host}:${dash_port} (background)" + # Prefix dashboard output so it's distinguishable from the main + # process in `docker logs`. stdbuf keeps the pipe line-buffered. + ( + stdbuf -oL -eL hermes dashboard "${dash_args[@]}" 2>&1 \ + | sed -u 's/^/[dashboard] /' + ) & + ;; +esac + +# Final exec: two supported invocation patterns. +# +# docker run -> exec `hermes` with no args (legacy default) +# docker run chat -q "..." -> exec `hermes chat -q "..."` (legacy wrap) +# docker run sleep infinity -> exec `sleep infinity` directly +# docker run bash -> exec `bash` directly +# +# If the first positional arg resolves to an executable on PATH, we assume the +# caller wants to run it directly (needed by the launcher which runs long-lived +# `sleep infinity` sandbox containers — see tools/environments/docker.py). +# Otherwise we treat the args as a hermes subcommand and wrap with `hermes`, +# preserving the documented `docker run ` behavior. +if [ $# -gt 0 ] && command -v "$1" >/dev/null 2>&1; then + exec "$@" +fi +exec hermes "$@" diff --git a/frameworks/hermes/0.14.0/test.sh b/frameworks/hermes/0.14.0/test.sh new file mode 100755 index 0000000..0196f7c --- /dev/null +++ b/frameworks/hermes/0.14.0/test.sh @@ -0,0 +1,131 @@ +#!/bin/bash +set -e + +IMAGE="${1:?ERROR: 缺少镜像参数。用法: bash test.sh }" + +echo "=== Hermes Agent 0.14.0 命令行功能测试 ===" +echo "镜像: ${IMAGE}" +echo "" + +# --------------------------------------------------------------------------- +# Test 1 — Python 版本检查 +# --------------------------------------------------------------------------- +echo -n "[1/9] 检查 Python 3.11+ ... " +docker run --rm "$IMAGE" python3 -c " +import sys +assert sys.version_info >= (3, 11), '需要 Python >= 3.11, 当前: ' + str(sys.version) +print('Python {}.{}.{}'.format(*sys.version_info[:3])) +" && echo "✓ 通过" || { echo "✗ 失败"; exit 1; } + +# --------------------------------------------------------------------------- +# Test 2 — tini init 系统可用 +# --------------------------------------------------------------------------- +echo -n "[2/9] 检查 tini init 系统 ... " +docker run --rm "$IMAGE" tini --version 2>&1 | grep -q tini && echo "✓ 通过" || { echo "✗ 失败"; exit 1; } + +# --------------------------------------------------------------------------- +# Test 3 — hermes version 输出正确版本号 +# --------------------------------------------------------------------------- +echo -n "[3/9] 检查 hermes version ... " +docker run --rm "$IMAGE" hermes version 2>&1 | grep -q "Hermes Agent v0.14.0" && echo "✓ 通过" || { echo "✗ 失败"; exit 1; } + +# --------------------------------------------------------------------------- +# Test 4 — gateway --help 列出子命令 +# --------------------------------------------------------------------------- +echo -n "[4/9] 检查 hermes gateway --help ... " +docker run --rm "$IMAGE" hermes gateway --help 2>&1 | grep -q "run\|status\|start" && echo "✓ 通过" || { echo "✗ 失败"; exit 1; } + +# --------------------------------------------------------------------------- +# Test 5 — gateway 进程启动 + 保持运行 (>5s 不崩溃) +# --------------------------------------------------------------------------- +echo -n "[5/9] 检查 gateway 进程启动并保持运行 ... " +CID=$(docker run -d --name hermes-gw-test \ + -e HERMES_HOME=/tmp/hermes_gw \ + -e API_SERVER_ENABLED=true -e API_SERVER_KEY=testkey12345 \ + -e API_SERVER_HOST=127.0.0.1 \ + "$IMAGE" hermes gateway run) +sleep 8 +# Gateway 必须在日志中打印启动消息 +docker logs hermes-gw-test 2>&1 | grep -q "Hermes Gateway Starting" || { + echo "✗ 失败 (日志中未找到启动消息)" + docker rm -f hermes-gw-test > /dev/null 2>&1 + exit 1 +} +# 容器必须在启动后保持运行(进程没有立即崩溃退出) +STATE=$(docker inspect hermes-gw-test --format '{{.State.Status}}' 2>&1) +if [ "$STATE" != "running" ]; then + echo "✗ 失败 (容器状态=$STATE, 期望=running)" + docker rm -f hermes-gw-test > /dev/null 2>&1 + exit 1 +fi +docker rm -f hermes-gw-test > /dev/null 2>&1 +echo "✓ 通过" + +# --------------------------------------------------------------------------- +# Test 6 — gateway status 报告运行状态 +# --------------------------------------------------------------------------- +echo -n "[6/9] 检查 gateway status 报告运行中 ... " +docker run -d --name hermes-gw-status \ + -e HERMES_HOME=/tmp/hermes_gw_status \ + "$IMAGE" hermes gateway run > /dev/null 2>&1 +sleep 5 +GW_STATUS=$(docker exec hermes-gw-status hermes gateway status 2>&1) +# 只要有 gateway 进程运行中就能看到 PID / running / 停止 等状态字样 +if echo "$GW_STATUS" | grep -qE "PID|pid|running|停止|stopped"; then + docker rm -f hermes-gw-status > /dev/null 2>&1 + echo "✓ 通过" +else + docker rm -f hermes-gw-status > /dev/null 2>&1 + echo "✗ 失败 (输出: $GW_STATUS)" + exit 1 +fi + +# --------------------------------------------------------------------------- +# Test 7 — dashboard --help 列出选项 +# --------------------------------------------------------------------------- +echo -n "[7/9] 检查 hermes dashboard --help ... " +docker run --rm "$IMAGE" hermes dashboard --help 2>&1 | grep -q "port\|host\|status" && echo "✓ 通过" || { echo "✗ 失败"; exit 1; } + +# --------------------------------------------------------------------------- +# Test 8 — dashboard 核心依赖(fastapi + uvicorn)已安装 +# hermes dashboard --status 在依赖缺失时会输出错误提示并退出; +# 依赖齐备时正常返回进程状态(可能为空)。 +# --------------------------------------------------------------------------- +echo -n "[8/9] 检查 dashboard 依赖正常 (fastapi+uvicorn) ... " +DASHBOARD_OUTPUT=$(docker run --rm -e HERMES_HOME=/tmp/hermes_dash "$IMAGE" hermes dashboard --status 2>&1) +if echo "$DASHBOARD_OUTPUT" | grep -qi "not installed\|missing\|need.*fastapi\|need.*uvicorn\|Re-install\|pip install\|ModuleNotFoundError"; then + echo "✗ 失败 (dashboard 依赖缺失)" + echo " 输出: $DASHBOARD_OUTPUT" + exit 1 +fi +# 正常情况:要么显示"没有运行中的实例",要么显示 PID +echo "$DASHBOARD_OUTPUT" | grep -qiE "dashboard|PID|process|running|not found|no.*running" && echo "✓ 通过" || { + echo "✗ 失败 (意外的输出: $DASHBOARD_OUTPUT)" + exit 1 +} + +# --------------------------------------------------------------------------- +# Test 9 — hermes doctor 无缺失依赖警告 +# --------------------------------------------------------------------------- +echo -n "[9/9] 检查 hermes doctor 无关键依赖缺失 ... " +DOCTOR_OUTPUT=$(docker run --rm -e HERMES_HOME=/tmp/hermes_doc "$IMAGE" timeout 30 hermes doctor 2>&1) +# 不应有"请重新安装"或"ModuleNotFoundError"这样的致命错误 +# 可选包(如 docker CLI、auth providers)可以正常显示"not installed" +if echo "$DOCTOR_OUTPUT" | grep -qi "Re-install\|ModuleNotFoundError\|pip install -e\."; then + echo "✗ 失败 (doctor 报告关键依赖缺失)" + echo " 输出: $DOCTOR_OUTPUT" + exit 1 +fi +# 确认核心包 (OpenAI SDK, Rich) 已通过 +if ! echo "$DOCTOR_OUTPUT" | grep -qi "OpenAI SDK"; then + echo "✗ 失败 (doctor 未找到 OpenAI SDK)" + echo " 输出: $DOCTOR_OUTPUT" + exit 1 +fi +echo "$DOCTOR_OUTPUT" | grep -qi "Hermes Doctor\|Python.*hermes" && echo "✓ 通过" || { + echo "✗ 失败 (未识别到 doctor 输出: $DOCTOR_OUTPUT)" + exit 1 +} + +echo "" +echo "=== 所有 9 项测试通过 ===" -- Gitee From 822176eede8aadccd064c604b121df08b4de0a85 Mon Sep 17 00:00:00 2001 From: samblade <812101+samblade@user.noreply.gitee.com> Date: Fri, 29 May 2026 02:22:11 +0000 Subject: [PATCH 2/2] update frameworks/hermes/0.14.0/README.md. Signed-off-by: samblade <812101+samblade@user.noreply.gitee.com> --- frameworks/hermes/0.14.0/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/frameworks/hermes/0.14.0/README.md b/frameworks/hermes/0.14.0/README.md index d9af0a6..5b0638a 100644 --- a/frameworks/hermes/0.14.0/README.md +++ b/frameworks/hermes/0.14.0/README.md @@ -5,7 +5,7 @@ - **基础镜像**:opencloudos9-minimal:latest - **Python 版本**:3.11.6 - **CUDA 版本**:N/A -- **镜像大小**:~835MB +- **镜像大小**:~1.38GB ## 简介 @@ -33,7 +33,7 @@ mkdir -p ~/.hermes docker run -it --rm \ -v ~/.hermes:/opt/data \ oc9-hermes:0.14.0 \ - python3 -m hermes_cli.main setup + hermes setup ``` 设置向导会提示输入 API 密钥并将其写入 `~/.hermes/.env`。 @@ -59,7 +59,7 @@ docker run -d \ -v ~/.hermes:/opt/data \ -p 8642:8642 \ oc9-hermes:0.14.0 \ - python3 -m hermes_cli.main gateway run + hermes gateway run ``` 端口 8642 暴露 gateway 的 OpenAI 兼容 API 服务器和健康检查端点。 @@ -77,13 +77,13 @@ docker run -d \ -e API_SERVER_KEY="$(openssl rand -hex 32)" \ -e API_SERVER_CORS_ORIGINS='*' \ oc9-hermes:0.14.0 \ - python3 -m hermes_cli.main gateway run + hermes gateway run ``` 查看 gateway 状态: ```bash -docker exec hermes python3 -m hermes_cli.main gateway status +docker exec hermes hermes gateway status ``` ### 运行 Dashboard @@ -98,7 +98,7 @@ docker run -d \ -p 8642:8642 \ -e HERMES_DASHBOARD=1 \ oc9-hermes:0.14.0 \ - python3 -m hermes_cli.main gateway run + hermes gateway run ``` Dashboard 环境变量: @@ -113,21 +113,21 @@ Dashboard 环境变量: 查看 dashboard 状态: ```bash -docker exec hermes python3 -m hermes_cli.main dashboard --status +docker exec hermes hermes dashboard --status ``` ### 验证安装 ```bash # 查看版本 -docker run --rm oc9-hermes:0.14.0 python3 -m hermes_cli.main version +docker run --rm oc9-hermes:0.14.0 hermes version # 运行诊断 docker run --rm -e HERMES_HOME=/tmp/hermes_test oc9-hermes:0.14.0 \ - python3 -m hermes_cli.main doctor + hermes doctor # 查看整体状态 -docker run --rm oc9-hermes:0.14.0 python3 -m hermes_cli.main status +docker run --rm oc9-hermes:0.14.0 hermes status ``` ## 数据持久化 -- Gitee