dsh-more 0.0.1
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/CONTRIBUTING.md +349 -0
- package/LICENSE +21 -0
- package/README.md +232 -0
- package/README.zh-CN.md +232 -0
- package/cordis.patch.yml +4 -0
- package/dist/client.js +954 -0
- package/dist/index.js +937 -0
- package/dist/types/client/index.d.ts +3 -0
- package/dist/types/generated/client-registry.d.ts +3 -0
- package/dist/types/generated/host-registry.d.ts +2 -0
- package/dist/types/generated/host-settings-schema.d.ts +10 -0
- package/dist/types/generated/patch-catalog.d.ts +30 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/kernel/client/activation.d.ts +19 -0
- package/dist/types/kernel/client/config-card.d.ts +11 -0
- package/dist/types/kernel/client/message-actions.d.ts +14 -0
- package/dist/types/kernel/client/patch.d.ts +16 -0
- package/dist/types/kernel/host/activation.d.ts +14 -0
- package/dist/types/kernel/host/patch.d.ts +12 -0
- package/dist/types/kernel/host/settings.d.ts +5 -0
- package/dist/types/kernel/message-visibility.d.ts +6 -0
- package/dist/types/patches/message-delete/client/index.d.ts +5 -0
- package/dist/types/patches/message-delete/host/index.d.ts +2 -0
- package/dist/types/patches/message-delete/host/message-selection.d.ts +7 -0
- package/dist/types/patches/message-delete/host/projection.d.ts +8 -0
- package/dist/types/patches/message-delete/host/rebuild.d.ts +9 -0
- package/dist/types/patches/message-delete/shared.d.ts +6 -0
- package/dist/types/patches/message-edit/client/index.d.ts +5 -0
- package/dist/types/patches/message-edit/host/edit-continuation.d.ts +15 -0
- package/dist/types/patches/message-edit/host/index.d.ts +2 -0
- package/dist/types/patches/message-edit/shared.d.ts +7 -0
- package/dist/types/patches/session-delete/client/index.d.ts +4 -0
- package/dist/types/patches/session-delete/host/index.d.ts +2 -0
- package/dist/types/patches/session-delete/host/live-session-handles.d.ts +7 -0
- package/dist/types/patches/session-delete/host/session-deletion.d.ts +6 -0
- package/dist/types/patches/session-delete/shared.d.ts +1 -0
- package/dist/types/platform/dsh/client/api.d.ts +6 -0
- package/dist/types/platform/dsh/client/message-content.d.ts +2 -0
- package/dist/types/platform/dsh/client/message-targets.d.ts +2 -0
- package/dist/types/platform/dsh/client/runtime.d.ts +7 -0
- package/dist/types/platform/dsh/client/session-sync.d.ts +18 -0
- package/dist/types/platform/dsh/client/styles.d.ts +1 -0
- package/dist/types/platform/dsh/host/agent-session.d.ts +6 -0
- package/dist/types/platform/dsh/host/api-router.d.ts +3 -0
- package/dist/types/platform/dsh/host/confirmation.d.ts +14 -0
- package/dist/types/platform/dsh/host/error.d.ts +6 -0
- package/dist/types/platform/dsh/host/runtime-context.d.ts +4 -0
- package/dist/types/platform/dsh/host/session-history.d.ts +8 -0
- package/dist/types/platform/dsh/host/trust.d.ts +3 -0
- package/dist/types/platform/dsh/host/wire.d.ts +6 -0
- package/dist/types/platform/dsh/identity.d.ts +4 -0
- package/docs/assets/dsh-more-architecture.jpg +0 -0
- package/docs/assets/dsh-more-architecture.zh-CN.jpg +0 -0
- package/docs/assets/dsh-more-safe-mutation-flow.jpg +0 -0
- package/docs/assets/dsh-more-safe-mutation-flow.zh-CN.jpg +0 -0
- package/dsh.plugin.json +12 -0
- package/package.json +140 -0
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# 为 DSH More 贡献代码
|
|
2
|
+
|
|
3
|
+
感谢你为 DSH More 补充功能。这个仓库的目标不是维护一个不断膨胀的单体插件,而是维护一组可以独立注册、启停、测试和移除的 DSH 补丁。
|
|
4
|
+
|
|
5
|
+
最重要的规则是:**普通功能补丁应当只新增或修改 `src/patches/<patch-id>/`。**
|
|
6
|
+
|
|
7
|
+
如果一个功能使用现有依赖,并且能够归入现有的 `message-action` 或 `standalone` Client 类型,通常不需要修改根入口、设置页面、注册表、构建脚本、`kernel/` 或 `platform/`。
|
|
8
|
+
|
|
9
|
+
## 开始之前
|
|
10
|
+
|
|
11
|
+
开发环境要求:
|
|
12
|
+
|
|
13
|
+
- Node.js `>= 22.19`;
|
|
14
|
+
- pnpm;
|
|
15
|
+
- 与项目当前依赖匹配的 DSH。本版本面向 DSH `0.1.0-rc.7`。
|
|
16
|
+
|
|
17
|
+
首次检出后执行:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
pnpm install
|
|
21
|
+
pnpm run check
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
开始编码前,请先确认:
|
|
25
|
+
|
|
26
|
+
1. 需求是新增补丁,还是扩展已有补丁;
|
|
27
|
+
2. 是否能使用现有 Host/Client 契约完成;
|
|
28
|
+
3. 是否涉及不可恢复的数据修改、持久化格式或已有会话;
|
|
29
|
+
4. 是否真的需要新增依赖或修改共享层。
|
|
30
|
+
|
|
31
|
+
## 仓库边界
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
src/
|
|
35
|
+
├── patches/ # 具体功能;普通贡献的默认工作区
|
|
36
|
+
├── kernel/ # 与 DSH 无关的补丁契约、激活和注册机制
|
|
37
|
+
├── platform/dsh/ # DSH Host/Client 适配与安全边界
|
|
38
|
+
└── generated/ # 构建时生成,不提交
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
各目录的责任:
|
|
42
|
+
|
|
43
|
+
- `src/patches/<patch-id>/`:一个补丁的清单、Host、Client、共享类型和测试;
|
|
44
|
+
- `src/kernel/`:所有补丁共同遵守的抽象,不放具体业务功能;
|
|
45
|
+
- `src/platform/dsh/`:对 DSH API、Context、DOM、请求和信任边界的集中适配;
|
|
46
|
+
- `scripts/`、`build.mjs`:注册表生成和发布构建;
|
|
47
|
+
- `test/`:跨补丁的 kernel/platform 契约测试;具体补丁测试不放这里;
|
|
48
|
+
- `src/generated/`、`dist/`:自动生成,不编辑、不提交。
|
|
49
|
+
|
|
50
|
+
只有以下情况才应修改补丁目录之外的代码:
|
|
51
|
+
|
|
52
|
+
- 新功能无法归入现有两种 Client 类型;
|
|
53
|
+
- 至少两个补丁需要同一个新的稳定公共能力;
|
|
54
|
+
- DSH API 变化,需要更新集中适配层;
|
|
55
|
+
- 新增依赖、构建能力或发布配置;
|
|
56
|
+
- 修复 kernel/platform 本身的缺陷。
|
|
57
|
+
|
|
58
|
+
这类修改需要在变更说明中解释为什么不能留在单个补丁目录,并补充共享层测试。
|
|
59
|
+
|
|
60
|
+
## 新增一个补丁
|
|
61
|
+
|
|
62
|
+
推荐结构:
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
src/patches/example-feature/
|
|
66
|
+
├── patch.json
|
|
67
|
+
├── shared.ts
|
|
68
|
+
├── host/
|
|
69
|
+
│ ├── index.ts
|
|
70
|
+
│ └── feature.ts
|
|
71
|
+
├── client/
|
|
72
|
+
│ └── index.tsx
|
|
73
|
+
└── example-feature.test.ts
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 1. 编写 `patch.json`
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"id": "example-feature",
|
|
81
|
+
"name": "面向用户的中文名称",
|
|
82
|
+
"description": "一句话说明开启后获得什么能力",
|
|
83
|
+
"defaultEnabled": true,
|
|
84
|
+
"order": 400,
|
|
85
|
+
"host": "./host/index.ts",
|
|
86
|
+
"client": {
|
|
87
|
+
"kind": "message-action",
|
|
88
|
+
"entry": "./client/index.tsx"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
清单规则:
|
|
94
|
+
|
|
95
|
+
- `id` 必须与目录名完全一致,并匹配 `[a-z][a-z0-9-]*`;
|
|
96
|
+
- `name` 和 `description` 会直接显示在设置卡片中,应使用清晰的中文产品语言;
|
|
97
|
+
- `order` 必须是仓库内唯一的安全整数,用于稳定排序;
|
|
98
|
+
- `host` 和 `client.entry` 必须指向真实文件;
|
|
99
|
+
- `client.kind` 当前只能是 `message-action` 或 `standalone`;
|
|
100
|
+
- 不要手工修改注册表或设置 schema,生成器会从这里派生它们。
|
|
101
|
+
|
|
102
|
+
可以执行下面的命令提前检查清单:
|
|
103
|
+
|
|
104
|
+
```sh
|
|
105
|
+
pnpm run generate
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 2. 定义共享标识和线协议
|
|
109
|
+
|
|
110
|
+
将 Host 与 Client 都需要的稳定内容放在 `shared.ts`,例如补丁 ID、预览结果和请求响应类型。不要把 DSH Context、DOM 节点或 Host 专属实现放进共享文件。
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
export const EXAMPLE_FEATURE_PATCH_ID = 'example-feature'
|
|
114
|
+
|
|
115
|
+
export interface ExamplePreview {
|
|
116
|
+
confirmToken: string
|
|
117
|
+
summary: string
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
跨 Host/Client 传输的数据应当是可 JSON 序列化的普通数据。
|
|
122
|
+
|
|
123
|
+
### 3. 实现 Host 入口
|
|
124
|
+
|
|
125
|
+
`host/index.ts` 必须导出名为 `hostPatch` 的 `HostPatch`:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import type { HostPatch } from '../../../kernel/host/patch.js'
|
|
129
|
+
import { EXAMPLE_FEATURE_PATCH_ID } from '../shared.js'
|
|
130
|
+
|
|
131
|
+
export const hostPatch: HostPatch = {
|
|
132
|
+
id: EXAMPLE_FEATURE_PATCH_ID,
|
|
133
|
+
setup: (ctx) => {
|
|
134
|
+
// 可选:安装只属于这个补丁的监听器或运行时包装。
|
|
135
|
+
return () => {
|
|
136
|
+
// 必须完整撤销 setup 创建的副作用。
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
routes: ({ ctx, confirmationSecret }) => ({
|
|
140
|
+
preview: async (payload) => {
|
|
141
|
+
// payload 是 unknown;先验证,再使用。
|
|
142
|
+
return { ok: true }
|
|
143
|
+
},
|
|
144
|
+
commit: async (payload) => {
|
|
145
|
+
return { ok: true }
|
|
146
|
+
},
|
|
147
|
+
}),
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Host 规则:
|
|
152
|
+
|
|
153
|
+
- 不要在模块顶层安装监听器或修改 DSH 对象;使用 `setup()`;
|
|
154
|
+
- `setup()` 必须返回 disposer,关闭补丁或卸载插件时应恢复原状;
|
|
155
|
+
- 当前注册表契约要求每个补丁至少提供一个 API action;
|
|
156
|
+
- 所有 payload 都按不可信的 `unknown` 处理,优先复用 `platform/dsh/host/` 中的校验和错误工具;
|
|
157
|
+
- action 名称和返回结构一旦发布就是线协议,修改时要考虑已有 Client;
|
|
158
|
+
- 具体业务逻辑放在补丁自己的 Host 文件,不要塞进共享 API router。
|
|
159
|
+
|
|
160
|
+
### 4. 选择 Client 类型
|
|
161
|
+
|
|
162
|
+
#### `message-action`
|
|
163
|
+
|
|
164
|
+
适用于在用户或助手消息旁增加操作。Client 入口必须导出 `useMessageActions`,返回按钮渲染器和可选 overlay。
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
export function useMessageActions(
|
|
168
|
+
props: ConversationHeaderProps & { ctx: ClientContext },
|
|
169
|
+
enabled: boolean,
|
|
170
|
+
): MessageActions {
|
|
171
|
+
// Hook 每次渲染都会被调用,包括 enabled === false。
|
|
172
|
+
// 所有 effect 都必须检查 enabled,并在 cleanup 中撤销。
|
|
173
|
+
return {
|
|
174
|
+
renderAction: (target) => enabled ? <button>操作</button> : null,
|
|
175
|
+
overlay: null,
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
不要自行扫描并重复创建消息操作容器;复用共享的 `MessageTarget` 和 action row。
|
|
181
|
+
|
|
182
|
+
#### `standalone`
|
|
183
|
+
|
|
184
|
+
适用于独立 slot、全局 overlay、侧栏菜单增强或 DOM observer。Client 入口必须导出 `clientPatch`:
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
export const clientPatch: ClientPatch = {
|
|
188
|
+
id: EXAMPLE_FEATURE_PATCH_ID,
|
|
189
|
+
install: (ctx, activation) => {
|
|
190
|
+
// 注册 slot 或挂载控制器;组件内订阅 activation。
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Standalone 规则:
|
|
196
|
+
|
|
197
|
+
- UI 必须响应补丁开关,关闭后移除按钮、observer 和未完成的局部状态;
|
|
198
|
+
- DOM 增强必须可重复安装,并能删除自己创建的节点和属性;
|
|
199
|
+
- 不要用新增行为替代 DSH 原生行为。例如永久删除是独立菜单项,不能覆盖原生归档;
|
|
200
|
+
- 调用 Host API 时复用 `callPatchApi()`,不要绕过统一路径和请求头。
|
|
201
|
+
|
|
202
|
+
如果功能不属于这两类,不要把它伪装成最接近的一类。先说明新的挂载需求,再扩展生成器和 kernel 契约。
|
|
203
|
+
|
|
204
|
+
## 编码约定
|
|
205
|
+
|
|
206
|
+
- 使用严格 TypeScript;项目开启了 `strict`、`noUncheckedIndexedAccess`、`exactOptionalPropertyTypes` 和未使用代码检查;
|
|
207
|
+
- 使用 ESM,并在相对 TypeScript import 中保留 `.js` 后缀;
|
|
208
|
+
- 类型导入使用 `import type`;
|
|
209
|
+
- 遵循现有代码风格:两个空格缩进、单引号、无分号;
|
|
210
|
+
- 用户可见文案使用清晰中文,不暴露内部模块名、路径、props 或工程术语;
|
|
211
|
+
- 补丁 ID、投影 key、持久化 metadata 和 API action 应使用稳定名称;
|
|
212
|
+
- 不要通过 `any`、双重断言或未验证的内部字段绕开边界。确实需要兼容 DSH 暂未公开的接口时,将兼容访问集中在 adapter 中并写失败测试;
|
|
213
|
+
- 不要顺手重构无关模块,让每次变更都能明确归属于一个补丁或一个共享缺陷。
|
|
214
|
+
|
|
215
|
+
## 数据、安全和兼容性
|
|
216
|
+
|
|
217
|
+
凡是会修改会话、历史或磁盘的功能,都要满足以下最低要求:
|
|
218
|
+
|
|
219
|
+
1. Client 只调用统一插件 API,不直连本地文件或 Host 内部对象;
|
|
220
|
+
2. Host 路由继续经过 loopback/trusted-host、same-origin 和自定义请求头检查;
|
|
221
|
+
3. 预览与提交分离,提交时重新校验会话 revision、目标和确认令牌;
|
|
222
|
+
4. 忙碌会话应拒绝操作,或先完成明确的 cancel、idle、flush、unload 流程;
|
|
223
|
+
5. 删除路径必须从 DSH 返回的逐对象定位信息推导,并验证仍位于受控根目录;
|
|
224
|
+
6. 禁止对宽泛目录、未解析变量或 glob 执行递归删除;
|
|
225
|
+
7. 错误时保持原数据,不要用“尽量成功”的部分写入掩盖失败。
|
|
226
|
+
|
|
227
|
+
以下内容属于持久化协议,不应在普通重构中随意改名:
|
|
228
|
+
|
|
229
|
+
- Session event 中的 `source.plugin`、`operation` 和 replay metadata;
|
|
230
|
+
- projection key 与 state version;
|
|
231
|
+
- Settings namespace 和字段;
|
|
232
|
+
- 已发布的 API action、请求和响应结构。
|
|
233
|
+
|
|
234
|
+
确实要做破坏性变更时,应明确声明不兼容范围,并增加迁移或拒绝旧数据的测试。
|
|
235
|
+
|
|
236
|
+
## 测试放在哪里
|
|
237
|
+
|
|
238
|
+
补丁测试与代码放在一起:
|
|
239
|
+
|
|
240
|
+
```text
|
|
241
|
+
src/patches/example-feature/example-feature.test.ts
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
至少覆盖:
|
|
245
|
+
|
|
246
|
+
- 核心选择、转换或状态计算;
|
|
247
|
+
- 正常路径和关键拒绝路径;
|
|
248
|
+
- `setup()` 安装、关闭和重复启用时的清理;
|
|
249
|
+
- payload 非法、状态过期或目标变化;
|
|
250
|
+
- 对磁盘操作使用 `mkdtemp()` 隔离,并在 `afterEach` 中清理精确临时目录;
|
|
251
|
+
- 修复 bug 时增加一个修复前会失败的回归用例。
|
|
252
|
+
|
|
253
|
+
只有修改 kernel/platform 时,才把测试放到 `test/kernel/` 或 `test/platform/`。共享测试应验证通用不变量,不能写死“仓库永远只有当前三个补丁”,否则新增补丁会被迫修改补丁目录之外的文件。
|
|
254
|
+
|
|
255
|
+
## 生成、检查和打包
|
|
256
|
+
|
|
257
|
+
日常开发可按需要执行:
|
|
258
|
+
|
|
259
|
+
```sh
|
|
260
|
+
pnpm run generate # 校验 patch.json 并重建注册表
|
|
261
|
+
pnpm run typecheck
|
|
262
|
+
pnpm test
|
|
263
|
+
pnpm run build
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
提交前必须执行:
|
|
267
|
+
|
|
268
|
+
```sh
|
|
269
|
+
pnpm run check
|
|
270
|
+
pnpm run package:check
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
说明:
|
|
274
|
+
|
|
275
|
+
- `build`、`typecheck` 和 `test` 都会自动运行生成器;
|
|
276
|
+
- `src/generated/`、`dist/` 和 `*.tgz` 已被忽略,不要强制加入 Git;
|
|
277
|
+
- 补丁目录里的 `*.test.ts(x)` 会参与类型检查和测试,但不会进入声明产物;
|
|
278
|
+
- `package:check` 会显示最终 npm 包内容,应确认没有源码测试、临时文件或本机路径。
|
|
279
|
+
|
|
280
|
+
## 发布 npm
|
|
281
|
+
|
|
282
|
+
发布前需要满足以下条件:
|
|
283
|
+
|
|
284
|
+
- 当前分支已经设置 upstream,并与远端完全同步;
|
|
285
|
+
- 工作区没有未提交文件;
|
|
286
|
+
- 已执行 `npm login --registry=https://registry.npmjs.org/`,且当前 npm 用户有权发布 `dsh-more`;
|
|
287
|
+
- Git 远端允许推送当前分支和 `v<version>` tag。
|
|
288
|
+
|
|
289
|
+
先执行不产生任何版本、tag 或发布记录的预检:
|
|
290
|
+
|
|
291
|
+
```sh
|
|
292
|
+
pnpm run release:dry-run
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
正式发布使用以下任一命令:
|
|
296
|
+
|
|
297
|
+
```sh
|
|
298
|
+
pnpm release # 首次发布保留当前版本;以后默认自动升级 patch
|
|
299
|
+
pnpm release:patch # 0.0.1 -> 0.0.2
|
|
300
|
+
pnpm release:minor # 0.0.1 -> 0.1.0
|
|
301
|
+
pnpm release:major # 0.0.1 -> 1.0.0
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
脚本会依次检查 Git/npm 状态,运行完整项目检查,自动修改 `package.json` 版本并创建 `chore(release): v<version>` 提交和同名 tag,发布到公共 npm registry,最后以一次原子 push 同时推送版本提交和 tag。首次发布时,若 npm 上还不存在这个包,`pnpm release` 会直接发布 `package.json` 中的当前版本并为当前提交创建 tag。
|
|
305
|
+
|
|
306
|
+
如果 npm 账户要求一次性验证码,可以使用:
|
|
307
|
+
|
|
308
|
+
```sh
|
|
309
|
+
pnpm run release -- --otp=123456
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
发布或推送被网络临时中断时,不要手动再次修改版本号。修复登录或网络问题后重新运行 `pnpm release`,脚本会识别本地 release tag、npm registry 和远端 tag 的状态,继续未完成的发布或推送,避免重复升级版本。
|
|
313
|
+
|
|
314
|
+
## 新增依赖
|
|
315
|
+
|
|
316
|
+
优先使用现有依赖和平台适配层。确实需要新增时:
|
|
317
|
+
|
|
318
|
+
- 只在构建或测试中使用的包放入 `devDependencies`;
|
|
319
|
+
- 运行时由 DSH 提供的外部包放入 `peerDependencies`,并在 `devDependencies` 固定本地开发版本;
|
|
320
|
+
- Client bundle 真正产生外部 `require()` 时,将对应模块加入 `dsh.client.inject`;
|
|
321
|
+
- 只用于 TypeScript module augmentation、且不会出现在发布声明或运行时 bundle 中的包,可以仅作为开发依赖;
|
|
322
|
+
- 同步 `pnpm-lock.yaml` 和 `package-lock.json`;
|
|
323
|
+
- 不要为了一个很小的 helper 引入大型依赖。
|
|
324
|
+
|
|
325
|
+
更新锁文件后再次运行完整检查,并在变更说明中写明依赖的用途。
|
|
326
|
+
|
|
327
|
+
## 提交前检查清单
|
|
328
|
+
|
|
329
|
+
- [ ] 普通功能改动只位于一个 `src/patches/<patch-id>/`;或已解释共享层修改原因;
|
|
330
|
+
- [ ] `patch.json` 的 ID、目录名和入口一致,order 唯一;
|
|
331
|
+
- [ ] Host 副作用可以在关闭补丁时完整清理;
|
|
332
|
+
- [ ] Client 关闭后不会残留按钮、DOM 属性、observer 或弹窗状态;
|
|
333
|
+
- [ ] 输入按不可信数据验证,破坏性操作有预览、确认和状态复核;
|
|
334
|
+
- [ ] 用户可见行为没有覆盖或偷偷改变 DSH 原生功能;
|
|
335
|
+
- [ ] 新测试放在补丁目录,并覆盖失败路径;
|
|
336
|
+
- [ ] 没有提交 `src/generated/`、`dist/`、tarball、日志或本机文件;
|
|
337
|
+
- [ ] `pnpm run check` 通过;
|
|
338
|
+
- [ ] `pnpm run package:check` 的文件清单符合预期。
|
|
339
|
+
|
|
340
|
+
## 变更说明建议
|
|
341
|
+
|
|
342
|
+
提交补丁或 Pull Request 时,至少说明:
|
|
343
|
+
|
|
344
|
+
1. 用户遇到的问题和期望行为;
|
|
345
|
+
2. 改动属于哪个 patch ID;
|
|
346
|
+
3. 是否修改持久化数据、原生 DSH 行为或公共契约;
|
|
347
|
+
4. 如何验证,包括执行的命令;
|
|
348
|
+
5. 有视觉变化时附上展开、关闭和错误状态截图;
|
|
349
|
+
6. 尚未在真实 DSH 环境确认的部分要明确标为待验证,不要用单元测试替代真实交互结论。
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
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
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# DSH More
|
|
2
|
+
|
|
3
|
+
[English](./README.md) | [简体中文](./README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
Practical, independently switchable patches for missing context and history controls in DeepSeek Harness Web.
|
|
6
|
+
|
|
7
|
+
`dsh-more` integrates with the existing DSH interface instead of adding a separate management dashboard. Its current patch set can edit a user message and restart from that point, remove one message while preserving the surrounding context, and permanently delete a session while leaving native archive behavior intact.
|
|
8
|
+
|
|
9
|
+
> **Compatibility:** package version `0.0.1` targets DeepSeek Harness `0.1.0-rc.7` and requires Node.js `>= 22.19`. DSH is still a release candidate, so upgrades may require changes in the centralized adapter layer described below.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
| Patch | Where it appears | Behavior | Default |
|
|
14
|
+
| --- | --- | --- | --- |
|
|
15
|
+
| Edit and restart | User-message action row | Creates a clean continuation before the selected turn, submits the edited text, archives the source branch, and opens the child session | On |
|
|
16
|
+
| Delete one message | User- and assistant-message action rows | Removes the selected model-surface node, expands only when tool call/result pairing requires it, rebuilds the surviving context, and opens a child session | On |
|
|
17
|
+
| Permanently delete session | Separate item beside the native Archive action | Stops and unloads the target session, removes its exact JSONL persistence directory, detaches it from workspaces, and synchronizes the lists incrementally | On |
|
|
18
|
+
|
|
19
|
+
All three switches live under **Settings → Plugins → Plugin configuration → DSH More** and take effect without rebuilding the plugin.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
### Requirements
|
|
24
|
+
|
|
25
|
+
- Node.js `>= 22.19`
|
|
26
|
+
- A working `dsh` CLI and a Web profile compatible with DSH `0.1.0-rc.7`
|
|
27
|
+
- Access to the npm registry that publishes `dsh-more`
|
|
28
|
+
|
|
29
|
+
pnpm is needed only when installing or developing from source. For a published release, use the DSH plugin command so the package is installed into the selected DSH profile; do not install it globally with `npm install -g`.
|
|
30
|
+
|
|
31
|
+
### Install the latest release from npm
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
dsh plugin --profile web add dsh-more
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This resolves `dsh-more` from npm and adds it to the Web profile. Restart the running Web service after the command finishes:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
dsh web
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If `dsh web` is already running, stop that process first and start it again. Then open **Settings → Plugins → Plugin configuration** and confirm that **DSH More** appears.
|
|
44
|
+
|
|
45
|
+
You can also verify the installed dependency from the command line:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
dsh plugin --profile web list dsh-more --depth 0
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Install a specific version
|
|
52
|
+
|
|
53
|
+
Pin the install when you need reproducible behavior or are testing a DSH compatibility combination:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
dsh plugin --profile web add dsh-more@0.0.1
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Update to the latest release
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
dsh plugin --profile web add dsh-more@latest
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Restart `dsh web` after updating. Existing patch settings remain in the DSH `dsh-more` Settings namespace.
|
|
66
|
+
|
|
67
|
+
### Newly published releases and `minimumReleaseAge`
|
|
68
|
+
|
|
69
|
+
A Web profile may intentionally reject a release that is newer than its pnpm `minimumReleaseAge`. The safest option is to wait for that cooling period. If you published or independently verified the release and intentionally want to install it immediately, relax the policy for this command only:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
dsh plugin --profile web add --config.minimumReleaseAge=0 dsh-more@latest
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This does not change the profile's long-term configuration, but it does relax the age check for the command's dependency resolution. Do not use it for an unverified package or rebuild the profile lockfile merely to bypass the policy.
|
|
76
|
+
|
|
77
|
+
### Install from a local checkout
|
|
78
|
+
|
|
79
|
+
Use this path for development before publishing to npm:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
pnpm install
|
|
83
|
+
pnpm run check
|
|
84
|
+
dsh plugin --profile web add /absolute/path/to/dsh-more
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
If local linking is blocked only because another dependency already locked in the Web profile has not satisfied `minimumReleaseAge`, use the existing store and a one-command policy override:
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
dsh plugin --profile web add --offline --config.minimumReleaseAge=0 /absolute/path/to/dsh-more
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`--offline` prevents new downloads, and both flags apply only to this command. Restart `dsh web` after installing the local checkout.
|
|
94
|
+
|
|
95
|
+
### Uninstall
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
dsh plugin --profile web remove dsh-more
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Restart `dsh web` once more. Removing the package removes its UI and runtime patches; it does not rewrite or recover session data that was already changed or permanently deleted while the plugin was installed.
|
|
102
|
+
|
|
103
|
+
## Usage and data semantics
|
|
104
|
+
|
|
105
|
+
### Edit a user message and restart
|
|
106
|
+
|
|
107
|
+
1. Hover over a user message and choose **Edit and restart from here**.
|
|
108
|
+
2. Change the text and select **Preview changes**.
|
|
109
|
+
3. Review how many turns will be discarded.
|
|
110
|
+
4. Confirm the edit.
|
|
111
|
+
|
|
112
|
+
The plugin cuts the durable log immediately before the selected message's owning turn, creates a child session with the same workspace, live preset composition, provider/model selection, and token limit, then sends the edited message as the next user input. The source session is archived, not physically deleted.
|
|
113
|
+
|
|
114
|
+
The first turn in the child reuses runtime context already retained by the seed so DSH does not append a duplicate system-prompt snapshot. Normal live context assembly resumes after that turn.
|
|
115
|
+
|
|
116
|
+
### Delete one message in place
|
|
117
|
+
|
|
118
|
+
1. Hover over a user or assistant message and choose **Delete this message**.
|
|
119
|
+
2. Review the message and any additional context nodes required for tool pairing.
|
|
120
|
+
3. Confirm the deletion.
|
|
121
|
+
|
|
122
|
+
Deletion is implemented as context reconstruction, not as a tombstone placed in front of the model. The plugin selects the surviving current model surface, rebuilds ordinary balanced turns and steps in a child session, archives the source branch, and opens the child. The deleted content is absent from the child's raw history, trajectory, and future model input.
|
|
123
|
+
|
|
124
|
+
Only the selected node is removed unless a tool call/result range must remain atomic. Editing is the operation that intentionally discards the selected turn and everything after it.
|
|
125
|
+
|
|
126
|
+
Both continuation operations preallocate their destination during preview and watch DSH's incremental `session-added` event while committing. As soon as the child appears, the client hands off directly instead of refreshing the list, passing through a blank view, and then reopening. A full refresh remains only as a compatibility fallback when the incremental event is missing.
|
|
127
|
+
|
|
128
|
+
### Permanently delete a session
|
|
129
|
+
|
|
130
|
+
Open a session row's overflow menu and choose the separate **Permanently delete session** action. Native **Archive session** remains unchanged.
|
|
131
|
+
|
|
132
|
+
After confirmation, DSH More cancels a running task, waits for idle, flushes the session, unloads its Agent/Session handles, removes the exact persistence directory returned by DSH, and detaches the session from its workspaces. The UI follows DSH incremental events first and refreshes the lists only when those events are missing. Deleting the current session selects an adjacent visible session first so the conversation area does not pass through a blank state.
|
|
133
|
+
|
|
134
|
+
> **Irreversible:** permanent deletion does not archive the session and does not move it into a DSH More trash directory.
|
|
135
|
+
|
|
136
|
+
## Architecture
|
|
137
|
+
|
|
138
|
+

|
|
139
|
+
|
|
140
|
+
The diagram intentionally contains no patch names or patch count. Adding a patch should update the generated catalog and the Markdown feature table, not require a new architecture image.
|
|
141
|
+
|
|
142
|
+
The repository is divided into four responsibilities:
|
|
143
|
+
|
|
144
|
+
- **Patch manifests and implementations** — each `src/patches/<patch-id>/patch.json` is the single discovery entry for one self-contained Host/Client patch.
|
|
145
|
+
- **Generated registry** — `pnpm run generate` validates manifests and derives the typed catalog, default Settings schema, Host registry, and Client registry under ignored `src/generated/`.
|
|
146
|
+
- **Patch kernel** — `src/kernel/` owns activation, disposal, shared message-action composition, Settings projection, and patch contracts without containing DSH-specific feature logic.
|
|
147
|
+
- **DSH adapter** — `src/platform/dsh/` concentrates Web slots/DOM targeting, the patch API, trust checks, session access, confirmation tokens, runtime-context replay, and wire-format validation.
|
|
148
|
+
|
|
149
|
+
At runtime, the Client bundle contributes controls to native DSH slots and calls `/<plugin>/api/<patch>/<action>`. The Host bundle gates the request, dispatches it to the owning enabled patch, and lets the patch use only the DSH services injected by the plugin manifest.
|
|
150
|
+
|
|
151
|
+
### Why manifest-driven patches?
|
|
152
|
+
|
|
153
|
+
A normal feature patch can be added without editing either root entry point:
|
|
154
|
+
|
|
155
|
+
1. create `src/patches/<patch-id>/`;
|
|
156
|
+
2. declare its identity, order, default state, Host entry, and Client kind in `patch.json`;
|
|
157
|
+
3. export `hostPatch` plus either `useMessageActions` or `clientPatch`;
|
|
158
|
+
4. colocate its tests;
|
|
159
|
+
5. run the generator and build.
|
|
160
|
+
|
|
161
|
+
The generator rejects missing entries, invalid or mismatched IDs, duplicate ordering, and unsupported Client kinds. This keeps discovery, runtime activation, Settings, and build output aligned.
|
|
162
|
+
|
|
163
|
+
## Safety model
|
|
164
|
+
|
|
165
|
+

|
|
166
|
+
|
|
167
|
+
The generic protocol is stable even as patches are added:
|
|
168
|
+
|
|
169
|
+
- mutation routes accept `POST` only, cap JSON bodies at 64 KiB, and require `x-dsh-more: 1`;
|
|
170
|
+
- the request authority must be loopback or an explicitly trusted host, cross-site requests are rejected, and a supplied `Origin` must match;
|
|
171
|
+
- disabled patches are rejected at the Host router even if an old Client still sends a request;
|
|
172
|
+
- all wire payloads start as `unknown` and are validated before use;
|
|
173
|
+
- message previews receive five-minute HMAC confirmation tokens bound to the session, log revision, surface generation, selected nodes, operation, target, preallocated continuation session, and edited-content digest;
|
|
174
|
+
- commits re-read current state, reject stale previews, and require an idle Agent maintenance window;
|
|
175
|
+
- Host setup and Client DOM effects are independently disposable when a patch is disabled;
|
|
176
|
+
- physical deletion accepts only an absolute per-session `jsonl` location, verifies the derived directory stays inside the persistence root, unloads the live session, then deletes that exact directory.
|
|
177
|
+
|
|
178
|
+
## Design principles
|
|
179
|
+
|
|
180
|
+
- **Native surfaces first.** Controls extend DSH's message rows, menus, overlays, and plugin settings instead of duplicating the product UI.
|
|
181
|
+
- **Clean continuations over synthetic history.** Context editing creates valid ordinary turns and steps; it does not inject deletion notices, empty assistant replacements, or special model-visible markers.
|
|
182
|
+
- **Preserve live composition.** Child sessions inherit the source Agent's current preset composition and model configuration rather than resolving a potentially different preset generation.
|
|
183
|
+
- **Centralize fragile integration.** DSH APIs, internal compatibility access, DOM selectors, request boundaries, and runtime replay live in the adapter layer rather than leaking across patches.
|
|
184
|
+
- **Everything switches off cleanly.** Host listeners/wrappers and Client observers/DOM contributions have explicit cleanup paths.
|
|
185
|
+
- **Keep native semantics separate.** Permanent deletion is a separate action; it never silently changes what DSH's Archive action means.
|
|
186
|
+
|
|
187
|
+
## Project layout
|
|
188
|
+
|
|
189
|
+
```text
|
|
190
|
+
src/
|
|
191
|
+
├── patches/ # Feature manifests, Host/Client code, shared wire types, tests
|
|
192
|
+
├── kernel/ # Patch contracts, activation, Settings card, shared UI composition
|
|
193
|
+
├── platform/dsh/ # Centralized DSH Host/Client adapters and security boundary
|
|
194
|
+
└── generated/ # Generated locally; ignored and never edited by hand
|
|
195
|
+
scripts/ # Registry generation
|
|
196
|
+
test/ # Cross-patch kernel/platform contract tests
|
|
197
|
+
build.mjs # Host ESM, Client bundle, declarations, injection validation
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Development
|
|
201
|
+
|
|
202
|
+
Read [CONTRIBUTING.md](./CONTRIBUTING.md) before adding a patch. It documents the patch-directory boundary, Host/Client contracts, destructive-operation requirements, test placement, and review checklist. The current contribution guide is written in Simplified Chinese.
|
|
203
|
+
|
|
204
|
+
```sh
|
|
205
|
+
pnpm run generate # Validate manifests and rebuild src/generated/
|
|
206
|
+
pnpm run typecheck # Generate, then run strict TypeScript checks
|
|
207
|
+
pnpm test # Generate, then run the Vitest suite
|
|
208
|
+
pnpm run build # Generate declarations plus Host and Client bundles
|
|
209
|
+
pnpm run check # Typecheck, test, and build
|
|
210
|
+
pnpm run package:check # Inspect the npm package without writing a tarball
|
|
211
|
+
pnpm run package # Build and create the npm tarball
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Generated source, `dist/`, and tarballs are not committed. `prepack` runs the complete check before `npm pack` or `npm publish`.
|
|
215
|
+
|
|
216
|
+
### Verification scope
|
|
217
|
+
|
|
218
|
+
Automated tests cover patch registry alignment, independent activation/disposal, Settings projection, mutation trust checks, confirmation expiry/tampering/staleness, message selection and clean rebuilds, runtime-context continuity, and cold/live session deletion.
|
|
219
|
+
|
|
220
|
+
The repository does not currently contain a full browser end-to-end suite against a real DSH Web instance. After changing DSH versions or UI adapters, manually verify message control placement, patch toggles, Chinese/English native menu detection, busy-session rejection, child-session navigation, and cold/live permanent deletion.
|
|
221
|
+
|
|
222
|
+
## Compatibility notes
|
|
223
|
+
|
|
224
|
+
- The current dependency set targets DSH `0.1.0-rc.7`; pre-release API or DOM changes can require adapter updates.
|
|
225
|
+
- In-product DSH More copy is currently primarily Simplified Chinese, although native Copy/Archive selectors recognize both Chinese and English labels.
|
|
226
|
+
- Message actions operate only on ordinary user messages and assistant messages still present in the current model surface; editing requires a completed owning turn.
|
|
227
|
+
- Permanent deletion supports DSH persistence locations of kind `jsonl` only.
|
|
228
|
+
- Unloading a live session prefers tracked public Agent handles. An already-live untracked session uses a guarded compatibility path for the current DSH registry internals, so this path deserves special review on DSH upgrades.
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
[MIT](./LICENSE)
|