@gjermundgaraba/clankercreds-api 0.0.0-stage → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/admin.d.ts +394 -0
- package/dist/admin.js +214 -0
- package/dist/credentials.d.ts +144 -0
- package/dist/credentials.js +91 -0
- package/dist/machine.d.ts +180 -0
- package/dist/machine.js +102 -0
- package/dist/shared.d.ts +84 -0
- package/dist/shared.js +87 -0
- package/package.json +47 -3
- package/README.md +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gjermund Garaba
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/admin.d.ts
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import * as Action from "@gjermundgaraba/effect-actions/Action";
|
|
3
|
+
import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
|
|
4
|
+
import * as Authentication from "@gjermundgaraba/effect-actions/Authentication";
|
|
5
|
+
import { ProviderUnavailable } from "@gjermundgaraba/clankerauth-sdk/errors";
|
|
6
|
+
import { CurrentPrincipal } from "@gjermundgaraba/clankerauth-sdk/session";
|
|
7
|
+
import { Conflict, CredentialKind, LoginKind, Name, NeedsSignIn, NotFound, scopes, SecretKind, UpstreamUnavailable } from "./shared.ts";
|
|
8
|
+
/** A stored secret's state. The value itself is never read back out. */
|
|
9
|
+
export declare const SecretStatus: Schema.Struct<{
|
|
10
|
+
readonly name: Schema.String;
|
|
11
|
+
readonly kind: Schema.Literals<readonly ["gh", "claude", "token"]>;
|
|
12
|
+
/** The host a `gh` token is for; null for every other kind. */
|
|
13
|
+
readonly host: Schema.NullOr<Schema.String>;
|
|
14
|
+
readonly expiresAt: Schema.NullOr<Schema.String>;
|
|
15
|
+
readonly createdAt: Schema.String;
|
|
16
|
+
readonly updatedAt: Schema.String;
|
|
17
|
+
}>;
|
|
18
|
+
/** A login's state. It exists from the first completed sign-in on. */
|
|
19
|
+
export declare const LoginStatus: Schema.Struct<{
|
|
20
|
+
readonly name: Schema.String;
|
|
21
|
+
readonly kind: Schema.Literals<readonly ["chatgpt", "grok", "cursor"]>;
|
|
22
|
+
readonly needsSignIn: Schema.Boolean;
|
|
23
|
+
readonly email: Schema.NullOr<Schema.String>;
|
|
24
|
+
/** The ChatGPT account a ChatGPT login is for; null for every other kind. */
|
|
25
|
+
readonly accountId: Schema.NullOr<Schema.String>;
|
|
26
|
+
readonly accessExpiresAt: Schema.String;
|
|
27
|
+
readonly lastRefreshAt: Schema.String;
|
|
28
|
+
readonly lastError: Schema.NullOr<Schema.String>;
|
|
29
|
+
readonly createdAt: Schema.String;
|
|
30
|
+
readonly updatedAt: Schema.String;
|
|
31
|
+
}>;
|
|
32
|
+
export declare const CredentialStatus: Schema.Union<readonly [Schema.Struct<{
|
|
33
|
+
readonly name: Schema.String;
|
|
34
|
+
readonly kind: Schema.Literals<readonly ["gh", "claude", "token"]>;
|
|
35
|
+
/** The host a `gh` token is for; null for every other kind. */
|
|
36
|
+
readonly host: Schema.NullOr<Schema.String>;
|
|
37
|
+
readonly expiresAt: Schema.NullOr<Schema.String>;
|
|
38
|
+
readonly createdAt: Schema.String;
|
|
39
|
+
readonly updatedAt: Schema.String;
|
|
40
|
+
}>, Schema.Struct<{
|
|
41
|
+
readonly name: Schema.String;
|
|
42
|
+
readonly kind: Schema.Literals<readonly ["chatgpt", "grok", "cursor"]>;
|
|
43
|
+
readonly needsSignIn: Schema.Boolean;
|
|
44
|
+
readonly email: Schema.NullOr<Schema.String>;
|
|
45
|
+
/** The ChatGPT account a ChatGPT login is for; null for every other kind. */
|
|
46
|
+
readonly accountId: Schema.NullOr<Schema.String>;
|
|
47
|
+
readonly accessExpiresAt: Schema.String;
|
|
48
|
+
readonly lastRefreshAt: Schema.String;
|
|
49
|
+
readonly lastError: Schema.NullOr<Schema.String>;
|
|
50
|
+
readonly createdAt: Schema.String;
|
|
51
|
+
readonly updatedAt: Schema.String;
|
|
52
|
+
}>]>;
|
|
53
|
+
export type CredentialStatus = typeof CredentialStatus.Type;
|
|
54
|
+
/**
|
|
55
|
+
* A machine key and the credentials it may read. A machine names which of them it syncs; a
|
|
56
|
+
* replacement key is granted the same ones and overlaps the old key until that is removed.
|
|
57
|
+
* A key may be granted several credentials of one kind: several accounts of a provider.
|
|
58
|
+
*/
|
|
59
|
+
export declare const KeyInfo: Schema.Struct<{
|
|
60
|
+
readonly id: Schema.String;
|
|
61
|
+
readonly credentials: Schema.$Array<Schema.String>;
|
|
62
|
+
}>;
|
|
63
|
+
/**
|
|
64
|
+
* A pending Claude sign-in: approve at `authorizeUrl`, then hand the code the callback page
|
|
65
|
+
* shows to `completeClaudeSignIn`. The code is useless without the verifier the service holds.
|
|
66
|
+
*/
|
|
67
|
+
export declare const ClaudeSignIn: Schema.Struct<{
|
|
68
|
+
readonly signInId: Schema.String;
|
|
69
|
+
readonly credential: Schema.String;
|
|
70
|
+
readonly authorizeUrl: Schema.String;
|
|
71
|
+
readonly expiresAt: Schema.String;
|
|
72
|
+
}>;
|
|
73
|
+
declare const SignInRefused_base: Schema.Class<SignInRefused, Schema.TaggedStruct<"SignInRefused", {
|
|
74
|
+
readonly message: Schema.String;
|
|
75
|
+
}>, import("effect/Cause").YieldableError>;
|
|
76
|
+
/**
|
|
77
|
+
* A Claude sign-in's code was not taken: it is not this sign-in's, or Anthropic refused it.
|
|
78
|
+
* The sign-in stays open until it expires, for the code again or a new one from its
|
|
79
|
+
* `authorizeUrl`. An approval that left out a scope the token needs ends it: start again.
|
|
80
|
+
*/
|
|
81
|
+
export declare class SignInRefused extends SignInRefused_base {
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* A login's sign-in: open `verificationUrl` and approve, entering `userCode` where the
|
|
85
|
+
* provider asks for one. Cursor's link carries everything and has no code.
|
|
86
|
+
*/
|
|
87
|
+
export declare const SignInState: Schema.Struct<{
|
|
88
|
+
readonly signInId: Schema.String;
|
|
89
|
+
readonly credential: Schema.String;
|
|
90
|
+
readonly kind: Schema.Literals<readonly ["chatgpt", "grok", "cursor"]>;
|
|
91
|
+
readonly status: Schema.Literals<readonly ["pending", "complete", "failed", "expired"]>;
|
|
92
|
+
readonly userCode: Schema.NullOr<Schema.String>;
|
|
93
|
+
readonly verificationUrl: Schema.String;
|
|
94
|
+
readonly expiresAt: Schema.String;
|
|
95
|
+
readonly message: Schema.optionalKey<Schema.String>;
|
|
96
|
+
}>;
|
|
97
|
+
/** Credentials by name: rows outlive what they name, and stay findable. */
|
|
98
|
+
export declare const AuditEntry: Schema.Struct<{
|
|
99
|
+
readonly id: Schema.Int;
|
|
100
|
+
readonly at: Schema.String;
|
|
101
|
+
readonly actorKind: Schema.Literals<readonly ["machine", "admin", "system"]>;
|
|
102
|
+
readonly keyId: Schema.NullOr<Schema.String>;
|
|
103
|
+
readonly clientId: Schema.NullOr<Schema.String>;
|
|
104
|
+
readonly action: Schema.String;
|
|
105
|
+
readonly credential: Schema.NullOr<Schema.String>;
|
|
106
|
+
readonly remoteAddr: Schema.NullOr<Schema.String>;
|
|
107
|
+
readonly detail: Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>>;
|
|
108
|
+
}>;
|
|
109
|
+
/** Every filter is optional; the page size defaults on the service. */
|
|
110
|
+
export declare const AuditQuery: Schema.Struct<{
|
|
111
|
+
readonly limit: Schema.optionalKey<Schema.Int>;
|
|
112
|
+
readonly before: Schema.optionalKey<Schema.Int>;
|
|
113
|
+
readonly action: Schema.optionalKey<Schema.String>;
|
|
114
|
+
readonly credential: Schema.optionalKey<Schema.String>;
|
|
115
|
+
readonly keyId: Schema.optionalKey<Schema.String>;
|
|
116
|
+
readonly clientId: Schema.optionalKey<Schema.String>;
|
|
117
|
+
}>;
|
|
118
|
+
/**
|
|
119
|
+
* A read whose code or link signs an account in: exported so the server's authorization
|
|
120
|
+
* rule can name it.
|
|
121
|
+
*/
|
|
122
|
+
export declare const GetSignIn: Action.Action<"getSignIn", Schema.Struct<{
|
|
123
|
+
readonly signInId: Schema.String;
|
|
124
|
+
}>, Schema.Struct<{
|
|
125
|
+
readonly signInId: Schema.String;
|
|
126
|
+
readonly credential: Schema.String;
|
|
127
|
+
readonly kind: Schema.Literals<readonly ["chatgpt", "grok", "cursor"]>;
|
|
128
|
+
readonly status: Schema.Literals<readonly ["pending", "complete", "failed", "expired"]>;
|
|
129
|
+
readonly userCode: Schema.NullOr<Schema.String>;
|
|
130
|
+
readonly verificationUrl: Schema.String;
|
|
131
|
+
readonly expiresAt: Schema.String;
|
|
132
|
+
readonly message: Schema.optionalKey<Schema.String>;
|
|
133
|
+
}>, readonly (typeof NotFound)[], true, typeof CurrentPrincipal>;
|
|
134
|
+
/** Everything an operator does; which scope each needs is the server's authorization rule. */
|
|
135
|
+
export declare const Admin: readonly [Action.Action<"listCredentials", Schema.$Record<Schema.String, Schema.Never>, Schema.Struct<{
|
|
136
|
+
readonly credentials: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
|
|
137
|
+
readonly name: Schema.String;
|
|
138
|
+
readonly kind: Schema.Literals<readonly ["gh", "claude", "token"]>;
|
|
139
|
+
/** The host a `gh` token is for; null for every other kind. */
|
|
140
|
+
readonly host: Schema.NullOr<Schema.String>;
|
|
141
|
+
readonly expiresAt: Schema.NullOr<Schema.String>;
|
|
142
|
+
readonly createdAt: Schema.String;
|
|
143
|
+
readonly updatedAt: Schema.String;
|
|
144
|
+
}>, Schema.Struct<{
|
|
145
|
+
readonly name: Schema.String;
|
|
146
|
+
readonly kind: Schema.Literals<readonly ["chatgpt", "grok", "cursor"]>;
|
|
147
|
+
readonly needsSignIn: Schema.Boolean;
|
|
148
|
+
readonly email: Schema.NullOr<Schema.String>;
|
|
149
|
+
/** The ChatGPT account a ChatGPT login is for; null for every other kind. */
|
|
150
|
+
readonly accountId: Schema.NullOr<Schema.String>;
|
|
151
|
+
readonly accessExpiresAt: Schema.String;
|
|
152
|
+
readonly lastRefreshAt: Schema.String;
|
|
153
|
+
readonly lastError: Schema.NullOr<Schema.String>;
|
|
154
|
+
readonly createdAt: Schema.String;
|
|
155
|
+
readonly updatedAt: Schema.String;
|
|
156
|
+
}>]>>;
|
|
157
|
+
}>, readonly never[], true, typeof CurrentPrincipal>, Action.Action<"putCredential", Schema.Struct<{
|
|
158
|
+
readonly name: Schema.String;
|
|
159
|
+
readonly kind: Schema.Literals<readonly ["gh", "claude", "token"]>;
|
|
160
|
+
/** Surrounding whitespace, such as a pasted line ending, is not part of a secret: a value carrying any is refused, never altered. */
|
|
161
|
+
readonly value: Schema.Trimmed;
|
|
162
|
+
readonly host: Schema.optionalKey<Schema.String>;
|
|
163
|
+
/** Any date string JavaScript parses; a typo is refused, so alerting never loses it. Left out, it does not expire. */
|
|
164
|
+
readonly expiresAt: Schema.optionalKey<Schema.DateFromString>;
|
|
165
|
+
}>, Schema.Struct<{
|
|
166
|
+
readonly name: Schema.String;
|
|
167
|
+
readonly kind: Schema.Literals<readonly ["gh", "claude", "token"]>;
|
|
168
|
+
/** The host a `gh` token is for; null for every other kind. */
|
|
169
|
+
readonly host: Schema.NullOr<Schema.String>;
|
|
170
|
+
readonly expiresAt: Schema.NullOr<Schema.String>;
|
|
171
|
+
readonly createdAt: Schema.String;
|
|
172
|
+
readonly updatedAt: Schema.String;
|
|
173
|
+
}>, readonly (typeof Conflict)[], false, typeof CurrentPrincipal>, Action.Action<"removeCredential", Schema.Struct<{
|
|
174
|
+
readonly name: Schema.String;
|
|
175
|
+
}>, Schema.Void, readonly (typeof NotFound | typeof Conflict)[], false, typeof CurrentPrincipal>, Action.Action<"startSignIn", Schema.Struct<{
|
|
176
|
+
readonly name: Schema.String;
|
|
177
|
+
readonly kind: Schema.Literals<readonly ["chatgpt", "grok", "cursor"]>;
|
|
178
|
+
}>, Schema.Struct<{
|
|
179
|
+
readonly signInId: Schema.String;
|
|
180
|
+
readonly credential: Schema.String;
|
|
181
|
+
readonly kind: Schema.Literals<readonly ["chatgpt", "grok", "cursor"]>;
|
|
182
|
+
readonly status: Schema.Literals<readonly ["pending", "complete", "failed", "expired"]>;
|
|
183
|
+
readonly userCode: Schema.NullOr<Schema.String>;
|
|
184
|
+
readonly verificationUrl: Schema.String;
|
|
185
|
+
readonly expiresAt: Schema.String;
|
|
186
|
+
readonly message: Schema.optionalKey<Schema.String>;
|
|
187
|
+
}>, readonly (typeof Conflict | typeof UpstreamUnavailable)[], false, typeof CurrentPrincipal>, Action.Action<"getSignIn", Schema.Struct<{
|
|
188
|
+
readonly signInId: Schema.String;
|
|
189
|
+
}>, Schema.Struct<{
|
|
190
|
+
readonly signInId: Schema.String;
|
|
191
|
+
readonly credential: Schema.String;
|
|
192
|
+
readonly kind: Schema.Literals<readonly ["chatgpt", "grok", "cursor"]>;
|
|
193
|
+
readonly status: Schema.Literals<readonly ["pending", "complete", "failed", "expired"]>;
|
|
194
|
+
readonly userCode: Schema.NullOr<Schema.String>;
|
|
195
|
+
readonly verificationUrl: Schema.String;
|
|
196
|
+
readonly expiresAt: Schema.String;
|
|
197
|
+
readonly message: Schema.optionalKey<Schema.String>;
|
|
198
|
+
}>, readonly (typeof NotFound)[], true, typeof CurrentPrincipal>, Action.Action<"refreshCredential", Schema.Struct<{
|
|
199
|
+
readonly name: Schema.String;
|
|
200
|
+
}>, Schema.Struct<{
|
|
201
|
+
readonly name: Schema.String;
|
|
202
|
+
readonly kind: Schema.Literals<readonly ["chatgpt", "grok", "cursor"]>;
|
|
203
|
+
readonly needsSignIn: Schema.Boolean;
|
|
204
|
+
readonly email: Schema.NullOr<Schema.String>;
|
|
205
|
+
/** The ChatGPT account a ChatGPT login is for; null for every other kind. */
|
|
206
|
+
readonly accountId: Schema.NullOr<Schema.String>;
|
|
207
|
+
readonly accessExpiresAt: Schema.String;
|
|
208
|
+
readonly lastRefreshAt: Schema.String;
|
|
209
|
+
readonly lastError: Schema.NullOr<Schema.String>;
|
|
210
|
+
readonly createdAt: Schema.String;
|
|
211
|
+
readonly updatedAt: Schema.String;
|
|
212
|
+
}>, readonly (typeof NotFound | typeof Conflict | typeof NeedsSignIn | typeof UpstreamUnavailable)[], false, typeof CurrentPrincipal>, Action.Action<"startClaudeSignIn", Schema.Struct<{
|
|
213
|
+
readonly name: Schema.String;
|
|
214
|
+
}>, Schema.Struct<{
|
|
215
|
+
readonly signInId: Schema.String;
|
|
216
|
+
readonly credential: Schema.String;
|
|
217
|
+
readonly authorizeUrl: Schema.String;
|
|
218
|
+
readonly expiresAt: Schema.String;
|
|
219
|
+
}>, readonly (typeof Conflict)[], false, typeof CurrentPrincipal>, Action.Action<"completeClaudeSignIn", Schema.Struct<{
|
|
220
|
+
readonly signInId: Schema.String;
|
|
221
|
+
/** As the callback page shows it: `<code>#<state>`. */
|
|
222
|
+
readonly code: Schema.String;
|
|
223
|
+
}>, Schema.Struct<{
|
|
224
|
+
readonly name: Schema.String;
|
|
225
|
+
readonly kind: Schema.Literals<readonly ["gh", "claude", "token"]>;
|
|
226
|
+
/** The host a `gh` token is for; null for every other kind. */
|
|
227
|
+
readonly host: Schema.NullOr<Schema.String>;
|
|
228
|
+
readonly expiresAt: Schema.NullOr<Schema.String>;
|
|
229
|
+
readonly createdAt: Schema.String;
|
|
230
|
+
readonly updatedAt: Schema.String;
|
|
231
|
+
}>, readonly (typeof NotFound | typeof Conflict | typeof UpstreamUnavailable | typeof SignInRefused)[], false, typeof CurrentPrincipal>, Action.Action<"listKeys", Schema.$Record<Schema.String, Schema.Never>, Schema.Struct<{
|
|
232
|
+
readonly keys: Schema.$Array<Schema.Struct<{
|
|
233
|
+
readonly id: Schema.String;
|
|
234
|
+
readonly credentials: Schema.$Array<Schema.String>;
|
|
235
|
+
}>>;
|
|
236
|
+
}>, readonly never[], true, typeof CurrentPrincipal>, Action.Action<"putKey", Schema.Struct<{
|
|
237
|
+
readonly id: Schema.String;
|
|
238
|
+
readonly credentials: Schema.$Array<Schema.String>;
|
|
239
|
+
}>, Schema.Struct<{
|
|
240
|
+
readonly id: Schema.String;
|
|
241
|
+
readonly credentials: Schema.$Array<Schema.String>;
|
|
242
|
+
}>, readonly (typeof NotFound)[], false, typeof CurrentPrincipal>, Action.Action<"listAudit", Schema.Struct<{
|
|
243
|
+
readonly limit: Schema.optionalKey<Schema.Int>;
|
|
244
|
+
readonly before: Schema.optionalKey<Schema.Int>;
|
|
245
|
+
readonly action: Schema.optionalKey<Schema.String>;
|
|
246
|
+
readonly credential: Schema.optionalKey<Schema.String>;
|
|
247
|
+
readonly keyId: Schema.optionalKey<Schema.String>;
|
|
248
|
+
readonly clientId: Schema.optionalKey<Schema.String>;
|
|
249
|
+
}>, Schema.Struct<{
|
|
250
|
+
readonly entries: Schema.$Array<Schema.Struct<{
|
|
251
|
+
readonly id: Schema.Int;
|
|
252
|
+
readonly at: Schema.String;
|
|
253
|
+
readonly actorKind: Schema.Literals<readonly ["machine", "admin", "system"]>;
|
|
254
|
+
readonly keyId: Schema.NullOr<Schema.String>;
|
|
255
|
+
readonly clientId: Schema.NullOr<Schema.String>;
|
|
256
|
+
readonly action: Schema.String;
|
|
257
|
+
readonly credential: Schema.NullOr<Schema.String>;
|
|
258
|
+
readonly remoteAddr: Schema.NullOr<Schema.String>;
|
|
259
|
+
readonly detail: Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>>;
|
|
260
|
+
}>>;
|
|
261
|
+
}>, readonly never[], true, typeof CurrentPrincipal>];
|
|
262
|
+
/**
|
|
263
|
+
* An operator's bearer token on the private port: an access token or API key of the admin
|
|
264
|
+
* resource, verified by its own provider.
|
|
265
|
+
*/
|
|
266
|
+
export declare const AdminLogin: Authentication.Descriptor<CurrentPrincipal, import("@gjermundgaraba/clankerauth-sdk/session").Caller, import("effect/http-api/HttpApiSecurity").Http, "clankercreds.admin", readonly [typeof ProviderUnavailable]>;
|
|
267
|
+
export declare const AdminHttp: ActionHttp.Binding<readonly [Action.Action<"listCredentials", Schema.$Record<Schema.String, Schema.Never>, Schema.Struct<{
|
|
268
|
+
readonly credentials: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
|
|
269
|
+
readonly name: Schema.String;
|
|
270
|
+
readonly kind: Schema.Literals<readonly ["gh", "claude", "token"]>;
|
|
271
|
+
/** The host a `gh` token is for; null for every other kind. */
|
|
272
|
+
readonly host: Schema.NullOr<Schema.String>;
|
|
273
|
+
readonly expiresAt: Schema.NullOr<Schema.String>;
|
|
274
|
+
readonly createdAt: Schema.String;
|
|
275
|
+
readonly updatedAt: Schema.String;
|
|
276
|
+
}>, Schema.Struct<{
|
|
277
|
+
readonly name: Schema.String;
|
|
278
|
+
readonly kind: Schema.Literals<readonly ["chatgpt", "grok", "cursor"]>;
|
|
279
|
+
readonly needsSignIn: Schema.Boolean;
|
|
280
|
+
readonly email: Schema.NullOr<Schema.String>;
|
|
281
|
+
/** The ChatGPT account a ChatGPT login is for; null for every other kind. */
|
|
282
|
+
readonly accountId: Schema.NullOr<Schema.String>;
|
|
283
|
+
readonly accessExpiresAt: Schema.String;
|
|
284
|
+
readonly lastRefreshAt: Schema.String;
|
|
285
|
+
readonly lastError: Schema.NullOr<Schema.String>;
|
|
286
|
+
readonly createdAt: Schema.String;
|
|
287
|
+
readonly updatedAt: Schema.String;
|
|
288
|
+
}>]>>;
|
|
289
|
+
}>, readonly never[], true, typeof CurrentPrincipal>, Action.Action<"putCredential", Schema.Struct<{
|
|
290
|
+
readonly name: Schema.String;
|
|
291
|
+
readonly kind: Schema.Literals<readonly ["gh", "claude", "token"]>;
|
|
292
|
+
/** Surrounding whitespace, such as a pasted line ending, is not part of a secret: a value carrying any is refused, never altered. */
|
|
293
|
+
readonly value: Schema.Trimmed;
|
|
294
|
+
readonly host: Schema.optionalKey<Schema.String>;
|
|
295
|
+
/** Any date string JavaScript parses; a typo is refused, so alerting never loses it. Left out, it does not expire. */
|
|
296
|
+
readonly expiresAt: Schema.optionalKey<Schema.DateFromString>;
|
|
297
|
+
}>, Schema.Struct<{
|
|
298
|
+
readonly name: Schema.String;
|
|
299
|
+
readonly kind: Schema.Literals<readonly ["gh", "claude", "token"]>;
|
|
300
|
+
/** The host a `gh` token is for; null for every other kind. */
|
|
301
|
+
readonly host: Schema.NullOr<Schema.String>;
|
|
302
|
+
readonly expiresAt: Schema.NullOr<Schema.String>;
|
|
303
|
+
readonly createdAt: Schema.String;
|
|
304
|
+
readonly updatedAt: Schema.String;
|
|
305
|
+
}>, readonly (typeof Conflict)[], false, typeof CurrentPrincipal>, Action.Action<"removeCredential", Schema.Struct<{
|
|
306
|
+
readonly name: Schema.String;
|
|
307
|
+
}>, Schema.Void, readonly (typeof NotFound | typeof Conflict)[], false, typeof CurrentPrincipal>, Action.Action<"startSignIn", Schema.Struct<{
|
|
308
|
+
readonly name: Schema.String;
|
|
309
|
+
readonly kind: Schema.Literals<readonly ["chatgpt", "grok", "cursor"]>;
|
|
310
|
+
}>, Schema.Struct<{
|
|
311
|
+
readonly signInId: Schema.String;
|
|
312
|
+
readonly credential: Schema.String;
|
|
313
|
+
readonly kind: Schema.Literals<readonly ["chatgpt", "grok", "cursor"]>;
|
|
314
|
+
readonly status: Schema.Literals<readonly ["pending", "complete", "failed", "expired"]>;
|
|
315
|
+
readonly userCode: Schema.NullOr<Schema.String>;
|
|
316
|
+
readonly verificationUrl: Schema.String;
|
|
317
|
+
readonly expiresAt: Schema.String;
|
|
318
|
+
readonly message: Schema.optionalKey<Schema.String>;
|
|
319
|
+
}>, readonly (typeof Conflict | typeof UpstreamUnavailable)[], false, typeof CurrentPrincipal>, Action.Action<"getSignIn", Schema.Struct<{
|
|
320
|
+
readonly signInId: Schema.String;
|
|
321
|
+
}>, Schema.Struct<{
|
|
322
|
+
readonly signInId: Schema.String;
|
|
323
|
+
readonly credential: Schema.String;
|
|
324
|
+
readonly kind: Schema.Literals<readonly ["chatgpt", "grok", "cursor"]>;
|
|
325
|
+
readonly status: Schema.Literals<readonly ["pending", "complete", "failed", "expired"]>;
|
|
326
|
+
readonly userCode: Schema.NullOr<Schema.String>;
|
|
327
|
+
readonly verificationUrl: Schema.String;
|
|
328
|
+
readonly expiresAt: Schema.String;
|
|
329
|
+
readonly message: Schema.optionalKey<Schema.String>;
|
|
330
|
+
}>, readonly (typeof NotFound)[], true, typeof CurrentPrincipal>, Action.Action<"refreshCredential", Schema.Struct<{
|
|
331
|
+
readonly name: Schema.String;
|
|
332
|
+
}>, Schema.Struct<{
|
|
333
|
+
readonly name: Schema.String;
|
|
334
|
+
readonly kind: Schema.Literals<readonly ["chatgpt", "grok", "cursor"]>;
|
|
335
|
+
readonly needsSignIn: Schema.Boolean;
|
|
336
|
+
readonly email: Schema.NullOr<Schema.String>;
|
|
337
|
+
/** The ChatGPT account a ChatGPT login is for; null for every other kind. */
|
|
338
|
+
readonly accountId: Schema.NullOr<Schema.String>;
|
|
339
|
+
readonly accessExpiresAt: Schema.String;
|
|
340
|
+
readonly lastRefreshAt: Schema.String;
|
|
341
|
+
readonly lastError: Schema.NullOr<Schema.String>;
|
|
342
|
+
readonly createdAt: Schema.String;
|
|
343
|
+
readonly updatedAt: Schema.String;
|
|
344
|
+
}>, readonly (typeof NotFound | typeof Conflict | typeof NeedsSignIn | typeof UpstreamUnavailable)[], false, typeof CurrentPrincipal>, Action.Action<"startClaudeSignIn", Schema.Struct<{
|
|
345
|
+
readonly name: Schema.String;
|
|
346
|
+
}>, Schema.Struct<{
|
|
347
|
+
readonly signInId: Schema.String;
|
|
348
|
+
readonly credential: Schema.String;
|
|
349
|
+
readonly authorizeUrl: Schema.String;
|
|
350
|
+
readonly expiresAt: Schema.String;
|
|
351
|
+
}>, readonly (typeof Conflict)[], false, typeof CurrentPrincipal>, Action.Action<"completeClaudeSignIn", Schema.Struct<{
|
|
352
|
+
readonly signInId: Schema.String;
|
|
353
|
+
/** As the callback page shows it: `<code>#<state>`. */
|
|
354
|
+
readonly code: Schema.String;
|
|
355
|
+
}>, Schema.Struct<{
|
|
356
|
+
readonly name: Schema.String;
|
|
357
|
+
readonly kind: Schema.Literals<readonly ["gh", "claude", "token"]>;
|
|
358
|
+
/** The host a `gh` token is for; null for every other kind. */
|
|
359
|
+
readonly host: Schema.NullOr<Schema.String>;
|
|
360
|
+
readonly expiresAt: Schema.NullOr<Schema.String>;
|
|
361
|
+
readonly createdAt: Schema.String;
|
|
362
|
+
readonly updatedAt: Schema.String;
|
|
363
|
+
}>, readonly (typeof NotFound | typeof Conflict | typeof UpstreamUnavailable | typeof SignInRefused)[], false, typeof CurrentPrincipal>, Action.Action<"listKeys", Schema.$Record<Schema.String, Schema.Never>, Schema.Struct<{
|
|
364
|
+
readonly keys: Schema.$Array<Schema.Struct<{
|
|
365
|
+
readonly id: Schema.String;
|
|
366
|
+
readonly credentials: Schema.$Array<Schema.String>;
|
|
367
|
+
}>>;
|
|
368
|
+
}>, readonly never[], true, typeof CurrentPrincipal>, Action.Action<"putKey", Schema.Struct<{
|
|
369
|
+
readonly id: Schema.String;
|
|
370
|
+
readonly credentials: Schema.$Array<Schema.String>;
|
|
371
|
+
}>, Schema.Struct<{
|
|
372
|
+
readonly id: Schema.String;
|
|
373
|
+
readonly credentials: Schema.$Array<Schema.String>;
|
|
374
|
+
}>, readonly (typeof NotFound)[], false, typeof CurrentPrincipal>, Action.Action<"listAudit", Schema.Struct<{
|
|
375
|
+
readonly limit: Schema.optionalKey<Schema.Int>;
|
|
376
|
+
readonly before: Schema.optionalKey<Schema.Int>;
|
|
377
|
+
readonly action: Schema.optionalKey<Schema.String>;
|
|
378
|
+
readonly credential: Schema.optionalKey<Schema.String>;
|
|
379
|
+
readonly keyId: Schema.optionalKey<Schema.String>;
|
|
380
|
+
readonly clientId: Schema.optionalKey<Schema.String>;
|
|
381
|
+
}>, Schema.Struct<{
|
|
382
|
+
readonly entries: Schema.$Array<Schema.Struct<{
|
|
383
|
+
readonly id: Schema.Int;
|
|
384
|
+
readonly at: Schema.String;
|
|
385
|
+
readonly actorKind: Schema.Literals<readonly ["machine", "admin", "system"]>;
|
|
386
|
+
readonly keyId: Schema.NullOr<Schema.String>;
|
|
387
|
+
readonly clientId: Schema.NullOr<Schema.String>;
|
|
388
|
+
readonly action: Schema.String;
|
|
389
|
+
readonly credential: Schema.NullOr<Schema.String>;
|
|
390
|
+
readonly remoteAddr: Schema.NullOr<Schema.String>;
|
|
391
|
+
readonly detail: Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>>;
|
|
392
|
+
}>>;
|
|
393
|
+
}>, readonly never[], true, typeof CurrentPrincipal>], [], Authentication.Descriptor<CurrentPrincipal, import("@gjermundgaraba/clankerauth-sdk/session").Caller, import("effect/http-api/HttpApiSecurity").Http, "clankercreds.admin", readonly [typeof ProviderUnavailable]>>;
|
|
394
|
+
export { Conflict, CredentialKind, LoginKind, Name, NeedsSignIn, NotFound, scopes, SecretKind, UpstreamUnavailable, };
|
package/dist/admin.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import * as Action from "@gjermundgaraba/effect-actions/Action";
|
|
3
|
+
import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
|
|
4
|
+
import * as Authentication from "@gjermundgaraba/effect-actions/Authentication";
|
|
5
|
+
import { ProviderUnavailable } from "@gjermundgaraba/clankerauth-sdk/errors";
|
|
6
|
+
import { CurrentPrincipal } from "@gjermundgaraba/clankerauth-sdk/session";
|
|
7
|
+
import { apiPath, Conflict, CredentialKind, Host, LoginKind, Name, NeedsSignIn, NotFound, scopes, SecretKind, UpstreamUnavailable, } from "./shared.js";
|
|
8
|
+
/** Written by the service, always ISO. What an operator enters is decoded as a date instead. */
|
|
9
|
+
const Timestamp = Schema.String;
|
|
10
|
+
/** A stored secret's state. The value itself is never read back out. */
|
|
11
|
+
export const SecretStatus = Schema.Struct({
|
|
12
|
+
name: Name,
|
|
13
|
+
kind: SecretKind,
|
|
14
|
+
/** The host a `gh` token is for; null for every other kind. */
|
|
15
|
+
host: Schema.NullOr(Schema.String),
|
|
16
|
+
expiresAt: Schema.NullOr(Timestamp),
|
|
17
|
+
createdAt: Timestamp,
|
|
18
|
+
updatedAt: Timestamp,
|
|
19
|
+
});
|
|
20
|
+
/** A login's state. It exists from the first completed sign-in on. */
|
|
21
|
+
export const LoginStatus = Schema.Struct({
|
|
22
|
+
name: Name,
|
|
23
|
+
kind: LoginKind,
|
|
24
|
+
needsSignIn: Schema.Boolean,
|
|
25
|
+
email: Schema.NullOr(Schema.String),
|
|
26
|
+
/** The ChatGPT account a ChatGPT login is for; null for every other kind. */
|
|
27
|
+
accountId: Schema.NullOr(Schema.String),
|
|
28
|
+
accessExpiresAt: Timestamp,
|
|
29
|
+
lastRefreshAt: Timestamp,
|
|
30
|
+
lastError: Schema.NullOr(Schema.String),
|
|
31
|
+
createdAt: Timestamp,
|
|
32
|
+
updatedAt: Timestamp,
|
|
33
|
+
});
|
|
34
|
+
export const CredentialStatus = Schema.Union([SecretStatus, LoginStatus]);
|
|
35
|
+
/** The clankerauth id of a machine key. */
|
|
36
|
+
const KeyId = Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(256));
|
|
37
|
+
/**
|
|
38
|
+
* A machine key and the credentials it may read. A machine names which of them it syncs; a
|
|
39
|
+
* replacement key is granted the same ones and overlaps the old key until that is removed.
|
|
40
|
+
* A key may be granted several credentials of one kind: several accounts of a provider.
|
|
41
|
+
*/
|
|
42
|
+
export const KeyInfo = Schema.Struct({
|
|
43
|
+
id: KeyId,
|
|
44
|
+
credentials: Schema.Array(Name),
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* A pending Claude sign-in: approve at `authorizeUrl`, then hand the code the callback page
|
|
48
|
+
* shows to `completeClaudeSignIn`. The code is useless without the verifier the service holds.
|
|
49
|
+
*/
|
|
50
|
+
export const ClaudeSignIn = Schema.Struct({
|
|
51
|
+
signInId: Schema.String,
|
|
52
|
+
credential: Name,
|
|
53
|
+
authorizeUrl: Schema.String,
|
|
54
|
+
expiresAt: Timestamp,
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* A Claude sign-in's code was not taken: it is not this sign-in's, or Anthropic refused it.
|
|
58
|
+
* The sign-in stays open until it expires, for the code again or a new one from its
|
|
59
|
+
* `authorizeUrl`. An approval that left out a scope the token needs ends it: start again.
|
|
60
|
+
*/
|
|
61
|
+
export class SignInRefused extends Schema.TaggedError()("SignInRefused", { message: Schema.String }, { httpApiStatus: 422 }) {
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A login's sign-in: open `verificationUrl` and approve, entering `userCode` where the
|
|
65
|
+
* provider asks for one. Cursor's link carries everything and has no code.
|
|
66
|
+
*/
|
|
67
|
+
export const SignInState = Schema.Struct({
|
|
68
|
+
signInId: Schema.String,
|
|
69
|
+
credential: Name,
|
|
70
|
+
kind: LoginKind,
|
|
71
|
+
status: Schema.Literals(["pending", "complete", "failed", "expired"]),
|
|
72
|
+
userCode: Schema.NullOr(Schema.String),
|
|
73
|
+
verificationUrl: Schema.String,
|
|
74
|
+
expiresAt: Timestamp,
|
|
75
|
+
message: Schema.optionalKey(Schema.String),
|
|
76
|
+
});
|
|
77
|
+
/** Credentials by name: rows outlive what they name, and stay findable. */
|
|
78
|
+
export const AuditEntry = Schema.Struct({
|
|
79
|
+
id: Schema.Int,
|
|
80
|
+
at: Timestamp,
|
|
81
|
+
actorKind: Schema.Literals(["machine", "admin", "system"]),
|
|
82
|
+
keyId: Schema.NullOr(Schema.String),
|
|
83
|
+
clientId: Schema.NullOr(Schema.String),
|
|
84
|
+
action: Schema.String,
|
|
85
|
+
credential: Schema.NullOr(Schema.String),
|
|
86
|
+
remoteAddr: Schema.NullOr(Schema.String),
|
|
87
|
+
detail: Schema.Record(Schema.String, Schema.Json),
|
|
88
|
+
});
|
|
89
|
+
/** Every filter is optional; the page size defaults on the service. */
|
|
90
|
+
export const AuditQuery = Schema.Struct({
|
|
91
|
+
limit: Schema.optionalKey(Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 500 }))),
|
|
92
|
+
before: Schema.optionalKey(Schema.Int),
|
|
93
|
+
action: Schema.optionalKey(Schema.String),
|
|
94
|
+
credential: Schema.optionalKey(Name),
|
|
95
|
+
keyId: Schema.optionalKey(Schema.String),
|
|
96
|
+
clientId: Schema.optionalKey(Schema.String),
|
|
97
|
+
});
|
|
98
|
+
/**
|
|
99
|
+
* A read whose code or link signs an account in: exported so the server's authorization
|
|
100
|
+
* rule can name it.
|
|
101
|
+
*/
|
|
102
|
+
export const GetSignIn = Action.make("getSignIn", {
|
|
103
|
+
readOnly: true,
|
|
104
|
+
caller: CurrentPrincipal,
|
|
105
|
+
description: "Read a sign-in. The service holds one in memory only, until the next sign-in starts after it expired; one it no longer holds is not found.",
|
|
106
|
+
input: Schema.Struct({ signInId: Schema.String }),
|
|
107
|
+
success: SignInState,
|
|
108
|
+
error: NotFound,
|
|
109
|
+
});
|
|
110
|
+
/** Everything an operator does; which scope each needs is the server's authorization rule. */
|
|
111
|
+
export const Admin = [
|
|
112
|
+
Action.make("listCredentials", {
|
|
113
|
+
readOnly: true,
|
|
114
|
+
caller: CurrentPrincipal,
|
|
115
|
+
description: "Every credential with its state: expiry, last refresh, last error and whether a login needs a new sign-in. Values are never returned.",
|
|
116
|
+
success: Schema.Struct({ credentials: Schema.Array(CredentialStatus) }),
|
|
117
|
+
}),
|
|
118
|
+
Action.make("putCredential", {
|
|
119
|
+
readOnly: false,
|
|
120
|
+
caller: CurrentPrincipal,
|
|
121
|
+
description: "Store a secret under a name, or replace its value and expiry: a `gh` or `claude` token a machine places, or a `token`, any other service's API key, which a service such as clankerusage reads. A `gh` token is for `host`, github.com unless given. A credential's kind and host never change; remove it and put it again instead. Keys granted it receive it on their next sync.",
|
|
122
|
+
input: Schema.Struct({
|
|
123
|
+
name: Name,
|
|
124
|
+
kind: SecretKind,
|
|
125
|
+
/** Surrounding whitespace, such as a pasted line ending, is not part of a secret: a value carrying any is refused, never altered. */
|
|
126
|
+
value: Schema.Trimmed.check(Schema.isMinLength(1), Schema.isMaxLength(16_384)),
|
|
127
|
+
host: Schema.optionalKey(Host),
|
|
128
|
+
/** Any date string JavaScript parses; a typo is refused, so alerting never loses it. Left out, it does not expire. */
|
|
129
|
+
expiresAt: Schema.optionalKey(Schema.DateFromString),
|
|
130
|
+
}).check(Schema.makeFilter(({ kind, host }) => kind === "gh" || host === undefined
|
|
131
|
+
? undefined
|
|
132
|
+
: { path: ["host"], issue: "Only a gh credential has a host." })),
|
|
133
|
+
success: SecretStatus,
|
|
134
|
+
error: Conflict,
|
|
135
|
+
}),
|
|
136
|
+
Action.make("removeCredential", {
|
|
137
|
+
readOnly: false,
|
|
138
|
+
caller: CurrentPrincipal,
|
|
139
|
+
description: "Delete a credential. Refused while a key is still granted it.",
|
|
140
|
+
input: Schema.Struct({ name: Name }),
|
|
141
|
+
success: Schema.Void,
|
|
142
|
+
error: [NotFound, Conflict],
|
|
143
|
+
}),
|
|
144
|
+
Action.make("startSignIn", {
|
|
145
|
+
readOnly: false,
|
|
146
|
+
caller: CurrentPrincipal,
|
|
147
|
+
description: "Start a sign-in for a ChatGPT, Grok or Cursor login; the credential is created, or its login replaced, when the sign-in completes. Open the returned verificationUrl and approve, entering userCode where one is given, then poll getSignIn.",
|
|
148
|
+
input: Schema.Struct({ name: Name, kind: LoginKind }),
|
|
149
|
+
success: SignInState,
|
|
150
|
+
error: [Conflict, UpstreamUnavailable],
|
|
151
|
+
}),
|
|
152
|
+
GetSignIn,
|
|
153
|
+
Action.make("refreshCredential", {
|
|
154
|
+
readOnly: false,
|
|
155
|
+
caller: CurrentPrincipal,
|
|
156
|
+
description: "Refresh one login now instead of waiting for its schedule.",
|
|
157
|
+
input: Schema.Struct({ name: Name }),
|
|
158
|
+
success: LoginStatus,
|
|
159
|
+
error: [NotFound, Conflict, NeedsSignIn, UpstreamUnavailable],
|
|
160
|
+
}),
|
|
161
|
+
Action.make("startClaudeSignIn", {
|
|
162
|
+
readOnly: false,
|
|
163
|
+
caller: CurrentPrincipal,
|
|
164
|
+
description: "Start a sign-in for a Claude credential: a token for both Claude Code and the usage endpoint, valid for a year. Approve at the returned authorizeUrl, then pass the code the callback page shows to completeClaudeSignIn within 15 minutes.",
|
|
165
|
+
input: Schema.Struct({ name: Name }),
|
|
166
|
+
success: ClaudeSignIn,
|
|
167
|
+
error: Conflict,
|
|
168
|
+
}),
|
|
169
|
+
Action.make("completeClaudeSignIn", {
|
|
170
|
+
readOnly: false,
|
|
171
|
+
caller: CurrentPrincipal,
|
|
172
|
+
description: "Exchange the code from an approved Claude sign-in for its token, stored under the sign-in's name: created, or its value and expiry replaced. Machines receive it on their next sync.",
|
|
173
|
+
input: Schema.Struct({
|
|
174
|
+
signInId: Schema.String,
|
|
175
|
+
/** As the callback page shows it: `<code>#<state>`. */
|
|
176
|
+
code: Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(4096)),
|
|
177
|
+
}),
|
|
178
|
+
success: SecretStatus,
|
|
179
|
+
error: [NotFound, Conflict, SignInRefused, UpstreamUnavailable],
|
|
180
|
+
}),
|
|
181
|
+
Action.make("listKeys", {
|
|
182
|
+
readOnly: true,
|
|
183
|
+
caller: CurrentPrincipal,
|
|
184
|
+
description: "Every machine key with the credentials it is granted.",
|
|
185
|
+
success: Schema.Struct({ keys: Schema.Array(KeyInfo) }),
|
|
186
|
+
}),
|
|
187
|
+
Action.make("putKey", {
|
|
188
|
+
readOnly: false,
|
|
189
|
+
caller: CurrentPrincipal,
|
|
190
|
+
description: "Grant a machine key, by its clankerauth id, exactly these credentials, replacing what it had; none takes every grant. A machine syncs the ones its configuration names; several of one kind may be granted. Revoke the key itself in clankerauth.",
|
|
191
|
+
input: Schema.Struct({ id: KeyId, credentials: Schema.Array(Name) }),
|
|
192
|
+
success: KeyInfo,
|
|
193
|
+
error: NotFound,
|
|
194
|
+
}),
|
|
195
|
+
Action.make("listAudit", {
|
|
196
|
+
readOnly: true,
|
|
197
|
+
caller: CurrentPrincipal,
|
|
198
|
+
description: "The audit log, newest first. Filter by action, credential, key or client id; page with `before`.",
|
|
199
|
+
input: AuditQuery,
|
|
200
|
+
success: Schema.Struct({ entries: Schema.Array(AuditEntry) }),
|
|
201
|
+
}),
|
|
202
|
+
];
|
|
203
|
+
/**
|
|
204
|
+
* An operator's bearer token on the private port: an access token or API key of the admin
|
|
205
|
+
* resource, verified by its own provider.
|
|
206
|
+
*/
|
|
207
|
+
export const AdminLogin = Authentication.make("clankercreds.admin", CurrentPrincipal, {
|
|
208
|
+
error: ProviderUnavailable,
|
|
209
|
+
});
|
|
210
|
+
export const AdminHttp = ActionHttp.make(Admin, {
|
|
211
|
+
prefix: apiPath,
|
|
212
|
+
authentication: AdminLogin,
|
|
213
|
+
});
|
|
214
|
+
export { Conflict, CredentialKind, LoginKind, Name, NeedsSignIn, NotFound, scopes, SecretKind, UpstreamUnavailable, };
|