- Home
- Plugin
- Sviluppo e strumenti per plugin
- dsh-onshelf
dsh-onshelf
frogflaying/dsh-onshelf
Onshelf for DeepSeek Harness. It registers 1 agent tool (onshelf).
Installazione
dsh plugin --profile web add github:frogflaying/dsh-onshelfREADME
上架台 · dsh-onshelf
把本地 DSH 插件一路送到 awesome-dsh-plugin: 扫描 → 体检 → 生成材料 → 上传截图 → 开 PR。
针对的是没有技术背景、只有 GitHub 账号的作者。五个步骤里,前三个完全不需要 token、不联网。
它解决什么
contributing.md 里的规则全写在散文里,没人读。这个插件把每一条变成检查项,
把"评审清单"变成"引导表单":
| 步骤 | 做什么 | 需要 token |
|---|---|---|
| ① 扫描 | 找出声明了 dsh.bundle 的本地插件目录;把只声明 dsh.client 的列为近失(CI 最常见的拒绝原因) | 否 |
| ② 体检 | 按 contributing.md 逐条检查,能自动修的就修(补 dsh.bundle、生成 cordis.patch.yml、官方包挪到 peerDependencies、修预发布范围陷阱) | 否 |
| ③ 条目 | 条目 YAML、中英双语描述、分类、screenshots.json 声明、PR 描述(截图本体在 ④) | 否 |
| ③.5 建仓上传 | 建 GitHub 仓库 + 把整个插件目录推上去,并把仓库地址填回条目 | 是 |
| ④ 截图 | 添加(粘贴/拖入/选择)、预览、上传截图到你自己的插件仓库 | 是 |
| ⑤ 提交 | fork → 建分支 → 提交 → 开 PR(已收录的插件走「更新」) | 是 |
能代做的就不再让人做(0.5.0)
之前 CI 有两条门槛,插件只是告诉你去 GitHub 手动处理:
| 门槛 | 之前 | 现在 |
|---|---|---|
缺 dsh-plugin topic | 让你去仓库页 About → Topics → 手输 → 保存 | 一个按钮直接加上,提交时也会自动补 |
| 仓库不满 1 天 | 只能等 | 仍然只能等,但会算出「还差约 9 小时」 |
topic 是一次 API 调用(PUT /repos/{owner}/{repo}/topics),用建仓库时已经申请的
Administration 权限就能做。让人为了一个勾选手动跑一趟 GitHub,正是这个插件应该消灭的那类步骤。
两条原则没变:只改自己仓库(token 属于别人时直接拒绝),以及改了就说
(✓ 已加上 topic: dsh-plugin)。
推送前检查本机痕迹(0.5.0)
插件 README 在一台机器上写、在几千台机器上读,所以「安装路径」和「这台机器上有 267 个程序」
这类内容对读者没用,而 C:\Users\<你的用户名> 往往是真实姓名。
上传前会扫一遍将要推送的文本文件,命中三类:
home-path—— 硬编码的家目录,含用户名的那个absolute-path—— 其它X:\...绝对路径local-figure—— 「这台机器上有 N 个」这类只对本机成立的数字
它不阻止上传,只是在计划里列出来(文件:行 + 类型),让你自己判断——有些本地路径是开发说明里 合理存在的。二进制文件跳过,每个文件只读前 256 KB。
这一版之前的真实案例:README.md 里写了 C:\Users\<用户名>\.dsh\... 的 node 路径、
D:\DeepSeek\harness\... 的安装命令、以及「这台机器上是 267 个」。都改成了通用写法。
「PR 已创建」不等于「能过 CI」(0.4.1)
第一次真的提到上游 PR 时,界面显示的是这样一个绿框:
PR 已创建:https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/8375 ! 仓库创建不满 1 天(0.66 天),CI 会拒绝。 ! 仓库缺少 dsh-plugin topic,CI 会拒绝。
PR 确实建成了,但绿色成功框里挂着两行红叹号,看起来像失败。这是展示的问题,不是提交的问题。
现在拆成两块:
- 绿框只说成功,并明写「这是成功,不是报错」;
- 红框单独说明「这个 PR 现在会被 CI 拒绝」,指出原因不在插件代码,而是两条仓库条件,并把它们列成编号清单。
两条条件的处理方式完全不同,所以警告文案也写清楚了:
| 条件 | 怎么修 | 多久生效 |
|---|---|---|
| 仓库不满 1 天 | 等 | 到点自动满足;还差几小时会算给你 |
缺 dsh-plugin topic | 仓库页右侧 About 齿轮 → Topics → 加 dsh-plugin | 立刻 |
两条都满足后,再点一次「创建 PR」推同一个分支,CI 就会重跑。0.4.0 起分支名稳定复用,所以不会变成第二个 PR。
界面上还有个「重新检查仓库状态」按钮,直接把当前年龄和 topic 状态读给你看,不用猜。
已收录之后再更新(0.4.0)
这里原来有个真实的坑:⑤ 遇到「条目已存在」直接拒绝,并让你自己去改,但没有告诉你该怎么改。而最常见的情形恰恰是「我发了新版本,要更新描述」。
现在分三种情况处理:
| 你的情况 | ⑤ 会怎么做 |
|---|---|
| 第一次投稿 | 新增 data/plugins/<owner>__<repo>.yml,开 PR |
| 已在列表里、内容有变化 | 判为更新:改现有文件、开 PR,标题是 Update …,正文列出逐行改动 |
| 已在列表里、内容没变 | 直接拦住,不给你开一个空 PR |
| 上次的 PR 还开着 | 找到那个分支,推送上去让 PR 自动更新,不新开第二个 |
两个配套改动:
- 分支名不再带版本号。以前是
add-owner__repo-v0.1.0,每发一版就是一个新分支 + 新 PR,对维护者是噪音。现在固定add-owner__repo,同一个插件永远复用同一分支。 - 旧版本留下的
-v<版本>分支仍然能被找到(升级后第一次更新不会丢失之前的 PR)。
「先演练一遍」会明确告诉你这次是 新增条目 还是 更新已有条目,以及改了几行。
③ 那两个产物是干嘛的
③ 点「生成投稿材料」会出现两块东西,它们不是表单,不需要你填:
| 产物 | 是什么 | 谁用它 |
|---|---|---|
条目文件 data/plugins/owner__repo.yml | 就是这个插件在 awesome 列表里的那一行 | ⑤ 把它提交进 awesome-dsh-plugin 仓库 |
| PR 描述 | 那个 PR 的正文 | ⑤ 自动填进 PR 的 body |
所以 ③ 只是让你先看一眼将要提交什么。点「复制」有两个用途: 确认内容没问题;或者你不想提供 token,就照着它自己在 GitHub 上提 PR——效果完全一样。
截图可以直接粘贴 / 拖进来(0.3.0)
原来只能手写相对路径:你得先把图存到 assets/、找到它、再照着敲一遍。这对小白是多出来的一步。
现在「截图路径」上方有一个拖放区,三种方式都行:
- 粘贴:鼠标点一下那个框,
Ctrl+V - 拖入:把图片文件拖进去
- 选择:点「选择图片」,或点整个区域
图片会由宿主写进插件的 assets/ 目录,并把路径自动填进草稿。已加的图会显示缩略图和删除按钮。
几个实现上的取舍:
- 浏览器给的是字节,不是路径——粘贴的截图在磁盘上根本不存在,所以必须由宿主写文件。
- 文件名保留你的(
main-window.png就是main-window.png),重名才加-1; 剪贴板那种image.png会改成screenshot-20261020103559.png,否则第二次粘贴就覆盖了第一次。 - 扩展名以真实文件名为准,没有才看 MIME type。
- 路径不能跳出插件目录,也不能写进 node_modules——
../outside这种直接 400 拒绝, 而不是"清理掉..然后写到别处"。这个边界是上一轮实现时漏掉的,测试抓到了。 - 缩略图走插件自己的
/shot路由:图片在插件的目录里,不在 web 根下,不这么走浏览器读不到。
0.3.1:③ 和 ④ 曾经各说各话
在 ③ 贴了截图,④ 却提示「没有截图可上传」。原因是同一件事有两个答案:
- ③ 把截图写进草稿(
~/.dsh/onshelf/drafts.json); - ④ 读的是插件目录里的
screenshots.json。
而 screenshots.json 之前只在点「生成投稿材料」时可能产生,贴图那一刻并没有写。于是草稿里有、磁盘上没有,④ 当然看不到。
现在草稿是唯一事实来源,任何改动都会同步写出 screenshots.json:
- 贴图 / 拖图 → 立刻写;
- 手动编辑「截图路径」→ 立刻写;
- 打开 ③ 时如果发现旧草稿有图而磁盘没声明 → 自动补齐(老用户不需要重做)。
④ 也跟着改了:
- 列出将要上传的截图缩略图和数量,不再是黑箱;
- 没有截图时直接给出粘贴/拖放区,不用退回 ③;
- 一张截图都没有时上传按钮是禁用的,而不是点下去才报错。
分类为什么是 ui(0.2.4)
两件事要分开看:
- 下拉框里的值可能是你早先手选的。手动选过的分类不会被自动推荐覆盖(这是对的,否则你改一下描述推荐就把你的选择冲掉了)。0.2.4 起界面会明说这一点,并给一个「改用推荐的分类」按钮。
- 自动推荐本身也错了。早先的规则是「带浏览器半身 ⇒ ui」,于是这个操作 Windows 桌面的插件被推荐成了
ui。0.2.4 起先看功能信号(desktop / file / folder / process…),dsh-desktop-organizer现在推荐tools。
顺带修了一个同类问题:把分类或描述清空时,?? 会保留空字符串而不回落到草稿,界面就剩一个空下拉框。现在空值会回落到推荐/草稿。
contributing.md的原话:分类选不准不会被打回,维护者会直接改。所以这两个都合规,tools更贴合。
建仓上传(0.2.0)
第一版把你自己的代码当成"你的事":让你去 GitHub 建仓库、推代码,再回来填仓库地址。 这正是"只有 GitHub 账号、不会用 git"的人做不到的一步,也是这个插件的立身之本。现在它做完了:
- 仓库不存在 → 按你给的名字新建(默认公开,因为 awesome 列表只收公开仓库);
- 仓库存在但为空 → 直接推;
- 仓库已有提交 → 默认拒绝,必须你明确勾选覆盖,不会悄悄替换你的历史;
- 先写
repository字段再推送,所以仓库的第一个提交里就有它; - 推完把仓库地址填回 ③ 的条目,
repository-missing这条警告随之消失。
推什么:整个插件目录,但 node_modules、.git、.dsh、各种备份与系统垃圾不传
(.github 保留),并且遵守插件自己的 .gitignore:运行产物(会话快照、缓存、本机配置覆盖)
通常只写在 .gitignore 里,那正是作者本人说"这些不是源码"的地方,工具不替你绕过它。
有上限:单文件 5 MB、总计 20 MB、最多 400 个文件——点错到放视频的文件夹也不会把仓库塞爆。
先看再传:点「先看一眼要上传什么」会列出仓库名、公开/私有、文件数、顶层目录、以及被跳过的文件,
按 .gitignore 排除掉的文件也会算在一起告诉你,确认后才真正写入。
用到的 GitHub 能力:Contents: read/write(推代码)+ Administration: read/write(建仓库)。
0.2.2 修的两个问题
空仓库被当成"有历史"(真 bug,用户实际踩到)。
GitHub 对没有提交的仓库,从 git-data 端点返回的是 409 "Git Repository is empty",不是 404。
原来只把 404 当空仓库,于是新建的仓库被判定为"已有提交",弹出一个不该有的覆盖确认,
最终提交里 parents: [null] 被 API 拒绝——仓库一直是空的,界面也没说清楚。
现在 409/404 都算空,并且"是不是根提交"改成看有没有 commit sha,而不是只看 empty 标志,
这样即使判断错了也不会生成 parents: [null]。
上传没有任何反馈(体验 bug)。 上传是一个文件一个请求,十几秒里按钮毫无变化,用户无法判断成功还是失败。 现在改成后台任务:
- 按钮下方出现进度条和「已上传 N / M 个文件」;
- 阶段文字会变:检查仓库 → 创建仓库 → 写入 repository 字段 → 读取文件 → 上传;
- 失败信息就出现在点下去的那个按钮下面,不再是页面顶部一条容易被忽略的横幅;
- 成功会给两个可点的链接:仓库地址和这次提交的 commit;
- 顺带把 blob 上传改成 4 并发,14 个文件的插件快了不少。
0.2.3:空仓库根本不能直接写 blob
0.2.2 只修了"读取 ref 时把 409 当空仓库",但写入路径同样会撞 409:
在一个还没有任何提交的仓库里,/git/blobs、/git/trees、/git/commits、/git/refs
全部返回 409 Git Repository is empty。所以真实的报错是:
✗ 上传 index.js 失败:Git Repository is empty.
正确做法是:空仓库只能从 Contents API 进门,因为那个端点会替你提交。 现在的流程是:
- 用 Contents API 提交第一个文件(优先
package.json)——这一步同时创建了分支; - 仓库于是有了一个提交,git-data 端点开始可用;
- 其余文件走正常的 blob → tree → commit → 更新分支。
代价是两次提交而不是一次(很便宜的代价),换来的是空仓库真的能推上去。
另外 commitFiles(④ 上传截图走的路)遇到 409 时会直接说人话:
「仓库还没有任何提交,先上传一次代码再来这一步」,而不是甩出 GitHub 的原文。
三条设计原则
1. 描述只写代码里能读到的事实。
生成器只声明它在 package.json 和入口文件里实际观察到的东西(注册了几个工具、命令、UI 挂载点),
不发明能力。每条可核实的声明都会单独列出来给你确认——因为 contributing.md 说描述会被对着代码核对。
2. 不夸大,也不替你决定事实。 机械性的形状问题(缺字段、patch 文件形状不对、官方包放错依赖块)才自动修。 描述、分类、截图这些"关于插件的事实"永远只是草稿。
3. 没有 token 也能走完 ③。 第 ③ 步的产物(YAML + PR 描述)就是完整的投稿内容,复制走自己提 PR 效果完全一样。 token 只存在会话内存里,不落盘、不写日志。
体检里的每个「→」都是真按钮(0.1.3 已修)
第一版有个明显的毛病:→ 按功能自动推荐 之类是纯文本,看着像按钮、点了没反应;
而且大部分问题根本没写修复实现,所以「一键修复」按钮压根不出现。
现在每条 finding 都带一个动作,由规则决定、UI 照做,两者不可能再漂移:
| 动作类型 | 适用 | 做了什么 |
|---|---|---|
file | 缺 dsh.bundle、patch 形状不对、官方包放错依赖块、预发布范围 | 改 package.json / cordis.patch.yml,改前备份,可撤销 |
draft | 分类没选、缺英文描述、描述含营销词、缺中文描述 | 直接填进 ③ 的草稿(分类推荐、按代码生成描述、去营销词、补句号) |
manual | 缺 github topic、仓库不满 1 天、repository 字段、截图 | 只给一句"你要做什么",不假装能自动修 |
「一键修复」= 先跑 file 修复,再跑所有 draft 动作,最多三轮(填完描述可能顺带解决几条)。
返回结果会明确说"改了 N 个文件、填了 N 项,还有 M 条只能手动"。
实测一个真实插件(dsh-desktop-organizer 副本):2 个错误 → 0 个,只剩 1 条手动项。
英文描述曾经是中文(0.1.3 已修)
description.en 直接取了 package.json#description,而中文插件的那个字段是中文——
结果"英文描述"里全是中文。这是会被打回的形状。
现在按语言分流:
- 每个来源先判语言(有 CJK 就不是英文),英文行只用英文来源;
- 没有英文来源时,用代码能证明的事实拼一句:
Desktop organizer for DeepSeek Harness. It registers 1 agent tool (desktop_organize). - 这句会标
needsTranslation,界面提示"这是推导出来的,请核对后改写"——不假装它是你写的英文; - 中文行只用中文来源;没有就留空(
contributing.md说缺翻译是维护者的活)。 - 另外新增一条检查:
en字段里出现中文直接报错,并给"改成一句英文事实陈述"的按钮。
扫描目录
默认扫描本插件所在的上一级目录(本地插件通常放在一起),可以在向导里「修改扫描目录」增删。 插件不知道你的项目放在哪里,所以这个默认值只是猜测,界面上会标明它来自哪里。
两条容易踩的坑,0.1.1 已修:
link:装的本插件是 junction/symlink,readdirSync对它们报isDirectory() === false。 只按isDirectory()下行会把你自己开发的插件全部漏掉。现在会跟随链接、按真实源码路径上报、按真实路径去重、并防环。- 默认根目录曾经指向
~/.dsh/profiles/<name>,那是已安装插件的地方, 结果列表里全是别人的市场插件,你自己的一个都没有。安装目录里的第三方包会标「已安装」并排在你自己的插件后面。
扫描为什么"卡在加载中"(0.1.2 已修)
实测本机扫描 12ms、体检 3ms,代码从来都不慢。真正的原因有两个:
- 标签页永远可点,但 ②–⑤ 步在无数据时都渲染
加载中…。 没选插件时check是null,模板就把"没有数据"显示成"正在加载",看上去永远卡住。 现在每一步都有明确的空状态("先在 ① 扫描里选一个插件")。 - 一次请求说完所有事,中间没有任何反馈。现在扫描是后台任务:
/scan/start立即返回, 前端轮询/scan/status显示「已扫描目录数 / 已找到候选 / 耗时」和进度条。
另外做了三处提速:
| 改动 | 效果 |
|---|---|
只在遇到链接时 statSync,普通目录直接用 Dirent | 每个条目少一次系统调用,本机 59ms → 12ms |
| 扫描器每 64 个目录让出事件循环 | 大目录不再冻结宿主,进度请求能返回 |
重新打开设置页先读 /scan/cached | 秒开,只有冷启动才真正扫盘 |
还加了两个保护:目录数上限 20000(指向 C:\ 会返回截断结果而不是永挂),以及前端 60s 请求超时(真卡住会报错而不是无声转圈)。
安装
dsh plugin --profile desktop add link:D:/DeepSeek/harness/dsh-onshelf
重启 DSH 后,设置 → 上架台。
作为 agent 工具
模型也可以直接调用:
onshelf action="scan"
onshelf action="check" dir="D:/path/to/your-plugin"
onshelf action="fix" dir="..." dryRun=true
onshelf action="prepare" dir="..." category="tools" repoUrl="you/repo"
onshelf action="submit" dir="..." dryRun=true
结构
index.js host 半身:agent 工具 + /api/onshelf/* 路由
client.js 浏览器半身:设置页向导(无构建,仅依赖 shell 提供的 react)
lib/scan.js ① 目录扫描与 manifest 解析
lib/rules.js ② 规则引擎——contributing.md 的逐条转写
lib/check.js ② 诊断与可撤销的自动修复
lib/entry.js ③ 投稿材料生成与校验
lib/github.js ③.5④⑤ GitHub REST(建仓 / 推目录 / 截图 / fork / commit / PR),token 不落盘`nlib/upload.js ③.5 收集待上传文件(忽略规则 + 上限)
test/ 282 项测试 + 真实机器冒烟脚本
lib/rules.js 是唯一允许存放规则的地方。上游指南变了,只改这一个文件。
测试
node test/all.mjs # 全部 282 项
node test/smoke-real.mjs # 扫本机自己的插件(依赖环境,不属于测试套件)
已知限制
latest/download/的 tarball 陷阱会被检测并警告,但修不了——那是发布侧的问题。- 分类只是推荐,
contributing.md明确说选不准不会被打回,维护者会改。 - 仓库年龄 1 天门槛和
dsh-plugintopic 需要 token 才能读到真实值,没 token 时这两项不检查。 - 第 ⑤ 步创建的 PR 不会自动合并,也不是安全审查——收录不等于背书。
License
MIT
Plugin correlati
deepseek-harness
deepseek-ai/deepseek-harness
dsh-market
dsh-market/dsh-market
dsh-web (dsh-plugin-manager)
zhu1090093659/dsh-web
dsh-web
zhu1090093659/dsh-web