Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

68 linesCodeBlameRaw
1/** Explicit Markdown negotiation; ordinary browsers and wildcard clients keep HTML. */
2export function pageRepresentation(accept: string | null): "html" | "markdown" | "none" {
3 if (!accept?.trim()) return "html";
4 const ranges = accept
5 .toLowerCase()
6 .split(",")
7 .map((part) => {
8 const [type, ...parameters] = part
9 .trim()
10 .split(";")
11 .map((value) => value.trim());
12 const quality = parameters.find((value) => value.startsWith("q="))?.slice(2) ?? "1";
13 const q = /^(?:0(?:\.\d{0,3})?|1(?:\.0{0,3})?)$/.test(quality) ? Number(quality) : 0;
14 return { type, q };
15 });
16 function weight(type: string) {
17 for (const [specificity, candidate] of [
18 [2, type],
19 [1, "text/*"],
20 [0, "*/*"],
21 ] as const) {
22 const matches = ranges.filter((range) => range.type === candidate);
23 if (matches.length) return { q: Math.max(...matches.map((range) => range.q)), specificity };
24 }
25 return { q: 0, specificity: -1 };
26 }
27 const html = weight("text/html");
28 const markdown = weight("text/markdown");
29 if (html.q === 0 && markdown.q === 0) return "none";
30 return markdown.q > html.q || (markdown.q === html.q && markdown.specificity > html.specificity) ? "markdown" : "html";
31}
32
33export function varyAccept(headers: Headers) {
34 const values = (headers.get("vary") ?? "")
35 .split(",")
36 .map((value) => value.trim())
37 .filter(Boolean);
38 if (!values.some((value) => value === "*" || value.toLowerCase() === "accept")) values.push("Accept");
39 headers.set("vary", values.join(", "));
40}
41
42export function developerResource(path: string, addresses: { api: string; mcp: string }): string | null {
43 const resources: Record<string, string> = {
44 "/developers": "https://docs.g1t.sh/reference/developers/",
45 "/openapi.json": `${addresses.api}/openapi.json`,
46 "/mcp": addresses.mcp,
47 };
48 return resources[path] ?? null;
49}
50
51/** Called only for document responses, after routing and authorization decide the status. */
52export function agentPage(request: Request, status: number, headers: Headers): Response | null {
53 if (!["GET", "HEAD"].includes(request.method)) return null;
54 const url = new URL(request.url);
55 if (status !== 404 && !(status === 200 && url.pathname === "/")) return null;
56 varyAccept(headers);
57 const representation = pageRepresentation(request.headers.get("accept"));
58 if (representation === "html") return null;
59 headers.set("content-type", representation === "markdown" ? "text/markdown; charset=utf-8" : "text/plain; charset=utf-8");
60 headers.delete("content-length");
61 const body =
62 representation === "none"
63 ? "This page is available as text/html or text/markdown.\n"
64 : status === 404
65 ? `# Page not found\n\nThe requested page does not exist or is not available to you.\n\nSee [g1t agent instructions](${url.origin}/llms.txt) or the [sitemap](${url.origin}/sitemap.xml) to find a public resource.\n`
66 : `# g1t\n\ng1t is an open-source workspace for repositories, work items, pull requests, reviews, required checks, a merge queue, deployments, team chat and Artifacts documents. Bring the coding agents you already use through MCP, REST or the CLI.\n\n## When to use g1t\n\nUse g1t to inspect a project's code and issues, prepare and review pull requests, follow checks and deployment status, and coordinate agent work in a shared repository. Use the API or MCP for structured reads and changes; use git for cloning and pushing code.\n\n## Developer resources\n\n- [Agent instructions](${url.origin}/llms.txt)\n- [g1t documentation](https://docs.g1t.sh/)\n- [REST API and versioning](https://docs.g1t.sh/reference/api/)\n- [OpenAPI](https://api.g1t.sh/openapi.json)\n- [Authentication and scopes](https://docs.g1t.sh/guides/authentication/)\n- [MCP server](https://mcp.g1t.sh)\n- [CLI installation](https://docs.g1t.sh/guides/cli/)\n- [Pricing](${url.origin}/pricing)\n\nPublic repositories can be read without credentials. Writes and private data require authorization; request only the scopes your task needs. Never ask a user to paste their password.\n`;
67 return new Response(request.method === "HEAD" ? null : body, { status: representation === "none" ? 406 : status, headers });
68}