Pular para o conteúdo principal
A

dsh-session-move

ayachinene285/dsh-session-move

Adds nested folders above workspaces in the sidebar, drag-to-reorder for workspaces, folders and sessions, and moves a session to another workspace together with its working directory.

Instalar

dsh plugin --profile web add github:ayachinene285/dsh-session-move

README

dsh-session-move

DSH 的侧栏只有「工作区(目录)」这一层,也没有把会话挪到别的工作区的接口。这个插件补了这两块:

  • 在工作区上面加一层文件夹。纯虚拟的,能嵌套,能放多个工作区,磁盘上不建任何目录;
  • 会话可以在工作区之间搬,搬完以后这条会话里跑的东西都在新目录下。

代码分两块。lib/index.js 跑在 DSH 的本地服务进程里,负责读会话文件、改 cwd、开一个本地接口;lib/client.js 是网页里的部分,负责侧栏、菜单和弹窗。

安装

  1. clone 或者下载这个仓库到任意目录;
  2. 装进 profile:dsh plugin --profile <profile 名> add <插件目录>。桌面版的 profile 由应用自己管理,命令被拒的话,在「插件」页里装,或者手动把 dsh-session-move 加到 profile 的 package.json 里 dsh.profile.bundles 下;
  3. 完全退出 DSH 再打开。改插件不是刷新页面就能生效,界面和后台都要重新加载。

用法

会话行右边的「…」里有「移动到工作区 / 文件夹…」,弹窗里选目标:

  • 选工作区:真的搬过去,会话的工作目录会改成那个目录;
  • 在文件夹那一行点「把会话放这里」:只在侧栏归组,工作目录不动;
  • 会话已经在某个文件夹里时,弹窗最上面多一行「移出文件夹」,点它回到工作区下面;
  • 也可以直接填一个新目录,插件会把它登记成新工作区。

同一条会话可以反复搬。已经在目标工作区、会话正在跑、子代理会话,这几种情况会直接给提示,不会动数据。目标目录不存在时,默认先按同名建一个空目录再搬,弹窗里可以把这个行为关掉。

拖动排序

工作区和文件夹都能直接拖动,改上下顺序、换所属层级。行首有一个浅灰色的小手柄 ⠿, 按住整行(或手柄)往上下拖即可 —— 用的是指针事件,不依赖浏览器原生拖放,Electron 里也稳。

  • 按住任意一行(工作区、文件夹、会话)拖动;
  • 指针落在目标行的上 30% → 插到它前面;下 30% → 插到它后面;
  • 落在文件夹行的中间 40% → 放进这个文件夹(并自动展开,免得看起来像消失了);
  • 列表最下面还有一条空白落点,拖过去就是放到最外层末尾;
  • 拖动时被拖的行变半透明,落点行会有一条蓝线(放进文件夹是一圈蓝框)。

会话也能拖:上下拖改顺序(工作区里的顺序存在浏览器 localStorage,文件夹里的顺序存在 storages/session-move-folders.json),拖进文件夹就归到那个文件夹下。拖动不会改会话的工作目录—— 真要换工作区还是用会话菜单里的「移动到工作区 / 文件夹…」。

顺序和层级都存在 storages/session-move-folders.json 里,重启 DSH 不变。 拖进自己的子文件夹、拖到原位这类操作会被忽略或由宿主拒绝并提示,不会写坏数据。

文件夹分组

侧栏右上角「+」可以新建文件夹,不用选本地目录。文件夹能装文件夹,也能装任意多个工作区;工作区本身不能当父级。

工作区行的「…」里有「移入文件夹…」和「不放入文件夹(顶层)」。文件夹行的「…」里有新建子文件夹、重命名、移入其它文件夹、删除。删除只是解散这个文件夹,里面的东西上移一层。

「+」→「新增工作区(选择本地目录)」还是官方的选目录流程;文件夹的「…」里也有「新建工作区(选目录后放进这里)」,建完自动放进去。

展开状态记在浏览器 localStorage 里,默认展开,点行首的箭头收放。

会话可见性跟官方一致:已归档的会话默认隐藏(工作区「…」→「显示已归档的会话」可以切,这个开关跟官方和归档管理插件共用一份状态),子代理会话不显示,不是当前会话的空会话不显示。没归进任何工作区的会话放在最后「未分组」里。

会话也能直接放进文件夹,只动侧栏分组:会话「…」→ 移动到工作区 / 文件夹… → 在文件夹那一行点「把会话放这里」。放进去之后它只在文件夹下面出现一次,不会再重复出现在所属工作区里;想放回去用「移出文件夹」。

会话移动是怎么做的

