Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 121 additions & 9 deletions config.example.yaml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/custom-provider/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const (
// transformation logic between OpenAI format and your provider's format.
func init() {
sdktr.Register(fOpenAI, fMyProv,
func(model string, raw []byte, stream bool) []byte { return raw },
func(model string, raw []byte, stream bool) ([]byte, error) { return raw, nil },
sdktr.ResponseTransform{
Stream: func(ctx context.Context, model string, originalReq, translatedReq, raw []byte, param *any) [][]byte {
return [][]byte{raw}
Expand Down
4 changes: 2 additions & 2 deletions examples/plugin/claude-web-search-router/go/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/router-for-me/CLIProxyAPI/v7/examples/plugin/claude-web-search-router/go

go 1.26.0
go 1.26.10

require (
github.com/router-for-me/CLIProxyAPI/v7 v7.0.0
Expand All @@ -12,7 +12,7 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/sys v0.48.0 // indirect
)

replace github.com/router-for-me/CLIProxyAPI/v7 => ../../../..
1 change: 1 addition & 0 deletions examples/plugin/claude-web-search-router/go/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
2 changes: 1 addition & 1 deletion examples/plugin/codex-service-tier/go/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/router-for-me/CLIProxyAPI/v7/examples/plugin/codex-service-tier/go

go 1.26.0
go 1.26.10

require (
github.com/router-for-me/CLIProxyAPI/v7 v7.0.0
Expand Down
2 changes: 1 addition & 1 deletion examples/plugin/frontend-auth-exclusive/go/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/router-for-me/CLIProxyAPI/v7/examples/plugin/frontend-auth-exclusive/go

go 1.26.0
go 1.26.10

require github.com/router-for-me/CLIProxyAPI/v7 v7.0.0

Expand Down
2 changes: 1 addition & 1 deletion examples/plugin/host-callback-auth-files/go/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/router-for-me/CLIProxyAPI/v7/examples/plugin/host-callback-auth-files/go

go 1.26.0
go 1.26.10

require github.com/router-for-me/CLIProxyAPI/v7 v7.0.0

Expand Down
2 changes: 1 addition & 1 deletion examples/plugin/host-model-callback/go/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/router-for-me/CLIProxyAPI/v7/examples/plugin/host-model-callback/go

go 1.26.0
go 1.26.10

require github.com/router-for-me/CLIProxyAPI/v7 v7.0.0

Expand Down
2 changes: 1 addition & 1 deletion examples/plugin/request-lifecycle/go/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/router-for-me/CLIProxyAPI/v7/examples/plugin/request-lifecycle/go

go 1.26.0
go 1.26.10

require (
github.com/router-for-me/CLIProxyAPI/v7 v7.0.0
Expand Down
2 changes: 1 addition & 1 deletion examples/plugin/scheduler/go/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/router-for-me/CLIProxyAPI/v7/examples/plugin/scheduler/go

go 1.26.0
go 1.26.10

require (
github.com/router-for-me/CLIProxyAPI/v7 v7.0.0
Expand Down
2 changes: 1 addition & 1 deletion examples/plugin/simple/go/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/router-for-me/CLIProxyAPI/v7/examples/plugin/simple/go

go 1.26.0
go 1.26.10

require github.com/router-for-me/CLIProxyAPI/v7 v7.0.0

Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/router-for-me/CLIProxyAPI/v8

go 1.26.0
go 1.26.10

require (
github.com/andybalholm/brotli v1.0.6
Expand All @@ -17,6 +17,7 @@ require (
github.com/joho/godotenv v1.5.1
github.com/klauspost/compress v1.18.7
github.com/libp2p/zeroconf/v2 v2.2.0
github.com/miekg/dns v1.1.43
github.com/minio/minio-go/v7 v7.0.66
github.com/pion/ice/v4 v4.3.0
github.com/pion/interceptor v0.1.45
Expand Down Expand Up @@ -44,7 +45,6 @@ require (
require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dlclark/regexp2/v2 v2.5.1 // indirect
github.com/miekg/dns v1.1.43 // indirect
github.com/pion/datachannel v1.6.2 // indirect
github.com/pion/dtls/v3 v3.1.5 // indirect
github.com/pion/logging v0.2.4 // indirect
Expand Down Expand Up @@ -107,7 +107,7 @@ require (
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pelletier/go-toml/v2 v2.4.3-0.20260624044523-35f78d5908f1 // indirect
github.com/pierrec/xxHash v0.1.5
github.com/pjbgf/sha1cd v0.6.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
Expand Down
3 changes: 1 addition & 2 deletions internal/auth/antigravity/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,13 +257,12 @@ func (o *AntigravityAuth) FetchProjectID(ctx context.Context, accessToken string
return "", fmt.Errorf("marshal request body: %w", errMarshal)
}

endpointURL := fmt.Sprintf("%s/%s:loadCodeAssist", APIEndpoint, APIVersion)
endpointURL := fmt.Sprintf("%s/%s:loadCodeAssist", DailyAPIEndpoint, APIVersion)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpointURL, strings.NewReader(string(rawBody)))
if err != nil {
return "", fmt.Errorf("create request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Accept", "*/*")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", userAgent)

Expand Down
25 changes: 25 additions & 0 deletions internal/auth/codex/filelock_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//go:build !windows

package codex

import (
"fmt"
"os"
"syscall"
)

func acquireInterProcessLockFile(authFilePath string) (func(), error) {
lockPath := canonicalLockPath(authFilePath) + ".lock"
f, errOpen := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0o600)
if errOpen != nil {
return nil, fmt.Errorf("failed to open credential lock file %s: %w", lockPath, errOpen)
}
if errFlock := syscall.Flock(int(f.Fd()), syscall.LOCK_EX); errFlock != nil {
_ = f.Close()
return nil, fmt.Errorf("failed to acquire flock on %s: %w", lockPath, errFlock)
}
return func() {
_ = syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
_ = f.Close()
}, nil
}
44 changes: 44 additions & 0 deletions internal/auth/codex/filelock_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//go:build windows

package codex

import (
"fmt"
"os"
"syscall"
"unsafe"
)

var (
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
procLockFileEx = modkernel32.NewProc("LockFileEx")
procUnlockFileEx = modkernel32.NewProc("UnlockFileEx")
)

const lockfileExclusiveLock = 0x00000002

func acquireInterProcessLockFile(authFilePath string) (func(), error) {
lockPath := canonicalLockPath(authFilePath) + ".lock"
f, errOpen := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0o600)
if errOpen != nil {
return nil, fmt.Errorf("failed to open credential lock file %s: %w", lockPath, errOpen)
}
var overlapped syscall.Overlapped
r1, _, errLock := procLockFileEx.Call(
f.Fd(),
uintptr(lockfileExclusiveLock),
0,
1,
0,
uintptr(unsafe.Pointer(&overlapped)),
)
if r1 == 0 {
_ = f.Close()
return nil, fmt.Errorf("failed to lock credential file %s: %w", lockPath, errLock)
}
return func() {
var ov syscall.Overlapped
_, _, _ = procUnlockFileEx.Call(f.Fd(), 0, 1, 0, uintptr(unsafe.Pointer(&ov)))
_ = f.Close()
}, nil
}
8 changes: 6 additions & 2 deletions internal/auth/codex/oauth_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ type OAuthResult struct {
Code string
// State is the state parameter used to prevent CSRF attacks
State string
// ClientID is the dynamically issued client ID returned by Sign in with ChatGPT
ClientID string
// Error contains any error message if the OAuth flow failed
Error string
}
Expand Down Expand Up @@ -175,6 +177,7 @@ func (s *OAuthServer) handleCallback(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
code := query.Get("code")
state := query.Get("state")
clientID := strings.TrimSpace(query.Get("client_id"))
errorParam := query.Get("error")

// Validate required parameters
Expand Down Expand Up @@ -210,8 +213,9 @@ func (s *OAuthServer) handleCallback(w http.ResponseWriter, r *http.Request) {

// Send successful result
result := &OAuthResult{
Code: code,
State: state,
Code: code,
State: state,
ClientID: clientID,
}
s.sendResult(result)

Expand Down
46 changes: 46 additions & 0 deletions internal/auth/codex/openai.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,52 @@ type CodexTokenData struct {
PlanType string `json:"plan_type,omitempty"`
}

// CodexSIWCTokenData holds the OAuth token information obtained from OpenAI's Sign in with ChatGPT (SIWC) flow.
type CodexSIWCTokenData struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
IDToken string `json:"id_token,omitempty"`
ClientID string `json:"client_id"`
Scope string `json:"scope,omitempty"`
Scopes []string `json:"scopes,omitempty"`
Expire string `json:"expired"`
ExpiresAt string `json:"expires_at,omitempty"`
LastRefresh string `json:"last_refresh"`
DeviceID string `json:"device_id,omitempty"`
}

// ToMap converts CodexSIWCTokenData into a map suitable for credential metadata persistence.
func (d *CodexSIWCTokenData) ToMap() map[string]any {
if d == nil {
return nil
}
out := map[string]any{
"access_token": d.AccessToken,
"refresh_token": d.RefreshToken,
"client_id": d.ClientID,
"expired": d.Expire,
"last_refresh": d.LastRefresh,
}
if d.ExpiresAt != "" {
out["expires_at"] = d.ExpiresAt
}
if d.IDToken != "" {
out["id_token"] = d.IDToken
}
if d.Scope != "" {
out["scope"] = d.Scope
}
if len(d.Scopes) > 0 {
scopesCopy := make([]string, len(d.Scopes))
copy(scopesCopy, d.Scopes)
out["scopes"] = scopesCopy
}
if d.DeviceID != "" {
out["device_id"] = d.DeviceID
}
return out
}

// CodexAuthBundle aggregates all authentication-related data after the OAuth flow is complete.
// This includes the API key, token data, and the timestamp of the last refresh.
type CodexAuthBundle struct {
Expand Down
8 changes: 6 additions & 2 deletions internal/auth/codex/openai_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ const (
// It manages the HTTP client and provides methods for generating authorization URLs,
// exchanging authorization codes for tokens, and refreshing access tokens.
type CodexAuth struct {
httpClient *http.Client
httpClient *http.Client
siwcAgentNameHint string
}

var codexRefreshGroup singleflight.Group
Expand All @@ -49,15 +50,18 @@ func NewCodexAuth(cfg *config.Config) *CodexAuth {
func NewCodexAuthWithProxyURL(cfg *config.Config, proxyURL string) *CodexAuth {
effectiveProxyURL := strings.TrimSpace(proxyURL)
var sdkCfg config.SDKConfig
agentNameHint := SIWCAgentNameHint
if cfg != nil {
sdkCfg = cfg.SDKConfig
if effectiveProxyURL == "" {
effectiveProxyURL = strings.TrimSpace(cfg.ProxyURL)
}
agentNameHint = cfg.Codex.EffectiveSignInWithChatGPTIdentifier()
}
sdkCfg.ProxyURL = effectiveProxyURL
return &CodexAuth{
httpClient: util.SetProxy(&sdkCfg, &http.Client{}),
httpClient: util.SetProxy(&sdkCfg, &http.Client{}),
siwcAgentNameHint: agentNameHint,
}
}

Expand Down
Loading
Loading