ygc-dsh-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/README.md +460 -2
- package/client.js +1826 -0
- package/cordis.patch.yml +5 -0
- package/icon.svg +19 -0
- package/index.js +767 -0
- package/lib/api.js +1510 -0
- package/lib/attach.js +137 -0
- package/lib/backup.js +396 -0
- package/lib/changelog.js +158 -0
- package/lib/core.js +443 -0
- package/lib/mnemon-drafter.js +151 -0
- package/lib/mnemon-optimize.js +606 -0
- package/lib/mnemon-pipeline.js +1156 -0
- package/lib/mnemon.js +440 -0
- package/lib/patrol.js +139 -0
- package/lib/plugins.js +907 -0
- package/lib/skills.js +1143 -0
- package/lib/zstd-frame.js +104 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +59 -4
- package/tools/_health.mjs +24 -0
- package/tools/add-version.mjs +119 -0
- package/tools/backfill-versions.mjs +54 -0
- package/tools/draft-live.mjs +93 -0
- package/tools/fix-skill-paths-py.mjs +106 -0
- package/tools/fix-skill-paths.mjs +119 -0
- package/tools/release-local.mjs +120 -0
- package/tools/retire-kb-zhishiku.mjs +51 -0
- package/tools/skill-lineage.mjs +243 -0
- package/tools/sync-lock.mjs +76 -0
- package/tools/verify-installed.mjs +131 -0
package/README.md
CHANGED
|
@@ -1,3 +1,461 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 会话同步备份(dsh-sync-backup)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
把指定文件夹内工作区的对话,按机器隔离备份到工作区里;支持一键拉取其他机器的会话。
|
|
4
|
+
|
|
5
|
+
## 它做什么
|
|
6
|
+
|
|
7
|
+
工作区位于「特定文件夹」内时,自动建立:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
<工作区>/
|
|
11
|
+
└─ .dsh-sync/sessions/
|
|
12
|
+
├─ <本机标识>/ ← 只有这一格可写
|
|
13
|
+
│ └─ <sessionId>/session.v4.jsonl.zstd
|
|
14
|
+
└─ <其他机器标识>/ ← 只读,拉取时读
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
三条规则:
|
|
18
|
+
|
|
19
|
+
1. **一轮对话结束即备份。** 你发出自然语言 → 到最终回复生成完毕算一轮,中间插话也算这一轮。落盘时机是 `turn/end` 后再等 4 秒,把这一轮写完的东西一次写掉。
|
|
20
|
+
2. **只写自己的目录。** 所有写入走同一个守卫(`assertWritable`),目标不在 `<工作区>/.dsh-sync/sessions/<本机标识>/` 下就抛错。别的机器的目录只读。
|
|
21
|
+
3. **备份是原字节复制。** 会话文件是多帧 zstd,整段重压会让 dsh 判 `corrupt session log`,所以从不重新压缩。
|
|
22
|
+
|
|
23
|
+
## 本机标识
|
|
24
|
+
|
|
25
|
+
格式 `<名称>-<特征值>`,例如 `DSH-b78936a7c1d2`。
|
|
26
|
+
|
|
27
|
+
**特征值在插件第一次安装时生成一次,之后永久固定,永不重算。**
|
|
28
|
+
|
|
29
|
+
- 来源是随机字节,不是主机名 / MachineGuid 复算
|
|
30
|
+
- 所以换主机名、注册表读不到、甚至重装系统都不会让标识漂移
|
|
31
|
+
- 生成时间记在 `machineHashGeneratedAt`,可追溯
|
|
32
|
+
- 唯一的迁移来源是老版本 state 里已有的哈希——那是既有目录名,必须原样继承,否则已备份的会话找不回来
|
|
33
|
+
|
|
34
|
+
**名称部分可改**:面板上填「李超台式」,标识变成 `李超台式-b78936a7c1d2`,特征值不变。改名后新备份写进新目录,旧目录不删。
|
|
35
|
+
|
|
36
|
+
状态文件:`~/.dsh/storages/sync-backup.json`
|
|
37
|
+
|
|
38
|
+
## 巡检
|
|
39
|
+
|
|
40
|
+
扫描每个「特定文件夹」的**直接子目录**:
|
|
41
|
+
|
|
42
|
+
| 情况 | 处理 |
|
|
43
|
+
|---|---|
|
|
44
|
+
| 有 `.dsh-sync/` 但本机未注册 | 列进「待注册」,**等你勾选确认后才注册** |
|
|
45
|
+
| 已注册但缺 `.dsh-sync/sessions/<本机标识>/` | 自动补齐(只建空目录) |
|
|
46
|
+
| 没有 `.dsh-sync/` | 忽略 |
|
|
47
|
+
| 已完整 | 忽略 |
|
|
48
|
+
|
|
49
|
+
**不会**扫进二级子目录,避免把 `工作区A/子目录` 误注册成独立工作区。
|
|
50
|
+
|
|
51
|
+
### 定时巡检:固定间隔
|
|
52
|
+
|
|
53
|
+
面板上勾选「定时巡检」并填间隔分钟数(默认 30,范围 1–1440)。
|
|
54
|
+
|
|
55
|
+
排程规则:
|
|
56
|
+
|
|
57
|
+
1. **DSH 启动后先巡检一次**(开机自动巡检,启动后约 8 秒)
|
|
58
|
+
2. **以这次巡检为起点计时**,之后每 N 分钟一次
|
|
59
|
+
3. 关闭再启用会重新首跑一次
|
|
60
|
+
4. 改间隔立即生效:从上次运行时间按新间隔重算,不用等完旧周期
|
|
61
|
+
5. 跨多个周期只补跑一次,不积压
|
|
62
|
+
|
|
63
|
+
实现上每 30 秒检查一次是否该跑;计时起点是进程内的 `nextDueAt`,不是持久化的时间戳,所以**每次启动都会立即巡检**,而不是"上次刚跑过就再等一会儿"。
|
|
64
|
+
|
|
65
|
+
DSH 关闭期间不跑,这是插件内定时器的固有限制。要关机也跑请用 `dsh-automation`。
|
|
66
|
+
|
|
67
|
+
注册时会顺手补齐本机特征文件夹。`workspaceRegistry.create()` 本身幂等,重复注册不会产生重复实体。
|
|
68
|
+
|
|
69
|
+
## 一键拉取
|
|
70
|
+
|
|
71
|
+
把别机备份的会话导回本机 `~/.dsh/sessions/`。冲突按**内容**判定,不看时间戳(NAS 同步会改 mtime):
|
|
72
|
+
|
|
73
|
+
| 情况 | 判定 | 动作 |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| 两边字节一致 | 相同 | 跳过 |
|
|
76
|
+
| 本机是别机的前缀 | 别机更新 | 覆盖本机(旧副本存进 `.conflicts/`) |
|
|
77
|
+
| 别机是本机的前缀 | 本机更新 | 跳过 |
|
|
78
|
+
| 两边都不是对方前缀 | 真分支 | 本机保留,别机那份导入为新 sessionId 并加标题后缀 |
|
|
79
|
+
|
|
80
|
+
会话日志是 append-only 的,所以「谁是前缀」用字节比较就能判,不需要解压。
|
|
81
|
+
|
|
82
|
+
拉取时会把 header 的 `cwd` 改写成本机这个工作区的路径——同一个工作区在宿主机是 `W:\`、在用户机是 `D:\`,不重写会归错工作区。只重写首帧,尾部帧逐字节保留。
|
|
83
|
+
|
|
84
|
+
**不需要重启 DSH。** 旧的说明写「新导入的会话需要完全退出 DSH 重开后才出现在侧栏」,那句话掩盖了真问题:当时缺的是**归属登记**,不是索引刷新。`sessionQuery.listSessions()` 每次调用都重新读盘 + 读归属表,没有缓存层,所以登记归属即时生效。
|
|
85
|
+
|
|
86
|
+
### 三者缺一不可
|
|
87
|
+
|
|
88
|
+
拉取要真正落到「对应的工作区」而不是「未分组」,四件事都得做全:
|
|
89
|
+
|
|
90
|
+
| 环节 | 旧版状态 | 后果 |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| 工作区目录里有别机备份 | 有 | — |
|
|
93
|
+
| 目录已注册成工作区 | **可能没有** | `/pull` 的匹配范围里都没有它,永远拉不到 |
|
|
94
|
+
| 会话日志写进 `~/.dsh/sessions/<projectKey>/` | 有 | — |
|
|
95
|
+
| sessionId 登记进 `workspace.json` 的归属账 | **没有** | 侧栏只认归属账,没登记就是「未分组」 |
|
|
96
|
+
|
|
97
|
+
1. **未注册的目录自动注册**:`/pull` 先扫一遍,凡是「目录里有 `.dsh-sync/`、且确实存在别的机器的会话备份」的,自动 `workspaceRegistry.create()` 后再拉。空壳目录不碰。传 `autoRegister: false` 可关闭。
|
|
98
|
+
2. **拉完必须登记归属**:DSH 只在「新建 / 分叉会话」时自己调 `attachSession()`;启动期的一次性 bootstrap 也只在 `initialized=false` 时跑一次。所以插件拉进来的会话必须自己补这一步,否则永远是「未分组」。
|
|
99
|
+
3. **header.id 必须跟着分叉改**:目录名 = `encodeSegment(header.id)`。只改目录不改 header,会让同一个 `header.id` 出现在两个 projectKey 下,`sessionPersistence` 直接抛 `duplicate JSONL session id`,整块工作区停摆。
|
|
100
|
+
|
|
101
|
+
### 补登记历史残留
|
|
102
|
+
|
|
103
|
+
旧版只写文件没登记,那些会话至今散在「未分组」里。面板上点「补登记归属」即可修回,或:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
curl -X POST http://127.0.0.1:<PORT>/api/local/dsh-sync-backup/attach/repair \
|
|
107
|
+
-H 'content-type: application/json' -d '{}'
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
默认**只补拉取产物**(别的机器备份目录里确实有它)。自动化插件和子代理留下的会话也命中 cwd,但它们不是拉来的,无差别补会让侧栏一次多出上百条——要全量补必须显式传 `{"all": true}`。
|
|
111
|
+
|
|
112
|
+
路径不可达的工作区(NAS 没挂载)登记会失败,会话仍留在磁盘,路径恢复后下次巡检自动补上。
|
|
113
|
+
|
|
114
|
+
## 界面
|
|
115
|
+
|
|
116
|
+
侧栏底部一个图标,点开是主面板:
|
|
117
|
+
|
|
118
|
+
- 本机标识(可改)
|
|
119
|
+
- 特定文件夹列表(可增删,命中其下的工作区才备份)
|
|
120
|
+
- 统计:本机已备份会话数、占用空间、其他机器可拉取数、最近备份时间
|
|
121
|
+
- 一键拉取 / 立即备份全部 / 补登记归属(可多选工作区)
|
|
122
|
+
- 最近记录
|
|
123
|
+
|
|
124
|
+
样式只用 `--dsw-alias-*` 主题令牌,跟随明暗模式。
|
|
125
|
+
|
|
126
|
+
## 技能同步
|
|
127
|
+
|
|
128
|
+
把母版文件夹里的技能拉到本地 `~/.agents/skills/` 部署。
|
|
129
|
+
|
|
130
|
+
### 四条规则
|
|
131
|
+
|
|
132
|
+
| 情况 | 处理 |
|
|
133
|
+
|---|---|
|
|
134
|
+
| 母版有、本地无 | 拉取到本地 |
|
|
135
|
+
| 两侧都有,**母版版本更高** | 拉取覆盖本地(旧版备份到 `.pre-pull-<时间戳>/`) |
|
|
136
|
+
| 两侧都有,**本地版本更高**或内容不同 | **只报告**,你在面板上勾选并点「推送到母版」才执行 |
|
|
137
|
+
| 本地有、母版没有 | **跳过**,不放进母版,本地也原样保留 |
|
|
138
|
+
|
|
139
|
+
母版里删掉的技能,本地**什么都不做**——不删、不搬,就是留着不用。
|
|
140
|
+
|
|
141
|
+
### 记忆插件的技能优化:同步时自动处理
|
|
142
|
+
|
|
143
|
+
记忆插件(`dsh-mnemon`)会自己审查会话、写技能草稿,但草稿写完停在
|
|
144
|
+
`ledger.json` 的 `drafts[]` 里等人在面板上点「采纳」。放着不管就越积越多
|
|
145
|
+
(本机曾积 12 份),而技能同步只看磁盘上的 `SKILL.md`——草稿还没落地,
|
|
146
|
+
一条都同步不出去。
|
|
147
|
+
|
|
148
|
+
所以**每轮技能同步开头先做一次预处理**,把两端接起来:
|
|
149
|
+
|
|
150
|
+
1. 读 `~/.mnemon/skills/ledger.json`,取 `status="ready"` 的草稿(同技能多份取最新)
|
|
151
|
+
2. 校验:技能名能当目录名、frontmatter 的 `name` 对得上、`update` 草稿的
|
|
152
|
+
`baseHash` 仍等于当前 `SKILL.md` 的 sha256
|
|
153
|
+
3. 落地到本机 `~/.agents/skills/<技能名>/SKILL.md`,旧版备份到
|
|
154
|
+
`.pre-mnemon-<时间戳>/`
|
|
155
|
+
4. 立刻推回母版,旧版备份到 `_backup/<技能名>.<时间戳>/`,并在母版
|
|
156
|
+
`_技能修改记录.md` 写一条,触发方式记为「记忆插件优化」
|
|
157
|
+
5. 然后照常比对/拉取/推送——这一轮看到的已经是落地后的内容
|
|
158
|
+
|
|
159
|
+
为什么单独推一次:草稿是本插件刚落的,谱系里还没记过这份内容,按判据会
|
|
160
|
+
落进「待你决定」。这一步是**明知新**,不猜。
|
|
161
|
+
|
|
162
|
+
四条边界:
|
|
163
|
+
|
|
164
|
+
| 情况 | 行为 |
|
|
165
|
+
|---|---|
|
|
166
|
+
| 本机 `SKILL.md` 已经是这份草稿的内容 | 跳过(`already-applied`),重复跑幂等 |
|
|
167
|
+
| `update` 草稿的 `baseHash` 与当前文件不符 | 跳过(`stale-base`),期间有人改过,交给待裁决 |
|
|
168
|
+
| `create` 草稿撞上已有同名技能 / 技能属性为「母版有·本机不拉取」 | 跳过 |
|
|
169
|
+
| `targetRoot` 是 `project-*` | 跳过(`project-scope`),项目技能不进母版 |
|
|
170
|
+
|
|
171
|
+
#### 落地之后要结案,否则面板永远显示「待审」
|
|
172
|
+
|
|
173
|
+
只把文件写进去、账本不动,面板上那条建议就永远停在 `review`、草稿永远停在
|
|
174
|
+
`ready`——看起来一直有活没干。所以落地成功后会顺手把账本里对应的
|
|
175
|
+
`draft` 与 `suggestion` 标成 `adopted`。
|
|
176
|
+
|
|
177
|
+
写账本是安全的,因为记忆插件每次操作都从磁盘重读 `ledger.json`
|
|
178
|
+
(`SkillsStore.ledger()` 里就是 `readJson`),没有内存缓存。只要格式对得上:
|
|
179
|
+
|
|
180
|
+
| 项 | 规则 |
|
|
181
|
+
|---|---|
|
|
182
|
+
| `generation` | 每次写入 +1(插件用它算 revision,不 +1 会被当成没变) |
|
|
183
|
+
| `reviews` | 截到最新的 200 条 |
|
|
184
|
+
| `drafts` | 非 `ready` 的只留 60 份(`ready` 全留,等处理) |
|
|
185
|
+
| 落盘 | 先写 `<file>.<pid>-<uuid>.tmp` 再 rename,`mode 600` |
|
|
186
|
+
| 并发 | 写之前比对 size + mtime,期间被改过就整体放弃,下一轮再来 |
|
|
187
|
+
|
|
188
|
+
结案判据仍然只用内容哈希:**磁盘上的 `SKILL.md` 必须还等于草稿内容**才结案。
|
|
189
|
+
刚落完就被人改回去、或者本机根与草稿目标根对不上,一律不结案,留在 `ready`
|
|
190
|
+
等下一轮。
|
|
191
|
+
|
|
192
|
+
面板上「记忆插件优化自动处理」可关(`skills.mnemonAuto`,默认开);
|
|
193
|
+
关键设置:`POST /skills/config` 传 `{"mnemonAuto": false}`。
|
|
194
|
+
|
|
195
|
+
#### 已经积压的历史账本
|
|
196
|
+
|
|
197
|
+
外部流程(本插件早期版本、手工采纳)落地过的草稿,账本里仍停在 `review`。
|
|
198
|
+
用 `C:\DSH-Work\mnemon-reconcile\reconcile.mjs` 一次性对账:
|
|
199
|
+
|
|
200
|
+
```powershell
|
|
201
|
+
node reconcile.mjs # 预演,只报告
|
|
202
|
+
node reconcile.mjs --apply # 真写(先自动备份 ledger.json)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
它按同样两条判据分流:内容对得上 → 结案并补一条 `reason=evolution` 的版本记录
|
|
206
|
+
(正文落到 `versions/<sha256(skillKey) 前16位>/<id>.md`,与插件 `recordVersion` 同格式);
|
|
207
|
+
对不上 → 草稿标 `discarded`、建议退回 `pending` 重新起稿。早已过期的建议顺手删掉。
|
|
208
|
+
|
|
209
|
+
### 新旧怎么判
|
|
210
|
+
|
|
211
|
+
**不看版本号。** 记忆插件优化技能时不升 version,按 version 比只会把已经优化的
|
|
212
|
+
版本判成「版本相同但内容不同」,永远卡在待确认。
|
|
213
|
+
|
|
214
|
+
**也不看文件 mtime。** NAS 同步、拉取、拷贝都会刷 mtime,它跟内容新旧无关。
|
|
215
|
+
|
|
216
|
+
判据按硬度依次尝试,命中即停:
|
|
217
|
+
|
|
218
|
+
| 顺序 | 判据 | 依据 | basis |
|
|
219
|
+
|---|---|---|---|
|
|
220
|
+
| 1 | **内容修改时间** | 谱系文件记录的「两侧各自的内容何时变成现在这样」 | `content-time` |
|
|
221
|
+
| 2 | **只一侧变过** | 上次比对时记的内容,现在只有一侧还对得上 → 另一侧是改的 | `content-time` |
|
|
222
|
+
| 3 | **记忆插件演化记录** | `~/.mnemon/skills/ledger.json` 里 `reason=evolution` 的哈希 + 时间 | `mnemon` |
|
|
223
|
+
| 4 | **历史快照时间** | 母版 `_backup/`、本机 `.pre-pull-*`、记忆插件 `versions/`、母版修改记录 | `content-time` |
|
|
224
|
+
| 5 | **谱系基线** | 上次两侧一致时的内容树;只有一侧偏离 | `lineage` |
|
|
225
|
+
| 6 | **包含关系** | 一侧完全包含另一侧(纯新增,没改写原意) | `superset` |
|
|
226
|
+
|
|
227
|
+
六段都拿不到证据 → 归「待你决定」,不自动覆盖任何一方。
|
|
228
|
+
|
|
229
|
+
#### 「内容修改时间」到底是什么
|
|
230
|
+
|
|
231
|
+
不是 `stat` 出来的 mtime,是同步插件**亲眼看到内容变成现在这样**的那一刻:
|
|
232
|
+
|
|
233
|
+
- 每次比对都会把两侧当前的内容哈希记进 `<母版>/.skill-lineage.json`
|
|
234
|
+
- **同一个哈希再次观测,时间保持不动**——内容没变就不算改动,哪怕文件被 NAS
|
|
235
|
+
搬过、mtime 全变了
|
|
236
|
+
- 内容真变了才刷新时间
|
|
237
|
+
|
|
238
|
+
所以「谁的内容时间更晚,谁就是新版」这句话,在同步场景下才成立;
|
|
239
|
+
换成 mtime 就完全不成立。
|
|
240
|
+
|
|
241
|
+
#### 记忆插件的修改留下了什么
|
|
242
|
+
|
|
243
|
+
三样东西,全部只读,记忆插件一行代码都不用改:
|
|
244
|
+
|
|
245
|
+
| 痕迹 | 位置 | 能证明什么 |
|
|
246
|
+
|---|---|---|
|
|
247
|
+
| **演化版本记录** | `~/.mnemon/skills/ledger.json` → `versions[]` | 采纳草稿时写一条 `reason="evolution"`,`hash` 正是新 SKILL.md 的 sha256 —— **「这次是记忆插件改的」最硬的证据** |
|
|
248
|
+
| **历史全文快照** | `~/.mnemon/skills/versions/<sha256(skillKey) 前16位>/<versionId>.md` | 当时那份全文,可做三方对比 |
|
|
249
|
+
| **演化建议/草稿** | `ledger.json` → `suggestions[]` / `drafts[]` | 还没采纳的改动在 `drafts[].content`,已采纳的进 `versions[]` |
|
|
250
|
+
|
|
251
|
+
它的目录名就是 `sha256(skillKey).hex.slice(0,16)`,`skillKey` 形如
|
|
252
|
+
`user-dsh:kb-zhishiku` —— 不用猜,照算就能对上号。
|
|
253
|
+
|
|
254
|
+
**注意**:记忆插件平常的技能注册表不在账本里(那是宿主扫磁盘得出的),账本只记
|
|
255
|
+
「安装」和「演化」。所以账本里查不到某个技能,**不能**推断是谁改的——
|
|
256
|
+
这种情况下判据继续往后走,不硬判。
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
$ node tools/skill-lineage.mjs --status
|
|
260
|
+
母版:D:\Ai工作空间\技能库
|
|
261
|
+
本机:C:\Users\DSH\.agents\skills
|
|
262
|
+
谱系:43 条观测
|
|
263
|
+
账本:2 个技能有记忆插件记录
|
|
264
|
+
|
|
265
|
+
一致 43 · 待你决定 4
|
|
266
|
+
|
|
267
|
+
本机新 demo [content-time] 上次比对后本机内容变了(11-01 21:51 之后)
|
|
268
|
+
母版新 demo2 [content-time] 母版这份内容更晚(11-01 22:03 vs 10-30 10:18)
|
|
269
|
+
本机新 demo3 [superset] 本机内容完全包含母版内容(只增不减)
|
|
270
|
+
待你决定 x [-] 两侧内容都改写过、没有快照或演化记录可比,需要你决定(保留哪边)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### 比对就是同步
|
|
274
|
+
|
|
275
|
+
规矩:**同步属性的技能,比对时就把能定下来的做掉**。
|
|
276
|
+
|
|
277
|
+
| 判定 | 动作 |
|
|
278
|
+
|---|---|
|
|
279
|
+
| 母版更新 | **自动拉取**覆盖本机(旧版备份到 `.pre-pull-<时间戳>/`) |
|
|
280
|
+
| 本机更新(同步属性) | **自动推送**覆盖母版(旧版备份到 `_backup/<技能>.<时间戳>/`,并写修改记录) |
|
|
281
|
+
| 判不出来 | **什么都不动**,列在面板上等你裁决,附差异摘要 |
|
|
282
|
+
|
|
283
|
+
`POST /skills/scan` 带 `{auto: false}` 时退化为纯比对(只报告,不动文件);
|
|
284
|
+
面板上的「比对」按钮走自动模式,定时器也是。
|
|
285
|
+
|
|
286
|
+
判不出来的那些会带一份差异摘要,一眼看出两边差在哪:
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
母版独有 3 行、本机独有 12 行(14770 vs 14735 字节)
|
|
290
|
+
- 旧的那一行
|
|
291
|
+
+ 新的那一行
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### 为什么不能靠 version 硬撑
|
|
295
|
+
|
|
296
|
+
版本号只能表达「谁手工标得更正式」,表达不了「谁的内容是后来的」。
|
|
297
|
+
存量 48 个技能里:
|
|
298
|
+
|
|
299
|
+
- `kb-zhishiku` 本机 2.0.0 / 母版 1.0.0 —— 版本号确实能分
|
|
300
|
+
- `skill-creator` 两侧都没有 version —— 版本号完全失效
|
|
301
|
+
- `expert-copywriting` 两侧都是 1.0.0 但内容不同 —— 版本号给出错误结论
|
|
302
|
+
|
|
303
|
+
现在这三种都由内容时间处理;真分不出来的才落到「待你决定」。
|
|
304
|
+
|
|
305
|
+
### 给技能补 version
|
|
306
|
+
|
|
307
|
+
`tools/add-version.mjs` 批量补 `version: 1.0.0`:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
node tools/add-version.mjs --dry # 只看会改哪些
|
|
311
|
+
node tools/add-version.mjs # 实际写入
|
|
312
|
+
node tools/add-version.mjs --dir <路径> # 指定技能根
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
行为:
|
|
316
|
+
|
|
317
|
+
- 已有 `version` 的**跳过**,不覆盖
|
|
318
|
+
- 无 front matter 的**跳过**,不强加
|
|
319
|
+
- 保留原编码(UTF-8 无 BOM / 有 BOM)
|
|
320
|
+
- 保留换行风格(CRLF / LF)
|
|
321
|
+
- 改写前备份为 `SKILL.md.bak-<时间戳>`
|
|
322
|
+
- 幂等:重复运行不产生第二份备份
|
|
323
|
+
|
|
324
|
+
**2026-10-29 已对 42 个技能执行**,全部补为 `1.0.0`,42 份备份在各自目录下。`wx-send-image` 是空目录(无 SKILL.md),未处理。
|
|
325
|
+
|
|
326
|
+
**2026-11-01 起 `version` 不再参与新旧判定**——它只是个给人看的标记。
|
|
327
|
+
判定改按内容修改时间,见上文「新旧怎么判」。补 version 这一步留着,
|
|
328
|
+
只是因为部分技能商店/清单用得上。
|
|
329
|
+
|
|
330
|
+
### 单机属性(技能列表)
|
|
331
|
+
|
|
332
|
+
每个技能有一个**只存本机**的属性,三种值:
|
|
333
|
+
|
|
334
|
+
| 属性 | 含义 | 改为该属性时做的事 |
|
|
335
|
+
|---|---|---|
|
|
336
|
+
| **同步** | 正常双向 | 本机有 → 推送到母版;母版有本机无 → 拉到本机 |
|
|
337
|
+
| **本机独属** | 本机有、母版不该有 | **删除母版**,保留本机(母版备份到 `_backup/`) |
|
|
338
|
+
| **母版有·本机不拉取** | 母版有、本机不要 | **删除本机**,保留母版(本机备份到 `.pre-mode-<时间戳>/`) |
|
|
339
|
+
|
|
340
|
+
**未显式设置的按自动判定**:本机有、母版没有 → 本机独属;其余 → 同步。
|
|
341
|
+
|
|
342
|
+
属性只写本机 state(`~/.dsh/storages/sync-backup.json` 的 `skills.modes`),不随母版走,所以每台机器可以各设各的。
|
|
343
|
+
|
|
344
|
+
**对母版的删除和新增都会写进母版修改记录**,记录里能看出谁、什么时候、加了还是删了。
|
|
345
|
+
|
|
346
|
+
修改本机或改属性失败时不会落属性——只有实质动作成功才记。
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
**每次插件对母版写入,都在母版根目录追加一条记录。**
|
|
351
|
+
|
|
352
|
+
文件:`<母版>/_技能修改记录.md`
|
|
353
|
+
|
|
354
|
+
- 不存在时自动创建(带表头)
|
|
355
|
+
- 已存在则只追加,保留全部历史
|
|
356
|
+
- 设置母版路径时就会建好,不用等第一次推送
|
|
357
|
+
|
|
358
|
+
字段:
|
|
359
|
+
|
|
360
|
+
| 列 | 内容 |
|
|
361
|
+
|---|---|
|
|
362
|
+
| 时间 | 本机时间,精确到秒 |
|
|
363
|
+
| 操作者 | `<本机名称> (<本机标识>)` |
|
|
364
|
+
| 操作 | `覆盖` / `新增` |
|
|
365
|
+
| 技能 | 技能名 |
|
|
366
|
+
| 触发方式 | `用户授权` / `自动` |
|
|
367
|
+
| 说明 | 覆盖时记旧版备份路径,新增时记「母版此前无此技能」 |
|
|
368
|
+
|
|
369
|
+
例:
|
|
370
|
+
|
|
371
|
+
```
|
|
372
|
+
| 2026-10-29 16:20:31 | 李超台式 (DSH-7e6b1bb7e052) | 覆盖 | expert-code-review | 用户授权 | 旧版备份于 _backup/expert-code-review.20261029-162031 |
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
只有**实际写入母版**才记录。拉取(母版 → 本地)不碰母版,不产生记录。
|
|
376
|
+
|
|
377
|
+
竖线和换行会转义,不会破坏表格。母版只读时记录失败不阻断推送,只在响应里报错。
|
|
378
|
+
|
|
379
|
+
面板上「查看母版修改记录」可直接读最近 30 条。
|
|
380
|
+
|
|
381
|
+
### 推送本地独有技能
|
|
382
|
+
|
|
383
|
+
本地有、母版没有的技能默认跳过。要让它进入母版,在面板上勾选后点「推送到母版」——此时属于**用户明确要求**,操作类型记为 `新增`。
|
|
384
|
+
|
|
385
|
+
### 定时(技能同步)
|
|
386
|
+
|
|
387
|
+
- 开机拉取一次(启动后约 12 秒)
|
|
388
|
+
- 之后每 `intervalMinutes` 分钟一次,**默认 120**
|
|
389
|
+
- 改间隔立即生效
|
|
390
|
+
- 与巡检共用同一套排程语义
|
|
391
|
+
- 每轮开头先跑记忆插件预处理(见上文),可在面板关掉
|
|
392
|
+
|
|
393
|
+
### 与旧脚本的关系
|
|
394
|
+
|
|
395
|
+
**本插件接管技能同步。** 如果 `Sync-Skills.ps1` 还在跑,它和本插件会同时对 `~/.agents/skills` 动手,可能互相覆盖。请停掉:
|
|
396
|
+
|
|
397
|
+
```powershell
|
|
398
|
+
# 停掉计划任务
|
|
399
|
+
Unregister-ScheduledTask -TaskName 'DSH-SyncSkills' -Confirm:$false
|
|
400
|
+
# 停掉登录自启的守护(如有)
|
|
401
|
+
Get-Process | Where-Object { $_.CommandLine -like '*Sync-Skills*' }
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
推送时母版旧版会备份到 `<母版>/_backup/<技能名>.<时间戳>/`。
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
## 接口
|
|
409
|
+
|
|
410
|
+
`/api/local/dsh-sync-backup/`
|
|
411
|
+
|
|
412
|
+
| 方法 | 路径 | 用途 |
|
|
413
|
+
|---|---|---|
|
|
414
|
+
| GET | `/state` | 配置、匹配工作区、统计、巡检与技能状态 |
|
|
415
|
+
| POST | `/config` | 保存 folders / machineName |
|
|
416
|
+
| POST | `/backup` | 备份(`workspaceIds` 可选) |
|
|
417
|
+
| POST | `/preview` | 预判拉取会做什么,不写文件 |
|
|
418
|
+
| POST | `/pull` | 拉取(`workspaceIds`、`machines` 可选) |
|
|
419
|
+
| POST | `/patrol/scan` | 巡检扫描,只读 |
|
|
420
|
+
| POST | `/patrol/register` | 注册选中的目录 + 补齐特征文件夹 |
|
|
421
|
+
| POST | `/patrol/repair` | 补全缺特征文件夹的工作区 |
|
|
422
|
+
| POST | `/patrol/config` | 定时巡检开关与间隔分钟数 |
|
|
423
|
+
| POST | `/skills/scan` | 技能比对,只读 |
|
|
424
|
+
| POST | `/skills/pull` | 拉取母版技能到本地 |
|
|
425
|
+
| POST | `/skills/push` | 把本地技能推回母版(覆盖或新增),并写修改记录 |
|
|
426
|
+
| POST | `/skills/config` | 母版路径、开关、间隔 |
|
|
427
|
+
| POST | `/skills/changelog` | 读母版修改记录 |
|
|
428
|
+
| POST | `/skills/list` | 技能列表 + 单机属性 |
|
|
429
|
+
| POST | `/skills/mode` | 切换属性并执行实质动作 |
|
|
430
|
+
|
|
431
|
+
## 测试
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
node test/projectkey.test.mjs # projectKey 编码与真实目录名比对
|
|
435
|
+
node test/zstd.test.mjs # 多帧 zstd 边界、尾部保留、前缀判定
|
|
436
|
+
node test/guard.test.mjs # 写权限边界(含同名前缀陷阱)
|
|
437
|
+
node test/api.test.mjs # 基础端点、前缀匹配、去重
|
|
438
|
+
node test/e2e.test.mjs # 备份 → 拉取 → 分叉 全流程
|
|
439
|
+
node test/patrol.test.mjs # 巡检发现、补全、标识拆分拼接
|
|
440
|
+
node test/patrol-api.test.mjs # 巡检四个端点 + 改名
|
|
441
|
+
node test/hash.test.mjs # 特征值生成规则
|
|
442
|
+
node test/persist.test.mjs # 跨进程持久性:生成后永不改变
|
|
443
|
+
node test/schedule.test.mjs # 排程:开机首跑 + 间隔计时 + 改配置立即生效
|
|
444
|
+
node test/host-load.test.mjs # Host 半加载与清理
|
|
445
|
+
node test/skills.test.mjs # 技能:版本比较、树哈希、比对规则、拉取推送
|
|
446
|
+
node test/skills-api.test.mjs # 技能四个端点
|
|
447
|
+
node test/changelog.test.mjs # 修改记录:自动建、追加、转义、只读容错
|
|
448
|
+
node test/changelog-api.test.mjs # 推送时写入记录、新增/覆盖区分
|
|
449
|
+
node test/addversion.test.mjs # 补 version:编码/换行保留、幂等、跳过规则
|
|
450
|
+
node test/client.test.mjs # 客户端契约:注入、注册配对、locale、组件可调用
|
|
451
|
+
node test/mode.test.mjs # 单机属性:三种切换的实质动作与审计
|
|
452
|
+
node test/mode-api.test.mjs # 列表与属性切换端点
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
共 325 项。
|
|
456
|
+
|
|
457
|
+
## 已知限制
|
|
458
|
+
|
|
459
|
+
- 拉取进来的会话要重启 DSH 才可见(无热重扫接口)。
|
|
460
|
+
- 不接管任何既有同步脚本,只管 `.dsh-sync/sessions/<标识>/` 这一块。
|
|
461
|
+
- 冲突留档放在本机标识目录内的 `.conflicts/`,不写工作区其他位置。
|