@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,150 @@
1
+ import { normalizeRegistry, sameRegistry, scopeOf } from './adapter-types.js';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ export const REGISTRY_MARKER = '# dsh-workbench 写的:这一行的包只在这个源上(dsh 卸载没有源参数,pnpm 得问得到)';
5
+ function registryRootOf(name, tarball) {
6
+ const at = tarball.indexOf(`/${name}/-/`);
7
+ if (at > 0)
8
+ return normalizeRegistry(tarball.slice(0, at));
9
+ try {
10
+ return normalizeRegistry(new URL(tarball).origin);
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ }
16
+ const PACKAGE_KEY = /^ {2}'?(\S+)'?:\s*$/;
17
+ const PACKAGE_NAME = /^(?:@[^/\s]+\/)?[^/\s@]+$/;
18
+ const TARBALL = /tarball:\s*(\S+?)\}?\s*$/;
19
+ export function sourcesFromLockfile(text) {
20
+ const found = new Map();
21
+ let name = null;
22
+ for (const line of text.split('\n')) {
23
+ const key = PACKAGE_KEY.exec(line);
24
+ if (key !== null) {
25
+ const raw = key[1] ?? '';
26
+ const at = raw.lastIndexOf('@');
27
+ const candidate = at > 0 ? raw.slice(0, at) : '';
28
+ name = PACKAGE_NAME.test(candidate) ? candidate : null;
29
+ continue;
30
+ }
31
+ if (name === null)
32
+ continue;
33
+ const tarball = TARBALL.exec(line);
34
+ if (tarball === null)
35
+ continue;
36
+ const scope = scopeOf(name);
37
+ const root = registryRootOf(name, tarball[1] ?? '');
38
+ name = null;
39
+ if (scope === null || root === null)
40
+ continue;
41
+ const seen = found.get(scope);
42
+ if (seen === undefined)
43
+ found.set(scope, root);
44
+ else if (!sameRegistry(seen, root))
45
+ found.set(scope, null);
46
+ }
47
+ return [...found.entries()]
48
+ .filter((entry) => entry[1] !== null)
49
+ .map(([scope, registry]) => ({ scope, registry }));
50
+ }
51
+ export function mergeRegistryConfig(text, entries) {
52
+ const lines = text.split('\n');
53
+ const present = (key) => lines.some((line) => line.trim().startsWith(`${key}=`));
54
+ const appended = [];
55
+ const added = [];
56
+ for (const entry of entries) {
57
+ const key = `${entry.scope}:registry`;
58
+ if (entry.registry.trim() === '' || present(key))
59
+ continue;
60
+ lines.push(`${key}=${normalizeRegistry(entry.registry)}`);
61
+ appended.push(`${key}=${normalizeRegistry(entry.registry)}`);
62
+ added.push(key);
63
+ }
64
+ if (appended.length === 0)
65
+ return { text, added: [] };
66
+ const base = text === '' ? '' : text.endsWith('\n') ? text : `${text}\n`;
67
+ const marker = base.includes(REGISTRY_MARKER) ? '' : `${REGISTRY_MARKER}\n`;
68
+ return { text: `${base}${marker}${appended.join('\n')}\n`, added };
69
+ }
70
+ export function replaceScopeRegistry(text, scope, registry) {
71
+ const key = `${scope}:registry`;
72
+ const value = normalizeRegistry(registry);
73
+ let found = false;
74
+ const lines = text.split('\n').map((line) => {
75
+ if (!line.trim().startsWith(`${key}=`))
76
+ return line;
77
+ found = true;
78
+ return `${key}=${value}`;
79
+ });
80
+ if (found)
81
+ return lines.join('\n');
82
+ const base = text === '' ? '' : text.endsWith('\n') ? text : `${text}\n`;
83
+ return `${base}${key}=${value}\n`;
84
+ }
85
+ function readNpmrc(file) {
86
+ try {
87
+ return fs.readFileSync(file, 'utf8');
88
+ }
89
+ catch {
90
+ return '';
91
+ }
92
+ }
93
+ function writeNpmrc(file, text) {
94
+ const temp = `${file}.dsh-workbench.tmp`;
95
+ fs.writeFileSync(temp, text, { mode: 0o600 });
96
+ fs.renameSync(temp, file);
97
+ }
98
+ export function createRegistryNotebook(dir) {
99
+ return {
100
+ sources() {
101
+ if (dir === null)
102
+ return [];
103
+ try {
104
+ return sourcesFromLockfile(fs.readFileSync(path.join(dir, 'pnpm-lock.yaml'), 'utf8'));
105
+ }
106
+ catch {
107
+ return [];
108
+ }
109
+ },
110
+ remember(entries) {
111
+ if (dir === null || entries.length === 0)
112
+ return [];
113
+ const file = path.join(dir, '.npmrc');
114
+ const merged = mergeRegistryConfig(readNpmrc(file), entries);
115
+ if (merged.added.length === 0)
116
+ return [];
117
+ writeNpmrc(file, merged.text);
118
+ return merged.added;
119
+ },
120
+ swapScope(scope, registry) {
121
+ if (dir === null || scope.trim() === '' || registry.trim() === '')
122
+ return () => { };
123
+ const file = path.join(dir, '.npmrc');
124
+ const key = `${scope.trim()}:registry`;
125
+ const value = normalizeRegistry(registry);
126
+ const before = readNpmrc(file);
127
+ const previous = before.split('\n').find((line) => line.trim().startsWith(`${key}=`));
128
+ const after = replaceScopeRegistry(before, scope.trim(), registry);
129
+ if (after === before)
130
+ return () => { };
131
+ writeNpmrc(file, after);
132
+ return () => {
133
+ const now = readNpmrc(file);
134
+ if (now === after) {
135
+ if (before === '')
136
+ fs.rmSync(file, { force: true });
137
+ else
138
+ writeNpmrc(file, before);
139
+ return;
140
+ }
141
+ if (!now.includes(`${key}=${value}`))
142
+ return;
143
+ const restored = previous === undefined
144
+ ? now.split('\n').filter((line) => !line.trim().startsWith(`${key}=`)).join('\n')
145
+ : now.replace(`${key}=${value}`, previous);
146
+ writeNpmrc(file, restored);
147
+ };
148
+ },
149
+ };
150
+ }
@@ -0,0 +1,21 @@
1
+ import type { DshShell } from '../api/dsh.js';
2
+ export interface RegistryDshLookup {
3
+ readonly readable: boolean;
4
+ readonly raw: unknown;
5
+ }
6
+ export interface RegistryLookup {
7
+ registryDshOf(options: RegistryDshOptions): Promise<RegistryDshLookup>;
8
+ registryDeclarationOf(options: RegistryDeclarationOptions): Promise<unknown | undefined>;
9
+ }
10
+ interface RegistryDshOptions {
11
+ readonly shell: DshShell;
12
+ readonly name: string;
13
+ readonly registry?: string | undefined;
14
+ readonly force?: boolean;
15
+ readonly cacheDir?: string | undefined;
16
+ }
17
+ interface RegistryDeclarationOptions extends RegistryDshOptions {
18
+ readonly registry: string;
19
+ }
20
+ export declare function createRegistryLookup(): RegistryLookup;
21
+ export {};
@@ -0,0 +1,33 @@
1
+ import { runShell } from './shell.js';
2
+ import { quoteShellArg } from './shell-text.js';
3
+ import { parseJsonLoose } from '../lib/text.js';
4
+ import { VERSION_CHECK_TIMEOUT_MS, VERSION_CHECK_TTL_MS } from '../config/constants.js';
5
+ function recordOf(value) {
6
+ return typeof value === 'object' && value !== null ? value : undefined;
7
+ }
8
+ export function createRegistryLookup() {
9
+ const cache = new Map();
10
+ return {
11
+ registryDshOf: (options) => dshOf(cache, options),
12
+ registryDeclarationOf: async (options) => {
13
+ const found = await dshOf(cache, options);
14
+ return found.readable ? recordOf(found.raw)?.workbench : undefined;
15
+ },
16
+ };
17
+ }
18
+ async function dshOf(cache, options) {
19
+ const { shell, name, registry, force = false, cacheDir } = options;
20
+ const key = `${registry ?? ''}|${name}`;
21
+ const hit = cache.get(key);
22
+ if (!force && hit !== undefined && Date.now() - hit.at < VERSION_CHECK_TTL_MS) {
23
+ return { readable: true, raw: hit.raw };
24
+ }
25
+ const at = registry === undefined ? '' : ` --registry ${quoteShellArg(registry)}`;
26
+ const cached = cacheDir === undefined ? '' : ` --cache ${quoteShellArg(cacheDir)}`;
27
+ const result = await runShell(shell, `npm view ${quoteShellArg(name)} dsh --json${at}${cached}`, { timeoutMs: VERSION_CHECK_TIMEOUT_MS }).catch(() => undefined);
28
+ const parsed = result?.exitCode === 0 ? parseJsonLoose(result.stdout) : undefined;
29
+ if (parsed === undefined)
30
+ return { readable: false, raw: undefined };
31
+ cache.set(key, { at: Date.now(), raw: parsed });
32
+ return { readable: true, raw: parsed };
33
+ }
@@ -0,0 +1,34 @@
1
+ import type { DshShell } from '../api/dsh.js';
2
+ import type { PluginRelease } from '../contract/unit.js';
3
+ export declare function parseLedgerVersion(stdout: string, name: string): string | undefined;
4
+ export interface ReleaseProbe {
5
+ latestVersion(options: LatestVersionOptions): Promise<string | undefined>;
6
+ missingReleaseParts(options: MissingPartsOptions): Promise<readonly string[]>;
7
+ probeRelease(options: ProbeReleaseOptions): Promise<PluginRelease | undefined>;
8
+ reset(): void;
9
+ }
10
+ export declare function createReleaseProbe(): ReleaseProbe;
11
+ interface LatestVersionOptions {
12
+ readonly shell: DshShell;
13
+ readonly name: string;
14
+ readonly registry: string;
15
+ readonly force?: boolean;
16
+ readonly cacheDir?: string | undefined;
17
+ }
18
+ interface MissingPartsOptions {
19
+ readonly shell: DshShell;
20
+ readonly name: string;
21
+ readonly registry: string;
22
+ readonly version: string;
23
+ readonly force?: boolean;
24
+ readonly cacheDir?: string | undefined;
25
+ }
26
+ interface ProbeReleaseOptions {
27
+ readonly shell: DshShell;
28
+ readonly package: string;
29
+ readonly registry?: string | undefined;
30
+ readonly force?: boolean;
31
+ readonly cacheDir?: string | undefined;
32
+ readonly prefix?: string | undefined;
33
+ }
34
+ export {};
@@ -0,0 +1,88 @@
1
+ import { RELEASE_PACKAGES } from './release-packages.generated.js';
2
+ import { VERSION_CHECK_TIMEOUT_MS, VERSION_CHECK_TTL_MS } from '../config/constants.js';
3
+ import { isValidVersion } from './version.js';
4
+ import { parseJsonLoose } from '../lib/text.js';
5
+ import { quoteShellArg } from './shell-text.js';
6
+ import { runShell } from './shell.js';
7
+ function cacheFlag(cacheDir) {
8
+ return cacheDir === undefined ? '' : ` --cache ${quoteShellArg(cacheDir)}`;
9
+ }
10
+ export function parseLedgerVersion(stdout, name) {
11
+ const parsed = parseJsonLoose(stdout);
12
+ if (typeof parsed !== "object" || parsed === null)
13
+ return undefined;
14
+ const entry = parsed
15
+ .dependencies?.[name];
16
+ if (typeof entry !== "object" || entry === null)
17
+ return undefined;
18
+ const version = entry.version;
19
+ return typeof version === "string" && isValidVersion(version.trim())
20
+ ? version.trim()
21
+ : undefined;
22
+ }
23
+ function parseVersion(stdout) {
24
+ const value = stdout.trim();
25
+ return isValidVersion(value) ? value : undefined;
26
+ }
27
+ export function createReleaseProbe() {
28
+ const latestCache = new Map();
29
+ const existsCache = new Map();
30
+ return {
31
+ latestVersion: async (options) => latestVersionOf(latestCache, options),
32
+ missingReleaseParts: async (options) => missingPartsOf(latestCache, existsCache, options),
33
+ probeRelease: async (options) => probeOf(latestCache, options),
34
+ reset: () => {
35
+ latestCache.clear();
36
+ existsCache.clear();
37
+ },
38
+ };
39
+ }
40
+ async function latestVersionOf(latestCache, options) {
41
+ const { shell, name, registry, force = false, cacheDir } = options;
42
+ const key = `${registry}|${name}`;
43
+ const hit = latestCache.get(key);
44
+ if (!force && hit !== undefined && Date.now() - hit.at < VERSION_CHECK_TTL_MS)
45
+ return hit.value;
46
+ const result = await runShell(shell, `npm view ${quoteShellArg(name)} version --registry ${quoteShellArg(registry)}${cacheFlag(cacheDir)}`, { timeoutMs: VERSION_CHECK_TIMEOUT_MS }).catch(() => undefined);
47
+ const read = result?.exitCode === 0 ? parseVersion(result.stdout) : undefined;
48
+ latestCache.set(key, { at: Date.now(), value: read });
49
+ return read;
50
+ }
51
+ async function versionExists(existsCache, options) {
52
+ const { shell, name, version, registry, force = false, cacheDir } = options;
53
+ const key = `${registry}|${name}@${version}`;
54
+ const hit = existsCache.get(key);
55
+ if (!force && hit !== undefined && (hit.value || Date.now() - hit.at < VERSION_CHECK_TTL_MS))
56
+ return hit.value;
57
+ const result = await runShell(shell, `npm view ${quoteShellArg(`${name}@${version}`)} version --registry ${quoteShellArg(registry)}${cacheFlag(cacheDir)}`, { timeoutMs: VERSION_CHECK_TIMEOUT_MS }).catch(() => undefined);
58
+ const value = result?.exitCode === 0 && parseVersion(result.stdout) !== undefined;
59
+ existsCache.set(key, { at: Date.now(), value });
60
+ return value;
61
+ }
62
+ async function missingPartsOf(latestCache, existsCache, options) {
63
+ const { shell, name, registry, version, force = false, cacheDir } = options;
64
+ const checked = await Promise.all(RELEASE_PACKAGES.filter((part) => part !== name).map(async (part) => ({
65
+ part,
66
+ present: await versionExists(existsCache, { shell, name: part, version, registry, force, cacheDir }),
67
+ })));
68
+ return checked.filter((entry) => !entry.present).map((entry) => entry.part);
69
+ }
70
+ async function probeOf(latestCache, options) {
71
+ const { shell, package: pkg, registry, force = false, cacheDir, prefix } = options;
72
+ if (pkg === "")
73
+ return undefined;
74
+ const ledger = await runShell(shell, `npm ls --prefix ${quoteShellArg(prefix ?? '')} ${quoteShellArg(pkg)} --json --depth=0${cacheFlag(cacheDir)}`, { timeoutMs: VERSION_CHECK_TIMEOUT_MS }).catch(() => undefined);
75
+ const installed = ledger === undefined ? undefined : parseLedgerVersion(ledger.stdout, pkg);
76
+ if (installed === undefined)
77
+ return undefined;
78
+ const source = registry === undefined || registry.trim() === ""
79
+ ? undefined
80
+ : registry.trim();
81
+ const latest = source === undefined
82
+ ? undefined
83
+ : await latestVersionOf(latestCache, { shell, name: pkg, registry: source, force, cacheDir });
84
+ return {
85
+ installed,
86
+ ...(latest === undefined ? {} : { latest }),
87
+ };
88
+ }
@@ -0,0 +1,26 @@
1
+ import type { DataRequest, DataResult } from '../contract/data.js';
2
+ /**
3
+ * 一条 opencli 命令的执行器:同一形状的 `execFile('opencli', args)`,结果只留 stdout。
4
+ */
5
+ export type LibRunner = (args: readonly string[]) => Promise<{
6
+ readonly stdout: string;
7
+ }>;
8
+ export type OpencliRunner = LibRunner;
9
+ /**
10
+ * 读缝(`DataReader.read`)的形状;模块直接把自己的 `data.read` 传进来即可。
11
+ */
12
+ export type OpencliRead = (request: DataRequest) => Promise<DataResult<unknown>>;
13
+ export interface OpencliRunnerOptions {
14
+ readonly timeoutMs?: number;
15
+ }
16
+ /**
17
+ * 把读缝包成一条 opencli 命令的执行器:给还按 `execFile('opencli', args)` 收结果的编排库用,
18
+ * 让它们跑在面板用的那份 opencli 上(同一套插件、同一份登录态)。
19
+ *
20
+ * 失败按 opencli 自己的约定还原:未登录是 `code: AUTH_REQUIRED` + 退出码 77,编排库的
21
+ * 「等登录再重试」才接得上;其它失败保留 `Command failed: opencli …` 那句外壳。
22
+ * @param read 模块手里的读缝(`data.read`)。
23
+ * @param options `timeoutMs` 透传给读缝;不传就用读缝自己的默认值。
24
+ * @returns 执行器;直接交给编排库的 `setOpencliRunner`。
25
+ */
26
+ export declare function opencliRunnerOf(read: OpencliRead, options?: OpencliRunnerOptions): OpencliRunner;
@@ -0,0 +1,50 @@
1
+ const AUTH_REQUIRED_EXIT_CODE = 77;
2
+ function textOf(value) {
3
+ return typeof value === 'string' ? value : '';
4
+ }
5
+ function failedOf(args, error) {
6
+ const head = `Command failed: opencli ${args.join(' ')}`;
7
+ const auth = error?.pageCode === 'auth-required';
8
+ const helpUrl = textOf(error?.helpUrl);
9
+ const detail = textOf(error?.detail);
10
+ const hint = textOf(error?.hint);
11
+ const failed = new Error([
12
+ head,
13
+ ...(auth
14
+ ? ['code: AUTH_REQUIRED', `message: ${textOf(error?.message)}`]
15
+ : [textOf(error?.message) || 'opencli 这条命令没跑通']),
16
+ ...(detail === '' ? [] : [detail]),
17
+ ...(hint === '' ? [] : [hint]),
18
+ ...(auth && helpUrl !== ''
19
+ ? [`help: Please open Chrome or Chromium and log in to ${helpUrl}`]
20
+ : []),
21
+ ].join('\n'));
22
+ if (auth)
23
+ failed.code = AUTH_REQUIRED_EXIT_CODE;
24
+ return failed;
25
+ }
26
+ /**
27
+ * 把读缝包成一条 opencli 命令的执行器:给还按 `execFile('opencli', args)` 收结果的编排库用,
28
+ * 让它们跑在面板用的那份 opencli 上(同一套插件、同一份登录态)。
29
+ *
30
+ * 失败按 opencli 自己的约定还原:未登录是 `code: AUTH_REQUIRED` + 退出码 77,编排库的
31
+ * 「等登录再重试」才接得上;其它失败保留 `Command failed: opencli …` 那句外壳。
32
+ * @param read 模块手里的读缝(`data.read`)。
33
+ * @param options `timeoutMs` 透传给读缝;不传就用读缝自己的默认值。
34
+ * @returns 执行器;直接交给编排库的 `setOpencliRunner`。
35
+ */
36
+ export function opencliRunnerOf(read, options = {}) {
37
+ return async (args) => {
38
+ const result = await read({
39
+ kind: 'command',
40
+ adapter: 'opencli',
41
+ argv: [...args],
42
+ ...(options.timeoutMs === undefined
43
+ ? {}
44
+ : { timeoutMs: options.timeoutMs }),
45
+ });
46
+ if (result.ok)
47
+ return { stdout: JSON.stringify(result.data) };
48
+ throw failedOf(args, result.error);
49
+ };
50
+ }
@@ -12,5 +12,3 @@ export declare function runShell(shell: DshShell, command: string, options?: {
12
12
  timeoutMs?: number;
13
13
  signal?: AbortSignal;
14
14
  }): Promise<ShellResult>;
