PR: fix(ui,deploy): Factory v2 apiFetch 修复 + blade deploy 支持任意目录发布
审核时间: 2026-06-26 | 共 7 轮独立审核(2 Claude Opus + 2 Codex + 3 Agent)
public-factory-v3 分支有 ~30 个 commit 未合入 main(含 UI 修复、沙盒端口、agent-board 等功能)public-factory-v3 中不存在(是 #1005 搬运到 main 的新文件),不构成回退综合结论:PR #1007 相比 public-factory-v3 无实质性功能回退
发现的 2 个低风险问题:
findManifestRoot() 返回 /root 但后端要求 /root/ 前缀,会被拒绝。但这个场景极其罕见(没人会把应用直接放在 home 目录根下),风险极低合并警告(审核 6):public-factory-v3 如果直接合入 main,会覆盖掉 PR #1007 的 app_dir deploy 功能,需要在合并时保留该功能
审核范围:4 个 factory-v2 前端文件的 fetch → apiFetch 迁移
结论:功能完全等价。apiFetch 在非 2xx 时抛出 BladeApiError,与原始 if (!resp.ok) throw 行为一致。readJsonResponse() 正确处理 204/空响应体(DELETE 请求)。
| 文件 | 行为对比 |
|---|---|
| AgentConfigModal.tsx | CRUD 操作均等价,DELETE 结果正确丢弃 |
| FactoryV2BoardPanel.tsx | 3 个列表查询的 [] fallback 用 try/catch 正确保留 |
| FactoryV2Page.tsx | listDir、validateRepo 的 fallback 均正确 |
| FactoryV2ProjectPage.tsx | 所有 API 调用行为一致 |
审核范围:deploy.go 的 working_dir → app_dir 重构、app_publish.py 的 _resolve_app_dir 新增
结论:后端完全向后兼容。PublishRequest 同时包含 app_dir 和 working_dir,逻辑优先使用 app_dir,无 app_dir 时 fallback 到旧的 working_dir 路径。
findManifestRoot() 直接定位 manifest,比旧的间接路径更可靠_resolve_app_dir() 安全性完备:验证 /root/ 前缀、拒绝 ..、检查 is_relative_to、验证目录和 manifest 存在审核范围:apiFetch 迁移对 UI 交互模式和错误处理语义的影响
发现 1 — removeSession 行为变更:
旧代码:fetch(DELETE) 不检查 resp.ok,无论成功失败都清理 UI 状态
新代码:apiFetch(DELETE) 在非 2xx 时抛异常,被 catch 捕获,清理逻辑不执行
评估:新行为实际更正确 — 服务端删除失败时不应清理前端状态。属于 bug 修复而非回退。
发现 2 — catch { return [] } 范围扩大:
旧代码:只在 !resp.ok 时返回 [],JSON 解析错误会向上抛出
新代码:try { apiFetch } catch { return [] } 吞掉所有错误(含 JSON 解析错误)
评估:实际影响极小 — 后端返回非法 JSON 的概率接近 0,且列表为空比崩溃对用户更友好。
审核范围:deploy API 变更的向后兼容性和边缘场景
确认向后兼容:后端同时接受 working_dir(旧 CLI)和 app_dir(新 CLI)
发现 — /root 根目录边缘 case:
如果 blade-app.yaml 直接放在 /root/ 下(无子目录),findManifestRoot() 返回 /root,但 _resolve_app_dir() 要求 app_dir.startswith("/root/"),会被拒绝
评估:极其罕见的场景 — 正常使用中应用总是在 /root 的子目录下开发。旧代码在此场景下也会走 workspace 全局扫描的 fallback 路径,属于非标准用法。风险评级:极低。
审核范围:逐函数比对 apiFetch 与原始 fetch 的错误处理语义
结论:所有 API 调用的错误处理行为均保持一致。
apiFetch 在非 2xx 时抛出 BladeApiError,与原始 throw new Error 等价readJsonResponse() 对 204/空 body 返回 undefined,DELETE 请求无问题Error 变为 BladeApiError,但 TanStack Query 的 mutation 和 query 都能正确捕获审核范围:PR #1007 涉及的 12 个文件是否与 public-factory-v3 的改动冲突
结论:PR #1007 本身不构成回退,但未来合并 public-factory-v3 时需注意:
合并冲突预警:
建议:合并 public-factory-v3 时,需要在 deploy 相关文件上 resolve conflict,保留 PR #1007 的 app_dir 功能
审核范围:TypeScript 类型定义、SDK 导出、编译检查
结论:无回退。
PublishRequest 新增的 app_dir?: string | null 字段是可选的,不破坏现有类型apiFetch 正确从 @blade-hq/agent-kit/react 导出| 审核 | 审核者 | 角度 | 结论 |
|---|---|---|---|
| #1 | Claude Opus | 前端 API 迁移 | 无回退 |
| #2 | Claude Opus | CLI + 后端 deploy | 无回退 |
| #3 | Codex | 前端功能完整性 | 低风险问题 |
| #4 | Codex | 后端 API 兼容性 | 边缘 case |
| #5 | Agent | 错误处理行为 | 无回退 |
| #6 | Agent | v3 功能丢失 | 合并警告 |
| #7 | Agent | TS/SDK 兼容 | 无回退 |