Skip to content
967 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.

Paid features: a workspace turns on Deployments with a monthly plan1import type { ActionsApi } from "./actions";
Billing accounts, terms and enterprises; g1t is no longer free2import type { BillingAdminApi, BillingApi } from "./billing";
Deployments: a preview for every pull request, production on g1t.page3import type { DeploymentsApi } from "./deployments";
Projects: what a workspace builds and runs, first on every page4import type { ProjectsApi } from "./projects";
Paid features: a workspace turns on Deployments with a monthly plan5import type { EventsApi } from "./events";
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace6import type { IdentityAdminApi, IdentityApi } from "./identity";
Paid features: a workspace turns on Deployments with a monthly plan7import type { IntegrationsApi } from "./integrations";
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member8import type { PackagesApi } from "./packages";
Paid features: a workspace turns on Deployments with a monthly plan9import type { WebhooksApi } from "./webhooks";
10import type { ReposApi } from "./repos";
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar11import type { PullDetail, WorkApi } from "./work";
12import type { Result } from "./result";
Fast pages, required checks on the branch, self-hosted runners, honest incidents13import type { RunnersApi } from "./runners";
Paid features: a workspace turns on Deployments with a monthly plan14
15/** A service binding, as far as these clients need it. */
16export type ServiceBinding = {
17 fetch(input: string, init?: RequestInit): Promise<Response>;
18};
19
20/**
21 * Calls a method on a service that speaks the JSON protocol used by the
22 * Rust services: `POST /rpc/<method>` with the arguments as the body.
23 */
24async function rpc<T>(
25 service: ServiceBinding,
26 method: string,
27 args: object,
28): Promise<T> {
29 // The hostname is ignored; a service binding always reaches its service.
30 const response = await service.fetch(`https://service/rpc/${method}`, {
31 method: "POST",
32 headers: { "content-type": "application/json" },
33 body: JSON.stringify(args),
34 });
35 if (!response.ok) {
36 throw new Error(`${method} failed with status ${response.status}`);
37 }
38 return (await response.json()) as T;
39}
40
41export function identityClient(service: ServiceBinding): IdentityApi {
42 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
43 return {
Merge invite emails that confirm the address: the emailed link carries a proof only that email has, so signing up from it needs no code; shared links and typed codes still confirm44 register: (username, email, password, inviteCode, client, emailProof) =>
45 call("register", {
46 username,
47 email,
48 password,
49 invite_code: inviteCode ?? null,
50 email_proof: emailProof ?? null,
51 client: client ?? null,
52 }),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look53 signIn: (username, password, client) => call("sign_in", { username, password, client: client ?? null }),
Merge main (membership, two-factor, GitHub repo roles) into tokens54 twoFactorSignIn: (challenge, code, client) => call("two_factor_sign_in", { challenge, code, client: client ?? null }),
Paid features: a workspace turns on Deployments with a monthly plan55 signOut: (sessionToken) => call("sign_out", { sessionToken }),
56 resendVerification: (user) => call("resend_verification", { user }),
57 verifyEmail: (token) => call("verify_email", { token }),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look58 requestPasswordReset: (email, client) => call("request_password_reset", { email, client: client ?? null }),
Paid features: a workspace turns on Deployments with a monthly plan59 resetPassword: (token, password) =>
60 call("reset_password", { token, password }),
61 deviceStart: (clientName) => call("device_start", { clientName }),
62 deviceLookup: (userCode) => call("device_lookup", { userCode }),
63 deviceResolve: (userCode, user, approve) =>
64 call("device_resolve", { userCode, user, approve }),
65 deviceClaim: (deviceCode) => call("device_claim", { deviceCode }),
66 oauthAuthorize: (user, approval) => call("oauth_authorize", { user, ...approval }),
67 oauthExchange: (code, codeVerifier, clientId, redirectUri) =>
68 call("oauth_exchange", { code, codeVerifier, clientId, redirectUri }),
69 oauthRefresh: (refreshToken, clientId) =>
70 call("oauth_refresh", { refreshToken, clientId }),
71 listOAuthGrants: (user) => call("list_oauth_grants", { user }),
72 revokeOAuthGrant: (user, id) => call("revoke_oauth_grant", { user, id }),
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step73 updateOAuthGrant: (user, id, grant) =>
74 call("update_oauth_grant", { user, id, scopes: grant.scopes }),
Paid features: a workspace turns on Deployments with a monthly plan75 createWorkspace: (user, slug, name) => call("create_workspace", { user, slug, name }),
76 getWorkspace: (slug) => call("get_workspace", { slug }),
Merge branch 'worktree-agent-a2013627e5ea4ab13'77 workspaceResidency: (slug) => call("workspace_residency", { slug }),
78 setWorkspaceResidency: (actor, slug, residency) => call("set_workspace_residency", { actor, slug, residency }),
Paid features: a workspace turns on Deployments with a monthly plan79 listMembers: (slug, viewer) => call("list_members", { slug, viewer }),
80 addMember: (actor, slug, username) => call("add_member", { actor, slug, username }),
81 removeMember: (actor, slug, username) =>
Merge main (membership, two-factor, GitHub repo roles) into tokens82 call("remove_member", { actor, slug, username, surface: "web" }),
83 updateMember: (actor, slug, username, change) =>
84 call("update_member", { actor, slug, username, role: change.role ?? null, org_roles: change.org_roles ?? null, surface: "web" }),
85 transferOwnership: (actor, slug, username) => call("transfer_ownership", { actor, slug, username, surface: "web" }),
86 leaveWorkspace: (user, slug) => call("leave_workspace", { user, slug, surface: "web" }),
87 setMemberPrivileges: (actor, slug, change) => call("set_member_privileges", { actor, slug, privileges: change, surface: "web" }),
88 setTwoFactorRequirement: (actor, slug, required) =>
89 call("set_two_factor_requirement", { actor, slug, required, surface: "web" }),
Paid features: a workspace turns on Deployments with a monthly plan90 updateWorkspace: (actor, slug, details) =>
91 call("update_workspace", { actor, slug, ...details }),
Agents and memory, checks and conflicts, profiles, slug renames, custom domains92 renameWorkspace: (actor, slug, newSlug) => call("rename_workspace", { actor, slug, newSlug }),
93 checkWorkspaceRename: (actor, slug, newSlug) =>
94 call("check_workspace_rename", { actor, slug, newSlug }),
95 resolveSlug: (slug) => call("resolve_slug", { slug }),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look96 deleteWorkspace: (actor, slug, confirm) => call("delete_workspace", { actor, slug, confirm }),
97 checkWorkspaceDeletion: (actor, slug) => call("check_workspace_deletion", { actor, slug }),
Workspace names and icons, and a component kit for every control98 setWorkspaceAvatar: (actor, slug, image) => call("set_workspace_avatar", { actor, slug, image }),
99 setUserAvatar: (user, image) => call("set_user_avatar", { user, image }),
Paid features: a workspace turns on Deployments with a monthly plan100 listWorkspaceTokens: (slug, viewer) => call("list_workspace_tokens", { slug, viewer }),
101 removeWorkspaceToken: (actor, slug, id) =>
102 call("remove_workspace_token", { actor, slug, id }),
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers103 createToken: (actor, input) =>
104 call("create_token", {
105 actor,
106 owner: input.owner ?? null,
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules107 name: input.name,
108 description: input.description ?? null,
109 ttl_seconds: input.ttlSeconds,
110 workspace: input.workspace,
111 repository_selection: input.repositorySelection,
112 repositories: input.repositories,
113 permissions: input.permissions,
Merge main into Artifacts Phase 2114 website: input.website ?? false,
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules115 }),
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers116 updateToken: (actor, id, change, owner) =>
117 call("update_token", {
118 actor,
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules119 id,
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers120 owner: owner ?? null,
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules121 name: change.name ?? null,
122 description: change.description ?? null,
123 repository_selection: change.repositorySelection ?? null,
124 repositories: change.repositories ?? null,
125 permissions: change.permissions ?? null,
Merge main into Artifacts Phase 2126 website: change.website ?? null,
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules127 }),
128 getTokenPolicy: (slug, viewer) => call("get_token_policy", { slug, viewer }),
129 setTokenPolicy: (actor, slug, change) =>
130 call("set_token_policy", {
131 actor,
132 slug,
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers133 allow_tokens_for_all_workspaces: change.allowTokensForAllWorkspaces ?? null,
134 allow_tokens_for_this_workspace: change.allowTokensForThisWorkspace ?? null,
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules135 require_approval: change.requireApproval ?? null,
136 max_lifetime_days: change.maxLifetimeDays ?? null,
137 forbid_no_expiry: change.forbidNoExpiry ?? null,
138 surface: "web",
139 }),
140 listMemberTokens: (actor, slug, filter = {}) =>
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers141 call("list_member_tokens", { actor, slug, status: filter.status ?? null }),
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules142 reviewTokenRequest: (actor, slug, id, approve, reason) =>
143 call("review_token_request", { actor, slug, id, approve, reason: reason ?? null, surface: "web" }),
144 revokeMemberToken: (actor, slug, id, reason) =>
145 call("revoke_member_token", { actor, slug, id, reason: reason ?? null, surface: "web" }),
Paid features: a workspace turns on Deployments with a monthly plan146 userForSession: (sessionToken) => call("user_for_session", { sessionToken }),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look147 registration: () => call("registration", {}),
148 listInvites: (user) => call("list_invites", { user }),
149 createInvite: (user, options = {}) =>
Merge two kinds of invite, kept apart: an invite to g1t (Settings, invite-only only, no workspace unless asked) and an invitation to a workspace (its People page)150 call("create_invite", { user, email: options.email ?? null, workspace: options.workspace ?? null, join: options.join ?? null, join_role: options.join ? (options.joinRole ?? null) : null }),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look151 revokeInvite: (user, id) => call("revoke_invite", { user, id }),
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas152 checkInvite: (code, client, options = {}) =>
153 call("check_invite", {
154 code,
155 client: client ?? null,
156 viewer: options.viewer ?? null,
157 any_status: options.anyStatus ?? false,
Merge invite emails that confirm the address: the emailed link carries a proof only that email has, so signing up from it needs no code; shared links and typed codes still confirm158 email_proof: options.emailProof ?? null,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas159 }),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look160 acceptInvite: (user, code) => call("accept_invite", { user, code }),
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)161 inviteMember: (actor, slug, who) =>
162 call("invite_member", { actor, slug, email: who.email ?? "", username: who.username ?? null, role: who.role ?? null }),
163 listInvitations: (user) => call("list_invitations", { user }),
164 acceptInvitation: (user, id) => call("accept_invitation", { user, id, surface: "web" }),
165 declineInvitation: (user, id) => call("decline_invitation", { user, id, surface: "web" }),
166 findPeople: (query, limit) => call("find_people", { query, limit: limit ?? null }),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look167 workspaceInvites: (slug, viewer) => call("workspace_invites", { slug, viewer }),
168 revokeWorkspaceInvite: (actor, slug, id) => call("revoke_workspace_invite", { actor, slug, id }),
169 requestAccess: (email, about, client) => call("request_access", { email, about, client: client ?? null }),
Paid features: a workspace turns on Deployments with a monthly plan170 userForGitCredentials: (username, secret) =>
171 call("user_for_git_credentials", { username, secret }),
172 userForAccessToken: (token) => call("user_for_access_token", { token }),
173 userForSshKey: (fingerprint) => call("user_for_ssh_key", { fingerprint }),
174 userByUsername: (username) => call("user_by_username", { username }),
175 usernames: (ids) => call("usernames", { ids }),
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)176 usersForAudience: (ids) => call("users_for_audience", { ids }),
Agents and memory, checks and conflicts, profiles, slug renames, custom domains177 profile: (username) => call("profile", { username }),
178 updateProfile: (actor, fields) => call("update_profile", { actor, ...fields }),
179 profileWorkspaces: (username, viewer, publicIn) =>
180 call("profile_workspaces", { username, viewer, public: publicIn }),
The apps you pin to your dock are kept with your account, per workspace and in your order, so the dock is the same on every device: identity keeps them in dock_pins and answers dock_pins and set_dock_pins, the dock reads them with the rest of the page, pins kept only on this device carry over with your first change, this device's copy still draws the dock when identity can't be reached, a pin that can't be saved says so, and they go when you or the workspace do; the workspaces guide says how.181 dockPins: (user, workspace) => call("dock_pins", { user, workspace }),
182 setDockPins: (user, workspace, apps) => call("set_dock_pins", { user, workspace, apps }),
Paid features: a workspace turns on Deployments with a monthly plan183 listSshKeys: (user) => call("list_ssh_keys", { user }),
184 addSshKey: (user, title, publicKey) =>
185 call("add_ssh_key", { user, title, publicKey }),
186 removeSshKey: (user, id) => call("remove_ssh_key", { user, id }),
187 listAccessTokens: (user) => call("list_access_tokens", { user }),
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step188 createAccessToken: (user, name, ttlSeconds, grant) =>
189 call("create_access_token", {
190 user,
191 name,
192 ttlSeconds,
193 scopes: grant?.scopes ?? null,
194 listed: grant?.listed ?? false,
195 }),
Paid features: a workspace turns on Deployments with a monthly plan196 createAgentToken: (onBehalfOf, scope, ttlSeconds) =>
197 call("create_agent_token", { onBehalfOf, scope, ttlSeconds }),
198 removeAccessToken: (user, id) => call("remove_access_token", { user, id }),
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API199 createRunCredential: (input) => call("create_run_credential", input),
200 bindRunCredentials: (tokenHashes, runId) => call("bind_run_credentials", { tokenHashes, runId }),
201 revokeRunCredentials: (target) => call("revoke_run_credentials", target),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look202 // Who has access to a repository; see access.ts.
203 repoAccess: (owner, name, viewer) => call("repo_access", { viewer, path: { namespace: owner, name } }),
204 addCollaborator: (actor, owner, name, invitee, role) =>
205 call("add_collaborator", { actor, path: { namespace: owner, name }, invitee, role }),
206 setCollaboratorRole: (actor, owner, name, username, role) =>
207 call("set_collaborator_role", { actor, path: { namespace: owner, name }, username, role }),
208 removeCollaborator: (actor, owner, name, username) =>
209 call("remove_collaborator", { actor, path: { namespace: owner, name }, username }),
210 collaboratorPermission: (viewer, owner, name, username) =>
211 call("collaborator_permission", { viewer, path: { namespace: owner, name }, username }),
212 myRepoInvitations: (user) => call("my_repo_invitations", { user }),
213 respondRepoInvitation: (user, id, accept) => call("respond_repo_invitation", { user, id, accept }),
214 revokeRepoInvitation: (actor, owner, name, id) =>
215 call("revoke_repo_invitation", { actor, path: { namespace: owner, name }, id }),
216 setBasePermission: (actor, slug, base) => call("set_base_permission", { actor, slug, base_permission: base }),
217 outsideCollaborators: (viewer, slug) => call("outside_collaborators", { viewer, slug }),
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca218 // A repository's deploy keys; see deploy-keys.ts.
219 listDeployKeys: (viewer, owner, name) => call("list_deploy_keys", { viewer, path: { namespace: owner, name } }),
220 addDeployKey: (actor, owner, name, key) =>
221 call("add_deploy_key", { actor, path: { namespace: owner, name }, title: key.title, key: key.key, read_only: key.readOnly }),
222 removeDeployKey: (actor, owner, name, id) => call("remove_deploy_key", { actor, path: { namespace: owner, name }, id }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar223 // Teams; see teams.ts.
224 listTeams: (viewer, workspace, query) => call("list_teams", { viewer, workspace, query: query ?? null }),
225 getTeam: (viewer, workspace, team) => call("get_team", { viewer, workspace, team }),
226 createTeam: (actor, workspace, team) => call("create_team", { actor, workspace, ...team }),
Merge branch 'worktree-agent-ad7c6d88d93adc817'227 setTeamCreation: (actor, slug, setting) => call("set_team_creation", { actor, slug, team_creation: setting }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar228 updateTeam: (actor, workspace, team, changes) => call("update_team", { actor, workspace, team, ...changes }),
229 deleteTeam: (actor, workspace, team) => call("delete_team", { actor, workspace, team }),
230 teamMembers: (viewer, workspace, team, includeChildTeams) =>
231 call("team_members", { viewer, workspace, team, include_child_teams: includeChildTeams ?? false }),
232 setTeamMember: (actor, workspace, team, username, role) =>
233 call("set_team_member", { actor, workspace, team, username, role }),
234 removeTeamMember: (actor, workspace, team, username) =>
235 call("remove_team_member", { actor, workspace, team, username }),
236 childTeams: (viewer, workspace, team) => call("child_teams", { viewer, workspace, team }),
237 teamRepos: (viewer, workspace, team) => call("team_repos", { viewer, workspace, team }),
238 setTeamRepo: (actor, workspace, team, owner, name, role) =>
239 call("set_team_repo", { actor, workspace, team, repo: { namespace: owner, name }, role }),
240 removeTeamRepo: (actor, workspace, team, owner, name) =>
241 call("remove_team_repo", { actor, workspace, team, repo: { namespace: owner, name } }),
242 userTeams: (viewer, workspace, username) => call("user_teams", { viewer, workspace, username }),
243 teamMemberships: (viewer, workspace) => call("team_memberships", { viewer, workspace }),
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.244 teamAgents: (viewer, workspace, team) => call("team_agents", { viewer, workspace, team }),
245 setTeamAgent: (actor, workspace, team, agentId) => call("set_team_agent", { actor, workspace, team, agent_id: agentId }),
246 removeTeamAgent: (actor, workspace, team, agentId) => call("remove_team_agent", { actor, workspace, team, agent_id: agentId }),
An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how.247 agentTeams: (workspace, agentId) => call("agent_teams", { workspace, agent_id: agentId }),
248 teamAgentIndex: (workspace) => call("team_agent_index", { workspace }),
249 adoptAgentTeams: (workspaceId, agents) => call("adopt_agent_teams", { workspace_id: workspaceId, agents }),
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.250 // People; see people.ts.
251 peopleDirectory: (viewer, workspace) => call("people_directory", { viewer, workspace }),
252 setMemberProfile: (actor, workspace, username, changes) => call("set_member_profile", { actor, workspace, username, ...changes }),
Paid features: a workspace turns on Deployments with a monthly plan253 };
254}
255
Agents and memory, checks and conflicts, profiles, slug renames, custom domains256/**
257 * The slug a renamed workspace has now, for a `workspace.renamed` handler:
258 * asked of identity by the workspace's id, so that renames delivered twice
259 * or out of order converge. Falls back to the event's `to`.
260 */
261export async function currentWorkspaceSlug(
262 identity: ServiceBinding,
263 renamed: { workspaceId: string; to: string },
264): Promise<string> {
265 const names = await identityClient(identity).usernames([renamed.workspaceId]);
266 return names[renamed.workspaceId] ?? renamed.to;
267}
268
269/** The slugs whose rows move to `current`: the two a rename names, less `current`. */
270export function staleSlugs(renamed: { from: string; to: string }, current: string): string[] {
271 return [...new Set([renamed.from, renamed.to])].filter((slug) => slug !== current);
272}
273
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look274/**
275 * Where a transferred repository is now, as `namespace/name`, for a
276 * `repo.transferred` handler: asked of repos by id, so transfers delivered
277 * twice or out of order converge. Falls back to the event's destination.
278 */
279export async function currentRepoPath(
280 repos: ServiceBinding,
281 transferred: { repoId: string; name: string; to: string },
282): Promise<string> {
283 const path = await rpc<{ namespace: string; name: string } | null>(repos, "path_by_id", { id: transferred.repoId });
284 return path ? `${path.namespace}/${path.name}` : `${transferred.to}/${transferred.name}`;
285}
286
287/** The paths whose rows move to `current`: the two a transfer names, less `current`. */
288export function stalePaths(transferred: { name: string; from: string; to: string }, current: string): string[] {
289 return [...new Set([transferred.from, transferred.to].map((ns) => `${ns}/${transferred.name}`))].filter(
290 (path) => path !== current,
291 );
292}
293
294/**
295 * A repository's path change, from `repo.transferred` or `repo.renamed`,
296 * read the same way: the two paths (`namespace/name`) the event names, old
297 * then new. Null for any other event.
298 */
299export type RepoMove = { repoId: string; paths: [string, string] };
300
301export function repoMove(event: { type: string; data: unknown }): RepoMove | null {
302 const data = event.data as Record<string, string>;
303 if (event.type === "repo.transferred") {
304 return { repoId: data.repoId!, paths: [`${data.from}/${data.name}`, `${data.to}/${data.name}`] };
305 }
306 if (event.type === "repo.renamed") {
307 return { repoId: data.repoId!, paths: [`${data.namespace}/${data.from}`, `${data.namespace}/${data.to}`] };
308 }
309 return null;
310}
311
312/**
313 * Where a moved repository is now, as `namespace/name`: asked of repos by
314 * id, so moves delivered twice or out of order converge. Falls back to the
315 * event's new path.
316 */
317export async function currentMovedPath(repos: ServiceBinding, move: RepoMove): Promise<string> {
318 const path = await rpc<{ namespace: string; name: string } | null>(repos, "path_by_id", { id: move.repoId });
319 return path ? `${path.namespace}/${path.name}` : move.paths[1];
320}
321
322/** The paths whose rows move to `current`: the two a move names, less `current`. */
323export function staleMovedPaths(move: RepoMove, current: string): string[] {
324 return [...new Set(move.paths)].filter((path) => path !== current);
325}
326
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace327/** Staff-only identity. Only sudo binds to it; see `IdentityAdminApi`. */
328export function identityAdminClient(service: ServiceBinding): IdentityAdminApi {
329 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
330 return {
331 workspaces: (query) => call("admin_workspaces", { query: query ?? null }),
332 workspace: (slug) => call("admin_workspace", { slug }),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look333 waitlist: (query, status) => call("admin_waitlist", { query: query ?? null, status: status ?? null }),
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas334 waitlistPending: () => call("admin_waitlist_pending", {}),
335 decideWaitlist: (id, approve, staff, note) => call("admin_decide_waitlist", { id, approve, staff, note: note ?? null }),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look336 invites: (query) => call("admin_invites", { query: query ?? null }),
337 revokeInvite: (id, staff) => call("admin_revoke_invite", { id, staff }),
338 mintInvite: (email, staff) => call("admin_mint_invite", { email, staff }),
339 grantInvites: (target, name, amount, note, staff) =>
340 call("admin_grant_invites", { target, name, amount, note, staff }),
341 inviteTree: (username) => call("admin_invite_tree", { username }),
342 workspaceInvites: (slug) => call("admin_workspace_invites", { slug }),
Merge shared invite links: label, uses, expiry, domains; joined through recorded (identity 0038)343 sharedInvites: () => call("admin_shared_invites", {}),
344 createSharedInvite: (link, staff) =>
345 call("admin_create_shared_invite", {
346 label: link.label,
347 max_uses: link.maxUses,
348 expires_on: link.expiresOn,
349 domains: link.domains,
350 staff,
351 }),
352 revokeSharedInvite: (id, staff) => call("admin_revoke_shared_invite", { id, staff }),
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member353 deletedWorkspaces: () => call("admin_deleted_workspaces", {}),
354 restoreWorkspace: (workspaceId, staff) => call("admin_restore_workspace", { workspaceId, staff }),
355 purgeWorkspace: (workspaceId, staff, confirm) => call("admin_purge_workspace", { workspaceId, staff, confirm }),
Merge branch 'worktree-agent-a8385d293d42c913a'356 aliases: () => call("admin_aliases", {}),
357 setAlias: (alias, workspace, note, staff) => call("admin_set_alias", { alias, workspace, note, staff }),
358 removeAlias: (alias, reason, staff) => call("admin_remove_alias", { alias, reason, staff }),
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace359 };
360}
361
Paid features: a workspace turns on Deployments with a monthly plan362export function reposClient(service: ServiceBinding): ReposApi {
363 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
364 return {
365 get: (path, viewer) => call("get", { path, viewer }),
366 getById: (id, viewer) => call("get_by_id", { id, viewer }),
Fast pages, required checks on the branch, self-hosted runners, honest incidents367 readable: (ids, viewer) => call("readable", { ids, viewer }),
Agents and memory, checks and conflicts, profiles, slug renames, custom domains368 publicNamespaces: (ownerId) => call("public_namespaces", { ownerId }),
Paid features: a workspace turns on Deployments with a monthly plan369 list: (viewer, options = {}) => call("list", { viewer, ...options }),
370 create: (owner, input) => call("create", { owner, ...input }),
371 update: (actor, path, changes) => call("update", { actor, path, ...changes }),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look372 transfer: (actor, path, to) => call("transfer", { actor, path, to }),
373 delete: (actor, path, confirm) => call("delete", { actor, path, confirm }),
374 deleted: (viewer, namespace) => call("deleted", { viewer, namespace }),
375 restore: (actor, path) => call("restore", { actor, path }),
376 purge: (actor, path, confirm) => call("purge", { actor, path, confirm }),
377 rename: (actor, path, name) => call("rename", { actor, path, name }),
378 archive: (actor, path, archived) => call("archive", { actor, path, archived }),
379 setVisibility: (actor, path, isPrivate, confirm) => call("set_visibility", { actor, path, isPrivate, confirm }),
380 setDefaultBranch: (actor, path, branch) => call("set_default_branch", { actor, path, branch }),
381 renameBranch: (actor, path, from, to) => call("rename_branch", { actor, path, from, to }),
382 resolveBranch: (repoId, branch) => call("resolve_branch", { repoId, branch }),
383 statusById: (id) => call("status_by_id", { id }),
Merge branch 'worktree-agent-a2013627e5ea4ab13'384 storageOptions: () => call("storage_options", {}),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look385 resolvePath: (path) => call("resolve_path", { path }),
Paid features: a workspace turns on Deployments with a monthly plan386 tree: (path, viewer, ref, treePath) =>
387 call("tree", { path, viewer, ref, treePath }),
388 blob: (path, viewer, ref, filePath) =>
389 call("blob", { path, viewer, ref, filePath }),
390 log: (path, viewer, ref, limit) => call("log", { path, viewer, ref, limit }),
391 blame: (path, viewer, ref, filePath) => call("blame", { path, viewer, ref, filePath }),
392 forkForPull: (sourceId, pullId, actor) =>
393 call("fork_for_pull", { sourceId, pullId, actor }),
394 gitAccess: (path, viewer, service) =>
395 call("git_access", { path, viewer, service }),
396 branches: (path, viewer) => call("branches", { path, viewer }),
Branches and Tags pages, each file's last commit, and the branch menu on files397 lastCommits: (path, viewer, ref, treePath) => call("last_commits", { path, viewer, ref, treePath }),
Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25398 branchDrift: (path, viewer, base, heads) => call("branch_drift", { path, viewer, base, heads }),
Branches and Tags pages, each file's last commit, and the branch menu on files399 tags: (path, viewer) => call("tags", { path, viewer }),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97400 about: (path, viewer) => call("about", { path, viewer }),
401 languages: (path, viewer) => call("languages", { path, viewer }),
402 contributors: (path, viewer) => call("contributors", { path, viewer }),
403 license: (path, viewer) => call("license", { path, viewer }),
404 stars: (path, viewer) => call("stars", { path, viewer }),
405 star: (actor, path, starred) => call("star", { path, actor, starred }),
406 stargazers: (path, viewer, page) => call("stargazers", { path, viewer, page: page ?? null }),
407 starred: (username, viewer) => call("starred", { username, viewer }),
408 releases: (path, viewer) => call("releases", { path, viewer }),
409 release: (path, viewer, which) => call("release", { path, viewer, id: which.id ?? null, tag: which.tag ?? null, latest: which.latest ?? false }),
410 createRelease: (actor, path, release) => call("create_release", { path, actor, ...release }),
411 updateRelease: (actor, path, id, change) => call("update_release", { path, actor, id, ...change }),
412 deleteRelease: (actor, path, id) => call("delete_release", { path, actor, id }),
Download ZIP from the Code button; a slimmer lifecycle panel; agent steps say what was done, not sandbox paths413 listFiles: (repoId, ref, limit) => call("list_files", { repoId, ref, skipDirs: [], limit }),
414 rawBlobs: (repoId, hashes, maxBytes) => call("raw_blobs", { repoId, hashes, maxBytes }),
Merge g1tusercontent.com: registry answers run nothing in a browser, the site's pages run only their own scripts, repository files and avatars on their own origin, raw files rate limited per address415 rawFile: (repoId, ref, path, maxBytes) => call("raw_file", { repoId, ref, path, maxBytes }),
Fast pages, required checks on the branch, self-hosted runners, honest incidents416 commitFile: (repo, actor, file) => call("commit_file", { repo, actor, ...file }),
Paid features: a workspace turns on Deployments with a monthly plan417 land: (sourceId, actor, branch) => call("land", { sourceId, actor, branch }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar418 compare: (repoId, viewer, base, head, baseBranch) => call("compare", { repoId, viewer, base, head, baseBranch }),
Merge branch 'worktree-agent-ac5b181a013e54348'419 claimBackups: (limit, maxRunning) => call("claim_backups", { limit, maxRunning }),
420 // The sandbox's own calls are snake_case (they come through the API).
421 failBackup: (jobId, token, error) => call("backup_fail", { job_id: jobId, token, error }),
Paid features: a workspace turns on Deployments with a monthly plan422 };
423}
424
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar425/**
426 * `PullDetail`'s own fields that the work service writes in snake_case:
427 * `g1t_contracts::work::PullDetail` has no camelCase renaming, though what
428 * it holds (`Pull`, `CheckRun` and the rest) does. Each with its name here.
429 */
430const PULL_DETAIL_FIELDS = [
431 ["review_pending", "reviewPending"],
432 ["earlier_checks", "earlierChecks"],
433 ["required_checks", "requiredChecks"],
434 ["code_owners", "codeOwners"],
435] as const;
436
437/**
438 * A pull request's detail as the work service sent it, with its own fields
439 * in the camelCase this package uses: read at the edge, by `workClient`'s
440 * `getPull`. Either spelling is taken, so a service that already sends
441 * camelCase reads the same.
442 */
443export function pullDetailFromWire(raw: Record<string, unknown>): PullDetail {
444 const detail: Record<string, unknown> = { ...raw };
445 for (const [snake, camel] of PULL_DETAIL_FIELDS) {
446 if (snake in detail) {
447 if (!(camel in detail)) detail[camel] = detail[snake];
448 delete detail[snake];
449 }
450 }
451 if (typeof detail.reviewPending !== "boolean") detail.reviewPending = false;
452 return detail as PullDetail;
453}
454
Paid features: a workspace turns on Deployments with a monthly plan455export function workClient(service: ServiceBinding): WorkApi {
456 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
457 return {
458 openIssue: (actor, repo, input) => call("open_issue", { actor, repo, ...input }),
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step459 delegateIssue: (actor, repo, input) => call("delegate_issue", { actor, repo, ...input }),
Paid features: a workspace turns on Deployments with a monthly plan460 listIssues: (repo, viewer, filter = {}) => call("list_issues", { repo, viewer, ...filter }),
461 getIssue: (repo, number, viewer) => call("get_issue", { repo, number, viewer }),
462 updateIssue: (actor, repo, number, input) =>
463 call("update_issue", { actor, repo, number, ...input }),
464 closeIssue: (actor, repo, number, reason) =>
465 call("close_issue", { actor, repo, number, reason }),
466 reopenIssue: (actor, repo, number) => call("reopen_issue", { actor, repo, number }),
467 listLabels: (repo, viewer) => call("list_labels", { repo, viewer }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar468 saveLabel: (actor, repo, label) => call("save_label", { actor, repo, ...label }),
469 deleteLabel: (actor, repo, name) => call("delete_label", { actor, repo, name }),
470 addDefaultLabels: (actor, repo) => call("add_default_labels", { actor, repo }),
471 setLabels: (actor, repo, number, labels, change = "set") =>
472 call("set_labels", { actor, repo, number, labels, change }),
473 listMilestones: (repo, viewer, state) => call("list_milestones", { repo, viewer, state }),
474 getMilestone: (repo, number, viewer) => call("get_milestone", { repo, number, viewer }),
475 saveMilestone: (actor, repo, milestone) => call("save_milestone", { actor, repo, ...milestone }),
476 deleteMilestone: (actor, repo, number) => call("delete_milestone", { actor, repo, number }),
Paid features: a workspace turns on Deployments with a monthly plan477 counts: (repo, viewer) => call("counts", { repo, viewer }),
478 addComment: (actor, repo, number, comment) =>
479 call("add_comment", { actor, repo, number, ...comment }),
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts480 editComment: (actor, repo, commentId, body) => call("edit_comment", { actor, repo, commentId, body }),
481 deleteComment: (actor, repo, commentId) => call("delete_comment", { actor, repo, commentId }),
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar482 workspaceAgentComment: (repo, number, agent, actingFor, body) =>
483 call("workspace_agent_comment", { repo, number, agent, acting_for: actingFor, body }),
484 workspaceAgentReview: (repo, number, agent, actingFor, verdict, body) =>
485 call("workspace_agent_review", { repo, number, agent, acting_for: actingFor, verdict, body }),
Paid features: a workspace turns on Deployments with a monthly plan486 startChecks: (pullId) => call("start_checks", { pullId }),
487 reportChecks: (runId, token, report) =>
488 call("report_checks", { runId, token, ...report }),
489 startReview: (pullId) => call("start_review", { pullId }),
490 failReview: (runId, token, error) => call("report_review", { runId, token, error }),
Agents and memory, checks and conflicts, profiles, slug renames, custom domains491 startMergecheck: (pullId) => call("start_mergecheck", { pullId }),
492 failMergecheck: (pullId, token, error) => call("report_mergecheck", { pullId, token, error }),
Paid features: a workspace turns on Deployments with a monthly plan493 advance: (pullId) => call("advance", { pullId }),
494 stall: (pullId, reason) => call("stall", { pullId, reason }),
495 managedPulls: (repoId) => call("managed_pulls", { repoId }),
496 queue: (repo, viewer) => call("queue", { repo, viewer }),
497 queueBuild: (repoId) => call("queue_build", { repoId }),
498 failQueue: (entryId, token, error) => call("report_queue", { entryId, token, error }),
499 removeFromQueue: (actor, repo, number) => call("remove_from_queue", { actor, repo, number }),
500 messageAgent: (actor, repo, number, body) => call("message_agent", { actor, repo, number, body }),
501 catchUpJob: (pullId) => call("catch_up_job", { pullId }),
502 wakeForMessages: (pullId) => call("wake_for_messages", { pullId }),
503 getSettings: (repo, viewer) => call("get_settings", { repo, viewer }),
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge504 // Rulesets travel in snake_case (rules.ts).
505 listRulesets: (owner, viewer, includeParents = false) =>
506 call("list_rulesets", { viewer, ...owner, include_parents: includeParents }),
507 getRuleset: (owner, id, viewer) => call("get_ruleset", { viewer, ...owner, id }),
508 saveRuleset: (actor, owner, ruleset, id) => call("save_ruleset", { actor, ...owner, id: id ?? null, ruleset }),
509 deleteRuleset: (actor, owner, id) => call("delete_ruleset", { actor, ...owner, id }),
510 effectiveRules: (repo, name, viewer, target = "branch") => call("effective_rules", { viewer, repo, name, target }),
511 ruleEvaluations: (owner, viewer, filter = {}) => call("rule_evaluations", { viewer, ...owner, ...filter }),
Fast pages, required checks on the branch, self-hosted runners, honest incidents512 seenChecks: (repo, viewer) => call("seen_checks", { repo, viewer }),
Merge checks: statuses and check runs on every commit513 commitChecks: (repo, viewer, shas) => call("commit_checks", { repo, viewer, shas }),
514 getCheckRun: (repo, id, viewer) => call("get_check_run", { repo, id, viewer }),
515 checkRunAnnotations: (repo, id, viewer) => call("check_run_annotations", { repo, id, viewer }),
516 requestCheckAction: (actor, repo, id, identifier) => call("request_check_action", { actor, repo, id, identifier }),
517 rerequestCheckRun: (actor, repo, id) => call("rerequest_check_run", { actor, repo, id }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar518 codeownersErrors: (repo, viewer, ref) => call("codeowners_errors", { repo, viewer, ref: ref ?? null }),
Paid features: a workspace turns on Deployments with a monthly plan519 updateSettings: (actor, repo, settings) =>
520 call("update_settings", { actor, repo, settings }),
521 openPull: (actor, repo, input) => call("open_pull", { actor, repo, ...input }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar522 listPulls: (repo, viewer, state, filter = {}) => call("list_pulls", { repo, viewer, state, ...filter }),
Fast pages, required checks on the branch, self-hosted runners, honest incidents523 pullsForRepos: (repoIds, viewer, limit) => call("pulls_for_repos", { repoIds, viewer, limit }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar524 // Its own fields arrive in snake_case: read into camelCase here.
525 getPull: (repo, number, viewer) =>
526 call<Result<Record<string, unknown>>>("get_pull", { repo, number, viewer }).then(
527 (found): Result<PullDetail> => (found.ok ? { ok: true, value: pullDetailFromWire(found.value) } : found),
528 ),
Paid features: a workspace turns on Deployments with a monthly plan529 updatePull: (actor, repo, number, changes) =>
530 call("update_pull", { actor, repo, number, ...changes }),
Catching up with main takes seconds when the two sides touched different files531 catchUpPull: (actor, repo, number) => call("catch_up_pull", { actor, repo, number }),
Paid features: a workspace turns on Deployments with a monthly plan532 readyPull: (actor, repo, number, summary) =>
533 call("ready_pull", { actor, repo, number, summary }),
534 closePull: (actor, repo, number) => call("close_pull", { actor, repo, number }),
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts535 reopenPull: (actor, repo, number) => call("reopen_pull", { actor, repo, number }),
536 convertPullToDraft: (actor, repo, number) => call("convert_pull_to_draft", { actor, repo, number }),
Paid features: a workspace turns on Deployments with a monthly plan537 mergePull: (actor, repo, number, options = {}) =>
538 call("merge_pull", { actor, repo, number, ...options }),
539 listActivePulls: (viewer) => call("list_active_pulls", { viewer }),
Agents and memory, checks and conflicts, profiles, slug renames, custom domains540 byAuthor: (username, viewer, filter = {}) => call("by_author", { username, viewer, ...filter }),
Chat controls, public profiles, shadcn selects, and no Docs tab in a project541 contributions: (username, viewer) => call("contributions", { username, viewer }),
Paid features: a workspace turns on Deployments with a monthly plan542 startPlan: (actor, repo, brief) => call("start_plan", { actor, repo, brief }),
543 failPlan: (planId, token, error) => call("report_plan", { planId, token, error }),
544 getPlan: (repo, viewer, id) => call("get_plan", { repo, viewer, id }),
545 listPlans: (repo, viewer) => call("list_plans", { repo, viewer }),
546 applyPlan: (actor, repo, id, options = {}) =>
547 call("apply_plan", { actor, repo, id, ...options }),
548 queueIssue: (actor, repo, number, queued) =>
549 call("queue_issue", { actor, repo, number, queued }),
550 readyIssues: (repoId) => call("ready_issues", { repoId }),
551 listAssignedIssues: (viewer) => call("list_assigned_issues", { viewer }),
552 appendSession: (actor, repo, number, entries) =>
553 call("append_session", { actor, repo, number, entries }),
554 readSession: (repo, number, viewer, afterSeq = 0) =>
555 call("read_session", { repo, number, viewer, afterSeq }),
556 };
557}
558
559export function billingClient(service: ServiceBinding): BillingApi {
560 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
561 return {
562 status: () => call("status", {}),
563 account: (workspace, viewer) => call("account", { workspace, viewer }),
564 ledger: (workspace, viewer) => call("ledger", { workspace, viewer }),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging565 credits: (workspace, viewer) => call("credits", { workspace, viewer }),
The statement is a month at a time, a line per kind of charge566 statement: (workspace, viewer, month = null, group = "day") => call("statement", { workspace, viewer, month, group }),
567 statementEntries: (workspace, viewer, filter) =>
568 call("statement_entries", {
569 workspace,
570 viewer,
571 month: filter.month,
572 kind: filter.kind,
573 day: filter.day ?? null,
574 project: filter.project ?? null,
575 before: filter.before ?? null,
576 }),
Paid features: a workspace turns on Deployments with a monthly plan577 usage: (workspace, viewer, since) => call("usage", { workspace, viewer, since }),
Mission control shows model usage, yours and the workspace's: tokens, cost, active days, cache share, each day, and the mix578 tokenUsage: (workspace, viewer, options = {}) =>
579 call("token_usage", { workspace, viewer, person: options.person ?? null, days: options.days ?? null }),
580 recordTokens: (usage) => call("record_tokens", usage),
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens581 gatewayModels: () => call("gateway_models", {}),
Merge branch 'main' into actions-toolkit-oidc-artifacts582 modelDefaults: () => call("model_defaults", {}),
583 recordDiscovery: (provider, models, by, error = null) => call("record_discovery", { provider, models, by, error }),
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens584 gatewayAdmit: (workspace) => call("gateway_admit", { workspace }),
585 recordGateway: (record) => call("record_gateway", record),
586 gatewayRequests: (workspace, viewer, options = {}) =>
587 call("gateway_requests", { workspace, viewer, limit: options.limit ?? null, before: options.before ?? null }),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look588 checkout: (actor, workspace, amountCents, returnUrl, method = "card") =>
589 call("checkout", { actor, workspace, amountCents, returnUrl, method }),
Paid features: a workspace turns on Deployments with a monthly plan590 confirm: (workspace, viewer, session) => call("confirm", { workspace, viewer, session }),
591 canStart: (workspace) => call("can_start", { workspace }),
592 trial: (workspace, exempt) => call("trial", { workspace, exempt }),
593 startRun: (run) => call("start_run", run),
594 features: (workspace, viewer) => call("features", { workspace, viewer }),
595 subscribe: (actor, workspace, feature, returnUrl) =>
596 call("subscribe", { actor, workspace, feature, returnUrl }),
597 confirmSubscription: (workspace, viewer, session) =>
598 call("confirm_subscription", { workspace, viewer, session }),
599 cancelSubscription: (actor, workspace, feature, resume = false) =>
600 call("cancel_subscription", { actor, workspace, feature, resume }),
601 hasFeature: (workspace, feature) => call("has_feature", { workspace, feature }),
Merge Stripe Tax, the card fee on card payments, and one free workspace per person602 freeWorkspaces: (workspaces) => call("free_workspaces", { workspaces }),
Paid features: a workspace turns on Deployments with a monthly plan603 chargeFeature: (charge) => call("charge_feature", charge),
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace604 billingPortal: (actor, workspace, returnUrl) => call("billing_portal", { actor, workspace, return_url: returnUrl }),
Every sandbox is metered by the second605 recordSandbox: (usage) => call("record_sandbox", usage),
Usage limits: unpaid usage can only go so far606 limit: (workspace, viewer) => call("limit", { workspace, viewer }),
607 checkLimit: (workspace) => call("check_limit", { workspace }),
Prices keep themselves current with what g1t pays608 prices: () => call("prices", {}),
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas609 notePending: (workspace, source, costMicros, detail = null) => call("note_pending", { workspace, source, costMicros, detail }),
610 usageMeters: (workspace, viewer) => call("usage_meters", { workspace, viewer }),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look611 setSpendLimit: (actor, workspace, spendLimitMicros, useFullLimit = false, raiseOnce = false) =>
612 call("set_spend_limit", { actor, workspace, spendLimitMicros, use_full_limit: useFullLimit, raise_once: raiseOnce }),
Two limits, real invoices, trust that grows by itself, sales signals613 invoices: (workspace, viewer) => call("invoices", { workspace, viewer }),
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put614 entitlements: (workspace) => call("entitlements", { workspace }),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look615 reserve: (reservation) => call("reserve", reservation),
616 settle: (reservationId, actualMicros) => call("settle", { reservationId, actualMicros }),
617 cardCheck: (actor, workspace, returnUrl) => call("card_check", { actor, workspace, returnUrl }),
618 confirmCardCheck: (workspace, viewer, session) => call("confirm_card_check", { workspace, viewer, session }),
619 requestLimit: (actor, workspace, request) => call("request_limit", { actor, workspace, ...request }),
620 limitRequests: (workspace, viewer) => call("limit_requests", { workspace, viewer }),
621 confirmSpike: (actor, workspace, keepGoing) => call("confirm_spike", { actor, workspace, keepGoing }),
622 setCaps: (actor, workspace, caps) => call("set_caps", { actor, workspace, ...caps }),
Usage, Billing settings and prepaid AI credit; fixes from the UX audit623 usageReport: (workspace, viewer, range) =>
624 call("usage_report", { workspace, viewer, from: range.from, until: range.until, products: range.products ?? [], projects: range.projects ?? [] }),
625 aiCredit: (workspace, viewer) => call("ai_credit", { workspace, viewer }),
626 buyAiCredit: (actor, workspace, amountCents, returnUrl) => call("buy_ai_credit", { actor, workspace, amountCents, returnUrl }),
627 confirmAiCredit: (workspace, viewer, session) => call("confirm_ai_credit", { workspace, viewer, session }),
628 setAiReload: (actor, workspace, reload) => call("set_ai_reload", { actor, workspace, ...reload }),
629 setBudget: (actor, workspace, budget) => call("set_budget", { actor, workspace, ...budget }),
630 billingDetails: (workspace, viewer) => call("billing_details", { workspace, viewer }),
631 setBillingDetails: (actor, workspace, details) => call("set_billing_details", { actor, workspace, ...details }),
Paid features: a workspace turns on Deployments with a monthly plan632 };
633}
634
Billing accounts, terms and enterprises; g1t is no longer free635export function billingAdminClient(service: ServiceBinding): BillingAdminApi {
636 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
637 return {
638 accounts: (query) => call("admin_accounts", { query: query ?? null }),
639 account: (id) => call("admin_account", { id }),
640 setTerms: (id, terms, by) => call("admin_set_terms", { id, terms, by }),
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put641 setAllowances: (id, allowances, note, by) => call("admin_set_allowances", { id, allowances, note, by }),
Billing accounts, terms and enterprises; g1t is no longer free642 createEnterprise: (name, workspaces, by) => call("admin_create_enterprise", { name, workspaces, by }),
643 attach: (workspace, account, by) => call("admin_attach", { workspace, account, by }),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging644 credit: (workspace, amountMicros, note, by, options = { kind: "goodwill" }) =>
645 call("admin_credit", {
646 workspace,
647 amount_micros: amountMicros,
648 note,
649 by,
650 kind: options.kind,
651 expires_at: options.expiresAt ?? null,
652 refund_for: options.refundFor ?? null,
653 refund_day: options.refundDay ?? null,
654 }),
655 credits: (filter = {}) =>
656 call("admin_credits", {
657 workspace: filter.workspace ?? null,
658 kind: filter.kind ?? null,
659 month: filter.month ?? null,
660 by: filter.by ?? null,
661 }),
662 revokeCredit: (id, note, by) => call("admin_revoke_credit", { id, note, by }),
sudo: reset a test workspace's billing so it starts again as a new customer; refused on a live Stripe key, for comped workspaces and for an enterprise's663 resetBilling: (workspace, confirm, note, by) => call("admin_reset_billing", { workspace, confirm, note, by }),
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace664 billingLink: (workspace, by) => call("admin_billing_link", { workspace, by }),
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard665 stripe: (fix = false, by) => call("admin_stripe", { fix, by: by ?? null }),
Stripe webhooks, enterprise invoices, and sudo for both666 enterpriseBilling: (id, email, by) => call("admin_enterprise_billing", { id, email, by }),
Merge Stripe Tax, the card fee on card payments, and one free workspace per person667 enterpriseAddress: (id, address, taxIdType, taxId, by) => call("admin_enterprise_address", { id, address, taxIdType, taxId, by }),
Stripe webhooks, enterprise invoices, and sudo for both668 invoiceEnterprise: (id, by) => call("admin_invoice_enterprise", { id, by }),
669 accountsFor: (workspaces) => call("admin_accounts", { query: null, workspaces }),
Two limits, real invoices, trust that grows by itself, sales signals670 signals: () => call("admin_signals", {}),
671 overview: () => call("admin_overview", {}),
672 sales: (workspace) => call("admin_sales", { workspace }),
673 setSales: (workspace, record, by) =>
674 call("admin_set_sales", {
675 workspace,
676 stage: record.stage,
677 owner: record.owner ?? null,
678 next_step: record.nextStep ?? null,
679 next_at: record.nextAt ?? null,
680 by,
681 }),
682 addNote: (workspace, text, by) => call("admin_add_note", { workspace, text, by }),
683 workspaceInvoices: (workspace) => call("admin_workspace_invoices", { workspace }),
Billing lists every invoice and every staff change; signals carry follow-ups684 allInvoices: (filter = {}) => call("admin_invoices", { status: filter.status ?? null, month: filter.month ?? null }),
685 audit: (filter = {}) =>
686 call("admin_audit", { by: filter.by ?? null, action: filter.action ?? null, before: filter.before ?? null }),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look687 limitRequests: (status = "open") => call("admin_limit_requests", { status }),
688 decideLimitRequest: (id, decision, amountMicros, note, by) =>
689 call("admin_decide_limit_request", { id, decision, amount_micros: amountMicros, note, by }),
690 overages: () => call("admin_overages", {}),
691 goodwill: (workspace, amountMicros, reason, by, day = null) =>
692 call("admin_goodwill", { workspace, amount_micros: amountMicros, reason, by, day }),
693 velocity: () => call("admin_velocity", {}),
694 recordPayment: (workspace, amountMicros, reference, note, by) =>
695 call("admin_record_payment", { workspace, amount_micros: amountMicros, reference, note, by }),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily696 costs: (days) => call("admin_costs", { days: days ?? null }),
697 costAlerts: () => call("admin_cost_alerts", {}),
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays698 spendCaps: () => call("admin_spend_caps", {}),
699 liftBreaker: (note, by) => call("admin_lift_breaker", { note, by }),
Merge platform pause and the hourly usage watcher: staff can pause compute, schedules, indexing or renders for everyone, the watcher emails on a breach and is never blind quietly, and the models proxy holds each run to its cap (billing 0051, integrations 0006)700 platformGuard: () => call("admin_platform_guard", {}),
701 setPause: (level, paused, note, by) => call("admin_set_pause", { level, paused, note, by }),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily702 decideProposal: (id, decision, note, by) => call("admin_decide_proposal", { id, decision, note, by }),
703 setCostSettings: (settings, by) => call("admin_set_cost_settings", { settings, by }),
704 setCostMapping: (mapping, by) =>
705 call("admin_set_cost_mapping", {
706 product: mapping.product,
707 meter: mapping.meter,
708 bucket: mapping.bucket ?? "",
709 price_meter: mapping.priceMeter ?? null,
710 own_meter: mapping.ownMeter ?? null,
711 scale_to_own: mapping.scaleToOwn ?? false,
712 drift_percent: mapping.driftPercent ?? null,
713 note: mapping.note ?? "",
714 remove: mapping.remove ?? false,
715 by,
716 }),
717 runCosts: (by) => call("admin_run_costs", { by }),
Merge branch 'main' into actions-toolkit-oidc-artifacts718 models: () => call("admin_models", {}),
719 decideModel: (model, decision, details, reason, by) =>
720 call("admin_decide_model", {
721 model,
722 decision,
723 name: details.name ?? null,
724 tier_hint: details.tierHint ?? null,
725 prices: details.prices
726 ? {
727 input_micros: details.prices.inputMicros,
728 output_micros: details.prices.outputMicros,
729 cache_read_micros: details.prices.cacheReadMicros,
730 cache_write_micros: details.prices.cacheWriteMicros,
731 cache_write_1h_micros: details.prices.cacheWrite1hMicros,
732 threshold: details.prices.threshold,
733 over_input_micros: details.prices.overInputMicros,
734 over_output_micros: details.prices.overOutputMicros,
735 over_cache_read_micros: details.prices.overCacheReadMicros,
736 over_cache_write_micros: details.prices.overCacheWriteMicros,
737 over_cache_write_1h_micros: details.prices.overCacheWrite1hMicros,
738 }
739 : null,
740 reason,
741 by,
742 }),
743 setModelDefault: (purpose, value, reason, by) =>
744 call("admin_set_model_default", {
745 purpose,
746 model: value.model ?? null,
747 tier: value.tier ?? null,
748 effort: value.effort ?? null,
749 reason,
750 by,
751 }),
Billing accounts, terms and enterprises; g1t is no longer free752 };
753}
754
Paid features: a workspace turns on Deployments with a monthly plan755export function eventsClient(service: ServiceBinding): EventsApi {
756 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
757 return {
758 publish: (events) => call("publish", { events }),
759 list: (query) => call("list", query),
Home says what people did as well as what agents did. Since you were last here now has a People column and an Agents column: who pushed how many commits to which projects, pull requests opened, merged and reviewed, issues opened and closed, docs edited, messages sent and deploys that went out, each line a link to where those are listed, with the agents' acceptance (first time, after review, didn't finish) kept as a row of their column; the sentence under the heading sums it up honestly, and says when a part of g1t could not be read, or that the span was quiet. Landed counts merged pull requests, commits pushed straight to a default branch, production deploys that went live, releases and packages, newest first; Running now adds workflow runs. Behind it, the events service answers an activity digest over a span in one round trip from its existing indexes, every push now records how many commits it carried, and chat counts the messages sent in the conversations you can read. The Home guide defines every line, and says how this scales.760 activityDigest: (args) => call("activity_digest", args),
Paid features: a workspace turns on Deployments with a monthly plan761 };
762}
763
764export function integrationsClient(service: ServiceBinding): IntegrationsApi {
765 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
766 return {
767 list: (workspace, viewer) => call("list", { workspace, viewer }),
768 connect: (actor, workspace, input) => call("connect", { actor, workspace, ...input }),
769 update: (actor, workspace, id, input) => call("update", { actor, workspace, id, ...input }),
770 disconnect: (actor, workspace, id) => call("disconnect", { actor, workspace, id }),
771 test: (actor, workspace, id) => call("test", { actor, workspace, id }),
772 deliveries: (workspace, viewer, id) => call("deliveries", { workspace, viewer, id }),
773 resolve: (workspace, viewer, reference) => call("resolve", { workspace, viewer, reference }),
774 references: (workspace, text, limit) => call("references", { workspace, text, limit }),
775 import: (actor, repo, reference, assign) => call("import", { actor, repo, reference, assign }),
Each agent has an Abilities tab: g1t's built-ins, always on within the asker's access; its computer, coming; each connected integration's actions one row each, read, import, comment or resolve, with a level for each, alone, alone when the person asked for it, ask first or never, and whose connection it runs on; and MCP servers an owner adds. Reading is alone, writing inside g1t follows today's choices, anything that leaves g1t asks first, and production deploys can't go above ask. The agents service enforces every level: ask first posts a card to allow or deny and parks a session until it's answered, a refusal names its rule in the transcript and the audit log, and a missing ability posts a request to owners. The agent abilities guide says how.776 comment: (actor, workspace, reference, text, link) => call("comment", { actor, workspace, reference, text, link }),
777 close: (actor, workspace, reference, text, link) => call("close", { actor, workspace, reference, text, link }),
Paid features: a workspace turns on Deployments with a monthly plan778 links: (repo, number) => call("links", { repo, number }),
779 modelProvider: (workspace) => call("model_provider", { workspace }),
780 openModelSession: (run) => call("open_model_session", run),
781 modelUpstream: (token) => call("model_upstream", { token }),
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens782 gatewayUpstream: (workspace) => call("gateway_upstream", { workspace }),
AI Gateway: OpenAI's format, open models, and your own providers783 gatewayProviders: (workspace) => call("gateway_providers", { workspace }),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily784 closeModelSessions: (tokenHashes) => call("close_model_sessions", { token_hashes: tokenHashes }),
Merge platform pause and the hourly usage watcher: staff can pause compute, schedules, indexing or renders for everyone, the watcher emails on a breach and is never blind quietly, and the models proxy holds each run to its cap (billing 0051, integrations 0006)785 capModelSessions: (tokenHashes, capMicros) => call("cap_model_sessions", { token_hashes: tokenHashes, cap_micros: capMicros }),
Paid features: a workspace turns on Deployments with a monthly plan786 routes: (workspace, viewer) => call("routes", { workspace, viewer }),
787 setRoutes: (actor, workspace, routes) => call("set_routes", { actor, workspace, routes }),
788 };
789}
790
791export function webhooksClient(service: ServiceBinding): WebhooksApi {
792 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
793 return {
794 list: (viewer, owner) => call("list", { viewer, ...owner }),
795 create: (actor, owner, input) => call("create", { actor, ...owner, ...input }),
796 update: (actor, owner, id, input) => call("update", { actor, ...owner, id, ...input }),
797 delete: (actor, owner, id) => call("delete", { actor, ...owner, id }),
798 ping: (actor, owner, id) => call("ping", { actor, ...owner, id }),
799 deliveries: (viewer, owner, id) => call("deliveries", { viewer, ...owner, id }),
800 redeliver: (actor, owner, deliveryId) => call("redeliver", { actor, ...owner, deliveryId }),
801 };
802}
803
804export function actionsClient(service: ServiceBinding): ActionsApi {
805 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
806 return {
807 workflows: (repo, viewer) => call("workflows", { repo, viewer }),
808 runs: (repo, viewer, filter = {}) => call("runs", { repo, viewer, ...filter }),
Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009)809 run: (repo, viewer, id, attempt) => call("run", { repo, viewer, id, attempt: attempt ?? null }),
Paid features: a workspace turns on Deployments with a monthly plan810 logs: (repo, viewer, job, after = 0) => call("logs", { repo, viewer, job, after }),
Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009)811 summaries: (repo, viewer, id, attempt) => call("summaries", { repo, viewer, id, attempt: attempt ?? null }),
812 jobLogText: (repo, viewer, job) => call("job_log_text", { repo, viewer, job }),
813 runLogs: (repo, viewer, id, attempt) => call("run_logs", { repo, viewer, id, attempt: attempt ?? null }),
Paid features: a workspace turns on Deployments with a monthly plan814 dispatch: (actor, repo, workflow, ref, inputs) => call("dispatch", { actor, repo, workflow, ref, inputs }),
Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009)815 cancel: (actor, repo, id, force = false) => call("cancel", { actor, repo, id, force }),
816 rerun: (actor, repo, id, failedOnly = false, options = {}) =>
817 call("rerun", { actor, repo, id, failed_only: failedOnly, job: options.job ?? null, debug: options.debug ?? false }),
Paid features: a workspace turns on Deployments with a monthly plan818 setWorkflowEnabled: (actor, repo, workflow, enabled) =>
819 call("set_workflow_enabled", { actor, repo, workflow, enabled }),
820 settings: (actor, owner, kind) => call("settings", { actor, ...owner, kind }),
Secrets and variables: one list, rows per environment, for workflows and deployments821 setSetting: (actor, owner, kind, name, value, options = {}) =>
822 call("set_setting", { actor, ...owner, kind, name, value, ...options }),
823 deleteSetting: (actor, owner, kind, name, id) => call("delete_setting", { actor, ...owner, kind, name, id }),
Merge branch 'worktree-agent-a3abfcce648e87dca'824 approveRun: (actor, repo, id) => call("approve_run", { actor, repo, id }),
825 pendingDeployments: (repo, viewer, id) => call("pending_deployments", { repo, viewer, id }),
826 reviewDeployments: (actor, repo, id, state, environments = [], comment) =>
827 call("review_deployments", { actor, repo, id, state, environments, comment: comment ?? null }),
828 actionsSettings: (repo, viewer) => call("actions_settings", { repo, viewer }),
829 setActionsSettings: (actor, repo, change) => call("set_actions_settings", { actor, repo, ...change }),
830 workspaceActionsSettings: (workspace, viewer) => call("workspace_actions_settings", { workspace, viewer }),
831 setWorkspaceActionsSettings: (actor, workspace, change) =>
832 call("set_workspace_actions_settings", { actor, workspace, ...change }),
833 environments: (repo, viewer) => call("environments", { repo, viewer }),
834 setEnvironment: (actor, repo, name, change) => call("set_environment", { actor, repo, name, ...change }),
835 deleteEnvironment: (actor, repo, name) => call("delete_environment", { actor, repo, name }),
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R2836 artifacts: (repo, viewer, filter = {}) => call("artifacts", { repo, viewer, ...filter }),
837 artifactDownload: (repo, viewer, by) => call("artifact_download", { repo, viewer, ...by }),
838 deleteArtifact: (actor, repo, id) => call("delete_artifact", { actor, repo, id }),
839 artifactRetention: (repo, viewer, days) => call("artifact_retention", { repo, viewer, days }),
Paid features: a workspace turns on Deployments with a monthly plan840 };
841}
842
Deployments: a preview for every pull request, production on g1t.page843
Fast pages, required checks on the branch, self-hosted runners, honest incidents844/** Self-hosted runners, kept by the actions service. */
845export function runnersClient(service: ServiceBinding): RunnersApi {
846 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
847 return {
848 stuck: (viewer) => call("stuck_jobs", { viewer }),
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.849 activity: (actor, workspace) => call("runner_activity", { actor, workspace }),
Fast pages, required checks on the branch, self-hosted runners, honest incidents850 list: (actor, owner) => call("runners", { actor, ...owner }),
851 createToken: (actor, owner, group) => call("create_registration_token", { actor, ...owner, group }),
852 remove: (actor, owner, id) => call("remove_runner", { actor, ...owner, id }),
853 groups: (actor, workspace) => call("runner_groups", { actor, workspace }),
854 setGroup: (actor, workspace, group) => call("set_runner_group", { actor, workspace, ...group }),
855 deleteGroup: (actor, workspace, id) => call("delete_runner_group", { actor, workspace, id }),
856 settings: (actor, owner) => call("runner_settings", { actor, ...owner }),
857 setSettings: (actor, owner, change) => call("set_runner_settings", { actor, ...owner, ...change }),
858 };
859}
860
Deployments: a preview for every pull request, production on g1t.page861export function deploymentsClient(service: ServiceBinding): DeploymentsApi {
862 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
863 return {
Projects: what a workspace builds and runs, first on every page864 settings: (project, viewer) => call("settings", { project, viewer }),
865 updateSettings: (actor, project, changes) => call("update_settings", { actor, project, changes }),
866 list: (project, viewer) => call("list", { project, viewer }),
867 get: (project, id, viewer) => call("get", { project, id, viewer }),
868 redeploy: (actor, project, branch) => call("redeploy", { actor, project, branch }),
869 takeDown: (actor, project, branch) => call("take_down", { actor, project, branch }),
870 overview: (workspace, viewer) => call("overview", { workspace, viewer }),
Deployments: a preview for every pull request, production on g1t.page871 usage: (workspace, viewer) => call("usage", { workspace, viewer }),
Agents and memory, checks and conflicts, profiles, slug renames, custom domains872 domains: (project, viewer) => call("domains", { project, viewer }),
873 addDomain: (actor, project, hostname, options = {}) =>
874 call("add_domain", { actor, project, hostname, twin: !!options.twin }),
875 removeDomain: (actor, project, id) => call("remove_domain", { actor, project, id }),
876 refreshDomain: (actor, project, id) => call("refresh_domain", { actor, project, id }),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97877 repoDeployments: (repo, viewer, filter = {}) => call("list_deployments", { repo, viewer, ...filter }),
878 repoDeployment: (repo, id, viewer) => call("get_deployment", { repo, id, viewer }),
879 environments: (repo, viewer) => call("list_environments", { repo, viewer }),
880 createDeployment: (actor, repo, input) => call("create_deployment", { repo, actor, ...input }),
881 createDeploymentStatus: (actor, repo, id, input) => call("create_deployment_status", { repo, actor, id, ...input }),
Deployments: a preview for every pull request, production on g1t.page882 };
883}
Projects: what a workspace builds and runs, first on every page884
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member885/** Packages: the registries beside the code (services/packages). */
886export function packagesClient(service: ServiceBinding): PackagesApi {
887 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
888 return {
889 list: (workspace, viewer, filter = {}) =>
890 call("list_packages", {
891 workspace,
892 viewer,
893 ecosystem: filter.ecosystem ?? null,
894 repo_id: filter.repoId ?? null,
895 query: filter.query ?? null,
896 }),
897 get: (workspace, ecosystem, name, viewer) => call("get_package", { workspace, ecosystem, name, viewer }),
898 deleteVersion: (actor, workspace, ecosystem, name, version, surface) =>
899 call("delete_version", { actor, workspace, ecosystem, name, version, surface: surface ?? null }),
900 deletePackage: (actor, workspace, ecosystem, name, surface) =>
901 call("delete_package", { actor, workspace, ecosystem, name, surface: surface ?? null }),
902 set: (actor, workspace, ecosystem, name, change, surface) =>
903 call("set_package", {
904 actor,
905 workspace,
906 ecosystem,
907 name,
908 visibility: change.visibility ?? null,
909 link: change.link ?? null,
910 unlink: change.unlink ?? false,
Merge packages: roles, Actions access, source label, soft delete, API911 inherit_access: change.inheritAccess ?? null,
912 surface: surface ?? null,
913 }),
914 settings: (workspace, ecosystem, name, viewer) => call("package_settings", { workspace, ecosystem, name, viewer }),
915 deleted: (workspace, viewer) => call("deleted_packages", { workspace, viewer }),
916 restorePackage: (actor, workspace, ecosystem, name, surface) =>
917 call("restore_package", { actor, workspace, ecosystem, name, surface: surface ?? null }),
918 restoreVersion: (actor, workspace, ecosystem, name, version, surface) =>
919 call("restore_version", { actor, workspace, ecosystem, name, version, surface: surface ?? null }),
920 setAccess: (actor, workspace, ecosystem, name, who, role, surface) =>
921 call("set_package_access", {
922 actor,
923 workspace,
924 ecosystem,
925 name,
926 user: "user" in who ? who.user : null,
927 team: "team" in who ? who.team : null,
928 role,
929 surface: surface ?? null,
930 }),
931 removeAccess: (actor, workspace, ecosystem, name, who, surface) =>
932 call("remove_package_access", {
933 actor,
934 workspace,
935 ecosystem,
936 name,
937 user: "user" in who ? who.user : null,
938 team: "team" in who ? who.team : null,
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member939 surface: surface ?? null,
940 }),
Merge packages: roles, Actions access, source label, soft delete, API941 setActionsAccess: (actor, workspace, ecosystem, name, repo, role, surface) =>
942 call("set_actions_access", { actor, workspace, ecosystem, name, repo, role, surface: surface ?? null }),
943 removeActionsAccess: (actor, workspace, ecosystem, name, repo, surface) =>
944 call("remove_actions_access", { actor, workspace, ecosystem, name, repo, surface: surface ?? null }),
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member945 storage: (workspace) => call("storage", { workspace }),
946 storageAll: () => call("storage_all", {}),
Composer from the workspace's own repositories, and go get from g1t.sh947 syncComposer: (repoId) => call("sync_composer", { repo_id: repoId }),
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member948 };
949}
950
Projects: what a workspace builds and runs, first on every page951export function projectsClient(service: ServiceBinding): ProjectsApi {
952 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
953 return {
954 list: (workspace, viewer) => call("list", { workspace, viewer }),
955 get: (workspace, slug, viewer) => call("get", { workspace, slug, viewer }),
956 byRepo: (repoId) => call("by_repo", { repoId }),
957 create: (actor, workspace, input) => call("create", { actor, workspace, input }),
958 update: (actor, workspace, slug, changes) => call("update", { actor, workspace, slug, changes }),
A project is an app or a library: libraries show their package and how to ship a release, not production959 deploymentsChanged: (projectId, enabled) => call("deployments_changed", { projectId, enabled }),
Projects: pinned and recent projects, per person per workspace960 shortcuts: (workspace, viewer) => call("shortcuts", { workspace, viewer }),
961 pin: (actor, workspace, slug, position) => call("pin", { actor, workspace, slug, position: position ?? null }),
962 unpin: (actor, workspace, slug) => call("unpin", { actor, workspace, slug }),
963 reorderPins: (actor, workspace, slugs) => call("reorder_pins", { actor, workspace, slugs }),
964 visited: (actor, projectId) => call("visited", { actor, projectId }),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97965 publicLinks: (repos) => call("public_links", { repos }),
Projects: what a workspace builds and runs, first on every page966 };
967}

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