dsh-instruction-memory 1.0.14 → 1.0.15
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/CHANGELOG.md +265 -0
- package/README.md +83 -6
- package/lib/client.js +617 -78
- package/lib/index.js +505 -46
- package/package.json +4 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# 更新日志(Changelog)
|
|
2
|
+
|
|
3
|
+
本文件记录 dsh-instruction-memory 的逐版本变更。格式参照 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/);
|
|
4
|
+
历史版本日期取自 Git 提交记录。每个已发布版本的详细背景见对应提交说明与 README。
|
|
5
|
+
|
|
6
|
+
> 1.0.10 / 1.0.11 / 1.0.12 因当时尚未配置 CI 发布,由本地令牌发布,**无 tag、无 provenance**;
|
|
7
|
+
> 1.0.13 起由 CI 经 OIDC Trusted Publishing 发布(首个带 provenance 的版本)。
|
|
8
|
+
> 这些历史 tag **不应回填**——npm 上的版本号已被占用。
|
|
9
|
+
|
|
10
|
+
## [1.0.15] — 2026-10-03
|
|
11
|
+
|
|
12
|
+
本轮为一次系统性缺陷清查后的批量修复,每一项都有对应的回归测试(`npm test` 五套件全绿:
|
|
13
|
+
lint、host-range、smoke、contract、verify)。
|
|
14
|
+
|
|
15
|
+
### 安全
|
|
16
|
+
|
|
17
|
+
- **Sec-Fetch-Site: cross-site 拒绝**:携带该头的请求一律 403,配合既有的
|
|
18
|
+
Origin/Host 校验堵住"同源表单可绕过 content-type 巧合"的窗口。
|
|
19
|
+
- **Host 头校验豁免逻辑修正**:此前仅 `0.0.0.0` 绑定豁免;现在**只有绑定回环**
|
|
20
|
+
(`127.0.0.1` / `localhost` / `::1` / 未指定)时才要求 Host 为回环,绑定
|
|
21
|
+
`::`、`0.0.0.0` 或具体 IP(对外发布的部署)不再误拒合法请求。
|
|
22
|
+
|
|
23
|
+
### 并发与数据完整性
|
|
24
|
+
|
|
25
|
+
- **跨进程写锁**:写盘前独占创建 `memory.json.lock`,写完即删;持锁超 10 秒视为
|
|
26
|
+
持锁进程已崩溃并接管,等待 2 秒仍拿不到锁**不阻断保存**(last-write-wins),
|
|
27
|
+
原子替换保证任何交错下文件不损坏。
|
|
28
|
+
- **修订号(`rev`)乐观锁**:每次成功写盘 `rev` 加一落盘;客户端修改请求携带
|
|
29
|
+
`baseRev`,与服务端不一致(另一窗口/进程刚提交)则拒绝并返回最新快照,防止
|
|
30
|
+
旧窗口把别的窗口刚删的条目"复活"。不带 `baseRev` 的旧客户端保持原行为。
|
|
31
|
+
- **未来格式版本拒绝**:`memory.json` 声明 `version` 非 1 时拒绝载入并**暂停一切
|
|
32
|
+
修改**(此时保存会用 v1 覆盖文件、可能损坏数据),提示升级插件或手动改回。
|
|
33
|
+
- **手改选项披露**:手改的 `enabled`(非布尔)/ `budgetChars`(越界或非数字)按
|
|
34
|
+
规则修正时,在设置页明确提示**原值 → 修正值**,不再静默改写。
|
|
35
|
+
- **`saved` 字段语义修正**:只反映"本次操作是否真的写盘"——`state` / `reload` /
|
|
36
|
+
`export-data` 等读取操作一律 `false`,载入即写回的分支(.bak 恢复、旧位置迁移、
|
|
37
|
+
首次物化)才为 `true`。
|
|
38
|
+
- **损坏恢复分支 `onDisk` 修正**:主文件解析失败(SyntaxError)时文件明明在磁盘上,
|
|
39
|
+
快照却报 `onDisk:false`,导致设置页显示"尚未写入磁盘"——已修正。
|
|
40
|
+
|
|
41
|
+
### 用户体验
|
|
42
|
+
|
|
43
|
+
- **编辑器取消确认**:草稿有未保存修改时点「取消」先确认;空白草稿或未改动副本
|
|
44
|
+
仍静默取消,不打扰。
|
|
45
|
+
- **外部改动自动刷新(SSE)**:设置页经 `EventSource` 订阅事件流,其他窗口保存、
|
|
46
|
+
其他进程或手工改写 `memory.json` 时**自动更新**,无需手动「重新载入」。宿主以
|
|
47
|
+
`fs.watch` 监听存储**目录**(原子替换在 Windows 上会废掉文件级监听)、去抖 250ms
|
|
48
|
+
后经与写盘相同的队列重读,内容确有变化才重载推送;发起修改的窗口按内容比对丢弃
|
|
49
|
+
自己写入的回声帧(否则刚出现的"已保存"提示会被冲掉)。流不可用时降级为手动刷新,
|
|
50
|
+
不弹错误。
|
|
51
|
+
- **删除撤销**:删除成功后出现撤销条,10 秒内可一键恢复刚删除的条目,到期自动
|
|
52
|
+
消失;恢复失败(如恰好撞上 rev 冲突)时撤销条保留并重新计时,可基于最新快照重试。
|
|
53
|
+
- **界面中英双语(i18n)**:内置 `zh` / `en` 两套字典。优先接入宿主官方
|
|
54
|
+
LocaleService(语言切换即时生效,导航标签随语言重译);无该服务的环境按浏览器
|
|
55
|
+
语言回退、再回退中文。两套字典键集与占位符由测试钉死一致。
|
|
56
|
+
- **宿主请求超时**:请求 15 秒无响应即显式报错并解除按钮锁定——此前挂死的宿主会让
|
|
57
|
+
面板永远停在「保存中…」。
|
|
58
|
+
- **导入文件大小预检**:选错文件(几十上百 MB 的日志/数据集)不再整读入内存、卡死
|
|
59
|
+
UI、上传后才被 413 拒绝——按宿主公布的 `maxBodyChars` 推算宽松上限(约 4 倍,
|
|
60
|
+
满配导出 ≈4 MB 必不误伤),读文件前即拒绝并给出具体数字;宿主未公布上限时不拦截。
|
|
61
|
+
- **条目更新时间展示**:每条指令显示「更新于 …」(由宿主在每次保存时盖章);从旧
|
|
62
|
+
导入文件来的无时间戳条目不显示,不会出现 1970 年。
|
|
63
|
+
|
|
64
|
+
### 文档
|
|
65
|
+
|
|
66
|
+
- README 新增**反向代理部署指引**(`X-Forwarded-Host` / `Host` 改写与校验豁免的关系)
|
|
67
|
+
与 SSE 自动刷新章节;并发保护章节补充文件锁与 `rev` 机制;组成表补记 `install.mjs`
|
|
68
|
+
与 `lint.mjs`。
|
|
69
|
+
|
|
70
|
+
### 测试
|
|
71
|
+
|
|
72
|
+
- 新增 `lint.mjs` **语法门禁**:`node --check` 解析所有随包发布与本仓库运行的 JS
|
|
73
|
+
文件(含 `lib/client.js` 这类生产环境只由 DSH webview 加载、node 从不导入的文件),
|
|
74
|
+
已并入 `npm test` 与发布前自检。
|
|
75
|
+
- CI 测试矩阵扩展到 **Node 20 / 22 / 24 × ubuntu / windows**:20 是
|
|
76
|
+
`engines.node >= 20` 声明的下限,24 是当前 LTS。
|
|
77
|
+
- `contract-test.mjs` 新增:rev 冲突拒绝、未来版本拒绝、Host 绑定矩阵、
|
|
78
|
+
Sec-Fetch-Site、SSE(连接即推快照、变更推送、外部文件变更推送、回声帧与响应
|
|
79
|
+
快照逐字节一致、关闭后退订)等 60+ 条断言。
|
|
80
|
+
- `verify.mjs` 新增 `draftIsDirty` / `sameSnapshot` / `confirmDiscard` /
|
|
81
|
+
`importSizeVerdict` 纯函数断言,`normalizeResult` 的信封边界用例(冲突信封、缺省
|
|
82
|
+
字段、不可用形态),以及 i18n 断言(zh/en 键集一致、占位符对齐、回退与插值行为)
|
|
83
|
+
与撤销 TTL 钉死。
|
|
84
|
+
- **修复测试桩的环境泄漏(首个 CI 矩阵运行抓到的真实缺陷)**:`verify.mjs` 伪造了
|
|
85
|
+
`window` 却沿用 Node 自带的 `navigator`(Node ≥ 21 报告**机器**语言),面板语言
|
|
86
|
+
随运行机器漂移——中文开发机上碰巧绿、CI 的 en_US 运行器上必红(Node 20 因尚无
|
|
87
|
+
navigator 全局而侥幸通过)。现在测试桩用 `Object.defineProperty` 钉死伪浏览器的
|
|
88
|
+
navigator(zh-CN),并以带查询串的二次 import 实例化英文浏览器,把两条字典选取
|
|
89
|
+
路径都变成显式断言。
|
|
90
|
+
|
|
91
|
+
## [1.0.14] — 2026-09-29
|
|
92
|
+
|
|
93
|
+
### 修复
|
|
94
|
+
|
|
95
|
+
- **声明的宿主范围此前不是门禁**:范围写在 `dsh.engines.dsh` 与
|
|
96
|
+
`dsh.compatibility.dsh`,但宿主的兼容性检查读的是 `manifest.peerDependencies`,
|
|
97
|
+
且该字段**缺失时直接跳过检查**——范围外的宿主可以无警告地装上。本版补上
|
|
98
|
+
`peerDependencies` 条目使范围真正生效;三处范围副本统一以 `host-range-test.mjs`
|
|
99
|
+
生成的值为唯一事实来源。
|
|
100
|
+
- 已用宿主自身的门禁函数实测:`0.2.0-rc.1` 通过、`0.1.2-rc.1` 被拦;删掉 peer
|
|
101
|
+
字段可复现旧的跳过行为;含负向对照。
|
|
102
|
+
|
|
103
|
+
### 文档
|
|
104
|
+
|
|
105
|
+
- 兼容性章节修正:已发布宿主 28 个、范围内 13 个(原 27/12);tier 1 纳入
|
|
106
|
+
`0.2.0-rc.1` 并单独标出一项非直接观测;"未声明的未来 minor 预发布无法被任何
|
|
107
|
+
范围覆盖"是按求值模式而定的;市场字段的结论标注为第三方证据而非官方。
|
|
108
|
+
|
|
109
|
+
## [1.0.13] — 2026-09-28
|
|
110
|
+
|
|
111
|
+
### 文档 / 基础设施
|
|
112
|
+
|
|
113
|
+
- 维护者章节改为真实的发布流程:推送 `v*` tag → CI 经 OIDC Trusted Publishing
|
|
114
|
+
发布(带 provenance);写明 npm 侧 trusted-publisher 字段与"保存时不校验、
|
|
115
|
+
填错要到首次发布才报 ENEEDAUTH"的坑;记录 1.0.10–1.0.12 为何是本地发布。
|
|
116
|
+
- **本版是首个由 CI 发布的版本**(首个带 provenance attestation)。
|
|
117
|
+
|
|
118
|
+
## [1.0.12] — 2026-09-28
|
|
119
|
+
|
|
120
|
+
### 文档(无代码变更)
|
|
121
|
+
|
|
122
|
+
- 兼容性声明按**证据分级**重述,不再笼统:"真实启动"5 个版本(插件加载、注入
|
|
123
|
+
真实到达组装后的提示词、HTTP 路由应答、客户端模块送达);"契约核对"范围内
|
|
124
|
+
全部 12 个已发布版本(逐一核对本插件用到的每个符号);"真实挂载"cordis
|
|
125
|
+
4.0.2 / 4.0.4。
|
|
126
|
+
- 记录两条不改范围的理由:`ctx.inject`(1.0.10 新增的唯一硬依赖)在全部 6 个已
|
|
127
|
+
发布 cordis 版本中都存在;范围拒绝的 15 个版本 API 完整但从未启动过,下限
|
|
128
|
+
不应放宽到没人跑过的版本上。
|
|
129
|
+
- 记录运维事实:早期 DSH 用 caret 范围声明兄弟包,今天安装旧版 DSH 会把兄弟包
|
|
130
|
+
解析到当前版本(抽样 10 个中 7 个漂移、3 个精确锁定)。
|
|
131
|
+
|
|
132
|
+
## [1.0.11] — 2026-09-27
|
|
133
|
+
|
|
134
|
+
### 修复
|
|
135
|
+
|
|
136
|
+
- **首次运行不再在启动时写空存储文件**:那是一次"无会话、无轮次、无用户动作"
|
|
137
|
+
的写盘;现在文件由第一个真正需要它的动作(保存、导入、设置页读 `state`)物化,
|
|
138
|
+
此前面板显示既有的"尚未写入磁盘"状态。启动期的两个写盘分支(.bak 恢复、旧位置
|
|
139
|
+
迁移)不变——它们都要求磁盘上已有真实数据。
|
|
140
|
+
- **挂载日志不再谎报 `injected=`**:改为 `section=N chars (pending assembly)` 并
|
|
141
|
+
上报可观测的 `onDisk=` 与 `pulls=`(注册的 thunk 被宿主求值的次数,即本节真实
|
|
142
|
+
到达提示词的次数)。渲染内容不变:有无 logger 字节一致。
|
|
143
|
+
|
|
144
|
+
## [1.0.10] — 2026-09-25
|
|
145
|
+
|
|
146
|
+
### 修复(端到端验证发现的七个缺陷,逐项先复现后修复并加回归守卫)
|
|
147
|
+
|
|
148
|
+
- 写盘失败后内存状态(与注入的提示词)领先于 `memory.json`,模型继续遵守一条
|
|
149
|
+
已被告知"未保存"的指令——变更改为经事务化助手提交,失败时回滚内存并重新同步
|
|
150
|
+
提示词。
|
|
151
|
+
- `memory.json` 缺失而 `.bak` 完好时,下次启动得到**空存储**、用户记忆静默消失
|
|
152
|
+
——滚动备份由改名改为**复制**(改名留有主文件不存在的窗口),主文件缺失时从
|
|
153
|
+
`.bak` 恢复并写回。
|
|
154
|
+
- 请求体上限写死 1,000,000,而满配存储(200 × 6000 字符)已达 1.2M——导出的
|
|
155
|
+
文件永远导不回来。上限改由条目上限推导。
|
|
156
|
+
- 超长导入条目被无提示截断——`truncatedFields()` 报告被截字段并披露。
|
|
157
|
+
- no-op 导入谎报 `saved:true`。
|
|
158
|
+
- `inject: ['systemPrompt', 'webServer']` 把 web 服务器变成整个插件的前置条件:
|
|
159
|
+
无 web server 的 profile(headless、tui)里 `apply()` 从不运行,设置页与提示词
|
|
160
|
+
注入**双双丢失**。路由改由非阻塞 `ctx.inject(['webServer'], …)` 子 fiber 注册。
|
|
161
|
+
- 设置页吞掉宿主 4xx/5xx 的可读错误消息。
|
|
162
|
+
|
|
163
|
+
### 新增
|
|
164
|
+
|
|
165
|
+
- **诊断日志**:挂载行(info)+ 逐操作明细(debug),走宿主 logger;纯日志,
|
|
166
|
+
渲染内容有无 logger 字节一致。
|
|
167
|
+
- **DNS 重绑定防护**:绑定回环时路由要求回环 Host 并校验 Origin 与 Host 一致
|
|
168
|
+
(content-type 规则依赖"浏览器把请求视为跨源"这一假设,DNS 重绑定摧毁该假设)。
|
|
169
|
+
|
|
170
|
+
## [1.0.9] — 2026-09-25
|
|
171
|
+
|
|
172
|
+
### 修复(适配 DSH 0.1.7)
|
|
173
|
+
|
|
174
|
+
- 兼容范围不认 0.1.7 预发布(与 1.0.8 同类假阴性,晚一个 minor):npm 的预发布
|
|
175
|
+
门是按集合、按 `[major, minor, patch]` 元组精确匹配的;补 0.1.7 段并把 0.1.6
|
|
176
|
+
段上界收紧到 `<0.1.7-0` 保持两段不相交。
|
|
177
|
+
- `dsh.client.inject` 还在声明 0.1.7 已移除的 `@deepseek-ai/dsh-client-runtime`
|
|
178
|
+
(它是死声明:模块图里不存在的名字被静默跳过)。改为真正提供本客户端所消费
|
|
179
|
+
能力的两个包:`dsh-client-ui-renderer`(`slots` 服务)与
|
|
180
|
+
`dsh-client-ui-settings`(`settings.section` 契约)。
|
|
181
|
+
- 提示词 section order 900 与保留槽 FILE_REFERENCE(900)平级——平级不抛错、
|
|
182
|
+
退化为代码单元名排序;移到 950(FILE_REFERENCE 900 与 TOOL_BASH 1000 之间)。
|
|
183
|
+
- `host-range-test.mjs` 此前是冻结矩阵,回归守卫只点名 0.1.6-alpha.2,真实宿主
|
|
184
|
+
前进了它依然全绿——新增覆盖 0.1.7 线的矩阵与**实时守卫**(读本机实际安装的
|
|
185
|
+
宿主版本直接断言)。
|
|
186
|
+
|
|
187
|
+
### 基础设施(1.0.8 之后、本版之前合入,未单独发版)
|
|
188
|
+
|
|
189
|
+
- 发布改为 OIDC Trusted Publishing(无令牌):npm 已于 2025-12-09 永久吊销全部
|
|
190
|
+
classic token,granular token 最长 90 天——旧令牌早已是死路。含 provenance
|
|
191
|
+
自动生成、npm ≥ 11.5.1 断言、版本与 tag 一致性守卫。
|
|
192
|
+
|
|
193
|
+
## [1.0.8] — 2026-09-24
|
|
194
|
+
|
|
195
|
+
### 修复
|
|
196
|
+
|
|
197
|
+
- **宿主范围对 0.1.6 预发布的假阴性**:声明的 `>=0.1.5-alpha.1` 读起来无上界,
|
|
198
|
+
实则永远匹配不了 `0.1.6-alpha.2`(预发布门要求比较器与版本共享完全相同的
|
|
199
|
+
`[major, minor, patch]` 元组)。此假阴性在实践中不可见,因为市场发现路径传了
|
|
200
|
+
`includePrerelease: true` 绕过了门——市场说兼容,npm/pnpm 解析说不兼容。
|
|
201
|
+
修复:逐预发布元组显式枚举,每段带上界(`<0.1.6-0` 是承重的:没有它上一段的
|
|
202
|
+
`>=` 链会放行 0.1.3-alpha.0/1)。两种求值模式对所有版本一致。
|
|
203
|
+
|
|
204
|
+
### 新增
|
|
205
|
+
|
|
206
|
+
- `host-range-test.mjs`(新套件):从 package.json 读范围而非复制;双模式断言、
|
|
207
|
+
已装宿主的具名回归守卫、模式一致性、覆盖线无缺口;接入 `npm test`。
|
|
208
|
+
- `settings.section` order 26 → 1200(官方区块止于 25,列表槽平级不抛错、只静默
|
|
209
|
+
把渲染顺序交给注册时机)。
|
|
210
|
+
- CI 增加 host-range job(semver 7.6.0 / 7.8.5 × 双 OS)。
|
|
211
|
+
|
|
212
|
+
## [1.0.7] — 2026-09-16
|
|
213
|
+
|
|
214
|
+
### 修复
|
|
215
|
+
|
|
216
|
+
- npm 页面的 README 截图裂图:截图改随 npm tarball 一起发布(`docs/screenshot.png`)。
|
|
217
|
+
|
|
218
|
+
## [1.0.6] — 2026-09-15
|
|
219
|
+
|
|
220
|
+
### 新增
|
|
221
|
+
|
|
222
|
+
- 滚动备份(`memory.json.bak`)、**导出 / 导入**(合并语义、按 id 与"标题+内容"
|
|
223
|
+
去重、只增不覆盖)、条目**搜索**与**展开/收起**、双语(中英)包元数据、CI。
|
|
224
|
+
|
|
225
|
+
## [1.0.5] — 2026-09-15
|
|
226
|
+
|
|
227
|
+
### 修复
|
|
228
|
+
|
|
229
|
+
- 存储与路由针对崩溃、竞态与静默数据丢失的加固(原子替换写盘、错误路径收口等)。
|
|
230
|
+
|
|
231
|
+
## [1.0.4] — 2026-09-13
|
|
232
|
+
|
|
233
|
+
### 文档 / 元数据(无代码变更)
|
|
234
|
+
|
|
235
|
+
- 市场从不读 `dsh.compatibility.dsh`:其发现索引从 `engines.dsh` /
|
|
236
|
+
`dsh.engines.dsh` / 宿主 peerDependencies 推导宿主要求,1.0.3 在市场上仍显示
|
|
237
|
+
"宿重要求未声明"。本版把同一已验证范围写入 `dsh.engines.dsh`(市场卡片可见、
|
|
238
|
+
安装前检查生效)。范围内宿主不受影响;范围外老版本成为确认不匹配。
|
|
239
|
+
|
|
240
|
+
## [1.0.3] — 2026-09-13
|
|
241
|
+
|
|
242
|
+
### 文档 / 元数据
|
|
243
|
+
|
|
244
|
+
- 按实际验证过的宿主版本声明兼容范围(`dsh.compatibility.dsh`):连续 5 个宿主
|
|
245
|
+
版本(0.1.3-alpha.2 → 0.1.5-rc.2)逐个**真实启动验证**(插件加载、组装后的
|
|
246
|
+
系统提示词、插件 HTTP 路由、客户端模块送达)。范围写成逐元组分支,因为
|
|
247
|
+
node-semver 只允许比较器共享完全相同 `[major, minor, patch]` 且自身带预发布
|
|
248
|
+
标签时预发布才满足范围。**故意不设上界**:未来宿主正式版不被"超上限"拒绝。
|
|
249
|
+
|
|
250
|
+
## [1.0.2] — 2026-09-12
|
|
251
|
+
|
|
252
|
+
### 文档
|
|
253
|
+
|
|
254
|
+
- README 补充安装说明。
|
|
255
|
+
|
|
256
|
+
## [1.0.1] — 2026-09-11
|
|
257
|
+
|
|
258
|
+
### 文档 / 元数据
|
|
259
|
+
|
|
260
|
+
- 补充 repository / homepage / bugs 元数据,链接 GitHub 仓库。
|
|
261
|
+
|
|
262
|
+
## 1.0.0 — 2026-09-11
|
|
263
|
+
|
|
264
|
+
- 首个发布版本(与 npm 上发布的 1.0.0 一致):DSH 指令记忆插件——长期指令
|
|
265
|
+
存储、系统提示词注入、设置页管理。
|
package/README.md
CHANGED
|
@@ -37,10 +37,12 @@ dsh plugin --profile web remove dsh-instruction-memory
|
|
|
37
37
|
| `lib/index.js` | Host 半:读取/保存记忆,注册 `systemPrompt` 段落,暴露同源 JSON 路由 |
|
|
38
38
|
| `lib/client.js` | Client 半:在 `settings.section` 注册「指令记忆」设置页(手写模块,无需构建) |
|
|
39
39
|
| `cordis.patch.yml` | bundle patch:把 Host 行插入宿主组合 |
|
|
40
|
+
| `install.mjs` | 开发安装脚本:从本仓库检出安装时,把包链接进 web profile 的 `node_modules`(junction)并注册 `dsh.profile.bundles`;`--remove` 完整回滚,改 profile 前自动备份 |
|
|
40
41
|
| `contract-test.mjs` | 契约测试:驱动真实路由,验证两半之间的响应格式 |
|
|
41
42
|
| `verify.mjs` | 装载验证:按 DSH 的方式加载两半 |
|
|
42
43
|
| `smoke-test.mjs` | 注入渲染逻辑的单元测试 |
|
|
43
44
|
| `host-range-test.mjs` | 宿主版本范围契约测试:固定矩阵 × 两种 semver 判定模式,并把 `peerDependencies`(**被执行的那个**)、`dsh.engines.dsh`、`dsh.compatibility.dsh` 三处声明锁到同一个生成值 |
|
|
45
|
+
| `lint.mjs` | 语法门禁:`node --check` 解析所有随包发布与本仓库运行的 JS 文件,已并入 `npm test` |
|
|
44
46
|
| `docs/demo.html` | 离线演示页:桩掉 DSH 外壳、加载真实 Client 半渲染设置页(不随 npm 包发布) |
|
|
45
47
|
|
|
46
48
|
## 生效方式
|
|
@@ -49,6 +51,9 @@ dsh plugin --profile web remove dsh-instruction-memory
|
|
|
49
51
|
- **按需** 条目:由模型按相关性自行判断是否适用,不相关时忽略;可填「适用场景」辅助判断。
|
|
50
52
|
- **优先级** 高 > 普通 > 低,决定注入顺序。
|
|
51
53
|
- **注入字数上限**:超出时按优先级保留,被略过的条目数会在注入文本末尾注明。
|
|
54
|
+
上限单位是**字符**而非 token——按 DeepSeek 官方分词比例约 1 个中文字符 ≈ 0.6 token、
|
|
55
|
+
1 个英文字符 ≈ 0.3 token(不同模型分词有差异,实际用量以模型返回为准),上限 40000
|
|
56
|
+
字符约合 24000 token。
|
|
52
57
|
|
|
53
58
|
条目全部停用或总开关关闭时,该提示词段落**整个注销**,系统提示词与未安装本插件时逐字节一致。
|
|
54
59
|
|
|
@@ -67,7 +72,8 @@ DSH 就会得到第二份空记忆,看起来像"记忆消失"。
|
|
|
67
72
|
> 1.0.0 之前的构建曾把数据写在宿主进程 cwd 下(`.dsh-instruction-memory.json`)。
|
|
68
73
|
> 首次启动新版本时会自动迁移到上述位置,并把原文件改名为 `*.migrated` 保留。
|
|
69
74
|
|
|
70
|
-
|
|
75
|
+
该文件可手工编辑,保存后已打开的设置页会**自动刷新**(见下文「外部改动自动刷新」);
|
|
76
|
+
流式更新不可用时「重新载入」按钮仍可手动读回。每次保存先写临时文件再原子替换,
|
|
71
77
|
进程崩溃不会留下损坏的半截 `memory.json`;保存前把上一版**复制**为 `memory.json.bak`,
|
|
72
78
|
若 `memory.json` 解析失败会自动临时回退到该备份并明确提示。
|
|
73
79
|
|
|
@@ -78,10 +84,51 @@ DSH 就会得到第二份空记忆,看起来像"记忆消失"。
|
|
|
78
84
|
> 前始终待在原位;同时启动时若发现 `memory.json` 缺失而 `.bak` 可用,会**从备份恢复并写回**
|
|
79
85
|
> 主路径,而不是从零开始。
|
|
80
86
|
|
|
87
|
+
### 多实例与多窗口(并发保护)
|
|
88
|
+
|
|
89
|
+
两个 DSH 进程共享同一 `$DSH_HOME`(如 `web` 与 `tui` profile 同时运行),或两个设置页
|
|
90
|
+
窗口同时打开时,数据可能被并发修改。两层保护:
|
|
91
|
+
|
|
92
|
+
- **跨进程写锁**:每次写盘前创建 `memory.json.lock`(独占创建),写完即删。锁存在超过
|
|
93
|
+
10 秒视为持锁进程已崩溃、直接接管;等待 2 秒仍拿不到锁**不会导致保存失败**——宁可继续
|
|
94
|
+
写入(last-write-wins)也不让用户的保存被卡死。原子替换保证任何交错下文件本身不会损坏。
|
|
95
|
+
- **修订号(`rev`)与冲突拒绝**:每次成功写入使 `data.rev` 加一并落盘。客户端的每次修改
|
|
96
|
+
请求都带上它看到的 `rev`(`baseRev`);若与服务端当前 `rev` 不一致(另一个窗口/进程刚
|
|
97
|
+
提交过),服务端拒绝本次修改并返回**最新快照**,面板自动刷新并提示重试——防止旧窗口
|
|
98
|
+
把别的窗口刚删除的条目"复活"回去。不带 `baseRev` 的旧客户端保持原有行为。
|
|
99
|
+
|
|
100
|
+
### 外部改动自动刷新(SSE)
|
|
101
|
+
|
|
102
|
+
设置页打开期间通过 `EventSource` 订阅同一路由上的事件流(GET +
|
|
103
|
+
`Accept: text/event-stream`),存储一旦在面板之外发生变化,已打开的面板**自动更新**,
|
|
104
|
+
无需手动「重新载入」:
|
|
105
|
+
|
|
106
|
+
- **触发来源**:其他窗口经同一宿主保存(写盘成功即向所有订阅者推送快照);`memory.json`
|
|
107
|
+
被外部进程或手工改写(宿主以 `fs.watch` 监听**存储目录**——原子替换在 Windows 上会
|
|
108
|
+
使文件级监听失效——去抖 250ms 后经与写盘相同的队列重读,内容确有变化才重载并推送;
|
|
109
|
+
自己的写入盘后磁盘与内存一致,天然被同一比较抑制,不会回声)。
|
|
110
|
+
- **回声抑制**:发起修改的窗口也会收到自己那次写入的推送帧,但帧内容与它已吸收的响应
|
|
111
|
+
快照逐字节相同,客户端按内容比对直接丢弃——否则刚出现的「已保存」提示会被空消息的
|
|
112
|
+
推送帧瞬间冲掉。
|
|
113
|
+
- **降级而非报错**:宿主不支持或连接中断时 `EventSource` 按 `retry: 5000` 自动重连,
|
|
114
|
+
重连失败期间面板照常可用(「重新载入」仍是兜底),不会弹出错误。
|
|
115
|
+
- 编辑器中未保存的草稿**不受推送影响**;保存时按当前 `rev` 提交,外部若已抢先修改
|
|
116
|
+
会被冲突拒绝拦下。
|
|
117
|
+
|
|
118
|
+
响应信封中的 `saved` 字段语义为「**本次操作是否把存储写入了磁盘**」,只反映真实写入:
|
|
119
|
+
普通读取(`state`/`reload`/`export-data`)一律 `false`;首次运行时 `state` 物化文件、
|
|
120
|
+
或 `reload` 触发从 `.bak` 恢复/旧位置迁移这类"载入即写回"的分支才为 `true`。早先版本
|
|
121
|
+
对读取操作也回 `true`,使该字段不携带任何信息。
|
|
122
|
+
|
|
81
123
|
若手工编辑后的文件有条目为空或超过 200 条上限,设置页会明确提示哪些条目未能载入
|
|
82
|
-
|
|
124
|
+
(它们将在下次保存时从文件中移除),不会静默丢弃。手改的**选项值**同样有披露:`enabled`
|
|
125
|
+
写成非布尔值、或 `budgetChars` 超出 800–40000 范围/不是数字时,载入会按规则修正
|
|
126
|
+
(回退到 `true` / 夹到边界),并在设置页明确提示**原值 → 修正值**——否则下次保存会把
|
|
127
|
+
修正值悄悄写回文件,你以为改过的设置其实从未生效。导入时若某条超出长度上限
|
|
83
128
|
(标题 120 / 内容 6000 / 适用场景 200 字符),会明确提示**有几条、哪个字段**被截断,
|
|
84
|
-
|
|
129
|
+
而不是悄悄改短。文件若声明了**未来的格式版本**(`version` 非 1),插件会拒绝载入并
|
|
130
|
+
**暂停一切修改**(此时保存会用 v1 格式覆盖文件,可能损坏数据),直到你升级插件或把
|
|
131
|
+
文件改回版本 1 后点「重新载入」。
|
|
85
132
|
|
|
86
133
|
设置页支持**导出 / 导入**:导出为自带说明的 JSON 文件;导入采用**合并语义**——按 id 与
|
|
87
134
|
「标题+内容」去重,只新增、绝不覆盖或清空现有条目。请求体上限由条目上限推导
|
|
@@ -164,11 +211,39 @@ DNS 重绑定恰好推翻了它**:`evil.com` 解析到 `127.0.0.1` 之后,
|
|
|
164
211
|
[instruction-memory] route rejected a request: Host 头不是回环地址(可能是 DNS 重绑定攻击):evil.com:8080
|
|
165
212
|
```
|
|
166
213
|
|
|
167
|
-
-
|
|
168
|
-
Host,强行限制只会打断所有正常客户端;`Origin` 校验仍然生效。
|
|
214
|
+
- 绑定到**任何非回环地址**(`--host 0.0.0.0`、`::` 或具体 IP,即主动对外发布)时 Host 校验**自动关闭**——那种情况下无法预知合法 Host,强行限制只会打断所有正常客户端;`Origin` 校验仍然生效。
|
|
169
215
|
- 无 `Host` 的 HTTP/1.1 请求由 Node 的 HTTP 解析器先以 400 拒掉,根本到不了路由。
|
|
170
216
|
- 端口不参与 `Origin` 比对(反向代理可能改写端口),主机名才是重绑定攻击的对象。
|
|
171
217
|
|
|
218
|
+
此外路由还读取 **`Sec-Fetch-Site`**(Fetch Metadata):现代浏览器给每个请求都打上它,
|
|
219
|
+
即使某些技巧(如 `no-cors` fetch)不带 `Origin`,`evil.com` 发来的请求仍会被标记为
|
|
220
|
+
`cross-site`——命中即 **403**。仅拒绝 `cross-site`;`same-site` 必须放行(同站判定不看端口,
|
|
221
|
+
GUI 与路由本就同主机不同端口),`none`(用户直接导航)放行;curl、Node 与 2020 年前的
|
|
222
|
+
老浏览器根本不发这个头,也不受影响(它们由 Host 与 content-type 校验兜底)。
|
|
223
|
+
|
|
224
|
+
### 经反向代理对外发布(自行承担风险)
|
|
225
|
+
|
|
226
|
+
默认部署(回环绑定 + 本机浏览器)不需要这一节。若你把 DSH 的 Web 服务器绑定到非回环地址
|
|
227
|
+
(`--host 0.0.0.0` 或具体 IP)并置于 nginx / Caddy 等反向代理之后远程访问,需要注意:
|
|
228
|
+
|
|
229
|
+
**先说清楚代价**:该路由**没有任何鉴权**。一旦发布到网络上,任何能访问它的人都能增删改你
|
|
230
|
+
那份"注入到此后每一轮对话"的长期指令。Host 校验在非回环绑定时自动关闭,`Origin` 与
|
|
231
|
+
`Sec-Fetch-Site` 只能拦"来自其他网页的请求",拦不了直接发 HTTP 的攻击者。**更安全的替代
|
|
232
|
+
是隧道方案**(SSH 端口转发 / Tailscale / WireGuard):宿主仍绑定 `127.0.0.1`,远程客户端
|
|
233
|
+
通过隧道以回环身份访问,所有校验原样生效。
|
|
234
|
+
|
|
235
|
+
仍要走反向代理时,代理必须满足:
|
|
236
|
+
|
|
237
|
+
1. **原样转发 `Host`**(nginx:`proxy_set_header Host $host;`)。GUI 页面与 API 走同一个
|
|
238
|
+
域名时,浏览器发的 `Origin` 与转发的 `Host` 主机名一致,`Origin` 校验通过;改写或剥掉
|
|
239
|
+
`Host` 会得到 403。
|
|
240
|
+
2. **不要注入 `Origin`**:代理自己添加的 `Origin` 头若与 `Host` 不同源,会被当作跨源拒绝。
|
|
241
|
+
3. **透传 `Sec-Fetch-Site`**:代理不应剥掉 Fetch Metadata 头;正常同站访问浏览器发的是
|
|
242
|
+
`same-origin`,不受影响。
|
|
243
|
+
4. **透传 `content-type` 与请求体**:路由只接受 `application/json` 的 POST,
|
|
244
|
+
路径前缀为 `/instruction-memory/api`,代理转发规则需覆盖它。
|
|
245
|
+
5. **限制来源**:至少用代理层做 IP 白名单或加 Basic Auth——插件本身不提供任何鉴权。
|
|
246
|
+
|
|
172
247
|
## 兼容性
|
|
173
248
|
|
|
174
249
|
同一串范围写在**三处**,而**只有一处会被宿主强制执行**——这一点以前写错过,现在按实测更正:
|
|
@@ -337,6 +412,7 @@ Package → Settings → Trusted publishing → Add trusted publisher
|
|
|
337
412
|
```json
|
|
338
413
|
{
|
|
339
414
|
"version": 1,
|
|
415
|
+
"rev": 3,
|
|
340
416
|
"enabled": true,
|
|
341
417
|
"budgetChars": 4000,
|
|
342
418
|
"entries": [
|
|
@@ -354,4 +430,5 @@ Package → Settings → Trusted publishing → Add trusted publisher
|
|
|
354
430
|
}
|
|
355
431
|
```
|
|
356
432
|
|
|
357
|
-
`mode` 为 `always` | `auto`;`priority` 为 `2` 高 / `1` 普通 / `0`
|
|
433
|
+
`mode` 为 `always` | `auto`;`priority` 为 `2` 高 / `1` 普通 / `0` 低。`rev` 是存储修订号,
|
|
434
|
+
由插件在每次成功写入时递增,**无需手填**(缺失视为 0)。
|