g1t/packages/contracts/src/billing.ts

129 lines4,753 bytesCodeBlame
1import type { User, Viewer } from "./identity";
2import type { RepoPath } from "./repos";
3import type { Result } from "./result";
4
5/** Millionths of a US dollar in one dollar: the unit money is held in. */
6export const MICROS_PER_DOLLAR = 1_000_000;
7
8/** Whether workspaces are charged for agents at all, and with real money. */
9export type BillingStatus = {
10 /**
11 * False when no card processor is configured: nothing is charged, and who
12 * may run agents is decided some other way.
13 */
14 enabled: boolean;
15 /** False while the card processor is in its test mode, where cards are not real. */
16 live: boolean;
17 /**
18 * True while g1t is being built out: runs are recorded with what they
19 * cost, but nothing is charged and no credit is needed. Not forever.
20 */
21 free?: boolean;
22};
23
24/** A workspace's standing. */
25export type BillingAccount = {
26 workspace: string;
27 /**
28 * Credit left, in millionths of a dollar. Can dip below zero by the cost
29 * of the runs that were under way when it ran out.
30 */
31 balanceMicros: number;
32 status: BillingStatus;
33 /** What is added to a run's cost, in percent. */
34 marginPercent: number;
35 /** What a run on the workspace's own model provider is charged instead. */
36 orchestrationFeeMicros: number;
37};
38
39/** One line of a workspace's statement. */
40export type LedgerEntry = {
41 id: string;
42 /** Credit bought with a card, or an agent's run. */
43 kind: "top_up" | "usage";
44 /** Positive for credit added, negative for usage. */
45 amountMicros: number;
46 description: string;
47 /** For usage: the repository and pull request the agent worked on. */
48 repo: string | null;
49 number: number | null;
50 /** For usage: `implement`, `review` or `update`. */
51 task: string | null;
52 /** For usage: the model, by its public name. */
53 model: string | null;
54 /** For usage: who paid the model provider. */
55 billedTo: "g1t" | "workspace";
56 /** For a top-up: the username of whoever paid. */
57 createdBy: string | null;
58 /** RFC 3339. */
59 createdAt: string;
60};
61
62/** What lets a sandbox, and nothing else, report what its run cost. */
63export type RunTicket = { runId: string; token: string };
64
65/**
66 * What agents cost, charged to the workspace they worked for. A workspace
67 * buys credit; each run deducts its cost plus g1t's margin; with no credit,
68 * no agent starts.
69 */
70export interface BillingApi {
71 status(): Promise<BillingStatus>;
72 /** Members of the workspace only. */
73 account(workspace: string, viewer: Viewer): Promise<Result<BillingAccount>>;
74 /** Newest first. Members of the workspace only. */
75 ledger(workspace: string, viewer: Viewer): Promise<Result<LedgerEntry[]>>;
76 /** What the workspace's agents cost since `since`, broken down. Members only. */
77 usage(workspace: string, viewer: Viewer, since: string): Promise<Result<Usage>>;
78 /**
79 * Starts a card payment for credit and returns the page to send the
80 * person to. Owners only. The payment's id comes back to `returnUrl` as
81 * `session`.
82 */
83 checkout(actor: User, workspace: string, amountCents: number, returnUrl: string): Promise<Result<{ url: string }>>;
84 /** Credits a payment once the processor says it was made. Safe to repeat. */
85 confirm(workspace: string, viewer: Viewer, session: string): Promise<Result<BillingAccount>>;
86 /**
87 * Whether a workspace may start an agent now, asked before anything is
88 * opened for it. A failure, with the reason to show, when it has no credit.
89 */
90 canStart(workspace: string): Promise<Result<boolean>>;
91 /**
92 * Asks whether a workspace may start an agent and opens the run it will be
93 * charged for. Null when billing is off; a failure when there is no credit.
94 */
95 startRun(run: {
96 workspace: string;
97 repo: RepoPath;
98 number: number;
99 task: string;
100 model: string;
101 /** `workspace` when the run uses the workspace's own model provider. */
102 billedTo?: "g1t" | "workspace";
103 }): Promise<Result<RunTicket | null>>;
104}
105
106
107/** One slice of usage: what it was for, what it cost, how many runs. */
108export type UsageSlice = { key: string; micros: number; runs: number };
109
110/** What a workspace's agents cost over a period. */
111export type Usage = {
112 since: string;
113 /** Charged, including g1t's margin. */
114 spentMicros: number;
115 /** What g1t's model provider charged, before the margin. */
116 costMicros: number;
117 /** What runs on the workspace's own provider cost there, estimated. Not charged by g1t. */
118 providerMicros: number;
119 runs: number;
120 /** Spend per day and task, keyed `YYYY-MM-DD/task`. */
121 byDay: UsageSlice[];
122 byTask: UsageSlice[];
123 byRepo: UsageSlice[];
124 /** Keyed `namespace/name#number`. */
125 byPull: UsageSlice[];
126 byModel: UsageSlice[];
127 /** Credit bought in the period. */
128 addedMicros: number;
129};