@@ -10,6 +10,8 @@ import (
1010
1111const MaxAccountCredentialImportItems = 200
1212
13+ const codexManagerOpenAIIssuer = "https://auth.openai.com"
14+
1315type AccountCredentialImportKind string
1416
1517const (
@@ -218,6 +220,10 @@ func accountCredentialImportSourcesFromValue(value any) ([]AccountCredentialImpo
218220}
219221
220222func accountCredentialImportSourceFromMap (item map [string ]any ) (AccountCredentialImportSource , error ) {
223+ if source , handled , err := accountCredentialImportSourceFromCodexManagerExport (item ); handled || err != nil {
224+ return source , err
225+ }
226+
221227 if field , found := findDisallowedCredentialImportField (item ); found {
222228 return AccountCredentialImportSource {}, fmt .Errorf ("disallowed credential field: %s" , field )
223229 }
@@ -342,6 +348,69 @@ func accountCredentialImportSourceFromMap(item map[string]any) (AccountCredentia
342348 return AccountCredentialImportSource {}, fmt .Errorf ("unsupported credential import item" )
343349}
344350
351+ func accountCredentialImportSourceFromCodexManagerExport (item map [string ]any ) (AccountCredentialImportSource , bool , error ) {
352+ tokens := importMapField (item , "tokens" )
353+ meta := importMapField (item , "meta" )
354+ if len (tokens ) == 0 || len (meta ) == 0 {
355+ return AccountCredentialImportSource {}, false , nil
356+ }
357+
358+ topLevel := copyImportMap (item )
359+ removeImportMapField (topLevel , "tokens" )
360+ removeImportMapField (topLevel , "meta" )
361+ if field , found := findDisallowedCredentialImportField (topLevel ); found {
362+ return AccountCredentialImportSource {}, true , fmt .Errorf ("disallowed credential field: %s" , field )
363+ }
364+ if field , found := findDisallowedCredentialImportField (tokens ); found {
365+ return AccountCredentialImportSource {}, true , fmt .Errorf ("disallowed credential field: %s" , field )
366+ }
367+ metaForSafety := copyImportMap (meta )
368+ removeImportMapField (metaForSafety , "issuer" )
369+ if field , found := findDisallowedCredentialImportField (metaForSafety ); found {
370+ return AccountCredentialImportSource {}, true , fmt .Errorf ("disallowed credential field: %s" , field )
371+ }
372+
373+ issuer := strings .TrimRight (strings .TrimSpace (importStringField (meta , "issuer" )), "/" )
374+ if issuer != "" && ! strings .EqualFold (issuer , codexManagerOpenAIIssuer ) {
375+ return AccountCredentialImportSource {}, true , fmt .Errorf ("unsupported Codex-Manager issuer: %s" , issuer )
376+ }
377+
378+ accessToken := importStringField (tokens , "access_token" , "accessToken" )
379+ if accessToken == "" {
380+ return AccountCredentialImportSource {}, true , fmt .Errorf ("OAuth credentials must include access_token" )
381+ }
382+
383+ credentials := map [string ]any {
384+ "access_token" : accessToken ,
385+ }
386+ if idToken := importStringField (tokens , "id_token" , "idToken" ); idToken != "" {
387+ credentials ["id_token" ] = idToken
388+ }
389+ if refreshToken := importStringField (tokens , "refresh_token" , "refreshToken" ); refreshToken != "" {
390+ credentials ["refresh_token" ] = refreshToken
391+ }
392+ if chatgptAccountID := importStringField (meta , "chatgpt_account_id" , "chatgptAccountId" ); chatgptAccountID != "" {
393+ credentials ["chatgpt_account_id" ] = chatgptAccountID
394+ }
395+ if workspaceID := importStringField (meta , "workspace_id" , "workspaceId" ); workspaceID != "" {
396+ credentials ["workspace_id" ] = workspaceID
397+ }
398+
399+ notes := importOptionalStringField (meta , "note" , "notes" , "description" )
400+ if notes == nil {
401+ notes = importOptionalStringField (item , "note" , "notes" , "description" )
402+ }
403+
404+ return AccountCredentialImportSource {
405+ Kind : AccountCredentialImportKindOAuthCredentials ,
406+ Name : credentialImportFirstNonEmptyString (importStringField (meta , "label" , "name" ), importStringField (item , "name" , "label" )),
407+ Notes : notes ,
408+ Platform : PlatformOpenAI ,
409+ Credentials : credentials ,
410+ Extra : map [string ]any {},
411+ }, true , nil
412+ }
413+
345414func accountCredentialImportSourceFromString (value , name string , notes * string ) (AccountCredentialImportSource , error ) {
346415 text := strings .TrimSpace (value )
347416 if text == "" {
@@ -469,6 +538,15 @@ func copyImportMap(values map[string]any) map[string]any {
469538 return out
470539}
471540
541+ func removeImportMapField (values map [string ]any , key string ) {
542+ normalizedTarget := normalizeCredentialImportKey (key )
543+ for existingKey := range values {
544+ if normalizeCredentialImportKey (existingKey ) == normalizedTarget {
545+ delete (values , existingKey )
546+ }
547+ }
548+ }
549+
472550func normalizeCredentialImportKey (key string ) string {
473551 return strings .NewReplacer ("-" , "_" , "." , "_" ).Replace (strings .ToLower (strings .TrimSpace (key )))
474552}
0 commit comments