Skip to content
dsh.fish
Bundle

dsh-workspace-mover

在 DSH 侧边栏安全迁移会话:真迁移原始档案,支持失联会话恢复、移动历史和一键撤回。

Source
PianoPrince
stars
2 stars
License
MIT
Updated
Updated 11 hours ago

Readme

# dsh-workspace-mover

**_> 非官方项目,由社区成员独立开发和维护。_**

<!-- Hero -->
<div align="center">
  <b style="font-size: 1.15em;">在侧边栏把会话拖到另一个工作区——真迁移原始档案,而不是复制</b><br /><br />
  <p style="font-size: 0; line-height: 1;">
    <a href="https://github.com/PianoPrince/dsh-workspace-mover/actions/workflows/test.yml"><img alt="CI" src="https://github.com/PianoPrince/dsh-workspace-mover/actions/workflows/test.yml/badge.svg" style="height:20px; margin:0 2px;" /></a>
    <a href="https://github.com/PianoPrince/dsh-workspace-mover/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/PianoPrince/dsh-workspace-mover" style="height:20px; margin:0 2px;" /></a>
    <a href="./LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-yellow.svg" style="height:20px; margin:0 2px;" /></a>
    <img alt="Node" src="https://img.shields.io/badge/Node-%E2%89%A522-339933" style="height:20px; margin:0 2px;" />
    <img alt="npm 依赖:0" src="https://img.shields.io/badge/npm%20%E4%BE%9D%E8%B5%96-0-4d6bfe" style="height:20px; margin:0 2px;" />
    <a href="https://awesome-dsh-plugin.com"><img alt="Awesome DSH Plugin" src="https://awesome-dsh-plugin.com/badge.svg" style="height:20px; margin:0 2px;" /></a>
  </p>
  <p style="font-size: 0; line-height: 1;">
    <img alt="真迁移" src="https://img.shields.io/badge/-真迁移-4d6bfe" style="height:20px; margin:0 2px;" />
    <img alt="零 token 消耗" src="https://img.shields.io/badge/-零%20token%20消耗-4d6bfe" style="height:20px; margin:0 2px;" />
    <img alt="备份回滚" src="https://img.shields.io/badge/-备份回滚-4d6bfe" style="height:20px; margin:0 2px;" />
    <img alt="孤儿会话救援" src="https://img.shields.io/badge/-孤儿会话救援-4d6bfe" style="height:20px; margin:0 2px;" />
    <img alt="一键撤回" src="https://img.shields.io/badge/-一键撤回-4d6bfe" style="height:20px; margin:0 2px;" />
    <img alt="主题自适应" src="https://img.shields.io/badge/-主题自适应-4d6bfe" style="height:20px; margin:0 2px;" />
    <img alt="GitHub clones observed" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FPianoPrince%2Fc14345658550a4a308570acfbaf9d170%2Fraw%2Fwsm-clones-total.json" style="height:20px; margin:0 2px;" />
    <img alt="Release downloads (shown after 10 downloads)" src="https://gist.githubusercontent.com/PianoPrince/c14345658550a4a308570acfbaf9d170/raw/wsm-release-downloads.svg" style="height:20px; margin:0 2px;" />
  </p>
</div>

<div align="center">
  🌏 中文 · <a href="./README_EN.md">English</a>
</div>

## 📑 目录

