Compare commits

..

15 Commits

Author SHA1 Message Date
Luis Pater
ed6e7750e2 Update config key gpt-5-codex to force-gpt-5-codex for clarity in example configuration file 2025-09-16 20:00:21 +08:00
hkfires
e9037fceb0 fix(windows): Improve path handling and file read reliability 2025-09-16 10:01:58 +08:00
Luis Pater
2406cc775e Add GPT-5 Codex model support and configuration options in documentation 2025-09-16 04:53:25 +08:00
Luis Pater
b84cbee77a Add support for forcing GPT-5 Codex model configuration
- Introduced a new `ForceGPT5Codex` configuration option in settings.
- Added relevant API endpoints for managing `ForceGPT5Codex`.
- Enhanced Codex client to handle GPT-5 Codex-specific logic and mapping.
- Updated example configuration file to include the new option.

Add GPT-5 Codex model support and configuration options in documentation
2025-09-16 04:40:19 +08:00
Luis Pater
fa762e69a4 Merge branch 'dev' of github.com:luispater/CLIProxyAPI into dev 2025-09-14 22:55:18 +08:00
Luis Pater
7e0fd1e260 Add Keep-Alive header 2025-09-14 22:54:36 +08:00
hkfires
d6037e5549 Refine .gitignore and .dockerignore files 2025-09-14 16:29:06 +08:00
Luis Pater
9fce13fe03 Update internal module imports to use v5 package path
- Updated all `github.com/luispater/CLIProxyAPI/internal/...` imports to point to `github.com/luispater/CLIProxyAPI/v5/internal/...`.
- Adjusted `go.mod` to specify `module github.com/luispater/CLIProxyAPI/v5`.
2025-09-13 23:34:32 +08:00
Luis Pater
4375822cbb Resolve logsDir path relative to configuration directory in FileRequestLogger 2025-09-12 15:28:07 +08:00
Luis Pater
e0d13148ef Merge branch 'main' into dev 2025-09-12 13:12:02 +08:00
Luis Pater
bd68472d3c Merge pull request #41 from kaixxx/main
Codex CLI - setting 'store = false' to prevent the request being rejected by OpenAI
2025-09-12 13:10:22 +08:00
kaixxx
b3c534bae5 Build instructions reformatting 2025-09-12 01:16:54 +02:00
kaixxx
b7d6ae1b48 Windows build instructions 2025-09-12 01:12:51 +02:00
kaixxx
aacfcae382 Codex CLI - setting 'store = false'
store = true leads to:
BadRequestError("Error code: 400 - {'detail': 'Store must be set to false'}")
2025-09-12 00:59:49 +02:00
Luis Pater
1c92034191 Implement IP-based rate limiting and ban mechanism for management API
- Introduced a new `attemptInfo` structure to track failed login attempts per IP.
- Added logic to temporarily ban IPs exceeding the allowed number of failures.
- Enhanced middleware to reset failed attempt counters on successful authentication.
- Updated `Handler` to include a `failedAttempts` map with thread-safe access.
2025-09-12 03:00:37 +08:00
87 changed files with 537 additions and 327 deletions

View File

