@dsh-workbench/shared 1.1.7 → 1.1.9

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 (239) hide show
  1. package/dist/api/blocker-table.d.ts +1 -0
  2. package/dist/api/blocker-table.js +1 -1
  3. package/dist/components/SelectField.css +0 -6
  4. package/dist/components/SelectField.d.ts +2 -3
  5. package/dist/components/SelectField.js +9 -30
  6. package/dist/components/SelectFieldNodes.d.ts +3 -1
  7. package/dist/components/SelectFieldNodes.js +2 -1
  8. package/dist/components/SelectFieldWheel.d.ts +9 -0
  9. package/dist/components/SelectFieldWheel.js +35 -0
  10. package/dist/config/constants.d.ts +4 -4
  11. package/dist/config/constants.js +4 -4
  12. package/dist/contract/capabilities.d.ts +29 -0
  13. package/dist/contract/capabilities.js +1 -0
  14. package/dist/contract/needs.d.ts +3 -3
  15. package/dist/contract/needs.js +1 -1
  16. package/dist/contract/plugin-source.d.ts +19 -0
  17. package/dist/contract/protocol/bundle-install.d.ts +3 -1
  18. package/dist/contract/protocol/bundle-install.js +3 -0
  19. package/dist/contract/protocol/market-index.d.ts +1 -0
  20. package/dist/contract/protocol/platform.d.ts +1 -1
  21. package/dist/contract/runner.d.ts +6 -0
  22. package/dist/contract/runner.js +1 -0
  23. package/dist/contract/unit.d.ts +4 -4
  24. package/dist/host/adapter-contract.d.ts +3 -0
  25. package/dist/host/adapter-contract.js +93 -0
  26. package/dist/host/adapter-set.js +6 -4
  27. package/dist/host/adapter-types.d.ts +1 -1
  28. package/dist/host/bundle-manager.d.ts +3 -0
  29. package/dist/host/bundle-manager.js +181 -0
  30. package/dist/host/cli.js +3 -1
  31. package/dist/host/config-sync.d.ts +48 -0
  32. package/dist/host/config-sync.js +342 -0
  33. package/dist/host/dsh-plugins.d.ts +5 -0
  34. package/dist/host/dsh-plugins.js +51 -0
  35. package/dist/host/extension.js +1 -1
  36. package/dist/host/flow/check.d.ts +9 -0
  37. package/dist/host/flow/check.js +179 -0
  38. package/dist/host/flow/expr.d.ts +30 -0
  39. package/dist/host/flow/expr.js +99 -0
  40. package/dist/host/flow/kit.d.ts +14 -0
  41. package/dist/host/flow/kit.js +13 -0
  42. package/dist/host/flow/run.d.ts +13 -0
  43. package/dist/host/flow/run.js +287 -0
  44. package/dist/host/flow/types.d.ts +188 -0
  45. package/dist/host/flow/types.js +1 -0
  46. package/dist/host/host-actions.d.ts +2 -0
  47. package/dist/host/host-actions.js +5 -0
  48. package/dist/host/host-kit.d.ts +19 -0
  49. package/dist/host/host-kit.js +14 -0
  50. package/dist/host/http.d.ts +2 -6
  51. package/dist/host/http.js +2 -2
  52. package/dist/host/installed-module.d.ts +23 -0
  53. package/dist/host/installed-module.js +264 -0
  54. package/dist/host/lib-install.d.ts +7 -9
  55. package/dist/host/lib-install.js +9 -12
  56. package/dist/host/lib-runners.d.ts +3 -5
  57. package/dist/host/lib-runners.js +2 -2
  58. package/dist/host/local-declaration.d.ts +7 -0
  59. package/dist/host/local-declaration.js +33 -0
  60. package/dist/host/market-index-claim.d.ts +0 -1
  61. package/dist/host/market-index-claim.js +0 -3
  62. package/dist/host/market-index-doc.d.ts +1 -45
  63. package/dist/host/market-index-parse.d.ts +9 -4
  64. package/dist/host/market-index-parse.js +17 -92
  65. package/dist/host/market-index.d.ts +5 -6
  66. package/dist/host/market-index.js +29 -87
  67. package/dist/host/module-root.d.ts +1 -0
  68. package/dist/host/module-root.js +5 -0
  69. package/dist/host/npm-registry.d.ts +10 -0
  70. package/dist/host/npm-registry.js +16 -0
  71. package/dist/host/opencli-runner.d.ts +2 -1
  72. package/dist/host/package-content.d.ts +7 -0
  73. package/dist/host/package-content.js +69 -0
  74. package/dist/host/package-kind.d.ts +13 -0
  75. package/dist/host/package-kind.js +51 -0
  76. package/dist/host/package-route.d.ts +31 -0
  77. package/dist/host/package-route.js +57 -0
  78. package/dist/host/paths.d.ts +11 -19
  79. package/dist/host/paths.js +16 -43
  80. package/dist/host/platform.js +1 -1
  81. package/dist/host/plugin-ledger.d.ts +8 -8
  82. package/dist/host/plugin-ledger.js +23 -16
  83. package/dist/host/plugin.d.ts +1 -2
  84. package/dist/host/plugin.js +4 -5
  85. package/dist/host/port-types.d.ts +42 -9
  86. package/dist/host/preflight.d.ts +3 -2
  87. package/dist/host/profile-create.d.ts +18 -0
  88. package/dist/host/profile-create.js +118 -0
  89. package/dist/host/profile-registries.d.ts +10 -0
  90. package/dist/host/profile-registries.js +150 -0
  91. package/dist/host/registry-declaration.d.ts +21 -0
  92. package/dist/host/registry-declaration.js +33 -0
  93. package/dist/host/release-probe.d.ts +34 -0
  94. package/dist/host/release-probe.js +88 -0
  95. package/dist/host/runner.d.ts +26 -0
  96. package/dist/host/runner.js +50 -0
  97. package/dist/host/shell.d.ts +0 -2
  98. package/dist/host/shell.js +0 -7
  99. package/dist/host/spec.js +1 -1
  100. package/dist/host/supply-index.d.ts +1 -0
  101. package/dist/host/supply-index.js +8 -7
  102. package/dist/host/supply-port.js +13 -3
  103. package/dist/host/unit-face.d.ts +2 -1
  104. package/dist/host/version.js +11 -6
  105. package/dist/host.d.ts +24 -6
  106. package/dist/host.js +23 -6
  107. package/dist/i18n/en.js +19 -20
  108. package/dist/i18n/index.d.ts +20 -21
  109. package/dist/i18n/runtime-terms.d.ts +15 -0
  110. package/dist/i18n/runtime-terms.js +22 -0
  111. package/dist/i18n/zh.d.ts +19 -20
  112. package/dist/i18n/zh.js +19 -20
  113. package/dist/index.d.ts +4 -0
  114. package/dist/index.js +4 -0
  115. package/dist/testing/fake-adapter-set.d.ts +5 -0
  116. package/dist/testing/fake-adapter-set.js +150 -0
  117. package/dist/testing/needs.js +1 -1
  118. package/dist/testing.d.ts +1 -0
  119. package/dist/testing.js +1 -0
  120. package/package.json +1 -1
  121. package/src/api/__tests__/blocker-table.test.ts +25 -0
  122. package/src/api/{workbench-client.test.ts → __tests__/workbench-client.test.ts} +2 -2
  123. package/src/api/blocker-table.ts +6 -2
  124. package/src/components/SelectField.css +0 -6
  125. package/src/components/SelectField.tsx +7 -36
  126. package/src/components/SelectFieldNodes.tsx +4 -1
  127. package/src/components/{Card.test.tsx → __tests__/Card.test.tsx} +2 -2
  128. package/src/components/{CardAnchor.test.tsx → __tests__/CardAnchor.test.tsx} +3 -3
  129. package/src/components/{CardHead.test.tsx → __tests__/CardHead.test.tsx} +1 -1
  130. package/src/components/{ChoiceGroup.test.tsx → __tests__/ChoiceGroup.test.tsx} +2 -2
  131. package/src/components/{ConfirmBar.test.tsx → __tests__/ConfirmBar.test.tsx} +5 -5
  132. package/src/components/{Field.test.tsx → __tests__/Field.test.tsx} +1 -1
  133. package/src/components/{Form.test.tsx → __tests__/Form.test.tsx} +5 -5
  134. package/src/components/{FormActions.test.tsx → __tests__/FormActions.test.tsx} +3 -3
  135. package/src/components/{FormRows.test.tsx → __tests__/FormRows.test.tsx} +2 -2
  136. package/src/components/{FormTypes.test.ts → __tests__/FormTypes.test.ts} +1 -1
  137. package/src/components/{FormWiring.test.tsx → __tests__/FormWiring.test.tsx} +4 -4
  138. package/src/components/{Link.test.tsx → __tests__/Link.test.tsx} +1 -1
  139. package/src/components/{Rows.test.tsx → __tests__/Rows.test.tsx} +2 -2
  140. package/src/components/{SelectField.test.tsx → __tests__/SelectField.test.tsx} +7 -37
  141. package/src/components/{Spinner.test.tsx → __tests__/Spinner.test.tsx} +4 -4
  142. package/src/config/{constants.test.ts → __tests__/constants.test.ts} +1 -1
  143. package/src/config/{storage.test.ts → __tests__/storage.test.ts} +1 -1
  144. package/src/config/constants.ts +4 -4
  145. package/src/contract/capabilities.ts +30 -0
  146. package/src/contract/needs.ts +4 -4
  147. package/src/contract/plugin-source.ts +24 -0
  148. package/src/contract/protocol/bundle-install.ts +7 -1
  149. package/src/contract/protocol/market-index.ts +1 -0
  150. package/src/contract/protocol/platform.ts +1 -1
  151. package/src/contract/runner.ts +6 -0
  152. package/src/contract/unit.ts +4 -4
  153. package/src/host/{extension.test.ts → __tests__/extension.test.ts} +4 -4
  154. package/src/host/{install-failure.test.ts → __tests__/install-failure.test.ts} +1 -1
  155. package/src/host/{installed-sources.test.ts → __tests__/installed-sources.test.ts} +1 -1
  156. package/src/host/__tests__/lib-runners.test.ts +11 -2
  157. package/src/host/__tests__/market-index-assemble.test.ts +26 -0
  158. package/src/host/__tests__/market-index.test.ts +41 -51
  159. package/src/host/__tests__/plugin-ledger.test.ts +19 -6
  160. package/src/host/{recipe.test.ts → __tests__/recipe.test.ts} +3 -3
  161. package/src/host/__tests__/runtime-layout.test.ts +14 -0
  162. package/src/host/__tests__/supply-index.test.ts +2 -2
  163. package/src/host/__tests__/supply-port.test.ts +32 -0
  164. package/src/host/adapter-contract.ts +93 -0
  165. package/src/host/adapter-set.ts +6 -4
  166. package/src/host/adapter-types.ts +1 -1
  167. package/src/host/bundle-manager.ts +195 -0
  168. package/src/host/cli.ts +3 -1
  169. package/src/host/config-sync.ts +386 -0
  170. package/src/host/dsh-plugins.ts +60 -0
  171. package/src/host/extension.ts +1 -1
  172. package/src/host/flow/__tests__/advanced.test.ts +302 -0
  173. package/src/host/flow/__tests__/check.test.ts +169 -0
  174. package/src/host/flow/__tests__/run.test.ts +301 -0
  175. package/src/host/flow/check.ts +216 -0
  176. package/src/host/flow/expr.ts +94 -0
  177. package/src/host/flow/kit.ts +30 -0
  178. package/src/host/flow/run.ts +325 -0
  179. package/src/host/flow/types.ts +199 -0
  180. package/src/host/host-actions.ts +7 -0
  181. package/src/host/host-kit.ts +26 -0
  182. package/src/host/http.ts +6 -8
  183. package/src/host/installed-module.ts +272 -0
  184. package/src/host/lib-install.ts +14 -12
  185. package/src/host/lib-runners.ts +6 -8
  186. package/src/host/local-declaration.ts +37 -0
  187. package/src/host/market-index-claim.ts +0 -4
  188. package/src/host/market-index-doc.ts +1 -53
  189. package/src/host/market-index-parse.ts +21 -85
  190. package/src/host/market-index.ts +33 -127
  191. package/src/host/module-root.ts +6 -0
  192. package/src/host/npm-registry.ts +21 -0
  193. package/src/host/package-content.ts +76 -0
  194. package/src/host/package-kind.ts +63 -0
  195. package/src/host/package-route.ts +93 -0
  196. package/src/host/paths.ts +27 -52
  197. package/src/host/platform.ts +1 -1
  198. package/src/host/plugin-ledger.ts +25 -18
  199. package/src/host/plugin.ts +4 -5
  200. package/src/host/port-types.ts +43 -9
  201. package/src/host/preflight.ts +3 -2
  202. package/src/host/profile-create.ts +133 -0
  203. package/src/host/profile-registries.ts +145 -0
  204. package/src/host/registry-declaration.ts +70 -0
  205. package/src/host/release-probe.ts +202 -0
  206. package/src/host/shell.ts +0 -9
  207. package/src/host/spec.ts +1 -1
  208. package/src/host/supply-index.ts +9 -7
  209. package/src/host/supply-port.ts +13 -3
  210. package/src/host/unit-face.ts +2 -1
  211. package/src/host/version.ts +12 -7
  212. package/src/host.ts +34 -6
  213. package/src/i18n/{index.test.ts → __tests__/index.test.ts} +3 -3
  214. package/src/i18n/{merge.test.ts → __tests__/merge.test.ts} +1 -1
  215. package/src/i18n/{zh.test.ts → __tests__/zh.test.ts} +2 -2
  216. package/src/i18n/en.ts +19 -21
  217. package/src/i18n/runtime-terms.ts +26 -0
  218. package/src/i18n/zh.ts +19 -21
  219. package/src/index.ts +4 -0
  220. package/src/lib/{board-fact.test.ts → __tests__/board-fact.test.ts} +1 -1
  221. package/src/lib/{busy.test.ts → __tests__/busy.test.ts} +1 -1
  222. package/src/lib/{form-problem.test.ts → __tests__/form-problem.test.ts} +1 -1
  223. package/src/lib/{git-address.test.ts → __tests__/git-address.test.ts} +1 -1
  224. package/src/lib/{react.test.ts → __tests__/react.test.ts} +1 -1
  225. package/src/lib/{text.test.ts → __tests__/text.test.ts} +1 -1
  226. package/src/lib/{ui.test.ts → __tests__/ui.test.ts} +1 -1
  227. package/src/testing/__tests__/fake-adapter-set.test.ts +28 -0
  228. package/src/testing/fake-adapter-set.ts +173 -0
  229. package/src/testing/needs.ts +1 -1
  230. package/src/testing.ts +1 -0
  231. package/src/host/__tests__/command-name.test.ts +0 -16
  232. package/src/host/__tests__/opencli-runner.test.ts +0 -120
  233. package/src/host/channel-plugin.ts +0 -55
  234. package/src/host/command-name.ts +0 -15
  235. package/src/host/opencli-runner.ts +0 -76
  236. package/src/host/paths.test.ts +0 -26
  237. package/src/host/plugin-source.ts +0 -137
  238. package/src/host/runtime-links.test.ts +0 -31
  239. package/src/host/runtime-links.ts +0 -23
