DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Git Sync — DeepSeek Harness 插件(DSH Plugin)
← Plugins

@fish-under-sea/dsh-git-sync

Git Sync

DeepSeek Harness 一键 GitHub 同步:把插件清单、插件启用状态、插件配置、本地设置、Skills、Skill 的 refs/脚本、看板/用量账本、设置导航顺序偏好、壁纸引擎整机配置快照($DSH_HOME/FISH.json)、免费模型插件配置与用量账本采集进你自己的私有 git 仓库并推送,换机可一键还原;采集前自动 fetch 远端并合并,密钥永不搬运。0.2.5 起不再同步 agent 预设 / 桌宠存档 / 工作区映射,0.2.6 起不再同步归档台账,0.2.7 起纳入 sk

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add @fish-under-sea/dsh-git-sync@0.4.0
README兼容性版本

兼容性与来源证明

Git Sync 以 @fish-under-sea/dsh-git-sync 发布,当前版本为 0.4.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
npm
Registry 更新时间
2026/10/16

版本

0.4.0stable
2026/10/12
0.3.3stable
2026/10/11
0.3.2stable
2026/10/10
查看其余 9 个版本收起版本
0.3.1stable
2026/10/10
0.3.0stable
2026/10/7
0.2.7stable
2026/10/7
0.2.5stable
2026/10/5
0.2.4stable
2026/10/5
0.2.3stable
2026/10/5
0.2.2stable
2026/10/5
0.2.1stable
2026/9/30
0.2.0stable
2026/9/30

相关插件

正在加载相关插件…

最新版
0.4.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
132.5 kB
文件数
6
Surface
web
许可证
MIT
发布源
npm
GitHub
★ 1
周下载
1,277
安全扫描
✓ v0.4.0 扫描通过
最近提交
2026/10/13
查看源码 ↗项目主页 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录

相关插件

继续浏览 developer-tools 分类下经过校验的插件。

Client Ui Git Graph@linxin666/dsh-client-ui-git-graph外部 dsh Web GUI 插件:空会话 Git 分支选择器和 Git 图,包含实际的主机端 Git 操作与防护,作为 dsh 配置文件包Better Sidebardsh-better-sidebarBetter Sidebar for DSH — 在一列中提供文件树、编辑器、变更、任务和侧边聊天,并为每个对话单独显示。同时提供服务,供其他插件注册侧边栏标签页和文件查看器。Ssh@linxin666/dsh-sshdsh Web GUI 的远程 SSH 操作:主机配置存储(~/.dsh/dsh-ssh.json,可从 ~/.ssh/config 导入)、支持跳板主机的持久化 ssh2 连接池、exec / PTY Web 终端 / SFTP 传输 / 本地端口转发隧道 / 集群执行Update@linxin666/dsh-update用于 dsh Web GUI 的自更新插件:探测 npm 上是否有更新的系列版本,并在原地运行所属配置的 pnpm 更新;可通过设置图标旁侧边栏中的触发器启动,并显示发布说明面板

README

@fish-under-sea/dsh-git-sync

一键把 DSH 配置面同步到私有 Git 仓库,换机还原

简体中文 · English


解决什么问题

DSH 把所有用户状态收敛在单个 home 目录($DSH_HOME,默认 ~/.dsh)。手动同步要记住一串路径、还要小心别把密钥推上去。本插件把这件事变成「设置 → Git 同步」里的几个按钮——把你的 DSH 环境(已安装的插件、每个插件是否启用、插件配置、本地设置、Skill、看板账本、壁纸引擎整机快照)采集进你自己的私有 Git 仓库并推送,或在新机器上一键还原。

效果

  • 白名单采集:只搬运配置面,密钥文件(.credentials.yaml)在硬黑名单里,永不搬运。
  • 三道安全闸:文件级硬黑名单 → 提交前复查暂存区 → 密钥体检(通用正则 + .credentials.yaml 精确匹配)。
  • 远端合并:采集 / 推送之前先 git fetch;远端领先且本机无提交时快进合并;两边都有提交时自动 rebase;冲突则回滚并如实报错,绝不留半合并状态。
  • 补推语义:推送与否按「本地是否领先远端」判断,而不是「本次是否产生了新提交」——推送失败后再点一次「一键同步」即可重试。
  • 单文件容错:单个文件读不到(被锁定、权限不足、或被文件策略拒绝)只会跳过并在日志里列出,不中断整次同步。
  • 额外扫描根(0.3.0,0.3.3 起表为空):白名单条目可以用「根前缀」指向 $DSH_HOME 之外的额外目录,仓库里落成同名子目录。唯一的消费者(壁纸引擎)在 0.3.3 收敛成 home 内的单条 FISH.json,所以这张表当前是空的——机制保留,给「插件把数据放在 home 之外」这类情况备用。
  • 零 npm 依赖:只用 node: 内置模块。

