@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
package/client.js CHANGED
@@ -426,6 +426,133 @@ window.__ModuleLoader__.load({
426
426
  if (error === void 0) return null;
427
427
  return error.code === "" ? "unknown" : error.code;
428
428
  }
429
+ var SUPPORTED_API_VERSIONS = [1];
430
+ var DECLARATION_KEYS = [
431
+ "apiVersion",
432
+ "unit",
433
+ "card",
434
+ "tools",
435
+ "host",
436
+ "env"
437
+ ];
438
+ var DECLARATION_REQUIRED_KEYS = [
439
+ "apiVersion",
440
+ "unit",
441
+ "card",
442
+ "tools",
443
+ "host"
444
+ ];
445
+ var CARD_KEYS = ["titleKey", "order"];
446
+ var TOOL_KEYS = [
447
+ "name",
448
+ "writes",
449
+ "key"
450
+ ];
451
+ var TOOL_KEY_SHAPE = /^[a-z][a-zA-Z0-9]*$/;
452
+ var HOST_KEYS = ["inject", "optional"];
453
+ function text(value) {
454
+ return typeof value === "string" ? value.trim() : "";
455
+ }
456
+ function isRecord(value) {
457
+ return typeof value === "object" && value !== null && !Array.isArray(value);
458
+ }
459
+ function outsideKeys(value, allowed) {
460
+ return Object.keys(value).filter((key) => !allowed.includes(key));
461
+ }
462
+ function listOf(keys) {
463
+ return keys.map((key) => JSON.stringify(key)).join(" / ");
464
+ }
465
+ function stringArrayProblems(at, value) {
466
+ if (!Array.isArray(value)) return [`${at} 要是一串字符串,现在是 ${JSON.stringify(value)}`];
467
+ return value.flatMap((item, index) => text(item) === "" ? [`${at}[${index}] 不是非空字符串:${JSON.stringify(item)}`] : []);
468
+ }
469
+ function declarationProblemsOf(subject, raw) {
470
+ if (!isRecord(raw)) return [`${subject}: dsh.workbench 不是一个对象 —— ${JSON.stringify(raw)}`];
471
+ const version = raw.apiVersion;
472
+ if (version === void 0) return [`${subject}: dsh.workbench 没有 apiVersion —— 协议从 v1 起,写 \`"apiVersion": 1\`。缺它 = 外壳不认这个模块(ADR-0038 §2.3)`];
473
+ if (!Number.isInteger(version) || !SUPPORTED_API_VERSIONS.includes(version)) return [`${subject}: dsh.workbench.apiVersion 是 ${JSON.stringify(version)},这里只认 ${SUPPORTED_API_VERSIONS.join(" / ")}(要整数)`];
474
+ const problems = [];
475
+ const unknown = outsideKeys(raw, DECLARATION_KEYS);
476
+ if (unknown.length > 0) problems.push(`${subject}: dsh.workbench 里有协议 v1 不认识的键 ${listOf(unknown)} —— 只有 ${listOf(DECLARATION_KEYS)}。(拼错的键会被外壳**静默忽略**:那句声明等于没写)`);
477
+ for (const key of DECLARATION_REQUIRED_KEYS) if (raw[key] === void 0) problems.push(`${subject}: dsh.workbench 缺 ${key}` + (key === "card" ? "(这个模块没有卡片就写 `\"card\": null`)" : ""));
478
+ const card = raw.card;
479
+ if (card !== void 0 && card !== null) {
480
+ if (!isRecord(card)) problems.push(`${subject}: dsh.workbench.card 要么是对象、要么是 null,现在是 ${JSON.stringify(card)}`);
481
+ else {
482
+ const extra = outsideKeys(card, CARD_KEYS);
483
+ if (extra.length > 0) problems.push(`${subject}: dsh.workbench.card 里有不认的键 ${listOf(extra)} —— card 只有 ${listOf(CARD_KEYS)}(卡片默认显不显示由运行期的面板配置决定,清单里没有这一位)`);
484
+ if (text(card.titleKey) === "") problems.push(`${subject}: dsh.workbench.card.titleKey 要是非空字符串(面板那一格的文案键)`);
485
+ if (typeof card.order !== "number" || !Number.isFinite(card.order)) problems.push(`${subject}: dsh.workbench.card.order 要是数字,现在是 ${JSON.stringify(card.order)}`);
486
+ }
487
+ }
488
+ const tools = raw.tools;
489
+ if (tools !== void 0) {
490
+ if (!Array.isArray(tools)) problems.push(`${subject}: dsh.workbench.tools 要是一串声明,现在是 ${JSON.stringify(tools)}`);
491
+ else {
492
+ const seen = /* @__PURE__ */ new Set();
493
+ const seenKeys = /* @__PURE__ */ new Set();
494
+ tools.forEach((tool, index) => {
495
+ const at = `dsh.workbench.tools[${index}]`;
496
+ if (!isRecord(tool)) {
497
+ problems.push(`${subject}: ${at} 要是对象,现在是 ${JSON.stringify(tool)}`);
498
+ return;
499
+ }
500
+ const extra = outsideKeys(tool, TOOL_KEYS);
501
+ if (extra.length > 0) problems.push(`${subject}: ${at} 里有不认的键 ${listOf(extra)} —— tools 只有 ${listOf(TOOL_KEYS)}`);
502
+ const name = text(tool.name);
503
+ if (name === "") problems.push(`${subject}: ${at}.name 要是非空字符串`);
504
+ else if (seen.has(name)) problems.push(`${subject}: ${at}.name「${name}」与前面那条撞了 —— 工具名会进 dsh 的工具表,不许重`);
505
+ else seen.add(name);
506
+ if (typeof tool.writes !== "boolean") problems.push(`${subject}: ${at}.writes 要是 true / false(它是"这会写"的唯一声明,缺了审批无从认领)`);
507
+ if (tool.key !== void 0) {
508
+ const key = text(tool.key);
509
+ if (!TOOL_KEY_SHAPE.test(key)) problems.push(`${subject}: ${at}.key 要是 lowerCamelCase 的标识符(如 \`sync\` / \`podLogs\`),现在是 ${JSON.stringify(tool.key)} —— 它会变成生成物里的键名,所以不引号、不许带点或横线`);
510
+ else if (seenKeys.has(key)) problems.push(`${subject}: ${at}.key「${key}」与前面那条撞了 —— 生成物里的键只能有一个`);
511
+ else seenKeys.add(key);
512
+ }
513
+ });
514
+ }
515
+ }
516
+ const host = raw.host;
517
+ if (host !== void 0) {
518
+ if (!isRecord(host)) problems.push(`${subject}: dsh.workbench.host 要是对象,现在是 ${JSON.stringify(host)}`);
519
+ else {
520
+ const extra = outsideKeys(host, HOST_KEYS);
521
+ if (extra.length > 0) problems.push(`${subject}: dsh.workbench.host 里有不认的键 ${listOf(extra)} —— host 只有 ${listOf(HOST_KEYS)}`);
522
+ if (host.inject === void 0) problems.push(`${subject}: dsh.workbench.host.inject 没写 —— 它声明这个模块要外壳的哪几个服务,外壳靠它判"能不能挂"。一个都不要也要写 \`"inject": []\``);
523
+ else problems.push(...stringArrayProblems(`${subject}: dsh.workbench.host.inject`, host.inject));
524
+ if (host.optional !== void 0) problems.push(...stringArrayProblems(`${subject}: dsh.workbench.host.optional`, host.optional));
525
+ }
526
+ }
527
+ if (raw.env !== void 0 && !isRecord(raw.env)) problems.push(`${subject}: dsh.workbench.env 要是对象(command / plugins / libs)—— 语义由门禁的 env 判据查,这里只查形状`);
528
+ return problems;
529
+ }
530
+ function readDeclaration(subject, raw) {
531
+ const problems = declarationProblemsOf(subject, raw);
532
+ if (problems.length > 0) return { problems };
533
+ const declaration = raw;
534
+ const card = declaration.card;
535
+ return {
536
+ declaration,
537
+ problems: [],
538
+ manifest: {
539
+ id: text(declaration.unit),
540
+ card: card === null ? null : {
541
+ titleKey: text(card.titleKey),
542
+ order: card.order
543
+ },
544
+ tools: declaration.tools.map((tool) => ({
545
+ name: text(tool.name),
546
+ writes: tool.writes
547
+ })),
548
+ host: {
549
+ inject: [...declaration.host.inject ?? []],
550
+ ...declaration.host.optional === void 0 ? {} : { optional: [...declaration.host.optional] }
551
+ },
552
+ ...declaration.env === void 0 ? {} : { env: declaration.env }
553
+ }
554
+ };
555
+ }
429
556
  //#endregion
430
557
  //#region ../shared/dist/config/constants.js
431
558
  var PLUGIN_ID = "@dsh-workbench/core";
@@ -613,7 +740,7 @@ window.__ModuleLoader__.load({
613
740
  if (owner?.approval !== void 0) {
614
741
  const Render = owner.approval.render;
615
742
  return h(CardBoundary, {
616
- unit: owner.manifest.id,
743
+ unit: owner.unit,
617
744
  resetKey: props.callId ?? "",
618
745
  t
619
746
  }, h(Render, {
@@ -1114,7 +1241,7 @@ window.__ModuleLoader__.load({
1114
1241
  "panels.market.missing": "not installed",
1115
1242
  "panels.market.unchecked": "unknown",
1116
1243
  "panels.market.checking": "checking",
1117
- "panels.market.sectionInstalled": "Installed",
1244
+ "panels.market.sectionInstalled": "Installed modules",
1118
1245
  "panels.market.sectionPending": "Not installed",
1119
1246
  "panels.market.sectionShared": "Shared dependencies",
1120
1247
  "panels.market.sharedUsedBy": "Used by {modules}",
@@ -1142,11 +1269,19 @@ window.__ModuleLoader__.load({
1142
1269
  "panels.market.noSource": "This module declares no install method — install it yourself",
1143
1270
  "panels.market.badSource": "The install method is incomplete — install it yourself",
1144
1271
  "panels.market.empty": "No modules available",
1145
- "panels.market.installedSection": "Installed",
1146
- "panels.market.sectionLocal": "Linked locally",
1272
+ "panels.market.sectionLocal": "Local modules",
1147
1273
  "panels.market.sectionUnlisted": "Not from the market",
1274
+ "panels.market.sectionPlugins": "Installed opencli plugins",
1148
1275
  "panels.market.moduleOutdated": "update available",
1276
+ "panels.market.moduleRefused": "This module was built for an older version of the workbench, so it does not work here. Update it, or uninstall.",
1277
+ "panels.market.moduleRefusedDetail": "Why it cannot be used: {reason}",
1149
1278
  "panels.market.moduleRestartHint": "installed but not active — restart dsh once to bring it up",
1279
+ "panels.market.pluginKeptInUse": "Files kept for now: {names} still uses it.",
1280
+ "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.",
1281
+ "panels.market.pluginKeptHeld": "Files kept: this one is yours, and a module letting go will not remove it.",
1282
+ "panels.market.pluginKeptUnnamed": "Files kept for now: this one cannot be identified as a plugin, so it is unclear who needs it.",
1283
+ "panels.market.pluginAlsoKnownAs": "Its package is {pkg} (updates and uninstall use that)",
1284
+ "panels.market.pluginBroken": "This install is broken: it cannot be identified as a plugin. Uninstall it, or install it again by package name above.",
1150
1285
  "panels.market.restartNeeded": "The new version is installed — restart once to pick it up.",
1151
1286
  "panels.market.foot": "After installing, enable it in the panel page of that project.",
1152
1287
  "panels.market.recheck": "Check for updates",
@@ -1194,7 +1329,11 @@ window.__ModuleLoader__.load({
1194
1329
  "panels.market.add.problemInstalled": "This module is already installed",
1195
1330
  "panels.market.add.problemNotFound": "No such module found",
1196
1331
  "panels.market.add.problemNotPackage": "That path does not exist or is not a valid module package",
1197
- "panels.market.add.problemNotBundle": "This package declares no bundle, so it cannot be installed as a module: {reason}",
1332
+ "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.",
1333
+ "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.",
1334
+ "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.",
1335
+ "panels.market.add.failureNotModuleOrPlugin": "This package is neither a workbench module nor an opencli plugin, so installing it would do nothing",
1336
+ "panels.market.add.installedPlugin": "Installed: the opencli plugin {name}",
1198
1337
  "panels.market.add.problemNetwork": "Cannot reach the module source; check the network and retry",
1199
1338
  "panels.market.add.problemUnknown": "Cannot fetch module information: {reason}",
1200
1339
  "panels.market.add.failureNetwork": "The network connection failed",
@@ -1225,6 +1364,7 @@ window.__ModuleLoader__.load({
1225
1364
  "panels.market.add.registryPnpm": "pnpm's own registry",
1226
1365
  "panels.market.add.check": "Check",
1227
1366
  "panels.market.add.checking": "Checking…",
1367
+ "panels.market.add.classifying": "Working out what it is…",
1228
1368
  "panels.market.add.change": "Edit",
1229
1369
  "panels.market.add.previewTitle": "About to install",
1230
1370
  "panels.market.add.fieldName": "Package",
@@ -1429,7 +1569,7 @@ window.__ModuleLoader__.load({
1429
1569
  "panels.market.missing": "未装",
1430
1570
  "panels.market.unchecked": "状态未知",
1431
1571
  "panels.market.checking": "检测中",
1432
- "panels.market.sectionInstalled": "已安装",
1572
+ "panels.market.sectionInstalled": "已安装模块",
1433
1573
  "panels.market.sectionPending": "未安装",
1434
1574
  "panels.market.sectionShared": "共用依赖",
1435
1575
  "panels.market.sharedUsedBy": "供 {modules} 使用",
@@ -1457,11 +1597,19 @@ window.__ModuleLoader__.load({
1457
1597
  "panels.market.noSource": "这个模块没声明怎么装,只能自己动手",
1458
1598
  "panels.market.badSource": "安装方式没写全,只能自己动手",
1459
1599
  "panels.market.empty": "暂无可用模块",
1460
- "panels.market.installedSection": "已安装",
1461
- "panels.market.sectionLocal": "本地装的",
1600
+ "panels.market.sectionLocal": "本地模块",
1462
1601
  "panels.market.sectionUnlisted": "不是从市场装的",
1602
+ "panels.market.sectionPlugins": "已安装 opencli 插件",
1463
1603
  "panels.market.moduleOutdated": "有新版",
1604
+ "panels.market.moduleRefused": "这个模块是给旧版本的工作台写的,现在用不了。更新它,或者卸载。",
1605
+ "panels.market.moduleRefusedDetail": "装不上的原因:{reason}",
1464
1606
  "panels.market.moduleRestartHint": "装好了但还没生效 —— 重启一次 dsh 就会出来",
1607
+ "panels.market.pluginKeptInUse": "文件先留着:{names} 还要用它。",
1608
+ "panels.market.pluginKeptUnsure": "文件先留着:这台机器上还有模块看不出用了什么,说不准谁在用它。",
1609
+ "panels.market.pluginKeptHeld": "文件留着:你自己装的那一份,模块放手不动它。",
1610
+ "panels.market.pluginKeptUnnamed": "文件先留着:看不出这是哪个插件,说不准谁在用它。",
1611
+ "panels.market.pluginAlsoKnownAs": "包名是 {pkg}(更新与卸载按它来)",
1612
+ "panels.market.pluginBroken": "这份装坏了:看不出它是个插件。可以卸载,或者去上面按包名再装一次。",
1465
1613
  "panels.market.restartNeeded": "新版装好了,重启一次才生效。",
1466
1614
  "panels.market.foot": "装好后到那个项目的面板页勾选它,模块才会出现。",
1467
1615
  "panels.market.recheck": "重新检查更新",
@@ -1509,7 +1657,11 @@ window.__ModuleLoader__.load({
1509
1657
  "panels.market.add.problemInstalled": "该模块已安装",
1510
1658
  "panels.market.add.problemNotFound": "未找到相关模块",
1511
1659
  "panels.market.add.problemNotPackage": "该路径不存在或不是有效的模块包",
1512
- "panels.market.add.problemNotBundle": "这个包没有声明组合包,无法作为模块安装:{reason}",
1660
+ "panels.market.add.problemNotBundle": "这个包装不进这里:{reason}。要是它其实是个 opencli 插件,请改用包名(例如 opencli-plugin-cmdb)或者本地目录 —— 从 git 地址和 tarball 判断不出它是什么。",
1661
+ "panels.market.add.problemNotWorkbenchModule": "这个包跟现在的工作台对不上,装上也用不了。换一个版本,或者找这个模块的作者。",
1662
+ "panels.market.add.problemNotModuleOrPlugin": "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上。换一个版本,或者找这个包的作者。",
1663
+ "panels.market.add.failureNotModuleOrPlugin": "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上",
1664
+ "panels.market.add.installedPlugin": "装上了:opencli 插件 {name}",
1513
1665
  "panels.market.add.problemNetwork": "无法连接模块源,请检查网络后重试",
1514
1666
  "panels.market.add.problemUnknown": "无法获取模块信息:{reason}",
1515
1667
  "panels.market.add.failureNetwork": "网络连接失败",
@@ -1540,6 +1692,7 @@ window.__ModuleLoader__.load({
1540
1692
  "panels.market.add.registryPnpm": "pnpm 自己配的源",
1541
1693
  "panels.market.add.check": "预检",
1542
1694
  "panels.market.add.checking": "正在预检…",
1695
+ "panels.market.add.classifying": "判定中…",
1543
1696
  "panels.market.add.change": "改一下",
1544
1697
  "panels.market.add.previewTitle": "将要安装",
1545
1698
  "panels.market.add.fieldName": "包名",
@@ -1816,6 +1969,8 @@ window.__ModuleLoader__.load({
1816
1969
  CenteredState: () => CenteredState,
1817
1970
  ChoiceGroup: () => ChoiceGroup,
1818
1971
  Component: () => Component,
1972
+ DECLARATION_KEYS: () => DECLARATION_KEYS,
1973
+ DECLARATION_REQUIRED_KEYS: () => DECLARATION_REQUIRED_KEYS,
1819
1974
  DEFAULT_WORKSPACE_TITLE: () => DEFAULT_WORKSPACE_TITLE,
1820
1975
  EVENTS_PATH: () => EVENTS_PATH,
1821
1976
  EmptyState: () => EmptyState,
@@ -1854,6 +2009,7 @@ window.__ModuleLoader__.load({
1854
2009
  STORAGE_KEY_PROJECT: () => STORAGE_KEY_PROJECT,
1855
2010
  STORAGE_KEY_RIGHTBAR: () => STORAGE_KEY_RIGHTBAR,
1856
2011
  STORAGE_KEY_RIGHTBAR_COLUMN: () => STORAGE_KEY_RIGHTBAR_COLUMN,
2012
+ SUPPORTED_API_VERSIONS: () => SUPPORTED_API_VERSIONS,
1857
2013
  SaveBar: () => SaveBar,
1858
2014
  SettingsSection: () => SettingsSection,
1859
2015
  SkeletonRows: () => SkeletonRows,
@@ -1864,6 +2020,7 @@ window.__ModuleLoader__.load({
1864
2020
  VERSION_CHECK_TIMEOUT_MS: () => VERSION_CHECK_TIMEOUT_MS,
1865
2021
  VERSION_CHECK_TTL_MS: () => VERSION_CHECK_TTL_MS,
1866
2022
  WARM_TIMEOUT_MS: () => WARM_TIMEOUT_MS,
2023
+ WORKBENCH_API_VERSION: () => 1,
1867
2024
  apiGet: () => apiGet,
1868
2025
  apiPost: () => apiPost,
1869
2026
  blockerActionLabel: () => blockerActionLabel,
@@ -1873,6 +2030,7 @@ window.__ModuleLoader__.load({
1873
2030
  buildMarker: () => buildMarker,
1874
2031
  createFactChannel: () => createFactChannel,
1875
2032
  cx: () => cx,
2033
+ declarationProblemsOf: () => declarationProblemsOf,
1876
2034
  decodeRef: () => decodeRef,
1877
2035
  dictionaries: () => dictionaries,
1878
2036
  en: () => en,
@@ -1896,6 +2054,7 @@ window.__ModuleLoader__.load({
1896
2054
  parseMarkers: () => parseMarkers,
1897
2055
  probeKeyOf: () => probeKeyOf,
1898
2056
  quoteShellArg: () => quoteShellArg,
2057
+ readDeclaration: () => readDeclaration,
1899
2058
  readFlag: () => readFlag,
1900
2059
  readText: () => readText,
1901
2060
  relativeTime: () => relativeTime,
@@ -2008,6 +2167,7 @@ window.__ModuleLoader__.load({
2008
2167
  //#endregion
2009
2168
  //#region src/shared/hooks/use-facts.ts
2010
2169
  var use_facts_exports = /* @__PURE__ */ __exportAll({
2170
+ subscribeFactRevision: () => subscribeFactRevision,
2011
2171
  useFactInvalidate: () => useFactInvalidate,
2012
2172
  useFactRevision: () => useFactRevision
2013
2173
  });
@@ -2015,6 +2175,9 @@ window.__ModuleLoader__.load({
2015
2175
  function useFactRevision(fact) {
2016
2176
  return useSyncExternalStore(useCallback((listener) => channel.subscribe(fact, listener), [fact]), useCallback(() => channel.revisionOf(fact), [fact]));
2017
2177
  }
2178
+ function subscribeFactRevision(fact, listener) {
2179
+ return channel.subscribe(fact, listener);
2180
+ }
2018
2181
  function useFactInvalidate() {
2019
2182
  return useCallback((fact) => channel.refresh(fact), []);
2020
2183
  }
@@ -2242,6 +2405,9 @@ window.__ModuleLoader__.load({
2242
2405
  //#endregion
2243
2406
  //#region src/app/layouts/rightbar/market-freshness.ts
2244
2407
  var MARKET_STALE_MS = VERSION_CHECK_TTL_MS;
2408
+ function marketSettled(freshness, installedRead) {
2409
+ return freshness.loaded && installedRead !== false;
2410
+ }
2245
2411
  function shouldProbeOnEnter(input) {
2246
2412
  if (input.checkedAt === null) return true;
2247
2413
  return input.now - input.checkedAt >= input.staleMs;
@@ -2539,8 +2705,7 @@ window.__ModuleLoader__.load({
2539
2705
  ...mine,
2540
2706
  card: {
2541
2707
  titleKey: "一个名字特别特别长的模块用来验证省略号会不会把按钮挤下去",
2542
- order: 10,
2543
- enabledByDefault: true
2708
+ order: 10
2544
2709
  }
2545
2710
  }],
2546
2711
  capabilities: probe$1(id, "ok"),
@@ -2644,6 +2809,24 @@ window.__ModuleLoader__.load({
2644
2809
  installedActions: ["update-bundle", "remove-bundle"]
2645
2810
  }
2646
2811
  },
2812
+ {
2813
+ id: "installed-client-refused",
2814
+ label: "本机装了,但浏览器这半没挂上(拒因由客户端报告)",
2815
+ manifests: [found],
2816
+ capabilities: {},
2817
+ installed: [{
2818
+ unit: id,
2819
+ pkg: "@dsh-workbench/module-one",
2820
+ installed: "0.1.2",
2821
+ clientRefused: [`宿主没有担保 unit「${id}」的清单 —— 这个包要么没装进这份 profile,要么它的 dsh.workbench 没通过校验。卡片先不挂:清单只有一处真相(ADR-0038 §2.2)`]
2822
+ }],
2823
+ expect: {
2824
+ rows: 0,
2825
+ installed: 1,
2826
+ installedActions: ["remove-bundle"],
2827
+ installedNote: true
2828
+ }
2829
+ },
2647
2830
  {
2648
2831
  id: "installed-confirm",
2649
2832
  label: "卸载本机模块(Modal 确认,标题带模块名)",
@@ -2692,6 +2875,94 @@ window.__ModuleLoader__.load({
2692
2875
  actions: ["uninstall"]
2693
2876
  }
2694
2877
  },
2878
+ {
2879
+ id: "plugin-outdated",
2880
+ label: "用户装的 opencli 插件:有新版(给更新 / 卸载)",
2881
+ manifests: [],
2882
+ capabilities: {},
2883
+ plugins: [{
2884
+ pkg: "opencli-plugin-cmdb",
2885
+ name: "cmdb",
2886
+ installed: "1.0.24",
2887
+ latest: "1.0.25"
2888
+ }],
2889
+ expect: {
2890
+ rows: 0,
2891
+ plugins: 1,
2892
+ pluginActions: ["update-plugin", "uninstall-plugin"]
2893
+ }
2894
+ },
2895
+ {
2896
+ id: "plugin-local",
2897
+ label: "本地目录装的 opencli 插件:没有版本源,只给卸载",
2898
+ manifests: [],
2899
+ capabilities: {},
2900
+ plugins: [{
2901
+ pkg: "/Users/me/my-local-plugin",
2902
+ name: "my-local-plugin",
2903
+ local: true
2904
+ }],
2905
+ expect: {
2906
+ rows: 0,
2907
+ plugins: 1,
2908
+ pluginActions: ["uninstall-plugin"],
2909
+ pluginNote: false
2910
+ }
2911
+ },
2912
+ {
2913
+ id: "plugin-alias",
2914
+ label: "插件名 ≠ 包名:行上两个都写出来(更新 / 卸载按包名)",
2915
+ manifests: [],
2916
+ capabilities: {},
2917
+ plugins: [{
2918
+ pkg: "opencli-plugin-cmdb",
2919
+ name: "cmdb",
2920
+ installed: "1.0.24",
2921
+ latest: "1.0.25"
2922
+ }],
2923
+ expect: {
2924
+ rows: 0,
2925
+ plugins: 1,
2926
+ pluginActions: ["update-plugin", "uninstall-plugin"],
2927
+ pluginNoteKey: "panels.market.pluginAlsoKnownAs"
2928
+ }
2929
+ },
2930
+ {
2931
+ id: "plugin-broken",
2932
+ label: "装坏了的那一份(目录在但看不出是个插件):行上直说,给卸载与指路",
2933
+ manifests: [],
2934
+ capabilities: {},
2935
+ plugins: [{
2936
+ pkg: "opencli-plugin-cmdb",
2937
+ name: "cmdb",
2938
+ installed: "1.0.24",
2939
+ broken: true
2940
+ }],
2941
+ expect: {
2942
+ rows: 0,
2943
+ plugins: 1,
2944
+ pluginActions: ["uninstall-plugin"],
2945
+ pluginNoteKey: "panels.market.pluginBroken"
2946
+ }
2947
+ },
2948
+ {
2949
+ id: "plugin-kept",
2950
+ label: "卸掉用户装的插件,但还有模块在用:文件先留着(行上给一句为什么)",
2951
+ manifests: [],
2952
+ capabilities: {},
2953
+ plugins: [{
2954
+ pkg: "opencli-plugin-cmdb",
2955
+ name: "cmdb",
2956
+ installed: "1.0.24"
2957
+ }],
2958
+ notice: "文件先留着:cmdb、argocd 还要用它。",
2959
+ expect: {
2960
+ rows: 0,
2961
+ plugins: 1,
2962
+ pluginActions: ["uninstall-plugin"],
2963
+ notice: true
2964
+ }
2965
+ },
2695
2966
  {
2696
2967
  id: "empty",
2697
2968
  label: "市场空态",
@@ -2749,7 +3020,6 @@ window.__ModuleLoader__.load({
2749
3020
  };
2750
3021
  },
2751
3022
  all: () => sorted,
2752
- visible: () => sorted.filter((module) => module.enabledByDefault),
2753
3023
  byId: (id) => index.get(id),
2754
3024
  stepOwner: (kind) => steps.get(kind),
2755
3025
  subscribe(listener) {
@@ -2761,20 +3031,21 @@ window.__ModuleLoader__.load({
2761
3031
  }
2762
3032
  //#endregion
2763
3033
  //#region src/app/units/registry.ts
2764
- function toBoardModule(face) {
3034
+ function toBoardModule(face, manifest) {
2765
3035
  return {
2766
- id: face.manifest.id,
2767
- titleKey: face.manifest.card?.titleKey ?? face.manifest.id,
2768
- order: face.manifest.card?.order ?? 100,
2769
- enabledByDefault: face.manifest.card?.enabledByDefault ?? false,
3036
+ id: manifest.id,
3037
+ titleKey: manifest.card?.titleKey ?? manifest.id,
3038
+ order: manifest.card?.order ?? 100,
2770
3039
  View: face.card,
2771
3040
  ...face.steps === void 0 ? {} : { steps: face.steps }
2772
3041
  };
2773
3042
  }
2774
- function createClientUnitRegistry() {
2775
- const faces = [];
3043
+ function createClientUnitRegistry(options = {}) {
3044
+ const registrations = [];
3045
+ const refusals = [];
2776
3046
  const listeners = /* @__PURE__ */ new Set();
2777
3047
  const board = createBoardRegistry();
3048
+ let known = /* @__PURE__ */ new Map();
2778
3049
  let revision = 0;
2779
3050
  let manifestsCache;
2780
3051
  function notify() {
@@ -2785,28 +3056,86 @@ window.__ModuleLoader__.load({
2785
3056
  console.warn(`[dsh-workbench] 套件注册的订阅者报错(不影响其它订阅者与这次注册):${String(error)}`);
2786
3057
  }
2787
3058
  }
3059
+ function warnRefusal(unit, reasons) {
3060
+ refusals.push({
3061
+ unit,
3062
+ reasons
3063
+ });
3064
+ console.warn(`[dsh-workbench] ${unit === "" ? "这个模块" : unit} 的卡片没挂上:${reasons.join(";")}`);
3065
+ }
3066
+ function takeBackRefusal(unit) {
3067
+ for (let at = refusals.length - 1; at >= 0; at -= 1) if (refusals[at]?.unit === unit) refusals.splice(at, 1);
3068
+ }
3069
+ function attach(entry) {
3070
+ const manifest = known.get(entry.unit);
3071
+ if (manifest === void 0) return false;
3072
+ entry.mounted = {
3073
+ manifest,
3074
+ unmount: board.register(toBoardModule(entry.face, manifest))
3075
+ };
3076
+ takeBackRefusal(entry.unit);
3077
+ return true;
3078
+ }
3079
+ function detach(entry) {
3080
+ entry.mounted?.unmount();
3081
+ entry.mounted = null;
3082
+ }
2788
3083
  return {
2789
3084
  register(face) {
2790
- const id = face.manifest.id;
2791
- if (faces.some((item) => item.manifest.id === id)) throw new Error(`套件 id 重复:${id}(id 会进持久化配置,不能撞)`);
2792
- const unregisterBoard = board.register(toBoardModule(face));
2793
- faces.push(face);
3085
+ const unit = typeof face?.unit === "string" ? face.unit.trim() : "";
3086
+ if (unit === "") {
3087
+ warnRefusal("", ["registerUnit({ unit, card }) 里的 unit 是空的 —— U1b 之前的旧形状是 registerUnit({ manifest, card });现在清单由外壳从**已装清单**读出来、按 unit 交给浏览器(ADR-0038 §2.2)"]);
3088
+ notify();
3089
+ return () => {};
3090
+ }
3091
+ if (registrations.some((entry) => entry.unit === unit)) throw new Error(`套件 id 重复:${unit}(id 会进持久化配置,不能撞)`);
3092
+ const entry = {
3093
+ face,
3094
+ unit,
3095
+ mounted: null
3096
+ };
3097
+ registrations.push(entry);
2794
3098
  manifestsCache = void 0;
3099
+ if (!attach(entry)) {
3100
+ warnRefusal(unit, [`宿主没有担保 unit「${unit}」的清单 —— 这个包要么没装进这份 profile,要么它的 dsh.workbench 没通过校验。卡片先不挂:清单只有一处真相(ADR-0038 §2.2)`]);
3101
+ options.onUnknownUnit?.(unit);
3102
+ }
2795
3103
  notify();
2796
3104
  return () => {
2797
- const at = faces.findIndex((item) => item.manifest.id === id);
3105
+ const at = registrations.indexOf(entry);
2798
3106
  if (at < 0) return;
2799
- faces.splice(at, 1);
3107
+ registrations.splice(at, 1);
3108
+ detach(entry);
3109
+ takeBackRefusal(unit);
2800
3110
  manifestsCache = void 0;
2801
- unregisterBoard();
2802
3111
  notify();
2803
3112
  };
2804
3113
  },
2805
- all: () => faces,
3114
+ all: () => registrations.map((entry) => entry.face),
2806
3115
  manifests: () => {
2807
- manifestsCache ??= faces.map((face) => face.manifest);
3116
+ manifestsCache ??= registrations.flatMap((entry) => entry.mounted === null ? [] : [entry.mounted.manifest]);
2808
3117
  return manifestsCache;
2809
3118
  },
3119
+ refusals: () => refusals,
3120
+ setManifests(manifests) {
3121
+ known = new Map(manifests.map((manifest) => [manifest.id, manifest]));
3122
+ let changed = false;
3123
+ for (const entry of registrations) {
3124
+ const next = known.get(entry.unit);
3125
+ if (entry.mounted === null) {
3126
+ if (next !== void 0 && attach(entry)) changed = true;
3127
+ continue;
3128
+ }
3129
+ if (next === void 0) {
3130
+ detach(entry);
3131
+ warnRefusal(entry.unit, [`宿主不再担保 unit「${entry.unit}」的清单(模块被卸了?)—— 这张卡片先摘下来`]);
3132
+ changed = true;
3133
+ }
3134
+ }
3135
+ if (!changed) return;
3136
+ manifestsCache = void 0;
3137
+ notify();
3138
+ },
2810
3139
  board,
2811
3140
  subscribe(listener) {
2812
3141
  listeners.add(listener);
@@ -2841,11 +3170,9 @@ window.__ModuleLoader__.load({
2841
3170
  };
2842
3171
  var manifest = {
2843
3172
  id: FIXTURE_UNIT_ID,
2844
- kind: "board",
2845
3173
  card: {
2846
3174
  titleKey: KEY.cardTitle,
2847
- order: 900,
2848
- enabledByDefault: true
3175
+ order: 900
2849
3176
  },
2850
3177
  tools: [],
2851
3178
  host: { inject: [] }
@@ -2868,6 +3195,7 @@ window.__ModuleLoader__.load({
2868
3195
  }))));
2869
3196
  }
2870
3197
  var FIXTURE_FACE = {
3198
+ unit: FIXTURE_UNIT_ID,
2871
3199
  manifest,
2872
3200
  messages,
2873
3201
  card: FixtureCard
@@ -2888,11 +3216,9 @@ window.__ModuleLoader__.load({
2888
3216
  };
2889
3217
  var crashManifest = {
2890
3218
  id: CRASH_UNIT_ID,
2891
- kind: "board",
2892
3219
  card: {
2893
3220
  titleKey: CRASH_KEY.cardTitle,
2894
- order: 899,
2895
- enabledByDefault: true
3221
+ order: 899
2896
3222
  },
2897
3223
  tools: [],
2898
3224
  host: { inject: [] }
@@ -2901,6 +3227,7 @@ window.__ModuleLoader__.load({
2901
3227
  throw new Error("夹具:这张卡片按 wbCrash=1 故意渲染失败(守「一张卡片崩溃不带走面板」)");
2902
3228
  }
2903
3229
  var CRASH_FACE = {
3230
+ unit: CRASH_UNIT_ID,
2904
3231
  manifest: crashManifest,
2905
3232
  messages: crashMessages,
2906
3233
  card: CrashingCard
@@ -2913,8 +3240,8 @@ window.__ModuleLoader__.load({
2913
3240
  return hasCard(roster) ? roster : SAMPLE_ROSTER;
2914
3241
  }
2915
3242
  function fixtureSeed(roster, all, crash = false) {
2916
- const base = hasCard(roster) ? all : [toBoardModule(FIXTURE_FACE)];
2917
- return crash ? [toBoardModule(CRASH_FACE), ...base] : base;
3243
+ const base = hasCard(roster) ? all : [toBoardModule(FIXTURE_FACE, FIXTURE_FACE.manifest)];
3244
+ return crash ? [toBoardModule(CRASH_FACE, CRASH_FACE.manifest), ...base] : base;
2918
3245
  }
2919
3246
  //#endregion
2920
3247
  //#region src/app/layouts/rightbar/market-rows.ts
@@ -2934,6 +3261,25 @@ window.__ModuleLoader__.load({
2934
3261
  unchecked: "idle",
2935
3262
  unknown: "idle"
2936
3263
  };
3264
+ function marketRowTitle(row, t) {
3265
+ return row.label ?? t(row.titleKey);
3266
+ }
3267
+ function withClientRefusals(modules, refusals) {
3268
+ if (refusals.length === 0) return modules;
3269
+ const byUnit = new Map(refusals.map((one) => [one.unit, one.reasons]));
3270
+ return modules.map((module) => {
3271
+ const reasons = byUnit.get(module.unit);
3272
+ return reasons === void 0 ? module : {
3273
+ ...module,
3274
+ clientRefused: reasons
3275
+ };
3276
+ });
3277
+ }
3278
+ function refusalReasons(host, client) {
3279
+ const reported = host ?? [];
3280
+ if (reported.length > 0) return reported;
3281
+ return [...new Set(client ?? [])];
3282
+ }
2937
3283
  function mergeTitles(known, manifests) {
2938
3284
  let next = null;
2939
3285
  for (const manifest of manifests) {
@@ -2952,11 +3298,16 @@ window.__ModuleLoader__.load({
2952
3298
  const latest = module.latest;
2953
3299
  const outdated = latest !== void 0 && latest !== module.installed;
2954
3300
  const loaded = titles.has(module.unit);
3301
+ const refused = refusalReasons(module.refused, module.clientRefused);
2955
3302
  rows.push({
2956
3303
  id: module.unit,
2957
3304
  titleKey: titles.get(module.unit) ?? known?.get(module.unit) ?? module.pkg,
2958
3305
  state: outdated ? "outdated" : "installed",
2959
- ...loaded ? {} : { hint: "panels.market.moduleRestartHint" },
3306
+ ...loaded || refused.length > 0 ? {} : { hint: "panels.market.moduleRestartHint" },
3307
+ ...refused.length === 0 ? {} : { note: {
3308
+ key: "panels.market.moduleRefused",
3309
+ params: { reason: refused.join(";") }
3310
+ } },
2960
3311
  ...module.local === true ? { local: true } : {},
2961
3312
  pkg: module.pkg,
2962
3313
  canInstall: false,
@@ -2975,6 +3326,7 @@ window.__ModuleLoader__.load({
2975
3326
  return rows;
2976
3327
  }
2977
3328
  function marketActions$1(row) {
3329
+ if (row.plugin === true) return [...row.canUpdate ? ["update-plugin"] : [], ...row.canUninstall ? ["uninstall-plugin"] : []];
2978
3330
  return [
2979
3331
  ...row.canInstall ? ["install"] : [],
2980
3332
  ...row.moduleUpdate === void 0 ? row.canUpdate ? ["update"] : [] : ["update-bundle"],
@@ -2998,7 +3350,7 @@ window.__ModuleLoader__.load({
2998
3350
  if (source.kind === "npm") return source.registryEnv;
2999
3351
  return "";
3000
3352
  }
3001
- function noteOf(state, install, manifest) {
3353
+ function noteOf$1(state, install, manifest) {
3002
3354
  if (state === "installed") return void 0;
3003
3355
  if (install === "env-missing") return {
3004
3356
  key: "panels.market.needEnv",
@@ -3057,7 +3409,7 @@ window.__ModuleLoader__.load({
3057
3409
  const capability = capabilities?.[manifest.id];
3058
3410
  const install = capability?.install;
3059
3411
  const state = capabilities === null ? "checking" : stateOf(capability);
3060
- const note = noteOf(state, install, manifest);
3412
+ const note = noteOf$1(state, install, manifest);
3061
3413
  const onMachine = isOnMachine(state);
3062
3414
  const reAdopt = capability?.released === true && install === void 0;
3063
3415
  const entry = updates[manifest.id];
@@ -3229,6 +3581,60 @@ window.__ModuleLoader__.load({
3229
3581
  };
3230
3582
  }
3231
3583
  //#endregion
3584
+ //#region src/app/layouts/rightbar/market-plugin-rows.ts
3585
+ function installedPluginKept(value) {
3586
+ if (typeof value !== "object" || value === null) return void 0;
3587
+ const kept = value.kept;
3588
+ if (typeof kept !== "object" || kept === null) return void 0;
3589
+ const kind = kept.kind;
3590
+ if (kind === "held") return { key: "panels.market.pluginKeptHeld" };
3591
+ if (kind === "unnamed") return { key: "panels.market.pluginKeptUnnamed" };
3592
+ if (kind === "unsure") return { key: "panels.market.pluginKeptUnsure" };
3593
+ if (kind !== "claimed") return void 0;
3594
+ const raw = kept.by;
3595
+ const names = Array.isArray(raw) ? raw.filter((item) => typeof item === "string") : [];
3596
+ return names.length === 0 ? { key: "panels.market.pluginKeptUnsure" } : {
3597
+ key: "panels.market.pluginKeptInUse",
3598
+ names
3599
+ };
3600
+ }
3601
+ function noteOf(plugin) {
3602
+ if (plugin.broken === true) return {
3603
+ key: "panels.market.pluginBroken",
3604
+ params: {}
3605
+ };
3606
+ if (plugin.local === true) return void 0;
3607
+ if (plugin.name !== void 0 && plugin.name !== plugin.pkg) return {
3608
+ key: "panels.market.pluginAlsoKnownAs",
3609
+ params: { pkg: plugin.pkg }
3610
+ };
3611
+ }
3612
+ function installedPluginRows(plugins) {
3613
+ return plugins.map((plugin) => {
3614
+ const name = plugin.name ?? plugin.pkg;
3615
+ const outdated = plugin.installed !== void 0 && plugin.latest !== void 0 && plugin.latest !== plugin.installed;
3616
+ const note = noteOf(plugin);
3617
+ return {
3618
+ id: `plugin:${plugin.pkg}`,
3619
+ titleKey: plugin.pkg,
3620
+ label: name,
3621
+ plugin: true,
3622
+ state: outdated ? "outdated" : "installed",
3623
+ canInstall: false,
3624
+ canUpdate: outdated,
3625
+ canUninstall: true,
3626
+ ...plugin.local === true ? { local: true } : {},
3627
+ ...plugin.broken === true ? { broken: true } : {},
3628
+ ...note === void 0 ? {} : { note },
3629
+ pkg: plugin.pkg,
3630
+ ...plugin.installed === void 0 ? {} : { version: {
3631
+ installed: plugin.installed,
3632
+ ...plugin.latest === void 0 ? {} : { latest: plugin.latest }
3633
+ } }
3634
+ };
3635
+ });
3636
+ }
3637
+ //#endregion
3232
3638
  //#region src/app/layouts/market/add-module-text.ts
3233
3639
  var GUIDE_ITEMS = [
3234
3640
  {
@@ -3347,17 +3753,23 @@ window.__ModuleLoader__.load({
3347
3753
  "not-found": "panels.market.add.problemNotFound",
3348
3754
  "not-a-package": "panels.market.add.problemNotPackage",
3349
3755
  "not-a-bundle": "panels.market.add.problemNotBundle",
3756
+ "not-a-workbench-module": "panels.market.add.problemNotWorkbenchModule",
3757
+ "not-a-module-or-plugin": "panels.market.add.problemNotModuleOrPlugin",
3350
3758
  network: "panels.market.add.problemNetwork",
3351
3759
  unknown: "panels.market.add.problemUnknown"
3352
3760
  };
3353
3761
  function problemKey(problem) {
3354
3762
  return PROBLEM_KEYS[problem] ?? PROBLEM_KEYS.unknown;
3355
3763
  }
3764
+ function heldWizardPhase(phase, classifying) {
3765
+ return classifying && (phase === "preview" || phase === "idle") ? "inspecting" : phase;
3766
+ }
3356
3767
  var FAILURE_KEYS = {
3357
3768
  network: "panels.market.add.failureNetwork",
3358
3769
  "spec-host": "panels.market.add.failureNetworkHost",
3359
3770
  "not-found": "panels.market.add.failureNotFound",
3360
3771
  "no-matching-version": "panels.market.add.failureNoMatchingVersion",
3772
+ "not-a-module-or-plugin": "panels.market.add.failureNotModuleOrPlugin",
3361
3773
  "disk-full": "panels.market.add.failureDiskFull",
3362
3774
  permission: "panels.market.add.failurePermission",
3363
3775
  "build-blocked": "panels.market.add.failureBuildBlocked",
@@ -3462,7 +3874,9 @@ window.__ModuleLoader__.load({
3462
3874
  "update-bundle": "panels.market.updating",
3463
3875
  "remove-bundle": "panels.market.uninstalling",
3464
3876
  uninstall: "panels.market.uninstalling",
3465
- "update-dependency": "panels.market.updating"
3877
+ "update-dependency": "panels.market.updating",
3878
+ "update-plugin": "panels.market.updating",
3879
+ "uninstall-plugin": "panels.market.uninstalling"
3466
3880
  };
3467
3881
  var ACTION_LABEL_KEY = {
3468
3882
  install: "panels.market.install",
@@ -3470,7 +3884,9 @@ window.__ModuleLoader__.load({
3470
3884
  "update-bundle": "panels.market.update",
3471
3885
  "remove-bundle": "panels.market.uninstall",
3472
3886
  uninstall: "panels.market.uninstall",
3473
- "update-dependency": "panels.market.update"
3887
+ "update-dependency": "panels.market.update",
3888
+ "update-plugin": "panels.market.update",
3889
+ "uninstall-plugin": "panels.market.uninstall"
3474
3890
  };
3475
3891
  //#endregion
3476
3892
  //#region src/app/layouts/rightbar/SharedSection.tsx
@@ -3532,7 +3948,11 @@ window.__ModuleLoader__.load({
3532
3948
  }
3533
3949
  //#endregion
3534
3950
  //#region src/app/layouts/rightbar/MarketView.tsx
3535
- var CONFIRM_UNINSTALL = ["uninstall", "remove-bundle"];
3951
+ var CONFIRM_UNINSTALL = [
3952
+ "uninstall",
3953
+ "remove-bundle",
3954
+ "uninstall-plugin"
3955
+ ];
3536
3956
  var CONFIRM_REMOVE = ["remove-bundle"];
3537
3957
  function MarketLine({ t, row, pending, failure, onAct }) {
3538
3958
  const running = pending !== null && pending.id === row.id ? pending : null;
@@ -3542,7 +3962,7 @@ window.__ModuleLoader__.load({
3542
3962
  return /* @__PURE__ */ h("li", { className: MarketPage_module_default.marketRow }, /* @__PURE__ */ h("span", {
3543
3963
  className: MarketPage_module_default.marketIcon,
3544
3964
  "aria-hidden": "true"
3545
- }, /* @__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", {
3965
+ }, /* @__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", {
3546
3966
  from: row.version.installed,
3547
3967
  to: row.version.latest
3548
3968
  }) : row.version.installed ?? row.version.latest ?? "")), /* @__PURE__ */ h("span", {
@@ -3567,11 +3987,14 @@ window.__ModuleLoader__.load({
3567
3987
  variant: action === "install" ? "primary" : "outline",
3568
3988
  className: danger ? "wb-dangerAction" : void 0,
3569
3989
  icon: danger ? /* @__PURE__ */ h(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineRegular, { size: 13 }) : void 0,
3570
- "aria-label": danger ? t("panels.market.uninstallLabel", { name: t(row.titleKey) }) : void 0,
3990
+ "aria-label": danger ? t("panels.market.uninstallLabel", { name: marketRowTitle(row, t) }) : void 0,
3571
3991
  disabled: locked,
3572
3992
  onClick: () => onAct(action)
3573
3993
  }, t(ACTION_LABEL_KEY[action]));
3574
- }))), 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));
3994
+ }))), row.note === void 0 ? null : /* @__PURE__ */ h("div", {
3995
+ className: MarketPage_module_default.marketNote,
3996
+ title: row.note.params.reason === void 0 ? void 0 : t("panels.market.moduleRefusedDetail", row.note.params)
3997
+ }, t(row.note.key, row.note.params)), failure === null ? null : /* @__PURE__ */ h("div", { className: MarketPage_module_default.marketFailure }, failure));
3575
3998
  }
3576
3999
  function ConfirmUninstall({ t, name, onConfirm, onCancel }) {
3577
4000
  const [acknowledged, setAcknowledged] = useState(false);
@@ -3600,7 +4023,48 @@ window.__ModuleLoader__.load({
3600
4023
  onAct: (action) => confirmActions.includes(action) ? onConfirming(row.id) : void act(row.id, action)
3601
4024
  }))));
3602
4025
  }
3603
- function MarketView({ t, manifests, capabilities, freshness, onChanged, updates, installed, installedRead, settling, mock }) {
4026
+ function MarketBody({ t, shown, pluginList, installedList, shared, pending, failed, notice, act, onConfirming }) {
4027
+ const failureOf = (id) => failed?.id === id ? failed.message : null;
4028
+ return /* @__PURE__ */ h(Fragment, null, notice === null ? null : /* @__PURE__ */ h("div", { className: MarketPage_module_default.marketNote }, notice), marketSections(shown).map((section) => /* @__PURE__ */ h(MarketSection, {
4029
+ key: section.id,
4030
+ t,
4031
+ titleKey: section.titleKey,
4032
+ rows: section.rows,
4033
+ pending,
4034
+ failureOf,
4035
+ act,
4036
+ confirmActions: CONFIRM_UNINSTALL,
4037
+ onConfirming
4038
+ })), /* @__PURE__ */ h(MarketSection, {
4039
+ t,
4040
+ titleKey: "panels.market.sectionPlugins",
4041
+ rows: pluginList,
4042
+ pending,
4043
+ failureOf,
4044
+ act,
4045
+ confirmActions: CONFIRM_UNINSTALL,
4046
+ onConfirming
4047
+ }), /* @__PURE__ */ h(MarketSection, {
4048
+ t,
4049
+ titleKey: installedList.length > 0 && installedList.every((row) => row.local === true) ? "panels.market.sectionLocal" : "panels.market.sectionUnlisted",
4050
+ rows: installedList,
4051
+ pending,
4052
+ failureOf,
4053
+ act,
4054
+ confirmActions: CONFIRM_REMOVE,
4055
+ onConfirming
4056
+ }), /* @__PURE__ */ h(PanelFoot, {
4057
+ size: "md",
4058
+ text: t("panels.market.foot")
4059
+ }), /* @__PURE__ */ h(SharedSection, {
4060
+ t,
4061
+ rows: shared,
4062
+ pending,
4063
+ failureOf,
4064
+ act
4065
+ }));
4066
+ }
4067
+ function MarketView({ t, manifests, capabilities, freshness, onChanged, updates, installed, installedRead, plugins, settling, mock }) {
3604
4068
  const [titles, setTitles] = useState(/* @__PURE__ */ new Map());
3605
4069
  useEffect(() => {
3606
4070
  if (mock === void 0) setTitles((known) => mergeTitles(known, manifests));
@@ -3617,6 +4081,7 @@ window.__ModuleLoader__.load({
3617
4081
  });
3618
4082
  const [confirming, setConfirming] = useState(mock?.confirming ?? null);
3619
4083
  const [failed, setFailed] = useState(mock?.failure ?? null);
4084
+ const [notice, setNotice] = useState(mock?.notice ?? null);
3620
4085
  const removing = pending !== null && pending.action === "remove-bundle" && failed?.id !== pending.id ? pending.id : null;
3621
4086
  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);
3622
4087
  const pendingRow = removing === null || pending === null || rows.some((row) => row.id === removing) ? void 0 : {
@@ -3630,7 +4095,12 @@ window.__ModuleLoader__.load({
3630
4095
  ...pending.version === void 0 ? {} : { version: { installed: pending.version } }
3631
4096
  };
3632
4097
  const shown = pendingRow === void 0 ? rows : [...rows, pendingRow];
3633
- const rowById = new Map([...shown, ...installedList].map((row) => [row.id, row]));
4098
+ const pluginList = installedPluginRows(mock?.plugins ?? plugins ?? []);
4099
+ const rowById = new Map([
4100
+ ...shown,
4101
+ ...installedList,
4102
+ ...pluginList
4103
+ ].map((row) => [row.id, row]));
3634
4104
  const confirmingRow = confirming === null ? void 0 : rowById.get(confirming);
3635
4105
  const act = async (id, action) => {
3636
4106
  if (mock !== void 0) return;
@@ -3644,13 +4114,15 @@ window.__ModuleLoader__.load({
3644
4114
  });
3645
4115
  setConfirming(null);
3646
4116
  setFailed(null);
4117
+ if (action !== "uninstall-plugin") setNotice(null);
3647
4118
  const dependency = action === "update-dependency";
4119
+ const byName = action === "remove-bundle" || action === "update-plugin" || action === "uninstall-plugin";
3648
4120
  const outcome = await provision.send(dependency ? {
3649
4121
  action,
3650
4122
  id
3651
- } : action === "remove-bundle" ? {
4123
+ } : byName ? {
3652
4124
  action,
3653
- name: rowById.get(id)?.pkg ?? ""
4125
+ name: row?.pkg ?? ""
3654
4126
  } : {
3655
4127
  action,
3656
4128
  unit: id
@@ -3664,6 +4136,10 @@ window.__ModuleLoader__.load({
3664
4136
  });
3665
4137
  return;
3666
4138
  }
4139
+ if (action === "uninstall-plugin") {
4140
+ const kept = installedPluginKept(outcome.value);
4141
+ setNotice(kept === void 0 ? null : t(kept.key, kept.names === void 0 ? {} : { names: kept.names.join(t("panels.market.listSeparator")) }));
4142
+ }
3667
4143
  const reason = changeFailure(changeOf(outcome.value), t);
3668
4144
  onChanged();
3669
4145
  if (reason !== void 0) setFailed({
@@ -3671,46 +4147,32 @@ window.__ModuleLoader__.load({
3671
4147
  message: t("panels.market.failed", { reason })
3672
4148
  });
3673
4149
  };
3674
- if (!(freshness.loaded && installedRead !== false) || (settling?.size ?? 0) > 0 && shown.length === 0 && installedList.length === 0) return /* @__PURE__ */ h(MarketChecking, { t });
3675
- if (shown.length === 0 && installedList.length === 0) return /* @__PURE__ */ h(MarketEmpty, { t });
3676
- return /* @__PURE__ */ h(Fragment, null, marketSections(shown).map((section) => /* @__PURE__ */ h(MarketSection, {
3677
- key: section.id,
4150
+ const settled = marketSettled(freshness, installedRead);
4151
+ const nothing = shown.length === 0 && installedList.length === 0 && pluginList.length === 0;
4152
+ if (!settled || (settling?.size ?? 0) > 0 && shown.length === 0 && installedList.length === 0) return /* @__PURE__ */ h(MarketChecking, { t });
4153
+ if (nothing) return /* @__PURE__ */ h(MarketEmpty, { t });
4154
+ return /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h(MarketBody, {
3678
4155
  t,
3679
- titleKey: section.titleKey,
3680
- rows: section.rows,
3681
- pending,
3682
- failureOf: (id) => failed?.id === id ? failed.message : null,
3683
- act,
3684
- confirmActions: CONFIRM_UNINSTALL,
3685
- onConfirming: setConfirming
3686
- })), /* @__PURE__ */ h(MarketSection, {
3687
- t,
3688
- titleKey: installedList.length > 0 && installedList.every((row) => row.local === true) ? "panels.market.sectionLocal" : "panels.market.sectionUnlisted",
3689
- rows: installedList,
4156
+ shown,
4157
+ pluginList,
4158
+ installedList,
4159
+ shared,
3690
4160
  pending,
3691
- failureOf: (id) => failed?.id === id ? failed.message : null,
4161
+ failed,
4162
+ notice,
3692
4163
  act,
3693
- confirmActions: CONFIRM_REMOVE,
3694
4164
  onConfirming: setConfirming
3695
- }), /* @__PURE__ */ h(PanelFoot, {
3696
- size: "md",
3697
- text: t("panels.market.foot")
3698
- }), /* @__PURE__ */ h(SharedSection, {
3699
- t,
3700
- rows: shared,
3701
- pending,
3702
- failureOf: (id) => failed?.id === id ? failed.message : null,
3703
- act
3704
4165
  }), /* @__PURE__ */ h(ConfirmUninstall, {
3705
4166
  key: confirming ?? "",
3706
4167
  t,
3707
- name: confirmingRow === void 0 ? null : t(confirmingRow.titleKey),
4168
+ name: confirmingRow === void 0 ? null : marketRowTitle(confirmingRow, t),
3708
4169
  onCancel: () => setConfirming(null),
3709
4170
  onConfirm: () => {
3710
4171
  const id = confirming;
3711
4172
  setConfirming(null);
3712
4173
  if (id === null) return;
3713
- act(id, rowById.get(id)?.pkg === void 0 ? "uninstall" : "remove-bundle");
4174
+ const row = rowById.get(id);
4175
+ act(id, row?.plugin === true ? "uninstall-plugin" : row?.pkg === void 0 ? "uninstall" : "remove-bundle");
3714
4176
  }
3715
4177
  }));
3716
4178
  }
@@ -3720,6 +4182,9 @@ window.__ModuleLoader__.load({
3720
4182
  var BUNDLE_INSPECT_ROUTE = "bundle-inspect";
3721
4183
  var MODULE_VERSIONS_ROUTE = "module-versions";
3722
4184
  //#endregion
4185
+ //#region src/shared/api/installed-plugins.ts
4186
+ var INSTALLED_PLUGINS_ROUTE = "panels/plugins";
4187
+ //#endregion
3723
4188
  //#region src/shared/hooks/bundle-queries.ts
3724
4189
  function readBundleRegistries() {
3725
4190
  return apiGet(BUNDLE_REGISTRIES_ROUTE);
@@ -3733,10 +4198,14 @@ window.__ModuleLoader__.load({
3733
4198
  function readModuleVersions(force = false) {
3734
4199
  return apiGet(MODULE_VERSIONS_ROUTE, force ? { force: "1" } : void 0);
3735
4200
  }
4201
+ function readInstalledPlugins(force = false) {
4202
+ return apiGet(INSTALLED_PLUGINS_ROUTE, force ? { force: "1" } : void 0);
4203
+ }
3736
4204
  //#endregion
3737
4205
  //#region src/app/layouts/market/use-module-versions.ts
3738
4206
  var state = {
3739
4207
  modules: null,
4208
+ plugins: null,
3740
4209
  read: false,
3741
4210
  checkedAt: null
3742
4211
  };
@@ -3761,10 +4230,13 @@ window.__ModuleLoader__.load({
3761
4230
  if (inFlight !== null && inFlight.force === force) return inFlight.promise;
3762
4231
  const seq = readSeq += 1;
3763
4232
  if (force) newestForceSeq = seq;
3764
- const promise = readModuleVersions(force).catch(() => null).then((response) => {
4233
+ const promise = Promise.all([readModuleVersions(force), readInstalledPlugins(force)]).catch(() => null).then((responses) => {
3765
4234
  if (!force && seq < newestForceSeq) return;
4235
+ const versions = responses?.[0];
4236
+ const plugins = responses?.[1];
3766
4237
  publish({
3767
- modules: response?.ok === true ? response.modules : state.modules,
4238
+ modules: versions?.ok === true ? versions.modules : state.modules,
4239
+ plugins: plugins?.ok === true ? plugins.plugins : state.plugins,
3768
4240
  read: true,
3769
4241
  checkedAt: Date.now()
3770
4242
  });
@@ -3796,6 +4268,7 @@ window.__ModuleLoader__.load({
3796
4268
  }, []);
3797
4269
  return {
3798
4270
  modules: value.modules,
4271
+ plugins: value.plugins,
3799
4272
  read: value.read,
3800
4273
  checkedAt: value.checkedAt,
3801
4274
  reload
@@ -4014,7 +4487,7 @@ window.__ModuleLoader__.load({
4014
4487
  })), /* @__PURE__ */ h("h2", {
4015
4488
  className: AddModule_module_default.pendingTitle,
4016
4489
  role: "status"
4017
- }, kind === "inspecting" ? t("panels.market.add.checking") : t("panels.market.add.installingTitle"))));
4490
+ }, kind === "inspecting" ? t("panels.market.add.classifying") : t("panels.market.add.installingTitle"))));
4018
4491
  }
4019
4492
  function AddModulePreview({ t, state }) {
4020
4493
  const preview = state.preview;
@@ -4032,6 +4505,7 @@ window.__ModuleLoader__.load({
4032
4505
  const restart = outcome.change.application === "restart-required";
4033
4506
  const cancelled = outcome.change.application === "cancelled";
4034
4507
  const notModule = outcome.supply.state === "not-a-module";
4508
+ const plugin = outcome.plugin;
4035
4509
  const name = outcome.module.kind === "module" ? outcome.module.unit : outcome.module.pkg;
4036
4510
  return /* @__PURE__ */ h("div", { className: AddModule_module_default.body }, /* @__PURE__ */ h("div", { className: AddModule_module_default.hero }, /* @__PURE__ */ h("span", {
4037
4511
  className: AddModule_module_default.heroIcon,
@@ -4043,7 +4517,7 @@ window.__ModuleLoader__.load({
4043
4517
  }) : /* @__PURE__ */ h(_deepseek_ai_dsh_client_ui_primitives.IconCheckCircleFillRegular, { size: 28 })), /* @__PURE__ */ h("h2", {
4044
4518
  className: AddModule_module_default.wizardTitle,
4045
4519
  role: failed ? "alert" : "status"
4046
- }, 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", {
4520
+ }, 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", {
4047
4521
  className: AddModule_module_default.resultWarn,
4048
4522
  role: "alert"
4049
4523
  }, 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)));
@@ -4114,6 +4588,7 @@ window.__ModuleLoader__.load({
4114
4588
  refused("not-found"),
4115
4589
  refused("not-a-package"),
4116
4590
  refused("not-a-bundle"),
4591
+ refused("not-a-module-or-plugin", "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上"),
4117
4592
  refused("network", "getaddrinfo ENOTFOUND registry.example.com"),
4118
4593
  refused("unknown", "pnpm 的返回码是 1"),
4119
4594
  {
@@ -4195,6 +4670,25 @@ window.__ModuleLoader__.load({
4195
4670
  spec: "@dsh-workbench/module-hello",
4196
4671
  outcome: outcome({})
4197
4672
  },
4673
+ {
4674
+ id: "done-plugin",
4675
+ label: "装好了(opencli 插件)",
4676
+ phase: "result",
4677
+ spec: "opencli-plugin-cmdb",
4678
+ outcome: {
4679
+ change: change({ target: "opencli-plugin-cmdb" }),
4680
+ module: {
4681
+ kind: "unknown",
4682
+ pkg: "opencli-plugin-cmdb"
4683
+ },
4684
+ supply: { state: "no-needs" },
4685
+ plugin: {
4686
+ kind: "opencli-plugin",
4687
+ pkg: "opencli-plugin-cmdb",
4688
+ name: "cmdb"
4689
+ }
4690
+ }
4691
+ },
4198
4692
  {
4199
4693
  id: "done-restart",
4200
4694
  label: "装好了(要重启)",
@@ -4289,6 +4783,42 @@ window.__ModuleLoader__.load({
4289
4783
  };
4290
4784
  }
4291
4785
  //#endregion
4786
+ //#region src/shared/hooks/use-visible-busy.ts
4787
+ var use_visible_busy_exports = /* @__PURE__ */ __exportAll({
4788
+ busyView: () => busyView,
4789
+ heldPhase: () => heldPhase,
4790
+ useVisibleBusy: () => useVisibleBusy
4791
+ });
4792
+ function heldPhase(phase, refreshing) {
4793
+ return refreshing && phase === "ready" ? "loading" : phase;
4794
+ }
4795
+ function busyView(active, visible) {
4796
+ return {
4797
+ busy: visible,
4798
+ settling: visible && !active
4799
+ };
4800
+ }
4801
+ function useVisibleBusy(active, floorMs) {
4802
+ const [visible, setVisible] = useState(active);
4803
+ const since = useRef(null);
4804
+ useEffect(() => {
4805
+ if (active) {
4806
+ if (since.current === null) since.current = Date.now();
4807
+ setVisible(true);
4808
+ return;
4809
+ }
4810
+ const held = holdBusy(since.current, Date.now(), floorMs);
4811
+ since.current = null;
4812
+ if (!held.visible) {
4813
+ setVisible(false);
4814
+ return;
4815
+ }
4816
+ const timer = setTimeout(() => setVisible(false), held.releaseInMs);
4817
+ return () => clearTimeout(timer);
4818
+ }, [active, floorMs]);
4819
+ return busyView(active, visible);
4820
+ }
4821
+ //#endregion
4292
4822
  //#region src/app/layouts/market/use-add-module.ts
4293
4823
  function newRequestId() {
4294
4824
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -4311,6 +4841,7 @@ window.__ModuleLoader__.load({
4311
4841
  const [failure, setFailure] = useState(null);
4312
4842
  const generationRef = useRef(0);
4313
4843
  const requestIdRef = useRef("");
4844
+ const { busy: classifying } = useVisibleBusy(phase === "inspecting", 500);
4314
4845
  const write = useHostWrite({
4315
4846
  channel: "provision",
4316
4847
  networkText: failedText
@@ -4408,7 +4939,8 @@ window.__ModuleLoader__.load({
4408
4939
  setOutcome({
4409
4940
  change: value.change,
4410
4941
  module: value.module,
4411
- supply: value.supply
4942
+ supply: value.supply,
4943
+ ...value.plugin === void 0 ? {} : { plugin: value.plugin }
4412
4944
  });
4413
4945
  setPhase("result");
4414
4946
  if (value.change.application === "applied" || value.change.application === "restart-required") onInstalled();
@@ -4453,7 +4985,7 @@ window.__ModuleLoader__.load({
4453
4985
  custom,
4454
4986
  registryOpen,
4455
4987
  guideOpen,
4456
- phase,
4988
+ phase: heldWizardPhase(phase, classifying),
4457
4989
  choices,
4458
4990
  problem,
4459
4991
  preview,
@@ -4611,10 +5143,10 @@ window.__ModuleLoader__.load({
4611
5143
  function MarketPanel({ t, units }) {
4612
5144
  const manifests = useLiveManifests(units);
4613
5145
  const { capabilities, checkedAt, checking, forceProbe, reload: reloadCapability } = useCapabilityMap(0);
4614
- const { modules, read: installedRead, reload: reloadVersions } = useModuleVersions();
5146
+ const { modules, plugins, read: installedRead, reload: reloadVersions } = useModuleVersions();
4615
5147
  const [adding, setAdding] = useState(false);
4616
5148
  const updates = useMemo(() => marketUpdatesOf(modules), [modules]);
4617
- const installed = modules ?? void 0;
5149
+ const installed = modules === null ? void 0 : withClientRefusals(modules, units.refusals());
4618
5150
  const reload = useCallback(() => {
4619
5151
  reloadCapability();
4620
5152
  reloadVersions();
@@ -4671,11 +5203,14 @@ window.__ModuleLoader__.load({
4671
5203
  });
4672
5204
  const mock = fixturePick.enabled ? fixturePick.current : void 0;
4673
5205
  const mockNonce = fixturePick.nonce;
5206
+ const shownFreshness = mock === void 0 ? freshness : mock.loaded === false ? pendingFreshness : MOCK_FRESHNESS;
5207
+ const shownRead = mock === void 0 ? installedRead : mock.installedRead ?? true;
5208
+ const busy = !marketSettled(shownFreshness, shownRead) || shownFreshness.checking;
4674
5209
  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", {
4675
5210
  type: "button",
4676
- className: `${MarketPage_module_default.marketIconButton}${checking ? ` ${MarketPage_module_default.marketIconButtonSpinning}` : ""}`,
5211
+ className: `${MarketPage_module_default.marketIconButton}${shownFreshness.checking ? ` ${MarketPage_module_default.marketIconButtonSpinning}` : ""}`,
4677
5212
  "aria-label": t("panels.market.checkUpdates"),
4678
- disabled: checking,
5213
+ disabled: busy,
4679
5214
  onClick: () => {
4680
5215
  forceProbe();
4681
5216
  reloadVersions({ force: true });
@@ -4688,6 +5223,7 @@ window.__ModuleLoader__.load({
4688
5223
  size: "sm",
4689
5224
  className: MarketPage_module_default.marketAddButton,
4690
5225
  icon: /* @__PURE__ */ h(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 13 }),
5226
+ disabled: busy,
4691
5227
  onClick: () => setAdding(true)
4692
5228
  }, t("panels.market.add.open")))), fixturePick.enabled ? /* @__PURE__ */ h(MockStrip, {
4693
5229
  mocks: marketMocks,
@@ -4698,11 +5234,12 @@ window.__ModuleLoader__.load({
4698
5234
  t,
4699
5235
  manifests,
4700
5236
  capabilities,
4701
- freshness: mock === void 0 ? freshness : mock.loaded === false ? pendingFreshness : MOCK_FRESHNESS,
5237
+ freshness: shownFreshness,
4702
5238
  onChanged: reload,
4703
5239
  updates,
4704
5240
  installed,
4705
- installedRead: mock === void 0 ? installedRead : mock.installedRead ?? true,
5241
+ installedRead: shownRead,
5242
+ plugins,
4706
5243
  settling: mock === void 0 ? settling : void 0,
4707
5244
  mock
4708
5245
  }), /* @__PURE__ */ h(AddModuleDialog, {
@@ -4758,6 +5295,41 @@ window.__ModuleLoader__.load({
4758
5295
  function assembleClientUnits(registry) {
4759
5296
  for (const face of CLIENT_UNITS) registry.register(face);
4760
5297
  }
5298
+ //#endregion
5299
+ //#region src/shared/api/unit-manifests.ts
5300
+ var UNIT_MANIFESTS_ROUTE = "unit-manifests";
5301
+ //#endregion
5302
+ //#region src/app/units/manifests.ts
5303
+ function createUnitManifestLoader(options = {}) {
5304
+ let inFlight = false;
5305
+ let queued = false;
5306
+ let watching = false;
5307
+ const subscribeFacts = options.subscribeFacts ?? subscribeFactRevision;
5308
+ const load = async (registry) => {
5309
+ if (inFlight) {
5310
+ queued = true;
5311
+ return;
5312
+ }
5313
+ inFlight = true;
5314
+ try {
5315
+ const response = await apiGet(UNIT_MANIFESTS_ROUTE).catch(() => null);
5316
+ if (response?.ok === true && Array.isArray(response.manifests)) registry.setManifests(response.manifests);
5317
+ } finally {
5318
+ inFlight = false;
5319
+ if (queued) {
5320
+ queued = false;
5321
+ load(registry);
5322
+ }
5323
+ }
5324
+ };
5325
+ return (registry) => {
5326
+ if (!watching) {
5327
+ watching = true;
5328
+ subscribeFacts(factOfRoute(UNIT_MANIFESTS_ROUTE), () => void load(registry));
5329
+ }
5330
+ load(registry);
5331
+ };
5332
+ }
4761
5333
  var BootGate_module_default = {
4762
5334
  actions: "_actions_jlw8j_1",
4763
5335
  progress: "_progress_jlw8j_2",
@@ -7435,42 +8007,6 @@ window.__ModuleLoader__.load({
7435
8007
  return { runs };
7436
8008
  }
7437
8009
  //#endregion
7438
- //#region src/shared/hooks/use-visible-busy.ts
7439
- var use_visible_busy_exports = /* @__PURE__ */ __exportAll({
7440
- busyView: () => busyView,
7441
- heldPhase: () => heldPhase,
7442
- useVisibleBusy: () => useVisibleBusy
7443
- });
7444
- function heldPhase(phase, refreshing) {
7445
- return refreshing && phase === "ready" ? "loading" : phase;
7446
- }
7447
- function busyView(active, visible) {
7448
- return {
7449
- busy: visible,
7450
- settling: visible && !active
7451
- };
7452
- }
7453
- function useVisibleBusy(active, floorMs) {
7454
- const [visible, setVisible] = useState(active);
7455
- const since = useRef(null);
7456
- useEffect(() => {
7457
- if (active) {
7458
- if (since.current === null) since.current = Date.now();
7459
- setVisible(true);
7460
- return;
7461
- }
7462
- const held = holdBusy(since.current, Date.now(), floorMs);
7463
- since.current = null;
7464
- if (!held.visible) {
7465
- setVisible(false);
7466
- return;
7467
- }
7468
- const timer = setTimeout(() => setVisible(false), held.releaseInMs);
7469
- return () => clearTimeout(timer);
7470
- }, [active, floorMs]);
7471
- return busyView(active, visible);
7472
- }
7473
- //#endregion
7474
8010
  //#region src/shared/hooks/use-unit-board.ts
7475
8011
  var use_unit_board_exports = /* @__PURE__ */ __exportAll({ useUnitBoard: () => useUnitBoard });
7476
8012
  function useUnitBoard(options) {
@@ -7684,7 +8220,7 @@ window.__ModuleLoader__.load({
7684
8220
  };
7685
8221
  function messagesOf(units) {
7686
8222
  return units.map((face) => ({
7687
- unit: face.manifest.id,
8223
+ unit: face.unit,
7688
8224
  ...face.messages === void 0 ? {} : { messages: face.messages }
7689
8225
  }));
7690
8226
  }
@@ -7725,12 +8261,14 @@ window.__ModuleLoader__.load({
7725
8261
  }
7726
8262
  }
7727
8263
  function mount(ctx) {
7728
- const units = createClientUnitRegistry();
8264
+ const loadUnitManifests = createUnitManifestLoader();
8265
+ const units = createClientUnitRegistry({ onUnknownUnit: () => loadUnitManifests(units) });
7729
8266
  ctx.provide(UNITS_SERVICE, {
7730
8267
  registerUnit: (face) => units.register(face),
7731
8268
  manifests: () => units.manifests()
7732
8269
  });
7733
8270
  assembleClientUnits(units);
8271
+ loadUnitManifests(units);
7734
8272
  const store = rightbarStore;
7735
8273
  const observable = {
7736
8274
  getSnapshot: () => store.getSnapshot(),