diff --git a/docs/integration/heicode-desktop-unified-api.md b/docs/integration/heicode-desktop-unified-api.md index 4f47e630..e8f68c10 100644 --- a/docs/integration/heicode-desktop-unified-api.md +++ b/docs/integration/heicode-desktop-unified-api.md @@ -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: `(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 `(或 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` 决定是否重试。