dsh-my-guardian 0.4.3 → 0.4.4
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/CHANGELOG.md +14 -0
- package/lib/dep-precheck.js +20 -5
- package/lib/events.js +19 -5
- package/lib/startup-check.js +17 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.4] - 2026-09-25
|
|
9
|
+
|
|
10
|
+
### 变更
|
|
11
|
+
|
|
12
|
+
- fix(guardian): #407 依赖预检解析 profiles 根宿主包与子路径导出 (#412)
|
|
13
|
+
- fix(dsh-my-guardian): 适配 0.1.7 HMR 重构,取证不绑包名并登记退役事件
|
|
14
|
+
- fix(test): 补测试临时目录配对清理,加有界兜底清扫防陈旧残留
|
|
15
|
+
|
|
16
|
+
## [Unreleased]
|
|
17
|
+
|
|
18
|
+
### 变更
|
|
19
|
+
|
|
20
|
+
- fix(guardian): 依赖预检解析 profiles 根 node_modules 的宿主包与子路径导出,消除「缺少依赖」误报
|
|
21
|
+
|
|
8
22
|
## [0.4.3] - 2026-09-25
|
|
9
23
|
|
|
10
24
|
### 变更
|
package/lib/dep-precheck.js
CHANGED
|
@@ -19,13 +19,28 @@ export function findModuleDir(nmRoot, packageName) {
|
|
|
19
19
|
const dir = join(nmRoot, packageName);
|
|
20
20
|
return existsSync(join(dir, 'package.json')) ? dir : null;
|
|
21
21
|
}
|
|
22
|
-
//
|
|
23
|
-
//
|
|
22
|
+
// Base package of a specifier: 'pkg' -> 'pkg', '@scope/pkg' -> '@scope/pkg',
|
|
23
|
+
// 'pkg/sub' -> 'pkg', '@scope/pkg/sub' -> '@scope/pkg'. Peer specs are package
|
|
24
|
+
// roots today, but resolving the base keeps the lookup correct for any spec.
|
|
25
|
+
export function basePackage(spec) {
|
|
26
|
+
const parts = spec.split('/');
|
|
27
|
+
return spec.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
|
|
28
|
+
}
|
|
29
|
+
// Resolve a dependency from the plugin's nested node_modules, the profile
|
|
30
|
+
// node_modules (hoisted installs), or the profiles-root node_modules where the
|
|
31
|
+
// harness installs its host-provided @deepseek-ai/* packages. The pre-check
|
|
32
|
+
// previously stopped at the profile dir, so every plugin declaring a host
|
|
33
|
+
// package as a peer was reported as missing even though Node resolves it by
|
|
34
|
+
// walking up to $DSH_HOME/profiles/node_modules. Returns the dir or null.
|
|
24
35
|
function resolveDependencyDir(profileDir, pluginDir, dep) {
|
|
25
|
-
const
|
|
36
|
+
const base = basePackage(dep);
|
|
37
|
+
const nested = pluginDir === null ? null : findModuleDir(join(pluginDir, 'node_modules'), base);
|
|
26
38
|
if (nested !== null)
|
|
27
39
|
return nested;
|
|
28
|
-
|
|
40
|
+
const inProfile = findModuleDir(join(profileDir, 'node_modules'), base);
|
|
41
|
+
if (inProfile !== null)
|
|
42
|
+
return inProfile;
|
|
43
|
+
return findModuleDir(join(profileDir, '..', 'node_modules'), base);
|
|
29
44
|
}
|
|
30
45
|
function readPackageJson(dir) {
|
|
31
46
|
if (dir === null)
|
|
@@ -97,7 +112,7 @@ function skippedResult(reason) {
|
|
|
97
112
|
* (ok: true) so an unusual install layout is never a false block.
|
|
98
113
|
*/
|
|
99
114
|
export function checkPeerDependencies({ profileDir, pluginName, }) {
|
|
100
|
-
const pluginDir = findModuleDir(join(profileDir, 'node_modules'), pluginName);
|
|
115
|
+
const pluginDir = findModuleDir(join(profileDir, 'node_modules'), basePackage(pluginName));
|
|
101
116
|
if (pluginDir === null)
|
|
102
117
|
return skippedResult(`无法定位插件 ${pluginName}(未在 profile node_modules 找到 package.json)`);
|
|
103
118
|
const pkg = readPackageJson(pluginDir);
|
package/lib/events.js
CHANGED
|
@@ -22,6 +22,10 @@ export function logEvent(shared, type, message) {
|
|
|
22
22
|
* (test/fixtures/host-events.json,由 scripts/host-events.mjs 从宿主源码取证:
|
|
23
23
|
* `interface Events` 声明 ∪ `ctx.<emit|parallel|...>` 派发),否则必须有
|
|
24
24
|
* HOST_EVENT_FALLBACKS 降级信号。新增/删除 ctx.on 时必须同步本表。
|
|
25
|
+
*
|
|
26
|
+
* 0.1.7-rc.2 起:`loader/entry-init`、`loader/partial-dispose` 仍由宿主
|
|
27
|
+
* cordis-plugin-loader 派发(有效);`hmr/config-update-failed` 已被删除,只剩
|
|
28
|
+
* "已退役 0.1.5-rc.1 兼容"意义,实际诊断走 HOST_EVENT_FALLBACKS 的日志通道。
|
|
25
29
|
*/
|
|
26
30
|
export const LISTENED_HOST_EVENTS = [
|
|
27
31
|
'loader/entry-init',
|
|
@@ -48,8 +52,14 @@ const PENDING_DETAIL = '(error detail logged by host)';
|
|
|
48
52
|
* 替代可观测量(逐字取证,非猜测):0.1.5-rc.1 的 cordis-plugin-hmr 与 0.1.7-rc.2 的
|
|
49
53
|
* dsh-hmr **在同一个 catch 里先打同一对 warn、然后旧版才发事件**:
|
|
50
54
|
* ctx.logger.warn('config reload at %C failed', filename); ctx.logger.warn(error)
|
|
51
|
-
* (旧:cordis-plugin-hmr/lib/index.js;新:
|
|
55
|
+
* (旧:cordis-plugin-hmr/lib/index.js;新:packages/boot/hmr/src/watch-config.ts,
|
|
56
|
+
* 已装宿主 0.1.7-rc.2 对应 node_modules/@deepseek-ai/dsh-hmr/lib/index.js)
|
|
52
57
|
* 所以结构化日志是两个宿主共有的可观测面,用它接手被删事件的诊断职责。
|
|
58
|
+
*
|
|
59
|
+
* 宿主升级到 0.1.7-rc.2 之后:已装宿主也变成 0.1.7-rc.2,`hmr/config-update-failed`
|
|
60
|
+
* 在两个通道里都不存在——该 `ctx.on` 只剩"已退役 0.1.5-rc.1 兼容"意义(注册未知
|
|
61
|
+
* 事件不报错,回退宿主仍可诊断),因此 legacySource 转为 legacyRetired 退役登记。
|
|
62
|
+
* 新宿主的诊断职责全部由结构化 warn 通道承担(marker 在参考源与已装宿主双向取证)。
|
|
53
63
|
*/
|
|
54
64
|
export const HOST_EVENT_FALLBACKS = [
|
|
55
65
|
{
|
|
@@ -57,7 +67,11 @@ export const HOST_EVENT_FALLBACKS = [
|
|
|
57
67
|
kind: 'logger-warn',
|
|
58
68
|
marker: CONFIG_FAILURE_MARKER,
|
|
59
69
|
targetSource: 'packages/boot/hmr/src/watch-config.ts',
|
|
60
|
-
|
|
70
|
+
legacyRetired: {
|
|
71
|
+
version: '0.1.5-rc.1',
|
|
72
|
+
source: 'node_modules/@deepseek-ai/cordis-plugin-hmr/lib/index.js',
|
|
73
|
+
reason: '0.1.7-rc.2 以 @deepseek-ai/dsh-hmr 取代 cordis-plugin-hmr,旧包已从已装宿主移除,双通道并存的在场取证不可复现',
|
|
74
|
+
},
|
|
61
75
|
},
|
|
62
76
|
];
|
|
63
77
|
/** 失败原因文本:Error 取 message、其他字符串化、未配对到时给占位。 */
|
|
@@ -148,9 +162,9 @@ function attachConfigFailureLogFallback(ctx, failures) {
|
|
|
148
162
|
/**
|
|
149
163
|
* Register the loader/HMR diagnostic listeners (R9/R10)。
|
|
150
164
|
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
165
|
+
* 两条通道都注册:`hmr/config-update-failed` 只对已退役的 0.1.5-rc.1 有效,在
|
|
166
|
+
* 0.1.7-rc.2(当前唯一在场宿主)上永不触发(注册未知事件不报错,回退宿主仍可诊断),
|
|
167
|
+
* 当前宿主的诊断完全由日志通道承担——靠 createConfigFailureTracker 去重合并成一条。
|
|
154
168
|
*/
|
|
155
169
|
export function attachEventListeners(ctx, shared) {
|
|
156
170
|
const failures = createConfigFailureTracker(ctx, shared);
|
package/lib/startup-check.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* tree simply yields no roster and an empty report.
|
|
15
15
|
*/
|
|
16
16
|
import { join } from 'node:path';
|
|
17
|
-
import { findModuleDir, checkPeerDependencies, buildDependencyMessage } from './dep-precheck.js';
|
|
17
|
+
import { findModuleDir, checkPeerDependencies, buildDependencyMessage, basePackage } from './dep-precheck.js';
|
|
18
18
|
import { writeStartupIssuesFile } from './state.js';
|
|
19
19
|
import { logEvent } from './events.js';
|
|
20
20
|
/** Profile-node_modules root used for roster resolvability checks. */
|
|
@@ -83,19 +83,21 @@ export function isDisabledEntry(entry) {
|
|
|
83
83
|
}
|
|
84
84
|
/** Removal hint shared by every issue kind (guardian never rewrites YAML). */
|
|
85
85
|
const REMOVE_HINT = '从启动名册(cordis.patch.yml / profile)中删除该条目行,或标记 disabled: true 暂缓加载';
|
|
86
|
-
/** Build an unresolvable-package issue (import stage would fail).
|
|
87
|
-
|
|
86
|
+
/** Build an unresolvable-package issue (import stage would fail). `installTarget`
|
|
87
|
+
* is the base package: a roster row may name a subpath export, and
|
|
88
|
+
* `dsh plugin add pkg/sub` would install the wrong thing. */
|
|
89
|
+
function unresolvedIssue(id, name, installTarget) {
|
|
88
90
|
return {
|
|
89
91
|
type: 'unresolvable',
|
|
90
92
|
entryId: id,
|
|
91
93
|
name,
|
|
92
94
|
message: `插件包 ${name} 无法解析(profile node_modules 中不存在),启动 import 将失败`,
|
|
93
|
-
fix: `dsh plugin add ${
|
|
95
|
+
fix: `dsh plugin add ${installTarget}`,
|
|
94
96
|
remove: REMOVE_HINT,
|
|
95
97
|
};
|
|
96
98
|
}
|
|
97
99
|
/** Build a dependency issue reusing the staged-mount pre-check result. */
|
|
98
|
-
function dependencyIssue(id, name, precheck) {
|
|
100
|
+
function dependencyIssue(id, name, precheck, installTarget) {
|
|
99
101
|
return {
|
|
100
102
|
type: 'dependency',
|
|
101
103
|
entryId: id,
|
|
@@ -103,7 +105,7 @@ function dependencyIssue(id, name, precheck) {
|
|
|
103
105
|
message: buildDependencyMessage(precheck),
|
|
104
106
|
missingDeps: [...precheck.missing, ...precheck.mismatched.map((item) => item.name)],
|
|
105
107
|
installHint: precheck.suggestions[0] ?? null,
|
|
106
|
-
fix: precheck.suggestions[0] ?? `dsh plugin add ${
|
|
108
|
+
fix: precheck.suggestions[0] ?? `dsh plugin add ${installTarget}`,
|
|
107
109
|
remove: REMOVE_HINT,
|
|
108
110
|
};
|
|
109
111
|
}
|
|
@@ -133,14 +135,19 @@ function checkPluginItem(issues, item, nmRoot, profileDir) {
|
|
|
133
135
|
const label = name !== '' ? name : id;
|
|
134
136
|
if (label === '' || !label.startsWith('dsh-'))
|
|
135
137
|
return;
|
|
136
|
-
|
|
138
|
+
// A row may name a subpath export ('dsh-openwrite/bridge'), which is not a
|
|
139
|
+
// directory under node_modules; the installable package is the base and the
|
|
140
|
+
// export map resolves the subpath at import time. Treating the whole name
|
|
141
|
+
// as a directory produced a false "unresolvable" for every subpath row.
|
|
142
|
+
const base = basePackage(label);
|
|
143
|
+
const pluginDir = findModuleDir(nmRoot, base);
|
|
137
144
|
if (pluginDir === null) {
|
|
138
|
-
issues.push(unresolvedIssue(id, label));
|
|
145
|
+
issues.push(unresolvedIssue(id, label, base));
|
|
139
146
|
return;
|
|
140
147
|
}
|
|
141
|
-
const precheck = checkPeerDependencies({ profileDir, pluginName:
|
|
148
|
+
const precheck = checkPeerDependencies({ profileDir, pluginName: base });
|
|
142
149
|
if (!precheck.ok)
|
|
143
|
-
issues.push(dependencyIssue(id, label, precheck));
|
|
150
|
+
issues.push(dependencyIssue(id, label, precheck, base));
|
|
144
151
|
}
|
|
145
152
|
/** Append one duplicate-id issue per id seen in more than one roster row. */
|
|
146
153
|
function collectDuplicateIssues(issues, byId) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-my-guardian",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "DSH 插件治理守护:新装/更新插件先进候选区,启动后热挂载——成功转正、失败自动禁用、连续失败冻结,一键安全模式,侧边栏诊断面板。DSH web plugin: staged plugin loading with auto-disable on failure, freeze, safe mode and a sidebar panel.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|