@dsh-workbench/shared 0.1.17 → 0.2.0

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.
@@ -8,6 +8,7 @@ export interface UnitCardDeclaration {
8
8
  export interface UnitToolDeclaration {
9
9
  readonly name: string;
10
10
  readonly writes: boolean;
11
+ readonly key?: string;
11
12
  }
12
13
  export interface UnitDeclaration {
13
14
  readonly apiVersion: number;
@@ -16,7 +16,8 @@ export const DECLARATION_REQUIRED_KEYS = [
16
16
  'host',
17
17
  ];
18
18
  const CARD_KEYS = ['titleKey', 'order'];
19
- const TOOL_KEYS = ['name', 'writes'];
19
+ const TOOL_KEYS = ['name', 'writes', 'key'];
20
+ const TOOL_KEY_SHAPE = /^[a-z][a-zA-Z0-9]*$/;
20
21
  const HOST_KEYS = ['inject', 'optional'];
21
22
  function text(value) {
22
23
  return typeof value === 'string' ? value.trim() : '';
@@ -89,6 +90,7 @@ export function declarationProblemsOf(subject, raw) {
89
90
  }
90
91
  else {
91
92
  const seen = new Set();
93
+ const seenKeys = new Set();
92
94
  tools.forEach((tool, index) => {
93
95
  const at = `dsh.workbench.tools[${index}]`;
94
96
  if (!isRecord(tool)) {
@@ -112,6 +114,19 @@ export function declarationProblemsOf(subject, raw) {
112
114
  if (typeof tool.writes !== 'boolean') {
113
115
  problems.push(`${subject}: ${at}.writes 要是 true / false(它是"这会写"的唯一声明,缺了审批无从认领)`);
114
116
  }
117
+ if (tool.key !== undefined) {
118
+ const key = text(tool.key);
119
+ if (!TOOL_KEY_SHAPE.test(key)) {
120
+ problems.push(`${subject}: ${at}.key 要是 lowerCamelCase 的标识符(如 \`sync\` / \`podLogs\`),` +
121
+ `现在是 ${JSON.stringify(tool.key)} —— 它会变成生成物里的键名,所以不引号、不许带点或横线`);
122
+ }
123
+ else if (seenKeys.has(key)) {
124
+ problems.push(`${subject}: ${at}.key「${key}」与前面那条撞了 —— 生成物里的键只能有一个`);
125
+ }
126
+ else {
127
+ seenKeys.add(key);
128
+ }
129
+ }
115
130
  });
116
131
  }
117
132
  }
@@ -1,5 +1,5 @@
1
1
  import type { PluginRelease } from '../api/unit.js';
2
- export type ProvisionAction = 'install' | 'update' | 'uninstall' | 'update-dependency' | 'install-bundle' | 'update-bundle' | 'remove-bundle' | 'cancel-install' | 'update-plugin' | 'uninstall-plugin';
2
+ export type ProvisionAction = 'install' | 'update' | 'uninstall' | 'update-dependency' | 'install-bundle' | 'update-bundle' | 'remove-bundle' | 'cancel-install' | 'update-plugin' | 'uninstall-plugin' | 'update-workbench';
3
3
  export interface PluginStatePayload {
4
4
  readonly name: string;
5
5
  readonly status: 'ok' | 'outdated' | 'missing';
@@ -45,7 +45,7 @@ export interface BoardModule {
45
45
  }
46
46
  export interface UnitClientFace {
47
47
  readonly unit: string;
48
- readonly card: (props: BoardModuleProps) => ReactNode;
48
+ readonly card?: (props: BoardModuleProps) => ReactNode;
49
49
  readonly steps?: Readonly<Record<string, UnitStep>>;
50
50
  readonly messages?: Dictionaries;
51
51
  readonly approval?: {
@@ -3,5 +3,7 @@ export interface IconProps {
3
3
  }
4
4
  export declare function IconConversation({ size }: IconProps): JSX.Element;
5
5
  export declare function IconBrandMark({ size }: IconProps): JSX.Element;
6
+ export declare function IconOpencli({ size }: IconProps): JSX.Element;
7
+ export declare function IconNpm({ size }: IconProps): JSX.Element;
6
8
  export declare function IconModules({ size }: IconProps): JSX.Element;
7
9
  export declare function IconPanelScreen({ size }: IconProps): JSX.Element;
@@ -11,6 +11,20 @@ export function IconBrandMark({ size = 24 }) {
11
11
  h("rect", { x: 3, y: 3, width: 18, height: 18, rx: 5, stroke: "currentColor" }),
12
12
  h("path", { d: "M8 9.5h8M8 12.5h8M8 15.5h5", stroke: "currentColor", strokeLinecap: "round" })));
13
13
  }
14
+ export function IconOpencli({ size = 16 }) {
15
+ return (h(Shell, { size: size, viewBox: "0 0 64 64" },
16
+ h("defs", null,
17
+ h("linearGradient", { id: "opencli-mark", x1: "0%", y1: "0%", x2: "100%", y2: "100%" },
18
+ h("stop", { offset: "0%", stopColor: "#00e5a0" }),
19
+ h("stop", { offset: "50%", stopColor: "#00b4d8" }),
20
+ h("stop", { offset: "100%", stopColor: "#7b61ff" }))),
21
+ h("rect", { width: 64, height: 64, rx: 14, fill: "#0a0a0f" }),
22
+ h("text", { x: "50%", y: "54%", dominantBaseline: "middle", textAnchor: "middle", fontFamily: "monospace", fontWeight: 700, fontSize: 28, fill: "url(#opencli-mark)" }, ">_")));
23
+ }
24
+ export function IconNpm({ size = 16 }) {
25
+ return (h(Shell, { size: size, viewBox: "0 0 1024 1024" },
26
+ h("path", { d: "M64 337.76h896v298.72H512v49.792H312.896v-49.824H64z m49.792 248.928h99.552v-149.376h49.76v149.344h49.792v-199.104H113.792z m248.896-199.136v248.928h99.552v-49.792h99.552v-199.136z m99.552 49.792H512v99.584h-49.792z m149.344-49.792v199.136h99.552v-149.376h49.792v149.344h49.792v-149.344h49.792v149.344h49.792v-199.104z", fill: "#CB3837" })));
27
+ }
14
28
  export function IconModules({ size = 16 }) {
15
29
  return (h("svg", { width: size, height: size, viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true" },
16
30
  h("rect", { x: "2.5", y: "2.5", width: "4.6", height: "4.6", rx: "1.2", stroke: "currentColor", strokeWidth: "1.2" }),
@@ -1,6 +1,6 @@
1
1
  import type { ProvisionAction } from '../api/workbench-contract.js';
2
2
  export type { ProvisionAction };
3
- export declare const PROVISION_ACTIONS: readonly ["install", "update", "uninstall", "install-bundle", "update-bundle", "remove-bundle", "cancel-install", "update-dependency", "update-plugin", "uninstall-plugin"];
3
+ export declare const PROVISION_ACTIONS: readonly ["install", "update", "uninstall", "install-bundle", "update-bundle", "remove-bundle", "cancel-install", "update-dependency", "update-plugin", "uninstall-plugin", "update-workbench"];
4
4
  export declare const DEPENDENCY_ACTIONS: readonly ["update-dependency"];
5
5
  export declare function isDependencyAction(action: ProvisionAction): action is (typeof DEPENDENCY_ACTIONS)[number];
6
6
  export type ProvisionTarget = 'unit' | 'id' | 'spec' | 'name' | 'requestId';
@@ -9,6 +9,7 @@ export const PROVISION_ACTIONS = [
9
9
  'update-dependency',
10
10
  'update-plugin',
11
11
  'uninstall-plugin',
12
+ 'update-workbench',
12
13
  ];
13
14
  export const DEPENDENCY_ACTIONS = ['update-dependency'];
14
15
  export function isDependencyAction(action) {
@@ -25,6 +26,8 @@ export function provisionTargetOf(action) {
25
26
  return 'name';
26
27
  if (action === 'update-plugin' || action === 'uninstall-plugin')
27
28
  return 'name';
29
+ if (action === 'update-workbench')
30
+ return 'name';
28
31
  if (action === 'cancel-install')
29
32
  return 'requestId';
30
33
  return 'unit';
@@ -54,6 +57,8 @@ function requestOf(action, value) {
54
57
  return { action, name: value };
55
58
  if (action === 'update-plugin' || action === 'uninstall-plugin')
56
59
  return { action, name: value };
60
+ if (action === 'update-workbench')
61
+ return { action, name: value };
57
62
  if (action === 'cancel-install')
58
63
  return { action, requestId: value };
59
64
  return { action, unit: value };
package/dist/i18n/en.js CHANGED
@@ -40,7 +40,12 @@ export const en = {
40
40
  'time.hours': '{n} h ago',
41
41
  'time.days': '{n} d ago',
42
42
  'panels.title': 'Panel modules',
43
- 'panels.reset': 'Reset',
43
+ 'panels.market.defaultNote': 'This default covers every workspace that has not changed this row; ones that have keep their own choice.',
44
+ 'panels.market.changedBy': '{count} workspaces have already changed this row',
45
+ 'panels.market.defaultLabel': 'On by default in all workspaces',
46
+ 'panels.followGlobal': 'Default',
47
+ 'panels.followGlobalHint': 'This row follows the default board (the switch shows that state). Clicking the switch makes it this project\'s own choice.',
48
+ 'panels.followAll': 'Restore the default everywhere',
44
49
  'panels.enabled': 'enabled in this project',
45
50
  'panels.disabled': 'not enabled in this project',
46
51
  'panels.choose': 'Choose modules',
@@ -59,17 +64,17 @@ export const en = {
59
64
  'panels.browseMarket': 'Browse the module market',
60
65
  'panels.noneInstalled': 'None of these modules has its plugin installed yet',
61
66
  'panels.noneInstalledHint': 'Once installed they come back here, and you pick which go on the panel.',
67
+ 'panels.rowPlugin': 'Plugin',
62
68
  'panels.market.installed': 'installed',
63
69
  'panels.market.outdated': 'needs update',
64
70
  'panels.market.hasNew': 'New version',
65
71
  'panels.market.missing': 'not installed',
66
72
  'panels.market.unchecked': 'unknown',
67
73
  'panels.market.checking': 'checking',
68
- 'panels.market.sectionInstalled': 'Installed',
74
+ 'panels.market.sectionInstalled': 'Installed modules',
69
75
  'panels.market.sectionPending': 'Not installed',
70
76
  'panels.market.sectionShared': 'Shared dependencies',
71
- 'panels.market.sharedUsedBy': 'Used by {modules}',
72
- 'panels.market.sharedFoot': 'These cannot be uninstalled here: they come with modules, and would be reinstalled anyway. To stop using one, turn off the modules that use it.',
77
+ 'panels.market.sharedUsedBy': 'Used by module {modules}',
73
78
  'panels.market.listSeparator': ', ',
74
79
  'panels.market.sharedMissing': 'not installed',
75
80
  'panels.market.sharedOutdated': 'update available',
@@ -94,21 +99,20 @@ export const en = {
94
99
  'panels.market.noSource': 'This module declares no install method — install it yourself',
95
100
  'panels.market.badSource': 'The install method is incomplete — install it yourself',
96
101
  'panels.market.empty': 'No modules available',
97
- 'panels.market.installedSection': 'Installed',
98
- 'panels.market.sectionLocal': 'Linked locally',
102
+ 'panels.market.sectionLocal': 'Local modules',
99
103
  'panels.market.sectionUnlisted': 'Not from the market',
100
104
  'panels.market.sectionPlugins': 'Installed opencli plugins',
101
105
  'panels.market.moduleOutdated': 'update available',
102
106
  'panels.market.moduleRefused': 'This module was built for an older version of the workbench, so it does not work here. Update it, or uninstall.',
103
107
  'panels.market.moduleRefusedDetail': 'Why it cannot be used: {reason}',
104
108
  'panels.market.moduleRestartHint': 'installed but not active — restart dsh once to bring it up',
105
- 'panels.market.pluginLocalNoUpdate': 'Installed from a local folder, so there is nowhere to check for a newer version — uninstall only',
109
+ 'panels.market.workbenchName': 'Workbench',
110
+ 'panels.market.workbenchLocal': 'This is the copy from your working repository; it does not come from the registry.',
106
111
  'panels.market.pluginKeptInUse': 'Files kept for now: {names} still uses it.',
107
112
  'panels.market.pluginKeptUnsure': 'Files kept for now: some modules on this machine do not say what they use, so it is unclear who needs it.',
108
113
  'panels.market.pluginKeptHeld': 'Files kept: this one is yours, and a module letting go will not remove it.',
109
114
  'panels.market.pluginKeptUnnamed': 'Files kept for now: this one cannot be identified as a plugin, so it is unclear who needs it.',
110
- 'panels.market.pluginAlsoKnownAs': 'Its package is {pkg} (updates and uninstall use that)',
111
- 'panels.market.pluginBroken': 'This install is broken: it cannot be identified as a plugin. Uninstall it, or install it again by package name above.',
115
+ 'panels.market.pluginBroken': 'This install is broken: it cannot be identified as a plugin. Uninstall it, or tell the agent to reinstall it by package name with workbench_modules (the install-bundle action).',
112
116
  'panels.market.restartNeeded': 'The new version is installed — restart once to pick it up.',
113
117
  'panels.market.foot': 'After installing, enable it in the panel page of that project.',
114
118
  'panels.market.recheck': 'Check for updates',
@@ -117,7 +121,7 @@ export const en = {
117
121
  'panels.market.justNow': 'just now',
118
122
  'panels.market.minutesAgo': '{n} min ago',
119
123
  'panels.market.hoursAgo': '{n} h ago',
120
- 'panels.market.add.description': 'Enter a package name, a Git URL, or a path to a local directory.',
124
+ "panels.market.add.description": "Enter a module or opencli plugin package name, Git repository URL, or local directory path.",
121
125
  'panels.market.add.specPlaceholder': 'e.g. @dsh-workbench/module-hello',
122
126
  'panels.market.add.guideToggle': 'Install guide and examples',
123
127
  'panels.market.add.guideHide': 'Hide guide',
@@ -156,11 +160,13 @@ export const en = {
156
160
  'panels.market.add.problemInstalled': 'This module is already installed',
157
161
  'panels.market.add.problemNotFound': 'No such module found',
158
162
  'panels.market.add.problemNotPackage': 'That path does not exist or is not a valid module package',
159
- 'panels.market.add.problemNotBundle': 'This package cannot be installed here: {reason}. If it is actually an opencli plugin, use its package name (for example opencli-plugin-cmdb) or a local folder — a git URL or tarball does not reveal what it is.',
163
+ 'panels.market.add.problemNotBundle': 'This package cannot be installed here: {reason}. This entry recognises workbench modules and opencli plugins by package name, and installs either; but a git URL or a tarball does not reveal what it is — if it is really one of those two, use its package name or a local folder instead.',
160
164
  'panels.market.add.problemNotWorkbenchModule': 'This package does not match the workbench you are running, so installing it would not work. Pick another version, or ask the module author.',
161
165
  'panels.market.add.problemNotModuleOrPlugin': 'This package is neither a workbench module nor an opencli plugin, so installing it would do nothing. Pick another version, or ask the package author.',
162
166
  'panels.market.add.failureNotModuleOrPlugin': 'This package is neither a workbench module nor an opencli plugin, so installing it would do nothing',
163
167
  'panels.market.add.installedPlugin': 'Installed: the opencli plugin {name}',
168
+ 'panels.market.add.pluginPreview': 'This is an opencli plugin: it goes into the opencli plugin area and works as soon as it is installed — no restart.',
169
+ 'panels.market.add.pluginAlias': 'Plugin name: {name}',
164
170
  'panels.market.add.problemNetwork': 'Cannot reach the module source; check the network and retry',
165
171
  'panels.market.add.problemUnknown': 'Cannot fetch module information: {reason}',
166
172
  'panels.market.add.failureNetwork': 'The network connection failed',
@@ -42,7 +42,12 @@ export declare const dictionaries: {
42
42
  readonly 'time.hours': "{n} 小时前";
43
43
  readonly 'time.days': "{n} 天前";
44
44
  readonly 'panels.title': "面板模块";
45
- readonly 'panels.reset': "恢复默认";
45
+ readonly 'panels.market.defaultNote': "这份默认给所有没在这一行改过的工作区;改过的保持自己的选择。";
46
+ readonly 'panels.market.changedBy': "{count} 个工作区已在这一行改过";
47
+ readonly 'panels.market.defaultLabel': "开启后所有工作区默认启用";
48
+ readonly 'panels.followGlobal': "默认";
49
+ readonly 'panels.followGlobalHint': "这一行跟着默认板走(右边那颗开关显示的就是默认的状态)。点一下开关 = 改成这个项目自己定。";
50
+ readonly 'panels.followAll': "全部恢复默认";
46
51
  readonly 'panels.enabled': "这个项目已启用";
47
52
  readonly 'panels.disabled': "这个项目未启用";
48
53
  readonly 'panels.choose': "选择模块";
@@ -61,17 +66,17 @@ export declare const dictionaries: {
61
66
  readonly 'panels.browseMarket': "浏览模块市场";
62
67
  readonly 'panels.noneInstalled': "这些模块要的插件都还没装";
63
68
  readonly 'panels.noneInstalledHint': "装好之后它们会回到这里,再由你决定放哪几个上板。";
69
+ readonly 'panels.rowPlugin': "插件";
64
70
  readonly 'panels.market.installed': "已安装";
65
71
  readonly 'panels.market.outdated': "要更新";
66
72
  readonly 'panels.market.hasNew': "有新版本";
67
73
  readonly 'panels.market.missing': "未装";
68
74
  readonly 'panels.market.unchecked': "状态未知";
69
75
  readonly 'panels.market.checking': "检测中";
70
- readonly 'panels.market.sectionInstalled': "已安装";
76
+ readonly 'panels.market.sectionInstalled': "已安装模块";
71
77
  readonly 'panels.market.sectionPending': "未安装";
72
78
  readonly 'panels.market.sectionShared': "共用依赖";
73
- readonly 'panels.market.sharedUsedBy': "供 {modules} 使用";
74
- readonly 'panels.market.sharedFoot': "这一段不给卸载:它们跟着模块来,卸掉也会被装回来。要停用一份,就去关掉正在用它的那些模块。";
79
+ readonly 'panels.market.sharedUsedBy': "供 {modules} 模块使用";
75
80
  readonly 'panels.market.listSeparator': "、";
76
81
  readonly 'panels.market.sharedMissing': "未装";
77
82
  readonly 'panels.market.sharedOutdated': "有新版";
@@ -96,21 +101,20 @@ export declare const dictionaries: {
96
101
  readonly 'panels.market.noSource': "这个模块没声明怎么装,只能自己动手";
97
102
  readonly 'panels.market.badSource': "安装方式没写全,只能自己动手";
98
103
  readonly 'panels.market.empty': "暂无可用模块";
99
- readonly 'panels.market.installedSection': "已安装";
100
- readonly 'panels.market.sectionLocal': "本地装的";
104
+ readonly 'panels.market.sectionLocal': "本地模块";
101
105
  readonly 'panels.market.sectionUnlisted': "不是从市场装的";
102
106
  readonly 'panels.market.sectionPlugins': "已安装 opencli 插件";
103
107
  readonly 'panels.market.moduleOutdated': "有新版";
104
108
  readonly 'panels.market.moduleRefused': "这个模块是给旧版本的工作台写的,现在用不了。更新它,或者卸载。";
105
109
  readonly 'panels.market.moduleRefusedDetail': "装不上的原因:{reason}";
106
110
  readonly 'panels.market.moduleRestartHint': "装好了但还没生效 —— 重启一次 dsh 就会出来";
107
- readonly 'panels.market.pluginLocalNoUpdate': "本地装的,没地方查新版,所以只给卸载";
111
+ readonly 'panels.market.workbenchName': "工作台";
112
+ readonly 'panels.market.workbenchLocal': "这份是本仓正在开发的那一份,不从源上取。";
108
113
  readonly 'panels.market.pluginKeptInUse': "文件先留着:{names} 还要用它。";
109
114
  readonly 'panels.market.pluginKeptUnsure': "文件先留着:这台机器上还有模块看不出用了什么,说不准谁在用它。";
110
115
  readonly 'panels.market.pluginKeptHeld': "文件留着:你自己装的那一份,模块放手不动它。";
111
116
  readonly 'panels.market.pluginKeptUnnamed': "文件先留着:看不出这是哪个插件,说不准谁在用它。";
112
- readonly 'panels.market.pluginAlsoKnownAs': "包名是 {pkg}(更新与卸载按它来)";
113
- readonly 'panels.market.pluginBroken': "这份装坏了:看不出它是个插件。可以卸载,或者去上面按包名再装一次。";
117
+ readonly 'panels.market.pluginBroken': "这份装坏了:看不出它是个插件。可以卸载,或者直接跟 agent 说:用 workbench_modules 的 install-bundle 按包名再装一次。";
114
118
  readonly 'panels.market.restartNeeded': "新版装好了,重启一次才生效。";
115
119
  readonly 'panels.market.foot': "装好后到那个项目的面板页勾选它,模块才会出现。";
116
120
  readonly 'panels.market.recheck': "重新检查更新";
@@ -119,7 +123,7 @@ export declare const dictionaries: {
119
123
  readonly 'panels.market.justNow': "刚刚";
120
124
  readonly 'panels.market.minutesAgo': "{n} 分钟前";
121
125
  readonly 'panels.market.hoursAgo': "{n} 小时前";
122
- readonly 'panels.market.add.description': "输入模块的包名、Git 仓库地址或本地目录路径。";
126
+ readonly 'panels.market.add.description': "输入模块或 opencli 插件的包名、Git 仓库地址或本地目录路径。";
123
127
  readonly 'panels.market.add.specPlaceholder': "例如 @dsh-workbench/module-hello";
124
128
  readonly 'panels.market.add.guideToggle': "模块安装引导和示例";
125
129
  readonly 'panels.market.add.guideHide': "收起引导";
@@ -158,11 +162,13 @@ export declare const dictionaries: {
158
162
  readonly 'panels.market.add.problemInstalled': "该模块已安装";
159
163
  readonly 'panels.market.add.problemNotFound': "未找到相关模块";
160
164
  readonly 'panels.market.add.problemNotPackage': "该路径不存在或不是有效的模块包";
161
- readonly 'panels.market.add.problemNotBundle': "这个包装不进这里:{reason}。要是它其实是个 opencli 插件,请改用包名(例如 opencli-plugin-cmdb)或者本地目录 —— 从 git 地址和 tarball 判断不出它是什么。";
165
+ readonly 'panels.market.add.problemNotBundle': "这个包装不进这里:{reason}。这个入口按包名认工作台模块和 opencli 插件,两种都能装;但 git 地址和 tarball 判断不出它是什么 —— 要是它其实是这两样之一,请改填包名或本地目录。";
162
166
  readonly 'panels.market.add.problemNotWorkbenchModule': "这个包跟现在的工作台对不上,装上也用不了。换一个版本,或者找这个模块的作者。";
163
167
  readonly 'panels.market.add.problemNotModuleOrPlugin': "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上。换一个版本,或者找这个包的作者。";
164
168
  readonly 'panels.market.add.failureNotModuleOrPlugin': "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上";
165
169
  readonly 'panels.market.add.installedPlugin': "装上了:opencli 插件 {name}";
170
+ readonly 'panels.market.add.pluginPreview': "这是一份 opencli 插件:装进 opencli 的插件区,装完就能用,不用重启。";
171
+ readonly 'panels.market.add.pluginAlias': "插件名:{name}";
166
172
  readonly 'panels.market.add.problemNetwork': "无法连接模块源,请检查网络后重试";
167
173
  readonly 'panels.market.add.problemUnknown': "无法获取模块信息:{reason}";
168
174
  readonly 'panels.market.add.failureNetwork': "网络连接失败";
@@ -329,7 +335,7 @@ export declare const dictionaries: {
329
335
  readonly 'panel.conversation': "对话";
330
336
  readonly 'panels.label': "全局面板";
331
337
  };
332
- en: Record<"time.now" | "time.minutes" | "time.hours" | "time.days" | "blocker.retry" | "blocker.copy" | "blocker.unknown.title" | "blocker.login.go" | "blocker.login.titleGeneric" | "blocker.login.title" | "blocker.login.manual" | "blocker.login.hint" | "blocker.forbidden.open" | "blocker.forbidden.titleGeneric" | "blocker.forbidden.title" | "blocker.forbidden.hint" | "blocker.timeout.hint" | "blocker.exec.hint" | "blocker.fix.install" | "blocker.fix.update" | "blocker.market.open" | "env.unit.failed" | "env.unit.failedHint" | "env.unit.missingPlugin" | "env.unit.hint" | "env.unit.missingCommands" | "env.unit.missingCommandsHint" | "env.unit.unchecked" | "env.unit.uncheckedHint" | "brand.name" | "toggle.open" | "toggle.collapse" | "project.label" | "workspace.section" | "project.none" | "project.empty" | "project.add" | "project.search.label" | "project.search.placeholder" | "project.search.empty" | "project.rename.row" | "project.rename.current" | "project.rename.title" | "project.rename.description" | "project.rename.field" | "project.rename.placeholder" | "project.rename.hint" | "project.rename.save" | "project.rename.saving" | "project.rename.cancel" | "project.rename.failed" | "session.new" | "session.new.label" | "session.empty" | "session.noProject" | "session.pinned" | "session.actions" | "status.idle" | "status.running" | "status.waitingApproval" | "status.planReview" | "status.waitingAnswer" | "status.completed" | "status.subagentsRunning.one" | "status.subagentsRunning.other" | "panels.title" | "panels.reset" | "panels.enabled" | "panels.disabled" | "panels.choose" | "panels.buildModule" | "panels.failed" | "panels.hintEffect" | "panels.dragHint" | "panels.unknownStep" | "panels.labelWithHint" | "panels.dep.failed" | "panels.dep.missingPlugin" | "panels.dep.missingCommands" | "panels.dep.unchecked" | "panels.tab.modules" | "panels.tab.market" | "panels.browseMarket" | "panels.noneInstalled" | "panels.noneInstalledHint" | "panels.market.installed" | "panels.market.outdated" | "panels.market.hasNew" | "panels.market.missing" | "panels.market.unchecked" | "panels.market.checking" | "panels.market.sectionInstalled" | "panels.market.sectionPending" | "panels.market.sectionShared" | "panels.market.sharedUsedBy" | "panels.market.sharedFoot" | "panels.market.listSeparator" | "panels.market.sharedMissing" | "panels.market.sharedOutdated" | "panels.market.sharedUnsatisfied" | "panels.market.sharedOk" | "panels.market.versionArrow" | "panels.market.lead" | "panels.market.install" | "panels.market.update" | "panels.market.uninstall" | "panels.market.uninstallLabel" | "panels.market.installing" | "panels.market.updating" | "panels.market.uninstalling" | "panels.market.confirmUninstallTitle" | "panels.market.confirmUninstallDetail" | "panels.market.confirmUninstallAck" | "panels.market.confirmYes" | "panels.market.confirmNo" | "panels.market.failed" | "panels.market.needEnv" | "panels.market.noSource" | "panels.market.badSource" | "panels.market.empty" | "panels.market.installedSection" | "panels.market.sectionLocal" | "panels.market.sectionUnlisted" | "panels.market.sectionPlugins" | "panels.market.moduleOutdated" | "panels.market.moduleRefused" | "panels.market.moduleRefusedDetail" | "panels.market.moduleRestartHint" | "panels.market.pluginLocalNoUpdate" | "panels.market.pluginKeptInUse" | "panels.market.pluginKeptUnsure" | "panels.market.pluginKeptHeld" | "panels.market.pluginKeptUnnamed" | "panels.market.pluginAlsoKnownAs" | "panels.market.pluginBroken" | "panels.market.restartNeeded" | "panels.market.foot" | "panels.market.recheck" | "panels.market.checkUpdates" | "panels.market.checkedAt" | "panels.market.justNow" | "panels.market.minutesAgo" | "panels.market.hoursAgo" | "panels.market.add.description" | "panels.market.add.specPlaceholder" | "panels.market.add.guideToggle" | "panels.market.add.guideHide" | "panels.market.add.guideIdTitle" | "panels.market.add.guideIdExample" | "panels.market.add.guideIdHint" | "panels.market.add.guideGitTitle" | "panels.market.add.guideGitExample" | "panels.market.add.guideGitHint" | "panels.market.add.guidePathTitle" | "panels.market.add.guidePathExample" | "panels.market.add.guidePathHint" | "panels.market.add.guideExampleLabel" | "panels.market.add.guideFill" | "panels.market.add.guideSafety" | "panels.market.add.registryLegend" | "panels.market.add.registryOfficial" | "panels.market.add.registryMirror" | "panels.market.add.registryWithHost" | "panels.market.add.registryToggle" | "panels.market.add.installVersion" | "panels.market.add.registryDefault" | "panels.market.add.registryCustom" | "panels.market.add.registryCustomPlaceholder" | "panels.market.add.registryCustomHint" | "panels.market.add.registryCustomInvalid" | "panels.market.add.run" | "panels.market.add.edit" | "panels.market.add.changeRegistry" | "panels.market.add.installingTitle" | "panels.market.add.installedTitle" | "panels.market.add.failedTitle" | "panels.market.add.cancelledTitle" | "panels.market.add.failureNetworkHost" | "panels.market.add.problemInvalid" | "panels.market.add.problemInstalled" | "panels.market.add.problemNotFound" | "panels.market.add.problemNotPackage" | "panels.market.add.problemNotBundle" | "panels.market.add.problemNotWorkbenchModule" | "panels.market.add.problemNotModuleOrPlugin" | "panels.market.add.failureNotModuleOrPlugin" | "panels.market.add.installedPlugin" | "panels.market.add.problemNetwork" | "panels.market.add.problemUnknown" | "panels.market.add.failureNetwork" | "panels.market.add.failureNotFound" | "panels.market.add.failureNoMatchingVersion" | "panels.market.add.failureDiskFull" | "panels.market.add.failurePermission" | "panels.market.add.failureBuildBlocked" | "panels.market.add.failureIntegrity" | "panels.market.add.failureTimeout" | "panels.market.add.failurePnpmMissing" | "panels.market.add.failureGeneric" | "panels.market.add.approveTitle" | "panels.market.add.approveCaution" | "panels.market.add.approveRun" | "panels.market.add.restart" | "panels.market.add.tookEffect" | "panels.market.add.details" | "panels.market.add.open" | "panels.market.add.title" | "panels.market.add.intro" | "panels.market.add.specLabel" | "panels.market.add.specHint" | "panels.market.add.exampleTitle" | "panels.market.add.exampleFill" | "panels.market.add.registryLabel" | "panels.market.add.registryProfile" | "panels.market.add.registryPnpm" | "panels.market.add.check" | "panels.market.add.checking" | "panels.market.add.classifying" | "panels.market.add.change" | "panels.market.add.previewTitle" | "panels.market.add.fieldName" | "panels.market.add.fieldVersion" | "panels.market.add.fieldDescription" | "panels.market.add.fieldSource" | "panels.market.add.install" | "panels.market.add.installing" | "panels.market.add.cancel" | "panels.market.add.close" | "panels.market.add.retry" | "panels.market.add.changeSource" | "panels.market.add.approveBuilds" | "panels.market.add.approveHint" | "panels.market.add.doneApplied" | "panels.market.add.doneRestart" | "panels.market.add.cancelled" | "panels.market.add.failed" | "panels.market.add.removed" | "panels.market.add.supplyPending" | "panels.market.add.supplyFailed" | "panels.market.add.notModule" | "panels.market.add.removedRestart" | "panels.market.add.remove" | "panels.market.add.problem.invalidSpec" | "panels.market.add.problem.alreadyInstalled" | "panels.market.add.problem.notFound" | "panels.market.add.problem.notAPackage" | "panels.market.add.problem.notABundle" | "panels.market.add.problem.network" | "panels.market.add.problem.unknown" | "panels.market.add.reason.invalidSpec" | "panels.market.add.reason.incompatible" | "panels.market.add.reason.notBundle" | "panels.market.add.reason.notRemovable" | "panels.market.add.reason.bundleInUse" | "panels.market.add.reason.stopProfile" | "panels.market.add.reason.staleApproval" | "panels.market.add.reason.operationError" | "panels.market.add.reason.buildBlocked" | "panels.market.add.reason.pnpmMissing" | "panels.market.add.reason.noMatchingVersion" | "panels.market.add.reason.diskFull" | "panels.market.add.reason.permission" | "panels.market.add.reason.integrity" | "panels.market.add.reason.timeout" | "panels.market.add.reason.network" | "panels.market.add.reason.notFound" | "panels.market.add.reason.unknown" | "blocker.fix.running" | "blocker.fix.failed" | "blocker.retry.running" | "panels.reading" | "panels.cardBroken" | "panels.cardRetry" | "panels.empty" | "panels.emptyHint" | "env.title" | "env.checking" | "env.installing" | "env.failed" | "env.elapsed" | "env.stepFailed.install" | "env.stepFailed.path" | "env.stepFailed.shell" | "env.bridge.ok" | "env.bridge.short" | "env.bridge.why" | "env.bridge.daemon" | "env.bridge.extension" | "env.bridge.extensionLink" | "env.bridge.profile" | "env.bridge.stale" | "env.bridge.unknown" | "env.fix.title" | "env.fix.why" | "env.fix.whyPath" | "env.fix.whyShell" | "env.fix.step" | "env.fix.stepPath" | "env.fix.stepShell" | "env.fix.permission" | "env.fix.network" | "env.fix.details" | "env.retry" | "env.copyCommand" | "env.copied" | "env.step.opencli" | "env.step.codingPlugin" | "env.step.bridge" | "env.step.warm" | "env.warm.done" | "env.warm.short" | "rightbar.label" | "rightbar.expand" | "rightbar.collapse" | "rightbar.column.label" | "rightbar.column.workbench" | "rightbar.column.dsh" | "rightbar.refresh" | "rightbar.refreshing" | "rightbar.updatedAt" | "rightbar.noProject" | "rightbar.openPlatform" | "coding.notARepo.title" | "error.unknown" | "board.title" | "panel.conversation" | "panels.label", string>;
338
+ en: Record<"time.now" | "time.minutes" | "time.hours" | "time.days" | "blocker.retry" | "blocker.copy" | "blocker.unknown.title" | "blocker.login.go" | "blocker.login.titleGeneric" | "blocker.login.title" | "blocker.login.manual" | "blocker.login.hint" | "blocker.forbidden.open" | "blocker.forbidden.titleGeneric" | "blocker.forbidden.title" | "blocker.forbidden.hint" | "blocker.timeout.hint" | "blocker.exec.hint" | "blocker.fix.install" | "blocker.fix.update" | "blocker.market.open" | "env.unit.failed" | "env.unit.failedHint" | "env.unit.missingPlugin" | "env.unit.hint" | "env.unit.missingCommands" | "env.unit.missingCommandsHint" | "env.unit.unchecked" | "env.unit.uncheckedHint" | "brand.name" | "toggle.open" | "toggle.collapse" | "project.label" | "workspace.section" | "project.none" | "project.empty" | "project.add" | "project.search.label" | "project.search.placeholder" | "project.search.empty" | "project.rename.row" | "project.rename.current" | "project.rename.title" | "project.rename.description" | "project.rename.field" | "project.rename.placeholder" | "project.rename.hint" | "project.rename.save" | "project.rename.saving" | "project.rename.cancel" | "project.rename.failed" | "session.new" | "session.new.label" | "session.empty" | "session.noProject" | "session.pinned" | "session.actions" | "status.idle" | "status.running" | "status.waitingApproval" | "status.planReview" | "status.waitingAnswer" | "status.completed" | "status.subagentsRunning.one" | "status.subagentsRunning.other" | "panels.title" | "panels.market.defaultNote" | "panels.market.changedBy" | "panels.market.defaultLabel" | "panels.followGlobal" | "panels.followGlobalHint" | "panels.followAll" | "panels.enabled" | "panels.disabled" | "panels.choose" | "panels.buildModule" | "panels.failed" | "panels.hintEffect" | "panels.dragHint" | "panels.unknownStep" | "panels.labelWithHint" | "panels.dep.failed" | "panels.dep.missingPlugin" | "panels.dep.missingCommands" | "panels.dep.unchecked" | "panels.tab.modules" | "panels.tab.market" | "panels.browseMarket" | "panels.noneInstalled" | "panels.noneInstalledHint" | "panels.rowPlugin" | "panels.market.installed" | "panels.market.outdated" | "panels.market.hasNew" | "panels.market.missing" | "panels.market.unchecked" | "panels.market.checking" | "panels.market.sectionInstalled" | "panels.market.sectionPending" | "panels.market.sectionShared" | "panels.market.sharedUsedBy" | "panels.market.listSeparator" | "panels.market.sharedMissing" | "panels.market.sharedOutdated" | "panels.market.sharedUnsatisfied" | "panels.market.sharedOk" | "panels.market.versionArrow" | "panels.market.lead" | "panels.market.install" | "panels.market.update" | "panels.market.uninstall" | "panels.market.uninstallLabel" | "panels.market.installing" | "panels.market.updating" | "panels.market.uninstalling" | "panels.market.confirmUninstallTitle" | "panels.market.confirmUninstallDetail" | "panels.market.confirmUninstallAck" | "panels.market.confirmYes" | "panels.market.confirmNo" | "panels.market.failed" | "panels.market.needEnv" | "panels.market.noSource" | "panels.market.badSource" | "panels.market.empty" | "panels.market.sectionLocal" | "panels.market.sectionUnlisted" | "panels.market.sectionPlugins" | "panels.market.moduleOutdated" | "panels.market.moduleRefused" | "panels.market.moduleRefusedDetail" | "panels.market.moduleRestartHint" | "panels.market.workbenchName" | "panels.market.workbenchLocal" | "panels.market.pluginKeptInUse" | "panels.market.pluginKeptUnsure" | "panels.market.pluginKeptHeld" | "panels.market.pluginKeptUnnamed" | "panels.market.pluginBroken" | "panels.market.restartNeeded" | "panels.market.foot" | "panels.market.recheck" | "panels.market.checkUpdates" | "panels.market.checkedAt" | "panels.market.justNow" | "panels.market.minutesAgo" | "panels.market.hoursAgo" | "panels.market.add.description" | "panels.market.add.specPlaceholder" | "panels.market.add.guideToggle" | "panels.market.add.guideHide" | "panels.market.add.guideIdTitle" | "panels.market.add.guideIdExample" | "panels.market.add.guideIdHint" | "panels.market.add.guideGitTitle" | "panels.market.add.guideGitExample" | "panels.market.add.guideGitHint" | "panels.market.add.guidePathTitle" | "panels.market.add.guidePathExample" | "panels.market.add.guidePathHint" | "panels.market.add.guideExampleLabel" | "panels.market.add.guideFill" | "panels.market.add.guideSafety" | "panels.market.add.registryLegend" | "panels.market.add.registryOfficial" | "panels.market.add.registryMirror" | "panels.market.add.registryWithHost" | "panels.market.add.registryToggle" | "panels.market.add.installVersion" | "panels.market.add.registryDefault" | "panels.market.add.registryCustom" | "panels.market.add.registryCustomPlaceholder" | "panels.market.add.registryCustomHint" | "panels.market.add.registryCustomInvalid" | "panels.market.add.run" | "panels.market.add.edit" | "panels.market.add.changeRegistry" | "panels.market.add.installingTitle" | "panels.market.add.installedTitle" | "panels.market.add.failedTitle" | "panels.market.add.cancelledTitle" | "panels.market.add.failureNetworkHost" | "panels.market.add.problemInvalid" | "panels.market.add.problemInstalled" | "panels.market.add.problemNotFound" | "panels.market.add.problemNotPackage" | "panels.market.add.problemNotBundle" | "panels.market.add.problemNotWorkbenchModule" | "panels.market.add.problemNotModuleOrPlugin" | "panels.market.add.failureNotModuleOrPlugin" | "panels.market.add.installedPlugin" | "panels.market.add.pluginPreview" | "panels.market.add.pluginAlias" | "panels.market.add.problemNetwork" | "panels.market.add.problemUnknown" | "panels.market.add.failureNetwork" | "panels.market.add.failureNotFound" | "panels.market.add.failureNoMatchingVersion" | "panels.market.add.failureDiskFull" | "panels.market.add.failurePermission" | "panels.market.add.failureBuildBlocked" | "panels.market.add.failureIntegrity" | "panels.market.add.failureTimeout" | "panels.market.add.failurePnpmMissing" | "panels.market.add.failureGeneric" | "panels.market.add.approveTitle" | "panels.market.add.approveCaution" | "panels.market.add.approveRun" | "panels.market.add.restart" | "panels.market.add.tookEffect" | "panels.market.add.details" | "panels.market.add.open" | "panels.market.add.title" | "panels.market.add.intro" | "panels.market.add.specLabel" | "panels.market.add.specHint" | "panels.market.add.exampleTitle" | "panels.market.add.exampleFill" | "panels.market.add.registryLabel" | "panels.market.add.registryProfile" | "panels.market.add.registryPnpm" | "panels.market.add.check" | "panels.market.add.checking" | "panels.market.add.classifying" | "panels.market.add.change" | "panels.market.add.previewTitle" | "panels.market.add.fieldName" | "panels.market.add.fieldVersion" | "panels.market.add.fieldDescription" | "panels.market.add.fieldSource" | "panels.market.add.install" | "panels.market.add.installing" | "panels.market.add.cancel" | "panels.market.add.close" | "panels.market.add.retry" | "panels.market.add.changeSource" | "panels.market.add.approveBuilds" | "panels.market.add.approveHint" | "panels.market.add.doneApplied" | "panels.market.add.doneRestart" | "panels.market.add.cancelled" | "panels.market.add.failed" | "panels.market.add.removed" | "panels.market.add.supplyPending" | "panels.market.add.supplyFailed" | "panels.market.add.notModule" | "panels.market.add.removedRestart" | "panels.market.add.remove" | "panels.market.add.problem.invalidSpec" | "panels.market.add.problem.alreadyInstalled" | "panels.market.add.problem.notFound" | "panels.market.add.problem.notAPackage" | "panels.market.add.problem.notABundle" | "panels.market.add.problem.network" | "panels.market.add.problem.unknown" | "panels.market.add.reason.invalidSpec" | "panels.market.add.reason.incompatible" | "panels.market.add.reason.notBundle" | "panels.market.add.reason.notRemovable" | "panels.market.add.reason.bundleInUse" | "panels.market.add.reason.stopProfile" | "panels.market.add.reason.staleApproval" | "panels.market.add.reason.operationError" | "panels.market.add.reason.buildBlocked" | "panels.market.add.reason.pnpmMissing" | "panels.market.add.reason.noMatchingVersion" | "panels.market.add.reason.diskFull" | "panels.market.add.reason.permission" | "panels.market.add.reason.integrity" | "panels.market.add.reason.timeout" | "panels.market.add.reason.network" | "panels.market.add.reason.notFound" | "panels.market.add.reason.unknown" | "blocker.fix.running" | "blocker.fix.failed" | "blocker.retry.running" | "panels.reading" | "panels.cardBroken" | "panels.cardRetry" | "panels.empty" | "panels.emptyHint" | "env.title" | "env.checking" | "env.installing" | "env.failed" | "env.elapsed" | "env.stepFailed.install" | "env.stepFailed.path" | "env.stepFailed.shell" | "env.bridge.ok" | "env.bridge.short" | "env.bridge.why" | "env.bridge.daemon" | "env.bridge.extension" | "env.bridge.extensionLink" | "env.bridge.profile" | "env.bridge.stale" | "env.bridge.unknown" | "env.fix.title" | "env.fix.why" | "env.fix.whyPath" | "env.fix.whyShell" | "env.fix.step" | "env.fix.stepPath" | "env.fix.stepShell" | "env.fix.permission" | "env.fix.network" | "env.fix.details" | "env.retry" | "env.copyCommand" | "env.copied" | "env.step.opencli" | "env.step.codingPlugin" | "env.step.bridge" | "env.step.warm" | "env.warm.done" | "env.warm.short" | "rightbar.label" | "rightbar.expand" | "rightbar.collapse" | "rightbar.column.label" | "rightbar.column.workbench" | "rightbar.column.dsh" | "rightbar.refresh" | "rightbar.refreshing" | "rightbar.updatedAt" | "rightbar.noProject" | "rightbar.openPlatform" | "coding.notARepo.title" | "error.unknown" | "board.title" | "panel.conversation" | "panels.label", string>;
333
339
  };
334
340
  export type { WorkbenchMessages };
335
341
  export interface Dictionaries {
package/dist/i18n/zh.d.ts CHANGED
@@ -40,7 +40,12 @@ export declare const zh: {
40
40
  readonly 'time.hours': "{n} 小时前";
41
41
  readonly 'time.days': "{n} 天前";
42
42
  readonly 'panels.title': "面板模块";
43
- readonly 'panels.reset': "恢复默认";
43
+ readonly 'panels.market.defaultNote': "这份默认给所有没在这一行改过的工作区;改过的保持自己的选择。";
44
+ readonly 'panels.market.changedBy': "{count} 个工作区已在这一行改过";
45
+ readonly 'panels.market.defaultLabel': "开启后所有工作区默认启用";
46
+ readonly 'panels.followGlobal': "默认";
47
+ readonly 'panels.followGlobalHint': "这一行跟着默认板走(右边那颗开关显示的就是默认的状态)。点一下开关 = 改成这个项目自己定。";
48
+ readonly 'panels.followAll': "全部恢复默认";
44
49
  readonly 'panels.enabled': "这个项目已启用";
45
50
  readonly 'panels.disabled': "这个项目未启用";
46
51
  readonly 'panels.choose': "选择模块";
@@ -59,17 +64,17 @@ export declare const zh: {
59
64
  readonly 'panels.browseMarket': "浏览模块市场";
60
65
  readonly 'panels.noneInstalled': "这些模块要的插件都还没装";
61
66
  readonly 'panels.noneInstalledHint': "装好之后它们会回到这里,再由你决定放哪几个上板。";
67
+ readonly 'panels.rowPlugin': "插件";
62
68
  readonly 'panels.market.installed': "已安装";
63
69
  readonly 'panels.market.outdated': "要更新";
64
70
  readonly 'panels.market.hasNew': "有新版本";
65
71
  readonly 'panels.market.missing': "未装";
66
72
  readonly 'panels.market.unchecked': "状态未知";
67
73
  readonly 'panels.market.checking': "检测中";
68
- readonly 'panels.market.sectionInstalled': "已安装";
74
+ readonly 'panels.market.sectionInstalled': "已安装模块";
69
75
  readonly 'panels.market.sectionPending': "未安装";
70
76
  readonly 'panels.market.sectionShared': "共用依赖";
71
- readonly 'panels.market.sharedUsedBy': "供 {modules} 使用";
72
- readonly 'panels.market.sharedFoot': "这一段不给卸载:它们跟着模块来,卸掉也会被装回来。要停用一份,就去关掉正在用它的那些模块。";
77
+ readonly 'panels.market.sharedUsedBy': "供 {modules} 模块使用";
73
78
  readonly 'panels.market.listSeparator': "、";
74
79
  readonly 'panels.market.sharedMissing': "未装";
75
80
  readonly 'panels.market.sharedOutdated': "有新版";
@@ -94,21 +99,20 @@ export declare const zh: {
94
99
  readonly 'panels.market.noSource': "这个模块没声明怎么装,只能自己动手";
95
100
  readonly 'panels.market.badSource': "安装方式没写全,只能自己动手";
96
101
  readonly 'panels.market.empty': "暂无可用模块";
97
- readonly 'panels.market.installedSection': "已安装";
98
- readonly 'panels.market.sectionLocal': "本地装的";
102
+ readonly 'panels.market.sectionLocal': "本地模块";
99
103
  readonly 'panels.market.sectionUnlisted': "不是从市场装的";
100
104
  readonly 'panels.market.sectionPlugins': "已安装 opencli 插件";
101
105
  readonly 'panels.market.moduleOutdated': "有新版";
102
106
  readonly 'panels.market.moduleRefused': "这个模块是给旧版本的工作台写的,现在用不了。更新它,或者卸载。";
103
107
  readonly 'panels.market.moduleRefusedDetail': "装不上的原因:{reason}";
104
108
  readonly 'panels.market.moduleRestartHint': "装好了但还没生效 —— 重启一次 dsh 就会出来";
105
- readonly 'panels.market.pluginLocalNoUpdate': "本地装的,没地方查新版,所以只给卸载";
109
+ readonly 'panels.market.workbenchName': "工作台";
110
+ readonly 'panels.market.workbenchLocal': "这份是本仓正在开发的那一份,不从源上取。";
106
111
  readonly 'panels.market.pluginKeptInUse': "文件先留着:{names} 还要用它。";
107
112
  readonly 'panels.market.pluginKeptUnsure': "文件先留着:这台机器上还有模块看不出用了什么,说不准谁在用它。";
108
113
  readonly 'panels.market.pluginKeptHeld': "文件留着:你自己装的那一份,模块放手不动它。";
109
114
  readonly 'panels.market.pluginKeptUnnamed': "文件先留着:看不出这是哪个插件,说不准谁在用它。";
110
- readonly 'panels.market.pluginAlsoKnownAs': "包名是 {pkg}(更新与卸载按它来)";
111
- readonly 'panels.market.pluginBroken': "这份装坏了:看不出它是个插件。可以卸载,或者去上面按包名再装一次。";
115
+ readonly 'panels.market.pluginBroken': "这份装坏了:看不出它是个插件。可以卸载,或者直接跟 agent 说:用 workbench_modules 的 install-bundle 按包名再装一次。";
112
116
  readonly 'panels.market.restartNeeded': "新版装好了,重启一次才生效。";
113
117
  readonly 'panels.market.foot': "装好后到那个项目的面板页勾选它,模块才会出现。";
114
118
  readonly 'panels.market.recheck': "重新检查更新";
@@ -117,7 +121,7 @@ export declare const zh: {
117
121
  readonly 'panels.market.justNow': "刚刚";
118
122
  readonly 'panels.market.minutesAgo': "{n} 分钟前";
119
123
  readonly 'panels.market.hoursAgo': "{n} 小时前";
120
- readonly 'panels.market.add.description': "输入模块的包名、Git 仓库地址或本地目录路径。";
124
+ readonly 'panels.market.add.description': "输入模块或 opencli 插件的包名、Git 仓库地址或本地目录路径。";
121
125
  readonly 'panels.market.add.specPlaceholder': "例如 @dsh-workbench/module-hello";
122
126
  readonly 'panels.market.add.guideToggle': "模块安装引导和示例";
123
127
  readonly 'panels.market.add.guideHide': "收起引导";
@@ -156,11 +160,13 @@ export declare const zh: {
156
160
  readonly 'panels.market.add.problemInstalled': "该模块已安装";
157
161
  readonly 'panels.market.add.problemNotFound': "未找到相关模块";
158
162
  readonly 'panels.market.add.problemNotPackage': "该路径不存在或不是有效的模块包";
159
- readonly 'panels.market.add.problemNotBundle': "这个包装不进这里:{reason}。要是它其实是个 opencli 插件,请改用包名(例如 opencli-plugin-cmdb)或者本地目录 —— 从 git 地址和 tarball 判断不出它是什么。";
163
+ readonly 'panels.market.add.problemNotBundle': "这个包装不进这里:{reason}。这个入口按包名认工作台模块和 opencli 插件,两种都能装;但 git 地址和 tarball 判断不出它是什么 —— 要是它其实是这两样之一,请改填包名或本地目录。";
160
164
  readonly 'panels.market.add.problemNotWorkbenchModule': "这个包跟现在的工作台对不上,装上也用不了。换一个版本,或者找这个模块的作者。";
161
165
  readonly 'panels.market.add.problemNotModuleOrPlugin': "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上。换一个版本,或者找这个包的作者。";
162
166
  readonly 'panels.market.add.failureNotModuleOrPlugin': "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上";
163
167
  readonly 'panels.market.add.installedPlugin': "装上了:opencli 插件 {name}";
168
+ readonly 'panels.market.add.pluginPreview': "这是一份 opencli 插件:装进 opencli 的插件区,装完就能用,不用重启。";
169
+ readonly 'panels.market.add.pluginAlias': "插件名:{name}";
164
170
  readonly 'panels.market.add.problemNetwork': "无法连接模块源,请检查网络后重试";
165
171
  readonly 'panels.market.add.problemUnknown': "无法获取模块信息:{reason}";
166
172
  readonly 'panels.market.add.failureNetwork': "网络连接失败";
package/dist/i18n/zh.js CHANGED
@@ -40,7 +40,12 @@ export const zh = {
40
40
  'time.hours': '{n} 小时前',
41
41
  'time.days': '{n} 天前',
42
42
  'panels.title': '面板模块',
43
- 'panels.reset': '恢复默认',
43
+ 'panels.market.defaultNote': '这份默认给所有没在这一行改过的工作区;改过的保持自己的选择。',
44
+ 'panels.market.changedBy': '{count} 个工作区已在这一行改过',
45
+ 'panels.market.defaultLabel': '开启后所有工作区默认启用',
46
+ 'panels.followGlobal': '默认',
47
+ 'panels.followGlobalHint': '这一行跟着默认板走(右边那颗开关显示的就是默认的状态)。点一下开关 = 改成这个项目自己定。',
48
+ 'panels.followAll': '全部恢复默认',
44
49
  'panels.enabled': '这个项目已启用',
45
50
  'panels.disabled': '这个项目未启用',
46
51
  'panels.choose': '选择模块',
@@ -59,17 +64,17 @@ export const zh = {
59
64
  'panels.browseMarket': '浏览模块市场',
60
65
  'panels.noneInstalled': '这些模块要的插件都还没装',
61
66
  'panels.noneInstalledHint': '装好之后它们会回到这里,再由你决定放哪几个上板。',
67
+ 'panels.rowPlugin': '插件',
62
68
  'panels.market.installed': '已安装',
63
69
  'panels.market.outdated': '要更新',
64
70
  'panels.market.hasNew': '有新版本',
65
71
  'panels.market.missing': '未装',
66
72
  'panels.market.unchecked': '状态未知',
67
73
  'panels.market.checking': '检测中',
68
- 'panels.market.sectionInstalled': '已安装',
74
+ 'panels.market.sectionInstalled': '已安装模块',
69
75
  'panels.market.sectionPending': '未安装',
70
76
  'panels.market.sectionShared': '共用依赖',
71
- 'panels.market.sharedUsedBy': '供 {modules} 使用',
72
- 'panels.market.sharedFoot': '这一段不给卸载:它们跟着模块来,卸掉也会被装回来。要停用一份,就去关掉正在用它的那些模块。',
77
+ 'panels.market.sharedUsedBy': '供 {modules} 模块使用',
73
78
  'panels.market.listSeparator': '、',
74
79
  'panels.market.sharedMissing': '未装',
75
80
  'panels.market.sharedOutdated': '有新版',
@@ -94,21 +99,20 @@ export const zh = {
94
99
  'panels.market.noSource': '这个模块没声明怎么装,只能自己动手',
95
100
  'panels.market.badSource': '安装方式没写全,只能自己动手',
96
101
  'panels.market.empty': '暂无可用模块',
97
- 'panels.market.installedSection': '已安装',
98
- 'panels.market.sectionLocal': '本地装的',
102
+ 'panels.market.sectionLocal': '本地模块',
99
103
  'panels.market.sectionUnlisted': '不是从市场装的',
100
104
  'panels.market.sectionPlugins': '已安装 opencli 插件',
101
105
  'panels.market.moduleOutdated': '有新版',
102
106
  'panels.market.moduleRefused': '这个模块是给旧版本的工作台写的,现在用不了。更新它,或者卸载。',
103
107
  'panels.market.moduleRefusedDetail': '装不上的原因:{reason}',
104
108
  'panels.market.moduleRestartHint': '装好了但还没生效 —— 重启一次 dsh 就会出来',
105
- 'panels.market.pluginLocalNoUpdate': '本地装的,没地方查新版,所以只给卸载',
109
+ 'panels.market.workbenchName': '工作台',
110
+ 'panels.market.workbenchLocal': '这份是本仓正在开发的那一份,不从源上取。',
106
111
  'panels.market.pluginKeptInUse': '文件先留着:{names} 还要用它。',
107
112
  'panels.market.pluginKeptUnsure': '文件先留着:这台机器上还有模块看不出用了什么,说不准谁在用它。',
108
113
  'panels.market.pluginKeptHeld': '文件留着:你自己装的那一份,模块放手不动它。',
109
114
  'panels.market.pluginKeptUnnamed': '文件先留着:看不出这是哪个插件,说不准谁在用它。',
110
- 'panels.market.pluginAlsoKnownAs': '包名是 {pkg}(更新与卸载按它来)',
111
- 'panels.market.pluginBroken': '这份装坏了:看不出它是个插件。可以卸载,或者去上面按包名再装一次。',
115
+ 'panels.market.pluginBroken': '这份装坏了:看不出它是个插件。可以卸载,或者直接跟 agent 说:用 workbench_modules 的 install-bundle 按包名再装一次。',
112
116
  'panels.market.restartNeeded': '新版装好了,重启一次才生效。',
113
117
  'panels.market.foot': '装好后到那个项目的面板页勾选它,模块才会出现。',
114
118
  'panels.market.recheck': '重新检查更新',
@@ -117,7 +121,7 @@ export const zh = {
117
121
  'panels.market.justNow': '刚刚',
118
122
  'panels.market.minutesAgo': '{n} 分钟前',
119
123
  'panels.market.hoursAgo': '{n} 小时前',
120
- 'panels.market.add.description': '输入模块的包名、Git 仓库地址或本地目录路径。',
124
+ 'panels.market.add.description': '输入模块或 opencli 插件的包名、Git 仓库地址或本地目录路径。',
121
125
  'panels.market.add.specPlaceholder': '例如 @dsh-workbench/module-hello',
122
126
  'panels.market.add.guideToggle': '模块安装引导和示例',
123
127
  'panels.market.add.guideHide': '收起引导',
@@ -156,11 +160,13 @@ export const zh = {
156
160
  'panels.market.add.problemInstalled': '该模块已安装',
157
161
  'panels.market.add.problemNotFound': '未找到相关模块',
158
162
  'panels.market.add.problemNotPackage': '该路径不存在或不是有效的模块包',
159
- 'panels.market.add.problemNotBundle': '这个包装不进这里:{reason}。要是它其实是个 opencli 插件,请改用包名(例如 opencli-plugin-cmdb)或者本地目录 —— 从 git 地址和 tarball 判断不出它是什么。',
163
+ 'panels.market.add.problemNotBundle': '这个包装不进这里:{reason}。这个入口按包名认工作台模块和 opencli 插件,两种都能装;但 git 地址和 tarball 判断不出它是什么 —— 要是它其实是这两样之一,请改填包名或本地目录。',
160
164
  'panels.market.add.problemNotWorkbenchModule': '这个包跟现在的工作台对不上,装上也用不了。换一个版本,或者找这个模块的作者。',
161
165
  'panels.market.add.problemNotModuleOrPlugin': '这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上。换一个版本,或者找这个包的作者。',
162
166
  'panels.market.add.failureNotModuleOrPlugin': '这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上',
163
167
  'panels.market.add.installedPlugin': '装上了:opencli 插件 {name}',
168
+ 'panels.market.add.pluginPreview': '这是一份 opencli 插件:装进 opencli 的插件区,装完就能用,不用重启。',
169
+ 'panels.market.add.pluginAlias': '插件名:{name}',
164
170
  'panels.market.add.problemNetwork': '无法连接模块源,请检查网络后重试',
165
171
  'panels.market.add.problemUnknown': '无法获取模块信息:{reason}',
166
172
  'panels.market.add.failureNetwork': '网络连接失败',
@@ -0,0 +1,2 @@
1
+ import type { UnitManifest } from '../api/unit.js';
2
+ export declare function declarationOfPackage(pkg: string, packageJson: URL | string): UnitManifest;
@@ -0,0 +1,21 @@
1
+ import fs from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { readDeclaration } from '../api/unit-declaration.js';
4
+ export function declarationOfPackage(pkg, packageJson) {
5
+ const at = typeof packageJson === 'string' ? packageJson : fileURLToPath(packageJson);
6
+ let parsed;
7
+ try {
8
+ parsed = JSON.parse(fs.readFileSync(at, 'utf8'));
9
+ }
10
+ catch (error) {
11
+ throw new Error(`${pkg}: 读不动 ${at} —— ${String(error)}`, { cause: error });
12
+ }
13
+ const declared = typeof parsed === 'object' && parsed !== null
14
+ ? parsed.dsh?.workbench
15
+ : undefined;
16
+ const read = readDeclaration(pkg, declared);
17
+ if (read.manifest === undefined) {
18
+ throw new Error(`${pkg} 的 dsh.workbench 读不出来:${read.problems.join(';')}`);
19
+ }
20
+ return read.manifest;
21
+ }
package/dist/testing.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './testing/host-fakes.js';
2
+ export * from './testing/declaration.js';
package/dist/testing.js CHANGED
@@ -1 +1,2 @@
1
1
  export * from './testing/host-fakes.js';
2
+ export * from './testing/declaration.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-workbench/shared",
3
- "version": "0.1.17",
3
+ "version": "0.2.0",
4
4
  "description": "工作台与模块共享的纯代码与契约类型:类型、纯逻辑、哑组件、平台解析。宿主侧的东西从 /host 进(不影响浏览器产物)。",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -0,0 +1,52 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { declarationProblemsOf, readDeclaration } from '../unit-declaration.js';
3
+
4
+ const declared = (tools: unknown) => ({
5
+ apiVersion: 1,
6
+ unit: 'demo',
7
+ card: null,
8
+ tools,
9
+ host: { inject: [] },
10
+ });
11
+
12
+ describe('清单里的工具声明', () => {
13
+ it('key 是可选的:不给就只是没有生成物可对', () => {
14
+ expect(declarationProblemsOf('@t/demo', declared([{ name: 'workbench_demo_sync', writes: true }]))).toEqual([]);
15
+ });
16
+
17
+ it('给了 key 就得是能当代码键名的 lowerCamelCase', () => {
18
+ expect(
19
+ declarationProblemsOf('@t/demo', declared([{ name: 'workbench_demo_sync', writes: true, key: 'sync' }])),
20
+ ).toEqual([]);
21
+ expect(
22
+ declarationProblemsOf('@t/demo', declared([{ name: 'workbench_demo_sync', writes: true, key: 'podLogs' }])),
23
+ ).toEqual([]);
24
+
25
+ for (const bad of ['Sync', 'pod-logs', 'pod_logs', '1sync', '', 'pod logs', '__proto__']) {
26
+ const problems = declarationProblemsOf(
27
+ '@t/demo',
28
+ declared([{ name: 'workbench_demo_sync', writes: true, key: bad }]),
29
+ );
30
+ expect(problems.join('\n'), `key=${JSON.stringify(bad)} 该红`).toMatch(/\.key 要是 lowerCamelCase/);
31
+ }
32
+ });
33
+
34
+ it('★ key 撞了当场红(生成物里的键只能有一个,不然生成出来是静默覆盖)', () => {
35
+ const problems = declarationProblemsOf(
36
+ '@t/demo',
37
+ declared([
38
+ { name: 'workbench_demo_sync', writes: true, key: 'sync' },
39
+ { name: 'workbench_demo_apply', writes: true, key: 'sync' },
40
+ ]),
41
+ );
42
+
43
+ expect(problems.join('\n')).toMatch(/key「sync」与前面那条撞了/);
44
+ });
45
+
46
+ it('key 不进外壳那份清单(外壳不读它,它是给代码用的)', () => {
47
+ const read = readDeclaration('@t/demo', declared([{ name: 'workbench_demo_sync', writes: true, key: 'sync' }]));
48
+
49
+ expect(read.problems).toEqual([]);
50
+ expect(read.manifest?.tools).toEqual([{ name: 'workbench_demo_sync', writes: true }]);
51
+ });
52
+ });
@@ -12,6 +12,7 @@ export interface UnitCardDeclaration {
12
12
  export interface UnitToolDeclaration {
13
13
  readonly name: string;
14
14
  readonly writes: boolean;
15
+ readonly key?: string;
15
16
  }
16
17
 
17
18
  export interface UnitDeclaration {
@@ -41,7 +42,9 @@ export const DECLARATION_REQUIRED_KEYS: readonly string[] = [
41
42
  ];
42
43
 
43
44
  const CARD_KEYS = ['titleKey', 'order'];
44
- const TOOL_KEYS = ['name', 'writes'];
45
+ const TOOL_KEYS = ['name', 'writes', 'key'];
46
+
47
+ const TOOL_KEY_SHAPE = /^[a-z][a-zA-Z0-9]*$/;
45
48
  const HOST_KEYS = ['inject', 'optional'];
46
49
 
47
50
  function text(value: unknown): string {
@@ -130,6 +133,7 @@ export function declarationProblemsOf(subject: string, raw: unknown): readonly s
130
133
  problems.push(`${subject}: dsh.workbench.tools 要是一串声明,现在是 ${JSON.stringify(tools)}`);
131
134
  } else {
132
135
  const seen = new Set<string>();
136
+ const seenKeys = new Set<string>();
133
137
  tools.forEach((tool, index) => {
134
138
  const at = `dsh.workbench.tools[${index}]`;
135
139
  if (!isRecord(tool)) {
@@ -153,6 +157,19 @@ export function declarationProblemsOf(subject: string, raw: unknown): readonly s
153
157
  `${subject}: ${at}.writes 要是 true / false(它是"这会写"的唯一声明,缺了审批无从认领)`,
154
158
  );
155
159
  }
160
+ if (tool.key !== undefined) {
161
+ const key = text(tool.key);
162
+ if (!TOOL_KEY_SHAPE.test(key)) {
163
+ problems.push(
164
+ `${subject}: ${at}.key 要是 lowerCamelCase 的标识符(如 \`sync\` / \`podLogs\`),` +
165
+ `现在是 ${JSON.stringify(tool.key)} —— 它会变成生成物里的键名,所以不引号、不许带点或横线`,
166
+ );
167
+ } else if (seenKeys.has(key)) {
168
+ problems.push(`${subject}: ${at}.key「${key}」与前面那条撞了 —— 生成物里的键只能有一个`);
169
+ } else {
170
+ seenKeys.add(key);
171
+ }
172
+ }
156
173
  });
157
174
  }
158
175
  }
@@ -10,7 +10,8 @@ export type ProvisionAction =
10
10
  | 'remove-bundle'
11
11
  | 'cancel-install'
12
12
  | 'update-plugin'
13
- | 'uninstall-plugin';
13
+ | 'uninstall-plugin'
14
+ | 'update-workbench';
14
15
 
15
16
  export interface PluginStatePayload {
16
17
  readonly name: string;
@@ -50,7 +50,7 @@ export interface BoardModule {
50
50
 
51
51
  export interface UnitClientFace {
52
52
  readonly unit: string;
53
- readonly card: (props: BoardModuleProps) => ReactNode;
53
+ readonly card?: (props: BoardModuleProps) => ReactNode;
54
54
  readonly steps?: Readonly<Record<string, UnitStep>>;
55
55
  readonly messages?: Dictionaries;
56
56
  readonly approval?:
@@ -56,6 +56,44 @@ export function IconBrandMark({ size = 24 }: IconProps) {
56
56
  );
57
57
  }
58
58
 
59
+ export function IconOpencli({ size = 16 }: IconProps) {
60
+ return (
61
+ <Shell size={size} viewBox="0 0 64 64">
62
+ <defs>
63
+ <linearGradient id="opencli-mark" x1="0%" y1="0%" x2="100%" y2="100%">
64
+ <stop offset="0%" stopColor="#00e5a0" />
65
+ <stop offset="50%" stopColor="#00b4d8" />
66
+ <stop offset="100%" stopColor="#7b61ff" />
67
+ </linearGradient>
68
+ </defs>
69
+ <rect width={64} height={64} rx={14} fill="#0a0a0f" />
70
+ <text
71
+ x="50%"
72
+ y="54%"
73
+ dominantBaseline="middle"
74
+ textAnchor="middle"
75
+ fontFamily="monospace"
76
+ fontWeight={700}
77
+ fontSize={28}
78
+ fill="url(#opencli-mark)"
79
+ >
80
+ &gt;_
81
+ </text>
82
+ </Shell>
83
+ );
84
+ }
85
+
86
+ export function IconNpm({ size = 16 }: IconProps) {
87
+ return (
88
+ <Shell size={size} viewBox="0 0 1024 1024">
89
+ <path
90
+ d="M64 337.76h896v298.72H512v49.792H312.896v-49.824H64z m49.792 248.928h99.552v-149.376h49.76v149.344h49.792v-199.104H113.792z m248.896-199.136v248.928h99.552v-49.792h99.552v-199.136z m99.552 49.792H512v99.584h-49.792z m149.344-49.792v199.136h99.552v-149.376h49.792v149.344h49.792v-149.344h49.792v149.344h49.792v-199.104z"
91
+ fill="#CB3837"
92
+ />
93
+ </Shell>
94
+ );
95
+ }
96
+
59
97
  export function IconModules({ size = 16 }: IconProps) {
60
98
  return (
61
99
  <svg width={size} height={size} viewBox="0 0 16 16" fill="none" aria-hidden="true">
@@ -14,6 +14,7 @@ describe('provision 请求体:每类动作各有各的目标字段', () => {
14
14
  ['update-dependency', 'id'],
15
15
  ['update-plugin', 'name'],
16
16
  ['uninstall-plugin', 'name'],
17
+ ['update-workbench', 'name'],
17
18
  ]);
18
19
  });
19
20
 
@@ -73,6 +74,18 @@ describe('provision 请求体:每类动作各有各的目标字段', () => {
73
74
  });
74
75
  });
75
76
 
77
+ it('更新工作台自己按包名认目标(界面把它自己那个包名交上来)', () => {
78
+ expect(parseProvisionBody({ action: 'update-workbench', name: ' @dsh-workbench/core ' })).toEqual({
79
+ ok: true,
80
+ request: { action: 'update-workbench', name: '@dsh-workbench/core' },
81
+ });
82
+ expect(parseProvisionBody({ action: 'update-workbench', unit: 'panels' })).toEqual({
83
+ ok: false,
84
+ code: 'workbench/no-name',
85
+ message: '缺少 name',
86
+ });
87
+ });
88
+
76
89
  it('拿错字段不算数(spec 动作给了 unit 也要打回)', () => {
77
90
  expect(parseProvisionBody({ action: 'install-bundle', unit: 'issues' })).toEqual({
78
91
  ok: false,
@@ -13,6 +13,7 @@ export const PROVISION_ACTIONS = [
13
13
  'update-dependency',
14
14
  'update-plugin',
15
15
  'uninstall-plugin',
16
+ 'update-workbench',
16
17
  ] as const satisfies readonly ProvisionAction[];
17
18
 
18
19
  export const DEPENDENCY_ACTIONS = ['update-dependency'] as const satisfies readonly ProvisionAction[];
@@ -35,6 +36,7 @@ export function provisionTargetOf(action: ProvisionAction): ProvisionTarget {
35
36
  if (action === 'install-bundle') return 'spec';
36
37
  if (action === 'remove-bundle') return 'name';
37
38
  if (action === 'update-plugin' || action === 'uninstall-plugin') return 'name';
39
+ if (action === 'update-workbench') return 'name';
38
40
  if (action === 'cancel-install') return 'requestId';
39
41
  return 'unit';
40
42
  }
@@ -78,6 +80,7 @@ function requestOf(action: ProvisionAction, value: string): ProvisionRequest {
78
80
  if (action === 'install-bundle') return { action, spec: value };
79
81
  if (action === 'remove-bundle') return { action, name: value };
80
82
  if (action === 'update-plugin' || action === 'uninstall-plugin') return { action, name: value };
83
+ if (action === 'update-workbench') return { action, name: value };
81
84
  if (action === 'cancel-install') return { action, requestId: value };
82
85
  return { action, unit: value };
83
86
  }
package/src/i18n/en.ts CHANGED
@@ -45,7 +45,12 @@ export const en: Record<keyof WorkbenchMessages, string> = {
45
45
  'time.days': '{n} d ago',
46
46
 
47
47
  'panels.title': 'Panel modules',
48
- 'panels.reset': 'Reset',
48
+ 'panels.market.defaultNote': 'This default covers every workspace that has not changed this row; ones that have keep their own choice.',
49
+ 'panels.market.changedBy': '{count} workspaces have already changed this row',
50
+ 'panels.market.defaultLabel': 'On by default in all workspaces',
51
+ 'panels.followGlobal': 'Default',
52
+ 'panels.followGlobalHint': 'This row follows the default board (the switch shows that state). Clicking the switch makes it this project\'s own choice.',
53
+ 'panels.followAll': 'Restore the default everywhere',
49
54
  'panels.enabled': 'enabled in this project',
50
55
  'panels.disabled': 'not enabled in this project',
51
56
  'panels.choose': 'Choose modules',
@@ -64,17 +69,17 @@ export const en: Record<keyof WorkbenchMessages, string> = {
64
69
  'panels.browseMarket': 'Browse the module market',
65
70
  'panels.noneInstalled': 'None of these modules has its plugin installed yet',
66
71
  'panels.noneInstalledHint': 'Once installed they come back here, and you pick which go on the panel.',
72
+ 'panels.rowPlugin': 'Plugin',
67
73
  'panels.market.installed': 'installed',
68
74
  'panels.market.outdated': 'needs update',
69
75
  'panels.market.hasNew': 'New version',
70
76
  'panels.market.missing': 'not installed',
71
77
  'panels.market.unchecked': 'unknown',
72
78
  'panels.market.checking': 'checking',
73
- 'panels.market.sectionInstalled': 'Installed',
79
+ 'panels.market.sectionInstalled': 'Installed modules',
74
80
  'panels.market.sectionPending': 'Not installed',
75
81
  'panels.market.sectionShared': 'Shared dependencies',
76
- 'panels.market.sharedUsedBy': 'Used by {modules}',
77
- 'panels.market.sharedFoot': 'These cannot be uninstalled here: they come with modules, and would be reinstalled anyway. To stop using one, turn off the modules that use it.',
82
+ 'panels.market.sharedUsedBy': 'Used by module {modules}',
78
83
  'panels.market.listSeparator': ', ',
79
84
  'panels.market.sharedMissing': 'not installed',
80
85
  'panels.market.sharedOutdated': 'update available',
@@ -101,21 +106,20 @@ export const en: Record<keyof WorkbenchMessages, string> = {
101
106
  'panels.market.noSource': 'This module declares no install method — install it yourself',
102
107
  'panels.market.badSource': 'The install method is incomplete — install it yourself',
103
108
  'panels.market.empty': 'No modules available',
104
- 'panels.market.installedSection': 'Installed',
105
- 'panels.market.sectionLocal': 'Linked locally',
109
+ 'panels.market.sectionLocal': 'Local modules',
106
110
  'panels.market.sectionUnlisted': 'Not from the market',
107
111
  'panels.market.sectionPlugins': 'Installed opencli plugins',
108
112
  'panels.market.moduleOutdated': 'update available',
109
113
  'panels.market.moduleRefused': 'This module was built for an older version of the workbench, so it does not work here. Update it, or uninstall.',
110
114
  'panels.market.moduleRefusedDetail': 'Why it cannot be used: {reason}',
111
115
  'panels.market.moduleRestartHint': 'installed but not active — restart dsh once to bring it up',
112
- 'panels.market.pluginLocalNoUpdate': 'Installed from a local folder, so there is nowhere to check for a newer version — uninstall only',
116
+ 'panels.market.workbenchName': 'Workbench',
117
+ 'panels.market.workbenchLocal': 'This is the copy from your working repository; it does not come from the registry.',
113
118
  'panels.market.pluginKeptInUse': 'Files kept for now: {names} still uses it.',
114
119
  'panels.market.pluginKeptUnsure': 'Files kept for now: some modules on this machine do not say what they use, so it is unclear who needs it.',
115
120
  'panels.market.pluginKeptHeld': 'Files kept: this one is yours, and a module letting go will not remove it.',
116
121
  'panels.market.pluginKeptUnnamed': 'Files kept for now: this one cannot be identified as a plugin, so it is unclear who needs it.',
117
- 'panels.market.pluginAlsoKnownAs': 'Its package is {pkg} (updates and uninstall use that)',
118
- 'panels.market.pluginBroken': 'This install is broken: it cannot be identified as a plugin. Uninstall it, or install it again by package name above.',
122
+ 'panels.market.pluginBroken': 'This install is broken: it cannot be identified as a plugin. Uninstall it, or tell the agent to reinstall it by package name with workbench_modules (the install-bundle action).',
119
123
  'panels.market.restartNeeded': 'The new version is installed — restart once to pick it up.',
120
124
  'panels.market.foot': 'After installing, enable it in the panel page of that project.',
121
125
  'panels.market.recheck': 'Check for updates',
@@ -124,7 +128,7 @@ export const en: Record<keyof WorkbenchMessages, string> = {
124
128
  'panels.market.justNow': 'just now',
125
129
  'panels.market.minutesAgo': '{n} min ago',
126
130
  'panels.market.hoursAgo': '{n} h ago',
127
- 'panels.market.add.description': 'Enter a package name, a Git URL, or a path to a local directory.',
131
+ "panels.market.add.description": "Enter a module or opencli plugin package name, Git repository URL, or local directory path.",
128
132
  'panels.market.add.specPlaceholder': 'e.g. @dsh-workbench/module-hello',
129
133
  'panels.market.add.guideToggle': 'Install guide and examples',
130
134
  'panels.market.add.guideHide': 'Hide guide',
@@ -164,11 +168,14 @@ export const en: Record<keyof WorkbenchMessages, string> = {
164
168
  'panels.market.add.problemNotFound': 'No such module found',
165
169
  'panels.market.add.problemNotPackage': 'That path does not exist or is not a valid module package',
166
170
  'panels.market.add.problemNotBundle':
167
- 'This package cannot be installed here: {reason}. If it is actually an opencli plugin, use its package name (for example opencli-plugin-cmdb) or a local folder — a git URL or tarball does not reveal what it is.',
171
+ 'This package cannot be installed here: {reason}. This entry recognises workbench modules and opencli plugins by package name, and installs either; but a git URL or a tarball does not reveal what it is — if it is really one of those two, use its package name or a local folder instead.',
168
172
  'panels.market.add.problemNotWorkbenchModule': 'This package does not match the workbench you are running, so installing it would not work. Pick another version, or ask the module author.',
169
173
  'panels.market.add.problemNotModuleOrPlugin': 'This package is neither a workbench module nor an opencli plugin, so installing it would do nothing. Pick another version, or ask the package author.',
170
174
  'panels.market.add.failureNotModuleOrPlugin': 'This package is neither a workbench module nor an opencli plugin, so installing it would do nothing',
171
175
  'panels.market.add.installedPlugin': 'Installed: the opencli plugin {name}',
176
+ 'panels.market.add.pluginPreview':
177
+ 'This is an opencli plugin: it goes into the opencli plugin area and works as soon as it is installed — no restart.',
178
+ 'panels.market.add.pluginAlias': 'Plugin name: {name}',
172
179
  'panels.market.add.problemNetwork': 'Cannot reach the module source; check the network and retry',
173
180
  'panels.market.add.problemUnknown': 'Cannot fetch module information: {reason}',
174
181
  'panels.market.add.failureNetwork': 'The network connection failed',
package/src/i18n/zh.ts CHANGED
@@ -43,7 +43,12 @@ export const zh = {
43
43
  'time.days': '{n} 天前',
44
44
 
45
45
  'panels.title': '面板模块',
46
- 'panels.reset': '恢复默认',
46
+ 'panels.market.defaultNote': '这份默认给所有没在这一行改过的工作区;改过的保持自己的选择。',
47
+ 'panels.market.changedBy': '{count} 个工作区已在这一行改过',
48
+ 'panels.market.defaultLabel': '开启后所有工作区默认启用',
49
+ 'panels.followGlobal': '默认',
50
+ 'panels.followGlobalHint': '这一行跟着默认板走(右边那颗开关显示的就是默认的状态)。点一下开关 = 改成这个项目自己定。',
51
+ 'panels.followAll': '全部恢复默认',
47
52
  'panels.enabled': '这个项目已启用',
48
53
  'panels.disabled': '这个项目未启用',
49
54
  'panels.choose': '选择模块',
@@ -62,17 +67,17 @@ export const zh = {
62
67
  'panels.browseMarket': '浏览模块市场',
63
68
  'panels.noneInstalled': '这些模块要的插件都还没装',
64
69
  'panels.noneInstalledHint': '装好之后它们会回到这里,再由你决定放哪几个上板。',
70
+ 'panels.rowPlugin': '插件',
65
71
  'panels.market.installed': '已安装',
66
72
  'panels.market.outdated': '要更新',
67
73
  'panels.market.hasNew': '有新版本',
68
74
  'panels.market.missing': '未装',
69
75
  'panels.market.unchecked': '状态未知',
70
76
  'panels.market.checking': '检测中',
71
- 'panels.market.sectionInstalled': '已安装',
77
+ 'panels.market.sectionInstalled': '已安装模块',
72
78
  'panels.market.sectionPending': '未安装',
73
79
  'panels.market.sectionShared': '共用依赖',
74
- 'panels.market.sharedUsedBy': '供 {modules} 使用',
75
- 'panels.market.sharedFoot': '这一段不给卸载:它们跟着模块来,卸掉也会被装回来。要停用一份,就去关掉正在用它的那些模块。',
80
+ 'panels.market.sharedUsedBy': '供 {modules} 模块使用',
76
81
  'panels.market.listSeparator': '、',
77
82
  'panels.market.sharedMissing': '未装',
78
83
  'panels.market.sharedOutdated': '有新版',
@@ -97,21 +102,20 @@ export const zh = {
97
102
  'panels.market.noSource': '这个模块没声明怎么装,只能自己动手',
98
103
  'panels.market.badSource': '安装方式没写全,只能自己动手',
99
104
  'panels.market.empty': '暂无可用模块',
100
- 'panels.market.installedSection': '已安装',
101
- 'panels.market.sectionLocal': '本地装的',
105
+ 'panels.market.sectionLocal': '本地模块',
102
106
  'panels.market.sectionUnlisted': '不是从市场装的',
103
107
  'panels.market.sectionPlugins': '已安装 opencli 插件',
104
108
  'panels.market.moduleOutdated': '有新版',
105
109
  'panels.market.moduleRefused': '这个模块是给旧版本的工作台写的,现在用不了。更新它,或者卸载。',
106
110
  'panels.market.moduleRefusedDetail': '装不上的原因:{reason}',
107
111
  'panels.market.moduleRestartHint': '装好了但还没生效 —— 重启一次 dsh 就会出来',
108
- 'panels.market.pluginLocalNoUpdate': '本地装的,没地方查新版,所以只给卸载',
112
+ 'panels.market.workbenchName': '工作台',
113
+ 'panels.market.workbenchLocal': '这份是本仓正在开发的那一份,不从源上取。',
109
114
  'panels.market.pluginKeptInUse': '文件先留着:{names} 还要用它。',
110
115
  'panels.market.pluginKeptUnsure': '文件先留着:这台机器上还有模块看不出用了什么,说不准谁在用它。',
111
116
  'panels.market.pluginKeptHeld': '文件留着:你自己装的那一份,模块放手不动它。',
112
117
  'panels.market.pluginKeptUnnamed': '文件先留着:看不出这是哪个插件,说不准谁在用它。',
113
- 'panels.market.pluginAlsoKnownAs': '包名是 {pkg}(更新与卸载按它来)',
114
- 'panels.market.pluginBroken': '这份装坏了:看不出它是个插件。可以卸载,或者去上面按包名再装一次。',
118
+ 'panels.market.pluginBroken': '这份装坏了:看不出它是个插件。可以卸载,或者直接跟 agent 说:用 workbench_modules 的 install-bundle 按包名再装一次。',
115
119
  'panels.market.restartNeeded': '新版装好了,重启一次才生效。',
116
120
  'panels.market.foot': '装好后到那个项目的面板页勾选它,模块才会出现。',
117
121
  'panels.market.recheck': '重新检查更新',
@@ -120,7 +124,7 @@ export const zh = {
120
124
  'panels.market.justNow': '刚刚',
121
125
  'panels.market.minutesAgo': '{n} 分钟前',
122
126
  'panels.market.hoursAgo': '{n} 小时前',
123
- 'panels.market.add.description': '输入模块的包名、Git 仓库地址或本地目录路径。',
127
+ 'panels.market.add.description': '输入模块或 opencli 插件的包名、Git 仓库地址或本地目录路径。',
124
128
  'panels.market.add.specPlaceholder': '例如 @dsh-workbench/module-hello',
125
129
  'panels.market.add.guideToggle': '模块安装引导和示例',
126
130
  'panels.market.add.guideHide': '收起引导',
@@ -160,11 +164,14 @@ export const zh = {
160
164
  'panels.market.add.problemNotFound': '未找到相关模块',
161
165
  'panels.market.add.problemNotPackage': '该路径不存在或不是有效的模块包',
162
166
  'panels.market.add.problemNotBundle':
163
- '这个包装不进这里:{reason}。要是它其实是个 opencli 插件,请改用包名(例如 opencli-plugin-cmdb)或者本地目录 —— 从 git 地址和 tarball 判断不出它是什么。',
167
+ '这个包装不进这里:{reason}。这个入口按包名认工作台模块和 opencli 插件,两种都能装;但 git 地址和 tarball 判断不出它是什么 —— 要是它其实是这两样之一,请改填包名或本地目录。',
164
168
  'panels.market.add.problemNotWorkbenchModule': '这个包跟现在的工作台对不上,装上也用不了。换一个版本,或者找这个模块的作者。',
165
169
  'panels.market.add.problemNotModuleOrPlugin': '这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上。换一个版本,或者找这个包的作者。',
166
170
  'panels.market.add.failureNotModuleOrPlugin': '这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上',
167
171
  'panels.market.add.installedPlugin': '装上了:opencli 插件 {name}',
172
+ 'panels.market.add.pluginPreview':
173
+ '这是一份 opencli 插件:装进 opencli 的插件区,装完就能用,不用重启。',
174
+ 'panels.market.add.pluginAlias': '插件名:{name}',
168
175
  'panels.market.add.problemNetwork': '无法连接模块源,请检查网络后重试',
169
176
  'panels.market.add.problemUnknown': '无法获取模块信息:{reason}',
170
177
  'panels.market.add.failureNetwork': '网络连接失败',
@@ -0,0 +1,48 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { afterEach, describe, expect, it } from 'vitest';
5
+ import { declarationOfPackage } from '../declaration.js';
6
+
7
+ const roots: string[] = [];
8
+
9
+ function packageAt(manifest: unknown, text?: string): string {
10
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'wb-declaration-'));
11
+ roots.push(root);
12
+ const at = path.join(root, 'package.json');
13
+ fs.writeFileSync(at, text ?? JSON.stringify({ name: '@t/demo', dsh: { workbench: manifest } }), 'utf8');
14
+ return at;
15
+ }
16
+
17
+ afterEach(() => {
18
+ for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true });
19
+ });
20
+
21
+ const DECLARED = {
22
+ apiVersion: 1,
23
+ unit: 'demo',
24
+ card: { titleKey: 'demo.cardTitle', order: 7 },
25
+ tools: [{ name: 'workbench_demo_list', writes: false }],
26
+ host: { inject: [] },
27
+ };
28
+
29
+ describe('模块作者写用例时读自己那份声明', () => {
30
+ it('读出来的就是外壳会拿到的那份清单', () => {
31
+ const manifest = declarationOfPackage('@t/demo', packageAt(DECLARED));
32
+
33
+ expect(manifest.id).toBe('demo');
34
+ expect(manifest.card).toEqual({ titleKey: 'demo.cardTitle', order: 7 });
35
+ expect(manifest.tools).toEqual([{ name: 'workbench_demo_list', writes: false }]);
36
+ });
37
+
38
+ it('★ 声明有毛病就当场抛,并把 problems 原样带出来(别让用例拿着半个清单继续跑)', () => {
39
+ expect(() => declarationOfPackage('@t/demo', packageAt({ unit: 'demo' }))).toThrow(/apiVersion/);
40
+ expect(() =>
41
+ declarationOfPackage('@t/demo', packageAt({ ...DECLARED, faces: { host: 'src/host.ts' } })),
42
+ ).toThrow(/faces/);
43
+ });
44
+
45
+ it('package.json 本身读不动时也说清是哪个文件(不是一句 JSON 语法错)', () => {
46
+ expect(() => declarationOfPackage('@t/demo', packageAt({}, '{ 坏掉的'))).toThrow(/读不动/);
47
+ });
48
+ });
@@ -0,0 +1,23 @@
1
+ import fs from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import type { UnitManifest } from '../api/unit.js';
4
+ import { readDeclaration } from '../api/unit-declaration.js';
5
+
6
+ export function declarationOfPackage(pkg: string, packageJson: URL | string): UnitManifest {
7
+ const at = typeof packageJson === 'string' ? packageJson : fileURLToPath(packageJson);
8
+ let parsed: unknown;
9
+ try {
10
+ parsed = JSON.parse(fs.readFileSync(at, 'utf8'));
11
+ } catch (error) {
12
+ throw new Error(`${pkg}: 读不动 ${at} —— ${String(error)}`, { cause: error });
13
+ }
14
+ const declared =
15
+ typeof parsed === 'object' && parsed !== null
16
+ ? (parsed as { dsh?: { workbench?: unknown } }).dsh?.workbench
17
+ : undefined;
18
+ const read = readDeclaration(pkg, declared);
19
+ if (read.manifest === undefined) {
20
+ throw new Error(`${pkg} 的 dsh.workbench 读不出来:${read.problems.join(';')}`);
21
+ }
22
+ return read.manifest;
23
+ }
package/src/testing.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './testing/host-fakes.js';
2
+ export * from './testing/declaration.js';