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

启动容错 MYCF711/dsh-escape-hatch

DeepSeek Harness
MYCF71138 天前有提交已提供中文文档扫描:未发现敏感能力

插件启动失败时保留界面,降级为可关闭警告

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

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

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

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:需留意
是什么[目录]—
dsh 原生插件 · ui
暂无时间戳·判据 →
装得上吗[实装验证]!
实装验证未通过(unknown),装前请到仓库确认最近更新与 issue
L4 · 真实安装等级说明 →
数据截至 2026/09/30·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/04·判据 →
还在维护吗[仓库]—
更新放缓:最近一次提交在 38 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

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

验证判据与等级说明 →

可信度证据

⚠︎ 实装验证未通过(unknown · 2026/9/30) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
安全扫描自动 · 静态分析源码
v2.7.0
🟢 扫描:未发现敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描未发现敏感能力:最多只发网络请求、读取自身元数据或只读文件,未发现执行命令、文件写入或敏感信息读取。

扫描于 2026/11/4(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

社区实装还没有人打卡 · 装过的第一个就是你
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-client-modules
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 启动失败保留界面
  • 报错降级为警告
  • 警告可关闭

插件启动失败时别让 DSH UI 整个挂掉,把致命的启动报错降级成可关闭的警告。

README

由 DeepSeek 最新模型翻译生成
dsh-escape-hatch 当一个插件启动失败时,保住你的 DSH 界面。 dsh-escape-hatch 是一个 DSH(DeepSeek Harness)插件。它把致命的 "Failed to load plugins" 启动死屏,降级为右下角一条可关闭的警告 —— 界面其余部分照常可用。 一个插件坏掉,不该拖垮整个应用。 问题 DSH Web 外壳在 boot 结束时,会对全部 Loader entry 做一次启动审计。只要有任意一个 entry 没达到 ACTIVE —— import 失败、pending 服务、fiber 失败 —— 它就抛错: function VS(e, n) { const o = []; for (const s of e.loader.entries()) { if (s.fiber === void 0) { o.push(${s.options.name}: import failed...); continue; } // ... } if (o.length > 0) throw new Error(web boot: ${o.length} entries did not activate\n${o.join("\n")}); }
展开完整文档(README 剩余 1 节)
dsh-escape-hatch

当一个插件启动失败时,保住你的 DSH 界面。

dsh-escape-hatch 是一个 DSH(DeepSeek Harness)插件。它把致命的 "Failed to load plugins" 启动死屏,降级为右下角一条可关闭的警告 —— 界面其余部分照常可用。

一个插件坏掉,不该拖垮整个应用。

问题

DSH Web 外壳在 boot 结束时,会对全部 Loader entry 做一次启动审计。只要有任意一个 entry 没达到 ACTIVE —— import 失败、pending 服务、fiber 失败 —— 它就抛错:

function VS(e, n) {
const o = [];
for (const s of e.loader.entries()) {
if (s.fiber === void 0) { o.push(${s.options.name}: import failed...); continue; }
// ...
}
if (o.length > 0)
throw new Error(web boot: ${o.length} entries did not activate\n${o.join("\n")});
}

这个 throw 冒泡到 boot runner 的 catch,兜底调用 boot 覆盖层的 page.fail():

catch (n) { console.error(n), e !== void 0 ? e(n) : this.page.fail(n instanceof Error ? n.message : String(n)) }

page.fail() → render() → card.replaceChildren(wordmark, failureBlock),把整张启动卡片替换成 "Failed to load plugins"。

更糟的是:异常中断了后续流程,紧随其后的应用挂载——

await e.inject(["uiRenderer"], (o) => {
o.effect(() => o.uiRenderer.mount(n), "web boot: application mount");
});

——永远不会执行。一个插件失败,意味着完全没有界面。

关键的割裂

宿主侧其实早就分清了主次。@deepseek-ai/dsh-app-boot 里存在明确的「必需 / 可选」区分:

function activationDiagnostic(binName, failures) {
// "Render optional-only warnings without changing startup policy."
}
function startupDiagnostic(binName, failures, required) { /* ... / }

可选插件失败只降级为警告,不改变启动策略。

而浏览器外壳没有这层区分,把任何未激活条目都当致命错误。本插件补的就是这道缝。

工作原理

dsh-escape-hatch 在运行期接管这条失败路径的两端,不修改任何发行产物。

| 环节 | 机制 |
| 捕获 | 包一层 window.__ModuleLoader__.create,留住外壳的模块系统 → Loader → 各 entry 的 fiber 状态 |
| 判定 | 在审计失败的那一刻读实时状态:有 entry 为 ACTIVE* 且 有 entry 未激活 ⇒ 可降级 |
| 拦截 | 包 Element.prototype.replaceChildren,在死屏即将被绘制的瞬间按住它 |
| 恢复 | 补挂应用,复刻外壳跳过的步骤:ctx.inject(['uiRenderer'], ui => ui.effect(() => ui.uiRenderer.mount(root))) |
| 告知 | 挂一条可关闭的角落警告条,列出未激活的插件与原始错误 |

恢复这步必需且不显然。抑制死屏渲染,并不会恢复被异常跳过的那次挂载 —— 少了它,页面会停在空白卡片上,比一块诚实的失败屏更糟。

安全边界

如果所有 entry 都未激活,那是平台级故障:挂什么都是空壳。此时插件判定为不可降级,原样保留致命死屏。它绝不掩盖真正的平台故障。

已验证行为

下面这些都可以用仓库自带脚本复现。没有"相信我"这一项。

npm run verify —— 21 项检查,全部通过

scripts/verify-bundle.mjs(13/13) —— 在最小 DOM/浏览器桩下验证客户端 bundle 契约:

- 恰好调用一次 window.__ModuleLoader__.load({ id, factory })
- 注册的 id 等于包名
- factory 可 materialize 并导出 apply
- apply(ctx) 装上全部拦截层
- ctx.effect 的清理能完整还原 create 与 replaceChildren

scripts/verify-rescue.mjs(8/8) —— 用真实 bundle 加忠实复刻的外壳 boot 序列做端到端验证:

| 场景 | 预期 | 结果 |
| A — 无插件,1 个插件失败 | 出现死屏,UI 不挂载 | ✅ 故障被复现 |
| B — 有插件,1 个插件失败 | 死屏抑制,警告条挂出,UI 正常挂载 | ✅ 成功救回 |
| C — 有插件,全部插件失败 | 死屏保留,不挂条,UI 不挂载 | ✅ 未掩盖平台故障 |

场景 A 的存在,是为了证明这套测试真的能抓到那个线上 bug。一个只会通过的测试不构成证据。

上下文与 token 开销:零

这是一个纯客户端的容错层,与 LLM 上下文窗口没有任何交互:

- 不注册任何工具(工具定义会占用系统提示词)
- 不订阅对话事件、不读取会话历史
- 无 LLM 调用、无后台轮询
- dsh.client.inject 为空 —— 连插件间服务依赖都没有
- 客户端半边跑在浏览器里,碰不到 prompt 组装管线

宿主半边只有一行 ctx.logger.info。它不注册 agent/pre-step 钩子、不注入系统提示词、不写 memory。

运行时占用:约 14 KB JavaScript,外加两个被包装的函数引用。警告条元素只在插件真的失败时才创建;正常启动时,DOM 上不会多任何东西。

安装

推荐:从源码构建并安装

npm run install:local # profile 默认 web
npm run install:local -- --profile tui

一条命令跑完全流程 —— 从 src/ 重建 lib/、跑完全部验证、打成 tarball、装进 profile。每次改完源码都用它。

手动安装

1. 打包
npm pack

2. 装进 profile
dsh plugin --profile web add "C:\路径\dsh-escape-hatch-1.0.0.tgz"

然后重启 DSH web 服务并刷新页面。

不要改已安装的那份

直接编辑 profile\node_modules\dsh-escape-hatch\lib\... 来修 bug 不会留存。下一次 dsh plugin install 或重建 profile 会把它覆盖掉,修复悄无声息地失效。

这条工作流的正解永远是:

- 改源头(src/,或上游依赖),
- 重新构建并重装(npm run install:local),
- 或者用 file: 把依赖指向本地打过补丁的源。

锁定明确版本

安装时给出明确版本号。不要留下 caret 范围(^1.0.0)—— 范围可能把含修复的那个版本挡在门外,这正是 ^0.6.11 踩过的坑。

按 tag 安装:

dsh plugin --profile web add https://gh-proxy.com/https://github.com/MYCF711/dsh-escape-hatch/releases/download/v1.0.0/dsh-escape-hatch-1.0.0.tgz

安装行为异常时

pnpm 在部分 profile 配置下会崩溃 —— 实测于 pnpm 12.4.2 + hoisted linker + 体积较大的包,报 memory allocation of 21474836480 bytes failed。崩完之后,下面三条记录经常互相矛盾,而退出码看起来却是正常的。

用 dsh-plugin-toolkit 三方对账:

node diagnose-install.mjs --profile web

它会比对 package.json 的依赖声明、pnpm-lock.yaml 的解析记录、以及 node_modules 里实际存在的东西(包括装的是实体目录还是符号链接),不一致时直接打印修复步骤。

根因是 pnpm 的缺陷,上游已在 12.7.0 修复。若出现该症状,先查 pnpm 版本:dsh-plugin-doctor 会在启动时锁定可用版本。

生效条件

客户端 bundle 由宿主扫描 Loader entries 中的 dsh.client 声明后发现,并组装进 window.__DSH_BOOT__。因此需要重启 DSH web 服务并刷新页面才会装载。immediately: true 保证插件的 apply 在 boot 序列之前运行 —— 这正是拦截得以成立的前提。

构建

自研插件必须附带打包好的客户端。运行时只认一种形式:

window.__ModuleLoader__.load({ id: "", factory: (require) => exports });

npm run build
或
node scripts/bundle-client.mjs --entry src/client.js --out lib/client.js

scripts/bundle-client.mjs 是一个零依赖、单入口的 ESM 打包器,产出上述形式。几个关键契约:

- 被执行的脚本只注册一个 factory。所有副作用留在闭包内,在 materialization 时才运行(lazy CJS 模型)。
- require 是同步表查找:平台 seed → 已 materialize 的模块 → 已注册的 factory → 否则抛错。依赖必须内联,或声明进 dsh.client.external。
- 本地模块的 import 语句会被改写成同步 require 调用;ESM 的 export 会被降级到 module.exports。

注册用的 id 从 package.json 读取,因此 bundle 会随包名自动保持同步。

仓库结构

.
├── cordis.patch.yml # loader 补丁:插入本插件 ├── package.json # 声明 dsh.client(platform/immediately/inject) ├── src/ │ ├── client.js # 浏览器半边 —— 真正的拦截逻辑 │ └── index.js # 宿主半边 —— 只有一行日志 ├── lib/ # 构建产物,刻意提交(运行时直接消费) │ ├── client.js # 已打包,__ModuleLoader__.load 包装 │ └── index.js └── scripts/ ├── bundle-client.mjs # 零依赖客户端打包器 ├── pack-and-install.mjs # 构建 → 验证 → 打包 → 安装,一步到位 ├── verify-bundle.mjs # 13 项契约检查 ├── verify-rescue.mjs # 8 项端到端行为检查 └── verify-install.mjs # 15 项安装态检查

通用的插件装载诊断工具在另一个仓库:dsh-plugin-toolkit。它们与本插件的用途无关,因此不在此重复保留。

lib/ 是故意提交的:运行时直接消费构建产物,而 DSH 对插件没有安装期构建步骤。

关于上游

长期看,这个 throw 更合理的形态应该是上游 DSH 里的警告 —— 可选插件失败不该拖垮界面,宿主侧本来就是这个行为。如果未来某个 DSH 版本放宽了客户端审计,本插件即失去意义,直接移除即可。

卸载

1. 从 cordis.patch.yml 移除对应的 insert 块
2. 删除 node_modules/dsh-escape-hatch 目录

不修改任何其他状态。

许可证

MIT © 2026 MYCF711
预览分享卡片长什么样 ↓MYCF711/dsh-escape-hatch
分享 · DeepSeek Harness Hub
MYCF711/dsh-escape-hatch

插件启动失败时保留界面,降级为可关闭警告

这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。
认领条目 →
回到顶部