@bushizhanghan/dsh-publish-gate 0.7.1 → 0.8.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/CHANGELOG.md +17 -0
- package/README.md +2 -1
- package/lib/engine.js +83 -2
- package/lib/index.js +31 -4
- package/package.json +52 -52
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.1
|
|
4
|
+
|
|
5
|
+
- `publish_gate_verify` gained `waitMs` (capped at 90 s) and reports `waitedMs`. npm answers 202 and keeps
|
|
6
|
+
the version invisible for 60–180 s after a publish, so the single-shot check reported "not on the registry"
|
|
7
|
+
for releases that had already succeeded — it happened on this plugin own 0.8.0 publish, which stayed
|
|
8
|
+
invisible for over three minutes. `waitForPublished()` polls until the shasum appears; `waitMs: 0` keeps
|
|
9
|
+
the old single-shot behaviour, and a real mismatch stops the poll immediately.
|
|
10
|
+
## 0.8.0
|
|
11
|
+
|
|
12
|
+
- `tool-input-throws` (error): `publish_gate_tools` gained `fuzz` and `skip`. With `fuzz: true` it drives
|
|
13
|
+
every declared parameter with a wrong-typed value and reports the tools that throw instead of answering.
|
|
14
|
+
The host never validates a call against `parameters`, so those values really arrive — this rule exists
|
|
15
|
+
because the same sweep found 12 real crashes across this workspace's own plugins (a number reaching
|
|
16
|
+
`path.join` via `session`, `profile`, `casePath` and `workspace`), all of which handed the caller a raw
|
|
17
|
+
Node TypeError. Only a throw counts: a filter that ignores a number and returns the unfiltered list still
|
|
18
|
+
answers, and every optional boolean treats any truthy value the same way.
|
|
19
|
+
- One finding per tool+parameter, not one per wrong value.
|
|
3
20
|
## 0.7.1
|
|
4
21
|
|
|
5
22
|
- Fixed: a non-string `dir` made every path tool throw a raw Node `TypeError`
|
package/README.md
CHANGED
|
@@ -106,6 +106,7 @@ dsh plugin --profile desktop add @bushizhanghan/dsh-publish-gate
|
|
|
106
106
|
| `tool-bad-parameter-type` | `parameters` 里某个 `type` 写错(`"strin"`)。宿主把 `parameters` 渲染进 prompt、但**从不校验**调用值,所以这个 typo 只是悄悄把类型提示弄丢 |
|
|
107
107
|
| `tool-undeclared-param` | `execute` 读了 `args.x`,但 `parameters.properties` 里没有 `x`。模型只能传 schema 声明过的参数,所以这个值在真实调用里永远是 `undefined`——代码在,永远不跑,也不报错。`args.x`、`args['x']`、`const { x } = args` 三种写法都认 |
|
|
108
108
|
| `tool-output-invalid` | 工具返回的东西**自己的 output schema 不收**。宿主每个结果都拿 schema 校验,不匹配就抛 `ToolOutputError`——工具从第一次调用起就不可用,而报错只给一个 JSON 路径,不给是哪行代码返回的。只有点名 `call` 真的调了才检查。这个插件自己踩过:schema 收紧成 `additionalProperties: false`,但有一条错误分支还在返回 `{ ok, error }` |
|
|
109
|
+
| `tool-input-throws` | 把每个声明过的参数**塞一个错类型的值**去调,工具直接抛 `TypeError` 而不是回答。宿主从不拿 `parameters` 校验入参,所以这个值真的会到你的代码里——调用方拿到的是 `The "path" argument must be of type string`,插件日志里没有一行说是哪个工具。这条规则是在自己的工作区里跑出 **12 个真实崩溃** 之后才加的(`session`/`profile`/`casePath`/`workspace` 各一个入口),现在九个插件全绿。默认关:它会把每个工具都调一遍,用 `skip` 点名排除那些会开程序、写文件的 |
|
|
109
110
|
|
|
110
111
|
### 浏览器半边:侧栏面板到底会不会出现
|
|
111
112
|
|
|
@@ -214,7 +215,7 @@ dsh plugin --profile desktop add @bushizhanghan/dsh-publish-gate
|
|
|
214
215
|
## 开发
|
|
215
216
|
|
|
216
217
|
```sh
|
|
217
|
-
node --test #
|
|
218
|
+
node --test # 92 个引擎单测
|
|
218
219
|
node test/integration.test.mjs # 27 条集成断言(9 个真实插件作 fixture)
|
|
219
220
|
```
|
|
220
221
|
|
package/lib/engine.js
CHANGED
|
@@ -1198,6 +1198,7 @@ ctx.tools = {
|
|
|
1198
1198
|
hasExecute: typeof def?.execute === 'function',
|
|
1199
1199
|
hasOutputSchema: Boolean(def && def.output && def.output.schema),
|
|
1200
1200
|
schema: (def && def.output && def.output.schema) || null,
|
|
1201
|
+
parameters: (def && def.parameters) || null,
|
|
1201
1202
|
cardKind: card,
|
|
1202
1203
|
undeclaredParams: undeclaredArgs(def),
|
|
1203
1204
|
run: typeof def?.execute === 'function' ? def.execute : null,
|
|
@@ -1278,7 +1279,45 @@ try {
|
|
|
1278
1279
|
calls.push({ name: entry.name, ok: false, timedOut: message === 'timed out', error: message })
|
|
1279
1280
|
}
|
|
1280
1281
|
}
|
|
1281
|
-
|
|
1282
|
+
// The host never validates tool input against \`parameters\`, so a number really
|
|
1283
|
+
// does reach a path argument. Fuzzing every declared parameter with the wrong
|
|
1284
|
+
// type found 12 crashes across this workspace's own plugins — each one a raw
|
|
1285
|
+
// TypeError handed to the caller instead of an answer.
|
|
1286
|
+
stage = 'fuzz'
|
|
1287
|
+
const fuzzIssues = []
|
|
1288
|
+
if (process.env.PG_FUZZ === '1') {
|
|
1289
|
+
const skip = new Set(JSON.parse(process.env.PG_FUZZ_SKIP || '[]'))
|
|
1290
|
+
const WRONG = {
|
|
1291
|
+
string: [123, true, { a: 1 }, [1]],
|
|
1292
|
+
boolean: ['yes', 1, {}],
|
|
1293
|
+
number: ['n', {}, []],
|
|
1294
|
+
array: ['x', 1, {}],
|
|
1295
|
+
object: ['x', 1, []],
|
|
1296
|
+
}
|
|
1297
|
+
for (const entry of registered) {
|
|
1298
|
+
if (!entry.run || skip.has(entry.name)) continue
|
|
1299
|
+
const props = entry.parameters && entry.parameters.properties
|
|
1300
|
+
if (!props || typeof props !== 'object') continue
|
|
1301
|
+
for (const [param, spec] of Object.entries(props)) {
|
|
1302
|
+
for (const bad of WRONG[spec && spec.type] || [123]) {
|
|
1303
|
+
try {
|
|
1304
|
+
await Promise.race([
|
|
1305
|
+
Promise.resolve(entry.run({ [param]: bad }, { signal: undefined })),
|
|
1306
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('timed out')), budget)),
|
|
1307
|
+
])
|
|
1308
|
+
} catch (error) {
|
|
1309
|
+
// Only a throw is a defect. A filter parameter that ignores a number
|
|
1310
|
+
// and returns the unfiltered list still answers, and every optional
|
|
1311
|
+
// boolean treats any truthy value the same way.
|
|
1312
|
+
const message = String((error && error.message) || error)
|
|
1313
|
+
if (message === 'timed out') continue
|
|
1314
|
+
fuzzIssues.push({ tool: entry.name, param, value: JSON.stringify(bad), problem: message.slice(0, 200) })
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
emit({ ok: true, tools: registered.map(({ run, ...rest }) => rest), badParamTypes, calls, fuzzIssues })
|
|
1282
1321
|
} catch (error) {
|
|
1283
1322
|
emit({ ok: false, stage, error: String((error && error.message) || error) })
|
|
1284
1323
|
}
|
|
@@ -1297,6 +1336,8 @@ export async function checkToolRegistrations(dir, opts = {}) {
|
|
|
1297
1336
|
...process.env,
|
|
1298
1337
|
PG_CALLS: JSON.stringify(Array.isArray(opts.calls) ? opts.calls : []),
|
|
1299
1338
|
PG_CALL_MS: String(opts.callTimeoutMs ?? 8000),
|
|
1339
|
+
PG_FUZZ: opts.fuzz ? '1' : '0',
|
|
1340
|
+
PG_FUZZ_SKIP: JSON.stringify(Array.isArray(opts.fuzzSkip) ? opts.fuzzSkip : []),
|
|
1300
1341
|
},
|
|
1301
1342
|
})
|
|
1302
1343
|
const line = String(stdout).split('\n').filter((l) => l.startsWith('@@PG@@')).pop()
|
|
@@ -1428,6 +1469,23 @@ export async function checkToolRegistrations(dir, opts = {}) {
|
|
|
1428
1469
|
),
|
|
1429
1470
|
)
|
|
1430
1471
|
}
|
|
1472
|
+
// One finding per tool+parameter: the first wrong value is enough to prove the
|
|
1473
|
+
// guard is missing, and four findings for the same line is noise.
|
|
1474
|
+
const fuzzSeen = new Set()
|
|
1475
|
+
for (const issue of Array.isArray(payload.fuzzIssues) ? payload.fuzzIssues : []) {
|
|
1476
|
+
const key = `${issue.tool}\u0000${issue.param}`
|
|
1477
|
+
if (fuzzSeen.has(key)) continue
|
|
1478
|
+
fuzzSeen.add(key)
|
|
1479
|
+
out.push(
|
|
1480
|
+
F(
|
|
1481
|
+
'tool-input-throws',
|
|
1482
|
+
'error',
|
|
1483
|
+
`${issue.tool}(${issue.param}: ${issue.value}) — ${issue.problem}`,
|
|
1484
|
+
'The host never validates a call against parameters, so this really reaches your code with the wrong type. The caller gets a raw Node TypeError instead of an answer, and nothing in the plugin log says which tool was at fault.',
|
|
1485
|
+
`Guard the top of ${issue.tool}: if (typeof args.${issue.param} !== 'string') return { ok: false, error: '${issue.param} must be a string' }.`,
|
|
1486
|
+
),
|
|
1487
|
+
)
|
|
1488
|
+
}
|
|
1431
1489
|
return { findings: out, tools }
|
|
1432
1490
|
}
|
|
1433
1491
|
|
|
@@ -1858,6 +1916,29 @@ export async function checkPublishedMatches(name, version, localPack, { registry
|
|
|
1858
1916
|
return out
|
|
1859
1917
|
}
|
|
1860
1918
|
|
|
1919
|
+
/**
|
|
1920
|
+
* Poll the registry until the published bytes show up.
|
|
1921
|
+
*
|
|
1922
|
+
* npm answers 202 and keeps the version invisible for 60–180 s, so a single
|
|
1923
|
+
* shot right after `npm publish` reports "not on the registry" for a release
|
|
1924
|
+
* that already succeeded — the most common false alarm this plugin produces,
|
|
1925
|
+
* including on its own publishes. `probe` is injectable so the loop tests
|
|
1926
|
+
* without the network.
|
|
1927
|
+
*/
|
|
1928
|
+
export async function waitForPublished(name, version, localPack, opts = {}) {
|
|
1929
|
+
const waitMs = Math.max(0, Number(opts.waitMs) || 0)
|
|
1930
|
+
const intervalMs = Math.max(1000, Number(opts.intervalMs) || 10_000)
|
|
1931
|
+
const probe = opts.probe || ((o) => checkPublishedMatches(name, version, localPack, o))
|
|
1932
|
+
const net = { registry: opts.registry, timeoutMs: opts.timeoutMs }
|
|
1933
|
+
const started = Date.now()
|
|
1934
|
+
let findings = await probe(net)
|
|
1935
|
+
while (!findings.some((f) => f.rule === 'publish-served') && Date.now() - started < waitMs) {
|
|
1936
|
+
await new Promise((r) => setTimeout(r, Math.min(intervalMs, waitMs - (Date.now() - started))))
|
|
1937
|
+
findings = await probe(net)
|
|
1938
|
+
}
|
|
1939
|
+
return { findings, waitedMs: Date.now() - started }
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1861
1942
|
/** The async half of the gate: everything that needs a subprocess or the network. */
|
|
1862
1943
|
export async function runArtifactChecks(dir, opts = {}) {
|
|
1863
1944
|
const packResult = opts.skipPack ? { findings: [], pack: null } : await checkPack(dir, opts)
|
|
@@ -2104,7 +2185,7 @@ const RULE_ORDER = [
|
|
|
2104
2185
|
'inject-missing-tools', 'inject-missing-web', 'inject-missing-webServer', 'inject-missing-timer',
|
|
2105
2186
|
'inject-unused-service',
|
|
2106
2187
|
'tool-missing-name', 'tool-duplicate-name', 'tool-missing-execute', 'tool-present-call-throws',
|
|
2107
|
-
'tool-bad-card-kind', 'tool-missing-output-schema', 'tool-none-registered', 'tool-bad-parameter-type', 'tool-undeclared-param', 'tool-call-throws', 'tool-output-invalid', 'tool-call-slow',
|
|
2188
|
+
'tool-bad-card-kind', 'tool-missing-output-schema', 'tool-none-registered', 'tool-bad-parameter-type', 'tool-undeclared-param', 'tool-input-throws', 'tool-call-throws', 'tool-output-invalid', 'tool-call-slow',
|
|
2108
2189
|
'lib-debugger', 'lib-console-log', 'lib-hardcoded-path', 'lib-url-pathname', 'node-too-old', 'stale-build', 'hand-edited-build',
|
|
2109
2190
|
'undeclared-import', 'dep-in-dev-only', 'unused-dependency',
|
|
2110
2191
|
'pack-omits-lib', 'pack-omits-patch', 'pack-omits-client', 'pack-oversize', 'pack-omits-readme', 'pack-omits-license', 'pack-ships-secret',
|
package/lib/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
checkClientRuntime,
|
|
25
25
|
checkPublishIdentity,
|
|
26
26
|
checkVersionTaken,
|
|
27
|
+
waitForPublished,
|
|
27
28
|
compareVersions,
|
|
28
29
|
latestPublished,
|
|
29
30
|
renderReport,
|
|
@@ -132,7 +133,7 @@ export function apply(ctx, config = {}) {
|
|
|
132
133
|
{
|
|
133
134
|
name: 'publish_gate_tools',
|
|
134
135
|
description:
|
|
135
|
-
'Import a plugin entry in an isolated child process, call apply(ctx) with a no-op context, and report what it actually registered. Catches the failures the manifest cannot show: the entry throws when imported, apply throws, a tool has no execute function, two tools share a name, presentCall throws, and a card kind the host does not render (the host only knows other/execute/search/read/edit/delete/fetch — anything else registers fine and renders a broken card). 20 s timeout, plugin code never runs in the host process. Returns {ok, tools:[{name,hasExecute,hasOutputSchema,cardKind}], findings, report}.',
|
|
136
|
+
'Import a plugin entry in an isolated child process, call apply(ctx) with a no-op context, and report what it actually registered. Catches the failures the manifest cannot show: the entry throws when imported, apply throws, a tool has no execute function, two tools share a name, presentCall throws, and a card kind the host does not render (the host only knows other/execute/search/read/edit/delete/fetch — anything else registers fine and renders a broken card). Optional deeper modes: `call` runs named tools with {} and validates each result against its own output schema; `fuzz` drives every declared parameter with a wrong-typed value and reports the tools that throw a raw TypeError instead of answering — the host never validates a call against parameters, so those values really arrive. 20 s timeout, plugin code never runs in the host process. Returns {ok, tools:[{name,hasExecute,hasOutputSchema,cardKind}], findings, report}.',
|
|
136
137
|
parameters: {
|
|
137
138
|
type: 'object',
|
|
138
139
|
properties: {
|
|
@@ -144,12 +145,27 @@ export function apply(ctx, config = {}) {
|
|
|
144
145
|
description:
|
|
145
146
|
'Tool names to actually call with empty arguments, e.g. ["page_watch_list"]. Off by default: some tools open applications or write files, so you name the ones you consider safe. A tool that throws on {} is a bug no other check can see, because registration never runs execute.',
|
|
146
147
|
},
|
|
148
|
+
fuzz: {
|
|
149
|
+
type: 'boolean',
|
|
150
|
+
description:
|
|
151
|
+
'Drive every declared parameter with wrong-typed values (a number where a string is declared, and so on) and report the tools that throw instead of answering. The host never validates a call against parameters, so these values really reach your code. Off by default because it calls every tool — pass skip for the ones that launch applications or write files.',
|
|
152
|
+
},
|
|
153
|
+
skip: {
|
|
154
|
+
type: 'array',
|
|
155
|
+
items: { type: 'string' },
|
|
156
|
+
description: 'Tool names the fuzz must not call, e.g. ["weapp_open","of_run","page_watch_add"].',
|
|
157
|
+
},
|
|
147
158
|
},
|
|
148
159
|
},
|
|
149
160
|
async execute(args) {
|
|
150
161
|
const dir = resolveDir(args.dir)
|
|
151
162
|
if (!dir) return { ok: false, error: 'no plugin directory given (dir must be a string path)' }
|
|
152
|
-
const { findings, tools } = await checkToolRegistrations(dir, {
|
|
163
|
+
const { findings, tools } = await checkToolRegistrations(dir, {
|
|
164
|
+
timeoutMs: args.timeoutMs,
|
|
165
|
+
calls: args.call,
|
|
166
|
+
fuzz: args.fuzz,
|
|
167
|
+
fuzzSkip: args.skip,
|
|
168
|
+
})
|
|
153
169
|
return { ok: true, tools, findings, report: renderReport(findings, { title: `runtime gate · ${dir}` }) }
|
|
154
170
|
},
|
|
155
171
|
},
|
|
@@ -174,12 +190,17 @@ export function apply(ctx, config = {}) {
|
|
|
174
190
|
{
|
|
175
191
|
name: 'publish_gate_verify',
|
|
176
192
|
description:
|
|
177
|
-
'Check that the tarball on the npm registry is the one in this directory. Runs `npm pack --dry-run` and compares its shasum with the registry dist.shasum — an exact comparison, not a version-number guess, so it catches "I bumped and forgot to publish" and "I published, then edited lib/". Reports publish-served (match), publish-served-mismatch (the version is spent and the registry holds different bytes), or publish-not-served (not published, or still inside the registry\'s
|
|
193
|
+
'Check that the tarball on the npm registry is the one in this directory. Runs `npm pack --dry-run` and compares its shasum with the registry dist.shasum — an exact comparison, not a version-number guess, so it catches "I bumped and forgot to publish" and "I published, then edited lib/". Reports publish-served (match), publish-served-mismatch (the version is spent and the registry holds different bytes), or publish-not-served (not published, or still inside the registry\'s 202-then-404 window — pass waitMs to poll through that window instead of concluding too early). Run it right after `npm publish`. Returns {ok, name, version, published, shasumMatch, localShasum, unpackedSize, files, waitedMs, findings, report}.',
|
|
178
194
|
parameters: {
|
|
179
195
|
type: 'object',
|
|
180
196
|
properties: {
|
|
181
197
|
dir: { type: 'string', description: 'Plugin directory.' },
|
|
182
198
|
version: { type: 'string', description: 'Version to verify (defaults to package.json version).' },
|
|
199
|
+
waitMs: {
|
|
200
|
+
type: 'number',
|
|
201
|
+
description:
|
|
202
|
+
'Poll until the registry serves the bytes, up to this many milliseconds (capped at 90000). npm returns 202 and keeps the version invisible for 60-180 s after a publish, so a single shot reports "not published" on a release that already succeeded.',
|
|
203
|
+
},
|
|
183
204
|
},
|
|
184
205
|
},
|
|
185
206
|
async execute(args) {
|
|
@@ -190,7 +211,12 @@ export function apply(ctx, config = {}) {
|
|
|
190
211
|
const version = args.version || json.version
|
|
191
212
|
const { pack, findings: packFindings } = await checkPack(dir)
|
|
192
213
|
if (!pack) return { ok: false, error: `npm pack --dry-run did not run, so there is nothing to compare: ${packFindings.map((f) => f.title).join('; ')}` }
|
|
193
|
-
const
|
|
214
|
+
const waitMs = Math.min(Math.max(0, Number(args.waitMs) || 0), 90_000)
|
|
215
|
+
const { findings, waitedMs } = await waitForPublished(json.name, version, pack, {
|
|
216
|
+
registry: cfg.registry,
|
|
217
|
+
timeoutMs: cfg.networkTimeoutMs,
|
|
218
|
+
waitMs,
|
|
219
|
+
})
|
|
194
220
|
const served = findings.some((f) => f.rule === 'publish-served')
|
|
195
221
|
return {
|
|
196
222
|
ok: true,
|
|
@@ -201,6 +227,7 @@ export function apply(ctx, config = {}) {
|
|
|
201
227
|
localShasum: pack.shasum,
|
|
202
228
|
unpackedSize: pack.unpackedSize,
|
|
203
229
|
files: (pack.files || []).length,
|
|
230
|
+
waitedMs,
|
|
204
231
|
findings,
|
|
205
232
|
report: renderReport(findings, { title: `publish verify · ${json.name}@${version}` }),
|
|
206
233
|
}
|
package/package.json
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@bushizhanghan/dsh-publish-gate",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Pre-publish gate for DSH plugins: manifest, dependencies, artifact, runtime, browser half, traceability and market readiness.
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "lib/index.js",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": "./lib/index.js",
|
|
9
|
-
"./engine": "./lib/engine.js",
|
|
10
|
-
"./package.json": "./package.json"
|
|
11
|
-
},
|
|
12
|
-
"files": [
|
|
13
|
-
"lib",
|
|
14
|
-
"cordis.patch.yml",
|
|
15
|
-
"README.md",
|
|
16
|
-
"LICENSE",
|
|
17
|
-
"CHANGELOG.md"
|
|
18
|
-
],
|
|
19
|
-
"keywords": [
|
|
20
|
-
"dsh",
|
|
21
|
-
"dsh-plugin",
|
|
22
|
-
"deepseek-harness",
|
|
23
|
-
"cordis",
|
|
24
|
-
"cordis-plugin",
|
|
25
|
-
"publish",
|
|
26
|
-
"preflight",
|
|
27
|
-
"npm",
|
|
28
|
-
"market",
|
|
29
|
-
"agent-tools"
|
|
30
|
-
],
|
|
31
|
-
"author": "bushizhanghan",
|
|
32
|
-
"license": "MIT",
|
|
33
|
-
"repository": {
|
|
34
|
-
"type": "git",
|
|
35
|
-
"url": "git+https://github.com/bushizhanghan/dsh-publish-gate.git"
|
|
36
|
-
},
|
|
37
|
-
"bugs": {
|
|
38
|
-
"url": "https://github.com/bushizhanghan/dsh-publish-gate/issues"
|
|
39
|
-
},
|
|
40
|
-
"homepage": "https://github.com/bushizhanghan/dsh-publish-gate#readme",
|
|
41
|
-
"engines": {
|
|
42
|
-
"node": ">=22"
|
|
43
|
-
},
|
|
44
|
-
"dsh": {
|
|
45
|
-
"bundle": {
|
|
46
|
-
"patch": "./cordis.patch.yml"
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
"scripts": {
|
|
50
|
-
"test": "node --test"
|
|
51
|
-
}
|
|
52
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@bushizhanghan/dsh-publish-gate",
|
|
3
|
+
"version": "0.8.1",
|
|
4
|
+
"description": "Pre-publish gate for DSH plugins: manifest, dependencies, artifact, runtime, browser half, traceability and market readiness — plus an input fuzzer that drives every declared parameter with the wrong type. 发布前的七层门禁 + 入参模糊测试。",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./lib/index.js",
|
|
9
|
+
"./engine": "./lib/engine.js",
|
|
10
|
+
"./package.json": "./package.json"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"lib",
|
|
14
|
+
"cordis.patch.yml",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE",
|
|
17
|
+
"CHANGELOG.md"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"dsh",
|
|
21
|
+
"dsh-plugin",
|
|
22
|
+
"deepseek-harness",
|
|
23
|
+
"cordis",
|
|
24
|
+
"cordis-plugin",
|
|
25
|
+
"publish",
|
|
26
|
+
"preflight",
|
|
27
|
+
"npm",
|
|
28
|
+
"market",
|
|
29
|
+
"agent-tools"
|
|
30
|
+
],
|
|
31
|
+
"author": "bushizhanghan",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/bushizhanghan/dsh-publish-gate.git"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/bushizhanghan/dsh-publish-gate/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/bushizhanghan/dsh-publish-gate#readme",
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=22"
|
|
43
|
+
},
|
|
44
|
+
"dsh": {
|
|
45
|
+
"bundle": {
|
|
46
|
+
"patch": "./cordis.patch.yml"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"test": "node --test"
|
|
51
|
+
}
|
|
52
|
+
}
|