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

456 lines22,501 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],
sudo: discounts listed among what was given away217 ["discounts", o.givenDiscountMicros ?? 0],
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 running218 ].filter(([, micros]) => (micros as number) > 0) as [string, number][];
219 const rows: { title: string; note: string; in: number | null; cost: number; result: number | null; tone?: "danger" | "warn" | "muted" }[] = [
220 {
221 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)222 note:
223 included > 0
sudo: the costs statement reads to the cent224 ? `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)225 : "What workspaces paid for usage, against what that usage cost",
226 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 running227 cost: usageCost,
228 result: usageMargin,
229 tone: usageMargin < 0 ? "danger" : undefined,
230 },
231 {
232 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)233 note:
234 included > 0
sudo: the costs statement reads to the cent235 ? `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)236 : "Plans, against Workers, D1, KV, Queues and the rest of the platform",
237 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 running238 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)239 result: o.plansMicros - included - running,
240 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 running241 },
242 {
243 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 & pricing244 note:
245 report.caps.fixedSource === "cloudflare"
246 ? `Fixed, as Cloudflare lists them${(report.caps.fixedItems ?? []).length > 0 ? ` (${(report.caps.fixedItems ?? []).map((i) => i.name).join(", ")})` : ""}; not on the usage bill`
247 : "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 running248 in: null,
249 cost: subscriptions,
250 result: -subscriptions,
251 tone: "muted",
252 },
253 ...(unmapped > 0
254 ? [{ title: "Not mapped", note: "Billed by Cloudflare, charged for by nothing yet", in: null, cost: unmapped, result: -unmapped, tone: "danger" as const }]
255 : []),
256 {
257 title: "Given away",
sudo: the costs statement reads to the cent258 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 running259 in: null,
260 cost: given,
261 result: null,
262 tone: "muted",
263 },
264 ];
265 const resultClass = (tone?: string) => (tone === "danger" ? "text-danger" : tone === "warn" ? "text-warn" : tone === "muted" ? "text-muted" : "text-fg");
266 return (
267 <>
268 <div className="mt-5 grid grid-cols-2 gap-3 lg:grid-cols-4">
269 <Stat
270 label="Margin on usage sold"
271 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 sold272 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 running273 tone={marginTone(usagePercent, floor)}
274 />
275 <Stat
276 label="Given away on purpose"
sudo: the costs statement reads to the cent277 value={usd(given, { cents: true })}
278 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 running279 />
280 <Stat
281 label="Who g1t paid"
sudo: the costs statement reads to the cent282 value={usd(o.costMicros, { cents: true })}
283 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 running284 />
285 <Stat
286 label="Proposals waiting"
287 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 & pricing288 hint={
289 <>
290 <Link to={costsHref("bill", range) + "#proposals"} className="underline underline-offset-2">
291 On Bill &amp; pricing
292 </Link>
293 {report.fetchedAt ? (
294 <>
295 {" "}
296 · bill read <When at={report.fetchedAt} time />
297 </>
298 ) : (
299 " · the bill has not been read yet"
300 )}
301 </>
302 }
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 running303 tone={proposals > 0 ? "warn" : undefined}
304 />
305 </div>
306 <div className="mt-3 overflow-x-auto rounded-lg border border-line bg-surface">
307 <table className="w-full min-w-[36rem] text-sm">
308 <thead>
309 <tr className="border-b border-line text-left text-xs text-muted">
310 <th className="px-4 py-2 font-medium sm:px-5">
311 {report.since} to {report.until}
312 </th>
313 <th className="px-4 py-2 text-right font-medium">Paid in</th>
314 <th className="px-4 py-2 text-right font-medium">Cost</th>
315 <th className="px-4 py-2 text-right font-medium sm:pr-5">Result</th>
316 </tr>
317 </thead>
318 <tbody>
319 {rows.map((row) => (
320 <tr key={row.title} className="border-b border-line">
321 <td className="px-4 py-2.5 sm:px-5">
322 <span className="text-fg">{row.title}</span>
323 <span className="block text-xs text-faint">{row.note}</span>
324 </td>
sudo: the costs statement reads to the cent325 <td className="tabular px-4 py-2.5 text-right">{row.in == null ? <span className="text-faint">—</span> : usd(row.in, { cents: true })}</td>
326 <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 running327 <td className={`tabular px-4 py-2.5 text-right sm:pr-5 ${resultClass(row.tone)}`}>
sudo: the costs statement reads to the cent328 {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 running329 </td>
330 </tr>
331 ))}
332 <tr>
333 <td className="px-4 py-2.5 sm:px-5">
334 <span className="font-medium text-fg">All in</span>
335 <span className="block text-xs text-faint">
sudo: the costs statement reads to the cent336 {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 running337 </span>
338 </td>
sudo: the costs statement reads to the cent339 <td className="tabular px-4 py-2.5 text-right font-medium">{usd(moneyIn, { cents: true })}</td>
340 <td className="tabular px-4 py-2.5 text-right font-medium">{usd(spent, { cents: true })}</td>
341 <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 running342 </tr>
343 </tbody>
344 </table>
345 </div>
346 </>
347 );
348}
349
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays350function SpendSection({ caps, error }: { caps: CostsReport["caps"]; error: string | null }) {
351 const { rows, totalMicros } = spendRows(caps);
352 const net = caps.revenueMicros - totalMicros;
353 return (
354 <Section
355 className="mt-6"
356 id="spend"
357 title="g1t's own spend"
358 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."
359 >
360 <div className="grid gap-3 lg:grid-cols-2">
361 <CapMeter
362 label={`Today, ${caps.day} (UTC)`}
363 used={caps.todayMicros}
364 cap={caps.dailyCapMicros}
365 hint={
366 caps.tripped ? (
367 <span className="text-danger">
368 Breaker open{caps.trippedAt ? <> since <When at={caps.trippedAt} time /></> : null}: new hosted-model runs g1t pays for wait until 00:00 UTC.
369 </span>
370 ) : caps.liftedBy ? (
371 <>
372 Lifted for today by {caps.liftedBy}
373 {caps.liftNote ? `: “${caps.liftNote}”` : ""}.
374 </>
375 ) : (
376 "Paying workspaces on a live card are never paused. PLATFORM_DAILY_SPEND_CAP_MICROS."
377 )
378 }
379 />
380 {caps.comped.map((b) => (
381 <CapMeter
382 key={b.account}
383 label={`${b.name}, ${caps.month} (comped)`}
384 used={b.usedMicros}
385 cap={b.ceilingMicros}
386 hint={
387 <>
388 {b.ceilingMicros > 0 && b.usedMicros >= b.ceilingMicros ? (
389 <span className="text-danger">Used up: new work on it is refused. </span>
390 ) : null}
391 {b.defaultCeiling ? "The default budget (COMPED_MONTHLY_CEILING_MICROS)" : "Its own budget, in its terms"}. Raise it on{" "}
392 <Link to={`/workspaces/${encodeURIComponent(b.name)}#billing`} className="underline underline-offset-2">
393 its account
394 </Link>
395 : Terms, Limit.
396 </>
397 }
398 />
399 ))}
400 </div>
401 {(caps.tripped || error) && (
402 <form method="post" action="#spend" className="mt-4 flex flex-col gap-2 sm:flex-row sm:items-end">
403 <input type="hidden" name="intent" value="lift" />
404 <Field label="Why lift it" hint="Recorded in the audit log.">
405 <Input name="note" required minLength={5} maxLength={500} placeholder="e.g. Launch day; watching it" className="sm:min-w-[24rem]" />
406 </Field>
407 <Button type="submit" variant="danger">
408 Lift for today
409 </Button>
410 </form>
411 )}
412 {error && (
413 <div className="mt-3">
414 <Notice tone="error">{error}</Notice>
415 </div>
416 )}
417 <div className="-mx-4 mt-5 overflow-x-auto sm:-mx-5">
418 <table className="w-full min-w-[34rem] text-sm">
419 <thead>
420 <tr className="border-b border-line text-left text-xs text-muted">
421 <th className="px-4 py-2 font-medium sm:px-5">{caps.month}, so far</th>
422 <th className="px-4 py-2 text-right font-medium sm:pr-5">g1t paid</th>
423 </tr>
424 </thead>
425 <tbody>
426 {rows.map((row) => (
427 <tr key={row.key} className="border-b border-line align-top">
428 <td className="px-4 py-2.5 sm:px-5">
429 {row.title}
430 <span className="block text-xs text-faint">{row.note}</span>
431 </td>
432 <td className="tabular px-4 py-2.5 text-right sm:pr-5">{usd(row.micros)}</td>
433 </tr>
434 ))}
435 <tr className="border-b border-line font-medium">
436 <td className="px-4 py-2.5 sm:px-5">All of it</td>
437 <td className="tabular px-4 py-2.5 text-right sm:pr-5">{usd(totalMicros)}</td>
438 </tr>
439 <tr className="border-b border-line">
440 <td className="px-4 py-2.5 sm:px-5">
441 Money in
442 <span className="block text-xs text-faint">Usage paid for and the plan, reconciled through yesterday</span>
443 </td>
444 <td className="tabular px-4 py-2.5 text-right sm:pr-5">{usd(caps.revenueMicros)}</td>
445 </tr>
446 <tr>
447 <td className="px-4 py-2.5 sm:px-5">Money in less what g1t paid</td>
448 <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>
449 </tr>
450 </tbody>
451 </table>
452 </div>
453 </Section>
454 );
455}
456