# 실행

> Nodaro 워크플로 실행을 노드별로 추적하고, 각 노드의 결과를 읽고, 지난 실행 목록을 보며, 실행을 바로 또는 실행 중인 노드가 끝난 뒤 취소합니다.

Source: https://nodaro.ai/ko/docs/developers/api/executions

**실행**은 워크플로 전체를 한 번 실행한 기록입니다. 실행에는 상태, 완료된 노드 수, 사용한 크레딧, 모든 노드의 상태와 결과가 기록되며, 실행이 만든 작업이 AI 노드마다 하나씩 묶입니다. `POST /v1/workflows/:id/run`은 `executionId`를 반환하고, 실행 엔드포인트로 그 실행을 끝까지 추적합니다.

## 엔드포인트
| 메서드 | 경로 | 설명 |
| --- | --- | --- |
| `GET` | `/v1/workflow-executions/:id` | 실행 하나의 상태, 노드 수, 크레딧, 모든 노드의 상태를 가져옵니다. |
| `GET` | `/v1/workflow-executions/:id/stream` | 같은 실행을 서버 전송 이벤트 스트림으로 받습니다. |
| `GET` | `/v1/workflows/:id/executions` | 한 워크플로의 실행을 페이지 단위로 가져옵니다. |
| `POST` | `/v1/workflow-executions/:id/cancel` | 실행을 바로, 또는 실행 중인 노드가 끝난 뒤 취소합니다. |
| `GET` | `/v1/api/status/:execId` | API 토큰 경로입니다. 실행의 상태, 노드 수, 사용한 크레딧을 가져옵니다. |
| `GET` | `/v1/api/result/:execId` | API 토큰 경로입니다. 끝난 실행의 출력을 가져옵니다. |

