@charara97/dsh-session-sync 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +234 -2
- package/client.js +1017 -0
- package/cordis.patch.yml +5 -0
- package/docs/design-sync-model.md +182 -0
- package/icon.svg +7 -0
- package/index.js +628 -0
- package/lib/engine.js +2613 -0
- package/lib/progress.js +96 -0
- package/lib/session.js +239 -0
- package/lib/util.js +214 -0
- package/locale/en.json +4 -0
- package/locale/zh.json +4 -0
- package/package.json +56 -4
- package/tests/client-render.test.mjs +588 -0
- package/tests/delivery.test.mjs +399 -0
- package/tests/engine.test.mjs +373 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CHARARA97
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,235 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 会话同步(dsh-chat-sync)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
把 DeepSeek Harness 的**会话**与**工作区项目文件**托管在你自己的 **GitHub 私有仓库**里,
|
|
4
|
+
在多台电脑之间手动搬运。
|
|
5
|
+
|
|
6
|
+
它是照着 [`dsh-chatsync`](https://www.npmjs.com/package/@dpskk2/dsh-chatsync) 的存储布局重写的,
|
|
7
|
+
但**同步模型完全不同**:以「逐个会话 / 逐个文件」为单位,而不是整条分支。
|
|
8
|
+
|
|
9
|
+
> 完整逻辑规格(含 13 种单元状态与对应行为)见 [docs/design-sync-model.md](docs/design-sync-model.md)。
|
|
10
|
+
|
|
11
|
+
## 核心性质
|
|
12
|
+
|
|
13
|
+
- **main 是唯一真相,而且立即可信**:你推送时,main 立刻接受你的改动。
|
|
14
|
+
- **推送永远不强迫你拉取**:推送不修改本机工作树、不修改本机 HEAD、不修改真实 index。
|
|
15
|
+
它是在 git 对象层面构造一个「交付提交」直接推进 main。
|
|
16
|
+
- **本机分支永不分叉**,只可能「落后」;取回时为**纯快进**,不产生合并提交。
|
|
17
|
+
- **可逐个会话忽略**:被忽略的会话不参与同步,其落后状态**不影响**其他条目交付。
|
|
18
|
+
- **部分交付**:一次推送中若存在真冲突,无冲突条目正常交付至 main,冲突条目留待裁决。
|
|
19
|
+
|
|
20
|
+
## 界面在哪
|
|
21
|
+
|
|
22
|
+
- **设置 → 会话同步**:完整页面(始终存在);
|
|
23
|
+
- **侧栏底部「会话同步」按钮**:打开同一个面板的浮层 —— 可以在面板里关掉
|
|
24
|
+
(勾选框「在侧栏左下角显示同步按钮」,存 `chat-sync.json` 的 `showSidebarButton`)。
|
|
25
|
+
|
|
26
|
+
控件:
|
|
27
|
+
|
|
28
|
+
1. **操作**:`推送(本机 → 远端)` / `取回(远端 → 本机)`
|
|
29
|
+
2. **范围**:`全部会话` / `单个会话`(搜索标题 / 目录 / id)
|
|
30
|
+
3. **与远端的差异**:逐单元状态表 —— 每个会话/文件是
|
|
31
|
+
`已同步 / 仅本机改了 / 仅远端改了 / 两边都改了·可自动合并 / 两边都改了·需裁决 / 本机删除了 /
|
|
32
|
+
远端删除了 / 本机删除+远端修改 / 远端删除+本机修改 / 已忽略`,每行可单独 **忽略 / 取消忽略**
|
|
33
|
+
4. **工作区**:勾选后同步项目文件;每个工作区可 **忽略整个工作区** 或 **改本机路径**
|
|
34
|
+
5. **重新对齐(纯快进)**:把本机拉到与远端一致,永远安全、不产生合并提交
|
|
35
|
+
6. **开始**:过程日志实时滚动
|
|
36
|
+
7. **需要你裁决的冲突**:逐项决定,且能看到两侧具体内容
|
|
37
|
+
|
|
38
|
+
## 逐个单元的行为
|
|
39
|
+
|
|
40
|
+
推送前会对每个单元独立比较三个版本:`B`(共同祖先)、`L`(本机)、`R`(main)。
|
|
41
|
+
|
|
42
|
+
| 状态 | 推送(本机 → main) | 取回(main → 本机) |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| 已同步 | 不做 | 不做 |
|
|
45
|
+
| 仅本机改 | 交付给 main | 不做 |
|
|
46
|
+
| 仅远端改 | main 保持不变 | 写进本机 |
|
|
47
|
+
| 两边都改·纯追加 | 自动拼接后交付 | 自动拼接后写入本机 |
|
|
48
|
+
| 两边都改·真冲突 | **保留 main,登记待裁决** | **暂停,本机文件不做改动** |
|
|
49
|
+
| 本机新增 | 交付给 main | 不做 |
|
|
50
|
+
| 远端新增 | main 保持不变 | 写进本机(新会话) |
|
|
51
|
+
| 本机删除 | **待裁决**(删除绝不自动传播) | 不做 |
|
|
52
|
+
| 远端删除 | 不做 | **待裁决**(本机文件不动) |
|
|
53
|
+
| 删除 + 修改 | **待裁决** | **待裁决** |
|
|
54
|
+
| 已忽略 | 不推 | 不拉 |
|
|
55
|
+
|
|
56
|
+
**会话日志只有「严格追加」才算可拼接**:事件带 `seq` / `turn` / `step`,两边各自追加会撞号,
|
|
57
|
+
硬拼出来的日志解不开。所以「两边都追加了」是真冲突,不是可自动合并。
|
|
58
|
+
|
|
59
|
+
唯一自动裁决的例外是**会话投影缓存**(`storages/session_projcache/**`):它是 DSH 会重建的
|
|
60
|
+
派生数据,两边都改时直接取本机,避免给每个可自动合并的会话都多加一条无意义的裁决项。
|
|
61
|
+
|
|
62
|
+
## 待裁决的选项
|
|
63
|
+
|
|
64
|
+
| 类型 | 选项 |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| 会话分叉 / 文本重叠 / 二进制两边都改 | `保留本机`(本轮把本机这一版交付给 main) / `采用远端`(用 main 的覆盖本机) / `两边都留`(本机交付给 main,同时把 main 的版本导入成新会话或另存副本) |
|
|
67
|
+
| 本机删除 / 远端删除 / 删除+修改 | `删除` / `保留` —— 选择会被**记住**,不会每次推送重复问同一件事 |
|
|
68
|
+
|
|
69
|
+
裁决前本机文件不做任何改动。会话类裁决均可「查看两侧内容」(两侧各自的事件数与最后若干条消息)。
|
|
70
|
+
|
|
71
|
+
## 安装到另一台机器
|
|
72
|
+
|
|
73
|
+
本插件以 Cordis bundle 形式装在 DSH 的 profile 里,源码仓库是
|
|
74
|
+
<https://github.com/CHARARA97/dsh-session-sync>,包名 `@charara97/dsh-session-sync`。
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
git clone https://github.com/CHARARA97/dsh-session-sync.git
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
然后在 DSH 里用插件管理器把这个目录装成 bundle(`target` 传克隆下来的**绝对路径**):
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
plugin_manager action: install_bundle
|
|
84
|
+
target: /绝对路径/dsh-session-sync
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
装完**重启 DSH**。宿主模块有 ESM 缓存,只刷新页面不够。
|
|
88
|
+
|
|
89
|
+
<details>
|
|
90
|
+
<summary>手动等效做法(本机就是这么装的,供参考)</summary>
|
|
91
|
+
|
|
92
|
+
在 `<DSH_HOME>/profiles/<profile>/package.json` 里加一条依赖,并把包名加进 bundle 列表:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"dependencies": {
|
|
97
|
+
"@charara97/dsh-session-sync": "link:/绝对路径/dsh-session-sync"
|
|
98
|
+
},
|
|
99
|
+
"dsh": { "profile": { "bundles": [ "...", "@charara97/dsh-session-sync" ] } }
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
然后在同一个目录 `pnpm install`,重启 DSH。
|
|
104
|
+
(`<profile>` 是 profile 名,例如 `web`;它也在 `$DSH_PROFILE` 里。)
|
|
105
|
+
</details>
|
|
106
|
+
|
|
107
|
+
**注意两件事不同**:
|
|
108
|
+
|
|
109
|
+
1. **本插件仓库**(公开,就是上面这个)与**你的会话数据仓库**(私有)是两个仓库。
|
|
110
|
+
数据仓库地址在「设置 → 会话同步 → 连接设置」里填,那才是存放会话与工作区文件的地方。
|
|
111
|
+
2. 本机专属设置(忽略清单、已记住的裁决)放在 `<DSH_HOME>/.chat-sync/ignore.json`,
|
|
112
|
+
它**不随同步走**,换机器需要重新配置。
|
|
113
|
+
|
|
114
|
+
## 配置
|
|
115
|
+
|
|
116
|
+
设置页「连接设置」里可改:仓库地址、分支、自动建仓仓库名、Git 代理、工作区统一目录。
|
|
117
|
+
保存到 `<DSH_HOME>/chat-sync.json`。
|
|
118
|
+
|
|
119
|
+
首次使用可以不填地址:远端为空时点「开始推送」,插件会用 `gh` 查找或创建一个**私有**仓库
|
|
120
|
+
(复用前会确认它确实是私有;确认不了就不上传)。
|
|
121
|
+
|
|
122
|
+
兼容读取:如果存在旧插件的 `<DSH_HOME>/dsh-sync.json`,只从中继承 `remote`/`branch`/`proxy`
|
|
123
|
+
等连接类字段,**绝不改写那个文件**。
|
|
124
|
+
|
|
125
|
+
### 本机专属设置(不随仓库同步)
|
|
126
|
+
|
|
127
|
+
放在 `<DSH_HOME>/.chat-sync/ignore.json`。**故意不放进 `chat-sync.json`** —— 后者会随仓库同步,
|
|
128
|
+
那样 A 机器的忽略会静默改变 B 机器的行为。
|
|
129
|
+
|
|
130
|
+
| 字段 | 说明 |
|
|
131
|
+
| --- | --- |
|
|
132
|
+
| `sessions` | 本机忽略的会话 id(既不推也不拉) |
|
|
133
|
+
| `workspaces` | 本机忽略的整个工作区 |
|
|
134
|
+
| `workspaceFiles` | `{ "<工作区id>": ["规则", …] }`,gitignore 语法(等价于直接编辑忽略文件,供工具用) |
|
|
135
|
+
| `decisions` | 已记住的裁决(删除类) |
|
|
136
|
+
|
|
137
|
+
### 工作区里的同步忽略文件
|
|
138
|
+
|
|
139
|
+
**每个工作区根目录下的 `.dsh-chat-sync-ignore` 即该工作区的忽略规则文件**,每行一条,gitignore 语法:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
dist/ 整个目录不同步
|
|
143
|
+
*.log 所有 .log 文件不同步
|
|
144
|
+
notes/secret.md 单个文件不同步
|
|
145
|
+
!keep.log 例外:它仍然同步(放在忽略规则之后才生效)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
- 第一次同步这个工作区时会**自动创建**它并写上说明(已有就不动);
|
|
149
|
+
- 界面上工作区那一行有 **「忽略规则」** 按钮,可以直接在面板里编辑并保存,也能看到文件的绝对路径;
|
|
150
|
+
- 它**本身不参与同步**,属各机器独立设置;
|
|
151
|
+
- 规则同时作用于**推送与取回**两个方向;命中路径不进入工作区影子仓库;
|
|
152
|
+
- 想关掉自动创建:`chat-sync.json` 里设 `createWorkspaceIgnoreFile: false`。
|
|
153
|
+
|
|
154
|
+
> 项目自身的 `.gitignore` 已直接对同步生效(git 会读取工作树中的 `.gitignore`,此处影子仓库的
|
|
155
|
+
> 工作树即项目目录)。`.dsh-chat-sync-ignore` 用于声明「本机不同步、但项目仍需保留」的内容,
|
|
156
|
+
> 无需修改项目的 `.gitignore`。
|
|
157
|
+
|
|
158
|
+
其他可用字段(写在 `chat-sync.json` 里):
|
|
159
|
+
|
|
160
|
+
| 字段 | 默认值 | 说明 |
|
|
161
|
+
| --- | --- | --- |
|
|
162
|
+
| `remote` | `""` | Git 远端地址;空值时尝试用 `gh` 自动建仓 |
|
|
163
|
+
| `branch` | `"main"` | 主数据分支 |
|
|
164
|
+
| `proxy` | `""` | Git 代理,如 `http://127.0.0.1:7890` |
|
|
165
|
+
| `autoRepo` | `true` | 远端为空时是否允许自动建仓 |
|
|
166
|
+
| `repoName` / `repoOwner` | `"dsh-sync"` / `""` | 自动建仓用的仓库名与账号 |
|
|
167
|
+
| `workspaceBase` | `""` | 把各工作区统一放到这个目录下(按文件夹名) |
|
|
168
|
+
| `workspaceBranchPrefix` | `"ws"` | 工作区分支前缀;已有同步数据时不要改 |
|
|
169
|
+
| `workspaceExtraIgnore` | `["node_modules",…]` | 工作区额外排除;`node_modules` 与嵌套 git 仓库总是排除 |
|
|
170
|
+
| `showSidebarButton` | `true` | 是否在侧栏左下角显示同步按钮 |
|
|
171
|
+
| `createWorkspaceIgnoreFile` | `true` | 是否在工作区根目录自动创建 `.dsh-chat-sync-ignore` |
|
|
172
|
+
| `extraIgnore` | `[]` | 只在**首次生成** `.gitignore` 时追加;已有文件请直接编辑 |
|
|
173
|
+
|
|
174
|
+
工作区在本机的目录按这个顺序决定:本机覆盖(界面上「改本机路径」写的就是它)
|
|
175
|
+
→ `workspaceBase` 配置 → 登记表里的路径(本机存在时)→ 把 `/home/<别的用户名>/…` 换成本机主目录。
|
|
176
|
+
|
|
177
|
+
## HTTP API
|
|
178
|
+
|
|
179
|
+
界面用的接口(前缀 `/chat-sync/api/`):
|
|
180
|
+
`status`、`ping`、`units`、`progress`、`config`、`ignore`、`run`、`align`、
|
|
181
|
+
`pending`、`pending/preview`、`pending/resolve`、`pending/discard`、`workspace/path`。
|
|
182
|
+
|
|
183
|
+
另有一个 `chat_sync` Agent 工具,参数是
|
|
184
|
+
`action`(`status` / `push` / `pull` / `conflicts` / `resolve` / `preview`)、
|
|
185
|
+
`scope`、`sessionIds`、`includeWorkspace`、`pendingId`、`conflictId`、`decisions`。
|
|
186
|
+
界面和工具走同一套引擎方法,没有两套逻辑。
|
|
187
|
+
|
|
188
|
+
## 边界与注意事项
|
|
189
|
+
|
|
190
|
+
- **`scope` 的真实含义**:它只决定「哪些改动被**提交**进本端历史」。一旦提交,下一次推送就会
|
|
191
|
+
交付它们。这是必须的 —— 交付提交的父提交包含本机 HEAD,如果交付树故意排除某个已提交的改动,
|
|
192
|
+
本机后续快进到该交付提交时那个改动会被**回退**掉。
|
|
193
|
+
- **本机的 `main` 分支不再是「我的分支」**:交付之后本机总是落后,落后的提交里也包含你自己刚
|
|
194
|
+
推上去的那批。所以界面不按分支术语说话,而按「每个会话相对远端的状态」说话。想对齐就点
|
|
195
|
+
「重新对齐(纯快进)」。
|
|
196
|
+
- **取回永远是「把远端并入本机」**:git 的分支是整份快照,没法只前进一个文件。
|
|
197
|
+
取回时不能自动覆盖的单元(忽略的、有未裁决冲突的、删除类的)会被**保护**住并登记。
|
|
198
|
+
- **无共同历史时不作推断**:三方比较不成立,所有「双方均有且内容不同」的路径一律走内容合并或
|
|
199
|
+
提交裁决,不以 main 为基线假定改动来源。
|
|
200
|
+
- **取回前会先把本机改动提交成一个本地快照**(只提交,不上传)—— 这是 git 的要求:工作树有
|
|
201
|
+
未提交改动时它拒绝合并;DSH 又几乎一直在写会话日志。
|
|
202
|
+
- **工作区文件同理**:推送工作区也走交付提交,本机项目文件不被改动;冲突则停下等裁决。
|
|
203
|
+
- **取回新会话后需刷新侧栏**:本机新增的会话需 DSH 重建索引后才会显示,必要时重启 DSH。
|
|
204
|
+
- 严格意义上这不是备份:它会**传播删除与错误修改**(删除需逐次确认)。
|
|
205
|
+
重要资料请另留副本。
|
|
206
|
+
- `.credentials.yaml` 与 `**/node_modules/` 等机器本地内容不会被同步;但写在会话、附件、`.env`
|
|
207
|
+
里的密钥仍可能被上传 —— 仓库私有也不等于加密。
|
|
208
|
+
|
|
209
|
+
## 开发
|
|
210
|
+
|
|
211
|
+
```sh
|
|
212
|
+
npm test # 三套都跑(43 项)
|
|
213
|
+
node tests/engine.test.mjs # 16 项:push / pull / 工作区 / 忽略文件 / 裁决
|
|
214
|
+
node tests/delivery.test.mjs # 12 项:新交付模型的核心性质
|
|
215
|
+
node tests/client-render.test.mjs # 15 项:界面渲染
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
- **engine / delivery**:在临时目录里造两台「机器」和一个裸仓库当远端。
|
|
219
|
+
delivery 那套专门验证:本端落后时仍可推送、推送后 main 同时具备双方内容而本机工作树未变、
|
|
220
|
+
本端永不分叉、被忽略的会话不参与同步且不阻塞其他条目交付。
|
|
221
|
+
它们只用自己创建的临时目录,不读写真实 `~/.dsh`,也不改 `~/.gitconfig`
|
|
222
|
+
(`file://` 传输所需的 `protocol.file.allow` 通过一次性的 `GIT_CONFIG_GLOBAL` 提供)。
|
|
223
|
+
- **client-render**:这个环境没有浏览器控制,也没有可用的 react/react-dom(React 由宿主在浏览器里
|
|
224
|
+
提供),所以用一个最小 React 模拟器把 `client.js` 真的渲染一遍。它**不能验证外观**,
|
|
225
|
+
但能挡住「渲染期抛异常导致槽位空白」这类问题。
|
|
226
|
+
|
|
227
|
+
| 文件 | 作用 |
|
|
228
|
+
| --- | --- |
|
|
229
|
+
| `index.js` | 宿主入口:HTTP API 与 `chat_sync` 工具 |
|
|
230
|
+
| `lib/engine.js` | 引擎:单元分类、交付提交构造、取回保护、冲突暂存与裁决 |
|
|
231
|
+
| `lib/session.js` | 会话日志的定位、解码、纯追加合并、复制成新会话 |
|
|
232
|
+
| `lib/progress.js` | 运行日志与单次操作状态 |
|
|
233
|
+
| `lib/util.js` | 子进程(含 stdin)、zstd、路径与格式化工具 |
|
|
234
|
+
| `client.js` | 浏览器端:设置页 + 侧栏按钮 + 浮层面板 + 逐单元状态 + 裁决 UI |
|
|
235
|
+
| `docs/design-sync-model.md` | 同步模型规格(13 种状态 × 推/拉行为) |
|