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/services/runner/src/delegate.ts

77 lines3,151 bytesCodeBlame
1/**
2 * Putting an agent on something in one step (`RunnerService.delegate`):
3 * what is said about the agent once its issue is open, whatever became of
4 * it. Pure, so it is tested on its own.
5 */
6import type { AgentStart, Delegated, Issue, Pull } from "@g1t/contracts";
7
8const SITE = "https://g1t.sh";
9
10/**
11 * What the workspace's agents have no model is said as. `preview`: g1t's
12 * hosted models are closed to it because billing takes no real money yet.
13 */
14export function noModelMessage(workspace: string, preview = false): string {
15 if (preview) {
16 return `g1t's hosted models are not open to the ${workspace} workspace yet: during the preview they run only for a few invited workspaces. An owner can connect the workspace's own model provider under Integrations, and its agents start at once.`;
17 }
18 return `The ${workspace} workspace has no model for its agents: its free allowance on g1t's models is used up or over. An owner can connect the workspace's own model provider under Integrations, and its agents start at once.`;
19}
20
21/**
22 * Where the fix for a refusal is: the workspace's billing page (its limit
23 * and per-issue caps have anchors there), its model settings, or support,
24 * which has no page to fix it on.
25 */
26export function fixUrlFor(code: string, workspace: string): string | null {
27 const slug = workspace.toLowerCase();
28 switch (code) {
29 case "not_paid":
30 case "trial_used":
31 case "oss_pool_empty":
32 return `${SITE}/${slug}/-/billing`;
33 case "limit":
34 return `${SITE}/${slug}/-/billing#limit`;
35 case "issue_cap":
36 return `${SITE}/${slug}/-/billing#caps`;
37 case "no_model":
38 return `${SITE}/${slug}/-/integrations`;
39 default:
40 return null;
41 }
42}
43
44/** The agent at work on `pull`. */
45export function started(issue: Issue, pull: Pull): Delegated {
46 return { issue, pull, agent: { status: "started", code: null, message: null, fixUrl: null } };
47}
48
49/** Every agent slot is busy: it starts by itself when one frees up. */
50export function queued(issue: Issue, message: string): Delegated {
51 return { issue, pull: null, agent: { status: "queued", code: "waiting", message, fixUrl: null } };
52}
53
54/** It did not start: why, and where to fix it. */
55export function notStarted(issue: Issue, code: string, message: string, workspace: string): Delegated {
56 const agent: AgentStart = { status: "not_started", code, message, fixUrl: fixUrlFor(code, workspace) };
57 return { issue, pull: null, agent };
58}
59
60/** The issue's title and body, tidied, and the checks with blank ones left out. */
61export function delegateInput(input: { title?: unknown; body?: unknown; checks?: unknown; labels?: unknown }): {
62 title: string;
63 body: string;
64 checks: string[];
65 labels: string[];
66} {
67 const list = (value: unknown) =>
68 (Array.isArray(value) ? value : typeof value === "string" ? value.split("\n") : [])
69 .map((item) => String(item).trim())
70 .filter(Boolean);
71 return {
72 title: typeof input.title === "string" ? input.title.trim() : "",
73 body: typeof input.body === "string" ? input.body.trim() : "",
74 checks: list(input.checks),
75 labels: list(input.labels),
76 };
77}