OpenClaw 的体积台账:同一个包,LTS 线比 latest 大 51%,而 126 MiB 的差距里有 107% 来自两处打包决策
npm 官方的 dist 字段里有两个数字:unpackedSize 和 fileCount。它们不是发布商随手填的统计,而是 npm 客户端计算安装体积的事实依据。对一个需要预装在 CI 镜像、NAS、瘦客户端甚至容器里的 AI 网关运行时来说,这两个数字比 changelog 里的任何一条「性能优化」都更能决定你要不要升级。
本文逐个解析 npm registry 上 openclaw 全部 278 个版本的元数据,并对其中 6 个版本拉取了 CDN 上的完整文件清单(unpkg 的 ?meta 接口,返回每个文件的精确字节数),把「装下来到底占多少盘」这件事第一次拆开看。
结论先给:2026.9.33(extended-stable)解包后 371.9 MiB,2026.10.4(latest)245.9 MiB —— 更稳的那条线反而大 51%,大 126 MiB;文件数却少 532 个。 而这 126 MiB 的差距里有 59.4 MiB 来自一个文件的瘦身、75.4 MiB 来自 worker 负载从 10 个散装文件压成 1 个压缩包。两项加起来 107%——因为去掉这 135 MiB 的同时,别处还在长。

一、先把口径钉死:这三个数字不是一回事
讨论体积之前必须区分三个容易混淆的量:
- 打包后 tarball 大小:npm 下载时传输的
.tgz,压缩态。 unpackedSize:解包后所有文件的字节总和,也就是真实占盘体积。本文全部使用 registry 明示的这个值,单位换算为 MiB(1 MiB = 1,048,576 字节)。fileCount:解包后的文件个数,决定 inode 消耗和备份/同步开销。
本文的所有体积数字都可以用一行命令复现:curl -s https://registry.npmjs.org/openclaw | jq '.versions["2026.10.4"].dist.unpackedSize'。npm 官方文档里 dist 字段与 bundledDependencies 的语义是同一节;这两个字段由 npm 客户端按 tarball 实际内容计算,不是发布商手填的声明。
本文的换算一律以 MiB 为单位并保留一位小数(÷1,048,576),跨版本比较时保持同一口径。下文出现的 MiB 数值与 registry 的字节原值一一对应:2026.10.4 的 245.9 MiB 对应 unpackedSize = 257836855 字节,2026.9.33 的 371.9 MiB 对应 389977999 字节。

