Origin 迁移 API
Origin 处于早期 beta 版阶段,可能会发生变化。更新集成时请查阅 OpenAPI specification。
Origin 迁移 API 面向需要在 GitHub 与 Origin 镜像之间迁移代码仓库的 operator。请在以 Cherri Code 用户身份运行的迁移脚本中调用这些端点,而不要从 Origin App 调用。
使用 origin auth login 登录,或通过 origin api 传入 Cherri Code API 密钥。参见用户认证的 CLI 请求。
这些端点使用 Origin API 的基础 URL https://api.cursor.com/v1/origin,以及相同的错误模型。
作用域
repository:mirror:write、repository:mirror:delete 和 repository:metadata:read 是用户 policy 作用域。转换代码仓库镜像 和 强制代码仓库镜像切换 需要 Write (repository:mirror:write) 权限。分离代码仓库镜像 需要 Admin (repository:mirror:mirror:delete) 权限。获取镜像转换作业 和 获取活跃的镜像转换作业 需要 repository:metadata:read 权限。此外,你还必须在其上游 GitHub 源上具备该代码仓库的管理权限。
这些作用域无法在 源站 App 安装过程中申请,源站 App 也无法调用这些端点。
端点参考
转换代码仓库镜像
/v1/origin/repos/{ownerSlug}/{repoName}/mirror:transition对镜像仓库启动一次镜像状态转换,并返回跟踪该转换的作业。作业运行期间,仓库会处于转换中的镜像状态,因此请轮询 Get Active Mirror Transition Job 或 Get Mirror Transition Job,直到作业进入终止状态。
若仓库不处于该转换所要求的起始状态,或已有进行中的转换作业,则返回 FailedPrecondition (HTTP 400) 。调用方必须对镜像的上游源仓库拥有管理权限;不具备该权限时返回 403。
Path Parameters
ownerSlug string 必填
repoName string 必填
Request Body
transition string 必填
initial_to_inbound、inbound_to_outbound、outbound_to_inbound。Response Fields
repository object
job object
curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/mirror:transition' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "transition": "inbound_to_outbound"}'响应结构:
{ "repository": { "id": "repo_01k2ja2000e0080000000000q4", "name": "rocket", "fullName": "acme/rocket", "owner": { "slug": "acme", "id": "ns_01k2ja2000e0080000000000p3", "type": "team" }, "defaultBranch": "main", "createdAt": "2026-08-01T09:30:00Z", "updatedAt": "2026-08-02T15:00:00Z", "pushedAt": "2026-08-02T14:45:00Z", "cloneUrl": "https://origin.cursor.com/git/acme/rocket.git", "mirror": { "source": "github", "sourceId": "R_kgDOAbc123", "status": "inbound" } }, "job": { "id": "rmt_01k2ja2000e0080000000000m3", "transition": "inbound_to_outbound", "status": "running", "phase": "draining-writes", "attemptCount": 1, "drainUntil": "2026-08-02T15:05:00Z", "startedAt": "2026-08-02T15:00:00Z", "createdAt": "2026-08-02T14:59:30Z", "updatedAt": "2026-08-02T15:01:00Z" }}强制代码仓库镜像切换
/v1/origin/repos/{ownerSlug}/{repoName}/mirror:forceCutover强制执行 outbound_to_inbound 切换,且不将本 host 上分叉的 state 推送回上游源。源将以其当前状态被采纳为 source of truth,仅存在于本 host 上的 refs 会被快照保存后废弃。返回用于追踪本次强制切换的作业。
仅当代码仓库处于 outbound status,或卡在 outbound 到 inbound 的转换中且其 active 作业报告 requires_attention 时,该请求才会被接受,此时该作业将被取代。其他任何 state (包括已排队或正在运行的转换作业) 都会返回 FailedPrecondition (HTTP 400) 。调用方必须在镜像的上游源上拥有该代码仓库的管理权限;不具备该 access 的调用方将收到 403。
Path Parameters
ownerSlug string 必填
repoName string 必填
Request Body
该请求不接受任何 fields,请发送一个空的 JSON object。
Response Fields
repository object
job object
curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/mirror:forceCutover' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{}'响应结构:
{ "repository": { "id": "repo_01k2ja2000e0080000000000q4", "name": "rocket", "fullName": "acme/rocket", "owner": { "slug": "acme", "id": "ns_01k2ja2000e0080000000000p3", "type": "team" }, "defaultBranch": "main", "createdAt": "2026-08-01T09:30:00Z", "updatedAt": "2026-08-02T15:00:00Z", "pushedAt": "2026-08-02T14:45:00Z", "cloneUrl": "https://origin.cursor.com/git/acme/rocket.git", "mirror": { "source": "github", "sourceId": "R_kgDOAbc123", "status": "outbound" } }, "job": { "id": "rmt_01k2ja2000e0080000000000m4", "transition": "outbound_to_inbound", "status": "running", "phase": "snapshotting-refs", "attemptCount": 1, "startedAt": "2026-08-02T15:00:00Z", "createdAt": "2026-08-02T14:59:30Z", "updatedAt": "2026-08-02T15:01:00Z" }}分离代码仓库镜像
/v1/origin/repos/{ownerSlug}/{repoName}/mirror将镜像代码仓库与其上游来源永久断开连接。代码仓库会保留当前内容并转为原生代码仓库,双向同步随之停止,镜像的部署凭据也会被删除。响应体为空。
分离操作无法通过此 API 撤销。从未设置过镜像的代码仓库会返回 FailedPrecondition (HTTP 400) ;对已分离的代码仓库再次执行分离会成功,但不产生任何效果。
Path Parameters
ownerSlug string 必填
repoName string 必填
Response Fields
请求成功时不返回响应体。
curl --request DELETE \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/mirror' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response:
204 No Content获取 Mirror 迁移作业
/v1/origin/repos/{ownerSlug}/{repoName}/mirror/transition-jobs/{jobId}根据 id 返回单个镜像迁移作业。若 id 不存在,则返回 404。
路径参数
ownerSlug string 必填
repoName string 必填
jobId string 必填
job.id 返回的值。响应字段
id string
transition string
initial_to_inbound、inbound_to_outbound、outbound_to_inbound。status string
queued、running、succeeded、failed_rolled_back、requires_attention、superseded。其中 succeeded、failed_rolled_back 和 superseded 为终态。requires_attention 需要操作人员介入或强制切换。phase string
status 内的进度详情,用于展示和调试。取值为 queued、starting、draining-writes、initializing-mirror-fetch、finalizing-mirror-fetch、finalizing-mirror-push、snapshotting-refs、verifying-integrity、reopening-inbound-mirror、committing-target-status、rolling-back 或 completed 之一。随着迁移流程的演进,可能会出现新的阶段,因此请通过轮询 status 判断是否完成,而不要匹配具体阶段。attemptCount 整数
drainUntil string
lastErrorCode string
InboundMirrorDrainTimeout 或 MirrorIntegrityMismatch。作业未失败时不返回该字段。lastErrorMessage string
lastErrorCode 的可读说明。作业未失败时不返回该字段。startedAt string
completedAt string
createdAt string
updatedAt string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/mirror/transition-jobs/JOB_ID' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'响应结构:
{ "id": "rmt_01k2ja2000e0080000000000m3", "transition": "inbound_to_outbound", "status": "succeeded", "phase": "completed", "attemptCount": 1, "startedAt": "2026-08-02T15:00:00Z", "completedAt": "2026-08-02T15:12:00Z", "createdAt": "2026-08-02T14:59:30Z", "updatedAt": "2026-08-02T15:12:00Z"}获取活跃的 镜像 转换作业
/v1/origin/repos/{ownerSlug}/{repoName}/mirror/transition-jobs:active返回该代码仓库当前活跃的 镜像 转换作业,以及最近一个进入终态的作业。两个字段均为可选,因此从未发生过转换的代码仓库会返回一个空 object。轮询此 endpoint 即可跟踪转换进度:当 activeJob 消失后,lastJob 会告诉你转换的最终结果。
Path Parameters
ownerSlug string 必填
repoName string 必填
Response Fields
activeJob object
lastJob object
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/mirror/transition-jobs:active' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'响应结构:
{ "activeJob": { "id": "rmt_01k2ja2000e0080000000000m4", "transition": "outbound_to_inbound", "status": "running", "phase": "draining-writes", "attemptCount": 1, "drainUntil": "2026-08-02T15:05:00Z", "startedAt": "2026-08-02T15:00:00Z", "createdAt": "2026-08-02T14:59:30Z", "updatedAt": "2026-08-02T15:01:00Z" }, "lastJob": { "id": "rmt_01k2ja2000e0080000000000m3", "transition": "inbound_to_outbound", "status": "succeeded", "phase": "completed", "attemptCount": 1, "startedAt": "2026-08-01T10:00:00Z", "completedAt": "2026-08-01T10:12:00Z", "createdAt": "2026-08-01T09:59:30Z", "updatedAt": "2026-08-01T10:12:00Z" }}