nzl153
dsh-preflight
DSH 插件安装前预演冲突、出问题后读日志定位根因,并给出可直接执行的修复命令。
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 15, 2026
- Updated
- Aug 15, 2026
Introduction
dsh-preflight
English | 中文
装了个插件,DSH 起不来了。日志刷出几十行插件加载失败,看着像全坏了——真正的原因可能只是两个插件抢了同一个 entry id。
dsh-preflight 就是为这种事写的:
- 装之前,把候选插件下载下来,跟你当前 profile 对撞一遍,告诉你会不会出事
- 出事之后,读 DSH 的日志,把满屏连锁报错还原成一句根因
两种情况它都会把该敲的命令直接打出来,但不替你敲。工具自己不动手,才敢去下载和解析陌生插件的产物。
安全边界:只下载并解包候选产物,静态解析 JSON/YAML 和源码文本。不安装插件、不执行候选代码或 install scripts、不改动 DSH profile。网络候选写进
mkdtemp临时目录,用完即删。
它回答的是「已实现的规则有没有发现问题」,不提供安全性证明,也没有 daemon 和后台自动修复。
要求
- Windows 优先;Node.js 22.19 或更高版本
- 默认 DSH 安装根:
E:\dsh(用--dsh-root或环境变量DSH_ROOT覆盖) - 默认 profile:
%USERPROFILE%\.dsh\profiles\web - 设置
DSH_HOME可覆盖.dsh根目录
使用
dsh-preflight check dsh-web-search-pro
dsh-preflight check github:owner/repo#main --profile web
dsh-preflight check "D:\work\my-plugin"
dsh-preflight audit
dsh-preflight diff dsh-web-search-pro --json
dsh-preflight explain
dsh-preflight explain --log "D:\logs\dsh.err.log" --json
explain 默认读取 DSH 安装根下的 dsh.err.log、dsh.log、dsh.restart.log 和 launch-trace.log。每个文件只读取尾部 2 MiB。日志不存在或为空时正常返回 INFO。
常用参数:
--profile <name>:profile 名,默认web--profile-dir <path>:直接指定 profile 目录--dsh-root <path>:覆盖 DSH 安装根--dsh-home <path>:覆盖.dsh根目录--registry <url>:覆盖 npm registry--log <path>:explain只分析指定日志--json:输出机器可读 JSON--strict:存在 WARN 时也返回退出码 1
检查项
| Finding | 级别 | 含义 |
|---|---|---|
ENTRY_ID_COLLISION | BLOCK | 候选 entry id 已被现有 bundle 使用 |
UNRESOLVABLE_AFTER_INSTALL | BLOCK / UNKNOWN | 安装后仍无法证明 insert[].name 可解析 |
CONFIG_OVERRIDE_SILENT | WARN | profile 后置层会整段替换候选同 id 的 config |
SERVICE_PROVIDER_DUPLICATE | WARN / UNKNOWN | 显式 service 重复,或 manifest 信息不足 |
MISSING_BUNDLE_MANIFEST | BLOCK | 缺少可读取的 dsh.bundle.patch |
ENTRY_MISSING_IN_ARTIFACT | BLOCK | main、module、bin 或递归 exports 目标不在产物中 |
BUILD_APPROVAL_REQUIRED | INFO | Git 来源包含需要人工授权的构建脚本 |
VERSION_RANGE_MISMATCH | WARN | @deepseek-ai/* peer range 与本机版本不匹配 |
UNPINNED_SOURCE | WARN | GitHub ref 不是完整 commit SHA |
SENSITIVE_API_SURFACE | INFO | 静态文本匹配到子进程、文件写入或外联 URL |
INSTALL_SCRIPT_PRESENT | WARN | 安装时存在生命周期脚本 |
BUNDLES_DEPS_DRIFT | BLOCK | profile 的 bundles、dependencies 与磁盘状态漂移 |
UNMET_PEER | WARN | 当前 profile 的 peer 缺失或版本不匹配 |
运行时日志模式
| Finding | 级别 | 含义 |
|---|---|---|
PORT_ALREADY_IN_USE | BLOCK | 端口被旧进程占用,新实例未启动,插件错误是连锁表象 |
BUNDLE_UNRESOLVED_AT_BOOT | BLOCK | 启动时无法解析 bundle;会与 profile 交叉判断卸载残骸 |
ENTRY_ID_COLLISION_AT_BOOT | BLOCK | 启动日志显示 entry id 被重复注册或覆盖 |
MODULE_ENTRY_MISSING | BLOCK | 已安装包的内部入口文件不在发布产物中 |
UNRECOGNIZED_ERROR | UNKNOWN | 日志有错误,但确定性规则无法归类 |
NO_LOGS_TO_ANALYZE | INFO | 默认日志或指定日志不存在、为空 |
NO_RECOGNIZED_RUNTIME_ERROR | INFO | 日志有内容,但没有匹配到错误或已实现模式 |
退出码
0:没有 BLOCK;默认允许 WARN1:存在 BLOCK,或--strict下存在 WARN2:参数、下载、解包或基础 profile 读取失败
验证状态
规则的可信度并不一致,这里如实标注。
已在真实故障上验证(隔离测试台复现 + 真实归档日志):
ENTRY_ID_COLLISION、BUNDLES_DEPS_DRIFT、ENTRY_MISSING_IN_ARTIFACT、UNMET_PEERPORT_ALREADY_IN_USE、ENTRY_ID_COLLISION_AT_BOOT、BUNDLE_UNRESOLVED_AT_BOOT、MODULE_ENTRY_MISSING
只有单元测试覆盖,尚未在真实场景触发过:
CONFIG_OVERRIDE_SILENT、SERVICE_PROVIDER_DUPLICATE、BUILD_APPROVAL_REQUIRED、VERSION_RANGE_MISMATCH、UNPINNED_SOURCE、SENSITIVE_API_SURFACE、INSTALL_SCRIPT_PRESENT、MISSING_BUNDLE_MANIFEST、UNRESOLVABLE_AFTER_INSTALL
已知局限:
explain的日志模式按当前 DSH 版本的措辞编写。DSH 若改动日志文案,相关规则会静默失效——不会报错,只是不再命中。- 本工具只说明「已实现的规则是否发现问题」,不构成安全性证明,也不能替代人工审阅插件源码。
- 输出为 CLEAR 只意味着这些规则没有命中,不意味着插件没有问题。
实现边界
- npm 来源读取 registry metadata 和 tarball,并校验可用的
sha512integrity。 - GitHub 来源使用 codeload archive;本地来源经过
realpath后只读。 - tar 解包拒绝路径穿越和绝对路径,不创建 archive 中的 symlink 或 hardlink,并限制下载、解压、单文件和文件数量。
- YAML 的
!!js只保留为字符串,绝不求值。 - service provider 只使用显式 manifest 声明;信息不足时报告 UNKNOWN。
- 源码扫描是有上限的正则匹配,只列事实,不判断恶意与否。
- 日志分析只读取文件尾部,不查询端口或进程。处方只作为命令文本输出,从不自动执行。
开发
pnpm install
pnpm check
node lib/cli.js --help
许可证:MIT。