Repository navigation
Releases
v0.2.1 —— 修复保存后界面不同步
修复:点了开关显示「已保存」,开关却纹丝不动
Field / BoolField 把 /settings 的响应整个丢掉了,界面只能等下一次轮询
(默认 30 秒)才更新。用户看到的是「已保存」但开关没动,退出重进设置页才显示新值
—— 因为那是重新拉了一次 /state。
文本框是同一个根因,只是症状不同:dirty 一直挂在 true 上,「未保存」不消失。
所有走 /settings 的控件都中招,不只是开关。
修法(两处,缺一不可)
-
/settings回一份完整状态快照(buildState()),不再只回
effective/defaults/ …。派生字段(工作区标题、解析出来的目录、回收站统计、
清理预览)根本不在原来的响应里,所以它们也必须一起回。这样客户端也不必再写
逐字段的合并逻辑 —— 少一处可以漏的地方。 -
面板新增「保存后广播」订阅:保存成功即把响应并回面板状态,并且作废在途的轮询
(否则一个更早发出的/state可能在保存之后才回来,把刚写进去的值盖回旧的
—— 那就变成了「有时候灵有时候不灵」)。
用订阅而不是给每个字段传 prop,是因为「保存后同步界面」这件事每个字段都必须做,
而漏掉一个的症状是静默的。订阅让新增字段自动获得这个行为 —— DangerZone 里嵌套的
那个字段正是传 prop 最容易漏掉的一处。
顺带:保存后显示的是宿主重算过的值
不是本地乐观值。所以非法输入被夹取(pollSeconds 填 99999 → 600)会立刻如实显示,
不用等半分钟;保存失败时开关也不会自己翻过去,而是显示宿主的错误。
测试脚手架修正:迷你 React 改成每个组件一份 hook 状态
以前整棵树共用一个扁平数组,于是「条件渲染掉一个带 hook 的子组件」会让它后面所有组件的
槽位整体前移,读到别的组件的状态 —— 测试里真的崩在 DangerZone 的 typed.trim() 上,
因为 typed 读到了相邻组件的 null。
真实 React 是按组件隔离 hook 的,所以那是脚手架在说谎,不是产品 bug。
但它会挡住正当的测试(比如测 enabled 开关),所以必须修。
测试
| 之前 | 现在 | |
|---|---|---|
| 宿主 | 64 | 64 |
| 客户端 | 38 | 42 |
新增 [J] 保存后界面立即同步(回归):切换开关后界面必须立刻反映新值
(且重渲染不产生额外请求)、保存文本框后要显示宿主夹取后的值且「未保存」消失、
嵌套在 DangerZone 里的字段同样同步、保存失败时开关保持原状并显示宿主错误。
该回归验证过:把
broadcastSettingsSaved(body)注释掉,测试立刻以
「保存后开关必须立刻反映新值」失败。
升级
插件安装后需要重启 DSH 才加载。升级请先「卸载」再装,然后重启。
重启后插件页标题右侧会显示 v0.2.1 —— 没看到就说明新构建还没生效。
完整变更:v0.2.0...v0.2.1
v0.2.0 —— 清空/重置工作区 + 可恢复回收站
新增
清空工作区 / 重置工作区
面板新增「清理与重置」区块(默认收起)。两个操作语义明确分开:
| 操作 | 搬走什么 | 留下什么 |
|---|---|---|
| 清空 | 顶层全部内容与子目录 | AGENTS.md、_trash/ |
| 重置 | 内容以及 AGENTS.md |
搬完后按当前设置重新生成 AGENTS.md,并确认工作区登记仍有效 |
删除是可恢复的
两个操作都不直接删除任何东西。实现上是对每个顶层条目做一次 rename,
搬进工作区里的回收站:
<工作区>/
├── AGENTS.md
└── _trash/
├── 20261009-010132-341/
│ ├── tmp.txt
│ ├── _scratch/
│ └── notes/
└── 20261008-231500-123/
想找回什么直接从 _trash/<批次>/ 拿;确认不要了就把批次目录删掉。
trashKeep(默认 5,范围 0–100)限制保留份数,超出的最旧批次才会真正删除。
自动清理只认严格匹配 YYYYMMDD-HHmmss-SSS 的目录名 —— 只删得掉插件自己创建的批次。
你自己往 _trash 里放的东西,哪怕叫 trash 或 2026,永远不会被碰。
打字确认
确认面板会列出将被搬走的顶层条目(超过 8 条折叠计数),并要求逐字输入工作区目录名
才让点确认。没有用「确定 / 取消」弹窗,因为它挡不住手滑,而输入框能 ——
而且它逼你确认自己清的是哪一个目录,毕竟目录名与父目录都可配置。
新端点
POST /api/dsh-bundle-default-workspace/clear 与 /reset,都支持 {"dryRun": true} 只读预演。
校验顺序:来源 → 方法 → body → 插件是否启用 → dryRun → 确认词。
插件停用时一律拒绝 —— 面板只读,接口也不放行。
清空与重置刻意不提供工具入口,只有面板能触发。工具描述里改为告诉用户去面板操作。
安全边界
- 不可能删到工作区外面。只做
rename,从不递归进入子目录。删目录树最怕符号链接与
Windows 联接点(递归删除会顺着链接走进C:\或网络盘),而rename搬走的是链接本身。
测试里真的建了一个指向工作区外面的联接点,断言搬完之后目标目录与其中文件毫发无损。 _trash永远不动。清空保留它,重置也排除它。此前reset只在clear的保留名单里
排除它,于是试图把_trash搬进_trash/<批次>/_trash—— 自己搬自己,Windows 直接EPERM。
回归测试留住了这条。- 不谎报。顶层条目超过 5000 个时直接拒绝执行并说明原因,而不是「搬走一部分然后报告成功」。
某个条目搬不动(被占用、权限)时继续搬其余的,并把它列进failed。
修复
measureTree会跟随作为「根」传入的符号链接。walk()只对子条目查
isSymbolicLink(),但readdir会跟随作为根传进来的链接,体积统计因此走进了目标目录。
生产路径本来就会先判类型才调用它,所以没有真的踩到,但已在入口补一层检查。movedCount把搬失败的条目也算成了成功,于是面板会说「已清空 3 项」同时列出一条
「没能搬走」。现在只数真正搬成功的,另加attempted。- 链接自身的元数据大小不再计入「待搬体积」(报 0)—— 那不是用户要搬走的内容。
其他
/state新增cleanup字段。体积统计用lstat不跟随链接,且有硬上限(2000 条目 / 8 层)。clear/reset两份清单改由同一次扫描派生,省一半 I/O,也从根上避免两份清单对不上。AGENTS.md种子新增「_trash/不要动」一节,免得智能体把它当成该清掉的垃圾顺手整理。
测试
| 之前 | 现在 | |
|---|---|---|
宿主 (test-e2e.mjs) |
36 | 64 |
客户端 (test-client.mjs) |
29 | 38 |
新增的用例覆盖:批次名格式、递归体积统计、保留策略只删自己创建的批次、链接安全、
dryRun 不动文件、确认词不符 / 缺失 / 空 body / 非 POST / 非回环来源各自的拒绝码、
内容确实进了回收站且可读回、子目录整棵搬走、重复清空是空操作、重置后重新生成 AGENTS.md、
trashKeep 生效与夹取、插件停用时接口拒绝,以及面板侧未输入 / 输错确认词时按钮必须禁用、
空操作与「全部搬不动」的文案分得清。
完整变更:v0.1.4...v0.2.0
v0.1.4 — 修复开关显示与字段标签
修复两个真的会影响可用性的问题 —— 不只是好不好看。
1. 开关永远显示为「关闭」
面板里所有开关(启用插件、自动创建、写入 AGENTS.md)看起来都是关的,即使它们实际是开的。
根因是轨道背景写成了:
background: "var(--dsw-alias-bg-brand, var(--dsw-alias-state-success))"这两个变量在 DSH 主题里都不存在。CSS 的规则是:一条声明里只要有无法解析的 var(),整条声明就被判为非法、整个忽略(不会退化到第二层回退,因为第二层也不存在)。所以轨道根本没有背景色,不管开还是关都只是一个空心胶囊。
滑块又是写死的 #fff,压在浅色背景上几乎看不见 —— 两个问题叠在一起,就变成了截图里那种「一排空心胶囊」。
现在开关改为与 DSH 自带开关组件完全一致的几何与配色(直接照 web-frontend 里的 ._switch_1ik0f_5 实现):
| 关闭 | 开启 | |
|---|---|---|
| 轨道 | --dsw-alias-border-l3 |
--dsw-alias-brand-primary |
| 滑块 | --dsw-alias-switch-thumb |
--dsw-alias-label-primary-foreground,位移 16px |
尺寸 36×20、padding: 2px、无边框 —— 和插件页里其它原生开关看起来是同一个东西。
2. 所有输入框都没有可见标签
Field 组件把 label 只用在 aria-label 上,从来没有渲染它。所以界面上只剩「一个孤零零的输入框 + 一串说明」,你根本不知道哪个框是「目录名」、哪个是「父目录」。
现在字段统一为两列网格:左列是标签 + 说明,右列是控件。
顺带做的
- 面板按 DSH 的设计令牌重做:按钮有 hover 反馈和主/次/幽灵三种变体,输入框有聚焦描边,「保存」只在真有改动时才升为主按钮。
- 又找出并修掉一批引用不存在变量名的样式(
--dsw-alias-state-danger、--dsw-alias-state-warning、--dsw-alias-font-mono等)。
为什么之前没测出来
测试脚手架自己有问题:createElement 不会真正调用函数组件、useEffect 是空实现。结果是面板永远停在 loading 分支,所有字段组件一次都没被真正渲染过,上面两类 bug 自然测不到。
已修正脚手架(真正调用组件、真正跑 effect、支持多次渲染),并补了三条回归:
- 所有
var(--dsw-*)必须存在于 DSH 主题的 token 清单里 —— 这类错误会让整条样式静默失效、界面无任何报错,只能靠断言拦住 Field/BoolField必须渲染可见标签(不能只靠aria-label)- 开关轨道在开/关两态都必须有背景色,几何与配色对齐原生组件
安装 / 升级
插件管理器目前不支持自动更新:先卸载,再重新安装,然后重启 DSH。
https://github.com/OraSkyC/dsh-bundle-default-workspace
重启后面板标题旁的版本号应显示 v0.1.4。
v0.1.3 — 默认工作区(首个公开发布)
第一个公开发布版本。
这是什么
给 DeepSeek Harness(DSH)一个「默认工作区」—— 杂七杂八、不想区分项目的小事都丢这里。
它解决的不是「怎么管理项目」,而是**「这类事根本不值得建项目,但随手丢进项目目录又很脏」**。
- 一个自动创建并登记的默认工作区(默认在
<文档目录>/deepseek-harness/default-workspace) - 一张可视化设置卡,改配置立即生效、不用重启
- 一个
default_workspace智能体工具(status/ensure/path/describe) - 往目录里写一份
AGENTS.md,明确告诉智能体这里不是项目,且不要在顶层直接创建文件
安装
设置 → 插件 → 添加插件,填入:
https://github.com/OraSkyC/dsh-bundle-default-workspace
或命令行:
dsh plugin --profile desktop add https://github.com/OraSkyC/dsh-bundle-default-workspace装完重启 DSH(安装界面会提示「下次启动后加载」)。
本次内容
0.1.3 — AGENTS.md 顶层禁令写成硬规则
原来的措辞是「保持根目录整洁;按主题建子目录」,语气是建议、埋在「约定」小节里,
而且只覆盖大文件/依赖/构建产物 —— 普通小文件和临时文件完全没提,也没说该放哪。
现在独立成节、排在最前,明确禁止在顶层创建文件,点名临时文件与中间产物
(tmp.txt、out.json、test.py…),给出 _scratch/ / notes/ / scripts/ 的去处,
列出依赖与构建产物,并补一条兜底:「拿不准就先问一句」。
0.1.2 — 面板折叠修复
- 修复区块收起后无法再展开(收起时把整张卡片连同标题按钮一起移出了渲染树)。
- 面板标题右侧显示版本号小标,用于确认新构建是否已被宿主加载。
VERSION改为从package.json读取,不再硬编码。
0.1.1 — 修复 inject 导致的 404
Cordis 的 ctx 是受限代理,访问未声明在 inject 里的属性会抛错而不是返回 undefined。
webServer 漏声明导致 apply() 整体失败、插件显示「异常」、三条路由全部 404。
0.1.0 — 首个版本
宿主半侧(配置归一化、三级目录解析、稀疏状态覆盖层、AGENTS.md 种子、三条回环 HTTP 路由、
智能体工具、幂等轮询)与浏览器半侧(设置卡、中英双语字典)。
要求
| 项目 | 要求 |
|---|---|
| DSH | web profile |
| Node | >= 22.19.0 |
| 运行时依赖 | 无(只用 node: 内建模块) |
| 构建步骤 | 无(手写纯 JS,git 装完直接可用) |
完整变更记录见 CHANGELOG.md,
用法与配置见 README.md。