dsh-rule-engine 0.5.17 → 0.6.1
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 +268 -236
- package/lib/core/authorization.js +68 -13
- package/lib/core/contract.js +1 -1
- package/lib/core/guard-core.js +46 -22
- package/lib/core/intent.js +322 -322
- package/lib/core/mount-signature.js +87 -87
- package/lib/core/patterns.js +744 -730
- package/lib/core/state.js +2 -1
- package/lib/core/text-detect.js +24 -4
- package/lib/core/tool-catalog.js +3 -0
- package/lib/index.js +101 -13
- package/lib/service.js +5 -2
- package/package.json +60 -58
- package/scripts/audit-mount-consistency.mjs +198 -198
- package/scripts/check-tool-coverage.mjs +63 -41
- package/scripts/lib/pnpm-exempt.mjs +56 -0
- package/scripts/local-residue-scan.mjs +40 -0
- package/scripts/publish-aptitude-check.mjs +102 -144
- package/scripts/readme-version-check.mjs +41 -0
- package/scripts/release-plugin.mjs +371 -329
- package/scripts/verify-all.mjs +85 -2
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
// mount-signature.js - 规则 27 装配状态哈希
|
|
2
|
-
// 用“装配内容”而不是单调 revision 判断是否需要重跑全量审计。
|
|
3
|
-
// 覆盖:profile bundles、本地 dependencies(link/file/github/http)、用户 patch、根 patch、bundle 内部 patch、运行时注入 registry。
|
|
4
|
-
import { createHash } from "node:crypto";
|
|
5
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
-
import { dirname, join } from "node:path";
|
|
7
|
-
import { dshHome } from "./paths.js";
|
|
8
|
-
|
|
9
|
-
function tryRead(p) {
|
|
10
|
-
try {
|
|
11
|
-
return existsSync(p) ? readFileSync(p, "utf8") : "";
|
|
12
|
-
} catch {
|
|
13
|
-
return "";
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function resolveBundlePkg(b, profileDir, home) {
|
|
18
|
-
const candidates = [
|
|
19
|
-
join(profileDir, "node_modules", b, "package.json"),
|
|
20
|
-
join(home, "profiles", "node_modules", b, "package.json")
|
|
21
|
-
];
|
|
22
|
-
if (b.startsWith("@")) {
|
|
23
|
-
const [scope, name] = b.split("/");
|
|
24
|
-
candidates.push(join(home, "profiles", "node_modules", scope, name, "package.json"));
|
|
25
|
-
}
|
|
26
|
-
for (const c of candidates) if (existsSync(c)) return c;
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** 从工具参数/命令中提取目标 profile 名(缺省 web) */
|
|
31
|
-
export function profileNameFromArgs(args = {}) {
|
|
32
|
-
const p = String(args.file_path || args.path || "");
|
|
33
|
-
const pm = p.match(/profiles[\\/]([^\\/]+)[\\/]/i);
|
|
34
|
-
if (pm) return pm[1];
|
|
35
|
-
if (args.profile) return String(args.profile);
|
|
36
|
-
const cmd = String(args.command || args.code || "");
|
|
37
|
-
let idx = cmd.indexOf("--profile");
|
|
38
|
-
if (idx >= 0) {
|
|
39
|
-
const rest = cmd.slice(idx + "--profile".length).trim();
|
|
40
|
-
const val = rest.replace(/^=/, "").split(/\s+/)[0];
|
|
41
|
-
if (val) return val;
|
|
42
|
-
}
|
|
43
|
-
return "web";
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** 计算装配状态哈希;profile 缺失时返回固定 missing 标记,避免误伤 */
|
|
47
|
-
export function computeMountSignature(profileName = "web") {
|
|
48
|
-
const home = dshHome();
|
|
49
|
-
const profileDir = join(home, "profiles", profileName);
|
|
50
|
-
const pkgPath = join(profileDir, "package.json");
|
|
51
|
-
if (!existsSync(pkgPath)) return `missing-profile:${profileName}`;
|
|
52
|
-
const hash = createHash("sha256");
|
|
53
|
-
let pkg;
|
|
54
|
-
try {
|
|
55
|
-
pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
56
|
-
} catch (e) {
|
|
57
|
-
return `profile-parse-error:${profileName}:${e instanceof Error ? e.message : String(e)}`;
|
|
58
|
-
}
|
|
59
|
-
const bundles = pkg?.dsh?.profile?.bundles || [];
|
|
60
|
-
const deps = pkg?.dependencies || {};
|
|
61
|
-
hash.update("bundles:" + JSON.stringify(bundles));
|
|
62
|
-
// 只把会影响装配类型判断的本地依赖纳入哈希;普通 registry 版本变化不触发重审计
|
|
63
|
-
const localDeps = {};
|
|
64
|
-
for (const [name, spec] of Object.entries(deps)) {
|
|
65
|
-
if (typeof spec === "string" && /^(link:|file:|github:|http)/i.test(spec)) localDeps[name] = spec;
|
|
66
|
-
}
|
|
67
|
-
hash.update("localDeps:" + JSON.stringify(localDeps));
|
|
68
|
-
hash.update("profilePatch:" + tryRead(join(profileDir, "cordis.patch.yml")));
|
|
69
|
-
hash.update("rootPatch:" + tryRead(join(home, "cordis.patch.yml")));
|
|
70
|
-
hash.update("injectRegistry:" + tryRead(join(home, "
|
|
71
|
-
for (const b of bundles) {
|
|
72
|
-
const bp = resolveBundlePkg(b, profileDir, home);
|
|
73
|
-
if (!bp) {
|
|
74
|
-
hash.update("bundleMissing:" + b);
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
try {
|
|
78
|
-
const pkgB = JSON.parse(readFileSync(bp, "utf8"));
|
|
79
|
-
hash.update("bundlePkg:" + b + ":" + JSON.stringify({ version: pkgB.version, dsh: pkgB.dsh }));
|
|
80
|
-
const patchRel = pkgB?.dsh?.bundle?.patch;
|
|
81
|
-
if (patchRel) hash.update("bundlePatch:" + b + ":" + tryRead(join(dirname(bp), patchRel)));
|
|
82
|
-
} catch (e) {
|
|
83
|
-
hash.update("bundlePkgError:" + b + ":" + (e instanceof Error ? e.message : String(e)));
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return hash.digest("hex");
|
|
87
|
-
}
|
|
1
|
+
// mount-signature.js - 规则 27 装配状态哈希
|
|
2
|
+
// 用“装配内容”而不是单调 revision 判断是否需要重跑全量审计。
|
|
3
|
+
// 覆盖:profile bundles、本地 dependencies(link/file/github/http)、用户 patch、根 patch、bundle 内部 patch、运行时注入 registry。
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { dshHome } from "./paths.js";
|
|
8
|
+
|
|
9
|
+
function tryRead(p) {
|
|
10
|
+
try {
|
|
11
|
+
return existsSync(p) ? readFileSync(p, "utf8") : "";
|
|
12
|
+
} catch {
|
|
13
|
+
return "";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function resolveBundlePkg(b, profileDir, home) {
|
|
18
|
+
const candidates = [
|
|
19
|
+
join(profileDir, "node_modules", b, "package.json"),
|
|
20
|
+
join(home, "profiles", "node_modules", b, "package.json")
|
|
21
|
+
];
|
|
22
|
+
if (b.startsWith("@")) {
|
|
23
|
+
const [scope, name] = b.split("/");
|
|
24
|
+
candidates.push(join(home, "profiles", "node_modules", scope, name, "package.json"));
|
|
25
|
+
}
|
|
26
|
+
for (const c of candidates) if (existsSync(c)) return c;
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** 从工具参数/命令中提取目标 profile 名(缺省 web) */
|
|
31
|
+
export function profileNameFromArgs(args = {}) {
|
|
32
|
+
const p = String(args.file_path || args.path || "");
|
|
33
|
+
const pm = p.match(/profiles[\\/]([^\\/]+)[\\/]/i);
|
|
34
|
+
if (pm) return pm[1];
|
|
35
|
+
if (args.profile) return String(args.profile);
|
|
36
|
+
const cmd = String(args.command || args.code || "");
|
|
37
|
+
let idx = cmd.indexOf("--profile");
|
|
38
|
+
if (idx >= 0) {
|
|
39
|
+
const rest = cmd.slice(idx + "--profile".length).trim();
|
|
40
|
+
const val = rest.replace(/^=/, "").split(/\s+/)[0];
|
|
41
|
+
if (val) return val;
|
|
42
|
+
}
|
|
43
|
+
return "web";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 计算装配状态哈希;profile 缺失时返回固定 missing 标记,避免误伤 */
|
|
47
|
+
export function computeMountSignature(profileName = "web") {
|
|
48
|
+
const home = dshHome();
|
|
49
|
+
const profileDir = join(home, "profiles", profileName);
|
|
50
|
+
const pkgPath = join(profileDir, "package.json");
|
|
51
|
+
if (!existsSync(pkgPath)) return `missing-profile:${profileName}`;
|
|
52
|
+
const hash = createHash("sha256");
|
|
53
|
+
let pkg;
|
|
54
|
+
try {
|
|
55
|
+
pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
56
|
+
} catch (e) {
|
|
57
|
+
return `profile-parse-error:${profileName}:${e instanceof Error ? e.message : String(e)}`;
|
|
58
|
+
}
|
|
59
|
+
const bundles = pkg?.dsh?.profile?.bundles || [];
|
|
60
|
+
const deps = pkg?.dependencies || {};
|
|
61
|
+
hash.update("bundles:" + JSON.stringify(bundles));
|
|
62
|
+
// 只把会影响装配类型判断的本地依赖纳入哈希;普通 registry 版本变化不触发重审计
|
|
63
|
+
const localDeps = {};
|
|
64
|
+
for (const [name, spec] of Object.entries(deps)) {
|
|
65
|
+
if (typeof spec === "string" && /^(link:|file:|github:|http)/i.test(spec)) localDeps[name] = spec;
|
|
66
|
+
}
|
|
67
|
+
hash.update("localDeps:" + JSON.stringify(localDeps));
|
|
68
|
+
hash.update("profilePatch:" + tryRead(join(profileDir, "cordis.patch.yml")));
|
|
69
|
+
hash.update("rootPatch:" + tryRead(join(home, "cordis.patch.yml")));
|
|
70
|
+
hash.update("injectRegistry:" + tryRead(join(home, "example-injector", "registry.json")));
|
|
71
|
+
for (const b of bundles) {
|
|
72
|
+
const bp = resolveBundlePkg(b, profileDir, home);
|
|
73
|
+
if (!bp) {
|
|
74
|
+
hash.update("bundleMissing:" + b);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const pkgB = JSON.parse(readFileSync(bp, "utf8"));
|
|
79
|
+
hash.update("bundlePkg:" + b + ":" + JSON.stringify({ version: pkgB.version, dsh: pkgB.dsh }));
|
|
80
|
+
const patchRel = pkgB?.dsh?.bundle?.patch;
|
|
81
|
+
if (patchRel) hash.update("bundlePatch:" + b + ":" + tryRead(join(dirname(bp), patchRel)));
|
|
82
|
+
} catch (e) {
|
|
83
|
+
hash.update("bundlePkgError:" + b + ":" + (e instanceof Error ? e.message : String(e)));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return hash.digest("hex");
|
|
87
|
+
}
|