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.

g1t.sh
121 linesCodeBlameRaw

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.

Usage, Billing settings and prepaid AI credit; fixes from the UX audit1import assert from "node:assert/strict";
2import { test } from "node:test";
3
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.4import { money } from "./money.ts";
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.5import { byProject, columns, defaultGrain, isStaff, monthSpan, offeredProducts, pendingSentence, productStyle, quantity, receipt, resolveRange, ticks, usageCsv } from "./usage.ts";
Usage, Billing settings and prepaid AI credit; fixes from the UX audit6
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.7const axisMoney = (micros: number) => money(micros, { compact: true });
8
Usage, Billing settings and prepaid AI credit; fixes from the UX audit9const NOW = new Date("2026-10-08T15:00:00Z");
10
11test("periods resolve to whole UTC days, both ends included", () => {
12 assert.deepEqual(resolveRange(null, {}, NOW), { period: "cycle", from: "2026-10-01", until: "2026-10-08" });
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.13 // The current cycle is the one "this month" Home asks billing for.
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.14 assert.deepEqual(resolveRange("cycle", {}, NOW), { period: "cycle", ...monthSpan(NOW) });
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.15 assert.deepEqual(monthSpan(NOW), { from: "2026-10-01", until: "2026-10-08" });
Usage, Billing settings and prepaid AI credit; fixes from the UX audit16 assert.deepEqual(resolveRange("last_cycle", {}, NOW), { period: "last_cycle", from: "2026-09-01", until: "2026-09-30" });
17 assert.deepEqual(resolveRange("7d", {}, NOW), { period: "7d", from: "2026-10-02", until: "2026-10-08" });
18 assert.deepEqual(resolveRange("custom", { from: "2026-10-05", until: "2026-09-20" }, NOW), { period: "custom", from: "2026-09-20", until: "2026-10-05" });
19 // A custom range that is not days falls back to the cycle.
20 assert.equal(resolveRange("custom", { from: "yesterday", until: "2026-10-01" }, NOW).period, "cycle");
21 assert.equal(resolveRange("nonsense", {}, NOW).period, "cycle");
22});
23
24test("columns cover every day, stack products, and add up when cumulative", () => {
25 const days = [
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.26 { day: "2026-10-01", product: "deployments", micros: 2_000_000 },
Usage, Billing settings and prepaid AI credit; fixes from the UX audit27 { day: "2026-10-01", product: "sandboxes", micros: 500_000 },
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.28 { day: "2026-10-03", product: "deployments", micros: 1_000_000 },
Usage, Billing settings and prepaid AI credit; fixes from the UX audit29 ];
30 const daily = columns(days, "2026-10-01", "2026-10-03");
31 assert.deepEqual(daily.map((c) => c.total), [2_500_000, 0, 1_000_000]);
32 assert.equal(daily[0]!.parts.sandboxes, 500_000);
33 const running = columns(days, "2026-10-01", "2026-10-03", "day", true);
34 assert.deepEqual(running.map((c) => c.total), [2_500_000, 2_500_000, 3_500_000]);
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.35 assert.equal(running[2]!.parts.deployments, 3_000_000);
Usage, Billing settings and prepaid AI credit; fixes from the UX audit36 // Weeks start on Monday: Oct 1 2026 is a Thursday.
37 const weekly = columns(days, "2026-10-01", "2026-10-08", "week");
38 assert.deepEqual(weekly.map((c) => c.key), ["2026-09-28", "2026-10-05"]);
39 assert.equal(weekly[0]!.total, 3_500_000);
40 assert.equal(columns(days, "2026-09-15", "2026-10-03", "month").length, 2);
41 assert.equal(defaultGrain("2026-10-01", "2026-10-31"), "day");
42 assert.equal(defaultGrain("2026-07-01", "2026-10-01"), "week");
43});
44
45test("the money axis has clean ticks, never the same label twice", () => {
46 assert.deepEqual(ticks(0), [0]);
47 const t = ticks(3_320_000);
48 assert.equal(t[0], 0);
49 assert.ok(t[t.length - 1]! >= 3_320_000);
50 const labels = t.map(axisMoney);
51 assert.equal(new Set(labels).size, labels.length, labels.join(" "));
52 const small = ticks(4_000).map(axisMoney);
53 assert.equal(new Set(small).size, small.length, small.join(" "));
54 assert.equal(axisMoney(2_000_000), "$2");
55 assert.equal(axisMoney(1_500_000_000), "$1.5K");
56});
57
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.58test("quantities read in their units", () => {
Usage, Billing settings and prepaid AI credit; fixes from the UX audit59 assert.equal(quantity(1_234_567, "tokens"), "1.2M tokens");
60 assert.equal(quantity(3_725, "seconds"), "1h 2m");
61 assert.equal(quantity(504_000_000, "bytes"), "504 MB");
62 assert.equal(quantity(1, "entries"), "1 entry");
63});
64
65test("the receipt shows usage at price, then what paid, then what is charged", () => {
66 const lines = receipt(
67 { priceMicros: 3_320_000, discountMicros: 3_320_000, includedMicros: 0, creditsMicros: 0, chargedMicros: 0, pendingMicros: 0, costMicros: 0 },
68 100,
69 );
70 assert.deepEqual(lines.map((l) => l.label), ["Usage at price", "Discount (100%)", "Charged"]);
71});
72
73test("grouping by project adds a project's meters together", () => {
74 const meter = (label: string, parts: [string, number][]) => ({
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.75 key: label, label, product: "deployments", unit: "entries", quantity: 0, micros: 0, daily: [],
Usage, Billing settings and prepaid AI credit; fixes from the UX audit76 byProject: parts.map(([project, micros]) => ({ project, micros, quantity: 0 })),
77 });
78 const rows = byProject({
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.79 products: [{ key: "deployments", label: "Deployments", micros: 0, meters: [meter("Requests", [["acme/web", 300], ["acme/api", 500]]), meter("Domains", [["acme/web", 400]])] }],
Usage, Billing settings and prepaid AI credit; fixes from the UX audit80 });
81 assert.deepEqual(rows.map((r) => [r.project, r.micros]), [["acme/web", 700], ["acme/api", 500]]);
82});
83
84test("the CSV has a row per day and meter with anything on it", () => {
85 const csv = usageCsv({
86 from: "2026-10-01",
87 until: "2026-10-02",
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.88 products: [{ key: "sandboxes", label: "Sandboxes", micros: 0, meters: [{ key: "sandbox_time", label: "Sandbox time", product: "sandboxes", unit: "seconds", quantity: 0, micros: 0, daily: [0, 1_500_000], byProject: [] }] }],
Usage, Billing settings and prepaid AI credit; fixes from the UX audit89 });
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.90 assert.equal(csv, "day,product,meter,usd\n2026-10-02,Sandboxes,Sandbox time,1.500000\n");
Usage, Billing settings and prepaid AI credit; fixes from the UX audit91});
92
93test("test-mode hints are for g1t's own people", () => {
94 assert.ok(isStaff({ workspaces: [{ slug: "flagon-io" }] }));
95 assert.ok(!isStaff({ workspaces: [{ slug: "acme" }] }));
96 assert.ok(!isStaff(null));
97});
Sidebar: soon rows and the project row keep their styles inside a hint; tiny amounts read as zero98
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.99test("the pending note says what the close will charge, with the fraction of a cent that explains a gap", () => {
100 // A comped workspace: $0.009 pending, nothing of it charged.
101 assert.equal(
102 pendingSentence({ pendingMicros: 9_000, pendingChargedMicros: 0 }),
103 "$0.009 of it is metered this month and not yet closed; nothing will be charged when the month closes, after your discount and included usage.",
104 );
105 assert.equal(
106 pendingSentence({ pendingMicros: 9_000, pendingChargedMicros: 6_300 }),
107 "$0.009 of it is metered this month and not yet closed; $0.0063 of it will be charged when the month closes, after your discount and included usage.",
108 );
109 assert.equal(
110 pendingSentence({ pendingMicros: 1_340_000, pendingChargedMicros: 1_340_000 }),
111 "$1.34 of it is metered this month and not yet closed; all of it will be charged when the month closes, after your discount and included usage.",
112 );
113 // An older billing, with no split yet.
114 assert.equal(pendingSentence({ pendingMicros: 9_000 }), "$0.009 of it is metered this month and not yet closed; it is charged when the month closes.");
Sidebar: soon rows and the project row keep their styles inside a hint; tiny amounts read as zero115});
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.116
117test("products no longer offered drop out of the filters but still read in older usage", () => {
118 assert.deepEqual(offeredProducts([{ key: "agent" }, { key: "sandboxes" }, { key: "gateway" }]).map((p) => p.key), ["sandboxes"]);
119 assert.equal(productStyle("agent").label, "Agent");
120 assert.equal(productStyle("sandboxes").label, "Sandboxes");
121});