Skip to content

Commit 5ec0d62

Browse files
committed
feat(openai): add image APIs and remove iflow
Add OpenAI image generation and edit support plus Codex builtins for gpt-5.5 and gpt-image-2. Remove the deprecated iFlow provider across auth, runtime, config, and docs to stay aligned with upstream.
1 parent aa297d5 commit 5ec0d62

47 files changed

Lines changed: 1709 additions & 3788 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,16 @@ Get 10% OFF GLM CODING PLAN:https://z.ai/subscribe?ic=8JVLJQFSKB
5050
- OpenAI/Gemini/Claude compatible API endpoints for CLI models
5151
- OpenAI Codex support (GPT models) via OAuth login
5252
- Claude Code support via OAuth login
53-
- iFlow support via OAuth login
5453
- Amp CLI and IDE extensions support with provider routing
5554
- Streaming and non-streaming responses
5655
- Function calling/tools support
5756
- Multimodal input support (text and images)
58-
- Multiple accounts with round-robin load balancing (Gemini, OpenAI, Claude and iFlow)
59-
- Simple CLI authentication flows (Gemini, OpenAI, Claude and iFlow)
57+
- Multiple accounts with round-robin load balancing (Gemini, OpenAI, Claude)
58+
- Simple CLI authentication flows (Gemini, OpenAI, Claude)
6059
- Generative Language API Key support
6160
- AI Studio Build multi-account load balancing
6261
- Gemini CLI multi-account load balancing
6362
- Claude Code multi-account load balancing
64-
- iFlow multi-account load balancing
6563
- OpenAI Codex multi-account load balancing
6664
- OpenAI-compatible upstream providers via config (e.g., OpenRouter)
6765
- Reusable Go SDK for embedding the proxy (see `docs/sdk-usage.md`)
@@ -177,7 +175,7 @@ helping users to immersively use AI assistants across applications on controlled
177175

178176
### [ProxyPal](https://github.com/buddingnewinsights/proxypal)
179177

180-
Cross-platform desktop app (macOS, Windows, Linux) wrapping CLIProxyAPI with a native GUI. Connects Claude, ChatGPT, Gemini, GitHub Copilot, iFlow, and custom OpenAI-compatible endpoints with usage analytics, request monitoring, and auto-configuration for popular coding tools - no API keys needed.
178+
Cross-platform desktop app (macOS, Windows, Linux) wrapping CLIProxyAPI with a native GUI. Connects Claude, ChatGPT, Gemini, GitHub Copilot, and custom OpenAI-compatible endpoints with usage analytics, request monitoring, and auto-configuration for popular coding tools - no API keys needed.
181179

182180
### [CLIProxyAPI Quota Inspector](https://github.com/AllenReder/CLIProxyAPI-Quota-Inspector)
183181

