@dsh-workbench/core 0.1.16 → 0.1.18

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 (68) hide show
  1. package/client.js +665 -127
  2. package/dist/board.css +31 -0
  3. package/dist/client/client.body.js +665 -127
  4. package/dist/client/index.js +665 -127
  5. package/dist/host/index.js +1510 -263
  6. package/dist/types/app/client.d.ts +124 -27
  7. package/dist/types/app/layouts/market/add-module-text.d.ts +2 -0
  8. package/dist/types/app/layouts/market/use-add-module.d.ts +4 -3
  9. package/dist/types/app/layouts/market/use-module-versions.d.ts +2 -0
  10. package/dist/types/app/layouts/rightbar/MarketView.d.ts +3 -1
  11. package/dist/types/app/layouts/rightbar/__tests__/market-rows-plugins.test.d.ts +1 -0
  12. package/dist/types/app/layouts/rightbar/market-freshness.d.ts +1 -0
  13. package/dist/types/app/layouts/rightbar/market-mock.d.ts +9 -0
  14. package/dist/types/app/layouts/rightbar/market-plugin-rows.d.ts +8 -0
  15. package/dist/types/app/layouts/rightbar/market-rows.d.ts +14 -3
  16. package/dist/types/app/layouts/rightbar/registry.d.ts +0 -1
  17. package/dist/types/app/units/__tests__/manifests.test.d.ts +1 -0
  18. package/dist/types/app/units/fixture-unit-mock.d.ts +3 -3
  19. package/dist/types/app/units/host.d.ts +2 -2
  20. package/dist/types/app/units/manifests.d.ts +5 -0
  21. package/dist/types/app/units/registry.d.ts +10 -2
  22. package/dist/types/modules/panels/__tests__/market-env.test.d.ts +1 -0
  23. package/dist/types/modules/panels/__tests__/market-plugin.test.d.ts +1 -0
  24. package/dist/types/modules/panels/__tests__/market-route.test.d.ts +1 -0
  25. package/dist/types/modules/panels/__tests__/plugin-actions.test.d.ts +1 -0
  26. package/dist/types/modules/panels/__tests__/plugin-ledger.test.d.ts +1 -0
  27. package/dist/types/modules/panels/__tests__/plugin-section.test.d.ts +1 -0
  28. package/dist/types/modules/panels/__tests__/scope.test.d.ts +1 -0
  29. package/dist/types/modules/panels/__tests__/service-release.test.d.ts +1 -0
  30. package/dist/types/modules/panels/index.d.ts +2 -2
  31. package/dist/types/modules/panels/model/config.d.ts +27 -9
  32. package/dist/types/modules/panels/model/market-dispatch.d.ts +9 -0
  33. package/dist/types/modules/panels/model/market-env.d.ts +20 -0
  34. package/dist/types/modules/panels/model/market-route.d.ts +26 -0
  35. package/dist/types/modules/panels/model/market-types.d.ts +71 -0
  36. package/dist/types/modules/panels/model/market.d.ts +7 -66
  37. package/dist/types/modules/panels/model/plugin-actions.d.ts +50 -0
  38. package/dist/types/modules/panels/model/plugin-ledger.d.ts +22 -0
  39. package/dist/types/modules/panels/model/plugin-name.d.ts +8 -0
  40. package/dist/types/modules/panels/model/plugin-name.test.d.ts +1 -0
  41. package/dist/types/modules/panels/model/plugin-section.d.ts +13 -0
  42. package/dist/types/modules/panels/model/service.d.ts +8 -6
  43. package/dist/types/shared/api/bundle-install.d.ts +8 -1
  44. package/dist/types/shared/api/host/__tests__/registry-declaration.test.d.ts +1 -0
  45. package/dist/types/shared/api/host/__tests__/unit-roster-facts.test.d.ts +1 -0
  46. package/dist/types/shared/api/host/bundle-routes.d.ts +1 -0
  47. package/dist/types/shared/api/host/installed-module.d.ts +7 -0
  48. package/dist/types/shared/api/host/mount-units.d.ts +5 -3
  49. package/dist/types/shared/api/host/package-content.d.ts +6 -0
  50. package/dist/types/shared/api/host/package-content.test.d.ts +1 -0
  51. package/dist/types/shared/api/host/package-kind.d.ts +21 -0
  52. package/dist/types/shared/api/host/package-kind.test.d.ts +1 -0
  53. package/dist/types/shared/api/host/package-route.d.ts +23 -0
  54. package/dist/types/shared/api/host/package-route.test.d.ts +1 -0
  55. package/dist/types/shared/api/host/registry-declaration.d.ts +23 -0
  56. package/dist/types/shared/api/host/unit-registry.d.ts +33 -4
  57. package/dist/types/shared/api/host/unit-roster-facts.d.ts +5 -0
  58. package/dist/types/shared/api/installed-plugins.d.ts +12 -0
  59. package/dist/types/shared/api/unit-manifests.d.ts +5 -0
  60. package/dist/types/shared/api/unit-manifests.test.d.ts +1 -0
  61. package/dist/types/shared/hooks/bundle-queries.d.ts +2 -0
  62. package/dist/types/shared/hooks/use-facts.d.ts +1 -0
  63. package/package.json +2 -2
  64. package/skills/workbench-module-diy/BUILD.md +5 -5
  65. package/skills/workbench-module-diy/DESIGN.md +7 -1
  66. package/skills/workbench-module-diy/REVIEW.md +4 -3
  67. package/skills/workbench-module-diy/TOOLING.md +2 -2
  68. package/skills/workbench-module-diy/TOOLS.md +13 -3
@@ -419,6 +419,133 @@ function probeKeyOf(error) {
419
419
  if (error === void 0) return null;
420
420
  return error.code === "" ? "unknown" : error.code;
421
421
  }
422
+ var SUPPORTED_API_VERSIONS = [1];
423
+ var DECLARATION_KEYS = [
424
+ "apiVersion",
425
+ "unit",
426
+ "card",
427
+ "tools",
428
+ "host",
429
+ "env"
430
+ ];
431
+ var DECLARATION_REQUIRED_KEYS = [
432
+ "apiVersion",
433
+ "unit",
434
+ "card",
435
+ "tools",
436
+ "host"
437
+ ];
438
+ var CARD_KEYS = ["titleKey", "order"];
439
+ var TOOL_KEYS = [
440
+ "name",
441
+ "writes",
442
+ "key"
443
+ ];
444
+ var TOOL_KEY_SHAPE = /^[a-z][a-zA-Z0-9]*$/;
445
+ var HOST_KEYS = ["inject", "optional"];
446
+ function text(value) {
447
+ return typeof value === "string" ? value.trim() : "";
448
+ }
449
+ function isRecord(value) {
450
+ return typeof value === "object" && value !== null && !Array.isArray(value);
451
+ }
452
+ function outsideKeys(value, allowed) {
453
+ return Object.keys(value).filter((key) => !allowed.includes(key));
454
+ }
455
+ function listOf(keys) {
456
+ return keys.map((key) => JSON.stringify(key)).join(" / ");
457
+ }
458
+ function stringArrayProblems(at, value) {
459
+ if (!Array.isArray(value)) return [`${at} 要是一串字符串,现在是 ${JSON.stringify(value)}`];
460
+ return value.flatMap((item, index) => text(item) === "" ? [`${at}[${index}] 不是非空字符串:${JSON.stringify(item)}`] : []);
461
+ }
462
+ function declarationProblemsOf(subject, raw) {
463
+ if (!isRecord(raw)) return [`${subject}: dsh.workbench 不是一个对象 —— ${JSON.stringify(raw)}`];
464
+ const version = raw.apiVersion;
465
+ if (version === void 0) return [`${subject}: dsh.workbench 没有 apiVersion —— 协议从 v1 起,写 \`"apiVersion": 1\`。缺它 = 外壳不认这个模块(ADR-0038 §2.3)`];
466
+ if (!Number.isInteger(version) || !SUPPORTED_API_VERSIONS.includes(version)) return [`${subject}: dsh.workbench.apiVersion 是 ${JSON.stringify(version)},这里只认 ${SUPPORTED_API_VERSIONS.join(" / ")}(要整数)`];
467
+ const problems = [];
468
+ const unknown = outsideKeys(raw, DECLARATION_KEYS);
469
+ if (unknown.length > 0) problems.push(`${subject}: dsh.workbench 里有协议 v1 不认识的键 ${listOf(unknown)} —— 只有 ${listOf(DECLARATION_KEYS)}。(拼错的键会被外壳**静默忽略**:那句声明等于没写)`);
470
+ for (const key of DECLARATION_REQUIRED_KEYS) if (raw[key] === void 0) problems.push(`${subject}: dsh.workbench 缺 ${key}` + (key === "card" ? "(这个模块没有卡片就写 `\"card\": null`)" : ""));
471
+ const card = raw.card;
472
+ if (card !== void 0 && card !== null) {
473
+ if (!isRecord(card)) problems.push(`${subject}: dsh.workbench.card 要么是对象、要么是 null,现在是 ${JSON.stringify(card)}`);
474
+ else {
475
+ const extra = outsideKeys(card, CARD_KEYS);
476
+ if (extra.length > 0) problems.push(`${subject}: dsh.workbench.card 里有不认的键 ${listOf(extra)} —— card 只有 ${listOf(CARD_KEYS)}(卡片默认显不显示由运行期的面板配置决定,清单里没有这一位)`);
477
+ if (text(card.titleKey) === "") problems.push(`${subject}: dsh.workbench.card.titleKey 要是非空字符串(面板那一格的文案键)`);
478
+ if (typeof card.order !== "number" || !Number.isFinite(card.order)) problems.push(`${subject}: dsh.workbench.card.order 要是数字,现在是 ${JSON.stringify(card.order)}`);
479
+ }
480
+ }
481
+ const tools = raw.tools;
482
+ if (tools !== void 0) {
483
+ if (!Array.isArray(tools)) problems.push(`${subject}: dsh.workbench.tools 要是一串声明,现在是 ${JSON.stringify(tools)}`);
484
+ else {
485
+ const seen = /* @__PURE__ */ new Set();
486
+ const seenKeys = /* @__PURE__ */ new Set();
487
+ tools.forEach((tool, index) => {
488
+ const at = `dsh.workbench.tools[${index}]`;
489
+ if (!isRecord(tool)) {
490
+ problems.push(`${subject}: ${at} 要是对象,现在是 ${JSON.stringify(tool)}`);
491
+ return;
492
+ }
493
+ const extra = outsideKeys(tool, TOOL_KEYS);
494
+ if (extra.length > 0) problems.push(`${subject}: ${at} 里有不认的键 ${listOf(extra)} —— tools 只有 ${listOf(TOOL_KEYS)}`);
495
+ const name = text(tool.name);
496
+ if (name === "") problems.push(`${subject}: ${at}.name 要是非空字符串`);
497
+ else if (seen.has(name)) problems.push(`${subject}: ${at}.name「${name}」与前面那条撞了 —— 工具名会进 dsh 的工具表,不许重`);
498
+ else seen.add(name);
499
+ if (typeof tool.writes !== "boolean") problems.push(`${subject}: ${at}.writes 要是 true / false(它是"这会写"的唯一声明,缺了审批无从认领)`);
500
+ if (tool.key !== void 0) {
501
+ const key = text(tool.key);
502
+ if (!TOOL_KEY_SHAPE.test(key)) problems.push(`${subject}: ${at}.key 要是 lowerCamelCase 的标识符(如 \`sync\` / \`podLogs\`),现在是 ${JSON.stringify(tool.key)} —— 它会变成生成物里的键名,所以不引号、不许带点或横线`);
503
+ else if (seenKeys.has(key)) problems.push(`${subject}: ${at}.key「${key}」与前面那条撞了 —— 生成物里的键只能有一个`);
504
+ else seenKeys.add(key);
505
+ }
506
+ });
507
+ }
508
+ }
509
+ const host = raw.host;
510
+ if (host !== void 0) {
511
+ if (!isRecord(host)) problems.push(`${subject}: dsh.workbench.host 要是对象,现在是 ${JSON.stringify(host)}`);
512
+ else {
513
+ const extra = outsideKeys(host, HOST_KEYS);
514
+ if (extra.length > 0) problems.push(`${subject}: dsh.workbench.host 里有不认的键 ${listOf(extra)} —— host 只有 ${listOf(HOST_KEYS)}`);
515
+ if (host.inject === void 0) problems.push(`${subject}: dsh.workbench.host.inject 没写 —— 它声明这个模块要外壳的哪几个服务,外壳靠它判"能不能挂"。一个都不要也要写 \`"inject": []\``);
516
+ else problems.push(...stringArrayProblems(`${subject}: dsh.workbench.host.inject`, host.inject));
517
+ if (host.optional !== void 0) problems.push(...stringArrayProblems(`${subject}: dsh.workbench.host.optional`, host.optional));
518
+ }
519
+ }
520
+ if (raw.env !== void 0 && !isRecord(raw.env)) problems.push(`${subject}: dsh.workbench.env 要是对象(command / plugins / libs)—— 语义由门禁的 env 判据查,这里只查形状`);
521
+ return problems;
522
+ }
523
+ function readDeclaration(subject, raw) {
524
+ const problems = declarationProblemsOf(subject, raw);
525
+ if (problems.length > 0) return { problems };
526
+ const declaration = raw;
527
+ const card = declaration.card;
528
+ return {
529
+ declaration,
530
+ problems: [],
531
+ manifest: {
532
+ id: text(declaration.unit),
533
+ card: card === null ? null : {
534
+ titleKey: text(card.titleKey),
535
+ order: card.order
536
+ },
537
+ tools: declaration.tools.map((tool) => ({
538
+ name: text(tool.name),
539
+ writes: tool.writes
540
+ })),
541
+ host: {
542
+ inject: [...declaration.host.inject ?? []],
543
+ ...declaration.host.optional === void 0 ? {} : { optional: [...declaration.host.optional] }
544
+ },
545
+ ...declaration.env === void 0 ? {} : { env: declaration.env }
546
+ }
547
+ };
548
+ }
422
549
  //#endregion