@@ -12,6 +12,7 @@ import type { ModuleBlocker } from '../contract/blocker.js';
12
12
  export declare function blockerOfError(error: ApiError | undefined, t: Translate, context?: {
13
13
  path?: string | null;
14
14
  platform?: string;
15
+ diagnose?: Readonly<Partial<Record<'exec' | 'login' | 'bridge', string>>>;
15
16
  }): ModuleBlocker;
16
17
  /**
17
18
  * 把宿主回的环境读数翻成卡住态:探针没查过(unchecked)只算提示,不挡卡片。
@@ -74,7 +74,7 @@ export function blockerOfError(error, t, context = {}) {
74
74
  id: 'cli-exec-failed',
75
75
  severity: 'block',
76
76
  title: error.message,
77
- hint: error.hint ?? t('blocker.exec.hint'),
77
+ hint: error.hint ?? context.diagnose?.exec ?? t('blocker.exec.hint'),
78
78
  ...(error.detail === undefined ? {} : { detail: error.detail }),
79
79
  actions: [retryAction(t)],
80
80
  };
@@ -70,12 +70,6 @@
70
70
  justify-content: center;
71
71
  }
72
72
 
73
- .wb-selectReveal {
74
- display: inline-flex;
75
- align-items: center;
76
- justify-content: center;
77
- }
78
-
79
73
  .wb-selectIconOnly .wb-selectIcon {
80
74
  width: 100%;
81
75
  height: 100%;
@@ -47,7 +47,6 @@ export interface SelectFieldProps {
47
47
  readonly selection?: 'check' | 'fill' | undefined;
48
48
  readonly variant?: 'ghost' | 'boxed' | undefined;
49
49
  readonly iconOnly?: boolean | undefined;
50
- readonly revealId?: string | undefined;
51
50
  readonly ariaLabel?: string | undefined;
52
51
  readonly title?: string | undefined;
53
52
  readonly id?: string | undefined;
@@ -65,8 +64,8 @@ export declare function filterSelectOptions(options: readonly SelectFieldOption[
65
64
  export declare function selectCursorValue(shown: readonly SelectFieldOption[], cursor: number | null): string | null;
66
65
  /** 按上下键移动游标(两头环绕):还没选过时向下取第一项、向上取最后一项;列表为空回 `null`(一步都动不了)。`step` 1 向下、-1 向上。 */
