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

An agent reads the changelog: GET /api/v1/changelog and an MCP kind answer what went live, as /changelog shows it #8392

Merged opened this

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

Merged

torque/forge:torque/8325-an-agent-reads-the-changelog-get-api-v1 → main 3 commits fork 3 threads · 3 unresolved

  • Merged. torque/8325-an-agent-reads-the-changelog-get-api-v1 was merged into main as 1a1edcc.

Review

Sign in to review this change.
14 files changed +190 −22
docs/MCP.md +1 −1
@@ -143,7 +143,7 @@ for a tunnel and a forge that sleeps.
143143 | `read_file` | a file (utf-8, or base64 for binary) or a directory listing at a ref, as GET …/contents; MP4/MOV/M4A files also carry about: {kind, seconds, width?, height?, says} | |
144144 | `release_hold` | (admin scope) `{ reason }` or `{ lift }`: hold the release keeper (#7557) with a reason, so it cuts nothing until the hold is lifted; or lift an owner hold by its id. A failed verify's hold is lifted by a verified release after a pull saying `release hold N` lands, not here. A person who may roll back, with an admin-scope token, never an agent | yes |
145145 | `release_keeper` | `{}`: what the release keeper (#7557) did: its releases newest first (version, commit (short sha), stage — rolling, verifying, verified or rolled_back —, the known-good version it would roll back to, roll probes and non-200s, probes and pages under load, worst ms, seconds dark, reason, landedToLiveSeconds — from the merge of its pull to the new version answering, null until it has (#8042)) and its holds (kind failed or owner, version, reason, probe numbers, the pull that said `release hold N`, lifted); `blocked` says what stops the next cut; for whoever may read /releases | |
146 | `releases` | `{shipped: true}`: what went live, for anyone: `version`, `commit`, `live` (newest first: `repo`, `number`, `title`, `at`) and `onItsWay` (landed, not yet in the running commit), the list /changelog shows. `{}`: the forge's kept releases newest first: short sha, version, the pull that landed it, when, and for the live one `live: true` and `since`; for whoever may read /releases, others get its refusal; no rollback, a person does that on the page | |
146 | `releases` | `{shipped: true, repo?}`: what went live, for anyone: `version`, `commit`, `live` (newest first: `repo`, `number`, `title`, `at`) and `onItsWay` (landed, not yet in the running commit), the list /changelog shows, for `repo` (`all` or `owner/name`; absent is every repository). `{}`: the forge's kept releases newest first: short sha, version, the pull that landed it, when, and for the live one `live: true` and `since`; for whoever may read /releases, others get its refusal; no rollback, a person does that on the page | |
147147 | `repo` | `{ repo }`: one repository's description, visibility, autonomy, `audience { level, sentence }` and `provenance` (where a migrated one came from, `null` when hidden), the same as `GET /api/v1/repos/:owner/:name` | |
148148 | `repo_labels` | `{ repo, action?, name?, color?, id? }`: list (`list`, the default, paged by `limit` 1–200 and `cursor`, as `{ items, next_cursor }`, each label with `open`, its open issues and pulls, for a writer; a read-only token may), create (`create`, `name`, `color` six hex digits), rename or recolour (`update`, `id`, `name` and/or `color`, as the labels page) or remove (`remove`, `id`) the repository's labels, as `GET`/`POST …/labels` and `PATCH`/`DELETE …/labels/:id`; create, update and remove need an admin token and a repository admin | yes |
149149 | `repo_settings` | (admin scope) `{ repo, description?, visibility? }` (admin): change a repository's description and visibility as `PATCH /api/v1/repos/:owner/:name` does, for the same callers (a repository admin); answers `{ repo, description, visibility }` (with none to change it only reads); a visibility other than public or private is `invalid` | yes |
docs/features/changelog.md +4
@@ -11,3 +11,7 @@ One bounded `git log` read, cached per head oid; Server-Timing names `releases`
1111 `/changelog` lists the pulls merged into a default branch, newest first, grouped by UTC day: title, link, repository, when it landed. A pull to the forge's own repository (`FORGE_REPO`) counts only once the running commit (`FORGE_COMMIT`, the one `/api/v1/health` reports; the default branch head for a forge run from source) contains its merge commit, asked of git with `merge-base --is-ancestor` and kept while live. Landed but not yet contained shows above as "On its way". A pull to any other public repository is live when it lands. The top line says the live version and commit and how many shipped today. `?repo=all` or `?repo=owner/name` filters; the forge's own repository is the default.
1212
1313 The list is a live region (`pull.merged`, `app.serving`): it swaps in place without a reload. MCP `releases` with `{shipped: true}` answers the same list to anyone. The "no release" alarm reads the same record (`release/landings.ts`), refreshed each minute, and is silent until a read has happened. The older `Release <version>: …` commit read (`release/changelog.ts`) now serves only the terminal's Changelog screen.
14
15 ## For agents
16
17 `GET /api/v1/changelog` is the page's list as JSON, public, cached five seconds: `{version, commit, live, onItsWay}`, each landing `{repo, number, title, at, live}`. `?repo=all` or `?repo=owner/name` filters; the default is the forge's own repository, as on the page. MCP `releases` with `{shipped: true, repo?}` and `GET /api/v1/releases?shipped=true[&repo=]` answer the same rows with the same default. Only public repositories are read; concurrent requests share one read of git and the merged rows, so an anonymous caller costs no more than a page view. The page links the JSON with `rel="alternate"`. The terminal's Changelog screen is the release-commit view (`release/changelog.ts`); it does not show the landings list.
src/contract/openapi/routes/getApiV1Changelog.ts added +10
@@ -0,0 +1,10 @@
1 import type { Route } from "../shared.ts";
2
3 export const route: Route = {
4 method: "get",
5 path: "/api/v1/changelog",
6 summary: "What went live and what is on its way, as /changelog shows it, for anyone: version, commit, live and onItsWay rows (repo, number, title, at) of public repositories; ?repo=all|owner/name, this forge's own by default (all when it has none)",
7 auth: false,
8 query: { repo: "string" },
9 refusals: {},
10 };
src/contract/openapi/routes/index.ts +1
@@ -50,6 +50,7 @@ export { route as getApiV1BoardsPins } from "./getApiV1BoardsPins.ts";
5050 export { route as getApiV1BoardsSlug } from "./getApiV1BoardsSlug.ts";
5151 export { route as getApiV1BoardsSlugPanelsId } from "./getApiV1BoardsSlugPanelsId.ts";
5252 export { route as getApiV1BoardsSources } from "./getApiV1BoardsSources.ts";
53 export { route as getApiV1Changelog } from "./getApiV1Changelog.ts";
5354 export { route as getApiV1CloudAccounts } from "./getApiV1CloudAccounts.ts";
5455 export { route as getApiV1Contribute } from "./getApiV1Contribute.ts";
5556 export { route as getApiV1ConversationTurns } from "./getApiV1ConversationTurns.ts";
src/http/api/releases.ts +1 −1
@@ -16,7 +16,7 @@ export function releasesRoutes(router: Router<Handler>): void {
1616 const shipped = ctx.url.searchParams.get("shipped");
1717 if (shipped !== null && shipped !== "true")
1818 return apiError("invalid_shipped", 422, "shipped is true (what went live) or absent (the kept releases)");
19 if (shipped === "true") return json(await shippedAnswer(ctx.app));
19 if (shipped === "true") return json(await shippedAnswer(ctx.app, ctx.url.searchParams.get("repo")));
2020 const answer = releasesAnswer(ctx.app, ctx.auth.user);
2121 return answer.ok ? json(answer.value) : apiError(answer.error, 403, answer.message);
2222 });
src/http/mcp/tools/releases.ts +3 −3
@@ -3,12 +3,12 @@ import { bad, good, type Tool } from "../shared.ts";
33
44 export const tool: Tool = {
55 name: "releases",
6 doc: "`{shipped: true}`: what went live, for anyone: `version`, `commit`, `live` (newest first: `repo`, `number`, `title`, `at`) and `onItsWay` (landed, not yet in the running commit), the list /changelog shows. `{}`: the forge's kept releases newest first: short sha, version, the pull that landed it, when, and for the live one `live: true` and `since`; for whoever may read /releases, others get its refusal; no rollback, a person does that on the page",
6 doc: "`{shipped: true, repo?}`: what went live, for anyone: `version`, `commit`, `live` (newest first: `repo`, `number`, `title`, `at`) and `onItsWay` (landed, not yet in the running commit), the list /changelog shows, for `repo` (`all` or `owner/name`; absent is the forge's own, as on the page). `{}`: the forge's kept releases newest first: short sha, version, the pull that landed it, when, and for the live one `live: true` and `since`; for whoever may read /releases, others get its refusal; no rollback, a person does that on the page",
77 description: "Kept releases, newest first.",
8 inputSchema: { type: "object", properties: { shipped: { type: "boolean" } } },
8 inputSchema: { type: "object", properties: { shipped: { type: "boolean" }, repo: { type: "string" } } },
99 writes: false,
1010 async run(ctx, user, args) {
11 if (args["shipped"] === true) return good(await shippedAnswer(ctx.app));
11 if (args["shipped"] === true) return good(await shippedAnswer(ctx.app, typeof args["repo"] === "string" ? args["repo"] : null));
1212 // The page's own gate: its refusal, in words.
1313 const answer = releasesAnswer(ctx.app, user);
1414 return answer.ok ? good(answer.value) : bad(answer.error, answer.message);
src/http/server.ts +3 −1
@@ -171,7 +171,7 @@ import {
171171 domainsMoved,
172172 } from "../web/pages/backups.ts";
173173 import { apiPage, appApiPage } from "../web/pages/api.ts";
174 import { changelogPage } from "../web/pages/changelog.ts";
174 import { changelogJson, changelogPage } from "../web/pages/changelog.ts";
175175 import { releaseHoldSubmit, liftSubmit, releasesPage, rollbackSubmit } from "../web/pages/releases.ts";
176176 import {
177177 cloudAdd,
@@ -1568,6 +1568,8 @@ export function buildApiRouter(): Router<Handler> {
15681568 apiRoutes(router);
15691569 feedRoutes(router);
15701570 checksRecheckRoutes(router);
1571 // What went live, as /changelog shows it; the page's own module answers.
1572 router.add("GET", "/api/v1/changelog", changelogJson);
15711573 // The reference page for these routes lives at their root, so it answers here.
15721574 router.add("GET", "/api", apiPage);
15731575 return router;
src/release/answer.ts +21 −5
@@ -9,7 +9,7 @@ import { getRepoById } from "../core/repos.ts";
99 import type { User } from "../core/users.ts";
1010 import { running } from "../kernel/version.ts";
1111 import { BOOTED, mayRead } from "./gate.ts";
12 import { readLandings } from "./landings.ts";
12 import { chosenRepo, readLandings, type Landing, type Landings } from "./landings.ts";
1313 import { listReleases, releaseName } from "./record.ts";
1414
1515 export const RELEASES_REFUSAL = "the release history is for the forge's administrators and its repository's readers";
@@ -18,10 +18,26 @@ export type ReleasesAnswer =
1818 | { ok: true; value: unknown }
1919 | { ok: false; error: "forbidden"; message: string };
2020
21 /** What went live, for anyone: the list /changelog shows. */
22 export async function shippedAnswer(app: App) {
23 const { version, commit, live, onItsWay } = await readLandings(app);
24 return { version, commit, live, onItsWay };
21 /** Reads in flight, shared: concurrent anonymous requests cost git and the merged rows once. */
22 let reading: Promise<Landings> | undefined;
23 function readOnce(app: App): Promise<Landings> {
24 reading ??= readLandings(app).finally(() => {
25 reading = undefined;
26 });
27 return reading;
28 }
29
outdated

undefined means all here but the HTTP route passes null and gets the forge's own: two defaults for one read. Unify.

30 /**
31 * What went live, for anyone: the list /changelog shows. `asked` is the
32 * page's `?repo=`: "all" or `owner/name`; absent, or not a repository in the
33 * list, is the forge's own, as on the page. Only public repositories are
34 * ever read (`mergedRows`).
35 */
36 export async function shippedAnswer(app: App, asked: string | null = null) {
37 const read = await readOnce(app);
38 const choice = chosenRepo(asked, [...read.onItsWay, ...read.live], read.own);
39 const keep = (l: Landing) => choice === "all" || l.repo === choice;
40 return { version: read.version, commit: read.commit, live: read.live.filter(keep), onItsWay: read.onItsWay.filter(keep) };
2541 }
2642
2743 /** The kept releases newest first, for whoever may read /releases; others get its refusal. */
src/release/landings.ts +7
@@ -39,6 +39,13 @@ export interface Landings {
3939 readonly onItsWay: readonly Landing[];
4040 }
4141
42 /** Which repository the reader asked for: "" for this forge's own, "all", or one named in the list; anything else is the default. */
43 export function chosenRepo(asked: string | null, all: readonly Landing[], own: string | undefined): string {
44 if (asked === "all") return "all";
45 if (asked !== null && all.some((l) => l.repo === asked)) return asked;
46 return own === undefined ? "all" : own;
47 }
48
4249 /** More merged pulls than this are older than anything the page shows. */
4350 const ROWS = 300;
4451 /** Merge commits of the forge's own repository asked of git on one read. */
src/web/layout.ts +6
@@ -99,6 +99,8 @@ export interface PageOptions {
9999 ogImage?: string | undefined;
100100 /** This page's feed, advertised in the head so a reader can find it. */
101101 feed?: { href: string; title: string };
102 /** This page's JSON twin, advertised in the head for agents. */
103 json?: { href: string; title: string };
102104 /** Breadcrumb segments rendered in the topbar, e.g. owner / repo. */
103105 crumbs?: { label: string; href: string }[];
104106 body: Html;
@@ -739,6 +741,10 @@ export function page(options: PageOptions, status = 200): Response {
739741 href="${f.href}"
740742 />`,
741743 )}
744 ${when(
745 options.json,
746 (j) => html`<link rel="alternate" type="application/json" title="${j.title}" href="${j.href}" />`,
747 )}
742748 <meta name="color-scheme" content="light dark" />
743749 ${theme
744750 ? html`<meta name="theme-color" content="${metaThemeColor(theme)}" />`
src/web/pages/changelog.ts +10 −9
@@ -11,9 +11,11 @@ import type { Ctx } from "../../http/context.ts";
1111 import { canRead, getRepo } from "../../core/repos.ts";
1212 import { newestLive, secondsWords } from "../../keeper/landed.ts";
1313 import { dayHeading, dayOf, daysJs, shippedToday } from "../../release/days.ts";
14 import { readLandings, type Landing, type Landings } from "../../release/landings.ts";
14 import { chosenRepo, readLandings, type Landing, type Landings } from "../../release/landings.ts";
1515 import { liveRegionsJs } from "../client.ts";
1616 import { pageHead } from "../chrome.ts";
17 import { json } from "../../http/respond.ts";
18 import { shippedAnswer } from "../../release/answer.ts";
1719 import { html, type Html } from "../html.ts";
1820 import { page } from "../layout.ts";
1921 import { badge, card, empty, listCard, row, seg, sha, time } from "../ui.ts";
@@ -23,13 +25,6 @@ const LIVE = "pull.merged,app.serving";
2325 /** Other repositories offered as filters: more than this is a search, not a chip. */
2426 const FILTERS = 6;
2527
26 /** Which repository the reader asked for: "" for this forge's own, "all", or one named in the list; anything else is the default. */
27 export function chosenRepo(asked: string | null, all: readonly Landing[], own: string | undefined): string {
28 if (asked === "all") return "all";
29 if (asked !== null && all.some((l) => l.repo === asked)) return asked;
30 return own === undefined ? "all" : own;
31 }
32
3328 function landingRow(l: Landing, own: string | undefined, showRepo: boolean): Html {
outdated

Drop this re-export; import chosenRepo from release/landings.ts where it is used.

3429 return row({
3530 href: `/${l.repo}/pulls/${l.number}`,
@@ -163,7 +158,13 @@ export async function changelogPage(ctx: Ctx): Promise<Response> {
163158 const readable = ownRepo !== undefined && canRead(ctx.app.db, ownRepo, ctx.auth.user);
164159 const body = html`${pageHead("Changelog", { sub: "What went live, as it happens." })}${filters}
165160 <div id="shipped" data-live="${LIVE}" aria-live="polite">${shippedRegion(read, choice, new Date(), "UTC", newestLive(ctx.app.db), readable)}</div>`;
166 const response = page({ ctx, title: "Changelog", body, scriptJs: `${liveRegionsJs}\n${localDaysJs}` });
161 const response = page({ ctx, title: "Changelog", body, json: { href: `/api/v1/changelog${choice === read.own ? "" : `?repo=${encodeURIComponent(choice)}`}`, title: "Changelog (JSON)" }, scriptJs: `${liveRegionsJs}\n${localDaysJs}` });
167162 notePhase(ctx.request, "render", performance.now() - rendering);
168163 return response;
169164 }
165
166 // GET /api/v1/changelog?repo=all|owner/name: the page's rows as JSON, public repositories only.
167 export async function changelogJson(ctx: Ctx): Promise<Response> {
168 notePhase(ctx.request, "session", takeSessionTime(ctx.request));
169 return json(await shippedAnswer(ctx.app, ctx.url.searchParams.get("repo")), 200, { "cache-control": "public, max-age=5" });
170 }
test/changelog-api.test.ts added +121
@@ -0,0 +1,121 @@
1 /** `GET /api/v1/changelog` and MCP `releases {shipped, repo}` answer the rows /changelog shows; a private repo's landing is never in them. */
2 import { afterAll, beforeAll, describe, expect, test } from "bun:test";
3 import { mkdtemp, rm } from "node:fs/promises";
4 import { tmpdir } from "node:os";
5 import { join } from "node:path";
6 import { createApp, type App } from "../src/app.ts";
7 import { createRepo } from "../src/actions/repos.ts";
8 import { createToken } from "../src/core/tokens.ts";
9 import { createUser } from "../src/core/users.ts";
10 import { forgetLive, keepLive } from "../src/release/landings.ts";
11 import { serve, type Servers } from "../src/http/server.ts";
12
13 describe("GET /api/v1/changelog", () => {
14 let dataDir: string;
15 let app: App;
16 let servers: Servers;
17 let admin: string;
18 const saved = process.env["FORGE_COMMIT"];
19 const get = (query: string, token?: string) =>
20 fetch(`${servers.url}/api/v1/changelog${query}`, token ? { headers: { authorization: `Bearer ${token}` } } : {});
21 const mcp = async (args: object, token: string) => {
22 const res = await fetch(`${servers.url}/mcp`, {
23 method: "POST",
24 headers: { "content-type": "application/json", accept: "application/json, text/event-stream", authorization: `Bearer ${token}` },
25 body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "releases", arguments: args } }),
26 });
27 const body: any = await res.json();
28 return JSON.parse(body.result.content[0].text);
29 };
30
31 beforeAll(async () => {
32 process.env["FORGE_COMMIT"] = "a".repeat(40);
33 dataDir = await mkdtemp(join(tmpdir(), "forge-changelog-api-"));
34 app = createApp({ dataDir, port: 0, forgeRepo: "alice/forge" });
35 servers = await serve(app);
36 const u = await createUser(app.db, {
37 origin: "seed", trusted: true, username: "alice", email: "[email protected]", password: "a-good-passphrase", adminIfFirst: true,
38 });
39 if (!u.ok) throw new Error(u.error);
40 admin = createToken(app.db, u.value.id, "a", "admin").token;
41 const insertIssue = app.db.query(
42 `INSERT INTO issue (id, repo_id, number, kind, title, author_id, state) VALUES (?1, ?2, ?3, 'pull', ?4, ?5, 'closed')`,
43 );
44 const insertPull = app.db.query(
45 `INSERT INTO pull (issue_id, base_branch, head_branch, base_oid, head_oid, merged_at, merged_oid) VALUES (?1, ?2, 'b', 'base', '', ?3, ?4)`,
46 );
47 let k = 0;
48 for (const [name, visibility, title] of [
49 ["forge", "public", "Public landing"],
50 ["notes", "public", "Other repo landing"],
51 ["secret", "private", "Private landing"],
52 ] as const) {
53 const made = await createRepo(app, u.value, { name, visibility });
54 if (!made.ok) throw new Error(String(made.error));
55 k++;
56 insertIssue.run(`i${k}`, made.value.id, 1, title, u.value.id);
57 insertPull.run(`i${k}`, made.value.defaultBranch, `2026-10-0${k}T10:00:00.000Z`, `oid-${k}`);
58 }
59 }, 30_000);
60
61 afterAll(async () => {
62 if (saved === undefined) delete process.env["FORGE_COMMIT"];
63 else process.env["FORGE_COMMIT"] = saved;
64 servers.stop();
65 app.close();
66 await rm(dataDir, { recursive: true, force: true });
67 });
68
69 const titles = (b: any) => [...b.live, ...b.onItsWay].map((l: any) => l.title).sort();
70
71 test("answers anyone with the page's shape", async () => {
72 const res = await get("?repo=all");
73 expect(res.status).toBe(200);
74 const b: any = await res.json();
75 expect(b.commit).toBe("a".repeat(40));
76 expect(typeof b.version).toBe("string");
77 const row = [...b.live, ...b.onItsWay][0];
78 expect(Object.keys(row).sort()).toEqual(["at", "live", "number", "repo", "title"]);
79 });
80
81 test("the repo filter: all, one, and everything when the forge has no repository of its own", async () => {
82 expect(titles(await (await get("?repo=all")).json())).toEqual(["Other repo landing", "Public landing"]);
83 expect(titles(await (await get("?repo=alice/notes")).json())).toEqual(["Other repo landing"]);
84 expect(titles(await (await get("")).json())).toEqual(["Other repo landing", "Public landing"]);
85 expect(titles(await (await get("?repo=nobody/nothing")).json())).toEqual(["Other repo landing", "Public landing"]);
86 expect(titles(await (await get("?repo=alice/forge")).json())).toEqual(["Public landing"]);
87 });
88
89 test("with a repository of its own the default is that repository, on every door", async () => {
90 keepLive("alice/forge", [], 0);
91 try {
92 expect(titles(await (await get("")).json())).toEqual(["Public landing"]);
93 expect(titles(await mcp({ shipped: true }, admin))).toEqual(["Public landing"]);
94 expect(titles(await (await fetch(`${servers.url}/api/v1/releases?shipped=true`)).json())).toEqual(["Public landing"]);
95 expect(titles(await (await get("?repo=all")).json())).toEqual(["Other repo landing", "Public landing"]);
96 expect((await get("")).headers.get("cache-control")).toBe("public, max-age=5");
97 } finally {
98 forgetLive();
99 }
100 });
101
102 test("a private repo's landing is hidden, even when named, from anonymous and from its owner's token", async () => {
103 for (const q of ["?repo=all", "?repo=alice/secret"]) {
104 expect(JSON.stringify(await (await get(q)).json())).not.toContain("Private landing");
105 expect(JSON.stringify(await (await get(q, admin)).json())).not.toContain("Private landing");
106 }
107 });
108
109 test("MCP releases returns the same rows", async () => {
110 for (const repo of ["all", "alice/notes"]) {
111 const viaMcp = await mcp({ shipped: true, repo }, admin);
112 expect(viaMcp).toEqual(await (await get(`?repo=${repo}`)).json());
113 }
114 });
115
116 test("the page links its JSON twin", async () => {
117 const html = await (await fetch(`${servers.url}/changelog`)).text();
118 expect(html).toContain('rel="alternate" type="application/json"');
119 expect(html).toContain('href="/api/v1/changelog?repo=all"');
120 });
121 });
test/changelog.test.ts +1 −1
@@ -125,7 +125,7 @@ describe("changelog page", () => {
125125 });
126126
127127 test("the same list is the MCP's, for a reader with a token", async () => {
128 const got = await mcp({ shipped: true });
128 const got = await mcp({ shipped: true, repo: "all" });
129129 expect(got["version"]).toBe("1.2.3");
130130 expect(got["commit"]).toBe(oids[1]);
131131 expect(got["live"].map((l: { title: string }) => l.title)).toEqual(["A note app change", "Second thing", "First <b>thing</b>"]);
test/design-audit.test.ts +1 −1
@@ -11,7 +11,7 @@ const doc = readFileSync("docs/design/audit.md", "utf8");
1111
1212 /** Routes that answer a file, a fragment, a redirect hop or a callback. */
1313 const NOT_PAGES =
14 /^\/(assets\/|\.well-known\/|icons\/|og\/|live\/|__forge\/|go\/|events$|system\/stream$|talk\/stream$|settings\/teams\/stream$|sw\.js$|robots\.txt$|favicon\.ico$|apple-touch-icon(-precomposed)?\.png$|sitemap\.xml$|manifest\.webmanifest$|opensearch\.xml$|llms\.txt$|releases\.json$|audit\.csv$|fleet\/graph\/live$|fleet\/graph\.csv$|settings\/origins\/callback$|home\/archived\/|settings\/backups\/live$)/;
14 /^\/(assets\/|\.well-known\/|icons\/|og\/|live\/|__forge\/|go\/|events$|system\/stream$|talk\/stream$|settings\/teams\/stream$|sw\.js$|robots\.txt$|favicon\.ico$|apple-touch-icon(-precomposed)?\.png$|sitemap\.xml$|manifest\.webmanifest$|opensearch\.xml$|llms\.txt$|releases\.json$|audit\.csv$|fleet\/graph\/live$|fleet\/graph\.csv$|settings\/origins\/callback$|home\/archived\/|settings\/backups\/live$|api\/v1\/)/;
1515
1616 const getRoutes = [
1717 ...server.matchAll(/router\.add\(\s*"GET",\s*"([^"]+)"/g),

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs