文档
检查并恢复项目变更
记录工作数据库、隔离提案分支、检查差异、运行提交的检查以及合并或恢复,并产生明显的副作用。
从英文自动翻译。 查看英文原文
将保存的资源与提交的历史记录分开
保存项目、图形或刀具路径会更新 SQLite 工作副本。创建项目提交会将工作资源快照到与 Git 兼容的对象中,并移动当前分支引用。这些是单独的操作:保存的图形可以不提交,提交的程序可以保留在本地,而无需任何远程镜像。
sidecar 的历史服务为每个资源构建一个规范的 JSON 文件。该树包括project.json、图纸、组件、刀具路径、参考材料/工具/机器配置和生产成本。工作流 YAML 和其他在存储库树中携带非实体文本,而不是普通的绘图记录。对一个实体的更改不需要将整个数据库视为不透明的二进制差异。
在遵循变异示例之前,选择一个一次性或预期的项目。放 PROJECT_ID 其真实标识符并使用当前连接 URL/应用程序承载。项目历史记录和工作流程端点记录在 历史 API 参考;项目元数据写入使用 项目API.
对象使用 git 的字节精确松散对象格式,因此提交和树 ID 匹配 git 计算相同的内容。分支引用位于 refs/heads/,默认分支是 main.
检查身份和工作状态
读取访问、提交、分支创建、打开拉取请求、审查和工作流调度需要访问项目。签出、恢复、远程配置、推/拉和合并需要维护者访问权限:项目所有者或组织管理员/经理。可选的帐户会话标头选择执行用户;如果没有有效的帐户会话,应用程序将使用其本地配置文件。
curl --fail-with-body \
-H "Authorization: Bearer $CADCAMFUN_TOKEN" \
"$CADCAMFUN_URL/api/projects/$PROJECT_ID/vcs/status" 响应命名分支、可为空的 HEAD 和脏路径。脏意味着当前资源文件与 HEAD 不同。非空列表是有用的评论信息,而不是存储故障。在移动工作副本之前,请确定这些更改是否属于提交或应故意恢复/丢弃。
如果 HEAD 为空,则在从 HEAD 创建分支之前记录第一个项目提交。分支 API 解析真实的源代码提交;没有占位符提交可以从新项目中分支。
创建基线或记录预期编辑
提交需要非空消息并捕获当前数据库支持的资源。首先检查脏路径:API 不会暂存单个实体文件。可选文件字段添加安全工作流程定义,稍后描述;它不会选择要包含哪些绘图编辑。
curl --fail-with-body \
-H "Authorization: Bearer $CADCAMFUN_TOKEN" \
-H "Content-Type: application/json" \
--data '{"message":"Record baseline blank"}' \
"$CADCAMFUN_URL/api/projects/$PROJECT_ID/vcs/commit" 响应是一个 201 提交记录,其中包含对象 ID、父 ID、消息和作者信息。记录其 OID 以供以后比较或恢复。未更改的树会被 NothingToCommit 拒绝,而不是根据相同内容制造另一个修订版。
authorKind 默认为用户。助理发起的提交使用 ai,不能在 main 上提交。参与者策略由项目服务强制执行;不同的显示消息不会绕过它。创建提交可以在记录提交后触发工作流。随后失败的工作流程不会撤消该提交。
创建并检查提案
创建分支写入ref;它不会切换工作数据库。结帐是明确的,拒绝未提交的工作更改。以下调用从当前 HEAD 创建一个分支并将其加载为签出分支。
curl --fail-with-body \
-H "Authorization: Bearer $CADCAMFUN_TOKEN" -H "Content-Type: application/json" \
--data '{"name":"proposal/blank-label"}' \
"$CADCAMFUN_URL/api/projects/$PROJECT_ID/vcs/branches"
curl --fail-with-body \
-H "Authorization: Bearer $CADCAMFUN_TOKEN" -H "Content-Type: application/json" \
--data '{"branch":"proposal/blank-label"}' \
"$CADCAMFUN_URL/api/projects/$PROJECT_ID/vcs/checkout" 要从其他地方开始,请将 from 修订添加到分支创建。名称必须是安全引用:遍历、重复斜杠、尾部斜杠/.lock 后缀和引用表达式片段都将被拒绝。重复使用现有名称也被拒绝。
签出后,通过其资源 API 保存预期的项目更改。对于小型演示,使用 PUT /api/projects/:id 和新名称更新项目元数据,然后提交。生成的 diff 更改了 project.json;它并不意味着几何形状的变化。在实际零件修订中,在提交之前保存绘图和任何重新生成的刀具路径。
阅读正在审核中的具体更改
GET /vcs/log 支持转速、限制和可选的跟踪路径。 GET /vcs/diff 需要 to 并接受可选的 from。使用显式 OID 进行可重现的比较。将分支名称和 JSON 路径编码为查询值,而不是将未转义的标点符号拼接到 URL 中。
curl --fail-with-body --get \
-H "Authorization: Bearer $CADCAMFUN_TOKEN" \
--data-urlencode "from=main" \
--data-urlencode "to=proposal/blank-label" \
"$CADCAMFUN_URL/api/projects/$PROJECT_ID/vcs/diff" 实体更改包括路径、资源种类、添加/修改/删除状态、可选 ID/名称以及 JSON 指针描述的属性更改。规范 JSON 以递归方式对对象键进行排序,同时保留数组顺序。非重叠的对象属性编辑可以在结构上合并;不兼容的并发数组编辑是原子冲突,而不是逐个元素的协调。
将制造证据作为刀具路径记录的一部分进行审查:匹配 G 代码和工件标识比熟悉的文件名更重要。项目提交捕获保存的数据;它不会仅仅因为树包含 NC 文本而运行缺失的本机制造请求。
对项目进行可执行检查
公共提交端点的额外文件仅限于 .cadcamfun/workflows/<safe-name>.yml 或 yaml。它接受文本值,而不是任意文件系统补丁。您不能使用此字段来替换绘图 JSON、编写 shell 脚本或通过发送 null 来删除文件。
定义作为 YAML 文本提交,然后运行程序从提交的引用加载它。下面的示例验证结构文件,要求对现有刀具路径进行 NC,检查操作顺序,并尝试重播携带存储的生成器请求的刀具路径。
name: Review checks
on:
manual: true
pullRequest:
branches: [main]
jobs:
inspect:
steps:
- id: structure
uses: project/validate
- uses: project/require-gcode
- uses: cam/sequence
- id: replay
uses: cam/verify 要从脚本提交它,请使用序列化器构建 JSON 主体,而不是手动转义 YAML:
const body = {
message: "Add committed review checks",
files: { ".cadcamfun/workflows/review.yml": await Bun.file("review.yml").text() }
}
const encoded = JSON.stringify(body) 提交仍会快照所有当前工作资源。在记录之前验证 YAML:历史提交可以成功,而生成的无效工作流程稍后会记录为失败的运行。生产引擎检查架构、依赖性、周期、重复步骤 ID 和注册使用标识符。
发送检查并解释其证据
POST /api/projects/:id/actions/dispatch 接受可选引用并运行与提交的引用匹配的每个手动工作流程。它不是单一工作流程名称选择器。阅读操作/工作流程中的定义以及操作/运行中的持久结果。默认情况下,该服务返回最新的 50 运行。
运行记录工作流、触发器、引用、SHA、时间戳、状态和事件。工作遵循依赖程度;独立的作业可以同时运行,而作业中的步骤是连续的。未知使用标识符在执行前失败。 if 值是成功、失败或始终,而不是任意表达式。失败的依赖项会完全跳过其下游作业。
仔细解释 CAM 输出。 cam/verify 和 cam/gcode 检查承诺 Toolpath.data.request。跳过的输出中可能会报告丢失的请求、不受支持的刀具路径或丢失的 NC。 verify=0 的成功运行并不是验证证据。 project/require-gcode 检查现有刀具路径;没有刀具路径的项目可以空洞地满足它。
自动 HTTP 推送触发集成当前不提供更改的路径,因此推送路径过滤器与这些事件不匹配。对此集成使用仅分支过滤器或显式手动调度。 HTTP 实时开始/完成事件在同步运行返回后发布;使用保存的运行以获得持久的结果。
写入工作流程步骤读取当前状态
当前的 vcs/commit 步骤委托项目提交,而无需清理工作副本防护。它在签出分支上提交当前工作数据库资源,这可能与运行的引用不同。它允许的额外文件并不能防止更广泛的快照副作用。仅当要提交当前分支和工作状态时才使用它。
打开、审查和合并提案
打开一个带有标题、头部、可选正文和基础(默认为主)的本地拉取请求。头部和底座必须不同,并且已经包含头部的底座没有任何可合并的内容。响应包括审阅和合并端点使用的项目本地编号。
curl --fail-with-body \
-H "Authorization: Bearer $CADCAMFUN_TOKEN" -H "Content-Type: application/json" \
--data '{"title":"Review blank label","head":"proposal/blank-label","base":"main","body":"Metadata change and committed checks."}' \
"$CADCAMFUN_URL/api/projects/$PROJECT_ID/pulls" GET /pulls/:number 返回请求、评论和更改。评论接受评论、批准或请求更改,并带有可选路径/指针。每个审阅者的最新非评论裁决控制请求更改块。后来的评论并没有澄清这一点;该审查者必须提交更新的批准裁决。当没有阻止判决时,实施没有积极的批准计数要求。
POST /pulls/:number/merge 需要维护者访问权限。 AI 演员无法合并到 main 中。如果签出基础,该服务需要一个干净的工作副本并将合并的快照应用到数据库。如果签出另一个分支,它会更新基本引用,而不替换当前工作数据库。合并可以快进或创建合并提交;检查血统而不是假设有两个父母。
合并的拉取请求差异使用记录的合并提交,在引用移动后保留审阅视图。本地拉取请求是应用程序审核记录,而不是自动的 GitHub 拉取请求。
恢复内容而不重写历史记录
POST /vcs/restore 采用 rev 和可选路径。它需要维护者访问权限和干净的工作副本,并创建包含早期内容的新提交。它不会向后重置分支并删除干预提交。然后该服务将新的 HEAD 应用到工作数据库。
{
"rev": "earlier-commit-oid",
"paths": ["drawings/actual-drawing-id.json"]
} 将路径替换为真实跟踪的资源并首先检查其差异。省略路径会恢复完整的早期树;可以替换或删除多个相关资源。整个项目快照还包含引用的库项目和成本,因此恢复的刀具路径可以保留依赖性。资源版本恢复和项目历史记录恢复具有不同的范围。
恢复制造源后,检查保存的本机输出是否仍与预期的图纸修订相匹配。历史操作保留了早期的工件;它不会将该工件转化为新修改的绘图的证据。
镜像提交的历史并恢复冲突
GitHub 镜像是可选的。使用 PUT /vcs/remote 将远程设置为所有者/存储库字符串,或将 null 设置为分离。推送和拉取使用 x-github-token 中用户的 GitHub 令牌进行该调用;它不保存在项目 JSON 中。这些 HTTP 操作需要维护者访问权限。
推送上传可到达的对象并拒绝覆盖未拉取的远程提交。拉取需要一个干净的工作副本,获取远程分支并合并。结构性 JSON 冲突可识别路径和指针,而无需编写部分存储库合并提交。检查双方,制定深思熟虑的解决方案,然后重试,而不是强迫一侧凌驾于另一侧。
HTTP 409 通常表示脏状态或合并冲突; 403 可以表示维护者或 AI 主限制; 404 可能意味着无法访问的项目或未知的修订版本; 400 涵盖无效名称、空消息或无更改。数据库故障映射到服务器存储错误。诊断不匹配时保留 OID 和失败消息。
来源出处: packages/vcs/src/repository.ts, json.ts, snapshot.ts, github.ts; apps/sidecar/src/services/project-vcs.ts, repos/project-vcs-repo.ts, 和 http/vcs.ts;工作流程架构/运行程序 packages/workflow/src 和边车 workflow-runner.ts/workflow-steps.ts.
评论如何决定合并
- 从功能分支打开拉取请求(AI 参与者必须使用
ai/*分支)进入main. - 审稿人提交
comment,approve, 或者request-changes,可选择锚定到文件path和一个 JSONpointer. - 在合并时,该服务会采用每个审阅者的最新非评论判决。如果其中任何一个是
request-changes,合并失败blocked:“审稿人要求更改”。 - 如果签出基础分支,则其工作副本必须是干净的。
- 每个 JSON 属性的合并是三向的。返回冲突及其路径和指针,并且不提交任何内容。
- 工作流程与
on.pullRequest与拉取请求打开时运行的基础分支相匹配,因此它们的结果在审核期间可用。
| 情况 | 该怎么办 |
|---|---|
blocked 对于人工智能演员来说 main | 承诺 ai/* 分支并打开拉取请求;一个人将其合并。 |
conflict 上 /width 的 drawings/d1.json | 确定一个分支上的值,提交并再次合并。 |
dirty | 首先提交或放弃本地编辑;自动化步骤永远不会吸收未完成的工作。 |
nothing-to-commit | 保存的资源已经与上次提交匹配。 |
恢复食谱
// Bring back one drawing as it was three commits ago
const old = repo.log()[3].oid
repo.restorePaths(old, ["drawings/d1.json"], me, "Restore plate outline")
// Make the whole tree equal to an older commit (a new commit on top)
repo.revertTo(old, me) // message defaults to "Restore <rev>"
// Who changed a toolpath, most recent first
repo.fileLog("toolpaths/op1.json").map((e) => e.message) 两个恢复操作都添加了新的提交,因此旧状态本身可以被撤消,并且历史记录保持可共享。在没有参考材料或工具的情况下恢复刀具路径文件可能会留下悬空参考;在同一次调用中恢复这些库文件。
完整的审查会议,捕获
下面的每个响应都是从一个新项目中正在运行的 sidecar 捕获的(id 已缩短)。路径是相对于 /api/projects/{id}/.
# fresh project with one drawing
GET vcs/status
{"branch":"main","head":null,"dirty":["drawings/2f315a02-….json","project.json"]}
POST vcs/commit {"message":"Baseline"}
{"oid":"8bdded63…","parents":[],"message":"Baseline\n","author":{"name":"Local user",…}}
POST vcs/branches {"name":"thicker"}
{"current":"main","branches":{"main":"8bdded63…","thicker":"8bdded63…"}}
POST vcs/checkout {"branch":"thicker"}
{"branch":"thicker","head":"8bdded63…","dirty":[]}
PUT /api/drawings/{id} {"name":"Plate 12mm"}
GET vcs/status
{"branch":"thicker","head":"8bdded63…","dirty":["drawings/2f315a02-….json"]}
POST vcs/commit {"message":"Rename plate"}
{"oid":"a6869011…","parents":["8bdded63…"],…}
GET vcs/diff?from=main&to=thicker
[{"path":"drawings/2f315a02-….json","kind":"drawings","id":"2f315a02-…","name":"Plate 12mm",
"status":"modified","changes":[
{"pointer":"/name","before":"Plate","after":"Plate 12mm"},
{"pointer":"/updatedAt","before":"…09:20.069Z","after":"…09:20.168Z"}]}]
POST pulls {"title":"Thicker plate","head":"thicker","base":"main"}
{"number":1,"state":"open","authorKind":"user","mergeCommit":null,…}
POST pulls/1/reviews {"verdict":"approve","body":"Looks good"}
{"verdict":"approve","body":"Looks good","path":null,"pointer":null,…}
POST pulls/1/merge
{"number":1,"state":"merged","mergeCommit":"a6869011…",…}
POST pulls/1/merge (again)
{"_tag":"BadRequest","message":"Pull request is not open"} | 你所看到的 | 这意味着什么 |
|---|---|
head: null 在第一次提交之前 | 存储库存在但尚未提交;每个资源都是脏的 |
肮脏的路径,例如 drawings/<id>.json, project.json | 工作副本被序列化为每个资源一个 JSON 文件 |
| 一个新的分支指向当前的头部 | 没有 from,分支从当前头开始 |
| Diff 更改是 JSON 指针 | 按属性报告更改(/name),不是每行 |
updatedAt 也改变了 | 时间戳随资源进行版本控制,因此每次保存都会显示 |
mergeCommit 等于分行头 | main 没有移动,所以合并快进 |
| 合并两次 → 400 | 仅开放的拉取请求可以合并或关闭 |
查看和查询字段
| 要求 | 领域 |
|---|---|
POST pulls/{number}/reviews | verdict (必需的): comment, approve 或 request-changes;选修的 body, 和 path + pointer 将评论锚定在一项财产上。任何其他判决都会被 400 拒绝。 |
GET pulls/{number} | 退货 pullRequest, 它是 reviews 和结构 changes 底座和头部之间 |
GET pulls?state=… | 按州过滤列表 |
GET vcs/log?rev=&limit=&path= | 修订历史记录,有限,可选择用于一个资源路径 |
GET vcs/diff?from=&to= | 两个修订或分支之间的结构差异 |
POST vcs/restore | rev (必填),可选 paths。未知修订版回答 404 Unknown revision <rev>. |
GET/PUT vcs/remote, POST vcs/push, POST vcs/pull | 配置镜像远程并推送或拉取分支 |