将已安装的 DSH 插件连同依赖闭包与文档导出到移动介质,在隔离内网中扫描目录并离线安装或更新插件。
安装
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:FlameSky-S/dsh-plugin-porter
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
English | 简体中文
这是本项目的完整中文文档。仓库首页的英文说明见 README.md。
把当前 DSH 里安装的插件(含依赖闭包和文档)导出成一个可刻盘 / 拷 U 盘的目录,带到隔离内网后,再从这个目录里选择安装或更新插件。
仓库:https://github.com/FlameSky-S/dsh-plugin-porter
问题反馈:https://github.com/FlameSky-S/dsh-plugin-porter/issues
它解决的是三个具体问题:
- 外网导出:选择插件,自动收集它的运行时依赖、README / LICENSE / GitHub 上游 README,生成带 SHA-256 校验的搬运包。
- 内网导入:在 DSH 的「设置 → 插件搬运 → 导入」里扫描搬运目录,看到版本与安装状态,勾选后离线安装 / 更新。
- 自举安装:内网机器还没有本插件时,用包内的
install-porter.cmd/.ps1一键把它装进 DSH profile,不依赖 npm、pnpm 或网络。
适用场景:外网机器可以联网,内网机器完全隔离;两台机器的 DSH 大版本、操作系统和 CPU 架构一致。
快速开始
第一步:外网导出
- 安装并启用本插件(见下方「安装本插件」)。
- 打开 设置 → 插件搬运 → 导出。
- 勾选要搬运的插件;输出目录默认是桌面。
- 点「导出所选插件」。导出在后台执行,完成后界面会显示目标目录与统计;大依赖闭包不会因为请求超时而失败。完成后会得到:
dsh-plugin-porter-export-20261107-161500/
├─ manifest.json # 插件、依赖、版本、来源清单
├─ checksums.json # 每个文件的 SHA-256
├─ packages/ # 插件 + 依赖闭包的完整包目录
├─ docs/ # 本地 README/LICENSE + GitHub 上游 README
├─ porter/ # 搬运助手自身(供安装脚本复制)
├─ install-porter.ps1 # 内网安装搬运助手(PowerShell)
├─ install-porter.cmd # Windows 双击入口
├─ install-porter.sh # Linux / macOS 入口
├─ porter-cli.cmd / .sh # 可选命令行入口(需要 node)
├─ INSTALL.md # 面向内网操作者的中文步骤
- 把整个目录刻盘或拷贝到内网。不要只挑
packages/子目录——导入依赖manifest.json和checksums.json。
第二步:内网安装本插件(首次或更新助手时)
- 完全退出 DSH(托盘图标 → 退出),不要只关窗口。
- 在搬运目录里运行:
install-porter.cmd
或手动:
powershell -NoProfile -ExecutionPolicy Bypass -File .\install-porter.ps1
脚本会:
- 找到
%USERPROFILE%\.dsh\profiles\desktop(或-ProfileDir指定的 profile); - 把
porter\复制到<profile>\node_modules\dsh-plugin-porter; - 在 profile 的
package.json里登记dependencies与dsh.profile.bundles; - 把旧
package.json备份到<profile>\.dsh-plugin-porter\backups\install-<时间戳>\; - 把旧插件目录改名为
node_modules\.dsh-plugin-porter-old-<时间戳>。
- 重新启动 DSH,打开 设置 → 插件搬运。
Linux / macOS:
./install-porter.sh /path/to/profile
# 或 DSH_PROFILE_DIR=/path/to/profile ./install-porter.sh
第三步:内网安装 / 更新插件
- 设置 → 插件搬运 → 导入。
- 点「选择文件夹」选择搬运目录(也可以直接粘贴绝对路径,例如
E:\dsh-plugin-porter-export-20261107-161500),点「扫描搬运目录」。 - 核对:
- 校验结果(SHA-256 是否全部通过);
- 每个插件显示:包内版本、已安装版本、状态(新装 / 相同 / 可更新 / 降级);
- 默认只勾选需要变化的插件。
- 点「安装所选插件」。安装会先备份受影响的旧包目录和
package.json,失败时自动回滚。 - 完全退出并重启 DSH,新插件生效。
安装本插件(开发 / 外网机器)
方式 0:从 GitHub Actions 下载构建好的 zip(推荐给普通用户)
- 打开 Build plugin package 工作流,点最近一次绿色运行。
- 在运行页面底部找到 Artifacts,下载
dsh-plugin-porter-0.1.1。 - 解压后按机器状态二选一:
- 内网机器还没有本插件:运行
scripts\install-porter.cmd,把搬运助手装进默认的desktopprofile; - 已经能在设置里看到「插件搬运」:把解压目录作为本地 bundle 安装,或运行
scripts\install-porter.ps1覆盖升级。
- 内网机器还没有本插件:运行
- 完全退出并重启 DSH。
GitHub Actions 的 artifact 下载需要登录 GitHub。如果维护者已经推送
v0.1.1标签,Releases 页面会附同样的 zip 和.sha256,可以直接拷到内网。
方式 A:从源码目录安装(推荐给开发者)
git clone https://github.com/FlameSky-S/dsh-plugin-porter.git
本仓库目录 dsh-plugin-porter/ 就是一个完整 bundle。用 DSH 的插件管理器安装它:
- 在 DSH 里让 agent 调用
plugin_manager,action: install_bundle,target指向本目录的绝对路径;或 - 用桌面端「插件 → 安装本地插件」选择本目录。
安装后重启 DSH,在 设置 → 插件搬运 看到入口即成功。
方式 B:内网自举安装
见上文「第二步」。这是给没有本插件、也没有 npm 源的内网机器准备的。
界面说明
导出页
- 列出当前 profile 中可识别的插件(有
package.json且在dependencies或node_modules中)。 - 显示体积、直接依赖数、文档数、是否 bundle / 是否有 Web UI。
- 选项:
- 包含文档:复制包内 README / LICENSE / CHANGELOG 等。
- 抓取 GitHub 上游文档:根据
repository字段下载README.md、README.zh-CN.md等(最多 2 个文件、单个不超过 2 MiB)。
- 导出时会计算完整依赖闭包:
dependencies+optionalDependencies,跳过peerDependencies/devDependencies;@deepseek-ai/*视为 DSH 运行时自带,不打包。
导入页
- 扫描目录后显示清单来源、生成时间、目标 profile、文件校验结果。
- 插件状态:
new:本机未安装;same:版本相同(默认不勾选,可手动重装);update:包内版本更高;downgrade:包内版本更低(默认不勾选,需手动选择)。
- 安装所选插件时,会把该插件的闭包中所有包一起复制,避免“插件在、依赖缺失”。
说明页
包含工作流、目录结构和限制说明。
GitHub Actions
仓库的 Build plugin package 工作流会在每次推送 main 时构建 zip 并上传 artifact;推送 v* 标签时会用同一个 zip 创建 Release(附 .sha256)。构建产物不需要提交到仓库,dist/ 已在 .gitignore 中忽略。
发布新版本(维护者)
修改
package.json里的version,提交并推送main。打标签并推送:
git tag v0.1.1 git push origin v0.1.1工作流会校验标签与
package.json版本一致,然后创建 Release 并附上 zip 与.sha256。
命令行
导出目录里带 porter-cli.cmd / porter-cli.sh,也可以用仓库里的 bin/porter.mjs。需要 PATH 中有 Node.js(DSH 自带的运行时也可以)。
# 查看 profile 与插件
node bin/porter.mjs list
node bin/porter.mjs status
# 导出(默认输出到桌面)
node bin/porter.mjs export --plugins dsh-skill-hub,dsh-ssh --out D:\porter-out
# 只导出指定插件,不抓 GitHub 文档
node bin/porter.mjs export --plugins dsh-skill-hub --no-remote-docs
# 扫描 / 校验
node bin/porter.mjs scan --dir E:\dsh-plugin-porter-export-20261107-161500
node bin/porter.mjs verify --dir E:\dsh-plugin-porter-export-20261107-161500
# 安装 / 更新(--allow-unverified 仅用于你确认过来源的损坏校验文件)
node bin/porter.mjs install --dir E:\... --plugins dsh-skill-hub
通用参数:--profile-dir <path>、--json、--help、--version。
命令行与 Web UI 调用的是同一套
lib/export.mjs/lib/import.mjs/lib/profile.mjs。
实现原理
为什么不用 pnpm / npm 源
内网没有 registry,也没有 pnpm。DSH Desktop 的 profile 使用 hoisted(扁平)node_modules:每个包是 node_modules/<name> 的真实目录。因此离线安装可以直接:
- 把包目录复制到
<profile>\node_modules\<name>;目标里已存在的依赖包不覆盖,只替换本次选中的插件本体; - 在 profile
package.json的dependencies写入name: version; - 如果包带
dsh.bundle.patch,把包名追加到dsh.profile.bundles。
DSH 启动时按 bundle 顺序解析 node_modules,不依赖 pnpm-lock.yaml。这就是本插件不调用 pnpm 也能安装的原因。
依赖闭包
从每个选中插件的 package.json 出发,BFS 遍历:
- 包含:
dependencies、optionalDependencies; - 跳过:
peerDependencies、devDependencies; @deepseek-ai/*视为 DSH 运行时提供,缺失时只记入runtimeProvided,不报错;- 其它缺失依赖会出现在
missing/optionalMissing中,并写进导出警告。
同一个 name@version 只打包一次。不同插件要求同一个包的不同版本时,导出会全部保留并给出警告;导入时优先保留目标 profile 里已经存在的版本,只对缺失的包复制最高版本。扁平 node_modules 无法真正并存多个版本,因此“已有环境不被改坏”优先于强行升级依赖。
校验
checksums.json记录导出目录中除自身外每个文件的 SHA-256(相对路径)。- 扫描时逐文件校验;缺失、哈希不匹配、路径穿越、符号链接都会报告。
- 默认安装前必须校验通过;
allowUnverified只应在你明确知道校验文件损坏时使用。
回滚
安装前:
- 备份
package.json到<profile>\.dsh-plugin-porter\backups\<时间戳>\package.json; - 对每个要覆盖的包,把旧目录重命名到备份目录。
安装中任一步失败:
- 删除新复制的包目录;
- 把旧目录移回;
- 恢复
package.json。
安装成功后旧目录保留在备份目录,确认无误后可手动清理。
兼容性与限制
- 平台 / 架构 / Node ABI 必须一致:搬运包只复制文件,不重新编译原生模块。Windows x64 → Windows x64、同 Node 大版本最稳妥。
- DSH 版本:manifest 记录来源 DSH 版本;导入时若与目标版本不同会给出警告,但不阻止安装。
- peerDependencies:不打包,假设由 DSH 或已安装插件提供。
pnpm-lock.yaml不更新:以后联网执行 pnpm 安装时可能重新解析依赖,这是预期行为。- 必须重启 DSH:新包目录与
dsh.profile.bundles在启动时解析;不重启不会加载新插件。 - Web UI 需要浏览器会话:所有
/api/dsh-plugin-porter/*路由都经过 DSH 的信任栅栏(回环地址 + 浏览器会话 + 同源检查),并额外要求x-dsh-plugin-porter: 1请求头。 - 只有用户级 profile 可写:本插件只操作当前 profile 的
node_modules与package.json;不会改 DSH 安装目录。 - 符号链接:导出时解引用(复制真实文件);导入时拒绝指向导出目录之外的符号链接。
故障排查
设置里看不到「插件搬运」
- 确认 profile
package.json的dsh.profile.bundles含dsh-plugin-porter; - 确认
node_modules\dsh-plugin-porter\package.json存在; - 完全退出并重启 DSH(不是刷新页面);
- 看 DSH 日志中是否有
[dsh-plugin-porter]的 bundle 加载错误。
安装脚本报“找不到 DSH profile”
用 -ProfileDir 显式指定,例如:
.\install-porter.ps1 -ProfileDir "C:\Users\you\.dsh\profiles\desktop"
安装脚本复制失败(文件被占用)
先完全退出 DSH(包括托盘进程),再运行脚本。脚本检测到 DeepSeek 进程时会提示。
扫描很慢
扫描会对导出目录内所有文件计算 SHA-256。几万个文件或几 GB 的包会明显变慢;这是完整性校验的代价。可以先用 verify 命令行确认,再在 UI 安装。
GitHub 文档抓取为 0
- 包
package.json没有repository字段; - 仓库不是 GitHub;
- 网络需要代理:本插件会先尝试 Node fetch,失败后在 Windows 上回退到 PowerShell(走系统代理),在 Linux/macOS 上回退到
curl; - README 文件名不在候选列表(
README.md、readme.md、README.zh-CN.md、README.zh.md)。本地 README 仍会照常复制。
安装后插件没生效
完全退出 DSH 再启动。Web 端刷新页面不会重新解析 bundle。
开发
# 语法检查
node --check index.js
node --check client.js
node --check bin/porter.mjs
# 本地导出到临时目录
node bin/porter.mjs export --plugins dsh-skill-hub --out .porter-test/exports
# 扫描
node bin/porter.mjs scan --dir .porter-test/exports/<生成的目录>
# 在临时 profile 上测试离线安装
node bin/porter.mjs install --dir <导出目录> --plugins dsh-skill-hub --profile-dir .porter-test/profile
目录:
index.js Host 入口:注册 /api/dsh-plugin-porter/*、信任栅栏、JSON 处理
client.js Web UI:settings.section 面板(导出 / 导入 / 说明)
lib/util.mjs 文件、哈希、版本、路径安全工具
lib/profile.mjs profile 解析、插件清单、依赖闭包、状态/默认值
lib/export.mjs 导出:复制包、文档、清单、校验和
lib/import.mjs 扫描、校验、安装/更新、回滚
bin/porter.mjs CLI
scripts/ 内网自举安装脚本(PowerShell / cmd / sh)
docs/ 本插件文档
无第三方 npm 依赖,仅使用 Node 内置模块。
License
MIT
链接
同类插件
dsh-market/dsh-market★ 10295
在 DeepSeek Harness 设置页内浏览、搜索并安装社区插件,支持分类筛选、一键更新与停用,以及主题切换与配置备份。
zhu1090093659/dsh-web#packages/dsh-plugin-manager★ 9959
设置 → 插件 分区里的插件管理 Tab:从 npm/git 安装带进度、下次启动生效的启停开关、安装冲突对账可撤销、失败一键转交修复会话。
kingOfSoySauce/dsh-skin-market★ 295
原生皮肤市场与生命周期管理器,发现社区皮肤、展示预览与兼容状态,并提供已验证的一键安装或手动安装入口。
awesome-dsh-plugin/dsh-find-plugin★ 216
会话内直接找插件:按关键词/分类搜索本精选 registry,返回描述与可直接执行的安装命令。
bradeGithub/DSH-Plugins-Marketplace★ 199
面向 GitHub dsh-plugin 话题的插件与技能市场:设置页内逛自动收录的全量索引(CI 每 2 小时刷新),一键安装带类型识别、安装脚本与宿主依赖遮蔽安全检查、环境变量密钥管理,并附 STANDARD.md 识别层规范。
Sanqi-normal/dsh-webui-market-plugin★ 103
dsh Web GUI 内的社区插件市场:浏览 awesome-dsh-plugin.com 目录,从 设置 → 插件 → 插件市场 安装/卸载插件到 profile。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。