npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@motrix/mdxp

v0.4.0

Published

Motrix Download eXchange Protocol — JSON-RPC 2.0 wire types and connection helpers

Readme

@motrix/mdxp

npm version license types

English | 简体中文

MDXP(Motrix Download eXchange Protocol)—— 一套 JSON-RPC 2.0 wire 类型、 Zod schema 与双向连接封装,让浏览器、CLI 或 AI agent 通过任意双工 transport, 把下载任务移交给 Motrix 桌面下载器。

@motrix/mdxp 是 MDXP wire 契约的唯一真源。bridge 的两端 —— 作为 serverMotrix 桌面端(持有下载引擎),与各类 client (浏览器扩展、CLI、agent)—— 都依赖本包,从而让协议形态只需定义一次。

本包不含任何 transport 相关实现:你提供一对 vscode-jsonrpcMessageReader/MessageWriter(stdio、socket、WebSocket、MessagePort 皆可), 本库便在其上构建出一个完全类型化的双向连接。

特性

  • Schema-first:每个 wire 形态都是一个 Zod schema, TypeScript 类型再由 schema z.infer 推导而来 —— 校验与类型因此永远不会脱节。
  • 完全类型化的连接sendRequest/onRequest/sendNotification/ onNotification 均以 method 名为泛型参数,params 与 result 类型据此自动推断, 调用处无需任何 cast。
  • transport-agnostic:只要实现了 MessageReader/MessageWriter,任何双工流都能用。
  • 平台 RAL 入口./node./browser 会替你装好对应的 vscode-jsonrpc runtime abstraction layer,并 re-export 它的 transport class —— 连接封装与 reader/writer 都从同一处 import。
  • 一步构造fromWebSocketfromStdiofromWorker 一行即可在常见 transport 上建好连接;createMdxpConnection 仍是通用的底层入口。
  • 面向 agent:内置的 tool registry 可产出一份 JSON-Schema tool catalog, 能直接对接 LLM 的 function-calling API。
  • forward-compatible:遇到未知的 method 或字段选择忽略,而非 reject。

安装

npm install @motrix/mdxp
# 或:pnpm add @motrix/mdxp · yarn add @motrix/mdxp

运行时依赖:vscode-jsonrpc ^9,随本包一并装上。它的 transport class(reader/writer),以及在本包 API 中出现的 那些 primitive —— MessageReaderMessageConnectionCancellationTokenCancellationTokenSource 等 —— 都已从 @motrix/mdxp re-export(见 入口点),因此你几乎无需直接 import vscode-jsonrpc。仅 ESM;需要 Node.js ≥ 18 或现代 bundler。

入口点

| import | 是否装 RAL | 使用场景 | | --- | --- | --- | | @motrix/mdxp | 否 —— 平台无关的核心 | 共享代码、测试、仅类型 import | | @motrix/mdxp/node | Node RAL | Node host(Electron main、CLI、native-messaging host) | | @motrix/mdxp/browser | Browser RAL | 浏览器 host(扩展 service worker、页面) |

vscode-jsonrpc v9 要求先装好一层 runtime abstraction layer(RAL),才能创建连接。 import @motrix/mdxp/node@motrix/mdxp/browser 会把对应的 RAL 装进本包所用的 同一个 vscode-jsonrpc 实例,并 re-export 完整的公开 API,外加该平台的 transport class(Node 的 StreamMessageReader/Writer、浏览器的 BrowserMessageReader/Writer)—— 于是 host 连同 reader/writer 都只需从一个入口 import。

快速开始

Node host(走 stdio)

import { fromStdio } from '@motrix/mdxp/node'

// 走 process.stdin / process.stdout(native-messaging / CLI 场景)。
const conn = fromStdio()

// 务必在 listen() 之前注册 handler。
conn.onNotification('$/task/progress', (p) => {
  const pct = p.bytesTotal ? Math.round((p.bytesDone / p.bytesTotal) * 100) : null
  console.log(`[${p.taskId}] ${p.phase} ${pct ?? '?'}% @ ${p.speedBps} B/s`)
})

conn.listen()

