Explorar o código

Revert "feat: add Claude Code channel support with OAuth integration"

Seefs hai 5 meses
pai
achega
f995e31d04

+ 0 - 2
common/api_type.go

@@ -65,8 +65,6 @@ func ChannelType2APIType(channelType int) (int, bool) {
 		apiType = constant.APITypeCoze
 	case constant.ChannelTypeJimeng:
 		apiType = constant.APITypeJimeng
-	case constant.ChannelTypeClaudeCode:
-		apiType = constant.APITypeClaudeCode
 	}
 	if apiType == -1 {
 		return constant.APITypeOpenAI, false

+ 0 - 1
constant/api_type.go

@@ -31,6 +31,5 @@ const (
 	APITypeXai
 	APITypeCoze
 	APITypeJimeng
-	APITypeClaudeCode
 	APITypeDummy // this one is only for count, do not add any channel after this
 )

+ 0 - 2
constant/channel.go

@@ -50,7 +50,6 @@ const (
 	ChannelTypeKling          = 50
 	ChannelTypeJimeng         = 51
 	ChannelTypeVidu           = 52
-	ChannelTypeClaudeCode     = 53
 	ChannelTypeDummy          // this one is only for count, do not add any channel after this
 
 )
@@ -109,5 +108,4 @@ var ChannelBaseURLs = []string{
 	"https://api.klingai.com",                   //50
 	"https://visual.volcengineapi.com",          //51
 	"https://api.vidu.cn",                       //52
-	"https://api.anthropic.com",                 //53
 }

+ 0 - 73
controller/claude_oauth.go

@@ -1,73 +0,0 @@
-package controller
-
-import (
-	"net/http"
-	"one-api/common"
-	"one-api/service"
-
-	"github.com/gin-gonic/gin"
-)
-
-// ExchangeCodeRequest 授权码交换请求
-type ExchangeCodeRequest struct {
-	AuthorizationCode string `json:"authorization_code" binding:"required"`
-	CodeVerifier      string `json:"code_verifier" binding:"required"`
-	State             string `json:"state" binding:"required"`
-}
-
-// GenerateClaudeOAuthURL 生成Claude OAuth授权URL
-func GenerateClaudeOAuthURL(c *gin.Context) {
-	params, err := service.GenerateOAuthParams()
-	if err != nil {
-		c.JSON(http.StatusInternalServerError, gin.H{
-			"success": false,
-			"message": "生成OAuth授权URL失败: " + err.Error(),
-		})
-		return
-	}
-
-	c.JSON(http.StatusOK, gin.H{
-		"success": true,
-		"message": "生成OAuth授权URL成功",
-		"data":    params,
-	})
-}
-
-// ExchangeClaudeOAuthCode 交换Claude OAuth授权码
-func ExchangeClaudeOAuthCode(c *gin.Context) {
-	var req ExchangeCodeRequest
-	if err := c.ShouldBindJSON(&req); err != nil {
-		c.JSON(http.StatusBadRequest, gin.H{
-			"success": false,
-			"message": "请求参数错误: " + err.Error(),
-		})
-		return
-	}
-
-	// 解析授权码
-	cleanedCode, err := service.ParseAuthorizationCode(req.AuthorizationCode)
-	if err != nil {
-		c.JSON(http.StatusBadRequest, gin.H{
-			"success": false,
-			"message": err.Error(),
-		})
-		return
-	}
-
-	// 交换token
-	tokenResult, err := service.ExchangeCode(cleanedCode, req.CodeVerifier, req.State, nil)
-	if err != nil {
-		common.SysError("Claude OAuth token exchange failed: " + err.Error())
-		c.JSON(http.StatusInternalServerError, gin.H{
-			"success": false,
-			"message": "授权码交换失败: " + err.Error(),
-		})
-		return
-	}
-
-	c.JSON(http.StatusOK, gin.H{
-		"success": true,
-		"message": "授权码交换成功",
-		"data":    tokenResult,
-	})
-}

+ 0 - 1
go.mod

@@ -87,7 +87,6 @@ require (
 	github.com/yusufpapurcu/wmi v1.2.3 // indirect
 	golang.org/x/arch v0.12.0 // indirect
 	golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect
-	golang.org/x/oauth2 v0.30.0 // indirect
 	golang.org/x/sys v0.30.0 // indirect
 	golang.org/x/text v0.22.0 // indirect
 	google.golang.org/protobuf v1.34.2 // indirect

+ 0 - 2
go.sum

@@ -231,8 +231,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
 golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
-golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
-golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
 golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=

+ 0 - 3
main.go

@@ -86,9 +86,6 @@ func main() {
 	// 数据看板
 	go model.UpdateQuotaData()
 
-	// Start Claude Code token refresh scheduler
-	service.StartClaudeTokenRefreshScheduler()
-
 	if os.Getenv("CHANNEL_UPDATE_FREQUENCY") != "" {
 		frequency, err := strconv.Atoi(os.Getenv("CHANNEL_UPDATE_FREQUENCY"))
 		if err != nil {

+ 0 - 158
relay/channel/claude_code/adaptor.go

@@ -1,158 +0,0 @@
-package claude_code
-
-import (
-	"errors"
-	"fmt"
-	"io"
-	"net/http"
-	"one-api/dto"
-	"one-api/relay/channel"
-	"one-api/relay/channel/claude"
-	relaycommon "one-api/relay/common"
-	"one-api/types"
-	"strings"
-
-	"github.com/gin-gonic/gin"
-)
-
-const (
-	RequestModeCompletion = 1
-	RequestModeMessage    = 2
-	DefaultSystemPrompt   = "You are Claude Code, Anthropic's official CLI for Claude."
-)
-
-type Adaptor struct {
-	RequestMode int
-}
-
-func (a *Adaptor) ConvertClaudeRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.ClaudeRequest) (any, error) {
-	// Use configured system prompt if available, otherwise use default
-	if info.ChannelSetting.SystemPrompt != "" {
-		request.System = info.ChannelSetting.SystemPrompt
-	} else {
-		request.System = DefaultSystemPrompt
-	}
-
-	return request, nil
-}
-
-func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.AudioRequest) (io.Reader, error) {
-	return nil, errors.New("not implemented")
-}
-
-func (a *Adaptor) ConvertImageRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.ImageRequest) (any, error) {
-	return nil, errors.New("not implemented")
-}
-
-func (a *Adaptor) Init(info *relaycommon.RelayInfo) {
-	if strings.HasPrefix(info.UpstreamModelName, "claude-2") || strings.HasPrefix(info.UpstreamModelName, "claude-instant") {
-		a.RequestMode = RequestModeCompletion
-	} else {
-		a.RequestMode = RequestModeMessage
-	}
-}
-
-func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
-	if a.RequestMode == RequestModeMessage {
-		return fmt.Sprintf("%s/v1/messages", info.BaseUrl), nil
-	} else {
-		return fmt.Sprintf("%s/v1/complete", info.BaseUrl), nil
-	}
-}
-
-func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Header, info *relaycommon.RelayInfo) error {
-	channel.SetupApiRequestHeader(info, c, req)
-
-	// Parse accesstoken|refreshtoken format and use only the access token
-	accessToken := info.ApiKey
-	if strings.Contains(info.ApiKey, "|") {
-		parts := strings.Split(info.ApiKey, "|")
-		if len(parts) >= 1 {
-			accessToken = parts[0]
-		}
-	}
-
-	// Claude Code specific headers - force override
-	req.Set("Authorization", "Bearer "+accessToken)
-	// 只有在没有设置的情况下才设置 anthropic-version
-	if req.Get("anthropic-version") == "" {
-		req.Set("anthropic-version", "2023-06-01")
-	}
-	req.Set("content-type", "application/json")
-
-	// 只有在 user-agent 不包含 claude-cli 时才设置
-	userAgent := req.Get("user-agent")
-	if userAgent == "" || !strings.Contains(strings.ToLower(userAgent), "claude-cli") {
-		req.Set("user-agent", "claude-cli/1.0.61 (external, cli)")
-	}
-
-	// 只有在 anthropic-beta 不包含 claude-code 时才设置
-	anthropicBeta := req.Get("anthropic-beta")
-	if anthropicBeta == "" || !strings.Contains(strings.ToLower(anthropicBeta), "claude-code") {
-		req.Set("anthropic-beta", "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14")
-	}
-	// if Anthropic-Dangerous-Direct-Browser-Access
-	anthropicDangerousDirectBrowserAccess := req.Get("anthropic-dangerous-direct-browser-access")
-	if anthropicDangerousDirectBrowserAccess == "" {
-		req.Set("anthropic-dangerous-direct-browser-access", "true")
-	}
-
-	return nil
-}
-
-func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.GeneralOpenAIRequest) (any, error) {
-	if request == nil {
-		return nil, errors.New("request is nil")
-	}
-
-	if a.RequestMode == RequestModeCompletion {
-		return claude.RequestOpenAI2ClaudeComplete(*request), nil
-	} else {
-		claudeRequest, err := claude.RequestOpenAI2ClaudeMessage(*request)
-		if err != nil {
-			return nil, err
-		}
-
-		// Use configured system prompt if available, otherwise use default
-		if info.ChannelSetting.SystemPrompt != "" {
-			claudeRequest.System = info.ChannelSetting.SystemPrompt
-		} else {
-			claudeRequest.System = DefaultSystemPrompt
-		}
-
-		return claudeRequest, nil
-	}
-}
-
-func (a *Adaptor) ConvertRerankRequest(c *gin.Context, relayMode int, request dto.RerankRequest) (any, error) {
-	return nil, nil
-}
-
-func (a *Adaptor) ConvertEmbeddingRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.EmbeddingRequest) (any, error) {
-	return nil, errors.New("not implemented")
-}
-
-func (a *Adaptor) ConvertOpenAIResponsesRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.OpenAIResponsesRequest) (any, error) {
-	return nil, errors.New("not implemented")
-}
-
-func (a *Adaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, requestBody io.Reader) (any, error) {
-	return channel.DoApiRequest(a, c, info, requestBody)
-}
-
-func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (usage any, err *types.NewAPIError) {
-	if info.IsStream {
-		err, usage = claude.ClaudeStreamHandler(c, resp, info, a.RequestMode)
-	} else {
-		err, usage = claude.ClaudeHandler(c, resp, a.RequestMode, info)
-	}
-	return
-}
-
-func (a *Adaptor) GetModelList() []string {
-	return ModelList
-}
-
-func (a *Adaptor) GetChannelName() string {
-	return ChannelName
-}

