@dsh-workbench/core 0.1.16 → 0.1.17

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 (66) hide show
  1. package/client.js +660 -128
  2. package/dist/client/client.body.js +660 -128
  3. package/dist/client/index.js +660 -128
  4. package/dist/host/index.js +1498 -263
  5. package/dist/types/app/client.d.ts +124 -9
  6. package/dist/types/app/layouts/market/add-module-text.d.ts +2 -0
  7. package/dist/types/app/layouts/market/use-add-module.d.ts +4 -3
  8. package/dist/types/app/layouts/market/use-module-versions.d.ts +2 -0
  9. package/dist/types/app/layouts/rightbar/MarketView.d.ts +3 -1
  10. package/dist/types/app/layouts/rightbar/__tests__/market-rows-plugins.test.d.ts +1 -0
  11. package/dist/types/app/layouts/rightbar/market-mock.d.ts +9 -0
  12. package/dist/types/app/layouts/rightbar/market-plugin-rows.d.ts +8 -0
  13. package/dist/types/app/layouts/rightbar/market-rows.d.ts +14 -3
  14. package/dist/types/app/layouts/rightbar/registry.d.ts +0 -1
  15. package/dist/types/app/units/__tests__/manifests.test.d.ts +1 -0
  16. package/dist/types/app/units/fixture-unit-mock.d.ts +3 -3
  17. package/dist/types/app/units/host.d.ts +2 -2
  18. package/dist/types/app/units/manifests.d.ts +5 -0
  19. package/dist/types/app/units/registry.d.ts +10 -2
  20. package/dist/types/modules/panels/__tests__/market-env.test.d.ts +1 -0
  21. package/dist/types/modules/panels/__tests__/market-plugin.test.d.ts +1 -0
  22. package/dist/types/modules/panels/__tests__/market-route.test.d.ts +1 -0
  23. package/dist/types/modules/panels/__tests__/plugin-actions.test.d.ts +1 -0
  24. package/dist/types/modules/panels/__tests__/plugin-ledger.test.d.ts +1 -0
  25. package/dist/types/modules/panels/__tests__/plugin-section.test.d.ts +1 -0
  26. package/dist/types/modules/panels/__tests__/scope.test.d.ts +1 -0
  27. package/dist/types/modules/panels/__tests__/service-release.test.d.ts +1 -0
  28. package/dist/types/modules/panels/index.d.ts +2 -2
  29. package/dist/types/modules/panels/model/config.d.ts +27 -9
  30. package/dist/types/modules/panels/model/market-dispatch.d.ts +9 -0
  31. package/dist/types/modules/panels/model/market-env.d.ts +20 -0
  32. package/dist/types/modules/panels/model/market-route.d.ts +26 -0
  33. package/dist/types/modules/panels/model/market-types.d.ts +71 -0
  34. package/dist/types/modules/panels/model/market.d.ts +7 -66
  35. package/dist/types/modules/panels/model/plugin-actions.d.ts +50 -0
  36. package/dist/types/modules/panels/model/plugin-ledger.d.ts +22 -0
  37. package/dist/types/modules/panels/model/plugin-name.d.ts +8 -0
  38. package/dist/types/modules/panels/model/plugin-name.test.d.ts +1 -0
  39. package/dist/types/modules/panels/model/plugin-section.d.ts +13 -0
  40. package/dist/types/modules/panels/model/service.d.ts +8 -6
  41. package/dist/types/shared/api/bundle-install.d.ts +8 -1
  42. package/dist/types/shared/api/host/__tests__/registry-declaration.test.d.ts +1 -0
  43. package/dist/types/shared/api/host/__tests__/unit-roster-facts.test.d.ts +1 -0
  44. package/dist/types/shared/api/host/bundle-routes.d.ts +1 -0
  45. package/dist/types/shared/api/host/installed-module.d.ts +7 -0
  46. package/dist/types/shared/api/host/mount-units.d.ts +5 -3
  47. package/dist/types/shared/api/host/package-content.d.ts +6 -0
  48. package/dist/types/shared/api/host/package-content.test.d.ts +1 -0
  49. package/dist/types/shared/api/host/package-kind.d.ts +21 -0
  50. package/dist/types/shared/api/host/package-kind.test.d.ts +1 -0
  51. package/dist/types/shared/api/host/package-route.d.ts +23 -0
  52. package/dist/types/shared/api/host/package-route.test.d.ts +1 -0
  53. package/dist/types/shared/api/host/registry-declaration.d.ts +23 -0
  54. package/dist/types/shared/api/host/unit-registry.d.ts +33 -4
  55. package/dist/types/shared/api/host/unit-roster-facts.d.ts +5 -0
  56. package/dist/types/shared/api/installed-plugins.d.ts +12 -0
  57. package/dist/types/shared/api/unit-manifests.d.ts +5 -0
  58. package/dist/types/shared/api/unit-manifests.test.d.ts +1 -0
  59. package/dist/types/shared/hooks/bundle-queries.d.ts +2 -0
  60. package/dist/types/shared/hooks/use-facts.d.ts +1 -0
  61. package/package.json +2 -2
  62. package/skills/workbench-module-diy/BUILD.md +5 -5
  63. package/skills/workbench-module-diy/DESIGN.md +7 -1
  64. package/skills/workbench-module-diy/REVIEW.md +3 -2
  65. package/skills/workbench-module-diy/TOOLING.md +2 -2
  66. package/skills/workbench-module-diy/TOOLS.md +9 -2
@@ -419,6 +419,121 @@ 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 = ["name", "writes"];
440
+ var HOST_KEYS = ["inject", "optional"];
441
+ function text(value) {
442
+ return typeof value === "string" ? value.trim() : "";
443
+ }
444
+ function isRecord(value) {
445
+ return typeof value === "object" && value !== null && !Array.isArray(value);
446
+ }
447
+ function outsideKeys(value, allowed) {
448
+ return Object.keys(value).filter((key) => !allowed.includes(key));
449
+ }
450
+ function listOf(keys) {
451
+ return keys.map((key) => JSON.stringify(key)).join(" / ");
452
+ }
453
+ function stringArrayProblems(at, value) {
454
+ if (!Array.isArray(value)) return [`${at} 要是一串字符串,现在是 ${JSON.stringify(value)}`];
455
+ return value.flatMap((item, index) => text(item) === "" ? [`${at}[${index}] 不是非空字符串:${JSON.stringify(item)}`] : []);
456
+ }
457
+ function declarationProblemsOf(subject, raw) {
458
+ if (!isRecord(raw)) return [`${subject}: dsh.workbench 不是一个对象 —— ${JSON.stringify(raw)}`];
459
+ const version = raw.apiVersion;
460
+ if (version === void 0) return [`${subject}: dsh.workbench 没有 apiVersion —— 协议从 v1 起,写 \`"apiVersion": 1\`。缺它 = 外壳不认这个模块(ADR-0038 §2.3)`];
461
+ if (!Number.isInteger(version) || !SUPPORTED_API_VERSIONS.includes(version)) return [`${subject}: dsh.workbench.apiVersion 是 ${JSON.stringify(version)},这里只认 ${SUPPORTED_API_VERSIONS.join(" / ")}(要整数)`];
462
+ const problems = [];
463
+ const unknown = outsideKeys(raw, DECLARATION_KEYS);
464
+ if (unknown.length > 0) problems.push(`${subject}: dsh.workbench 里有协议 v1 不认识的键 ${listOf(unknown)} —— 只有 ${listOf(DECLARATION_KEYS)}。(拼错的键会被外壳**静默忽略**:那句声明等于没写)`);
465
+ for (const key of DECLARATION_REQUIRED_KEYS) if (raw[key] === void 0) problems.push(`${subject}: dsh.workbench 缺 ${key}` + (key === "card" ? "(这个模块没有卡片就写 `\"card\": null`)" : ""));
466
+ const card = raw.card;
467
+ if (card !== void 0 && card !== null) {
468
+ if (!isRecord(card)) problems.push(`${subject}: dsh.workbench.card 要么是对象、要么是 null,现在是 ${JSON.stringify(card)}`);
469
+ else {
470
+ const extra = outsideKeys(card, CARD_KEYS);
471
+ if (extra.length > 0) problems.push(`${subject}: dsh.workbench.card 里有不认的键 ${listOf(extra)} —— card 只有 ${listOf(CARD_KEYS)}(卡片默认显不显示由运行期的面板配置决定,清单里没有这一位)`);
472
+ if (text(card.titleKey) === "") problems.push(`${subject}: dsh.workbench.card.titleKey 要是非空字符串(面板那一格的文案键)`);
473
+ if (typeof card.order !== "number" || !Number.isFinite(card.order)) problems.push(`${subject}: dsh.workbench.card.order 要是数字,现在是 ${JSON.stringify(card.order)}`);
474
+ }
475
+ }
476
+ const tools = raw.tools;
477
+ if (tools !== void 0) {
478
+ if (!Array.isArray(tools)) problems.push(`${subject}: dsh.workbench.tools 要是一串声明,现在是 ${JSON.stringify(tools)}`);
479
+ else {
480
+ const seen = /* @__PURE__ */ new Set();
481
+ tools.forEach((tool, index) => {
482
+ const at = `dsh.workbench.tools[${index}]`;
483
+ if (!isRecord(tool)) {
484
+ problems.push(`${subject}: ${at} 要是对象,现在是 ${JSON.stringify(tool)}`);
485
+ return;
486
+ }
487
+ const extra = outsideKeys(tool, TOOL_KEYS);
488
+ if (extra.length > 0) problems.push(`${subject}: ${at} 里有不认的键 ${listOf(extra)} —— tools 只有 ${listOf(TOOL_KEYS)}`);
489
+ const name = text(tool.name);
490
+ if (name === "") problems.push(`${subject}: ${at}.name 要是非空字符串`);
491
+ else if (seen.has(name)) problems.push(`${subject}: ${at}.name「${name}」与前面那条撞了 —— 工具名会进 dsh 的工具表,不许重`);
492
+ else seen.add(name);
493
+ if (typeof tool.writes !== "boolean") problems.push(`${subject}: ${at}.writes 要是 true / false(它是"这会写"的唯一声明,缺了审批无从认领)`);
494
+ });
495
+ }
496
+ }
497
+ const host = raw.host;
498
+ if (host !== void 0) {
499
+ if (!isRecord(host)) problems.push(`${subject}: dsh.workbench.host 要是对象,现在是 ${JSON.stringify(host)}`);
500
+ else {
501
+ const extra = outsideKeys(host, HOST_KEYS);
502
+ if (extra.length > 0) problems.push(`${subject}: dsh.workbench.host 里有不认的键 ${listOf(extra)} —— host 只有 ${listOf(HOST_KEYS)}`);
503
+ if (host.inject === void 0) problems.push(`${subject}: dsh.workbench.host.inject 没写 —— 它声明这个模块要外壳的哪几个服务,外壳靠它判"能不能挂"。一个都不要也要写 \`"inject": []\``);
504
+ else problems.push(...stringArrayProblems(`${subject}: dsh.workbench.host.inject`, host.inject));
505
+ if (host.optional !== void 0) problems.push(...stringArrayProblems(`${subject}: dsh.workbench.host.optional`, host.optional));
506
+ }
507
+ }
508
+ if (raw.env !== void 0 && !isRecord(raw.env)) problems.push(`${subject}: dsh.workbench.env 要是对象(command / plugins / libs)—— 语义由门禁的 env 判据查,这里只查形状`);
509
+ return problems;
510
+ }
511
+ function readDeclaration(subject, raw) {
512
+ const problems = declarationProblemsOf(subject, raw);
513
+ if (problems.length > 0) return { problems };
514
+ const declaration = raw;
515
+ const card = declaration.card;
516
+ return {
517
+ declaration,
518
+ problems: [],
519
+ manifest: {
520
+ id: text(declaration.unit),
521
+ card: card === null ? null : {
522
+ titleKey: text(card.titleKey),
523
+ order: card.order
524
+ },
525
+ tools: declaration.tools.map((tool) => ({
526
+ name: text(tool.name),
527
+ writes: tool.writes
528
+ })),
529
+ host: {
530
+ inject: [...declaration.host.inject ?? []],
531
+ ...declaration.host.optional === void 0 ? {} : { optional: [...declaration.host.optional] }
532
+ },
533
+ ...declaration.env === void 0 ? {} : { env: declaration.env }
534
+ }
535
+ };
536
+ }
422
537
  //#endregion
