docs(integration): flesh out unified-api with flow details planned in the spec

Bring the client-facing API doc up to parity with heicode-sub-mode-flow-spec.md.
Adds the interfaces/fields the spec planned but the API layer hadn't captured:

- §3.1 create body: resource_bindings (mandatory git) + git_options
  (base_branch/mode/allowed_paths/write_mode) + X-Idempotency-Key; mandatory-git rule.
- §5.2 NEW git_ref delivery reference (provider/repo/branch/commit_sha/tag/PR/
  agent_branches) — how the client learns where to git pull. §4.1 workflow now
  returns git_ref + per-agent current_action/branch. Fixed orphaned 5.3 block;
  §5 reorganized (5.1 git / 5.2 git_ref / 5.3 non-code artifacts / 5.4 retired).
- §6 rewritten as "修改/重做 (baseline = git)" table (messages/execute/redo/stop),
  local-edits retired.
- §4b NEW planned SSE contract (event envelope, event types incl delivery.pushed,
  Last-Event-ID reconnect, polling fallback).
- §7 deploy: formalized short-lived credential lease interface (POST .../lease,
  TTL/revoke/approval-gate, V2-encrypted).
- §8 split into 8.1 web-console (session) and 8.2 client-facing V2-signed
  endpoints (list/get/create/secret/delete/lease) + binding metadata + precheck.
- §4 verdict rule now keyed on git_ref; §0.1 flow + success criterion updated;
  §11 new error codes (SUB_GIT_BINDING_REQUIRED/LEASE_*/APPROVAL_REQUIRED/
  BUDGET_EXCEEDED), legacy artifact codes struck through. budget_exceeded status.