+ 0 - 14
relay/channel/claude_code/constants.go

@@ -1,14 +0,0 @@
-package claude_code
-
-var ModelList = []string{
-	"claude-3-5-haiku-20241022",
-	"claude-3-5-sonnet-20241022",
-	"claude-3-7-sonnet-20250219",
-	"claude-3-7-sonnet-20250219-thinking",
-	"claude-sonnet-4-20250514",
-	"claude-sonnet-4-20250514-thinking",
-	"claude-opus-4-20250514",
-	"claude-opus-4-20250514-thinking",
-}
-
-var ChannelName = "claude_code"

+ 0 - 4
relay/channel/claude_code/dto.go

@@ -1,4 +0,0 @@
-package claude_code
-
-// Claude Code uses the same DTO structures as Claude since it's based on the same API
-// This file is kept for consistency with the channel structure pattern

+ 0 - 3
relay/relay_adaptor.go

@@ -9,7 +9,6 @@ import (
 	"one-api/relay/channel/baidu"
 	"one-api/relay/channel/baidu_v2"
 	"one-api/relay/channel/claude"
-	"one-api/relay/channel/claude_code"
 	"one-api/relay/channel/cloudflare"
 	"one-api/relay/channel/cohere"
 	"one-api/relay/channel/coze"
@@ -99,8 +98,6 @@ func GetAdaptor(apiType int) channel.Adaptor {
 		return &coze.Adaptor{}
 	case constant.APITypeJimeng:
 		return &jimeng.Adaptor{}
-	case constant.APITypeClaudeCode:
-		return &claude_code.Adaptor{}
 	}
 	return nil
 }

