fix(cli): anet -v 声明 Bun 前置(此前唯一的硬前置在自报信息里是隐形的) - #744
Conversation
`anet -v` 此前只列 "Optional runtimes"(claude / codex),**完全不提 Bun** ——
而 `anet hub start` 对 Bun 是硬要求,缺了直接 process.exit(1)。
更糟的是组件缺失时它会说:
Nothing is broken — components are fetched the first time you run:
anet hub start # bootstraps commhub-server
没有 Bun 时这句是错的:`anet hub start` 不会「自动拉取后正常工作」,
它会在前置校验处退出。用户只能撞上去才知道。
改动:
- detectInstalledPackages 增加 bun 探测
- 新增独立一节 "Required to run a hub on this machine",不混进 Optional
—— 缺失语义完全不同:可选运行时缺了只是少一种 runtime,Bun 缺了 hub 起不来
- 缺失时给可直接执行的安装命令,而不是 "only needed for …"
- 组件缺失且 Bun 缺失时,把 "Nothing is broken" 那句收回来
措辞限定在「本机跑 hub」:节点连远程 hub 不需要 Bun,说成笼统必需是过度声称。
三个分支都真跑验证:
A 有 Bun → ✓ Bun v1.3.14
B 无 Bun、组件在 → ✗ Bun not found + 安装命令
C 无 Bun、组件也缺 → 上述 + ⚠️ 收回 "Nothing is broken"
输出自检无中文(面向用户文案保持英文)。
回归:改动前后 bun test src/ 均为 433 pass / 3 fail,且是同样三条测试名
—— 零新增失败。那 3 条是 main 上既有的红,与本改动无关。
|
Source-side review found two user-visible correctness issues that should be fixed before merge. The current CI failures are separate and should not drive a product-code workaround. 1. The version report does not mirror the actual Hub prerequisiteThe production guard accepts either executable: if (!commandExists("bunx") && !commandExists("bun")) { ... exit(1) }This PR only probes bun: detectCommandVersion("bun", "Bun")Therefore a PATH containing a valid 2. The newly recommended install command reintroduces the exact fail-open pipeline shape being removed elsewhereThe output tells operators to execute: curl -fsSL https://bun.sh/install | bashUnder shells without CI attributionThe observed red checks are unrelated infrastructure dependencies:
Those belong to #728 and are not evidence against this PR's CLI branch. Do not patch No source, CI rerun, merge, or production change was performed by this review. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e29a72bb81
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Bun 不是「可选运行时」,它是本机跑 hub 的硬前置:`anet hub start` 在 | ||
| // 缺 bun/bunx 时直接 process.exit(1)(见 hub start 里的前置校验)。 | ||
| // 此前自报里完全不提它,用户只能撞上去才知道 —— 这正是本次要修的。 | ||
| bun: detectCommandVersion("bun", "Bun"), |
There was a problem hiding this comment.
Check bunx before reporting the hub prerequisite as ready
When bun is present but bunx is absent, this reports ✓ Bun, but serverCommand always launches spawn("bunx", ...) at line 5708, so anet hub start still fails with ENOENT. Conversely, a usable bunx without a bun command is reported as missing even though the hub preflight at line 5689 accepts it. Detect the executable the hub actually launches, or make the launch path consistently fall back to bun x.
Useful? React with 👍 / 👎.
| if (pkg.state === "ok" && pkg.version) return `✓ ${pkg.displayName} v${pkg.version}`; | ||
| if (pkg.state === "unknown") return `✓ ${pkg.displayName} installed`; | ||
| return `✗ ${pkg.displayName} not found — \`anet hub start\` will fail without it ` | ||
| + `(commhub-server is bun-only). Install: curl -fsSL https://bun.sh/install | bash`; |
There was a problem hiding this comment.
Provide a platform-appropriate Bun installation command
On native Windows PowerShell, which this CLI explicitly supports in docs-site/docs/en/guide/windows.md:3-29, the suggested curl ... | bash command is not usable because bash is not available in that environment. A Windows user running anet -v to diagnose a missing local-hub prerequisite therefore receives a remediation that cannot install it; select Bun's PowerShell installer when process.platform === "win32".
Useful? React with 👍 / 👎.
| * 可选运行时缺了只是少一种 runtime,Bun 缺了 `anet hub start` 直接失败, | ||
| * 所以这里要给出可直接执行的安装命令,而不是一句 "only needed for …"。 */ | ||
| function formatRequiredBun(pkg: DetectedVersion): string { | ||
| if (pkg.state === "ok" && pkg.version) return `✓ ${pkg.displayName} v${pkg.version}`; |
There was a problem hiding this comment.
Reject Bun versions below the declared minimum
When an older Bun such as 1.1.x is on PATH, this branch prints ✓ Bun v1.1.x and treats the hub prerequisite as satisfied even though server/package.json:45-46 declares bun >=1.2.0 and the getting-started guide specifies the same minimum. Compare the detected version against the supported floor so the report does not direct users with an incompatible runtime to proceed to anet hub start.
Useful? React with 👍 / 👎.
| if (!isInstalled(versions.bun)) { | ||
| console.log("\n ⚠️ but `anet hub start` will not succeed until Bun is installed — see above."); |
There was a problem hiding this comment.
Replace the unconditional all-clear when Bun is missing
When either lazy component and Bun are both missing, the report still prints Nothing is broken and advertises anet hub start before appending this warning. That leaves the exact false all-clear this change intends to remove in the output, merely followed by a contradictory qualification; condition the earlier message on Bun being ready or use wording that says only the lazy components are healthy.
Useful? React with 👍 / 👎.
| * 所以这里要给出可直接执行的安装命令,而不是一句 "only needed for …"。 */ | ||
| function formatRequiredBun(pkg: DetectedVersion): string { | ||
| if (pkg.state === "ok" && pkg.version) return `✓ ${pkg.displayName} v${pkg.version}`; | ||
| if (pkg.state === "unknown") return `✓ ${pkg.displayName} installed`; |
There was a problem hiding this comment.
Do not mark a failed Bun probe as ready
If bun is discoverable but bun --version exits nonzero, times out, or emits an unparseable version, detectCommandVersion returns state: "unknown", yet this branch prints a success checkmark and isInstalled suppresses the missing-prerequisite warning. In that scenario the report cannot establish that the hard prerequisite is runnable, so it should surface an unverifiable or failed state rather than declaring Bun installed.
Useful? React with 👍 / 👎.
…till promise it) (#750) anet.sh 至今在多处用**现在时**声称:没装 Bun 时 `anet hub start` 会裸崩 `spawn bunx ENOENT`。这是 #235 之前的行为,#235 特意把它消灭了。 代码侧的真实行为(agent-network/bin/cli.ts:5652-5672,在 `if (!serverAlreadyRunning)` 内): if (!commandExists("bunx") && !commandExists("bun")) { ❌ anet hub start requires the Bun runtime (commhub-server is bun-only …) process.exit(1); } 那段代码的注释自己写了动机:裸 spawn ENOENT「user-hostile and misdirects troubleshooting toward Node internals instead of the actual missing dependency」。 文档现在指的正是那个被特意消灭的错误串。 分三类处理,不是全局替换: 1. getting-started(中英)与 clean-server 顶部(中英)—— 现在时的错误声称,改为 真实文案 + 退出码 1,并注明 #235 之前才是裸崩; 2. troubleshooting(中英)—— 标题里的 `spawn bunx ENOENT` 是**搜索锚点**, 从旧版本搜过来的人靠它找到本节,所以保留该字样,但把标题改成 `requires the Bun runtime` / `spawn bunx ENOENT` 并在正文首段给出当前真实输出; 3. clean-server:363 的复盘表**不动** —— 它记录的是当时那次部署踩到的历史现象, 而且「修复」列本来就写着 #235 给了 preflight + 友好提示,语境正确。 验证:docs-site 本地 npm run build 通过(该项目未设 ignoreDeadLinks,有死链即 fail)。 发现路径:核 #744(anet -v 不声明 Bun 前置)时顺带查文档,发现文档其实**声明了** 前置 —— 缺的是 CLI 自报;但文档对"缺了会怎样"的描述停在了 #235 之前。 Co-authored-by: vansin <smartflowaiteam@gmail.com>
通信牛在 #744 源码审里指出两条 correctness,都成立,我复验后修: ① **探测口径与 hub 守卫不同源。** 守卫是 if (!commandExists("bunx") && !commandExists("bun")) —— **任一存在即放行**。而自报只探 `bun`,于是「只有 bunx」的 PATH 上 会假报 "hub start will fail",而实际 hub 能起来。 假警报比不报更糟:它让人去装本来不需要装的东西,并开始怀疑其它自报信息。 ② **新文案教用户跑 `curl -fsSL https://bun.sh/install | bash`。** 这正是 #729/#733/#743/#728 一整条线在修的 fail-open 形状 —— 管道退出码只反映 consumer,producer 失败被吞掉。 我们自己在 CI 里把它当缺陷修,就不该在 CLI 里教用户这么做。 改为 `npm i -g bun`(有校验、可回滚)+ 官方安装页,由用户选。 三个 PATH 场景真跑: A 有 bun ✓ Bun v1.3.14 B 只有 bunx ✓ Bun (via bunx) v1.3.14 ← 假报已修 C 两个都没有 ✗ … Install with: npm i -g bun — or follow bun.sh/docs/installation 并确认输出里已无 `curl … | bash` 回归:bun test src/ 与 main 基线一致,无新增失败。 Co-authored-by: vansin <smartflowaiteam@gmail.com>
* fix(anet): a start that starts nothing must not report success — plus the bunx guard three copies got wrong
Found by doing what I had said I had not done: exercising the local-hub
auto-start in a clean container instead of on this box, where everything was
already installed and warm.
── 1. `anet node start --copresence` exited 0 having started nothing ──────────
waitForTmuxPaneText's poll loop abandoned itself when the pane was not listable
yet:
const paneTarget = tmuxPaneTarget(sessionName);
if (!paneTarget) return false; // neither resolves nor reschedules
The promise never settled, the event loop drained, and node exited 0. The
launcher printed two lines — the identity marker and "① app-server tmux=… " —
and returned SUCCESS with no bridge, no TUI, and nothing registered.
A pane is not listable the instant `tmux new-session -d` returns, so this fires
whenever the tmux server is not already warm: the FIRST node on a fresh machine.
That is why it never showed here and reproduced every single time in a container.
Instrumented to confirm the exact branch before touching it.
before rc=0, 1s, 2 lines, nothing running
after rc=1, 26s, "❌ app-server did not bind ws://127.0.0.1:24700 within 25s"
── 2. Three copies described a guard that had been tightened out from under them ──
`anet hub start` requires bunx SPECIFICALLY:
if (!commandExists("bunx")) // cli.ts, hub start
#766 tightened that from OR, with the reason written beside it: the only spawn
point is `spawn("bunx", …)`, so bun alone was never enough. Three other places
still described the OLD guard —
* the doctor probe, implementing the OR, reporting "fine" on a bun-only box
where `anet hub start` then exits 1;
* its comment, citing a #744 review that verified "守卫确实是 OR" (true then);
* the self-upgrade note, same claim, so a bun-only machine got no warning.
and this branch's own dependency preflight, written from one of those comments.
All four now key on bunx. Verified: a container with bun and no bunx passed the
old preflight, auto-start fired, and hub start died with "找到了 bun,但没有 bunx".
The hint distinguishes the two cases, because telling someone who HAS bun to
install bun is wrong twice:
ln -s "$(command -v bun)" "$(dirname "$(command -v bun)")/bunx" # bun is here, bunx is not
── 3. Two defects in the auto-start I added one commit ago ────────────────────
* It spawned a bare `anet hub start`. Not on PATH in the container; worse in
production, where whoever launched this may be running npx or a local build
and a bare name would start a DIFFERENT version's hub. Now re-invokes this
process's own entry point.
* It ignored the node's port. `anet hub start` defaults to 9200 regardless, so
a node on any other port got a hub it could never reach and a 60s timeout
against a perfectly healthy server. Observed: node on 9299, "Server:
http://127.0.0.1:9200". Now passes --port from the node's hub URL.
Its failure message also pointed at `tmux attach -t '=anet-hub'` — a session
that is usually already gone, because hub start exits on a failed preflight and
takes it with it. It now tees the hub's own output and prints that instead. That
change paid for itself immediately: the very next run said "anet: command not
found", which is how defect 3a above was found.
── Tests ─────────────────────────────────────────────────────────────────────
test750 grows L8, the layer that would have caught #1: a start that did not
bring the node up must exit non-zero and name the step. Witnessed red by putting
the bug back — unit tests stayed green (0 fail), only the suite caught it:
FAIL: start exited 0 while the node did not come up (rc=0) — the silent-success regression
The suite image needed a bunx symlink added, which is itself evidence: it went
red the moment the preflight learned the real requirement.
Suite is now 9 groups / 0 failures, 154s (up from ~70s — each failed start now
correctly waits out its 25s bind timeout instead of returning instantly).
Unit suite 546 pass / 0 fail.
Real-machine regression on a production node (waitForTmuxPaneText is shared):
通信牛 came up rc=0, sandbox=danger-full-access, hub-side get_inbox after
respawn.
Gates run locally: doc-symbol-pins OK (15/5/10/0, counts unchanged),
bun-install-pin OK, test-file-coverage OK.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(anet): route the bunx prediction through one predicate instead of restating the guard
test766-bunx-preflight went red on the previous commit, and it was right to.
That suite pins the hub-start guard by its literal text and mutates it to prove
the gate is live:
[[ "$(grep -Fc 'if (!commandExists("bunx")) {' agent-network/bin/cli.ts)" -eq 1 ]]
sed -i 's/if (!commandExists("bunx")) {/…old OR…/' agent-network/bin/cli.ts
Fixing the two stale copies by writing the same condition again took that count
from 1 to 3 — and `sed -i` would have mutated all three. The uniqueness
assertion is load-bearing, not incidental.
So the two places that PREDICT the guard now call a named predicate:
function bunxAvailable(): boolean { return commandExists("bunx"); }
and the guard itself keeps its literal, deliberately not routed through the
helper: test766 needs to see it to mutate it, and a helper call there would make
the guard invisible to its own test. That is written at the helper.
This is also the better shape for the original defect. Three independent
restatements of one condition are what drifted apart in the first place; two
call sites and one definition cannot.
test766: 4 pass / 0 fail locally, including both of its mutation checks — the
gate is still live, not merely green because the mutation no longer applies.
test750: 9 groups / 0 failures. Unit suite: 546 pass / 0 fail.
doc-symbol-pins OK (15/5/10/0, counts unchanged).
---------
Co-authored-by: t <t@t>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
问题
anet -v此前只列 "Optional runtimes"(claude / codex),完全不提 Bun ——而
anet hub start对 Bun 是硬要求,缺了直接process.exit(1)(cli.ts的 hub start 前置校验)。更糟的是,组件缺失时它会说:
没有 Bun 时这句是错的。
anet hub start不会「自动拉取后正常工作」,它会在前置校验处退出。唯一的硬前置在自报信息里是隐形的,用户只能撞上去才知道。
(文档侧 #720 已经写清了三条前置链,CLI 自报没跟上。)
怎么发现的
分流 #444 时,把它的
onboarding-cli-smoke.test.ts摘到当前 main 上跑,其中一条
version/setup-facing report declares the Bun prerequisite不通过。那个 PR 落后 422 个 commit,但这条断言指出的缺口至今还在。
改了什么
detectInstalledPackages增加 bun 探测;两者缺失语义完全不同:可选运行时缺了只是少一种 runtime,Bun 缺了 hub 起不来;
措辞刻意限定在「本机跑 hub」:节点连远程 hub 并不需要 Bun,说成笼统「必需」是过度声称。
验证:三个分支都真跑了
✓ Bun v1.3.14✗ Bun not found — anet hub start will fail without it … Install: curl -fsSL https://bun.sh/install | bash⚠️ but anet hub start will not succeed until Bun is installed — see above.B/C 用剥掉 PATH 的方式构造(
env PATH=<只含 sh 的临时目录>),不是 mock。输出自检无中文(面向用户文案保持英文)。
回归
改动前后
bun test src/均为 433 pass / 3 fail,且是同样三条测试名 —— 零新增失败。main上既有的红,与本改动无关。其中一条值得单独看:OpenCode agent-node release pairing > pins the exact versions being released together—— 它是一条故意设置的绊线(源码注释:the unit test intentionally fails when either
package is bumped independently),现在正红着,说明 pin 与已发布版本失同步。
我另开条目跟踪,不在本 PR 范围内。