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

会话同步 linbin-mk/dsh-session-sync

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

deepseek-harness 会话同步

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

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

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

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么[目录]—
dsh 原生插件 · other
暂无时间戳·判据 →
装得上吗[实装验证]✓
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L5 · 运行时冒烟等级说明 →
数据截至 2026/11/02·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/03·判据 →
还在维护吗[仓库]✓
活跃:最近一次提交在 8 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

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

验证判据与等级说明 →

可信度证据

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

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

读写/删除本地文件发起外部网络请求
查看扫描证据(共 25 处,展示前 25 条)
  • 发起网络请求
    packages/session-sync/src/engine.ts:201
    fetch(): Promise<void>
  • 读取本地文件
    packages/session-sync/src/engine.ts:456
    return await realpath(left) === await realpath(right)
  • 发起网络请求
    packages/session-sync/src/engine.ts:1059
    await deps.git.fetch()
  • 读取本地文件
    packages/session-sync/src/git.ts:155
    return (await stat(join(this.dir, '.git'))).isDirectory()
  • 写入本地文件
    packages/session-sync/src/git.ts:177
    await mkdir(this.dir, { recursive: true })
  • 发起网络请求
    packages/session-sync/src/git.ts:211
    async fetch(branch: string): Promise<void> {
  • 写入本地文件
    packages/session-sync/src/index.ts:464
    await mkdir(dirname(path), { recursive: true })
  • 写入本地文件
    packages/session-sync/src/index.ts:465
    await writeFile(path, text, 'utf8')
  • 读取本地文件
    packages/session-sync/src/index.ts:477
    return parse(await readFile(path, 'utf8'))
  • 读取本地文件
    packages/session-sync/src/index.ts:760
    const parsed: unknown = JSON.parse(await readFile(this.switchMarksPath(), 'utf8'))
  • 写入本地文件
    packages/session-sync/src/index.ts:780
    await mkdir(dirname(path), { recursive: true })
  • 写入本地文件
    packages/session-sync/src/index.ts:781
    await writeFile(path, snapshot, 'utf8')
  • 发起网络请求
    packages/session-sync/src/index.ts:843
    fetch: () => repository.fetch(settings.branch),
  • 读取本地文件
    packages/session-sync/src/index.ts:1065
    return await readFile(this.abs(rel), 'utf8')
  • 写入本地文件
    packages/session-sync/src/index.ts:1075
    await mkdir(dirname(abs), { recursive: true })
  • 写入本地文件
    packages/session-sync/src/index.ts:1076
    await writeFile(abs, content, 'utf8')
  • 删除本地文件
    packages/session-sync/src/index.ts:1081
    await unlink(this.abs(rel))
  • 删除本地文件
    packages/session-sync/src/index.ts:1092
    await rmdir(this.abs(rel))
  • 读取本地文件
    packages/session-sync/src/index.ts:1131
    entries = await readdir(this.abs(rel), { withFileTypes: true })
  • 写入本地文件
    packages/session-sync/src/log.ts:99
    await mkdir(this.dir, { recursive: true })
  • 写入本地文件
    packages/session-sync/src/log.ts:100
    await writeFile(this.dayFilePath(this.now()), JSON.stringify(entry) + '\n', { encoding: 'utf8', flag: 'a' })
  • 读取本地文件
    packages/session-sync/src/log.ts:112
    names = await readdir(this.dir)
  • 删除本地文件
    packages/session-sync/src/log.ts:125
    await rm(join(this.dir, name), { force: true })
  • 读取本地文件
    packages/session-sync/src/log.ts:141
    names = await readdir(this.dir)
  • 读取本地文件
    packages/session-sync/src/log.ts:153
    const text = await readFile(join(this.dir, file), 'utf8')
扫描于 2026/11/3(1 天前) · 范围 全包扫描 · 来源 GitHub 仓库

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

社区实装还没有人打卡 · 装过的第一个就是你
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • Git同步会话
  • 多机同步
  • 挑选会话

README

由 DeepSeek 最新模型翻译生成
dsh-session-sync 面向 DeepSeek Harness(DSH)的第三方会话同步插件:借一个 git 仓库,在多台电脑之间同步你亲手挑的会话,并自带 Web 界面。 设置 → 会话同步:总开关、SSH 仓库与同步集合都在这一页。选中会话的入口在侧栏会话行的 ... 菜单里。 ┌───────────── 电脑 A ─────────────┐ ┌───────────── 电脑 B ─────────────┐ │ DSH host ── session-sync 插件 ───┼─git──┼─── session-sync 插件 ── DSH host ─│ │ └─ /session-sync/(自建 HTTP API)│ │ └─ /session-sync/(自建 HTTP API)│ │ 浏览器 UI ── fetch(同源) │ │ 浏览器 UI ── fetch(同源) │ └─────────────────────────────────┘ └─────────────────────────────────┘ 它能做什么 - 换一台电脑,接着聊。 在 A 机选中的会话经 git 仓库出现在 B 机的同名工作区里,历史、标题与子代理分组一起过去。 - 只同步你挑的会话。
展开完整文档(README 剩余 1 节)
dsh-session-sync

面向 DeepSeek Harness(DSH)的第三方会话同步插件:借一个 git 仓库,在多台电脑之间同步你亲手挑的会话,并自带 Web 界面。

设置 → 会话同步:总开关、SSH 仓库与同步集合都在这一页。选中会话的入口在侧栏会话行的 ... 菜单里。

┌───────────── 电脑 A ─────────────┐ ┌───────────── 电脑 B ─────────────┐ │ DSH host ── session-sync 插件 ───┼─git──┼─── session-sync 插件 ── DSH host ─│ │ └─ /session-sync/(自建 HTTP API)│ │ └─ /session-sync/(自建 HTTP API)│ │ 浏览器 UI ── fetch(同源) │ │ 浏览器 UI ── fetch(同源) │ └─────────────────────────────────┘ └─────────────────────────────────┘

它能做什么

- 换一台电脑,接着聊。 在 A 机选中的会话经 git 仓库出现在 B 机的同名工作区里,历史、标题与子代理分组一起过去。
- 只同步你挑的会话。 不置顶、不映射项目、不同步整个配置档:一个会话是否跨机,只由会话行 ... 菜单里的「同步会话 / 会话同步中」决定。
- 路径不跨机。 仓库里只记工作区的稳定 key 与显示名称,导入时按名称落到本机路径;机器路径永远不会被写进仓库。
- 不会悄悄丢数据。 冲突保留双副本并上报,未闭合的 turn 不出网,关闭同步、归档、清理都不删除本机会话。

三步用起来

1. 在 A 机选中会话。 侧栏会话行 ... →「同步会话」;插件立刻跑一个周期,把会话推进 git 仓库。
2. 在 B 机等它落位。 B 机按工作区名称找同名工作区,下一个周期自动导入。没有同名工作区时,会话会留在设置页的「没有同名工作区」分组里——在本机建一个同名工作区即可。
3. 随时查看。 「同步状态」卡片显示上次同步与本次结果;会话行 ... →「会话同步中」能看到这台会话在哪台机器、何时推/拉过,也能就地关闭同步。

前提:两台电脑都装了本插件、都指向同一个 git 仓库,并且设置页的「开启自动同步」已打开。

界面一览

会话行菜单:同步哪一个会话,由你决定

侧栏会话行的「...」菜单

未同步的会话显示「同步会话」,已同步的显示「会话同步中」;点开是同步记录与「关闭同步」。

设置页:待匹配工作区、Git 空间清理与同步状态

设置页下半部分

集合按工作区分组;仓库里有、本机却没有同名工作区的会话会作为带告警的分组留下,并写明补救方式。

深色模式

深色模式下的会话同步设置页

工作方式

只同步你选中的会话

- 点一下才开始同步。 在侧栏会话行的 ... 菜单里选「同步会话」,这个会话就加入跨机共享的同步集合;再次打开菜单会看到「会话同步中」,点它弹出同步日志(哪台机器、什么时候、推还是拉、多少条事件),弹窗里可以直接「关闭同步」。不需要先置顶,也不再有「项目映射」这类配置。
- 集合是全局一份真相。 仓库根部的 sync.json 是整份快照(不是并集),每台机器把它镜像进自己的选择;谁在哪台机器上关闭同步,都会传播到所有机器。为了不让「谁改了什么」互相打架,每台机器在 harness home 下记一份锚点(state.json):自己的选择变了就发布,没变就采纳仓库的。
- 首次运行只采纳、不删除。 一台还没跑过周期的机器(没有锚点)不会把仓库里的选择当成「要关闭同步」,因此不会误删仓库产物。同一条保护也覆盖之后任何无法发布的周期:锚点只记录真正应用的选择,且只在推送成功之后记录,所以本机从未持有过的选择不会被读成「你关闭了它」,远端没接受的发布也会重试而不是被当成删除。

会话如何落到另一台电脑

- 会话跟着你走,机器路径不跟。 每个会话导出为 workspaces/<key>/session-<id>.jsonl;首行是插件自有的版本化文件头,记录工作区的稳定 key 而不是本机路径。导入时按工作区名称解析成本机路径,插件不复制 harness JSONL 后端的私有文件、压缩或行编码。
- 按名字落到工作区。 每个 workspaces/<key>/manifest.json 记录该工作区的显示名称:另一台电脑上标题完全相同的那个工作区就是目的地。只有恰好命中一个才自动导入;一个都没命中(包括任一方改过名——改名不适配)、或者本机有多个同名工作区(harness 允许不同路径共用标题)时,这些会话留在待匹配里等你在本机建出同名工作区,下一个周期自动落位。没有绑定 UI,也不写任何本地映射。
- 不落地就不导入。 harness 里一个会话只能挂到「路径等于该会话 header 里 cwd」的工作区,而且 cwd 一旦创建就不可改、也没有跨工作区改挂的接口。所以插件在导入之前就完成匹配并把 cwd 写成本机工作区路径;匹配不上就原样留在仓库里等,绝不猜一个位置先塞进去。

数据不会丢

- 冲突策略:前缀合并 + 双副本。 会话日志是只追加的事件流:一份日志是另一份的严格前缀时,更长者胜出;真正分叉时,远端尾巴原样保留在 conflicts/<key>/<session-id>-<host>.jsonl 并上报——数据绝不静默丢失。分叉时的导出永远不会覆盖仓库工件。
- 未闭合 turn 不出网。 没有以 turn/end 收尾的日志,要么正在所属电脑上实时运行,要么是崩溃后等待 harness 的「中断修复」。把它导出等于发布一个截断快照:导入方加载后会被 harness 补上合成的修复尾巴,从此与本机的真实后续内容永久分叉。因此导出跳过未闭合 turn 的日志,导入也跳过未闭合 turn 的工件。
- 本机会话在任何情况下都不会被删除。 关闭同步、归档、清理,都只影响仓库里的产物和共享标记,本机那份完整保留。

仓库与 git 空间

- 仓库只是传输介质。 每个周期执行 fetch → 硬重置 → 导入 → 导出 → 提交 → 推送,git 合并冲突无从产生。远程命令(ls-remote、fetch、push)自带指数退避重试(默认最多 3 次尝试),一次瞬时的 SSH 连接损坏不再让整个周期报废。
- 归档标记跟着走,归档内容退出 git。 在一台电脑上归档的会话,通过 workspaces/<key>/archived.json 里的标记在每台电脑的会话列表中都隐藏,同时它在仓库里的 session-<id>.jsonl 会在下一次同步时被删除——归档会话不再占用 git 空间。仓库里的标记是只增并集,永远不会产生冲突。
- 定期清理 git 空间。 git 不会遗忘已删除的文件:归档删除只影响 HEAD,历史里的每个旧版本永远留在对象库里。清理功能按周期(小时)把共享历史重写到只保留最近 N 次提交(默认 200)并强推(--force-with-lease),被截断的提交连同它们的 blob 一起退出仓库;重放保留提交时最新树原样保留——当前所有文件一个不少,其他电脑在下一个周期自动按重写后的历史重新同步。

用起来是什么样

- 导入即预热投影缓存。 同步导入直接写持久化、绕过 live 会话存储,harness 的投影缓存不会自动折叠它;插件在每次导入后对会话做一次冷读预热(fail-soft),标题、子代理分组等列表行元数据立即可见,无需先点开会话。
- 切换电脑后的首次续聊注入一次提醒。 每当导入给某个会话带来来自其他电脑的新事件,插件就为它武装一个一次性标记(持久化在 harness home 下,重启不丢)。该会话的用户首次发消息时——经 harness 的 agent/pre-step 缝检测——一条插件 notice 消息会紧跟在用户消息之后折入上下文,告知大模型:本会话历史是从另一台电脑同步而来的,历史路径可能与当前电脑不符,此后一律以当前工作目录为准。标记注入即消耗。子代理会话除外。
- 同步日志保留 3 天。 每个周期在 harness home 的 session-sync/logs/sync-YYYY-MM-DD.jsonl 里追加开始/成功/失败记录(含计数、冲突副本、错误与耗时),读写时自动清理超出 3 天窗口的日志文件;设置页的折叠面板展示最近记录。另外,每个会话有一份自己的同步记录(workspaces/<key>/session-<id>.records.json,保留最近 20 条),那是行菜单弹窗里显示的「机器 + 时间」来源,会随仓库传播,所以你能看到另一台机器什么时候拉过它。
- 插件清单里显示本地化名称与图标。 包内 icon.png(两枚对话气泡与双向箭头)与 locale/en.json、locale/zh.json(各自带 meta.title / meta.description)经 ./icon、./locale/.json 导出,harness 的 Plugin Manager、bundle 详情与设置页插件清单在不激活插件的情况下就能显示「会话同步 / Session Sync」的标题、描述和图标。
- 自带 Web 界面。 设置页分成四张卡片:同步设置(总开关、SSH 仓库地址、分支与间隔同行)、同步会话集合、Git 空间清理、同步状态(立即同步与同步日志)。每个动作都待在它作用的对象上:「立即清理」在清理卡片的标题行,「立即同步」在状态卡片的标题行。集合树与侧栏会话树同一套行结构:工作区行(文件夹图标,hover 让位给展开箭头,可折叠)、32px 会话行(状态点:冲突红点、未同步到本机灰点;尾部是「最近同步」的相对时间,hover 换成「…」菜单——查看同步记录 / 关闭同步)。仓库里有、本机却没有同名工作区的会话不再另起一块,而是作为带告警的分组留在树里,组头写明「没有同名工作区」与等待数量,组下一行说明补救方式。设置区仍是草稿 + 显式保存:改动后页面底部浮出保存条,点「保存」才写入,「撤销修改」丢弃改动,校验(必填、分支非空)就地提示;写失败也显示在保存条里。另有侧栏同步状态点、会话行菜单项、同步日志弹窗。

为什么这个插件不需要改 harness 核心代码

harness 的 RPC 表(apiproxy)是编译器锁死的静态注册表——第三方包无法往里添加 sessionSync. 方法。本插件改在 harness 开放的 webServer 路由缝上注册自己的同源 HTTP API:

| 路由 | 方法 | 用途 |
| /session-sync/status | GET | 只读同步状态视图 |
| /session-sync/selection | GET | 同步集合树(工作区 → 会话)+ 待匹配 |
| /session-sync/sessions/<id> | POST | 把会话加入同步集合 |
| /session-sync/sessions/<id> | DELETE | 关闭该会话的同步 |
| /session-sync/sessions/<id>/records | GET | 该会话的同步记录(机器 + 时间,新的在前) |
| /session-sync/sync-now | POST | 执行一个同步周期并返回最新状态 |
| /session-sync/cleanup-now | POST | 执行一次 git 空间清理并返回最新状态 |
| /session-sync/settings | GET | 设置视图(writable + 配置段) |
| /session-sync/settings | POST | 合并一个 patch 到设置段(host 校验) |
| /session-sync/logs | GET | 最近同步日志(保留窗口内,新的在前,?limit= 上限 500) |

浏览器端用普通 fetch 调用这些路由。写路由拒绝跨源请求(Origin 头必须指向本服务器自身 host)和畸形请求体。设置段的读写走 harness 通用的 config form(ctx.configForms,入口 id 就是 profile 里的 session-sync 行);插件自己的设置路由保留两个用途:GET 供共享表单还没有活的 Host 文档时(加载中、不可用或已销毁)按该路由自己的 writable 展示配置,POST 是唯一带回 host 拒绝原因的写入路径。

界面本身也全部走 harness 开放的插槽,没有一处需要改核心:

| 槽 | id | 内容 |
| settings.section | sync | 同步设置页(四张卡片 + 吸底保存条 + 会话集合树) |
| sidebar.footer.action | session-sync-status | 侧栏状态点 |
| sidebar.workspaces.session.menu.item | session-sync.toggle | 会话行 ... 菜单项(order 500) |
| shell.overlay | session-sync.dialog | 同步日志弹窗 |

仓库结构

packages/session-sync/ @linbin-mk/dsh-session-sync(host 半边 + 浏览器半边,一个包)

host 包包含同步引擎(engine.ts、format.ts、git.ts、settings.ts)、选择树投影(selection.ts)、服务(index.ts)与 HTTP 层(api.ts、routes.ts)。浏览器包含设置页、集合树、会话行菜单项、同步日志弹窗、状态页脚、页面控制器与 fetch 客户端。

仓库格式(v2):

sync.json 跨机同步集合(整份快照)
workspaces/<key>/manifest.json { version, key, name, updatedAt } ← name 是匹配用的真相
workspaces/<key>/session-<id>.jsonl 会话工件 workspaces/<key>/session-<id>.records.json 该会话的同步记录(机器 + 时间,最近 20 条) workspaces/<key>/archived.json 归档标记(只增并集) conflicts/<key>/<session-id>-<host>.jsonl 冲突副本

sync.json 的每条记录带 id、key、workspaceName、title、addedAt、addedBy。带 title 不是冗余:工件头里没有标题(只有 id、createdAt、parentSession、isSeeded、origin、delegationDepth、agentPreset),而集合里包含本机还没导入的会话——不写进来,设置页就画不出完整清单。

本机状态(harness home 下的 session-sync/):

selection.json { sessionIds } 本机当前的同步集合
state.json { firstSeen, syncedIds, ownedIds, workspaceKeys } 编辑检测锚点 + 工作区→key 记忆
repo/ git 工作树 logs/ 周期日志

workspaceKeys 是工作区 id → 仓库目录 key 的记忆表:key 一旦分配就不再变化,所以改名只会重写 manifest 里的 name,不会搬动任何产物。

环境要求

- Node.js ^22.19 || >=24
- DeepSeek Harness 0.2.1-rc.3(所有 @deepseek-ai/ 依赖均使用 npm 发布版本,不含本机 harness 链接)
- 包声明的 peer 线:@deepseek-ai/cordis ^4.0.5、@deepseek-ai/schemastery ^3.18.5、cordis-plugin-loader ^1.0.6、cordis-plugin-include ^1.0.10
- PATH 中有 git,且同步远端需要 SSH key(host key 策略:StrictHostKeyChecking=accept-new)
- 只有从源码构建时才需要 pnpm

0.10.0 破坏性变更:跟随 harness 0.2.1-rc.3

- 依赖线与 engines.dsh 下限一起抬到 ^0.2.1-rc.3。 26 个 @deepseek-ai/dsh- peer / dev 依赖、pnpm-workspace.yaml 的 release-age 豁免表、以及包声明的 harness 下限,全部从 rc.2 线换到 rc.3 线;cordis ^4.0.5、schemastery ^3.18.5、cordis-plugin-loader ^1.0.6、cordis-plugin-include ^1.0.10 在 rc.3 没有变化,保持不变。仍停在 0.2.1-rc.2 及更早的配置档,会在安装时被拒,或在组合期被一行 disabling profile plugin 禁用。
- rc.3 没有需要插件改代码的破坏性变更,所以本版本没有功能改动。 插件用到的 ui-settings、ui-slots、app-boot、dsh-session、dsh-settings、dsh-workspace 在 rc.2→rc.3 之间只改版本号或内部实现;变化都落在插件不消费、或只做新增的面上:ui-sidebar 新增 sidebar.panellist.actions 槽与面板 More 菜单,ui-primitives 的 Menu 新增 current / minWidthFromAnchor 属性,llm-deepseek 的模型列表逻辑重写(插件只用 dsh-llm 的 createUserMessage / ContextFormed,与它无关)。本插件注册的 settings.section / sidebar.footer.action / sidebar.workspaces.session.menu.item / shell.overlay 四个槽与全部 host 服务接口契约不变,pnpm typecheck 与 359 项测试在 rc.3 上原样通过。
- 声明的下限同时接受 rc.4 与 0.2.1 正式版。 按 node-semver 的预发布规则,engines.dsh: ^0.2.1-rc.3 与全部 ^0.2.1-rc.3 peer 对 0.2.1-rc.4、0.2.1 都成立(同一 patch 的预发布递增在 caret 内,上限是 0.3.0),rc.4 发布后无需再发一版。

0.9.0 破坏性变更:跟随 harness 0.2.1-rc.2

- 设置页「能不能写」的判据换了,不再看进程内/宿主二选一。 harness 0.2.1-alpha.3(提交 23c29d067a,fix(web): persist authenticated browser preferences on the Host)从客户端 config form 的快照里去掉了 mode 字段:认证过的浏览器一律把偏好持久化到 Host 文档。0.8.1 仍要求 snapshot.mode === 'host' 才允许编辑,于是在 0.2.1-rc.2 上插件能加载、设置页也画得出来,但所有写入控件都渲染成禁用,页面只有一条「当前部署的设置文档为只读」提示——没有报错,只是任何配置都保存不了。0.9.0 改为在共享表单就绪时只看 status === 'ready' && writable(表单还没有活的 Host 文档时,才回落到插件设置路由的 writable),并删除 memory 模式分支;tests/controller.client.spec.ts 用不带 mode 的 ready + writable 快照、以及共享表单不提供 Host 文档时的只读路由页面各钉一条回归。

- peer 线与 engines.dsh 下限一起抬到 ^0.2.1-rc.2。 在 0.2.1-rc.2 上,兼容闸门 evaluatePluginCompatibility() 的规则是:插件只要声明了 engines.dsh,就只按它判定,@deepseek-ai/dsh- peer 不再参与——所以这条声明的下限是真正的门,而不是 0.7.0 一节当时写的纯声明性字段(那时 alpha.2 确实只校验 peer)。本版本声明 engines.dsh: ^0.2.1-rc.2,仍停在 0.2.1-alpha.2 / rc.1 的配置档会在安装时被拒、或在组合期被禁用。cordis ^4.0.5、schemastery ^3.18.5、cordis-plugin-loader ^1.0.6、cordis-plugin-include ^1.0.10 随之去掉 -alpha.1 后缀。

- 会话事件表新增两种 job 事件,导入侧跟着放开。 0.2.1-rc.2 的 KNOWN_SESSION_EVENT_TYPES 新增 job/registered 与 job/settled(后台任务的生命周期时间线)。工件导入照 dsh-session 这份表校验必需事件类型,所以启动或结束过后台任务的会话在旧 peer 上会被判「未知的必需事件类型」而整份拒绝导入;跟随 rc.2 的 peer 后自然接受,tests/format.spec.ts 加了回归用例。

- 插件清单里有了显示元数据。 包内新增 icon.svg 与 locale/en.json / locale/zh.json,经 ./icon 与 ./locale/.json 导出;harness 的 Plugin Manager、bundle 详情和设置页插件清单会按包名解析这些资源,不需要激活插件就能显示本地化标题、描述与图标。资源缺失时 harness 会静默回落到包名与通用占位图,所以 tests/display-metadata.spec.ts 按它的解析规则钉住了导出、语言 id、图标格式与 files 白名单。0.9.1 起图标改为 icon.png(两枚对话气泡与双向箭头),导出与白名单同步调整。

0.7.0 破坏性变更:跟随 harness 0.2.1-alpha.2

- 整条 @deepseek-ai/ 依赖线从 0.1.7-rc.2 升到 0.2.1-alpha.2:cordis ^4.0.5-alpha.1、schemastery ^3.18.5-alpha.1、cordis-plugin-loader ^1.0.6-alpha.1、cordis-plugin-include ^1.0.10-alpha.1。
harness 现在会在组合期逐个插件校验 @deepseek-ai/dsh- peer:evaluatePluginCompatibility() 拿运行时的 dsh 版本做 semver.satisfies(runtime, range, { includePrerelease: true }),不满足就把该行标成 disabled,并在 stderr 打一行 disabling profile plugin …。而 0.x 的 caret 不跨次版本,预发布版本还额外要求 major.minor.patch 完全一致——^0.1.7-rc.2 匹配不到 0.2.1-alpha.2,所以旧 peer 线会让 session-sync 这一行在 harness 0.2.1 上被整个禁用:不是降级、不是警告,是根本不加载。抬到 ^0.2.1-alpha.2 后校验通过(includePrerelease: true 下同样为真)。

包同时新增 engines.dsh: ^0.2.1-alpha.2,把兼容下限记在 harness 自己的 DshEnginesManifest 位置上。该字段在 0.2.1-alpha.2 上确实是声明性的——没有任何读取方强制它,上面那道 peer 校验才是真正的闸门;0.2.1-rc.2 起这一说法已不成立,声明了 engines.dsh 时闸门只按它判定,见上方 0.9.0 一节。
- 顺带修掉一类导入失败。 harness 0.2.1 给会话事件表加了 working-directory/change(事件类型总数 60),而工件导入是照 dsh-session 自己那份 KNOWN_SESSION_EVENT_TYPES 校验的(src/format.ts:385,只作用于导入侧)。仍用旧 peer 时,日志里含这个事件的会话会在导入时被判成「未知的必需事件类型」而拒绝——切换过工作目录的会话就落不到另一台机器上。换到新 peer 后自然接受,tests/format.spec.ts 加了回归测试钉住。
- @deepseek-ai/dsh-invariants 已在 0.2.x 被 harness 删除,pnpm-workspace.yaml 的 release-age 豁免表里那条随之清掉;插件从未 import 它,仅是过期条目。

0.6.0 破坏性变更:两个包合并成一个

host 半边与浏览器半边现在是同一个包 @linbin-mk/dsh-session-sync:main 是 Host 插件,exports["./client"] 是 Web 客户端加载的浏览器 bundle,dsh.client + dsh.bundle 让一行 profile 条目同时承载两半(与 harness 自己的 @deepseek-ai/dsh-experimental-inspector 同一形态)。@linbin-mk/dsh-client-ui-settings-sync 不再发布新版本。

- 升级要两步:先 dsh plugin --profile <name> remove @linbin-mk/dsh-client-ui-settings-sync 把旧的那行摘掉,再 dsh plugin --profile <name> add @linbin-mk/dsh-session-sync。不摘旧行的话,旧包仍会注册同一批槽(settings.section / sidebar.footer.action / sidebar.workspaces.session.menu.item / shell.overlay),与新包重复。
- 顺带删掉了旧浏览器包里的 ./invariant 空伴生导出(没有任何 patch 行引用它)。

0.5.0 破坏性变更:从「置顶即同步」到「逐会话显式同步」

- 同步选择换了主人。 v0.4 的选择 = 映射项目 ∩ 置顶会话,靠 harness 的置顶集合承载;v0.5 是插件自己的显式集合,入口在会话行 ... 菜单。插件不再读写任何置顶状态。
- 项目映射整个删除。 跨机落位改为按工作区名称匹配 manifest.json 的 name。
- 仓库格式升级到 v2,且不兼容 v0.4 写出的仓库。 pinned.json 与 projects/ 目录不再被读取,工件头版本从 1 升到 2。请用一个新仓库(或清空旧仓库内容)后重新逐个添加要同步的会话;升级本身不做迁移,也不会删除任何本机会话。
- 需要 DeepSeek Harness 0.1.7-rc.2;仍停留在 0.1.7-alpha.1 的 profile 无法安装本版本。

安装

把已发布的包装进自定义 Web profile。包声明了 dsh.bundle patch,一行会自动加入,它同时承载 Host 半边与浏览器半边:

dsh --profile web-sync --from-default-profile web --dump-config
dsh plugin --profile web-sync add @linbin-mk/dsh-session-sync
dsh --profile web-sync

改为安装本地构建的 tarball:

cd dsh-session-sync
pnpm install && pnpm build
pnpm --dir packages/session-sync pack

dsh plugin --profile web-sync add ./linbin-mk-dsh-session-sync-*.tgz

从同一个 profile 移除:

dsh plugin --profile web-sync remove @linbin-mk/dsh-session-sync

组合配置

- id: session-sync
name: '@linbin-mk/dsh-session-sync'
config:
startupSyncDelayMs: 3000

安装会自动加入上面的两行。host 插件要求 settings 与 sessionPersistence 服务;workspaceRegistry 现在是必需的能力(没有它就无法按名匹配或导出任何会话,插件会跳过导出并报告),存在 sessionProjectionCache(可选注入)时导入完成即预热投影缓存,同时设置页用它零 I/O 地读出会话标题。

配置项

配置就是 profile 里 session-sync 这一行的 Cordis Config:所有用户可改字段都是「活引用」(.volatile()),harness 把每次设置写入提交进这些引用,并持久化到 profile patch 文档。字段表:

| 字段 | 含义 |
| startupSyncDelayMs | 启动后到首次自动周期的延迟(毫秒,默认 3000)。部署项:它不在设置页里。 |
| enabled | 总开关;开启时 remote 必填。关闭时会话行菜单项不显示。 |
| remote | Git 远端地址(SSH)。凭据来自 ~/.ssh。 |
| branch | 同步分支;默认 main。 |
| intervalMinutes | 自动同步间隔(分钟,最小 1,默认 5)。 |
| cleanup | { enabled, periodHours, keepCommits }:定期 git 空间清理开关、清理周期(小时,最小 1,默认 24)、保留的最近提交数量(最小 1,默认 200)。 |

同步集合不是配置项,它是数据:设置在设置页里只读展示,写入入口只有会话行菜单(以及设置页集合行 hover 出的「…」菜单里的「关闭同步」)。

节奏:启动 startupSyncDelayMs 后跑一个自动周期,之后每 intervalMinutes 一次。用户点「同步会话」会立刻触发一个周期(单飞守卫),所以本机是即时的,对端什么时候看到取决于对端的 intervalMinutes。清理在每次成功周期之后检查一次到期,外加设置页的立即清理按钮。

待匹配工作区

导入前先按名匹配。落不进去的会话不会消失,它们留在仓库里,并在设置页的「待匹配工作区」区列出(含该工作区下等待的会话数)。让它们落位的唯一动作是:在本机用侧栏「添加工作区」建一个标题完全相同的工作区,下一个周期会自动匹配并导入。改了名的工作区不会自动适配——这是刻意的,猜错位置会让会话永久挂错工作区。

安全姿态

- 插件不存储任何凭据:git 远端是你的 SSH 地址,key 在 ~/.ssh。
- 机器路径永不进入仓库(导出时改写为工作区 key)。
- 写路由强制同源校验;状态与设置读取无副作用。
- 插件自己的 git 提交使用固定身份 dsh-session-sync <dsh-session-sync@localhost>。

开发

pnpm install
pnpm test # engine、format、selection、git、log、settings、service、routes、组合、配置写入、UI
pnpm typecheck
pnpm build # 两个包的 tsc + 浏览器 bundle(lib/client.js)

设计与实现契约见 docs/spec/session-sync-v2.md。

关于第三方测试环境

已发布的 harness 客户端包只以浏览器 bundle 形式提供其 /client 运行时面(它们经 harness 模块表加载),第三方包无法在 Node 测试里导入 SlotRegistry、createSnapshotStore 或测试运行时。因此本仓库:

- 把小型快照存储引擎 vendor 进 packages/session-sync/src/client/store.ts(裁剪自 harness 的 MIT 源码并注明出处),并且
- 在 packages/session-sync/tests/helpers.ts 中用最小假件测试 slot 注册、locale 与 remote 失效路径。

真实 slot 核心行为由 harness 侧集成覆盖。

常见问题

- 刚发版后 dsh plugin add 报 404。 全新版本在 registry 读路径上需要几分钟才可见。稍后重试即可;不要重复发布。
- 通过镜像安装报 ERR_PNPM_FETCH_404。 给命令加 --registry=https://registry.npmjs.org,或等镜像同步。
- pnpm 拒绝或询问刚发布的版本。 这是 pnpm 的 minimumReleaseAge 延迟保护。放行该包或等过这个时间窗口。
- Web GUI 起不来、或设置页里没有「同步」。 浏览器半侧靠 harness 的模块表索引,索引键是包名,不是短名。确认两行都在:

dsh --profile web-sync --dump-config | grep -A 2 -e session-sync -e ui-settings-sync

Host 半侧与同步引擎此时可能完全正常,所以 --dump-config 看着没问题不能证明浏览器半侧已加载——唯一可靠的判据是浏览器控制台无错误、且设置页出现「同步」。
- 会话行菜单里没有「同步会话」。 三种情况:总开关没开或没填远端;该会话不属于任何工作区;本机不持有该会话。设置页的集合树能看到集合本身。
- 另一台电脑上看不到某个会话。 先看设置页的「待匹配工作区」:多半是那台机器没有同名工作区。在那里建一个同名工作区即可。
- 同步周期报 SSH 错误。 插件用 StrictHostKeyChecking=accept-new,首次连接新主机仍需要你的 key 可用(先试 ssh -T git@<host>)。瞬时损坏由指数退避重试吸收;持续失败请看设置页的同步日志面板。
- 在一台电脑归档后,另一台仍能看到该会话。 归档标记是只增并集,需要一次成功周期才会传播。确认两端都完成了至少一个周期。

已知限制

- 状态刷新为轮询(页脚每 60 秒 + 每次操作后 + 菜单打开时定向刷新);harness 内的事件推送路径需要核心权限,故刻意不用。
- 设置改动由 harness 推送到已打开的设置页(config form 订阅 + settings/document-updated),无需刷新。
- 仅支持 SSH 远端;HTTPS + token 未实现。
- 重命名工作区不适配。 任一方改名,名称就对不上,那些会话进入待匹配;仓库侧的 key 不变,产物不会搬动。
- 本机有多个同名工作区时不自动匹配。 会留在待匹配里,需要你自行消除歧义(harness 允许不同路径共用同一显示标题)。
- 投影预热是 fail-soft 的:某次预热失败时该行暂时退回工作区名显示,点开会话或以后再次导入时会刷新。
- 冲突副本需要手动处理;页面只显示数量与最近一次记录。
- 共享归档标记只增不删,取并集:要让取消归档传播出去,需要在每个工作区的 archived.json 里加入墓碑记录(并升格式版本),本版本未实现。无论哪种情况,归档内容都无法从仓库恢复:标记落地时会话文件即被删除。
- 清理是历史重写:本地与远端的历史提交被丢弃(最新树保留)。若多台电脑几乎同时清理,--force-with-lease 会让后到者的强推失败,并在下一周期重试,不会静默覆盖别人的新提交。
- 一个会话的同步记录上限 20 条(按时间保留最新),超出后最旧的记录不再显示。
- 若 harness 上游未来把会话同步并入核心 RPC,可以换回 RPC 原生变体,引擎无需改动。

许可证

MIT —— 见 LICENSE。

packages/session-sync/src/client/store.ts 中的快照存储引擎裁剪自采用 MIT 许可证的 DeepSeek Harness;浏览器 bundle(lib/client.js)在构建期内联了同样采用 MIT 许可证的 zustand、immer 与 clsx。上述上游版权声明与许可证文本见 NOTICE。
预览分享卡片长什么样 ↓linbin-mk/dsh-session-sync
分享 · DeepSeek Harness Hub
linbin-mk/dsh-session-sync

deepseek-harness 会话同步

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