diff --git a/docs/integration/heicode-desktop-unified-api.md b/docs/integration/heicode-desktop-unified-api.md index 401b056..40ab853 100644 --- a/docs/integration/heicode-desktop-unified-api.md +++ b/docs/integration/heicode-desktop-unified-api.md @@ -14,7 +14,9 @@ Manager 生产地址:`https://code.xinghanlab.com` > - **部署 = 客户端执行 + 用户必须确认**;HM 只把凭据从 Key Vault 安全发给客户端,**HM 不部署、不连 VM**(见 §7,已改)。 > - **产物归宿是用户自己的 git 仓库**(github/gitea);先绑仓库(及 vm/db/blob)→ §8 资源绑定。 > -> **2026-06-02 更新(gap-analysis P0/P1 闭环并实跑验证)**:GET 无 body 设备签名(§1)、审批列表(§3.2)、`project_folder` 归一化(§5)、`/archive` 契约(§5)、`?path=`(§5)、revision accepted/applied(§6)、Swarm 同形(§3)。 +> **2026-06-02 更新(gap-analysis P0/P1 闭环并实跑验证)**:GET 无 body 设备签名(§1)、审批列表(§3.2)、Swarm 同形(§3)。 +> +> **2026-06-03 更新(产物模型对齐)**:sub 模式**强制绑定 git** 才能用,否则只能本地跑——因此**代码产物的唯一归宿是用户自己的 git 仓库**(clone/pull)。早期"HM 把文本产物解析成项目文件夹(`project_folder`/manifest/files/archive)"是无 git 流程的残留,**不再作为代码交付路径**;artifacts 接口仅保留给**非代码产物**(test_report/摘要)。详见 §5。 ## 0. 命名与总原则 @@ -32,9 +34,8 @@ Manager 生产地址:`https://code.xinghanlab.com` 3. (可选) POST .../tasks/{id}/execute 若需"先建后跑"或重试派发 4. 轮询 GET .../tasks/{id}/workflow (运行中 3-5s) 读 display_status + 三层状态 + agents (work 视图) 5. 出现 approval 时: GET .../approvals?status=pending -> approve/reject -6. display_status=completed 后看产物 (两条路): - (a) 直接 git clone/pull 用户自己的仓库 (完整工程+历史+diff) - (b) 经 HM: GET .../artifacts -> /manifest -> /files?path= -> /archive +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 不代执行) ``` @@ -106,12 +107,10 @@ signature = base64( ed25519_sign(device_priv, sha256(canonical)) ) | GET | `/api/heicode/sub-agile/tasks/{task_id}/metrics` | 指标 | | GET | `/api/heicode/sub-agile/tasks/{task_id}/diagnostics` | 诊断(直查 Runtime 状态 + warnings) | | GET | `/api/heicode/sub-agile/tasks/{task_id}/sk-snapshots` | SK 快照 | -| GET | `/api/heicode/sub-agile/tasks/{task_id}/artifacts` | 产物列表(主产物归一化为 `project_folder`,见 §5) | -| GET | `/api/heicode/sub-agile/tasks/{task_id}/artifacts/{artifact_id}/manifest` | 项目文件树 | -| GET | `/api/heicode/sub-agile/tasks/{task_id}/artifacts/{artifact_id}/files?path=` | 单文件正文 | -| GET | `/api/heicode/sub-agile/tasks/{task_id}/artifacts/{artifact_id}/archive` | 整包 zip | -| GET | `/api/heicode/sub-agile/tasks/{task_id}/artifacts/{artifact_id}/revisions` | revision 列表 | -| POST | `/api/heicode/sub-agile/tasks/{task_id}/artifacts/{artifact_id}/local-edits` | 本地修改回传(批量 `.../local-edits/batch`) | +| GET | `/api/heicode/sub-agile/tasks/{task_id}/artifacts` | 产物列表(**仅非代码产物**:test_report/摘要等;代码看 git,见 §5) | +| GET | `/api/heicode/sub-agile/tasks/{task_id}/artifacts/{artifact_id}/content` | 单产物正文(如测试报告) | +| ~~GET~~ | ~~`.../artifacts/{artifact_id}/{manifest,files,archive,revisions}`~~ | **遗留**:无 git 流程的"HM 解析项目文件夹"接口,sub 强制 git 后不再作为代码交付路径(后端仍在,见 §5) | +| ~~POST~~ | ~~`.../artifacts/{artifact_id}/local-edits[/batch]`~~ | **遗留**:本地改产物改用 git 提交 | | GET | `/api/heicode/sub-agile/tasks/{task_id}/approvals?status=pending` | 审批列表(gap P0-2) | | POST | `/api/heicode/sub-agile/tasks/{task_id}/approvals/{approval_id}/approve` | 同意审批 | | POST | `/api/heicode/sub-agile/tasks/{task_id}/approvals/{approval_id}/reject` | 拒绝审批 | @@ -247,98 +246,32 @@ signature = base64( ed25519_sign(device_priv, sha256(canonical)) ) 不要订阅 `.../events/stream`(未提供,会 404)。 -## 5. 产物:项目文件夹 +## 5. 产物(代码在 git;artifacts 仅非代码产物) -Runtime 返回单个文本/markdown 多文件 artifact;Manager 解析成项目文件树后提供。 +> **前提:sub 模式强制绑定 git 才能用**(未绑 git 只能本地跑,进不了 sub)。所以**代码产物的唯一归宿就是用户自己绑定的 git 仓库**。 -**产物列表** `GET .../tasks/{task_id}/artifacts`: +**5.1 代码产物 = git 仓库(唯一路径)** -```json -{ "success": true, "data": { - "deployment_id":"dep_xxx","task_id":"dep_xxx","total":1, - "items":[ - {"artifact_id":"art_swm_xxx_backend_1","artifact_type":"code_patch","title":"backend task delivery", - "summary":"...","uri":"azblob://...", - "is_project":true,"display_artifact_type":"project_folder", - "manifest_path":".../artifacts/art_swm_xxx_backend_1/manifest", - "files_path":".../artifacts/art_swm_xxx_backend_1/files", - "archive_path":".../artifacts/art_swm_xxx_backend_1/archive", - "revisions_path":".../artifacts/art_swm_xxx_backend_1/revisions"} - ] -}} -``` +`display_status=completed` 后,客户端直接对**用户自己绑定的仓库**(§8 资源绑定里的 `git` 类型)做 `git clone` / `git pull`,拿到完整工程 + 提交历史 + diff。本地改、重做、合并、部署都在工作副本上走标准 git,**不经 HM**——HM 不托管、不解析、不打包项目代码。 -**主产物类型以 `display_artifact_type` 为准**(gap P0-3):结构性代码任务的主交付物 Manager 一律归一化为 `display_artifact_type:"project_folder"` 且 `is_project:true`,并直接给出 manifest/files/archive/revisions 子路径——客户端不用再靠原始 `artifact_type`(可能是 `code_patch`)猜它是不是完整项目。非交付物(纯摘要)`is_project:false`,`display_artifact_type` 回落为原始类型。 -判断是否真实交付:`display_status=completed` 且 `is_project:true`(Manager 已裁决,客户端无需自判)。 +**5.2 非代码产物 = artifacts 接口** + +agent_management 跑出的**非代码**东西(如 `test_report` 测试报告、`summary` 摘要、说明),通过 artifacts 列表给客户端**查看**,不承载工程代码: | 方法 | 路径 | 说明 | |---|---|---| -| GET | `.../artifacts/{artifact_id}/content` | 原始正文(兼容) | -| GET | `.../artifacts/{artifact_id}/manifest` | 项目文件树(entries[path,type,mime,size,hash,content_path] + archive) | -| GET | `.../artifacts/{artifact_id}/files?path=<相对路径>` | 单个文件正文(见下「路径编码」) | -| GET | `.../artifacts/{artifact_id}/archive` | 整个项目 zip 下载(见下「下载契约」) | +| GET | `.../tasks/{task_id}/artifacts` | 非代码产物列表(test_report / summary 等) | +| GET | `.../artifacts/{artifact_id}/content` | 单个产物正文(如测试报告全文) | -manifest 示例: +`test_report` 只是给客户端/用户**看**,**不参与 HM 的 `display_status` 裁决**(HM 不读代码、不判对错,见 §4 / spec §6.6)。 -```json -{ "success": true, "data": { - "artifact_id":"art_xxx","artifact_type":"project_folder","root_dir":"project","revision":1, - "content_hash":"sha256:...","file_count":2, - "entries":[ - {"path":"app.py","type":"file","mime_type":"text/x-python","size_bytes":420,"content_hash":"sha256:...","content_path":".../files?path=app.py"}, - {"path":"frontend/src/App.tsx","type":"file","mime_type":"text/typescript","size_bytes":300,"content_hash":"sha256:...","content_path":".../files?path=frontend%2Fsrc%2FApp.tsx"} - ], - "archive":{"format":"zip","download_path":".../archive"} -}} -``` +## 6. 本地修改回传 → 改用 git(旧 revision 接口已退役) -**单文件「路径编码」(gap P1-1)**:用 `?path=` query 传相对路径,整体做一次 URL encode(含 `/`、空格、中文、`#`、`?` 等保留字符)。例:`GET .../files?path=frontend%2Fsrc%2FApp.tsx`。直接用 manifest 里给的 `content_path` 即可,无需自己拼。找不到文件返回 `{"error":{"code":"FILE_NOT_FOUND"}}`;缺 path 返回 `FILE_PATH_REQUIRED`。 +旧流程靠 `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` 后端仍在(向后兼容),但**新集成不要用**。 -**整包「下载契约」(gap P0-4)**:`GET .../archive` 成功时返回真实 zip 二进制,响应头: -```text -HTTP 200 -Content-Type: application/zip -Content-Disposition: attachment; filename="art_xxx.zip" -Content-Length: <字节数> -``` -客户端必须读 `Content-Disposition` 文件名、按 `Content-Length` 校验落盘,**保存成功后才提示「已下载」**。产物尚未就绪(无文件)时返回 `HTTP 200` body 为: -```json -{ "success": false, "error": { "code": "ARTIFACT_ARCHIVE_NOT_READY", "message": "项目压缩包尚未生成(产物还未就绪),请稍后重试。", "retryable": true } } -``` -此时客户端**不要**提示已下载,按 `retryable` 稍后重试。 +**5.3 遗留接口(已不再作为代码交付路径)** -## 6. 本地修改回传(revision) - -用户在本地改了产物后,必须显式上传;Manager 存为新 revision。 - -| 方法 | 路径 | 说明 | -|---|---|---| -| GET | `.../artifacts/{artifact_id}/revisions` | revision 列表 + `current_project_revision` | -| POST | `.../artifacts/{artifact_id}/local-edits` | 上传单文件修改 | -| POST | `.../artifacts/{artifact_id}/local-edits/batch` | 上传多文件修改(`changes[]`,op=update/create/delete) | - -单文件上传请求: - -```json -{ "base_artifact_id":"art_xxx","base_revision":1,"path":"frontend/src/App.tsx", - "content":"...","content_hash":"sha256:new","change_summary":"改了首页标题" } -``` - -批量请求:`{ "base_revision":1, "changes":[ {"op":"update","path":"a.py","content":"..."},{"op":"create","path":"b.py","content":"..."},{"op":"delete","path":"c.py"} ], "change_summary":"..." }` - -成功响应:`{ "success":true,"data":{ "artifact_id":"art_xxx","revision":2,"project_revision":2,"source":"client_local_edit","status":"accepted","content_hash":"sha256:new" } }` - -**revision 状态语义(gap P1-2)**: - -| status | 含义 | -|---|---| -| `accepted` | 已被接受为**当前可用基线**(无冲突即直接接受,Manager 持有基线) | -| `applied` | 已被 Runtime 在后续 `/messages` 或 `/execute` 消费 | -| `conflict` | 与最新 revision 冲突(见下,不落库) | - -**后续执行使用最新 accepted revision(gap P1-3)**:调用 `POST .../tasks/{id}/messages` 或 `.../execute` 时,Manager 自动取该任务最新 `accepted` revision 作为基线并置为 `applied`,响应回带 `active_project_revision`(无本地修改时不返回该字段)。客户端无需手动指定 revision。 - -冲突:若 `base_revision` 不等于当前最新,返回 `{ "success":false,"error":{"code":"ARTIFACT_REVISION_CONFLICT","retryable":false},"data":{"current_project_revision":3} }`,客户端提示用户先同步最新再提交。 +下列接口是早期「无 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 仓库有提交为准。 ## 7. 部署(✅ 客户端执行 + 用户必须确认;HM 只发凭据)