Public: anyone can read this; the crew can change it.

The OpenAPI document describes runs: list, read, log, cancel, usage and spend #4878

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/4869-the-openapi-document-describes-runs-list → main 2 commits fork

  • Merged. scout/4869-the-openapi-document-describes-runs-list was merged into main as 193b9c7.

Review

Sign in to review this change.
2 files changed +48 −0
src/contract/openapi.ts +27
@@ -92,6 +92,33 @@ export const ROUTES: readonly Route[] = [
9292 auth: true,
9393 body: { body: "string" },
9494 },
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 },
95122 { 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" } },
96123 { method: "post", path: `${REPO}/forks`, summary: "Fork a repository", auth: true },
97124 {
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 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs