From 79009bb3d4da31a3d8de193c6683336695766512 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Wed, 4 Mar 2026 02:06:24 +0800 Subject: [PATCH 1/4] Fixed: #797 **test(auth): add test for preserving ModelStates during auth updates** --- sdk/cliproxy/auth/conductor.go | 11 +++-- sdk/cliproxy/auth/conductor_update_test.go | 49 ++++++++++++++++++++++ sdk/cliproxy/service.go | 3 ++ 3 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 sdk/cliproxy/auth/conductor_update_test.go diff --git a/sdk/cliproxy/auth/conductor.go b/sdk/cliproxy/auth/conductor.go index 3434b7a7..ae5b745c 100644 --- a/sdk/cliproxy/auth/conductor.go +++ b/sdk/cliproxy/auth/conductor.go @@ -463,9 +463,14 @@ func (m *Manager) Update(ctx context.Context, auth *Auth) (*Auth, error) { return nil, nil } m.mu.Lock() - if existing, ok := m.auths[auth.ID]; ok && existing != nil && !auth.indexAssigned && auth.Index == "" { - auth.Index = existing.Index - auth.indexAssigned = existing.indexAssigned + if existing, ok := m.auths[auth.ID]; ok && existing != nil { + if !auth.indexAssigned && auth.Index == "" { + auth.Index = existing.Index + auth.indexAssigned = existing.indexAssigned + } + if len(auth.ModelStates) == 0 && len(existing.ModelStates) > 0 { + auth.ModelStates = existing.ModelStates + } } auth.EnsureIndex() m.auths[auth.ID] = auth.Clone() diff --git a/sdk/cliproxy/auth/conductor_update_test.go b/sdk/cliproxy/auth/conductor_update_test.go new file mode 100644 index 00000000..f058f517 --- /dev/null +++ b/sdk/cliproxy/auth/conductor_update_test.go @@ -0,0 +1,49 @@ +package auth + +import ( + "context" + "testing" +) + +func TestManager_Update_PreservesModelStates(t *testing.T) { + m := NewManager(nil, nil, nil) + + model := "test-model" + backoffLevel := 7 + + if _, errRegister := m.Register(context.Background(), &Auth{ + ID: "auth-1", + Provider: "claude", + Metadata: map[string]any{"k": "v"}, + ModelStates: map[string]*ModelState{ + model: { + Quota: QuotaState{BackoffLevel: backoffLevel}, + }, + }, + }); errRegister != nil { + t.Fatalf("register auth: %v", errRegister) + } + + if _, errUpdate := m.Update(context.Background(), &Auth{ + ID: "auth-1", + Provider: "claude", + Metadata: map[string]any{"k": "v2"}, + }); errUpdate != nil { + t.Fatalf("update auth: %v", errUpdate) + } + + updated, ok := m.GetByID("auth-1") + if !ok || updated == nil { + t.Fatalf("expected auth to be present") + } + if len(updated.ModelStates) == 0 { + t.Fatalf("expected ModelStates to be preserved") + } + state := updated.ModelStates[model] + if state == nil { + t.Fatalf("expected model state to be present") + } + if state.Quota.BackoffLevel != backoffLevel { + t.Fatalf("expected BackoffLevel to be %d, got %d", backoffLevel, state.Quota.BackoffLevel) + } +} diff --git a/sdk/cliproxy/service.go b/sdk/cliproxy/service.go index 4be83816..9952e7b2 100644 --- a/sdk/cliproxy/service.go +++ b/sdk/cliproxy/service.go @@ -290,6 +290,9 @@ func (s *Service) applyCoreAuthAddOrUpdate(ctx context.Context, auth *coreauth.A auth.CreatedAt = existing.CreatedAt auth.LastRefreshedAt = existing.LastRefreshedAt auth.NextRefreshAfter = existing.NextRefreshAfter + if len(auth.ModelStates) == 0 && len(existing.ModelStates) > 0 { + auth.ModelStates = existing.ModelStates + } op = "update" _, err = s.coreManager.Update(ctx, auth) } else { From b48485b42b854d91979d0d75980dad03049615b9 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Wed, 4 Mar 2026 02:31:20 +0800 Subject: [PATCH 2/4] Fixed: #822 **fix(auth): normalize ID casing on Windows to prevent duplicate entries due to case-insensitive paths** --- .../api/handlers/management/auth_files.go | 22 +++++++++++-------- internal/watcher/synthesizer/file.go | 5 +++++ sdk/auth/filestore.go | 16 +++++++++----- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/internal/api/handlers/management/auth_files.go b/internal/api/handlers/management/auth_files.go index dcff98d7..e0a16377 100644 --- a/internal/api/handlers/management/auth_files.go +++ b/internal/api/handlers/management/auth_files.go @@ -13,6 +13,7 @@ import ( "net/http" "os" "path/filepath" + "runtime" "sort" "strconv" "strings" @@ -692,17 +693,20 @@ func (h *Handler) authIDForPath(path string) string { if path == "" { return "" } - if h == nil || h.cfg == nil { - return path + id := path + if h != nil && h.cfg != nil { + authDir := strings.TrimSpace(h.cfg.AuthDir) + if authDir != "" { + if rel, errRel := filepath.Rel(authDir, path); errRel == nil && rel != "" { + id = rel + } + } } - authDir := strings.TrimSpace(h.cfg.AuthDir) - if authDir == "" { - return path + // On Windows, normalize ID casing to avoid duplicate auth entries caused by case-insensitive paths. + if runtime.GOOS == "windows" { + id = strings.ToLower(id) } - if rel, err := filepath.Rel(authDir, path); err == nil && rel != "" { - return rel - } - return path + return id } func (h *Handler) registerAuthFromFile(ctx context.Context, path string, data []byte) error { diff --git a/internal/watcher/synthesizer/file.go b/internal/watcher/synthesizer/file.go index 4e053117..ea96118b 100644 --- a/internal/watcher/synthesizer/file.go +++ b/internal/watcher/synthesizer/file.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "runtime" "strconv" "strings" "time" @@ -72,6 +73,10 @@ func (s *FileSynthesizer) Synthesize(ctx *SynthesisContext) ([]*coreauth.Auth, e if rel, errRel := filepath.Rel(ctx.AuthDir, full); errRel == nil && rel != "" { id = rel } + // On Windows, normalize ID casing to avoid duplicate auth entries caused by case-insensitive paths. + if runtime.GOOS == "windows" { + id = strings.ToLower(id) + } proxyURL := "" if p, ok := metadata["proxy_url"].(string); ok { diff --git a/sdk/auth/filestore.go b/sdk/auth/filestore.go index c424a89b..987d305e 100644 --- a/sdk/auth/filestore.go +++ b/sdk/auth/filestore.go @@ -10,6 +10,7 @@ import ( "net/url" "os" "path/filepath" + "runtime" "strings" "sync" "time" @@ -257,14 +258,17 @@ func (s *FileTokenStore) readAuthFile(path, baseDir string) (*cliproxyauth.Auth, } func (s *FileTokenStore) idFor(path, baseDir string) string { - if baseDir == "" { - return path + id := path + if baseDir != "" { + if rel, errRel := filepath.Rel(baseDir, path); errRel == nil && rel != "" { + id = rel + } } - rel, err := filepath.Rel(baseDir, path) - if err != nil { - return path + // On Windows, normalize ID casing to avoid duplicate auth entries caused by case-insensitive paths. + if runtime.GOOS == "windows" { + id = strings.ToLower(id) } - return rel + return id } func (s *FileTokenStore) resolveAuthPath(auth *cliproxyauth.Auth) (string, error) { From 5c84d69d42bd5bf76a946ac740de26de6a74d9ad Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Wed, 4 Mar 2026 13:11:07 +0800 Subject: [PATCH 3/4] feat(translator): map output_config.effort to adaptive thinking level in antigravity --- .../claude/antigravity_claude_request.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/internal/translator/antigravity/claude/antigravity_claude_request.go b/internal/translator/antigravity/claude/antigravity_claude_request.go index c4e07b6a..35387488 100644 --- a/internal/translator/antigravity/claude/antigravity_claude_request.go +++ b/internal/translator/antigravity/claude/antigravity_claude_request.go @@ -441,9 +441,19 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _ out, _ = sjson.Set(out, "request.generationConfig.thinkingConfig.includeThoughts", true) } case "adaptive", "auto": - // Keep adaptive/auto as a high level sentinel; ApplyThinking resolves it - // to model-specific max capability. - out, _ = sjson.Set(out, "request.generationConfig.thinkingConfig.thinkingLevel", "high") + // Adaptive/auto thinking: + // - If output_config.effort is present, pass it through as thinkingLevel. + // - Otherwise, default to "high". + // ApplyThinking later normalizes/clamps and may convert level → budget per target model. + effort := "" + if v := gjson.GetBytes(rawJSON, "output_config.effort"); v.Exists() && v.Type == gjson.String { + effort = strings.ToLower(strings.TrimSpace(v.String())) + } + if effort != "" { + out, _ = sjson.Set(out, "request.generationConfig.thinkingConfig.thinkingLevel", effort) + } else { + out, _ = sjson.Set(out, "request.generationConfig.thinkingConfig.thinkingLevel", "high") + } out, _ = sjson.Set(out, "request.generationConfig.thinkingConfig.includeThoughts", true) } } From b680c146c1b25a5e45437cdb2065aa91f2e6aea7 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Wed, 4 Mar 2026 18:29:23 +0800 Subject: [PATCH 4/4] chore(docs): update sponsor image links in README files --- README.md | 2 +- README_CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 80f6fbd0..8491b97c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ So you can use local or multi-account CLI access with OpenAI(include Responses)/ ## Sponsor -[![z.ai](https://assets.router-for.me/english-5.png)](https://z.ai/subscribe?ic=8JVLJQFSKB) +[![z.ai](https://assets.router-for.me/english-5-0.jpg)](https://z.ai/subscribe?ic=8JVLJQFSKB) This project is sponsored by Z.ai, supporting us with their GLM CODING PLAN. diff --git a/README_CN.md b/README_CN.md index add9c5cf..6e987fdf 100644 --- a/README_CN.md +++ b/README_CN.md @@ -10,7 +10,7 @@ ## 赞助商 -[![bigmodel.cn](https://assets.router-for.me/chinese-5.png)](https://www.bigmodel.cn/claude-code?ic=RRVJPB5SII) +[![bigmodel.cn](https://assets.router-for.me/chinese-5-0.jpg)](https://www.bigmodel.cn/claude-code?ic=RRVJPB5SII) 本项目由 Z智谱 提供赞助, 他们通过 GLM CODING PLAN 对本项目提供技术支持。