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

Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1import type { ActionsApi } from "./actions";
2import type { BillingAdminApi, BillingApi } from "./billing";
3import type { DeploymentsApi } from "./deployments";
4import type { ProjectsApi } from "./projects";
5import type { EventsApi } from "./events";
6import type { IdentityAdminApi, IdentityApi } from "./identity";
7import type { IntegrationsApi } from "./integrations";
8import type { PackagesApi } from "./packages";
9import 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";
Merge main into rm-agents: mirroring removal, open signups, free-tier limits, the g1t CLI and runner fixes, with the agent removal kept. The CLI's table of operations is regenerated from the API without the agent, customize and AI credit operations; no CLI command used them.13import type { RunnersAdminApi, RunnersApi } from "./runners";
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running14
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 }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running53 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 }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running55 signOut: (sessionToken) => call("sign_out", { sessionToken }),
56 resendVerification: (user) => call("resend_verification", { user }),
57 verifyEmail: (token) => call("verify_email", { token }),
58 requestPasswordReset: (email, client) => call("request_password_reset", { email, client: client ?? null }),
59 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 }),
73 updateOAuthGrant: (user, id, grant) =>
74 call("update_oauth_grant", { user, id, scopes: grant.scopes }),
75 createWorkspace: (user, slug, name) => call("create_workspace", { user, slug, name }),
76 getWorkspace: (slug) => call("get_workspace", { slug }),
77 workspaceResidency: (slug) => call("workspace_residency", { slug }),
78 setWorkspaceResidency: (actor, slug, residency) => call("set_workspace_residency", { actor, slug, residency }),
79 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" }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running90 updateWorkspace: (actor, slug, details) =>
91 call("update_workspace", { actor, slug, ...details }),
92 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 }),
96 deleteWorkspace: (actor, slug, confirm) => call("delete_workspace", { actor, slug, confirm }),
97 checkWorkspaceDeletion: (actor, slug) => call("check_workspace_deletion", { actor, slug }),
98 setWorkspaceAvatar: (actor, slug, image) => call("set_workspace_avatar", { actor, slug, image }),
99 setUserAvatar: (user, image) => call("set_user_avatar", { user, image }),
100 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 }),
An access token can be regenerated: a new secret, its name, permissions, repositories and reach kept, and the old secret refused at once. Its page (yours under Settings, a workspace's under its settings for owners) has Regenerate with a new expiry, within the workspace's rules on lifetime, and shows the secret once; identity's regenerate_token logs it to the security log or the workspace's audit log, and refuses a revoked token. The token form's quick permissions have All, every resource at its highest level. A project's Observability settings no longer list the token g1t gives its apps on g1t.page: it is g1t's, and the page says apps there need none.128 regenerateToken: (actor, id, ttlSeconds, owner) =>
129 call("regenerate_token", { actor, id, owner: owner ?? null, ttl_seconds: ttlSeconds ?? null }),
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules130 getTokenPolicy: (slug, viewer) => call("get_token_policy", { slug, viewer }),
131 setTokenPolicy: (actor, slug, change) =>
132 call("set_token_policy", {
133 actor,
134 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 managers135 allow_tokens_for_all_workspaces: change.allowTokensForAllWorkspaces ?? null,
136 allow_tokens_for_this_workspace: change.allowTokensForThisWorkspace ?? null,
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules137 require_approval: change.requireApproval ?? null,
138 max_lifetime_days: change.maxLifetimeDays ?? null,
139 forbid_no_expiry: change.forbidNoExpiry ?? null,
140 surface: "web",
141 }),
142 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 managers143 call("list_member_tokens", { actor, slug, status: filter.status ?? null }),
Settings: fine-grained tokens, workspace token Admin, workspace personal access token rules144 reviewTokenRequest: (actor, slug, id, approve, reason) =>
145 call("review_token_request", { actor, slug, id, approve, reason: reason ?? null, surface: "web" }),
146 revokeMemberToken: (actor, slug, id, reason) =>
147 call("revoke_member_token", { actor, slug, id, reason: reason ?? null, surface: "web" }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running148 userForSession: (sessionToken) => call("user_for_session", { sessionToken }),
Anyone on g1t can invite a friend again: Invite a friend, in the account menu with how many are left beside it, opens Settings → Invites to g1t with a dialog for their email address and a note. The friend gets an email from them whose link opens sign-up with the address filled in and, because it came from that email, confirmed; their account starts with a workspace of its own, and the page lists every invite sent with where it stands (Pending, confirming their email, Joined as @them), with Copy link, Send again and Revoke on each. It is word of mouth, never a gate: sign-ups stay open to everyone, nothing asks for an invite, and running out says friends can still sign up without one.149 listInvites: (user) => call("list_invites", { user }),
150 createInvite: (user, invite) =>
151 call("create_invite", {
152 user,
153 email: invite.email,
154 message: invite.message ?? null,
155 join: invite.join ?? null,
156 join_role: invite.join ? (invite.joinRole ?? null) : null,
157 surface: "web",
158 }),
159 revokeInvite: (user, id) => call("revoke_invite", { user, id }),
160 resendInvite: (user, id) => call("resend_invite", { user, id }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running161 checkInvite: (code, client, options = {}) =>
162 call("check_invite", {
163 code,
164 client: client ?? null,
165 viewer: options.viewer ?? null,
166 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 confirm167 email_proof: options.emailProof ?? null,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running168 }),
169 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)170 inviteMember: (actor, slug, who) =>
Workspace is the workspace's settings, in one place. Its sidebar is grouped, General, Access, Money, Compute, Code, Agents, Chat, Artifacts, Security and Integrations, every page one click away with no settings inside settings, the groups folding and the owner-only pages hidden from members; what is not here yet is marked Soon with a hint. Members is a list with search and filters for role, two-factor and team, and Invite opens a dialog: who, by username, name or email, their role and a note that goes into the invitation; nothing is filled in inline any more. Invitations is its own page with All, Pending, Accepted, Declined, Expired and Revoked filters that say how many, and each row's menu can copy the link, send it again or revoke it; identity learned to send an invitation again and to carry the note. Permissions gathers every rule about who may do what, by part: Code's base permission and member privileges, who creates teams, who creates channels, and, marked Soon with what applies today, forking private repositories, adding agents to conversations, messaging agents directly, creating spaces and default sharing, creating workspace agents and raising budgets, deploying to production and publishing packages. General lost what moved. The permissions guide is new, and the workspaces, people and teams, access, chat, teams, authentication and billing guides say where things are now.171 call("invite_member", { actor, slug, email: who.email ?? "", username: who.username ?? null, role: who.role ?? null, message: who.message ?? null }),
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)172 listInvitations: (user) => call("list_invitations", { user }),
173 acceptInvitation: (user, id) => call("accept_invitation", { user, id, surface: "web" }),
174 declineInvitation: (user, id) => call("decline_invitation", { user, id, surface: "web" }),
175 findPeople: (query, limit) => call("find_people", { query, limit: limit ?? null }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running176 workspaceInvites: (slug, viewer) => call("workspace_invites", { slug, viewer }),
177 revokeWorkspaceInvite: (actor, slug, id) => call("revoke_workspace_invite", { actor, slug, id }),
Workspace is the workspace's settings, in one place. Its sidebar is grouped, General, Access, Money, Compute, Code, Agents, Chat, Artifacts, Security and Integrations, every page one click away with no settings inside settings, the groups folding and the owner-only pages hidden from members; what is not here yet is marked Soon with a hint. Members is a list with search and filters for role, two-factor and team, and Invite opens a dialog: who, by username, name or email, their role and a note that goes into the invitation; nothing is filled in inline any more. Invitations is its own page with All, Pending, Accepted, Declined, Expired and Revoked filters that say how many, and each row's menu can copy the link, send it again or revoke it; identity learned to send an invitation again and to carry the note. Permissions gathers every rule about who may do what, by part: Code's base permission and member privileges, who creates teams, who creates channels, and, marked Soon with what applies today, forking private repositories, adding agents to conversations, messaging agents directly, creating spaces and default sharing, creating workspace agents and raising budgets, deploying to production and publishing packages. General lost what moved. The permissions guide is new, and the workspaces, people and teams, access, chat, teams, authentication and billing guides say where things are now.178 resendWorkspaceInvite: (actor, slug, id) => call("resend_workspace_invite", { actor, slug, id }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running179 userForGitCredentials: (username, secret) =>
180 call("user_for_git_credentials", { username, secret }),
181 userForAccessToken: (token) => call("user_for_access_token", { token }),
182 userForSshKey: (fingerprint) => call("user_for_ssh_key", { fingerprint }),
183 userByUsername: (username) => call("user_by_username", { username }),
184 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)185 usersForAudience: (ids) => call("users_for_audience", { ids }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running186 profile: (username) => call("profile", { username }),
187 updateProfile: (actor, fields) => call("update_profile", { actor, ...fields }),
188 profileWorkspaces: (username, viewer, publicIn) =>
189 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.190 dockPins: (user, workspace) => call("dock_pins", { user, workspace }),
191 setDockPins: (user, workspace, apps) => call("set_dock_pins", { user, workspace, apps }),
Chat has sounds. A short warm cue plays for a message in a conversation you have open but aren't looking at, a direct message, a mention of you and an agent finishing something for you, with a soft tick for sending that is off until you turn it on; never for your own messages, a muted conversation, a conversation you are looking at in a window that has the front, or while you have paused notifications, and never twice for one message or more than once in a second and a half. The cues are made by the browser itself in two sets, Soft and Bright, so nothing is downloaded; the Sounds and notifications section of your notification settings turns them on and off, picks the set and the volume, plays each one, and offers desktop notifications for when the window is behind; Do not disturb is the one pause everyone sees, set from the chat sidebar's bell, the settings, or your avatar menu, now with a two-hour choice, and the top bar's bell shows a dot while it's on. Your choices are kept with your account. The sound layer sits behind one small interface so the desktop app can play them natively later. The chat and notifications guides say the rules.192 soundSettings: (user) => call("sound_settings", { user }),
193 setSoundSettings: (user, change) => call("set_sound_settings", { user, change }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running194 listSshKeys: (user) => call("list_ssh_keys", { user }),
195 addSshKey: (user, title, publicKey) =>
196 call("add_ssh_key", { user, title, publicKey }),
197 removeSshKey: (user, id) => call("remove_ssh_key", { user, id }),
198 listAccessTokens: (user) => call("list_access_tokens", { user }),
199 createAccessToken: (user, name, ttlSeconds, grant) =>
200 call("create_access_token", {
201 user,
202 name,
203 ttlSeconds,
204 scopes: grant?.scopes ?? null,
205 listed: grant?.listed ?? false,
206 }),
207 removeAccessToken: (user, id) => call("remove_access_token", { user, id }),
208 createRunCredential: (input) => call("create_run_credential", input),
209 bindRunCredentials: (tokenHashes, runId) => call("bind_run_credentials", { tokenHashes, runId }),
210 revokeRunCredentials: (target) => call("revoke_run_credentials", target),
211 // Who has access to a repository; see access.ts.
212 repoAccess: (owner, name, viewer) => call("repo_access", { viewer, path: { namespace: owner, name } }),
213 addCollaborator: (actor, owner, name, invitee, role) =>
214 call("add_collaborator", { actor, path: { namespace: owner, name }, invitee, role }),
215 setCollaboratorRole: (actor, owner, name, username, role) =>
216 call("set_collaborator_role", { actor, path: { namespace: owner, name }, username, role }),
217 removeCollaborator: (actor, owner, name, username) =>
218 call("remove_collaborator", { actor, path: { namespace: owner, name }, username }),
219 collaboratorPermission: (viewer, owner, name, username) =>
220 call("collaborator_permission", { viewer, path: { namespace: owner, name }, username }),
221 myRepoInvitations: (user) => call("my_repo_invitations", { user }),
222 respondRepoInvitation: (user, id, accept) => call("respond_repo_invitation", { user, id, accept }),
223 revokeRepoInvitation: (actor, owner, name, id) =>
224 call("revoke_repo_invitation", { actor, path: { namespace: owner, name }, id }),
225 setBasePermission: (actor, slug, base) => call("set_base_permission", { actor, slug, base_permission: base }),
226 outsideCollaborators: (viewer, slug) => call("outside_collaborators", { viewer, slug }),
Merge branch 'worktree-agent-a5a5de74d8863d554' into worktree-agent-a16631325aecf58ca227 // A repository's deploy keys; see deploy-keys.ts.
228 listDeployKeys: (viewer, owner, name) => call("list_deploy_keys", { viewer, path: { namespace: owner, name } }),
229 addDeployKey: (actor, owner, name, key) =>
230 call("add_deploy_key", { actor, path: { namespace: owner, name }, title: key.title, key: key.key, read_only: key.readOnly }),
231 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 bar232 // Teams; see teams.ts.
233 listTeams: (viewer, workspace, query) => call("list_teams", { viewer, workspace, query: query ?? null }),
234 getTeam: (viewer, workspace, team) => call("get_team", { viewer, workspace, team }),
235 createTeam: (actor, workspace, team) => call("create_team", { actor, workspace, ...team }),
Merge branch 'worktree-agent-ad7c6d88d93adc817'236 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 bar237 updateTeam: (actor, workspace, team, changes) => call("update_team", { actor, workspace, team, ...changes }),
238 deleteTeam: (actor, workspace, team) => call("delete_team", { actor, workspace, team }),
239 teamMembers: (viewer, workspace, team, includeChildTeams) =>
240 call("team_members", { viewer, workspace, team, include_child_teams: includeChildTeams ?? false }),
241 setTeamMember: (actor, workspace, team, username, role) =>
242 call("set_team_member", { actor, workspace, team, username, role }),
243 removeTeamMember: (actor, workspace, team, username) =>
244 call("remove_team_member", { actor, workspace, team, username }),
245 childTeams: (viewer, workspace, team) => call("child_teams", { viewer, workspace, team }),
246 teamRepos: (viewer, workspace, team) => call("team_repos", { viewer, workspace, team }),
247 setTeamRepo: (actor, workspace, team, owner, name, role) =>
248 call("set_team_repo", { actor, workspace, team, repo: { namespace: owner, name }, role }),
249 removeTeamRepo: (actor, workspace, team, owner, name) =>
250 call("remove_team_repo", { actor, workspace, team, repo: { namespace: owner, name } }),
251 userTeams: (viewer, workspace, username) => call("user_teams", { viewer, workspace, username }),
252 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.253 // People; see people.ts.
254 peopleDirectory: (viewer, workspace) => call("people_directory", { viewer, workspace }),
255 setMemberProfile: (actor, workspace, username, changes) => call("set_member_profile", { actor, workspace, username, ...changes }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running256 };
257}
258
259/**
260 * The slug a renamed workspace has now, for a `workspace.renamed` handler:
261 * asked of identity by the workspace's id, so that renames delivered twice
262 * or out of order converge. Falls back to the event's `to`.
263 */
264export async function currentWorkspaceSlug(
265 identity: ServiceBinding,
266 renamed: { workspaceId: string; to: string },
267): Promise<string> {
268 const names = await identityClient(identity).usernames([renamed.workspaceId]);
269 return names[renamed.workspaceId] ?? renamed.to;
270}
271
272/** The slugs whose rows move to `current`: the two a rename names, less `current`. */
273export function staleSlugs(renamed: { from: string; to: string }, current: string): string[] {
274 return [...new Set([renamed.from, renamed.to])].filter((slug) => slug !== current);
275}
276
277/**
278 * Where a transferred repository is now, as `namespace/name`, for a
279 * `repo.transferred` handler: asked of repos by id, so transfers delivered
280 * twice or out of order converge. Falls back to the event's destination.
281 */
282export async function currentRepoPath(
283 repos: ServiceBinding,
284 transferred: { repoId: string; name: string; to: string },
285): Promise<string> {
286 const path = await rpc<{ namespace: string; name: string } | null>(repos, "path_by_id", { id: transferred.repoId });
287 return path ? `${path.namespace}/${path.name}` : `${transferred.to}/${transferred.name}`;
288}
289
290/** The paths whose rows move to `current`: the two a transfer names, less `current`. */
291export function stalePaths(transferred: { name: string; from: string; to: string }, current: string): string[] {
292 return [...new Set([transferred.from, transferred.to].map((ns) => `${ns}/${transferred.name}`))].filter(
293 (path) => path !== current,
294 );
295}
296
297/**
298 * A repository's path change, from `repo.transferred` or `repo.renamed`,
299 * read the same way: the two paths (`namespace/name`) the event names, old
300 * then new. Null for any other event.
301 */
302export type RepoMove = { repoId: string; paths: [string, string] };
303
304export function repoMove(event: { type: string; data: unknown }): RepoMove | null {
305 const data = event.data as Record<string, string>;
306 if (event.type === "repo.transferred") {
307 return { repoId: data.repoId!, paths: [`${data.from}/${data.name}`, `${data.to}/${data.name}`] };
308 }
309 if (event.type === "repo.renamed") {
310 return { repoId: data.repoId!, paths: [`${data.namespace}/${data.from}`, `${data.namespace}/${data.to}`] };
311 }
312 return null;
313}
314
315/**
316 * Where a moved repository is now, as `namespace/name`: asked of repos by
317 * id, so moves delivered twice or out of order converge. Falls back to the
318 * event's new path.
319 */
320export async function currentMovedPath(repos: ServiceBinding, move: RepoMove): Promise<string> {
321 const path = await rpc<{ namespace: string; name: string } | null>(repos, "path_by_id", { id: move.repoId });
322 return path ? `${path.namespace}/${path.name}` : move.paths[1];
323}
324
325/** The paths whose rows move to `current`: the two a move names, less `current`. */
326export function staleMovedPaths(move: RepoMove, current: string): string[] {
327 return [...new Set(move.paths)].filter((path) => path !== current);
328}
329
330/** Staff-only identity. Only sudo binds to it; see `IdentityAdminApi`. */
331export function identityAdminClient(service: ServiceBinding): IdentityAdminApi {
332 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
333 return {
334 workspaces: (query) => call("admin_workspaces", { query: query ?? null }),
335 workspace: (slug) => call("admin_workspace", { slug }),
Anyone on g1t can invite a friend again: Invite a friend, in the account menu with how many are left beside it, opens Settings → Invites to g1t with a dialog for their email address and a note. The friend gets an email from them whose link opens sign-up with the address filled in and, because it came from that email, confirmed; their account starts with a workspace of its own, and the page lists every invite sent with where it stands (Pending, confirming their email, Joined as @them), with Copy link, Send again and Revoke on each. It is word of mouth, never a gate: sign-ups stay open to everyone, nothing asks for an invite, and running out says friends can still sign up without one.336 invites: (query, options = {}) => call("admin_invites", { query: query ?? null, to_g1t: options.toG1t ?? false }),
337 grantInvites: (username, amount, note, staff) => call("admin_grant_invites", { username, amount, note, staff }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running338 revokeInvite: (id, staff) => call("admin_revoke_invite", { id, staff }),
Merge main into rm-agents: mirroring removal, open signups, free-tier limits, the g1t CLI and runner fixes, with the agent removal kept. The CLI's table of operations is regenerated from the API without the agent, customize and AI credit operations; no CLI command used them.339 suspendUser: (name, suspend, reason, staff) => call("admin_suspend_user", { name, suspend, reason, staff }),
340 suspendWorkspace: (name, suspend, reason, staff) => call("admin_suspend_workspace", { name, suspend, reason, staff }),
341 blockedDomains: () => call("admin_blocked_domains", {}),
342 blockDomain: (domain, block, reason, staff) => call("admin_block_domain", { domain, block, reason, staff }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running343 deletedWorkspaces: () => call("admin_deleted_workspaces", {}),
344 restoreWorkspace: (workspaceId, staff) => call("admin_restore_workspace", { workspaceId, staff }),
345 purgeWorkspace: (workspaceId, staff, confirm) => call("admin_purge_workspace", { workspaceId, staff, confirm }),
Merge branch 'worktree-agent-a8385d293d42c913a'346 aliases: () => call("admin_aliases", {}),
347 setAlias: (alias, workspace, note, staff) => call("admin_set_alias", { alias, workspace, note, staff }),
348 removeAlias: (alias, reason, staff) => call("admin_remove_alias", { alias, reason, staff }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running349 };
350}
351
352export function reposClient(service: ServiceBinding): ReposApi {
353 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
354 return {
355 get: (path, viewer) => call("get", { path, viewer }),
356 getById: (id, viewer) => call("get_by_id", { id, viewer }),
357 readable: (ids, viewer) => call("readable", { ids, viewer }),
358 publicNamespaces: (ownerId) => call("public_namespaces", { ownerId }),
359 list: (viewer, options = {}) => call("list", { viewer, ...options }),
360 create: (owner, input) => call("create", { owner, ...input }),
361 update: (actor, path, changes) => call("update", { actor, path, ...changes }),
362 transfer: (actor, path, to) => call("transfer", { actor, path, to }),
363 delete: (actor, path, confirm) => call("delete", { actor, path, confirm }),
364 deleted: (viewer, namespace) => call("deleted", { viewer, namespace }),
365 restore: (actor, path) => call("restore", { actor, path }),
366 purge: (actor, path, confirm) => call("purge", { actor, path, confirm }),
367 rename: (actor, path, name) => call("rename", { actor, path, name }),
368 archive: (actor, path, archived) => call("archive", { actor, path, archived }),
369 setVisibility: (actor, path, isPrivate, confirm) => call("set_visibility", { actor, path, isPrivate, confirm }),
370 setDefaultBranch: (actor, path, branch) => call("set_default_branch", { actor, path, branch }),
371 renameBranch: (actor, path, from, to) => call("rename_branch", { actor, path, from, to }),
The Branches page does things to branches now. It has tabs (Overview, Yours, Active, Stale, All, kept in the address as ?tab=) and search; Overview shows the default branch, then the first five of your branches, the active ones and the stale ones, each linking to the rest. Each branch is a row with its name in a code chip with a copy button, who made its latest commit and when, that commit's checks, behind and ahead counts with bars drawn against the largest shown, and its pull request (the open one, else the newest merged or closed) with its state; on a phone the columns fold under the name. Yours is branches whose latest commit is the viewer's; stale is no commit in 90 days.372 createBranch: (actor, path, name, from = "") => call("create_branch", { actor, path, name, from }),
373 removeBranch: (actor, path, branch, head) => call("remove_branch", { actor, path, branch, head }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running374 resolveBranch: (repoId, branch) => call("resolve_branch", { repoId, branch }),
375 statusById: (id) => call("status_by_id", { id }),
376 storageOptions: () => call("storage_options", {}),
377 resolvePath: (path) => call("resolve_path", { path }),
378 tree: (path, viewer, ref, treePath) =>
379 call("tree", { path, viewer, ref, treePath }),
380 blob: (path, viewer, ref, filePath) =>
381 call("blob", { path, viewer, ref, filePath }),
382 log: (path, viewer, ref, limit) => call("log", { path, viewer, ref, limit }),
383 blame: (path, viewer, ref, filePath) => call("blame", { path, viewer, ref, filePath }),
384 forkForPull: (sourceId, pullId, actor) =>
385 call("fork_for_pull", { sourceId, pullId, actor }),
386 gitAccess: (path, viewer, service) =>
387 call("git_access", { path, viewer, service }),
388 branches: (path, viewer) => call("branches", { path, viewer }),
389 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 25390 branchDrift: (path, viewer, base, heads) => call("branch_drift", { path, viewer, base, heads }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running391 tags: (path, viewer) => call("tags", { path, viewer }),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97392 about: (path, viewer) => call("about", { path, viewer }),
393 languages: (path, viewer) => call("languages", { path, viewer }),
394 contributors: (path, viewer) => call("contributors", { path, viewer }),
395 license: (path, viewer) => call("license", { path, viewer }),
396 stars: (path, viewer) => call("stars", { path, viewer }),
397 star: (actor, path, starred) => call("star", { path, actor, starred }),
398 stargazers: (path, viewer, page) => call("stargazers", { path, viewer, page: page ?? null }),
399 starred: (username, viewer) => call("starred", { username, viewer }),
400 releases: (path, viewer) => call("releases", { path, viewer }),
401 release: (path, viewer, which) => call("release", { path, viewer, id: which.id ?? null, tag: which.tag ?? null, latest: which.latest ?? false }),
402 createRelease: (actor, path, release) => call("create_release", { path, actor, ...release }),
403 updateRelease: (actor, path, id, change) => call("update_release", { path, actor, id, ...change }),
404 deleteRelease: (actor, path, id) => call("delete_release", { path, actor, id }),
Merge GitHub pull request #8 (claude/cool-newton-7nm8r9): Docker deployments on g1t.page behind a switch that stays off until Cloudflare's container APIs are checked live, workflows a workspace manages in its repositories, tarball and zipball downloads, refs/pull/* kept to g1t alone, framework detection for g1t.page builds, and a workflow's event payload matching GitHub's.405 listFiles: (repoId, ref, limit, options) => call("list_files", { repoId, ref, skipDirs: [], limit, links: options?.links ?? false }),
406 openArchiveToken: (path, token) => call("open_archive_token", { path, token }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running407 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 address408 rawFile: (repoId, ref, path, maxBytes) => call("raw_file", { repoId, ref, path, maxBytes }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running409 commitFile: (repo, actor, file) => call("commit_file", { repo, actor, ...file }),
410 land: (sourceId, actor, branch) => call("land", { sourceId, actor, branch }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar411 compare: (repoId, viewer, base, head, baseBranch) => call("compare", { repoId, viewer, base, head, baseBranch }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running412 claimBackups: (limit, maxRunning) => call("claim_backups", { limit, maxRunning }),
413 // The sandbox's own calls are snake_case (they come through the API).
414 failBackup: (jobId, token, error) => call("backup_fail", { job_id: jobId, token, error }),
415 };
416}
417
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar418/**
419 * `PullDetail`'s own fields that the work service writes in snake_case:
420 * `g1t_contracts::work::PullDetail` has no camelCase renaming, though what
421 * it holds (`Pull`, `CheckRun` and the rest) does. Each with its name here.
422 */
423const PULL_DETAIL_FIELDS = [
424 ["earlier_checks", "earlierChecks"],
425 ["required_checks", "requiredChecks"],
426 ["code_owners", "codeOwners"],
427] as const;
428
429/**
430 * A pull request's detail as the work service sent it, with its own fields
431 * in the camelCase this package uses: read at the edge, by `workClient`'s
432 * `getPull`. Either spelling is taken, so a service that already sends
433 * camelCase reads the same.
434 */
435export function pullDetailFromWire(raw: Record<string, unknown>): PullDetail {
436 const detail: Record<string, unknown> = { ...raw };
437 for (const [snake, camel] of PULL_DETAIL_FIELDS) {
438 if (snake in detail) {
439 if (!(camel in detail)) detail[camel] = detail[snake];
440 delete detail[snake];
441 }
442 }
443 return detail as PullDetail;
444}
445
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running446export function workClient(service: ServiceBinding): WorkApi {
447 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
448 return {
449 openIssue: (actor, repo, input) => call("open_issue", { actor, repo, ...input }),
450 listIssues: (repo, viewer, filter = {}) => call("list_issues", { repo, viewer, ...filter }),
451 getIssue: (repo, number, viewer) => call("get_issue", { repo, number, viewer }),
452 updateIssue: (actor, repo, number, input) =>
453 call("update_issue", { actor, repo, number, ...input }),
454 closeIssue: (actor, repo, number, reason) =>
455 call("close_issue", { actor, repo, number, reason }),
456 reopenIssue: (actor, repo, number) => call("reopen_issue", { actor, repo, number }),
457 listLabels: (repo, viewer) => call("list_labels", { repo, viewer }),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar458 saveLabel: (actor, repo, label) => call("save_label", { actor, repo, ...label }),
459 deleteLabel: (actor, repo, name) => call("delete_label", { actor, repo, name }),
460 addDefaultLabels: (actor, repo) => call("add_default_labels", { actor, repo }),
461 setLabels: (actor, repo, number, labels, change = "set") =>
462 call("set_labels", { actor, repo, number, labels, change }),
463 listMilestones: (repo, viewer, state) => call("list_milestones", { repo, viewer, state }),
464 getMilestone: (repo, number, viewer) => call("get_milestone", { repo, number, viewer }),
465 saveMilestone: (actor, repo, milestone) => call("save_milestone", { actor, repo, ...milestone }),
466 deleteMilestone: (actor, repo, number) => call("delete_milestone", { actor, repo, number }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running467 counts: (repo, viewer) => call("counts", { repo, viewer }),
468 addComment: (actor, repo, number, comment) =>
469 call("add_comment", { actor, repo, number, ...comment }),
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts470 editComment: (actor, repo, commentId, body) => call("edit_comment", { actor, repo, commentId, body }),
471 deleteComment: (actor, repo, commentId) => call("delete_comment", { actor, repo, commentId }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running472 startChecks: (pullId) => call("start_checks", { pullId }),
473 reportChecks: (runId, token, report) =>
474 call("report_checks", { runId, token, ...report }),
475 startMergecheck: (pullId) => call("start_mergecheck", { pullId }),
476 failMergecheck: (pullId, token, error) => call("report_mergecheck", { pullId, token, error }),
477 advance: (pullId) => call("advance", { pullId }),
478 stall: (pullId, reason) => call("stall", { pullId, reason }),
479 managedPulls: (repoId) => call("managed_pulls", { repoId }),
480 queue: (repo, viewer) => call("queue", { repo, viewer }),
481 queueBuild: (repoId) => call("queue_build", { repoId }),
482 failQueue: (entryId, token, error) => call("report_queue", { entryId, token, error }),
483 removeFromQueue: (actor, repo, number) => call("remove_from_queue", { actor, repo, number }),
484 getSettings: (repo, viewer) => call("get_settings", { repo, viewer }),
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge485 // Rulesets travel in snake_case (rules.ts).
486 listRulesets: (owner, viewer, includeParents = false) =>
487 call("list_rulesets", { viewer, ...owner, include_parents: includeParents }),
488 getRuleset: (owner, id, viewer) => call("get_ruleset", { viewer, ...owner, id }),
489 saveRuleset: (actor, owner, ruleset, id) => call("save_ruleset", { actor, ...owner, id: id ?? null, ruleset }),
490 deleteRuleset: (actor, owner, id) => call("delete_ruleset", { actor, ...owner, id }),
491 effectiveRules: (repo, name, viewer, target = "branch") => call("effective_rules", { viewer, repo, name, target }),
492 ruleEvaluations: (owner, viewer, filter = {}) => call("rule_evaluations", { viewer, ...owner, ...filter }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running493 seenChecks: (repo, viewer) => call("seen_checks", { repo, viewer }),
Merge checks: statuses and check runs on every commit494 commitChecks: (repo, viewer, shas) => call("commit_checks", { repo, viewer, shas }),
495 getCheckRun: (repo, id, viewer) => call("get_check_run", { repo, id, viewer }),
496 checkRunAnnotations: (repo, id, viewer) => call("check_run_annotations", { repo, id, viewer }),
497 requestCheckAction: (actor, repo, id, identifier) => call("request_check_action", { actor, repo, id, identifier }),
498 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 bar499 codeownersErrors: (repo, viewer, ref) => call("codeowners_errors", { repo, viewer, ref: ref ?? null }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running500 updateSettings: (actor, repo, settings) =>
501 call("update_settings", { actor, repo, settings }),
502 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 bar503 listPulls: (repo, viewer, state, filter = {}) => call("list_pulls", { repo, viewer, state, ...filter }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running504 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 bar505 // Its own fields arrive in snake_case: read into camelCase here.
506 getPull: (repo, number, viewer) =>
507 call<Result<Record<string, unknown>>>("get_pull", { repo, number, viewer }).then(
508 (found): Result<PullDetail> => (found.ok ? { ok: true, value: pullDetailFromWire(found.value) } : found),
509 ),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running510 updatePull: (actor, repo, number, changes) =>
511 call("update_pull", { actor, repo, number, ...changes }),
512 catchUpPull: (actor, repo, number) => call("catch_up_pull", { actor, repo, number }),
513 readyPull: (actor, repo, number, summary) =>
514 call("ready_pull", { actor, repo, number, summary }),
515 closePull: (actor, repo, number) => call("close_pull", { actor, repo, number }),
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts516 reopenPull: (actor, repo, number) => call("reopen_pull", { actor, repo, number }),
517 convertPullToDraft: (actor, repo, number) => call("convert_pull_to_draft", { actor, repo, number }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running518 mergePull: (actor, repo, number, options = {}) =>
519 call("merge_pull", { actor, repo, number, ...options }),
520 listActivePulls: (viewer) => call("list_active_pulls", { viewer }),
521 byAuthor: (username, viewer, filter = {}) => call("by_author", { username, viewer, ...filter }),
Chat controls, public profiles, shadcn selects, and no Docs tab in a project522 contributions: (username, viewer) => call("contributions", { username, viewer }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running523 listAssignedIssues: (viewer) => call("list_assigned_issues", { viewer }),
524 appendSession: (actor, repo, number, entries) =>
525 call("append_session", { actor, repo, number, entries }),
526 readSession: (repo, number, viewer, afterSeq = 0) =>
527 call("read_session", { repo, number, viewer, afterSeq }),
Merge branch 'worktree-agent-aa68cf6ecc03dffaf'528 // Coordination: harness sessions, claims, an issue's whole context and
529 // decision records. Their arguments are snake_case, as the Rust service
530 // reads them; what comes back is camelCase.
531 getWork: (repo, number, viewer) => call("get_work", { repo, number, viewer }),
532 listClaims: (repo, viewer, filter = {}) =>
533 call("list_claims", { repo, viewer, state: filter.state ?? null, issue: filter.issue ?? null }),
534 listWorkSessions: (repo, viewer, filter = {}) =>
535 call("list_work_sessions", {
536 repo,
537 viewer,
538 state: filter.state ?? null,
539 issue: filter.issue ?? null,
540 pull: filter.pull ?? null,
g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other.541 path: filter.path ?? null,
542 subagents: filter.subagents ?? false,
Merge branch 'worktree-agent-aa68cf6ecc03dffaf'543 }),
544 getWorkSession: (repo, id, viewer, afterSeq = 0) =>
545 call("get_work_session", { repo, id, viewer, after_seq: afterSeq }),
546 listDecisions: (repo, number, viewer) => call("list_decisions", { repo, number, viewer }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running547 };
548}
549
550export function billingClient(service: ServiceBinding): BillingApi {
551 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
552 return {
553 status: () => call("status", {}),
554 account: (workspace, viewer) => call("account", { workspace, viewer }),
555 ledger: (workspace, viewer) => call("ledger", { workspace, viewer }),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging556 credits: (workspace, viewer) => call("credits", { workspace, viewer }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running557 statement: (workspace, viewer, month = null, group = "day") => call("statement", { workspace, viewer, month, group }),
558 statementEntries: (workspace, viewer, filter) =>
559 call("statement_entries", {
560 workspace,
561 viewer,
562 month: filter.month,
563 kind: filter.kind,
564 day: filter.day ?? null,
565 project: filter.project ?? null,
566 before: filter.before ?? null,
567 }),
568 usage: (workspace, viewer, since) => call("usage", { workspace, viewer, since }),
569 checkout: (actor, workspace, amountCents, returnUrl, method = "card") =>
570 call("checkout", { actor, workspace, amountCents, returnUrl, method }),
571 confirm: (workspace, viewer, session) => call("confirm", { workspace, viewer, session }),
572 trial: (workspace, exempt) => call("trial", { workspace, exempt }),
573 features: (workspace, viewer) => call("features", { workspace, viewer }),
574 subscribe: (actor, workspace, feature, returnUrl) =>
575 call("subscribe", { actor, workspace, feature, returnUrl }),
576 confirmSubscription: (workspace, viewer, session) =>
577 call("confirm_subscription", { workspace, viewer, session }),
578 cancelSubscription: (actor, workspace, feature, resume = false) =>
579 call("cancel_subscription", { actor, workspace, feature, resume }),
580 hasFeature: (workspace, feature) => call("has_feature", { workspace, feature }),
Merge Stripe Tax, the card fee on card payments, and one free workspace per person581 freeWorkspaces: (workspaces) => call("free_workspaces", { workspaces }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running582 chargeFeature: (charge) => call("charge_feature", charge),
583 billingPortal: (actor, workspace, returnUrl) => call("billing_portal", { actor, workspace, return_url: returnUrl }),
584 recordSandbox: (usage) => call("record_sandbox", usage),
585 limit: (workspace, viewer) => call("limit", { workspace, viewer }),
586 checkLimit: (workspace) => call("check_limit", { workspace }),
587 prices: () => call("prices", {}),
588 notePending: (workspace, source, costMicros, detail = null) => call("note_pending", { workspace, source, costMicros, detail }),
589 usageMeters: (workspace, viewer) => call("usage_meters", { workspace, viewer }),
590 setSpendLimit: (actor, workspace, spendLimitMicros, useFullLimit = false, raiseOnce = false) =>
591 call("set_spend_limit", { actor, workspace, spendLimitMicros, use_full_limit: useFullLimit, raise_once: raiseOnce }),
592 invoices: (workspace, viewer) => call("invoices", { workspace, viewer }),
593 entitlements: (workspace) => call("entitlements", { workspace }),
594 reserve: (reservation) => call("reserve", reservation),
595 settle: (reservationId, actualMicros) => call("settle", { reservationId, actualMicros }),
596 cardCheck: (actor, workspace, returnUrl) => call("card_check", { actor, workspace, returnUrl }),
597 confirmCardCheck: (workspace, viewer, session) => call("confirm_card_check", { workspace, viewer, session }),
598 requestLimit: (actor, workspace, request) => call("request_limit", { actor, workspace, ...request }),
599 limitRequests: (workspace, viewer) => call("limit_requests", { workspace, viewer }),
600 confirmSpike: (actor, workspace, keepGoing) => call("confirm_spike", { actor, workspace, keepGoing }),
601 setCaps: (actor, workspace, caps) => call("set_caps", { actor, workspace, ...caps }),
Usage, Billing settings and prepaid AI credit; fixes from the UX audit602 usageReport: (workspace, viewer, range) =>
603 call("usage_report", { workspace, viewer, from: range.from, until: range.until, products: range.products ?? [], projects: range.projects ?? [] }),
604 setBudget: (actor, workspace, budget) => call("set_budget", { actor, workspace, ...budget }),
605 billingDetails: (workspace, viewer) => call("billing_details", { workspace, viewer }),
606 setBillingDetails: (actor, workspace, details) => call("set_billing_details", { actor, workspace, ...details }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running607 };
608}
609
610export function billingAdminClient(service: ServiceBinding): BillingAdminApi {
611 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
612 return {
613 accounts: (query) => call("admin_accounts", { query: query ?? null }),
614 account: (id) => call("admin_account", { id }),
615 setTerms: (id, terms, by) => call("admin_set_terms", { id, terms, by }),
616 setAllowances: (id, allowances, note, by) => call("admin_set_allowances", { id, allowances, note, by }),
617 createEnterprise: (name, workspaces, by) => call("admin_create_enterprise", { name, workspaces, by }),
618 attach: (workspace, account, by) => call("admin_attach", { workspace, account, by }),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging619 credit: (workspace, amountMicros, note, by, options = { kind: "goodwill" }) =>
620 call("admin_credit", {
621 workspace,
622 amount_micros: amountMicros,
623 note,
624 by,
625 kind: options.kind,
626 expires_at: options.expiresAt ?? null,
627 refund_for: options.refundFor ?? null,
628 refund_day: options.refundDay ?? null,
629 }),
630 credits: (filter = {}) =>
631 call("admin_credits", {
632 workspace: filter.workspace ?? null,
633 kind: filter.kind ?? null,
634 month: filter.month ?? null,
635 by: filter.by ?? null,
636 }),
637 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's638 resetBilling: (workspace, confirm, note, by) => call("admin_reset_billing", { workspace, confirm, note, by }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running639 billingLink: (workspace, by) => call("admin_billing_link", { workspace, by }),
640 stripe: (fix = false, by) => call("admin_stripe", { fix, by: by ?? null }),
641 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 person642 enterpriseAddress: (id, address, taxIdType, taxId, by) => call("admin_enterprise_address", { id, address, taxIdType, taxId, by }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running643 invoiceEnterprise: (id, by) => call("admin_invoice_enterprise", { id, by }),
644 accountsFor: (workspaces) => call("admin_accounts", { query: null, workspaces }),
645 signals: () => call("admin_signals", {}),
646 overview: () => call("admin_overview", {}),
647 sales: (workspace) => call("admin_sales", { workspace }),
648 setSales: (workspace, record, by) =>
649 call("admin_set_sales", {
650 workspace,
651 stage: record.stage,
652 owner: record.owner ?? null,
653 next_step: record.nextStep ?? null,
654 next_at: record.nextAt ?? null,
655 by,
656 }),
657 addNote: (workspace, text, by) => call("admin_add_note", { workspace, text, by }),
658 workspaceInvoices: (workspace) => call("admin_workspace_invoices", { workspace }),
659 allInvoices: (filter = {}) => call("admin_invoices", { status: filter.status ?? null, month: filter.month ?? null }),
660 audit: (filter = {}) =>
661 call("admin_audit", { by: filter.by ?? null, action: filter.action ?? null, before: filter.before ?? null }),
662 limitRequests: (status = "open") => call("admin_limit_requests", { status }),
663 decideLimitRequest: (id, decision, amountMicros, note, by) =>
664 call("admin_decide_limit_request", { id, decision, amount_micros: amountMicros, note, by }),
665 overages: () => call("admin_overages", {}),
666 goodwill: (workspace, amountMicros, reason, by, day = null) =>
667 call("admin_goodwill", { workspace, amount_micros: amountMicros, reason, by, day }),
668 velocity: () => call("admin_velocity", {}),
669 recordPayment: (workspace, amountMicros, reference, note, by) =>
670 call("admin_record_payment", { workspace, amount_micros: amountMicros, reference, note, by }),
671 costs: (days) => call("admin_costs", { days: days ?? null }),
672 costAlerts: () => call("admin_cost_alerts", {}),
673 spendCaps: () => call("admin_spend_caps", {}),
674 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)675 platformGuard: () => call("admin_platform_guard", {}),
676 setPause: (level, paused, note, by) => call("admin_set_pause", { level, paused, note, by }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running677 decideProposal: (id, decision, note, by) => call("admin_decide_proposal", { id, decision, note, by }),
678 setCostSettings: (settings, by) => call("admin_set_cost_settings", { settings, by }),
679 setCostMapping: (mapping, by) =>
680 call("admin_set_cost_mapping", {
681 product: mapping.product,
682 meter: mapping.meter,
683 bucket: mapping.bucket ?? "",
684 price_meter: mapping.priceMeter ?? null,
685 own_meter: mapping.ownMeter ?? null,
686 scale_to_own: mapping.scaleToOwn ?? false,
687 drift_percent: mapping.driftPercent ?? null,
688 note: mapping.note ?? "",
689 remove: mapping.remove ?? false,
690 by,
691 }),
692 runCosts: (by) => call("admin_run_costs", { by }),
693 };
694}
695
696export function eventsClient(service: ServiceBinding): EventsApi {
697 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
698 return {
699 publish: (events) => call("publish", { events }),
700 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.701 activityDigest: (args) => call("activity_digest", args),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running702 };
703}
704
705export function integrationsClient(service: ServiceBinding): IntegrationsApi {
706 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
707 return {
708 list: (workspace, viewer) => call("list", { workspace, viewer }),
709 connect: (actor, workspace, input) => call("connect", { actor, workspace, ...input }),
710 update: (actor, workspace, id, input) => call("update", { actor, workspace, id, ...input }),
711 disconnect: (actor, workspace, id) => call("disconnect", { actor, workspace, id }),
712 test: (actor, workspace, id) => call("test", { actor, workspace, id }),
713 deliveries: (workspace, viewer, id) => call("deliveries", { workspace, viewer, id }),
714 resolve: (workspace, viewer, reference) => call("resolve", { workspace, viewer, reference }),
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.715 import: (actor, repo, reference) => call("import", { actor, repo, reference }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running716 links: (repo, number) => call("links", { repo, number }),
Merge branch 'ask-g1t'717 askSettings: (workspace, viewer) => call("ask_settings", { workspace, viewer }),
718 setAskSettings: (actor, workspace, connectionId, model) =>
719 call("set_ask_settings", { actor, workspace, connection_id: connectionId, model }),
720 // Streamed: the response itself, not its parsed body.
721 ask: (actor, workspace, messages, page, signal) =>
722 service.fetch("https://service/rpc/ask", {
723 method: "POST",
724 headers: { "content-type": "application/json" },
725 body: JSON.stringify({ actor, workspace, messages, page }),
726 signal,
727 }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running728 };
729}
730
731export function webhooksClient(service: ServiceBinding): WebhooksApi {
732 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
733 return {
734 list: (viewer, owner) => call("list", { viewer, ...owner }),
735 create: (actor, owner, input) => call("create", { actor, ...owner, ...input }),
736 update: (actor, owner, id, input) => call("update", { actor, ...owner, id, ...input }),
737 delete: (actor, owner, id) => call("delete", { actor, ...owner, id }),
738 ping: (actor, owner, id) => call("ping", { actor, ...owner, id }),
739 deliveries: (viewer, owner, id) => call("deliveries", { viewer, ...owner, id }),
740 redeliver: (actor, owner, deliveryId) => call("redeliver", { actor, ...owner, deliveryId }),
741 };
742}
743
744export function actionsClient(service: ServiceBinding): ActionsApi {
745 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
746 return {
747 workflows: (repo, viewer) => call("workflows", { repo, viewer }),
748 runs: (repo, viewer, filter = {}) => call("runs", { repo, viewer, ...filter }),
Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009)749 run: (repo, viewer, id, attempt) => call("run", { repo, viewer, id, attempt: attempt ?? null }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running750 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)751 summaries: (repo, viewer, id, attempt) => call("summaries", { repo, viewer, id, attempt: attempt ?? null }),
752 jobLogText: (repo, viewer, job) => call("job_log_text", { repo, viewer, job }),
753 runLogs: (repo, viewer, id, attempt) => call("run_logs", { repo, viewer, id, attempt: attempt ?? null }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running754 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)755 cancel: (actor, repo, id, force = false) => call("cancel", { actor, repo, id, force }),
756 rerun: (actor, repo, id, failedOnly = false, options = {}) =>
757 call("rerun", { actor, repo, id, failed_only: failedOnly, job: options.job ?? null, debug: options.debug ?? false }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running758 setWorkflowEnabled: (actor, repo, workflow, enabled) =>
759 call("set_workflow_enabled", { actor, repo, workflow, enabled }),
760 settings: (actor, owner, kind) => call("settings", { actor, ...owner, kind }),
761 setSetting: (actor, owner, kind, name, value, options = {}) =>
762 call("set_setting", { actor, ...owner, kind, name, value, ...options }),
763 deleteSetting: (actor, owner, kind, name, id) => call("delete_setting", { actor, ...owner, kind, name, id }),
Merge branch 'worktree-agent-a3abfcce648e87dca'764 approveRun: (actor, repo, id) => call("approve_run", { actor, repo, id }),
765 pendingDeployments: (repo, viewer, id) => call("pending_deployments", { repo, viewer, id }),
766 reviewDeployments: (actor, repo, id, state, environments = [], comment) =>
767 call("review_deployments", { actor, repo, id, state, environments, comment: comment ?? null }),
768 actionsSettings: (repo, viewer) => call("actions_settings", { repo, viewer }),
769 setActionsSettings: (actor, repo, change) => call("set_actions_settings", { actor, repo, ...change }),
770 workspaceActionsSettings: (workspace, viewer) => call("workspace_actions_settings", { workspace, viewer }),
771 setWorkspaceActionsSettings: (actor, workspace, change) =>
772 call("set_workspace_actions_settings", { actor, workspace, ...change }),
Merge GitHub pull request #8 (claude/cool-newton-7nm8r9): Docker deployments on g1t.page behind a switch that stays off until Cloudflare's container APIs are checked live, workflows a workspace manages in its repositories, tarball and zipball downloads, refs/pull/* kept to g1t alone, framework detection for g1t.page builds, and a workflow's event payload matching GitHub's.773 workspaceManagedWorkflows: (workspace, viewer) => call("workspace_managed_workflows", { workspace, viewer }),
774 setWorkspaceManagedWorkflow: (actor, workspace, name, change, create = false) =>
775 call("set_workspace_managed_workflow", { actor, workspace, name, create, ...change }),
776 removeWorkspaceManagedWorkflow: (actor, workspace, name) =>
777 call("remove_workspace_managed_workflow", { actor, workspace, name }),
Merge branch 'worktree-agent-a3abfcce648e87dca'778 environments: (repo, viewer) => call("environments", { repo, viewer }),
779 setEnvironment: (actor, repo, name, change) => call("set_environment", { actor, repo, name, ...change }),
780 deleteEnvironment: (actor, repo, name) => call("delete_environment", { actor, repo, name }),
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R2781 artifacts: (repo, viewer, filter = {}) => call("artifacts", { repo, viewer, ...filter }),
782 artifactDownload: (repo, viewer, by) => call("artifact_download", { repo, viewer, ...by }),
783 deleteArtifact: (actor, repo, id) => call("delete_artifact", { actor, repo, id }),
784 artifactRetention: (repo, viewer, days) => call("artifact_retention", { repo, viewer, days }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running785 };
786}
787
788
Merge main into rm-agents: mirroring removal, open signups, free-tier limits, the g1t CLI and runner fixes, with the agent removal kept. The CLI's table of operations is regenerated from the API without the agent, customize and AI credit operations; no CLI command used them.789/** Staff-only self-hosted runners, on the actions service: only sudo calls it. */
790export function runnersAdminClient(service: ServiceBinding): RunnersAdminApi {
791 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
792 return {
793 list: (workspace) => call("admin_runners", { workspace }),
794 remove: (workspace, id, staff) => call("admin_remove_runners", { workspace, id, staff }),
795 };
796}
797
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running798/** Self-hosted runners, kept by the actions service. */
799export function runnersClient(service: ServiceBinding): RunnersApi {
800 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
801 return {
802 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.803 activity: (actor, workspace) => call("runner_activity", { actor, workspace }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running804 list: (actor, owner) => call("runners", { actor, ...owner }),
805 createToken: (actor, owner, group) => call("create_registration_token", { actor, ...owner, group }),
806 remove: (actor, owner, id) => call("remove_runner", { actor, ...owner, id }),
807 groups: (actor, workspace) => call("runner_groups", { actor, workspace }),
808 setGroup: (actor, workspace, group) => call("set_runner_group", { actor, workspace, ...group }),
809 deleteGroup: (actor, workspace, id) => call("delete_runner_group", { actor, workspace, id }),
810 settings: (actor, owner) => call("runner_settings", { actor, ...owner }),
811 setSettings: (actor, owner, change) => call("set_runner_settings", { actor, ...owner, ...change }),
Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.812 reportDesktop: (actor, report) => call("report_desktop", { actor, ...report }),
813 desktops: (actor, workspace) => call("desktop_runners", { actor, workspace }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running814 };
815}
816
817export function deploymentsClient(service: ServiceBinding): DeploymentsApi {
818 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
819 return {
820 settings: (project, viewer) => call("settings", { project, viewer }),
821 updateSettings: (actor, project, changes) => call("update_settings", { actor, project, changes }),
822 list: (project, viewer) => call("list", { project, viewer }),
823 get: (project, id, viewer) => call("get", { project, id, viewer }),
824 redeploy: (actor, project, branch) => call("redeploy", { actor, project, branch }),
825 takeDown: (actor, project, branch) => call("take_down", { actor, project, branch }),
826 overview: (workspace, viewer) => call("overview", { workspace, viewer }),
827 usage: (workspace, viewer) => call("usage", { workspace, viewer }),
828 domains: (project, viewer) => call("domains", { project, viewer }),
829 addDomain: (actor, project, hostname, options = {}) =>
830 call("add_domain", { actor, project, hostname, twin: !!options.twin }),
831 removeDomain: (actor, project, id) => call("remove_domain", { actor, project, id }),
832 refreshDomain: (actor, project, id) => call("refresh_domain", { actor, project, id }),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97833 repoDeployments: (repo, viewer, filter = {}) => call("list_deployments", { repo, viewer, ...filter }),
834 repoDeployment: (repo, id, viewer) => call("get_deployment", { repo, id, viewer }),
835 environments: (repo, viewer) => call("list_environments", { repo, viewer }),
836 createDeployment: (actor, repo, input) => call("create_deployment", { repo, actor, ...input }),
837 createDeploymentStatus: (actor, repo, id, input) => call("create_deployment_status", { repo, actor, id, ...input }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running838 };
839}
840
841/** Packages: the registries beside the code (services/packages). */
842export function packagesClient(service: ServiceBinding): PackagesApi {
843 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
844 return {
845 list: (workspace, viewer, filter = {}) =>
846 call("list_packages", {
847 workspace,
848 viewer,
849 ecosystem: filter.ecosystem ?? null,
850 repo_id: filter.repoId ?? null,
851 query: filter.query ?? null,
852 }),
853 get: (workspace, ecosystem, name, viewer) => call("get_package", { workspace, ecosystem, name, viewer }),
854 deleteVersion: (actor, workspace, ecosystem, name, version, surface) =>
855 call("delete_version", { actor, workspace, ecosystem, name, version, surface: surface ?? null }),
856 deletePackage: (actor, workspace, ecosystem, name, surface) =>
857 call("delete_package", { actor, workspace, ecosystem, name, surface: surface ?? null }),
858 set: (actor, workspace, ecosystem, name, change, surface) =>
859 call("set_package", {
860 actor,
861 workspace,
862 ecosystem,
863 name,
864 visibility: change.visibility ?? null,
865 link: change.link ?? null,
866 unlink: change.unlink ?? false,
Merge packages: roles, Actions access, source label, soft delete, API867 inherit_access: change.inheritAccess ?? null,
868 surface: surface ?? null,
869 }),
870 settings: (workspace, ecosystem, name, viewer) => call("package_settings", { workspace, ecosystem, name, viewer }),
871 deleted: (workspace, viewer) => call("deleted_packages", { workspace, viewer }),
872 restorePackage: (actor, workspace, ecosystem, name, surface) =>
873 call("restore_package", { actor, workspace, ecosystem, name, surface: surface ?? null }),
874 restoreVersion: (actor, workspace, ecosystem, name, version, surface) =>
875 call("restore_version", { actor, workspace, ecosystem, name, version, surface: surface ?? null }),
876 setAccess: (actor, workspace, ecosystem, name, who, role, surface) =>
877 call("set_package_access", {
878 actor,
879 workspace,
880 ecosystem,
881 name,
882 user: "user" in who ? who.user : null,
883 team: "team" in who ? who.team : null,
884 role,
885 surface: surface ?? null,
886 }),
887 removeAccess: (actor, workspace, ecosystem, name, who, surface) =>
888 call("remove_package_access", {
889 actor,
890 workspace,
891 ecosystem,
892 name,
893 user: "user" in who ? who.user : null,
894 team: "team" in who ? who.team : null,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running895 surface: surface ?? null,
896 }),
Merge packages: roles, Actions access, source label, soft delete, API897 setActionsAccess: (actor, workspace, ecosystem, name, repo, role, surface) =>
898 call("set_actions_access", { actor, workspace, ecosystem, name, repo, role, surface: surface ?? null }),
899 removeActionsAccess: (actor, workspace, ecosystem, name, repo, surface) =>
900 call("remove_actions_access", { actor, workspace, ecosystem, name, repo, surface: surface ?? null }),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running901 storage: (workspace) => call("storage", { workspace }),
902 storageAll: () => call("storage_all", {}),
903 syncComposer: (repoId) => call("sync_composer", { repo_id: repoId }),
904 };
905}
906
907export function projectsClient(service: ServiceBinding): ProjectsApi {
908 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
909 return {
910 list: (workspace, viewer) => call("list", { workspace, viewer }),
911 get: (workspace, slug, viewer) => call("get", { workspace, slug, viewer }),
912 byRepo: (repoId) => call("by_repo", { repoId }),
913 create: (actor, workspace, input) => call("create", { actor, workspace, input }),
914 update: (actor, workspace, slug, changes) => call("update", { actor, workspace, slug, changes }),
Merge branch 'worktree-agent-a5b0d9e2733e9e382'915 setProductionImage: (actor, workspace, slug, change) => call("set_production_image", { actor, workspace, slug, change }),
916 externalProductions: () => call("external_productions", {}),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running917 deploymentsChanged: (projectId, enabled) => call("deployments_changed", { projectId, enabled }),
Projects: pinned and recent projects, per person per workspace918 shortcuts: (workspace, viewer) => call("shortcuts", { workspace, viewer }),
919 pin: (actor, workspace, slug, position) => call("pin", { actor, workspace, slug, position: position ?? null }),
920 unpin: (actor, workspace, slug) => call("unpin", { actor, workspace, slug }),
921 reorderPins: (actor, workspace, slugs) => call("reorder_pins", { actor, workspace, slugs }),
922 visited: (actor, projectId) => call("visited", { actor, projectId }),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97923 publicLinks: (repos) => call("public_links", { repos }),
Every agent can have its own computer. A session that needs one wakes it: a home of its own on g1t cloud, one per agent and never shared, where it runs commands, reads and writes files and keeps what it made, with each session working in its own folder under a shared home; after ten idle minutes it sleeps, its home kept as a snapshot and restored when it wakes, and Reset wipes the home while memory and artifacts stay. Its shell and files are abilities with the usual choices, Alone, Alone when asked, Ask first or Never, offered only inside sessions and never to a chat reply; every command shows on the session with its output, and the agent's new Computer tab shows the state, the disk used of the five gigabytes included, the recent commands, and Wake, Put to sleep and Reset. Machine time counts only while it is awake, on the sandbox lines of the ledger that name the agent and who asked, held to the same spend caps as the session; the disk itself costs nothing in this version. The runner gained a long-lived supervisor that answers the computer's requests inside the container, and the runner service a computer per agent that keeps its snapshot in the agent homes bucket when one is attached, and says so when none is. The REST API and the agent tool can read a computer, wake it, put it to sleep and reset it. The agents, abilities, sessions, runners, billing and deploy guides say how it works and what an operator sets up; pinning a computer to your own runner, its browser and take-over come next.924 };
925}

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