g1t/apps/sudo/app/routes/costs.tsx

455 lines22,454 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.

Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays1import type { ReactNode } from "react";
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing2import { Link } from "react-router";
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily3
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing4import type { CostsReport } from "@g1t/contracts";
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily5
6import type { Route } from "./+types/costs";
7import { DaysChart } from "~/components/costs";
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing8import { CostsHeader, chip, costsHref } from "~/components/costs-header";
9import { Badge, Button, Field, Input, Notice, Section, Stat, When } from "~/components/ui";
10import { capPercent, daySeries, marginTone, parseBucket, percentLabel, spendRows } from "~/lib/costs";
11import { type CostsActionResult, costsAction, costsLoader } from "~/lib/costs-route.server";
12import { usd } from "~/lib/money";
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily13
14export const meta: Route.MetaFunction = () => [{ title: "Costs & margin · sudo" }, { name: "robots", content: "noindex, nofollow" }];
15
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing16export const loader = ({ request, context }: Route.LoaderArgs) => costsLoader(request, context);
17export const action = ({ request, context }: Route.ActionArgs) => costsAction(request, context);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily18
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing19/**
20 * Where g1t's money goes: the statement, what g1t pays for itself against
21 * its caps, and the same by day, by product and by workspace.
22 */
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily23export default function Costs({ loaderData, actionData }: Route.ComponentProps) {
24 const { range, report, error, done } = loaderData;
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing25 const failed = actionData && "error" in actionData ? (actionData as CostsActionResult) : null;
26 const description =
27 "What g1t cost to run (Cloudflare's bill and the model providers') against what workspaces paid, with what g1t gave away on purpose kept apart. Prices are cost plus 20%; the bill itself, drift and the price book are on Bill & pricing.";
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily28 if (!report) {
29 return (
30 <main className="mx-auto max-w-6xl px-4 py-8 sm:py-10">
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing31 <CostsHeader page="costs" range={range} report={null} done={null} runError={null} description={description} />
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily32 <div className="mt-5">
33 <Notice tone="warn">Billing did not answer for costs: {error}</Notice>
34 </div>
35 </main>
36 );
37 }
38 const bucket = parseBucket(loaderData.bucket, report.products.map((p) => p.bucket));
39 const product = report.products.find((p) => p.bucket === bucket) ?? null;
40 const series = daySeries(report.days, report.since, report.until, bucket);
41 const floor = report.settings.marginFloorPercent;
42 const open = report.proposals.filter((p) => p.status === "open");
43 return (
44 <main className="mx-auto max-w-6xl px-4 py-8 sm:py-10">
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing45 <CostsHeader page="costs" range={range} report={report} done={done} runError={failed?.section === "run" ? failed.error : null} description={description} />
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily46
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running47 <Statement report={report} floor={floor} range={range} proposals={open.length} />
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily48
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays49 <SpendSection caps={report.caps} error={failed?.section === "lift" ? failed.error : null} />
50
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily51 <Section
52 className="mt-6"
53 title={product ? `${product.title}, by day` : "By day"}
54 description={
55 product
56 ? "What customers were charged for it at price, before included usage and pools paid part, against what it cost."
57 : "Money in (usage paid for and the plan) against every cost. Pick a product to see its own."
58 }
59 >
60 <nav aria-label="Product" className="mb-4 flex flex-wrap gap-2">
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing61 <Link to={costsHref("costs", range, null)} aria-current={!bucket ? "page" : undefined} className={chip(!bucket)}>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily62 All of g1t
63 </Link>
64 {report.products.map((p) => (
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing65 <Link key={p.bucket} to={costsHref("costs", range, p.bucket)} aria-current={p.bucket === bucket ? "page" : undefined} className={chip(p.bucket === bucket)}>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily66 {p.title}
67 </Link>
68 ))}
69 </nav>
70 <DaysChart
71 days={series}
72 floor={floor}
73 revenueLabel={product ? "Charged at price" : "Money in"}
74 label={product ? `${product.title}: charged against Cloudflare cost by day` : "Money in against Cloudflare cost by day"}
75 />
76 </Section>
77
78 <Section className="mt-6" title="By product" description={`Each of g1t's products over the last ${range} days. The floor is ${floor}%.`}>
79 <div className="-mx-4 overflow-x-auto sm:-mx-5">
80 <table className="w-full min-w-[44rem] text-sm">
81 <thead>
82 <tr className="border-b border-line text-left text-xs text-muted">
83 <th className="px-4 py-2 font-medium sm:px-5">Product</th>
84 <th className="px-4 py-2 text-right font-medium">Charged at price</th>
85 <th className="px-4 py-2 text-right font-medium">Cost</th>
86 <th className="px-4 py-2 text-right font-medium">Price book said</th>
87 <th className="px-4 py-2 text-right font-medium">Margin</th>
88 <th className="px-4 py-2 text-right font-medium sm:pr-5">%</th>
89 </tr>
90 </thead>
91 <tbody>
92 {report.products.map((p) => {
93 const tone = p.overhead ? undefined : marginTone(p.marginPercent, floor);
94 return (
95 <tr key={p.bucket} className="border-b border-line last:border-0">
96 <td className="px-4 py-2.5 sm:px-5">
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing97 <Link to={costsHref("costs", range, p.bucket)} className="text-fg hover:underline">
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily98 {p.title}
99 </Link>
100 <span className="block text-xs text-faint">
101 {p.overhead ? "Paid by the plan" : p.costSource === "ledger" ? "Cost from the gateway, not Cloudflare" : p.bucket}
102 </span>
103 </td>
104 <td className="tabular px-4 py-2.5 text-right">{usd(p.valueMicros)}</td>
105 <td className="tabular px-4 py-2.5 text-right">{usd(p.costMicros)}</td>
106 <td className="tabular px-4 py-2.5 text-right text-muted">{usd(p.ownCostMicros)}</td>
107 <td className={`tabular px-4 py-2.5 text-right ${p.marginMicros < 0 && !p.overhead ? "text-danger" : ""}`}>{usd(p.marginMicros)}</td>
108 <td className="px-4 py-2.5 text-right sm:pr-5">
109 {p.overhead ? (
110 <span className="text-faint">—</span>
111 ) : (
112 <Badge tone={tone === "mint" ? "mint" : tone === "warn" ? "warn" : tone === "danger" ? "danger" : "plain"}>{percentLabel(p.marginPercent)}</Badge>
113 )}
114 </td>
115 </tr>
116 );
117 })}
118 </tbody>
119 </table>
120 </div>
121 </Section>
122
123 <Section
124 className="mt-6"
125 title="Workspaces that cost most"
126 description="Cloudflare's costs shared out by each workspace's own meters (its git operations, its usage), against what it paid. Over its revenue for 30 days, a workspace shows on Reach out."
127 >
128 {report.topWorkspaces.length === 0 ? (
129 <p className="text-sm text-muted">No costs shared out yet.</p>
130 ) : (
131 <div className="-mx-4 overflow-x-auto sm:-mx-5">
132 <table className="w-full min-w-[34rem] text-sm">
133 <thead>
134 <tr className="border-b border-line text-left text-xs text-muted">
135 <th className="px-4 py-2 font-medium sm:px-5">Workspace</th>
136 <th className="px-4 py-2 text-right font-medium">Cost to g1t</th>
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it137 <th className="px-4 py-2 text-right font-medium">Given away</th>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily138 <th className="px-4 py-2 text-right font-medium">Paid</th>
139 <th className="px-4 py-2 text-right font-medium sm:pr-5">Net</th>
140 </tr>
141 </thead>
142 <tbody>
143 {report.topWorkspaces.map((w) => {
144 const net = w.revenueMicros - w.costMicros;
145 return (
146 <tr key={w.workspace} className="border-b border-line last:border-0">
147 <td className="px-4 py-2.5 sm:px-5">
148 <Link to={`/workspaces/${encodeURIComponent(w.workspace)}#billing`} className="font-mono text-fg hover:underline">
149 {w.workspace}
150 </Link>
151 {w.internal && <span className="ml-2 text-xs text-faint">g1t's own</span>}
152 </td>
153 <td className="tabular px-4 py-2.5 text-right">{usd(w.costMicros)}</td>
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it154 <td className="tabular px-4 py-2.5 text-right text-fg-soft">{usd(w.givenMicros ?? 0)}</td>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily155 <td className="tabular px-4 py-2.5 text-right">{usd(w.revenueMicros)}</td>
156 <td className={`tabular px-4 py-2.5 text-right sm:pr-5 ${net < 0 && !w.internal ? "text-danger" : "text-fg-soft"}`}>{usd(net)}</td>
157 </tr>
158 );
159 })}
160 </tbody>
161 </table>
162 </div>
163 )}
164 </Section>
165
166 </main>
167 );
168}
169
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays170function CapMeter({ label, used, cap, hint }: { label: string; used: number; cap: number; hint: ReactNode }) {
171 const percent = capPercent(used, cap);
172 const tone = cap > 0 && used >= cap ? "bg-danger" : percent >= 75 ? "bg-warn" : "bg-merged";
173 return (
174 <div className="rounded-md border border-line px-4 py-3">
175 <div className="flex flex-wrap items-baseline justify-between gap-2">
176 <span className="text-sm font-medium">{label}</span>
177 <span className="tabular text-sm">
178 {usd(used)} <span className="text-faint">of {cap > 0 ? usd(cap) : "no cap"}</span>
179 </span>
180 </div>
181 <div className="mt-2 h-1.5 overflow-hidden rounded-full bg-line" role="meter" aria-valuemin={0} aria-valuemax={100} aria-valuenow={Math.round(percent)} aria-label={label}>
182 <div className={`h-full ${tone}`} style={{ width: `${percent}%` }} />
183 </div>
184 <p className="mt-1.5 text-xs text-muted">{hint}</p>
185 </div>
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running186 );
187}
188
189/**
190 * Where the money went, as a short statement: usage sold and running g1t
191 * (each against what paid for it), what g1t gave away on purpose (a budget,
192 * never a loss), and who was paid.
193 */
194function Statement({ report, floor, range, proposals }: { report: CostsReport; floor: number; range: number; proposals: number }) {
195 const o = report.overall;
196 // Reports from before the statement have only the totals.
197 const given = o.givenMicros ?? 0;
198 const usageCost = o.usageCostMicros ?? o.costMicros - given;
Costs: the plan's included usage counts as paid for the usage it covered, out of what plans leave for running g1t; the run button shows it is running with CSS alone (sudo ships no JavaScript)199 const included = o.includedMicros ?? 0;
200 const usageIn = o.usageMicros + included;
201 const usageMargin = o.usageMarginMicros ?? usageIn - usageCost;
sudo: a billing reset runs the costs analysis again so every figure is fresh; every submit button shows it is working (CSS only); no margin percentage on less than a cent sold202 // Under a cent sold, a percentage says nothing (a few micros against none).
203 const soldSomething = usageIn >= 10_000;
204 const usagePercent = soldSomething && o.usageMarginPercent !== undefined ? o.usageMarginPercent : null;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running205 const running = o.runningCostMicros ?? 0;
206 const unmapped = o.unmappedCostMicros ?? 0;
207 // Cloudflare's subscriptions are not on the usage bill: the estimate, over the range.
208 const subscriptions = Math.round((report.caps.fixedMonthlyMicros * range) / 30);
209 const moneyIn = o.usageMicros + o.plansMicros;
210 const spent = o.costMicros + subscriptions;
211 const net = moneyIn - spent;
212 const givenParts = [
213 ["comped", o.givenCompedMicros ?? 0],
214 ["free use", o.givenFreeMicros ?? 0],
215 ["trial", o.givenTrialMicros ?? 0],
216 ["open-source pool", o.givenPoolMicros ?? 0],
217 ].filter(([, micros]) => (micros as number) > 0) as [string, number][];
218 const rows: { title: string; note: string; in: number | null; cost: number; result: number | null; tone?: "danger" | "warn" | "muted" }[] = [
219 {
220 title: "Usage sold",
Costs: the plan's included usage counts as paid for the usage it covered, out of what plans leave for running g1t; the run button shows it is running with CSS alone (sudo ships no JavaScript)221 note:
222 included > 0
sudo: the costs statement reads to the cent223 ? `What workspaces paid for usage (${usd(o.usageMicros, { cents: true })}) and their plan's included usage (${usd(included, { cents: true })}), against what it cost`
Costs: the plan's included usage counts as paid for the usage it covered, out of what plans leave for running g1t; the run button shows it is running with CSS alone (sudo ships no JavaScript)224 : "What workspaces paid for usage, against what that usage cost",
225 in: usageIn,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running226 cost: usageCost,
227 result: usageMargin,
228 tone: usageMargin < 0 ? "danger" : undefined,
229 },
230 {
231 title: "Running g1t",
Costs: the plan's included usage counts as paid for the usage it covered, out of what plans leave for running g1t; the run button shows it is running with CSS alone (sudo ships no JavaScript)232 note:
233 included > 0
sudo: the costs statement reads to the cent234 ? `Plans (${usd(o.plansMicros, { cents: true })}) less the included usage they paid for, against Workers, D1, KV, Queues and the rest`
Costs: the plan's included usage counts as paid for the usage it covered, out of what plans leave for running g1t; the run button shows it is running with CSS alone (sudo ships no JavaScript)235 : "Plans, against Workers, D1, KV, Queues and the rest of the platform",
236 in: o.plansMicros - included,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running237 cost: running,
Costs: the plan's included usage counts as paid for the usage it covered, out of what plans leave for running g1t; the run button shows it is running with CSS alone (sudo ships no JavaScript)238 result: o.plansMicros - included - running,
239 tone: o.plansMicros - included - running < 0 ? "warn" : undefined,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running240 },
241 {
242 title: "Cloudflare subscriptions",
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing243 note:
244 report.caps.fixedSource === "cloudflare"
245 ? `Fixed, as Cloudflare lists them${(report.caps.fixedItems ?? []).length > 0 ? ` (${(report.caps.fixedItems ?? []).map((i) => i.name).join(", ")})` : ""}; not on the usage bill`
246 : "Fixed, an estimate (CLOUDFLARE_FIXED_MONTHLY_MICROS) until Cloudflare's list is read; not on the usage bill",
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running247 in: null,
248 cost: subscriptions,
249 result: -subscriptions,
250 tone: "muted",
251 },
252 ...(unmapped > 0
253 ? [{ title: "Not mapped", note: "Billed by Cloudflare, charged for by nothing yet", in: null, cost: unmapped, result: -unmapped, tone: "danger" as const }]
254 : []),
255 {
256 title: "Given away",
sudo: the costs statement reads to the cent257 note: givenParts.length > 0 ? givenParts.map(([why, micros]) => `${why} ${usd(micros, { cents: true })}`).join(" · ") : "Nothing this range",
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running258 in: null,
259 cost: given,
260 result: null,
261 tone: "muted",
262 },
263 ];
264 const resultClass = (tone?: string) => (tone === "danger" ? "text-danger" : tone === "warn" ? "text-warn" : tone === "muted" ? "text-muted" : "text-fg");
265 return (
266 <>
267 <div className="mt-5 grid grid-cols-2 gap-3 lg:grid-cols-4">
268 <Stat
269 label="Margin on usage sold"
270 value={percentLabel(usagePercent)}
sudo: a billing reset runs the costs analysis again so every figure is fresh; every submit button shows it is working (CSS only); no margin percentage on less than a cent sold271 hint={soldSomething ? `${usd(usageIn, { cents: true })} paid for usage that cost ${usd(usageCost, { cents: true })}` : "No usage sold in this range"}
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running272 tone={marginTone(usagePercent, floor)}
273 />
274 <Stat
275 label="Given away on purpose"
sudo: the costs statement reads to the cent276 value={usd(given, { cents: true })}
277 hint={givenParts.length > 0 ? givenParts.map(([why, micros]) => `${usd(micros, { cents: true })} ${why}`).join(", ") : "Nothing given this range"}
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running278 />
279 <Stat
280 label="Who g1t paid"
sudo: the costs statement reads to the cent281 value={usd(o.costMicros, { cents: true })}
282 hint={`Cloudflare ${usd(o.cloudflareCostMicros ?? o.costMicros - (o.modelsCostMicros ?? 0), { cents: true })}, model providers ${usd(o.modelsCostMicros ?? 0, { cents: true })}`}
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running283 />
284 <Stat
285 label="Proposals waiting"
286 value={String(proposals)}
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing287 hint={
288 <>
289 <Link to={costsHref("bill", range) + "#proposals"} className="underline underline-offset-2">
290 On Bill &amp; pricing
291 </Link>
292 {report.fetchedAt ? (
293 <>
294 {" "}
295 · bill read <When at={report.fetchedAt} time />
296 </>
297 ) : (
298 " · the bill has not been read yet"
299 )}
300 </>
301 }
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running302 tone={proposals > 0 ? "warn" : undefined}
303 />
304 </div>
305 <div className="mt-3 overflow-x-auto rounded-lg border border-line bg-surface">
306 <table className="w-full min-w-[36rem] text-sm">
307 <thead>
308 <tr className="border-b border-line text-left text-xs text-muted">
309 <th className="px-4 py-2 font-medium sm:px-5">
310 {report.since} to {report.until}
311 </th>
312 <th className="px-4 py-2 text-right font-medium">Paid in</th>
313 <th className="px-4 py-2 text-right font-medium">Cost</th>
314 <th className="px-4 py-2 text-right font-medium sm:pr-5">Result</th>
315 </tr>
316 </thead>
317 <tbody>
318 {rows.map((row) => (
319 <tr key={row.title} className="border-b border-line">
320 <td className="px-4 py-2.5 sm:px-5">
321 <span className="text-fg">{row.title}</span>
322 <span className="block text-xs text-faint">{row.note}</span>
323 </td>
sudo: the costs statement reads to the cent324 <td className="tabular px-4 py-2.5 text-right">{row.in == null ? <span className="text-faint">—</span> : usd(row.in, { cents: true })}</td>
325 <td className="tabular px-4 py-2.5 text-right">{usd(row.cost, { cents: true })}</td>
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running326 <td className={`tabular px-4 py-2.5 text-right sm:pr-5 ${resultClass(row.tone)}`}>
sudo: the costs statement reads to the cent327 {row.result == null ? "a budget" : usd(row.result, { signed: true, cents: true })}
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running328 </td>
329 </tr>
330 ))}
331 <tr>
332 <td className="px-4 py-2.5 sm:px-5">
333 <span className="font-medium text-fg">All in</span>
334 <span className="block text-xs text-faint">
sudo: the costs statement reads to the cent335 {given > 0 ? `${usd(given, { cents: true })} of the cost was given away on purpose; without it, ${usd(net + given, { signed: true, cents: true })}` : "Everything above"}
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running336 </span>
337 </td>
sudo: the costs statement reads to the cent338 <td className="tabular px-4 py-2.5 text-right font-medium">{usd(moneyIn, { cents: true })}</td>
339 <td className="tabular px-4 py-2.5 text-right font-medium">{usd(spent, { cents: true })}</td>
340 <td className="tabular px-4 py-2.5 text-right font-medium text-fg sm:pr-5">{usd(net, { signed: true, cents: true })}</td>
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running341 </tr>
342 </tbody>
343 </table>
344 </div>
345 </>
346 );
347}
348
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays349function SpendSection({ caps, error }: { caps: CostsReport["caps"]; error: string | null }) {
350 const { rows, totalMicros } = spendRows(caps);
351 const net = caps.revenueMicros - totalMicros;
352 return (
353 <Section
354 className="mt-6"
355 id="spend"
356 title="g1t's own spend"
357 description="What g1t pays for itself, at cost: comped accounts, the trial and open-source pools, free workspaces' overruns, and anything charged without real money behind it. Two caps hold it: each comped account's monthly budget, and a daily breaker on all of it that pauses new hosted-model agent runs g1t would pay for."
358 >
359 <div className="grid gap-3 lg:grid-cols-2">
360 <CapMeter
361 label={`Today, ${caps.day} (UTC)`}
362 used={caps.todayMicros}
363 cap={caps.dailyCapMicros}
364 hint={
365 caps.tripped ? (
366 <span className="text-danger">
367 Breaker open{caps.trippedAt ? <> since <When at={caps.trippedAt} time /></> : null}: new hosted-model runs g1t pays for wait until 00:00 UTC.
368 </span>
369 ) : caps.liftedBy ? (
370 <>
371 Lifted for today by {caps.liftedBy}
372 {caps.liftNote ? `: “${caps.liftNote}”` : ""}.
373 </>
374 ) : (
375 "Paying workspaces on a live card are never paused. PLATFORM_DAILY_SPEND_CAP_MICROS."
376 )
377 }
378 />
379 {caps.comped.map((b) => (
380 <CapMeter
381 key={b.account}
382 label={`${b.name}, ${caps.month} (comped)`}
383 used={b.usedMicros}
384 cap={b.ceilingMicros}
385 hint={
386 <>
387 {b.ceilingMicros > 0 && b.usedMicros >= b.ceilingMicros ? (
388 <span className="text-danger">Used up: new work on it is refused. </span>
389 ) : null}
390 {b.defaultCeiling ? "The default budget (COMPED_MONTHLY_CEILING_MICROS)" : "Its own budget, in its terms"}. Raise it on{" "}
391 <Link to={`/workspaces/${encodeURIComponent(b.name)}#billing`} className="underline underline-offset-2">
392 its account
393 </Link>
394 : Terms, Limit.
395 </>
396 }
397 />
398 ))}
399 </div>
400 {(caps.tripped || error) && (
401 <form method="post" action="#spend" className="mt-4 flex flex-col gap-2 sm:flex-row sm:items-end">
402 <input type="hidden" name="intent" value="lift" />
403 <Field label="Why lift it" hint="Recorded in the audit log.">
404 <Input name="note" required minLength={5} maxLength={500} placeholder="e.g. Launch day; watching it" className="sm:min-w-[24rem]" />
405 </Field>
406 <Button type="submit" variant="danger">
407 Lift for today
408 </Button>
409 </form>
410 )}
411 {error && (
412 <div className="mt-3">
413 <Notice tone="error">{error}</Notice>
414 </div>
415 )}
416 <div className="-mx-4 mt-5 overflow-x-auto sm:-mx-5">
417 <table className="w-full min-w-[34rem] text-sm">
418 <thead>
419 <tr className="border-b border-line text-left text-xs text-muted">
420 <th className="px-4 py-2 font-medium sm:px-5">{caps.month}, so far</th>
421 <th className="px-4 py-2 text-right font-medium sm:pr-5">g1t paid</th>
422 </tr>
423 </thead>
424 <tbody>
425 {rows.map((row) => (
426 <tr key={row.key} className="border-b border-line align-top">
427 <td className="px-4 py-2.5 sm:px-5">
428 {row.title}
429 <span className="block text-xs text-faint">{row.note}</span>
430 </td>
431 <td className="tabular px-4 py-2.5 text-right sm:pr-5">{usd(row.micros)}</td>
432 </tr>
433 ))}
434 <tr className="border-b border-line font-medium">
435 <td className="px-4 py-2.5 sm:px-5">All of it</td>
436 <td className="tabular px-4 py-2.5 text-right sm:pr-5">{usd(totalMicros)}</td>
437 </tr>
438 <tr className="border-b border-line">
439 <td className="px-4 py-2.5 sm:px-5">
440 Money in
441 <span className="block text-xs text-faint">Usage paid for and the plan, reconciled through yesterday</span>
442 </td>
443 <td className="tabular px-4 py-2.5 text-right sm:pr-5">{usd(caps.revenueMicros)}</td>
444 </tr>
445 <tr>
446 <td className="px-4 py-2.5 sm:px-5">Money in less what g1t paid</td>
447 <td className={`tabular px-4 py-2.5 text-right sm:pr-5 ${net < 0 ? "text-danger" : "text-fg-soft"}`}>{usd(net, { signed: true })}</td>
448 </tr>
449 </tbody>
450 </table>
451 </div>
452 </Section>
453 );
454}
455