@dsh-workbench/shared 0.1.9 → 0.1.11
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.js +10 -0
- package/dist/api/dsh-client.d.ts +26 -3
- package/dist/api/dsh.d.ts +10 -0
- package/dist/api/workbench-contract.d.ts +1 -1
- package/dist/components/ApprovalDetail.js +6 -2
- package/dist/components/CardBoundary.d.ts +21 -0
- package/dist/components/CardBoundary.js +34 -0
- package/dist/host/tool-approval.d.ts +10 -1
- package/dist/host/tool-approval.js +24 -169
- package/dist/host/unit-face.d.ts +2 -1
- package/dist/host/unit-libs.d.ts +1 -1
- package/dist/i18n/en.js +20 -0
- package/dist/i18n/index.d.ts +21 -1
- package/dist/i18n/merge.d.ts +9 -0
- package/dist/i18n/merge.js +22 -0
- package/dist/i18n/zh.d.ts +20 -0
- package/dist/i18n/zh.js +20 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/react.d.ts +1 -0
- package/dist/lib/react.js +1 -0
- package/package.json +1 -1
- package/src/api/__tests__/blocker-table.test.ts +13 -0
- package/src/api/blocker-table.ts +11 -0
- package/src/api/dsh-client.ts +27 -1
- package/src/api/dsh.ts +5 -0
- package/src/api/workbench-contract.ts +1 -1
- package/src/components/ApprovalDetail.tsx +9 -1
- package/src/components/CardBoundary.tsx +56 -0
- package/src/host/tool-approval.ts +41 -187
- package/src/host/unit-face.ts +2 -1
- package/src/host/unit-libs.ts +1 -1
- package/src/i18n/en.ts +20 -0
- package/src/i18n/merge.test.ts +62 -1
- package/src/i18n/merge.ts +36 -0
- package/src/i18n/zh.ts +20 -0
- package/src/index.ts +1 -0
- package/src/lib/react.ts +2 -0
package/src/host/unit-face.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { RouteTable } from '../host/http.js';
|
|
|
4
4
|
import type { LongTaskBase, LongTaskHost, LongTaskRegistry } from '../host/long-task.js';
|
|
5
5
|
import type { ProvisionHandler } from '../host/provision.js';
|
|
6
6
|
import type { UnitCapability } from './unit-libs.js';
|
|
7
|
-
import type { ApprovalCall, ApprovalFacts } from '../host/tool-approval.js';
|
|
7
|
+
import type { ApprovalCall, ApprovalFacts, ApprovalPolicy } from '../host/tool-approval.js';
|
|
8
8
|
import type { ValueHandoff } from '../host/value-handoff.js';
|
|
9
9
|
import type { WorkspaceStore } from '../host/workspace-store.js';
|
|
10
10
|
|
|
@@ -57,6 +57,7 @@ export interface UnitMountResult {
|
|
|
57
57
|
readonly approvalDetail?:
|
|
58
58
|
| ((call: ApprovalCall) => ApprovalFacts | undefined | Promise<ApprovalFacts | undefined>)
|
|
59
59
|
| undefined;
|
|
60
|
+
readonly needsApproval?: ApprovalPolicy | undefined;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
export interface UnitHostFace {
|
package/src/host/unit-libs.ts
CHANGED
|
@@ -27,7 +27,7 @@ export interface UnitPluginState {
|
|
|
27
27
|
|
|
28
28
|
export interface UnitCapability {
|
|
29
29
|
readonly unit: string;
|
|
30
|
-
readonly status: "ok" | "missing-commands" | "missing-plugin" | "unchecked";
|
|
30
|
+
readonly status: "ok" | "failed" | "missing-commands" | "missing-plugin" | "unchecked";
|
|
31
31
|
readonly plugin?: string;
|
|
32
32
|
readonly required: readonly string[];
|
|
33
33
|
readonly missing: readonly string[];
|
package/src/i18n/en.ts
CHANGED
|
@@ -10,6 +10,20 @@ export const en: Record<keyof WorkbenchMessages, string> = {
|
|
|
10
10
|
'project.none': 'No project',
|
|
11
11
|
'project.empty': 'No projects yet. Use “Add project” below.',
|
|
12
12
|
'project.add': 'Add project…',
|
|
13
|
+
'project.search.label': 'Search workspaces',
|
|
14
|
+
'project.search.placeholder': 'Search workspaces (folder / name / path)',
|
|
15
|
+
'project.search.empty': 'No workspace matches “{query}”.',
|
|
16
|
+
'project.rename.row': 'Rename “{name}”',
|
|
17
|
+
'project.rename.current': 'Give this workspace a display name…',
|
|
18
|
+
'project.rename.title': 'Give “{name}” a display name',
|
|
19
|
+
'project.rename.description': 'Shown after the folder name. It renames the workspace itself.',
|
|
20
|
+
'project.rename.field': 'Display name',
|
|
21
|
+
'project.rename.placeholder': 'e.g. Workbench shell',
|
|
22
|
+
'project.rename.hint': 'Empty = folder name only',
|
|
23
|
+
'project.rename.save': 'Save',
|
|
24
|
+
'project.rename.saving': 'Saving…',
|
|
25
|
+
'project.rename.cancel': 'Cancel',
|
|
26
|
+
'project.rename.failed': 'Rename failed. Try again.',
|
|
13
27
|
'session.new': 'New session',
|
|
14
28
|
'session.new.label': 'Start a session in the current project',
|
|
15
29
|
'session.empty': 'No sessions in this project yet.',
|
|
@@ -35,11 +49,13 @@ export const en: Record<keyof WorkbenchMessages, string> = {
|
|
|
35
49
|
'panels.enabled': 'enabled in this project',
|
|
36
50
|
'panels.disabled': 'not enabled in this project',
|
|
37
51
|
'panels.choose': 'Choose modules',
|
|
52
|
+
'panels.buildModule': 'Build a module',
|
|
38
53
|
'panels.failed': 'Could not save the change. Try again.',
|
|
39
54
|
'panels.hintEffect': 'Disabling = the agent cannot reach that data',
|
|
40
55
|
'panels.dragHint': 'Drag to reorder',
|
|
41
56
|
'panels.unknownStep': 'No suite provides this view',
|
|
42
57
|
'panels.labelWithHint': '{label} ({hint})',
|
|
58
|
+
'panels.dep.failed': '⚠ failed to start',
|
|
43
59
|
'panels.dep.missingPlugin': '⚠ not ready yet',
|
|
44
60
|
'panels.dep.missingCommands': '⚠ needs an update',
|
|
45
61
|
'panels.dep.unchecked': '⚠ cannot check',
|
|
@@ -235,6 +251,8 @@ export const en: Record<keyof WorkbenchMessages, string> = {
|
|
|
235
251
|
'blocker.exec.hint': 'Make sure opencli is installed and on PATH (which opencli).',
|
|
236
252
|
'blocker.unknown.title': 'Something went wrong',
|
|
237
253
|
'panels.reading': 'Reading the panel setup…',
|
|
254
|
+
'panels.cardBroken': 'This module will not render: {unit} ({reason})',
|
|
255
|
+
'panels.cardRetry': 'Retry',
|
|
238
256
|
'panels.empty': 'No modules on the panel yet',
|
|
239
257
|
'panels.emptyHint': 'You can also just tell the agent: “add a module to the panel”.',
|
|
240
258
|
'env.title': 'Preparing the workbench',
|
|
@@ -273,6 +291,8 @@ export const en: Record<keyof WorkbenchMessages, string> = {
|
|
|
273
291
|
'env.step.warm': 'Warming up site sessions',
|
|
274
292
|
'env.warm.done': '{n} site(s) warmed up',
|
|
275
293
|
'env.warm.short': 'Not warmed this time — harmless; the panel heals on first read',
|
|
294
|
+
'env.unit.failed': 'This module cannot start',
|
|
295
|
+
'env.unit.failedHint': 'It failed while assembling itself; other modules are unaffected.',
|
|
276
296
|
'env.unit.missingPlugin': 'This module is not ready yet',
|
|
277
297
|
'env.unit.missingCommandsHint': 'Updating it should fix this.',
|
|
278
298
|
'env.unit.unchecked': 'Cannot check: no shell channel',
|
package/src/i18n/merge.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { mergeMessages } from './merge.js';
|
|
2
|
+
import { mergeMessages, mergeMessagesSkipping } from './merge.js';
|
|
3
3
|
|
|
4
4
|
const base = { zh: { 'board.title': '看板' }, en: { 'board.title': 'Board' } };
|
|
5
5
|
|
|
@@ -58,3 +58,64 @@ describe('mergeMessages', () => {
|
|
|
58
58
|
).toThrow(/是空的/);
|
|
59
59
|
});
|
|
60
60
|
});
|
|
61
|
+
|
|
62
|
+
describe('mergeMessagesSkipping', () => {
|
|
63
|
+
const good = {
|
|
64
|
+
unit: 'issues',
|
|
65
|
+
messages: { zh: { 'issues.mine': '我的缺陷' }, en: { 'issues.mine': 'My defects' } },
|
|
66
|
+
};
|
|
67
|
+
const bad = {
|
|
68
|
+
unit: 'cmdb',
|
|
69
|
+
messages: { zh: { 'wrong.prefix': '错了' }, en: { 'wrong.prefix': 'nope' } },
|
|
70
|
+
};
|
|
71
|
+
const later = {
|
|
72
|
+
unit: 'builds',
|
|
73
|
+
messages: { zh: { 'builds.recent': '最近构建' }, en: { 'builds.recent': 'Recent builds' } },
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
it('★ 一份坏文案只废它自己:好的照并、坏的报出来', () => {
|
|
77
|
+
const { dictionaries, problems } = mergeMessagesSkipping(base, [good, bad, later]);
|
|
78
|
+
|
|
79
|
+
expect(dictionaries.zh).toEqual({
|
|
80
|
+
'board.title': '看板',
|
|
81
|
+
'issues.mine': '我的缺陷',
|
|
82
|
+
'builds.recent': '最近构建',
|
|
83
|
+
});
|
|
84
|
+
expect(dictionaries.en['builds.recent']).toBe('Recent builds');
|
|
85
|
+
expect(problems).toHaveLength(1);
|
|
86
|
+
expect(problems[0]?.unit).toBe('cmdb');
|
|
87
|
+
expect(problems[0]?.reason).toMatch(/必须以 "cmdb\." 开头/);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('★ 坏的在前、好的在后:后面那份不受影响(不会因为前者坏就整批拒收)', () => {
|
|
91
|
+
const { dictionaries, problems } = mergeMessagesSkipping(base, [bad, good]);
|
|
92
|
+
|
|
93
|
+
expect(dictionaries.zh['issues.mine']).toBe('我的缺陷');
|
|
94
|
+
expect(problems.map((item) => item.unit)).toEqual(['cmdb']);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('都好时与 mergeMessages 逐字一致、没有问题', () => {
|
|
98
|
+
const { dictionaries, problems } = mergeMessagesSkipping(base, [good, later]);
|
|
99
|
+
|
|
100
|
+
expect(dictionaries).toEqual(mergeMessages(base, [good, later]));
|
|
101
|
+
expect(problems).toEqual([]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('与外壳自己的键撞名的那份被跳过,且报的是撞名的双方', () => {
|
|
105
|
+
const clash = {
|
|
106
|
+
unit: 'board',
|
|
107
|
+
messages: { zh: { 'board.title': '看板改' }, en: { 'board.title': 'Board!' } },
|
|
108
|
+
};
|
|
109
|
+
const { dictionaries, problems } = mergeMessagesSkipping(base, [clash]);
|
|
110
|
+
|
|
111
|
+
expect(dictionaries.zh['board.title']).toBe('看板');
|
|
112
|
+
expect(problems[0]?.reason).toMatch(/文案键冲突:board\.title 由 外壳 与 board/);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('没带文案的那份直接跳过,不算问题', () => {
|
|
116
|
+
const { dictionaries, problems } = mergeMessagesSkipping(base, [{ unit: 'nothing' }]);
|
|
117
|
+
|
|
118
|
+
expect(dictionaries).toEqual(base);
|
|
119
|
+
expect(problems).toEqual([]);
|
|
120
|
+
});
|
|
121
|
+
});
|
package/src/i18n/merge.ts
CHANGED
|
@@ -52,3 +52,39 @@ export function mergeMessages(
|
|
|
52
52
|
|
|
53
53
|
return { zh, en };
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
export interface MessagesProblem {
|
|
57
|
+
readonly unit: string;
|
|
58
|
+
readonly reason: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface MessagesMerge {
|
|
62
|
+
readonly dictionaries: Dictionaries;
|
|
63
|
+
readonly problems: readonly MessagesProblem[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function reasonOf(error: unknown): string {
|
|
67
|
+
if (error instanceof Error) return error.message;
|
|
68
|
+
return typeof error === 'string' ? error : String(error);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function mergeMessagesSkipping(
|
|
72
|
+
base: Dictionaries,
|
|
73
|
+
contributions: readonly MessagesContribution[],
|
|
74
|
+
): MessagesMerge {
|
|
75
|
+
let dictionaries = base;
|
|
76
|
+
const accepted: MessagesContribution[] = [];
|
|
77
|
+
const problems: MessagesProblem[] = [];
|
|
78
|
+
|
|
79
|
+
for (const item of contributions) {
|
|
80
|
+
if (item.messages === undefined) continue;
|
|
81
|
+
try {
|
|
82
|
+
dictionaries = mergeMessages(base, [...accepted, item]);
|
|
83
|
+
accepted.push(item);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
problems.push({ unit: item.unit, reason: reasonOf(error) });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return { dictionaries, problems };
|
|
90
|
+
}
|
package/src/i18n/zh.ts
CHANGED
|
@@ -8,6 +8,20 @@ export const zh = {
|
|
|
8
8
|
'project.none': '未选择项目',
|
|
9
9
|
'project.empty': '还没有项目。用下面的「添加项目」选一个目录。',
|
|
10
10
|
'project.add': '添加项目…',
|
|
11
|
+
'project.search.label': '搜索工作区',
|
|
12
|
+
'project.search.placeholder': '搜索工作区(目录名 / 中文名 / 路径)',
|
|
13
|
+
'project.search.empty': '没有匹配「{query}」的工作区。',
|
|
14
|
+
'project.rename.row': '改「{name}」的中文名',
|
|
15
|
+
'project.rename.current': '改当前工作区的中文名…',
|
|
16
|
+
'project.rename.title': '给「{name}」配一个中文名',
|
|
17
|
+
'project.rename.description': '显示在目录名后面。改的是工作区本身的名字。',
|
|
18
|
+
'project.rename.field': '中文名',
|
|
19
|
+
'project.rename.placeholder': '例如:工作台外壳',
|
|
20
|
+
'project.rename.hint': '留空 = 只显示目录名',
|
|
21
|
+
'project.rename.save': '保存',
|
|
22
|
+
'project.rename.saving': '保存中…',
|
|
23
|
+
'project.rename.cancel': '取消',
|
|
24
|
+
'project.rename.failed': '改名失败了,再试一次。',
|
|
11
25
|
'session.new': '新会话',
|
|
12
26
|
'session.new.label': '在当前项目新建会话',
|
|
13
27
|
'session.empty': '这个项目还没有会话。',
|
|
@@ -33,11 +47,13 @@ export const zh = {
|
|
|
33
47
|
'panels.enabled': '这个项目已启用',
|
|
34
48
|
'panels.disabled': '这个项目未启用',
|
|
35
49
|
'panels.choose': '选择模块',
|
|
50
|
+
'panels.buildModule': '造一个模块',
|
|
36
51
|
'panels.failed': '改配置失败了,再试一次。',
|
|
37
52
|
'panels.hintEffect': '停用 = agent 拿不到相关数据',
|
|
38
53
|
'panels.dragHint': '拖动调整顺序',
|
|
39
54
|
'panels.unknownStep': '这一层没有套件提供',
|
|
40
55
|
'panels.labelWithHint': '{label}({hint})',
|
|
56
|
+
'panels.dep.failed': '⚠ 起不来',
|
|
41
57
|
'panels.dep.missingPlugin': '⚠ 还没装好',
|
|
42
58
|
'panels.dep.missingCommands': '⚠ 需要更新',
|
|
43
59
|
'panels.dep.unchecked': '⚠ 检测不了',
|
|
@@ -231,6 +247,8 @@ export const zh = {
|
|
|
231
247
|
'blocker.exec.hint': '确认 opencli 已安装且在 PATH 里(which opencli)。',
|
|
232
248
|
'blocker.unknown.title': '出了点问题',
|
|
233
249
|
'panels.reading': '正在读取面板配置…',
|
|
250
|
+
'panels.cardBroken': '这个模块起不来:{unit}({reason})',
|
|
251
|
+
'panels.cardRetry': '重试',
|
|
234
252
|
'panels.empty': '面板里还没有模块',
|
|
235
253
|
'panels.emptyHint': '也可以直接跟 agent 说:「把某个模块加到面板」。',
|
|
236
254
|
'env.title': '准备工作台',
|
|
@@ -269,6 +287,8 @@ export const zh = {
|
|
|
269
287
|
'env.step.warm': '预热站点登录态',
|
|
270
288
|
'env.warm.done': '已预热 {n} 个站点',
|
|
271
289
|
'env.warm.short': '这次没热上 —— 不影响用,面板第一次读取会自己补',
|
|
290
|
+
'env.unit.failed': '这个模块起不来',
|
|
291
|
+
'env.unit.failedHint': '它自己装配时报了错,别的模块不受影响。',
|
|
272
292
|
'env.unit.missingPlugin': '这个模块还没装好',
|
|
273
293
|
'env.unit.missingCommandsHint': '更新之后就能用了。',
|
|
274
294
|
'env.unit.unchecked': '检测不了:拿不到执行通道',
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ export * from './components/Activity.js';
|
|
|
14
14
|
export * from './components/ApprovalDetail.js';
|
|
15
15
|
export * from './components/ApprovalPlan.js';
|
|
16
16
|
export * from './components/Card.js';
|
|
17
|
+
export * from './components/CardBoundary.js';
|
|
17
18
|
export * from './components/CardHead.js';
|
|
18
19
|
export * from './components/ChoiceGroup.js';
|
|
19
20
|
export * from './components/LongTaskRow.js';
|
package/src/lib/react.ts
CHANGED
|
@@ -6,6 +6,8 @@ export const h = react.createElement;
|
|
|
6
6
|
|
|
7
7
|
export const Fragment = react.Fragment;
|
|
8
8
|
|
|
9
|
+
export const Component = react.Component;
|
|
10
|
+
|
|
9
11
|
export const useState = react.useState;
|
|
10
12
|
export const useEffect = react.useEffect;
|
|
11
13
|
export const useLayoutEffect = react.useLayoutEffect;
|