15
- export declare function backgroundPrefix(): readonly string[];
16
- export declare function backgroundLine(command: string, argv: readonly string[]): string;
@@ -1,5 +1,4 @@
1
1
  import { GIT_TIMEOUT_MS } from '../config/constants.js';
2
- import { quoteShellArg } from './shell-text.js';
3
2
  const DEFAULT_MAX_BYTES = 8 * 1024 * 1024;
4
3
  export async function runShell(shell, command, options = {}) {
5
4
  const { workdir, timeoutMs = GIT_TIMEOUT_MS, signal } = options;
@@ -22,9 +21,3 @@ export async function runShell(shell, command, options = {}) {
22
21
  truncated: result.stdout.truncated || result.stderr.truncated,
23
22
  };
24
23
  }
25
- export function backgroundPrefix() {
26
- return [quoteShellArg('env'), quoteShellArg('OPENCLI_WINDOW=background')];
27
- }
28
- export function backgroundLine(command, argv) {
29
- return [...backgroundPrefix(), quoteShellArg(command), ...argv.map(quoteShellArg)].join(' ');
30
- }
package/dist/host/spec.js CHANGED
@@ -20,5 +20,5 @@ export function localDirOf(spec) {
20
20
  return path.isAbsolute(dir) ? dir : undefined;
21
21
  }
22
22
  export function refusalReason() {
23
- return '这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上';
23
+ return '这个包既不是工作台的模块,也不是运行时的插件,装了也用不上';
24
24
  }
@@ -5,6 +5,7 @@ export interface SupplyDeclaration {
5
5
  }
6
6
  export interface SupplyIndexOptions {
7
7
  readonly env?: Readonly<Record<string, string | undefined>>;
8
+ readonly packagePrefix?: string | undefined;
8
9
  }
9
10
  export interface SupplyEntry {
10
11
  readonly key: string;
@@ -1,12 +1,12 @@
1
- import { npmRegistryOf } from './plugin-source.js';
1
+ import { npmRegistryOf } from './npm-registry.js';
2
2
  export function npmSourceOf(sources) {
3
3
  return sources?.find((source) => source.kind === 'npm');
4
4
  }
5
5
  function text(value) {
6
6
  return value === undefined ? '' : value.trim();
7
7
  }
8
- function conventionPackage(name) {
9
- return `opencli-plugin-${name}`;
8
+ function conventionPackage(name, prefix) {
9
+ return `${prefix}${name}`;
10
10
  }
11
11
  function nativeSourcePackage(source) {
12
12
  if (source.kind === 'npm')
@@ -15,15 +15,15 @@ function nativeSourcePackage(source) {
15
15
  return `git:${text(source.url)}`;
16
16
  return `local:${text(source.env) || text(source.path)}`;
17
17
  }
18
- function packageOf(need) {
18
+ function packageOf(need, prefix) {
19
19
  if (need.kind === 'lib')
20
20
  return text(need.name);
21
21
  const first = need.sources?.[0];
22
22
  if (first === undefined)
23
- return conventionPackage(need.name);
23
+ return conventionPackage(need.name, prefix);
24
24
  if (first.kind === 'npm') {
25
25
  const pkg = text(first.package);
26
- return pkg === '' ? conventionPackage(need.name) : pkg;
26
+ return pkg === '' ? conventionPackage(need.name, prefix) : pkg;
27
27
  }
28
28
  return nativeSourcePackage(first);
29
29
  }
@@ -107,6 +107,7 @@ function addDeclarer(entry, unit, commands) {
107
107
  }
108
108
  export function buildSupplyIndex(declarations, options = {}) {
109
109
  const env = options.env ?? {};
110
+ const prefix = options.packagePrefix ?? '';
110
111
  const entries = new Map();
111
112
  const byName = new Map();
112
113
  const conflicts = [];
@@ -133,7 +134,7 @@ export function buildSupplyIndex(declarations, options = {}) {
133
134
  for (const need of declaration.needs ?? []) {
134
135
  if (need.kind !== 'extension' && need.kind !== 'lib')
135
136
  continue;
136
- const pkg = packageOf(need);
137
+ const pkg = packageOf(need, prefix);
137
138
  const entry = ensure(need.kind, pkg);
138
139
  if (!entry.names.includes(need.name))
139
140
  entry.names.push(need.name);
@@ -3,20 +3,30 @@ export function supplyPortOf(ctx) {
3
3
  const set = adaptersOf(ctx);
4
4
  const env = set.supply;
5
5
  const modules = set.modules;
6
+ const pluginPackagePrefix = modules.pluginPackagePrefix;
7
+ const pluginManifestFile = modules.pluginManifestFile;
8
+ const cliCatalogPath = modules.cliCatalogPath;
9
+ const vendorPluginsDir = modules.vendorPluginsDir;
10
+ const runtimeInstallCommand = modules.runtimeInstallCommand;
6
11
  return {
7
12
  env,
8
13
  modules,
9
14
  resolvePluginSource: (source, environment, options) => env.resolvePluginSource(source, environment, options),
10
15
  libInstallCommand: (options) => env.libInstallCommand(options),
16
+ libUninstallCommand: (options) => env.libUninstallCommand(options),
11
17
  pluginInstallPlan: (options) => env.pluginInstallPlan(options),
12
18
  latestVersion: (options) => env.latestVersion(options),
13
19
  missingReleaseParts: (options) => env.missingReleaseParts(options),
14
20
  probeRelease: (options) => env.probeRelease(options),
15
21
  splitNpmSpec: (spec) => env.splitNpmSpec(spec),
22
+ isolatedRoot: () => modules.isolatedRoot(),
16
23
  isolatedHome: () => modules.isolatedHome(),
17
- cliCatalogPath: () => modules.cliCatalogPath(),
18
- vendorPluginsDir: () => modules.vendorPluginsDir(),
19
- opencliInstallCommand: () => modules.opencliInstallCommand(),
24
+ ...(pluginPackagePrefix === undefined ? {} : { pluginPackagePrefix: () => pluginPackagePrefix() }),
25
+ ...(pluginManifestFile === undefined ? {} : { pluginManifestFile: () => pluginManifestFile() }),
26
+ ...(cliCatalogPath === undefined ? {} : { cliCatalogPath: () => cliCatalogPath() }),
27
+ ...(vendorPluginsDir === undefined ? {} : { vendorPluginsDir: () => vendorPluginsDir() }),
28
+ ...(runtimeInstallCommand === undefined ? {} : { runtimeInstallCommand: () => runtimeInstallCommand() }),
29
+ afterLibInstall: () => modules.afterLibInstall(),
20
30
  profileDirOf: (context) => modules.profileDirOf(context),
21
31
  profileModules: (dir) => modules.profileModules(dir),
22
32
  readInstalledModule: (dir, pkg) => modules.readInstalledModule(dir, pkg),
@@ -11,11 +11,12 @@ 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
  export interface ToolScopeContext {
15
16
  readonly workspace: string | null;
16
17
  readonly preset: string | null;
17
18
  }
18
- export interface UnitMountDeps {
19
+ export interface UnitMountDeps extends HostKit {
19
20
  readonly manifest: UnitManifest;
20
21
  readonly ctx: DshHostContext;
21
22
  readonly logger?: {
@@ -17,11 +17,16 @@ export function isNewerVersion(latest, installed) {
17
17
  return compared === undefined ? true : compared > 0;
18
18
  }
19
19
  export function runningVersionOf(moduleUrl) {
20
- try {
21
- const read = JSON.parse(readFileSync(new URL('../../package.json', moduleUrl), 'utf8'));
22
- return typeof read.version === 'string' && read.version !== '' ? read.version : undefined;
23
- }
24
- catch {
25
- return undefined;
20
+ for (const depth of ['../../package.json', '../package.json']) {
21
+ try {
22
+ const read = JSON.parse(readFileSync(new URL(depth, moduleUrl), 'utf8'));
23
+ if (typeof read.name !== 'string' || !read.name.startsWith('@dsh-workbench/'))
24
+ continue;
25
+ return typeof read.version === 'string' && read.version !== '' ? read.version : undefined;
26
+ }
27
+ catch {
28
+ continue;
29
+ }
26
30
  }
31
+ return undefined;
27
32
  }
package/dist/host.d.ts CHANGED
@@ -19,6 +19,21 @@ 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 { configSyncAction, configSyncApply, configSyncHandler, configSyncSnapshot, mergeBlocks, normalizeBlock, readPatchBlocks, syncRows, type ConfigSyncDeps, } from './host/config-sync.js';
36
+ export * from './host/profile-create.js';
22
37
  export * from './host/context-ref.js';
23
38
  export * from './host/diy-debug.js';
24
39
  export * from './host/diy-record.js';
@@ -27,6 +42,12 @@ export * from './host/git.js';
27
42
  export * from './host/http.js';
28
43
  export * from './host/lib-capabilities.js';
29
44
  export * from './host/long-task.js';
45
+ export * from './host/flow/types.js';
46
+ export * from './host/flow/expr.js';
47
+ export * from './host/flow/check.js';
48
+ export * from './host/flow/run.js';
49
+ export * from './host/flow/kit.js';
50
+ export * from './host/host-kit.js';
30
51
  export * from './host/panel-state.js';
31
52
  export * from './host/plugin-ledger.js';
32
53
  export * from './host/time.js';
@@ -34,7 +55,6 @@ export * from './host/tokens.js';
34
55
  export * from './host/tool-approval.js';
35
56
  export * from './host/tool-kit.js';
36
57
  export * from './host/unit-face.js';
37
- export * from './host/opencli-runner.js';
38
58
  export * from './host/lib-runners.js';
39
59
  export * from './host/value-handoff.js';
40
60
  export * from './host/workspace-store.js';
@@ -45,9 +65,7 @@ export * from './host/shell-text.js';
45
65
  export * from './host/shell.js';
46
66
  export * from './host/data-error.js';
47
67
  export * from './host/install-failure.js';
48
- export * from './host/runtime-links.js';
49
68
  export * from './host/provision.js';
50
- export * from './host/channel-plugin.js';
51
69
  export * from './host/recipe.js';
52
70
  export * from './host/recipe-rules.js';
53
71
  export * from './host/release-packages.generated.js';
@@ -55,7 +73,6 @@ export * from './contract/unit-capability.js';
55
73
  export * from './contract/plugin-source.js';
56
74
  export * from './contract/cli.js';
57
75
  export * from './host/cli.js';
58
- export * from './host/command-name.js';
59
76
  export * from './host/browser.js';
60
77
  export * from './host/preflight.js';
61
78
  export * from './host/platform.js';
@@ -66,8 +83,8 @@ export * from './host/adapter-types.js';
66
83
  export * from './host/paths.js';
67
84
  export * from './host/plugin.js';
68
85
  export * from './host/spec.js';
69
- export * from './host/plugin-source.js';
70
- export * from './host/lib-install.js';
86
+ export type { PluginSourceCommand, PluginSourceOptions, ResolvedPluginSource } from './contract/plugin-source.js';
87
+ export * from './host/npm-registry.js';
71
88
  export * from './host/supply-index.js';
72
89
  export * from './host/patch.js';
73
90
  export * from './host/adapters.js';
@@ -75,6 +92,7 @@ export * from './host/adapter-set.js';
75
92
  export * from './host/needs.js';
76
93
  export * from './host/extension.js';
77
94
  export * from './host/port-types.js';
95
+ export * from './host/adapter-contract.js';
78
96
  export * from './host/version-range.js';
79
97
  export * from './host/market-index.js';
80
98
  export * from './host/market-index-claim.js';