浏览器 host(走 WebSocket)

import { fromWebSocket } from '@motrix/mdxp/browser'

const conn = fromWebSocket(new WebSocket('ws://127.0.0.1:16650/v1'))
conn.onNotification('$/task/progress', (p) => {})
conn.listen()

便捷构造器

createMdxpConnection(reader, writer) 是通用入口 —— 传入任意 vscode-jsonrpc 的 reader/writer。常见 transport 则可直接省去样板:

| 构造器 | 入口 | Transport | | --- | --- | --- | | fromWebSocket(ws) | ./node · ./browser | 浏览器 WebSocket 或 Node 的 ws socket | | fromStdio(opts?) | ./node | process.stdin / process.stdout,或指定的流 | | fromWorker(port) | ./browser | 一个 WorkerMessagePort |

每个都返回开箱即用的 MdxpConnection —— 你仍需注册 handler 并调用 listen()。 其它 transport 则自行构造 reader/writer,再直接调用 createMdxpConnection

核心概念

server 与 client:Motrix 桌面端是 server —— 下载引擎由它持有。client 则是驱动它的一方:浏览器扩展、CLI 或 agent。连接本身是对称的,但每个 method 都有 既定的调用方向(见下)。

握手先行motrix/initialize 必须是每个 session 的第一条消息 —— 它负责协商 protocol version、交换身份、声明 capabilities。在它 resolve 之前,不应发送任何其它消息。

消息方向:绝大多数 method 是 client→server(由 client 请求下载器做事)。只有两个是 server→client —— server 请求 client 去检视某个页面:url/probeurl/resolve (见 SERVER_INITIATED_METHODS)。既然这两个都由 server 发起,client 一侧就用 onRequest 应答,server 一侧则用 sendRequest 调用。

用法

握手

const hello = await conn.sendRequest('motrix/initialize', {
  protocolVersion: '1.0',
  client: {
    kind: 'cli',              // 或 'extension'
    name: 'my-download-agent',
    version: '1.0.0',
    locale: 'zh-CN',
  },
  capabilities: { submitDownload: true, progress: true, cancellation: true },
  adapters: [],              // 该 client 能解析的页面 adapter(如有)
})

console.log(hello.server.name, hello.server.version)
console.log(hello.capabilities.selectionKinds) // 例如 ['direct', 'hls', 'mux']

添加下载(client → server)

download/add 是面向 agent 的公开入口,接受直连 URL 列表、magnet 链接或 base64 torrent,并直接返回新建的 task 快照 —— 调用方无需轮询即可渲染。

// 直连 HTTP(S) 文件
const task = await conn.sendRequest('download/add', {
  kind: 'url',
  saveDir: '/Users/me/Downloads',
  uris: ['https://cdn.example.com/releases/app-1.4.2-arm64.dmg'],
  connections: 8,
})
console.log(task.id, task.status) // "t_01H…", "downloading"

// magnet 链接
await conn.sendRequest('download/add', {
  kind: 'magnet',
  saveDir: '/Users/me/Downloads',
  uri: 'magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a',
})

只接受 httphttpsftpftpssftp 协议的 URL —— schema 会在契约边界 直接 reject 掉 file:data:javascript:,因此 agent 绝无可能被诱导去读取 本地文件。

查询与控制 task(client → server)

const { tasks, total } = await conn.sendRequest('task/list', {
  status: 'downloading',
  limit: 20,
})

await conn.sendRequest('task/pause',  { taskId: task.id })
await conn.sendRequest('task/resume', { taskId: task.id })
await conn.sendRequest('task/remove', { taskId: task.id, deleteFiles: false })

解析页面(server → client)

桌面端会先问某个 client 能否处理这个页面(url/probe),再请它抽取出可下载的资源 (url/resolve)。client 通过注册 handler 来应答:

conn.onRequest('url/probe', async ({ url }) => ({
  handled: /videos\.example\.com/.test(url),
  adapterId: 'example-video',
  confidence: 'high',
}))

