dsh-more 0.0.1 → 0.0.2
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 +6 -6
- package/README.md +10 -9
- package/README.zh-CN.md +10 -9
- package/dist/client.js +178 -82
- package/dist/index.js +229 -23
- package/dist/types/generated/host-settings-schema.d.ts +2 -0
- package/dist/types/generated/patch-catalog.d.ts +8 -0
- package/dist/types/patches/conversation-markdown-export/client/filename.d.ts +1 -0
- package/dist/types/patches/conversation-markdown-export/client/index.d.ts +2 -0
- package/dist/types/patches/conversation-markdown-export/host/index.d.ts +2 -0
- package/dist/types/patches/conversation-markdown-export/host/transcript.d.ts +9 -0
- package/dist/types/patches/conversation-markdown-export/shared.d.ts +5 -0
- package/dist/types/patches/session-delete/host/session-deletion.d.ts +0 -1
- package/dist/types/platform/dsh/client/styles.d.ts +1 -1
- package/dist/types/platform/dsh/host/continuation.d.ts +4 -0
- package/dist/types/platform/dsh/host/error.d.ts +1 -1
- package/dist/types/platform/dsh/host/wire.d.ts +1 -0
- package/dsh.plugin.json +12 -2
- package/package.json +7 -4
package/CONTRIBUTING.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
开发环境要求:
|
|
12
12
|
|
|
13
|
-
- Node.js
|
|
13
|
+
- Node.js `22.19.x` 或 `>= 24`;
|
|
14
14
|
- pnpm;
|
|
15
15
|
- 与项目当前依赖匹配的 DSH。本版本面向 DSH `0.1.0-rc.7`。
|
|
16
16
|
|
|
@@ -220,9 +220,9 @@ Standalone 规则:
|
|
|
220
220
|
2. Host 路由继续经过 loopback/trusted-host、same-origin 和自定义请求头检查;
|
|
221
221
|
3. 预览与提交分离,提交时重新校验会话 revision、目标和确认令牌;
|
|
222
222
|
4. 忙碌会话应拒绝操作,或先完成明确的 cancel、idle、flush、unload 流程;
|
|
223
|
-
5. 删除路径必须从 DSH
|
|
223
|
+
5. 删除路径必须从 DSH 返回的逐对象定位信息推导,只接受已知的逐会话 artifact 布局,不把本机路径返回 Client;
|
|
224
224
|
6. 禁止对宽泛目录、未解析变量或 glob 执行递归删除;
|
|
225
|
-
7.
|
|
225
|
+
7. 多步写入必须把可撤销副作用记录下来,并在后续步骤失败时执行补偿;不要用“尽量成功”的部分写入掩盖失败。
|
|
226
226
|
|
|
227
227
|
以下内容属于持久化协议,不应在普通重构中随意改名:
|
|
228
228
|
|
|
@@ -283,7 +283,7 @@ pnpm run package:check
|
|
|
283
283
|
|
|
284
284
|
- 当前分支已经设置 upstream,并与远端完全同步;
|
|
285
285
|
- 工作区没有未提交文件;
|
|
286
|
-
-
|
|
286
|
+
- 正式发布前已执行 `npm login --registry=https://registry.npmjs.org/`,且当前 npm 用户有权发布 `dsh-more`;dry-run 不要求 npm 登录;
|
|
287
287
|
- Git 远端允许推送当前分支和 `v<version>` tag。
|
|
288
288
|
|
|
289
289
|
先执行不产生任何版本、tag 或发布记录的预检:
|
|
@@ -301,7 +301,7 @@ pnpm release:minor # 0.0.1 -> 0.1.0
|
|
|
301
301
|
pnpm release:major # 0.0.1 -> 1.0.0
|
|
302
302
|
```
|
|
303
303
|
|
|
304
|
-
脚本会依次检查 Git/npm 状态,运行完整项目检查,自动修改 `package.json`
|
|
304
|
+
脚本会依次检查 Git/npm 状态,运行完整项目检查,自动修改 `package.json` 版本并通过 npm `version` 生命周期同步 `dsh.plugin.json`,随后创建 `chore(release): v<version>` 提交和同名 tag,发布到公共 npm registry,最后以一次原子 push 同时推送版本提交和 tag。首次发布时,若 npm 上还不存在这个包,`pnpm release` 会直接发布 `package.json` 中的当前版本并为当前提交创建 tag。
|
|
305
305
|
|
|
306
306
|
如果 npm 账户要求一次性验证码,可以使用:
|
|
307
307
|
|
|
@@ -319,7 +319,7 @@ pnpm run release -- --otp=123456
|
|
|
319
319
|
- 运行时由 DSH 提供的外部包放入 `peerDependencies`,并在 `devDependencies` 固定本地开发版本;
|
|
320
320
|
- Client bundle 真正产生外部 `require()` 时,将对应模块加入 `dsh.client.inject`;
|
|
321
321
|
- 只用于 TypeScript module augmentation、且不会出现在发布声明或运行时 bundle 中的包,可以仅作为开发依赖;
|
|
322
|
-
- 同步 `pnpm-lock.yaml
|
|
322
|
+
- 同步 `pnpm-lock.yaml`;本仓库不维护并行的 `package-lock.json`;
|
|
323
323
|
- 不要为了一个很小的 helper 引入大型依赖。
|
|
324
324
|
|
|
325
325
|
更新锁文件后再次运行完整检查,并在变更说明中写明依赖的用途。
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Practical, independently switchable patches for missing context and history cont
|
|
|
6
6
|
|
|
7
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
8
|
|
|
9
|
-
> **Compatibility:**
|
|
9
|
+
> **Compatibility:** the current release targets DeepSeek Harness `0.1.0-rc.7` and requires Node.js `>= 24`. DSH is still a release candidate, so upgrades may require changes in the centralized adapter layer described below.
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
@@ -22,7 +22,7 @@ All three switches live under **Settings → Plugins → Plugin configuration
|
|
|
22
22
|
|
|
23
23
|
### Requirements
|
|
24
24
|
|
|
25
|
-
- Node.js `>=
|
|
25
|
+
- Node.js `>= 24`
|
|
26
26
|
- A working `dsh` CLI and a Web profile compatible with DSH `0.1.0-rc.7`
|
|
27
27
|
- Access to the npm registry that publishes `dsh-more`
|
|
28
28
|
|
|
@@ -105,9 +105,7 @@ Restart `dsh web` once more. Removing the package removes its UI and runtime pat
|
|
|
105
105
|
### Edit a user message and restart
|
|
106
106
|
|
|
107
107
|
1. Hover over a user message and choose **Edit and restart from here**.
|
|
108
|
-
2. Change the text and select **
|
|
109
|
-
3. Review how many turns will be discarded.
|
|
110
|
-
4. Confirm the edit.
|
|
108
|
+
2. Change the text and select **Edit and restart**. The change is applied directly without a second preview-and-confirm step.
|
|
111
109
|
|
|
112
110
|
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
111
|
|
|
@@ -123,7 +121,7 @@ Deletion is implemented as context reconstruction, not as a tombstone placed in
|
|
|
123
121
|
|
|
124
122
|
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
123
|
|
|
126
|
-
|
|
124
|
+
Editing performs its state check and preallocates the continuation inside the single submission; message deletion still shows a preview before confirmation. Both operations 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
125
|
|
|
128
126
|
### Permanently delete a session
|
|
129
127
|
|
|
@@ -166,14 +164,16 @@ The generator rejects missing entries, invalid or mismatched IDs, duplicate orde
|
|
|
166
164
|
|
|
167
165
|
The generic protocol is stable even as patches are added:
|
|
168
166
|
|
|
169
|
-
- mutation routes accept `POST` only, cap
|
|
167
|
+
- mutation routes accept `POST` with `application/json` only, cap bodies at 64 KiB, and require `x-dsh-more: 1`;
|
|
170
168
|
- the request authority must be loopback or an explicitly trusted host, cross-site requests are rejected, and a supplied `Origin` must match;
|
|
171
169
|
- disabled patches are rejected at the Host router even if an old Client still sends a request;
|
|
172
170
|
- all wire payloads start as `unknown` and are validated before use;
|
|
173
|
-
- message
|
|
171
|
+
- message-operation state checks 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; editing performs the check and commit within one click, while message deletion retains its visible preview confirmation;
|
|
174
172
|
- commits re-read current state, reject stale previews, and require an idle Agent maintenance window;
|
|
173
|
+
- failed continuation publication detaches the preallocated child and disposes its Agent handle before returning the error;
|
|
174
|
+
- unexpected Host failures are logged with diagnostics while the browser receives a generic internal-error response instead of local paths or stack details;
|
|
175
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
|
|
176
|
+
- physical deletion accepts only an absolute per-session `jsonl` location with DSH's fixed transcript filename, unloads the live session, then deletes that exact session-owned directory; local paths are not returned to the Client.
|
|
177
177
|
|
|
178
178
|
## Design principles
|
|
179
179
|
|
|
@@ -203,6 +203,7 @@ Read [CONTRIBUTING.md](./CONTRIBUTING.md) before adding a patch. It documents th
|
|
|
203
203
|
|
|
204
204
|
```sh
|
|
205
205
|
pnpm run generate # Validate manifests and rebuild src/generated/
|
|
206
|
+
pnpm run peers:check # Verify the locked peer-dependency graph
|
|
206
207
|
pnpm run typecheck # Generate, then run strict TypeScript checks
|
|
207
208
|
pnpm test # Generate, then run the Vitest suite
|
|
208
209
|
pnpm run build # Generate declarations plus Host and Client bundles
|
package/README.zh-CN.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
`dsh-more` 不创建平行的“管理中心”,而是把能力直接嵌入 DSH 现有界面。当前补丁可以编辑用户消息并从该处重新开始、在保留前后上下文的前提下删除单条消息,以及在不改变原生归档语义的情况下永久删除会话。
|
|
8
8
|
|
|
9
|
-
> **兼容性:**
|
|
9
|
+
> **兼容性:** 当前发布面向 DeepSeek Harness `0.1.0-rc.7`,要求 Node.js `>= 24`。DSH 仍是 RC 版本,升级后可能需要调整下文所述的集中适配层。
|
|
10
10
|
|
|
11
11
|
## 功能概览
|
|
12
12
|
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
### 环境要求
|
|
24
24
|
|
|
25
|
-
- Node.js `>=
|
|
25
|
+
- Node.js `>= 24`
|
|
26
26
|
- 可正常使用的 `dsh` CLI,以及与 DSH `0.1.0-rc.7` 兼容的 Web profile
|
|
27
27
|
- 能访问发布 `dsh-more` 的 npm registry
|
|
28
28
|
|
|
@@ -105,9 +105,7 @@ dsh plugin --profile web remove dsh-more
|
|
|
105
105
|
### 编辑用户消息并从这里重新开始
|
|
106
106
|
|
|
107
107
|
1. 把鼠标移到用户消息上,选择 **编辑并从这里重新开始**。
|
|
108
|
-
2. 修改文本并点击
|
|
109
|
-
3. 检查将丢弃的轮次数。
|
|
110
|
-
4. 确认编辑。
|
|
108
|
+
2. 修改文本并点击 **修改并重新开始**,修改会直接生效,不再显示二次预览确认。
|
|
111
109
|
|
|
112
110
|
插件会在所选消息所属轮次之前切断持久日志,创建继承相同 Workspace、源 Agent 当前 Preset composition、provider/model 和 token limit 的子会话,再把编辑后的消息作为下一条用户输入。源会话只会归档,不会物理删除。
|
|
113
111
|
|
|
@@ -123,7 +121,7 @@ dsh plugin --profile web remove dsh-more
|
|
|
123
121
|
|
|
124
122
|
除非工具调用与结果必须保持原子性,否则只删除用户点选的节点。会主动丢弃所选轮次及其后内容的是“编辑并重新开始”。
|
|
125
123
|
|
|
126
|
-
|
|
124
|
+
编辑会在单次提交内部完成状态检查并预分配延续会话;消息删除仍在确认前显示预览。两者都会在提交期间监听 DSH 的 `session-added` 增量事件;新会话一出现就直接完成交接,不再先刷新列表、短暂进入空白页再打开新会话。全量刷新只保留为增量事件缺失时的兼容兜底。
|
|
127
125
|
|
|
128
126
|
### 永久删除会话
|
|
129
127
|
|
|
@@ -166,14 +164,16 @@ dsh plugin --profile web remove dsh-more
|
|
|
166
164
|
|
|
167
165
|
这套通用协议不会随补丁数量变化:
|
|
168
166
|
|
|
169
|
-
- 写接口只接受 `POST`,
|
|
167
|
+
- 写接口只接受 `POST` 与 `application/json`,body 上限为 64 KiB,并要求 `x-dsh-more: 1`;
|
|
170
168
|
- 请求 authority 必须是 loopback 或显式 trusted host;拒绝 cross-site;存在 `Origin` 时必须与 Host 匹配;
|
|
171
169
|
- 即使旧 Client 继续发请求,Host router 也会拒绝已关闭补丁;
|
|
172
170
|
- 所有线协议 payload 都从不可信 `unknown` 开始,先校验再使用;
|
|
173
|
-
-
|
|
171
|
+
- 消息操作的状态检查会获得五分钟 HMAC 确认令牌,绑定 Session、日志 revision、surface generation、选中节点、操作类型、目标、预分配的延续会话与编辑内容摘要;编辑在一次点击内完成检查与提交,消息删除则保留用户可见的预览确认;
|
|
174
172
|
- 提交阶段重新读取当前状态,过期或失配的预览会被拒绝,并要求 Agent 处于 idle maintenance window;
|
|
173
|
+
- 续接发布失败时,会先从 Workspace 移除预分配子会话并释放其 Agent handle,再返回错误;
|
|
174
|
+
- 未预期的 Host 异常保留在服务端日志中,浏览器只收到通用内部错误,不暴露本机路径或堆栈细节;
|
|
175
175
|
- 关闭补丁时,Host setup 和 Client DOM 副作用都可以独立清理;
|
|
176
|
-
- 物理删除只接受绝对的逐会话 `jsonl`
|
|
176
|
+
- 物理删除只接受绝对的逐会话 `jsonl` 定位与 DSH 固定 transcript 文件名,卸载实时会话后只删除该会话自有目录;本机路径不会返回 Client。
|
|
177
177
|
|
|
178
178
|
## 设计思想
|
|
179
179
|
|
|
@@ -203,6 +203,7 @@ build.mjs # Host ESM、Client bundle、声明和 inject 校验
|
|
|
203
203
|
|
|
204
204
|
```sh
|
|
205
205
|
pnpm run generate # 校验清单并重建 src/generated/
|
|
206
|
+
pnpm run peers:check # 校验锁定依赖的 peer 契约
|
|
206
207
|
pnpm run typecheck # 生成后执行严格 TypeScript 检查
|
|
207
208
|
pnpm test # 生成后执行 Vitest 测试
|
|
208
209
|
pnpm run build # 生成声明、Host 与 Client bundle
|
package/dist/client.js
CHANGED
|
@@ -25,19 +25,21 @@ __export(index_exports, {
|
|
|
25
25
|
inject: () => inject
|
|
26
26
|
});
|
|
27
27
|
module.exports = __toCommonJS(index_exports);
|
|
28
|
-
var
|
|
28
|
+
var import_react7 = require("react");
|
|
29
29
|
var import_react_dom = require("react-dom");
|
|
30
30
|
|
|
31
31
|
// src/generated/patch-catalog.ts
|
|
32
32
|
var PATCH_CATALOG = [
|
|
33
33
|
{ "id": "message-edit", "name": "\u7F16\u8F91\u5E76\u91CD\u65B0\u5F00\u59CB", "description": "\u7F16\u8F91\u7528\u6237\u6D88\u606F\u5E76\u4E22\u5F03\u8BE5\u6D88\u606F\u4E4B\u540E\u7684\u5386\u53F2", "defaultEnabled": true, "order": 100, "clientKind": "message-action" },
|
|
34
34
|
{ "id": "message-delete", "name": "\u5355\u6761\u6D88\u606F\u5220\u9664", "description": "\u5220\u9664\u6307\u5B9A\u6D88\u606F\u5E76\u4FDD\u7559\u524D\u540E\u5386\u53F2\u8BB0\u5F55", "defaultEnabled": true, "order": 200, "clientKind": "message-action" },
|
|
35
|
-
{ "id": "session-delete", "name": "\u6C38\u4E45\u5220\u9664\u4F1A\u8BDD", "description": "\u505C\u6B62\u76EE\u6807\u4F1A\u8BDD\u5E76\u4ECE\u78C1\u76D8\u6C38\u4E45\u5220\u9664\uFF0C\u4E0D\u5F71\u54CD\u539F\u751F\u5F52\u6863", "defaultEnabled": true, "order": 300, "clientKind": "standalone" }
|
|
35
|
+
{ "id": "session-delete", "name": "\u6C38\u4E45\u5220\u9664\u4F1A\u8BDD", "description": "\u505C\u6B62\u76EE\u6807\u4F1A\u8BDD\u5E76\u4ECE\u78C1\u76D8\u6C38\u4E45\u5220\u9664\uFF0C\u4E0D\u5F71\u54CD\u539F\u751F\u5F52\u6863", "defaultEnabled": true, "order": 300, "clientKind": "standalone" },
|
|
36
|
+
{ "id": "conversation-markdown-export", "name": "\u5BFC\u51FA\u5B8C\u6574\u804A\u5929\u8BB0\u5F55", "description": "\u5C06\u5F53\u524D\u4F1A\u8BDD\u7684\u5B8C\u6574\u804A\u5929\u8BB0\u5F55\u4E0B\u8F7D\u4E3A Markdown \u6587\u4EF6", "defaultEnabled": true, "order": 400, "clientKind": "standalone" }
|
|
36
37
|
];
|
|
37
38
|
var DEFAULT_PATCH_SETTINGS = {
|
|
38
39
|
"message-edit": true,
|
|
39
40
|
"message-delete": true,
|
|
40
|
-
"session-delete": true
|
|
41
|
+
"session-delete": true,
|
|
42
|
+
"conversation-markdown-export": true
|
|
41
43
|
};
|
|
42
44
|
function decodePatchSettings(value) {
|
|
43
45
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
@@ -68,6 +70,7 @@ var DshMoreApiError = class extends Error {
|
|
|
68
70
|
this.code = code;
|
|
69
71
|
this.name = "DshMoreApiError";
|
|
70
72
|
}
|
|
73
|
+
code;
|
|
71
74
|
};
|
|
72
75
|
async function callPatchApi(patchId, action, payload) {
|
|
73
76
|
let response;
|
|
@@ -191,41 +194,28 @@ var api = {
|
|
|
191
194
|
function useMessageActions(props, enabled) {
|
|
192
195
|
const [target, setTarget] = (0, import_react.useState)(null);
|
|
193
196
|
const [text, setText] = (0, import_react.useState)("");
|
|
194
|
-
const [preview, setPreview] = (0, import_react.useState)(null);
|
|
195
197
|
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
196
198
|
const [error, setError] = (0, import_react.useState)(null);
|
|
197
199
|
(0, import_react.useEffect)(() => {
|
|
198
200
|
if (enabled) return;
|
|
199
201
|
setTarget(null);
|
|
200
|
-
setPreview(null);
|
|
201
202
|
setError(null);
|
|
202
203
|
}, [enabled]);
|
|
203
204
|
const open = (next) => {
|
|
204
205
|
setTarget(next);
|
|
205
206
|
setText(next.text);
|
|
206
|
-
setPreview(null);
|
|
207
207
|
setError(null);
|
|
208
208
|
};
|
|
209
|
-
const
|
|
209
|
+
const applyChange = async () => {
|
|
210
210
|
if (target === null) return;
|
|
211
211
|
setBusy(true);
|
|
212
212
|
setError(null);
|
|
213
|
-
try {
|
|
214
|
-
setPreview(await api.preview({ sessionId: props.sessionId, targetSeq: target.seq, text }));
|
|
215
|
-
} catch (caught) {
|
|
216
|
-
setError(apiErrorText(caught));
|
|
217
|
-
} finally {
|
|
218
|
-
setBusy(false);
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
const commit = async () => {
|
|
222
|
-
if (target === null || preview === null) return;
|
|
223
|
-
setBusy(true);
|
|
224
|
-
setError(null);
|
|
225
213
|
const sessions = props.ctx.sessions;
|
|
226
|
-
|
|
227
|
-
const handoff = followSessionHandoff(sessions, continuationSessionId);
|
|
214
|
+
let handoff;
|
|
228
215
|
try {
|
|
216
|
+
const preview = await api.preview({ sessionId: props.sessionId, targetSeq: target.seq, text });
|
|
217
|
+
const continuationSessionId = preview.continuationSessionId;
|
|
218
|
+
handoff = followSessionHandoff(sessions, continuationSessionId);
|
|
229
219
|
const result = await api.commit({
|
|
230
220
|
sessionId: props.sessionId,
|
|
231
221
|
targetSeq: target.seq,
|
|
@@ -236,13 +226,12 @@ function useMessageActions(props, enabled) {
|
|
|
236
226
|
await handoff.finish(result.sessionId);
|
|
237
227
|
setTarget(null);
|
|
238
228
|
} catch (caught) {
|
|
239
|
-
handoff
|
|
229
|
+
handoff?.cancel();
|
|
240
230
|
const sourceId = props.sessionId;
|
|
241
231
|
const sessionList = sessions.list.getSnapshot();
|
|
242
232
|
const sourceAvailable = sessionList.byId[sourceId] !== void 0;
|
|
243
233
|
const sourceArchived = props.ctx.workspaces.list.getSnapshot().archivedSessionIds.includes(sourceId);
|
|
244
234
|
if (sourceAvailable && !sourceArchived && sessionList.current !== sourceId) openSessionWithoutGap(sessions, sourceId);
|
|
245
|
-
setPreview(null);
|
|
246
235
|
setError(apiErrorText(caught));
|
|
247
236
|
} finally {
|
|
248
237
|
setBusy(false);
|
|
@@ -262,12 +251,9 @@ function useMessageActions(props, enabled) {
|
|
|
262
251
|
closeLabel: "\u5173\u95ED",
|
|
263
252
|
className: "dshmore-edit-modal",
|
|
264
253
|
description: "\u4FEE\u6539\u540E\u4F1A\u521B\u5EFA\u5E72\u51C0\u7684\u524D\u7F00\u4F1A\u8BDD\uFF0C\u8FD9\u6761\u6D88\u606F\u4E4B\u540E\u7684\u5185\u5BB9\u4E0D\u4F1A\u4FDD\u7559\u3002",
|
|
265
|
-
footer:
|
|
254
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
266
255
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", disabled: busy, onClick: () => setTarget(null), children: "\u53D6\u6D88" }),
|
|
267
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy || text.trim() === "", onClick: () => void
|
|
268
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
269
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", disabled: busy, onClick: () => setPreview(null), children: "\u8FD4\u56DE\u4FEE\u6539" }),
|
|
270
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy, onClick: () => void commit(), children: busy ? "\u6B63\u5728\u91CD\u5EFA\u2026" : "\u786E\u8BA4\u7F16\u8F91\u5E76\u4E22\u5F03\u540E\u7EED" })
|
|
256
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy || text.trim() === "", onClick: () => void applyChange(), children: busy ? "\u6B63\u5728\u4FEE\u6539\u2026" : "\u4FEE\u6539\u5E76\u91CD\u65B0\u5F00\u59CB" })
|
|
271
257
|
] }),
|
|
272
258
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dshmore-dialog-body", children: [
|
|
273
259
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -275,23 +261,15 @@ function useMessageActions(props, enabled) {
|
|
|
275
261
|
{
|
|
276
262
|
className: "dshmore-editor",
|
|
277
263
|
value: text,
|
|
278
|
-
disabled: busy
|
|
264
|
+
disabled: busy,
|
|
279
265
|
onChange: (event) => {
|
|
280
266
|
setText(event.currentTarget.value);
|
|
281
|
-
setPreview(null);
|
|
282
267
|
setError(null);
|
|
283
268
|
},
|
|
284
269
|
rows: 7,
|
|
285
270
|
autoFocus: true
|
|
286
271
|
}
|
|
287
272
|
),
|
|
288
|
-
preview !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dshmore-warning", children: [
|
|
289
|
-
"\u5C06\u4ECE\u7B2C ",
|
|
290
|
-
preview.turn,
|
|
291
|
-
" \u8F6E\u4E4B\u524D\u91CD\u5EFA\u4F1A\u8BDD\uFF0C\u5E76\u4E22\u5F03\u5F53\u524D\u8F6E\u53CA\u4E4B\u540E\u5171 ",
|
|
292
|
-
preview.laterTurnCount,
|
|
293
|
-
" \u8F6E\u5185\u5BB9\u3002"
|
|
294
|
-
] }),
|
|
295
273
|
error !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dshmore-error", children: error })
|
|
296
274
|
] })
|
|
297
275
|
}
|
|
@@ -403,6 +381,7 @@ var import_dsh_client_ui_primitives3 = require("@deepseek-ai/dsh-client-ui-primi
|
|
|
403
381
|
|
|
404
382
|
// src/platform/dsh/client/styles.ts
|
|
405
383
|
var styles = `
|
|
384
|
+
[data-dshmore-hidden] { display: none !important; }
|
|
406
385
|
.dshmore-inline-actions { display: inline-flex; align-items: center; gap: 10px; }
|
|
407
386
|
.dshmore-inline-button { width: 28px; height: 28px; color: var(--dsw-alias-label-tertiary); cursor: pointer; background: transparent; border: 0; border-radius: 28px; display: inline-flex; align-items: center; justify-content: center; padding: 6px; }
|
|
408
387
|
.dshmore-inline-button:hover { color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-interactive-bg-hover); }
|
|
@@ -460,18 +439,24 @@ function replaceExactText(root, before, after) {
|
|
|
460
439
|
}
|
|
461
440
|
function installSessionDeleteMenuItems(onDelete) {
|
|
462
441
|
let frame = null;
|
|
442
|
+
const deleteItems = /* @__PURE__ */ new WeakMap();
|
|
463
443
|
const sync = () => {
|
|
464
444
|
for (const archiveButton of document.querySelectorAll('button[role="menuitem"]')) {
|
|
465
445
|
const text = archiveButton.textContent?.trim();
|
|
466
446
|
const deleteLabel = text === "\u5F52\u6863\u4F1A\u8BDD" ? "\u6C38\u4E45\u5220\u9664\u4F1A\u8BDD" : text === "Archive session" ? "Permanently delete session" : void 0;
|
|
467
|
-
if (deleteLabel === void 0
|
|
447
|
+
if (deleteLabel === void 0) continue;
|
|
468
448
|
const wrapper = archiveButton.parentElement;
|
|
469
449
|
if (wrapper === null) continue;
|
|
450
|
+
if (archiveButton.hasAttribute(DELETE_SOURCE_ATTRIBUTE)) {
|
|
451
|
+
if (deleteItems.get(archiveButton)?.isConnected === true) continue;
|
|
452
|
+
archiveButton.removeAttribute(DELETE_SOURCE_ATTRIBUTE);
|
|
453
|
+
}
|
|
470
454
|
const clone = wrapper.cloneNode(true);
|
|
471
455
|
const deleteButton = clone.querySelector('button[role="menuitem"]');
|
|
472
456
|
if (deleteButton === null) continue;
|
|
473
457
|
archiveButton.setAttribute(DELETE_SOURCE_ATTRIBUTE, "");
|
|
474
458
|
clone.setAttribute(DELETE_MENU_ATTRIBUTE, "");
|
|
459
|
+
deleteItems.set(archiveButton, clone);
|
|
475
460
|
replaceExactText(deleteButton, text, deleteLabel);
|
|
476
461
|
deleteButton.setAttribute("aria-label", deleteLabel);
|
|
477
462
|
deleteButton.classList.add("dshmore-session-delete-menu-item");
|
|
@@ -623,15 +608,131 @@ var clientPatch = {
|
|
|
623
608
|
}
|
|
624
609
|
};
|
|
625
610
|
|
|
611
|
+
// src/patches/conversation-markdown-export/client/index.tsx
|
|
612
|
+
var import_react4 = require("react");
|
|
613
|
+
var import_dsh_client_ui_primitives4 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
614
|
+
|
|
615
|
+
// src/patches/conversation-markdown-export/shared.ts
|
|
616
|
+
var CONVERSATION_MARKDOWN_EXPORT_PATCH_ID = "conversation-markdown-export";
|
|
617
|
+
|
|
618
|
+
// src/patches/conversation-markdown-export/client/filename.ts
|
|
619
|
+
function safeStem(value) {
|
|
620
|
+
return [...value.normalize("NFKC").replace(/[\u0000-\u001f\u007f/\\?%*:|"<>]+/g, "-").replace(/\s+/g, " ").replace(/^[.\s-]+|[.\s-]+$/g, "")].slice(0, 80).join("").trim();
|
|
621
|
+
}
|
|
622
|
+
function markdownFilename(title, sessionId) {
|
|
623
|
+
const fallback = safeStem(`dsh-conversation-${sessionId}`) || "dsh-conversation";
|
|
624
|
+
return `${safeStem(title) || fallback}.md`;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// src/patches/conversation-markdown-export/client/index.tsx
|
|
628
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
629
|
+
var exportStyles = `
|
|
630
|
+
.dshmore-markdown-export-button { box-sizing: border-box; min-width: 128px; height: 32px; color: var(--dsw-alias-label-primary); cursor: pointer; background: transparent; border: 1px solid var(--dsw-alias-border-l2); border-radius: 18px; display: inline-flex; align-items: center; justify-content: center; gap: 5px; padding: 6px 12px; font-family: var(--dsw-font-family); font-size: 13px; line-height: 20px; }
|
|
631
|
+
.dshmore-markdown-export-button:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
|
|
632
|
+
.dshmore-markdown-export-button:disabled { color: var(--dsw-alias-label-dimmed); cursor: wait; }
|
|
633
|
+
.dshmore-markdown-export-button > span, .dshmore-markdown-export-button > svg { flex: none; }
|
|
634
|
+
`;
|
|
635
|
+
function saveMarkdown(filename, markdown) {
|
|
636
|
+
const url = URL.createObjectURL(new Blob(["\uFEFF", markdown], { type: "text/markdown;charset=utf-8" }));
|
|
637
|
+
const anchor = document.createElement("a");
|
|
638
|
+
anchor.href = url;
|
|
639
|
+
anchor.download = filename;
|
|
640
|
+
anchor.style.display = "none";
|
|
641
|
+
document.body.appendChild(anchor);
|
|
642
|
+
anchor.click();
|
|
643
|
+
anchor.remove();
|
|
644
|
+
window.setTimeout(() => {
|
|
645
|
+
URL.revokeObjectURL(url);
|
|
646
|
+
}, 0);
|
|
647
|
+
}
|
|
648
|
+
function ConversationMarkdownExportButton(props) {
|
|
649
|
+
const settings = (0, import_react4.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot);
|
|
650
|
+
const enabled = settings[CONVERSATION_MARKDOWN_EXPORT_PATCH_ID];
|
|
651
|
+
const title = props.useSessions((state) => state.byId[props.sessionId]?.displayTitle ?? String(props.sessionId));
|
|
652
|
+
const [busy, setBusy] = (0, import_react4.useState)(false);
|
|
653
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
654
|
+
const generation = (0, import_react4.useRef)(0);
|
|
655
|
+
const activeSession = (0, import_react4.useRef)(null);
|
|
656
|
+
activeSession.current = enabled ? String(props.sessionId) : null;
|
|
657
|
+
(0, import_react4.useEffect)(() => {
|
|
658
|
+
generation.current += 1;
|
|
659
|
+
setBusy(false);
|
|
660
|
+
setError(null);
|
|
661
|
+
}, [enabled, props.sessionId]);
|
|
662
|
+
const download = async () => {
|
|
663
|
+
const requestSessionId = String(props.sessionId);
|
|
664
|
+
const requestGeneration = ++generation.current;
|
|
665
|
+
setBusy(true);
|
|
666
|
+
setError(null);
|
|
667
|
+
try {
|
|
668
|
+
const result = await callPatchApi(
|
|
669
|
+
CONVERSATION_MARKDOWN_EXPORT_PATCH_ID,
|
|
670
|
+
"render",
|
|
671
|
+
{ sessionId: props.sessionId, title }
|
|
672
|
+
);
|
|
673
|
+
if (generation.current !== requestGeneration || activeSession.current !== requestSessionId) return;
|
|
674
|
+
saveMarkdown(markdownFilename(title, String(props.sessionId)), result.markdown);
|
|
675
|
+
} catch (caught) {
|
|
676
|
+
if (generation.current === requestGeneration && activeSession.current === requestSessionId) setError(apiErrorText(caught));
|
|
677
|
+
} finally {
|
|
678
|
+
if (generation.current === requestGeneration && activeSession.current === requestSessionId) setBusy(false);
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
if (!enabled) return null;
|
|
682
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
683
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: exportStyles }),
|
|
684
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
685
|
+
"button",
|
|
686
|
+
{
|
|
687
|
+
type: "button",
|
|
688
|
+
className: "dshmore-markdown-export-button",
|
|
689
|
+
disabled: busy,
|
|
690
|
+
"aria-busy": busy,
|
|
691
|
+
"aria-label": "\u5BFC\u51FA\u5B8C\u6574\u804A\u5929\u8BB0\u5F55\u4E3A Markdown",
|
|
692
|
+
onClick: () => {
|
|
693
|
+
void download();
|
|
694
|
+
},
|
|
695
|
+
children: [
|
|
696
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: busy ? "\u6B63\u5728\u5BFC\u51FA\u2026" : "\u5BFC\u51FA Markdown" }),
|
|
697
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_dsh_client_ui_primitives4.IconDownloadOutline16, { size: 12 })
|
|
698
|
+
]
|
|
699
|
+
}
|
|
700
|
+
),
|
|
701
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
702
|
+
import_dsh_client_ui_primitives4.Modal,
|
|
703
|
+
{
|
|
704
|
+
open: error !== null,
|
|
705
|
+
onClose: () => setError(null),
|
|
706
|
+
title: "\u65E0\u6CD5\u5BFC\u51FA\u804A\u5929\u8BB0\u5F55",
|
|
707
|
+
closeLabel: "\u5173\u95ED",
|
|
708
|
+
description: error ?? "",
|
|
709
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_dsh_client_ui_primitives4.Button, { variant: "primary", onClick: () => setError(null), children: "\u5173\u95ED" })
|
|
710
|
+
}
|
|
711
|
+
)
|
|
712
|
+
] });
|
|
713
|
+
}
|
|
714
|
+
var clientPatch2 = {
|
|
715
|
+
id: CONVERSATION_MARKDOWN_EXPORT_PATCH_ID,
|
|
716
|
+
install: (ctx, activation) => {
|
|
717
|
+
ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
|
|
718
|
+
name: "conversation.session.header.utilities",
|
|
719
|
+
id: `${PLUGIN_NAME}-${CONVERSATION_MARKDOWN_EXPORT_PATCH_ID}`,
|
|
720
|
+
order: 100,
|
|
721
|
+
registrant: PLUGIN_NAME
|
|
722
|
+
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ConversationMarkdownExportButton, { ...props, activation })));
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
|
|
626
726
|
// src/generated/client-registry.ts
|
|
627
727
|
var MESSAGE_ACTION_PATCHES = [{ id: "message-edit", order: 100, useActions: useMessageActions }, { id: "message-delete", order: 200, useActions: useMessageActions2 }];
|
|
628
|
-
var CLIENT_PATCHES = [clientPatch];
|
|
728
|
+
var CLIENT_PATCHES = [clientPatch, clientPatch2];
|
|
629
729
|
|
|
630
730
|
// src/kernel/client/activation.ts
|
|
631
731
|
var ClientPatchActivation = class {
|
|
632
732
|
constructor(scope) {
|
|
633
733
|
this.scope = scope;
|
|
634
734
|
}
|
|
735
|
+
scope;
|
|
635
736
|
lastScope;
|
|
636
737
|
lastValue = { ...DEFAULT_PATCH_SETTINGS };
|
|
637
738
|
getSnapshot = () => {
|
|
@@ -647,19 +748,19 @@ var ClientPatchActivation = class {
|
|
|
647
748
|
};
|
|
648
749
|
|
|
649
750
|
// src/kernel/client/config-card.tsx
|
|
650
|
-
var
|
|
651
|
-
var
|
|
652
|
-
var
|
|
751
|
+
var import_react5 = require("react");
|
|
752
|
+
var import_dsh_client_ui_primitives5 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
753
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
653
754
|
function PatchConfigCard({ activation, initiallyOpen = false }) {
|
|
654
|
-
const scope = (0,
|
|
755
|
+
const scope = (0, import_react5.useSyncExternalStore)(
|
|
655
756
|
activation.subscribe,
|
|
656
757
|
activation.getSettingsSnapshot,
|
|
657
758
|
activation.getSettingsSnapshot
|
|
658
759
|
);
|
|
659
760
|
const settings = activation.getSnapshot();
|
|
660
|
-
const [open, setOpen] = (0,
|
|
661
|
-
const [busy, setBusy] = (0,
|
|
662
|
-
const [error, setError] = (0,
|
|
761
|
+
const [open, setOpen] = (0, import_react5.useState)(initiallyOpen);
|
|
762
|
+
const [busy, setBusy] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
|
|
763
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
663
764
|
if (scope.status === "unavailable") return null;
|
|
664
765
|
const update = async (id, enabled) => {
|
|
665
766
|
setBusy((current) => /* @__PURE__ */ new Set([...current, id]));
|
|
@@ -676,9 +777,9 @@ function PatchConfigCard({ activation, initiallyOpen = false }) {
|
|
|
676
777
|
});
|
|
677
778
|
}
|
|
678
779
|
};
|
|
679
|
-
return /* @__PURE__ */ (0,
|
|
680
|
-
/* @__PURE__ */ (0,
|
|
681
|
-
/* @__PURE__ */ (0,
|
|
780
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("li", { className: `dshmore-config-card${open ? " dshmore-config-card-open" : ""}`, children: [
|
|
781
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: styles }),
|
|
782
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
682
783
|
"button",
|
|
683
784
|
{
|
|
684
785
|
type: "button",
|
|
@@ -689,23 +790,23 @@ function PatchConfigCard({ activation, initiallyOpen = false }) {
|
|
|
689
790
|
setOpen((current) => !current);
|
|
690
791
|
},
|
|
691
792
|
children: [
|
|
692
|
-
/* @__PURE__ */ (0,
|
|
693
|
-
/* @__PURE__ */ (0,
|
|
694
|
-
/* @__PURE__ */ (0,
|
|
793
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "dshmore-config-heading-copy", children: [
|
|
794
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: "DSH More" }),
|
|
795
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u52A8\u6001\u5F00\u542F\u6216\u5173\u95ED\u72EC\u7ACB\u8865\u4E01\uFF0C\u9ED8\u8BA4\u5168\u90E8\u5F00\u542F\u3002" })
|
|
695
796
|
] }),
|
|
696
|
-
/* @__PURE__ */ (0,
|
|
797
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_dsh_client_ui_primitives5.IconChevronDownOutline14, { className: `dshmore-config-chevron${open ? " dshmore-config-chevron-open" : ""}` })
|
|
697
798
|
]
|
|
698
799
|
}
|
|
699
800
|
),
|
|
700
|
-
open && /* @__PURE__ */ (0,
|
|
701
|
-
/* @__PURE__ */ (0,
|
|
801
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dshmore-config-body", children: [
|
|
802
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dshmore-config-list", children: PATCH_CATALOG.map((patch) => {
|
|
702
803
|
const waiting = busy.has(patch.id);
|
|
703
|
-
return /* @__PURE__ */ (0,
|
|
704
|
-
/* @__PURE__ */ (0,
|
|
705
|
-
/* @__PURE__ */ (0,
|
|
706
|
-
/* @__PURE__ */ (0,
|
|
804
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "dshmore-config-row", children: [
|
|
805
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "dshmore-config-copy", children: [
|
|
806
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: patch.name }),
|
|
807
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: patch.description })
|
|
707
808
|
] }),
|
|
708
|
-
/* @__PURE__ */ (0,
|
|
809
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
709
810
|
"input",
|
|
710
811
|
{
|
|
711
812
|
type: "checkbox",
|
|
@@ -720,9 +821,9 @@ function PatchConfigCard({ activation, initiallyOpen = false }) {
|
|
|
720
821
|
)
|
|
721
822
|
] }, patch.id);
|
|
722
823
|
}) }),
|
|
723
|
-
scope.status === "loading" && /* @__PURE__ */ (0,
|
|
724
|
-
scope.status === "ready" && !scope.writable && /* @__PURE__ */ (0,
|
|
725
|
-
error !== null && /* @__PURE__ */ (0,
|
|
824
|
+
scope.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "dshmore-config-note", children: "\u6B63\u5728\u8BFB\u53D6\u63D2\u4EF6\u914D\u7F6E\u2026" }),
|
|
825
|
+
scope.status === "ready" && !scope.writable && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "dshmore-config-note", children: "\u5F53\u524D Settings \u5B58\u50A8\u4E3A\u53EA\u8BFB\u3002" }),
|
|
826
|
+
error !== null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "dshmore-config-error", children: error })
|
|
726
827
|
] })
|
|
727
828
|
] });
|
|
728
829
|
}
|
|
@@ -732,14 +833,14 @@ function installPatchConfigCard(ctx, activation) {
|
|
|
732
833
|
key: PATCH_SETTINGS_NAMESPACE,
|
|
733
834
|
priority: 90,
|
|
734
835
|
registrant: PLUGIN_NAME
|
|
735
|
-
}, (props) => /* @__PURE__ */ (0,
|
|
836
|
+
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PatchConfigCard, { ...props, activation })));
|
|
736
837
|
}
|
|
737
838
|
|
|
738
839
|
// src/kernel/message-visibility.ts
|
|
739
840
|
var MESSAGE_VISIBILITY_PROJECTION_KEY = "dshMoreMessageDelete";
|
|
740
841
|
|
|
741
842
|
// src/platform/dsh/client/message-targets.tsx
|
|
742
|
-
var
|
|
843
|
+
var import_react6 = require("react");
|
|
743
844
|
|
|
744
845
|
// src/platform/dsh/client/message-content.ts
|
|
745
846
|
function contentText(content) {
|
|
@@ -845,8 +946,8 @@ function trajectorySourceSeq(row) {
|
|
|
845
946
|
}
|
|
846
947
|
function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys) {
|
|
847
948
|
const snapshot = props.useSession((state) => state);
|
|
848
|
-
const [targets, setTargets] = (0,
|
|
849
|
-
(0,
|
|
949
|
+
const [targets, setTargets] = (0, import_react6.useState)([]);
|
|
950
|
+
(0, import_react6.useLayoutEffect)(() => {
|
|
850
951
|
let frame = null;
|
|
851
952
|
const scan = () => {
|
|
852
953
|
frame = null;
|
|
@@ -858,13 +959,11 @@ function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys) {
|
|
|
858
959
|
const seq = presentationSeq(node);
|
|
859
960
|
if (seq !== void 0 && hiddenSeqs.has(seq)) {
|
|
860
961
|
row.dataset.dshmoreHidden = "";
|
|
861
|
-
row.style.display = "none";
|
|
862
962
|
row.querySelector("[data-dshmore-message-actions]")?.remove();
|
|
863
963
|
continue;
|
|
864
964
|
}
|
|
865
965
|
if (row.dataset.dshmoreHidden !== void 0) {
|
|
866
966
|
delete row.dataset.dshmoreHidden;
|
|
867
|
-
row.style.removeProperty("display");
|
|
868
967
|
}
|
|
869
968
|
const info = messageNodeInfo(node);
|
|
870
969
|
if (info !== null) next.push({ key, ...info, host: hostForRow(row, info.kind) });
|
|
@@ -874,10 +973,8 @@ function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys) {
|
|
|
874
973
|
const key = trajectoryRecordKey(row);
|
|
875
974
|
if (seq !== void 0 && hiddenSeqs.has(seq) || key !== void 0 && hiddenTrajectoryKeys.has(key)) {
|
|
876
975
|
row.dataset.dshmoreHidden = "";
|
|
877
|
-
row.style.display = "none";
|
|
878
976
|
} else if (row.dataset.dshmoreHidden !== void 0) {
|
|
879
977
|
delete row.dataset.dshmoreHidden;
|
|
880
|
-
row.style.removeProperty("display");
|
|
881
978
|
}
|
|
882
979
|
}
|
|
883
980
|
setTargets((current) => sameTargets(current, next) ? current : next);
|
|
@@ -893,7 +990,6 @@ function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys) {
|
|
|
893
990
|
if (frame !== null) window.cancelAnimationFrame(frame);
|
|
894
991
|
for (const row of document.querySelectorAll("[data-dshmore-hidden]")) {
|
|
895
992
|
delete row.dataset.dshmoreHidden;
|
|
896
|
-
row.style.removeProperty("display");
|
|
897
993
|
}
|
|
898
994
|
for (const host of document.querySelectorAll("[data-dshmore-message-actions]")) host.remove();
|
|
899
995
|
};
|
|
@@ -902,14 +998,14 @@ function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys) {
|
|
|
902
998
|
}
|
|
903
999
|
|
|
904
1000
|
// src/client/index.tsx
|
|
905
|
-
var
|
|
1001
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
906
1002
|
var inject = ["slots", "sessions", "workspaces", "settingsScope"];
|
|
907
1003
|
function MessageActionPatchController({ patch, props, targets, enabled }) {
|
|
908
1004
|
const actions = patch.useActions(props, enabled);
|
|
909
1005
|
const style = { "--dshmore-action-order": patch.order };
|
|
910
|
-
return /* @__PURE__ */ (0,
|
|
1006
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
911
1007
|
enabled && targets.map((target) => (0, import_react_dom.createPortal)(
|
|
912
|
-
/* @__PURE__ */ (0,
|
|
1008
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dshmore-action-contribution", style, children: actions.renderAction(target) }),
|
|
913
1009
|
target.host,
|
|
914
1010
|
`${patch.id}:${target.key}`
|
|
915
1011
|
)),
|
|
@@ -917,14 +1013,14 @@ function MessageActionPatchController({ patch, props, targets, enabled }) {
|
|
|
917
1013
|
] });
|
|
918
1014
|
}
|
|
919
1015
|
function MessageActionsController(props) {
|
|
920
|
-
const settings = (0,
|
|
1016
|
+
const settings = (0, import_react7.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot);
|
|
921
1017
|
const projected = props.useProjection(MESSAGE_VISIBILITY_PROJECTION_KEY);
|
|
922
|
-
const hiddenSeqs = (0,
|
|
923
|
-
const hiddenTrajectoryKeys = (0,
|
|
1018
|
+
const hiddenSeqs = (0, import_react7.useMemo)(() => new Set(projected?.deletedSeqs ?? []), [projected]);
|
|
1019
|
+
const hiddenTrajectoryKeys = (0, import_react7.useMemo)(() => new Set(projected?.hiddenTrajectoryKeys ?? []), [projected]);
|
|
924
1020
|
const targets = useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys);
|
|
925
|
-
return /* @__PURE__ */ (0,
|
|
926
|
-
/* @__PURE__ */ (0,
|
|
927
|
-
MESSAGE_ACTION_PATCHES.map((patch) => /* @__PURE__ */ (0,
|
|
1021
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1022
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: styles }),
|
|
1023
|
+
MESSAGE_ACTION_PATCHES.map((patch) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
928
1024
|
MessageActionPatchController,
|
|
929
1025
|
{
|
|
930
1026
|
patch,
|
|
@@ -948,7 +1044,7 @@ function apply(ctx) {
|
|
|
948
1044
|
id: `${PLUGIN_NAME}-message-actions`,
|
|
949
1045
|
order: 90,
|
|
950
1046
|
registrant: PLUGIN_NAME
|
|
951
|
-
}, (props) => /* @__PURE__ */ (0,
|
|
1047
|
+
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageActionsController, { ...props, ctx, activation })));
|
|
952
1048
|
for (const patch of CLIENT_PATCHES) patch.install(ctx, activation);
|
|
953
1049
|
}
|
|
954
1050
|
return module.exports; } });
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,8 @@ var DshMoreError = class extends Error {
|
|
|
16
16
|
this.status = status;
|
|
17
17
|
this.name = "DshMoreError";
|
|
18
18
|
}
|
|
19
|
+
code;
|
|
20
|
+
status;
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
// src/platform/dsh/host/agent-session.ts
|
|
@@ -76,13 +78,20 @@ function verifyConfirmationToken(secret, token, expected, now = Date.now()) {
|
|
|
76
78
|
|
|
77
79
|
// src/platform/dsh/host/wire.ts
|
|
78
80
|
var MAX_BODY_BYTES = 64 * 1024;
|
|
81
|
+
function requireJsonContentType(req) {
|
|
82
|
+
const value = req.headers["content-type"];
|
|
83
|
+
const contentType = (Array.isArray(value) ? value[0] : value)?.split(";", 1)[0]?.trim().toLowerCase();
|
|
84
|
+
if (contentType !== "application/json") {
|
|
85
|
+
throw new DshMoreError("unsupported-media-type", "\u8BF7\u6C42\u5FC5\u987B\u4F7F\u7528 application/json\u3002", 415);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
79
88
|
async function readJson(req) {
|
|
80
89
|
const chunks = [];
|
|
81
90
|
let size = 0;
|
|
82
91
|
for await (const chunk of req) {
|
|
83
92
|
const bytes = Buffer.from(chunk);
|
|
84
93
|
size += bytes.length;
|
|
85
|
-
if (size > MAX_BODY_BYTES) throw new DshMoreError("
|
|
94
|
+
if (size > MAX_BODY_BYTES) throw new DshMoreError("payload-too-large", "\u8BF7\u6C42\u5185\u5BB9\u8FC7\u5927\u3002", 413);
|
|
86
95
|
chunks.push(bytes);
|
|
87
96
|
}
|
|
88
97
|
const text = Buffer.concat(chunks).toString("utf8");
|
|
@@ -119,8 +128,7 @@ function writeError(res, error) {
|
|
|
119
128
|
write(res, error.status, { ok: false, error: { code: error.code, message: error.message } });
|
|
120
129
|
return;
|
|
121
130
|
}
|
|
122
|
-
|
|
123
|
-
write(res, 500, { ok: false, error: { code: "internal", message } });
|
|
131
|
+
write(res, 500, { ok: false, error: { code: "internal", message: "\u5185\u90E8\u9519\u8BEF\uFF0C\u8BF7\u91CD\u8BD5\u6216\u67E5\u770B DSH \u65E5\u5FD7\u3002" } });
|
|
124
132
|
}
|
|
125
133
|
|
|
126
134
|
// src/patches/message-edit/shared.ts
|
|
@@ -180,6 +188,25 @@ function replaySeedRuntimeContext(agentCtx, seed) {
|
|
|
180
188
|
}));
|
|
181
189
|
}
|
|
182
190
|
|
|
191
|
+
// src/platform/dsh/host/continuation.ts
|
|
192
|
+
async function rollbackFailedContinuation(child, workspace, attached, cause) {
|
|
193
|
+
const errors = [cause];
|
|
194
|
+
if (attached && workspace !== void 0) {
|
|
195
|
+
try {
|
|
196
|
+
await workspace.detachSession(child.agent.id);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
errors.push(error);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
await child.dispose();
|
|
203
|
+
} catch (error) {
|
|
204
|
+
errors.push(error);
|
|
205
|
+
}
|
|
206
|
+
if (errors.length === 1) throw cause;
|
|
207
|
+
throw new AggregateError(errors, "continuation failed and cleanup was incomplete");
|
|
208
|
+
}
|
|
209
|
+
|
|
183
210
|
// src/patches/message-edit/host/edit-continuation.ts
|
|
184
211
|
function textDigest(text) {
|
|
185
212
|
return createHash("sha256").update(text).digest("hex");
|
|
@@ -244,20 +271,23 @@ async function createEditedContinuation(ctx, sourceAgent, cut, editedText, child
|
|
|
244
271
|
releaseRuntimeContextReplay = replaySeedRuntimeContext(agentCtx, seed);
|
|
245
272
|
}
|
|
246
273
|
});
|
|
274
|
+
const workspace = ctx.workspaceRegistry.list().find((candidate) => candidate.sessionIds.includes(source.id));
|
|
275
|
+
let attached = false;
|
|
247
276
|
try {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
277
|
+
if (workspace !== void 0) {
|
|
278
|
+
await workspace.attachSession(childId);
|
|
279
|
+
attached = true;
|
|
280
|
+
}
|
|
251
281
|
child.agent.followup(createUserMessage({
|
|
252
282
|
content: editedContent(original.data.content, editedText.trim()),
|
|
253
283
|
source: { kind: "user" }
|
|
254
284
|
}));
|
|
255
285
|
void child.agent.whenIdle().then(releaseReplay, releaseReplay);
|
|
286
|
+
await ctx.workspaceRegistry.archiveSession(source.id);
|
|
256
287
|
return { sessionId: childId };
|
|
257
288
|
} catch (error) {
|
|
258
289
|
releaseReplay();
|
|
259
|
-
|
|
260
|
-
throw error;
|
|
290
|
+
return rollbackFailedContinuation(child, workspace, attached, error);
|
|
261
291
|
}
|
|
262
292
|
}
|
|
263
293
|
|
|
@@ -547,15 +577,18 @@ async function createDeletedContinuation(ctx, sourceAgent, selection, childId =
|
|
|
547
577
|
});
|
|
548
578
|
}
|
|
549
579
|
});
|
|
580
|
+
const workspace = ctx.workspaceRegistry.list().find((candidate) => candidate.sessionIds.includes(source.id));
|
|
581
|
+
let attached = false;
|
|
550
582
|
try {
|
|
551
|
-
|
|
552
|
-
|
|
583
|
+
if (workspace !== void 0) {
|
|
584
|
+
await workspace.attachSession(childId);
|
|
585
|
+
attached = true;
|
|
586
|
+
}
|
|
553
587
|
await ctx.workspaceRegistry.archiveSession(source.id);
|
|
554
588
|
return { sessionId: childId };
|
|
555
589
|
} catch (error) {
|
|
556
590
|
releaseReplay();
|
|
557
|
-
|
|
558
|
-
throw error;
|
|
591
|
+
return rollbackFailedContinuation(child, workspace, attached, error);
|
|
559
592
|
}
|
|
560
593
|
}
|
|
561
594
|
|
|
@@ -618,7 +651,7 @@ var hostPatch2 = {
|
|
|
618
651
|
var SESSION_DELETE_PATCH_ID = "session-delete";
|
|
619
652
|
|
|
620
653
|
// src/patches/session-delete/host/live-session-handles.ts
|
|
621
|
-
var TRACKER = Symbol.for("dsh-more.session-delete.live-session-handles");
|
|
654
|
+
var TRACKER = /* @__PURE__ */ Symbol.for("dsh-more.session-delete.live-session-handles");
|
|
622
655
|
function trackedHandle(tracker, handle) {
|
|
623
656
|
let disposed = false;
|
|
624
657
|
const wrapped = {
|
|
@@ -667,7 +700,7 @@ function getLiveSessionHandle(ctx, sessionId) {
|
|
|
667
700
|
|
|
668
701
|
// src/patches/session-delete/host/session-deletion.ts
|
|
669
702
|
import { rm, stat } from "node:fs/promises";
|
|
670
|
-
import { dirname, isAbsolute
|
|
703
|
+
import { basename, dirname, isAbsolute } from "node:path";
|
|
671
704
|
import { SessionId as SessionId6 } from "@deepseek-ai/dsh-session";
|
|
672
705
|
function detachInternal(registry, sessionId, label) {
|
|
673
706
|
const internal = registry;
|
|
@@ -704,14 +737,16 @@ async function unloadLiveSession(ctx, sessionId) {
|
|
|
704
737
|
throw new DshMoreError("internal", "\u4F1A\u8BDD\u505C\u6B62\u540E\u4ECD\u5904\u4E8E\u52A0\u8F7D\u72B6\u6001\uFF0C\u672A\u6267\u884C\u78C1\u76D8\u5220\u9664\u3002", 500);
|
|
705
738
|
}
|
|
706
739
|
}
|
|
707
|
-
function
|
|
740
|
+
function sessionDirectoryFromLocation(locationPath) {
|
|
708
741
|
if (!isAbsolute(locationPath)) {
|
|
709
742
|
throw new DshMoreError("internal", "\u5F53\u524D\u6301\u4E45\u5316\u540E\u7AEF\u6CA1\u6709\u8FD4\u56DE\u7EDD\u5BF9\u4F1A\u8BDD\u8DEF\u5F84\u3002", 409);
|
|
710
743
|
}
|
|
744
|
+
const artifact = basename(locationPath);
|
|
745
|
+
if (artifact !== "session.jsonl" && artifact !== "session.jsonl.zstd") {
|
|
746
|
+
throw new DshMoreError("internal", "\u6301\u4E45\u5316\u540E\u7AEF\u8FD4\u56DE\u4E86\u672A\u77E5\u7684\u4F1A\u8BDD\u6587\u4EF6\u5E03\u5C40\u3002", 500);
|
|
747
|
+
}
|
|
711
748
|
const sessionDir = dirname(locationPath);
|
|
712
|
-
|
|
713
|
-
const withinRoot = relative(sessionsRoot, sessionDir);
|
|
714
|
-
if (withinRoot === "" || withinRoot.startsWith("..") || isAbsolute(withinRoot)) {
|
|
749
|
+
if (sessionDir === dirname(sessionDir)) {
|
|
715
750
|
throw new DshMoreError("internal", "\u6301\u4E45\u5316\u540E\u7AEF\u8FD4\u56DE\u4E86\u4E0D\u5B89\u5168\u7684\u4F1A\u8BDD\u8DEF\u5F84\u3002", 500);
|
|
716
751
|
}
|
|
717
752
|
return sessionDir;
|
|
@@ -728,7 +763,7 @@ async function deleteSessionPermanently(ctx, rawSessionId) {
|
|
|
728
763
|
if (location.kind !== "jsonl") {
|
|
729
764
|
throw new DshMoreError("internal", `\u6301\u4E45\u5316\u540E\u7AEF ${location.kind} \u4E0D\u652F\u6301\u5B89\u5168\u7684\u9010\u76EE\u5F55\u5220\u9664\u3002`, 409);
|
|
730
765
|
}
|
|
731
|
-
const sessionDir =
|
|
766
|
+
const sessionDir = sessionDirectoryFromLocation(location.path);
|
|
732
767
|
await unloadLiveSession(ctx, sessionId);
|
|
733
768
|
const info = await stat(sessionDir).catch(() => void 0);
|
|
734
769
|
if (info !== void 0) {
|
|
@@ -737,7 +772,7 @@ async function deleteSessionPermanently(ctx, rawSessionId) {
|
|
|
737
772
|
}
|
|
738
773
|
const workspaces = ctx.workspaceRegistry.list().filter((candidate) => candidate.sessionIds.includes(sessionId));
|
|
739
774
|
await Promise.all(workspaces.map(async (workspace) => workspace.detachSession(sessionId)));
|
|
740
|
-
return { sessionId
|
|
775
|
+
return { sessionId };
|
|
741
776
|
}
|
|
742
777
|
|
|
743
778
|
// src/patches/session-delete/host/index.ts
|
|
@@ -755,14 +790,177 @@ var hostPatch3 = {
|
|
|
755
790
|
})
|
|
756
791
|
};
|
|
757
792
|
|
|
793
|
+
// src/patches/conversation-markdown-export/shared.ts
|
|
794
|
+
var CONVERSATION_MARKDOWN_EXPORT_PATCH_ID = "conversation-markdown-export";
|
|
795
|
+
|
|
796
|
+
// src/patches/conversation-markdown-export/host/transcript.ts
|
|
797
|
+
function singleLine(value) {
|
|
798
|
+
return value.replace(/[\u0000-\u001f\u007f]+/g, " ").replace(/\s+/g, " ").trim();
|
|
799
|
+
}
|
|
800
|
+
function isoTime(value) {
|
|
801
|
+
const date = new Date(value);
|
|
802
|
+
return Number.isFinite(date.getTime()) ? date.toISOString() : "\u672A\u77E5\u65F6\u95F4";
|
|
803
|
+
}
|
|
804
|
+
function codeFence(value, language = "") {
|
|
805
|
+
const longest = Math.max(0, ...[...value.matchAll(/`+/g)].map((match) => match[0].length));
|
|
806
|
+
const fence = "`".repeat(Math.max(3, longest + 1));
|
|
807
|
+
return `${fence}${language}
|
|
808
|
+
${value}
|
|
809
|
+
${fence}`;
|
|
810
|
+
}
|
|
811
|
+
function jsonBlock(value) {
|
|
812
|
+
let serialized;
|
|
813
|
+
try {
|
|
814
|
+
serialized = JSON.stringify(value, null, 2) ?? String(value);
|
|
815
|
+
} catch {
|
|
816
|
+
serialized = String(value);
|
|
817
|
+
}
|
|
818
|
+
return codeFence(serialized, "json");
|
|
819
|
+
}
|
|
820
|
+
function imageDescription(block) {
|
|
821
|
+
const attachment = block.attachment;
|
|
822
|
+
const name2 = singleLine(attachment.name ?? "\u56FE\u7247") || "\u56FE\u7247";
|
|
823
|
+
return `[${name2}\uFF1B\u9644\u4EF6 ID\uFF1A${String(attachment.attachmentId)}\uFF1B${attachment.mediaType}\uFF1B${String(attachment.width)}\xD7${String(attachment.height)}\uFF1B${String(attachment.bytes)} \u5B57\u8282]`;
|
|
824
|
+
}
|
|
825
|
+
function contentBlockMarkdown(block) {
|
|
826
|
+
switch (block.type) {
|
|
827
|
+
case "text":
|
|
828
|
+
return block.text;
|
|
829
|
+
case "reasoning":
|
|
830
|
+
return `<details>
|
|
831
|
+
<summary>\u601D\u8003\u8FC7\u7A0B</summary>
|
|
832
|
+
|
|
833
|
+
${block.text}
|
|
834
|
+
|
|
835
|
+
</details>`;
|
|
836
|
+
case "image":
|
|
837
|
+
return imageDescription(block);
|
|
838
|
+
case "tool-call": {
|
|
839
|
+
const name2 = singleLine(block.name) || "\u672A\u547D\u540D\u5DE5\u5177";
|
|
840
|
+
return `### \u5DE5\u5177\u8C03\u7528\uFF1A${name2}
|
|
841
|
+
|
|
842
|
+
${codeFence(block.arguments, "json")}`;
|
|
843
|
+
}
|
|
844
|
+
case "tool-result": {
|
|
845
|
+
const status = block.isError === true ? "\uFF08\u5931\u8D25\uFF09" : "";
|
|
846
|
+
return `### \u5DE5\u5177\u7ED3\u679C${status}
|
|
847
|
+
|
|
848
|
+
${contentMarkdown(block.content)}`;
|
|
849
|
+
}
|
|
850
|
+
default:
|
|
851
|
+
return `### \u672A\u8BC6\u522B\u5185\u5BB9
|
|
852
|
+
|
|
853
|
+
${jsonBlock(block)}`;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
function contentMarkdown(content) {
|
|
857
|
+
if (content.length === 0) return "\uFF08\u7A7A\u6D88\u606F\uFF09";
|
|
858
|
+
return content.map(contentBlockMarkdown).filter((part) => part !== "").join("\n\n") || "\uFF08\u7A7A\u6D88\u606F\uFF09";
|
|
859
|
+
}
|
|
860
|
+
function messageSection(label, time, body) {
|
|
861
|
+
return `## ${singleLine(label)}
|
|
862
|
+
|
|
863
|
+
> \u65F6\u95F4\uFF1A${isoTime(time)}
|
|
864
|
+
|
|
865
|
+
${body}`;
|
|
866
|
+
}
|
|
867
|
+
function sourceLabel(event) {
|
|
868
|
+
const source = event.data.source;
|
|
869
|
+
if (source.kind === "user") return "\u7528\u6237";
|
|
870
|
+
if (source.kind === "plugin") return `\u4E0A\u4E0B\u6587 \xB7 ${singleLine(source.plugin) || "\u63D2\u4EF6"}`;
|
|
871
|
+
return "\u4E0A\u4E0B\u6587";
|
|
872
|
+
}
|
|
873
|
+
function toolCallIds(content) {
|
|
874
|
+
return content.filter((block) => block.type === "tool-call").map((block) => String(block.id));
|
|
875
|
+
}
|
|
876
|
+
function conversationMarkdown(events, options) {
|
|
877
|
+
const sections = [];
|
|
878
|
+
const renderedCalls = /* @__PURE__ */ new Set();
|
|
879
|
+
const callNames = /* @__PURE__ */ new Map();
|
|
880
|
+
for (const event of events) {
|
|
881
|
+
if (event.type === "user/message") {
|
|
882
|
+
sections.push(messageSection(sourceLabel(event), event.time, contentMarkdown(event.data.content)));
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
if (event.type === "assistant/message") {
|
|
886
|
+
for (const callId of toolCallIds(event.data.message.content)) renderedCalls.add(callId);
|
|
887
|
+
sections.push(messageSection("\u52A9\u624B", event.time, contentMarkdown(event.data.message.content)));
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
if (event.type === "tool/call") {
|
|
891
|
+
const callId = String(event.data.callId);
|
|
892
|
+
callNames.set(callId, event.data.name);
|
|
893
|
+
if (!renderedCalls.has(callId)) {
|
|
894
|
+
sections.push(messageSection(
|
|
895
|
+
`\u5DE5\u5177\u8C03\u7528 \xB7 ${singleLine(event.data.name) || "\u672A\u547D\u540D\u5DE5\u5177"}`,
|
|
896
|
+
event.time,
|
|
897
|
+
codeFence(event.data.arguments, "json")
|
|
898
|
+
));
|
|
899
|
+
renderedCalls.add(callId);
|
|
900
|
+
}
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
if (event.type === "tool/result") {
|
|
904
|
+
const callId = String(event.data.message.source.callId);
|
|
905
|
+
const block = event.data.message.content[0];
|
|
906
|
+
const failed = block?.type === "tool-result" && block.isError === true;
|
|
907
|
+
const resultContent = block?.type === "tool-result" ? block.content : event.data.message.content;
|
|
908
|
+
const name2 = singleLine(callNames.get(callId) ?? callId) || "\u672A\u547D\u540D\u5DE5\u5177";
|
|
909
|
+
sections.push(messageSection(
|
|
910
|
+
`\u5DE5\u5177\u7ED3\u679C \xB7 ${name2}${failed ? "\uFF08\u5931\u8D25\uFF09" : ""}`,
|
|
911
|
+
event.time,
|
|
912
|
+
contentMarkdown(resultContent)
|
|
913
|
+
));
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
const title = singleLine(options.title) || "\u672A\u547D\u540D\u4F1A\u8BDD";
|
|
917
|
+
const exportedAt = isoTime(options.exportedAt ?? Date.now());
|
|
918
|
+
const header2 = [
|
|
919
|
+
`# ${title}`,
|
|
920
|
+
`- \u4F1A\u8BDD ID\uFF1A${singleLine(options.sessionId)}`,
|
|
921
|
+
`- \u5BFC\u51FA\u65F6\u95F4\uFF1A${exportedAt}`,
|
|
922
|
+
`- \u8BB0\u5F55\u6761\u76EE\uFF1A${String(sections.length)}`
|
|
923
|
+
].join("\n\n");
|
|
924
|
+
return {
|
|
925
|
+
markdown: sections.length === 0 ? `${header2}
|
|
926
|
+
|
|
927
|
+
\uFF08\u6682\u65E0\u804A\u5929\u8BB0\u5F55\uFF09
|
|
928
|
+
` : `${header2}
|
|
929
|
+
|
|
930
|
+
---
|
|
931
|
+
|
|
932
|
+
${sections.join("\n\n---\n\n")}
|
|
933
|
+
`,
|
|
934
|
+
entryCount: sections.length
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// src/patches/conversation-markdown-export/host/index.ts
|
|
939
|
+
var MAX_TITLE_LENGTH = 500;
|
|
940
|
+
function render(ctx, payload) {
|
|
941
|
+
const sessionId = requireString(payload, "sessionId");
|
|
942
|
+
const title = requireString(payload, "title");
|
|
943
|
+
if ([...title].length > MAX_TITLE_LENGTH) throw new DshMoreError("bad-request", "\u4F1A\u8BDD\u6807\u9898\u8FC7\u957F\u3002");
|
|
944
|
+
const session = requireLiveSession(ctx, sessionId);
|
|
945
|
+
requireIdleAgent(ctx, sessionId, session);
|
|
946
|
+
return conversationMarkdown(session.events, { title, sessionId });
|
|
947
|
+
}
|
|
948
|
+
var hostPatch4 = {
|
|
949
|
+
id: CONVERSATION_MARKDOWN_EXPORT_PATCH_ID,
|
|
950
|
+
routes: ({ ctx }) => ({
|
|
951
|
+
render: (payload) => render(ctx, payload)
|
|
952
|
+
})
|
|
953
|
+
};
|
|
954
|
+
|
|
758
955
|
// src/generated/host-registry.ts
|
|
759
|
-
var HOST_PATCHES = [hostPatch, hostPatch2, hostPatch3];
|
|
956
|
+
var HOST_PATCHES = [hostPatch, hostPatch2, hostPatch3, hostPatch4];
|
|
760
957
|
|
|
761
958
|
// src/generated/patch-catalog.ts
|
|
762
959
|
var DEFAULT_PATCH_SETTINGS = {
|
|
763
960
|
"message-edit": true,
|
|
764
961
|
"message-delete": true,
|
|
765
|
-
"session-delete": true
|
|
962
|
+
"session-delete": true,
|
|
963
|
+
"conversation-markdown-export": true
|
|
766
964
|
};
|
|
767
965
|
|
|
768
966
|
// src/kernel/host/activation.ts
|
|
@@ -772,6 +970,7 @@ var HostPatchActivation = class {
|
|
|
772
970
|
this.ctx = ctx;
|
|
773
971
|
this.byId = new Map(patches.map((patch) => [patch.id, patch]));
|
|
774
972
|
}
|
|
973
|
+
ctx;
|
|
775
974
|
byId;
|
|
776
975
|
disposers = /* @__PURE__ */ new Map();
|
|
777
976
|
settings = { ...DEFAULT_PATCH_SETTINGS };
|
|
@@ -805,7 +1004,8 @@ import z2 from "@deepseek-ai/schemastery";
|
|
|
805
1004
|
var PatchSettingsSchema = z2.object({
|
|
806
1005
|
"message-edit": z2.boolean().default(true),
|
|
807
1006
|
"message-delete": z2.boolean().default(true),
|
|
808
|
-
"session-delete": z2.boolean().default(true)
|
|
1007
|
+
"session-delete": z2.boolean().default(true),
|
|
1008
|
+
"conversation-markdown-export": z2.boolean().default(true)
|
|
809
1009
|
});
|
|
810
1010
|
|
|
811
1011
|
// src/kernel/host/settings.ts
|
|
@@ -878,6 +1078,7 @@ function collectRoutes(patches, runtime) {
|
|
|
878
1078
|
function registerPatchApi(ctx, patches, confirmationSecret, isEnabled) {
|
|
879
1079
|
const host = ctx;
|
|
880
1080
|
const routes = collectRoutes(patches, { ctx, confirmationSecret });
|
|
1081
|
+
const logger = ctx.logger(PLUGIN_NAME);
|
|
881
1082
|
ctx.effect(() => ctx.webServer.register({
|
|
882
1083
|
kind: "prefix",
|
|
883
1084
|
path: PLUGIN_API_PREFIX,
|
|
@@ -903,8 +1104,13 @@ function registerPatchApi(ctx, patches, confirmationSecret, isEnabled) {
|
|
|
903
1104
|
return;
|
|
904
1105
|
}
|
|
905
1106
|
try {
|
|
1107
|
+
requireJsonContentType(req);
|
|
906
1108
|
writeOk(res, await registered.handler(await readJson(req)));
|
|
907
1109
|
} catch (error) {
|
|
1110
|
+
if (!(error instanceof DshMoreError) || error.status >= 500) {
|
|
1111
|
+
const detail = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
1112
|
+
logger.error("patch API %s failed: %s", route, detail);
|
|
1113
|
+
}
|
|
908
1114
|
writeError(res, error);
|
|
909
1115
|
}
|
|
910
1116
|
}
|
|
@@ -3,8 +3,10 @@ export declare const PatchSettingsSchema: z<Schemastery.ObjectS<{
|
|
|
3
3
|
"message-edit": z<boolean, boolean>;
|
|
4
4
|
"message-delete": z<boolean, boolean>;
|
|
5
5
|
"session-delete": z<boolean, boolean>;
|
|
6
|
+
"conversation-markdown-export": z<boolean, boolean>;
|
|
6
7
|
}>, Schemastery.ObjectT<{
|
|
7
8
|
"message-edit": z<boolean, boolean>;
|
|
8
9
|
"message-delete": z<boolean, boolean>;
|
|
9
10
|
"session-delete": z<boolean, boolean>;
|
|
11
|
+
"conversation-markdown-export": z<boolean, boolean>;
|
|
10
12
|
}>>;
|
|
@@ -19,12 +19,20 @@ export declare const PATCH_CATALOG: readonly [{
|
|
|
19
19
|
readonly defaultEnabled: true;
|
|
20
20
|
readonly order: 300;
|
|
21
21
|
readonly clientKind: "standalone";
|
|
22
|
+
}, {
|
|
23
|
+
readonly id: "conversation-markdown-export";
|
|
24
|
+
readonly name: "导出完整聊天记录";
|
|
25
|
+
readonly description: "将当前会话的完整聊天记录下载为 Markdown 文件";
|
|
26
|
+
readonly defaultEnabled: true;
|
|
27
|
+
readonly order: 400;
|
|
28
|
+
readonly clientKind: "standalone";
|
|
22
29
|
}];
|
|
23
30
|
export type PatchId = typeof PATCH_CATALOG[number]['id'];
|
|
24
31
|
export interface PatchSettings {
|
|
25
32
|
"message-edit": boolean;
|
|
26
33
|
"message-delete": boolean;
|
|
27
34
|
"session-delete": boolean;
|
|
35
|
+
"conversation-markdown-export": boolean;
|
|
28
36
|
}
|
|
29
37
|
export declare const DEFAULT_PATCH_SETTINGS: PatchSettings;
|
|
30
38
|
export declare function decodePatchSettings(value: unknown): PatchSettings | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function markdownFilename(title: string, sessionId: string): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SessionEvent } from '@deepseek-ai/dsh-session';
|
|
2
|
+
import type { ConversationMarkdownExport } from '../shared.js';
|
|
3
|
+
export interface ConversationMarkdownOptions {
|
|
4
|
+
title: string;
|
|
5
|
+
sessionId: string;
|
|
6
|
+
exportedAt?: number;
|
|
7
|
+
}
|
|
8
|
+
/** Convert one immutable Session event snapshot into a complete Markdown transcript. */
|
|
9
|
+
export declare function conversationMarkdown(events: readonly SessionEvent[], options: ConversationMarkdownOptions): ConversationMarkdownExport;
|
|
@@ -2,5 +2,4 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
2
2
|
/** Stop/unload a Session, then recursively remove its exact persistence directory. */
|
|
3
3
|
export declare function deleteSessionPermanently(ctx: Context, rawSessionId: string): Promise<{
|
|
4
4
|
sessionId: string;
|
|
5
|
-
deletedPath: string;
|
|
6
5
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const styles = "\n.dshmore-inline-actions { display: inline-flex; align-items: center; gap: 10px; }\n.dshmore-inline-button { width: 28px; height: 28px; color: var(--dsw-alias-label-tertiary); cursor: pointer; background: transparent; border: 0; border-radius: 28px; display: inline-flex; align-items: center; justify-content: center; padding: 6px; }\n.dshmore-inline-button:hover { color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-interactive-bg-hover); }\n.dshmore-edit-modal { box-sizing: border-box; width: min(620px, calc(100vw - 32px)) !important; max-width: min(620px, calc(100vw - 32px)) !important; }\n.dshmore-dialog-body { box-sizing: border-box; display: grid; gap: 12px; width: 100%; min-width: 0; overflow: hidden; }\n.dshmore-message-preview { box-sizing: border-box; width: 100%; min-width: 0; max-height: 132px; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; padding: 12px; border-radius: 10px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-1); font-size: 13px; line-height: 1.55; }\n.dshmore-editor { box-sizing: border-box; display: block; width: 100%; min-width: 0; max-width: 100%; resize: vertical; min-height: 150px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 10px; outline: none; padding: 12px; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-1); font: inherit; line-height: 1.55; overflow-x: hidden; overflow-wrap: anywhere; word-break: break-word; }\n.dshmore-editor:focus { border-color: var(--dsw-alias-state-business-primary); }\n.dshmore-warning { padding: 10px 12px; border: 1px solid rgba(220,138,40,.35); border-radius: 10px; background: rgba(220,138,40,.09); color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 1.5; }\n.dshmore-danger { display: flex; gap: 8px; align-items: flex-start; padding: 10px 12px; border-radius: 10px; background: rgba(208,58,58,.10); color: var(--dsw-alias-state-error-primary, rgb(220,90,90)); font-size: 12px; line-height: 1.5; }\n.dshmore-error { padding: 10px 12px; border-radius: 10px; background: rgba(208,58,58,.10); color: var(--dsw-alias-state-error-primary, rgb(220,90,90)); font-size: 12px; line-height: 1.5; }\n@media (max-width: 620px) { .dshmore-edit-modal { width: calc(100vw - 24px) !important; max-width: calc(100vw - 24px) !important; } }\n.dshmore-action-contribution { display: inline-flex; align-items: center; order: var(--dshmore-action-order); }\n.dshmore-config-card { box-sizing: border-box; list-style: none; border: 1px solid var(--dsw-alias-border-l2); border-radius: 12px; background: var(--dsw-alias-bg-layer-3); overflow: hidden; transition: border-color .16s, background .16s; }\n.dshmore-config-card:hover { border-color: var(--dsw-alias-label-dimmed); }\n.dshmore-config-card-open { border-color: var(--dsw-alias-label-dimmed); background: var(--dsw-alias-bg-layer-2); }\n.dshmore-config-heading { appearance: none; box-sizing: border-box; width: 100%; color: inherit; font: inherit; text-align: left; cursor: pointer; background: transparent; border: 0; border-radius: 12px; display: flex; align-items: center; gap: 12px; padding: 14px 16px; }\n.dshmore-config-heading:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: -2px; }\n.dshmore-config-heading-copy { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 4px; }\n.dshmore-config-heading-copy strong { color: var(--dsw-alias-label-primary); font-size: 15px; line-height: 1.4; }\n.dshmore-config-heading-copy > span, .dshmore-config-copy span, .dshmore-config-note { color: var(--dsw-alias-label-tertiary); font-size: 13px; line-height: 1.5; }\n.dshmore-config-chevron { flex: none; color: var(--dsw-alias-label-tertiary); }\n.dshmore-config-chevron-open { transform: rotate(180deg); }\n.dshmore-config-body { margin: 0 16px; padding-bottom: 8px; border-top: 1px solid var(--dsw-alias-border-l2); }\n.dshmore-config-list { display: grid; }\n.dshmore-config-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 12px 0; cursor: pointer; }\n.dshmore-config-row + .dshmore-config-row { border-top: 1px solid var(--dsw-alias-border-l1); }\n.dshmore-config-row:hover { background: var(--dsw-alias-interactive-bg-hover); }\n.dshmore-config-copy { min-width: 0; display: grid; gap: 2px; }\n.dshmore-config-copy strong { color: var(--dsw-alias-label-primary); font-size: 13px; }\n.dshmore-config-row input { width: 34px; height: 18px; flex: none; cursor: pointer; accent-color: var(--dsw-alias-state-business-primary); }\n.dshmore-config-row input:disabled { cursor: default; opacity: .5; }\n.dshmore-config-note, .dshmore-config-error { margin: 0; padding: 10px 0; border-top: 1px solid var(--dsw-alias-border-l1); }\n.dshmore-config-error { color: var(--dsw-alias-state-error-primary, rgb(220,90,90)); font-size: 12px; }\n@media (prefers-reduced-motion: no-preference) { .dshmore-config-chevron { transition: transform .16s; } }\n";
|
|
1
|
+
export declare const styles = "\n[data-dshmore-hidden] { display: none !important; }\n.dshmore-inline-actions { display: inline-flex; align-items: center; gap: 10px; }\n.dshmore-inline-button { width: 28px; height: 28px; color: var(--dsw-alias-label-tertiary); cursor: pointer; background: transparent; border: 0; border-radius: 28px; display: inline-flex; align-items: center; justify-content: center; padding: 6px; }\n.dshmore-inline-button:hover { color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-interactive-bg-hover); }\n.dshmore-edit-modal { box-sizing: border-box; width: min(620px, calc(100vw - 32px)) !important; max-width: min(620px, calc(100vw - 32px)) !important; }\n.dshmore-dialog-body { box-sizing: border-box; display: grid; gap: 12px; width: 100%; min-width: 0; overflow: hidden; }\n.dshmore-message-preview { box-sizing: border-box; width: 100%; min-width: 0; max-height: 132px; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; padding: 12px; border-radius: 10px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-1); font-size: 13px; line-height: 1.55; }\n.dshmore-editor { box-sizing: border-box; display: block; width: 100%; min-width: 0; max-width: 100%; resize: vertical; min-height: 150px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 10px; outline: none; padding: 12px; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-1); font: inherit; line-height: 1.55; overflow-x: hidden; overflow-wrap: anywhere; word-break: break-word; }\n.dshmore-editor:focus { border-color: var(--dsw-alias-state-business-primary); }\n.dshmore-warning { padding: 10px 12px; border: 1px solid rgba(220,138,40,.35); border-radius: 10px; background: rgba(220,138,40,.09); color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 1.5; }\n.dshmore-danger { display: flex; gap: 8px; align-items: flex-start; padding: 10px 12px; border-radius: 10px; background: rgba(208,58,58,.10); color: var(--dsw-alias-state-error-primary, rgb(220,90,90)); font-size: 12px; line-height: 1.5; }\n.dshmore-error { padding: 10px 12px; border-radius: 10px; background: rgba(208,58,58,.10); color: var(--dsw-alias-state-error-primary, rgb(220,90,90)); font-size: 12px; line-height: 1.5; }\n@media (max-width: 620px) { .dshmore-edit-modal { width: calc(100vw - 24px) !important; max-width: calc(100vw - 24px) !important; } }\n.dshmore-action-contribution { display: inline-flex; align-items: center; order: var(--dshmore-action-order); }\n.dshmore-config-card { box-sizing: border-box; list-style: none; border: 1px solid var(--dsw-alias-border-l2); border-radius: 12px; background: var(--dsw-alias-bg-layer-3); overflow: hidden; transition: border-color .16s, background .16s; }\n.dshmore-config-card:hover { border-color: var(--dsw-alias-label-dimmed); }\n.dshmore-config-card-open { border-color: var(--dsw-alias-label-dimmed); background: var(--dsw-alias-bg-layer-2); }\n.dshmore-config-heading { appearance: none; box-sizing: border-box; width: 100%; color: inherit; font: inherit; text-align: left; cursor: pointer; background: transparent; border: 0; border-radius: 12px; display: flex; align-items: center; gap: 12px; padding: 14px 16px; }\n.dshmore-config-heading:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: -2px; }\n.dshmore-config-heading-copy { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 4px; }\n.dshmore-config-heading-copy strong { color: var(--dsw-alias-label-primary); font-size: 15px; line-height: 1.4; }\n.dshmore-config-heading-copy > span, .dshmore-config-copy span, .dshmore-config-note { color: var(--dsw-alias-label-tertiary); font-size: 13px; line-height: 1.5; }\n.dshmore-config-chevron { flex: none; color: var(--dsw-alias-label-tertiary); }\n.dshmore-config-chevron-open { transform: rotate(180deg); }\n.dshmore-config-body { margin: 0 16px; padding-bottom: 8px; border-top: 1px solid var(--dsw-alias-border-l2); }\n.dshmore-config-list { display: grid; }\n.dshmore-config-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 12px 0; cursor: pointer; }\n.dshmore-config-row + .dshmore-config-row { border-top: 1px solid var(--dsw-alias-border-l1); }\n.dshmore-config-row:hover { background: var(--dsw-alias-interactive-bg-hover); }\n.dshmore-config-copy { min-width: 0; display: grid; gap: 2px; }\n.dshmore-config-copy strong { color: var(--dsw-alias-label-primary); font-size: 13px; }\n.dshmore-config-row input { width: 34px; height: 18px; flex: none; cursor: pointer; accent-color: var(--dsw-alias-state-business-primary); }\n.dshmore-config-row input:disabled { cursor: default; opacity: .5; }\n.dshmore-config-note, .dshmore-config-error { margin: 0; padding: 10px 0; border-top: 1px solid var(--dsw-alias-border-l1); }\n.dshmore-config-error { color: var(--dsw-alias-state-error-primary, rgb(220,90,90)); font-size: 12px; }\n@media (prefers-reduced-motion: no-preference) { .dshmore-config-chevron { transition: transform .16s; } }\n";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AgentHandle } from '@deepseek-ai/dsh-agent';
|
|
2
|
+
import type { Workspace } from '@deepseek-ai/dsh-workspace';
|
|
3
|
+
/** Undo the public side effects of a continuation that failed before publication. */
|
|
4
|
+
export declare function rollbackFailedContinuation(child: AgentHandle, workspace: Workspace | undefined, attached: boolean, cause: unknown): Promise<never>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type DshMoreErrorCode = 'bad-request' | 'forbidden' | 'method-not-allowed' | 'not-found' | 'session-not-live' | 'session-busy' | 'invalid-turn-range' | 'selection-empty' | 'stale-preview' | 'patch-disabled' | 'internal';
|
|
1
|
+
export type DshMoreErrorCode = 'bad-request' | 'payload-too-large' | 'forbidden' | 'method-not-allowed' | 'unsupported-media-type' | 'not-found' | 'session-not-live' | 'session-busy' | 'invalid-turn-range' | 'selection-empty' | 'stale-preview' | 'patch-disabled' | 'internal';
|
|
2
2
|
export declare class DshMoreError extends Error {
|
|
3
3
|
readonly code: DshMoreErrorCode;
|
|
4
4
|
readonly status: number;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
export declare function requireJsonContentType(req: IncomingMessage): void;
|
|
2
3
|
export declare function readJson(req: IncomingMessage): Promise<unknown>;
|
|
3
4
|
export declare function requireString(value: unknown, key: string): string;
|
|
4
5
|
export declare function requireInteger(value: unknown, key: string): number;
|
package/dsh.plugin.json
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-more",
|
|
3
3
|
"description": "Practical patches for missing DSH Web context and history controls",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.2",
|
|
5
5
|
"entry": {
|
|
6
6
|
"name": "dsh-more",
|
|
7
|
-
"inject": [
|
|
7
|
+
"inject": [
|
|
8
|
+
"webServer",
|
|
9
|
+
"webRuntime",
|
|
10
|
+
"agents",
|
|
11
|
+
"sessions",
|
|
12
|
+
"sessionPersistence",
|
|
13
|
+
"sessionProjections",
|
|
14
|
+
"workspaceRegistry",
|
|
15
|
+
"agentPresets",
|
|
16
|
+
"settings"
|
|
17
|
+
]
|
|
8
18
|
},
|
|
9
19
|
"client": {
|
|
10
20
|
"platform": "web"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-more",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Practical patches for missing DeepSeek Harness Web context and history controls.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"dsh-plugin"
|
|
20
20
|
],
|
|
21
21
|
"engines": {
|
|
22
|
-
"node": "
|
|
22
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
23
23
|
},
|
|
24
24
|
"packageManager": "pnpm@11.6.0",
|
|
25
25
|
"publishConfig": {
|
|
@@ -66,11 +66,13 @@
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
"scripts": {
|
|
69
|
+
"validate:package": "node scripts/validate-package-manifests.mjs",
|
|
70
|
+
"peers:check": "pnpm peers check",
|
|
69
71
|
"generate": "node scripts/generate-patch-registry.mjs",
|
|
70
72
|
"build": "node build.mjs",
|
|
71
73
|
"typecheck": "npm run generate && tsc --noEmit",
|
|
72
74
|
"test": "npm run generate && vitest run",
|
|
73
|
-
"check": "npm run typecheck && npm run test && npm run build",
|
|
75
|
+
"check": "npm run validate:package && npm run peers:check && npm run typecheck && npm run test && npm run build",
|
|
74
76
|
"package": "npm pack",
|
|
75
77
|
"package:check": "npm pack --dry-run",
|
|
76
78
|
"release": "node scripts/release.mjs",
|
|
@@ -78,6 +80,7 @@
|
|
|
78
80
|
"release:patch": "node scripts/release.mjs patch",
|
|
79
81
|
"release:minor": "node scripts/release.mjs minor",
|
|
80
82
|
"release:major": "node scripts/release.mjs major",
|
|
83
|
+
"version": "node scripts/sync-plugin-version.mjs",
|
|
81
84
|
"prepack": "npm run check"
|
|
82
85
|
},
|
|
83
86
|
"peerDependencies": {
|
|
@@ -130,7 +133,7 @@
|
|
|
130
133
|
"@types/node": "^24.0.0",
|
|
131
134
|
"@types/react": "~18.3.0",
|
|
132
135
|
"@types/react-dom": "~18.3.0",
|
|
133
|
-
"esbuild": "^0.
|
|
136
|
+
"esbuild": "^0.28.2",
|
|
134
137
|
"react": "18.3.1",
|
|
135
138
|
"react-dom": "18.3.1",
|
|
136
139
|
"typescript": "^5.9.2",
|