Skip to content

REST API 参考

CommHub Server 提供 REST API 供 Dashboard、CLI 和第三方系统调用。

基础信息

Base URLhttp://YOUR_IP:9200
认证Authorization: Bearer <token> (推荐)?token=<token> URL query 为 SSE / 浏览器 EventSource 保留(有 access-log 泄漏风险,详见 安全设计
内容类型application/json
编码UTF-8
Endpoint 数30+(12 类公开 1 · 认证 5 · 网络 5 · 数据查询 10 · 任务派发 2 · MCP 1 · SSE 1 · Token 管理 4 · 网络成员 6 · 节点改名 3 · Tmux 调试 3 (opt-in) · Legacy 2
全 endpoint sourceserver/src/index.ts:390-1160

公开端点

GET /health

源码 ↗

健康检查,不需要认证。

bash
curl http://localhost:9200/health
json
{
  "ok": true,
  "version": "0.8.4",
  "api_version": "v3",
  "transport": "streamable-http",
  "sessions_count": 0,
  "sse_connections": 0,
  "sse_sessions": {},
  "auth": "user-token",
  "security": "secured",
  "tmux": "disabled",
  "v3_auth": true,
  "multi_network": true,
  "license": "trial",
  "uptime": 3600
}

license 字段是 v0.6 legacy

license: "trial" 是 v0.6 时代 14 天试用机制的残留字段,Apache 2.0 OSS 后不再作为商业功能门控(自部署没有"过期"概念)。send_task 路径仍跑 trial 检查仅为后向兼容(verify server/src/tools.ts:521 license_expired 仍 emit),若命中见 troubleshootingv0.9.x / v0.10.x scope 都未动(Recovery & Observability 主题为先),整段移除排到 v0.11+ / 未排期。


认证端点

POST /api/auth/register

源码 ↗

注册新用户。第一个注册的用户自动成为管理员。

bash
# v0.8+:注册不需要 master token,公开端点
curl -X POST http://localhost:9200/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "username": "alice",
    "password": "mypassword2026",
    "email": "alice@example.com",
    "display_name": "Alice"
  }'

请求体

字段类型必需说明
usernamestring用户名(2-50 字符,字母/数字/下划线/中文)
passwordstring密码(>= 8 字符 + 非弱密码字典;首个 bootstrap admin 例外,>= 4 即可)
emailstring邮箱
display_namestring显示名

响应

json
{
  "ok": true,
  "user": {
    "user_id": "u_abc123",
    "username": "alice",
    "display_name": "Alice",
    "email": "alice@example.com",
    "role": "admin"
  },
  "token": "utok_xxxxxxxxxxxxxxxx",
  "network_token": "ntok_xxxxxxxxxxxxxxxx",
  "network_id": "net_xxxxxxxx"
}

user 对象 5 字段对照 server/src/auth.ts:7-13 AuthUser interface(display_name / email 可为 null);tokenutok_ 给 CLI/Dashboard 用,network_tokenntok_ 给 default network 里的 agent 用。

常见 4xx(verify auth.ts:30-48 register()):

状态error触发条件
400username must be at least 2 characters用户名 < 2 字符
400username too long (max 50)用户名 > 50 字符
400username contains invalid characters含非 a-zA-Z0-9_\- 或非中文字符
400username already taken用户名重复
400password must be at least 8 characters第二个起注册用户密码 < 8
400password must be at least 4 characters首位用户(bootstrap admin)密码 < 4
400password is too common命中弱密码字典(password-dict.ts,首位用户豁免)
429too many requests, try again later超过 30/分 IP rate limit(index.ts:430;localhost 豁免,详见 安全 — IP rate limit

速率限制:30 次/分钟 per IP。


POST /api/auth/login

源码 ↗

用户登录。

bash
# v0.8+:登录不需要 master token,公开端点
curl -X POST http://localhost:9200/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "username": "alice",
    "password": "mypassword2026"
  }'

响应

json
{
  "ok": true,
  "user": {
    "user_id": "u_abc123",
    "username": "alice",
    "display_name": "Alice",
    "email": "alice@example.com",
    "role": "admin"
  },
  "token": "utok_xxxxxxxxxxxxxxxx",
  "network_id": "net_xxxxxxxx"
}

user 对象 5 字段同 register 响应(注 email 可为 null);network_id 是该用户作为 owner 的 default network(auth.ts:113-115ORDER BY role = 'owner' DESC LIMIT 1)。每次 login 都签发新的 utok_(不撤销已有,多设备登录互不踢,auth.ts:102-110)。

常见 4xx(verify auth.ts:94-100 login()):

状态error触发条件
401invalid username or password用户名不存在 密码哈希不匹配(auth.ts:99-100 故意把两种错误合并成同一文案,避免 username enumeration);server 同时写 login_failed audit
429too many attempts, try again later超过 10/分 IP rate limit(index.ts:445;触发时写 login_rate_limited audit + clientIP)

速率限制:10 次/分钟 per IP。


GET /api/auth/me

源码 ↗

获取当前用户信息。

bash
curl http://localhost:9200/api/auth/me \
  -H "Authorization: Bearer utok_xxx"

响应

json
{
  "ok": true,
  "user": {
    "user_id": "u_abc123",
    "username": "alice",
    "display_name": "Alice",
    "email": "alice@example.com",
    "role": "admin"
  },
  "networks": [
    { "network_id": "net_xxx", "network_name": "default", "member_role": "owner" },
    { "network_id": "net_yyy", "network_name": "team-prod", "member_role": "member" }
  ],
  "current_network": "net_xxx"
}

networks 数组列出当前用户所属的所有 network 及在该 network 的 member_role(字段名跟 GET /api/networks 一致);anet whoami 用它显示「← current」标记(结合 config.json 里的 network_id 字段)。current_network 字段是 server 端根据当前 token 的 binding 解析出的 network_id(utok_ 是全局 token 取 ~/.anet/config.json 的 network_id;ntok_ 强制 binding 到颁发时的 network)。


PUT /api/auth/me

源码 ↗

修改个人信息。

bash
curl -X PUT http://localhost:9200/api/auth/me \
  -H "Authorization: Bearer utok_xxx" \
  -H "Content-Type: application/json" \
  -d '{"display_name": "Alice Smith", "email": "alice@example.com"}'

请求体

字段类型必需说明
display_namestring显示名
emailstring邮箱

只更新提供的字段(server/src/index.ts:478-479if (body.X) 条件 SQL);username / role / password 通过此 endpoint 修改。

响应(成功):

json
{
  "ok": true,
  "user": {
    "user_id": "u_abc123",
    "username": "alice",
    "display_name": "Alice Smith",
    "email": "alice@example.com",
    "role": "admin"
  }
}

常见 4xx(verify server/src/index.ts:469-491):

状态error触发条件
400<JSON parse error>请求体不是合法 JSON(catch 块直接 echo 异常 message)
401token required / invalid token缺/无效 utok_

字段缺失不报错

如果只传 display_name 而省略 email(或两者都不传),server 不会报 400 —— index.ts:478-479if (body.X) 条件累加 SQL,全部省略时只 re-SELECT user 返回。无字段长度校验(v0.9.x / v0.10.x 都未动,schema-level 校验排到 v0.11+ / 未排期)。


POST /api/auth/password

源码 ↗

修改密码。

bash
curl -X POST http://localhost:9200/api/auth/password \
  -H "Authorization: Bearer utok_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "old_password": "oldpass",
    "new_password": "newpass123"
  }'

响应

json
{
  "ok": true,
  "revoked": 2,
  "token": "utok_xxxxxxxxxxxxxxxx",
  "token_id": "tok_new_session_id"
}

