dsh-mcp-connector 0.2.78 → 0.2.80
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 +22 -0
- package/README.en.md +2 -2
- package/README.md +2 -2
- package/lib/connection-scopes.js +18 -11
- package/lib/governance.js +15 -12
- package/lib/host-tool-names.js +36 -0
- package/lib/session-injection.js +19 -8
- package/package.json +5 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
本项目遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/);首个公开版本从 `0.1.0` 开始。
|
|
4
4
|
|
|
5
|
+
## [0.2.80] - 2026-10-25
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- #181:将 Zod 从宿主 peer 改为插件直接依赖(^4.1.8),全新 generation 安装无需借用宿主 asar 或手工创建共享 profiles/node_modules。保留 React 和 @deepseek-ai/* 宿主 peer,不放宽宿主隔离校验。
|
|
10
|
+
- 增加 Windows/Linux 全新打包安装门禁:禁用 peer 自动安装,无共享依赖目录,对照 0.2.79 缺失依赖并验证新包提升到 live 后 Zod 的真实路径与实际消费者导入。
|
|
11
|
+
- 该门禁是隔离安装测试,不等于 Windows Desktop 插件管理器 UI 实机验收;#178 仍待原报告者 Windows 性能复测。
|
|
12
|
+
|
|
13
|
+
## [0.2.79] - 2026-10-24
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- #178:治理、项目作用域和会话注入刷新改为轻量名称读取,不再调用 tools.schemas() 深克隆参数,切断 schema 读取副作用引起的延迟事件回路。
|
|
18
|
+
- 默认全局/always 且无 deny 或停用连接时跳过清单读取;项目隔离每轮只读取一次,不按 Agent 数重复。
|
|
19
|
+
- 保留执行 guard、工具增删感知和权限交集;名称读取失败时保留已有限制,不把错误当空清单,也不回退 schema 扫描。
|
|
20
|
+
|
|
21
|
+
### Compatibility and verification
|
|
22
|
+
|
|
23
|
+
- 优先使用上游公开 definitions();已发布旧 Host 通过集中、只读、形状校验的内部 view() 兼容层读取名称。内部接口属于兼容风险,增加 Windows/Linux × Host 0.2.1-rc.2/rc.3 契约门禁;不修改宿主安装。
|
|
24
|
+
- 增加同步、微任务、下一轮事件交付及残余 schema 反馈回路回归。详见 docs/ACCEPTANCE-178-20261024.md。
|
|
25
|
+
- 本次是工具运行时机制修复;报告者 Windows Desktop UI 的原始卡顿与 1.29 亿次调用对照仍需复测,#178 保留跟踪。不包含延期的国际化或其他草案功能。
|
|
26
|
+
|
|
5
27
|
## [0.2.78] - 2026-10-22
|
|
6
28
|
|
|
7
29
|
### Fixed
|
package/README.en.md
CHANGED
|
@@ -85,7 +85,7 @@ If the plugin helps you connect an MCP server faster, consider [starring the rep
|
|
|
85
85
|
- Explicit, non-destructive migration from the two earlier Qichacha OAuth plugins, plus active-plugin conflict detection that blocks duplicate server management and credential overwrites.
|
|
86
86
|
|
|
87
87
|
<!-- catalog-stats:start -->
|
|
88
|
-
As of 2026-10-
|
|
88
|
+
As of 2026-10-24, the public Registry publishes 112 connector descriptors. After merging and deduplicating them with the 4 bundled Qichacha cards, the Marketplace exposes 116 cards across 9 business categories. Recommendations remain limited to the four Qichacha cards, PKULaw, and Wind, for 6 featured cards in total. The Registry evolves independently; the badge shown after a client refresh and the live badges above are the authoritative current counts.
|
|
89
89
|
<!-- catalog-stats:end -->
|
|
90
90
|
|
|
91
91
|
## Interface and demo
|
|
@@ -225,7 +225,7 @@ npm run dev:ui
|
|
|
225
225
|
|
|
226
226
|
Every Registry merge regenerates `catalog-stats.json`; an hourly workflow in this repository synchronizes the Chinese and English product copy plus a local stats snapshot. The static npm README updates with package releases, while the live badges above read the Registry directly and therefore stay current without another npm release.
|
|
227
227
|
|
|
228
|
-
The current public version is [`dsh-mcp-connector@0.2.
|
|
228
|
+
The current public version is [`dsh-mcp-connector@0.2.80`](https://www.npmjs.com/package/dsh-mcp-connector), with [GitHub Release v0.2.80](https://github.com/duhu2000/dsh-mcp-connector/releases/tag/v0.2.80).
|
|
229
229
|
|
|
230
230
|
See [CHANGELOG.md](CHANGELOG.md) for version history and [docs/DESKTOP-E2E.md](docs/DESKTOP-E2E.md) for the Desktop release checklist.
|
|
231
231
|
|
package/README.md
CHANGED
|
@@ -87,7 +87,7 @@ dsh plugin --profile web add dsh-mcp-connector
|
|
|
87
87
|
- 对话工具:`mcp_connector_catalog`、`connect`、`configure`、`import_json`、`export_config`、`snapshot`、`install_from_url`、`status`、`scope`、`health_check`、`policy`、`set_enabled`、`session_tools`、`disconnect`、`refresh_catalog`、`publish`、`tools_list`、`tool_search`、`tool_detail`。搜索/详情只做渐进式能力发现,不执行目标 MCP 工具。
|
|
88
88
|
|
|
89
89
|
<!-- catalog-stats:start -->
|
|
90
|
-
截至 2026-10-
|
|
90
|
+
截至 2026-10-24,公共 Registry 已发布 112 条连接器描述;与随包的 4 张企查查卡片合并去重后,市场页可浏览 116 张卡片,覆盖企业数据、金融投资、法律合规、开发工具、办公协作、调研分析、设计创意、效率工具、其他 9 类。推荐位严格保留 4 张企查查卡片、北大法宝和 Wind,共 6 张;其他连接器按业务分类展示。Registry 可独立持续更新,实际数量以客户端刷新后的市场页签徽标和上方实时统计徽标为准。
|
|
91
91
|
<!-- catalog-stats:end -->
|
|
92
92
|
|
|
93
93
|
## 界面与演示
|
|
@@ -220,7 +220,7 @@ npm run dev:ui
|
|
|
220
220
|
|
|
221
221
|
公共 Registry 每次合并后会生成 `catalog-stats.json`;本仓库的定时工作流每小时同步中英文介绍和统计快照。npm 页面中的静态正文随版本发布更新,上方动态统计徽标则直接读取 Registry,可在不发布新 npm 版本时保持实时数量一致。
|
|
222
222
|
|
|
223
|
-
当前公开版本为 [`dsh-mcp-connector@0.2.
|
|
223
|
+
当前公开版本为 [`dsh-mcp-connector@0.2.80`](https://www.npmjs.com/package/dsh-mcp-connector),对应 [GitHub Release v0.2.80](https://github.com/duhu2000/dsh-mcp-connector/releases/tag/v0.2.80)。
|
|
224
224
|
|
|
225
225
|
版本能力与变更记录见 [CHANGELOG.md](CHANGELOG.md)。
|
|
226
226
|
Desktop 发版回归见 [docs/DESKTOP-E2E.md](docs/DESKTOP-E2E.md)。
|
package/lib/connection-scopes.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* credential.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { readHostToolNames, supportsHostToolNames } from './host-tool-names.js';
|
|
11
|
+
|
|
10
12
|
export const CONNECTION_SCOPE_HISTORY_LIMIT = 20;
|
|
11
13
|
export const CONNECTION_SCOPE_TYPES = ['global', 'project'];
|
|
12
14
|
|
|
@@ -280,19 +282,18 @@ export function createHostScopeController(ctx, {
|
|
|
280
282
|
: null;
|
|
281
283
|
|
|
282
284
|
function observedPublicNames() {
|
|
283
|
-
if (typeof ctx.tools?.schemas !== 'function') return [];
|
|
284
285
|
try {
|
|
285
|
-
return ctx.tools
|
|
286
|
+
return readHostToolNames(ctx.tools)
|
|
286
287
|
.filter((name) => typeof name === 'string' && recordForPublicName(name))
|
|
287
288
|
.sort();
|
|
288
289
|
} catch (error) {
|
|
289
290
|
logger?.warn?.(`scope read Host tools failed: ${error.message}`);
|
|
290
|
-
return
|
|
291
|
+
return null;
|
|
291
292
|
}
|
|
292
293
|
}
|
|
293
294
|
|
|
294
|
-
function installRestriction(agent) {
|
|
295
|
-
const denied =
|
|
295
|
+
function installRestriction(agent, observed) {
|
|
296
|
+
const denied = observed.filter((name) => {
|
|
296
297
|
const record = recordForPublicName(name);
|
|
297
298
|
return record && !visible(record, agent);
|
|
298
299
|
});
|
|
@@ -315,15 +316,21 @@ export function createHostScopeController(ctx, {
|
|
|
315
316
|
let syncing = false;
|
|
316
317
|
function refresh() {
|
|
317
318
|
if (disposed || !agentsMounted) return;
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
// the same single-threaded state and can otherwise keep the event loop in
|
|
321
|
-
// a permanent tools/change cycle with plugins that re-register tools.
|
|
319
|
+
// Ignore synchronous restriction notifications while reconciling. Name-only
|
|
320
|
+
// reads avoid schema side effects; delayed events still observe real changes.
|
|
322
321
|
if (syncing) return;
|
|
323
322
|
syncing = true;
|
|
324
323
|
try {
|
|
325
324
|
const live = new Set(mountedAgentCtx?.agents?.list?.() ?? []);
|
|
326
|
-
|
|
325
|
+
// Global bindings cannot hide tools. When project isolation is needed,
|
|
326
|
+
// enumerate once per refresh, not once per live Agent.
|
|
327
|
+
const currentBindings = bindings();
|
|
328
|
+
const needsRestriction = records().some((record) => (
|
|
329
|
+
!bindingForConnection(currentBindings, record.key).global
|
|
330
|
+
));
|
|
331
|
+
const observed = live.size && needsRestriction ? observedPublicNames() : [];
|
|
332
|
+
if (observed === null) return;
|
|
333
|
+
for (const agent of live) installRestriction(agent, observed);
|
|
327
334
|
for (const agent of restrictions.keys()) if (!live.has(agent)) restrictions.delete(agent);
|
|
328
335
|
} finally {
|
|
329
336
|
syncing = false;
|
|
@@ -363,7 +370,7 @@ export function createHostScopeController(ctx, {
|
|
|
363
370
|
capabilities() {
|
|
364
371
|
return {
|
|
365
372
|
executionGuard: Boolean(disposeGuard),
|
|
366
|
-
visibilityRestriction: agentsMounted && typeof ctx.tools?.restrict === 'function',
|
|
373
|
+
visibilityRestriction: agentsMounted && typeof ctx.tools?.restrict === 'function' && supportsHostToolNames(ctx.tools),
|
|
367
374
|
workspaceRegistry: typeof workspaceIdForAgent === 'function',
|
|
368
375
|
restrictedAgents: restrictions.size,
|
|
369
376
|
};
|
package/lib/governance.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* 连接记录的 lifecycle disabled 是物理下线状态,任何 allow 规则都不能覆盖。
|
|
6
6
|
*/
|
|
7
7
|
import { createHash } from 'node:crypto';
|
|
8
|
+
import { readHostToolNames, supportsHostToolNames } from './host-tool-names.js';
|
|
8
9
|
|
|
9
10
|
export const GOVERNANCE_HISTORY_LIMIT = 20;
|
|
10
11
|
export const GOVERNANCE_SCOPES = ['connection', 'server', 'tool'];
|
|
@@ -302,20 +303,23 @@ export function createHostGovernanceController(ctx, { getRules, getRecords, logg
|
|
|
302
303
|
: null;
|
|
303
304
|
|
|
304
305
|
function observedPublicNames() {
|
|
305
|
-
if (typeof ctx.tools?.schemas !== 'function') return [];
|
|
306
306
|
try {
|
|
307
|
-
return ctx.tools
|
|
308
|
-
.map((schema) => schema?.name)
|
|
307
|
+
return readHostToolNames(ctx.tools)
|
|
309
308
|
.filter((name) => typeof name === 'string' && targetForPublicName(name))
|
|
310
309
|
.sort();
|
|
311
310
|
} catch (error) {
|
|
312
311
|
logger?.warn?.(`governance read Host tools failed: ${error.message}`);
|
|
313
|
-
return
|
|
312
|
+
return null;
|
|
314
313
|
}
|
|
315
314
|
}
|
|
316
315
|
|
|
317
316
|
function deniedObservedNames() {
|
|
318
|
-
|
|
317
|
+
// The default policy cannot deny anything. Avoid deep-cloning every Host
|
|
318
|
+
// schema just to prove that the resulting restriction would be empty.
|
|
319
|
+
// Disabled connections still require enforcement even without a deny rule.
|
|
320
|
+
if (!records().some((record) => record.enabled === false)
|
|
321
|
+
&& !rules().some((rule) => rule.effect === 'deny')) return [];
|
|
322
|
+
return observedPublicNames()?.filter((name) => inspect(name)?.policy.effect === 'deny') ?? null;
|
|
319
323
|
}
|
|
320
324
|
|
|
321
325
|
function installRestriction(agent, denied) {
|
|
@@ -338,16 +342,15 @@ export function createHostGovernanceController(ctx, { getRules, getRecords, logg
|
|
|
338
342
|
let syncing = false;
|
|
339
343
|
function syncRestrictions() {
|
|
340
344
|
if (disposed || !agentsMounted) return;
|
|
341
|
-
//
|
|
342
|
-
//
|
|
343
|
-
//
|
|
344
|
-
// so replaying that nested event only scans the same state again and can
|
|
345
|
-
// form an endless tools/change -> schemas -> tools/change loop.
|
|
345
|
+
// Restriction installation may synchronously notify the same controller.
|
|
346
|
+
// Name-only reads below do not materialize schemas; delayed notifications
|
|
347
|
+
// are harmless rechecks and identical deny sets do not reinstall a mask.
|
|
346
348
|
if (syncing) return;
|
|
347
349
|
syncing = true;
|
|
348
350
|
try {
|
|
349
|
-
const denied = deniedObservedNames();
|
|
350
351
|
const live = new Set(mountedAgentCtx?.agents?.list?.() ?? []);
|
|
352
|
+
const denied = live.size ? deniedObservedNames() : [];
|
|
353
|
+
if (denied === null) return;
|
|
351
354
|
for (const agent of live) installRestriction(agent, denied);
|
|
352
355
|
for (const agent of agentRestrictions.keys()) {
|
|
353
356
|
if (!live.has(agent)) agentRestrictions.delete(agent);
|
|
@@ -385,7 +388,7 @@ export function createHostGovernanceController(ctx, { getRules, getRecords, logg
|
|
|
385
388
|
capabilities() {
|
|
386
389
|
return {
|
|
387
390
|
executionGuard: Boolean(disposeGuard),
|
|
388
|
-
visibilityRestriction: agentsMounted && typeof ctx.tools?.restrict === 'function',
|
|
391
|
+
visibilityRestriction: agentsMounted && typeof ctx.tools?.restrict === 'function' && supportsHostToolNames(ctx.tools),
|
|
389
392
|
activeAgents: mountedAgentCtx?.agents?.list?.().length ?? 0,
|
|
390
393
|
restrictedAgents: agentRestrictions.size,
|
|
391
394
|
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Name-only Host adapter. Never materialize schemas on the visibility event path.
|
|
3
|
+
*
|
|
4
|
+
* Upstream exposes definitions() on main. Published Host 0.1.2-rc.1 and
|
|
5
|
+
* 0.2.1-rc.2/rc.3 instead expose view() at runtime (private in their typings).
|
|
6
|
+
* Keep that legacy dependency here, read-only and shape-checked; never patch
|
|
7
|
+
* the Host or retain its definitions. Pinned real-Host tests cover the bridge.
|
|
8
|
+
*/
|
|
9
|
+
export function supportsHostToolNames(tools) {
|
|
10
|
+
return typeof tools?.definitions === 'function' || typeof tools?.view === 'function';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function readHostToolNames(tools) {
|
|
14
|
+
let names;
|
|
15
|
+
if (typeof tools?.definitions === 'function') {
|
|
16
|
+
const definitions = tools.definitions();
|
|
17
|
+
if (!Array.isArray(definitions)) throw new Error('Host tools.definitions() 返回格式不兼容');
|
|
18
|
+
names = definitions.map((definition) => definition?.name);
|
|
19
|
+
} else if (typeof tools?.view === 'function') {
|
|
20
|
+
const view = tools.view();
|
|
21
|
+
if (!(view?.visible instanceof Map)
|
|
22
|
+
|| !(view.knownNames instanceof Set)
|
|
23
|
+
|| !(view.restrictableNames instanceof Set)) {
|
|
24
|
+
throw new Error('Host tools.view() 返回格式不兼容');
|
|
25
|
+
}
|
|
26
|
+
names = [...view.visible.keys()];
|
|
27
|
+
} else {
|
|
28
|
+
// A schema fallback would reintroduce #178. Callers retain existing masks
|
|
29
|
+
// on error and their independent execution guards remain active.
|
|
30
|
+
throw new Error('当前 Host 不支持轻量工具名称读取,无法刷新工具可见性限制');
|
|
31
|
+
}
|
|
32
|
+
if (names.some((name) => typeof name !== 'string' || !name)) {
|
|
33
|
+
throw new Error('Host 工具名称格式不兼容');
|
|
34
|
+
}
|
|
35
|
+
return [...new Set(names)].sort();
|
|
36
|
+
}
|
package/lib/session-injection.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* another conversation or survive a DSH restart.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { readHostToolNames, supportsHostToolNames } from './host-tool-names.js';
|
|
11
|
+
|
|
10
12
|
export const INJECTION_MODES = ['always', 'session'];
|
|
11
13
|
export const DEFAULT_SESSION_TOOL_TTL_MINUTES = 30;
|
|
12
14
|
export const MAX_SESSION_TOOL_TTL_MINUTES = 30;
|
|
@@ -68,14 +70,13 @@ export function createSessionToolInjectionController(ctx, {
|
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
function observedPublicNames() {
|
|
71
|
-
if (typeof ctx.tools?.schemas !== 'function') return [];
|
|
72
73
|
try {
|
|
73
|
-
return ctx.tools
|
|
74
|
+
return readHostToolNames(ctx.tools)
|
|
74
75
|
.filter((name) => typeof name === 'string' && recordForPublicName(name))
|
|
75
76
|
.sort();
|
|
76
77
|
} catch (error) {
|
|
77
78
|
logger?.warn?.(`session injection read Host tools failed: ${error.message}`);
|
|
78
|
-
return
|
|
79
|
+
return null;
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
|
|
@@ -165,9 +166,15 @@ export function createSessionToolInjectionController(ctx, {
|
|
|
165
166
|
if (disposed || !agentsMounted || syncing) return;
|
|
166
167
|
syncing = true;
|
|
167
168
|
try {
|
|
168
|
-
const observed = observedPublicNames();
|
|
169
|
-
const observedSet = new Set(observed);
|
|
170
169
|
const live = new Set(mountedAgentCtx?.agents?.list?.() ?? []);
|
|
170
|
+
// Legacy/default always-on connections need no session visibility mask.
|
|
171
|
+
// Still reconcile empty masks and purge activations when the mode changes.
|
|
172
|
+
const needsRestriction = records().some((record) => (
|
|
173
|
+
record.enabled !== false && normalizeInjectionMode(record.injectionMode) === 'session'
|
|
174
|
+
));
|
|
175
|
+
const observed = live.size && needsRestriction ? observedPublicNames() : [];
|
|
176
|
+
if (observed === null) return;
|
|
177
|
+
const observedSet = new Set(observed);
|
|
171
178
|
for (const agent of live) {
|
|
172
179
|
purgeState(agent, observedSet);
|
|
173
180
|
installRestriction(agent, deniedNames(agent, observed));
|
|
@@ -223,7 +230,9 @@ export function createSessionToolInjectionController(ctx, {
|
|
|
223
230
|
if (!isRecordVisible(record, agent)) throw new Error(`连接 "${record.name}" 不属于当前工作区`);
|
|
224
231
|
const requested = uniqueNames(input.publicNames);
|
|
225
232
|
if (requested.length === 0) throw new Error('必须指定至少一个精确 public tool name');
|
|
226
|
-
const
|
|
233
|
+
const names = observedPublicNames();
|
|
234
|
+
if (names === null) throw new Error('Host 工具名称读取失败,暂不能激活工具');
|
|
235
|
+
const observed = new Set(names);
|
|
227
236
|
for (const publicName of requested) {
|
|
228
237
|
if (!observed.has(publicName)) throw new Error(`Host 尚未观察到工具 "${publicName}"`);
|
|
229
238
|
const owner = recordForPublicName(publicName);
|
|
@@ -249,7 +258,9 @@ export function createSessionToolInjectionController(ctx, {
|
|
|
249
258
|
|
|
250
259
|
function status(agent) {
|
|
251
260
|
assertAgent(agent);
|
|
252
|
-
const
|
|
261
|
+
const names = observedPublicNames();
|
|
262
|
+
if (names === null) throw new Error('Host 工具名称读取失败,暂不能读取会话工具状态');
|
|
263
|
+
const observed = new Set(names);
|
|
253
264
|
purgeState(agent, observed);
|
|
254
265
|
const state = stateFor(agent);
|
|
255
266
|
const active = [...(state?.activations ?? new Map()).entries()].map(([publicName, activation]) => ({
|
|
@@ -375,7 +386,7 @@ export function createSessionToolInjectionController(ctx, {
|
|
|
375
386
|
function capabilities() {
|
|
376
387
|
return {
|
|
377
388
|
executionGuard: Boolean(disposeGuard),
|
|
378
|
-
visibilityRestriction: agentsMounted && typeof ctx.tools?.restrict === 'function',
|
|
389
|
+
visibilityRestriction: agentsMounted && typeof ctx.tools?.restrict === 'function' && supportsHostToolNames(ctx.tools),
|
|
379
390
|
activeAgents: mountedAgentCtx?.agents?.list?.().length ?? 0,
|
|
380
391
|
restrictedAgents: restrictions.size,
|
|
381
392
|
maxTtlMinutes: MAX_SESSION_TOOL_TTL_MINUTES,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mcp-connector",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.80",
|
|
4
4
|
"description": "DeepSeek Harness MCP Connector: connect servers, search tools across active connections, and troubleshoot discovery. Includes a continuously updated catalog; supports OAuth 2.0 PKCE, API keys, Streamable HTTP/stdio, and mcpServers JSON import.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -149,19 +149,18 @@
|
|
|
149
149
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
150
150
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
151
151
|
"react": "^18.2.0",
|
|
152
|
-
"react-dom": "^18.2.0"
|
|
153
|
-
"zod": ">=4.1.8 <5"
|
|
152
|
+
"react-dom": "^18.2.0"
|
|
154
153
|
},
|
|
155
154
|
"dependencies": {
|
|
156
155
|
"cross-spawn": "^7.0.6",
|
|
157
|
-
"undici": "^6.29.0"
|
|
156
|
+
"undici": "^6.29.0",
|
|
157
|
+
"zod": "^4.1.8"
|
|
158
158
|
},
|
|
159
159
|
"devDependencies": {
|
|
160
160
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
161
161
|
"@deepseek-ai/dsh-storage": "^0.1.0-rc.7",
|
|
162
162
|
"@deepseek-ai/dsh-storage-domain": "^0.1.0-rc.7",
|
|
163
|
-
"@deepseek-ai/schemastery": "^3.18.1"
|
|
164
|
-
"zod": "^4.4.0"
|
|
163
|
+
"@deepseek-ai/schemastery": "^3.18.1"
|
|
165
164
|
},
|
|
166
165
|
"engines": {
|
|
167
166
|
"node": ">=20"
|