npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kyorakuyk/dsh-plugin-manager

v0.1.5

Published

Web UI plugin hot lifecycle for DeepSeek Harness: hot install / uninstall / update / enable-disable of profile plugin bundles without restart. Out-of-tree dual-face bundle: host gateway + browser settings tab over a plugin-owned HTTP transport.

Readme

dsh-hotreload-plugin-manager

中文 | English

一个树外(out-of-tree)DeepSeek Harness 插件:在运行中的实例上直接完成插件的热安装、热卸载、禁用/启用——无需重启, 并提供 Web 设置页标签页:

  • 🔥 热安装:按 spec(npm 包名 / git URL / github:owner/repo / file: 路径) 安装插件 bundle,立即热生效于运行中的 dsh web,且重启后保留。
  • 🔥 热卸载:移除 bundle——行与 fiber 即刻卸载、依赖被清理、陈旧的 bundle 层条目一并移除。
  • 🔥 禁用 / 启用:在 UI 上一键切换已安装 bundle 的条目开关,状态持久化且立即生效。
  • 热更新:热替换 bundle 的补丁行并实时重放配置(模块代码变更按 Node 进程内 缓存特性仍需重启)。
  • 零内核改动:纯树外 bundle,dsh checkout 完全不被触碰。
  • 双面包:一个 npm 包同时是 host 网关与浏览器设置标签页。

架构

┌────────────────────────────── 浏览器(React)──────────────────────────────┐
│  设置 → 插件 → "插件管理"标签页(槽位 settings.plugins.tab)                 │
│        │  fetch('/plugin-manager/*')                                      │
└────────┼──────────────────────────────────────────────────────────────────┘
         ▼
┌──────────────────────────── dsh web 进程 ──────────────────────────────────┐
│  webserver 路由(node:http)──▶ manager 核心                               │
│    ① pnpm add/remove(profile 内的真实依赖)                               │
│    ② 行写入 profile 用户补丁层(cordis.patch.yml)                         │
│    ③ 根 Include entry.update → Loader 挂载/卸载 fiber                      │
│  网关:TypertRemoteService(源码模式反射,无代码生成)                       │
└────────────────────────────────────────────────────────────────────────────┘

包是双面的:

  • Node 半lib/index.js,tsc 编译)——PluginManagerGateway,一个注册为 pluginManagerTypertRemoteService。api-gateway 可通过源码模式反射 分发它的 @Remote 方法(无需生成产物);同时它在 webServer 服务上注册浏览器 HTTP 传输。
  • 浏览器半lib/client.js,esbuild 打包)——注册进 settings.plugins.tab 槽位(id manager)的 Web 设置标签页,由 dsh-client-modules 发现,并以普通 模块表 bundle(window.__ModuleLoader__.load)形式服务。

工作原理

全部复用 launcher 已有的机制,因此插件不需要对 dsh checkout 做任何改动:

  1. 安装:在 profile 目录执行 pnpm add <spec>。包成为真实依赖(进入 profile 的 node_modulespackage.json,pnpm 不会剪除它)。管理器读取 该 bundle 的 dsh.bundle.patch 行,写入 profile 的用户补丁层 (cordis.patch.yml)。
  2. 热应用:直接对根 Include 条目调用 entry.update(携带新补丁列表)—— 与 launcher 用户补丁 watcher 相同的调用。管理器自己应用自己的写入,因为文件 watcher 可能漏掉紧跟其后的写入事件。
  3. 重启持久:用户补丁层在启动时被重新组合,因此已安装 bundle 在重启后保留, 且不触碰 dsh.profile.bundles(也就不会双组合行)。
  4. 卸载 / 更新:移除或替换行,并执行 pnpm remove / pnpm update (registry 包)或 remove + re-add(file:/link:——pnpm 的增量重拷不会 物化新增文件)。
  5. 启停:写入用户层禁用行({ id, disabled }),按 id 覆盖 bundle 行而不 产生重复。

依赖语义透传给 pnpm。 管理器不解释被安装 bundle 的 manifest: optionalDependencies 与可选 peer 的行为完全遵循 pnpm 的定义(失败的可选 依赖被跳过、安装继续;profile 使用 autoInstallPeers: false,peer 从不被 自动安装,运行时从 dsh 安装闭包解析)。插件若 import 非闭包 peer,其可用性 由 bundle 作者负责。

