Skip to content

g1t/crates/contracts/src/billing.rs

4,269 lines154,906 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.

Agents as a team: lifecycle, merge queue, billing and a new shell1//! The billing service: what agents cost, charged to the workspace they
2//! worked for.
3//!
4//! A workspace buys credit and each agent run deducts what it cost, plus
5//! g1t's margin. With no credit, no agent starts. Money is held in
6//! millionths of a US dollar, so that a run costing a fraction of a cent is
7//! recorded exactly.
8//!
9//! Each `*Args` struct is the argument of the method of the same name,
10//! served at `POST /rpc/<method>`.
11
12use serde::{Deserialize, Serialize};
13
14use crate::repos::RepoPath;
15use crate::{User, Viewer};
16
17/// Millionths of a US dollar in one dollar.
18pub const MICROS_PER_DOLLAR: i64 = 1_000_000;
19
20/// Whether workspaces are charged for agents at all, and with real money.
21/// `status` takes nothing and returns this.
22#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
23pub struct Status {
24 /// False when no payment provider is configured: nothing is charged,
25 /// and who may run agents is decided some other way.
26 pub enabled: bool,
27 /// False while the payment provider is in its test mode, where cards
28 /// are not real.
29 pub live: bool,
Free while g1t is being built out; agents can check out their own forks30 /// True while g1t is being built out: runs are recorded, with what
31 /// they cost, but nothing is charged and no credit is needed. Not a
32 /// promise that it stays free.
33 #[serde(default)]
34 pub free: bool,
Agents as a team: lifecycle, merge queue, billing and a new shell35}
36
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put37/// `trial`: a workspace's trial credit, so people can try g1t (its agents on
38/// g1t's hosted models among it) without a key or a card of their own. Each
39/// new workspace gets one grant of usage credit (`TRIAL_WORKSPACE_MICROS`),
40/// made when it first uses something, out of a pool for everyone that
41/// resets each calendar month (`TRIAL_MONTHLY_POOL_MICROS`). When this
42/// month's pool is given out, new grants wait for the next month. Returns
43/// `Trial`.
A free allowance on g1t's models, so anyone can try its agents44#[derive(Debug, Serialize, Deserialize)]
45#[serde(rename_all = "camelCase")]
46pub struct TrialArgs {
47 pub workspace: String,
48 /// Workspaces open to hosted models anyway, whose use is not counted
49 /// against the pool.
50 #[serde(default)]
51 pub exempt: Vec<String>,
52}
53
54#[derive(Clone, Debug, Serialize, Deserialize)]
55#[serde(rename_all = "camelCase")]
56pub struct Trial {
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put57 /// Whether its agents may use g1t's hosted models on the trial now: it
58 /// has credit left, or this month's pool can still grant it some.
A free allowance on g1t's models, so anyone can try its agents59 pub open: bool,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put60 /// What the trial has paid for so far, in millionths of a dollar.
A free allowance on g1t's models, so anyone can try its agents61 pub used_micros: i64,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put62 /// Its grant, or what it would be granted.
A free allowance on g1t's models, so anyone can try its agents63 pub limit_micros: i64,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put64 /// No longer used: the trial does not end on a date. Kept for older
65 /// readers; always null.
A free allowance on g1t's models, so anyone can try its agents66 pub ends_at: Option<String>,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put67 /// Why it is closed: `off` (no trials), `used` (this workspace's grant
68 /// is spent) or `pool` (this month's grants are all given out; see
69 /// `waits_until`). `ended` is no longer sent.
A free allowance on g1t's models, so anyone can try its agents70 pub reason: Option<String>,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put71 /// Whether the workspace has its grant already.
72 #[serde(default)]
73 pub granted: bool,
74 /// RFC 3339: when a workspace waiting for a grant can get one, the
75 /// first of next month. Only with reason `pool`.
76 #[serde(default)]
77 pub waits_until: Option<String>,
A free allowance on g1t's models, so anyone can try its agents78}
79
Agents as a team: lifecycle, merge queue, billing and a new shell80/// A workspace's standing.
81#[derive(Clone, Debug, Serialize, Deserialize)]
82#[serde(rename_all = "camelCase")]
83pub struct Account {
84 pub workspace: String,
85 /// Credit left, in millionths of a dollar. Can dip below zero by the
86 /// cost of the runs that were under way when it ran out.
87 pub balance_micros: i64,
88 pub status: Status,
89 /// What is added to a run's cost, in percent.
90 pub margin_percent: u32,
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace91 /// The card g1t charges as the workspace nears its limit and when a
92 /// month closes, if one is on file.
93 #[serde(default)]
94 pub card: Option<Card>,
Agents as a team: lifecycle, merge queue, billing and a new shell95}
96
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace97/// A saved card, as far as it is safe to show.
98#[derive(Clone, Debug, Serialize, Deserialize)]
99#[serde(rename_all = "camelCase")]
100pub struct Card {
101 /// `visa`, `mastercard`, ...
102 pub brand: String,
103 pub last4: String,
104 pub exp_month: u32,
105 pub exp_year: u32,
106}
107
108/// `billing_portal`: Stripe's hosted billing page for the workspace, where
109/// an owner adds or replaces the card, sees invoices and receipts, and sets
110/// the billing email and address. g1t never handles card numbers. Owners
111/// only. Returns `Outcome<Checkout>` (its `url`); Stripe sends them back
112/// to `return_url`.
113#[derive(Debug, Serialize, Deserialize)]
114pub struct BillingPortalArgs {
115 pub actor: User,
116 pub workspace: String,
117 pub return_url: String,
118}
119
120/// `admin_billing_link`: for staff to send a customer: their Stripe billing
121/// page. Returns `Outcome<BillingLink>`.
122#[derive(Debug, Serialize, Deserialize)]
123pub struct AdminBillingLinkArgs {
124 pub workspace: String,
125 pub by: String,
126}
127
128#[derive(Clone, Debug, Serialize, Deserialize)]
129#[serde(rename_all = "camelCase")]
130pub struct BillingLink {
131 /// A one-time session on Stripe's billing page, signed in already.
132 pub portal_url: String,
133 /// The billing page's sign-in page, which does not expire: the
134 /// customer signs in with the email Stripe has for them.
135 pub login_url: Option<String>,
136 pub customer_email: Option<String>,
137 pub expires_note: String,
138}
139
Agents as a team: lifecycle, merge queue, billing and a new shell140#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
141#[serde(rename_all = "snake_case")]
142pub enum EntryKind {
143 /// Credit bought with a card.
144 TopUp,
Paid features: a workspace turns on Deployments with a monthly plan145 /// An agent's run, or a paid feature's usage past its allowance.
Agents as a team: lifecycle, merge queue, billing and a new shell146 Usage,
147}
148
149/// One line of a workspace's statement.
150#[derive(Clone, Debug, Serialize, Deserialize)]
151#[serde(rename_all = "camelCase")]
152pub struct LedgerEntry {
153 pub id: String,
154 pub kind: EntryKind,
155 /// Positive for credit added, negative for usage.
156 pub amount_micros: i64,
157 pub description: String,
158 /// For usage: the repository and pull request the agent worked on.
159 pub repo: Option<String>,
160 pub number: Option<u32>,
161 /// For usage: `implement`, `review` or `update`.
162 pub task: Option<String>,
163 /// For usage: the model, by its public name.
164 pub model: Option<String>,
Integrations: your own model provider, alerts that open issues, tickets agents read165 /// For usage: `g1t` when g1t paid the model provider, `workspace` when
Prices are what g1t pays plus 20%, from the first second166 /// the workspace's own account did. Runs on the workspace's own
167 /// provider pay only their sandbox time now, so only older entries
168 /// are `workspace`.
Integrations: your own model provider, alerts that open issues, tickets agents read169 #[serde(default = "g1t")]
170 pub billed_to: String,
Agents as a team: lifecycle, merge queue, billing and a new shell171 /// For a top-up: the username of whoever paid.
172 pub created_by: Option<String>,
173 /// RFC 3339.
174 pub created_at: String,
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace175 /// The workspace the line belongs to, which tells an enterprise's
176 /// lines apart.
177 #[serde(default, skip_serializing_if = "Option::is_none")]
178 pub workspace: Option<String>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look179 /// For usage: what the g1t plan's monthly included usage paid of it.
180 /// The entry's `amount_micros` is what is left to pay.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put181 #[serde(default)]
182 pub credit_micros: i64,
183 /// For usage: what the workspace's trial credit paid of it.
184 #[serde(default)]
185 pub trial_micros: i64,
186 /// For usage: what g1t's open-source pool paid of it.
187 #[serde(default)]
188 pub oss_micros: i64,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look189 /// For usage: what g1t covered itself, such as the part of a free
190 /// workspace's last trial run that went past its trial credit.
191 #[serde(default)]
192 pub given_micros: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging193 /// For usage: what the account's discount took off its price. The
194 /// price is `-amount_micros` plus this and what paid for it.
195 #[serde(default)]
196 pub discount_micros: i64,
197 /// For a credit from g1t, and for what of one expired or was revoked:
198 /// its kind.
199 #[serde(default, skip_serializing_if = "Option::is_none")]
200 pub credit_kind: Option<CreditKind>,
Agents as a team: lifecycle, merge queue, billing and a new shell201}
202
Integrations: your own model provider, alerts that open issues, tickets agents read203fn g1t() -> String {
204 "g1t".to_owned()
205}
206
Agents as a team: lifecycle, merge queue, billing and a new shell207/// `account` (`Outcome<Account>`) and `ledger` (`Outcome<Vec<LedgerEntry>>`,
208/// newest first). Members of the workspace only.
209#[derive(Debug, Serialize, Deserialize)]
210pub struct AccountArgs {
211 pub workspace: String,
212 pub viewer: Viewer,
213}
214
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look215/// `checkout`: prepays usage: money paid in advance, drawn down by usage
216/// after the plan's included usage, which raises what can be used before
217/// work stops by the same amount at once. $25 at the least. By card, with
218/// 3-D Secure; from $1,000 also by bank transfer. Owners of the workspace
Agents as a team: lifecycle, merge queue, billing and a new shell219/// only. Returns `Outcome<Checkout>`.
220#[derive(Debug, Serialize, Deserialize)]
221#[serde(rename_all = "camelCase")]
222pub struct CheckoutArgs {
223 pub actor: User,
224 pub workspace: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look225 /// How much to prepay, in cents.
Agents as a team: lifecycle, merge queue, billing and a new shell226 pub amount_cents: u32,
227 /// Where the payment page sends the person afterwards. The payment's
228 /// id is appended as `session`.
229 pub return_url: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look230 /// `card` (the default) or `bank_transfer` (from $1,000): Stripe gives
231 /// the account details, and the money counts once it arrives.
232 #[serde(default)]
233 pub method: Option<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell234}
235
236#[derive(Debug, Serialize, Deserialize)]
237pub struct Checkout {
238 /// The payment page to send the person to.
239 pub url: String,
240}
241
242/// `confirm`: credits a payment once the provider says it was made. Safe
243/// to call any number of times. Returns `Outcome<Account>`.
244#[derive(Debug, Serialize, Deserialize)]
245pub struct ConfirmArgs {
246 pub workspace: String,
247 pub viewer: Viewer,
248 /// The payment's id, as returned to `return_url`.
249 pub session: String,
250}
251
252/// `can_start`: whether a workspace may start an agent now, asked before
253/// anything is opened for it. Returns `Outcome<bool>`: a failure, with the
254/// reason to show, when it has no credit.
255#[derive(Debug, Serialize, Deserialize)]
256pub struct CanStartArgs {
257 pub workspace: String,
258}
259
260/// `start_run`: asks whether a workspace may start an agent, and opens the
261/// run it will be charged for. Called by the runner service. Returns
262/// `Outcome<Option<RunTicket>>`: no ticket when billing is off, a failure
263/// when the workspace has no credit.
264#[derive(Debug, Serialize, Deserialize)]
265pub struct StartRunArgs {
266 pub workspace: String,
267 pub repo: RepoPath,
268 pub number: u32,
269 /// `implement`, `review` or `update`.
270 pub task: String,
271 /// The model, by its public name.
272 pub model: String,
Integrations: your own model provider, alerts that open issues, tickets agents read273 /// `workspace` when the run uses the workspace's own model provider.
Models per workspace: several providers, routed by kind of work274 /// The runner, which is TypeScript, sends it as `billedTo`.
275 #[serde(default = "g1t", alias = "billedTo")]
Integrations: your own model provider, alerts that open issues, tickets agents read276 pub billed_to: String,
Merge branch 'model-routing'277 /// The model session's id. Through g1t's AI Gateway, settling charges
278 /// the run what the gateway priced its requests at; on the workspace's
279 /// own provider, it is what the proxy counts the run's tokens under,
280 /// for the agent rate.
Prices keep themselves current with what g1t pays281 #[serde(default)]
282 pub session: Option<String>,
Merge branch 'model-routing'283 /// `small`, `large` or `frontier`: the tier g1t routed the run to.
284 /// None when the workspace's own provider names its model.
Auto model routing: the cheapest tier that can do each piece of work, a retry goes up a tier, and each run records its tier285 #[serde(default)]
286 pub tier: Option<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell287}
288
289#[derive(Clone, Debug, Serialize, Deserialize)]
290#[serde(rename_all = "camelCase")]
291pub struct RunTicket {
292 pub run_id: String,
293 /// Lets the sandbox, and nothing else, report what this run cost.
294 pub token: String,
295}
296
297/// `finish_run`: what a run cost, as its sandbox reports it. Charged once.
298/// Returns `Outcome<bool>`.
299#[derive(Debug, Serialize, Deserialize)]
300#[serde(rename_all = "camelCase")]
301pub struct FinishRunArgs {
302 pub run_id: String,
303 pub token: String,
304 /// What the model provider charged, in US dollars.
305 pub cost_usd: f64,
306 #[serde(default)]
307 pub turns: u32,
Merge branch 'model-routing'308 /// The tokens the run used, as the harness counted them from the
309 /// provider's answers. On the workspace's own provider, the agent rate
310 /// is charged on no fewer than these. Absent from older sandboxes.
311 #[serde(default)]
312 pub tokens: Option<RunTokens>,
313}
314
315/// The tokens one run used, by kind.
316#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
317#[serde(rename_all = "camelCase")]
318pub struct RunTokens {
319 #[serde(default)]
320 pub input: u64,
321 #[serde(default)]
322 pub output: u64,
323 #[serde(default)]
324 pub cache_read: u64,
325 #[serde(default)]
326 pub cache_write: u64,
327}
328
329impl RunTokens {
330 /// Every token, of every kind: what the agent rate is charged on.
331 pub fn total(&self) -> u64 {
332 self.input.saturating_add(self.output).saturating_add(self.cache_read).saturating_add(self.cache_write)
333 }
Agents as a team: lifecycle, merge queue, billing and a new shell334}
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request335
336
337/// `usage`: what a workspace's agents cost over a period, broken down.
338/// Members only. Returns `Outcome<Usage>`.
339#[derive(Debug, Serialize, Deserialize)]
340pub struct UsageArgs {
341 pub workspace: String,
342 pub viewer: Viewer,
343 /// RFC 3339: the start of the period. The period runs to now.
344 pub since: String,
345}
346
347/// One slice of usage: what it was for, what it cost, how many runs.
348#[derive(Clone, Debug, Serialize, Deserialize)]
349#[serde(rename_all = "camelCase")]
350pub struct UsageSlice {
351 pub key: String,
352 pub micros: i64,
353 pub runs: u32,
354}
355
356/// What a workspace's agents cost over a period.
357#[derive(Clone, Debug, Serialize, Deserialize)]
358#[serde(rename_all = "camelCase")]
359pub struct Usage {
360 pub since: String,
361 /// Charged, including g1t's margin.
362 pub spent_micros: i64,
Billing and Usage reconcile: own-provider runs leave Billing's at-price total, and Usage's not-charged part is at price less charged363 /// What g1t's usage came to at price, less what was charged: the plan's
364 /// included usage, the trial, a pool or a free period paid it. Usage at
365 /// price is `spent_micros` plus this.
Billing's usage total is labeled at price, and Usage says what part of it was paid for366 #[serde(default)]
367 pub covered_micros: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging368 /// What the account's discount took off the price. Usage at price is
369 /// `spent_micros` plus `covered_micros` plus this.
370 #[serde(default)]
371 pub discount_micros: i64,
372 /// The account's discount now, in percent; absent without one. With
373 /// one, the slices measure usage at price.
374 #[serde(default)]
375 pub discount_percent: Option<u32>,
Usage, Billing settings and prepaid AI credit; fixes from the UX audit376 /// Usage at price: `spent_micros` plus `covered_micros` plus
377 /// `discount_micros`, from the same ledger lines. The one figure every
378 /// page shows as usage (mission control, the agent fleet, Usage and
379 /// Billing), labelled "usage at price".
380 #[serde(default)]
381 pub price_micros: i64,
Integrations: your own model provider, alerts that open issues, tickets agents read382 /// What g1t's model provider charged, before the margin.
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request383 pub cost_micros: i64,
Integrations: your own model provider, alerts that open issues, tickets agents read384 /// What runs on the workspace's own provider cost there, as the harness
385 /// estimated it. Not charged by g1t.
386 pub provider_micros: i64,
Usage while free is shown at cost; agents get rustfmt and clippy387 /// What the runs used, at cost: g1t's models and the workspace's own
388 /// provider together, whatever was charged for them.
389 pub used_micros: i64,
390 /// g1t charges nothing for now. The slices then measure usage at cost,
391 /// since every charge is zero.
392 pub free: bool,
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request393 pub runs: u32,
394 /// Spend per day (`YYYY-MM-DD`) and task, as `day/task` keys.
395 pub by_day: Vec<UsageSlice>,
396 /// Per task: implement, review, revise, update, plan.
397 pub by_task: Vec<UsageSlice>,
398 /// Per repository, `namespace/name`.
399 pub by_repo: Vec<UsageSlice>,
400 /// The pull requests that cost most, as `namespace/name#number`.
401 pub by_pull: Vec<UsageSlice>,
402 /// Per model, by its public name.
403 pub by_model: Vec<UsageSlice>,
404 /// Credit bought in the period.
405 pub added_micros: i64,
406}
Models per workspace: several providers, routed by kind of work407
Mission control shows model usage, yours and the workspace's: tokens, cost, active days, cache share, each day, and the mix408/// `record_tokens`: what one model answer used, added to the day's count
409/// for its run. The model proxy sends it after each answer. For usage
410/// views only: runs are still priced from AI Gateway. Returns
411/// `Outcome<bool>`: false when there was nothing to count.
412#[derive(Debug, Serialize, Deserialize)]
413#[serde(rename_all = "camelCase")]
414pub struct RecordTokensArgs {
415 pub workspace: String,
416 /// The model session's id (`ModelSession::id`), one per run.
417 pub session: String,
418 /// The person the run is for, by username. Absent when nobody asked.
419 #[serde(default)]
420 pub person: Option<String>,
421 pub model: String,
Merge branch 'model-routing'422 /// The tier g1t routed the run to: `small`, `large` or `frontier`.
Mission control shows model usage, yours and the workspace's: tokens, cost, active days, cache share, each day, and the mix423 #[serde(default)]
424 pub tier: Option<String>,
425 #[serde(default)]
426 pub input: u64,
427 #[serde(default)]
428 pub output: u64,
429 #[serde(default)]
430 pub cache_read: u64,
431 #[serde(default)]
432 pub cache_write: u64,
433}
434
435/// `token_usage`: the model tokens a workspace's runs used, day by day,
436/// for the whole workspace or for one person. Members only; a member may
437/// ask only for themselves, an owner for anyone. Returns
438/// `Outcome<TokenUsage>`.
439#[derive(Debug, Serialize, Deserialize)]
440pub struct TokenUsageArgs {
441 pub workspace: String,
442 pub viewer: Viewer,
443 /// A username: only the runs for them.
444 #[serde(default)]
445 pub person: Option<String>,
446 /// How many days, to today: 42 when absent, 366 at most.
447 #[serde(default)]
448 pub days: Option<u32>,
449}
450
451/// One day's tokens.
452#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
453pub struct DayTokens {
454 /// `YYYY-MM-DD`, UTC.
455 pub day: String,
456 pub tokens: u64,
457}
458
459/// The model tokens runs used over a window of days.
460#[derive(Clone, Debug, Serialize, Deserialize)]
461#[serde(rename_all = "camelCase")]
462pub struct TokenUsage {
463 /// `YYYY-MM-DD`: the first day counted.
464 pub since: String,
465 pub days: u32,
466 /// Null for the whole workspace.
467 pub person: Option<String>,
468 pub total_tokens: u64,
469 pub input_tokens: u64,
470 pub output_tokens: u64,
471 pub cache_read_tokens: u64,
472 pub cache_write_tokens: u64,
473 /// What those runs were charged, as `usage` measures it.
474 pub cost_micros: i64,
475 /// Days in the window with any tokens.
476 pub active_days: u32,
477 /// Every day in the window, oldest first, zeros included.
478 pub by_day: Vec<DayTokens>,
479}
480
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens481// --- AI Gateway -------------------------------------------------------------
482//
483// A workspace's own model requests, sent with one of its access tokens to
AI Gateway: OpenAI's format, open models, and your own providers484// the model proxy (`models.g1t.sh/anthropic` in Anthropic's Messages format,
485// `models.g1t.sh/openai/v1` in OpenAI's Chat Completions format). On g1t's
486// models each request
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens487// is charged to the workspace at the model's price, with the price book's
488// `gateway_models` markup, drawn from AI credit; on the workspace's own
489// provider key it is only counted.
490
491/// A model the AI Gateway offers on g1t's own key, with its price per
492/// million tokens of each kind. `gateway_models` takes nothing and returns
493/// these, in the order they are shown.
494#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
495#[serde(rename_all = "camelCase")]
496pub struct GatewayModel {
AI Gateway: OpenAI's format, open models, and your own providers497 /// The provider's own id, such as `claude-sonnet-5-5` or
498 /// `@cf/openai/gpt-oss-120b`. A request names it as it is or with its
499 /// provider in front (`anthropic/claude-sonnet-5-5`,
500 /// `workers-ai/@cf/openai/gpt-oss-120b`).
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens501 pub model: String,
502 /// For people: `Claude Sonnet 5.5`.
503 pub name: String,
AI Gateway: OpenAI's format, open models, and your own providers504 /// `anthropic` or `workers-ai`.
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens505 pub provider: String,
AI Gateway: OpenAI's format, open models, and your own providers506 /// `chat`, or `embeddings` for a model that only embeds text.
507 #[serde(default = "chat")]
508 pub kind: String,
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens509 pub input_micros: i64,
510 pub output_micros: i64,
511 pub cache_read_micros: i64,
AI Gateway: OpenAI's format, open models, and your own providers512 /// Cache writes that live five minutes.
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens513 pub cache_write_micros: i64,
AI Gateway: OpenAI's format, open models, and your own providers514 /// Cache writes that live an hour.
515 #[serde(default)]
516 pub cache_write_1h_micros: i64,
517 /// A model priced by the prompt's length: a request whose prompt (its
518 /// input, cache read and cache write tokens) is longer than this many
519 /// tokens is charged entirely at the `over_` prices. 0 for one price.
520 #[serde(default)]
521 pub threshold: u64,
522 #[serde(default)]
523 pub over_input_micros: i64,
524 #[serde(default)]
525 pub over_output_micros: i64,
526 #[serde(default)]
527 pub over_cache_read_micros: i64,
528 #[serde(default)]
529 pub over_cache_write_micros: i64,
530 #[serde(default)]
531 pub over_cache_write_1h_micros: i64,
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens532}
533
AI Gateway: OpenAI's format, open models, and your own providers534fn chat() -> String {
535 "chat".to_owned()
536}
537
538fn anthropic_format() -> String {
539 "anthropic".to_owned()
540}
541
Merge branch 'main' into actions-toolkit-oidc-artifacts542// --- The model catalogue ----------------------------------------------------
543//
544// Every model g1t can use, in one table (billing's `gateway_models`): the
545// models agents run on, the AI Gateway's, and the embeddings model. New
546// models are found by the models service listing each provider daily
547// (`record_discovery`) and wait as `new` until staff approve them in sudo.
548// Which model each purpose uses by default is staff's choice
549// (`model_defaults`), read by the runner and the AI Gateway.
550
551/// Where a model stands. Only `available` models are routed to; the AI
552/// Gateway offers `available` and `deprecated` ones that have a price.
553pub mod model_status {
554 /// Approved and priced: routed to and offered.
555 pub const AVAILABLE: &str = "available";
556 /// Found by discovery and not approved yet: never routed to, offered or charged.
557 pub const NEW: &str = "new";
558 /// The provider stopped listing it: still offered to anyone who names
559 /// it, but no default routes to it.
560 pub const DEPRECATED: &str = "deprecated";
561 /// Staff retired it: neither routed to nor offered.
562 pub const RETIRED: &str = "retired";
563}
564
565/// One model in the catalogue: its prices (as the AI Gateway reads them)
566/// and what g1t knows about it. `admin_models` returns these.
567#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
568#[serde(rename_all = "camelCase")]
569pub struct CatalogueModel {
570 #[serde(flatten)]
571 pub prices: GatewayModel,
572 /// Other ids the provider lists it by, such as a dated one.
573 #[serde(default)]
574 pub aliases: Vec<String>,
575 /// `haiku`, `sonnet`, `opus`, `fable`, or a Workers AI author.
576 #[serde(default)]
577 pub family: String,
578 /// The agent tier it suits: `small`, `large`, `frontier`, or empty.
579 #[serde(default)]
580 pub tier_hint: String,
581 /// Tokens it reads at most; 0 when not known.
582 #[serde(default)]
583 pub context_window: u64,
584 /// Tokens it writes at most; 0 when not known.
585 #[serde(default)]
586 pub max_output: u64,
587 /// Any of `effort`, `thinking`, `tools`, `vision`, `embeddings`.
588 #[serde(default)]
589 pub capabilities: Vec<String>,
590 /// An embeddings model's vector length; 0 otherwise or when not known.
591 #[serde(default)]
592 pub dimensions: u32,
593 /// `available`, `new`, `deprecated` or `retired` (`model_status`).
594 pub status: String,
595 /// Whether its prices are known. An unpriced model is never routed to,
596 /// offered or charged for.
597 pub priced: bool,
598 /// `discovered` (found by listing its provider) or `staff`.
599 pub source: String,
600 #[serde(default)]
601 pub first_seen_at: Option<String>,
602 /// When its provider last listed it.
603 #[serde(default)]
604 pub last_seen_at: Option<String>,
605 /// Since when its provider has not listed it.
606 #[serde(default)]
607 pub missing_since: Option<String>,
608 #[serde(default)]
609 pub approved_by: Option<String>,
610 #[serde(default)]
611 pub approved_at: Option<String>,
612 #[serde(default)]
613 pub note: String,
614 /// What a typical agent run would cost on it, in millionths of a
615 /// dollar, from its prices (`typical_run` in billing's catalogue.rs);
616 /// 0 for an embeddings or unpriced model.
617 #[serde(default)]
618 pub typical_run_micros: i64,
619}
620
621/// A provider's list price per million tokens, as its listing gives it, in
622/// millionths of a dollar.
623#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
624#[serde(rename_all = "camelCase")]
625pub struct ListedPrice {
626 pub input_micros: i64,
627 #[serde(default)]
628 pub output_micros: i64,
629}
630
631/// One model as its provider lists it, from the models service's
632/// discovery.
633#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
634#[serde(rename_all = "camelCase")]
635pub struct ProviderModel {
636 /// The provider's id: `claude-haiku-5-5`, `@cf/openai/gpt-oss-120b`.
637 pub id: String,
638 /// For people, as the provider names it.
639 #[serde(default)]
640 pub name: String,
641 /// `chat`, `embeddings`, or anything else (not added to the catalogue,
642 /// but still counted as listed).
643 #[serde(default)]
644 pub kind: String,
645 #[serde(default)]
646 pub context_window: u64,
647 #[serde(default)]
648 pub max_output: u64,
649 #[serde(default)]
650 pub capabilities: Vec<String>,
651 /// Workers AI lists a price with each model; Anthropic does not.
652 #[serde(default)]
653 pub price: Option<ListedPrice>,
654}
655
656/// `record_discovery`: what one provider lists now, from the models
657/// service (daily, or when staff press "Check for new models"). Billing
658/// adds new ids as `new`, marks ones no longer listed `deprecated`, records
659/// the check and emails staff about anything new. With `error` (the listing
660/// failed) only the check is recorded. Returns `DiscoveryResult`.
661#[derive(Clone, Debug, Default, Serialize, Deserialize)]
662#[serde(rename_all = "camelCase")]
663pub struct RecordDiscoveryArgs {
664 /// `anthropic` or `workers-ai`.
665 pub provider: String,
666 #[serde(default)]
667 pub models: Vec<ProviderModel>,
668 /// The staff member who asked, or `schedule`.
669 pub by: String,
670 #[serde(default)]
671 pub error: Option<String>,
672}
673
674/// What one check found.
675#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
676#[serde(rename_all = "camelCase")]
677pub struct DiscoveryResult {
678 pub provider: String,
679 pub checked_at: String,
680 pub by: String,
681 /// Ids the provider listed.
682 pub listed: u32,
683 /// Ids added to the catalogue as `new`.
684 pub added: Vec<String>,
685 /// Catalogue models the provider no longer lists, now `deprecated`.
686 pub deprecated: Vec<String>,
687 /// Deprecated models listed again.
688 pub restored: Vec<String>,
689 /// The listing failed: nothing changed.
690 #[serde(default)]
691 pub error: Option<String>,
692}
693
694/// Which model, tier or effort one purpose uses by default, as staff last
695/// set it.
696#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
697#[serde(rename_all = "camelCase")]
698pub struct ModelDefault {
699 /// `tier_small`, `tier_large`, `tier_frontier`, `background`,
700 /// `gateway_first`, or `job_<kind>` for `implement`, `revise`,
701 /// `answer`, `review`, `update` and `plan`.
702 pub purpose: String,
703 /// The model, for a model purpose.
704 #[serde(default)]
705 pub model: Option<String>,
706 /// For a job: `small`, `large`, `frontier`, or `change` (sized by the change).
707 #[serde(default)]
708 pub tier: Option<String>,
709 /// For a job: `low`, `medium`, `high`, `xhigh` or `max`; none for the harness's own.
710 #[serde(default)]
711 pub effort: Option<String>,
712 pub updated_at: String,
713 pub updated_by: String,
714 #[serde(default)]
715 pub reason: String,
716}
717
718/// A model purpose's default as it applies now: the chosen model, or the
719/// one routing falls back to when the chosen one cannot be used.
720#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
721#[serde(rename_all = "camelCase")]
722pub struct ResolvedModel {
723 pub purpose: String,
724 /// The model staff chose.
725 pub chosen: String,
726 /// The model to use, with its prices; none when neither the chosen
727 /// model nor any other suits (callers keep their own fallback).
728 #[serde(default)]
729 pub model: Option<GatewayModel>,
730 #[serde(default)]
731 pub capabilities: Vec<String>,
732 /// Why it is not the chosen one, in a sentence: `Claude Haiku 5.5 is
733 /// retired; using Claude Haiku 4.5.`
734 #[serde(default)]
735 pub note: Option<String>,
736}
737
738/// One kind of agent job's starting tier and effort.
739#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
740#[serde(rename_all = "camelCase")]
741pub struct JobDefault {
742 /// `implement`, `revise`, `answer`, `review`, `update` or `plan`.
743 pub kind: String,
744 /// `small`, `large`, `frontier` or `change`.
745 pub tier: String,
746 #[serde(default)]
747 pub effort: Option<String>,
748}
749
750/// `model_defaults` takes nothing and returns this: every purpose's model
751/// as it applies now, and each job's tier and effort. Read by the runner
752/// (cached a minute) and the AI Gateway.
753#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
754#[serde(rename_all = "camelCase")]
755pub struct ModelDefaults {
756 pub models: Vec<ResolvedModel>,
757 pub jobs: Vec<JobDefault>,
758}
759
760/// A check of one provider, as `model_checks` keeps it.
761#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
762#[serde(rename_all = "camelCase")]
763pub struct ModelCheck {
764 pub id: String,
765 pub provider: String,
766 pub checked_at: String,
767 pub by: String,
768 pub listed: u32,
769 pub added: Vec<String>,
770 pub deprecated: Vec<String>,
771 #[serde(default)]
772 pub error: Option<String>,
773}
774
775/// `admin_models` takes nothing and returns this: sudo's Agents & models.
776#[derive(Clone, Debug, Default, Serialize, Deserialize)]
777#[serde(rename_all = "camelCase")]
778pub struct AdminModels {
779 /// Every model, `new` ones first, then by provider and position.
780 pub catalogue: Vec<CatalogueModel>,
781 pub defaults: Vec<ModelDefault>,
782 pub resolved: ModelDefaults,
783 /// The latest checks, newest first.
784 pub checks: Vec<ModelCheck>,
785 /// The token mix `typical_run_micros` prices, for the page to state.
786 pub typical: TypicalRun,
787}
788
789/// The tokens of the typical agent run estimates are priced from.
790#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
791#[serde(rename_all = "camelCase")]
792pub struct TypicalRun {
793 pub requests: u64,
794 /// Per request.
795 pub input: u64,
796 pub output: u64,
797 pub cache_read: u64,
798 pub cache_write: u64,
799}
800
801/// A model's prices as staff confirm them, per million tokens in
802/// millionths of a dollar.
803#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
804pub struct ModelPrices {
805 pub input_micros: i64,
806 pub output_micros: i64,
807 pub cache_read_micros: i64,
808 pub cache_write_micros: i64,
809 #[serde(default)]
810 pub cache_write_1h_micros: i64,
811 #[serde(default)]
812 pub threshold: u64,
813 #[serde(default)]
814 pub over_input_micros: i64,
815 #[serde(default)]
816 pub over_output_micros: i64,
817 #[serde(default)]
818 pub over_cache_read_micros: i64,
819 #[serde(default)]
820 pub over_cache_write_micros: i64,
821 #[serde(default)]
822 pub over_cache_write_1h_micros: i64,
823}
824
825/// `admin_decide_model`: `approve` a model (its prices confirmed, made
826/// available), `retire` one, or `restore` a retired or deprecated one.
827/// Audited. Returns `Outcome<CatalogueModel>`.
828#[derive(Clone, Debug, Default, Serialize, Deserialize)]
829pub struct AdminDecideModelArgs {
830 pub model: String,
831 pub decision: String,
832 /// On approval: the name people see, and the prices.
833 #[serde(default)]
834 pub name: Option<String>,
835 #[serde(default)]
836 pub tier_hint: Option<String>,
837 #[serde(default)]
838 pub prices: Option<ModelPrices>,
839 pub reason: String,
840 pub by: String,
841}
842
843/// `admin_set_model_default`: one purpose's default. A model purpose takes
844/// `model` (available, priced, and suited to the purpose); a job takes
845/// `tier` and `effort`. Audited with the old and new values and why.
846/// Returns `Outcome<ModelDefault>`.
847#[derive(Clone, Debug, Default, Serialize, Deserialize)]
848pub struct AdminSetModelDefaultArgs {
849 pub purpose: String,
850 #[serde(default)]
851 pub model: Option<String>,
852 #[serde(default)]
853 pub tier: Option<String>,
854 #[serde(default)]
855 pub effort: Option<String>,
856 pub reason: String,
857 pub by: String,
858}
859
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens860/// `gateway_admit`: whether a workspace's next AI Gateway request may go to
861/// g1t's models. Fails with `payment_required` and what to do when it may
862/// not: over its spend limit, out of AI credit, or not on the plan. Returns
863/// `Outcome<bool>`.
864#[derive(Debug, Serialize, Deserialize)]
865pub struct GatewayAdmitArgs {
866 pub workspace: String,
867}
868
869/// `record_gateway`: one AI Gateway request, logged, and charged when it
870/// went to g1t's models and used tokens. The model proxy sends it after
871/// the answer. `id` makes it idempotent: a request recorded twice is
872/// logged and charged once. Returns `Outcome<bool>`: false when it was
873/// already recorded.
874#[derive(Debug, Serialize, Deserialize)]
875#[serde(rename_all = "camelCase")]
876pub struct RecordGatewayArgs {
877 /// `gw_…`, chosen by the proxy.
878 pub id: String,
879 pub workspace: String,
880 /// The access token's id and name.
881 pub token_id: String,
882 #[serde(default)]
883 pub token_name: Option<String>,
884 pub model: String,
885 #[serde(default)]
886 pub input: u64,
887 #[serde(default)]
888 pub output: u64,
889 #[serde(default)]
890 pub cache_read: u64,
AI Gateway: OpenAI's format, open models, and your own providers891 /// Every cache write, of either lifetime.
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens892 #[serde(default)]
893 pub cache_write: u64,
AI Gateway: OpenAI's format, open models, and your own providers894 /// Of `cache_write`, those that live an hour.
895 #[serde(default)]
896 pub cache_write_hour: u64,
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens897 /// The HTTP status the caller was answered with.
898 pub status: u16,
899 /// On the workspace's own provider key: counted, never charged.
900 #[serde(default)]
901 pub own_key: bool,
AI Gateway: OpenAI's format, open models, and your own providers902 /// The format the request was sent in: `anthropic` or `openai`.
903 #[serde(default = "anthropic_format")]
904 pub format: String,
905 /// Who served it: on g1t's key the catalogue's provider (`anthropic`,
906 /// `workers-ai`); on the workspace's own, its connection's provider
907 /// (`openai`, `openai_endpoint`…). Empty when it never got that far.
908 #[serde(default)]
909 pub provider: String,
910 /// On the workspace's own provider: the connection's name.
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens911 #[serde(default)]
AI Gateway: OpenAI's format, open models, and your own providers912 pub connection: Option<String>,
913 #[serde(default)]
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens914 pub streamed: bool,
915 #[serde(default)]
916 pub duration_ms: u64,
917 /// What went wrong, for a request that was refused or failed.
918 #[serde(default)]
919 pub error: Option<String>,
920}
921
922/// `gateway_requests`: a workspace's recent AI Gateway requests, newest
923/// first. Members only. Returns `Outcome<GatewayRequests>`.
924#[derive(Debug, Serialize, Deserialize)]
925pub struct GatewayRequestsArgs {
926 pub workspace: String,
927 pub viewer: Viewer,
928 /// How many, 50 when absent, 200 at most.
929 #[serde(default)]
930 pub limit: Option<u32>,
931 /// Only requests older than this one (a request's `id`), for the next page.
932 #[serde(default)]
933 pub before: Option<String>,
934}
935
936/// One AI Gateway request, as its log keeps it.
937#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
938#[serde(rename_all = "camelCase")]
939pub struct GatewayRequest {
940 pub id: String,
941 /// RFC 3339.
942 pub created_at: String,
943 pub model: String,
944 pub token_id: String,
945 pub token_name: Option<String>,
946 pub input: u64,
947 pub output: u64,
948 pub cache_read: u64,
949 pub cache_write: u64,
AI Gateway: OpenAI's format, open models, and your own providers950 /// Of `cache_write`, those that live an hour.
951 #[serde(default)]
952 pub cache_write_hour: u64,
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens953 /// What the tokens cost at the model's price.
954 pub cost_micros: i64,
955 /// What the workspace was charged for it, before included usage and
956 /// credit paid for it: 0 on its own key.
957 pub charged_micros: i64,
958 pub status: u16,
959 pub own_key: bool,
AI Gateway: OpenAI's format, open models, and your own providers960 /// `anthropic` or `openai`: the format it was sent in.
961 #[serde(default = "anthropic_format")]
962 pub format: String,
963 /// Who served it: `anthropic` or `workers-ai` on g1t's key, the
964 /// connection's provider on the workspace's own.
965 #[serde(default)]
966 pub provider: String,
967 /// On the workspace's own provider: the connection's name.
968 #[serde(default)]
969 pub connection: Option<String>,
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens970 pub streamed: bool,
971 pub duration_ms: u64,
972 pub error: Option<String>,
973}
974
975/// A page of AI Gateway requests.
976#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
977#[serde(rename_all = "camelCase")]
978pub struct GatewayRequests {
979 pub requests: Vec<GatewayRequest>,
980 /// The `before` for the next page, when there is one.
981 pub next: Option<String>,
982 /// How many days requests are kept.
983 pub retention_days: u32,
984}
985
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look986/// What a workspace pays a monthly price for. There is one plan, `plan`
987/// ("g1t"): a flat price per workspace, never per person, with included
988/// usage each month, more private storage, and deployments. Never free:
989/// `FREE_WHILE_BUILDING` does not cover it.
990///
991/// `deployments` is not sold on its own any more: it comes with the plan.
992/// A service that asks `has_feature` for it is told whether the workspace
993/// has the plan, and a Deployments subscription bought before the change
994/// keeps working until its period ends.
Paid features: a workspace turns on Deployments with a monthly plan995#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
996#[serde(rename_all = "snake_case")]
997pub enum Feature {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look998 /// The g1t plan. Older readers called it `team`.
999 #[serde(alias = "team")]
1000 Plan,
1001 /// Previews per pull request and production on g1t.page: part of the
1002 /// plan.
Paid features: a workspace turns on Deployments with a monthly plan1003 Deployments,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1004 /// The Security and quality activation: the security suite's paid
1005 /// features on private repositories, for a monthly price per workspace
1006 /// from the price book (`security_activation`). Sold on its own; it
1007 /// does not need the plan, and the plan does not include it.
1008 Security,
Paid features: a workspace turns on Deployments with a monthly plan1009}
1010
1011impl Feature {
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1012 /// What is sold: the plan, and the Security and quality activation.
1013 pub const ALL: [Feature; 2] = [Feature::Plan, Feature::Security];
Paid features: a workspace turns on Deployments with a monthly plan1014
1015 pub fn as_str(self) -> &'static str {
1016 match self {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1017 Feature::Plan => "plan",
Paid features: a workspace turns on Deployments with a monthly plan1018 Feature::Deployments => "deployments",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1019 Feature::Security => "security",
Paid features: a workspace turns on Deployments with a monthly plan1020 }
1021 }
1022
1023 pub fn parse(name: &str) -> Option<Feature> {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1024 match name {
1025 "plan" | "team" => Some(Feature::Plan),
1026 "deployments" => Some(Feature::Deployments),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1027 "security" => Some(Feature::Security),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1028 _ => None,
1029 }
Paid features: a workspace turns on Deployments with a monthly plan1030 }
1031
1032 pub fn title(self) -> &'static str {
1033 match self {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1034 Feature::Plan => "g1t",
Paid features: a workspace turns on Deployments with a monthly plan1035 Feature::Deployments => "Deployments",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1036 Feature::Security => "Security and quality",
Paid features: a workspace turns on Deployments with a monthly plan1037 }
1038 }
1039}
1040
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1041/// What deployments cost g1t, in millionths of a dollar: fallbacks for
1042/// when billing's price book cannot be read. Nothing here is an allowance:
1043/// on the plan every unit is metered from the first, at cost plus the
1044/// margin, and drawn from the plan's included usage before anything is
1045/// charged. Projects, previews and the apps behind them are not metered at
1046/// all: Cloudflare's Workers for Platforms includes far more scripts than
1047/// g1t runs, so an app costs g1t only the requests and CPU it answers with.
1048pub mod deployment_costs {
1049 /// Workers for Platforms: $0.30 per million requests.
Paid features: a workspace turns on Deployments with a monthly plan1050 pub const MICROS_PER_MILLION_REQUESTS: i64 = 300_000;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1051 /// $0.02 per million CPU milliseconds.
Paid features: a workspace turns on Deployments with a monthly plan1052 pub const MICROS_PER_MILLION_CPU_MS: i64 = 20_000;
Deployments: a preview for every pull request, production on g1t.page1053 /// What one second of a build's sandbox costs g1t (Cloudflare
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1054 /// Containers, standard-1: half a vCPU, 4 GiB, 8 GB disk), rounded up,
1055 /// as the price keeper measured it on 2026-10-05 (14.5). Only a
1056 /// fallback: billing charges builds at the price book's `build_second`,
1057 /// which the keeper keeps current.
1058 pub const MICROS_PER_BUILD_SECOND: i64 = 15;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1059 /// What one custom hostname costs g1t a month (Cloudflare for SaaS):
1060 /// $0.10.
Agents and memory, checks and conflicts, profiles, slug renames, custom domains1061 pub const MICROS_PER_DOMAIN_MONTH: i64 = 100_000;
Paid features: a workspace turns on Deployments with a monthly plan1062}
1063
Every sandbox is metered by the second1064/// `record_sandbox`: how long one sandbox ran for a workspace, reported by
Prices are what g1t pays plus 20%, from the first second1065/// the runner when it stops. Every sandbox g1t starts for a workspace
1066/// (agents, reviews, checks, the merge queue, workflow jobs) is metered by
1067/// the second, from the first: recorded once per `reference`, with what it
1068/// cost g1t, and charged at the price book's `sandbox_second` price unless
1069/// `FREE_WHILE_BUILDING`. Deploy builds are charged by the Deployments plan
1070/// instead.
Every sandbox is metered by the second1071/// Returns `Outcome<bool>`: false if that reference was recorded before.
1072#[derive(Debug, Serialize, Deserialize)]
1073#[serde(rename_all = "camelCase")]
1074pub struct RecordSandboxArgs {
1075 pub workspace: String,
1076 pub seconds: u32,
1077 /// What ran, e.g. `Checks on acme/api#12`.
1078 pub description: String,
1079 /// `namespace/name`.
1080 pub repo: Option<String>,
1081 /// Unique to the run.
1082 pub reference: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1083 /// What ran: `agent`, `check`, `workflow` or `queue`. Decides whether
1084 /// g1t's open-source pool may pay for it (checks, workflows and the
1085 /// merge queue on public repositories). Absent: not the pool.
1086 #[serde(default)]
1087 pub kind: Option<ComputeKind>,
1088 /// The vCPU-seconds the sandbox used, when it can tell. With it, the
1089 /// run is priced on its own CPU (`sandbox_base_second` per second plus
1090 /// `sandbox_cpu_second` per vCPU-second); without it, at the average
1091 /// (`sandbox_second`).
1092 #[serde(default, alias = "cpu_seconds")]
1093 pub cpu_seconds: Option<f64>,
1094 /// The reservation the work started under, settled with this cost.
1095 #[serde(default, alias = "reservation_id")]
1096 pub reservation_id: Option<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1097 /// It ran on one of the workspace's self-hosted runners: recorded as
1098 /// self-hosted time, for the minutes, at $0.
1099 #[serde(default, alias = "self_hosted")]
1100 pub self_hosted: bool,
1101 /// The machine it ran on, by label (`g1t-4core`); absent, the standard
1102 /// one. A larger machine's memory and disk cost more each second.
1103 #[serde(default)]
1104 pub instance: Option<String>,
Every sandbox is metered by the second1105}
1106
Usage limits: unpaid usage can only go so far1107/// How much a workspace has earned g1t's trust with money, which sets how
1108/// far its unpaid usage can go before its work stops.
1109#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1110#[serde(rename_all = "snake_case")]
1111pub enum Trust {
1112 /// No live payment yet: only a little past the free allowances.
1113 New,
1114 /// Has paid g1t real money: the ceiling grows with what it has paid.
1115 Paid,
Two limits, real invoices, trust that grows by itself, sales signals1116 /// Has paid steadily for months, with nothing disputed or declined:
1117 /// the ceiling follows its monthly spend, up to $10,000, by itself.
1118 Established,
Usage limits: unpaid usage can only go so far1119 /// A ceiling g1t set by hand, after talking to the workspace.
1120 Reviewed,
1121 /// g1t's own workspaces: no ceiling.
1122 Internal,
1123}
1124
1125#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1126#[serde(rename_all = "snake_case")]
1127pub enum LimitState {
1128 Ok,
1129 /// Past 80% of the ceiling.
1130 Warning,
1131 /// At or past it: no new sandboxes, builds or app requests.
1132 Stopped,
1133}
1134
1135/// How far a workspace's unpaid usage has gone this month, and where its
1136/// work stops: like Fly's or Cloudflare's limits for new accounts, so no
1137/// one runs up costs g1t cannot collect. Usage counts at what it cost g1t
1138/// or what it is charged, whichever is more, so it counts while g1t is
1139/// free too.
1140#[derive(Clone, Debug, Serialize, Deserialize)]
1141#[serde(rename_all = "camelCase")]
1142pub struct Limit {
1143 pub workspace: String,
Billing accounts, terms and enterprises; g1t is no longer free1144 /// The account that pays, whose usage and payments the limit counts:
1145 /// the workspace's own, or its enterprise's.
1146 #[serde(default)]
1147 pub account: String,
1148 #[serde(default)]
1149 pub account_name: String,
Usage limits: unpaid usage can only go so far1150 pub trust: Trust,
1151 /// Usage this month (UTC) less what was paid this month.
1152 pub exposure_micros: i64,
1153 /// Where work stops: the lower of g1t's ceiling and the owner's own
1154 /// spend limit. None for g1t's own workspaces.
1155 pub ceiling_micros: Option<i64>,
1156 /// The ceiling g1t sets from `trust`.
1157 pub trust_ceiling_micros: Option<i64>,
1158 /// The owner's own monthly limit, if they set one.
1159 pub spend_limit_micros: Option<i64>,
1160 pub state: LimitState,
1161 /// What to tell people when work is stopped or close to it.
1162 pub message: Option<String>,
Two limits, real invoices, trust that grows by itself, sales signals1163 /// Charged this month, which the spend limit is measured against.
1164 #[serde(default)]
1165 pub spent_micros: i64,
1166 /// True while the owners have not chosen a spend limit of their own, so
1167 /// the automatic one applies: $200, or twice last month's spend.
1168 #[serde(default)]
1169 pub default_spend_limit: bool,
1170 /// The most the owners may set their own limit to: g1t's ceiling. To
1171 /// go past it, they contact g1t.
1172 #[serde(default)]
1173 pub available_micros: Option<i64>,
1174 /// How the ceiling grows from here, in a sentence.
1175 #[serde(default)]
1176 pub growth: Option<String>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1177 /// Money paid in advance and not used yet. It raises what can be used
1178 /// before work stops by the same amount, at once.
1179 #[serde(default)]
1180 pub prepaid_micros: i64,
1181 /// The highest ceiling the workspace has ever had. Owners may set their
1182 /// spend limit anywhere up to it (plus what is prepaid) without asking.
1183 #[serde(default)]
1184 pub max_ceiling_micros: Option<i64>,
1185 /// The most the owners may raise the limit to themselves, once, with
1186 /// `raise_once`: twice the highest ceiling. None once it is used.
1187 #[serde(default)]
1188 pub raise_once_micros: Option<i64>,
1189 /// When the one-time raise was used, RFC 3339.
1190 #[serde(default)]
1191 pub raised_at: Option<String>,
1192 /// True in a paid workspace's first billing cycle, when the ceiling is
1193 /// the starting one (`LIMIT_PAID_START_MICROS`).
1194 #[serde(default)]
1195 pub first_month: bool,
Usage, Billing settings and prepaid AI credit; fixes from the UX audit1196 /// The budget's alerts, in percent of the spend limit: some of 50, 75,
1197 /// 90 and 100. Each is emailed to the owners once a month.
1198 #[serde(default)]
1199 pub alert_levels: Vec<u32>,
1200 /// Whether usage pauses at the spend limit (the default). Off, the
1201 /// limit only alerts; g1t's own ceiling still applies.
1202 #[serde(default = "yes")]
1203 pub pause_at_limit: bool,
1204 /// An HTTPS address told of each budget alert with a JSON POST.
1205 #[serde(default)]
1206 pub budget_webhook: Option<String>,
Usage limits: unpaid usage can only go so far1207}
1208
Usage, Billing settings and prepaid AI credit; fixes from the UX audit1209fn yes() -> bool {
1210 true
1211}
1212
Usage limits: unpaid usage can only go so far1213/// `limit`: a workspace's limit, for its members. Returns `Outcome<Limit>`.
1214#[derive(Debug, Serialize, Deserialize)]
1215pub struct LimitArgs {
1216 pub workspace: String,
1217 pub viewer: Viewer,
1218}
1219
1220/// `check_limit`: the same, for the services that enforce it. Returns
1221/// `Outcome<Limit>`.
1222#[derive(Debug, Serialize, Deserialize)]
1223pub struct CheckLimitArgs {
1224 pub workspace: String,
1225}
1226
Prices keep themselves current with what g1t pays1227/// `note_pending`: usage this month that will be charged later, such as
1228/// app traffic past a plan, so the workspace's limit counts it now. Each
1229/// report replaces the last for that workspace, source and month. Called
1230/// by the service that meters it. Returns `bool`.
1231#[derive(Debug, Serialize, Deserialize)]
1232#[serde(rename_all = "camelCase")]
1233pub struct NotePendingArgs {
1234 pub workspace: String,
Fast pages, required checks on the branch, self-hosted runners, honest incidents1235 /// `deployments`, `security` (scans), `context` (search embeddings),
1236 /// `storage` or `cache` (actions/cache, plan only). Billing charges
1237 /// `security`, `context`, `storage` and `cache` itself once the month
1238 /// is over; `deployments` charges its own.
Prices keep themselves current with what g1t pays1239 pub source: String,
1240 /// What it cost g1t so far this month, before the margin.
1241 pub cost_micros: i64,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1242 /// How much of it, for the Billing page: `1.2 million requests and
1243 /// 3.4 million CPU ms`, `2 custom domains`.
1244 #[serde(default)]
1245 pub detail: Option<String>,
Prices keep themselves current with what g1t pays1246}
1247
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1248/// `usage_meters`: this month's usage for a workspace, one line per kind
1249/// of meter, at what it is charged (cost plus the margin, on the account's
1250/// terms) before the plan's included usage, the trial or g1t's pools paid
1251/// for any of it. Members only. Returns `Outcome<Vec<MeterUsage>>`.
1252#[derive(Debug, Serialize, Deserialize)]
1253pub struct UsageMetersArgs {
1254 pub workspace: String,
1255 pub viewer: Viewer,
1256}
1257
1258/// One kind of meter's usage this month.
1259#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1260#[serde(rename_all = "camelCase")]
1261pub struct MeterUsage {
1262 /// `agents` (agent runs, models and sandboxes for checks, workflows
1263 /// and the merge queue), `builds`, `requests` (app requests and CPU),
1264 /// `domains`, `git_storage` (git operations and private storage) or
1265 /// `search_scans` (search embeddings and security scans).
1266 pub key: String,
1267 pub label: String,
1268 /// At price, before what paid for it.
1269 pub micros: i64,
1270 /// How much, when it is known: `12 runs`, `41 build minutes`.
1271 #[serde(default)]
1272 pub quantity: Option<String>,
1273}
1274
Usage limits: unpaid usage can only go so far1275/// `set_spend_limit`: the owner's own monthly ceiling, under g1t's; None
1276/// removes it. Owners only. Returns `Outcome<Limit>`.
1277#[derive(Debug, Serialize, Deserialize)]
1278#[serde(rename_all = "camelCase")]
1279pub struct SetSpendLimitArgs {
1280 pub actor: User,
1281 pub workspace: String,
Two limits, real invoices, trust that grows by itself, sales signals1282 /// A monthly limit, at most what is available; None goes back to the
1283 /// default.
Usage limits: unpaid usage can only go so far1284 pub spend_limit_micros: Option<i64>,
Two limits, real invoices, trust that grows by itself, sales signals1285 /// Use everything available, with no limit of their own.
1286 #[serde(default)]
1287 pub use_full_limit: bool,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1288 /// Use the one-time raise: up to twice the highest ceiling the
1289 /// workspace has had, without asking. Once per workspace.
1290 #[serde(default, alias = "raiseOnce")]
1291 pub raise_once: bool,
Usage limits: unpaid usage can only go so far1292}
1293
Prices keep themselves current with what g1t pays1294/// One metered unit: what it costs g1t, and what it is sold at. The price
1295/// is always `cost × (100 + markup) / 100`, so it follows the cost.
1296#[derive(Clone, Debug, Serialize, Deserialize)]
1297#[serde(rename_all = "camelCase")]
1298pub struct Price {
1299 /// `sandbox_second`, `build_second`, `app_requests`, `app_cpu`, `app_month`.
1300 pub meter: String,
1301 pub title: String,
1302 pub unit: String,
1303 /// Millionths of a dollar per unit; may have a fraction.
1304 pub cost_micros: f64,
1305 pub markup_percent: u32,
1306 pub price_micros: f64,
1307 /// `list`: Cloudflare's published price. `cloudflare`: what Cloudflare
1308 /// actually billed g1t, measured.
1309 pub source: String,
1310 /// When it was last checked against Cloudflare's bill.
1311 pub checked_at: Option<String>,
1312 pub updated_at: String,
1313}
1314
1315impl Price {
1316 pub fn price_for(cost_micros: f64, markup_percent: u32) -> f64 {
1317 cost_micros * f64::from(100 + markup_percent) / 100.0
1318 }
1319}
1320
1321/// A cost that moved.
1322#[derive(Clone, Debug, Serialize, Deserialize)]
1323#[serde(rename_all = "camelCase")]
1324pub struct PriceChange {
1325 pub meter: String,
1326 pub old_cost_micros: f64,
1327 pub new_cost_micros: f64,
1328 pub markup_percent: u32,
Prices are what g1t pays plus 20%, from the first second1329 /// The markup before, when the change was to the markup rather than
1330 /// to the cost. Absent when the markup stayed `markup_percent`.
1331 #[serde(default, skip_serializing_if = "Option::is_none")]
1332 pub old_markup_percent: Option<u32>,
Prices keep themselves current with what g1t pays1333 pub reason: String,
1334 pub created_at: String,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1335 /// When a change still to come takes effect: a rise is announced
1336 /// before it is charged. Absent for changes already made.
1337 #[serde(default, skip_serializing_if = "Option::is_none")]
1338 pub effective_at: Option<String>,
Prices keep themselves current with what g1t pays1339}
1340
1341/// `prices`: every metered price and the recent changes. Public. Returns
1342/// `PriceBook`.
1343#[derive(Clone, Debug, Serialize, Deserialize)]
1344#[serde(rename_all = "camelCase")]
1345pub struct PriceBook {
1346 pub prices: Vec<Price>,
1347 pub changes: Vec<PriceChange>,
1348 /// The margin on model usage, which is charged at what AI Gateway
1349 /// priced each request at.
1350 pub model_margin_percent: u32,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1351 /// Every plan, as it is sold now.
1352 #[serde(default)]
1353 pub plans: Vec<Plan>,
1354 /// What is free, and what pays for it.
1355 #[serde(default)]
1356 pub free: Option<FreeTier>,
Prices keep themselves current with what g1t pays1357}
1358
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1359/// What g1t gives without a plan, each with what pays for it: a capped
1360/// budget, never an open-ended allowance.
1361#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1362#[serde(rename_all = "camelCase")]
1363pub struct FreeTier {
1364 /// Each new workspace's trial credit, once.
1365 pub trial_workspace_micros: i64,
1366 /// Trial grants each month, in all; new trials wait when it is spent.
1367 pub trial_monthly_pool_micros: i64,
1368 /// g1t's open-source pool each month, and any one repository's share.
1369 pub oss_pool_micros: i64,
1370 pub oss_repo_micros: i64,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1371 /// Private repository storage that is free for every workspace. Past
1372 /// it, the plan pays at cost plus the margin; a free workspace's pushes
1373 /// to private repositories stop instead.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1374 pub free_private_storage_bytes: i64,
Audit logs are kept by plan: a week on free, 90 days on the plan, and what staff set for an account in sudo1375 /// Days of audit log a free workspace keeps.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1376 pub audit_retention_days: u32,
Audit logs are kept by plan: a week on free, 90 days on the plan, and what staff set for an account in sudo1377 /// Days of audit log the g1t plan keeps, and g1t's own and enterprise
1378 /// workspaces. Longer is by arrangement, set per account in sudo.
1379 #[serde(default)]
1380 pub plan_audit_retention_days: u32,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1381 /// The smallest amount a card is charged when a month closes; less
1382 /// carries over. Charges at a limit always go through.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1383 pub min_charge_micros: i64,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1384 /// Git operations (clones, fetches and pushes through g1t) that are
1385 /// free for every workspace each month. Past it, the plan pays at cost
1386 /// plus the margin and is never slowed; a free workspace is slowed
1387 /// down, never charged.
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1388 #[serde(default)]
1389 pub git_operations_included: u64,
1390 /// A new paid workspace's ceiling in its first month.
1391 #[serde(default)]
1392 pub paid_start_ceiling_micros: i64,
1393 /// The most a one-click goodwill credit can cost g1t.
1394 #[serde(default)]
1395 pub overage_forgive_cost_micros: i64,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1396}
1397
Billing accounts, terms and enterprises; g1t is no longer free1398/// Who pays: a billing account. Every workspace has one; by default its
1399/// own. An enterprise account pays for several workspaces at once, as
1400/// GitHub Enterprise does: one bill, one limit, one set of terms.
1401#[derive(Clone, Debug, Serialize, Deserialize)]
1402#[serde(rename_all = "camelCase")]
1403pub struct BillingAccount {
1404 /// `ws_<slug>` for a workspace's own account; `ent_…` for an enterprise.
1405 pub id: String,
1406 pub kind: AccountKind,
1407 pub name: String,
1408 pub terms: Terms,
1409 /// The workspaces it pays for.
1410 pub workspaces: Vec<String>,
Stripe webhooks, enterprise invoices, and sudo for both1411 /// Where an enterprise's invoices go.
1412 #[serde(default)]
1413 pub billing_email: Option<String>,
1414 /// An enterprise's invoices, newest first. Empty for a workspace's own.
1415 #[serde(default)]
1416 pub invoices: Vec<EnterpriseInvoice>,
Billing accounts, terms and enterprises; g1t is no longer free1417 pub created_at: String,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1418 /// What g1t staff set for the account beyond its terms.
1419 #[serde(default)]
1420 pub allowances: Allowances,
1421}
1422
1423/// Set per account by g1t staff in sudo, on top of its terms.
1424#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1425#[serde(rename_all = "camelCase")]
1426pub struct Allowances {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1427 /// The g1t plan without paying for its monthly price, such as for a
1428 /// partner. Usage is charged as usual. Comped accounts have it anyway.
1429 #[serde(default, alias = "team")]
1430 pub plan: bool,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1431 /// Each of the account's public repositories' monthly cap on g1t's
1432 /// open-source pool, in place of `OSS_REPO_MICROS`. None: the default.
1433 #[serde(default)]
1434 pub oss_repo_micros: Option<i64>,
1435 /// The trial credit each of its workspaces gets, in place of
1436 /// `TRIAL_WORKSPACE_MICROS`, outside the monthly pool. None: the default.
1437 #[serde(default)]
1438 pub trial_micros: Option<i64>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1439 /// Agents at once, in place of the plan's (2 in the first month or on
1440 /// the trial, then 10). None: the default.
1441 #[serde(default)]
1442 pub max_concurrent_agents: Option<u32>,
1443 /// One run's spend cap, in place of `RUN_CAP_MICROS` and the owners'
1444 /// own. None: theirs, or the default.
1445 #[serde(default)]
1446 pub run_cap_micros: Option<i64>,
1447 /// What the agents on one issue may spend in all, in place of
1448 /// `ISSUE_CAP_MICROS` and the owners' own. None: theirs, or the default.
1449 #[serde(default)]
1450 pub issue_cap_micros: Option<i64>,
Audit logs are kept by plan: a week on free, 90 days on the plan, and what staff set for an account in sudo1451 /// Days of audit log its workspaces keep, in place of the plan's (7
1452 /// free, 90 on the plan), longer or shorter. None: the plan's.
1453 #[serde(default)]
1454 pub audit_retention_days: Option<u32>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1455 /// A hold g1t staff put on new compute, with why. None: no hold.
1456 #[serde(default)]
1457 pub hold: Option<String>,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1458}
1459
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1460/// `admin_set_allowances`: the plan on or off without charge, overrides of
1461/// the plan's caps, a hold, and the account's share of g1t's pools.
1462/// Recorded with who and why. Returns `Outcome<BillingAccount>`.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1463#[derive(Debug, Serialize, Deserialize)]
1464pub struct AdminSetAllowancesArgs {
1465 pub id: String,
1466 pub allowances: Allowances,
1467 pub note: String,
1468 pub by: String,
1469}
1470
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1471// --- Entitlements, and compute started under a reservation -----------------
1472//
1473// Every service that starts compute (sandboxes for agents, checks,
1474// workflows and the merge queue; builds; models; semantic search) asks
1475// billing first:
1476//
1477// 1. `entitlements { workspace }` says what the workspace may do at all:
1478// its plan, whether it may start compute, its caps, and whether compute
1479// is paused.
1480// 2. `reserve { workspace, repo, public, kind, estimate_micros }` holds the
1481// work's estimated cost against what may pay for it, so that starts at
1482// the same moment cannot overshoot the ceiling together. It answers who
1483// pays first, or refuses with a stable code and a message for the owner.
1484// 3. `settle { reservation_id, actual_micros }` releases the hold once the
1485// work is done. The charge itself goes on the ledger the usual way
1486// (`finish_run`, `record_sandbox`, `charge_feature`, `note_pending`).
1487//
1488// A reservation never settled expires after `RESERVATION_HOURS`.
1489
1490/// A reservation that is never settled stops holding after this long.
1491pub const RESERVATION_HOURS: u64 = 3;
1492/// What a ceiling reads as when there is none (g1t's own workspaces): a
1493/// billion dollars, which JavaScript holds exactly.
1494pub const UNLIMITED_MICROS: i64 = 1_000_000_000_000_000;
1495
1496/// What a workspace pays g1t on, as far as compute is concerned.
1497#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1498#[serde(rename_all = "snake_case")]
1499pub enum PlanKind {
1500 /// No plan: the forge is free; compute only from a trial or g1t's
1501 /// open-source pool, after a card check.
1502 Free,
1503 /// The g1t plan, paid for (or given by g1t staff without its price).
1504 Paid,
1505 /// g1t's own workspaces and Flagon's (comped terms): the plan without
1506 /// being charged. Usage is still recorded at what it cost.
1507 Internal,
1508 /// Paid for by an enterprise account, invoiced.
1509 Enterprise,
1510}
1511
1512impl PlanKind {
1513 pub fn as_str(self) -> &'static str {
1514 match self {
1515 PlanKind::Free => "free",
1516 PlanKind::Paid => "paid",
1517 PlanKind::Internal => "internal",
1518 PlanKind::Enterprise => "enterprise",
1519 }
1520 }
1521
1522 /// Whether usage past what is included may be charged (on demand).
1523 pub fn on_demand(self) -> bool {
1524 !matches!(self, PlanKind::Free)
1525 }
1526}
1527
1528/// What compute is for.
1529#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1530#[serde(rename_all = "snake_case")]
1531pub enum ComputeKind {
1532 /// An agent's run: its sandbox and its model.
1533 Agent,
1534 /// Checks on a pull request.
1535 Check,
1536 /// A workflow job.
1537 Workflow,
1538 /// The merge queue's checks.
1539 Queue,
1540 /// A deployment's build.
1541 Deploy,
1542 /// Semantic search: embeddings in the context hub.
1543 Embedding,
1544}
1545
1546impl ComputeKind {
1547 pub fn as_str(self) -> &'static str {
1548 match self {
1549 ComputeKind::Agent => "agent",
1550 ComputeKind::Check => "check",
1551 ComputeKind::Workflow => "workflow",
1552 ComputeKind::Queue => "queue",
1553 ComputeKind::Deploy => "deploy",
1554 ComputeKind::Embedding => "embedding",
1555 }
1556 }
1557
1558 /// Whether g1t's open-source pool may pay for it on a public
1559 /// repository: checks, workflows and the merge queue only.
1560 pub fn open_source_pool(self) -> bool {
1561 matches!(self, ComputeKind::Check | ComputeKind::Workflow | ComputeKind::Queue)
1562 }
1563}
1564
1565/// Who pays first for reserved work. What the first source cannot cover
1566/// falls to the next, in this order.
1567#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1568#[serde(rename_all = "snake_case")]
1569pub enum PaidBy {
1570 /// The plan's included usage this month.
1571 Credit,
1572 /// The workspace's one-time trial credit.
1573 Trial,
1574 /// g1t's open-source pool.
1575 Oss,
1576 /// Charged to the workspace, at cost plus the margin.
1577 OnDemand,
1578}
1579
1580/// `entitlements`: what a workspace may do now, for the services that
1581/// start compute and the pages that show it. Takes `EntitlementsArgs`;
1582/// returns `Entitlements`. No viewer: callers decide who sees it.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1583#[derive(Debug, Serialize, Deserialize)]
1584pub struct EntitlementsArgs {
1585 pub workspace: String,
1586}
1587
Audit logs are kept by plan: a week on free, 90 days on the plan, and what staff set for an account in sudo1588/// `audit_retention`: how many days of audit log each workspace keeps, for
1589/// the events service's daily purge. Takes `AuditRetentionArgs`; returns
1590/// `Vec<AuditRetention>`, one for each workspace asked about.
1591#[derive(Debug, Serialize, Deserialize)]
1592pub struct AuditRetentionArgs {
1593 pub workspaces: Vec<String>,
1594}
1595
1596#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1597pub struct AuditRetention {
1598 pub workspace: String,
1599 pub days: u32,
1600}
1601
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1602#[derive(Clone, Debug, Serialize, Deserialize)]
1603#[serde(rename_all = "camelCase")]
1604pub struct Entitlements {
1605 pub workspace: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1606 pub plan: PlanKind,
1607 /// May start sandboxes, models, deployments and semantic search at all:
1608 /// paid, internal and enterprise workspaces, or a free one with trial
1609 /// credit left. A free workspace may still use the open-source pool
1610 /// for checks, workflows and the merge queue on public repositories
1611 /// after a card check; `reserve` decides that per start.
1612 pub compute: bool,
1613 /// The one-time trial credit left; 0 if none was granted or it is used.
1614 pub trial_micros_left: i64,
1615 /// A card check has been done. The trial and the open-source pool need
1616 /// it.
1617 pub trial_verified: bool,
1618 /// A paid workspace still in its first billing cycle.
1619 pub first_month: bool,
1620 /// Agents at once: 2 in the first month or on the trial, 10 after;
1621 /// staff can override it.
1622 pub max_concurrent_agents: u32,
1623 /// The longest one run may take: 60 minutes in the first month or on
1624 /// the trial; otherwise the guardrails' own caps (`MAX_MINUTES`).
1625 pub max_run_minutes: u32,
1626 /// One run's spend cap (`RUN_CAP_MICROS`, $2 by default); staff can
1627 /// override it.
1628 pub run_cap_micros: i64,
1629 /// What agents may spend on one issue in all (`ISSUE_CAP_MICROS`, $10
1630 /// by default); the owners can set it (`set_caps`), and staff override.
1631 pub issue_cap_micros: i64,
1632 /// Where on-demand work stops: g1t's ceiling on usage not yet paid
1633 /// for. `UNLIMITED_MICROS` for g1t's own workspaces; 0 for a free one,
1634 /// which has no on-demand usage.
1635 pub ceiling_micros: i64,
1636 /// Usage not yet paid for this month, with prepayment taken off.
1637 pub exposure_micros: i64,
1638 /// Why new compute is paused, for the owner: the limit is reached, a
1639 /// spend spike is waiting for an owner to confirm it, or g1t staff put
1640 /// a hold on it. None when it is not.
1641 pub paused: Option<String>,
1642 // What the workspace's plan gives it, for its pages.
1643 /// What open reservations hold now.
1644 #[serde(default)]
1645 pub held_micros: i64,
1646 /// Paid in advance and not used yet.
1647 #[serde(default)]
1648 pub prepaid_micros: i64,
1649 /// The plan's included usage each month, and what of it is used.
1650 #[serde(default)]
1651 pub included_micros: i64,
1652 #[serde(default)]
1653 pub included_used_micros: i64,
Audit logs are kept by plan: a week on free, 90 days on the plan, and what staff set for an account in sudo1654 /// How far back the audit log can be read and exported, and what is
1655 /// kept: the plan's days, or what g1t staff set for the account.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1656 pub audit_retention_days: u32,
Audit logs are kept by plan: a week on free, 90 days on the plan, and what staff set for an account in sudo1657 /// Whether `audit_retention_days` is what staff set for the account
1658 /// rather than the plan's.
1659 #[serde(default)]
1660 pub audit_retention_custom: bool,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1661 /// Private repository storage that is free for every workspace: past
1662 /// it, the plan pays for it and a free workspace's pushes stop.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1663 pub free_private_storage_bytes: i64,
1664 /// The last daily measure of the workspace's private repositories.
1665 pub private_storage_bytes: i64,
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member1666 /// On a paid plan (not Free): storage past the free amounts below is
1667 /// charged, so nothing is refused for it.
1668 #[serde(default)]
1669 pub has_plan: bool,
1670 /// Package storage free for every workspace, public and private: past
1671 /// it, the plan pays for it and a free workspace's pushes are refused.
1672 #[serde(default)]
1673 pub package_public_free_bytes: i64,
1674 #[serde(default)]
1675 pub package_private_free_bytes: i64,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1676 /// What g1t's open-source pool paid for the workspace this month.
1677 pub oss_paid_micros: i64,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1678 /// Deploy build time this month, every second of it metered.
1679 #[serde(default)]
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1680 pub build_seconds_used: u32,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1681 /// Git operations this month, and how many are free for every
1682 /// workspace (past it: metered on the plan, slowed when free).
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1683 #[serde(default)]
1684 pub git_operations: u64,
1685 #[serde(default)]
1686 pub git_operations_included: u64,
1687 /// The smallest amount a card is charged when a month closes.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1688 pub min_charge_micros: i64,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1689 /// A spend spike waiting for an owner, or decided.
1690 #[serde(default)]
1691 pub spike: Option<Spike>,
1692 /// Where usage stands against what is included and the limits, from 50%.
1693 #[serde(default)]
1694 pub alerts: Vec<UsageAlert>,
1695}
1696
1697/// One level reached: 50, 75, 90 or 100 percent of something.
1698#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1699#[serde(rename_all = "camelCase")]
1700pub struct UsageAlert {
1701 /// `included` (the plan's included usage), `spend_limit` (the owners'
1702 /// own limit) or `ceiling` (g1t's, on usage not yet paid for).
1703 pub meter: String,
1704 pub level: u32,
1705 pub used_micros: i64,
1706 pub limit_micros: i64,
1707 pub message: String,
Billing accounts, terms and enterprises; g1t is no longer free1708}
1709
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1710/// An hour's spend well above the workspace's usual pace: new compute
1711/// waits until an owner says to keep going.
1712#[derive(Clone, Debug, Serialize, Deserialize)]
1713#[serde(rename_all = "camelCase")]
1714pub struct Spike {
1715 pub id: String,
1716 /// `open` (waiting for an owner), `continued` (an owner said keep
1717 /// going) or `stopped` (an owner said stop).
1718 pub status: String,
1719 /// The hour's spend when it was found, and the usual hour's.
1720 pub hour_micros: i64,
1721 pub average_micros: i64,
1722 pub detected_at: String,
1723 #[serde(default)]
1724 pub decided_by: Option<String>,
1725 #[serde(default)]
1726 pub decided_at: Option<String>,
1727 /// While continued: until when, unless spend doubles again first.
1728 #[serde(default)]
1729 pub until: Option<String>,
1730}
1731
1732/// `reserve`: holds an estimate of a start's cost before the work starts.
1733/// Returns `Outcome<Reservation>`, or a failure whose code says why not:
1734///
1735/// - `paused`: a spend spike waiting for an owner, or a hold.
1736/// - `limit`: the spend limit or g1t's ceiling would be passed.
1737/// - `not_paid`: no plan, and nothing else pays for this kind of work (or
1738/// no card check yet).
1739/// - `trial_used`: the one-time trial is spent.
1740/// - `oss_pool_empty`: the open-source pool, or the repository's share of
1741/// it, is spent this month.
1742///
1743/// The message says exactly what to do, with the page to do it on (such as
1744/// `/acme/-/billing`).
1745#[derive(Debug, Serialize, Deserialize)]
1746#[serde(rename_all = "camelCase")]
1747pub struct ReserveArgs {
1748 pub workspace: String,
1749 pub repo: RepoPath,
1750 /// Whether the repository is public: the open-source pool pays only for
1751 /// public repositories' checks, workflows and merge queue.
1752 pub public: bool,
1753 pub kind: ComputeKind,
1754 /// The most the work is expected to cost g1t, before the margin, in
1755 /// millionths of a dollar (billing adds the margin, as it does to every
1756 /// charge). For an agent, its model's average plus its sandbox for its
1757 /// whole time cap.
1758 #[serde(alias = "estimate_micros")]
1759 pub estimate_micros: i64,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays1760 /// An agent run on g1t's hosted models (not the workspace's own
1761 /// provider). Unsaid, an agent run is taken to be one. g1t's daily
1762 /// spend breaker pauses these when g1t is paying for them.
1763 #[serde(default, alias = "hosted_model")]
1764 pub hosted_model: Option<bool>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1765}
1766
1767#[derive(Clone, Debug, Serialize, Deserialize)]
1768#[serde(rename_all = "camelCase")]
1769pub struct Reservation {
1770 pub id: String,
1771 pub paid_by: PaidBy,
1772 /// What is held, at cost; less than the estimate when a free
1773 /// workspace's last bit of trial credit is all there is.
1774 #[serde(default)]
1775 pub held_micros: i64,
1776 /// RFC 3339: when the hold lapses if never settled.
1777 #[serde(default)]
1778 pub expires_at: String,
1779}
1780
1781/// `settle`: releases a reservation's hold with what the work cost. The
1782/// charge goes on the ledger the usual way. Safe to repeat. Returns
1783/// `Outcome<bool>`: false if it was settled or had lapsed before.
1784#[derive(Debug, Serialize, Deserialize)]
1785#[serde(rename_all = "camelCase")]
1786pub struct SettleArgs {
1787 #[serde(alias = "reservation_id")]
1788 pub reservation_id: String,
1789 /// What the work cost g1t, before the margin.
1790 #[serde(alias = "actual_micros")]
1791 pub actual_micros: i64,
1792}
1793
1794// --- Card checks, the plan, prepayment -------------------------------------
1795
1796/// `card_check`: starts Stripe's page to save and verify a card: a setup
1797/// with 3-D Secure where the card supports it, which the card's bank sees
1798/// as a $0 or $1 authorization that is never charged. The trial and the
1799/// open-source pool need it, and it is the card the plan uses. Owners only.
1800/// Returns `Outcome<Checkout>`; the page's id comes back to `return_url` as
1801/// `session`, for `confirm_card_check`.
1802#[derive(Debug, Serialize, Deserialize)]
1803#[serde(rename_all = "camelCase")]
1804pub struct CardCheckArgs {
1805 pub actor: User,
1806 pub workspace: String,
1807 #[serde(alias = "return_url")]
1808 pub return_url: String,
1809}
1810
1811/// `confirm_card_check`: records the check once Stripe says the card was
1812/// verified, and grants the trial if the month's pool has room and the card
1813/// has not had one before. Safe to repeat. Returns `Outcome<Entitlements>`.
1814#[derive(Debug, Serialize, Deserialize)]
1815pub struct ConfirmCardCheckArgs {
1816 pub workspace: String,
1817 pub viewer: Viewer,
1818 pub session: String,
1819}
1820
1821// --- Limits: raising them, and spikes ---------------------------------------
1822
1823/// A request to g1t: a higher limit, or help with usage that went past
1824/// what was meant.
1825#[derive(Clone, Debug, Serialize, Deserialize)]
1826#[serde(rename_all = "camelCase")]
1827pub struct LimitRequest {
1828 pub id: String,
1829 pub workspace: String,
1830 /// `limit` (raise my limit) or `overage` (spent more than meant to).
1831 pub kind: String,
1832 /// The limit asked for; for an overage, what they think went wrong.
1833 pub amount_micros: i64,
1834 pub reason: String,
1835 pub expected_monthly_micros: i64,
1836 /// `open`, `approved` or `declined`.
1837 pub status: String,
1838 /// What was approved, which may differ from what was asked.
1839 #[serde(default)]
1840 pub decided_micros: Option<i64>,
1841 #[serde(default)]
1842 pub decided_by: Option<String>,
1843 /// The answer, as the owner sees it.
1844 #[serde(default)]
1845 pub answer: Option<String>,
1846 pub created_by: String,
1847 pub created_at: String,
1848 #[serde(default)]
1849 pub decided_at: Option<String>,
1850}
1851
1852/// `request_limit`: an owner asks g1t for more, or for help with usage past
1853/// what they meant. Answered within one business day, in the app and by
1854/// email. Owners only. Returns `Outcome<LimitRequest>`.
1855#[derive(Debug, Serialize, Deserialize)]
1856#[serde(rename_all = "camelCase")]
1857pub struct RequestLimitArgs {
1858 pub actor: User,
1859 pub workspace: String,
1860 /// `limit` or `overage`.
1861 pub kind: String,
1862 #[serde(alias = "amount_micros")]
1863 pub amount_micros: i64,
1864 pub reason: String,
1865 #[serde(default, alias = "expected_monthly_micros")]
1866 pub expected_monthly_micros: i64,
1867}
1868
1869/// `limit_requests`: a workspace's requests, newest first. Members only.
1870/// Returns `Outcome<Vec<LimitRequest>>`.
1871#[derive(Debug, Serialize, Deserialize)]
1872pub struct LimitRequestsArgs {
1873 pub workspace: String,
1874 pub viewer: Viewer,
1875}
1876
1877/// `confirm_spike`: an owner's answer to a spend spike. Keep going lifts the
1878/// pause for 24 hours, or until the hour's spend doubles again; stop keeps
1879/// new compute paused until an owner says to keep going. Owners only.
1880/// Returns `Outcome<Entitlements>`.
1881#[derive(Debug, Serialize, Deserialize)]
1882#[serde(rename_all = "camelCase")]
1883pub struct ConfirmSpikeArgs {
1884 pub actor: User,
1885 pub workspace: String,
1886 #[serde(alias = "keep_going")]
1887 pub keep_going: bool,
1888}
1889
1890/// `set_caps`: the owners' own caps on agents: one run's spend ($0.10 to
1891/// $100) and what the agents on one issue may spend in all ($1 to $1,000).
1892/// None goes back to the default ($2 and $10). A cap g1t staff set for the
1893/// account wins over both. Owners only. Returns `Outcome<Entitlements>`.
1894#[derive(Debug, Serialize, Deserialize)]
1895#[serde(rename_all = "camelCase")]
1896pub struct SetCapsArgs {
1897 pub actor: User,
1898 pub workspace: String,
1899 #[serde(default, alias = "run_cap_micros")]
1900 pub run_cap_micros: Option<i64>,
1901 #[serde(default, alias = "issue_cap_micros")]
1902 pub issue_cap_micros: Option<i64>,
1903}
1904
1905/// What staff see beside a request: the workspace's history with g1t.
1906#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1907#[serde(rename_all = "camelCase")]
1908pub struct WorkspaceHistory {
1909 pub plan: Option<PlanKind>,
1910 /// The last six months, oldest first.
1911 pub months: Vec<MonthFigures>,
1912 /// Live payments that have cleared, and how many.
1913 pub paid_cleared_micros: i64,
1914 pub payments: u32,
1915 pub disputes: u32,
1916 pub declines: u32,
1917 /// The first time the workspace appears in billing, RFC 3339.
1918 pub first_seen: Option<String>,
1919 pub ceiling_micros: Option<i64>,
1920 pub max_ceiling_micros: Option<i64>,
1921 pub spend_limit_micros: Option<i64>,
1922 /// Recent velocity: the last hour, the usual hour over the last week,
1923 /// and the last 24 hours, at price.
1924 pub last_hour_micros: i64,
1925 pub average_hour_micros: i64,
1926 pub last_day_micros: i64,
1927}
1928
1929#[derive(Clone, Debug, Serialize, Deserialize)]
1930#[serde(rename_all = "camelCase")]
1931pub struct LimitRequestReview {
1932 pub request: LimitRequest,
1933 pub history: WorkspaceHistory,
1934}
1935
1936/// `admin_limit_requests`: requests for staff, oldest open first. Returns
1937/// `Vec<LimitRequestReview>`.
1938#[derive(Debug, Default, Serialize, Deserialize)]
1939pub struct AdminLimitRequestsArgs {
1940 /// `open` (the default), `approved`, `declined` or `all`.
1941 #[serde(default)]
1942 pub status: Option<String>,
1943}
1944
1945/// `admin_decide_limit_request`: approve (at the amount asked, or
1946/// `amount_micros`) or decline. The owner is told in the app and by email.
1947/// Recorded with who and why. Returns `Outcome<LimitRequest>`.
1948#[derive(Debug, Serialize, Deserialize)]
1949pub struct AdminDecideLimitRequestArgs {
1950 pub id: String,
1951 /// `approve` or `decline`.
1952 pub decision: String,
1953 #[serde(default)]
1954 pub amount_micros: Option<i64>,
1955 /// What the owner is told, beside the decision.
1956 #[serde(default)]
1957 pub note: String,
1958 pub by: String,
1959}
1960
1961/// `admin_record_payment`: money that reached g1t outside the card pages,
1962/// such as a bank transfer, entered as a payment (it raises the limit like
1963/// one). Recorded with who and the transfer's reference. Returns
1964/// `Outcome<LedgerEntry>`.
1965#[derive(Debug, Serialize, Deserialize)]
1966pub struct AdminRecordPaymentArgs {
1967 pub workspace: String,
1968 pub amount_micros: i64,
1969 /// The bank's reference for the transfer, or Stripe's payment id.
1970 pub reference: String,
1971 pub note: String,
1972 pub by: String,
1973}
1974
1975// --- Overages and goodwill (sudo) --------------------------------------------
1976
1977/// What a one-time goodwill credit would come to: g1t's margin on the
1978/// overage, always, plus as much of its underlying cost as the cap allows.
1979#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
1980#[serde(rename_all = "camelCase")]
1981pub struct Goodwill {
1982 /// This month's charges above the workspace's typical month.
1983 pub overage_micros: i64,
1984 /// The part of the overage that is g1t's margin.
1985 pub margin_micros: i64,
1986 /// The part that is what g1t paid its providers.
1987 pub cost_micros: i64,
1988 /// The one-click credit: the margin plus the cost up to the cap.
1989 pub credit_micros: i64,
1990 /// Of the credit, the real cost g1t absorbs.
1991 pub absorbed_micros: i64,
1992}
1993
1994/// A workspace whose month went well past its usual, or hit a spike.
1995#[derive(Clone, Debug, Serialize, Deserialize)]
1996#[serde(rename_all = "camelCase")]
1997pub struct Overage {
1998 pub workspace: String,
1999 pub plan: PlanKind,
2000 /// The median of its last three months' charges.
2001 pub typical_month_micros: i64,
2002 pub this_month_micros: i64,
2003 /// What this month cost g1t, and what g1t keeps of it.
2004 pub cost_micros: i64,
2005 pub margin_micros: i64,
2006 /// A spike this month, if there was one.
2007 pub spike: Option<Spike>,
2008 /// The runs that cost the most this month.
2009 pub top_entries: Vec<LedgerEntry>,
2010 pub goodwill: Goodwill,
2011 /// False when a goodwill credit was given in the last 12 months.
2012 pub goodwill_available: bool,
2013 pub last_goodwill_at: Option<String>,
2014 /// An open overage request from the owner, if there is one.
2015 pub request: Option<LimitRequest>,
2016}
2017
2018/// `admin_overages`: the Overages queue. Returns `Vec<Overage>`.
2019#[derive(Debug, Default, Serialize, Deserialize)]
2020pub struct AdminOveragesArgs {}
2021
2022/// `admin_goodwill`: credits a workspace for accidental usage. With no
2023/// amount, the one-click credit (`Goodwill::credit_micros`), once per
2024/// workspace in 12 months. A larger amount, or a second within 12 months,
2025/// needs a typed reason. It shows on the statement as "Credit from g1t:
2026/// accidental usage on <date>". Returns `Outcome<LedgerEntry>`.
2027#[derive(Debug, Serialize, Deserialize)]
2028pub struct AdminGoodwillArgs {
2029 pub workspace: String,
2030 #[serde(default)]
2031 pub amount_micros: Option<i64>,
2032 /// Why, typed by staff; needed past the one-click credit.
2033 #[serde(default)]
2034 pub reason: String,
2035 /// The day the accidental usage happened, `YYYY-MM-DD`; today if absent.
2036 #[serde(default)]
2037 pub day: Option<String>,
2038 pub by: String,
2039}
2040
2041/// One workspace's recent pace, for sudo's velocity view.
2042#[derive(Clone, Debug, Serialize, Deserialize)]
2043#[serde(rename_all = "camelCase")]
2044pub struct Velocity {
2045 pub workspace: String,
2046 pub plan: PlanKind,
2047 pub last_hour_micros: i64,
2048 pub average_hour_micros: i64,
2049 pub last_day_micros: i64,
2050 pub this_month_micros: i64,
2051 /// The last hour over the usual hour; 0 with no history.
2052 pub ratio: f64,
2053 pub spike: Option<Spike>,
2054 pub first_seen: Option<String>,
2055}
2056
2057/// `admin_velocity`: workspaces spending in the last day, fastest first.
2058/// Returns `Vec<Velocity>`.
2059#[derive(Debug, Default, Serialize, Deserialize)]
2060pub struct AdminVelocityArgs {}
2061
Billing accounts, terms and enterprises; g1t is no longer free2062#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2063#[serde(rename_all = "snake_case")]
2064pub enum AccountKind {
2065 Workspace,
2066 Enterprise,
2067}
2068
2069/// How an account is charged. Standard unless g1t set otherwise in sudo.
2070#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
2071#[serde(rename_all = "camelCase")]
2072pub struct Terms {
2073 pub kind: TermsKind,
2074 /// Off every usage charge, in percent. Custom terms only.
2075 #[serde(default)]
2076 pub discount_percent: u32,
2077 /// A ceiling on unpaid usage that replaces the one trust would give.
2078 #[serde(default)]
2079 pub ceiling_micros: Option<i64>,
2080 /// Why, for whoever looks next.
2081 #[serde(default)]
2082 pub note: String,
2083 /// When the terms end and the account goes back to standard.
2084 #[serde(default)]
2085 pub until: Option<String>,
2086 #[serde(default)]
2087 pub set_by: Option<String>,
2088 #[serde(default)]
2089 pub set_at: Option<String>,
2090}
2091
2092impl Terms {
2093 pub fn standard() -> Self {
2094 Terms {
2095 kind: TermsKind::Standard,
2096 discount_percent: 0,
2097 ceiling_micros: None,
2098 note: String::new(),
2099 until: None,
2100 set_by: None,
2101 set_at: None,
2102 }
2103 }
2104
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2105 /// The discount in percent, 0 to 100. Terms from before discounts
2106 /// replaced "comped" read as 100%.
2107 pub fn percent_off(&self) -> u32 {
2108 match self.kind {
2109 TermsKind::Comped => 100,
2110 TermsKind::Custom => self.discount_percent.min(100),
2111 TermsKind::Standard => 0,
2112 }
2113 }
2114
2115 /// A 100% discount: nothing is charged, usage is recorded at its price
2116 /// and discounted in full. g1t's own workspaces and partners. Paid
2117 /// features are on without a plan, and g1t's own spend on it is held to
2118 /// a monthly budget (the terms' ceiling, at cost).
2119 pub fn full_discount(&self) -> bool {
2120 self.percent_off() >= 100
2121 }
2122
Billing accounts, terms and enterprises; g1t is no longer free2123 /// What a charge becomes under these terms.
2124 pub fn apply(&self, charge_micros: i64) -> i64 {
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2125 charge_micros * i64::from(100 - self.percent_off()) / 100
Billing accounts, terms and enterprises; g1t is no longer free2126 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'2127
2128 /// What a charge at cost plus the margin becomes under these terms, and
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2129 /// what the discount took off it (`ledger.discount_micros`), so the
2130 /// statement shows the usage at its price and the discount beside it,
2131 /// and a discount below cost plus the margin is counted as given, never
2132 /// lost. A 100% discount takes it all.
Merge branch 'worktree-agent-a633ac0f7f66d419d'2133 pub fn discounted(&self, charge_micros: i64) -> (i64, i64) {
2134 let charged = self.apply(charge_micros);
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2135 (charged, (charge_micros - charged).max(0))
2136 }
2137
2138 /// How the statement and sudo name the terms: `100% discount`, `30% off`.
2139 pub fn discount_label(&self) -> Option<String> {
2140 match self.percent_off() {
2141 0 => None,
2142 100 => Some("100% discount".to_owned()),
2143 percent => Some(format!("{percent}% off")),
Merge branch 'worktree-agent-a633ac0f7f66d419d'2144 }
2145 }
Billing accounts, terms and enterprises; g1t is no longer free2146}
2147
2148#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2149#[serde(rename_all = "snake_case")]
2150pub enum TermsKind {
2151 /// Prices as published, limits by trust.
2152 Standard,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2153 /// Before discounts: what a 100% discount is now. Read as one
2154 /// (`Terms::percent_off`); billing never writes it (migration 0039).
Billing accounts, terms and enterprises; g1t is no longer free2155 Comped,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2156 /// A discount (up to 100%), a ceiling, or both.
Billing accounts, terms and enterprises; g1t is no longer free2157 Custom,
2158}
2159
Stripe webhooks, enterprise invoices, and sudo for both2160/// `stripe_webhook`: an event from Stripe, as the API received it: the raw
2161/// body and its `Stripe-Signature` header. Billing checks the signature
2162/// against the secret of the endpoint it registered, and handles each
2163/// event once. Returns `Outcome<bool>`: false for one already handled.
2164#[derive(Debug, Serialize, Deserialize)]
2165pub struct StripeWebhookArgs {
2166 pub payload: String,
2167 pub signature: String,
2168}
2169
2170/// `admin_stripe`: where billing stands with Stripe. Staff only. Returns
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard2171/// `StripeStatus`. With `fix: true`, first enables the destination at
2172/// billing's address and gives it the events billing needs.
Stripe webhooks, enterprise invoices, and sudo for both2173#[derive(Debug, Default, Serialize, Deserialize)]
2174pub struct AdminStripeArgs {
2175 #[serde(default)]
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard2176 pub fix: bool,
Stripe webhooks, enterprise invoices, and sudo for both2177 #[serde(default)]
2178 pub by: Option<String>,
2179}
2180
2181#[derive(Clone, Debug, Serialize, Deserialize)]
2182#[serde(rename_all = "camelCase")]
2183pub struct StripeStatus {
2184 /// `test` or `live`, from the key; `off` without one.
2185 pub mode: String,
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard2186 /// Whether `STRIPE_WEBHOOK_SECRET` is set, so events can be checked.
2187 pub secret_set: bool,
2188 /// The destination at billing's address in Stripe, as Stripe has it.
Stripe webhooks, enterprise invoices, and sudo for both2189 pub webhook: Option<StripeWebhook>,
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard2190 /// Events billing handles that the destination does not send.
2191 pub missing_events: Vec<String>,
Stripe webhooks, enterprise invoices, and sudo for both2192 /// The latest events handled, newest first.
2193 pub recent_events: Vec<StripeEventSummary>,
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard2194 /// What went wrong reading or fixing the destination, if it did.
Stripe webhooks, enterprise invoices, and sudo for both2195 pub error: Option<String>,
2196}
2197
2198#[derive(Clone, Debug, Serialize, Deserialize)]
2199#[serde(rename_all = "camelCase")]
2200pub struct StripeWebhook {
2201 pub url: String,
2202 pub endpoint_id: String,
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard2203 /// `enabled` or `disabled`.
2204 pub status: String,
Stripe webhooks, enterprise invoices, and sudo for both2205 pub events: Vec<String>,
2206 pub created_at: String,
2207}
2208
2209#[derive(Clone, Debug, Serialize, Deserialize)]
2210#[serde(rename_all = "camelCase")]
2211pub struct StripeEventSummary {
2212 pub id: String,
2213 pub kind: String,
2214 pub outcome: String,
2215 pub received_at: String,
2216}
2217
2218/// `admin_enterprise_billing`: where an enterprise's invoices go. Creates
2219/// or updates its Stripe customer. Returns `Outcome<BillingAccount>`.
2220#[derive(Debug, Serialize, Deserialize)]
2221pub struct AdminEnterpriseBillingArgs {
2222 pub id: String,
2223 pub email: String,
2224 pub by: String,
2225}
2226
Merge Stripe Tax, the card fee on card payments, and one free workspace per person2227/// `admin_enterprise_address`: the enterprise's billing address and tax ID,
2228/// saved on its Stripe customer (made by `admin_enterprise_billing`).
2229/// Stripe Tax works its invoices' tax out from the address; an invoice is
2230/// not sent without one. Returns `Outcome<bool>`.
2231#[derive(Debug, Serialize, Deserialize)]
2232#[serde(rename_all = "camelCase")]
2233pub struct AdminEnterpriseAddressArgs {
2234 pub id: String,
2235 pub address: PostalAddress,
2236 #[serde(default, alias = "tax_id_type")]
2237 pub tax_id_type: Option<String>,
2238 #[serde(default, alias = "tax_id")]
2239 pub tax_id: Option<String>,
2240 pub by: String,
2241}
2242
Stripe webhooks, enterprise invoices, and sudo for both2243/// `admin_invoice_enterprise`: sends an enterprise its invoice now, for
2244/// what its workspaces owe, rather than waiting for the month to close.
2245/// Returns `Outcome<EnterpriseInvoice>`.
2246#[derive(Debug, Serialize, Deserialize)]
2247pub struct AdminInvoiceEnterpriseArgs {
2248 pub id: String,
2249 pub by: String,
2250}
2251
2252/// An enterprise's invoice: one line per workspace, paid on Stripe.
2253#[derive(Clone, Debug, Serialize, Deserialize)]
2254#[serde(rename_all = "camelCase")]
2255pub struct EnterpriseInvoice {
2256 pub invoice_id: String,
2257 /// Stripe's page for it, where it is paid.
2258 pub hosted_url: Option<String>,
2259 pub amount_micros: i64,
2260 /// `open`, `paid`, `overdue` or `void`.
2261 pub status: String,
2262 pub period: String,
2263 pub lines: Vec<InvoiceLine>,
2264 pub created_at: String,
2265}
2266
2267#[derive(Clone, Debug, Serialize, Deserialize)]
2268#[serde(rename_all = "camelCase")]
2269pub struct InvoiceLine {
2270 pub workspace: String,
2271 pub amount_micros: i64,
2272}
2273
Two limits, real invoices, trust that grows by itself, sales signals2274/// A workspace's invoice from g1t: one per month, and one each time it is
2275/// charged near its limit. Itemised, charged to the card on file, and kept
2276/// in Stripe's billing page with its PDF.
2277#[derive(Clone, Debug, Serialize, Deserialize)]
2278#[serde(rename_all = "camelCase")]
2279pub struct WorkspaceInvoice {
2280 pub invoice_id: String,
2281 pub workspace: String,
2282 /// `month` (2026-10) or `threshold`.
2283 pub reason: String,
2284 pub period: String,
2285 pub amount_micros: i64,
2286 /// `paid`, `open`, `failed` or `void`.
2287 pub status: String,
2288 pub hosted_url: Option<String>,
2289 pub pdf_url: Option<String>,
2290 pub lines: Vec<InvoiceItem>,
2291 pub created_at: String,
Merge Stripe Tax, the card fee on card payments, and one free workspace per person2292 /// The card processing fee on top of `amount_micros`, when the invoice
2293 /// is charged to a card; never part of the usage it pays for.
2294 #[serde(default)]
2295 pub fee_micros: i64,
2296 /// The tax Stripe added on top, once it is known (after paying).
2297 #[serde(default)]
2298 pub tax_micros: i64,
Two limits, real invoices, trust that grows by itself, sales signals2299}
2300
2301#[derive(Clone, Debug, Serialize, Deserialize)]
2302#[serde(rename_all = "camelCase")]
2303pub struct InvoiceItem {
2304 pub description: String,
2305 pub amount_micros: i64,
2306}
2307
2308/// `invoices`: a workspace's invoices from g1t, newest first. Members
2309/// only. Returns `Outcome<Vec<WorkspaceInvoice>>`.
2310#[derive(Debug, Serialize, Deserialize)]
2311pub struct InvoicesArgs {
2312 pub workspace: String,
2313 pub viewer: Viewer,
2314}
2315
2316/// `admin_workspace_invoices`: the same, for staff. Returns
2317/// `Vec<WorkspaceInvoice>`.
2318#[derive(Debug, Serialize, Deserialize)]
2319pub struct AdminWorkspaceInvoicesArgs {
2320 pub workspace: String,
2321}
2322
The statement is a month at a time, a line per kind of charge2323/// `statement`: a month of a workspace's ledger, grouped by day (or by
2324/// project) with a line per kind of charge. Members only. Returns
2325/// `Outcome<Statement>`.
2326#[derive(Debug, Serialize, Deserialize)]
2327pub struct StatementArgs {
2328 pub workspace: String,
2329 pub viewer: Viewer,
2330 /// YYYY-MM; this month when absent.
2331 #[serde(default)]
2332 pub month: Option<String>,
2333 /// `day` (the default) or `project`.
2334 #[serde(default)]
2335 pub group: Option<String>,
2336}
2337
2338#[derive(Clone, Debug, Serialize, Deserialize)]
2339#[serde(rename_all = "camelCase")]
2340pub struct Statement {
2341 pub month: String,
2342 /// Months with any entries, newest first.
2343 pub months: Vec<String>,
2344 pub groups: Vec<StatementGroup>,
2345 pub totals: StatementTotals,
2346}
2347
2348#[derive(Clone, Debug, Serialize, Deserialize)]
2349#[serde(rename_all = "camelCase")]
2350pub struct StatementGroup {
2351 /// The day (YYYY-MM-DD) or the project (`owner/name`, or empty).
2352 pub key: String,
2353 pub label: String,
2354 pub lines: Vec<StatementLine>,
2355 /// What the group's charges come to.
2356 pub charged_micros: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2357 /// Its usage at price, and what the discount took off it.
2358 #[serde(default)]
2359 pub price_micros: i64,
2360 #[serde(default)]
2361 pub discount_micros: i64,
The statement is a month at a time, a line per kind of charge2362}
2363
2364#[derive(Clone, Debug, Serialize, Deserialize)]
2365#[serde(rename_all = "camelCase")]
2366pub struct StatementLine {
2367 /// Agent runs, Sandbox time, Deployments, Payments, Credits from g1t,
Prices are what g1t pays plus 20%, from the first second2368 /// Refunds, and, for older entries, Runs on your own model provider.
The statement is a month at a time, a line per kind of charge2369 pub kind: String,
2370 pub count: u32,
2371 /// Charges positive; money in (payments, credits) negative.
2372 pub charged_micros: i64,
2373 pub cost_micros: i64,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put2374 /// Of the usage on the line, what was paid for before it was charged:
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2375 /// by the plan's included usage, the trial credit, g1t's open-source
2376 /// pool, or g1t itself. Not in `charged_micros`.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put2377 #[serde(default)]
2378 pub covered_micros: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2379 /// Usage at its price: charged, plus what paid for it and what the
2380 /// discount took off. Zero for money in.
2381 #[serde(default)]
2382 pub price_micros: i64,
2383 /// What the account's discount took off the line's price.
2384 #[serde(default)]
2385 pub discount_micros: i64,
Merge Stripe Tax, the card fee on card payments, and one free workspace per person2386 /// On the `Tax` and `Card processing fees` lines: what was paid with
2387 /// payments on top of what reached the balance (negative for what a
2388 /// refund gave back). Never in `charged_micros` or the balance.
2389 #[serde(default)]
2390 pub passed_micros: i64,
The statement is a month at a time, a line per kind of charge2391}
2392
2393#[derive(Clone, Debug, Serialize, Deserialize)]
2394#[serde(rename_all = "camelCase")]
2395pub struct StatementTotals {
2396 pub charged_micros: i64,
2397 pub paid_micros: i64,
2398 pub cost_micros: i64,
2399 pub entries: u32,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2400 /// Usage at price, and what the discount took off it: charged is the
2401 /// price less the discount and what paid for it.
2402 #[serde(default)]
2403 pub price_micros: i64,
2404 #[serde(default)]
2405 pub discount_micros: i64,
2406 /// The account's discount now, in percent; absent without one.
2407 #[serde(default)]
2408 pub discount_percent: Option<u32>,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put2409 /// What paid for usage before it was charged, one line per source,
2410 /// such as "Paid by g1t's open-source pool".
2411 #[serde(default)]
2412 pub covered: Vec<Covered>,
2413 /// Owed when the month closed but under the minimum charge, so it
2414 /// carries over to the next invoice. Zero when nothing carried.
2415 #[serde(default)]
2416 pub carried_micros: i64,
Merge Stripe Tax, the card fee on card payments, and one free workspace per person2417 /// Tax and card processing fees paid with the month's payments, on top
2418 /// of `paid_micros`.
2419 #[serde(default)]
2420 pub tax_micros: i64,
2421 #[serde(default)]
2422 pub card_fee_micros: i64,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put2423}
2424
2425/// One source that paid for usage before it was charged.
2426#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
2427#[serde(rename_all = "camelCase")]
2428pub struct Covered {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2429 /// `included`, `trial`, `oss_pool` or `given`.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put2430 pub source: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2431 /// "Paid by your plan's included usage", "Paid by your trial credit",
2432 /// "Paid by g1t's open-source pool", "Covered by g1t".
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put2433 pub label: String,
2434 pub micros: i64,
The statement is a month at a time, a line per kind of charge2435}
2436
2437/// `statement_entries`: one statement line's entries, newest first, 50 at
2438/// a time (`before` = the last id seen). Returns `Outcome<Vec<LedgerEntry>>`.
2439#[derive(Debug, Serialize, Deserialize)]
2440pub struct StatementEntriesArgs {
2441 pub workspace: String,
2442 pub viewer: Viewer,
2443 pub month: String,
2444 pub kind: String,
2445 #[serde(default)]
2446 pub day: Option<String>,
2447 #[serde(default)]
2448 pub project: Option<String>,
2449 #[serde(default)]
2450 pub before: Option<String>,
2451}
2452
Two limits, real invoices, trust that grows by itself, sales signals2453// --- Sales (sudo.g1t.sh) ------------------------------------------------------
2454//
2455// What staff need to know to reach out: who is growing, who is close to
2456// their limit, who was declined, who has become a steady customer. And what
2457// was done about it: a stage, an owner on g1t's side, a next step, notes.
2458
2459/// Why a workspace is worth a look.
2460#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2461#[serde(rename_all = "snake_case")]
2462pub enum SignalKind {
2463 /// At its limit, or its own spend limit: work is stopped.
2464 AtLimit,
2465 /// Past 80% of what is available to it: about to need more.
2466 NearCeiling,
2467 /// Its card was declined or a payment disputed.
2468 Declined,
2469 /// This month is well ahead of last month.
2470 Growing,
2471 /// Became Established: the ceiling now follows its spend.
2472 Established,
2473 /// Paid g1t for the first time.
2474 FirstPayment,
2475 /// Spending enough that custom terms or an enterprise may suit it.
2476 HighSpend,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2477 /// Costs g1t more on Cloudflare than it pays, over 30 days: a pricing
2478 /// gap or abuse to look at (billing's `margin`).
2479 CostOverRevenue,
Two limits, real invoices, trust that grows by itself, sales signals2480}
2481
2482#[derive(Clone, Debug, Serialize, Deserialize)]
2483#[serde(rename_all = "camelCase")]
2484pub struct Signal {
2485 pub workspace: String,
2486 pub kind: SignalKind,
2487 /// One sentence, with the figures.
2488 pub detail: String,
2489 /// The figure that matters, such as this month's spend.
2490 pub value_micros: i64,
2491 /// Its sales stage, if staff gave it one.
2492 pub stage: Option<String>,
2493 pub owner: Option<String>,
Billing lists every invoice and every staff change; signals carry follow-ups2494 #[serde(default)]
2495 pub next_step: Option<String>,
2496 /// When the next step is due, `YYYY-MM-DD`.
2497 #[serde(default)]
2498 pub next_at: Option<String>,
2499}
2500
2501/// `admin_invoices`: every invoice g1t has sent, workspaces' and
2502/// enterprises', newest first. Returns `Vec<InvoiceSummary>`.
2503#[derive(Debug, Default, Serialize, Deserialize)]
2504pub struct AdminInvoicesArgs {
2505 /// `paid`, `open`, `failed`, `overdue` or `void`.
2506 #[serde(default)]
2507 pub status: Option<String>,
2508 /// YYYY-MM, by when it was sent.
2509 #[serde(default)]
2510 pub month: Option<String>,
2511}
2512
2513#[derive(Clone, Debug, Serialize, Deserialize)]
2514#[serde(rename_all = "camelCase")]
2515pub struct InvoiceSummary {
2516 pub invoice_id: String,
2517 /// `workspace` or `enterprise`.
2518 pub kind: String,
2519 /// The workspace's slug, or the enterprise's account id.
2520 pub account: String,
2521 /// What to call it: the workspace, or the enterprise's name.
2522 pub name: String,
2523 pub reason: String,
2524 pub period: String,
2525 pub amount_micros: i64,
2526 pub status: String,
2527 pub hosted_url: Option<String>,
2528 pub created_at: String,
2529 pub paid_at: Option<String>,
2530}
2531
2532/// `admin_audit`: every change made in sudo, and by Stripe, newest first.
2533/// Returns `Vec<AdminAction>`.
2534#[derive(Debug, Default, Serialize, Deserialize)]
2535pub struct AdminAuditArgs {
2536 #[serde(default)]
2537 pub by: Option<String>,
2538 #[serde(default)]
2539 pub action: Option<String>,
2540 /// Only those before this time, for paging.
2541 #[serde(default)]
2542 pub before: Option<String>,
Two limits, real invoices, trust that grows by itself, sales signals2543}
2544
2545/// `admin_signals`: every workspace worth reaching out to, most urgent
2546/// first. Returns `Vec<Signal>`.
2547#[derive(Debug, Default, Serialize, Deserialize)]
2548pub struct AdminSignalsArgs {}
2549
2550/// What staff are doing about a workspace.
2551#[derive(Clone, Debug, Serialize, Deserialize)]
2552#[serde(rename_all = "camelCase")]
2553pub struct SalesRecord {
2554 pub workspace: String,
2555 /// `none`, `lead`, `contacted`, `negotiating`, `won`, `lost` or `churn_risk`.
2556 pub stage: String,
2557 /// The staff member looking after it.
2558 pub owner: Option<String>,
2559 pub next_step: Option<String>,
2560 /// RFC 3339 date.
2561 pub next_at: Option<String>,
2562 pub notes: Vec<SalesNote>,
2563 pub updated_at: Option<String>,
2564}
2565
2566#[derive(Clone, Debug, Serialize, Deserialize)]
2567#[serde(rename_all = "camelCase")]
2568pub struct SalesNote {
2569 pub id: String,
2570 pub text: String,
2571 pub by: String,
2572 pub created_at: String,
2573}
2574
2575/// `admin_sales`: a workspace's sales record. Returns `SalesRecord`.
2576#[derive(Debug, Serialize, Deserialize)]
2577pub struct AdminSalesArgs {
2578 pub workspace: String,
2579}
2580
2581/// `admin_set_sales`: its stage, owner and next step. Returns `Outcome<SalesRecord>`.
2582#[derive(Debug, Serialize, Deserialize)]
2583pub struct AdminSetSalesArgs {
2584 pub workspace: String,
2585 pub stage: String,
2586 #[serde(default)]
2587 pub owner: Option<String>,
2588 #[serde(default)]
2589 pub next_step: Option<String>,
2590 #[serde(default)]
2591 pub next_at: Option<String>,
2592 pub by: String,
2593}
2594
2595/// `admin_add_note`. Returns `Outcome<SalesRecord>`.
2596#[derive(Debug, Serialize, Deserialize)]
2597pub struct AdminAddNoteArgs {
2598 pub workspace: String,
2599 pub text: String,
2600 pub by: String,
2601}
2602
2603/// `admin_overview`: the business at a glance. Returns `Overview`.
2604#[derive(Debug, Default, Serialize, Deserialize)]
2605pub struct AdminOverviewArgs {}
2606
2607#[derive(Clone, Debug, Serialize, Deserialize)]
2608#[serde(rename_all = "camelCase")]
2609pub struct Overview {
2610 /// YYYY-MM.
2611 pub month: String,
2612 /// The last six months, oldest first, all workspaces together.
2613 pub months: Vec<MonthFigures>,
2614 /// This month by kind of usage: models, sandbox, deployments, plans.
2615 pub by_kind: Vec<KindFigures>,
2616 pub paying_workspaces: u32,
2617 pub stopped: u32,
2618 pub near_ceiling: u32,
2619 pub declined: u32,
2620 /// Sent and not yet paid, workspaces and enterprises.
2621 pub open_invoices_micros: i64,
2622 /// Follow-ups due today or earlier.
2623 pub follow_ups_due: u32,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put2624 /// The capped budgets g1t pays from, this month.
2625 #[serde(default)]
2626 pub pools: Option<Pools>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2627 /// This month's revenue: usage charged plus the plan's price paid.
2628 #[serde(default)]
2629 pub revenue_micros: i64,
2630 /// Workspaces on the paid plan now, and what their price comes to a
2631 /// month.
2632 #[serde(default)]
2633 pub active_plans: u32,
2634 #[serde(default)]
2635 pub plan_mrr_micros: i64,
2636 /// What g1t gave this month, by source, apart from its margin.
2637 #[serde(default)]
2638 pub given: Vec<GivenFigures>,
2639 /// g1t's own and Flagon's workspaces this month: what their use cost,
2640 /// and why they are not charged.
2641 #[serde(default)]
2642 pub internal: Vec<InternalUse>,
2643 /// Open limit requests, and workspaces in the Overages queue.
2644 #[serde(default)]
2645 pub open_requests: u32,
2646 #[serde(default)]
2647 pub overages: u32,
2648 /// Spend spikes waiting for an owner.
2649 #[serde(default)]
2650 pub open_spikes: u32,
Two limits, real invoices, trust that grows by itself, sales signals2651}
2652
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2653/// What g1t gave this month from one source.
2654#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2655#[serde(rename_all = "camelCase")]
2656pub struct GivenFigures {
2657 /// `internal`, `trial`, `oss_pool`, `goodwill` or `covered`.
2658 pub source: String,
2659 pub label: String,
2660 /// At price, and what it cost g1t.
2661 pub micros: i64,
2662 pub cost_micros: i64,
2663}
2664
2665/// One internal workspace's use this month.
2666#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2667#[serde(rename_all = "camelCase")]
2668pub struct InternalUse {
2669 pub workspace: String,
2670 /// Why it is not charged: its terms' note.
2671 pub reason: String,
2672 pub cost_micros: i64,
2673 pub entries: u32,
2674}
2675
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put2676/// g1t's capped budgets for free usage, this calendar month (UTC).
2677#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2678#[serde(rename_all = "camelCase")]
2679pub struct Pools {
2680 /// YYYY-MM.
2681 pub month: String,
2682 /// Trial grants made this month, against the month's pool.
2683 pub trial_granted_micros: i64,
2684 pub trial_pool_micros: i64,
2685 pub trial_grants: u32,
2686 /// What the open-source pool paid this month, against its cap.
2687 pub oss_used_micros: i64,
2688 pub oss_pool_micros: i64,
2689 /// Each public repository's monthly cap on the pool.
2690 pub oss_repo_micros: i64,
2691}
2692
Two limits, real invoices, trust that grows by itself, sales signals2693#[derive(Clone, Debug, Serialize, Deserialize)]
2694#[serde(rename_all = "camelCase")]
2695pub struct KindFigures {
2696 pub kind: String,
2697 pub charged_micros: i64,
2698 pub cost_micros: i64,
2699}
2700
Billing accounts, terms and enterprises; g1t is no longer free2701// --- Staff (sudo.g1t.sh) ------------------------------------------------------
2702//
2703// Called only by the sudo app, which only g1t staff can reach (behind
2704// Cloudflare Access). Each change names who made it, and is kept in the
2705// audit log.
2706
2707/// `admin_accounts`: every billing account, with where each stands this
2708/// month. Returns `Vec<AccountSummary>`.
2709#[derive(Debug, Default, Serialize, Deserialize)]
2710pub struct AdminAccountsArgs {
2711 #[serde(default)]
2712 pub query: Option<String>,
Stripe webhooks, enterprise invoices, and sudo for both2713 /// Exactly these workspaces' accounts, such as one page of sudo's
2714 /// list; every account with activity when absent.
2715 #[serde(default)]
2716 pub workspaces: Option<Vec<String>>,
Billing accounts, terms and enterprises; g1t is no longer free2717}
2718
2719#[derive(Clone, Debug, Serialize, Deserialize)]
2720#[serde(rename_all = "camelCase")]
2721pub struct AccountSummary {
2722 pub account: BillingAccount,
2723 pub limit: Limit,
2724 /// Charged this month, after terms.
2725 pub charged_micros: i64,
2726 /// What this month's usage cost g1t.
2727 pub cost_micros: i64,
2728 /// Paid, ever.
2729 pub paid_micros: i64,
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace2730 /// The same figures for each of the account's workspaces that has
2731 /// any, so staff can see what one member of an enterprise used.
2732 #[serde(default)]
2733 pub by_workspace: Vec<WorkspaceFigures>,
Two limits, real invoices, trust that grows by itself, sales signals2734 /// The last six months, oldest first, for trends.
2735 #[serde(default)]
2736 pub months: Vec<MonthFigures>,
2737}
2738
2739/// One month of an account's billing.
2740#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2741#[serde(rename_all = "camelCase")]
2742pub struct MonthFigures {
2743 /// YYYY-MM.
2744 pub month: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2745 /// Usage charged, after what paid for it first.
Two limits, real invoices, trust that grows by itself, sales signals2746 pub charged_micros: i64,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2747 /// What usage cost g1t: only what g1t paid for, never a workspace's own
2748 /// model provider.
Two limits, real invoices, trust that grows by itself, sales signals2749 pub cost_micros: i64,
2750 pub paid_micros: i64,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2751 /// The plan's monthly price, paid.
2752 #[serde(default)]
2753 pub plans_micros: i64,
2754 /// What g1t gave, at price: internal (comped) use, trials, the
2755 /// open-source pool, goodwill credits and what g1t covered. Not margin.
2756 #[serde(default)]
2757 pub given_micros: i64,
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace2758}
2759
2760/// One workspace's share of an [`AccountSummary`].
2761#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2762#[serde(rename_all = "camelCase")]
2763pub struct WorkspaceFigures {
2764 pub workspace: String,
2765 pub charged_micros: i64,
2766 pub cost_micros: i64,
2767 pub paid_micros: i64,
Billing accounts, terms and enterprises; g1t is no longer free2768}
2769
2770/// `admin_account`: one account in full. Returns `Outcome<AccountDetail>`.
2771#[derive(Debug, Serialize, Deserialize)]
2772pub struct AdminAccountArgs {
2773 /// An account id, or a workspace slug.
2774 pub id: String,
2775}
2776
2777#[derive(Clone, Debug, Serialize, Deserialize)]
2778#[serde(rename_all = "camelCase")]
2779pub struct AccountDetail {
2780 pub summary: AccountSummary,
2781 /// Each workspace's limit, for an enterprise.
2782 pub workspaces: Vec<Limit>,
2783 pub ledger: Vec<LedgerEntry>,
2784 pub audit: Vec<AdminAction>,
2785}
2786
2787/// `admin_set_terms`. Returns `Outcome<BillingAccount>`.
2788#[derive(Debug, Serialize, Deserialize)]
2789pub struct AdminSetTermsArgs {
2790 pub id: String,
2791 pub terms: Terms,
2792 pub by: String,
2793}
2794
2795/// `admin_create_enterprise`. Returns `Outcome<BillingAccount>`.
2796#[derive(Debug, Serialize, Deserialize)]
2797pub struct AdminCreateEnterpriseArgs {
2798 pub name: String,
2799 pub workspaces: Vec<String>,
2800 pub by: String,
2801}
2802
2803/// `admin_attach`: moves a workspace onto an enterprise account, or back
2804/// onto its own with `account: None`. Returns `Outcome<BillingAccount>`.
2805#[derive(Debug, Serialize, Deserialize)]
2806pub struct AdminAttachArgs {
2807 pub workspace: String,
2808 pub account: Option<String>,
2809 pub by: String,
2810}
2811
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2812/// Why g1t gave a workspace credit.
2813#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
2814#[serde(rename_all = "snake_case")]
2815pub enum CreditKind {
2816 /// Marketing: a welcome, a referral, an event. Given away when spent.
2817 Promotional,
2818 /// An apology, or accidental usage forgiven. Given away when spent.
2819 #[default]
2820 Goodwill,
2821 /// Money back for something that went wrong. Not given away: it gives
2822 /// back money already paid, so it comes off what was paid on the day
2823 /// it refunds, and what it pays for later is paid for.
2824 Refund,
2825 /// Bought by the workspace (prepaid AI): money paid in up front, owed
2826 /// as usage until spent. What it pays for is paid for, never given.
2827 /// Staff never give it; its ledger line is a payment, not `crd…`.
2828 Purchased,
2829}
2830
2831impl CreditKind {
2832 pub fn as_str(self) -> &'static str {
2833 match self {
2834 CreditKind::Promotional => "promotional",
2835 CreditKind::Goodwill => "goodwill",
2836 CreditKind::Refund => "refund",
2837 CreditKind::Purchased => "purchased",
2838 }
2839 }
2840
2841 pub fn parse(text: &str) -> Option<CreditKind> {
2842 match text {
2843 "promotional" => Some(CreditKind::Promotional),
2844 "goodwill" => Some(CreditKind::Goodwill),
2845 "refund" => Some(CreditKind::Refund),
2846 "purchased" => Some(CreditKind::Purchased),
2847 _ => None,
2848 }
2849 }
2850
2851 /// As people read it: `Promotional`.
2852 pub fn label(self) -> &'static str {
2853 match self {
2854 CreditKind::Promotional => "Promotional",
2855 CreditKind::Goodwill => "Goodwill",
2856 CreditKind::Refund => "Refund",
2857 CreditKind::Purchased => "Purchased",
2858 }
2859 }
2860}
2861
2862/// `admin_credit`: credit g1t gives a workspace: promotional, goodwill or a
2863/// refund, with a note, and optionally an expiry. It is spent before
2864/// anything paid in advance, the soonest-expiring first. The workspace's
2865/// owners are emailed. Returns `Outcome<LedgerEntry>`.
Billing accounts, terms and enterprises; g1t is no longer free2866#[derive(Debug, Serialize, Deserialize)]
2867pub struct AdminCreditArgs {
2868 pub workspace: String,
2869 pub amount_micros: i64,
2870 pub note: String,
2871 pub by: String,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2872 #[serde(default)]
2873 pub kind: CreditKind,
2874 /// RFC 3339; unused credit stops counting then. Never for a refund.
2875 #[serde(default)]
2876 pub expires_at: Option<String>,
2877 /// A refund: what it refunds, in a line, and the day of it
2878 /// (`YYYY-MM-DD`; today if absent).
2879 #[serde(default)]
2880 pub refund_for: Option<String>,
2881 #[serde(default)]
2882 pub refund_day: Option<String>,
2883}
2884
2885/// One credit g1t gave, with what of it was used: spent on usage, the
2886/// soonest-expiring grant first, before anything paid in advance.
2887#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2888#[serde(rename_all = "camelCase")]
2889pub struct CreditGrant {
2890 /// `crd_…`, the grant's ledger reference.
2891 pub id: String,
2892 pub workspace: String,
2893 pub kind: CreditKind,
2894 pub amount_micros: i64,
2895 pub used_micros: i64,
2896 /// What can still be spent: nothing once it expired or was revoked.
2897 pub left_micros: i64,
2898 pub note: String,
2899 #[serde(default)]
2900 pub refund_for: Option<String>,
2901 #[serde(default)]
2902 pub refund_day: Option<String>,
2903 pub expires_at: Option<String>,
2904 pub created_by: String,
2905 pub created_at: String,
2906 /// `open`, `used`, `expired` or `revoked`.
2907 pub state: String,
2908 #[serde(default)]
2909 pub closed_at: Option<String>,
2910 #[serde(default)]
2911 pub closed_note: Option<String>,
2912 #[serde(default)]
2913 pub closed_by: Option<String>,
2914 /// What expiring or revoking took off the balance.
2915 #[serde(default)]
2916 pub closed_micros: i64,
2917 /// What it pays for: `all` usage, or `models` only (agent runs' model
2918 /// cost), which is spent first.
2919 #[serde(default)]
2920 pub scope: String,
2921 /// Where it came from: `staff`, `purchase` or `promo_code`.
2922 #[serde(default)]
2923 pub source: String,
2924}
2925
2926/// `credits` (`Outcome<Credits>`, `AccountArgs`): a workspace's credits from
2927/// g1t, newest first, for its members.
2928#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2929#[serde(rename_all = "camelCase")]
2930pub struct Credits {
2931 pub grants: Vec<CreditGrant>,
2932 /// What is left to spend, in all.
2933 pub left_micros: i64,
2934}
2935
2936/// `admin_credits`: every credit g1t gave, newest first, filtered. Returns
2937/// `AdminCredits`.
2938#[derive(Debug, Default, Serialize, Deserialize)]
2939pub struct AdminCreditsArgs {
2940 #[serde(default)]
2941 pub workspace: Option<String>,
2942 #[serde(default)]
2943 pub kind: Option<CreditKind>,
2944 /// `YYYY-MM`: given that month.
2945 #[serde(default)]
2946 pub month: Option<String>,
2947 /// Given by this member of staff.
2948 #[serde(default)]
2949 pub by: Option<String>,
2950}
2951
2952/// One month's credits of one kind: given, used on usage that month, and
2953/// taken back unused (expired or revoked).
2954#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2955#[serde(rename_all = "camelCase")]
2956pub struct CreditMonth {
2957 pub month: String,
2958 pub kind: CreditKind,
2959 pub given_micros: i64,
2960 pub grants: u32,
2961 pub used_micros: i64,
2962 pub expired_micros: i64,
2963 pub revoked_micros: i64,
2964}
2965
2966#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2967#[serde(rename_all = "camelCase")]
2968pub struct AdminCredits {
2969 /// At most 200.
2970 pub grants: Vec<CreditGrant>,
2971 /// The last 12 months, newest first, whatever the month filter.
2972 pub months: Vec<CreditMonth>,
2973 /// Who has given credit, for the filter.
2974 pub staff: Vec<String>,
2975}
2976
2977/// `admin_revoke_credit`: what is left of a grant, taken off the balance,
2978/// with why. Returns `Outcome<CreditGrant>`.
2979#[derive(Debug, Serialize, Deserialize)]
2980pub struct AdminRevokeCreditArgs {
2981 pub id: String,
2982 pub note: String,
2983 pub by: String,
Billing accounts, terms and enterprises; g1t is no longer free2984}
2985
sudo: reset a test workspace's billing so it starts again as a new customer; refused on a live Stripe key, for comped workspaces and for an enterprise's2986/// `admin_reset_billing`: a test workspace's billing wiped, so it starts
2987/// again as a new customer. Only while billing runs on Stripe's test key;
2988/// never a comped workspace or one an enterprise pays for. `confirm` is the
2989/// workspace's slug typed out. Returns `Outcome<BillingReset>`.
2990#[derive(Debug, Serialize, Deserialize)]
2991pub struct AdminResetBillingArgs {
2992 pub workspace: String,
2993 pub confirm: String,
2994 pub note: String,
2995 pub by: String,
2996}
2997
2998/// What a reset removed.
2999#[derive(Clone, Debug, Serialize, Deserialize)]
3000#[serde(rename_all = "camelCase")]
3001pub struct BillingReset {
3002 pub workspace: String,
3003 pub rows: u32,
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 sold3004 /// Whether the costs analysis ran again after it, so the margin
3005 /// figures no longer hold the workspace's past usage.
3006 #[serde(default)]
3007 pub refreshed: bool,
sudo: reset a test workspace's billing so it starts again as a new customer; refused on a live Stripe key, for comped workspaces and for an enterprise's3008}
3009
Billing accounts, terms and enterprises; g1t is no longer free3010/// One change made in sudo.
3011#[derive(Clone, Debug, Serialize, Deserialize)]
3012#[serde(rename_all = "camelCase")]
3013pub struct AdminAction {
3014 pub id: String,
3015 pub account: String,
3016 pub action: String,
3017 pub detail: String,
3018 pub by: String,
3019 pub created_at: String,
3020}
3021
Paid features: a workspace turns on Deployments with a monthly plan3022/// What a feature's plan costs and includes.
3023#[derive(Clone, Debug, Serialize, Deserialize)]
3024#[serde(rename_all = "camelCase")]
3025pub struct Plan {
3026 pub feature: Feature,
3027 pub title: String,
Merge Stripe Tax, the card fee on card payments, and one free workspace per person3028 /// Charged every month while the plan is on, in cents, excluding tax.
Paid features: a workspace turns on Deployments with a monthly plan3029 pub monthly_cents: u32,
Merge Stripe Tax, the card fee on card payments, and one free workspace per person3030 /// The card processing fee on top each month, in cents (0 when the
3031 /// fee is off). Excluding tax, like the price.
3032 #[serde(default)]
3033 pub card_fee_cents: u32,
Paid features: a workspace turns on Deployments with a monthly plan3034 /// What the monthly price includes, one line each, for people to read.
3035 pub includes: Vec<String>,
3036 /// How usage past the allowance is charged, for people to read.
3037 pub overage: String,
3038}
3039
3040#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
3041#[serde(rename_all = "snake_case")]
3042pub enum SubscriptionStatus {
3043 /// Paid up; the feature works.
3044 Active,
3045 /// Paid up to the end of the period, and ends then.
3046 Canceling,
3047 /// The last payment failed; the feature is off until it is paid.
3048 PastDue,
3049 /// Ended.
3050 Canceled,
3051}
3052
3053impl SubscriptionStatus {
3054 /// Whether the feature works in this state.
3055 pub fn on(self) -> bool {
3056 matches!(self, SubscriptionStatus::Active | SubscriptionStatus::Canceling)
3057 }
3058}
3059
3060/// A workspace's plan for one feature.
3061#[derive(Clone, Debug, Serialize, Deserialize)]
3062#[serde(rename_all = "camelCase")]
3063pub struct Subscription {
3064 pub feature: Feature,
3065 pub status: SubscriptionStatus,
3066 /// RFC 3339: when the period paid for ends, and the plan renews or
3067 /// ends.
3068 pub period_end: Option<String>,
3069 /// Username of whoever turned it on.
3070 pub started_by: String,
3071 /// RFC 3339.
3072 pub started_at: String,
3073}
3074
3075/// A feature as a workspace sees it: what it costs, and its plan if it has
3076/// one.
3077#[derive(Clone, Debug, Serialize, Deserialize)]
3078#[serde(rename_all = "camelCase")]
3079pub struct FeatureState {
3080 pub plan: Plan,
3081 pub subscription: Option<Subscription>,
3082 /// Whether the feature works for the workspace now.
3083 pub on: bool,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put3084 /// On without a plan: comped terms, or given by g1t. Nothing to pay
3085 /// and nothing to turn off.
3086 #[serde(default)]
3087 pub included: bool,
Paid features: a workspace turns on Deployments with a monthly plan3088}
3089
3090/// `features`: every paid feature and the workspace's plan for each.
3091/// Members only. Returns `Outcome<Vec<FeatureState>>`.
3092#[derive(Debug, Serialize, Deserialize)]
3093pub struct FeaturesArgs {
3094 pub workspace: String,
3095 pub viewer: Viewer,
3096}
3097
3098/// `subscribe`: starts the card page for a feature's monthly plan. Owners
3099/// only. Returns `Outcome<Checkout>`; the page's id comes back to
3100/// `return_url` as `session`, for `confirm_subscription`.
3101#[derive(Debug, Serialize, Deserialize)]
3102#[serde(rename_all = "camelCase")]
3103pub struct SubscribeArgs {
3104 pub actor: User,
3105 pub workspace: String,
3106 pub feature: Feature,
3107 pub return_url: String,
3108}
3109
3110/// `confirm_subscription`: turns the feature on once the processor says
3111/// the plan was paid for. Safe to call any number of times. Returns
3112/// `Outcome<FeatureState>`.
3113#[derive(Debug, Serialize, Deserialize)]
3114pub struct ConfirmSubscriptionArgs {
3115 pub workspace: String,
3116 pub viewer: Viewer,
3117 pub session: String,
3118}
3119
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member3120/// `admin_log`: a staff change another service made to a workspace, kept
3121/// in sudo's audit log with billing's own (`admin_audit`). For identity's
3122/// restores and purges of deleted workspaces. Returns `bool`.
3123#[derive(Debug, Serialize, Deserialize)]
3124pub struct AdminLogArgs {
3125 pub workspace: String,
3126 pub action: String,
3127 pub detail: String,
3128 /// The staff member's email.
3129 pub by: String,
3130}
3131
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look3132/// `close_workspace`: settles a workspace that is about to be deleted.
3133/// Owners only. Refused while it has an invoice that failed, while it
3134/// holds prepaid credit, or while it owes money it cannot be charged for
3135/// now; otherwise what it owes is invoiced to its card at once (no
3136/// minimum), its plan is cancelled at Stripe straight away, and its
3137/// account is marked closed, so the month-end close, autopay and limit
3138/// warnings pass it by. Its ledger, invoices and statements stay. With
3139/// `dry_run`, only says whether it could, changing nothing. Returns
3140/// `Outcome<bool>`.
3141#[derive(Debug, Serialize, Deserialize)]
3142#[serde(rename_all = "camelCase")]
3143pub struct CloseWorkspaceArgs {
3144 pub actor: User,
3145 pub workspace: String,
3146 #[serde(default)]
3147 pub dry_run: bool,
3148}
3149
Paid features: a workspace turns on Deployments with a monthly plan3150/// `cancel_subscription` (`resume` false) ends a plan at the end of the
3151/// period paid for; with `resume` true, takes that back. Owners only.
3152/// Returns `Outcome<FeatureState>`.
3153#[derive(Debug, Serialize, Deserialize)]
3154pub struct CancelSubscriptionArgs {
3155 pub actor: User,
3156 pub workspace: String,
3157 pub feature: Feature,
3158 #[serde(default)]
3159 pub resume: bool,
3160}
3161
3162/// `has_feature`: whether a feature works for a workspace now, asked by the
3163/// service that provides it before doing paid work. Returns
3164/// `Outcome<bool>`: a failure, with the reason to show, when it does not.
3165/// True everywhere when no card processor is configured.
3166#[derive(Debug, Serialize, Deserialize)]
3167pub struct HasFeatureArgs {
3168 pub workspace: String,
3169 pub feature: Feature,
3170}
3171
Merge Stripe Tax, the card fee on card payments, and one free workspace per person3172/// `free_workspaces`: which of `workspaces` are free, that is on no paid
3173/// plan. Paid is the g1t plan, an enterprise's terms, or a discount of
3174/// 100% (g1t's own workspaces). Identity asks before a workspace is made
3175/// (a person owns at most one free workspace) and before anyone is added
3176/// to one (a free workspace cannot invite). Returns `Vec<String>`, the
3177/// free ones, lower-cased; none where payments are not set up.
3178#[derive(Debug, Serialize, Deserialize)]
3179pub struct FreeWorkspacesArgs {
3180 pub workspaces: Vec<String>,
3181}
3182
Paid features: a workspace turns on Deployments with a monthly plan3183/// `charge_feature`: usage of a feature past its plan's allowance, charged
3184/// from the workspace's credit at cost plus the margin, whatever
3185/// `FREE_WHILE_BUILDING` says. Called by the service that provides it.
3186/// Charged once per `reference`. Returns `Outcome<bool>`: false if that
3187/// reference was charged before.
3188#[derive(Debug, Serialize, Deserialize)]
3189#[serde(rename_all = "camelCase")]
3190pub struct ChargeFeatureArgs {
3191 pub workspace: String,
3192 pub feature: Feature,
3193 /// What it cost g1t, in millionths of a dollar, before the margin.
3194 pub cost_micros: i64,
3195 pub description: String,
3196 /// `namespace/name`, when the usage was one repository's.
3197 pub repo: Option<String>,
3198 /// Unique to this charge, e.g. `deployments/acme/2026-10`.
3199 pub reference: String,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put3200 /// For a build: how long it ran. The plan's included build time this
3201 /// month pays for what it can, and only the rest of `cost_micros` is
3202 /// charged.
3203 #[serde(default)]
3204 pub build_seconds: Option<u32>,
Paid features: a workspace turns on Deployments with a monthly plan3205}
3206
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily3207// ---------------------------------------------------------------------
3208// Costs and margin: what Cloudflare charges g1t against what g1t
3209// charges (billing's costs.rs, margin.rs and pricing.rs). Staff only.
3210// ---------------------------------------------------------------------
3211
3212/// `admin_costs`: the Costs & margin page. Returns `CostsReport`.
3213#[derive(Debug, Default, Serialize, Deserialize)]
3214pub struct AdminCostsArgs {
3215 /// How many days back, 7 to 90; 30 when absent.
3216 #[serde(default)]
3217 pub days: Option<u32>,
3218}
3219
3220/// One of g1t's products on one day.
3221#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3222#[serde(rename_all = "camelCase")]
3223pub struct CostDay {
3224 pub day: String,
3225 pub bucket: String,
3226 /// What Cloudflare charged g1t.
3227 pub cf_cost_micros: i64,
3228 /// What g1t's meters recorded it cost, at the price book's cost.
3229 pub own_cost_micros: i64,
3230 /// What customers were charged for it at price, before included
3231 /// usage, trials and pools paid for some.
3232 pub value_micros: i64,
3233 /// Of that, what workspaces paid.
3234 pub cash_micros: i64,
3235}
3236
3237/// One product over the range.
3238#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3239#[serde(rename_all = "camelCase")]
3240pub struct ProductMargin {
3241 pub bucket: String,
3242 pub title: String,
3243 /// The cost the margin is taken from: Cloudflare's bill, or g1t's own
3244 /// figure for what Cloudflare does not bill (models).
3245 pub cost_micros: i64,
3246 pub cf_cost_micros: i64,
3247 pub own_cost_micros: i64,
3248 pub value_micros: i64,
3249 pub margin_micros: i64,
3250 pub margin_percent: Option<f64>,
3251 /// `cloudflare` or `ledger`.
3252 pub cost_source: String,
3253 /// Running g1t itself, paid for by the plan.
3254 pub overhead: bool,
3255}
3256
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it3257/// All of g1t over the range: money in against every cost, and against
3258/// the cost of what was sold (every cost less what g1t gave away).
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily3259#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3260#[serde(rename_all = "camelCase")]
3261pub struct OverallMargin {
3262 /// What workspaces paid for usage, and for the plan.
3263 pub usage_micros: i64,
3264 pub plans_micros: i64,
3265 pub cost_micros: i64,
3266 pub margin_micros: i64,
3267 pub margin_percent: Option<f64>,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it3268 /// Of `cost_micros`, what went on usage g1t gave away on purpose:
3269 /// comped workspaces, free periods, the trial and the open-source pool.
3270 #[serde(default)]
3271 pub given_micros: i64,
3272 /// Money in against `cost_micros - given_micros`.
3273 #[serde(default)]
3274 pub sold_margin_micros: i64,
3275 #[serde(default)]
3276 pub sold_margin_percent: Option<f64>,
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 running3277 /// What was sold, apart: usage (`usage_micros` against what that usage
3278 /// cost, less what was given), running g1t (`plans_micros` against the
3279 /// platform's cost, less its given share) and what no mapping names.
3280 #[serde(default)]
3281 pub usage_cost_micros: i64,
3282 #[serde(default)]
3283 pub usage_margin_micros: i64,
3284 #[serde(default)]
3285 pub usage_margin_percent: Option<f64>,
3286 #[serde(default)]
3287 pub running_cost_micros: i64,
3288 #[serde(default)]
3289 pub unmapped_cost_micros: i64,
3290 /// `given_micros` by why: comped workspaces, free use (free periods,
3291 /// free allowances, overruns g1t covered), the trial, the open-source pool.
3292 #[serde(default)]
3293 pub given_comped_micros: i64,
3294 #[serde(default)]
3295 pub given_free_micros: i64,
3296 #[serde(default)]
3297 pub given_trial_micros: i64,
3298 #[serde(default)]
3299 pub given_pool_micros: i64,
Merge branch 'worktree-agent-a633ac0f7f66d419d'3300 /// What discounts on an account's terms took below cost plus the
3301 /// margin: given, so a discounted sale is not margin lost.
3302 #[serde(default)]
3303 pub given_discount_micros: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging3304 /// Credits from g1t spent on usage, by kind: given, so usage paid for
3305 /// with them is never money in. Refunds are not here: they come off
3306 /// money in on the day they refund.
3307 #[serde(default)]
3308 pub given_credit_promotional_micros: i64,
3309 #[serde(default)]
3310 pub given_credit_goodwill_micros: i64,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb973311 /// What testing resets wiped that g1t paid for (`reset_costs`): the
3312 /// model calls and Cloudflare usage still happened, so their cost is
3313 /// given, never a leak.
3314 #[serde(default)]
3315 pub given_reset_micros: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging3316 /// Credits over the range: given (every kind), spent on usage, and
3317 /// refunds' money given back.
3318 #[serde(default)]
3319 pub credits_given_micros: i64,
3320 #[serde(default)]
3321 pub credits_used_micros: i64,
3322 #[serde(default)]
3323 pub credits_refunded_micros: i64,
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 running3324 /// `cost_micros` by who g1t pays: Cloudflare's bill (billed amounts,
3325 /// after the included allowances), and model providers (the ledger's
3326 /// cost of the tokens, which Cloudflare's bill does not show).
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)3327 /// What the plan's included usage paid for, at price (the ledger's
3328 /// `credit_micros`, comped workspaces left out): money in for usage,
3329 /// paid out of `plans_micros`.
3330 #[serde(default)]
3331 pub included_micros: i64,
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 running3332 #[serde(default)]
3333 pub cloudflare_cost_micros: i64,
3334 #[serde(default)]
3335 pub models_cost_micros: i64,
Merge Stripe Tax, the card fee on card payments, and one free workspace per person3336 /// Tax collected with payments over the range, net of refunds: owed to
3337 /// the tax authorities, never in cash or revenue.
3338 #[serde(default)]
3339 pub tax_collected_micros: i64,
3340 /// Card processing fees passed on with card payments, net of refunds:
3341 /// they pay Stripe's fee, so they are not revenue either.
3342 #[serde(default)]
3343 pub card_fees_micros: i64,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily3344}
3345
3346/// A count, cost or leak that does not add up.
3347#[derive(Clone, Debug, Serialize, Deserialize)]
3348#[serde(rename_all = "camelCase")]
3349pub struct CostDrift {
3350 pub bucket: String,
3351 pub title: String,
3352 /// `count` (units g1t counted against Cloudflare's), `cost` (the bill
Merge branch 'worktree-agent-a633ac0f7f66d419d'3353 /// against the price book's cost of the same usage; for models, what AI
3354 /// Gateway priced g1t's provider traffic at against the ledger's model
3355 /// cost), `unpriced` (model usage AI Gateway put no price on, so its
3356 /// cost is not the providers'), or `leak`.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily3357 pub kind: String,
3358 pub ours: f64,
3359 pub cloudflare: f64,
3360 pub delta_percent: Option<f64>,
3361 pub detail: String,
3362 pub found_at: String,
3363}
3364
3365/// A margin alert, open while its condition lasts.
3366#[derive(Clone, Debug, Serialize, Deserialize)]
3367#[serde(rename_all = "camelCase")]
3368pub struct MarginAlert {
3369 pub id: String,
3370 /// `margin`, `overall`, `leak`, `drift` or `workspace`.
3371 pub kind: String,
3372 /// The product, or the workspace.
3373 pub subject: String,
3374 pub detail: String,
3375 pub since: String,
3376 pub opened_at: String,
3377 pub emailed_at: Option<String>,
3378}
3379
3380/// A change to a price the reconciler measured.
3381#[derive(Clone, Debug, Serialize, Deserialize)]
3382#[serde(rename_all = "camelCase")]
3383pub struct PriceProposal {
3384 pub id: String,
3385 pub meter: String,
3386 pub title: String,
3387 pub unit: String,
3388 pub current_cost_micros: f64,
3389 pub proposed_cost_micros: f64,
3390 pub change_percent: f64,
3391 pub markup_percent: u32,
3392 pub reason: String,
3393 /// `keeper` or `reconciler`.
3394 pub source: String,
3395 /// Far off the current cost: look before approving.
3396 pub suspect: bool,
3397 /// `open`, `applied`, `approved`, `rejected` or `superseded`.
3398 pub status: String,
3399 pub created_at: String,
3400 pub decided_at: Option<String>,
3401 pub decided_by: Option<String>,
3402 pub note: Option<String>,
3403 /// When it takes or took effect, once approved or applied.
3404 pub effective_at: Option<String>,
3405}
3406
3407/// One version of one meter's price. Never changed once written.
3408#[derive(Clone, Debug, Serialize, Deserialize)]
3409#[serde(rename_all = "camelCase")]
3410pub struct PriceVersion {
3411 pub id: String,
3412 pub meter: String,
3413 pub version: u32,
3414 pub cost_micros: f64,
3415 pub markup_percent: u32,
3416 pub price_micros: f64,
3417 pub effective_at: String,
3418 pub reason: String,
3419 pub created_by: String,
3420 /// When the price book took it on; absent while it waits for its date.
3421 pub applied_at: Option<String>,
3422}
3423
3424/// What a workspace cost g1t over the range, Cloudflare's costs shared
3425/// out by g1t's own meters, against what it paid.
3426#[derive(Clone, Debug, Serialize, Deserialize)]
3427#[serde(rename_all = "camelCase")]
3428pub struct WorkspaceCost {
3429 pub workspace: String,
3430 pub cost_micros: i64,
3431 pub revenue_micros: i64,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it3432 /// Of `cost_micros`, what g1t gave away.
3433 #[serde(default)]
3434 pub given_micros: i64,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily3435 /// One of g1t's own (comped) workspaces.
3436 pub internal: bool,
3437}
3438
3439/// One Cloudflare meter over the range, and the product it is a cost of.
3440#[derive(Clone, Debug, Serialize, Deserialize)]
3441#[serde(rename_all = "camelCase")]
3442pub struct CostLineSummary {
3443 pub product: String,
3444 pub meter: String,
3445 pub raw_name: String,
3446 pub unit: String,
3447 pub source: String,
3448 pub quantity: f64,
3449 pub cost_micros: i64,
3450 /// Absent when no mapping claims it.
3451 pub bucket: Option<String>,
3452}
3453
3454/// A row of the mapping from Cloudflare's meters to g1t's products.
3455#[derive(Clone, Debug, Serialize, Deserialize)]
3456#[serde(rename_all = "camelCase")]
3457pub struct CostMapping {
3458 pub product: String,
3459 pub meter: String,
3460 pub bucket: String,
3461 pub price_meter: Option<String>,
3462 pub own_meter: Option<String>,
3463 pub scale_to_own: bool,
3464 pub drift_percent: f64,
3465 pub note: String,
3466 pub updated_at: String,
3467 pub updated_by: String,
3468}
3469
3470/// The guardrails on prices and the alerts.
3471#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3472#[serde(rename_all = "camelCase")]
3473pub struct CostSettings {
3474 /// Apply small moves without staff.
3475 pub auto_apply: bool,
3476 /// The largest move applied without staff, either way, in percent.
3477 pub auto_apply_percent: f64,
3478 /// Days between telling customers of a rise and charging it.
3479 pub notice_days: u32,
3480 /// Below this margin, in percent, for `alert_days` days in a row, alert.
3481 pub margin_floor_percent: f64,
3482 pub alert_days: u32,
3483 /// Days with less cost than this say nothing about a margin.
3484 pub min_daily_cost_micros: i64,
3485 /// A workspace costing more than its revenue times this, over 30 days,
3486 /// and at least `anomaly_floor_micros`, is flagged.
3487 pub anomaly_factor: f64,
3488 pub anomaly_floor_micros: i64,
Merge Stripe Tax, the card fee on card payments, and one free workspace per person3489 /// Pass Stripe's card fee on as its own line on every card payment (the
3490 /// plan, Security and quality, prepaying, AI credit, auto-reload and
3491 /// invoices charged to a card), never on a bank transfer or an invoice
3492 /// sent to be paid (`card_fee_percent` and `card_fee_fixed` in the
3493 /// price book). On by default.
Usage, Billing settings and prepaid AI credit; fixes from the UX audit3494 #[serde(default = "yes")]
3495 pub card_fee: bool,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily3496}
3497
3498impl Default for CostSettings {
3499 fn default() -> Self {
3500 CostSettings {
3501 auto_apply: true,
3502 auto_apply_percent: 25.0,
3503 notice_days: 14,
3504 margin_floor_percent: 10.0,
3505 alert_days: 3,
3506 min_daily_cost_micros: 100_000,
3507 anomaly_factor: 1.0,
3508 anomaly_floor_micros: 1_000_000,
Usage, Billing settings and prepaid AI credit; fixes from the UX audit3509 card_fee: true,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily3510 }
3511 }
3512}
3513
3514/// The Costs & margin page.
3515#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3516#[serde(rename_all = "camelCase")]
3517pub struct CostsReport {
3518 /// A token to read Cloudflare's bill is set.
3519 pub configured: bool,
3520 /// When Cloudflare's bill was last read.
3521 pub fetched_at: Option<String>,
3522 /// The days shown, YYYY-MM-DD.
3523 pub since: String,
3524 pub until: String,
3525 pub days: Vec<CostDay>,
3526 pub products: Vec<ProductMargin>,
3527 pub overall: OverallMargin,
3528 pub drift: Vec<CostDrift>,
3529 pub alerts: Vec<MarginAlert>,
3530 pub proposals: Vec<PriceProposal>,
3531 pub versions: Vec<PriceVersion>,
3532 pub top_workspaces: Vec<WorkspaceCost>,
3533 pub lines: Vec<CostLineSummary>,
3534 pub mappings: Vec<CostMapping>,
3535 pub settings: CostSettings,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays3536 /// g1t's own spend against its two caps.
3537 #[serde(default)]
3538 pub caps: SpendCaps,
3539}
3540
3541/// What g1t itself pays for, against its caps (billing's `budget`): the
3542/// daily breaker on all of it, and each comped account's monthly budget.
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing3543/// One of Cloudflare's subscriptions, at what it comes to a month.
3544#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3545#[serde(rename_all = "camelCase")]
3546pub struct FixedCost {
3547 pub name: String,
3548 pub monthly_micros: i64,
3549}
3550
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays3551/// At cost, never at price. What sudo's Costs page and its red bar show.
3552#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3553#[serde(rename_all = "camelCase")]
3554pub struct SpendCaps {
3555 /// Today (UTC), YYYY-MM-DD, and this month, YYYY-MM.
3556 pub day: String,
3557 pub month: String,
3558 /// What g1t paid for itself today across every workspace: comped work,
3559 /// the trial and open-source pools, free workspaces' overruns, and
3560 /// anything charged without real money behind it.
3561 pub today_micros: i64,
3562 /// `PLATFORM_DAILY_SPEND_CAP_MICROS`. Zero: no breaker.
3563 pub daily_cap_micros: i64,
3564 /// The breaker is open: new hosted-model agent runs that g1t would pay
3565 /// for wait until tomorrow (UTC) or until staff lift it.
3566 pub tripped: bool,
3567 pub tripped_at: Option<String>,
3568 /// Staff lifted it for the rest of the day.
3569 pub lifted_by: Option<String>,
3570 pub lifted_at: Option<String>,
3571 pub lift_note: Option<String>,
3572 /// This month so far, by what paid: `comped`, `trial`, `oss`, `given`,
3573 /// `unpaid`.
3574 pub month_buckets: Vec<SpendBucket>,
3575 /// Each comped account's monthly budget.
3576 pub comped: Vec<CompedBudget>,
3577 /// Free workspaces' share of this month's reconciled costs (git,
3578 /// storage, platform), through yesterday.
3579 pub free_tier_micros: i64,
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing3580 /// Cloudflare's subscriptions a month: as read from Cloudflare each
3581 /// day, else `CLOUDFLARE_FIXED_MONTHLY_MICROS`, an estimate.
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays3582 pub fixed_monthly_micros: i64,
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing3583 /// `cloudflare` or `estimate`.
3584 #[serde(default)]
3585 pub fixed_source: String,
3586 #[serde(default)]
3587 pub fixed_read_at: Option<String>,
3588 /// Each subscription, when read from Cloudflare.
3589 #[serde(default)]
3590 pub fixed_items: Vec<FixedCost>,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays3591 /// Money in this month, through the last reconciled day.
3592 pub revenue_micros: i64,
3593}
3594
3595#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3596#[serde(rename_all = "camelCase")]
3597pub struct SpendBucket {
3598 pub bucket: String,
3599 pub title: String,
3600 pub micros: i64,
3601}
3602
3603/// A comped account's monthly budget: what its work cost g1t this month.
3604#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3605#[serde(rename_all = "camelCase")]
3606pub struct CompedBudget {
3607 pub account: String,
3608 pub name: String,
3609 pub used_micros: i64,
3610 /// Zero: no budget.
3611 pub ceiling_micros: i64,
3612 /// The ceiling is `COMPED_MONTHLY_CEILING_MICROS`, not the account's own.
3613 pub default_ceiling: bool,
3614 /// 50, 75, 90, 100, or 0.
3615 pub level: u32,
3616}
3617
3618/// `admin_spend_caps`: g1t's own spend against its caps. Returns `SpendCaps`.
3619#[derive(Debug, Default, Serialize, Deserialize)]
3620pub struct AdminSpendCapsArgs {}
3621
3622/// `admin_lift_breaker`: lets hosted-model runs start again for the rest
3623/// of today (UTC), with why. Recorded in the audit log. Returns
3624/// `Outcome<SpendCaps>`.
3625#[derive(Debug, Serialize, Deserialize)]
3626pub struct AdminLiftBreakerArgs {
3627 pub note: String,
3628 pub by: String,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily3629}
3630
3631/// `admin_cost_alerts`: the open margin alerts, for sudo's banner.
3632/// Returns `Vec<MarginAlert>`.
3633#[derive(Debug, Default, Serialize, Deserialize)]
3634pub struct AdminCostAlertsArgs {}
3635
3636/// `admin_decide_proposal`: approve or reject a price proposal. An
3637/// approved rise takes effect after the notice period. Returns
3638/// `Outcome<PriceProposal>`.
3639#[derive(Debug, Serialize, Deserialize)]
3640pub struct AdminDecideProposalArgs {
3641 pub id: String,
3642 /// `approve` or `reject`.
3643 pub decision: String,
3644 #[serde(default)]
3645 pub note: String,
3646 pub by: String,
3647}
3648
3649/// `admin_set_cost_settings`. Returns `Outcome<CostSettings>`.
3650#[derive(Debug, Serialize, Deserialize)]
3651pub struct AdminSetCostSettingsArgs {
3652 pub settings: CostSettings,
3653 pub by: String,
3654}
3655
3656/// `admin_set_cost_mapping`: adds, changes or (with `remove`) removes a
3657/// mapping row. Returns `Outcome<CostMapping>`.
3658#[derive(Debug, Serialize, Deserialize)]
3659pub struct AdminSetCostMappingArgs {
3660 pub product: String,
3661 pub meter: String,
3662 #[serde(default)]
3663 pub bucket: String,
3664 #[serde(default)]
3665 pub price_meter: Option<String>,
3666 #[serde(default)]
3667 pub own_meter: Option<String>,
3668 #[serde(default)]
3669 pub scale_to_own: bool,
3670 #[serde(default)]
3671 pub drift_percent: Option<f64>,
3672 #[serde(default)]
3673 pub note: String,
3674 #[serde(default)]
3675 pub remove: bool,
3676 pub by: String,
3677}
3678
3679/// `admin_run_costs`: reads Cloudflare's bill and reconciles now, as the
3680/// daily run does. Returns `Outcome<CostsRun>`.
3681#[derive(Debug, Default, Serialize, Deserialize)]
3682pub struct AdminRunCostsArgs {
3683 #[serde(default)]
3684 pub by: String,
3685}
3686
3687#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3688#[serde(rename_all = "camelCase")]
3689pub struct CostsRun {
3690 pub lines: u32,
3691 pub days: u32,
3692 pub proposals: u32,
3693 pub alerts: u32,
3694 /// What could not be read, in words.
3695 pub problems: Vec<String>,
3696}
3697
Usage, Billing settings and prepaid AI credit; fixes from the UX audit3698// --- The Usage page ----------------------------------------------------------
3699
3700/// The product families the Usage page groups meters into, in order, with
3701/// their names.
3702pub const PRODUCTS: [(&str, &str); 8] = [
3703 ("agent", "Agent"),
3704 ("sandboxes", "Sandboxes"),
3705 ("gateway", "AI Gateway"),
3706 ("deployments", "Deployments"),
3707 ("git_storage", "Git & storage"),
3708 ("packages", "Packages"),
3709 ("security", "Security & quality"),
3710 ("search", "Search"),
3711];
3712
3713/// `usage_report`: a workspace's usage over a range of days, at price, by
3714/// product, meter, project and day. The figures are the ledger's: the same
3715/// lines the statement and invoices read, so every page agrees. Members
3716/// only. Returns `Outcome<UsageReport>`.
3717#[derive(Debug, Serialize, Deserialize)]
3718#[serde(rename_all = "camelCase")]
3719pub struct UsageReportArgs {
3720 pub workspace: String,
3721 pub viewer: Viewer,
3722 /// The first day, `YYYY-MM-DD` (UTC).
3723 pub from: String,
3724 /// The last day, `YYYY-MM-DD`, included.
3725 pub until: String,
3726 /// Only these product families (`agent`, `sandboxes`…); all when empty.
3727 #[serde(default)]
3728 pub products: Vec<String>,
3729 /// Only these projects (repositories, `owner/name`); all when empty.
3730 #[serde(default)]
3731 pub projects: Vec<String>,
3732}
3733
3734/// What usage came to over a range, and what paid for it. `price_micros`
3735/// less `discount_micros`, `included_micros` and `credits_micros` is
3736/// `charged_micros`.
3737#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3738#[serde(rename_all = "camelCase")]
3739pub struct UsageTotals {
3740 /// Usage at price, metered usage not yet charged (`pending_micros`)
3741 /// included.
3742 pub price_micros: i64,
3743 /// What the account's discount took off.
3744 pub discount_micros: i64,
3745 /// What the plan's included usage, the trial and g1t's pools paid.
3746 pub included_micros: i64,
3747 /// What credit paid: AI credit, credit from g1t.
3748 pub credits_micros: i64,
3749 /// What is left for the workspace to pay.
3750 pub charged_micros: i64,
3751 /// Metered this month and charged when it closes (storage, git
3752 /// operations, scans, embeddings, domains), at price.
3753 pub pending_micros: i64,
3754 /// What it cost g1t, before any markup.
3755 pub cost_micros: i64,
3756}
3757
3758/// One day's usage of one product, at price.
3759#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3760#[serde(rename_all = "camelCase")]
3761pub struct UsageDay {
3762 /// `YYYY-MM-DD`.
3763 pub day: String,
3764 pub product: String,
3765 pub micros: i64,
3766}
3767
3768/// How much of an allowance is used, in the meter's unit.
3769#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3770#[serde(rename_all = "camelCase")]
3771pub struct Allowance {
3772 pub used: f64,
3773 pub of: f64,
3774 /// `bytes`, `operations`, `dollars`…
3775 pub unit: String,
3776}
3777
3778/// One project's part of a meter.
3779#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3780#[serde(rename_all = "camelCase")]
3781pub struct ProjectUsage {
3782 /// `owner/name`, or empty for usage that is not one project's.
3783 pub project: String,
3784 pub micros: i64,
3785 pub quantity: f64,
3786}
3787
3788/// One meter over the range.
3789#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3790#[serde(rename_all = "camelCase")]
3791pub struct MeterLine {
3792 /// `agent_models`, `agent_rate`, `sandbox`, `builds`…
3793 pub key: String,
3794 pub label: String,
3795 pub product: String,
3796 /// What `quantity` counts: `tokens`, `seconds`, `bytes`, `operations`,
3797 /// `entries`.
3798 pub unit: String,
3799 pub quantity: f64,
3800 /// At price.
3801 pub micros: i64,
3802 /// Of `micros`, metered this month and charged when it closes.
3803 #[serde(default)]
3804 pub pending_micros: i64,
3805 /// Every day of the range, oldest first, at price: the sparkline.
3806 pub daily: Vec<i64>,
3807 #[serde(default)]
3808 pub allowance: Option<Allowance>,
3809 pub by_project: Vec<ProjectUsage>,
Merge branch 'model-routing'3810 /// How the quantity is counted, when that needs saying: for the agent
3811 /// rate, its tokens are weighted by kind, and this names the weights.
3812 #[serde(default)]
3813 pub note: Option<String>,
Usage, Billing settings and prepaid AI credit; fixes from the UX audit3814}
3815
3816/// A part of a product, such as the agent's runs, reviews and plans.
3817#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3818#[serde(rename_all = "camelCase")]
3819pub struct FeatureUsage {
3820 pub key: String,
3821 pub label: String,
3822 pub micros: i64,
3823 pub count: u32,
3824}
3825
Merge branch 'model-routing'3826/// The tokens one model used over the range, as the model proxy counted
3827/// them: on g1t's models and the workspace's own provider alike.
3828#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3829#[serde(rename_all = "camelCase")]
3830pub struct ModelTokens {
3831 /// The model's id, as it ran.
3832 pub model: String,
3833 pub input: u64,
3834 pub output: u64,
3835 pub cache_read: u64,
3836 pub cache_write: u64,
3837}
3838
Usage, Billing settings and prepaid AI credit; fixes from the UX audit3839/// One product family over the range.
3840#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3841#[serde(rename_all = "camelCase")]
3842pub struct ProductUsage {
3843 pub key: String,
3844 pub label: String,
3845 pub micros: i64,
3846 pub meters: Vec<MeterLine>,
3847 /// For the agent: by what it was doing (runs, reviews, plans, checks).
3848 #[serde(default)]
3849 pub features: Vec<FeatureUsage>,
3850}
3851
3852#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3853#[serde(rename_all = "camelCase")]
3854pub struct UsageReport {
3855 pub from: String,
3856 pub until: String,
3857 pub totals: UsageTotals,
3858 /// Each day and product with usage, oldest first.
3859 pub days: Vec<UsageDay>,
3860 /// Every product family, in order, even with nothing used.
3861 pub products: Vec<ProductUsage>,
3862 /// Every project with usage in the range, for the filter.
3863 pub projects: Vec<String>,
Merge branch 'model-routing'3864 /// Agent tokens by model over the range, most first.
3865 #[serde(default)]
3866 pub models: Vec<ModelTokens>,
Usage, Billing settings and prepaid AI credit; fixes from the UX audit3867 /// The plan's included usage this month, when the workspace has it.
3868 #[serde(default)]
3869 pub included: Option<Allowance>,
3870 /// The account's discount, in percent, when it has one.
3871 #[serde(default)]
3872 pub discount_percent: Option<u32>,
3873 /// AI credit left now, and credit from g1t for everything.
3874 pub ai_credit_micros: i64,
3875 pub credit_micros: i64,
3876 /// The trial credit left, for a workspace on its trial.
3877 #[serde(default)]
3878 pub trial_micros: Option<i64>,
3879 pub plan: PlanKind,
3880 /// Nothing is charged while g1t is being built out.
3881 pub free: bool,
3882}
3883
3884// --- AI credit -----------------------------------------------------------------
3885
3886/// Auto-reload: when AI credit falls below `threshold_micros`, the saved
3887/// card is charged to bring it back to `target_micros`, at most
3888/// `monthly_max_micros` in a calendar month. Off by default. A failed
3889/// charge turns it off and tells the owners.
3890#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3891#[serde(rename_all = "camelCase")]
3892pub struct AiReload {
3893 pub enabled: bool,
3894 pub threshold_micros: i64,
3895 pub target_micros: i64,
3896 pub monthly_max_micros: i64,
3897 /// Reloaded this month so far.
3898 #[serde(default)]
3899 pub reloaded_micros: i64,
3900 /// When it last failed and was turned off, and why.
3901 #[serde(default)]
3902 pub failed_at: Option<String>,
3903 #[serde(default)]
3904 pub error: Option<String>,
3905}
3906
3907/// The card fee passed on when AI credit is bought by card.
3908#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3909#[serde(rename_all = "camelCase")]
3910pub struct CardFee {
3911 pub on: bool,
3912 /// Per dollar charged, in millionths: 29,000 is 2.9%.
3913 pub percent_micros: f64,
3914 pub fixed_cents: u32,
3915}
3916
3917/// `ai_credit` (`AccountArgs`): a workspace's prepaid AI credit, what it
3918/// pays for and how it is bought. Members only. Returns `Outcome<AiCredit>`.
3919#[derive(Clone, Debug, Serialize, Deserialize)]
3920#[serde(rename_all = "camelCase")]
3921pub struct AiCredit {
3922 /// What is left to spend on Agent and AI Gateway usage.
3923 pub balance_micros: i64,
3924 /// Of it, bought (paid) and given (promotional).
3925 pub purchased_micros: i64,
3926 pub given_micros: i64,
3927 /// Its grants, newest first.
3928 pub grants: Vec<CreditGrant>,
3929 /// A 100% discount: AI usage is free, shown at its price then the
3930 /// discount. Nothing to buy.
3931 pub free_via_discount: bool,
3932 /// Invoiced terms (an enterprise): models are billed after use, so no
3933 /// credit is needed.
3934 pub postpaid: bool,
3935 /// Whether new runs on g1t's models are refused now for want of credit.
3936 pub blocked: bool,
3937 /// Whether the workspace may buy it: on the plan, not free.
3938 pub can_buy: bool,
3939 pub presets_cents: Vec<u32>,
3940 pub min_cents: u32,
3941 pub max_cents: u32,
3942 pub card_fee: CardFee,
3943 pub reload: AiReload,
3944 /// The agent rate per million tokens, now, at price.
3945 pub agent_rate_micros: f64,
3946 /// The markup on models' provider price, in percent.
3947 pub model_markup_percent: u32,
3948 /// The markup on AI Gateway's provider price, in percent.
3949 pub gateway_markup_percent: u32,
3950 /// The AI credit given once on starting the plan.
3951 pub upgrade_credit_micros: i64,
3952 /// How long bought credit lasts, in days.
3953 pub expires_days: u32,
3954}
3955
3956/// `buy_ai_credit`: Stripe's page to buy AI credit, one payment by card,
3957/// with the card fee as its own line. Owners only. Returns
3958/// `Outcome<Checkout>`; the page's id comes back to `return_url` as
3959/// `ai_credit`, for `confirm_ai_credit`.
3960#[derive(Debug, Serialize, Deserialize)]
3961#[serde(rename_all = "camelCase")]
3962pub struct BuyAiCreditArgs {
3963 pub actor: User,
3964 pub workspace: String,
3965 /// The credit, in cents; the card fee is added on top.
3966 #[serde(alias = "amount_cents")]
3967 pub amount_cents: u32,
3968 #[serde(alias = "return_url")]
3969 pub return_url: String,
3970}
3971
3972/// `confirm_ai_credit`: credits a purchase once Stripe says it was paid,
3973/// once. Safe to repeat; the webhook does the same. Returns
3974/// `Outcome<AiCredit>`.
3975#[derive(Debug, Serialize, Deserialize)]
3976pub struct ConfirmAiCreditArgs {
3977 pub workspace: String,
3978 pub viewer: Viewer,
3979 pub session: String,
3980}
3981
3982/// `set_ai_reload`: auto-reload's settings. Owners only. Returns
3983/// `Outcome<AiCredit>`.
3984#[derive(Debug, Serialize, Deserialize)]
3985#[serde(rename_all = "camelCase")]
3986pub struct SetAiReloadArgs {
3987 pub actor: User,
3988 pub workspace: String,
3989 pub enabled: bool,
3990 #[serde(alias = "threshold_micros")]
3991 pub threshold_micros: i64,
3992 #[serde(alias = "target_micros")]
3993 pub target_micros: i64,
3994 #[serde(alias = "monthly_max_micros")]
3995 pub monthly_max_micros: i64,
3996}
3997
3998// --- Budgets ------------------------------------------------------------------
3999
4000/// `set_budget`: the monthly budget on usage after included usage: the
4001/// owners' spend limit, its alerts, whether usage pauses at 100%, and an
4002/// optional webhook. Owners only. Returns `Outcome<Limit>`.
4003#[derive(Debug, Serialize, Deserialize)]
4004#[serde(rename_all = "camelCase")]
4005pub struct SetBudgetArgs {
4006 pub actor: User,
4007 pub workspace: String,
4008 /// The amount; None keeps the automatic one.
4009 #[serde(default, alias = "amount_micros")]
4010 pub amount_micros: Option<i64>,
4011 /// Some of 50, 75, 90 and 100.
4012 #[serde(default)]
4013 pub alerts: Vec<u32>,
4014 #[serde(default = "yes", alias = "pause_at_limit")]
4015 pub pause_at_limit: bool,
4016 /// An HTTPS address, or None for no webhook.
4017 #[serde(default)]
4018 pub webhook: Option<String>,
4019 /// Leave the spend limit as it is and change only the alerts, the
4020 /// pause and the webhook.
4021 #[serde(default, alias = "keep_limit")]
4022 pub keep_limit: bool,
4023}
4024
4025// --- Billing details -----------------------------------------------------------
4026
4027/// A postal address, as Stripe keeps it.
4028#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
4029#[serde(rename_all = "camelCase")]
4030pub struct PostalAddress {
4031 #[serde(default)]
4032 pub line1: String,
4033 #[serde(default)]
4034 pub line2: String,
4035 #[serde(default)]
4036 pub city: String,
4037 #[serde(default)]
4038 pub state: String,
4039 #[serde(default)]
4040 pub postal_code: String,
4041 /// Two letters, `US`.
4042 #[serde(default)]
4043 pub country: String,
4044}
4045
4046/// The default way the workspace pays, as far as it is safe to show.
4047#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
4048#[serde(rename_all = "camelCase")]
4049pub struct PaymentMethod {
4050 /// `card`, or another kind Stripe has.
4051 pub kind: String,
4052 #[serde(default)]
4053 pub brand: Option<String>,
4054 #[serde(default)]
4055 pub last4: Option<String>,
4056 #[serde(default)]
4057 pub exp_month: Option<u32>,
4058 #[serde(default)]
4059 pub exp_year: Option<u32>,
4060}
4061
4062/// One of the customer's invoices at Stripe: the plan, activations, AI
4063/// credit and month-end usage.
4064#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
4065#[serde(rename_all = "camelCase")]
4066pub struct StripeInvoice {
4067 pub id: String,
4068 #[serde(default)]
4069 pub number: Option<String>,
4070 /// `paid`, `open`, `void`, `uncollectible` or `draft`.
4071 pub status: String,
4072 pub total_cents: i64,
4073 pub currency: String,
4074 /// RFC 3339.
4075 pub created_at: String,
4076 #[serde(default)]
4077 pub description: Option<String>,
4078 #[serde(default)]
4079 pub hosted_url: Option<String>,
4080 #[serde(default)]
4081 pub pdf_url: Option<String>,
4082}
4083
4084/// What the next invoice will be, from g1t's own ledger.
4085#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
4086#[serde(rename_all = "camelCase")]
4087pub struct UpcomingInvoice {
4088 /// When the month closes, RFC 3339.
4089 pub closes_at: String,
4090 /// The plan and activations, at their monthly price.
4091 pub subscriptions_micros: i64,
4092 /// Usage still owed, after included usage, credit and any discount.
4093 pub usage_micros: i64,
4094 pub total_micros: i64,
4095}
4096
4097/// `billing_details` (`AccountArgs`): who the invoices are for, the default
4098/// payment method, and the invoices, from the Stripe customer. Members see
4099/// it; owners change it. Returns `Outcome<BillingDetails>`.
4100#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
4101#[serde(rename_all = "camelCase")]
4102pub struct BillingDetails {
4103 /// Whether the workspace has a Stripe customer yet.
4104 pub customer: bool,
4105 pub email: Option<String>,
4106 pub name: Option<String>,
4107 pub address: Option<PostalAddress>,
4108 /// `eu_vat`, `us_ein`…, and its value.
4109 pub tax_id_type: Option<String>,
4110 pub tax_id: Option<String>,
4111 /// Printed on invoices.
4112 pub po_number: Option<String>,
4113 /// The invoices' language, such as `en` or `fr`.
4114 pub language: Option<String>,
4115 pub payment_method: Option<PaymentMethod>,
4116 pub invoices: Vec<StripeInvoice>,
4117 pub upcoming: UpcomingInvoice,
4118 /// Stripe could not be read: what is shown is what g1t keeps.
4119 #[serde(default)]
4120 pub unavailable: Option<String>,
Merge Stripe Tax, the card fee on card payments, and one free workspace per person4121 /// Whether Stripe Tax can place the customer from the address: tax
4122 /// is worked out from it, and without it nothing is charged.
4123 #[serde(default)]
4124 pub tax_location: bool,
4125 /// Set when g1t did not charge for want of an address (RFC 3339).
4126 #[serde(default)]
4127 pub tax_address_needed_at: Option<String>,
4128 /// Stripe's check of the tax ID: `pending`, `verified`, `unverified` or
4129 /// `unavailable`.
4130 #[serde(default)]
4131 pub tax_id_status: Option<String>,
4132 /// `none`, `exempt` or `reverse`, as staff set it at Stripe; g1t never
4133 /// changes it.
4134 #[serde(default)]
4135 pub tax_exempt: Option<String>,
Usage, Billing settings and prepaid AI credit; fixes from the UX audit4136}
4137
4138/// `set_billing_details`: saves the invoice details on the Stripe customer.
4139/// Owners only. Absent fields are left as they are; an empty string clears
4140/// one. Returns `Outcome<BillingDetails>`.
4141#[derive(Debug, Serialize, Deserialize)]
4142#[serde(rename_all = "camelCase")]
4143pub struct SetBillingDetailsArgs {
4144 pub actor: User,
4145 pub workspace: String,
4146 #[serde(default)]
4147 pub email: Option<String>,
4148 #[serde(default)]
4149 pub name: Option<String>,
4150 #[serde(default)]
4151 pub address: Option<PostalAddress>,
4152 #[serde(default, alias = "tax_id_type")]
4153 pub tax_id_type: Option<String>,
4154 #[serde(default, alias = "tax_id")]
4155 pub tax_id: Option<String>,
4156 #[serde(default, alias = "po_number")]
4157 pub po_number: Option<String>,
4158 #[serde(default)]
4159 pub language: Option<String>,
4160}
4161
Models per workspace: several providers, routed by kind of work4162#[cfg(test)]
4163mod tests {
4164 use super::*;
4165
4166 #[test]
Prices are what g1t pays plus 20%, from the first second4167 fn an_account_carries_no_run_fee() {
4168 let account = Account {
4169 workspace: "acme".into(),
4170 balance_micros: 0,
4171 status: Status { enabled: true, live: false, free: false },
4172 margin_percent: 20,
4173 card: None,
4174 };
4175 let json = serde_json::to_value(account).unwrap();
4176 let mut keys: Vec<&str> = json.as_object().unwrap().keys().map(String::as_str).collect();
4177 keys.sort_unstable();
4178 assert_eq!(keys, ["balanceMicros", "card", "marginPercent", "status", "workspace"]);
4179 }
4180
4181 #[test]
4182 fn a_price_change_says_when_the_markup_moved() {
4183 let change = PriceChange {
4184 meter: "sandbox_second".into(),
4185 old_cost_micros: 21.0,
4186 new_cost_micros: 21.0,
4187 markup_percent: 20,
4188 old_markup_percent: Some(138),
4189 reason: "Sandbox time is now charged at cost plus 20% from the first second".into(),
4190 created_at: "2026-10-05T00:00:00Z".into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily4191 effective_at: None,
Prices are what g1t pays plus 20%, from the first second4192 };
4193 assert_eq!(serde_json::to_value(&change).unwrap()["oldMarkupPercent"], 138);
4194 let cost_only = PriceChange { old_markup_percent: None, ..change };
4195 assert!(serde_json::to_value(&cost_only).unwrap().get("oldMarkupPercent").is_none());
4196 }
4197
4198 #[test]
Paid features: a workspace turns on Deployments with a monthly plan4199 fn features_are_named_as_the_site_sends_them() {
4200 assert_eq!(
4201 serde_json::to_value(Feature::Deployments).unwrap(),
4202 serde_json::json!("deployments")
4203 );
4204 assert_eq!(Feature::parse("deployments"), Some(Feature::Deployments));
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look4205 assert_eq!(serde_json::to_value(Feature::Plan).unwrap(), serde_json::json!("plan"));
4206 assert_eq!(Feature::parse("plan"), Some(Feature::Plan));
4207 // Older readers named the plan Team.
4208 assert_eq!(Feature::parse("team"), Some(Feature::Plan));
4209 assert_eq!(serde_json::from_value::<Feature>(serde_json::json!("team")).unwrap(), Feature::Plan);
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar4210 assert_eq!(Feature::ALL, [Feature::Plan, Feature::Security]);
4211 assert_eq!(Feature::parse("security"), Some(Feature::Security));
4212 assert_eq!(serde_json::to_value(Feature::Security).unwrap(), serde_json::json!("security"));
Paid features: a workspace turns on Deployments with a monthly plan4213 assert!(SubscriptionStatus::Canceling.on());
4214 assert!(!SubscriptionStatus::PastDue.on());
4215 }
4216
4217 #[test]
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look4218 fn a_reservation_is_asked_for_and_answered_in_camel_case() {
4219 let asked: ReserveArgs = serde_json::from_value(serde_json::json!({
4220 "workspace": "acme",
4221 "repo": { "namespace": "acme", "name": "web" },
4222 "public": true,
4223 "kind": "check",
4224 "estimateMicros": 2_000_000,
4225 }))
4226 .unwrap();
4227 assert_eq!(asked.kind, ComputeKind::Check);
4228 assert!(asked.kind.open_source_pool());
4229 assert!(!ComputeKind::Agent.open_source_pool());
4230 // Rust callers that write snake_case are read too.
4231 let snake: ReserveArgs = serde_json::from_value(serde_json::json!({
4232 "workspace": "acme",
4233 "repo": { "namespace": "acme", "name": "web" },
4234 "public": false,
4235 "kind": "agent",
4236 "estimate_micros": 1,
4237 }))
4238 .unwrap();
4239 assert_eq!(snake.estimate_micros, 1);
4240 let answer = Reservation { id: "rsv_1".into(), paid_by: PaidBy::OnDemand, held_micros: 5, expires_at: String::new() };
4241 assert_eq!(serde_json::to_value(&answer).unwrap()["paidBy"], "on_demand");
4242 assert_eq!(serde_json::to_value(PlanKind::Internal).unwrap(), "internal");
4243 assert!(!PlanKind::Free.on_demand() && PlanKind::Enterprise.on_demand());
4244 }
4245
4246 #[test]
4247 fn a_refusal_carries_its_own_code() {
4248 let refused: crate::Outcome<Reservation> =
4249 crate::Outcome::fail(crate::FailureCode::OssPoolEmpty, "The open-source pool is spent.");
4250 let json = serde_json::to_value(&refused).unwrap();
4251 assert_eq!(json["error"]["code"], "oss_pool_empty");
4252 assert_eq!(crate::FailureCode::NotPaid.http_status(), 402);
4253 assert_eq!(crate::FailureCode::Paused.http_status(), 409);
4254 }
4255
4256 #[test]
Models per workspace: several providers, routed by kind of work4257 fn who_pays_is_read_as_the_runner_sends_it() {
4258 let run: StartRunArgs = serde_json::from_value(serde_json::json!({
4259 "workspace": "acme",
4260 "repo": { "namespace": "acme", "name": "web" },
4261 "number": 7,
4262 "task": "implement",
4263 "model": "Claude Sonnet 5.5",
4264 "billedTo": "workspace",
4265 }))
4266 .unwrap();
4267 assert_eq!(run.billed_to, "workspace");
4268 }
4269}

This file's history is long; its oldest lines are credited to the oldest commit read.