安装

# 方式一(推荐):从 npm 安装
# --profile 后跟本机实际的 profile 名:桌面版是 desktop,Web 版是 web
dsh plugin --profile <profile> add @fish-under-sea/dsh-git-sync

# 方式二:link 安装(改源码开发时用)—— 仓库源码即安装源,改完重启 DSH 即生效
dsh plugin --profile <profile> add "link:<仓库路径>/packages/dsh-git-sync"

# 方式三:拷贝安装(file:)—— 改完源码必须重新 add 才生效
dsh plugin --profile <profile> add "file:<仓库路径>/packages/dsh-git-sync"

怎么选:日常使用走方式一(npm,版本可追溯、可 update);要改源码时用方式二(link: 建的是目录联接,仓库源码即安装源,改完重启 DSH 即可)。file: 是 pnpm 的目录拷贝——装完之后改 lib/*.js 不会反映到已安装的那份,重启也没用,必须重新 add 一次,只适合一次性试用。

装完重启 DSH(插件行与设置页都是下次启动生效)。

换机提醒:安装会把本机绝对路径写进 profiles/<profile>/package.json("@fish-under-sea/dsh-git-sync": "link:D:/…/plugin")。这个文件会被同步到另一台机器,而那边没有这个目录,dsh plugin install 会在这一项上失败。要么两台机器 clone 到同一路径,要么在新机器上先 dsh plugin --profile web remove @fish-under-sea/dsh-git-sync 再按本机路径 add 回去。

使用

打开 设置 → Git 同步:

按钮作用
一键同步先 git fetch 远端并自动合并,再采集 → 提交 → 推送,日常用这一个就够
仅采集并提交$DSH_HOME 的白名单内容 → 仓库目录,然后 git add / commit(不推送)
从仓库还原到本机仓库目录 → $DSH_HOME,覆盖前备份到仓库的 _backup/
密钥体检扫描将上传的文本文件 + 用 .credentials.yaml 里的真实密钥值做精确匹配
刷新重新读取仓库状态

状态卡怎么看

卡片含义
待同步本机 ↔ 仓库的内容差异(新增 / 变更 / 仓库多出)。为 0 表示两边一致
仓库内文件仓库里已同步的载荷规模(文件数 · 体积)。这不是待办量
工作区仓库的 git 工作区是否干净、是否领先远端
分支 / 远端当前分支与 origin URL
上次运行上一次动作、时间与成败

「待同步」与「工作区」要一起看:待同步 = 0 且工作区干净 ⇒ 本机配置已完整提交并推送到远端,没有什么可同步的。早先版本把「仓库内文件数」标成了「待同步文件」,会让人误以为有几十个文件排队等着上传——已改。

关键语义

  • 「一键同步」会补推之前没推上去的提交。 推送与否按「本地是否领先远端」判断,而不是「本次是否产生了新提交」。推送失败(TLS 拦截、断网、凭据过期)后不用做别的,再点一次「一键同步」即可——曾经有个隐蔽的 bug:旧代码只在本次产生新提交时才推送,于是推送失败后再点一键同步会因为「本机已无变更」而永不重试,还回报成功。已修,并加了回归测试。
  • 「仅采集并提交」是「先看后推」的闸口。 它只提交、不推送,方便你先看差异再决定要不要送上 GitHub。(pushgit 动作仍在 API 上保留,作为应急通道。)
  • 跨机同步不再需要手工救火。 采集与推送之前会先 git fetch 远端:远端领先且本机没有提交时快进合并;两边都有提交时自动 rebase(保持线性历史);遇到冲突则回滚到操作前并如实报错,绝不留半合并状态。此前只会 push,后果是远端一有新提交就永远卡在 ! [rejected] main -> main (fetch first);更隐蔽的是本机没有提交时 @{u} 引用陈旧,会谎报「本地与远端完全一致」而什么都不做。

配置

仓库目录在面板里直接改、点「保存设置」即可。它存在 $DSH_HOME/dsh-git-sync/config.json——故意放在同步范围之外,所以每台机器可以指向自己的克隆路径,不会被互相覆盖。

同步范围(白名单)

白名单列出相对 $DSH_HOME 的路径。<profile> 按目录动态枚举。

路径含义
settings.yaml0.1.x 口径的全局设置;0.2.0 起 home 下的它已被 patch layer 改名 settings.yaml.imported,该条目只在从 0.1.x 老机器迁移时有用
skin-center-active.json皮肤中心当前激活项
skills/Skill 目录($DSH_HOME/skills)
skill-refs/Skill 的 refs 判据、可运行脚本与测试样本(0.2.7 起纳入)
AGENTS.md用户级全局指令
task-board/ledger-v2.json看板账本
task-board/scheduler-v2.json看板调度器状态
dsh-usage/用量账本
dsh-settings-nav-order/state.json设置导航顺序偏好(详见下方专节)
dsh-session-title-refresh/config.json标题自动刷新的用户设置:用哪个模型起标题(provider / model)与全部调参(首轮轮次、间隔、窗口、输出预算、超时、目标字数、是否跳过子代理会话)。同目录的 history.json 是本机运行记录,不进白名单
our-free-model/settings.json免费模型插件(dsh-our-free-model)的用户设置:开关、探测间隔、转发、出口、渠道网关、默认 maxTokens(详见下方专节)
our-free-model/catalog.json免费模型目录快照(模型 id 列表 + 时间戳),无密钥
our-free-model/availability.json免费模型可用性快照(出口 IP + 各模型探测结果),无密钥
our-free-model/stats.json免费模型用量统计账本(详见下方专节,含跨机覆盖说明)
profiles/<profile>/package.json装了什么插件 + bundle 层顺序
profiles/<profile>/cordis.patch.yml每个插件是否启用(disabled: 行)+ 配置覆盖
profiles/<profile>/cordis.patch.yml.bak-plugin-manager插件管理器写的配置备份(精确整路径放行,其它 *.bak* 仍一律拒绝)
profiles/<profile>/pnpm-lock.yaml精确版本,保证可复现
profiles/<profile>/pnpm-workspace.yamlpnpm 配置
FISH.json壁纸引擎的整机配置快照(0.3.3 起,详见下方专节)。这是壁纸引擎「导出预设」得到的那份文件,包含全部观感与行为设置,并内嵌字体与吉祥物资产本体

<profile> 按目录动态枚举:本机 profiles/ 下每个 profile 目录都会被覆盖,桌面版是 desktop、Web 版是 web。写死 profile 名会漏掉「装了什么插件 / 每个插件是否启用 / 精确版本」这最要紧的三样——0.2.0 桌面版踩过这个坑:profile 改名后白名单一条都命中不了,仓库里只剩 0.1.x 的 profiles/web 快照。

设置导航顺序

设置菜单的顺序与隐藏项真正生效的地方是浏览器 localStorage(键 dsh-settings-nav-order/v1),本插件在宿主进程里够不着它。dsh-settings-nav-order 的宿主半区因此把它镜像成 $DSH_HOME/dsh-settings-nav-order/state.json——用户每次保存时用自己那条同源路由写入,本插件只负责按相对路径搬运。少了这一条,换机后设置菜单的顺序与隐藏项就复原不了(剩下的都能复原)。

标题自动刷新设置

⚠️ 0.7.8 起该插件已从聚合包移出、暂停维护:DSH 自 v0.2.1-alpha.2 起已内置会话自动重命名。仍单独安装它的用户,本节仍然适用。

dsh-session-title-refresh 的用户设置落成 $DSH_HOME/dsh-session-title-refresh/config.json——里面是用哪个模型起标题(provider / model)与整套调参(首轮轮次、间隔、窗口、输出预算、超时、目标字数、是否跳过子代理会话)。少了这一条,换机后要把标题模型和这套参数在原界面上重设一遍。

同目录的 history.json 是本机运行记录(每次刷新的成败与会话 id),只增不减、跨机互相覆盖且没有复原价值,所以只点名 config.json,不写整目录。

壁纸引擎:只收一份整机配置快照(0.3.3 收敛)

dsh-plugin-wallpaper-engine(壁纸引擎)的数据目录是 ~/.dsh-wallpaper-engine,与 $DSH_HOME 同级。0.3.0 起本插件用额外扫描根收录它下面的 config.json 与三个素材目录。

0.3.3 起这四条收敛成一条:FISH.json(在 $DSH_HOME 之内,普通相对路径点名即可)。

原因在壁纸引擎自己那边——它的 ADR-0011 把「预设」从玻璃子集快照升为整机配置快照,并新增了导入 / 导出。面板里点「导出预设…」得到的就是这份文件:

{
  "$schema": "dsh-we/profile-preset@1",
  "id": "preset-mv3uxy23-hemr",
  "name": "FISH",
  "values": { /* 116 个键:全部观感与行为设置 */ },
  "assets": {
    "font":   { "name": "默认", "values": { /* 字体集正文 */ } },
    "mascot": { "file": "mascot-….webp", "box": "96x96", "data": "<base64 图片本体>" }
  }
}

