@fabrika/control-contract 0.2.0 → 0.3.2
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/package.json +5 -3
- package/src/builder-bootstrap.ts +71 -0
- package/src/builder-connections.ts +86 -0
- package/src/builder-pairing.ts +24 -0
- package/src/index.ts +33 -53
- package/src/rpc.ts +25 -3
- package/src/source-connection-list.ts +13 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fabrika/control-contract",
|
|
3
|
-
"version": "0.2
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"typecheck": "tsc --noEmit"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"
|
|
28
|
-
"@fabrika/
|
|
27
|
+
"zod": "^3.25.76",
|
|
28
|
+
"@fabrika/auth-core": "^0.3.2",
|
|
29
|
+
"@fabrika/app": "^0.3.2",
|
|
30
|
+
"@fabrika/provider-contract": "^0.3.2"
|
|
29
31
|
}
|
|
30
32
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const BUILDER_APPROVAL_STORAGE_KEY = 'fabrika.builder.approval.v1'
|
|
4
|
+
export const BUILDER_AUTHORIZE_PATH = '/builder/authorize'
|
|
5
|
+
|
|
6
|
+
export interface BuilderApprovalState {
|
|
7
|
+
readonly attemptId: string
|
|
8
|
+
readonly approvalVerifier: string
|
|
9
|
+
readonly expiresAt: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface BuilderInstallationDiscoveryDto {
|
|
13
|
+
readonly version: 1
|
|
14
|
+
readonly controlOrigin: string
|
|
15
|
+
readonly iamIssuer: string
|
|
16
|
+
readonly provider: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Self-contained so the public capture page can embed this exact validator without external assets. */
|
|
20
|
+
export function parseBuilderApprovalState(value: unknown, nowSeconds = Math.floor(Date.now() / 1000)): BuilderApprovalState | null {
|
|
21
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) return null
|
|
22
|
+
if (Object.keys(value).length !== 3 || Object.keys(value).some((key) => !['attemptId', 'approvalVerifier', 'expiresAt'].includes(key))) return null
|
|
23
|
+
const attemptId: unknown = Reflect.get(value, 'attemptId')
|
|
24
|
+
const approvalVerifier: unknown = Reflect.get(value, 'approvalVerifier')
|
|
25
|
+
const expiresAt: unknown = Reflect.get(value, 'expiresAt')
|
|
26
|
+
if (typeof attemptId !== 'string' || !/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(attemptId)) return null
|
|
27
|
+
if (typeof approvalVerifier !== 'string' || !/^[A-Za-z0-9_-]{43}$/.test(approvalVerifier)) return null
|
|
28
|
+
if (typeof expiresAt !== 'number' || !Number.isSafeInteger(expiresAt) || expiresAt <= nowSeconds || expiresAt > nowSeconds + 600) return null
|
|
29
|
+
return { attemptId, approvalVerifier, expiresAt }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Accept only a credential-free installation origin, never a redirect or a repository-selected path. */
|
|
33
|
+
export function parseBuilderPublicOrigin(value: unknown): string | null {
|
|
34
|
+
if (typeof value !== 'string' || value.length > 2048) return null
|
|
35
|
+
try {
|
|
36
|
+
const url = new URL(value)
|
|
37
|
+
const local = url.hostname === 'localhost' || url.hostname.endsWith('.localhost') || url.hostname === '[::1]'
|
|
38
|
+
|| /^127(?:\.[0-9]{1,3}){3}$/.test(url.hostname)
|
|
39
|
+
if (url.protocol !== 'https:' && !(url.protocol === 'http:' && local)) return null
|
|
40
|
+
if (url.username !== '' || url.password !== '' || url.pathname !== '/' || url.search !== '' || url.hash !== '') return null
|
|
41
|
+
return url.origin
|
|
42
|
+
} catch {
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function parseBuilderInstallationDiscovery(value: unknown): BuilderInstallationDiscoveryDto | null {
|
|
48
|
+
const result = builderInstallationDiscoverySchema.safeParse(value)
|
|
49
|
+
return result.success ? result.data : null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const builderPublicOriginSchema = z.string().transform((value, ctx) => {
|
|
53
|
+
const origin = parseBuilderPublicOrigin(value)
|
|
54
|
+
if (origin !== null) return origin
|
|
55
|
+
ctx.addIssue({ code: 'custom', message: 'invalid builder public origin' })
|
|
56
|
+
return z.NEVER
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
export const builderInstallationDiscoverySchema = z.preprocess(
|
|
60
|
+
(value) => {
|
|
61
|
+
// Discovery requires four own fields, even when a prototype supplies otherwise valid values.
|
|
62
|
+
if (typeof value === 'object' && value !== null && Object.keys(value).length !== 4) return undefined
|
|
63
|
+
return value
|
|
64
|
+
},
|
|
65
|
+
z.object({
|
|
66
|
+
version: z.literal(1),
|
|
67
|
+
controlOrigin: builderPublicOriginSchema,
|
|
68
|
+
iamIssuer: builderPublicOriginSchema,
|
|
69
|
+
provider: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/),
|
|
70
|
+
}).strict(),
|
|
71
|
+
)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import type { AppDto } from './index.js'
|
|
3
|
+
|
|
4
|
+
export const BUILDER_CONNECTION_ATTEMPT_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/
|
|
5
|
+
export const BUILDER_APPROVAL_VERIFIER = /^[A-Za-z0-9_-]{43}$/
|
|
6
|
+
export const BUILDER_CONNECTION_IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/
|
|
7
|
+
|
|
8
|
+
export interface BuilderConnectionEnvironmentDto {
|
|
9
|
+
readonly env: string
|
|
10
|
+
readonly namespaceId: string | null
|
|
11
|
+
readonly domain: string | null
|
|
12
|
+
readonly publicOrigin: string | null
|
|
13
|
+
readonly triggerRef: string | null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface BuilderConnectionReceiptDto {
|
|
17
|
+
readonly version: 1
|
|
18
|
+
readonly attemptId: string
|
|
19
|
+
readonly provider: string
|
|
20
|
+
readonly app: AppDto
|
|
21
|
+
readonly environment: BuilderConnectionEnvironmentDto
|
|
22
|
+
readonly createdAt: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface CreateBuilderConnectionReceiptRequest {
|
|
26
|
+
readonly attemptId: string
|
|
27
|
+
readonly approvalVerifier: string
|
|
28
|
+
readonly appId: string
|
|
29
|
+
readonly environment: BuilderConnectionEnvironmentDto
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface GetBuilderConnectionReceiptRequest {
|
|
33
|
+
readonly attemptId: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Decode the persisted/wire snapshot without accepting provider envelopes or credential fields. */
|
|
37
|
+
export function parseBuilderConnectionReceipt(value: unknown): BuilderConnectionReceiptDto {
|
|
38
|
+
const result = builderConnectionReceiptSchema.safeParse(value)
|
|
39
|
+
if (result.success) return result.data
|
|
40
|
+
const issues = result.error.issues
|
|
41
|
+
const objectIssue = (path: string) =>
|
|
42
|
+
issues.some((issue) => issue.path.join('.') === path && (issue.code === 'invalid_type' || issue.code === 'unrecognized_keys'))
|
|
43
|
+
if (objectIssue('')) throw new Error('invalid builder connection receipt object')
|
|
44
|
+
if (issues.some((issue) => issue.path[0] === 'version')) throw new Error('unsupported builder connection receipt version')
|
|
45
|
+
if (objectIssue('app') || objectIssue('environment')) throw new Error('invalid builder connection receipt object')
|
|
46
|
+
if (issues.some((issue) => issue.path.join('.') === 'app.githubInstallationId')) throw new Error('invalid githubInstallationId')
|
|
47
|
+
const issue = issues[0]
|
|
48
|
+
throw new Error(`invalid receipt ${issue?.path.at(-1) ?? 'object'}`)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const receiptTimestampSchema = z.number().int().safe().nonnegative()
|
|
52
|
+
const receiptIdentifierSchema = z.string().regex(BUILDER_CONNECTION_IDENTIFIER)
|
|
53
|
+
|
|
54
|
+
export const builderConnectionAppSchema = z.object({
|
|
55
|
+
id: receiptIdentifierSchema,
|
|
56
|
+
repoUrl: z.string(),
|
|
57
|
+
defaultBranch: z.string(),
|
|
58
|
+
workerDir: z.string().nullable(),
|
|
59
|
+
buildCmd: z.string().nullable(),
|
|
60
|
+
configPath: z.string().nullable(),
|
|
61
|
+
githubConnectionId: z.string().nullable().optional(),
|
|
62
|
+
githubInstallationId: receiptTimestampSchema.nullable(),
|
|
63
|
+
createdAt: receiptTimestampSchema,
|
|
64
|
+
}).strict().transform(({ githubConnectionId, githubInstallationId, createdAt, ...app }) => ({
|
|
65
|
+
...app,
|
|
66
|
+
...(githubConnectionId === undefined ? {} : { githubConnectionId }),
|
|
67
|
+
githubInstallationId,
|
|
68
|
+
createdAt,
|
|
69
|
+
}))
|
|
70
|
+
|
|
71
|
+
export const builderConnectionEnvironmentSchema = z.object({
|
|
72
|
+
env: receiptIdentifierSchema,
|
|
73
|
+
namespaceId: z.string().min(1).max(128).nullable(),
|
|
74
|
+
domain: z.string().min(1).max(253).nullable(),
|
|
75
|
+
publicOrigin: z.string().min(1).max(2048).nullable(),
|
|
76
|
+
triggerRef: z.string().min(1).max(1024).nullable(),
|
|
77
|
+
}).strict()
|
|
78
|
+
|
|
79
|
+
export const builderConnectionReceiptSchema = z.object({
|
|
80
|
+
version: z.literal(1),
|
|
81
|
+
attemptId: z.string().regex(BUILDER_CONNECTION_ATTEMPT_ID),
|
|
82
|
+
provider: receiptIdentifierSchema,
|
|
83
|
+
app: builderConnectionAppSchema,
|
|
84
|
+
environment: builderConnectionEnvironmentSchema,
|
|
85
|
+
createdAt: receiptTimestampSchema,
|
|
86
|
+
}).strict()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
ApproveDeviceAuthorizationResult,
|
|
3
|
+
DenyDeviceAuthorizationResult,
|
|
4
|
+
DeviceAuthorizationFailure,
|
|
5
|
+
DeviceAuthorizationReceipt,
|
|
6
|
+
DeviceAuthorizationRequest,
|
|
7
|
+
InspectDeviceAuthorizationResult,
|
|
8
|
+
ListOwnDeviceAuthorizationsResult,
|
|
9
|
+
RevokeKeyResult,
|
|
10
|
+
} from '@fabrika/auth-core'
|
|
11
|
+
|
|
12
|
+
export interface BuilderPairingProofRequest {
|
|
13
|
+
readonly attemptId: string
|
|
14
|
+
readonly approvalVerifier: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ListOwnBuilderPairingsRequest {
|
|
18
|
+
readonly before?: string
|
|
19
|
+
readonly limit?: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface RevokeBuilderPairingRequest {
|
|
23
|
+
readonly credentialId: string
|
|
24
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { JsonValue, ProviderEnvelope } from '@fabrika/provider-contract'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
export * from './builder-bootstrap.js'
|
|
5
|
+
export * from './builder-connections.js'
|
|
6
|
+
export * from './builder-pairing.js'
|
|
2
7
|
|
|
3
8
|
export type { JsonValue }
|
|
4
9
|
|
|
@@ -15,6 +20,12 @@ export interface CursorList<T> {
|
|
|
15
20
|
|
|
16
21
|
export type ProviderEnvelopeDto = ProviderEnvelope
|
|
17
22
|
|
|
23
|
+
/** What the console needs to know about the installation it runs in. */
|
|
24
|
+
export interface InstallationDto {
|
|
25
|
+
/** The id of the one provider bundle this installation composes (ADR-0011). */
|
|
26
|
+
readonly provider: string
|
|
27
|
+
}
|
|
28
|
+
|
|
18
29
|
export interface GitHubSourceRepositoryDto {
|
|
19
30
|
readonly owner: string
|
|
20
31
|
readonly name: string
|
|
@@ -79,9 +90,7 @@ export interface GitHubSourceConnectionConnectedDto extends GitHubSourceConnecti
|
|
|
79
90
|
|
|
80
91
|
export type GitHubSourceConnectionWorkflowDto = Exclude<GitHubSourceConnectionStatusDto, GitHubSourceConnectionConnectedDto>
|
|
81
92
|
|
|
82
|
-
export
|
|
83
|
-
export const GITHUB_SOURCE_CONNECTION_MAX_PAGE_SIZE = 100
|
|
84
|
-
export const GITHUB_SOURCE_CONNECTION_PAGE_CURSOR_MAX_LENGTH = 512
|
|
93
|
+
export * from './source-connection-list.js'
|
|
85
94
|
|
|
86
95
|
export interface GitHubSourceConnectionListInput {
|
|
87
96
|
readonly cursor?: string
|
|
@@ -95,32 +104,6 @@ export interface GitHubSourceConnectionListResponse {
|
|
|
95
104
|
readonly workflow: GitHubSourceConnectionWorkflowDto | null
|
|
96
105
|
}
|
|
97
106
|
|
|
98
|
-
export function decodeGitHubSourceConnectionListInput(value: unknown): GitHubSourceConnectionListInput {
|
|
99
|
-
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
100
|
-
throw new Error('source connection list input must be an object')
|
|
101
|
-
}
|
|
102
|
-
const unknownField = Object.keys(value).find((key) => key !== 'cursor' && key !== 'limit')
|
|
103
|
-
if (unknownField !== undefined) throw new Error('source connection list input contains an unknown field')
|
|
104
|
-
const cursorValue = Reflect.get(value, 'cursor')
|
|
105
|
-
if (
|
|
106
|
-
cursorValue !== undefined
|
|
107
|
-
&& (typeof cursorValue !== 'string' || cursorValue.length === 0 || cursorValue.length > GITHUB_SOURCE_CONNECTION_PAGE_CURSOR_MAX_LENGTH)
|
|
108
|
-
) {
|
|
109
|
-
throw new Error(`source connection list cursor must be a non-empty string of at most ${GITHUB_SOURCE_CONNECTION_PAGE_CURSOR_MAX_LENGTH} characters`)
|
|
110
|
-
}
|
|
111
|
-
const limitValue = Reflect.get(value, 'limit')
|
|
112
|
-
if (
|
|
113
|
-
limitValue !== undefined
|
|
114
|
-
&& (typeof limitValue !== 'number' || !Number.isSafeInteger(limitValue) || limitValue < 1 || limitValue > GITHUB_SOURCE_CONNECTION_MAX_PAGE_SIZE)
|
|
115
|
-
) {
|
|
116
|
-
throw new Error(`source connection list limit must be an integer from 1 to ${GITHUB_SOURCE_CONNECTION_MAX_PAGE_SIZE}`)
|
|
117
|
-
}
|
|
118
|
-
return {
|
|
119
|
-
...(cursorValue !== undefined ? { cursor: cursorValue } : {}),
|
|
120
|
-
...(limitValue !== undefined ? { limit: limitValue } : {}),
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
107
|
export interface GitHubSourceConnectionRepairRequiredDto extends GitHubSourceConnectionBaseDto {
|
|
125
108
|
readonly state: 'repair-required'
|
|
126
109
|
readonly connectionId: string
|
|
@@ -150,12 +133,14 @@ export interface StartGitHubSourceConnectionResponse {
|
|
|
150
133
|
readonly continuePath: string
|
|
151
134
|
}
|
|
152
135
|
|
|
153
|
-
/** One redacted line of deploy output
|
|
154
|
-
export
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
136
|
+
/** One redacted line of deploy output: the runner's NDJSON log line and the control API's view of it. */
|
|
137
|
+
export const runLogLineSchema = z.object({
|
|
138
|
+
ts: z.number().finite(),
|
|
139
|
+
stream: z.enum(['stdout', 'stderr', 'meta']),
|
|
140
|
+
text: z.string(),
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
export type RunLogLine = z.infer<typeof runLogLineSchema>
|
|
159
144
|
|
|
160
145
|
/** Backward-compatible short name for browser consumers. */
|
|
161
146
|
export type LogLine = RunLogLine
|
|
@@ -182,7 +167,9 @@ export interface AppOptionalFields {
|
|
|
182
167
|
workerDir?: string | null
|
|
183
168
|
buildCmd?: string | null
|
|
184
169
|
configPath?: string | null
|
|
170
|
+
/** Must equal the installation of the repository owner's GitHub connection. */
|
|
185
171
|
githubInstallationId?: number | null
|
|
172
|
+
/** Bind the app to the repository owner's GitHub connection (one per owner). */
|
|
186
173
|
resolveInstallationId?: boolean
|
|
187
174
|
}
|
|
188
175
|
|
|
@@ -212,22 +199,6 @@ export interface PutAppEnvRequest {
|
|
|
212
199
|
artifact: ProviderEnvelopeDto
|
|
213
200
|
}
|
|
214
201
|
|
|
215
|
-
export interface CloudflareTargetEnvelope extends ProviderEnvelopeDto {
|
|
216
|
-
provider: 'cloudflare'
|
|
217
|
-
version: 1
|
|
218
|
-
payload: {
|
|
219
|
-
stateNamespace?: string
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export interface CloudflareArtifactEnvelope extends ProviderEnvelopeDto {
|
|
224
|
-
provider: 'cloudflare'
|
|
225
|
-
version: 1
|
|
226
|
-
payload: {
|
|
227
|
-
configPath: string
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
202
|
export type DeploymentNamespaceState = 'pending' | 'provisioning' | 'ready' | 'failed'
|
|
232
203
|
|
|
233
204
|
export interface DeploymentNamespaceDto {
|
|
@@ -341,17 +312,26 @@ export interface PlanDeploymentNamespaceResponse {
|
|
|
341
312
|
readonly presentation: DeploymentNamespacePresentationDto
|
|
342
313
|
}
|
|
343
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Where an app secret's value is kept: `vault` (control's vault), `provider` (the provider's own storage),
|
|
317
|
+
* or `legacy` — a row from before secrets were vault-only, which no deploy resolves until it is set again.
|
|
318
|
+
*/
|
|
319
|
+
export type AppSecretStorage = 'vault' | 'provider' | 'legacy'
|
|
320
|
+
|
|
344
321
|
export interface AppSecretDto {
|
|
345
322
|
appId: string
|
|
346
323
|
env: string | null
|
|
347
324
|
name: string
|
|
348
|
-
|
|
325
|
+
storage: AppSecretStorage
|
|
326
|
+
/** The `vault:` ref; null for any other storage, whose stored ref control cannot vouch for. */
|
|
327
|
+
valueRef: string | null
|
|
349
328
|
createdAt: number
|
|
350
329
|
}
|
|
351
330
|
|
|
331
|
+
/** The plaintext is write-only: control seals it and stores only the resulting ref. */
|
|
352
332
|
export interface PutAppSecretRequest {
|
|
353
333
|
name: string
|
|
354
|
-
|
|
334
|
+
value: string
|
|
355
335
|
env?: string | null
|
|
356
336
|
}
|
|
357
337
|
|
package/src/rpc.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import type { RpcProcedureContract } from '@fabrika/app'
|
|
2
|
+
import type { BuilderConnectionReceiptDto, CreateBuilderConnectionReceiptRequest, GetBuilderConnectionReceiptRequest } from './builder-connections.js'
|
|
3
|
+
import type {
|
|
4
|
+
ApproveDeviceAuthorizationResult,
|
|
5
|
+
BuilderPairingProofRequest,
|
|
6
|
+
DenyDeviceAuthorizationResult,
|
|
7
|
+
InspectDeviceAuthorizationResult,
|
|
8
|
+
ListOwnBuilderPairingsRequest,
|
|
9
|
+
ListOwnDeviceAuthorizationsResult,
|
|
10
|
+
RevokeBuilderPairingRequest,
|
|
11
|
+
RevokeKeyResult,
|
|
12
|
+
} from './builder-pairing.js'
|
|
2
13
|
import type {
|
|
3
14
|
AdoptDeploymentNamespaceRequest,
|
|
4
15
|
AppDto,
|
|
@@ -12,6 +23,7 @@ import type {
|
|
|
12
23
|
GitHubSourceConnectionListInput,
|
|
13
24
|
GitHubSourceConnectionListResponse,
|
|
14
25
|
GitHubSourceConnectionStatusDto,
|
|
26
|
+
InstallationDto,
|
|
15
27
|
ListResponse,
|
|
16
28
|
PlanDeploymentNamespaceRequest,
|
|
17
29
|
PlanDeploymentNamespaceResponse,
|
|
@@ -24,7 +36,6 @@ import type {
|
|
|
24
36
|
RunDto,
|
|
25
37
|
RunLogResponse,
|
|
26
38
|
RunTailResponse,
|
|
27
|
-
SetSecretValueRequest,
|
|
28
39
|
StartGitHubSourceConnectionRequest,
|
|
29
40
|
StartGitHubSourceConnectionResponse,
|
|
30
41
|
TriggerDeployRequest,
|
|
@@ -105,7 +116,6 @@ export interface GitHubSourceConnectionInput {
|
|
|
105
116
|
|
|
106
117
|
/** Provider-neutral admin seam. A statically composed provider implements these GitHub operations. */
|
|
107
118
|
export interface GitHubSourceConnectionRpcContract {
|
|
108
|
-
status: RpcProcedure<void, GitHubSourceConnectionStatusDto>
|
|
109
119
|
list: RpcProcedure<GitHubSourceConnectionListInput, GitHubSourceConnectionListResponse>
|
|
110
120
|
start: RpcProcedure<StartGitHubSourceConnectionRequest, StartGitHubSourceConnectionResponse>
|
|
111
121
|
verifyInstallation: RpcProcedure<GitHubSourceConnectionInput, GitHubSourceConnectionStatusDto>
|
|
@@ -113,8 +123,20 @@ export interface GitHubSourceConnectionRpcContract {
|
|
|
113
123
|
reconcile: RpcProcedure<GitHubSourceConnectionInput, GitHubSourceConnectionStatusDto>
|
|
114
124
|
}
|
|
115
125
|
|
|
116
|
-
/** Portable Delivery control API
|
|
126
|
+
/** Portable Delivery control API shared by browser and machine callers. */
|
|
117
127
|
export interface ControlRpcContract {
|
|
128
|
+
installation: RpcProcedure<void, InstallationDto>
|
|
129
|
+
builderPairing: {
|
|
130
|
+
inspect: RpcProcedure<BuilderPairingProofRequest, InspectDeviceAuthorizationResult>
|
|
131
|
+
approve: RpcProcedure<BuilderPairingProofRequest, ApproveDeviceAuthorizationResult>
|
|
132
|
+
deny: RpcProcedure<BuilderPairingProofRequest, DenyDeviceAuthorizationResult>
|
|
133
|
+
listOwn: RpcProcedure<ListOwnBuilderPairingsRequest, ListOwnDeviceAuthorizationsResult>
|
|
134
|
+
revoke: RpcProcedure<RevokeBuilderPairingRequest, RevokeKeyResult>
|
|
135
|
+
}
|
|
136
|
+
builderConnections: {
|
|
137
|
+
createReceipt: RpcProcedure<CreateBuilderConnectionReceiptRequest, BuilderConnectionReceiptDto>
|
|
138
|
+
getReceipt: RpcProcedure<GetBuilderConnectionReceiptRequest, BuilderConnectionReceiptDto>
|
|
139
|
+
}
|
|
118
140
|
sourceConnection: GitHubSourceConnectionRpcContract
|
|
119
141
|
apps: {
|
|
120
142
|
list: RpcProcedure<void, ListResponse<AppDto>>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const GITHUB_SOURCE_CONNECTION_DEFAULT_PAGE_SIZE = 50
|
|
4
|
+
export const GITHUB_SOURCE_CONNECTION_MAX_PAGE_SIZE = 100
|
|
5
|
+
export const GITHUB_SOURCE_CONNECTION_PAGE_CURSOR_MAX_LENGTH = 512
|
|
6
|
+
|
|
7
|
+
export const githubSourceConnectionListInputSchema = z.object({
|
|
8
|
+
cursor: z.string().min(1).max(GITHUB_SOURCE_CONNECTION_PAGE_CURSOR_MAX_LENGTH).optional(),
|
|
9
|
+
limit: z.number().int().safe().min(1).max(GITHUB_SOURCE_CONNECTION_MAX_PAGE_SIZE).optional(),
|
|
10
|
+
}).strict().transform(({ cursor, limit }) => ({
|
|
11
|
+
...(cursor === undefined ? {} : { cursor }),
|
|
12
|
+
...(limit === undefined ? {} : { limit }),
|
|
13
|
+
}))
|