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

Docs: usage is shown at cost while g1t is free1import 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";
Docs: usage is shown at cost while g1t is free5import type { EventsApi } from "./events";
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace6import type { IdentityAdminApi, IdentityApi } from "./identity";
Docs: usage is shown at cost while g1t is free7import 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";
Docs: usage is shown at cost while g1t is free9import 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";
Docs: usage is shown at cost while g1t is free14
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 }),
Docs: usage is shown at cost while g1t is free55 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 }),
Docs: usage is shown at cost while g1t is free59 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 }),
Docs: usage is shown at cost while g1t is free75 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 }),
Docs: usage is shown at cost while g1t is free79 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" }),
Docs: usage is shown at cost while g1t is free90 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 }),
Docs: usage is shown at cost while g1t is free100 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" }),
Docs: usage is shown at cost while g1t is free146 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 }),
Docs: usage is shown at cost while g1t is free170 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 }),
Docs: usage is shown at cost while g1t is free183 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 }),
Docs: usage is shown at cost while g1t is free196 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 }),
Docs: usage is shown at cost while g1t is free253 };
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
Docs: usage is shown at cost while g1t is free362export 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 }),
Docs: usage is shown at cost while g1t is free369 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 }),
Docs: usage is shown at cost while g1t is free386 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 }),
Docs: usage is shown at cost while g1t is free417 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 }),
Docs: usage is shown at cost while g1t is free422 };
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
Docs: usage is shown at cost while g1t is free455export 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 }),
Docs: usage is shown at cost while g1t is free460 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 }),
Docs: usage is shown at cost while g1t is free477 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 }),
Docs: usage is shown at cost while g1t is free486 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 }),
Docs: usage is shown at cost while g1t is free493 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 }),
Agents asked while not at work are woken to answer502 wakeForMessages: (pullId) => call("wake_for_messages", { pullId }),
Docs: usage is shown at cost while g1t is free503 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 }),
Docs: usage is shown at cost while g1t is free519 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 ),
Docs: usage is shown at cost while g1t is free529 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 }),
Docs: usage is shown at cost while g1t is free532 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 }),
Docs: usage is shown at cost while g1t is free537 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 }),
Docs: usage is shown at cost while g1t is free542 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 }),
Docs: usage is shown at cost while g1t is free577 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 }),
Docs: usage is shown at cost while g1t is free590 confirm: (workspace, viewer, session) => call("confirm", { workspace, viewer, session }),
591 canStart: (workspace) => call("can_start", { workspace }),
A free allowance on g1t's models, so anyone can try its agents592 trial: (workspace, exempt) => call("trial", { workspace, exempt }),
Docs: usage is shown at cost while g1t is free593 startRun: (run) => call("start_run", run),
Paid features: a workspace turns on Deployments with a monthly plan594 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 }),
Docs: usage is shown at cost while g1t is free632 };
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
Docs: usage is shown at cost while g1t is free755export 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),
760 };
761}
762
763export function integrationsClient(service: ServiceBinding): IntegrationsApi {
764 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
765 return {
766 list: (workspace, viewer) => call("list", { workspace, viewer }),
767 connect: (actor, workspace, input) => call("connect", { actor, workspace, ...input }),
768 update: (actor, workspace, id, input) => call("update", { actor, workspace, id, ...input }),
769 disconnect: (actor, workspace, id) => call("disconnect", { actor, workspace, id }),
770 test: (actor, workspace, id) => call("test", { actor, workspace, id }),
771 deliveries: (workspace, viewer, id) => call("deliveries", { workspace, viewer, id }),
772 resolve: (workspace, viewer, reference) => call("resolve", { workspace, viewer, reference }),
773 references: (workspace, text, limit) => call("references", { workspace, text, limit }),
774 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.775 comment: (actor, workspace, reference, text, link) => call("comment", { actor, workspace, reference, text, link }),
776 close: (actor, workspace, reference, text, link) => call("close", { actor, workspace, reference, text, link }),
Docs: usage is shown at cost while g1t is free777 links: (repo, number) => call("links", { repo, number }),
778 modelProvider: (workspace) => call("model_provider", { workspace }),
779 openModelSession: (run) => call("open_model_session", run),
780 modelUpstream: (token) => call("model_upstream", { token }),
Merge the AI Gateway: Anthropic's Messages API on a workspace's tokens781 gatewayUpstream: (workspace) => call("gateway_upstream", { workspace }),
AI Gateway: OpenAI's format, open models, and your own providers782 gatewayProviders: (workspace) => call("gateway_providers", { workspace }),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily783 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)784 capModelSessions: (tokenHashes, capMicros) => call("cap_model_sessions", { token_hashes: tokenHashes, cap_micros: capMicros }),
Docs: usage is shown at cost while g1t is free785 routes: (workspace, viewer) => call("routes", { workspace, viewer }),
786 setRoutes: (actor, workspace, routes) => call("set_routes", { actor, workspace, routes }),
787 };
788}
789
790export function webhooksClient(service: ServiceBinding): WebhooksApi {
791 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
792 return {
793 list: (viewer, owner) => call("list", { viewer, ...owner }),
794 create: (actor, owner, input) => call("create", { actor, ...owner, ...input }),
795 update: (actor, owner, id, input) => call("update", { actor, ...owner, id, ...input }),
796 delete: (actor, owner, id) => call("delete", { actor, ...owner, id }),
797 ping: (actor, owner, id) => call("ping", { actor, ...owner, id }),
798 deliveries: (viewer, owner, id) => call("deliveries", { viewer, ...owner, id }),
799 redeliver: (actor, owner, deliveryId) => call("redeliver", { actor, ...owner, deliveryId }),
800 };
801}
802
803export function actionsClient(service: ServiceBinding): ActionsApi {
804 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
805 return {
806 workflows: (repo, viewer) => call("workflows", { repo, viewer }),
807 runs: (repo, viewer, filter = {}) => call("runs", { repo, viewer, ...filter }),
Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009)808 run: (repo, viewer, id, attempt) => call("run", { repo, viewer, id, attempt: attempt ?? null }),
Docs: usage is shown at cost while g1t is free809 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)810 summaries: (repo, viewer, id, attempt) => call("summaries", { repo, viewer, id, attempt: attempt ?? null }),
811 jobLogText: (repo, viewer, job) => call("job_log_text", { repo, viewer, job }),
812 runLogs: (repo, viewer, id, attempt) => call("run_logs", { repo, viewer, id, attempt: attempt ?? null }),
Docs: usage is shown at cost while g1t is free813 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)814 cancel: (actor, repo, id, force = false) => call("cancel", { actor, repo, id, force }),
815 rerun: (actor, repo, id, failedOnly = false, options = {}) =>
816 call("rerun", { actor, repo, id, failed_only: failedOnly, job: options.job ?? null, debug: options.debug ?? false }),
Docs: usage is shown at cost while g1t is free817 setWorkflowEnabled: (actor, repo, workflow, enabled) =>
818 call("set_workflow_enabled", { actor, repo, workflow, enabled }),
819 settings: (actor, owner, kind) => call("settings", { actor, ...owner, kind }),
Secrets and variables: one list, rows per environment, for workflows and deployments820 setSetting: (actor, owner, kind, name, value, options = {}) =>
821 call("set_setting", { actor, ...owner, kind, name, value, ...options }),
822 deleteSetting: (actor, owner, kind, name, id) => call("delete_setting", { actor, ...owner, kind, name, id }),
Merge branch 'worktree-agent-a3abfcce648e87dca'823 approveRun: (actor, repo, id) => call("approve_run", { actor, repo, id }),
824 pendingDeployments: (repo, viewer, id) => call("pending_deployments", { repo, viewer, id }),
825 reviewDeployments: (actor, repo, id, state, environments = [], comment) =>
826 call("review_deployments", { actor, repo, id, state, environments, comment: comment ?? null }),
827 actionsSettings: (repo, viewer) => call("actions_settings", { repo, viewer }),
828 setActionsSettings: (actor, repo, change) => call("set_actions_settings", { actor, repo, ...change }),
829 workspaceActionsSettings: (workspace, viewer) => call("workspace_actions_settings", { workspace, viewer }),
830 setWorkspaceActionsSettings: (actor, workspace, change) =>
831 call("set_workspace_actions_settings", { actor, workspace, ...change }),
832 environments: (repo, viewer) => call("environments", { repo, viewer }),
833 setEnvironment: (actor, repo, name, change) => call("set_environment", { actor, repo, name, ...change }),
834 deleteEnvironment: (actor, repo, name) => call("delete_environment", { actor, repo, name }),
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R2835 artifacts: (repo, viewer, filter = {}) => call("artifacts", { repo, viewer, ...filter }),
836 artifactDownload: (repo, viewer, by) => call("artifact_download", { repo, viewer, ...by }),
837 deleteArtifact: (actor, repo, id) => call("delete_artifact", { actor, repo, id }),
838 artifactRetention: (repo, viewer, days) => call("artifact_retention", { repo, viewer, days }),
Docs: usage is shown at cost while g1t is free839 };
840}
841
Deployments: a preview for every pull request, production on g1t.page842
Fast pages, required checks on the branch, self-hosted runners, honest incidents843/** Self-hosted runners, kept by the actions service. */
844export function runnersClient(service: ServiceBinding): RunnersApi {
845 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
846 return {
847 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.848 activity: (actor, workspace) => call("runner_activity", { actor, workspace }),
Fast pages, required checks on the branch, self-hosted runners, honest incidents849 list: (actor, owner) => call("runners", { actor, ...owner }),
850 createToken: (actor, owner, group) => call("create_registration_token", { actor, ...owner, group }),
851 remove: (actor, owner, id) => call("remove_runner", { actor, ...owner, id }),
852 groups: (actor, workspace) => call("runner_groups", { actor, workspace }),
853 setGroup: (actor, workspace, group) => call("set_runner_group", { actor, workspace, ...group }),
854 deleteGroup: (actor, workspace, id) => call("delete_runner_group", { actor, workspace, id }),
855 settings: (actor, owner) => call("runner_settings", { actor, ...owner }),
856 setSettings: (actor, owner, change) => call("set_runner_settings", { actor, ...owner, ...change }),
857 };
858}
859
Deployments: a preview for every pull request, production on g1t.page860export function deploymentsClient(service: ServiceBinding): DeploymentsApi {
861 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
862 return {
Projects: what a workspace builds and runs, first on every page863 settings: (project, viewer) => call("settings", { project, viewer }),
864 updateSettings: (actor, project, changes) => call("update_settings", { actor, project, changes }),
865 list: (project, viewer) => call("list", { project, viewer }),
866 get: (project, id, viewer) => call("get", { project, id, viewer }),
867 redeploy: (actor, project, branch) => call("redeploy", { actor, project, branch }),
868 takeDown: (actor, project, branch) => call("take_down", { actor, project, branch }),
869 overview: (workspace, viewer) => call("overview", { workspace, viewer }),
Deployments: a preview for every pull request, production on g1t.page870 usage: (workspace, viewer) => call("usage", { workspace, viewer }),
Agents and memory, checks and conflicts, profiles, slug renames, custom domains871 domains: (project, viewer) => call("domains", { project, viewer }),
872 addDomain: (actor, project, hostname, options = {}) =>
873 call("add_domain", { actor, project, hostname, twin: !!options.twin }),
874 removeDomain: (actor, project, id) => call("remove_domain", { actor, project, id }),
875 refreshDomain: (actor, project, id) => call("refresh_domain", { actor, project, id }),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97876 repoDeployments: (repo, viewer, filter = {}) => call("list_deployments", { repo, viewer, ...filter }),
877 repoDeployment: (repo, id, viewer) => call("get_deployment", { repo, id, viewer }),
878 environments: (repo, viewer) => call("list_environments", { repo, viewer }),
879 createDeployment: (actor, repo, input) => call("create_deployment", { repo, actor, ...input }),
880 createDeploymentStatus: (actor, repo, id, input) => call("create_deployment_status", { repo, actor, id, ...input }),
Deployments: a preview for every pull request, production on g1t.page881 };
882}
Projects: what a workspace builds and runs, first on every page883
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member884/** Packages: the registries beside the code (services/packages). */
885export function packagesClient(service: ServiceBinding): PackagesApi {
886 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
887 return {
888 list: (workspace, viewer, filter = {}) =>
889 call("list_packages", {
890 workspace,
891 viewer,
892 ecosystem: filter.ecosystem ?? null,
893 repo_id: filter.repoId ?? null,
894 query: filter.query ?? null,
895 }),
896 get: (workspace, ecosystem, name, viewer) => call("get_package", { workspace, ecosystem, name, viewer }),
897 deleteVersion: (actor, workspace, ecosystem, name, version, surface) =>
898 call("delete_version", { actor, workspace, ecosystem, name, version, surface: surface ?? null }),
899 deletePackage: (actor, workspace, ecosystem, name, surface) =>
900 call("delete_package", { actor, workspace, ecosystem, name, surface: surface ?? null }),
901 set: (actor, workspace, ecosystem, name, change, surface) =>
902 call("set_package", {
903 actor,
904 workspace,
905 ecosystem,
906 name,
907 visibility: change.visibility ?? null,
908 link: change.link ?? null,
909 unlink: change.unlink ?? false,
Merge packages: roles, Actions access, source label, soft delete, API910 inherit_access: change.inheritAccess ?? null,
911 surface: surface ?? null,
912 }),
913 settings: (workspace, ecosystem, name, viewer) => call("package_settings", { workspace, ecosystem, name, viewer }),
914 deleted: (workspace, viewer) => call("deleted_packages", { workspace, viewer }),
915 restorePackage: (actor, workspace, ecosystem, name, surface) =>
916 call("restore_package", { actor, workspace, ecosystem, name, surface: surface ?? null }),
917 restoreVersion: (actor, workspace, ecosystem, name, version, surface) =>
918 call("restore_version", { actor, workspace, ecosystem, name, version, surface: surface ?? null }),
919 setAccess: (actor, workspace, ecosystem, name, who, role, surface) =>
920 call("set_package_access", {
921 actor,
922 workspace,
923 ecosystem,
924 name,
925 user: "user" in who ? who.user : null,
926 team: "team" in who ? who.team : null,
927 role,
928 surface: surface ?? null,
929 }),
930 removeAccess: (actor, workspace, ecosystem, name, who, surface) =>
931 call("remove_package_access", {
932 actor,
933 workspace,
934 ecosystem,
935 name,
936 user: "user" in who ? who.user : null,
937 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 member938 surface: surface ?? null,
939 }),
Merge packages: roles, Actions access, source label, soft delete, API940 setActionsAccess: (actor, workspace, ecosystem, name, repo, role, surface) =>
941 call("set_actions_access", { actor, workspace, ecosystem, name, repo, role, surface: surface ?? null }),
942 removeActionsAccess: (actor, workspace, ecosystem, name, repo, surface) =>
943 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 member944 storage: (workspace) => call("storage", { workspace }),
945 storageAll: () => call("storage_all", {}),
Composer from the workspace's own repositories, and go get from g1t.sh946 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 member947 };
948}
949
Projects: what a workspace builds and runs, first on every page950export function projectsClient(service: ServiceBinding): ProjectsApi {
951 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
952 return {
953 list: (workspace, viewer) => call("list", { workspace, viewer }),
954 get: (workspace, slug, viewer) => call("get", { workspace, slug, viewer }),
955 byRepo: (repoId) => call("by_repo", { repoId }),
956 create: (actor, workspace, input) => call("create", { actor, workspace, input }),
957 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 production958 deploymentsChanged: (projectId, enabled) => call("deployments_changed", { projectId, enabled }),
Projects: pinned and recent projects, per person per workspace959 shortcuts: (workspace, viewer) => call("shortcuts", { workspace, viewer }),
960 pin: (actor, workspace, slug, position) => call("pin", { actor, workspace, slug, position: position ?? null }),
961 unpin: (actor, workspace, slug) => call("unpin", { actor, workspace, slug }),
962 reorderPins: (actor, workspace, slugs) => call("reorder_pins", { actor, workspace, slugs }),
963 visited: (actor, projectId) => call("visited", { actor, projectId }),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97964 publicLinks: (repos) => call("public_links", { repos }),
Projects: what a workspace builds and runs, first on every page965 };
966}

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