Files
yuan-lab-llm--clawmanager/docs/clawmanager-agent-v2-development-guide.md
Li-Day-Day 58c19856fd
Release / Prepare Scheduled Release (push) Has been skipped
Release / Publish Release (push) Failing after 0s
feat:Improve Lite gateway scheduling, proxy stability, and Hermes recovery (#160)
* feat(runtime): improve Lite gateway scheduling and proxy stability

- Expand the runtime gateway port range to match 100 instances per pod
- Scale runtime deployments based on pending backlog
- Serialize gateway creation per pod and handle starting/creating states
- Clean up missing gateway bindings from runtime agent reports
- Improve Lite gateway proxy token stripping and auth header injection
- Update deployment manifests, docs, and related tests

* fix(runtime): harden Hermes Lite gateway recovery

- Add hashed gateway token aliases for short-term token compatibility
- Fix /resume conversations failing when old sessions use mismatched gateway tokens
- Recover ports only from stale error bindings without affecting active gateways

* feat(runtime): enable bidirectional desktop clipboard and direct proxy

- Enable WebTop/Selkies/Kasm clipboard by default with per-instance overrides
- Document clipboard verification, configuration pitfalls, and IME troubleshooting
- Enable direct desktop proxy in the K8s and K3s manifests

---------

Co-authored-by: litiantian03 <litiantian03@ieisystem.com>
2026-07-13 14:37:03 +08:00

889 lines
26 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ClawManager Agent V2 开发规范
本文整理当前 ClawManager 代码中的最新 agent 约定,面向 OpenClaw、Hermes 以及后续新增的托管 runtime。开发或改造 runtime 镜像时,应优先遵守本文;字段级契约以 `docs/clawmanager-agent-v2-contract.md` 和后端代码为准。
## 1. 架构定位
最新架构中有两类 agent 通信面:
1. **Runtime Pod Agent,也就是本文主线的 V2 agent**
运行在共享 runtime Pod 内,负责本 Pod 内多个 gateway 子进程的创建、删除、端口、workspace、资源隔离、健康检查和状态上报。ClawManager 通过它把一个实例绑定到 `pod_ip + gateway_port`
2. **Instance Agent Control Plane,旧的 `/api/v1/agent/*` 协议**
面向单个实例的状态、命令、skill inventory、skill package 上传和配置 revision 下载。当前后端仍保留这套协议,runtime 可以继续复用它完成实例级状态与 skill 管理;但 gateway 生命周期管理必须走 Runtime Pod Agent。
一句话原则:**Runtime Pod Agent 是 Pod 内 gateway 管理器,不是一个阻塞式启动脚本。**
## 2. 支持范围
当前 V2 托管 runtime 类型:
| 类型 | 说明 |
| --- | --- |
| `openclaw` | OpenClaw runtime |
| `hermes` | Hermes runtime |
当前平台默认值:
| 项 | 默认值 |
| --- | --- |
| workspace root | `/workspaces` |
| workspace path | `/workspaces/{runtime}/user-{user_id}/instance-{instance_id}` |
| gateway 端口范围 | `20000-20299` |
| 单 Runtime Pod 容量 | `100` |
| 实例 UID/GID | `200000 + instance_id` |
| agent 控制端口 | `19090` |
## 3. Runtime Pod Agent 职责
Runtime Pod Agent 必须实现:
- 启动本地控制服务:`GET /v1/health``POST /v1/gateways``DELETE /v1/gateways/{gateway_id}``POST /v1/drain`
- 向 ClawManager 上报 runtime pod 注册、heartbeat、metrics、gateway 状态和 skill 状态。
- 管理本 Pod 内 gateway 子进程生命周期。
- 管理端口池,避免同一 Pod 内端口冲突。
- 创建并校验 workspace,设置正确 UID/GID。
- 将 ClawManager 下发的 LLM、代理、实例 token、runtime 配置写入实例自己的 workspace。
- 实现 drain,拒绝新 gateway,保持已有 gateway 运行,等待 ClawManager 迁移和删除。
- 避免任何敏感 token、API key、Authorization header 出现在日志中。
不应该由 Runtime Pod Agent 实现:
- 直接对浏览器暴露 gateway 自签地址。
- 管理 ClawManager 用户权限。
- 删除用户 workspace。
- 把外部 NodePort、外部 HTTPS 入口或固定内网 IP 写死进 runtime 配置。
## 4. 运行环境变量
当前 Runtime Deployment 构建器注入以下变量,agent 实现应优先支持:
| 变量 | 说明 |
| --- | --- |
| `CLAWMANAGER_RUNTIME_TYPE` | `openclaw``hermes` |
| `CLAWMANAGER_AGENT_PORT` | agent 控制端口,当前为 `19090` |
| `CLAWMANAGER_GATEWAY_PORT_START` | gateway 起始端口 |
| `CLAWMANAGER_GATEWAY_PORT_END` | gateway 结束端口 |
| `CLAWMANAGER_AGENT_CONTROL_TOKEN` | ClawManager 调 agent 控制接口使用 |
| `CLAWMANAGER_AGENT_REPORT_TOKEN` | agent 上报 ClawManager 使用 |
为了兼容早期文档和旧镜像,agent 建议同时识别以下别名:
| 新变量 | 兼容别名 |
| --- | --- |
| `CLAWMANAGER_AGENT_CONTROL_TOKEN` | `RUNTIME_AGENT_CONTROL_TOKEN` |
| `CLAWMANAGER_AGENT_REPORT_TOKEN` | `RUNTIME_AGENT_REPORT_TOKEN` |
| `CLAWMANAGER_GATEWAY_PORT_START` | `RUNTIME_GATEWAY_PORT_START` |
| `CLAWMANAGER_GATEWAY_PORT_END` | `RUNTIME_GATEWAY_PORT_END` |
| `CLAWMANAGER_AGENT_PORT` | `RUNTIME_AGENT_PUBLIC_PORT` |
建议支持的附加变量:
| 变量 | 默认值/要求 |
| --- | --- |
| `RUNTIME_WORKSPACE_ROOT` | 默认 `/workspaces` |
| `RUNTIME_AGENT_LISTEN_ADDR` | 默认 `0.0.0.0:19090` |
| `CLAWMANAGER_BACKEND_URL` | ClawManager backend 内部地址,推荐 Kubernetes Service DNS |
| `CLAWMANAGER_RUNTIME_IMAGE_REF` | 当前镜像标识,用于注册上报 |
| `POD_NAME` / `POD_NAMESPACE` / `POD_IP` / `NODE_NAME` | 建议通过 Downward API 注入 |
| `CLAWMANAGER_TRUSTED_PROXY_CIDRS` | ClawManager app/gateway 的可信代理网段 |
`CLAWMANAGER_BACKEND_URL` 必须使用集群内部地址,例如:
```text
http://clawmanager-gateway.clawmanager-system.svc.cluster.local:9001
```
不要在 runtime 内部配置中写入浏览器外部入口,例如 `https://172.16.1.12:39443`
## 5. Agent 到 ClawManager 的上报接口
所有 Runtime Pod Agent 上报请求必须带:
```http
X-ClawManager-Agent-Token: ${CLAWMANAGER_AGENT_REPORT_TOKEN}
Content-Type: application/json
```
后端统一返回:
```json
{
"success": true,
"message": "...",
"data": {}
}
```
### 5.1 注册 Runtime Pod
```http
POST {CLAWMANAGER_BACKEND_URL}/api/v1/runtime-agent/register
```
必填字段:
- `runtime_type`
- `namespace`
- `pod_name`
- `deployment_name`
- `image_ref`
推荐完整 payload
```json
{
"runtime_type": "openclaw",
"namespace": "clawmanager-system",
"pod_name": "openclaw-runtime-6f77f8b8c7-abcde",
"pod_uid": "pod-uid",
"pod_ip": "10.42.0.31",
"node_name": "node-a",
"deployment_name": "openclaw-runtime",
"image_ref": "ghcr.io/yuan-lab-llm/agentsruntime/openclaw:latest",
"agent_endpoint": "http://10.42.0.31:19090",
"state": "ready",
"capacity": 100,
"used_slots": 0,
"draining": false,
"metrics": {
"agent_version": "0.1.0"
},
"reported_at": "2026-06-08T09:00:00Z"
}
```
要求:
- `agent_endpoint` 必须是 ClawManager Pod 可以访问的地址,通常是 `http://{POD_IP}:19090`,不能是 `127.0.0.1`
- `capacity <= 0` 时后端会按 `100` 处理,但 agent 应主动上报真实容量。
- `state` 为空时后端按 `ready` 处理;agent 不应在初始化未完成时上报 ready。
- `metrics` 必须是合法 JSON。
### 5.2 Heartbeat
```http
POST {CLAWMANAGER_BACKEND_URL}/api/v1/runtime-agent/heartbeat
```
payload
```json
{
"pod_id": 17,
"namespace": "clawmanager-system",
"pod_name": "openclaw-runtime-6f77f8b8c7-abcde",
"state": "ready",
"used_slots": 37,
"draining": false,
"reported_at": "2026-06-08T09:00:02Z"
}
```
要求:
- 优先使用注册返回的 `data.pod.id` 作为 `pod_id`
- 没有 `pod_id` 时必须带 `namespace + pod_name`
- `state` 必填。
- `used_slots` 必须来自当前真实 `starting/running` gateway 数量,不要使用过期缓存。
- 建议 heartbeat 间隔 2 秒;如果环境压力较大,可放宽,但必须小于 ClawManager 的 heartbeat 超时窗口。
### 5.3 Metrics
```http
POST {CLAWMANAGER_BACKEND_URL}/api/v1/runtime-agent/metrics/report
```
payload
```json
{
"pod_id": 17,
"cpu_millis_used": 13600,
"memory_bytes_used": 42949672960,
"disk_bytes_used": 214748364800,
"network_rx_bytes": 9223372,
"network_tx_bytes": 19223372,
"metrics": {
"gateway_count": 37,
"load_1m": 2.4
},
"reported_at": "2026-06-08T09:00:05Z"
}
```
要求:
- CPU 单位是 millicore。
- memory/disk/network 单位是 bytes。
- network 字段必须是单调递增 counter,不要填瞬时速率。
- `metrics` 是扩展 JSON,必须合法。
### 5.4 Gateway Report
```http
POST {CLAWMANAGER_BACKEND_URL}/api/v1/runtime-agent/gateways/report
```
payload
```json
{
"pod_id": 17,
"gateways": [
{
"instance_id": 123,
"gateway_id": "gw-123-7",
"gateway_port": 20017,
"gateway_pid": 8842,
"state": "running",
"generation": 7,
"health_at": "2026-06-08T09:00:05Z"
}
]
}
```
要求:
- `gateways` 必填。
- 每个 gateway 的 `instance_id``state``generation` 必填。
- 后端只接受“当前绑定 Pod + 当前 generation”的上报;旧 Pod 或旧 generation 上报会被忽略。
- `state=running``state=healthy` 会把绑定更新为 running。
- `starting``stopped``error``unhealthy` 等状态会更新绑定状态。
- 错误状态必须带 `error_message`,不要只上报空状态。
### 5.5 Skills Report
```http
POST {CLAWMANAGER_BACKEND_URL}/api/v1/runtime-agent/skills/report
```
当前后端接收并发布任意 JSON payload。推荐按实例分组:
```json
{
"pod_id": 17,
"runtime_type": "openclaw",
"mode": "full",
"reported_at": "2026-06-08T09:00:05Z",
"instances": [
{
"instance_id": 123,
"workspace_path": "/workspaces/openclaw/user-45/instance-123",
"skills": [
{
"skill_id": "weather",
"skill_version": "1.0.0",
"identifier": "weather",
"install_path": "/workspaces/openclaw/user-45/instance-123/.openclaw/skills/weather",
"content_md5": "0123456789abcdef0123456789abcdef",
"source": "runtime",
"type": "agent-skill"
}
]
}
]
}
```
skill inventory 必须按 `instance_id``workspace_path` 隔离,不要把一个实例的 skill 混到另一个实例。
## 6. ClawManager 到 Agent 的控制接口
所有控制请求必须校验:
```http
X-ClawManager-Control-Token: ${CLAWMANAGER_AGENT_CONTROL_TOKEN}
```
token 错误返回 `401`。不要返回 `3xx` redirect;后端会把 redirect 当成失败。
### 6.1 Health
```http
GET /v1/health
```
任意 HTTP 2xx 都会被后端视为成功。建议响应:
```json
{
"status": "ready"
}
```
以下情况必须返回 `503`
- 必填环境变量缺失。
- workspace root 不可访问。
- 端口池不可用。
- control/report token 缺失。
- clock skew 超过健康阈值。
- agent 正在关闭,不再接受新请求。
### 6.2 Create Gateway
```http
POST /v1/gateways
```
请求:
```json
{
"instance_id": 123,
"user_id": 45,
"agent_type": "openclaw",
"workspace_path": "/workspaces/openclaw/user-45/instance-123",
"port_range": {
"start": 20000,
"end": 20299
},
"uid": 200123,
"gid": 200123,
"cpu_cores": 2,
"memory_mb": 4096,
"disk_quota_mb": 20480,
"generation": 7,
"environment": {
"CLAWMANAGER_LLM_BASE_URL": "http://clawmanager-gateway.clawmanager-system.svc.cluster.local:9001/api/v1/gateway/llm",
"CLAWMANAGER_LLM_API_KEY": "instance-token",
"CLAWMANAGER_LLM_MODEL": "[\"auto\"]",
"CLAWMANAGER_LLM_PROVIDER": "openai-compatible",
"CLAWMANAGER_INSTANCE_TOKEN": "instance-token",
"OPENAI_API_KEY": "instance-token"
}
}
```
响应:
```json
{
"gateway_id": "gw-123-7",
"port": 20017,
"pid": 8842,
"status": "starting"
}
```
实现要求:
- handler 必须快速返回,通常 1 到 3 秒内返回 `starting` 或已有 gateway 元数据。
- 不能在 HTTP handler 内阻塞等待前台 gateway 进程完整运行。
- `agent_type` 必须等于当前 Pod runtime type。
- Pod draining 时返回 `409``503`,拒绝新建。
-`instance_id + generation` 为幂等键;重复请求不能重复启动进程。
- 收到更高 generation 时,停止同实例旧 generation gateway 并释放端口。
- 端口必须来自请求的 `port_range`
- 没有可用端口时返回:
```http
HTTP/1.1 409 Conflict
Content-Type: text/plain
```
- 启动失败、健康检查失败、配置写入失败时,上报 gateway `error` 并释放端口。
### 6.3 Delete Gateway
```http
DELETE /v1/gateways/{gateway_id}
```
要求:
- `gateway_id` 是 URL path escapedagent 必须 decode 后再查找。
- 优雅停止进程组,超时后强制 kill。
- 释放端口、cgroup、进程表和本地状态。
- 不删除 workspace。
- gateway 不存在时建议返回 `204`,保持删除幂等。
- 成功状态可以是 `200``202``204`
### 6.4 Drain
```http
POST /v1/drain
```
请求:
```json
{
"draining": true
}
```
要求:
- 设置本地 draining 状态。
- 立即拒绝新的 `POST /v1/gateways`
- 已有 gateway 不主动停止,继续健康检查和上报。
- 立即补发 heartbeat`state=draining, draining=true`
- 等 ClawManager 迁移实例后,逐个收到 DELETE 再清理。
## 7. Gateway 生命周期
推荐状态机:
```text
requested -> reserved_port -> starting -> running
-> error
running -> stopping -> stopped
running -> unhealthy -> running/error
```
创建流程:
1. 校验 token、runtime type、instance/user/generation、workspace、端口范围。
2. 如果 draining,拒绝创建。
3. 查本地状态,处理幂等。
4. 如果 generation 更高,清理旧 generation。
5. 加锁分配端口,标记为 reserved。
6. 创建 workspace 和 home,设置 owner 为请求中的 `uid/gid`
7. 写入 runtime 配置和 LLM/proxy 配置。
8. 持久化 gateway 元数据为 `starting`
9. 后台启动 gateway 子进程,立即返回。
10. 健康检查通过后上报 `running`
11. 失败时上报 `error`,记录短错误信息并释放端口。
`running` 必须以真实进程和真实健康检查为准,不能只读旧状态文件。
## 8. 端口管理
agent 必须实现并发安全的端口分配器:
```text
reserve(instance_id, generation, count) -> port_set
commit(instance_id, generation, port_set)
release(instance_id, generation)
list_used()
```
要求:
- 分配时持有互斥锁。
- 在锁内检查 agent 内存状态和系统监听端口。
- 启动失败、健康检查失败、DELETE 成功后必须释放端口。
- 如果 runtime 需要多个端口,必须一次性分配端口组,避免部分成功。
- 同一 Pod 内端口不能冲突;不同 Pod 可以复用相同端口,因为路由使用 `pod_ip + port`
## 9. Workspace 与资源隔离
workspace 固定格式:
```text
/workspaces/{runtime}/user-{user_id}/instance-{instance_id}
```
agent 必须:
- 对 workspace root 和请求路径执行 realpath。
- 拒绝 `..`、绝对路径逃逸、符号链接逃逸、跨用户、跨实例路径。
- 创建 `${workspace}/home`,并设置 `HOME=${workspace}/home`
- owner 设置为请求中的 `uid/gid`,当前默认 `200000 + instance_id`
- token、provider、配置文件权限建议 `0600`
- 删除 gateway 时不删除 workspace。
资源限制要求:
- 每个 gateway 使用独立进程组。
- 业务进程最终以实例 UID/GID 运行。
- CPU/Memory 使用 cgroup 限制。
- Disk 优先使用 filesystem quota;不支持时至少周期扫描并在超限时上报错误。
- 资源限制无法启用时必须在 report 或错误消息中体现,不要静默忽略。
## 10. Runtime 配置和 LLM 注入
ClawManager 会通过 `POST /v1/gateways``environment` 字段下发实例级 LLM 环境变量。agent 只能转发白名单变量:
```text
CLAWMANAGER_LLM_BASE_URL
CLAWMANAGER_LLM_API_KEY
CLAWMANAGER_LLM_MODEL
CLAWMANAGER_LLM_PROVIDER
CLAWMANAGER_INSTANCE_TOKEN
OPENAI_BASE_URL
OPENAI_API_BASE
OPENAI_API_KEY
OPENAI_MODEL
```
要求:
- LLM base URL 使用 ClawManager 内部 Service DNS。
- API key 使用当前实例 token。
- 不要求用户在 runtime 页面手工填写 OpenAI key。
- 不打印 token/API key。
- 如果缺少 LLM key,不能上报 gateway `running`;应上报 `error` 并说明缺少实例 LLM token。
runtime 配置必须写在实例 workspace 内,例如:
| Runtime | 推荐路径 |
| --- | --- |
| OpenClaw | `{workspace}/home/.openclaw/openclaw.json` |
| Hermes | `{workspace}/home/.hermes/hermes.json` 或 Hermes 原生配置路径 |
JSON merge 规则:
- 对象字段递归 merge。
- 保留用户已有未知字段。
- 平台必须控制的字段可以覆盖,但要在代码中显式列出。
- 数组 append 后去重,例如 `allowedOrigins`
- 写入前先生成临时文件,再原子 rename。
## 11. 代理和浏览器访问
浏览器访问链路必须是:
```text
Browser HTTPS -> ClawManager HTTPS -> ClawManager backend proxy -> Runtime Pod HTTP gateway
```
agent 写 runtime 配置时必须保证:
- gateway 支持 `/api/v1/instances/{instance_id}/proxy` base path。
- allowed origin 使用 ClawManager 内部 origin,例如 `http://clawmanager-gateway.clawmanager-system.svc.cluster.local:9001`
- trusted proxies 来自 `CLAWMANAGER_TRUSTED_PROXY_CIDRS` 或等价 env。
- 不使用外部地址作为 runtime 内部 trusted origin。
- 通过 ClawManager proxy 访问时,不应要求用户手工粘贴 gateway token。
## 12. 时间同步
agent 必须处理 clock skew
- 注册和 heartbeat 响应如果带 `server_time`,记录本地时间和服务端时间偏移。
- 偏移超过 30 秒时记录 warning,并在 health 中暴露 `clock_skew_warning`
- 偏移超过 120 秒时不应上报 `ready` 或 gateway `running`
- 所有签名、token 时间和上报时间使用 UTC。
- 不在容器里修改系统时间,时间同步由 Kubernetes 节点 NTP/chrony 负责。
常见症状包括 `device signature expired`、JWT/WebSocket 鉴权异常、签名 URL 过期和 gateway 实际状态与 ClawManager 展示不一致。
## 13. Instance Agent v1 兼容协议
当前后端仍提供 `/api/v1/agent/*`,用于实例级状态、命令和 skill 管理。托管 runtime 如果需要复用这套能力,应由具体实例的 gateway/agent 逻辑使用实例级环境变量。
ClawManager 会为支持托管集成的实例注入:
| 变量 | 说明 |
| --- | --- |
| `CLAWMANAGER_AGENT_ENABLED` | `true` 时启用实例 agent |
| `CLAWMANAGER_AGENT_BASE_URL` | ClawManager API base URL,不带 `/api/v1/agent` 后缀 |
| `CLAWMANAGER_AGENT_BOOTSTRAP_TOKEN` | 首次注册使用 |
| `CLAWMANAGER_AGENT_INSTANCE_ID` | 当前实例 ID |
| `CLAWMANAGER_AGENT_PROTOCOL_VERSION` | 当前为 `v1` |
| `CLAWMANAGER_AGENT_PERSISTENT_DIR` | 当前实例持久化目录 |
| `CLAWMANAGER_AGENT_DISK_LIMIT_BYTES` | 实例磁盘配额 |
### 13.1 Register
```http
POST {base}/api/v1/agent/register
Authorization: Bearer ${CLAWMANAGER_AGENT_BOOTSTRAP_TOKEN}
```
payload
```json
{
"instance_id": 123,
"agent_id": "openclaw-123-main",
"agent_version": "0.1.0",
"protocol_version": "v1",
"capabilities": [
"runtime.status",
"runtime.health",
"metrics.report",
"skills.inventory",
"skills.upload",
"commands.poll",
"llm.gateway"
],
"host_info": {
"runtime_type": "openclaw",
"runtime_name": "OpenClaw",
"image": "ghcr.io/yuan-lab-llm/agentsruntime/openclaw:latest",
"persistent_dir": "/config"
}
}
```
响应 `data` 包含:
- `session_token`
- `session_expires_at`
- `heartbeat_interval_seconds`,当前默认 `15`
- `command_poll_interval_seconds`,当前默认 `5`
- `server_time`
session token 每次成功 heartbeat 会续期 24 小时。收到 `401` 时删除本地 session,并用 bootstrap token 重新注册。
### 13.2 Heartbeat
```http
POST {base}/api/v1/agent/heartbeat
Authorization: Bearer ${session_token}
```
payload
```json
{
"agent_id": "openclaw-123-main",
"timestamp": "2026-06-08T09:00:15Z",
"openclaw_status": "running",
"current_config_revision_id": null,
"summary": {
"runtime_type": "openclaw",
"runtime_status": "running",
"runtime_pid": 245,
"openclaw_pid": 245,
"skill_count": 8,
"disk_used_bytes": 2147483648,
"disk_limit_bytes": 10737418240
}
}
```
兼容要求:
- `openclaw_status``openclaw_pid``openclaw_version` 是历史字段名;非 OpenClaw runtime 也需要填自己的主进程状态、PID 和版本,直到协议升级。
- 后端 45 秒内收到 heartbeat 展示 online45 到 120 秒展示 stale,超过 120 秒展示 offline。
- heartbeat 响应里的 `has_pending_command=true` 时,应立即拉取命令。
### 13.3 State Report
```http
POST {base}/api/v1/agent/state/report
Authorization: Bearer ${session_token}
```
payload
```json
{
"agent_id": "openclaw-123-main",
"reported_at": "2026-06-08T09:00:20Z",
"runtime": {
"openclaw_status": "running",
"openclaw_pid": 245,
"openclaw_version": "openclaw-2026.5.4",
"current_config_revision_id": null
},
"system_info": {
"runtime_type": "openclaw",
"cpu": {
"cores": 2,
"load": {
"1m": 0.64,
"5m": 0.52,
"15m": 0.4
}
},
"memory": {
"mem_total_bytes": 4294967296,
"mem_available_bytes": 2147483648
},
"disk": {
"root_total_bytes": 10737418240,
"root_free_bytes": 8589934592
},
"network": {
"interfaces": [
{
"name": "eth0",
"status": "up",
"addresses": ["10.42.0.12"],
"rx_bytes": 123456789,
"tx_bytes": 98765432
}
]
}
},
"health": {
"runtime_process": "ok",
"agent": "ok",
"metrics_collector": "ok"
}
}
```
network 的 `rx_bytes` / `tx_bytes` 必须是单调递增 counter。
### 13.4 Commands
拉取:
```http
GET {base}/api/v1/agent/commands/next
Authorization: Bearer ${session_token}
```
开始:
```http
POST {base}/api/v1/agent/commands/{id}/start
Authorization: Bearer ${session_token}
```
完成:
```http
POST {base}/api/v1/agent/commands/{id}/finish
Authorization: Bearer ${session_token}
```
finish 只允许:
- `succeeded`
- `failed`
当前支持的命令类型:
```text
start_openclaw
stop_openclaw
restart_openclaw
collect_system_info
apply_config_revision
reload_config
health_check
install_skill
update_skill
uninstall_skill
remove_skill
disable_skill
quarantine_skill
handle_skill_risk
sync_skill_inventory
refresh_skill_inventory
collect_skill_package
```
未知命令必须 finish 为 `failed``error_message` 写明 `unsupported command type: <type>`
### 13.5 Skills
inventory
```http
POST {base}/api/v1/agent/skills/inventory
Authorization: Bearer ${session_token}
```
upload
```http
POST {base}/api/v1/agent/skills/upload
Authorization: Bearer ${session_token}
Content-Type: multipart/form-data
```
上传表单字段:
- `file`
- `agent_id`
- `skill_id`
- `skill_version`
- `identifier`
- `content_md5`
- `source`
要求:
- `identifier``content_md5` 必须稳定。
- `mode=full` 的 inventory 代表全量结果,平台会用它对齐实例 skill 状态。
- `content_md5` 按目录内容指纹计算,不是 zip 文件 MD5;算法见 `docs/skill-content-md5-spec.md`
- 打包和解压必须防止路径逃逸。
## 14. 安全规范
- 控制接口只接受正确 `X-ClawManager-Control-Token`
- 上报接口只使用 `X-ClawManager-Agent-Token`
- 实例级接口使用 Bearer bootstrap/session token。
- 日志不得打印 token、API key、完整 Authorization header、channel secret。
- 启动进程使用 argv 数组,不使用 shell 拼接:
```text
execve(binary, ["openclaw", "gateway", "run", "--port", "20017"], env)
```
- 不允许用户环境变量覆盖 agent 自己的 control/report token。
- 不允许 `allowedOrigins=["*"]` 作为生产配置。
- 所有用户输入路径都要边界校验。
- 命令执行必须有超时,超过 `timeout_seconds` 主动终止并 finish failed。
## 15. 测试要求
单元测试至少覆盖:
- Runtime type 只接受 `openclaw` / `hermes`
- create gateway 幂等。
- 更高 generation 替换旧 generation。
- 端口并发分配无冲突。
- 端口被系统占用时跳过。
- 端口耗尽返回 `409 no free port`
- workspace path 越权被拒绝。
- JSON merge 保留用户字段,数组去重。
- LLM env 白名单转发。
- 缺少 LLM token 时 gateway 不报 `running`
- trusted proxy 和 allowed origin 配置生成。
- clock skew 阈值判断。
- redirect 响应不被当成成功。
- DELETE gateway id path escape/decode。
集成测试至少覆盖:
- Runtime Pod 注册为 `ready`capacity/used_slots/draining 正确。
- heartbeat 更新 Pod 状态。
- metrics 上报后管理端 Runtime Pods 页面能看到指标。
- 创建 100 个 gateway 端口唯一,第 101 个触发扩容或 no capacity。
- 重复 create 同一 `instance_id + generation` 不重复启动。
- gateway report 旧 generation 不覆盖新 generation。
- ClawManager 通过 proxy 能访问 gateway 页面和 WebSocket。
- 通过 ClawManager proxy 访问时不要求用户手工粘贴 gateway token。
- Pod drain 后拒绝新 create,已有 gateway 持续上报,DELETE 后释放端口。
- Pod 重启后不误报旧 gateway running。
- instance agent register/heartbeat/state/commands/skills 全链路可用。
- 日志中没有 token 和 API key。
## 16. Do / Don't
| Do | Don't |
| --- | --- |
| 使用 Kubernetes Service DNS 访问 ClawManager backend | 写死外部 NodePort、`172.16.1.12` 或浏览器 HTTPS 入口 |
| `POST /v1/gateways` 快速返回 `starting` | 在 HTTP handler 中阻塞等待前台进程退出 |
| 以 `instance_id + generation` 幂等 | 重试时重复启动多个 gateway |
| 以真实进程和健康检查决定 `running` | 只读旧状态文件就上报 `running` |
| JSON merge runtime 配置 | 覆盖用户整个配置文件 |
| 只转发白名单 env | 把用户请求里的全部 env 原样传给进程 |
| trusted proxy 模式下通过 ClawManager 自动进入 | 要求用户手工粘贴 gateway token |
| 检测并上报 clock skew | 忽略时间导致签名和 WebSocket 鉴权问题反复出现 |
| DELETE gateway 不删除 workspace | 清理进程时顺手删除用户数据 |
## 17. 代码位置
后端关键实现:
- Runtime Pod 上报接口:`backend/internal/handlers/runtime_agent_handler.go`
- ClawManager 调 agent 客户端:`backend/internal/services/runtime_agent_client.go`
- Runtime 调度器:`backend/internal/services/runtime_scheduler.go`
- Runtime Deployment 构建:`backend/internal/services/k8s/runtime_deployment_service.go`
- Runtime 常量与路径:`backend/internal/services/runtime_capacity.go`
- 实例 agent handler`backend/internal/handlers/agent_handler.go`
- 实例 agent service`backend/internal/services/instance_agent_service.go`
- 实例命令 service`backend/internal/services/instance_command_service.go`
- 实例 runtime 状态 service`backend/internal/services/instance_runtime_status_service.go`
相关文档:
- `docs/clawmanager-agent-v2-contract.md`
- `docs/runtime-agent-integration-guide.md`
- `docs/hermes-lite-pro-agent-development.md`
- `docs/hermes-runtime-agent-development.md`
- `docs/skill-content-md5-spec.md`