diff --git a/.gitignore b/.gitignore index e373c58..5204da9 100644 --- a/.gitignore +++ b/.gitignore @@ -177,7 +177,7 @@ cython_debug/ .env .DS_Store -# 本地实例数据(默认 hermes-agent-instances/,也可由 OPENCLAW_HOME_DATA_DIR 指向其它路径) +# 本地实例数据(默认 hermes-agent-instances/,也可由 HERMES_HOME_DATA_DIR 指向其它路径) hermes-agent-instances/ .venv/ __pycache__/ diff --git a/README.md b/README.md index 09fcc52..39a035e 100644 --- a/README.md +++ b/README.md @@ -2,40 +2,36 @@ 在本机用 **Docker Compose** 管理多个 **[NousResearch Hermes Agent](https://github.com/NousResearch/hermes-agent)** 实例:CLI + 可选 Web 管理台。 -**不是 OpenClaw。** 底层镜像与服务来自官方 `hermes-agent` 仓库;每实例独立数据目录挂载到容器 `/opt/data`(`config.yaml`、`.env`、`sessions/` 等)。 - -## 与 openclaw-home 的区别 - -| 项目 | openclaw-home | hermes-agent-home(本仓库) | -|------|---------------|---------------------------| -| 上游仓库 | OpenClaw | **NousResearch/hermes-agent** | -| Compose 服务 | `openclaw-gateway` | **`gateway`** | -| 配置 | `config/openclaw.json` | **`data/config.yaml`** | -| 网关/Dashboard 端口 | 18780+N / 18790+N | **8641+N / 9118+N**(实例 1 → 8642 / 9119) | -| Compose 项目名 | `claw-` | `hermes-` | +每实例独立数据目录挂载到容器 `/opt/data`(`config.yaml`、`.env`、`sessions/` 等)。默认使用官方镜像 `nousresearch/hermes-agent:latest`,无需 clone 源码。 ## 快速开始 ```bash -git clone https://github.com/NousResearch/hermes-agent.git /path/to/hermes-agent - cd /path/to/hermes-agent-home -cp .env.example .env # 设置 HERMES_SOURCE=/path/to/hermes-agent +cp .env.example .env # 可选:改 HERMES_IMAGE、Web 密码等 ./hermes-agent-cli.sh check -./hermes-agent-cli.sh compose-build 1 # 首次构建镜像(较久) +./hermes-agent-cli.sh compose-build 1 # 拉取官方镜像 ./hermes-agent-cli.sh setup 1 # 交互配置 API 密钥等(或 Web「Hermes 引导」) ./hermes-agent-cli.sh run 1 # 实例 1:API http://127.0.0.1:8642 · Dashboard http://127.0.0.1:9119 ``` +本地从源码构建镜像时,在 `.env` 设置 `HERMES_USE_LOCAL_BUILD=1` 与 `HERMES_SOURCE`(指向含 `Dockerfile` 的 hermes-agent 仓库根目录)。 + ## Web 见 [hermes-agent-web/README.md](hermes-agent-web/README.md)。默认 http://127.0.0.1:8781 -## 从旧版(OpenClaw 兼容层)迁移 +## 端口与命名 -若此前用 `OPENCLAW_SOURCE` 跑过实例,请**销毁旧实例**后按上表重建;旧目录 `hermes-agent-instances//config/` 与 Hermes 的 `data/` 布局不兼容。 +| 项目 | 说明 | +|------|------| +| Compose 项目名 | `hermes-<序号>` | +| 服务名 | `gateway` | +| 容器名示例 | `hermes-1-gateway-1` | +| 网关 API 端口 | `8641 + 序号`(实例 1 → 8642) | +| Dashboard 端口 | `9118 + 序号`(实例 1 → 9119) | 文档:https://hermes-agent.nousresearch.com/docs/user-guide/docker diff --git a/allowall.sh b/allowall.sh deleted file mode 100644 index d3ca34a..0000000 --- a/allowall.sh +++ /dev/null @@ -1,96 +0,0 @@ -mkdir -p ~/.openclaw && python3 - <<'PY' -import json, os, pathlib, copy - -base = pathlib.Path(os.path.expanduser("~/.openclaw")) -openclaw_path = base / "openclaw.json" -approvals_path = base / "exec-approvals.json" - -def load_json(path, default): - if not path.exists(): - return copy.deepcopy(default) - try: - return json.loads(path.read_text(encoding="utf-8")) - except Exception: - return copy.deepcopy(default) - -# 1) 修 openclaw.json -cfg = load_json(openclaw_path, {}) -cfg.setdefault("tools", {}) -cfg["tools"].setdefault("exec", {}) -cfg["tools"]["exec"]["security"] = "full" -cfg["tools"]["exec"]["ask"] = "off" - -# 可选但实用:把常见会挡住 skill 的 deny 清掉 -agents = cfg.get("agents", {}).get("list", []) -if isinstance(agents, list): - for agent in agents: - if not isinstance(agent, dict): - continue - tools = agent.setdefault("tools", {}) - deny = tools.get("deny", []) - if isinstance(deny, list): - tools["deny"] = [] - -openclaw_path.write_text(json.dumps(cfg, ensure_ascii=False, indent=2), encoding="utf-8") - -# 2) 修 exec-approvals.json -default_approvals = { - "version": 1, - "socket": { - "path": str(base / "exec-approvals.sock"), - "token": "" - }, - "defaults": { - "security": "full", - "ask": "off", - "askFallback": "full", - "autoAllowSkills": True - }, - "agents": {} -} - -ap = load_json(approvals_path, default_approvals) - -# 保留已有 socket,避免你本机原来的 socket/token 被抹掉 -if not isinstance(ap, dict): - ap = copy.deepcopy(default_approvals) - -ap["version"] = 1 -ap.setdefault("socket", {}) -ap["socket"].setdefault("path", str(base / "exec-approvals.sock")) -ap["socket"].setdefault("token", "") - -ap.setdefault("defaults", {}) -ap["defaults"]["security"] = "full" -ap["defaults"]["ask"] = "off" -ap["defaults"]["askFallback"] = "full" -ap["defaults"]["autoAllowSkills"] = True - -ap.setdefault("agents", {}) -if isinstance(ap["agents"], dict): - for agent_name, agent_cfg in list(ap["agents"].items()): - if isinstance(agent_cfg, dict): - agent_cfg["security"] = "full" - agent_cfg["ask"] = "off" - agent_cfg["askFallback"] = "full" - agent_cfg["autoAllowSkills"] = True - -approvals_path.write_text(json.dumps(ap, ensure_ascii=False, indent=2), encoding="utf-8") - -print("DONE") -print(openclaw_path) -print(approvals_path) -PY - -openclaw gateway start -echo '----- GATEWAY STATUS -----' -openclaw gateway status -echo '----- APPROVALS EFFECTIVE -----' -openclaw approvals get --gateway || true -echo '----- OPENCLAW EXEC CONFIG -----' -python3 - <<'PY' -import json, os, pathlib -p = pathlib.Path(os.path.expanduser("~/.openclaw/openclaw.json")) -cfg = json.loads(p.read_text(encoding="utf-8")) -print(json.dumps(cfg.get("tools", {}).get("exec", {}), ensure_ascii=False, indent=2)) -PY \ No newline at end of file diff --git a/hermes-agent-cli.sh b/hermes-agent-cli.sh index c22d512..03a3a15 100755 --- a/hermes-agent-cli.sh +++ b/hermes-agent-cli.sh @@ -478,10 +478,6 @@ case "$CMD" in shift 2 cmd_config "$_id" "$@" ;; - channels|channel|devices|allowall) - echo "子命令 $CMD 为旧版 OpenClaw 专用;请使用: $0 cli <序号> … 或查阅 Hermes 文档。" >&2 - exit 1 - ;; *) echo "未知子命令: $CMD" >&2 echo "运行 $0 help 查看说明。" >&2 diff --git a/hermes-agent-web/README.md b/hermes-agent-web/README.md index b98f25f..5263d01 100644 --- a/hermes-agent-web/README.md +++ b/hermes-agent-web/README.md @@ -1,63 +1,44 @@ # Hermes Agent Web -在浏览器里管理本机多个 Hermes Agent(OpenClaw 兼容)实例:**列表与状态、新建、bootstrap 后启动、停止、重启、销毁**;**引导**在 **「Hermes 引导」** 页用浏览器内终端(PTY + xterm.js)完成 `setup`、`run`、`onboard`、`config`、`channels` 等。服务端用 **Flask + Jinja2**,**单进程 Python** 即可运行。 +在浏览器里管理本机多个 **Hermes Agent** 实例:**列表与状态、新建、bootstrap、启动、停止、重启、销毁**;**「Hermes 引导」** 页用浏览器内终端(PTY + xterm.js)完成 `setup`、启动 gateway、进入容器 shell。Flask + Jinja2,单进程即可运行。 -所有与 Docker Compose / 实例生命周期相关的操作都通过上级目录的 [`hermes-agent-cli.sh`](../hermes-agent-cli.sh) 完成(含 `check`、`status`、`stop`、`run`、`restart`、`destroy`、`bootstrap`、`cli` 透传容器内 `openclaw-cli`)。实例配置可通过 **`/instance/<序号>/config`** 编辑宿主机上的 `hermes-agent-instances/<序号>/config/openclaw.json`(保存前 JSON 校验并备份 `.bak`)。**销毁**会同时删除该序号下整份实例数据目录。 +所有 Docker / 实例操作均通过上级 [`hermes-agent-cli.sh`](../hermes-agent-cli.sh)。配置编辑页读写宿主机 `hermes-agent-instances/<序号>/data/config.yaml`(YAML 校验,备份 `.bak`)。**销毁**会删除该序号下整份实例目录。 ## 前置条件 -- 本机已安装 **Docker**,且 `docker compose` 可用。 -- 已配置 **`hermes-agent-home/.env`**(与 CLI 相同),至少设置 **`HERMES_SOURCE`** 或 **`OPENCLAW_SOURCE`**(OpenClaw 仓库根目录,内含 `docker-compose.yml`)。 -- 可选 **`HERMES_HOME_DATA_DIR`**:实例数据父目录;不设置时默认使用 `hermes-agent-home/hermes-agent-instances/`。 +- Docker(Desktop / Colima / dockerd) +- Python 3.10+ +- 可选:`hermes-agent-home/.env` 中设置 `HERMES_AGENT_WEB_ADMIN_PASSWORD` + +默认使用官方镜像,**不必**设置 `HERMES_SOURCE`;仅本地 build 时才需要。 ## 安装与启动 ```bash -cd /path/to/hermes-agent-home/hermes-agent-web -python3 -m venv .venv -.venv/bin/pip install -r requirements.txt -export HERMES_AGENT_WEB_ADMIN_PASSWORD='你的强密码' -.venv/bin/python app.py +cd hermes-agent-web +python3 -m venv .venv && source .venv/bin/activate +pip install -r requirements.txt + +# 在 hermes-agent-home 根目录配置 Web 密码后: +export HERMES_AGENT_WEB_ADMIN_PASSWORD='你的密码' +python app.py +# 或: python app.py --admin-password '你的密码' --host 127.0.0.1 --port 8781 ``` -浏览器访问终端中打印的地址(默认 **http://127.0.0.1:8781**)。 +浏览器打开 http://127.0.0.1:8781 ,默认用户 `hermes`(可用 `HERMES_AGENT_WEB_ADMIN_USER` 修改)。 -默认监听 **`127.0.0.1`**,仅本机可访问;若需局域网访问,设置 `HERMES_AGENT_WEB_HOST=0.0.0.0` 并自行做好访问控制。 +## 功能摘要 -## 环境变量 +- **实例列表**:数据目录 `hermes-agent-instances//` 与 Docker 项目 `hermes-` 的并集(含已停止容器),与 `./hermes-agent-cli.sh list-ids` 一致。 +- **拉取镜像 / 启动 / 停止 / 重启 / 销毁**:调用 CLI 对应子命令。 +- **Hermes 引导**:`/instance/<序号>/guide`,自动 `setup`(如需)→ `run` → 容器 shell。 +- **终端**:`/instance/<序号>/docker`,进入运行中 gateway 容器。 +- **编辑配置**:`/instance/<序号>/config`,编辑 `data/config.yaml`。 -| 变量 | 说明 | -|------|------| -| `HERMES_AGENT_WEB_ADMIN_PASSWORD` | 登录密码(建议必填)。也可设为 Werkzeug 生成的哈希串(以 `pbkdf2:` / `scrypt:` 开头) | -| `HERMES_AGENT_WEB_ADMIN_USER` | 登录用户名,默认 `hermes` | -| `HERMES_AGENT_WEB_SECRET_KEY` | Flask session 签名密钥;不设置则每次进程启动随机,**重启后已登录 session 会失效** | -| `HERMES_AGENT_WEB_HOST` | 监听地址,默认 `127.0.0.1` | -| `HERMES_AGENT_WEB_PORT` | 端口,默认 `8781` | -| `HERMES_AGENT_WEB_LOG_CLI` | 是否在终端打印每次执行的 `hermes-agent-cli.sh` 命令、`exit` 码与 stdout/stderr(默认开启;`0` / `false` 关闭)。单条输出超过约 64KB 会截断 | - -应用会加载 **`hermes-agent-home/.env`**(与 `HERMES_SOURCE` 等共用),无需在 `hermes-agent-web` 目录再复制一份,除非你希望单独覆盖。 - -## 功能简述 - -- **实例列表**:序号为 **数据目录下数字文件夹** 与 **Docker 里仍存在的 `hermes-` 项目 / `hermes--openclaw-gateway-*` 容器** 的并集(**已停止的容器**也会出现在列表里)。与同目录 `./hermes-agent-cli.sh list-ids` 一致。 -- **新建实例**:分配新序号、创建目录并执行脚本的 `bootstrap`。 -- **启动**:`run --quick <序号>`。 -- **停止 / 重启 / 销毁**:分别对应脚本的 `stop`、`restart`、`destroy`(**销毁**会先 `compose down`,再 **`rm -rf` 宿主上该实例目录**,不可恢复)。 -- **Hermes 引导**:在实例行点 **「Hermes 引导」** 打开 **`/instance/<序号>/guide`**。进入后工作目录为 **hermes-agent-home**,可执行 `./hermes-agent-cli.sh setup`、`cli <序号> onboard` 等;旧链接 **`/instance//channels`** 会重定向到本引导页。 - -## CLI 对照(调试) +## Docker 核对 ```bash -cd /path/to/hermes-agent-home -./hermes-agent-cli.sh check -./hermes-agent-cli.sh status 1 -./hermes-agent-cli.sh stop 1 -./hermes-agent-cli.sh cli 1 --help -docker ps -a --filter name=openclaw-gateway +docker ps -a --filter name=hermes-1-gateway ``` -更多子命令见脚本内 `help`。 - -## Docker 里找不到网关? - -本仓库脚本为**每个实例**创建独立 Compose **项目名** `hermes-<序号>`,网关服务名为 **`openclaw-gateway`**,容器名大致为 **`hermes-<序号>-openclaw-gateway-1`**。在 Docker Desktop 可搜 **`hermes-1`**(或你的序号)或 **`openclaw-gateway`**。执行 `docker ps -a --filter name=hermes-1-openclaw-gateway` 核对。 +项目名 `hermes-<序号>`,服务 `gateway`,容器名约 `hermes-<序号>-gateway-1`。 diff --git a/hermes-agent-web/app.py b/hermes-agent-web/app.py index a22cf6f..eb752bf 100644 --- a/hermes-agent-web/app.py +++ b/hermes-agent-web/app.py @@ -206,7 +206,7 @@ def _run_cli( def _cli_compose_check() -> str | None: - """通过脚本的 check 子命令校验 Docker、HERMES_SOURCE 与 compose(含 docker compose config)。""" + """通过脚本的 check 子命令校验 Docker 与 compose(含 docker compose config)。""" cp = _run_cli("check", timeout=60) if cp.returncode != 0: return (cp.stderr or cp.stdout or "check 失败").strip() @@ -846,7 +846,7 @@ def docker_pty_ws(ws, iid: int): @app.route("/instance//guide") @login_required def instance_guide(iid): - """引导终端:自动执行 setup(如需)→ 启动网关 → onboard,用户只应答交互提示。""" + """引导终端:自动执行 setup(如需)→ 启动网关 → 容器 shell,用户只应答交互提示。""" if sys.platform == "win32": flash("网页引导终端仅支持 macOS / Linux。", "error") return redirect(url_for("index")) @@ -877,13 +877,6 @@ def instance_docker_terminal(iid): ) -@app.route("/instance//channels") -@app.route("/instance//channels/term") -@login_required -def legacy_channels_redirect(iid): - return redirect(url_for("instance_guide", iid=iid)) - - def main(): logging.basicConfig( level=logging.INFO, diff --git a/hermes-agent-web/templates/guide_terminal.html b/hermes-agent-web/templates/guide_terminal.html index c420594..2c14aa6 100644 --- a/hermes-agent-web/templates/guide_terminal.html +++ b/hermes-agent-web/templates/guide_terminal.html @@ -3,7 +3,7 @@ {% block body %}

← 返回实例列表

实例 {{ iid }} · 引导终端

-

打开本页后会自动在后台执行官方引导链路(未完成 setup 时先跑向导,再启动网关,再进入 onboard)。 +

打开本页后会自动执行引导链路(未完成 setup 时先跑向导,再启动 gateway,再进入容器 shell)。 你一般不用输入命令,只需在终端里按程序提问操作;需要改配置可用「编辑配置」。

关闭本页请先 Ctrl+C 结束当前命令。需联网加载 xterm(CDN)。