Skip to content

Commit fa4f7f8

Browse files
authored
Merge pull request #1454 from Ac-spider/codex/latest-20260709
Support Codex personal access token reverse proxy
2 parents 93c34cd + 02a08a7 commit fa4f7f8

4 files changed

Lines changed: 678 additions & 25 deletions

File tree

sidecars/cockpit-cliproxy/main.go

Lines changed: 280 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"os"
1919
"os/signal"
2020
"path/filepath"
21+
"runtime"
2122
"sort"
2223
"strconv"
2324
"strings"
@@ -93,6 +94,8 @@ type manifest struct {
9394
accountByID map[string]*accountSpec
9495
accountByAuthID map[string]*accountSpec
9596
accountByAPIKey map[string]*accountSpec
97+
accountByChatGPT map[string]*accountSpec
98+
accountByEmail map[string]*accountSpec
9699
aliasToSource map[string]string
97100
originalIndexByID map[string]int
98101
}
@@ -127,6 +130,9 @@ type accountSpec struct {
127130
ID string `json:"id"`
128131
Email string `json:"email"`
129132
AuthID string `json:"authId,omitempty"`
133+
AuthKind string `json:"authKind,omitempty"`
134+
AccessTokenOnly bool `json:"accessTokenOnly,omitempty"`
135+
ChatGPTAccountID string `json:"chatgptAccountId,omitempty"`
130136
UpstreamAPIKey string `json:"upstreamApiKey,omitempty"`
131137
PlanRank *int `json:"planRank,omitempty"`
132138
RemainingQuota *int `json:"remainingQuota,omitempty"`
@@ -493,23 +499,42 @@ func loadManifest(path string) (*manifest, error) {
493499
m.accountByID = make(map[string]*accountSpec)
494500
m.accountByAuthID = make(map[string]*accountSpec)
495501
m.accountByAPIKey = make(map[string]*accountSpec)
502+
m.accountByChatGPT = make(map[string]*accountSpec)
503+
m.accountByEmail = make(map[string]*accountSpec)
496504
m.originalIndexByID = make(map[string]int)
497505
for i := range m.Accounts {
498506
account := &m.Accounts[i]
499507
account.ID = strings.TrimSpace(account.ID)
500508
if account.ID == "" {
501509
continue
502510
}
511+
account.Email = strings.TrimSpace(account.Email)
512+
account.AuthKind = strings.ToLower(strings.TrimSpace(account.AuthKind))
513+
account.ChatGPTAccountID = strings.TrimSpace(account.ChatGPTAccountID)
503514
m.accountByID[account.ID] = account
504515
m.originalIndexByID[account.ID] = i
505516
if authID := strings.TrimSpace(account.AuthID); authID != "" {
506517
account.AuthID = authID
507518
m.accountByAuthID[strings.ToLower(authID)] = account
519+
if base := filepath.Base(authID); base != authID {
520+
m.accountByAuthID[strings.ToLower(base)] = account
521+
}
508522
}
509523
if key := strings.TrimSpace(account.UpstreamAPIKey); key != "" {
510524
account.UpstreamAPIKey = key
511525
m.accountByAPIKey[key] = account
512526
}
527+
if account.ChatGPTAccountID != "" {
528+
m.accountByChatGPT[strings.ToLower(account.ChatGPTAccountID)] = account
529+
}
530+
if account.Email != "" {
531+
key := strings.ToLower(account.Email)
532+
if existing, exists := m.accountByEmail[key]; exists && existing != account {
533+
m.accountByEmail[key] = nil
534+
} else {
535+
m.accountByEmail[key] = account
536+
}
537+
}
513538
}
514539
m.aliasToSource = make(map[string]string)
515540
for _, alias := range m.ModelAliases {
@@ -2530,6 +2555,10 @@ func newSidecarRuntime(ctx context.Context, configPath string, cfg *config.Confi
25302555
cancel()
25312556
return nil, err
25322557
}
2558+
if err := registerManifestCodexTokenAuths(runtimeCtx, service, cfg, m, manager); err != nil {
2559+
cancel()
2560+
return nil, err
2561+
}
25332562
for _, auth := range manager.List() {
25342563
if auth == nil || !strings.EqualFold(strings.TrimSpace(auth.Provider), "codex") {
25352564
continue
@@ -2571,6 +2600,187 @@ func registerConfigCodexAPIKeyAuths(ctx context.Context, service *cliproxy.Servi
25712600
return nil
25722601
}
25732602

2603+
func registerManifestCodexTokenAuths(
2604+
ctx context.Context,
2605+
service *cliproxy.Service,
2606+
cfg *config.Config,
2607+
m *manifest,
2608+
manager *coreauth.Manager,
2609+
) error {
2610+
if service == nil || cfg == nil || m == nil {
2611+
return nil
2612+
}
2613+
for i := range m.Accounts {
2614+
account := &m.Accounts[i]
2615+
authID := strings.TrimSpace(account.AuthID)
2616+
if authID == "" || manifestAccountAuthKind(account) == "api_key" {
2617+
continue
2618+
}
2619+
path := authID
2620+
if !filepath.IsAbs(path) {
2621+
path = filepath.Join(cfg.AuthDir, path)
2622+
}
2623+
auth, err := readManifestCodexTokenAuth(account, cfg.AuthDir, path)
2624+
if err != nil {
2625+
return err
2626+
}
2627+
registered, err := service.UpsertRuntimeAuth(coreauth.WithSkipPersist(ctx), auth)
2628+
if err != nil {
2629+
return fmt.Errorf("register codex token auth %s: %w", auth.ID, err)
2630+
}
2631+
linkManifestAccountForAuth(m, registered)
2632+
registerManifestModelsForAuth(manager, m, registered)
2633+
}
2634+
return nil
2635+
}
2636+
2637+
func readManifestCodexTokenAuth(account *accountSpec, authDir, path string) (*coreauth.Auth, error) {
2638+
data, err := os.ReadFile(path)
2639+
if err != nil {
2640+
return nil, fmt.Errorf("read codex token auth file %s: %w", path, err)
2641+
}
2642+
metadata := make(map[string]any)
2643+
if err = json.Unmarshal(data, &metadata); err != nil {
2644+
return nil, fmt.Errorf("parse codex token auth file %s: %w", path, err)
2645+
}
2646+
provider := strings.TrimSpace(metadataString(metadata, "type"))
2647+
if provider == "" {
2648+
provider = "codex"
2649+
}
2650+
if !strings.EqualFold(provider, "codex") {
2651+
return nil, fmt.Errorf("codex token auth file %s has unsupported provider %q", path, provider)
2652+
}
2653+
accessToken := firstMetadataString(
2654+
metadata,
2655+
"personal_access_token",
2656+
"at_token",
2657+
"access_token",
2658+
)
2659+
if accessToken == "" {
2660+
return nil, fmt.Errorf("codex token auth file %s is missing access_token", path)
2661+
}
2662+
metadata["access_token"] = accessToken
2663+
if strings.TrimSpace(metadataString(metadata, "token_type")) == "" {
2664+
metadata["token_type"] = "Bearer"
2665+
}
2666+
if account != nil &&
2667+
(account.AccessTokenOnly || manifestAccountAuthKind(account) == "access_token") {
2668+
if strings.TrimSpace(metadataString(metadata, "auth_mode")) == "" {
2669+
metadata["auth_mode"] = "personal_access_token"
2670+
}
2671+
if strings.TrimSpace(metadataString(metadata, "openai_auth_mode")) == "" {
2672+
metadata["openai_auth_mode"] = "personal_access_token"
2673+
}
2674+
}
2675+
2676+
info, err := os.Stat(path)
2677+
if err != nil {
2678+
return nil, fmt.Errorf("stat codex token auth file %s: %w", path, err)
2679+
}
2680+
id := manifestAuthFileID(authDir, path)
2681+
label := ""
2682+
if account != nil {
2683+
label = strings.TrimSpace(account.Email)
2684+
}
2685+
if label == "" {
2686+
label = firstMetadataString(metadata, "email", "label")
2687+
}
2688+
disabled, _ := metadata["disabled"].(bool)
2689+
status := coreauth.StatusActive
2690+
if disabled {
2691+
status = coreauth.StatusDisabled
2692+
}
2693+
auth := &coreauth.Auth{
2694+
ID: id,
2695+
Provider: "codex",
2696+
FileName: id,
2697+
Label: label,
2698+
Status: status,
2699+
Disabled: disabled,
2700+
Attributes: map[string]string{
2701+
"path": path,
2702+
"auth_kind": manifestAccountAuthKind(account),
2703+
},
2704+
Metadata: metadata,
2705+
CreatedAt: info.ModTime(),
2706+
UpdatedAt: info.ModTime(),
2707+
LastRefreshedAt: time.Time{},
2708+
}
2709+
if account != nil {
2710+
auth.Attributes["account_id"] = strings.TrimSpace(account.ID)
2711+
if strings.TrimSpace(account.Email) != "" {
2712+
auth.Attributes["email"] = strings.TrimSpace(account.Email)
2713+
}
2714+
if strings.TrimSpace(account.ChatGPTAccountID) != "" {
2715+
auth.Attributes["chatgpt_account_id"] = strings.TrimSpace(account.ChatGPTAccountID)
2716+
}
2717+
}
2718+
if email := firstMetadataString(metadata, "email"); email != "" {
2719+
auth.Attributes["email"] = email
2720+
}
2721+
if proxyURL := firstMetadataString(metadata, "proxy_url", "proxy-url"); proxyURL != "" {
2722+
auth.ProxyURL = proxyURL
2723+
}
2724+
coreauth.ApplyCustomHeadersFromMetadata(auth)
2725+
return auth, nil
2726+
}
2727+
2728+
func manifestAccountAuthKind(account *accountSpec) string {
2729+
if account == nil {
2730+
return ""
2731+
}
2732+
if kind := strings.ToLower(strings.TrimSpace(account.AuthKind)); kind != "" {
2733+
switch kind {
2734+
case "api-key", "apikey", "api key":
2735+
return "api_key"
2736+
case "access-token", "accesstoken", "access token",
2737+
"personal_access_token", "pat", "at":
2738+
return "access_token"
2739+
default:
2740+
return kind
2741+
}
2742+
}
2743+
if strings.TrimSpace(account.UpstreamAPIKey) != "" {
2744+
return "api_key"
2745+
}
2746+
if account.AccessTokenOnly {
2747+
return "access_token"
2748+
}
2749+
return "oauth"
2750+
}
2751+
2752+
func manifestAuthFileID(authDir, path string) string {
2753+
id := path
2754+
if strings.TrimSpace(authDir) != "" {
2755+
if rel, err := filepath.Rel(authDir, path); err == nil && strings.TrimSpace(rel) != "" {
2756+
id = rel
2757+
}
2758+
}
2759+
if runtime.GOOS == "windows" {
2760+
id = strings.ToLower(id)
2761+
}
2762+
return id
2763+
}
2764+
2765+
func metadataString(metadata map[string]any, key string) string {
2766+
if metadata == nil {
2767+
return ""
2768+
}
2769+
if raw, ok := metadata[key].(string); ok {
2770+
return strings.TrimSpace(raw)
2771+
}
2772+
return ""
2773+
}
2774+
2775+
func firstMetadataString(metadata map[string]any, keys ...string) string {
2776+
for _, key := range keys {
2777+
if value := metadataString(metadata, key); value != "" {
2778+
return value
2779+
}
2780+
}
2781+
return ""
2782+
}
2783+
25742784
func (r *sidecarRuntime) Execute(ctx context.Context, providers []string, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (cliproxyexecutor.Response, error) {
25752785
if r == nil || r.service == nil {
25762786
return cliproxyexecutor.Response{}, fmt.Errorf("runtime is not initialized")
@@ -2626,16 +2836,84 @@ func linkManifestAccountForAuth(m *manifest, auth *coreauth.Auth) {
26262836
if m.accountByAuthID == nil {
26272837
m.accountByAuthID = make(map[string]*accountSpec)
26282838
}
2629-
if _, exists := m.accountByAuthID[strings.ToLower(auth.ID)]; exists {
2839+
authID := strings.ToLower(strings.TrimSpace(auth.ID))
2840+
if _, exists := m.accountByAuthID[authID]; exists {
2841+
return
2842+
}
2843+
if account := findManifestAccountForAuth(m, auth); account != nil {
2844+
m.accountByAuthID[authID] = account
2845+
if base := strings.ToLower(filepath.Base(strings.TrimSpace(auth.ID))); base != "" && base != authID {
2846+
m.accountByAuthID[base] = account
2847+
}
26302848
return
26312849
}
2850+
}
2851+
2852+
func findManifestAccountForAuth(m *manifest, auth *coreauth.Auth) *accountSpec {
2853+
if m == nil || auth == nil {
2854+
return nil
2855+
}
2856+
for _, candidate := range []string{
2857+
strings.TrimSpace(auth.ID),
2858+
filepath.Base(strings.TrimSpace(auth.ID)),
2859+
strings.TrimSpace(auth.FileName),
2860+
filepath.Base(strings.TrimSpace(auth.FileName)),
2861+
} {
2862+
if candidate == "." || candidate == "" {
2863+
continue
2864+
}
2865+
if account := m.accountByAuthID[strings.ToLower(candidate)]; account != nil {
2866+
return account
2867+
}
2868+
}
26322869
if auth.Attributes != nil {
2870+
if path := strings.TrimSpace(auth.Attributes["path"]); path != "" {
2871+
if account := m.accountByAuthID[strings.ToLower(path)]; account != nil {
2872+
return account
2873+
}
2874+
if account := m.accountByAuthID[strings.ToLower(filepath.Base(path))]; account != nil {
2875+
return account
2876+
}
2877+
}
26332878
if key := strings.TrimSpace(auth.Attributes["api_key"]); key != "" {
26342879
if account := m.accountByAPIKey[key]; account != nil {
2635-
m.accountByAuthID[strings.ToLower(auth.ID)] = account
2880+
return account
2881+
}
2882+
}
2883+
if accountID := strings.TrimSpace(auth.Attributes["account_id"]); accountID != "" {
2884+
if account := m.accountByID[accountID]; account != nil {
2885+
return account
2886+
}
2887+
}
2888+
if chatGPTID := strings.TrimSpace(auth.Attributes["chatgpt_account_id"]); chatGPTID != "" {
2889+
if account := m.accountByChatGPT[strings.ToLower(chatGPTID)]; account != nil {
2890+
return account
2891+
}
2892+
}
2893+
if email := strings.TrimSpace(auth.Attributes["email"]); email != "" {
2894+
if account := m.accountByEmail[strings.ToLower(email)]; account != nil {
2895+
return account
26362896
}
26372897
}
26382898
}
2899+
if auth.Metadata != nil {
2900+
for _, key := range []string{"account_id", "chatgpt_account_id"} {
2901+
if value := metadataString(auth.Metadata, key); value != "" {
2902+
if account := m.accountByChatGPT[strings.ToLower(value)]; account != nil {
2903+
return account
2904+
}
2905+
if account := m.accountByID[value]; account != nil {
2906+
return account
2907+
}
2908+
}
2909+
}
2910+
if email := metadataString(auth.Metadata, "email"); email != "" {
2911+
if account := m.accountByEmail[strings.ToLower(email)]; account != nil {
2912+
return account
2913+
}
2914+
}
2915+
}
2916+
return nil
26392917
}
26402918

26412919
func registerManifestModelsForAuth(manager *coreauth.Manager, m *manifest, auth *coreauth.Auth) {

0 commit comments

Comments
 (0)