@dsh-workbench/shared 0.1.9 → 0.1.10
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 +6 -3
- 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/unit-libs.d.ts +1 -1
- package/dist/i18n/en.js +19 -0
- package/dist/i18n/index.d.ts +20 -1
- package/dist/i18n/merge.d.ts +9 -0
- package/dist/i18n/merge.js +22 -0
- package/dist/i18n/zh.d.ts +19 -0
- package/dist/i18n/zh.js +19 -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 +7 -1
- 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/unit-libs.ts +1 -1
- package/src/i18n/en.ts +19 -0
- package/src/i18n/merge.test.ts +62 -1
- package/src/i18n/merge.ts +36 -0
- package/src/i18n/zh.ts +19 -0
- package/src/index.ts +1 -0
- package/src/lib/react.ts +2 -0
|
@@ -99,6 +99,16 @@ export function blockerOfCapability(capability, t) {
|
|
|
99
99
|
...(sourceUnknown ? [{ kind: 'open-market', label: t('blocker.market.open'), unit: capability.unit }] : []),
|
|
100
100
|
...(capability.command === undefined || sourceUnknown ? [] : [copyAction(t, capability.command)]),
|
|
101
101
|
];
|
|
102
|
+
if (capability.status === 'failed') {
|
|
103
|
+
return {
|
|
104
|
+
id: 'unit-mount-failed',
|
|
105
|
+
severity: 'block',
|
|
106
|
+
title: t('env.unit.failed'),
|
|
107
|
+
hint: t('env.unit.failedHint'),
|
|
108
|
+
...(capability.detail === undefined ? {} : { detail: capability.detail }),
|
|
109
|
+
actions: [retryAction(t)],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
102
112
|
if (capability.status === 'missing-plugin') {
|
|
103
113
|
return {
|
|
104
114
|
id: 'unit-missing-plugin',
|
package/dist/api/dsh-client.d.ts
CHANGED
|
@@ -99,12 +99,15 @@ export interface DshUiWorkspace {
|
|
|
99
99
|
startSession(workspaceId: string): void;
|
|
100
100
|
pickDirectory(): Promise<string | null>;
|
|
101
101
|
}
|
|
102
|
+
export interface DshWorkspaceWritten {
|
|
103
|
+
readonly workspaceId?: string;
|
|
104
|
+
readonly title?: string;
|
|
105
|
+
}
|
|
102
106
|
export interface DshWorkspaces {
|
|
103
107
|
create(request: {
|
|
104
108
|
path: string;
|
|
105
|
-
}): Promise<
|
|
106
|
-
|
|
107
|
-
} | undefined>;
|
|
109
|
+
}): Promise<DshWorkspaceWritten | undefined>;
|
|
110
|
+
rename(workspaceId: string, title: string): Promise<DshWorkspaceWritten>;
|
|
108
111
|
}
|
|
109
112
|
export interface DshReferenceInsert {
|
|
110
113
|
readonly source: string;
|
|
@@ -10,7 +10,7 @@ export interface PluginStatePayload {
|
|
|
10
10
|
}
|
|
11
11
|
export interface CapabilityPayload {
|
|
12
12
|
readonly unit: string;
|
|
13
|
-
readonly status: 'ok' | 'missing-commands' | 'missing-plugin' | 'unchecked';
|
|
13
|
+
readonly status: 'ok' | 'failed' | 'missing-commands' | 'missing-plugin' | 'unchecked';
|
|
14
14
|
readonly plugin?: string;
|
|
15
15
|
readonly required: readonly string[];
|
|
16
16
|
readonly missing: readonly string[];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { h } from '../lib/react.js';
|
|
2
2
|
import { CodeBlock } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
3
|
+
import { CardBoundary } from './CardBoundary.js';
|
|
3
4
|
import './ApprovalDetail.css';
|
|
4
5
|
function callOf(snapshot, callId) {
|
|
5
6
|
if (callId === undefined)
|
|
@@ -29,8 +30,11 @@ export function ApprovalDetail(props) {
|
|
|
29
30
|
const input = { tool: call?.name ?? '', args };
|
|
30
31
|
const command = typeof args.command === 'string' ? args.command : '';
|
|
31
32
|
const owner = (props.units ?? []).find((face) => face.approval?.tools.includes(input.tool) === true);
|
|
32
|
-
if (owner?.approval !== undefined)
|
|
33
|
-
|
|
33
|
+
if (owner?.approval !== undefined) {
|
|
34
|
+
const Render = owner.approval.render;
|
|
35
|
+
return (h(CardBoundary, { unit: owner.manifest.id, resetKey: props.callId ?? '', t: t },
|
|
36
|
+
h(Render, { input: input, t: t })));
|
|
37
|
+
}
|
|
34
38
|
if (command === '')
|
|
35
39
|
return null;
|
|
36
40
|
return (h(CodeBlock, { code: command, className: "wb-approvalCommand", copyLabel: t('blocker.copy'), copiedLabel: t('env.copied') }));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Translate } from '../api/dsh-client.js';
|
|
2
|
+
import type { ReactNode } from '../lib/react.js';
|
|
3
|
+
import { Component } from '../lib/react.js';
|
|
4
|
+
export interface CardBoundaryProps {
|
|
5
|
+
readonly unit: string;
|
|
6
|
+
readonly resetKey: string;
|
|
7
|
+
readonly t: Translate;
|
|
8
|
+
readonly children?: ReactNode | undefined;
|
|
9
|
+
}
|
|
10
|
+
export interface CardBoundaryState {
|
|
11
|
+
readonly reason: string | null;
|
|
12
|
+
}
|
|
13
|
+
export declare function errorReasonOf(error: unknown): string;
|
|
14
|
+
export declare class CardBoundary extends Component<CardBoundaryProps, CardBoundaryState> {
|
|
15
|
+
state: CardBoundaryState;
|
|
16
|
+
static getDerivedStateFromError(error: unknown): CardBoundaryState;
|
|
17
|
+
componentDidCatch(error: unknown): void;
|
|
18
|
+
componentDidUpdate(previous: CardBoundaryProps): void;
|
|
19
|
+
private readonly retry;
|
|
20
|
+
render(): ReactNode;
|
|
21
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Button } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
2
|
+
import { Component, h } from '../lib/react.js';
|
|
3
|
+
import { Note } from './Note.js';
|
|
4
|
+
export function errorReasonOf(error) {
|
|
5
|
+
if (error instanceof Error)
|
|
6
|
+
return error.message;
|
|
7
|
+
return typeof error === 'string' ? error : JSON.stringify(error) ?? String(error);
|
|
8
|
+
}
|
|
9
|
+
export class CardBoundary extends Component {
|
|
10
|
+
state = { reason: null };
|
|
11
|
+
static getDerivedStateFromError(error) {
|
|
12
|
+
return { reason: errorReasonOf(error) };
|
|
13
|
+
}
|
|
14
|
+
componentDidCatch(error) {
|
|
15
|
+
console.error(`[dsh-workbench] 模块 ${this.props.unit} 渲染失败:${errorReasonOf(error)}`, error);
|
|
16
|
+
}
|
|
17
|
+
componentDidUpdate(previous) {
|
|
18
|
+
if (previous.resetKey !== this.props.resetKey && this.state.reason !== null) {
|
|
19
|
+
this.setState({ reason: null });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
retry = () => {
|
|
23
|
+
this.setState({ reason: null });
|
|
24
|
+
};
|
|
25
|
+
render() {
|
|
26
|
+
const { reason } = this.state;
|
|
27
|
+
if (reason === null)
|
|
28
|
+
return this.props.children;
|
|
29
|
+
return (h(Note, { tone: "problem" },
|
|
30
|
+
h("span", null, this.props.t('panels.cardBroken', { unit: this.props.unit, reason })),
|
|
31
|
+
' ',
|
|
32
|
+
h(Button, { type: "button", variant: "outline", size: "sm", onClick: this.retry }, this.props.t('panels.cardRetry'))));
|
|
33
|
+
}
|
|
34
|
+
}
|
package/dist/host/unit-libs.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface UnitPluginState {
|
|
|
23
23
|
}
|
|
24
24
|
export interface UnitCapability {
|
|
25
25
|
readonly unit: string;
|
|
26
|
-
readonly status: "ok" | "missing-commands" | "missing-plugin" | "unchecked";
|
|
26
|
+
readonly status: "ok" | "failed" | "missing-commands" | "missing-plugin" | "unchecked";
|
|
27
27
|
readonly plugin?: string;
|
|
28
28
|
readonly required: readonly string[];
|
|
29
29
|
readonly missing: readonly string[];
|
package/dist/i18n/en.js
CHANGED
|
@@ -7,6 +7,20 @@ export const en = {
|
|
|
7
7
|
'project.none': 'No project',
|
|
8
8
|
'project.empty': 'No projects yet. Use “Add project” below.',
|
|
9
9
|
'project.add': 'Add project…',
|
|
10
|
+
'project.search.label': 'Search workspaces',
|
|
11
|
+
'project.search.placeholder': 'Search workspaces (folder / name / path)',
|
|
12
|
+
'project.search.empty': 'No workspace matches “{query}”.',
|
|
13
|
+
'project.rename.row': 'Rename “{name}”',
|
|
14
|
+
'project.rename.current': 'Give this workspace a display name…',
|
|
15
|
+
'project.rename.title': 'Give “{name}” a display name',
|
|
16
|
+
'project.rename.description': 'Shown after the folder name. It renames the workspace itself.',
|
|
17
|
+
'project.rename.field': 'Display name',
|
|
18
|
+
'project.rename.placeholder': 'e.g. Workbench shell',
|
|
19
|
+
'project.rename.hint': 'Empty = folder name only',
|
|
20
|
+
'project.rename.save': 'Save',
|
|
21
|
+
'project.rename.saving': 'Saving…',
|
|
22
|
+
'project.rename.cancel': 'Cancel',
|
|
23
|
+
'project.rename.failed': 'Rename failed. Try again.',
|
|
10
24
|
'session.new': 'New session',
|
|
11
25
|
'session.new.label': 'Start a session in the current project',
|
|
12
26
|
'session.empty': 'No sessions in this project yet.',
|
|
@@ -35,6 +49,7 @@ export const en = {
|
|
|
35
49
|
'panels.dragHint': 'Drag to reorder',
|
|
36
50
|
'panels.unknownStep': 'No suite provides this view',
|
|
37
51
|
'panels.labelWithHint': '{label} ({hint})',
|
|
52
|
+
'panels.dep.failed': '⚠ failed to start',
|
|
38
53
|
'panels.dep.missingPlugin': '⚠ not ready yet',
|
|
39
54
|
'panels.dep.missingCommands': '⚠ needs an update',
|
|
40
55
|
'panels.dep.unchecked': '⚠ cannot check',
|
|
@@ -228,6 +243,8 @@ export const en = {
|
|
|
228
243
|
'blocker.exec.hint': 'Make sure opencli is installed and on PATH (which opencli).',
|
|
229
244
|
'blocker.unknown.title': 'Something went wrong',
|
|
230
245
|
'panels.reading': 'Reading the panel setup…',
|
|
246
|
+
'panels.cardBroken': 'This module will not render: {unit} ({reason})',
|
|
247
|
+
'panels.cardRetry': 'Retry',
|
|
231
248
|
'panels.empty': 'No modules on the panel yet',
|
|
232
249
|
'panels.emptyHint': 'You can also just tell the agent: “add a module to the panel”.',
|
|
233
250
|
'env.title': 'Preparing the workbench',
|
|
@@ -266,6 +283,8 @@ export const en = {
|
|
|
266
283
|
'env.step.warm': 'Warming up site sessions',
|
|
267
284
|
'env.warm.done': '{n} site(s) warmed up',
|
|
268
285
|
'env.warm.short': 'Not warmed this time — harmless; the panel heals on first read',
|
|
286
|
+
'env.unit.failed': 'This module cannot start',
|
|
287
|
+
'env.unit.failedHint': 'It failed while assembling itself; other modules are unaffected.',
|
|
269
288
|
'env.unit.missingPlugin': 'This module is not ready yet',
|
|
270
289
|
'env.unit.missingCommandsHint': 'Updating it should fix this.',
|
|
271
290
|
'env.unit.unchecked': 'Cannot check: no shell channel',
|
package/dist/i18n/index.d.ts
CHANGED
|
@@ -9,6 +9,20 @@ export declare const dictionaries: {
|
|
|
9
9
|
readonly 'project.none': "未选择项目";
|
|
10
10
|
readonly 'project.empty': "还没有项目。用下面的「添加项目」选一个目录。";
|
|
11
11
|
readonly 'project.add': "添加项目…";
|
|
12
|
+
readonly 'project.search.label': "搜索工作区";
|
|
13
|
+
readonly 'project.search.placeholder': "搜索工作区(目录名 / 中文名 / 路径)";
|
|
14
|
+
readonly 'project.search.empty': "没有匹配「{query}」的工作区。";
|
|
15
|
+
readonly 'project.rename.row': "改「{name}」的中文名";
|
|
16
|
+
readonly 'project.rename.current': "改当前工作区的中文名…";
|
|
17
|
+
readonly 'project.rename.title': "给「{name}」配一个中文名";
|
|
18
|
+
readonly 'project.rename.description': "显示在目录名后面。改的是工作区本身的名字。";
|
|
19
|
+
readonly 'project.rename.field': "中文名";
|
|
20
|
+
readonly 'project.rename.placeholder': "例如:工作台外壳";
|
|
21
|
+
readonly 'project.rename.hint': "留空 = 只显示目录名";
|
|
22
|
+
readonly 'project.rename.save': "保存";
|
|
23
|
+
readonly 'project.rename.saving': "保存中…";
|
|
24
|
+
readonly 'project.rename.cancel': "取消";
|
|
25
|
+
readonly 'project.rename.failed': "改名失败了,再试一次。";
|
|
12
26
|
readonly 'session.new': "新会话";
|
|
13
27
|
readonly 'session.new.label': "在当前项目新建会话";
|
|
14
28
|
readonly 'session.empty': "这个项目还没有会话。";
|
|
@@ -37,6 +51,7 @@ export declare const dictionaries: {
|
|
|
37
51
|
readonly 'panels.dragHint': "拖动调整顺序";
|
|
38
52
|
readonly 'panels.unknownStep': "这一层没有套件提供";
|
|
39
53
|
readonly 'panels.labelWithHint': "{label}({hint})";
|
|
54
|
+
readonly 'panels.dep.failed': "⚠ 起不来";
|
|
40
55
|
readonly 'panels.dep.missingPlugin': "⚠ 还没装好";
|
|
41
56
|
readonly 'panels.dep.missingCommands': "⚠ 需要更新";
|
|
42
57
|
readonly 'panels.dep.unchecked': "⚠ 检测不了";
|
|
@@ -230,6 +245,8 @@ export declare const dictionaries: {
|
|
|
230
245
|
readonly 'blocker.exec.hint': "确认 opencli 已安装且在 PATH 里(which opencli)。";
|
|
231
246
|
readonly 'blocker.unknown.title': "出了点问题";
|
|
232
247
|
readonly 'panels.reading': "正在读取面板配置…";
|
|
248
|
+
readonly 'panels.cardBroken': "这个模块起不来:{unit}({reason})";
|
|
249
|
+
readonly 'panels.cardRetry': "重试";
|
|
233
250
|
readonly 'panels.empty': "面板里还没有模块";
|
|
234
251
|
readonly 'panels.emptyHint': "也可以直接跟 agent 说:「把某个模块加到面板」。";
|
|
235
252
|
readonly 'env.title': "准备工作台";
|
|
@@ -268,6 +285,8 @@ export declare const dictionaries: {
|
|
|
268
285
|
readonly 'env.step.warm': "预热站点登录态";
|
|
269
286
|
readonly 'env.warm.done': "已预热 {n} 个站点";
|
|
270
287
|
readonly 'env.warm.short': "这次没热上 —— 不影响用,面板第一次读取会自己补";
|
|
288
|
+
readonly 'env.unit.failed': "这个模块起不来";
|
|
289
|
+
readonly 'env.unit.failedHint': "它自己装配时报了错,别的模块不受影响。";
|
|
271
290
|
readonly 'env.unit.missingPlugin': "这个模块还没装好";
|
|
272
291
|
readonly 'env.unit.missingCommandsHint': "更新之后就能用了。";
|
|
273
292
|
readonly 'env.unit.unchecked': "检测不了:拿不到执行通道";
|
|
@@ -290,7 +309,7 @@ export declare const dictionaries: {
|
|
|
290
309
|
readonly 'panel.conversation': "对话";
|
|
291
310
|
readonly 'panels.label': "全局面板";
|
|
292
311
|
};
|
|
293
|
-
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.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" | "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.failed" | "panels.hintEffect" | "panels.dragHint" | "panels.unknownStep" | "panels.labelWithHint" | "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.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.moduleOutdated" | "panels.market.moduleRestartHint" | "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.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.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.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" | "coding.notARepo.title" | "error.unknown" | "board.title" | "panel.conversation" | "panels.label", string>;
|
|
312
|
+
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.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.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.moduleOutdated" | "panels.market.moduleRestartHint" | "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.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.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" | "coding.notARepo.title" | "error.unknown" | "board.title" | "panel.conversation" | "panels.label", string>;
|
|
294
313
|
};
|
|
295
314
|
export type { WorkbenchMessages };
|
|
296
315
|
export interface Dictionaries {
|
package/dist/i18n/merge.d.ts
CHANGED
|
@@ -4,3 +4,12 @@ export interface MessagesContribution {
|
|
|
4
4
|
readonly messages?: Dictionaries | undefined;
|
|
5
5
|
}
|
|
6
6
|
export declare function mergeMessages(base: Dictionaries, contributions: readonly MessagesContribution[]): Dictionaries;
|
|
7
|
+
export interface MessagesProblem {
|
|
8
|
+
readonly unit: string;
|
|
9
|
+
readonly reason: string;
|
|
10
|
+
}
|
|
11
|
+
export interface MessagesMerge {
|
|
12
|
+
readonly dictionaries: Dictionaries;
|
|
13
|
+
readonly problems: readonly MessagesProblem[];
|
|
14
|
+
}
|
|
15
|
+
export declare function mergeMessagesSkipping(base: Dictionaries, contributions: readonly MessagesContribution[]): MessagesMerge;
|
package/dist/i18n/merge.js
CHANGED
|
@@ -38,3 +38,25 @@ export function mergeMessages(base, contributions) {
|
|
|
38
38
|
}
|
|
39
39
|
return { zh, en };
|
|
40
40
|
}
|
|
41
|
+
function reasonOf(error) {
|
|
42
|
+
if (error instanceof Error)
|
|
43
|
+
return error.message;
|
|
44
|
+
return typeof error === 'string' ? error : String(error);
|
|
45
|
+
}
|
|
46
|
+
export function mergeMessagesSkipping(base, contributions) {
|
|
47
|
+
let dictionaries = base;
|
|
48
|
+
const accepted = [];
|
|
49
|
+
const problems = [];
|
|
50
|
+
for (const item of contributions) {
|
|
51
|
+
if (item.messages === undefined)
|
|
52
|
+
continue;
|
|
53
|
+
try {
|
|
54
|
+
dictionaries = mergeMessages(base, [...accepted, item]);
|
|
55
|
+
accepted.push(item);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
problems.push({ unit: item.unit, reason: reasonOf(error) });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { dictionaries, problems };
|
|
62
|
+
}
|
package/dist/i18n/zh.d.ts
CHANGED
|
@@ -7,6 +7,20 @@ export declare const zh: {
|
|
|
7
7
|
readonly 'project.none': "未选择项目";
|
|
8
8
|
readonly 'project.empty': "还没有项目。用下面的「添加项目」选一个目录。";
|
|
9
9
|
readonly 'project.add': "添加项目…";
|
|
10
|
+
readonly 'project.search.label': "搜索工作区";
|
|
11
|
+
readonly 'project.search.placeholder': "搜索工作区(目录名 / 中文名 / 路径)";
|
|
12
|
+
readonly 'project.search.empty': "没有匹配「{query}」的工作区。";
|
|
13
|
+
readonly 'project.rename.row': "改「{name}」的中文名";
|
|
14
|
+
readonly 'project.rename.current': "改当前工作区的中文名…";
|
|
15
|
+
readonly 'project.rename.title': "给「{name}」配一个中文名";
|
|
16
|
+
readonly 'project.rename.description': "显示在目录名后面。改的是工作区本身的名字。";
|
|
17
|
+
readonly 'project.rename.field': "中文名";
|
|
18
|
+
readonly 'project.rename.placeholder': "例如:工作台外壳";
|
|
19
|
+
readonly 'project.rename.hint': "留空 = 只显示目录名";
|
|
20
|
+
readonly 'project.rename.save': "保存";
|
|
21
|
+
readonly 'project.rename.saving': "保存中…";
|
|
22
|
+
readonly 'project.rename.cancel': "取消";
|
|
23
|
+
readonly 'project.rename.failed': "改名失败了,再试一次。";
|
|
10
24
|
readonly 'session.new': "新会话";
|
|
11
25
|
readonly 'session.new.label': "在当前项目新建会话";
|
|
12
26
|
readonly 'session.empty': "这个项目还没有会话。";
|
|
@@ -35,6 +49,7 @@ export declare const zh: {
|
|
|
35
49
|
readonly 'panels.dragHint': "拖动调整顺序";
|
|
36
50
|
readonly 'panels.unknownStep': "这一层没有套件提供";
|
|
37
51
|
readonly 'panels.labelWithHint': "{label}({hint})";
|
|
52
|
+
readonly 'panels.dep.failed': "⚠ 起不来";
|
|
38
53
|
readonly 'panels.dep.missingPlugin': "⚠ 还没装好";
|
|
39
54
|
readonly 'panels.dep.missingCommands': "⚠ 需要更新";
|
|
40
55
|
readonly 'panels.dep.unchecked': "⚠ 检测不了";
|
|
@@ -228,6 +243,8 @@ export declare const zh: {
|
|
|
228
243
|
readonly 'blocker.exec.hint': "确认 opencli 已安装且在 PATH 里(which opencli)。";
|
|
229
244
|
readonly 'blocker.unknown.title': "出了点问题";
|
|
230
245
|
readonly 'panels.reading': "正在读取面板配置…";
|
|
246
|
+
readonly 'panels.cardBroken': "这个模块起不来:{unit}({reason})";
|
|
247
|
+
readonly 'panels.cardRetry': "重试";
|
|
231
248
|
readonly 'panels.empty': "面板里还没有模块";
|
|
232
249
|
readonly 'panels.emptyHint': "也可以直接跟 agent 说:「把某个模块加到面板」。";
|
|
233
250
|
readonly 'env.title': "准备工作台";
|
|
@@ -266,6 +283,8 @@ export declare const zh: {
|
|
|
266
283
|
readonly 'env.step.warm': "预热站点登录态";
|
|
267
284
|
readonly 'env.warm.done': "已预热 {n} 个站点";
|
|
268
285
|
readonly 'env.warm.short': "这次没热上 —— 不影响用,面板第一次读取会自己补";
|
|
286
|
+
readonly 'env.unit.failed': "这个模块起不来";
|
|
287
|
+
readonly 'env.unit.failedHint': "它自己装配时报了错,别的模块不受影响。";
|
|
269
288
|
readonly 'env.unit.missingPlugin': "这个模块还没装好";
|
|
270
289
|
readonly 'env.unit.missingCommandsHint': "更新之后就能用了。";
|
|
271
290
|
readonly 'env.unit.unchecked': "检测不了:拿不到执行通道";
|
package/dist/i18n/zh.js
CHANGED
|
@@ -7,6 +7,20 @@ export const zh = {
|
|
|
7
7
|
'project.none': '未选择项目',
|
|
8
8
|
'project.empty': '还没有项目。用下面的「添加项目」选一个目录。',
|
|
9
9
|
'project.add': '添加项目…',
|
|
10
|
+
'project.search.label': '搜索工作区',
|
|
11
|
+
'project.search.placeholder': '搜索工作区(目录名 / 中文名 / 路径)',
|
|
12
|
+
'project.search.empty': '没有匹配「{query}」的工作区。',
|
|
13
|
+
'project.rename.row': '改「{name}」的中文名',
|
|
14
|
+
'project.rename.current': '改当前工作区的中文名…',
|
|
15
|
+
'project.rename.title': '给「{name}」配一个中文名',
|
|
16
|
+
'project.rename.description': '显示在目录名后面。改的是工作区本身的名字。',
|
|
17
|
+
'project.rename.field': '中文名',
|
|
18
|
+
'project.rename.placeholder': '例如:工作台外壳',
|
|
19
|
+
'project.rename.hint': '留空 = 只显示目录名',
|
|
20
|
+
'project.rename.save': '保存',
|
|
21
|
+
'project.rename.saving': '保存中…',
|
|
22
|
+
'project.rename.cancel': '取消',
|
|
23
|
+
'project.rename.failed': '改名失败了,再试一次。',
|
|
10
24
|
'session.new': '新会话',
|
|
11
25
|
'session.new.label': '在当前项目新建会话',
|
|
12
26
|
'session.empty': '这个项目还没有会话。',
|
|
@@ -35,6 +49,7 @@ export const zh = {
|
|
|
35
49
|
'panels.dragHint': '拖动调整顺序',
|
|
36
50
|
'panels.unknownStep': '这一层没有套件提供',
|
|
37
51
|
'panels.labelWithHint': '{label}({hint})',
|
|
52
|
+
'panels.dep.failed': '⚠ 起不来',
|
|
38
53
|
'panels.dep.missingPlugin': '⚠ 还没装好',
|
|
39
54
|
'panels.dep.missingCommands': '⚠ 需要更新',
|
|
40
55
|
'panels.dep.unchecked': '⚠ 检测不了',
|
|
@@ -228,6 +243,8 @@ export const zh = {
|
|
|
228
243
|
'blocker.exec.hint': '确认 opencli 已安装且在 PATH 里(which opencli)。',
|
|
229
244
|
'blocker.unknown.title': '出了点问题',
|
|
230
245
|
'panels.reading': '正在读取面板配置…',
|
|
246
|
+
'panels.cardBroken': '这个模块起不来:{unit}({reason})',
|
|
247
|
+
'panels.cardRetry': '重试',
|
|
231
248
|
'panels.empty': '面板里还没有模块',
|
|
232
249
|
'panels.emptyHint': '也可以直接跟 agent 说:「把某个模块加到面板」。',
|
|
233
250
|
'env.title': '准备工作台',
|
|
@@ -266,6 +283,8 @@ export const zh = {
|
|
|
266
283
|
'env.step.warm': '预热站点登录态',
|
|
267
284
|
'env.warm.done': '已预热 {n} 个站点',
|
|
268
285
|
'env.warm.short': '这次没热上 —— 不影响用,面板第一次读取会自己补',
|
|
286
|
+
'env.unit.failed': '这个模块起不来',
|
|
287
|
+
'env.unit.failedHint': '它自己装配时报了错,别的模块不受影响。',
|
|
269
288
|
'env.unit.missingPlugin': '这个模块还没装好',
|
|
270
289
|
'env.unit.missingCommandsHint': '更新之后就能用了。',
|
|
271
290
|
'env.unit.unchecked': '检测不了:拿不到执行通道',
|
package/dist/index.d.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/dist/index.js
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/dist/lib/react.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type * as ReactTypes from 'react';
|
|
2
2
|
export declare const h: typeof ReactTypes.createElement;
|
|
3
3
|
export declare const Fragment: ReactTypes.ExoticComponent<ReactTypes.FragmentProps>;
|
|
4
|
+
export declare const Component: typeof ReactTypes.Component;
|
|
4
5
|
export declare const useState: typeof ReactTypes.useState;
|
|
5
6
|
export declare const useEffect: typeof ReactTypes.useEffect;
|
|
6
7
|
export declare const useLayoutEffect: typeof ReactTypes.useLayoutEffect;
|
package/dist/lib/react.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const react = require('react');
|
|
2
2
|
export const h = react.createElement;
|
|
3
3
|
export const Fragment = react.Fragment;
|
|
4
|
+
export const Component = react.Component;
|
|
4
5
|
export const useState = react.useState;
|
|
5
6
|
export const useEffect = react.useEffect;
|
|
6
7
|
export const useLayoutEffect = react.useLayoutEffect;
|
package/package.json
CHANGED
|
@@ -141,6 +141,19 @@ describe('blockerOfCapability', () => {
|
|
|
141
141
|
expect(blockerOfCapability(capability({ status: 'ok', plugin: 'coding' }), t)).toBeUndefined();
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
+
it('★ 起不来(模块自己装配炸了)→ 只说"起不来" + 把宿主给的原因原样附上,不给装/卸的按钮', () => {
|
|
145
|
+
const blocker = blockerOfCapability(
|
|
146
|
+
capability({ status: 'failed', detail: '模块自己的 bug:kit.components.StateBlock 取不到' }),
|
|
147
|
+
t,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
expect(blocker?.id).toBe('unit-mount-failed');
|
|
151
|
+
expect(blocker?.title).toBe('env.unit.failed');
|
|
152
|
+
expect(blocker?.detail).toBe('模块自己的 bug:kit.components.StateBlock 取不到');
|
|
153
|
+
expect(blocker?.hint).toBe('env.unit.failedHint');
|
|
154
|
+
expect(blocker?.actions.map((action) => action.kind)).toEqual(['retry']);
|
|
155
|
+
});
|
|
156
|
+
|
|
144
157
|
it('★ 没装好 → 标题只说"这个模块还没装好",**不点名插件**(用户不认识插件)', () => {
|
|
145
158
|
const blocker = blockerOfCapability(
|
|
146
159
|
capability({
|
package/src/api/blocker-table.ts
CHANGED
|
@@ -122,6 +122,17 @@ export function blockerOfCapability(capability: CapabilityPayload, t: Translate)
|
|
|
122
122
|
...(capability.command === undefined || sourceUnknown ? [] : [copyAction(t, capability.command)]),
|
|
123
123
|
];
|
|
124
124
|
|
|
125
|
+
if (capability.status === 'failed') {
|
|
126
|
+
return {
|
|
127
|
+
id: 'unit-mount-failed',
|
|
128
|
+
severity: 'block',
|
|
129
|
+
title: t('env.unit.failed'),
|
|
130
|
+
hint: t('env.unit.failedHint'),
|
|
131
|
+
...(capability.detail === undefined ? {} : { detail: capability.detail }),
|
|
132
|
+
actions: [retryAction(t)],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
125
136
|
if (capability.status === 'missing-plugin') {
|
|
126
137
|
return {
|
|
127
138
|
id: 'unit-missing-plugin',
|
package/src/api/dsh-client.ts
CHANGED
|
@@ -113,8 +113,14 @@ export interface DshUiWorkspace {
|
|
|
113
113
|
pickDirectory(): Promise<string | null>;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
export interface DshWorkspaceWritten {
|
|
117
|
+
readonly workspaceId?: string;
|
|
118
|
+
readonly title?: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
116
121
|
export interface DshWorkspaces {
|
|
117
|
-
create(request: { path: string }): Promise<
|
|
122
|
+
create(request: { path: string }): Promise<DshWorkspaceWritten | undefined>;
|
|
123
|
+
rename(workspaceId: string, title: string): Promise<DshWorkspaceWritten>;
|
|
118
124
|
}
|
|
119
125
|
|
|
120
126
|
export interface DshReferenceInsert {
|
|
@@ -21,7 +21,7 @@ export interface PluginStatePayload {
|
|
|
21
21
|
|
|
22
22
|
export interface CapabilityPayload {
|
|
23
23
|
readonly unit: string;
|
|
24
|
-
readonly status: 'ok' | 'missing-commands' | 'missing-plugin' | 'unchecked';
|
|
24
|
+
readonly status: 'ok' | 'failed' | 'missing-commands' | 'missing-plugin' | 'unchecked';
|
|
25
25
|
readonly plugin?: string;
|
|
26
26
|
readonly required: readonly string[];
|
|
27
27
|
readonly missing: readonly string[];
|
|
@@ -2,6 +2,7 @@ import type { Translate } from '../api/dsh-client.js';
|
|
|
2
2
|
import type { ApprovalInput, UnitClientFace } from '../components/board-module.js';
|
|
3
3
|
import { h } from '../lib/react.js';
|
|
4
4
|
import { CodeBlock } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
5
|
+
import { CardBoundary } from './CardBoundary.js';
|
|
5
6
|
import './ApprovalDetail.css';
|
|
6
7
|
|
|
7
8
|
interface ToolCallRoot {
|
|
@@ -53,7 +54,14 @@ export function ApprovalDetail(props: ApprovalDetailProps) {
|
|
|
53
54
|
const command = typeof args.command === 'string' ? args.command : '';
|
|
54
55
|
|
|
55
56
|
const owner = (props.units ?? []).find((face) => face.approval?.tools.includes(input.tool) === true);
|
|
56
|
-
if (owner?.approval !== undefined)
|
|
57
|
+
if (owner?.approval !== undefined) {
|
|
58
|
+
const Render = owner.approval.render;
|
|
59
|
+
return (
|
|
60
|
+
<CardBoundary unit={owner.manifest.id} resetKey={props.callId ?? ''} t={t}>
|
|
61
|
+
<Render input={input} t={t} />
|
|
62
|
+
</CardBoundary>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
57
65
|
|
|
58
66
|
if (command === '') return null;
|
|
59
67
|
return (
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Button } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
2
|
+
import type { Translate } from '../api/dsh-client.js';
|
|
3
|
+
import type { ReactNode } from '../lib/react.js';
|
|
4
|
+
import { Component, h } from '../lib/react.js';
|
|
5
|
+
import { Note } from './Note.js';
|
|
6
|
+
|
|
7
|
+
export interface CardBoundaryProps {
|
|
8
|
+
readonly unit: string;
|
|
9
|
+
readonly resetKey: string;
|
|
10
|
+
readonly t: Translate;
|
|
11
|
+
readonly children?: ReactNode | undefined;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface CardBoundaryState {
|
|
15
|
+
readonly reason: string | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function errorReasonOf(error: unknown): string {
|
|
19
|
+
if (error instanceof Error) return error.message;
|
|
20
|
+
return typeof error === 'string' ? error : JSON.stringify(error) ?? String(error);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class CardBoundary extends Component<CardBoundaryProps, CardBoundaryState> {
|
|
24
|
+
override state: CardBoundaryState = { reason: null };
|
|
25
|
+
|
|
26
|
+
static getDerivedStateFromError(error: unknown): CardBoundaryState {
|
|
27
|
+
return { reason: errorReasonOf(error) };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
override componentDidCatch(error: unknown): void {
|
|
31
|
+
console.error(`[dsh-workbench] 模块 ${this.props.unit} 渲染失败:${errorReasonOf(error)}`, error);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
override componentDidUpdate(previous: CardBoundaryProps): void {
|
|
35
|
+
if (previous.resetKey !== this.props.resetKey && this.state.reason !== null) {
|
|
36
|
+
this.setState({ reason: null });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private readonly retry = (): void => {
|
|
41
|
+
this.setState({ reason: null });
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
override render(): ReactNode {
|
|
45
|
+
const { reason } = this.state;
|
|
46
|
+
if (reason === null) return this.props.children;
|
|
47
|
+
return (
|
|
48
|
+
<Note tone="problem">
|
|
49
|
+
<span>{this.props.t('panels.cardBroken', { unit: this.props.unit, reason })}</span>{' '}
|
|
50
|
+
<Button type="button" variant="outline" size="sm" onClick={this.retry}>
|
|
51
|
+
{this.props.t('panels.cardRetry')}
|
|
52
|
+
</Button>
|
|
53
|
+
</Note>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
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.',
|
|
@@ -40,6 +54,7 @@ export const en: Record<keyof WorkbenchMessages, string> = {
|
|
|
40
54
|
'panels.dragHint': 'Drag to reorder',
|
|
41
55
|
'panels.unknownStep': 'No suite provides this view',
|
|
42
56
|
'panels.labelWithHint': '{label} ({hint})',
|
|
57
|
+
'panels.dep.failed': '⚠ failed to start',
|
|
43
58
|
'panels.dep.missingPlugin': '⚠ not ready yet',
|
|
44
59
|
'panels.dep.missingCommands': '⚠ needs an update',
|
|
45
60
|
'panels.dep.unchecked': '⚠ cannot check',
|
|
@@ -235,6 +250,8 @@ export const en: Record<keyof WorkbenchMessages, string> = {
|
|
|
235
250
|
'blocker.exec.hint': 'Make sure opencli is installed and on PATH (which opencli).',
|
|
236
251
|
'blocker.unknown.title': 'Something went wrong',
|
|
237
252
|
'panels.reading': 'Reading the panel setup…',
|
|
253
|
+
'panels.cardBroken': 'This module will not render: {unit} ({reason})',
|
|
254
|
+
'panels.cardRetry': 'Retry',
|
|
238
255
|
'panels.empty': 'No modules on the panel yet',
|
|
239
256
|
'panels.emptyHint': 'You can also just tell the agent: “add a module to the panel”.',
|
|
240
257
|
'env.title': 'Preparing the workbench',
|
|
@@ -273,6 +290,8 @@ export const en: Record<keyof WorkbenchMessages, string> = {
|
|
|
273
290
|
'env.step.warm': 'Warming up site sessions',
|
|
274
291
|
'env.warm.done': '{n} site(s) warmed up',
|
|
275
292
|
'env.warm.short': 'Not warmed this time — harmless; the panel heals on first read',
|
|
293
|
+
'env.unit.failed': 'This module cannot start',
|
|
294
|
+
'env.unit.failedHint': 'It failed while assembling itself; other modules are unaffected.',
|
|
276
295
|
'env.unit.missingPlugin': 'This module is not ready yet',
|
|
277
296
|
'env.unit.missingCommandsHint': 'Updating it should fix this.',
|
|
278
297
|
'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': '这个项目还没有会话。',
|
|
@@ -38,6 +52,7 @@ export const zh = {
|
|
|
38
52
|
'panels.dragHint': '拖动调整顺序',
|
|
39
53
|
'panels.unknownStep': '这一层没有套件提供',
|
|
40
54
|
'panels.labelWithHint': '{label}({hint})',
|
|
55
|
+
'panels.dep.failed': '⚠ 起不来',
|
|
41
56
|
'panels.dep.missingPlugin': '⚠ 还没装好',
|
|
42
57
|
'panels.dep.missingCommands': '⚠ 需要更新',
|
|
43
58
|
'panels.dep.unchecked': '⚠ 检测不了',
|
|
@@ -231,6 +246,8 @@ export const zh = {
|
|
|
231
246
|
'blocker.exec.hint': '确认 opencli 已安装且在 PATH 里(which opencli)。',
|
|
232
247
|
'blocker.unknown.title': '出了点问题',
|
|
233
248
|
'panels.reading': '正在读取面板配置…',
|
|
249
|
+
'panels.cardBroken': '这个模块起不来:{unit}({reason})',
|
|
250
|
+
'panels.cardRetry': '重试',
|
|
234
251
|
'panels.empty': '面板里还没有模块',
|
|
235
252
|
'panels.emptyHint': '也可以直接跟 agent 说:「把某个模块加到面板」。',
|
|
236
253
|
'env.title': '准备工作台',
|
|
@@ -269,6 +286,8 @@ export const zh = {
|
|
|
269
286
|
'env.step.warm': '预热站点登录态',
|
|
270
287
|
'env.warm.done': '已预热 {n} 个站点',
|
|
271
288
|
'env.warm.short': '这次没热上 —— 不影响用,面板第一次读取会自己补',
|
|
289
|
+
'env.unit.failed': '这个模块起不来',
|
|
290
|
+
'env.unit.failedHint': '它自己装配时报了错,别的模块不受影响。',
|
|
272
291
|
'env.unit.missingPlugin': '这个模块还没装好',
|
|
273
292
|
'env.unit.missingCommandsHint': '更新之后就能用了。',
|
|
274
293
|
'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;
|