Skip to content

fix(cli): anet -v 声明 Bun 前置(此前唯一的硬前置在自报信息里是隐形的) - #744

Merged
vansin merged 1 commit into
mainfrom
fix/version-report-declares-bun
Aug 12, 2026
Merged

fix(cli): anet -v 声明 Bun 前置(此前唯一的硬前置在自报信息里是隐形的)#744
vansin merged 1 commit into
mainfrom
fix/version-report-declares-bun

Conversation

@vansin

@vansin vansin commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

问题

anet -v 此前只列 "Optional runtimes"(claude / codex),完全不提 Bun ——
anet hub start 对 Bun 是硬要求,缺了直接 process.exit(1)(cli.ts 的 hub start 前置校验)。

更糟的是,组件缺失时它会说:

Nothing is broken — components are fetched the first time you run:
  anet hub start          # bootstraps commhub-server

没有 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 探测;
  • 新增独立一节 "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 — anet hub start will fail without it … Install: curl -fsSL https://bun.sh/install | bash
C 无 Bun、组件也缺 上述 + ⚠️ 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,且是同样三条测试名 —— 零新增失败。

⚠️ 那 3 条是 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 范围内。

`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 上既有的红,与本改动无关。
@vansin

vansin commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

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 prerequisite

The production guard accepts either executable:

if (!commandExists("bunx") && !commandExists("bun")) { ... exit(1) }

This PR only probes bun:

bun: detectCommandVersion("bun", "Bun")

Therefore a PATH containing a valid bunx but no bun is reported as “Bun not found / hub start will fail”, while the real guard permits startup. Add the missing branch to the behavior matrix and derive the displayed prerequisite from the same bun || bunx contract (including an honest label/version when only bunx is present).

2. The newly recommended install command reintroduces the exact fail-open pipeline shape being removed elsewhere

The output tells operators to execute:

curl -fsSL https://bun.sh/install | bash

Under shells without pipefail, the pipeline status reflects the consumer; a failed producer can be masked. #733/#743 and #728 explicitly treat that shape as unsafe/non-hermetic. A user-facing recovery instruction should not teach the shape the repository is removing. Use a fail-closed download-then-execute form or point to the authoritative installation page without claiming a single command is safely executable.

CI attribution

The observed red checks are unrelated infrastructure dependencies:

  • E2E: oven-sh/setup-bun@v2 hit socket hang-up / HTTP 503 before tests started;
  • L0/L1: qa-cli-02-network-create/Dockerfile failed at its existing RUN curl -fsSL https://bun.sh/install | bash.

Those belong to #728 and are not evidence against this PR's CLI branch. Do not patch cli.ts merely to satisfy those reds, but do not merge until the two product findings above are closed and the exact branch tests are rerun.

No source, CI rerun, merge, or production change was performed by this review.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread agent-network/bin/cli.ts
// Bun 不是「可选运行时」,它是本机跑 hub 的硬前置:`anet hub start` 在
// 缺 bun/bunx 时直接 process.exit(1)(见 hub start 里的前置校验)。
// 此前自报里完全不提它,用户只能撞上去才知道 —— 这正是本次要修的。
bun: detectCommandVersion("bun", "Bun"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread agent-network/bin/cli.ts
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`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread agent-network/bin/cli.ts
* 可选运行时缺了只是少一种 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}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread agent-network/bin/cli.ts
Comment on lines +1815 to +1816
if (!isInstalled(versions.bun)) {
console.log("\n ⚠️ but `anet hub start` will not succeed until Bun is installed — see above.");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread agent-network/bin/cli.ts
* 所以这里要给出可直接执行的安装命令,而不是一句 "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`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

vansin added a commit that referenced this pull request Aug 12, 2026
…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>
@vansin
vansin merged commit f4ac75c into main Aug 12, 2026
10 of 12 checks passed
@vansin
vansin deleted the fix/version-report-declares-bun branch August 12, 2026 20:12
vansin added a commit that referenced this pull request Aug 12, 2026
通信牛在 #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>
vansin added a commit that referenced this pull request Aug 20, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants