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

/api/v1/here names the OpenAPI document among what an anonymous visitor can do #8368

Merged opened this

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

Merged

scout/forge:scout/8331-api-v1-here-names-the-openapi-document-a → main 2 commits fork

  • Merged. scout/8331-api-v1-here-names-the-openapi-document-a was merged into main as 399d11a.

Review

Sign in to review this change.
5 files changed +25 −1
src/actions/here.ts +5
@@ -306,6 +306,11 @@ function forgeActions(
306306 mcp: "contribute",
307307 account: false,
308308 },
309 {
310 do: "read the HTTP contract",
311 http: "GET /contract/openapi.json",
312 account: false,
313 },
309314 ...(viewer === undefined
310315 ? [{ do: "use the MCP", http: "POST /mcp", account: true }]
311316 : [{ do: "use the MCP", http: "POST /mcp" }]),
src/contract/openapi/routes/getContractOpenapiJson.ts added +3
@@ -0,0 +1,3 @@
1 import type { Route } from "../shared.ts";
2
3 export const route: Route = { method: "get", path: "/contract/openapi.json", summary: "This document: the OpenAPI 3.1 description of the forge's public API; /api/v1/openapi.json redirects here", auth: false };
src/contract/openapi/routes/index.ts +1
@@ -172,6 +172,7 @@ export { route as getApiV1UserPins } from "./getApiV1UserPins.ts";
172172 export { route as getApiV1UsersUsername } from "./getApiV1UsersUsername.ts";
173173 export { route as getApiV1UsersUsernameRepos } from "./getApiV1UsersUsernameRepos.ts";
174174 export { route as getApiV1UsersUsernameSignins } from "./getApiV1UsersUsernameSignins.ts";
175 export { route as getContractOpenapiJson } from "./getContractOpenapiJson.ts";
175176 export { route as getMetrics } from "./getMetrics.ts";
176177 export { route as patchApiV1AdminSettings } from "./patchApiV1AdminSettings.ts";
177178 export { route as patchApiV1AgentsUsername } from "./patchApiV1AgentsUsername.ts";
test/here-manifest.test.ts +15
@@ -269,6 +269,21 @@ describe("the manifest: what you can do here, as who you are", () => {
269269 }
270270 });
271271
272 test("the forge's manifest names the OpenAPI document, and it answers", async () => {
273 for (const who of [undefined, "carol"]) {
274 const { body, size } = await manifest("/", who);
275 expect(body.actions).toContainEqual({
276 do: "read the HTTP contract",
277 http: "GET /contract/openapi.json",
278 account: false,
279 });
280 expect(size).toBeLessThan(2048);
281 }
282 const res = await fetch(`${base}/contract/openapi.json`);
283 expect(res.status).toBe(200);
284 expect(res.headers.get("content-type")).toContain("application/json");
285 });
286
272287 test("/explore and /search name their query API and MCP tool, the query carried over", async () => {
273288 for (const who of [undefined, "carol"]) {
274289 const e = (await manifest("/explore?q=kind:app", who)).body;
test/openapi.test.ts +1 −1
@@ -30,7 +30,7 @@ test("every http route the manifest names is described", () => {
3030 });
3131
3232 test("every described route is registered in the router", () => {
33 const src = ["src/http/server.ts", "src/http/api.ts", "src/http/mcp.ts", "src/http/passport_edge.ts", "src/http/feed.ts", "src/http/checks_recheck.ts",
33 const src = ["src/http/server.ts", "src/http/api.ts", "src/http/mcp.ts", "src/http/passport_edge.ts", "src/http/feed.ts", "src/http/checks_recheck.ts", "src/contract/served.ts",
3434 ...readdirSync("src/http/api").map((f) => `src/http/api/${f}`)]
3535 .map((f) => readFileSync(f, "utf8"))
3636 .join("\n")

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs