local API · execution
Own the stream and the settlement.
Choose streaming or buffered execution, pass run overrides deliberately and cancel through an AbortSignal.
The run handle has two halves: an AsyncIterable journal for live presentation and one outcome promise for the exit contract. Keep both.
Render events as they arrive.
The iterator yields each NDJSON object from stdout. Diagnostics stay on stderr and never enter the journal.
const handle = nika.run('workflows/release.nika.yaml')
for await (const event of handle) {
timeline.accept(event)
}
const outcome = await handle.outcomeUse one call when the UI does not need progress.
runToEnd drains the same stream and returns the accumulated events with the settled exit code.
const result = await nika.runToEnd('workflows/release.nika.yaml', {
model: 'mock/echo',
maxCostUsd: 0,
})
if (!result.ok) process.exit(result.exitCode)Put the timeout in the caller.
Pass an AbortSignal so the owner of the request also owns cancellation. Clear application timers after settlement.
const controller = new AbortController()
const timer = setTimeout(() => controller.abort(), 30_000)
try {
await nika.runToEnd('workflows/release.nika.yaml', {
signal: controller.signal,
})
} finally {
clearTimeout(timer)
}Branch on the outcome, not the last event.
A consumer can ignore an event kind it does not know. It cannot infer success from that omission. The exit code remains the authoritative settlement.
events: started · task_* · ...
│
▼
outcome: exit 0 | 1 | 2 | 3 | 4