# REST API + 代码骨架 > 新增 5 个 REST 端点,全部挂在 `/api/devloop/*`。骨架代码合计约 700 行,分布在 3 个新文件 + 1 处修改。 ## 端点清单 | 方法 | 路径 | 说明 | 触发的 Loop | | --- | --- | --- | --- | | `POST` | `/api/devloop/projects` | 创建项目,传入初始需求 | L3 启动 | | `POST` | `/api/devloop/projects/:pid/requirement` | 追加人类反馈,触发 PRD 增量更新 | L3 | | `POST` | `/api/devloop/projects/:pid/run` | 启动 / 续跑 LoopController | L1 + L2 | | `GET` | `/api/devloop/projects/:pid/state` | 返回 units.json + 当前 loop 阶段 | UI 轮询 | | `GET` | `/api/devloop/projects/:pid/stream` | SSE — 实时推送 Agent 输出 / 状态变更 | UI 流式 | | `POST` | `/api/devloop/projects/:pid/sign-off` | 人类显式 sign-off,状态置 `shipped-final` | L3 终止 | | `POST` | `/api/devloop/projects/:pid/abandon` | 放弃项目(保留数据,从列表移除) | L3 终止 | ## Request / Response 示例 ```js # 创建项目 # 严格按 CLAUDE.md 警告:先写 /tmp/req.json,再 -d @file curl -X POST http://localhost:3010/api/devloop/projects \ -H "Content-Type: application/json" \ -d @/tmp/create.json # /tmp/create.json — 单行紧凑 {"company_id":"c-lemonv-001","name":"Auto Auth System","requirement":"需要一个邮箱密码登录的系统"} # 响应 { "project_id": "p-731042", "status": "prd_drafting", "stream_url": "/api/devloop/projects/p-731042/stream" } ``` ```js # 追加新需求 curl -X POST http://localhost:3010/api/devloop/projects/p-731042/requirement \ -H "Content-Type: application/json" \ -d @/tmp/feedback.json # /tmp/feedback.json {"text":"再加 GitHub OAuth","origin":"yibo"} # 响应(即刻返回,PM 是异步跑) { "accepted": true, "queued_rev": 3, "eta_seconds": 30 } ``` ```js # 查询状态 curl http://localhost:3010/api/devloop/projects/p-731042/state { "project_id": "p-731042", "prd_rev": "3", "sdd_rev": "5", "current_loop": "L1", "units": [ { "id": "auth-be", "status": "passed" }, { "id": "auth-fe", "status": "testing" }, { "id": "oauth-be", "status": "pending" } ], "l2_rounds": 0, "l2_budget": 3, "agents_active": ["tester:auth-fe"] } ``` ```js # SSE 流(浏览器或 curl --no-buffer) curl -N http://localhost:3010/api/devloop/projects/p-731042/stream event: unit.state data: {"unit_id":"auth-fe","from":"coding","to":"testing"} event: agent.stream data: {"agent":"tester","unit_id":"auth-fe","chunk":"PASS tests/login.spec.ts > valid creds\n"} event: agent.stream data: {"agent":"tester","unit_id":"auth-fe","chunk":"FAIL tests/login.spec.ts > invalid pwd\n"} event: unit.state data: {"unit_id":"auth-fe","from":"testing","to":"failed","retries":1} event: prd.updated data: {"rev":4,"changeSummary":"+ NFR: session timeout 1h"} ``` ## routes/devloop.js 骨架 ```js // server/routes/devloop.js · NEW · ~200 行 const express = require('express'); const { loopController } = require('../src/core/loop-controller'); const { docStore } = require('../services/doc-store'); const { unitRegistry } = require('../services/unit-registry'); const { sseBroker } = require('../services/sse-broker'); const router = express.Router(); router.post('/projects', async (req, res) => { const { company_id, name, requirement } = req.body; const pid = await loopController.createProject({ company_id, name }); loopController.onHumanFeedback(pid, { text: requirement, origin: req.user?.id }); res.json({ project_id: pid, status: 'prd_drafting', stream_url: `/api/devloop/projects/${pid}/stream` }); }); router.post('/projects/:pid/requirement', async (req, res) => { loopController.onHumanFeedback(req.params.pid, req.body); res.json({ accepted: true }); }); router.get('/projects/:pid/state', (req, res) => { res.json(loopController.getState(req.params.pid)); }); router.get('/projects/:pid/stream', (req, res) => { sseBroker.subscribe(req.params.pid, req, res); }); module.exports = router; ``` ## 挂载到 server/index.js 仅 1 行改动: ```js // server/index.js 第 90 行后追加 app.use('/api/devloop', require('./routes/devloop')); ``` ## 认证 沿用 `routes/auth.js` 的 JWT 中间件: - POST 类端点:必须带 `Authorization: Bearer `,校验 `req.user.company_id` 与 path 中的 cid 匹配。 - GET state / stream:同上。 - Open API 调用(无人类 cookie,纯 server-to-server):使用现有的 `OPEN_API_KEYS` 环境变量 + `X-API-Key` header。 初版不做 RBAC(任何登录用户都能创建项目)。多用户隔离靠 cid。这是已知简化,P5 再加。