@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
@@ -56,7 +56,6 @@ export interface SelectFieldProps {
56
56
  readonly selection?: 'check' | 'fill' | undefined;
57
57
  readonly variant?: 'ghost' | 'boxed' | undefined;
58
58
  readonly iconOnly?: boolean | undefined;
59
- readonly revealId?: string | undefined;
60
59
  readonly ariaLabel?: string | undefined;
61
60
  readonly title?: string | undefined;
62
61
  readonly id?: string | undefined;
@@ -76,41 +75,15 @@ function triggerClassOf(variantClass: string | undefined, iconOnly: boolean): st
76
75
  return mergeClass('wb-selectTrigger', variantClass, iconOnly ? 'wb-selectIconOnly' : undefined);
77
76
  }
78
77
 
79
- function itemOf(option: SelectFieldOption, revealId: string | undefined): MenuItem {
80
- const mark = option.value === revealId;
81
- if (!mark) {
82
- return {
83
- id: option.value,
84
- label: option.label,
85
- ...(option.icon === undefined ? {} : { icon: option.icon }),
86
- ...(option.disabled === true ? { disabled: true } : {}),
87
- };
88
- }
89
- const revealed = (node: ReactNode): ReactNode => (
90
- <span className="wb-selectReveal" data-wb-select-reveal="">
91
- {node}
92
- </span>
93
- );
78
+ function itemOf(option: SelectFieldOption): MenuItem {
94
79
  return {
95
80
  id: option.value,
96
- label: option.icon === undefined ? revealed(option.label) : option.label,
97
- ...(option.icon === undefined ? {} : { icon: revealed(option.icon) }),
81
+ label: option.label,
82
+ ...(option.icon === undefined ? {} : { icon: option.icon }),
98
83
  ...(option.disabled === true ? { disabled: true } : {}),
99
84
  };
100
85
  }
101
86
 
102
- function revealNode(): HTMLElement | null {
103
- const found = document.querySelector('[data-wb-select-reveal]');
104
- return found instanceof HTMLElement ? found : null;
105
- }
106
-
107
- function useRevealOnOpen(open: boolean, revealId: string | undefined): void {
108
- useEffect(() => {
109
- if (!open || revealId === undefined) return;
110
- revealNode()?.scrollIntoView({ block: 'nearest' });
111
- }, [open, revealId]);
112
- }
113
-
114
87
  function selectValueNode(
115
88
  current: SelectFieldOption | undefined,
116
89
  renderValue: SelectFieldProps['renderValue'],
@@ -251,11 +224,10 @@ export function stepSelectCursor(
251
224
  return (cursor + step + shown.length) % shown.length;
252
225
  }
253
226
 
254
- /** 把候选排成 `Menu` 要的条目表:按 `group` 插分组标题;一项都没有时退回 `emptyText` 那一行(不给就是空表)。`revealId` 是开列表时要滚进可视区的那个 value。 */
227
+ /** 把候选排成 `Menu` 要的条目表:按 `group` 插分组标题;一项都没有时退回 `emptyText` 那一行(不给就是空表)。 */
255
228
  export function selectEntries(
256
229
  shown: readonly SelectFieldOption[],
257
230
  emptyText: string | undefined,
258
- revealId?: string | undefined,
259
231
  ): readonly MenuEntry[] {
260
232
  if (shown.length === 0) {
261
233
  return emptyText === undefined ? [] : [{ type: 'label', id: EMPTY_ID, text: emptyText }];
@@ -267,7 +239,7 @@ export function selectEntries(
267
239
  out.push({ type: 'label', id: `__wb-select-group-${String(out.length)}`, text: option.group });
268
240
  group = option.group;
269
241
  }
270
- out.push(itemOf(option, revealId));
242
+ out.push(itemOf(option));
271
243
  }
272
244
  return out;
273
245
  }
@@ -372,7 +344,6 @@ export function SelectField(props: SelectFieldProps) {
372
344
  selection = 'fill',
373
345
  variant = 'boxed',
374
346
  iconOnly = false,
375
- revealId,
376
347
  ariaLabel,
377
348
  title,
378
349
  id,
@@ -383,7 +354,6 @@ export function SelectField(props: SelectFieldProps) {
383
354
  const state = useSelectState(props);
384
355
  const current = options.find((option) => option.value === value);
385
356
 
386
- useRevealOnOpen(state.open, revealId);
387
357
  const variantClass = variant === 'ghost' ? 'wb-selectGhost' : undefined;
388
358
  const triggerClass = triggerClassOf(variantClass, iconOnly);
389
359
 
@@ -425,6 +395,7 @@ export function SelectField(props: SelectFieldProps) {
425
395
  value: selectValueNode(current, renderValue, placeholder),
426
396
  iconOnly,
427
397
  onOpen: () => state.setOpen(true),
398
+ ref: state.triggerRef,
428
399
  });
429
400
 
430
401
  const anchor =
@@ -470,7 +441,7 @@ export function SelectField(props: SelectFieldProps) {
470
441
  anchor,
471
442
  selection,
472
443
  selectedId: selectCursorValue(state.shown, state.cursor) ?? undefined,
473
- items: selectEntries(state.shown, state.empty, revealId),
444
+ items: selectEntries(state.shown, state.empty),
474
445
  footer:
475
446
  footer === undefined
476
447
  ? undefined
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from 'react';
1
+ import type { ReactNode, Ref } from 'react';
2
2
  import { Fragment, h } from '../lib/react.js';
3
3
  import {
4
4
  IconChevronDownOutlineRegular,
@@ -19,6 +19,7 @@ import { Spinner } from './Spinner.js';
19
19
  * @param args.value - 已渲染好的当前值节点(文本或占位)。
20
20
  * @param args.iconOnly - 只画图标(窄栏那颗切换钮),值与箭头都不画。
21
21
  * @param args.onOpen - 点它做什么,通常是把列表打开。
22
+ * @param args.ref - 挂到这颗 `<button>` 上,取消后回焦点、菜单开着头条滚轮要用它。
22
23
  * @returns 一颗 `<button>`。
23
24
  */
24
25
  export function selectTriggerNode(args: {
@@ -33,10 +34,12 @@ export function selectTriggerNode(args: {
33
34
  readonly value: ReactNode;
34
35
  readonly iconOnly: boolean;
35
36
  readonly onOpen: () => void;
37
+ readonly ref: Ref<HTMLButtonElement> | undefined;
36
38
  }): ReactNode {
37
39
  return (
38
40
  <button
39
41
  type="button"
42
+ ref={args.ref}
40
43
  id={args.id}
41
44
  className={args.className}
42
45
  disabled={args.disabled}
@@ -1,10 +1,10 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
2
 
3
- vi.mock('../lib/react.js', () => ({
3
+ vi.mock('../../lib/react.js', () => ({
4
4
  h: (type: unknown, props: unknown, ...children: unknown[]) => ({ type, props, children }),
5
5
  }));
6
6
 
7
- import { Card, type CardProps } from './Card.js';
7
+ import { Card, type CardProps } from '../Card.js';
8
8
 
9
9
  const t = (key: string): string => key;
10
10
 
@@ -1,12 +1,12 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
2
 
3
- vi.mock('../lib/react.js', () => ({
3
+ vi.mock('../../lib/react.js', () => ({
4
4
  h: (type: unknown, props: unknown, ...children: unknown[]) => ({ type, props, children }),
5
5
  }));
6
6
 
7
- vi.mock('./CardAnchor.module.css', () => ({ default: { anchor: 'wb-anchor' } }));
7
+ vi.mock('../CardAnchor.module.css', () => ({ default: { anchor: 'wb-anchor' } }));
8
8
 
9
- import { CardAnchor, cardIdOf } from './CardAnchor.js';
9
+ import { CardAnchor, cardIdOf } from '../CardAnchor.js';
10
10
 
11
11
  interface AnyElement {
12
12
  readonly type: unknown;
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { CardHead } from './CardHead.js';
2
+ import { CardHead } from '../CardHead.js';
3
3
 
4
4
  interface AnyElement {
5
5
  readonly type: unknown;
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { Fragment, h } from '../lib/react.js';
3
- import { ChoiceGroup } from './ChoiceGroup.js';
2
+ import { Fragment, h } from '../../lib/react.js';
3
+ import { ChoiceGroup } from '../ChoiceGroup.js';
4
4
 
5
5
  interface AnyElement {
6
6
  readonly type: unknown;
@@ -1,9 +1,9 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { describe, expect, it } from 'vitest';
3
- import { ConfirmBar, type ConfirmRow } from './ConfirmBar.js';
4
- import { FormActions, type FormAction } from './FormActions.js';
5
- import { Note } from './Note.js';
6
- import { Rows } from './Rows.js';
3
+ import { ConfirmBar, type ConfirmRow } from '../ConfirmBar.js';
4
+ import { FormActions, type FormAction } from '../FormActions.js';
5
+ import { Note } from '../Note.js';
6
+ import { Rows } from '../Rows.js';
7
7
 
8
8
  interface AnyElement {
9
9
  readonly type: unknown;
@@ -84,7 +84,7 @@ describe('确认条(契约类 wb-confirmBar)', () => {
84
84
  });
85
85
  });
86
86
 
87
- const CSS = readFileSync(new URL('./ConfirmBar.css', import.meta.url), 'utf8');
87
+ const CSS = readFileSync(new URL('../ConfirmBar.css', import.meta.url), 'utf8');
88
88
 
89
89
  function declarations(selector: string): string[] {
90
90
  for (const block of CSS.split('}')) {
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { Field } from './Field.js';
2
+ import { Field } from '../Field.js';
3
3
 
4
4
  function texts(node: unknown): string[] {
5
5
  if (node === null || node === undefined || typeof node === 'boolean') return [];
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { describe, expect, it } from 'vitest';
3
- import type { FormField, FormValue } from './FormTypes.js';
3
+ import type { FormField, FormValue } from '../FormTypes.js';
4
4
  import {
5
5
  duplicateKeysOf,
6
6
  formActionsOf,
@@ -11,9 +11,9 @@ import {
11
11
  formValuesOf,
12
12
  reportDuplicateKeys,
13
13
  visibleFieldsOf,
14
- } from './FormNodes.js';
15
- import { FormRow } from './FormRows.js';
16
- import type { FormAction } from './FormActions.js';
14
+ } from '../FormNodes.js';
15
+ import { FormRow } from '../FormRows.js';
16
+ import type { FormAction } from '../FormActions.js';
17
17
 
18
18
  function field(key: string, value: FormValue, validate?: FormField['validate']): FormField {
19
19
  return {
@@ -444,7 +444,7 @@ describe('formRowsOf', () => {
444
444
  });
445
445
  });
446
446
 
447
- const CSS = readFileSync(new URL('./Form.css', import.meta.url), 'utf8');
447
+ const CSS = readFileSync(new URL('../Form.css', import.meta.url), 'utf8');
448
448
 
449
449
  function declarations(selector: string): string[] {
450
450
  for (const block of CSS.split('}')) {
@@ -1,7 +1,7 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { describe, expect, it, vi } from 'vitest';
3
- import { FormActions, type FormAction, type FormActionKind } from './FormActions.js';
4
- import { SaveBar } from './SaveBar.js';
3
+ import { FormActions, type FormAction, type FormActionKind } from '../FormActions.js';
4
+ import { SaveBar } from '../SaveBar.js';
5
5
 
6
6
  function action(key: string, kind?: FormActionKind): FormAction {
7
7
  return { key, label: key, ...(kind === undefined ? {} : { kind }), onClick: () => {} };
@@ -90,7 +90,7 @@ describe('FormActions', () => {
90
90
  });
91
91
  });
92
92
 
93
- const CSS = readFileSync(new URL('./FormActions.css', import.meta.url), 'utf8');
93
+ const CSS = readFileSync(new URL('../FormActions.css', import.meta.url), 'utf8');
94
94
 
95
95
  function declarations(selector: string): string[] {
96
96
  for (const block of CSS.split('}')) {
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { describe, expect, it } from 'vitest';
3
- import { FormRow, FormRows } from './FormRows.js';
3
+ import { FormRow, FormRows } from '../FormRows.js';
4
4
 
5
5
  function texts(node: unknown): string[] {
6
6
  if (node === null || node === undefined || typeof node === 'boolean') return [];
@@ -111,7 +111,7 @@ describe('FormRow', () => {
111
111
  });
112
112
  });
113
113
 
114
- const FORM_CSS = readFileSync(new URL('./FormRows.css', import.meta.url), 'utf8');
114
+ const FORM_CSS = readFileSync(new URL('../FormRows.css', import.meta.url), 'utf8');
115
115
 
116
116
  function normalize(selector: string): string {
117
117
  return selector
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, expectTypeOf, it } from 'vitest';
2
- import type { FormFieldOf, FormProps } from './FormTypes.js';
2
+ import type { FormFieldOf, FormProps } from '../FormTypes.js';
3
3
 
4
4
  type Draft = { readonly env: string; readonly branch?: string };
5
5
 
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
- import type { FormField, FormProps, FormValue } from './FormTypes.js';
3
- import { Form } from './Form.js';
2
+ import type { FormField, FormProps, FormValue } from '../FormTypes.js';
3
+ import { Form } from '../Form.js';
4
4
 
5
5
  function field(key: string, value: FormValue, validate?: FormField['validate']): FormField {
6
6
  return {
@@ -13,8 +13,8 @@ function field(key: string, value: FormValue, validate?: FormField['validate']):
13
13
  };
14
14
  }
15
15
 
16
- vi.mock('../lib/react.js', async () => {
17
- const actual = await vi.importActual<Record<string, unknown>>('../lib/react.js');
16
+ vi.mock('../../lib/react.js', async () => {
17
+ const actual = await vi.importActual<Record<string, unknown>>('../../lib/react.js');
18
18
  return {
19
19
  ...actual,
20
20
  useState: (initial: unknown) => [typeof initial === 'function' ? (initial as () => unknown)() : initial, () => {}],
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { Link } from './Link.js';
2
+ import { Link } from '../Link.js';
3
3
 
4
4
  interface AnyElement {
5
5
  readonly type: unknown;
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { describe, expect, it } from 'vitest';
3
- import { Row, Rows } from './Rows.js';
3
+ import { Row, Rows } from '../Rows.js';
4
4
 
5
5
  function texts(node: unknown): string[] {
6
6
  if (node === null || node === undefined || typeof node === 'boolean') return [];
@@ -35,7 +35,7 @@ describe('Row', () => {
35
35
  });
36
36
  });
37
37
 
38
- const ROWS_CSS = readFileSync(new URL('./Rows.css', import.meta.url), 'utf8');
38
+ const ROWS_CSS = readFileSync(new URL('../Rows.css', import.meta.url), 'utf8');
39
39
  const FLEX_CONTAINER = `.wb-rows {
40
40
  display: flex;
41
41
  flex-direction: column;
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
2
 
3
3
  let states: unknown[] = [];
4
- vi.mock('../lib/react.js', () => ({
4
+ vi.mock('../../lib/react.js', () => ({
5
5
  Fragment: Symbol.for('react.fragment'),
6
6
  h: (type: unknown, props: unknown, ...children: unknown[]) => ({ type, props, children }),
7
7
  useEffect: () => {},
@@ -10,21 +10,21 @@ vi.mock('../lib/react.js', () => ({
10
10
  useState: (initial: unknown) => [states.length > 0 ? states.shift() : initial, () => {}],
11
11
  }));
12
12
 
13
- vi.mock('./SelectField.css', () => ({ default: {} }));
13
+ vi.mock('../SelectField.css', () => ({ default: {} }));
14
14
 
15
15
  import { Menu, TextShimmer } from '@deepseek-ai/dsh-client-ui-primitives';
16
- import { Spinner } from './Spinner.js';
16
+ import { Spinner } from '../Spinner.js';
17
17
  import type { MenuEntry } from '@deepseek-ai/dsh-client-ui-primitives';
18
- import { h } from '../lib/react.js';
19
- import { Field } from './Field.js';
20
- import { TextField } from './TextField.js';
18
+ import { h } from '../../lib/react.js';
19
+ import { Field } from '../Field.js';
20
+ import { TextField } from '../TextField.js';
21
21
  import {
22
22
  SelectField,
23
23
  defaultSelectFilter,
24
24
  selectCursorValue,
25
25
  selectEntries,
26
26
  stepSelectCursor,
27
- } from './SelectField.js';
27
+ } from '../SelectField.js';
28
28
 
29
29
  interface AnyElement {
30
30
  readonly type: unknown;
@@ -54,10 +54,6 @@ function elementsOf(root: unknown): readonly AnyElement[] {
54
54
  return collect(root);
55
55
  }
56
56
 
57
- function marked(nodes: readonly AnyElement[]): boolean {
58
- return nodes.some((one) => one.props?.['data-wb-select-reveal'] === '');
59
- }
60
-
61
57
  function menuOf(root: unknown): AnyElement {
62
58
  const menu = elementsOf(root).find((one) => one.type === Menu);
63
59
  if (menu === undefined) throw new Error('没有渲染出 Menu');
@@ -276,32 +272,6 @@ describe('SelectField:工作区那套下拉的公共形状', () => {
276
272
  expect(defaultSelectFilter(option, 'nope')).toBe(false);
277
273
  });
278
274
 
279
- it('★ revealId:只给那一行的图标裹上标记(开列表时靠它滚进可视区),别行一个字节不变', () => {
280
- const items = selectEntries(
281
- [
282
- { value: 'a', label: '甲', icon: <span>甲</span> },
283
- { value: 'b', label: '乙', icon: <span>乙</span> },
284
- ],
285
- undefined,
286
- 'b',
287
- );
288
- const iconOf = (id: string): unknown =>
289
- (items.find((one) => (one as { id?: string }).id === id) as { icon?: unknown } | undefined)?.icon;
290
-
291
- expect(marked(elementsOf(iconOf('a')))).toBe(false);
292
- expect(marked(elementsOf(iconOf('b')))).toBe(true);
293
- });
294
-
295
- it('★ revealId 撞上没有图标的行:退而标记它的文字(标记只落一处)', () => {
296
- const items = selectEntries([{ value: 'a', label: '甲' }], undefined, 'a');
297
- const row = items.find((one) => (one as { id?: string }).id === 'a') as
298
- | { label?: unknown; icon?: unknown }
299
- | undefined;
300
-
301
- expect(row?.icon).toBeUndefined();
302
- expect(marked(elementsOf(row?.label))).toBe(true);
303
- });
304
-
305
275
  it('条目构造:图标与禁用照传,分组标题只在换组时出现一次', () => {
306
276
  const items = selectEntries(
307
277
  [
@@ -1,13 +1,13 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
2
 
3
- vi.mock('../lib/react.js', async () => {
4
- const actual = await vi.importActual<typeof import('../lib/react.js')>('../lib/react.js');
3
+ vi.mock('../../lib/react.js', async () => {
4
+ const actual = await vi.importActual<typeof import('../../lib/react.js')>('../../lib/react.js');
5
5
  return { ...actual, h: (type: unknown, props?: unknown, ...children: unknown[]) => ({ type, props: props ?? {}, children }) };
6
6
  });
7
- vi.mock('./Spinner.css', () => ({ default: {} }));
7
+ vi.mock('../Spinner.css', () => ({ default: {} }));
8
8
 
9
9
  import { StateDot } from '@deepseek-ai/dsh-client-ui-primitives';
10
- import { Spinner } from './Spinner.js';
10
+ import { Spinner } from '../Spinner.js';
11
11
 
12
12
  describe('Spinner(loading 那颗)', () => {
13
13
  it('画的是 dsh 的 ongoing 状态点(视觉只有这一处实现)', () => {
@@ -8,7 +8,7 @@ import {
8
8
  PLUGIN_ID,
9
9
  STORAGE_KEY_PROJECT,
10
10
  STORAGE_KEY_RIGHTBAR,
11
- } from './constants.js';
11
+ } from '../constants.js';
12
12
 
13
13
  describe('constants', () => {
14
14
  it('API_PREFIX 是合法前缀:以 / 开头、不以 / 结尾、不带查询符', () => {
@@ -1,5 +1,5 @@
1
1
  import { afterEach, describe, expect, it, vi } from 'vitest';
2
- import { readText, writeText } from './storage.js';
2
+ import { readText, writeText } from '../storage.js';
3
3
 
4
4
  function fakeStorage(options: { throwOnWrite?: boolean } = {}) {
5
5
  const data = new Map<string, string>();
@@ -31,7 +31,7 @@ export const DIY_RECORD_ACT_PATH = `${DIY_RECORD_PATH}/act`;
31
31
  /** 面板运行态的路由前缀,挂在外壳 API 下:上报口用它,拒报回 report-refused。 */
32
32
  export const PANEL_STATE_PREFIX = `${API_PREFIX}/${PANEL_STATE_PATH}`;
33
33
 
34
- /** 钉住的 opencli 插件版本:`opencli-runtime` 按它校验、取那一份。 */
34
+ /** 钉住的运行时版本:隔离运行时按它校验、取那一份。 */
35
35
  export const OPENCLI_PIN = "1.8.8";
36
36
 
37
37
  /** 缺陷看板套件的 dsh 服务名(`workbenchIssues`):套件照它登记自己那份服务。 */
@@ -55,13 +55,13 @@ export const DIY_RECORD_SERVICE = "workbenchDiyRecord";
55
55
  /** 会话投影服务的 dsh 服务名(`sessionProjections`):宿主经 `stateOf(session, 'agentPreset')` 读那一档。 */
56
56
  export const SESSION_PROJECTIONS_SERVICE = "sessionProjections";
57
57
 
58
- /** 面板清单一次取回的行数上限(条):外部模块拿它当 opencli `--pageSize`。 */
58
+ /** 面板清单一次取回的行数上限(条):外部模块拿它当分页参数。 */
59
59
  export const PANEL_LIST_LIMIT = 50;
60
60
 
61
- /** 单次 opencli 调用的默认超时(毫秒);调用点没传 `timeoutMs` 时按它算。 */
61
+ /** 单次运行时调用的默认超时(毫秒);调用点没传 `timeoutMs` 时按它算。 */
62
62
  export const OPENCLI_TIMEOUT_MS = 120_000;
63
63
 
64
- /** 同时最多几条 opencli 在跑(条):`opencli.ts` 超出就排队。 */
64
+ /** 同时最多几条运行时调用在跑(条):超出就排队。 */
65
65
  export const OPENCLI_MAX_CONCURRENT = 4;
66
66
 
67
67
  /** 启动预热站点那一跳的超时(毫秒);超时按未热对待,不挡面板。 */
@@ -0,0 +1,30 @@
1
+ export interface RuntimeCapabilities {
2
+ readonly console: boolean;
3
+ readonly pluginMarket: boolean;
4
+ readonly browserBridge: boolean;
5
+ }
6
+
7
+ export interface RuntimeText {
8
+ readonly zh: string;
9
+ readonly en: string;
10
+ }
11
+
12
+ export interface RuntimeTerms {
13
+ readonly plugin: RuntimeText;
14
+ readonly pluginOf: RuntimeText;
15
+ readonly pluginThing: RuntimeText;
16
+ readonly pluginArea: RuntimeText;
17
+ }
18
+
19
+ export interface RuntimeBridgeFix {
20
+ readonly text: RuntimeText;
21
+ readonly link?: { readonly label: RuntimeText; readonly href: string };
22
+ }
23
+
24
+ export interface RuntimeProfile {
25
+ readonly label: string;
26
+ readonly terms: RuntimeTerms;
27
+ readonly installHint: RuntimeText;
28
+ readonly diagnose: Readonly<Partial<Record<'exec' | 'login' | 'bridge', RuntimeText>>>;
29
+ readonly bridge?: Readonly<Partial<Record<string, RuntimeBridgeFix>>>;
30
+ }
@@ -69,7 +69,7 @@ export interface BinNeed extends NeedCommon {
69
69
  }
70
70
 
71
71
  /**
72
- * 某个运行时上要有一个扩展(opencli 的插件就是这种),并且要提供这些命令。
72
+ * 某个运行时上要有一个扩展(运行时自己的插件就是这种),并且要提供这些命令。
73
73
  */
74
74
  export interface ExtensionNeed extends NeedCommon {
75
75
  readonly kind: 'extension';
@@ -82,8 +82,8 @@ export interface ExtensionNeed extends NeedCommon {
82
82
 
83
83
  /**
84
84
  * 要在隔离运行时里有一个共用依赖包。
85
- * `runs` 写它自己会起哪些运行时的命令(如 `["opencli"]`)—— 写了外壳就把那份执行器注入进去
86
- * (库侧约定 `setOpencliRunner`);不写就按它自己在 PATH 上找。
85
+ * `runs` 写它自己会起哪个运行时的命令 —— 写了外壳就把那份执行器注入进去
86
+ * (库侧约定 `set<Runtime>Runner`);不写就按它自己在 PATH 上找。
87
87
  */
88
88
  export interface LibNeed extends NeedCommon {
89
89
  readonly kind: 'lib';
@@ -294,7 +294,7 @@ function perKindProblemsOf(
294
294
  if (raw['docs'] !== undefined && text(raw['docs']) === '') errors.push(`${at}.docs 是空串`);
295
295
  if (kind === 'lib') errors.push(...libRunsProblemsOf(raw['runs'], at));
296
296
  if (kind === 'extension') {
297
- if (text(raw['runtime']) === '') errors.push(`${at}.runtime 没写 —— 扩展属于哪个运行时(如 opencli)`);
297
+ if (text(raw['runtime']) === '') errors.push(`${at}.runtime 没写 —— 扩展属于哪个运行时`);
298
298
  if (text(raw['name']) === '') errors.push(`${at}.name 没写`);
299
299
  if (raw['commands'] !== undefined) {
300
300
  if (!Array.isArray(raw['commands'])) errors.push(`${at}.commands 要是数组`);
@@ -1 +1,25 @@
1
1
  export type ProvisionProblem = 'not-declared' | 'env-missing' | 'malformed';
2
+
3
+ export interface PluginSourceCommand {
4
+ readonly line: string;
5
+ readonly optional?: true;
6
+ }
7
+
8
+ export type ResolvedPluginSource =
9
+ | {
10
+ readonly ok: true;
11
+ readonly commands: readonly PluginSourceCommand[];
12
+ readonly line: string;
13
+ }
14
+ | {
15
+ readonly ok: false;
16
+ readonly problem: ProvisionProblem;
17
+ readonly env?: string;
18
+ };
19
+
20
+ export interface PluginSourceOptions {
21
+ readonly command?: string;
22
+ readonly plugin?: string;
23
+ readonly version?: string;
24
+ readonly mode?: 'install' | 'update';
25
+ }
@@ -155,8 +155,14 @@ export interface BundleSupplyPayload {
155
155
  readonly reason?: string;
156
156
  }
157
157
 
158
+ export type PluginPackageKind = 'runtime-plugin';
159
+
160
+ export function isPluginPackageKind(kind: unknown): kind is PluginPackageKind {
161
+ return kind === 'runtime-plugin';
162
+ }
163
+
158
164
  export interface BundleInstalledPlugin {
159
- readonly kind: 'opencli-plugin';
165
+ readonly kind: PluginPackageKind;
160
166
  readonly pkg: string;
161
167
  readonly name?: string;
162
168
  }
@@ -263,6 +263,7 @@ export interface PublishPreview {
263
263
  readonly name: string;
264
264
  readonly version: string;
265
265
  readonly author?: string;
266
+ readonly labels?: readonly string[];
266
267
  readonly registry?: string;
267
268
  readonly market?: MarketPublishDestination;
268
269
  readonly claim?: MarketClaim;
@@ -21,7 +21,7 @@ export interface HostCapabilities {
21
21
 
22
22
  export interface ToolchainPlan {
23
23
  readonly node: { readonly source: 'desktop-shim' | 'path' };
24
- readonly npm: { readonly neededFor: 'opencli-install' };
24
+ readonly npm: { readonly neededFor: 'runtime-install' };
25
25
  readonly pnpm: { readonly neededFor: 'workshop'; readonly expectedMajor: 10 };
26
26
  }
27
27
 
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 一条运行时命令的执行器:同一形状的 `execFile('<runtime>', args)`,结果只留 stdout。
3
+ */
4
+ export type LibRunner = (
5
+ args: readonly string[],
6
+ ) => Promise<{ readonly stdout: string }>;
@@ -25,9 +25,9 @@ export interface UnitCard {
25
25
  }
26
26
 
27
27
  /**
28
- * 合成工具背后那条 opencli 命令:插件名与命令名都是 opencli 那边的原名,`args` 是命令自己声明的参数名(按声明顺序)。
28
+ * 合成工具背后那条运行时命令:插件名与命令名都是运行时那边的原名,`args` 是命令自己声明的参数名(按声明顺序)。
29
29
  */
30
- export interface UnitToolOpencli {
30
+ export interface UnitToolRuntime {
31
31
  readonly plugin: string;
32
32
  readonly command: string;
33
33
  readonly args: readonly string[];
@@ -35,12 +35,12 @@ export interface UnitToolOpencli {
35
35
 
36
36
  /**
37
37
  * 一条工具在清单里的样子:全名加会不会写;审批按 writes 认领。
38
- * `opencli` 只有「按已装插件合成」的单元写,手写模块的工具没有这一格。
38
+ * `runtime` 只有「按已装插件合成」的单元写,手写模块的工具没有这一格。
39
39
  */
40
40
  export interface UnitTool {
41
41
  readonly name: string;
42
42
  readonly writes: boolean;
43
- readonly opencli?: UnitToolOpencli;
43
+ readonly runtime?: UnitToolRuntime;
44
44
  }
45
45
 
46
46
  /**