pr_01m47d15m3e54sn21z27rpy5n9/packages/contracts/src/identity.ts

219 lines8,534 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Agents as a team: lifecycle, merge queue, billing and a new shell1import type { RepoPath } from "./repos";
Initial g1t: services, event bus, intents and attempts2import type { Result } from "./result";
3
Email verification, password reset, and Git for AI scale positioning4export type User = {
5 id: string;
6 username: string;
7 /**
Agents as a team: lifecycle, merge queue, billing and a new shell8 * `workspace` when a workspace is acting through one of its own access
9 * tokens: `id` is then the workspace's and `username` its slug. Absent
10 * means `user`.
11 */
12 kind?: "user" | "workspace" | "agent";
13 /**
Email verification, password reset, and Git for AI scale positioning14 * Whether the account's email address is confirmed. Only set on users
15 * resolved from credentials; unverified accounts cannot change anything.
16 */
17 verified?: boolean;
Workspaces own repositories18 /**
19 * The workspaces this user belongs to. Set on users resolved from
20 * credentials, so any service can authorize from it.
21 */
22 workspaces?: Membership[];
Email verification, password reset, and Git for AI scale positioning23};
Initial g1t: services, event bus, intents and attempts24
Workspaces own repositories25/** What a member may do: an owner also manages the workspace's members. */
26export type Role = "owner" | "member";
27
28export type Membership = { slug: string; role: Role };
29
30/**
31 * A workspace: the owner of repositories, and the first segment of their
32 * URLs. A person's own space and a team's are the same thing.
33 */
34export type Workspace = {
35 id: string;
36 slug: string;
37 name: string;
Agents as a team: lifecycle, merge queue, billing and a new shell38 /** One line saying what the workspace is for. */
39 description: string | null;
Workspaces own repositories40 /** RFC 3339. */
41 createdAt: string;
42 memberCount: number;
43};
44
45export type Member = { username: string; role: Role };
46
Initial g1t: services, event bus, intents and attempts47/** Who is asking. Every read and write in every service takes one. */
48export type Viewer = User | null;
49
50export type SshKey = {
51 id: string;
52 title: string;
53 fingerprint: string;
RFC 3339 timestamps in identity and repos54 /** RFC 3339. */
55 createdAt: string;
Initial g1t: services, event bus, intents and attempts56};
57
Agents as a team: lifecycle, merge queue, billing and a new shell58export type AccessToken = {
59 id: string;
60 name: string;
61 /** RFC 3339. */
62 createdAt: string;
63 /** RFC 3339, to within a few minutes. Null until it is first used. */
64 lastUsedAt: string | null;
65 /**
66 * For a workspace's token, the username of the member who made it. Null
67 * once that account is gone, and on personal tokens.
68 */
69 createdBy: string | null;
70};
Initial g1t: services, event bus, intents and attempts71
Device sign-in replaces registering and minting tokens over the API72export type DeviceStart = {
73 /** Secret held by the tool and exchanged for a token once approved. */
74 deviceCode: string;
75 /** Short code shown to the person, e.g. `WDJB-MJHT`. */
76 userCode: string;
77 /** Seconds until both codes stop working. */
78 expiresIn: number;
79 /** Seconds the tool should wait between polls. */
80 interval: number;
81};
82
83export type DeviceRequest = { userCode: string; clientName: string };
84
85export type DeviceClaim =
86 | { status: "pending" | "denied" | "expired" }
87 | { status: "approved"; token: string; user: User };
88
OAuth 2.1 sign-in for MCP clients and other applications89/** What the site passes on once a person has approved an application. */
90export type OAuthApproval = {
91 clientId: string;
92 /** Shown wherever the application's access is listed. */
93 clientName: string;
94 redirectUri: string;
95 /** PKCE challenge, method S256. */
96 codeChallenge: string;
97};
98
99export type OAuthTokens = {
100 accessToken: string;
101 /** Works once; using it returns the next one. */
102 refreshToken: string;
103 /** Seconds until the access token stops working. */
104 expiresIn: number;
105};
106
107/** An application a person has signed in to. */
108export type OAuthGrant = {
109 id: string;
110 clientName: string;
111 /** RFC 3339. */
112 createdAt: string;
113 /** RFC 3339. */
114 lastUsedAt: string;
115};
116
Initial g1t: services, event bus, intents and attempts117/** Accounts, credentials and sessions. */
118export interface IdentityApi {
API and MCP server, Rust identity service, registration, site redesign119 /** Creates an account and signs it in. */
120 register(username: string, email: string, password: string): Promise<Result<{ user: User; sessionToken: string }>>;
Initial g1t: services, event bus, intents and attempts121 /** Verifies a username and password for website sign-in. */
122 signIn(username: string, password: string): Promise<Result<{ user: User; sessionToken: string }>>;
123 signOut(sessionToken: string): Promise<void>;
Email verification, password reset, and Git for AI scale positioning124
125 /** Sends the confirmation email again. */
126 resendVerification(user: User): Promise<Result<boolean>>;
127 /** Confirms the address the emailed token was sent to. */
128 verifyEmail(token: string): Promise<Result<User>>;
129 /** Emails a reset link if the address has an account. Always resolves. */
130 requestPasswordReset(email: string): Promise<boolean>;
131 /** Sets a new password from an emailed token and ends every session. */
132 resetPassword(token: string, password: string): Promise<Result<User>>;
133
Device sign-in replaces registering and minting tokens over the API134 /**
135 * Device sign-in (RFC 8628). A tool starts a request, a person approves
136 * its short code in a browser, and the tool claims an access token.
137 */
138 deviceStart(clientName: string): Promise<DeviceStart>;
139 /** What a user code is asking for, or null if it is not valid. */
140 deviceLookup(userCode: string): Promise<DeviceRequest | null>;
141 deviceResolve(userCode: string, user: User, approve: boolean): Promise<Result<boolean>>;
142 deviceClaim(deviceCode: string): Promise<DeviceClaim>;
143
OAuth 2.1 sign-in for MCP clients and other applications144 /**
145 * OAuth 2.1 for applications that sign a person in through the browser.
146 * The caller has checked the client and its redirect address; this
147 * returns the one-time code the application exchanges for tokens.
148 */
149 oauthAuthorize(user: User, approval: OAuthApproval): Promise<{ code: string }>;
150 /** Redeems a code. It works once, for that client, with the PKCE verifier. */
151 oauthExchange(code: string, codeVerifier: string, clientId: string, redirectUri: string): Promise<Result<OAuthTokens>>;
152 /** Trades a refresh token for new tokens; the old ones stop working. */
153 oauthRefresh(refreshToken: string, clientId: string): Promise<Result<OAuthTokens>>;
154 /** Applications the user has signed in to, most recently used first. */
155 listOAuthGrants(user: User): Promise<OAuthGrant[]>;
156 /** Signs an application out. */
157 revokeOAuthGrant(user: User, id: string): Promise<void>;
158
Workspaces own repositories159 createWorkspace(user: User, slug: string, name: string): Promise<Result<Workspace>>;
160 /** Public details of a workspace, or null. */
161 getWorkspace(slug: string): Promise<Workspace | null>;
162 /** Members only. */
163 listMembers(slug: string, viewer: Viewer): Promise<Result<Member[]>>;
164 /** Owners only. */
165 addMember(actor: User, slug: string, username: string): Promise<Result<boolean>>;
166 /** Owners only. */
167 removeMember(actor: User, slug: string, username: string): Promise<Result<boolean>>;
Agents as a team: lifecycle, merge queue, billing and a new shell168 /** Owners only. An empty name falls back to the slug. */
169 updateWorkspace(actor: User, slug: string, details: { name: string; description: string }): Promise<Result<Workspace>>;
Workspaces own repositories170
Agents as a team: lifecycle, merge queue, billing and a new shell171 /**
172 * A workspace's own access tokens. They belong to the workspace, act as
173 * it, and keep working when the member who made one leaves. Members only.
174 */
175 listWorkspaceTokens(slug: string, viewer: Viewer): Promise<Result<AccessToken[]>>;
176 /** Owners only. The plaintext token is returned once and never stored. */
177 createWorkspaceToken(actor: User, slug: string, name: string): Promise<Result<{ token: string; info: AccessToken }>>;
178 /** Owners only. */
179 removeWorkspaceToken(actor: User, slug: string, id: string): Promise<Result<boolean>>;
180
Initial g1t: services, event bus, intents and attempts181 userForSession(sessionToken: string): Promise<Viewer>;
182
183 /** Verifies git credentials: the account password or an access token. */
184 userForGitCredentials(username: string, secret: string): Promise<Viewer>;
API and MCP server, Rust identity service, registration, site redesign185 /** Resolves a `g1t_…` access token, as sent to the API and MCP server. */
186 userForAccessToken(token: string): Promise<Viewer>;
Initial g1t: services, event bus, intents and attempts187 userForSshKey(fingerprint: string): Promise<Viewer>;
188 userByUsername(username: string): Promise<Viewer>;
What happened across an outcome, as a feed beside its graph189 /** The names behind account and workspace ids; unknown ids are left out. */
190 usernames(ids: string[]): Promise<Record<string, string>>;
Initial g1t: services, event bus, intents and attempts191
192 listSshKeys(user: User): Promise<SshKey[]>;
193 /** Takes one line in OpenSSH public key format. */
194 addSshKey(user: User, title: string, publicKey: string): Promise<Result<SshKey>>;
195 removeSshKey(user: User, id: string): Promise<void>;
196
197 listAccessTokens(user: User): Promise<AccessToken[]>;
Account dropdown, llms.txt onboarding, hosted agent runner (not yet deployed)198 /**
Agents as a team: lifecycle, merge queue, billing and a new shell199 * The plaintext token is returned once and never stored. With
200 * `ttlSeconds` the token expires and is left out of token lists; that
201 * form is used for hosted agents. A token made for a workspace acting
202 * through a token of its own belongs to that workspace too.
Account dropdown, llms.txt onboarding, hosted agent runner (not yet deployed)203 */
204 createAccessToken(user: User, name: string, ttlSeconds?: number): Promise<{ token: string; info: AccessToken }>;
Agents as a team: lifecycle, merge queue, billing and a new shell205 /**
206 * A token for a g1t agent working for `onBehalfOf`: it acts as
207 * `g1t-agent`, in `scope.repo` only, and only for `scope.operations`.
208 */
209 createAgentToken(
210 onBehalfOf: User,
211 scope: AgentScope,
212 ttlSeconds: number,
213 ): Promise<{ token: string; info: AccessToken }>;
Initial g1t: services, event bus, intents and attempts214 removeAccessToken(user: User, id: string): Promise<void>;
215}
Agents as a team: lifecycle, merge queue, billing and a new shell216
217
218/** What an agent's token may do: these operations, in this repository. */
219export type AgentScope = { repo: RepoPath; operations: string[] };