docs: update desktop sub encrypted request flow
This commit is contained in:
@@ -18,13 +18,13 @@ Manager 负责辅助控制面:任务草稿桥接、资源/权限、Agnet deplo
|
||||
|
||||
| API 前缀 | 用途 | 认证 |
|
||||
|---|---|---|
|
||||
| `/api/heicode-auth/api/user/tasks/*` | HeicodeTask 任务编排代理,创建任务、追问、查询任务 | `Authorization: Bearer <heicode_access_token>` |
|
||||
| `/api/agnet/user/*` | Manager 用户态 Agnet 控制面,deployment、timeline、artifact、审批 | Manager 登录 session cookie + `New-Api-User: <user_id>` |
|
||||
| `/api/heicode-auth/api/user/tasks/*` | HeicodeTask 任务编排代理,创建任务、追问、查询任务 | V2 加密 body + `Authorization: Bearer <heicode_access_token>` |
|
||||
| `/api/agnet/user/*` | Manager 用户态 Agnet 控制面,deployment、timeline、artifact、审批 | V2 加密 body;未加密 Web 控制台请求继续使用 Manager session + `New-Api-User` |
|
||||
| `/api/user/self` | 查询当前 Manager 用户 | Manager 登录 session cookie |
|
||||
|
||||
### 2.2 桌面端请求 body 加密
|
||||
|
||||
桌面客户端调用 Manager 的 sub 流程接口时,应优先使用与模型调用一致的 V2 加密请求协议。
|
||||
桌面客户端调用 Manager 的 sub 流程接口时,应使用与模型调用一致的 V2 加密请求协议。生产 Manager 自 `1.4.7` 起已支持该能力。
|
||||
|
||||
适用接口:
|
||||
|
||||
@@ -74,9 +74,17 @@ Authorization: Bearer <heicode_access_token>
|
||||
|
||||
原因:该 token 是上游 HeicodeTask 服务认证用;V2 设备签名只证明请求来自已配对的 Manager 桌面设备。
|
||||
|
||||
客户端实现要求:
|
||||
|
||||
1. sub 流程调用不要新增一套加密协议,直接复用模型调用的 `encryptedFetch` / V2 设备签名实现。
|
||||
2. `aad` 和 `canonical` 中的 `path_with_query` 必须是 Manager 实际收到的 path,例如 `/api/agnet/user/tasks/task-1/deployment-draft`,不能把 origin 写进去。
|
||||
3. 加密前的 plaintext 必须是最终 JSON body;签名里的 `sha256_hex(plaintext_body)` 必须和该 JSON 字节完全一致。
|
||||
4. 每次请求必须使用新的 `X-Heicode-Nonce` 和新的 X25519 ephemeral key。
|
||||
5. V2 请求失败时优先读取 `X-Heicode-Auth-Error` 和 `X-Heicode-Server-Time`,用于提示设备未配对、时间漂移、nonce 重放、签名错误或解密失败。
|
||||
|
||||
### 2.3 Manager 用户态 Header
|
||||
|
||||
调用 `/api/agnet/user/*` 时必须带:
|
||||
未加密 Web 控制台请求调用 `/api/agnet/user/*` 时必须带:
|
||||
|
||||
```http
|
||||
Cookie: session=<manager-session>
|
||||
@@ -87,7 +95,7 @@ Accept: application/json
|
||||
|
||||
`New-Api-User` 必须等于当前登录用户 ID,否则会返回未授权。
|
||||
|
||||
使用 V2 加密 body 时,`/api/agnet/user/*` 可以不依赖浏览器 session cookie;Manager 会从设备绑定 token 中解析用户身份。为兼容当前 Web 控制台,未加密请求仍按 session cookie + `New-Api-User` 处理。
|
||||
使用 V2 加密 body 时,`/api/agnet/user/*` 不依赖浏览器 session cookie,也不需要 `New-Api-User`;Manager 会从设备绑定 token 中解析用户身份。为兼容当前 Web 控制台,未加密请求仍按 session cookie + `New-Api-User` 处理。
|
||||
|
||||
### 2.4 统一响应 Envelope
|
||||
|
||||
@@ -1116,31 +1124,61 @@ Runtime callback / timeline 中可使用:
|
||||
## 14. 桌面端最小伪代码
|
||||
|
||||
```ts
|
||||
type Json = Record<string, unknown>
|
||||
|
||||
// 复用模型调用已经使用的 V2 加密 fetch:
|
||||
// - 自动拉取 /api/server-pubkey
|
||||
// - 每次请求生成 nonce + X25519 ephemeral key
|
||||
// - 用 ChaCha20-Poly1305 加密 body
|
||||
// - 用设备 Ed25519 私钥签名 canonical
|
||||
// - 设置 Content-Encoding: heicode-aead-v1 和所有 X-Heicode-* 头
|
||||
async function encryptedManagerRequest<T>(
|
||||
method: 'POST',
|
||||
path: string,
|
||||
body?: Json,
|
||||
extraHeaders: Record<string, string> = {}
|
||||
): Promise<T> {
|
||||
return encryptedFetch<T>(`${managerBaseUrl}${path}`, {
|
||||
method,
|
||||
plaintextJson: body,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
...extraHeaders,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const self = await manager.get('/api/user/self')
|
||||
const userId = self.data.id
|
||||
|
||||
const task = await mcp.post('/api/heicode-auth/api/user/tasks/intent', {
|
||||
intent: userInput,
|
||||
}, {
|
||||
headers: {
|
||||
const task = await encryptedManagerRequest<Envelope<TaskSnapshot>>(
|
||||
'POST',
|
||||
'/api/heicode-auth/api/user/tasks/intent',
|
||||
{ intent: userInput },
|
||||
{
|
||||
Authorization: `Bearer ${heicodeAccessToken}`,
|
||||
},
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
let current = task.data
|
||||
while (current.status === 'configuring') {
|
||||
const followup = findNextFollowup(current)
|
||||
const answer = await askUser(followup)
|
||||
current = await mcp.post(
|
||||
current = await encryptedManagerRequest<Envelope<TaskSnapshot>>(
|
||||
'POST',
|
||||
`/api/heicode-auth/api/user/tasks/${current.id}/answer`,
|
||||
{
|
||||
question_id: followup.id,
|
||||
option_id: answer.id,
|
||||
},
|
||||
{
|
||||
Authorization: `Bearer ${heicodeAccessToken}`,
|
||||
}
|
||||
).then((r) => r.data)
|
||||
}
|
||||
|
||||
const draft = await manager.post(
|
||||
const draft = await encryptedManagerRequest<Envelope<DeploymentDraft>>(
|
||||
'POST',
|
||||
`/api/agnet/user/tasks/${current.id}/deployment-draft`,
|
||||
{
|
||||
task: current,
|
||||
@@ -1148,26 +1186,25 @@ const draft = await manager.post(
|
||||
binding_scope: `task-${current.id}`,
|
||||
role_templates: ['backend', 'frontend', 'reviewer'],
|
||||
default_model_id: 'agnet-model-builder',
|
||||
},
|
||||
{ headers: { 'New-Api-User': String(userId) } }
|
||||
}
|
||||
)
|
||||
|
||||
const deployment = await manager.post(
|
||||
const deployment = await encryptedManagerRequest<Envelope<Deployment>>(
|
||||
'POST',
|
||||
'/api/agnet/user/deployments',
|
||||
{ orchestration_plan: draft.data.orchestration_plan },
|
||||
{ headers: { 'New-Api-User': String(userId) } }
|
||||
{ orchestration_plan: draft.data.orchestration_plan }
|
||||
)
|
||||
|
||||
const deploymentId = deployment.data.deployment_id
|
||||
|
||||
setInterval(async () => {
|
||||
const timeline = await manager.get(
|
||||
const timeline = await manager.get<Envelope<DeploymentTimeline>>(
|
||||
`/api/agnet/user/deployments/${deploymentId}/timeline`,
|
||||
{ headers: { 'New-Api-User': String(userId) } }
|
||||
)
|
||||
renderTimeline(timeline.data.timeline)
|
||||
|
||||
const approvals = await manager.get(
|
||||
const approvals = await manager.get<Envelope<ApprovalList>>(
|
||||
`/api/agnet/approvals?status=pending&deployment_id=${deploymentId}`,
|
||||
{ headers: { 'New-Api-User': String(userId) } }
|
||||
)
|
||||
@@ -1175,11 +1212,21 @@ setInterval(async () => {
|
||||
}, 5000)
|
||||
```
|
||||
|
||||
兼容说明:
|
||||
|
||||
- 桌面客户端对 `POST` 等有 body 的 sub 请求走 V2 加密时,`/api/agnet/user/*` 不需要 `New-Api-User`,也不依赖浏览器 cookie。
|
||||
- `GET` 查询接口本身没有请求 body,当前生产兼容路径仍使用 Manager session cookie + `New-Api-User`。如果桌面本地服务后续要完全脱离 session cookie 查询 timeline / artifact / approval,需要再补“无 body 的 V2 设备签名 GET”协议。
|
||||
- `/api/heicode-auth/*` 仍必须带 `Authorization: Bearer <heicode_access_token>`,该 token 只用于上游 HeicodeTask 认证。
|
||||
- 如果客户端临时还没有接入 V2 加密,只能作为调试兼容路径使用 Manager session + `New-Api-User` 调 `/api/agnet/user/*`;正式桌面流程不要依赖该路径。
|
||||
|
||||
## 15. 当前生产注意事项
|
||||
|
||||
1. `https://code.xinghanlab.com` 的 Manager 用户态接口已上线。
|
||||
1. `https://code.xinghanlab.com` 的 Manager 用户态接口已上线,当前生产版本为 `1.4.7`。
|
||||
2. Manager 本地控制面可创建 `sub_mode=agile/waterfall` deployment。
|
||||
3. 生产 Manager 已配置 Agent Manager Runtime,当前直接走 `http://20.212.121.126`;域名和 HTTPS 后续单独处理,不作为客户端当前接入阻塞项。
|
||||
4. `deployment-draft -> create -> detail -> stop` 已在生产验证通过,客户端可按本文参数形状接入。
|
||||
4. V2 加密 `deployment-draft` 已在生产验证通过:真实构造 `Content-Encoding: heicode-aead-v1` 请求返回 200,`sub_mode=agile`,`user_id=22`。
|
||||
5. `events/logs/artifacts/sk-snapshots/timeline` 查询接口已验证不报错;真实阶段事件、产物、SK 调用结果需要 Agent Manager 执行任务并回调后才会出现。
|
||||
6. `POST /api/heicode-auth/api/user/tasks/intent` 需要桌面客户端提供 `heicode_access_token`;没有该 token 会返回 401。
|
||||
6. `deployment-draft -> create -> detail -> stop` 已在生产验证通过,客户端可按本文参数形状接入。
|
||||
7. `POST /api/heicode-auth/api/user/tasks/intent` 需要桌面客户端提供 `heicode_access_token`;没有该 token 会返回 401。
|
||||
8. malformed V2 请求已在生产验证会返回 `X-Heicode-Auth-Error`,客户端应把该头转成可读错误提示。
|
||||
9. 当前 `GET` 查询接口没有请求 body,仍按 session + `New-Api-User` 验证;这不影响 body 加密要求,但客户端若要全链路无 cookie,需要后续补无 body 签名 GET。
|
||||
|
||||
Reference in New Issue
Block a user