这个插件让 DeepSeek Harness 在移动硬盘 / U 盘上创建完全自包含的便携工作区:会话、插件、工程文件、交付、日志、账号凭据全部落在盘上;拔盘显示脱机,插盘自动重新识别;换一台装了本插件的电脑插上盘即可继续工作。
「安装插件」装的是整个目录,不是某个单独文件。目录名 portable-workspace-plugin(包名 dsh-portable-workspace)。关键文件如下:
| 文件 | 作用 |
|---|---|
package.json | 插件清单,里面 dsh.bundle.patch 指向 ./cordis.patch.yml —— 这是 DSH 识别「这是一个 bundle 插件」的入口 |
cordis.patch.yml | 把插件行插进 profile 的插件树(id: portable-workspace,name: dsh-portable-workspace) |
index.js | Host 半:HTTP 路由、Agent 工具、工作区增删改查 / 打开 / 迁移 / 密码 / 插件清单 |
client.js | Client 半:侧栏图标 + 主面板 + 全部对话框(React slot 组件) |
lib/*.js | 11 个零依赖模块:asar 读取、兼容性、账号、文件、启动器、包名解析、profile、会话日志、卷识别、工作区等 |
locale/{zh,en}.json · icon.svg | 显示名称、字典与图标 |
1把整个 portable-workspace-plugin 目录放到本机一个固定、不会被删的位置,例如 E:\DSH工作区\portable-workspace-plugin。
link: 软链接(不是复制):本机 profile 只记一条指向该目录的路径。所以这个目录不能移动或删除,否则插件会失效。若要换位置,重新执行一次 install_bundle 即可。
2在 DSH 里用 plugin_manager 安装这个目录(把下面的路径换成你自己的绝对路径):
plugin_manager install_bundle E:\DSH工作区\portable-workspace-plugin
返回 application: applied、warnings: [] 即成功。等价地在会话里说「把这个目录作为 bundle 插件安装」也可以。
3侧栏「全局面板」里出现 「移动工作区」 入口(U 盘图标),点击打开主面板。首次进入可能是空态:
4改了 JS 之后要重载插件才生效(或重启 DSH):
plugin_manager set_bundle dsh-portable-workspace enabled=false
plugin_manager set_bundle dsh-portable-workspace enabled=true
两次都返回 application: applied 即完成。这是开发/升级时最常用的动作。
插入 U 盘后点「重新扫描」。第一次使用点「初始化这块盘」,填入根目录(例如 H:\Protable-DSH)。插件会在盘上写根标记 .dsh-portable.json(含随机 rootId),并建好三个目录:Protable-Workspaces / Projects / support。
<盘>:\Protable-DSH\.dsh-portable.json,另外把记住的非盘符路径写进本机 roots.json 作桥梁。拔盘后该卷进入「脱机的盘」区显示「移动盘未连接」;插回后重新扫描即恢复。
点「新建移动工作区」,填写根目录 / 名称 / 备注,密码可留空(留空 = 不设密码)。设密码时要输入两次(密码 + 确认密码),两次不一致会红字提示;每个密码框右侧有一个「小眼睛」按钮,点一下可明文查看输入内容。
workspace.json,不存明文。创建出来的工程目录在 Protable-DSH\Projects\<ws-id>。
点「打开」。有密码的工作区会弹密码框,输错会被拒绝且不启动:
密码通过后,插件核对工作区声明的插件清单。缺插件会弹对话框,区分「必需 / 可选」,并给出「一键安装缺失插件」「连可选插件一起装」「跳过,直接打开」:
约十几秒后弹出「打开 · 便携实例地址」对话框,给出一个带 token 的随机端口 URL(如 http://127.0.0.1:5xxxx/?token=…)。点「在新窗口打开」会打开一个独立的便携实例窗口。它是全新的 DSH home,第一次进入会看到初始引导页;如果之前点过「同步本机登录」,就已经带着账号,无需重新登录。
工作区行上现在有一排按钮:打开 / 登出 / 修改密码 / 添加插件 / 迁移 / 删除;有密码且已解锁时会显示绿色「已解锁」徽章。「添加插件到这块盘」对话框里可填包名、勾选「作为可选插件」:
卷卡片上的「同步本机登录」把本机 .credentials.yaml / .anonymous-user-id 复制到盘上的便携 home,永不从盘回写本机。这样换电脑不用重新登录。对应的「退出登录」会把盘上这两个凭据删掉。不想把账号带在 U 盘上就不要点同步(凭据以明文 YAML 随盘,保管好 U 盘)。
「登出」会停掉便携实例并清掉本次解锁状态。之后再次打开必须重新输密码。此外:
「修改密码」对话框要求:当前密码(无密码的工作区留空)、新密码、确认新密码。旧密码错误 / 两次新密码不一致都会拒绝。新密码留空 = 取消密码。
点「删除」会弹确认框,必须:
我已知晓删除该工作区,无法恢复!(不输入或输错,确认按钮不可用,Host 端也会再次校验,防止直接调接口绕过);两者都满足才真正删除(连同会话、工程、日志一起)。
「迁移」把工作区复制到另一块盘或本地硬盘路径,可勾选「迁移后删除源工作区」。迁移会重写 rootId 并记 origin,重写每份会话日志 header 里的绝对 cwd 并把会话目录收拢到新位置(否则 jsonl 后端判定损坏),同步修正 workspace registry;新路径写入本机索引,重新扫描即可识别。
从 npm 官方源下载,装到移动盘上的便携配置 Protable-DSH\support\home\profiles\portable,不是从本机已装插件里复制。链路是:随包 node 跑随包 pnpm 执行 pnpm add <包名> → 成功后把带 dsh.bundle 的包追加进 dsh.profile.bundles(这一步才真正激活,只 add 到 dependencies 是不会被加载的)→ 重启便携实例生效。U 盘是 exFAT、无硬链接,冷装一个带几百个传递依赖的插件实测约 2–5 分钟;装过一次后 node_modules 留在盘上,下次秒开。
不是限制,是包名写错。插件市场里显示的是短名(dsh-web-all、modlens),而 npm 上的真实包名带作者前缀(@linxin666/dsh-web-all、@liustack/modlens)。用短名去请求 registry.npmjs.org/dsh-web-all 必然 404;dshmarket 没有 scope,所以能装上。日志里的 ERR_PNPM_FETCH_404 ... dsh-web-all is not in the npm registry 正是这个原因。
dsh-web-all 即可,插件会解析成 @linxin666/dsh-web-all@0.4.14(最新版 0.4.15 要求 DSH ≥0.2.2-rc.1,本机 0.2.1-rc.5 不满足,故自动降到兼容版)。填完整包名或带版本号(@linxin666/dsh-web-all@0.4.13)也支持;404 时会给出「可能需要 scope 前缀」的提示。
便携市场本身是好的(8.9k 条目,见图 6),「搜不到」是按最新版兼容性过滤:市场索引里 @linxin666/dsh-web-all 的最新版 0.4.15 要求 engines.dsh >=0.2.2-rc.1,而本机 DSH 是 0.2.1-rc.5 不满足,于是被「已隐藏 60 个与 DSH 0.2.1-rc.5 不兼容的插件」过滤掉;本机桌面市场的缓存还停在旧的 0.4.14(>=0.2.1-rc.3 满足),所以本机搜得到。解决办法:用「添加插件到这块盘」直接填短名 dsh-web-all(或完整包名),让插件自动挑兼容版本安装 —— 不必依赖市场搜索结果。
--profile desktop 还是 --profile web?都不对。便携实例的 DSH_HOME 被指向 H:\Protable-DSH\support\home,profile 名固定是 portable(lib/constants.js 的 PROFILE_NAME)。在便携实例的会话里执行 dsh plugin --profile desktop add dshmarket 或 --profile web,会写到本机 desktop / 便携 home 里那个用户自建的 web profile,都不是当前实例实际用的 portable,因此「装了却看不到市场」。
portable profile 的 dependencies 并写进 dsh.profile.bundles、重启实例。若一定要用命令,则是 --profile portable(且环境变量 DSH_HOME 要指向便携 home),但仍需手动把包名加进 bundles 才生效 —— 所以更推荐用面板。
一句话:每个工作区 = 移动盘上的一份独立 DSH 数据 + 本机 DSH 启动的一个独立 CLI 实例。打开工作区时,插件生成一个 --patch 覆盖层并启动便携实例:
DeepSeek Harness.exe --expose-internals <cli.js> portable \
--patch <工作区>/launch/cordis.patch.yml --port 0 --no-open
| 机制 | 作用 |
|---|---|
DSH_HOME=<root>/support/home | profile、附件、storages 全在盘上 |
patch 覆盖 session-persistence-jsonl.root / storage-json.root | 会话与存储落在工作区目录,多个工作区互不混淆 |
patch 覆盖 working-directory.defaultDirectory / workspace-controller.documentsDirectory | 工程与交付落在 Projects/<id> |
patch 覆盖 agent-default-model → deepseek-account / deepseek-flash | 用同步上盘的 DeepSeek 账号 grant,无需另配 API Key |
CLI web host(ELECTRON_RUN_AS_NODE=1) | 不启动 Electron 桌面壳,故没有 userData 落本地;随机端口 + token,独立窗口 |
依赖(都是本机已有的,不需要额外装东西):
dsh-desktop-host/lib/cli.js)、随包 node、随包 pnpm、asar 内的基础 bundles。插件本身零 DSH 包依赖,服务都从 ctx 运行时取。~/.dsh/portable-workspace/roots.json(只记路径,不含任何工作区数据)。边界 物理拔插未做真实断电测试(用移除根标记模拟),跨电脑未做双机实测(只有一台机器);其余功能均在真实 U 盘 H: 上全流程验证通过。
先说结论:插件不会随 DSH 升级「自动更新」,但也不会被升级删掉,通常升级后照常可用;要不要更新、怎么更新,取决于你是「本机插件」还是「盘上插件」这两类。
★ 升级前必须先释放文件锁,否则升级会失败
便携实例是以 detached 独立进程运行的 DeepSeek Harness.exe,会长期占用安装目录里的 DeepSeek Harness.exe 与 resources/app.asar;Windows 不允许覆盖正在运行的可执行文件。升级器把新版下载到 %LOCALAPPDATA%\@deepseek-aidsh-desktop-updater\pending\ 后,若文件仍被锁住,就会卡在安装阶段、版本一直不变。
正确顺序:
DeepSeek Harness.exe 版本已变(如 0.2.2-rc.1)后,再重新打开移动工作区。面板会自动检测「待安装升级」与「残留便携实例」并弹出黄色警示条;工具 stop-all 可脚本化完成第 1 步。
★ 若已「关闭全部便携实例」、也完全退出主程序,升级仍失败
如果弹的是「未能安全停止任务,更新尚未安装,请稍后重试。」,那与移动工作区插件无关:这是 DSH 升级器自身要求 desktop host 在 10 秒内优雅退出(exitCode=0 且收到 IPC shutdown-complete),而 host 上挂着一个慢启动的 stdio MCP 子进程(例如 npx @playwright/mcp,其 stdio-startup-guard 超时是 15 秒)没能在窗口内收尾,触发 preparationFailure="stop-failed" 拒绝安装,随后 host 被自动拉起、客户端显示「重新连接中...」。关掉便携实例并不能解决这一条。
处理:到「MCP 连接器」把卡住的 playwright 连接停用/删除后重试;或完全退出全部 DSH 进程(任务管理器确认没有 DeepSeek Harness.exe 残留),再手动运行 %LOCALAPPDATA%\@deepseek-aidsh-desktop-updater\pending\ 里已下载好的 .exe 安装器(NSIS 安装器,可直接双击),绕过 host 优雅退出流程。装完确认 exe 版本变化即可。
| 对象 | 存在哪里 | DSH 升级时 |
|---|---|---|
移动工作区插件本体(dsh-portable-workspace) | 本机目录,以 link: 挂在当前 profile 的 dependencies / bundles 里 | profile 配置是持久的,升级 DSH 不会动它;插件代码就在那个目录,原地保留 |
便携实例的基础 bundles(@deepseek-ai/dsh-base 等) | 本机 DSH 安装目录的 app.asar 内 | 随 DSH 升级自动更新 —— 便携实例每次启动都读本机当前版本的 CLI 与 asar |
| 工作区数据(会话 / 工程 / 日志 / 便携 home) | 移动盘 | 完全不受影响,一直在盘上 |
| 工作区依赖的第三方插件 | 移动盘上的便携 profile | 不随 DSH 升级变,版本由你自己「添加插件」时决定 |
link: 记录还在,重启后自动加载,无需任何操作。engines.dsh 硬红线,且所有能力都通过公开 Service / ctx 取得,一般跨小版本可用。若升级后内部 Service 契约变化,现象会是面板报错或打不开 —— 这时把插件目录里的代码更新到适配新版的版本,再 set_bundle false→true 重载即可(数据仍在盘上,不会丢)。DeepSeek Harness.exe + cli.js 启动。所以升级 DSH 后,下次打开工作区,便携实例就跑在新版上了。engines.dsh 重新过滤;升级到 ≥0.2.2-rc.1 后,dsh-web-all 0.4.15 之类原本被隐藏的插件就会重新出现。link: 到这个目录);plugin_manager set_bundle dsh-portable-workspace enabled=false 再 enabled=true 重载,或重启 DSH;install_bundle <新路径>。整个过程不需要动移动盘,工作区数据不动、不迁移。
cwd 对齐到新盘路径,避免 jsonl 后端判损坏。
# 安装(每台电脑一次)
plugin_manager install_bundle <插件目录绝对路径>
# 改代码后重载
plugin_manager set_bundle dsh-portable-workspace enabled=false
plugin_manager set_bundle dsh-portable-workspace enabled=true
# 添加插件(在面板里操作即可,等价于下列链路)
pnpm --dir <盘>:\Protable-DSH\support\home\profiles\portable add <完整包名>
# 然后把包名追加进该 profile 的 package.json → dsh.profile.bundles,重启便携实例
<盘>:\Protable-DSH\
├─ .dsh-portable.json 根标记(kind/rootId/format),盘符无关识别的桥梁
├─ Protable-Workspaces\
│ └─ <ws-id>\
│ ├─ workspace.json 清单:名称、密码 scrypt、插件声明、profile、origin
│ ├─ sessions\ 会话日志(.jsonl.zstd)
│ ├─ storages\ 存储域 / 会话投影缓存 / workspace registry
│ ├─ logs\ launch.log、plugins.log
│ └─ launch\ cordis.patch.yml、start-portable.cmd、runtime.json
├─ Projects\<ws-id>\ 工程文件与交付(默认工作目录也指向这里)
└─ support\home\ 便携 DSH_HOME:profiles\portable\、.credentials.yaml 等
卸载是按电脑进行的:它只把这台电脑 profile 里那条 link: 软链接移除,不会删除移动盘上的任何数据。卸载后换一台仍装了插件的电脑插上盘,工作区照常打开。
DeepSeek Harness.exe。plugin_manager set_bundle dsh-portable-workspace enabled=false
plugin_manager remove_bundle dsh-portable-workspace
也可以在 DSH 的「插件」面板里找到「移动工作区」→ 停用 → 移除。enabled=false 会触发插件的 dispose 清理,remove_bundle 再从 profile 的 dependencies / dsh.profile.bundles 里删掉这条记录。C:\Users\<用户>\.dsh\portable-workspace\roots.json。它只记录「这台电脑见过哪些移动工作区根路径」,不含任何工作区数据;删掉后再插盘会当作全新卷重新识别(不会丢数据)。link: 安装,移除 bundle 后本机那个解压出来的插件目录就可以安全删掉了。<盘>:\Protable-DSH\ 下的 Protable-Workspaces\(会话/清单/日志)、Projects\(工程与交付)、support\home\(便携 profile 与插件)全部原样保留。卸载 ≠ 删除工作区;想彻底销毁某个工作区,请在卸载前用面板里的「删除」(需固定文字 + 密码双确认),或卸载后手动删除盘上对应目录。