浏览器半通过插件自有的同源 HTTP 传输/plugin-manager/*)与 host 通信, 而不是 Typert remote 管线:npm 上的 dsh-api-gateway 包装不了(其若干依赖 dsh-type-metadsh-compact 等未发布),树外无法挂载 client contribution。 host 网关仍注册 Typert 绑定,供进程内消费者使用。

可安装的插件(什么能装)

唯一硬性要求:一个 npm 包,其 package.json 声明 "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }(标准 DSH 插件 bundle)。满足即装,插件内部形态不限。

来源形式(spec 白名单,底层透传 pnpm add,pnpm 能解析的规范基本都能用):

  • npm 包名(可带版本):@kyorakuyk/dsh-plugin-manager@scope/name@^1.0.0
  • git URL:https://github.com/x/y.gitgit+ssh://…
  • GitHub 简写:github:owner/repoowner/repo
  • 本地路径:file:C:/path/to/bundlelink:…(开发/离线最常用)

插件内部可以是什么(补丁行由根 Include 的插值机制统一处理):

  • Host 插件:普通 Cordis 插件、Service、工具注册
  • 浏览器 UI 插件:双面 dsh.client 包(浏览器半经 dsh-client-modules 可用)
  • skill 提供方 / MCP 适配器:挂 dsh-skill-filesystemdsh-mcp-client 的 bundle
  • 补丁行含 insert / id-targeted 覆盖 / 禁用行 / group / !!js 表达式
  • optionalDependencies 的 bundle(缺失的可选依赖被跳过、安装继续——已实测)

装不了的(会明确拒绝)

  • dsh.bundle.patch 的普通库 → 拒绝并自动 pnpm remove 回滚,不留残渣
  • .dsh-plugin / config.yaml 安装格式 → 不支持(DSH 核心已整体移除该路径)
  • 补丁行 id 与用户层或其他 bundle 冲突 → 拒绝安装(防双组合)
  • git 规格装完无法识别包名 → 明确报错 "could not resolve the installed package name"

边界提醒

  • 非闭包 peer:bundle 若 import 既不在 profile 也不在 dsh 安装闭包里的 peer,运行时挂——这是 bundle 作者的责任,管理器不干预
  • 模块代码级更新需重启(Node 进程内缓存,见"已知限制")
  • 管理器与 dsh plugin CLI 不要对同一 bundle 混用(卸载会自动清理 CLI 残留的 bundle 层条目)

快速开始(其他用户)

前置条件:Node ≥ 22、pnpm ≥ 10、已安装 dsh 并初始化过目标 profile。

① 直接从 GitHub 安装(推荐)——无需克隆本仓库:

dsh plugin --profile web add "github:kyorakuyk/dsh-hotreload-plugin-manager"
# 或完整 git URL:
# dsh plugin --profile web add "https://github.com/kyorakuyk/dsh-hotreload-plugin-manager.git"

② 或者克隆后本地安装

git clone https://github.com/kyorakuyk/dsh-hotreload-plugin-manager.git
dsh plugin --profile web add "file:$(pwd)/dsh-hotreload-plugin-manager"

③ 或者从 npm 安装(发布后可用):

dsh plugin --profile web add "@kyorakuyk/dsh-plugin-manager"

装完后必须重启 dsh web 实例(新 bundle 需要重启才挂载;仓库已包含构建产物 lib/,无需先构建),浏览器硬刷新一次(Ctrl+Shift+R):

dsh --profile web

然后打开 Web UI → 设置 → 插件 → 插件管理,按 spec 安装其他插件——npm 包名、 git URL、github:owner/repo、或 file:/link: 路径均可。

npm 已发布:dsh plugin --profile web add "@kyorakuyk/dsh-plugin-manager"

Web UI

设置 → 插件 → 插件管理

  • 安装输入框(支持上述 spec 形式);
  • 已安装列表,每个包带 禁用/启用更新卸载 按钮(禁用后显示 "(已禁用)",按钮切换为"启用");
  • 手动 刷新 按钮(列表实时反映热变更,无需整页刷新);
  • 操作结果内联展示(失败时包含 pnpm 输出尾部)。

HTTP API

| 方法 | 路径 | Body | 结果 | |---|---|---|---| | GET | /plugin-manager/list | — | { ok, value: Bundle[] } | | POST | /plugin-manager/install | { spec } | { ok, message, exitCode?, tail? } | | POST | /plugin-manager/uninstall | { packageName } | 同上 | | POST | /plugin-manager/update | { packageName } | 同上 | | POST | /plugin-manager/setEnabled | { entryId, enabled } | 同上 |

Bundle 形如 { packageName, version?, spec, rowIds, disabled }

开发

pnpm install
pnpm build        # tsc(Node 半)+ esbuild(浏览器 bundle)
pnpm test         # vitest:REAL-composition / HTTP / bundle 契约共 22 个测试
pnpm typecheck

发布(npm Trusted Publishing)

发布走 GitHub Actions OIDC 信任发布——无需 token、无需 OTP/恢复码

npm version 0.1.5 --no-git-tag-version   # 升版本
git add package.json pnpm-lock.yaml && git commit -m "pkg: bump to 0.1.5"
git push
git tag v0.1.5 && git push origin v0.1.5 # 触发 .github/workflows/publish.yml

workflow 会:typecheck → build → test → npm publish --provenance(OIDC) → 自动创建 GitHub Release。发布失败可重跑:

gh workflow run publish.yml --ref main -f publish_ref=refs/tags/v0.1.5

前置:npm 账号需把本仓库 + publish.yml 注册为该包的 Trusted Publisher (npmjs.com → 包设置 → Trusted Publisher)。

目录结构:

src/                 host 半(tsc → lib/*.js)
├── index.ts         PluginManagerGateway(TypertRemoteService + @Remote)
├── manager.ts       核心生命周期操作
├── http.ts          webserver 路由处理器
└── spec|pnpm|patch-layer|state|types.ts
src/client/index.tsx 浏览器半(esbuild → lib/client.js)
scripts/build-client.mjs  esbuild 配置(模块表闭包契约)
tests/               vitest 套件 + fixtures/ 测试夹具

依赖按"运行时共享安装内单一 Cordis 实例"分类:@deepseek-ai/* 包为 peerDependencies(从 dsh 安装闭包解析),js-yaml 是唯一运行时依赖,构建与 测试工具放在 devDependencies

已知限制与待办

  • 远程信任/plugin-manager/* 路由对任何能触达服务器的人都开放。Web 面 默认仅回环绑定(launcher 拒绝 --host 0.0.0.0),但暴露到局域网时不能依赖 /api 信任围栏的保护;按可信权威校验为待办。
  • 与 CLI 混用:管理器把 bundle 装为真实依赖,但不加入 dsh.profile.bundles (行在用户补丁层)。之后若再运行 dsh plugin CLI,reconcile 会把声明 dsh.bundle 的依赖并入 bundle 层,下一次启动时双组合这些行。每个 bundle 只用 一种机制(卸载时会自动清理 CLI 残留的 bundle 层条目)。
  • 无法解析包名的 spec:安装 git URL 时若无法在 profile 的 node_modules 中识别出包名,会以 "could not resolve the installed package name" 明确失败。
  • 行 id 冲突在安装时被拒绝:补丁行与用户层(或其他 bundle)冲突的 bundle 不会被安装。
  • 更新热应用的是组合与配置,不是模块代码。Node 会在进程内缓存已解析的模块 与包 exports,因此 代码exports map 变更的版本需要重启才生效;manager 的 update 热替换补丁行(行集合与行配置),依赖本身经 pnpm update (registry)或 remove + re-add(file:/link:)刷新。
  • 启停按行 id 定位条目;非补丁行 id 的生成式 Loader id 无法被切换。
  • pnpm ≥ 11 的 supply-chain 策略:默认 minimumReleaseAge(24h)会拒绝 "刚发布"的包(ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION)。管理器执行 pnpm 时统一附加 --config.minimumReleaseAge=0 跳过冷却(安装是用户主动 操作),并把违规包的 scope 追加到 profile pnpm-workspace.yamlminimumReleaseAgeExclude(例如 @kyorakuyk/*)作为第二道防线。带 构建脚本的 git 依赖(prepare 等)会被 allowBuilds 白名单拦截 (ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED / ERR_PNPM_IGNORED_BUILDS): 管理器会从 pnpm 输出提取其要求的精确 spec包名@git+url#sha), 自动写入 allowBuilds 并重试一次。手动处理时把对应 scope/spec 加入 pnpm-workspace.yaml 对应区块即可。
  • 暂无插件市场:安装是 spec 驱动的;浏览与精选发现为待办。
  • 向运行中的 profile 部署 bundle 变更需要重启。启动清单里的 bundle rev 与 host 半都在进程激活时冻结:把变更后的代码重装进已运行的 profile,服务端按 请求读取新 client bundle,但 host 仍运行旧逻辑(且启动图保持旧 rev);浏览器 也可能在旧 URL 下缓存旧 bundle。重装 host 代码后请重启实例,并硬刷新一次 浏览器;管理器标签页能容忍错配窗口(对缺失的行状态做了防御)。
  • in-box 的"插件清单"标签页是即时快照。它在首次挂载时拉取一次 Loader 快照,之后保持挂载(隐藏),因此不会反映在"插件管理"里做的热生命周期变更 ——需要刷新页面。管理器标签页本身是实时的(每次操作后自动重拉,并有手动 刷新按钮)。

Model Experience

无:管理器不注册任何 prompt、工具、消息或 provider 请求。

KV Cache 影响

无;本包从不组装模型输入。

License

MIT