@perrylink/dsh-plugin-doctor 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 +201 -0
- package/README.md +67 -0
- package/SURVEY.md +265 -0
- package/doctor.mjs +80 -0
- package/lib/checks-collections.mjs +129 -0
- package/lib/checks-cordis.mjs +196 -0
- package/lib/checks-package.mjs +175 -0
- package/lib/checks-smoke.mjs +93 -0
- package/lib/framework.mjs +71 -0
- package/lib/util.mjs +90 -0
- package/package.json +37 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright 2026 dsh-memento contributors
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# dsh-plugin-doctor
|
|
2
|
+
|
|
3
|
+
dsh 插件「完整性 + 运行流畅」一体检测器。零依赖(Node ≥22 自带能力),一次运行同时覆盖
|
|
4
|
+
**包结构静态检查(R)→ cordis 契约扫描(K)→ 动态沙箱冒烟(D)→ 生态集合站清单校验(CC)** 四层。
|
|
5
|
+
判据全部来自 2026-09-07 三路一手调研:deepseek-harness 文档/源码、cordiverse/cordis 源码契约、
|
|
6
|
+
工作区全渠道存量盘点(详见 `SURVEY.md`)。
|
|
7
|
+
|
|
8
|
+
## 用法
|
|
9
|
+
|
|
10
|
+
```powershell
|
|
11
|
+
node doctor.mjs --repo <插件仓路径> # 全量(含动态冒烟,需网络 + pnpm)
|
|
12
|
+
node doctor.mjs --repo <路径> --no-smoke # 仅静态 + 清单
|
|
13
|
+
node doctor.mjs --repo <路径> --dsh 0.1.2-rc.1 # 冒烟宿主版本(默认 npm latest 已发布线)
|
|
14
|
+
node doctor.mjs --repo <路径> --only 静态·包结构,静态·cordis 契约扫描
|
|
15
|
+
node doctor.mjs --repo <路径> --json report.json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- 退出码:`0` = 无 fail/error(可含 warn/skip);`1` = 存在 fail/error。适合直接挂 CI。
|
|
19
|
+
- 冒烟全程使用 `%TEMP%` mkdtemp 临时 `DSH_HOME`/`DSH_AGENTS_HOME`,绝不触碰真实 `~/.dsh`(红线 3)。
|
|
20
|
+
- 每步子进程 stdout/stderr 落盘 `%TEMP%\dsh-doctor-logs-*`,报告尾部打印路径,证据可查。
|
|
21
|
+
|
|
22
|
+
## 检测目录
|
|
23
|
+
|
|
24
|
+
| 分组 | 项 | 判据要点 |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| 静态·包结构 | R0–R8 | 基础字段;**激活门 `dsh.bundle.patch`(关键)**;`npm pack --dry-run` tarball 含入口与 patch;cordis.patch.yml 结构;入口 name/apply 导出;rescope 依赖口径(禁裸 `cordis`);engines 对齐 `^22.19.0 \|\| >=24.0.0`;预构建 + files 白名单;peer 旧 rc 残留(2026-09-05 双基线教训) |
|
|
27
|
+
| 静态·cordis 契约 | K1–K9 | 服务访问 vs inject 声明;ctx 活数据序列化红线;定时器/监听器未包 `ctx.effect`;Schema 含函数;apply 返回形状(v4 Effect 契约);inject 服务名 seam;**v3 遗留 API(3.x→4.x 已删除清单)**;Config 必须是 Standard Schema;`name==='apply'` 特例 |
|
|
28
|
+
| 动态·沙箱冒烟 | D0–D3、D9 | npm pack → `dsh plugin --profile headless add <tarball>` → 断言 `dsh.profile.bundles` 含包名 → `--dump-config` 层标记 → keyless headless 运行期望 **exit 1 + `dsh: MISSING_CREDENTIAL`**(=组合 boot 到请求阶段;排除 NO_ADAPTER/ERR_MODULE_NOT_FOUND/SyntaxError/TypeError)→ 沙箱清理 |
|
|
29
|
+
| 生态·集合站清单 | CC1–CC5 | 认证注册表 spec v1 五维 evidence;adp-list yml 字段/枚举/描述;dsh-catalog 目录条目约束(禁安装命令、截断启发式);omdsh `dshWorkshop` activation 5 值;dsh-plugin-kit 三门(license/五语 README/seam 三角色,优先调用 kit 官方 CLI) |
|
|
30
|
+
|
|
31
|
+
## 判据来源(SURVEY.md 有全文与 URL)
|
|
32
|
+
|
|
33
|
+
- **harness 侧**:`docs/user/develop/basic/publish.md`、`apps/cli/src/plugin.ts`(激活门=唯一开关)、
|
|
34
|
+
`packages/bundle/headless/README.md`(MISSING_CREDENTIAL 判据)、Releases(0.1.2-rc.1 / 0.1.3-alpha.1 变更)、
|
|
35
|
+
`@deepseek-ai/dsh-loader-smoke`(官方化"临时 DSH_HOME + 期望退出码"模式)。
|
|
36
|
+
- **cordis 侧**:cordiverse/cordis v4 源码(registry/fiber/reflect/events.ts)+ DSH cordis-primer/tutorial 文档 +
|
|
37
|
+
v3 `@cordisjs/core@3.10.2` d.ts 差异(3.x→4.x 黑名单)。
|
|
38
|
+
- **生态侧**:dsh-plugin-certification spec v1、adp-list `entries.mjs`/`check-submission.mjs`、
|
|
39
|
+
dsh-catalog `validate.mjs`/`deploy.yml` live smoke、omdsh build-submission、dsh-plugin-kit `verify/*`。
|
|
40
|
+
|
|
41
|
+
## 已知局限(诚实声明)
|
|
42
|
+
|
|
43
|
+
- K 组为**启发式静态扫描**:K1/K3/K4 会漏掉复杂包装、也可能误报——warn 级均需人工复核,不自动判死。
|
|
44
|
+
- D3 只证明「组合可 boot 到模型请求」,**不证明工具 schema 合法或业务逻辑正确**(需带钥 e2e 或 mock LLM 补充)。
|
|
45
|
+
- pnpm `ignored-builds` 属环境配方问题:D1 命中时降级为 warn 并给出 compat.yml allowBuilds 配方提示,
|
|
46
|
+
与认证 spec v1 的 environment-blocked 口径一致,不计为插件缺陷。
|
|
47
|
+
- npm 线宿主(0.1.2-rc.1)packument 无 engines/peerDependencies 强制,R6 为建议门。
|
|
48
|
+
- 本环境实测陷阱:`$` 锚点(无 m 标志)不匹配行尾孤立 `\r` 之前的位置,解析 CRLF 文本必须按
|
|
49
|
+
`/\r?\n/` 切行(已内建处理,勿回退)。
|
|
50
|
+
|
|
51
|
+
## 目录结构
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
doctor.mjs CLI 入口(分组编排、退出码、JSON 报告)
|
|
55
|
+
lib/framework.mjs 检查注册/运行/判定/渲染(零依赖)
|
|
56
|
+
lib/util.mjs 临时沙箱 + 子进程执行(stdout/stderr 落盘,规避管道捕获限制)
|
|
57
|
+
lib/checks-package.mjs 静态·包结构 R0–R8
|
|
58
|
+
lib/checks-cordis.mjs 静态·cordis 契约 K1–K9
|
|
59
|
+
lib/checks-smoke.mjs 动态·沙箱冒烟 D0–D3、D9
|
|
60
|
+
lib/checks-collections.mjs 生态·集合站清单 CC1–CC5
|
|
61
|
+
SURVEY.md 全渠道检测方法梳理 + 判据出处
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 状态
|
|
65
|
+
|
|
66
|
+
WIP 工作夹(非 git 仓,同 `dsh-ticktick-handoff` 口径)。检测器本体零依赖、可独立运行;
|
|
67
|
+
后续如需发布,可参照工作区发布流程另立仓库。
|
package/SURVEY.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# dsh 插件「完整性 + 运行流畅」检测方法全梳理
|
|
2
|
+
|
|
3
|
+
> 2026-09-07 · 依据三路一手调研:① deepseek-harness 官方文档/源码/Releases(web + 只读 checkout
|
|
4
|
+
> `D:\deepseek-harness`@3801401cac);② cordiverse/cordis v4 源码契约 + DSH cordis 文档 + v3 d.ts 差异;
|
|
5
|
+
> ③ 本工作区 `D:\Projects\dsh\plugins` 全渠道存量盘点(认证体系/adp-list/catalog/omdsh/plugin-kit/CI/compat/docs)。
|
|
6
|
+
> 自研检测器实现:本目录 `doctor.mjs`(四层 R/K/D/CC)。所有"通过"结论必须来自真实执行输出(证据纪律)。
|
|
7
|
+
|
|
8
|
+
## 0. 摘要(最重要的 8 条)
|
|
9
|
+
|
|
10
|
+
1. **激活门是唯一硬开关**:host 扫描 profile dependencies,凡解析出的 package.json 声明
|
|
11
|
+
`"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }` 即加入 `dsh.profile.bundles`;没有它 =
|
|
12
|
+
装为普通依赖 + stderr 警告、**永不激活**。`dshWorkshop` 是 omdsh 社区元数据,harness 不读,不能替代。
|
|
13
|
+
2. **keyless headless 冒烟判据**:无凭据 `dsh --profile headless <任务>` 期望 **exit 1 且 stderr 严格匹配
|
|
14
|
+
`dsh: MISSING_CREDENTIAL`**;必须同时排除 `NO_ADAPTER`/`ERR_MODULE_NOT_FOUND`/`SyntaxError`/`TypeError`。
|
|
15
|
+
走到 MISSING_CREDENTIAL ⇒ bundle 层生效 + 插件 apply 成功 + 组合到达模型请求阶段。官方
|
|
16
|
+
`@deepseek-ai/dsh-loader-smoke` 的 `expectedExitCode` 就是这个模式的成品(隔离临时 DSH_HOME + 超时 + 清场)。
|
|
17
|
+
3. **无崩溃隔离**:插件与宿主同进程;boot 期任何 parse/schema/resolution/加载失败 → fail-loud 全 profile
|
|
18
|
+
非零退出。坏插件会杀死启动——这本身是检测信号,但也意味着不能只测 apply,必须测真实 Loader 组合。
|
|
19
|
+
4. **cordis v4 契约要点**:apply 返回只接受 函数/null/Promise\<disposer\>/(async) iterable,其余
|
|
20
|
+
`TypeError('Invalid effect')`;inject=硬依赖(数组/对象形状),可选依赖走 `ctx.get(name)`;未声明直接
|
|
21
|
+
访问 `ctx.xxx` 抛 `cannot get property ... without inject`;一切注册皆 effect,卸载自动回滚;v3 的
|
|
22
|
+
fork/reusable/using/scope/runtime/lifecycle/config/collect/accept/decline/alias/off 全部删除(3.x 插件必查)。
|
|
23
|
+
5. **npm 线 vs checkout 线分裂必须进检测**:npm latest=0.1.2-rc.1(packument 无 engines/peerDependencies);
|
|
24
|
+
checkout 0.1.3-alpha.1 的 session 破坏性变更(SessionHandle / 异步 create / SESSION_FORMAT v2)未上 npm。
|
|
25
|
+
冒烟基线默认钉 npm 线;会话类插件另做 0.1.3-alpha.1 专项审查。
|
|
26
|
+
6. **生态存量检测资产已相当成熟**:认证 spec v1 五维、adp-list 七 workflow 门禁、catalog 双 schema + live
|
|
27
|
+
smoke、plugin-kit 三门、compat.yml 三段——自研检测器的正确姿势是**复用 + 补缺**,不是另起炉灶。
|
|
28
|
+
7. **工程红线**:一切动态测试用 `%TEMP%` mkdtemp 临时 DSH_HOME(绝不碰真实 `~/.dsh`);pnpm ignored-builds
|
|
29
|
+
属环境配方问题(按 allowBuilds compat 配方),记 environment-blocked 而非插件缺陷;证据不足记
|
|
30
|
+
`no-evidence`,禁止编造通过。
|
|
31
|
+
8. **自研检测器 `dsh-plugin-doctor`**:零依赖、四层(R 包结构 / K cordis 契约 / D 沙箱冒烟 / CC 集合站清单)、
|
|
32
|
+
退出码 CI 友好;2026-09-07 已对 dsh-github、dsh-memento 实测(§5)。
|
|
33
|
+
|
|
34
|
+
## 1. 渠道全景:每个集合站/渠道的检测方法与判据
|
|
35
|
+
|
|
36
|
+
### 1.1 deepseek-harness 官方运行时(deepseek-ai/deepseek-harness)
|
|
37
|
+
|
|
38
|
+
**安装与激活链**(`docs/user/develop/basic/publish.md`、`apps/cli/src/plugin.ts`、`apps/cli/reference/README.md`):
|
|
39
|
+
|
|
40
|
+
- `dsh plugin --profile <name> <args...>` = pnpm 转发器:首用初始化 profile(`$DSH_HOME/profiles/<name>/`)→
|
|
41
|
+
`spawnSync('pnpm', …)` → pnpm exit 0 后 reconcile bundles。DSH_HOME 优先级:显式路径 > `$DSH_HOME` env > `~/.dsh`。
|
|
42
|
+
- 五内建模板:web / headless / sdk / sdk-minimal / acp。web=常驻+浏览器+热载(`dsh web`,默认端口 3080);
|
|
43
|
+
headless=一次性(位置参数任务、无端口)。**bundle 成员变化是启动边界**:add/remove/update 后必须重启。
|
|
44
|
+
- 层序:bundles 列表序 → profile `cordis.patch.yml` → 家级 `$DSH_HOME/cordis.patch.yml` → `--patch` overlay;
|
|
45
|
+
后层按 id **整行替换**(非深合并)。`--dump-config` 不 boot 验证层,输出 `# == <pkg>` 来源标记。
|
|
46
|
+
|
|
47
|
+
**npm 插件包形态(官方范式)**:`name`/`version`/`type: "module"`/`main`(构建产物,范式 `lib/index.js`)/
|
|
48
|
+
`files` 含入口 + `cordis.patch.yml`/**`"dsh": {"bundle": {"patch": "./cordis.patch.yml"}}`**;patch 为 YAML 数组
|
|
49
|
+
`- insert: - id: <rowId>, name: <包名>`(name 经 profile node_modules 解析,必须是包名);入口导出
|
|
50
|
+
`export const name` + `export function apply(ctx)`,可选 `inject: [...]` 与 `Config`(schemastery Schema);
|
|
51
|
+
依赖口径 `@deepseek-ai/cordis` 同时 peer+dev(裸 `cordis` 是上游 cordiverse 包);工具注册正典
|
|
52
|
+
`ctx.tools.register(defineTool({...}))`(来自 `@deepseek-ai/dsh-tools`);git 直装需 `prepare` + 用户侧
|
|
53
|
+
`pnpm-workspace.yaml allowBuilds`,npm/tarball 必须预构建。
|
|
54
|
+
|
|
55
|
+
**官方验证设施(可直接复用的检测模式)**:`@deepseek-ai/dsh-loader-smoke`(`runLoaderSmoke({tempDirPrefix,
|
|
56
|
+
binScript, configPath, expectedExitCode, processTimeoutMs, inspect})`:隔离临时 cwd + 隔离
|
|
57
|
+
DSH_HOME/DSH_AGENTS_HOME + 超时杀进程 + 期望退出码判定 + 清场);`apps/cli/tests/profiles/headless/tests/
|
|
58
|
+
keyless-smoke.e2e.ts`(真实 Loader 树 + 断言会话 JSONL 落盘 + 工具清单);打包门 publint /
|
|
59
|
+
verify-node-next-types / verify-npm-install-layout / verify-application-entrypoints。
|
|
60
|
+
|
|
61
|
+
**引擎与版本**:`engines.node: "^22.19.0 || >=24.0.0"`(CI 22.19/24/26;Node 23 整线排除;22.19 地板由
|
|
62
|
+
pi-ai 抬升);pnpm 11.7(Corepack);npm 线 `@deepseek-ai/dsh@0.1.2-rc.1` packument **无 engines/无
|
|
63
|
+
peerDependencies**(插件 engines 声明是建议非强制)。
|
|
64
|
+
|
|
65
|
+
**影响插件的变更史**(Releases 即 changelog):rc.1 = `Session.events` → `seq`/`eventAt()`/`snapshotEvents()`、
|
|
66
|
+
`ApiProxy` 移除 → `@Remote`、SQLite session 后端移除、Node 24.0–24.11.1 修复、官方请求默认附带插件包名+版本;
|
|
67
|
+
0.1.3-alpha.1(未上 npm)= SessionHandle / 异步 `agentLoop.create()` / **SESSION_FORMAT v2**(v0/v1 相邻
|
|
68
|
+
generation 迁移);rc.8 SQLite 格式不兼容;rc.7 设置卡片能力。README 明示 developer preview、破坏必续。
|
|
69
|
+
|
|
70
|
+
### 1.2 awesome-dsh-plugin(`adp-list/` 上游收录)
|
|
71
|
+
|
|
72
|
+
- **条目 schema**(`scripts/lib/entries.mjs` `validateEntries()`):键白名单 `{url,name,category,description,tarball}`;
|
|
73
|
+
url 必须 `https://github.com/owner/repo`;文件名 = slugFor(url)(monorepo 子包 `owner__repo--path.yml`);
|
|
74
|
+
category 23 值枚举;`description.en` 必填单行(zh 可选);tarball 必须 GitHub Release 托管 https `.tgz`
|
|
75
|
+
(github.com / objects.githubusercontent.com / release-assets.githubusercontent.com);BOM 剥离。
|
|
76
|
+
- **contributing 硬要求**:`dsh.bundle`(仅 `dsh.client` 拒)、真实代码(占位/README-only 拒)、仓库 ≥1 天、
|
|
77
|
+
活跃维护、`dsh-plugin` topic、描述属实(数字/API 名与代码核对)、分类贴切、纯聚合包不收、依赖指向上游作者、
|
|
78
|
+
每 PR ≤3 条、screenshots 1–8 张 GitHub 托管、npm 映射自动采集(手写 `npm:` 键拒)。
|
|
79
|
+
- **CI 门禁五件套**:`pr-check`(stale-fork 守卫、yml 位置、README 与数据一致 `generate-readme.mjs --check`、
|
|
80
|
+
awesome-lint、build-site);`pr-gate`(`check-submission.mjs`:仓库存在且未归档、非 DSH 本体、未 vendor
|
|
81
|
+
dsh-base/web-app/headless、全树扫 `dsh.bundle` ≤40 manifest、年龄 ≥1 天、每 PR ≤3 条;rate-limit 未检项
|
|
82
|
+
记 incomplete **不阻断**);`pr-guard`(动 `.github/` = stale fork;90 分钟无 check fail);`regate`(6 小时
|
|
83
|
+
重跑未验/过期/中性/48h 旧 PASS);`decay-scan`(周扫 gone/archived/dormant 6 月/unbundled,只报 issue 不删,
|
|
84
|
+
inconclusive >5% 拒发布)。辅助:check-bleed(描述窜扰)、probe-npm(registry repository 回指)、
|
|
85
|
+
probe-tarballs(死链剔除)、probe-readmes/downloads/stars/screenshots/updates(新鲜度)。
|
|
86
|
+
|
|
87
|
+
### 1.3 dsh-plugin-certification(spec v1 认证体系)
|
|
88
|
+
|
|
89
|
+
- **五维**:A 清单合规(bundle/LICENSE SPDX/keywords/五语 README/engines)· B 构建卫生(files 白名单、peer/
|
|
90
|
+
optional、无恶意 postinstall、lint/typecheck 门禁)· C 供应链(OpenSSF Scorecard 官方 API)· D 发布完整性
|
|
91
|
+
(npm provenance SLSA,`npm audit signatures` 可验)· E 安装冒烟(隔离一次性 profile 真实 install/load/
|
|
92
|
+
keyless boot,四态 `ok`/`load-fail`/`install-fail`/`skip`)。
|
|
93
|
+
- **判据**:A = 五维全过且 E=ok 无 veto;B = E 过且 A–D ≥3;C = E 过其余不全;D = 硬门失败(缺 bundle/无
|
|
94
|
+
license/恶意模式);Security veto(混淆/凭据外传/异常安装期行为)直接 D 并公布原因;**environment-blocked E
|
|
95
|
+
(如 pnpm approve-builds 交互门)保 B 并记录复现命令,绝不记 D**;无证据记 `no-evidence`。
|
|
96
|
+
- 数据 `data/certified.json`(specVersion v1,entries 含 grade/snapshot/dimensions 五维 evidence/veto);
|
|
97
|
+
`scripts/badge.mjs` 生成徽章;`registry.yml` 每日 cron 重渲染;`dsh-cert-mcp` 只读 MCP 暴露注册表(get/list/
|
|
98
|
+
spec 三工具)。现态:PerryLink/dsh-auto-review grade A。
|
|
99
|
+
|
|
100
|
+
### 1.4 DSH Desktop Market(`dsh-catalog/` 标准目录源)
|
|
101
|
+
|
|
102
|
+
- 源数据 `data/packages.json`(37 条 `{npm,repo,displayName,categories,summary}`)→ `build-catalog.mjs [origin]`
|
|
103
|
+
→ `artifacts/v1/plugins.json` + `catalog-source.json`(v1 双 schema 镜像)。
|
|
104
|
+
- **validate.mjs 判据**:manifestVersion 1.0.0;providerId 模式;endpoint https 且 /v1/plugins;items ≤200;id
|
|
105
|
+
模式+去重;长度上限(name≤160/displayName≤120/summary≤1000);禁控制/bidi 字符;repository 或 package 二选一;
|
|
106
|
+
icon 与 endpoint 同源 `/icons/*.png`;**条目文本禁安装命令**(`dsh plugin --profile|pnpm add|npm i` 正则);
|
|
107
|
+
失败 exit 1。`refresh-snapshot.mjs` 带截断启发式(未以 `[.!?)—–"」』]` 结尾告警)与 mojibake 启发式。
|
|
108
|
+
- **CI live smoke**(deploy.yml):build-icons → build(真实 origin) → validate → 部署(Vercel / CF workers 两段式)
|
|
109
|
+
→ `curl -fsS` manifest + 插件页 + 一个 icon PNG 魔数断言 `[137,80,78,71,13,10,26,10]`;items<34 或缺
|
|
110
|
+
media.icon.url 即 fail。
|
|
111
|
+
|
|
112
|
+
### 1.5 omdsh hub(Workshop 提交)
|
|
113
|
+
|
|
114
|
+
- `build-omdsh-submission.mjs`:package.json 原文读取;`dshWorkshop.lifecycle.activation` ∈
|
|
115
|
+
{immediate, hot-reload, restart-plugin, restart-profile, restart-host} 否则 throw;`restartRequired=/^restart-/`
|
|
116
|
+
(hub intake-lib.mjs 规则);产出 `omdsh-workshop-submission/v2` create-project JSON,`packageManifest`
|
|
117
|
+
verbatim 复制 dshWorkshop。
|
|
118
|
+
- hub 侧前置:`prepare-issue-intake.mjs <event.json>` + `npm run check` 双过;投稿后盯 intake run 与 bot 评论,
|
|
119
|
+
preflight failed 须修后重投。
|
|
120
|
+
|
|
121
|
+
### 1.6 deepseek1024 官方榜与 Showcase
|
|
122
|
+
|
|
123
|
+
- `dsh1024 plugin --profile web add <pkg>` 安装行计入安装排行(真实可装 = 上榜资格;36 个 PerryLink 条目已收录)。
|
|
124
|
+
- Showcase 帖(官方 Discussion)人工审查:描述属实、可复现。
|
|
125
|
+
|
|
126
|
+
### 1.7 Gitee 镜像
|
|
127
|
+
|
|
128
|
+
- 存在性与同步(perrylink gitee-sync,每日 cron),不构成质量门,仅传播渠道。
|
|
129
|
+
|
|
130
|
+
### 1.8 各插件仓自身门禁链(37 仓通用 + 特例)
|
|
131
|
+
|
|
132
|
+
- **check 链**:`pnpm run typecheck && pnpm test && pnpm run build && pnpm run verify && pnpm pack`;
|
|
133
|
+
`prepublishOnly` 重跑全门禁;发布 = push tag → publish workflow(`--provenance`、Trusted Publishing 11 包)。
|
|
134
|
+
- **compat.yml(每月+手动,宿主钉 `@deepseek-ai/dsh@0.1.2-rc.1`)三段**:① bare-import(allowBuilds 预置四包
|
|
135
|
+
dsh-subprocess-local/koffi/node-pty/protobufjs + tarball 空项目 import)② profile(DSH_HOME 临时 + add dsh-base/
|
|
136
|
+
dsh-headless/tarball → `--dump-config` grep 包名 → `timeout 120 dsh --profile compat "Reply with exactly: ok"`,
|
|
137
|
+
判据:日志含 `MISSING_CREDENTIAL`=PASS(树加载证明)或 exit 0 且边界正则匹配 ok=PASS,否则 FAIL exit 1)③
|
|
138
|
+
uninstall 可逆。
|
|
139
|
+
- **CI 标配**:Scorecard、host-compat 版本钉号(`check-host-versions.mjs`)、README 官方仓库声明、安装行、funding、
|
|
140
|
+
`dshWorkshop` manifest;特例门:dsh-memento 覆盖率阈值(lib≥90%/index≥85%/全文件≥90%)+ `test:conformance`
|
|
141
|
+
协议黄金参考;check-endpoints 存活探测(**401/403/405/5xx=存活、404/410/DNS/TLS/超时=失败**)。
|
|
142
|
+
- 可复用模板资产:`scripts/loader-runner.mjs`(独立进程真 Loader)、`verify-self-contained.mjs`、
|
|
143
|
+
`verify-artifacts.mjs`、`check-readmes.mjs`、`smoke-package.mjs`、`check-host-versions.mjs`、dsh-test-drive
|
|
144
|
+
全链路 e2e(install/config/smoke/uninstall/cleanup 五阶段)。
|
|
145
|
+
|
|
146
|
+
## 2. cordis v4 契约要点(插件运行正确的硬约束)
|
|
147
|
+
|
|
148
|
+
来源:cordiverse/cordis v4 源码(registry/fiber/reflect/events/service.ts)+ DSH cordis-primer/cordis-api/
|
|
149
|
+
framework/cordis-tutorial 文档 + v3 `@cordisjs/core@3.10.2` d.ts 差异。cordis 上游 4.0.x stable 从未发布
|
|
150
|
+
(latest=4.0.0-rc.9),DSH 将其 vendored 为 `@deepseek-ai/cordis`——**以 rescope 包为准**。
|
|
151
|
+
|
|
152
|
+
- **插件形态**:function / 有 prototype 的 class / `{apply}` 对象;非法抛 `invalid plugin, expect function or
|
|
153
|
+
object with an "apply" method`;`name==='apply'` 被重置为 undefined;`Plugin.Transform {schema:true}` 用于配置变换。
|
|
154
|
+
- **apply 返回(v4 Effect 契约)**:同步或 async 均可,但返回值只接受 函数 disposer / null / undefined /
|
|
155
|
+
`Promise<disposer>`(fiber 停留 LOADING 直至 settle,期间服务不可见)/ (async) iterable of disposers;
|
|
156
|
+
**其余形状(含 v3 `{dispose}` 对象)抛 `TypeError('Invalid effect')`**。
|
|
157
|
+
- **注入**:`inject: (keyof M)[] | { name?: interceptConfig }`;任一缺失 → fiber 永久 PENDING、apply 不执行;
|
|
158
|
+
服务消失自动卸载、恢复重载;可选依赖 = `ctx.get(name)`(strict=true 只看 ACTIVE provider,缺失 undefined);
|
|
159
|
+
未声明访问抛 `cannot get property "x" without inject`;`ctx.set` 需先 provide(否则
|
|
160
|
+
`cannot set property ... without provide`);同隔离域重复 provide 抛 `service "x" has been registered at ...`;
|
|
161
|
+
provide 卸载链先 `await Promise.allSettled(依赖方.await())` 再清理自身。
|
|
162
|
+
- **生命周期**:`ctx.effect(() => disposer)` 立即执行、disposer 逆序、双调 no-op、卸载后注册抛
|
|
163
|
+
`CordisError('INACTIVE_EFFECT')`;多个异步 disposer **并发**执行(顺序敏感的 teardown 必须写在同一个
|
|
164
|
+
disposer 内串行 await);`ctx.on/plugin/provide/accessor/mixin` 皆 effect 返回 disposer;状态机
|
|
165
|
+
`PENDING→LOADING→ACTIVE→UNLOADING→DISPOSED`(`LOADING↘FAILED`),迁移发 `internal/status`;**FAILED 后依赖
|
|
166
|
+
变化不复活,只能 `update()`**(rc.9 #98);`fiber.await()/restart()/update()/state/getEffects()` 为诊断接口。
|
|
167
|
+
- **事件五模式**:emit 同步不等待;parallel 并发+AggregateError;serial/bail 首 bail 停;waterfall 中间件链
|
|
168
|
+
(**不调 next() = 短路 veto**;next() 二调抛错;返回值同步)。bail 判定 = 非 null/false/undefined。
|
|
169
|
+
`on/once(name, listener, opts)` 返回 disposer;v4 **删除 off()**;事件名可 symbol/原型名(rc.9 #51);
|
|
170
|
+
`internal/*` 保留事件(plugin/status/service/update(waterfall)/get/set/listener/dispatch)。
|
|
171
|
+
- **Schema**:Config 必须是 Standard Schema 校验器("普通对象导出为 Config 无法工作"——教程 05);异步校验抛
|
|
172
|
+
`TypeError('Async config validation is not supported')`;失败抛 `ValidationError` 聚合 issues 带路径
|
|
173
|
+
(`... (at targets)`);default 补齐;schema 可 JSON round-trip、函数/类实例值禁止、`!!js` 仅限 config/disabled。
|
|
174
|
+
- **3.x→4.x 黑名单(迁移必查)**:`reusable`/`reactive`/fork 机制、`inject:{required,optional}`/`using`、
|
|
175
|
+
函数型 Config、`{dispose}` 返回、`ctx.using/scope/runtime/lifecycle/config/start()/stop()/collect()/accept()/
|
|
176
|
+
decline()/alias()/off()`、`Context.service()` 装饰器、v3 Service 类 `static immediate`/`protected start/stop/fork`。
|
|
177
|
+
- **序列化红线**:Context 是 Proxy(inspect.custom),JSON.stringify/structuredClone/全量枚举 = 违规。
|
|
178
|
+
- 上游无插件 lint 工具;可借鉴 cordis-rs 163 场景对照表、DSH `@mode` 事件目录交叉校验(gen-cordis-catalog)、
|
|
179
|
+
`ctx.invariants` 注册表(InvariantError 机械校验)。
|
|
180
|
+
|
|
181
|
+
## 3. 自研检测器:dsh-plugin-doctor(本目录)
|
|
182
|
+
|
|
183
|
+
### 3.1 设计原则
|
|
184
|
+
|
|
185
|
+
零依赖(Node 22 内置);判据全部溯源到 §1/§2 的文件或 URL;静态层启发式只降 warn 不判死、动态层严格断言;
|
|
186
|
+
子进程 stdout/stderr 落盘 `%TEMP%\dsh-doctor-logs-*`(证据纪律);退出码 0/1 直接挂 CI;
|
|
187
|
+
冒烟全程 `%TEMP%` mkdtemp DSH_HOME(红线 3);pnpm ignored-builds 按 environment-blocked 口径降 warn。
|
|
188
|
+
|
|
189
|
+
### 3.2 判据映射表
|
|
190
|
+
|
|
191
|
+
| 检测项 | 来源依据 | 实现方式 |
|
|
192
|
+
|---|---|---|
|
|
193
|
+
| R1 激活门 `dsh.bundle.patch`(关键) | plugin.ts reconcile 逻辑 | 静态 JSON 字段 |
|
|
194
|
+
| R2 tarball 完整性 | publish.md 包形态 + `files` | `npm pack --dry-run --json` 清单核对 |
|
|
195
|
+
| R3 patch 结构 | patch YAML 约定(id/name=包名) | 启发式解析;D2 dump-config 动态兜底 |
|
|
196
|
+
| R4 入口 name/apply/inject | index.md 入口契约 | 产物 + 源码双重 grep |
|
|
197
|
+
| R5 rescope 依赖口径 | publish.md + 工作区双基线 | 依赖字段扫描 + 源码 import 对照 |
|
|
198
|
+
| R6 engines | 官方 `^22.19.0 \|\| >=24.0.0` | 声明解析(建议门) |
|
|
199
|
+
| R7 预构建 + files 覆盖 | publish.md(npm 预构建/git prepare) | main 指向 + files 白名单覆盖 |
|
|
200
|
+
| R8 peer 旧 rc 残留 | 2026-09-05 全仓统一教训 | 旧 prerelease-tuple 正则 |
|
|
201
|
+
| K1 服务访问 vs inject | reflect.ts throw 文案 | ctx.X 全集 vs 全仓 inject 并集 |
|
|
202
|
+
| K2 序列化红线 | Proxy/def-site 追踪 | JSON.stringify(ctx 等正则 |
|
|
203
|
+
| K3 资源未包 ctx.effect | 教程 02 原文 | 定时器/监听器上下文窗口 |
|
|
204
|
+
| K4 Schema 含函数 | schemastery README | Schema+箭头函数同文件 |
|
|
205
|
+
| K5 apply 返回形状 | fiber.ts Invalid effect | async apply 提示 + {dispose} 告警 |
|
|
206
|
+
| K6 inject 已知 seam | architecture.md seam 表 | seam 白名单(含实测补充) |
|
|
207
|
+
| K7 v3 遗留 API | v3 d.ts 差异 11 条 | 黑名单正则(fail 级) |
|
|
208
|
+
| K8 Config 校验器 | 教程 05 | Config 导出初值启发式 |
|
|
209
|
+
| K9 name==='apply' | registry.ts 特例 | 字面量正则 |
|
|
210
|
+
| D0–D3 沙箱冒烟 | compat.yml 正典 + loader-smoke | 临时 DSH_HOME 全链路 + 严格 MISSING_CREDENTIAL |
|
|
211
|
+
| CC1 认证注册表 | spec v1 certified.json | 五维 evidence/veto 核对 |
|
|
212
|
+
| CC2 adp-list 条目 | entries.mjs schema | 本地字段/枚举/描述校验 |
|
|
213
|
+
| CC3 市场目录条目 | catalog v1 schema | 条目约束 + 截断/安装命令启发式 |
|
|
214
|
+
| CC4 omdsh 清单 | build-submission.mjs | activation 5 值 + restartRequired |
|
|
215
|
+
| CC5 插件三门 | dsh-plugin-kit verify/* | 优先调 kit 官方 CLI(未构建则本地降级) |
|
|
216
|
+
|
|
217
|
+
### 3.3 局限(诚实声明)
|
|
218
|
+
|
|
219
|
+
- K 组为启发式:漏检复杂包装、可能误报——warn 均需人工复核;`$` 锚点与行尾 `\r` 的环境陷阱已内建规避。
|
|
220
|
+
- D3 只证明「组合可 boot 到模型请求」,不证明工具 schema/业务正确(需带钥 e2e 或 mock LLM 补)。
|
|
221
|
+
- 未覆盖:L9 浏览器 UI(Playwright)、L8 soak、性能/启动开销、跨平台矩阵——可后续加组。
|
|
222
|
+
|
|
223
|
+
## 4. 使用建议
|
|
224
|
+
|
|
225
|
+
1. **提交前**:`node doctor.mjs --repo . --no-smoke`(秒级);发版/收录前:全量(含 D 组,约几分钟)。
|
|
226
|
+
2. **CI 接入**:静态层 job(`--no-smoke --json`)+ 每月 compat.yml 已有冒烟,双轨互补。
|
|
227
|
+
3. **双基线**:npm 线冒烟钉 `--dsh 0.1.2-rc.1`;checkout/0.1.3-alpha.1 线用各仓 `typecheck`(tsconfig paths 对照)
|
|
228
|
+
+ 会话类插件按 §1.1 变更清单专项审查(SessionHandle/异步 create/v2)。
|
|
229
|
+
4. **收录/认证工作流**:改动 adp-list yml、catalog 条目、认证五维证据后跑对应 CC 项 + D 组取证。
|
|
230
|
+
5. **pnpm ignored-builds**:按 compat.yml allowBuilds 配方(dsh-subprocess-local/koffi/node-pty/protobufjs)处理,
|
|
231
|
+
记录复现命令,不判插件缺陷。
|
|
232
|
+
|
|
233
|
+
## 5. 实测记录(2026-09-07 真实执行,证据纪律:以下结论均出自真实运行输出)
|
|
234
|
+
|
|
235
|
+
### dsh-github(@perrylink/dsh-github 0.7.5 · 全量含动态冒烟)
|
|
236
|
+
|
|
237
|
+
- 静态 R0–R8 **全 PASS**(激活门 / tarball 88 文件 / patch 结构 / 入口导出 / rescope 口径 / engines /
|
|
238
|
+
预构建 / peer 对齐)。
|
|
239
|
+
- K 组:**K1 WARN**(`state.ts:97` 的 `subagents: ctx.subagents` 未在 inject 声明——硬依赖应 inject、
|
|
240
|
+
可选依赖应 `ctx.get()`);**K3 WARN**(`bot.ts:145` setInterval、`github.ts:98–99` timeout+abort 监听、
|
|
241
|
+
`jobs.ts:100` timeout 疑似未包 `ctx.effect`,需人工复核);其余 K2/K4–K9 PASS。
|
|
242
|
+
- CC 组:CC2/CC3/CC4 PASS;CC1 SKIP(未进认证注册表);**CC5 FAIL**(kit 官方 CLI:src 缺
|
|
243
|
+
Service Definition/Service Provider/Consumer 三角色 marker)。
|
|
244
|
+
- D 组 **D0–D3 全 PASS**:`plugin add` 后 `dsh.profile.bundles` 含 `@perrylink/dsh-github` → `--dump-config`
|
|
245
|
+
层标记 `# == @perrylink/dsh-github` → keyless headless **exit 1 + `dsh: MISSING_CREDENTIAL`**(组合 boot
|
|
246
|
+
至模型请求阶段)→ 沙箱清理。证据:`reports/dsh-github.json` + 日志 `dsh-doctor-logs-WdvLA1`。
|
|
247
|
+
|
|
248
|
+
### dsh-memento(dsh-memento 0.5.6 · 全量含动态冒烟)
|
|
249
|
+
|
|
250
|
+
- **R8 FAIL(真实缺陷,检测器首个战果)**:peer `@deepseek-ai/dsh-settings: >=0.1.0-rc.1 <0.2.0` 是旧
|
|
251
|
+
prerelease-tuple 区间(裸解析只匹配 0.1.0-rc.x 单行,与宿主 0.1.2-rc.1 波次冲突——2026-09-05 同款教训;
|
|
252
|
+
devDep 已正确钉 0.1.2-rc.1,建议 peer 改为 `>=0.1.2-rc.1 <0.2.0`)。
|
|
253
|
+
- K 组:仅 **K4 WARN**(`index.mjs` 同文件出现 Schema 与箭头函数,需人工复核是否有函数值进 schema);
|
|
254
|
+
K1/K2/K3/K5–K9 PASS(剥注释扫描后假阳性清零)。
|
|
255
|
+
- CC 组:CC2/CC3/CC4 PASS;CC1 SKIP;**CC5 FAIL**(纯 JS 仓无 `src/`,kit verify-seam 结构不适用——
|
|
256
|
+
检测器已自动加注,建议 kit 侧扩展扫描范围或人工豁免)。
|
|
257
|
+
- D 组 **D0–D3 全 PASS**(MISSING_CREDENTIAL 判据,树加载证明)。证据:`reports/dsh-memento.json` +
|
|
258
|
+
日志 `dsh-doctor-logs-lJNAmQ`。
|
|
259
|
+
|
|
260
|
+
### 检测器自身迭代记录(本会话修复清单)
|
|
261
|
+
|
|
262
|
+
Windows shell 解析 npm.cmd shim;**行尾孤立 `\r` 与 `$` 锚点的环境陷阱**(本机 `$` 不匹配行尾 `\r` 前,
|
|
263
|
+
CRLF 行必须按 `/\r?\n/` 切分);node.exe 直调不走 shell(路径含空格);K 组:全仓 inject 并集、
|
|
264
|
+
剥注释后扫描(JSDoc 假阳性清零)、纯 JS 仓源文件发现(无 build 脚本且 main 指向 lib/ 时 lib 即源码)、
|
|
265
|
+
自 provide 服务从"未知 seam"豁免;CC5 纯 JS 仓注记。
|
package/doctor.mjs
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
// dsh-plugin-doctor:dsh 插件「完整性 + 运行流畅」一体检测器
|
|
4
|
+
// 分组:静态·包结构 / 静态·cordis 契约扫描 / 动态·沙箱冒烟 / 生态·集合站清单
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { readFileSync, existsSync, mkdtempSync, writeFileSync } from 'node:fs'
|
|
7
|
+
import { tmpdir } from 'node:os'
|
|
8
|
+
import { Doctor, verdict, render } from './lib/framework.mjs'
|
|
9
|
+
import { addChecks as addPackageChecks } from './lib/checks-package.mjs'
|
|
10
|
+
import { addChecks as addCordisChecks } from './lib/checks-cordis.mjs'
|
|
11
|
+
import { addSmokeChecks } from './lib/checks-smoke.mjs'
|
|
12
|
+
import { addChecks as addCollectionChecks } from './lib/checks-collections.mjs'
|
|
13
|
+
|
|
14
|
+
const USAGE = `dsh-plugin-doctor —— dsh 插件完整性 + 运行流畅一体检测器
|
|
15
|
+
|
|
16
|
+
用法: node doctor.mjs --repo <插件仓路径> [选项]
|
|
17
|
+
|
|
18
|
+
选项:
|
|
19
|
+
--no-smoke 跳过动态沙箱冒烟(默认执行;需网络 + pnpm)
|
|
20
|
+
--dsh <版本> 冒烟宿主版本(默认 0.1.2-rc.1 = npm 已发布 latest)
|
|
21
|
+
--only <分组> 只跑指定分组(逗号分隔):
|
|
22
|
+
静态·包结构, 静态·cordis 契约扫描, 动态·沙箱冒烟, 生态·集合站清单
|
|
23
|
+
--json <路径> 另存 JSON 报告
|
|
24
|
+
-h, --help 显示帮助
|
|
25
|
+
|
|
26
|
+
退出码: 0 = 无 fail/error(可含 warn/skip);1 = 存在 fail/error
|
|
27
|
+
安全: 冒烟全程使用 %TEMP% mkdtemp 临时 DSH_HOME,绝不触碰真实 ~/.dsh`
|
|
28
|
+
|
|
29
|
+
function parseArgs(argv) {
|
|
30
|
+
const opts = { repo: null, smoke: true, dshVersion: '0.1.2-rc.1', json: null, groups: null, help: false }
|
|
31
|
+
for (let i = 0; i < argv.length; i++) {
|
|
32
|
+
const a = argv[i]
|
|
33
|
+
if (a === '--repo' || a === '-r') opts.repo = argv[++i]
|
|
34
|
+
else if (a === '--no-smoke') opts.smoke = false
|
|
35
|
+
else if (a === '--dsh') opts.dshVersion = argv[++i]
|
|
36
|
+
else if (a === '--json') opts.json = argv[++i]
|
|
37
|
+
else if (a === '--only') opts.groups = argv[++i].split(',').map((s) => s.trim())
|
|
38
|
+
else if (a === '--help' || a === '-h') opts.help = true
|
|
39
|
+
else if (!a.startsWith('-')) opts.repo = a
|
|
40
|
+
}
|
|
41
|
+
return opts
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function main() {
|
|
45
|
+
const opts = parseArgs(process.argv.slice(2))
|
|
46
|
+
if (opts.help) { console.log(USAGE); return }
|
|
47
|
+
if (!opts.repo) { console.error('缺少 --repo <路径>\n\n' + USAGE); process.exitCode = 2; return }
|
|
48
|
+
const repoPath = path.resolve(opts.repo)
|
|
49
|
+
const pkgPath = path.join(repoPath, 'package.json')
|
|
50
|
+
if (!existsSync(pkgPath)) { console.error(`未找到 ${pkgPath}`); process.exitCode = 2; return }
|
|
51
|
+
let pkg
|
|
52
|
+
try { pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) } catch (e) {
|
|
53
|
+
console.error(`package.json 解析失败: ${e.message}`); process.exitCode = 2; return
|
|
54
|
+
}
|
|
55
|
+
const logDir = mkdtempSync(path.join(tmpdir(), 'dsh-doctor-logs-'))
|
|
56
|
+
const ctx = {
|
|
57
|
+
repoPath,
|
|
58
|
+
pkg,
|
|
59
|
+
pkgName: pkg.name,
|
|
60
|
+
logDir,
|
|
61
|
+
workspaceRoot: path.resolve(import.meta.dirname, '..'),
|
|
62
|
+
}
|
|
63
|
+
const doctor = new Doctor()
|
|
64
|
+
addPackageChecks(doctor, ctx)
|
|
65
|
+
addCordisChecks(doctor, ctx)
|
|
66
|
+
addCollectionChecks(doctor, ctx)
|
|
67
|
+
if (opts.smoke) addSmokeChecks(doctor, ctx, { dshVersion: opts.dshVersion })
|
|
68
|
+
|
|
69
|
+
const results = await doctor.run(ctx, { groups: opts.groups })
|
|
70
|
+
console.log(`# dsh-plugin-doctor 报告\n目标: ${repoPath}\n包名: ${ctx.pkgName}`)
|
|
71
|
+
console.log(render(results))
|
|
72
|
+
console.log(`日志目录: ${logDir}`)
|
|
73
|
+
if (opts.json) {
|
|
74
|
+
writeFileSync(opts.json, JSON.stringify({ repo: repoPath, pkgName: ctx.pkgName, results }, null, 2), 'utf8')
|
|
75
|
+
console.log(`JSON 报告: ${opts.json}`)
|
|
76
|
+
}
|
|
77
|
+
process.exitCode = verdict(results).ok ? 0 : 1
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
main().catch((err) => { console.error(err); process.exitCode = 2 })
|