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
102 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.

Merge remote-tracking branch 'origin/main' into workspace-chat1/**
2 * How a sandbox's Durable Object handles the end of its container without
3 * failing the invocation it happens in. The containers library calls
4 * `onStop` from the object's alarm: a stop hook that throws fails that
5 * alarm, which Cloudflare retries and counts as an error each time, and
6 * which runs the whole hook again. So the hook's calls to other services
7 * go through `tellStopped`, which tries twice and logs at error level, and
8 * never throws. Pure, so it is tested on its own.
9 */
10
11/** How long `tellStopped` waits before its second try. */
12export const RETRY_AFTER_MS = 500;
13
14/**
15 * Tells another service that a sandbox stopped: `step` runs, and once more
16 * if it throws. Never throws: a step that fails twice is logged with
17 * `log` as `sandbox stop not reported`, with what and why, so it shows in
18 * the runner's logs at error level and the five-minute sweep picks the
19 * work up instead. Returns whether it got through.
20 */
21export async function tellStopped(
22 what: string,
23 step: () => Promise<unknown>,
24 options: { log?: (...data: unknown[]) => void; attempts?: number; waitMs?: number } = {},
25): Promise<boolean> {
26 const log = options.log ?? console.error;
27 const attempts = Math.max(1, options.attempts ?? 2);
28 let last: unknown = null;
29 for (let attempt = 1; attempt <= attempts; attempt++) {
30 try {
31 await step();
32 return true;
33 } catch (error) {
34 last = error;
35 if (attempt < attempts) await new Promise((resolve) => setTimeout(resolve, options.waitMs ?? RETRY_AFTER_MS));
36 }
37 }
38 log("sandbox stop not reported", what, describeError(last));
39 return false;
40}
41
42/**
43 * A service binding's answer as a step's outcome: a 5xx (or 429) throws, so
44 * `tellStopped` tries again and logs it. A refusal is not a failure: the
45 * work already reported its end, and services say so with `ok: false` or a
46 * 4xx (the deployments service answers 404 for a build that has finished).
47 */
48export async function answered(what: string, response: Response): Promise<void> {
49 if (response.status < 500 && response.status !== 429) return;
50 const body = await response.text().catch(() => "");
51 throw new Error(`${what} answered ${response.status}${body ? `: ${body.slice(0, 200)}` : ""}`);
52}
53
54/**
55 * Whether a sandbox whose `sleepAfter` has passed is still inside its run's
56 * time cap, and so keeps running: the cap, not inactivity, ends a run (the
57 * runner never fetches the container, so to the library every sandbox looks
58 * idle). Without a cap or a start time, `sleepAfter` applies.
59 */
60export function withinTimeCap(
61 started: number | null | undefined,
62 capMinutes: number | null | undefined,
63 now: number,
64 graceSeconds: number,
65): boolean {
66 if (typeof started !== "number" || typeof capMinutes !== "number" || capMinutes <= 0) return false;
67 return now < started + (capMinutes * 60 + graceSeconds) * 1000;
68}
69
Merge main into rm-agents: mirroring removal, open signups, free-tier limits, the g1t CLI and runner fixes, with the agent removal kept. The CLI's table of operations is regenerated from the API without the agent, customize and AI credit operations; no CLI command used them.70/**
71 * Whether the run a sandbox holds now has used its whole time cap, so a
72 * `timeUp` alarm may stop it. A sandbox's Durable Object is named for its
73 * work (`actions:{job}` and the like), and a re-run of that work starts in
74 * the same object; an alarm an earlier attempt left must not stop the
75 * attempt running now, which started later and has its own alarm.
76 */
77export function capReached(started: number | null | undefined, capMinutes: number | null | undefined, now: number): boolean {
78 if (typeof started !== "number" || typeof capMinutes !== "number" || capMinutes <= 0) return false;
79 return now >= started + capMinutes * 60 * 1000;
80}
81
82/**
83 * Whether a sandbox's stop ends its work as a failure. A sandbox g1t
84 * stopped (at its time cap, say) can come back with exit code 0, since the
85 * container library reports a destroyed container as a clean exit; the
86 * reason g1t stopped it, not the exit code, says how it ended.
87 */
88export function stoppedShort(exitCode: number, why: string | null): boolean {
89 return exitCode !== 0 || why !== null;
90}
91
Merge remote-tracking branch 'origin/main' into workspace-chat92/** An error as one line, for a log: its message, or what was thrown. */
93export function describeError(error: unknown): string {
94 if (error instanceof Error) return error.message || error.name;
95 if (error === undefined) return "no reason given";
96 if (typeof error === "string") return error;
97 try {
98 return JSON.stringify(error) ?? String(error);
99 } catch {
100 return String(error);
101 }
102}

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