@michengai/dsh-archive-manager 1.1.6 → 1.1.8

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.
@@ -5,11 +5,45 @@ import { join, resolve, basename, dirname } from "node:path";
5
5
  import * as zlib from "node:zlib";
6
6
  import { classifySessionError } from "./archive-discovery.js";
7
7
  const LIMIT = 32 * 1024 * 1024;
8
+ function scanFirstZstdFrame(buffer) {
9
+ if (buffer.length < 4 || buffer.readUInt32LE(0) !== 4247762216) return void 0;
10
+ let offset = 4;
11
+ if (offset >= buffer.length) return void 0;
12
+ const descriptor = buffer.readUInt8(offset++);
13
+ if ((descriptor & 24) !== 0) return void 0;
14
+ const contentSizeFlag = descriptor >>> 6;
15
+ const singleSegment = (descriptor & 32) !== 0;
16
+ const checksum = (descriptor & 4) !== 0;
17
+ const dictionaryFlag = descriptor & 3;
18
+ const dictionaryBytes = dictionaryFlag === 3 ? 4 : dictionaryFlag;
19
+ const contentSizeBytes = contentSizeFlag === 0 ? singleSegment ? 1 : 0 : 1 << contentSizeFlag;
20
+ const remainingHeaderBytes = (singleSegment ? 0 : 1) + dictionaryBytes + contentSizeBytes;
21
+ if (buffer.length - offset < remainingHeaderBytes) return void 0;
22
+ offset += remainingHeaderBytes;
23
+ for (; ; ) {
24
+ if (buffer.length - offset < 3) return void 0;
25
+ const blockHeader = buffer.readUIntLE(offset, 3);
26
+ offset += 3;
27
+ const lastBlock = (blockHeader & 1) !== 0;
28
+ const blockType = blockHeader >>> 1 & 3;
29
+ const blockSize = blockHeader >>> 3;
30
+ if (blockType === 3) return void 0;
31
+ const payloadBytes = blockType === 1 ? 1 : blockSize;
32
+ if (buffer.length - offset < payloadBytes) return void 0;
33
+ offset += payloadBytes;
34
+ if (lastBlock) break;
35
+ }
36
+ if (checksum) {
37
+ if (buffer.length - offset < 4) return void 0;
38
+ offset += 4;
39
+ }
40
+ return { start: 0, end: offset };
41
+ }
8
42
  function decompressFrame(bytes, maxOutputLength) {
9
- const result = record(zlib.zstdDecompressSync(bytes, { info: true, maxOutputLength }));
10
- const engine = record(result.engine);
11
- if (!Buffer.isBuffer(result.buffer) || typeof engine.bytesWritten !== "number") throw new Error("\u538B\u7F29\u5E27\u8FD4\u56DE\u683C\u5F0F\u65E0\u6548");
12
- return { buffer: result.buffer, engine: { bytesWritten: engine.bytesWritten } };
43
+ const frame = scanFirstZstdFrame(bytes);
44
+ const target = frame ? bytes.subarray(0, frame.end) : bytes;
45
+ const buffer = zlib.zstdDecompressSync(target, { maxOutputLength });
46
+ return { buffer, engine: { bytesWritten: frame ? frame.end : bytes.length } };
13
47
  }
14
48
  const digest = (bytes) => createHash("sha256").update(bytes).digest("hex");
