@tokensapi/dsh-plugin-check 0.3.3 → 0.3.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/README.md +114 -95
- package/bin/dsh-plugin-check.mjs +6 -0
- package/docs/CHECKS.md +236 -0
- package/lib/contract.mjs +80 -0
- package/lib/cowork-plugin.mjs +118 -113
- package/lib/manifest-check.mjs +138 -139
- package/lib/registry-check.mjs +206 -205
- package/package.json +45 -44
package/lib/registry-check.mjs
CHANGED
|
@@ -1,205 +1,206 @@
|
|
|
1
|
-
/* ============================================================
|
|
2
|
-
* 已发布包体检(注册表版,零依赖纯函数)
|
|
3
|
-
* ============================================================
|
|
4
|
-
* 对 npm registry 的版本文档跑清单规则,与目录体检(manifest-check)
|
|
5
|
-
* 同一套语义,但不需要文件系统 —— 供 Cowork 插件进程内使用。
|
|
6
|
-
*
|
|
7
|
-
* 两个入口:
|
|
8
|
-
* - checkPublishedManifest 纯同步零依赖,只看清单字段;
|
|
9
|
-
* - checkPublishedPackage 额外取 tarball 读出 cordis.patch.yml,
|
|
10
|
-
* 补上 M4/M5 —— 只看清单会漏掉"补丁行 name 写成插件名"这类
|
|
11
|
-
* 装上即让整棵插件树崩溃的缺陷。
|
|
12
|
-
* 冒烟阶段无法在宿主进程执行,完整体检走 CLI 或 Plugin Check
|
|
13
|
-
* workflow。
|
|
14
|
-
* ============================================================ */
|
|
15
|
-
import { checkPatchContent } from './patch-rows.mjs'
|
|
16
|
-
import { fetchTarEntry } from './tarball.mjs'
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const isIdentityCore = (name) =>
|
|
20
|
-
name === '@deepseek-ai/cordis' || name.startsWith('@deepseek-ai/cordis-') || name.startsWith('@deepseek-ai/dsh')
|
|
21
|
-
|
|
22
|
-
/* ------------------------- 迷你 SemVer ------------------------- */
|
|
23
|
-
// 只实现体检需要的子集:比较 + 范围判定(空格=且、||=或、^、~、
|
|
24
|
-
// >=、>、<=、<、=、裸版本),预发布参与比较;识别不了的范围返回
|
|
25
|
-
// null 由调用方按"范围不合法"处理。
|
|
26
|
-
|
|
27
|
-
function parseVersion(input) {
|
|
28
|
-
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/u.exec(String(input).trim())
|
|
29
|
-
if (!match) return null
|
|
30
|
-
return {
|
|
31
|
-
parts: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
32
|
-
prerelease: match[4] === undefined ? [] : match[4].split('.'),
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function compareVersions(a, b) {
|
|
37
|
-
for (let index = 0; index < 3; index += 1) {
|
|
38
|
-
if (a.parts[index] !== b.parts[index]) return a.parts[index] < b.parts[index] ? -1 : 1
|
|
39
|
-
}
|
|
40
|
-
if (a.prerelease.length === 0 && b.prerelease.length === 0) return 0
|
|
41
|
-
if (a.prerelease.length === 0) return 1
|
|
42
|
-
if (b.prerelease.length === 0) return -1
|
|
43
|
-
const length = Math.max(a.prerelease.length, b.prerelease.length)
|
|
44
|
-
for (let index = 0; index < length; index += 1) {
|
|
45
|
-
const left = a.prerelease[index]
|
|
46
|
-
const right = b.prerelease[index]
|
|
47
|
-
if (left === undefined) return -1
|
|
48
|
-
if (right === undefined) return 1
|
|
49
|
-
const leftNumeric = /^\d+$/u.test(left)
|
|
50
|
-
const rightNumeric = /^\d+$/u.test(right)
|
|
51
|
-
if (leftNumeric && rightNumeric) {
|
|
52
|
-
if (Number(left) !== Number(right)) return Number(left) < Number(right) ? -1 : 1
|
|
53
|
-
} else if (leftNumeric !== rightNumeric) {
|
|
54
|
-
return leftNumeric ? -1 : 1
|
|
55
|
-
} else if (left !== right) {
|
|
56
|
-
return left < right ? -1 : 1
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return 0
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function comparatorHolds(version, operator, boundary) {
|
|
63
|
-
const cmp = compareVersions(version, boundary)
|
|
64
|
-
if (operator === '>') return cmp > 0
|
|
65
|
-
if (operator === '>=') return cmp >= 0
|
|
66
|
-
if (operator === '<') return cmp < 0
|
|
67
|
-
if (operator === '<=') return cmp <= 0
|
|
68
|
-
return cmp === 0
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function versionSatisfies(versionInput, rangeInput) {
|
|
72
|
-
const version = parseVersion(versionInput)
|
|
73
|
-
if (version === null) return null
|
|
74
|
-
const alternatives = String(rangeInput).split('||')
|
|
75
|
-
let recognized = false
|
|
76
|
-
for (const alternative of alternatives) {
|
|
77
|
-
const comparators = alternative.trim().split(/\s+/u).filter(Boolean)
|
|
78
|
-
if (comparators.length === 0) continue
|
|
79
|
-
let holds = true
|
|
80
|
-
let valid = true
|
|
81
|
-
for (const comparator of comparators) {
|
|
82
|
-
if (comparator === '*' || comparator === 'x') continue
|
|
83
|
-
const match = /^(>=|<=|>|<|=|\^|~)?(.+)$/u.exec(comparator)
|
|
84
|
-
const boundary = parseVersion(match[2])
|
|
85
|
-
if (boundary === null) { valid = false; break }
|
|
86
|
-
const operator = match[1] ?? '='
|
|
87
|
-
if (operator === '^') {
|
|
88
|
-
const upper = boundary.parts[0] > 0
|
|
89
|
-
? { parts: [boundary.parts[0] + 1, 0, 0], prerelease: ['0'] }
|
|
90
|
-
: boundary.parts[1] > 0
|
|
91
|
-
? { parts: [0, boundary.parts[1] + 1, 0], prerelease: ['0'] }
|
|
92
|
-
: { parts: [0, 0, boundary.parts[2] + 1], prerelease: ['0'] }
|
|
93
|
-
holds = holds && comparatorHolds(version, '>=', boundary) && comparatorHolds(version, '<', upper)
|
|
94
|
-
} else if (operator === '~') {
|
|
95
|
-
const upper = { parts: [boundary.parts[0], boundary.parts[1] + 1, 0], prerelease: ['0'] }
|
|
96
|
-
holds = holds && comparatorHolds(version, '>=', boundary) && comparatorHolds(version, '<', upper)
|
|
97
|
-
} else {
|
|
98
|
-
holds = holds && comparatorHolds(version, operator, boundary)
|
|
99
|
-
}
|
|
100
|
-
if (!holds) break
|
|
101
|
-
}
|
|
102
|
-
if (!valid) continue
|
|
103
|
-
recognized = true
|
|
104
|
-
if (holds) return true
|
|
105
|
-
}
|
|
106
|
-
return recognized ? false : null
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/* --------------------------- 规则 --------------------------- */
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* 对一份 npm 版本清单跑体检规则。
|
|
113
|
-
* @param {object} manifest - registry 版本文档(scripts/dependencies/
|
|
114
|
-
* peerDependencies/dsh/engines/license/version)。
|
|
115
|
-
* @param {string} runtime - 目标 DSH 运行时版本。
|
|
116
|
-
* @returns {Array<{level: 'error'|'warning', rule: string, message: string}>}
|
|
117
|
-
*/
|
|
118
|
-
export function checkPublishedManifest(manifest, runtime) {
|
|
119
|
-
const findings = []
|
|
120
|
-
const error = (rule, message) => findings.push({ level: 'error', rule, message })
|
|
121
|
-
const warning = (rule, message) => findings.push({ level: 'warning', rule, message })
|
|
122
|
-
|
|
123
|
-
const scripts = manifest.scripts ?? {}
|
|
124
|
-
for (const script of
|
|
125
|
-
if (typeof scripts[script] === 'string')
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (satisfied
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (satisfied
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (manifest.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
* @param {
|
|
174
|
-
* @param {
|
|
175
|
-
* @
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
1
|
+
/* ============================================================
|
|
2
|
+
* 已发布包体检(注册表版,零依赖纯函数)
|
|
3
|
+
* ============================================================
|
|
4
|
+
* 对 npm registry 的版本文档跑清单规则,与目录体检(manifest-check)
|
|
5
|
+
* 同一套语义,但不需要文件系统 —— 供 Cowork 插件进程内使用。
|
|
6
|
+
*
|
|
7
|
+
* 两个入口:
|
|
8
|
+
* - checkPublishedManifest 纯同步零依赖,只看清单字段;
|
|
9
|
+
* - checkPublishedPackage 额外取 tarball 读出 cordis.patch.yml,
|
|
10
|
+
* 补上 M4/M5 —— 只看清单会漏掉"补丁行 name 写成插件名"这类
|
|
11
|
+
* 装上即让整棵插件树崩溃的缺陷。
|
|
12
|
+
* 冒烟阶段无法在宿主进程执行,完整体检走 CLI 或 Plugin Check
|
|
13
|
+
* workflow。
|
|
14
|
+
* ============================================================ */
|
|
15
|
+
import { checkPatchContent } from './patch-rows.mjs'
|
|
16
|
+
import { fetchTarEntry } from './tarball.mjs'
|
|
17
|
+
import { LIFECYCLE_SCRIPTS, lifecycleMessage } from './contract.mjs'
|
|
18
|
+
|
|
19
|
+
const isIdentityCore = (name) =>
|
|
20
|
+
name === '@deepseek-ai/cordis' || name.startsWith('@deepseek-ai/cordis-') || name.startsWith('@deepseek-ai/dsh')
|
|
21
|
+
|
|
22
|
+
/* ------------------------- 迷你 SemVer ------------------------- */
|
|
23
|
+
// 只实现体检需要的子集:比较 + 范围判定(空格=且、||=或、^、~、
|
|
24
|
+
// >=、>、<=、<、=、裸版本),预发布参与比较;识别不了的范围返回
|
|
25
|
+
// null 由调用方按"范围不合法"处理。
|
|
26
|
+
|
|
27
|
+
function parseVersion(input) {
|
|
28
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/u.exec(String(input).trim())
|
|
29
|
+
if (!match) return null
|
|
30
|
+
return {
|
|
31
|
+
parts: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
32
|
+
prerelease: match[4] === undefined ? [] : match[4].split('.'),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function compareVersions(a, b) {
|
|
37
|
+
for (let index = 0; index < 3; index += 1) {
|
|
38
|
+
if (a.parts[index] !== b.parts[index]) return a.parts[index] < b.parts[index] ? -1 : 1
|
|
39
|
+
}
|
|
40
|
+
if (a.prerelease.length === 0 && b.prerelease.length === 0) return 0
|
|
41
|
+
if (a.prerelease.length === 0) return 1
|
|
42
|
+
if (b.prerelease.length === 0) return -1
|
|
43
|
+
const length = Math.max(a.prerelease.length, b.prerelease.length)
|
|
44
|
+
for (let index = 0; index < length; index += 1) {
|
|
45
|
+
const left = a.prerelease[index]
|
|
46
|
+
const right = b.prerelease[index]
|
|
47
|
+
if (left === undefined) return -1
|
|
48
|
+
if (right === undefined) return 1
|
|
49
|
+
const leftNumeric = /^\d+$/u.test(left)
|
|
50
|
+
const rightNumeric = /^\d+$/u.test(right)
|
|
51
|
+
if (leftNumeric && rightNumeric) {
|
|
52
|
+
if (Number(left) !== Number(right)) return Number(left) < Number(right) ? -1 : 1
|
|
53
|
+
} else if (leftNumeric !== rightNumeric) {
|
|
54
|
+
return leftNumeric ? -1 : 1
|
|
55
|
+
} else if (left !== right) {
|
|
56
|
+
return left < right ? -1 : 1
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return 0
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function comparatorHolds(version, operator, boundary) {
|
|
63
|
+
const cmp = compareVersions(version, boundary)
|
|
64
|
+
if (operator === '>') return cmp > 0
|
|
65
|
+
if (operator === '>=') return cmp >= 0
|
|
66
|
+
if (operator === '<') return cmp < 0
|
|
67
|
+
if (operator === '<=') return cmp <= 0
|
|
68
|
+
return cmp === 0
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function versionSatisfies(versionInput, rangeInput) {
|
|
72
|
+
const version = parseVersion(versionInput)
|
|
73
|
+
if (version === null) return null
|
|
74
|
+
const alternatives = String(rangeInput).split('||')
|
|
75
|
+
let recognized = false
|
|
76
|
+
for (const alternative of alternatives) {
|
|
77
|
+
const comparators = alternative.trim().split(/\s+/u).filter(Boolean)
|
|
78
|
+
if (comparators.length === 0) continue
|
|
79
|
+
let holds = true
|
|
80
|
+
let valid = true
|
|
81
|
+
for (const comparator of comparators) {
|
|
82
|
+
if (comparator === '*' || comparator === 'x') continue
|
|
83
|
+
const match = /^(>=|<=|>|<|=|\^|~)?(.+)$/u.exec(comparator)
|
|
84
|
+
const boundary = parseVersion(match[2])
|
|
85
|
+
if (boundary === null) { valid = false; break }
|
|
86
|
+
const operator = match[1] ?? '='
|
|
87
|
+
if (operator === '^') {
|
|
88
|
+
const upper = boundary.parts[0] > 0
|
|
89
|
+
? { parts: [boundary.parts[0] + 1, 0, 0], prerelease: ['0'] }
|
|
90
|
+
: boundary.parts[1] > 0
|
|
91
|
+
? { parts: [0, boundary.parts[1] + 1, 0], prerelease: ['0'] }
|
|
92
|
+
: { parts: [0, 0, boundary.parts[2] + 1], prerelease: ['0'] }
|
|
93
|
+
holds = holds && comparatorHolds(version, '>=', boundary) && comparatorHolds(version, '<', upper)
|
|
94
|
+
} else if (operator === '~') {
|
|
95
|
+
const upper = { parts: [boundary.parts[0], boundary.parts[1] + 1, 0], prerelease: ['0'] }
|
|
96
|
+
holds = holds && comparatorHolds(version, '>=', boundary) && comparatorHolds(version, '<', upper)
|
|
97
|
+
} else {
|
|
98
|
+
holds = holds && comparatorHolds(version, operator, boundary)
|
|
99
|
+
}
|
|
100
|
+
if (!holds) break
|
|
101
|
+
}
|
|
102
|
+
if (!valid) continue
|
|
103
|
+
recognized = true
|
|
104
|
+
if (holds) return true
|
|
105
|
+
}
|
|
106
|
+
return recognized ? false : null
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* --------------------------- 规则 --------------------------- */
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 对一份 npm 版本清单跑体检规则。
|
|
113
|
+
* @param {object} manifest - registry 版本文档(scripts/dependencies/
|
|
114
|
+
* peerDependencies/dsh/engines/license/version)。
|
|
115
|
+
* @param {string} runtime - 目标 DSH 运行时版本。
|
|
116
|
+
* @returns {Array<{level: 'error'|'warning', rule: string, message: string}>}
|
|
117
|
+
*/
|
|
118
|
+
export function checkPublishedManifest(manifest, runtime) {
|
|
119
|
+
const findings = []
|
|
120
|
+
const error = (rule, message) => findings.push({ level: 'error', rule, message })
|
|
121
|
+
const warning = (rule, message) => findings.push({ level: 'warning', rule, message })
|
|
122
|
+
|
|
123
|
+
const scripts = manifest.scripts ?? {}
|
|
124
|
+
for (const script of LIFECYCLE_SCRIPTS) {
|
|
125
|
+
if (typeof scripts[script] === 'string') warning('M2-lifecycle', lifecycleMessage(script))
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (const name of Object.keys(manifest.dependencies ?? {})) {
|
|
129
|
+
if (isIdentityCore(name)) {
|
|
130
|
+
error('M3-core-peer', `${name} 出现在 dependencies;核心运行时必须是 peerDependencies,否则双内核实例会让 Cowork 启动失败`)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const bundle = manifest.dsh?.bundle ?? {}
|
|
135
|
+
if (typeof bundle.patch !== 'string' || bundle.patch === '') {
|
|
136
|
+
error('M4-bundle', 'dsh.bundle.patch 缺失;宿主靠它把插件挂进加载树')
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const engine = typeof manifest.dsh?.engine === 'string' ? manifest.dsh.engine : undefined
|
|
140
|
+
if (engine === undefined) {
|
|
141
|
+
warning('M6-engine', 'dsh.engine 未声明(目标运行时 SemVer 范围);'
|
|
142
|
+
+ '上游目前不强制读取该字段,但声明之后体检才能判断你与目标运行时是否相容')
|
|
143
|
+
} else {
|
|
144
|
+
const satisfied = versionSatisfies(runtime, engine)
|
|
145
|
+
if (satisfied === null) error('M6-engine', `dsh.engine 不是可识别的 SemVer 范围: ${engine}`)
|
|
146
|
+
else if (!satisfied) error('M6-engine', `dsh.engine "${engine}" 不覆盖当前运行时 ${runtime}`)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
for (const [name, range] of Object.entries(manifest.peerDependencies ?? {})) {
|
|
150
|
+
if (!name.startsWith('@deepseek-ai/dsh')) continue
|
|
151
|
+
const satisfied = versionSatisfies(runtime, String(range))
|
|
152
|
+
if (satisfied === null) error('M7-peer-range', `peer ${name} 的范围不可识别: ${range}`)
|
|
153
|
+
else if (!satisfied) warning('M7-peer-range', `peer ${name}@"${range}" 不含当前运行时 ${runtime};宿主升级后可能运行时断裂`)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (manifest.engines?.node === undefined) warning('M8-node-engines', 'engines.node 未声明;宿主运行在 Node 22+')
|
|
157
|
+
if (manifest.license === undefined) warning('M9-license', 'license 未声明')
|
|
158
|
+
|
|
159
|
+
const parsed = parseVersion(typeof manifest.version === 'string' ? manifest.version : '')
|
|
160
|
+
if (parsed !== null && parsed.prerelease.length > 0) {
|
|
161
|
+
warning('M1-manifest', `version ${manifest.version} 是预发布版;`
|
|
162
|
+
+ `市场受控安装只认稳定的精确版(上游 stableExactVersion),npm latest 停在预发布版时`
|
|
163
|
+
+ `用户端只会看到手动安装命令。预发布请发在 next 之类的 dist-tag 上`)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return findings
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 已发布包的完整清单侧体检:清单规则 + 包内补丁行检查。
|
|
171
|
+
* 取不到 tarball(离线、超时、体积过大)时降级为 warning 并如实
|
|
172
|
+
* 标注未检查,绝不因网络问题把插件判成不合格。
|
|
173
|
+
* @param {object} manifest - registry 版本文档(需含 dist.tarball)。
|
|
174
|
+
* @param {string} runtime - 目标 DSH 运行时版本。
|
|
175
|
+
* @param {{ maxBytes?: number, timeoutMs?: number }} [options]
|
|
176
|
+
* @returns {Promise<{ findings: object[], inspectedPatch: boolean }>}
|
|
177
|
+
*/
|
|
178
|
+
export async function checkPublishedPackage(manifest, runtime, options = {}) {
|
|
179
|
+
const findings = checkPublishedManifest(manifest, runtime)
|
|
180
|
+
const patch = typeof manifest.dsh?.bundle?.patch === 'string' ? manifest.dsh.bundle.patch : ''
|
|
181
|
+
const tarball = typeof manifest.dist?.tarball === 'string' ? manifest.dist.tarball : ''
|
|
182
|
+
// 补丁未声明时 M4 已在清单阶段报错,无需再取包。
|
|
183
|
+
if (patch === '' || tarball === '') return { findings, inspectedPatch: false }
|
|
184
|
+
|
|
185
|
+
const relative = patch.replace(/^\.?\//u, '')
|
|
186
|
+
const result = await fetchTarEntry(tarball, entry => entry === relative, options)
|
|
187
|
+
if (result.error !== undefined) {
|
|
188
|
+
findings.push({
|
|
189
|
+
level: 'warning',
|
|
190
|
+
rule: 'M4-bundle',
|
|
191
|
+
message: `未能读取包内 ${relative}(${result.error});本次跳过补丁行检查,完整体检请走 CLI`,
|
|
192
|
+
})
|
|
193
|
+
return { findings, inspectedPatch: false }
|
|
194
|
+
}
|
|
195
|
+
if (result.content === undefined) {
|
|
196
|
+
findings.push({
|
|
197
|
+
level: 'error',
|
|
198
|
+
rule: 'M4-bundle',
|
|
199
|
+
message: `dsh.bundle.patch 指向的文件不在发布内容里: ${patch};宿主取不到补丁就挂不上插件(检查 files 白名单)`,
|
|
200
|
+
})
|
|
201
|
+
return { findings, inspectedPatch: true }
|
|
202
|
+
}
|
|
203
|
+
const packageName = typeof manifest.name === 'string' ? manifest.name : ''
|
|
204
|
+
findings.push(...checkPatchContent(packageName, result.content).findings)
|
|
205
|
+
return { findings, inspectedPatch: true }
|
|
206
|
+
}
|
package/package.json
CHANGED
|
@@ -1,44 +1,45 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@tokensapi/dsh-plugin-check",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "TokensCowork/DSH
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"dsh-plugin-check": "bin/dsh-plugin-check.mjs"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"bin",
|
|
11
|
-
"lib",
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"@deepseek-ai/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@tokensapi/dsh-plugin-check",
|
|
3
|
+
"version": "0.3.4",
|
|
4
|
+
"description": "TokensCowork/DSH 插件合格性体检:CLI 一条命令判定插件能否安全装入宿主;装入 Cowork 后提供会话内 plugin_check 工具。",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dsh-plugin-check": "bin/dsh-plugin-check.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"lib",
|
|
12
|
+
"docs",
|
|
13
|
+
"cordis.patch.yml",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "node --test \"tests/*.test.mjs\""
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22.0.0"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"js-yaml": "^4.1.0",
|
|
24
|
+
"semver": "^7.6.0"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/TokensAPI/tokens_DshPluginCheck_code.git"
|
|
30
|
+
},
|
|
31
|
+
"main": "lib/cowork-plugin.mjs",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": "./lib/cowork-plugin.mjs"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@deepseek-ai/cordis": "4.0.1 || 4.0.2",
|
|
37
|
+
"@deepseek-ai/dsh-tools": "0.1.0-rc.8 || 0.1.3-alpha.1"
|
|
38
|
+
},
|
|
39
|
+
"dsh": {
|
|
40
|
+
"engine": ">=0.1.0-rc.8 <0.2.0",
|
|
41
|
+
"bundle": {
|
|
42
|
+
"patch": "./cordis.patch.yml"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|