# XenoCI: 이 안내를 AI에게 주세요

## 한국어

Mac 구매와 결제는 사람이, 빌드와 실패 분석은 AI가 맡습니다. Claude Code, Codex, Cursor 또는 HTTP 요청을 실행할 수 있는 ChatGPT에 아래 블록을 붙여 넣으세요. 키는 신뢰하는 비공개 대화나 에이전트의 비밀 입력에만 전달하세요. HTTP 실행 기능이 없는 대화는 직접 빌드할 수 없습니다.

### 복사해서 시작하기

```text
XenoCI로 이 프로젝트의 빌드와 PR 빌드 상태 확인을 맡아줘.
API 키: <YOUR_XENOCI_API_KEY> (이미 XENOCI_API_KEY에 있으면 그 값을 사용)
https://xenoci.com/openapi.json 과 https://xenoci.com/llms.txt 를 읽고,
첫 인증 호출은 GET https://xenoci.com/api/ci/v1/status 로 해.
키는 Authorization: Bearer로만 전달하고 출력, 로그, 커밋, 빌드 스크립트에 남기지 마.
API/CLI/MCP 중 가능한 도구로 프로젝트를 빌드하고 /v1/builds에서 해당 PR/commit의
결과를 찾아 /v1/builds/{id}로 확인해. failure.summary와
/v1/builds/{id}/log?tail=200을 읽고 고객 코드 오류는 고쳐서 다시 빌드해.
대기 중에는 /v1/queue로 순번을 알리고 같은 빌드를 중복 제출하지 마.
작업 중 /v1/status의 advice와 남은 시간을 확인하고 expiring_soon이면 나에게 알려줘.
24시간 연장 견적이 9,900원이라면 "이 Mac은 N시간 남았어요, 연장하는 게 좋겠어요 - 하루 연장 9,900원"
이라고 물어봐. N은 실제 남은 시간, 가격은 해당 Mac의 최신 견적으로 바꿔.
내가 명시적으로 동의한 뒤에만 연장 주문을 만들고 응답의 pay_url을 나에게 줘.
스스로 구매, 결제, 자동 연장하지 마. Mac이 없으면 https://xenoci.com/app/store 를 알려줘.
오류의 retryable, retry_after_s, next[]를 따르되 구매 승인 규칙을 넘지 마.
플랫폼 장애는 재시도 가능할 때 한 번만 다시 시도하고 계속 실패하면 request_id와 빌드 ID를 보고해.
최종 상태, 실패 원인과 수정 내용, 남은 Mac 시간, 내가 해야 할 일만 간결히 보고해.
```

### 주소와 실행 규칙

