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

插件入口迁移 hhse/dsh-plugin-entry-sync

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

扫描各配置档插件入口,迁移并修复未生效的登记

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

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

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

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么[目录]—
dsh 原生插件 · market
暂无时间戳·判据 →
装得上吗[实装验证]—
本站尚未做安装检查
暂无时间戳·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/04·判据 →
还在维护吗[仓库]✓
活跃:最近一次提交在 2 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

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

验证判据与等级说明 →

可信度证据

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

可执行外部命令读写/删除本地文件
查看扫描证据(共 41 处,展示前 39 条)
  • 写入本地文件
    scripts/apply.mjs:86
    mkdirSync(backupDir, { recursive: true });
  • 写入本地文件
    scripts/apply.mjs:89
    if (existsSync(src)) copyFileSync(src, join(backupDir, f));
  • 读取本地文件
    scripts/apply.mjs:89
    if (existsSync(src)) copyFileSync(src, join(backupDir, f));
  • 执行外部命令来自配置/变量
    scripts/apply.mjs:107
    const pre = spawnSync(
  • 读取本地文件
    scripts/apply.mjs:135
    if (!existsSync(from)) continue;
  • 写入本地文件
    scripts/apply.mjs:136
    copyFileSync(from, join(targetProfile.dir, f));
  • 执行外部命令来自配置/变量
    scripts/apply.mjs:143
    const res = spawnSync(
  • 执行外部命令来自配置/变量
    scripts/apply.mjs:162
    spawnSync(tool.command, [...tool.args, 'add', spec, '--reporter=append-only'], {
  • 读取本地文件
    scripts/apply.mjs:181
    const yaml = existsSync(yamlPath) ? readFileSync(yamlPath, 'utf8') : '';
  • 读取本地文件
    scripts/apply.mjs:181
    const yaml = existsSync(yamlPath) ? readFileSync(yamlPath, 'utf8') : '';
  • 写入本地文件
    scripts/apply.mjs:187
    writeFileSync(yamlPath, next, 'utf8');
  • 读取本地文件
    scripts/apply.mjs:214
    if (patchRel && !existsSync(join(pkgDir ?? '', patchRel))) {
  • 写入本地文件
    scripts/apply.mjs:223
    writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
  • 读取本地文件
    scripts/apply.mjs:245
    if (patchRel && !existsSync(join(resolvePkgDir(targetProfile.dir, name) ?? '', patchRel))) {
  • 读取本地文件
    scripts/install-guard.mjs:38
    const healPath = existsSync(installedHeal) ? installedHeal : localHeal;
  • 读取本地文件
    scripts/install-guard.mjs:79
    if (!existsSync(healPath)) {
  • 读取本地文件
    scripts/install-guard.mjs:87
    if (!existsSync(file)) {
  • 读取本地文件
    scripts/install-guard.mjs:91
    const text = readFileSync(file, 'utf8');
  • 删除本地文件
    scripts/install-guard.mjs:100
    unlinkSync(file);
  • 读取本地文件
    scripts/install-guard.mjs:105
    if (existsSync(file)) {
  • 读取本地文件
    scripts/install-guard.mjs:106
    const text = readFileSync(file, 'utf8');
  • 写入本地文件
    scripts/install-guard.mjs:128
    if (existsSync(file)) copyFileSync(file, `${file}.bak`);
  • 写入本地文件
    scripts/install-guard.mjs:129
    writeFileSync(file, source, 'utf8');
  • 读取本地文件
    scripts/lib.mjs:57
    return JSON.parse(readFileSync(file, 'utf8'));
  • 读取本地文件
    scripts/lib.mjs:83
    return existsSync(join(dir, 'package.json')) ? safeRealpath(dir) : undefined;
  • 读取本地文件
    scripts/lib.mjs:113
    out.entryFile = existsSync(file) ? file : null;
  • 读取本地文件
    scripts/lib.mjs:117
    text = readFileSync(out.entryFile).subarray(0, 512 * 1024).toString('utf8');
  • 读取本地文件
    scripts/lib.mjs:144
    if (!existsSync(PROFILES_DIR)) return [];
  • 执行外部命令固定命令
    scripts/lib.mjs:145
    return execFileSync('ls', ['-1', PROFILES_DIR], { encoding: 'utf8' })
  • 读取本地文件
    scripts/lib.mjs:149
    .filter((dir) => existsSync(join(dir, 'package.json')));
  • 执行外部命令固定命令
    scripts/lib.mjs:154
    return execFileSync('ps', ['-Ao', 'command='], { encoding: 'utf8' }).split('\n');
  • 读取本地文件
    scripts/heal.mjs:29
    if (!existsSync(patchScript)) {
  • 执行外部命令来自配置/变量
    scripts/heal.mjs:34
    const res = spawnSync(process.execPath, [patchScript, '--profile', profileName], {
  • 读取本地文件
    scripts/patch-skillhub.mjs:183
    return candidates.find((p) => existsSync(p));
  • 读取本地文件
    scripts/patch-skillhub.mjs:187
    const original = readFileSync(file, 'utf8');
  • 读取本地文件
    scripts/patch-skillhub.mjs:247
    if (!existsSync(bak)) continue;
  • 写入本地文件
    scripts/patch-skillhub.mjs:248
    copyFileSync(bak, file);
  • 写入本地文件
    scripts/patch-skillhub.mjs:287
    copyFileSync(file, `${file}.bak`);
  • 写入本地文件
    scripts/patch-skillhub.mjs:288
    writeFileSync(file, res.text, 'utf8');
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

社区实装还没有人打卡 · 装过的第一个就是你
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

DSH 技能:把插件客户端入口(侧边栏面板、市场、启动屏)从 web 配置档迁移到桌面配置档,并修复未注册的 bundle 补丁。

README

由 DeepSeek 最新模型翻译生成
dsh-plugin-entry-sync 给 DeepSeek Harness(DSH) 用的 Agent Skill,由两部分组成: - 体检(核心,长期有用):只读扫描所有配置档,回答「我装了什么、入口到底是哪个插槽、为什么没生效」。 - 迁移(一次性动作):把 web 配置档里配好的入口插件搬到 desktop 配置档,修掉「装了但入口不显示」的登记断点。 扫描 → 出计划 → 执行 → 自检(失败自动回滚)→ 重启生效 另外附带一个临时兼容层 patch-skillhub.mjs:修 @cocofhu/skillhub 在桌面端把安装目标解析成 web 的 bug。它是负债不是资产,上游修好后应当卸载(见「退役条件」)。 它解决什么问题 DSH 的每个配置档是独立依赖树,位于 ~/.dsh/profiles/<name>/: - desktop:桌面 App 用 - web:dsh web / 浏览器用 两者依赖互不相通。
展开完整文档(README 剩余 1 节)
dsh-plugin-entry-sync

给 DeepSeek Harness(DSH) 用的 Agent Skill,由两部分组成:

- 体检(核心,长期有用):只读扫描所有配置档,回答「我装了什么、入口到底是哪个插槽、为什么没生效」。
- 迁移(一次性动作):把 web 配置档里配好的入口插件搬到 desktop 配置档,修掉「装了但入口不显示」的登记断点。

扫描 → 出计划 → 执行 → 自检(失败自动回滚)→ 重启生效

另外附带一个临时兼容层 patch-skillhub.mjs:修 @cocofhu/skillhub 在桌面端把安装目标解析成 web 的 bug。它是负债不是资产,上游修好后应当卸载(见「退役条件」)。

它解决什么问题

DSH 的每个配置档是独立依赖树,位于 ~/.dsh/profiles/<name>/:

- desktop:桌面 App 用
- web:dsh web / 浏览器用

两者依赖互不相通。于是常见的三种症状:

| 症状 | 真正原因 |
| 插件在浏览器能用,桌面端没有 | 插件只装在 web 配置档 |
| 装了插件,重启后什么也没多 | 依赖装了,但没登记 dsh.profile.bundles,插件根本没加载 |
| 桌面端插件广场显示的是 web 的已装列表,装了不生效 | @cocofhu/skillhub 读不到宿主进程的 DSH_PROFILE,回退到 web |

本 skill 全部覆盖,包括第三个的补丁。

快速开始

1. 只读扫描,看清楚每个 profile 装了什么、入口有没有生效
node scripts/scan.mjs

2. 先修登记(纯改 package.json,App 开着也能跑)
node scripts/apply.mjs --yes --bundles-only

3. 需要安装新插件时,退出 App 后执行
node scripts/apply.mjs --yes

4. 修 skillhub 装到 web profile 的 bug
node scripts/patch-skillhub.mjs

然后完全退出 DeepSeek Harness(菜单栏 → 退出,不是关窗口)再打开,新入口才生效。

命令一览

scan.mjs

只读,不写任何文件。

node scripts/scan.mjs # 人类可读表格 + 迁移计划
node scripts/scan.mjs --json # 机器可读
node scripts/scan.mjs --source web --target desktop

输出每个插件的:

- 入口:侧栏入口 / 客户端·<插槽名> / 客户端·未检出插槽 / 无客户端 插槽名是从 slots.inject(...) / slots.register(...) 的实参里取的,不是全文搜点号字符串——否则 main.js、settings.get 会被误判成入口。
- 登记状态:已登记 / ⚠未登记bundle / 非bundle
- 风险等级:正常 / 低 / 注意 / 高危
- 迁移动作:安装到目标 / 补登记 bundle / 跳过高危

apply.mjs

默认 dry-run,必须 --yes 才写入。

| 参数 | 作用 |
| --yes | 真正执行 |
| --dry-run | 只打印计划 |
| --bundles-only | 只修 dsh.profile.bundles,不碰 node_modules |
| --install-only | 只装插件,不改 bundles |
| --source / --target | 指定迁移方向(默认「插件最多的另一个配置档 → 当前运行配置档」) |

执行前自动备份到 <profile>/.dsh-entry-sync-backup/<时间戳>/。

执行流程(--yes 之后):

1. 预检:对每个待装插件先跑 pnpm add <spec> --lockfile-only,解析不过就地收手,不碰 node_modules
2. 真装 + 补登记 bundle
3. 自检:每个依赖必须能解析到、每个 bundle 的 patch 文件必须存在、登记项必须在 dependencies 里
4. 失败自动回滚:还原 package.json / pnpm-lock.yaml / cordis.patch.yml,再 pnpm install --frozen-lockfile 把 node_modules 带回旧状态,退出码非 0

为什么要自动回滚:配置档写坏之后,唯一的修复入口(桌面 App)可能起不来,「已备份,请手动 cp」对新手等于没有备份。

成功时会打印「重启后应看到」清单(哪个插件、哪个插槽),对不上就说明没生效。

patch-skillhub.mjs

node scripts/patch-skillhub.mjs --dry-run # 预览
node scripts/patch-skillhub.mjs # 修补所有装了 skillhub 的 profile
node scripts/patch-skillhub.mjs --revert # 从 .bak 还原

补丁做三件事:

1. activeProfile() 不再只看 DSH_PROFILE,增加「本模块所在配置档目录」和「宿主 argv」两条来源
2. webProfileDir() 同步修正,插件广场的「已装列表」才对得上当前配置档
3. 安装链路:解除 runDshPlugin() 的「仅支持 web」守卫;desktop 目标改走 App 内置 pnpm(ELECTRON_RUN_AS_NODE=1),直接装进 profile 目录,装完自动补登记漏掉的 dsh.profile.bundles——因为 dsh plugin --profile desktop 会被命令行硬拒绝,转发命令行这条路走不通

从上游原版 0.2.16 即可直接打,也能从旧 v1 补丁升级;重复执行靠 Local patch v2 标记跳过。

验证(清空环境变量后仍解析为 desktop 才算成功):

env -u DSH_PROFILE -u DSH_PROFILE_DIR node --input-type=module -e "
const m = await import('file://' + process.env.HOME + '/.dsh/profiles/desktop/node_modules/@cocofhu/skillhub/lib/dsh-cli.js');
console.log(m.activeProfile(), m.webProfileDir(), m.desktopToolchain()?.file);"

pnpm 只要动过该 profile 的 node_modules(升级、重装依赖),补丁就会被覆盖。

install-guard.mjs

把上面那次「重跑」变成自动的:

node scripts/install-guard.mjs # 装到当前运行 profile
node scripts/install-guard.mjs --dry-run # 只看会写什么
node scripts/install-guard.mjs --remove # 卸载

它往 profile 根目录写一个 .pnpmfile.cjs,在 hooks.afterAllResolved 里调用 scripts/heal.mjs → patch-skillhub.mjs。pnpm 每次解析依赖都会触发,补丁被覆盖后自动打回来;任何异常都吞掉、exit 0,绝不阻断安装。

为什么不是 package.json 的 postinstall:实测 pnpm 11 根本不执行 root 项目自己的 preinstall/install/postinstall/prepare(pnpm add、pnpm install 都不跑),只有 .pnpmfile.cjs 会触发。

装好后 pnpm-lock.yaml 会多一行 pnpmfileChecksum,属于正常现象。若 profile 里已有你自己的 .pnpmfile.cjs,脚本拒绝覆盖(exit 2)并打印手动接入代码。

退役条件

这套补丁存在的唯一理由是上游 bug。满足任意一条就该卸掉,而不是继续维护:

- skillhub 自己的「已安装」列表能正确显示当前 profile(activeProfile 不再是写死的 web)
- 官方在桌面端修复了 dsh plugin --profile desktop 的拒绝行为
- 升到 0.3.x 后 patch-skillhub.mjs 报「结构不匹配」(说明源码已变,补丁不该盲目套)

退役步骤:

node scripts/install-guard.mjs --remove # 摘掉自愈钩子
node scripts/patch-skillhub.mjs --revert # 还原 .bak
rm -rf <profile>/node_modules/@cocofhu/skillhub # 让 pnpm 重装原版(可选)

heal.mjs 在上游自行修复后会主动提示这一点(补丁不再匹配时输出提示,而不是静默改坏文件)。

安全设计

- 高危插件拦截:references/risky-plugins.json 记录 dsh-purge 等破甲/越狱类插件(会替换模型提示词绕过安全策略),扫描时标红、迁移时自动跳过,并要求 Agent 明确提示用户「新手不要装」。
- 构建脚本最小放行:遇到 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 时,只把该 spec 的精确键写进 pnpm-workspace.yaml 的 allowBuilds,不会开 dangerouslyAllowAllBuilds。
- 只动配置文件:不删除依赖、不改 cordis.patch.yml、不碰已有插件的源码。
- 失败自动回滚:预检(--lockfile-only)→ 安装 → 自检,任一步失败即还原三个配置文件并同步回旧 lockfile,不会把 profile 留在半坏状态。
- 诚实报告:入口写 客户端·未检出插槽 而不是假装「没有入口」;自检失败直接报错,不吞。

实现要点(踩过的坑)

| 坑 | 处理 |
| dsh plugin --profile desktop 被拒:profile 由 Electron 独占 | desktop 只能由 App 宿主管理 → 用 App 内 plugin_manager,或直接对 profile 目录跑 pnpm |
| 系统 pnpm 10 与 App 内置 pnpm 11 混用触发 ERR_PNPM_PUBLIC_HOIST_PATTERN_DIFF | 自动改用 App 内置 pnpm:Electron 二进制 + ELECTRON_RUN_AS_NODE=1 跑 pnpm.mjs |
| 宿主进程没有 DSH_PROFILE,但工具 shell 有 | 不能相信环境变量,改用 import.meta.url 定位 profile(见上方补丁) |
| dsh.client.platform: "web" 是否意味着只能装 web profile | 不是。桌面端同样是 Web UI 内核,可以迁移 |
| 哪些插件值得迁 | 有 dsh.client 才有入口;只有 dsh.bundle 的属于后台能力,没有界面 |
| 只搜 sidebar.panellist 会把设置页/工具条入口误报成「无入口」 | 改成从 slots.inject(...) / slots.register(...) 实参取插槽名 |
| 全文搜点号字符串又会把 main.js、settings.get 当成入口 | 只在注册调用的实参位置取,并过滤 .js/.css 这类文件名 |
| 写坏 profile 后 App 起不来,用户没有自救入口 | 预检 + 自检 + 失败自动回滚(见「执行流程」) |
| App 装在非默认路径 / Windows | DSH_APP_PATH 指向 app 根目录即可覆盖探测顺序 |

测试

无第三方依赖,用 Node 内置 test runner:

node --test test/lib.test.mjs

覆盖:插槽探测(含误报回归)、入口分级、断点识别、高危判定、迁移计划生成。

作为 Agent Skill 安装

git clone https://github.com/hhse/dsh-plugin-entry-sync.git \
~/.dsh/skills/dsh-plugin-entry-sync

之后 Agent 会在「插件在 web 能用、桌面端没有」「统一配置插件入口」「桌面端没有侧栏/市场/启动页」这类请求下自动加载它。

环境要求

- DeepSeek Harness 桌面版。macOS 默认路径 /Applications/DeepSeek Harness.app 会被自动找到;其他位置(含 Windows)用 DSH_APP_PATH 指过去
- Node.js ≥ 20(用 node: 前缀的内置模块与 ESM)
- 无第三方依赖

已知限制

- Windows 未在实机上验证。代码层面已经处理了三处最容易踩的地方:profiles/ 的 \ 与 / 两种分隔符、DeepSeek Harness.exe 的进程识别、%LOCALAPPDATA%\Programs 与 %PROGRAMFILES% 两个默认安装位置(都可以用 DSH_APP_PATH 覆盖)。逻辑有测试覆盖,但没有真机跑过,遇到问题请开 issue。
- 高危插件名单是静态表(references/risky-plugins.json),会随时间失效;它只用于提示,不阻塞你手动安装。

License

MIT
预览分享卡片长什么样 ↓hhse/dsh-plugin-entry-sync
分享 · DeepSeek Harness Hub
hhse/dsh-plugin-entry-sync

扫描各配置档插件入口,迁移并修复未生效的登记

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