Skip to content

[grok-copresence][P0] 现在新起/重启任何共存节点都会被自己的 pre-spawn 审计拒掉 —— 生成侧写绝对路径,审计侧要求裸 "bun" #1016

Description

@vansin

一句话

产品自己生成的 config.toml 里写的是绝对路径,而它自己的 pre-spawn 审计要求那一格恰好是裸字符串 "bun"
两者由同一份代码在同一次启动里产生,不可能同时满足 ⇒ 现在 prepare 出来的共存节点,
过不了自己的审计。

证据链(四段,每段都可复验)

① 生成侧:解析器永远不可能返回裸命令名

agent-node/src/runtime/grok-build-cli-home.ts:60

export function resolveGrokCommhubMcpCommand(command: string, pathEnv: string): string {
  const candidates = isAbsolute(command) ? [command]
    : pathEnv.split(delimiter).filter(Boolean).map((directory) => resolve(directory, command));
  for (const candidate of candidates) {
    try {
      const canonical = realpathSync(candidate);
      if (!statSync(canonical).isFile()) continue;
      accessSync(canonical, constants.X_OK);
      return canonical;            // ← 唯一的成功返回,realpath 之后的绝对路径
    } catch {}
  }
  throw new Error("grok copresence CommHub MCP command could not be resolved or executed");
}

只有两种出口:绝对规范路径,或抛错。"bun" 不在其中。

② 那个值被原样写进 config.toml

agent-node/src/cli.ts:3682

const commhubMcpCommand = resolveGrokCommhubMcpCommand(process.env.BUN_BIN || "bun", process.env.PATH || "");

agent-node/src/runtime/grok-build-cli-home.ts:1471

`command = ${JSON.stringify(commhubMcp.command)}`,

(同一文件 :1067 还额外强制 realpathSync(command) === command,也就是必须 canonical。)

③ 真 grok 0.2.93 把它原样报成 target(实测,不是推断)

在容器里装钉死版本、--network none 离线跑(宿主那份是 1.0.5,且其符号链接被 63 个在跑节点共用,没有动):

容器内 grok --version → grok 0.2.93 (f00f96316d)
config.toml: command = "/usr/local/bin/fake-bun"

$ grok inspect --json
"mcpServers": [{
  "name": "commhub", "transport": "stdio",
  "target": "/usr/local/bin/fake-bun",
  "source": { "type": "configToml", "path": "/tmp/gh/config.toml" }
}]

target = config.toml 的 command,逐字,绝对路径。

④ 审计要求它恰好是裸 "bun"

agent-node/src/runtime/grok-copresence/runtime.ts:550

|| commhubRecord.target !== "bun"

beforeSpawn无条件挂上的(agent-node/src/cli.ts:3844):

beforeSpawn: () => auditAndPrepareRuntime().env,

所以每次 spawn 都跑这条审计,失败即 GrokCopresenceFailure: grok copresence pre-spawn audit failed

磁盘上的现状与这条推论一致

本机 ~/.anet-grok/node-*/config.toml(只读了 command 那一行):

node-47d475f619473b592af34838   command = "bun"                                    ← 旧代码写的,能过审计
node-a2f3e13fd1cab3e4f498a3a3   command = "bun"
node-c05a2744ac212c3f0c4ab709   command = "bun"
node-1a793d6b1274fe11f641001c   command = "/home/vansin/.nvm/…/bun/bin/bun.exe"    ← 绝对路径,按 :550 会被拒
其余 2 个没有 [mcp_servers.commhub] 段

node-1a793d6b… 的 session 目录是 %2Fhome%2Fvansin%2Fgrok-commdog-workspace通信狗

而当前 5 个 grok 共存节点(A站狗 / P站狗 / 通信狗 / 指挥狗 / TM宿舍狗)全部 status=offline

🔴 我不声称这就是每一个节点 offline 的原因 —— 那需要各自的启动日志。
上面成立的是一条结构性推论用当前代码 prepare 出来的配置,必然过不了当前代码的审计。
三个 command = "bun" 是历史遗留(旧版本写的),它们只要不重新 prepare 就还能跑;
一旦重启并重写 config.toml,就会变成绝对路径,然后被拒。

影响范围

  • 版本上:那行审计由 8167c494(2026-08-04)引入;@sleep2agi/agent-node@2.5.0-preview.31
    发布于 2026-08-12在跑的 preview 含这行。main 同样含。
  • 也就是说 main 和在跑的 preview 都在这个状态,不是「main 已修、线上未部署」。

不要按直觉修

有两个方向,选错会把现在还能跑的那三个也弄坏

  1. 把审计改成「和解析后的绝对命令比」(= 被我从 fix(grok): 把 #867 落到 main —— 含 #883(P0) 点名的修复,并修掉它自带的一处能力边界回归 #1010 摘掉的 7914755a 的方向)——
    对新配置正确,但会立刻拒掉那三个 command = "bun" 的旧配置
  2. 让审计同时接受两种形态(把 target 也按 PATH 解析一次再比)——
    兼容旧配置,代价是审计逻辑变复杂。

⚠️ 还有一件必须先确认的:那三个旧配置是「历史遗留」还是「某条代码路径仍会写裸 bun」
如果是后者,方向 1 会周期性地反复弄坏节点。我没有查到写裸 "bun" 的路径
resolveGrokCommhubMcpCommand 排除了这个可能),但**「我没找到」不等于「不存在」**。

#1011 的关系

#1011 是同一根因的另一半(夹具把 target 写死成 "bun",所以这个矛盾在 CI 里永远看不见)。
本条记的是后果:产品起不来。两条一起看才完整。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions