@chaoset/session-archive 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts ADDED
@@ -0,0 +1,130 @@
1
+ import type { Context as CordisContext } from "@deepseek-ai/cordis";
2
+
3
+ declare module "@deepseek-ai/cordis" {
4
+ interface Context extends CordisContext {
5
+ workspaceRegistry: {
6
+ [key: string]: any;
7
+ [key: symbol]: any;
8
+ archivedSessionIds: any;
9
+ enqueueOperation(operation: () => void | Promise<void>): Promise<void>;
10
+ requireState(): { archivedSessionIds: string[]; [key: string]: any };
11
+ setState(next: Record<string, any>): Promise<void> | void;
12
+ };
13
+ sessionPersistence: {
14
+ [key: string]: any;
15
+ [key: symbol]: any;
16
+ list(): Promise<Array<{ id: string; cwd?: string; createdAt?: number; [key: string]: any }>>;
17
+ locate(meta: { id: string; [key: string]: any }): { kind: string; path: string; [key: string]: any };
18
+ readFrom(id: string, offset: number): Promise<{ meta: Record<string, any>; events: Array<any> }>;
19
+ };
20
+ sessions: {
21
+ [key: string]: any;
22
+ [key: symbol]: any;
23
+ get(id: string): any;
24
+ };
25
+ }
26
+ }
27
+
28
+ /**
29
+ * session-archive — 归档会话管理插件(@chaoset/session-archive)
30
+ *
31
+ * 补上 DSH 缺失的"归档"后半程:web 侧边栏新增"归档"面板,可查看归档
32
+ * 会话(列表 + 会话内容只读浏览)、一次多选批量恢复归档(unarchive,
33
+ * 会话回到会话树原位置)或彻底删除归档(删除持久化文件与归档记录)。
34
+ *
35
+ * host 端全部逻辑基于官方 service 接口(workspaceRegistry /
36
+ * sessionPersistence / sessions),不依赖 dsh 内部实现:
37
+ * 1. list() — archivedSessionIds ∩ sessionPersistence.list(),
38
+ * 每条附带标题(从事件流折叠 session/title)、目录、
39
+ * 创建时间、最后修改时间(文件 mtime)、体积与
40
+ * live 状态(会话仍在内存中运行时禁止删除)。
41
+ * 2. detail(id) — readFrom(id, 0) 只读取会话事件:标题 + 文本消息
42
+ * (user/assistant),供面板"查看"展开。
43
+ * 3. delete(ids) — 批量彻底删除:live 会话拒绝;逐个删除持久化文件
44
+ * (locate() 定位)+ 会话目录。删除后**保留**该会话在
45
+ * 归档集合中的 ghost id(不调用移除):宿主的
46
+ * archiveSession 只改归档注册表、不停止内存会话,web
47
+ * 客户端重连还会把旧 tab 恢复进内存——删除时若把 id
48
+ * 移出归档集合,仍挂在内存里的会话会因"不再归档"而
49
+ * 立刻重新出现在侧边栏对话列表(效果等同"恢复")。
50
+ * 保留 ghost id 后由 list() 的存在性过滤隐藏,归档面板
51
+ * 与侧边栏都不会再显示该会话。
52
+ * 4. unarchive(ids) — 批量恢复:仅从归档集合移除**仍存在持久化文件**的
53
+ * 会话 id(文件已删的 ghost id 拒绝恢复,防止已彻底
54
+ * 删除的会话"复活"回侧边栏);会话数据不动。
55
+ *
56
+ * 归档集合(workspaceRegistry.archivedSessionIds)的写入没有官方 API,
57
+ * 这里复用 registry 自身的串行化写入通道(enqueueOperation → requireState
58
+ * → setState,与 archiveSession 相同的路径);若 registry 内部形状变化,
59
+ * 自动降级为"仅删文件",归档列表会以存在性过滤幽灵 id,功能仍正确。
60
+ *
61
+ * 本文件不依赖任何 dsh 内部包(纯 ESM + ctx.* service),可独立安装。
62
+ */
63
+ import type { Context } from '@deepseek-ai/cordis';
64
+ export declare const name = "session-archive";
65
+ /** sessions 参与 inject:删除前必须能查询 live 会话(存在即拒绝删除)。 */
66
+ export declare const inject: string[];
67
+ export declare const config: {
68
+ /** detail() 返回的最大消息条数(超出仅计数)。 */
69
+ detailMaxMessages: number;
70
+ /** 每条消息预览的最大字符数(超出截断加省略号)。 */
71
+ messagePreviewChars: number;
72
+ /** list() 时并发读取标题的最大并行数。 */
73
+ titleReadConcurrency: number;
74
+ };
75
+ /**
76
+ * 构造归档管理 host 逻辑(绑定 ctx 与配置)。
77
+ * 只读操作失败各自容错:单个会话的标题/详情读取失败不拖垮列表。
78
+ */
79
+ export declare function createArchiveHost(ctx: Context, cfg: Record<string, any>): {
80
+ /** 列出全部归档会话(存在性过滤:文件已删的幽灵归档记录不显示)。 */
81
+ list(): Promise<{
82
+ items: any[];
83
+ }>;
84
+ /** 读取一个归档会话的只读详情(标题 + 文本消息)。 */
85
+ detail(sessionId: string): Promise<{
86
+ sessionId: string;
87
+ header: {
88
+ cwd: any;
89
+ createdAt: any;
90
+ parentSession: any;
91
+ agentPreset: any;
92
+ };
93
+ title: string | null;
94
+ messageCount: number;
95
+ messages: {
96
+ role: string;
97
+ text: string;
98
+ time: any;
99
+ }[];
100
+ live: boolean;
101
+ }>;
102
+ /**
103
+ * 批量彻底删除归档会话。live 会话拒绝(先停止再删);每个会话删除
104
+ * 持久化文件与会话目录。删除后**不**从归档集合移除 id(保留 ghost id):
105
+ * 归档不停止内存会话,一旦把 id 移出归档集合,侧边栏(以"不在归档
106
+ * 集合中"作为显示条件)会立刻把仍在内存中的会话重新显示出来,等同
107
+ * "恢复"。ghost id 由 list() 的存在性过滤隐藏,面板与侧边栏均不再
108
+ * 显示该会话;`removedFromArchive` 因此恒为 0。
109
+ */
110
+ deleteArchived(sessionIds: string[]): Promise<{
111
+ deleted: string[];
112
+ failed: {
113
+ sessionId: string;
114
+ reason: string;
115
+ }[];
116
+ removedFromArchive: number;
117
+ }>;
118
+ /**
119
+ * 批量恢复归档会话(仅从归档集合移除,会话数据不动)。只恢复仍存在
120
+ * 持久化文件的会话:文件已删的 ghost id(已彻底删除的会话)拒绝恢复,
121
+ * 避免删除后的会话再次出现在侧边栏对话列表。
122
+ */
123
+ unarchive(sessionIds: string[]): Promise<{
124
+ restored: string[];
125
+ removedFromArchive: number;
126
+ }>;
127
+ };
128
+ /** 插件 apply:注册远程服务(面板 UI 读写)。 */
129
+ export declare function apply(ctx: Context, config?: any): any;
130
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAYnD,eAAO,MAAM,IAAI,oBAAoB,CAAC;AAEtC,oDAAoD;AACpD,eAAO,MAAM,MAAM,UAA0D,CAAC;AAY9E,eAAO,MAAM,MAAM;IARjB,iCAAiC;;IAEjC,8BAA8B;;IAE9B,4BAA4B;;CAIa,CAAC;AAiE5C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IA8EpE,sCAAsC;IAChC,IAAI;;;IAeV,gCAAgC;IAC1B,MAAM,YAAY,MAAM;;;YAkBxB,GAAG;YACH,SAAS;YACT,aAAa;YACb,WAAW;;;;;;;;;;;IASjB;;;;;;;OAOG;IACG,cAAc,aAAa,MAAM,EAAE;;;;;;;;IAqCzC;;;;OAIG;IACG,SAAS,aAAa,MAAM,EAAE;;;;EAevC;AAED,iCAAiC;AACjC,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,GAAG,CAmBrD"}
package/lib/index.js ADDED
@@ -0,0 +1,334 @@
1
+ /**
2
+ * session-archive — 归档会话管理插件(@chaoset/session-archive)
3
+ *
4
+ * 补上 DSH 缺失的"归档"后半程:web 侧边栏新增"归档"面板,可查看归档
5
+ * 会话(列表 + 会话内容只读浏览)、一次多选批量恢复归档(unarchive,
6
+ * 会话回到会话树原位置)或彻底删除归档(删除持久化文件与归档记录)。
7
+ *
8
+ * host 端全部逻辑基于官方 service 接口(workspaceRegistry /
9
+ * sessionPersistence / sessions),不依赖 dsh 内部实现:
10
+ * 1. list() — archivedSessionIds ∩ sessionPersistence.list(),
11
+ * 每条附带标题(从事件流折叠 session/title)、目录、
12
+ * 创建时间、最后修改时间(文件 mtime)、体积与
13
+ * live 状态(会话仍在内存中运行时禁止删除)。
14
+ * 2. detail(id) — readFrom(id, 0) 只读取会话事件:标题 + 文本消息
15
+ * (user/assistant),供面板"查看"展开。
16
+ * 3. delete(ids) — 批量彻底删除:live 会话拒绝;逐个删除持久化文件
17
+ * (locate() 定位)+ 会话目录。删除后**保留**该会话在
18
+ * 归档集合中的 ghost id(不调用移除):宿主的
19
+ * archiveSession 只改归档注册表、不停止内存会话,web
20
+ * 客户端重连还会把旧 tab 恢复进内存——删除时若把 id
21
+ * 移出归档集合,仍挂在内存里的会话会因"不再归档"而
22
+ * 立刻重新出现在侧边栏对话列表(效果等同"恢复")。
23
+ * 保留 ghost id 后由 list() 的存在性过滤隐藏,归档面板
24
+ * 与侧边栏都不会再显示该会话。
25
+ * 4. unarchive(ids) — 批量恢复:仅从归档集合移除**仍存在持久化文件**的
26
+ * 会话 id(文件已删的 ghost id 拒绝恢复,防止已彻底
27
+ * 删除的会话"复活"回侧边栏);会话数据不动。
28
+ *
29
+ * 归档集合(workspaceRegistry.archivedSessionIds)的写入没有官方 API,
30
+ * 这里复用 registry 自身的串行化写入通道(enqueueOperation → requireState
31
+ * → setState,与 archiveSession 相同的路径);若 registry 内部形状变化,
32
+ * 自动降级为"仅删文件",归档列表会以存在性过滤幽灵 id,功能仍正确。
33
+ *
34
+ * 本文件不依赖任何 dsh 内部包(纯 ESM + ctx.* service),可独立安装。
35
+ */
36
+ import { stat, rm } from 'node:fs/promises';
37
+ import { dirname } from 'node:path';
38
+ import { createConfigStore } from './config-store.js';
39
+ // remote 服务(侧边栏面板 UI 的读写)可选:typert-protocol 不可用时
40
+ // 动态 import 失败,仅面板不可用,host 逻辑不注册(无其他消费者)。
41
+ let SessionArchiveGateway = null;
42
+ try {
43
+ ({ SessionArchiveGateway } = await import('./remote.js'));
44
+ }
45
+ catch (error) {
46
+ console.warn('session-archive: remote gateway unavailable: ' + (error?.message ?? String(error)));
47
+ }
48
+ export const name = 'session-archive';
49
+ /** sessions 参与 inject:删除前必须能查询 live 会话(存在即拒绝删除)。 */
50
+ export const inject = ['workspaceRegistry', 'sessionPersistence', 'sessions'];
51
+ /** 默认配置。apply 时与 YAML 传入的 config 合并(cordis 不合并小写 config 导出)。 */
52
+ const DEFAULT_CONFIG = {
53
+ /** detail() 返回的最大消息条数(超出仅计数)。 */
54
+ detailMaxMessages: 200,
55
+ /** 每条消息预览的最大字符数(超出截断加省略号)。 */
56
+ messagePreviewChars: 2000,
57
+ /** list() 时并发读取标题的最大并行数。 */
58
+ titleReadConcurrency: 4,
59
+ };
60
+ export const config = { ...DEFAULT_CONFIG };
61
+ /** 配置校验:只接受正整数数值字段,避免脏配置拖垮并发/截断逻辑。 */
62
+ function validateConfig(partial) {
63
+ if (partial === null || typeof partial !== 'object' || Array.isArray(partial)) {
64
+ throw new TypeError('session-archive config must be a plain object');
65
+ }
66
+ for (const key of ['detailMaxMessages', 'messagePreviewChars', 'titleReadConcurrency']) {
67
+ if (partial[key] !== undefined && (!Number.isInteger(partial[key]) || partial[key] <= 0)) {
68
+ throw new TypeError(`session-archive config field "${key}" must be a positive integer`);
69
+ }
70
+ }
71
+ }
72
+ /** 配置归一化:非法/缺失数值回退默认值,保证运行时不会拿到 NaN/负数。 */
73
+ function normalizeConfig(source, defaults = DEFAULT_CONFIG) {
74
+ const raw = source !== null && typeof source === 'object' && !Array.isArray(source) ? source : {};
75
+ const merged = { ...defaults, ...raw };
76
+ const positiveInt = (value, fallback) => Number.isInteger(value) && value > 0 ? value : fallback;
77
+ return {
78
+ detailMaxMessages: positiveInt(merged.detailMaxMessages, defaults.detailMaxMessages),
79
+ messagePreviewChars: positiveInt(merged.messagePreviewChars, defaults.messagePreviewChars),
80
+ titleReadConcurrency: positiveInt(merged.titleReadConcurrency, defaults.titleReadConcurrency),
81
+ };
82
+ }
83
+ /** 并发限制器:最多 N 个任务并行,其余排队。 */
84
+ function limitedConcurrency(limit, tasks) {
85
+ const results = new Array(tasks.length);
86
+ let cursor = 0;
87
+ const workers = Array.from({ length: Math.min(limit, tasks.length) }, async () => {
88
+ while (cursor < tasks.length) {
89
+ const at = cursor++;
90
+ const task = tasks[at];
91
+ results[at] = await task();
92
+ }
93
+ });
94
+ return Promise.all(workers).then(() => results);
95
+ }
96
+ /** 从事件流折叠最新会话标题(与 dsh-session-title 相同的折叠规则)。 */
97
+ function foldTitle(events) {
98
+ for (let i = events.length - 1; i >= 0; i--) {
99
+ const event = events[i];
100
+ if (event && event.type === 'session/title') {
101
+ const title = event.data && typeof event.data.title === 'string' ? event.data.title.trim() : '';
102
+ return title.length > 0 ? title : null;
103
+ }
104
+ }
105
+ return null;
106
+ }
107
+ /** 从一条消息(Message 结构)提取纯文本(text 块拼接,忽略图片/工具块)。 */
108
+ function messageText(message, maxChars) {
109
+ if (!message || !Array.isArray(message.content))
110
+ return '';
111
+ let text = '';
112
+ for (const block of message.content) {
113
+ if (block && block.type === 'text' && typeof block.text === 'string') {
114
+ text += block.text;
115
+ if (text.length > maxChars)
116
+ break;
117
+ }
118
+ }
119
+ return text.length > maxChars ? text.slice(0, maxChars) + '…' : text;
120
+ }
121
+ /**
122
+ * 构造归档管理 host 逻辑(绑定 ctx 与配置)。
123
+ * 只读操作失败各自容错:单个会话的标题/详情读取失败不拖垮列表。
124
+ */
125
+ export function createArchiveHost(ctx, cfg) {
126
+ const registry = ctx.workspaceRegistry;
127
+ const persistence = ctx.sessionPersistence;
128
+ /** 当前归档集合快照。 */
129
+ const archivedSet = () => new Set(typeof registry.archivedSessionIds === 'function'
130
+ ? registry.archivedSessionIds()
131
+ : (Array.isArray(registry.archivedSessionIds) ? registry.archivedSessionIds : []));
132
+ /** 会话是否"活跃"(不能安全删除)。
133
+ * 宿主的 archiveSession 只改归档注册表、不停止内存会话,web 客户端
134
+ * 重连还会把旧 tab 恢复进内存——归档会话因此长期"内存存在"。但归档
135
+ * 会话已从会话列表移除、无法继续对话,不会再写持久化文件,删除安全;
136
+ * 若把内存存在当作 live,归档面板会永远显示"运行中"且无法勾选删除。
137
+ * 因此 live 仅对"内存存在且**未归档**"的会话为真(防将来误用),
138
+ * 归档面板中恒为 false。 */
139
+ const isLive = (sessionId) => ctx.sessions.get(sessionId) !== undefined && !archivedSet().has(sessionId);
140
+ /** 归档瞬间的生成流兜底:会话仍在内存且文件最近有写入(60s 内)时
141
+ * 视为忙碌——删除文件后进行中的请求会把半截日志 append 回来。 */
142
+ const isBusy = (sessionId, file) => file !== null && ctx.sessions.get(sessionId) !== undefined && Date.now() - file.mtimeMs < 60_000;
143
+ /** 从归档集合移除若干 id(恢复用;删除不调用——见 deleteArchived),返回实际移除的 id。 */
144
+ async function removeFromArchiveSet(ids) {
145
+ const set = new Set(ids);
146
+ const canWrite = registry !== undefined &&
147
+ typeof registry.enqueueOperation === 'function' &&
148
+ typeof registry.requireState === 'function' &&
149
+ typeof registry.setState === 'function';
150
+ if (!canWrite)
151
+ return []; // 降级:仅删文件,列表按存在性过滤幽灵 id
152
+ let removedIds = [];
153
+ await registry.enqueueOperation(async () => {
154
+ const state = registry.requireState();
155
+ const current = Array.isArray(state.archivedSessionIds) ? state.archivedSessionIds : [];
156
+ removedIds = current.filter((id) => set.has(id));
157
+ const remaining = current.filter((id) => !set.has(id));
158
+ if (removedIds.length > 0)
159
+ await registry.setState({ ...state, archivedSessionIds: remaining });
160
+ });
161
+ return removedIds;
162
+ }
163
+ /** 归档会话的文件信息(路径 + stat),会话文件缺失时返回 null。 */
164
+ async function fileInfo(header) {
165
+ try {
166
+ const location = persistence.locate(header);
167
+ if (location === undefined || typeof location.path !== 'string' || location.path.length === 0)
168
+ return null;
169
+ const info = await stat(location.path);
170
+ return { path: location.path, size: info.size, mtimeMs: info.mtimeMs };
171
+ }
172
+ catch {
173
+ return null;
174
+ }
175
+ }
176
+ /** 单个归档会话的展示行。标题读取失败回退 null(面板显示目录名)。 */
177
+ async function rowFor(sessionId, header) {
178
+ const file = await fileInfo(header);
179
+ let title = null;
180
+ try {
181
+ const { events } = await persistence.readFrom(sessionId, 0);
182
+ title = foldTitle(events);
183
+ }
184
+ catch { }
185
+ return {
186
+ sessionId,
187
+ title,
188
+ cwd: header.cwd ?? null,
189
+ createdAt: header.createdAt,
190
+ updatedAt: file !== null ? file.mtimeMs : header.createdAt,
191
+ size: file !== null ? file.size : 0,
192
+ live: isLive(sessionId),
193
+ };
194
+ }
195
+ return {
196
+ /** 列出全部归档会话(存在性过滤:文件已删的幽灵归档记录不显示)。 */
197
+ async list() {
198
+ const archived = [...archivedSet()];
199
+ if (archived.length === 0)
200
+ return { items: [] };
201
+ const headers = await persistence.list();
202
+ const byId = new Map(headers.map((header) => [header.id, header]));
203
+ const rows = [];
204
+ for (const sessionId of archived) {
205
+ const header = byId.get(sessionId);
206
+ if (header === undefined)
207
+ continue; // 幽灵 id:会话文件已不存在
208
+ rows.push({ sessionId, header });
209
+ }
210
+ const items = await limitedConcurrency(cfg.titleReadConcurrency, rows.map(({ sessionId, header }) => () => rowFor(sessionId, header)));
211
+ return { items };
212
+ },
213
+ /** 读取一个归档会话的只读详情(标题 + 文本消息)。 */
214
+ async detail(sessionId) {
215
+ const { meta, events } = await persistence.readFrom(sessionId, 0);
216
+ const title = foldTitle(events);
217
+ const messages = [];
218
+ for (const event of events) {
219
+ if (event.type !== 'user/message' && event.type !== 'assistant/message')
220
+ continue;
221
+ const text = messageText(event.data, cfg.messagePreviewChars);
222
+ if (text.length === 0)
223
+ continue;
224
+ messages.push({
225
+ role: event.type === 'user/message' ? 'user' : 'assistant',
226
+ text,
227
+ time: event.time,
228
+ });
229
+ if (messages.length >= cfg.detailMaxMessages)
230
+ break;
231
+ }
232
+ return {
233
+ sessionId,
234
+ header: {
235
+ cwd: meta.cwd ?? null,
236
+ createdAt: meta.createdAt,
237
+ parentSession: meta.parentSession ?? null,
238
+ agentPreset: meta.agentPreset ?? null,
239
+ },
240
+ title,
241
+ messageCount: messages.length,
242
+ messages,
243
+ live: isLive(sessionId),
244
+ };
245
+ },
246
+ /**
247
+ * 批量彻底删除归档会话。live 会话拒绝(先停止再删);每个会话删除
248
+ * 持久化文件与会话目录。删除后**不**从归档集合移除 id(保留 ghost id):
249
+ * 归档不停止内存会话,一旦把 id 移出归档集合,侧边栏(以"不在归档
250
+ * 集合中"作为显示条件)会立刻把仍在内存中的会话重新显示出来,等同
251
+ * "恢复"。ghost id 由 list() 的存在性过滤隐藏,面板与侧边栏均不再
252
+ * 显示该会话;`removedFromArchive` 因此恒为 0。
253
+ */
254
+ async deleteArchived(sessionIds) {
255
+ const unique = [...new Set(sessionIds)];
256
+ const deleted = [];
257
+ const failed = [];
258
+ const headers = await persistence.list();
259
+ const headersById = new Map(headers.map((item) => [item.id, item]));
260
+ for (const sessionId of unique) {
261
+ if (isLive(sessionId)) {
262
+ failed.push({ sessionId, reason: 'live' });
263
+ continue;
264
+ }
265
+ const header = headersById.get(sessionId);
266
+ if (header === undefined) {
267
+ // 会话文件已不存在:幂等删除(ghost id 保留在归档集合中)。
268
+ deleted.push(sessionId);
269
+ continue;
270
+ }
271
+ const file = await fileInfo(header);
272
+ if (file === null) {
273
+ deleted.push(sessionId);
274
+ continue;
275
+ }
276
+ if (isBusy(sessionId, file)) {
277
+ failed.push({ sessionId, reason: 'busy' });
278
+ continue;
279
+ }
280
+ try {
281
+ await rm(file.path, { force: true });
282
+ await rm(dirname(file.path), { recursive: true, force: true });
283
+ deleted.push(sessionId);
284
+ }
285
+ catch (error) {
286
+ failed.push({ sessionId, reason: error?.message ?? 'delete-failed' });
287
+ }
288
+ }
289
+ return { deleted, failed, removedFromArchive: 0 };
290
+ },
291
+ /**
292
+ * 批量恢复归档会话(仅从归档集合移除,会话数据不动)。只恢复仍存在
293
+ * 持久化文件的会话:文件已删的 ghost id(已彻底删除的会话)拒绝恢复,
294
+ * 避免删除后的会话再次出现在侧边栏对话列表。
295
+ */
296
+ async unarchive(sessionIds) {
297
+ const unique = [...new Set(sessionIds)];
298
+ const headers = await persistence.list();
299
+ const restorable = [];
300
+ for (const sessionId of unique) {
301
+ const header = headers.find((item) => item.id === sessionId);
302
+ if (header === undefined)
303
+ continue; // 持久化记录已删:拒绝恢复
304
+ const file = await fileInfo(header);
305
+ if (file === null)
306
+ continue; // 有记录无文件:拒绝恢复
307
+ restorable.push(sessionId);
308
+ }
309
+ const restored = await removeFromArchiveSet(restorable);
310
+ return { restored, removedFromArchive: restored.length };
311
+ },
312
+ };
313
+ }
314
+ /** 插件 apply:注册远程服务(面板 UI 读写)。 */
315
+ export function apply(ctx, config) {
316
+ const patchConfig = config || {};
317
+ const cfg = normalizeConfig({ ...DEFAULT_CONFIG, ...patchConfig });
318
+ const store = createConfigStore({
319
+ name,
320
+ defaults: DEFAULT_CONFIG,
321
+ patchConfig,
322
+ validate: validateConfig,
323
+ onUpdate: (merged) => {
324
+ Object.assign(cfg, normalizeConfig({ ...DEFAULT_CONFIG, ...patchConfig, ...merged }));
325
+ },
326
+ });
327
+ // 启动时也以 config.json(若有)为权威,和其余插件保持一致。
328
+ Object.assign(cfg, normalizeConfig(store.effective()));
329
+ if (SessionArchiveGateway !== null) {
330
+ ctx.plugin(SessionArchiveGateway, { host: createArchiveHost(ctx, cfg), serviceKey: 'sessionArchive' });
331
+ }
332
+ return store;
333
+ }
334
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * remote.ts — 归档管理插件的远程服务(侧边栏归档面板通过 ctx.remote.sessionArchive 调用)
3
+ *
4
+ * DSH 的 Remote 装饰器是 ECMAScript 标准装饰器语法(Node 默认未启用),
5
+ * 这里用"手动构造装饰器上下文"的方式等价调用:Remote(name)(method, context)
6
+ * 把标记初始化器收集起来,在模块加载时用 Object.create 模拟实例执行
7
+ * (mark 以实例原型为准,与真实实例化等价)。
8
+ *
9
+ * typert-protocol 惰性加载:npm 模式从包内 node_modules 解析;file:// 模式
10
+ * (~/.dsh/plugins/)从 harness 的 profile 依赖树解析。两者都不可用时
11
+ * 模块加载失败,由 index.ts 的动态 import 捕获——核心 host 逻辑照常注册,
12
+ * 仅侧边栏面板的远程读写不可用。
13
+ */
14
+ import type { Context } from "@deepseek-ai/cordis";
15
+ declare const TypertRemoteService: typeof import("@deepseek-ai/dsh-typert-protocol").TypertRemoteService;
16
+ /**
17
+ * 归档管理远程服务:list 列出归档会话;detail 读取会话内容;
18
+ * delete 批量删除归档会话(文件删除,归档集合保留 ghost id 防止内存会话
19
+ * 重现侧边栏);unarchive 批量恢复归档(仅限仍存在文件的会话)。
20
+ * 所有逻辑委托给 host 模块(lib/index.ts 传入的 archiveHost)。
21
+ * @param ctx - 插件上下文。
22
+ * @param config - { host: archiveHost, serviceKey: 远程服务名 }。
23
+ */
24
+ export declare class SessionArchiveGateway extends TypertRemoteService {
25
+ private host;
26
+ constructor(ctx: Context, config: {
27
+ host: any;
28
+ serviceKey: string;
29
+ });
30
+ list(): any;
31
+ detail(sessionId: string): any;
32
+ delete(sessionIds: string[]): any;
33
+ unarchive(sessionIds: string[]): any;
34
+ }
35
+ export {};
36
+ //# sourceMappingURL=remote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAyBnD,QAAA,MAAgB,mBAAmB,uEAAuB,CAAC;AA2B3D;;;;;;;GAOG;AACH,qBAAa,qBAAsB,SAAQ,mBAAmB;IAC5D,OAAO,CAAC,IAAI,CAAM;IAClB,YAAY,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAIlE;IACD,IAAI,QAEH;IACD,MAAM,CAAC,SAAS,EAAE,MAAM,OAKvB;IACD,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,OAK1B;IACD,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,OAK7B;CACF"}
package/lib/remote.js ADDED
@@ -0,0 +1,108 @@
1
+ /**
2
+ * remote.ts — 归档管理插件的远程服务(侧边栏归档面板通过 ctx.remote.sessionArchive 调用)
3
+ *
4
+ * DSH 的 Remote 装饰器是 ECMAScript 标准装饰器语法(Node 默认未启用),
5
+ * 这里用"手动构造装饰器上下文"的方式等价调用:Remote(name)(method, context)
6
+ * 把标记初始化器收集起来,在模块加载时用 Object.create 模拟实例执行
7
+ * (mark 以实例原型为准,与真实实例化等价)。
8
+ *
9
+ * typert-protocol 惰性加载:npm 模式从包内 node_modules 解析;file:// 模式
10
+ * (~/.dsh/plugins/)从 harness 的 profile 依赖树解析。两者都不可用时
11
+ * 模块加载失败,由 index.ts 的动态 import 捕获——核心 host 逻辑照常注册,
12
+ * 仅侧边栏面板的远程读写不可用。
13
+ */
14
+ import { createRequire } from "node:module";
15
+ import { homedir } from "node:os";
16
+ import { join, resolve } from "node:path";
17
+ import { pathToFileURL } from "node:url";
18
+ // @deepseek-ai/dsh-typert-protocol 是 ESM 包。Node 20 早期版本尚不支持
19
+ // require(ESM),因此统一用 import() 加载;fallback 时先用 createRequire
20
+ // 解析出实际文件,再以 file URL 导入。
21
+ async function loadTypert() {
22
+ try {
23
+ return await import("@deepseek-ai/dsh-typert-protocol");
24
+ }
25
+ catch { }
26
+ const dshHome = process.env.DSH_HOME?.trim() ? resolve(process.env.DSH_HOME) : join(homedir(), ".dsh");
27
+ const candidates = [
28
+ join(dshHome, "profiles", "web", "package.json"),
29
+ join(dshHome, "profiles", "tui", "package.json"),
30
+ join(dshHome, "profiles", "headless", "package.json"),
31
+ ];
32
+ for (const base of candidates) {
33
+ try {
34
+ const resolved = createRequire(base).resolve("@deepseek-ai/dsh-typert-protocol");
35
+ return await import(pathToFileURL(resolved).href);
36
+ }
37
+ catch { }
38
+ }
39
+ throw new Error("typert-protocol is unavailable (neither package deps nor harness profiles resolve it)");
40
+ }
41
+ const { Remote, TypertRemoteService } = await loadTypert();
42
+ let pending = [];
43
+ /** 手动标记一个类原型方法为 Remote 端点(等价 @Remote(exportName))。 */
44
+ function markRemoteMethod(proto, method, exportName) {
45
+ const initializers = [];
46
+ const context = {
47
+ kind: "method",
48
+ name: method,
49
+ private: false,
50
+ static: false,
51
+ addInitializer(fn) { initializers.push(fn); },
52
+ };
53
+ Remote(exportName ?? method)(proto[method], context);
54
+ pending.push({ initializers });
55
+ }
56
+ /** 执行收集到的标记(mark 以 Object.getPrototypeOf(this) 为原型)。 */
57
+ function runPendingMarks(instance) {
58
+ const batch = pending;
59
+ pending = [];
60
+ for (const { initializers } of batch) {
61
+ for (const init of initializers)
62
+ init.call(instance);
63
+ }
64
+ }
65
+ /**
66
+ * 归档管理远程服务:list 列出归档会话;detail 读取会话内容;
67
+ * delete 批量删除归档会话(文件删除,归档集合保留 ghost id 防止内存会话
68
+ * 重现侧边栏);unarchive 批量恢复归档(仅限仍存在文件的会话)。
69
+ * 所有逻辑委托给 host 模块(lib/index.ts 传入的 archiveHost)。
70
+ * @param ctx - 插件上下文。
71
+ * @param config - { host: archiveHost, serviceKey: 远程服务名 }。
72
+ */
73
+ export class SessionArchiveGateway extends TypertRemoteService {
74
+ host;
75
+ constructor(ctx, config) {
76
+ super(ctx, config.serviceKey);
77
+ runPendingMarks(this);
78
+ this.host = config.host;
79
+ }
80
+ list() {
81
+ return this.host.list();
82
+ }
83
+ detail(sessionId) {
84
+ if (typeof sessionId !== "string" || sessionId.length === 0) {
85
+ throw new TypeError("detail expects a session id string");
86
+ }
87
+ return this.host.detail(sessionId);
88
+ }
89
+ delete(sessionIds) {
90
+ if (!Array.isArray(sessionIds) || sessionIds.some((id) => typeof id !== "string")) {
91
+ throw new TypeError("delete expects an array of session id strings");
92
+ }
93
+ return this.host.deleteArchived(sessionIds);
94
+ }
95
+ unarchive(sessionIds) {
96
+ if (!Array.isArray(sessionIds) || sessionIds.some((id) => typeof id !== "string")) {
97
+ throw new TypeError("unarchive expects an array of session id strings");
98
+ }
99
+ return this.host.unarchive(sessionIds);
100
+ }
101
+ }
102
+ markRemoteMethod(SessionArchiveGateway.prototype, "list");
103
+ markRemoteMethod(SessionArchiveGateway.prototype, "detail");
104
+ markRemoteMethod(SessionArchiveGateway.prototype, "delete");
105
+ markRemoteMethod(SessionArchiveGateway.prototype, "unarchive");
106
+ // 模块加载时立即执行标记(Object.create 模拟实例;构造函数里的 runPendingMarks 幂等保留无害)。
107
+ runPendingMarks(Object.create(SessionArchiveGateway.prototype));
108
+ //# sourceMappingURL=remote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote.js","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"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"}