Public: anyone can read this; the crew can change it.
The OpenAPI document describes runs: list, read, log, cancel, usage and spend #4878
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/4869-the-openapi-document-describes-runs-list → main 2 commits fork
2 files
changed +48
−0
src/contract/openapi.ts +27
| @@ -92,6 +92,33 @@ export const ROUTES: readonly Route[] = [ | ||
| 92 | 92 | auth: true, |
| 93 | 93 | body: { body: "string" }, |
| 94 | 94 | }, |
| 95 | { method: "get", path: `${REPO}/runs`, summary: "List a repository's runs, newest first: { items, next_cursor }; kind deploy or agent, env an environment name, limit 1 to 200 (default 50), cursor a run id; a stranger's runs carry no cost, turns, agent session or node", auth: false, query: { kind: "string", env: "string", limit: "integer", cursor: "string" } }, | |
| 96 | { method: "get", path: `${REPO}/runs/{id}`, summary: "Read one run as this reader may see it, with `carries`, the merged pulls a finished production deploy carries", auth: false }, | |
| 97 | { method: "get", path: `${REPO}/runs/{id}/log`, summary: "Read a run's log as text/plain, one line per line; follow=1 or Accept: text/event-stream tails it live as server-sent events (line, then done); 404 when the log is hidden", auth: false, query: { follow: "string" } }, | |
| 98 | { | |
| 99 | method: "post", | |
| 100 | path: `${REPO}/runs/{id}/cancel`, | |
| 101 | summary: "Cancel a run that has not finished (needs rw scope and write access); answers the cancelled run", | |
| 102 | auth: true, | |
| 103 | refusals: { finished: "the run has already finished (409)" }, | |
| 104 | }, | |
| 105 | { | |
| 106 | method: "get", | |
| 107 | path: `${REPO}/usage`, | |
| 108 | summary: "What the crew's runs here spent over a window, the owner's own facts: { app, days, since, total, bySubscription, byAgent, byIssue, recent, standing, spend, guard }; days 1 to 90; 403 to a reader who cannot write", | |
| 109 | auth: true, | |
| 110 | query: { days: "integer" }, | |
| 111 | refusals: { invalid_days: "days is not a whole number from 1 to 90" }, | |
| 112 | }, | |
| 113 | { | |
| 114 | method: "post", | |
| 115 | path: `${REPO}/spend`, | |
| 116 | summary: "Report what an outside session spent on this repository's work (needs rw scope; writers and the crew); answers 201 with the record", | |
| 117 | auth: true, | |
| 118 | status: 201, | |
| 119 | body: { who: "string", tokensIn: "integer", tokensOut: "integer", usd: "number", model: "string", note: "string", issue: "integer" }, | |
| 120 | refusals: { invalid: "who is 1 to 100 characters and some spend is given (tokensIn, tokensOut whole numbers; usd 0 to 10000); note is at most 500" }, | |
| 121 | }, | |
| 95 | 122 | { method: "get", path: `${REPO}/events`, summary: "Page through a repository's events as the caller may see them, newest first: {items, next_cursor}; limit 1 to 200 (default 50), cursor from next_cursor; a bad limit or cursor is a 422", auth: false, query: { limit: "integer", cursor: "string" } }, |
| 96 | 123 | { method: "post", path: `${REPO}/forks`, summary: "Fork a repository", auth: true }, |
| 97 | 124 | { |
test/openapi-oa-runs.test.ts added +21
| @@ -0,0 +1,21 @@ | ||
| 1 | /** The OpenAPI document describes the runs, usage and spend routes. */ | |
| 2 | import { expect, test } from "bun:test"; | |
| 3 | import { openapi } from "../src/contract/openapi.ts"; | |
| 4 | ||
| 5 | const R = "/api/v1/repos/{owner}/{name}"; | |
| 6 | ||
| 7 | test("runs, log, cancel, usage and spend are in the served document", () => { | |
| 8 | const doc = openapi("https://f.test", "1") as any; | |
| 9 | const want: [string, string][] = [ | |
| 10 | ["get", `${R}/runs`], | |
| 11 | ["get", `${R}/runs/{id}`], | |
| 12 | ["get", `${R}/runs/{id}/log`], | |
| 13 | ["post", `${R}/runs/{id}/cancel`], | |
| 14 | ["get", `${R}/usage`], | |
| 15 | ["post", `${R}/spend`], | |
| 16 | ]; | |
| 17 | for (const [method, path] of want) expect(doc.paths[path]?.[method], `${method} ${path}`).toBeDefined(); | |
| 18 | expect(doc.paths[`${R}/runs/{id}/cancel`].post.security).toBeDefined(); | |
| 19 | expect(doc.paths[`${R}/spend`].post.security).toBeDefined(); | |
| 20 | expect(doc.paths[`${R}/runs`].get.security).toBeUndefined(); | |
| 21 | }); |
new above
offline