@lijian-ui/dsh-session-cleaner 0.1.2 → 0.1.3
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/lib/client.js +719 -737
- package/lib/index.js +26 -41
- package/package.json +1 -1
- /package/lib/{index.d.ts → index-DdPrDbW6.d.ts} +0 -0
package/lib/index.js
CHANGED
|
@@ -5,18 +5,30 @@ import * as fs from "fs";
|
|
|
5
5
|
import * as path from "path";
|
|
6
6
|
import { homedir } from "os";
|
|
7
7
|
import { zstdDecompressSync } from "node:zlib";
|
|
8
|
-
|
|
9
8
|
//#region src/session-store.ts
|
|
10
9
|
/**
|
|
11
|
-
|
|
10
|
+
* 会话存储层(session-cleaner host half)
|
|
11
|
+
* ------------------------------------------------------------------
|
|
12
|
+
* 直接操作 dsh 官方文件层(无需官方 API):
|
|
13
|
+
*
|
|
14
|
+
* sessions 树: ~/.dsh/sessions/<encoded-workspace>/<sessionId>/session.jsonl.zstd
|
|
15
|
+
* workspace 状态:~/.dsh/storages/workspace.json
|
|
16
|
+
* - global.archivedSessionIds ← 归档状态(与官方 UI 共享)
|
|
17
|
+
* - tables.workspaces[*].sessionIds ← 会话归属
|
|
18
|
+
* projcache: ~/.dsh/storages/session_projcache.json(删除时必须清,否则旧投影残留)
|
|
19
|
+
*
|
|
20
|
+
* 删除 = 三处清理:session 目录 + projcache + workspace.json(sessionIds/archivedSessionIds)。
|
|
21
|
+
* 归档/恢复 = 读写 workspace.json 的 global.archivedSessionIds(单真相源与官方打通)。
|
|
22
|
+
*
|
|
23
|
+
* ⚠️ 已知边界:归档/恢复直接改文件,host 内存态不会热更新——插件页面以文件为准(准确),
|
|
24
|
+
* 官方 UI 的隐藏/恢复在 dsh 重启后同步。删除同理(已删除的会话从文件层消失,官方 UI
|
|
25
|
+
* 重启后不再列出)。
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
12
28
|
* dsh 会话 ID ↔ 文件系统目录名 编解码(与 dsh-session-persistence-jsonl 的 encodeSegment 对齐)。
|
|
13
|
-
|
|
14
29
|
* dsh 把 sessionId 中的非 [A-Za-z0-9._-] 字符编码为 ~XXXX(charCode 的 4 位十六进制大写)。
|
|
15
|
-
|
|
16
30
|
* 例如 `im:qq-s5ey:c2c:...` → `im~003Aqq-s5ey~003Ac2c~003A...`。
|
|
17
|
-
|
|
18
31
|
* session-cleaner 扫描文件系统得到编码后的目录名,需要解码回原始 ID 才能与 workspace.json 的 sessionIds 匹配。
|
|
19
|
-
|
|
20
32
|
*/
|
|
21
33
|
function encodeSegment(raw) {
|
|
22
34
|
if (raw.length === 0) return raw;
|
|
@@ -100,11 +112,8 @@ function writeArchived(archived) {
|
|
|
100
112
|
writeJsonAtomic(workspaceFile(), ws);
|
|
101
113
|
}
|
|
102
114
|
/**
|
|
103
|
-
|
|
104
115
|
* 扫描全部 session(文件层权威)。
|
|
105
|
-
|
|
106
116
|
* 遍历 ~/.dsh/sessions/<workspace>/<sessionId>/,结合 workspace.json 标注状态。
|
|
107
|
-
|
|
108
117
|
*/
|
|
109
118
|
function listSessions() {
|
|
110
119
|
const root = sessionsRoot();
|
|
@@ -112,7 +121,7 @@ function listSessions() {
|
|
|
112
121
|
if (!fs.existsSync(root)) return out;
|
|
113
122
|
const archived = readArchived();
|
|
114
123
|
const ws = readWorkspace();
|
|
115
|
-
const activeIds = new Set();
|
|
124
|
+
const activeIds = /* @__PURE__ */ new Set();
|
|
116
125
|
for (const w of Object.values(ws.tables?.workspaces ?? {})) for (const id of w.sessionIds ?? []) activeIds.add(id);
|
|
117
126
|
for (const workspaceDir of fs.readdirSync(root, { withFileTypes: true })) {
|
|
118
127
|
if (!workspaceDir.isDirectory()) continue;
|
|
@@ -179,13 +188,9 @@ function removeFromProjcache(id) {
|
|
|
179
188
|
if (walk(data)) writeJsonAtomic(file, data);
|
|
180
189
|
}
|
|
181
190
|
/**
|
|
182
|
-
|
|
183
191
|
* 真删除会话(不可恢复,调用方必须已二次确认):
|
|
184
|
-
|
|
185
192
|
* ① 删会话目录;② 清 projcache;③ 清 workspace.json(sessionIds + archivedSessionIds)。
|
|
186
|
-
|
|
187
193
|
* 返回已删除的目录路径。
|
|
188
|
-
|
|
189
194
|
*/
|
|
190
195
|
function deleteSession(id) {
|
|
191
196
|
const root = sessionsRoot();
|
|
@@ -230,13 +235,9 @@ function unarchiveSession(id) {
|
|
|
230
235
|
return { ok: true };
|
|
231
236
|
}
|
|
232
237
|
/**
|
|
233
|
-
|
|
234
238
|
* 多帧 zstd 解码:dsh 的 session 日志每次追加写一个独立 zstd 帧
|
|
235
|
-
|
|
236
239
|
* (文件里多个 magic 头),Node 的 zstdDecompressSync 只解第一帧——
|
|
237
|
-
|
|
238
240
|
* 必须按 magic 位置逐帧解压拼接。
|
|
239
|
-
|
|
240
241
|
*/
|
|
241
242
|
function zstdDecompressAll(input) {
|
|
242
243
|
const magic = Buffer.from([
|
|
@@ -264,8 +265,7 @@ function zstdDecompressAll(input) {
|
|
|
264
265
|
function readSessionLines(logFile) {
|
|
265
266
|
try {
|
|
266
267
|
if (!fs.existsSync(logFile)) return null;
|
|
267
|
-
|
|
268
|
-
return raw.toString("utf8").split("\n").filter(Boolean);
|
|
268
|
+
return zstdDecompressAll(fs.readFileSync(logFile)).toString("utf8").split("\n").filter(Boolean);
|
|
269
269
|
} catch {
|
|
270
270
|
return null;
|
|
271
271
|
}
|
|
@@ -292,15 +292,14 @@ function extractHeader(lines) {
|
|
|
292
292
|
cwd: header.cwd
|
|
293
293
|
};
|
|
294
294
|
} catch {
|
|
295
|
-
return
|
|
295
|
+
return;
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
298
|
/** 从消息事件提取纯文本(宽容解析各种 content 形态) */
|
|
299
299
|
function messageText(message) {
|
|
300
300
|
if (typeof message === "string") return message;
|
|
301
301
|
if (!message || typeof message !== "object") return "";
|
|
302
|
-
const
|
|
303
|
-
const content = m["content"];
|
|
302
|
+
const content = message["content"];
|
|
304
303
|
if (typeof content === "string") return content;
|
|
305
304
|
if (Array.isArray(content)) return content.map((part) => {
|
|
306
305
|
if (typeof part === "string") return part;
|
|
@@ -325,11 +324,8 @@ function readSessionTitle(logFile) {
|
|
|
325
324
|
return extractTitle(lines);
|
|
326
325
|
}
|
|
327
326
|
/**
|
|
328
|
-
|
|
329
327
|
* 读取会话预览:header 元数据 + 最近 N 条用户/助手消息文本。
|
|
330
|
-
|
|
331
328
|
* 找不到文件抛错(调用方转 RPC 错误)。
|
|
332
|
-
|
|
333
329
|
*/
|
|
334
330
|
function previewSession(id, limit = 12) {
|
|
335
331
|
const root = sessionsRoot();
|
|
@@ -373,7 +369,6 @@ function previewSession(id, limit = 12) {
|
|
|
373
369
|
messages: messages.slice(-limit)
|
|
374
370
|
});
|
|
375
371
|
}
|
|
376
|
-
|
|
377
372
|
//#endregion
|
|
378
373
|
//#region src/remote.ts
|
|
379
374
|
const SERVICE = "sessionCleanerRemote";
|
|
@@ -384,11 +379,8 @@ const codec = (typeSymbol) => ({
|
|
|
384
379
|
schema: { parse: (v) => v }
|
|
385
380
|
});
|
|
386
381
|
/**
|
|
387
|
-
|
|
388
382
|
* 注册到 `ctx.typert` 的 host 面契约。
|
|
389
|
-
|
|
390
383
|
* face: 'host' + invocations(client 侧的 $mount 贡献则带 descriptors)。
|
|
391
|
-
|
|
392
384
|
*/
|
|
393
385
|
const CONTRIBUTION = {
|
|
394
386
|
package: PACKAGE,
|
|
@@ -474,11 +466,8 @@ const CONTRIBUTION = {
|
|
|
474
466
|
}
|
|
475
467
|
};
|
|
476
468
|
/**
|
|
477
|
-
|
|
478
469
|
* Remote 服务实现。构造时注册 `sessionCleanerRemote` cordis 服务,
|
|
479
|
-
|
|
480
470
|
* 上面的贡献让 API gateway 能分发四个方法。
|
|
481
|
-
|
|
482
471
|
*/
|
|
483
472
|
var SessionCleanerApi = class extends TypertRemoteService {
|
|
484
473
|
constructor(ctx) {
|
|
@@ -510,7 +499,6 @@ function registerSessionCleanerRemote(ctx) {
|
|
|
510
499
|
new SessionCleanerApi(ctx);
|
|
511
500
|
ctx.effect(() => ctx.get("typert").register(CONTRIBUTION), "session-cleaner: typert contribution");
|
|
512
501
|
}
|
|
513
|
-
|
|
514
502
|
//#endregion
|
|
515
503
|
//#region src/index.ts
|
|
516
504
|
/**
|
|
@@ -543,19 +531,17 @@ function apply(ctx) {
|
|
|
543
531
|
*/
|
|
544
532
|
function installConsoleLoggerExporter(ctx) {
|
|
545
533
|
try {
|
|
546
|
-
|
|
547
|
-
logger?.exporter?.({
|
|
534
|
+
ctx.root.logger?.exporter?.({
|
|
548
535
|
colors: 0,
|
|
549
536
|
export: (message) => {
|
|
550
537
|
const { name = "session-cleaner", type = "log", args = [] } = message;
|
|
551
|
-
const
|
|
538
|
+
const line = `[${name}] ${args.map((arg) => arg instanceof Error ? arg.stack ?? arg.message : typeof arg === "string" ? arg : (() => {
|
|
552
539
|
try {
|
|
553
540
|
return JSON.stringify(arg);
|
|
554
541
|
} catch {
|
|
555
542
|
return String(arg);
|
|
556
543
|
}
|
|
557
|
-
})()).join(" ")
|
|
558
|
-
const line = `[${name}] ${rendered}`;
|
|
544
|
+
})()).join(" ")}`;
|
|
559
545
|
if (type === "error") console.error(line);
|
|
560
546
|
else if (type === "warn") console.warn(line);
|
|
561
547
|
else if (type === "debug") console.debug(line);
|
|
@@ -564,6 +550,5 @@ function installConsoleLoggerExporter(ctx) {
|
|
|
564
550
|
});
|
|
565
551
|
} catch {}
|
|
566
552
|
}
|
|
567
|
-
|
|
568
553
|
//#endregion
|
|
569
|
-
export { Config, apply, inject };
|
|
554
|
+
export { Config, apply, inject };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lijian-ui/dsh-session-cleaner",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Session management plugin for DeepSeek Harness (dsh): list / delete / archive & restore sessions, with a built-in \"Archived\" group and a delete confirm dialog (real delete, not trash). 为 DeepSeek Harness 提供会话管理:列表 / 真删除(二次确认)/ 归档分组与恢复。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
File without changes
|