flagon-io/g1t

public

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t/apps/web/app/lib/guardrails.ts

72 lines2,677 bytesCodeBlame
1import type { GuardrailSettings, RunKind } from "@g1t/contracts";
2
3/** What a level's form field means: inherit, or a choice of its own. */
4export type Tri = "inherit" | "on" | "off";
5
6export function tri(value: boolean | null | undefined): Tri {
7 return value == null ? "inherit" : value ? "on" : "off";
8}
9
10/** Lines of a textarea, trimmed, without blanks or repeats. */
11export function lines(value: FormDataEntryValue | null): string[] {
12 const seen = new Set<string>();
13 for (const line of String(value ?? "").split(/\r?\n/)) {
14 const trimmed = line.trim();
15 if (trimmed) seen.add(trimmed);
16 }
17 return [...seen];
18}
19
20/** A number from a field; empty means inherit. Not a number is kept, to be refused. */
21function amount(value: FormDataEntryValue | null): number | null {
22 const text = String(value ?? "").trim().replace(/^\$/, "");
23 if (!text) return null;
24 const number = Number(text);
25 return Number.isFinite(number) ? number : Number.NaN;
26}
27
28/** What one level's form says, as the settings that level keeps. */
29export function settingsFromForm(
30 form: FormData,
31 catalog: { registries: string[]; rules: string[]; kinds: readonly RunKind[] },
32): GuardrailSettings {
33 const choice = (name: string): boolean | null => {
34 const value = form.get(name);
35 return value === "on" ? true : value === "off" ? false : null;
36 };
37 const rules: Record<string, boolean> = {};
38 for (const id of catalog.rules) {
39 const on = choice(`rule:${id}`);
40 if (on != null) rules[id] = on;
41 }
42 const minutes: Record<string, number> = {};
43 for (const kind of catalog.kinds) {
44 const cap = amount(form.get(`minutes:${kind}`));
45 if (cap != null) minutes[kind] = Number.isNaN(cap) ? 0 : Math.trunc(cap);
46 }
47 const budget = amount(form.get("budgetUsd"));
48 return {
49 restrictNetwork: choice("restrictNetwork"),
50 registries:
51 form.get("registries") === "custom"
52 ? catalog.registries.filter((id) => form.get(`registry:${id}`) === "on")
53 : null,
54 domains: lines(form.get("domains")),
55 rules,
56 deny: lines(form.get("deny")),
57 // Not a number is sent as one the service refuses, with why.
58 budgetUsd: budget == null ? null : Number.isNaN(budget) ? -1 : budget,
59 minutes,
60 };
61}
62
63/** A cost in dollars, or "no cap". */
64export function formatCap(usd: number | null | undefined): string {
65 return usd == null ? "no cap" : `$${usd.toFixed(2)}`;
66}
67
68/** How full a cap is, from 0 to 1, or null with no cap. */
69export function capShare(used: number | null | undefined, cap: number | null | undefined): number | null {
70 if (cap == null || cap <= 0 || used == null) return null;
71 return Math.min(1, Math.max(0, used / cap));
72}