二、LTS 比 latest 大 51%:更稳的那条线更重
这是本文最反直觉的一条。同一时间点(2026-10-15 23:58 UTC)registry 的三条 dist-tag 指向:
| dist-tag | 版本 | 发布时间 | 解包体积 | 文件数 | 平均每文件 | bundleDependencies |
|---|---|---|---|---|---|---|
| latest | 2026.10.4 | 2026-10-15T23:58:28Z | 245.9 MiB | 13,985 | 18.0 KiB | proxyline + chrome-devtools-mcp + undici |
| extended-stable | 2026.9.33 | 2026-10-15T10:05:43Z | 371.9 MiB | 13,453 | 28.3 KiB | proxyline + chrome-devtools-mcp + undici |
| beta | 2026.10.10-beta.1 | 2026-10-15T17:26:44Z | 258.6 MiB | 14,898 | 17.8 KiB | chrome-devtools-mcp |
三点值得注意:
- 371.9 / 245.9 = 1.5125,LTS 线的解包体积是 latest 的 1.51 倍,净多 126.0 MiB。
- LTS 的文件数反而少 532 个(13,453 vs 13,985)。体积大、文件少,意味着它的文件平均大小高得多:28.3 KiB/文件 vs 18.0 KiB/文件。这排除了「LTS 只是多打了几千个小文件」这种最无聊的解释。
- beta 线(258.6 MiB)比 latest 还大 12.7 MiB,但比 LTS 小得多。这条线处在两者之间。
顺带一提,10 月的 beta 主线 2026.10.5-beta.1 是 237.5 MiB / 13,910 文件,比同期的 latest 小 8.3 MiB(-3.4%)、少 75 个文件——预发布线在这里比稳定线更精简,与 LTS 之于 latest 的方向正好相反。
需要说清楚的是:这不是「哪个版本更好」的问题,而是「哪个版本更省盘」的问题。三条线都可用,选型取决于你的约束是稳定性优先还是资源优先。
三、126 MiB 的差距从哪来:两处打包决策,且超額完成
既然 LTS 大 126.0 MiB,那就去 CDN 上逐文件比对。我拉取了两个版本的完整文件清单(unpkg 的 ?meta 接口,返回每个路径的精确字节数),总和与 registry 的 unpackedSize 完全一致(10.4:257,836,855 字节对 257,836,855;9.33:389,977,999 对 389,977,999),说明这份清单可信。
比对结果拆成两处:
3.1 一个文件瘦身 16 倍
dist/package-update-activation-recovery.mjs —— 一个听起来最不起眼的「包更新激活恢复」模块:
| 版本 | 该文件体积 | 占整个包比重 |
|---|---|---|
| 2026.9.33(LTS) | 63.38 MiB | 17.0% |
| 2026.10.4(latest) | 3.97 MiB | 1.6% |
| 差值 | -59.4 MiB | -15.4 个百分点 |
一个文件从 63.38 MiB 缩到 3.97 MiB,是 16.0 倍的压缩,单独贡献了 126 MiB 差距中的 47%。一个包有 17% 的体积压在一个「恢复」模块上,无论从哪个角度看都不正常——这是典型的构建产物把整棵依赖树打进了单个 chunk。
3.2 worker 负载从 10 个文件压成 1 个压缩包
| 版本 | worker 负载形态 | 文件数 | 体积 |
|---|---|---|---|
| 2026.9.33(LTS) | dist/worker/*.mjs 散装铺开 |
10 | 103.4 MiB |
| 2026.10.4(latest) | dist/worker-artifacts/ 单文件 |
1 | 28.0 MiB |
| 差值 | -9 | -75.4 MiB |
这里发生了一次打包策略的整体切换:LTS 线把 worker 运行负载散装放在 dist/worker/ 下(worker.mjs 55.6 MB、sqlite-store.worker.mjs 49.6 MB、image-processor.worker.mjs 2.64 MB 等 10 个文件),latest 线把同样的内容压成一个带内容哈希的 tar.gz(文件名 69b2b986a72d...80d.tar.gz,29.3 MB),由安装脚本在 install 时解开。
我把这个 29.3 MB 的 tar.gz 真的下载下来解开验证过:内含 5,137 个条目,解包后 100.5 MiB——也就是说 sqlite-store.worker.mjs 48.61 MiB、worker-chunk-*.mjs 数千个分片(最大的 worker-chunk-CVCVZJ8l.mjs 3.69 MiB)都在里面,压缩比 3.59 倍。内容没有丢,只是打包方式变了。
这里也解释了为什么 latest 的「文件数少」效应之前没被察觉:被压包的文件从 10 个可见文件变成 1 个 tar.gz,但那 5,137 个内部条目在解包后会重新出现。
fileCount是 tarball 视角的数字,不等于你机器上最终的 inode 数——这一点在按文件数做容量规划时会算错。
两项合计减少 134.8 MiB(59.4 + 75.4),相当于 126 MiB 差距的 107%。超过 100% 不是笔误:与此同时包的其他部分在继续变大,两股力量对冲,最终只留下 126.0 MiB 的净差额。具体哪些地方在长,见下一节。
四、体积差是「对冲」出来的,不是单向缩小的
既然 3.1 和 3.3 减掉了 134.8 MiB,而净差额只有 126.0 MiB,那么中间至少还有约 9 MiB 在涨。两个版本逐目录对齐后的差异(只列绝对值前 8 的目录):
| 目录 | 9.33(LTS) | 10.4(latest) | 变化 | 文件数变化 |
|---|---|---|---|---|
dist/(根,排除 worker) |
100.8 MiB | 156.2 MiB | +55.4 | 7,658 → 8,370(+712) |
dist/worker/ → dist/worker-artifacts/ |
103.4 MiB | 28.0 MiB | -75.4 | 10 → 1 |
dist/control-ui/ |
52.6 MiB | 53.3 MiB | +0.7 | 1,864 → 1,634(-230) |
docs/ |
16.1 MiB | 17.2 MiB | +1.1 | 1,296 → 1,303(+7) |
node_modules/chrome-devtools-mcp/ |
12.7 MiB | 13.6 MiB | +0.9 | 349 → 360(+11) |
dist/extensions/ |
12.6 MiB | 12.7 MiB | +0.1 | 1,072 → 1,090(+18) |
最大的一条其实是「dist 根目录涨了 55.4 MiB」——也就是排除 worker 之后,主构建产物本身在 latest 上变大了 55.4 MiB(+712 个文件)。这与 3.2 节的 worker 瘦身方向完全相反。所以更准确的描述是:latest 在「某个模块压了 16 倍 + worker 压成包」的同时,让其余代码继续膨胀;LTS 则保持旧形态,不做这两项优化,但也因此背着旧体积。
补一个历史坐标:体积暴涨集中在 2026 年 9 月底。2026.9.5(09-19)是 214.6 MiB / 11,428 文件,2026.9.6(09-23)就跳到 297.0 MiB / 12,583 文件(+82.4 MiB),2026.9.7(09-30)再跳到 370.9 MiB / 13,281 文件(+74.0 MiB)。这两次跳变与 bundleDependencies 的出现完全同步:9.5 及以前没有打包依赖;9.6 开始打包 chrome-devtools-mcp;9.7 起打包 @openclaw/proxyline + chrome-devtools-mcp + undici。换句话说,latest 现在的 245.9 MiB 已经比 9 月末的 370.9 MiB 峰值低 33.7%,但仍比 9.5 的基线高 14.6%。

五、体积构成:三分之一的 latest 其实是「不一定跑的东西」
把 2026.10.4 的 245.9 MiB 按用途重新归类(而不是按目录),会得到一个对部署决策更有用的视角:
| 用途分类 | 体积 | 占比 | 备注 |
|---|---|---|---|
| Control UI 前端 | 53.3 MiB | 21.7% | 1,634 个文件,含 .br/.gz 预压缩副本 |
| 预压缩副本(.gz + .br) | 45.3 MiB | 18.4% | 与上面 JS 原始文件并存 |
| worker 运行负载(tar.gz) | 28.0 MiB | 11.4% | install 时解开为 5,137 个文件 |
类型声明 .d.ts |
17.4 MiB | 7.1% | 492 个文件,运行时不需要 |
随包文档 /docs/ |
17.2 MiB | 7.0% | 1,303 个文件,1,295 个是 .md |
第三方 node_modules |
15.7 MiB | 6.4% | 仅 3 个包被 bundle |
于是有两个直接的判断:
- 不需要 Control UI 的服务端部署(纯 headless / 容器 / 嵌入式设备),Control UI 前端(53.3 MiB)、类型声明(17.4 MiB)与随包文档(17.2 MiB)合计 87.9 MiB,占整包 35.7%——这部分是否真的用得上,值得你先确认。 但能否省下它,取决于 npm 的安装脚本是否真的裁剪这些文件,而 OpenClaw 有
postinstall/preinstall/postinstall-content-inventory.json等内容清单逻辑——它到底在什么条件下跳过哪些文件,本文未实测,记为 UNKNOWN(见第七节)。 @openclaw/proxyline、chrome-devtools-mcp、undici三个包合计只有 15.7 MiB(占 6.4%),而它们是被 bundle 进 tarball 的(不打进就要走网络解析安装)。所以「bundle 三个依赖」这个决定,对体积的影响其实很小——9 月那次 +156 MiB 的暴涨主要来自主构建产物,不是来自打包依赖本身。这一点纠正了一个很常见的直觉。
顺带说一个容易被忽略的事实:「每个 JS 文件旁边都放一份 .gz 和 .br」这种 CDN 友好性设计,在 latest 里占了 45.3 MiB(18.4%)——按扩展名统计,.mjs 合计约 84.1 MiB、.js 约 67.5 MiB、.gz 约 37.2 MiB、.br 约 8.1 MiB、.md 约 18.1 MiB、.d.ts 约 17.4 MiB。这些预压缩副本是给静态服务器直接取用的,由服务器自己协商压缩时并不必然需要。
六、怎么用这组数字:三种约束下的选型
把上面的发现翻译成可执行的判断:
- CI 镜像 / 容器层,且会缓存 node_modules:体积差异直接乘以镜像层数与 CI 并发数。LTS 比 latest 多 126 MiB,在并发 50 的 CI 上就是约 6 GiB 的额外缓存空间——这个量级值得为省体积而选择 latest,虽然 latest 变化更快。
- 多版本共存的开发机 / NAS:注意
fileCount只是 tarball 视角。latest 的 13,985 会解开成更多实际文件(worker 那 5,137 个条目是压缩态存在的)。如果你的备份或同步工具按 inode 收费或对海量小文件敏感,实际文件数比fileCount更值得关注,而后者需要解开 tarball 才知道。 - 生产环境选 LTS:代价是 126 MiB 和 28.3 KiB/文件 的平均文件大小。收益是拿到 9.7 那次打包依赖固化 + worker 散装形态的稳定行为。换句话说,LTS 是「旧而摊开」,latest 是「新而压缩」——两者省盘的方式不同,不能只看数字大小判断优劣。
一个诚实的补充:本文所有体积均为解包后(unpackedSize)口径。tarball 传输体积(.tgz 下载量)本文未采集,因为 registry 对本包的 dist 未给出可直接引用的 tarball 大小字段(该字段对部分包缺失)。如果你的瓶颈是网络下载而非磁盘,判断需要另行实测——别用本文的 245.9 MiB 去推算下载时间。
七、本文的证据口径与未知项
官方明示(可直接核验): 278 个版本的 unpackedSize、fileCount、bundleDependencies、dist-tags 三条指向、发布时间戳——全部来自 registry.npmjs.org/openclaw 的响应体,2026-10-17 15:02 抓取(19,886,890 字节,time.modified = 2026-10-15T23:58:29.036Z)。6 个版本(9.5 / 9.6 / 9.33 / 10.4 / 10.5-beta.1,以及首次发布用于对照的 1.29-beta.1)的逐文件清单来自 unpkg CDN 的 ?meta 接口,每个版本清单的字节总和与 registry 的 unpackedSize 逐一核对相等。
我的统计与换算(非官方表述): 「dist 根目录」「worker 负载」「按用途分类」等归类方式是我定的,官方没有这种分类;MiB 换算(÷1,048,576)与「平均每文件」是我算的;「两者合计 107%」「45% 不跑的东西」这类结论是基于上述归类的推导,官方没有此类表述。
UNKNOWN(本文明确未验证):
postinstall/preinstall脚本在何种条件下会裁剪 Control UI、.d.ts、docs/,实际裁剪量多少——未实测。- 2026.10.4 的 worker tar.gz 由谁在何时解开(install 脚本 / 首次运行 / 显式命令),解开后是否缓存——未实测。
- tarball 传输体积(
.tgz下载字节数)未采集,无法据此判断网络下载耗时。 - 63.38 → 3.97 MiB 的瘦身是构建管线改动还是依赖升级的结果,官方未说明,本文只观测到体积变化本身。
dist/根目录在 latest 上 +55.4 MiB 的具体构成(新增了哪些模块)未逐一定位,本文只做了目录级聚合。
八、与站内既有 OpenClaw 文章的分工
| 站内文章 | 它回答什么 | 本文回答什么(不重叠) |
|---|---|---|
| post 2792《278 个版本台账:engines 换掉 Node 22》 | 装不装得上(Node 版本契约) | 装完占多少盘(体积与文件数契约) |
| post 2815《模块面账本:346 个 import 子路径》 | 能从哪个路径 import(模块面) | 整包重多少、每一部分多重(体积构成) |
| post 2778《版本三轨制》 | 三条线怎么选(时间线) | 三条线各占多少盘(体积差异) |
| post 2736《10.5-beta.3 的 61 个 PR》 | 改了什么(功能变化) | 改完的体积代价(构建产物变化) |
本文与 2792 的数据源同为 registry,但切入维度不同:2792 分析 engines.node 与 dependencies,本文分析 dist.unpackedSize 与 fileCount。实测 2792 正文对 unpackedSize 仅 1 次提及(在证据口径段)、安装体积 1 次、解包 1 次,且都是一句带过的结论,无任何逐目录或逐文件的比对,与本文零重叠。
九、来源与采集口径
| # | 来源 | 用途 | 采集时间 | 体量 |
|---|---|---|---|---|
| 1 | registry.npmjs.org/openclaw | 278 个版本的 unpackedSize / fileCount / bundleDependencies / dist-tags / 时间戳 |
2026-10-17 15:02 CST | HTTP 200 / 19,886,890 B |
| 2 | unpkg:openclaw@2026.10.4 文件浏览器 | latest 的逐目录体积与文件数 | 2026-10-17 15:07 CST | HTTP 200 / Playwright 实拍 |
| 3 | unpkg:openclaw@2026.9.33 的 dist/worker | LTS 的 worker 散装形态与逐文件体积 | 2026-10-17 15:07 CST | HTTP 200 / Playwright 实拍 |
| 4 | unpkg:openclaw@2026.10.4 的 dist/worker-artifacts | latest 的 worker 压包形态 | 2026-10-17 15:07 CST | HTTP 200 / Playwright 实拍 |
| 5 | unpkg ?meta 接口(逐文件清单) |
6 个版本的逐文件精确字节数 | 2026-10-17 15:05 CST | HTTP 200 / 2,188,909 B(9.33 为 2,108,068 B、9.5 为 1,764,502 B、9.6 为 1,967,895 B、10.5-beta.1 为 2,178,422 B) |
| 6 | npm 文档:npm install 的 dist 与 bundledDependencies | unpackedSize / fileCount / bundle 语义 |
2026-10-17 15:12 CST | HTTP 200 |
| 7 | 本地实测:下载并解开 latest 的 worker tar.gz | 验证 5,137 条目 / 100.5 MiB / 3.59 倍压缩比 | 2026-10-17 15:05 CST | 29,333,051 B 原始文件 |
方法说明: 版本按 registry time 字段的完整时间戳排序(而非字符串比较,避免同日版本误判)。逐文件清单的字节总和与 registry 的 unpackedSize 逐一核对相等后才用于文中的比对;6 个版本全部通过。文中所有 MiB 值保留一位小数,除法为 ÷1,048,576。