마지막 두 라우트는 `POST /v1/api/run`으로 시작한 실행에 쓰입니다. [새 입력값으로 워크플로 실행](https://nodaro.ai/docs/developers/api/workflows#run-a-workflow-with-new-input-values)에서 설명합니다.

## 실행 가져오기
**curl**

```bash
curl -s https://app.nodaro.ai/v1/workflow-executions/3f9e2b1a-7c6d-4e5f-8a9b-0c1d2e3f4a5b \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

**TypeScript SDK**

```ts
const { data } = await client.executions.get(executionId)
console.log(data.status, `${data.completedNodes}/${data.totalNodes}`)
```

**CLI**

```bash
nodaro executions get 3f9e2b1a-7c6d-4e5f-8a9b-0c1d2e3f4a5b --json
```

```json
{
"data": {
"id": "3f9e2b1a-7c6d-4e5f-8a9b-0c1d2e3f4a5b",
"workflowId": "8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f",
"status": "running",
"triggerType": "manual",
"totalNodes": 4,
"completedNodes": 2,
"failedNodes": 0,
"totalCreditsUsed": 45,
"errorMessage": null,
"nodeStates": {
"text-prompt-1": { "status": "completed", "output": { "text": "a knight on a hill at dawn" } },
"generate-image-1": { "status": "completed", "output": { "imageUrl": "https://…/knight.png" } },
"generate-video-1": { "status": "running" },
"add-captions-1": { "status": "pending" }
},
"completedAt": null
}
}
```

| 필드 | 의미 |
| --- | --- |
| `status` | 실행의 상태입니다. 아래 표를 참조하세요. |
| `triggerType` | 실행을 시작한 주체입니다. 예: `manual`, `webhook`, `schedule`, `app_run`, `single-node`. |
| `totalNodes`, `completedNodes`, `failedNodes` | 노드 수입니다. 진행률은 `completedNodes / totalNodes`로 표시하세요. |
| `totalCreditsUsed` | 지금까지 실행에 사용한 크레딧입니다. |
| `errorMessage` | 실행이 실패하거나 멈춘 이유를 설명하는 문장입니다. |
| `nodeStates` | 노드 id를 키로 하는 모든 노드의 상태입니다. |
| `completedAt` | 실행이 끝난 시각 또는 `null`입니다. |

독립 단일 노드 작업의 id도 여기서 쓸 수 있습니다. 서버는 같은 형태로, 그 노드 하나를 설명하는 응답을 반환합니다. 존재하지 않거나 내 것이 아닌 id는 `404`를 반환합니다.

### 실행 상태
| 상태 | 최종 | 의미 |
| --- | --- | --- |
| `pending` | 아니요 | 실행이 대기열에 있습니다. |
| `running` | 아니요 | 노드가 실행 중입니다. |
| `stopping` | 아니요 | `after_current`로 취소했습니다. 실행 중인 노드가 끝나면 실행이 멈춥니다. |
| `completed` | 예 | 실행이 끝났습니다. |
| `failed` | 예 | 실행이 실패했습니다. `errorMessage`에 이유가 있습니다. |
| `cancelled` | 예 | 실행이 취소되었습니다. |
| `timed_out` | 예 | 실행 시간이 초과되었습니다. |
| `discarded` | 예 | `discard`로 취소했습니다. 실행 중인 작업이 캔버스를 업데이트하지 않고 끝났습니다. |

### 노드 상태
`nodeStates`의 각 항목에는 `status`가 있으며, 값은 `pending`, `running`, `completed`, `failed`, `skipped` 중 하나입니다. 실패한 노드에는 `error`도 있습니다. 이번 실행에서 입력이 아무것도 전달하지 않아 건너뛴 노드는 `{ "status": "skipped", "skipReason": "empty_input" }`입니다. 라우터의 비활성 경로 때문에 건너뛴 노드에는 `skipReason`이 없습니다. [새 내용이 없는 실행](https://nodaro.ai/docs/developers/api/workflows#runs-that-find-nothing-new)을 참고하세요.

완료된 노드는 `output`에 결과를 담습니다. 키는 노드의 출력에 따라 다릅니다. `url`, `imageUrl`, `videoUrl`, `audioUrl`, `resultUrl`, `text` 순서로 찾으세요.

**실패한** 노드에도, 실행이 구조화된 결과를 남긴 경우에는 `output`이 있을 수 있습니다. 현재는 3D 장면 작성 노드가 이에 해당합니다. 모든 수정을 거친 뒤에도 시각 검토를 통과하지 못한 장면도 초안은 게시하며, 노드는 그 초안을 `output.plan`에 담은 채 실패합니다. 여기서 두 가지 규칙이 나옵니다.

- **상태가 아니라 필드를 확인하세요.** `pending`이나 `running` 노드에는 `output`이 없으며, 앞으로 다른 노드 유형도 결과를 남길 수 있습니다.
- **`output`이 있다고 성공한 것은 아닙니다.** 노드는 실패했고, 무언가를 남겼을 뿐입니다.

SDK는 `completed`와 `failed`에서 `true`인 `nodeStateMayCarryOutput(status)`를 내보내며, 같은 두 값을 `OUTPUT_BEARING_NODE_STATUSES`로도 제공합니다.

## 실행이 끝날 때까지 대기
상태가 최종 상태가 될 때까지 2~5초마다 폴링하세요.

**TypeScript SDK**

```ts
const { executionId } = await client.workflows.run(workflowId)

const final = ['completed', 'failed', 'cancelled', 'timed_out', 'discarded']
while (true) {
const { data } = await client.executions.get(executionId)
console.log(`${data.completedNodes}/${data.totalNodes} nodes done`)
if (final.includes(data.status)) {
if (data.status !== 'completed') throw new Error(`Run ${data.status}: ${data.errorMessage ?? 'no message'}`)
console.log(`Done. Used ${data.totalCreditsUsed} credits.`)
break
}
await new Promise((r) => setTimeout(r, 2_000))
}
```

**curl**

```bash
while true; do
STATUS=$(curl -s -H "Authorization: Bearer $NODARO_API_KEY" \
"https://app.nodaro.ai/v1/workflow-executions/$EXEC" | jq -r .data.status)
echo "Status: $STATUS"
case "$STATUS" in completed|failed|cancelled|timed_out|discarded) break;; esac
sleep 3
done
```

**CLI**

```bash
nodaro executions get 3f9e2b1a-7c6d-4e5f-8a9b-0c1d2e3f4a5b --watch
```

`--watch`를 쓰면 CLI가 실행이 끝날 때까지 폴링하며, 성공하면 코드 `0`, 실행이 실패하면 `2`, 취소되면 `130`으로 종료합니다. `--json`을 쓰면 페이로드를 출력하고 정상 종료하므로, `.status`를 직접 확인하세요.

`GET /v1/workflow-executions/:id/stream`은 실행이 진행되는 동안 같은 실행 상태를 서버 전송 이벤트로 보내며, 위의 읽기와 같은 노드 `output` 규칙을 따릅니다. 대부분의 연동에는 폴링이 더 간단합니다.

## 워크플로의 실행 목록
`GET /v1/workflows/:id/executions`는 워크플로의 실행을 `{ data, nextCursor }` 형태로 페이지 단위로 반환합니다. 목록에는 전체 실행과 함께 그 워크플로의 독립 단일 노드 작업도 포함됩니다.

| 쿼리 | 의미 |
| --- | --- |
| `limit` | 페이지 크기입니다. |
| `cursor` | 이전 페이지의 `nextCursor`입니다. |
| `status` | 쉼표로 구분한 상태입니다. 예: `pending,running`. |
| `source` | `editor`는 앱, Webhook, 일정으로 시작한 실행을 제외합니다. `all`은 이 실행도 포함합니다. |

**curl**

```bash
curl -s "https://app.nodaro.ai/v1/workflows/$WORKFLOW_ID/executions?limit=20&status=completed" \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

