Skip to main content

Command Palette

Search for a command to run...

API

Origin 迁移 API

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 也无法调用这些端点。

端点参考

转换代码仓库镜像

POST/v1/origin/repos/{ownerSlug}/{repoName}/mirror:transition
Scoperepository:mirror:writeAuthUser access token

对镜像仓库启动一次镜像状态转换,并返回跟踪该转换的作业。作业运行期间,仓库会处于转换中的镜像状态,因此请轮询 Get Active Mirror Transition Job 或 Get Mirror Transition Job,直到作业进入终止状态。

若仓库不处于该转换所要求的起始状态,或已有进行中的转换作业,则返回 FailedPrecondition (HTTP 400) 。调用方必须对镜像的上游源仓库拥有管理权限;不具备该权限时返回 403。

Path Parameters

ownerSlug string 必填

所有者实体的唯一 slug。

repoName string 必填

仓库名称,在所有者实体内唯一。

Request Body

transition string 必填

要启动的镜像状态变更。允许的值:initial_to_inbound、inbound_to_outbound、outbound_to_inbound。

Response Fields

repository object

该仓库,反映其转换中的镜像状态。字段与 Get Repo 相同。

job object

跟踪该转换的作业,字段与 Get Mirror Transition Job 相同。请持续轮询,直到其进入终止状态。
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"  }}

强制代码仓库镜像切换

POST/v1/origin/repos/{ownerSlug}/{repoName}/mirror:forceCutover
Scoperepository:mirror:writeAuthUser access token

强制执行 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 必填

所有者实体的唯一 slug。

repoName string 必填

仓库名称,在所有者实体内唯一。

Request Body

该请求不接受任何 fields,请发送一个空的 JSON object。

Response Fields

repository object

该代码仓库,反映其正在转换中的 mirror state。包含与 Get Repo 相同的 fields。

job object

追踪该转换的作业,包含与 Get Mirror Transition Job 相同的 fields。请轮询该作业,直到其进入终止 status。
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"  }}

分离代码仓库镜像

DELETE/v1/origin/repos/{ownerSlug}/{repoName}/mirror
Scoperepository:mirror:deleteAuthUser access token

将镜像代码仓库与其上游来源永久断开连接。代码仓库会保留当前内容并转为原生代码仓库,双向同步随之停止,镜像的部署凭据也会被删除。响应体为空。

分离操作无法通过此 API 撤销。从未设置过镜像的代码仓库会返回 FailedPrecondition (HTTP 400) ;对已分离的代码仓库再次执行分离会成功,但不产生任何效果。

Path Parameters

ownerSlug string 必填

所有者实体的唯一 slug。

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 迁移作业

GET/v1/origin/repos/{ownerSlug}/{repoName}/mirror/transition-jobs/{jobId}
Scoperepository:metadata:readAuthUser access token

根据 id 返回单个镜像迁移作业。若 id 不存在,则返回 404。

路径参数

ownerSlug string 必填

所有者实体的唯一 slug。

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

正在进行的迁移的写入排空窗口结束时间,采用 RFC 3339 时间戳格式。非排空阶段时不返回该字段。

lastErrorCode string

标识作业上次失败原因的稳定代码,例如 InboundMirrorDrainTimeout 或 MirrorIntegrityMismatch。作业未失败时不返回该字段。

lastErrorMessage string

lastErrorCode 的可读说明。作业未失败时不返回该字段。

startedAt string

作业开始运行时间的 RFC 3339 时间戳。排队期间不返回该字段。

completedAt string

作业进入终止状态的时间,采用 RFC 3339 时间戳格式。在此之前不返回该字段。

createdAt string

RFC 3339 格式的作业创建时间戳。

updatedAt string

RFC 3339 格式的作业更新时间戳。
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"}

获取活跃的 镜像 转换作业

GET/v1/origin/repos/{ownerSlug}/{repoName}/mirror/transition-jobs:active
Scoperepository:metadata:readAuthUser access token

返回该代码仓库当前活跃的 镜像 转换作业,以及最近一个进入终态的作业。两个字段均为可选,因此从未发生过转换的代码仓库会返回一个空 object。轮询此 endpoint 即可跟踪转换进度:当 activeJob 消失后,lastJob 会告诉你转换的最终结果。

Path Parameters

ownerSlug string 必填

所有者实体的唯一 slug。

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"  }}