dsh-packer
Agent 配置打包器:把 Skills、会话、已装插件和设置封成一个 zip,换电脑时一键开箱;敏感内容先脱敏或加密。
简体中文 · English
v0.7.2 · MIT License · DSH 0.2.x(>=0.2.0-rc.2 <0.3.0-0,0.2.1-rc.3 桌面版实测)· Node ≥ 22.19.0
dsh-packer 是 DeepSeek Harness(DSH)的「Agent 配置打包器」插件:把本地 Agent 资产按模块打包成标准 zip,用于两种场景:
- 迁移 —— 换机器或重装后,把整套环境完整搬过去。
- 分享 —— 把 Skills 等资产交给别人,敏感内容自动拦截。
打包内容可按模块自由组合(Skills / 会话 / 附件 / Profile / 设置 / 预设 / 记忆),恢复带差异对比与冲突策略,隐私安全扫描全程护航——全部能力按需开启。
功能特性
| 特性 | 说明 |
|---|
| 模块化打包 | 九个模块任意组合:skills / sessions / attachments(会话里图片/附件的字节,迁移默认带上)/ profiles / plugins(已安装插件的本体,默认迁移带上)/ settings / presets / memory(Markdown 镜像)/ memoryDb(记忆真库 SQLite 一致快照,默认带上) |
| 双模式预设 | 迁移(全选,含已装插件本体与会话附件)/ 分享(只勾 Skills,自动排除会话、附件、记忆、插件与个人 skill 子目录) |
| 隐私扫描 + 真处理 | 检测盘符 / Unix / UNC 路径、用户目录路径、疑似密钥赋值、密钥形状(sk-、ghp_、AKIA、JWT)、个人昵称;命中后不再只是提示,打包页给两选:清理后打包(只改包内副本:路径→占位符、密钥→<已移除的密钥>,源文件不动;分享模式强制)/ 不清理直接打包(保真,靠口令保护;含凭据又未加密时明确告警) |
| 脱敏安全兜底 | 单遍最左扫描 + 结构字符排除 → 不会吃掉引号/括号/逗号;.json/.jsonl 脱敏后真跑解析校验、行数缩进必须不变,否则整文件回退原文并记 warning;非 UTF-8 文件不脱敏。限制:YAML 无解析器证明(零依赖),极端写法仍可能产生语义差异 |
| 文件级操作预览 | 打包前预览完整文件清单;恢复前差异报告(新增 / 变更 / 相同 / 跳过) |
| 恢复冲突三选 | 覆盖 / 跳过 / 内容合并(合并 = 追加,绝不覆盖已有内容) |
| 恢复安全写入 | 先备份目标到 packs 目录下的 .restore-backups/<时间戳>/,写临时文件后 rename 原子替换;任一环节失败即中止并回滚已替换/已新增的文件 |
| 清单完整性校验 | manifest.json 记录 schemaVersion + 每个文件 SHA-256 指纹;恢复前校验源文件与清单一致,缺指纹或指纹格式非法一律拒绝(fail-closed) |
| 包管理与备注 | 包列表(时间 / 大小 / 模块 / 备注)、删除、重命名、打包时填写备注 |
| 分享包自动附 README | 自动生成并附带说明包内容的 README.md |
| 深色模式适配 | 设置页跟随 DSH 主题(--dsw-alias-* 令牌 + 品牌琥珀双色板,双通道探测) |
| 零原生依赖 | 纯 JS 读写标准 zip(zip.mjs + node:zlib),不调用 tar/unzip 等外部命令,任何解压工具可打开 |
| 选择性恢复 | 恢复前可按模块勾选(只恢复 Skills、不动会话),includeDb 单独开关控制是否恢复记忆真库 |
| 恢复前预览 | 导入即算差异:逐文件列出 新增 / 变更 / 相同 / 跳过,并在执行后报告备份目录 |
| 一键回滚 | /restore/backups 列出每次恢复前的备份;/restore/rollback(设置页「回滚」按钮)把该次覆盖的文件原样还原 |
| 包管理 | 查看包内文件清单并顺带校验 SHA-256、重命名、删除(删除送当前平台的系统回收站:Windows 回收站 / macOS 废纸篓 / Linux Trash,均可找回;鸿蒙无回收站,删除前会明确提示不可恢复) |
| 加密包 | AES-256-GCM + scrypt 口令加密(PKENC1 容器),加密包只在提供口令时才能读清单/恢复;口令错误 fail-closed |
| 进度与取消 | 打包/恢复都是后台作业(/jobs/:id),界面显示 done/total 百分比,支持中途取消(文件边界生效,不残留半成品) |
安装
桌面版:按包名安装(推荐)
- 打开 DSH 桌面版 → 左侧 插件 → 加插件;
- 在包名输入框填
dsh-packer,点 安装(安装完自动启用)。
命令行等价:
# 把 desktop 换成你的 profile 名
dsh plugin --profile desktop add dsh-packer
从 GitHub 安装(备选)
# 需要已安装 git
dsh plugin --profile desktop add github:KLRSL/dsh-packer
本地 bundle(开发 / link)
# 在项目目录下执行
dsh plugin --profile desktop add link:./dsh-packer
dsh plugin 会自动把安装的包登记到 profile 的 dsh.profile.bundles 并挂载补丁;安装完成后重启 DSH。
安装后验证:
- 打开 设置页 →「配置打包」标签页——能看到模块勾选界面与包列表,即安装成功;
- 或在终端运行
/pack list——返回包列表(首次使用为空列表或提示暂无包),即命令已注册;
- 再跑一次
/pack create --dry-run——不生成真实 zip,仅预览文件清单与扫描结果,确认各模块路径可读。
快速开始
打包(三步)
# 1. 创建迁移包(默认全选模块)
/pack create --note "迁移到新机器"
# 2. 或创建分享包(只含 Skills,自动排除敏感内容)
/pack create --share --note "分享给朋友"
# 3. 先预览再打包(不生成 zip)
/pack create --mode migrate --dry-run
打包完成后,zip 写入 ~/.dsh/packs/(DSH_PACKS_DIR 可覆盖);分享包自动附带 README.md。
恢复(三步)
# 1. 导入包
/pack restore ~/.dsh/packs/dsh-packer-2026-09-05-223045-migrate.zip
# 2. 查看差异报告(新增 / 变更 / 相同 / 跳过)
# 3. 指定冲突策略:overwrite | skip | merge
/pack restore <zip路径> --strategy merge
恢复前自动校验 manifest.json(schemaVersion + 每条指纹的合法性 + 每个源文件的 SHA-256),确认差异报告后按策略应用;写入前先备份、rename 原子替换,失败即中止回滚。完成按需重启 DSH。
打包模块
| 模块 | 内容 | 迁移预设 | 分享预设 |
|---|
skills | Skills(含记忆机制 skill),位于 ~/.dsh/skills | ✅ | ✅ |
sessions | 会话记录(.zstd 格式),位于 ~/.dsh/sessions | ✅ | ❌ |
attachments | 会话附件对象库:会话里的图片/文件字节(内容寻址,位于 ~/.dsh/attachments/v1/objects;不含可重建的请求图片缓存与 tmp 暂存) | ✅ | ❌ |
profiles | Profile 配置(不含 node_modules),位于 ~/.dsh/profiles | ✅ | ❌ |
plugins | 已安装插件本体(profiles/<profile>/node_modules 的一级包目录),体积大 | ✅ | ❌ |
settings | 全局设置(settings.yaml) | ✅ | ❌ |
presets | Agent 预设(.agent-presets) | ✅ | ❌ |
memory | 记忆镜像(Markdown 只读副本,位于 DSH_MEMORY_ROOT 或 ~/.dsh/memory;不含 backups/) | ✅ | ❌ |
memoryDb | 记忆真库(<DSH_BIOMEMORY_DIR || ~/.dsh/biomemory>/biomemory.db):打包时用 VACUUM INTO 取一致快照(只读连接、不碰运行中的 WAL),快照失败则该文件不进包并计入 unreadable | ✅ | ❌ |
会话里的图片在 attachments:会话日志里的图片只是 attachmentId 引用(sha256:<64hex>),字节在 ~/.dsh/attachments/v1/objects。只备份 sessions 的话,恢复后的会话里图片会裂(能打开历史,但图取不到字节);设置页选**「会话记录」组**(= sessions + attachments)会一起打包。对象是内容寻址的,目标机不需要改写任何引用。
双模式预设:
- 迁移 —— 全部模块默认勾选,适合搬迁整套环境。
- 分享 —— 只勾
skills;会话、附件与记忆数据自动排除,个人 skill 子目录(_shared)在分享时也会排除,让敏感内容尽可能少进包。
隐私与安全
扫描规则(扫描范围:已知文本后缀的文件 + 无扩展名但确定是文本的文件,如 .env;含 NUL 字节的二进制文件跳过):
| 规则 | 说明 |
|---|
| 本地绝对路径(盘符) | 盘符形式路径,如 D:\...、C:/... |
| Unix 家目录路径 | /home/<名>/...、/Users/<名>/...、/root/...——只认家目录(路径里含「这是谁的目录」)。/usr、/etc、/var、/opt、/srv、/mnt、/media、/tmp 是系统公共路径,不含个人信息,v0.7.0 起不再计入命中、也不再脱敏 |
| UNC / 网络路径 | \\服务器\共享\... |
| 用户目录路径 | 操作系统用户配置文件目录下的路径(C:\Users\<名>、/home/<名>、/Users/<名>);家目录之前的挂载前缀也一并吃掉(/System/Volumes/Data/Users/<名>/…、/Volumes/<卷名>/Users/<名>/…、`/mnt |
| 挂载卷 / 派生路径 | /Volumes/<卷名>、/System/Volumes/Data、/private、/nix/store——这些是本机特有的(卷名由用户自定、/private 含逐机不同的哈希、/nix/store 暴露本机包布局),与 /usr /etc 这类任何机器都一样的系统公共路径不同,替换为 <本机路径> 并保留子路径 |
| 疑似密钥 / Token 赋值 | api_key、access_key、secret、password、token、bearer、authorization、credential 等赋值——带引号或不带引号都算 |
| 密钥形状 | 裸密钥本身:sk-...、ghp_...、github_pat_...、glpat-...、AKIA...、xox?-...、JWT(eyJ.....*.*) |
| 个人昵称 | 用户昵称文本(由部署者通过 config.personalPatterns 注入) |
计数口径:同一行内同一规则出现多处会如实累计(不会每规则只算 1 处);同一处若被多条规则命中,各规则各自上报一次(扫描宁可多报,不漏报)。注意扫描与脱敏口径不同:脱敏是单遍最左扫描 + 区间去重,同一个片段只替换一次,不会出现占位符套占位符。
扫描缓存:界面「先预览」与「开始打包」打的是同一个 /create,后端按「文件名 + 内容 SHA-256」缓存扫描结论,同一批内容不会被整篇读两遍;内容一变指纹就变、立即重扫,所以这是性能优化而不是正确性妥协(没有 SHA-256 的调用不进缓存;部署者改动个人敏感词后缓存整体失效)。
拦截策略:
- 分享模式 —— 命中不拦截,但强制脱敏:忽略界面上传入的
sanitize 值一律按「清理后打包」处理(路径→占位符、密钥→<已移除的密钥>,只改包内副本、源文件不动)。分享包是给别人用的,宁可改内容也不放行原文。分享模式下真正会打包失败的是两类:勾了分享预设不允许的模块(只有 skills 允许,其余一律返回 share_scope);有文件读不出内容、没能经过扫描(fail-closed,明确列出是哪些文件)。
- 迁移模式 —— 仅报告不拦截,可用
/pack scan 或 --dry-run 提前查看命中点,自行判断;也可以主动选「清理后打包」。
永不打包:.credentials.yaml、.anonymous-user-id——遍历任何模块时一律跳过,这类本地身份/凭据文件不允许进入任何包(无论迁移还是分享)。
其他安全措施:
- 每个文件的 SHA-256 指纹写入
manifest.json,恢复时用于完整性校验(fail-closed);清单条目缺指纹或指纹格式非法一律拒绝,不写入指纹的文件在打包时被跳过并如实上报(绝不写空指纹)。
- 恢复对源路径与目标路径双向做 containment 校验:源必须在解压目录内,目标必须落在模块目标根内(zip-slip / 篡改清单的
../ 越界一律拒绝);绝对路径、盘符路径、UNC 路径、.. 片段在恢复前就被白名单拒绝。
- 解包前先解析 zip 中央目录做白名单校验(拒绝绝对路径 / 盘符 / UNC /
..),并拒绝符号链接、硬链接等非普通文件类型;解压后再扫一遍解压结果,出现符号链接即拒绝。临时解压目录统一在 try/finally 中清理(成功、失败都不残留)。
- 恢复前先备份目标(
<packs>/.restore-backups/<时间戳>/),写临时文件后 rename 原子替换;任一环节失败即中止并回滚本次已替换/已新增的文件。
- SQLite 库的处理分两侧:打包走
VACUUM INTO 一致快照(memoryDb 模块,v0.2.6 起)——不直接复制被 DSH 持有的 WAL 库,取不到就跳过并计入 unreadable;恢复默认仍跳过 *.db*(includeDb: true 才写入),避免覆盖目标机上正在使用的记忆库。
记忆怎么迁移
v0.2.6 起,记忆真库默认就在包里。 记忆有两个模块,职责不同:
| 模块 | 内容 | 迁移够不够 |
|---|
memoryDb | `<DSH_BIOMEMORY_DIR | |
memory | DSH_MEMORY_ROOT 下的 Markdown 只读镜像,供人查看 | ⚠️ 只是可读副本 |
快照用只读连接打开源库,产出的是 SQLite 自己保证一致的文件,因此不必先停 DSH。
取不到快照(库被独占、磁盘满等)时该文件不进包,并如实出现在 unreadable 列表里——绝不退化成“直接复制运行中的库”。
恢复时 memoryDb 默认不写入(避免覆盖目标机正在使用的记忆库):需要时用 includeDb: true(命令行 /pack restore --include-db,接口参数同名字段)。
只搬镜像不搬真库=目标机没有记忆本体。
- 打包文件名带唯一后缀(
dsh-packer-<时间戳>-<随机>-<模式>.zip),同一秒内多次打包不互相覆盖。
- 打包使用纯 JS zip 实现(zip.mjs + node:zlib)生成标准 zip,零原生 npm 依赖。
- 文件与子进程操作全部异步(
node:fs/promises + execFile),哈希与复制走有界并发(默认 16 路),大批量打包不会卡住 DSH 的事件循环。
设置页 Web API(/packer/api/*)的鉴权与限额
设置页与 /pack 命令等价,走插件自己注册的前缀路由 /packer/api/*。这条路由的默认姿态是 fail-closed——判定顺序为「速率限制 → 鉴权 → 体积 → 路由」,前三步都在读取请求体之前完成:
| 项目 | 默认 | 行为 |
|---|
| 鉴权 | api.authMode: 'auto' | 只用官方机制:ctx.get('connection')(dsh-client-connection)的 requestRejection(req)——与官方 /api 通道同一套 Host/Origin 信任 + 浏览器会话鉴权。返回 401 → 401,403 → 403,只有 undefined 才放行 |
| 拿不到鉴权服务 | 硬拒绝 403 | connection 服务缺失、没有 requestRejection、或该调用抛错,一律 403 并说明原因;不会静默降级到更弱的通道(要用插件自有令牌,必须显式选 authMode: 'token') |
| 速率限制 | 60 次 / 60 秒 | 最外层:按客户端地址(remoteAddress)滑动窗口,先于鉴权与读体判定,未授权流量同样吃配额。超限回 429 + retry-after;键数量有上界(无定时器、内存有界) |
| 请求体上限 | 8 MB | 在完整缓冲请求体之前判定:有 Content-Length 先比对(超限直接 413,一个字节都不读);没有长度则边读边累计,一超限立刻解绑监听、暂停连接并回 413 |
| 错误文案 | 出网脱敏 | 服务端绝对路径(盘符 / UNC / Unix 用户目录)统一替换为 <路径已隐去>,不回显本机目录结构 |
api 配置项(apply(ctx, { api: { ... } })):
| 选项 | 默认 | 说明 |
|---|
authMode | 'auto' | 'auto':只认官方 connection,缺失即 403;'token':显式 opt-in 的插件自有回退——Host / Origin / Sec-Fetch-Site 同源校验 + 一次性令牌(apply 时随机生成,经 webServer.tapIndex 注入同源 index.html,前端自动带 x-dsh-packer-token 头),用于宿主确实没有 connection 的场合;'off':不安全,仅隔离测试用,真实部署别开 |
maxBodyBytes | 8388608 | 请求体上限(字节) |
rateLimit / rateWindowMs | 60 / 60000 | 速率限制次数与窗口(毫秒) |
token | 随机生成 | 仅 authMode: 'token' 生效;显式传入便于固定令牌/多实例场景 |
authMode: 'off' 与显式传入的固定 token 都会把安全责任交给部署者,README 记录其存在只为「知道自己选了什么」,不代表推荐。
恢复与差异
恢复流程:
- 导入 zip —— 设置页选择文件,或
/pack restore <zip路径>。
- 解包校验 —— 先列包内成员做白名单校验(绝对路径 / 盘符 / UNC /
.. / 链接类成员一律拒绝),再读 manifest.json。
- manifest 校验 ——
manifest.json 存在、schemaVersion 与当前版本兼容、每条清单条目的 SHA-256 指纹存在且合法、每个源文件哈希与清单一致;任一不匹配即 fail-closed 拒绝。
- 差异报告 —— 新增 / 变更 / 相同 / 跳过 四类计数与文件清单。
- 冲突策略三选:
overwrite —— 用包内内容覆盖目标文件(默认);
skip —— 保留目标文件,跳过冲突项;
merge —— 文本文件把包内内容追加到目标文件末尾(带分隔注释),已有内容绝不覆盖;非文本文件退化为覆盖。
- 备份 → 原子替换 → 失败回滚 —— 每个被覆盖/追加的目标先备份到
<packs>/.restore-backups/<时间戳>/,包内内容先写成临时文件再 rename 原子替换;任一环节失败即中止,并按记录把本次已替换/已新增的文件回滚回去(计数随回滚归零,回滚数单独上报)。
- 应用,按需重启 DSH。
与包内完全一致的文件在任何策略下都会自动跳过。JSON / YAML 等结构化配置不支持 merge(追加即损坏),请使用覆盖或手工合并。运行中的 SQLite 库(*.db*)默认跳过不恢复(避免覆盖记忆数据)。
/pack 命令参考
/pack list # 列出已有包(含 [加密] 标记)
/pack create [--modules skills,memory] [--mode migrate|share] [--note 备注] [--dry-run] [--encrypt 口令]
/pack create --share # --share 是 --mode share 的简写
/pack restore <zip路径> [--strategy overwrite|skip|merge] [--modules a,b] [--include-db] [--password 口令]
/pack scan # 对所有可打包模块做隐私扫描
| 命令 | 参数 | 说明 |
|---|
list | — | 列出已生成包:创建时间、大小、模块、备注、是否加密 |
create | --modules a,b 按名选择模块;--mode migrate|share(默认 migrate);--share 简写;--note 备注;--dry-run 仅预览;--encrypt 口令 | 不指定 --modules 时按模式预设自动选择(migrate = 全选;share = 只选 Skills) |
restore | <zip路径>;--strategy overwrite|skip|merge;--modules a,b 只恢复指定模块;--include-db 连记忆真库一起恢复(需先停 DSH);--password 口令 | 导入 → 校验 → 差异 → 按策略应用;加密包必须给口令 |
scan | — | 对所有可打包模块执行隐私扫描,报告敏感痕迹 |
包完整性校验不再单独开命令:设置页「查看」会在列出文件的同时重算 SHA-256,不一致项直接标红。
输出目录:打包结果写入 ~/.dsh/packs/(DSH_PACKS_DIR 可覆盖);文件名形如 dsh-packer-<时间戳>-<随机后缀>-<模式>.zip(唯一后缀保证同秒多次打包不互相覆盖),每次打包还会在同目录生成一个同名 .json 摘要文件(记录时间 / 模块 / 备注 / 文件数),供包列表与快速识别使用。恢复时的目标备份写入同目录下的 .restore-backups/<时间戳>-<随机后缀>/(可随时删除,列表与本插件都不会读它)。包列表、删除、重命名也可在设置页「配置打包」标签页操作。
配置与环境变量
| 环境变量 | 默认值 | 说明 |
|---|
DSH_PACKS_DIR | ~/.dsh/packs | 打包输出目录 |
DSH_MEMORY_ROOT | ~/.dsh/memory | memory 模块数据位置 |
DSH_HOME | ~/.dsh | DSH 数据根目录(各模块路径基准) |
设置页按「配置打包器」的任务链组织成一页五个区块:已有的配置包 → 打包内容 → 打包选项 → 恢复(含差异对比)→ 结果明细。包信息、完整性校验、改名、删除(送当前平台的系统回收站,鸿蒙为永久删除并先提示)都在包列表行上;恢复前先看差异对比,选模块与策略后执行,完成页给出备份目录与一键撤回。界面中文单语、无图标字符,状态一律文字表达。
兼容性
- Node.js ≥ 22.19.0
- 多平台通用:Windows / macOS / Linux / 鸿蒙 共用同一套代码与包格式;回收站按平台分派(Windows 回收站 / macOS 废纸篓 / Linux Trash),鸿蒙仍有两项受限(回收站、系统文件选择框),原因与矩阵见 docs/platform-support.md。
- DSH 版本闸门:
engines.dsh = >=0.2.0-rc.2 <0.3.0-0(0.2.1-rc.3 桌面版实测)。DSH 的兼容性检查带 includePrerelease,预发布号参与范围比较;超出该范围的 DSH 会在安装前被插件管理器拒绝。
- peer 依赖:
@deepseek-ai/cordis ^4.0.2(插件生命周期基准,由宿主提供);@deepseek-ai/dsh-tools / @deepseek-ai/dsh-session 范围同 >=0.2.0-rc.2 <0.3.0-0,且未被 index.mjs 直接 import——本插件只使用 ctx.commands / ctx.webServer / ctx.slots 等宿主内建服务,故这两项在 peerDependenciesMeta 中标记为 optional: true。
- 无需外部命令(打包/恢复全程):包由 zip.mjs(node:zlib)直接读写,创建 / 列成员 / 解包都不调用 tar、bsdtar、unzip;任何标准解压工具都能打开生成的包。(只有「删除进回收站」会调系统命令:Windows PowerShell / macOS osascript;Linux 为纯 Node。)
版本历史
| 版本 | 日期 | 类型 | 要点 |
|---|
| v0.7.2 | 2026-10-28 | 修「打包失败(network · Failed to fetch)」误报 | 满量迁移(17469 个文件)时,预览响应约 3.9 MB(其中 manifest 3.5 MB)、终态轮询还要搬 2000+ 条隐私命中;宿主桥一旦在这两个大响应上断开,UI 就报 network · Failed to fetch,而服务端其实已经把包写完了(实测 zip 218 MB / 17470 条目完好)。三处修正:①预览只回前 1000 条文件明细 + filesTotal(面板本来就只渲染前 200 条);②轮询视图把 result.privacy 换成 privacyCount(创建流程只用 pack / warnings);③前端轮询遇到网络错误/5xx 改为退避重试(最多 10 次,≤5 s/次),不再一次抖动就判定整次失败。 |
| v0.7.1 | 2026-10-28 | 测试随包发布 | files 补 tests,新增 tests/privacy.test.mjs(零依赖,node --test):锁死 shebang 原样保留、Unix 系统公共路径不计命中/不脱敏、家目录与 Windows 用户目录必须脱敏、URL 整段不动,以及 SCHEMA_VERSION=1(老包仍可 restore)。此前 manifest 挂了 npm test 但 tgz 不带 tests,装完就跑不了。 |
| v0.7.0 | 2026-10-27 | 工程化重构 + 隐私口径修正 + 界面无障碍 | ①后端分层:index.mjs 3204 → 306 行(只做装配与再导出),拆出 lib/{constants,errors,logger,util,modules,privacy,recycle,archive,pack,restore,jobs,api,cli}.mjs,隐私规则收敛为单一规则源(扫描与脱敏共用一份,不再各写一套正则)。②隐私口径修正:Unix 路径规则收窄为只认家目录(/home /Users /root),/usr /etc /var /opt /srv /mnt /media /tmp 等系统公共路径不含个人信息,不再误报也不再误脱敏(此前 #!/usr/bin/env node 的 shebang 会被换成 #!<Unix路径> node,脚本直接报废);shebang 行与 URL 整段纳入保护区。③前端工程化:lib/client.js 由「编译产物当源码维护」改为 TypeScript 源码 + esbuild 可复现构建(src/client/:copy / styles / types / format / api / hooks / model / ui / view/*),npm run typecheck 零错误。④界面无障碍:弹窗补齐 role=dialog + aria-modal + aria-labelledby + Esc 关闭 + Tab 焦点陷阱 + 关闭后焦点归位;进度条为 role=progressbar 且不确定态不上报 ;表头加 ;异步结果用 播报。⑤:ZIP 经典格式 65535 条目上限改为中文语义报错(不再写出损坏包); 的 SQLite 快照目录不再泄漏;补上缺失的 文案。测试 91 → 102(新增产物冒烟与 jsdom 真实渲染两组)。⑥:新增「挂载卷 / 派生路径」规则——、、、 现在会被替换(此前这些前缀要么整段漏掉,要么只替换到家目录为止、留下 这种半脱敏结果);用户目录规则改为连挂载前缀一起吃掉。隐私扫描结果按「文件名 + 内容 SHA-256」缓存,「先预览」+「开始打包」不再把同一批文件扫两遍(内容一变立即失效)。UI 规约文档同步为 v0.7.0 结构(,取代 )。测试 103 → 106。 |
常见问题
生成的 zip 打不开 / 提示损坏?
包是纯 JS 生成的标准 zip,Windows 资源管理器与常见解压工具均可打开。若校验失败,请勿手工解压改动包内容(会破坏 manifest.json 里的 SHA-256 指纹),直接用 /pack create 重新生成。可用 /pack list 查看 ~/.dsh/packs(或 DSH_PACKS_DIR 指向的目录)里有哪些包。
恢复时 manifest 校验失败?
通常是以下几种情况:该 zip 不是 dsh-packer 生成的(包内没有 manifest.json)、manifest.json 缺失或 schemaVersion 与当前版本不兼容、或包生成后被改动过。请重新分发原包或重新生成。
分享包被拦截怎么办?
分享模式不因隐私命中而拦截(命中一律强制脱敏后打包),真正会失败的只有两类:勾了分享预设不允许的模块(只有 skills 允许,其余返回 share_scope);有文件读不出内容、没能经过扫描(fail-closed,响应里会列出是哪些文件)。前者去掉不该分享的模块即可;后者先处理那些文件(权限/占用/损坏)再重试。需要保真备份请用迁移模式(仅报告,不强制脱敏)。
「合并」策略具体怎么工作?
对文本文件:把包内内容追加到目标文件末尾,带分隔注释——已有内容绝不覆盖;非文本文件退化为覆盖;JSON / YAML 等结构化配置不支持合并(追加即损坏),请用覆盖或手工合并。与包内完全一致的文件在任何策略下都自动跳过。
恢复报「已中止 / 已回滚」是什么意思?
恢复是「整批或不做」的:任一环节(路径校验、指纹校验、写盘)失败就立即中止,并把本次已经替换/新增的文件按备份回滚回去——已回滚 N 表示回滚了多少个文件,失败原因在设置页的失败清单里逐条列出。备份留在 <packs>/.restore-backups/ 下,可手工核对。
为什么记忆数据库(*.db*)默认不被恢复?
运行中的 SQLite 库被 DSH / 记忆插件持有,直接覆写可能损坏数据。所以本插件默认不打包也不恢复 *.db、*.db-wal、*.db-shm、*.sqlite,恢复时会把它计入「跳过」并单列原因。
要真的把记忆搬过去:先退出 DSH,在恢复页打开 includeDb 开关(或 /pack restore <包> --include-db)再执行。打包侧的记忆真库来自 VACUUM INTO 一致快照(memoryDb 模块),是完整可用的库。
恢复报「清单条目缺少合法 SHA-256 指纹」?
该包不是 dsh-packer 生成的、或 manifest.json 被手工改过。指纹缺失/格式非法一律拒绝恢复(fail-closed),请重新分发原包或重新生成。
包存在哪里?
默认 ~/.dsh/packs,可用 DSH_PACKS_DIR 环境变量覆盖;每个包在同目录下还带一个同名 .json 摘要文件,便于识别与包列表展示。
开发
npm test # node --test "tests/*.test.mjs",37 个用例全绿
node scripts/release-check.mjs # 发布一致性自检(版本号 / README 版本露出 / files 白名单 / git 状态)
CI:.github/workflows/ci.yml 在 Node 22.x / 24.x 上执行「发布自检 → 单元测试 → npm pack --dry-run」(本包零运行时依赖,无需安装步骤),推送与 PR 都会触发。
License
MIT — 详见 LICENSE。