移除 OpenClaw/ClawHub 遗留并更新文档
删除 allowall.sh、旧版 CLI/Web 路由,README 改为 Hermes 专用说明。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+1
-1
@@ -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__/
|
||||
|
||||
@@ -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-<N>` | `hermes-<N>` |
|
||||
每实例独立数据目录挂载到容器 `/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/<N>/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
|
||||
|
||||
-96
@@ -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
|
||||
@@ -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
|
||||
|
||||
+25
-44
@@ -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/<N>/` 与 Docker 项目 `hermes-<N>` 的并集(含已停止容器),与 `./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-<N>` 项目 / `hermes-<N>-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/<N>/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`。
|
||||
|
||||
@@ -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/<int:iid>/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/<int:iid>/channels")
|
||||
@app.route("/instance/<int:iid>/channels/term")
|
||||
@login_required
|
||||
def legacy_channels_redirect(iid):
|
||||
return redirect(url_for("instance_guide", iid=iid))
|
||||
|
||||
|
||||
def main():
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{% block body %}
|
||||
<p><a href="{{ url_for('index') }}">← 返回实例列表</a></p>
|
||||
<h2 style="margin-top:0;">实例 <code>{{ iid }}</code> · 引导终端</h2>
|
||||
<p class="muted">打开本页后会<strong>自动</strong>在后台执行官方引导链路(未完成 setup 时先跑向导,再启动网关,再进入 <code>onboard</code>)。
|
||||
<p class="muted">打开本页后会<strong>自动</strong>执行引导链路(未完成 setup 时先跑向导,再启动 gateway,再进入容器 shell)。
|
||||
你一般<strong>不用输入命令</strong>,只需在终端里按程序提问操作;需要改配置可用「编辑配置」。</p>
|
||||
<div id="term-wrap" style="height:min(75vh,620px);border:1px solid var(--border);border-radius:8px;padding:2px;background:#0c1017;"></div>
|
||||
<p class="muted" style="margin-top:0.75rem;">关闭本页请先 <kbd>Ctrl+C</kbd> 结束当前命令。需联网加载 xterm(CDN)。</p>
|
||||
|
||||
Reference in New Issue
Block a user