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.

87 linesCodeBlameRaw
1// Read-only production verification. --mcp uses the existing CLI login without logging it.
2// Run: node --use-system-ca scripts/check-agent-readiness.mjs [--mcp]
3import assert from "node:assert/strict";
4import { execFileSync } from "node:child_process";
5
6let failures = 0;
7async function check(name, run) {
8 try { await run(); console.log(`PASS ${name}`); }
9 catch (error) { failures++; console.error(`FAIL ${name}: ${error.message}`); }
10}
11async function get(url, headers = {}) {
12 const response = await fetch(url, { headers, signal: AbortSignal.timeout(20_000) });
13 return { response, body: await response.text() };
14}
15const varies = (response) => (response.headers.get("vary") ?? "").toLowerCase().split(",").map((v) => v.trim()).includes("accept");
16for (const [path, status] of [["/", 200], ["/__agent-readiness-nonexistent", 404]]) {
17 for (const accept of ["text/markdown", "text/html", "text/html;q=0.2,text/markdown;q=1"]) {
18 await check(`${path} Accept: ${accept}`, async () => {
19 const { response, body } = await get(`https://g1t.sh${path}`, { accept });
20 assert.equal(response.status, status);
21 assert.ok(varies(response), "Vary must contain Accept");
22 const markdown = accept !== "text/html";
23 assert.ok(response.headers.get("content-type")?.startsWith(markdown ? "text/markdown" : "text/html"));
24 if (markdown) { assert.ok(body.length > 20); assert.match(body, /\[.*\]\(https:\/\/g1t.sh\/llms.txt\)/); }
25 else assert.match(body, /<html/);
26 });
27 }
28}
29await check("unsupported homepage representation", async () => assert.equal((await get("https://g1t.sh/", { accept: "application/json" })).response.status, 406));
30await check("homepage metadata", async () => {
31 const { body } = await get("https://g1t.sh/", { accept: "text/html" });
32 assert.match(body, /<html[^>]*lang="en"/);
33 assert.match(body, /<link[^>]*rel="canonical"[^>]*href="https:\/\/g1t.sh\/"/);
34 assert.match(body, /property="og:image"/);
35 assert.match(body, /property="og:type"/);
36 const schemas = [...body.matchAll(/<script[^>]*type="application\/ld\+json"[^>]*>([\s\S]*?)<\/script>/g)].map((m) => JSON.parse(m[1]));
37 const organization = schemas.find((schema) => schema["@type"] === "Organization");
38 assert.equal(organization?.contactPoint?.email, "hey@flagon.io");
39});
40await check("llms.txt instructions and developer links", async () => {
41 const { response, body } = await get("https://g1t.sh/llms.txt");
42 assert.equal(response.status, 200);
43 assert.match(body, /^# g1t\s/m);
44 assert.match(body, /## When to use g1t/);
45 for (const url of ["https://api.g1t.sh/openapi.json", "https://mcp.g1t.sh/.well-known/oauth-protected-resource", "https://g1t.sh/settings/tokens"]) assert.ok(body.includes(url));
46});
47await check("OpenAPI scopes and unprefixed versioned API", async () => {
48 const { body, response } = await get("https://api.g1t.sh/openapi.json");
49 assert.equal(response.status, 200);
50 const spec = JSON.parse(body);
51 assert.equal(spec.servers[0].url, "https://api.g1t.sh");
52 const scopes = spec.components.securitySchemes.oauth.flows.authorizationCode.scopes;
53 const metadata = JSON.parse((await get("https://mcp.g1t.sh/.well-known/oauth-protected-resource")).body);
54 assert.deepEqual(Object.keys(scopes).sort(), metadata.scopes_supported.sort());
55 assert.equal(spec.components.parameters.ApiVersion.name, "X-G1t-Api-Version");
56 assert.ok(spec.components.responses.RateLimited.headers["Retry-After"]);
57});
58for (const path of ["/repos", "/v1/repos"]) await check(`REST ${path} version and limit headers`, async () => {
59 const { response, body } = await get(`https://api.g1t.sh${path}`, { "X-G1t-Api-Version": "1" });
60 assert.equal(response.status, 200);
61 assert.ok(Array.isArray(JSON.parse(body)));
62 assert.equal(response.headers.get("x-g1t-api-version"), "1");
63 assert.equal(response.headers.get("ratelimit-policy"), '"anonymous";q=60;w=60');
64});
65await check("unsupported REST version fails", async () => assert.equal((await get("https://api.g1t.sh/repos", { "X-G1t-Api-Version": "not-a-version" })).response.status, 422));
66for (const url of ["https://g1t.sh/developers", "https://g1t.sh/openapi.json", "https://g1t.sh/mcp", "https://g1t.sh/sitemap.xml", "https://g1t.sh/robots.txt", "https://docs.g1t.sh/reference/api/", "https://docs.g1t.sh/reference/rate-limits/", "https://docs.g1t.sh/guides/authentication/", "https://docs.g1t.sh/guides/cli/", "https://docs.g1t.sh/guides/self-hosting/", "https://g1t.sh/downloads/cli/latest.json"]) {
67 await check(url, async () => { const { response, body } = await get(url); assert.equal(response.status, 200); assert.ok(body.length > 20); });
68}
69await check("published versioning documentation", async () => assert.match((await get("https://docs.g1t.sh/reference/api/")).body, /X-G1t-Api-Version/));
70await check("MCP still requires authorization", async () => {
71 const response = await fetch("https://mcp.g1t.sh", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list", params: {} }), signal: AbortSignal.timeout(20_000) });
72 assert.equal(response.status, 401);
73 assert.match(response.headers.get("www-authenticate") ?? "", /resource_metadata/);
74 await response.body?.cancel();
75});
76if (process.argv.includes("--mcp")) await check("authorized MCP initialize and tools/list", async () => {
77 const token = execFileSync("g1t", ["auth", "token", "--hostname", "g1t.sh"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
78 for (const [id, method, params] of [[1, "initialize", { protocolVersion: "2025-03-26", capabilities: {}, clientInfo: { name: "g1t-readiness-check", version: "1" } }], [2, "tools/list", {}]]) {
79 const response = await fetch("https://mcp.g1t.sh", { method: "POST", headers: { authorization: `Bearer ${token}`, "content-type": "application/json", accept: "application/json, text/event-stream" }, body: JSON.stringify({ jsonrpc: "2.0", id, method, params }), signal: AbortSignal.timeout(20_000) });
80 assert.equal(response.status, 200);
81 const result = await response.json();
82 assert.ok(!result.error, "MCP returned a protocol error");
83 if (method === "tools/list") assert.ok(result.result.tools.length > 0);
84 else assert.ok(result.result.protocolVersion);
85 }
86});
87process.exitCode = failures ? 1 : 0;