+ 0 - 3
router/api-router.go

@@ -120,9 +120,6 @@ func SetApiRouter(router *gin.Engine) {
 			channelRoute.POST("/batch/tag", controller.BatchSetChannelTag)
 			channelRoute.GET("/tag/models", controller.GetTagModels)
 			channelRoute.POST("/copy/:id", controller.CopyChannel)
-			// Claude OAuth路由
-			channelRoute.GET("/claude/oauth/url", controller.GenerateClaudeOAuthURL)
-			channelRoute.POST("/claude/oauth/exchange", controller.ExchangeClaudeOAuthCode)
 		}
 		tokenRoute := apiRouter.Group("/token")
 		tokenRoute.Use(middleware.UserAuth())

+ 0 - 171
service/claude_oauth.go

@@ -1,171 +0,0 @@
-package service
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-	"os"
-	"strings"
-	"time"
-
-	"golang.org/x/oauth2"
-)
-
-const (
-	// Default OAuth configuration values
-	DefaultAuthorizeURL = "https://claude.ai/oauth/authorize"
-	DefaultTokenURL     = "https://console.anthropic.com/v1/oauth/token"
-	DefaultClientID     = "9d1c250a-e61b-44d9-88ed-5944d1962f5e"
-	DefaultRedirectURI  = "https://console.anthropic.com/oauth/code/callback"
-	DefaultScopes       = "user:inference"
-)
-
-// getOAuthValues returns OAuth configuration values from environment variables or defaults
-func getOAuthValues() (authorizeURL, tokenURL, clientID, redirectURI, scopes string) {
-	authorizeURL = os.Getenv("CLAUDE_AUTHORIZE_URL")
-	if authorizeURL == "" {
-		authorizeURL = DefaultAuthorizeURL
-	}
-
-	tokenURL = os.Getenv("CLAUDE_TOKEN_URL")
-	if tokenURL == "" {
-		tokenURL = DefaultTokenURL
-	}
-
-	clientID = os.Getenv("CLAUDE_CLIENT_ID")
-	if clientID == "" {
-		clientID = DefaultClientID
-	}
-
-	redirectURI = os.Getenv("CLAUDE_REDIRECT_URI")
-	if redirectURI == "" {
-		redirectURI = DefaultRedirectURI
-	}
-
-	scopes = os.Getenv("CLAUDE_SCOPES")
-	if scopes == "" {
-		scopes = DefaultScopes
-	}
-
-	return
-}
-
-type OAuth2Credentials struct {
-	AuthURL       string `json:"auth_url"`
-	CodeVerifier  string `json:"code_verifier"`
-	State         string `json:"state"`
-	CodeChallenge string `json:"code_challenge"`
-}
-
-// GetClaudeOAuthConfig returns the Claude OAuth2 configuration
-func GetClaudeOAuthConfig() *oauth2.Config {
-	authorizeURL, tokenURL, clientID, redirectURI, scopes := getOAuthValues()
-
-	return &oauth2.Config{
-		ClientID:    clientID,
-		RedirectURL: redirectURI,
-		Scopes:      strings.Split(scopes, " "),
-		Endpoint: oauth2.Endpoint{
-			AuthURL:  authorizeURL,
-			TokenURL: tokenURL,
-		},
-	}
-}
-
-// getOAuthConfig is kept for backward compatibility
-func getOAuthConfig() *oauth2.Config {
-	return GetClaudeOAuthConfig()
-}
-
-// GenerateOAuthParams generates OAuth authorization URL and related parameters
-func GenerateOAuthParams() (*OAuth2Credentials, error) {
-	config := getOAuthConfig()
-
-	// Generate PKCE parameters
-	codeVerifier := oauth2.GenerateVerifier()
-	state := oauth2.GenerateVerifier() // Reuse generator as state
-
-	// Generate authorization URL
-	authURL := config.AuthCodeURL(state,
-		oauth2.S256ChallengeOption(codeVerifier),
-		oauth2.SetAuthURLParam("code", "true"), // Claude-specific parameter
-	)
-
-	return &OAuth2Credentials{
-		AuthURL:       authURL,
-		CodeVerifier:  codeVerifier,
-		State:         state,
-		CodeChallenge: oauth2.S256ChallengeFromVerifier(codeVerifier),
-	}, nil
-}
-
-// ExchangeCode
-func ExchangeCode(authorizationCode, codeVerifier, state string, client *http.Client) (*oauth2.Token, error) {
-	config := getOAuthConfig()
-
-	if strings.Contains(authorizationCode, "#") {
-		parts := strings.Split(authorizationCode, "#")
-		if len(parts) > 0 {
-			authorizationCode = parts[0]
-		}
-	}
-
-	ctx := context.Background()
-	if client != nil {
-		ctx = context.WithValue(ctx, oauth2.HTTPClient, client)
-	}
-
-	token, err := config.Exchange(ctx, authorizationCode,
-		oauth2.VerifierOption(codeVerifier),
-		oauth2.SetAuthURLParam("state", state),
-	)
-	if err != nil {
-		return nil, fmt.Errorf("token exchange failed: %w", err)
-	}
-
-	return token, nil
-}
-
-func ParseAuthorizationCode(input string) (string, error) {
-	if input == "" {
-		return "", fmt.Errorf("please provide a valid authorization code")
-	}
-	// URLs are not allowed
-	if strings.Contains(input, "http") || strings.Contains(input, "https") {
-		return "", fmt.Errorf("authorization code cannot contain URLs")
-	}
-
-	return input, nil
-}
-
-// GetClaudeHTTPClient returns a configured HTTP client for Claude OAuth operations
-func GetClaudeHTTPClient() *http.Client {
-	return &http.Client{
-		Timeout: 30 * time.Second,
-	}
-}
-
-// RefreshClaudeToken refreshes a Claude OAuth token using the refresh token
-func RefreshClaudeToken(accessToken, refreshToken string) (*oauth2.Token, error) {
-	config := GetClaudeOAuthConfig()
-
-	// Create token from current values
-	currentToken := &oauth2.Token{
-		AccessToken:  accessToken,
-		RefreshToken: refreshToken,
-		TokenType:    "Bearer",
-	}
-
-	ctx := context.Background()
-	if client := GetClaudeHTTPClient(); client != nil {
-		ctx = context.WithValue(ctx, oauth2.HTTPClient, client)
-	}
-
-	// Refresh the token
-	newToken, err := config.TokenSource(ctx, currentToken).Token()
-	if err != nil {
-		return nil, fmt.Errorf("failed to refresh Claude token: %w", err)
-	}
-
-	return newToken, nil
-}

