Skip to content

request-doctor

Verified

request-doctor · v0.18.3 · MIT · Web UI

自动修复客户端引起的请求失败,并让版本不匹配的 DSH 插件能装上、能跑起来。

Install

dsh plugin add request-doctor

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Readme

request-doctor · 请求急救员

一个 DSH 插件:自动修好客户端这边造成的请求失败,并让版本对不上的插件能装上、能跑。

它解决什么问题

请求莫名失败

网络抖一下、服务端超时、模型返回空、上下文太长——这些都不是你操作的问题,但默认会让一次对话直接断掉。

插件判断这属于「本机这边能挽回」的失败后自动重试,间隔逐次拉长,不会把对方打得更糟。

不重试:欠费、限流、服务端故障、内容审核拦截、图片超限。这些重试没有意义。

不重放:会改动东西的操作(执行命令、写文件、发消息)。只重放只读的(读文件、搜索、抓网页、看页面)。

版本对不上的插件

DSH 升级后,旧插件可能只认 0.2.1-rc.2,而你现在是 0.2.1-rc.3。默认结果是插件用不了,甚至装不上。

插件不会去关掉这个检查,而是给那个插件搭一个它期望的旧环境,跟 Windows 的「以兼容模式运行」是一个意思——不是忽略问题,是提供它需要的环境。

分两种情况:

当前实现还够用:在插件目录里放一层很薄的转发包,声明旧版本号,调用转到当前实现。不复制代码,开销极小。

当前实现缺了插件要的东西:把插件当初对着写的那份真实旧版本从仓库取回来装进去。跑的是它认识的那份代码,不是一个缺胳膊少腿的替身。

取回的版本缓存在 profile 下的 .request-doctor-cache/,同一个版本只下载一次。

旧版本里也没有:如实报告不兼容。不伪造、不放占位——放一个「调用时才报错」的假货,只是把崩溃推迟到运行期,那是骗自己。

装不上的插件

分两种情况,命令只有一个:

/doctor install <插件目录,或者包名>

本地目录:先把环境备好,再正常安装,管理器的版本检查就会按它自己的规则通过。

注册表里的包(dsh-ssh-ops、@scope/[email protected] 这类):管理器的安装路径是「pnpm 装完再检查」。远程包的字节在检查之前就落地了,没法提前改好 manifest,所以它对不兼容的包永远装不上。

这种情况由插件自己把 pnpm 该做的事做一遍:取包、取依赖、对齐声明、建兼容环境、登记为 bundle、重载。装完会打印位置、环境状态、依赖和重载结果。

覆盖范围

扫描的是所有不兼容的插件,不是一个。来源包括:Loader 拒绝的包、已启用但没被拒绝的、已禁用但装着的、安装时环境还欠着的,以及加载失败后不在插件清单里、只能靠扫目录发现的。

建完会核对,不是建完就算

环境建好之后,会逐条确认插件要的每个符号是否真能拿到,再决定算不算修好。

这一条是必要的:垫片少写一个导出,插件在加载那一刻就会崩,而修复流程会以为一切正常。核对通不过就记为未修复,不谎报成功。

检查脚本也接上了核对:垫片存在但内容不对的插件会被重新适配,不会当作已经修好放着。

路由冲突只报告

两个插件可能声明同一个 LLM provider id。插件会提示你,但不会替你停用任何一个——那是你的插件,由你决定。

运行开销

稳态下几乎不耗资源。

  • 源码和 manifest 按文件签名缓存,没变就不重解析
  • 后台扫描健康时逐步退避到 30 分钟一次
  • 内容没变就不写盘,不触发文件监听
  • 长会话里那些记账用的结构都有上限
  • 定时器全部 unref,不拖住进程退出

安装

仓库根目录就是包本身:没有构建步骤,没有运行时依赖,npm pack 出来 33 个文件,装进去就能用。

从可视化界面安装

打开「插件」页,选安装,填本仓库地址或本地目录。装完立即生效,不用重启。

仓库里带 dsh.bundle.patch,DSH 会据此把插件挂进组合;版本检查对得上,不会出现在「不兼容」列表里。

用命令安装

/doctor install <仓库本地路径或包名>

