g1t/packages/contracts/src/repos.ts

386 lines14,353 bytesCodeBlame

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.

Initial g1t: services, event bus, intents and attempts1import type { User, Viewer } from "./identity";
2import type { Result } from "./result";
3
4export type Repo = {
5 id: string;
Workspaces own repositories6 /** The slug of the workspace that owns it: the first URL segment. */
Initial g1t: services, event bus, intents and attempts7 namespace: string;
8 name: string;
9 description: string | null;
10 isPrivate: boolean;
11 ownerId: string;
12 defaultBranch: string;
Issues and pull requests replace intents and attempts13 /** Set when this repo is a pull request's working copy of another repo. */
Initial g1t: services, event bus, intents and attempts14 forkOf: string | null;
Agents as a team: lifecycle, merge queue, billing and a new shell15 /**
16 * Whether the default branch is protected: it changes only by merging a
17 * pull request, and pushes to it are refused.
18 */
19 protected: boolean;
RFC 3339 timestamps in identity and repos20 /** RFC 3339. */
21 createdAt: string;
Search across all of g1t, Explore, and a command palette22 /**
23 * Words that say what it is about, for search and Explore: lowercase
24 * letters, digits and hyphens, at most 20.
25 */
26 topics: string[];
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look27 /** Its home page, an http(s) address. */
28 website?: string | null;
29 /**
30 * RFC 3339: when it was archived. While archived it is read-only:
31 * pushes, merges, agents and workflows are refused, and issues and pull
32 * requests are locked. Null when it is not archived.
33 */
34 archivedAt?: string | null;
35};
36
37/** How long a deleted repository can be restored before it is purged. */
38export const RESTORE_DAYS = 30;
39
40/** A deleted repository, as its workspace's Recently deleted list shows it. */
41export type DeletedRepo = {
42 id: string;
43 namespace: string;
44 name: string;
45 description: string | null;
46 isPrivate: boolean;
47 /** RFC 3339. */
48 deletedAt: string;
49 /** The username of who deleted it. */
50 deletedBy: string;
51 /** RFC 3339: when it is purged, unless restored first. */
52 purgeAfter: string;
Initial g1t: services, event bus, intents and attempts53};
54
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look55/** Whether a repository is archived or deleted, for services deciding whether to act on it. */
56export type RepoStatus = { archived: boolean; deleted: boolean };
57
Search across all of g1t, Explore, and a command palette58/** The most topics a repository has, and the longest topic. */
59export const MAX_TOPICS = 20;
60export const MAX_TOPIC_CHARS = 35;
61
Initial g1t: services, event bus, intents and attempts62export type RepoPath = { namespace: string; name: string };
63
64export type Commit = {
65 hash: string;
66 treeHash: string;
67 message: string;
68 author: { name: string; email: string };
69 parents: string[];
RFC 3339 timestamps in identity and repos70 /** RFC 3339. */
71 authoredAt: string;
Initial g1t: services, event bus, intents and attempts72};
73
74export type TreeEntry = {
75 name: string;
76 hash: string;
77 kind: "tree" | "blob" | "symlink" | "gitlink" | "exec";
78};
79
80export type TreeView = {
81 repo: Repo;
82 ref: string;
83 path: string;
84 /** Null when the repo has no commits yet. */
85 head: Commit | null;
86 entries: TreeEntry[];
87 readme: { name: string; text: string | null } | null;
88};
89
90export type BlobView = {
91 repo: Repo;
92 ref: string;
93 path: string;
94 size: number;
95 /** Null when the file is binary or too large to show. */
96 text: string | null;
97};
98
99/** A git remote and a short-lived credential for it. */
100export type GitAccess = { remote: string; token: string };
101
102export type GitService = "git-upload-pack" | "git-receive-pack";
103
104export type CreateRepoInput = {
Workspaces own repositories105 /** The workspace to create it in; the creator must be a member. */
106 namespace: string;
Initial g1t: services, event bus, intents and attempts107 name: string;
108 description?: string | null;
109 isPrivate?: boolean;
Agents as a team: lifecycle, merge queue, billing and a new shell110 /**
111 * The https address of a public git repository to copy the default branch
112 * of, such as `https://github.com/owner/repo`.
113 */
114 importUrl?: string;
Initial g1t: services, event bus, intents and attempts115};
116
117/** Repositories: metadata, contents and git access. */
118export interface ReposApi {
119 get(path: RepoPath, viewer: Viewer): Promise<Result<Repo>>;
120 getById(id: string, viewer: Viewer): Promise<Result<Repo>>;
Agents and memory, checks and conflicts, profiles, slug renames, custom domains121 /**
Fast pages, required checks on the branch, self-hosted runners, honest incidents122 * Of these ids (at most 500), the repositories the viewer can read, in
123 * one call. Forks and deleted repositories are left out.
124 */
125 readable(ids: string[], viewer: Viewer): Promise<Repo[]>;
126 /**
Agents and memory, checks and conflicts, profiles, slug renames, custom domains127 * The workspaces in which this account made a public repository, and so
128 * a public project anyone can see. By account id.
129 */
130 publicNamespaces(ownerId: string): Promise<string[]>;
Initial g1t: services, event bus, intents and attempts131 /** Repos the viewer may see, newest first, optionally matching `query`. */
Workspaces own repositories132 list(
133 viewer: Viewer,
134 options?: {
135 query?: string;
136 /** Only repos in this workspace. */
137 namespace?: string;
138 /** Only repos in workspaces the viewer belongs to. */
139 memberOnly?: boolean;
140 },
141 ): Promise<Repo[]>;
Initial g1t: services, event bus, intents and attempts142 create(owner: User, input: CreateRepoInput): Promise<Result<Repo>>;
Agents as a team: lifecycle, merge queue, billing and a new shell143 /**
144 * Changes whichever details are given. Members of the repository's
145 * workspace only. An empty description clears it.
146 */
147 update(
148 actor: User,
149 path: RepoPath,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look150 changes: {
151 description?: string;
152 isPrivate?: boolean;
153 protected?: boolean;
154 topics?: string[];
155 /** An empty string clears it. */
156 website?: string;
157 },
Agents as a team: lifecycle, merge queue, billing and a new shell158 ): Promise<Result<Repo>>;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look159 /**
160 * Deletes the repository. Owners only, who type its full name as
161 * `confirm`. It is hidden at once and can be restored for
162 * `RESTORE_DAYS` days, then purged. Publishes `repo.deleted`.
163 */
164 delete(actor: User, path: RepoPath, confirm: string): Promise<Result<DeletedRepo>>;
165 /** A workspace's recently deleted repositories, newest first. Owners only; empty otherwise. */
166 deleted(viewer: Viewer, namespace: string): Promise<DeletedRepo[]>;
167 /** Brings a deleted repository back as it was. Owners only. Publishes `repo.restored`. */
168 restore(actor: User, path: RepoPath): Promise<Result<Repo>>;
169 /**
170 * Removes a deleted repository for good now, its git data with it, and
171 * frees its name. Owners only, who type its full name. Publishes `repo.purged`.
172 */
173 purge(actor: User, path: RepoPath, confirm: string): Promise<Result<boolean>>;
174 /**
175 * Renames the repository in its workspace. Owners only. The old path
176 * redirects until a repository is made there. Publishes `repo.renamed`.
177 */
178 rename(actor: User, path: RepoPath, name: string): Promise<Result<Repo>>;
179 /**
180 * Archives the repository (read-only) or unarchives it. Owners only.
181 * Publishes `repo.archived` or `repo.unarchived`.
182 */
183 archive(actor: User, path: RepoPath, archived: boolean): Promise<Result<Repo>>;
184 /**
185 * Makes the repository public or private. Owners only, who type its full
186 * name as `confirm`. Publishes `repo.updated` and `repo.visibility_changed`.
187 */
188 setVisibility(actor: User, path: RepoPath, isPrivate: boolean, confirm: string): Promise<Result<Repo>>;
189 /** Makes another existing branch the default. Members. */
190 setDefaultBranch(actor: User, path: RepoPath, branch: string): Promise<Result<Repo>>;
191 /**
192 * Renames a branch; pull requests from it follow, and addresses naming the
193 * old one redirect. Members; only owners rename the default branch.
194 */
195 renameBranch(actor: User, path: RepoPath, from: string, to: string): Promise<Result<Repo>>;
196 /** What a branch renamed away from `branch` is called now, or null. */
197 resolveBranch(repoId: string, branch: string): Promise<string | null>;
198 /** Whether a repository is archived or deleted; an unknown id answers as deleted. */
199 statusById(id: string): Promise<RepoStatus>;
200 /**
201 * Moves the repository to the workspace `to`, keeping its name, id and
202 * everything under it. The actor must own both workspaces. The old path
203 * redirects until a repository is made there. Publishes `repo.transferred`.
204 */
205 transfer(actor: User, path: RepoPath, to: string): Promise<Result<Repo>>;
206 /**
207 * Where a repository transferred away from `path` is now; null when
208 * `path` is a repository or never was one that moved. Callers check the
209 * viewer may see it there.
210 */
211 resolvePath(path: RepoPath): Promise<RepoPath | null>;
Initial g1t: services, event bus, intents and attempts212
213 tree(path: RepoPath, viewer: Viewer, ref: string | null, treePath: string): Promise<Result<TreeView>>;
214 blob(path: RepoPath, viewer: Viewer, ref: string, filePath: string): Promise<Result<BlobView>>;
215 log(path: RepoPath, viewer: Viewer, ref: string | null, limit: number): Promise<Result<Commit[]>>;
Agents as a team: lifecycle, merge queue, billing and a new shell216 /**
217 * Who last changed each line of a file as of `ref` (the default branch if
218 * null). Not found when the file is missing or is not text.
219 */
220 blame(path: RepoPath, viewer: Viewer, ref: string | null, filePath: string): Promise<Result<Blame>>;
Initial g1t: services, event bus, intents and attempts221
222 /**
Issues and pull requests replace intents and attempts223 * A copy-on-write copy of `source`, hidden from listings, for one pull request
Initial g1t: services, event bus, intents and attempts224 * to work in.
225 */
Issues and pull requests replace intents and attempts226 forkForPull(sourceId: string, pullId: string, actor: User): Promise<Result<Repo>>;
Initial g1t: services, event bus, intents and attempts227
228 /**
229 * Authorizes a git operation and returns where to send it. Pushing to a
230 * repo that does not exist creates it in the pusher's own namespace.
231 */
232 gitAccess(path: RepoPath, viewer: Viewer, service: GitService): Promise<Result<GitAccess>>;
Rust repos service with shipping; pull requests kept in the model233
Pull requests from branches234 /** The repository's branches, default branch first. */
235 branches(path: RepoPath, viewer: Viewer): Promise<Result<Branch[]>>;
236
Branches and Tags pages, each file's last commit, and the branch menu on files237 /** Which commit last changed each entry of a directory at `ref` (the default branch when null). */
238 lastCommits(path: RepoPath, viewer: Viewer, ref: string | null, treePath: string): Promise<Result<LastCommits>>;
239
240 /** The repository's tags, newest commit first, at most 100. */
241 tags(path: RepoPath, viewer: Viewer): Promise<Result<Tag[]>>;
242
Rust repos service with shipping; pull requests kept in the model243 /**
Download ZIP from the Code button; a slimmer lifecycle panel; agent steps say what was done, not sandbox paths244 * Every file at `ref` (the default branch when null), at most `limit`
245 * (10,000 at most). No viewer: check access first.
246 */
247 listFiles(repoId: string, ref: string | null, limit: number): Promise<FileList>;
248
249 /** Blobs' bytes as standard base64, at most 100; `data` is null for one missing or over `maxBytes`. No viewer. */
250 rawBlobs(repoId: string, hashes: string[], maxBytes: number): Promise<RawBlob[]>;
251
252 /**
Fast pages, required checks on the branch, self-hosted runners, honest incidents253 * Writes one file on a new branch made from the default branch's head, as
254 * one commit by `actor`, for a change g1t proposes on their behalf (a
255 * starter workflow). Refused unless they may push, when the branch exists,
256 * or when the file is already there.
257 */
258 commitFile(
259 repo: RepoPath,
260 actor: User,
261 file: { branch: string; path: string; content: string; message: string },
262 ): Promise<Result<{ branch: string; commit: string }>>;
263
264 /**
Pull requests from branches265 * Moves a repository's default branch to the head of a pull request's
266 * source: a fork (`sourceId` is the fork) or one of the repository's own
267 * branches (`sourceId` is the repository, and `branch` is required).
268 * Refused with "conflict" when the source is behind, since that would
Rust repos service with shipping; pull requests kept in the model269 * discard commits.
270 */
Pull requests from branches271 land(sourceId: string, actor: User, branch?: string | null): Promise<Result<{ commit: string; previous: string | null }>>;
Diffs on attempts; hosted agent presented as the g1t agent272
273 /**
Pull requests from branches274 * What `head` changes relative to `base`. `head` is a branch or a commit
275 * and defaults to the default branch. With no `base`, a fork is compared
276 * with the last commit it shares with the repository it came from, and a
277 * branch with the point where it left the default branch.
Diffs on attempts; hosted agent presented as the g1t agent278 */
Pull requests from branches279 compare(repoId: string, viewer: Viewer, base?: string | null, head?: string | null): Promise<Result<Comparison>>;
Runner: each sweep starts a few of the queued nightly backups280
281 /**
282 * Services only, for the runner's sweep: up to `limit` queued nightly
283 * backups, each now running with a token of its own, so long as no more
284 * than `maxRunning` are then running. Empty when backups are off.
285 */
286 claimBackups(limit: number, maxRunning: number): Promise<BackupClaim[]>;
287
288 /**
289 * Services only: a backup's sandbox stopped before it reported, so the
290 * job is tried again later. Refused harmlessly once it has reported.
291 */
292 failBackup(jobId: string, token: string, error: string): Promise<Result<boolean>>;
Initial g1t: services, event bus, intents and attempts293}
Diffs on attempts; hosted agent presented as the g1t agent294
Runner: each sweep starts a few of the queued nightly backups295/**
296 * A nightly backup to start (`g1t_contracts::backups`): the sandbox is
297 * given the job's id and token, and nothing else.
298 */
299export type BackupClaim = {
300 jobId: string;
301 token: string;
302 repoId: string;
303 path: RepoPath;
304};
305
Agents as a team: lifecycle, merge queue, billing and a new shell306/** Lines `start` to `end` (inclusive, from 1) last changed by `commit`. */
307export type BlameRange = { start: number; end: number; commit: string };
308
309/** Who last changed each line of a file. */
310export type Blame = {
311 /** The commit the file was read at. */
312 head: string;
313 /** Every line, in order, in runs that share a commit. */
314 ranges: BlameRange[];
315 /** The commits the ranges name, each once. */
316 commits: Commit[];
317 /** True when the history was too long to read in full. */
318 partial: boolean;
319};
320
Pull requests from branches321/** A branch and the commit it points to. */
322export type Branch = { name: string; hash: string };
323
Branches and Tags pages, each file's last commit, and the branch menu on files324/** Each entry's last commit; `complete` is false when some were not reached. */
325export type LastCommits = { entries: { name: string; commit: Commit }[]; complete: boolean };
326
327/** A tag, and its commit when it could be read. */
328export type Tag = { name: string; commit: Commit | null };
329
Download ZIP from the Code button; a slimmer lifecycle panel; agent steps say what was done, not sandbox paths330/** Files at a commit, and whether there were more than were listed. */
331export type FileList = { commit: string | null; files: { path: string; hash: string | null }[]; truncated: boolean };
332
333/** One blob's bytes, standard base64; null when missing or too large. */
334export type RawBlob = { hash: string; size: number; data: string | null };
335
Catching up with main takes seconds when the two sides touched different files336/**
337 * What came of bringing a pull request up to date with the default branch
338 * without a sandbox. `needs_agent` pushed nothing: the runner's `update`
339 * merges it in a sandbox, with an agent if it conflicts.
340 */
341export type PullBranchUpdate =
342 | { outcome: "updated"; commit: string; previous: string }
343 | { outcome: "up_to_date"; commit: string }
344 | {
345 outcome: "needs_agent";
346 /**
347 * `overlap`: both sides changed some of the same files. `conflicting`:
348 * merging is known to conflict. `unsupported`: it could not be worked
349 * out without git, such as for a very large change.
350 */
351 reason: "overlap" | "conflicting" | "unsupported";
352 detail: string;
353 /** The files both changed, or that conflict, when known. */
354 paths: string[];
355 };
356
Diffs on attempts; hosted agent presented as the g1t agent357export type DiffLine = {
358 kind: "context" | "add" | "delete";
359 /** Line number in the old file; null for added lines. */
360 old: number | null;
361 /** Line number in the new file; null for deleted lines. */
362 new: number | null;
363 text: string;
364};
365
366/** A run of changed lines with their surrounding context. */
367export type Hunk = { lines: DiffLine[] };
368
369export type FileDiff = {
370 path: string;
371 status: "added" | "modified" | "deleted";
372 additions: number;
373 deletions: number;
374 /** True when the file is binary or too large, so no lines are shown. */
375 binary: boolean;
376 hunks: Hunk[];
377};
378
379/** What changed between two commits. */
380export type Comparison = {
381 base: string | null;
382 head: string;
383 files: FileDiff[];
384 /** True when the change was too large to return in full. */
385 truncated: boolean;
386};