**TypeScript SDK**

```ts
const { data, nextCursor } = await client.executions.listForWorkflow(workflowId, {
limit: 20,
status: 'completed',
})
```

## 실행 취소
`POST /v1/workflow-executions/:id/cancel`은 실행을 멈춥니다. 본문의 선택 사항인 `mode`가 이미 실행 중인 노드를 어떻게 처리할지 정합니다.

| `mode` | 동작 | 최종 상태 |
| --- | --- | --- |
| 없음 | 실행이 즉시 멈춥니다. 실행 중인 작업은 취소되고 예약된 크레딧이 환불됩니다. | `cancelled` |
| `after_current` | 실행 중인 노드가 끝나고, 그 결과가 캔버스와 내 라이브러리에 들어갑니다. 그다음 실행이 멈춥니다. | `stopping` 후 최종 상태 |
| `discard` | 새 노드는 시작되지 않습니다. 실행 중인 작업은 모델 쪽에서 멈출 수 없으므로 끝까지 실행되어 내 라이브러리에 저장되지만, 결과가 캔버스에 기록되지는 않습니다. 그 작업은 완료되었으므로 환불되지 않습니다. | `discarded` |

응답은 `{ "success": true }`입니다.

**curl**

```bash
curl -s -X POST https://app.nodaro.ai/v1/workflow-executions/$EXEC/cancel \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"mode": "after_current"}'
```

**TypeScript SDK**

```ts
await client.executions.cancel(executionId)                           // now
await client.executions.cancel(executionId, { mode: 'after_current' }) // after running nodes
await client.executions.cancel(executionId, { mode: 'discard' })       // stop scheduling
```

**CLI**

```bash
nodaro executions cancel 3f9e2b1a-7c6d-4e5f-8a9b-0c1d2e3f4a5b                # now
nodaro executions cancel 3f9e2b1a-7c6d-4e5f-8a9b-0c1d2e3f4a5b --mode stopping # after running nodes
```

전체 실행 대신 생성 하나만 멈추려면 해당 작업을 취소하세요. [작업](https://nodaro.ai/docs/developers/api/jobs#cancel-or-delete-a-job)을 참조하세요.

## 실행과 작업
실행의 AI 노드는 각각 [작업](https://nodaro.ai/docs/developers/api/jobs)을 하나씩 만들며, 작업이 끝나면 `nodeStates`에 각 노드의 결과가 담깁니다. 실행 전체를 추적하려면 실행을 읽으세요. 생성 하나의 세부 정보, 즉 `error_hint`, `credit_status`, 또는 모델에 보낸 모든 내용이 담긴 `input_data`를 보려면 작업을 읽으세요.

[**Webhook 트리거**](https://nodaro.ai/docs/nodes/automate/webhook-trigger) 호출이나 [**일정 트리거**](https://nodaro.ai/docs/nodes/automate/schedule-trigger) 발동처럼 트리거가 시작한 실행도 실행이며, `triggerType`은 `webhook` 또는 `schedule`입니다. [Webhook](https://nodaro.ai/docs/developers/api/webhooks)을 참조하세요.

## 자주 묻는 질문

### 실행과 작업은 어떻게 다른가요?

실행은 워크플로 전체를 한 번 실행한 기록입니다. 모든 노드의 상태를 기록하고, 실행이 만든 작업을 AI 노드마다 하나씩 묶습니다. 작업은 이미지 한 장이나 동영상 렌더링 한 번 같은 단일 생성입니다.

### 워크플로 실행의 상태를 확인하려면 어떻게 하나요?

GET /v1/workflow-executions/:id를 2~5초마다 폴링하세요. 실행의 상태, 완료되거나 실패한 노드 수, 지금까지 사용한 크레딧, 모든 노드의 상태를 반환합니다. 상태가 completed, failed, cancelled, timed_out, discarded 중 하나가 되면 멈추세요.

### 워크플로 실행을 취소하려면 어떻게 하나요?

POST /v1/workflow-executions/:id/cancel을 보내세요. mode 없이 보내면 즉시 취소되고 예약된 크레딧이 환불됩니다. mode가 after_current이면 실행 중인 노드가 먼저 끝납니다. mode가 discard이면 실행 중인 작업은 끝나지만 새 노드는 시작되지 않습니다.

### 실행에서 노드의 결과는 어디에 있나요?

nodeStates 안에서 노드의 id 아래에 있습니다. 완료된 노드는 output에 결과를 담습니다. 예: output.imageUrl, output.videoUrl, output.audioUrl, output.text.

### 워크플로의 지난 실행 목록을 볼 수 있나요?

네. GET /v1/workflows/:id/executions는 워크플로의 실행을 페이지 단위로 반환하며, 상태와 실행을 시작한 곳으로 필터링할 수 있습니다.