为什么这一份就够(2026-10-11 实测):FISH.json 的 values 有 116 键,config.json.settings 有 132 键,差集恰好是 12 个「硬排除」键 + 4 个资产指针——硬排除的是当前壁纸 / 隐藏清单 / 轮播列表 / userProps(本机绝对路径的摘要)/ 运行期失败记忆 / 版本号,它们描述「现在用哪张」而非「这个插件怎么配」,跨机本就该失效。⇒ 快照的 settings 段与这个插件的配置面严格相等。

为什么能顺带撤下那三个素材目录:assets 段内嵌了资产本体。0.3.2 单收 fontsets/ 与 mascot/ 是为了补「设置文件里只留一个指针、真身在旁边另存」那个缺口——现在本体已经在快照里,那两个目录连同 config.json 一起冗余。

为什么是「一条」而不是「四条」:本项目的实测教训是手抄的清单必然漂(见 isBakAllowed 与 entriesOf 的注释)。四条 = 四个漂移点;一条 = 一个。

⚠️ 撤下目录还顺带修掉一个真实缺陷:采集是单向拷贝(只从 home 往仓库复制,从不删仓库侧),所以白名单目录里本机删掉的文件会永远留在仓库,被差异引擎如实算成「仓库多出 N」。实测本机当时正是 4 个文件有差异(新增 0 · 变更 1 · 仓库多出 3)——多出的 3 个全在被撤下的目录里(两份旧格式玻璃预设 + 一张旧立绘)。撤下目录后这一类残留不再产生。注意这只治了壁纸引擎这一处:skills/ 等目录级条目仍有同样的只增不减特性。