- REST 기준 주소는 `https://xenoci.com/api/ci/v1`입니다. 이 문서의 `/v1/...`는 `https://xenoci.com/api/ci` 뒤에 붙입니다. 기준 주소에 `/v1`을 또 붙이지 마세요.
- 공개 설명서는 `https://xenoci.com/openapi.json`, `https://xenoci.com/llms.txt`입니다. 키를 붙이지 않고 읽습니다. 이 안내서의 배포 주소는 `https://xenoci.com/api/ci/v1/agent/start`와 `https://xenoci.com/agent-start.md`입니다.
- CLI/MCP의 `XENOCI_API_URL`은 REST 기준 주소가 아니라 `https://xenoci.com`입니다. 클라이언트가 API 접두사를 붙입니다.
- 키 권한은 세 단계입니다. `read`는 모든 조회, `build`는 빌드 제출·취소와 업로드, `manage`는 Mac 리셋·Xcode·설정 변경, 승인받은 연장 주문, 시크릿을 처리합니다. 예전 `order`, `secrets` 권한은 `manage`에 포함됩니다. 권한이 부족하면(`insufficient_scope`) 소유자에게 필요한 권한만 요청합니다.
- XenoCI 키는 GitHub 읽기 권한이 아닙니다. 비공개 프로젝트는 에이전트가 접근 가능한 로컬 폴더를 CLI/MCP로 업로드하세요. 키를 추가로 요구하지 않아도 됩니다.
- 빌드 생성 응답의 `id`를 보관합니다. PR 번호와 실제 빌드한 commit SHA, 빌드 ID를 함께 기록하세요. 현재 클라이언트는 PR을 `--ref refs/pull/123/head`, commit을 `--ref <SHA>`로 지정합니다. PR head는 바뀔 수 있으므로 정확한 재현에는 SHA를 사용하세요.
- `GET /v1/builds`의 `q`, `state`, `before`, `limit`으로 조회하고 `next_before`가 있으면 다음 페이지를 확인합니다. PR ref 또는 SHA로 검색한 뒤 저장소와 ref를 대조합니다. 지원하지 않는 PR 필터나 PR 전용 경로를 만들지 마세요. 실제 OpenAPI에 `pr`/`commit` 메타데이터가 있으면 그 스키마를 따릅니다.
- 오류는 기본 응답의 `error_detail`, 또는 `XenoCI-Error-Format: 2` 응답의 `error` 객체에서 읽습니다. `retryable=false`면 자동 재시도하지 않습니다. `retry_after_s`가 있으면 그 시간보다 일찍 재시도하지 않고, `next[]`의 method/path/body/query를 확인합니다. 경로가 `/api/ci/v1/...`면 호스트에 붙입니다. 주문 안내가 있어도 소유자 승인이 먼저입니다.
- 같은 제출의 네트워크 재시도에는 같은 `Idempotency-Key`를 사용합니다. 코드 수정 후 새 빌드나 실패한 빌드의 의도적인 재실행에는 새 키를 사용합니다. 대기는 `GET /v1/builds/{id}/wait?timeout=60` 또는 MCP `wait_build`를 사용하고, 반환 후 아직 실행 중이면 다시 기다립니다.
- `failure.category`가 없으면 기존 `failure.fault`를 읽습니다. `failure.summary`, 로그 끝부분, 필요한 오류 주변 줄을 함께 보고 원인을 판정하세요. 분류가 없으면 플랫폼 장애로 단정하지 않습니다.

### MCP 연결

