g1t/apps/status/src/components.ts

179 lines5,758 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1/**
2 * The parts of g1t the page lists, and how each is checked. Every address
3 * comes from the Worker's vars, so an installation of its own names its own
4 * hosts; a part whose address is empty is not part of that installation
5 * and is left off the page.
6 *
7 * Checks go over the public internet, as people reach g1t, so they measure
8 * what people see. Billing has no public address of its own, so it is the
9 * one part checked through a service binding, when there is one.
10 */
11
12/** The Worker's vars that say where things are. */
13export type Targets = {
14 /** The site: `https://g1t.sh`. */
15 SITE_URL: string;
16 /** The API: `https://api.g1t.sh`. */
17 API_URL: string;
18 MCP_URL: string;
19 DOCS_URL: string;
20 /** Where deployed apps live: `https://g1t.page`. */
21 PAGES_URL: string;
22 MODELS_URL: string;
23 /** A public repository that always exists, `owner/name`, to fetch refs of. */
24 PROBE_REPO: string;
25 /** The site as browsers reach it, when the checks reach it another way. */
26 PUBLIC_SITE_URL?: string;
27};
28
29/** One request a check makes, and the answer that means the part works. */
30export type Step = {
31 url: string;
32 headers?: Record<string, string>;
33 /** The status that means it works; any 2xx when absent. */
34 expect?: number;
35};
36
37export type Check =
38 | { kind: "http"; steps: Step[] }
39 /** Billing, through its binding: reading its price book. */
40 | { kind: "billing" }
41 /** No check yet: the page says so rather than showing green. */
42 | { kind: "none" };
43
44export type ComponentInfo = {
45 key: string;
46 name: string;
47 address: string;
48 checks: string;
49 /** Whether g1t as a whole is down when this is. */
50 core: boolean;
51 check: Check;
52};
53
54/** A token no one holds: the API asks identity about it and refuses it. */
55export const NO_TOKEN = `g1t_${"0".repeat(40)}`;
56
57function host(url: string): string {
58 try {
59 return new URL(url).host;
60 } catch {
61 return url;
62 }
63}
64
65const trim = (url: string | undefined) => (url ?? "").trim().replace(/\/+$/, "");
66
67/** The parts, in the order the page lists them. */
68export function components(vars: Partial<Targets>, billing = true): ComponentInfo[] {
69 const site = trim(vars.SITE_URL);
70 const api = trim(vars.API_URL);
71 const mcp = trim(vars.MCP_URL);
72 const docs = trim(vars.DOCS_URL);
73 const pages = trim(vars.PAGES_URL);
74 const models = trim(vars.MODELS_URL);
75 const repo = (vars.PROBE_REPO ?? "").trim();
76 // What the page shows as the site's address: where people reach it.
77 const shown = host(trim(vars.PUBLIC_SITE_URL) || site);
78 const list: (ComponentInfo | null)[] = [
79 site
80 ? {
81 key: "site",
82 name: "Website and sign-in",
83 address: shown,
84 checks: api
85 ? "The sign-in page loading, and the account service answering an access token through the API."
86 : "The sign-in page loading.",
87 core: true,
88 check: {
89 kind: "http",
90 steps: [
91 { url: `${site}/login` },
92 ...(api ? [{ url: `${api}/v1/user`, headers: { authorization: `Bearer ${NO_TOKEN}` }, expect: 401 }] : []),
93 ],
94 },
95 }
96 : null,
97 api
98 ? {
99 key: "api",
100 name: "API",
101 address: host(api),
102 checks: "The API's front page.",
103 core: true,
104 check: { kind: "http", steps: [{ url: `${api}/` }] },
105 }
106 : null,
107 site && repo
108 ? {
109 key: "git",
110 name: "Git and repositories",
111 address: `${shown}/<owner>/<repo>.git`,
112 checks: "Listing the branches of a public repository over HTTPS, the first step of every clone.",
113 core: true,
114 check: { kind: "http", steps: [{ url: `${site}/${repo}.git/info/refs?service=git-upload-pack` }] },
115 }
116 : null,
117 mcp
118 ? {
119 key: "mcp",
120 name: "MCP server",
121 address: host(mcp),
122 checks: "The MCP server's description.",
123 core: false,
124 check: { kind: "http", steps: [{ url: `${mcp}/`, headers: { accept: "application/json" } }] },
125 }
126 : null,
127 docs
128 ? {
129 key: "docs",
130 name: "Documentation",
131 address: host(docs),
132 checks: "The documentation's front page.",
133 core: false,
134 check: { kind: "http", steps: [{ url: `${docs}/` }] },
135 }
136 : null,
137 pages
138 ? {
139 key: "deployments",
140 name: "Deployments",
141 address: `*.${host(pages)}`,
142 checks: `${host(pages)} answering. Each deployed app is not checked one by one.`,
143 core: false,
144 check: { kind: "http", steps: [{ url: `${pages}/` }] },
145 }
146 : null,
147 models
148 ? {
149 key: "agents",
150 name: "Agents' model proxy",
151 address: host(models),
152 checks: "The proxy agents reach their model through. The model providers behind it are not checked.",
153 core: false,
154 check: { kind: "http", steps: [{ url: `${models}/` }] },
155 }
156 : null,
157 models
158 ? {
159 key: "sandboxes",
160 name: "Sandboxes",
161 address: "Agents, checks, workflows and builds",
162 checks: "Starting a sandbox costs money and takes seconds, so it is not checked from here yet.",
163 core: false,
164 check: { kind: "none" },
165 }
166 : null,
167 billing && site
168 ? {
169 key: "billing",
170 name: "Billing",
171 address: `${shown}/<workspace>/-/billing`,
172 checks: "The billing service reading its price book. Stripe itself is not checked.",
173 core: false,
174 check: { kind: "billing" },
175 }
176 : null,
177 ];
178 return list.filter((c): c is ComponentInfo => c != null);
179}