mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-09-03 06:35:00 +08:00
feat(codex): add disable-codex-cloaking config option and refine header management
- Introduced `disable-codex-cloaking` to allow disabling enforced `User-Agent` and `Originator` headers for Codex requests. - Updated header application logic to conditionally include `codexUserAgent` and `codexOriginator` based on configuration. - Enhanced config diff tracking to detect changes in `disable-codex-cloaking`. - Expanded tests to cover new config behavior and header application scenarios.
This commit is contained in:
@@ -220,6 +220,8 @@ codex:
|
||||
# Some superstitious users believe request tracking identifiers can be used
|
||||
# as evidence for TOS enforcement bans; this option only satisfies those odd concerns.
|
||||
identity-confuse: false
|
||||
# Disable forcing the official Codex User-Agent and Originator headers on HTTP requests.
|
||||
disable-codex-cloaking: false
|
||||
# When true, optimize Codex Desktop and codex-tui requests for multi-agent v2.
|
||||
# This refreshes Codex spawn_agent model details, removes message parameter encryption,
|
||||
# normalizes encrypted agent_message content for Codex, and converts agent_message input
|
||||
|
||||
@@ -29,6 +29,9 @@ codex-header-defaults:
|
||||
if got := cfg.CodexHeaderDefaults.BetaFeatures; got != "feature-a,feature-b" {
|
||||
t.Fatalf("BetaFeatures = %q, want %q", got, "feature-a,feature-b")
|
||||
}
|
||||
if cfg.Codex.DisableCodexCloaking {
|
||||
t.Fatal("DisableCodexCloaking = true, want default false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigOptional_CodexIdentityConfuse(t *testing.T) {
|
||||
@@ -37,6 +40,7 @@ func TestLoadConfigOptional_CodexIdentityConfuse(t *testing.T) {
|
||||
configYAML := []byte(`
|
||||
codex:
|
||||
identity-confuse: true
|
||||
disable-codex-cloaking: true
|
||||
optimize-multi-agent-v2: true
|
||||
`)
|
||||
if err := os.WriteFile(configPath, configYAML, 0o600); err != nil {
|
||||
@@ -51,6 +55,9 @@ codex:
|
||||
if !cfg.Codex.IdentityConfuse {
|
||||
t.Fatalf("IdentityConfuse = false, want true")
|
||||
}
|
||||
if !cfg.Codex.DisableCodexCloaking {
|
||||
t.Fatal("DisableCodexCloaking = false, want true")
|
||||
}
|
||||
if !cfg.Codex.OptimizeMultiAgentV2 {
|
||||
t.Fatalf("OptimizeMultiAgentV2 = false, want true")
|
||||
}
|
||||
|
||||
@@ -127,6 +127,8 @@ type XAIConfig struct {
|
||||
// CodexConfig configures provider-wide Codex request behavior.
|
||||
type CodexConfig struct {
|
||||
IdentityConfuse bool `yaml:"identity-confuse" json:"identity-confuse"`
|
||||
// DisableCodexCloaking disables forcing the official Codex identity headers on HTTP requests.
|
||||
DisableCodexCloaking bool `yaml:"disable-codex-cloaking" json:"disable-codex-cloaking"`
|
||||
// OptimizeMultiAgentV2 optimizes official Codex multi-agent requests.
|
||||
OptimizeMultiAgentV2 bool `yaml:"optimize-multi-agent-v2" json:"optimize-multi-agent-v2"`
|
||||
// LiveMediaRelay terminates and relays Codex Live WebRTC media in this process.
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
codexUserAgent = "codex-tui/0.135.0 (Mac OS 26.5.0; arm64) iTerm.app/3.6.10 (codex-tui; 0.135.0)"
|
||||
codexUserAgent = "codex-tui/0.146.0 (Mac OS 26.5.0; arm64) iTerm.app/3.6.10 (codex-tui; 0.146.0)"
|
||||
codexOriginator = "codex-tui"
|
||||
codexDefaultImageToolModel = "gpt-image-2"
|
||||
codexResponsesLiteHeader = "X-OpenAI-Internal-Codex-Responses-Lite"
|
||||
@@ -352,6 +352,10 @@ func applyCodexHeadersFromSources(r *http.Request, auth *cliproxyauth.Auth, toke
|
||||
attrs = auth.Attributes
|
||||
}
|
||||
util.ApplyCustomHeadersFromAttrs(r, attrs)
|
||||
if cfg != nil && !cfg.Codex.DisableCodexCloaking {
|
||||
r.Header.Set("User-Agent", codexUserAgent)
|
||||
r.Header.Set("Originator", codexOriginator)
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeCodexInstructions(body []byte) []byte {
|
||||
|
||||
@@ -105,8 +105,8 @@ func TestCodexExecutorDirectOpenAIImageGenerationUsesImagesEndpoint(t *testing.T
|
||||
if gotClientRequestID != "client-request-1" {
|
||||
t.Fatalf("X-Client-Request-Id = %q, want %q", gotClientRequestID, "client-request-1")
|
||||
}
|
||||
if gotOriginator != "Codex Desktop" {
|
||||
t.Fatalf("Originator = %q, want %q", gotOriginator, "Codex Desktop")
|
||||
if gotOriginator != codexOriginator {
|
||||
t.Fatalf("Originator = %q, want %q", gotOriginator, codexOriginator)
|
||||
}
|
||||
if got := gjson.GetBytes(gotBody, "model").String(); got != "gpt-image-1.5" {
|
||||
t.Fatalf("model = %q, want gpt-image-1.5; body=%s", got, string(gotBody))
|
||||
|
||||
@@ -1510,6 +1510,7 @@ func TestApplyCodexHeadersUsesConfigUserAgentForOAuth(t *testing.T) {
|
||||
t.Fatalf("NewRequest() error = %v", err)
|
||||
}
|
||||
cfg := &config.Config{
|
||||
Codex: config.CodexConfig{DisableCodexCloaking: true},
|
||||
CodexHeaderDefaults: config.CodexHeaderDefaults{
|
||||
UserAgent: "config-ua",
|
||||
BetaFeatures: "config-beta",
|
||||
@@ -1533,6 +1534,41 @@ func TestApplyCodexHeadersUsesConfigUserAgentForOAuth(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyCodexHeadersDefaultsToCodexCloaking(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodPost, "https://example.com/responses", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewRequest() error = %v", err)
|
||||
}
|
||||
req.Header.Set("User-Agent", "existing-ua")
|
||||
req.Header.Set("Originator", "existing-origin")
|
||||
cfg := &config.Config{
|
||||
CodexHeaderDefaults: config.CodexHeaderDefaults{
|
||||
UserAgent: "config-ua",
|
||||
},
|
||||
}
|
||||
auth := &cliproxyauth.Auth{
|
||||
Provider: "codex",
|
||||
Attributes: map[string]string{
|
||||
"api_key": "api-key",
|
||||
"header:User-Agent": "custom-ua",
|
||||
"header:Originator": "custom-origin",
|
||||
},
|
||||
}
|
||||
ginHeaders := http.Header{
|
||||
"User-Agent": []string{"client-ua"},
|
||||
"Originator": []string{"client-origin"},
|
||||
}
|
||||
|
||||
applyCodexHeadersFromSources(req, auth, "api-key", false, cfg, ginHeaders)
|
||||
|
||||
if got := req.Header.Get("User-Agent"); got != codexUserAgent {
|
||||
t.Fatalf("User-Agent = %q, want %q", got, codexUserAgent)
|
||||
}
|
||||
if got := req.Header.Get("Originator"); got != codexOriginator {
|
||||
t.Fatalf("Originator = %q, want %q", got, codexOriginator)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyModelHeaderOverridesFromModelConfig(t *testing.T) {
|
||||
const wantUA = "codex-tui/0.144.0 (Mac OS 26.5.1; arm64) iTerm.app/3.6.11 (codex-tui; 0.144.0)"
|
||||
req, err := http.NewRequest(http.MethodPost, "https://example.com/responses", nil)
|
||||
@@ -1614,7 +1650,8 @@ func TestApplyCodexHeadersPassesThroughClientIdentityHeaders(t *testing.T) {
|
||||
"X-Client-Request-Id": "019d2233-e240-7162-992d-38df0a2a0e0d",
|
||||
}))
|
||||
|
||||
applyCodexHeaders(req, auth, "oauth-token", true, nil)
|
||||
cfg := &config.Config{Codex: config.CodexConfig{DisableCodexCloaking: true}}
|
||||
applyCodexHeaders(req, auth, "oauth-token", true, cfg)
|
||||
|
||||
if got := req.Header.Get("Originator"); got != "Codex Desktop" {
|
||||
t.Fatalf("Originator = %s, want %s", got, "Codex Desktop")
|
||||
|
||||
@@ -108,6 +108,9 @@ func BuildConfigChangeDetails(oldCfg, newCfg *config.Config) []string {
|
||||
if oldCfg.Codex.IdentityConfuse != newCfg.Codex.IdentityConfuse {
|
||||
changes = append(changes, fmt.Sprintf("codex.identity-confuse: %t -> %t", oldCfg.Codex.IdentityConfuse, newCfg.Codex.IdentityConfuse))
|
||||
}
|
||||
if oldCfg.Codex.DisableCodexCloaking != newCfg.Codex.DisableCodexCloaking {
|
||||
changes = append(changes, fmt.Sprintf("codex.disable-codex-cloaking: %t -> %t", oldCfg.Codex.DisableCodexCloaking, newCfg.Codex.DisableCodexCloaking))
|
||||
}
|
||||
if oldCfg.Codex.OptimizeMultiAgentV2 != newCfg.Codex.OptimizeMultiAgentV2 {
|
||||
changes = append(changes, fmt.Sprintf("codex.optimize-multi-agent-v2: %t -> %t", oldCfg.Codex.OptimizeMultiAgentV2, newCfg.Codex.OptimizeMultiAgentV2))
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ func TestBuildConfigChangeDetails(t *testing.T) {
|
||||
newCfg := &config.Config{
|
||||
Port: 9090,
|
||||
AuthDir: "/tmp/auth-new",
|
||||
Codex: config.CodexConfig{DisableCodexCloaking: true},
|
||||
GeminiKey: []config.GeminiKey{
|
||||
{APIKey: "old", BaseURL: "http://old", ExcludedModels: []string{"old-model", "extra"}},
|
||||
},
|
||||
@@ -78,6 +79,7 @@ func TestBuildConfigChangeDetails(t *testing.T) {
|
||||
expectContains(t, details, "remote-management.allow-remote: false -> true")
|
||||
expectContains(t, details, "remote-management.disable-auto-update-panel: false -> true")
|
||||
expectContains(t, details, "remote-management.secret-key: updated")
|
||||
expectContains(t, details, "codex.disable-codex-cloaking: false -> true")
|
||||
expectContains(t, details, "oauth-excluded-models[providera]: updated (1 -> 2 entries)")
|
||||
expectContains(t, details, "oauth-excluded-models[providerb]: added (1 entries)")
|
||||
expectContains(t, details, "openai-compatibility:")
|
||||
|
||||
Reference in New Issue
Block a user