revoked 字段是其他设备上被撤销的 utok_/atok_ 数量(不含本次调用方自己的 token,那个是 index.ts L490 单独撤销的)。

关键副作用 (verify auth.ts:267-282 changePassword + revokeOtherUserTokens + index.ts:493-503):

  1. 当前调用方的 utok_ (resolved.tokenId) 立即撤销(index.ts:503 revokeToken(...) 显式删)
  2. 其他设备的所有 utok_ / atok_ 同步撤销(auth.ts:269-270 DELETE ... WHERE user_id=? AND network_id IS NULL AND token_id != ?currentTokenId 一锅端)—— 计数返回到 revoked 字段
  3. ntok_ 不受影响revokeOtherUserTokens 只删 network_id IS NULL 的 token,agent node 用 ntok_ 跑着的不会被改密打断;跟 account-system 改密码副作用 ZH 描述一致)
  4. utok_ (issued.token) 颁发给调用方作为响应返回 —— 调用方应立即用新 token 覆盖本地存储
  5. 写 audit log: action='password_changed'

anet passwd CLI 行为一致(CLI 拿到新 token 后自动写 ~/.anet/config.json)。其他设备下次请求拿 401 invalid token → 必须 anet login 重新登录。

常见 4xx(verify auth.ts:274-282 changePassword()):

状态error触发条件
400new password must be at least 8 characters新密码 < 8 字符
400new password is too common命中弱密码字典(password-dict.ts
400user not founduser_id 不存在(罕见,token 已 expire 或 user 被 admin 删)
400incorrect current passwordold_password 跟存的 hash 不匹配
401token required / invalid token缺 / 无效 utok_

跟 register 强度规则一致

密码强度规则跟 register 共用 validatePasswordStrength()(参 POST /api/auth/register 4xx)。bootstrap admin 豁免仅适用于首位注册,改密码无豁免


网络端点

GET /api/networks

源码 ↗

获取用户所属的所有网络。

bash
curl http://localhost:9200/api/networks \
  -H "Authorization: Bearer utok_xxx"

响应

json
{
  "ok": true,
  "networks": [
    {
      "network_id": "net_abc123",
      "network_name": "default",
      "owner_id": "u_abc123",
      "description": "Auto-created default network",
      "settings": null,
      "visibility": "private",
      "max_members": 50,
      "created_at": "2026-04-12 10:00:00",
      "updated_at": "2026-04-12 10:00:00",
      "member_role": "owner"
    }
  ]
}

networks 数组每行 10 字段:9 个 networks 表字段 (server/src/db.ts:168-177 含 v3 migration visibility + max_members) + 1 个 join 字段 member_roleauth.ts:382-388 JOIN network_members)。排序:owner 在前,其余按 created_atORDER BY nm.role = 'owner' DESC, n.created_at)。settings / description 可为 nullntok_ 调用只返回当前 binding 那一个 network(不是全部);utok_ 返回所有所属网络。


POST /api/networks

源码 ↗

创建新网络。

bash
curl -X POST http://localhost:9200/api/networks \
  -H "Authorization: Bearer utok_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "prod",
    "description": "生产环境网络"
  }'

响应(成功):

json
{
  "ok": true,
  "network_id": "net_xyz789",
  "network_name": "prod"
}

常见 4xx(verify auth.ts:182-206 createNetwork()):

状态error触发条件
400network name already exists同一 owner 名下已有同名 network(UNIQUE(owner_id, network_name) 约束)
400quota exceeded: max N networks for free plan触发 plan quota 配额限制(v0.8 起 admin 用户豁免;free plan 默认 max_networks_owned=2,当前 quota 仍在 auth.ts:184-189 enforced,跟 networks 表的 max_members 不同:那个 dormant、这个 active)
401token required / invalid token未提供 / 提供了无效 utok_

GET /api/networks/:id

源码 ↗

获取网络详情(含成员身份校验:必须是该 network 成员或系统 admin,否则 403)。

bash
curl http://localhost:9200/api/networks/net_abc123 \
  -H "Authorization: Bearer utok_xxx"

响应

json
{
  "ok": true,
  "network": {
    "network_id": "net_abc123",
    "network_name": "prod",
    "owner_id": "u_abc123",
    "description": "生产环境网络",
    "settings": null,
    "visibility": "private",
    "max_members": 50,
    "created_at": "2026-04-12 10:00:00",
    "updated_at": "2026-04-12 10:00:00"
  },
  "stats": {
    "nodes": 5,
    "sessions": 4,
    "tasks": [
      { "status": "replied", "count": 42 },
      { "status": "running", "count": 3 }
    ]
  }
}

network 对象 9 字段 = SELECT * FROM networks WHERE network_id = ?1 (server/src/index.ts:734) 完整 schema (含 v3 migration visibility + max_members)。settings 字段保留作未来 per-network JSON 配置,目前为 nullstats.tasks 按 status 聚合(同 GET /api/stats 内嵌结构)。


PUT /api/networks/:id

源码 ↗

重命名网络(仅 owner)。

bash
curl -X PUT http://localhost:9200/api/networks/net_abc123 \
  -H "Authorization: Bearer utok_xxx" \
  -H "Content-Type: application/json" \
  -d '{"name": "development"}'

请求体

字段类型必需说明
namestring新网络名(注意字段名是 name 不是 network_name;缺失时返回 name required 400)

响应(成功):

json
{ "ok": true }

常见 4xx

