pr_01m47d24b0e6n91zwymwxg0vpx/packages/contracts/src/billing.ts

116 lines4,149 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
19/** A workspace's standing. */
20export type BillingAccount = {
21 workspace: string;
22 /**
23 * Credit left, in millionths of a dollar. Can dip below zero by the cost
24 * of the runs that were under way when it ran out.
25 */
26 balanceMicros: number;
27 status: BillingStatus;
28 /** What is added to a run's cost, in percent. */
29 marginPercent: number;
30};
31
32/** One line of a workspace's statement. */
33export type LedgerEntry = {
34 id: string;
35 /** Credit bought with a card, or an agent's run. */
36 kind: "top_up" | "usage";
37 /** Positive for credit added, negative for usage. */
38 amountMicros: number;
39 description: string;
40 /** For usage: the repository and pull request the agent worked on. */
41 repo: string | null;
42 number: number | null;
43 /** For usage: `implement`, `review` or `update`. */
44 task: string | null;
45 /** For usage: the model, by its public name. */
46 model: string | null;
47 /** For a top-up: the username of whoever paid. */
48 createdBy: string | null;
49 /** RFC 3339. */
50 createdAt: string;
51};
52
53/** What lets a sandbox, and nothing else, report what its run cost. */
54export type RunTicket = { runId: string; token: string };
55
56/**
57 * What agents cost, charged to the workspace they worked for. A workspace
58 * buys credit; each run deducts its cost plus g1t's margin; with no credit,
59 * no agent starts.
60 */
61export interface BillingApi {
62 status(): Promise<BillingStatus>;
63 /** Members of the workspace only. */
64 account(workspace: string, viewer: Viewer): Promise<Result<BillingAccount>>;
65 /** Newest first. Members of the workspace only. */
66 ledger(workspace: string, viewer: Viewer): Promise<Result<LedgerEntry[]>>;
67 /** What the workspace's agents cost since `since`, broken down. Members only. */
68 usage(workspace: string, viewer: Viewer, since: string): Promise<Result<Usage>>;
69 /**
70 * Starts a card payment for credit and returns the page to send the
71 * person to. Owners only. The payment's id comes back to `returnUrl` as
72 * `session`.
73 */
74 checkout(actor: User, workspace: string, amountCents: number, returnUrl: string): Promise<Result<{ url: string }>>;
75 /** Credits a payment once the processor says it was made. Safe to repeat. */
76 confirm(workspace: string, viewer: Viewer, session: string): Promise<Result<BillingAccount>>;
77 /**
78 * Whether a workspace may start an agent now, asked before anything is
79 * opened for it. A failure, with the reason to show, when it has no credit.
80 */
81 canStart(workspace: string): Promise<Result<boolean>>;
82 /**
83 * Asks whether a workspace may start an agent and opens the run it will be
84 * charged for. Null when billing is off; a failure when there is no credit.
85 */
86 startRun(run: {
87 workspace: string;
88 repo: RepoPath;
89 number: number;
90 task: string;
91 model: string;
92 }): Promise<Result<RunTicket | null>>;
93}
94
95
96/** One slice of usage: what it was for, what it cost, how many runs. */
97export type UsageSlice = { key: string; micros: number; runs: number };
98
99/** What a workspace's agents cost over a period. */
100export type Usage = {
101 since: string;
102 /** Charged, including g1t's margin. */
103 spentMicros: number;
104 /** What the model provider charged, before the margin. */
105 costMicros: number;
106 runs: number;
107 /** Spend per day and task, keyed `YYYY-MM-DD/task`. */
108 byDay: UsageSlice[];
109 byTask: UsageSlice[];
110 byRepo: UsageSlice[];
111 /** Keyed `namespace/name#number`. */
112 byPull: UsageSlice[];
113 byModel: UsageSlice[];
114 /** Credit bought in the period. */
115 addedMicros: number;
116};