diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4bb5e63b..64e7a5b7 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -19,7 +19,7 @@ jobs: - run: git fetch --force --tags - uses: actions/setup-go@v4 with: - go-version: '>=1.24.0' + go-version: '>=1.26.0' cache: true - name: Generate Build Metadata run: | diff --git a/go.mod b/go.mod index 38a499be..9e9a9c9e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/router-for-me/CLIProxyAPI/v6 -go 1.24.0 +go 1.26.0 require ( github.com/andybalholm/brotli v1.0.6 diff --git a/internal/api/handlers/management/config_basic.go b/internal/api/handlers/management/config_basic.go index ee2d5c35..f77e91e9 100644 --- a/internal/api/handlers/management/config_basic.go +++ b/internal/api/handlers/management/config_basic.go @@ -28,8 +28,7 @@ func (h *Handler) GetConfig(c *gin.Context) { c.JSON(200, gin.H{}) return } - cfgCopy := *h.cfg - c.JSON(200, &cfgCopy) + c.JSON(200, new(*h.cfg)) } type releaseInfo struct { diff --git a/internal/api/modules/amp/amp.go b/internal/api/modules/amp/amp.go index b5626ce9..a12733e2 100644 --- a/internal/api/modules/amp/amp.go +++ b/internal/api/modules/amp/amp.go @@ -127,8 +127,7 @@ func (m *AmpModule) Register(ctx modules.Context) error { m.modelMapper = NewModelMapper(settings.ModelMappings) // Store initial config for partial reload comparison - settingsCopy := settings - m.lastConfig = &settingsCopy + m.lastConfig = new(settings) // Initialize localhost restriction setting (hot-reloadable) m.setRestrictToLocalhost(settings.RestrictManagementToLocalhost) diff --git a/internal/cmd/anthropic_login.go b/internal/cmd/anthropic_login.go index dafdd02b..f7381461 100644 --- a/internal/cmd/anthropic_login.go +++ b/internal/cmd/anthropic_login.go @@ -40,8 +40,7 @@ func DoClaudeLogin(cfg *config.Config, options *LoginOptions) { _, savedPath, err := manager.Login(context.Background(), "claude", cfg, authOpts) if err != nil { - var authErr *claude.AuthenticationError - if errors.As(err, &authErr) { + if authErr, ok := errors.AsType[*claude.AuthenticationError](err); ok { log.Error(claude.GetUserFriendlyMessage(authErr)) if authErr.Type == claude.ErrPortInUse.Type { os.Exit(claude.ErrPortInUse.Code) diff --git a/internal/cmd/iflow_login.go b/internal/cmd/iflow_login.go index 07360b8c..49e18e5b 100644 --- a/internal/cmd/iflow_login.go +++ b/internal/cmd/iflow_login.go @@ -32,8 +32,7 @@ func DoIFlowLogin(cfg *config.Config, options *LoginOptions) { _, savedPath, err := manager.Login(context.Background(), "iflow", cfg, authOpts) if err != nil { - var emailErr *sdkAuth.EmailRequiredError - if errors.As(err, &emailErr) { + if emailErr, ok := errors.AsType[*sdkAuth.EmailRequiredError](err); ok { log.Error(emailErr.Error()) return } diff --git a/internal/cmd/login.go b/internal/cmd/login.go index 3286e7a7..1d8a1ae3 100644 --- a/internal/cmd/login.go +++ b/internal/cmd/login.go @@ -148,8 +148,7 @@ func DoLogin(cfg *config.Config, projectID string, options *LoginOptions) { for _, candidateID := range projectSelections { log.Infof("Activating project %s", candidateID) if errSetup := performGeminiCLISetup(ctx, httpClient, storage, candidateID); errSetup != nil { - var projectErr *projectSelectionRequiredError - if errors.As(errSetup, &projectErr) { + if _, ok := errors.AsType[*projectSelectionRequiredError](errSetup); ok { log.Error("Failed to start user onboarding: A project ID is required.") showProjectSelectionHelp(storage.Email, projects) return diff --git a/internal/cmd/openai_login.go b/internal/cmd/openai_login.go index 5f2fb162..783a9484 100644 --- a/internal/cmd/openai_login.go +++ b/internal/cmd/openai_login.go @@ -54,8 +54,7 @@ func DoCodexLogin(cfg *config.Config, options *LoginOptions) { _, savedPath, err := manager.Login(context.Background(), "codex", cfg, authOpts) if err != nil { - var authErr *codex.AuthenticationError - if errors.As(err, &authErr) { + if authErr, ok := errors.AsType[*codex.AuthenticationError](err); ok { log.Error(codex.GetUserFriendlyMessage(authErr)) if authErr.Type == codex.ErrPortInUse.Type { os.Exit(codex.ErrPortInUse.Code) diff --git a/internal/cmd/qwen_login.go b/internal/cmd/qwen_login.go index 92a57aa5..10179fa8 100644 --- a/internal/cmd/qwen_login.go +++ b/internal/cmd/qwen_login.go @@ -44,8 +44,7 @@ func DoQwenLogin(cfg *config.Config, options *LoginOptions) { _, savedPath, err := manager.Login(context.Background(), "qwen", cfg, authOpts) if err != nil { - var emailErr *sdkAuth.EmailRequiredError - if errors.As(err, &emailErr) { + if emailErr, ok := errors.AsType[*sdkAuth.EmailRequiredError](err); ok { log.Error(emailErr.Error()) return } diff --git a/internal/registry/model_registry.go b/internal/registry/model_registry.go index edb1f124..7b8b262e 100644 --- a/internal/registry/model_registry.go +++ b/internal/registry/model_registry.go @@ -596,8 +596,7 @@ func (r *ModelRegistry) SetModelQuotaExceeded(clientID, modelID string) { defer r.mutex.Unlock() if registration, exists := r.models[modelID]; exists { - now := time.Now() - registration.QuotaExceededClients[clientID] = &now + registration.QuotaExceededClients[clientID] = new(time.Now()) log.Debugf("Marked model %s as quota exceeded for client %s", modelID, clientID) } } diff --git a/internal/runtime/executor/gemini_cli_executor.go b/internal/runtime/executor/gemini_cli_executor.go index 4ac7bdba..3e218c0f 100644 --- a/internal/runtime/executor/gemini_cli_executor.go +++ b/internal/runtime/executor/gemini_cli_executor.go @@ -899,8 +899,7 @@ func parseRetryDelay(errorBody []byte) (*time.Duration, error) { if matches := re.FindStringSubmatch(message); len(matches) > 1 { seconds, err := strconv.Atoi(matches[1]) if err == nil { - duration := time.Duration(seconds) * time.Second - return &duration, nil + return new(time.Duration(seconds) * time.Second), nil } } } diff --git a/sdk/api/handlers/gemini/gemini-cli_handlers.go b/sdk/api/handlers/gemini/gemini-cli_handlers.go index 917902e7..07cedc55 100644 --- a/sdk/api/handlers/gemini/gemini-cli_handlers.go +++ b/sdk/api/handlers/gemini/gemini-cli_handlers.go @@ -185,8 +185,7 @@ func (h *GeminiCLIAPIHandler) handleInternalGenerateContent(c *gin.Context, rawJ func (h *GeminiCLIAPIHandler) forwardCLIStream(c *gin.Context, flusher http.Flusher, alt string, cancel func(error), data <-chan []byte, errs <-chan *interfaces.ErrorMessage) { var keepAliveInterval *time.Duration if alt != "" { - disabled := time.Duration(0) - keepAliveInterval = &disabled + keepAliveInterval = new(time.Duration(0)) } h.ForwardStream(c, flusher, cancel, data, errs, handlers.StreamForwardOptions{ diff --git a/sdk/api/handlers/gemini/gemini_handlers.go b/sdk/api/handlers/gemini/gemini_handlers.go index 71c485ad..a5eb337d 100644 --- a/sdk/api/handlers/gemini/gemini_handlers.go +++ b/sdk/api/handlers/gemini/gemini_handlers.go @@ -300,8 +300,7 @@ func (h *GeminiAPIHandler) handleGenerateContent(c *gin.Context, modelName strin func (h *GeminiAPIHandler) forwardGeminiStream(c *gin.Context, flusher http.Flusher, alt string, cancel func(error), data <-chan []byte, errs <-chan *interfaces.ErrorMessage) { var keepAliveInterval *time.Duration if alt != "" { - disabled := time.Duration(0) - keepAliveInterval = &disabled + keepAliveInterval = new(time.Duration(0)) } h.ForwardStream(c, flusher, cancel, data, errs, handlers.StreamForwardOptions{ diff --git a/sdk/auth/antigravity.go b/sdk/auth/antigravity.go index ecca0a00..6ed31d6d 100644 --- a/sdk/auth/antigravity.go +++ b/sdk/auth/antigravity.go @@ -28,8 +28,7 @@ func (AntigravityAuthenticator) Provider() string { return "antigravity" } // RefreshLead instructs the manager to refresh five minutes before expiry. func (AntigravityAuthenticator) RefreshLead() *time.Duration { - lead := 5 * time.Minute - return &lead + return new(5 * time.Minute) } // Login launches a local OAuth flow to obtain antigravity tokens and persists them. diff --git a/sdk/auth/claude.go b/sdk/auth/claude.go index a6b19af5..706763b3 100644 --- a/sdk/auth/claude.go +++ b/sdk/auth/claude.go @@ -32,8 +32,7 @@ func (a *ClaudeAuthenticator) Provider() string { } func (a *ClaudeAuthenticator) RefreshLead() *time.Duration { - d := 4 * time.Hour - return &d + return new(4 * time.Hour) } func (a *ClaudeAuthenticator) Login(ctx context.Context, cfg *config.Config, opts *LoginOptions) (*coreauth.Auth, error) { diff --git a/sdk/auth/codex.go b/sdk/auth/codex.go index b655a239..c81842eb 100644 --- a/sdk/auth/codex.go +++ b/sdk/auth/codex.go @@ -34,8 +34,7 @@ func (a *CodexAuthenticator) Provider() string { } func (a *CodexAuthenticator) RefreshLead() *time.Duration { - d := 5 * 24 * time.Hour - return &d + return new(5 * 24 * time.Hour) } func (a *CodexAuthenticator) Login(ctx context.Context, cfg *config.Config, opts *LoginOptions) (*coreauth.Auth, error) { diff --git a/sdk/auth/iflow.go b/sdk/auth/iflow.go index 6d4ff946..a695311d 100644 --- a/sdk/auth/iflow.go +++ b/sdk/auth/iflow.go @@ -26,8 +26,7 @@ func (a *IFlowAuthenticator) Provider() string { return "iflow" } // RefreshLead indicates how soon before expiry a refresh should be attempted. func (a *IFlowAuthenticator) RefreshLead() *time.Duration { - d := 24 * time.Hour - return &d + return new(24 * time.Hour) } // Login performs the OAuth code flow using a local callback server. diff --git a/sdk/auth/qwen.go b/sdk/auth/qwen.go index 151fba68..310d4987 100644 --- a/sdk/auth/qwen.go +++ b/sdk/auth/qwen.go @@ -27,8 +27,7 @@ func (a *QwenAuthenticator) Provider() string { } func (a *QwenAuthenticator) RefreshLead() *time.Duration { - d := 3 * time.Hour - return &d + return new(3 * time.Hour) } func (a *QwenAuthenticator) Login(ctx context.Context, cfg *config.Config, opts *LoginOptions) (*coreauth.Auth, error) { diff --git a/sdk/cliproxy/auth/conductor.go b/sdk/cliproxy/auth/conductor.go index 51c40537..2c3e9f48 100644 --- a/sdk/cliproxy/auth/conductor.go +++ b/sdk/cliproxy/auth/conductor.go @@ -599,8 +599,7 @@ func (m *Manager) executeMixedOnce(ctx context.Context, providers []string, req return cliproxyexecutor.Response{}, errCtx } result.Error = &Error{Message: errExec.Error()} - var se cliproxyexecutor.StatusError - if errors.As(errExec, &se) && se != nil { + if se, ok := errors.AsType[cliproxyexecutor.StatusError](errExec); ok && se != nil { result.Error.HTTPStatus = se.StatusCode() } if ra := retryAfterFromError(errExec); ra != nil { @@ -655,8 +654,7 @@ func (m *Manager) executeCountMixedOnce(ctx context.Context, providers []string, return cliproxyexecutor.Response{}, errCtx } result.Error = &Error{Message: errExec.Error()} - var se cliproxyexecutor.StatusError - if errors.As(errExec, &se) && se != nil { + if se, ok := errors.AsType[cliproxyexecutor.StatusError](errExec); ok && se != nil { result.Error.HTTPStatus = se.StatusCode() } if ra := retryAfterFromError(errExec); ra != nil { @@ -710,8 +708,7 @@ func (m *Manager) executeStreamMixedOnce(ctx context.Context, providers []string return nil, errCtx } rerr := &Error{Message: errStream.Error()} - var se cliproxyexecutor.StatusError - if errors.As(errStream, &se) && se != nil { + if se, ok := errors.AsType[cliproxyexecutor.StatusError](errStream); ok && se != nil { rerr.HTTPStatus = se.StatusCode() } result := Result{AuthID: auth.ID, Provider: provider, Model: routeModel, Success: false, Error: rerr} @@ -732,8 +729,7 @@ func (m *Manager) executeStreamMixedOnce(ctx context.Context, providers []string if chunk.Err != nil && !failed { failed = true rerr := &Error{Message: chunk.Err.Error()} - var se cliproxyexecutor.StatusError - if errors.As(chunk.Err, &se) && se != nil { + if se, ok := errors.AsType[cliproxyexecutor.StatusError](chunk.Err); ok && se != nil { rerr.HTTPStatus = se.StatusCode() } m.MarkResult(streamCtx, Result{AuthID: streamAuth.ID, Provider: streamProvider, Model: routeModel, Success: false, Error: rerr}) @@ -1431,8 +1427,7 @@ func retryAfterFromError(err error) *time.Duration { if retryAfter == nil { return nil } - val := *retryAfter - return &val + return new(*retryAfter) } func statusCodeFromResult(err *Error) int {