@wjj-8283/dsh-temp-workspace
Temporary workspace for DSH web — one click creates a throwaway workspace whose conversations are deleted at the next restart (immediately or after a delay, optionally behind a confirm dialog). Pin a temp workspace to keep it: its files move into the folder you pick, its title becomes that folder's
Install
dsh plugin --profile web add @wjj-8283/dsh-temp-workspace
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
@wjj-8283/dsh-temp-workspace
一个 DSH web 客户端插件,提供 临时工作区(临时工作区) 功能。
点击侧边栏「添加工作区」(+)右侧的小沙漏图标,即可创建一个一次性工作区并打开一个全新的对话。下次 Harness 重启时,这个插件会删除该工作区下的全部对话以及工作区本身——临时工作区不留任何痕迹。
它是什么
- 宿主半边(
lib/index.js)—— 一个 Cordis 插件,注入
webServer / workspaceRegistry / sessionPersistence(可信授权列表改为
按需读取 web app 的 webStartup 服务,不作为硬依赖),注册受浏览器信任围墙保护的
/temp-workspace/api 路由,并在启动时驱动清理逻辑。
- 浏览器半边(
src/client.js→lib/client.js)—— 在侧边栏「+ 添加工作区」
按钮旁注入一个小图标;点击后调用宿主路由,并在新建的工作区里打开一个新对话。
它还注册了一个 设置 → 临时工作区 独立页面,并在设置开启时于启动阶段弹确认对话框。
安装方式
从 npm 安装(预构建,推荐)
该包已发布到 npm,lib/ 为预构建产物,安装无需构建、可跳过 pnpm 的 allowBuilds 授权——一条命令搞定:
dsh plugin --profile web add @wjj-8283/dsh-temp-workspace
dsh plugin … add 会把该 spec 转发给 profile 目录里的 pnpm,并代为合并 dsh.profile.bundles。因为清单声明了 dsh.bundle.patch,包会自动加入 Loader 层栈,下次启动 dsh web 时宿主把它组合为 Loader 条目、注册 /temp-workspace/api 路由,客户端插件(含设置页面)进入 __DSH_BOOT__。
从 GitHub 安装
直接从 GitHub 安装,使用 dsh plugin 转发器(在 profile 目录里 pnpm clone + add,并自动合并 bundle 层):
# 启动某个 profile,例如 web 应用 dsh plugin --profile web add github:wjj-8283/dsh-temp-workspace
因为清单声明了 dsh.bundle.patch,add 会把该包自动追加到 dsh.profile.bundles,下次启动 dsh web 时宿主就把它组合为 Loader 条目、注册 /temp-workspace/api 路由,客户端插件(含设置页面)进入 __DSH_BOOT__。
若想自己驱动 pnpm,可在 profile 目录里执行同一 spec:
cd "$DSH_HOME/profiles/web" pnpm add github:wjj-8283/dsh-temp-workspace
要固定到某个分支/标签/提交,按 pnpm 的 git ref 语法追加:
dsh plugin --profile web add github:wjj-8283/dsh-temp-workspace#<ref>
构建后的客户端 bundle(lib/client.js)已提交在仓库里,所以 git 安装不需要构建步骤。要改它,克隆仓库后用node build.mjs --watch(见[开发循环](#开发循环)),或改为从本地 checkout 安装(见[接入 profile](#接入-profile))。
设置 → 临时工作区
插件在 设置导航 里注册了自己的独立页面(settings.section 插槽,id 为
dsh-temp-workspace),页面里是下面三个设置项。取值存放在 profile patch 中 Loader 条目
dsh-temp-workspace 的 config 里(dsh 0.1.7 起旧的 settings.plugin.item 卡片插槽与
~/.dsh/settings.yaml 均已移除):
设置 · 取值 · 默认 · 含义
deleteMode · immediate / delayed · immediate · 下次启动时何时删除临时工作区
deleteDelay · 秒 · 3600 · delayed 模式下删除前的宽限期
confirmBeforeDelete · 开 / 关 · 开 · 删除前是否先询问用户
confirmBeforeDelete开:宿主启动时绝不自动删除,而是挂起这些工作区,
浏览器弹出确认对话框,提供两个选项:
- 删除 —— 清空被挂起的工作区(会话 + 注册 + 目录)。
- 临时保留 —— 本次不动它们,但仍然是临时:它们还在标记里,会在之后的
启动时再次(按设置)被确认/清理。
- 未作答时工作区原样保留到下次启动。
永久保留不在启动确认弹窗里——它有自己的按钮。侧边栏里每个临时工作区行
(标题「临时工作区」)在其「+」(New Session) 旁边有一个图钉按钮。点击后选取一个
目标文件夹(系统目录选择器:桌面端有 __DSH_DIRECTORY_PICKER__ 桥时直接用它,否则
dsh 0.2.x 走 uiWorkspace.pickDirectory(),0.1.x 走 ctx.workspaces.pickDirectory())。临时工作区的文件会
直接移进该文件夹(不会额外创建「临时工作区」子文件夹),该文件夹本身成为永久工作区,
并用其文件夹名作为工作区标题。同时迁移对话:把每个会话日志头行的 cwd 改写为该
文件夹路径,并把日志目录移到新 cwd 的 projectKey 槽位。迁移进行期间会显示一个
「正在迁移工作区…」提示(没有进度条,只提醒不要中途退出 Harness;10 秒后会给出关闭入口)。
成功后弹出的对话框只说明如何手动重启——没有「立即重启」按钮,因为桌面端无法重启自己的
宿主进程:
- Web 版:在终端里退出
dsh进程,然后重新打开; - 桌面版:彻底退出桌面端再重新打开
- Windows:右键托盘区的 DSH 图标 → 退出;
- macOS:直接按 Command+Q,或右键 Dock 栏里的图标 → 退出。
confirmBeforeDelete关:自动删除——启动时立即执行,或在deleteMode = 'delayed'
时等待 deleteDelay 秒后执行。
清理是如何工作的
DSH 没有公开的「删除会话日志」API——持久化层是 append-only。所以临时工作区的删除分为四步:
0. 先拆在线会话 —— 如果宿主进程里还活着该工作区的会话(确认弹窗、延迟自动删除、或只刷新了页面而宿主并未真正重启时,删除都会在仍持有这些会话的宿主里运行),会尽力拆除它们:
先取消 agent 并等它回到空闲(whenIdle,镜像 agent loop 自身的销毁顺序),再先把待写内容落盘
(sessions.flush),最后从会话存储中摘除该条目(发出 session/disposed,客户端立即关闭该对话)。
先 flush 再摘除对多会话工作区至关重要:摘除会触发 session/disposed,持久化协调器随之做一次
"退休刷新"(retire → flush → initFor),若会话还有未落盘的写入,这次刷新会在目录被删除后把日志
重新物化出来——那些会话就会变成不可读取的 Ungrouped 残留。先 flush 后,退休刷新无内容可写,日志
只会被删除一次。
0b. 归档冷会话 —— 本轮宿主里从未被打开(未 attach)的会话只有磁盘日志,删日志后浏览器收不到
session-removed 事件,会在侧边栏残留成不可读取的 Ungrouped 条目。archiveColdSessions 在删日志
之前把它们逐一归档(workspaceRegistry.archiveSession):归档集变化会推送
host/archived-sessions-changed,侧边栏(分组、扁平列表、搜索)都会过滤归档 id,残留条目立即消失。
归档留下的 archived-id 记录由启动清扫回收。
1. 再删会话 —— 工作区所属的每个会话(其头行 cwd 等于工作区路径)通过
ctx.sessionPersistence.locate 定位,并用 fs.rm 删除其磁盘目录。
2. 删注册 —— ctx.workspaceRegistry.delete(id) 删除工作区记录(本身不动日志/目录)。
3. 删目录 —— 删除临时目录(位于 <dsh-home>/temp-workspaces/<uuid>)。
插件用 <dsh-home>/temp-workspaces/state.json 记录临时工作区(持久化标记
{ workspaceId, path, createdAt })。删除由该标记里的 path 驱动,所以重启前已通过
UI 删除注册记录的临时工作区仍会清理残留目录(修复了「残留文件夹」bug)——目录以
标记路径(而非注册记录)为准。
零残留:删除时还会清理会话的投影缓存行(session_projcache 域,位于
~/.dsh/storages/session_projcache.json)与旧布局遗留的 per-session 缓存文件。
启动时还有一次孤儿残留清扫(pruneOrphanTempResidue):凡 cwd 位于临时根目录下、
且不再被标记引用的投影缓存行,连同其 archived-id 记录与遗留缓存文件一并删除。这些行
永远不会把幽灵对话显示出来(身份校验后读不到),但"不留痕迹"就是不留痕迹;用户对真实
会话的主动归档不会被触碰。
目录结构
temp-workspace-plugin/ package.json name, dsh.bundle.patch + dsh.client, exports cordis.patch.yml 把该插件插入为宿主 Loader 条目 lib/index.js 节点半边 —— /temp-workspace/api 路由 + 设置 + 启动清理 src/client.js 浏览器半边源码(图标 + 设置页面 + 确认弹窗) lib/client.js 构建后的浏览器半边(生成文件,勿改) build.mjs 把 src/client.js 包装 -> lib/client.js(HMR watch)
开发循环
node build.mjs --watch
lib/client.js 是 client-modules 提供、dsh-client-hmr 轮询的唯一文件;编辑
src/client.js 会热更新界面(无需刷新页面)。宿主半边改动(lib/index.js)需要重启
dsh web。
接入 profile
# 本地 checkout / clone(任意绝对或相对路径) dsh plugin --profile web add /path/to/dsh-temp-workspace
这会把它加入 dsh.profile.bundles。下次启动 dsh web 时宿主组合该行、
注册 /temp-workspace/api 路由,客户端插件(含设置页面)进入 __DSH_BOOT__,并运行启动清理/确认流程。
API(/temp-workspace/api/<method>)
读取用 GET,变更用 POST。
方法 · 返回 · 说明
POST create · { workspace, created: true } · mkdir + workspaceRegistry.create + 标记
GET list · { entries } · 当前标记为临时的 [{ workspaceId, path, createdAt }]
POST delete · { ok: true } · 立即删除一个临时工作区(会话 + 注册 + 目录)
GET/POST config · { ok, value } · 读取设置,或 POST {…} 写入补丁
POST configReset · { ok, value } · 恢复所有设置为默认
GET pending · { ok, value } · 被挂起的工作区 + confirmBeforeDelete + deleteAt
POST confirm · `{
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::3,最后更新 2026-10-25