All not-yet-built items are marked 🔴 so the client can pre-wire to the contract.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 12:08:58 +08:00
co-authored by Claude Opus 4.8
parent 233f99fd22
commit 32ebc2bb6d
+171 -40
View File
@@ -28,19 +28,21 @@ Manager 生产地址:`https://code.xinghanlab.com`
## 0.1 推荐完整流程
```text
0. (一次性) 绑定资源: github/gitea 仓库 + (可选) vm/db/blob -> 凭据进 Key Vault, 拿 resource_binding_id (§8)
0. (★必做) 绑定 git 仓库 (github/gitea) + (可选) vm/db/blob -> 凭据进 Key Vault, 拿 resource_binding_id (§8)
未绑 git 则禁用 sub 入口 (强制绑 git, §3.1)
1. GET /api/heicode/capabilities 取模式/模型,渲染模式与模型选择
2. POST /api/heicode/sub-agile/tasks (需求包) 创建任务 -> 响应里拿 deployment_id(=task_id)
2. POST /api/heicode/sub-agile/tasks (需求包, 带 resource_bindings.git + X-Idempotency-Key)
创建任务 -> 响应里拿 deployment_id(=task_id) (§3.1)
3. (可选) POST .../tasks/{id}/execute 若需"先建后跑"或重试派发
4. 轮询 GET .../tasks/{id}/workflow (运行中 3-5s) 读 display_status + 三层状态 + agents (work 视图)
4. 轮询 GET .../tasks/{id}/workflow (运行中 3-5s) / 将来 SSE 读 display_status + 三层状态 + agents + git_ref (§4b)
5. 出现 approval 时: GET .../approvals?status=pending -> approve/reject
6. display_status=completed 后: git clone/pull 用户自己的仓库拿代码产物 (完整工程+历史+diff)
(非代码产物如 test_report/摘要 -> GET .../artifacts 查看; HM 不托管/不解析项目代码, 见 §5)
7. 不满意 -> 改/重做 (两条路): POST .../messages|/execute 回云端改; 或 git pull 本地用客户端自己的 agent 改
8. (可选) 部署: 用户确认 -> 客户端凭 resource_binding_id 向 HM 取凭据(V2) -> 客户端本地 git/ssh/部署 (§7, HM 不代执行)
6. display_status=completed 后: 按 workflow 里的 git_ref 做 git clone/pull (完整工程+历史+diff) (§5)
(非代码运行信息如 test_report/摘要 -> GET .../artifacts 查看; HM 不托管/不解析项目代码)
7. 不满意 -> 改/重做: POST .../messages|/execute 回云端改; 或 git pull 本地用客户端自己的 agent 改 (§6)
8. (可选) 部署: 用户确认 -> 客户端凭 resource_binding_id 取短期租约(V2) -> 客户端本地 git/ssh/部署 (§7, HM 不代执行)
```
> 成功判据:`display_status=completed` 且 artifacts 非空 = **跑完且有真东西**;但**"代码对不对"客户端自己拉下来跑/测 + 用户 review**(`completed` 不保证正确,见 §4)。
> 成功判据:`display_status=completed` 且 `git_ref` 有新 commit = **跑完且有真东西**;但**"代码对不对"客户端自己拉下来跑/测 + 用户 review**(`completed` 不保证正确,见 §4)。
## 1. 认证与加密(重要)
@@ -99,7 +101,7 @@ signature = base64( ed25519_sign(device_priv, sha256(canonical)) )
| POST | `/api/heicode/sub-agile/tasks/{task_id}/messages` | 持续对话:追加用户消息 |
| POST | `/api/heicode/sub-agile/tasks/{task_id}/execute` | 触发/确保执行(未派发则派发 Runtime) |
| DELETE | `/api/heicode/sub-agile/tasks/{task_id}` | 删除/停止任务 |
| GET | `/api/heicode/sub-agile/tasks/{task_id}/workflow` | 工作流投影(右侧面板:mode/display_status/phases/agents(含 tokens/tools/elapsed/artifact_ids)/metrics/artifacts) |
| GET | `/api/heicode/sub-agile/tasks/{task_id}/workflow` | 工作流投影(右侧面板:mode/display_status/phases/agents(含 current_action/branch/tokens/tools/elapsed/artifact_ids)/metrics/**git_ref**/artifacts) |
| POST | `/api/heicode/sub-agile/tasks/{task_id}/stop` | 停止 |
| GET | `/api/heicode/sub-agile/tasks/{task_id}/timeline` | 时间线(events/callbacks/artifacts 聚合) |
| GET | `/api/heicode/sub-agile/tasks/{task_id}/logs` | 日志(含 `user_logs` 友好 + `debug_logs` 原始两层) |
@@ -134,15 +136,33 @@ signature = base64( ed25519_sign(device_priv, sha256(canonical)) )
"default_model": "gpt-5.4",
"roles": {"backend":"gpt-5.4","frontend":"gpt-5.4"}
},
"roles": ["backend", "frontend"]
"roles": ["backend", "frontend"],
"resource_bindings": {
"git": "rb_git_xxx", // ★ 必填:用户绑定的 git 仓库(§8)。缺则拒绝建任务
"vm": "rb_vm_xxx", // 可选:部署目标,仅在需要时
"database": "rb_db_xxx", // 可选
"blob": "rb_blob_xxx" // 可选
},
"git_options": { // 可选:覆盖 binding 默认
"base_branch": "main", // 从哪个分支拉基线
"mode": "existing", // greenfield(绿地新建) | existing(已有代码上增量改)
"allowed_paths": ["backend/", "frontend/"], // 限定 agent 工作目录,避免动无关代码
"write_mode": "pr" // pr(开 PR/交付分支,建议) | branch | main
}
}
```
- **★ `resource_bindings.git` 必填**:sub 模式强制绑 git(spec §3.1 决策)。未传或 binding 失效 → HM 拒绝建任务,返回 `SUB_GIT_BINDING_REQUIRED`(见 §11)。客户端 UI 应在未绑 git 时**禁用 sub 入口**,引导先绑(§8)或改用本地模式。
- 只传 `resource_binding_id`(如 `rb_git_xxx`),**绝不内联明文凭据**;HM 从 Key Vault 解出短期 PAT 注入 AM。
- `model_selection.type`:`default`(全局 `default_model`)/ `per_role`(`roles` 映射)/ `primary`(Swarm 用 `primary_model`)。
- `conversation_id` 作为任务关联键(落到 `correlation_id`)。
- 模型必须用生产 NewAPI 已存在模型(当前推荐 `gpt-5.4`)。
- 兼容:也接受直接传 `{ "orchestration_plan": {...} }`(高级用法)。
**幂等**:创建请求带 `X-Idempotency-Key: <uuid>`(spec §9.3)。HM 对同 key 去重,网络重试不会建重复任务/重复 git 分支;重复命中返回首次的 `deployment_id`。
> 状态标注:`resource_bindings` / `git_options` / `SUB_GIT_BINDING_REQUIRED` / `X-Idempotency-Key` 均为 🔴 待建(随 git 化与客户端版资源接口一起上线,见 §8)。当前后端建任务尚未强制 git;客户端应**按本规范预留字段**,HM 侧补齐校验后即对齐。
`/messages` body:`{"message":"继续把测试补上","role":"user"}`。
`/execute` body 可空;用于"创建后再触发"或重试派发。
@@ -189,11 +209,16 @@ signature = base64( ed25519_sign(device_priv, sha256(canonical)) )
|---|---|
| `accepted` / `running` / `waiting_approval` | 进行中 |
| `completed` | 跑完且**有真实产物**(非空、非兜底)—— 注意:**不代表代码正确** |
| `completed_without_deliverable` | Runtime 完成但无产物 |
| `completed_without_deliverable` | Runtime 完成但无产物(无 git 提交) |
| `needs_codegen` | 只有方案/总结,需继续生成代码 |
| `budget_exceeded` 🔴 | 超 token/时长/成本上限,已暂停待用户决定续不续(spec §9.3) |
| `failed` / `stopped` | 终态 |
裁决规则:`completed` 且存在非兜底(`metadata.synthesized!=true` 且非纯总结)的真实 artifact → `completed`;否则降级为 `needs_codegen`(有总结)或 `completed_without_deliverable`(无产物)。
裁决规则(对齐 spec §6.4):AM 说 `completed` 后,HM 看**有没有真交付**——
- **目标态**:回调带 `git_ref` 且有**真实提交**(commit/改了 N 个文件)→ `completed` ✅。
- 只有方案/总结、没有提交 → `needs_codegen`;什么都没有 → `completed_without_deliverable`。
- 过渡期(AM 未 git 化):带"改了 N 个文件"信号的非兜底 artifact(`metadata.synthesized!=true` 且非纯总结)也按真交付算。
HM 判的是**"有没有真东西"这个事实**,**不读代码、不判对错**。
> ⚠️ **`display_status` 的边界(重要,见 spec §6.6)**:HM 只判**事实**——跑完没、有没有真产物、是不是空壳/兜底;**HM 不读代码、不编译、不测试,判不了"代码对不对"**。`completed` 只保证"有真东西",**不保证代码正确**。**对错由客户端自己拉产物跑/测 + 用户 review 决定**。若 agent_management 跑了测试,会作为 `test_report` 产物给客户端看,**不是给 HM 裁决**。
@@ -216,9 +241,16 @@ signature = base64( ed25519_sign(device_priv, sha256(canonical)) )
],
"agents":[
{"agent_id":"agi_x","name":"backend","role":"backend","status":"completed",
"current_action":"已合并到交付分支", // 🔴 一句话「现在在干啥」,给 work 视图滚动
"branch":"agent/backend", // 🔴 该 agent 的分支(看单角色 diff)
"tokens":0,"tools":0,"elapsed_seconds":0,"artifact_ids":["art_x"]}
],
"artifacts":[{"artifact_id":"art_x","title":"...","artifact_type":"code_patch"}],
"git_ref": { // 🔴 交付引用(见 §5.2);未就绪时为 null
"provider":"github","repo_url":"https://github.com/owner/repo",
"branch":"delivery/dep_xxx","commit_sha":"a1b2c3d","tag":"delivery-dep_xxx-rev1",
"pull_request_url":null
},
"artifacts":[{"artifact_id":"art_x","title":"测试报告","artifact_type":"test_report"}], // 仅非代码运行信息
"artifact_ids":["art_x"],
"metrics":{"tokens_used":618,"tools":0,"elapsed_seconds":33,"total_messages":2},
"tokens":618,"tools":0,"elapsed_seconds":33
@@ -227,9 +259,11 @@ signature = base64( ed25519_sign(device_priv, sha256(canonical)) )
字段说明(gap A/D/F):
- `mode`:顶层 `sub_agile|swarm`,与 `capabilities` 一致;列表 `GET .../tasks` 的每个 item 也带 `mode` 和 `display_status`,重启后从列表恢复任务不会把 swarm 当成 sub-agile。`sub_mode` 是 sub_agile 内部节奏。
- `git_ref`:**客户端据此 `git pull` 看产物**(§5.2)。`completed` + `git_ref` 有 `commit_sha` = 本轮真交付落到了用户仓库;续跑/重做后 `commit_sha` 会更新。🔴 待 AM 回调带 git_ref + HM 透出。
- `phases[]`:始终是数组。当前 sub-mode runtime 只上报单个 `phase`,故默认是「当前阶段」一条;待 runtime 上报阶段细分后会展开。
- `metrics` / 顶层 `tokens/tools/elapsed_seconds`:来自 runtime 状态指标(`tokens_used`/`elapsed_seconds`/`total_messages` 为真实值)。
- 每个 agent 的 `tokens`/`tools`/`elapsed_seconds`:**待 agent_management 上报 per-agent 指标**,未上报时为 `0`;`artifact_ids` 按产物的 `source_agent_role` 归到对应 agent(runtime 未标注来源角色时为空)。
- 每个 agent 的 `current_action`/`branch`/`tokens`/`tools`/`elapsed_seconds`:**待 agent_management 上报 per-agent 富字段**(🔴),未上报时为空/`0`;`artifact_ids` 按 `source_agent_role` 归到对应 agent。
- `artifacts[]`:只放**非代码运行信息**(test_report/summary);代码看 `git_ref`,不在此处。
`client_task_status`(本地交互态)由客户端自己维护,不由 Manager 返回。
@@ -240,38 +274,84 @@ signature = base64( ed25519_sign(device_priv, sha256(canonical)) )
`task_id` / `deployment_id` / `conversation_id` / `correlation_id` 在各接口的 `data` 内返回(无独立 `trace` 字段)。
**实时刷新**:当前**未提供 SSE**;客户端按统一方案 §17.3 兜底用**轮询**刷新:
**实时刷新(现状 🟢 轮询)**:当前**未提供 SSE**;客户端按统一方案 §17.3 兜底用**轮询**刷新:
- 运行中:每 3–5 秒 `GET .../workflow`(或 `.../timeline`);
- 终态:降到 15–30 秒或停止。
- 现在**不要**订阅 `.../events/stream`(未提供,会 404)。
不要订阅 `.../events/stream`(未提供,会 404)。
**实时流(目标 🔴,对齐 spec §5)**:上线后 HM 提供 `GET /api/heicode/sub-agile/tasks/{id}/events/stream`(SSE),把 AM 回调实时转发给客户端,**只推运行信息**(不推产物文件)。统一事件信封:
## 5. 产物(代码在 git;artifacts 仅非代码产物)
```json
{ "event_type":"agent.action", "deployment_id":"dep_xxx", "agent_id":"agi_x",
"phase":"development", "occurred_at":"2026-06-03T...", "payload":{ "...": "..." } }
```
> **前提:sub 模式强制绑定 git 才能用**(未绑 git 只能本地跑,进不了 sub)。所以**代码产物的唯一归宿就是用户自己绑定的 git 仓库**。
事件类型(最小集):`agent.action`(某 agent 现在在干啥)、`tool.call`(调了什么工具)、`phase.changed`(阶段切换)、`log.line`(日志行,分 user/debug)、`delivery.pushed`(**产物就绪通知,payload 带 `git_ref`,不是文件**)、`status.changed`(display_status 变化)、`approval.required`(出现待审批)。
**5.1 代码产物 = git 仓库(唯一路径)**
- **断线重连**:用 `Last-Event-ID`(基于 HM 事件游标)续传。
- **降级**:SSE 不可用自动回落轮询,UI 行为一致(只是延迟);未上线前 `/events/stream` 返回未实现,客户端按 `capabilities`/探测决定走哪条,**别硬连**。
## 5. 产物(代码只在 git;artifacts 仅非代码运行信息)
> **前提:sub 模式强制绑定 git 才能用**(未绑 git 只能本地跑,进不了 sub)。所以**代码产物的唯一归宿就是用户自己绑定的 git 仓库**。**没有"下载产物"这回事**(`project_folder`/manifest/files/archive zip 全部退役,见 5.4)。
### 5.1 代码产物 = git 仓库(唯一路径)🟡
`display_status=completed` 后,客户端直接对**用户自己绑定的仓库**(§8 资源绑定里的 `git` 类型)做 `git clone` / `git pull`,拿到完整工程 + 提交历史 + diff。本地改、重做、合并、部署都在工作副本上走标准 git,**不经 HM**——HM 不托管、不解析、不打包项目代码。
**5.2 非代码产物 = artifacts 接口**
> 状态:AM 真正"git 入库 + 多 agent 分支 + 合并"为 🔴 待建;当前过渡实现见 spec §3.5。
agent_management 跑出的**非代码**东西(如 `test_report` 测试报告、`summary` 摘要、说明),通过 artifacts 列表给客户端**查看**,不承载工程代码:
### 5.2 交付引用 `git_ref`:客户端怎么知道去哪 pull 🔴
客户端**不需要自己猜分支**——HM 在任务详情 / `workflow` 里回带一个 `git_ref`,指明交付落在哪个仓库/分支/commit;客户端据此 `pull`:
```json
"git_ref": {
"provider": "github",
"repo_url": "https://github.com/owner/repo",
"branch": "delivery/dep_b5fab27e9255", // 交付分支(或 PR 源分支)
"commit_sha": "a1b2c3d…", // 本轮交付的提交
"tag": "delivery-dep_b5fab27e9255-rev2", // 可选:版本标签,便于回溯/指定版本部署
"pull_request_url": "https://github.com/owner/repo/pull/12", // 若走 PR 模式
"agent_branches": [ // 可选:各 agent 的分支,便于看单角色 diff
{"role":"backend","branch":"agent/backend"},
{"role":"frontend","branch":"agent/frontend"}
]
}
```
- `git_ref` 出现 = 本轮已 push 到用户仓库(配合 `display_status=completed`)。客户端 `git fetch && git checkout <branch>`(或 pull)即可。
- **重做/续跑**会更新 `commit_sha`(及可能新的 `branch`/`tag`);客户端 pull 最新即可。
- HM 只**记录并回带这个引用**,自己不执行任何 git 操作(git 由 AM 用注入的短期 PAT 执行,见 spec §7)。
- 状态:🔴 待建——需 AM 回调带 `git_ref`、HM 在 `workflow`/详情透出。当前过渡期 AM 还回 blob artifact(见 5.4)。
### 5.3 非代码产物 = artifacts 接口(运行信息,非下载交付物)🟡
agent_management 跑出的**非代码**东西(如 `test_report` 测试报告、`summary` 摘要、说明),通过 artifacts 列表给客户端在 work 视图里**查看**,**不是可下载的工程产物**:
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `.../tasks/{task_id}/artifacts` | 非代码产物列表(test_report / summary 等) |
| GET | `.../artifacts/{artifact_id}/content` | 单个产物正文(如测试报告全文) |
| GET | `.../tasks/{task_id}/artifacts` | 非代码产物列表(test_report / summary 等运行信息记录) |
| GET | `.../artifacts/{artifact_id}/content` | 单条记录正文(如测试报告全文) |
`test_report` 只是给客户端/用户**看**,**不参与 HM 的 `display_status` 裁决**(HM 不读代码、不判对错,见 §4 / spec §6.6)。
## 6. 本地修改回传 → 改用 git(旧 revision 接口已退役)
### 5.4 遗留接口(已退役,新集成勿用)
旧流程靠 `POST .../artifacts/{id}/local-edits[/batch]` 把本地改动以「revision(accepted/applied/conflict)」回传给 HM,再在 `/messages`、`/execute` 时作为基线消费。**sub 强制 git 后这套退役**:本地改完直接 `git add/commit/push` 到用户自己的仓库,云端要继续改就 `git pull` 同步——冲突、历史、diff 全交给 git,HM 不再做基线托管。`local-edits`/`revisions` 后端仍在(向后兼容),但**新集成不要用**。
早期「无 git 流程」里 runtime 只吐一坨 markdown 文本,HM 现解析成项目文件树(`project_folder` / `manifest` / `files?path=` / `archive` / `revisions` / `local-edits`)供浏览、下载、回传。**sub 强制 git 后这套全部退役**:看代码=git 工作副本,回传改动=`git commit/push`。后端实现暂保留(向后兼容),但**新集成不要依赖**,也不要再用 `is_project` / `display_artifact_type:"project_folder"` 判交付——交付以 `display_status=completed`(§4)+ `git_ref` 有新 commit 为准。
**5.3 遗留接口(已不再作为代码交付路径)**
## 6. 修改 / 重做(基线 = git)🟡🔴
下列接口是早期「无 git 流程」的残留——当时 runtime 只吐一坨 markdown 文本,HM 现解析成项目文件树(`project_folder` / `manifest` / `files?path=` / `archive` / `revisions` / `local-edits`)供客户端浏览、下载、回传修改。**sub 强制 git 后这套不再使用**:浏览看 git 工作副本,回传改动用 `git commit/push`。后端实现仍保留(向后兼容 / 万一有无 git 的历史任务),但**新集成不要依赖**,也不要再靠 `is_project` / `display_artifact_type:"project_folder"` 判断交付——是否真实交付以 `display_status=completed`(§4)+ git 仓库有提交为准。
> **唯一基线 = 用户仓库的最新提交**。不存在 HM 侧 `local-edits`/revision 回传机制(已退役)。
| 动作 | 接口 | 谁干 | 状态 |
|---|---|---|---|
| **续跑/改**(路径 A,回云端) | `POST .../tasks/{id}/messages`(追加要求) | AM 先 `git pull` 取最新提交作基线 → 在其上改 → push;HM 转发 | 🟡 HM 转发已通;AM 中途续跑 🔴 |
| **重做** | `POST .../tasks/{id}/execute`(或将来 `/redo`) | AM 在**新分支**重新生成(旧交付分支保留可对比),回带新 `git_ref` | 🔴 待 AM 支持 redo 语义 |
| **本地改**(路径 B,回本地模式) | 客户端 `git pull` → 本地 agent 改 → `git push` | 客户端(模型仍走 HM `/v1/*`);想让云端接力就 push 后再 `/messages`,AM `pull` 自然拿到 | 🟡 |
| **停止** | `POST .../tasks/{id}/stop` / `DELETE .../tasks/{id}` | 已 commit 的代码**保留在分支不回滚**,任务标 `stopped`;可在该分支续跑或丢弃 | 🟢/🟡 |
`/messages` body:`{"message":"继续把测试补上","role":"user"}`。`/execute` body 可空。
## 7. 部署(✅ 客户端执行 + 用户必须确认;HM 只发凭据)
@@ -280,10 +360,32 @@ agent_management 跑出的**非代码**东西(如 `test_report` 测试报告
**新流程:**
1. 用户先在 HM **绑定** git/vm/db/blob 资源(§8),拿到 `resource_binding_id`(凭据已进 Key Vault,只留 `secret_ref`)。
2. 客户端发起部署 → **弹窗让用户确认**(目标 VM / 环境 / 用哪条 binding / 影响范围)。
3. 用户确认 → 客户端向 HM 的**凭据读取接口**(V2 加密)凭 `resource_binding_id` 换取该绑定的凭据(经加密通道返回,带审计)。
3. 用户确认 → 客户端向 HM 的**凭据租约接口**(V2 加密)凭 `resource_binding_id` 换取**短期凭据租约**(经加密通道返回,带 TTL + 审计)。
4. **客户端在用户机器上执行**:`git pull` 用户仓库 → ssh 到 VM → 跑部署命令 → 在 VM 上看日志。**全程 HM 不碰 VM。**
> 🔴 接口待定:凭据读取接口(`GET /api/heicode/resources/{binding_id}/credential` 之类,V2 加密 + 审计)正在 HM 侧建。旧的 `/deployment-targets`、`POST .../deployments`(executor:pending_worker)**不再作为部署主路径**。
**凭据租约接口(🔴 待建,对齐 spec §3.8/§4.5):**
`POST /api/heicode/resources/{binding_id}/lease`(V2 加密 + 设备签名)
请求:`{ "purpose":"deploy", "target":"vm:rb_vm_xxx", "task_id":"dep_xxx", "ttl_seconds":600 }`
响应:
```json
{ "success": true, "data": {
"lease_id": "lease_xxx",
"binding_id": "rb_vm_xxx",
"credential": { "type":"ssh_key", "username":"deploy", "private_key":"<经 V2 通道明文,仅本次>" },
"expires_at": "2026-06-03T...Z", // 短 TTL
"revocable": true
}}
```
- 凭据**只在本次响应内**经 V2 加密通道下发,客户端**用完即弃、不落盘、不进日志**(§10)。
- **高危目标**(如生产 VM):HM 先建一条审批(`display_status=waiting_approval` / §3.2),**审批通过后才发租约**。
- 吊销:`DELETE /api/heicode/resources/leases/{lease_id}`,或用户删 binding → 已发租约到期自然失效。
- HM 只**发租约 + 记审计**(谁/哪条 binding/用途/时间/TTL,明文不入审计),**不代执行部署**。
> 旧的 `/deployment-targets`、`POST .../deployments`(executor:pending_worker)**已废弃,不再作为部署路径**。
## 8. 资源绑定(git/vm/db/blob → Key Vault)
@@ -291,18 +393,46 @@ agent_management 跑出的**非代码**东西(如 `test_report` 测试报告
**5 种类型**:`git`(github/gitea,用 `provider` 区分,即用户**自己的仓库** URL + PAT)、`vm`(ssh)、`database`、`blob`。git 仓库是产物归宿(agent 在此 commit/合并,客户端 clone/pull)。
**后端接口**(当前 `UserAuth` 会话鉴权,Manager 网页控制台「资源绑定」页已用;🔴 **面向桌面客户端的 V2 设备签名版正在建**——见下):
**8.1 网页控制台接口(🟢 已用,`UserAuth` 会话鉴权)**
Manager 网页控制台「资源绑定」页已在用:
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/api/resources/?status=active` | 列出已绑资源(给客户端选 `resource_binding_id`) |
| GET | `/api/resources/?status=active` | 列出已绑资源 |
| POST | `/api/resources/` | 建绑定(非密字段进 metadata,不含凭据) |
| POST | `/api/resources/{id}/secret` | 写凭据 → Key Vault,返回 `secret_ref` |
| DELETE | `/api/resources/{id}` | 解绑(吊销) |
建绑定示例(github):`{"name":"my-repo","resource_type":"git","provider":"github","external_id":"https://github.com/owner/repo","metadata":{"repo_url":"...","default_branch":"main"}}` → 再 `POST /{id}/secret {"data":{"token":"ghp_..."}}`。
建绑定示例(github):
```json
{ "name":"my-repo", "resource_type":"git", "provider":"github",
"external_id":"https://github.com/owner/repo",
"metadata":{ "repo_url":"https://github.com/owner/repo", "default_branch":"main",
"api_base":null, // gitea 自建需填 API 地址
"mode":"existing", // greenfield | existing
"allowed_paths":["backend/","frontend/"],
"permission_scope":"repo:write" } }
```
→ 再 `POST /{id}/secret {"data":{"token":"ghp_..."}}`(PAT 进 KV,库里只留 `secret_ref`)。
> 🔴 **待建(HM 侧地基)**:① 客户端用 **V2 设备签名** 拉资源列表(`/api/heicode/resources` 之类) → 让用户/任务选 binding;② 客户端凭 `resource_binding_id` **取凭据**(部署用,§7)。这两条上线后,客户端即可"读绑定去用 + 取凭据部署"。当前 `/api/resources/*` 是会话鉴权(网页控制台用)。
- **provider 由 `repo_url` 推断**:`github.com` → github API;其他 → gitea(需 `api_base`)。
- **凭据有效性预检**:HM 在用前对 git binding 做一次 `GET repo` 预检;失效则标 `status=invalid`,建任务/部署时报 `RESOURCE_BINDING_INVALID`,提示用户**重新绑定**,不静默失败。
- vm:`{resource_type:"vm",metadata:{host,port,user,auth:"ssh_key|password"}}`;database:`{engine,host,port,db}`;blob:`{account,container}`。secret 一律走 `/{id}/secret` 进 KV。
**8.2 客户端接口(🔴 待建,V2 设备签名)**
桌面客户端不走会话 cookie,需要一套 **V2 设备签名**版资源接口(与任务接口同一套加密/签名,§1):
| 方法 | 路径 | 说明 | 状态 |
|---|---|---|---|
| GET | `/api/heicode/resources?type=git&status=active` | 列已绑资源 → 让用户/任务选 `resource_binding_id` | 🔴 |
| GET | `/api/heicode/resources/{id}` | 单条详情(非密元数据 + status) | 🔴 |
| POST | `/api/heicode/resources` + `/{id}/secret` | 客户端内直接绑定(经 V2 加密传凭据) | 🔴 |
| DELETE | `/api/heicode/resources/{id}` | 解绑 | 🔴 |
| POST | `/api/heicode/resources/{id}/lease` | 取**短期凭据租约**(部署用,见 §7) | 🔴 |
> 这两层(网页 8.1 / 客户端 8.2)操作的是**同一份绑定数据**,只是鉴权方式不同。8.2 上线后客户端即可"列绑定 → 建任务时引用 → 部署时取租约"全闭环。**当前客户端可先用 8.1 的会话路径联调**,待 8.2 就绪切换。
## 9. Runtime 回调(仅 Runtime 用,客户端无需关心)
@@ -322,12 +452,13 @@ Schema 查询:`GET /api/agent/callbacks/runtime-events/schema`。
| code | 场景 | retryable |
|---|---|---|
| ~~`ARTIFACT_REVISION_CONFLICT`~~ | **遗留**(local-edits revision,已退役,见 §6) | false |
| ~~`ARTIFACT_ARCHIVE_NOT_READY`~~ | **遗留**(archive zip 下载,已退役,见 §5.3) | true |
| `ARTIFACT_ARCHIVE_FAILED` | 打包失败 | false |
| `RESOURCE_BINDING_INVALID` | resource_binding_id 不存在或非本人 | false |
| `DEPLOY_TARGET_DISABLED` | 云目标未启用 | false |
| `FILE_NOT_FOUND` | 项目文件不存在 | false |
| `FILE_PATH_REQUIRED` | 取单文件未带 `?path=` | false |
| `SUB_GIT_BINDING_REQUIRED` 🔴 | sub 建任务未带(或失效的)`resource_bindings.git`——强制绑 git(§3.1) | false |
| `RESOURCE_BINDING_INVALID` | `resource_binding_id` 不存在/非本人/预检失效(提示重新绑定,§8.1) | false |
| `LEASE_EXPIRED` 🔴 | 凭据租约已过期(重新取,§7) | true |
| `LEASE_REVOKED` 🔴 | 租约已被吊销/binding 已删 | false |
| `APPROVAL_REQUIRED` 🔴 | 高危部署需先过审批(`waiting_approval`,§3.2/§7) | false |
| `BUDGET_EXCEEDED` 🔴 | 任务超 token/时长/成本上限,已暂停待用户决定(spec §9.3) | false |
| `DEPLOY_TARGET_DISABLED` | 目标未启用 | false |
| ~~`ARTIFACT_REVISION_CONFLICT`~~ / ~~`ARTIFACT_ARCHIVE_NOT_READY`~~ / ~~`ARTIFACT_ARCHIVE_FAILED`~~ / ~~`FILE_NOT_FOUND`~~ / ~~`FILE_PATH_REQUIRED`~~ | **遗留**(local-edits/archive/files 已退役,见 §5.4/§6) | — |
其余错误码沿用旧文档 §13。所有错误统一形如 `{"success":false,"error":{"code","message","retryable"}}`,可读 `error.retryable` 决定是否重试。