+ 0 - 94
service/claude_token_refresh.go

@@ -1,94 +0,0 @@
-package service
-
-import (
-	"fmt"
-	"one-api/common"
-	"one-api/constant"
-	"one-api/model"
-	"strings"
-	"time"
-
-	"github.com/bytedance/gopkg/util/gopool"
-)
-
-// StartClaudeTokenRefreshScheduler starts the scheduled token refresh for Claude Code channels
-func StartClaudeTokenRefreshScheduler() {
-	ticker := time.NewTicker(5 * time.Minute)
-	gopool.Go(func() {
-		defer ticker.Stop()
-		for range ticker.C {
-			RefreshClaudeCodeTokens()
-		}
-	})
-	common.SysLog("Claude Code token refresh scheduler started (5 minute interval)")
-}
-
-// RefreshClaudeCodeTokens refreshes tokens for all active Claude Code channels
-func RefreshClaudeCodeTokens() {
-	var channels []model.Channel
-
-	// Get all active Claude Code channels
-	err := model.DB.Where("type = ? AND status = ?", constant.ChannelTypeClaudeCode, common.ChannelStatusEnabled).Find(&channels).Error
-	if err != nil {
-		common.SysError("Failed to get Claude Code channels: " + err.Error())
-		return
-	}
-
-	refreshCount := 0
-	for _, channel := range channels {
-		if refreshTokenForChannel(&channel) {
-			refreshCount++
-		}
-	}
-
-	if refreshCount > 0 {
-		common.SysLog(fmt.Sprintf("Successfully refreshed %d Claude Code channel tokens", refreshCount))
-	}
-}
-
-// refreshTokenForChannel attempts to refresh token for a single channel
-func refreshTokenForChannel(channel *model.Channel) bool {
-	// Parse key in format: accesstoken|refreshtoken
-	if channel.Key == "" || !strings.Contains(channel.Key, "|") {
-		common.SysError(fmt.Sprintf("Channel %d has invalid key format, expected accesstoken|refreshtoken", channel.Id))
-		return false
-	}
-
-	parts := strings.Split(channel.Key, "|")
-	if len(parts) < 2 {
-		common.SysError(fmt.Sprintf("Channel %d has invalid key format, expected accesstoken|refreshtoken", channel.Id))
-		return false
-	}
-
-	accessToken := parts[0]
-	refreshToken := parts[1]
-
-	if refreshToken == "" {
-		common.SysError(fmt.Sprintf("Channel %d has empty refresh token", channel.Id))
-		return false
-	}
-
-	// Check if token needs refresh (refresh 30 minutes before expiry)
-	// if !shouldRefreshToken(accessToken) {
-	// 	return false
-	// }
-
-	// Use shared refresh function
-	newToken, err := RefreshClaudeToken(accessToken, refreshToken)
-	if err != nil {
-		common.SysError(fmt.Sprintf("Failed to refresh token for channel %d: %s", channel.Id, err.Error()))
-		return false
-	}
-
-	// Update channel with new tokens
-	newKey := fmt.Sprintf("%s|%s", newToken.AccessToken, newToken.RefreshToken)
-
-	err = model.DB.Model(channel).Update("key", newKey).Error
-	if err != nil {
-		common.SysError(fmt.Sprintf("Failed to update channel %d with new token: %s", channel.Id, err.Error()))
-		return false
-	}
-
-	common.SysLog(fmt.Sprintf("Successfully refreshed token for Claude Code channel %d (%s)", channel.Id, channel.Name))
-	return true
-}

