npm.io
1.0.1 • Published 5h ago

pi-webdav-backup

Licence
MIT
Version
1.0.1
Deps
0
Vulns
0
Weekly
0
Stars
1

pi WebDAV 备份扩展

把 pi 的插件、扩展、模型配置自动备份到任意 WebDAV 服务(坚果云 / Nextcloud / 群晖 / 自建 rclone)。

零第三方依赖,只用 Node 内置模块。密钥默认不明文落盘(DPAPI / 环境变量 / 命令)。


安装

一行命令(pi 官方包机制):

pi install git:github.com/ddldxiacan/pi-webdav-backup@v1

只想试一下、不安装(仅本次运行生效):

pi -e git:github.com/ddldxiacan/pi-webdav-backup

其他方式:

pi install npm:pi-webdav-backup            # 已发布到 npm 时
pi install /path/to/pi-webdav-backup       # 本地目录(下载 ZIP 解压后)

手动安装:把 extensions/webdav-backup.tsextensions/webdav-backup/ 拷到 ~/.pi/agent/extensions/

装好后在 pi 里运行 /reload 加载。

本包带 pi-package 关键字,会出现在 pi.dev 包画廊

运行时产生的文件:

~/.pi/agent/webdav-backup.json        ← 备份配置(/backup-setup 生成)
~/.pi/agent/secrets.dpapi.json        ← DPAPI 加密的密钥(自动创建,0600)

快速开始

/backup-setup       # 向导:填地址、账号、密码(默认 DPAPI 加密保存),自动测连接
/backup             # 立即备份
/backup verify      # 体检:密钥来源 / 明文告警 / 连接性
/backup check       # 单独测连接
/backup list        # 看远端已有备份
/backup restore     # 选一个备份恢复到临时目录

配置写入 ~/.pi/agent/webdav-backup.json


命令一览

命令 作用
/backup 立即备份(同步执行,完成后通知)
/backup check 测试 WebDAV 连通性与认证
/backup list 列出远端备份文件
/backup dry 试运行:只扫描统计,不上传
/backup status 查看上次备份时间
/backup prune 清理旧备份(保留 keepVersions 个)
/backup restore 交互选择备份并恢复到临时目录
/backup verify 配置体检:密钥来源、明文告警、连接性
/backup keys list 列出已加密保存的密钥
/backup keys set <名称> 用 DPAPI 加密保存密钥
/backup keys rm <名称> 删除密钥
/backup keys migrate 把配置里的明文密钥迁成加密引用
/backup-setup 配置向导(默认走 DPAPI 加密)
/backup-cron [HH:MM] 安装每日定时备份(Windows 计划任务)
/backup-cron remove 移除定时任务
/backup-log 查看最近 20 行日志

命令行也可直接用:

node ~/.pi/agent/extensions/webdav-backup/cli.mjs backup --json
node ~/.pi/agent/extensions/webdav-backup/cli.mjs doctor --json
node ~/.pi/agent/extensions/webdav-backup/cli.mjs migrate --method dpapi
node ~/.pi/agent/extensions/webdav-backup/cli.mjs restore --list
node ~/.pi/agent/extensions/webdav-backup/cli.mjs restore --file pi-agent-20260924-120000.tar.gz --to D:\restore

密钥不明文落盘

remote.passwordencryptKey 不直接写明文,而是写引用

