🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

请求急救员 LuoLingP/request-doctor

DeepSeek Harness
LuoLingP6 天前有提交已提供中文文档扫描:含敏感能力

自动重试可挽回的请求失败,并为旧插件搭建兼容环境

32.2/100
综合分
3
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/29 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add LuoLingP/request-doctor
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · other
装得上吗
本站已在 dsh 0.2.1-rc.2 下真实安装成功
非静态推断
L5 · 运行时冒烟等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 6 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件发起外部网络请求
查看扫描证据(共 72 处,展示前 61 条)
  • 发起网络请求
    client.js:57
    const response = await fetch(path, init)
  • 读取本地文件
    lib/compat-repair.js:23
    return readFileSync(path, 'utf8');
  • 读取本地文件
    lib/install-support.js:29
    return existsSync(join(absolute, 'package.json')) ? { kind: 'local', dir: absolute } : { kind: 'remote' };
  • 读取本地文件
    lib/install-support.js:118
    if (existsSync(join(dir, STAGING_DIR))) rmSync(join(dir, STAGING_DIR), { recursive: true, force: true });
  • 删除本地文件递归
    lib/install-support.js:118
    if (existsSync(join(dir, STAGING_DIR))) rmSync(join(dir, STAGING_DIR), { recursive: true, force: true });
  • 写入本地文件
    lib/install-support.js:139
    writeFileSync(
  • 写入本地文件
    lib/install-support.js:166
    writeFileSync(
  • 删除本地文件
    lib/install-support.js:181
    rmSync(join(dir, STAGING_DIR), { force: true });
  • 读取本地文件
    lib/install-support.js:190
    if (!existsSync(staging)) return false;
  • 删除本地文件
    lib/install-support.js:195
    rmSync(staging, { force: true });
  • 读取本地文件
    lib/install-support.js:205
    return existsSync(join(dir, STAGING_DIR));
  • 删除本地文件递归
    lib/install-bundle.js:62
    rmSync(target, { recursive: true, force: true });
  • 删除本地文件递归
    lib/install-bundle.js:69
    rmSync(target, { recursive: true, force: true });
  • 删除本地文件递归
    lib/install-bundle.js:103
    rmSync(target, { recursive: true, force: true });
  • 读取本地文件
    lib/install-bundle.js:166
    if (existsSync(join(destination, 'package.json'))) continue;
  • 写入本地文件
    lib/install-bundle.js:194
    writeFileSync(path, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
  • 读取本地文件
    lib/install-rescue.js:100
    return readFileSync(join(profileDir, 'node_modules', ...name.split('/'), 'package.json'), 'utf8') !== '';
  • 读取本地文件
    lib/install-rescue.js:123
    manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
  • 写入本地文件
    lib/diagnostics.js:37
    writeFileSync(input.statusFile, `${JSON.stringify(snapshot(), null, 2)}\n`, 'utf8');
  • 读取本地文件
    lib/runtime-probe.js:18
    if (existsSync(join(candidate, 'package.json'))) return candidate;
  • 读取本地文件
    lib/runtime-probe.js:111
    if (candidate.includes('app.asar') || existsSync(join(candidate, 'package.json'))) return candidate;
  • 读取本地文件
    lib/runtime-probe.js:145
    if (candidate.includes('app.asar') || existsSync(candidate)) return candidate;
  • 读取本地文件
    lib/version-repair.js:263
    manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
  • 读取本地文件
    lib/version-repair.js:281
    const manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
  • 读取本地文件
    lib/compat-env.js:32
    const value = JSON.parse(readFileSync(path, 'utf8'));
  • 读取本地文件
    lib/compat-env.js:50
    if (typeof fromRuntime === 'string' && (fromRuntime.includes('app.asar') || existsSync(join(fromRuntime, 'package.json')))) {
  • 读取本地文件
    lib/compat-env.js:54
    return existsSync(join(candidate, 'package.json')) ? candidate : undefined;
  • 写入本地文件
    lib/compat-env.js:68
    mkdirSync(input.shimDir, { recursive: true });
  • 读取本地文件
    lib/compat-env.js:71
    const realSource = readFileSync(input.realEntry, 'utf8');
  • 读取本地文件
    lib/compat-env.js:76
    const hasDefault = parseExportedNames(input.realEntry, (file) => readFileSync(file, 'utf8'))?.includes('default')
  • 读取本地文件
    lib/compat-env.js:111
    if (readFileSync(path, 'utf8') === content) return false;
  • 写入本地文件
    lib/compat-env.js:115
    writeFileSync(path, content, 'utf8');
  • 删除本地文件递归
    lib/compat-env.js:159
    rmSync(shimDir, { recursive: true, force: true });
  • 删除本地文件递归
    lib/compat-env.js:184
    rmSync(destination, { recursive: true, force: true });
  • 写入本地文件
    lib/compat-env.js:193
    writeFileSync(
  • 删除本地文件递归
    lib/compat-env.js:324
    rmSync(join(root, ...stale.split('/')), { recursive: true, force: true });
  • 删除本地文件递归
    lib/compat-env.js:341
    rmSync(join(root, ...name.split('/')), { recursive: true, force: true });
  • 删除本地文件
    lib/compat-env.js:350
    rmSync(marker, { force: true });
  • 删除本地文件
    lib/compat-env.js:363
    if (readdirSync(dir).length === 0) rmdirSync(dir);
  • 写入本地文件
    lib/compat-env.js:451
    if (!existsSync(backupPath)) copyFileSync(manifestPath, backupPath);
  • 写入本地文件
    lib/compat-env.js:462
    writeFileSync(temporary, content, 'utf8');
  • 写入本地文件
    lib/compat-env.js:463
    renameSync(temporary, path);
  • 发起网络请求
    lib/package-fetch.js:23
    const response = await fetch(`${REGISTRY}/${encodedName(name)}`, { signal: AbortSignal.timeout(TIMEOUT_MS) });
  • 写入本地文件
    lib/package-fetch.js:139
    mkdirSync(dirname(target), { recursive: true });
  • 写入本地文件
    lib/package-fetch.js:140
    writeFileSync(target, data);
  • 发起网络请求
    lib/package-fetch.js:171
    const response = await fetch(url, { signal: AbortSignal.timeout(TIMEOUT_MS) });
  • 写入本地文件
    lib/package-fetch.js:175
    mkdirSync(destDir, { recursive: true });
  • 读取本地文件
    lib/stat-cache.js:23
    const stat = statSync(path);
  • 读取本地文件
    lib/stat-cache.js:45
    const manifest = JSON.parse(readFileSync(path, 'utf8'));
  • 读取本地文件
    lib/env-verify.js:20
    const stat = statSync(path);
  • 读取本地文件
    lib/env-verify.js:43
    return readFileSync(path, 'utf8');
  • 读取本地文件
    lib/env-verify.js:90
    const shimNames = parseExportedNames(shimPath, (source) => readFileSync(source, 'utf8')) ?? [];
  • 读取本地文件
    lib/env-verify.js:99
    const names = parseExportedNames(path, (file) => readFileSync(file, 'utf8'));
  • 写入本地文件
    lib/cache-store.js:12
    utimesSync(dir, now, now);
  • 读取本地文件
    lib/cache-store.js:25
    return statSync(dir).mtimeMs;
  • 读取本地文件
    lib/cache-store.js:40
    entries = readdirSync(base, { withFileTypes: true });
  • 读取本地文件
    lib/cache-store.js:56
    inner = readdirSync(path, { withFileTypes: true });
  • 删除本地文件递归
    lib/cache-store.js:70
    rmSync(item.path, { recursive: true, force: true });
  • 读取本地文件
    lib/plugin-discovery.js:97
    if (!existsSync(join(nested, ...name.split('/'), 'package.json'))) return false;
  • 读取本地文件
    lib/plugin-discovery.js:107
    entries = readdirSync(root, { withFileTypes: true });
  • 读取本地文件
    lib/plugin-discovery.js:118
    scoped = readdirSync(join(root, entry.name), { withFileTypes: true });
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

一个 DSH 插件:自动修好客户端这边造成的请求失败,并为老版本插件提供兼容

README

由 DeepSeek 最新模型翻译生成
dsh-request-doctor · 请求急救员 DSH 插件:自动修好客户端这边造成的请求失败,并让版本对不上的插件能装上、能跑。 它解决什么问题 请求莫名失败 网络抖一下、服务端超时、模型返回空、上下文太长——这些都不是你操作的问题,但默认会让一次对话直接断掉。 插件判断这属于「本机这边能挽回」的失败后自动重试,间隔逐次拉长,不会把对方打得更糟。 不重试:欠费、限流、服务端故障、内容审核拦截、图片超限。这些重试没有意义。 不重放:会改动东西的操作(执行命令、写文件、发消息)。只重放只读的(读文件、搜索、抓网页、看页面)。 版本对不上的插件 DSH 升级后,旧插件可能只认 0.2.1-rc.2,而你现在是 0.2.1-rc.3。默认结果是插件用不了,甚至装不上。 插件不会去关掉这个检查,而是给那个插件搭一个它期望的旧环境,跟 Windows 的「以兼容模式运行」是一个意思——不是忽略问题,是提供它需要的环境。 分两种情况: 当前实现还够用:在插件目录里放一层很薄的转发包,声明旧版本号,调用转到当前实现。不复制代码,开销极小。 当前实现缺了插件要的东西:把插件当初对着写的那份真实旧版本从仓库取回来装进去。跑的是它认识的那份代码,不是一个缺胳膊少腿的替身。
展开完整文档(README 剩余 1 节)
dsh-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/name@1.2.3 这类):管理器的安装路径是「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/dsh-request-doctor/
2. 在 <profile>/package.json 的 dependencies 里加上 "dsh-request-doctor": "link:<本目录>"
3. 在 <profile>/cordis.patch.yml 里插入:

- insert:
- id: request-doctor
name: 'dsh-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 | 见下 | 允许重放的工具名正则 |
| 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
预览分享卡片长什么样 ↓LuoLingP/request-doctor
分享 · DeepSeek Harness Hub
LuoLingP/request-doctor

自动重试可挽回的请求失败,并为旧插件搭建兼容环境

这是你的仓库?认领条目

回到顶部