refactor(provider): remove Gemini Web cookie-based support

This commit is contained in:
hkfires
2025-10-11 12:56:07 +08:00
parent b895018ff5
commit c3f88126e6
17 changed files with 0 additions and 296 deletions

View File

@@ -639,19 +639,6 @@ These endpoints initiate provider login flows and return a URL to open in a brow
{ "status": "ok", "url": "https://..." }
```
- POST `/gemini-web-token` — Save Gemini Web cookies directly
- Request:
```bash
curl -H 'Authorization: Bearer <MANAGEMENT_KEY>' \
-H 'Content-Type: application/json' \
-d '{"secure_1psid": "<__Secure-1PSID>", "secure_1psidts": "<__Secure-1PSIDTS>", "label": "<LABEL>"}' \
http://localhost:8317/v0/management/gemini-web-token
```
- Response:
```json
{ "status": "ok", "file": "gemini-web-<hash>.json" }
```
- GET `/qwen-auth-url` — Start Qwen login (device flow)
- Request:
```bash

View File

@@ -639,19 +639,6 @@
{ "status": "ok", "url": "https://..." }
```
- POST `/gemini-web-token` — 直接保存 Gemini Web Cookie
- 请求:
```bash
curl -H 'Authorization: Bearer <MANAGEMENT_KEY>' \
-H 'Content-Type: application/json' \
-d '{"secure_1psid": "<__Secure-1PSID>", "secure_1psidts": "<__Secure-1PSIDTS>", "label": "<LABEL>"}' \
http://localhost:8317/v0/management/gemini-web-token
```
- 响应:
```json
{ "status": "ok", "file": "gemini-web-<hash>.json" }
```
- GET `/qwen-auth-url` — 开始 Qwen 登录(设备授权流程)
- 请求:
```bash

View File

