Grok Bot 开源复刻,卡点究竟在哪

xAI 把 Grok Bot 客户端做成黑盒,b-nnett 凭什么能用源码级方式复刻 0.18.0?本文拆解 2487 星仓库里五个真实工程卡点。

源仓库: b-nnett/grok-bot-0.18-reconstructed

grok-bot-0.18-reconstructed 是开发者 b-nnett 在 GitHub 上发起的非官方项目,目标是以源码导向(source-oriented)的方式复刻并扩展 xAI 的 Grok Bot 0.18.0 客户端。截至 2026 年 8 月 26 日,该仓库已获得 2487 颗星。它火起来的原因很直接——xAI 把 Grok Bot 的客户端逻辑做成黑盒,外部开发者拿不到 JS bundle 之外的任何说明,而这个项目把反编译、模块切分、接口对齐、扩展注入做成了可复现的工程流程,让”读懂 Grok”不再是少数人的特权。

问题一:闭源 bundle 如何拆出可用模块

拿到 0.18.0 发行包,第一关就是拆 bundle。主文件是带字符串混淆的 webpack 输出,直接 require 会报 Cannot find module './chunk-xxx'。根因是 xAI 用了 webpack5 + module federation 做了按需加载,运行时才拼装模块,磁盘上的文件其实只是半成品。

复刻者通过 webpack --json 导出模块清单,再用社区维护的 unwebpack 还原目录树:

npm i -g unwebpack
unwebpack ./grok-bot-0.18.0.asar -o ./src

解法关键:unpack 后保持原 chunk 编号,扩展代码放在 src/extensions/ 平行目录,import 路径与原模块一致,日后合并上游 patch 时 diff 最小。

问题二:扩展代码如何与原模块共存而不覆盖

直接修改拆出的源码会让 0.18.1 升级时整个 diff 炸掉。根因是原作者使用 ast-patch 这一工具,对每个目标函数做 AST 注入,注入位置通过 md5(moduleId + exportName) 锁定,避免版本漂移时误伤。

// src/extensions/streaming-token-counter/index.js
const { patch } = require('ast-patch');
patch('./src/core/chat/sendMessage.js', {
  exportName: 'sendMessage',
  after: 'stream.onToken'
}, (token) => {
  metrics.inc('tokens', token.length);
});

这样做的好处是上游发版只需要重跑 patch 脚本,无需重新解包整个 asar。

问题三:上下文窗口对齐 Grok 2 推理层

Grok Bot 0.18.0 默认调用 Grok-1 推理服务,复刻版若照抄请求体,会被服务端以 unsupported schema 拒掉。根因是 xAI 服务端从 2026 Q2 起强制要求 messages[].role 必须是 system|user|assistant|tool 之一,且 tool_calls 必须带 id

解法:在 src/adapters/grok2.js 里重写 schema 转换层:

function toGrok2(messages) {
  return messages.map(m => ({
    role: m.role,
    content: m.content,
    ...(m.tool_calls
      ? { tool_calls: m.tool_calls.map((t, i) => ({ ...t, id: t.id || `call_${i}` })) }
      : {})
  }));
}
export { toGrok2 };

问题四:模型 ID 漂移导致接口 404

0.18.0 写死了 grok-1.5-fast,但后端自 8 月初已下线该模型,迁移到 grok-2-mini。根因是 xAI 在加速模型迭代,硬编码必然踩坑。复刻版用 manifest.json + 启动时拉 /api/models 实时刷新:

{
  "modelAlias": {
    "fast": "grok-2-mini",
    "smart": "grok-2"
  }
}

启动钩子每 6 小时拉一次最新清单,404 自动降级到次级模型,避免单点失败。

问题五:插件签名校验怎么过

xAI 对官方 plugin 包做 RSA 签名,复刻版的扩展如果不签,会被 pluginLoader 拒绝加载。根因是签名链路在 node_modules/@xai/plugin-loader/lib/verify.js,硬编码公钥指向 xAI 自家 CA,外部证书一律拒绝。

解法是替换 verify.js 里的公钥到自签 CA。这一步只能本地用,作者在 issue #142 里特意警告过:把替换公钥的步骤自动化到 CI 会导致 plugin 拒绝任何加载请求,且明显违反 xAI 服务条款——这就是为什么仓库 README 把它单独标红。

Sources

  • GitHub Trending 2026-08-26(b-nnett/grok-bot-0.18-reconstructed,#2487stars)
  • r/LocalLLaMA 讨论串:“Reverse engineering closed AI clients”
  • 项目 issue #142:公钥替换的安全边界提醒