dsh-sessions-manager 3.5.2 → 3.5.4

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-sessions-manager",
3
3
  "description": "DSH 设置面板会话管理器:归档 / 恢复 / 彻底删除 / 移动到其他工作区,带工作区标签与会话日期;统一「会话管理」面板。Session manager for the DeepSeek Harness settings panel — archive / restore / permanently delete / move sessions across workspaces, with workspace tags & session dates in one unified panel.",
4
- "version": "3.5.2",
4
+ "version": "3.5.4",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {
@@ -1353,9 +1353,15 @@ function installSidebarSessionMenuAug() {
1353
1353
  sub.setAttribute('data-dsm-sub', '')
1354
1354
  sub.innerHTML = '<div class="dsm-sub-loading">加载工作区…</div>'
1355
1355
  const r = moveBtn.getBoundingClientRect()
1356
- sub.style.top = Math.max(8, Math.round(r.top - 4)) + 'px'
1357
- sub.style.left = Math.round(r.right + 10) + 'px'
1356
+ // Fixed-position card: once the async content renders it can be tall or
1357
+ // wide enough to hang past the viewport edges, so re-clamp both anchors
1358
+ // against the rendered size.
1359
+ const placeSub = () => {
1360
+ sub.style.top = Math.max(8, Math.min(Math.round(r.top - 4), window.innerHeight - sub.offsetHeight - 8)) + 'px'
1361
+ sub.style.left = Math.max(8, Math.min(Math.round(r.right + 10), window.innerWidth - sub.offsetWidth - 8)) + 'px'
1362
+ }
1358
1363
  document.body.appendChild(sub)
1364
+ placeSub()
1359
1365
  const closeSub = () => {
1360
1366
  activeSubClose = null
1361
1367
  if (sub.parentNode) sub.remove()
@@ -1384,7 +1390,7 @@ function installSidebarSessionMenuAug() {
1384
1390
  const items = ws.items || []
1385
1391
  const cur = (sess.items || []).find((x) => x.sessionId === info.id)
1386
1392
  const curPath = cur ? cur.workspacePath : (info.cwd || null)
1387
- if (!items.length) { sub.innerHTML = '<div class="dsm-sub-empty">(暂无可用工作区)</div>'; return }
1393
+ if (!items.length) { sub.innerHTML = '<div class="dsm-sub-empty">(暂无可用工作区)</div>'; placeSub(); return }
1388
1394
  sub.innerHTML = ''
1389
1395
  items.forEach((w) => {
1390
1396
  const isCur = !!curPath && w.path === curPath
@@ -1414,8 +1420,10 @@ function installSidebarSessionMenuAug() {
1414
1420
  })
1415
1421
  sub.appendChild(b)
1416
1422
  })
1423
+ placeSub()
1417
1424
  }).catch((e) => {
1418
1425
  sub.innerHTML = '<div class="dsm-sub-err">' + escapeHtml(String((e && e.message) || e)) + '</div>'
1426
+ placeSub()
1419
1427
  })
1420
1428
  }
1421
1429
  const openDeleteConfirm = (id) => {
@@ -1510,6 +1518,11 @@ function installSidebarSessionMenuAug() {
1510
1518
  viewport.appendChild(del.wrap)
1511
1519
  // 标记未读 goes to the very top of the menu (above DSH's native items).
1512
1520
  viewport.insertBefore(mark.wrap, viewport.firstElementChild)
1521
+ // DSH's portalled Menu clamped its position using the height it measured
1522
+ // before this shim injected rows, so the grown card can hang past the
1523
+ // viewport bottom and cover the sidebar foot. Reuse the host's own
1524
+ // placement by firing the resize event it listens for while open.
1525
+ window.dispatchEvent(new Event('resize'))
1513
1526
  }
1514
1527
 
1515
1528
  const seen = new WeakSet()
@@ -13,6 +13,8 @@
13
13
  // is an opaque change token valid ONLY within one service instance and one
14
14
  // session id (see src/session-meta-cache.js).
15
15
 
16
+ import { locateSessionArtifacts } from '../handle-era-paths.js'
17
+
16
18
  const DEFAULT_CHUNK = 400
17
19
  // 防御上限:一次 inspect 的分块循环绝不能无限自旋(后端 read 行为异常时快速失败)。
18
20
  const MAX_CHUNKS = 20000
package/src/index.js CHANGED
@@ -554,7 +554,9 @@ export function apply(ctx) {
554
554
  const entry = store.items.find((t) => String(t.sessionId) === sid)
555
555
  let originalPath = entry && typeof entry.originalPath === 'string' ? entry.originalPath : null
556
556
  if (!originalPath && header) {
557
- const loc = persistence.locate(header)
557
+ // handle 时代官方收走了 locate,用守卫式推导(root → 目录结构 → id 归属)
558
+ // 代替:推导成功且文件在盘 = 真核验通过,而不是标注 unverified 放行。
559
+ const loc = await persistence.locateVerified(header).catch(() => null)
558
560
  if (loc && typeof loc.path === 'string') originalPath = loc.path
559
561
  }
560
562
  if (originalPath) {
@@ -625,12 +627,31 @@ export function apply(ctx) {
625
627
  const entry = store.items.find((t) => String(t.sessionId) === sid)
626
628
  if (!entry) { const error = new Error('回收站中找不到该会话'); error.status = 404; throw error }
627
629
  let target = null
630
+ let locatedHeader = null
628
631
  try {
629
632
  const entries = await persistence.listEntries()
630
633
  const current = entries.find((entry) => entry.id === sid)
631
- const located = current && persistence.locate(current.header)
634
+ locatedHeader = current ? current.header : null
635
+ // locateVerified:legacy 走官方 locate;handle 时代官方收走了 locate,
636
+ // 改由三层守卫推导(root → 目录结构 → id 归属),失败返回 null。
637
+ // 比仅靠回收站条目里的 originalPath 更可靠:originalPath 过期或缺失时
638
+ // 仍能从当前存储布局重新推导。
639
+ const located = current ? await persistence.locateVerified(current.header) : null
632
640
  if (located && typeof located.path === 'string') target = located.path
633
641
  } catch (e) {}
642
+ // 幽灵记录兜底:会话不在 list 里(后端索引滞后/索引缺失)但日志仍在盘上。
643
+ // 用 statSession 拿官方 header,再走守卫推导,避免退化成「只删单文件」
644
+ // 或直接 409 拒绝。
645
+ if (!locatedHeader && typeof persistence.statSession === 'function') {
646
+ try {
647
+ const snap = await persistence.statSession(sid)
648
+ if (snap && snap.header) {
649
+ locatedHeader = snap.header
650
+ const located = await persistence.locateVerified(snap.header)
651
+ if (located && typeof located.path === 'string') target = located.path
652
+ }
653
+ } catch (e) {}
654
+ }
634
655
  if (!target && typeof entry.originalPath === 'string') target = entry.originalPath
635
656
  if (!target) {
636
657
  const error = new Error('无法确认该会话的物理日志位置,已停止永久删除')