⚠️ 还原不是全自动,这一步必须手工做:本插件只负责把文件搬回 $DSH_HOME/FISH.json;真正生效要在壁纸引擎面板里点「导入预设…」选中它。而且「导入 ≠ 立即应用」——导入后还要在预设列表里点一下那份预设才会套用。快照里 assets 缺席的段(例如导出时没勾头像)在应用时保持接收方现值不动,不是回落默认。

额外扫描根(0.3.0)现状:机制保留、当前表为空。白名单条目仍可用「根前缀」指向 $DSH_HOME 之外的目录(代码里是 EXTRA_ROOTS 表),只是壁纸引擎不再需要它。再加一个根只要两步:表里补一行,然后往 WHITE_LIST 加 <key>/… 条目。

明确不收录:壁纸引擎的 cache/(约 2.8 GB 派生缓存)、ffmpeg/ 二进制、bin/、diag/、avatars/、config.json、fontsets/、glass-presets/、mascot/(后四者已由 FISH.json 覆盖)。

实现要点:0.3.0 把原来散在五处(采集 / 还原 / 差异比较 / 密钥体检 / 面板统计)的白名单循环收敛成唯一入口 entriesOf(base, side) ——否则「新增一种扫描口径只在其中一两处生效」是必然结局;本项目已经因为「多层防御各自为政」踩过两次(见 isBakAllowed 与 .gitignore 的注释),所以扫描口径必须只有一个落点。0.3.3 收敛白名单正是靠这条:改动只落在 WHITE_LIST 一处,五个消费者自动跟上。

免费模型插件(our-free-model)

dsh-our-free-model 的配置目录 $DSH_HOME/our-free-model/ 在 home 之内,所以按普通相对路径点名,不需要额外扫描根。收录四份:

  • our-free-model/settings.json —— 用户设置:启用开关、探测间隔、转发、出口、渠道网关、默认 maxTokens;
  • our-free-model/catalog.json —— 模型目录快照(模型 id 列表 + 时间戳);
  • our-free-model/availability.json —— 可用性快照(出口 IP + 各模型探测结果);
  • our-free-model/stats.json —— 用量统计账本。

