Skip to content

计算资产 API

管理项目的计算资产(分组、资产、连接、远程桌面、容器控制、磁盘),支持 managed(平台托管)和 external(外部连接)两种模式。

基础路径

/projects/:projectId/assets

认证方式

所有端点均需要 JWT Bearer TokenAPI Key 认证(combinedAuth 中间件)。

权限说明

  • 读取操作(GET):需要项目访问权限
  • 写入操作(POST / PATCH / DELETE):需要项目写入权限(write
  • 删除分组:需要项目管理员权限(admin

分组 API

GET /projects/:projectId/assets/groups

获取项目的分组列表。

响应 200

json
{
  "groups": [
    {
      "id": "uuid",
      "name": "生产环境",
      "description": "...",
      "ai_accessible": true,
      "sort_order": 0
    }
  ]
}

POST /projects/:projectId/assets/groups

创建分组。

请求体

参数类型必填说明
namestring分组名称
descriptionstring分组描述
ai_accessibleboolean是否允许 AI 访问

响应 201

json
{
  "group": { ... }
}
状态码说明
409分组名称已存在

PATCH /projects/:projectId/assets/groups/reorder

批量排序分组。

请求体

参数类型必填说明
orderedIdsstring[]按新顺序排列的分组 ID 数组

响应 200

json
{
  "success": true
}

PATCH /projects/:projectId/assets/groups/:groupId

更新分组信息。

请求体

可选字段:namedescriptionai_accessible

响应 200

json
{
  "group": { ... }
}

DELETE /projects/:projectId/assets/groups/:groupId

删除分组(需要项目管理员权限)。

响应 200

json
{
  "success": true
}

资产 API

GET /projects/:projectId/assets/templates

获取可用的镜像模板列表(按类型分组),用于创建 managed 资产时选择模板。

响应 200

json
{
  "templates": [ ... ]
}

GET /projects/:projectId/assets

获取资产列表,支持按分组过滤。

Query 参数

参数类型必填说明
groupIdstring按分组 ID 过滤

响应 200

json
{
  "assets": [
    {
      "id": "uuid",
      "name": "开发服务器",
      "type": "docker",
      "source": "managed",
      "status": "running",
      "group_id": "uuid"
    }
  ]
}

POST /projects/:projectId/assets

创建资产。

请求体

参数类型必填说明
namestring资产名称
typestring类型:docker / browserdesktop 暂未开放)
sourcestring来源:managed(平台托管)/ external(外部连接)
templateIdstring条件必填模板 slug(managed 资产必填)
groupIdstring所属分组 ID
descriptionstring资产描述
regionstring区域
configobject额外配置
diskIdstring磁盘 ID(仅 selfhosted 模式)
connectionobject连接信息(external 资产需提供 protocol/hostname/port)

前置校验

  • managed 资产需要 Worker 就绪,否则返回 503
  • external 资产的 hostname 需通过 SSRF 校验
  • 模板存在且已启用,类型匹配

响应 201

json
{
  "asset": { ... }
}
状态码说明
400输入校验失败
409资产名称已存在
503Assets Worker 未就绪

PATCH /projects/:projectId/assets/:assetId

更新资产信息,支持磁盘绑定更新。

请求体

可选字段:namedescriptiongroupIddiskId(仅 selfhosted 模式,需资产已停止)。

响应 200

json
{
  "asset": { ... }
}

DELETE /projects/:projectId/assets/:assetId

软删除资产。managed 资产若有容器需要 Worker 回收。

响应 200

json
{
  "success": true
}
状态码说明
404资产不存在
503Worker 未就绪,无法回收容器

连接凭证 API

POST /projects/:projectId/assets/:assetId/connections

保存外部资产的连接信息。

请求体

参数类型必填说明
protocolstring协议:vnc / rdp / ssh / telnet(隧道资产仅 ssh / vnc
hostnamestring是*主机名(需通过 SSRF 校验)。*隧道资产不需要传:服务端钉死 127.0.0.1(连接器视角的本机)
portnumber端口(1-65535)
usernamestring用户名
passwordstring密码
private_keystringSSH 私钥
settingsobject额外连接设置

特殊行为

  • 保存成功后自动将 pending/error 状态的资产设为 running
  • 隧道资产(access_channel = 'tunnel')修改端口后,已签发的设备凭据 allowlist 不会自动更新——需重新签发凭据(re-pair)才放行新端口(widening scope 必须重新配对)

响应 201

json
{
  "success": true
}

GET /projects/:projectId/assets/:assetId/connections

获取连接信息(脱敏,仅外部资产)。

响应 200

json
{
  "connections": [ ... ]
}

本地设备(隧道)API

本地设备接入(反向隧道):NAT 后的本地电脑作为 external 资产接入(access_channel = 'tunnel'), 本机运行 WaiNao Connect 连接器出站建立 WSS 反向隧道,网页经隧道桥接直连本机 SSH / VNC。 协议与安全不变量详见仓库文档 docs/features/assets/agent-tunnel.md(docs-site 未收录该页)。

三个端点均要求:资产存在且 source = 'external'access_channel = 'tunnel',否则 400 not_tunnel_asset; 需要项目写入权限。支持 JWT 与 OAuth(scope assets:agent)两种鉴权。

POST /projects/:projectId/assets/:assetId/agent-device

签发(或轮换)连接器设备凭据。明文 token 仅此一次返回,服务端只存 sha256。

前置条件

  • 当前节点配置了 AGENT_TUNNEL_NODE_ORIGIN(未配置 → 503 tunnel_disabled
  • 资产已配置主连接(无 → 400 connection_required),协议为 ssh / vnc(否则 400 tunnel_protocol_unsupported

特殊行为

  • 服务端 allowlist(allowed_services)从资产主连接快照 {protocol, port},与连接器本地 allowlist 双端强制
  • 轮换语义:重复调用即轮换——旧 token 立即失效,若旧连接器在线会被立即断开、资产置 stopped

响应 200

json
{
  "token": "<prefix>.<secret>",
  "prefix": "ab12cd34",
  "nodeOrigin": "https://block2.wainao.chat",
  "pairCommand": "wainao-connect pair --server https://block2.wainao.chat --asset <assetId> --token <prefix>.<secret> --service ssh:22"
}

GET /projects/:projectId/assets/:assetId/agent-device

查询设备配对与在线状态(不含 token hash / 明文)。

响应 200(未配对时仅 { "paired": false }

json
{
  "paired": true,
  "prefix": "ab12cd34",
  "allowedServices": [{ "protocol": "ssh", "port": 22 }],
  "platform": "darwin",
  "online": true,
  "connectedNode": "https://block2.wainao.chat",
  "lastSeenAt": "2026-07-11T12:00:00Z",
  "revokedAt": null,
  "createdAt": "2026-07-11T10:00:00Z"
}

online 反映本节点进程内是否持有该设备隧道;多节点部署下设备连在其他节点时本节点返回 online: false,以 connectedNode 判断实际归属节点。


DELETE /projects/:projectId/assets/:assetId/agent-device

吊销设备凭据(软删 revoked_at)。若设备在线,立即断开隧道并把资产置 stopped; 连接器收到 401/403 后清除本地配置退出。

响应 200

json
{
  "success": true
}

远程桌面连接 API

POST /projects/:projectId/assets/:assetId/connect

发起远程桌面连接,返回加密 Token 和 WebSocket URL。

前置检查

  • Guacamole 代理就绪
  • 资产状态为 running
  • 并发会话数不超过 5
  • 无连接锁定(指数退避)

响应 200

json
{
  "wsUrl": "wss://host/ws/guacamole",
  "token": "encrypted-token",
  "sessionId": "uuid",
  "protocol": "vnc",
  "mode": "control"
}
字段类型说明
wsUrlstringWebSocket 连接地址
tokenstring加密连接令牌(10 秒过期)
sessionIdstring会话 ID
protocolstring连接协议
modestring会话模式:control(控制)/ observe(观察),VNC/RDP 协议同一资产仅一个 control 会话
状态码说明
400资产未运行 / 未配置连接信息
429已达最大并发连接数 / 连接锁定中(含 Retry-After 头)
503Guacamole 代理未就绪

隧道资产附加检查access_channel = 'tunnel',在上述检查之外)

状态码code说明
400device_unpaired设备未配对或凭据已吊销
400device_offline设备离线(连接器未运行)
409tunnel_on_other_node设备连接在其他节点(响应含 connectedNode),需到对应节点访问。DB connected_node 为 owner 权威——即使本节点进程仍残留旧隧道 socket 也不放行

POST /projects/:projectId/assets/:assetId/control/claim

同账号主动接管控制权:把当前 observe(或被提升但物理仍只读的)会话升级为 control,返回新的 control 连接凭证。仅 managed 资产、仅 vnc/rdp 协议。 客户端拿到新 token 后需重连才真正获得读写(observe 的 read-only 在 guacd 连接时固定,运行时不可变)。

请求体

字段类型必填说明
sessionIdstring请求方当前会话 ID(须为本人、本资产、open 状态)

响应 200(结构与 connect 一致)

json
{
  "wsUrl": "wss://host/ws/guacamole",
  "token": "encrypted-token",
  "sessionId": "uuid",
  "protocol": "vnc",
  "mode": "control",
  "terminalMode": "guacamole"
}
状态码code说明
400缺 sessionId / 资产非 managed 或未运行 / 协议非 vnc/rdp / 无效的接管会话
403缺少项目写入权限
404资产不存在
409CONTROL_HELD_BY_OTHER他人正在操作,无法接管(非 retryable)
409CONTROL_CONTENTION / CONTROL_STATE_CHANGED同账号竞争 / 控制权状态已变化(retryable,可重试)
503Guacamole 代理未就绪

接管成功后,被降级的旧 control 端会通过 Realtime(assets_sessionsmode='observe' 广播)收到通知并断开。状态机为「先预创建新 control → 收敛旧 control → 最终 invariant 复读 → 广播降级 → 签 token」,无 DDL、best-effort 收敛。


会话 API

GET /projects/:projectId/assets/:assetId/sessions

获取指定资产的会话列表(W5 云桌面 / 终端会话)。需要项目写入权限(与 connect 一致):只读成员不可枚举他人会话,避免泄露 issued 会话 ID、成员在线状态与连接历史。

路径参数

参数说明
projectId项目 ID
assetId资产 ID(会校验归属于该项目)

Query 参数

参数类型必填说明
statusstring按状态过滤:issued / open / closed / failed
limitnumber返回条数,默认 50,范围 1-200

响应 200

json
{
  "sessions": [
    {
      "id": "uuid",
      "user_id": "uuid",
      "status": "open",
      "mode": "control",
      "protocol": "vnc",
      "issued_at": "2026-06-22T08:00:00.000Z",
      "opened_at": "2026-06-22T08:00:05.000Z",
      "closed_at": null,
      "duration_ms": null,
      "last_heartbeat_at": "2026-06-22T08:05:00.000Z"
    }
  ]
}
字段类型说明
idstring会话 ID
user_idstring会话所属用户 ID
statusstring会话状态:issued / open / closed / failed
modestring会话模式:control(控制)/ observe(观察)
protocolstring连接协议,如 vnc / rdp / ssh
issued_atstring会话签发时间(ISO 8601),列表按此倒序
opened_atstring | null会话建立时间
closed_atstring | null会话关闭时间
duration_msnumber | null会话时长(毫秒)
last_heartbeat_atstring | null最近心跳时间

出于安全考虑,响应不回传 client_info(含 IP/UA),token_nonce 永不出库。

状态码说明
400status 取值非法 / limit 非正整数或超出 1-200 范围
403缺少项目写入权限
404资产不存在(或不属于该项目)

容器控制 API(Managed 资产)

POST /projects/:projectId/assets/:assetId/start

启动 managed 资产。设置 desired_status = 'running',由 Worker 异步执行。

前置检查:资产为 managed 类型且未在运行中,Worker 就绪。

响应 200

json
{
  "success": true,
  "message": "启动请求已提交"
}

POST /projects/:projectId/assets/:assetId/stop

停止 managed 资产。

响应 200

json
{
  "success": true,
  "message": "停止请求已提交"
}

POST /projects/:projectId/assets/:assetId/restart

重启 managed 资产(通过 restart_requested 标记实现)。

响应 200

json
{
  "success": true,
  "message": "重启请求已提交"
}

磁盘 API(仅 selfhosted 模式)

GET /projects/:projectId/assets/disks

获取项目的磁盘列表。

响应 200

json
{
  "disks": [ ... ]
}

POST /projects/:projectId/assets/disks

创建磁盘。

请求体

参数类型必填说明
namestring磁盘名称
descriptionstring磁盘描述

响应 201

json
{
  "disk": { ... }
}

PATCH /projects/:projectId/assets/disks/:diskId

更新磁盘信息。

请求体

可选字段:namedescription

响应 200

json
{
  "disk": { ... }
}

DELETE /projects/:projectId/assets/disks/:diskId

删除磁盘(不能正在被资产使用)。

响应 200

json
{
  "success": true
}
状态码说明
400磁盘正在使用
404磁盘不存在
501非 selfhosted 模式不可用

补充端点

本节补充 plans/need-update-api.md 中尚未覆盖到 docs-site 的接口。端点标题保持严格的 METHOD /path 格式,便于后台文档覆盖率服务识别。

GET /projects/:projectId/assets/capability

读取项目计算资产能力开关与当前可用资源能力。

认证:JWT Bearer Token 或 API Key(combinedAuth)。 路径参数

参数说明
projectId项目 ID

请求:无请求体。Query 参数用于分页、过滤、搜索或状态筛选;未传时按后端默认排序与分页返回。

响应:成功时返回目标资源详情、状态或配置对象。

常见错误400 参数非法,401 未认证,403 权限不足,404 资源不存在;服务端或上游异常返回 500


GET /projects/:projectId/assets/compute-specs

可用计算规格

认证:JWT Bearer Token 或 API Key(combinedAuth)。 路径参数

参数说明
projectId项目 ID

请求:无请求体。Query 参数用于分页、过滤、搜索或状态筛选;未传时按后端默认排序与分页返回。

响应:成功时返回目标资源详情、状态或配置对象。

常见错误400 参数非法,401 未认证,403 权限不足,404 资源不存在;服务端或上游异常返回 500


GET /projects/:projectId/assets/resource-catalog

读取创建资产时需要的资源目录,包括规格、区域、模板或供应商能力。

认证:JWT Bearer Token 或 API Key(combinedAuth)。 路径参数

参数说明
projectId项目 ID

请求:无请求体。Query 参数用于分页、过滤、搜索或状态筛选;未传时按后端默认排序与分页返回。

响应:成功时返回目标资源详情、状态或配置对象。

常见错误400 参数非法,401 未认证,403 权限不足,404 资源不存在;服务端或上游异常返回 500

AI Workflow Editor