@@ -17,7 +17,6 @@ Chinese providers have now been added: [Qwen Code](https://github.com/QwenLM/qwe
- Claude Code support via OAuth login
- Qwen Code support via OAuth login
- iFlow support via OAuth login
- Gemini Web support via cookie-based login
- Streaming and non-streaming responses
- Function calling/tools support
- Multimodal input support (text and images)
@@ -99,13 +98,6 @@ You can authenticate for Gemini, OpenAI, Claude, Qwen, and/or iFlow. All can coe
Options: add `--no-browser` to print the login URL instead of opening a browser. The local OAuth callback uses port `8085`.
- Gemini Web (via Cookies):
This method authenticates by simulating a browser, using cookies obtained from the Gemini website.
```bash
./cli-proxy-api --gemini-web-auth
```
You will be prompted to enter your `__Secure-1PSID` and `__Secure-1PSIDTS` values. Please retrieve these cookies from your browser's developer tools.
- OpenAI (Codex/GPT via OAuth):
```bash
./cli-proxy-api --codex-login
@@ -334,11 +326,6 @@ The server uses a YAML configuration file (`config.yaml`) located in the project
| `openai-compatibility.*.models` | object[] | [] | The actual model name. |
| `openai-compatibility.*.models.*.name` | string | "" | The models supported by the provider. |
| `openai-compatibility.*.models.*.alias` | string | "" | The alias used in the API. |
| `gemini-web` | object | {} | Configuration specific to the Gemini Web client. |
| `gemini-web.context` | boolean | true | Enables conversation context reuse for continuous dialogue. |
| `gemini-web.gem-mode` | string | "" | Selects a predefined Gem to attach for Gemini Web requests; allowed values: `coding-partner`, `writing-editor`. When empty, no Gem is attached. |
| `gemini-web.max-chars-per-request` | integer | 1,000,000 | The maximum number of characters to send to Gemini Web in a single request. |
| `gemini-web.disable-continuation-hint` | boolean | false | Disables the continuation hint for split prompts. |
### Example Configuration File
@@ -388,12 +375,6 @@ quota-exceeded:
switch-project: true # Whether to automatically switch to another project when a quota is exceeded
switch-preview-model: true # Whether to automatically switch to a preview model when a quota is exceeded
# Gemini Web client configuration
gemini-web:
context: true # Enable conversation context reuse
gem-mode: "" # Select Gem: "coding-partner" or "writing-editor"; empty means no Gem
max-chars-per-request: 1000000 # Max characters per request
# API keys for official Generative Language API
generative-language-api-key:
- "AIzaSy...01"
@@ -604,12 +585,6 @@ Run the following command to login (Gemini OAuth on port 8085):
docker run --rm -p 8085:8085 -v /path/to/your/config.yaml:/CLIProxyAPI/config.yaml -v /path/to/your/auth-dir:/root/.cli-proxy-api eceasy/cli-proxy-api:latest /CLIProxyAPI/CLIProxyAPI --login
```
Run the following command to login (Gemini Web Cookies):
```bash
docker run -it --rm -v /path/to/your/config.yaml:/CLIProxyAPI/config.yaml -v /path/to/your/auth-dir:/root/.cli-proxy-api eceasy/cli-proxy-api:latest /CLIProxyAPI/CLIProxyAPI --gemini-web-auth
```
Run the following command to login (OpenAI OAuth on port 1455):
```bash
@@ -680,10 +655,6 @@ docker run --rm -p 8317:8317 -v /path/to/your/config.yaml:/CLIProxyAPI/config.ya
```bash
docker compose exec cli-proxy-api /CLIProxyAPI/CLIProxyAPI -no-browser --login
```
- **Gemini Web**:
```bash
docker compose exec cli-proxy-api /CLIProxyAPI/CLIProxyAPI --gemini-web-auth
```
- **OpenAI (Codex)**:
```bash
docker compose exec cli-proxy-api /CLIProxyAPI/CLIProxyAPI -no-browser --codex-login

View File

@@ -37,7 +37,6 @@
- 新增 Claude Code 支持OAuth 登录)
- 新增 Qwen Code 支持OAuth 登录)
- 新增 iFlow 支持OAuth 登录)
- 新增 Gemini Web 支持(通过 Cookie 登录)
- 支持流式与非流式响应
- 函数调用/工具支持
- 多模态输入(文本、图片)
@@ -113,13 +112,6 @@ CLIProxyAPI 的基于 Web 的管理中心。
选项:加上 `--no-browser` 可打印登录地址而不自动打开浏览器。本地 OAuth 回调端口为 `8085`。
- Gemini Web (通过 Cookie):
此方法通过模拟浏览器行为,使用从 Gemini 网站获取的 Cookie 进行身份验证。
```bash
./cli-proxy-api --gemini-web-auth
```
程序将提示您输入 `__Secure-1PSID` 和 `__Secure-1PSIDTS` 的值。请从您的浏览器开发者工具中获取这些 Cookie。
- OpenAICodex/GPTOAuth
```bash
./cli-proxy-api --codex-login
@@ -347,11 +339,6 @@ console.log(await claudeResponse.json());
| `openai-compatibility.*.models` | object[] | [] | 实际的模型名称。 |
| `openai-compatibility.*.models.*.name` | string | "" | 提供商支持的模型。 |
| `openai-compatibility.*.models.*.alias` | string | "" | 在API中使用的别名。 |
| `gemini-web` | object | {} | Gemini Web 客户端的特定配置。 |
| `gemini-web.context` | boolean | true | 是否启用会话上下文重用,以实现连续对话。 |
| `gemini-web.gem-mode` | string | "" | 选择要附加的预设 Gem`coding-partner` 或 `writing-editor`);为空表示不附加。 |
| `gemini-web.max-chars-per-request` | integer | 1,000,000 | 单次请求发送给 Gemini Web 的最大字符数。 |
| `gemini-web.disable-continuation-hint` | boolean | false | 当提示被拆分时,是否禁用连续提示的暗示。 |
### 配置文件示例
@@ -401,12 +388,6 @@ quota-exceeded:
switch-project: true # 当配额超限时是否自动切换到另一个项目
switch-preview-model: true # 当配额超限时是否自动切换到预览模型
# Gemini Web 客户端配置
gemini-web:
context: true # 启用会话上下文重用
gem-mode: "" # 选择 Gem"coding-partner" 或 "writing-editor";为空表示不附加
max-chars-per-request: 1000000 # 单次请求最大字符数
# AIStduio Gemini API 的 API 密钥
generative-language-api-key:
- "AIzaSy...01"
@@ -613,12 +594,6 @@ auth.json:
docker run --rm -p 8085:8085 -v /path/to/your/config.yaml:/CLIProxyAPI/config.yaml -v /path/to/your/auth-dir:/root/.cli-proxy-api eceasy/cli-proxy-api:latest /CLIProxyAPI/CLIProxyAPI --login
```
运行以下命令进行登录Gemini Web Cookie
```bash
docker run -it --rm -v /path/to/your/config.yaml:/CLIProxyAPI/config.yaml -v /path/to/your/auth-dir:/root/.cli-proxy-api eceasy/cli-proxy-api:latest /CLIProxyAPI/CLIProxyAPI --gemini-web-auth
```
运行以下命令进行登录OpenAI OAuth端口 1455
```bash
@@ -690,10 +665,6 @@ docker run --rm -p 8317:8317 -v /path/to/your/config.yaml:/CLIProxyAPI/config.ya
```bash
docker compose exec cli-proxy-api /CLIProxyAPI/CLIProxyAPI -no-browser --login
```
- **Gemini Web**:
```bash
docker compose exec cli-proxy-api /CLIProxyAPI/CLIProxyAPI --gemini-web-auth
```
- **OpenAI (Codex)**:
```bash
docker compose exec cli-proxy-api /CLIProxyAPI/CLIProxyAPI -no-browser --codex-login

