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.

52 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.toLowerCase().split(",").map((part) => {
5 const [type, ...parameters] = part.trim().split(";").map((value) => value.trim());
6 const quality = parameters.find((value) => value.startsWith("q="))?.slice(2) ?? "1";
7 const q = /^(?:0(?:\.\d{0,3})?|1(?:\.0{0,3})?)$/.test(quality) ? Number(quality) : 0;
8 return { type, q };
9 });
10 function weight(type: string) {
11 for (const [specificity, candidate] of [[2, type], [1, "text/*"], [0, "*/*"]] as const) {
12 const matches = ranges.filter((range) => range.type === candidate);
13 if (matches.length) return { q: Math.max(...matches.map((range) => range.q)), specificity };
14 }
15 return { q: 0, specificity: -1 };
16 }
17 const html = weight("text/html");
18 const markdown = weight("text/markdown");
19 if (html.q === 0 && markdown.q === 0) return "none";
20 return markdown.q > html.q || (markdown.q === html.q && markdown.specificity > html.specificity) ? "markdown" : "html";
21}
22
23export function varyAccept(headers: Headers) {
24 const values = (headers.get("vary") ?? "").split(",").map((value) => value.trim()).filter(Boolean);
25 if (!values.some((value) => value === "*" || value.toLowerCase() === "accept")) values.push("Accept");
26 headers.set("vary", values.join(", "));
27}
28
29export function developerResource(path: string, addresses: { api: string; mcp: string }): string | null {
30 const resources: Record<string, string> = {
31 "/developers": "https://docs.g1t.sh/reference/api/",
32 "/openapi.json": `${addresses.api}/openapi.json`,
33 "/mcp": addresses.mcp,
34 };
35 return resources[path] ?? null;
36}
37
38/** Called only for document responses, after routing and authorization decide the status. */
39export function agentPage(request: Request, status: number, headers: Headers): Response | null {
40 if (!["GET", "HEAD"].includes(request.method)) return null;
41 const url = new URL(request.url);
42 if (status !== 404 && !(status === 200 && url.pathname === "/")) return null;
43 varyAccept(headers);
44 const representation = pageRepresentation(request.headers.get("accept"));
45 if (representation === "html") return null;
46 headers.set("content-type", representation === "markdown" ? "text/markdown; charset=utf-8" : "text/plain; charset=utf-8");
47 headers.delete("content-length");
48 const body = representation === "none" ? "This page is available as text/html or text/markdown.\n" : status === 404
49 ? `# 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`
50 : `# g1t\n\ng1t is an open-source workspace for repositories, issues, 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`;
51 return new Response(request.method === "HEAD" ? null : body, { status: representation === "none" ? 406 : status, headers });
52}