conn.onRequest('url/resolve', async ({ url, preferences }) => ({
  selections: [
    {
      kind: 'direct',
      primary: {
        url: 'https://cdn.example.com/v/abc123/1080p.mp4',
        headers: {},
        cookies: [],
        refererPolicy: 'strict-origin-when-cross-origin',
      },
      container: 'mp4',
      quality: preferences?.maxQuality ?? '1080p',
      sizeBytes: 734_003_200,
    },
  ],
  meta: { title: 'Sample clip', author: 'example.com', durationSec: 372 },
  extractedBy: {
    adapterId: 'example-video',
    adapterVersion: '1.0.0',
    extractedAt: Date.now(),
  },
}))

一个 selection 是按 kind 区分的 discriminated union:direct(单个文件)、 hls(一份 playlist),或 mux(分离的 video 与 audio 流,由 server 端合流)。 每个 Resource 都带有 server 端重新抓取时所需的 headers/cookies

进度与生命周期(server → client)

conn.onNotification('$/task/progress', (p) => {
  // p.phase: 'queued' | 'downloading' | 'muxing' | 'finalizing'
})
conn.onNotification('$/task/completed', (p) => {
  console.log('完成 →', p.filePath, `(${p.durationMs} ms)`)
})
conn.onNotification('$/task/error', (p) => {
  console.error(`task ${p.taskId} 失败:[${p.code}] ${p.message}`)
})

取消

sendRequest 可接受一个可选的 CancellationToken。取消时会在 wire 上发出 $/cancelRequest(由 vscode-jsonrpc 处理);采用协作式取消的 handler 只需观察 token.isCancellationRequested 即可响应。

import { CancellationTokenSource } from '@motrix/mdxp'

const cts = new CancellationTokenSource()
const pending = conn.sendRequest('url/resolve', { url }, cts.token)
// …用户离开了页面:
cts.cancel()

运行时校验

每个 wire 形态都配有 schema。在边界处先用 safeParse 校验不可信输入,通过后再执行:

import { DownloadAddParamsSchema } from '@motrix/mdxp'

const parsed = DownloadAddParamsSchema.safeParse(untrusted)
if (!parsed.success) {
  // parsed.error —— 一个精确指出问题所在的 ZodError
  return
}
await conn.sendRequest('download/add', parsed.data)

Error 模型

在 handler 里用 makeMdxpError 返回结构化的错误。code 是 JSON-RPC error code; data 则携带机器可读的 appCode、重试提示,以及任意自定义上下文。

import { ErrorCodes, makeMdxpError } from '@motrix/mdxp'

throw makeMdxpError(
  ErrorCodes.ResourceUnavailable,
  'The requested file is no longer available',
  { appCode: 'http.gone', retryable: false, context: { status: 410 } },
)

收到 code 后,可用 isProtocolError(code)(JSON-RPC 保留段)或 isMotrixError(code) (Motrix 的 -32001…-32099 段)为它归类。

AI-agent tool catalog

面向 agent 的那些 method 会被导出成一份 JSON-Schema tool catalog,可直接对接 LLM 的 function-calling / tool-use API:

import { toAgentToolCatalog } from '@motrix/mdxp'

const tools = toAgentToolCatalog()
// [
//   { name: 'download/add', description, inputSchema: {…JSON Schema}, outputSchema },
//   { name: 'task/list',    … },
//   …
// ]

API 参考

导出

| 导出 | 类型 | 作用 | | --- | --- | --- | | createMdxpConnection(reader, writer) | function | 把一对 reader/writer 封装成类型化的 MdxpConnection。 | | fromWebSocket · fromStdio · fromWorker | function | 在 WebSocket / stdio / Worker 上的一步构造器(来自 ./node · ./browser)。 | | MdxpConnection | type | 连接接口(sendRequestonRequestsendNotificationonNotificationdisposeraw)。 | | MdxpRequestMap / MdxpNotificationMap | type | method / notification 名 → params/result 类型的映射表。 | | Methods / Notifications | const | wire 名常量(Methods.DownloadAdd === 'download/add')。 | | ErrorCodes | const | JSON-RPC 与 Motrix 自定义的 error code。 | | makeMdxpError(code, msg, data?) | function | 构造结构化的 MdxpError。 | | isProtocolError / isMotrixError | function | 为 error code 归类。 | | Tools | const | 每个 client→server method 的注册表 → { description, paramsSchema, resultSchema, agentFacing }。 | | toAgentToolCatalog() | function | 取 agentFacing 子集,转成 JSON-Schema tools。 | | SERVER_INITIATED_METHODS | const | 由 server 向 client 发起的 method(url/probeurl/resolve)。 | | *Schema | Zod schema | 全部 wire 形态,供运行时校验。 | | MessageReader · MessageWriter · MessageConnection · CancellationToken · CancellationTokenSource · Disposable | re-export | 本包 API 中用到的 vscode-jsonrpc primitive。平台 transport class(StreamMessageReader/WriterBrowserMessageReader/Writer)从 ./node./browser re-export。 |

