package core import "testing" // TRAE 的**两套头不能混用** —— 这是 2026-10-20 那次"目录永远拉不到"的真因。 // // 事故链:模型目录端点在 `trae-api-cn.mchost.guru`(IDE 面),需要 IDE 头族 // (Trae UA + X-App-Id + X-Ide-Version* + 凭据里的 X-Machine-Id/X-Device-Id); // 而 `api.trae.cn` 的签到/积分端点需要另一套(VSCode UA + X-Market-* + // X-Lscbd-* + 派生 market device id)。插件当时"复用签到头"去拉目录,上游对 // 22 个 function 全部回 `error_code 2001 Config detail for function: X not found` // (HTTP 200!),于是远端目录恒空、只能退回兜底表 —— 用户看到的却是 // "trae 模型不正确 / 渠道不可用",与真因隔了整整一层协议。 // // 这个测试把两套头的**契约**钉死:键集合必须互不越界。它不联网,因此能 // 在 CI 里稳定复现"用错头"这一类回归。 func TestTraeHeadersKeptSeparate(t *testing.T) { c := kernelTestCore(t) ps := c.Plugins() src, err := readBundledChannels(t) if err != nil { t.Fatal(err) } if err := ps.LoadSource("channels", src); err != nil { t.Fatal(err) } out, err := ps.Call("channels", "__trae_headers_probe", map[string]interface{}{}) if err != nil { t.Fatalf("probe: %v", err) } j, _ := out.(map[string]interface{})["json"].(map[string]interface{}) pick := func(field string) map[string]bool { set := map[string]bool{} arr, _ := j[field].([]interface{}) for _, raw := range arr { if s, ok := raw.(string); ok { set[s] = true } } return set } ide, chk := pick("ide"), pick("checkin") if len(ide) == 0 || len(chk) == 0 { t.Fatalf("两套头都应有内容;实得 ide=%v checkin=%v", ide, chk) } // IDE 面(目录/对话):身份与版本头必须在 for _, k := range []string{"X-App-Id", "X-Ide-Version", "X-App-Version-Code", "X-Machine-Id", "X-Device-Id", "Request-Traffic-Type"} { if !ide[k] { t.Errorf("IDE 头缺少 %q(目录端点会回 2001)", k) } } // IDE 面**不得**带 UG/积分的头 for _, k := range []string{"X-Market-Client-Id", "X-Lscbd-Aid", "X-Market-User-Id"} { if ide[k] { t.Errorf("IDE 头不应包含 %q(那是签到头)", k) } } // 签到头:UG/积分标记必须在 for _, k := range []string{"X-Market-Client-Id", "X-Market-User-Id", "X-Lscbd-Aid"} { if !chk[k] { t.Errorf("签到头缺少 %q", k) } } // 签到头不得冒用 IDE 的 App/机型身份 for _, k := range []string{"X-App-Id", "X-Ide-Version", "X-Machine-Id"} { if chk[k] { t.Errorf("签到头不应包含 %q(那是 IDE 身份)", k) } } }