@dsh-workbench/shared 1.1.3 → 1.1.5
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/host-write.d.ts +4 -1
- package/dist/api/workbench-client.d.ts +2 -0
- package/dist/components/ApprovalPlan.css +7 -8
- package/dist/config/constants.d.ts +4 -0
- package/dist/config/constants.js +4 -0
- package/dist/contract/approval.d.ts +4 -0
- package/dist/contract/approval.js +4 -0
- package/dist/contract/command.d.ts +33 -0
- package/dist/contract/command.js +1 -1
- package/dist/contract/needs.d.ts +17 -0
- package/dist/contract/needs.js +41 -1
- package/dist/contract/protocol/bundle-install.d.ts +4 -0
- package/dist/contract/protocol/market-index.d.ts +14 -0
- package/dist/contract/protocol/market-index.js +1 -0
- package/dist/contract/unit.d.ts +10 -0
- package/dist/host/adapter-types.d.ts +2 -0
- package/dist/host/data-error.d.ts +2 -0
- package/dist/host/data-error.js +3 -0
- package/dist/host/http.js +1 -0
- package/dist/host/install-failure.d.ts +49 -0
- package/dist/host/install-failure.js +167 -0
- package/dist/host/installed-sources.d.ts +10 -0
- package/dist/host/installed-sources.js +64 -0
- package/dist/host/lib-install.d.ts +12 -1
- package/dist/host/lib-install.js +33 -1
- package/dist/host/paths.d.ts +7 -0
- package/dist/host/paths.js +17 -0
- package/dist/host/runtime-links.d.ts +7 -0
- package/dist/host/runtime-links.js +25 -0
- package/dist/host/tool-approval.d.ts +4 -2
- package/dist/host/tool-approval.js +4 -2
- package/dist/host.d.ts +3 -0
- package/dist/host.js +3 -0
- package/dist/i18n/en.js +46 -1
- package/dist/i18n/index.d.ts +47 -2
- package/dist/i18n/zh.d.ts +46 -1
- package/dist/i18n/zh.js +46 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
- package/src/api/host-write.ts +5 -1
- package/src/api/workbench-client.ts +2 -0
- package/src/components/ApprovalPlan.css +7 -8
- package/src/config/constants.ts +6 -0
- package/src/contract/__tests__/needs.test.ts +53 -0
- package/src/contract/approval.ts +4 -0
- package/src/contract/command.ts +22 -0
- package/src/contract/needs.ts +56 -1
- package/src/contract/protocol/bundle-install.ts +5 -0
- package/src/contract/protocol/market-index.ts +17 -0
- package/src/contract/unit.ts +11 -0
- package/src/host/adapter-types.ts +2 -0
- package/src/host/data-error.ts +3 -1
- package/src/host/http.ts +1 -0
- package/src/host/install-failure.test.ts +121 -0
- package/src/host/install-failure.ts +198 -0
- package/src/host/installed-sources.test.ts +80 -0
- package/src/host/installed-sources.ts +73 -0
- package/src/host/lib-install.ts +38 -2
- package/src/host/paths.test.ts +26 -0
- package/src/host/paths.ts +18 -0
- package/src/host/runtime-links.test.ts +31 -0
- package/src/host/runtime-links.ts +23 -0
- package/src/host/tool-approval.ts +11 -4
- package/src/host.ts +3 -0
- package/src/i18n/en.ts +47 -1
- package/src/i18n/zh.ts +47 -1
- package/src/index.ts +1 -0
package/dist/i18n/zh.d.ts
CHANGED
|
@@ -155,7 +155,42 @@ export declare const zh: {
|
|
|
155
155
|
readonly 'panels.market.confirmUninstallAck': "我明白:卸载后想再用,得重新装一遍。";
|
|
156
156
|
readonly 'panels.market.confirmYes': "卸载";
|
|
157
157
|
readonly 'panels.market.confirmNo': "取消";
|
|
158
|
+
readonly 'panels.market.noMarketSource': "索引里没有这个模块的新版本(刷新一次索引再试)";
|
|
158
159
|
readonly 'panels.market.failed': "没成功:{reason}";
|
|
160
|
+
readonly 'panels.market.askAgent': "问 agent";
|
|
161
|
+
readonly 'panels.market.askAgentCopied': "已复制";
|
|
162
|
+
readonly 'panels.market.askAgentText': "工作台里「{name}」(单元 {unit})用不了:{reason}。帮我查清缺什么、怎么补,补完确认真能用。";
|
|
163
|
+
readonly 'panels.market.failure.permission': "没装上:没有写入权限";
|
|
164
|
+
readonly 'panels.market.failure.diskFull': "没装上:磁盘满了";
|
|
165
|
+
readonly 'panels.market.failure.sandbox': "没装上:被系统拦住了";
|
|
166
|
+
readonly 'panels.market.failure.unknown': "没装上:原因没认出来";
|
|
167
|
+
readonly 'panels.market.failure.factOwner': "{path} 属于 {owner},当前账号({me})写不进去。把它还给自己就行(会要开机密码)。";
|
|
168
|
+
readonly 'panels.market.failure.factMode': "{path} 的权限是 {mode},当前账号没有写权限。";
|
|
169
|
+
readonly 'panels.market.failure.factSpace': "{path} 所在的磁盘满了。";
|
|
170
|
+
readonly 'panels.market.failure.factStuck': "这条命令跑在受限环境里,写不进 {path}。";
|
|
171
|
+
readonly 'panels.market.failure.nowhere': "(没认出是哪个目录)";
|
|
172
|
+
readonly 'panels.market.failure.copyFix': "复制修复命令";
|
|
173
|
+
readonly 'panels.market.failure.copied': "已复制";
|
|
174
|
+
readonly 'panels.market.failure.copiedHint': "粘到终端跑一次,回来点「再检测」。";
|
|
175
|
+
readonly 'panels.market.failure.askAgent': "复制给 agent";
|
|
176
|
+
readonly 'panels.market.failure.askAgentText': "装这个模块时写不进去:`{command}` 退出码 {code},卡在 {path}。帮我看看是被沙箱拦的还是权限问题,换一条能写进去的路再装。";
|
|
177
|
+
readonly 'panels.market.failure.askAgentHint': "粘到对话里,让 agent 接着弄。";
|
|
178
|
+
readonly 'panels.market.failure.recheck': "再检测";
|
|
179
|
+
readonly 'panels.market.failure.showOutput': "查看安装输出";
|
|
180
|
+
readonly 'panels.market.failure.hideOutput': "收起安装输出";
|
|
181
|
+
readonly 'panels.market.failure.term.commandLine': "第 {line} 行的命令";
|
|
182
|
+
readonly 'panels.market.failure.term.signal': "被 {signal} 终止";
|
|
183
|
+
readonly 'panels.market.failure.term.exitCode': "退出码 {exitCode}";
|
|
184
|
+
readonly 'panels.market.failure.term.noExitCode': "没有退出码";
|
|
185
|
+
readonly 'panels.market.failure.term.running': "在跑";
|
|
186
|
+
readonly 'panels.market.failure.term.failed': "没跑通";
|
|
187
|
+
readonly 'panels.market.failure.term.done': "跑完了";
|
|
188
|
+
readonly 'panels.market.failure.term.copy': "复制";
|
|
189
|
+
readonly 'panels.market.failure.term.copied': "已复制";
|
|
190
|
+
readonly 'panels.market.failure.term.noOutput': "没有输出";
|
|
191
|
+
readonly 'panels.market.failure.term.collapse': "收起";
|
|
192
|
+
readonly 'panels.market.failure.term.expandAria': "展开剩下的 {hidden} 行";
|
|
193
|
+
readonly 'panels.market.failure.term.expand': "展开 {hidden} 行";
|
|
159
194
|
readonly 'panels.market.needEnv': "先在启动工作台的终端里设 {env}";
|
|
160
195
|
readonly 'panels.market.needCommands': "缺这几条命令:{commands}";
|
|
161
196
|
readonly 'panels.market.needBinary': "先装 {command} 命令";
|
|
@@ -191,9 +226,13 @@ export declare const zh: {
|
|
|
191
226
|
readonly 'panels.market.pluginBroken': "这份插件装坏了:认不出它是什么。可以先卸载,再让 agent 按包名重装一次。";
|
|
192
227
|
readonly 'panels.market.restartNeeded': "新版装好了,重启一次才生效。";
|
|
193
228
|
readonly 'panels.market.foot': "装好后到那个项目的面板页勾选它,模块才会出现。";
|
|
194
|
-
readonly 'panels.market.recheck': "重新检查更新";
|
|
195
229
|
readonly 'panels.market.checkUpdates': "检查更新";
|
|
230
|
+
readonly 'panels.market.checkingNow': "正在检查…";
|
|
231
|
+
readonly 'panels.market.checkFailed': "最近一次没查到,检查网络后重试";
|
|
196
232
|
readonly 'panels.market.checkedAt': "最近检查:{time}";
|
|
233
|
+
readonly 'panels.market.latestPartial': "{n} 个模块没查成";
|
|
234
|
+
readonly 'panels.market.latestNoRegistry': "没配来源,未检查新版";
|
|
235
|
+
readonly 'panels.market.latestProbeFailed': "问不到源,稍后重试";
|
|
197
236
|
readonly 'panels.market.justNow': "刚刚";
|
|
198
237
|
readonly 'panels.market.minutesAgo': "{n} 分钟前";
|
|
199
238
|
readonly 'panels.market.hoursAgo': "{n} 小时前";
|
|
@@ -370,6 +409,9 @@ export declare const zh: {
|
|
|
370
409
|
readonly 'panels.discover.transportTest': "试这个地址";
|
|
371
410
|
readonly 'panels.discover.transportPlaceholder': "或贴一个别的地址(公司镜像、别名)";
|
|
372
411
|
readonly 'panels.discover.transportNeedsAuth': "这个仓库要凭证:先在终端里把它 clone 一次,或配好 SSH key / 凭据助手;配好后再点安装。";
|
|
412
|
+
readonly 'panels.discover.transportChange': "换一个地址";
|
|
413
|
+
readonly 'panels.discover.transportChangeHide': "收起";
|
|
414
|
+
readonly 'panels.discover.transportNone': "没有可读的地址";
|
|
373
415
|
readonly 'panels.discover.expand': "展开版本";
|
|
374
416
|
readonly 'panels.discover.collapse': "收起版本";
|
|
375
417
|
readonly 'panels.discover.versionCount': "{n} 个版本";
|
|
@@ -655,6 +697,9 @@ export declare const zh: {
|
|
|
655
697
|
readonly 'approval.record.noSwitch': "录完不会替你切回来(左栏底部会亮一条状态)";
|
|
656
698
|
readonly 'approval.longTask.what': "要停的那次任务";
|
|
657
699
|
readonly 'approval.longTask.signal': "停了只是让工作台发出中断信号 —— 这次任务怎么收尾由模块自己负责";
|
|
700
|
+
readonly 'approval.plugin.name': "插件";
|
|
701
|
+
readonly 'approval.plugin.command': "命令";
|
|
702
|
+
readonly 'approval.plugin.note': "agent 说";
|
|
658
703
|
readonly 'approval.publish.module': "模块";
|
|
659
704
|
readonly 'approval.publish.target': "目标";
|
|
660
705
|
readonly 'approval.publish.version': "版本";
|
package/dist/i18n/zh.js
CHANGED
|
@@ -147,7 +147,42 @@ export const zh = {
|
|
|
147
147
|
'panels.market.confirmUninstallAck': '我明白:卸载后想再用,得重新装一遍。',
|
|
148
148
|
'panels.market.confirmYes': '卸载',
|
|
149
149
|
'panels.market.confirmNo': '取消',
|
|
150
|
+
'panels.market.noMarketSource': '索引里没有这个模块的新版本(刷新一次索引再试)',
|
|
150
151
|
'panels.market.failed': '没成功:{reason}',
|
|
152
|
+
'panels.market.askAgent': '问 agent',
|
|
153
|
+
'panels.market.askAgentCopied': '已复制',
|
|
154
|
+
'panels.market.askAgentText': '工作台里「{name}」(单元 {unit})用不了:{reason}。帮我查清缺什么、怎么补,补完确认真能用。',
|
|
155
|
+
'panels.market.failure.permission': '没装上:没有写入权限',
|
|
156
|
+
'panels.market.failure.diskFull': '没装上:磁盘满了',
|
|
157
|
+
'panels.market.failure.sandbox': '没装上:被系统拦住了',
|
|
158
|
+
'panels.market.failure.unknown': '没装上:原因没认出来',
|
|
159
|
+
'panels.market.failure.factOwner': '{path} 属于 {owner},当前账号({me})写不进去。把它还给自己就行(会要开机密码)。',
|
|
160
|
+
'panels.market.failure.factMode': '{path} 的权限是 {mode},当前账号没有写权限。',
|
|
161
|
+
'panels.market.failure.factSpace': '{path} 所在的磁盘满了。',
|
|
162
|
+
'panels.market.failure.factStuck': '这条命令跑在受限环境里,写不进 {path}。',
|
|
163
|
+
'panels.market.failure.nowhere': '(没认出是哪个目录)',
|
|
164
|
+
'panels.market.failure.copyFix': '复制修复命令',
|
|
165
|
+
'panels.market.failure.copied': '已复制',
|
|
166
|
+
'panels.market.failure.copiedHint': '粘到终端跑一次,回来点「再检测」。',
|
|
167
|
+
'panels.market.failure.askAgent': '复制给 agent',
|
|
168
|
+
'panels.market.failure.askAgentText': '装这个模块时写不进去:`{command}` 退出码 {code},卡在 {path}。帮我看看是被沙箱拦的还是权限问题,换一条能写进去的路再装。',
|
|
169
|
+
'panels.market.failure.askAgentHint': '粘到对话里,让 agent 接着弄。',
|
|
170
|
+
'panels.market.failure.recheck': '再检测',
|
|
171
|
+
'panels.market.failure.showOutput': '查看安装输出',
|
|
172
|
+
'panels.market.failure.hideOutput': '收起安装输出',
|
|
173
|
+
'panels.market.failure.term.commandLine': '第 {line} 行的命令',
|
|
174
|
+
'panels.market.failure.term.signal': '被 {signal} 终止',
|
|
175
|
+
'panels.market.failure.term.exitCode': '退出码 {exitCode}',
|
|
176
|
+
'panels.market.failure.term.noExitCode': '没有退出码',
|
|
177
|
+
'panels.market.failure.term.running': '在跑',
|
|
178
|
+
'panels.market.failure.term.failed': '没跑通',
|
|
179
|
+
'panels.market.failure.term.done': '跑完了',
|
|
180
|
+
'panels.market.failure.term.copy': '复制',
|
|
181
|
+
'panels.market.failure.term.copied': '已复制',
|
|
182
|
+
'panels.market.failure.term.noOutput': '没有输出',
|
|
183
|
+
'panels.market.failure.term.collapse': '收起',
|
|
184
|
+
'panels.market.failure.term.expandAria': '展开剩下的 {hidden} 行',
|
|
185
|
+
'panels.market.failure.term.expand': '展开 {hidden} 行',
|
|
151
186
|
'panels.market.needEnv': '先在启动工作台的终端里设 {env}',
|
|
152
187
|
'panels.market.needCommands': '缺这几条命令:{commands}',
|
|
153
188
|
'panels.market.needBinary': '先装 {command} 命令',
|
|
@@ -183,9 +218,13 @@ export const zh = {
|
|
|
183
218
|
'panels.market.pluginBroken': '这份插件装坏了:认不出它是什么。可以先卸载,再让 agent 按包名重装一次。',
|
|
184
219
|
'panels.market.restartNeeded': '新版装好了,重启一次才生效。',
|
|
185
220
|
'panels.market.foot': '装好后到那个项目的面板页勾选它,模块才会出现。',
|
|
186
|
-
'panels.market.recheck': '重新检查更新',
|
|
187
221
|
'panels.market.checkUpdates': '检查更新',
|
|
222
|
+
'panels.market.checkingNow': '正在检查…',
|
|
223
|
+
'panels.market.checkFailed': '最近一次没查到,检查网络后重试',
|
|
188
224
|
'panels.market.checkedAt': '最近检查:{time}',
|
|
225
|
+
'panels.market.latestPartial': '{n} 个模块没查成',
|
|
226
|
+
'panels.market.latestNoRegistry': '没配来源,未检查新版',
|
|
227
|
+
'panels.market.latestProbeFailed': '问不到源,稍后重试',
|
|
189
228
|
'panels.market.justNow': '刚刚',
|
|
190
229
|
'panels.market.minutesAgo': '{n} 分钟前',
|
|
191
230
|
'panels.market.hoursAgo': '{n} 小时前',
|
|
@@ -362,6 +401,9 @@ export const zh = {
|
|
|
362
401
|
'panels.discover.transportTest': '试这个地址',
|
|
363
402
|
'panels.discover.transportPlaceholder': '或贴一个别的地址(公司镜像、别名)',
|
|
364
403
|
'panels.discover.transportNeedsAuth': '这个仓库要凭证:先在终端里把它 clone 一次,或配好 SSH key / 凭据助手;配好后再点安装。',
|
|
404
|
+
'panels.discover.transportChange': '换一个地址',
|
|
405
|
+
'panels.discover.transportChangeHide': '收起',
|
|
406
|
+
'panels.discover.transportNone': '没有可读的地址',
|
|
365
407
|
'panels.discover.expand': '展开版本',
|
|
366
408
|
'panels.discover.collapse': '收起版本',
|
|
367
409
|
'panels.discover.versionCount': '{n} 个版本',
|
|
@@ -647,6 +689,9 @@ export const zh = {
|
|
|
647
689
|
'approval.record.noSwitch': '录完不会替你切回来(左栏底部会亮一条状态)',
|
|
648
690
|
'approval.longTask.what': '要停的那次任务',
|
|
649
691
|
'approval.longTask.signal': '停了只是让工作台发出中断信号 —— 这次任务怎么收尾由模块自己负责',
|
|
692
|
+
'approval.plugin.name': '插件',
|
|
693
|
+
'approval.plugin.command': '命令',
|
|
694
|
+
'approval.plugin.note': 'agent 说',
|
|
650
695
|
'approval.publish.module': '模块',
|
|
651
696
|
'approval.publish.target': '目标',
|
|
652
697
|
'approval.publish.version': '版本',
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export * from './components/board-module.js';
|
|
|
35
35
|
export * from './components/icons.js';
|
|
36
36
|
export * from './config/constants.js';
|
|
37
37
|
export * from './config/storage.js';
|
|
38
|
+
export * from './contract/approval.js';
|
|
38
39
|
export * from './contract/blocker.js';
|
|
39
40
|
export * from './contract/data.js';
|
|
40
41
|
export * from './contract/extension.js';
|
package/dist/index.js
CHANGED
|
@@ -35,6 +35,7 @@ export * from './components/board-module.js';
|
|
|
35
35
|
export * from './components/icons.js';
|
|
36
36
|
export * from './config/constants.js';
|
|
37
37
|
export * from './config/storage.js';
|
|
38
|
+
export * from './contract/approval.js';
|
|
38
39
|
export * from './contract/blocker.js';
|
|
39
40
|
export * from './contract/data.js';
|
|
40
41
|
export * from './contract/extension.js';
|
package/package.json
CHANGED
package/src/api/host-write.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { InstallFailure } from '../contract/command.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* 宿主写口的五条通道(偏好、动作、供给、工坊调试、工坊录制),是写路由的第一段。
|
|
3
5
|
*/
|
|
@@ -9,7 +11,8 @@ export type WriteChannel =
|
|
|
9
11
|
| 'diy-record';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
|
-
*
|
|
14
|
+
* 写通道统一的结果:成了带值,没成带一句能给人看的话(可选明细与帮助链接);
|
|
15
|
+
* 安装类的失败多带一份结构化现场,界面据此给修法。
|
|
13
16
|
*/
|
|
14
17
|
export type WriteOutcome<T> =
|
|
15
18
|
| { readonly ok: true; readonly value: T }
|
|
@@ -18,4 +21,5 @@ export type WriteOutcome<T> =
|
|
|
18
21
|
readonly message: string;
|
|
19
22
|
readonly detail?: string;
|
|
20
23
|
readonly helpUrl?: string;
|
|
24
|
+
readonly failure?: InstallFailure;
|
|
21
25
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { API_PREFIX } from '../config/constants.js';
|
|
2
|
+
import type { InstallFailure } from '../contract/command.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* 宿主读口 / 写口回的错误:code 给判据分档,message 给人看,helpUrl 有就去点。
|
|
@@ -9,6 +10,7 @@ export interface ApiError {
|
|
|
9
10
|
readonly detail?: string;
|
|
10
11
|
readonly hint?: string;
|
|
11
12
|
readonly helpUrl?: string;
|
|
13
|
+
readonly failure?: InstallFailure;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
/**
|
|
@@ -10,22 +10,21 @@
|
|
|
10
10
|
color: var(--dsw-alias-label-secondary);
|
|
11
11
|
}
|
|
12
12
|
.wb-approvalRows {
|
|
13
|
-
display:
|
|
14
|
-
|
|
15
|
-
gap: var(--wb-len-
|
|
13
|
+
display: grid;
|
|
14
|
+
grid-template-columns: minmax(var(--wb-len-56), auto) minmax(0, 1fr);
|
|
15
|
+
column-gap: var(--wb-len-8);
|
|
16
|
+
row-gap: var(--wb-len-2);
|
|
17
|
+
align-items: baseline;
|
|
16
18
|
margin: 0;
|
|
17
19
|
}
|
|
18
20
|
.wb-approvalRow {
|
|
19
|
-
display:
|
|
20
|
-
align-items: baseline;
|
|
21
|
-
gap: var(--wb-len-8);
|
|
21
|
+
display: contents;
|
|
22
22
|
}
|
|
23
23
|
.wb-approvalLabel {
|
|
24
|
-
flex: none;
|
|
25
|
-
width: var(--wb-len-56);
|
|
26
24
|
font-size: var(--dsw-font-xxs-12-font-size);
|
|
27
25
|
line-height: var(--wb-line-18);
|
|
28
26
|
color: var(--dsw-alias-label-tertiary);
|
|
27
|
+
overflow-wrap: anywhere;
|
|
29
28
|
}
|
|
30
29
|
.wb-approvalValue {
|
|
31
30
|
margin: 0;
|
package/src/config/constants.ts
CHANGED
|
@@ -109,6 +109,12 @@ export const STORAGE_KEY_RIGHTBAR_COLUMN = "dsh-workbench.rightbar.column";
|
|
|
109
109
|
/** 记住右栏宽度(像素数字的字符串)的 localStorage 键。 */
|
|
110
110
|
export const STORAGE_KEY_RIGHTBAR_WIDTH = "dsh-workbench.rightbar.width";
|
|
111
111
|
|
|
112
|
+
/** 记住「添加模块」浮层选过的安装源的 localStorage 键。 */
|
|
113
|
+
export const STORAGE_KEY_MARKET_REGISTRY = "dsh-workbench.market.registry";
|
|
114
|
+
|
|
115
|
+
/** 记住「添加模块」浮层上次填过的自定义源地址的 localStorage 键。 */
|
|
116
|
+
export const STORAGE_KEY_MARKET_REGISTRY_CUSTOM = "dsh-workbench.market.registry.custom";
|
|
117
|
+
|
|
112
118
|
/** 右栏宽度达到或超过多少像素就按全屏对待(阈值,像素)。 */
|
|
113
119
|
export const RIGHTBAR_FULLSCREEN_WIDTH = 768;
|
|
114
120
|
|
|
@@ -37,6 +37,59 @@ describe('needs 校验矩阵', () => {
|
|
|
37
37
|
);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
+
it('bin 声明 install → 过;install 写歪了要判红并点名', () => {
|
|
41
|
+
expect(
|
|
42
|
+
errorsOf({
|
|
43
|
+
kind: 'bin',
|
|
44
|
+
name: 'gitops',
|
|
45
|
+
purpose: '平台命令',
|
|
46
|
+
install: { kind: 'command', line: 'brew install pagoda/tap/gitops', platforms: ['darwin'] },
|
|
47
|
+
docs: 'https://internal/gitops',
|
|
48
|
+
}),
|
|
49
|
+
).toEqual([]);
|
|
50
|
+
|
|
51
|
+
expect(errorsOf({ kind: 'bin', name: 'x', purpose: 'p', install: { kind: 'brew', line: 'x' } }).join(' ')).toContain(
|
|
52
|
+
'install.kind 只认 command',
|
|
53
|
+
);
|
|
54
|
+
expect(errorsOf({ kind: 'bin', name: 'x', purpose: 'p', install: { kind: 'command', line: '' } }).join(' ')).toContain(
|
|
55
|
+
'install.line 没写',
|
|
56
|
+
);
|
|
57
|
+
expect(
|
|
58
|
+
errorsOf({ kind: 'bin', name: 'x', purpose: 'p', install: { kind: 'command', line: 'x', platforms: ['mac'] } }).join(' '),
|
|
59
|
+
).toContain('darwin / linux / win32');
|
|
60
|
+
expect(errorsOf({ kind: 'bin', name: 'x', purpose: 'p', install: { kind: 'command', line: 'x', url: 'y' } }).join(' ')).toContain(
|
|
61
|
+
'install 有不认识的字段 url',
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('bin 的 install 也可以是一个 npm 包(命令由包提供,装进隔离运行时)', () => {
|
|
66
|
+
expect(
|
|
67
|
+
errorsOf({
|
|
68
|
+
kind: 'bin',
|
|
69
|
+
name: 'gitops',
|
|
70
|
+
purpose: '平台命令',
|
|
71
|
+
install: {
|
|
72
|
+
kind: 'npm',
|
|
73
|
+
package: 'opencli-workflow',
|
|
74
|
+
registryEnv: 'ACME_REGISTRY',
|
|
75
|
+
registryDefault: 'https://registry.test/',
|
|
76
|
+
},
|
|
77
|
+
}),
|
|
78
|
+
).toEqual([]);
|
|
79
|
+
|
|
80
|
+
expect(errorsOf({ kind: 'bin', name: 'x', purpose: 'p', install: { kind: 'npm' } }).join(' ')).toContain(
|
|
81
|
+
'install.package 没写',
|
|
82
|
+
);
|
|
83
|
+
expect(
|
|
84
|
+
errorsOf({
|
|
85
|
+
kind: 'bin',
|
|
86
|
+
name: 'x',
|
|
87
|
+
purpose: 'p',
|
|
88
|
+
install: { kind: 'npm', package: 'x', registryDefault: 'ftp://x' },
|
|
89
|
+
}).join(' '),
|
|
90
|
+
).toContain('registryDefault 要是 http(s):// 或 file:// 的源地址');
|
|
91
|
+
});
|
|
92
|
+
|
|
40
93
|
it('bin / env 写了 sources → 红(没有安装路径)', () => {
|
|
41
94
|
expect(errorsOf({ kind: 'bin', name: 'x', purpose: '测试', sources: [npm] }).join(' ')).toContain(
|
|
42
95
|
'不支持 sources',
|
package/src/contract/command.ts
CHANGED
|
@@ -23,3 +23,25 @@ export interface CommandResult {
|
|
|
23
23
|
export interface CommandReader {
|
|
24
24
|
run(request: CommandRequest): Promise<CommandResult>;
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
export const INSTALL_FAILURE_KINDS = ['permission', 'disk-full', 'sandbox', 'unknown'] as const;
|
|
28
|
+
|
|
29
|
+
export type InstallFailureKind = (typeof INSTALL_FAILURE_KINDS)[number];
|
|
30
|
+
|
|
31
|
+
export type InstallFix =
|
|
32
|
+
| { readonly kind: 'chown'; readonly path: string; readonly uid: number; readonly gid: number }
|
|
33
|
+
| { readonly kind: 'chmod'; readonly path: string }
|
|
34
|
+
| { readonly kind: 'space'; readonly path: string }
|
|
35
|
+
| { readonly kind: 'handoff' };
|
|
36
|
+
|
|
37
|
+
export interface InstallFailure {
|
|
38
|
+
readonly kind: InstallFailureKind;
|
|
39
|
+
readonly command: string;
|
|
40
|
+
readonly exitCode: number | null;
|
|
41
|
+
readonly output: string;
|
|
42
|
+
readonly path?: string | undefined;
|
|
43
|
+
readonly mode?: string | undefined;
|
|
44
|
+
readonly owner?: { readonly uid: number; readonly gid: number } | undefined;
|
|
45
|
+
readonly current: { readonly uid: number; readonly gid: number };
|
|
46
|
+
readonly fix: InstallFix;
|
|
47
|
+
}
|
package/src/contract/needs.ts
CHANGED
|
@@ -41,12 +41,31 @@ export interface NeedCommon {
|
|
|
41
41
|
readonly id?: string;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* 一条装这个可执行文件的命令;模块作者写,工作台照抄执行。
|
|
46
|
+
*/
|
|
47
|
+
export type BinInstall =
|
|
48
|
+
| {
|
|
49
|
+
readonly kind: 'command';
|
|
50
|
+
readonly line: string;
|
|
51
|
+
readonly platforms?: readonly string[] | undefined;
|
|
52
|
+
}
|
|
53
|
+
| {
|
|
54
|
+
readonly kind: 'npm';
|
|
55
|
+
readonly package: string;
|
|
56
|
+
readonly registryEnv?: string | undefined;
|
|
57
|
+
readonly registryDefault?: string | undefined;
|
|
58
|
+
readonly platforms?: readonly string[] | undefined;
|
|
59
|
+
};
|
|
60
|
+
|
|
44
61
|
/**
|
|
45
62
|
* 机器上要有一个可执行文件;探法是 `command -v` / `where`。
|
|
46
63
|
*/
|
|
47
64
|
export interface BinNeed extends NeedCommon {
|
|
48
65
|
readonly kind: 'bin';
|
|
49
66
|
readonly name: string;
|
|
67
|
+
readonly install?: BinInstall | undefined;
|
|
68
|
+
readonly docs?: string | undefined;
|
|
50
69
|
}
|
|
51
70
|
|
|
52
71
|
/**
|
|
@@ -107,6 +126,8 @@ export const NEED_KEYS: readonly string[] = [
|
|
|
107
126
|
'version',
|
|
108
127
|
'sources',
|
|
109
128
|
'secret',
|
|
129
|
+
'install',
|
|
130
|
+
'docs',
|
|
110
131
|
];
|
|
111
132
|
|
|
112
133
|
/**
|
|
@@ -188,7 +209,7 @@ function sourcesProblemsOf(
|
|
|
188
209
|
if (!Array.isArray(raw)) return [`${at}.sources 要是数组`];
|
|
189
210
|
if (raw.length === 0) return [`${at}.sources 是空数组 —— 不代装就整条删掉`];
|
|
190
211
|
if (allowed.length === 0) {
|
|
191
|
-
return [`${at} 这种需要不支持 sources ——
|
|
212
|
+
return [`${at} 这种需要不支持 sources —— 要代装就写 install(一条命令)`];
|
|
192
213
|
}
|
|
193
214
|
const errors: string[] = [];
|
|
194
215
|
raw.forEach((source, index) => {
|
|
@@ -214,6 +235,38 @@ function commonProblemsOf(raw: Readonly<Record<string, unknown>>, at: string): r
|
|
|
214
235
|
return errors;
|
|
215
236
|
}
|
|
216
237
|
|
|
238
|
+
export function binInstallProblemsOf(raw: unknown, at: string): readonly string[] {
|
|
239
|
+
if (raw === undefined) return [];
|
|
240
|
+
if (!isRecord(raw)) return [`${at}.install 要是对象`];
|
|
241
|
+
const errors: string[] = [];
|
|
242
|
+
for (const key of Object.keys(raw)) {
|
|
243
|
+
if (!['kind', 'line', 'platforms', 'package', 'registryEnv', 'registryDefault'].includes(key)) {
|
|
244
|
+
errors.push(`${at}.install 有不认识的字段 ${key}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const kind = text(raw['kind']);
|
|
248
|
+
if (kind !== 'command' && kind !== 'npm') {
|
|
249
|
+
errors.push(`${at}.install.kind 只认 command(一条命令)或 npm(一个包,装进隔离运行时)`);
|
|
250
|
+
}
|
|
251
|
+
if (kind === 'command' && text(raw['line']) === '') {
|
|
252
|
+
errors.push(`${at}.install.line 没写 —— 要一条能直接粘进终端的命令`);
|
|
253
|
+
}
|
|
254
|
+
if (kind === 'npm' && text(raw['package']) === '') {
|
|
255
|
+
errors.push(`${at}.install.package 没写 —— 这个命令由哪个 npm 包提供`);
|
|
256
|
+
}
|
|
257
|
+
if (raw['registryDefault'] !== undefined && !REGISTRY_PATTERN.test(text(raw['registryDefault']))) {
|
|
258
|
+
errors.push(`${at}.install.registryDefault 要是 http(s):// 或 file:// 的源地址`);
|
|
259
|
+
}
|
|
260
|
+
if (raw['platforms'] !== undefined) {
|
|
261
|
+
if (!Array.isArray(raw['platforms']) || raw['platforms'].length === 0) {
|
|
262
|
+
errors.push(`${at}.install.platforms 要是非空数组`);
|
|
263
|
+
} else if (raw['platforms'].some((one) => !['darwin', 'linux', 'win32'].includes(text(one)))) {
|
|
264
|
+
errors.push(`${at}.install.platforms 只认 darwin / linux / win32`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return errors;
|
|
268
|
+
}
|
|
269
|
+
|
|
217
270
|
function perKindProblemsOf(
|
|
218
271
|
kind: NeedKind,
|
|
219
272
|
raw: Readonly<Record<string, unknown>>,
|
|
@@ -223,6 +276,8 @@ function perKindProblemsOf(
|
|
|
223
276
|
if (kind === 'bin' || kind === 'lib' || kind === 'env') {
|
|
224
277
|
if (text(raw['name']) === '') errors.push(`${at}.name 没写`);
|
|
225
278
|
}
|
|
279
|
+
if (kind === 'bin') errors.push(...binInstallProblemsOf(raw['install'], at));
|
|
280
|
+
if (raw['docs'] !== undefined && text(raw['docs']) === '') errors.push(`${at}.docs 是空串`);
|
|
226
281
|
if (kind === 'extension') {
|
|
227
282
|
if (text(raw['runtime']) === '') errors.push(`${at}.runtime 没写 —— 扩展属于哪个运行时(如 opencli)`);
|
|
228
283
|
if (text(raw['name']) === '') errors.push(`${at}.name 没写`);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { MarketInstalledModule } from './market-index.js';
|
|
1
2
|
import type { UnitTitle } from '../unit.js';
|
|
2
3
|
|
|
3
4
|
export const BUNDLE_REGISTRIES_ROUTE = 'bundle-registries';
|
|
@@ -178,6 +179,8 @@ export interface BundleCancellationPayload {
|
|
|
178
179
|
|
|
179
180
|
export type ModuleUpdateBlock = 'needs-workbench' | 'incompatible';
|
|
180
181
|
|
|
182
|
+
export type LatestUnavailable = 'local' | 'no-registry' | 'not-registry' | 'probe-failed';
|
|
183
|
+
|
|
181
184
|
export interface ModuleVersion {
|
|
182
185
|
readonly unit: string;
|
|
183
186
|
readonly pkg: string;
|
|
@@ -185,8 +188,10 @@ export interface ModuleVersion {
|
|
|
185
188
|
readonly title?: UnitTitle;
|
|
186
189
|
readonly latest?: string;
|
|
187
190
|
readonly latestBlocked?: ModuleUpdateBlock;
|
|
191
|
+
readonly latestUnavailable?: LatestUnavailable;
|
|
188
192
|
readonly local?: boolean;
|
|
189
193
|
readonly refused?: readonly string[];
|
|
194
|
+
readonly market?: MarketInstalledModule;
|
|
190
195
|
}
|
|
191
196
|
|
|
192
197
|
export interface WorkbenchVersion {
|
|
@@ -3,6 +3,7 @@ export const MARKET_INDEX_PATH = 'market.json';
|
|
|
3
3
|
export const MARKET_DISCOVER_ROUTE = 'panels/market-discover';
|
|
4
4
|
export const MARKET_DOC_ROUTE = 'panels/market-doc';
|
|
5
5
|
export const MARKET_GIT_PROBE_ROUTE = 'panels/git-probe';
|
|
6
|
+
export const MARKET_INSTALLED_ROUTE = 'panels/market-installed';
|
|
6
7
|
export const MARKET_PUBLISH_PREVIEW_ROUTE = 'panels/market-publish-preview';
|
|
7
8
|
export const MARKET_SOURCES_RESOURCE = 'market-sources';
|
|
8
9
|
export const MARKET_SOURCES_ROUTE = 'panels/market-sources';
|
|
@@ -217,9 +218,25 @@ export interface DiscoverFilter {
|
|
|
217
218
|
readonly label?: string;
|
|
218
219
|
}
|
|
219
220
|
|
|
221
|
+
export type GitTransportKind = 'https' | 'ssh';
|
|
222
|
+
|
|
220
223
|
export interface MarketSourcesPayload {
|
|
221
224
|
readonly sources: readonly MarketSourceConfig[];
|
|
222
225
|
readonly selected: string | null;
|
|
226
|
+
readonly transports?: Readonly<Record<string, GitTransportKind>>;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export interface MarketInstalledModule {
|
|
230
|
+
readonly pkg: string;
|
|
231
|
+
readonly unit: string;
|
|
232
|
+
readonly version: string;
|
|
233
|
+
readonly sourceId: string;
|
|
234
|
+
readonly sourceLabel: string;
|
|
235
|
+
readonly source: MarketIndexSource;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export interface MarketInstalledPayload {
|
|
239
|
+
readonly modules: readonly MarketInstalledModule[];
|
|
223
240
|
}
|
|
224
241
|
|
|
225
242
|
export interface MarketPublishDestination {
|
package/src/contract/unit.ts
CHANGED
|
@@ -24,12 +24,23 @@ export interface UnitCard {
|
|
|
24
24
|
readonly order: number;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* 合成工具背后那条 opencli 命令:插件名与命令名都是 opencli 那边的原名,`args` 是命令自己声明的参数名(按声明顺序)。
|
|
29
|
+
*/
|
|
30
|
+
export interface UnitToolOpencli {
|
|
31
|
+
readonly plugin: string;
|
|
32
|
+
readonly command: string;
|
|
33
|
+
readonly args: readonly string[];
|
|
34
|
+
}
|
|
35
|
+
|
|
27
36
|
/**
|
|
28
37
|
* 一条工具在清单里的样子:全名加会不会写;审批按 writes 认领。
|
|
38
|
+
* `opencli` 只有「按已装插件合成」的单元写,手写模块的工具没有这一格。
|
|
29
39
|
*/
|
|
30
40
|
export interface UnitTool {
|
|
31
41
|
readonly name: string;
|
|
32
42
|
readonly writes: boolean;
|
|
43
|
+
readonly opencli?: UnitToolOpencli;
|
|
33
44
|
}
|
|
34
45
|
|
|
35
46
|
/**
|
|
@@ -25,6 +25,7 @@ export interface ProfileModule {
|
|
|
25
25
|
readonly version: string;
|
|
26
26
|
readonly registry?: string;
|
|
27
27
|
readonly local?: boolean;
|
|
28
|
+
readonly fromRegistry?: boolean;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export interface ProfilePackage {
|
|
@@ -51,6 +52,7 @@ export interface RegistryEntry {
|
|
|
51
52
|
export interface RegistryNotebook {
|
|
52
53
|
sources(): readonly RegistryEntry[];
|
|
53
54
|
remember(entries: readonly RegistryEntry[]): readonly string[];
|
|
55
|
+
swapScope?(scope: string, registry: string): () => void;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
export interface SelfUpdatePaths {
|
package/src/host/data-error.ts
CHANGED
|
@@ -4,11 +4,12 @@ export class WorkbenchDataError extends Error {
|
|
|
4
4
|
readonly hint?: string;
|
|
5
5
|
readonly project?: unknown;
|
|
6
6
|
readonly helpUrl?: string;
|
|
7
|
+
readonly failure?: unknown;
|
|
7
8
|
|
|
8
9
|
constructor(
|
|
9
10
|
code: string,
|
|
10
11
|
message: string,
|
|
11
|
-
extra: { detail?: string; hint?: string; project?: unknown; helpUrl?: string } = {},
|
|
12
|
+
extra: { detail?: string; hint?: string; project?: unknown; helpUrl?: string; failure?: unknown } = {},
|
|
12
13
|
) {
|
|
13
14
|
super(message);
|
|
14
15
|
this.name = 'WorkbenchDataError';
|
|
@@ -17,5 +18,6 @@ export class WorkbenchDataError extends Error {
|
|
|
17
18
|
if (extra.hint !== undefined) this.hint = extra.hint;
|
|
18
19
|
if (extra.project !== undefined) this.project = extra.project;
|
|
19
20
|
if (extra.helpUrl !== undefined) this.helpUrl = extra.helpUrl;
|
|
21
|
+
if (extra.failure !== undefined) this.failure = extra.failure;
|
|
20
22
|
}
|
|
21
23
|
}
|
package/src/host/http.ts
CHANGED
|
@@ -49,6 +49,7 @@ function toErrorPayload(error: unknown): Record<string, unknown> {
|
|
|
49
49
|
...(data.hint === undefined ? {} : { hint: data.hint }),
|
|
50
50
|
...(data.helpUrl === undefined ? {} : { helpUrl: data.helpUrl }),
|
|
51
51
|
...(data.project === undefined ? {} : { project: data.project }),
|
|
52
|
+
...(data.failure === undefined ? {} : { failure: data.failure }),
|
|
52
53
|
};
|
|
53
54
|
}
|
|
54
55
|
return { code: 'internal', message: error instanceof Error ? error.message : String(error) };
|