WorkBuddy 数据同步(workbuddy-sync)
一台机器上配好的东西,换台电脑不用重配,会话也能接着聊。
工具本体随技能一起分发,无需 npm 安装任何依赖(只用 Node 内置模块)。
工具位置与初始化
技能目录下自带工具本体(src/)。命令里统一这样定位,两种安装位置都能跑:
SKILL_DIR=~/.workbuddy/skills/workbuddy-sync
WB=~/.workbuddy/tools/workbuddy-sync # 已安装的稳定位置
[ -f "$WB/src/cli.js" ] || WB="$SKILL_DIR" # 没装过就用技能自带的
node "$WB/src/cli.js" <命令> # 通用入口(Windows 也用它)
"$WB/wbsync.sh" <命令> # Git Bash / macOS / Linux
> 发布包内只有 src/cli.js 与 wbsync.sh 两个入口,不含 .cmd / .bat / .exe
> (技能市场一律拒收这类可执行文件)。Windows 上没有 Git Bash 时,用 node 直接调用。
首次使用先装到稳定位置(斜杠命令与跨设备分发都依赖固定路径):
sh "$SKILL_DIR/scripts/install-to-workbuddy.sh"
再初始化,指定一个两台设备都能访问的共享目录(OneDrive / 坚果云 / U 盘 / 共享盘):
node "$WB/src/cli.js" init --folder ~/OneDrive/WorkBuddySync
对应斜杠命令:/wbsync:status、/wbsync:sync、/wbsync:newdevice、/wbsync:backup。
模板在 references/commands/wbsync/,需要就复制到 ~/.workbuddy/commands/wbsync/。
同步了哪些范围
| 范围 | 内容 | 加密 |
|---|---|---|
mcp |
MCP / API 配置(含密钥) | ✅ |
models |
模型配置 | — |
settings |
全局设置(JSON 键级合并) | — |
tasks |
项目任务 / 待办 | ✅ |
memory |
Agent 长期记忆 | ✅ |
sessions |
完整会话转录(换机继续对话用) | ✅ |
skills commands identity tools |
技能、斜杠命令、身份文件、工具本体 | — |
命令速查
| 意图 | 命令 |
|---|---|
| 看差异(只读,安全) | status |
| 同步一轮 | sync |
| 只推 / 只拉 | push / pull |
| 常驻自动同步 | watch --interval 60 |
| 新电脑接管 | 先 bootstrap 打包 → 新机解压 → sync → sessions import --create-cwd |
| 单文件加密归档 | export / restore <文件> |
| 备份存进 WorkBuddy 资料库 | 见下方「存到 WorkBuddy 资料库」 |
| 归档上传乐享知识库 | 见下方「备份到乐享知识库」 |
| 看同步范围 | roots |
| 清理远端残留对象 | gc(默认预演,--delete 真删) |
| 补齐新版本新增的同步根 | upgrade |
存到 WorkBuddy 资料库(可选)
WorkBuddy 原生的「资料库」有内置 skill,Agent 可直接写入,而且
归档原文件 .wbsync 就能直接收 —— 不必转 Base64、不必改扩展名。
用户说"存到资料库 / 我的资料 / 我的文档"时走这条,不要反问存哪个产品。
目标位置每次现查,不要硬编码任何 ID(ID 因账号而异):
skill=~/.workbuddy/plugins/marketplaces/workbuddy-builtin/skills/library
# 鉴权:ToolSearch {"tool_names":["connect_open_platform"]}
# → DeferExecuteTool {toolName:"connect_open_platform", params:{skill_id:"library"}}
# 1) 定位空间:取 category=personal 的那个 spaceId,记作 $SPACE
printf '%s' "<token>" | python3 "$skill/space_api.py" space.workspace.list-user-spaces --token-stdin
# 2) 定位目录:列根目录找 title=wbsync 的节点,记作 $DIR;没有就建一个
printf '%s' "<token>" | python3 "$skill/space_api.py" space.workspace.list-node --token-stdin
printf '%s' "<token>" | python3 "$skill/doc/create_doc.py" --token-stdin \
--title "wbsync" --content-file "<目录页.md>" --space-id "$SPACE"
# 3) 上传归档(drive 通道)+ 配说明页(doc 通道),都带上空间与目录
printf '%s' "<token>" | python3 "$skill/drive/upload_drive_file.py" --token-stdin \
"<归档绝对路径>" --file-name "wbsync-backup-<日期>.wbsync" \
--space-id "$SPACE" --parent-id "$DIR"
printf '%s' "<token>" | python3 "$skill/doc/create_doc.py" --token-stdin \
--title "workbuddy-sync 备份说明(含恢复步骤)" --content-file "<说明.md>" \
--space-id "$SPACE" --parent-id "$DIR"
--parent-id 必须与 --space-id 一起传;省略就落空间根目录,之后得手动归置。
资料库没有独立的「建文件夹」接口:目录 = 一篇 kind=doc 的文档当目录页,其余节点挂它下面。
重名节点会让目标不唯一,先查再建。原本落在别处的节点用
space.workspace.move-node --node-id X --target-parent-id "$DIR" 搬进来
(不能跨空间:源节点与目标目录 spaceId 必须一致)。
- 分流规则:
.md→doc、.csv→database、.html/.zip→page、其他任意文件→drive(上限 100 MiB) - 写前读
$skill/mutation.md:「我的文档」(personal)由用户原始请求直接授权,
无需二次确认;只有团队空间才停下确认 - 脚本回执的
KS_USER_REPLY原样透传,不要自己拼链接或暴露内部 ID - 资料库没有删除节点的 Agent API,传错了只能去界面手动删
备份到乐享知识库(可选)
乐享(lexiang)连接器可用时,可以把 export 产出的单个加密归档长期存进去。
> ⚠️ 实测:乐享不收压缩包。 .wbsync / .zip / .gz 传上去条目状态都是
> failed / extension_not_support(统一被识别成 extension: "pkg")。
> 必须先把归档转成 Base64 文本,再以 .txt 上传 —— 测过 .txt 正常。
> Base64 只是编码,加密强度不变,存进去的仍是密文。
# 1. 生成归档(单文件)
node "$WB/src/cli.js" export --out ~/wbsync-backup-$(date +%Y%m%d).wbsync
# 2. 转 Base64 文本(体积约 ×1.35,每 76 字符换行)
base64 -w 76 ~/wbsync-backup-$(date +%Y%m%d).wbsync > ~/wbsync-backup-$(date +%Y%m%d).b64.txt
# 3. 乐享 MCP 三步:file_apply_upload(name 以 .txt 结尾, mime_type: text/plain, size 填转码后字节数)
# → curl -X PUT -H "Content-Type: text/plain" --data-binary "@<绝对路径>/xxx.b64.txt" "<upload_url>"
# → file_commit_upload(session_id)
# 4. 校验:entry_list_children 看 status,finished / processing 为成功
几个必须记住的点:
curl的--data-binary "@<路径>"里路径要用本机原生绝对路径(Windows 形如C:/Users/...),
Git Bash 的/tmp/xxx会让 curl 报error encountered when reading a file。- 上传完顺手建一份 markdown 说明页(
entry_import_content,必须同时传parent_id+space_id,
否则报parent entry not belong to space),写明解码 +restore命令和口令位置,
免得日后打开知识库只看到一堆 Base64。 - 写入目标必须由用户明确指定(知识库链接或
entry_id)—— 乐享有安全红线,禁止自行遍历/猜测目标。 - 绝不要上传
~/.workbuddy/.sync/passphrase:那是解密钥匙,归档本身不含它。 - 乐享 MCP 没有删除知识条目的接口,传错东西只能去乐享网页端手动删。
完整流程与实测记录见 references/commands/wbsync/backup.md。
执行纪律(重要)
- 先
status再sync。status是纯预演,只读本机与远端清单,不写任何文件。 - 看到
⛔警告必须停下来,把原文交给用户并解释风险,等确认再继续。
典型警告:批量删除超过一半本地文件、共享目录配错、远端为空。 gc --delete、restore会改远端/本地文件,执行前必须让用户明确确认。sessions import会写数据库,但写前会自动备份workbuddy.db;默认不覆盖本机更新的版本。- 报错时按顺序退让:
node …/src/cli.js→wbsync.sh(Git Bash / macOS / Linux)。
两者都不行才报告工具未安装 —— 包内没有.cmd,别去找它。
换一台新电脑
鸡生蛋问题
本工具的斜杠命令(/wbsync:sync 等)和技能本身也是靠同步分发的,
新电脑第一次 sync 时它们还没到 —— 鸡生蛋。
解法:在老机器上 bootstrap 打一个自包含的引导包(含工具+配置+口令),新机解压 → 跑 sync → 之后命令自然可用。
老机器:打引导包
node "$WB/src/cli.js" bootstrap --out ~/Desktop/wbsync-bootstrap.tgz
# 不含口令版本(走不可信渠道时用):
node "$WB/src/cli.js" bootstrap --out ~/share/wbsync.tgz --no-secret
# 目录形式(方便审阅每个文件):
node "$WB/src/cli.js" bootstrap --out ~/share/bootstrap --no-archive
新机器:解包 → 同步 → 导入
# 1) 解压
mkdir -p "$HOME/.workbuddy"
tar -xzf wbsync-bootstrap.tgz -C "$HOME/.workbuddy/"
# 2) 一次性同步
node "$HOME/.workbuddy/tools/workbuddy-sync/src/cli.js" sync
# 3) 预演会话导入
node "$HOME/.workbuddy/tools/workbuddy-sync/src/cli.js" sessions import --dry-run
# 4) 真正导入(看路径映射决定要不要 --create-cwd / --cwd-map)
node "$HOME/.workbuddy/tools/workbuddy-sync/src/cli.js" sessions import --create-cwd
# 重启 WorkBuddy,会话列表才刷新;命令与技能要新开一个对话才加载
没引导包时(手动搬)
至少要有 ~/.workbuddy/sync.config.json + ~/.workbuddy/.sync/passphrase + 工具本体,
且 folder.path 指向的位置本机也能访问(OneDrive / 坚果云 / 共享盘)。
斜杠命令和技能本身也会同步过去
- 命令目录
~/.workbuddy/commands/**是同步根commands,CLI 从它递归扫描.md,
子目录变成:分层命名(commands/wbsync/sync.md→/wbsync:sync) - 技能目录
~/.workbuddy/skills/**是同步根skills - 工具本体在
~/.workbuddy/tools/workbuddy-sync/**,是同步根tools
新机器上确认到位:ls ~/.workbuddy/commands/wbsync/ 应有 4 个 .md。
命令与技能在会话启动时加载 —— 目录在但列表里没有,开个新对话就好。
路径映射是这一步的关键
按优先级自动决策:
--cwd-map "源前缀=目标前缀"(显式指定,无条件优先)- 原路径在本机存在 → 直接用
- 家目录前缀替换
- 本地
~/WorkBuddy下同名末级目录
都对不上会跳过并提示,不会写到错的位置。目标工作区不存在时加 --create-cwd 建空目录。
用户名不同建议一次写进配置:options.sessions.cwdMap + createMissingCwd: true。
会话同步的机制要点
- 会话 =
workbuddy.db的sessions行 +projects/<目录名>/<会话ID>.jsonl转录 - 目录名由 cwd 派生:盘符小写 + 分隔符全换成
-(wbsync sessions check可自检本机规则) - 转录按 256K 分片:jsonl 是追加写的,追加只改尾片,所以每次只传变化的那一小片
- 任务按会话 ID 索引 —— 会话不在,任务面板就看不到,所以两者必须一起同步
安全设计
- 敏感范围(
mcp/tasks/memory/sessions)默认 AES-256-GCM 加密后才落盘,
明文不离开本机;口令存~/.workbuddy/.sync/passphrase,可用环境变量WB_SYNC_PASSPHRASE覆盖。 - 加密是确定性的(IV 由密钥与明文派生),所以同一份内容在多台设备上密文一致,
去重与变更检测才成立。 - 远端为空 + 本机有基线 → 中止,不会把本地当垃圾清掉;单轮删除超过一半会被拦下。
- 写数据库前自动备份
workbuddy.db(含-wal/-shm)。 - 同步全程只在本机与你自己指定的共享目录/服务之间进行,不经过任何第三方服务器。
常见问题
| 现象 | 处理 |
|---|---|
找不到同步口令 |
拷贝 .sync/passphrase,或设环境变量 WB_SYNC_PASSPHRASE |
等待同步锁超时 |
上一次同步被中断留下的锁;本机会自动接管失效锁,别机的锁等它释放或删掉 .lock 目录 |
| 提示共享目录与本机同盘 | 共享目录必须是两台设备都能访问的位置,改到 OneDrive / 坚果云 / 共享盘 |
| 会话导入后看不到 | 重启 WorkBuddy;再确认 projects/ 下的目录名与 cwd 推导一致(sessions check) |
新电脑上看不到 /wbsync:* |
命令在会话启动时加载 —— 先开新对话。仍没有就确认 ~/.workbuddy/commands/wbsync/*.md 存在(sync 会把它带过来)。第一次接管请用 bootstrap 打引导包再解包,否则就是鸡生蛋 |
| 提示远端清单是空的 | 换到了全新的共享位置,会自动重新推送全部内容,不会删本地 |
边界(要如实告知用户)
- 工作区里的项目代码不在同步范围内,那部分要用 git 管。
--create-cwd只建空目录让"先接着聊"成立。 - 转录里的历史绝对路径不会被改写 —— 那是对话记录本身,改了会失真。
- 会话同步的单会话转录上限默认 200MB(
options.sessions.maxTranscriptMB)。 - 默认跳过已删除的会话,要一起带走加
--include-deleted。 - 需要 Node.js 22+(用到
node:sqlite读会话库)。