feat(release): ship OpenTUI runtime via per-platform npm packages - #9885
Conversation
|
Thanks for the PR — this closes a real gap in the OpenTUI rollout. Template: complete ✓ — all required sections present, with a concrete reviewer test plan and local evidence. Problem: real, not theoretical. After #8677 lands, the npm-installed CLI runs under Node and the OpenTUI renderer silently falls back to ink — only the standalone archives (which bundle a pinned Bun) actually render OpenTUI. That is a documented distribution gap of the migration (milestone #8662), and the PR body lays out the before/after precisely. Direction: aligned — the point of the OpenTUI migration is that OpenTUI is the better default, and npm is the largest channel, so leaving it on the ink fallback would undercut the migration. Two things I'm flagging up front, neither a rejection:
Size: no core-module paths touched — release scripts, one workflow, a design doc, and one line in the root Approach: the per-platform Risk: no high-risk paths from the revert-correlation list are touched. The release.yml change is itself the sensitive surface: publish ordering (platform packages before the main package) and idempotent re-publish behavior are where review should focus. Publishing five new npm package names is additive but effectively irreversible once published — the dry-run default is the right guard. Moving on to code review, with the release-mechanics escalation noted. 🔍 中文说明感谢贡献——这个 PR 补上了 OpenTUI 迁移中一个真实的缺口。 **模板:**完整 ✓——必填章节齐全,有具体的 reviewer 测试计划和本地验证证据。 **问题:**真实存在,不是理论问题。#8677 合入后,npm 安装的 CLI 跑在 Node 上,OpenTUI 渲染器会静默回退到 ink——只有自带 pinned Bun 的 standalone 包才真正渲染 OpenTUI。这是迁移过程中已记录的发行缺口(milestone #8662),PR 描述把 before/after 讲得很清楚。 **方向:**对齐——OpenTUI 迁移的意义就在于 OpenTUI 是更好的默认渲染器,而 npm 是最大的分发渠道,让它停留在 ink 回退会削弱迁移本身。有两点提前说明,都不是拒绝:
**规模:**未触及核心模块路径——只有发布脚本、一个 workflow、一篇设计文档和根 **方案:**按平台 **风险:**未触及 revert 相关性列表中的高风险路径。release.yml 本身就是敏感面:review 应聚焦发布顺序(平台包先于主包)和重复发布的幂等行为。发布五个新的 npm 包名是增量操作,但一旦发布实际上不可撤销——dry-run 默认开启是正确的防护。 进入代码审查,并已记录发布机制升级事项。🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewThe approach is the right one — per-platform
Three findings, none a blocker:
No correctness or security blockers found. No existing platform-resolution logic existed in the repo to reuse; The launch chain the PR introduces, for orientation: sequenceDiagram
participant P1 as npm user
participant P2 as npm bin shim (node)
participant P3 as npm-bin.js launcher
participant P4 as Bun child (platform package)
P1->>P2: qwen args
P2->>P3: node npm-bin.js args
P3->>P3: resolve platform package via createRequire
P3->>P4: spawn bundled Bun on cli-entry.js, sets QWEN_CODE_LAUNCHER_PATH
P4-->>P3: close event with code or signal
P3-->>P1: mirror exit status
Test evidence — the PR's own CIThis commit has no code CI at all: the repo's CI workflow triggers only for bases
Everything behavioral therefore rests on the author's local report (macOS arm64 only, per the PR body — their claim, not independently re-run here; unattended CI does not execute PR code): launcher Sandboxed verification would settle this: 中文说明代码审查:方案正确——按平台 三个非阻塞发现:① 测试证据:该提交没有任何代码 CI——仓库 CI 只对 base 为 沙箱验证可以补齐: — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 3/5 — the code is genuinely good and the approach is the right pattern, but this is a release/public-contract change with no CI signal behind it, so it needs a human release-owner's call, not a bot approval. Stepping back: this is the right solution to a real problem. After #8677 lands, npm installs run under Node and OpenTUI silently falls back to ink while standalone users get the new renderer — the two channels diverge, and npm is the biggest channel. The per-platform Why I'm deferring instead of approving:
To get this over the line: #8677 lands, base re-pointed to Deferring to @pomelo-nwu @wenshao — release/public-contract change with no CI coverage on a stacked branch; needs a human call on the release mechanics and a green CI run once it's re-pointed at 中文说明信心:3/5 —— 代码质量确实好、方案也是正确的模式,但这是一个发布/公开契约层面的改动,且背后没有任何 CI 信号,需要人类发布负责人拍板,而不是机器人批准。 退一步看:这是对真实问题的正确解法。#8677 合入后,npm 安装在 Node 下运行,OpenTUI 会静默回退到 ink,而 standalone 用户能拿到新渲染器——两个渠道分叉,而 npm 是最大的渠道。按平台 为什么是"暂缓"而非"批准":
推进路径:#8677 合入 → base 改回 暂缓并转交 @pomelo-nwu @wenshao —— 发布/公开契约改动,且 stacked 分支上无 CI 覆盖;需要人类对发布机制拍板,并在其改回 — Qwen Code · qwen3.8-max Reviewed at |
|
⏸️ Deferring to @pomelo-nwu @wenshao — release/public-contract change (main package |
d6e42a6 to
dd0ef60
Compare
|
Please do not rebase or force-push to an active PR as it invalidates existing review comments. Note for future reference, the bots always squash all changes into a single commit automatically as part of the integration. 中文请勿对活跃的 PR 执行 rebase 或 force-push,因为这会使已有的评审评论失效。另外,供日后参考:作为集成流程的一部分,机器人始终会自动将所有改动压缩(squash)为单个提交。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
2 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R1-5 musl launcher error lacks the documented fallback pointer — already reported (triage stage-2 review, comment 5392897105)
- R1-6 optional-dependencies.json unconsumed, platform list triplicated — already reported (triage stage-2 review, comment 5392897105)
Not reviewed: build-and-test — no code CI (unit / lint / typecheck / integration) fires on the stacked base feat/opentui-migrate (only bot orchestration checks ran); the existing scripts/tests suite was not run in full locally (package-assets.test.js ran during verification: 34/34).
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 2 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:build-and-test — no code CI (unit / lint / typecheck / integration) fires on the stacked base feat/opentui-migrate (only bot orchestration checks ran); the existing scripts/tests suite was not run in full locally (package-assets.test.js ran during verification: 34/34)。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
— qwen3.8-max via Qwen Code /review (v0.22.0)
dd0ef60 to
6a1ae8a
Compare
a2c3a48 to
b25ee16
Compare
6a1ae8a to
0671500
Compare
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
2 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- musl/Alpine fallback pointer — already reported (triage stage-2 review, comment 5392897105)
- optional-dependencies.json unconsumed — already reported (triage stage-2 review, comment 5392897105)
Not reviewed: build-and-test — no code CI fires on the stacked base feat/opentui-migrate (CI filters bases main/release/**); build-test found no workspace affected by the diff (root-level scripts only). scripts/tests release-workflow + package-assets suites were run green (47/47) by Agent 7, but no lane covers the new scripts end-to-end.
Not explored to full depth (tool budget reached): "agent 5": none (no check was cut short)..
Not reviewed: reverse audit — stopped before round 8 by the review time budget.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 2 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:build-and-test — no code CI fires on the stacked base feat/opentui-migrate (CI filters bases main/release/**); build-test found no workspace affected by the diff (root-level scripts only). scripts/tests release-workflow + package-assets suites were run green (47/47) by Agent 7, but no lane covers the new scripts end-to-end。
未探索到全部深度(达到工具调用预算):"agent 5":none (no check was cut short).。
未审查:反向审计——评审时间预算不足,未能开始第 8 轮。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
— qwen3.8-max via Qwen Code /review (v0.22.0)
0671500 to
5ad434d
Compare
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
2 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- musl/spawn-error branch lacks the documented fallback pointer (scripts/npm-bin.js:117) — already reported (triage stage-2 review, comment 5392897105)
- optional-dependencies.json dead artifact (scripts/package-npm-platform-packages.js:186) — already reported (triage stage-2 review, comment 5392897105)
Not reviewed: build-and-test — diff touches only root-level files outside every workspace; build-test scoped to zero suites, and no code CI fires on the stacked base feat/opentui-migrate.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Convergence: round 3 posted 21 inline comment(s), 3 of them reported for the first time; the previous round posted 19 (7 new). Findings keep coming back to the same files: .github/workflows/release.yml (findings in rounds 1, 2; 1 more now); scripts/package-npm-platform-packages.js (findings in rounds 1, 2; 1 more now). A cluster that keeps producing siblings usually means the fixes are treating instances of a shared root cause — triaging that cause before the next round, or splitting an independent cluster into its own pull request, tends to end the loop faster than fixing them one at a time. (Observation only — nothing was withheld from this review because of this observation.)
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 2 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:build-and-test — diff touches only root-level files outside every workspace; build-test scoped to zero suites, and no code CI fires on the stacked base feat/opentui-migrate。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
收敛情况:第 3 轮发布了 21 条行内评论,其中 3 条是首次提出;上一轮发布了 19 条(其中 7 条首次提出)。发现反复回到同一批文件:.github/workflows/release.yml(第 1、2 轮已出过发现,本轮又有 1 条);scripts/package-npm-platform-packages.js(第 1、2 轮已出过发现,本轮又有 1 条)。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。(仅为观察——本轮评审未因此扣留任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.0)
…ints, release guards (R1-1..R1-13, R2-1..R2-7)
|
Review batch 3 pushed in ccc5f52. Every Critical and most Suggestions from all three rounds are addressed; per-thread replies follow. Critical
Suggestions
Verification: 中文说明第三批评审修复已推送(ccc5f52874)。三轮评审的全部 Critical 与大部分 Suggestion 已处理,逐 thread 回复见下。 Critical:R1-1 信号重抛前先移除转发监听器;R1-2(含 Suggestion:新增 验证:相关 scripts 测试套件全绿、 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
2 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R2-3 publish-guard block duplication (.github/workflows/release.yml:529) — still stands; author deferred to a follow-up, settled in its thread
- R2-4 launcher forwards only a terminating-signal subset (scripts/npm-bin.js) — partially addressed (SIGQUIT added); author's cross-platform rationale settled the thread
Not reviewed: the executable-script lint — Dockerfile: hadolint is not installed.
Not explored to full depth (tool budget reached): "agent 8a": none — all six invariant areas were walked to a conclusion..
Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 5.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Test Plan (not a blocker): 3 assertions PASS — this review observed 22264 passed.
Deferred under the convergence posture (round 4, not a blocker) — recorded, not requested in this round:
scripts/npm-bin.js:34 — [probe] launcher/manifest platform sets unpinned against RELEASE_TARGETS (drift mutant keeps 94 tests green)scripts/package-npm-platform-packages.js:149 — [probe] the no-exports-map resolution contract is unpinned (adding exports silently disables the Bun channel)docs/design/npm-platform-runtime-packages.md:39 — [review] design doc contradicts shipped code (QWEN_CODE_LAUNCHER_PATH stamping; exit-1 fallback claim)
Convergence: round 4 posted 20 inline comment(s), 19 of them reported for the first time; the previous round posted 21 (3 new). Findings keep coming back to the same files: scripts/npm-bin.js (findings in rounds 1, 2; 1 more now); scripts/prepare-package.js (findings in rounds 1, 2; 1 more now). The rate of new findings is not falling. A cluster that keeps producing siblings usually means the fixes are treating instances of a shared root cause — triaging that cause before the next round, or splitting an independent cluster into its own pull request, tends to end the loop faster than fixing them one at a time. Batching the remaining fixes and verifying them before the next push, or dropping this PR's reviews to --severity-floor critical, keeps the loop from re-deriving the same set. (Observation only — nothing was withheld from this review because of this observation.)
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 2 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:the executable-script lint — Dockerfile: hadolint is not installed。
未探索到全部深度(达到工具调用预算):"agent 8a":none — all six invariant areas were walked to a conclusion.。
未审查:反向审计——在 5 轮的反审轮数上限内未收敛。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
Test Plan(非阻断):3 assertions PASS — this review observed 22264 passed。
收敛姿态下延后(第 4 轮,非阻断)——已记录,本轮不要求修改:共 3 条(原文未翻译,列表见上方英文部分)。
收敛情况:第 4 轮发布了 20 条行内评论,其中 19 条是首次提出;上一轮发布了 21 条(其中 3 条首次提出)。发现反复回到同一批文件:scripts/npm-bin.js(第 1、2 轮已出过发现,本轮又有 1 条);scripts/prepare-package.js(第 1、2 轮已出过发现,本轮又有 1 条)。新发现的产出速度没有下降。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。把剩余修复攒成一批、验证后再推送,或将本 PR 的评审降到 --severity-floor critical,可以避免循环反复推导同一组发现。(仅为观察——本轮评审未因此扣留任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.0)
…ck, test hardening (R4-1..R4-20)
|
Review batch 4 pushed in c63e912 — all 20 R4 comments addressed (per-thread replies posted, threads resolved). Critical fixes
Suggestion fixes — hardcoded platform list replaced by manifest-derived purge (R4-5 + matching test R4-6); launcher test rewrite: exact runtime paths incl. win32 Deferred — R4-10 (cross-launcher-root cleanup of stale version directories) needs live-session pins for activated version directories beyond the staging window; impact is bounded (platform payloads are now purged before activation, so stale roots no longer grow). Tracked as a follow-up issue per the review-round policy. Verification: scripts tests 70/70, cli update/install tests green, |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
1 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- QWEN_CODE_LAUNCHER_PATH design-doc drift (docs/design/npm-platform-runtime-packages.md:38) — already recorded on the PR in the round-4 deferred list (docs/design/npm-platform-runtime-packages.md:39)
Not reviewed: the executable-script lint — Dockerfile: hadolint is not installed.
Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 5.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Deferred under the convergence posture (round 5, not a blocker) — recorded, not requested in this round:
scripts/package-npm-platform-packages.js:61 — [review] --help prints a misleading version error and exits 1scripts/prepare-package.js:355 — [review] platform set hardcoded in two lists with no cross-check.github/workflows/release.yml:522 — [review] nothing pins the packaging step feeding the publish loopscripts/npm-bin.js:94 — [test] forwarder teardown before re-raise is unpinned (mutant survived)scripts/npm-bin.js:98 — [test] the ?? 1 exit-code fallback is unpinned (mutant survived)scripts/tests/npm-bin.test.js:70 — [review] call-through process.on spy leaks real signal handlers on the workerscripts/tests/npm-bin.test.js:20 — [review] no-exports-map contract for platform packages is unpinnedscripts/tests/npm-bin.test.js:99 — [review] a shell:true spawn mutant survives the suitescripts/tests/npm-bin.test.js:142 — [review] fallbackEntry's import.meta.url derivation is unwitnessed
Convergence: round 5 posted 17 inline comment(s), 17 of them reported for the first time; the previous round posted 20 (19 new). Findings keep coming back to the same files: scripts/tests/npm-bin.test.js (findings in round 4; 5 more now); scripts/npm-bin.js (findings in round 4; 4 more now); scripts/tests/package-npm-platform-packages.test.js (findings in round 4; 2 more now), and 3 more file(s). A cluster that keeps producing siblings usually means the fixes are treating instances of a shared root cause — triaging that cause before the next round, or splitting an independent cluster into its own pull request, tends to end the loop faster than fixing them one at a time. (Observation only — nothing was withheld from this review because of this observation.)
Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had no anchor this round could use either — none at all, one with no certifier, one certified by an identity other than the one this round runs under, or one this round's fetch refused or resolved to the head — so the next review re-reads the whole diff unless recovery grafts an earlier own anchor that the round running it can use onto the complete work list this round leaves behind, and keeps doing so until a round's marker carries an anchor again or a graft lands that the round running it can use. (Stated, not acted on — this changes nothing about what the round posts.)
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 1 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:the executable-script lint — Dockerfile: hadolint is not installed。
未审查:反向审计——在 5 轮的反审轮数上限内未收敛。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
收敛姿态下延后(第 5 轮,非阻断)——已记录,本轮不要求修改:共 9 条(原文未翻译,列表见上方英文部分)。
收敛情况:第 5 轮发布了 17 条行内评论,其中 17 条是首次提出;上一轮发布了 20 条(其中 19 条首次提出)。发现反复回到同一批文件:scripts/tests/npm-bin.test.js(第 4 轮已出过发现,本轮又有 5 条);scripts/npm-bin.js(第 4 轮已出过发现,本轮又有 4 条);scripts/tests/package-npm-platform-packages.test.js(第 4 轮已出过发现,本轮又有 2 条),另有 3 个文件。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。(仅为观察——本轮评审未因此扣留任何内容。)
机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有留下本轮可用的锚点——要么完全没有、要么没有认证者、要么由本轮运行身份之外的身份认证、要么被本轮的获取拒绝或解析为头提交——因此下一次评审将重读整个 diff,除非恢复流程把本轮能使用的更早自有锚点嫁接到本轮留下的完整工作清单上;并会一直如此,直到某一轮的标记重新带上锚点,或落地的嫁接能被运行该轮的评审使用。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.2)
… watcher, serve signal dedupe (R5 criticals)
- npm-bin: drop the failed child's signal forwarders before launching the fallback, so a re-raised death signal is no longer swallowed (launcher exited 0 for signal-killed runs).
- npm-bin: install a presence-only SIGINT watcher on win32; without one libuv's console control handler returns FALSE for CTRL_C_EVENT and Windows terminates the launcher instantly while the CLI child keeps running. Never forward: child.kill('SIGINT') maps to TerminateProcess on Windows.
- npm-bin tests: pin platform/arch in the happy path, normalize path separators in the /bun/ predicate, add a teardown witness asserting forwarders are removed on the spawn-error path, and rewrite the win32 test around the watcher contract.
- run-qwen-serve: dedupe signals redelivered within 50ms of drain start (process-group forwarding from the npm launcher, cgroup-wide stops) instead of treating them as an operator double-press; same shape as gemini.tsx's SIGINT_RERAISE_IGNORE_MS.
- run-qwen-serve tests: rework the force-exit test past the dedupe window and add a duplicate-delivery test covering the process-group shape.
|
Review batch 5 pushed in 9eb74f6 — all 17 R5 comments addressed (per-thread replies posted, threads resolved). Criticals — all five fixed:
Two Suggestions were implemented alongside the Critical fixes (they witness the same paths): R4-11 fallback-child close assertions and the first-child forwarder-teardown witness. Deferred to a follow-up (per AGENTS.md — this PR is past ~5 review rounds, so only Critical fixes land in this round; recording here so nothing is silently dropped):
Verification: |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
1 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- optional-dependencies.json unconsumed / platform list triplicated — already reported (round 1 as R1-6, triage stage-2 review, comment 5392897105)
Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 5.
Not reviewed: the executable-script lint — Dockerfile: hadolint is not installed.
Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.
Test Plan (not a blocker): 3 assertions PASS — this review observed 22267 passed.
Deferred under the convergence posture (round 6, not a blocker) — recorded, not requested in this round:
packages/cli/src/serve/run-qwen-serve.test.ts:10321 — [review] force-exit kill wiring lost its only positive witness (fix-induced by the R5-3 fix)packages/cli/src/utils/installationInfo.ts:291 — [probe] platform branch has no Windows-shaped test witnessscripts/tests/npm-bin.test.js:80 — [probe] suite leaks real signal listeners into the vitest workerscripts/tests/npm-bin.test.js:330 — [probe] win32 SIGTERM forwarder never invoked in the testscripts/tests/package-assets.test.js:779 — [probe] node-fallback cli-entry.js shipment is unpinnedscripts/tests/package-npm-platform-packages.test.js:161 — [probe] platform manifest shipment fields unpinned
Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had no anchor this round could use either — none at all, one with no certifier, one certified by an identity other than the one this round runs under, or one this round's fetch refused or resolved to the head — so the next review re-reads the whole diff unless recovery grafts an earlier own anchor that the round running it can use onto the complete work list this round leaves behind, and keeps doing so until a round's marker carries an anchor again or a graft lands that the round running it can use. (Stated, not acted on — this changes nothing about what the round posts.)
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 1 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:reverse audit — did not converge within the reverse-audit round cap of 5。
未审查:the executable-script lint — Dockerfile: hadolint is not installed。
未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。
Test Plan(非阻断):3 assertions PASS — this review observed 22267 passed。
收敛姿态下延后(第 6 轮,非阻断)——已记录,本轮不要求修改:共 6 条(原文未翻译,列表见上方英文部分)。
机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有留下本轮可用的锚点——要么完全没有、要么没有认证者、要么由本轮运行身份之外的身份认证、要么被本轮的获取拒绝或解析为头提交——因此下一次评审将重读整个 diff,除非恢复流程把本轮能使用的更早自有锚点嫁接到本轮留下的完整工作清单上;并会一直如此,直到某一轮的标记重新带上锚点,或落地的嫁接能被运行该轮的评审使用。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.2)
What this PR does
After #8677 lands,
npm install -g @qwen-code/qwen-codestill runs the CLI under Node, where the OpenTUI renderer silently falls back to ink — only the standalone archives (which bundle a pinned Bun runtime) actually render OpenTUI. This follow-up closes that gap by publishing the standalone runtime as five per-platform npm packages and wiring the main package's bin to them, the same distribution pattern used by @lydell/node-pty, @teddyzhu/clipboard in this repo, and by esbuild/swc/opencode across the ecosystem:scripts/package-npm-platform-packages.jsrepackages the five standalone archives into npm platform packages@qwen-code/qwen-code-{darwin-arm64, darwin-x64, linux-arm64, linux-x64, win-x64}. Each carries the pinned Bun build, the native renderer libraries, and the bundled CLI;os/cpumanifest fields make npm install exactly one per host.scripts/npm-bin.jsbecomes the main package's bin: it resolves the installed platform package for the current OS/arch and spawns its bundled Bun onlib/cli-entry.js, settingQWEN_CODE_LAUNCHER_PATHexactly like the standalonebin/qwenwrapper so the in-CLI updater relaunches correctly. No postinstall —--ignore-scriptsinstalls behave identically, and a missing platform package (--omit=optional, mirror gaps) produces a clear error pointing at the still-shipped node entry (cli-entry.js), which the launcher never hides.prepare-package.jsstamps the five platform packages intooptionalDependencies(version-locked to the main package version, mirroring@qwen-code/audio-capture) and shipsnpm-bin.jsas the bin.release.ymlpackages and publishes the platform packages before the main package, reusing the existing dry-run / already-published /--tagguards.Design doc with the full rationale (why launcher over postinstall copying, alternatives rejected):
docs/design/npm-platform-runtime-packages.md.Why it's needed
Without it, the npm channel and the standalone channel ship different renderers after #8677: npm users get ink (with its known flicker issues) while standalone users get OpenTUI. The whole point of the migration is that OpenTUI is the better default; npm is the largest distribution channel and should not be left behind.
Reviewer Test Plan
How to verify: the E2E chain below was run locally against this branch's artifacts (main package packed from
dist/, platform package packed fromdist/npm-platform/qwen-code-darwin-arm64/); reproducing needs nothing beyond npm.npm packboth packages and install them into a scratch prefix.qwen --versionruns through the launcher (Bun path) and prints the version.CSI ?1000h/?1006h), the composer, and DEC 2026 sync — OpenTUI is active, not the ink fallback.node <package-root>/cli-entry.jscommand was itself verified to run (ink fallback intact).npm installof the main package alone (no platform package available) still succeeds — the dependency is optional, so CI and--omit=optionalflows are unaffected.Evidence: local run on macOS arm64 — launcher
qwen --version→0.21.11(exit 0); PTY smoke 3/3 assertions PASS; fallback error path exit 1 with a working node command; yamllint clean onrelease.yml; eslint clean on the touched scripts.Tested on:
Environment: node 22/24 npm 10/11, Bun 1.3.14 (bundled in the platform package), built from
d6e42a6636.Not validated: real
npm publish(requires release secrets) — the publish steps mirror the battle-tested@qwen-code/audio-capturestep and run behind the release workflow's dry-run default; aworkflow_dispatchdry-run of the Release workflow on a branch is the natural pre-merge check. Windows launcher path is implemented per the standaloneqwen.cmdconventions but only smoke-checked structurally.Risk & Scope: additive to the release pipeline; no CLI runtime code changes. Main package
binchanges from the node entry to the launcher, which is the intended behavior change (npm users get OpenTUI); the node entry remains in the tarball as the documented fallback. The platform packages add ~50MB (compressed) per published platform per release — npm storage only, the JS tarball is unchanged apart from the launcher.Linked Issues: follow-up to #8677 (stacked on it); distribution aspect of the milestone tracked in #8662.
中文说明
本 PR 是 #8677 的后续(stacked PR,base 为
feat/opentui-migrate分支,#8677 合并后请将 base 改为main)。#8677 合入后,npm 安装的 CLI 仍在 Node 下运行,OpenTUI 渲染器会静默回退到 ink——只有自带 Bun 运行时的 standalone 包才能真正跑 OpenTUI。本 PR 补齐这一缺口:把 standalone 运行时发布为五个平台 npm 包(
@qwen-code/qwen-code-{darwin-arm64, darwin-x64, linux-arm64, linux-x64, win-x64},os/cpu字段让 npm 每台主机只装一个),主包 bin 换成npm-bin.js启动器——运行时解析对应平台包并用其内置 Bun 启动 CLI,行为与 standalone 的bin/qwen包装一致(含QWEN_CODE_LAUNCHER_PATH供 CLI 内更新器使用)。不使用 postinstall(对比 opencode 的做法):
--ignore-scripts安装行为一致、无安装期副作用、无半拷贝状态;平台包缺失时给出明确报错并指向仍在包内的 node 入口(ink 回退,已验证可跑)。发布流程在主包之前打包并发布平台包,复用现有@qwen-code/audio-capture发布步骤的 dry-run / 已发布跳过 / dist-tag 防护。本地已验证:两包
npm pack+ 安装 →qwen --version走 Bun 链路;PTY 冒烟 3/3 断言(mouse tracking / composer / DEC 2026)确认 OpenTUI 激活;删平台包后报错路径与 node 回退命令均实测可用。设计决策详见docs/design/npm-platform-runtime-packages.md。