dsh-sessions-manager 3.4.2 → 3.5.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/README.en.md +6 -0
- package/README.md +6 -0
- package/lib/client.js +60 -17
- package/lib/client.js.map +2 -2
- package/lib/index.js +182 -58
- package/lib/index.js.map +4 -4
- package/package.json +1 -1
- package/src/client/index.jsx +42 -7
- package/src/compat/capabilities.js +34 -0
- package/src/compat/persistence.js +72 -0
- package/src/index.js +87 -55
package/README.en.md
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
A persistent DSH plugin (host + browser halves). Starting with v3.0.0, it covers both the **settings panel** and the **main sidebar**, so frequent session actions don't require opening Settings.
|
|
16
16
|
|
|
17
|
+
v3.5.0 adds a two-generation DSH persistence adapter. It understands both legacy header lists/`readFrom` and the new snapshot/`SessionHandle` API, and reports action-level capabilities to the UI. When a runtime has no verified safe purge or migration path, the corresponding controls explain why they are disabled and the Host rejects direct calls, preventing false success messages.
|
|
18
|
+
|
|
17
19
|
## Features
|
|
18
20
|
|
|
19
21
|
### Settings panel: Session Manager
|
|
@@ -149,6 +151,7 @@ lib/client.js pre-built client (ModuleLoader CJS handshake)
|
|
|
149
151
|
| POST | `/archived-sessions/storage` | Storage-usage aggregation (per-workspace ranking + largest sessions) `{ topN }` |
|
|
150
152
|
| POST | `/archived-sessions/auto-archive/settings` | Read or update the auto-archive policy `{ inactiveDays, skipStarred }`; reading lazily triggers the daily check |
|
|
151
153
|
| POST | `/archived-sessions/auto-archive/run` | Run a one-off auto-archive check immediately (ignores the daily throttle) |
|
|
154
|
+
| POST | `/archived-sessions/capabilities` | Report the persistence generation and per-action read/archive/trash/purge/cross-workspace-move capabilities |
|
|
152
155
|
|
|
153
156
|
> Deleting a session moves it to the recycle bin by default; only "permanently delete" inside the recycle bin physically removes the log. Permanently deleted sessions are hidden forever on the client side so they don't reappear in the sidebar or "ungrouped" due to DSH runtime caching.
|
|
154
157
|
|
|
@@ -157,6 +160,9 @@ lib/client.js pre-built client (ModuleLoader CJS handshake)
|
|
|
157
160
|
- **Supported platforms**: cross-platform (macOS / Windows / Linux) — wherever DSH runs the plugin runs; the host half is Node (about `^22.19` or `>=24`) and the client is React, with no OS-specific APIs.
|
|
158
161
|
- Works in both DSH Desktop and DSH web (same host + client halves).
|
|
159
162
|
- Peer dependencies are listed in `package.json`; `react` and `@deepseek-ai/*` are provided by the DSH runtime.
|
|
163
|
+
- `0.1.2-rc.1`: the existing read, archive, recycle-bin, permanent-purge, and cross-workspace move paths remain available.
|
|
164
|
+
- `0.1.3-alpha.1`: snapshot lists and read-only `SessionHandle` flows are supported. Permanent purge and cross-workspace move are disabled when no verified safe path is available; other management functions continue to work. The panel shows the effective capabilities.
|
|
165
|
+
- Unverified future runtimes expose only capabilities the plugin can safely identify; method presence alone is not presented as behavioral compatibility.
|
|
160
166
|
|
|
161
167
|
## License
|
|
162
168
|
|
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
一个 DSH 持久化插件(host + browser 双半)。v3.0.0 起同时覆盖「设置面板」与「主页侧边栏」两个入口,无需打开设置即可完成高频会话操作。
|
|
16
16
|
|
|
17
|
+
v3.5.0 新增 DSH 双代持久化适配:同时识别旧式 header 列表/`readFrom` 与新版 snapshot/`SessionHandle`,并向界面报告每项操作的实际能力。当前 Runtime 没有经过验证的安全删除或迁移路径时,相关按钮会显示禁用原因,Host 也会拒绝调用,避免出现“提示成功但日志没有删除/移动”的假成功。
|
|
18
|
+
|
|
17
19
|
## 功能
|
|
18
20
|
|
|
19
21
|
### 设置面板:会话管理
|
|
@@ -149,6 +151,7 @@ lib/client.js 预构建 client(ModuleLoader CJS handshake)
|
|
|
149
151
|
| POST | `/archived-sessions/storage` | 存储占用聚合(按工作区排行 + 最大的会话)`{ topN }` |
|
|
150
152
|
| POST | `/archived-sessions/auto-archive/settings` | 读取或更新自动归档策略 `{ inactiveDays, skipStarred }`;读取时惰性触发每日检查 |
|
|
151
153
|
| POST | `/archived-sessions/auto-archive/run` | 立即执行一次自动归档检查(忽略每日节流) |
|
|
154
|
+
| POST | `/archived-sessions/capabilities` | 返回当前持久化代际及读取、归档、回收站、永久删除、跨工作区移动能力 |
|
|
152
155
|
|
|
153
156
|
> 删除会话默认进入回收站,只有回收站内的「彻底删除」才会物理移除日志。被彻底删除的会话由前端永久隐藏,避免 DSH 运行时缓存使其重新出现在侧栏或「未分组」中。
|
|
154
157
|
|
|
@@ -157,6 +160,9 @@ lib/client.js 预构建 client(ModuleLoader CJS handshake)
|
|
|
157
160
|
- **适配系统**:跨平台(macOS / Windows / Linux)——只要 DSH 能在该系统运行即可;本插件 host 基于 Node(约 `^22.19` 或 `>=24`)、浏览器端为 React,不依赖特定操作系统 API。
|
|
158
161
|
- DSH Desktop / web 均可(同一套 host + client)。
|
|
159
162
|
- peerDependencies 见 `package.json`;`react`、`@deepseek-ai/*` 由 DSH 运行时提供。
|
|
163
|
+
- `0.1.2-rc.1`:现有读取、归档、回收站、永久删除和跨工作区移动能力保持可用。
|
|
164
|
+
- `0.1.3-alpha.1`:支持 snapshot 列表和 `SessionHandle` 只读流程;永久删除与跨工作区移动在缺少已验证安全路径时自动禁用,其余管理能力继续工作。能力以面板实际提示为准。
|
|
165
|
+
- 未经验证的未来 Runtime 默认只开放能够识别的安全能力;插件不会用方法存在与否冒充行为兼容。
|
|
160
166
|
|
|
161
167
|
## License
|
|
162
168
|
|
package/lib/client.js
CHANGED
|
@@ -278,6 +278,7 @@ function installSettingsNavIcons(ctx) {
|
|
|
278
278
|
function SessionPanel({ workspacesSvc }) {
|
|
279
279
|
const [sessions, setSessions] = (0, import_react.useState)(null);
|
|
280
280
|
const [workspaces, setWorkspaces] = (0, import_react.useState)([]);
|
|
281
|
+
const [capabilities, setCapabilities] = (0, import_react.useState)(null);
|
|
281
282
|
const initialPrefs = (0, import_react.useRef)(loadPanelPrefs()).current;
|
|
282
283
|
const [filter, setFilter] = (0, import_react.useState)(() => ["all", "active", "archived", "starred", "trash"].includes(initialPrefs.filter) ? initialPrefs.filter : "all");
|
|
283
284
|
const [query, setQuery] = (0, import_react.useState)("");
|
|
@@ -326,12 +327,14 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
326
327
|
setError(null);
|
|
327
328
|
Promise.all([
|
|
328
329
|
postJSON("/archived-sessions/sessions", {}),
|
|
329
|
-
postJSON("/archived-sessions/workspaces", {})
|
|
330
|
-
|
|
330
|
+
postJSON("/archived-sessions/workspaces", {}),
|
|
331
|
+
postJSON("/archived-sessions/capabilities", {})
|
|
332
|
+
]).then(([s, works, caps]) => {
|
|
331
333
|
const purged = dsmLoadPurged();
|
|
332
334
|
const visible = (s.items || []).filter((x) => !purged.has(String(x.sessionId)));
|
|
333
335
|
setSessions(visible);
|
|
334
336
|
setWorkspaces(works.items || []);
|
|
337
|
+
setCapabilities(caps || null);
|
|
335
338
|
setSelected({});
|
|
336
339
|
setDelTarget(null);
|
|
337
340
|
setConfirmBatch(false);
|
|
@@ -428,6 +431,9 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
428
431
|
const w = workspaces.find((x) => x.workspaceId === id);
|
|
429
432
|
return w ? w.path : "";
|
|
430
433
|
};
|
|
434
|
+
const actionCapability = (name) => capabilities && capabilities.actions && capabilities.actions[name] || { available: false, reason: "\u6B63\u5728\u68C0\u67E5\u5F53\u524D DSH \u7684\u517C\u5BB9\u80FD\u529B\u2026" };
|
|
435
|
+
const canPurge = actionCapability("purge");
|
|
436
|
+
const canMove = actionCapability("move");
|
|
431
437
|
const archivedList = sessions ? sessions.filter((x) => x.archived) : [];
|
|
432
438
|
const activeList = sessions ? sessions.filter((x) => !x.archived) : [];
|
|
433
439
|
const starredList = starredOf(sessions);
|
|
@@ -511,7 +517,9 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
511
517
|
postJSON("/archived-sessions/trash/verify", {}).then((r) => {
|
|
512
518
|
setTrashBusy(null);
|
|
513
519
|
setTrashCheck(r);
|
|
514
|
-
|
|
520
|
+
if (r.missing) showToast(`\u53D1\u73B0 ${r.missing} \u6761\u65E5\u5FD7\u7F3A\u5931`);
|
|
521
|
+
else if (r.unverified) showToast(`${r.unverified} \u6761\u65E5\u5FD7\u4F4D\u7F6E\u7531\u5F53\u524D Runtime \u7BA1\u7406\uFF0C\u65E0\u6CD5\u76F4\u63A5\u6838\u9A8C`);
|
|
522
|
+
else showToast("\u56DE\u6536\u7AD9\u6821\u9A8C\u901A\u8FC7");
|
|
515
523
|
}).catch((e) => {
|
|
516
524
|
setTrashBusy(null);
|
|
517
525
|
setError(String(e && e.message || e));
|
|
@@ -612,6 +620,10 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
612
620
|
});
|
|
613
621
|
};
|
|
614
622
|
const doMove = (it) => {
|
|
623
|
+
if (!canMove.available) {
|
|
624
|
+
setError(canMove.reason || "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u8DE8\u5DE5\u4F5C\u533A\u79FB\u52A8");
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
615
627
|
const targetPath = moveMode === "new" ? newPath.trim() : wsPath(targetWs);
|
|
616
628
|
if (!targetPath) {
|
|
617
629
|
setError("\u8BF7\u9009\u62E9\u5DF2\u6709\u5DE5\u4F5C\u533A\u6216\u8F93\u5165\u65B0\u7684\u76EE\u6807\u76EE\u5F55\u8DEF\u5F84");
|
|
@@ -653,6 +665,10 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
653
665
|
});
|
|
654
666
|
};
|
|
655
667
|
const openMoveFor = (it) => {
|
|
668
|
+
if (!canMove.available) {
|
|
669
|
+
setError(canMove.reason || "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u8DE8\u5DE5\u4F5C\u533A\u79FB\u52A8");
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
656
672
|
if (openMove === it.sessionId) {
|
|
657
673
|
setOpenMove(null);
|
|
658
674
|
return;
|
|
@@ -738,17 +754,22 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
738
754
|
children: "\u22EF"
|
|
739
755
|
}
|
|
740
756
|
),
|
|
741
|
-
openMenu === it.sessionId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "more-menu", role: "menu", children: items.map(([id, label]) =>
|
|
742
|
-
"
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
757
|
+
openMenu === it.sessionId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "more-menu", role: "menu", children: items.map(([id, label]) => {
|
|
758
|
+
const blocked = id === "move" && !canMove.available;
|
|
759
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
760
|
+
"button",
|
|
761
|
+
{
|
|
762
|
+
type: "button",
|
|
763
|
+
role: "menuitem",
|
|
764
|
+
className: "more-item" + (id === "delete" ? " more-item-danger" : ""),
|
|
765
|
+
disabled: blocked,
|
|
766
|
+
title: blocked ? canMove.reason : void 0,
|
|
767
|
+
onClick: () => runMenu(id, it),
|
|
768
|
+
children: label
|
|
769
|
+
},
|
|
770
|
+
id
|
|
771
|
+
);
|
|
772
|
+
}) })
|
|
752
773
|
] });
|
|
753
774
|
};
|
|
754
775
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv", role: "region", "aria-label": "\u4F1A\u8BDD\u7BA1\u7406", children: [
|
|
@@ -1191,22 +1212,25 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
1191
1212
|
] }),
|
|
1192
1213
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-field", children: [
|
|
1193
1214
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u6C38\u4E45\u6E05\u7406" }),
|
|
1194
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", disabled: trashBusy !== null || !trash.length, onClick: () => setPurgeTarget("__all"), children: "\u6E05\u7A7A\u56DE\u6536\u7AD9" })
|
|
1215
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", disabled: trashBusy !== null || !trash.length || !canPurge.available, title: !canPurge.available ? canPurge.reason : void 0, onClick: () => setPurgeTarget("__all"), children: "\u6E05\u7A7A\u56DE\u6536\u7AD9" })
|
|
1195
1216
|
] })
|
|
1196
1217
|
] }),
|
|
1218
|
+
!canPurge.available && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-empty", role: "status", children: canPurge.reason }),
|
|
1197
1219
|
trashCheck && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: trashCheck.missing ? "archv-err" : "archv-empty", role: "status", children: [
|
|
1198
1220
|
"\u6821\u9A8C\u5B8C\u6210\uFF1A",
|
|
1199
1221
|
trashCheck.healthy,
|
|
1200
1222
|
" \u6761\u6B63\u5E38\uFF0C",
|
|
1201
1223
|
trashCheck.missing,
|
|
1202
|
-
" \u6761\u65E5\u5FD7\u7F3A\u5931\
|
|
1224
|
+
" \u6761\u65E5\u5FD7\u7F3A\u5931\uFF0C",
|
|
1225
|
+
trashCheck.unverified || 0,
|
|
1226
|
+
" \u6761\u65E0\u6CD5\u76F4\u63A5\u6838\u9A8C\u3002"
|
|
1203
1227
|
] }),
|
|
1204
1228
|
trash.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsm-trash-empty", children: "\u56DE\u6536\u7AD9\u4E3A\u7A7A\u3002\u5220\u9664\u7684\u4F1A\u8BDD\u4F1A\u5148\u8FDB\u5165\u8FD9\u91CC\uFF0C\u53EF\u6062\u590D\u6216\u5F7B\u5E95\u5220\u9664\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsm-trash-list", children: trash.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsm-trash-row", children: [
|
|
1205
1229
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-trash-name", title: t.sessionId, children: t.title || t.sessionId }),
|
|
1206
1230
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-trash-date", children: fmtDate(t.deletedAt) }),
|
|
1207
1231
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsm-trash-actions", children: [
|
|
1208
1232
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: trashBusy !== null, onClick: () => restoreTrash(t.sessionId), children: "\u6062\u590D" }),
|
|
1209
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", disabled: trashBusy !== null, onClick: () => setPurgeTarget(t.sessionId), children: "\u5F7B\u5E95\u5220\u9664" })
|
|
1233
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", disabled: trashBusy !== null || !canPurge.available, title: !canPurge.available ? canPurge.reason : void 0, onClick: () => setPurgeTarget(t.sessionId), children: "\u5F7B\u5E95\u5220\u9664" })
|
|
1210
1234
|
] })
|
|
1211
1235
|
] }, t.sessionId)) })
|
|
1212
1236
|
] }),
|
|
@@ -1319,6 +1343,17 @@ var dsmTrashIds = null;
|
|
|
1319
1343
|
var dsmServerPurgedIds = /* @__PURE__ */ new Set();
|
|
1320
1344
|
var dsmAuthoritativeTitles = /* @__PURE__ */ new Map();
|
|
1321
1345
|
var dsmTrashTick = 0;
|
|
1346
|
+
var dsmCapabilities = null;
|
|
1347
|
+
async function dsmLoadCapabilities() {
|
|
1348
|
+
try {
|
|
1349
|
+
dsmCapabilities = await postJSON("/archived-sessions/capabilities", {});
|
|
1350
|
+
} catch (e) {
|
|
1351
|
+
}
|
|
1352
|
+
return dsmCapabilities;
|
|
1353
|
+
}
|
|
1354
|
+
function dsmActionCapability(name) {
|
|
1355
|
+
return dsmCapabilities && dsmCapabilities.actions && dsmCapabilities.actions[name];
|
|
1356
|
+
}
|
|
1322
1357
|
async function dsmLoadTrashIds() {
|
|
1323
1358
|
try {
|
|
1324
1359
|
const r = await postJSON("/archived-sessions/sidebar-state", {});
|
|
@@ -1549,6 +1584,11 @@ function installSidebarSessionMenuAug() {
|
|
|
1549
1584
|
const move = mk("\u79FB\u52A8\u4F1A\u8BDD", ICON_MOVE, false);
|
|
1550
1585
|
const del = mk("\u5220\u9664\u4F1A\u8BDD", ICON_DEL, true);
|
|
1551
1586
|
const mark = mk(dsmLoadManual().has(info.id) ? "\u6807\u8BB0\u5DF2\u8BFB" : "\u6807\u8BB0\u672A\u8BFB", ICON_UNREAD, false);
|
|
1587
|
+
const moveCapability = dsmActionCapability("move");
|
|
1588
|
+
if (!moveCapability || !moveCapability.available) {
|
|
1589
|
+
move.btn.disabled = true;
|
|
1590
|
+
move.btn.title = moveCapability && moveCapability.reason || "\u6B63\u5728\u68C0\u67E5\u5F53\u524D\u7248\u672C\u7684\u79FB\u52A8\u80FD\u529B";
|
|
1591
|
+
}
|
|
1552
1592
|
if (mark.btn.firstChild) mark.btn.firstChild.style.color = "var(--dsw-alias-state-business-primary)";
|
|
1553
1593
|
const chev = document.createElement("span");
|
|
1554
1594
|
chev.style.cssText = "margin-left:auto;flex:none;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:1";
|
|
@@ -1785,6 +1825,8 @@ function installSidebarWorkspaceDrag() {
|
|
|
1785
1825
|
maybeRefresh();
|
|
1786
1826
|
}, true);
|
|
1787
1827
|
document.addEventListener("dragstart", (event) => {
|
|
1828
|
+
const moveCapability = dsmActionCapability("move");
|
|
1829
|
+
if (!moveCapability || !moveCapability.available) return;
|
|
1788
1830
|
const row = eventRow(event);
|
|
1789
1831
|
const item = row && sessionForRow(row);
|
|
1790
1832
|
if (!item || moving) return;
|
|
@@ -1864,6 +1906,7 @@ function installSidebarWorkspaceDrag() {
|
|
|
1864
1906
|
}
|
|
1865
1907
|
}
|
|
1866
1908
|
function apply(ctx) {
|
|
1909
|
+
dsmLoadCapabilities();
|
|
1867
1910
|
installSettingsNavIcons(ctx);
|
|
1868
1911
|
installSidebarSessionMenuAug();
|
|
1869
1912
|
installSidebarStatusDots();
|