dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
把工作区里的 DSH 插件按「干净副本」推送到各自独立的 GitHub 仓库:/git 指令按需触发,平时不注入任何提示词,token 只存本地一次。
@local/dsh-github-sync)专为 DSH 插件开发者设计:把本地工作区里的插件作为干净副本推送到各自独立的 GitHub 仓库,并给新仓库打上 dsh-plugin topic。
/git 语法,用户自然表达同步意图即可唤醒执行。github_sync_push,若未经用户确认,调用会被工具层强拦截拒绝,强制要求先跑 github_sync_plan 汇报改动,并在获得用户明确肯定后带上 confirm: true 才能执行真实推送。%USERPROFILE%.dsh\github-sync\config.json。sk- 风格密钥、Google API key、私钥文件头及 .pem, .key, .crt, .cert, .token, .secret, .auth 等敏感扩展名。文档里的占位符(如 ghp_xxxx)不会误报。.git/、node_modules/、cache/、dist/、build/、coverage/、*.status.json、*.local.json、.env*、_scratch/、_research/ 等。.gitignore 或配置了 include 白名单,一律尊重(include 白名单也不会绕过密钥扫描)。maxFileBytes = 100 MiB,正是 GitHub 的硬上限(官方原文:*GitHub blocks files larger than 100 MiB*);超过 50 MiB 按 GitHub 的规则给警告但仍可推(官方原文:*you will receive a warning from Git*)。超过上限是阻断项而非静默漏掉——静默丢文件会发布出一个悄悄坏掉的插件。密钥扫描的范围与上限一致,大文本文件无法靠体积绕过扫描。+@users.noreply.github.com:既能关联到你的头像,又不公开真实邮箱。git.userEmail(git.userName 可选)。前提是该邮箱已在 GitHub 账号里验证过:GitHub 靠邮箱归属提交,没验证过的邮箱会让提交显示成一个没有头像、点不开的陌生人。%USERPROFILE%.dsh\github-sync\mirrors,.git 保留 → 历史正常累积。git log / git blame / git revert 全部可用。fetch 并以远端 tip 为基准提交,所以别人在 GitHub 网页上的修改、或另一台克隆推上来的提交,都会被保留,你的提交落在它们上面(而不是把它们抹掉)。unchanged。LICENSE / COPYING 等文件时直接沿用。github.license 时,从 GitHub 官方接口 GET /licenses/{key} 取权威正文(不自己编),替换版权行后作为 LICENSE 写进同一个提交。正文会缓存到 licensesDir,之后不再依赖网络。[year]/[fullname],Apache-2.0 用 [yyyy],MPL-2.0 无占位符则原样保留)。package.json 的 dsh.bundle / dsh.client,或关键词含 dsh(cordis.patch.yml 同样算),并把这个判定和它的依据报出来(状态、试运行、/git 说明里都写),所以判断是可见、可反驳的,不靠猜。dsh- 前缀、dsh-plugin topic 只对判定为 DSH 插件的目标生效;普通项目不套用,也不会被打上 DSH 的标签。node_modules 一起当成待发布物。github_sync_plan / github_sync_push 的 plugins 参数也可以直接写目录路径(相对或绝对),扫描没认出来的目录照样能发布。详见下一条。plugins 参数接受三种写法,按顺序解析:插件名(仓库名 / 目录名 / 包名)→ 目录路径 → 都失败才报错。/git 推送 my-app(工作区里同名目录)和 /git 推送 ../my-app(工作区外)都能用。适合自己的普通项目、别的宿主的插件,或任何你不想为了发布而改目录名/加 DSH 标记的东西。node_modules、cache、.env、*.status.json…)、密钥扫描、单文件上限、真实提交与普通推送。唯一的差别是不套 DSH 的命名约定,也不打 `dsh-plugin` topic(除非你显式要求)。plugins(键可用请求时的原字符串 / 绝对路径 / 目录名任一):"plugins": { "my-app": { "repo": "my-app-gh", "visibility": "public" } }。~/.dsh,存着 token、会话记录、发布镜像)相重叠的目录——例如 ~、~/.dsh、~/.dsh/profiles。这是为了保护密钥,不是约定。dsh- 前缀,仓库名默认就等于那个文件夹名(文件夹 = 插件在磁盘上的身份)。所以 dsh-sticker/ → 仓库 dsh-sticker。naming: "warn"):仓库名会照文件夹名走,在 dsh-plugin 汇总里不太像同类——这是外观问题,不是数据问题,所以不值得拒绝发布。警告里会给出确切的改名建议(例如 nova-preset → dsh-nova-preset)以及改名的代价。naming: "block";naming: "off" 则完全不检查。package.json 的 name)与文件夹名不一致不影响判定:规则只认文件夹。plugins..repo 即按你的值走,不再判定合规。link: 依赖,改完需要在插件管理里重新安装/链接。dsh-plugin topic,全部汇总到 https://github.com/topics/dsh-plugin —— 这就是"联合投稿"的落地方式,不需要任何中心化注册表。github.topics 可加自己的标签;写 [] 表示不打标签;单个插件可用 plugins..topics 覆盖。public。编辑用户配置文件(不在任何仓库里): %USERPROFILE%.dsh\github-sync\config.json (插件首次加载会自动生成带注释的模板,所以通常直接打开改就行。)
{
"github": {
"owner": "你的GitHub用户名",
"token": "ghp_xxxx 或 github_pat_xxxx",
"topics": ["dsh-plugin"],
"license": "MIT",
"copyright": "你的署名或组织名"
}
}owner 也可以写在插件目录的 sync.config.json 里;用户文件里的空字符串不会覆盖插件目录里已填的值(留空即"未指定",要清空请写 null)。 github.license 留空表示不自动生成(只在状态里告警);github.copyright 留空则用 github.owner。 想让提交署自己的名字/邮箱,再加一段 "git": { "userName": "你的名字", "userEmail": "你的邮箱" };该邮箱要先在 GitHub 账号里验证过才会关联到你的头像。留空则自动用账号的 noreply 邮箱(可关联、且不公开真实邮箱)。
Token 权限要求(依据 GitHub REST 官方文档核实)
经典 Token(Classic)— 只勾 `repo` 就够:
-repo已包含public_repo,所以勾了它就同时覆盖公开仓库与私有仓库。
-POST /user/repos(在自己账号下建仓)官方原文:*need thepublic_repoorreposcope to create a public repository, andreposcope to create a private repository.* → `repo` 一个就够。
- POST /orgs/{org}/repos(在组织下建仓)官方原文同上,但额外要求:*The authenticated user must be a member of the organization.*- repo 同时给到代码读写,所以推送也用它。- 勾repo时 UI 会自动带上repo:status、repo_deployment、public_repo、repo:invite,无害,不用管。security_events、delete_repo、write:packages都不需要。
- 唯一例外:若某插件目录含.github/workflows/*.yml,需要额外勾workflow(授予"添加和更新 Actions 工作流文件")。首次推送到空仓库时不存在"同名同内容的旧分支",那条豁免不成立。
细粒度 Token(Fine-grained):
- 推送代码:Contents: Read and write。- 自动新建仓库:Administration: write或Repository creation: write(官方为"满足其一")。
- 打 topics:Administration: write。- 组织启用了 SAML SSO 时,token 需单独授权给该组织,否则 403。
权限不足时不用猜:报错会直接打出 HTTP 状态码与对应提示(401 认证失败 / 403 权限不足 / 404 看不到)。分支保护导致推送被拒时会明确说是分支保护,而不是伪装成别的错误。
得益于 v0.5.0 的双阶段安全防护,您既可以自然对话唤起,也可以随时发起预览:
帮我看看有哪些插件改动了 → 自动调用 github_sync_status / github_sync_plan,只读汇报
把 dsh-sticker 推到 GitHub → 自动先执行试运行 Plan,向您汇报改动文件与大小,等待确认
推吧 / 确认推送 → 收到您的肯定答复后,才真正执行增量提交与远程推送/git 命令依然作为快捷入口保留,您可以根据习惯随时使用。
/git [要求]:快捷触发入口(留空只读调查,带意图发起同步)。github_sync_status:只读查看配置、token 来源、git 可用性、目标清单与判定结果(哪些是 DSH 插件、哪些不是)、协议与镜像位置(只读,不联网)。github_sync_plan:试运行(Dry Run),列出将提交/排除的文件与原因、会打的 topic、协议是沿用还是生成。verbose 可看全部排除项(只读,不提交也不推送)。github_sync_push:真正的提交与推送。默认增量提交 + 普通 push;force: true 才会用单次快照替换远端历史。仓库不存在时按 visibility 自动创建,随后打 topics。需要 `confirm: true` 确认参数或前置授权。想做的事 · 怎么做 只想看会发生什么 · github_sync_plan(或 github_sync_push 加 dryRun: true),都不提交、不推送 重跑一次被拒的推送 · 直接再推一次:会先 fetch 远端再提交,通常就正常了 某个提交必须从历史里消失 · github_sync_push 带 force: true(远端历史被替换成单次快照) 镜像状态坏了 · 删掉 %USERPROFILE%.dsh\github-sync\mirrors,下次自动重建 某个插件不想带协议 · 该插件目录放自己的 LICENSE(会被沿用),或给该插件设 license: ""
DSH 的 Loader 按模块 URL 缓存,并且不看内容与修改时间:文件内容改掉之后,运行中的进程仍然复用旧模块。实测过两次,结论是:
cordis.patch.yml 里的 name 指向入口文件,入口改名时同步更新;package.json 的 exports / files 也要跟着改。实际操作最省事的流程:改完代码 → 重启 DSH。只有在不能重启时才用改名法。
main.mjs 入口:系统提示词闸门 + /git 指令 + 三个工具 + 守卫
core/config.js 配置合并、凭据定位、命名与 topic 规范化、告警
core/discover.js 插件发现 + dsh- 前缀约定(文件夹即仓库名)
core/manifest.js 干净副本清单 + 密钥扫描 + glob/gitignore 匹配
core/license.js 协议检测与生成(正文取自 GitHub 官方接口并缓存)
core/git.js 常驻镜像、增量提交、普通/强制推送、凭据助手
core/github.js REST:建仓、查仓库、打 topics
core/sync.js 计划 / 执行编排(命名阻断、协议、topics、结果汇总)
core/prompt.js /git 闸门(按会话、按轮次授权)与工具守卫
sync.config.json 插件自带配置(只放通用默认值)
sync.status.json 运行期诊断(自动生成,永不推送)(src/、lib/、internal/ 与 index.js、host.mjs、plugin.mjs 都是同一份代码的早期名字:因为上面的模块缓存机制,每次改动都得换 URL,所以历史名被留在了旧代次里。今后请优先重启 DSH,别靠改名。)
这个插件自己也遵守它检查别人的那两条约定(/git 的说明与 Skill 规范中均有明确要求):
/git 才能推送的硬性拦截,全面支持自然语言对话驱动。真实推送工具 github_sync_push 引入 confirm: true 参数校验,未确认前调用会自动拦截并提示先汇报 Plan,彻底杜绝 Agent 误推送。core/git.js 中的文件同步逻辑,从暴力的“全盘物理清空再全量重新写入”升级为“智能比对大小与 mtime 的增量同步”,大幅削减高频磁盘 I/O 并根绝 Windows 文件句柄占用冲突(EBUSY)。dsh-github-sync 标准技能,做到日常对话 0 Token 消耗,并在需要同步时自然激活。.pem, .key, .crt, .cert, .token, .secret, .auth 等扩展名纳入密钥扫描范围,消除漏检隐患。github_sync_plan / github_sync_push 的 plugins 参数现在也接受目录路径(相对或绝对),解析顺序是「插件名 → 目录路径 → 报错」。原来只认扫描结果的写法把发现当成了授权,而那两件事毫无关系:扫描器只是便利设施(列一下工作区里有什么),发布权限来自人的要求。plugins 里用「请求原字符串 / 绝对路径 / 目录名」任一键覆盖。~/.dsh,含 token、会话记录、发布镜像)相重叠的路径一律拒绝,避免误把凭据推上去。目录恰好是扫描根目录本身时会额外提醒。package.json 复用了扫描器的函数(无 DSH 标记即返回 undefined),导致普通项目的名字与版本读不到(显示 0.0.0)。现在"读清单"与"判定是不是插件"是两件事。/git 说明新增一节「范围不等于扫描结果」。/git 本身只进入调查轮(只读),推送工具会被拒绝并说明"要上传请再发一条带推送意图的 /git";只有带推送意图(推送 / 上传 / 发布 / 同步 / push…)才是推送轮。这样"看看会推什么"不再被迫变成一次上传,而上传依然只可能发生在人明确要求的当轮。明说"先别推送/只看"会覆盖掉句中的"推送"。package.json 的 dsh.bundle / dsh.client,或关键词含 dsh),并把判定结果与依据报在状态、试运行与 /git 说明里;dsh- 前缀与 dsh-plugin topic 只对 DSH 插件生效,普通项目不套用、不打 DSH 标签。naming: "warn" | "block" | "off",默认 warn(仍可发布,警告里给出确切改名建议与改名代价);要恢复原来的硬拦设 block。任何情况下都不会静默改名。/git 的说明里新增一节讲清"先判定再套规则",并明确:硬性要求只有两条——不要静默改名,不要静默替人决定协议。core/discover.js 末尾多余的 export default(同一类 unwrapExports 坑)。import { createUserMessage } from '@deepseek-ai/dsh-llm',但实测该裸模块名从插件目录解析不到(宿主的包在 app.asar 内的 /dsh/node_modules/,插件在外部,Node 只会从插件目录往上找 node_modules)。静态 import 解析失败会让整个 bundle 加载失败——/git 闸门与三个工具会一起消失,比原 bug 更严重。deepFreeze(structuredClone({ ...input, role: 'user', id: randomUUID() })),与宿主 createMessage 逐字对应)。两条路径产出的消息都带非空 id,都不会破坏会话回放。ghsync-steer-test.mjs:驱动真实 bundle 与真实 apply(),断言真正进入收件箱的消息具备 id/role/source/content 且被冻结、两次注入 id 不同。故意不桩掉 `@deepseek-ai/dsh-llm`——"这个包能否解析"正是必须不影响结果的那件事。dsh-agent/README.md 里 steer() 的官方示例只写了 content 与 source(没有 `id`),而 steer(input) 的实现是原样 inbox.splice(input)、不补 id,所以照文档写出来的插件会写出回放时校验失败的会话事件。宿主自己的调用方(dsh-api-session-controller)都是先用 createUserMessage 造消息再 steer,照它做才对。github.owner / token / topics,新装的人根本不知道还能配提交署名和开源协议。现在模板同时列出 git.userName / git.userEmail 与 github.license / github.copyright,并注明"留空就按账号派生 noreply 邮箱(可关联头像、不公开真实邮箱)""自填邮箱需先在 GitHub 账号里验证过"。默认值仍安全留白:协议留空 = 不替你选协议,只提醒。core/prompt.js、core/config.js 末尾多余的 export default(unwrapExports 的 exports.default ?? exports 会因此把具名导出整片遮掉,属同一类坑)。/git 之前用裸对象 { role, content, source } 调 agent.steer(),而会话日志按原样记录 user/message,缺少 id 的事件在重启回放时被校验器拒绝——报 session event at seq N lacks an identified message,整个会话的历史加载都会失败(不只是 /git 那一轮)。createUserMessage(来自 @deepseek-ai/dsh-llm)构造消息,由它生成并冻结 id,与其余用户消息一致。/git 注入的轮次;已写坏的历史事件需要单独修补,插件本身无法回改。%USERPROFILE%.dsh\github-sync\mirrors),每次发布是增量提交并普通推送,历史累积、可用 git log/blame/revert;无变更不产生空提交。fetch 并以远端 tip 为基准提交,所以别人在网页或别处推上来的提交会被保留,不会被覆盖。force:生成孤立根提交(干净副本)替换远端历史,用于"某个提交必须消失"(例如误提交密钥),不是日常发布。LICENSE 则沿用;缺失且配置了 github.license 时从 GitHub 官方接口取正文(替换版权行、缓存到本地)写进同一提交;未配置只告警——选协议是法律决定,不替用户定。+@users.noreply.github.com,可关联头像且不公开真实邮箱;可用 git.userName/git.userEmail 覆盖。maxFileBytes = 100 MiB(官方硬上限),超过 50 MiB 按官方规则警告但可推;超限是阻断项而非静默漏掉。密钥扫描范围与上限一致。/git 单轮授权闸门:平时零注入,插件绝不自动上传,未授权轮的推送工具调用会被硬拦截。dsh- 前缀命名约定(文件夹即仓库名),不合规列为阻断项并给出确切改名建议。dsh-plugin topic(联合投稿汇总页),在代码推送成功后设置。