只排除一份:

  • eac-user.json —— 含真实登录 token 与 GitHub 登录名,属凭据。与 .credentials.yaml 同理:不走 git,需要时自行拷贝。它同时进了 NEVER_COPY,所以即使白名单哪天被误改成 'our-free-model' 整目录,这道闸仍然拦得住。

stats.json 的跨机语义(收录它是有代价的,这个代价是知道的):它是本机累计量(days / models / requests / failedRequests / samples),两台机器各自累加后经仓库互相覆盖,最后同步的那台会盖掉另一台的计数。「两边都完整」用当前实现做不到,只能保证「有一份跟着仓库走」。要两边都准,得把它改成合并式账本(按天 / 按模型取并集或取最大值)——尚未实现。

settings.json 里的 forward.key / egress.url / chanGateway.relay.key 目前是空串。将来真填了密钥,由「密钥体检」如实报出并拦下提交,而不是放宽体检规则。

Skill 位置

DSH 会从多个根目录读 Skill:<项目根>/.dsh/skills、<项目根>/.agents/skills、<DSH_HOME>/skills、~/.agents/skills。本插件只覆盖 <DSH_HOME>/skills(即 ~/.dsh/skills)——所以 Skill 必须放在用户级目录才会被同步。放在项目级 .dsh/skills 的不在同步范围内。

永不搬运

路径的任一段命中即拒绝:

  • 精确文件名:.credentials.yaml / credentials.yaml / credentials.json / .env / node_modules / .pnpm / .git / session_projcache / .anonymous-user-id / .dshw-size.json / .dshw-usage.json / cordis.yml / workspace-local-paths.json
  • 后缀规则:*.bak* / *.pem / *.key / .credentials*
  • 唯一例外:profiles/<profile>/cordis.patch.yml.bak-plugin-manager(插件管理器写的配置备份,换机复原时有用)按精确整路径放行,其它 *.bak* 仍一律拒绝

不在范围内:sessions/、attachments/(0.2.0 起永久排除)、.agent-presets/、pet.json、storages/workspace.json(0.2.5 起撤下)、dsh-session-archive/(0.2.6 起撤下)、node_modules、密钥文件、派生物(cordis.yml、storages/session_projcache)、本机状态(.anonymous-user-id 等)。

0.2.5 撤下三条,理由各自独立:

  • .agent-presets/ —— 不用自定义 agent 预设;
  • pet.json —— 桌宠插件没启用,纯死文件;
  • storages/workspace.json —— 里面是机器相关的绝对路径,搬到另一台机器本来也要手工改(见下方「兼容与边界」),同步它只会带来「换机后工作区指向不存在的位置」的噪声。

0.2.6 再撤一条:

  • dsh-session-archive/ —— @linxin666/dsh-session-archive 的归档台账与运行状态(archive-ledger.json + state.json)。它是纯本机状态:记「哪些会话何时被归档」,而会话本身永久不跨机同步(sessions/、attachments/ 已排除),台账换机后没有意义;自动归档的策略在 profiles/<profile>/cordis.patch.yml 里、那份是同步的,新机器会自己重新记账。此前它还制造了一个假象:配置仓的 .gitignore 恰好也排除了它,于是「复制进仓库却永远不提交」,而面板显示待同步 0。

撤下的内容仍留在本仓历史里(git log -- <路径> 可取回);想恢复同步,把对应的行加回 WHITE_LIST 即可。

侧记(2026-10-06 实测的一次教训):白名单里任何被配置仓 .gitignore 排除的条目,都会变成「本地复制、永不提交、面板却显示已同步」的假象。改动白名单时顺手核对一遍仓库的 .gitignore 是值得的。

同步范围 0.2.0 起收缩为「配置面」:会话记录与附件不再上传。sessions/ 是 zstd 二进制,只增不减、git 无法 diff 也无法行级合并,两台机器同时改必然冲突;attachments/ 同理且属本机隐私数据。配置面换机后靠这一份清单 + 各自的会话副本复原即可。已提交到仓库的旧会话文件不会被删除(只停新增),历史保留可查。

导出私钥配置(0.4.0 新增,纯本机)

这是与「同步到 git 仓库」完全分开的第二条通路,别把两者混起来:

