ygc-dsh-plugin-autoupdate 0.9.4 → 0.9.5
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 +90 -42
- package/client.js +19 -13
- package/index.js +26 -2
- package/lib/api.js +32 -1
- package/lib/core.js +28 -0
- package/lib/share.js +6 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -197,12 +197,16 @@ Electron 的 main / gpu-process / renderer / utility,以及以 `ELECTRON_RUN_A
|
|
|
197
197
|
|
|
198
198
|
`profile/package.json` 里 `dsh.profile.bundles` 列出的就是插件全集,再按 `dependencies` 的来源分三类:
|
|
199
199
|
|
|
200
|
-
| 来源写法 | 类别 | 是否参与更新 |
|
|
201
|
-
|
|
202
|
-
| `1.2.3` / `^1.2.3` | `registry` | 是 |
|
|
203
|
-
| `file:` / `link:` / `workspace:` | `local` | 否(没有「发布」概念) |
|
|
204
|
-
| `git+` / `github:`
|
|
205
|
-
|
|
|
200
|
+
| 来源写法 | 类别 | 是否参与更新 | 面板 |
|
|
201
|
+
|---|---|---|---|
|
|
202
|
+
| `1.2.3` / `^1.2.3` | `registry` | 是 | 显示 |
|
|
203
|
+
| `file:` / `link:` / `workspace:` | `local` | 否(没有「发布」概念) | 显示 |
|
|
204
|
+
| `git+` / `github:` | `git` | 是(比 commit) | 显示 |
|
|
205
|
+
| `http(s)://…tgz` 直链 | `local` | 否(没有版本号可比) | 显示 |
|
|
206
|
+
| 不在 `dependencies` 里(随 DSH 安装提供) | `installation` | 否(版本跟随宿主) | **不显示** |
|
|
207
|
+
| 包名在 `@deepseek-ai/` 下 | — | 否 | **不显示** |
|
|
208
|
+
|
|
209
|
+
> 后两行是 0.9.5 的行为:DSH 本体插件整体隐藏,判据见下面「DSH 本体插件」一节。
|
|
206
210
|
|
|
207
211
|
## HTTP API
|
|
208
212
|
|
|
@@ -406,8 +410,8 @@ $node = "C:\DeepSeekHarness\resources\runtime\primary-runtime\dependencies\node\
|
|
|
406
410
|
| `machine.test.mjs` | 本机标识:继承会话同步的 machineId、固定后不漂移、改名不换特征值、卸载会话同步后旧值仍有效、写边界(别人的格 / `../` 逃逸 / 前缀相近) |
|
|
407
411
|
| `share.test.mjs` | 特征文件夹:清单分类(registry 只写名 / 本地打副本 / 宿主自带不写)、tgz 结构、同步幂等与副本复用、他人清单合并去重、安装路径选择、特征名防上跳 |
|
|
408
412
|
| `schedule.test.mjs` | 定时排期:更新与共享同步各自独立开关 |
|
|
409
|
-
| `system-plugins.test.mjs` |
|
|
410
|
-
| `system-plugins.behavior.test.mjs` |
|
|
413
|
+
| `system-plugins.test.mjs` | **本体插件隐藏契约**(源码级):`isBuiltinPlugin` 判据只有一处定义、listPlugins 出口过滤并缓存隐藏名单、runCheck 不查、snapshot 再兜一层并透出 `builtin.count`、策略/更新接口对本体插件一律 400、共享清单同一判据、面板整组隐藏只留一行说明 |
|
|
414
|
+
| `system-plugins.behavior.test.mjs` | **本体插件隐藏行为**(真执行 + 假宿主 + 假 profile):`@deepseek-ai/*` 一行都不出现、第三方插件照常可写策略、`builtin.count` 数对、`pluginManager` 缺失 / 抛错 / 脏数据三种降级都不让 `/state` 失败 |
|
|
411
415
|
| `restart-safety.test.mjs` | **重启安全契约**(源码级):不得按映像名杀、必须排除自身、枚举→批量结束→拉起的顺序固定、助手须由 WMI 送出进程树、直接 `/F` 不做 WM_CLOSE、有冷却与锁、助手解释器须优先用自带 node.exe |
|
|
412
416
|
| `restart-interpreter.test.mjs` | **助手解释器与安全闸门**:`canEnumerateByPath()` 拒绝 node.exe/node、payload 带闸门结果与显式 pid、固定等待 ≤ 1 秒、不再每轮重新枚举、批量 taskkill |
|
|
413
417
|
| `pidtree.test.mjs` | **进程树解析**(合成进程表):Host 爬到 main、renderer 爬到同一个 main、起点缺失/父进程缺失/链过深时的边界、只在 Electron 里才爬 |
|
|
@@ -428,56 +432,100 @@ $env:ELECTRON_RUN_AS_NODE = '1'
|
|
|
428
432
|
& "C:\DeepSeekHarness\DeepSeek Harness.exe" "C:\DSH-Work\plugin-autoupdate\pkg\test\_live-pidtree.mjs"
|
|
429
433
|
```
|
|
430
434
|
|
|
431
|
-
##
|
|
435
|
+
## DSH 本体插件:不显示、不管理(0.9.5)
|
|
436
|
+
|
|
437
|
+
`@deepseek-ai/*` 这一批(`dsh-base`、`dsh-web-app`、`dsh-experimental-*`、`dsh-subagent-*` 等)
|
|
438
|
+
是**随桌面端一起发布的官方 bundle**。它们不在 `profile/node_modules` 里,而在安装目录
|
|
439
|
+
(`resources/app/dsh/node_modules`),版本跟着桌面端走,装/升/卸都由桌面端负责。
|
|
440
|
+
|
|
441
|
+
**0.9.5 起它们彻底不进这个面板**:不显示、不检查、不更新、不卸载。
|
|
432
442
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
443
|
+
> 为什么改:本机实测 40 行里 24 行是本体插件(可见行 16),真正的第三方插件被淹掉;
|
|
444
|
+
> 而这些行的策略下拉、「立即更新」、「刷新」本来就是假的(点了必然报错或无意义),
|
|
445
|
+
> 列出来只增加误点。用户要的是「我装的那些插件」,不是「宿主里所有的 bundle」。
|
|
436
446
|
|
|
437
|
-
|
|
447
|
+
### 判据只有一处:`isBuiltinPlugin()`
|
|
448
|
+
|
|
449
|
+
`lib/core.js` 导出 `BUILTIN_SCOPE = '@deepseek-ai/'` 与 `isBuiltinPlugin(plugin)`,命中任意一条即算本体:
|
|
450
|
+
|
|
451
|
+
| 判据 | 说明 |
|
|
438
452
|
|---|---|
|
|
439
|
-
|
|
|
440
|
-
|
|
|
441
|
-
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
453
|
+
| 包名以 `@deepseek-ai/` 开头 | 官方 scope |
|
|
454
|
+
| `availability === 'installation'` | 宿主说「随 DSH 安装提供」 |
|
|
455
|
+
| `kind === 'installation'` | 不在 profile `dependencies` 里,没有可查的版本约束 |
|
|
456
|
+
|
|
457
|
+
面板、检查、共享、卸载四处引用同一个函数 —— 只有一处定义,才不会出现
|
|
458
|
+
「面板藏了、共享清单里还在」这种自相矛盾。
|
|
459
|
+
|
|
460
|
+
### 四处生效点
|
|
461
|
+
|
|
462
|
+
| 位置 | 行为 |
|
|
463
|
+
|---|---|
|
|
464
|
+
| `listPlugins()`(`index.js`) | 出口过滤;**被剔除的名字记进 `builtinNamesCache`** |
|
|
465
|
+
| `runCheck()`(`index.js`) | 双重排除 `availability !== 'installation'` 且 `!isBuiltinPlugin`,不发网络请求 |
|
|
466
|
+
| `snapshot()`(`lib/api.js`) | 再兜一层(`deps.listPlugins` 可注入,未必带过滤);透出 `builtin.count` / `builtin.names` |
|
|
467
|
+
| `shareablePlugins()`(`lib/share.js`) | 不进他人的共享清单 |
|
|
468
|
+
| `client.js` | 按 scope / availability / kind 再筛一次,整组不渲染 |
|
|
469
|
+
|
|
470
|
+
### 面板上还能看到什么
|
|
471
|
+
|
|
472
|
+
只有一行说明,不给任何控件:
|
|
473
|
+
|
|
474
|
+
```
|
|
475
|
+
已隐藏 24 个 DSH 本体插件(@deepseek-ai/ 下的官方包)· 随桌面端更新,不由本插件检查、更新或卸载
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
条数取 `snapshot.builtin.count`,它由两个来源并集去重得到:
|
|
479
|
+
`listPlugins()` 记录的名字 + `snapshot()` 自己筛掉的条数。
|
|
480
|
+
注入的 `listPlugins` 不带过滤时也不会报 0(否则面板会说「什么都没隐藏」)。
|
|
481
|
+
|
|
482
|
+
### 写接口对本体插件一律拒绝
|
|
483
|
+
|
|
484
|
+
即使面板不显示,直接打接口也不能得手 —— 改它们的策略或版本只会让宿主状态不一致:
|
|
485
|
+
|
|
486
|
+
```json
|
|
487
|
+
POST /api/local/plugin-autoupdate/plugin-policy {"name":"@deepseek-ai/dsh-base","policy":"immediate"}
|
|
488
|
+
→ 400 {"ok":false,"error":"@deepseek-ai/dsh-base 是 DSH 本体插件,不由本插件管理"}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
`POST /update` 同形。
|
|
492
|
+
|
|
493
|
+
### 历史(别把这两处修复改回去)
|
|
494
|
+
|
|
495
|
+
> **0.9.3** 修过「Dsh 系统插件不在自动更新插件里显示」:它们其实一直在列表里,
|
|
496
|
+
> 只是 `version` 读成了 `null`(只查 profile 的 `node_modules`),渲染成「未装」。
|
|
497
|
+
> 修法是 `listPlugins(host)` 把宿主 `pluginManager.listBundles()` 当第二来源合并,
|
|
498
|
+
> 版本回落顺序「profile 里实际装的 → 宿主报的 → null」,名字集取并集。
|
|
499
|
+
> **这段逻辑仍然在**(`system-plugins.test.mjs` 有断言),只是筛出来的行现在被隐藏了。
|
|
448
500
|
>
|
|
449
|
-
>
|
|
450
|
-
>
|
|
451
|
-
>
|
|
501
|
+
> **0.9.4** 修过两处归类错误:`availability=missing` 的官方包被划进「已装」组,
|
|
502
|
+
> 显示成「随安装 + 未装」的矛盾行;以及 `snapshot()` 读了一个从未赋值的
|
|
503
|
+
> `_removableCache`,导致所有插件都不可卸。
|
|
504
|
+
>
|
|
505
|
+
> **0.9.5** 按用户要求改为整体隐藏。`missing` 但**非官方 scope** 的第三方缺失插件
|
|
506
|
+
> 仍然单列「可选、本机未安装(N)」——那是另一回事,用户可能真想装。
|
|
452
507
|
|
|
453
508
|
宿主清单按 **10 秒 TTL** 缓存在 `hostBundlesCache`,由 `GET /state` 等入口惰性刷新。
|
|
454
509
|
不在 `apply` 阶段拉:那时别的插件可能还没注册完,`pluginManager` 未必在。
|
|
455
510
|
`pluginManager` 缺失、`listBundles()` 抛错、或返回脏数据(`null` / 数字 name)都只降级,
|
|
456
511
|
不会让 `/state` 失败 —— 这条由 `system-plugins.behavior.test.mjs` 逐个场景验证。
|
|
457
512
|
|
|
458
|
-
###
|
|
513
|
+
### 五种来源,各自怎么显示
|
|
459
514
|
|
|
460
515
|
宿主清单的 `availability` 决定分组,`spec` 决定来源类型。两者**都**要看,
|
|
461
516
|
只按其中一个分就会混组:
|
|
462
517
|
|
|
463
518
|
| 来源 | 判据 | 面板位置 | 状态文案 | 能自动更新 | 能卸载 |
|
|
464
519
|
|---|---|---|---|---|---|
|
|
465
|
-
|
|
|
466
|
-
|
|
|
520
|
+
| **DSH 本体** | 官方 scope / `availability=installation` / `kind=installation` | **不显示**(仅一行「已隐藏 N 个」) | — | 否 | 否 |
|
|
521
|
+
| 第三方但宿主知道、机器上没装 | `availability=missing` 且非官方 scope | 「可选、本机未安装(N)」 | **未安装** | 否 | 否(无需) |
|
|
467
522
|
| npm registry | `kind=registry` | 「已装插件(N)」 | 可更新 / 已是最新 | 是 | 宿主说 removable 才行 |
|
|
468
523
|
| git 来源 | `kind=git` | 同上 | 比 commit | 是 | 同上 |
|
|
469
|
-
|
|
|
470
|
-
|
|
471
|
-
>
|
|
472
|
-
>
|
|
473
|
-
>
|
|
474
|
-
> `managed = availability !== 'installation'` 把它们划进了「已装」组,
|
|
475
|
-
> 同时 `kind` 又被算成 `installation`,于是显示成「随安装 + 未装」的矛盾行。
|
|
476
|
-
> 现在 `missing` 单列一组,状态写「未安装」,并且 `installed: false` 一并透出。
|
|
477
|
-
>
|
|
478
|
-
> 顺带把 `dsh-purge-community` 这类**直链 release tar** 标成「直链·手动更新」:
|
|
479
|
-
> 它的 `spec` 是 `https://github.com/.../releases/latest/download/xxx.tgz`,
|
|
480
|
-
> 没有版本号可比、也没有 ref 可追,说「已是最新」是假的。
|
|
524
|
+
| 直链 tar 包 | `kind=local` + `spec` 是 `https://` | 「本地路径 / 直链来源」 | **直链·手动更新** | 否 | 同上 |
|
|
525
|
+
|
|
526
|
+
> **直链 release tar 为什么单独说**:`spec` 是
|
|
527
|
+
> `https://github.com/.../releases/latest/download/xxx.tgz` 这类,没有版本号可比、
|
|
528
|
+
> 也没有 ref 可追,说「已是最新」是假的。
|
|
481
529
|
|
|
482
530
|
### 可卸载性:不要再自己算一遍
|
|
483
531
|
|
|
@@ -497,8 +545,8 @@ $env:ELECTRON_RUN_AS_NODE = '1'
|
|
|
497
545
|
|
|
498
546
|
- 只**更新**已装插件,不安装新插件;**卸载仅限 profile 自装且宿主判定 removable 的包**(见「卸载插件」)
|
|
499
547
|
- 只在 `registry` 来源上工作;`local` / `git` / `installation` 来源只显示、不处理
|
|
500
|
-
-
|
|
501
|
-
版本跟桌面端走,`registry`
|
|
548
|
+
- **DSH 本体插件(`@deepseek-ai/*` / `availability=installation` / `kind=installation`)不显示、不检查、不更新、不卸载**:
|
|
549
|
+
版本跟桌面端走,`registry` 上的同名包与它无关;策略/更新接口对它们一律 400
|
|
502
550
|
- 更新失败只记历史,不自动回滚(避免把用户自己手改的清单吞掉);要回滚用 `/restore`
|
|
503
551
|
- DSH 关闭期间不检查(进程内定时器的固有限制);重开后会立刻按上游发布时间重新判定
|
|
504
552
|
- 共享只**同步清单与副本**,不自动安装别人的插件;安装必须人工点选
|
package/client.js
CHANGED
|
@@ -179,20 +179,27 @@ function makePanel(React) {
|
|
|
179
179
|
);
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
182
|
+
// DSH 本体插件(@deepseek-ai/* 与随安装提供的)不显示、不管理:
|
|
183
|
+
// 版本跟着桌面端走,装/升/卸都由桌面端负责。宿主侧已经把它们剔掉了,
|
|
184
|
+
// 这里再筛一次,防止老宿主或注入的实现把整张名单原样送过来。
|
|
185
|
+
const isBuiltinRow = (p) =>
|
|
186
|
+
Boolean(p) &&
|
|
187
|
+
(p.availability === 'installation' ||
|
|
188
|
+
p.kind === 'installation' ||
|
|
189
|
+
(typeof p.name === 'string' && p.name.startsWith('@deepseek-ai/')));
|
|
190
|
+
const allRows = Array.isArray(snap.plugins) ? snap.plugins : [];
|
|
191
|
+
const plugins = allRows.filter((p) => !isBuiltinRow(p));
|
|
192
|
+
// 「宿主清单里有、但本机没装」仍然单列:它不是本体插件,用户可能真想装。
|
|
188
193
|
const missingPlugins = plugins.filter((p) => p.availability === 'missing');
|
|
189
|
-
const managed = plugins.filter((p) => p.availability !== '
|
|
194
|
+
const managed = plugins.filter((p) => p.availability !== 'missing');
|
|
190
195
|
const registryPlugins = managed.filter((p) => p.kind === 'registry');
|
|
191
196
|
// git 与 registry 一样参与自动更新,只是比的是 commit 而不是版本号
|
|
192
197
|
const gitPlugins = managed.filter((p) => p.kind === 'git');
|
|
193
198
|
const otherPlugins = managed.filter((p) => p.kind !== 'registry' && p.kind !== 'git');
|
|
194
199
|
const available = plugins.filter((p) => p.updateAvailable);
|
|
195
200
|
const ready = available.filter((p) => !p.waitMs);
|
|
201
|
+
// 被隐藏的本体插件数量:宿主报了就用宿主的,没报就按本地筛掉的差值兜底。
|
|
202
|
+
const builtinHidden = (snap.builtin && Number(snap.builtin.count)) || allRows.length - plugins.length;
|
|
196
203
|
|
|
197
204
|
const header = h(
|
|
198
205
|
'div',
|
|
@@ -334,7 +341,7 @@ function makePanel(React) {
|
|
|
334
341
|
const pluginRow = (plugin) => {
|
|
335
342
|
const isGit = plugin.kind === 'git';
|
|
336
343
|
// 三种特殊行,各自的状态文案必须诚实:
|
|
337
|
-
// system —— 随 DSH
|
|
344
|
+
// system —— 随 DSH 安装(本体插件已整组隐藏,这里只作兜底)
|
|
338
345
|
// missing —— 宿主知道但没装,不是「已是最新」
|
|
339
346
|
// link —— 直链 tar 包(如 GitHub release),没有版本号可比
|
|
340
347
|
const isSystem = plugin.availability === 'installation';
|
|
@@ -572,18 +579,17 @@ function makePanel(React) {
|
|
|
572
579
|
),
|
|
573
580
|
)
|
|
574
581
|
: null,
|
|
575
|
-
//
|
|
576
|
-
//
|
|
577
|
-
|
|
582
|
+
// DSH 本体插件整组隐藏,只留一行说明:用户问过「我明明装了怎么这里没有」,
|
|
583
|
+
// 所以要能看出「有几个被隐了、为什么隐」,但不占用列表、不给任何控件。
|
|
584
|
+
builtinHidden > 0
|
|
578
585
|
? h(
|
|
579
586
|
'div',
|
|
580
587
|
{ style: { marginTop: 12 } },
|
|
581
588
|
h(
|
|
582
589
|
'div',
|
|
583
590
|
{ className: 'pau-row-d' },
|
|
584
|
-
|
|
591
|
+
`已隐藏 ${builtinHidden} 个 DSH 本体插件(@deepseek-ai/ 下的官方包)· 随桌面端更新,不由本插件检查、更新或卸载`,
|
|
585
592
|
),
|
|
586
|
-
systemPlugins.map(pluginRow),
|
|
587
593
|
)
|
|
588
594
|
: null,
|
|
589
595
|
// 「宿主清单里有、本机没装」:既不是系统插件也不是已装插件。
|
package/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
installedMeta,
|
|
20
20
|
installedVersion,
|
|
21
21
|
installedGitHead,
|
|
22
|
+
isBuiltinPlugin,
|
|
22
23
|
isNewerVersion,
|
|
23
24
|
isUpdatableKind,
|
|
24
25
|
loadState,
|
|
@@ -98,6 +99,13 @@ export function apply(ctx, config = {}) {
|
|
|
98
99
|
* `profile/node_modules` 里没有它们 —— 只有宿主知道它们的版本。
|
|
99
100
|
*/
|
|
100
101
|
let hostBundlesCache = Object.freeze({ bundles: [], at: 0, error: '' });
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 被隐藏的 DSH 本体插件名单(同步缓存,给面板显示「已隐藏 N 个」用)。
|
|
105
|
+
* listPlugins() 每次调用都会刷新它。
|
|
106
|
+
*/
|
|
107
|
+
let builtinNamesCache = [];
|
|
108
|
+
const builtinNames = () => builtinNamesCache;
|
|
101
109
|
const HOST_BUNDLES_TTL = 10 * 1000;
|
|
102
110
|
|
|
103
111
|
const readHostManager = () => {
|
|
@@ -183,7 +191,7 @@ export function apply(ctx, config = {}) {
|
|
|
183
191
|
// 宿主里有、profile bundles 里没有的(例如其他 app 入口),也补进来:
|
|
184
192
|
// 用户要的是「我装了什么」,不是「profile 声明了什么」。
|
|
185
193
|
const allNames = [...new Set([...names, ...hostMap.keys()])];
|
|
186
|
-
|
|
194
|
+
const rows = allNames
|
|
187
195
|
.map((name) => {
|
|
188
196
|
const meta = installedMeta(name);
|
|
189
197
|
const spec = typeof specs[name] === 'string' ? specs[name] : null;
|
|
@@ -214,6 +222,18 @@ export function apply(ctx, config = {}) {
|
|
|
214
222
|
homepage: meta.homepage || meta.repository || '',
|
|
215
223
|
};
|
|
216
224
|
});
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* DSH 本体插件(@deepseek-ai/* 与随安装提供的)从这里就剔掉:
|
|
228
|
+
* 不显示、不检查、不更新、不卸载——版本跟着桌面端走,动它们等于拆宿主。
|
|
229
|
+
*
|
|
230
|
+
* 但被隐藏的名字要留一份给面板,否则用户会问「我明明装了怎么这里没有」。
|
|
231
|
+
*/
|
|
232
|
+
builtinNamesCache = rows
|
|
233
|
+
.filter((row) => isBuiltinPlugin(row))
|
|
234
|
+
.map((row) => row.name)
|
|
235
|
+
.sort();
|
|
236
|
+
return rows.filter((row) => !isBuiltinPlugin(row));
|
|
217
237
|
};
|
|
218
238
|
|
|
219
239
|
const registries = () => {
|
|
@@ -379,7 +399,9 @@ export function apply(ctx, config = {}) {
|
|
|
379
399
|
// 随 DSH 安装提供的(availability=installation)不查:它们的版本跟着桌面端走,
|
|
380
400
|
// 查 registry 只会得到无关结论,还白白打网络。
|
|
381
401
|
const plugins = listPlugins(hostBundles()).filter(
|
|
382
|
-
|
|
402
|
+
// 本体插件已在 listPlugins 里剔掉,这里再挡一次 isBuiltinPlugin:
|
|
403
|
+
// 版本跟着桌面端走,查 registry 只会得到无关结论,还白白打网络。
|
|
404
|
+
(p) => isUpdatableKind(p.kind) && p.version && p.availability !== 'installation' && !isBuiltinPlugin(p),
|
|
383
405
|
);
|
|
384
406
|
const { registry, fallbackRegistries } = registries();
|
|
385
407
|
|
|
@@ -846,6 +868,8 @@ export function apply(ctx, config = {}) {
|
|
|
846
868
|
ensureHostBundles: () => refreshHostBundles(false),
|
|
847
869
|
// 同步读取当前缓存(快照是同步函数)
|
|
848
870
|
hostBundles,
|
|
871
|
+
// 被隐藏的本体插件名单:面板据此说明「为什么我这里只看到 N 个」
|
|
872
|
+
builtinNames,
|
|
849
873
|
runCheck: checkAndRecord,
|
|
850
874
|
applyUpdate,
|
|
851
875
|
migrateToNpm,
|
package/lib/api.js
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
saveState,
|
|
19
19
|
updateKind,
|
|
20
20
|
isUpdatableKind,
|
|
21
|
+
isBuiltinPlugin,
|
|
21
22
|
} from './core.js';
|
|
22
23
|
import { normalizeRegistry, queryLatest } from './registry.js';
|
|
23
24
|
import { restoreBackup, listBackups } from './backup.js';
|
|
@@ -52,7 +53,12 @@ function snapshot(deps) {
|
|
|
52
53
|
// 同步预取一次(快照本来就是 async 调用方在用),拿不到就整体置灰。
|
|
53
54
|
const removable = deps._removableCache || null;
|
|
54
55
|
const host = typeof deps.hostBundles === 'function' ? deps.hostBundles() : undefined;
|
|
55
|
-
|
|
56
|
+
// DSH 本体插件(@deepseek-ai/* 与随安装提供的)不显示、不管理:
|
|
57
|
+
// listPlugins 已经剔过一遍,这里再挡一层——deps.listPlugins 是可注入的,
|
|
58
|
+
// 注入方(测试、其他宿主)未必带过滤,漏一层就会在面板上冒出来。
|
|
59
|
+
const listed = deps.listPlugins(host);
|
|
60
|
+
const rawPlugins = listed.filter((plugin) => !isBuiltinPlugin(plugin));
|
|
61
|
+
const plugins = rawPlugins.map((plugin) => {
|
|
56
62
|
const policy = policyFor(state, plugin.name);
|
|
57
63
|
const pending = state.pending[plugin.name];
|
|
58
64
|
const seen = cached[plugin.name];
|
|
@@ -127,6 +133,21 @@ function snapshot(deps) {
|
|
|
127
133
|
busy: deps.isBusy(),
|
|
128
134
|
pendingRestart: needsRestart,
|
|
129
135
|
plugins,
|
|
136
|
+
// 被隐藏的 DSH 本体插件:面板只显示条数 + 可展开的名字,
|
|
137
|
+
// 既回答「我明明装了怎么这里没有」,又不把它们混进可管理列表。
|
|
138
|
+
//
|
|
139
|
+
// 条数取两个来源的并集:
|
|
140
|
+
// - deps.builtinNames():listPlugins 自己剔除时记下的名字(正常路径);
|
|
141
|
+
// - listed.length - rawPlugins.length:注入的 listPlugins 没剔除时,
|
|
142
|
+
// 在本函数里被筛掉的条数(也不能报 0,否则面板说「什么都没隐藏」)。
|
|
143
|
+
builtin: (() => {
|
|
144
|
+
const recorded = typeof deps.builtinNames === 'function' ? deps.builtinNames() : [];
|
|
145
|
+
const names = [...new Set([
|
|
146
|
+
...(Array.isArray(recorded) ? recorded : []),
|
|
147
|
+
...listed.filter((plugin) => isBuiltinPlugin(plugin)).map((plugin) => plugin.name),
|
|
148
|
+
].filter((n) => typeof n === 'string' && n))].sort();
|
|
149
|
+
return { hidden: true, count: names.length, names: names.slice(0, 200) };
|
|
150
|
+
})(),
|
|
130
151
|
history: (state.history || []).slice(0, 40),
|
|
131
152
|
share: {
|
|
132
153
|
enabled: state.shareEnabled,
|
|
@@ -210,6 +231,12 @@ export function createApi(deps) {
|
|
|
210
231
|
json(res, 400, { ok: false, error: '缺少 name' });
|
|
211
232
|
return;
|
|
212
233
|
}
|
|
234
|
+
// 本体插件(@deepseek-ai/* 等)不受理任何写入:面板已不显示它们,
|
|
235
|
+
// 这里挡的是直接打接口的调用方。
|
|
236
|
+
if (isBuiltinPlugin({ name })) {
|
|
237
|
+
json(res, 400, { ok: false, error: `${name} 是 DSH 本体插件,不由本插件管理` });
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
213
240
|
const state = loadState();
|
|
214
241
|
if (body.policy === 'inherit') {
|
|
215
242
|
delete state.perPlugin[name];
|
|
@@ -292,6 +319,10 @@ export function createApi(deps) {
|
|
|
292
319
|
json(res, 400, { ok: false, error: '缺少 name,或 version / commit' });
|
|
293
320
|
return;
|
|
294
321
|
}
|
|
322
|
+
if (isBuiltinPlugin({ name })) {
|
|
323
|
+
json(res, 400, { ok: false, error: `${name} 是 DSH 本体插件,不由本插件管理` });
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
295
326
|
const state = loadState();
|
|
296
327
|
const kind = updateKind(name, { publishedNames: state.publishedNames });
|
|
297
328
|
if (!isUpdatableKind(kind)) {
|
package/lib/core.js
CHANGED
|
@@ -250,6 +250,34 @@ export function isUpdatableKind(kind) {
|
|
|
250
250
|
return kind === 'registry' || kind === 'git';
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
+
/* ------------------------------------------------------- DSH 本体插件 */
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* DSH 本体(随桌面端一起发布的官方 bundle)的识别规则。
|
|
257
|
+
*
|
|
258
|
+
* 为什么需要它:这些包不属于「用户装的第三方插件」。
|
|
259
|
+
* - 版本跟着桌面端走,查 registry 只会得到无关结论;
|
|
260
|
+
* - 装上/升/卸都由桌面端负责,这里动它们只会把宿主拆坏;
|
|
261
|
+
* - 面板把它们列出来只会把真正的第三方插件淹掉(本机实测 40 行里 25 行是本体)。
|
|
262
|
+
*
|
|
263
|
+
* 判据三条,命中任意一条即算本体:
|
|
264
|
+
* 1. 包名在官方 scope 下(`@deepseek-ai/`);
|
|
265
|
+
* 2. 宿主说 availability === 'installation'(随 DSH 安装提供);
|
|
266
|
+
* 3. updateKind() 判成 'installation'(不在 profile dependencies 里,没有可查的版本约束)。
|
|
267
|
+
*
|
|
268
|
+
* @param {{name?: string, availability?: string|null, kind?: string}} plugin
|
|
269
|
+
*/
|
|
270
|
+
export const BUILTIN_SCOPE = '@deepseek-ai/';
|
|
271
|
+
|
|
272
|
+
export function isBuiltinPlugin(plugin) {
|
|
273
|
+
if (!plugin || typeof plugin !== 'object') return false;
|
|
274
|
+
const name = typeof plugin.name === 'string' ? plugin.name : '';
|
|
275
|
+
if (name.startsWith(BUILTIN_SCOPE)) return true;
|
|
276
|
+
if (plugin.availability === 'installation') return true;
|
|
277
|
+
if (plugin.kind === 'installation') return true;
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
|
|
253
281
|
/* ---------------------------------------------------------------- state */
|
|
254
282
|
|
|
255
283
|
/** 默认状态。 */
|
package/lib/share.js
CHANGED
|
@@ -30,6 +30,7 @@ import { spawn } from 'node:child_process';
|
|
|
30
30
|
import {
|
|
31
31
|
installedMeta,
|
|
32
32
|
installedVersion,
|
|
33
|
+
isBuiltinPlugin,
|
|
33
34
|
isNewerVersion,
|
|
34
35
|
profileBundleNames,
|
|
35
36
|
profileDependencySpecs,
|
|
@@ -343,12 +344,13 @@ export function collectLocalPlugins(options) {
|
|
|
343
344
|
/**
|
|
344
345
|
* 共享清单里只要「自己装的第三方插件」。
|
|
345
346
|
*
|
|
346
|
-
* `kind === 'installation'
|
|
347
|
-
*
|
|
348
|
-
*
|
|
347
|
+
* DSH 本体插件(`@deepseek-ai/*` 与随安装提供的 `kind === 'installation'`)
|
|
348
|
+
* 别人机器上本来就有,写进共享清单只是噪声;而且它们没有独立版本号,去了也没法装。
|
|
349
|
+
* 判据统一走 core.js 的 isBuiltinPlugin:面板、检查、共享、卸载四处同一套规则,
|
|
350
|
+
* 不会出现「面板藏了、共享里还在」这种自相矛盾。
|
|
349
351
|
*/
|
|
350
352
|
export function shareablePlugins(plugins) {
|
|
351
|
-
return plugins.filter((p) => p && p.kind !== 'installation');
|
|
353
|
+
return plugins.filter((p) => p && !isBuiltinPlugin(p) && p.kind !== 'installation');
|
|
352
354
|
}
|
|
353
355
|
|
|
354
356
|
/* ------------------------------------------------------------ 打包副本 */
|
package/package.json
CHANGED