423
538
  //#region ../shared/dist/config/constants.js
424
539
  var PLUGIN_ID = "@dsh-workbench/core";
@@ -606,7 +721,7 @@ function ApprovalDetail(props) {
606
721
  if (owner?.approval !== void 0) {
607
722
  const Render = owner.approval.render;
608
723
  return h(CardBoundary, {
609
- unit: owner.manifest.id,
724
+ unit: owner.unit,
610
725
  resetKey: props.callId ?? "",
611
726
  t
612
727
  }, h(Render, {
@@ -1111,6 +1226,7 @@ var en = {
1111
1226
  "panels.market.sectionPending": "Not installed",
1112
1227
  "panels.market.sectionShared": "Shared dependencies",
1113
1228
  "panels.market.sharedUsedBy": "Used by {modules}",
1229
+ "panels.market.sharedFoot": "These cannot be uninstalled here: they come with modules, and would be reinstalled anyway. To stop using one, turn off the modules that use it.",
1114
1230
  "panels.market.listSeparator": ", ",
1115
1231
  "panels.market.sharedMissing": "not installed",
1116
1232
  "panels.market.sharedOutdated": "update available",
@@ -1138,8 +1254,18 @@ var en = {
1138
1254
  "panels.market.installedSection": "Installed",
1139
1255
  "panels.market.sectionLocal": "Linked locally",
1140
1256
  "panels.market.sectionUnlisted": "Not from the market",
1257
+ "panels.market.sectionPlugins": "Installed opencli plugins",
1141
1258
  "panels.market.moduleOutdated": "update available",
1259
+ "panels.market.moduleRefused": "This module was built for an older version of the workbench, so it does not work here. Update it, or uninstall.",
1260
+ "panels.market.moduleRefusedDetail": "Why it cannot be used: {reason}",
1142
1261
  "panels.market.moduleRestartHint": "installed but not active — restart dsh once to bring it up",
1262
+ "panels.market.pluginLocalNoUpdate": "Installed from a local folder, so there is nowhere to check for a newer version — uninstall only",
1263
+ "panels.market.pluginKeptInUse": "Files kept for now: {names} still uses it.",
1264
+ "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.",
1265
+ "panels.market.pluginKeptHeld": "Files kept: this one is yours, and a module letting go will not remove it.",
1266
+ "panels.market.pluginKeptUnnamed": "Files kept for now: this one cannot be identified as a plugin, so it is unclear who needs it.",
1267
+ "panels.market.pluginAlsoKnownAs": "Its package is {pkg} (updates and uninstall use that)",
1268
+ "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
1269
  "panels.market.restartNeeded": "The new version is installed — restart once to pick it up.",
1144
1270
  "panels.market.foot": "After installing, enable it in the panel page of that project.",
1145
1271
  "panels.market.recheck": "Check for updates",
@@ -1187,7 +1313,11 @@ var en = {
1187
1313
  "panels.market.add.problemInstalled": "This module is already installed",
1188
1314
  "panels.market.add.problemNotFound": "No such module found",
1189
1315
  "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}",
1316
+ "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.",
1317
+ "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.",
1318
+ "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.",
1319
+ "panels.market.add.failureNotModuleOrPlugin": "This package is neither a workbench module nor an opencli plugin, so installing it would do nothing",
1320
+ "panels.market.add.installedPlugin": "Installed: the opencli plugin {name}",
1191
1321
  "panels.market.add.problemNetwork": "Cannot reach the module source; check the network and retry",
1192
1322
  "panels.market.add.problemUnknown": "Cannot fetch module information: {reason}",
1193
1323
  "panels.market.add.failureNetwork": "The network connection failed",
@@ -1218,6 +1348,7 @@ var en = {
1218
1348
  "panels.market.add.registryPnpm": "pnpm's own registry",
1219
1349
  "panels.market.add.check": "Check",
1220
1350
  "panels.market.add.checking": "Checking…",
1351
+ "panels.market.add.classifying": "Working out what it is…",
1221
1352
  "panels.market.add.change": "Edit",
1222
1353
  "panels.market.add.previewTitle": "About to install",
1223
1354
  "panels.market.add.fieldName": "Package",
@@ -1426,6 +1557,7 @@ var zh = {
1426
1557
  "panels.market.sectionPending": "未安装",
1427
1558
  "panels.market.sectionShared": "共用依赖",
1428
1559
  "panels.market.sharedUsedBy": "供 {modules} 使用",
1560
+ "panels.market.sharedFoot": "这一段不给卸载:它们跟着模块来,卸掉也会被装回来。要停用一份,就去关掉正在用它的那些模块。",
1429
1561
  "panels.market.listSeparator": "、",
1430
1562
  "panels.market.sharedMissing": "未装",
1431
1563
  "panels.market.sharedOutdated": "有新版",
@@ -1453,8 +1585,18 @@ var zh = {
1453
1585
  "panels.market.installedSection": "已安装",
1454
1586
  "panels.market.sectionLocal": "本地装的",
1455
1587
  "panels.market.sectionUnlisted": "不是从市场装的",
1588
+ "panels.market.sectionPlugins": "已安装 opencli 插件",
1456
1589
  "panels.market.moduleOutdated": "有新版",
1590
+ "panels.market.moduleRefused": "这个模块是给旧版本的工作台写的,现在用不了。更新它,或者卸载。",
1591
+ "panels.market.moduleRefusedDetail": "装不上的原因:{reason}",
1457
1592
  "panels.market.moduleRestartHint": "装好了但还没生效 —— 重启一次 dsh 就会出来",
1593
+ "panels.market.pluginLocalNoUpdate": "本地装的,没地方查新版,所以只给卸载",
1594
+ "panels.market.pluginKeptInUse": "文件先留着:{names} 还要用它。",
1595
+ "panels.market.pluginKeptUnsure": "文件先留着:这台机器上还有模块看不出用了什么,说不准谁在用它。",
1596
+ "panels.market.pluginKeptHeld": "文件留着:你自己装的那一份,模块放手不动它。",
1597
+ "panels.market.pluginKeptUnnamed": "文件先留着:看不出这是哪个插件,说不准谁在用它。",
1598
+ "panels.market.pluginAlsoKnownAs": "包名是 {pkg}(更新与卸载按它来)",
1599
+ "panels.market.pluginBroken": "这份装坏了:看不出它是个插件。可以卸载,或者去上面按包名再装一次。",
1458
1600
  "panels.market.restartNeeded": "新版装好了,重启一次才生效。",
1459
1601
  "panels.market.foot": "装好后到那个项目的面板页勾选它,模块才会出现。",
1460
1602
  "panels.market.recheck": "重新检查更新",
@@ -1502,7 +1644,11 @@ var zh = {
1502
1644
  "panels.market.add.problemInstalled": "该模块已安装",
1503
1645
  "panels.market.add.problemNotFound": "未找到相关模块",
1504
1646
  "panels.market.add.problemNotPackage": "该路径不存在或不是有效的模块包",
1505
- "panels.market.add.problemNotBundle": "这个包没有声明组合包,无法作为模块安装:{reason}",
1647
+ "panels.market.add.problemNotBundle": "这个包装不进这里:{reason}。要是它其实是个 opencli 插件,请改用包名(例如 opencli-plugin-cmdb)或者本地目录 —— 从 git 地址和 tarball 判断不出它是什么。",
1648
+ "panels.market.add.problemNotWorkbenchModule": "这个包跟现在的工作台对不上,装上也用不了。换一个版本,或者找这个模块的作者。",
1649
+ "panels.market.add.problemNotModuleOrPlugin": "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上。换一个版本,或者找这个包的作者。",
1650
+ "panels.market.add.failureNotModuleOrPlugin": "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上",
1651
+ "panels.market.add.installedPlugin": "装上了:opencli 插件 {name}",
1506
1652
  "panels.market.add.problemNetwork": "无法连接模块源,请检查网络后重试",
1507
1653
  "panels.market.add.problemUnknown": "无法获取模块信息:{reason}",
1508
1654
  "panels.market.add.failureNetwork": "网络连接失败",
@@ -1533,6 +1679,7 @@ var zh = {
1533
1679
  "panels.market.add.registryPnpm": "pnpm 自己配的源",
1534
1680
  "panels.market.add.check": "预检",
1535
1681
  "panels.market.add.checking": "正在预检…",
1682
+ "panels.market.add.classifying": "判定中…",
1536
1683
  "panels.market.add.change": "改一下",
1537
1684
  "panels.market.add.previewTitle": "将要安装",
1538
1685
  "panels.market.add.fieldName": "包名",
@@ -1809,6 +1956,8 @@ var dist_exports = /* @__PURE__ */ __exportAll({
1809
1956
  CenteredState: () => CenteredState,
1810
1957
  ChoiceGroup: () => ChoiceGroup,
1811
1958
  Component: () => Component,
1959
+ DECLARATION_KEYS: () => DECLARATION_KEYS,
1960
+ DECLARATION_REQUIRED_KEYS: () => DECLARATION_REQUIRED_KEYS,
1812
1961
  DEFAULT_WORKSPACE_TITLE: () => DEFAULT_WORKSPACE_TITLE,
1813
1962
  EVENTS_PATH: () => EVENTS_PATH,
1814
1963
  EmptyState: () => EmptyState,
@@ -1847,6 +1996,7 @@ var dist_exports = /* @__PURE__ */ __exportAll({
1847
1996
  STORAGE_KEY_PROJECT: () => STORAGE_KEY_PROJECT,
1848
1997
  STORAGE_KEY_RIGHTBAR: () => STORAGE_KEY_RIGHTBAR,
1849
1998
  STORAGE_KEY_RIGHTBAR_COLUMN: () => STORAGE_KEY_RIGHTBAR_COLUMN,
1999
+ SUPPORTED_API_VERSIONS: () => SUPPORTED_API_VERSIONS,
1850
2000
  SaveBar: () => SaveBar,
1851
2001
  SettingsSection: () => SettingsSection,
1852
2002
  SkeletonRows: () => SkeletonRows,
@@ -1857,6 +2007,7 @@ var dist_exports = /* @__PURE__ */ __exportAll({
1857
2007
  VERSION_CHECK_TIMEOUT_MS: () => VERSION_CHECK_TIMEOUT_MS,
1858
2008
  VERSION_CHECK_TTL_MS: () => VERSION_CHECK_TTL_MS,
1859
2009
  WARM_TIMEOUT_MS: () => WARM_TIMEOUT_MS,
2010
+ WORKBENCH_API_VERSION: () => 1,
1860
2011
  apiGet: () => apiGet,
1861
2012
  apiPost: () => apiPost,
1862
2013
  blockerActionLabel: () => blockerActionLabel,
@@ -1866,6 +2017,7 @@ var dist_exports = /* @__PURE__ */ __exportAll({
1866
2017
  buildMarker: () => buildMarker,
1867
2018
  createFactChannel: () => createFactChannel,
1868
2019
  cx: () => cx,
2020
+ declarationProblemsOf: () => declarationProblemsOf,
1869
2021
  decodeRef: () => decodeRef,
1870
2022
  dictionaries: () => dictionaries,
1871
2023
  en: () => en,
@@ -1889,6 +2041,7 @@ var dist_exports = /* @__PURE__ */ __exportAll({
1889
2041
  parseMarkers: () => parseMarkers,
1890
2042
  probeKeyOf: () => probeKeyOf,
1891
2043
  quoteShellArg: () => quoteShellArg,
2044
+ readDeclaration: () => readDeclaration,
1892
2045
  readFlag: () => readFlag,
1893
2046
  readText: () => readText,
1894
2047
  relativeTime: () => relativeTime,
@@ -2001,6 +2154,7 @@ function createAdaptiveTransport() {
2001
2154
  //#endregion
2002
2155
  //#region src/shared/hooks/use-facts.ts
2003
2156
  var use_facts_exports = /* @__PURE__ */ __exportAll({
2157
+ subscribeFactRevision: () => subscribeFactRevision,
2004
2158
  useFactInvalidate: () => useFactInvalidate,
2005
2159
  useFactRevision: () => useFactRevision
2006
2160
  });
@@ -2008,6 +2162,9 @@ var channel = createFactChannel({ transport: createAdaptiveTransport() });
2008
2162
  function useFactRevision(fact) {
2009
2163
  return useSyncExternalStore(useCallback((listener) => channel.subscribe(fact, listener), [fact]), useCallback(() => channel.revisionOf(fact), [fact]));
2010
2164
  }
2165
+ function subscribeFactRevision(fact, listener) {
2166
+ return channel.subscribe(fact, listener);
2167
+ }
2011
2168
  function useFactInvalidate() {
2012
2169
  return useCallback((fact) => channel.refresh(fact), []);
2013
2170
  }
@@ -2532,8 +2689,7 @@ function buildMarketMocks(roster) {
2532
2689
  ...mine,
2533
2690
  card: {
2534
2691
  titleKey: "一个名字特别特别长的模块用来验证省略号会不会把按钮挤下去",
2535
- order: 10,
2536
- enabledByDefault: true
2692
+ order: 10
2537
2693
  }
2538
2694
  }],
2539
2695
  capabilities: probe$1(id, "ok"),
@@ -2637,6 +2793,24 @@ function buildMarketMocks(roster) {
2637
2793
  installedActions: ["update-bundle", "remove-bundle"]
2638
2794
  }
2639
2795
  },
2796
+ {
2797
+ id: "installed-client-refused",
2798
+ label: "本机装了,但浏览器这半没挂上(拒因由客户端报告)",
2799
+ manifests: [found],
2800
+ capabilities: {},
2801
+ installed: [{
2802
+ unit: id,
2803
+ pkg: "@dsh-workbench/module-one",
2804
+ installed: "0.1.2",
2805
+ clientRefused: [`宿主没有担保 unit「${id}」的清单 —— 这个包要么没装进这份 profile,要么它的 dsh.workbench 没通过校验。卡片先不挂:清单只有一处真相(ADR-0038 §2.2)`]
2806
+ }],
2807
+ expect: {
2808
+ rows: 0,
2809
+ installed: 1,
2810
+ installedActions: ["remove-bundle"],
2811
+ installedNote: true
2812
+ }
2813
+ },
2640
2814
  {
2641
2815
  id: "installed-confirm",
2642
2816
  label: "卸载本机模块(Modal 确认,标题带模块名)",
@@ -2685,6 +2859,95 @@ function buildMarketMocks(roster) {
2685
2859
  actions: ["uninstall"]
2686
2860
  }
2687
2861
  },
2862
+ {
2863
+ id: "plugin-outdated",
2864
+ label: "用户装的 opencli 插件:有新版(给更新 / 卸载)",
2865
+ manifests: [],
2866
+ capabilities: {},
2867
+ plugins: [{
2868
+ pkg: "opencli-plugin-cmdb",
2869
+ name: "cmdb",
2870
+ installed: "1.0.24",
2871
+ latest: "1.0.25"
2872
+ }],
2873
+ expect: {
2874
+ rows: 0,
2875
+ plugins: 1,
2876
+ pluginActions: ["update-plugin", "uninstall-plugin"]
2877
+ }
2878
+ },
2879
+ {
2880
+ id: "plugin-local",
2881
+ label: "本地目录装的 opencli 插件:没有版本源(只给卸载,行上说清为什么)",
2882
+ manifests: [],
2883
+ capabilities: {},
2884
+ plugins: [{
2885
+ pkg: "/Users/me/my-local-plugin",
2886
+ name: "my-local-plugin",
2887
+ local: true
2888
+ }],
2889
+ expect: {
2890
+ rows: 0,
2891
+ plugins: 1,
2892
+ pluginActions: ["uninstall-plugin"],
2893
+ pluginNote: true,
2894
+ pluginNoteKey: "panels.market.pluginLocalNoUpdate"
2895
+ }
2896
+ },
2897
+ {
2898
+ id: "plugin-alias",
2899
+ label: "插件名 ≠ 包名:行上两个都写出来(更新 / 卸载按包名)",
2900
+ manifests: [],
2901
+ capabilities: {},
2902
+ plugins: [{
2903
+ pkg: "opencli-plugin-cmdb",
2904
+ name: "cmdb",
2905
+ installed: "1.0.24",
2906
+ latest: "1.0.25"
2907
+ }],
2908
+ expect: {
2909
+ rows: 0,
2910
+ plugins: 1,
2911
+ pluginActions: ["update-plugin", "uninstall-plugin"],
2912
+ pluginNoteKey: "panels.market.pluginAlsoKnownAs"
2913
+ }
2914
+ },
2915
+ {
2916
+ id: "plugin-broken",
2917
+ label: "装坏了的那一份(目录在但看不出是个插件):行上直说,给卸载与指路",
2918
+ manifests: [],
2919
+ capabilities: {},
2920
+ plugins: [{
2921
+ pkg: "opencli-plugin-cmdb",
2922
+ name: "cmdb",
2923
+ installed: "1.0.24",
2924
+ broken: true
2925
+ }],
2926
+ expect: {
2927
+ rows: 0,
2928
+ plugins: 1,
2929
+ pluginActions: ["uninstall-plugin"],
2930
+ pluginNoteKey: "panels.market.pluginBroken"
2931
+ }
2932
+ },
2933
+ {
2934
+ id: "plugin-kept",
2935
+ label: "卸掉用户装的插件,但还有模块在用:文件先留着(行上给一句为什么)",
2936
+ manifests: [],
2937
+ capabilities: {},
2938
+ plugins: [{
2939
+ pkg: "opencli-plugin-cmdb",
2940
+ name: "cmdb",
2941
+ installed: "1.0.24"
2942
+ }],
2943
+ notice: "文件先留着:cmdb、argocd 还要用它。",
2944
+ expect: {
2945
+ rows: 0,
2946
+ plugins: 1,
2947
+ pluginActions: ["uninstall-plugin"],
2948
+ notice: true
2949
+ }
2950
+ },
2688
2951
  {
2689
2952
  id: "empty",
2690
2953
  label: "市场空态",
@@ -2742,7 +3005,6 @@ function createBoardRegistry() {
2742
3005
  };
2743
3006
  },
2744
3007
  all: () => sorted,
2745
- visible: () => sorted.filter((module) => module.enabledByDefault),
2746
3008
  byId: (id) => index.get(id),
2747
3009
  stepOwner: (kind) => steps.get(kind),
2748
3010
  subscribe(listener) {
@@ -2754,20 +3016,21 @@ function createBoardRegistry() {
2754
3016
  }
2755
3017
  //#endregion
2756
3018
  //#region src/app/units/registry.ts
2757
- function toBoardModule(face) {
3019
+ function toBoardModule(face, manifest) {
2758
3020
  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,
3021
+ id: manifest.id,
3022
+ titleKey: manifest.card?.titleKey ?? manifest.id,
3023
+ order: manifest.card?.order ?? 100,
2763
3024
  View: face.card,
2764
3025
  ...face.steps === void 0 ? {} : { steps: face.steps }
2765
3026
  };
2766
3027
  }
2767
- function createClientUnitRegistry() {
2768
- const faces = [];
3028
+ function createClientUnitRegistry(options = {}) {
3029
+ const registrations = [];
3030
+ const refusals = [];
2769
3031
  const listeners = /* @__PURE__ */ new Set();
2770
3032
  const board = createBoardRegistry();
3033
+ let known = /* @__PURE__ */ new Map();
2771
3034
  let revision = 0;
2772
3035
  let manifestsCache;
2773
3036
  function notify() {
@@ -2778,28 +3041,86 @@ function createClientUnitRegistry() {
2778
3041
  console.warn(`[dsh-workbench] 套件注册的订阅者报错(不影响其它订阅者与这次注册):${String(error)}`);
2779
3042
  }
2780
3043
  }
3044
+ function warnRefusal(unit, reasons) {
3045
+ refusals.push({
3046
+ unit,
3047
+ reasons
3048
+ });
3049
+ console.warn(`[dsh-workbench] ${unit === "" ? "这个模块" : unit} 的卡片没挂上:${reasons.join(";")}`);
3050
+ }
3051
+ function takeBackRefusal(unit) {
3052
+ for (let at = refusals.length - 1; at >= 0; at -= 1) if (refusals[at]?.unit === unit) refusals.splice(at, 1);
3053
+ }
3054
+ function attach(entry) {
3055
+ const manifest = known.get(entry.unit);
3056
+ if (manifest === void 0) return false;
3057
+ entry.mounted = {
3058
+ manifest,
3059
+ unmount: board.register(toBoardModule(entry.face, manifest))
3060
+ };
3061
+ takeBackRefusal(entry.unit);
3062
+ return true;
3063
+ }
3064
+ function detach(entry) {
3065
+ entry.mounted?.unmount();
3066
+ entry.mounted = null;
3067
+ }
2781
3068
  return {
2782
3069
  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);
3070
+ const unit = typeof face?.unit === "string" ? face.unit.trim() : "";
3071
+ if (unit === "") {
3072
+ warnRefusal("", ["registerUnit({ unit, card }) 里的 unit 是空的 —— U1b 之前的旧形状是 registerUnit({ manifest, card });现在清单由外壳从**已装清单**读出来、按 unit 交给浏览器(ADR-0038 §2.2)"]);
3073
+ notify();
3074
+ return () => {};
3075
+ }
3076
+ if (registrations.some((entry) => entry.unit === unit)) throw new Error(`套件 id 重复:${unit}(id 会进持久化配置,不能撞)`);
3077
+ const entry = {
3078
+ face,
3079
+ unit,
3080
+ mounted: null
3081
+ };
3082
+ registrations.push(entry);
2787
3083
  manifestsCache = void 0;
3084
+ if (!attach(entry)) {
3085
+ warnRefusal(unit, [`宿主没有担保 unit「${unit}」的清单 —— 这个包要么没装进这份 profile,要么它的 dsh.workbench 没通过校验。卡片先不挂:清单只有一处真相(ADR-0038 §2.2)`]);
3086
+ options.onUnknownUnit?.(unit);
3087
+ }
2788
3088
  notify();
2789
3089
  return () => {
2790
- const at = faces.findIndex((item) => item.manifest.id === id);
3090
+ const at = registrations.indexOf(entry);
2791
3091
  if (at < 0) return;
2792
- faces.splice(at, 1);
3092
+ registrations.splice(at, 1);
3093
+ detach(entry);
3094
+ takeBackRefusal(unit);
2793
3095
  manifestsCache = void 0;
2794
- unregisterBoard();
2795
3096
  notify();
2796
3097
  };
2797
3098
  },
2798
- all: () => faces,
3099
+ all: () => registrations.map((entry) => entry.face),
2799
3100
  manifests: () => {
2800
- manifestsCache ??= faces.map((face) => face.manifest);
3101
+ manifestsCache ??= registrations.flatMap((entry) => entry.mounted === null ? [] : [entry.mounted.manifest]);
2801
3102
  return manifestsCache;
2802
3103
  },
3104
+ refusals: () => refusals,
3105
+ setManifests(manifests) {
3106
+ known = new Map(manifests.map((manifest) => [manifest.id, manifest]));
3107
+ let changed = false;
3108
+ for (const entry of registrations) {
3109
+ const next = known.get(entry.unit);
3110
+ if (entry.mounted === null) {
3111
+ if (next !== void 0 && attach(entry)) changed = true;
3112
+ continue;
3113
+ }
3114
+ if (next === void 0) {
3115
+ detach(entry);
3116
+ warnRefusal(entry.unit, [`宿主不再担保 unit「${entry.unit}」的清单(模块被卸了?)—— 这张卡片先摘下来`]);
3117
+ changed = true;
3118
+ }
3119
+ }
3120
+ if (!changed) return;
3121
+ manifestsCache = void 0;
3122
+ notify();
3123
+ },
2803
3124
  board,
2804
3125
  subscribe(listener) {
2805
3126
  listeners.add(listener);
@@ -2834,11 +3155,9 @@ var messages = {
2834
3155
  };
2835
3156
  var manifest = {
2836
3157
  id: FIXTURE_UNIT_ID,
2837
- kind: "board",
2838
3158
  card: {
2839
3159
  titleKey: KEY.cardTitle,
2840
- order: 900,
2841
- enabledByDefault: true
3160
+ order: 900
2842
3161
  },
2843
3162
  tools: [],
2844
3163
  host: { inject: [] }
@@ -2861,6 +3180,7 @@ function FixtureCard({ t, onRefresh }) {
2861
3180
  }))));
2862
3181
  }
2863
3182
  var FIXTURE_FACE = {
3183
+ unit: FIXTURE_UNIT_ID,
2864
3184
  manifest,
2865
3185
  messages,
2866
3186
  card: FixtureCard
@@ -2881,11 +3201,9 @@ var crashMessages = {
2881
3201
  };
2882
3202
  var crashManifest = {
2883
3203
  id: CRASH_UNIT_ID,
2884
- kind: "board",
2885
3204
  card: {
2886
3205
  titleKey: CRASH_KEY.cardTitle,
2887
- order: 899,
2888
- enabledByDefault: true
3206
+ order: 899
2889
3207
  },
2890
3208
  tools: [],
2891
3209
  host: { inject: [] }
@@ -2894,6 +3212,7 @@ function CrashingCard() {
2894
3212
  throw new Error("夹具:这张卡片按 wbCrash=1 故意渲染失败(守「一张卡片崩溃不带走面板」)");
2895
3213
  }
2896
3214
  var CRASH_FACE = {
3215
+ unit: CRASH_UNIT_ID,
2897
3216
  manifest: crashManifest,
2898
3217
  messages: crashMessages,
2899
3218
  card: CrashingCard
@@ -2906,8 +3225,8 @@ function fixtureRoster(roster) {
2906
3225
  return hasCard(roster) ? roster : SAMPLE_ROSTER;
2907
3226
  }
2908
3227
  function fixtureSeed(roster, all, crash = false) {
2909
- const base = hasCard(roster) ? all : [toBoardModule(FIXTURE_FACE)];
2910
- return crash ? [toBoardModule(CRASH_FACE), ...base] : base;
3228
+ const base = hasCard(roster) ? all : [toBoardModule(FIXTURE_FACE, FIXTURE_FACE.manifest)];
3229
+ return crash ? [toBoardModule(CRASH_FACE, CRASH_FACE.manifest), ...base] : base;
2911
3230
  }
2912
3231
  //#endregion
2913
3232
  //#region src/app/layouts/rightbar/market-rows.ts
@@ -2927,6 +3246,25 @@ var MARKET_DOT = {
2927
3246
  unchecked: "idle",
2928
3247
  unknown: "idle"
2929
3248
  };
3249
+ function marketRowTitle(row, t) {
3250
+ return row.label ?? t(row.titleKey);
3251
+ }
3252
+ function withClientRefusals(modules, refusals) {
3253
+ if (refusals.length === 0) return modules;
3254
+ const byUnit = new Map(refusals.map((one) => [one.unit, one.reasons]));
3255
+ return modules.map((module) => {
3256
+ const reasons = byUnit.get(module.unit);
3257
+ return reasons === void 0 ? module : {
3258
+ ...module,
3259
+ clientRefused: reasons
3260
+ };
3261
+ });
3262
+ }
3263
+ function refusalReasons(host, client) {
3264
+ const reported = host ?? [];
3265
+ if (reported.length > 0) return reported;
3266
+ return [...new Set(client ?? [])];
3267
+ }
2930
3268
  function mergeTitles(known, manifests) {
2931
3269
  let next = null;
2932
3270
  for (const manifest of manifests) {
@@ -2945,11 +3283,16 @@ function installedRows(modules, manifests, listed, known) {
2945
3283
  const latest = module.latest;
2946
3284
  const outdated = latest !== void 0 && latest !== module.installed;
2947
3285
  const loaded = titles.has(module.unit);
3286
+ const refused = refusalReasons(module.refused, module.clientRefused);
2948
3287
  rows.push({
2949
3288
  id: module.unit,
2950
3289
  titleKey: titles.get(module.unit) ?? known?.get(module.unit) ?? module.pkg,
2951
3290
  state: outdated ? "outdated" : "installed",
2952
- ...loaded ? {} : { hint: "panels.market.moduleRestartHint" },
3291
+ ...loaded || refused.length > 0 ? {} : { hint: "panels.market.moduleRestartHint" },
3292
+ ...refused.length === 0 ? {} : { note: {
3293
+ key: "panels.market.moduleRefused",
3294
+ params: { reason: refused.join(";") }
3295
+ } },
2953
3296
  ...module.local === true ? { local: true } : {},
2954
3297
  pkg: module.pkg,
2955
3298
  canInstall: false,
@@ -2968,6 +3311,7 @@ function installedRows(modules, manifests, listed, known) {
2968
3311
  return rows;
2969
3312
  }
2970
3313
  function marketActions$1(row) {
3314
+ if (row.plugin === true) return [...row.canUpdate ? ["update-plugin"] : [], ...row.canUninstall ? ["uninstall-plugin"] : []];
2971
3315
  return [
2972
3316
  ...row.canInstall ? ["install"] : [],
2973
3317
  ...row.moduleUpdate === void 0 ? row.canUpdate ? ["update"] : [] : ["update-bundle"],
@@ -2991,7 +3335,7 @@ function envNameOf(source) {
2991
3335
  if (source.kind === "npm") return source.registryEnv;
2992
3336
  return "";
2993
3337
  }
2994
- function noteOf(state, install, manifest) {
3338
+ function noteOf$1(state, install, manifest) {
2995
3339
  if (state === "installed") return void 0;
2996
3340
  if (install === "env-missing") return {
2997
3341
  key: "panels.market.needEnv",
@@ -3050,7 +3394,7 @@ function marketRows(manifests, capabilities, updates = {}) {
3050
3394
  const capability = capabilities?.[manifest.id];
3051
3395
  const install = capability?.install;
3052
3396
  const state = capabilities === null ? "checking" : stateOf(capability);
3053
- const note = noteOf(state, install, manifest);
3397
+ const note = noteOf$1(state, install, manifest);
3054
3398
  const onMachine = isOnMachine(state);
3055
3399
  const reAdopt = capability?.released === true && install === void 0;
3056
3400
  const entry = updates[manifest.id];
@@ -3222,6 +3566,63 @@ function useHostWrite(options) {
3222
3566
  };
3223
3567
  }
3224
3568
  //#endregion
3569
+ //#region src/app/layouts/rightbar/market-plugin-rows.ts
3570
+ function installedPluginKept(value) {
3571
+ if (typeof value !== "object" || value === null) return void 0;
3572
+ const kept = value.kept;
3573
+ if (typeof kept !== "object" || kept === null) return void 0;
3574
+ const kind = kept.kind;
3575
+ if (kind === "held") return { key: "panels.market.pluginKeptHeld" };
3576
+ if (kind === "unnamed") return { key: "panels.market.pluginKeptUnnamed" };
3577
+ if (kind === "unsure") return { key: "panels.market.pluginKeptUnsure" };
3578
+ if (kind !== "claimed") return void 0;
3579
+ const raw = kept.by;
3580
+ const names = Array.isArray(raw) ? raw.filter((item) => typeof item === "string") : [];
3581
+ return names.length === 0 ? { key: "panels.market.pluginKeptUnsure" } : {
3582
+ key: "panels.market.pluginKeptInUse",
3583
+ names
3584
+ };
3585
+ }
3586
+ function noteOf(plugin) {
3587
+ if (plugin.broken === true) return {
3588
+ key: "panels.market.pluginBroken",
3589
+ params: {}
3590
+ };
3591
+ if (plugin.local === true) return {
3592
+ key: "panels.market.pluginLocalNoUpdate",
3593
+ params: {}
3594
+ };
3595
+ if (plugin.name !== void 0 && plugin.name !== plugin.pkg) return {
3596
+ key: "panels.market.pluginAlsoKnownAs",
3597
+ params: { pkg: plugin.pkg }
3598
+ };
3599
+ }
3600
+ function installedPluginRows(plugins) {
3601
+ return plugins.map((plugin) => {
3602
+ const name = plugin.name ?? plugin.pkg;
3603
+ const outdated = plugin.installed !== void 0 && plugin.latest !== void 0 && plugin.latest !== plugin.installed;
3604
+ const note = noteOf(plugin);
3605
+ return {
3606
+ id: `plugin:${plugin.pkg}`,
3607
+ titleKey: plugin.pkg,
3608
+ label: name,
3609
+ plugin: true,
3610
+ state: outdated ? "outdated" : "installed",
3611
+ canInstall: false,
3612
+ canUpdate: outdated,
3613
+ canUninstall: true,
3614
+ ...plugin.local === true ? { local: true } : {},
3615
+ ...plugin.broken === true ? { broken: true } : {},
3616
+ ...note === void 0 ? {} : { note },
3617
+ pkg: plugin.pkg,
3618
+ ...plugin.installed === void 0 ? {} : { version: {
3619
+ installed: plugin.installed,
3620
+ ...plugin.latest === void 0 ? {} : { latest: plugin.latest }
3621
+ } }
3622
+ };
3623
+ });
3624
+ }
3625
+ //#endregion
3225
3626
  //#region src/app/layouts/market/add-module-text.ts
3226
3627
  var GUIDE_ITEMS = [
3227
3628
  {
@@ -3340,17 +3741,23 @@ var PROBLEM_KEYS = {
3340
3741
  "not-found": "panels.market.add.problemNotFound",
3341
3742
  "not-a-package": "panels.market.add.problemNotPackage",
3342
3743
  "not-a-bundle": "panels.market.add.problemNotBundle",
3744
+ "not-a-workbench-module": "panels.market.add.problemNotWorkbenchModule",
3745
+ "not-a-module-or-plugin": "panels.market.add.problemNotModuleOrPlugin",
3343
3746
  network: "panels.market.add.problemNetwork",
3344
3747
  unknown: "panels.market.add.problemUnknown"
3345
3748
  };
3346
3749
  function problemKey(problem) {
3347
3750
  return PROBLEM_KEYS[problem] ?? PROBLEM_KEYS.unknown;
3348
3751
  }
3752
+ function heldWizardPhase(phase, classifying) {
3753
+ return classifying && (phase === "preview" || phase === "idle") ? "inspecting" : phase;
3754
+ }
3349
3755
  var FAILURE_KEYS = {
3350
3756
  network: "panels.market.add.failureNetwork",
3351
3757
  "spec-host": "panels.market.add.failureNetworkHost",
3352
3758
  "not-found": "panels.market.add.failureNotFound",
3353
3759
  "no-matching-version": "panels.market.add.failureNoMatchingVersion",
3760
+ "not-a-module-or-plugin": "panels.market.add.failureNotModuleOrPlugin",
3354
3761
  "disk-full": "panels.market.add.failureDiskFull",
3355
3762
  permission: "panels.market.add.failurePermission",
3356
3763
  "build-blocked": "panels.market.add.failureBuildBlocked",
@@ -3447,6 +3854,16 @@ function MarketChecking({ t }) {
3447
3854
  function MarketEmpty({ t }) {
3448
3855
  return /* @__PURE__ */ h("div", { className: MarketPage_module_default.marketEmpty }, /* @__PURE__ */ h("div", { className: MarketPage_module_default.marketEmptyTitle }, t("panels.market.empty")));
3449
3856
  }
3857
+ var PanelFoot_module_default = {
3858
+ foot: "_foot_1us83_1",
3859
+ footText: "_footText_1us83_12",
3860
+ footTextMd: "_footTextMd_1us83_17"
3861
+ };
3862
+ //#endregion
3863
+ //#region src/shared/components/PanelFoot.tsx
3864
+ function PanelFoot({ text, size = "sm", children }) {
3865
+ return /* @__PURE__ */ h("footer", { className: PanelFoot_module_default.foot }, /* @__PURE__ */ h("span", { className: size === "md" ? PanelFoot_module_default.footTextMd : PanelFoot_module_default.footText }, text), children ?? null);
3866
+ }
3450
3867
  //#endregion
3451
3868
  //#region src/app/layouts/rightbar/market-view-text.ts
3452
3869
  var PROGRESS_KEY = {
@@ -3455,7 +3872,9 @@ var PROGRESS_KEY = {
3455
3872
  "update-bundle": "panels.market.updating",
3456
3873
  "remove-bundle": "panels.market.uninstalling",
3457
3874
  uninstall: "panels.market.uninstalling",
3458
- "update-dependency": "panels.market.updating"
3875
+ "update-dependency": "panels.market.updating",
3876
+ "update-plugin": "panels.market.updating",
3877
+ "uninstall-plugin": "panels.market.uninstalling"
3459
3878
  };
3460
3879
  var ACTION_LABEL_KEY = {
3461
3880
  install: "panels.market.install",
@@ -3463,7 +3882,9 @@ var ACTION_LABEL_KEY = {
3463
3882
  "update-bundle": "panels.market.update",
3464
3883
  "remove-bundle": "panels.market.uninstall",
3465
3884
  uninstall: "panels.market.uninstall",
3466
- "update-dependency": "panels.market.update"
3885
+ "update-dependency": "panels.market.update",
3886
+ "update-plugin": "panels.market.update",
3887
+ "uninstall-plugin": "panels.market.uninstall"
3467
3888
  };
3468
3889
  //#endregion
3469
3890
  //#region src/app/layouts/rightbar/SharedSection.tsx
@@ -3511,21 +3932,18 @@ function SharedSection({ t, rows, pending, failureOf, act }) {
3511
3932
  pending,
3512
3933
  failure: failureOf(row.id),
3513
3934
  onAct: (action) => void act(row.id, action)
3514
- }))));
3515
- }
3516
- var PanelFoot_module_default = {
3517
- foot: "_foot_1us83_1",
3518
- footText: "_footText_1us83_12",
3519
- footTextMd: "_footTextMd_1us83_17"
3520
- };
3521
- //#endregion
3522
- //#region src/shared/components/PanelFoot.tsx
3523
- function PanelFoot({ text, size = "sm", children }) {
3524
- return /* @__PURE__ */ h("footer", { className: PanelFoot_module_default.foot }, /* @__PURE__ */ h("span", { className: size === "md" ? PanelFoot_module_default.footTextMd : PanelFoot_module_default.footText }, text), children ?? null);
3935
+ }))), /* @__PURE__ */ h(PanelFoot, {
3936
+ size: "md",
3937
+ text: t("panels.market.sharedFoot")
3938
+ }));
3525
3939
  }
3526
3940
  //#endregion
3527
3941
  //#region src/app/layouts/rightbar/MarketView.tsx
3528
- var CONFIRM_UNINSTALL = ["uninstall", "remove-bundle"];
3942
+ var CONFIRM_UNINSTALL = [
3943
+ "uninstall",
3944
+ "remove-bundle",
3945
+ "uninstall-plugin"
3946
+ ];
3529
3947
  var CONFIRM_REMOVE = ["remove-bundle"];
3530
3948
  function MarketLine({ t, row, pending, failure, onAct }) {
3531
3949
  const running = pending !== null && pending.id === row.id ? pending : null;
@@ -3535,7 +3953,7 @@ function MarketLine({ t, row, pending, failure, onAct }) {
3535
3953
  return /* @__PURE__ */ h("li", { className: MarketPage_module_default.marketRow }, /* @__PURE__ */ h("span", {
3536
3954
  className: MarketPage_module_default.marketIcon,
3537
3955
  "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", {
3956
+ }, /* @__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
3957
  from: row.version.installed,
3540
3958
  to: row.version.latest
3541
3959
  }) : row.version.installed ?? row.version.latest ?? "")), /* @__PURE__ */ h("span", {
@@ -3560,11 +3978,14 @@ function MarketLine({ t, row, pending, failure, onAct }) {
3560
3978
  variant: action === "install" ? "primary" : "outline",
3561
3979
  className: danger ? "wb-dangerAction" : void 0,
3562
3980
  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,
3981
+ "aria-label": danger ? t("panels.market.uninstallLabel", { name: marketRowTitle(row, t) }) : void 0,
3564
3982
  disabled: locked,
3565
3983
  onClick: () => onAct(action)
3566
3984
  }, 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));
3985
+ }))), row.note === void 0 ? null : /* @__PURE__ */ h("div", {
3986
+ className: MarketPage_module_default.marketNote,
3987
+ title: row.note.params.reason === void 0 ? void 0 : t("panels.market.moduleRefusedDetail", row.note.params)
3988
+ }, t(row.note.key, row.note.params)), failure === null ? null : /* @__PURE__ */ h("div", { className: MarketPage_module_default.marketFailure }, failure));
3568
3989
  }
3569
3990
  function ConfirmUninstall({ t, name, onConfirm, onCancel }) {
3570
3991
  const [acknowledged, setAcknowledged] = useState(false);
@@ -3593,7 +4014,48 @@ function MarketSection({ t, titleKey, rows, pending, failureOf, act, confirmActi
3593
4014
  onAct: (action) => confirmActions.includes(action) ? onConfirming(row.id) : void act(row.id, action)
3594
4015
  }))));
3595
4016
  }
3596
- function MarketView({ t, manifests, capabilities, freshness, onChanged, updates, installed, installedRead, settling, mock }) {
4017
+ function MarketBody({ t, shown, pluginList, installedList, shared, pending, failed, notice, act, onConfirming }) {
4018
+ const failureOf = (id) => failed?.id === id ? failed.message : null;
4019
+ return /* @__PURE__ */ h(Fragment, null, notice === null ? null : /* @__PURE__ */ h("div", { className: MarketPage_module_default.marketNote }, notice), marketSections(shown).map((section) => /* @__PURE__ */ h(MarketSection, {
4020
+ key: section.id,
4021
+ t,
4022
+ titleKey: section.titleKey,
4023
+ rows: section.rows,
4024
+ pending,
4025
+ failureOf,
4026
+ act,
4027
+ confirmActions: CONFIRM_UNINSTALL,
4028
+ onConfirming
4029
+ })), /* @__PURE__ */ h(MarketSection, {
4030
+ t,
4031
+ titleKey: "panels.market.sectionPlugins",
4032
+ rows: pluginList,
4033
+ pending,
4034
+ failureOf,
4035
+ act,
4036
+ confirmActions: CONFIRM_UNINSTALL,
4037
+ onConfirming
4038
+ }), /* @__PURE__ */ h(MarketSection, {
4039
+ t,
4040
+ titleKey: installedList.length > 0 && installedList.every((row) => row.local === true) ? "panels.market.sectionLocal" : "panels.market.sectionUnlisted",
4041
+ rows: installedList,
4042
+ pending,
4043
+ failureOf,
4044
+ act,
4045
+ confirmActions: CONFIRM_REMOVE,
4046
+ onConfirming
4047
+ }), /* @__PURE__ */ h(PanelFoot, {
4048
+ size: "md",
4049
+ text: t("panels.market.foot")
4050
+ }), /* @__PURE__ */ h(SharedSection, {
4051
+ t,
4052
+ rows: shared,
4053
+ pending,
4054
+ failureOf,
4055
+ act
4056
+ }));
4057
+ }
4058
+ function MarketView({ t, manifests, capabilities, freshness, onChanged, updates, installed, installedRead, plugins, settling, mock }) {
3597
4059
  const [titles, setTitles] = useState(/* @__PURE__ */ new Map());
3598
4060
  useEffect(() => {
3599
4061
  if (mock === void 0) setTitles((known) => mergeTitles(known, manifests));
@@ -3610,6 +4072,7 @@ function MarketView({ t, manifests, capabilities, freshness, onChanged, updates,
3610
4072
  });
3611
4073
  const [confirming, setConfirming] = useState(mock?.confirming ?? null);
3612
4074
  const [failed, setFailed] = useState(mock?.failure ?? null);
4075
+ const [notice, setNotice] = useState(mock?.notice ?? null);
3613
4076
  const removing = pending !== null && pending.action === "remove-bundle" && failed?.id !== pending.id ? pending.id : null;
3614
4077
  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
4078
  const pendingRow = removing === null || pending === null || rows.some((row) => row.id === removing) ? void 0 : {
@@ -3623,7 +4086,12 @@ function MarketView({ t, manifests, capabilities, freshness, onChanged, updates,
3623
4086
  ...pending.version === void 0 ? {} : { version: { installed: pending.version } }
3624
4087
  };
3625
4088
  const shown = pendingRow === void 0 ? rows : [...rows, pendingRow];
3626
- const rowById = new Map([...shown, ...installedList].map((row) => [row.id, row]));
4089
+ const pluginList = installedPluginRows(mock?.plugins ?? plugins ?? []);
4090
+ const rowById = new Map([
4091
+ ...shown,
4092
+ ...installedList,
4093
+ ...pluginList
4094
+ ].map((row) => [row.id, row]));
3627
4095
  const confirmingRow = confirming === null ? void 0 : rowById.get(confirming);
3628
4096
  const act = async (id, action) => {
3629
4097
  if (mock !== void 0) return;
@@ -3637,13 +4105,15 @@ function MarketView({ t, manifests, capabilities, freshness, onChanged, updates,
3637
4105
  });
3638
4106
  setConfirming(null);
3639
4107
  setFailed(null);
4108
+ if (action !== "uninstall-plugin") setNotice(null);
3640
4109
  const dependency = action === "update-dependency";
4110
+ const byName = action === "remove-bundle" || action === "update-plugin" || action === "uninstall-plugin";
3641
4111
  const outcome = await provision.send(dependency ? {
3642
4112
  action,
3643
4113
  id
3644
- } : action === "remove-bundle" ? {
4114
+ } : byName ? {
3645
4115
  action,
3646
- name: rowById.get(id)?.pkg ?? ""
4116
+ name: row?.pkg ?? ""
3647
4117
  } : {
3648
4118
  action,
3649
4119
  unit: id
@@ -3657,6 +4127,10 @@ function MarketView({ t, manifests, capabilities, freshness, onChanged, updates,
3657
4127
  });
3658
4128
  return;
3659
4129
  }
4130
+ if (action === "uninstall-plugin") {
4131
+ const kept = installedPluginKept(outcome.value);
4132
+ setNotice(kept === void 0 ? null : t(kept.key, kept.names === void 0 ? {} : { names: kept.names.join(t("panels.market.listSeparator")) }));
4133
+ }
3660
4134
  const reason = changeFailure(changeOf(outcome.value), t);
3661
4135
  onChanged();
3662
4136
  if (reason !== void 0) setFailed({
@@ -3664,46 +4138,32 @@ function MarketView({ t, manifests, capabilities, freshness, onChanged, updates,
3664
4138
  message: t("panels.market.failed", { reason })
3665
4139
  });
3666
4140
  };
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,
3671
- 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, {
4141
+ const settled = freshness.loaded && installedRead !== false;
4142
+ const nothing = shown.length === 0 && installedList.length === 0 && pluginList.length === 0;
4143
+ if (!settled || (settling?.size ?? 0) > 0 && shown.length === 0 && installedList.length === 0) return /* @__PURE__ */ h(MarketChecking, { t });
4144
+ if (nothing) return /* @__PURE__ */ h(MarketEmpty, { t });
4145
+ return /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h(MarketBody, {
3680
4146
  t,
3681
- titleKey: installedList.length > 0 && installedList.every((row) => row.local === true) ? "panels.market.sectionLocal" : "panels.market.sectionUnlisted",
3682
- rows: installedList,
4147
+ shown,
4148
+ pluginList,
4149
+ installedList,
4150
+ shared,
3683
4151
  pending,
3684
- failureOf: (id) => failed?.id === id ? failed.message : null,
4152
+ failed,
4153
+ notice,
3685
4154
  act,
3686
- confirmActions: CONFIRM_REMOVE,
3687
4155
  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
4156
  }), /* @__PURE__ */ h(ConfirmUninstall, {
3698
4157
  key: confirming ?? "",
3699
4158
  t,
3700
- name: confirmingRow === void 0 ? null : t(confirmingRow.titleKey),
4159
+ name: confirmingRow === void 0 ? null : marketRowTitle(confirmingRow, t),
3701
4160
  onCancel: () => setConfirming(null),
3702
4161
  onConfirm: () => {
3703
4162
  const id = confirming;
3704
4163
  setConfirming(null);
3705
4164
  if (id === null) return;
3706
- act(id, rowById.get(id)?.pkg === void 0 ? "uninstall" : "remove-bundle");
4165
+ const row = rowById.get(id);
4166
+ act(id, row?.plugin === true ? "uninstall-plugin" : row?.pkg === void 0 ? "uninstall" : "remove-bundle");
3707
4167
  }
3708
4168
  }));
3709
4169
  }
@@ -3713,6 +4173,9 @@ var BUNDLE_REGISTRIES_ROUTE = "bundle-registries";
3713
4173
  var BUNDLE_INSPECT_ROUTE = "bundle-inspect";
3714
4174
  var MODULE_VERSIONS_ROUTE = "module-versions";
3715
4175
  //#endregion
4176
+ //#region src/shared/api/installed-plugins.ts
4177
+ var INSTALLED_PLUGINS_ROUTE = "panels/plugins";
4178
+ //#endregion
3716
4179
  //#region src/shared/hooks/bundle-queries.ts
3717
4180
  function readBundleRegistries() {
3718
4181
  return apiGet(BUNDLE_REGISTRIES_ROUTE);
@@ -3726,10 +4189,14 @@ function readBundleInspection(spec, registry) {
3726
4189
  function readModuleVersions(force = false) {
3727
4190
  return apiGet(MODULE_VERSIONS_ROUTE, force ? { force: "1" } : void 0);
3728
4191
  }
4192
+ function readInstalledPlugins(force = false) {
4193
+ return apiGet(INSTALLED_PLUGINS_ROUTE, force ? { force: "1" } : void 0);
4194
+ }
3729
4195
  //#endregion
3730
4196
  //#region src/app/layouts/market/use-module-versions.ts
3731
4197
  var state = {
3732
4198
  modules: null,
4199
+ plugins: null,
3733
4200
  read: false,
3734
4201
  checkedAt: null
3735
4202
  };
@@ -3754,10 +4221,13 @@ function runModuleVersionsRead(force = false) {
3754
4221
  if (inFlight !== null && inFlight.force === force) return inFlight.promise;
3755
4222
  const seq = readSeq += 1;
3756
4223
  if (force) newestForceSeq = seq;
3757
- const promise = readModuleVersions(force).catch(() => null).then((response) => {
4224
+ const promise = Promise.all([readModuleVersions(force), readInstalledPlugins(force)]).catch(() => null).then((responses) => {
3758
4225
  if (!force && seq < newestForceSeq) return;
4226
+ const versions = responses?.[0];
4227
+ const plugins = responses?.[1];
3759
4228
  publish({
3760
- modules: response?.ok === true ? response.modules : state.modules,
4229
+ modules: versions?.ok === true ? versions.modules : state.modules,
4230
+ plugins: plugins?.ok === true ? plugins.plugins : state.plugins,
3761
4231
  read: true,
3762
4232
  checkedAt: Date.now()
3763
4233
  });
@@ -3789,6 +4259,7 @@ function useModuleVersions() {
3789
4259
  }, []);
3790
4260
  return {
3791
4261
  modules: value.modules,
4262
+ plugins: value.plugins,
3792
4263
  read: value.read,
3793
4264
  checkedAt: value.checkedAt,
3794
4265
  reload
@@ -4007,7 +4478,7 @@ function AddModulePending({ t, kind }) {
4007
4478
  })), /* @__PURE__ */ h("h2", {
4008
4479
  className: AddModule_module_default.pendingTitle,
4009
4480
  role: "status"
4010
- }, kind === "inspecting" ? t("panels.market.add.checking") : t("panels.market.add.installingTitle"))));
4481
+ }, kind === "inspecting" ? t("panels.market.add.classifying") : t("panels.market.add.installingTitle"))));
4011
4482
  }
4012
4483
  function AddModulePreview({ t, state }) {
4013
4484
  const preview = state.preview;
@@ -4025,6 +4496,7 @@ function AddModuleResult({ t, state }) {
4025
4496
  const restart = outcome.change.application === "restart-required";
4026
4497
  const cancelled = outcome.change.application === "cancelled";
4027
4498
  const notModule = outcome.supply.state === "not-a-module";
4499
+ const plugin = outcome.plugin;
4028
4500
  const name = outcome.module.kind === "module" ? outcome.module.unit : outcome.module.pkg;
4029
4501
  return /* @__PURE__ */ h("div", { className: AddModule_module_default.body }, /* @__PURE__ */ h("div", { className: AddModule_module_default.hero }, /* @__PURE__ */ h("span", {
4030
4502
  className: AddModule_module_default.heroIcon,
@@ -4036,7 +4508,7 @@ function AddModuleResult({ t, state }) {
4036
4508
  }) : /* @__PURE__ */ h(_deepseek_ai_dsh_client_ui_primitives.IconCheckCircleFillRegular, { size: 28 })), /* @__PURE__ */ h("h2", {
4037
4509
  className: AddModule_module_default.wizardTitle,
4038
4510
  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", {
4511
+ }, 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
4512
  className: AddModule_module_default.resultWarn,
4041
4513
  role: "alert"
4042
4514
  }, 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 +4579,7 @@ var ADD_MODULE_MOCKS = [
4107
4579
  refused("not-found"),
4108
4580
  refused("not-a-package"),
4109
4581
  refused("not-a-bundle"),
4582
+ refused("not-a-module-or-plugin", "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上"),
4110
4583
  refused("network", "getaddrinfo ENOTFOUND registry.example.com"),
4111
4584
  refused("unknown", "pnpm 的返回码是 1"),
4112
4585
  {
@@ -4188,6 +4661,25 @@ var ADD_MODULE_MOCKS = [
4188
4661
  spec: "@dsh-workbench/module-hello",
4189
4662
  outcome: outcome({})
4190
4663
  },
4664
+ {
4665
+ id: "done-plugin",
4666
+ label: "装好了(opencli 插件)",
4667
+ phase: "result",
4668
+ spec: "opencli-plugin-cmdb",
4669
+ outcome: {
4670
+ change: change({ target: "opencli-plugin-cmdb" }),
4671
+ module: {
4672
+ kind: "unknown",
4673
+ pkg: "opencli-plugin-cmdb"
4674
+ },
4675
+ supply: { state: "no-needs" },
4676
+ plugin: {
4677
+ kind: "opencli-plugin",
4678
+ pkg: "opencli-plugin-cmdb",
4679
+ name: "cmdb"
4680
+ }
4681
+ }
4682
+ },
4191
4683
  {
4192
4684
  id: "done-restart",
4193
4685
  label: "装好了(要重启)",
@@ -4282,6 +4774,42 @@ function stateOfMock(base, mock) {
4282
4774
  };
4283
4775
  }
4284
4776
  //#endregion
4777
+ //#region src/shared/hooks/use-visible-busy.ts
4778
+ var use_visible_busy_exports = /* @__PURE__ */ __exportAll({
4779
+ busyView: () => busyView,
4780
+ heldPhase: () => heldPhase,
4781
+ useVisibleBusy: () => useVisibleBusy
4782
+ });
4783
+ function heldPhase(phase, refreshing) {
4784
+ return refreshing && phase === "ready" ? "loading" : phase;
4785
+ }
4786
+ function busyView(active, visible) {
4787
+ return {
4788
+ busy: visible,
4789
+ settling: visible && !active
4790
+ };
4791
+ }
4792
+ function useVisibleBusy(active, floorMs) {
4793
+ const [visible, setVisible] = useState(active);
4794
+ const since = useRef(null);
4795
+ useEffect(() => {
4796
+ if (active) {
4797
+ if (since.current === null) since.current = Date.now();
4798
+ setVisible(true);
4799
+ return;
4800
+ }
4801
+ const held = holdBusy(since.current, Date.now(), floorMs);
4802
+ since.current = null;
4803
+ if (!held.visible) {
4804
+ setVisible(false);
4805
+ return;
4806
+ }
4807
+ const timer = setTimeout(() => setVisible(false), held.releaseInMs);
4808
+ return () => clearTimeout(timer);
4809
+ }, [active, floorMs]);
4810
+ return busyView(active, visible);
4811
+ }
4812
+ //#endregion
4285
4813
  //#region src/app/layouts/market/use-add-module.ts
4286
4814
  function newRequestId() {
4287
4815
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -4304,6 +4832,7 @@ function useAddModule(options) {
4304
4832
  const [failure, setFailure] = useState(null);
4305
4833
  const generationRef = useRef(0);
4306
4834
  const requestIdRef = useRef("");
4835
+ const { busy: classifying } = useVisibleBusy(phase === "inspecting", 500);
4307
4836
  const write = useHostWrite({
4308
4837
  channel: "provision",
4309
4838
  networkText: failedText
@@ -4401,7 +4930,8 @@ function useAddModule(options) {
4401
4930
  setOutcome({
4402
4931
  change: value.change,
4403
4932
  module: value.module,
4404
- supply: value.supply
4933
+ supply: value.supply,
4934
+ ...value.plugin === void 0 ? {} : { plugin: value.plugin }
4405
4935
  });
4406
4936
  setPhase("result");
4407
4937
  if (value.change.application === "applied" || value.change.application === "restart-required") onInstalled();
@@ -4446,7 +4976,7 @@ function useAddModule(options) {
4446
4976
  custom,
4447
4977
  registryOpen,
4448
4978
  guideOpen,
4449
- phase,
4979
+ phase: heldWizardPhase(phase, classifying),
4450
4980
  choices,
4451
4981
  problem,
4452
4982
  preview,
@@ -4604,10 +5134,10 @@ function makeMarketEntryIcon(units) {
4604
5134
  function MarketPanel({ t, units }) {
4605
5135
  const manifests = useLiveManifests(units);
4606
5136
  const { capabilities, checkedAt, checking, forceProbe, reload: reloadCapability } = useCapabilityMap(0);
4607
- const { modules, read: installedRead, reload: reloadVersions } = useModuleVersions();
5137
+ const { modules, plugins, read: installedRead, reload: reloadVersions } = useModuleVersions();
4608
5138
  const [adding, setAdding] = useState(false);
4609
5139
  const updates = useMemo(() => marketUpdatesOf(modules), [modules]);
4610
- const installed = modules ?? void 0;
5140
+ const installed = modules === null ? void 0 : withClientRefusals(modules, units.refusals());
4611
5141
  const reload = useCallback(() => {
4612
5142
  reloadCapability();
4613
5143
  reloadVersions();
@@ -4696,6 +5226,7 @@ function MarketPanel({ t, units }) {
4696
5226
  updates,
4697
5227
  installed,
4698
5228
  installedRead: mock === void 0 ? installedRead : mock.installedRead ?? true,
5229
+ plugins,
4699
5230
  settling: mock === void 0 ? settling : void 0,
4700
5231
  mock
4701
5232
  }), /* @__PURE__ */ h(AddModuleDialog, {
@@ -4751,6 +5282,41 @@ var CLIENT_UNITS = [];
4751
5282
  function assembleClientUnits(registry) {
4752
5283
  for (const face of CLIENT_UNITS) registry.register(face);
4753
5284
  }
5285
+ //#endregion
5286
+ //#region src/shared/api/unit-manifests.ts
5287
+ var UNIT_MANIFESTS_ROUTE = "unit-manifests";
5288
+ //#endregion
5289
+ //#region src/app/units/manifests.ts
5290
+ function createUnitManifestLoader(options = {}) {
5291
+ let inFlight = false;
5292
+ let queued = false;
5293
+ let watching = false;
5294
+ const subscribeFacts = options.subscribeFacts ?? subscribeFactRevision;
5295
+ const load = async (registry) => {
5296
+ if (inFlight) {
5297
+ queued = true;
5298
+ return;
5299
+ }
5300
+ inFlight = true;
5301
+ try {
5302
+ const response = await apiGet(UNIT_MANIFESTS_ROUTE).catch(() => null);
5303
+ if (response?.ok === true && Array.isArray(response.manifests)) registry.setManifests(response.manifests);
5304
+ } finally {
5305
+ inFlight = false;
5306
+ if (queued) {
5307
+ queued = false;
5308
+ load(registry);
5309
+ }
5310
+ }
5311
+ };
5312
+ return (registry) => {
5313
+ if (!watching) {
5314
+ watching = true;
5315
+ subscribeFacts(factOfRoute(UNIT_MANIFESTS_ROUTE), () => void load(registry));
5316
+ }
5317
+ load(registry);
5318
+ };
5319
+ }
4754
5320
  var BootGate_module_default = {
4755
5321
  actions: "_actions_jlw8j_1",
4756
5322
  progress: "_progress_jlw8j_2",
@@ -7428,42 +7994,6 @@ function useLongTask(options) {
7428
7994
  return { runs };
7429
7995
  }
7430
7996
  //#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
7997
  //#region src/shared/hooks/use-unit-board.ts
7468
7998
  var use_unit_board_exports = /* @__PURE__ */ __exportAll({ useUnitBoard: () => useUnitBoard });
7469
7999
  function useUnitBoard(options) {
@@ -7677,7 +8207,7 @@ var kit = {
7677
8207
  };
7678
8208
  function messagesOf(units) {
7679
8209
  return units.map((face) => ({
7680
- unit: face.manifest.id,
8210
+ unit: face.unit,
7681
8211
  ...face.messages === void 0 ? {} : { messages: face.messages }
7682
8212
  }));
7683
8213
  }
@@ -7718,12 +8248,14 @@ function apply(ctx) {
7718
8248
  }
7719
8249
  }
7720
8250
  function mount(ctx) {
7721
- const units = createClientUnitRegistry();
8251
+ const loadUnitManifests = createUnitManifestLoader();
8252
+ const units = createClientUnitRegistry({ onUnknownUnit: () => loadUnitManifests(units) });
7722
8253
  ctx.provide(UNITS_SERVICE, {
7723
8254
  registerUnit: (face) => units.register(face),
7724
8255
  manifests: () => units.manifests()
7725
8256
  });
7726
8257
  assembleClientUnits(units);
8258
+ loadUnitManifests(units);
7727
8259
  const store = rightbarStore;
7728
8260
  const observable = {
7729
8261
  getSnapshot: () => store.getSnapshot(),