同步(一键同步 / 采集 / 还原)导出私钥配置
去向你的私有 git 仓库(会推到远端)本机磁盘上的一个文件夹
能不能带凭据绝对不能 —— 凭据在硬黑名单里可以 —— 它从不上传
网络会 fetch / push完全不联网

面板上的「导出私钥配置」按钮会把下面这几份拷到目标目录(面板里可改,留空即桌面)下的 DSH私钥配置 文件夹,保留原来的相对目录结构,并写入一份 README.md 说明每个文件是什么、要放回新机器的哪里:

文件要不要带是什么
channel-pack/state.json必需渠道账号花名册与凭据索引(provider / 昵称 / 启用状态 / credentialRef)。本身不含明文密钥,但没有它账号列表就空了
channel-pack/auto-checkin.json可选自动签到记录。不带就换机后当天重新计一次
channel-pack/token-ledger.json可选用量账本。不带就换机后统计从零开始
.credentials.yaml必需⚠️ 凭据本体,所有渠道账号的密钥都在这一个文件里
our-free-model/eac-user.json视情况⚠️ 共创通道的 GitHub 登录 token 与登录名(与渠道账号无关)。不搬就要在新机器上重新授权一次

为什么必须单独做一条通路:.credentials.yaml 与 eac-user.json 都在同步硬黑名单里(见上一节),「一键同步」永远碰不到它们 —— 这是有意的。所以换机时这几份只能手工搬,本功能就是把「手工搬」变得不容易漏。

三条实现约束(改这段代码时不要放宽):

  1. 纯本地 —— 不碰 git、不发任何网络请求;
  2. 不解析内容 —— 只做字节拷贝,绝不把 .credentials.yaml / eac-user.json 的内容读进日志或写进 README(内容一进日志就等于外泄);
  3. 不放宽权限 —— 走 fs.copyFileSync 而不是内部那个会把目标 chmod 成 0666 的 copyFileRobust,POSIX 下再把目标收紧到 0600。

导入怎么做:本功能只提供导出,没有「导入」按钮 —— 按导出文件夹里那份 README 的对照表,把文件拷回新机器的 $DSH_HOME 对应位置,重启 DSH 即可。本插件与 DSH 在任何时候都不会主动读取这些凭据文件去上传或比对。

测试见 test/test-private-export.mjs(32 个断言),其中专门断言了「日志与 README 都不得包含凭据内容」与「清单里的路径一律不在同步白名单里」。

安全设计(三道闸)

新机器请不必动 API 设置:DEEPSEEK_API_KEY、BAILIAN_API_KEY、BAILIAN_HE_API_KEY 等密钥不在同步范围内,新机器上直接配即可。

  1. 文件级硬黑名单:路径任一段命中即拒绝。这是唯一不依赖内容判断的闸,.credentials.yaml 因此在结构上就不可能被搬运。
  2. 提交前复查暂存区:git add 后检查 git diff --cached --name-only,一旦出现密钥类文件名 → git reset 并中止提交。
  3. 密钥体检:读 .credentials.yaml 提取真实密钥值做精确匹配,叠加通用正则(sk-…、GitHub token、AWS key、私钥块、Bearer、api_key: 赋值)。

三道闸里前两道硬拦、第三道只告警。正则无法穷尽密钥形态,体检是辅助手段而非主闸——主闸是第一道的文件级拒绝。

其他约束:不做 force push、不重写历史;git 以非交互方式运行(GIT_TERMINAL_PROMPT=0),不会弹凭据窗口卡住宿主;API 路由只允许同源请求(拒绝 sec-fetch-site: cross-site)。