Methods

| Method | 方向 | agent-facing | 作用 | | --- | --- | :---: | --- | | motrix/initialize | client → server | | 握手:协商 version、身份、capabilities。 | | system/ping | client → server | | 存活探测;回显 sentAtrecvAt。 | | download/submit | client → server | | 提交浏览器侦测到的 page 形态下载。 | | download/cancel | client → server | | 按 task id 取消已提交的下载。 | | download/add | client → server | ✓ | 按 URL / magnet / torrent 添加下载。 | | task/list | client → server | ✓ | 列出 task,可过滤、可分页。 | | task/get | client → server | ✓ | 按 id 取单个 task。 | | task/pause · task/resume | client → server | ✓ | 暂停 / 恢复 task。 | | task/remove | client → server | ✓ | 移除 task,可选一并删除文件。 | | stats/get | client → server | ✓ | 取聚合的全局统计(速度 + 计数)。 | | engine/status | client → server | ✓ | 取下载引擎的生命周期状态与 feature report。 | | url/probe | server → client | | 该 client 的 adapter 能否处理某页面? | | url/resolve | server → client | | 从页面中抽取可下载资源。 |

Notifications

| Notification | 方向 | 载荷 | | --- | --- | --- | | motrix/initialized | client → server | 握手完成(无载荷)。 | | $/task/progress | server → client | bytesDonebytesTotalspeedBpsetaSecphase。 | | $/task/completed | server → client | filePathdurationMs。 | | $/task/error | server → client | codemessage。 | | $/stats | server → client | 周期性推送的聚合统计。 | | $/pair/revoked | server → client | 配对被撤销(reason)。 | | $/cancelRequest | 双向 | 取消 —— 由 vscode-jsonrpc 处理。 |

Error codes

| Code | 值 | 所属段 | | --- | --- | --- | | ParseError | -32700 | JSON-RPC 保留 | | InvalidRequest | -32600 | JSON-RPC 保留 | | MethodNotFound | -32601 | JSON-RPC 保留 | | InvalidParams | -32602 | JSON-RPC 保留 | | InternalError | -32603 | JSON-RPC 保留 | | RequestCancelled | -32800 | LSP 扩展 | | AdapterError | -32001 | Motrix | | ResourceUnavailable | -32002 | Motrix | | PermissionDenied | -32003 | Motrix | | RateLimited | -32004 | Motrix | | CapabilityNotSupported | -32005 | Motrix | | PairRevoked | -32006 | Motrix |

协议说明

  • 版本protocolVersion'1.0'。这是 wire 的兼容性版本,与本包的 npm version 相互独立。
  • 不支持 batching:JSON-RPC batching 被明确禁止 —— 一帧一消息。
  • forward-compatible:result 与 notification 的载荷都是 non-strict 的 —— 较新的 server 可以新增字段,较旧的 client 忽略即可;未知的 method 会以 MethodNotFound 被拒绝,而不会拖垮整个 session。

设计原则

  • transport-agnostic —— 本库不假定任何特定 transport;任何 MessageReader/ MessageWriter 双工流都能承载它。
  • schema-first —— 先定义 Zod schema,再推断类型;绝不手写已有对应 schema 的类型。
  • forward-compatible —— 对未知之物选择忽略,而非 reject。

License

MIT © Dr_rOot