@dsh-workbench/shared 1.1.8 → 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.
- package/dist/api/blocker-table.d.ts +1 -0
- package/dist/api/blocker-table.js +1 -1
- package/dist/components/SelectField.css +0 -6
- package/dist/components/SelectField.d.ts +2 -3
- package/dist/components/SelectField.js +9 -30
- package/dist/components/SelectFieldNodes.d.ts +3 -1
- package/dist/components/SelectFieldNodes.js +2 -1
- package/dist/components/SelectFieldWheel.d.ts +9 -0
- package/dist/components/SelectFieldWheel.js +35 -0
- package/dist/config/constants.d.ts +4 -4
- package/dist/config/constants.js +4 -4
- package/dist/contract/capabilities.d.ts +29 -0
- package/dist/contract/capabilities.js +1 -0
- package/dist/contract/needs.d.ts +3 -3
- package/dist/contract/needs.js +1 -1
- package/dist/contract/plugin-source.d.ts +19 -0
- package/dist/contract/protocol/bundle-install.d.ts +3 -1
- package/dist/contract/protocol/bundle-install.js +3 -0
- package/dist/contract/protocol/platform.d.ts +1 -1
- package/dist/contract/runner.d.ts +6 -0
- package/dist/contract/runner.js +1 -0
- package/dist/contract/unit.d.ts +4 -4
- package/dist/host/adapter-contract.d.ts +3 -0
- package/dist/host/adapter-contract.js +93 -0
- package/dist/host/adapter-set.js +6 -4
- package/dist/host/adapter-types.d.ts +1 -1
- package/dist/host/bundle-manager.d.ts +3 -0
- package/dist/host/bundle-manager.js +181 -0
- package/dist/host/cli.js +3 -1
- package/dist/host/config-sync.d.ts +48 -0
- package/dist/host/config-sync.js +342 -0
- package/dist/host/dsh-plugins.d.ts +5 -0
- package/dist/host/dsh-plugins.js +51 -0
- package/dist/host/extension.js +1 -1
- package/dist/host/flow/check.d.ts +9 -0
- package/dist/host/flow/check.js +179 -0
- package/dist/host/flow/expr.d.ts +30 -0
- package/dist/host/flow/expr.js +99 -0
- package/dist/host/flow/kit.d.ts +14 -0
- package/dist/host/flow/kit.js +13 -0
- package/dist/host/flow/run.d.ts +13 -0
- package/dist/host/flow/run.js +287 -0
- package/dist/host/flow/types.d.ts +188 -0
- package/dist/host/flow/types.js +1 -0
- package/dist/host/host-actions.d.ts +2 -0
- package/dist/host/host-actions.js +5 -0
- package/dist/host/host-kit.d.ts +19 -0
- package/dist/host/host-kit.js +14 -0
- package/dist/host/http.d.ts +2 -6
- package/dist/host/http.js +2 -2
- package/dist/host/installed-module.d.ts +23 -0
- package/dist/host/installed-module.js +264 -0
- package/dist/host/lib-install.d.ts +7 -9
- package/dist/host/lib-install.js +9 -12
- package/dist/host/lib-runners.d.ts +3 -5
- package/dist/host/lib-runners.js +2 -2
- package/dist/host/local-declaration.d.ts +7 -0
- package/dist/host/local-declaration.js +33 -0
- package/dist/host/module-root.d.ts +1 -0
- package/dist/host/module-root.js +5 -0
- package/dist/host/npm-registry.d.ts +10 -0
- package/dist/host/npm-registry.js +16 -0
- package/dist/host/opencli-runner.d.ts +2 -1
- package/dist/host/package-content.d.ts +7 -0
- package/dist/host/package-content.js +69 -0
- package/dist/host/package-kind.d.ts +13 -0
- package/dist/host/package-kind.js +51 -0
- package/dist/host/package-route.d.ts +31 -0
- package/dist/host/package-route.js +57 -0
- package/dist/host/paths.d.ts +11 -19
- package/dist/host/paths.js +16 -43
- package/dist/host/platform.js +1 -1
- package/dist/host/plugin-ledger.d.ts +8 -8
- package/dist/host/plugin-ledger.js +23 -16
- package/dist/host/plugin.d.ts +1 -2
- package/dist/host/plugin.js +4 -5
- package/dist/host/port-types.d.ts +42 -9
- package/dist/host/preflight.d.ts +3 -2
- package/dist/host/profile-create.d.ts +18 -0
- package/dist/host/profile-create.js +118 -0
- package/dist/host/profile-registries.d.ts +10 -0
- package/dist/host/profile-registries.js +150 -0
- package/dist/host/registry-declaration.d.ts +21 -0
- package/dist/host/registry-declaration.js +33 -0
- package/dist/host/release-probe.d.ts +34 -0
- package/dist/host/release-probe.js +88 -0
- package/dist/host/runner.d.ts +26 -0
- package/dist/host/runner.js +50 -0
- package/dist/host/shell.d.ts +0 -2
- package/dist/host/shell.js +0 -7
- package/dist/host/spec.js +1 -1
- package/dist/host/supply-index.d.ts +1 -0
- package/dist/host/supply-index.js +8 -7
- package/dist/host/supply-port.js +13 -3
- package/dist/host/unit-face.d.ts +2 -1
- package/dist/host/version.js +11 -6
- package/dist/host.d.ts +24 -6
- package/dist/host.js +23 -6
- package/dist/i18n/en.js +16 -19
- package/dist/i18n/index.d.ts +17 -20
- package/dist/i18n/runtime-terms.d.ts +15 -0
- package/dist/i18n/runtime-terms.js +22 -0
- package/dist/i18n/zh.d.ts +16 -19
- package/dist/i18n/zh.js +16 -19
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/testing/fake-adapter-set.d.ts +5 -0
- package/dist/testing/fake-adapter-set.js +150 -0
- package/dist/testing/needs.js +1 -1
- package/dist/testing.d.ts +1 -0
- package/dist/testing.js +1 -0
- package/package.json +1 -1
- package/src/api/__tests__/blocker-table.test.ts +25 -0
- package/src/api/{workbench-client.test.ts → __tests__/workbench-client.test.ts} +2 -2
- package/src/api/blocker-table.ts +6 -2
- package/src/components/SelectField.css +0 -6
- package/src/components/SelectField.tsx +7 -36
- package/src/components/SelectFieldNodes.tsx +4 -1
- package/src/components/{Card.test.tsx → __tests__/Card.test.tsx} +2 -2
- package/src/components/{CardAnchor.test.tsx → __tests__/CardAnchor.test.tsx} +3 -3
- package/src/components/{CardHead.test.tsx → __tests__/CardHead.test.tsx} +1 -1
- package/src/components/{ChoiceGroup.test.tsx → __tests__/ChoiceGroup.test.tsx} +2 -2
- package/src/components/{ConfirmBar.test.tsx → __tests__/ConfirmBar.test.tsx} +5 -5
- package/src/components/{Field.test.tsx → __tests__/Field.test.tsx} +1 -1
- package/src/components/{Form.test.tsx → __tests__/Form.test.tsx} +5 -5
- package/src/components/{FormActions.test.tsx → __tests__/FormActions.test.tsx} +3 -3
- package/src/components/{FormRows.test.tsx → __tests__/FormRows.test.tsx} +2 -2
- package/src/components/{FormTypes.test.ts → __tests__/FormTypes.test.ts} +1 -1
- package/src/components/{FormWiring.test.tsx → __tests__/FormWiring.test.tsx} +4 -4
- package/src/components/{Link.test.tsx → __tests__/Link.test.tsx} +1 -1
- package/src/components/{Rows.test.tsx → __tests__/Rows.test.tsx} +2 -2
- package/src/components/{SelectField.test.tsx → __tests__/SelectField.test.tsx} +7 -37
- package/src/components/{Spinner.test.tsx → __tests__/Spinner.test.tsx} +4 -4
- package/src/config/{constants.test.ts → __tests__/constants.test.ts} +1 -1
- package/src/config/{storage.test.ts → __tests__/storage.test.ts} +1 -1
- package/src/config/constants.ts +4 -4
- package/src/contract/capabilities.ts +30 -0
- package/src/contract/needs.ts +4 -4
- package/src/contract/plugin-source.ts +24 -0
- package/src/contract/protocol/bundle-install.ts +7 -1
- package/src/contract/protocol/platform.ts +1 -1
- package/src/contract/runner.ts +6 -0
- package/src/contract/unit.ts +4 -4
- package/src/host/{extension.test.ts → __tests__/extension.test.ts} +4 -4
- package/src/host/{install-failure.test.ts → __tests__/install-failure.test.ts} +1 -1
- package/src/host/{installed-sources.test.ts → __tests__/installed-sources.test.ts} +1 -1
- package/src/host/__tests__/lib-runners.test.ts +11 -2
- package/src/host/__tests__/plugin-ledger.test.ts +19 -6
- package/src/host/{recipe.test.ts → __tests__/recipe.test.ts} +3 -3
- package/src/host/__tests__/runtime-layout.test.ts +14 -0
- package/src/host/__tests__/supply-index.test.ts +2 -2
- package/src/host/__tests__/supply-port.test.ts +32 -0
- package/src/host/adapter-contract.ts +93 -0
- package/src/host/adapter-set.ts +6 -4
- package/src/host/adapter-types.ts +1 -1
- package/src/host/bundle-manager.ts +195 -0
- package/src/host/cli.ts +3 -1
- package/src/host/config-sync.ts +386 -0
- package/src/host/dsh-plugins.ts +60 -0
- package/src/host/extension.ts +1 -1
- package/src/host/flow/__tests__/advanced.test.ts +302 -0
- package/src/host/flow/__tests__/check.test.ts +169 -0
- package/src/host/flow/__tests__/run.test.ts +301 -0
- package/src/host/flow/check.ts +216 -0
- package/src/host/flow/expr.ts +94 -0
- package/src/host/flow/kit.ts +30 -0
- package/src/host/flow/run.ts +325 -0
- package/src/host/flow/types.ts +199 -0
- package/src/host/host-actions.ts +7 -0
- package/src/host/host-kit.ts +26 -0
- package/src/host/http.ts +6 -8
- package/src/host/installed-module.ts +272 -0
- package/src/host/lib-install.ts +14 -12
- package/src/host/lib-runners.ts +6 -8
- package/src/host/local-declaration.ts +37 -0
- package/src/host/module-root.ts +6 -0
- package/src/host/npm-registry.ts +21 -0
- package/src/host/package-content.ts +76 -0
- package/src/host/package-kind.ts +63 -0
- package/src/host/package-route.ts +93 -0
- package/src/host/paths.ts +27 -52
- package/src/host/platform.ts +1 -1
- package/src/host/plugin-ledger.ts +25 -18
- package/src/host/plugin.ts +4 -5
- package/src/host/port-types.ts +43 -9
- package/src/host/preflight.ts +3 -2
- package/src/host/profile-create.ts +133 -0
- package/src/host/profile-registries.ts +145 -0
- package/src/host/registry-declaration.ts +70 -0
- package/src/host/release-probe.ts +202 -0
- package/src/host/shell.ts +0 -9
- package/src/host/spec.ts +1 -1
- package/src/host/supply-index.ts +9 -7
- package/src/host/supply-port.ts +13 -3
- package/src/host/unit-face.ts +2 -1
- package/src/host/version.ts +12 -7
- package/src/host.ts +34 -6
- package/src/i18n/{index.test.ts → __tests__/index.test.ts} +3 -3
- package/src/i18n/{merge.test.ts → __tests__/merge.test.ts} +1 -1
- package/src/i18n/{zh.test.ts → __tests__/zh.test.ts} +2 -2
- package/src/i18n/en.ts +16 -20
- package/src/i18n/runtime-terms.ts +26 -0
- package/src/i18n/zh.ts +16 -20
- package/src/index.ts +4 -0
- package/src/lib/{board-fact.test.ts → __tests__/board-fact.test.ts} +1 -1
- package/src/lib/{busy.test.ts → __tests__/busy.test.ts} +1 -1
- package/src/lib/{form-problem.test.ts → __tests__/form-problem.test.ts} +1 -1
- package/src/lib/{git-address.test.ts → __tests__/git-address.test.ts} +1 -1
- package/src/lib/{react.test.ts → __tests__/react.test.ts} +1 -1
- package/src/lib/{text.test.ts → __tests__/text.test.ts} +1 -1
- package/src/lib/{ui.test.ts → __tests__/ui.test.ts} +1 -1
- package/src/testing/__tests__/fake-adapter-set.test.ts +28 -0
- package/src/testing/fake-adapter-set.ts +173 -0
- package/src/testing/needs.ts +1 -1
- package/src/testing.ts +1 -0
- package/src/host/__tests__/command-name.test.ts +0 -16
- package/src/host/__tests__/opencli-runner.test.ts +0 -120
- package/src/host/channel-plugin.ts +0 -55
- package/src/host/command-name.ts +0 -15
- package/src/host/opencli-runner.ts +0 -76
- package/src/host/paths.test.ts +0 -26
- package/src/host/plugin-source.ts +0 -137
- package/src/host/runtime-links.test.ts +0 -31
- package/src/host/runtime-links.ts +0 -23
|
@@ -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
|
+
}
|
package/src/i18n/zh.ts
CHANGED
|
@@ -78,7 +78,11 @@ export const zh = {
|
|
|
78
78
|
|
|
79
79
|
'panels.title': '面板模块',
|
|
80
80
|
'panels.groupBoardless': '无看板模块',
|
|
81
|
-
'panels.groupPlugins': '
|
|
81
|
+
'panels.groupPlugins': '插件',
|
|
82
|
+
'runtime.plugin': '插件',
|
|
83
|
+
'runtime.pluginOf': '运行时提供的插件',
|
|
84
|
+
'runtime.pluginThing': '插件',
|
|
85
|
+
'runtime.pluginArea': '插件区',
|
|
82
86
|
'panels.market.defaultHint': '打开后所有工作区默认开启,也可在工作区面板配置中单独关闭',
|
|
83
87
|
'panels.market.defaultLabel': '全局默认开启',
|
|
84
88
|
'panels.market.defaultLocked': '这份现在用不了,先更新或卸载',
|
|
@@ -156,10 +160,6 @@ export const zh = {
|
|
|
156
160
|
'panels.market.confirmNo': '取消',
|
|
157
161
|
'panels.market.noMarketSource': '索引里没有这个模块的新版本(刷新一次索引再试)',
|
|
158
162
|
'panels.market.failed': '没成功:{reason}',
|
|
159
|
-
'panels.market.askAgent': '问 agent',
|
|
160
|
-
'panels.market.askAgentCopied': '已复制',
|
|
161
|
-
'panels.market.askAgentText':
|
|
162
|
-
'工作台里「{name}」(单元 {unit})用不了:{reason}。帮我查清缺什么、怎么补,补完确认真能用。',
|
|
163
163
|
'panels.market.failure.permission': '没装上:没有写入权限',
|
|
164
164
|
'panels.market.failure.diskFull': '没装上:磁盘满了',
|
|
165
165
|
'panels.market.failure.sandbox': '没装上:被系统拦住了',
|
|
@@ -199,7 +199,7 @@ export const zh = {
|
|
|
199
199
|
'panels.market.noSource': '这个模块没声明怎么装,只能自己动手',
|
|
200
200
|
'panels.market.badSource': '安装方式没写全,只能自己动手',
|
|
201
201
|
'panels.market.empty': '暂无可用模块',
|
|
202
|
-
'panels.market.sectionPlugins': '
|
|
202
|
+
'panels.market.sectionPlugins': '已安装的插件',
|
|
203
203
|
'panels.market.moduleOutdated': '有新版',
|
|
204
204
|
'panels.market.updateNeedsWorkbench': '新版本要先把工作台升级才能用',
|
|
205
205
|
'panels.market.updateIncompatible': '新版本和当前的工作台不匹配,暂时不能更新',
|
|
@@ -237,7 +237,7 @@ export const zh = {
|
|
|
237
237
|
'panels.market.justNow': '刚刚',
|
|
238
238
|
'panels.market.minutesAgo': '{n} 分钟前',
|
|
239
239
|
'panels.market.hoursAgo': '{n} 小时前',
|
|
240
|
-
'panels.market.add.description': '输入模块或
|
|
240
|
+
'panels.market.add.description': '输入模块或 {plugin}的包名、Git 仓库地址或本地目录路径。',
|
|
241
241
|
'panels.market.add.specPlaceholder': '例如 @dsh-workbench/module-hello',
|
|
242
242
|
'panels.market.add.guideToggle': '模块安装引导和示例',
|
|
243
243
|
'panels.market.add.guideHide': '收起引导',
|
|
@@ -277,13 +277,13 @@ export const zh = {
|
|
|
277
277
|
'panels.market.add.problemNotFound': '未找到相关模块',
|
|
278
278
|
'panels.market.add.problemNotPackage': '该路径不存在或不是有效的模块包',
|
|
279
279
|
'panels.market.add.problemNotBundle':
|
|
280
|
-
'这个包装不进这里:{reason}。这个入口按包名认工作台模块和
|
|
280
|
+
'这个包装不进这里:{reason}。这个入口按包名认工作台模块和 {plugin},两种都能装;但 git 地址和 tarball 判断不出它是什么 —— 要是它其实是这两样之一,请改填包名或本地目录。',
|
|
281
281
|
'panels.market.add.problemNotWorkbenchModule': '这个包跟现在的工作台对不上,装上也用不了。换一个版本,或者找这个模块的作者。',
|
|
282
|
-
'panels.market.add.problemNotModuleOrPlugin': '这个包既不是工作台的模块,也不是
|
|
283
|
-
'panels.market.add.failureNotModuleOrPlugin': '这个包既不是工作台的模块,也不是
|
|
284
|
-
'panels.market.add.installedPlugin': '装上了:
|
|
282
|
+
'panels.market.add.problemNotModuleOrPlugin': '这个包既不是工作台的模块,也不是 {pluginOf},装了也用不上。换一个版本,或者找这个包的作者。',
|
|
283
|
+
'panels.market.add.failureNotModuleOrPlugin': '这个包既不是工作台的模块,也不是 {pluginOf},装了也用不上',
|
|
284
|
+
'panels.market.add.installedPlugin': '装上了:{plugin} {name}',
|
|
285
285
|
'panels.market.add.pluginPreview':
|
|
286
|
-
'这是一份
|
|
286
|
+
'这是一份 {pluginThing}:装进 {pluginArea},装完就能用,不用重启。',
|
|
287
287
|
'panels.market.add.pluginAlias': '插件名:{name}',
|
|
288
288
|
'panels.market.add.problemNetwork': '无法连接模块源,请检查网络后重试',
|
|
289
289
|
'panels.market.add.problemUnknown': '无法获取模块信息:{reason}',
|
|
@@ -560,7 +560,7 @@ export const zh = {
|
|
|
560
560
|
'blocker.forbidden.hint': '账号已登录,但没有这个项目/仓库的权限;申请之后再试。',
|
|
561
561
|
'blocker.forbidden.open': '打开权限页',
|
|
562
562
|
'blocker.timeout.hint': '后台浏览器没起来,重试一次通常就好。',
|
|
563
|
-
'blocker.exec.hint': '
|
|
563
|
+
'blocker.exec.hint': '确认运行时已经装好、在终端里能直接跑起来。',
|
|
564
564
|
'blocker.unknown.title': '出了点问题',
|
|
565
565
|
'panels.reading': '正在读取面板配置…',
|
|
566
566
|
'panels.cardBroken': '这个模块起不来:{unit}({reason})',
|
|
@@ -577,16 +577,11 @@ export const zh = {
|
|
|
577
577
|
'env.stepFailed.shell': '连不上命令行',
|
|
578
578
|
'env.toolchain.ok': '正常',
|
|
579
579
|
'env.toolchain.warn': '还没完全准备好',
|
|
580
|
-
'env.
|
|
580
|
+
'env.runtime.ok': '已就绪',
|
|
581
581
|
'env.bridge.ok': '已连接',
|
|
582
582
|
'env.bridge.short': '还没连上',
|
|
583
583
|
'env.bridge.why': '面板的数据要用你浏览器里的登录状态。这个连接没通,模块就取不到数据;不拦路:修好它,或者先进入工作台。',
|
|
584
584
|
'env.bridge.hold': '浏览器还没连上 —— 按下面修好后点「重试」,也可以先进工作台。',
|
|
585
|
-
'env.bridge.daemon': '后台服务没起来:正常它会自己起,先在终端跑下面这条命令。',
|
|
586
|
-
'env.bridge.extension': '浏览器扩展没连上:确认浏览器开着、扩展已启用;没装就从商店装一次(装完回来点「重试」)。',
|
|
587
|
-
'env.bridge.extensionLink': '去 Chrome 应用商店装扩展',
|
|
588
|
-
'env.bridge.profile': '连上了多个浏览器账号,但没选默认的那个:先看有哪些(下面这条命令),再选一个。',
|
|
589
|
-
'env.bridge.stale': '默认的浏览器账号已经失效(它记的是上一次的扩展实例):重新选一个。',
|
|
590
585
|
'env.bridge.unknown': '没认出是哪种问题:展开原文,按里面的步骤处理。',
|
|
591
586
|
'env.fix.title': '怎么解决',
|
|
592
587
|
'env.fix.why': '工作台用它自己那份后台工具(与你在终端里那套分开装,互不影响),这台机器上还没装好它。',
|
|
@@ -616,7 +611,8 @@ export const zh = {
|
|
|
616
611
|
'env.copyCommand': '复制命令',
|
|
617
612
|
'env.copied': '已复制',
|
|
618
613
|
'env.step.toolchain': '运行环境',
|
|
619
|
-
'env.step.
|
|
614
|
+
'env.step.adapter': '运行时',
|
|
615
|
+
'env.step.runtime': '后台工具',
|
|
620
616
|
'env.step.bridge': '浏览器连接',
|
|
621
617
|
'env.step.diy': 'DIY 模式',
|
|
622
618
|
'env.step.warm': '站点登录状态',
|
package/src/index.ts
CHANGED
|
@@ -42,6 +42,8 @@ export * from './contract/extension.js';
|
|
|
42
42
|
export * from './contract/lib.js';
|
|
43
43
|
export * from './contract/git.js';
|
|
44
44
|
export * from './contract/command.js';
|
|
45
|
+
export * from './contract/runner.js';
|
|
46
|
+
export * from './contract/plugin-source.js';
|
|
45
47
|
export * from './contract/facts.js';
|
|
46
48
|
export * from './contract/long-task.js';
|
|
47
49
|
export * from './contract/protocol/bundle-install.js';
|
|
@@ -57,12 +59,14 @@ export * from './contract/protocol/unit-manifests.js';
|
|
|
57
59
|
export * from './contract/unit-board.js';
|
|
58
60
|
export * from './contract/unit-declaration.js';
|
|
59
61
|
export * from './contract/unit.js';
|
|
62
|
+
export * from './contract/capabilities.js';
|
|
60
63
|
export * from './contract/needs.js';
|
|
61
64
|
export * from './contract/unit-capability.js';
|
|
62
65
|
export * from './contract/workbench-contract.js';
|
|
63
66
|
export * from './i18n/en.js';
|
|
64
67
|
export * from './i18n/index.js';
|
|
65
68
|
export * from './i18n/merge.js';
|
|
69
|
+
export * from './i18n/runtime-terms.js';
|
|
66
70
|
export * from './i18n/settings.js';
|
|
67
71
|
export * from './i18n/zh.js';
|
|
68
72
|
export * from './lib/blocker-action.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { boardSituation, insideWorkshop, type BoardProbe } from '
|
|
2
|
+
import { boardSituation, insideWorkshop, type BoardProbe } from '../board-fact.js';
|
|
3
3
|
|
|
4
4
|
const WORKSHOP = '/w/workbench/diy';
|
|
5
5
|
const FL0XIN = '/w/floxin-app';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { firstProblemOf, revealFirstProblem } from '
|
|
2
|
+
import { firstProblemOf, revealFirstProblem } from '../form-problem.js';
|
|
3
3
|
|
|
4
4
|
function rootWith(problem: unknown): { readonly root: ParentNode; readonly queries: string[] } {
|
|
5
5
|
const queries: string[] = [];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { gitAddressOf, gitAddressText, gitTransportCandidates } from '
|
|
2
|
+
import { gitAddressOf, gitAddressText, gitTransportCandidates } from '../git-address.js';
|
|
3
3
|
|
|
4
4
|
describe('git 地址的两种读法', () => {
|
|
5
5
|
it('https 原样留着,另一条推成 ssh://', () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import { Fragment, h, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from '
|
|
3
|
+
import { Fragment, h, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from '../react.js';
|
|
4
4
|
|
|
5
5
|
describe('react 入口', () => {
|
|
6
6
|
it('h 就是 React.createElement(同一个引用,不是包装)', () => {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { adapterContractGaps } from '../../host/adapter-contract.js';
|
|
3
|
+
import { fakeAdapterSet } from '../fake-adapter-set.js';
|
|
4
|
+
|
|
5
|
+
const ctx = { get: () => undefined } as never;
|
|
6
|
+
|
|
7
|
+
describe('假集合:证明协议是闭合的', () => {
|
|
8
|
+
it('一个最小的外来实现(不含任何具体运行时)也能过整份契约', () => {
|
|
9
|
+
expect(adapterContractGaps(fakeAdapterSet(), ctx)).toEqual([]);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('它声明的能力全是 false —— 外壳不该能从它身上猜到任何具体运行时', () => {
|
|
13
|
+
expect(fakeAdapterSet().runtime.capabilities(ctx)).toEqual({
|
|
14
|
+
console: false,
|
|
15
|
+
pluginMarket: false,
|
|
16
|
+
browserBridge: false,
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('覆盖项能替换任意成员(测试就地造场景)', () => {
|
|
21
|
+
const set = fakeAdapterSet({ runtime: { ...fakeAdapterSet().runtime, capabilities: () => ({
|
|
22
|
+
console: true,
|
|
23
|
+
pluginMarket: false,
|
|
24
|
+
browserBridge: false,
|
|
25
|
+
}) } });
|
|
26
|
+
expect(set.runtime.capabilities(ctx).console).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type { DataReader, DataRequest, DataResult, RecipeCalls, RecipeResults, RequestRecipe } from '../contract/data.js';
|
|
2
|
+
import type { AdapterSet } from '../host/adapters.js';
|
|
3
|
+
import type { EnvSupplyPort, ModulePort, ProbePort, ProfilePort, RuntimePort, SupplyPort } from '../host/port-types.js';
|
|
4
|
+
import type { HostActions } from '../host/port-types.js';
|
|
5
|
+
import type { PluginSurface } from '../host/port-types.js';
|
|
6
|
+
import { ADAPTER_SET_API_VERSION } from '../host/adapters.js';
|
|
7
|
+
import { readBundleManager } from '../host/bundle-manager.js';
|
|
8
|
+
import {
|
|
9
|
+
declarationGaps,
|
|
10
|
+
moduleDeclarationOf,
|
|
11
|
+
profileDirOf,
|
|
12
|
+
profileModules,
|
|
13
|
+
readInstalledModule,
|
|
14
|
+
readProfilePackage,
|
|
15
|
+
} from '../host/installed-module.js';
|
|
16
|
+
import { isNewerVersion } from '../host/version.js';
|
|
17
|
+
import { refusalReason } from '../host/spec.js';
|
|
18
|
+
import { declarationRefusalOf, pathDeclarationOf } from '../host/local-declaration.js';
|
|
19
|
+
import { createRegistryLookup } from '../host/registry-declaration.js';
|
|
20
|
+
import { createPackageRouter } from '../host/package-route.js';
|
|
21
|
+
import { createReleaseProbe } from '../host/release-probe.js';
|
|
22
|
+
|
|
23
|
+
export const FAKE_ADAPTER_MARKER = { adapterSet: 'fake' } as const;
|
|
24
|
+
|
|
25
|
+
function result<T>(): DataResult<T> {
|
|
26
|
+
return { ok: true, data: FAKE_ADAPTER_MARKER as unknown as T };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const data: DataReader = {
|
|
30
|
+
async read<T = unknown>(_request: DataRequest): Promise<DataResult<T>> {
|
|
31
|
+
return result<T>();
|
|
32
|
+
},
|
|
33
|
+
async request<T = unknown>(_recipe: RequestRecipe): Promise<DataResult<T>> {
|
|
34
|
+
return result<T>();
|
|
35
|
+
},
|
|
36
|
+
async requestMany<T = unknown>(_recipe: RequestRecipe, _calls: RecipeCalls): Promise<RecipeResults<T>> {
|
|
37
|
+
return {};
|
|
38
|
+
},
|
|
39
|
+
async submit<T = unknown>(_recipe: RequestRecipe): Promise<DataResult<T>> {
|
|
40
|
+
return result<T>();
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const supply: EnvSupplyPort = {
|
|
45
|
+
resolvePluginSource: () => ({ ok: false, problem: 'not-declared' }),
|
|
46
|
+
libInstallCommand: () => '',
|
|
47
|
+
libUninstallCommand: () => '',
|
|
48
|
+
pluginInstallPlan: () => ({ ok: false, problem: 'not-declared' }),
|
|
49
|
+
latestVersion: async () => undefined,
|
|
50
|
+
missingReleaseParts: async () => [],
|
|
51
|
+
probeRelease: async () => undefined,
|
|
52
|
+
splitNpmSpec: () => undefined,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const modules: ModulePort = {
|
|
56
|
+
isolatedRoot: () => '/tmp/dsh-workbench-fake',
|
|
57
|
+
isolatedHome: () => '/tmp/dsh-workbench-fake/home',
|
|
58
|
+
pluginPackagePrefix: () => 'fake-plugin-',
|
|
59
|
+
pluginManifestFile: () => 'fake-plugin.json',
|
|
60
|
+
cliCatalogPath: () => '/tmp/dsh-workbench-fake/catalog.json',
|
|
61
|
+
vendorPluginsDir: () => '/tmp/dsh-workbench-fake/plugins',
|
|
62
|
+
runtimeInstallCommand: () => 'npm i -g fake-runtime',
|
|
63
|
+
afterLibInstall: () => undefined,
|
|
64
|
+
profileDirOf: (ctx) => profileDirOf(ctx),
|
|
65
|
+
profileModules,
|
|
66
|
+
readInstalledModule,
|
|
67
|
+
readProfilePackage,
|
|
68
|
+
readBundleManager,
|
|
69
|
+
createRegistryNotebook: () => ({}) as never,
|
|
70
|
+
declarationGaps,
|
|
71
|
+
moduleDeclarationOf,
|
|
72
|
+
registryDeclarationOf: async () => undefined,
|
|
73
|
+
pathDeclarationOf,
|
|
74
|
+
declarationRefusalOf,
|
|
75
|
+
refusalReason,
|
|
76
|
+
packageRouteOf: async () => undefined,
|
|
77
|
+
isNewerVersion,
|
|
78
|
+
startSelfUpdate: () => ({}) as never,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const profile: ProfilePort = {
|
|
82
|
+
dirOf: () => null,
|
|
83
|
+
dshPlugins: () => () => ({ ok: true, plugins: [] }),
|
|
84
|
+
selfUpdateView: () => undefined,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const probe: ProbePort = {
|
|
88
|
+
probeUnits: async () => [],
|
|
89
|
+
registerPreflight: () => undefined,
|
|
90
|
+
declaredPluginCommands: () => new Map(),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const runtimePreset = (capabilities: RuntimePort['capabilities'] extends never ? never : ReturnType<RuntimePort['capabilities']>): RuntimePort => ({
|
|
94
|
+
packageRoot: () => '/tmp/dsh-workbench-fake',
|
|
95
|
+
capabilities: () => capabilities,
|
|
96
|
+
profile: () => ({
|
|
97
|
+
label: 'fake',
|
|
98
|
+
terms: {
|
|
99
|
+
plugin: { zh: '插件', en: 'plugin' },
|
|
100
|
+
pluginOf: { zh: '的插件', en: 'a plugin of' },
|
|
101
|
+
pluginThing: { zh: '插件', en: 'a plugin' },
|
|
102
|
+
pluginArea: { zh: '插件区', en: 'the plugin area' },
|
|
103
|
+
},
|
|
104
|
+
installHint: { zh: '无', en: 'nothing to install' },
|
|
105
|
+
diagnose: {},
|
|
106
|
+
}),
|
|
107
|
+
commandSurface: () => ({
|
|
108
|
+
name: 'fake',
|
|
109
|
+
pluginPrefix: 'fake-plugin-',
|
|
110
|
+
invoke: (site: string, command?: string) => (command === undefined ? `fake ${site}` : `fake ${site} ${command}`),
|
|
111
|
+
help: (site: string) => `fake ${site} --help`,
|
|
112
|
+
browser: (verb: readonly string[]) => `fake browser ${verb.join(' ')}`,
|
|
113
|
+
}),
|
|
114
|
+
commands: () => ({}),
|
|
115
|
+
runners: () => ({}),
|
|
116
|
+
health: () => ({}),
|
|
117
|
+
seed: () => undefined,
|
|
118
|
+
warnUnwritableRoot: () => undefined,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const plugins: PluginSurface = {
|
|
122
|
+
declarationLookup: () => () => undefined,
|
|
123
|
+
createUnits: () => ({
|
|
124
|
+
refresh: async () => ({ units: [] }),
|
|
125
|
+
touch: () => undefined,
|
|
126
|
+
report: () => ({ units: [] }),
|
|
127
|
+
dispose: () => undefined,
|
|
128
|
+
}),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const actions: HostActions = {
|
|
132
|
+
configSyncHandler: () => (async () => ({ ok: true })) as never,
|
|
133
|
+
profileCreateHandler: () => (async () => ({ ok: true })) as never,
|
|
134
|
+
configSyncAction: () => ({}) as never,
|
|
135
|
+
profileCreateAction: () => ({}) as never,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export function fakeAdapterSet(overrides: Partial<AdapterSet> = {}): AdapterSet {
|
|
139
|
+
const release = createReleaseProbe();
|
|
140
|
+
const registry = createRegistryLookup();
|
|
141
|
+
const router = createPackageRouter({ manifestFile: 'fake-plugin.json', dshLookup: registry.registryDshOf });
|
|
142
|
+
const withRelease: EnvSupplyPort = {
|
|
143
|
+
...supply,
|
|
144
|
+
latestVersion: release.latestVersion,
|
|
145
|
+
missingReleaseParts: release.missingReleaseParts,
|
|
146
|
+
probeRelease: release.probeRelease,
|
|
147
|
+
};
|
|
148
|
+
return {
|
|
149
|
+
apiVersion: ADAPTER_SET_API_VERSION,
|
|
150
|
+
id: 'fake-adapter-set',
|
|
151
|
+
version: '0.0.0',
|
|
152
|
+
data,
|
|
153
|
+
supply: {
|
|
154
|
+
...withRelease,
|
|
155
|
+
...modules,
|
|
156
|
+
env: withRelease,
|
|
157
|
+
modules,
|
|
158
|
+
registryDeclarationOf: registry.registryDeclarationOf,
|
|
159
|
+
packageRouteOf: router.packageRouteOf,
|
|
160
|
+
} as SupplyPort,
|
|
161
|
+
modules,
|
|
162
|
+
profile,
|
|
163
|
+
probe,
|
|
164
|
+
runtime: runtimePreset({
|
|
165
|
+
console: false,
|
|
166
|
+
pluginMarket: false,
|
|
167
|
+
browserBridge: false,
|
|
168
|
+
}),
|
|
169
|
+
plugins,
|
|
170
|
+
actions,
|
|
171
|
+
...overrides,
|
|
172
|
+
};
|
|
173
|
+
}
|
package/src/testing/needs.ts
CHANGED
|
@@ -21,7 +21,7 @@ export const fakeNpmSource: SupplySource = {
|
|
|
21
21
|
export function extensionNeedOf(name: string, extra: Partial<ExtensionNeed> = {}): ExtensionNeed {
|
|
22
22
|
return {
|
|
23
23
|
kind: 'extension',
|
|
24
|
-
runtime: '
|
|
24
|
+
runtime: 'fake-runtime',
|
|
25
25
|
name,
|
|
26
26
|
commands: [`${name}-read`],
|
|
27
27
|
purpose: '测试',
|
package/src/testing.ts
CHANGED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { commandNamesMatch } from '../command-name.js';
|
|
3
|
-
|
|
4
|
-
describe('commandNamesMatch:声明的命令名与解析出来的命令路径', () => {
|
|
5
|
-
it('缺省 opencli 不管写成名字还是绝对路径都算同一个', () => {
|
|
6
|
-
expect(commandNamesMatch('opencli', '/Users/me/.dsh/workbench/opencli/bin/opencli')).toBe(true);
|
|
7
|
-
expect(commandNamesMatch('opencli.cmd', 'C:\\dsh\\opencli.cmd')).toBe(true);
|
|
8
|
-
expect(commandNamesMatch('opencli.exe', 'C:\\dsh\\opencli.exe')).toBe(true);
|
|
9
|
-
expect(commandNamesMatch('gitops', '/usr/local/bin/gitops')).toBe(true);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('不同的命令不算同一个', () => {
|
|
13
|
-
expect(commandNamesMatch('gitops', '/usr/local/bin/opencli')).toBe(false);
|
|
14
|
-
expect(commandNamesMatch('', 'opencli')).toBe(false);
|
|
15
|
-
});
|
|
16
|
-
});
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import type { DataRequest, DataResult } from '../../contract/data.js';
|
|
3
|
-
import { opencliRunnerOf } from '../opencli-runner.js';
|
|
4
|
-
|
|
5
|
-
type Read = (request: DataRequest) => Promise<DataResult<unknown>>;
|
|
6
|
-
|
|
7
|
-
function readerOf(result: DataResult<unknown>): {
|
|
8
|
-
readonly read: Read;
|
|
9
|
-
readonly spy: ReturnType<typeof vi.fn>;
|
|
10
|
-
} {
|
|
11
|
-
const spy = vi.fn(async (_request: DataRequest) => result);
|
|
12
|
-
return { read: spy as unknown as Read, spy };
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
describe('opencliRunnerOf', () => {
|
|
16
|
-
it('把 argv 原样交给读缝,解析后的值序列化回 stdout', async () => {
|
|
17
|
-
const { read, spy } = readerOf({ ok: true, data: [{ name: 'lsznh' }] });
|
|
18
|
-
const runner = opencliRunnerOf(read);
|
|
19
|
-
|
|
20
|
-
await expect(
|
|
21
|
-
runner(['argocd', 'application-resource-tree', '--name', 'lsznh']),
|
|
22
|
-
).resolves.toEqual({ stdout: JSON.stringify([{ name: 'lsznh' }]) });
|
|
23
|
-
expect(spy).toHaveBeenCalledWith({
|
|
24
|
-
kind: 'command',
|
|
25
|
-
adapter: 'opencli',
|
|
26
|
-
argv: ['argocd', 'application-resource-tree', '--name', 'lsznh'],
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('timeoutMs 透传给读缝;不传就不出现这一格', async () => {
|
|
31
|
-
const withTimeout = readerOf({ ok: true, data: [] });
|
|
32
|
-
await opencliRunnerOf(withTimeout.read, { timeoutMs: 1_800_000 })([
|
|
33
|
-
'coding',
|
|
34
|
-
'ci-status',
|
|
35
|
-
]);
|
|
36
|
-
expect(withTimeout.spy).toHaveBeenCalledWith(
|
|
37
|
-
expect.objectContaining({ timeoutMs: 1_800_000 }),
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
const withoutTimeout = readerOf({ ok: true, data: [] });
|
|
41
|
-
await opencliRunnerOf(withoutTimeout.read)(['coding', 'ci-status']);
|
|
42
|
-
expect(withoutTimeout.spy).toHaveBeenCalledWith(
|
|
43
|
-
expect.not.objectContaining({ timeoutMs: expect.anything() }),
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('未登录还原成 opencli 的约定:退出码 77 + AUTH_REQUIRED + 登录地址', async () => {
|
|
48
|
-
const { read } = readerOf({
|
|
49
|
-
ok: false,
|
|
50
|
-
error: {
|
|
51
|
-
code: 'auth',
|
|
52
|
-
pageCode: 'auth-required',
|
|
53
|
-
message: 'Coding 未登录',
|
|
54
|
-
helpUrl: 'https://baiguokeji.coding.net/login',
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const failure = await opencliRunnerOf(read)(['coding', 'ci-status']).then(
|
|
59
|
-
() => null,
|
|
60
|
-
(error: unknown) => error as Error & { code?: number },
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
expect(failure).toBeInstanceOf(Error);
|
|
64
|
-
expect(failure?.code).toBe(77);
|
|
65
|
-
expect(failure?.message).toBe(
|
|
66
|
-
[
|
|
67
|
-
'Command failed: opencli coding ci-status',
|
|
68
|
-
'code: AUTH_REQUIRED',
|
|
69
|
-
'message: Coding 未登录',
|
|
70
|
-
'help: Please open Chrome or Chromium and log in to https://baiguokeji.coding.net/login',
|
|
71
|
-
].join('\n'),
|
|
72
|
-
);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('其它失败保留 Command failed 外壳与原因 / 细节 / 下一步', async () => {
|
|
76
|
-
const { read } = readerOf({
|
|
77
|
-
ok: false,
|
|
78
|
-
error: {
|
|
79
|
-
code: 'command',
|
|
80
|
-
pageCode: 'cli-command-failed',
|
|
81
|
-
message: 'UNKNOWN:插件里没有这条命令',
|
|
82
|
-
detail: 'error: unknown command',
|
|
83
|
-
hint: '升级插件后重试',
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
const failure = await opencliRunnerOf(read)([
|
|
88
|
-
'argocd',
|
|
89
|
-
'application-resource-tree',
|
|
90
|
-
]).then(
|
|
91
|
-
() => null,
|
|
92
|
-
(error: unknown) => error as Error,
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
expect(failure?.message).toBe(
|
|
96
|
-
[
|
|
97
|
-
'Command failed: opencli argocd application-resource-tree',
|
|
98
|
-
'UNKNOWN:插件里没有这条命令',
|
|
99
|
-
'error: unknown command',
|
|
100
|
-
'升级插件后重试',
|
|
101
|
-
].join('\n'),
|
|
102
|
-
);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('没有原因时也有一句能看懂的兜底', async () => {
|
|
106
|
-
const { read } = readerOf({
|
|
107
|
-
ok: false,
|
|
108
|
-
error: { code: 'command', message: '' },
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
const failure = await opencliRunnerOf(read)(['cmdb', 'publish']).then(
|
|
112
|
-
() => null,
|
|
113
|
-
(error: unknown) => error as Error,
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
expect(failure?.message).toBe(
|
|
117
|
-
'Command failed: opencli cmdb publish\nopencli 这条命令没跑通',
|
|
118
|
-
);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { isolatedHome, toolchainOpencliDir, vendorPeerLink, vendorPluginsDir } from './paths.js';
|
|
4
|
-
import { quoteShellArg } from './shell-text.js';
|
|
5
|
-
|
|
6
|
-
export const CHANNEL_PLUGIN_NAME = 'opencli-plugin-workbench';
|
|
7
|
-
export const CHANNEL_PLUGIN_SITE = 'workbench';
|
|
8
|
-
|
|
9
|
-
export interface ChannelPluginPlan {
|
|
10
|
-
readonly dest: string;
|
|
11
|
-
readonly version: string;
|
|
12
|
-
readonly commands: readonly string[];
|
|
13
|
-
readonly line: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function textOf(value: unknown): string {
|
|
17
|
-
return typeof value === 'string' ? value.trim() : '';
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function channelVersionOf(dir: string): string | undefined {
|
|
21
|
-
try {
|
|
22
|
-
const parsed: unknown = JSON.parse(readFileSync(join(dir, 'opencli-plugin.json'), 'utf8'));
|
|
23
|
-
if (typeof parsed !== 'object' || parsed === null) return undefined;
|
|
24
|
-
const version = textOf((parsed as Record<string, unknown>)['version']);
|
|
25
|
-
return version === '' ? undefined : version;
|
|
26
|
-
} catch {
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function channelPluginPlan(options: {
|
|
32
|
-
readonly channelDir: string;
|
|
33
|
-
readonly command?: string;
|
|
34
|
-
}): ChannelPluginPlan | undefined {
|
|
35
|
-
const version = channelVersionOf(options.channelDir);
|
|
36
|
-
if (version === undefined) return undefined;
|
|
37
|
-
const command = options.command ?? 'opencli';
|
|
38
|
-
const dest = join(vendorPluginsDir(), CHANNEL_PLUGIN_NAME);
|
|
39
|
-
const peer = vendorPeerLink();
|
|
40
|
-
const commands = [
|
|
41
|
-
`rm -rf ${quoteShellArg(dest)} && mkdir -p ${quoteShellArg(dest)} && cp -R ${quoteShellArg(options.channelDir)}/. ${quoteShellArg(dest)}/`,
|
|
42
|
-
`rm -rf ${quoteShellArg(peer.dir)} && mkdir -p ${quoteShellArg(peer.parent)} && ln -s ${quoteShellArg(toolchainOpencliDir())} ${quoteShellArg(peer.dir)}`,
|
|
43
|
-
`(${quoteShellArg(command)} plugin uninstall ${quoteShellArg(CHANNEL_PLUGIN_SITE)} || true)`,
|
|
44
|
-
`${quoteShellArg(command)} plugin install ${quoteShellArg(dest)}`,
|
|
45
|
-
];
|
|
46
|
-
return { dest, version, commands, line: commands.join(' && ') };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function channelInstalledVersion(): string | undefined {
|
|
50
|
-
return channelVersionOf(join(vendorPluginsDir(), CHANNEL_PLUGIN_NAME));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function channelSiteInstalled(): boolean {
|
|
54
|
-
return existsSync(join(isolatedHome(), '.opencli', 'plugins', CHANNEL_PLUGIN_SITE));
|
|
55
|
-
}
|
package/src/host/command-name.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
function baseName(value: string): string {
|
|
2
|
-
const parts = value.trim().split(/[\\/]/);
|
|
3
|
-
return parts[parts.length - 1] ?? '';
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
function isOpencli(name: string): boolean {
|
|
7
|
-
return name === 'opencli' || name === 'opencli.cmd' || name === 'opencli.exe';
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function commandNamesMatch(declared: string, resolved: string): boolean {
|
|
11
|
-
const left = baseName(declared);
|
|
12
|
-
const right = baseName(resolved);
|
|
13
|
-
if (left === '' || right === '') return false;
|
|
14
|
-
return left === right || (isOpencli(left) && isOpencli(right));
|
|
15
|
-
}
|