Files
deepseek-harness/packages/host/plugin-inventory/README.zh.md
T
Pine a53769955b feat(plugin): plugin marketplace, durable install persistence, skill manager
Add an in-app plugin marketplace fed by a remote web catalog: the host
plugin-inventory gateway gains marketplaceList/Install/Uninstall remotes,
a durable per-user install table reconciled against the actual profile, and
git/npm/tarball/bundle install paths with non-interactive git and vendored
pnpm. The Web Settings surface gains a sibling 插件市场 tab that lists the
catalog with recommended badges, repository links, and a combined sort
(recommended first, then catalog priority, then id).

Add a host + client skill manager: list local skills by direct filesystem
discovery, and install/uninstall/toggle/edit their SKILL.md from git/npm/
tarball/local sources in the writable user root.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-15 17:04:36 +08:00

6.3 KiB
Raw Blame History

@deepseek-ai/dsh-host-plugin-inventory

English | 中文

当前 Cordis Loader 树的 Host 投影,带逐插件启用/停用。PluginInventoryGateway 注册 pluginInventory 服务,并发布两个由 Typert 生成的直接 RemotepluginInventory/listpluginInventory/setEnabledlist 直接读取 ctx.loader.entries(),跳过结构性的 group 行,再按 Loader 顺序返回其余条目,并且只包含 Loader 条目 id、模块标识、有效启用状态与当前根 Fiber 阶段。

阶段为 pendingloadingactivefailedunloading;条目没有存活的根 Fiber 时则为 null。该快照刻意只表示调用当下:Loader 仍是唯一的生命周期权威,本包不拥有缓存、历史、来源模型或事件流。setEnabled 通过 ctx.loader.update 实时切换单条条目,并把显式 disabled 覆盖写进 profile 的用户补丁层,使选择在重启后保留(bundle 默认禁用的行需要 disabled: false 覆盖才能保持启用)。

每条条目带 protected 标记。src/required.ts 中的守卫默认开放,含两个可由代码编辑的名单:REQUIRED_PLUGINS(黑名单,即不可停用的承重核心——入口树、Remote RPC 主干、session 与 agent 主干)与 USER_TOGGLEABLE_PLUGINS(白名单,覆盖黑名单、对显式可开关的插件生效);未列入任何名单的插件默认可切换。随包 base bundle 的完整依赖图分类见 docs/plugin-system.mdsetEnabled 拒绝停用必需插件;apply 真正失败的插件会经 loader 自身的启动错误被拒绝,而仅仅等待依赖(PENDING)的插件会保持启用,待依赖就绪后自动激活。Web 插件列表 tab 渲染单一扁平列表,包含所有条目:每项按真实启用状态显示,可切换插件带"启用"或"停用"按钮(这样 bundle 默认禁用的插件也能重新启用),必需插件只显示只读说明。

网关还通过 availableBundles/installPlugin/installedBundles/uninstall 管理安装。availableBundles 列出 src/bundles.tsAVAILABLE_BUNDLES)中策展的离线可安装可选 bundle;该目录目前为空,直到有可选 bundle 随包——profile 的默认 bundledsh-basedsh-web-appdsh-image-recognition-bundle)是部署的一部分,非可选插件,uninstall 拒绝移除它们。installedBundles 列出 profile 中用户安装的依赖(即可卸载的那些)。uninstallpnpm remove 完整移除用户安装的依赖(同时删除依赖与它声明的 bundle 层,和安装一样受 dshAllowPluginInstall 门禁),对已组合的离线可选 bundle 仅取消组合,并拒绝内置默认 bundle。installPlugin 在可写的 profile 目录对任意 pnpm add specifier 运行 pnpm——用内置进 harness 的 pnpm,或当没有内置 pnpm(如开发 checkout)时用 PATH 上的 pnpm,只有两者都不存在才显式报错——裸 npm 包名、tarball 路径或 URL(含 GitHub archive .tar.gz)、git/GitHub 仓库地址,所有社区安装来源共用同一条路径。registry 安装受 dshAllowPluginInstall 上下文标志门禁,仅桌面启动会开启;裸包名 spec 会依次尝试 src/install.ts 中排序的 INSTALL_REGISTRIES 镜像源列表,直到其中一个成功——官方 npm 源排在最后作为保底,只有所有镜像源都不可达才报错,而 git/tarball/path spec 只运行一次、不经过镜像源。裸包名 spec 还会带上 --minimum-release-age-exclude,使刚发布的插件也能安装。

由于 pnpm 11 默认拦截依赖的构建脚本,当有脚本被拦截时安装分两阶段:首次调用把被拦截的包作为 pendingBuilds 返回而非失败,设置页 tab 以沙箱警告展示这些包供用户逐包同意,重试时把已同意的集合放在 consentBuilds 里——宿主把恰好这些包的按包 allowBuilds mappnpm ≥11)与 onlyBuiltDependencies 数组(pnpm 10)直接写进 profile 的 pnpm-workspace.yaml,然后重新安装。直接写配置而非运行 pnpm approve-builds,不依赖这些包是否处于 pnpm 的 pending 状态。当 boot 提供了 dshReloadProfile 句柄时,网关在写入后重组合运行中的树,使插件立即生效(restartRequired: false);否则安装持久化 profile 清单,需重启生效(restartRequired: true)。公开 payload 类型位于 ./typesTypert 生成由 ./typert./remote 导出的 Host 和 Client Remote 产物。

插件市场通过 marketplaceList/marketplaceInstall/marketplaceUninstall 暴露。目录放在静态网站宿主上(默认 https://deepseek.pinesound.cn/plugins/plugins.json,可用 DSH_MARKETPLACE_URL 覆盖):一个索引 JSON 列出插件,旁边每个插件的 JSON 规定安装方式(gitnpmtarballbundle)以及 pnpm specifier 和依赖名。marketplaceList 叠加持久化安装表——$DSH_HOME/plugin-marketplace/installed.json 下的一小份 JSON 文档——把每条标记为已安装;该表是判断是否已安装的权威依据。marketplaceInstall 把安装方式映射到既有安装路径(git/npm/tarball 走 registry Remotebundle 走离线组合),成功后在表中记录安装,并沿用同样的两阶段构建脚本同意流程;marketplaceUninstall 从表中解析依赖名,执行既有卸载并删除表行。每个插件的 spec URL 由固定目录基地址加插件 id 推导(绝不取自目录内容),因此恶意目录无法把应用指向任意 URL。

该服务仅供 Remote 使用,刻意不声明同进程 Cordis Context merge。Client 包通过显式的 api-remotes 组合消费它,而不导入 Host 实现。

模型体验

无,因为这个仅限 Host 的清单投影不注册提示词、工具、消息或提供方请求。

KV Cache 影响

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

已知限制与暂缓事项

  • 仅表示调用当下 —— 结果不包含持久的失败历史或订阅;只要不存在存活的根 Fiber,就会报告 null,而不区分其原因。
  • 无来源、不能增删 —— 服务不识别条目由哪个 bundle、profile 或 override 引入,也不能添加或移除插件。启用/停用持久化到 profile 的用户补丁层;profile 未挂载(任何 bundle 都没有)的行无法从这里切换。