Skip to content
78 linesCodeBlameRaw
1/**
2 * Guardrails: how long a workspace's runs may take, kept by the work
3 * service. Mirrors `g1t_contracts::guardrails`. Sandboxes reach the
4 * internet as any CI machine does; guardrails say nothing about the
5 * network.
6 */
7import type { ServiceBinding } from "./clients";
8import type { User, Viewer } from "./identity";
9import type { RepoPath } from "./repos";
10import type { Result } from "./result";
11
12/** What one level, the workspace or a project, sets. Unset is inherited. */
13export type GuardrailSettings = {
14 /** How long each kind of run may take, in minutes. */
15 minutes?: Record<string, number>;
16 updatedBy?: string | null;
17 updatedAt?: string | null;
18};
19
20/** What a run actually gets. */
21export type Guardrails = {
22 /**
23 * No longer sent: command rules, deny patterns and the cost cap were for
24 * g1t's own agents. Kept optional for the runner, which still passes
25 * them on (and a plan's cost cap) when they are there.
26 */
27 rules?: Record<string, boolean>;
28 deny?: string[];
29 budgetUsd?: number | null;
30 minutes: Record<string, number>;
31};
32
33export type GuardrailsView = {
34 workspace: GuardrailSettings;
35 project: GuardrailSettings | null;
36 defaults: Guardrails;
37 /** g1t's defaults with the workspace's: what a project inherits. */
38 inherited: Guardrails;
39 effective: Guardrails;
40};
41
42export interface GuardrailsApi {
43 /** Members only. With `repo`, that project's level too. */
44 getGuardrails(viewer: Viewer, workspace: string, repo?: RepoPath | null): Promise<Result<GuardrailsView>>;
45 /** The workspace's level (owners), or with `repo`, that project's (members). */
46 updateGuardrails(
47 actor: User,
48 workspace: string,
49 repo: RepoPath | null,
50 settings: GuardrailSettings,
51 ): Promise<Result<GuardrailsView>>;
52 /**
53 * For the runner: what a run in `repo` gets, which is always its
54 * project's. `repoId` names the project however it has moved since; a
55 * pull request's working copy (`pulls/<pull id>`) stands for the
56 * repository the pull request is to.
57 */
58 runGuardrails(repo: RepoPath, repoId?: string | null): Promise<Result<Guardrails>>;
59}
60
61/** The guardrails methods of the work service. */
62export function guardrailsClient(service: ServiceBinding): GuardrailsApi {
63 const call = async <T>(method: string, args: object): Promise<T> => {
64 const response = await service.fetch(`https://service/rpc/${method}`, {
65 method: "POST",
66 headers: { "content-type": "application/json" },
67 body: JSON.stringify(args),
68 });
69 if (!response.ok) throw new Error(`${method} failed with status ${response.status}`);
70 return (await response.json()) as T;
71 };
72 return {
73 getGuardrails: (viewer, workspace, repo) => call("get_guardrails", { viewer, workspace, repo: repo ?? null }),
74 updateGuardrails: (actor, workspace, repo, settings) =>
75 call("update_guardrails", { actor, workspace, repo, settings }),
76 runGuardrails: (repo, repoId) => call("run_guardrails", { repo, repo_id: repoId ?? null }),
77 };
78}