67
66
  export declare function stepSelectCursor(shown: readonly SelectFieldOption[], cursor: number | null, step: 1 | -1): number | null;
68
- /** 把候选排成 `Menu` 要的条目表:按 `group` 插分组标题;一项都没有时退回 `emptyText` 那一行(不给就是空表)。`revealId` 是开列表时要滚进可视区的那个 value。 */
69
- export declare function selectEntries(shown: readonly SelectFieldOption[], emptyText: string | undefined, revealId?: string | undefined): readonly MenuEntry[];
67
+ /** 把候选排成 `Menu` 要的条目表:按 `group` 插分组标题;一项都没有时退回 `emptyText` 那一行(不给就是空表)。 */
68
+ export declare function selectEntries(shown: readonly SelectFieldOption[], emptyText: string | undefined): readonly MenuEntry[];
70
69
  /**
71
70
  * 下拉选择:默认是跟 `TextField` 一套的常规下拉框(不是原生 `<select>`),无边框那种给 `variant: 'ghost'`。
72
71
  * `open` 不给就内部自己管(两种接法下 `onOpenChange` 都会通知),`loading` 把触发器换成转圈 + `loadingText`(默认 `…`),
@@ -12,35 +12,14 @@ function mergeClass(...parts) {
12
12
  function triggerClassOf(variantClass, iconOnly) {
13
13
  return mergeClass('wb-selectTrigger', variantClass, iconOnly ? 'wb-selectIconOnly' : undefined);
14
14
  }
15
- function itemOf(option, revealId) {
16
- const mark = option.value === revealId;
17
- if (!mark) {
18
- return {
19
- id: option.value,
20
- label: option.label,
21
- ...(option.icon === undefined ? {} : { icon: option.icon }),
22
- ...(option.disabled === true ? { disabled: true } : {}),
23
- };
24
- }
25
- const revealed = (node) => (h("span", { className: "wb-selectReveal", "data-wb-select-reveal": "" }, node));
15
+ function itemOf(option) {
26
16
  return {
27
17
  id: option.value,
28
- label: option.icon === undefined ? revealed(option.label) : option.label,
29
- ...(option.icon === undefined ? {} : { icon: revealed(option.icon) }),
18
+ label: option.label,
19
+ ...(option.icon === undefined ? {} : { icon: option.icon }),
30
20
  ...(option.disabled === true ? { disabled: true } : {}),
31
21
  };
32
22
  }
33
- function revealNode() {
34
- const found = document.querySelector('[data-wb-select-reveal]');
35
- return found instanceof HTMLElement ? found : null;
36
- }
37
- function useRevealOnOpen(open, revealId) {
38
- useEffect(() => {
39
- if (!open || revealId === undefined)
40
- return;
41
- revealNode()?.scrollIntoView({ block: 'nearest' });
42
- }, [open, revealId]);
43
- }
44
23
  function selectValueNode(current, renderValue, placeholder) {
45
24
  if (current === undefined) {
46
25
  return h("span", { className: "wb-selectPlaceholder" }, placeholder ?? '—');
@@ -101,8 +80,8 @@ export function stepSelectCursor(shown, cursor, step) {
101
80
  return step === 1 ? 0 : shown.length - 1;
102
81
  return (cursor + step + shown.length) % shown.length;
103
82
  }
104
- /** 把候选排成 `Menu` 要的条目表:按 `group` 插分组标题;一项都没有时退回 `emptyText` 那一行(不给就是空表)。`revealId` 是开列表时要滚进可视区的那个 value。 */
105
- export function selectEntries(shown, emptyText, revealId) {
83
+ /** 把候选排成 `Menu` 要的条目表:按 `group` 插分组标题;一项都没有时退回 `emptyText` 那一行(不给就是空表)。 */
84
+ export function selectEntries(shown, emptyText) {
106
85
  if (shown.length === 0) {
107
86
  return emptyText === undefined ? [] : [{ type: 'label', id: EMPTY_ID, text: emptyText }];
108
87
  }
@@ -113,7 +92,7 @@ export function selectEntries(shown, emptyText, revealId) {
113
92
  out.push({ type: 'label', id: `__wb-select-group-${String(out.length)}`, text: option.group });
114
93
  group = option.group;
115
94
  }
116
- out.push(itemOf(option, revealId));
95
+ out.push(itemOf(option));
117
96
  }
118
97
  return out;
119
98
  }