View File

@@ -44,7 +44,6 @@ func main() {
var claudeLogin bool
var qwenLogin bool
var iflowLogin bool
var geminiWebAuth bool
var noBrowser bool
var projectID string
var configPath string
@@ -56,7 +55,6 @@ func main() {
flag.BoolVar(&claudeLogin, "claude-login", false, "Login to Claude using OAuth")
flag.BoolVar(&qwenLogin, "qwen-login", false, "Login to Qwen using OAuth")
flag.BoolVar(&iflowLogin, "iflow-login", false, "Login to iFlow using OAuth")
flag.BoolVar(&geminiWebAuth, "gemini-web-auth", false, "Auth Gemini Web using cookies")
flag.BoolVar(&noBrowser, "no-browser", false, "Don't open browser automatically for OAuth")
flag.StringVar(&projectID, "project_id", "", "Project ID (Gemini only, not required)")
flag.StringVar(&configPath, "config", DefaultConfigPath, "Configure File Path")
@@ -182,8 +180,6 @@ func main() {
cmd.DoQwenLogin(cfg, options)
} else if iflowLogin {
cmd.DoIFlowLogin(cfg, options)
} else if geminiWebAuth {
cmd.DoGeminiWebAuth(cfg)
} else {
// In cloud deploy mode without config file, just wait for shutdown signals
if isCloudDeploy && !configFileExists {

View File

@@ -79,19 +79,3 @@ quota-exceeded:
# models: # The models supported by the provider.
# - name: "moonshotai/kimi-k2:free" # The actual model name.
# alias: "kimi-k2" # The alias used in the API.
# Gemini Web settings
#gemini-web:
# # Conversation reuse: set to true to enable (default), false to disable.
# context: true
# # Maximum characters per single request to Gemini Web. Requests exceeding this
# # size split into chunks. Only the last chunk carries files and yields the final answer.
# max-chars-per-request: 1000000
# # Disable the short continuation hint appended to intermediate chunks
# # when splitting long prompts. Default is false (hint enabled by default).
# disable-continuation-hint: false
# # Gem selection (Gem Mode):
# # - "coding-partner": attach the predefined Coding partner Gem
# # - "writing-editor": attach the predefined Writing editor Gem
# # - empty: do not attach any Gem
# gem-mode: ""

View File

@@ -3,8 +3,6 @@ package management
import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
@@ -25,7 +23,6 @@ import (
geminiAuth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/gemini"
iflowauth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/iflow"
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/qwen"
// legacy client removed
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
@@ -903,65 +900,6 @@ func (h *Handler) RequestGeminiCLIToken(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok", "url": authURL, "state": state})
}
func (h *Handler) CreateGeminiWebToken(c *gin.Context) {
ctx := c.Request.Context()
var payload struct {
Secure1PSID string `json:"secure_1psid"`
Secure1PSIDTS string `json:"secure_1psidts"`
Label string `json:"label"`
}
if err := c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid body"})
return
}
payload.Secure1PSID = strings.TrimSpace(payload.Secure1PSID)
payload.Secure1PSIDTS = strings.TrimSpace(payload.Secure1PSIDTS)
payload.Label = strings.TrimSpace(payload.Label)
if payload.Secure1PSID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "secure_1psid is required"})
return
}
if payload.Secure1PSIDTS == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "secure_1psidts is required"})
return
}
if payload.Label == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "label is required"})
return
}
sha := sha256.New()
sha.Write([]byte(payload.Secure1PSID))
hash := hex.EncodeToString(sha.Sum(nil))
fileName := fmt.Sprintf("gemini-web-%s.json", hash[:16])
tokenStorage := &geminiAuth.GeminiWebTokenStorage{
Secure1PSID: payload.Secure1PSID,
Secure1PSIDTS: payload.Secure1PSIDTS,
Label: payload.Label,
}
// Provide a stable label (gemini-web-<hash>) for logging and identification
tokenStorage.Label = strings.TrimSuffix(fileName, ".json")
record := &coreauth.Auth{
ID: fileName,
Provider: "gemini-web",
FileName: fileName,
Storage: tokenStorage,
}
savedPath, errSave := h.saveTokenRecord(ctx, record)
if errSave != nil {
log.Errorf("Failed to save Gemini Web token: %v", errSave)
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save token"})
return
}
fmt.Printf("Successfully saved Gemini Web token to: %s\n", savedPath)
c.JSON(http.StatusOK, gin.H{"status": "ok", "file": filepath.Base(savedPath)})
}
func (h *Handler) RequestCodexToken(c *gin.Context) {
ctx := context.Background()

View File

@@ -419,7 +419,6 @@ func (s *Server) registerManagementRoutes() {
mgmt.GET("/anthropic-auth-url", s.mgmt.RequestAnthropicToken)
mgmt.GET("/codex-auth-url", s.mgmt.RequestCodexToken)
mgmt.GET("/gemini-cli-auth-url", s.mgmt.RequestGeminiCLIToken)
mgmt.POST("/gemini-web-token", s.mgmt.CreateGeminiWebToken)
mgmt.GET("/qwen-auth-url", s.mgmt.RequestQwenToken)
mgmt.GET("/iflow-auth-url", s.mgmt.RequestIFlowToken)
mgmt.GET("/get-auth-status", s.mgmt.GetAuthStatus)

View File

@@ -53,42 +53,6 @@ type Config struct {
// RemoteManagement nests management-related options under 'remote-management'.
RemoteManagement RemoteManagement `yaml:"remote-management" json:"-"`
// GeminiWeb groups configuration for Gemini Web client
GeminiWeb GeminiWebConfig `yaml:"gemini-web" json:"gemini-web"`
}
// GeminiWebConfig nests Gemini Web related options under 'gemini-web'.
type GeminiWebConfig struct {
// Context enables JSON-based conversation reuse.
// Defaults to true if not set in YAML (see LoadConfig).
Context bool `yaml:"context" json:"context"`
// GemMode selects a predefined Gem to attach for Gemini Web requests.
// Allowed values:
// - "coding-partner"
// - "writing-editor"
// When empty, no Gem is attached by configuration.
// This is independent from CodeMode below, which is kept for backwards compatibility.
GemMode string `yaml:"gem-mode" json:"gem-mode"`
// CodeMode enables legacy coding-mode behaviors for Gemini Web.
// Backwards compatibility: when true, the service behaves as before by
// attaching the predefined "Coding partner" Gem and enabling extra
// conveniences (e.g., XML wrapping hints). Prefer GemMode for selecting
// a Gem going forward.
CodeMode bool `yaml:"code-mode" json:"code-mode"`
// MaxCharsPerRequest caps the number of characters (runes) sent to
// Gemini Web in a single request. Long prompts will be split into
// multiple requests with a continuation hint, and only the final
// request will carry any files. When unset or <=0, a conservative
// default of 1,000,000 will be used.
MaxCharsPerRequest int `yaml:"max-chars-per-request" json:"max-chars-per-request"`
// DisableContinuationHint, when true, disables the continuation hint for split prompts.
// The hint is enabled by default.
DisableContinuationHint bool `yaml:"disable-continuation-hint,omitempty" json:"disable-continuation-hint,omitempty"`
}
// RemoteManagement holds management API configuration under 'remote-management'.
@@ -212,7 +176,6 @@ func LoadConfigOptional(configFile string, optional bool) (*Config, error) {
// Set defaults before unmarshal so that absent keys keep defaults.
cfg.LoggingToFile = true
cfg.UsageStatisticsEnabled = true
cfg.GeminiWeb.Context = true
if err = yaml.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("failed to parse config file: %w", err)
}

View File

@@ -10,9 +10,6 @@ const (
// GeminiCLI represents the Google Gemini CLI provider identifier.
GeminiCLI = "gemini-cli"
// GeminiWeb represents the Google Gemini Web provider identifier.
GeminiWeb = "gemini-web"
// Codex represents the OpenAI Codex provider identifier.
Codex = "codex"

View File

@@ -23,9 +23,6 @@ import (
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/openai/chat-completions"
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/openai/responses"
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini-web/openai/chat-completions"
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini-web/openai/responses"
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/openai/claude"
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/openai/gemini"
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/openai/gemini-cli"

View File

@@ -512,21 +512,6 @@ func (w *Watcher) reloadConfig() bool {
if oldConfig.RequestRetry != newConfig.RequestRetry {
log.Debugf(" request-retry: %d -> %d", oldConfig.RequestRetry, newConfig.RequestRetry)
}
if oldConfig.GeminiWeb.Context != newConfig.GeminiWeb.Context {
log.Debugf(" gemini-web.context: %t -> %t", oldConfig.GeminiWeb.Context, newConfig.GeminiWeb.Context)
}
if oldConfig.GeminiWeb.MaxCharsPerRequest != newConfig.GeminiWeb.MaxCharsPerRequest {
log.Debugf(" gemini-web.max-chars-per-request: %d -> %d", oldConfig.GeminiWeb.MaxCharsPerRequest, newConfig.GeminiWeb.MaxCharsPerRequest)
}
if oldConfig.GeminiWeb.DisableContinuationHint != newConfig.GeminiWeb.DisableContinuationHint {
log.Debugf(" gemini-web.disable-continuation-hint: %t -> %t", oldConfig.GeminiWeb.DisableContinuationHint, newConfig.GeminiWeb.DisableContinuationHint)
}
if oldConfig.GeminiWeb.GemMode != newConfig.GeminiWeb.GemMode {
log.Debugf(" gemini-web.gem-mode: %s -> %s", oldConfig.GeminiWeb.GemMode, newConfig.GeminiWeb.GemMode)
}
if oldConfig.GeminiWeb.CodeMode != newConfig.GeminiWeb.CodeMode {
log.Debugf(" gemini-web.code-mode: %t -> %t", oldConfig.GeminiWeb.CodeMode, newConfig.GeminiWeb.CodeMode)
}
if len(oldConfig.APIKeys) != len(newConfig.APIKeys) {
log.Debugf(" api-keys count: %d -> %d", len(oldConfig.APIKeys), len(newConfig.APIKeys))
}

View File

@@ -9,7 +9,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
conversation "github.com/router-for-me/CLIProxyAPI/v6/internal/provider/gemini-web/conversation"
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
coreexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
@@ -49,8 +48,6 @@ type BaseAPIHandler struct {
Cfg *config.SDKConfig
}
const geminiWebProvider = "gemini-web"
// NewBaseAPIHandlers creates a new API handlers instance.
// It takes a slice of clients and configuration as input.
//
@@ -140,7 +137,6 @@ func (h *BaseAPIHandler) ExecuteWithAuthManager(ctx context.Context, handlerType
if len(providers) == 0 {
return nil, &interfaces.ErrorMessage{StatusCode: http.StatusBadRequest, Error: fmt.Errorf("unknown provider for model %s", modelName)}
}
metadata := h.buildGeminiWebMetadata(handlerType, providers, rawJSON)
req := coreexecutor.Request{
Model: modelName,
Payload: cloneBytes(rawJSON),
@@ -150,7 +146,6 @@ func (h *BaseAPIHandler) ExecuteWithAuthManager(ctx context.Context, handlerType
Alt: alt,
OriginalRequest: cloneBytes(rawJSON),
SourceFormat: sdktranslator.FromString(handlerType),
Metadata: metadata,
}
resp, err := h.AuthManager.Execute(ctx, providers, req, opts)
if err != nil {
@@ -166,7 +161,6 @@ func (h *BaseAPIHandler) ExecuteCountWithAuthManager(ctx context.Context, handle
if len(providers) == 0 {
return nil, &interfaces.ErrorMessage{StatusCode: http.StatusBadRequest, Error: fmt.Errorf("unknown provider for model %s", modelName)}
}
metadata := h.buildGeminiWebMetadata(handlerType, providers, rawJSON)
req := coreexecutor.Request{
Model: modelName,
Payload: cloneBytes(rawJSON),
@@ -176,7 +170,6 @@ func (h *BaseAPIHandler) ExecuteCountWithAuthManager(ctx context.Context, handle
Alt: alt,
OriginalRequest: cloneBytes(rawJSON),
SourceFormat: sdktranslator.FromString(handlerType),
Metadata: metadata,
}
resp, err := h.AuthManager.ExecuteCount(ctx, providers, req, opts)
if err != nil {
@@ -195,7 +188,6 @@ func (h *BaseAPIHandler) ExecuteStreamWithAuthManager(ctx context.Context, handl
close(errChan)
return nil, errChan
}
metadata := h.buildGeminiWebMetadata(handlerType, providers, rawJSON)
req := coreexecutor.Request{
Model: modelName,
Payload: cloneBytes(rawJSON),
@@ -205,7 +197,6 @@ func (h *BaseAPIHandler) ExecuteStreamWithAuthManager(ctx context.Context, handl
Alt: alt,
OriginalRequest: cloneBytes(rawJSON),
SourceFormat: sdktranslator.FromString(handlerType),
Metadata: metadata,
}
chunks, err := h.AuthManager.ExecuteStream(ctx, providers, req, opts)
if err != nil {
@@ -241,18 +232,6 @@ func cloneBytes(src []byte) []byte {
return dst
}
func (h *BaseAPIHandler) buildGeminiWebMetadata(handlerType string, providers []string, rawJSON []byte) map[string]any {
if !util.InArray(providers, geminiWebProvider) {
return nil
}
meta := make(map[string]any)
msgs := conversation.ExtractMessages(handlerType, rawJSON)
if len(msgs) > 0 {
meta[conversation.MetadataMessagesKey] = msgs
}
return meta
}
// WriteErrorResponse writes an error message to the response writer using the HTTP status embedded in the message.
func (h *BaseAPIHandler) WriteErrorResponse(c *gin.Context, msg *interfaces.ErrorMessage) {
status := http.StatusInternalServerError

View File

@@ -13,7 +13,6 @@ func init() {
registerRefreshLead("iflow", func() Authenticator { return NewIFlowAuthenticator() })
registerRefreshLead("gemini", func() Authenticator { return NewGeminiAuthenticator() })
registerRefreshLead("gemini-cli", func() Authenticator { return NewGeminiAuthenticator() })
registerRefreshLead("gemini-web", func() Authenticator { return NewGeminiWebAuthenticator() })
}
func registerRefreshLead(provider string, factory func() Authenticator) {

View File

@@ -285,9 +285,6 @@ func (m *Manager) executeWithProvider(ctx context.Context, provider string, req
log.Debugf("Use API key %s for model %s", util.HideAPIKey(accountInfo), req.Model)
} else if accountType == "oauth" {
log.Debugf("Use OAuth %s for model %s", accountInfo, req.Model)
} else if accountType == "cookie" {
// Only Gemini Web uses cookie; print stable account label as-is.
log.Debugf("Use Cookie %s for model %s", accountInfo, req.Model)
}
tried[auth.ID] = struct{}{}
@@ -333,8 +330,6 @@ func (m *Manager) executeCountWithProvider(ctx context.Context, provider string,
log.Debugf("Use API key %s for model %s", util.HideAPIKey(accountInfo), req.Model)
} else if accountType == "oauth" {
log.Debugf("Use OAuth %s for model %s", accountInfo, req.Model)
} else if accountType == "cookie" {
log.Debugf("Use Cookie %s for model %s", accountInfo, req.Model)
}
tried[auth.ID] = struct{}{}
@@ -380,8 +375,6 @@ func (m *Manager) executeStreamWithProvider(ctx context.Context, provider string
log.Debugf("Use API key %s for model %s", util.HideAPIKey(accountInfo), req.Model)
} else if accountType == "oauth" {
log.Debugf("Use OAuth %s for model %s", accountInfo, req.Model)
} else if accountType == "cookie" {
log.Debugf("Use Cookie %s for model %s", accountInfo, req.Model)
}
tried[auth.ID] = struct{}{}

View File

@@ -134,24 +134,6 @@ func (a *Auth) AccountInfo() (string, string) {
if a == nil {
return "", ""
}
// For Gemini Web, prefer explicit cookie label for stability.
if strings.ToLower(a.Provider) == "gemini-web" {
// Prefer explicit label written into auth file (e.g., gemini-web-<hash>)
if a.Metadata != nil {
if v, ok := a.Metadata["label"].(string); ok && strings.TrimSpace(v) != "" {
return "cookie", strings.TrimSpace(v)
}
}
// Minimal fallback to cookie value for backward compatibility
if a.Metadata != nil {
if v, ok := a.Metadata["secure_1psid"].(string); ok && v != "" {
return "cookie", v
}
if v, ok := a.Metadata["__Secure-1PSID"].(string); ok && v != "" {
return "cookie", v
}
}
}
// For Gemini CLI, include project ID in the OAuth account info if present.
if strings.ToLower(a.Provider) == "gemini-cli" {
if a.Metadata != nil {

View File

@@ -14,8 +14,6 @@ import (
"github.com/router-for-me/CLIProxyAPI/v6/internal/api"
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
geminiwebclient "github.com/router-for-me/CLIProxyAPI/v6/internal/provider/gemini-web"
conversation "github.com/router-for-me/CLIProxyAPI/v6/internal/provider/gemini-web/conversation"
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
"github.com/router-for-me/CLIProxyAPI/v6/internal/runtime/executor"
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/usage"
@@ -207,23 +205,6 @@ func (s *Service) applyCoreAuthRemoval(ctx context.Context, id string) {
}
GlobalModelRegistry().UnregisterClient(id)
if existing, ok := s.coreManager.GetByID(id); ok && existing != nil {
if strings.EqualFold(existing.Provider, "gemini-web") {
// Prefer the stable cookie label stored in metadata when available.
var label string
if existing.Metadata != nil {
if v, ok := existing.Metadata["label"].(string); ok {
label = strings.TrimSpace(v)
}
}
if label == "" {
label = strings.TrimSpace(existing.Label)
}
if label != "" {
if err := conversation.RemoveMatchesByLabel(label); err != nil {
log.Debugf("failed to remove gemini web sticky entries for %s: %v", label, err)
}
}
}
existing.Disabled = true
existing.Status = coreauth.StatusDisabled
if _, err := s.coreManager.Update(ctx, existing); err != nil {
@@ -271,9 +252,6 @@ func (s *Service) ensureExecutorsForAuth(a *coreauth.Auth) {
s.coreManager.RegisterExecutor(executor.NewGeminiExecutor(s.cfg))
case "gemini-cli":
s.coreManager.RegisterExecutor(executor.NewGeminiCLIExecutor(s.cfg))
case "gemini-web":
s.coreManager.RegisterExecutor(executor.NewGeminiWebExecutor(s.cfg))
s.coreManager.EnableGeminiWebStickySelector()
case "claude":
s.coreManager.RegisterExecutor(executor.NewClaudeExecutor(s.cfg))
case "codex":
@@ -536,8 +514,6 @@ func (s *Service) registerModelsForAuth(a *coreauth.Auth) {
models = registry.GetGeminiModels()
case "gemini-cli":
models = registry.GetGeminiCLIModels()
case "gemini-web":
models = geminiwebclient.GetGeminiWebAliasedModels()
case "claude":
models = registry.GetClaudeModels()
case "codex":