状态error触发条件
400name required请求体缺 name 字段(注意不是 network_name
400network not foundnetwork_id 不存在
400not your network调用者不是该网络的 owner
400name already taken该 owner 名下已有同名网络

写 audit log action='network_renamed'detail 字段记新名。


DELETE /api/networks/:id

源码 ↗

删除网络(仅 owner,必须无活跃 session)。

bash
curl -X DELETE http://localhost:9200/api/networks/net_abc123 \
  -H "Authorization: Bearer utok_xxx"

响应(成功):

json
{ "ok": true }

常见 4xx

状态error触发条件
400network not foundnetwork_id 不存在
400not your network调用者不是该网络的 owner
400network has N active session(s) — stop them first还有正在跑的 agent session 关联此网络(anet node stop <name> 全部停掉后再删)

写 audit log action='network_deleted'


数据查询端点

GET /api/status

源码 ↗

获取所有 session 状态。

bash
curl "http://localhost:9200/api/status?network_id=net_xxx" \
  -H "Authorization: Bearer ntok_xxx"

查询参数

参数说明
network_id按网络过滤(绑了 ntok_ 时此参数被强制覆盖为 token 自带的 network)
status按状态过滤(idle / working / offline)

响应

json
{
  "ok": true,
  "sessions": [
    {
      "resume_id": "sdk-n_xxx",
      "alias": "代码1号",
      "status": "idle",
      "agent": "agent-node:codex-sdk",
      "model": "your-model-id",
      "task": null,
      "progress": null,
      "last_seen_at": "2026-04-12 10:00:00"
    }
  ],
  "summary": {
    "idle": 7,
    "working": 1,
    "offline": 2,
    "total": 10
  }
}

summary 字段是按 status 聚合的计数(server/src/index.ts:780-787):working 类把 working / blocked / error / waiting_input / running / busy 都归一进去;offline 类是 server 端 updated_at 落后 10 分钟的 session(每次 GET 实时计算并写回 DB);其他算 idle


GET /api/tasks

源码 ↗

获取任务列表。

bash
curl "http://localhost:9200/api/tasks?status=running&limit=10" \
  -H "Authorization: Bearer ntok_xxx"

查询参数

参数说明
network_id按网络过滤(绑了 ntok_ 时此参数被强制覆盖为 token 自带的 network)
status按状态过滤;任何 Task 生命周期状态机 状态都可传
to_name按接收者过滤
from_name按发送者过滤
limit最大条数(默认 50)

响应

json
{
  "ok": true,
  "tasks": [
    {
      "task_id": "t_a1b2c3d4",
      "from_node_id": null,
      "from_name": "指挥室",
      "to_node_id": "node_xxx",
      "to_name": "代码1号",
      "priority": "normal",
      "status": "replied",
      "content": "写一个 Python 快排算法",
      "result": "已完成,使用快排实现",
      "in_reply_to": null,
      "requires_response": "reply",
      "scope": "single",
      "created_at": "2026-04-12 10:00:00",
      "delivered_at": "2026-04-12 10:00:01",
      "started_at": "2026-04-12 10:00:02",
      "completed_at": "2026-04-12 10:00:15",
      "expires_at": "2026-04-12 11:00:00"
    }
  ],
  "count": 1,
  "stats": [
    { "status": "replied", "count": 85 },
    { "status": "running", "count": 5 }
  ]
}

字段对照 tasks 表 schema (server/src/db.ts:87-105) SELECT *:主键是 task_id 不是 message_id;任务完成时间字段是 completed_at 不是 replied_at;TTL 字段是 expires_at 绝对时间不是 ttl_seconds 相对秒(ttl_seconds 仅 send_task 入参用,写入时算成 expires_at)。anet tasks CLI 用 from_name / to_name / status / created_at / content 渲染表格 (cli.ts L2810-2817)。


GET /api/nodes

源码 ↗

获取节点列表(持久化节点信息,区别于 session 的临时状态)。

bash
curl http://localhost:9200/api/nodes \
  -H "Authorization: Bearer ntok_xxx"

查询参数

参数说明
node_id按节点 ID 过滤
alias按别名过滤
network_id按网络过滤(ntok_ 强制 binding 时此参数被覆盖)

响应

json
{
  "ok": true,
  "nodes": [
    {
      "node_id": "node_abc123",
      "node_name": "代码1号",
      "alias": "代码1号",
      "runtime": "claude-agent-sdk",
      "model": "your-model-id",
      "config_path": ".anet/nodes/代码1号/config.json",
      "channels": null,
      "server": "http://localhost:9200",
      "hostname": "dev-machine",
      "network_id": "net_xxxxx",
      "created_at": "2026-04-12 10:00:00",
      "updated_at": "2026-04-12 10:00:00"
    }
  ],
  "count": 1
}

nodes vs sessions

nodes 表是持久节点身份(创建即写入,删 agent 才删),sessions 表是运行时心跳状态(agent 启动写入,10 分钟无心跳标 offline)。看 agent 是否在线用 GET /api/status,看 agent 配置元数据用本 endpoint。


DELETE /api/nodes/:ref

源码 ↗

删除节点(hub server 端)—— 从 nodes 表删持久身份 + 从 sessions 表删运行时心跳记录(同一个 transaction),并往 alias channel + network channel 推 node_deleted SSE 事件让 dashboard 实时刷新。配套 PR #86「node delete cascade and node_deleted SSE」。

bash
# :ref 接受 node_id / node_name / alias 任一(URL-encoded)
curl -X DELETE "http://localhost:9200/api/nodes/n_abc12345" \
  -H "Authorization: Bearer ntok_xxx"

# 中文 alias 要 URL-encode
curl -X DELETE "http://localhost:9200/api/nodes/%E4%BB%A3%E7%A0%811%E5%8F%B7" \
  -H "Authorization: Bearer ntok_xxx"

路径参数:ref 在 server 端 server/src/index.ts:1170-1174 用 OR 拼 node_id = ? OR node_name = ? OR alias = ? 找节点(网络作用域过滤后取 updated_at DESC 第一条)。

响应(成功,200):

json
{
  "ok": true,
  "deleted": true,
  "node_id": "n_abc12345",
  "node_name": "代码1号",
  "alias": "代码1号",
  "network_id": "net_xxxxx"
}

SSE 副作用:删完往两个 SSE channelnode_deleted event(server/src/index.ts:1192-1199):

  • alias 自身的 SSE channel(如果还有订阅者)
  • network_id 的 user 级 SSE channel(每个网络成员都能立刻看到)
json
// node_deleted SSE event payload
{ "type": "node_deleted", "node_id": "n_abc12345", "node_name": "代码1号", "alias": "代码1号", "network_id": "net_xxxxx" }

错误响应

状态error触发条件
404node not found:ref 在当前网络作用域内匹配不到 nodes 行
403permission_denied调用方在该 network 是 viewer,或 ntok_ 锁定的不是这个 network

网络作用域:跟 GET /api/nodes 一致 —— ntok_ 锁 token 的 network;utok_ 看到有权限的所有 networks 里的节点。

anet node delete 不一样

这个 REST endpoint 只删 hub server 端的 nodes / sessions 行;删本地 .anet/nodes/<alias>/ 配置目录,也自动撤销 ntok_。从 hub 端清节点身份用本 endpoint;从 client CLI 一站式清干净(含本地 dir + tmux + 可选撤销 ntok_)用 anet node delete <alias>(详见 CLI — anet node delete)。


GET /api/servers

源码 ↗

物理服务器hostname + ip)聚合 agent 列表 + host 实时遥测,给 dashboard 「服务器侧栏」用。Refs issue #119

bash
curl http://localhost:9200/api/servers \
  -H "Authorization: Bearer ntok_xxx"

返回前的副作用:跟 /api/status 一样,先把 10 分钟以上没心跳的 session 标 offlineUPDATE sessions SET status='offline' WHERE updated_at < cutoff),再做聚合。所以本 endpoint 的 agent_count 反映所有 session(不限 status);要排除 offline 自己在客户端过滤 last_seen 即可。

响应:注意是裸数组,不是 { ok: true, ... } 包裹(跟同文件其他 endpoint 不同,是历史选择)。

