🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

数据备份 shiinakaze/dsh-plugin-backup

DeepSeek Harness
shiinakaze22 天前有提交已提供中文文档扫描:含敏感能力

按分类打包配置与数据,随时校验恢复并同步 GitHub

29.3/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/28 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add shiinakaze/dsh-plugin-backup
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么[目录]—
dsh 原生插件 · tool
暂无时间戳·判据 →
装得上吗[实装验证]—
本站尚未做安装检查
暂无时间戳·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/04·判据 →
还在维护吗[仓库]✓
活跃:最近一次提交在 22 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →

可信度证据

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件发起外部网络请求
查看扫描证据(共 46 处,展示前 44 条)
  • 读取本地文件
    index.js:188
    const stream = createReadStream(ticket.path);
  • 读取本地文件
    lib/core/engine.js:40
    const manifest = JSON.parse(await readFile(require.resolve('@deepseek-ai/dsh/package.json'), 'utf8'));
  • 写入本地文件
    lib/core/engine.js:221
    await mkdir(directory, { recursive: true });
  • 读取本地文件
    lib/core/engine.js:306
    await stat(join(root, id));
  • 读取本地文件
    lib/core/engine.js:420
    exists = (await stat(destination)).isFile();
  • 写入本地文件
    lib/core/engine.js:426
    await mkdir(dirname(destination), { recursive: true });
  • 读取本地文件
    lib/core/engine.js:428
    await writeAtomic(`${destination}.pre-restore`, await readFile(destination));
  • 写入本地文件
    lib/core/engine.js:430
    openSink = createWriteStream(destination);
  • 读取本地文件
    lib/core/engine.js:491
    size = (await stat(path)).size;
  • 读取本地文件
    lib/core/plan.js:132
    names = await readdir(base);
  • 读取本地文件
    lib/core/plan.js:175
    names = await readdir(current, { withFileTypes: true });
  • 读取本地文件
    lib/core/plan.js:204
    const info = await stat(absolute);
  • 读取本地文件
    lib/core/plan.js:244
    info = await stat(absolute);
  • 读取本地文件
    lib/core/util.js:112
    return JSON.parse(await readFile(path, 'utf8'));
  • 写入本地文件
    lib/core/util.js:124
    await mkdir(dirname(path), { recursive: true });
  • 写入本地文件
    lib/core/util.js:133
    await rename(temporary, path);
  • 写入本地文件
    lib/core/util.js:156
    await mkdir(path, { recursive: true });
  • 读取本地文件
    lib/core/util.js:157
    await access(path, fsConstants.W_OK);
  • 读取本地文件
    lib/core/util.js:164
    await access(path, fsConstants.R_OK);
  • 删除本地文件递归
    lib/core/util.js:176
    await rm(path, { recursive: true, force: true });
  • 写入本地文件
    lib/core/github.js:47
    await mkdir(directory, { recursive: true });
  • 发起网络请求
    lib/core/github.js:93
    response = await fetch(`${API}${input.path}`, {
  • 写入本地文件
    lib/core/github.js:295
    await mkdir(target, { recursive: true });
  • 读取本地文件
    lib/core/github.js:319
    return readFile(path);
  • 读取本地文件
    lib/core/settings.js:184
    const info = await stat(absolute);
  • 读取本地文件
    lib/core/store.js:31
    entries = await readdir(directory, { withFileTypes: true });
  • 读取本地文件
    lib/core/store.js:45
    const info = await stat(absolute);
  • 读取本地文件
    lib/core/store.js:63
    names = await readdir(root, { withFileTypes: true });
  • 读取本地文件
    lib/core/store.js:124
    return await readFile(path);
  • 写入本地文件
    lib/core/store.js:147
    await mkdir(root, { recursive: true });
  • 读取本地文件
    lib/core/store.js:158
    age = Date.now() - (await stat(path)).mtimeMs;
  • 删除本地文件
    lib/core/store.js:174
    await unlink(path);
  • 删除本地文件递归
    lib/core/store.js:203
    await rm(row.directory, { recursive: true, force: true });
  • 读取本地文件
    lib/core/store.js:224
    await stat(directory);
  • 删除本地文件递归
    lib/core/store.js:233
    await rm(backupDirectoryOf(root, clean), { recursive: true, force: true });
  • 删除本地文件
    lib/core/store.js:253
    await rm(path, { force: true });
  • 删除本地文件递归
    lib/core/store.js:263
    await rm(directory, { recursive: true, force: true });
  • 写入本地文件
    lib/core/tar.js:227
    await mkdir(dirname(input.target), { recursive: true });
  • 读取本地文件
    lib/core/tar.js:251
    const info = await stat(file.absolute);
  • 读取本地文件
    lib/core/tar.js:256
    const reader = createReadStream(file.absolute);
  • 写入本地文件
    lib/core/tar.js:275
    createWriteStream(partial),
  • 删除本地文件
    lib/core/tar.js:278
    await rm(partial, { force: true });
  • 写入本地文件
    lib/core/tar.js:281
    await rename(partial, input.target);
  • 读取本地文件
    lib/core/tar.js:294
    const raw = createReadStream(input.source);
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

社区实装还没有人打卡 · 装过的第一个就是你
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/schemastery@deepseek-ai/dsh-tools@deepseek-ai/dsh-typert-protocol
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

README

由 DeepSeek 最新模型翻译生成
dsh-plugin-backup DeepSeek Harness 的备份插件:把 $DSH_HOME 按分类打成可校验、可恢复、可下载的归档,同名备份可推送到 GitHub。全部能力同时开放给 Web 设置页、模型工具面 与 CLI 会话 (三者共用同一套引擎与同一把单飞锁)。 - 备份命名:dsh-backup-<YYYYMMDD>-<HHmmss> - 分类:settings(DSH 设置)/ skills(技能)/ plugins(插件)/ sessions(会话)/ storages(存储) - 每个备份:下载 / 验证 / 恢复 / 删除 - 配置:备份路径、备份时间间隔、分类开关、保留份数、创建后立即验证、单类归档体积上限、GitHub 同步 - 零构建、零第三方运行时依赖:宿主半是纯 ESM,浏览器半是可直接手写下发的 CJS 工厂 安装 本地目录安装(开发) dsh plugin --profile web add file:C:/path/to/dsh-plugin-backup 或从 GitHub 安装 dsh plugin --profile web add github:shiinakaze/dsh-plugin-backup
展开完整文档(README 剩余 1 节)
dsh-plugin-backup

DeepSeek Harness 的备份插件:把 $DSH_HOME 按分类打成可校验、可恢复、可下载的归档,同名备份可推送到 GitHub。全部能力同时开放给 Web 设置页、模型工具面 与 CLI 会话 (三者共用同一套引擎与同一把单飞锁)。

- 备份命名:dsh-backup-<YYYYMMDD>-<HHmmss>
- 分类:settings(DSH 设置)/ skills(技能)/ plugins(插件)/ sessions(会话)/ storages(存储)
- 每个备份:下载 / 验证 / 恢复 / 删除
- 配置:备份路径、备份时间间隔、分类开关、保留份数、创建后立即验证、单类归档体积上限、GitHub 同步
- 零构建、零第三方运行时依赖:宿主半是纯 ESM,浏览器半是可直接手写下发的 CJS 工厂

安装

本地目录安装(开发)
dsh plugin --profile web add file:C:/path/to/dsh-plugin-backup

或从 GitHub 安装
dsh plugin --profile web add github:shiinakaze/dsh-plugin-backup

桌面端自管 desktop profile:通过其插件管理器安装(或 dsh plugin --profile desktop add …),然后重启应用。

dsh plugin add 会同时完成两件事:把包写进 profile 的 dependencies,并把包名追加到 dsh.profile.bundles(构成顺序里的一层)。安装后重载或重启 Web 界面,打开 设置 → 数据备份。

运行时支持范围:本插件面向 @deepseek-ai/dsh 的 0.1.7 与 0.2.0 两条线—— peerDependencies 中 dsh-tools 与 dsh-typert-protocol 声明为
^0.1.7-rc.2 || ^0.2.0-rc.1,engines.dsh 同步该窗口。0.2.0 的启动链会在装载前做
兼容性预检,对 peer 不满足的 bundle 直接拒绝安装/挂载,因此精确锁某一个 rc 会被直接拒装。两个分支不是修饰:包管理器按 npm 的预发布规则解析,只有当某个比较符钉住完全相同的版本元组时才放行预发布版,所以写成单条 =0.1.7-rc.2 <0.3.0 会静默排除 0.2.0-rc.1 与 0.2.0-rc.2,在 0.2.0 线的主机上让 npm install 报 ERESOLVE。窗口必须与插件实际使用的 API 保持同步。

file: 安装是快照:profile 里存的是包的副本,改动源码后需要
dsh plugin --profile web remove <name> 再 add 一次才会生效。
本插件的实测记录见 docs/verify.md。

该包是 DSH 插件而非普通依赖:宿主半的 inject 需要 schema 中的 tools 服务,浏览器半通过 package.json 的 dsh.client 声明被发现。缺少 $DSH_HOME 时会退回 ~/.dsh;非 Web profile(没有 webServer 服务)时仅“下载”不可用,其余功能与工具照常。

界面

设置 → 数据备份:

| 区块 | 内容 |
| 备份设置 | 备份路径(留空 = C:\Users\<用户名>\dsh_backups,其他平台 ~/dsh_backups;支持 ~ 与 ${DSH_HOME})、备份间隔(关闭 / 15 分钟 / 每小时 / 6 小时 / 每天 / 每周)、保留份数、创建后立即验证 |
| 分类备份 | 五个分类的备份选择:勾选哪些就备份哪些;「全选 / 全不选」一键切换;按钮显示当前选择(如「立即备份(3 个分类)」),未选任何分类时按钮禁用并给出提示 |
| 已有备份 | 每行显示 ID、创建时间、文件数、体积、分类徽标、验证结论;操作为 下载(下拉选某一分类或 manifest)、验证、仅预览恢复、恢复、删除(恢复与删除需二次点击确认,删除按钮为危险色) |
| GitHub 同步 | 启用开关、仓库 owner/repo、分支、子目录、令牌环境变量、令牌是否已配置,以及 推送到远端 / 从远端拉取 |

「立即备份」只备份当前勾选的分类;点「保存设置」后,该选择同时成为定时备份的默认分类。

设置写入插件的用户层文件 $DSH_HOME/dsh-plugin-backup/settings.json(0600),部署基线仍在 profile 的 cordis.patch.yml 里,二者做浅层合并(用户层优先)。

目录布局

<备份路径>/ dsh-backup-20260930-141205/ manifest.json # schema 版本、主机信息、逐类 files/bytes/sha256/归档名、上次验证结论 settings.tar.gz # 每个分类一个归档(gzip 压缩的 USTAR) skills.tar.gz plugins.tar.gz

manifest.json 最后写入且原子落盘,所以“目录里有 manifest”即代表这份备份完整。
创建期间所有中间文件以 .part 结尾,崩溃残留会在下次运行开始时清理或被忽略。

分类与排除

| key | 来源(相对 $DSH_HOME) | 默认 |
| settings | settings.yaml、cordis.patch.yml、.credentials.yaml、.anonymous-user-id、dsh-skill-hub.json、skills-market-sync.json | 开 |
| skills | skills-management/ | 开 |
| plugins | profiles//package.json、pnpm-lock.yaml、pnpm-workspace.yaml、cordis.patch.yml,以及 plugins/ | 开 |
| sessions | sessions/ | 关 |
| storages | storages/ | 关 |

全局排除:任意层级的 node_modules、.git、符号链接;备份根目录自身也会被排除,因此把备份放在 $DSH_HOME 内不会自包含递归。

真实体量(本机实测,settings + plugins + skills)

| 分类 | 文件数 | 原始 | 归档 | 耗时 |
| settings | 5 | 2.0 KB | 1.3 KB | < 0.1 s |
| plugins | 39 | 1.24 MB | 1.12 MB | 0.2 s |
| skills | 29 356 | 342.5 MB | 144.8 MB | ≈ 50 s |

创建与校验都是流式的(恒定内存,实测 RSS 57 MB → 158 MB),不会把一整个分类读进内存。
因此默认单类上限设为 512 MiB;若你的技能树更大,请在设置里调高 maxArchiveBytes,或把 skills 排除后改为手工备份。保留 20 份、且开启 skills 时约占用 3 GB 磁盘。

GitHub 同步

1. 在设置页勾选“启用同步”,填写 owner/repo(也可写完整 URL)、分支、子目录。
2. 令牌来源(按优先级):
- 环境变量:默认 DSH_BACKUP_GITHUB_TOKEN(可在设置里改名字);
- 状态文件:$DSH_HOME/dsh-plugin-backup/github-token.json,内容 {"token": "…"},权限 0600。
令牌只从这两处读取,永不写入配置、日志或工具输出。
3. 推送到远端:逐文件调用 Contents API,归档先推、manifest.json 后推;远端已有同名且内容一致的备份会跳过。从远端拉取:只下载本地缺失的备份,并按 manifest.json 的 sha256 校验每个归档,校验不过就丢弃该备份并报告原因。

远端结构:<子目录>/<备份ID>/{manifest.json,<分类>.tar.gz}。

模型工具

| 工具 | 参数 | 说明 |
| backup_run | categories?、label? | 立即创建一份备份 |
| backup_list | limit? | 列出已有备份(最新在前) |
| backup_verify | id | 重算 sha256 并重读 tar 结构,逐类报告差异 |
| backup_restore | id、categories?、dryRun?、force? | 恢复;dryRun 只列计划 |
| backup_delete | id、force? | 删除;仍有更旧备份时最新一份受保护 |
| backup_sync | direction、ids? | GitHub 推送 / 拉取 |

典型用法:改动前 backup_run,出问题后 backup_verify 再 backup_restore; backup_restore 会先跑一次校验,校验不过会拒绝(除非 force)。

安全边界

| 场景 | 行为 |
| 路径穿越 | 备份 ID 必须匹配 ^dsh-backup-\d{8}-\d{6}$;归档内条目拒绝绝对路径、盘符路径、.. |
| 归档损坏 | verify 报出具体类别与期望/实际 sha256;restore 默认拒绝,force 时降级为“跳过该分类并报告” |
| 覆盖不可逆 | 恢复前把将被覆盖的文件就地留一份 <name>.pre-restore |
| 并发 | 单飞锁(<备份路径>/.lock,30 分钟视为过期);冲突返回 busy,绝不并发写 |
| 误删 | 仍有更旧备份时,最新一份需要 force 才能删除;只剩一份时可直接删除 |
| 体积失控 | 单类归档超过 maxArchiveBytes 即中止本次运行并删除半成品 |
| 令牌泄露 | 令牌只读环境变量 / 0600 文件;所有输出只出现仓库路径 |
| 下载链接 | 单次使用的下载票据(24 字节随机、120 秒过期、消费即失效),由 GET /dsh-plugin-backup/download?token=… 提供 |

下载路由不做登录校验(票据本身是一次性凭证),因此请保持 Web 服务仅监听回环地址,不要额外暴露到不可信网络。

开发

要开发/移植别的插件? 请直接看 DSH 插件开发手册: docs/plugin-development-guide.md —— Desktop 0.2.0 运行时解剖、peer 兼容性预检规则、六步适配/验证流程(命令均实测)、宿主/Typert/浏览器两面契约速查、0.1.7→0.2.0 API 差异表,以及跨版本坑位清单。

npm test # 50 项:tar/manifest/engine/插件装配/浏览器半/打包契约
node --check index.js
node --check client.js

测试不需要 DSH 运行时,但会尽量使用真实的官方包做契约校验:

- @deepseek-ai/dsh-typert-loader 的 validateTypertManifest 校验宿主描述符;
- 真实 TypertRegistry 注册 9 个端点;
- 真实 dsh-tools 的 defineTool 编译 6 个工具 schema;
- 真实 @deepseek-ai/cordis 根上下文挂载插件,并驱动下载路由读写真实归档;
- 浏览器半通过模块装载器假实现 + 迷你 hooks 运行时渲染,并逐字段比对宿主/客户端描述符。

node test/local/measure-real-home.local.mjs settings plugins skills
node test/local/remount.local.mjs

上面的脚本会对真实 $DSH_HOME 做一次备份、校验、dry-run 恢复并清理,用于测量体积与耗时; remount.local.mjs 复现 profile 重组时的重复挂载,用于确认服务、工具与下载路由不会泄漏或失效。

结构与职责

index.js 宿主插件:Config schema、apply、下载路由 typert.js 宿主 Typert 描述符 + BackupService(远端方法实现) lib/tools.js 6 个 backup_* 工具 client.js 浏览器半:设置页 + Remote 装载
lib/core/{tar,manifest,plan,store,settings,github,engine,util}.js
test/.test.mjs 离线测试;test/local/ 为测量脚本

故障排查

| 现象 | 处理 |
| 设置页没有“数据备份” | 确认包已加入 dsh.profile.bundles;重载页面;查看启动日志是否有该插件的 FAILED fiber |
| 页面提示“备份路径当前不可用” | 路径不可写或被同名文件占用;改成可用目录(默认 C:\Users\<用户名>\dsh_backups)后保存 |
| 备份报 busy | 有另一次运行持锁(定时备份 / 另一会话);等待或删除 <备份路径>/.lock |
| 报 too-large | 调高 maxArchiveBytes,或先关闭 skills 分类 |
| 恢复被拒(corrupt) | 先 backup_verify 看具体类别;归档确实损坏时换一份更新的备份 |
| GitHub 报 github-auth | 令牌未配置或权限不足(需要目标仓库的 contents: write) |
| 下载无反应 | 票据 120 秒过期;重新点“下载” |
| 页面报 method "backup/remove" conflicts with its namespace service | 0.1.1 及更早的构建问题:客户端载体的命名空间服务自带 remove 成员。0.1.2 起删除端点线上名为 deleteBackup;若自行扩展本插件,请避开那组保留名 |
| 页面报 cannot get property "…" without inject | 0.1.2 及更早的构建问题:宿主/客户端直接读了 Cordis 服务属性而没有声明注入。0.1.3 起改为 ctx.get(...) 与 ctx.inject(...) 子 fiber;升级后重启 dsh web 即可 |
| 点「保存设置」后设置页整片变空白(刷新页面才恢复) | 0.1.3 及更早的构建问题:保存动作把宿主返回的 Preferences(无 rows/total/state)当成了整页快照使用,渲染时抛错,而槽位错误边界会把这个分区永久除名。0.1.4 起改为保存后重读快照,并加了载荷归一化与本节区错误边界;升级后刷新页面即可 |
| 桌面端安装被拒:「Plugin dsh-plugin-backup@… is incompatible with dsh 0.2.0-rc.2: peerDependencies …」* | 0.1.4 及更早的打包问题:dsh 依赖被精确锁在 0.1.7-rc.2,而 0.2.0 启动链的兼容性预检会直接拒绝 peer 不满足的 bundle。移除后重新 add 即可;临时绕过可用 dsh plugin allow-version 授予一次性豁免 |
| 0.2.0-rc 主机上 npm install 报 ERESOLVE … peerOptional @deepseek-ai/dsh-tools@">=0.1.7-rc.2 <0.3.0" | 仅 0.2.0 线的打包问题:那条窗口看似同时覆盖两条线,但 npm 只在某个比较符钉住完全相同版本元组时才放行预发布版,于是 0.2.0-rc.1/0.2.0-rc.2 被静默排除,冲突被报在宿主自带的副本上。1.0.0 起改为 ^0.1.7-rc.2 \|\| ^0.2.0-rc.1,同时满足 npm 规则与运行时预检;升级即可,或改用一直容忍旧范围的 pnpm / dsh plugin add |

设计说明

- 为什么默认备份到用户目录:C:\Users\<用户名>\dsh_backups(其他平台 ~/dsh_backups)与 DSH home 解耦,重置或迁移 $DSH_HOME 不会连带丢掉备份,也便于用户直接用资源管理器打开。
- 为什么设置放在插件自己的 settings.json:备份路径/间隔是运行期偏好,直接改写 profile 的 YAML 补丁风险更高;插件用户层 + 部署基线两层合并更可诊断。
- 为什么归档按分类拆分:可只恢复/只下载/只推送某一类;sessions 默认关闭,因为它体积最大。
- 为什么手写 USTAR 与 Typert 描述符:插件不引入任何第三方运行时依赖,安装后不需要构建;描述符与官方生成物同构,由官方 loader/registry 校验。
- 远端方法名避开载体保留字:客户端载体的命名空间服务自带 remove、has、install、 installDirect、installScoped、assertMethodAvailable 以及 ctx/name/namespace/methods/ empty/invokeRemote 等成员,同名远端方法会在挂载时被拒绝(method "backup/remove" conflicts with its namespace service)。因此本插件的删除端点在线上叫 deleteBackup;test/plugin.test.mjs 里有一条断言锁死这组保留名。
- 服务一律用 ctx.get(...) 读,不用属性读:Cordis 的 ctx 是代理,ctx.webServer / ctx.tools / ctx.remote.backup 这类属性读只在“该名字已注入到本 fiber”时才合法,否则会在激活之后抛 cannot get property "…" without inject。本插件因此:
- inject 里不声明可选服务,改用 ctx.inject([...], cb) 的子 fiber 去等待。
webServer 是典型:真实启动里它比本插件晚约 0.8 秒才可用,早先在 apply 里直接读会让下载路由静默失效(页面因此整体读不到状态);工具注册表同理,只在可用时挂载。
- 其余场合统一 ctx.get(name)(免注入读取),例如工具注册表与已挂载的 remote.backup。
test/plugin.test.mjs 与 test/client.test.mjs 都用真实 Cordis 上下文跑激活,所以这两类错误会被测试直接抓住。

许可

MIT
预览分享卡片长什么样 ↓shiinakaze/dsh-plugin-backup
分享 · DeepSeek Harness Hub
shiinakaze/dsh-plugin-backup

按分类打包配置与数据,随时校验恢复并同步 GitHub

这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。
认领条目 →
回到顶部