语法 说明 适用
dpapi:名称 推荐。Windows DPAPI 用户级加密存入 secrets.dpapi.json,本机本用户可解,无需主密码 Windows
$ENV_VAR / ${ENV_VAR} 环境变量(也支持 $A-$B 拼接,$ 转义) 跨平台
file:路径 从文件读取(自动去换行;相对路径基于 ~/.pi/agent 跨平台
!命令 执行命令取 stdout(可对接密码管理器 CLI) 跨平台
plain:值 显式声明明文(会告警,仅调试用)
其他值 视为明文(会告警

该语法与 pi 自身的 resolve-config-value 约定一致,auth.jsonkey 字段也支持 $ENV_VAR!命令

推荐做法
{
  "remote": {
    "url": "https://dav.jianguoyun.com/dav/",
    "username": "you@example.com",
    "password": "dpapi:webdav-password"      // 不是明文
  },
  "encrypt": true,
  "encryptKey": "dpapi:backup-key"           // 不是明文
}

密钥本身这样存:

/backup keys set webdav-password      # 提示输入,DPAPI 加密入库
/backup keys set backup-key
/backup keys list                     # 只看名称与时间,不显示明文

已有明文配置?一条命令迁走(自动备份原文件):

/backup keys migrate

非 Windows 或偏好环境变量:

node cli.mjs migrate --method env    # 写入用户环境变量(setx)
node cli.mjs migrate --method file   # 写入 0600 权限的 secrets/ 目录
体检
/backup verify

输出密钥来源、是否明文、DPAPI 是否可用、连接是否通过。有明文会直接标 。

DPAPI 的边界:防御的是「配置文件/备份被拷走或误传到云端」。不防御已经能以你的身份运行代码的本机恶意程序。若需更强隔离,用 !命令 对接外部密码管理器。

环境变量方式也支持

不想改配置文件时,仍可用环境变量(优先级低于配置里的值):

setx PI_WEBDAV_BACKUP_PASSWORD "你的应用密码"
setx PI_WEBDAV_BACKUP_KEY "你的加密口令"

备份什么

默认备份 ~/.pi/agent/除以下内容外的所有文件:

排除项 原因
sessions/** 会话历史,通常很大(可用 includeSessions 打开)
tmp/**web-search-cache/** 缓存
**/node_modules/** 可重装
**/*.log 日志

包含:extensions/(你写的扩展)、models.jsonmodels-store.jsonsettings.jsontrust.jsonauth.jsonnpm/package.json 等。

敏感文件脱敏(重要)

未加密备份时,auth.jsonwebdav-backup.json 会先脱敏再上传:

  • 按字段名匹配:key / token / secret / password / credential / cookie / private
  • 并按值的形状匹配sk-…ghp_…AIza…、JWT、32 位以上随机串
  • 命中替换为 __REDACTED__,结构保留
  • 解析失败时整文件替换为占位符,绝不原样上传

脱敏是「尽力而为」的防泄漏措施。真正的安全做法是开启 encrypt


配置项

{
  "enabled": true,

  "remote": {
    "url": "https://dav.jianguoyun.com/dav/",   // 见下方各服务填法
    "username": "you@example.com",
    "password": "dpapi:webdav-password",        // 密钥引用,见上文;留空则读 $PI_WEBDAV_BACKUP_PASSWORD
    "remoteDir": "pi-backup",                    // 远端目录
    "remoteName": "pi-agent"                     // 文件名前缀
  },

  "scope": ["."],                  // 相对 ~/.pi/agent 的路径,可写 ["extensions", "models.json"]
  "exclude": [],                   // 额外 glob 排除规则
  "includeSessions": false,        // 是否备份会话历史
  "snapshot": false,               // 见下文「两种模式」

  "encrypt": false,                // AES-256-GCM 加密
  "encryptKey": "dpapi:backup-key",  // 密钥引用(≥16 位),或 $PI_WEBDAV_BACKUP_KEY

  "backupOnExit": true,            // 退出 pi 时自动备份
  "backupOnExitMinIntervalMinutes": 30,   // 两次自动备份最小间隔
  "backupOnExitTimeoutMs": 600000,        // 自动备份等待上限

  "keepVersions": 10,              // 保留最近 N 个归档(0 = 不清理)
  "timeoutMs": 60000,
  "insecureTls": false             // 自签名证书时才设 true
}
各服务 URL 填法
服务 url username password
坚果云 https://dav.jianguoyun.com/dav/ 注册邮箱 应用密码(账户信息 → 安全选项 → 添加应用密码)
Nextcloud https://云地址/remote.php/dav/files/<用户名>/ 用户名 登录密码或应用密码
群晖 WebDAV https://NAS地址:5006/<共享文件夹> DSM 账号 DSM 密码
自建 rclone serve http://主机:端口/ --user 指定的值 --pass 指定的值

密码建议用 dpapi: 引用而非明文,见下方「密钥不明文落盘」:

/backup keys set webdav-password
# 然后在配置里写 "password": "dpapi:webdav-password"

也可用环境变量:setx PI_WEBDAV_BACKUP_PASSWORD "你的应用密码"


两种备份模式

归档模式(默认,snapshot: false

每次打包成一个 pi-agent-<时间戳>.tar.gz(可选 .pibak 加密)上传。

  • 单个文件,下载/归档方便
  • 天然有版本历史
  • 每次全量上传
快照模式(snapshot: true

逐文件上传到 pi-backup/pi-agent/,用远端 manifest.json 做增量比对。

  • 只传变化的文件,第二次起非常快
  • 能单独恢复某一个文件
  • 只保留最新状态,无历史版本

建议:日常用快照模式(省流量),配合每周一次归档(可另配一个 remoteName)。


退出自动备份怎么工作

session_shutdownreason: quit)触发时:

  1. 检查 enabled / backupOnExit
  2. 距上次自动备份不足 backupOnExitMinIntervalMinutes → 跳过(避免反复开关 pi 重复上传)
  3. 否则 spawn 一个分离子进程执行上传,立刻返回

用分离进程的原因:pi 退出时若被 SIGTERM/关窗打断,留在主进程的上传会被中断;分离进程不受影响,且不会卡住退出。

日志写在 ~/.pi/agent/webdav-backup.log

每日定时备份
# 在 pi 里:/backup-cron 03:00     或命令行:
node "$env:USERPROFILE\.pi\agent\extensions\webdav-backup\cli.mjs" install-cron --time 03:00

需要管理员权限(创建计划任务)。移除:/backup-cron remove


恢复

/backup restore          # 选择备份 → 恢复到临时目录

默认恢复到临时目录,不会直接覆盖 ~/.pi/agent —— 因为恢复旧配置可能覆盖你当前的密钥。

恢复后自行拷贝需要的文件:

# 归档恢复结果示例
# C:\Users\...\Temp\pi-restore-XXXX\restored\extensions\...
copy "$env:TEMP\pi-restore-XXXX\restored\extensions\*.ts" "$env:USERPROFILE\.pi\agent\extensions\"

指定目标目录(谨慎):

node cli.mjs restore --file pi-agent-20260924-120000.tar.gz --to D:\pi-restore
加密备份的恢复

需要 encryptKey 一致(配置文件或 $PI_WEBDAV_BACKUP_KEY)。密钥不对会明确报错,不会写出损坏文件。

解包过程会拒绝绝对路径与 .. 穿越,防止恶意归档写出到目标目录之外。


测试

cd pi-webdav-backup
npm test
# 等价于:node extensions/webdav-backup/run-tests.mjs

扩展契约校验(模拟 pi 加载器导入入口文件):

node extensions/webdav-backup/verify-load.mjs

6 个测试文件、284 项断言,用内置的内存 WebDAV 服务端做真实 HTTP 往返:

文件 覆盖
test.mjs 收集/排除、归档、加密往返、快照增量、prune
test-cli.mjs 真实子进程调用 CLI、退出备份、DPAPI 端到端、doctor、日志、状态
test-restore.mjs 归档/加密/快照恢复、目录穿越防护
test-redact.mjs 脱敏(含「字段名无关但值是密钥」的回归用例)
test-secrets.mjs dpapi/$ENV/file/!命令/plain 各分支、明文告警、三种迁移方式
test-auth-migrate.mjs auth.json 明文密钥审计与迁移(迁移前自动备份、可回滚)

故障排查

现象 处理
401 认证失败 坚果云必须用应用密码;确认 username 是邮箱
404 路径不存在 检查 url 结尾斜杠与路径;Nextcloud 要带 /remote.php/dav/files/<用户名>/
403 拒绝访问 远端目录权限不足;坚果云需先在网页版建好目录
TLS 证书错误 自签名证书才设 insecureTls: true(有中间人风险)
备份很慢 改用 snapshot: true;或把 includeSessions 关掉
退出时没备份 webdav-backup.log;确认 backupOnExit: true 且距上次已过间隔
想立刻看到日志 /backup-log
DPAPI 解密失败 密文只能在本机本用户下解;换机器/换用户需重新 /backup keys set
提示「环境变量未设置」 /backup keys set 改用 dpapi,或先设好该环境变量
配置体检 /backup verify 一次看全部状态

Keywords