兼容与边界

  • 工作区映射不再同步(0.2.5 起):storages/workspace.json 里是绝对路径(如 D:\Fish-code\DSH),换机后本来就该由那台机器自己生成;本插件既不采集它、也不在还原时覆盖它。
  • 会话是 zstd 压缩二进制,git 无法 diff / 行级合并。两台机器同时改同一会话会二进制冲突,本插件不做内容级合并。
  • 仓库只增不减:会话是追加型数据,删本地不会缩小仓库。
  • 装完插件后需要重启 DSH 才能在设置页看到它。
  • 依赖 git 在 PATH 中。若你的机器用 HTTPS 拦截式加速器(如 Watt Toolkit / SteamTools),git 可能报 TLS 错误——解决办法见仓库的 AGENT-GUIDE.md §9.1。
  • 本机没有 git 身份也能同步:提交前会探一次 user.name / user.email,没有就临时用 origin 的 GitHub 主人名提交(详见下方「三个已修的坑」)。想固定成你自己的身份,在仓库里执行 git config user.name / user.email 即可。
  • 主机半边的 ctx.webServer 路由是 loopback + 同源保护,没有额外的鉴权层。
  • 只覆盖用户级 Skill($DSH_HOME/skills)。项目级 .dsh/skills、.agents/skills 与 ~/.agents/skills 不在同步范围内。
  • 刻意不同步:skin-center/(导入到壁纸库的壁纸工程——体积上百 MB,且能从本机 Wallpaper Engine 重新导入;其 .cache/we-tokens.json 里全是本机 Steam 绝对路径)、*.bak*。至于 task-board/ledger-v2.lock 这类锁文件:它们只是不在白名单里,所以同样不会被搬运,并非另有一条「锁文件规则」。

三个已修的坑

本机没有 git 身份会让整次同步失败。 全新机器上 user.name / user.email 都没有时,git commit 直接拒绝(Author identity unknown … unable to auto-detect email address)—— 而全新机器恰恰是「换机复原」最需要同步成功的一刻。0.2.3 及以前把这句报错笼统报成「推送失败」,面板上只显示「推送不行」,完全看不出真正原因。现在:提交前先探一次身份(git var GIT_AUTHOR_IDENT,与 git commit 同一套解析),没有就按 origin 的 GitHub 主人名提交(配成 <主人>@users.noreply.github.com,与仓库既有提交一致),拿不到 origin 就退到「本机登录名 @ 主机名」,并在面板日志里写明用了谁、怎么固定下来。配了身份的机器完全不受影响(连 -c 都不注入,作者就是你配置里的那个)。

Windows 只读目标会让覆盖失败。 copyFileSync 会把源文件的只读属性带到目标,而 Windows 的 CopyFileW 在目标已存在且带 ReadOnly 时直接返回 ERROR_ACCESS_DENIED。两者叠加的结果是「第一次采集成功,之后每次都失败」。处理:拷前清掉目标只读位;仍失败则删掉目标重来;拷后保持可写。(实测:修复前仓库里有 108 个只读文件,修复后为 0,采集 0 跳过。)

单个文件失败的不该炸掉整次同步。 插件跑在 DSH 宿主进程内,而会话与附件正在被该进程写入,出现 EBUSY / EPERM 是常态。现在每个文件独立 try/catch,失败记入 skipped 并在面板日志里列出,其余文件照常同步。

开发与测试

  • 零 npm 依赖:只用 node: 内置模块。宿主 loader 在插件未导出 Config schema 时会把 config 原样透传(if (!runtime.Config) return config),所以这里不引入 schemastery。
  • Web 半边用 react.createElement 手写,只 require('react'),不依赖任何 @deepseek-ai/* 客户端包。
  • 宿主导出:name / inject = ['webServer'] / apply(ctx, config)。
  • Web 半导出:inject = ['slots'] / apply(ctx),向 settings.section 注册一页。

测试:同步引擎 120 个用例全部通过(node test/test-sync-engine.mjs);设置页客户端 37 个用例全部通过(node test/test-client.mjs)。

本包没有配置 scripts.test;测试入口为六个文件:

node test/test-sync-engine.mjs      # 同步引擎:白名单 / 采集 / 还原 / 差异 / 拒绝闸
node test/test-client.mjs           # Web 半边渲染与文案断言
node test/client.test.mjs           # Web 半边装载冒烟(node:test)
node test/commit-identity.test.mjs  # 没有 git 身份时的提交回退(真实临时仓库 + 真实提交)
node test/test-push-retry.mjs       # 补推语义(真实远端)
node test/test-remote-merge.mjs     # 远端分叉时的快进 / rebase(真实远端)

直接 node <测试文件> 即可,不要用 node --test test/:测试运行器会派生子进程并捕获管道输出,在受限沙箱里会以 EPERM 失败。

后三个用例(commit-identity.test.mjs / test-push-retry.mjs / test-remote-merge.mjs)会 spawn 子进程调 git,在受限沙箱里以 spawn EPERM 失败——这是环境限制,不是代码问题;请在正常终端里跑它们。

与聚合包的关系

本包是 @fish-under-sea/dsh-fish 聚合包的成员之一;单独安装只影响这一项。

许可

MIT © Fish-under-sea