@noob-stupid/dsh-plugin-console 0.3.67 → 0.4.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.
Files changed (48) hide show
  1. package/lib/client.js +126 -31
  2. package/lib/index.js +60 -9687
  3. package/lib/server/domain/ai-run.js +479 -0
  4. package/lib/server/domain/ai.js +246 -0
  5. package/lib/server/domain/compat.js +474 -0
  6. package/lib/server/domain/components.js +108 -0
  7. package/lib/server/domain/dep-source.js +122 -0
  8. package/lib/server/domain/format-contract.js +265 -0
  9. package/lib/server/domain/format-scan.js +434 -0
  10. package/lib/server/domain/framework.js +393 -0
  11. package/lib/server/domain/install-job.js +561 -0
  12. package/lib/server/domain/install.js +599 -0
  13. package/lib/server/domain/jobs.js +28 -0
  14. package/lib/server/domain/market.js +409 -0
  15. package/lib/server/domain/patch.js +203 -0
  16. package/lib/server/domain/presets.js +93 -0
  17. package/lib/server/domain/quarantine.js +224 -0
  18. package/lib/server/domain/release-source.js +504 -0
  19. package/lib/server/domain/repoland.js +119 -0
  20. package/lib/server/domain/revoke.js +184 -0
  21. package/lib/server/domain/runtime.js +118 -0
  22. package/lib/server/domain/selfupdate.js +319 -0
  23. package/lib/server/domain/skills.js +234 -0
  24. package/lib/server/domain/sources.js +297 -0
  25. package/lib/server/domain/suite.js +220 -0
  26. package/lib/server/infra/exec.js +98 -0
  27. package/lib/server/infra/fsx.js +163 -0
  28. package/lib/server/infra/fw-integrity-check.js +37 -0
  29. package/lib/server/infra/http.js +373 -0
  30. package/lib/server/infra/httpd.js +51 -0
  31. package/lib/server/infra/mask.js +19 -0
  32. package/lib/server/infra/paths.js +177 -0
  33. package/lib/server/infra/semver.js +168 -0
  34. package/lib/server/routes/ai.js +172 -0
  35. package/lib/server/routes/components.js +254 -0
  36. package/lib/server/routes/framework-preflight.js +160 -0
  37. package/lib/server/routes/framework-upgrade.js +679 -0
  38. package/lib/server/routes/framework.js +544 -0
  39. package/lib/server/routes/github-login.js +198 -0
  40. package/lib/server/routes/index.js +128 -0
  41. package/lib/server/routes/install.js +116 -0
  42. package/lib/server/routes/market.js +415 -0
  43. package/lib/server/routes/plugins.js +562 -0
  44. package/lib/server/routes/skills.js +107 -0
  45. package/lib/server/routes/sources.js +437 -0
  46. package/lib/server/routes/state.js +125 -0
  47. package/lib/server/state.js +22 -0
  48. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -276,7 +276,7 @@ window.__ModuleLoader__.load({
276
276
  aiFallbackLabel: "AI 兜底",
277
277
  aiEmpowerLabel: "AI 赋能",
278
278
  aiEmpowerTitle: "AI 赋能:读文档自动规划并部署组件;识别到服务器类组件后自动加启动/停止按钮",
279
- aiEmpowerSrcTitle: "输入要部署的组件(npm 包名或 GitHub 仓库,如 Noob-stupid/dsh-plugin-hub 或 @noob-stupid/dsh-plugin-console)",
279
+ aiEmpowerSrcTitle: "输入要部署的组件(npm 包名或 GitHub 仓库,如 Noob-stupid/dsh-plugin-gating-hub 或 @noob-stupid/dsh-plugin-console)",
280
280
  aiEmpowerPlanTitle: "AI 赋能部署计划",
281
281
  aiEmpowerType: "类型",
282
282
  aiEmpowerTypeMapping: { "pure-plugin": "纯插件", "service": "服务器组件", "config-only": "仅配置" },
@@ -475,6 +475,20 @@ window.__ModuleLoader__.load({
475
475
  frameworkUpgradeTarget: "升级目标",
476
476
  frameworkUpgradePick: "选择要升级到的版本(列出所有比当前新的版本,含预发布;latest 为稳定版)",
477
477
  frameworkUpgradeMore: "展开全部可选版本({n})",
478
+ // 升级前契约预检(2026-09-24 事故:v4 消息格式变更让运行期生产方全部失效)
479
+ fwPreflightBtn: "升级预检",
480
+ fwPreflightTitle: "升级前契约预检:抓目标版本的会话格式契约(迁移边),扫描 agent-presets 与已装插件里的旧形状消息来源;可一键适配并自动备份(.bak-preflight-*)",
481
+ fwPreflightRunning: "预检中…",
482
+ fwPreflightClean: "预检通过:本地生产方没有会触发格式校验的旧写法",
483
+ fwPreflightFound: "预检发现 {n} 处必须适配(另有 {m} 处提示)",
484
+ fwPreflightContract: "会话格式契约:{summary}",
485
+ fwPreflightNoContract: "契约未知:{err}(已降级为内置规则扫描)",
486
+ fwPreflightPatch: "一键适配(自动备份)",
487
+ fwPreflightApplyConfirm: "将修改本地文件(每个文件自动备份为 .bak-preflight-*),继续?",
488
+ fwPreflightPatched: "已适配 {n} 处,备份 {b} 个文件",
489
+ fwPreflightRemaining: "适配后仍有 {n} 处需人工处理(预检不会自动改)",
490
+ fwPreflightNeedManual: "需人工处理:{why}",
491
+ fwPreflightScanned: "已扫描 {n} 个生产方文件(预设 + 插件)",
478
492
  frameworkUpgradeFold: "收起版本列表",
479
493
  frameworkUpgradeChannelStable: "稳定版",
480
494
  frameworkUpgradeChannelPre: "预发布",
@@ -887,6 +901,20 @@ window.__ModuleLoader__.load({
887
901
  frameworkUpgradeTarget: "Upgrade target",
888
902
  frameworkUpgradePick: "Pick a version to upgrade to (every version newer than the current one, pre-releases included; latest = stable channel)",
889
903
  frameworkUpgradeMore: "Show all available versions ({n})",
904
+ // Pre-upgrade contract preflight (2026-09-24 incident: v4 message-format change broke every runtime producer)
905
+ fwPreflightBtn: "Pre-upgrade preflight",
906
+ fwPreflightTitle: "Pre-upgrade contract preflight: fetch the target session-format contract (migration edges) and scan agent-presets + installed plugins for legacy message sources; one click adapts with .bak-preflight-* backups",
907
+ fwPreflightRunning: "Preflighting…",
908
+ fwPreflightClean: "Preflight passed: no legacy producer shape that would fail format validation",
909
+ fwPreflightFound: "Preflight found {n} blockers to adapt ({m} advisories)",
910
+ fwPreflightContract: "Session-format contract: {summary}",
911
+ fwPreflightNoContract: "Contract unknown: {err} (degraded to built-in rules)",
912
+ fwPreflightPatch: "Adapt now (auto backup)",
913
+ fwPreflightApplyConfirm: "This will modify local files (each backed up as .bak-preflight-*). Continue?",
914
+ fwPreflightPatched: "Adapted {n} spot(s), backed up {b} file(s)",
915
+ fwPreflightRemaining: "{n} spot(s) still need manual work (preflight never auto-fixes those)",
916
+ fwPreflightNeedManual: "Needs manual work: {why}",
917
+ fwPreflightScanned: "Scanned {n} producer file(s) (presets + plugins)",
890
918
  frameworkUpgradeFold: "Collapse version list",
891
919
  frameworkUpgradeChannelStable: "stable",
892
920
  frameworkUpgradeChannelPre: "pre-release",
@@ -1392,6 +1420,10 @@ window.__ModuleLoader__.load({
1392
1420
  ? fwSelectedVersion
1393
1421
  : (fwCheck && fwCheck.tagDefault) || (fwCheck && fwCheck.target) || (fwVersions[0] ? fwVersions[0].version : null);
1394
1422
  const fwChannelLabel = (v) => (v && v.isLatest === true ? t("frameworkUpgradeChannelStable") + " · latest" : (v && typeof v.channel === "string" && v.channel !== "" ? t("frameworkUpgradeChannelPre") + " · " + v.channel : t("frameworkUpgradeChannelPre")));
1423
+ // 升级前契约预检(Step 1+2):report=只读预检结论,patch=一键适配结果;两者分开放,
1424
+ // 免得把「扫描结论」和「已经改过什么」混成一团(用户要能分辨「该不该升级」与「改了什么」)
1425
+ const [fwPreflightBusy, setFwPreflightBusy] = react.useState(false);
1426
+ const [fwPreflight, setFwPreflight] = react.useState(null);
1395
1427
  // 面板里的状态刷新:**无视**「终态已关闭」标记——卡片可以被永久关掉,面板必须永远能查到真相
1396
1428
  const fwStatusRefresh = () => {
1397
1429
  call("/plugin-console/framework-upgrade-status").then(
@@ -1411,6 +1443,24 @@ window.__ModuleLoader__.load({
1411
1443
  (error) => { setFwCheckBusy(false); setMessage(t("failed") + ":" + friendlyGithubError(error).message); },
1412
1444
  );
1413
1445
  };
1446
+ // 升级前契约预检:先只读预检(抓目标版本会话格式契约 + 扫本地生产方),
1447
+ // 再按需一键适配(服务端逐文件备份 .bak-preflight-*,名单外文件一律拒绝写入)。
1448
+ // 为什么必须独立于「框架升级」按钮:包级适配门看不到运行期契约破坏(2026-09-24 事故)。
1449
+ const fwPreflightRun = (apply) => {
1450
+ const targetVersion = fwSelected ?? fwCheck?.target ?? null;
1451
+ if (apply === true && !window.confirm(t("fwPreflightApplyConfirm"))) return;
1452
+ setFwPreflightBusy(true);
1453
+ const path = apply === true ? "/plugin-console/framework-preflight-patch" : "/plugin-console/framework-preflight";
1454
+ const payload = apply === true ? { targetVersion, mode: "apply" } : { targetVersion };
1455
+ call(path, payload).then(
1456
+ (data) => {
1457
+ setFwPreflightBusy(false);
1458
+ setFwPreflight((prev) => (apply === true ? { report: (prev && prev.report) ?? null, patch: data } : { report: data, patch: null }));
1459
+ },
1460
+ (error) => { setFwPreflightBusy(false); setMessage(t("failed") + ":" + friendlyGithubError(error).message); },
1461
+ );
1462
+ };
1463
+ const fwShortPath = (p) => String(p ?? "").replace(/\\/gu, "/").split("/").slice(-3).join("/");
1414
1464
  const doFrameworkRollback = () => {
1415
1465
  setFrameworkUpgrading(true);
1416
1466
  call("/plugin-console/framework-rollback", {}).then(
@@ -2832,7 +2882,9 @@ window.__ModuleLoader__.load({
2832
2882
  return;
2833
2883
  }
2834
2884
  if ((item.subpackagePath || item.npmPackage === true) && typeof item.packageName === "string" && item.packageName !== "") {
2835
- // monorepo 子包:搜索结果带 packageName,按 npm 包名安装
2885
+ // monorepo 子包 / npm 包名命中:搜索结果带 packageName,按 npm 包名安装
2886
+ //(npm 包名反查命中的条目没有 subpackagePath,只有 packageName + npmPackage,
2887
+ // 不改这一行就会退化成"按仓库装",装到的不是用户输入的那个包 —— 2026-09-20)
2836
2888
  startJob(item.fullName, item.packageName, item.source ?? "github");
2837
2889
  return;
2838
2890
  }
@@ -3663,8 +3715,47 @@ window.__ModuleLoader__.load({
3663
3715
  : null,
3664
3716
  frameworkStatus.message ? el("p", { className: styles.status, "data-error": frameworkStatus.status === "failed" ? "true" : undefined }, frameworkStatus.message) : null)
3665
3717
  : el("p", { className: styles.status }, t("fwPanelNoRecord")),
3718
+ // ③.0 升级预检报告(只读结论 + 一键适配结果)——放在升级按钮之前,
3719
+ // 让「先预检、再升级」成为默认路径
3720
+ fwPreflight !== null
3721
+ ? el("div", { style: { flexDirection: "column", gap: 6, display: "flex", border: "1px solid var(--dsw-alias-border-l2)", borderRadius: 8, padding: 8 } },
3722
+ el("strong", { className: styles.name },
3723
+ t("fwPreflightBtn")
3724
+ + (fwPreflight.report?.scan ? "(" + t("fwPreflightScanned").replace("{n}", String(fwPreflight.report.scan.filesScanned)) + ")" : "")),
3725
+ fwPreflight.report === null || fwPreflight.report === undefined
3726
+ ? null
3727
+ : (fwPreflight.report.contract !== null && fwPreflight.report.contract !== undefined
3728
+ ? el("p", { className: styles.status }, t("fwPreflightContract").replace("{summary}", String(fwPreflight.report.contractSummary ?? "")))
3729
+ : el("p", { className: styles.message, "data-warn": "true" }, t("fwPreflightNoContract").replace("{err}", String(fwPreflight.report.contractError ?? "?")))),
3730
+ fwPreflight.report === null || fwPreflight.report === undefined
3731
+ ? null
3732
+ : (fwPreflight.report.scan.ok === true
3733
+ ? el("p", { className: styles.status }, "✓ " + t("fwPreflightClean"))
3734
+ : el("p", { className: styles.message, "data-error": "true" }, t("fwPreflightFound").replace("{n}", String(fwPreflight.report.scan.blockers)).replace("{m}", String(fwPreflight.report.scan.warnings)))),
3735
+ fwPreflight.report === null || fwPreflight.report === undefined
3736
+ ? null
3737
+ : el("div", { style: { flexDirection: "column", gap: 2, display: "flex", maxHeight: 160, overflowY: "auto" } },
3738
+ (fwPreflight.report.scan.findings ?? []).slice(0, 10).map((f, idx) => el("p", { key: idx, className: styles.status },
3739
+ "• " + fwShortPath(f.file) + ":" + f.line + " — " + f.rule
3740
+ + (f.fixable === true ? "" : "(" + t("fwPreflightNeedManual").replace("{why}", String(f.reason ?? f.note ?? "?")) + ")")
3741
+ + (f.after ? " → " + f.after : "")))),
3742
+ fwPreflight.patch !== null && fwPreflight.patch !== undefined
3743
+ ? el("p", { className: styles.status, "data-pending": "true" },
3744
+ t("fwPreflightPatched").replace("{n}", String((fwPreflight.patch.changes ?? []).length)).replace("{b}", String((fwPreflight.patch.backups ?? []).length)))
3745
+ : null,
3746
+ fwPreflight.patch !== null && fwPreflight.patch !== undefined && (fwPreflight.patch.remaining?.blockers ?? 0) > 0
3747
+ ? el("p", { className: styles.message, "data-warn": "true" }, t("fwPreflightRemaining").replace("{n}", String(fwPreflight.patch.remaining.blockers)))
3748
+ : null,
3749
+ el("div", { className: styles.rowTop },
3750
+ fwPreflight.report !== null && fwPreflight.report !== undefined && fwPreflight.report.scan.blockers > 0
3751
+ ? el("button", { type: "button", className: styles.toggle, disabled: fwPreflightBusy, onClick: () => fwPreflightRun(true) }, t("fwPreflightPatch"))
3752
+ : null,
3753
+ el("button", { type: "button", className: styles.toggle, disabled: fwPreflightBusy, onClick: () => fwPreflightRun(false) }, fwPreflightBusy ? t("fwPreflightRunning") : t("fwPreflightBtn")),
3754
+ el("button", { type: "button", className: styles.toggle, onClick: () => setFwPreflight(null) }, t("closeModal"))))
3755
+ : null,
3666
3756
  // ③ 操作:升级(两步确认)/ 回滚 / 重启 / 重新检查
3667
3757
  el("div", { className: styles.rowTop, style: { flexWrap: "wrap" } },
3758
+ el("button", { type: "button", className: styles.toggle, disabled: fwPreflightBusy, title: t("fwPreflightTitle"), onClick: () => fwPreflightRun(false) }, fwPreflightBusy ? t("fwPreflightRunning") : t("fwPreflightBtn")),
3668
3759
  fwCheck?.target
3669
3760
  ? (fwConfirmPanel
3670
3761
  ? el("div", { style: { flexDirection: "column", gap: 8, display: "flex", flexBasis: "100%" } },
@@ -3716,34 +3807,36 @@ window.__ModuleLoader__.load({
3716
3807
  el("p", { className: styles.message }, t("compatGateHint")),
3717
3808
  el("p", { className: styles.status, "data-pending": gatePendingCount > 0 ? "true" : undefined },
3718
3809
  gatePendingCount > 0 ? t("gatePendingInfo").replace("{n}", String(gatePendingCount)) : t("gatePendingNone")),
3719
- // 明细清单(2026-09-24 用户要求:面板要能看到是哪些插件,有列表数据)
3720
- gateRows !== null && gateRows.pendingCount + gateRows.adoptedCount > 0
3721
- ? el("div", { style: { flexDirection: "column", gap: 6, display: "flex" } },
3722
- el("strong", { className: styles.name }, t("gateListPending").replace("{n}", String(gateRows.pendingCount))),
3723
- gateRows.pending.length === 0
3724
- ? el("p", { className: styles.status }, t("gatePendingNone"))
3725
- : null,
3726
- (gateShowAll ? gateRows.pending : gateRows.pending.slice(0, 8)).map((r) => el("div", {
3727
- key: "p-" + String(r.rowId),
3728
- style: { flexDirection: "column", gap: 2, display: "flex", padding: "4px 0" },
3729
- },
3730
- el("span", { className: styles.name }, (r.enabled === false ? "⛔ " : "• ") + String(r.name) + (r.version ? " v" + r.version : "")),
3731
- el("span", { className: styles.status }, t("gateRowReason").replace("{reason}", String(r.note ?? "—"))),
3732
- el("span", { className: styles.status },
3733
- (r.source === "boot-quarantine" ? t("gateRowFromBoot") : t("gateRowFromUpgrade"))
3734
- + (r.forcedAt ? t("gateRowAt").replace("{t}", gateTime(r.forcedAt)) : "")
3735
- + (r.adoptable ? t("gateRowAdoptable") : "")))),
3736
- gateRows.pendingCount > 8
3737
- ? el("button", { type: "button", className: styles.toggle, onClick: () => setGateShowAll((v) => !v) },
3738
- gateShowAll ? t("gateListFold") : t("gateListMore").replace("{n}", String(gateRows.pendingCount)))
3739
- : null,
3740
- gateRows.adoptedCount > 0
3741
- ? el("div", { style: { flexDirection: "column", gap: 2, display: "flex" } },
3742
- el("strong", { className: styles.name }, t("gateListAdopted").replace("{n}", String(gateRows.adoptedCount))),
3743
- gateRows.adopted.slice(0, 8).map((r) => el("span", { key: "a-" + String(r.rowId), className: styles.status },
3744
- "✓ " + String(r.name) + (r.version ? " v" + r.version : "") + (r.adoptedAt ? " · " + gateTime(r.adoptedAt) : ""))))
3745
- : null)
3746
- : null)))
3810
+ el("p", { className: styles.status, "data-pending": gatePendingCount > 0 ? "true" : undefined },
3811
+ gatePendingCount > 0 ? t("gatePendingInfo").replace("{n}", String(gatePendingCount)) : t("gatePendingNone")),
3812
+ // 明细清单(2026-09-24 用户要求:面板要能看到是哪些插件,有列表数据)
3813
+ gateRows !== null && gateRows.pendingCount + gateRows.adoptedCount > 0
3814
+ ? el("div", { style: { flexDirection: "column", gap: 6, display: "flex" } },
3815
+ el("strong", { className: styles.name }, t("gateListPending").replace("{n}", String(gateRows.pendingCount))),
3816
+ gateRows.pending.length === 0
3817
+ ? el("p", { className: styles.status }, t("gatePendingNone"))
3818
+ : null,
3819
+ (gateShowAll ? gateRows.pending : gateRows.pending.slice(0, 8)).map((r) => el("div", {
3820
+ key: "p-" + String(r.rowId),
3821
+ style: { flexDirection: "column", gap: 2, display: "flex", padding: "4px 0" },
3822
+ },
3823
+ el("span", { className: styles.name }, (r.enabled === false ? "⛔ " : "• ") + String(r.name) + (r.version ? " v" + r.version : "")),
3824
+ el("span", { className: styles.status }, t("gateRowReason").replace("{reason}", String(r.note ?? "—"))),
3825
+ el("span", { className: styles.status },
3826
+ (r.source === "boot-quarantine" ? t("gateRowFromBoot") : t("gateRowFromUpgrade"))
3827
+ + (r.forcedAt ? t("gateRowAt").replace("{t}", gateTime(r.forcedAt)) : "")
3828
+ + (r.adoptable ? t("gateRowAdoptable") : "")))),
3829
+ gateRows.pendingCount > 8
3830
+ ? el("button", { type: "button", className: styles.toggle, onClick: () => setGateShowAll((v) => !v) },
3831
+ gateShowAll ? t("gateListFold") : t("gateListMore").replace("{n}", String(gateRows.pendingCount)))
3832
+ : null,
3833
+ gateRows.adoptedCount > 0
3834
+ ? el("div", { style: { flexDirection: "column", gap: 2, display: "flex" } },
3835
+ el("strong", { className: styles.name }, t("gateListAdopted").replace("{n}", String(gateRows.adoptedCount))),
3836
+ gateRows.adopted.slice(0, 8).map((r) => el("span", { key: "a-" + String(r.rowId), className: styles.status },
3837
+ "✓ " + String(r.name) + (r.version ? " v" + r.version : "") + (r.adoptedAt ? " · " + gateTime(r.adoptedAt) : ""))))
3838
+ : null)
3839
+ : null)))
3747
3840
  : null,
3748
3841
  aiOpen
3749
3842
  ? el("div", { className: styles.modalBackdrop },
@@ -3860,7 +3953,9 @@ window.__ModuleLoader__.load({
3860
3953
  setMessage(t("cleanResiduals") + "…");
3861
3954
  call("/plugin-console/clean-residuals", {}).then(
3862
3955
  (data) => {
3863
- if (data && data.ok === true) setMessage(t("cleanResiduals") + ":" + (data.count === 0 ? "无残余" : "已清理 " + data.count + " 项"));
3956
+ const keptCount = Array.isArray(data && data.kept) ? data.kept.length : 0;
3957
+ const keptNote = keptCount > 0 ? ";保留在用插件 " + keptCount + " 个" : "";
3958
+ if (data && data.ok === true) setMessage(t("cleanResiduals") + ":" + (data.count === 0 ? "无残余" : "已清理 " + data.count + " 项") + keptNote);
3864
3959
  },
3865
3960
  (error) => setMessage(t("failed") + ":" + friendlyGithubError(error).message),
3866
3961
  );