공개 저장소 [Xeno-CI/build](https://github.com/Xeno-CI/build)의 `xenoci-mcp` 실행 파일을 사용합니다. Node.js 18 이상과 `npx`가 필요합니다. 아래 키 자리만 비공개 설정에서 교체하고 실제 키를 채운 설정 파일은 커밋하지 마세요. 키를 출력하는 디버그 로그나 셸 추적(`set -x`)은 끕니다.

Claude Code 등록:

```sh
claude mcp add xenoci --env 'XENOCI_API_KEY=<YOUR_XENOCI_API_KEY>' -- npx -y -p github:xeno-ci/build xenoci-mcp
```

위 명령의 `<YOUR_XENOCI_API_KEY>`는 실행 전에 실제 값으로 교체해야 합니다. 명령 기록에 키를 남기지 않으려면 비공개 MCP 설정 편집기를 사용하세요.

Codex는 `~/.codex/config.toml`에 다음을 추가합니다. 에이전트를 실행하는 환경에 `XENOCI_API_KEY`를 비밀로 제공하면 설정 파일에 키를 저장할 필요가 없습니다.

```toml
[mcp_servers.xenoci]
command = "npx"
args = ["-y", "-p", "github:xeno-ci/build", "xenoci-mcp"]
env_vars = ["XENOCI_API_KEY"]
```

Cursor의 사용자 MCP 설정(프로젝트에 넣는 경우 `.cursor/mcp.json`)과 Claude Code의 `.mcp.json`은 다음 구조를 사용합니다. 실제 키가 들어간 파일은 비공개로 유지하세요.

```json
{
  "mcpServers": {
    "xenoci": {
      "command": "npx",
      "args": ["-y", "-p", "github:xeno-ci/build", "xenoci-mcp"],
      "env": {
        "XENOCI_API_KEY": "<YOUR_XENOCI_API_KEY>",
        "XENOCI_API_URL": "https://xenoci.com"
      }
    }
  }
}
```

연결 후 사용 도구는 `list_macs`, `build`, `build_status`, `wait_build`, `build_log`, `list_errors`, `extend`입니다. `build_log`는 `{"id":"<BUILD_ID>","mode":"tail","lines":200}`, 연장 견적은 `extend`에 `{"rental_id":"<RENTAL_ID>","hours":24,"quote_only":true}`를 전달합니다. `quote_only`를 빼면 주문이 만들어지므로 소유자 승인 전에는 빼지 마세요. MCP에 상태/대기열 도구가 없으면 해당 조회는 아래 REST를 사용합니다.

### CLI 빠른 명령

`XENOCI_API_KEY`가 비밀 환경 변수로 설정된 셸에서 실행합니다. `./ci.sh`는 프로젝트의 실제 빌드 스크립트여야 합니다. Node.js가 있으면 별도 전역 설치 없이 공개 저장소에서 실행할 수 있습니다.

```sh
npx -y github:xeno-ci/build whoami --json
npx -y github:xeno-ci/build macs --json
npx -y github:xeno-ci/build build --script ./ci.sh --json
npx -y github:xeno-ci/build build --script ./ci.sh --repo OWNER/REPO --ref refs/pull/123/head --no-wait --json
npx -y github:xeno-ci/build status "$BUILD_ID" --json
npx -y github:xeno-ci/build logs "$BUILD_ID" --tail 200 --json
npx -y github:xeno-ci/build errors --kind build --json
npx -y github:xeno-ci/build extend "$RENTAL_ID" --hours 24 --quote --json
```

첫 인증 호출인 REST 상태 조회를 먼저 하세요. CLI의 `status`는 계정 상태가 아니라 빌드 ID 조회입니다. 기본 `build`는 완료까지 로그를 출력하며 빌드 종료 코드를 반환합니다. `--no-wait`는 접수만 합니다. 기다리는 CLI를 Ctrl+C로 끊으면 빌드도 취소됩니다.

CLI 없이 `bash`·`curl`만 쓰는 CI별 예제(Jenkins, GitLab custom executor, Buildkite, CircleCI, Bitrise, Azure, 로컬 sh·PowerShell)는 https://github.com/Xeno-CI/build/tree/main/integrations 에 있습니다. 파일을 저장소에 그대로 복사해 씁니다.

### REST 예시

Bash 예시입니다. 키는 이미 비밀 환경 변수 `XENOCI_API_KEY`에 있다고 가정합니다. `BUILD_ID`, `RENTAL_ID`는 API가 반환한 값으로 지정하세요. 오류 JSON을 읽을 수 있도록 `curl -sS`를 사용하며 HTTP 상태도 함께 확인하세요.

```sh
BASE='https://xenoci.com/api/ci/v1'

# 첫 인증 호출: 내 Mac, 남은 시간, 조언
curl -sS -H "Authorization: Bearer $XENOCI_API_KEY" "$BASE/status"

# PR head 빌드. OWNER/REPO와 스크립트를 실제 공개 프로젝트에 맞게 변경
curl -sS -X POST "$BASE/builds" \
  -H "Authorization: Bearer $XENOCI_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: pr-123-attempt-1' \
  --data '{"repo":"OWNER/REPO","ref":"refs/pull/123/head","script":"bash ./ci.sh"}'

# 특정 commit 빌드. ref는 실제 commit SHA로 변경
curl -sS -X POST "$BASE/builds" \
  -H "Authorization: Bearer $XENOCI_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: commit-build-attempt-1' \
  --data '{"repo":"OWNER/REPO","ref":"<COMMIT_SHA>","script":"bash ./ci.sh"}'

# PR 결과 목록과 실패 목록. 페이지가 더 있으면 next_before를 before로 전달
curl -sS -G -H "Authorization: Bearer $XENOCI_API_KEY" "$BASE/builds" \
  --data-urlencode 'q=refs/pull/123/head' --data-urlencode 'limit=50'
curl -sS -H "Authorization: Bearer $XENOCI_API_KEY" "$BASE/builds?state=failed&limit=50"

# 단일 결과, 마지막 200줄, 내 대기열
curl -sS -H "Authorization: Bearer $XENOCI_API_KEY" "$BASE/builds/$BUILD_ID"
curl -sS -H "Authorization: Bearer $XENOCI_API_KEY" "$BASE/builds/$BUILD_ID/log?tail=200"
curl -sS -H "Authorization: Bearer $XENOCI_API_KEY" "$BASE/queue"

# 견적만 조회. 24시간 = 하루, 결제나 연장 주문을 만들지 않음
curl -sS -X POST "$BASE/rentals/$RENTAL_ID/extend/quote" \
  -H "Authorization: Bearer $XENOCI_API_KEY" \
  -H 'Content-Type: application/json' --data '{"hours":24}'
```

두 빌드 생성 예시는 대안입니다. 둘 다 무조건 실행하지 마세요. 같은 `Idempotency-Key`를 다른 작업에 재사용하지 말고 시도별 고유 값으로 바꾸세요. PR/commit은 `ref`로 제출하므로 현재 API가 저장하지 않는 필드를 보낸 뒤 추적된다고 가정하지 않습니다.

소유자가 Mac ID, 24시간, 견적 금액에 동의한 뒤에만 다음을 실행하고, 응답의 `pay_url`을 그대로 소유자에게 전달합니다. 사람이 그 링크에서 로그인, 약관 동의, 결제를 합니다. 주문 생성만으로 연장된 것이 아닙니다.

```sh
curl -sS -X POST "$BASE/rentals/$RENTAL_ID/extend" \
  -H "Authorization: Bearer $XENOCI_API_KEY" \
  -H 'Content-Type: application/json' --data '{"hours":24}'
```

결제 후 `GET /v1/orders/{no}` 또는 `GET /v1/orders/{no}/wait?timeout=60`으로 `ready`를 확인한 뒤 `GET /v1/status`를 다시 읽습니다. `ended`는 그 주문의 Mac이 모두 끝났다는 뜻이니 빌드하지 말고 새 Mac이 필요하면 소유자에게 상점을 안내합니다. 금액은 견적 응답을 기준으로 합니다. Lite 하루 9,900원, Standard 하루 14,900원 예시를 혼동하지 마세요. 준비 중인 Mac은 실제 이용 가능 상태와 남은 시간을 서버 응답대로 보고하며 자체적으로 이용 시간을 차감하지 않습니다.

### 상황별 판단

| 상황 | 에이전트가 할 일 |
| --- | --- |
| `queued` | 대기열과 `queue_position`, `wait_reason`을 보고하고 기다립니다. `wait_reason`이 `no_mac_with_xcode`나 `pinned_mac_xcode_mismatch`면 저절로 풀리지 않으니 소유자에게 `wait_next`대로 Mac의 Xcode를 바꿀지 묻거나 빌드를 취소합니다. 예상 시각이 없으면 만들지 않습니다. 중복 제출하지 않습니다. |
| `failure.category=customer` 또는 `failure.fault=customer` | `failure.summary`와 로그 끝부분을 읽고 사용자 코드 오류를 수정해 새 빌드를 제출합니다. |
| `failure.category=platform` 또는 `failure.fault=platform` | 재시도 가능 여부와 대기 시간을 지켜 한 번 재실행합니다. 계속 실패하면 빌드 ID, `request_id`, 요약을 보고합니다. 분류가 원인 증거를 대신하지는 않습니다. |
| `no_active_mac` 또는 기존 `no_active_rental` | https://xenoci.com/app/store 에서 구매해야 한다고 알립니다. 에이전트가 임의로 주문하거나 결제하지 않습니다. |
| `expiring_soon` 조언 | 남은 N시간과 24시간 연장 견적을 알리고 동의를 구합니다. 동의 후에만 연장 주문을 만들고 `pay_url`을 전달합니다. |
| 재시도 가능한 API 오류 | `retry_after_s` 이후 `next[]`를 확인해 필요한 요청만 합니다. 응답이 불명확한 제출은 같은 멱등 키로 확인합니다. |
| `request_id`가 빌드에 없음 | API 응답의 `X-Request-Id` 또는 오류 봉투를 확인하고 필요하면 `GET /v1/errors`에서 빌드 ID와 대조합니다. 없는 ID는 만들지 않습니다. |
| 어디서 막혔는지 모름 | `GET /v1/logs/requests?status=failed&limit=20`으로 이 계정의 최근 실패 호출(시각·경로·상태·오류 code·`next`·`request_id`)을 읽고 같은 요청을 고쳐 보냅니다. 소유자는 같은 기록을 /app/api-keys에서 봅니다. |
| 인증/권한 오류 또는 알 수 없는 실패 | 반복 호출을 멈추고 키 값 없이 오류 코드와 필요한 조치를 소유자에게 알립니다. |

## English

### Give this to your AI

Use Claude Code, Codex, Cursor, or ChatGPT with a tool that can make authenticated HTTP requests. A chat without execution tools can't run builds. Share your key only through a trusted private conversation or secret input.

```text
Use XenoCI to build this project and track its PR builds.
API key: <YOUR_XENOCI_API_KEY> (use XENOCI_API_KEY if already provided).
Read https://xenoci.com/openapi.json and https://xenoci.com/llms.txt.
Make GET https://xenoci.com/api/ci/v1/status your first authenticated call.
Send the key only as Authorization: Bearer. Never print it or put it in logs, commits, or build scripts.
Use API, CLI, or MCP to build. Find the PR/commit result through /v1/builds and
check /v1/builds/{id}. Read failure.summary and /v1/builds/{id}/log?tail=200,
fix customer code errors, then rebuild. Report queue position through /v1/queue; don't submit duplicates.
Check /v1/status advice and remaining rental time during the task. For expiring_soon,
tell me: "This Mac has N hours left. I recommend extending it. One extra day costs KRW 9,900."
Replace N with actual remaining time and the price with this Mac's current 24-hour quote.
Only after I explicitly approve may you create an extension order and give me its pay_url.
Never buy, pay, or auto-renew on your own. If no Mac is active, direct me to https://xenoci.com/app/store.
Respect retryable, retry_after_s, and next[] without bypassing owner approval.
For platform failures, retry once only when allowed, then report request_id and build ID if it still fails.
Finish with the result, cause and fix, remaining Mac time, and anything I must do.
```

### Setup and commands

The REST base is `https://xenoci.com/api/ci/v1`. A path written as `/v1/...` belongs under `https://xenoci.com/api/ci`, not under the base a second time. CLI/MCP `XENOCI_API_URL` is the origin `https://xenoci.com`. Public docs don't need the key. Keys have three levels: `read` for every GET, `build` to submit and cancel builds and upload, and `manage` for Mac reset, Xcode and setup changes, approved extension orders, and secrets. The older `order` and `secrets` scopes are part of `manage`. On `insufficient_scope`, ask the owner only for the missing level. This guide is published at `https://xenoci.com/api/ci/v1/agent/start` and `https://xenoci.com/agent-start.md`.

The MCP snippets above work for Claude Code, Codex, and Cursor. They run the real `xenoci-mcp` binary from [Xeno-CI/build](https://github.com/Xeno-CI/build), using Node.js 18+ and `npx`. Replace placeholders privately. Keep filled configuration files out of version control; disable shell tracing. Codex's `env_vars` passes the secret from its launch environment instead of storing it in TOML. The JSON snippet is for Cursor's MCP settings or Claude Code's `.mcp.json`.

After the initial REST status call, the CLI quick commands above upload a local folder, submit a PR ref, inspect a build, read its log tail, and quote an extension. Replace the build script, repository, and IDs with real project values. CLI `status` needs a build ID; it isn't the account status endpoint. Default `build` follows logs and exits with the build's exit code. `--no-wait` submits only. Interrupting a waiting CLI cancels that build.

CI examples that need only `bash` and `curl` (Jenkins, GitLab custom executor, Buildkite, CircleCI, Bitrise, Azure, local sh and PowerShell): https://github.com/Xeno-CI/build/tree/main/integrations. Copy the files into your repository.

MCP tools include `list_macs`, `build`, `build_status`, `wait_build`, `build_log`, `list_errors`, and `extend`. Read the tail with `build_log` arguments `{"id":"<BUILD_ID>","mode":"tail","lines":200}`. Quote with `extend` arguments `{"rental_id":"<RENTAL_ID>","hours":24,"quote_only":true}`. Omitting `quote_only` creates an order and requires prior owner approval. Use REST for status or queue if your installed MCP doesn't expose them.

### REST and build tracking

The curl block above is runnable Bash with `XENOCI_API_KEY` supplied as a secret environment variable. It covers status, PR and commit submission, listing failures, getting a build, log tail, queue, and a 24-hour extension quote. Set `BUILD_ID` and `RENTAL_ID` from API responses. The two POST build examples are alternatives, not a sequence to run blindly. Inspect HTTP status along with the returned JSON.

PR builds use `ref: "refs/pull/123/head"`; immutable commit builds use `ref: "<COMMIT_SHA>"`. Keep the PR number, resolved SHA, and returned build ID together. Search `GET /v1/builds` with `q` for the ref or SHA, compare the repository/ref, and follow `next_before` using `before`. Use `state=failed` to list failures. Don't invent a PR endpoint or assume unsupported metadata was stored. If the live OpenAPI exposes explicit `pr`/`commit` fields, follow that schema. For private code, upload the agent's local checkout through CLI/MCP; an XenoCI key isn't a GitHub credential.

Reuse the same `Idempotency-Key` when retrying an uncertain submission, but use a new value for a changed build or deliberate rerun. Wait through `GET /v1/builds/{id}/wait?timeout=60` or MCP `wait_build` rather than submitting again. If the response is still queued or running, wait again.

Error details are in `error_detail` by default, or the `error` object with request header `XenoCI-Error-Format: 2`. Don't retry when `retryable=false`. Honor `retry_after_s` and inspect `next[]` method/path/body/query. A suggested path starting with `/api/ci/v1` is relative to the origin. A suggested purchase never overrides the approval rule. Use `failure.category`, falling back to the existing `failure.fault`, together with `failure.summary` and the log tail.

### Extensions and decisions

Quote with `POST /v1/rentals/{id}/extend/quote`, body `{"hours":24}`. Tell the owner the Mac, remaining time, duration, and quoted amount. The KRW 9,900 example is Lite pricing; Standard is KRW 14,900 per day. Always use the current quote. Only after approval call `POST /v1/rentals/{id}/extend` with the same duration and hand the returned `pay_url` to the owner. They log in, accept terms, and pay. Creating an order isn't a completed extension. Check `GET /v1/orders/{no}` or its `/wait?timeout=60` route for `ready`, then refresh status. `ended` means every Mac of that order has ended: don't build on it; if a new Mac is needed, point the owner to the store. Report preparation state and remaining time as the server returns them; don't start your own rental clock.

| When | The agent should |
| --- | --- |
| `queued` | Wait and report `queue_position` and `wait_reason`. `no_mac_with_xcode` and `pinned_mac_xcode_mismatch` never clear by themselves: ask the owner whether to change a Mac's Xcode as `wait_next` shows, or cancel. Don't invent an estimated start or submit duplicates. |
| `failure.category=customer` or `failure.fault=customer` | Read the summary and log tail, fix the user's code, and submit a new build. |
| `failure.category=platform` or `failure.fault=platform` | Retry once if allowed, respecting the retry delay. If it fails again, report the build ID, `request_id`, and summary. |
| `xcode_not_on_any_mac` or `pinned_mac_xcode_mismatch` (409 on submit) | None of your Macs (or not the pinned one) has that Xcode. Ask the owner before changing a Mac's Xcode with `POST /v1/rentals/xcode` or `PATCH /v1/rentals/{id}`; then submit again. |
| `no_active_mac` or existing `no_active_rental` | Tell the owner to buy at https://xenoci.com/app/store. Don't place an order on your own. |
| `expiring_soon` advice | Ask about an extension using the remaining hours and quote. Create the order only after explicit approval, then give the owner `pay_url`. |
| Retryable API error | Follow the retry delay and relevant `next[]` actions without crossing the payment approval boundary. |
| No build `request_id` | Check `X-Request-Id`, the error envelope, or `GET /v1/errors` matched to the build ID. Don't invent an ID. |
| Not sure where it got stuck | Read `GET /v1/logs/requests?status=failed&limit=20`: this account's recent failed calls with time, path, status, error code, `next`, and `request_id`. Fix that request. The owner sees the same log at /app/api-keys. |
| Authentication, scope, or unknown failure | Stop repeated calls. Report the code and required action without exposing the key. |