+ 0 - 3
setting/operation_setting/operation_setting.go

@@ -13,9 +13,6 @@ var AutomaticDisableKeywords = []string{
 	"The security token included in the request is invalid",
 	"Operation not allowed",
 	"Your account is not authorized",
-	// Claude Code
-	"Invalid bearer token",
-	"OAuth authentication is currently not allowed for this endpoint",
 }
 
 func AutomaticDisableKeywordsToString() string {

+ 26 - 231
web/src/components/table/channels/modals/EditChannelModal.jsx

@@ -17,6 +17,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
 For commercial licensing, please contact [email protected]
 */
 
+import React, { useEffect, useState, useRef, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
 import {
   API,
   showError,
@@ -24,42 +26,38 @@ import {
   showSuccess,
   verifyJSON,
 } from '../../../../helpers';
+import { useIsMobile } from '../../../../hooks/common/useIsMobile.js';
+import { CHANNEL_OPTIONS } from '../../../../constants';
 import {
-  Avatar,
-  Banner,
-  Button,
-  Card,
-  Checkbox,
-  Col,
-  Form,
-  Highlight,
-  ImagePreview,
-  Input,
-  Modal,
-  Row,
   SideSheet,
   Space,
   Spin,
-  Tag,
+  Button,
   Typography,
+  Checkbox,
+  Banner,
+  Modal,
+  ImagePreview,
+  Card,
+  Tag,
+  Avatar,
+  Form,
+  Row,
+  Col,
+  Highlight,
 } from '@douyinfe/semi-ui';
 import { getChannelModels, copy, getChannelIcon, getModelCategories, selectFilter } from '../../../../helpers';
 import ModelSelectModal from './ModelSelectModal';
 import JSONEditor from '../../../common/JSONEditor';
-import { CHANNEL_OPTIONS, CLAUDE_CODE_DEFAULT_SYSTEM_PROMPT } from '../../../../constants';
 import {
-  IconBolt,
-  IconClose,
-  IconCode,
-  IconGlobe,
   IconSave,
+  IconClose,
   IconServer,
   IconSetting,
+  IconCode,
+  IconGlobe,
+  IconBolt,
 } from '@douyinfe/semi-icons';
-import React, { useEffect, useMemo, useRef, useState } from 'react';
-
-import { useIsMobile } from '../../../../hooks/common/useIsMobile.js';
-import { useTranslation } from 'react-i18next';
 
 const { Text, Title } = Typography;
 
@@ -95,8 +93,6 @@ function type2secretPrompt(type) {
       return '按照如下格式输入: AccessKey|SecretKey, 如果上游是New API,则直接输ApiKey';
     case 51:
       return '按照如下格式输入: Access Key ID|Secret Access Key';
-    case 53:
-      return '按照如下格式输入:AccessToken|RefreshToken';
     default:
       return '请输入渠道对应的鉴权密钥';
   }
@@ -149,10 +145,6 @@ const EditChannelModal = (props) => {
   const [customModel, setCustomModel] = useState('');
   const [modalImageUrl, setModalImageUrl] = useState('');
   const [isModalOpenurl, setIsModalOpenurl] = useState(false);
-  const [showOAuthModal, setShowOAuthModal] = useState(false);
-  const [authorizationCode, setAuthorizationCode] = useState('');
-  const [oauthParams, setOauthParams] = useState(null);
-  const [isExchangingCode, setIsExchangingCode] = useState(false);
   const [modelModalVisible, setModelModalVisible] = useState(false);
   const [fetchedModels, setFetchedModels] = useState([]);
   const formApiRef = useRef(null);
@@ -361,24 +353,6 @@ const EditChannelModal = (props) => {
         data.system_prompt = '';
       }
 
-      // 特殊处理Claude Code渠道的密钥拆分和系统提示词
-      if (data.type === 53) {
-        // 拆分密钥
-        if (data.key) {
-          const keyParts = data.key.split('|');
-          if (keyParts.length === 2) {
-            data.access_token = keyParts[0];
-            data.refresh_token = keyParts[1];
-          } else {
-            // 如果没有 | 分隔符,表示只有access token
-            data.access_token = data.key;
-            data.refresh_token = '';
-          }
-        }
-        // 强制设置固定系统提示词
-        data.system_prompt = CLAUDE_CODE_DEFAULT_SYSTEM_PROMPT;
-      }
-
       setInputs(data);
       if (formApiRef.current) {
         formApiRef.current.setValues(data);
@@ -502,72 +476,6 @@ const EditChannelModal = (props) => {
     }
   };
 
-  // 生成OAuth授权URL
-  const handleGenerateOAuth = async () => {
-    try {
-      setLoading(true);
-      const res = await API.get('/api/channel/claude/oauth/url');
-      if (res.data.success) {
-        setOauthParams(res.data.data);
-        setShowOAuthModal(true);
-        showSuccess(t('OAuth授权URL生成成功'));
-      } else {
-        showError(res.data.message || t('生成OAuth授权URL失败'));
-      }
-    } catch (error) {
-      showError(t('生成OAuth授权URL失败:') + error.message);
-    } finally {
-      setLoading(false);
-    }
-  };
-
-  // 交换授权码
-  const handleExchangeCode = async () => {
-    if (!authorizationCode.trim()) {
-      showError(t('请输入授权码'));
-      return;
-    }
-
-    if (!oauthParams) {
-      showError(t('OAuth参数丢失,请重新生成'));
-      return;
-    }
-
-    try {
-      setIsExchangingCode(true);
-      const res = await API.post('/api/channel/claude/oauth/exchange', {
-        authorization_code: authorizationCode,
-        code_verifier: oauthParams.code_verifier,
-        state: oauthParams.state,
-      });
-
-      if (res.data.success) {
-        const tokenData = res.data.data;
-        // 自动填充access token和refresh token
-        handleInputChange('access_token', tokenData.access_token);
-        handleInputChange('refresh_token', tokenData.refresh_token);
-        handleInputChange('key', `${tokenData.access_token}|${tokenData.refresh_token}`);
-
-        // 更新表单字段
-        if (formApiRef.current) {
-          formApiRef.current.setValue('access_token', tokenData.access_token);
-          formApiRef.current.setValue('refresh_token', tokenData.refresh_token);
-        }
-
-        setShowOAuthModal(false);
-        setAuthorizationCode('');
-        setOauthParams(null);
-        showSuccess(t('授权码交换成功,已自动填充tokens'));
-      } else {
-        showError(res.data.message || t('授权码交换失败'));
-      }
-    } catch (error) {
-      showError(t('授权码交换失败:') + error.message);
-    } finally {
-      setIsExchangingCode(false);
-    }
-  };
-
   useEffect(() => {
     const modelMap = new Map();
 
@@ -880,7 +788,7 @@ const EditChannelModal = (props) => {
   const batchExtra = batchAllowed ? (
     <Space>
       <Checkbox
-        disabled={isEdit || inputs.type === 53}
+        disabled={isEdit}
         checked={batch}
         onChange={(e) => {
           const checked = e.target.checked;
@@ -1216,49 +1124,6 @@ const EditChannelModal = (props) => {
                             />
                           )}
                         </>
-                      ) : inputs.type === 53 ? (
-                        <>
-                          <Form.Input
-                            field='access_token'
-                            label={isEdit ? t('Access Token(编辑模式下,保存的密钥不会显示)') : t('Access Token')}
-                            placeholder={t('sk-ant-xxx')}
-                            rules={isEdit ? [] : [{ required: true, message: t('请输入Access Token') }]}
-                            autoComplete='new-password'
-                            onChange={(value) => {
-                              handleInputChange('access_token', value);
-                              // 同时更新key字段,格式为access_token|refresh_token
-                              const refreshToken = inputs.refresh_token || '';
-                              handleInputChange('key', `${value}|${refreshToken}`);
-                            }}
-                            suffix={
-                              <Button
-                                size="small"
-                                type="primary"
-                                theme="light"
-                                onClick={handleGenerateOAuth}
-                              >
-                                {t('生成OAuth授权码')}
-                              </Button>
-                            }
-                            extraText={batchExtra}
-                            showClear
-                          />
-                          <Form.Input
-                            field='refresh_token'
-                            label={isEdit ? t('Refresh Token(编辑模式下,保存的密钥不会显示)') : t('Refresh Token')}
-                            placeholder={t('sk-ant-xxx(可选)')}
-                            rules={[]}
-                            autoComplete='new-password'
-                            onChange={(value) => {
-                              handleInputChange('refresh_token', value);
-                              // 同时更新key字段,格式为access_token|refresh_token
-                              const accessToken = inputs.access_token || '';
-                              handleInputChange('key', `${accessToken}|${value}`);
-                            }}
-                            extraText={batchExtra}
-                            showClear
-                          />
-                        </>
                       ) : (
                         <Form.Input
                           field='key'
@@ -1767,19 +1632,11 @@ const EditChannelModal = (props) => {
                   <Form.TextArea
                     field='system_prompt'
                     label={t('系统提示词')}
-                    placeholder={inputs.type === 53 ? CLAUDE_CODE_DEFAULT_SYSTEM_PROMPT : t('输入系统提示词,用户的系统提示词将优先于此设置')}
-                    onChange={(value) => {
-                      if (inputs.type === 53) {
-                        // Claude Code渠道系统提示词固定,不允许修改
-                        return;
-                      }
-                      handleChannelSettingsChange('system_prompt', value);
-                    }}
-                    disabled={inputs.type === 53}
-                    value={inputs.type === 53 ? CLAUDE_CODE_DEFAULT_SYSTEM_PROMPT : undefined}
+                    placeholder={t('输入系统提示词,用户的系统提示词将优先于此设置')}
+                    onChange={(value) => handleChannelSettingsChange('system_prompt', value)}
                     autosize
-                    showClear={inputs.type !== 53}
-                    extraText={inputs.type === 53 ? t('Claude Code渠道系统提示词固定为官方CLI身份,不可修改') : t('用户优先:如果用户在请求中指定了系统提示词,将优先使用用户的设置')}
+                    showClear
+                    extraText={t('用户优先:如果用户在请求中指定了系统提示词,将优先使用用户的设置')}
                   />
                 </Card>
               </div>
@@ -1803,70 +1660,8 @@ const EditChannelModal = (props) => {
         }}
         onCancel={() => setModelModalVisible(false)}
       />
-
-      {/* OAuth Authorization Modal */}
-      <Modal
-        title={t('生成Claude Code OAuth授权码')}
-        visible={showOAuthModal}
-        onCancel={() => {
-          setShowOAuthModal(false);
-          setAuthorizationCode('');
-          setOauthParams(null);
-        }}
-        onOk={handleExchangeCode}
-        okText={isExchangingCode ? t('交换中...') : t('确认')}
-        cancelText={t('取消')}
-        confirmLoading={isExchangingCode}
-        width={600}
-      >
-        <div className="space-y-4">
-          <div>
-            <Text className="text-sm font-medium mb-2 block">{t('请访问以下授权地址:')}</Text>
-            <div className="p-3 bg-gray-50 rounded-lg border">
-              <Text
-                link
-                underline
-                className="text-sm font-mono break-all cursor-pointer text-blue-600 hover:text-blue-800"
-                onClick={() => {
-                  if (oauthParams?.auth_url) {
-                    window.open(oauthParams.auth_url, '_blank');
-                  }
-                }}
-              >
-                {oauthParams?.auth_url || t('正在生成授权地址...')}
-              </Text>
-              <div className="mt-2">
-                <Text
-                  copyable={{ content: oauthParams?.auth_url }}
-                  type="tertiary"
-                  size="small"
-                >
-                  {t('复制链接')}
-                </Text>
-              </div>
-            </div>
-          </div>
-
-          <div>
-            <Text className="text-sm font-medium mb-2 block">{t('授权后,请将获得的授权码粘贴到下方:')}</Text>
-            <Input
-              value={authorizationCode}
-              onChange={setAuthorizationCode}
-              placeholder={t('请输入授权码')}
-              showClear
-              style={{ width: '100%' }}
-            />
-          </div>
-
-          <Banner
-            type="info"
-            description={t('获得授权码后,系统将自动换取access token和refresh token并填充到表单中。')}
-            className="!rounded-lg"
-          />
-        </div>
-      </Modal>
     </>
   );
 };
 
-export default EditChannelModal;
+export default EditChannelModal; 

+ 0 - 8
web/src/constants/channel.constants.js

@@ -159,14 +159,6 @@ export const CHANNEL_OPTIONS = [
     color: 'purple',
     label: 'Vidu',
   },
-  {
-    value: 53,
-    color: 'indigo',
-    label: 'Claude Code',
-  },
 ];
 
 export const MODEL_TABLE_PAGE_SIZE = 10;
-
-// Claude Code 相关常量
-export const CLAUDE_CODE_DEFAULT_SYSTEM_PROMPT = "You are Claude Code, Anthropic's official CLI for Claude.";

+ 0 - 1
web/src/helpers/render.js

@@ -358,7 +358,6 @@ export function getChannelIcon(channelType) {
       return <Ollama size={iconSize} />;
     case 14: // Anthropic Claude
     case 33: // AWS Claude
-    case 53: // Claude Code
       return <Claude.Color size={iconSize} />;
     case 41: // Vertex AI
       return <Gemini.Color size={iconSize} />;