⚠️ SUPERSEDED / 历史快照(HISTORICAL SNAPSHOT) — 标注追加于 2026-09-24(任务 #122,承接 #109 审计;正文未改写)
本报告是 2026-09-04 当时状态 的只读快照。其中出现的下列称谓/结论可能已过时,请勿据此判断当前实现: - 旧品牌
serverless-sandbox/ 旧 CLIsbox→ 现为agent-sandbox/abx,见 ADR2026-09-09-brand-rename-sbox-to-abx.md。 - 自此已删除的 CLI 命令组(auth/session/secret/skill),见 ADR2026-09-23-cli-command-reduction.md。 - 自此已移除的旧架构(integrations集成层 / 能力门控 / 序列化 / keychain),见 ADR2026-09-23-overdesign-cleanup.md。当前权威说明以
AGENTS.md与docs/为准。
PyPI 发布就绪审计报告(Serverless Sandbox SDK + sbox CLI)
- 日期:2026-09-04
- 任务 ID:95
- 模式:只读审计(未修改任何代码/配置)
- 安全声明:本报告不包含、不引用任何 PyPI token/密钥值。发布应通过 CI 使用 GitHub Actions secret
PYPI_API_TOKEN完成。
1. Summary verdict(结论先行)
| 项目 | 结论 |
|---|---|
| 推荐发行名(distribution name) | serverless-sandbox |
sbox 作为发行名是否可用 |
不可用(已被占用) — PyPI 已存在名为 sbox 的项目(v1.1.0,Owner evryoneowo,"A simple secret manager...",MIT)。见证据 §5。 |
serverless-sandbox 是否可用 |
可用(HTTP 404) — PyPI 上尚无此发行名。见证据 §5。 |
当前 pyproject.toml 的 name |
已经是 serverless-sandbox(正确,无需改名) |
| 是否可以在修复前直接发布 | 否 — 存在若干 blocker(version 为 0.1.0-dev、URL 为 <personal-account>/* 占位符、无任何 CI/twine/build 自动化)。见 §4。 |
关键澄清:org 审批 ≠ 名称可用性
PyPI 的发行名(distribution name)是全局唯一命名空间。用户创建的 org 账户 sbox(display "Serverless Sandbox")仍在审批中,该审批只影响归属/组织命名空间与所有权管理,并不使 sbox 这个发行名变得可申请。由于 sbox 发行名早已被第三方(evryoneowo,2026-01-08 上传)占用,任何人(包括本 org)都无法再以 sbox 作为发行名发布。因此:
- 发行名必须用 serverless-sandbox(已可用)。
- CLI 命令名 sbox(console script)与发行名相互独立,可继续使用(详见 §3 的命名冲突分析)。
2. Packaging metadata table(现状 vs 发布所需)
证据文件:pyproject.toml(1-108)、src/serverless_sandbox/_version.py(1)。
| 字段 | 当前值(file:line) | 发布所需 | 状态 |
|---|---|---|---|
| build-backend | hatchling.build(pyproject.toml:2-3) |
hatchling/setuptools/poetry 任一 | OK(hatchling) |
[project].name |
serverless-sandbox(:6) |
全局唯一、可用 | OK(PyPI 404 可用) |
| version | dynamic → _version.py:1 = "0.1.0-dev" |
稳定版如 0.1.0 |
BLOCKER(dev 预发布版) |
| description | Serverless Sandbox SDK — ...(:8) |
一句话摘要 | OK |
| readme | README.md(:9) |
存在且非空 | OK(README.md 129 行,hatchling 自动设 Description-Content-Type: text/markdown,无需手写 content-type) |
| license | Apache-2.0(SPDX 表达式, PEP 639)(:10) |
合法许可证 | OK(见 §4 关于 classifier 重复的小问题) |
| requires-python | >=3.10(:11) |
明确下限 | OK |
| authors | Anycodes(:12-14) |
— | OK(可选:补 email) |
| keywords | sandbox/serverless/ai/agent/cloud/e2b(:15) | — | OK |
| classifiers | 见 :16-26(含 Alpha、Apache、Py3.10-3.13) | 与 license/python 一致 | 基本 OK(License classifier 与 SPDX 表达式并存,见 §4) |
| dependencies | httpx[http2]>=0.27, websockets>=12.0, pydantic>=2.0, python-dotenv>=1.0(:28-33) | 运行期最小集 | OK |
| optional-dependencies | cli / mcp / fast / declarative / session / all / dev(:35-67) | extras | OK(CLI 依赖在 [cli] extra,见 §3) |
[project.scripts] |
sbox = "serverless_sandbox.cli.main:cli"(:69-70) |
存在 sbox 入口 |
OK(存在且指向正确) |
[project.urls] |
Homepage/Documentation/Repository/Issues 全部指向 github.com/<personal-account>/serverless-sandbox*(:72-76) |
真实公开 URL | BLOCKER(占位符) — 任务说明已计划后续修复,此处仅 FLAG |
| wheel packages | packages = ["src/serverless_sandbox"](:81-82) |
src-layout 正确 | OK |
| version 来源 | [tool.hatch.version] path = "src/serverless_sandbox/_version.py"(:78-79) |
— | OK |
| package data / force-include | 无(无 [tool.hatch.build.targets.wheel.force-include]、无 artifacts) |
若运行期需要模板数据则需配置 | 见 §4(本项目为纯 Python,无强依赖,属"提示级") |
3. 名称一致性分析(distribution vs import vs CLI)
| 维度 | 值 | 证据 |
|---|---|---|
| 发行名(pip install X) | serverless-sandbox |
pyproject.toml:6;README.md:5-6;docs/DESIGN.md:18 |
| 导入名(import Y) | serverless_sandbox |
src 目录名 src/serverless_sandbox/;entry point 目标 serverless_sandbox.cli.main:cli(:70) |
| CLI 命令名 | sbox |
[project.scripts] sbox = ...(:70) |
协调性结论:三者是有意为之的标准组合(发行名连字符 serverless-sandbox ↔ 导入名下划线 serverless_sandbox ↔ 短命令 sbox),内部一致、无技术缺口。docs/DESIGN.md:18 明确记载该设计。
但存在两个必须提示用户的"命名陷阱":
pip install sbox会装错包。PyPI 上sbox是第三方的 "secret manager"(evryoneowo)。用户若直觉性执行pip install sbox,装到的是别人的库,不是本项目。README/文档必须明确:安装用pip install serverless-sandbox,安装后才有sbox命令。- CLI 命令名
sbox与第三方发行名sbox同名。二者不会在文件层直接冲突(第三方sbox提供的是可导入模块site-packages/sbox/,且其 metadata 未声明 console_scripts;本项目提供的是可执行文件bin/sbox)。因此在同一环境同时安装两者通常不会互相覆盖脚本,但用户心智层面易混淆,建议在文档中说明。 - CLI 依赖是 extra:
sbox命令依赖 click/rich/pyyaml,位于[project.optional-dependencies].cli(:36-40),不在核心 dependencies。裸pip install serverless-sandbox后运行sbox可能因缺 click 而报错。建议文档指引pip install "serverless-sandbox[cli]"(或[all])。(这是一个可用性 blocker,见 §4。)
4. Blockers / 待修清单
硬 Blocker(发布前必须解决)
- version 为预发布
0.1.0-dev(_version.py:1)。PEP 440 会将其规范化为0.1.0.dev0,上传后成为 dev 预发布版,pip install serverless-sandbox默认不会安装 dev 版(除非--pre)。→ 发布前须改为0.1.0。 [project.urls]全为<personal-account>/*占位符(pyproject.toml:72-76)。指向未确定/占位仓库,PyPI 页面会展示错误链接。→ 任务说明指出后续 push-ready 任务会修,此处仅 FLAG,不修。- 无任何发布自动化(详见 §6):无
.github/workflows/(整个目录不存在)、Makefile 无build/dist/publish/release目标、无 twine 配置。→ 需新增 CI 发布流程或至少本地python -m build+twine check+twine upload流程。
可用性 Blocker(强烈建议发布前解决)
- CLI 依赖未在核心 dependencies(:28-40)。裸安装后
sbox命令缺 click/rich/pyyaml。→ 文档须指引[cli]/[all]extra,或考虑将 CLI 依赖并入核心。
提示级(非阻断,但建议关注)
- License classifier 与 SPDX 表达式并存:
license = "Apache-2.0"(PEP 639 表达式,:10)同时又有旧式 classifier"License :: OSI Approved :: Apache Software License"(:19)。新版 core-metadata 下二者并存可能触发工具弃用告警(twine check一般仍 PASS,但属不一致)。建议二选一(保留 SPDX 表达式,移除 License classifier)。 - *package data(examples/templates/.yaml、各 README)未纳入 wheel:wheel 仅打包
src/serverless_sandbox(:81-82),而examples/templates/**/sandbox-template.yaml(10 个模板)与 README 均位于src/之外。经核查,包内代码无importlib.resources/__file__加载这些数据文件(Grep 无匹配),即项目为纯 Python 包、运行期不依赖这些数据,故不构成技术 blocker;这些属于示例资产,随 sdist/仓库分发即可。若未来将模板内置进包,需补[tool.hatch.build.targets.wheel.force-include]或 artifacts。 - long_description content-type:无需手写。
readme = "README.md"(:9)由 hatchling 自动注入text/markdown,不是 blocker(任务清单里列的"content-type 缺失"在本项目不成立)。
5. Web 检查证据(PyPI 名称可用性)
PyPI 网页版有 JS 挑战,故改用 PyPI JSON API(返回干净的 200/404)验证:
sbox — HTTP 200(已占用,不可用)
- URL:
https://pypi.org/pypi/sbox/json - 关键字段(引自响应):
"name":"sbox","version":"1.1.0""summary":"A simple secret manager with Pydantic model migration support""author_email":"evryoneowo <...>"(邮箱已在本报告隐去细节)"license":"MIT","project_urls":{"Homepage":"https://github.com/evryoneowo/sbox"}"ownership":{"organization":null,"roles":[{"role":"Owner","user":"evryoneowo"}]}- 首次上传
2026-01-08T13:02:56(1.0.0),最新 1.1.0 上传2026-01-09 - 结论:
sbox发行名归属第三方,无法申请。
serverless-sandbox — HTTP 404(可用)
- URL:
https://pypi.org/pypi/serverless-sandbox/json→ 返回404(工具响应:error: code = 60001 message = 404) - 结论:
serverless-sandbox发行名当前无人占用,可申请发布。
说明:404 = 名称未注册 = 可用;200 = 已存在 = 被占用。此为 PyPI 官方 JSON API 的标准行为。
6. 现有发布自动化盘点(存在 vs 缺失)
| 类别 | 现状 | 证据 |
|---|---|---|
.github/workflows/*.yml |
完全不存在 | Glob .github/workflows/* → 0 结果;Glob **/*.yml → 0 结果;.github/ 目录仅含 CODE_OF_CONDUCT/CONTRIBUTING/PULL_REQUEST_TEMPLATE/SECURITY 及模板目录,无 workflows/ 子目录 |
Makefile build/dist/publish/release |
无 | Makefile(1-30) 仅有 install/dev/test/test-cov/lint/format/typecheck/clean/help;.PHONY(:1) 未含 build/publish/release/dist;clean(:27-28) 会 rm -rf dist/ 但无构建目标 |
| twine 配置 | 无 | 全仓 Grep twine|python -m build|pypi → 仅 README 徽章与文档提及,无任何 twine/构建脚本;无 .pypirc、无 tox.ini 发布环境 |
| 版本来源 | 有(dynamic) | [tool.hatch.version](:78-79) 读 _version.py |
| CI secret | 已配置(用户侧) | GitHub repo secret PYPI_API_TOKEN(用户已配置,本报告不涉及其值) |
结论:发布自动化 从零开始——无 workflow、无 build/publish make 目标、无 twine 配置。全部需要新建。
7. 推荐的安全发布路径(含两种鉴权方案对比)
7.1 前置修复(发布前)
_version.py→__version__ = "0.1.0"(去掉-dev)。[project.urls]→ 替换为真实公开 URL(后续 push-ready 任务处理,本任务仅 FLAG)。- (建议)移除重复的 License classifier,保留 SPDX
license = "Apache-2.0"。 - (建议)README 顶部明确安装命令:
pip install "serverless-sandbox[cli]",并提示"sbox是命令名,PyPI 上同名发行名不属于本项目"。
7.2 构建 + 校验(本地或 CI)
python -m pip install --upgrade build twine
python -m build # 生成 dist/*.whl 与 dist/*.tar.gz
twine check dist/* # 校验 metadata / long_description 渲染
twine check 应输出 PASSED;若报 license classifier 与 expression 冲突告警,回到 7.1.3 处理。
7.3 先 TestPyPI 演练(DRY-RUN),再正式 PyPI
- TestPyPI 演练:
twine upload --repository testpypi dist/*,随后在干净虚拟环境pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "serverless-sandbox[cli]"验证import serverless_sandbox与sbox --help。 - 正式发布:
twine upload dist/*(CI 中用 secret 注入,绝不硬编码 token)。
PyPI 不可逆性(务必周知): - 同一发行名的同一版本号一旦上传即永久占用,不可重传/覆盖(即使删除文件,版本号也不能复用)。 - yank(撤回)只是标记,仍消耗该版本号——
0.1.0一旦 yank 也不能再发0.1.0,只能升0.1.1。 - 因此正式发布前务必先在 TestPyPI 演练,并对0.1.0的 metadata 做最终确认。
7.4 CI 发布 workflow 的两种鉴权方案对比
| 维度 | 方案 A:pypa/gh-action-pypi-publish + PYPI_API_TOKEN(token 方式) |
方案 B:PyPI Trusted Publishing(OIDC,无长期 token) |
|---|---|---|
| 现状契合度 | 高——用户已配置 repo secret PYPI_API_TOKEN,可立即使用 |
需在 PyPI 项目侧预先配置 trusted publisher(owner/repo/workflow/environment),且要求发行名已存在或先创建项目 |
| 密钥管理 | 需长期保管 token;泄漏风险高(本次即因 token 在聊天中暴露,应视为已泄漏并轮换/吊销) | 无长期密钥,CI 运行时用短时 OIDC 令牌,安全性最佳 |
| 权限范围 | token 作用域可能覆盖账户多个项目(除非用 project-scoped token) | 精确绑定到单个 PyPI 项目 + 单个 workflow |
| 配置复杂度 | 低(with: password: ${{ secrets.PYPI_API_TOKEN }}) |
中(PyPI 侧 + workflow permissions: id-token: write) |
| 推荐 | 作为立即可行的过渡方案 | 作为中长期首选;建议 serverless-sandbox 首发后尽快切换到 Trusted Publishing |
建议:因用户已备好 PYPI_API_TOKEN,首版 CI 用方案 A 快速打通;但因该 token 已在聊天中暴露,必须在 PyPI 侧吊销/轮换该 token,且 CI 只通过 secret 引用、绝不打印。发布流程跑通后尽快迁移到方案 B(Trusted Publishing) 以消除长期 token 风险。
注意:
sboxorg 审批 pending 不阻塞以serverless-sandbox名义发布——发行名可用性与 org 归属是两回事。org 审批通过后仅影响该项目的组织归属管理,可在发布后再把项目纳入 org。
8. Evidence 汇总(file:line + web)
代码/配置证据:
- pyproject.toml:2-3 build-backend = hatchling
- pyproject.toml:6 name = "serverless-sandbox"
- pyproject.toml:7 dynamic = ["version"]
- pyproject.toml:9 readme = "README.md"
- pyproject.toml:10 license = "Apache-2.0"(SPDX)
- pyproject.toml:11 requires-python = ">=3.10"
- pyproject.toml:19 License classifier(与 :10 并存)
- pyproject.toml:28-33 core dependencies
- pyproject.toml:35-67 optional-dependencies(cli/mcp/fast/declarative/session/all/dev)
- pyproject.toml:69-70 [project.scripts] sbox = "serverless_sandbox.cli.main:cli"
- pyproject.toml:72-76 [project.urls] 全部 github.com/<personal-account>/serverless-sandbox*(占位符)
- pyproject.toml:78-79 [tool.hatch.version] path = src/serverless_sandbox/_version.py
- pyproject.toml:81-82 [tool.hatch.build.targets.wheel] packages = ["src/serverless_sandbox"](无 force-include)
- src/serverless_sandbox/_version.py:1 __version__ = "0.1.0-dev"
- Makefile:1,6-29 无 build/dist/publish/release 目标
- .github/(list_dir)无 workflows/ 子目录;Glob .github/workflows/*=0、**/*.yml=0
- Grep importlib.resources|__file__|files( 于 src/serverless_sandbox/:无数据文件加载(纯 Python 包)
- examples/templates/**/sandbox-template.yaml(10 个模板,位于 src/ 之外,未纳入 wheel)
- README.md:5-6 徽章已指向 pypi.org/project/serverless-sandbox/
- docs/DESIGN.md:18 "Python 包名 serverless-sandbox(PyPI),导入名 serverless_sandbox"
Web 证据:
- https://pypi.org/pypi/sbox/json → 200:"name":"sbox", "version":"1.1.0", "summary":"A simple secret manager with Pydantic model migration support", owner evryoneowo, MIT, 首传 2026-01-08 → 名称已占用
- https://pypi.org/pypi/serverless-sandbox/json → 404 → 名称可用
- (网页版 https://pypi.org/project/sbox/ 与 /project/serverless-sandbox/ 因 JS 挑战无法直接抓取,故采用官方 JSON API 判定 200/404)
9. 只读声明
本次审计未修改 pyproject.toml、未新增 workflow、未构建包、未接触任何 token 值。占位符 URL 修复由后续 push-ready 任务处理,本报告仅 FLAG。
10. SDK/CLI packaging structure options(追加范围:结构评估)
应 leader 追加范围(2026-09-04)补充。仍为只读评估,不涉及任何 token。
10.a 现状确认:单一发行(single distribution)
SDK 与 CLI 当前打包在同一个发行内,共用一个 wheel 与一个 sbox console-script 入口:
- 一个包目录 src/serverless_sandbox/,同时含 SDK 层(api/ transport/ protocol/ models/ session/ agent/ declarative/ integrations/)与 CLI 层(cli/)。
- 入口点:pyproject.toml:69-70 [project.scripts] sbox = "serverless_sandbox.cli.main:cli"。
- 打包范围:pyproject.toml:81-82 [tool.hatch.build.targets.wheel] packages = ["src/serverless_sandbox"](整个包,含 cli/)。
- CLI 依赖被隔离为 extra:pyproject.toml:36-40 [project.optional-dependencies].cli = [click, rich, pyyaml](核心 dependencies 不含 CLI 依赖)。
结论:未拆分,是"one-repo-one-distribution + CLI 作为 extra"的结构。
10.b 拆分可行性(split feasibility)
依赖方向干净、单向(CLI → SDK),无导入环,拆分在技术上可行。
- CLI 依赖 SDK:cli/commands/sandbox.py:40-51 导入 api.sandbox、agent.infer;cli/commands/sandbox.py:179-184 导入 models.sandbox / protocol.sandbox / transport.auth / transport.config / transport.http / utils.async_bridge;cli/main.py:81-96 导入 models.errors、cli.formatters。
- 反向为零:对 api/ models/ transport/ protocol/ session/ agent/ declarative/ integrations/ compat/ extensions/ utils/ 全量 Grep serverless_sandbox.cli / from .cli → 0 匹配。即 SDK 从不导入 CLI。
- 因此可安全拆为:serverless-sandbox(纯 SDK)+ 独立 sbox CLI 元包(install_requires = ["serverless-sandbox==<version>"])。无循环依赖阻碍。
拆分的成本与复杂点:
1. 命名占用(硬约束):独立 CLI 发行名想叫 sbox 不可行——PyPI 上 sbox 已被第三方占用(见 §5)。CLI 元包只能另取名(如 serverless-sandbox-cli),console script 仍可叫 sbox。这削弱了"拆出 sbox 包"的初衷。
2. 版本协同成本:两发行须锁版本(CLI 依赖 SDK 精确/兼容版本),发布节奏需同步,CI 需两次构建+两次上传+版本矩阵测试。
3. *package data(examples/templates/.yaml):这些资产位于 src/ 之外(§4.6),当前不随任何 wheel 分发、运行期也无代码加载(Grep importlib.resources/__file__ = 0)。因此拆分不会新增 package-data 冲突——但反过来说,若未来想让 sbox install <template> 内置模板,需决定模板数据归 SDK 还是 CLI 包,并补 force-include,这会成为拆分后的归属争议点。
4. 收益有限:当前用 extra([cli])已能实现"只装 SDK / 装 SDK+CLI"的分离效果,拆成两发行带来的额外收益(独立版本、独立依赖)相对其协同成本偏低。
10.c 二进制(freeze)可行性:shiv / pex / PyInstaller
运行期依赖集合(CLI 实际用到):
- 核心:httpx[http2]>=0.27、websockets>=12.0、pydantic>=2.0、python-dotenv>=1.0(pyproject.toml:28-33)
- CLI extra:click>=8.0、rich>=13.0、pyyaml>=6.0(:36-40)
- 可选 extras:mcp(:41-43)、orjson(fast, :44-46)、cloudpickle+msgpack(declarative, :47-51)
原生/二进制依赖(影响 freeze):
- pydantic>=2 → 依赖 pydantic-core(Rust 编译扩展)。PyInstaller 需 pydantic hook(社区已有,通常可用);shiv/pex 打包平台轮子即可,但产物平台相关。
- pyyaml → 可选 libyaml C 扩展(yaml.CParser),有纯 Python 回退。
- websockets → 可选 C 加速扩展,有纯 Python 回退。
- orjson(fast extra)→ Rust 编译;msgpack(declarative extra)→ C 编译。仅当启用相应 extras 时进入依赖图。
- 结论:原生依赖使 shiv/pex 产物非跨平台通用(需按 OS/arch/Python 分别构建),PyInstaller 需正确 hooks。均可行但增加构建矩阵复杂度。
动态导入(freeze 的主要破坏点):
- cli/main.py:44-51 _load_command() 使用 importlib.import_module(mod_path),模块路径来自字符串字典 lazy_subcommands(main.py:144-158,13 条,如 "serverless_sandbox.cli.commands.sandbox:create")。freeze 工具做静态分析,看不到这些字符串动态导入 → 直接 freeze 会漏收 cli.commands.* 子模块,运行时 sbox create 报 ModuleNotFound。
- 缓解:PyInstaller --hidden-import/--collect-submodules serverless_sandbox.cli.commands;shiv/pex 无静态分析(打包整包 wheel),此问题较小,但仍需确保所有子模块在 wheel 内(当前满足)。
- cli/main.py:167 @click.version_option(package_name="serverless-sandbox") → 运行时用 importlib.metadata 读取已安装发行元数据。frozen 二进制内通常无 *.dist-info → sbox --version 可能抛错。需在 freeze 前改为静态版本或补 metadata。
- 其余大量 CLI→SDK 导入采用函数内惰性 import(如 cli/commands/sandbox.py:40,179-184),属正常静态可分析导入,freeze 工具可识别。
插件/entry-point 发现:未发现基于 entry_points/pkg_resources 的插件发现(Grep = 0);唯一 entry point 是自身的 console script。动态性集中在上面的 LazyGroup。
数据文件访问:包内无 importlib.resources/__file__ 数据加载(§4.6)→ 模板 yaml 由用户路径读入,非包内资源,freeze 无数据文件收集问题。
重要定性:二进制(freeze)是分发 UX 选项(免 Python 环境、单文件执行),不是依赖环的修复手段——本项目本就无依赖环(§10.b)。是否 freeze 取决于"是否想给用户免安装的单文件 CLI",与打包结构正确性无关。
10.d 推荐
| 方案 | 评价 | 推荐度 |
|---|---|---|
A. one-repo-one-dist(现状:单发行 + [cli] extra) |
依赖单向无环、发布简单、版本天然一致;extra 已能区分 SDK-only 与 SDK+CLI | 首选(推荐维持) |
| B. two-dist(SDK + 独立 CLI 元包) | 技术可行但收益有限:sbox 名不可用、需版本协同、双份 CI;仅在 CLI 需完全独立发布节奏时才值得 |
次选(暂不建议) |
| C. two-repo(SDK 与 CLI 分仓) | 在 B 的成本上再加分仓协作/issue/CI 复杂度,当前团队规模不划算 | 不建议 |
D. binary(shiv/pex/PyInstaller 冻结 sbox) |
纯 UX 加分项(免环境单文件);需处理 LazyGroup 动态导入的 hidden-imports、version_option 的 metadata、pydantic-core/orjson/msgpack 原生扩展与跨平台构建矩阵 |
可选增强(非必需,且非结构修复) |
结论:维持 方案 A(单发行 serverless-sandbox + [cli] extra)。理由:SDK→CLI 依赖单向且无环,extra 机制已满足"轻量 SDK / 完整 CLI"两种安装诉求;拆分会引入 sbox 命名不可用、版本协同与双 CI 成本而收益有限。二进制冻结仅作为未来可选的分发 UX 增强,且必须先解决 LazyGroup 动态导入与 version_option metadata 两处 freeze 阻断点——它与"是否拆分""是否有依赖环"无关。