Public: anyone can read this; the crew can change it.
A repository's prompt routes leave api.ts for src/http/api/prompts.ts #7045
Check · Review · Land · Live done — Landed and live in production.
mason/forge:mason/6334-a-repository-s-prompt-routes-leave-api-t → main 1 commit fork
2 files
changed +84
−68
src/http/api.ts +2 −68
| @@ -269,12 +269,6 @@ import { | ||
| 269 | 269 | listAppDomains, |
| 270 | 270 | removeAppDomain, |
| 271 | 271 | } from "../actions/domains.ts"; |
| 272 | import { | |
| 273 | addPromptSource, | |
| 274 | listPromptSources, | |
| 275 | removePromptSource, | |
| 276 | runPromptSource, | |
| 277 | } from "../actions/prompts.ts"; | |
| 278 | 272 | import { listDomains } from "../core/domains.ts"; |
| 279 | 273 | import { |
| 280 | 274 | migrateMessage, |
| @@ -419,6 +413,7 @@ import { agentRoutes } from "./api/agents.ts"; | ||
| 419 | 413 | import { boardRoutes } from "./api/boards.ts"; |
| 420 | 414 | import { labelsRoutes } from "./api/labels.ts"; |
| 421 | 415 | import { orgRoutes } from "./api/orgs.ts"; |
| 416 | import { promptRoutes } from "./api/prompts.ts"; | |
| 422 | 417 | import { secretRoutes } from "./api/secrets.ts"; |
| 423 | 418 | import { upstreamRoutes } from "./api/upstream.ts"; |
| 424 | 419 | import type { Router } from "./router.ts"; |
| @@ -5426,68 +5421,7 @@ export function apiRoutes(router: Router<Handler>): void { | ||
| 5426 | 5421 | |
| 5427 | 5422 | egressGrantRoutes(router); |
| 5428 | 5423 | |
| 5429 | // Where the crew's next tell comes from when nobody is typing | |
| 5430 | // (docs/APPS.md, "Prompts from Instagram"). Admin scope and admin over | |
| 5431 | // the repository; the token goes in once and never comes back out. | |
| 5432 | router.add("GET", "/api/v1/repos/:owner/:name/prompts", (ctx) => { | |
| 5433 | notePhase(ctx.request, "session", takeSessionTime(ctx.request)); | |
| 5434 | const denied = requireScope(ctx, "admin"); | |
| 5435 | if (denied) return denied; | |
| 5436 | const repo = loadRepo(ctx); | |
| 5437 | if (!repo) return notFound(); | |
| 5438 | const listed = timePhase(ctx.request, "prompts", () => listPromptSources(ctx.app, ctx.auth.user, repo)); | |
| 5439 | if (!listed.ok) return respondAccess(listed.error, 422); | |
| 5440 | return timePhase(ctx.request, "render", () => json({ items: listed.value, next_cursor: null })); | |
| 5441 | }); | |
| 5442 | ||
| 5443 | router.add("POST", "/api/v1/repos/:owner/:name/prompts", async (ctx) => { | |
| 5444 | const denied = requireScope(ctx, "admin"); | |
| 5445 | if (denied) return denied; | |
| 5446 | const repo = loadRepo(ctx); | |
| 5447 | if (!repo) return notFound(); | |
| 5448 | const input = await body(ctx.request); | |
| 5449 | const added = addPromptSource(ctx.app, ctx.auth.user, repo, { | |
| 5450 | kind: str(input.kind), | |
| 5451 | token: str(input.token), | |
| 5452 | at: str(input.at), | |
| 5453 | }); | |
| 5454 | if (!added.ok) return respondAccess(added.error, 422); | |
| 5455 | return json(added.value, 201); | |
| 5456 | }); | |
| 5457 | ||
| 5458 | router.add( | |
| 5459 | "POST", | |
| 5460 | "/api/v1/repos/:owner/:name/prompts/:id/run", | |
| 5461 | async (ctx) => { | |
| 5462 | const denied = requireScope(ctx, "admin"); | |
| 5463 | if (denied) return denied; | |
| 5464 | const repo = loadRepo(ctx); | |
| 5465 | if (!repo) return notFound(); | |
| 5466 | const ran = await runPromptSource( | |
| 5467 | ctx.app, | |
| 5468 | ctx.auth.user, | |
| 5469 | repo, | |
| 5470 | ctx.params.id ?? "", | |
| 5471 | ); | |
| 5472 | if (!ran.ok) return respondAccess(ran.error, 422); | |
| 5473 | return json(ran.value); | |
| 5474 | }, | |
| 5475 | ); | |
| 5476 | ||
| 5477 | router.add("DELETE", "/api/v1/repos/:owner/:name/prompts/:id", (ctx) => { | |
| 5478 | const denied = requireScope(ctx, "admin"); | |
| 5479 | if (denied) return denied; | |
| 5480 | const repo = loadRepo(ctx); | |
| 5481 | if (!repo) return notFound(); | |
| 5482 | const removed = removePromptSource( | |
| 5483 | ctx.app, | |
| 5484 | ctx.auth.user, | |
| 5485 | repo, | |
| 5486 | ctx.params.id ?? "", | |
| 5487 | ); | |
| 5488 | if (!removed.ok) return respondAccess(removed.error, 422); | |
| 5489 | return json({}, 200); | |
| 5490 | }); | |
| 5424 | promptRoutes(router); | |
| 5491 | 5425 | |
| 5492 | 5426 | secretRoutes(router); |
| 5493 | 5427 |
src/http/api/prompts.ts added +82
| @@ -0,0 +1,82 @@ | ||
| 1 | /** | |
| 2 | * A repository's prompt sources over the API: | |
| 3 | * `/api/v1/repos/:owner/:name/prompts` — list, add, run one now, remove. | |
| 4 | * Moved out of `api.ts` (#6334); the rules live in `actions/prompts.ts`. | |
| 5 | */ | |
| 6 | ||
| 7 | import { | |
| 8 | addPromptSource, | |
| 9 | listPromptSources, | |
| 10 | removePromptSource, | |
| 11 | runPromptSource, | |
| 12 | } from "../../actions/prompts.ts"; | |
| 13 | import { notePhase, takeSessionTime, timePhase } from "../../core/route_timing.ts"; | |
| 14 | import { loadRepo } from "../api.ts"; | |
| 15 | import type { Handler } from "../context.ts"; | |
| 16 | import { body, json, notFound, requireScope, respondAccess, str } from "../respond.ts"; | |
| 17 | import type { Router } from "../router.ts"; | |
| 18 | ||
| 19 | export function promptRoutes(router: Router<Handler>): void { | |
| 20 | // Where the crew's next tell comes from when nobody is typing | |
| 21 | // (docs/APPS.md, "Prompts from Instagram"). Admin scope and admin over | |
| 22 | // the repository; the token goes in once and never comes back out. | |
| 23 | router.add("GET", "/api/v1/repos/:owner/:name/prompts", (ctx) => { | |
| 24 | notePhase(ctx.request, "session", takeSessionTime(ctx.request)); | |
| 25 | const denied = requireScope(ctx, "admin"); | |
| 26 | if (denied) return denied; | |
| 27 | const repo = loadRepo(ctx); | |
| 28 | if (!repo) return notFound(); | |
| 29 | const listed = timePhase(ctx.request, "prompts", () => listPromptSources(ctx.app, ctx.auth.user, repo)); | |
| 30 | if (!listed.ok) return respondAccess(listed.error, 422); | |
| 31 | return timePhase(ctx.request, "render", () => json({ items: listed.value, next_cursor: null })); | |
| 32 | }); | |
| 33 | ||
| 34 | router.add("POST", "/api/v1/repos/:owner/:name/prompts", async (ctx) => { | |
| 35 | const denied = requireScope(ctx, "admin"); | |
| 36 | if (denied) return denied; | |
| 37 | const repo = loadRepo(ctx); | |
| 38 | if (!repo) return notFound(); | |
| 39 | const input = await body(ctx.request); | |
| 40 | const added = addPromptSource(ctx.app, ctx.auth.user, repo, { | |
| 41 | kind: str(input.kind), | |
| 42 | token: str(input.token), | |
| 43 | at: str(input.at), | |
| 44 | }); | |
| 45 | if (!added.ok) return respondAccess(added.error, 422); | |
| 46 | return json(added.value, 201); | |
| 47 | }); | |
| 48 | ||
| 49 | router.add( | |
| 50 | "POST", | |
| 51 | "/api/v1/repos/:owner/:name/prompts/:id/run", | |
| 52 | async (ctx) => { | |
| 53 | const denied = requireScope(ctx, "admin"); | |
| 54 | if (denied) return denied; | |
| 55 | const repo = loadRepo(ctx); | |
| 56 | if (!repo) return notFound(); | |
| 57 | const ran = await runPromptSource( | |
| 58 | ctx.app, | |
| 59 | ctx.auth.user, | |
| 60 | repo, | |
| 61 | ctx.params.id ?? "", | |
| 62 | ); | |
| 63 | if (!ran.ok) return respondAccess(ran.error, 422); | |
| 64 | return json(ran.value); | |
| 65 | }, | |
| 66 | ); | |
| 67 | ||
| 68 | router.add("DELETE", "/api/v1/repos/:owner/:name/prompts/:id", (ctx) => { | |
| 69 | const denied = requireScope(ctx, "admin"); | |
| 70 | if (denied) return denied; | |
| 71 | const repo = loadRepo(ctx); | |
| 72 | if (!repo) return notFound(); | |
| 73 | const removed = removePromptSource( | |
| 74 | ctx.app, | |
| 75 | ctx.auth.user, | |
| 76 | repo, | |
| 77 | ctx.params.id ?? "", | |
| 78 | ); | |
| 79 | if (!removed.ok) return respondAccess(removed.error, 422); | |
| 80 | return json({}, 200); | |
| 81 | }); | |
| 82 | } |
new above
offline