dsh-plugin-discovery 1.2.0 → 1.2.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.
Files changed (5) hide show
  1. package/README.md +103 -96
  2. package/client.js +496 -21
  3. package/data/recommended.json +1206 -1206
  4. package/index.js +281 -14
  5. package/package.json +74 -74
package/client.js CHANGED
@@ -49,6 +49,20 @@ window.__ModuleLoader__.load({
49
49
  installLog: "安装输出:{text}",
50
50
  installFailed: "安装失败",
51
51
  retry: "重试",
52
+ copy: "复制",
53
+ copied: "已复制",
54
+ copyFailed: "复制失败",
55
+ riskInstall: "承担风险安装",
56
+ riskTitle: "版本不兼容,确认风险后安装?",
57
+ riskDesc:
58
+ "插件 {package} 与当前 DSH {runtime} 声明的 peer 不匹配。强行安装可能导致崩溃或数据丢失。仅在你明确愿意承担该风险时继续。",
59
+ riskAck: "我已了解风险,仍要为这对精确版本授予豁免并重试安装",
60
+ riskCancel: "取消",
61
+ riskConfirm: "确认豁免并安装",
62
+ riskWorking: "正在授权并重试…",
63
+ riskAltTitle: "推荐改用兼容插件",
64
+ riskAltDesc: "以下精选包更可能匹配当前 DSH 版本,可直接安装代替强行豁免:",
65
+ riskAltInstall: "安装推荐",
52
66
  browseRecommend: "为你推荐",
53
67
  browseCategory: "分类浏览",
54
68
  browseSearch: "在线搜索",
@@ -129,6 +143,20 @@ window.__ModuleLoader__.load({
129
143
  installLog: "Output: {text}",
130
144
  installFailed: "Install failed",
131
145
  retry: "Retry",
146
+ copy: "Copy",
147
+ copied: "Copied",
148
+ copyFailed: "Copy failed",
149
+ riskInstall: "Install anyway",
150
+ riskTitle: "Incompatible version — install at your own risk?",
151
+ riskDesc:
152
+ "Plugin {package} does not match peers for DSH {runtime}. Forcing install may crash or lose data. Continue only if you accept that risk.",
153
+ riskAck: "I understand the risk and grant an exact-version exemption for this pair",
154
+ riskCancel: "Cancel",
155
+ riskConfirm: "Grant exemption and install",
156
+ riskWorking: "Granting and retrying…",
157
+ riskAltTitle: "Try a compatible plugin instead",
158
+ riskAltDesc: "These curated packages are more likely to match this DSH version:",
159
+ riskAltInstall: "Install recommended",
132
160
  browseRecommend: "For you",
133
161
  browseCategory: "Categories",
134
162
  browseSearch: "Online search",
@@ -593,7 +621,7 @@ window.__ModuleLoader__.load({
593
621
  },
594
622
  errorBox: {
595
623
  display: "flex",
596
- alignItems: "center",
624
+ alignItems: "flex-start",
597
625
  justifyContent: "space-between",
598
626
  gap: "12px",
599
627
  padding: "12px 14px",
@@ -602,6 +630,115 @@ window.__ModuleLoader__.load({
602
630
  background: "var(--dsw-alias-settings-card-fill, #ffffff)",
603
631
  color: "var(--dsw-alias-state-warn-primary, var(--dsw-alias-label-secondary))",
604
632
  fontSize: "13px",
633
+ lineHeight: "20px",
634
+ },
635
+ errorText: {
636
+ flex: 1,
637
+ minWidth: 0,
638
+ whiteSpace: "pre-wrap",
639
+ wordBreak: "break-word",
640
+ overflowWrap: "anywhere",
641
+ },
642
+ errorActions: {
643
+ display: "flex",
644
+ alignItems: "center",
645
+ gap: "8px",
646
+ flexShrink: 0,
647
+ flexWrap: "wrap",
648
+ justifyContent: "flex-end",
649
+ },
650
+ riskOverlay: {
651
+ position: "fixed",
652
+ inset: 0,
653
+ zIndex: 10000,
654
+ display: "flex",
655
+ alignItems: "center",
656
+ justifyContent: "center",
657
+ padding: "24px",
658
+ background: "rgba(15, 23, 42, 0.45)",
659
+ },
660
+ riskDialog: {
661
+ width: "min(520px, 100%)",
662
+ maxHeight: "90vh",
663
+ overflow: "auto",
664
+ borderRadius: "var(--dsw-radius-md, 12px)",
665
+ border: "1px solid var(--dsw-alias-border-l2, #d0d5dd)",
666
+ background: "var(--dsw-alias-settings-card-fill, #ffffff)",
667
+ color: "var(--dsw-alias-label-primary)",
668
+ padding: "20px",
669
+ boxShadow: "0 16px 40px rgba(15, 23, 42, 0.18)",
670
+ },
671
+ riskTitle: {
672
+ margin: "0 0 10px",
673
+ fontSize: "16px",
674
+ fontWeight: 600,
675
+ lineHeight: "24px",
676
+ },
677
+ riskDesc: {
678
+ margin: "0 0 14px",
679
+ fontSize: "13px",
680
+ lineHeight: "20px",
681
+ color: "var(--dsw-alias-label-secondary)",
682
+ whiteSpace: "pre-wrap",
683
+ },
684
+ riskAckRow: {
685
+ display: "flex",
686
+ alignItems: "flex-start",
687
+ gap: "10px",
688
+ margin: "0 0 16px",
689
+ fontSize: "13px",
690
+ lineHeight: "20px",
691
+ },
692
+ riskAltBox: {
693
+ margin: "0 0 16px",
694
+ padding: "12px",
695
+ borderRadius: "var(--dsw-radius-sm, 8px)",
696
+ border: "1px solid var(--dsw-alias-border-l2, #d0d5dd)",
697
+ background: "var(--dsw-alias-settings-card-fill-secondary, #f8fafc)",
698
+ },
699
+ riskAltTitle: {
700
+ margin: "0 0 6px",
701
+ fontSize: "13px",
702
+ fontWeight: 600,
703
+ lineHeight: "20px",
704
+ },
705
+ riskAltDesc: {
706
+ margin: "0 0 10px",
707
+ fontSize: "12px",
708
+ lineHeight: "18px",
709
+ color: "var(--dsw-alias-label-secondary)",
710
+ },
711
+ riskAltList: {
712
+ display: "flex",
713
+ flexDirection: "column",
714
+ gap: "8px",
715
+ },
716
+ riskAltRow: {
717
+ display: "flex",
718
+ alignItems: "center",
719
+ justifyContent: "space-between",
720
+ gap: "10px",
721
+ },
722
+ riskAltMeta: {
723
+ minWidth: 0,
724
+ flex: 1,
725
+ },
726
+ riskAltSpec: {
727
+ fontSize: "13px",
728
+ fontWeight: 500,
729
+ lineHeight: "18px",
730
+ wordBreak: "break-all",
731
+ },
732
+ riskAltHint: {
733
+ fontSize: "12px",
734
+ lineHeight: "16px",
735
+ color: "var(--dsw-alias-label-secondary)",
736
+ },
737
+ riskFooter: {
738
+ display: "flex",
739
+ justifyContent: "flex-end",
740
+ gap: "8px",
741
+ flexWrap: "wrap",
605
742
  },
606
743
  updateBox: {
607
744
  display: "flex",
@@ -751,12 +888,25 @@ window.__ModuleLoader__.load({
751
888
  const err = result.error;
752
889
  return {
753
890
  ok: false,
754
- message: (err && (err.message || err.code)) || fallbackMessage,
891
+ message: (err && (err.message || err.diagnostic || err.code)) || fallbackMessage,
755
892
  };
756
893
  }
757
894
  return result.value;
758
895
  }
759
896
 
897
+ function pickInstallError(res, fallback, lastDetail) {
898
+ const primary = String((res && (res.detail || res.message)) || "").trim();
899
+ const weak =
900
+ !primary ||
901
+ /^install failed$/i.test(primary) ||
902
+ /^安装失败([((]failed[))])?$/i.test(primary);
903
+ if (!weak) return primary;
904
+ const extra = String(lastDetail || "").trim();
905
+ const title = fallback || "安装失败";
906
+ if (extra && extra !== primary) return `${title}\n${extra}`;
907
+ return primary || title;
908
+ }
909
+
760
910
  function Panel(props) {
761
911
  const api = props.api;
762
912
  const t = props.t;
@@ -775,6 +925,11 @@ window.__ModuleLoader__.load({
775
925
  const [results, setResults] = React.useState([]);
776
926
  const [message, setMessage] = React.useState("");
777
927
  const [error, setError] = React.useState("");
928
+ const [copyHint, setCopyHint] = React.useState("");
929
+ const [riskPrompt, setRiskPrompt] = React.useState(null);
930
+ const [riskOpen, setRiskOpen] = React.useState(false);
931
+ const [riskAck, setRiskAck] = React.useState(false);
932
+ const [riskBusy, setRiskBusy] = React.useState(false);
778
933
  const [busy, setBusy] = React.useState(false);
779
934
  const [resolveInput, setResolveInput] = React.useState("");
780
935
  const [installDetail, setInstallDetail] = React.useState("");
@@ -783,6 +938,7 @@ window.__ModuleLoader__.load({
783
938
  const [page, setPage] = React.useState(1);
784
939
  const jobsRef = React.useRef(jobs);
785
940
  const installedNamesRef = React.useRef(installedNames);
941
+ const installDetailRef = React.useRef("");
786
942
  React.useEffect(() => {
787
943
  jobsRef.current = jobs;
788
944
  writeJobCache(jobs);
@@ -790,6 +946,37 @@ window.__ModuleLoader__.load({
790
946
  React.useEffect(() => {
791
947
  installedNamesRef.current = installedNames;
792
948
  }, [installedNames]);
949
+ React.useEffect(() => {
950
+ installDetailRef.current = installDetail;
951
+ }, [installDetail]);
952
+ React.useEffect(() => {
953
+ setCopyHint("");
954
+ }, [error]);
955
+
956
+ const openRiskPrompt = React.useCallback((res, item) => {
957
+ const rows = Array.isArray(res && res.incompatible) ? res.incompatible : [];
958
+ const first = rows[0];
959
+ if (!(res && res.needsRiskExemption) || !first) return;
960
+ const packageVersion = `${first.name}@${first.version}`;
961
+ const runtimeVersion = first.runtimeVersion || "unknown";
962
+ const recommendations = Array.isArray(res && res.recommendations) ? res.recommendations : [];
963
+ setRiskAck(false);
964
+ setRiskPrompt({
965
+ spec: (res && res.installSpec) || (item && (item.installSpec || item.spec)) || first.name,
966
+ item: item || null,
967
+ packageVersion,
968
+ runtimeVersion,
969
+ incompatible: rows,
970
+ recommendations,
971
+ });
972
+ setRiskOpen(true);
973
+ }, []);
974
+
975
+ const closeRiskPrompt = React.useCallback(() => {
976
+ if (riskBusy) return;
977
+ setRiskOpen(false);
978
+ setRiskAck(false);
979
+ }, [riskBusy]);
793
980
 
794
981
  const refreshInstalled = React.useCallback(async () => {
795
982
  if (!api || typeof api.listInstalled !== "function") return;
@@ -1045,18 +1232,36 @@ window.__ModuleLoader__.load({
1045
1232
  setMessage(res.message || t("installed"));
1046
1233
  await refreshInstalled();
1047
1234
  } else {
1235
+ const lastDetail =
1236
+ (jobsRef.current[job.requestId] && jobsRef.current[job.requestId].detail) ||
1237
+ installDetailRef.current;
1238
+ const errText = pickInstallError(res, t("installFailed"), lastDetail);
1048
1239
  finishJob(job.requestId, {
1049
1240
  status: "failed",
1050
- detail: (res && res.message) || t("installFailed"),
1241
+ detail: errText,
1242
+ });
1243
+ setError(errText);
1244
+ openRiskPrompt(res, {
1245
+ spec: job.spec,
1246
+ installSpec: job.spec,
1247
+ title: job.packageName || job.spec,
1051
1248
  });
1052
- setError((res && res.message) || t("installFailed"));
1053
1249
  }
1054
1250
  } catch (e) {
1055
1251
  if (cancelled) return;
1252
+ const lastDetail =
1253
+ (jobsRef.current[job.requestId] && jobsRef.current[job.requestId].detail) ||
1254
+ installDetailRef.current;
1255
+ const errText = pickInstallError(
1256
+ { message: (e && e.message) || String(e) },
1257
+ t("installFailed"),
1258
+ lastDetail,
1259
+ );
1056
1260
  finishJob(job.requestId, {
1057
1261
  status: "failed",
1058
- detail: (e && e.message) || String(e),
1262
+ detail: errText,
1059
1263
  });
1264
+ setError(errText);
1060
1265
  } finally {
1061
1266
  if (!cancelled) setInstallDetail("");
1062
1267
  }
@@ -1151,11 +1356,20 @@ window.__ModuleLoader__.load({
1151
1356
  setMessage(installed.message || t("installed"));
1152
1357
  await refreshInstalled();
1153
1358
  } else {
1359
+ const lastDetail =
1360
+ (jobsRef.current[requestId] && jobsRef.current[requestId].detail) ||
1361
+ installDetailRef.current;
1362
+ const errText = pickInstallError(installed, t("installFailed"), lastDetail);
1154
1363
  finishJob(requestId, {
1155
1364
  status: "failed",
1156
- detail: (installed && installed.message) || t("inspectFailed"),
1365
+ detail: errText,
1366
+ });
1367
+ setError(errText);
1368
+ openRiskPrompt(installed, {
1369
+ spec: res.spec,
1370
+ installSpec: res.spec,
1371
+ title: (res.inspection && res.inspection.name) || res.spec,
1157
1372
  });
1158
- setError((installed && installed.message) || t("inspectFailed"));
1159
1373
  }
1160
1374
  } else {
1161
1375
  setResults([]);
@@ -1204,23 +1418,79 @@ window.__ModuleLoader__.load({
1204
1418
  await refreshInstalled();
1205
1419
  setMessage(res.message || t("installed"));
1206
1420
  } else {
1421
+ const lastDetail =
1422
+ (jobsRef.current[requestId] && jobsRef.current[requestId].detail) || installDetailRef.current;
1423
+ const errText = pickInstallError(res, t("installFailed"), lastDetail);
1207
1424
  finishJob(requestId, {
1208
1425
  status: "failed",
1209
- detail: (res && res.message) || t("installFailed"),
1426
+ detail: errText,
1210
1427
  });
1211
- setError((res && res.message) || t("installFailed"));
1428
+ setError(errText);
1429
+ openRiskPrompt(res, item);
1212
1430
  }
1213
1431
  } catch (e) {
1432
+ const lastDetail =
1433
+ (jobsRef.current[requestId] && jobsRef.current[requestId].detail) || installDetailRef.current;
1434
+ const errText = pickInstallError(
1435
+ { message: (e && e.message) || String(e) },
1436
+ t("installFailed"),
1437
+ lastDetail,
1438
+ );
1214
1439
  finishJob(requestId, {
1215
1440
  status: "failed",
1216
- detail: (e && e.message) || String(e),
1441
+ detail: errText,
1217
1442
  });
1218
- setError((e && e.message) || String(e));
1443
+ setError(errText);
1219
1444
  } finally {
1220
1445
  setInstallDetail("");
1221
1446
  }
1222
1447
  };
1223
1448
 
1449
+ const confirmRiskInstall = async () => {
1450
+ if (!riskPrompt || !riskAck || !api || typeof api.grantVersionExemption !== "function") return;
1451
+ setRiskBusy(true);
1452
+ setMessage("");
1453
+ try {
1454
+ const granted = await api.grantVersionExemption(
1455
+ riskPrompt.packageVersion,
1456
+ riskPrompt.runtimeVersion,
1457
+ true,
1458
+ );
1459
+ if (!granted || !granted.ok) {
1460
+ setError((granted && granted.message) || t("installFailed"));
1461
+ return;
1462
+ }
1463
+ const item = riskPrompt.item || {
1464
+ spec: riskPrompt.spec,
1465
+ installSpec: riskPrompt.spec,
1466
+ title: riskPrompt.packageVersion,
1467
+ };
1468
+ setRiskOpen(false);
1469
+ setRiskAck(false);
1470
+ setError("");
1471
+ await onInstall(item);
1472
+ } catch (e) {
1473
+ setError((e && e.message) || String(e));
1474
+ } finally {
1475
+ setRiskBusy(false);
1476
+ }
1477
+ };
1478
+
1479
+ const installRecommendedAlt = async (alt) => {
1480
+ if (!alt || riskBusy) return;
1481
+ const item = {
1482
+ spec: alt.installSpec || alt.spec,
1483
+ installSpec: alt.installSpec || alt.spec,
1484
+ title: alt.title || alt.spec,
1485
+ source: alt.source || "catalog",
1486
+ catalogId: alt.catalogId,
1487
+ };
1488
+ setRiskOpen(false);
1489
+ setRiskAck(false);
1490
+ setError("");
1491
+ await onInstall(item);
1492
+ };
1493
+
1224
1494
  const sourceOptions = [
1225
1495
  { value: "all", label: t("sourceAll") },
1226
1496
  { value: "npm", label: t("sourceNpm") },
@@ -1334,6 +1604,9 @@ window.__ModuleLoader__.load({
1334
1604
  : t("selfUpdateOriginNpm");
1335
1605
 
1336
1606
  return h(
1607
+ React.Fragment,
1608
+ null,
1609
+ h(
1337
1610
  "div",
1338
1611
  { style: styles.page },
1339
1612
  h(
@@ -1501,16 +1774,67 @@ window.__ModuleLoader__.load({
1501
1774
  ? h(
1502
1775
  "div",
1503
1776
  { style: styles.errorBox },
1504
- h("span", null, error),
1777
+ h("div", { style: styles.errorText }, error),
1505
1778
  h(
1506
- Btn,
1507
- {
1508
- size: "sm",
1509
- variant: "outline",
1510
- disabled: busy,
1511
- onClick: onSearch,
1512
- },
1513
- t("retry"),
1779
+ "div",
1780
+ { style: styles.errorActions },
1781
+ h(
1782
+ Btn,
1783
+ {
1784
+ size: "sm",
1785
+ variant: "outline",
1786
+ disabled: busy || riskBusy,
1787
+ onClick: async () => {
1788
+ const text = String(error || "");
1789
+ try {
1790
+ if (navigator.clipboard && navigator.clipboard.writeText) {
1791
+ await navigator.clipboard.writeText(text);
1792
+ } else {
1793
+ const ta = document.createElement("textarea");
1794
+ ta.value = text;
1795
+ ta.setAttribute("readonly", "");
1796
+ ta.style.position = "fixed";
1797
+ ta.style.left = "-9999px";
1798
+ document.body.appendChild(ta);
1799
+ ta.select();
1800
+ document.execCommand("copy");
1801
+ document.body.removeChild(ta);
1802
+ }
1803
+ setCopyHint(t("copied"));
1804
+ window.setTimeout(() => setCopyHint(""), 1600);
1805
+ } catch {
1806
+ setCopyHint(t("copyFailed"));
1807
+ window.setTimeout(() => setCopyHint(""), 1600);
1808
+ }
1809
+ },
1810
+ },
1811
+ copyHint || t("copy"),
1812
+ ),
1813
+ riskPrompt
1814
+ ? h(
1815
+ Btn,
1816
+ {
1817
+ size: "sm",
1818
+ variant: "outline",
1819
+ disabled: busy || riskBusy,
1820
+ onClick: () => {
1821
+ setRiskAck(false);
1822
+ setRiskOpen(true);
1823
+ },
1824
+ },
1825
+ t("riskInstall"),
1826
+ )
1827
+ : null,
1828
+ h(
1829
+ Btn,
1830
+ {
1831
+ size: "sm",
1832
+ variant: "outline",
1833
+ disabled: busy || riskBusy,
1834
+ onClick: onSearch,
1835
+ },
1836
+ t("retry"),
1837
+ ),
1514
1838
  ),
1515
1839
  )
1516
1840
  : null,
@@ -1634,6 +1958,110 @@ window.__ModuleLoader__.load({
1634
1958
  )
1635
1959
  : null,
1636
1960
  ),
1961
+ ),
1962
+ riskOpen && riskPrompt
1963
+ ? h(
1964
+ "div",
1965
+ {
1966
+ style: styles.riskOverlay,
1967
+ onClick: (e) => {
1968
+ if (e.target === e.currentTarget) closeRiskPrompt();
1969
+ },
1970
+ },
1971
+ h(
1972
+ "div",
1973
+ {
1974
+ style: styles.riskDialog,
1975
+ role: "dialog",
1976
+ "aria-modal": "true",
1977
+ onClick: (e) => e.stopPropagation(),
1978
+ },
1979
+ h("h2", { style: styles.riskTitle }, t("riskTitle")),
1980
+ h(
1981
+ "p",
1982
+ { style: styles.riskDesc },
1983
+ format(t("riskDesc"), {
1984
+ package: riskPrompt.packageVersion,
1985
+ runtime: riskPrompt.runtimeVersion,
1986
+ }),
1987
+ ),
1988
+ Array.isArray(riskPrompt.recommendations) && riskPrompt.recommendations.length
1989
+ ? h(
1990
+ "div",
1991
+ { style: styles.riskAltBox },
1992
+ h("div", { style: styles.riskAltTitle }, t("riskAltTitle")),
1993
+ h("p", { style: styles.riskAltDesc }, t("riskAltDesc")),
1994
+ h(
1995
+ "div",
1996
+ { style: styles.riskAltList },
1997
+ ...riskPrompt.recommendations.map((alt) =>
1998
+ h(
1999
+ "div",
2000
+ { key: alt.spec, style: styles.riskAltRow },
2001
+ h(
2002
+ "div",
2003
+ { style: styles.riskAltMeta },
2004
+ h("div", { style: styles.riskAltSpec }, alt.title || alt.spec),
2005
+ h(
2006
+ "div",
2007
+ { style: styles.riskAltHint },
2008
+ alt.spec === (alt.title || "")
2009
+ ? alt.reason || ""
2010
+ : alt.spec,
2011
+ ),
2012
+ ),
2013
+ h(
2014
+ Btn,
2015
+ {
2016
+ size: "sm",
2017
+ variant: "outline",
2018
+ disabled: riskBusy,
2019
+ onClick: () => void installRecommendedAlt(alt),
2020
+ },
2021
+ t("riskAltInstall"),
2022
+ ),
2023
+ ),
2024
+ ),
2025
+ ),
2026
+ )
2027
+ : null,
2028
+ h(
2029
+ "label",
2030
+ { style: styles.riskAckRow },
2031
+ h("input", {
2032
+ type: "checkbox",
2033
+ checked: riskAck,
2034
+ disabled: riskBusy,
2035
+ onChange: (e) => setRiskAck(!!e.target.checked),
2036
+ }),
2037
+ h("span", null, t("riskAck")),
2038
+ ),
2039
+ h(
2040
+ "div",
2041
+ { style: styles.riskFooter },
2042
+ h(
2043
+ Btn,
2044
+ {
2045
+ size: "sm",
2046
+ variant: "outline",
2047
+ disabled: riskBusy,
2048
+ onClick: closeRiskPrompt,
2049
+ },
2050
+ t("riskCancel"),
2051
+ ),
2052
+ h(
2053
+ Btn,
2054
+ {
2055
+ size: "sm",
2056
+ disabled: riskBusy || !riskAck,
2057
+ onClick: () => void confirmRiskInstall(),
2058
+ },
2059
+ riskBusy ? t("riskWorking") : t("riskConfirm"),
2060
+ ),
2061
+ ),
2062
+ ),
2063
+ )
2064
+ : null,
1637
2065
  );
1638
2066
  }
1639
2067
 
@@ -1651,6 +2079,10 @@ window.__ModuleLoader__.load({
1651
2079
  const SPEC_CODEC = strictJson("dsh-plugin-discovery#pluginDiscovery/installPlugin:spec");
1652
2080
  const INSTALL_OPTIONS_CODEC = strictJson("dsh-plugin-discovery#pluginDiscovery/installPlugin:options");
1653
2081
  const INSTALL_RESULT = strictJson("dsh-plugin-discovery#pluginDiscovery/installPlugin:result");
2082
+ const GRANT_PACKAGE = strictJson("dsh-plugin-discovery#pluginDiscovery/grantVersionExemption:packageVersion");
2083
+ const GRANT_RUNTIME = strictJson("dsh-plugin-discovery#pluginDiscovery/grantVersionExemption:runtimeVersion");
2084
+ const GRANT_ACCEPT = strictJson("dsh-plugin-discovery#pluginDiscovery/grantVersionExemption:acceptRisk");
2085
+ const GRANT_RESULT = strictJson("dsh-plugin-discovery#pluginDiscovery/grantVersionExemption:result");
1654
2086
  const LIST_INSTALLED_RESULT = strictJson("dsh-plugin-discovery#pluginDiscovery/listInstalled:result");
1655
2087
  const WAIT_REQUEST_ID = strictJson("dsh-plugin-discovery#pluginDiscovery/waitInstall:requestId");
1656
2088
  const WAIT_RESULT = strictJson("dsh-plugin-discovery#pluginDiscovery/waitInstall:result");
@@ -1754,6 +2186,19 @@ window.__ModuleLoader__.load({
1754
2186
  ],
1755
2187
  result: INSTALL_RESULT,
1756
2188
  },
2189
+ {
2190
+ id: "dsh-plugin-discovery#pluginDiscovery/grantVersionExemption",
2191
+ service: "pluginDiscovery",
2192
+ namespace: "pluginDiscovery",
2193
+ method: "grantVersionExemption",
2194
+ invocation: { kind: "direct" },
2195
+ parameters: [
2196
+ { name: "packageVersion", wire: "packageVersion", source: "json", codec: GRANT_PACKAGE },
2197
+ { name: "runtimeVersion", wire: "runtimeVersion", source: "json", codec: GRANT_RUNTIME },
2198
+ { name: "acceptRisk", wire: "acceptRisk", source: "json", codec: GRANT_ACCEPT },
2199
+ ],
2200
+ result: GRANT_RESULT,
2201
+ },
1757
2202
  {
1758
2203
  id: "dsh-plugin-discovery#pluginDiscovery/listInstalled",
1759
2204
  service: "pluginDiscovery",
@@ -1875,10 +2320,27 @@ window.__ModuleLoader__.load({
1875
2320
  packageName: value && value.packageName,
1876
2321
  requestId: value && value.requestId,
1877
2322
  message: value && value.message,
2323
+ detail: value && value.detail,
2324
+ code: value && value.code,
2325
+ incompatible: value && value.incompatible,
2326
+ needsRiskExemption: !!(value && value.needsRiskExemption),
2327
+ recommendations: value && value.recommendations,
2328
+ installSpec: (value && value.installSpec) || spec,
1878
2329
  };
1879
2330
  }
1880
2331
  return value;
1881
2332
  },
2333
+ grantVersionExemption: async (packageVersion, runtimeVersion, acceptRisk) => {
2334
+ if (mountError) return { ok: false, message: mountError };
2335
+ const remote = remoteNs();
2336
+ if (!remote || typeof remote.grantVersionExemption !== "function") {
2337
+ return { ok: false, message: "remote.pluginDiscovery.grantVersionExemption unavailable" };
2338
+ }
2339
+ const wire = await remote.grantVersionExemption(packageVersion, runtimeVersion, acceptRisk);
2340
+ const value = unwrapRemote(wire, "grantVersionExemption failed");
2341
+ if (wire && wire.ok === false) return { ok: false, message: value.message };
2342
+ return value;
2343
+ },
1882
2344
  listInstalled: async () => {
1883
2345
  if (mountError) return { ok: false, names: [] };
1884
2346
  const remote = remoteNs();
@@ -1898,7 +2360,20 @@ window.__ModuleLoader__.load({
1898
2360
  }
1899
2361
  const wire = await remote.waitInstall(requestId);
1900
2362
  const value = unwrapRemote(wire, "waitInstall failed");
1901
- if (wire && wire.ok === false) return { ok: false, message: value.message || "waitInstall failed" };
2363
+ if (wire && wire.ok === false) {
2364
+ return {
2365
+ ok: false,
2366
+ message: (value && value.message) || "waitInstall failed",
2367
+ detail: value && value.detail,
2368
+ code: value && value.code,
2369
+ incompatible: value && value.incompatible,
2370
+ needsRiskExemption: !!(value && value.needsRiskExemption),
2371
+ recommendations: value && value.recommendations,
2372
+ installSpec: value && value.installSpec,
2373
+ packageName: value && value.packageName,
2374
+ pending: !!(value && value.pending),
2375
+ };
2376
+ }
1902
2377
  return value;
1903
2378
  },
1904
2379
  subscribePluginChanged: (listener) => {