mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-02 20:40:52 +08:00
refactor(provider): remove Gemini Web cookie-based support
This commit is contained in:
@@ -639,19 +639,6 @@ These endpoints initiate provider login flows and return a URL to open in a brow
|
|||||||
{ "status": "ok", "url": "https://..." }
|
{ "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)
|
- GET `/qwen-auth-url` — Start Qwen login (device flow)
|
||||||
- Request:
|
- Request:
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -639,19 +639,6 @@
|
|||||||
{ "status": "ok", "url": "https://..." }
|
{ "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 登录(设备授权流程)
|
- GET `/qwen-auth-url` — 开始 Qwen 登录(设备授权流程)
|
||||||
- 请求:
|
- 请求:
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
29
README.md
29
README.md
@@ -17,7 +17,6 @@ Chinese providers have now been added: [Qwen Code](https://github.com/QwenLM/qwe
|
|||||||
- Claude Code support via OAuth login
|
- Claude Code support via OAuth login
|
||||||
- Qwen Code support via OAuth login
|
- Qwen Code support via OAuth login
|
||||||
- iFlow support via OAuth login
|
- iFlow support via OAuth login
|
||||||
- Gemini Web support via cookie-based login
|
|
||||||
- Streaming and non-streaming responses
|
- Streaming and non-streaming responses
|
||||||
- Function calling/tools support
|
- Function calling/tools support
|
||||||
- Multimodal input support (text and images)
|
- 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`.
|
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):
|
- OpenAI (Codex/GPT via OAuth):
|
||||||
```bash
|
```bash
|
||||||
./cli-proxy-api --codex-login
|
./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` | object[] | [] | The actual model name. |
|
||||||
| `openai-compatibility.*.models.*.name` | string | "" | The models supported by the provider. |
|
| `openai-compatibility.*.models.*.name` | string | "" | The models supported by the provider. |
|
||||||
| `openai-compatibility.*.models.*.alias` | string | "" | The alias used in the API. |
|
| `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
|
### 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-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
|
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
|
# API keys for official Generative Language API
|
||||||
generative-language-api-key:
|
generative-language-api-key:
|
||||||
- "AIzaSy...01"
|
- "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
|
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):
|
Run the following command to login (OpenAI OAuth on port 1455):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -680,10 +655,6 @@ docker run --rm -p 8317:8317 -v /path/to/your/config.yaml:/CLIProxyAPI/config.ya
|
|||||||
```bash
|
```bash
|
||||||
docker compose exec cli-proxy-api /CLIProxyAPI/CLIProxyAPI -no-browser --login
|
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)**:
|
- **OpenAI (Codex)**:
|
||||||
```bash
|
```bash
|
||||||
docker compose exec cli-proxy-api /CLIProxyAPI/CLIProxyAPI -no-browser --codex-login
|
docker compose exec cli-proxy-api /CLIProxyAPI/CLIProxyAPI -no-browser --codex-login
|
||||||
|
|||||||
29
README_CN.md
29
README_CN.md
@@ -37,7 +37,6 @@
|
|||||||
- 新增 Claude Code 支持(OAuth 登录)
|
- 新增 Claude Code 支持(OAuth 登录)
|
||||||
- 新增 Qwen Code 支持(OAuth 登录)
|
- 新增 Qwen Code 支持(OAuth 登录)
|
||||||
- 新增 iFlow 支持(OAuth 登录)
|
- 新增 iFlow 支持(OAuth 登录)
|
||||||
- 新增 Gemini Web 支持(通过 Cookie 登录)
|
|
||||||
- 支持流式与非流式响应
|
- 支持流式与非流式响应
|
||||||
- 函数调用/工具支持
|
- 函数调用/工具支持
|
||||||
- 多模态输入(文本、图片)
|
- 多模态输入(文本、图片)
|
||||||
@@ -113,13 +112,6 @@ CLIProxyAPI 的基于 Web 的管理中心。
|
|||||||
|
|
||||||
选项:加上 `--no-browser` 可打印登录地址而不自动打开浏览器。本地 OAuth 回调端口为 `8085`。
|
选项:加上 `--no-browser` 可打印登录地址而不自动打开浏览器。本地 OAuth 回调端口为 `8085`。
|
||||||
|
|
||||||
- Gemini Web (通过 Cookie):
|
|
||||||
此方法通过模拟浏览器行为,使用从 Gemini 网站获取的 Cookie 进行身份验证。
|
|
||||||
```bash
|
|
||||||
./cli-proxy-api --gemini-web-auth
|
|
||||||
```
|
|
||||||
程序将提示您输入 `__Secure-1PSID` 和 `__Secure-1PSIDTS` 的值。请从您的浏览器开发者工具中获取这些 Cookie。
|
|
||||||
|
|
||||||
- OpenAI(Codex/GPT,OAuth):
|
- OpenAI(Codex/GPT,OAuth):
|
||||||
```bash
|
```bash
|
||||||
./cli-proxy-api --codex-login
|
./cli-proxy-api --codex-login
|
||||||
@@ -347,11 +339,6 @@ console.log(await claudeResponse.json());
|
|||||||
| `openai-compatibility.*.models` | object[] | [] | 实际的模型名称。 |
|
| `openai-compatibility.*.models` | object[] | [] | 实际的模型名称。 |
|
||||||
| `openai-compatibility.*.models.*.name` | string | "" | 提供商支持的模型。 |
|
| `openai-compatibility.*.models.*.name` | string | "" | 提供商支持的模型。 |
|
||||||
| `openai-compatibility.*.models.*.alias` | string | "" | 在API中使用的别名。 |
|
| `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-project: true # 当配额超限时是否自动切换到另一个项目
|
||||||
switch-preview-model: 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 密钥
|
# AIStduio Gemini API 的 API 密钥
|
||||||
generative-language-api-key:
|
generative-language-api-key:
|
||||||
- "AIzaSy...01"
|
- "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
|
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):
|
运行以下命令进行登录(OpenAI OAuth,端口 1455):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -690,10 +665,6 @@ docker run --rm -p 8317:8317 -v /path/to/your/config.yaml:/CLIProxyAPI/config.ya
|
|||||||
```bash
|
```bash
|
||||||
docker compose exec cli-proxy-api /CLIProxyAPI/CLIProxyAPI -no-browser --login
|
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)**:
|
- **OpenAI (Codex)**:
|
||||||
```bash
|
```bash
|
||||||
docker compose exec cli-proxy-api /CLIProxyAPI/CLIProxyAPI -no-browser --codex-login
|
docker compose exec cli-proxy-api /CLIProxyAPI/CLIProxyAPI -no-browser --codex-login
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ func main() {
|
|||||||
var claudeLogin bool
|
var claudeLogin bool
|
||||||
var qwenLogin bool
|
var qwenLogin bool
|
||||||
var iflowLogin bool
|
var iflowLogin bool
|
||||||
var geminiWebAuth bool
|
|
||||||
var noBrowser bool
|
var noBrowser bool
|
||||||
var projectID string
|
var projectID string
|
||||||
var configPath string
|
var configPath string
|
||||||
@@ -56,7 +55,6 @@ func main() {
|
|||||||
flag.BoolVar(&claudeLogin, "claude-login", false, "Login to Claude using OAuth")
|
flag.BoolVar(&claudeLogin, "claude-login", false, "Login to Claude using OAuth")
|
||||||
flag.BoolVar(&qwenLogin, "qwen-login", false, "Login to Qwen 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(&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.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(&projectID, "project_id", "", "Project ID (Gemini only, not required)")
|
||||||
flag.StringVar(&configPath, "config", DefaultConfigPath, "Configure File Path")
|
flag.StringVar(&configPath, "config", DefaultConfigPath, "Configure File Path")
|
||||||
@@ -182,8 +180,6 @@ func main() {
|
|||||||
cmd.DoQwenLogin(cfg, options)
|
cmd.DoQwenLogin(cfg, options)
|
||||||
} else if iflowLogin {
|
} else if iflowLogin {
|
||||||
cmd.DoIFlowLogin(cfg, options)
|
cmd.DoIFlowLogin(cfg, options)
|
||||||
} else if geminiWebAuth {
|
|
||||||
cmd.DoGeminiWebAuth(cfg)
|
|
||||||
} else {
|
} else {
|
||||||
// In cloud deploy mode without config file, just wait for shutdown signals
|
// In cloud deploy mode without config file, just wait for shutdown signals
|
||||||
if isCloudDeploy && !configFileExists {
|
if isCloudDeploy && !configFileExists {
|
||||||
|
|||||||
@@ -79,19 +79,3 @@ quota-exceeded:
|
|||||||
# models: # The models supported by the provider.
|
# models: # The models supported by the provider.
|
||||||
# - name: "moonshotai/kimi-k2:free" # The actual model name.
|
# - name: "moonshotai/kimi-k2:free" # The actual model name.
|
||||||
# alias: "kimi-k2" # The alias used in the API.
|
# 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: ""
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ package management
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -25,7 +23,6 @@ import (
|
|||||||
geminiAuth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/gemini"
|
geminiAuth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/gemini"
|
||||||
iflowauth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/iflow"
|
iflowauth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/iflow"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/qwen"
|
"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/interfaces"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
"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})
|
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) {
|
func (h *Handler) RequestCodexToken(c *gin.Context) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
|||||||
@@ -419,7 +419,6 @@ func (s *Server) registerManagementRoutes() {
|
|||||||
mgmt.GET("/anthropic-auth-url", s.mgmt.RequestAnthropicToken)
|
mgmt.GET("/anthropic-auth-url", s.mgmt.RequestAnthropicToken)
|
||||||
mgmt.GET("/codex-auth-url", s.mgmt.RequestCodexToken)
|
mgmt.GET("/codex-auth-url", s.mgmt.RequestCodexToken)
|
||||||
mgmt.GET("/gemini-cli-auth-url", s.mgmt.RequestGeminiCLIToken)
|
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("/qwen-auth-url", s.mgmt.RequestQwenToken)
|
||||||
mgmt.GET("/iflow-auth-url", s.mgmt.RequestIFlowToken)
|
mgmt.GET("/iflow-auth-url", s.mgmt.RequestIFlowToken)
|
||||||
mgmt.GET("/get-auth-status", s.mgmt.GetAuthStatus)
|
mgmt.GET("/get-auth-status", s.mgmt.GetAuthStatus)
|
||||||
|
|||||||
@@ -53,42 +53,6 @@ type Config struct {
|
|||||||
|
|
||||||
// RemoteManagement nests management-related options under 'remote-management'.
|
// RemoteManagement nests management-related options under 'remote-management'.
|
||||||
RemoteManagement RemoteManagement `yaml:"remote-management" json:"-"`
|
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'.
|
// 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.
|
// Set defaults before unmarshal so that absent keys keep defaults.
|
||||||
cfg.LoggingToFile = true
|
cfg.LoggingToFile = true
|
||||||
cfg.UsageStatisticsEnabled = true
|
cfg.UsageStatisticsEnabled = true
|
||||||
cfg.GeminiWeb.Context = true
|
|
||||||
if err = yaml.Unmarshal(data, &cfg); err != nil {
|
if err = yaml.Unmarshal(data, &cfg); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse config file: %w", err)
|
return nil, fmt.Errorf("failed to parse config file: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,6 @@ const (
|
|||||||
// GeminiCLI represents the Google Gemini CLI provider identifier.
|
// GeminiCLI represents the Google Gemini CLI provider identifier.
|
||||||
GeminiCLI = "gemini-cli"
|
GeminiCLI = "gemini-cli"
|
||||||
|
|
||||||
// GeminiWeb represents the Google Gemini Web provider identifier.
|
|
||||||
GeminiWeb = "gemini-web"
|
|
||||||
|
|
||||||
// Codex represents the OpenAI Codex provider identifier.
|
// Codex represents the OpenAI Codex provider identifier.
|
||||||
Codex = "codex"
|
Codex = "codex"
|
||||||
|
|
||||||
|
|||||||
@@ -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/chat-completions"
|
||||||
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/openai/responses"
|
_ "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/claude"
|
||||||
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/openai/gemini"
|
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/openai/gemini"
|
||||||
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/openai/gemini-cli"
|
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/openai/gemini-cli"
|
||||||
|
|||||||
@@ -512,21 +512,6 @@ func (w *Watcher) reloadConfig() bool {
|
|||||||
if oldConfig.RequestRetry != newConfig.RequestRetry {
|
if oldConfig.RequestRetry != newConfig.RequestRetry {
|
||||||
log.Debugf(" request-retry: %d -> %d", 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) {
|
if len(oldConfig.APIKeys) != len(newConfig.APIKeys) {
|
||||||
log.Debugf(" api-keys count: %d -> %d", len(oldConfig.APIKeys), len(newConfig.APIKeys))
|
log.Debugf(" api-keys count: %d -> %d", len(oldConfig.APIKeys), len(newConfig.APIKeys))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
"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"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||||
coreexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
coreexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||||
@@ -49,8 +48,6 @@ type BaseAPIHandler struct {
|
|||||||
Cfg *config.SDKConfig
|
Cfg *config.SDKConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
const geminiWebProvider = "gemini-web"
|
|
||||||
|
|
||||||
// NewBaseAPIHandlers creates a new API handlers instance.
|
// NewBaseAPIHandlers creates a new API handlers instance.
|
||||||
// It takes a slice of clients and configuration as input.
|
// 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 {
|
if len(providers) == 0 {
|
||||||
return nil, &interfaces.ErrorMessage{StatusCode: http.StatusBadRequest, Error: fmt.Errorf("unknown provider for model %s", modelName)}
|
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{
|
req := coreexecutor.Request{
|
||||||
Model: modelName,
|
Model: modelName,
|
||||||
Payload: cloneBytes(rawJSON),
|
Payload: cloneBytes(rawJSON),
|
||||||
@@ -150,7 +146,6 @@ func (h *BaseAPIHandler) ExecuteWithAuthManager(ctx context.Context, handlerType
|
|||||||
Alt: alt,
|
Alt: alt,
|
||||||
OriginalRequest: cloneBytes(rawJSON),
|
OriginalRequest: cloneBytes(rawJSON),
|
||||||
SourceFormat: sdktranslator.FromString(handlerType),
|
SourceFormat: sdktranslator.FromString(handlerType),
|
||||||
Metadata: metadata,
|
|
||||||
}
|
}
|
||||||
resp, err := h.AuthManager.Execute(ctx, providers, req, opts)
|
resp, err := h.AuthManager.Execute(ctx, providers, req, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -166,7 +161,6 @@ func (h *BaseAPIHandler) ExecuteCountWithAuthManager(ctx context.Context, handle
|
|||||||
if len(providers) == 0 {
|
if len(providers) == 0 {
|
||||||
return nil, &interfaces.ErrorMessage{StatusCode: http.StatusBadRequest, Error: fmt.Errorf("unknown provider for model %s", modelName)}
|
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{
|
req := coreexecutor.Request{
|
||||||
Model: modelName,
|
Model: modelName,
|
||||||
Payload: cloneBytes(rawJSON),
|
Payload: cloneBytes(rawJSON),
|
||||||
@@ -176,7 +170,6 @@ func (h *BaseAPIHandler) ExecuteCountWithAuthManager(ctx context.Context, handle
|
|||||||
Alt: alt,
|
Alt: alt,
|
||||||
OriginalRequest: cloneBytes(rawJSON),
|
OriginalRequest: cloneBytes(rawJSON),
|
||||||
SourceFormat: sdktranslator.FromString(handlerType),
|
SourceFormat: sdktranslator.FromString(handlerType),
|
||||||
Metadata: metadata,
|
|
||||||
}
|
}
|
||||||
resp, err := h.AuthManager.ExecuteCount(ctx, providers, req, opts)
|
resp, err := h.AuthManager.ExecuteCount(ctx, providers, req, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -195,7 +188,6 @@ func (h *BaseAPIHandler) ExecuteStreamWithAuthManager(ctx context.Context, handl
|
|||||||
close(errChan)
|
close(errChan)
|
||||||
return nil, errChan
|
return nil, errChan
|
||||||
}
|
}
|
||||||
metadata := h.buildGeminiWebMetadata(handlerType, providers, rawJSON)
|
|
||||||
req := coreexecutor.Request{
|
req := coreexecutor.Request{
|
||||||
Model: modelName,
|
Model: modelName,
|
||||||
Payload: cloneBytes(rawJSON),
|
Payload: cloneBytes(rawJSON),
|
||||||
@@ -205,7 +197,6 @@ func (h *BaseAPIHandler) ExecuteStreamWithAuthManager(ctx context.Context, handl
|
|||||||
Alt: alt,
|
Alt: alt,
|
||||||
OriginalRequest: cloneBytes(rawJSON),
|
OriginalRequest: cloneBytes(rawJSON),
|
||||||
SourceFormat: sdktranslator.FromString(handlerType),
|
SourceFormat: sdktranslator.FromString(handlerType),
|
||||||
Metadata: metadata,
|
|
||||||
}
|
}
|
||||||
chunks, err := h.AuthManager.ExecuteStream(ctx, providers, req, opts)
|
chunks, err := h.AuthManager.ExecuteStream(ctx, providers, req, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -241,18 +232,6 @@ func cloneBytes(src []byte) []byte {
|
|||||||
return dst
|
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.
|
// 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) {
|
func (h *BaseAPIHandler) WriteErrorResponse(c *gin.Context, msg *interfaces.ErrorMessage) {
|
||||||
status := http.StatusInternalServerError
|
status := http.StatusInternalServerError
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ func init() {
|
|||||||
registerRefreshLead("iflow", func() Authenticator { return NewIFlowAuthenticator() })
|
registerRefreshLead("iflow", func() Authenticator { return NewIFlowAuthenticator() })
|
||||||
registerRefreshLead("gemini", func() Authenticator { return NewGeminiAuthenticator() })
|
registerRefreshLead("gemini", func() Authenticator { return NewGeminiAuthenticator() })
|
||||||
registerRefreshLead("gemini-cli", 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) {
|
func registerRefreshLead(provider string, factory func() Authenticator) {
|
||||||
|
|||||||
@@ -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)
|
log.Debugf("Use API key %s for model %s", util.HideAPIKey(accountInfo), req.Model)
|
||||||
} else if accountType == "oauth" {
|
} else if accountType == "oauth" {
|
||||||
log.Debugf("Use OAuth %s for model %s", accountInfo, req.Model)
|
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{}{}
|
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)
|
log.Debugf("Use API key %s for model %s", util.HideAPIKey(accountInfo), req.Model)
|
||||||
} else if accountType == "oauth" {
|
} else if accountType == "oauth" {
|
||||||
log.Debugf("Use OAuth %s for model %s", accountInfo, req.Model)
|
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{}{}
|
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)
|
log.Debugf("Use API key %s for model %s", util.HideAPIKey(accountInfo), req.Model)
|
||||||
} else if accountType == "oauth" {
|
} else if accountType == "oauth" {
|
||||||
log.Debugf("Use OAuth %s for model %s", accountInfo, req.Model)
|
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{}{}
|
tried[auth.ID] = struct{}{}
|
||||||
|
|||||||
@@ -134,24 +134,6 @@ func (a *Auth) AccountInfo() (string, string) {
|
|||||||
if a == nil {
|
if a == nil {
|
||||||
return "", ""
|
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.
|
// For Gemini CLI, include project ID in the OAuth account info if present.
|
||||||
if strings.ToLower(a.Provider) == "gemini-cli" {
|
if strings.ToLower(a.Provider) == "gemini-cli" {
|
||||||
if a.Metadata != nil {
|
if a.Metadata != nil {
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ import (
|
|||||||
|
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/api"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/api"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
"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/registry"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/runtime/executor"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/runtime/executor"
|
||||||
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/usage"
|
_ "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)
|
GlobalModelRegistry().UnregisterClient(id)
|
||||||
if existing, ok := s.coreManager.GetByID(id); ok && existing != nil {
|
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.Disabled = true
|
||||||
existing.Status = coreauth.StatusDisabled
|
existing.Status = coreauth.StatusDisabled
|
||||||
if _, err := s.coreManager.Update(ctx, existing); err != nil {
|
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))
|
s.coreManager.RegisterExecutor(executor.NewGeminiExecutor(s.cfg))
|
||||||
case "gemini-cli":
|
case "gemini-cli":
|
||||||
s.coreManager.RegisterExecutor(executor.NewGeminiCLIExecutor(s.cfg))
|
s.coreManager.RegisterExecutor(executor.NewGeminiCLIExecutor(s.cfg))
|
||||||
case "gemini-web":
|
|
||||||
s.coreManager.RegisterExecutor(executor.NewGeminiWebExecutor(s.cfg))
|
|
||||||
s.coreManager.EnableGeminiWebStickySelector()
|
|
||||||
case "claude":
|
case "claude":
|
||||||
s.coreManager.RegisterExecutor(executor.NewClaudeExecutor(s.cfg))
|
s.coreManager.RegisterExecutor(executor.NewClaudeExecutor(s.cfg))
|
||||||
case "codex":
|
case "codex":
|
||||||
@@ -536,8 +514,6 @@ func (s *Service) registerModelsForAuth(a *coreauth.Auth) {
|
|||||||
models = registry.GetGeminiModels()
|
models = registry.GetGeminiModels()
|
||||||
case "gemini-cli":
|
case "gemini-cli":
|
||||||
models = registry.GetGeminiCLIModels()
|
models = registry.GetGeminiCLIModels()
|
||||||
case "gemini-web":
|
|
||||||
models = geminiwebclient.GetGeminiWebAliasedModels()
|
|
||||||
case "claude":
|
case "claude":
|
||||||
models = registry.GetClaudeModels()
|
models = registry.GetClaudeModels()
|
||||||
case "codex":
|
case "codex":
|
||||||
|
|||||||
Reference in New Issue
Block a user