本地目录和注册表里的包都走这一条。

手动放进 profile

  1. 把本仓库内容复制到 <profile>/node_modules/request-doctor/
  2. 在 <profile>/package.json 的 dependencies 里加上 "request-doctor": "link:<本目录>"
  3. 在 <profile>/cordis.patch.yml 里插入:
- insert:
    - id: request-doctor
      name: 'request-doctor'
  1. 重启 DSH

装完自检

/doctor

会打印已修复的数量、最近几次修复的记录,以及最近一轮版本审计的结论。任何一项为 0 都正常——没坏东西可修的时候它不动作。

配置

都放在 cordis.patch.yml 的 config 下,全部可选。

配置项 类型 默认 说明
autoRepair boolean true 总开关
autoVersionRepair boolean true 是否自动处理版本不匹配
maxRetries number 12 单步模型请求最多修几次
maxToolRetries number 2 单次工具调用最多重放几次
maxVersionRepairs number 64 单轮扫描最多适配几个插件,0 表示不限
maxAuthRetries number 2 鉴权类失败最多重试几次,额度单独且更小
maxRepeatedToolCalls number 3 同名同参调用超过几次后不再重放
initialDelayMs number 400 首次退避基准
maxDelayMs number 20000 退避上限
jitterRatio number 0.25 退避抖动比例
retryableToolPattern string 见下 允许重放的工具名正则
statusFile string profile 下的 .request-doctor-status.json 诊断快照路径,空串表示不写盘
recordRetryEvents boolean true 是否写入会话重试记录

默认只读工具白名单:

^(read|read_image|glob|grep|web_fetch|web_search|cordis_inspect_.*|browser_(list_tabs|snapshot|get_text|wait|scroll)|db_(query|preview|describe_table|list_tables))$

使用

/doctor

看各类修复计数和最近发生的事。

/doctor install <目录路径或包名>

本地插件先备好环境再让你正常安装;注册表里的包则直接装上。已经装着的包会就地修复,不会重装一遍。

/doctor allow <包名@版本>

只授权、不安装。给那些想继续在插件面板里点安装的人用:先放行这个版本,再在界面上装,装完声明对齐和兼容环境会自动补上。

插件面板里的「不兼容安装」

打开「插件」,标签栏里会多一个「不兼容安装」。里面列出当前被 DSH 版本检查拒掉的插件,每行一个按钮;顶上还有一个输入框,可以直接填 包名@版本。

点按钮做的事和 /doctor install 一样:先按管理器的规则授权这个版本,再让它自己装,装完声明对齐和兼容环境由后台扫描补上。点完不会立刻变绿,等几秒。

界面是随插件一起加的客户端模块。新装的插件要先刷新一次页面,浏览器才会加载它——插件热替换只覆盖已经载入过的客户端模块。

页面渲染出错时不会留一片空白,而是把那句错显示出来。看到「这个页面渲染出错」就是那条信息本身在告诉你原因。

还原

被适配过的插件目录里有 package.json.request-doctor-original,是改动前的备份。

要恢复原状:删掉插件目录下的 node_modules(如果只有本插件为它建过),用备份覆盖 package.json,再删掉 .request-doctor-compat.json。

兼容环境只包含标记里记下的那些包,删除时也只删那些——插件自带的依赖不会被误删。

已知边界

  • 兼容环境只能保证「插件要用的东西还在」。插件内部若有别的假设已经变了,仍可能表现异常,需要作者更新。
  • 第三方依赖不在检查范围,那是插件自己的事。
  • 安装前预置只支持本地目录。注册表包的字节还没落盘,提前看一眼是不可能的,这种情况会明确告诉你。

License

MIT

维护者检查

仓库里自带三个脚本,改完东西依次跑:

npm test              # 单元与集成测试(仅仓库)
npm run check:quality # 代码质量门禁(复杂度、重复率、注释密度、文件长度)
npm run check:docs    # 文档与实现是否还对得上

check:docs 会核对:需求条目是否都有覆盖行、报告引用的测试是否存在、设计说明的模块清单是否与 lib/ 一致、六份文档版本号、质量报告数字、路由与命令是否写进文档。它随包发布,也可以在解压后的包里直接跑。