dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DSH 插件发布器:把本地做好的插件一键推送到 GitHub 仓库(凭据 → 建仓 → 提交 → 推送 → 可选 Release)
把本地做好的 DSH 插件一键上传到 GitHub 仓库:自动初始化仓库、提交改动、按需建仓、推送,可选打标签并发布 Release。整个操作在 DSH 的图形界面里完成,不需要记 git 命令,也不需要 gh CLI。
/user)后存到本机。link: 安装的插件与 node_modules 下的软链插件),点 一键上传。没有令牌也能先点 预检:它只做只读检查(目录、git 状态、仓库是否存在),不写盘、不建仓、不推送。
# 默认装官方桌面端 profile(%USERPROFILE%\.dsh\profiles\desktop)
pwsh -File install.ps1
# 指定 profile
pwsh -File install.ps1 -ProfileDir "D:\some\profile"
# 不用软链,直接拷贝一份(改了源码要重装才生效)
pwsh -File install.ps1 -Copy
脚本做两件事:在 <profile>\node_modules\dsh-github-publish 建一个指向本包目录的 junction,并把包名写进 <profile>\package.json 的 dependencies 与 dsh.profile.bundles。
如果系统禁止运行未签名脚本(
about_Execution_Policies报错),在命令前加一段进程级放行:Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force(只影响当前窗口,不改机器策略)。
装完必须重启 DSH 客户端:profile 未启用 HMR 时,新 bundle 只在启动时装载。
卸载:
pwsh -File uninstall.ps1 # 保留设置与令牌
pwsh -File uninstall.ps1 -PurgeData # 连设置与令牌一起删
| 区块 | 作用 |
|---|---|
| GitHub 凭据 | 显示令牌是否就绪、来源(插件设置 / 环境变量 / 系统凭据管理器)、掩码与账号;可保存、验证、清除。 |
| 目标仓库 | 账号或组织、可见性(公开/私有)、仓库名模板、是否自动建仓、是否写 topics、是否打标签发 Release、是否允许强制覆盖、是否允许回退系统凭据。 |
| 发现范围 | 额外扫描根目录(每个目录向下扫描两层找 package.json)与手动添加的插件目录。 |
| 本地插件 | 每个插件一行:包名/版本、真实路径、git 事实(分支、提交数、是否干净)、GitHub 侧状态(仓库是否存在、是否同步),以及 预检 / 一键上传 按钮。 |
| 当前任务 | 步骤进度、实时日志(同时落盘到 <DSH_HOME>\dsh-github-publish\logs\)、结果链接与取消按钮;推送被拒绝时出现 强制覆盖并重试。 |
| 步骤 | 内容 | 失败时的行为 |
|---|---|---|
| prepare | 检查 git 可用、目录可读、package.json 合法;位于更大仓库内部时默认拒绝 | 给出原因与处理建议 |
| git-init | 目录不是仓库时 git init -b main;补一份最小 .gitignore(node_modules/ 等) | 原样回报 git 输出 |
| commit | git add -A,有改动就提交(提交信息模板可配),身份取 git config,缺失时用 GitHub 账号的无回复邮箱 | 提示补 user.name/user.email |
| github-repo | GET /repos/... 判断仓库是否存在(插件已有指向 GitHub 的 origin 时默认沿用该仓库),不存在且开启自动建仓则创建(用户走 /user/repos,组织走 /orgs/{org}/repos);按需写 topics | 令牌/权限问题给出对应提示 |
| remote | 把 origin 指向 GitHub 返回的 clone_url(企业版同样正确) | — |
| push | 用一次性 HTTP 头携带令牌推送当前分支(-- 终止选项解析,禁用仓库自带钩子),并建立 upstream | 被拒绝时提示开启强制覆盖;强制推送用远端实时 sha 作 --force-with-lease 预期值;鉴权失败时提示令牌权限 |
| release | 可选:v<版本> 标签 + GitHub Release,已存在则跳过 | 跳过并记录原因,不影响整体成功 |
| verify | 读远端分支头提交与本地 HEAD 比对 | 不一致 → 任务判失败(提示分支保护 / pushurl 被改写等可能原因);没令牌或分支还不存在 → 步骤标记为「跳过」并给出提示 |
任何一个步骤失败都会:把该步骤标红、把错误与建议写进任务面板与日志文件,且不回滚已完成的本地工作(重试即可继续)。
解析顺序(第一个命中者生效):
<DSH_HOME>\dsh-github-publish\token(0600)GITHUB_TOKENGH_TOKENgit credential fill(Git Credential Manager 已登录过 GitHub 时可直接复用;用 credential.interactive=false + GCM_INTERACTIVE=never 调用,任何情况下都不会弹出登录窗口;本机没有存凭据时会立刻返回“无令牌”,可用设置项关闭这条回退)需要什么权限
repo(要发 Release / 写 topics 也在这个范围内)Contents: Read and write;要自动建仓还需 Administration: Read and write,并授权目标账号/组织令牌只用于两处:GitHub REST 调用(Authorization: Bearer …)与 git push(http.extraheader 一次性注入)。
令牌
ghp_…abcd)与来源;GIT_CONFIG_COUNT/GIT_CONFIG_KEY_0/GIT_CONFIG_VALUE_0 注入 HTTP 头,不进 argv、不写 .git/config,进程退出即消失;<DSH_HOME>/dsh-github-publish/no-hooks):仓库自带的 pre-push/pre-commit、仓库级 core.hooksPath 都不会执行(命令行 -c 优先级最高,实测压得住仓库本地配置);GITHUB_TOKEN/GH_TOKEN 等常见变量名,并删除任何取值等于已登记令牌的变量——仓库里的 filter.*.clean、core.fsmonitor 这类"必然要执行"的程序即使跑了,也拿不到令牌;redact():既抹当前令牌(含其 base64 形式),也按模式抹掉 任何 URL userinfo、ghp_/github_pat_ 形态、Authorization 头与 password= 行——origin 里残留的别的令牌同样不会被打印;token 文件),删文件即撤销,不需要动设置;.git/config、.git 目录全量文件、发布日志、任务响应里都不允许出现令牌明文。参数与远端安全
branch / owner / repo 全部走白名单校验(lib/validate.js):非法直接 400。git 会解析位置参数之后的选项,不做这层校验时 branch: "--mirror" 会变成真正的 --mirror(删远端引用);-- 之后,git push origin -- <branch>、git fetch/ls-remote ... -- <branch>;remote.origin.pushurl / receivepack / uploadpack / proxy、http.proxy / https.proxy / http.sslVerify、core.gitProxy、url.*.insteadOf / pushInsteadOf(git remote set-url 只改 url,不会动 pushurl);清掉哪些键会写进任务告警,清理失败直接中止推送;同样的中和也内建在 lib/git.js 的 push() 里,直接调用内层 API 同样受保护;git ls-remote 的实时 sha,并按 refs/heads/<branch> 精确比对(ls-remote 的 pattern 是后缀匹配,远端同时有 a/b 与 b 时取错行会让 lease 失真);远端没有该分支时用「期望不存在」;换 origin 时会清掉旧的 refs/remotes/origin/*;http:// 的重定向直接报错,不把 Bearer 令牌交出去。发布行为
git add -A 会把整个仓库推上去),需要显式勾选「只发布这个子目录(在插件目录内单独 git init)」;--force-with-lease=<branch>:<sha>);push --delete,不 remote prune;git add -A 之后扫描将要提交的文件名,命中 .env、*.pem、id_rsa*、credentials* 等形态时在界面与日志里告警(默认可见性是 public,误推代价很大);.gitignore 只忽略 node_modules/、*.log 与系统垃圾文件;dist/、build/ 不再被静默排除——发布的是成品,是否包含由你决定;<DSH_HOME>\dsh-github-publish\logs\publish-*.log。Windows 说明:
token/日志目录按 POSIX 权限位创建(0600 / 0700),但 Windows 上 Node 的 mode 不生效,实际保护来自用户目录 ACL。需要更强的保护请使用系统凭据管理器(本插件支持回退)或自行给目录收紧 ACL。
HTTP 接口(/dsh-github-publish/api/*)
127.0.0.1/::1)连接,其余 403;x-dsh-github-publish: 1 自定义头 —— 跨站表单/图片请求无法伪造,跨源 fetch 会先触发预检,而本路由不返回任何 CORS 头;Origin 时必须与 Host 同源;application/json,请求体上限 256 KB(超出回 413),响应一律 no-store。保存在 <DSH_HOME>\dsh-github-publish\settings.json。
| 键 | 默认 | 说明 |
|---|---|---|
owner | "" | 空 = 用令牌所属账号;组织名直接填组织 |
visibility | public | public / private |
repoTemplate | {name} | 支持 {name}(去 scope 包名)、{scope}、{full}、{version} |
createRepo | true | 仓库不存在时自动创建 |
reuseExistingRemote | true | 插件已有指向 GitHub 的 origin 时,沿用该仓库而不是另建新仓(账号/仓库名被显式指定时以指定值为准) |
setTopics | true | 用 package.json 的 keywords + dsh-plugin/deepseek-harness 写 topics |
release | false | 打 v<version> 标签并发布 Release |
allowForce | false | 允许 --force-with-lease |
useCredentialHelper | true | 无令牌时回退到系统凭据管理器 |
commitMessage | chore(release): {name} v{version} | 提交信息模板 |
extraRoots | [] | 额外扫描根目录(向下两层) |
manualDirs | [] | 手动添加的插件目录 |
includeInstalled | true | 是否扫描 profile 的 node_modules 软链插件 |
apiBase | https://api.github.com | GitHub REST 基址(企业版改这里) |
remoteBase | https://github.com | 展示用基址 |
环境变量覆盖(不会写进磁盘,适合企业部署与自检):
DSH_GITHUB_PUBLISH_API_BASE=http://127.0.0.1:8080 # GitHub API 基址
DSH_GITHUB_PUBLISH_REMOTE_BASE=https://ghe.corp # 远端基址
DSH_GITHUB_PUBLISH_OWNER=some-org # 默认 owner
DSH_GITHUB_PUBLISH_NO_CREDENTIAL_HELPER=1 # 禁止回退系统凭据管理器
dsh-github-publish/
package.json dsh.bundle.patch + dsh.client 声明
cordis.patch.yml 插入 profile 配置树的挂载声明
lib/
index.js 宿主半区入口:HTTP 路由、请求防护、状态与发现接口
publish.js 一键发布流水线(job/步骤/日志/失败提示)
discover.js 插件发现(link: 依赖、node_modules 软链、扫描根、手动目录)
git.js git 调用封装(非交互、有界输出、一次性鉴权头)
github.js GitHub REST 客户端(零依赖 node:https)
credentials.js 令牌解析(设置 → 环境变量 → git credential)与脱敏
validate.js 分支/账号/仓库名白名单校验 + 机密文件名识别
settings.js 路径推导、设置读写、令牌文件(0600)
client.js 浏览器半区成品 bundle(设置分区 UI,手写无编译)
test/
harness.mjs 宿主半区端到端自检(假 GitHub + 本地裸仓库)
client-smoke.mjs 浏览器半区冒烟测试(真 React + jsdom)
install.ps1 / uninstall.ps1
依赖:只用 Node 内置模块(node:http(s)、node:child_process、node:fs…),零运行时依赖;浏览器半区只 require('react')(DSH 的 PLATFORM_MODULES 提供),无需构建步骤。
宿主半区端到端(不需要真实 GitHub,会起一个假 REST 服务 + 本地裸仓库,并断言令牌不落盘):
node test\harness.mjs # 130 项断言
node test\harness.mjs --keep # 保留临时目录便于排查
覆盖:环境状态、请求防护(缺头/错误 Content-Type/超大请求体)、无令牌失败路径、令牌保存/校验/清除、插件发现、预检不写盘、首次发布全链路(建仓+提交+推送+校验)、幂等二次发布、沿用既有 origin、远端分叉(拒绝→强制覆盖)、陈旧跟踪引用下的强制推送、嵌套仓库防护、设置持久化、并发 409 与取消、参数注入防护(--mirror/--dry-run/非法 owner 与仓库名)、远端校验不一致判失败、机密文件告警、钩子隔离(A/B 对照)、pushurl/insteadOf/http.proxy 重定向防护、a/b 与 b 同名后缀下的 lease 精确匹配、仓库级 filter.clean 环境脱敏、内层 push() 防护、脱敏与校验函数单测。
浏览器半区冒烟(真 React 18 + jsdom 渲染整个面板并点击按钮):
# 一次性装上测试期依赖(不进插件包)
npm i --prefix ..\..\.testdeps react@18.3.1 react-dom@18.3.1 jsdom@24.1.3
node test\client-smoke.mjs # 44 项断言
npm 默认缓存目录不可写时(
EPERM: ...\_cacache),加--cache ..\..\.npm-cache指定一个工作区内的缓存目录。
| 现象 | 原因 | 处理 |
|---|---|---|
| 设置里没有「插件发布」 | 未安装 / 未重启 / 客户端 bundle 未送达 | 确认 dsh.profile.bundles 含本包,重启 DSH;看客户端控制台有没有 client-modules: client bundle not found |
| 面板提示「读不到宿主接口」 | 宿主半区未加载(fiber 失败) | 看 DSH 日志里 dsh-github-publish 的加载行;确认 lib/index.js 存在 |
| 保存令牌返回 401 | 令牌无效或过期 | 重新生成(classic 勾 repo;fine-grained 给 Contents 读写) |
| 建仓返回 403 / 422 | 权限不足 / 仓库名被占用 | fine-grained 需要 Administration 读写;换个仓库名或先手动建仓 |
| 推送被拒绝(non-fast-forward) | 远端有本地没有的提交 | 先 git pull --rebase,或开启「允许强制覆盖」后点 强制覆盖并重试 |
| 提示「强制推送被拒绝:预期值与远端不一致」 | 读取远端之后远端又变了(stale info) | 再点一次「强制覆盖并重试」即可(会重新读取远端 sha) |
| 提示「远端校验不一致」 | 分支保护改写了提交 / remote.origin.pushurl 指向别的仓库 / 远端被同时推送 | 打开仓库确认实际内容;这类情况插件按失败处理,不会假装成功 |
| 提示「疑似机密文件将被提交」 | 目录里有 .env、私钥等 | 确认要发布就忽略;不想发就加进 .gitignore 再重试 |
推送提示 could not read Username | 没令牌且系统凭据不可用 | 保存令牌,或关闭 DSH_GITHUB_PUBLISH_NO_CREDENTIAL_HELPER 限制 |
| 拒绝发布「位于更大的仓库内」 | 插件目录在某个仓库的子目录里 | 勾选「只发布这个子目录」,或把插件移出该仓库 |
| 提交身份报错 | 本机没配 git user.name/user.email | 用 GitHub 令牌时插件会自动兜底;否则 git config --global user.name/user.email |
| 发布成功但远端没有文件 | 目录被 .gitignore 全部忽略 | 看任务日志里的「已暂存 N 处改动」与 .gitignore |
<owner>/<repo> 由模板决定,不做 monorepo 子目录推送(需要的场景可以用「扫描根目录 + 手动目录」分别发布)。.gitattributes 里的 LFS 规则由本机 git-lfs 处理。0.0.0.0。-- 隔离是硬要求,而不是可选项。--force-with-lease 只保护「读到的那一刻」:读取远端 sha 与推送之间仍有极小窗口,中间人推送会被拒绝而不是被覆盖(这是设计取向)。