dsh-session-control
已验证dsh-session-control · v1.1.0 · MIT · Web 界面
DSH web GUI session management and service control: delete sessions (including ungrouped ones, with graceful agent termination) from a Settings page, plus restart / shutdown the dsh web host. · dsh Web GUI 会话管理与服务控制:设置页一键删除会话(含未分组、优雅终止 agent)+ 重启/关闭宿主服务。
安装
dsh plugin add dsh-session-control 用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。
源码
发布到 npm 但没有公开仓库。安装前请检查包内容。
标签
作者
说明文档
dsh-session-control
DSH web GUI 会话管理与服务控制插件:设置页「会话管理」一键删除会话(含未分组 / 已归档 / 空会话,运行中的会话先优雅终止再物理删除),设置页「服务控制」重启 / 关闭 dsh Web 宿主(隐藏控制台,重启后页面自动恢复)。
一个包、两个功能,零修改 dsh 安装文件,与现有补丁互不干扰(路由挂 /dsh-sc/*,不占 RPC 路由)。
安装
发布到 npm 后:
dsh plugin --profile web add dsh-session-control
本地未发布时用 link 安装:
dsh plugin --profile web add link:D:\WorkSpacee\project-006-dsh-web插件化\code\plugin\dsh-session-control
安装后重启 dsh web,设置页出现「会话管理」(order 30)与「服务控制」(order 40)两个分区。
使用
- 会话管理:按工作区分组列出全部会话(含未分组桶),每行显示运行状态、归档标记与最近活动时间,垃圾桶按钮弹确认框。删除 = 优雅终止 live agent → 等待最终落盘(轮询
sessionPersistence.list())→ 从各 workspace 摘除账目 → 清归档标记 → 物理删除会话目录 → 发出workspace/session-deleted(宿主据此广播host/session-removed帧,侧边栏与所有标签页同步消失)。客户端删除成功后调用公开的sessions.refresh()重拉列表兜底;遇到session-not-found(会话已在别处删除)也立即刷新列表,幽灵条目不再残留。- 优雅终止是双层的,不依赖补丁:优先用
agentLoop.ownership.liveById(已打补丁 / 新版 dsh 原生带disposeById);缺失时回退到公开的AgentHandle——插件在宿主启动时包装ctx.agents.create/resume,把每个{ agent, dispose }句柄按 sessionId 捕获(lib/agent-handles.js),删除时直接handle.dispose()。因此未打补丁的 dsh(如 rc.2)也能删除空闲/挂载中的会话,不再误报「该会话正在使用中」。
- 优雅终止是双层的,不依赖补丁:优先用
- 服务控制:重启 = 分离拉起内置
web-restarter.mjs→ 3.5s 后优雅 dispose 自身 → 4s 重启器兜底强杀 → 隐藏控制台(non-detached powershell +Start-Process -WindowStyle Hidden)拉起新宿主 → 冒烟检查端口;客户端轮询 origin,观测到宕机窗口后等新宿主就绪再location.reload()。关闭 = 1s 后 dispose + exit,5s 硬退出兜底。
安全
所有路由仅接受同源回环请求(127.0.0.1 / ::1 / ::ffff:127.0.0.1,拒绝任何转发链头,Origin 必须与 Host 同源),与 dshmarket 的 trustedRestartRequest 同一纪律。
架构
| 文件 | 职责 |
|---|---|
package.json |
dsh.bundle.patch → cordis.patch.yml;dsh.client.inject 声明 client 依赖(connection/runtime/locale/ui-settings);exports["./client"] |
cordis.patch.yml |
插入 profile 层栈的插件条目 |
lib/index.js |
host 插件入口:ctx.inject(["webServer","workspaceRegistry","sessionPersistence"]),安装 AgentHandle 捕获并挂路由 |
lib/agent-handles.js |
包装 ctx.agents.create/resume 捕获 AgentHandle 并按 sessionId 索引;disposeById 优雅终止(未打补丁 dsh 的终止路径) |
lib/routes.js |
`POST /dsh-sc/delete |
lib/delete-session.js |
会话删除流程(对齐 project-006 补丁语义,全部走公有面 + 运行时探测降级) |
lib/lifecycle.js |
重启器拉起(保留当前 argv)与优雅关闭调度 |
lib/web-restarter.mjs |
分离重启器(隐藏控制台纪律,禁止改动 spawn 方式) |
client/client.js |
__ModuleLoader__ bundle:两个 settings.section + zh/en 文案 + 自注入 CSS |
非公开面(ownership.liveById、registry 内部 state 助手)全部 optional-chain + 优雅降级:有就用最贴近补丁的原生路径,没有就回退到公开的 AgentHandle,缺失时功能降级为「尽力而为 / 明确报错」而非崩溃。
开发
node --check lib\*.js lib\*.mjs client\client.js # 静态校验
node test\verify-client.cjs # 客户端 bundle 结构校验
隔离 E2E(不碰真实 profile / 会话):
$env:DSH_HOME = "D:\Temp\opencode\dsh-e2e"
dsh plugin --profile e2e add link:D:\...\dsh-session-control
# 编辑 D:\Temp\opencode\dsh-e2e\profiles\e2e\package.json 的 bundles 加上 @deepseek-ai/dsh-web-app
node "D:\Program Files\nodejs\node_global\node_modules\@deepseek-ai\dsh\lib\bin.js" --profile e2e --port 18080
伪造会话 artifact(zstd,header 的 cwd 必须与目录一致,目录名规则 --<encoded-cwd>--/<id>/):
const h = JSON.stringify({type:"session",version:0,id:"e2e-session-0001",createdAt:Date.now(),delegationDepth:0,cwd:"e2e-fake"});
fs.writeFileSync(".../sessions/--e2e-fake--/e2e-session-0001/session.jsonl.zstd", zlib.zstdCompressSync(Buffer.from(h+"\n")));
发布
npm publish --access public
版本
- 1.1.0:优雅终止不依赖补丁——
agentLoop.ownership.liveById缺失(未打补丁 / rc.2)时,回退到包装ctx.agents.create/resume捕获的公开AgentHandle来按会话终止 agent,修复「空闲会话误报正在使用中,无法删除」。zero 修改 dsh 文件。 - 1.0.1:修复删除会话失败——客户端改用公开的
sessions.refresh()(不再调用私有的recordMutation);session-not-found时刷新列表移除幽灵条目;host 侧删除成功后发出workspace/session-deleted,让所有客户端经host/session-removed帧同步移除。 - 1.0.0:会话删除 + 服务控制,目标
@deepseek-ai/[email protected]。