本文定义任意新 runtime 接入 ClawManager Agent Control Plane 的通用方案。后续新增 OpenClaw、Hermes 以外的 runtime 时,应优先遵守本文,再补充该 runtime 自己的镜像构建细节。
每个可托管 runtime 镜像内都应包含一个常驻 agent。Agent 不需要暴露端口,只需要向 ClawManager 发起出站 HTTP 请求,并完成:
- 注册和 session token 续期。
- 心跳和在线状态上报。
- 运行时状态、CPU、内存、磁盘、网络、健康信息上报。
- skill inventory 同步和 skill 包上传。
- 平台命令轮询、执行、完成结果回传。
- 使用 ClawManager 注入的 AI Gateway 环境变量访问 LLM。
Agent 应把 runtime 自身名称写入 host_info.runtime_type、summary.runtime_type、system_info.runtime_type。当前 v1 协议里仍保留 openclaw_status、openclaw_pid、openclaw_version 这几个历史字段名,新 runtime 在协议升级前需要复用这些字段承载自己的主进程状态、PID 和版本。
新增 runtime 不只是做镜像。ClawManager 平台侧至少需要完成下面几项:
| 模块 | 必做事项 |
|---|---|
| 数据库 | 在 instances.type 枚举迁移中加入新的 runtime_type |
| 后端 runtime 支持 | 在 runtime 类型校验、镜像解析、默认端口、持久化目录、代理路径逻辑中加入新类型 |
| 后端托管能力 | 在 supportsManagedRuntimeIntegration 加入新类型,否则不会注入 Agent 和 AI Gateway 环境变量,注册也会被拒绝 |
| Agent 注册 | 确认注册 allowlist、错误映射、测试覆盖都包含新类型 |
| Runtime Image Cards | 在系统镜像设置的支持类型、默认镜像、默认启用项中加入新类型 |
| 创建实例页 | 在前端 INSTANCE_TYPES、类型文案、图标、默认 env 模板中加入新类型 |
| 图标 | 提供 runtime 官方图标或明确授权图标,放入 frontend/public 或现有资产路径 |
| 测试 | 覆盖 env 注入、agent 注册、runtime 状态上报、创建实例表单、系统镜像卡片 |
如果新 runtime 采用 Webtop/KasmVNC 基础镜像,默认约定通常是:
- 桌面端口:
3001 - 持久化目录:
/config - 代理路径:由 ClawManager 写入
SUBFOLDER=/api/v1/instances/{instance_id}/proxy/
如果新 runtime 不基于 Webtop,必须在平台侧明确它的服务端口、健康检查、代理路径、持久化目录和用户数据目录,不能让镜像和平台各自猜测。
Runtime 镜像必须满足:
- Agent 随容器启动自动运行,例如 systemd、s6 overlay、supervisord 或入口脚本。
- Agent 不占用 runtime 的业务端口或桌面端口。
- Agent 通过环境变量读取所有 ClawManager 配置,不写死地址、实例 ID、token、路径。
- Agent 的本地状态必须写入持久化目录,例如
${CLAWMANAGER_AGENT_PERSISTENT_DIR}/<runtime>-agent/。 - Agent 日志中不能打印 bootstrap token、session token、AI Gateway API key。
- Agent 可以在 ClawManager 暂时不可达时退避重试,不应退出导致容器不可用。
建议本地状态目录:
${CLAWMANAGER_AGENT_PERSISTENT_DIR}/<runtime>-agent/
session.json
state.json
logs/
cache/
| 变量 | 说明 |
|---|---|
CLAWMANAGER_AGENT_ENABLED |
为 true 时启动 agent |
CLAWMANAGER_AGENT_BASE_URL |
ClawManager API 根地址,不带 /api/v1/agent 后缀 |
CLAWMANAGER_AGENT_BOOTSTRAP_TOKEN |
首次注册使用的一次性 bootstrap token |
CLAWMANAGER_AGENT_INSTANCE_ID |
当前实例 ID |
CLAWMANAGER_AGENT_PROTOCOL_VERSION |
当前为 v1 |
CLAWMANAGER_AGENT_PERSISTENT_DIR |
当前实例持久化目录 |
CLAWMANAGER_AGENT_DISK_LIMIT_BYTES |
实例磁盘配额字节数 |
Agent 启动时如果 CLAWMANAGER_AGENT_ENABLED 不是 true,应进入空闲状态或直接不启动控制面逻辑。
支持托管 runtime 的实例会被注入 OpenAI-compatible 网关变量:
| 变量 | 说明 |
|---|---|
CLAWMANAGER_LLM_BASE_URL |
ClawManager AI Gateway OpenAI-compatible base URL |
CLAWMANAGER_LLM_API_KEY |
当前实例专属 Gateway API key |
CLAWMANAGER_LLM_MODEL |
平台注入的模型目录 JSON,首项通常包含 auto |
CLAWMANAGER_LLM_PROVIDER |
当前为 openai-compatible |
CLAWMANAGER_INSTANCE_TOKEN |
当前实例 token,和 Gateway API key 同源 |
OPENAI_BASE_URL |
OpenAI SDK 兼容别名 |
OPENAI_API_BASE |
OpenAI SDK 兼容别名 |
OPENAI_API_KEY |
OpenAI SDK 兼容别名 |
OPENAI_MODEL |
默认模型,通常为 auto |
Runtime 内的应用和 agent 如果需要调用模型,优先使用这些变量,不要让用户在镜像内手工写入 provider key。
推荐主循环:
- 读取环境变量,确认 agent 已启用。
- 生成稳定
agent_id,建议格式为<runtime_type>-<instance_id>-main。 - 从持久化目录读取 session token;没有 token 时用 bootstrap token 注册。
- 发送一次完整 state report。
- 按注册响应里的
heartbeat_interval_seconds发送 heartbeat。 - 按注册响应里的
command_poll_interval_seconds拉取命令;heartbeat 返回has_pending_command=true时立即拉取。 - 按 5 到 10 秒间隔采样并发送 state report。
- 定期同步 skill inventory,或在 skill 变化后立即同步。
- 接口返回 401、session 过期或本地 token 丢失时,重新注册。
注册响应里的 session token 当前为滚动续期。每次成功 heartbeat 都会延长有效期。Agent 仍应能处理 401 并自动重新注册。
以下 {base} 都表示 CLAWMANAGER_AGENT_BASE_URL,认证头均为:
Authorization: Bearer <token>
Content-Type: application/jsonPOST {base}/api/v1/agent/register
Authorization: Bearer {CLAWMANAGER_AGENT_BOOTSTRAP_TOKEN}{
"instance_id": 123,
"agent_id": "myruntime-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": "myruntime",
"runtime_name": "My Runtime",
"image": "registry.example.com/myruntime:latest",
"desktop_base": "webtop",
"persistent_dir": "/config",
"port": 3001,
"arch": "amd64"
}
}响应:
{
"data": {
"session_token": "agt_sess_xxx",
"session_expires_at": "2026-04-28T10:00:00Z",
"heartbeat_interval_seconds": 15,
"command_poll_interval_seconds": 5,
"server_time": "2026-04-27T10:00:00Z"
}
}Agent 必须缓存 session_token,后续接口都使用它认证。
POST {base}/api/v1/agent/heartbeat
Authorization: Bearer {session_token}{
"agent_id": "myruntime-123-main",
"timestamp": "2026-04-27T10:00:15Z",
"openclaw_status": "running",
"summary": {
"runtime_type": "myruntime",
"runtime_status": "running",
"runtime_pid": 245,
"runtime_version": "0.4.0",
"openclaw_pid": 245,
"skill_count": 8,
"disk_used_bytes": 2147483648,
"disk_limit_bytes": 10737418240
}
}兼容要求:
openclaw_status当前仍是 v1 状态槽位。新 runtime 填自己的主进程状态。summary.openclaw_pid是当前后端识别 PID 的兼容别名;建议同时上报runtime_pid和openclaw_pid,后续协议升级后再收敛。- 状态建议使用
starting、running、stopped、error、unknown。 - 心跳按服务端响应间隔执行,默认约 15 秒。
- ClawManager 45 秒内收到心跳显示 online,45 到 120 秒显示 stale,超过 120 秒显示 offline。
POST {base}/api/v1/agent/state/report
Authorization: Bearer {session_token}{
"agent_id": "myruntime-123-main",
"reported_at": "2026-04-27T10:00:20Z",
"runtime": {
"openclaw_status": "running",
"openclaw_pid": 245,
"openclaw_version": "myruntime-0.4.0",
"current_config_revision_id": null
},
"system_info": {
"runtime_type": "myruntime",
"runtime_name": "My Runtime",
"sampled_at": "2026-04-27T10:00:20Z",
"cpu": {
"cores": 2,
"load": {
"1m": 0.64,
"5m": 0.52,
"15m": 0.40
}
},
"memory": {
"mem_total_bytes": 4294967296,
"mem_available_bytes": 2147483648
},
"disk": {
"mount_path": "/config",
"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",
"desktop": "ok",
"agent": "ok",
"metrics_collector": "ok",
"metrics_sample_interval_seconds": 5
}
}后端会原样保存 system_info 和 health。前端实例详情页当前按以下字段绘制指标:
| 路径 | 类型 | 单位 | 说明 |
|---|---|---|---|
system_info.cpu.cores |
number | 核数 | 容器可用 CPU 核数 |
system_info.cpu.load.1m |
number | load average | 1 分钟 load |
system_info.cpu.load.5m |
number | load average | 5 分钟 load |
system_info.cpu.load.15m |
number | load average | 15 分钟 load |
system_info.memory.mem_total_bytes |
number | bytes | 内存上限或总内存 |
system_info.memory.mem_available_bytes |
number | bytes | 可用内存 |
system_info.disk.root_total_bytes |
number | bytes | 持久化目录所在文件系统总容量 |
system_info.disk.root_free_bytes |
number | bytes | 持久化目录所在文件系统剩余容量 |
system_info.network.interfaces[].rx_bytes |
number | bytes | 网卡累计接收字节数 |
system_info.network.interfaces[].tx_bytes |
number | bytes | 网卡累计发送字节数 |
CPU 百分比由前端按 load.1m / cores * 100 计算。内存百分比由 (mem_total_bytes - mem_available_bytes) / mem_total_bytes * 100 计算。磁盘百分比由 (root_total_bytes - root_free_bytes) / root_total_bytes * 100 计算。网络速率由相邻两次 rx_bytes、tx_bytes counter 差值计算,所以 agent 必须上报单调递增的累计 counter,不要把瞬时速率填进这两个字段。
推荐采样来源:
- CPU load:
/proc/loadavg。 - CPU cores:优先 cgroup CPU quota,例如 cgroup v2
/sys/fs/cgroup/cpu.max;没有 quota 时使用/proc/cpuinfo或语言运行时 CPU 数。 - 内存:优先 cgroup memory limit/current,例如 cgroup v2
/sys/fs/cgroup/memory.max、/sys/fs/cgroup/memory.current;没有限制时使用/proc/meminfo的MemTotal和MemAvailable。 - 磁盘:对
CLAWMANAGER_AGENT_PERSISTENT_DIR调用statvfs。 - 网络:读取
/proc/net/dev,默认排除lo。
上报频率:
- 启动成功后立即发送一次。
- 正常运行每 5 秒发送一次;资源敏感时可放宽到 10 秒。
- 采样间隔不要短于 2 秒。
- runtime 状态变化、skill inventory 变化、命令完成后立即补发一次。
拉取命令:
GET {base}/api/v1/agent/commands/next
Authorization: Bearer {session_token}如果没有命令,响应的 data 可能为空。拿到命令后,agent 必须先标记开始,再执行:
POST {base}/api/v1/agent/commands/{id}/start
Authorization: Bearer {session_token}{
"agent_id": "myruntime-123-main",
"started_at": "2026-04-27T10:01:00Z"
}完成后必须 finish,失败也要 finish:
POST {base}/api/v1/agent/commands/{id}/finish
Authorization: Bearer {session_token}{
"agent_id": "myruntime-123-main",
"status": "succeeded",
"finished_at": "2026-04-27T10:01:05Z",
"result": {
"message": "system info collected"
},
"error_message": ""
}当前通用命令类型:
| 命令 | Agent 行为 |
|---|---|
collect_system_info |
立即采样,发送 state report,并在 finish result 中带上同一份摘要 |
health_check |
检查主进程、桌面入口、agent、metrics collector,并发送 state report |
sync_skill_inventory |
扫描 skill 目录并上报完整 inventory |
refresh_skill_inventory |
重新扫描 skill 目录并上报完整 inventory |
collect_skill_package |
打包指定 skill 并上传 |
install_skill |
下载并安装平台指定 skill version |
update_skill |
更新已安装 skill |
uninstall_skill / remove_skill |
移除指定 skill |
disable_skill |
禁用指定 skill |
quarantine_skill |
隔离指定 skill |
handle_skill_risk |
按平台风控 payload 处理 skill |
apply_config_revision |
获取并应用配置 revision |
reload_config |
重新加载 runtime 配置 |
start_openclaw、stop_openclaw、restart_openclaw 是历史命名命令。非 OpenClaw runtime 不应误执行,除非平台侧明确把它们映射为该 runtime 的启动、停止、重启语义。
Agent 遇到未知命令时,应 finish 为 failed,error_message 写明 unsupported command type: <type>。
上报:
POST {base}/api/v1/agent/skills/inventory
Authorization: Bearer {session_token}{
"agent_id": "myruntime-123-main",
"reported_at": "2026-04-27T10:02:00Z",
"mode": "full",
"trigger": "startup",
"skills": [
{
"skill_id": "filesystem-name-or-manifest-id",
"skill_version": "1.0.0",
"identifier": "vendor.skill-name",
"install_path": "/config/myruntime/skills/vendor.skill-name",
"content_md5": "0123456789abcdef0123456789abcdef",
"source": "runtime",
"type": "agent-skill",
"size_bytes": 12345,
"file_count": 12,
"collected_at": "2026-04-27T10:02:00Z",
"metadata": {
"runtime_type": "myruntime"
}
}
]
}要求:
identifier和content_md5必须稳定。mode=full表示这次 inventory 是全量结果,平台会用它对齐实例 skill 状态。- skill 内容变化后要重新计算
content_md5并上报。 - 如果支持上传 skill 包,使用
POST {base}/api/v1/agent/skills/upload,multipart 表单中带file、agent_id、skill_id、skill_version、identifier、content_md5、source。 content_md5必须按目录内容指纹计算,不是 zip 文件 MD5。完整算法见 Skill Content MD5 Calculation Spec。
应用配置 revision:
GET {base}/api/v1/agent/config/revisions/{id}
Authorization: Bearer {session_token}下载 skill version:
GET {base}/api/v1/agent/skills/versions/{external_version_id}/download
Authorization: Bearer {session_token}下载内容只能应用到当前实例,不应泄露到其他实例或日志。
Agent 应按下面规则处理错误:
| 情况 | 处理 |
|---|---|
| HTTP 401 | 删除本地 session token,使用 bootstrap token 重新注册 |
| HTTP 403 | 停止重试当前认证路径,记录简短错误,等待配置修复 |
| HTTP 404 | 对命令或资源类请求标记失败,错误写入 finish |
| HTTP 429 / 5xx / 网络错误 | 指数退避重试,保留本地状态 |
| JSON 校验失败 | 修正 payload;命令执行场景下 finish 为 failed |
| 采样部分失败 | 上报可用字段,并在 health.metrics_collector 写 degraded 或 error |
推荐退避:1s、2s、5s、10s、30s,最大 60s,并加入少量 jitter。
- 不记录 token、API key、完整 Authorization header。
- session token 只写入当前实例持久化目录,文件权限建议
0600。 - 不接受来自用户输入的任意 URL 覆盖
CLAWMANAGER_AGENT_BASE_URL。 - 上传 skill 包前需要限制目录边界,不能打包持久化目录以外的文件。
- 解压平台下发的 skill 包时必须防止 zip slip,例如拒绝
../和绝对路径。 - 命令执行要做超时控制,超过
timeout_seconds应主动终止并 finish 为 failed。
新增 runtime agent 接入完成后,至少满足:
- 创建实例后容器环境中存在 Agent 和 AI Gateway 变量。
- Agent 能注册成功,实例详情页显示 agent online。
- 心跳持续发送,45 秒内不会变 stale。
GET /api/v1/instances/{instance_id}/runtime能看到runtime.system_info和runtime.health。- CPU、Memory、Disk、Network 指标在实例详情页 10 秒内开始刷新。
collect_system_info命令能成功完成,并更新 state report。health_check命令能成功完成,并体现 runtime、agent、metrics collector 状态。- Skill inventory 能上报全量结果,skill 变化后能重新同步。
- session token 过期或 401 后能自动重新注册。
- 日志中没有 bootstrap token、session token、AI Gateway API key。
load env
if CLAWMANAGER_AGENT_ENABLED != "true":
sleep forever
agent_id = "<runtime_type>-" + CLAWMANAGER_AGENT_INSTANCE_ID + "-main"
session = load session from persistent dir
if session missing:
session = register_with_bootstrap_token()
save session
report_state(sample_system_info(), health_check())
sync_skill_inventory(trigger="startup")
loop:
every heartbeat_interval:
resp = heartbeat(summary)
if resp.has_pending_command:
poll_and_execute_commands()
every command_poll_interval:
poll_and_execute_commands()
every 5 seconds:
report_state(sample_system_info(), health_snapshot)
on 401:
delete session
session = register_with_bootstrap_token()
save session