- [✨ 功能一览](#-功能一览)
- [🔬 技术要点](#-技术要点)
- [🚀 安装](#-安装)
- [🖼️ 特性巡礼](#️-特性巡礼)
- [⌨️ 使用](#️-使用)
- [🔌 与 DSH 的集成方式](#-与-dsh-的集成方式)
- [🤝 与其他插件的共存](#-与其他插件的共存)
- [🆕 最近更新](#-最近更新)
- [🔐 安全设计](#-安全设计) · [⚠️ 已知限制](#️-已知限制)

---

## ✨ 功能一览

DeepSeek Harness 的侧边栏支持工作区内拖拽排序会话,但把会话拖到**另一个工作区**上会被静默忽略——官方 RPC 只暴露了单工作区内的 `insertSessionBefore`,没有跨工作区移动接口。本插件补上这块:

- **🖱️ 拖拽交互**:把任意空闲会话行拖到目标工作区的标题行,确认框亮出目标路径,一键迁移
- **📦 批量迁移**:Ctrl/Shift+点击多选会话行(插件自建选择集,带计数徽章),拖任一选中行整批迁移;也可点工作区标题行的「⋯」菜单选「整组迁移…」。单批至多 50 个,逐条独立备份回滚、失败互不牵连
- **🚚 真迁移**:物理搬移原始 `session.jsonl.zstd` 档案、改写头部 `cwd`、更新工作区注册表——会话 id 与全部历史**原样保留**,不产生副本、不重新注入上下文、**零 token 消耗**
- **🏠 工作区搬家向导**:项目文件夹被移动/改名后,一键把失效的工作区**原地重定向**到新位置——工作区 id、标题、排序、归档位全部保持,名下会话连同旧路径的失联散件批量原样迁移;运行中的自动跳过,中断后续跑只补剩余
- **🛟 孤儿会话救援**(设置页「会话救援」面板):扫描磁盘上全部会话档案并分类处理——
  - **失联(orphaned)**:项目文件夹被移动/改名/删除导致 cwd 失效、从侧边栏"消失"的会话(官方讨论 #3012 的社区修复),可一键真迁移到任意现有工作区
  - **未记账(unregistered)**:cwd 仍有效但从未被任何工作区记账的会话(bootstrap 只跑一次、agent 内部 fork 不注册等),可原地补挂账
  - **挂错分组(misfiled)**:真实文件夹属于 B 分组、记账却在 A 分组的会话(克隆式迁移工具、改名后重建分组等场景),识别出应属分组后一键归位或全部归位——只修正归属记录,文件不动
  - **已归档会话**:被官方「归档」隐藏、再也无法从界面找回的会话按分组列出,一键恢复原分组或换组恢复;真实文件夹属于别分组的会话带归位建议标记
  - **幽灵记账(ghosts)**:注册表有账但磁盘档案已缺失的 id(只读提示)
  - 全部走同一条备份+回滚管线
- **🧹 分组合并**:在组标题的「⋯」菜单选「整组迁移…」迁入目标分组后,若源分组已空可一键删除——两条命令完成分组合并
- **🗂️ 空分组清理**:救援面板只列出真正零成员的工作区(归档会话、幽灵记录都算成员,绝不误报),单个删除或全部清理;删除只移除分组登记,不碰任何会话
- **🗑️ 会话回收站**:救援面板可把异常/归档会话移入回收站——文件、标题、归属、归档状态完整保留,随时还原到原位置或任意分组;彻底删除需二次确认
- **💾 备份管理**:迁移自动生成的字节级备份按会话聚合展示(份数/占用/时间跨度),一键恢复(回读校验后才挂账)或按会话清理
- **📂 打开文件夹**:组标题「⋯」菜单一键用系统文件管理器打开该分组目录
- **⏪ 移动历史与撤回**:记录最近 100 次跨工作区移动,批量移动聚合为一条记录、整批一键撤回,撤回本身同样生成备份并复用回滚保护
- **🧾 迁移任务中心 + 🛡️ 数据保护**:批量迁移逐项持久化记录(完成/失败、最后错误与尝试时间),失败项一键重试;回收站与备份的份数/占用汇总,按时间清理先预览释放量再执行
- **🔒 并发防护 + 错误码协议**:同一会话/同一目标工作区的并发操作立即报「处理中」而不会排队或互相踩踏(先备份、后动手的事务顺序保证备份失败零副作用);所有 RPC 错误携带稳定错误码;「回滚也失败」时写入恢复记录并在面板标红提示人工确认——文件与备份始终原地保留
- **✅ 迁移后校验 + 一键修复**:每次搬运落地即回读档案做 id/cwd 双确认,不符视同失败整体回退;救援面板"一键修复"自动跑完可修复项(逐项隔离,三态汇报),全部列表支持按标题/ID/路径即时筛选
- **🏷️ 会话标题优先**:确认框、救援列表和最近移动记录都先显示会话标题,找不到标题时显示「未命名会话」

## 🔬 技术要点

1. **常驻会话一致性修复**:打开过的会话在宿主内存里有冻结头与持久化写入缓存。直接搬文件会导致它下次对话时把新事件**写回旧路径**造成历史分叉——本插件迁移后清理陈旧写入状态并刷新注册表索引,宿主自动从新位置重新接管。
2. **安全兜底**:每次移动前强制字节级备份;改写、搬运、记账任一步失败自动回滚到移动前状态。
3. **Windows 加固**:目录内刚发生文件改名后立刻改目录名会瞬时 EPERM——指数退避重试,仍失败退化为复制+删除。
4. **主题自适应 UI**:确认框/Toast 全部使用官方 `--dsw-alias-*` 设计令牌,跟随设置里的外观即时切换。
5. **零依赖免构建**:host 半零 npm 依赖,client 半 source-as-product,无构建产物漂移风险。
6. **重定向的剪枝防御**:官方工作区实体的每次写入都会按「内存索引中的会话 cwd」剪枝成员名单——搬家向导先把全部受影响会话的三张索引预置成新路径,再经实体的统一写入通道 `mutate` 原地换 path,成员一个不丢。

## 🚀 安装

```bash
dsh plugin --profile web add "github:PianoPrince/dsh-workspace-mover"
# 重启 dsh web 一次
```

> **零构建授权**:本插件是纯 JavaScript 源码即产物(无 TypeScript、无构建步骤),从 GitHub 安装时**不需要** `allowBuilds` 构建授权——pnpm 不会执行任何安装期脚本。

<details>
<summary><b>npm 渠道</b></summary>

```bash
dsh plugin --profile web add dsh-workspace-mover
```

</details>

<details>
<summary><b>本地开发安装</b></summary>

```bash
dsh plugin --profile web add "link:E:/path/to/dsh-workspace-mover"
```

</details>

<details>
<summary><b>常见问题</b></summary>

| 现象 | 原因与解决 |
|---|---|
| 拖了但没反应 | 只在「分组视图」把会话行投到**工作区标题行**上才会触发;「扁平列表」视图没有标题行,本插件在该视图不激活 |
| 提示会话正在运行中 | 宿主端校验回合状态;等该会话回合结束再拖即可 |
| 移动失败的 toast | 每次操作前都有字节级备份、失败自动回滚;按 toast 说明处理后重试,详细原因见宿主日志中的 `MOVE FAILED` 条目 |
| 移动成功但侧边栏没归位 | 插件迁移后会主动重拉一次工作区基线;偶发未生效时手动刷新页面 |
| 有些会话从侧边栏不见了 | 打开 **设置 → 会话救援** 自动扫描,「失联」「未记账」「挂错分组」三类都能一键找回 |

</details>

## 🖼️ 特性巡礼

> 以下均为真实界面实拍(点击可放大)。

### 拖拽跨工作区迁移

| | |
|---|---|
| **把空闲会话行拖到目标工作区标题行,出现虚线高亮** | **确认框亮出目标工作区路径,一键移动** |
| ![把一个会话拖到另一个工作区](docs/media/drag_session_to_another_workspace.png) | ![跨工作区移动确认框](docs/media/confirm_popup.png) |
| **设置 → 会话救援:一键找回失联与未记账的会话** | |
| ![会话救援设置面板](docs/media/setting_dialogue_repair.png) | |

### 批量迁移 · 多选拖拽

| |
|---|
| **Ctrl+点击选中多个会话(当前打开的会话自动带上),左下角亮出计数徽章;拖到目标工作区标题行即整批移动,Esc 清空** |
| ![批量移动选中时:三个会话高亮,左下角显示已选计数徽章](docs/media/batch_move_selection.png) |
| **组标题「⋯」菜单里的「整组迁移…」:整组搬移,迁入后可删除已空的源分组(分组合并)** |
| ![组标题菜单中的整组迁移入口](docs/media/workspace_move.png) |

### 工作区搬家向导 · 实测全程

以下为一次真实搬家的完整记录:把 `Test1` 文件夹改名为 `Test2` 后,用向导原地修复工作区。

| | |
|---|---|
| **改名前:`Test1` 分组正常工作** | **改名后侧边栏仍显示旧分组(磁盘上文件夹已不在)** |
| ![改名前的工作区](docs/media/original_workspace.png) | ![改名后的工作区](docs/media/workspace_after_rename.png) |
| **打开设置 → 会话修复:「工作区体检」把分组标为「路径失效」,填入新路径** | **确认框亮出起讫路径与将要迁移的会话数** |
| ![工作区体检面板](docs/media/workspace_examination.png) | ![搬家确认弹窗](docs/media/remove_popup.png) |
| **搬家完成:分组原地更名为 Test2,会话与历史原样保留** | |
| ![搬家后的工作区](docs/media/workspace_after_move.png) | |

## ⌨️ 使用

### 拖拽跨工作区迁移

1. 重启后在侧边栏**分组视图**里,按住任意空闲会话行;
2. 拖到目标工作区的标题行(出现虚线高亮)松手;
3. 确认框显示目标工作区路径 → 点「移动」;
4. 完成 toast 提示;若宿主广播未触发自动刷新,手动刷新页面即可。

运行中的会话会被拒绝(宿主端校验),移动失败自动回滚并在 toast 中说明原因。

### 会话救援面板

1. 重启后打开 **设置 → 会话救援**,面板自动完成首次扫描;
2. **失联**行:选目标工作区 → 点「迁移过去」(真迁移,ID 保留);
3. **未记账**行:点「补挂账」原地挂到路径匹配的工作区;
4. **挂错分组**行:显示「当前分组 → 应属分组」,点「归位」或「全部归位」即时修正归属(文件不动);
5. **一键修复**:自动跑完可修复项(挂错归位、未记账补账,逐项隔离失败),孤儿/损坏项跳过并说明原因;
6. **筛选框**:按标题 / 会话 ID / 路径 / 分组即时过滤所有列表;
7. **已归档 / 回收站 / 备份**:归档会话一键恢复;删除的会话进回收站,可还原到原位置或任意分组、确认后彻底删除;迁移自动生成的字节级备份按会话聚合,可恢复或清理;
8. 每次操作前后都有备份与回滚保护,结果按 已修复/跳过/失败 即时反馈。

### 批量迁移

1. 侧边栏 **Ctrl/Cmd+点击** 会话行加入多选(再点取消),**Shift+点击** 在组内范围选择,左下角徽章实时显示已选数量,**Esc** 清空;
2. 按住任一选中行拖到目标工作区标题行,确认框显示本批数量 → 点「全部移动」;
3. 也可点工作区标题行的**「⋯」菜单 →「整组迁移…」**,选目标分组整组搬移;源分组因此清空时,可确认后直接删除空分组(分组合并);
4. 每个会话独立备份回滚,个别失败(如正在运行)不影响其余,结果 toast 汇总成功与跳过数。

### 工作区搬家向导

1. 文件夹被移动/改名后,面板顶部的**工作区体检**会把对应分组标记为「路径失效」;
2. 在该行的输入框填入文件夹现在的完整路径,点「搬家」;
3. 确认框亮出旧路径 → 新路径与将要迁移的会话数量,确认后执行;
4. 名下会话连同旧路径的失联散件一起原样迁移;正在运行的会话本次跳过,结束后用同样的输入再跑一次即可续跑剩余部分。

## 🔌 与 DSH 的集成方式

- **Host 半**(`lib/index.js`,零 npm 依赖):经 `cordis.patch.yml` 以标准 `insert` 行挂载;通过 `ctx.connection.rpc.handle('/workspace-mover', …)` 注册逻辑通道,端点 `mover.status / mover.workspaces / mover.move / mover.moveMany / mover.scan / mover.repair / mover.repairAll / mover.history / mover.undo / mover.ws.audit / mover.repoint / mover.archived / mover.unarchive / mover.openFolder / mover.session.delete / mover.trash.list / mover.trash.restore / mover.trash.purge / mover.backups.list / mover.backups.restore / mover.backups.deleteOne`,失败详情写入宿主日志(`MOVE FAILED`)。
- **移动算法**:
  1. 运行状态检查:仅拒绝回合进行中的会话(`agents.get(id)?.status === 'running'`,与宿主 UI"进行中"徽标同款判据);常驻内存但空闲的会话允许迁移;
  2. 从磁盘读取权威会话头,校验目标 ≠ 源;
  3. 原始字节备份到 `$DSH_HOME/workspace-mover/backups/`(每会话保留最近 20 份);
  4. 仅重写首帧(头部 cwd),其余帧字节级保留;临时文件 + 原子改名发布;
  5. 会话目录整体搬移(Windows 目录改名怪癖:指数退避重试,仍失败退化为复制+删除);
  6. 内存一致性收尾:失效注册表三张索引;常驻会话额外清理持久化协调器的陈旧写入状态、刷新索引并预置目标记账(绕开冻结头的旧 cwd 校验);
  7. 调用目标实体 `attachSession` 持久化记账,源实体已先行 `detachSession`;
  8. 任一步失败自动回滚:撤销预置 → 还原索引快照 → 原件放回源目录 → 重新挂回源工作区。
- **Client 半**(`client/client.js`,免构建 source-as-product):仅依赖 ARIA 语义属性定位行元素(会话行 `[aria-selected]` / 工作区标题行 `[aria-expanded]`),不碰 CSS-module 哈希类名;只拦截「跨组投放」场景,官方同组排序不受影响。迁移成功后主动重拉一次工作区基线(公开 API),侧边栏分组即时归位。
- **救援面板**:经官方 `settings.section` 插槽注册设置页分栏,RPC 端点 `mover.scan`(分类扫描)与 `mover.repair`(批量 attach/relink,relink 复用同一条迁移管线)。
- **迁移历史**:保存于 `$DSH_HOME/workspace-mover/history.json`,最多保留最近 100 条;原工作区仍存在时可直接撤回,原工作区已删除时会明确要求重新选择目标分组。

## 🤝 与其他插件的共存

**设计上就按"与生态共生"标准实现**,与其他插件冲突面很小:

- **通信全命名空间**:RPC 只占 `/workspace-mover` 一个通道、不注册任何 HTTP 路由;面板走官方 `settings.section` 插槽(槽位系统天生支持多插件并存);CSS 类 `wsm-*` 与 DOM 属性 `data-wsm-*` 均为自有命名空间;
- **不改写官方 bundle 任何字节**,只挂事件监听与槽位注入(对比少数直接 patch 官方 bundle 的插件,不存在那类雷);
- **宿主写入全走官方通道**(registry 的 `mutate`/`attachSession`/`detachSession`、持久域状态),不引入私有数据形态,其他插件读到的永远是官方形态的数据;
- **零 npm 依赖**,不存在共享依赖的版本冲突。

与其他类目插件同装的兼容性:

| 同装插件类目 | 兼容性 | 说明 |
| --- | --- | --- |
| 侧边栏增强 / better-sidebar / 终端 / 费用统计 / 记忆 / 导出分享 | ✅ 无冲突 | 面板与数据面完全不同 |
| 归档管理类插件 | ✅ 兼容 | 双方都读写官方归档集,数据层一致(面板功能会有些重复) |
| 排序 / 置顶类插件 | ⚠️ 基本兼容 | 本插件移动后精确重排「最近更新」,置顶/排序插件各自维护自己的集合——并存无碍,仅各自面板展示顺序可能不完全一致 |
| 重画侧边栏的插件(自绘工作区树) | ⚠️ 降级共存 | 若对方替换官方工作区 DOM,本插件的 ARIA 语义选择器可能找不到行——表现为功能静默不触发,**不会损坏数据** |
| 其他会话移动器 | ❌ 建议二选一 | 同类插件同样在 document 层拦截拖拽投放,同时安装可能导致一次拖拽被双重处理。本插件已覆盖移动 / 批量 / 合并 / 归位 / 归档恢复场景,无需重复安装 |

**DSH 版本敏感点**(非插件冲突):取消归档走 registry 持久状态写通道,在不支持的宿主版本上会明确提示而非报错;投影缓存标题按 v3 形状防御性解析,文件缺失时退化为档案头标题。

## 🆕 最近更新

### v1.4.0 · 2026-09-06

- 并发防护:同会话 / 同目标工作区的并发操作立即返回「处理中」,绝不排队、绝不死锁;批量迁移先去重,自己不会和自己抢锁
- 错误码协议:所有 RPC 错误携带稳定错误码(busy / conflict / not-found / rollback-failed 等),客户端按码决定交互、不再匹配英文文案
- 恢复记录:迁移挂账回滚失败、回收站落盘双重失败等极端路径写入持久化恢复记录,体检面板标红提示人工确认(文件与备份始终保留在原处,绝不自动删除)
- 能力自检:`mover.status` 汇报检测到的官方服务与降级项,支持排障一眼可见
- 事务顺序加固:字节级备份提前到一切副作用之前——备份失败即整体放弃,记账、文件、索引零改动

### v1.3.0 · 2026-09-05

- 数据保护汇总与按时清理:回收站与备份数量/占用一屏可见;「清理 30 天前数据」先预览释放空间,确认后执行

### v1.2.0 · 2026-09-05

- 迁移任务中心:批量迁移逐会话持久化记录(完成/失败、最后错误与尝试时间);失败项一键重试——按会话当前位置重新迁移,绝不用陈旧旧路径;清除记录不影响已迁移会话

### v1.1.0 · 2026-09-05

- 预检补全:动任何东西之前完成预检——双记账检测(多余归属随移动一并摘除)、目标可写探测、磁盘空间提示性检查;移动前摘除全部归属方,回滚时逐一恢复

### v1.0.0 · 2026-09-05

- 迁移后一致性校验:每次搬运(单个/批量/工作区搬家)落地后回读档案,id 与 cwd 双确认才算成功,不符自动整体回退
- 一键修复:可自动修复项(挂错归位、未记账补账)一次跑完,逐项隔离失败;孤儿/损坏项跳过并说明原因,结果按 已修复/跳过/失败 汇报
- 面板筛选:按标题 / 会话 ID / 路径 / 分组即时过滤所有救援列表,分区显示 命中/总数

### v0.9.0 · 2026-09-05

- 会话回收站:救援面板各区块新增「删除」,会话完整移入回收站(文件、标题、归属、归档状态全保留),一键还原到原位置或任意分组,确认后彻底删除
- 备份管理:迁移自动生成的字节级备份终于可见——按会话聚合展示份数/占用/时间跨度,一键恢复(回读校验后才挂账)或按会话删除
- 删除即四件套清理:文件、记账、投影缓存、注册表索引一次清干净,不留幽灵

### v0.8.0 · 2026-09-05

- 归档会话管理:救援面板新增「已归档的会话」区——被官方归档隐藏的会话按分组列出,一键恢复原分组,或「恢复到…」换组恢复(走完整迁移保护);挂错的归档会话带归位建议标记
- 空分组检测与清理:只列出真正零成员的工作区(归档会话、幽灵记录都算成员,绝不误报),支持单个删除与全部清理
- 工作区「⋯」菜单新增「打开文件夹」:系统文件管理器直达分组目录

### v0.7.0 · 2026-08-28

- 会话救援新增「挂错分组」识别:真实文件夹属于 B 分组、记账却在 A 分组的会话自动列出应属分组,逐条「归位」或一键「全部归位」——只修正归属记录,文件不动
- 分组合并:组标题「⋯」菜单新增「整组迁移…」入口;整组迁入后源分组已空时可直接删除,两条命令完成合并
- 扫描摘要与各区块同步展示「挂错分组」数量

### v0.6.3 · 2026-08-28

- 会话移动后自动按「最近更新」精确归位,无需手动切换排序
- 行→会话识别升级为直读行元素自带的会话标识:无论分组里有多少隐藏/归档会话,多选与移动都精确命中所选
- 新增批量多选实拍截图

### v0.6.2 · 2026-08-28

- 设置页撤回 / 补账 / 重新归组后侧边栏即时刷新,会话立刻回到目标分组
- Ctrl+点击开始多选时自动带上当前打开的会话:正开着 A、Ctrl+点 B,一步选中 {A, B}

### v0.6.1 · 2026-08-28

- 批量移动在「最近移动」聚合为一条记录,支持整批一键撤回
- 普通点击会话行即退出多选;Esc 可随时清空多选

### v0.6.0 · 2026-08-28

- 批量迁移:插件自建侧边栏多选(Ctrl/Shift+点击、Esc 清空、计数徽章),拖任一选中行整批迁移;组标题「⋯」菜单可整组搬移
- 新增 `mover.moveMany` 端点:单批 ≤50 个,复用单条迁移管线——逐会话独立备份回滚、错误隔离、移动历史落账(可撤回)
- 测试 27 → 30 用例

### v0.5.1 · 2026-08-27

- 搬家时工作区标题自动跟随新文件夹名(自定义标题原样保留)
- 迁移后 `@` 文件引用即刻指向新位置,无需重启
- 冷启动保留会话列表标题缓存,列表稳定显示会话名
- 测试 24 → 27 用例

### v0.5.0 · 2026-08-27

- 工作区搬家向导:体检面板识别「路径失效」的分组,一键原地重定向到新位置——工作区 id、标题、排序、归档位全部保持,经实体统一写入通道 `mutate` 换 path(先预置三张索引,成员零丢失)
- 批量迁移名下会话与旧路径失联散件:逐文件备份回滚、常驻写入状态清理;运行中的自动跳过,中断可携原路径续跑
- 新增端点 `mover.ws.audit` / `mover.repoint`(18 → 24 用例)

### v0.4.0 · 2026-08-26

- 移动历史与一键撤回:保存最近 100 条跨工作区移动记录(`mover.history` / `mover.undo` 端点),设置页确认后移回原分组,撤回同样受备份与回滚保护
- 确认框、救援列表与移动记录优先显示会话标题

### v0.3.2

- 孤儿会话救援:磁盘全量扫描、失联会话真迁移、未记账会话补挂账,全程回滚保护

## 🔐 安全设计

- 移动前强制备份;落地回读校验(id/cwd 双确认,不符整体回退);attach 失败自动回滚(撤销预置记账 → 还原索引 → 还原字节 + 清理目标 + 重新挂回源工作区);
- 删除进回收站:物理移动先行,失败零副作用;manifest 完整记录还原所需信息;彻底删除需二次确认;
- 驻留内存的会话拒绝删除(防止文件被驻留对象重建为僵尸),并给出重启释放的明确指引;
- 仅拒绝回合进行中的会话;常驻空闲会话迁移后修复写路径归属,杜绝历史分叉;
- 注册表/持久化内部访问全部包在 try/catch 中,失败降级为功能可用 + 重启建议提示;
- 兼容性目标:Node ≥ 22,dsh 0.1.1-rc.2;核心纯函数与端到端沙箱测试见 `npm test`(77 用例,含回滚路径、救援扫描/修复、历史撤回、工作区重定向、批量迁移、迁移后校验、回收站与备份恢复、迁移任务中心与数据保护清理、并发锁与错误码协议)。

## ⚠️ 已知限制

- 不支持把会话移入「Ungrouped」桶;
- 常驻内存的会话(近期打开过)不能直接删除——文件会被驻留对象重建;重启 Harness 释放后再删,删除时会给出 toast 提示;
- 行 → 会话识别优先读取行元素自带的会话标识(React props),渲染顺序对齐仅作兜底;若第三方插件替换侧边栏 DOM 导致 ARIA 选择器失效,相关功能静默停用(不损坏数据);
- 「扁平列表」视图无工作区标题行,本插件在该视图不激活;
- 若宿主升级改变了注册表缓存字段名或实体结构,相关步骤走降级路径(功能可用,归属刷新可能需重启);取消归档依赖 registry 持久写通道,不可用时明确报错而非静默失败;
- 工作区搬家依赖实体的统一写入通道 `mutate`;若宿主结构变化使其不可用,向导会在改动第一个文件之前中止并明确提示。

## License

MIT

Install

dsh plugin --profile web add github:PianoPrince/dsh-workspace-mover

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source