Skip to content

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.sh
163 linesCodeBlameRaw

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.

Project settings say who created the project, by name1import { env } from "cloudflare:workers";
2
3import {
Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.4 type DesktopRunner,
Project settings say who created the project, by name5 type RegistrationToken,
6 type Runner,
7 type RunnerGroup,
8 type RunnerSettings,
9 type RunnersOwner,
10 type User,
The Runners page shows the machines a workspace's agents and workflow jobs run on, in Workspace under Compute: g1t's cloud beside your own runners, what each is running now with a link to it, what's waiting, this month's machine time and its cost (your own runners' free), where agent work and workflow jobs run, adding a runner, and groups; runner_activity in Actions says what runs where, work handed to your runners keeps its agent run, and the self-hosted runners guide says how.11 RUNNER_ACTIVITY_LIMIT,
Project settings say who created the project, by name12 runnersClient,
13} from "@g1t/contracts";
14
15import { instrumented } from "./perf.server";
The Runners page shows the machines a workspace's agents and workflow jobs run on, in Workspace under Compute: g1t's cloud beside your own runners, what each is running now with a link to it, what's waiting, this month's machine time and its cost (your own runners' free), where agent work and workflow jobs run, adding a runner, and groups; runner_activity in Actions says what runs where, work handed to your runners keeps its agent run, and the self-hosted runners guide says how.16import { type CloudNow, type CloudPrice, type OwnWaiting, type RunnerCost, cloudNow, cloudPrices, ownWaiting, runnerCost } from "./runners";
17import { agents, billing } from "./services.server";
Project settings say who created the project, by name18
19/** Self-hosted runners, kept by the actions service. */
20export const runners = runnersClient(instrumented("actions", env.ACTIONS));
21
22export type RunnersData = {
23 runners: Runner[];
24 /** A workspace's groups; empty for a repository. */
25 groups: RunnerGroup[];
26 settings: RunnerSettings | null;
27 /** The workspace's repositories, for choosing a group's. */
28 repositories: string[];
29 error: string | null;
30};
31
32export async function loadRunners(owner: RunnersOwner, actor: User, repositories: string[] = []): Promise<RunnersData> {
33 const [list, groups, settings] = await Promise.all([
34 runners.list(actor, owner),
35 "workspace" in owner ? runners.groups(actor, owner.workspace) : Promise.resolve(null),
36 runners.settings(actor, owner),
37 ]);
38 return {
39 runners: list.ok ? list.value : [],
40 groups: groups?.ok ? groups.value : [],
41 settings: settings.ok ? settings.value : null,
42 repositories,
43 error: list.ok ? null : list.error.message,
44 };
45}
46
The Runners page shows the machines a workspace's agents and workflow jobs run on, in Workspace under Compute: g1t's cloud beside your own runners, what each is running now with a link to it, what's waiting, this month's machine time and its cost (your own runners' free), where agent work and workflow jobs run, adding a runner, and groups; runner_activity in Actions says what runs where, work handed to your runners keeps its agent run, and the self-hosted runners guide says how.47/** The workspace's Runners page: its runners, and g1t's cloud beside them. */
48export type RunnersPageData = RunnersData & {
49 /** What runs on g1t's cloud now; null when neither the work nor the actions service answered. */
50 cloud: CloudNow | null;
51 /** What waits for the workspace's own runners; null when the actions service did not answer. */
52 waiting: OwnWaiting | null;
53 /** This month's machine time; null when billing did not answer. */
54 cost: RunnerCost | null;
55 /** A minute on g1t's cloud, from the price book; null when it has none. */
56 prices: CloudPrice[] | null;
Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.57 /** People's computers with the desktop app, as each app last said; null when the actions service did not answer. */
58 desktops: DesktopRunner[] | null;
The Runners page shows the machines a workspace's agents and workflow jobs run on, in Workspace under Compute: g1t's cloud beside your own runners, what each is running now with a link to it, what's waiting, this month's machine time and its cost (your own runners' free), where agent work and workflow jobs run, adding a runner, and groups; runner_activity in Actions says what runs where, work handed to your runners keeps its agent run, and the self-hosted runners guide says how.59};
60
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.61/** Active sandbox runs read: the work service's most in one list. */
The Runners page shows the machines a workspace's agents and workflow jobs run on, in Workspace under Compute: g1t's cloud beside your own runners, what each is running now with a link to it, what's waiting, this month's machine time and its cost (your own runners' free), where agent work and workflow jobs run, adding a runner, and groups; runner_activity in Actions says what runs where, work handed to your runners keeps its agent run, and the self-hosted runners guide says how.62const RUN_PAGE = 200;
63
64const warn = (what: string) => (error: unknown) => {
65 console.warn(`runners: ${what} failed`, error);
66 return null;
67};
68
69/**
70 * Every read the workspace's Runners page makes, at once. The runners,
71 * groups and settings come as on a project's page; what runs on g1t's
72 * cloud, what waits, and this month's time each come from their own
73 * service, and one that does not answer leaves only its part saying so.
74 */
75export async function loadRunnersPage(workspace: string, actor: User, repositories: string[] = [], now = new Date()): Promise<RunnersPageData> {
76 const from = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)).toISOString().slice(0, 10);
77 const until = now.toISOString().slice(0, 10);
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.78 const [base, activity, runs, report, book, desktops] = await Promise.all([
The Runners page shows the machines a workspace's agents and workflow jobs run on, in Workspace under Compute: g1t's cloud beside your own runners, what each is running now with a link to it, what's waiting, this month's machine time and its cost (your own runners' free), where agent work and workflow jobs run, adding a runner, and groups; runner_activity in Actions says what runs where, work handed to your runners keeps its agent run, and the self-hosted runners guide says how.79 loadRunners({ workspace }, actor, repositories),
80 runners
81 .activity(actor, workspace)
82 .then((result) => (result.ok ? result.value : null))
83 .catch(warn("runner activity")),
84 agents
85 .listRuns(actor, { workspace, active: true, limit: RUN_PAGE })
86 .then((result) => (result.ok ? result.value : null))
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.87 .catch(warn("sandbox runs")),
The Runners page shows the machines a workspace's agents and workflow jobs run on, in Workspace under Compute: g1t's cloud beside your own runners, what each is running now with a link to it, what's waiting, this month's machine time and its cost (your own runners' free), where agent work and workflow jobs run, adding a runner, and groups; runner_activity in Actions says what runs where, work handed to your runners keeps its agent run, and the self-hosted runners guide says how.88 billing
89 .usageReport(workspace, actor, { from, until })
90 .then((result) => (result.ok ? result.value : null))
91 .catch(warn("usage report")),
92 billing.prices().catch(warn("prices")),
Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.93 runners
94 .desktops(actor, workspace)
95 .then((result) => (result.ok ? result.value : null))
96 .catch(warn("desktops")),
The Runners page shows the machines a workspace's agents and workflow jobs run on, in Workspace under Compute: g1t's cloud beside your own runners, what each is running now with a link to it, what's waiting, this month's machine time and its cost (your own runners' free), where agent work and workflow jobs run, adding a runner, and groups; runner_activity in Actions says what runs where, work handed to your runners keeps its agent run, and the self-hosted runners guide says how.97 ]);
98 return {
99 ...base,
100 cloud: cloudNow(runs, activity, RUN_PAGE, RUNNER_ACTIVITY_LIMIT),
101 waiting: ownWaiting(activity),
102 cost: runnerCost(report),
103 prices: cloudPrices(book?.prices ?? null),
Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.104 desktops,
The Runners page shows the machines a workspace's agents and workflow jobs run on, in Workspace under Compute: g1t's cloud beside your own runners, what each is running now with a link to it, what's waiting, this month's machine time and its cost (your own runners' free), where agent work and workflow jobs run, adding a runner, and groups; runner_activity in Actions says what runs where, work handed to your runners keeps its agent run, and the self-hosted runners guide says how.105 };
106}
107
Project settings say who created the project, by name108export type RunnersAction = {
109 error?: string;
110 notice?: string;
111 /** A registration token just made: shown once, in the install steps. */
112 token?: RegistrationToken;
113};
114
115/** Every form on the Runners page. */
116export async function actOnRunners(owner: RunnersOwner, actor: User, form: FormData): Promise<RunnersAction> {
117 const intent = String(form.get("intent") ?? "");
118 const workspace = "workspace" in owner ? owner.workspace : owner.repo.namespace.toLowerCase();
119 const list = (name: string) =>
120 String(form.get(name) ?? "")
121 .split(/[\s,]+/)
122 .map((item) => item.trim())
123 .filter(Boolean);
124 switch (intent) {
125 case "token": {
126 const made = await runners.createToken(actor, owner, String(form.get("group") ?? "") || undefined);
127 return made.ok ? { token: made.value } : { error: made.error.message };
128 }
129 case "remove": {
130 const removed = await runners.remove(actor, owner, String(form.get("id") ?? ""));
131 return removed.ok ? { notice: `Removed ${String(form.get("name") ?? "the runner")}.` } : { error: removed.error.message };
132 }
133 case "group": {
134 const id = String(form.get("id") ?? "") || undefined;
135 const repositories = form.get("reach") === "some" ? form.getAll("repository").map(String) : [];
136 if (form.get("reach") === "some" && repositories.length === 0) return { error: "Choose at least one repository, or every repository." };
137 const saved = await runners.setGroup(actor, workspace, {
138 id,
139 name: String(form.get("name") ?? "").trim() || undefined,
140 repositories,
141 });
142 return saved.ok ? { notice: `Saved the group ${saved.value.name}.` } : { error: saved.error.message };
143 }
144 case "delete-group": {
145 const deleted = await runners.deleteGroup(actor, workspace, String(form.get("id") ?? ""));
146 return deleted.ok ? { notice: "Deleted the group; its runners joined the default group." } : { error: deleted.error.message };
147 }
148 case "settings": {
149 const saved = await runners.setSettings(actor, owner, {
150 agents_on_self_hosted: form.get("agents") === "on",
151 agent_labels: list("agentLabels"),
152 fork_pull_requests: form.get("forks") === "on",
153 });
154 return saved.ok ? { notice: "Saved." } : { error: saved.error.message };
155 }
156 case "inherit": {
157 const saved = await runners.setSettings(actor, owner, { inherit: true });
158 return saved.ok ? { notice: "This project follows the workspace's runner settings again." } : { error: saved.error.message };
159 }
160 default:
161 return { error: "Nothing to do." };
162 }
163}

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