| 1 | import * as Schema from "effect/Schema"; |
| 2 | |
| 3 | import { |
| 4 | EnvironmentId, |
| 5 | ForwardCompatibleArray, |
| 6 | ForwardCompatibleOptional, |
| 7 | IsoDateTime, |
| 8 | TrimmedNonEmptyString, |
| 9 | } from "./baseSchemas.ts"; |
| 10 | import { ProviderDriverKind, ProviderInstanceId } from "./providerInstance.ts"; |
| 11 | |
| 12 | export const ProviderSetupInput = Schema.Struct({ |
| 13 | instanceId: ProviderInstanceId, |
| 14 | }); |
| 15 | export type ProviderSetupInput = typeof ProviderSetupInput.Type; |
| 16 | |
| 17 | export 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 | }); |
| 24 | export type CodexAuthCallbackInput = typeof CodexAuthCallbackInput.Type; |
| 25 | export 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 | |
| 33 | const SetupOperationId = TrimmedNonEmptyString.check(Schema.isMaxLength(128)); |
| 34 | |
| 35 | export 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 | }); |
| 42 | export 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. |
| 46 | export 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 | ]); |
| 78 | export type ProviderAuthInteraction = typeof ProviderAuthInteraction.Type; |
| 79 | |
| 80 | export 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 | ]); |
| 102 | export type ProviderAuthResponse = typeof ProviderAuthResponse.Type; |
| 103 | |
| 104 | export 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 | }); |
| 110 | export type ProviderAuthStartInput = typeof ProviderAuthStartInput.Type; |
| 111 | |
| 112 | export const ProviderAuthRespondInput = Schema.Struct({ |
| 113 | instanceId: ProviderInstanceId, |
| 114 | flowId: SetupOperationId, |
| 115 | interactionId: SetupOperationId, |
| 116 | response: ProviderAuthResponse, |
| 117 | }); |
| 118 | export type ProviderAuthRespondInput = typeof ProviderAuthRespondInput.Type; |
| 119 | |
| 120 | export 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 | }); |
| 143 | export type ProviderAuthState = typeof ProviderAuthState.Type; |
| 144 | |
| 145 | export const ProviderAuthCompleteInput = Schema.Struct({ |
| 146 | instanceId: ProviderInstanceId, |
| 147 | flowId: SetupOperationId, |
| 148 | callbackUrl: TrimmedNonEmptyString.check(Schema.isMaxLength(16_384)), |
| 149 | }); |
| 150 | export type ProviderAuthCompleteInput = typeof ProviderAuthCompleteInput.Type; |
| 151 | |
| 152 | export const ProviderAuthCancelInput = Schema.Struct({ |
| 153 | instanceId: ProviderInstanceId, |
| 154 | flowId: SetupOperationId, |
| 155 | }); |
| 156 | export type ProviderAuthCancelInput = typeof ProviderAuthCancelInput.Type; |
| 157 | |
| 158 | const ByteCount = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)); |
| 159 | |
| 160 | export 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 | }); |
| 181 | export type ProviderInstallState = typeof ProviderInstallState.Type; |
| 182 | |
| 183 | export const ProviderInstallCancelInput = Schema.Struct({ |
| 184 | instanceId: ProviderInstanceId, |
| 185 | operationId: SetupOperationId, |
| 186 | }); |
| 187 | export type ProviderInstallCancelInput = typeof ProviderInstallCancelInput.Type; |
| 188 | |
| 189 | /** Safe setup failure text. Never include OAuth codes, URLs, or native token data. */ |
| 190 | export 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. |
| 205 | export 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 | }); |
| 218 | export type ChatGptReconnectProfile = typeof ChatGptReconnectProfile.Type; |
| 219 | export 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 | }); |
| 234 | export type ChatGptTransferredProfile = typeof ChatGptTransferredProfile.Type; |
| 235 | export const ChatGptReconnectProfileInput = Schema.Struct({ |
| 236 | instanceId: ProviderInstanceId, |
| 237 | methodId: Schema.String, |
| 238 | }); |
| 239 | export const ChatGptImportProfileInput = Schema.Struct({ |
| 240 | instanceId: ProviderInstanceId, |
| 241 | profile: ChatGptTransferredProfile, |
| 242 | }); |
| 243 | export 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 | }); |
| 250 | export type ChatGptHandoffInput = typeof ChatGptHandoffInput.Type; |
| 251 | export const ChatGptHandoffState = Schema.Union([ |
| 252 | Schema.Struct({ phase: Schema.Literal("auth"), state: ProviderAuthState }), |
| 253 | Schema.Struct({ phase: Schema.Literal("finished"), profile: ChatGptTransferredProfile }), |
| 254 | ]); |
| 255 | export type ChatGptHandoffState = typeof ChatGptHandoffState.Type; |