15
49
  function decodeRepairLog(bytes, compressed) {
@@ -170,6 +204,8 @@ export {
170
204
  childCatalogFact,
171
205
  classifySessionError,
172
206
  decodeRepairLog,
207
+ decompressFrame,
173
208
  normalizeAutomationSources,
174
- prepareAutomationRepair
209
+ prepareAutomationRepair,
210
+ scanFirstZstdFrame
175
211
  };
@@ -0,0 +1,393 @@
1
+ import * as React from "react";
2
+ import { IconArchiveOutlineMedium, Switch as PrimitiveSwitch } from "@deepseek-ai/dsh-client-ui-primitives";
3
+ const h = React.createElement;
4
+ const BUNDLE_KEY = "@michengai/dsh-archive-manager";
5
+ const SIDEBAR_PANEL_ID = "archived-sessions";
6
+ const SIDEBAR_PANEL_ORDER = 4;
7
+ const SIDEBAR_ENTRY_STYLE_ID = "dsham-sidebar-entry-style";
8
+ const sidebarEntryCss = `
9
+ .dsham_mainPanel{box-sizing:border-box;height:100%;min-height:0;overflow-y:auto;display:flex;flex-direction:column;align-items:center;padding:28px clamp(24px,4vw,48px) 48px}
10
+ .dsham_mainPanel>*{width:100%;max-width:960px}
11
+ .dsham_bundle_row{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:12px 0;border-bottom:0.5px solid var(--dsw-alias-border-l2)}
12
+ .dsham_bundle_label_box{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1;text-align:left}
13
+ .dsham_bundle_title{font-size:13px;font-weight:500;line-height:20px;color:var(--dsw-alias-label-primary)}
14
+ .dsham_bundle_desc{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}
15
+ `;
16
+ function ensureSidebarEntryStyle() {
17
+ if (typeof document === "undefined" || typeof document.getElementById !== "function") return;
18
+ if (document.getElementById(SIDEBAR_ENTRY_STYLE_ID) !== null) return;
19
+ if (typeof document.createElement !== "function") return;
20
+ const style = document.createElement("style");
21
+ style.id = SIDEBAR_ENTRY_STYLE_ID;
22
+ style.textContent = sidebarEntryCss;
23
+ (document.head ?? document.documentElement)?.appendChild?.(style);
24
+ }
25
+ function ArchiveSidebarIcon(props) {
26
+ const size = props && typeof props.size === "number" ? props.size : 16;
27
+ return h(IconArchiveOutlineMedium, { size });
28
+ }
29
+ function ArchiveBundleConfigCard(props) {
30
+ ensureSidebarEntryStyle();
31
+ const {
32
+ t,
33
+ enabled,
34
+ onToggle,
35
+ disabled,
36
+ onlineUpdateEnabled = true,
37
+ onToggleOnlineUpdate,
38
+ onlineUpdateDisabled,
39
+ onlineUpdateLocked = false
40
+ } = props;
41
+ const rows = [
42
+ h(
43
+ "div",
44
+ { key: "sidebar-entry", className: "dsham_bundle_row" },
45
+ h(
46
+ "div",
47
+ { className: "dsham_bundle_label_box" },
48
+ h("span", { className: "dsham_bundle_title" }, t("sidebar.entry.title")),
49
+ h("span", { className: "dsham_bundle_desc" }, t("sidebar.entry.desc"))
50
+ ),
51
+ h(PrimitiveSwitch, {
52
+ checked: enabled,
53
+ onChange: onToggle,
54
+ label: t("sidebar.entry.toggle"),
55
+ disabled
56
+ })
57
+ )
58
+ ];
59
+ if (typeof onToggleOnlineUpdate === "function") {
60
+ const updateDesc = onlineUpdateLocked ? `${t("update.entry.desc")} (${t("update.entry.locked")})` : t("update.entry.desc");
61
+ rows.push(
62
+ h(
63
+ "div",
64
+ { key: "online-update", className: "dsham_bundle_row" },
65
+ h(
66
+ "div",
67
+ { className: "dsham_bundle_label_box" },
68
+ h("span", { className: "dsham_bundle_title" }, t("update.entry.title")),
69
+ h("span", { className: "dsham_bundle_desc" }, updateDesc)
70
+ ),
71
+ h(PrimitiveSwitch, {
72
+ checked: onlineUpdateEnabled,
73
+ onChange: onToggleOnlineUpdate,
74
+ label: t("update.entry.toggle"),
75
+ disabled: onlineUpdateDisabled || onlineUpdateLocked,
76
+ title: onlineUpdateLocked ? t("update.entry.locked") : void 0
77
+ })
78
+ )
79
+ );
80
+ }
81
+ return h(React.Fragment, null, rows);
82
+ }
83
+ function ArchiveBundleConfigCardWrapper(props) {
84
+ const [enabled, setEnabled] = React.useState(props.getEnabled);
85
+ const [onlineUpdateEnabled, setOnlineUpdateEnabled] = React.useState(props.getOnlineUpdateEnabled ? props.getOnlineUpdateEnabled : () => true);
86
+ const [onlineUpdateLocked, setOnlineUpdateLocked] = React.useState(props.getOnlineUpdateLocked ? props.getOnlineUpdateLocked : () => false);
87
+ const [savingSidebar, setSavingSidebar] = React.useState(false);
88
+ const [savingUpdate, setSavingUpdate] = React.useState(false);
89
+ React.useEffect(() => {
90
+ const syncState = () => {
91
+ setEnabled(props.getEnabled());
92
+ if (props.getOnlineUpdateEnabled) {
93
+ setOnlineUpdateEnabled(props.getOnlineUpdateEnabled());
94
+ }
95
+ if (props.getOnlineUpdateLocked) {
96
+ setOnlineUpdateLocked(props.getOnlineUpdateLocked());
97
+ }
98
+ };
99
+ syncState();
100
+ return props.subscribe(syncState);
101
+ }, [props]);
102
+ const onToggleSidebar = (next) => {
103
+ setSavingSidebar(true);
104
+ setEnabled(next);
105
+ props.setSidebar(next).catch(() => {
106
+ setEnabled(props.getEnabled());
107
+ }).finally(() => {
108
+ setSavingSidebar(false);
109
+ });
110
+ };
111
+ const onToggleOnlineUpdate = props.setOnlineUpdate ? (next) => {
112
+ if (props.getOnlineUpdateLocked?.()) return;
113
+ setSavingUpdate(true);
114
+ setOnlineUpdateEnabled(next);
115
+ props.setOnlineUpdate(next).catch(() => {
116
+ if (props.getOnlineUpdateEnabled) {
117
+ setOnlineUpdateEnabled(props.getOnlineUpdateEnabled());
118
+ }
119
+ }).finally(() => {
120
+ setSavingUpdate(false);
121
+ });
122
+ } : void 0;
123
+ return h(ArchiveBundleConfigCard, {
124
+ t: props.t,
125
+ enabled,
126
+ disabled: savingSidebar,
127
+ onToggle: onToggleSidebar,
128
+ onlineUpdateEnabled,
129
+ onlineUpdateDisabled: savingUpdate || onlineUpdateLocked,
130
+ onlineUpdateLocked,
131
+ onToggleOnlineUpdate
132
+ });
133
+ }
134
+ function setupSidebarEntry(ctx, options) {
135
+ ensureSidebarEntryStyle();
136
+ if (!ctx.locale) {
137
+ return {
138
+ isEnabled: () => false,
139
+ isSupported: () => false,
140
+ setEnabled: async () => false,
141
+ isOnlineUpdateEnabled: () => true,
142
+ isOnlineUpdateLocked: () => false,
143
+ setOnlineUpdate: async () => false,
144
+ destroy: () => {
145
+ }
146
+ };
147
+ }
148
+ const { callApi, renderPanel, ns } = options;
149
+ let sidebarEntryEnabled = false;
150
+ let sidebarSupported = false;
151
+ let mainSupported = false;
152
+ let offPanel = null;
153
+ let offRow = null;
154
+ let epoch = 0;
155
+ let destroyed = false;
156
+ let layoutService;
157
+ const listeners = /* @__PURE__ */ new Set();
158
+ const notify = () => {
159
+ if (destroyed) return;
160
+ for (const listener of listeners) {
161
+ try {
162
+ listener();
163
+ } catch {
164
+ }
165
+ }
166
+ };
167
+ const mountMain = () => {
168
+ if (destroyed || !mainSupported || !sidebarEntryEnabled || offPanel) return;
169
+ const res = ctx.slots.register({
170
+ name: "main",
171
+ key: SIDEBAR_PANEL_ID,
172
+ locale: ns,
173
+ inject: () => {
174
+ const extra = typeof options.injectProps === "function" ? options.injectProps() : {};
175
+ return Object.assign({ t: ctx.locale?.bind ? ctx.locale.bind(ns) : void 0 }, extra);
176
+ }
177
+ }, (ownerProps) => h("div", { className: "dsham_mainPanel" }, renderPanel(ownerProps)));
178
+ offPanel = typeof res === "function" ? res : () => {
179
+ };
180
+ };
181
+ const unmountMain = () => {
182
+ if (offPanel) {
183
+ try {
184
+ offPanel();
185
+ } catch {
186
+ }
187
+ offPanel = null;
188
+ }
189
+ };
190
+ const mountRow = () => {
191
+ if (destroyed || !sidebarSupported || !sidebarEntryEnabled || offRow) return;
192
+ const res = ctx.slots.register({
193
+ name: "sidebar.panellist",
194
+ id: SIDEBAR_PANEL_ID,
195
+ order: SIDEBAR_PANEL_ORDER,
196
+ label: () => ctx.locale.bind(ns)("archives.title"),
197
+ locale: ns
198
+ }, (ownerProps) => ArchiveSidebarIcon(ownerProps && typeof ownerProps === "object" && "size" in ownerProps ? ownerProps : void 0));
199
+ offRow = typeof res === "function" ? res : () => {
200
+ };
201
+ };
202
+ const unmountRow = () => {
203
+ if (offRow) {
204
+ try {
205
+ offRow();
206
+ } catch {
207
+ }
208
+ offRow = null;
209
+ }
210
+ };
211
+ const sync = () => {
212
+ if (destroyed) return;
213
+ if (sidebarEntryEnabled) {
214
+ mountMain();
215
+ mountRow();
216
+ } else {
217
+ if (layoutService?.panelInfo && typeof layoutService.selectPanel === "function") {
218
+ try {
219
+ if (layoutService.panelInfo.getSnapshot().activePanelId === SIDEBAR_PANEL_ID) {
220
+ layoutService.selectPanel(null);
221
+ }
222
+ } catch {
223
+ }
224
+ }
225
+ unmountRow();
226
+ unmountMain();
227
+ }
228
+ };
229
+ const setEnabled = async (next) => {
230
+ if (destroyed) return sidebarEntryEnabled;
231
+ const currentEpoch = ++epoch;
232
+ sidebarEntryEnabled = next;
233
+ sync();
234
+ notify();
235
+ try {
236
+ await callApi("/sidebar-entry", {
237
+ method: "POST",
238
+ headers: { "content-type": "application/json" },
239
+ body: JSON.stringify({ enabled: next })
240
+ });
241
+ return next;
242
+ } catch (err) {
243
+ if (currentEpoch === epoch) {
244
+ sidebarEntryEnabled = !next;
245
+ sync();
246
+ notify();
247
+ }
248
+ throw err;
249
+ }
250
+ };
251
+ let onlineUpdateEnabled = true;
252
+ let onlineUpdateLocked = false;
253
+ let updateEpoch = 0;
254
+ const setOnlineUpdate = async (next) => {
255
+ if (destroyed || onlineUpdateLocked) return onlineUpdateEnabled;
256
+ const currentEpoch = ++updateEpoch;
257
+ onlineUpdateEnabled = next;
258
+ notify();
259
+ if (typeof options.onOnlineUpdateChange === "function") {
260
+ try {
261
+ options.onOnlineUpdateChange(next);
262
+ } catch {
263
+ }
264
+ }
265
+ try {
266
+ await callApi("/online-update", {
267
+ method: "POST",
268
+ headers: { "content-type": "application/json" },
269
+ body: JSON.stringify({ enabled: next })
270
+ });
271
+ if (currentEpoch === updateEpoch && typeof options.onOnlineUpdateChange === "function") {
272
+ try {
273
+ options.onOnlineUpdateChange(next);
274
+ } catch {
275
+ }
276
+ }
277
+ return next;
278
+ } catch (err) {
279
+ if (currentEpoch === updateEpoch) {
280
+ onlineUpdateEnabled = !next;
281
+ notify();
282
+ if (typeof options.onOnlineUpdateChange === "function") {
283
+ try {
284
+ options.onOnlineUpdateChange(!next);
285
+ } catch {
286
+ }
287
+ }
288
+ }
289
+ throw err;
290
+ }
291
+ };
292
+ const loadEpoch = epoch;
293
+ callApi("/sidebar-entry").then((res) => {
294
+ if (!destroyed && epoch === loadEpoch && res && typeof res.enabled === "boolean") {
295
+ sidebarEntryEnabled = res.enabled;
296
+ sync();
297
+ notify();
298
+ }
299
+ }).catch(() => {
300
+ });
301
+ const loadUpdateEpoch = updateEpoch;
302
+ callApi("/online-update").then((res) => {
303
+ if (!destroyed && updateEpoch === loadUpdateEpoch && res && typeof res.enabled === "boolean") {
304
+ onlineUpdateEnabled = res.enabled;
305
+ if (res.locked === true) onlineUpdateLocked = true;
306
+ notify();
307
+ if (typeof options.onOnlineUpdateChange === "function") {
308
+ try {
309
+ options.onOnlineUpdateChange(onlineUpdateEnabled);
310
+ } catch {
311
+ }
312
+ }
313
+ }
314
+ }).catch(() => {
315
+ });
316
+ const hostCtx = ctx;
317
+ if (typeof hostCtx.inject === "function") {
318
+ hostCtx.inject(["layout"], (scoped) => {
319
+ if (!destroyed && scoped.layout) layoutService = scoped.layout;
320
+ });
321
+ }
322
+ ctx.slots.inject("main", () => {
323
+ if (destroyed) return;
324
+ mainSupported = true;
325
+ mountMain();
326
+ return () => {
327
+ mainSupported = false;
328
+ unmountMain();
329
+ };
330
+ });
331
+ ctx.slots.inject("sidebar.panellist", () => {
332
+ if (destroyed) return;
333
+ sidebarSupported = true;
334
+ mountRow();
335
+ return () => {
336
+ sidebarSupported = false;
337
+ unmountRow();
338
+ };
339
+ });
340
+ ctx.slots.inject("plugins.bundle.config", () => {
341
+ if (destroyed) return;
342
+ return ctx.slots.register({
343
+ name: "plugins.bundle.config",
344
+ key: BUNDLE_KEY,
345
+ locale: ns
346
+ }, (ownerProps) => {
347
+ if (ownerProps && ownerProps.view === "summary") return null;
348
+ return h(ArchiveBundleConfigCardWrapper, {
349
+ t: ctx.locale.bind(ns),
350
+ getEnabled: () => sidebarEntryEnabled,
351
+ setSidebar: setEnabled,
352
+ getOnlineUpdateEnabled: () => onlineUpdateEnabled,
353
+ setOnlineUpdate,
354
+ getOnlineUpdateLocked: () => onlineUpdateLocked,
355
+ subscribe: (fn) => {
356
+ listeners.add(fn);
357
+ return () => {
358
+ listeners.delete(fn);
359
+ };
360
+ }
361
+ });
362
+ });
363
+ });
364
+ return {
365
+ isEnabled: () => sidebarEntryEnabled,
366
+ isSupported: () => sidebarSupported,
367
+ setEnabled,
368
+ isOnlineUpdateEnabled: () => onlineUpdateEnabled,
369
+ isOnlineUpdateLocked: () => onlineUpdateLocked,
370
+ setOnlineUpdate,
371
+ destroy: () => {
372
+ if (destroyed) return;
373
+ destroyed = true;
374
+ epoch++;
375
+ updateEpoch++;
376
+ layoutService = void 0;
377
+ listeners.clear();
378
+ unmountRow();
379
+ unmountMain();
380
+ }
381
+ };
382
+ }
383
+ export {
384
+ ArchiveBundleConfigCard,
385
+ ArchiveSidebarIcon,
386
+ BUNDLE_KEY,
387
+ SIDEBAR_ENTRY_STYLE_ID,
388
+ SIDEBAR_PANEL_ID,
389
+ SIDEBAR_PANEL_ORDER,
390
+ ensureSidebarEntryStyle,
391
+ setupSidebarEntry,
392
+ sidebarEntryCss
393
+ };