Motrix MDXP Bridge 端到端验收测试指南:`motrix` CLI 与 Electron / Server 双运行时的配对与下载全链路验证
本指南聚焦 Motrix 仓库 e2e/bridge 目录下的 MDXP bridge 端到端(E2E)验收测试体系,它用真实发布版 @motrix/cli(npm 包,仓库中以 devDependency 形式引入)驱动真实 Electron app 与 headless Node Server,完整验收 product 承诺的两段核心能力:device-code pairing(配对) 与 download invocation(下载调用)。读完本文,你将掌握三条测试链路(Electron / Server / 远程浏览器 Extension)的构建、运行、环境变量与退出码语义,以及仓库是如何用同一套 E2E 同时守卫安全对抗探针与双仓协议契约的。
关联文档为 e2e/bridge/README.zh-CN.md,对应的测试实体位于 e2e/bridge 目录内;文中源码级细节均以该目录下真实 spec / driver 及根目录 package.json 的 scripts 为据。
Bridge E2E 概览:两条本地运行链路
e2e/bridge 提供 MDXP bridge 的端到端验收测试,覆盖产品承诺的两段核心能力——device-code pairing 与 download invocation,并同时验证两种 runtime shell:
| 链路 | 文件 | Runner | 被测 shell |
|---|---|---|---|
| Electron | e2e/bridge/cli-pair-and-download.spec.ts | Playwright | 打包后的 Electron app(dist/main/index.cjs) |
| Server | e2e/bridge/server-leg.mjs | standalone node |
headless Node shell(dist/server/index.mjs) |
两条链路都会驱动真实 CLI binary 和真实 aria2 下载(使用本地、限速、确定性的 HTTP fixture,不访问公网),并断言完整流程:
pair → approve → token → download/add → completed → 落盘字节 → watch SSE
测试还包含对抗性探针:错误 token → 退出码 4、token 仅交付一次、deny(拒绝)路径,以及 Server 链路上的 Spec 9 self-approval-bypass(匿名自批准绕过必须被关闭)。
为什么要拆成两种形式?
Electron app 与 Node server 需要相反的 better-sqlite3 native ABI,不能共用同一份 node_modules。因此:
- Electron 链路放在标准 Playwright suite 中(
.spec.ts,会被*.spec.tsglob 捕获); - Server 链路是一个独立的
.mjsdriver,运行在单独的 node-ABI build(一个 git worktree)上——server-leg.mjs以.mjs结尾正是为了让 Playwright suite 的*.spec.tsglob 将其跳过,从文件命名上就隔离了 ABI 语境。
前置条件
- 主 checkout 中至少运行过一次
pnpm install。 aria2:默认会自动使用extra/<platform>/<arch>/aria2c中的 bundled binary(macOS/arm64 不需要额外配置)。如需使用系统aria2c,可通过MOTRIX_ARIA2_BIN覆盖。@motrix/cliCLI:由pnpm install自动安装(它是 devDependency)。E2E 从node_modules解析其打包后的 bin——不再有 in-tree 构建 CLI 的步骤。
从仓库看,这条「CLI 外置化」的策略在源码中有明确落点:package.json 的 devDependencies 声明 "@motrix/cli": "^0.5.0";而在 cli-pair-and-download.spec.ts 与 server-leg.mjs 中,均通过 createRequire(import.meta.url).resolve('@motrix/cli/dist/bin/motrix.js') 从 node_modules 解析出真实发布产物的入口文件来 spawn。也就是说,测试驱动的不是仓库内源码构建的「替身」,而是消费者视角下真正会被 pnpm i 装进来的发布版 CLI——这本身就是对 CLI 打包产物可用性的一层验收。
Electron 链路(Playwright)
主 checkout 必须携带 Electron 版本的 better-sqlite3 ABI(这是 pnpm install / pnpm start 后的默认状态)。如果你之前在这个 checkout 中运行过 pnpm start:server,请先恢复:
pnpm run rebuild:for-electron
该脚本在 package.json 中定义为 electron-rebuild --force --only better-sqlite3,作用是强制将 better-sqlite3 重新编译到 Electron 运行时所需的 ABI(与 rebuild:for-node = pnpm rebuild better-sqlite3 正好相反,两者分别对应两个 runtime shell)。
构建并运行:
pnpm build:electron # dist/{main,preload,renderer,worker}
pnpm exec playwright test e2e/bridge/cli-pair-and-download.spec.ts
说明:
- 这个 spec 会在每个 test 中启动真实 Electron app,并通过
window.motrix.invoke('bridge:resolvePair', …)approve pairing;这正是 PendingApprovalsSection 中 "Approve" 按钮调用的 IPC(spec 头注释明确指出该通道与 renderer 的pair-resolve.ts集成、进而走真实主进程DeviceCodeService.approve → PairingService.issueToken路径字节级一致)。 - 运行期间 Electron 会打开 headed window。在桌面 macOS session 中可以直接运行;Linux CI 中请使用
xvfb-run。 pnpm test:e2e(完整 suite)也会通过*.spec.tsglob 自动包含这个 spec。
spec 内部结构与对抗性探针(源码层展开)
cli-pair-and-download.spec.ts 共包含 5 个 test,分别验证:
- device-code pair:用 hermetic credentials 目录(
HOME/XDG_CONFIG_HOME指向 userDataDir 内cli-home,避免写真实 home)spawnmotrix --endpoint <bridge> pair --name e2e-cli;随后通过bridge:listPendingPairRequests轮询 inbox,断言请求出现在 token-free 的 DTO 中(userCode匹配/^[A-Z2-9]{4}-[A-Z2-9]{4}$/,且不携带token/deviceId),CLI stderr 打印了相同 human code;再经bridge:resolvePair({kind:'cli', requestId, decision:'allow'})批准,CLI 轮询拿到 token 后以退出码 0 收尾,bridge:listPaired中新增该 client,inbox 中请求消失。 - invocation:先启动
--json watch再add一个本地限速 fixture(2 MiB,256 KiB/s 节流,保证能观测到Downloading阶段并产生多条$/task/progressSSE 帧),轮询list直到completed,校验落盘字节数与 fixture size 完全一致,最后对watch进程发送SIGINT并断言其 stdout 的 NDJSON 帧中出现了$/task/progress与$/task/completed或$/stats。 - adversarial: wrong bearer token:携带伪造 token 调用
list,断言退出码为4且 stderr 匹配/auth/——即「AUTH」类失败的错误码约定。 - adversarial: one-time token delivery:直接向
/mdxp/pair/request发 JSON POST 造出请求,批准后首次/mdxp/pair/poll返回approved+ token,重放同一 poll 必须返回{ status: 'expired' }——token 只交付一次。 - adversarial: deny:批准路径改为
decision:'deny',poll 返回denied/expired且不含 token。
此外 spec 还内置了两个测试基建,值得复用理解:waitForEndpoint(userDataDir) 会轮询 app 启动后写入的 bridge/endpoint.json(含 port、pid、localToken);bridgeInvoke(page, channel, params) 把 channel+params 通过 page.evaluate 的参数序列化传进浏览器上下文,在页面内仅引用自己的 arg 调用 window.motrix.invoke——与 renderer transport 使用同一个调用面。
Server 链路(standalone driver)
一次性准备:在相邻 worktree 中构建 node-ABI server
这样可以让主 checkout 保持 Electron ABI,而 worktree 持有 Node ABI——这是应对「Electron 与 Node 需要相反 better-sqlite3 ABI」这一约束的标准做法。
git worktree add --detach ../motrix-turbo-srv HEAD
cd ../motrix-turbo-srv
MOTRIX_SKIP_ELECTRON_REBUILD=1 pnpm --config.dangerouslyAllowAllBuilds=true install
pnpm build:server # dist/server + dist/renderer-web
cd -
--config.dangerouslyAllowAllBuilds=true 会允许 install 运行 better-sqlite3 自己的 Node build script(→ Node ABI),同时满足 pnpm 11 的 deps-check,避免 build:server 静默重新 install 并把 ABI 切回去。MOTRIX_SKIP_ELECTRON_REBUILD=1 会跳过 postinstall 中的 Electron rebuild。根目录 package.json 中 start:server = MOTRIX_SKIP_ELECTRON_REBUILD=1 node dist/server/index.mjs 也是同一套心智:跑 server 前先跳过 Electron rebuild 以保住 Node ABI。
运行(可反复执行)
node e2e/bridge/server-leg.mjs
退出码:0 = 全部检查通过,1 = 某项检查失败,2 = server build 缺失(需要重新构建 worktree)。
driver 内部验收清单(源码层展开)
server-leg.mjs 是一个按编号分段的验收 driver,实测它 spawn 真实 dist/server/index.mjs(headless、带 MDXP bridge + Spec 9 operator-auth gate),随后用真实 motrix CLI 打它:
- dual-listener topology:web control plane 的
/healthz返回{ok:true};bridge 在MOTRIX_MDXP_PORT端口写出endpoint.json(含localToken),并应答GET /nonce(200)。 - Spec 9 deny-by-default:匿名
POST /rpc/query/bridge:listPendingPairRequests与匿名POST /api/tasks/pause-all都必须 401。 - Operator authentication:
POST /rpc/auth/login(body{token})返回 200 且Set-Cookie含mtx_op=;随后用Authorization: Bearer op-<hex>访问/rpc/query放行并返回数组。 - Engine readiness:容忍式地等待
query:getEngineStatus达到state === 'ready'。 - INVOCATION:
watch --json与add一个 2 MiB / 256 KiB/s 限速 fixture,轮询至completed,校验磁盘字节数,SIGINT结束 watch 并断言 SSE 事件流出现$/task/progress与终态事件。 - PAIRING(operator-gated approval):spawn
motrix pair --name srv-cli,在 operator inbox 看到 token-free 的请求后,先探测匿名bridge:resolvePair必须 401(self-approval bypass 已关闭),再由 operator 用 Bearer 批准,CLI 退出 0,bridge:listPaired收录该 client。 - Adversarial:错误 bridge token → CLI 退出码 4。
driver 内部自带一个 check(name, cond, detail) 计数器和失败时的 server log tail 兜底输出,方便 CI 排障。
远程浏览器 Extension 链路(Chromium + Firefox)
该门禁会把真实 Server MBP1 runtime 放在本地 HTTPS/WSS 反向代理之后,并在两个浏览器中驱动生产 Extension 构建。测试分别经过 /bridge 代理前缀和代理根路径,覆盖 fresh pair、按 authority 隔离的 consent、敏感 header/Cookie 剥离、浏览器重启重连、Server 重启重连、durable revoke 和 re-pair。独立 Chromium 场景还会在同一持久 profile 中保留两个 Server 的配对,证明 consent 不会跨 Server 泄漏,且提交始终跟随当前选中的 authority。Firefox 还必须断言无 NM ticket 的远程身份保持为 unverified。
先在 Extension checkout 构建两个产物,再从 Motrix 运行:
pnpm --filter @motrix/extension build:chromium
pnpm --filter @motrix/extension build:firefox
MOTRIX_EXTENSION_BUILD=/absolute/path/to/motrix-extension/packages/ext/dist/chromium \
MOTRIX_FIREFOX_EXTENSION_BUILD=/absolute/path/to/motrix-extension/packages/ext/dist/firefox \
pnpm test:e2e:remote-extension
Playwright 启动前,该命令会先验证 e2e/bridge/remote-extension-threat-evidence.json:T01–T29 每项威胁都必须继续绑定至少一个双仓测试文件和测试标题。Extension checkout 默认从 MOTRIX_EXTENSION_BUILD 推导;只有构建产物不在 checkout 内时才需要显式设置 MOTRIX_EXTENSION_REPO。删除或重命名测试、缺少威胁 ID、不安全路径或符号链接证据都会使门禁失败。
可用 MOTRIX_CHROMIUM_EXECUTABLE 与 MOTRIX_FIREFOX_EXECUTABLE 覆盖浏览器路径。Firefox runner 使用 WebDriver BiDi 标准临时扩展安装命令。证书绕过仅存在于本地测试 profile;独立 WSS integration suite 会在不绕过验证的情况下证明受信 CA 成功,以及 unknown CA、过期证书和 hostname mismatch 必须失败。Chromium 应使用与 Playwright 匹配的 Chrome-for-Testing/Chromium 构建。部分正式版 Google Chrome 会忽略自动加载 unpacked Extension 的参数,最终超时等待 service worker;即便显式设置可执行文件,也必须指向支持该测试模式的浏览器构建。
测试运行时与浏览器启动参数(源码层展开)
远程链路由 e2e/bridge/remote-extension.playwright.config.ts 约束:testDir: '.' 且 testMatch 只匹配 remote-extension(?:-firefox)?-wss\.spec\.ts$,workers: 1、retries: 0,整体 timeout: 120_000。从 e2e/bridge/remote-extension-wss.spec.ts 可以看到 Chromium 以 launchPersistentContext 启动,携带 --disable-extensions-except / --load-extension、用 --host-resolver-rules=MAP motrix.test 127.0.0.1 把伪域名解析回本机,并通过 --ignore-certificate-errors-spki-list=<sha256 spki pin> 仅对测试自签证书放行;harness 侧(e2e/bridge/remote-extension-harness.ts)则通过 bootstrapBridgeForServer 把真实 bridge 暴露在 wss://motrix.test:<port>/bridge 与代理根路径之后。
固定双仓兼容版本
浏览器 harness 是跨仓协议契约。两端实现改动提交之前,不得把当前 working tree 的旧 HEAD 写成兼容证据。先分别创建 Extension 与 Motrix 实现提交,再把 e2e/bridge/remote-extension-compatibility.example.json 复制为 remote-extension-compatibility.json,并将两个占位符替换为对应实现提交的完整 40 位小写 SHA(manifest 的 extension.commit 与 motrix.commit 字段,schema 中还声明了 protocol: "MDXP-over-MBP1"、browserCases: 5 与执行命令)。然后在 Motrix 仓库执行:
pnpm check:remote-extension-compatibility \
--manifest e2e/bridge/remote-extension-compatibility.json \
--motrix-repo . \
--extension-repo /absolute/path/to/motrix-extension
验证器会拒绝占位符、短 SHA、大写 SHA、协议漂移、少于五个浏览器场景、来自错误仓库的提交,以及不是对应 checkout 当前 HEAD 祖先的提交。验证通过后,用一个更晚的 Motrix 提交记录该清单;这样既避免 Motrix SHA 自引用,也能让审阅者精确复现兼容组合。
Beta soak 与发布门禁
Soak runner 会重复执行同一套带威胁前置门禁的五场景测试;任意一次失败都会令命令失败。默认执行 20 轮、共 100 个浏览器场景,并限制最多 100 轮,避免环境配置错误产生无界任务:
MOTRIX_CHROMIUM_EXECUTABLE=/path/to/chrome-for-testing \
MOTRIX_EXTENSION_BUILD=/absolute/path/to/motrix-extension/packages/ext/dist/chromium \
MOTRIX_FIREFOX_EXTENSION_BUILD=/absolute/path/to/motrix-extension/packages/ext/dist/firefox \
MOTRIX_REMOTE_EXTENSION_SOAK_REPEATS=20 \
pnpm test:e2e:remote-extension:soak
必须归档完整输出,以及 OS、浏览器版本、两端实现 SHA、轮数、起止时间和所有代理/网络故障注入记录。一次普通 E2E 全绿只是回归证据,不能代替 beta soak 门禁。
固定兼容 SHA 清单提交后,发布门禁必须改用自动生成证据的包装命令:
MOTRIX_CHROMIUM_EXECUTABLE=/path/to/chrome-for-testing \
MOTRIX_FIREFOX_EXECUTABLE=/path/to/firefox \
MOTRIX_EXTENSION_REPO=/absolute/path/to/motrix-extension \
MOTRIX_EXTENSION_BUILD=/absolute/path/to/motrix-extension/packages/ext/dist/chromium \
MOTRIX_FIREFOX_EXTENSION_BUILD=/absolute/path/to/motrix-extension/packages/ext/dist/firefox \
MOTRIX_REMOTE_EXTENSION_SOAK_EVIDENCE_DIR=/absolute/archive/remote-extension-soak \
MOTRIX_REMOTE_EXTENSION_SOAK_FAULTS=none \
pnpm test:e2e:remote-extension:release-soak
发布模式强制恰好 20 轮、两个仓库均干净、Extension HEAD 与固定 SHA 完全一致,且 Motrix 实现 SHA 之后只能新增兼容清单。源码预检通过后,它会从固定 Extension checkout 重新构建两个浏览器版本,拒绝仓库外或符号链接构建目录,并计算新产物哈希。它会记录显式浏览器版本与 OS 信息,生成 evidence.json 和完整的 Playwright playwright-report.json;报告必须能解析出恰好 100 个通过场景,且不得包含顶层或场景错误。浏览器失败会归档为 failed;即使进程返回 0,只要 JSON 报告缺失或无效也会归档为 incomplete 并使门禁失败。证据目录必须是新目录,后续运行不能覆盖先前记录。
Server 链路维护
环境变量覆盖(全部可选)
| 变量 | 默认值 | 含义 |
|---|---|---|
MOTRIX_SERVER_DIR |
../motrix-turbo-srv |
node-ABI build 目录 |
MOTRIX_E2E_WEB_PORT |
8090 |
web / operator control-plane 端口 |
MOTRIX_E2E_MDXP_PORT |
16801 |
MDXP bridge 端口 |
MOTRIX_ARIA2_BIN |
bundled | aria2c binary |
这些覆盖在 server-leg.mjs 中都有同名的默认值兜底实现:MOTRIX_ARIA2_BIN 缺省时解析到主 checkout 的 extra/<platform>/<arch>/aria2c;Server 进程还会把 PORT、MOTRIX_MDXP_HOST/PORT、MOTRIX_DATA_DIR、MOTRIX_OPERATOR_TOKEN、MOTRIX_PUBLIC_URL、MOTRIX_RENDERER_DIR 注入运行环境——其中 operator token 会在运行时用 crypto.randomBytes 现生成,保证每次运行的匿名 401 探针都是真实鉴权结果。
拉取新代码后刷新 worktree
cd ../motrix-turbo-srv
git fetch && git checkout --detach origin/main # 或被测 branch
pnpm build:server # 如果 deps 变化,重新运行上面的 install
cd -
清理
git worktree remove --force ../motrix-turbo-srv
注意事项(设计边界)
- CLI auto-discovery 在 darwin 上是 hardcoded:
~/Library/Application Support/Motrix/bridge/endpoint.json。它只能找到 Electron bridge,永远不会找到 server。因此 Server 链路始终显式传入--endpoint http://127.0.0.1:<mdxp-port> --token <localToken>。 - Pairing approval 是刻意保留的人工步骤(没有 headless auto-approve)。测试驱动的是真实 approval surface:Electron 上的
bridge:resolvePairIPC,以及 Server 上由 operator gate 保护的POST /rpc/command/bridge:resolvePair。这正是上面 Electron 与 Server 两条链路分别用window.motrix.invoke与 operator Bearer 去批准配对的根本原因——批准面就是产品实际暴露给用户的那个面。 - e2e/bridge/remote-extension-wss.spec.ts 与
remote-extension-firefox-wss.spec.ts是当前启用的 browser-extension WSS 生命周期门禁。旧的pair-and-submit、receiver-direct、revoke仍是窄范围 placeholder,不能作为覆盖证据。
综合来看,这套 e2e/bridge 体系的价值在于:它把「CLI 能否与宿主配对并驱动真实下载」这一最容易在跨仓、跨 ABI、跨浏览器场景中回归的承诺,固化成三条相互补充的本地可复现门禁——Electron 链路守卫桌面 app 的真实 IPC 批准面,Server 链路守卫 operator 鉴权与 deny-by-default,远程浏览器链路则守卫双仓协议契约与威胁模型(T01–T29)的持续绑定,任何一侧的实现漂移都会在合并前被拦截。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00