@@ -193,10 +172,9 @@ function useSelectState(props) {
193
172
  * @returns 触发器,以及打开时的搜索框与列表。
194
173
  */
195
174
  export function SelectField(props) {
196
- const { value, options, onChange, label, hint, icon, renderValue, placeholder, search = true, searchLabel, searchPlaceholder, footer, onFooter, disabled = false, invalid = false, loading = false, loadingText, fallback, selection = 'fill', variant = 'boxed', iconOnly = false, revealId, ariaLabel, title, id, name, className, menuClassName, } = props;
175
+ const { value, options, onChange, label, hint, icon, renderValue, placeholder, search = true, searchLabel, searchPlaceholder, footer, onFooter, disabled = false, invalid = false, loading = false, loadingText, fallback, selection = 'fill', variant = 'boxed', iconOnly = false, ariaLabel, title, id, name, className, menuClassName, } = props;
197
176
  const state = useSelectState(props);
198
177
  const current = options.find((option) => option.value === value);
199
- useRevealOnOpen(state.open, revealId);
200
178
  const variantClass = variant === 'ghost' ? 'wb-selectGhost' : undefined;
201
179
  const triggerClass = triggerClassOf(variantClass, iconOnly);
202
180
  if (loading) {
@@ -217,6 +195,7 @@ export function SelectField(props) {
217
195
  value: selectValueNode(current, renderValue, placeholder),
218
196
  iconOnly,
219
197
  onOpen: () => state.setOpen(true),
198
+ ref: state.triggerRef,
220
199
  });
221
200
  const anchor = state.open && search
222
201
  ? searchNode({
@@ -255,7 +234,7 @@ export function SelectField(props) {
255
234
  anchor,
256
235
  selection,
257
236
  selectedId: selectCursorValue(state.shown, state.cursor) ?? undefined,
258
- items: selectEntries(state.shown, state.empty, revealId),
237
+ items: selectEntries(state.shown, state.empty),
259
238
  footer: footer === undefined
260
239
  ? undefined
261
240
  : [
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from 'react';
1
+ import type { ReactNode, Ref } from 'react';
2
2
  /**
3
3
  * 下拉触发器那颗按钮:图标 + 当前值 + 箭头,交给 `Menu` 当 anchor;宽窄两档共用一个形状。
4
4
  * @param args.id - 触发器 id。
@@ -12,6 +12,7 @@ import type { ReactNode } from 'react';
12
12
  * @param args.value - 已渲染好的当前值节点(文本或占位)。
13
13
  * @param args.iconOnly - 只画图标(窄栏那颗切换钮),值与箭头都不画。
14
14
  * @param args.onOpen - 点它做什么,通常是把列表打开。
15
+ * @param args.ref - 挂到这颗 `<button>` 上,取消后回焦点、菜单开着头条滚轮要用它。
15
16
  * @returns 一颗 `<button>`。
16
17
  */
17
18
  export declare function selectTriggerNode(args: {
@@ -26,6 +27,7 @@ export declare function selectTriggerNode(args: {
26
27
  readonly value: ReactNode;
27
28
  readonly iconOnly: boolean;
28
29
  readonly onOpen: () => void;
30
+ readonly ref: Ref<HTMLButtonElement> | undefined;
29
31
  }): ReactNode;
30
32
  /**
31
33
  * 名单还没读到时的触发器:禁用 + `aria-busy` 的按钮,`SelectField` 在 `loading` 那档画它。
@@ -14,10 +14,11 @@ import { Spinner } from './Spinner.js';
14
14
  * @param args.value - 已渲染好的当前值节点(文本或占位)。
15
15
  * @param args.iconOnly - 只画图标(窄栏那颗切换钮),值与箭头都不画。
16
16
  * @param args.onOpen - 点它做什么,通常是把列表打开。
17
+ * @param args.ref - 挂到这颗 `<button>` 上,取消后回焦点、菜单开着头条滚轮要用它。
17
18
  * @returns 一颗 `<button>`。
18
19
  */
19
20
  export function selectTriggerNode(args) {
20
- return (h("button", { type: "button", id: args.id, className: args.className, disabled: args.disabled, "aria-haspopup": "listbox", "aria-expanded": args.open, "aria-invalid": args.invalid || undefined, "aria-label": args.ariaLabel, title: args.title, onClick: args.onOpen }, args.iconOnly ? (h("span", { className: "wb-selectIcon" }, args.icon)) : (h(Fragment, null,
21
+ return (h("button", { type: "button", ref: args.ref, id: args.id, className: args.className, disabled: args.disabled, "aria-haspopup": "listbox", "aria-expanded": args.open, "aria-invalid": args.invalid || undefined, "aria-label": args.ariaLabel, title: args.title, onClick: args.onOpen }, args.iconOnly ? (h("span", { className: "wb-selectIcon" }, args.icon)) : (h(Fragment, null,
21
22
  args.icon === undefined ? null : h("span", { className: "wb-selectIcon" }, args.icon),
22
23
  h("span", { className: "wb-selectValue" }, args.value),
23
24
  h("span", { className: "wb-selectChevron" },
@@ -0,0 +1,9 @@
1
+ import type { RefObject } from 'react';
2
+ /**
3
+ * 菜单开着时,滚轮落在「头条」(搜索框 / 触发钮)上也滚列表:点开菜单后指针还停在原来那个点上,
4
+ * 第一下滚动不该没有反应。列表自己那一格照旧由 dsh 的 `Menu` 滚,这里只补头条这段死区;
5
+ * 列表短到不用滚时不接管,滚轮该给谁还给谁。
6
+ * @param open - 菜单是否开着;关着时不接管滚轮。
7
+ * @param head - 头条那个节点的 ref:`search` 那档是搜索框,窄栏(`iconOnly`)那档是触发钮。
8
+ */
9
+ export declare function useHeadWheel(open: boolean, head: RefObject<HTMLElement | null>): void;
@@ -0,0 +1,35 @@
1
+ import { useEffect } from '../lib/react.js';
2
+ const MENU_CARD = '[role="menu"].wb-selectMenu';
3
+ const LINE_HEIGHT_PX = 16;
4
+ function menuViewport() {
5
+ const cards = document.querySelectorAll(MENU_CARD);
6
+ const card = cards[cards.length - 1];
7
+ const viewport = card?.querySelector('[role="presentation"]');
8
+ return viewport instanceof HTMLElement ? viewport : null;
9
+ }
10
+ /**
11
+ * 菜单开着时,滚轮落在「头条」(搜索框 / 触发钮)上也滚列表:点开菜单后指针还停在原来那个点上,
12
+ * 第一下滚动不该没有反应。列表自己那一格照旧由 dsh 的 `Menu` 滚,这里只补头条这段死区;
13
+ * 列表短到不用滚时不接管,滚轮该给谁还给谁。
14
+ * @param open - 菜单是否开着;关着时不接管滚轮。
15
+ * @param head - 头条那个节点的 ref:`search` 那档是搜索框,窄栏(`iconOnly`)那档是触发钮。
16
+ */
17
+ export function useHeadWheel(open, head) {
18
+ useEffect(() => {
19
+ const node = head.current;
20
+ if (!open || node === null)
21
+ return undefined;
22
+ const onWheel = (event) => {
23
+ const viewport = menuViewport();
24
+ if (viewport === null || viewport.scrollHeight <= viewport.clientHeight)
25
+ return;
26
+ const step = event.deltaMode === WheelEvent.DOM_DELTA_LINE ? event.deltaY * LINE_HEIGHT_PX : event.deltaY;
27
+ viewport.scrollTop += step;
28
+ event.preventDefault();
29
+ };
30
+ node.addEventListener('wheel', onWheel, { passive: false });
31
+ return () => {
32
+ node.removeEventListener('wheel', onWheel);
33
+ };
34
+ }, [open, head]);
35
+ }
@@ -22,7 +22,7 @@ export declare const DIY_RECORD_PATH = "diy-record";
22
22
  export declare const DIY_RECORD_ACT_PATH = "diy-record/act";
23
23
  /** 面板运行态的路由前缀,挂在外壳 API 下:上报口用它,拒报回 report-refused。 */
24
24
  export declare const PANEL_STATE_PREFIX = "/workbench/api/panel-state";
25
- /** 钉住的 opencli 插件版本:`opencli-runtime` 按它校验、取那一份。 */
25
+ /** 钉住的运行时版本:隔离运行时按它校验、取那一份。 */
26
26
  export declare const OPENCLI_PIN = "1.8.8";
27
27
  /** 缺陷看板套件的 dsh 服务名(`workbenchIssues`):套件照它登记自己那份服务。 */
28
28
  export declare const ISSUES_SERVICE = "workbenchIssues";
@@ -38,11 +38,11 @@ export declare const DIY_DEBUG_SERVICE = "workbenchDiyDebug";
38
38
  export declare const DIY_RECORD_SERVICE = "workbenchDiyRecord";
39
39
  /** 会话投影服务的 dsh 服务名(`sessionProjections`):宿主经 `stateOf(session, 'agentPreset')` 读那一档。 */
40
40
  export declare const SESSION_PROJECTIONS_SERVICE = "sessionProjections";
41
- /** 面板清单一次取回的行数上限(条):外部模块拿它当 opencli `--pageSize`。 */
41
+ /** 面板清单一次取回的行数上限(条):外部模块拿它当分页参数。 */
42
42
  export declare const PANEL_LIST_LIMIT = 50;
43
- /** 单次 opencli 调用的默认超时(毫秒);调用点没传 `timeoutMs` 时按它算。 */
43
+ /** 单次运行时调用的默认超时(毫秒);调用点没传 `timeoutMs` 时按它算。 */
44
44
  export declare const OPENCLI_TIMEOUT_MS = 120000;
45
- /** 同时最多几条 opencli 在跑(条):`opencli.ts` 超出就排队。 */
45
+ /** 同时最多几条运行时调用在跑(条):超出就排队。 */
46
46
  export declare const OPENCLI_MAX_CONCURRENT = 4;
47
47
  /** 启动预热站点那一跳的超时(毫秒);超时按未热对待,不挡面板。 */
48
48
  export declare const WARM_TIMEOUT_MS = 20000;
@@ -22,7 +22,7 @@ export const DIY_RECORD_PATH = "diy-record";
22
22
  export const DIY_RECORD_ACT_PATH = `${DIY_RECORD_PATH}/act`;
23
23
  /** 面板运行态的路由前缀,挂在外壳 API 下:上报口用它,拒报回 report-refused。 */
24
24
  export const PANEL_STATE_PREFIX = `${API_PREFIX}/${PANEL_STATE_PATH}`;
25
- /** 钉住的 opencli 插件版本:`opencli-runtime` 按它校验、取那一份。 */
25
+ /** 钉住的运行时版本:隔离运行时按它校验、取那一份。 */
26
26
  export const OPENCLI_PIN = "1.8.8";
27
27
  /** 缺陷看板套件的 dsh 服务名(`workbenchIssues`):套件照它登记自己那份服务。 */
28
28
  export const ISSUES_SERVICE = "workbenchIssues";
@@ -38,11 +38,11 @@ export const DIY_DEBUG_SERVICE = "workbenchDiyDebug";
38
38
  export const DIY_RECORD_SERVICE = "workbenchDiyRecord";
39
39
  /** 会话投影服务的 dsh 服务名(`sessionProjections`):宿主经 `stateOf(session, 'agentPreset')` 读那一档。 */
40
40
  export const SESSION_PROJECTIONS_SERVICE = "sessionProjections";
41
- /** 面板清单一次取回的行数上限(条):外部模块拿它当 opencli `--pageSize`。 */
41
+ /** 面板清单一次取回的行数上限(条):外部模块拿它当分页参数。 */
42
42
  export const PANEL_LIST_LIMIT = 50;
43
- /** 单次 opencli 调用的默认超时(毫秒);调用点没传 `timeoutMs` 时按它算。 */
43
+ /** 单次运行时调用的默认超时(毫秒);调用点没传 `timeoutMs` 时按它算。 */
44
44
  export const OPENCLI_TIMEOUT_MS = 120_000;
45
- /** 同时最多几条 opencli 在跑(条):`opencli.ts` 超出就排队。 */
45
+ /** 同时最多几条运行时调用在跑(条):超出就排队。 */
46
46
  export const OPENCLI_MAX_CONCURRENT = 4;
47
47
  /** 启动预热站点那一跳的超时(毫秒);超时按未热对待,不挡面板。 */
48
48
  export const WARM_TIMEOUT_MS = 20_000;
@@ -0,0 +1,29 @@
1
+ export interface RuntimeCapabilities {
2
+ readonly console: boolean;
3
+ readonly pluginMarket: boolean;
4
+ readonly browserBridge: boolean;
5
+ }
6
+ export interface RuntimeText {
7
+ readonly zh: string;
8
+ readonly en: string;
9
+ }
10
+ export interface RuntimeTerms {
11
+ readonly plugin: RuntimeText;
12
+ readonly pluginOf: RuntimeText;
13
+ readonly pluginThing: RuntimeText;
14
+ readonly pluginArea: RuntimeText;
15
+ }
16
+ export interface RuntimeBridgeFix {
17
+ readonly text: RuntimeText;
18
+ readonly link?: {
19
+ readonly label: RuntimeText;
20
+ readonly href: string;
21
+ };
22
+ }
23
+ export interface RuntimeProfile {
24
+ readonly label: string;
25
+ readonly terms: RuntimeTerms;
26
+ readonly installHint: RuntimeText;
27
+ readonly diagnose: Readonly<Partial<Record<'exec' | 'login' | 'bridge', RuntimeText>>>;
28
+ readonly bridge?: Readonly<Partial<Record<string, RuntimeBridgeFix>>>;
29
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -64,7 +64,7 @@ export interface BinNeed extends NeedCommon {
64
64
  readonly docs?: string | undefined;
65
65
  }
66
66
  /**
67
- * 某个运行时上要有一个扩展(opencli 的插件就是这种),并且要提供这些命令。
67
+ * 某个运行时上要有一个扩展(运行时自己的插件就是这种),并且要提供这些命令。
68
68
  */
69
69
  export interface ExtensionNeed extends NeedCommon {
70
70
  readonly kind: 'extension';
@@ -76,8 +76,8 @@ export interface ExtensionNeed extends NeedCommon {
76
76
  }
77
77
  /**
78
78
  * 要在隔离运行时里有一个共用依赖包。
79
- * `runs` 写它自己会起哪些运行时的命令(如 `["opencli"]`)—— 写了外壳就把那份执行器注入进去
80
- * (库侧约定 `setOpencliRunner`);不写就按它自己在 PATH 上找。
79
+ * `runs` 写它自己会起哪个运行时的命令 —— 写了外壳就把那份执行器注入进去
80
+ * (库侧约定 `set<Runtime>Runner`);不写就按它自己在 PATH 上找。
81
81
  */
82
82
  export interface LibNeed extends NeedCommon {
83
83
  readonly kind: 'lib';
@@ -178,7 +178,7 @@ function perKindProblemsOf(kind, raw, at) {
178
178
  errors.push(...libRunsProblemsOf(raw['runs'], at));
179
179
  if (kind === 'extension') {
180
180
  if (text(raw['runtime']) === '')
181
- errors.push(`${at}.runtime 没写 —— 扩展属于哪个运行时(如 opencli)`);
181
+ errors.push(`${at}.runtime 没写 —— 扩展属于哪个运行时`);
182
182
  if (text(raw['name']) === '')
183
183
  errors.push(`${at}.name 没写`);
184
184
  if (raw['commands'] !== undefined) {
@@ -1 +1,20 @@
1
1
  export type ProvisionProblem = 'not-declared' | 'env-missing' | 'malformed';
2
+ export interface PluginSourceCommand {
3
+ readonly line: string;
4
+ readonly optional?: true;
5
+ }
6
+ export type ResolvedPluginSource = {
7
+ readonly ok: true;
8
+ readonly commands: readonly PluginSourceCommand[];
9
+ readonly line: string;
10
+ } | {
11
+ readonly ok: false;
12
+ readonly problem: ProvisionProblem;
13
+ readonly env?: string;
14
+ };
15
+ export interface PluginSourceOptions {
16
+ readonly command?: string;
17
+ readonly plugin?: string;
18
+ readonly version?: string;
19
+ readonly mode?: 'install' | 'update';
20
+ }
@@ -98,8 +98,10 @@ export interface BundleSupplyPayload {
98
98
  readonly state: BundleSupplyState;
99
99
  readonly reason?: string;
100
100
  }
101
+ export type PluginPackageKind = 'runtime-plugin';
102
+ export declare function isPluginPackageKind(kind: unknown): kind is PluginPackageKind;
101
103
  export interface BundleInstalledPlugin {
102
- readonly kind: 'opencli-plugin';
104
+ readonly kind: PluginPackageKind;
103
105
  readonly pkg: string;
104
106
  readonly name?: string;
105
107
  }
@@ -10,3 +10,6 @@ export const RESTART_REASONS = [
10
10
  'unmanaged-plugin',
11
11
  'module-comparison',
12
12
  ];
13
+ export function isPluginPackageKind(kind) {
14
+ return kind === 'runtime-plugin';
15
+ }
@@ -208,6 +208,7 @@ export interface PublishPreview {
208
208
  readonly name: string;
209
209
  readonly version: string;
210
210
  readonly author?: string;
211
+ readonly labels?: readonly string[];
211
212
  readonly registry?: string;
212
213
  readonly market?: MarketPublishDestination;
213
214
  readonly claim?: MarketClaim;
@@ -17,7 +17,7 @@ export interface ToolchainPlan {
17
17
  readonly source: 'desktop-shim' | 'path';
18
18
  };
19
19
  readonly npm: {
20
- readonly neededFor: 'opencli-install';
20
+ readonly neededFor: 'runtime-install';
21
21
  };
22
22
  readonly pnpm: {
23
23
  readonly neededFor: 'workshop';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 一条运行时命令的执行器:同一形状的 `execFile('<runtime>', args)`,结果只留 stdout。
3
+ */
4
+ export type LibRunner = (args: readonly string[]) => Promise<{
5
+ readonly stdout: string;
6
+ }>;
@@ -0,0 +1 @@
1
+ export {};
@@ -19,21 +19,21 @@ export interface UnitCard {
19
19
  readonly order: number;
20
20
  }
21
21
  /**
22
- * 合成工具背后那条 opencli 命令:插件名与命令名都是 opencli 那边的原名,`args` 是命令自己声明的参数名(按声明顺序)。
22
+ * 合成工具背后那条运行时命令:插件名与命令名都是运行时那边的原名,`args` 是命令自己声明的参数名(按声明顺序)。
23
23
  */
24
- export interface UnitToolOpencli {
24
+ export interface UnitToolRuntime {
25
25
  readonly plugin: string;
26
26
  readonly command: string;
27
27
  readonly args: readonly string[];
28
28
  }
29
29
  /**
30
30
  * 一条工具在清单里的样子:全名加会不会写;审批按 writes 认领。
31
- * `opencli` 只有「按已装插件合成」的单元写,手写模块的工具没有这一格。
31
+ * `runtime` 只有「按已装插件合成」的单元写,手写模块的工具没有这一格。
32
32
  */
33
33
  export interface UnitTool {
34
34
  readonly name: string;
35
35
  readonly writes: boolean;
36
- readonly opencli?: UnitToolOpencli;
36
+ readonly runtime?: UnitToolRuntime;
37
37
  }
38
38
  /**
39
39
  * 模块要外壳的哪些服务:inject 缺一个就挂不上,optional 缺了照挂。
@@ -0,0 +1,3 @@
1
+ import type { DshHostContext } from '../api/dsh.js';
2
+ import type { AdapterSet } from './adapters.js';
3
+ export declare function adapterContractGaps(set: AdapterSet, ctx: DshHostContext): readonly string[];
@@ -0,0 +1,93 @@
1
+ import { validateAdapterSet } from './adapter-set.js';
2
+ const CAPABILITY_KEYS = ['console', 'pluginMarket', 'browserBridge'];
3
+ const DATA_METHODS = ['read', 'request', 'requestMany', 'submit'];
4
+ function isNonEmptyString(value) {
5
+ return typeof value === 'string' && value.trim() !== '';
6
+ }
7
+ const CLI_DIR_METHODS = [
8
+ ['cliCatalogPath', 'cliCatalogPath'],
9
+ ['vendorPluginsDir', 'vendorPluginsDir'],
10
+ ];
11
+ export function adapterContractGaps(set, ctx) {
12
+ const gaps = [];
13
+ const validated = validateAdapterSet(set, '契约');
14
+ if (!validated.ok) {
15
+ gaps.push(`成员不全:${validated.detail}`);
16
+ return gaps;
17
+ }
18
+ if (!Number.isInteger(set.apiVersion) || set.apiVersion < 1) {
19
+ gaps.push(`apiVersion 不是正整数:${String(set.apiVersion)}`);
20
+ }
21
+ const capabilities = set.runtime.capabilities(ctx);
22
+ for (const key of CAPABILITY_KEYS) {
23
+ if (typeof capabilities[key] !== 'boolean')
24
+ gaps.push(`capabilities.${key} 不是布尔值`);
25
+ }
26
+ if (typeof set.runtime.profile === 'function') {
27
+ const profile = set.runtime.profile(ctx);
28
+ if (!isNonEmptyString(profile?.label))
29
+ gaps.push('runtime.profile().label 不是非空字符串');
30
+ for (const key of ['plugin', 'pluginOf', 'pluginThing', 'pluginArea']) {
31
+ const text = profile?.terms?.[key];
32
+ if (!isNonEmptyString(text?.zh) || !isNonEmptyString(text?.en)) {
33
+ gaps.push(`runtime.profile().terms.${key} 要给 zh / en 两份非空文案`);
34
+ }
35
+ }
36
+ if (!isNonEmptyString(profile?.installHint?.zh) || !isNonEmptyString(profile?.installHint?.en)) {
37
+ gaps.push('runtime.profile().installHint 要给 zh / en 两份非空文案');
38
+ }
39
+ }
40
+ if (typeof set.runtime.commandSurface === 'function') {
41
+ const surface = set.runtime.commandSurface(ctx);
42
+ if (surface !== undefined) {
43
+ if (!isNonEmptyString(surface.name))
44
+ gaps.push('runtime.commandSurface().name 不是非空字符串');
45
+ if (!isNonEmptyString(surface.pluginPrefix)) {
46
+ gaps.push('runtime.commandSurface().pluginPrefix 不是非空字符串');
47
+ }
48
+ if (typeof surface.invoke !== 'function')
49
+ gaps.push('runtime.commandSurface().invoke 不是函数');
50
+ else if (!isNonEmptyString(surface.invoke('site', 'command'))) {
51
+ gaps.push('runtime.commandSurface().invoke(site, command) 拼不出非空命令行');
52
+ }
53
+ if (typeof surface.help !== 'function')
54
+ gaps.push('runtime.commandSurface().help 不是函数');
55
+ else if (!isNonEmptyString(surface.help('site'))) {
56
+ gaps.push('runtime.commandSurface().help(site) 拼不出非空提示');
57
+ }
58
+ }
59
+ }
60
+ const health = set.runtime.health(ctx);
61
+ const commands = set.runtime.commands(ctx);
62
+ for (const [name, status] of Object.entries(health)) {
63
+ if (!isNonEmptyString(status?.command))
64
+ gaps.push(`health['${name}'].command 不是非空字符串`);
65
+ if (!['env', 'isolated', 'path'].includes(status?.source))
66
+ gaps.push(`health['${name}'].source 不是已知来源`);
67
+ }
68
+ for (const name of Object.keys(commands)) {
69
+ if (health[name] === undefined)
70
+ gaps.push(`commands 里的 ${name} 在 health 里没有对应条目`);
71
+ else if (!isNonEmptyString(commands[name]))
72
+ gaps.push(`commands['${name}'] 不是非空字符串`);
73
+ }
74
+ for (const method of DATA_METHODS) {
75
+ if (typeof set.data[method] !== 'function')
76
+ gaps.push(`data.${method} 不是函数`);
77
+ }
78
+ if (!isNonEmptyString(set.modules.isolatedRoot()))
79
+ gaps.push('modules.isolatedRoot() 不是非空字符串');
80
+ if (set.modules.runtimeInstallCommand !== undefined && !isNonEmptyString(set.modules.runtimeInstallCommand())) {
81
+ gaps.push('modules.runtimeInstallCommand() 给了但不是非空字符串');
82
+ }
83
+ for (const [name, method] of CLI_DIR_METHODS) {
84
+ const given = set.modules[method];
85
+ if (given !== undefined && !isNonEmptyString(given()))
86
+ gaps.push(`modules.${method}() 给了但不是非空字符串(${name})`);
87
+ }
88
+ if (typeof set.probe.probeUnits !== 'function')
89
+ gaps.push('probe.probeUnits 不是函数');
90
+ if (typeof set.probe.registerPreflight !== 'function')
91
+ gaps.push('probe.registerPreflight 不是函数');
92
+ return gaps;
93
+ }
@@ -26,10 +26,9 @@ const CORE_METHODS = [
26
26
  ];
27
27
  const FULL_MEMBERS = ['modules', 'runtime', 'plugins', 'actions'];
28
28
  const FULL_METHODS = [
29
+ ['modules', 'isolatedRoot'],
29
30
  ['modules', 'isolatedHome'],
30
- ['modules', 'cliCatalogPath'],
31
- ['modules', 'vendorPluginsDir'],
32
- ['modules', 'opencliInstallCommand'],
31
+ ['modules', 'afterLibInstall'],
33
32
  ['modules', 'profileDirOf'],
34
33
  ['modules', 'profileModules'],
35
34
  ['modules', 'readInstalledModule'],
@@ -46,7 +45,10 @@ const FULL_METHODS = [
46
45
  ['modules', 'isNewerVersion'],
47
46
  ['modules', 'startSelfUpdate'],
48
47
  ['runtime', 'packageRoot'],
49
- ['runtime', 'opencliCommand'],
48
+ ['runtime', 'capabilities'],
49
+ ['runtime', 'commands'],
50
+ ['runtime', 'runners'],
51
+ ['runtime', 'health'],
50
52
  ['runtime', 'seed'],
51
53
  ['runtime', 'warnUnwritableRoot'],
52
54
  ['plugins', 'declarationLookup'],
@@ -63,7 +63,7 @@ export interface SelfUpdateStart {
63
63
  readonly paths: SelfUpdatePaths;
64
64
  readonly forward: string;
65
65
  }
66
- export interface OpencliResolution {
66
+ export interface RuntimeStatus {
67
67
  readonly command: string;
68
68
  readonly source: 'env' | 'isolated' | 'path';
69
69
  readonly isolated: string | null;
@@ -0,0 +1,3 @@
1
+ import type { DshHostContext } from '../api/dsh.js';
2
+ import type { BundleManager } from './adapter-types.js';
3
+ export declare function readBundleManager(ctx: DshHostContext): BundleManager | undefined;