dsh-plugin-discovery 1.2.4 → 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.
- package/README.md +2 -1
- package/client.js +154 -7
- package/index.js +39 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ github:hjhcos/dsh-plugin-discovery
|
|
|
52
52
|
|
|
53
53
|
详见 [`CHANGELOG.md`](./CHANGELOG.md)。每次发版会同步推送 GitHub / Gitee 并发布 npm。
|
|
54
54
|
|
|
55
|
-
当前:**1.2.
|
|
55
|
+
当前:**1.2.5** — 构建脚本拦截时可在面板内确认批准并重试;版本不兼容时推荐相近插件。
|
|
56
56
|
|
|
57
57
|
## 功能
|
|
58
58
|
|
|
@@ -62,6 +62,7 @@ github:hjhcos/dsh-plugin-discovery
|
|
|
62
62
|
- **远程目录更新**:优先 Gitee raw JSON,失败再试 GitHub;按 `version` 写入 `$DSH_HOME/cache/...`
|
|
63
63
|
- **插件自更新**:启动/打开面板时检查 npm 最新版(Gitee `package.json` 兜底);有新版本可自动或一键更新
|
|
64
64
|
- **不兼容引导**:安装 peer 不匹配时展示真实错误、复制、风险豁免重试,并推荐精选替代包
|
|
65
|
+
- **构建脚本批准**:pnpm 拦截依赖 install 脚本(如 `ssh2`)时,可在面板确认后代为批准并重试
|
|
65
66
|
- 关键词搜索(全部 / npm / GitHub)与链接解析安装
|
|
66
67
|
- 安装三态:未安装 / 安装中 / 已安装
|
|
67
68
|
- 中英文界面
|
package/client.js
CHANGED
|
@@ -63,6 +63,13 @@ window.__ModuleLoader__.load({
|
|
|
63
63
|
riskAltTitle: "推荐改用兼容插件",
|
|
64
64
|
riskAltDesc: "以下精选包更可能匹配当前 DSH 版本,可直接安装代替强行豁免:",
|
|
65
65
|
riskAltInstall: "安装推荐",
|
|
66
|
+
buildTitle: "需要批准依赖构建脚本",
|
|
67
|
+
buildDesc:
|
|
68
|
+
"安装「{spec}」时,pnpm 拦截了以下依赖的安装脚本(常用于编译原生模块)。批准后将以你的用户权限执行这些脚本,并重试安装。",
|
|
69
|
+
buildAck: "我已了解风险,允许这些依赖运行安装脚本并重试",
|
|
70
|
+
buildCancel: "取消",
|
|
71
|
+
buildConfirm: "批准并重试安装",
|
|
72
|
+
buildWorking: "正在批准并重试…",
|
|
66
73
|
browseRecommend: "为你推荐",
|
|
67
74
|
browseCategory: "分类浏览",
|
|
68
75
|
browseSearch: "在线搜索",
|
|
@@ -157,6 +164,13 @@ window.__ModuleLoader__.load({
|
|
|
157
164
|
riskAltTitle: "Try a compatible plugin instead",
|
|
158
165
|
riskAltDesc: "These curated packages are more likely to match this DSH version:",
|
|
159
166
|
riskAltInstall: "Install recommended",
|
|
167
|
+
buildTitle: "Dependency build scripts need approval",
|
|
168
|
+
buildDesc:
|
|
169
|
+
"Installing “{spec}” was blocked because pnpm ignored build scripts for: the packages listed below (often native modules). Approving runs those scripts as your user and retries the install.",
|
|
170
|
+
buildAck: "I understand and allow these packages to run install scripts",
|
|
171
|
+
buildCancel: "Cancel",
|
|
172
|
+
buildConfirm: "Approve and retry",
|
|
173
|
+
buildWorking: "Approving and retrying…",
|
|
160
174
|
browseRecommend: "For you",
|
|
161
175
|
browseCategory: "Categories",
|
|
162
176
|
browseSearch: "Online search",
|
|
@@ -930,6 +944,10 @@ window.__ModuleLoader__.load({
|
|
|
930
944
|
const [riskOpen, setRiskOpen] = React.useState(false);
|
|
931
945
|
const [riskAck, setRiskAck] = React.useState(false);
|
|
932
946
|
const [riskBusy, setRiskBusy] = React.useState(false);
|
|
947
|
+
const [buildPrompt, setBuildPrompt] = React.useState(null);
|
|
948
|
+
const [buildOpen, setBuildOpen] = React.useState(false);
|
|
949
|
+
const [buildAck, setBuildAck] = React.useState(false);
|
|
950
|
+
const [buildBusy, setBuildBusy] = React.useState(false);
|
|
933
951
|
const [busy, setBusy] = React.useState(false);
|
|
934
952
|
const [resolveInput, setResolveInput] = React.useState("");
|
|
935
953
|
const [installDetail, setInstallDetail] = React.useState("");
|
|
@@ -978,6 +996,24 @@ window.__ModuleLoader__.load({
|
|
|
978
996
|
setRiskAck(false);
|
|
979
997
|
}, [riskBusy]);
|
|
980
998
|
|
|
999
|
+
const openBuildPrompt = React.useCallback((res, item) => {
|
|
1000
|
+
const pending = Array.isArray(res && res.pendingBuilds) ? res.pendingBuilds.filter(Boolean) : [];
|
|
1001
|
+
if (!(res && res.needsBuildApproval) || !pending.length) return;
|
|
1002
|
+
setBuildAck(false);
|
|
1003
|
+
setBuildPrompt({
|
|
1004
|
+
spec: (res && res.installSpec) || (item && (item.installSpec || item.spec)) || "",
|
|
1005
|
+
item: item || null,
|
|
1006
|
+
pendingBuilds: pending,
|
|
1007
|
+
});
|
|
1008
|
+
setBuildOpen(true);
|
|
1009
|
+
}, []);
|
|
1010
|
+
|
|
1011
|
+
const closeBuildPrompt = React.useCallback(() => {
|
|
1012
|
+
if (buildBusy) return;
|
|
1013
|
+
setBuildOpen(false);
|
|
1014
|
+
setBuildAck(false);
|
|
1015
|
+
}, [buildBusy]);
|
|
1016
|
+
|
|
981
1017
|
const refreshInstalled = React.useCallback(async () => {
|
|
982
1018
|
if (!api || typeof api.listInstalled !== "function") return;
|
|
983
1019
|
try {
|
|
@@ -1241,11 +1277,13 @@ window.__ModuleLoader__.load({
|
|
|
1241
1277
|
detail: errText,
|
|
1242
1278
|
});
|
|
1243
1279
|
setError(errText);
|
|
1244
|
-
|
|
1280
|
+
const jobItem = {
|
|
1245
1281
|
spec: job.spec,
|
|
1246
1282
|
installSpec: job.spec,
|
|
1247
1283
|
title: job.packageName || job.spec,
|
|
1248
|
-
}
|
|
1284
|
+
};
|
|
1285
|
+
openBuildPrompt(res, jobItem);
|
|
1286
|
+
openRiskPrompt(res, jobItem);
|
|
1249
1287
|
}
|
|
1250
1288
|
} catch (e) {
|
|
1251
1289
|
if (cancelled) return;
|
|
@@ -1365,11 +1403,13 @@ window.__ModuleLoader__.load({
|
|
|
1365
1403
|
detail: errText,
|
|
1366
1404
|
});
|
|
1367
1405
|
setError(errText);
|
|
1368
|
-
|
|
1406
|
+
const resolveItem = {
|
|
1369
1407
|
spec: res.spec,
|
|
1370
1408
|
installSpec: res.spec,
|
|
1371
1409
|
title: (res.inspection && res.inspection.name) || res.spec,
|
|
1372
|
-
}
|
|
1410
|
+
};
|
|
1411
|
+
openBuildPrompt(installed, resolveItem);
|
|
1412
|
+
openRiskPrompt(installed, resolveItem);
|
|
1373
1413
|
}
|
|
1374
1414
|
} else {
|
|
1375
1415
|
setResults([]);
|
|
@@ -1383,10 +1423,14 @@ window.__ModuleLoader__.load({
|
|
|
1383
1423
|
}
|
|
1384
1424
|
};
|
|
1385
1425
|
|
|
1386
|
-
const onInstall = async (item) => {
|
|
1426
|
+
const onInstall = async (item, installOpts) => {
|
|
1387
1427
|
const spec = item.installSpec || item.spec;
|
|
1428
|
+
const approvedBuilds = Array.isArray(installOpts && installOpts.approvedBuilds)
|
|
1429
|
+
? installOpts.approvedBuilds.filter(Boolean)
|
|
1430
|
+
: [];
|
|
1388
1431
|
const current = resolveItemStatus(item, installedNames, jobs);
|
|
1389
|
-
if (!spec || current.status === "installed"
|
|
1432
|
+
if (!spec || current.status === "installed") return;
|
|
1433
|
+
if (current.status === "installing" && !approvedBuilds.length) return;
|
|
1390
1434
|
const requestId = newRequestId();
|
|
1391
1435
|
upsertJob(requestId, {
|
|
1392
1436
|
status: "installing",
|
|
@@ -1399,7 +1443,10 @@ window.__ModuleLoader__.load({
|
|
|
1399
1443
|
setMessage("");
|
|
1400
1444
|
setError("");
|
|
1401
1445
|
try {
|
|
1402
|
-
const res = await api.install(spec, {
|
|
1446
|
+
const res = await api.install(spec, {
|
|
1447
|
+
requestId,
|
|
1448
|
+
...(approvedBuilds.length ? { approvedBuilds } : {}),
|
|
1449
|
+
});
|
|
1403
1450
|
if (res && res.alreadyInstalled) {
|
|
1404
1451
|
finishJob(requestId, {
|
|
1405
1452
|
status: "installed",
|
|
@@ -1426,6 +1473,7 @@ window.__ModuleLoader__.load({
|
|
|
1426
1473
|
detail: errText,
|
|
1427
1474
|
});
|
|
1428
1475
|
setError(errText);
|
|
1476
|
+
openBuildPrompt(res, item);
|
|
1429
1477
|
openRiskPrompt(res, item);
|
|
1430
1478
|
}
|
|
1431
1479
|
} catch (e) {
|
|
@@ -1476,6 +1524,29 @@ window.__ModuleLoader__.load({
|
|
|
1476
1524
|
}
|
|
1477
1525
|
};
|
|
1478
1526
|
|
|
1527
|
+
const confirmBuildInstall = async () => {
|
|
1528
|
+
if (!buildPrompt || !buildAck || !api) return;
|
|
1529
|
+
const pending = Array.isArray(buildPrompt.pendingBuilds) ? buildPrompt.pendingBuilds.filter(Boolean) : [];
|
|
1530
|
+
if (!pending.length) return;
|
|
1531
|
+
setBuildBusy(true);
|
|
1532
|
+
setMessage("");
|
|
1533
|
+
try {
|
|
1534
|
+
const item = buildPrompt.item || {
|
|
1535
|
+
spec: buildPrompt.spec,
|
|
1536
|
+
installSpec: buildPrompt.spec,
|
|
1537
|
+
title: buildPrompt.spec,
|
|
1538
|
+
};
|
|
1539
|
+
setBuildOpen(false);
|
|
1540
|
+
setBuildAck(false);
|
|
1541
|
+
setError("");
|
|
1542
|
+
await onInstall(item, { approvedBuilds: pending });
|
|
1543
|
+
} catch (e) {
|
|
1544
|
+
setError((e && e.message) || String(e));
|
|
1545
|
+
} finally {
|
|
1546
|
+
setBuildBusy(false);
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
|
|
1479
1550
|
const installRecommendedAlt = async (alt) => {
|
|
1480
1551
|
if (!alt || riskBusy) return;
|
|
1481
1552
|
const item = {
|
|
@@ -1959,6 +2030,78 @@ window.__ModuleLoader__.load({
|
|
|
1959
2030
|
: null,
|
|
1960
2031
|
),
|
|
1961
2032
|
),
|
|
2033
|
+
buildOpen && buildPrompt
|
|
2034
|
+
? h(
|
|
2035
|
+
"div",
|
|
2036
|
+
{
|
|
2037
|
+
style: styles.riskOverlay,
|
|
2038
|
+
onClick: (e) => {
|
|
2039
|
+
if (e.target === e.currentTarget) closeBuildPrompt();
|
|
2040
|
+
},
|
|
2041
|
+
},
|
|
2042
|
+
h(
|
|
2043
|
+
"div",
|
|
2044
|
+
{
|
|
2045
|
+
style: styles.riskDialog,
|
|
2046
|
+
role: "dialog",
|
|
2047
|
+
"aria-modal": "true",
|
|
2048
|
+
onClick: (e) => e.stopPropagation(),
|
|
2049
|
+
},
|
|
2050
|
+
h("h2", { style: styles.riskTitle }, t("buildTitle")),
|
|
2051
|
+
h(
|
|
2052
|
+
"p",
|
|
2053
|
+
{ style: styles.riskDesc },
|
|
2054
|
+
format(t("buildDesc"), { spec: buildPrompt.spec || "" }),
|
|
2055
|
+
),
|
|
2056
|
+
h(
|
|
2057
|
+
"div",
|
|
2058
|
+
{ style: styles.riskAltBox },
|
|
2059
|
+
h(
|
|
2060
|
+
"div",
|
|
2061
|
+
{ style: styles.riskAltList },
|
|
2062
|
+
...(Array.isArray(buildPrompt.pendingBuilds) ? buildPrompt.pendingBuilds : []).map(
|
|
2063
|
+
(name) =>
|
|
2064
|
+
h("div", { key: name, style: styles.riskAltSpec }, name),
|
|
2065
|
+
),
|
|
2066
|
+
),
|
|
2067
|
+
),
|
|
2068
|
+
h(
|
|
2069
|
+
"label",
|
|
2070
|
+
{ style: styles.riskAckRow },
|
|
2071
|
+
h("input", {
|
|
2072
|
+
type: "checkbox",
|
|
2073
|
+
checked: buildAck,
|
|
2074
|
+
disabled: buildBusy,
|
|
2075
|
+
onChange: (e) => setBuildAck(!!e.target.checked),
|
|
2076
|
+
}),
|
|
2077
|
+
h("span", null, t("buildAck")),
|
|
2078
|
+
),
|
|
2079
|
+
h(
|
|
2080
|
+
"div",
|
|
2081
|
+
{ style: styles.riskFooter },
|
|
2082
|
+
h(
|
|
2083
|
+
Btn,
|
|
2084
|
+
{
|
|
2085
|
+
size: "sm",
|
|
2086
|
+
variant: "outline",
|
|
2087
|
+
disabled: buildBusy,
|
|
2088
|
+
onClick: closeBuildPrompt,
|
|
2089
|
+
},
|
|
2090
|
+
t("buildCancel"),
|
|
2091
|
+
),
|
|
2092
|
+
h(
|
|
2093
|
+
Btn,
|
|
2094
|
+
{
|
|
2095
|
+
size: "sm",
|
|
2096
|
+
disabled: buildBusy || !buildAck,
|
|
2097
|
+
onClick: () => void confirmBuildInstall(),
|
|
2098
|
+
},
|
|
2099
|
+
buildBusy ? t("buildWorking") : t("buildConfirm"),
|
|
2100
|
+
),
|
|
2101
|
+
),
|
|
2102
|
+
),
|
|
2103
|
+
)
|
|
2104
|
+
: null,
|
|
1962
2105
|
riskOpen && riskPrompt
|
|
1963
2106
|
? h(
|
|
1964
2107
|
"div",
|
|
@@ -2324,6 +2467,8 @@ window.__ModuleLoader__.load({
|
|
|
2324
2467
|
code: value && value.code,
|
|
2325
2468
|
incompatible: value && value.incompatible,
|
|
2326
2469
|
needsRiskExemption: !!(value && value.needsRiskExemption),
|
|
2470
|
+
pendingBuilds: value && value.pendingBuilds,
|
|
2471
|
+
needsBuildApproval: !!(value && value.needsBuildApproval),
|
|
2327
2472
|
recommendations: value && value.recommendations,
|
|
2328
2473
|
installSpec: (value && value.installSpec) || spec,
|
|
2329
2474
|
};
|
|
@@ -2368,6 +2513,8 @@ window.__ModuleLoader__.load({
|
|
|
2368
2513
|
code: value && value.code,
|
|
2369
2514
|
incompatible: value && value.incompatible,
|
|
2370
2515
|
needsRiskExemption: !!(value && value.needsRiskExemption),
|
|
2516
|
+
pendingBuilds: value && value.pendingBuilds,
|
|
2517
|
+
needsBuildApproval: !!(value && value.needsBuildApproval),
|
|
2371
2518
|
recommendations: value && value.recommendations,
|
|
2372
2519
|
installSpec: value && value.installSpec,
|
|
2373
2520
|
packageName: value && value.packageName,
|
package/index.js
CHANGED
|
@@ -501,6 +501,28 @@ function extractIncompatibility(result) {
|
|
|
501
501
|
};
|
|
502
502
|
}
|
|
503
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
|
+
|
|
504
526
|
/** 安装失败时从精选目录推荐相近/同标签插件,优先给出可替代的当前生态包。 */
|
|
505
527
|
function buildFailureRecommendations(catalog, failedSpec, limit = 4) {
|
|
506
528
|
const failed = String(failedSpec || "").trim();
|
|
@@ -1269,10 +1291,12 @@ class PluginDiscoveryService extends TypertRemoteService {
|
|
|
1269
1291
|
}
|
|
1270
1292
|
return `${text}。可改试精选目录中的相近插件:${alts.join("、")}`;
|
|
1271
1293
|
};
|
|
1294
|
+
const approvedBuilds = normalizeApprovedBuilds(opts.approvedBuilds);
|
|
1272
1295
|
try {
|
|
1273
1296
|
const result = await this.ctx.pluginManager.installBundle(trimmed, {
|
|
1274
1297
|
requestId,
|
|
1275
1298
|
enabled: true,
|
|
1299
|
+
...(approvedBuilds.length ? { approvedBuilds } : {}),
|
|
1276
1300
|
});
|
|
1277
1301
|
const packageName = result?.bundle || pre.name || trimmed;
|
|
1278
1302
|
const application = result?.application;
|
|
@@ -1281,6 +1305,7 @@ class PluginDiscoveryService extends TypertRemoteService {
|
|
|
1281
1305
|
? formatChangeFailure(result, application || "failed")
|
|
1282
1306
|
: `「${packageName}」安装完成(${application || "applied"} / ${result?.stage || "install"})`;
|
|
1283
1307
|
const incompat = failed ? extractIncompatibility(result) : {};
|
|
1308
|
+
const buildBlock = failed ? extractBuildBlock(result) : {};
|
|
1284
1309
|
const recommendations =
|
|
1285
1310
|
failed && (incompat.needsRiskExemption || incompat.code === "incompatible-version")
|
|
1286
1311
|
? buildFailureRecommendations(this.getCatalog(), trimmed, 4)
|
|
@@ -1299,9 +1324,15 @@ class PluginDiscoveryService extends TypertRemoteService {
|
|
|
1299
1324
|
message,
|
|
1300
1325
|
detail: failed ? rawMessage : undefined,
|
|
1301
1326
|
logPath: failed ? result?.packageResult?.logPath : undefined,
|
|
1302
|
-
code:
|
|
1327
|
+
code:
|
|
1328
|
+
incompat.code ||
|
|
1329
|
+
(buildBlock.needsBuildApproval ? "build-blocked" : undefined) ||
|
|
1330
|
+
undefined,
|
|
1303
1331
|
incompatible: incompat.incompatible || undefined,
|
|
1304
1332
|
needsRiskExemption: incompat.needsRiskExemption === true,
|
|
1333
|
+
pendingBuilds: buildBlock.pendingBuilds || undefined,
|
|
1334
|
+
needsBuildApproval: buildBlock.needsBuildApproval === true,
|
|
1335
|
+
approvedBuilds: approvedBuilds.length ? approvedBuilds : result?.approvedBuilds,
|
|
1305
1336
|
recommendations,
|
|
1306
1337
|
installSpec: trimmed,
|
|
1307
1338
|
result,
|
|
@@ -1371,6 +1402,7 @@ class PluginDiscoveryService extends TypertRemoteService {
|
|
|
1371
1402
|
const failed = settled.application === "failed" || settled.application === "cancelled";
|
|
1372
1403
|
const failText = failed ? formatChangeFailure(settled, settled.application) : "";
|
|
1373
1404
|
const incompat = failed ? extractIncompatibility(settled) : {};
|
|
1405
|
+
const buildBlock = failed ? extractBuildBlock(settled) : {};
|
|
1374
1406
|
const failedSpec =
|
|
1375
1407
|
(Array.isArray(incompat.incompatible) && incompat.incompatible[0]
|
|
1376
1408
|
? `${incompat.incompatible[0].name}@${incompat.incompatible[0].version}`
|
|
@@ -1395,9 +1427,14 @@ class PluginDiscoveryService extends TypertRemoteService {
|
|
|
1395
1427
|
message,
|
|
1396
1428
|
detail: failed ? failText : undefined,
|
|
1397
1429
|
logPath: failed ? settled?.packageResult?.logPath : undefined,
|
|
1398
|
-
code:
|
|
1430
|
+
code:
|
|
1431
|
+
incompat.code ||
|
|
1432
|
+
(buildBlock.needsBuildApproval ? "build-blocked" : undefined) ||
|
|
1433
|
+
undefined,
|
|
1399
1434
|
incompatible: incompat.incompatible || undefined,
|
|
1400
1435
|
needsRiskExemption: incompat.needsRiskExemption === true,
|
|
1436
|
+
pendingBuilds: buildBlock.pendingBuilds || undefined,
|
|
1437
|
+
needsBuildApproval: buildBlock.needsBuildApproval === true,
|
|
1401
1438
|
recommendations,
|
|
1402
1439
|
installSpec: failedSpec || undefined,
|
|
1403
1440
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-discovery",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Discover and install community DSH bundles from the sidebar: newbie essentials, recommendations, categories, self-update, and npm/GitHub search.",
|
|
6
6
|
"author": "hjhcos",
|