json
[
  {
    "hostname": "dev-machine",
    "ip": "192.168.1.42",
    "agent_count": 7,
    "cpu_load_1min": 0.42,
    "cpu_cores": 8,
    "mem_avail_gb": 12.3,
    "mem_used_gb": 19.7,
    "last_seen": "2026-05-15 11:23:45"
  }
]
字段来源说明
hostnameagent-node os.hostname()没 telemetry 的老 agent 显示 "unknown"
ipagent-node 首个 non-internal IPv4没 telemetry 显示 "unknown"
agent_countserver 聚合时 +1该 host 上的 session 总数(含 offline)
cpu_load_1minLinux /proc/loadavg;macOS/Win os.loadavg()(Windows 永远 [0,0,0] 主动转 null同 hostname+ip 取最新那条
cpu_coresos.cpus().length同上
mem_avail_gbLinux /proc/meminfo MemAvailable;macOS/Win os.freemem()GB, 0.1 精度
mem_used_gbmem_total - mem_availGB, 0.1 精度
last_seenCOALESCE(last_seen_at, updated_at)该 host 下最新心跳时间

网络作用域:跟 /api/status 一样走 addNetworkScope —— ntok_ 强制锁定该 token 的 network,utok_ 看到自己有权限的所有 networks。

数据来源

host telemetry 由 agent-node 在每次 report_status 时带上(issue #119 step 1,agent-node v2.3.8+)。老 agent 不带 telemetry 字段时 SQL NULLhostname/ip 渲染成 "unknown"、其他字段为 null。server 端 schema 是 silent-drop unknown keys,可以独立升级 agent / server。


GET /api/server/:host/health

源码 ↗ · v0.10.0 / commhub-server@0.8.2

单台物理服务器的当前健康快照 + 24h 分桶历史 telemetry。Refs issue #99(守护节点 Phase 1 scaffold)。

需要 agent-network@2.2.1+

通过 anet hub start 默认路径要拿到这个 endpoint,agent-network 必须 ≥ 2.2.1(v0.10.1 hotfix PINNED_SERVER_VERSION 0.8.00.8.2)。老版本(含 2.2.0)anet hub start 仍跑 commhub-server@0.8.0,本 endpoint 不存在 → 404。绕开方案:手动 bunx --bun @sleep2agi/commhub-server@latest --host 127.0.0.1 起新版 server。

bash
curl http://localhost:9200/api/server/dev-machine/health \
  -H "Authorization: Bearer ntok_xxx"

# host 含特殊字符(如 IP `192.168.1.42` 不用 encode;hostname 含空格 / `/` 需 urlencode)
curl "http://localhost:9200/api/server/$(python3 -c 'import urllib.parse; print(urllib.parse.quote("my host"))')/health" \
  -H "Authorization: Bearer ntok_xxx"

路径参数

参数说明
:hosthostnameip(任一匹配即可,URL-encoded)

返回前的副作用:跟 /api/servers 一样先把 10 分钟无心跳 session 标 offline,再做查询。

响应

json
{
  "ok": true,
  "host": "dev-machine",
  "hostname": "dev-machine",
  "ip": "192.168.1.42",
  "agent_count": 7,
  "alert_level": "ok",
  "alerts": [],
  "latest": {
    "cpu_load_1min": 0.42,
    "cpu_cores": 8,
    "cpu_pct": 5.3,
    "mem_total_gb": 32.0,
    "mem_used_gb": 19.7,
    "mem_avail_gb": 12.3,
    "disk_total_gb": 500.0,
    "disk_used_gb": 213.5,
    "disk_avail_gb": 286.5,
    "last_seen": "2026-05-16 18:23:45"
  },
  "history": {
    "5m":  [{ "ts": "...", "cpu_pct": 5.1, "mem_used_gb": 19.5, ... }, ...],
    "1h":  [{ "ts": "...", "cpu_pct": 4.8, "mem_used_gb": 18.9, ... }, ...],
    "24h": [{ "ts": "...", "cpu_pct": 4.2, "mem_used_gb": 17.6, ... }, ...]
  }
}
字段说明
host请求路径里传入的 host 值
agent_count该 host 上活跃 session 数(窗口取最新一行的 COUNT(*) OVER ()
alert_levelok / warn / critical(取 serverAlertLevel(latest) 计算;v0.10.2+ 加 disk_avail_gb < 1 → critical / < 5 → warn 触发,verify server/src/index.ts:253-258
alerts当前命中告警列表,alert_level != ok 时非空
latest该 host 最近一次心跳的瞬时 telemetry(CPU / mem / disk + last_seen
latest.disk_total_gb / disk_used_gb / disk_avail_gbv0.10.2 起(agent-node 2.4.1+host-telemetry.ts readDiskStats())—— 通过 execFileSync('df', ['-k', '/']) 采样,POSIX -k Linux + macOS 同 parse 路径;Windows / 解析失败 graceful null(dashboard 渲染 不误导成 0)。老 agent (< 2.4.1) 不带字段时三字段都 null
history.5m最近 5min,1 min bucket(取自 agent_telemetry 历史表)
history.1h最近 1h,5 min bucket
history.24h最近 24h,1 hour bucket;v0.10.2 起 bucket 内附 disk_avail_min / disk_used_max 字段(极值聚合,verify server/src/index.ts:311-326

404{ "ok": false, "error": "server not found" } —— 该 host 没有任何(活跃或离线)session 命中。

网络作用域:同 /api/serversntok_ 锁 token network;utok_ 看所有有权限 networks。


GET /api/server/:host/agents

源码 ↗ · v0.10.0 / commhub-server@0.8.2

单台服务器上的 agent 列表 + per-agent 进程 telemetry(rss / cpu / uptime / in-flight count)。Refs issue #99 + issue #142 per-agent process telemetry。

bash
curl http://localhost:9200/api/server/dev-machine/agents \
  -H "Authorization: Bearer ntok_xxx"

响应

json
{
  "ok": true,
  "host": "dev-machine",
  "agent_count": 2,
  "agents": [
    {
      "alias": "代码1号",
      "runtime": "claude-code-cli",
      "raw_agent": "claude-code-cli",
      "model": null,
      "status": "idle",
      "task": null,
      "progress": 0,
      "last_seen": "2026-05-16 18:23:45",
      "health": "online",
      "hostname": "dev-machine",
      "ip": "192.168.1.42",
      "telemetry": {
        "cpu_load_1min": 0.42, "cpu_cores": 8, "cpu_pct": 5.3,
        "mem_total_gb": 32.0, "mem_used_gb": 19.7, "mem_avail_gb": 12.3,
        "disk_total_gb": 500.0, "disk_used_gb": 213.5, "disk_avail_gb": 286.5,
        "process_rss_bytes": 245678912, "process_rss_mb": 234.3,
        "process_cpu_pct": 3.1, "process_uptime_seconds": 1842,
        "process_in_flight_count": 0
      },
      "process_telemetry": {
        "rss_bytes": 245678912, "rss_mb": 234.3,
        "cpu_pct": 3.1, "uptime_seconds": 1842, "in_flight_count": 0
      }
    }
  ]
}
字段说明
agents[].runtimenormalizeRuntime(agent) 归一化后的 runtime ID(claude-code-cli / claude-agent-sdk / codex-sdk
agents[].raw_agentagent 字段(未归一化),方便排查
agents[].healthagentHealthChip(status, last_seen) 健康灯(online / idle / offline / 等)
agents[].telemetry该 agent 心跳带上的 host-level + process-level 完整 telemetry(reading-friendly 视图)
agents[].process_telemetryper-agent 进程 telemetry(rss_bytes / rss_mb / cpu_pct / uptime_seconds / in_flight_countissue #142 ship in agent-node@2.4.0,server schema align in commhub-server@0.8.2

404{ "ok": false, "error": "server not found" } —— 该 host 没匹配到任何 session。

网络作用域:同 /api/server/:host/health


GET /api/messages

源码 ↗

获取最近 inbox 消息列表。

bash
curl "http://localhost:9200/api/messages?limit=100" \
  -H "Authorization: Bearer ntok_xxx"

查询参数

参数说明
since起始时间,默认最近 1 小时
limit最大条数,默认 100,最大 500

响应

json
{
  "ok": true,
  "messages": [
    {
      "id": "m_abc123",
      "from_alias": "代码1号",
      "to_alias": "指挥室",
      "type": "reply",
      "priority": "normal",
      "content": "[代码1号] 已完成,使用快排实现",
      "created_at": "2026-04-12 10:00:15",
      "network_id": "net_xxxxx"
    },
    {
      "id": "m_def456",
      "from_alias": "指挥室",
      "to_alias": "代码1号",
      "type": "task",
      "priority": "normal",
      "content": "写一个快排算法",
      "created_at": "2026-04-12 10:00:00",
      "network_id": "net_xxxxx"
    }
  ]
}

字段对照 server SELECT (server/src/index.ts:1013) id, session_name as to_alias, from_session as from_alias, type, priority, content, created_at, network_id —— 主键字段是 id 不是 message_id,含 priority + network_id 两个之前 doc 漏掉的字段。

当前 schema 限制

SELECT 暂未包含 in_reply_to 字段;轮询匹配回复消息时按 from_alias + type='reply' + recency 启发式匹配(详见 cli.ts 注释)。


GET /api/completions

源码 ↗

获取完成记录(agent 通过 report_completion MCP 工具写入的总结性记录,跟 tasksstatus='replied' 的简单 reply 不同)。

bash
curl "http://localhost:9200/api/completions?since=2026-04-12T00:00:00Z" \
  -H "Authorization: Bearer ntok_xxx"

查询参数

参数说明
since起始时间(ISO 8601);默认最近 24 小时
network_id按网络过滤(绑了 ntok_ 时此参数被强制覆盖为 token 自带的 network)

固定返回最多 100 条(server 端硬编码 LIMIT 100,无 limit 参数)。

响应

json
{
  "ok": true,
  "completions": [
    {
      "id": "c_abc123",
      "session_name": "代码1号",
      "task": "写一个 Python 快排算法",
      "result": "已完成,使用 Lomuto partition,附加 unit test",
      "artifacts": "[{\"file\":\"quicksort.py\"}]",
      "score": 0.95,
      "duration_minutes": 2.5,
      "network_id": "net_xxxxx",
      "completed_at": "2026-04-12 10:00:15"
    }
  ]
}

artifacts 字段是 JSON 字符串(agent 自由 schema),消费侧需 JSON.parse()


GET /api/task_events

源码 ↗

获取任务状态变更日志(task 生命周期审计)。每次 task status 变化 server 都会插一行,是排查「任务卡住 / 谁改了状态」的主要数据源。

bash
curl "http://localhost:9200/api/task_events?task_id=t_a1b2c3d4" \
  -H "Authorization: Bearer ntok_xxx"

查询参数

参数说明
task_id按特定 task 过滤(不传则返回最近所有 task 的事件)
network_id按网络过滤(绑了 ntok_ 时此参数被强制覆盖为 token 自带的 network)
limit最大条数(默认 50,最大 500)

network_id 不在 task_events handler 本体里读,而是所有 REST 端点统一走 resolveRestNetworkScope (index.ts:189-208)utok_ 调用可传 network_id 指定网络(校验 membership),ntok_ 调用强制锁到 token 绑定的 network,system admin 可查任意网络。

响应

json
{
  "ok": true,
  "events": [
    {
      "id": 1234,
      "task_id": "t_a1b2c3d4",
      "from_status": "delivered",
      "to_status": "running",
      "actor": "node_abc123",
      "detail": null,
      "created_at": "2026-04-12 10:00:02"
    },
    {
      "id": 1235,
      "task_id": "t_a1b2c3d4",
      "from_status": "running",
      "to_status": "replied",
      "actor": "node_abc123",
      "detail": "completed in 12s",
      "created_at": "2026-04-12 10:00:14"
    }
  ],
  "count": 2
}

事件按 created_at DESC 排序(最新的在最前)。actor 是触发状态变更的发起方(agent node_id / 'hub' / 'system' 等),from_status 在初始 created 事件可能为 null。完整状态机见 Task 生命周期


GET /api/stats

源码 ↗

获取统计数据。

bash
curl http://localhost:9200/api/stats \
  -H "Authorization: Bearer utok_xxx"

响应

json
{
  "ok": true,
  "network_id": "net_xxx",
  "tasks": {
    "total": 100,
    "by_status": [
      { "status": "replied", "count": 85 },
      { "status": "running", "count": 5 }
    ]
  },
  "sessions": {
    "by_status": [
      { "status": "idle", "count": 7 },
      { "status": "offline", "count": 3 }
    ]
  },
  "nodes": { "total": 10 },
  "recent_tasks": []
}

GET /api/server-logs

源码 ↗

读 hub 进程内内存环形 buffer 里的最近 N 行 console 日志(debug 用)。users.role = 'admin' 系统 admin 可调(跟 GET /api/users / GET /api/audit-log 同款 system-admin gate,注意不是网络级 admin)。Buffer 容量默认 500 行(由 COMMHUB_LOG_RING env 调,index.ts:40)。

bash
curl "http://localhost:9200/api/server-logs?limit=100" \
  -H "Authorization: Bearer utok_xxx"

查询参数

参数说明
limit最大行数(默认 200,最大 = COMMHUB_LOG_RING,默认上限 500)
sinceISO 8601 时间戳;只返回 ts > since 的新日志(增量轮询用)

响应

json
{
  "ok": true,
  "logs": [
    { "ts": "2026-04-12T10:00:00.123Z", "level": "log", "line": "[10:00:00] 代码1号 (sdk-n_xxx) → report_status: working | 写排序算法" },
    { "ts": "2026-04-12T10:00:01.456Z", "level": "warn", "line": "⚠ deprecation: ..." }
  ],
  "capacity": 500
}

按时间倒序返回(最新在最前);每行 line 截断到 4000 字符(index.ts:46)。进程重启 buffer 清空 —— 这不是持久化日志,要持久化日志做 stdout/journald 重定向。

4xx

状态error触发条件
401auth required / invalid token缺/无效 utok_
403admin only调用者 users.role !== 'admin'(首位注册用户默认是 admin)

GET /api/audit-log

源码 ↗

获取审计日志。权限:所有已认证用户都可调,但非系统 admin** 只能看到自己的 log 行**(server 端走 users.role !== 'admin' 自动加 WHERE user_id = <caller> 过滤,见 server/src/index.ts:1089)。系统 admin (users.role = 'admin') 看全部 + 可用 user_id 参数过滤任意用户。

不是网络级 admin/owner

这里的 "admin" 指 users.role='admin'系统级,首位注册用户默认是),不是网络级别的 owner / admin / member / viewer。详见 GET /api/users 同款区分。

bash
curl "http://localhost:9200/api/audit-log?limit=50" \
  -H "Authorization: Bearer utok_xxx"

查询参数

参数说明
limit最大条数(默认 50,最大 200)
action按 action 过滤(任何角色可用)
user_id按用户过滤(仅系统 admin 有效,非 admin 传也被忽略,强制走 own-logs)

响应

json
{
  "ok": true,
  "logs": [
    {
      "user_id": "u_abc123",
      "username": "alice",
      "action": "password_reset_by_admin",
      "target_type": "user",
      "target_id": "u_def456",
      "detail": "local cli reset-user",
      "created_at": "2026-04-12 10:00:00"
    },
    {
      "user_id": "u_abc123",
      "username": "alice",
      "action": "network_renamed",
      "target_type": "network",
      "target_id": "net_xyz789",
      "detail": "prod-v2",
      "created_at": "2026-04-12 09:55:00"
    }
  ],
  "count": 2
}

字段名是 logs + count不是 audit_log,之前 doc 误写)。audit_log schema 见 server/src/db.ts:201-212 完整 10 列(含 ip + network_id)。完整 action 值列表 + 触发场景安全设计 — 审计日志

create_network 不审计

POST /api/networks 不调 logAudit,所以 audit_log 里不会create_network 行。看 network 创建请走 GET /api/networks 列表对比,或借 target_type='network' + action='network_renamed' 间接推断(跟 security.md 审计 ::: info 一致)。


GET /api/users

源码 ↗

获取所有用户列表(仅系统 admin —— 即 users.role = 'admin',跟网络级别的 owner / admin / member / viewer 角色不同)。

bash
curl http://localhost:9200/api/users \
  -H "Authorization: Bearer utok_xxx"

响应

json
{
  "ok": true,
  "users": [
    {
      "user_id": "u_abc123",
      "username": "alice",
      "display_name": "Alice",
      "email": "alice@example.com",
      "role": "admin",
      "created_at": "2026-04-12 10:00:00"
    },
    {
      "user_id": "u_def456",
      "username": "bob",
      "display_name": null,
      "email": null,
      "role": "user",
      "created_at": "2026-04-13 09:00:00"
    }
  ]
}

4xx

状态error触发条件
401auth requiredAuthorization header
403admin required调用者不是 users.role='admin'(仅首位注册用户默认 admin)

响应不含 password_hash 字段(SELECT 显式 enumerate 6 列)。按 created_at 升序排(首位注册的 admin 在最前)。


任务派发端点

REST 版的 send_task / broadcast(非 MCP 路径,适合 webhook / 反代 / Dashboard 用)。

POST /api/task

源码 ↗

REST 版 send_task:往指定 alias 的 inbox 投递任务 + 写 tasks 表 + SSE 推 new_task

bash
curl -X POST http://localhost:9200/api/task \
  -H "Authorization: Bearer ntok_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "alias": "代码1号",
    "task": "写一个快排算法",
    "priority": "high",
    "ttl_seconds": 7200
  }'

请求体(verify TaskSchema):

字段类型必需说明
aliasstring目标 Agent 别名(最大 200 字符)
taskstring任务内容(最大 10000 字符)
priorityenumhigh / normal(默认)/ low
fromstring发送者标识(默认 "api"
network_idstring目标 network(utok_ 调用时;ntok_ 调用强制绑定)
ttl_secondsnumber过期秒数(默认 3600;非 schema 字段,server 在 index.ts:876 直接取 body.ttl_seconds

响应(成功):

json
{ "ok": true, "message_id": "uuid-xxx" }

常见 4xx

状态error触发条件
400invalid JSON请求体解析失败
400invalid input字段类型/长度不符合 TaskSchema(含 details 字段附 zod 报错)
400network_id required for user token when multiple networks are availableutok_ 调用方有多个 network,必须显式指定 network_id
403access denied to requested networkutok_ 调用方不是 network_id 成员
403permission_denied角色不足(viewer 不能写)

写 audit log(/api/task 处理函数 index.ts:845-861 没有 logAudit 调用,跟 POST /api/networks 的「不写」一致);成功后给 target alias 推送 new_task SSE 事件。

POST /api/broadcast

源码 ↗

REST 版 broadcast:往一组 session 的 inbox 同步广播 + 给每个 SSE 推 broadcast

bash
curl -X POST http://localhost:9200/api/broadcast \
  -H "Authorization: Bearer utok_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "5 分钟后例会,请保存进度",
    "filter_status": "idle"
  }'

请求体(verify BroadcastSchema):

字段类型必需说明
messagestring广播内容(最大 10000 字符;字段名是 message 不是 content
filter_serverstring只发给指定 server 字段的 session
filter_statusstring只发给指定 status 的 session(如 idle / working

跟 MCP broadcast 同款字段;from_session 不是参数,server 端硬编码 'api'index.ts:945 跟 MCP 版的 'hub' 不同)。

响应(成功):

json
{
  "ok": true,
  "recipients": 10,
  "message_ids": ["uuid-1", "uuid-2"]
}

message_ids.length === recipients,每个 target session 一个 inbox row。

常见 4xx

状态error触发条件
400invalid JSON / invalid input请求体解析或字段验证失败
400network_id required for user token when broadcastingutok_ 调用方有多个 network,须先 ?network_id= 或带 ntok_ 绑定
403permission_denied角色不足(viewer 不能写)

MCP 端点

POST /mcp

源码 ↗

MCP Streamable HTTP 端点,Agent 通过此端点调用 MCP Tools。

bash
curl -X POST http://localhost:9200/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ntok_xxx" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "get_all_status",
      "arguments": {}
    },
    "id": 1
  }'

SSE 端点

GET /events/:name

源码 ↗

SSE 实时推送端点,客户端通过长连接接收事件。路径段 :name 是一个通用 channel 名(源码里叫 :session):Agent 用自己的 node alias 订阅、Dashboard 用 username 订阅 user channel。SSE 层本身是 per-channel-name 的 Mappush.ts:11 clients),不区分 alias / username —— pushEvent(name, ...) 推给谁取决于谁注册了那个 name(如 node.renamed 同时推 alias 流和成员 username channel,见下表)。

bash
# 推荐:Authorization header(避免 token 写进代理 / 浏览器历史 / access log)
curl -N -H "Authorization: Bearer ntok_xxx" http://localhost:9200/events/代码1号

# 兼容:URL query token(为浏览器原生 EventSource 保留,但有 access-log 泄漏风险 — 见 [安全设计](/concepts/security))
curl -N "http://localhost:9200/events/代码1号?token=ntok_xxx"

推送的事件类型(verify grep pushEvent server/src/{tools,rename}.ts + push.ts):

事件触发条件数据
connected初始连接握手(push.ts:35,每个 client 连上 SSE 时发一次){session, network_id}
new_task收到新任务(send_task / retry_task / reassign_task / REST POST /api/task{inbox_count, priority, from}
new_message收到新消息(send_message{from, message_id}
new_reply收到 reply(send_reply{from, message_id, in_reply_to, status}
broadcast收到广播(broadcast 工具){inbox_count}
chained_reply子任务完成自动串回上游父任务发起者 (tools.ts:286/646){parent_task_id, child_task_id, child_alias}
node.renamedRFC-010 节点改名 COMMIT 时广播(rename.ts:100-123),推给 old + new 两个 alias 流 + 每个网络成员的 user channel(dashboard 订阅的是 /events/<username> user channel、不是 per-alias 流,#84 SSE channel fix){txn_id, alias(=new_alias), network_id, data:{old_alias, new_alias, surfaces_updated[], history_policy:"preserve"}}

旧 doc 在 new_message 上写过 message 字段、broadcast 上写过 {content, from} —— 都不对。verify tools.ts:571 + 911 实际 payload 只有上表中字段。

校正:原表列过 heartbeat event with {time} payload,源码不发这个事件。push.ts:38-44 实际发 SSE comment 行 : keepalive\n\n(每 30s 一次,纯粹是给 proxy / LB 防 idle timeout 用),不会被 EventSource onmessage / addEventListener 触发,也不带 JSON payload。connected event 才是真正每次连接发一次的初始事件(agent-node 在 agent-node/src/cli.ts 显式处理它)。

示例 SSE 数据流

event: connected
data: {"type":"connected","session":"代码1号","network_id":"net_xxx"}

event: new_task
data: {"type":"new_task","inbox_count":1,"priority":"high","from":"指挥室"}

: keepalive

: keepalive

Token 管理端点

POST /api/auth/node-token

源码 ↗

为某个节点创建网络绑定的 ntok_anet node create 会自动调用它,写入到 .anet/nodes/<node-name>/config.jsontoken 字段。

bash
curl -X POST http://localhost:9200/api/auth/node-token \
  -H "Authorization: Bearer utok_xxx" \
  -H "Content-Type: application/json" \
  -d '{"network_id": "net_xxx", "node_name": "代码1号"}'

响应(成功):

json
{
  "ok": true,
  "token": "ntok_xxxxxxxxxxxxxxxx"
}

token 是该 (node_name, network_id) 组合的 ntok_,hub 端强制 binding——agent 用这个 token 调 MCP 时,server 自动锁定到 network_id,跨网络访问拒绝。详见 Token 概念 — ntok_

常见 4xx(verify auth.ts:130-141 createNetworkTokenForNode() + index.ts:514-529 route):

状态error触发条件
400network_id and node_name required请求体缺 network_idnode_name
400not a member of this network调用者不在 network_id 内(必须先 join 才能 mint ntok_)
400no write access to this network调用者是 viewer 角色(viewer 不能创建 full-access network token)
401auth required / invalid token缺/无效 utok_

POST /api/auth/tokens

源码 ↗

创建 API Token。

bash
curl -X POST http://localhost:9200/api/auth/tokens \
  -H "Authorization: Bearer utok_xxx" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-agent", "network_id": "net_xxx"}'

响应

json
{
  "ok": true,
  "token": "atok_xxxxxxxxxxxxxxxx",
  "token_id": "tok_abc123def456"
}

Token 明文只返回一次

token 字段是明文 Token,仅在创建时返回这一次——hub 端只存 hash。丢失后请用 DELETE /api/auth/tokens/:id 撤销 + 重新创建。

这个 endpoint 创建的是 legacy atok_

本 endpoint 走 auth.ts:243 generateToken() 颁发 atok_ 前缀 + scope='full' token,是 V2 时代的兼容路径,不是 v0.8 主线的 utok_ / ntok_。新代码请用:

详见 Token 体系

GET /api/auth/tokens

源码 ↗

列出用户的所有 Token。

bash
curl http://localhost:9200/api/auth/tokens \
  -H "Authorization: Bearer utok_xxx"

响应

json
{
  "ok": true,
  "tokens": [
    {
      "token_id": "tok_abc123def456",
      "name": "node:代码1号",
      "scope": "network",
      "network_id": "net_xxxxxxxx",
      "last_used_at": "2026-04-12 10:00:00",
      "created_at": "2026-04-10 09:00:00"
    },
    {
      "token_id": "tok_xyz789",
      "name": "user-login",
      "scope": "user",
      "network_id": null,
      "last_used_at": null,
      "created_at": "2026-04-12 10:30:00"
    }
  ]
}

每行 6 字段对照 auth.ts:209-213 listTokens SELECT:token_id / name / scope / network_id / last_used_at / created_atscope 取值 user (utok_) / network (ntok_) / full (legacy atok_);network_idnetwork / full scope 有值。按 created_at DESC 排序。明文 Token 字段不返回(只能在 POST 创建时拿一次)。

DELETE /api/auth/tokens/:id

源码 ↗

撤销 Token(hub 端立即吊销,跟 anet logout 仅本机清 token 区别开)。

bash
curl -X DELETE http://localhost:9200/api/auth/tokens/tok_xxx \
  -H "Authorization: Bearer utok_xxx"

响应(成功):

json
{ "ok": true }

4xx

状态error触发条件
404token not foundtoken_id 不存在或不属于当前 user(auth.ts:252-254 DELETE ... WHERE token_id=?1 AND user_id=?2 受影响行 0)

写 audit log action='token_revoked'。撤销后该 token 的下一次请求拿 401 invalid token


网络成员端点

GET /api/networks/:id/members

源码 ↗

获取网络成员列表(仅 owner / admin)。

bash
curl http://localhost:9200/api/networks/net_xxx/members \
  -H "Authorization: Bearer utok_xxx"

响应

json
{
  "ok": true,
  "members": [
    {
      "user_id": "u_abc123",
      "username": "alice",
      "display_name": "Alice",
      "role": "owner",
      "joined_at": "2026-04-12 10:00:00"
    },
    {
      "user_id": "u_def456",
      "username": "bob",
      "display_name": "Bob",
      "role": "member",
      "joined_at": "2026-04-15 14:30:00"
    }
  ]
}

anet network members CLI 用这个响应渲染成员列表(按 m.display_name || m.username 显示,role 加 emoji 图标)。

POST /api/networks/:id/members

源码 ↗

添加网络成员(owner / admin only;通常 invite 流程更顺,POST /api/networks/:id/invite 创建邀请码让对方自行加入)。

bash
curl -X POST http://localhost:9200/api/networks/net_xxx/members \
  -H "Authorization: Bearer utok_xxx" \
  -H "Content-Type: application/json" \
  -d '{"user_id": "u_def456", "role": "member"}'

请求体

字段类型必需说明
user_idstring目标用户 ID
roleenumadmin / member / viewer(默认 member

响应(成功):

json
{ "ok": true }

常见 4xx

状态error触发条件
403not a member of this network调用者本身不在该网络
403owner/admin required调用者是 member / viewer,无权添加成员
400user already a memberuser_id 已经是该网络成员

写 audit log action='member_added'detail 字段记 <user_id> as <role>

PUT /api/networks/:id/members/:user_id

源码 ↗

修改成员角色(仅 owner,不能修改 owner 自己的角色)。

bash
curl -X PUT http://localhost:9200/api/networks/net_xxx/members/u_def456 \
  -H "Authorization: Bearer utok_xxx" \
  -H "Content-Type: application/json" \
  -d '{"role": "admin"}'

请求体

字段类型必需说明
roleenum新角色:admin / member / viewer(不能改成 owner

响应(成功):

json
{ "ok": true }

常见 4xx

状态error触发条件
403not a member of this network调用者本身不在该网络
403owner required仅 owner 能改角色(admin 也不行)
400cannot assign owner rolerole 字段传 owner,server 拒绝(owner 通过创建网络获得,不能后续 promote)
400member not found or is owner目标 user_id 不在网络内,或者是 owner 自己(owner 角色不可改)

写 audit log action='member_role_changed'detail 字段记 <user_id> → <new_role>。FAQ Q17 提到的「改角色」入口就是这个 endpoint。

DELETE /api/networks/:id/members/:user_id

源码 ↗

移除成员(owner / admin only,不能移除 owner 自己)。

bash
curl -X DELETE http://localhost:9200/api/networks/net_xxx/members/u_def456 \
  -H "Authorization: Bearer utok_xxx"

响应(成功):

json
{ "ok": true }

常见 4xx

状态error触发条件
403not a member of this network调用者本身不在该网络
403owner/admin required调用者是 member / viewer,无权移除成员
400not a member目标 user_id 不在该网络
400cannot remove owner目标是 owner(删除网络才能移除 owner,见 DELETE /api/networks/:id

写 audit log action='member_removed'detail 字段记 <user_id>

POST /api/networks/:id/invite

源码 ↗

创建邀请码。

bash
curl -X POST http://localhost:9200/api/networks/net_xxx/invite \
  -H "Authorization: Bearer utok_xxx" \
  -H "Content-Type: application/json" \
  -d '{"role": "member", "max_uses": 5, "expires_days": 7}'

请求体

字段类型必需说明
roleenumadmin / member / viewer(默认 member
max_usesnumber最大使用次数(默认 1-1 无限)
expires_daysnumber过期天数(不传则不过期)

响应(成功):

json
{
  "ok": true,
  "invite_code": "inv_abc123def456"
}

常见 4xx(verify auth.ts:344-356 createInvite() + index.ts:634 route handler):

状态error触发条件
400invalid rolerole 不是 admin / member / viewer 之一
403not a member of this network调用者本身不在该网络(index.ts:659 callerRole gate
403owner/admin required调用者是 member / viewer,无权 issue 邀请码

接收方用 anet network join inv_abc123def456POST /api/networks/join 加入。invite_codeinv_ 前缀 + 12 字符(auth.ts:346 slice(0, 12))。

POST /api/networks/join

源码 ↗

用邀请码加入网络。

bash
curl -X POST http://localhost:9200/api/networks/join \
  -H "Authorization: Bearer utok_xxx" \
  -H "Content-Type: application/json" \
  -d '{"invite_code": "inv_abc123def456"}'

响应(成功):

json
{
  "ok": true,
  "network_id": "net_abc123",
  "role": "member"
}

常见 4xx(verify auth.ts:358-378 joinByInvite()):

状态error触发条件
400invalid invite codeinvite_code 不存在
400invite code fully usedused_count >= max_uses(max_uses=-1 无限)
400invite code expiredexpires_at < now()(不传 expires_days 创建则不会过期)
400already a member of this network调用者已是该网络成员

anet network join CLI 拿到该响应后会自动切换到加入的 network(即 ~/.anet/config.jsonnetwork_id 字段更新为 res.network_id),并打印 Joined network as <role>。同时 server 自动颁发一个 network_id 绑定的 token 给加入者(auth.ts:374-377 name='auto-join' scope='full'),写 audit network_joined


错误响应格式

错误通常返回以下格式:

json
{
  "ok": false,
  "error": "error_code",
  "message": "Human-readable error message (when available)"
}
HTTP 状态码含义
200成功
400请求参数错误
401未认证
403权限不足
404资源不存在
429速率限制
500服务器错误

节点改名端点(RFC-010)

RFC-010 active-rename 两阶段事务的协调端点,由 anet node rename 内部调用(流程见 node-lifecycle §7)。一般不直接手调,列在此处供集成方参考。三个端点都要 Authorization: Bearer(缺 token 401 / 无效 token 401)。

POST /api/node-rename/prepare

源码 ↗

PHASE 1:登记一笔改名事务(old node 不动,全程可回滚)。成功后写 node_rename_prepared audit。

bash
curl -X POST http://localhost:9200/api/node-rename/prepare \
  -H "Authorization: Bearer utok_xxx" -H "Content-Type: application/json" \
  -d '{"network_id":"net_xxx","old_alias":"old-bot","new_alias":"new-bot"}'
字段必填说明
network_id节点所在网络
old_alias当前 alias
new_alias目标 alias

响应{ ok, txn_id } —— txn_id 用于后续 commit / abort。三个字段缺一返回 400。

POST /api/node-rename/commit

源码 ↗

PHASE 2 C1:提交改名事务(CommHub 路由切到 new_alias)。成功后写 node_rename_committed audit。

bash
curl -X POST http://localhost:9200/api/node-rename/commit \
  -H "Authorization: Bearer utok_xxx" -H "Content-Type: application/json" \
  -d '{"txn_id":"..."}'

body { txn_id } 必填(缺则 400)。

POST /api/node-rename/abort

源码 ↗

回滚改名事务(C1 之前调用,old node 恢复原状)。成功后写 node_rename_aborted audit。

bash
curl -X POST http://localhost:9200/api/node-rename/abort \
  -H "Authorization: Bearer utok_xxx" -H "Content-Type: application/json" \
  -d '{"txn_id":"..."}'

body { txn_id } 必填(缺则 400)。


Tmux 调试端点(opt-in)

默认关闭

仅在 COMMHUB_ENABLE_TMUX=1 启动 hub 时启用(index.ts:14)。默认全部返回 404 tmux disabled。启用后还需 (a) 调用方 IP 在 COMMHUB_TMUX_ALLOWLIST 允许范围(逗号分隔,默认仅 localhost;verify index.ts:17)+ (b) users.role='admin' system-admin auth。设计意图:让 hub 主机上的 agent tmux session 暴露给同机的 dev / dashboard 调试,绝不要在公网开。公网部署 hardening 步骤见 生产部署 §5 tmux 控制面已关闭

GET /api/tmux/:name

源码 ↗

抓取指定 tmux session 当前 pane 末尾 N 行输出(tmux capture-pane -t <name> -p 包装)。

bash
curl "http://localhost:9200/api/tmux/anet-node-代码1号?lines=50" \
  -H "Authorization: Bearer utok_xxx"

查询参数

参数说明
lines末尾行数(默认 30)

响应(成功):

json
{ "ok": true, "tmux_name": "anet-node-代码1号", "lines": 50, "output": "...captured pane content..." }

POST /api/tmux/:name/send

源码 ↗

往指定 tmux session 注入按键(tmux send-keys -t <name> "<text>" Enter 包装)。

bash
curl -X POST "http://localhost:9200/api/tmux/anet-node-代码1号/send" \
  -H "Authorization: Bearer utok_xxx" \
  -H "Content-Type: application/json" \
  -d '{"text": "/help", "enter": true}'

请求体

字段类型必需说明
textstring要注入的按键内容
enterboolean是否末尾追加 Enter 键(默认 true

4xx / 4xx 都共用

状态error触发条件
404tmux disabled未设 COMMHUB_ENABLE_TMUX=1
403tmux access denied from this ip调用方 IP 不在 COMMHUB_TMUX_ALLOWLIST 范围(默认仅 localhost)
401 / 403需 admin auth(同 GET /api/server-logs
400text is required (POST only)请求体缺 text 字段
400<tmux stderr>tmux 子进程非 0 退出(如 session 不存在)

GET /ws/tmux/:name

源码 ↗

WebSocket 端点 —— 实时流式推送指定 tmux session 的 pane 输出。是 GET /api/tmux/:name 的 live 版本:HTTP 那个是一次性 capture-pane,这个是连上后持续 stream。鉴权门控跟上面两个 HTTP 端点完全一致(走同一个 requireTmuxAccess —— COMMHUB_ENABLE_TMUX=1 + IP 在 COMMHUB_TMUX_ALLOWLIST 内 + users.role='admin' auth;任一不满足在 WS upgrade 前就被拒)。

ws://localhost:9200/ws/tmux/anet-node-代码1号

连上后 server 按固定间隔 tmux capture-pane 把 pane 内容推过来;连接断开自动停止轮询。同样绝不要在公网开


Legacy 端点(v0.6 时代,OSS 后不再演进)

Apache 2.0 OSS 后不再依赖

v0.8 起项目转 Apache 2.0 开源 + 自部署,没有官方付费 license。下面两个 endpoint 是 v0.6 试用 / 激活码体系的遗留路径,hub 仍保留 licenses 表 + 14 天 trial 兜底,但新用户和文档主线不需要碰。命中 license_expiredtroubleshooting

GET /api/license

源码 ↗

licenses 表第一行(按 created_at 升序),返回 trial / pro 状态 + 剩余天数。

bash
curl http://localhost:9200/api/license
# → 公开端点(不需要 Authorization header)

响应(trial / pro):

json
{
  "ok": true,
  "license": { "type": "trial", "expires_at": "2026-04-25 12:00:00", "days_left": 12, "expired": false },
  "limits": { "max_agents": 5, "max_networks": 1, "max_tasks_day": 100 }
}

响应(无 license 行):

json
{ "ok": true, "status": "no_license" }

POST /api/license/activate

源码 ↗

注入 pro license key(index.ts:411 只校验 key.startsWith('anet-') && length >= 16没真正的服务端校验)。删除原有 license 行 + 写新 pro license(限额 50 agent / 10 network / 10000 task/day,过期 365 天)。

bash
curl -X POST http://localhost:9200/api/license/activate \
  -H "Content-Type: application/json" \
  -d '{"key": "anet-anything-16-plus-chars"}'

响应(成功):

json
{ "ok": true, "type": "pro", "expires_in_days": 365 }

4xx

状态error触发条件
400key required请求体缺 key
400invalid license keykey 不以 anet- 开头或长度 < 16(仅前缀长度校验,无真实签名

这个 endpoint 几乎是「自助绕过」,OSS 后只为兜底命中 license_expired 用。详见 troubleshooting — license_expired + CLI anet activate


下一步

对应 MCP 工具

  • MCP 工具 — Agent 端用的 stdio MCP 协议(自动调 REST)

深入鉴权

实战调用

  • Dashboard — 实际 UI 调用了哪些 REST 端点

Powered by Sleep2AGI