起因
新版本升级上去之后不好用,想回退到上一个版本,结果发现 TRAE 官网只提供最新版下载,更新日志里只有版本号,没有任何历史安装包入口。
于是就有了下面这一次「从更新日志倒推到安装包直链」的折腾。最终把 3.5.97 ~ 3.5.104 这一批(更新日志 2026-09-20 那条 "TraeCode v3.5.97 ~ 3.5.104 版本正式发布")挖出来了,记录一下完整思路。
结论先行
| 版本 | 构建号 | 上传时间 |
|---|---|---|
| 3.5.103 | 2.3.87602 |
2026-09-20 |
| 3.5.104 | 2.3.88407 |
2026-09-22 |
macOS(Apple Silicon):
ruby
https://lf-cdn.trae.ai/obj/trae-ai-sg/pkg/app/releases/stable/2.3.88407/darwin/TraeCode-darwin-arm64.dmg
macOS(Intel):
ruby
https://lf-cdn.trae.ai/obj/trae-ai-sg/pkg/app/releases/stable/2.3.88407/darwin/TraeCode-darwin-x64.dmg
Windows x64:
ruby
https://lf-cdn.trae.ai/obj/trae-ai-sg/pkg/app/releases/stable/2.3.88407/win32/TraeCode-Setup-x64.exe
Linux x64:
ruby
https://lf-cdn.trae.ai/obj/trae-ai-sg/pkg/app/releases/stable/2.3.88407/linux/TraeCode-linux-x64.tar.gz
3.5.103 把路径里的 2.3.88407 换成 2.3.87602 即可,文件名不变。
一、先摸清「下载链路」
官网页面的下载按钮是 JS 动态渲染的,直接抓 HTML 拿不到链接,所以要顺着它调的接口找。
抓官网 www.trae.ai/download 的网络请求,会发现一个很关键的接口:
bash
https://icube-normal.trae.ai/icube/api/v1/native/version/trae/latest
它直接返回当前各产品线的安装包地址,结构大致是:
json
{
"data": {
"manifest": { "darwin": { "versions": [ { "url": "...stable/2.3.91093/darwin/TRAE-darwin-arm64.dmg", "version": "3.6.2" } ] } },
"solo": { "...": "TraeWork" },
"tob": { "win32": { "download": [ { "x64": "...stable/2.3.68993/win32/TraeCode-Setup-x64.exe" } ] } }
}
}
两个信息点:
- 安装包放在
.../pkg/app/releases/stable/下,路径中间那串2.3.91093是构建号; - 接口只给「最新版」,历史构建得自己找,但路径规律已经到手了。
另外 tob 那条就是企业版 / TraeCode 线,包名前缀是 TraeCode-。
二、URL 规律
ruby
https://lf-cdn.trae.ai/obj/trae-ai-sg/pkg/app/releases/stable/<构建号>/<平台>/<文件名>
- 平台目录:
darwin/win32/linux - 文件名前缀区分产品线:
TRAE-*→ 国际版 TRAE IDETraeCode-*→ 企业版 / TraeCodeTrae_CN-*→ 国内个人版TRAE_Work_CN-*→ TRAE Work 国内版
- macOS 是
-darwin-arm64.dmg/-darwin-x64.dmg,Windows 是-Setup-x64.exe - 每个构建目录里只放一条产品线的包
镜像域名换掉就行,路径一致:
- 国内:
https://lf-cdn.trae.com.cn/obj/trae-com-cn/... - 美国:
https://lf-cdn.trae.ai/obj/trae-ai-us/... - 美国静态:
https://lf-static.traecdn.us/obj/trae-ai-tx/...
三、核心难点:构建号 ≠ 版本号
界面显示的 3.5.104 和路径里的 2.3.88407 没有任何直接换算关系。也就是说,即使把路径规律摸清楚了,你还是不知道「3.5.97 ~ 3.5.104」对应哪个构建号。
常规做法是暴力遍历:从最新构建号往下递减,HEAD 请求探测哪些目录存在。但这样只能拿到一串「存在的构建号」,依然不知道每个构建对应什么版本------除非把几百 MB 的安装包整个下下来,挂载 DMG 看 Info.plist。每个候选都下几百 MB,显然不现实。
破局点:用 HTTP Range 只下几 KB,读出包内版本号
每个构建目录里除了 .dmg / .exe,还有一份 darwin/*.zip(macOS 免安装包)。而 zip 的格式决定了:
- 文件尾部有 EOCD 记录,里面写着中央目录的偏移和大小;
- 中央目录里列出所有条目的文件名、偏移、压缩方式、压缩后大小;
- 条目数据可以单独按偏移取。
所以只要:
- 取最后 ~80 KB,找到 EOCD,读出中央目录位置;
- 取中央目录那一段(几十 KB),定位到
Trae.app/Contents/Info.plist; - 按偏移取该条目几十 KB 的 deflate 数据,解压后读
CFBundleShortVersionString。
整个流程只需要几 KB 流量,就能确知某个构建号背后的真实版本号。探测成本从「几百 MB」降到「几 KB」,整件事就可行了。
核心代码(Python 标准库 + curl 即可):
python
def read_version(zip_url):
size = content_length(zip_url)
tail = rng(zip_url, size - 80000, size - 1)
i = tail.rfind(b"PK\x05\x06") # EOCD
cd_size, cd_off = struct.unpack("<II", tail[i + 12:i + 20])
cd = rng(zip_url, cd_off, cd_off + cd_size - 1)
# 解析中央目录,找到 Trae.app/Contents/Info.plist
...
raw = rng(zip_url, data_off, data_off + comp - 1)
data = zlib.decompress(raw, -15) if method == 8 else raw
return re.search(r"<key>CFBundleShortVersionString</key>\s*<string>([^<]+)</string>", data)
四、实测结果
把 69000 ~ 95000 这一段全部扫了一遍(HEAD 探测 + 上面的 Range 读版本),企业版 / TraeCode 线公开的构建是:
| 版本号 | 构建号 | 上传时间 | 对应更新日志 |
|---|---|---|---|
| 3.5.87 | 2.3.68993 | 2026-08-12 | 3.5.84 ~ 3.5.92 那批 |
| 3.5.91 | 2.3.73738 | 2026-08-20 | 同上 |
| 3.5.103 | 2.3.87602 | 2026-09-20 | 3.5.97 ~ 3.5.104 那批 |
| 3.5.104 | 2.3.88407 | 2026-09-22 | 同上 |
| 3.6.0 | 2.3.88717 / 88917 / 89047 / 89097 / 89127 | - | 合并后的新版 TRAE |
| 3.6.2 | 2.3.91093 | 2026-10 初 | 当前最新 |
注意 3.5.103 的上传时间 2026-09-20 13:25 GMT,和更新日志那条日期完全对上,可以确认就是同一批发布。
另外更新日志虽然写的是「3.5.97 ~ 3.5.104」,但公开 CDN 上只有 3.5.103 / 3.5.104 两个构建,中间的 3.5.97 ~ 3.5.102 没有对外发布的安装包。
验证方式:下载后挂载 DMG,读 Trae.app/Contents/Info.plist 确认版本号,并检查代码签名主体是否与现行版本一致(都是 Developer ID Application: SPRING (SG) PTE. LTD. (79M8227NKH)),确认是官方包。
五、几个坑
- 并发别开太高。一开始用 50+ 并发跑 Range 请求,被 Akamai 挡了,返回的是一个 HTML 错误页而不是 404 或 403,脚本会误判。降到 20 ~ 24 并发就稳定了。
- 探测用 HEAD 而不是带 Range 的 GET。HEAD 便宜,不容易触发源站,实测 6000 个探测点 2 分多钟跑完。
- 偶发
000(连接失败)。网络抖动导致,不是包不存在,脚本里要做重试,Range 请求要校验返回长度是否等于请求长度。 - 构建目录不通用 。一个构建目录只放一条产品线的包,不要拿
TRAE-*的名字去试TraeCode的构建号。 - 第三方资料大多停更。网上能找到的整理(GitHub issue、Gitee 归档仓库)基本只覆盖到几个月前,而且大多只给构建号不给版本号,最新的还是得自己扫。
六、装了老版本,怎么防止又被自动升级
- 装完之后先断网再打开 Trae;
- 设置里搜「更新」,把
Update: Mode改成manual,取消勾选Update: Show Release Notes:
json
{
"update.mode": "manual",
"update.showReleaseNotes": false
}
- 更彻底一点,在 hosts 里屏蔽更新域名(要下载新包时再把这几行注释掉):
css
0.0.0.0 icube-normal.trae.ai
0.0.0.0 lf-cdn.trae.ai
0.0.0.0 api.trae.ai
注意 hosts 拦截之后,官网和 CDN 也一起下不了包了,需要先注释掉再下载。
七、一点总结
整个思路其实就是三步:
- 找接口:官网页面是动态渲染的,抓它调的接口,拿到「最新版」的完整下载地址,反推出 URL 规律;
- 降成本:暴力遍历只能拿到构建号,用 zip 中央目录 + HTTP Range 只取几 KB,就能把「构建号 → 版本号」的映射补上;
- 做映射:扫描 + 读版本 + 和更新日志的日期对齐,确认哪一批对应哪个构建。
这套方法不限于 TRAE,凡是「electron 应用 + 对象存储分发 + 只有最新版入口」的软件基本都能套用,关键就是找到那个可以被 Range 请求精确切片的容器格式(zip / dmg / exe 里的元数据)。
希望官方以后能把历史版本入口做出来,就不用这么折腾了 🙃