DSH 判断一条会话属于哪个工作区,看的是会话自己记录的工作目录:

  • 挂载会话的时候,DSH 拿会话文件里的 cwd 跟工作区路径比对,不一致就拒绝;
  • 会话文件在磁盘上的位置也由这个 cwd 推出来,位置和记录对不上会被当成坏会话;
  • 官方没有提供移动会话的接口。

所以插件分两步做完:

  1. 改数据:重写会话文件第一个 zstd 帧里记录的 cwd,把会话目录搬到新 cwd 对应的项目目录下,正文逐字节不动。写完马上自检,失败就回滚。
  2. 重新挂载:让 DSH 按磁盘现状重新读一遍这条会话,挂到目标工作区,从旧工作区摘掉,然后马上放掉,不长期占内存。

移动完不需要重启 DSH,也不用手工改 workspace.json,归属由 DSH 自己写。

冷会话和打开着的会话

一条会话只要被打开过(在对话页看过、聊过),DSH 的进程就会一直留着它,并且握着会话文件的写入权,官方没有关掉它的接口。所以:

  • 没打开过的:点一下就好,不用重启;
  • 打开着但空闲的:这时候改不了文件,插件把这次移动记成待办,下次 DSH 启动时(那时会话都是冷的)自动做完。待办存在 <DSH 数据目录>/storages/session-move-pending.json。重放失败不会把记录删掉,失败原因写进 lastError,下次启动继续试,弹窗顶上也会显示上次为什么没成;
  • 有回合在跑的:直接拒绝,等它跑完再搬。

工作区目录不存在时

工作区的目录如果在 DSH 外面被删了或者改了名,DSH 就解析不出会话记录的 cwd,会话会在侧栏掉进「未分组」,工作区的会话数变成 0,往那里搬也只会得到一句「目标目录不存在」。

现在弹窗会把它标出来(行尾写「目录不存在」,选中后下面显示完整路径),并且默认直接修:

  • 点「移动」就行,插件先按同名把空目录建出来,再搬会话,然后把归属补上。补归属的时候会顺手让 DSH 重新解析一次 cwd,所以不用重启就能在侧栏看到;
  • 不想让它建目录,把弹窗里的「目录不存在时自动新建同名目录」取消掉,那样会直接报错;
  • 如果目录只是改了名,别让它建空目录,用下面的「移动到新目录」填真正的路径,会自动登记成新工作区。

重启后重放的待办也按「目录不在就先建」处理。

本地接口

后台只加了一个本地接口,不替换官方任何东西:

方法路径说明
GET/api/dsh-session-move?sessionId=<id>工作区列表、这条会话的归属、它的待办移动
POST/api/dsh-session-move{ action: "move", sessionId, workspaceId 或 targetPath, title?, createMissingDir? }

写请求只接受本机同源调用,会校验 Origin、Host 和 Sec-Fetch-Site。

配置

package.json 的 dsh.client 指向界面部分的入口,cordis.patch.yml 只有一条 insert,不禁用官方任何入口。

- insert:
    - id: dsh-session-move
      name: 'dsh-session-move'

开发时踩过的坑

  • 改了界面部分也要完整重启 DSH。DSH 会给每个界面模块按文件内容算一个版本号,并且按 immutable 缓存;只刷新页面的话,页面会拿着旧的版本号去取,被后台拒掉,表现可能是「插件加载失败」,严重的时候应用起不来。
  • 不要直接读没声明依赖的 ctx.xxx。Cordis 里访问没声明的服务属性会直接抛异常,取可选服务用 ctx.get("xxx")。
  • 注册类调用都要自带 try/catch,包括路由、快捷键、槽位注册。界面部分初始化时抛错,DSH 会判定 entry did not activate,弹启动失败并提示禁用第三方插件。
  • 同理,插件对外注册的地方都做了兜底,最坏情况只是这个功能不可用,不会连累 DSH 启动。

已知边界

  • 移动会改会话的工作目录,之后这条会话里的工具调用都在新目录下跑;历史记录里的旧路径不会改写。
  • 有回合在跑的会话不能移动。
  • 只处理官方的 JSONL 存储(session-persistence-jsonl,日志格式 v4),别的后端会因为没有 locate() 被拒。
  • 已经归档的会话也能移动(归档集合按 id 全局存),但它在侧栏里还是显示为归档。
  • 恢复会话时 DSH 会追加一条 session/end-seed 事件,这是官方的恢复标记,不影响历史内容。

卸载

从 profile 的 dsh.profile.bundles 里删掉 dsh-session-move,执行 dsh plugin --profile <name> remove dsh-session-move,然后完整重启 DSH。

Plugins relacionados