README_CN.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,15 @@ GLM CODING PLAN 是专为AI编码打造的订阅套餐,每月最低仅需20元
5151
- 为 CLI 模型提供 OpenAI/Gemini/Claude/Codex 兼容的 API 端点
5252
- 新增 OpenAI Codex(GPT 系列)支持(OAuth 登录)
5353
- 新增 Claude Code 支持(OAuth 登录)
54-
- 新增 iFlow 支持(OAuth 登录)
5554
- 支持流式与非流式响应
5655
- 函数调用/工具支持
5756
- 多模态输入(文本、图片)
58-
- 多账户支持与轮询负载均衡(Gemini、OpenAI、Claude 与 iFlow
59-
- 简单的 CLI 身份验证流程(Gemini、OpenAI、Claude 与 iFlow
57+
- 多账户支持与轮询负载均衡(Gemini、OpenAI、Claude)
58+
- 简单的 CLI 身份验证流程(Gemini、OpenAI、Claude)
6059
- 支持 Gemini AIStudio API 密钥
6160
- 支持 AI Studio Build 多账户轮询
6261
- 支持 Gemini CLI 多账户轮询
6362
- 支持 Claude Code 多账户轮询
64-
- 支持 iFlow 多账户轮询
6563
- 支持 OpenAI Codex 多账户轮询
6664
- 通过配置接入上游 OpenAI 兼容提供商(例如 OpenRouter)
6765
- 可复用的 Go SDK(见 `docs/sdk-usage_CN.md`
@@ -173,7 +171,7 @@ Shadow AI 是一款专为受限环境设计的 AI 辅助工具。提供无窗口
173171

174172
### [ProxyPal](https://github.com/buddingnewinsights/proxypal)
175173

176-
跨平台桌面应用(macOS、Windows、Linux),以原生 GUI 封装 CLIProxyAPI。支持连接 Claude、ChatGPT、Gemini、GitHub Copilot、iFlow 及自定义 OpenAI 兼容端点,具备使用分析、请求监控和热门编程工具自动配置功能,无需 API 密钥。
174+
跨平台桌面应用(macOS、Windows、Linux),以原生 GUI 封装 CLIProxyAPI。支持连接 Claude、ChatGPT、Gemini、GitHub Copilot 及自定义 OpenAI 兼容端点,具备使用分析、请求监控和热门编程工具自动配置功能,无需 API 密钥。
177175

178176
### [CLIProxyAPI Quota Inspector](https://github.com/AllenReder/CLIProxyAPI-Quota-Inspector)
179177

README_JA.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,16 @@ GLM CODING PLANを10%割引で取得:https://z.ai/subscribe?ic=8JVLJQFSKB
5050
- CLIモデル向けのOpenAI/Gemini/Claude互換APIエンドポイント
5151
- OAuthログインによるOpenAI Codexサポート(GPTモデル)
5252
- OAuthログインによるClaude Codeサポート
53-
- OAuthログインによるiFlowサポート
5453
- プロバイダールーティングによるAmp CLIおよびIDE拡張機能のサポート
5554
- ストリーミングおよび非ストリーミングレスポンス
5655
- 関数呼び出し/ツールのサポート
5756
- マルチモーダル入力サポート(テキストと画像)
58-
- ラウンドロビン負荷分散による複数アカウント対応(Gemini、OpenAI、ClaudeおよびiFlow
59-
- シンプルなCLI認証フロー(Gemini、OpenAI、ClaudeおよびiFlow
57+
- ラウンドロビン負荷分散による複数アカウント対応(Gemini、OpenAI、Claude
58+
- シンプルなCLI認証フロー(Gemini、OpenAI、Claude
6059
- Generative Language APIキーのサポート
6160
- AI Studioビルドのマルチアカウント負荷分散
6261
- Gemini CLIのマルチアカウント負荷分散
6362
- Claude Codeのマルチアカウント負荷分散
64-
- iFlowのマルチアカウント負荷分散
6563
- OpenAI Codexのマルチアカウント負荷分散
6664
- 設定によるOpenAI互換アップストリームプロバイダー(例:OpenRouter)
6765
- プロキシ埋め込み用の再利用可能なGo SDK(`docs/sdk-usage.md`を参照)
@@ -174,7 +172,7 @@ Shadow AIは制限された環境向けに特別に設計されたAIアシスタ
174172

175173
### [ProxyPal](https://github.com/buddingnewinsights/proxypal)
176174

177-
CLIProxyAPIをネイティブGUIでラップしたクロスプラットフォームデスクトップアプリ(macOS、Windows、Linux)。Claude、ChatGPT、Gemini、GitHub Copilot、iFlow、カスタムOpenAI互換エンドポイントに対応し、使用状況分析、リクエスト監視、人気コーディングツールの自動設定機能を搭載 - APIキー不要
175+
CLIProxyAPIをネイティブGUIでラップしたクロスプラットフォームデスクトップアプリ(macOS、Windows、Linux)。Claude、ChatGPT、Gemini、GitHub Copilot、カスタムOpenAI互換エンドポイントに対応し、使用状況分析、リクエスト監視、人気コーディングツールの自動設定機能を搭載 - APIキー不要
178176

179177
### [CLIProxyAPI Quota Inspector](https://github.com/AllenReder/CLIProxyAPI-Quota-Inspector)
180178

cmd/server/main.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ type commandFlags struct {
5555
codexLogin bool
5656
codexDeviceLogin bool
5757
claudeLogin bool
58-
iflowLogin bool
59-
iflowCookie bool
6058
noBrowser bool
6159
oauthCallbackPort int
6260
antigravityLogin bool
@@ -107,8 +105,6 @@ func parseCommandFlags() commandFlags {
107105
flag.BoolVar(&flagsState.codexLogin, "codex-login", false, "Login to Codex using OAuth")
108106
flag.BoolVar(&flagsState.codexDeviceLogin, "codex-device-login", false, "Login to Codex using device code flow")
109107
flag.BoolVar(&flagsState.claudeLogin, "claude-login", false, "Login to Claude using OAuth")
110-
flag.BoolVar(&flagsState.iflowLogin, "iflow-login", false, "Login to iFlow using OAuth")
111-
flag.BoolVar(&flagsState.iflowCookie, "iflow-cookie", false, "Login to iFlow using Cookie")
112108
flag.BoolVar(&flagsState.noBrowser, "no-browser", false, "Don't open browser automatically for OAuth")
113109
flag.IntVar(&flagsState.oauthCallbackPort, "oauth-callback-port", 0, "Override OAuth callback port (defaults to provider-specific port)")
114110
flag.BoolVar(&flagsState.antigravityLogin, "antigravity-login", false, "Login to Antigravity using OAuth")
@@ -154,10 +150,6 @@ func handleCommandMode(cfg *config.Config, flagsState commandFlags, options *cmd
154150
cmd.DoCodexDeviceLogin(cfg, options)
155151
case flagsState.claudeLogin:
156152
cmd.DoClaudeLogin(cfg, options)
157-
case flagsState.iflowLogin:
158-
cmd.DoIFlowLogin(cfg, options)
159-
case flagsState.iflowCookie:
160-
cmd.DoIFlowCookieAuth(cfg, options)
161153
case flagsState.kimiLogin:
162154
cmd.DoKimiLogin(cfg, options)
163155
default:

config.example.yaml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ nonstream-keepalive-interval: 0
308308

309309
# Global OAuth model name aliases (per channel)
310310
# These aliases rename model IDs for both model listing and request routing.
311-
# Supported channels: gemini-cli, vertex, aistudio, antigravity, claude, codex, iflow, kimi.
311+
# Supported channels: gemini-cli, vertex, aistudio, antigravity, claude, codex, kimi.
312312
# NOTE: Aliases do not apply to gemini-api-key, codex-api-key, claude-api-key, openai-compatibility, vertex-api-key, or ampcode.
313313
# NOTE: Because aliases affect the merged /v1 model list and merged request routing, overlapping
314314
# client-visible names can become ambiguous across providers. /api/provider/{provider}/... helps
@@ -335,9 +335,6 @@ nonstream-keepalive-interval: 0
335335
# codex:
336336
# - name: "gpt-5"
337337
# alias: "g5"
338-
# iflow:
339-
# - name: "glm-4.7"
340-
# alias: "glm-god"
341338
# kimi:
342339
# - name: "kimi-k2.5"
343340
# alias: "k2.5"
@@ -359,8 +356,6 @@ nonstream-keepalive-interval: 0
359356
# - "claude-3-5-haiku-20241022"
360357
# codex:
361358
# - "gpt-5-codex-mini"
362-
# iflow:
363-
# - "tstars2.0"
364359
# kimi:
365360
# - "kimi-k2-thinking"
366361

internal/api/handlers/management/auth_files.go

Lines changed: 0 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"github.com/NGLSL/CLIProxyAPI/v6/internal/auth/claude"
2626
"github.com/NGLSL/CLIProxyAPI/v6/internal/auth/codex"
2727
geminiAuth "github.com/NGLSL/CLIProxyAPI/v6/internal/auth/gemini"
28-
iflowauth "github.com/NGLSL/CLIProxyAPI/v6/internal/auth/iflow"
2928
"github.com/NGLSL/CLIProxyAPI/v6/internal/auth/kimi"
3029
"github.com/NGLSL/CLIProxyAPI/v6/internal/interfaces"
3130
"github.com/NGLSL/CLIProxyAPI/v6/internal/misc"
@@ -2355,215 +2354,6 @@ func (h *Handler) RequestKimiToken(c *gin.Context) {
23552354
c.JSON(200, gin.H{"status": "ok", "url": authURL, "state": state})
23562355
}
23572356

2358-
func (h *Handler) RequestIFlowToken(c *gin.Context) {
2359-
ctx := context.Background()
2360-
ctx = PopulateAuthContext(ctx, c)
2361-
2362-
fmt.Println("Initializing iFlow authentication...")
2363-
2364-
state := fmt.Sprintf("ifl-%d", time.Now().UnixNano())
2365-
authSvc := iflowauth.NewIFlowAuth(h.cfg)
2366-
authURL, redirectURI := authSvc.AuthorizationURL(state, iflowauth.CallbackPort)
2367-
2368-
RegisterOAuthSession(state, "iflow")
2369-
2370-
isWebUI := isWebUIRequest(c)
2371-
var forwarder *callbackForwarder
2372-
if isWebUI {
2373-
targetURL, errTarget := h.managementCallbackURL("/iflow/callback")
2374-
if errTarget != nil {
2375-
log.WithError(errTarget).Error("failed to compute iflow callback target")
2376-
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "error": "callback server unavailable"})
2377-
return
2378-
}
2379-
var errStart error
2380-
if forwarder, errStart = startCallbackForwarder(iflowauth.CallbackPort, "iflow", targetURL); errStart != nil {
2381-
log.WithError(errStart).Error("failed to start iflow callback forwarder")
2382-
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "error": "failed to start callback server"})
2383-
return
2384-
}
2385-
}
2386-
2387-
go func() {
2388-
if isWebUI {
2389-
defer stopCallbackForwarderInstance(iflowauth.CallbackPort, forwarder)
2390-
}
2391-
fmt.Println("Waiting for authentication...")
2392-
2393-
waitFile := filepath.Join(h.cfg.AuthDir, fmt.Sprintf(".oauth-iflow-%s.oauth", state))
2394-
deadline := time.Now().Add(5 * time.Minute)
2395-
var resultMap map[string]string
2396-
for {
2397-
if !IsOAuthSessionPending(state, "iflow") {
2398-
return
2399-
}
2400-
if time.Now().After(deadline) {
2401-
SetOAuthSessionError(state, "Authentication failed")
2402-
fmt.Println("Authentication failed: timeout waiting for callback")
2403-
return
2404-
}
2405-
if data, errR := os.ReadFile(waitFile); errR == nil {
2406-
_ = os.Remove(waitFile)
2407-
_ = json.Unmarshal(data, &resultMap)
2408-
break
2409-
}
2410-
time.Sleep(500 * time.Millisecond)
2411-
}
2412-
2413-
if errStr := strings.TrimSpace(resultMap["error"]); errStr != "" {
2414-
SetOAuthSessionError(state, "Authentication failed")
2415-
fmt.Printf("Authentication failed: %s\n", errStr)
2416-
return
2417-
}
2418-
if resultState := strings.TrimSpace(resultMap["state"]); resultState != state {
2419-
SetOAuthSessionError(state, "Authentication failed")
2420-
fmt.Println("Authentication failed: state mismatch")
2421-
return
2422-
}
2423-
2424-
code := strings.TrimSpace(resultMap["code"])
2425-
if code == "" {
2426-
SetOAuthSessionError(state, "Authentication failed")
2427-
fmt.Println("Authentication failed: code missing")
2428-
return
2429-
}
2430-
2431-
tokenData, errExchange := authSvc.ExchangeCodeForTokens(ctx, code, redirectURI)
2432-
if errExchange != nil {
2433-
SetOAuthSessionError(state, "Authentication failed")
2434-
fmt.Printf("Authentication failed: %v\n", errExchange)
2435-
return
2436-
}
2437-
2438-
tokenStorage := authSvc.CreateTokenStorage(tokenData)
2439-
identifier := strings.TrimSpace(tokenStorage.Email)
2440-
if identifier == "" {
2441-
identifier = fmt.Sprintf("%d", time.Now().UnixMilli())
2442-
tokenStorage.Email = identifier
2443-
}
2444-
record := &coreauth.Auth{
2445-
ID: fmt.Sprintf("iflow-%s.json", identifier),
2446-
Provider: "iflow",
2447-
FileName: fmt.Sprintf("iflow-%s.json", identifier),
2448-
Storage: tokenStorage,
2449-
Metadata: map[string]any{"email": identifier, "api_key": tokenStorage.APIKey},
2450-
Attributes: map[string]string{"api_key": tokenStorage.APIKey},
2451-
}
2452-
2453-
savedPath, errSave := h.saveTokenRecord(ctx, record)
2454-
if errSave != nil {
2455-
SetOAuthSessionError(state, "Failed to save authentication tokens")
2456-
log.Errorf("Failed to save authentication tokens: %v", errSave)
2457-
return
2458-
}
2459-
2460-
fmt.Printf("Authentication successful! Token saved to %s\n", savedPath)
2461-
if tokenStorage.APIKey != "" {
2462-
fmt.Println("API key obtained and saved")
2463-
}
2464-
fmt.Println("You can now use iFlow services through this CLI")
2465-
CompleteOAuthSession(state)
2466-
CompleteOAuthSessionsByProvider("iflow")
2467-
}()
2468-
2469-
c.JSON(http.StatusOK, gin.H{"status": "ok", "url": authURL, "state": state})
2470-
}
2471-
2472-
func (h *Handler) RequestIFlowCookieToken(c *gin.Context) {
2473-
ctx := context.Background()
2474-
2475-
var payload struct {
2476-
Cookie string `json:"cookie"`
2477-
}
2478-
if err := c.ShouldBindJSON(&payload); err != nil {
2479-
c.JSON(http.StatusBadRequest, gin.H{"status": "error", "error": "cookie is required"})
2480-
return
2481-
}
2482-
2483-
cookieValue := strings.TrimSpace(payload.Cookie)
2484-
2485-
if cookieValue == "" {
2486-
c.JSON(http.StatusBadRequest, gin.H{"status": "error", "error": "cookie is required"})
2487-
return
2488-
}
2489-
2490-
cookieValue, errNormalize := iflowauth.NormalizeCookie(cookieValue)
2491-
if errNormalize != nil {
2492-
c.JSON(http.StatusBadRequest, gin.H{"status": "error", "error": errNormalize.Error()})
2493-
return
2494-
}
2495-
2496-
// Check for duplicate BXAuth before authentication
2497-
bxAuth := iflowauth.ExtractBXAuth(cookieValue)
2498-
if existingFile, err := iflowauth.CheckDuplicateBXAuth(h.cfg.AuthDir, bxAuth); err != nil {
2499-
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "error": "failed to check duplicate"})
2500-
return
2501-
} else if existingFile != "" {
2502-
existingFileName := filepath.Base(existingFile)
2503-
c.JSON(http.StatusConflict, gin.H{"status": "error", "error": "duplicate BXAuth found", "existing_file": existingFileName})
2504-
return
2505-
}
2506-
2507-
authSvc := iflowauth.NewIFlowAuth(h.cfg)
2508-
tokenData, errAuth := authSvc.AuthenticateWithCookie(ctx, cookieValue)
2509-
if errAuth != nil {
2510-
c.JSON(http.StatusBadRequest, gin.H{"status": "error", "error": errAuth.Error()})
2511-
return
2512-
}
2513-
2514-
tokenData.Cookie = cookieValue
2515-
2516-
tokenStorage := authSvc.CreateCookieTokenStorage(tokenData)
2517-
email := strings.TrimSpace(tokenStorage.Email)
2518-
if email == "" {
2519-
c.JSON(http.StatusBadRequest, gin.H{"status": "error", "error": "failed to extract email from token"})
2520-
return
2521-
}
2522-
2523-
fileName := iflowauth.SanitizeIFlowFileName(email)
2524-
if fileName == "" {
2525-
fileName = fmt.Sprintf("iflow-%d", time.Now().UnixMilli())
2526-
} else {
2527-
fileName = fmt.Sprintf("iflow-%s", fileName)
2528-
}
2529-
2530-
tokenStorage.Email = email
2531-
timestamp := time.Now().Unix()
2532-
2533-
record := &coreauth.Auth{
2534-
ID: fmt.Sprintf("%s-%d.json", fileName, timestamp),
2535-
Provider: "iflow",
2536-
FileName: fmt.Sprintf("%s-%d.json", fileName, timestamp),
2537-
Storage: tokenStorage,
2538-
Metadata: map[string]any{
2539-
"email": email,
2540-
"api_key": tokenStorage.APIKey,
2541-
"expired": tokenStorage.Expire,
2542-
"cookie": tokenStorage.Cookie,
2543-
"type": tokenStorage.Type,
2544-
"last_refresh": tokenStorage.LastRefresh,
2545-
},
2546-
Attributes: map[string]string{
2547-
"api_key": tokenStorage.APIKey,
2548-
},
2549-
}
2550-
2551-
savedPath, errSave := h.saveTokenRecord(ctx, record)
2552-
if errSave != nil {
2553-
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "error": "failed to save authentication tokens"})
2554-
return
2555-
}
2556-
2557-
fmt.Printf("iFlow cookie authentication successful. Token saved to %s\n", savedPath)
2558-
c.JSON(http.StatusOK, gin.H{
2559-
"status": "ok",
2560-
"saved_path": savedPath,
2561-
"email": email,
2562-
"expired": tokenStorage.Expire,
2563-
"type": tokenStorage.Type,
2564-
})
2565-
}
2566-
25672357
type projectSelectionRequiredError struct{}
25682358

25692359
func (e *projectSelectionRequiredError) Error() string {

internal/api/handlers/management/oauth_sessions.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,6 @@ func NormalizeOAuthProvider(provider string) (string, error) {
225225
return "codex", nil
226226
case "gemini", "google":
227227
return "gemini", nil
228-
case "iflow", "i-flow":
229-
return "iflow", nil
230228
case "antigravity", "anti-gravity":
231229
return "antigravity", nil
232230
default:

0 commit comments

Comments
 (0)