dsh-plugin-manager-companion 0.1.0
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/LICENSE +21 -0
- package/README.en.md +144 -0
- package/README.md +142 -0
- package/cordis.patch.yml +9 -0
- package/dist/about.d.ts +77 -0
- package/dist/about.js +179 -0
- package/dist/cli.d.ts +226 -0
- package/dist/cli.js +856 -0
- package/dist/client/AboutPage.d.ts +75 -0
- package/dist/client/ConsolePage.d.ts +79 -0
- package/dist/client/KindsPage.d.ts +21 -0
- package/dist/client/MarketplacePage.d.ts +36 -0
- package/dist/client/OfficialSlots.d.ts +35 -0
- package/dist/client/UpgradeRow.d.ts +108 -0
- package/dist/client/index.d.ts +26 -0
- package/dist/client/locales.d.ts +475 -0
- package/dist/client/pmSelect.d.ts +38 -0
- package/dist/client/shared.d.ts +928 -0
- package/dist/client/upgradeView.d.ts +278 -0
- package/dist/client/wire.d.ts +401 -0
- package/dist/client.js +9194 -0
- package/dist/diagnostics.d.ts +332 -0
- package/dist/diagnostics.js +2631 -0
- package/dist/envManager.d.ts +1047 -0
- package/dist/envManager.js +3214 -0
- package/dist/fix.d.ts +60 -0
- package/dist/fix.js +168 -0
- package/dist/guard.d.ts +133 -0
- package/dist/guard.js +232 -0
- package/dist/index.d.ts +121 -0
- package/dist/index.js +1150 -0
- package/dist/installSession.d.ts +111 -0
- package/dist/installSession.js +150 -0
- package/dist/kinds.d.ts +464 -0
- package/dist/kinds.js +1029 -0
- package/dist/marketView.d.ts +261 -0
- package/dist/marketView.js +406 -0
- package/dist/marketplace.d.ts +248 -0
- package/dist/marketplace.js +500 -0
- package/dist/match.d.ts +67 -0
- package/dist/match.js +203 -0
- package/dist/net.d.ts +108 -0
- package/dist/net.js +163 -0
- package/dist/official.d.ts +145 -0
- package/dist/official.js +205 -0
- package/dist/paths.d.ts +108 -0
- package/dist/paths.js +236 -0
- package/dist/presets.d.ts +299 -0
- package/dist/presets.js +578 -0
- package/dist/qualityGate.d.ts +66 -0
- package/dist/qualityGate.js +247 -0
- package/dist/rank.d.ts +88 -0
- package/dist/rank.js +164 -0
- package/dist/registry.d.ts +295 -0
- package/dist/registry.js +686 -0
- package/dist/rest.d.ts +122 -0
- package/dist/rest.js +219 -0
- package/dist/scan.d.ts +134 -0
- package/dist/scan.js +396 -0
- package/dist/settings.d.ts +447 -0
- package/dist/settings.js +263 -0
- package/dist/tags.d.ts +119 -0
- package/dist/tags.js +166 -0
- package/dist/tools.d.ts +131 -0
- package/dist/tools.js +377 -0
- package/dist/types.d.ts +651 -0
- package/dist/types.js +13 -0
- package/dist/upgrade.d.ts +428 -0
- package/dist/upgrade.js +1100 -0
- package/dist/upgradeView.d.ts +313 -0
- package/dist/upgradeView.js +273 -0
- package/docs/images/readme/01-console-health.png +0 -0
- package/docs/images/readme/02-console-envs.png +0 -0
- package/docs/images/readme/03-marketplace.png +0 -0
- package/docs/images/readme/04-official-plugin-page.png +0 -0
- package/package.json +104 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LX2000WASD
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# dsh-plugin-manager-companion
|
|
2
|
+
|
|
3
|
+
Companion to the official DSH plugin manager: pre-install quality gate, five-layer environment diagnostics, multi-environment management, and a plugin marketplace.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/dsh-plugin-manager-companion)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
This is a rewrite of [dsh-web-plugin-manager](https://github.com/LX2000WASD/dsh-web-plugin-manager) (0.6.3, unmaintained).
|
|
9
|
+
DSH 0.1.6-alpha.2 ships its own plugin management page, which retired the old approach of shadowing that page and writing profile state directly.
|
|
10
|
+
|
|
11
|
+
[中文说明(主文档)](./README.md)
|
|
12
|
+
|
|
13
|
+
## Screenshots
|
|
14
|
+
|
|
15
|
+
| | |
|
|
16
|
+
|---|---|
|
|
17
|
+
| <br>Five diagnostic layers: grouped findings, each with severity and evidence; unchecked layers read "not checked" | <br>Start, stop, copy and restore across profiles |
|
|
18
|
+
| <br>Cards show risk, category and topics; the detail view shows the index text as published | <br>Registered into the official plugin page: opening your own entry shows the configuration form |
|
|
19
|
+
|
|
20
|
+
## Contents
|
|
21
|
+
|
|
22
|
+
- [What it does, and what it does not](#what-it-does-and-what-it-does-not)
|
|
23
|
+
- [Install](#install)
|
|
24
|
+
- [Capabilities](#capabilities)
|
|
25
|
+
- [Command line](#command-line)
|
|
26
|
+
- [Platform support](#platform-support)
|
|
27
|
+
- [Known limitations](#known-limitations)
|
|
28
|
+
- [Contributing](#contributing)
|
|
29
|
+
- [License](#license)
|
|
30
|
+
|
|
31
|
+
## What it does, and what it does not
|
|
32
|
+
|
|
33
|
+
The official plugin manager's README states what it does not do. This plugin fills only those gaps.
|
|
34
|
+
|
|
35
|
+
| Capability | Official | This plugin |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| Enabling and disabling bundles and plugin rows | Present | Not done |
|
|
38
|
+
| Installing and removing bundles | Present | Not done (quality gate runs first) |
|
|
39
|
+
| Hosting plugin configuration pages | Slots provided | Registers into them |
|
|
40
|
+
| Static checks before install | Absent | Quality gate |
|
|
41
|
+
| Environment diagnostics | Absent | Environment console · Health check |
|
|
42
|
+
| Modifying another profile | Explicitly out of scope | Environment console · Environments |
|
|
43
|
+
| Loading plain plugin modules | Declared a file operation | Skills and presets install |
|
|
44
|
+
| Version listing and update checks | Explicitly out of scope | Plugin marketplace |
|
|
45
|
+
|
|
46
|
+
All writes go through official channels: the official `pluginManager` service for the current environment, and the official `runPluginCommand` across environments.
|
|
47
|
+
This plugin never writes `cordis.patch.yml` and never invokes pnpm directly.
|
|
48
|
+
|
|
49
|
+
Verbatim quotes for the four "explicitly out of scope" items, the feature each maps to, and how this plugin steps back if the official side adds them, are in
|
|
50
|
+
[docs/OFFICIAL-DEPENDENCIES.md](docs/OFFICIAL-DEPENDENCIES.md) §1-F.
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
dsh plugin --profile <name> add dsh-plugin-manager-companion@latest
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Requires DSH >= 0.1.6-alpha.2. After installing, enable it on the plugin management page and restart that profile.
|
|
59
|
+
|
|
60
|
+
## Capabilities
|
|
61
|
+
|
|
62
|
+
### Quality gate before install
|
|
63
|
+
|
|
64
|
+
Hooked onto the official `installBundle` "install but do not activate" switch:
|
|
65
|
+
|
|
66
|
+
1. `inspect(spec)`: the official side reports what this spec points to;
|
|
67
|
+
2. `installBundle(spec, { enabled: false })`: the official side installs into the environment without activating;
|
|
68
|
+
3. Scan the package: undeclared imports, declared but not installed, official packages declared as plain `dependencies` (which installs a second copy inside the profile and makes the official loader row resolve to it), unresolvable entry points;
|
|
69
|
+
4. On failure, `removeBundle` rolls back; on success, `setBundleEnabled` activates.
|
|
70
|
+
|
|
71
|
+
The trial install is off by default. When enabled, the candidate is first installed into a `<environment>-dpmc` trial environment and started once; only a passing candidate is installed into the real environment. Trial environments have no count limit and are kept for 14 days by default. A trial install really installs the candidate on your machine and runs its install scripts.
|
|
72
|
+
|
|
73
|
+
### Environment console
|
|
74
|
+
|
|
75
|
+
One settings entry, three sub-pages:
|
|
76
|
+
|
|
77
|
+
- **Health check**: five diagnostic layers. Each finding carries evidence (file and line, or a runtime object) and one of three actions: auto-fixable, needs confirmation, report only. The five layers are L1 dependencies, L2 composition, L3 runtime, L4 consistency, L5 ecosystem; a layer that was not checked reads "not checked" rather than 0.
|
|
78
|
+
- **Environments**: lists every profile on this machine with its run state (process and port). Supports start (terminal window or background), stop, create, rename, remove, copying plugins across environments, backup export, diff and restore.
|
|
79
|
+
- **Settings**: this plugin's configuration, stored in the official settings service and edited in the UI.
|
|
80
|
+
|
|
81
|
+
### Marketplace, skills and presets
|
|
82
|
+
|
|
83
|
+
The marketplace page shows the community index, with search over name, repository, topics and description, and sorting by stars, update time and recent activity. Cards show risk level and installability (community pick, manual install, non-plugin entry). Installs go through the quality gate and official channels. The detail view shows the verification verdict and risk details from the index, with a link to the original report.
|
|
84
|
+
|
|
85
|
+
The skills and presets page manages SKILL.md files and agent presets installed through this plugin: install records, re-fetch, and uninstall.
|
|
86
|
+
|
|
87
|
+
### Upgrades
|
|
88
|
+
|
|
89
|
+
The "About" settings page lists version status for the official runtime, official experimental packages, and this plugin itself. Update checks run on demand and can also be triggered manually; version selection lists every dist-tag and preselects the newest one on the same line. Upgrades go through official channels and are verified once in a trial environment first; a version that fails verification is never installed into the real environment. A new version loads at the next start.
|
|
90
|
+
|
|
91
|
+
## Command line
|
|
92
|
+
|
|
93
|
+
`dshpmc` offers the same capabilities as the UI, for scripts and CI, for terminal work, and as a fallback when the UI cannot be opened.
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
dshpmc analyze --profile <name> # five-layer health check; exit code 1 when there are findings
|
|
97
|
+
dshpmc list --profile <name> # composition layers, dependencies, installed skills and presets
|
|
98
|
+
dshpmc install | remove | update | mount | uninstall-kind
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`update <name>` rewrites the dependency declaration to `@latest` and reinstalls. A bare `dsh plugin add` does not upgrade a declared range, and `pnpm update` only re-resolves within the declared range, so crossing versions requires rewriting the declaration. The whole upgrade path is the same one the UI uses.
|
|
102
|
+
|
|
103
|
+
`analyze` does not need a running instance: it only reads the filesystem, so an environment with a broken configuration file or a failed start still yields a root cause and file lines. When key layers were not checked, it says the conclusion is incomplete instead of reporting healthy.
|
|
104
|
+
|
|
105
|
+
An agent inside the host process cannot perform plugin writes from the command line: the guard rejects bare `dsh plugin` and `pnpm` mutation commands and points to the official `plugin_manager` tool. `dshpmc` uses the same pnpm channel as that tool, so it is not intercepted.
|
|
106
|
+
|
|
107
|
+
## Platform support
|
|
108
|
+
|
|
109
|
+
All checks run on Linux.
|
|
110
|
+
|
|
111
|
+
**Linux**: verified on real hardware, including unit tests and two end-to-end checks.
|
|
112
|
+
|
|
113
|
+
**Windows x64**: the official host supports Windows. The platform-specific safety fixes (case-insensitive built-in environment names, process tree termination, quoted command lines, `.cmd` entry points, terminal fallback, cross-platform test entry) were each independently re-verified on a real win32 Node. The following are unverified on real hardware or known not to hold:
|
|
114
|
+
|
|
115
|
+
- Terminal window mode depends on Windows Terminal (`wt`); machines without it fall back to background start and the result states why.
|
|
116
|
+
- File permission bits (0600/0700) are a no-op on Windows; a start log containing an access token is protected only by the profile directory ACL.
|
|
117
|
+
- Stopping an environment on Windows is `taskkill /T /F` on the process tree, not a graceful stop; a force-killed instance does not run exit cleanup.
|
|
118
|
+
- Installing skill or preset repositories that contain symlinks needs Developer Mode or administrator rights.
|
|
119
|
+
- The two end-to-end checks and the screenshot tooling depend on bash and Chrome/CDP; the check entry on Windows is `pnpm test`.
|
|
120
|
+
- Unverified: ACLs, service accounts, sessions without a desktop. When enterprise policy disables PowerShell, process information is unreadable and shown as "unknown".
|
|
121
|
+
|
|
122
|
+
**macOS**: unverified. The filesystem is case-insensitive by default; the case checks shared with Windows already cover it.
|
|
123
|
+
|
|
124
|
+
## Known limitations
|
|
125
|
+
|
|
126
|
+
- Only DSH >= 0.1.6-alpha.2 is supported; for earlier versions use 0.6.x from the old repository.
|
|
127
|
+
- Two fixes that require changing the structure of `cordis.patch.yml` (removing duplicate rows, removing orphan rows) only produce steps for you to apply. This plugin does not write that file.
|
|
128
|
+
- A quality-gate rollback can fail: when the official side refuses to remove a bundle (for example one it protects), the result says what remains and you remove it by hand.
|
|
129
|
+
- The install guard intercepts agent tool calls; it does not intercept commands typed in a terminal.
|
|
130
|
+
- Sensitive environment-variable filtering matches by shape, not exhaustively; unmatched shapes still reach the git-source install subprocess.
|
|
131
|
+
- The L3 registration-name conflict check covers profile-local package sources only, not rows in the official scope.
|
|
132
|
+
- A port is known only when the instance command line carries `--port`; a host started on the default port shows "port unknown".
|
|
133
|
+
- The L5 ecosystem layer does not go online and does not judge; it is recorded as "not checked".
|
|
134
|
+
- Marketplace index and risk grading come from the upstream index; this plugin presents them and makes no second judgment.
|
|
135
|
+
|
|
136
|
+
## Contributing
|
|
137
|
+
|
|
138
|
+
Development environment, checks, code layout and the pre-commit checklist are in [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md).
|
|
139
|
+
Design in [docs/DESIGN.md](docs/DESIGN.md), engineering policy in [docs/CODE-POLICY.md](docs/CODE-POLICY.md),
|
|
140
|
+
host and client contracts in [docs/REST-CONTRACT.md](docs/REST-CONTRACT.md), official facts this plugin depends on in [docs/OFFICIAL-DEPENDENCIES.md](docs/OFFICIAL-DEPENDENCIES.md).
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# dsh-plugin-manager-companion
|
|
2
|
+
|
|
3
|
+
DSH 官方插件管理器的补充:安装前质量门、五层环境诊断、多环境管理与插件市场。
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/dsh-plugin-manager-companion)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
前身是 [dsh-web-plugin-manager](https://github.com/LX2000WASD/dsh-web-plugin-manager)(0.6.3,已停止维护)。
|
|
9
|
+
DSH 0.1.6-alpha.2 起官方自带插件管理页,旧仓库遮蔽官方页面并自建写权的做法不再适用,本仓库为重写。
|
|
10
|
+
|
|
11
|
+
## 截图
|
|
12
|
+
|
|
13
|
+
| | |
|
|
14
|
+
|---|---|
|
|
15
|
+
| <br>五层诊断:分组折叠,每条带严重度与证据;未查到的层显示「未查」 | <br>多环境启停、复制与备份恢复 |
|
|
16
|
+
| <br>卡片标出风险、分类与主题;详情展示索引原文 | <br>注册进官方插件页,点开自己的条目就是配置表单 |
|
|
17
|
+
|
|
18
|
+
## 目录
|
|
19
|
+
|
|
20
|
+
- [它做什么,不做什么](#它做什么不做什么)
|
|
21
|
+
- [安装](#安装)
|
|
22
|
+
- [能力](#能力)
|
|
23
|
+
- [命令行](#命令行)
|
|
24
|
+
- [平台支持](#平台支持)
|
|
25
|
+
- [已知限制](#已知限制)
|
|
26
|
+
- [参与开发](#参与开发)
|
|
27
|
+
- [许可](#许可)
|
|
28
|
+
|
|
29
|
+
## 它做什么,不做什么
|
|
30
|
+
|
|
31
|
+
官方插件管理器的 README 列明了自己不做的事。本插件只补这些缺口。
|
|
32
|
+
|
|
33
|
+
| 能力 | 官方 | 本插件 |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| 组合包与插件行的启停 | 已有 | 不做 |
|
|
36
|
+
| 组合包安装与卸载 | 已有 | 不做(安装前插质量门)|
|
|
37
|
+
| 插件配置页托管 | 提供插槽 | 作为注册方接入 |
|
|
38
|
+
| 安装前静态检查 | 无 | 质量门 |
|
|
39
|
+
| 环境诊断 | 无 | 环境控制台 · 体检 |
|
|
40
|
+
| 修改另一个 profile | 明确不做 | 环境控制台 · 环境 |
|
|
41
|
+
| 普通插件模块的加载 | 声明为文件操作 | 技能与预设安装 |
|
|
42
|
+
| 版本列表与更新检测 | 明确不做 | 插件市场 |
|
|
43
|
+
|
|
44
|
+
写操作一律走官方通道:当前环境用官方 `pluginManager` 服务,跨环境用官方 `runPluginCommand`。
|
|
45
|
+
本插件不写 `cordis.patch.yml`,不直接调用 pnpm。
|
|
46
|
+
|
|
47
|
+
官方四处「明确不做」的原文引用、对应的本插件功能,以及官方补上后的退出方式,见
|
|
48
|
+
[docs/OFFICIAL-DEPENDENCIES.md](docs/OFFICIAL-DEPENDENCIES.md) §1-F。
|
|
49
|
+
|
|
50
|
+
## 安装
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
dsh plugin --profile <name> add dsh-plugin-manager-companion@latest
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
要求 DSH >= 0.1.6-alpha.2。装好后在插件管理页启用,重启该 profile。
|
|
57
|
+
|
|
58
|
+
## 能力
|
|
59
|
+
|
|
60
|
+
### 安装前质量门
|
|
61
|
+
|
|
62
|
+
接在官方 `installBundle` 的「装但不激活」开关上:
|
|
63
|
+
|
|
64
|
+
1. `inspect(spec)`:官方确认这个 spec 指向什么;
|
|
65
|
+
2. `installBundle(spec, { enabled: false })`:官方装进环境,不激活;
|
|
66
|
+
3. 扫描该包:未声明的 import、声明了但没装、把官方包声明成普通 `dependencies`(会在 profile 里装出第二份副本,让官方 loader 行解析到它)、入口解析不到;
|
|
67
|
+
4. 不合格则 `removeBundle` 回滚,合格则 `setBundleEnabled` 激活。
|
|
68
|
+
|
|
69
|
+
试装默认关闭。开启后,候选包会先装进 `<环境名>-dpmc` 测试环境并启动一次,通过后才装进真实环境。测试环境不设数量上限,默认保留 14 天。试装会在本机真实安装候选包并执行它自带的安装脚本。
|
|
70
|
+
|
|
71
|
+
### 环境控制台
|
|
72
|
+
|
|
73
|
+
一个设置页入口,三个子页:
|
|
74
|
+
|
|
75
|
+
- **体检**:五层诊断。每条发现带证据(文件与行号,或运行时对象),处置分三级:可自动修复、需确认、仅报告。五层为 L1 依赖、L2 组合、L3 运行时、L4 一致性、L5 生态;某一层未查时,层计数显示「未查」,不显示 0。
|
|
76
|
+
- **环境**:列出本机所有 profile 及其运行状态(进程与端口),支持启动(终端窗口或后台)、停止、新建、重命名、删除、跨环境复制插件、备份导出、差异对比与恢复。
|
|
77
|
+
- **设置**:本插件配置,保存在官方 settings 服务中,在界面上修改。
|
|
78
|
+
|
|
79
|
+
### 插件市场与技能预设
|
|
80
|
+
|
|
81
|
+
市场页展示社区索引,支持按名称、仓库、主题与描述搜索,按星数、更新时间与近期热度排序。卡片标出风险等级与可装性(社区精选、需手动安装、非插件条目)。安装走质量门与官方通道。详情展示索引中的校验结论与风险明细,并给出原始报告链接。
|
|
82
|
+
|
|
83
|
+
技能与预设页管理通过本插件安装的 SKILL.md 与 agent 预设,可查看安装记录、重新拉取与卸载。
|
|
84
|
+
|
|
85
|
+
### 升级
|
|
86
|
+
|
|
87
|
+
设置页「关于」列出官方运行时、官方实验包与本插件自身的版本状态。检查更新按需触发,也可手动检查;版本选择列出全部 dist-tags,默认选中与当前同线的最新版本。升级走官方通道,并在测试环境中先验证一遍;未通过验证的版本不会装进真实环境。新版本在下次启动时加载。
|
|
88
|
+
|
|
89
|
+
## 命令行
|
|
90
|
+
|
|
91
|
+
`dshpmc` 提供与界面等价的能力,用于脚本与 CI、终端操作,以及界面打不开时的备用入口。
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
dshpmc analyze --profile <name> # 五层体检;有问题退出码 1
|
|
95
|
+
dshpmc list --profile <name> # 组合层、依赖、本插件装过的技能与预设
|
|
96
|
+
dshpmc install | remove | update | mount | uninstall-kind
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`update <name>` 把依赖声明重写为 `@latest` 后重装。不带版本号的 `dsh plugin add` 不会升级已声明的范围,`pnpm update` 也只在已声明范围内重解析,跨版本升级必须重写声明。整个升级过程与界面操作走同一条链路。
|
|
100
|
+
|
|
101
|
+
`analyze` 不依赖运行中的实例,只读磁盘,配置文件写坏或启动失败的环境同样能给出根因与文件行号。关键层未查完时,它说明结论不完整,不报健康。
|
|
102
|
+
|
|
103
|
+
宿主进程内的 agent 不能通过命令行做插件写操作:守卫拒绝裸的 `dsh plugin` 与 `pnpm` 变更命令,并指向官方 `plugin_manager` 工具。`dshpmc` 走与官方工具相同的 pnpm 通道,不在拦截范围内。
|
|
104
|
+
|
|
105
|
+
## 平台支持
|
|
106
|
+
|
|
107
|
+
全部检查在 Linux 上运行。
|
|
108
|
+
|
|
109
|
+
**Linux**:已实机验证,含单元测试与两条端到端检查。
|
|
110
|
+
|
|
111
|
+
**Windows x64**:宿主官方支持 Windows。与平台相关的安全修复(内置环境名大小写、进程树终止、带引号命令行识别、`.cmd` 入口、终端降级、跨平台测试入口)已在真实 win32 Node 上逐条独立复验。以下各项未在真机验证或已知不成立:
|
|
112
|
+
|
|
113
|
+
- 终端窗口模式依赖 Windows Terminal(`wt`);没有 wt 的机器降级为后台启动,结果中写明原因。
|
|
114
|
+
- 文件权限位(0600/0700)在 Windows 上是空操作;含访问 token 的启动日志只受 profile 目录的 ACL 保护。
|
|
115
|
+
- 停止环境在 Windows 上是 `taskkill /T /F` 强制结束进程树,不是优雅停止;被强制结束的实例不执行退出清理。
|
|
116
|
+
- 安装含符号链接的技能或预设仓库需要开发者模式或管理员权限。
|
|
117
|
+
- 两条端到端检查与截图工具依赖 bash 与 Chrome/CDP;Windows 上的检查入口是 `pnpm test`。
|
|
118
|
+
- 未验证:ACL、服务账户、无桌面会话。企业策略禁用 PowerShell 时进程信息不可读,此时显示「未知」。
|
|
119
|
+
|
|
120
|
+
**macOS**:未验证。文件系统默认大小写不敏感;与 Windows 同类的大小写检查已覆盖。
|
|
121
|
+
|
|
122
|
+
## 已知限制
|
|
123
|
+
|
|
124
|
+
- 只支持 DSH >= 0.1.6-alpha.2;更早的版本请用旧仓库的 0.6.x。
|
|
125
|
+
- 两条需要修改 `cordis.patch.yml` 结构的修复(删除重复行、删除孤儿行)只给出步骤,由用户手工完成。本插件不写这个文件。
|
|
126
|
+
- 质量门回滚可能失败:官方拒绝移除部分组合包时(例如它被官方保护),结果中会说明残留,需要手工删除。
|
|
127
|
+
- 安装守卫拦的是 agent 的工具调用,拦不住在终端里手敲的命令。
|
|
128
|
+
- 环境变量敏感键过滤按形态匹配,不是全集;未匹配的形态仍会传给 git 源安装的子进程。
|
|
129
|
+
- L3 的注册名冲突只覆盖 profile 本地包的源码,不扫官方 scope 的行。
|
|
130
|
+
- 端口只在实例命令行带 `--port` 时可知;以默认端口启动的宿主显示「端口未知」。
|
|
131
|
+
- L5 生态层不联网、不判定,记为「未查」。
|
|
132
|
+
- 市场索引与风险分级来自上游索引,本插件只呈现,不作二次判断。
|
|
133
|
+
|
|
134
|
+
## 参与开发
|
|
135
|
+
|
|
136
|
+
开发环境、检查项、代码结构与提交前清单见 [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md)。
|
|
137
|
+
设计见 [docs/DESIGN.md](docs/DESIGN.md),工程纪律见 [docs/CODE-POLICY.md](docs/CODE-POLICY.md),
|
|
138
|
+
宿主与客户端契约见 [docs/REST-CONTRACT.md](docs/REST-CONTRACT.md),依赖的官方事实见 [docs/OFFICIAL-DEPENDENCIES.md](docs/OFFICIAL-DEPENDENCIES.md)。
|
|
139
|
+
|
|
140
|
+
## 许可
|
|
141
|
+
|
|
142
|
+
MIT
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# dsh-plugin-manager-companion: 官方插件管理器的伴生补强。
|
|
2
|
+
#
|
|
3
|
+
# 行 id 用包名,绝不用 'plugin-manager'——官方 0.1.6-alpha.2 的 dsh-base
|
|
4
|
+
# 已插入同名行(packages/bundle/base/cordis.patch.yml:20),patch 是数组追加
|
|
5
|
+
# 语义,同 id 不覆盖而是并存,loader 会抛 duplicate loader entry id 让整个
|
|
6
|
+
# profile 起不来。旧仓库正是踩了这个坑(实测复现)。
|
|
7
|
+
- insert:
|
|
8
|
+
- id: dsh-plugin-manager-companion
|
|
9
|
+
name: 'dsh-plugin-manager-companion'
|
package/dist/about.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* about.ts — 「关于」页要的事实:**只读,不猜**(task-95)。
|
|
3
|
+
*
|
|
4
|
+
* 归属:A 类·新模块(补官方没有的信息面板)。
|
|
5
|
+
* 旧实现参考:无(旧 dsh-web-plugin-manager 没有信息面板)。
|
|
6
|
+
* 官方复用:`ctx.profileContext.installAnchor`(安装锚点,与官方包操作同源)。
|
|
7
|
+
* 前提检查:这些事实**客户端一个都拿不到**(浏览器 bundle:无 process、无 node:fs),
|
|
8
|
+
* 所以必须由 host 读、经 op 下发。调研记录见 docs/private/task76-recon.md。
|
|
9
|
+
*
|
|
10
|
+
* ## 本模块的核心纪律:只读事实,不猜事实(DESIGN §12.10)
|
|
11
|
+
*
|
|
12
|
+
* 每个字段要么是**读到的事实**(带来源),要么是 **unknown + 原因**。
|
|
13
|
+
* 明确不做三件事:
|
|
14
|
+
* · 不用 `navigator.userAgent` 推平台(客户端根本没有这个字段,且那是推断不是事实);
|
|
15
|
+
* · 不把版本号写成打包时常量(构建期与运行期可能不是同一份安装);
|
|
16
|
+
* · 不用"看起来像"的兜底值填空(那正是 §12.10 说的"用代理代替事实")。
|
|
17
|
+
*
|
|
18
|
+
* 为什么每条都要带 `source`:用户与后来人都要能判断"这条事实怎么来的"。
|
|
19
|
+
* 一个没有来源的版本号,与一个猜出来的版本号,在界面上长得一模一样。
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* 一条**可能读不到**的事实。
|
|
23
|
+
*
|
|
24
|
+
* 形状刻意做成"三选一"而不是 `value | undefined`:
|
|
25
|
+
* · 读到了 → `{ value, source }`;
|
|
26
|
+
* · 读不到 → `{ unknown: 原因 }`。
|
|
27
|
+
* 用可选值的话,调用方很容易把"读不到"渲染成空白或 0——那正是 §12.3.3 禁的形态。
|
|
28
|
+
* 类型上强制二选一,漏判会在编译期暴露。
|
|
29
|
+
*/
|
|
30
|
+
export type AboutFact<T> = {
|
|
31
|
+
readonly value: T;
|
|
32
|
+
readonly source: string;
|
|
33
|
+
} | {
|
|
34
|
+
readonly unknown: string;
|
|
35
|
+
};
|
|
36
|
+
/** 「关于」页的全部事实(op: `about`)。 */
|
|
37
|
+
export interface AboutFacts {
|
|
38
|
+
/** 官方 dsh 运行时:版本与安装位置。 */
|
|
39
|
+
readonly runtime: {
|
|
40
|
+
readonly version: AboutFact<string>;
|
|
41
|
+
readonly installAnchor: AboutFact<string>;
|
|
42
|
+
};
|
|
43
|
+
/** 本进程的运行时环境。 */
|
|
44
|
+
readonly process: {
|
|
45
|
+
readonly node: AboutFact<string>;
|
|
46
|
+
readonly platform: AboutFact<string>;
|
|
47
|
+
readonly arch: AboutFact<string>;
|
|
48
|
+
};
|
|
49
|
+
/** 本插件自身。 */
|
|
50
|
+
readonly companion: {
|
|
51
|
+
readonly version: AboutFact<string>;
|
|
52
|
+
};
|
|
53
|
+
/** 当前 profile。 */
|
|
54
|
+
readonly profile: {
|
|
55
|
+
readonly name: AboutFact<string>;
|
|
56
|
+
readonly dir: AboutFact<string>;
|
|
57
|
+
};
|
|
58
|
+
/** 用户文件与缓存。 */
|
|
59
|
+
readonly files: {
|
|
60
|
+
readonly settingsPath: AboutFact<string>;
|
|
61
|
+
readonly registryCachePath: AboutFact<string>;
|
|
62
|
+
readonly registryCacheAgeMs: AboutFact<number>;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 收集「关于」页的全部事实。
|
|
67
|
+
*
|
|
68
|
+
* @param options - 注入的锚点与 profile 事实(来自官方 ctx)。
|
|
69
|
+
* @returns 事实集合(每条要么带来源、要么带读不到的原因)。
|
|
70
|
+
*/
|
|
71
|
+
export declare function collectAboutFacts(options?: {
|
|
72
|
+
readonly installAnchor?: string;
|
|
73
|
+
readonly profileName?: string;
|
|
74
|
+
readonly profileDir?: string;
|
|
75
|
+
}): AboutFacts;
|
|
76
|
+
/** 本模块用到的包名常量(供 op 层与测试引用,避免字面量散落)。 */
|
|
77
|
+
export declare const ABOUT_PACKAGE_NAME = "dsh-plugin-manager-companion";
|
package/dist/about.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* about.ts — 「关于」页要的事实:**只读,不猜**(task-95)。
|
|
3
|
+
*
|
|
4
|
+
* 归属:A 类·新模块(补官方没有的信息面板)。
|
|
5
|
+
* 旧实现参考:无(旧 dsh-web-plugin-manager 没有信息面板)。
|
|
6
|
+
* 官方复用:`ctx.profileContext.installAnchor`(安装锚点,与官方包操作同源)。
|
|
7
|
+
* 前提检查:这些事实**客户端一个都拿不到**(浏览器 bundle:无 process、无 node:fs),
|
|
8
|
+
* 所以必须由 host 读、经 op 下发。调研记录见 docs/private/task76-recon.md。
|
|
9
|
+
*
|
|
10
|
+
* ## 本模块的核心纪律:只读事实,不猜事实(DESIGN §12.10)
|
|
11
|
+
*
|
|
12
|
+
* 每个字段要么是**读到的事实**(带来源),要么是 **unknown + 原因**。
|
|
13
|
+
* 明确不做三件事:
|
|
14
|
+
* · 不用 `navigator.userAgent` 推平台(客户端根本没有这个字段,且那是推断不是事实);
|
|
15
|
+
* · 不把版本号写成打包时常量(构建期与运行期可能不是同一份安装);
|
|
16
|
+
* · 不用"看起来像"的兜底值填空(那正是 §12.10 说的"用代理代替事实")。
|
|
17
|
+
*
|
|
18
|
+
* 为什么每条都要带 `source`:用户与后来人都要能判断"这条事实怎么来的"。
|
|
19
|
+
* 一个没有来源的版本号,与一个猜出来的版本号,在界面上长得一模一样。
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
22
|
+
import { dirname, join } from 'node:path';
|
|
23
|
+
import { OUR_PACKAGE_NAME, dshHome } from "./paths.js";
|
|
24
|
+
import { registryCachePath } from "./registry.js";
|
|
25
|
+
/**
|
|
26
|
+
* 读一个 JSON 文件的某个字符串字段。
|
|
27
|
+
*
|
|
28
|
+
* @param path - 文件绝对路径。
|
|
29
|
+
* @param field - 要读的字段名。
|
|
30
|
+
* @param what - 面向用户的说明(写进读不到的原因里)。
|
|
31
|
+
* @returns 事实(读到了给来源;读不到给原因)。
|
|
32
|
+
*/
|
|
33
|
+
function readJsonField(path, field, what) {
|
|
34
|
+
if (!existsSync(path))
|
|
35
|
+
return { unknown: what + ':文件不存在(' + path + ')' };
|
|
36
|
+
try {
|
|
37
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
38
|
+
const value = parsed[field];
|
|
39
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
40
|
+
return { unknown: what + ':文件里没有 ' + field + ' 字段(' + path + ')' };
|
|
41
|
+
}
|
|
42
|
+
return { value, source: path };
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
return { unknown: what + ':读不出来(' + path + '):' + messageOf(error) };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 定位官方 dsh 安装的 package.json(安装锚点)。
|
|
50
|
+
*
|
|
51
|
+
* 两条路,按可靠性排序(与 src/cli.ts 的 resolveInstallAnchor 同源,但这里**不做**
|
|
52
|
+
* "从 cwd 向上找"那一条:服务端进程的 cwd 未必与安装位置有关,猜出来的锚点比没有更坏)。
|
|
53
|
+
* 1. 注入的锚点(`ctx.profileContext.installAnchor`,官方权威);
|
|
54
|
+
* 2. `DSH_INSTALL_ANCHOR` 环境变量(官方 CLI 的同一约定)。
|
|
55
|
+
*
|
|
56
|
+
* @param injected - 注入的锚点。
|
|
57
|
+
* @returns 锚点绝对路径;都拿不到时 undefined(**不猜**)。
|
|
58
|
+
*/
|
|
59
|
+
function locateInstallAnchor(injected) {
|
|
60
|
+
if (injected !== undefined && injected.trim() !== '')
|
|
61
|
+
return injected;
|
|
62
|
+
const fromEnv = process.env['DSH_INSTALL_ANCHOR'];
|
|
63
|
+
if (fromEnv !== undefined && fromEnv.trim() !== '')
|
|
64
|
+
return fromEnv;
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 从锚点文件推出官方 dsh 的 package.json 路径。
|
|
69
|
+
*
|
|
70
|
+
* 锚点**就是**那个 package.json(官方 `installAnchor` 的语义),所以这里只做一次校验:
|
|
71
|
+
* 路径以 `@deepseek-ai/dsh/package.json` 结尾就认;否则如实报"锚点看起来不是官方包的 package.json",
|
|
72
|
+
* 而不是硬读一个可能不存在的文件。
|
|
73
|
+
*
|
|
74
|
+
* @param anchor - 锚点路径。
|
|
75
|
+
* @returns 官方 dsh 的 package.json 路径;不像时 undefined。
|
|
76
|
+
*/
|
|
77
|
+
function dshManifestFrom(anchor) {
|
|
78
|
+
const normalized = anchor.replace(/\\/g, '/');
|
|
79
|
+
if (normalized.endsWith('@deepseek-ai/dsh/package.json'))
|
|
80
|
+
return anchor;
|
|
81
|
+
// 锚点也可能是安装根(含 node_modules 的目录):那种情况下拼一个候选出来,
|
|
82
|
+
// 但**只在它真的存在时**才用——不存在就走 unknown。
|
|
83
|
+
const candidate = join(dirname(anchor), 'node_modules', '@deepseek-ai', 'dsh', 'package.json');
|
|
84
|
+
return existsSync(candidate) ? candidate : undefined;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 读本插件自身的 package.json 版本。
|
|
88
|
+
*
|
|
89
|
+
* 为什么不用 `upgradeCheck` 的 self 单元(Lead 明确点名):那条路是"顺带拿到"——
|
|
90
|
+
* 它要用户先进升级检查、且语义是"这个包的升级单元",不是"我正在跑的是哪一版"。
|
|
91
|
+
* 关于页问的是后者,所以直接读自身 manifest。
|
|
92
|
+
*
|
|
93
|
+
* 路径用 `import.meta.url` 推导(本模块被打进 dist/about.js,自身包根在上一级),
|
|
94
|
+
* **不**依赖 cwd、**不**依赖 DSH_HOME:这份代码装在哪,就报哪一份的版本。
|
|
95
|
+
*
|
|
96
|
+
* @returns 事实。
|
|
97
|
+
*/
|
|
98
|
+
function readOwnVersion() {
|
|
99
|
+
try {
|
|
100
|
+
const here = dirname(new URL(import.meta.url).pathname);
|
|
101
|
+
const candidate = join(here, '..', 'package.json');
|
|
102
|
+
return readJsonField(candidate, 'version', '本插件版本');
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
return { unknown: '本插件版本:定位自身位置失败:' + messageOf(error) };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 收集「关于」页的全部事实。
|
|
110
|
+
*
|
|
111
|
+
* @param options - 注入的锚点与 profile 事实(来自官方 ctx)。
|
|
112
|
+
* @returns 事实集合(每条要么带来源、要么带读不到的原因)。
|
|
113
|
+
*/
|
|
114
|
+
export function collectAboutFacts(options = {}) {
|
|
115
|
+
const anchor = locateInstallAnchor(options.installAnchor);
|
|
116
|
+
const manifest = anchor === undefined ? undefined : dshManifestFrom(anchor);
|
|
117
|
+
const runtimeVersion = manifest === undefined
|
|
118
|
+
? { unknown: anchor === undefined
|
|
119
|
+
? 'DSH 版本:拿不到官方安装锚点(本进程不是以 dsh profile 启动的)'
|
|
120
|
+
: 'DSH 版本:锚点不像官方包的 package.json(' + anchor + ')' }
|
|
121
|
+
: readJsonField(manifest, 'version', 'DSH 版本');
|
|
122
|
+
const cachePath = registryCachePath();
|
|
123
|
+
return {
|
|
124
|
+
runtime: {
|
|
125
|
+
version: runtimeVersion,
|
|
126
|
+
installAnchor: anchor === undefined
|
|
127
|
+
? { unknown: '安装位置:拿不到官方安装锚点(本进程不是以 dsh profile 启动的)' }
|
|
128
|
+
: { value: anchor, source: '官方 profileContext.installAnchor' },
|
|
129
|
+
},
|
|
130
|
+
// 进程事实是**这个进程**的事实,不是"猜的":process.* 由 Node 直接给出。
|
|
131
|
+
process: {
|
|
132
|
+
node: { value: process.version, source: '本进程的 process.version' },
|
|
133
|
+
platform: { value: process.platform, source: '本进程的 process.platform' },
|
|
134
|
+
arch: { value: process.arch, source: '本进程的 process.arch' },
|
|
135
|
+
},
|
|
136
|
+
companion: { version: readOwnVersion() },
|
|
137
|
+
profile: {
|
|
138
|
+
name: options.profileName === undefined || options.profileName === ''
|
|
139
|
+
? { unknown: '当前环境名:宿主没有提供 profileContext' }
|
|
140
|
+
: { value: options.profileName, source: '官方 profileContext.name' },
|
|
141
|
+
dir: options.profileDir === undefined || options.profileDir === ''
|
|
142
|
+
? { unknown: '环境目录:宿主没有提供 profileContext' }
|
|
143
|
+
: { value: options.profileDir, source: '官方 profileContext.dir' },
|
|
144
|
+
},
|
|
145
|
+
files: {
|
|
146
|
+
settingsPath: { value: join(dshHome(), 'settings.yaml'), source: '由 DSH_HOME 推导' },
|
|
147
|
+
registryCachePath: { value: cachePath, source: '本插件的市场索引缓存位置' },
|
|
148
|
+
registryCacheAgeMs: readFileAgeMs(cachePath),
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* 读一个文件的年龄(毫秒)。
|
|
154
|
+
*
|
|
155
|
+
* 缓存不存在时**不是 0**:0 是"刚刚写过",而"没有缓存"是另一件事(§12.3.3)。
|
|
156
|
+
*
|
|
157
|
+
* @param path - 文件绝对路径。
|
|
158
|
+
* @returns 事实:年龄(毫秒)或读不到的原因。
|
|
159
|
+
*/
|
|
160
|
+
function readFileAgeMs(path) {
|
|
161
|
+
try {
|
|
162
|
+
const stat = statSync(path);
|
|
163
|
+
return { value: Date.now() - stat.mtimeMs, source: '缓存文件的修改时间' };
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return { unknown: '缓存年龄:缓存文件还不存在(还没有成功抓过索引)' };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 取错误消息(`unknown` 窄化)。
|
|
171
|
+
*
|
|
172
|
+
* @param error - 任意抛出物。
|
|
173
|
+
* @returns 可读消息。
|
|
174
|
+
*/
|
|
175
|
+
function messageOf(error) {
|
|
176
|
+
return error instanceof Error ? error.message : String(error);
|
|
177
|
+
}
|
|
178
|
+
/** 本模块用到的包名常量(供 op 层与测试引用,避免字面量散落)。 */
|
|
179
|
+
export const ABOUT_PACKAGE_NAME = OUR_PACKAGE_NAME;
|