dsh-plugin-discovery 1.2.0 → 1.2.5

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.
Files changed (5) hide show
  1. package/README.md +104 -96
  2. package/client.js +646 -24
  3. package/data/recommended.json +1206 -1206
  4. package/index.js +318 -14
  5. package/package.json +74 -74
package/index.js CHANGED
@@ -389,6 +389,193 @@ async function searchNpm(query, limit) {
389
389
  });
390
390
  }
391
391
 
392
+ /** 在线搜索时把精选目录命中置顶,避免同名冷门包(如 @dsh-ssh/dsh-ssh)盖过已验证的 @linxin666/dsh-ssh。 */
393
+ function catalogHitsForQuery(catalog, query, limit) {
394
+ const rows = filterCatalogPlugins(catalog, {
395
+ category: "",
396
+ query,
397
+ personalize: false,
398
+ installedNames: [],
399
+ });
400
+ return rows.slice(0, Math.max(0, limit)).map((row) => ({
401
+ ...mapCatalogPlugin(row),
402
+ reason: "catalog",
403
+ }));
404
+ }
405
+
406
+ function suggestCatalogAlternatives(catalog, failedSpec, limit = 3) {
407
+ const failed = String(failedSpec || "").trim().toLowerCase();
408
+ if (!failed) return [];
409
+ const tokens = failed
410
+ .replace(/^@/, "")
411
+ .split(/[/@._-]+/)
412
+ .map((part) => part.trim().toLowerCase())
413
+ .filter((part) => part.length >= 3 && part !== "dsh" && part !== "plugin");
414
+ const uniq = [];
415
+ const seen = new Set([failed]);
416
+ for (const token of tokens.length ? tokens : [failed]) {
417
+ for (const row of catalogHitsForQuery(catalog, token, 8)) {
418
+ const spec = String(row.spec || "").trim();
419
+ if (!spec || seen.has(spec.toLowerCase())) continue;
420
+ seen.add(spec.toLowerCase());
421
+ uniq.push(spec);
422
+ if (uniq.length >= limit) return uniq;
423
+ }
424
+ }
425
+ return uniq;
426
+ }
427
+
428
+ /** ManagementError 没有 message 字段,只有 code / diagnostic;再拼上 packageResult 输出尾部。 */
429
+ function formatChangeFailure(result, fallbackApplication = "failed") {
430
+ const parts = [];
431
+ const err = result?.error;
432
+ if (err && typeof err === "object") {
433
+ if (err.diagnostic) parts.push(String(err.diagnostic).trim());
434
+ if (err.code) parts.push(`错误码:${err.code}`);
435
+ if (Array.isArray(err.incompatible) && err.incompatible.length) {
436
+ const names = err.incompatible
437
+ .map((row) => `${row?.name || "?"}@${row?.version || "?"}`)
438
+ .filter(Boolean);
439
+ if (names.length) parts.push(`不兼容:${names.join("、")}`);
440
+ }
441
+ } else if (typeof err === "string" && err.trim()) {
442
+ parts.push(err.trim());
443
+ } else if (err?.message) {
444
+ parts.push(String(err.message).trim());
445
+ }
446
+
447
+ const pkg = result?.packageResult;
448
+ if (pkg && typeof pkg === "object") {
449
+ if (pkg.kind) parts.push(`失败类型:${pkg.kind}`);
450
+ if (pkg.timedOut) parts.push("安装超时(长时间无输出)");
451
+ if (typeof pkg.exitCode === "number") parts.push(`退出码:${pkg.exitCode}`);
452
+ const output = String(pkg.output || "").trim();
453
+ if (output) {
454
+ const lines = output
455
+ .split(/\r?\n/)
456
+ .map((line) => line.trim())
457
+ .filter(Boolean);
458
+ const tail = lines.slice(-8).join("\n");
459
+ if (tail) parts.push(tail);
460
+ }
461
+ if (pkg.logPath) parts.push(`完整日志:${pkg.logPath}`);
462
+ }
463
+
464
+ if (result?.failedAt) parts.push(`失败位置:${result.failedAt}`);
465
+ if (Array.isArray(result?.pendingBuilds) && result.pendingBuilds.length) {
466
+ parts.push(`待批准构建脚本:${result.pendingBuilds.join("、")}`);
467
+ }
468
+ if (Array.isArray(result?.warnings) && result.warnings.length) {
469
+ parts.push(`警告:${result.warnings.slice(0, 3).join(";")}`);
470
+ }
471
+
472
+ const unique = [];
473
+ const seen = new Set();
474
+ for (const part of parts) {
475
+ const text = String(part || "").trim();
476
+ if (!text || seen.has(text)) continue;
477
+ seen.add(text);
478
+ unique.push(text);
479
+ }
480
+ if (unique.length) return unique.join("\n");
481
+ const application = result?.application || fallbackApplication;
482
+ return `安装失败(${application})。插件管理器未返回 diagnostic / 安装日志。`;
483
+ }
484
+
485
+ function extractIncompatibility(result) {
486
+ const code = result?.error?.code || null;
487
+ const incompatible = Array.isArray(result?.error?.incompatible)
488
+ ? result.error.incompatible
489
+ .map((row) => ({
490
+ name: String(row?.name || "").trim(),
491
+ version: String(row?.version || "").trim(),
492
+ runtimeVersion: String(row?.runtimeVersion || "").trim(),
493
+ peers: row?.peers && typeof row.peers === "object" ? row.peers : {},
494
+ }))
495
+ .filter((row) => row.name && row.version)
496
+ : [];
497
+ return {
498
+ code,
499
+ incompatible,
500
+ needsRiskExemption: code === "incompatible-version" && incompatible.length > 0,
501
+ };
502
+ }
503
+
504
+ /** pnpm 11 拦截依赖安装脚本时,失败结果带 pendingBuilds;需用户确认后经 approvedBuilds 重试。 */
505
+ function extractBuildBlock(result) {
506
+ const pendingBuilds = Array.isArray(result?.pendingBuilds)
507
+ ? [...new Set(result.pendingBuilds.map((name) => String(name || "").trim()).filter(Boolean))]
508
+ : [];
509
+ const kind = result?.packageResult?.kind || null;
510
+ const code = result?.error?.code || null;
511
+ const blocked =
512
+ kind === "build-blocked" ||
513
+ code === "build-blocked" ||
514
+ /ERR_PNPM_IGNORED_BUILDS|Ignored build scripts/i.test(String(result?.packageResult?.output || ""));
515
+ return {
516
+ pendingBuilds,
517
+ needsBuildApproval: blocked && pendingBuilds.length > 0,
518
+ };
519
+ }
520
+
521
+ function normalizeApprovedBuilds(value) {
522
+ if (!Array.isArray(value)) return [];
523
+ return [...new Set(value.map((name) => String(name || "").trim()).filter(Boolean))];
524
+ }
525
+
526
+ /** 安装失败时从精选目录推荐相近/同标签插件,优先给出可替代的当前生态包。 */
527
+ function buildFailureRecommendations(catalog, failedSpec, limit = 4) {
528
+ const failed = String(failedSpec || "").trim();
529
+ const seen = new Set([failed.toLowerCase()]);
530
+ const out = [];
531
+ const pushRow = (row, reason) => {
532
+ const spec = String(row?.spec || "").trim();
533
+ if (!spec || seen.has(spec.toLowerCase())) return;
534
+ seen.add(spec.toLowerCase());
535
+ out.push({
536
+ spec,
537
+ title: row.title || spec,
538
+ description: row.description || "",
539
+ source: "catalog",
540
+ installSpec: spec,
541
+ catalogId: row.id || row.spec,
542
+ reason: reason || "similar",
543
+ featured: row.featured === true,
544
+ rank: typeof row.rank === "number" ? row.rank : 0,
545
+ });
546
+ };
547
+
548
+ const plugins = Array.isArray(catalog?.plugins) ? catalog.plugins : [];
549
+ const failedRow = plugins.find((row) => String(row.spec || "").toLowerCase() === failed.toLowerCase());
550
+ if (failedRow) {
551
+ for (const rel of failedRow.related || []) {
552
+ const hit = plugins.find((row) => String(row.spec || "") === rel || String(row.id || "") === rel);
553
+ if (hit) pushRow(hit, "related");
554
+ if (out.length >= limit) return out;
555
+ }
556
+ for (const tag of failedRow.tags || []) {
557
+ for (const row of plugins) {
558
+ if ((row.tags || []).includes(tag)) pushRow(row, "tag");
559
+ if (out.length >= limit) return out;
560
+ }
561
+ }
562
+ for (const cat of failedRow.categories || []) {
563
+ if (cat === "featured") continue;
564
+ for (const row of plugins) {
565
+ if ((row.categories || []).includes(cat)) pushRow(row, "category");
566
+ if (out.length >= limit) return out;
567
+ }
568
+ }
569
+ }
570
+
571
+ for (const spec of suggestCatalogAlternatives(catalog, failed, limit)) {
572
+ const hit = plugins.find((row) => String(row.spec || "") === spec) || { spec, title: spec };
573
+ pushRow(hit, "similar");
574
+ if (out.length >= limit) break;
575
+ }
576
+ return out.slice(0, limit);
577
+ }
578
+
392
579
  function mapGithubRepos(items) {
393
580
  return (Array.isArray(items) ? items : []).map((repo) => ({
394
581
  source: "github",
@@ -936,6 +1123,9 @@ class PluginDiscoveryService extends TypertRemoteService {
936
1123
 
937
1124
  const limit = this.config.maxCandidates;
938
1125
  const token = opts.githubToken ?? this.config.githubToken;
1126
+ await this.refreshCatalog({ force: false }).catch(() => {});
1127
+ const catalog = this.getCatalog();
1128
+ const catalogHits = catalogHitsForQuery(catalog, q === "dsh" ? "" : q, limit);
939
1129
  let raw = [];
940
1130
  try {
941
1131
  if (source === "npm" || source === "all") {
@@ -946,23 +1136,27 @@ class PluginDiscoveryService extends TypertRemoteService {
946
1136
  }
947
1137
  } catch (error) {
948
1138
  this.ctx.logger.warn("[plugin-discovery] search failed", error);
949
- return {
950
- ok: false,
951
- message: error instanceof Error ? error.message : String(error),
952
- results: [],
953
- total: 0,
954
- };
1139
+ if (!catalogHits.length) {
1140
+ return {
1141
+ ok: false,
1142
+ message: error instanceof Error ? error.message : String(error),
1143
+ results: [],
1144
+ total: 0,
1145
+ };
1146
+ }
1147
+ // 在线源失败时仍返回精选命中,避免「搜得到却全空」
1148
+ raw = [];
955
1149
  }
956
1150
 
957
1151
  const seen = new Set();
958
1152
  const deduped = [];
959
- for (const row of raw) {
1153
+ for (const row of [...catalogHits, ...raw]) {
960
1154
  if (!row.spec || seen.has(row.spec)) continue;
961
1155
  seen.add(row.spec);
962
1156
  deduped.push({
963
1157
  ...row,
964
1158
  installSpec: row.spec,
965
- bundle: null,
1159
+ bundle: row.bundle ?? null,
966
1160
  });
967
1161
  if (deduped.length >= limit) break;
968
1162
  }
@@ -1086,14 +1280,40 @@ class PluginDiscoveryService extends TypertRemoteService {
1086
1280
  message: `「${existing}」已在 DSH 插件列表中,无需重复安装。请到「插件」页查看。`,
1087
1281
  };
1088
1282
  }
1283
+ const withInstallHint = (message) => {
1284
+ const text = String(message || "").trim() || "安装失败";
1285
+ const looksNetwork =
1286
+ /ERR_PNPM_META_FETCH_FAIL|fetch failed|ECONNRESET|ETIMEDOUT|ENOTFOUND|network|镜像/i.test(text);
1287
+ if (!looksNetwork) return text;
1288
+ const alts = suggestCatalogAlternatives(this.getCatalog(), trimmed);
1289
+ if (!alts.length) {
1290
+ return `${text}。若持续失败,可改用「分类浏览 / 为你推荐」里的精选包,或稍后重试。`;
1291
+ }
1292
+ return `${text}。可改试精选目录中的相近插件:${alts.join("、")}`;
1293
+ };
1294
+ const approvedBuilds = normalizeApprovedBuilds(opts.approvedBuilds);
1089
1295
  try {
1090
1296
  const result = await this.ctx.pluginManager.installBundle(trimmed, {
1091
1297
  requestId,
1092
1298
  enabled: true,
1299
+ ...(approvedBuilds.length ? { approvedBuilds } : {}),
1093
1300
  });
1094
1301
  const packageName = result?.bundle || pre.name || trimmed;
1095
1302
  const application = result?.application;
1096
1303
  const failed = application === "failed" || application === "cancelled";
1304
+ const rawMessage = failed
1305
+ ? formatChangeFailure(result, application || "failed")
1306
+ : `「${packageName}」安装完成(${application || "applied"} / ${result?.stage || "install"})`;
1307
+ const incompat = failed ? extractIncompatibility(result) : {};
1308
+ const buildBlock = failed ? extractBuildBlock(result) : {};
1309
+ const recommendations =
1310
+ failed && (incompat.needsRiskExemption || incompat.code === "incompatible-version")
1311
+ ? buildFailureRecommendations(this.getCatalog(), trimmed, 4)
1312
+ : undefined;
1313
+ let message = failed ? withInstallHint(rawMessage) : rawMessage;
1314
+ if (failed && recommendations && recommendations.length) {
1315
+ message = `${message}\n推荐改试:${recommendations.map((row) => row.spec).join("、")}`;
1316
+ }
1097
1317
  return {
1098
1318
  ok: !failed,
1099
1319
  alreadyInstalled: false,
@@ -1101,15 +1321,71 @@ class PluginDiscoveryService extends TypertRemoteService {
1101
1321
  requestId,
1102
1322
  application,
1103
1323
  stage: result?.stage,
1104
- message: failed
1105
- ? result?.error?.message || `安装失败(${application || "unknown"})`
1106
- : `「${packageName}」安装完成(${application || "applied"} / ${result?.stage || "install"})`,
1324
+ message,
1325
+ detail: failed ? rawMessage : undefined,
1326
+ logPath: failed ? result?.packageResult?.logPath : undefined,
1327
+ code:
1328
+ incompat.code ||
1329
+ (buildBlock.needsBuildApproval ? "build-blocked" : undefined) ||
1330
+ undefined,
1331
+ incompatible: incompat.incompatible || undefined,
1332
+ needsRiskExemption: incompat.needsRiskExemption === true,
1333
+ pendingBuilds: buildBlock.pendingBuilds || undefined,
1334
+ needsBuildApproval: buildBlock.needsBuildApproval === true,
1335
+ approvedBuilds: approvedBuilds.length ? approvedBuilds : result?.approvedBuilds,
1336
+ recommendations,
1337
+ installSpec: trimmed,
1107
1338
  result,
1108
1339
  };
1109
1340
  } catch (error) {
1110
1341
  return {
1111
1342
  ok: false,
1112
1343
  requestId,
1344
+ message: withInstallHint(error instanceof Error ? error.message : String(error)),
1345
+ installSpec: trimmed,
1346
+ };
1347
+ }
1348
+ }
1349
+
1350
+ /** Typert Remote:参数必须是独立形参,不能解构、不能默认值、不能 rest。 */
1351
+ async grantVersionExemption(packageVersion, runtimeVersion, acceptRisk) {
1352
+ if (!this.config.enabled) {
1353
+ return { ok: false, message: "发现插件已停用" };
1354
+ }
1355
+ const pkg = String(packageVersion == null ? "" : packageVersion).trim();
1356
+ const runtime = String(runtimeVersion == null ? "" : runtimeVersion).trim();
1357
+ const accepted = acceptRisk === true;
1358
+ if (!pkg || !runtime) {
1359
+ return { ok: false, message: "缺少 packageVersion 或 runtimeVersion" };
1360
+ }
1361
+ if (!accepted) {
1362
+ return {
1363
+ ok: false,
1364
+ message: "未确认风险。授予版本豁免必须 acceptRisk=true,并先明确告知可能崩溃或数据丢失。",
1365
+ };
1366
+ }
1367
+ if (typeof this.ctx.pluginManager?.setVersionExemption !== "function") {
1368
+ return { ok: false, message: "当前 DSH 不支持 setVersionExemption" };
1369
+ }
1370
+ try {
1371
+ const result = await this.ctx.pluginManager.setVersionExemption(pkg, runtime, true, true);
1372
+ const application = result?.application;
1373
+ const failed = application === "failed" || application === "cancelled";
1374
+ return {
1375
+ ok: !failed,
1376
+ packageVersion: pkg,
1377
+ runtimeVersion: runtime,
1378
+ application,
1379
+ message: failed
1380
+ ? formatChangeFailure(result, application || "failed")
1381
+ : `已为 ${pkg} 在 DSH ${runtime} 上授予精确版本豁免,可重新安装。`,
1382
+ result,
1383
+ };
1384
+ } catch (error) {
1385
+ return {
1386
+ ok: false,
1387
+ packageVersion: pkg,
1388
+ runtimeVersion: runtime,
1113
1389
  message: error instanceof Error ? error.message : String(error),
1114
1390
  };
1115
1391
  }
@@ -1124,6 +1400,23 @@ class PluginDiscoveryService extends TypertRemoteService {
1124
1400
  return { ok: true, settled: null, pending: true, message: "安装仍在进行或已丢失" };
1125
1401
  }
1126
1402
  const failed = settled.application === "failed" || settled.application === "cancelled";
1403
+ const failText = failed ? formatChangeFailure(settled, settled.application) : "";
1404
+ const incompat = failed ? extractIncompatibility(settled) : {};
1405
+ const buildBlock = failed ? extractBuildBlock(settled) : {};
1406
+ const failedSpec =
1407
+ (Array.isArray(incompat.incompatible) && incompat.incompatible[0]
1408
+ ? `${incompat.incompatible[0].name}@${incompat.incompatible[0].version}`
1409
+ : "") || settled.bundle || "";
1410
+ const recommendations =
1411
+ failed && (incompat.needsRiskExemption || incompat.code === "incompatible-version")
1412
+ ? buildFailureRecommendations(this.getCatalog(), failedSpec, 4)
1413
+ : undefined;
1414
+ let message = failed
1415
+ ? failText
1416
+ : `安装完成(${settled.application} / ${settled.stage})`;
1417
+ if (failed && recommendations && recommendations.length) {
1418
+ message = `${message}\n推荐改试:${recommendations.map((row) => row.spec).join("、")}`;
1419
+ }
1127
1420
  return {
1128
1421
  ok: !failed,
1129
1422
  settled,
@@ -1131,9 +1424,19 @@ class PluginDiscoveryService extends TypertRemoteService {
1131
1424
  packageName: settled.bundle,
1132
1425
  application: settled.application,
1133
1426
  stage: settled.stage,
1134
- message: failed
1135
- ? settled.error?.message || `安装失败(${settled.application})`
1136
- : `安装完成(${settled.application} / ${settled.stage})`,
1427
+ message,
1428
+ detail: failed ? failText : undefined,
1429
+ logPath: failed ? settled?.packageResult?.logPath : undefined,
1430
+ code:
1431
+ incompat.code ||
1432
+ (buildBlock.needsBuildApproval ? "build-blocked" : undefined) ||
1433
+ undefined,
1434
+ incompatible: incompat.incompatible || undefined,
1435
+ needsRiskExemption: incompat.needsRiskExemption === true,
1436
+ pendingBuilds: buildBlock.pendingBuilds || undefined,
1437
+ needsBuildApproval: buildBlock.needsBuildApproval === true,
1438
+ recommendations,
1439
+ installSpec: failedSpec || undefined,
1137
1440
  };
1138
1441
  } catch (error) {
1139
1442
  return {
@@ -1260,6 +1563,7 @@ markRemoteMethod(PluginDiscoveryService.prototype, "checkSelfUpdate");
1260
1563
  markRemoteMethod(PluginDiscoveryService.prototype, "updateSelf");
1261
1564
  markRemoteMethod(PluginDiscoveryService.prototype, "resolve");
1262
1565
  markRemoteMethod(PluginDiscoveryService.prototype, "installPlugin");
1566
+ markRemoteMethod(PluginDiscoveryService.prototype, "grantVersionExemption");
1263
1567
  markRemoteMethod(PluginDiscoveryService.prototype, "listInstalled");
1264
1568
  markRemoteMethod(PluginDiscoveryService.prototype, "waitInstall");
1265
1569
 
package/package.json CHANGED
@@ -1,74 +1,74 @@
1
- {
2
- "name": "dsh-plugin-discovery",
3
- "version": "1.2.0",
4
- "type": "module",
5
- "description": "Discover and install community DSH bundles from the sidebar: newbie essentials, recommendations, categories, self-update, and npm/GitHub search.",
6
- "author": "hjhcos",
7
- "license": "MIT",
8
- "keywords": [
9
- "dsh",
10
- "dsh-plugin",
11
- "deepseek",
12
- "deepseek-harness",
13
- "plugin",
14
- "bundle",
15
- "discovery",
16
- "marketplace",
17
- "recommend"
18
- ],
19
- "homepage": "https://github.com/hjhcos/dsh-plugin-discovery#readme",
20
- "bugs": {
21
- "url": "https://github.com/hjhcos/dsh-plugin-discovery/issues"
22
- },
23
- "repository": {
24
- "type": "git",
25
- "url": "git+https://github.com/hjhcos/dsh-plugin-discovery.git"
26
- },
27
- "icon": "./icon.svg",
28
- "exports": {
29
- ".": "./index.js",
30
- "./client": "./client.js",
31
- "./package.json": "./package.json",
32
- "./locale/*.json": "./locale/*.json"
33
- },
34
- "files": [
35
- "index.js",
36
- "mark-remote.js",
37
- "client.js",
38
- "cordis.patch.yml",
39
- "icon.svg",
40
- "data",
41
- "locale",
42
- "skill",
43
- "docs",
44
- "LICENSE",
45
- "README.md"
46
- ],
47
- "dependencies": {
48
- "@deepseek-ai/cordis": "~4.0.4",
49
- "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
50
- "@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
51
- "@deepseek-ai/schemastery": "~3.18.4"
52
- },
53
- "peerDependencies": {
54
- "@deepseek-ai/cordis": "~4.0.4",
55
- "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
56
- "@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
57
- "@deepseek-ai/schemastery": "*"
58
- },
59
- "dsh": {
60
- "bundle": {
61
- "patch": "./cordis.patch.yml"
62
- },
63
- "client": {
64
- "platform": "web",
65
- "immediately": false,
66
- "inject": [
67
- "@deepseek-ai/dsh-client-ui-sidebar",
68
- "@deepseek-ai/dsh-client-ui-layout",
69
- "@deepseek-ai/dsh-client-locale",
70
- "@deepseek-ai/dsh-api-remotes"
71
- ]
72
- }
73
- }
74
- }
1
+ {
2
+ "name": "dsh-plugin-discovery",
3
+ "version": "1.2.5",
4
+ "type": "module",
5
+ "description": "Discover and install community DSH bundles from the sidebar: newbie essentials, recommendations, categories, self-update, and npm/GitHub search.",
6
+ "author": "hjhcos",
7
+ "license": "MIT",
8
+ "keywords": [
9
+ "dsh",
10
+ "dsh-plugin",
11
+ "deepseek",
12
+ "deepseek-harness",
13
+ "plugin",
14
+ "bundle",
15
+ "discovery",
16
+ "marketplace",
17
+ "recommend"
18
+ ],
19
+ "homepage": "https://github.com/hjhcos/dsh-plugin-discovery#readme",
20
+ "bugs": {
21
+ "url": "https://github.com/hjhcos/dsh-plugin-discovery/issues"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/hjhcos/dsh-plugin-discovery.git"
26
+ },
27
+ "icon": "./icon.svg",
28
+ "exports": {
29
+ ".": "./index.js",
30
+ "./client": "./client.js",
31
+ "./package.json": "./package.json",
32
+ "./locale/*.json": "./locale/*.json"
33
+ },
34
+ "files": [
35
+ "index.js",
36
+ "mark-remote.js",
37
+ "client.js",
38
+ "cordis.patch.yml",
39
+ "icon.svg",
40
+ "data",
41
+ "locale",
42
+ "skill",
43
+ "docs",
44
+ "LICENSE",
45
+ "README.md"
46
+ ],
47
+ "dependencies": {
48
+ "@deepseek-ai/cordis": "~4.0.4",
49
+ "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
50
+ "@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
51
+ "@deepseek-ai/schemastery": "~3.18.4"
52
+ },
53
+ "peerDependencies": {
54
+ "@deepseek-ai/cordis": "~4.0.4",
55
+ "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
56
+ "@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
57
+ "@deepseek-ai/schemastery": "*"
58
+ },
59
+ "dsh": {
60
+ "bundle": {
61
+ "patch": "./cordis.patch.yml"
62
+ },
63
+ "client": {
64
+ "platform": "web",
65
+ "immediately": false,
66
+ "inject": [
67
+ "@deepseek-ai/dsh-client-ui-sidebar",
68
+ "@deepseek-ai/dsh-client-ui-layout",
69
+ "@deepseek-ai/dsh-client-locale",
70
+ "@deepseek-ai/dsh-api-remotes"
71
+ ]
72
+ }
73
+ }
74
+ }