dsh-disk-cleaner 1.6.9 → 1.6.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  <!-- badges -->
6
6
  | | |
7
7
  | --- | --- |
8
- | **version** | `1.6.9` |
8
+ | **version** | `1.6.10` |
9
9
  | **DSH** | `0.2.1-rc.5`(开发与实测版本;用到 `plugins.bundle.config`、`settings.section`) |
10
10
  | **license** | [MIT](LICENSE) |
11
11
  | **Node** | `>= 22`(仅运行冒烟测试需要) |
@@ -166,16 +166,16 @@ These are enforced in `lib/catalog.js` and must not be relaxed:
166
166
 
167
167
  Listed in the curated [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) catalog, which powers **dshmarket → Discover** ([PR #12970](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/12970), merged): search **dsh-disk-cleaner** there and install it in one click. The direct installs below do the same thing by hand.
168
168
 
169
- Published on npm as [`dsh-disk-cleaner`](https://www.npmjs.com/package/dsh-disk-cleaner) — latest `1.6.9`:
169
+ Published on npm as [`dsh-disk-cleaner`](https://www.npmjs.com/package/dsh-disk-cleaner) — latest `1.6.10`:
170
170
 
171
171
  ```bash
172
172
  npm i dsh-disk-cleaner
173
173
  ```
174
174
 
175
- A prebuilt tarball is attached to the [v1.6.9 Release](https://github.com/stallenlu/dsh-disk-cleaner/releases/tag/v1.6.9); installing from it skips DSH's build-approval step:
175
+ A prebuilt tarball is attached to the [v1.6.10 Release](https://github.com/stallenlu/dsh-disk-cleaner/releases/tag/v1.6.10); installing from it skips DSH's build-approval step:
176
176
 
177
177
  ```bash
178
- dsh plugin --profile <profile> add https://github.com/stallenlu/dsh-disk-cleaner/releases/download/v1.6.9/dsh-disk-cleaner-1.6.9.tgz
178
+ dsh plugin --profile <profile> add https://github.com/stallenlu/dsh-disk-cleaner/releases/download/v1.6.10/dsh-disk-cleaner-1.6.10.tgz
179
179
  ```
180
180
 
181
181
  Then enable it in the profile — in `$DSH_PROFILE_DIR/package.json`:
@@ -183,7 +183,7 @@ Then enable it in the profile — in `$DSH_PROFILE_DIR/package.json`:
183
183
  ```json
184
184
  {
185
185
  "dependencies": {
186
- "dsh-disk-cleaner": "^1.6.9"
186
+ "dsh-disk-cleaner": "^1.6.10"
187
187
  },
188
188
  "dsh": {
189
189
  "profile": {
@@ -204,7 +204,7 @@ The bundle's own `cordis.patch.yml` inserts the plugin row; no profile patch edi
204
204
  ### Development
205
205
 
206
206
  ```bash
207
- node smoke.mjs # 114 assertions, zero dependencies
207
+ node smoke.mjs # 116 assertions, zero dependencies
208
208
  node --check lib/client.js # and lib/index.js, lib/catalog.js, lib/audit.js, lib/plan.js
209
209
  ```
210
210
 
@@ -260,7 +260,7 @@ What the smoke test deliberately does **not** cover: anything that walks a real
260
260
  ├─ package.json dsh.bundle.patch + dsh.client { platform: "web" }
261
261
  ├─ cordis.patch.yml - insert: id: dsh-disk-cleaner
262
262
  ├─ lib/index.js (525) Host: HTTP route + disk_cleaner tool, same run() for both; reveal
263
- ├─ lib/catalog.js (1242) The 32 targets, measurePath / purgePath / resolveTargets / drives
263
+ ├─ lib/catalog.js (1252) The 32 targets, measurePath / purgePath / resolveTargets / drives
264
264
  ├─ lib/audit.js (365) Read-only assessment, 10-minute cache, areaPaths, exposes no deletion
265
265
  ├─ lib/plan.js (150) Pure: measurements → ordered steps
266
266
  ├─ lib/history.js (71) Append-only run log in $DSH_HOME/dsh-disk-cleaner; never throws
@@ -269,13 +269,21 @@ What the smoke test deliberately does **not** cover: anything that walks a real
269
269
  ├─ icon.svg Plugin icon
270
270
  ├─ README.md This file
271
271
  ├─ LICENSE MIT
272
- └─ smoke.mjs 114 assertions, zero dependencies
272
+ └─ smoke.mjs 116 assertions, zero dependencies
273
273
  ```
274
274
 
275
275
  Client-half slots: `sidebar.panellist` (order 50, disposed in settings-only mode), `main` (keyed by the package name), `settings.section` (order 30, disposed in sidebar-only mode), `plugins.bundle.config` (keyed by the package name).
276
276
 
277
277
  ### What's new
278
278
 
279
+ - **1.6.10** — Cleaning no longer restarts the harness. The `app-caches` target listed the
280
+ harness's own Chromium profile (`%APPDATA%\@deepseek-ai\dsh-desktop`), so cleaning it
281
+ emptied the caches of the very process drawing the panel: Chromium rebuilt them at once and
282
+ the renderer reloaded, which reads as the plugin hot-restarting instead of returning to its
283
+ own screen. Those four folders are gone from the target, and `resolveTargets()` now refuses
284
+ any path inside the host's data folder outright, so a future definition cannot add them
285
+ back. Everything else in `app-caches` (WPS, FTNN, Qoder, Trae, WeChat devtools, appGallery)
286
+ is unchanged. Two regression assertions cover both halves of the fix.
279
287
  - **1.6.9** — The one white box left in a dark panel is gone. The confirmation was `window.confirm`, which the browser draws outside the document: it cannot see `color-scheme`, the baseline tokens or the host theme, so a dark panel got a light rectangle in the middle of it. It is now a dialog the panel draws itself — same information (how many, how much, every target with its size, anything irreversible), painted with `--dsb-*`, dismissible with Escape, the overlay or Cancel, and the destructive button deliberately holds no focus so a stray Enter cannot delete. The plan-step confirmation goes through the same component. No behaviour change beyond the painting.
280
288
  - **1.6.8** — Fixes the other half of the 1.6.7 story: the panel still answered "origin rejected" when the desktop window sent `Origin: http://localhost:<port>` while the request itself arrived at `127.0.0.1`, because the check compared those strings literally. The gate now decides on the **peer address** of the connection — something a remote page cannot influence — and only then looks at `Origin`: the app's own scheme, no `Origin` at all, or any loopback host name is accepted, everything else is still refused. An `https://evil.example` origin is rejected even from a loopback socket. The three assessment error paths (evaluate, reveal, run-a-plan-step) now go through the same translated-code path as the rest of the panel, so a Chinese user sees "请求来源不被接受" rather than the raw `origin rejected` string.
281
289
  - **1.6.7** — Fixes a regression 1.6.6 shipped: refusing every request without an `Origin` header locked the plugin's own panel out of its API ("请求来源不被接受"), because the desktop window posts from `dsh-app://app`, not from `http://127.0.0.1:<port>`. The check now accepts the desktop scheme and a header the desktop forwarder stripped, but only when the request already arrived on a loopback address; a plain web page still cannot reach a loopback port without sending an `Origin`, so the door stays shut. No other behaviour changed.
@@ -443,16 +451,16 @@ Client-half slots: `sidebar.panellist` (order 50, disposed in settings-only mode
443
451
 
444
452
  已收录进精选目录 [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin),它是 **dshmarket → 发现** 的数据源([PR #12970](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/12970),已合并):在 dshmarket 里搜索 **dsh-disk-cleaner** 即可一键安装。下面的直接安装方式就是手动做同一件事。
445
453
 
446
- 已发布到 npm:[`dsh-disk-cleaner`](https://www.npmjs.com/package/dsh-disk-cleaner),最新版 `1.6.9`。
454
+ 已发布到 npm:[`dsh-disk-cleaner`](https://www.npmjs.com/package/dsh-disk-cleaner),最新版 `1.6.10`。
447
455
 
448
456
  ```bash
449
457
  npm i dsh-disk-cleaner
450
458
  ```
451
459
 
452
- [v1.6.9 Release](https://github.com/stallenlu/dsh-disk-cleaner/releases/tag/v1.6.9) 还附带一个预构建 tarball,从它安装可跳过 DSH 的构建授权步骤:
460
+ [v1.6.10 Release](https://github.com/stallenlu/dsh-disk-cleaner/releases/tag/v1.6.10) 还附带一个预构建 tarball,从它安装可跳过 DSH 的构建授权步骤:
453
461
 
454
462
  ```bash
455
- dsh plugin --profile <profile> add https://github.com/stallenlu/dsh-disk-cleaner/releases/download/v1.6.9/dsh-disk-cleaner-1.6.9.tgz
463
+ dsh plugin --profile <profile> add https://github.com/stallenlu/dsh-disk-cleaner/releases/download/v1.6.10/dsh-disk-cleaner-1.6.10.tgz
456
464
  ```
457
465
 
458
466
  然后在 profile 里启用 —— 在 `$DSH_PROFILE_DIR/package.json`:
@@ -460,7 +468,7 @@ dsh plugin --profile <profile> add https://github.com/stallenlu/dsh-disk-cleaner
460
468
  ```json
461
469
  {
462
470
  "dependencies": {
463
- "dsh-disk-cleaner": "^1.6.9"
471
+ "dsh-disk-cleaner": "^1.6.10"
464
472
  },
465
473
  "dsh": {
466
474
  "profile": {
@@ -481,7 +489,7 @@ bundle 自带的 `cordis.patch.yml` 会插入插件行,无需改 profile 的 p
481
489
  ### 开发
482
490
 
483
491
  ```bash
484
- node smoke.mjs # 114 条断言,零依赖
492
+ node smoke.mjs # 116 条断言,零依赖
485
493
  node --check lib/client.js # 以及 lib/index.js、lib/catalog.js、lib/audit.js、lib/plan.js
486
494
  ```
487
495
 
@@ -537,7 +545,7 @@ node --check lib/client.js # 以及 lib/index.js、lib/catalog.js、li
537
545
  ├─ package.json dsh.bundle.patch + dsh.client { platform: "web" }
538
546
  ├─ cordis.patch.yml - insert: id: dsh-disk-cleaner
539
547
  ├─ lib/index.js (525) Host:HTTP 路由 + disk_cleaner 工具,共用同一个 run(),含 reveal
540
- ├─ lib/catalog.js (1242) 32 个目标,measurePath / purgePath / resolveTargets / drives
548
+ ├─ lib/catalog.js (1252) 32 个目标,measurePath / purgePath / resolveTargets / drives
541
549
  ├─ lib/audit.js (365) 只读评估,10 分钟缓存,带 areaPaths,不暴露任何删除
542
550
  ├─ lib/plan.js (150) 纯函数:测量结果 → 有序步骤
543
551
  ├─ lib/history.js (71) 只追加的运行日志,落在 $DSH_HOME/dsh-disk-cleaner;永不抛错
@@ -546,13 +554,19 @@ node --check lib/client.js # 以及 lib/index.js、lib/catalog.js、li
546
554
  ├─ icon.svg 插件图标
547
555
  ├─ README.md 本文件
548
556
  ├─ LICENSE MIT
549
- └─ smoke.mjs 114 条断言,零依赖
557
+ └─ smoke.mjs 116 条断言,零依赖
550
558
  ```
551
559
 
552
560
  Client 半面注册的 slot:`sidebar.panellist`(order 50,仅设置页模式下拆除)、`main`(以包名为 key)、`settings.section`(order 30,仅侧边栏模式下拆除)、`plugins.bundle.config`(以包名为 key)。
553
561
 
554
562
  ### 更新记录
555
563
 
564
+ - **1.6.10** — 清理不再把宿主自己重启一遍。`app-caches` 目标里列了宿主自己的 Chromium 配置目录
565
+ (`%APPDATA%\@deepseek-ai\dsh-desktop`),于是清它等于清掉正在绘制这个面板的进程的缓存:
566
+ Chromium 立刻重建这些目录,渲染进程随之重载,看上去就像插件热重启了一次、而不是回到自己的界面。
567
+ 现在这 4 个目录已从目标中移除,并且 `resolveTargets()` 会直接拒绝宿主数据目录下的任何路径,
568
+ 以后改目标定义也加不回来。`app-caches` 里其余部分(WPS、FTNN、Qoder、Trae、微信开发者工具、appGallery)不变。
569
+ 两条回归断言覆盖这个修复的两半。
556
570
  - **1.6.9** — 深色面板里最后一块白框没了。确认框原来是 `window.confirm`,它由浏览器画在文档之外:读不到 `color-scheme`、读不到基线令牌、也不跟宿主主题,于是深色面板中间出现一个浅色矩形。现在改成面板自己画的弹层:信息一条不少(几项、多少字节、每个目标的名称与大小、哪些不可恢复),用 `--dsb-*` 上色,Escape、点遮罩、点取消都能退出,危险按钮刻意不自动聚焦,免得顺手一个回车就删了。计划步骤的确认走同一个组件。除绘制方式外没有行为变化。
557
571
  - **1.6.8** — 补上 1.6.7 的另一半:桌面窗口如果发的是 `Origin: http://localhost:<端口>`、而请求实际到达 `127.0.0.1`,1.6.7 仍然会报「origin rejected」——因为它逐字比较这两个字符串。现在判定改为看**连接的来源地址**(远端网页无法影响这个值),再看 `Origin`:桌面自有协议、压根没有 `Origin`、或任何 loopback 主机名都放行,其余照旧拒绝;即便是 loopback 连接,`https://evil.example` 也进不来。空间评估的三条错误路径(评估、打开位置、跑计划步骤)统一走面板已有的「稳定 code → 中文文案」通道,不会再直接显示原始英文串。
558
572
  - **1.6.7** — 修掉 1.6.6 带进来的一个回归:把「没有 Origin 头的请求」一律拒绝,连插件自己的面板都进不了接口(界面报「请求来源不被接受」)——桌面窗口发请求时 Origin 是 `dsh-app://app`,不是 `http://127.0.0.1:<端口>`。现在的判定接受桌面协议,也接受被桌面转发层剥掉的头,但前提是请求本身已经到达 loopback 地址;普通网页不发 Origin 就到不了 loopback 端口,所以门仍然是关的。其余行为没变。
package/lib/catalog.js CHANGED
@@ -172,7 +172,6 @@ const appCaches = () => {
172
172
  ...subdirs(path.join(roaming, 'com.qoder.app.stable'), ['Cache', 'Code Cache']),
173
173
  ...subdirs(path.join(roaming, 'Trae CN'), ['Cache', 'CachedData']),
174
174
  ...subdirs(path.join(roaming, 'Trae'), ['Cache', 'CachedData']),
175
- ...subdirs(path.join(roaming, '@deepseek-ai', 'dsh-desktop'), ['Cache', 'Code Cache', 'GPUCache', 'GrShaderCache']),
176
175
  ...subdirs(path.join(roaming, 'PersonalCenter'), ['Cache']),
177
176
  ...subdirs(path.join(roaming, '微信开发者工具'), ['Cache']),
178
177
  ...subdirs(path.join(roaming, '腾讯电脑管家-全局搜'), ['Cache']),
@@ -911,6 +910,12 @@ const tooBroad = (abs) => {
911
910
  return parts.length < 3 || /^[A-Za-z]:$/.test(path.resolve(abs));
912
911
  };
913
912
 
913
+ /** Folders this plugin must never delete from: the host's own Chromium profile. */
914
+ const hostOwned = () => {
915
+ const roaming = env('APPDATA') ?? '';
916
+ return roaming === '' ? [] : [path.join(roaming, '@deepseek-ai', 'dsh-desktop')];
917
+ };
918
+
914
919
  const realOrSelf = (p) => {
915
920
  try {
916
921
  return fs.realpathSync.native(p);
@@ -963,11 +968,16 @@ function statReal(p) {
963
968
  /** Resolve catalog ids to the concrete directories that exist right now, optionally on one volume. */
964
969
  export function resolveTargets(only, drive) {
965
970
  const selected = only === undefined ? CATALOG : CATALOG.filter((entry) => only.includes(entry.id));
971
+ const owned = hostOwned();
966
972
  return selected.map((entry) => {
967
973
  const found = [];
968
974
  for (const candidate of entry.paths()) {
969
975
  if (typeof candidate !== 'string' || candidate === '') continue;
970
976
  if (tooBroad(candidate)) continue;
977
+ // The harness running this plugin keeps its renderer caches under its own data folder.
978
+ // Emptying them makes the live page rebuild and reload itself mid-clean; never allow it,
979
+ // even if a target definition added the path back later.
980
+ if (owned.some((root) => path.resolve(candidate) === path.resolve(root) || within(candidate, root))) continue;
971
981
  if (!isDir(candidate) && !isFile(candidate)) continue;
972
982
  if (!onDrive(candidate, drive)) continue;
973
983
  // A target that names no root is still bounded by the volume it sits on. An empty list
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-disk-cleaner",
3
- "version": "1.6.9",
3
+ "version": "1.6.10",
4
4
  "type": "module",
5
5
  "description": "Disk Cleaner for DSH: scan known-safe cache locations, see what they occupy, and reclaim the space from a panel or from the agent.",
6
6
  "keywords": [
package/smoke.mjs CHANGED
@@ -416,7 +416,22 @@ check(
416
416
  source.includes('QQ') ,
417
417
  'it only ever measures the QQGuild folder',
418
418
  );
419
- check('the version badge and package agree', pkg.version === '1.6.9');
419
+ check('the version badge and package agree', pkg.version === '1.6.10');
420
+
421
+ check(
422
+ 'the harness keeps its own renderer caches out of every target',
423
+ !/subdirs\(path\.join\(roaming, '@deepseek-ai', 'dsh-desktop'\)/.test(catalog) &&
424
+ /const hostOwned = \(\) => \{/.test(catalog) &&
425
+ /if \(owned\.some\(\(root\) =>/.test(catalog),
426
+ 'emptying them makes the live page rebuild and reload itself mid-clean',
427
+ );
428
+
429
+ check(
430
+ 'a path inside the running host is refused even if a target asks for it',
431
+ /const owned = hostOwned\(\);/.test(catalog) &&
432
+ /within\(candidate, root\)\)\) continue;/.test(catalog),
433
+ 'the exclusion belongs in resolveTargets so one definition cannot reintroduce it',
434
+ );
420
435
 
421
436
  check(
422
437
  'a dry run is one click away and never sends confirm',