@@ -1,6 +1,6 @@
# Git and GitHub folders
.git
.github
.git/*
.github/*
# Docker and CI/CD related files
docker-compose.yml
@@ -10,6 +10,7 @@ docker-compose.yml
Dockerfile
# Documentation and license
docs/*
README.md
README_CN.md
MANAGEMENT_API.md
@@ -20,6 +21,11 @@ LICENSE
config.example.yaml
# Runtime data folders (should be mounted as volumes)
auths
logs
auths/*
logs/*
conv/*
config.yaml
# Development/editor
.claude/*
.vscode/*

8
.gitignore vendored
View File

@@ -1,6 +1,12 @@
config.yaml
*.exe
bin/*
docs/*
logs/*
conv/*
auths/*
!auths/.gitkeep
AGENTS.md
.vscode/*
.claude/*
AGENTS.md
CLAUDE.md

View File

@@ -47,9 +47,16 @@ The first Chinese provider has now been added: [Qwen Code](https://github.com/Qw
```
2. Build the application:
Linux, macOS:
```bash
go build -o cli-proxy-api ./cmd/server
```
Windows:
```bash
go build -o cli-proxy-api.exe ./cmd/server
```
## Usage
@@ -222,6 +229,7 @@ console.log(await claudeResponse.json());
- gemini-2.5-flash
- gemini-2.5-flash-lite
- gpt-5
- gpt-5-codex
- claude-opus-4-1-20250805
- claude-opus-4-20250514
- claude-sonnet-4-20250514
@@ -255,6 +263,7 @@ The server uses a YAML configuration file (`config.yaml`) located in the project
| `debug` | boolean | false | Enable debug mode for verbose logging. |
| `api-keys` | string[] | [] | List of API keys that can be used to authenticate requests. |
| `generative-language-api-key` | string[] | [] | List of Generative Language API keys. |
| `force-gpt-5-codex` | bool | false | Force the conversion of GPT-5 calls to GPT-5 Codex. |
| `codex-api-key` | object | {} | List of Codex API keys. |
| `codex-api-key.api-key` | string | "" | Codex API key. |
| `codex-api-key.base-url` | string | "" | Custom Codex API endpoint, if you use a third-party API endpoint. |
@@ -315,6 +324,9 @@ generative-language-api-key:
- "AIzaSy...03"
- "AIzaSy...04"
# Force the conversion of GPT-5 calls to GPT-5 Codex.
force-gpt-5-codex: true
# Codex API keys
codex-api-key:
- api-key: "sk-atSM..."
@@ -416,7 +428,7 @@ export ANTHROPIC_MODEL=gemini-2.5-pro
export ANTHROPIC_SMALL_FAST_MODEL=gemini-2.5-flash
```
Using OpenAI models:
Using OpenAI GPT 5 models:
```bash
export ANTHROPIC_BASE_URL=http://127.0.0.1:8317
export ANTHROPIC_AUTH_TOKEN=sk-dummy
@@ -424,6 +436,14 @@ export ANTHROPIC_MODEL=gpt-5
export ANTHROPIC_SMALL_FAST_MODEL=gpt-5-minimal
```
Using OpenAI GPT 5 Codex models:
```bash
export ANTHROPIC_BASE_URL=http://127.0.0.1:8317
export ANTHROPIC_AUTH_TOKEN=sk-dummy
export ANTHROPIC_MODEL=gpt-5-codex
export ANTHROPIC_SMALL_FAST_MODEL=gpt-5-codex-low
```
Using Claude models:
```bash
export ANTHROPIC_BASE_URL=http://127.0.0.1:8317
@@ -447,7 +467,7 @@ Start CLI Proxy API server, and then edit the `~/.codex/config.toml` and `~/.cod
config.toml:
```toml
model_provider = "cliproxyapi"
model = "gpt-5" # You can use any of the models that we support.
model = "gpt-5-codex" # Or gpt-5, you can also use any of the models that we support.
model_reasoning_effort = "high"
[model_providers.cliproxyapi]

View File

@@ -241,6 +241,7 @@ console.log(await claudeResponse.json());
- gemini-2.5-flash
- gemini-2.5-flash-lite
- gpt-5
- gpt-5-codex
- claude-opus-4-1-20250805
- claude-opus-4-20250514
- claude-sonnet-4-20250514
@@ -274,6 +275,7 @@ console.log(await claudeResponse.json());
| `debug` | boolean | false | 启用调试模式以获取详细日志。 |
| `api-keys` | string[] | [] | 可用于验证请求的API密钥列表。 |
| `generative-language-api-key` | string[] | [] | 生成式语言API密钥列表。 |
| `force-gpt-5-codex` | bool | false | 强制将 GPT-5 调用转换成 GPT-5 Codex。 |
| `codex-api-key` | object | {} | Codex API密钥列表。 |
| `codex-api-key.api-key` | string | "" | Codex API密钥。 |
| `codex-api-key.base-url` | string | "" | 自定义的Codex API端点 |
@@ -334,6 +336,9 @@ generative-language-api-key:
- "AIzaSy...03"
- "AIzaSy...04"
# 强制将 GPT-5 调用转换成 GPT-5 Codex.
force-gpt-5-codex: true
# Codex API 密钥
codex-api-key:
- api-key: "sk-atSM..."
@@ -430,7 +435,7 @@ export ANTHROPIC_MODEL=gemini-2.5-pro
export ANTHROPIC_SMALL_FAST_MODEL=gemini-2.5-flash
```
使用 OpenAI 模型:
使用 OpenAI GPT 5 模型:
```bash
export ANTHROPIC_BASE_URL=http://127.0.0.1:8317
export ANTHROPIC_AUTH_TOKEN=sk-dummy
@@ -438,6 +443,15 @@ export ANTHROPIC_MODEL=gpt-5
export ANTHROPIC_SMALL_FAST_MODEL=gpt-5-minimal
```
使用 OpenAI GPT 5 Codex 模型:
```bash
export ANTHROPIC_BASE_URL=http://127.0.0.1:8317
export ANTHROPIC_AUTH_TOKEN=sk-dummy
export ANTHROPIC_MODEL=gpt-5-codex
export ANTHROPIC_SMALL_FAST_MODEL=gpt-5-codex-low
```
使用 Claude 模型:
```bash
export ANTHROPIC_BASE_URL=http://127.0.0.1:8317
@@ -461,7 +475,7 @@ export ANTHROPIC_SMALL_FAST_MODEL=qwen3-coder-flash
config.toml:
```toml
model_provider = "cliproxyapi"
model = "gpt-5" # 你可以使用任何我们支持的模型
model = "gpt-5-codex" # 或者是gpt-5你也可以使用任何我们支持的模型
model_reasoning_effort = "high"
[model_providers.cliproxyapi]

View File

@@ -11,10 +11,10 @@ import (
"path/filepath"
"strings"
"github.com/luispater/CLIProxyAPI/internal/cmd"
"github.com/luispater/CLIProxyAPI/internal/config"
_ "github.com/luispater/CLIProxyAPI/internal/translator"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/cmd"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
)
@@ -122,13 +122,14 @@ func main() {
log.Fatalf("failed to get home directory: %v", errUserHomeDir)
}
// Reconstruct the path by replacing the tilde with the user's home directory.
parts := strings.Split(cfg.AuthDir, string(os.PathSeparator))
if len(parts) > 1 {
parts[0] = home
cfg.AuthDir = filepath.Join(parts...)
} else {
// If the path is just "~", set it to the home directory.
remainder := strings.TrimPrefix(cfg.AuthDir, "~")
remainder = strings.TrimLeft(remainder, "/\\")
if remainder == "" {
cfg.AuthDir = home
} else {
// Normalize any slash style in the remainder so Windows paths keep nested directories.
normalized := strings.ReplaceAll(remainder, "\\", "/")
cfg.AuthDir = filepath.Join(home, filepath.FromSlash(normalized))
}
}

View File

@@ -41,6 +41,9 @@ generative-language-api-key:
- "AIzaSy...03"
- "AIzaSy...04"
# forces the use of GPT-5 Codex model.
force-gpt-5-codex: true
# Codex API keys
codex-api-key:
- api-key: "sk-atSM..."

2
go.mod
View File

@@ -1,4 +1,4 @@
module github.com/luispater/CLIProxyAPI
module github.com/luispater/CLIProxyAPI/v5
go 1.24

View File

@@ -13,11 +13,11 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/api/handlers"
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/registry"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/api/handlers"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/registry"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
)

View File

@@ -14,10 +14,10 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/api/handlers"
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/api/handlers"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
)

View File

@@ -13,11 +13,11 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/api/handlers"
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/registry"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/api/handlers"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/registry"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
)

View File

@@ -8,10 +8,10 @@ import (
"sync"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/client"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/client"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"golang.org/x/net/context"
)

View File

@@ -13,13 +13,13 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/auth/claude"
"github.com/luispater/CLIProxyAPI/internal/auth/codex"
geminiAuth "github.com/luispater/CLIProxyAPI/internal/auth/gemini"
"github.com/luispater/CLIProxyAPI/internal/auth/qwen"
"github.com/luispater/CLIProxyAPI/internal/client"
"github.com/luispater/CLIProxyAPI/internal/misc"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/claude"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/codex"
geminiAuth "github.com/luispater/CLIProxyAPI/v5/internal/auth/gemini"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/qwen"
"github.com/luispater/CLIProxyAPI/v5/internal/client"
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"golang.org/x/oauth2"

View File

@@ -12,6 +12,14 @@ func (h *Handler) GetConfig(c *gin.Context) {
func (h *Handler) GetDebug(c *gin.Context) { c.JSON(200, gin.H{"debug": h.cfg.Debug}) }
func (h *Handler) PutDebug(c *gin.Context) { h.updateBoolField(c, func(v bool) { h.cfg.Debug = v }) }
// ForceGPT5Codex
func (h *Handler) GetForceGPT5Codex(c *gin.Context) {
c.JSON(200, gin.H{"gpt-5-codex": h.cfg.ForceGPT5Codex})
}
func (h *Handler) PutForceGPT5Codex(c *gin.Context) {
h.updateBoolField(c, func(v bool) { h.cfg.ForceGPT5Codex = v })
}
// Request log
func (h *Handler) GetRequestLog(c *gin.Context) { c.JSON(200, gin.H{"request-log": h.cfg.RequestLog}) }
func (h *Handler) PutRequestLog(c *gin.Context) {

View File

@@ -5,7 +5,7 @@ import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
)
// Generic helpers for list[string]

View File

@@ -7,22 +7,31 @@ import (
"net/http"
"strings"
"sync"
"time"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
"golang.org/x/crypto/bcrypt"
)
type attemptInfo struct {
count int
blockedUntil time.Time
}
// Handler aggregates config reference, persistence path and helpers.
type Handler struct {
cfg *config.Config
configFilePath string
mu sync.Mutex
attemptsMu sync.Mutex
failedAttempts map[string]*attemptInfo // keyed by client IP
}
// NewHandler creates a new management handler instance.
func NewHandler(cfg *config.Config, configFilePath string) *Handler {
return &Handler{cfg: cfg, configFilePath: configFilePath}
return &Handler{cfg: cfg, configFilePath: configFilePath, failedAttempts: make(map[string]*attemptInfo)}
}
// SetConfig updates the in-memory config reference when the server hot-reloads.
@@ -32,11 +41,32 @@ func (h *Handler) SetConfig(cfg *config.Config) { h.cfg = cfg }
// All requests (local and remote) require a valid management key.
// Additionally, remote access requires allow-remote-management=true.
func (h *Handler) Middleware() gin.HandlerFunc {
const maxFailures = 5
const banDuration = 30 * time.Minute
return func(c *gin.Context) {
clientIP := c.ClientIP()
// Remote access control: when not loopback, must be enabled
// For remote IPs, enforce allow-remote-management and ban checks
if !(clientIP == "127.0.0.1" || clientIP == "::1") {
// Check if IP is currently blocked
h.attemptsMu.Lock()
ai := h.failedAttempts[clientIP]
if ai != nil {
if !ai.blockedUntil.IsZero() {
if time.Now().Before(ai.blockedUntil) {
remaining := time.Until(ai.blockedUntil).Round(time.Second)
h.attemptsMu.Unlock()
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": fmt.Sprintf("IP banned due to too many failed attempts. Try again in %s", remaining)})
return
}
// Ban expired, reset state
ai.blockedUntil = time.Time{}
ai.count = 0
}
}
h.attemptsMu.Unlock()
allowRemote := h.cfg.RemoteManagement.AllowRemote
if !allowRemote {
allowRemote = true
@@ -67,15 +97,41 @@ func (h *Handler) Middleware() gin.HandlerFunc {
}
if !(clientIP == "127.0.0.1" || clientIP == "::1") {
// For remote IPs, enforce key and track failures
fail := func() {
h.attemptsMu.Lock()
ai := h.failedAttempts[clientIP]
if ai == nil {
ai = &attemptInfo{}
h.failedAttempts[clientIP] = ai
}
ai.count++
if ai.count >= maxFailures {
ai.blockedUntil = time.Now().Add(banDuration)
ai.count = 0
}
h.attemptsMu.Unlock()
}
if provided == "" {
fail()
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "missing management key"})
return
}
if err := bcrypt.CompareHashAndPassword([]byte(secret), []byte(provided)); err != nil {
fail()
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid management key"})
return
}
// Success: reset failed count for this IP
h.attemptsMu.Lock()
if ai := h.failedAttempts[clientIP]; ai != nil {
ai.count = 0
ai.blockedUntil = time.Time{}
}
h.attemptsMu.Unlock()
}
c.Next()

View File

@@ -14,11 +14,11 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/api/handlers"
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/registry"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/api/handlers"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/registry"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"

View File

@@ -13,11 +13,11 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/api/handlers"
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/registry"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/api/handlers"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/registry"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
)

View File

@@ -8,7 +8,7 @@ import (
"io"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/logging"
"github.com/luispater/CLIProxyAPI/v5/internal/logging"
)
// RequestLoggingMiddleware creates a Gin middleware that logs HTTP requests and responses.

View File

@@ -8,8 +8,8 @@ import (
"strings"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/logging"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/logging"
)
// RequestInfo holds essential details of an incoming HTTP request for logging purposes.

View File

@@ -10,20 +10,21 @@ import (
"fmt"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/api/handlers"
"github.com/luispater/CLIProxyAPI/internal/api/handlers/claude"
"github.com/luispater/CLIProxyAPI/internal/api/handlers/gemini"
managementHandlers "github.com/luispater/CLIProxyAPI/internal/api/handlers/management"
"github.com/luispater/CLIProxyAPI/internal/api/handlers/openai"
"github.com/luispater/CLIProxyAPI/internal/api/middleware"
"github.com/luispater/CLIProxyAPI/internal/client"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/logging"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/api/handlers"
"github.com/luispater/CLIProxyAPI/v5/internal/api/handlers/claude"
"github.com/luispater/CLIProxyAPI/v5/internal/api/handlers/gemini"
managementHandlers "github.com/luispater/CLIProxyAPI/v5/internal/api/handlers/management"
"github.com/luispater/CLIProxyAPI/v5/internal/api/handlers/openai"
"github.com/luispater/CLIProxyAPI/v5/internal/api/middleware"
"github.com/luispater/CLIProxyAPI/v5/internal/client"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/logging"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
)
@@ -75,7 +76,8 @@ func NewServer(cfg *config.Config, cliClients []interfaces.Client, configFilePat
engine.Use(gin.Recovery())
// Add request logging middleware (positioned after recovery, before auth)
requestLogger := logging.NewFileRequestLogger(cfg.RequestLog, "logs")
// Resolve logs directory relative to the configuration file directory.
requestLogger := logging.NewFileRequestLogger(cfg.RequestLog, "logs", filepath.Dir(configFilePath))
engine.Use(middleware.RequestLoggingMiddleware(requestLogger))
engine.Use(corsMiddleware())
@@ -198,6 +200,10 @@ func (s *Server) setupRoutes() {
mgmt.PUT("/debug", s.mgmt.PutDebug)
mgmt.PATCH("/debug", s.mgmt.PutDebug)
mgmt.GET("/force-gpt-5-codex", s.mgmt.GetForceGPT5Codex)
mgmt.PUT("/force-gpt-5-codex", s.mgmt.PutForceGPT5Codex)
mgmt.PATCH("/force-gpt-5-codex", s.mgmt.PutForceGPT5Codex)
mgmt.GET("/proxy-url", s.mgmt.GetProxyURL)
mgmt.PUT("/proxy-url", s.mgmt.PutProxyURL)
mgmt.PATCH("/proxy-url", s.mgmt.PutProxyURL)

View File

@@ -13,8 +13,8 @@ import (
"strings"
"time"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
)

View File

@@ -14,8 +14,8 @@ import (
"strings"
"time"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
)

View File

@@ -15,10 +15,10 @@ import (
"net/url"
"time"
"github.com/luispater/CLIProxyAPI/internal/auth/codex"
"github.com/luispater/CLIProxyAPI/internal/browser"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/codex"
"github.com/luispater/CLIProxyAPI/v5/internal/browser"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"golang.org/x/net/proxy"

View File

@@ -13,8 +13,8 @@ import (
"strings"
"time"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
)

View File

@@ -16,16 +16,16 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/auth"
"github.com/luispater/CLIProxyAPI/internal/auth/claude"
"github.com/luispater/CLIProxyAPI/internal/auth/empty"
"github.com/luispater/CLIProxyAPI/internal/config"
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/misc"
"github.com/luispater/CLIProxyAPI/internal/registry"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/auth"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/claude"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/empty"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
"github.com/luispater/CLIProxyAPI/v5/internal/registry"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"

View File

@@ -11,9 +11,9 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/auth"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/internal/registry"
"github.com/luispater/CLIProxyAPI/v5/internal/auth"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/registry"
)
// ClientBase provides a common base structure for all AI API clients.

View File

@@ -17,15 +17,15 @@ import (
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/luispater/CLIProxyAPI/internal/auth"
"github.com/luispater/CLIProxyAPI/internal/auth/codex"
"github.com/luispater/CLIProxyAPI/internal/auth/empty"
"github.com/luispater/CLIProxyAPI/internal/config"
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/registry"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/auth"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/codex"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/empty"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/registry"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
@@ -136,6 +136,10 @@ func (c *CodexClient) CanProvideModel(modelName string) bool {
"gpt-5-low",
"gpt-5-medium",
"gpt-5-high",
"gpt-5-codex",
"gpt-5-codex-low",
"gpt-5-codex-medium",
"gpt-5-codex-high",
"codex-mini-latest",
}
return util.InArray(models, modelName)
@@ -415,6 +419,25 @@ func (c *CodexClient) APIRequest(ctx context.Context, modelName, endpoint string
case "gpt-5-high":
jsonBody, _ = sjson.SetBytes(jsonBody, "reasoning.effort", "high")
}
} else if util.InArray([]string{"gpt-5-codex", "gpt-5-codex-low", "gpt-5-codex-medium", "gpt-5-codex-high"}, modelName) {
jsonBody, _ = sjson.SetBytes(jsonBody, "model", "gpt-5-codex")
switch modelName {
case "gpt-5-codex":
jsonBody, _ = sjson.SetBytes(jsonBody, "reasoning.effort", "medium")
case "gpt-5-codex-low":
jsonBody, _ = sjson.SetBytes(jsonBody, "reasoning.effort", "low")
case "gpt-5-codex-medium":
jsonBody, _ = sjson.SetBytes(jsonBody, "reasoning.effort", "medium")
case "gpt-5-codex-high":
jsonBody, _ = sjson.SetBytes(jsonBody, "reasoning.effort", "high")
}
} else if c.cfg.ForceGPT5Codex {
if gjson.GetBytes(jsonBody, "model").String() == "gpt-5" {
if gjson.GetBytes(jsonBody, "reasoning.effort").String() == "minimal" {
jsonBody, _ = sjson.SetBytes(jsonBody, "reasoning.effort", "low")
}
jsonBody, _ = sjson.SetBytes(jsonBody, "model", "gpt-5-codex")
}
}
url := fmt.Sprintf("%s%s", chatGPTEndpoint, endpoint)
@@ -447,6 +470,7 @@ func (c *CodexClient) APIRequest(ctx context.Context, modelName, endpoint string
req.Header.Set("Openai-Beta", "responses=experimental")
req.Header.Set("Session_id", sessionID)
req.Header.Set("Accept", "text/event-stream")
req.Header.Set("Connection", "Keep-Alive")
if c.apiKeyIndex != -1 {
// Using API key authentication

View File

@@ -18,13 +18,13 @@ import (
"time"
"github.com/gin-gonic/gin"
geminiAuth "github.com/luispater/CLIProxyAPI/internal/auth/gemini"
"github.com/luispater/CLIProxyAPI/internal/config"
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/registry"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
"github.com/luispater/CLIProxyAPI/internal/util"
geminiAuth "github.com/luispater/CLIProxyAPI/v5/internal/auth/gemini"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/registry"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"

View File

@@ -15,12 +15,12 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/config"
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/registry"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/registry"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
)

View File

@@ -15,13 +15,13 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/auth"
"github.com/luispater/CLIProxyAPI/internal/config"
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/registry"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/auth"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/registry"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/sjson"
)

View File

@@ -17,14 +17,14 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/luispater/CLIProxyAPI/internal/auth"
"github.com/luispater/CLIProxyAPI/internal/auth/qwen"
"github.com/luispater/CLIProxyAPI/internal/config"
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/registry"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/auth"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/qwen"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/registry"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"

View File

@@ -11,12 +11,12 @@ import (
"strings"
"time"
"github.com/luispater/CLIProxyAPI/internal/auth/claude"
"github.com/luispater/CLIProxyAPI/internal/browser"
"github.com/luispater/CLIProxyAPI/internal/client"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/internal/misc"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/claude"
"github.com/luispater/CLIProxyAPI/v5/internal/browser"
"github.com/luispater/CLIProxyAPI/v5/internal/client"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
)

View File

@@ -7,9 +7,9 @@ import (
"context"
"os"
"github.com/luispater/CLIProxyAPI/internal/auth/gemini"
"github.com/luispater/CLIProxyAPI/internal/client"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/gemini"
"github.com/luispater/CLIProxyAPI/v5/internal/client"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
log "github.com/sirupsen/logrus"
)

View File

@@ -11,12 +11,12 @@ import (
"strings"
"time"
"github.com/luispater/CLIProxyAPI/internal/auth/codex"
"github.com/luispater/CLIProxyAPI/internal/browser"
"github.com/luispater/CLIProxyAPI/internal/client"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/internal/misc"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/codex"
"github.com/luispater/CLIProxyAPI/v5/internal/browser"
"github.com/luispater/CLIProxyAPI/v5/internal/client"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
)

View File

@@ -8,10 +8,10 @@ import (
"fmt"
"os"
"github.com/luispater/CLIProxyAPI/internal/auth/qwen"
"github.com/luispater/CLIProxyAPI/internal/browser"
"github.com/luispater/CLIProxyAPI/internal/client"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/qwen"
"github.com/luispater/CLIProxyAPI/v5/internal/browser"
"github.com/luispater/CLIProxyAPI/v5/internal/client"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
log "github.com/sirupsen/logrus"
)

View File

@@ -18,16 +18,16 @@ import (
"syscall"
"time"
"github.com/luispater/CLIProxyAPI/internal/api"
"github.com/luispater/CLIProxyAPI/internal/auth/claude"
"github.com/luispater/CLIProxyAPI/internal/auth/codex"
"github.com/luispater/CLIProxyAPI/internal/auth/gemini"
"github.com/luispater/CLIProxyAPI/internal/auth/qwen"
"github.com/luispater/CLIProxyAPI/internal/client"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/internal/watcher"
"github.com/luispater/CLIProxyAPI/v5/internal/api"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/claude"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/codex"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/gemini"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/qwen"
"github.com/luispater/CLIProxyAPI/v5/internal/client"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/watcher"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
)

View File

@@ -44,6 +44,9 @@ type Config struct {
// ClaudeKey defines a list of Claude API key configurations as specified in the YAML configuration file.
ClaudeKey []ClaudeKey `yaml:"claude-api-key" json:"claude-api-key"`
// ForceGPT5Codex forces the use of GPT-5 Codex model.
ForceGPT5Codex bool `yaml:"force-gpt-5-codex" json:"force-gpt-5-codex"`
// Codex defines a list of Codex API key configurations as specified in the YAML configuration file.
CodexKey []CodexKey `yaml:"codex-api-key" json:"codex-api-key"`

View File

@@ -15,7 +15,7 @@ import (
"strings"
"time"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
)
// RequestLogger defines the interface for logging HTTP requests and responses.
@@ -98,20 +98,18 @@ type FileRequestLogger struct {
//
// Parameters:
// - enabled: Whether request logging should be enabled
// - logsDir: The directory where log files should be stored
// - logsDir: The directory where log files should be stored (can be relative)
// - configDir: The directory of the configuration file; when logsDir is
// relative, it will be resolved relative to this directory
//
// Returns:
// - *FileRequestLogger: A new file-based request logger instance
func NewFileRequestLogger(enabled bool, logsDir string) *FileRequestLogger {
// Resolve logsDir relative to the executable directory when it's not absolute.
func NewFileRequestLogger(enabled bool, logsDir string, configDir string) *FileRequestLogger {
// Resolve logsDir relative to the configuration file directory when it's not absolute.
if !filepath.IsAbs(logsDir) {
if exePath, err := os.Executable(); err == nil {
// Resolve symlinks to get the real executable path
if realExe, errEvalSymlinks := filepath.EvalSymlinks(exePath); errEvalSymlinks == nil {
exePath = realExe
}
execDir := filepath.Dir(exePath)
logsDir = filepath.Join(execDir, logsDir)
// If configDir is provided, resolve logsDir relative to it.
if configDir != "" {
logsDir = filepath.Join(configDir, logsDir)
}
}
return &FileRequestLogger{

View File

@@ -215,6 +215,58 @@ func GetOpenAIModels() []*ModelInfo {
MaxCompletionTokens: 128000,
SupportedParameters: []string{"tools"},
},
{
ID: "gpt-5-codex",
Object: "model",
Created: time.Now().Unix(),
OwnedBy: "openai",
Type: "openai",
Version: "gpt-5-2025-09-15",
DisplayName: "GPT 5 Codex",
Description: "Stable version of GPT 5 Codex, The best model for coding and agentic tasks across domains.",
ContextLength: 400000,
MaxCompletionTokens: 128000,
SupportedParameters: []string{"tools"},
},
{
ID: "gpt-5-codex-low",
Object: "model",
Created: time.Now().Unix(),
OwnedBy: "openai",
Type: "openai",
Version: "gpt-5-2025-09-15",
DisplayName: "GPT 5 Codex Low",
Description: "Stable version of GPT 5 Codex, The best model for coding and agentic tasks across domains.",
ContextLength: 400000,
MaxCompletionTokens: 128000,
SupportedParameters: []string{"tools"},
},
{
ID: "gpt-5-codex-medium",
Object: "model",
Created: time.Now().Unix(),
OwnedBy: "openai",
Type: "openai",
Version: "gpt-5-2025-09-15",
DisplayName: "GPT 5 Codex Medium",
Description: "Stable version of GPT 5 Codex, The best model for coding and agentic tasks across domains.",
ContextLength: 400000,
MaxCompletionTokens: 128000,
SupportedParameters: []string{"tools"},
},
{
ID: "gpt-5-codex-high",
Object: "model",
Created: time.Now().Unix(),
OwnedBy: "openai",
Type: "openai",
Version: "gpt-5-2025-09-15",
DisplayName: "GPT 5 Codex High",
Description: "Stable version of GPT 5 Codex, The best model for coding and agentic tasks across domains.",
ContextLength: 400000,
MaxCompletionTokens: 128000,
SupportedParameters: []string{"tools"},
},
{
ID: "codex-mini-latest",
Object: "model",

View File

@@ -8,7 +8,7 @@ package geminiCLI
import (
"bytes"
. "github.com/luispater/CLIProxyAPI/internal/translator/claude/gemini"
. "github.com/luispater/CLIProxyAPI/v5/internal/translator/claude/gemini"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)

View File

@@ -7,7 +7,7 @@ package geminiCLI
import (
"context"
. "github.com/luispater/CLIProxyAPI/internal/translator/claude/gemini"
. "github.com/luispater/CLIProxyAPI/v5/internal/translator/claude/gemini"
"github.com/tidwall/sjson"
)

View File

@@ -1,9 +1,9 @@
package geminiCLI
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -12,7 +12,7 @@ import (
"math/big"
"strings"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)

View File

@@ -1,9 +1,9 @@
package gemini
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -1,9 +1,9 @@
package chat_completions
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -1,9 +1,9 @@
package responses
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -11,7 +11,7 @@ import (
"strconv"
"strings"
"github.com/luispater/CLIProxyAPI/internal/misc"
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)

View File

@@ -1,9 +1,9 @@
package claude
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -8,7 +8,7 @@ package geminiCLI
import (
"bytes"
. "github.com/luispater/CLIProxyAPI/internal/translator/codex/gemini"
. "github.com/luispater/CLIProxyAPI/v5/internal/translator/codex/gemini"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)

View File

@@ -7,7 +7,7 @@ package geminiCLI
import (
"context"
. "github.com/luispater/CLIProxyAPI/internal/translator/codex/gemini"
. "github.com/luispater/CLIProxyAPI/v5/internal/translator/codex/gemini"
"github.com/tidwall/sjson"
)

View File

@@ -1,9 +1,9 @@
package geminiCLI
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -13,8 +13,8 @@ import (
"strconv"
"strings"
"github.com/luispater/CLIProxyAPI/internal/misc"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)

View File

@@ -1,9 +1,9 @@
package gemini
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -12,7 +12,7 @@ import (
"strconv"
"strings"
"github.com/luispater/CLIProxyAPI/internal/misc"
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
@@ -259,9 +259,6 @@ func ConvertOpenAIRequestToCodex(modelName string, inputRawJSON []byte, stream b
out, _ = sjson.Set(out, "text.verbosity", v.Value())
}
}
// The examples include store: true when response_format is provided
store = true
} else if text.Exists() {
// If only text.verbosity present (no response_format), map verbosity
if v := text.Get("verbosity"); v.Exists() {
@@ -306,10 +303,6 @@ func ConvertOpenAIRequestToCodex(modelName string, inputRawJSON []byte, stream b
out, _ = sjson.SetRaw(out, "tools.-1", item)
}
}
// The examples include store: true when tools and formatting are used; be conservative
if rf.Exists() {
store = true
}
}
out, _ = sjson.Set(out, "store", store)

View File

@@ -1,9 +1,9 @@
package chat_completions
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -3,7 +3,7 @@ package responses
import (
"bytes"
"github.com/luispater/CLIProxyAPI/internal/misc"
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)

View File

@@ -6,7 +6,7 @@ import (
"context"
"fmt"
"github.com/luispater/CLIProxyAPI/internal/misc"
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)

View File

@@ -1,9 +1,9 @@
package responses
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -10,8 +10,8 @@ import (
"encoding/json"
"strings"
client "github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/util"
client "github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)

View File

@@ -1,9 +1,9 @@
package claude
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -1,9 +1,9 @@
package gemini
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -7,8 +7,8 @@ import (
"fmt"
"strings"
"github.com/luispater/CLIProxyAPI/internal/misc"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"

View File

@@ -11,7 +11,7 @@ import (
"fmt"
"time"
. "github.com/luispater/CLIProxyAPI/internal/translator/gemini/openai/chat-completions"
. "github.com/luispater/CLIProxyAPI/v5/internal/translator/gemini/openai/chat-completions"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)

View File

@@ -1,9 +1,9 @@
package chat_completions
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -3,8 +3,8 @@ package responses
import (
"bytes"
. "github.com/luispater/CLIProxyAPI/internal/translator/gemini-cli/gemini"
. "github.com/luispater/CLIProxyAPI/internal/translator/gemini/openai/responses"
. "github.com/luispater/CLIProxyAPI/v5/internal/translator/gemini-cli/gemini"
. "github.com/luispater/CLIProxyAPI/v5/internal/translator/gemini/openai/responses"
)
func ConvertOpenAIResponsesRequestToGeminiCLI(modelName string, inputRawJSON []byte, stream bool) []byte {

View File

@@ -3,7 +3,7 @@ package responses
import (
"context"
. "github.com/luispater/CLIProxyAPI/internal/translator/gemini/openai/responses"
. "github.com/luispater/CLIProxyAPI/v5/internal/translator/gemini/openai/responses"
"github.com/tidwall/gjson"
)

View File

@@ -1,9 +1,9 @@
package responses
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -10,8 +10,8 @@ import (
"encoding/json"
"strings"
client "github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/util"
client "github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)

View File

@@ -1,9 +1,9 @@
package claude
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -1,9 +1,9 @@
package geminiCLI
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -1,9 +1,9 @@
package gemini
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
// Register a no-op response translator and a request normalizer for Gemini→Gemini.

View File

@@ -7,8 +7,8 @@ import (
"fmt"
"strings"
"github.com/luispater/CLIProxyAPI/internal/misc"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/misc"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"

View File

@@ -1,9 +1,9 @@
package chat_completions
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -1,9 +1,9 @@
package responses
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -1,30 +1,30 @@
package translator
import (
_ "github.com/luispater/CLIProxyAPI/internal/translator/claude/gemini"
_ "github.com/luispater/CLIProxyAPI/internal/translator/claude/gemini-cli"
_ "github.com/luispater/CLIProxyAPI/internal/translator/claude/openai/chat-completions"
_ "github.com/luispater/CLIProxyAPI/internal/translator/claude/openai/responses"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/claude/gemini"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/claude/gemini-cli"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/claude/openai/chat-completions"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/claude/openai/responses"
_ "github.com/luispater/CLIProxyAPI/internal/translator/codex/claude"
_ "github.com/luispater/CLIProxyAPI/internal/translator/codex/gemini"
_ "github.com/luispater/CLIProxyAPI/internal/translator/codex/gemini-cli"
_ "github.com/luispater/CLIProxyAPI/internal/translator/codex/openai/chat-completions"
_ "github.com/luispater/CLIProxyAPI/internal/translator/codex/openai/responses"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/codex/claude"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/codex/gemini"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/codex/gemini-cli"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/codex/openai/chat-completions"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/codex/openai/responses"
_ "github.com/luispater/CLIProxyAPI/internal/translator/gemini-cli/claude"
_ "github.com/luispater/CLIProxyAPI/internal/translator/gemini-cli/gemini"
_ "github.com/luispater/CLIProxyAPI/internal/translator/gemini-cli/openai/chat-completions"
_ "github.com/luispater/CLIProxyAPI/internal/translator/gemini-cli/openai/responses"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/gemini-cli/claude"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/gemini-cli/gemini"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/gemini-cli/openai/chat-completions"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/gemini-cli/openai/responses"
_ "github.com/luispater/CLIProxyAPI/internal/translator/gemini/claude"
_ "github.com/luispater/CLIProxyAPI/internal/translator/gemini/gemini"
_ "github.com/luispater/CLIProxyAPI/internal/translator/gemini/gemini-cli"
_ "github.com/luispater/CLIProxyAPI/internal/translator/gemini/openai/chat-completions"
_ "github.com/luispater/CLIProxyAPI/internal/translator/gemini/openai/responses"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/gemini/claude"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/gemini/gemini"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/gemini/gemini-cli"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/gemini/openai/chat-completions"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/gemini/openai/responses"
_ "github.com/luispater/CLIProxyAPI/internal/translator/openai/claude"
_ "github.com/luispater/CLIProxyAPI/internal/translator/openai/gemini"
_ "github.com/luispater/CLIProxyAPI/internal/translator/openai/gemini-cli"
_ "github.com/luispater/CLIProxyAPI/internal/translator/openai/openai/responses"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/openai/claude"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/openai/gemini"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/openai/gemini-cli"
_ "github.com/luispater/CLIProxyAPI/v5/internal/translator/openai/openai/responses"
)

View File

@@ -1,9 +1,9 @@
package claude
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -10,7 +10,7 @@ import (
"encoding/json"
"strings"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
"github.com/tidwall/gjson"
)

View File

@@ -1,9 +1,9 @@
package geminiCLI
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -8,7 +8,7 @@ package geminiCLI
import (
"bytes"
. "github.com/luispater/CLIProxyAPI/internal/translator/openai/gemini"
. "github.com/luispater/CLIProxyAPI/v5/internal/translator/openai/gemini"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)

View File

@@ -8,7 +8,7 @@ package geminiCLI
import (
"context"
. "github.com/luispater/CLIProxyAPI/internal/translator/openai/gemini"
. "github.com/luispater/CLIProxyAPI/v5/internal/translator/openai/gemini"
"github.com/tidwall/sjson"
)

View File

@@ -1,9 +1,9 @@
package gemini
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -1,9 +1,9 @@
package responses
import (
. "github.com/luispater/CLIProxyAPI/internal/constant"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/translator/translator"
. "github.com/luispater/CLIProxyAPI/v5/internal/constant"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/translator/translator"
)
func init() {

View File

@@ -3,7 +3,7 @@ package translator
import (
"context"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
log "github.com/sirupsen/logrus"
)

View File

@@ -6,7 +6,7 @@ package util
import (
"strings"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
)
// GetProviderName determines the AI service provider based on the model name.

View File

@@ -9,7 +9,7 @@ import (
"net/http"
"net/url"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
log "github.com/sirupsen/logrus"
"golang.org/x/net/proxy"
)

View File

@@ -1,7 +1,7 @@
package util
import (
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
log "github.com/sirupsen/logrus"
)

View File

@@ -18,14 +18,14 @@ import (
"time"
"github.com/fsnotify/fsnotify"
"github.com/luispater/CLIProxyAPI/internal/auth/claude"
"github.com/luispater/CLIProxyAPI/internal/auth/codex"
"github.com/luispater/CLIProxyAPI/internal/auth/gemini"
"github.com/luispater/CLIProxyAPI/internal/auth/qwen"
"github.com/luispater/CLIProxyAPI/internal/client"
"github.com/luispater/CLIProxyAPI/internal/config"
"github.com/luispater/CLIProxyAPI/internal/interfaces"
"github.com/luispater/CLIProxyAPI/internal/util"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/claude"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/codex"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/gemini"
"github.com/luispater/CLIProxyAPI/v5/internal/auth/qwen"
"github.com/luispater/CLIProxyAPI/v5/internal/client"
"github.com/luispater/CLIProxyAPI/v5/internal/config"
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
"github.com/luispater/CLIProxyAPI/v5/internal/util"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
)
@@ -44,6 +44,11 @@ type Watcher struct {
lastConfigHash string
}
const (
authFileReadMaxAttempts = 5
authFileReadRetryDelay = 100 * time.Millisecond
)
// NewWatcher creates a new file watcher instance
func NewWatcher(configPath, authDir string, reloadCallback func(map[string]interfaces.Client, *config.Config)) (*Watcher, error) {
watcher, errNewWatcher := fsnotify.NewWatcher()
@@ -240,6 +245,9 @@ func (w *Watcher) reloadConfig() bool {
if oldConfig.RemoteManagement.AllowRemote != newConfig.RemoteManagement.AllowRemote {
log.Debugf(" remote-management.allow-remote: %t -> %t", oldConfig.RemoteManagement.AllowRemote, newConfig.RemoteManagement.AllowRemote)
}
if oldConfig.ForceGPT5Codex != newConfig.ForceGPT5Codex {
log.Debugf(" gpt-5-codex: %t -> %t", oldConfig.ForceGPT5Codex, newConfig.ForceGPT5Codex)
}
}
log.Infof("config successfully reloaded, triggering client reload")
@@ -295,7 +303,7 @@ func (w *Watcher) reloadClients() {
// Rebuild auth file hash cache for current clients
w.lastAuthHashes = make(map[string]string, len(newFileClients))
for path := range newFileClients {
if data, err := os.ReadFile(path); err == nil && len(data) > 0 {
if data, err := readAuthFileWithRetry(path, authFileReadMaxAttempts, authFileReadRetryDelay); err == nil && len(data) > 0 {
sum := sha256.Sum256(data)
w.lastAuthHashes[path] = hex.EncodeToString(sum[:])
}
@@ -324,7 +332,7 @@ func (w *Watcher) reloadClients() {
// createClientFromFile creates a single client instance from a given token file path.
func (w *Watcher) createClientFromFile(path string, cfg *config.Config) (interfaces.Client, error) {
data, errReadFile := os.ReadFile(path)
data, errReadFile := readAuthFileWithRetry(path, authFileReadMaxAttempts, authFileReadRetryDelay)
if errReadFile != nil {
return nil, errReadFile
}
@@ -382,8 +390,38 @@ func (w *Watcher) clientsToSlice(clientMap map[string]interfaces.Client) []inter
return s
}
// readAuthFileWithRetry attempts to read the auth file multiple times to work around
// short-lived locks on Windows while token files are being written.
func readAuthFileWithRetry(path string, attempts int, delay time.Duration) ([]byte, error) {
var lastErr error
for i := 0; i < attempts; i++ {
data, err := os.ReadFile(path)
if err == nil {
return data, nil
}
lastErr = err
if i < attempts-1 {
time.Sleep(delay)
}
}
return nil, lastErr
}
// addOrUpdateClient handles the addition or update of a single client.
func (w *Watcher) addOrUpdateClient(path string) {
data, errRead := readAuthFileWithRetry(path, authFileReadMaxAttempts, authFileReadRetryDelay)
if errRead != nil {
log.Errorf("failed to read auth file %s: %v", filepath.Base(path), errRead)
return
}
if len(data) == 0 {
log.Debugf("ignoring empty auth file: %s", filepath.Base(path))
return
}
sum := sha256.Sum256(data)
curHash := hex.EncodeToString(sum[:])
w.clientsMutex.Lock()
cfg := w.config
@@ -392,24 +430,6 @@ func (w *Watcher) addOrUpdateClient(path string) {
w.clientsMutex.Unlock()
return
}
// Read file to check for emptiness and calculate hash
data, errRead := os.ReadFile(path)
if errRead != nil {
log.Errorf("failed to read auth file %s: %v", filepath.Base(path), errRead)
w.clientsMutex.Unlock()
return
}
if len(data) == 0 {
// Empty file: ignore (wait for a subsequent WRITE)
log.Debugf("ignoring empty auth file: %s", filepath.Base(path))
w.clientsMutex.Unlock()
return
}
// Calculate a hash of the current content and compare with the cache
sum := sha256.Sum256(data)
curHash := hex.EncodeToString(sum[:])
if prev, ok := w.lastAuthHashes[path]; ok && prev == curHash {
log.Debugf("auth file unchanged (hash match), skipping reload: %s", filepath.Base(path))
w.clientsMutex.Unlock()