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

The anonymous /api/v1/here manifest says how to get an account: make one and sign in, each with its route #8477

Merged opened this

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

Merged

mason/forge:mason/8461-the-anonymous-api-v1-here-manifest-says → main 3 commits fork

  • Merged. mason/8461-the-anonymous-api-v1-here-manifest-says was merged into main as 85f900d.

Review

Sign in to review this change.
2 files changed +48 −2
src/actions/here.ts +12 −1
@@ -12,6 +12,7 @@
1212 * An action that needs an account is listed for someone without one,
1313 * saying so, because that is the next thing they need to know.
1414 */
15 import { activeEdgeToken } from "../http/edge.ts";
1516 import { sshTellCommand } from "../ssh/command.ts";
1617 import { mcpPointers, TOKEN_FREE_TOOLS } from "../core/mcp_card.ts";
1718 import type { App } from "../app.ts";
@@ -310,7 +311,7 @@ function forgeActions(
310311 {
311312 do: "see what went live",
312313 http: "GET /api/v1/changelog",
313 account: false,
314 mcp: "releases",
314315 },
315316 {
316317 do: "read the HTTP contract",
@@ -321,6 +322,16 @@ function forgeActions(
321322 ? [{ do: "use the MCP", http: "POST /mcp", account: true }]
322323 : [{ do: "use the MCP", http: "POST /mcp" }]),
323324 ...(tellSsh === undefined ? [] : [{ do: "tell the crew", ssh: tellSsh }]),
325 // How a stranger gets the account the rows above ask for. An edge-login
326 // forge makes no account at /signup, as the home page's buttons say.
327 ...(viewer !== undefined
328 ? []
329 : [
330 ...(activeEdgeToken(app) === undefined
331 ? [{ do: "make an account", http: "POST /api/v1/users", account: false }]
332 : []),
333 { do: "sign in", http: "POST /api/v1/tokens", account: false },
334 ]),
324335 ];
325336 }
326337
test/here-manifest.test.ts +36 −1
@@ -274,12 +274,47 @@ describe("the manifest: what you can do here, as who you are", () => {
274274 expect(body.actions).toContainEqual({
275275 do: "see what went live",
276276 http: "GET /api/v1/changelog",
277 account: false,
277 mcp: "releases",
278278 });
279279 expect(size).toBeLessThan(2048);
280280 }
281281 });
282282
283 test("an anonymous viewer is told how to get an account; a signed-in one is not", async () => {
284 const anon = (await manifest("/")).body.actions;
285 expect(anon).toContainEqual({
286 do: "make an account",
287 http: "POST /api/v1/users",
288 account: false,
289 });
290 expect(anon).toContainEqual({
291 do: "sign in",
292 http: "POST /api/v1/tokens",
293 account: false,
294 });
295 expect(anon.findIndex((a: any) => a.do === "use the MCP")).toBeLessThan(
296 anon.findIndex((a: any) => a.do === "make an account"),
297 );
298 const signedIn = does((await manifest("/", "carol")).body);
299 expect(signedIn).not.toContain("make an account");
300 expect(signedIn).not.toContain("sign in");
301 });
302
303 test("an edge-login forge offers sign in but no sign-up", async () => {
304 const dir = await mkdtemp(join(tmpdir(), "forge-here-edge-"));
305 const edgeApp = createApp({ dataDir: dir, port: 0, edgeToken: "e" });
306 const edgeServers = await serve(edgeApp);
307 try {
308 const res = await fetch(`${edgeServers.url}/api/v1/here`);
309 const names = (await res.json() as any).actions.map((a: any) => a.do);
310 expect(names).toContain("sign in");
311 expect(names).not.toContain("make an account");
312 } finally {
313 edgeServers.stop();
314 await rm(dir, { recursive: true, force: true });
315 }
316 });
317
283318 test("the forge's manifest names the OpenAPI document, and it answers", async () => {
284319 for (const who of [undefined, "carol"]) {
285320 const { body, size } = await manifest("/", who);

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs