@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
@@ -0,0 +1,202 @@
1
+ import type { DshShell } from '../api/dsh.js';
2
+ import type { PluginRelease } from '../contract/unit.js';
3
+
4
+ import { RELEASE_PACKAGES } from './release-packages.generated.js';
5
+ import { VERSION_CHECK_TIMEOUT_MS, VERSION_CHECK_TTL_MS } from '../config/constants.js';
6
+ import { isValidVersion } from './version.js';
7
+ import { parseJsonLoose } from '../lib/text.js';
8
+ import { quoteShellArg } from './shell-text.js';
9
+ import { runShell } from './shell.js';
10
+
11
+ function cacheFlag(cacheDir: string | undefined): string {
12
+ return cacheDir === undefined ? '' : ` --cache ${quoteShellArg(cacheDir)}`;
13
+ }
14
+
15
+ export function parseLedgerVersion(
16
+ stdout: string,
17
+ name: string,
18
+ ): string | undefined {
19
+ const parsed = parseJsonLoose(stdout);
20
+ if (typeof parsed !== "object" || parsed === null) return undefined;
21
+ const entry = (parsed as { dependencies?: Record<string, unknown> })
22
+ .dependencies?.[name];
23
+ if (typeof entry !== "object" || entry === null) return undefined;
24
+ const version = (entry as { version?: unknown }).version;
25
+ return typeof version === "string" && isValidVersion(version.trim())
26
+ ? version.trim()
27
+ : undefined;
28
+ }
29
+
30
+ function parseVersion(stdout: string): string | undefined {
31
+ const value = stdout.trim();
32
+ return isValidVersion(value) ? value : undefined;
33
+ }
34
+
35
+ interface CacheEntry {
36
+ readonly at: number;
37
+ readonly value: string | undefined;
38
+ }
39
+
40
+
41
+ export interface ReleaseProbe {
42
+ latestVersion(options: LatestVersionOptions): Promise<string | undefined>;
43
+ missingReleaseParts(options: MissingPartsOptions): Promise<readonly string[]>;
44
+ probeRelease(options: ProbeReleaseOptions): Promise<PluginRelease | undefined>;
45
+ reset(): void;
46
+ }
47
+
48
+ export function createReleaseProbe(): ReleaseProbe {
49
+ const latestCache = new Map<string, CacheEntry>();
50
+ const existsCache = new Map<string, ExistsEntry>();
51
+
52
+ return {
53
+ latestVersion: async (options) => latestVersionOf(latestCache, options),
54
+ missingReleaseParts: async (options) => missingPartsOf(latestCache, existsCache, options),
55
+ probeRelease: async (options) => probeOf(latestCache, options),
56
+ reset: () => {
57
+ latestCache.clear();
58
+ existsCache.clear();
59
+ },
60
+ };
61
+ }
62
+
63
+ interface LatestVersionOptions {
64
+ readonly shell: DshShell;
65
+ readonly name: string;
66
+ readonly registry: string;
67
+ readonly force?: boolean;
68
+ readonly cacheDir?: string | undefined;
69
+ }
70
+
71
+ interface MissingPartsOptions {
72
+ readonly shell: DshShell;
73
+ readonly name: string;
74
+ readonly registry: string;
75
+ readonly version: string;
76
+ readonly force?: boolean;
77
+ readonly cacheDir?: string | undefined;
78
+ }
79
+
80
+ interface ProbeReleaseOptions {
81
+ readonly shell: DshShell;
82
+ readonly package: string;
83
+ readonly registry?: string | undefined;
84
+ readonly force?: boolean;
85
+ readonly cacheDir?: string | undefined;
86
+ readonly prefix?: string | undefined;
87
+ }
88
+
89
+ async function latestVersionOf(latestCache: Map<string, CacheEntry>, options: {
90
+ shell: DshShell;
91
+ name: string;
92
+ registry: string;
93
+ force?: boolean;
94
+ cacheDir?: string | undefined;
95
+ }): Promise<string | undefined> {
96
+ const { shell, name, registry, force = false, cacheDir } = options;
97
+ const key = `${registry}|${name}`;
98
+ const hit = latestCache.get(key);
99
+ if (!force && hit !== undefined && Date.now() - hit.at < VERSION_CHECK_TTL_MS)
100
+ return hit.value;
101
+
102
+ const result = await runShell(
103
+ shell,
104
+ `npm view ${quoteShellArg(name)} version --registry ${quoteShellArg(registry)}${cacheFlag(cacheDir)}`,
105
+ { timeoutMs: VERSION_CHECK_TIMEOUT_MS },
106
+ ).catch(() => undefined);
107
+ const read = result?.exitCode === 0 ? parseVersion(result.stdout) : undefined;
108
+ latestCache.set(key, { at: Date.now(), value: read });
109
+ return read;
110
+ }
111
+
112
+ interface ExistsEntry {
113
+ readonly at: number;
114
+ readonly value: boolean;
115
+ }
116
+
117
+
118
+ async function versionExists(
119
+ existsCache: Map<string, ExistsEntry>,
120
+ options: {
121
+ shell: DshShell;
122
+ name: string;
123
+ version: string;
124
+ registry: string;
125
+ force?: boolean;
126
+ cacheDir?: string | undefined;
127
+ }): Promise<boolean> {
128
+ const { shell, name, version, registry, force = false, cacheDir } = options;
129
+ const key = `${registry}|${name}@${version}`;
130
+ const hit = existsCache.get(key);
131
+ if (!force && hit !== undefined && (hit.value || Date.now() - hit.at < VERSION_CHECK_TTL_MS))
132
+ return hit.value;
133
+
134
+ const result = await runShell(
135
+ shell,
136
+ `npm view ${quoteShellArg(`${name}@${version}`)} version --registry ${quoteShellArg(registry)}${cacheFlag(cacheDir)}`,
137
+ { timeoutMs: VERSION_CHECK_TIMEOUT_MS },
138
+ ).catch(() => undefined);
139
+ const value = result?.exitCode === 0 && parseVersion(result.stdout) !== undefined;
140
+ existsCache.set(key, { at: Date.now(), value });
141
+ return value;
142
+ }
143
+
144
+ async function missingPartsOf(
145
+ latestCache: Map<string, CacheEntry>,
146
+ existsCache: Map<string, ExistsEntry>,
147
+ options: {
148
+ shell: DshShell;
149
+ name: string;
150
+ registry: string;
151
+ version: string;
152
+ force?: boolean;
153
+ cacheDir?: string | undefined;
154
+ }): Promise<readonly string[]> {
155
+ const { shell, name, registry, version, force = false, cacheDir } = options;
156
+ const checked = await Promise.all(
157
+ RELEASE_PACKAGES.filter((part) => part !== name).map(async (part) => ({
158
+ part,
159
+ present: await versionExists(existsCache, { shell, name: part, version, registry, force, cacheDir }),
160
+ })),
161
+ );
162
+ return checked.filter((entry) => !entry.present).map((entry) => entry.part);
163
+ }
164
+
165
+ async function probeOf(
166
+ latestCache: Map<string, CacheEntry>,
167
+ options: {
168
+ shell: DshShell;
169
+ package: string;
170
+ registry?: string | undefined;
171
+ force?: boolean;
172
+ cacheDir?: string | undefined;
173
+ prefix?: string | undefined;
174
+ }): Promise<PluginRelease | undefined> {
175
+ const { shell, package: pkg, registry, force = false, cacheDir, prefix } = options;
176
+ if (pkg === "") return undefined;
177
+
178
+ const ledger = await runShell(
179
+ shell,
180
+ `npm ls --prefix ${quoteShellArg(prefix ?? '')} ${quoteShellArg(pkg)} --json --depth=0${cacheFlag(cacheDir)}`,
181
+ { timeoutMs: VERSION_CHECK_TIMEOUT_MS },
182
+ ).catch(() => undefined);
183
+ const installed =
184
+ ledger === undefined ? undefined : parseLedgerVersion(ledger.stdout, pkg);
185
+ if (installed === undefined) return undefined;
186
+
187
+ const source =
188
+ registry === undefined || registry.trim() === ""
189
+ ? undefined
190
+ : registry.trim();
191
+ const latest =
192
+ source === undefined
193
+ ? undefined
194
+ : await latestVersionOf(latestCache, { shell, name: pkg, registry: source, force, cacheDir });
195
+
196
+ return {
197
+ installed,
198
+ ...(latest === undefined ? {} : { latest }),
199
+ };
200
+ }
201
+
202
+
package/src/host/shell.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import type { DshShell } from '../api/dsh.js';
2
2
  import { GIT_TIMEOUT_MS } from '../config/constants.js';
3
- import { quoteShellArg } from './shell-text.js';
4
3
 
5
4
  export interface ShellResult {
6
5
  readonly exitCode: number | null;
@@ -41,11 +40,3 @@ export async function runShell(
41
40
  truncated: result.stdout.truncated || result.stderr.truncated,
42
41
  };
43
42
  }
44
-
45
- export function backgroundPrefix(): readonly string[] {
46
- return [quoteShellArg('env'), quoteShellArg('OPENCLI_WINDOW=background')];
47
- }
48
-
49
- export function backgroundLine(command: string, argv: readonly string[]): string {
50
- return [...backgroundPrefix(), quoteShellArg(command), ...argv.map(quoteShellArg)].join(' ');
51
- }
package/src/host/spec.ts CHANGED
@@ -28,5 +28,5 @@ export function localDirOf(spec: string): string | undefined {
28
28
  }
29
29
 
30
30
  export function refusalReason(): string {
31
- return '这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上';
31
+ return '这个包既不是工作台的模块,也不是运行时的插件,装了也用不上';
32
32
  }
@@ -1,5 +1,5 @@
1
1
  import type { ExtensionNeed, LibNeed, Need, SupplySource } from '../contract/needs.js';
2
- import { npmRegistryOf } from './plugin-source.js';
2
+ import { npmRegistryOf } from './npm-registry.js';
3
3
 
4
4
  export interface SupplyDeclaration {
5
5
  readonly id: string;
@@ -8,6 +8,7 @@ export interface SupplyDeclaration {
8
8
 
9
9
  export interface SupplyIndexOptions {
10
10
  readonly env?: Readonly<Record<string, string | undefined>>;
11
+ readonly packagePrefix?: string | undefined;
11
12
  }
12
13
 
13
14
  export interface SupplyEntry {
@@ -61,8 +62,8 @@ function text(value: string | undefined): string {
61
62
  return value === undefined ? '' : value.trim();
62
63
  }
63
64
 
64
- function conventionPackage(name: string): string {
65
- return `opencli-plugin-${name}`;
65
+ function conventionPackage(name: string, prefix: string): string {
66
+ return `${prefix}${name}`;
66
67
  }
67
68
 
68
69
  function nativeSourcePackage(source: SupplySource): string {
@@ -71,13 +72,13 @@ function nativeSourcePackage(source: SupplySource): string {
71
72
  return `local:${text(source.env) || text(source.path)}`;
72
73
  }
73
74
 
74
- function packageOf(need: ExtensionNeed | LibNeed): string {
75
+ function packageOf(need: ExtensionNeed | LibNeed, prefix: string): string {
75
76
  if (need.kind === 'lib') return text(need.name);
76
77
  const first = need.sources?.[0];
77
- if (first === undefined) return conventionPackage(need.name);
78
+ if (first === undefined) return conventionPackage(need.name, prefix);
78
79
  if (first.kind === 'npm') {
79
80
  const pkg = text(first.package);
80
- return pkg === '' ? conventionPackage(need.name) : pkg;
81
+ return pkg === '' ? conventionPackage(need.name, prefix) : pkg;
81
82
  }
82
83
  return nativeSourcePackage(first);
83
84
  }
@@ -180,6 +181,7 @@ export function buildSupplyIndex(
180
181
  options: SupplyIndexOptions = {},
181
182
  ): SupplyIndex {
182
183
  const env = options.env ?? {};
184
+ const prefix = options.packagePrefix ?? '';
183
185
  const entries = new Map<string, MutableEntry>();
184
186
  const byName = new Map<string, MutableEntry>();
185
187
  const conflicts: SupplyConflict[] = [];
@@ -206,7 +208,7 @@ export function buildSupplyIndex(
206
208
  for (const declaration of declarations) {
207
209
  for (const need of declaration.needs ?? []) {
208
210
  if (need.kind !== 'extension' && need.kind !== 'lib') continue;
209
- const pkg = packageOf(need);
211
+ const pkg = packageOf(need, prefix);
210
212
  const entry = ensure(need.kind, pkg);
211
213
  if (!entry.names.includes(need.name)) entry.names.push(need.name);
212
214
  if (need.kind === 'extension') {
@@ -8,20 +8,30 @@ export function supplyPortOf(ctx: DshHostContext): SupplyPort {
8
8
  const set = adaptersOf(ctx);
9
9
  const env = set.supply;
10
10
  const modules = set.modules;
11
+ const pluginPackagePrefix = modules.pluginPackagePrefix;
12
+ const pluginManifestFile = modules.pluginManifestFile;
13
+ const cliCatalogPath = modules.cliCatalogPath;
14
+ const vendorPluginsDir = modules.vendorPluginsDir;
15
+ const runtimeInstallCommand = modules.runtimeInstallCommand;
11
16
  return {
12
17
  env,
13
18
  modules,
14
19
  resolvePluginSource: (source, environment, options) => env.resolvePluginSource(source, environment, options),
15
20
  libInstallCommand: (options) => env.libInstallCommand(options),
21
+ libUninstallCommand: (options) => env.libUninstallCommand(options),
16
22
  pluginInstallPlan: (options) => env.pluginInstallPlan(options),
17
23
  latestVersion: (options) => env.latestVersion(options),
18
24
  missingReleaseParts: (options) => env.missingReleaseParts(options),
19
25
  probeRelease: (options) => env.probeRelease(options),
20
26
  splitNpmSpec: (spec) => env.splitNpmSpec(spec),
27
+ isolatedRoot: () => modules.isolatedRoot(),
21
28
  isolatedHome: () => modules.isolatedHome(),
22
- cliCatalogPath: () => modules.cliCatalogPath(),
23
- vendorPluginsDir: () => modules.vendorPluginsDir(),
24
- opencliInstallCommand: () => modules.opencliInstallCommand(),
29
+ ...(pluginPackagePrefix === undefined ? {} : { pluginPackagePrefix: () => pluginPackagePrefix() }),
30
+ ...(pluginManifestFile === undefined ? {} : { pluginManifestFile: () => pluginManifestFile() }),
31
+ ...(cliCatalogPath === undefined ? {} : { cliCatalogPath: () => cliCatalogPath() }),
32
+ ...(vendorPluginsDir === undefined ? {} : { vendorPluginsDir: () => vendorPluginsDir() }),
33
+ ...(runtimeInstallCommand === undefined ? {} : { runtimeInstallCommand: () => runtimeInstallCommand() }),
34
+ afterLibInstall: () => modules.afterLibInstall(),
25
35
  profileDirOf: (context) => modules.profileDirOf(context),
26
36
  profileModules: (dir) => modules.profileModules(dir),
27
37
  readInstalledModule: (dir, pkg) => modules.readInstalledModule(dir, pkg),
@@ -11,13 +11,14 @@ import type { DataReader } from '../contract/data.js';
11
11
  import type { LibReader } from '../contract/lib.js';
12
12
  import type { GitReader } from '../contract/git.js';
13
13
  import type { CommandReader } from '../contract/command.js';
14
+ import type { HostKit } from './host-kit.js';
14
15
 
15
16
  export interface ToolScopeContext {
16
17
  readonly workspace: string | null;
17
18
  readonly preset: string | null;
18
19
  }
19
20
 
20
- export interface UnitMountDeps {
21
+ export interface UnitMountDeps extends HostKit {
21
22
  readonly manifest: UnitManifest;
22
23
  readonly ctx: DshHostContext;
23
24
  readonly logger?: { info?(message: string): void; warn?(message: string): void } | undefined;
@@ -19,12 +19,17 @@ export function isNewerVersion(latest: string, installed: string): boolean {
19
19
  }
20
20
 
21
21
  export function runningVersionOf(moduleUrl: string): string | undefined {
22
- try {
23
- const read = JSON.parse(readFileSync(new URL('../../package.json', moduleUrl), 'utf8')) as {
24
- readonly version?: unknown;
25
- };
26
- return typeof read.version === 'string' && read.version !== '' ? read.version : undefined;
27
- } catch {
28
- return undefined;
22
+ for (const depth of ['../../package.json', '../package.json']) {
23
+ try {
24
+ const read = JSON.parse(readFileSync(new URL(depth, moduleUrl), 'utf8')) as {
25
+ readonly version?: unknown;
26
+ readonly name?: unknown;
27
+ };
28
+ if (typeof read.name !== 'string' || !read.name.startsWith('@dsh-workbench/')) continue;
29
+ return typeof read.version === 'string' && read.version !== '' ? read.version : undefined;
30
+ } catch {
31
+ continue;
32
+ }
29
33
  }
34
+ return undefined;
30
35
  }
package/src/host.ts CHANGED
@@ -19,6 +19,31 @@ export * from './lib/git-address.js';
19
19
  export * from './api/blocker-table.js';
20
20
  export * from './contract/unit-declaration.js';
21
21
  export * from './host/board-fact.js';
22
+ export * from './host/bundle-manager.js';
23
+ export * from './host/installed-module.js';
24
+ export * from './host/local-declaration.js';
25
+ export * from './host/registry-declaration.js';
26
+ export * from './host/release-probe.js';
27
+ export * from './host/package-kind.js';
28
+ export * from './host/package-content.js';
29
+ export * from './host/package-route.js';
30
+ export * from './host/lib-install.js';
31
+ export * from './host/module-root.js';
32
+ export * from './host/dsh-plugins.js';
33
+ export * from './host/profile-registries.js';
34
+ export * from './host/host-actions.js';
35
+ export {
36
+ configSyncAction,
37
+ configSyncApply,
38
+ configSyncHandler,
39
+ configSyncSnapshot,
40
+ mergeBlocks,
41
+ normalizeBlock,
42
+ readPatchBlocks,
43
+ syncRows,
44
+ type ConfigSyncDeps,
45
+ } from './host/config-sync.js';
46
+ export * from './host/profile-create.js';
22
47
  export * from './host/context-ref.js';
23
48
  export * from './host/diy-debug.js';
24
49
  export * from './host/diy-record.js';
@@ -27,6 +52,12 @@ export * from './host/git.js';
27
52
  export * from './host/http.js';
28
53
  export * from './host/lib-capabilities.js';
29
54
  export * from './host/long-task.js';
55
+ export * from './host/flow/types.js';
56
+ export * from './host/flow/expr.js';
57
+ export * from './host/flow/check.js';
58
+ export * from './host/flow/run.js';
59
+ export * from './host/flow/kit.js';
60
+ export * from './host/host-kit.js';
30
61
  export * from './host/panel-state.js';
31
62
  export * from './host/plugin-ledger.js';
32
63
  export * from './host/time.js';
@@ -34,7 +65,6 @@ export * from './host/tokens.js';
34
65
  export * from './host/tool-approval.js';
35
66
  export * from './host/tool-kit.js';
36
67
  export * from './host/unit-face.js';
37
- export * from './host/opencli-runner.js';
38
68
  export * from './host/lib-runners.js';
39
69
  export * from './host/value-handoff.js';
40
70
  export * from './host/workspace-store.js';
@@ -45,9 +75,7 @@ export * from './host/shell-text.js';
45
75
  export * from './host/shell.js';
46
76
  export * from './host/data-error.js';
47
77
  export * from './host/install-failure.js';
48
- export * from './host/runtime-links.js';
49
78
  export * from './host/provision.js';
50
- export * from './host/channel-plugin.js';
51
79
  export * from './host/recipe.js';
52
80
  export * from './host/recipe-rules.js';
53
81
  export * from './host/release-packages.generated.js';
@@ -55,7 +83,6 @@ export * from './contract/unit-capability.js';
55
83
  export * from './contract/plugin-source.js';
56
84
  export * from './contract/cli.js';
57
85
  export * from './host/cli.js';
58
- export * from './host/command-name.js';
59
86
  export * from './host/browser.js';
60
87
  export * from './host/preflight.js';
61
88
  export * from './host/platform.js';
@@ -66,8 +93,8 @@ export * from './host/adapter-types.js';
66
93
  export * from './host/paths.js';
67
94
  export * from './host/plugin.js';
68
95
  export * from './host/spec.js';
69
- export * from './host/plugin-source.js';
70
- export * from './host/lib-install.js';
96
+ export type { PluginSourceCommand, PluginSourceOptions, ResolvedPluginSource } from './contract/plugin-source.js';
97
+ export * from './host/npm-registry.js';
71
98
  export * from './host/supply-index.js';
72
99
  export * from './host/patch.js';
73
100
  export * from './host/adapters.js';
@@ -75,6 +102,7 @@ export * from './host/adapter-set.js';
75
102
  export * from './host/needs.js';
76
103
  export * from './host/extension.js';
77
104
  export * from './host/port-types.js';
105
+ export * from './host/adapter-contract.js';
78
106
  export * from './host/version-range.js';
79
107
  export * from './host/market-index.js';
80
108
  export * from './host/market-index-claim.js';
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { en } from './en.js';
3
- import { dictionaries } from './index.js';
4
- import { zh } from './zh.js';
2
+ import { en } from '../en.js';
3
+ import { dictionaries } from '../index.js';
4
+ import { zh } from '../zh.js';
5
5
 
6
6
  describe('dictionaries', () => {
7
7
  it('同时导出两种语言,且就是那两份字典', () => {
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { mergeMessages, mergeMessagesSkipping } from './merge.js';
2
+ import { mergeMessages, mergeMessagesSkipping } from '../merge.js';
3
3
 
4
4
  const base = { zh: { 'board.title': '看板' }, en: { 'board.title': 'Board' } };
5
5
 
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { en } from './en.js';
3
- import { zh } from './zh.js';
2
+ import { en } from '../en.js';
3
+ import { zh } from '../zh.js';
4
4
 
5
5
  function placeholders(text: string): string[] {
6
6
  return [...text.matchAll(/\{(\w+)\}/g)].map((match) => match[1] ?? '').sort();
package/src/i18n/en.ts CHANGED
@@ -79,7 +79,11 @@ export const en: Record<keyof WorkbenchMessages, string> = {
79
79
 
80
80
  'panels.title': 'Panel modules',
81
81
  'panels.groupBoardless': 'No-board modules',
82
- 'panels.groupPlugins': 'opencli plugins',
82
+ 'panels.groupPlugins': 'Plugins',
83
+ 'runtime.plugin': 'plugin',
84
+ 'runtime.pluginOf': 'a plugin the runtime provides',
85
+ 'runtime.pluginThing': 'a plugin',
86
+ 'runtime.pluginArea': 'the plugin area',
83
87
  'panels.market.defaultHint': 'When on, it is on by default in every workspace; you can also turn it off for a single workspace in Panel setup.',
84
88
  'panels.market.defaultLabel': 'On by default everywhere',
85
89
  'panels.market.defaultLocked': 'Unusable right now — update or uninstall it first',
@@ -159,10 +163,6 @@ export const en: Record<keyof WorkbenchMessages, string> = {
159
163
  'panels.market.confirmNo': 'Cancel',
160
164
  'panels.market.noMarketSource': 'The index has no newer version of this module (refresh the index and retry)',
161
165
  'panels.market.failed': 'Failed: {reason}',
162
- 'panels.market.askAgent': 'Ask the agent',
163
- 'panels.market.askAgentCopied': 'Copied',
164
- 'panels.market.askAgentText':
165
- 'This workbench module cannot be used: "{name}" (unit {unit}) — {reason}. Work out what is missing and how to add it, then confirm it works.',
166
166
  'panels.market.failure.permission': "Didn't install: no write access",
167
167
  'panels.market.failure.diskFull': "Didn't install: the disk is full",
168
168
  'panels.market.failure.sandbox': "Didn't install: the system blocked it",
@@ -202,7 +202,7 @@ export const en: Record<keyof WorkbenchMessages, string> = {
202
202
  'panels.market.noSource': 'This module declares no install method — install it yourself',
203
203
  'panels.market.badSource': 'The install method is incomplete — install it yourself',
204
204
  'panels.market.empty': 'No modules available',
205
- 'panels.market.sectionPlugins': 'Installed opencli plugins',
205
+ 'panels.market.sectionPlugins': 'Installed plugins',
206
206
  'panels.market.moduleOutdated': 'update available',
207
207
  'panels.market.updateNeedsWorkbench': 'this version needs a newer workbench first',
208
208
  'panels.market.updateIncompatible': 'the new version does not match this workbench, so it cannot be installed now',
@@ -240,7 +240,7 @@ export const en: Record<keyof WorkbenchMessages, string> = {
240
240
  'panels.market.justNow': 'just now',
241
241
  'panels.market.minutesAgo': '{n} min ago',
242
242
  'panels.market.hoursAgo': '{n} h ago',
243
- "panels.market.add.description": "Enter a module or opencli plugin package name, Git repository URL, or local directory path.",
243
+ "panels.market.add.description": "Enter a module or {plugin} package name, Git repository URL, or local directory path.",
244
244
  'panels.market.add.specPlaceholder': 'e.g. @dsh-workbench/module-hello',
245
245
  'panels.market.add.guideToggle': 'Install guide and examples',
246
246
  'panels.market.add.guideHide': 'Hide guide',
@@ -280,13 +280,13 @@ export const en: Record<keyof WorkbenchMessages, string> = {
280
280
  'panels.market.add.problemNotFound': 'No such module found',
281
281
  'panels.market.add.problemNotPackage': 'That path does not exist or is not a valid module package',
282
282
  'panels.market.add.problemNotBundle':
283
- 'This package cannot be installed here: {reason}. This entry recognises workbench modules and opencli plugins by package name, and installs either; but a git URL or a tarball does not reveal what it is — if it is really one of those two, use its package name or a local folder instead.',
283
+ 'This package cannot be installed here: {reason}. This entry recognises workbench modules and {plugin}s by package name, and installs either; but a git URL or a tarball does not reveal what it is — if it is really one of those two, use its package name or a local folder instead.',
284
284
  '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.',
285
- '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.',
286
- 'panels.market.add.failureNotModuleOrPlugin': 'This package is neither a workbench module nor an opencli plugin, so installing it would do nothing',
287
- 'panels.market.add.installedPlugin': 'Installed: the opencli plugin {name}',
285
+ 'panels.market.add.problemNotModuleOrPlugin': 'This package is neither a workbench module nor {pluginOf}, so installing it would do nothing. Pick another version, or ask the package author.',
286
+ 'panels.market.add.failureNotModuleOrPlugin': 'This package is neither a workbench module nor {pluginOf}, so installing it would do nothing',
287
+ 'panels.market.add.installedPlugin': 'Installed: {plugin} {name}',
288
288
  'panels.market.add.pluginPreview':
289
- 'This is an opencli plugin: it goes into the opencli plugin area and works as soon as it is installed — no restart.',
289
+ 'This is {pluginThing}: it goes into {pluginArea} and works as soon as it is installed — no restart.',
290
290
  'panels.market.add.pluginAlias': 'Plugin name: {name}',
291
291
  'panels.market.add.problemNetwork': 'Cannot reach the module source; check the network and retry',
292
292
  'panels.market.add.problemUnknown': 'Cannot fetch module information: {reason}',
@@ -383,7 +383,7 @@ export const en: Record<keyof WorkbenchMessages, string> = {
383
383
  'panels.discover.missingIndex': 'This repository has no {path} yet.',
384
384
  'panels.discover.initTitle': 'Initialize this repository as a module market index?',
385
385
  'panels.discover.initCta': 'Initialize market index',
386
- 'panels.discover.initWhat': 'It will write the index directory market/ and README.md on {branch}, commit once, and push. Modules you publish from this workbench will commit their build files under artifacts/<package-name>/<version>/ and their entry under market/<package-name>/<version>.json. The repository stays owned by its owner.',
386
+ 'panels.discover.initWhat': 'It will write the index directory market/ and README.md on {branch}, commit once, and push. Modules you publish from this workbench will commit their build files under artifacts/<package-name>/ and their entry in market/<package-name>.json. The repository stays owned by its owner.',
387
387
  'panels.discover.initBranch': 'Branch',
388
388
  'panels.discover.initBranchHint': 'The index and build files go to this branch; an empty repository gets it created.',
389
389
  'panels.discover.initBranchBad': 'Use only letters, digits, dots, underscores, slashes, or dashes in a branch name.',
@@ -564,7 +564,7 @@ export const en: Record<keyof WorkbenchMessages, string> = {
564
564
  'blocker.forbidden.hint': 'You are signed in, but this project/repo is not permitted for your account.',
565
565
  'blocker.forbidden.open': 'Open permissions page',
566
566
  'blocker.timeout.hint': 'The background browser may not have started; retrying usually helps.',
567
- 'blocker.exec.hint': 'Make sure opencli is installed and runs from your terminal (which opencli).',
567
+ 'blocker.exec.hint': 'Make sure the runtime is installed and runs from your terminal.',
568
568
  'blocker.unknown.title': 'Something went wrong',
569
569
  'panels.reading': 'Reading the panel setup…',
570
570
  'panels.cardBroken': 'This module will not render: {unit} ({reason})',
@@ -581,16 +581,11 @@ export const en: Record<keyof WorkbenchMessages, string> = {
581
581
  'env.stepFailed.shell': 'cannot reach the command line',
582
582
  'env.toolchain.ok': 'Looks good',
583
583
  'env.toolchain.warn': 'Almost ready',
584
- 'env.opencli.ok': 'Ready',
584
+ 'env.runtime.ok': 'Ready',
585
585
  'env.bridge.ok': 'Connected',
586
586
  'env.bridge.short': 'Not connected yet',
587
587
  'env.bridge.why': 'Panel data uses the login state in your browser. Without this connection, modules cannot fetch data. It does not block startup: fix it, or enter the workbench first.',
588
588
  'env.bridge.hold': 'The browser is not connected yet \u2014 fix it below and hit Retry, or enter the workbench first.',
589
- 'env.bridge.daemon': 'The background service is not running. It usually starts by itself \u2014 run the command below in your terminal first.',
590
- 'env.bridge.extension': 'The browser extension is not connected. Make sure the browser is running and the extension is enabled; install it once from the store if needed (then hit Retry here).',
591
- 'env.bridge.extensionLink': 'Get the extension in the Chrome Web Store',
592
- 'env.bridge.profile': 'Several browser accounts are connected but none is the default: list them (command below), then pick one.',
593
- 'env.bridge.stale': 'The default browser account is stale (it names an extension instance that no longer exists): pick one again.',
594
589
  'env.bridge.unknown': 'Could not tell what went wrong. Expand the raw output and follow the steps in it.',
595
590
  'env.fix.title': 'How to fix it',
596
591
  'env.fix.why': 'The workbench uses its own background tool (installed separately from the one in your terminal), and it is not set up on this machine yet.',
@@ -620,7 +615,8 @@ export const en: Record<keyof WorkbenchMessages, string> = {
620
615
  'env.copyCommand': 'Copy command',
621
616
  'env.copied': 'Copied',
622
617
  'env.step.toolchain': 'Runtime',
623
- 'env.step.opencli': 'Background tools',
618
+ 'env.step.adapter': 'Runtime',
619
+ 'env.step.runtime': 'Background tools',
624
620
  'env.step.bridge': 'Browser connection',
625
621
  'env.step.diy': 'DIY mode',
626
622
  'env.step.warm': 'Site sign-in',
@@ -722,6 +718,8 @@ export const en: Record<keyof WorkbenchMessages, string> = {
722
718
  'approval.publish.version': 'Version',
723
719
  'approval.publish.author': 'Author',
724
720
  'approval.publish.authorMissing': 'unsigned',
721
+ 'approval.publish.labels': 'Labels',
722
+ 'approval.publish.labelsMissing': 'none yet (1–2 picked on publish)',
725
723
  'approval.publish.owner': 'Owner',
726
724
  'approval.publish.ownerUnowned': 'originally unattributed',
727
725
  'approval.publish.ownerTaken': 'Owned by {owner}',
@@ -0,0 +1,26 @@
1
+ import type { Translate } from '../lib/dsh-client.js';
2
+ import type { RuntimeText } from '../contract/capabilities.js';
3
+
4
+ /**
5
+ * 把适配器给的 zh / en 两份文案落到当前语言。
6
+ * @param text 适配器档案里的那段话。
7
+ * @param resolve dsh 的语言解析器;没有就取中文那份。
8
+ * @returns 当前语言的那一段。
9
+ */
10
+ export function resolveRuntimeText(text: RuntimeText, resolve?: (text: RuntimeText) => string): string {
11
+ return resolve === undefined ? text.zh : resolve(text);
12
+ }
13
+
14
+ /**
15
+ * 取当前语言的运行时术语(没给档案时的兜底)。
16
+ * @param t 文案函数。
17
+ * @returns `plugin` / `pluginOf` / `pluginThing` / `pluginArea` 四个名词短语。
18
+ */
19
+ export function runtimeTermsOf(t: Translate): Readonly<Record<string, string>> {
20
+ return {
21
+ plugin: t('runtime.plugin'),
22
+ pluginOf: t('runtime.pluginOf'),
23
+ pluginThing: t('runtime.pluginThing'),
24
+ pluginArea: t('runtime.pluginArea'),
25
+ };
26
+ }