1import * as Schema from "effect/Schema";
2
3import {
4 EnvironmentId,
5 ForwardCompatibleArray,
6 ForwardCompatibleOptional,
7 IsoDateTime,
8 TrimmedNonEmptyString,
9} from "./baseSchemas.ts";
10import { ProviderDriverKind, ProviderInstanceId } from "./providerInstance.ts";
11
12export const ProviderSetupInput = Schema.Struct({
13 instanceId: ProviderInstanceId,
14});
15export type ProviderSetupInput = typeof ProviderSetupInput.Type;
16
17export const CodexAuthCallbackInput = Schema.Struct({
18 authorizationUrl: Schema.String.check(Schema.isMaxLength(16_384)),
19 returnUrl: Schema.String.check(Schema.isMaxLength(4_096)),
20 environmentId: EnvironmentId,
21 instanceId: ProviderInstanceId,
22 flowId: TrimmedNonEmptyString.check(Schema.isMaxLength(128)),
23});
24export type CodexAuthCallbackInput = typeof CodexAuthCallbackInput.Type;
25export const CodexAuthCallbackState = Schema.Union([
26 Schema.Struct({ phase: Schema.Literal("ready") }),
27 Schema.Struct({
28 phase: Schema.Literal("finished"),
29 callbackUrl: Schema.String.check(Schema.isMaxLength(16_384)),
30 }),
31]);
32
33const SetupOperationId = TrimmedNonEmptyString.check(Schema.isMaxLength(128));
34
35export const ProviderAuthMethod = Schema.Struct({
36 id: SetupOperationId,
37 accountEmail: Schema.optional(TrimmedNonEmptyString),
38 name: TrimmedNonEmptyString,
39 description: Schema.NullOr(Schema.String),
40 type: Schema.Literals(["agent", "terminal", "credentials"]),
41});
42export type ProviderAuthMethod = typeof ProviderAuthMethod.Type;
43
44// These describe client interactions, not OAuth grant types. The provider
45// adapter remains responsible for credentials, callbacks, and refresh.
46export const ProviderAuthInteraction = Schema.Union([
47 Schema.Struct({
48 type: Schema.Literal("browser"),
49 id: SetupOperationId,
50 url: TrimmedNonEmptyString.check(Schema.isMaxLength(16_384)),
51 requiresConsent: Schema.Boolean,
52 acceptsCallback: Schema.optionalKey(Schema.Boolean),
53 }),
54 Schema.Struct({
55 type: Schema.Literal("deviceCode"),
56 id: SetupOperationId,
57 url: TrimmedNonEmptyString.check(Schema.isMaxLength(16_384)),
58 userCode: TrimmedNonEmptyString.check(Schema.isMaxLength(256)),
59 }),
60 Schema.Struct({
61 type: Schema.Literal("terminal"),
62 id: SetupOperationId,
63 output: Schema.String.check(Schema.isMaxLength(16_384)),
64 outputOffset: Schema.optionalKey(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))),
65 }),
66 Schema.Struct({
67 type: Schema.Literal("credentials"),
68 id: SetupOperationId,
69 fields: Schema.Array(
70 Schema.Struct({
71 name: SetupOperationId,
72 label: TrimmedNonEmptyString,
73 secret: Schema.Boolean,
74 }),
75 ).check(Schema.isMaxLength(16)),
76 }),
77]);
78export type ProviderAuthInteraction = typeof ProviderAuthInteraction.Type;
79
80export const ProviderAuthResponse = Schema.Union([
81 Schema.Struct({
82 type: Schema.Literal("browser"),
83 action: Schema.Literals(["accept", "decline"]),
84 }),
85 Schema.Struct({
86 type: Schema.Literal("terminal"),
87 data: Schema.String.check(Schema.isMaxLength(4_096)),
88 size: Schema.optionalKey(
89 Schema.Struct({
90 cols: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 500 })),
91 rows: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 200 })),
92 }),
93 ),
94 }),
95 Schema.Struct({
96 type: Schema.Literal("credentials"),
97 values: Schema.Record(SetupOperationId, Schema.String.check(Schema.isMaxLength(16_384))).check(
98 Schema.isMaxProperties(16),
99 ),
100 }),
101]);
102export type ProviderAuthResponse = typeof ProviderAuthResponse.Type;
103
104export const ProviderAuthStartInput = Schema.Struct({
105 instanceId: ProviderInstanceId,
106 methodId: Schema.optionalKey(SetupOperationId),
107 returnUrl: Schema.optionalKey(Schema.String),
108 callbackMode: Schema.optionalKey(Schema.Literals(["server", "client"])),
109});
110export type ProviderAuthStartInput = typeof ProviderAuthStartInput.Type;
111
112export const ProviderAuthRespondInput = Schema.Struct({
113 instanceId: ProviderInstanceId,
114 flowId: SetupOperationId,
115 interactionId: SetupOperationId,
116 response: ProviderAuthResponse,
117});
118export type ProviderAuthRespondInput = typeof ProviderAuthRespondInput.Type;
119
120export const ProviderAuthState = Schema.Struct({
121 instanceId: ProviderInstanceId,
122 phase: Schema.Literals([
123 "idle",
124 "starting",
125 "waiting",
126 "verifying",
127 "succeeded",
128 "failed",
129 "cancelled",
130 ]),
131 flowId: Schema.NullOr(SetupOperationId),
132 authorizationUrl: Schema.NullOr(Schema.String),
133 expiresAt: Schema.NullOr(IsoDateTime),
134 message: Schema.NullOr(Schema.String),
135 // Newer servers may add method types, interactions, or owners; older
136 // clients drop what they cannot decode instead of rejecting the state.
137 methods: Schema.optionalKey(
138 ForwardCompatibleArray(ProviderAuthMethod).check(Schema.isMaxLength(32)),
139 ),
140 interaction: ForwardCompatibleOptional(Schema.NullOr(ProviderAuthInteraction)),
141 credentialOwner: ForwardCompatibleOptional(Schema.Literals(["provider", "t3"])),
142});
143export type ProviderAuthState = typeof ProviderAuthState.Type;
144
145export const ProviderAuthCompleteInput = Schema.Struct({
146 instanceId: ProviderInstanceId,
147 flowId: SetupOperationId,
148 callbackUrl: TrimmedNonEmptyString.check(Schema.isMaxLength(16_384)),
149});
150export type ProviderAuthCompleteInput = typeof ProviderAuthCompleteInput.Type;
151
152export const ProviderAuthCancelInput = Schema.Struct({
153 instanceId: ProviderInstanceId,
154 flowId: SetupOperationId,
155});
156export type ProviderAuthCancelInput = typeof ProviderAuthCancelInput.Type;
157
158const ByteCount = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
159
160export const ProviderInstallState = Schema.Struct({
161 driver: ProviderDriverKind,
162 operationId: Schema.NullOr(SetupOperationId),
163 phase: Schema.Literals([
164 "idle",
165 "downloading",
166 "extracting",
167 "verifying",
168 "succeeded",
169 "failed",
170 "cancelled",
171 ]),
172 downloadedBytes: ByteCount,
173 totalBytes: Schema.NullOr(ByteCount),
174 version: Schema.NullOr(TrimmedNonEmptyString),
175 installedVersion: Schema.NullOr(TrimmedNonEmptyString),
176 executablePath: Schema.optionalKey(Schema.NullOr(TrimmedNonEmptyString)),
177 source: Schema.optionalKey(Schema.NullOr(Schema.Literals(["managed", "local"]))),
178 canRemove: Schema.Boolean,
179 message: Schema.NullOr(Schema.String),
180});
181export type ProviderInstallState = typeof ProviderInstallState.Type;
182
183export const ProviderInstallCancelInput = Schema.Struct({
184 instanceId: ProviderInstanceId,
185 operationId: SetupOperationId,
186});
187export type ProviderInstallCancelInput = typeof ProviderInstallCancelInput.Type;
188
189/** Safe setup failure text. Never include OAuth codes, URLs, or native token data. */
190export class ProviderSetupError extends Schema.TaggedError<ProviderSetupError>()(
191 "ProviderSetupError",
192 {
193 instanceId: ProviderInstanceId,
194 operation: Schema.String,
195 detail: Schema.String,
196 cause: Schema.optional(Schema.Defect()),
197 },
198) {
199 override get message(): string {
200 return this.detail;
201 }
202}
203
204// A selected registration is reused on the primary without copying refresh ownership.
205export const ChatGptReconnectProfile = Schema.Struct({
206 clientId: Schema.String.check(Schema.isPattern(/^oaiapp_[\w-]+$/u)),
207 subject: Schema.optionalKey(Schema.String),
208 email: Schema.optionalKey(Schema.NullOr(Schema.String)),
209 redirectUri: Schema.optionalKey(
210 Schema.String.check(
211 Schema.isPattern(/^http:\/\/(?:127\.0\.0\.1|localhost):[1-9]\d{0,4}\/auth\/callback$/u),
212 ),
213 ),
214 connectionLabel: Schema.optionalKey(Schema.String),
215 sharingEnabled: Schema.optionalKey(Schema.Boolean),
216 idTokenHint: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(16_384))),
217});
218export type ChatGptReconnectProfile = typeof ChatGptReconnectProfile.Type;
219export const ChatGptTransferredProfile = Schema.Struct({
220 registration: ChatGptReconnectProfile,
221 credentials: Schema.Struct({
222 clientId: Schema.String,
223 accessToken: Schema.NonEmptyString.check(Schema.isMaxLength(16_384)),
224 refreshToken: Schema.NullOr(Schema.String.check(Schema.isMaxLength(16_384))),
225 idToken: Schema.NonEmptyString.check(Schema.isMaxLength(16_384)),
226 issuer: Schema.String,
227 expiresAt: Schema.Finite,
228 earliestRefreshAt: Schema.NullOr(Schema.Finite),
229 scopes: Schema.Array(Schema.String),
230 subject: Schema.String,
231 email: Schema.NullOr(Schema.String),
232 }),
233});
234export type ChatGptTransferredProfile = typeof ChatGptTransferredProfile.Type;
235export const ChatGptReconnectProfileInput = Schema.Struct({
236 instanceId: ProviderInstanceId,
237 methodId: Schema.String,
238});
239export const ChatGptImportProfileInput = Schema.Struct({
240 instanceId: ProviderInstanceId,
241 profile: ChatGptTransferredProfile,
242});
243export const ChatGptHandoffInput = Schema.Struct({
244 instanceId: ProviderInstanceId,
245 environmentId: EnvironmentId,
246 attemptId: Schema.String.check(Schema.isMaxLength(128)),
247 returnUrl: Schema.String.check(Schema.isMaxLength(4_096)),
248 profile: Schema.NullOr(ChatGptReconnectProfile),
249});
250export type ChatGptHandoffInput = typeof ChatGptHandoffInput.Type;
251export const ChatGptHandoffState = Schema.Union([
252 Schema.Struct({ phase: Schema.Literal("auth"), state: ProviderAuthState }),
253 Schema.Struct({ phase: Schema.Literal("finished"), profile: ChatGptTransferredProfile }),
254]);
255export type ChatGptHandoffState = typeof ChatGptHandoffState.Type;