423
550
  //#region ../shared/dist/config/constants.js
424
551
  var PLUGIN_ID = "@dsh-workbench/core";
@@ -606,7 +733,7 @@ function ApprovalDetail(props) {
606
733
  if (owner?.approval !== void 0) {
607
734
  const Render = owner.approval.render;
608
735
  return h(CardBoundary, {
609
- unit: owner.manifest.id,
736
+ unit: owner.unit,
610
737
  resetKey: props.callId ?? "",
611
738
  t
612
739
  }, h(Render, {
@@ -1107,7 +1234,7 @@ var en = {
1107
1234
  "panels.market.missing": "not installed",
1108
1235
  "panels.market.unchecked": "unknown",
1109
1236
  "panels.market.checking": "checking",
1110
- "panels.market.sectionInstalled": "Installed",
1237
+ "panels.market.sectionInstalled": "Installed modules",
1111
1238
  "panels.market.sectionPending": "Not installed",
1112
1239
  "panels.market.sectionShared": "Shared dependencies",
1113
1240
  "panels.market.sharedUsedBy": "Used by {modules}",
@@ -1135,11 +1262,19 @@ var en = {
1135
1262
  "panels.market.noSource": "This module declares no install method — install it yourself",
1136
1263
  "panels.market.badSource": "The install method is incomplete — install it yourself",
1137
1264
  "panels.market.empty": "No modules available",
1138
- "panels.market.installedSection": "Installed",
1139
- "panels.market.sectionLocal": "Linked locally",
1265
+ "panels.market.sectionLocal": "Local modules",
1140
1266
  "panels.market.sectionUnlisted": "Not from the market",
1267
+ "panels.market.sectionPlugins": "Installed opencli plugins",
1141
1268
  "panels.market.moduleOutdated": "update available",
1269
+ "panels.market.moduleRefused": "This module was built for an older version of the workbench, so it does not work here. Update it, or uninstall.",
1270
+ "panels.market.moduleRefusedDetail": "Why it cannot be used: {reason}",
1142
1271
  "panels.market.moduleRestartHint": "installed but not active — restart dsh once to bring it up",
1272
+ "panels.market.pluginKeptInUse": "Files kept for now: {names} still uses it.",
1273
+ "panels.market.pluginKeptUnsure": "Files kept for now: some modules on this machine do not say what they use, so it is unclear who needs it.",
1274
+ "panels.market.pluginKeptHeld": "Files kept: this one is yours, and a module letting go will not remove it.",
1275
+ "panels.market.pluginKeptUnnamed": "Files kept for now: this one cannot be identified as a plugin, so it is unclear who needs it.",
1276
+ "panels.market.pluginAlsoKnownAs": "Its package is {pkg} (updates and uninstall use that)",
1277
+ "panels.market.pluginBroken": "This install is broken: it cannot be identified as a plugin. Uninstall it, or install it again by package name above.",
1143
1278
  "panels.market.restartNeeded": "The new version is installed — restart once to pick it up.",
1144
1279
  "panels.market.foot": "After installing, enable it in the panel page of that project.",
1145
1280
  "panels.market.recheck": "Check for updates",
@@ -1187,7 +1322,11 @@ var en = {
1187
1322
  "panels.market.add.problemInstalled": "This module is already installed",
1188
1323
  "panels.market.add.problemNotFound": "No such module found",
1189
1324
  "panels.market.add.problemNotPackage": "That path does not exist or is not a valid module package",
1190
- "panels.market.add.problemNotBundle": "This package declares no bundle, so it cannot be installed as a module: {reason}",
1325
+ "panels.market.add.problemNotBundle": "This package cannot be installed here: {reason}. If it is actually an opencli plugin, use its package name (for example opencli-plugin-cmdb) or a local folder — a git URL or tarball does not reveal what it is.",
1326
+ "panels.market.add.problemNotWorkbenchModule": "This package does not match the workbench you are running, so installing it would not work. Pick another version, or ask the module author.",
1327
+ "panels.market.add.problemNotModuleOrPlugin": "This package is neither a workbench module nor an opencli plugin, so installing it would do nothing. Pick another version, or ask the package author.",
1328
+ "panels.market.add.failureNotModuleOrPlugin": "This package is neither a workbench module nor an opencli plugin, so installing it would do nothing",
1329
+ "panels.market.add.installedPlugin": "Installed: the opencli plugin {name}",
1191
1330
  "panels.market.add.problemNetwork": "Cannot reach the module source; check the network and retry",
1192
1331
  "panels.market.add.problemUnknown": "Cannot fetch module information: {reason}",
1193
1332
  "panels.market.add.failureNetwork": "The network connection failed",
@@ -1218,6 +1357,7 @@ var en = {
1218
1357
  "panels.market.add.registryPnpm": "pnpm's own registry",
1219
1358
  "panels.market.add.check": "Check",
1220
1359
  "panels.market.add.checking": "Checking…",
1360
+ "panels.market.add.classifying": "Working out what it is…",
1221
1361
  "panels.market.add.change": "Edit",
1222
1362
  "panels.market.add.previewTitle": "About to install",
1223
1363
  "panels.market.add.fieldName": "Package",
@@ -1422,7 +1562,7 @@ var zh = {
1422
1562
  "panels.market.missing": "未装",
1423
1563
  "panels.market.unchecked": "状态未知",
1424
1564
  "panels.market.checking": "检测中",
1425
- "panels.market.sectionInstalled": "已安装",
1565
+ "panels.market.sectionInstalled": "已安装模块",
1426
1566
  "panels.market.sectionPending": "未安装",
1427
1567
  "panels.market.sectionShared": "共用依赖",
1428
1568
  "panels.market.sharedUsedBy": "供 {modules} 使用",
@@ -1450,11 +1590,19 @@ var zh = {
1450
1590
  "panels.market.noSource": "这个模块没声明怎么装,只能自己动手",
1451
1591
  "panels.market.badSource": "安装方式没写全,只能自己动手",
1452
1592
  "panels.market.empty": "暂无可用模块",
1453
- "panels.market.installedSection": "已安装",
1454
- "panels.market.sectionLocal": "本地装的",
1593
+ "panels.market.sectionLocal": "本地模块",
1455
1594
  "panels.market.sectionUnlisted": "不是从市场装的",
1595
+ "panels.market.sectionPlugins": "已安装 opencli 插件",
1456
1596
  "panels.market.moduleOutdated": "有新版",
1597
+ "panels.market.moduleRefused": "这个模块是给旧版本的工作台写的,现在用不了。更新它,或者卸载。",
1598
+ "panels.market.moduleRefusedDetail": "装不上的原因:{reason}",
1457
1599
  "panels.market.moduleRestartHint": "装好了但还没生效 —— 重启一次 dsh 就会出来",
1600
+ "panels.market.pluginKeptInUse": "文件先留着:{names} 还要用它。",
1601
+ "panels.market.pluginKeptUnsure": "文件先留着:这台机器上还有模块看不出用了什么,说不准谁在用它。",
1602
+ "panels.market.pluginKeptHeld": "文件留着:你自己装的那一份,模块放手不动它。",
1603
+ "panels.market.pluginKeptUnnamed": "文件先留着:看不出这是哪个插件,说不准谁在用它。",
1604
+ "panels.market.pluginAlsoKnownAs": "包名是 {pkg}(更新与卸载按它来)",
1605
+ "panels.market.pluginBroken": "这份装坏了:看不出它是个插件。可以卸载,或者去上面按包名再装一次。",
1458
1606
  "panels.market.restartNeeded": "新版装好了,重启一次才生效。",
1459
1607
  "panels.market.foot": "装好后到那个项目的面板页勾选它,模块才会出现。",
1460
1608
  "panels.market.recheck": "重新检查更新",
@@ -1502,7 +1650,11 @@ var zh = {
1502
1650
  "panels.market.add.problemInstalled": "该模块已安装",
1503
1651
  "panels.market.add.problemNotFound": "未找到相关模块",
1504
1652
  "panels.market.add.problemNotPackage": "该路径不存在或不是有效的模块包",
1505
- "panels.market.add.problemNotBundle": "这个包没有声明组合包,无法作为模块安装:{reason}",
1653
+ "panels.market.add.problemNotBundle": "这个包装不进这里:{reason}。要是它其实是个 opencli 插件,请改用包名(例如 opencli-plugin-cmdb)或者本地目录 —— 从 git 地址和 tarball 判断不出它是什么。",
1654
+ "panels.market.add.problemNotWorkbenchModule": "这个包跟现在的工作台对不上,装上也用不了。换一个版本,或者找这个模块的作者。",
1655
+ "panels.market.add.problemNotModuleOrPlugin": "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上。换一个版本,或者找这个包的作者。",
1656
+ "panels.market.add.failureNotModuleOrPlugin": "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上",
1657
+ "panels.market.add.installedPlugin": "装上了:opencli 插件 {name}",
1506
1658
  "panels.market.add.problemNetwork": "无法连接模块源,请检查网络后重试",
1507
1659
  "panels.market.add.problemUnknown": "无法获取模块信息:{reason}",
1508
1660
  "panels.market.add.failureNetwork": "网络连接失败",
@@ -1533,6 +1685,7 @@ var zh = {
1533
1685
  "panels.market.add.registryPnpm": "pnpm 自己配的源",
1534
1686
  "panels.market.add.check": "预检",
1535
1687
  "panels.market.add.checking": "正在预检…",
1688
+ "panels.market.add.classifying": "判定中…",
1536
1689
  "panels.market.add.change": "改一下",
1537
1690
  "panels.market.add.previewTitle": "将要安装",
1538
1691
  "panels.market.add.fieldName": "包名",
@@ -1809,6 +1962,8 @@ var dist_exports = /* @__PURE__ */ __exportAll({
1809
1962
  CenteredState: () => CenteredState,
1810
1963
  ChoiceGroup: () => ChoiceGroup,
1811
1964
  Component: () => Component,
1965
+ DECLARATION_KEYS: () => DECLARATION_KEYS,
1966
+ DECLARATION_REQUIRED_KEYS: () => DECLARATION_REQUIRED_KEYS,
1812
1967
  DEFAULT_WORKSPACE_TITLE: () => DEFAULT_WORKSPACE_TITLE,
1813
1968
  EVENTS_PATH: () => EVENTS_PATH,
1814
1969
  EmptyState: () => EmptyState,
@@ -1847,6 +2002,7 @@ var dist_exports = /* @__PURE__ */ __exportAll({
1847
2002
  STORAGE_KEY_PROJECT: () => STORAGE_KEY_PROJECT,
1848
2003
  STORAGE_KEY_RIGHTBAR: () => STORAGE_KEY_RIGHTBAR,
1849
2004
  STORAGE_KEY_RIGHTBAR_COLUMN: () => STORAGE_KEY_RIGHTBAR_COLUMN,
2005
+ SUPPORTED_API_VERSIONS: () => SUPPORTED_API_VERSIONS,
1850
2006
  SaveBar: () => SaveBar,
1851
2007
  SettingsSection: () => SettingsSection,
1852
2008
  SkeletonRows: () => SkeletonRows,
@@ -1857,6 +2013,7 @@ var dist_exports = /* @__PURE__ */ __exportAll({
1857
2013
  VERSION_CHECK_TIMEOUT_MS: () => VERSION_CHECK_TIMEOUT_MS,
1858
2014
  VERSION_CHECK_TTL_MS: () => VERSION_CHECK_TTL_MS,
1859
2015
  WARM_TIMEOUT_MS: () => WARM_TIMEOUT_MS,
2016
+ WORKBENCH_API_VERSION: () => 1,
1860
2017
  apiGet: () => apiGet,
1861
2018
  apiPost: () => apiPost,
1862
2019
  blockerActionLabel: () => blockerActionLabel,
@@ -1866,6 +2023,7 @@ var dist_exports = /* @__PURE__ */ __exportAll({
1866
2023
  buildMarker: () => buildMarker,
1867
2024
  createFactChannel: () => createFactChannel,
1868
2025
  cx: () => cx,
2026
+ declarationProblemsOf: () => declarationProblemsOf,
1869
2027
  decodeRef: () => decodeRef,
1870
2028
  dictionaries: () => dictionaries,
1871
2029
  en: () => en,
@@ -1889,6 +2047,7 @@ var dist_exports = /* @__PURE__ */ __exportAll({
1889
2047
  parseMarkers: () => parseMarkers,
1890
2048
  probeKeyOf: () => probeKeyOf,
1891
2049
  quoteShellArg: () => quoteShellArg,
2050
+ readDeclaration: () => readDeclaration,
1892
2051
  readFlag: () => readFlag,
1893
2052
  readText: () => readText,
1894
2053
  relativeTime: () => relativeTime,
@@ -2001,6 +2160,7 @@ function createAdaptiveTransport() {
2001
2160
  //#endregion
2002
2161
  //#region src/shared/hooks/use-facts.ts
2003
2162
  var use_facts_exports = /* @__PURE__ */ __exportAll({
2163
+ subscribeFactRevision: () => subscribeFactRevision,
2004
2164
  useFactInvalidate: () => useFactInvalidate,
2005
2165
  useFactRevision: () => useFactRevision
2006
2166
  });
@@ -2008,6 +2168,9 @@ var channel = createFactChannel({ transport: createAdaptiveTransport() });
2008
2168
  function useFactRevision(fact) {
2009
2169
  return useSyncExternalStore(useCallback((listener) => channel.subscribe(fact, listener), [fact]), useCallback(() => channel.revisionOf(fact), [fact]));
2010
2170
  }
2171
+ function subscribeFactRevision(fact, listener) {
2172
+ return channel.subscribe(fact, listener);
2173
+ }
2011
2174
  function useFactInvalidate() {
2012
2175
  return useCallback((fact) => channel.refresh(fact), []);
2013
2176
  }
@@ -2235,6 +2398,9 @@ function panelRowName(label, hint, t) {
2235
2398
  //#endregion
2236
2399
  //#region src/app/layouts/rightbar/market-freshness.ts
2237
2400
  var MARKET_STALE_MS = VERSION_CHECK_TTL_MS;
2401
+ function marketSettled(freshness, installedRead) {
2402
+ return freshness.loaded && installedRead !== false;
2403
+ }
2238
2404
  function shouldProbeOnEnter(input) {
2239
2405
  if (input.checkedAt === null) return true;
2240
2406
  return input.now - input.checkedAt >= input.staleMs;
@@ -2532,8 +2698,7 @@ function buildMarketMocks(roster) {
2532
2698
  ...mine,
2533
2699
  card: {
2534
2700
  titleKey: "一个名字特别特别长的模块用来验证省略号会不会把按钮挤下去",
2535
- order: 10,
2536
- enabledByDefault: true
2701
+ order: 10
2537
2702
  }
2538
2703
  }],
2539
2704
  capabilities: probe$1(id, "ok"),
@@ -2637,6 +2802,24 @@ function buildMarketMocks(roster) {
2637
2802
  installedActions: ["update-bundle", "remove-bundle"]
2638
2803
  }
2639
2804
  },
2805
+ {
2806
+ id: "installed-client-refused",
2807
+ label: "本机装了,但浏览器这半没挂上(拒因由客户端报告)",
2808
+ manifests: [found],
2809
+ capabilities: {},
2810
+ installed: [{
2811
+ unit: id,
2812
+ pkg: "@dsh-workbench/module-one",
2813
+ installed: "0.1.2",
2814
+ clientRefused: [`宿主没有担保 unit「${id}」的清单 —— 这个包要么没装进这份 profile,要么它的 dsh.workbench 没通过校验。卡片先不挂:清单只有一处真相(ADR-0038 §2.2)`]
2815
+ }],
2816
+ expect: {
2817
+ rows: 0,
2818
+ installed: 1,
2819
+ installedActions: ["remove-bundle"],
2820
+ installedNote: true
2821
+ }
2822
+ },
2640
2823
  {
2641
2824
  id: "installed-confirm",
2642
2825
  label: "卸载本机模块(Modal 确认,标题带模块名)",
@@ -2685,6 +2868,94 @@ function buildMarketMocks(roster) {
2685
2868
  actions: ["uninstall"]
2686
2869
  }
2687
2870
  },
2871
+ {
2872
+ id: "plugin-outdated",
2873
+ label: "用户装的 opencli 插件:有新版(给更新 / 卸载)",
2874
+ manifests: [],
2875
+ capabilities: {},
2876
+ plugins: [{
2877
+ pkg: "opencli-plugin-cmdb",
2878
+ name: "cmdb",
2879
+ installed: "1.0.24",
2880
+ latest: "1.0.25"
2881
+ }],
2882
+ expect: {
2883
+ rows: 0,
2884
+ plugins: 1,
2885
+ pluginActions: ["update-plugin", "uninstall-plugin"]
2886
+ }
2887
+ },
2888
+ {
2889
+ id: "plugin-local",
2890
+ label: "本地目录装的 opencli 插件:没有版本源,只给卸载",
2891
+ manifests: [],
2892
+ capabilities: {},
2893
+ plugins: [{
2894
+ pkg: "/Users/me/my-local-plugin",
2895
+ name: "my-local-plugin",
2896
+ local: true
2897
+ }],
2898
+ expect: {
2899
+ rows: 0,
2900
+ plugins: 1,
2901
+ pluginActions: ["uninstall-plugin"],
2902
+ pluginNote: false
2903
+ }
2904
+ },
2905
+ {
2906
+ id: "plugin-alias",
2907
+ label: "插件名 ≠ 包名:行上两个都写出来(更新 / 卸载按包名)",
2908
+ manifests: [],
2909
+ capabilities: {},
2910
+ plugins: [{
2911
+ pkg: "opencli-plugin-cmdb",
2912
+ name: "cmdb",
2913
+ installed: "1.0.24",
2914
+ latest: "1.0.25"
2915
+ }],
2916
+ expect: {
2917
+ rows: 0,
2918
+ plugins: 1,
2919
+ pluginActions: ["update-plugin", "uninstall-plugin"],
2920
+ pluginNoteKey: "panels.market.pluginAlsoKnownAs"
2921
+ }
2922
+ },
2923
+ {
2924
+ id: "plugin-broken",
2925
+ label: "装坏了的那一份(目录在但看不出是个插件):行上直说,给卸载与指路",
2926
+ manifests: [],
2927
+ capabilities: {},
2928
+ plugins: [{
2929
+ pkg: "opencli-plugin-cmdb",
2930
+ name: "cmdb",
2931
+ installed: "1.0.24",
2932
+ broken: true
2933
+ }],
2934
+ expect: {
2935
+ rows: 0,
2936
+ plugins: 1,
2937
+ pluginActions: ["uninstall-plugin"],
2938
+ pluginNoteKey: "panels.market.pluginBroken"
2939
+ }
2940
+ },
2941
+ {
2942
+ id: "plugin-kept",
2943
+ label: "卸掉用户装的插件,但还有模块在用:文件先留着(行上给一句为什么)",
2944
+ manifests: [],
2945
+ capabilities: {},
2946
+ plugins: [{
2947
+ pkg: "opencli-plugin-cmdb",
2948
+ name: "cmdb",
2949
+ installed: "1.0.24"
2950
+ }],
2951
+ notice: "文件先留着:cmdb、argocd 还要用它。",
2952
+ expect: {
2953
+ rows: 0,
2954
+ plugins: 1,
2955
+ pluginActions: ["uninstall-plugin"],
2956
+ notice: true
2957
+ }
2958
+ },
2688
2959
  {
2689
2960
  id: "empty",
2690
2961
  label: "市场空态",
@@ -2742,7 +3013,6 @@ function createBoardRegistry() {
2742
3013
  };
2743
3014
  },
2744
3015
  all: () => sorted,
2745
- visible: () => sorted.filter((module) => module.enabledByDefault),
2746
3016
  byId: (id) => index.get(id),
2747
3017
  stepOwner: (kind) => steps.get(kind),
2748
3018
  subscribe(listener) {
@@ -2754,20 +3024,21 @@ function createBoardRegistry() {
2754
3024
  }
2755
3025
  //#endregion
2756
3026
  //#region src/app/units/registry.ts
2757
- function toBoardModule(face) {
3027
+ function toBoardModule(face, manifest) {
2758
3028
  return {
2759
- id: face.manifest.id,
2760
- titleKey: face.manifest.card?.titleKey ?? face.manifest.id,
2761
- order: face.manifest.card?.order ?? 100,
2762
- enabledByDefault: face.manifest.card?.enabledByDefault ?? false,
3029
+ id: manifest.id,
3030
+ titleKey: manifest.card?.titleKey ?? manifest.id,
3031
+ order: manifest.card?.order ?? 100,
2763
3032
  View: face.card,
2764
3033
  ...face.steps === void 0 ? {} : { steps: face.steps }
2765
3034
  };
2766
3035
  }
2767
- function createClientUnitRegistry() {
2768
- const faces = [];
3036
+ function createClientUnitRegistry(options = {}) {
3037
+ const registrations = [];
3038
+ const refusals = [];
2769
3039
  const listeners = /* @__PURE__ */ new Set();
2770
3040
  const board = createBoardRegistry();
3041
+ let known = /* @__PURE__ */ new Map();
2771
3042
  let revision = 0;
2772
3043
  let manifestsCache;
2773
3044
  function notify() {
@@ -2778,28 +3049,86 @@ function createClientUnitRegistry() {
2778
3049
  console.warn(`[dsh-workbench] 套件注册的订阅者报错(不影响其它订阅者与这次注册):${String(error)}`);
2779
3050
  }
2780
3051
  }
3052
+ function warnRefusal(unit, reasons) {
3053
+ refusals.push({
3054
+ unit,
3055
+ reasons
3056
+ });
3057
+ console.warn(`[dsh-workbench] ${unit === "" ? "这个模块" : unit} 的卡片没挂上:${reasons.join(";")}`);
3058
+ }
3059
+ function takeBackRefusal(unit) {
3060
+ for (let at = refusals.length - 1; at >= 0; at -= 1) if (refusals[at]?.unit === unit) refusals.splice(at, 1);
3061
+ }
3062
+ function attach(entry) {
3063
+ const manifest = known.get(entry.unit);
3064
+ if (manifest === void 0) return false;
3065
+ entry.mounted = {
3066
+ manifest,
3067
+ unmount: board.register(toBoardModule(entry.face, manifest))
3068
+ };
3069
+ takeBackRefusal(entry.unit);
3070
+ return true;
3071
+ }
3072
+ function detach(entry) {
3073
+ entry.mounted?.unmount();
3074
+ entry.mounted = null;
3075
+ }
2781
3076
  return {
2782
3077
  register(face) {
2783
- const id = face.manifest.id;
2784
- if (faces.some((item) => item.manifest.id === id)) throw new Error(`套件 id 重复:${id}(id 会进持久化配置,不能撞)`);
2785
- const unregisterBoard = board.register(toBoardModule(face));
2786
- faces.push(face);
3078
+ const unit = typeof face?.unit === "string" ? face.unit.trim() : "";
3079
+ if (unit === "") {
3080
+ warnRefusal("", ["registerUnit({ unit, card }) 里的 unit 是空的 —— U1b 之前的旧形状是 registerUnit({ manifest, card });现在清单由外壳从**已装清单**读出来、按 unit 交给浏览器(ADR-0038 §2.2)"]);
3081
+ notify();
3082
+ return () => {};
3083
+ }
3084
+ if (registrations.some((entry) => entry.unit === unit)) throw new Error(`套件 id 重复:${unit}(id 会进持久化配置,不能撞)`);
3085
+ const entry = {
3086
+ face,
3087
+ unit,
3088
+ mounted: null
3089
+ };
3090
+ registrations.push(entry);
2787
3091
  manifestsCache = void 0;
3092
+ if (!attach(entry)) {
3093
+ warnRefusal(unit, [`宿主没有担保 unit「${unit}」的清单 —— 这个包要么没装进这份 profile,要么它的 dsh.workbench 没通过校验。卡片先不挂:清单只有一处真相(ADR-0038 §2.2)`]);
3094
+ options.onUnknownUnit?.(unit);
3095
+ }
2788
3096
  notify();
2789
3097
  return () => {
2790
- const at = faces.findIndex((item) => item.manifest.id === id);
3098
+ const at = registrations.indexOf(entry);
2791
3099
  if (at < 0) return;
2792
- faces.splice(at, 1);
3100
+ registrations.splice(at, 1);
3101
+ detach(entry);
3102
+ takeBackRefusal(unit);
2793
3103
  manifestsCache = void 0;
2794
- unregisterBoard();
2795
3104
  notify();
2796
3105
  };
2797
3106
  },
2798
- all: () => faces,
3107
+ all: () => registrations.map((entry) => entry.face),
2799
3108
  manifests: () => {
2800
- manifestsCache ??= faces.map((face) => face.manifest);
3109
+ manifestsCache ??= registrations.flatMap((entry) => entry.mounted === null ? [] : [entry.mounted.manifest]);
2801
3110
  return manifestsCache;
2802
3111
  },
3112
+ refusals: () => refusals,
3113
+ setManifests(manifests) {
3114
+ known = new Map(manifests.map((manifest) => [manifest.id, manifest]));
3115
+ let changed = false;
3116
+ for (const entry of registrations) {
3117
+ const next = known.get(entry.unit);
3118
+ if (entry.mounted === null) {
3119
+ if (next !== void 0 && attach(entry)) changed = true;
3120
+ continue;
3121
+ }
3122
+ if (next === void 0) {
3123
+ detach(entry);
3124
+ warnRefusal(entry.unit, [`宿主不再担保 unit「${entry.unit}」的清单(模块被卸了?)—— 这张卡片先摘下来`]);
3125
+ changed = true;
3126
+ }
3127
+ }
3128
+ if (!changed) return;
3129
+ manifestsCache = void 0;
3130
+ notify();
3131
+ },
2803
3132
  board,
2804
3133
  subscribe(listener) {
2805
3134
  listeners.add(listener);
@@ -2834,11 +3163,9 @@ var messages = {
2834
3163
  };
2835
3164
  var manifest = {
2836
3165
  id: FIXTURE_UNIT_ID,
2837
- kind: "board",
2838
3166
  card: {
2839
3167
  titleKey: KEY.cardTitle,
2840
- order: 900,
2841
- enabledByDefault: true
3168
+ order: 900
2842
3169
  },
2843
3170
  tools: [],
2844
3171
  host: { inject: [] }
@@ -2861,6 +3188,7 @@ function FixtureCard({ t, onRefresh }) {
2861
3188
  }))));
2862
3189
  }
2863
3190
  var FIXTURE_FACE = {
3191
+ unit: FIXTURE_UNIT_ID,
2864
3192
  manifest,
2865
3193
  messages,
2866
3194
  card: FixtureCard
@@ -2881,11 +3209,9 @@ var crashMessages = {
2881
3209
  };
2882
3210
  var crashManifest = {
2883
3211
  id: CRASH_UNIT_ID,
2884
- kind: "board",
2885
3212
  card: {
2886
3213
  titleKey: CRASH_KEY.cardTitle,
2887
- order: 899,
2888
- enabledByDefault: true
3214
+ order: 899
2889
3215
  },
2890
3216
  tools: [],
2891
3217
  host: { inject: [] }
@@ -2894,6 +3220,7 @@ function CrashingCard() {
2894
3220
  throw new Error("夹具:这张卡片按 wbCrash=1 故意渲染失败(守「一张卡片崩溃不带走面板」)");
2895
3221
  }
2896
3222
  var CRASH_FACE = {
3223
+ unit: CRASH_UNIT_ID,
2897
3224
  manifest: crashManifest,
2898
3225
  messages: crashMessages,
2899
3226
  card: CrashingCard
@@ -2906,8 +3233,8 @@ function fixtureRoster(roster) {
2906
3233
  return hasCard(roster) ? roster : SAMPLE_ROSTER;
2907
3234
  }
2908
3235
  function fixtureSeed(roster, all, crash = false) {
2909
- const base = hasCard(roster) ? all : [toBoardModule(FIXTURE_FACE)];
2910
- return crash ? [toBoardModule(CRASH_FACE), ...base] : base;
3236
+ const base = hasCard(roster) ? all : [toBoardModule(FIXTURE_FACE, FIXTURE_FACE.manifest)];
3237
+ return crash ? [toBoardModule(CRASH_FACE, CRASH_FACE.manifest), ...base] : base;
2911
3238
  }
2912
3239
  //#endregion
2913
3240
  //#region src/app/layouts/rightbar/market-rows.ts
@@ -2927,6 +3254,25 @@ var MARKET_DOT = {
2927
3254
  unchecked: "idle",
2928
3255
  unknown: "idle"
2929
3256
  };
3257
+ function marketRowTitle(row, t) {
3258
+ return row.label ?? t(row.titleKey);
3259
+ }
3260
+ function withClientRefusals(modules, refusals) {
3261
+ if (refusals.length === 0) return modules;
3262
+ const byUnit = new Map(refusals.map((one) => [one.unit, one.reasons]));
3263
+ return modules.map((module) => {
3264
+ const reasons = byUnit.get(module.unit);
3265
+ return reasons === void 0 ? module : {
3266
+ ...module,
3267
+ clientRefused: reasons
3268
+ };
3269
+ });
3270
+ }
3271
+ function refusalReasons(host, client) {
3272
+ const reported = host ?? [];
3273
+ if (reported.length > 0) return reported;
3274
+ return [...new Set(client ?? [])];
3275
+ }
2930
3276
  function mergeTitles(known, manifests) {
2931
3277
  let next = null;
2932
3278
  for (const manifest of manifests) {
@@ -2945,11 +3291,16 @@ function installedRows(modules, manifests, listed, known) {
2945
3291
  const latest = module.latest;
2946
3292
  const outdated = latest !== void 0 && latest !== module.installed;
2947
3293
  const loaded = titles.has(module.unit);
3294
+ const refused = refusalReasons(module.refused, module.clientRefused);
2948
3295
  rows.push({
2949
3296
  id: module.unit,
2950
3297
  titleKey: titles.get(module.unit) ?? known?.get(module.unit) ?? module.pkg,
2951
3298
  state: outdated ? "outdated" : "installed",
2952
- ...loaded ? {} : { hint: "panels.market.moduleRestartHint" },
3299
+ ...loaded || refused.length > 0 ? {} : { hint: "panels.market.moduleRestartHint" },
3300
+ ...refused.length === 0 ? {} : { note: {
3301
+ key: "panels.market.moduleRefused",
3302
+ params: { reason: refused.join(";") }
3303
+ } },
2953
3304
  ...module.local === true ? { local: true } : {},
2954
3305
  pkg: module.pkg,
2955
3306
  canInstall: false,
@@ -2968,6 +3319,7 @@ function installedRows(modules, manifests, listed, known) {
2968
3319
  return rows;
2969
3320
  }
2970
3321
  function marketActions$1(row) {
3322
+ if (row.plugin === true) return [...row.canUpdate ? ["update-plugin"] : [], ...row.canUninstall ? ["uninstall-plugin"] : []];
2971
3323
  return [
2972
3324
  ...row.canInstall ? ["install"] : [],
2973
3325
  ...row.moduleUpdate === void 0 ? row.canUpdate ? ["update"] : [] : ["update-bundle"],
@@ -2991,7 +3343,7 @@ function envNameOf(source) {
2991
3343
  if (source.kind === "npm") return source.registryEnv;
2992
3344
  return "";
2993
3345
  }
2994
- function noteOf(state, install, manifest) {
3346
+ function noteOf$1(state, install, manifest) {
2995
3347
  if (state === "installed") return void 0;
2996
3348
  if (install === "env-missing") return {
2997
3349
  key: "panels.market.needEnv",
@@ -3050,7 +3402,7 @@ function marketRows(manifests, capabilities, updates = {}) {
3050
3402
  const capability = capabilities?.[manifest.id];
3051
3403
  const install = capability?.install;
3052
3404
  const state = capabilities === null ? "checking" : stateOf(capability);
3053
- const note = noteOf(state, install, manifest);
3405
+ const note = noteOf$1(state, install, manifest);
3054
3406
  const onMachine = isOnMachine(state);
3055
3407
  const reAdopt = capability?.released === true && install === void 0;
3056
3408
  const entry = updates[manifest.id];
@@ -3222,6 +3574,60 @@ function useHostWrite(options) {
3222
3574
  };
3223
3575
  }
3224
3576
  //#endregion
3577
+ //#region src/app/layouts/rightbar/market-plugin-rows.ts
3578
+ function installedPluginKept(value) {
3579
+ if (typeof value !== "object" || value === null) return void 0;
3580
+ const kept = value.kept;
3581
+ if (typeof kept !== "object" || kept === null) return void 0;
3582
+ const kind = kept.kind;
3583
+ if (kind === "held") return { key: "panels.market.pluginKeptHeld" };
3584
+ if (kind === "unnamed") return { key: "panels.market.pluginKeptUnnamed" };
3585
+ if (kind === "unsure") return { key: "panels.market.pluginKeptUnsure" };
3586
+ if (kind !== "claimed") return void 0;
3587
+ const raw = kept.by;
3588
+ const names = Array.isArray(raw) ? raw.filter((item) => typeof item === "string") : [];
3589
+ return names.length === 0 ? { key: "panels.market.pluginKeptUnsure" } : {
3590
+ key: "panels.market.pluginKeptInUse",
3591
+ names
3592
+ };
3593
+ }
3594
+ function noteOf(plugin) {
3595
+ if (plugin.broken === true) return {
3596
+ key: "panels.market.pluginBroken",
3597
+ params: {}
3598
+ };
3599
+ if (plugin.local === true) return void 0;
3600
+ if (plugin.name !== void 0 && plugin.name !== plugin.pkg) return {
3601
+ key: "panels.market.pluginAlsoKnownAs",
3602
+ params: { pkg: plugin.pkg }
3603
+ };
3604
+ }
3605
+ function installedPluginRows(plugins) {
3606
+ return plugins.map((plugin) => {
3607
+ const name = plugin.name ?? plugin.pkg;
3608
+ const outdated = plugin.installed !== void 0 && plugin.latest !== void 0 && plugin.latest !== plugin.installed;
3609
+ const note = noteOf(plugin);
3610
+ return {
3611
+ id: `plugin:${plugin.pkg}`,
3612
+ titleKey: plugin.pkg,
3613
+ label: name,
3614
+ plugin: true,
3615
+ state: outdated ? "outdated" : "installed",
3616
+ canInstall: false,
3617
+ canUpdate: outdated,
3618
+ canUninstall: true,
3619
+ ...plugin.local === true ? { local: true } : {},
3620
+ ...plugin.broken === true ? { broken: true } : {},
3621
+ ...note === void 0 ? {} : { note },
3622
+ pkg: plugin.pkg,
3623
+ ...plugin.installed === void 0 ? {} : { version: {
3624
+ installed: plugin.installed,
3625
+ ...plugin.latest === void 0 ? {} : { latest: plugin.latest }
3626
+ } }
3627
+ };
3628
+ });
3629
+ }
3630
+ //#endregion
3225
3631
  //#region src/app/layouts/market/add-module-text.ts
3226
3632
  var GUIDE_ITEMS = [
3227
3633
  {
@@ -3340,17 +3746,23 @@ var PROBLEM_KEYS = {
3340
3746
  "not-found": "panels.market.add.problemNotFound",
3341
3747
  "not-a-package": "panels.market.add.problemNotPackage",
3342
3748
  "not-a-bundle": "panels.market.add.problemNotBundle",
3749
+ "not-a-workbench-module": "panels.market.add.problemNotWorkbenchModule",
3750
+ "not-a-module-or-plugin": "panels.market.add.problemNotModuleOrPlugin",
3343
3751
  network: "panels.market.add.problemNetwork",
3344
3752
  unknown: "panels.market.add.problemUnknown"
3345
3753
  };
3346
3754
  function problemKey(problem) {
3347
3755
  return PROBLEM_KEYS[problem] ?? PROBLEM_KEYS.unknown;
3348
3756
  }
3757
+ function heldWizardPhase(phase, classifying) {
3758
+ return classifying && (phase === "preview" || phase === "idle") ? "inspecting" : phase;
3759
+ }
3349
3760
  var FAILURE_KEYS = {
3350
3761
  network: "panels.market.add.failureNetwork",
3351
3762
  "spec-host": "panels.market.add.failureNetworkHost",
3352
3763
  "not-found": "panels.market.add.failureNotFound",
3353
3764
  "no-matching-version": "panels.market.add.failureNoMatchingVersion",
3765
+ "not-a-module-or-plugin": "panels.market.add.failureNotModuleOrPlugin",
3354
3766
  "disk-full": "panels.market.add.failureDiskFull",
3355
3767
  permission: "panels.market.add.failurePermission",
3356
3768
  "build-blocked": "panels.market.add.failureBuildBlocked",
@@ -3455,7 +3867,9 @@ var PROGRESS_KEY = {
3455
3867
  "update-bundle": "panels.market.updating",
3456
3868
  "remove-bundle": "panels.market.uninstalling",
3457
3869
  uninstall: "panels.market.uninstalling",
3458
- "update-dependency": "panels.market.updating"
3870
+ "update-dependency": "panels.market.updating",
3871
+ "update-plugin": "panels.market.updating",
3872
+ "uninstall-plugin": "panels.market.uninstalling"
3459
3873
  };
3460
3874
  var ACTION_LABEL_KEY = {
3461
3875
  install: "panels.market.install",
@@ -3463,7 +3877,9 @@ var ACTION_LABEL_KEY = {
3463
3877
  "update-bundle": "panels.market.update",
3464
3878
  "remove-bundle": "panels.market.uninstall",
3465
3879
  uninstall: "panels.market.uninstall",
3466
- "update-dependency": "panels.market.update"
3880
+ "update-dependency": "panels.market.update",
3881
+ "update-plugin": "panels.market.update",
3882
+ "uninstall-plugin": "panels.market.uninstall"
3467
3883
  };
3468
3884
  //#endregion
3469
3885
  //#region src/app/layouts/rightbar/SharedSection.tsx
@@ -3525,7 +3941,11 @@ function PanelFoot({ text, size = "sm", children }) {
3525
3941
  }
3526
3942
  //#endregion
3527
3943
  //#region src/app/layouts/rightbar/MarketView.tsx
3528
- var CONFIRM_UNINSTALL = ["uninstall", "remove-bundle"];
3944
+ var CONFIRM_UNINSTALL = [
3945
+ "uninstall",
3946
+ "remove-bundle",
3947
+ "uninstall-plugin"
3948
+ ];
3529
3949
  var CONFIRM_REMOVE = ["remove-bundle"];
3530
3950
  function MarketLine({ t, row, pending, failure, onAct }) {
3531
3951
  const running = pending !== null && pending.id === row.id ? pending : null;
@@ -3535,7 +3955,7 @@ function MarketLine({ t, row, pending, failure, onAct }) {
3535
3955
  return /* @__PURE__ */ h("li", { className: MarketPage_module_default.marketRow }, /* @__PURE__ */ h("span", {
3536
3956
  className: MarketPage_module_default.marketIcon,
3537
3957
  "aria-hidden": "true"
3538
- }, /* @__PURE__ */ h(IconModules, { size: 18 })), /* @__PURE__ */ h("div", { className: MarketPage_module_default.marketMain }, /* @__PURE__ */ h("span", { className: MarketPage_module_default.marketName }, t(row.titleKey), row.version === void 0 ? null : /* @__PURE__ */ h("span", { className: MarketPage_module_default.marketVersion }, row.version.installed !== void 0 && row.version.latest !== void 0 && row.version.installed !== row.version.latest ? t("panels.market.versionArrow", {
3958
+ }, /* @__PURE__ */ h(IconModules, { size: 18 })), /* @__PURE__ */ h("div", { className: MarketPage_module_default.marketMain }, /* @__PURE__ */ h("span", { className: MarketPage_module_default.marketName }, marketRowTitle(row, t), row.version === void 0 ? null : /* @__PURE__ */ h("span", { className: MarketPage_module_default.marketVersion }, row.version.installed !== void 0 && row.version.latest !== void 0 && row.version.installed !== row.version.latest ? t("panels.market.versionArrow", {
3539
3959
  from: row.version.installed,
3540
3960
  to: row.version.latest
3541
3961
  }) : row.version.installed ?? row.version.latest ?? "")), /* @__PURE__ */ h("span", {
@@ -3560,11 +3980,14 @@ function MarketLine({ t, row, pending, failure, onAct }) {
3560
3980
  variant: action === "install" ? "primary" : "outline",
3561
3981
  className: danger ? "wb-dangerAction" : void 0,
3562
3982
  icon: danger ? /* @__PURE__ */ h(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineRegular, { size: 13 }) : void 0,
3563
- "aria-label": danger ? t("panels.market.uninstallLabel", { name: t(row.titleKey) }) : void 0,
3983
+ "aria-label": danger ? t("panels.market.uninstallLabel", { name: marketRowTitle(row, t) }) : void 0,
3564
3984
  disabled: locked,
3565
3985
  onClick: () => onAct(action)
3566
3986
  }, t(ACTION_LABEL_KEY[action]));
3567
- }))), row.note === void 0 ? null : /* @__PURE__ */ h("div", { className: MarketPage_module_default.marketNote }, t(row.note.key, row.note.params)), failure === null ? null : /* @__PURE__ */ h("div", { className: MarketPage_module_default.marketFailure }, failure));
3987
+ }))), row.note === void 0 ? null : /* @__PURE__ */ h("div", {
3988
+ className: MarketPage_module_default.marketNote,
3989
+ title: row.note.params.reason === void 0 ? void 0 : t("panels.market.moduleRefusedDetail", row.note.params)
3990
+ }, t(row.note.key, row.note.params)), failure === null ? null : /* @__PURE__ */ h("div", { className: MarketPage_module_default.marketFailure }, failure));
3568
3991
  }
3569
3992
  function ConfirmUninstall({ t, name, onConfirm, onCancel }) {
3570
3993
  const [acknowledged, setAcknowledged] = useState(false);
@@ -3593,7 +4016,48 @@ function MarketSection({ t, titleKey, rows, pending, failureOf, act, confirmActi
3593
4016
  onAct: (action) => confirmActions.includes(action) ? onConfirming(row.id) : void act(row.id, action)
3594
4017
  }))));
3595
4018
  }
3596
- function MarketView({ t, manifests, capabilities, freshness, onChanged, updates, installed, installedRead, settling, mock }) {
4019
+ function MarketBody({ t, shown, pluginList, installedList, shared, pending, failed, notice, act, onConfirming }) {
4020
+ const failureOf = (id) => failed?.id === id ? failed.message : null;
4021
+ return /* @__PURE__ */ h(Fragment, null, notice === null ? null : /* @__PURE__ */ h("div", { className: MarketPage_module_default.marketNote }, notice), marketSections(shown).map((section) => /* @__PURE__ */ h(MarketSection, {
4022
+ key: section.id,
4023
+ t,
4024
+ titleKey: section.titleKey,
4025
+ rows: section.rows,
4026
+ pending,
4027
+ failureOf,
4028
+ act,
4029
+ confirmActions: CONFIRM_UNINSTALL,
4030
+ onConfirming
4031
+ })), /* @__PURE__ */ h(MarketSection, {
4032
+ t,
4033
+ titleKey: "panels.market.sectionPlugins",
4034
+ rows: pluginList,
4035
+ pending,
4036
+ failureOf,
4037
+ act,
4038
+ confirmActions: CONFIRM_UNINSTALL,
4039
+ onConfirming
4040
+ }), /* @__PURE__ */ h(MarketSection, {
4041
+ t,
4042
+ titleKey: installedList.length > 0 && installedList.every((row) => row.local === true) ? "panels.market.sectionLocal" : "panels.market.sectionUnlisted",
4043
+ rows: installedList,
4044
+ pending,
4045
+ failureOf,
4046
+ act,
4047
+ confirmActions: CONFIRM_REMOVE,
4048
+ onConfirming
4049
+ }), /* @__PURE__ */ h(PanelFoot, {
4050
+ size: "md",
4051
+ text: t("panels.market.foot")
4052
+ }), /* @__PURE__ */ h(SharedSection, {
4053
+ t,
4054
+ rows: shared,
4055
+ pending,
4056
+ failureOf,
4057
+ act
4058
+ }));
4059
+ }
4060
+ function MarketView({ t, manifests, capabilities, freshness, onChanged, updates, installed, installedRead, plugins, settling, mock }) {
3597
4061
  const [titles, setTitles] = useState(/* @__PURE__ */ new Map());
3598
4062
  useEffect(() => {
3599
4063
  if (mock === void 0) setTitles((known) => mergeTitles(known, manifests));
@@ -3610,6 +4074,7 @@ function MarketView({ t, manifests, capabilities, freshness, onChanged, updates,
3610
4074
  });
3611
4075
  const [confirming, setConfirming] = useState(mock?.confirming ?? null);
3612
4076
  const [failed, setFailed] = useState(mock?.failure ?? null);
4077
+ const [notice, setNotice] = useState(mock?.notice ?? null);
3613
4078
  const removing = pending !== null && pending.action === "remove-bundle" && failed?.id !== pending.id ? pending.id : null;
3614
4079
  const installedList = installedRows((mock?.installed ?? installed ?? []).filter((module) => !(settling?.has(module.unit) ?? false) && module.unit !== removing), catalog, new Set(rows.map((row) => row.id)), titles);
3615
4080
  const pendingRow = removing === null || pending === null || rows.some((row) => row.id === removing) ? void 0 : {
@@ -3623,7 +4088,12 @@ function MarketView({ t, manifests, capabilities, freshness, onChanged, updates,
3623
4088
  ...pending.version === void 0 ? {} : { version: { installed: pending.version } }
3624
4089
  };
3625
4090
  const shown = pendingRow === void 0 ? rows : [...rows, pendingRow];
3626
- const rowById = new Map([...shown, ...installedList].map((row) => [row.id, row]));
4091
+ const pluginList = installedPluginRows(mock?.plugins ?? plugins ?? []);
4092
+ const rowById = new Map([
4093
+ ...shown,
4094
+ ...installedList,
4095
+ ...pluginList
4096
+ ].map((row) => [row.id, row]));
3627
4097
  const confirmingRow = confirming === null ? void 0 : rowById.get(confirming);
3628
4098
  const act = async (id, action) => {
3629
4099
  if (mock !== void 0) return;
@@ -3637,13 +4107,15 @@ function MarketView({ t, manifests, capabilities, freshness, onChanged, updates,
3637
4107
  });
3638
4108
  setConfirming(null);
3639
4109
  setFailed(null);
4110
+ if (action !== "uninstall-plugin") setNotice(null);
3640
4111
  const dependency = action === "update-dependency";
4112
+ const byName = action === "remove-bundle" || action === "update-plugin" || action === "uninstall-plugin";
3641
4113
  const outcome = await provision.send(dependency ? {
3642
4114
  action,
3643
4115
  id
3644
- } : action === "remove-bundle" ? {
4116
+ } : byName ? {
3645
4117
  action,
3646
- name: rowById.get(id)?.pkg ?? ""
4118
+ name: row?.pkg ?? ""
3647
4119
  } : {
3648
4120
  action,
3649
4121
  unit: id
@@ -3657,6 +4129,10 @@ function MarketView({ t, manifests, capabilities, freshness, onChanged, updates,
3657
4129
  });
3658
4130
  return;
3659
4131
  }
4132
+ if (action === "uninstall-plugin") {
4133
+ const kept = installedPluginKept(outcome.value);
4134
+ setNotice(kept === void 0 ? null : t(kept.key, kept.names === void 0 ? {} : { names: kept.names.join(t("panels.market.listSeparator")) }));
4135
+ }
3660
4136
  const reason = changeFailure(changeOf(outcome.value), t);
3661
4137
  onChanged();
3662
4138
  if (reason !== void 0) setFailed({
@@ -3664,46 +4140,32 @@ function MarketView({ t, manifests, capabilities, freshness, onChanged, updates,
3664
4140
  message: t("panels.market.failed", { reason })
3665
4141
  });
3666
4142
  };
3667
- if (!(freshness.loaded && installedRead !== false) || (settling?.size ?? 0) > 0 && shown.length === 0 && installedList.length === 0) return /* @__PURE__ */ h(MarketChecking, { t });
3668
- if (shown.length === 0 && installedList.length === 0) return /* @__PURE__ */ h(MarketEmpty, { t });
3669
- return /* @__PURE__ */ h(Fragment, null, marketSections(shown).map((section) => /* @__PURE__ */ h(MarketSection, {
3670
- key: section.id,
4143
+ const settled = marketSettled(freshness, installedRead);
4144
+ const nothing = shown.length === 0 && installedList.length === 0 && pluginList.length === 0;
4145
+ if (!settled || (settling?.size ?? 0) > 0 && shown.length === 0 && installedList.length === 0) return /* @__PURE__ */ h(MarketChecking, { t });
4146
+ if (nothing) return /* @__PURE__ */ h(MarketEmpty, { t });
4147
+ return /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h(MarketBody, {
3671
4148
  t,
3672
- titleKey: section.titleKey,
3673
- rows: section.rows,
3674
- pending,
3675
- failureOf: (id) => failed?.id === id ? failed.message : null,
3676
- act,
3677
- confirmActions: CONFIRM_UNINSTALL,
3678
- onConfirming: setConfirming
3679
- })), /* @__PURE__ */ h(MarketSection, {
3680
- t,
3681
- titleKey: installedList.length > 0 && installedList.every((row) => row.local === true) ? "panels.market.sectionLocal" : "panels.market.sectionUnlisted",
3682
- rows: installedList,
4149
+ shown,
4150
+ pluginList,
4151
+ installedList,
4152
+ shared,
3683
4153
  pending,
3684
- failureOf: (id) => failed?.id === id ? failed.message : null,
4154
+ failed,
4155
+ notice,
3685
4156
  act,
3686
- confirmActions: CONFIRM_REMOVE,
3687
4157
  onConfirming: setConfirming
3688
- }), /* @__PURE__ */ h(PanelFoot, {
3689
- size: "md",
3690
- text: t("panels.market.foot")
3691
- }), /* @__PURE__ */ h(SharedSection, {
3692
- t,
3693
- rows: shared,
3694
- pending,
3695
- failureOf: (id) => failed?.id === id ? failed.message : null,
3696
- act
3697
4158
  }), /* @__PURE__ */ h(ConfirmUninstall, {
3698
4159
  key: confirming ?? "",
3699
4160
  t,
3700
- name: confirmingRow === void 0 ? null : t(confirmingRow.titleKey),
4161
+ name: confirmingRow === void 0 ? null : marketRowTitle(confirmingRow, t),
3701
4162
  onCancel: () => setConfirming(null),
3702
4163
  onConfirm: () => {
3703
4164
  const id = confirming;
3704
4165
  setConfirming(null);
3705
4166
  if (id === null) return;
3706
- act(id, rowById.get(id)?.pkg === void 0 ? "uninstall" : "remove-bundle");
4167
+ const row = rowById.get(id);
4168
+ act(id, row?.plugin === true ? "uninstall-plugin" : row?.pkg === void 0 ? "uninstall" : "remove-bundle");
3707
4169
  }
3708
4170
  }));
3709
4171
  }
@@ -3713,6 +4175,9 @@ var BUNDLE_REGISTRIES_ROUTE = "bundle-registries";
3713
4175
  var BUNDLE_INSPECT_ROUTE = "bundle-inspect";
3714
4176
  var MODULE_VERSIONS_ROUTE = "module-versions";
3715
4177
  //#endregion
4178
+ //#region src/shared/api/installed-plugins.ts
4179
+ var INSTALLED_PLUGINS_ROUTE = "panels/plugins";
4180
+ //#endregion
3716
4181
  //#region src/shared/hooks/bundle-queries.ts
3717
4182
  function readBundleRegistries() {
3718
4183
  return apiGet(BUNDLE_REGISTRIES_ROUTE);
@@ -3726,10 +4191,14 @@ function readBundleInspection(spec, registry) {
3726
4191
  function readModuleVersions(force = false) {
3727
4192
  return apiGet(MODULE_VERSIONS_ROUTE, force ? { force: "1" } : void 0);
3728
4193
  }
4194
+ function readInstalledPlugins(force = false) {
4195
+ return apiGet(INSTALLED_PLUGINS_ROUTE, force ? { force: "1" } : void 0);
4196
+ }
3729
4197
  //#endregion
3730
4198
  //#region src/app/layouts/market/use-module-versions.ts
3731
4199
  var state = {
3732
4200
  modules: null,
4201
+ plugins: null,
3733
4202
  read: false,
3734
4203
  checkedAt: null
3735
4204
  };
@@ -3754,10 +4223,13 @@ function runModuleVersionsRead(force = false) {
3754
4223
  if (inFlight !== null && inFlight.force === force) return inFlight.promise;
3755
4224
  const seq = readSeq += 1;
3756
4225
  if (force) newestForceSeq = seq;
3757
- const promise = readModuleVersions(force).catch(() => null).then((response) => {
4226
+ const promise = Promise.all([readModuleVersions(force), readInstalledPlugins(force)]).catch(() => null).then((responses) => {
3758
4227
  if (!force && seq < newestForceSeq) return;
4228
+ const versions = responses?.[0];
4229
+ const plugins = responses?.[1];
3759
4230
  publish({
3760
- modules: response?.ok === true ? response.modules : state.modules,
4231
+ modules: versions?.ok === true ? versions.modules : state.modules,
4232
+ plugins: plugins?.ok === true ? plugins.plugins : state.plugins,
3761
4233
  read: true,
3762
4234
  checkedAt: Date.now()
3763
4235
  });
@@ -3789,6 +4261,7 @@ function useModuleVersions() {
3789
4261
  }, []);
3790
4262
  return {
3791
4263
  modules: value.modules,
4264
+ plugins: value.plugins,
3792
4265
  read: value.read,
3793
4266
  checkedAt: value.checkedAt,
3794
4267
  reload
@@ -4007,7 +4480,7 @@ function AddModulePending({ t, kind }) {
4007
4480
  })), /* @__PURE__ */ h("h2", {
4008
4481
  className: AddModule_module_default.pendingTitle,
4009
4482
  role: "status"
4010
- }, kind === "inspecting" ? t("panels.market.add.checking") : t("panels.market.add.installingTitle"))));
4483
+ }, kind === "inspecting" ? t("panels.market.add.classifying") : t("panels.market.add.installingTitle"))));
4011
4484
  }
4012
4485
  function AddModulePreview({ t, state }) {
4013
4486
  const preview = state.preview;
@@ -4025,6 +4498,7 @@ function AddModuleResult({ t, state }) {
4025
4498
  const restart = outcome.change.application === "restart-required";
4026
4499
  const cancelled = outcome.change.application === "cancelled";
4027
4500
  const notModule = outcome.supply.state === "not-a-module";
4501
+ const plugin = outcome.plugin;
4028
4502
  const name = outcome.module.kind === "module" ? outcome.module.unit : outcome.module.pkg;
4029
4503
  return /* @__PURE__ */ h("div", { className: AddModule_module_default.body }, /* @__PURE__ */ h("div", { className: AddModule_module_default.hero }, /* @__PURE__ */ h("span", {
4030
4504
  className: AddModule_module_default.heroIcon,
@@ -4036,7 +4510,7 @@ function AddModuleResult({ t, state }) {
4036
4510
  }) : /* @__PURE__ */ h(_deepseek_ai_dsh_client_ui_primitives.IconCheckCircleFillRegular, { size: 28 })), /* @__PURE__ */ h("h2", {
4037
4511
  className: AddModule_module_default.wizardTitle,
4038
4512
  role: failed ? "alert" : "status"
4039
- }, failed ? t("panels.market.add.failedTitle") : cancelled ? t("panels.market.add.cancelledTitle") : t("panels.market.add.installedTitle"))), /* @__PURE__ */ h("div", { className: AddModule_module_default.subject }, /* @__PURE__ */ h("p", { className: AddModule_module_default.subjectName }, outcome.module.pkg), name === outcome.module.pkg ? null : /* @__PURE__ */ h("p", { className: AddModule_module_default.subjectMeta }, name), outcome.module.version === void 0 ? null : /* @__PURE__ */ h("p", { className: AddModule_module_default.subjectMeta }, t("panels.market.add.installVersion", { version: outcome.module.version }))), failed ? /* @__PURE__ */ h("p", {
4513
+ }, failed ? t("panels.market.add.failedTitle") : cancelled ? t("panels.market.add.cancelledTitle") : t("panels.market.add.installedTitle"))), /* @__PURE__ */ h("div", { className: AddModule_module_default.subject }, /* @__PURE__ */ h("p", { className: AddModule_module_default.subjectName }, outcome.module.pkg), name === outcome.module.pkg ? null : /* @__PURE__ */ h("p", { className: AddModule_module_default.subjectMeta }, name), outcome.module.version === void 0 ? null : /* @__PURE__ */ h("p", { className: AddModule_module_default.subjectMeta }, t("panels.market.add.installVersion", { version: outcome.module.version }))), plugin !== void 0 ? /* @__PURE__ */ h("p", { className: AddModule_module_default.result }, t("panels.market.add.installedPlugin", { name: plugin.name ?? plugin.pkg })) : failed ? /* @__PURE__ */ h("p", {
4040
4514
  className: AddModule_module_default.resultWarn,
4041
4515
  role: "alert"
4042
4516
  }, t("panels.market.add.failed", { reason })) : restart ? /* @__PURE__ */ h("p", { className: AddModule_module_default.resultWarn }, t("panels.market.add.doneRestart")) : cancelled || notModule ? null : /* @__PURE__ */ h("p", { className: AddModule_module_default.result }, t("panels.market.add.tookEffect")), builds.length === 0 ? null : /* @__PURE__ */ h("div", { className: AddModule_module_default.approval }, /* @__PURE__ */ h("p", { className: AddModule_module_default.approvalTitle }, t("panels.market.add.approveTitle")), /* @__PURE__ */ h("p", { className: AddModule_module_default.approvalText }, t("panels.market.add.approveHint", { packages: builds.join("、") })), /* @__PURE__ */ h("p", { className: AddModule_module_default.approvalCaution }, t("panels.market.add.approveCaution"))), supply === void 0 || outcome.supply.state !== "failed" && (failed || cancelled) ? null : /* @__PURE__ */ h("p", { className: supplyTone(outcome.supply.state) }, t(supply, { reason: outcome.supply.reason ?? "" })), state.failure === null ? null : /* @__PURE__ */ h("p", { className: AddModule_module_default.problem }, state.failure), outcome.change.errorDetail === void 0 ? null : /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h("p", { className: AddModule_module_default.detailLabel }, t("panels.market.add.details")), /* @__PURE__ */ h("p", { className: AddModule_module_default.detail }, outcome.change.errorDetail)));
@@ -4107,6 +4581,7 @@ var ADD_MODULE_MOCKS = [
4107
4581
  refused("not-found"),
4108
4582
  refused("not-a-package"),
4109
4583
  refused("not-a-bundle"),
4584
+ refused("not-a-module-or-plugin", "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上"),
4110
4585
  refused("network", "getaddrinfo ENOTFOUND registry.example.com"),
4111
4586
  refused("unknown", "pnpm 的返回码是 1"),
4112
4587
  {
@@ -4188,6 +4663,25 @@ var ADD_MODULE_MOCKS = [
4188
4663
  spec: "@dsh-workbench/module-hello",
4189
4664
  outcome: outcome({})
4190
4665
  },
4666
+ {
4667
+ id: "done-plugin",
4668
+ label: "装好了(opencli 插件)",
4669
+ phase: "result",
4670
+ spec: "opencli-plugin-cmdb",
4671
+ outcome: {
4672
+ change: change({ target: "opencli-plugin-cmdb" }),
4673
+ module: {
4674
+ kind: "unknown",
4675
+ pkg: "opencli-plugin-cmdb"
4676
+ },
4677
+ supply: { state: "no-needs" },
4678
+ plugin: {
4679
+ kind: "opencli-plugin",
4680
+ pkg: "opencli-plugin-cmdb",
4681
+ name: "cmdb"
4682
+ }
4683
+ }
4684
+ },
4191
4685
  {
4192
4686
  id: "done-restart",
4193
4687
  label: "装好了(要重启)",
@@ -4282,6 +4776,42 @@ function stateOfMock(base, mock) {
4282
4776
  };
4283
4777
  }
4284
4778
  //#endregion
4779
+ //#region src/shared/hooks/use-visible-busy.ts
4780
+ var use_visible_busy_exports = /* @__PURE__ */ __exportAll({
4781
+ busyView: () => busyView,
4782
+ heldPhase: () => heldPhase,
4783
+ useVisibleBusy: () => useVisibleBusy
4784
+ });
4785
+ function heldPhase(phase, refreshing) {
4786
+ return refreshing && phase === "ready" ? "loading" : phase;
4787
+ }
4788
+ function busyView(active, visible) {
4789
+ return {
4790
+ busy: visible,
4791
+ settling: visible && !active
4792
+ };
4793
+ }
4794
+ function useVisibleBusy(active, floorMs) {
4795
+ const [visible, setVisible] = useState(active);
4796
+ const since = useRef(null);
4797
+ useEffect(() => {
4798
+ if (active) {
4799
+ if (since.current === null) since.current = Date.now();
4800
+ setVisible(true);
4801
+ return;
4802
+ }
4803
+ const held = holdBusy(since.current, Date.now(), floorMs);
4804
+ since.current = null;
4805
+ if (!held.visible) {
4806
+ setVisible(false);
4807
+ return;
4808
+ }
4809
+ const timer = setTimeout(() => setVisible(false), held.releaseInMs);
4810
+ return () => clearTimeout(timer);
4811
+ }, [active, floorMs]);
4812
+ return busyView(active, visible);
4813
+ }
4814
+ //#endregion
4285
4815
  //#region src/app/layouts/market/use-add-module.ts
4286
4816
  function newRequestId() {
4287
4817
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -4304,6 +4834,7 @@ function useAddModule(options) {
4304
4834
  const [failure, setFailure] = useState(null);
4305
4835
  const generationRef = useRef(0);
4306
4836
  const requestIdRef = useRef("");
4837
+ const { busy: classifying } = useVisibleBusy(phase === "inspecting", 500);
4307
4838
  const write = useHostWrite({
4308
4839
  channel: "provision",
4309
4840
  networkText: failedText
@@ -4401,7 +4932,8 @@ function useAddModule(options) {
4401
4932
  setOutcome({
4402
4933
  change: value.change,
4403
4934
  module: value.module,
4404
- supply: value.supply
4935
+ supply: value.supply,
4936
+ ...value.plugin === void 0 ? {} : { plugin: value.plugin }
4405
4937
  });
4406
4938
  setPhase("result");
4407
4939
  if (value.change.application === "applied" || value.change.application === "restart-required") onInstalled();
@@ -4446,7 +4978,7 @@ function useAddModule(options) {
4446
4978
  custom,
4447
4979
  registryOpen,
4448
4980
  guideOpen,
4449
- phase,
4981
+ phase: heldWizardPhase(phase, classifying),
4450
4982
  choices,
4451
4983
  problem,
4452
4984
  preview,
@@ -4604,10 +5136,10 @@ function makeMarketEntryIcon(units) {
4604
5136
  function MarketPanel({ t, units }) {
4605
5137
  const manifests = useLiveManifests(units);
4606
5138
  const { capabilities, checkedAt, checking, forceProbe, reload: reloadCapability } = useCapabilityMap(0);
4607
- const { modules, read: installedRead, reload: reloadVersions } = useModuleVersions();
5139
+ const { modules, plugins, read: installedRead, reload: reloadVersions } = useModuleVersions();
4608
5140
  const [adding, setAdding] = useState(false);
4609
5141
  const updates = useMemo(() => marketUpdatesOf(modules), [modules]);
4610
- const installed = modules ?? void 0;
5142
+ const installed = modules === null ? void 0 : withClientRefusals(modules, units.refusals());
4611
5143
  const reload = useCallback(() => {
4612
5144
  reloadCapability();
4613
5145
  reloadVersions();
@@ -4664,11 +5196,14 @@ function MarketPanel({ t, units }) {
4664
5196
  });
4665
5197
  const mock = fixturePick.enabled ? fixturePick.current : void 0;
4666
5198
  const mockNonce = fixturePick.nonce;
5199
+ const shownFreshness = mock === void 0 ? freshness : mock.loaded === false ? pendingFreshness : MOCK_FRESHNESS;
5200
+ const shownRead = mock === void 0 ? installedRead : mock.installedRead ?? true;
5201
+ const busy = !marketSettled(shownFreshness, shownRead) || shownFreshness.checking;
4667
5202
  return /* @__PURE__ */ h("div", { className: MarketPage_module_default.marketPage }, /* @__PURE__ */ h("header", { className: MarketPage_module_default.marketPageHead }, /* @__PURE__ */ h("div", null, /* @__PURE__ */ h("h1", { className: MarketPage_module_default.marketPageTitle }, t("panels.tab.market")), /* @__PURE__ */ h("p", { className: MarketPage_module_default.marketPageIntro }, t("panels.market.lead"))), /* @__PURE__ */ h("div", { className: MarketPage_module_default.marketPageTools }, /* @__PURE__ */ h(Tip, { label: t("panels.market.recheck") }, /* @__PURE__ */ h("button", {
4668
5203
  type: "button",
4669
- className: `${MarketPage_module_default.marketIconButton}${checking ? ` ${MarketPage_module_default.marketIconButtonSpinning}` : ""}`,
5204
+ className: `${MarketPage_module_default.marketIconButton}${shownFreshness.checking ? ` ${MarketPage_module_default.marketIconButtonSpinning}` : ""}`,
4670
5205
  "aria-label": t("panels.market.checkUpdates"),
4671
- disabled: checking,
5206
+ disabled: busy,
4672
5207
  onClick: () => {
4673
5208
  forceProbe();
4674
5209
  reloadVersions({ force: true });
@@ -4681,6 +5216,7 @@ function MarketPanel({ t, units }) {
4681
5216
  size: "sm",
4682
5217
  className: MarketPage_module_default.marketAddButton,
4683
5218
  icon: /* @__PURE__ */ h(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 13 }),
5219
+ disabled: busy,
4684
5220
  onClick: () => setAdding(true)
4685
5221
  }, t("panels.market.add.open")))), fixturePick.enabled ? /* @__PURE__ */ h(MockStrip, {
4686
5222
  mocks: marketMocks,
@@ -4691,11 +5227,12 @@ function MarketPanel({ t, units }) {
4691
5227
  t,
4692
5228
  manifests,
4693
5229
  capabilities,
4694
- freshness: mock === void 0 ? freshness : mock.loaded === false ? pendingFreshness : MOCK_FRESHNESS,
5230
+ freshness: shownFreshness,
4695
5231
  onChanged: reload,
4696
5232
  updates,
4697
5233
  installed,
4698
- installedRead: mock === void 0 ? installedRead : mock.installedRead ?? true,
5234
+ installedRead: shownRead,
5235
+ plugins,
4699
5236
  settling: mock === void 0 ? settling : void 0,
4700
5237
  mock
4701
5238
  }), /* @__PURE__ */ h(AddModuleDialog, {
@@ -4751,6 +5288,41 @@ var CLIENT_UNITS = [];
4751
5288
  function assembleClientUnits(registry) {
4752
5289
  for (const face of CLIENT_UNITS) registry.register(face);
4753
5290
  }
5291
+ //#endregion
5292
+ //#region src/shared/api/unit-manifests.ts
5293
+ var UNIT_MANIFESTS_ROUTE = "unit-manifests";
5294
+ //#endregion
5295
+ //#region src/app/units/manifests.ts
5296
+ function createUnitManifestLoader(options = {}) {
5297
+ let inFlight = false;
5298
+ let queued = false;
5299
+ let watching = false;
5300
+ const subscribeFacts = options.subscribeFacts ?? subscribeFactRevision;
5301
+ const load = async (registry) => {
5302
+ if (inFlight) {
5303
+ queued = true;
5304
+ return;
5305
+ }
5306
+ inFlight = true;
5307
+ try {
5308
+ const response = await apiGet(UNIT_MANIFESTS_ROUTE).catch(() => null);
5309
+ if (response?.ok === true && Array.isArray(response.manifests)) registry.setManifests(response.manifests);
5310
+ } finally {
5311
+ inFlight = false;
5312
+ if (queued) {
5313
+ queued = false;
5314
+ load(registry);
5315
+ }
5316
+ }
5317
+ };
5318
+ return (registry) => {
5319
+ if (!watching) {
5320
+ watching = true;
5321
+ subscribeFacts(factOfRoute(UNIT_MANIFESTS_ROUTE), () => void load(registry));
5322
+ }
5323
+ load(registry);
5324
+ };
5325
+ }
4754
5326
  var BootGate_module_default = {
4755
5327
  actions: "_actions_jlw8j_1",
4756
5328
  progress: "_progress_jlw8j_2",
@@ -7428,42 +8000,6 @@ function useLongTask(options) {
7428
8000
  return { runs };
7429
8001
  }
7430
8002
  //#endregion
7431
- //#region src/shared/hooks/use-visible-busy.ts
7432
- var use_visible_busy_exports = /* @__PURE__ */ __exportAll({
7433
- busyView: () => busyView,
7434
- heldPhase: () => heldPhase,
7435
- useVisibleBusy: () => useVisibleBusy
7436
- });
7437
- function heldPhase(phase, refreshing) {
7438
- return refreshing && phase === "ready" ? "loading" : phase;
7439
- }
7440
- function busyView(active, visible) {
7441
- return {
7442
- busy: visible,
7443
- settling: visible && !active
7444
- };
7445
- }
7446
- function useVisibleBusy(active, floorMs) {
7447
- const [visible, setVisible] = useState(active);
7448
- const since = useRef(null);
7449
- useEffect(() => {
7450
- if (active) {
7451
- if (since.current === null) since.current = Date.now();
7452
- setVisible(true);
7453
- return;
7454
- }
7455
- const held = holdBusy(since.current, Date.now(), floorMs);
7456
- since.current = null;
7457
- if (!held.visible) {
7458
- setVisible(false);
7459
- return;
7460
- }
7461
- const timer = setTimeout(() => setVisible(false), held.releaseInMs);
7462
- return () => clearTimeout(timer);
7463
- }, [active, floorMs]);
7464
- return busyView(active, visible);
7465
- }
7466
- //#endregion
7467
8003
  //#region src/shared/hooks/use-unit-board.ts
7468
8004
  var use_unit_board_exports = /* @__PURE__ */ __exportAll({ useUnitBoard: () => useUnitBoard });
7469
8005
  function useUnitBoard(options) {
@@ -7677,7 +8213,7 @@ var kit = {
7677
8213
  };
7678
8214
  function messagesOf(units) {
7679
8215
  return units.map((face) => ({
7680
- unit: face.manifest.id,
8216
+ unit: face.unit,
7681
8217
  ...face.messages === void 0 ? {} : { messages: face.messages }
7682
8218
  }));
7683
8219
  }
@@ -7718,12 +8254,14 @@ function apply(ctx) {
7718
8254
  }
7719
8255
  }
7720
8256
  function mount(ctx) {
7721
- const units = createClientUnitRegistry();
8257
+ const loadUnitManifests = createUnitManifestLoader();
8258
+ const units = createClientUnitRegistry({ onUnknownUnit: () => loadUnitManifests(units) });
7722
8259
  ctx.provide(UNITS_SERVICE, {
7723
8260
  registerUnit: (face) => units.register(face),
7724
8261
  manifests: () => units.manifests()
7725
8262
  });
7726
8263
  assembleClientUnits(units);
8264
+ loadUnitManifests(units);
7727
8265
  const store = rightbarStore;
7728
8266
  const observable = {
7729
8267
  getSnapshot: () => store.getSnapshot(),