Boot-time watchdog for DSH web: detects broken plugins, disables them persistently so the UI opens, with quarantine audit, automatic probe recovery, and one-click restore.
Install
# from npm (prebuilt)
dsh plugin --profile web add @dsh-error-tell/client-tell
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:dphmoblie/dsh-error-tell#path:/packages/client-tell
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
This plugin publishes its README in Chinese only.
DSH 插件自检/守护:在
dsh web启动时检测其他插件的加载/激活问题,把问题插件持久化禁用(写入用户补丁层disabled: true),让 dsh web 正常打开,并提供隔离账本(quarantine)与一键恢复。
为什么需要它
DSH 的启动策略是 fail-loud:
- 宿主侧:任一插件 import/apply 失败会让
dsh web进程直接退出(installFailLoud),网页根本起不来; - 浏览器侧:
dsh-client-web一次性 settle,任一客户端插件失败会让加载页停在 "Failed to load plugins",UI 不挂载。
同树的插件救不了当次启动(树已整体回滚)。所以本项目的形态是:检测 → 落盘禁用 → 重启/刷新,而不是"启动时互救"。
功能特性
- 启动预检(boot-guard):组合配置 → 静态检查(重复 id / 缺 name)→ import 干跑(子进程隔离)→ 发现问题直接禁用,坏插件根本不进启动流程;
- 自动重启包装:
dsh web失败后从 stderr 归因插件名 → 追加禁用 → 重启(限次 + 熔断,无法归因不循环); - 运行时哨兵(runtime-guard bundle):捕获 apply/import 失败,在进程退出之前同步写账本 + 禁用,重启后生效;
- 浏览器一键恢复(client-tell):加载页自动注入「禁用并重载」按钮 +
POST /api/error-tell/disable端点,刷新即恢复; - 管理面板交互:徽标可拖拽,面板锚定在徽标旁并跟随移动;历史记录显示每个插件的功能描述(package.json description)、包名与失败原因,方便使用的人排错;
- 设置页「错误哨兵」分区(客户端模块):dsh 设置左侧新增分区,可读取全部插件状态(运行中/已禁用/挂载失败/用户层禁用 + 功能描述 + 哨兵历史),并对任意插件手动禁用/恢复(走同一安全阀:保护名单拒禁、maxDisable 熔断、只写 managed 段、热重载 1-2 秒生效);
- 隔离账本:每次禁用的行、包名、阶段、错误、来源均可审计;
restore一键回滚; - 防误杀:
maxDisable熔断(默认 5)、环境/批量失败过滤、自我禁用保护、CSRF 防护头、重启循环上限。
架构(三层)
┌─ boot-guard(CLI,主防线)─────────────────────────────┐
│ dump-config → 检查 → 写 managed 段 → spawn dsh web │
│ 失败归因 → 追加禁用 → 重启(限次) │
└───────────────────────────────────────────────────────┘
┌─ runtime-guard(宿主 bundle,辅防线)──────────────────┐
│ internal/status + _initTask + 兜底扫描 → 同步落盘 │
└───────────────────────────────────────────────────────┘
┌─ client-tell(浏览器,用户闭环)───────────────────────┐
│ tapIndex 注入按钮 → POST /api/error-tell/disable │
│ → watchUserPatches 热重载 → 刷新即恢复正常 │
└───────────────────────────────────────────────────────┘
详见 docs/architecture.md 与 docs/plan.md。
安装
方式一:本地仓库开发(推荐先体验)
git clone https://github.com/dphmoblie/dsh-error-tell.git
cd dsh-error-tell && pnpm install
node scripts/setup-hooks.mjs # 可选:接入 Gitleaks 提交门禁(需先安装 gitleaks)
pnpm test # 单元测试
pnpm e2e:cd # 分段 e2e(client-tell + import 预检)
pnpm e2e:efg # 分段 e2e(幂等性 / YAML 损坏 / 多坏插件)
pnpm e2e:h # 分段 e2e(apply 挂起 → 进程级超时 → 熔断)
方式二:作为 bundle 装入你的 profile
# 在 profile 里安装 runtime-guard 与 client-tell(file: 或发布到 npm 后按包名)
cd ~/.dsh/profiles/web
pnpm add @dsh-error-tell/runtime-guard @dsh-error-tell/client-tell
# 把两个包加入 package.json 的 dsh.profile.bundles,重启 dsh web 生效
发布状态:已发布 core 0.1.2 / boot-guard 0.1.2 / runtime-guard 0.1.2 / client-tell 0.1.8(MIT)。注意:client-tell 必须用
pnpm publish(自动把workspace:*依赖转换为具体版本);0.1.6 因误用npm publish而依赖未转换,已废弃。
使用
# 预检 + 启动(失败自动禁用并重启,最多 restart-limit 次)
dsh-error-tell guard --profile web --restart-limit 2
# 只做预检,不启动、不写配置
dsh-error-tell guard --dry-run
# 查看隔离账本 / 当前禁用的插件
dsh-error-tell status
dsh-error-tell quarantine
# 恢复某个被禁用的插件
dsh-error-tell restore <rowId>
参数
| 参数 | 默认 | 说明 |
|---|---|---|
--profile <name> |
web |
要守护的 profile |
--patch <file> |
- | 附加 patch 覆盖层(可重复) |
--dry-run |
false |
只检查并打印计划,不启动不落盘 |
--restart-limit <n> |
2 |
失败归因后的最大重启次数 |
--max-disable <n> |
5 |
单次最多自动禁用行数(熔断防误杀,DSH_ERROR_TELL_MAX_DISABLE 可覆盖) |
--timeout-ms <n> |
120000 |
dsh 子进程超时(apply 挂起时熔断) |
--port <n> |
0 |
传给 dsh 的端口 |
落盘位置
| 文件 | 说明 |
|---|---|
$DSH_HOME/cordis.patch.yml |
home 级补丁;# --- dsh-error-tell managed ... --- 段为自动管理区(请勿手改) |
$DSH_HOME/state/dsh-error-tell/quarantine.json |
隔离账本(每次禁用的审计记录) |
环境变量:DSH_HOME(默认 ~/.dsh)、DSH_ERROR_TELL_QUIT_AFTER_MS(测试钩子,正常启动后自动退出,勿在生产使用)。
安全设计
- 禁用端点要求
x-dsh-error-tell: 1头(防跨站请求); - 拒绝禁用自身与
error-tell-*守护行; maxDisable熔断:待禁用行数超限时拒绝修改任何配置;- 哨兵不递归、不禁自己、失败只记日志;
- 连续 2 次失败才禁用(账本 failCount,
--fail-threshold可调),瞬态失败不会被永久封杀; - 启动探针自动恢复:已禁用行每次启动临时启用真实加载,成功即自动解除禁用;
- web 管理面板:正常页面自动显示被禁用插件列表,一键恢复(
/api/error-tell/status+/restore); - 重启循环有上限,无法从 stderr 归因时熔断不循环;
- 所有自动改动都可审计、可
restore回滚。 - 说明:
dsh --dump-config预检会触发 dsh 自身的模块 heal(创建profiles/node_modules),属 dsh 行为;guard 本身不写任何配置。
验证记录
- 单元测试 83 项:core + boot-guard + runtime-guard + 注入脚本 VM ×7 + meta 解析 ×4 + 客户端模块 VM ×3(设置分区注册契约)+ runChecks 干跑编排 ×5 + Windows 参数转义 ×6 + 熔断增量语义回归 ×1 + cause 链归因 ×4(
culpritOf/stageOf)+ e2e 辅助 ×7(web URL 解析 / 参数安全 / 超时无孤儿进程)+ 并发与账本回归 ×21(跨进程锁 / 损坏账本备份 / 全新 DSH_HOME / 回退条件 / 干跑假阳性 / Origin 校验 / rowId 校验 …) - e2e Phase A–H:坏插件 → 启动失败 → 自动禁用 → 重启成功;runtime-guard 进程退出前落盘;client-tell 端点 + 组合图排除;import 预检拦截;幂等性(零副作用);YAML 损坏友好失败;多坏插件;挂起超时熔断
- 详见 docs/verification.md
开发
packages/
boot-guard/ # 预检 CLI + 重启包装(bin: dsh-error-tell)
runtime-guard/ # 宿主哨兵 bundle(dsh.bundle.patch)
client-tell/ # 双面包:tapIndex 注入 + 禁用端点
test-fixtures/ # 坏插件工厂(apply / import / client / hang)
test/ # e2e(run-e2e 全量 + verify-cd/efg 分段)+ 注入脚本 VM 测试
docs/ # plan / architecture / verification
.github/workflows/ # CI:fast(单测+分段 e2e)+ full(全链路 A-H)
Roadmap
- M0 骨架 + 禁用通道验证
- M1 boot-guard(预检/账本/patch-writer/重启包装)
- M2 runtime-guard(同步落盘)
- M3 client-tell(注入脚本 + 禁用端点)
- M4 用例矩阵 + 熔断 + CI
- S2 评审修复:连续失败判定 + 探针自动恢复 + web 管理面板恢复
- 发布 npm(core/boot-guard/runtime-guard/client-tell 0.1.x)
- 真实用户环境试点
License
MIT
Links
More in this category
yjh051108/dsh-routing-suite★ 7014
One repository, three parts: a runtime injector for DSH plugin packages (inject, hot-reload, unload, promote a dev staging tool to the front, route self-heal, plus a settings-page plugin manager that lists, unloads and drags folders in to internalize), a task-aware reasoning-mode router agent preset (router-standard / router-spec / router-react), and a graded two-level task protocol whose six tools (commit_star, lock_stage, revise_do, edit_plan, mark_task, redteam_verdict) pin task state to disk. The injector implementation ships in-tree, so the install carries its own behaviour rather than a dependency list.
strukto-ai/mirage#dsh★ 3682
Swaps the filesystem and bash providers for a mirage virtual workspace: file tools and shell commands run over mounted resources (RAM, S3, Redis, Slack, Gmail, Notion, Postgres) instead of the host disk, with per-mount read/write/exec modes, per-command sandbox routing (monty, pyodide, quickjs in process; docker, e2b, daytona remote), and installed CLIs (git, gh, slack, linear, ntn, gws, or one you register) as head words in the virtual terminal.
hust-open-atom-club/oh-dsh★ 322
Community distribution: TUI, desktop, and Web UI as one bundle with layered installation.
weijiafu14/pi2dsh★ 212
Pi Host ABI compatibility engine: after one install, unmodified Pi extensions from npm mount as native DSH plugins with `dsh plugin add <pi-package>`. Verified end to end on stock DSH with pi-mcp-adapter (full MCP manager: OAuth, resources, prompts, MCP Apps, elicitation, sampling), @tintinweb/pi-subagents, pi-code, pi-hermes-memory and pi-background-tasks; `pi2dsh inspect` reports a package's compatibility before installing.
Fishquito7/dsh-skill-mcp-panel★ 193
Manages DSH skills and MCP servers from the web settings: skill cards with hot enable/disable, workspace scopes, groups, batch migration and drag-and-drop import, plus stdio/HTTP MCP CRUD with connection tests, secret redaction and the unified dsh-panel CLI.
lire1131/dsh-undo-savepoint★ 179
Undo/redo & rollback system for DSH: every config change is auto-snapshotted; undo/redo/restore to any version from the WebUI or the offline CLI/GUI tools (works even when DSH fails to boot).
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.