🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

会话管家 citydirector/dsh-session-manager

DeepSeek Harness
citydirector17 天前有提交已提供中文文档扫描:含敏感能力

离线整理、修复并清理 DSH 会话数据

29.5/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/22 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add citydirector/dsh-session-manager
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么
dsh 原生插件 · market
装得上吗
本站尚未做安装检查
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 17 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件
查看扫描证据(共 78 处,展示前 55 条)
  • 写入本地文件
    bin/dsm.mjs:354
    copyFileSync(b.file, b.target)
  • 删除本地文件
    bin/dsm.mjs:357
    if (id) for (const p of projcachePaths(home, id)) rmSync(p, { force: true })
  • 读取本地文件
    src/store.mjs:38
    export function readLog(path) { return decode(readFileSync(path)) }
  • 写入本地文件
    src/store.mjs:84
    writeFileSync(tmp, body)
  • 写入本地文件
    src/store.mjs:85
    renameSync(tmp, path)
  • 读取本地文件
    src/store.mjs:103
    if (!existsSync(p)) return undefined
  • 读取本地文件
    src/store.mjs:104
    return JSON.parse(readFileSync(p, 'utf8'))
  • 读取本地文件
    src/store.mjs:109
    if (existsSync(p)) {
  • 写入本地文件
    src/store.mjs:111
    mkdirSync(bdir, { recursive: true })
  • 读取本地文件
    src/store.mjs:112
    if (!existsSync(join(bdir, 'workspace.json'))) copyFileSync(p, join(bdir, 'workspace.json'))
  • 写入本地文件
    src/store.mjs:112
    if (!existsSync(join(bdir, 'workspace.json'))) copyFileSync(p, join(bdir, 'workspace.json'))
  • 写入本地文件
    src/store.mjs:115
    writeFileSync(tmp, JSON.stringify(ws))
  • 写入本地文件
    src/store.mjs:116
    renameSync(tmp, p)
  • 读取本地文件
    src/store.mjs:136
    export function safeDir(d) { try { return readdirSync(d) } catch { return [] } }
  • 写入本地文件
    src/tail.mjs:90
    mkdirSync(dir, { recursive: true })
  • 写入本地文件
    src/tail.mjs:91
    copyFileSync(session.latest.path, join(dir, session.latest.file))
  • 读取本地文件
    src/tail.mjs:94
    if (existsSync(p)) { mkdirSync(join(dir, 'projcache'), { recursive: true }); renameSync(p, join(dir, 'projcache', basename(p))) }
  • 写入本地文件
    src/tail.mjs:94
    if (existsSync(p)) { mkdirSync(join(dir, 'projcache'), { recursive: true }); renameSync(p, join(dir, 'projcache', basename(p))) }
  • 写入本地文件
    src/tail.mjs:94
    if (existsSync(p)) { mkdirSync(join(dir, 'projcache'), { recursive: true }); renameSync(p, join(dir, 'projcache', basename(p))) }
  • 读取本地文件
    src/discover.mjs:28
    if (existsSync(join(dir, 'storages', 'workspace.json'))) return true
  • 读取本地文件
    src/discover.mjs:30
    if (!existsSync(sess)) return false
  • 读取本地文件
    src/discover.mjs:32
    for (const proj of readdirSync(sess, { withFileTypes: true })) {
  • 读取本地文件
    src/discover.mjs:34
    for (const s of readdirSync(join(sess, proj.name), { withFileTypes: true })) {
  • 读取本地文件
    src/discover.mjs:36
    if (readdirSync(join(sess, proj.name, s.name)).some((f) => /^session(\.v\d+)?\.jsonl/.test(f))) return true
  • 读取本地文件
    src/discover.mjs:42
    return ['cordis.patch.yml', '.credentials.yaml', 'profiles', 'llm-deepseek'].some((f) => existsSync(join(dir, f)))
  • 写入本地文件
    src/discover.mjs:73
    mkdirSync(dir, { recursive: true })
  • 写入本地文件
    src/discover.mjs:74
    writeFileSync(join(dir, 'config.json'), JSON.stringify(cfg, null, 2))
  • 执行外部命令固定命令
    src/discover.mjs:100
    const raw = execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', PS_PROBE],
  • 执行外部命令固定命令
    src/guard.mjs:19
    const out = execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', ps], { encoding: 'utf8', timeout: 30000, windowsHide: true, m
  • 执行外部命令固定命令
    src/guard.mjs:23
    const out = execFileSync('ps', ['-eo', 'pid=,args='], { encoding: 'utf8', timeout: 15000, maxBuffer: 64 << 20 })
  • 写入本地文件
    src/guard.mjs:60
    mkdirSync(dir, { recursive: true })
  • 读取本地文件
    src/guard.mjs:62
    if (existsSync(lock)) {
  • 读取本地文件
    src/guard.mjs:64
    try { pid = JSON.parse(readFileSync(lock, 'utf8')).pid } catch { }
  • 写入本地文件
    src/guard.mjs:69
    writeFileSync(lock, JSON.stringify({ pid: process.pid, at: new Date().toISOString() }))
  • 读取本地文件
    src/guard.mjs:70
    const release = () => { try { if (JSON.parse(readFileSync(lock, 'utf8')).pid === process.pid) rmSync(lock) } catch { } }
  • 删除本地文件
    src/guard.mjs:70
    const release = () => { try { if (JSON.parse(readFileSync(lock, 'utf8')).pid === process.pid) rmSync(lock) } catch { } }
  • 读取本地文件
    src/runtime.mjs:20
    for (const p of preferred) if (p && existsSync(p)) return p
  • 执行外部命令来自配置/变量
    src/runtime.mjs:24
    const hit = execFileSync(cmd, ['node'], { encoding: 'utf8', windowsHide: true }).split(/ ? /).find(Boolean)
  • 读取本地文件
    src/runtime.mjs:25
    if (hit && existsSync(hit.trim())) return hit.trim()
  • 读取本地文件
    src/ops.mjs:31
    if (statSync(session.latest.path).size > EMPTY_PROBE_LIMIT) return false
  • 写入本地文件
    src/ops.mjs:50
    mkdirSync(batch, { recursive: true })
  • 读取本地文件
    src/ops.mjs:55
    if (existsSync(entry)) throw new Error(`回收站条目已存在:${entry}`)
  • 写入本地文件
    src/ops.mjs:56
    mkdirSync(entry, { recursive: true })
  • 写入本地文件
    src/ops.mjs:64
    renameSync(s.dir, join(entry, 'session'))
  • 读取本地文件
    src/ops.mjs:66
    if (existsSync(p)) { mkdirSync(join(entry, 'projcache'), { recursive: true }); renameSync(p, join(entry, 'projcache', basename(p))) }
  • 写入本地文件
    src/ops.mjs:66
    if (existsSync(p)) { mkdirSync(join(entry, 'projcache'), { recursive: true }); renameSync(p, join(entry, 'projcache', basename(p))) }
  • 写入本地文件
    src/ops.mjs:66
    if (existsSync(p)) { mkdirSync(join(entry, 'projcache'), { recursive: true }); renameSync(p, join(entry, 'projcache', basename(p))) }
  • 写入本地文件
    src/ops.mjs:68
    writeFileSync(join(entry, 'manifest.json'), JSON.stringify(manifest, null, 2))
  • 读取本地文件
    src/ops.mjs:99
    const m = JSON.parse(readFileSync(join(entry, 'manifest.json'), 'utf8'))
  • 读取本地文件
    src/ops.mjs:115
    if (existsSync(target)) throw new Error(`恢复冲突:${target} 已存在(同一会话不能有两份历史)。`)
  • 读取本地文件
    src/ops.mjs:120
    if (dest && !existsSync(dest)) { mkdirSync(dirname(dest), { recursive: true }); renameSync(join(it.entry, 'projcache', f), dest) }
  • 删除本地文件递归
    src/ops.mjs:130
    rmSync(it.entry, { recursive: true, force: true })
  • 删除本地文件递归
    src/ops.mjs:140
    for (const it of items) rmSync(it.entry, { recursive: true, force: true })
  • 删除本地文件递归
    src/ops.mjs:147
    if (safeDir(p).length === 0) rmSync(p, { recursive: true, force: true })
  • 执行外部命令来自配置/变量
    src/ops.mjs:334
    const r = spawnSync(install.node, [install.migrator, '--sessions-dir', join(home, 'sessions'), '--jobs', '1'], {
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

README

由 DeepSeek 最新模型翻译生成
dsh-session-manager English · 简体中文 DeepSeek Harness(DSH)的独立会话管理器:离线命令行工具,不是插件,零依赖。DSH 便携版、桌面版、标准安装版(~/.dsh)和 npm 版都能用。 | 功能 | 说明 | | 列出会话 | 跨所有工作区列出会话:标题、最后使用时间、目录、大小,以及「归档 / 置顶 / 子代理 / 旧代际 / 被 DSH 隐藏」标记 | | 升级旧会话 | 把 v0 / v2 / v3 会话升到当前代际(v4),调用 DSH 自带的迁移器,旧文件原样保留 | | 清理空会话 | 找出「新建后一条消息都没发」的会话,移入回收站 | | 归档 / 取消归档 | 用 DSH 自己的归档集合(archivedSessionIds),日志和工作区槽位都不动;已归档会话可单独列出,挑着删或全删 | | 回收站 | 移入、恢复(回到原工作区槽位,归档 / 置顶标记一并恢复)、彻底删除 | | 清洗循环 | 截掉历史里已经提交的 Go. Emit. Let me write. 类循环(检测规则与 dsh-loop-rescue 同一套) | | 修复烂尾会话 | 截掉末尾只有报错 / 重试的轮次,上下文仍过大时离线压缩,让「怎么重试都超长」的会话能接着用 |
展开完整文档(README 剩余 1 节)
dsh-session-manager

English · 简体中文

DeepSeek Harness(DSH)的独立会话管理器:离线命令行工具,不是插件,零依赖。DSH 便携版、桌面版、标准安装版(~/.dsh)和 npm 版都能用。

| 功能 | 说明 |
| 列出会话 | 跨所有工作区列出会话:标题、最后使用时间、目录、大小,以及「归档 / 置顶 / 子代理 / 旧代际 / 被 DSH 隐藏」标记 |
| 升级旧会话 | 把 v0 / v2 / v3 会话升到当前代际(v4),调用 DSH 自带的迁移器,旧文件原样保留 |
| 清理空会话 | 找出「新建后一条消息都没发」的会话,移入回收站 |
| 归档 / 取消归档 | 用 DSH 自己的归档集合(archivedSessionIds),日志和工作区槽位都不动;已归档会话可单独列出,挑着删或全删 |
| 回收站 | 移入、恢复(回到原工作区槽位,归档 / 置顶标记一并恢复)、彻底删除 |
| 清洗循环 | 截掉历史里已经提交的 Go. Emit. Let me write. 类循环(检测规则与 dsh-loop-rescue 同一套) |
| 修复烂尾会话 | 截掉末尾只有报错 / 重试的轮次,上下文仍过大时离线压缩,让「怎么重试都超长」的会话能接着用 |
| 修复被隐藏的会话 | 压缩帧布局不合 DSH 要求的会话会被 DSH 当成损坏、从列表里静默隐藏;按 DSH 的格式重写即可恢复 |

需要 Windows(dsm.exe)或 Node 22.15+(源码)。支持 DSH 0.1.2 起生成的 v0–v4 会话,已在 DSH 0.2.1 上端到端验证。

快速开始

Windows(推荐): 从 Releases 下载 dsm-win-x64.zip,解压出 dsm.exe(单文件,自带 Node,不用另装任何东西)。

1. 把 dsm.exe 放在任意位置(推荐放进 DSH 安装目录,例如 D:\dsh-desktop\tools\)。
2. 先完全退出 DSH(含托盘)。
3. 双击 dsm.exe,按菜单序号操作:

1. 列出会话 8. 从回收站恢复
2. 升级旧会话 9. 彻底清空回收站
3. 清理空会话 10. 清洗循环
4. 归档会话 11. 修复烂尾会话(截尾 + 压缩上下文)
5. 取消归档 12. 撤销上次日志修改
6. 已归档会话(查看/删除) 13. 修复被 DSH 隐藏的会话
7. 移入回收站 14. 换一个数据目录

dsm.exe 没有代码签名,第一次运行 Windows SmartScreen 可能提示「已保护你的电脑」,点「更多信息 → 仍要运行」。
升级旧会话要跑 DSH 自己的迁移器,dsm.exe 会用 DSH 自带的 node(便携版 node\node.exe、桌面版本体),其它功能都不依赖外部程序。

源码方式: 也可以克隆仓库后双击 dsm.cmd,它会找 Node 22.15+(DSM_NODE → DSH 便携版/桌面版自带的 node → PATH)。

命令行

dsm list --all :: 全部会话(含已归档)
dsm upgrade :: 升级旧代际会话
dsm empty :: 空会话 → 回收站
dsm archive 3 5 :: 按序号归档;也可以写会话 ID 前缀或标题关键字
dsm unarchive "qq-bot"
dsm trash 2-4 :: 移入回收站
dsm restore :: 选择恢复
dsm purge --older-than 30 :: 彻底删除 30 天前进回收站的条目
dsm archived :: 只列已归档会话,可挑着删或全删
dsm archived rm all --purge :: 已归档的全部彻底删除(不加 --purge 则进回收站)
dsm loops :: 检测并清洗循环
dsm tail "qq-bot" :: 修复烂尾会话:截掉末尾只有报错/重试的轮次,上下文过大时离线压缩
dsm undo :: 撤销上一次日志修改(循环清洗 / 烂尾修复)
dsm repair :: 修复被 DSH 隐藏的会话

通用选项:--home <数据目录>、--dry-run(只看不改)、--yes(不逐个确认)、--force(跳过「DSH 是否在运行」检测,不推荐)。

数据目录怎么找

DSH 装在哪、叫什么名字都行。按下面顺序找,前几步找到就直接用:

1. --home(安装目录或 data 目录都可以,也会被记住)
2. 环境变量 DSH_HOME
3. dsm.exe 所在目录往上逐层:放进安装目录里任意一层都能找到
4. 上次用过的目录(记在 %APPDATA%\dsm\config.json)
5. ~/.dsh(DSH 官方默认位置,标准安装版和 npm 版都在这里)
6. 以上都没有时:开始菜单 / 桌面 / 任务栏里指向 DSH 的快捷方式、卸载注册表里的安装位置、用户和系统级的 DSH_HOME
7. 再没有:扫描每个盘往下 3 层(跳过 Windows、AppData 等系统目录,最多几秒)
8. 还没有:让你把安装目录拖进窗口

扫描时只认真正的 DSH 数据(storages\workspace.json,或 sessions\<项目>\session-xxx\ 里有日志),别的程序的 data 文件夹不会被误认。菜单「14. 换一个数据目录」可以随时重新搜索或手动指定。

dsm.exe --home D:\dsh-desktop\data

修复烂尾会话(dsm tail)

典型症状:会话越聊越长,某次换了模型或上下文插件失效后,每次「重试」都报 maximum context length / prompt is too large,连 /compact 也失败(压缩请求本身就要带上全部历史)。

dsm tail 会列出最后十几轮的结局(完成 / 中止 / 出错)、回复条数和体量,并给出建议截断点(最后一轮模型确实产出过回复的轮次),然后:

1. 截尾: 把建议点之后只有报错、重试、空中止的轮次整段截掉(日志尾部截断,seq 仍连续)。截掉的轮次里新开的子代理会话一并进回收站。
2. 离线压缩: 截完后估算上下文仍超过约 25 万 token 时,不调用任何模型,用 DSH 自己的压缩事件(compaction/start → compaction/summary → 检查点 user/message(surface replace)→ compaction/end)把较早的轮次换成一条「摘录式」检查点:每轮的用户原话、助手最终回复、写过的文件。最近几轮(约 12 万 token)保留原文。完整日志一个字都不删,DSH 里照样能往上翻看历史。

两步都先备份原日志和投影缓存,dsm undo 可恢复。已在真实 DSH 0.2.1 上验证:修复后的会话能正常恢复,新一轮请求只带系统提示 + 检查点 + 保留轮次。

修完打开会话后,先在模型菜单确认选的是现在可用的模型(截尾会回到截断点时的模型选择)。

安全

- DSH 必须完全退出。 DSH 的 JSONL 后端只允许一个写入者,账目(workspace.json)也由 DSH 在内存里持有。实测:DSH 运行时从外部改的归档,会在 DSH 下一次写账目时被静默覆盖。所以所有修改类操作都会先检测 DSH 进程,检测不到结果也按「在运行」处理(失败即关闭)。
- 日志内容只在三种操作里被改写: 循环清洗、帧修复、烂尾修复(截尾只删末尾事件,压缩只追加事件)。三者都先把原文件备份到 <数据目录>\.dsm\backups\<时间>\,写后自检(事件数不变、能完整解码、帧布局合法)。
- 回收站不删任何东西: 整个会话目录连同投影缓存搬到 <数据目录>\.dsm\trash\<时间>\,每次一个新批次,不覆盖旧条目。恢复时目标已存在就拒绝,绝不合并两份历史。
- 账目修改前备份: 每次写 workspace.json 前在 .dsm\backups\<时间>\ 留一份。
- 工作区目录永远从会话日志头读取,不从有损的项目目录名反推。投影缓存(session_projcache)是派生数据,只随会话搬动,不编辑。
- 升级用 DSH 自带的迁移器(便携版 app\migrate-sessions-v4.mjs,桌面版 resources\dsh-build\migrate-sessions-v4.mjs),以 --jobs 1 串行运行:并发迁移时父子会话会互相触发 generation changed during verification。没有迁移器时(标准安装版通常没有单独的迁移器)可以用 --migrator <路径> 指定,或者在 DSH 里逐个打开旧会话让 DSH 自己升级。

DSH 格式细节

1. session.v4.jsonl.zstd 是多帧文件: 头部一帧,之后每次追加一帧。Node 的 zlib.zstdDecompressSync 只解第一帧,直接用它读会只拿到头部一行。本工具逐帧解码。
2. 第一帧必须恰好是一行头部: DSH 列会话时只解第一帧并校验,不合格就把会话当损坏、不显示、不报错。把整个日志压成单帧写回(很多第三方工具会这么做)就会让会话「消失」——dsm repair 能找回来。

开发与测试

npm test # 单元测试
node scripts/build-exe.mjs # 打包 dist/dsm.exe(Node SEA,Linux/Windows 都能打 Windows 版)
node scripts/smoke-exe.mjs dist/dsm.exe # 打包后冒烟测试,含「DSH 在跑就拒绝」

GitHub Actions 在 windows-latest 上跑单元测试、打包和冒烟测试,打 v* 标签时自动发布 dsm-win-x64.zip。

单元测试用合成的 DSH 布局覆盖全部操作。另外在 DSH 0.2.1-rc.2(以及用 0.1.2-rc.1、0.1.6-alpha.2、0.1.7-rc.2、0.2.0-rc.2 生成的旧会话)上做了端到端验证:每步操作后重启 DSH,通过 DSH 自己的 session/list 核对会话、代际状态和空白标记,并对升级、清洗后的会话继续发消息。

相关项目

- dsh-remote-access-proxy:在手机、平板或另一台电脑上使用 DSH
- deepinfra-fusion-proxy:DeepInfra Standard / Flex 按会话切换与自动回退
- dsh-loop-rescue:模型复读时从流内切断、剪掉循环并自动续跑

许可证

MIT
预览分享卡片长什么样 ↓citydirector/dsh-session-manager
分享 · DeepSeek Harness Hub
citydirector/dsh-session-manager

离线整理、修复并清理 DSH 会话数据

这是你的仓库?认领条目

回到顶部