给侧栏会话上锁,防止误归档后从列表消失
29.7/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/28 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Setsuna-Kitahara/dsh-session-lock该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:仅索引
是什么
dsh 原生插件 · browser
装得上吗
本站尚未做安装检查
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 9 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件发起外部网络请求
查看扫描证据(共 11 处,展示前 10 条)
- 读取本地文件lib/index.js:171
projects = await readdir(sessionsRoot, { withFileTypes: true }); - 读取本地文件lib/index.js:181
sessionDirs = await readdir(projectPath, { withFileTypes: true }); - 读取本地文件lib/index.js:190
files = await readdir(dirPath); - 读取本地文件lib/index.js:198
const info = await stat(join(dirPath, file)); - 读取本地文件lib/index.js:223
const raw = await readFile(join(dshHome, 'storages', 'session_projcache', 'sessions', `${sessionId}.json`), 'utf8'); - 读取本地文件lib/index.js:261
state = normalizeState(JSON.parse(await readFile(statePath, 'utf8'))); - 写入本地文件lib/index.js:275
await mkdir(dataDir, { recursive: true }); - 写入本地文件lib/index.js:277
await writeFile(temp, payload, 'utf8'); - 写入本地文件lib/index.js:278
await rename(temp, statePath); - 发起网络请求lib/client.js:78
const res = await fetch(`${API}${path}`, {
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
给 DSH Web 侧边栏加上按会话生效的锁;客户端归档已锁定的会话会被拒绝 | DSH 会话锁插件(由 DeepSeek Harness 维护)
README
由 DeepSeek 最新模型翻译生成dsh-session-lock
给 DeepSeek Harness(DSH)的会话上一把锁:锁住的会话,浏览器侧的归档入口都动不了它。
DSH 侧栏的归档会把会话从列表里藏起来,数据仍留在磁盘上。
这个插件给侧栏每一行加一个锁按钮;锁住的会话经客户端 workspaces.archiveSession 归档时会被拒绝——不管是 ⋮ 菜单,还是别的插件调用同一个客户端方法。直接打到宿主注册表(workspaceRegistry.archiveSession)的调用不会被拦截。宿主半边还暴露一个 archive-inactive 路由,用来批量归档陈旧会话,始终跳过锁住的会话。
MIT · 零运行时依赖 · 需要 DSH >= 0.1.5-rc.1
为什么需要它
DSH 侧栏的「归档」是一个隐藏但不删数据的动作:点一下,会话就从列表里消失了。
- 手滑点错菜单,正在跟的会话就不见了;
- 别的插件走客户端 workspaces.archiveSession 批量归档时,分不清哪些是你还要用的;
- 归档是不可逆的 UI 操作(数据还在 $DSH_HOME/sessions/ 里,但要找回来得手动翻目录)。
dsh-session-lock 把这些会话挑出来锁上:锁清单存在宿主里,和浏览器无关;锁定之后经客户端触发的归档都会被拒绝,并且告诉你原因。
展开完整文档(README 剩余 1 节)
dsh-session-lock
给 DeepSeek Harness(DSH)的会话上一把锁:锁住的会话,浏览器侧的归档入口都动不了它。
DSH 侧栏的归档会把会话从列表里藏起来,数据仍留在磁盘上。
这个插件给侧栏每一行加一个锁按钮;锁住的会话经客户端 workspaces.archiveSession 归档时会被拒绝——不管是 ⋮ 菜单,还是别的插件调用同一个客户端方法。直接打到宿主注册表(workspaceRegistry.archiveSession)的调用不会被拦截。宿主半边还暴露一个 archive-inactive 路由,用来批量归档陈旧会话,始终跳过锁住的会话。
MIT · 零运行时依赖 · 需要 DSH >= 0.1.5-rc.1
为什么需要它
DSH 侧栏的「归档」是一个隐藏但不删数据的动作:点一下,会话就从列表里消失了。
- 手滑点错菜单,正在跟的会话就不见了;
- 别的插件走客户端 workspaces.archiveSession 批量归档时,分不清哪些是你还要用的;
- 归档是不可逆的 UI 操作(数据还在 $DSH_HOME/sessions/ 里,但要找回来得手动翻目录)。
dsh-session-lock 把这些会话挑出来锁上:锁清单存在宿主里,和浏览器无关;锁定之后经客户端触发的归档都会被拒绝,并且告诉你原因。
特性
| 特性 | 说明 |
| 侧栏锁按钮 | 每个会话行一个 20×20 的锁图标:空心锁 = 未锁,琥珀色实心锁 = 已锁,同时该行左侧有一条琥珀色细边 |
| 客户端侧拦截 | 经客户端 workspaces.archiveSession 的归档——侧栏 ⋮ 菜单、其它插件调用的同一个方法——都会被拦下,弹提示说明原因,会话不会被隐藏;同时以拒绝(reject)表态,官方那句「会话已归档」的成功提示和「撤销」按钮不会再出现。直接打到宿主 workspaceRegistry.archiveSession 的调用不在拦截范围内 |
| 宿主持久化 | 锁清单在 $DSH_HOME/plugin-data/dsh-session-lock/locks.json,换浏览器、清缓存、重启 dsh 都在 |
| 批量归档不活跃会话 | 宿主路由 POST /plugins/dsh-session-lock/archive-inactive,供每日维护任务调用;上锁的会话永远被跳过 |
| 零运行时依赖 | 浏览器半边是纯 DOM + 一个 fetch,不 require 任何宿主内部包,宿主内部包改名不会影响它 |
| 只读安全 | 除锁清单外不写任何东西;归档走进程内官方注册表,不直接改 storages/workspace.json |
安装
需要 DSH >= 0.1.5-rc.1;<profile> 是你的配置档名(多数人是 web)。
方式一:插件市场
如果你装了插件市场 dshmarket,直接在市场里搜 dsh-session-lock 安装即可(它会把包名写进 dsh.profile.bundles)。
方式二:命令行
1. 装依赖(dsh plugin 会把参数原样转发给 profile 目录里的 pnpm)
dsh plugin --profile web add dsh-session-lock
2. 把 "dsh-session-lock" 追加到 ~/.dsh/profiles/web/package.json 的 dsh.profile.bundles 列表里(bundles 决定哪些包会被挂载;只装依赖不写这一段,插件不会加载)
3. 重启 dsh(宿主半边不参与热加载):托管在 systemd 上就是
systemctl restart dsh-web,其它部署方式重启你启动 dsh 的那个进程
systemctl restart dsh-web
方式三:本地源码
git clone https://github.com/Setsuna-Kitahara/dsh-session-lock
dsh plugin --profile web add link:/path/to/dsh-session-lock
同样要把 "dsh-session-lock" 加进 dsh.profile.bundles,然后重启 dsh-web
卸载
dsh plugin --profile web remove dsh-session-lock
从 dsh.profile.bundles 里删掉那一行,重启 dsh-web
锁清单文件会留下,不需要时手动删 $DSH_HOME/plugin-data/dsh-session-lock/locks.json。
使用
1. 刷新一次浏览器页面(客户端半边会随 dsh-client-modules 一起加载)。
2. 侧栏每个会话行最左边出现一个锁按钮,点它就是锁定/解锁,整个按钮都能点(图标在按钮正中间,不会把点击吃掉)。
3. 锁定后试着从 ⋮ 菜单归档它:会被拦下,并弹出一条「该会话已锁定,未被归档」。
4. 批量归档不活跃会话(可选):
curl -sS -X POST http://127.0.0.1:3080/plugins/dsh-session-lock/archive-inactive \
-H 'x-dsh-session-lock: 1' -H 'content-type: application/json' \
-d '{"days":3,"dryRun":true}'
先 "dryRun":true 看候选列表,确认无误再去掉 dryRun 真归档。
HTTP 接口
所有路由都在 /plugins/dsh-session-lock/ 下,都要求 x-dsh-session-lock: 1 请求头(这是一个跨站表单发不出来的非简单头),并拒绝显式的 sec-fetch-site: cross-site。
| 路由 | 方法 | 说明 |
| /locks | GET | { ok, locked: string[], records } —— 当前锁清单 |
| /set | POST | { sessionId, locked, note? } —— 上锁/解锁一个会话 |
| /unlock-all | POST | 清空全部锁 |
| /health | GET | { ok, count, path } —— 锁清单路径与条数 |
| /archive-inactive | POST | { days?: 3, dryRun?: false, sessionIds?: string[] } —— 归档不活跃且未上锁的会话 |
archive-inactive 的判定与豁免:
- 口径:会话目录里 session..jsonl(.zstd) 的最新 mtime 早于 now - days24h,即视为不活跃。默认 3 天,上限 3650 天。
- 豁免顺序:已上锁 → 已归档 → 未到期 → 正在运行(ctx.sessions.get(id) 命中)→ 不在 sessionIds 白名单里(给了白名单时)。
- fail-closed:锁清单直接读盘、不用内存缓存,读不出来就整次拒绝(503),一个都不归档——宁可不归档,也不归档一个用户锁住的会话。
- 走 ctx.workspaceRegistry.archiveSession(),即官方进程内写路径;不要绕过它去改 $DSH_HOME/storages/workspace.json(那是运行中进程内存状态的镜像,外部改写会被下一次写回覆盖)。
- 单次最多归档 500 个;返回 { ok, dryRun, days, cutoff, scanned, candidates, archived, failed, truncated, skipped{locked,live,alreadyArchived,recent} }。
- 刻意不做回环(loopback)围栏:本插件常见部署是 nginx 反代域名访问的,socket 对端是本地代理、Host 是公网域名,回环围栏会把正常用法直接挡掉。防护靠上面那个自定义请求头。
工作原理
| 半边 | 文件 | 职责 |
| 宿主 | lib/index.js | 锁账本(临时文件 + rename 原子整文件写)+ 五条 HTTP 路由(含 archive-inactive) |
| 浏览器 | lib/client.js | 包装 workspaces.archiveSession、给侧栏会话行注入锁按钮、提示条 |
| 自测 | test/*.test.mjs | 用假的 cordis ctx / DOM 驱动真代码,离线跑,不碰任何真实 DSH 状态 |
几个实现上的点,读代码前先知道会省事:
- 浏览器半边从会话行的 React fiber 上读会话 id。侧栏不把会话 id 写进 DOM,fiber 上 row 组件的 props 是唯一稳定的句柄;读不到就不装饰该行——宁可少画一个按钮,也不猜错会话。
- 拦截器包裹的是 workspaces.archiveSession 这个方法引用,并每 3 秒重新断言一次:同级插件(以及本插件自己的 HMR 重载)可能替换掉它。
- 锁定时必须 reject,不能 resolve。 调用方(DSH 官方的 uiWorkspace.archiveSession、dsh-archived-chats 的通知拦截器、批量归档)都把「promise resolve」读成「归档成功了」:于是官方会弹「会话已归档,可撤销」并清空主视图,dsh-archived-chats 再弹一次自己的已归档提示,而这个插件同时又在说「未被归档」——一次点击,屏幕上既有成功又有失败。现在返回的是带 code: "session-locked" 的 SessionLockedError(message 就是提示条那句话),成功路径因此保持安静,用户只看到拒绝的那一条。test/archive-guard.test.mjs 守着这条语义。
- 按钮的重绘是幂等的:侧栏扫描挂在 MutationObserver(document.body) 上,任何 DOM 变更都会触发一轮重绘。因此 paintButton 只在图标状态真正变化时才写 innerHTML(.dsh-session-lock-btn>svg { pointer-events:none } 是第二道保险)。不要把它改成无条件 innerHTML:那会每帧替换 子节点,而浏览器在 mousedown 目标被移出 DOM 后不会再派发 click,结果是图标区域点不动、只有按钮边缘一圈有反应。
设计取舍
诚实地说清楚边界,比事后解释便宜:
- 只拦「归档」,不拦「彻底删除」。 删除走的是另一条链路(dsh-archived-chats 等),本插件不接管。
- 拦截面只到客户端。 包裹的是浏览器侧 workspaces.archiveSession 这一处方法引用;宿主侧 workspaceRegistry.archiveSession 没有被包装——别的插件或脚本绕过客户端、直接调宿主注册表时,锁不会生效。要覆盖那一条,需要在宿主侧包装并向官方提,而不是在客户端假装拦住了。
- 浏览器半边是 fail-open,宿主半边是 fail-closed。 客户端拦截器读不到锁清单时账本视为空,归档会正常执行——在「拦下一个用户没锁的会话」和「放走一个用户锁了的会话」之间,客户端侧选了后者(读失败会在控制台留 warn 并弹提示条)。而 archive-inactive 相反:读不出锁清单就整次拒绝。
- 多标签页不做实时同步。 客户端在加载时读一次锁清单,之后每次切换锁都会重读;另一个标签页里的改动要等本页下一次切换或刷新才可见。宿主账本始终是唯一事实来源,所以不会写错。
- 宿主半边改动不参与 HMR(只有客户端半边会),改完要重启 dsh-web。
开发与测试
node test/archive-inactive.test.mjs # 宿主半边:路由、扫描口径、fail-closed
node test/client-decoration.test.mjs # 浏览器半边:注入、重绘稳定性、点击目标
node test/archive-guard.test.mjs # 浏览器半边:锁定时必须 reject、提示条只出现一次
或者
npm test
三个测试都是自足的:宿主的用临时 $DSH_HOME 造假会话目录,客户端的自带一个 ~120 行的 DOM stub,都不需要跑着一个 DSH,也不会写你的真实状态。
客户端那两个测试专门守着本文档上面提到的两个坑:25 次重绘必须零 innerHTML 写入、 节点身份必须不变(把 paintButton 改回无条件重写,它会立刻失败);锁定时必须 reject 而不是 resolve(改回 Promise.resolve(),「锁定的归档会 reject」那条会立刻失败)。
常见问题
点了锁按钮没反应? 先刷新一次页面(客户端半边随页面加载)。若仍无反应,打开浏览器控制台看有没有 session-lock 的报错:宿主路由挂了(例如没写进 dsh.profile.bundles,或忘了重启 dsh-web)时, /locks 会 404。
锁按钮为什么不是圆形的、为什么这么小? 它按侧栏行高做的 20×20,图标 14×14,跟 DSH 自带的行动作按钮同尺寸。悬停时会出现底色。
锁定会话会不会阻止 DSH 自己删数据? 不会。锁只作用于「归档」这一个动作。
能用 curl 从别的机器上锁吗? 接口只要求 x-dsh-session-lock: 1 头,能访问到 DSH 的 HTTP 端口就能调用;反代部署时请自行在反代层做访问控制。
更新日志
0.1.1
- 修复:归档一个上锁会话时,屏幕上会同时出现「会话已归档(可撤销)」和「该会话已锁定,未被归档」。
拦截器以前在锁定时返回 Promise.resolve(),上游把它当作归档成功,于是官方成功提示、 dsh-archived-chats 的已归档通知都会照常弹出(还会顺手清空主视图)。现在锁定时返回拒绝,成功路径不再触发,只剩插件的拒绝提示。
- 新增 test/archive-guard.test.mjs:锁定时必须 reject、不得调用真实归档、提示条只出现一次,解除拦截后原方法恢复。
0.1.0
首个公开版本。
- 修复:锁按钮只有边缘一圈能点、鼠标压到锁图标上就失效。原因有两个,都是客户端半边的: (1) 重绘循环每帧重写 innerHTML,把 mousedown 落点的 换掉,浏览器随即丢弃这次 click; (2) 按钮从未被加上 CSS 里定义的那个 class,尺寸/hover/锁定态样式其实一直是死的。
现在重绘幂等、class 由代码负责、图标对指针透明,并加了回归测试。
- 新增:宿主半边的测试改为自足运行(临时 $DSH_HOME),不再依赖真实机器上的会话目录。
许可
MIT © 2026 Setsuna-Kitahara
DSH 本身由 DeepSeek 维护,本插件是独立的第三方插件,与 DeepSeek 官方无关。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
Setsuna-Kitahara/dsh-session-lock
给侧栏会话上锁,防止误归档后从列表消失
相关插件 · 浏览器
智能体插件框架deepseek-ai/deepseek-harness
DeepSeek Harnessdsh是由 DeepSeek AI 开发的开源 agent harness智能体框架。
★ 270,422已汉化DSH
71.9
多设备 Agent 看板YaoApp/yao
✨ 所有 Agent 和工作区集中管理,覆盖你的每一台设备。任务看板追踪进度,桌面端、移动端、浏览器、API…
★ 8,147已汉化DSH
71.2
可扩展侧边栏工作台omdsh-dev/DSH-better-sidebar
DSH 的双工作台:原生右侧栏 + 自绘底部面板,并把 ctx.betterSidebar 服务开放给所有插件
★ 4,761已汉化DSH
71.1
浏览器自动化桥Tencent/BrowserSkill
你的 Agent。你的浏览器。连接,就能协作。
★ 9,967已汉化DSH
71
AI 小说创作工作台EthanYoQ/AI-Novel-Writer
AI 小说作家 / AI Novel Writer
★ 1,591已汉化DSH
70.7
智能体浏览器引擎platonai/Browser4
DeepSeek Harness 集成
★ 1,175已汉化DSH
70.4
这是你的仓库?认领条目