@dsh-workbench/shared 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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',
@@ -99,12 +99,16 @@ 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 {
107
+ readonly list: DshObservable<WorkspaceSnapshot>;
103
108
  create(request: {
104
109
  path: string;
105
- }): Promise<{
106
- workspaceId?: string;
107
- } | undefined>;
110
+ }): Promise<DshWorkspaceWritten | undefined>;
111
+ rename(workspaceId: string, title: string): Promise<DshWorkspaceWritten>;
108
112
  }
109
113
  export interface DshReferenceInsert {
110
114
  readonly source: string;
@@ -156,6 +160,12 @@ export interface DshSessionsService {
156
160
  getSnapshot(): SessionsSnapshot;
157
161
  };
158
162
  scope(sessionId: string): unknown | undefined;
163
+ create?(request: {
164
+ readonly workspaceId?: string;
165
+ readonly sessionId?: string;
166
+ readonly cwd?: string;
167
+ readonly agentPreset?: string;
168
+ }): Promise<unknown>;
159
169
  }
160
170
  export interface DshObservable<T> {
161
171
  getSnapshot(): T;
@@ -168,6 +178,18 @@ export interface DshSidebarRight {
168
178
  openResource(address: string, options?: unknown): void;
169
179
  openTab(kind: string, options?: unknown): void;
170
180
  }
181
+ export interface DshSessionsRemote {
182
+ create(request: {
183
+ readonly workspaceId?: string;
184
+ readonly cwd?: string;
185
+ readonly agentPreset?: string;
186
+ }): Promise<{
187
+ readonly sessionId: string;
188
+ }>;
189
+ }
190
+ export interface DshClientRemote {
191
+ readonly sessions: DshSessionsRemote;
192
+ }
171
193
  export interface DshClientContext {
172
194
  get<T = unknown>(name: string): T | undefined;
173
195
  provide(name: string, value: unknown): void;
@@ -178,4 +200,5 @@ export interface DshClientContext {
178
200
  readonly uiWorkspace: DshUiWorkspace;
179
201
  readonly workspaces: DshWorkspaces;
180
202
  readonly shortcuts: DshShortcutCatalog;
203
+ readonly remote: DshClientRemote;
181
204
  }
package/dist/api/dsh.d.ts CHANGED
@@ -91,6 +91,7 @@ export interface DshHostContext {
91
91
  get<T = unknown>(name: string): T | undefined;
92
92
  provide(name: string, value: unknown): () => void;
93
93
  effect(effect: () => void | (() => void), label?: string): () => void;
94
+ inject(names: readonly string[], effect: (scoped: DshHostContext) => void): () => void;
94
95
  readonly logger?: DshLogger;
95
96
  readonly webServer: {
96
97
  register(route: DshWebRoute): () => void;
@@ -128,7 +129,16 @@ export interface DshWorkspaceRegistry {
128
129
  path?: string;
129
130
  title?: string;
130
131
  } | undefined;
132
+ resolveByPath(path: string): Promise<{
133
+ readonly id: unknown;
134
+ } | undefined>;
135
+ create(path: string, title?: string): Promise<{
136
+ id?: string;
137
+ path?: string;
138
+ title?: string;
139
+ } | undefined>;
131
140
  }
141
+ export type DshHomePath = (relative: string) => string;
132
142
  export interface DshToolDefinition {
133
143
  name: string;
134
144
  description: 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
- return h(owner.approval.render, { input: input, t: t });
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
+ }
@@ -21,9 +21,18 @@ export interface ApprovalCall {
21
21
  readonly arguments?: unknown;
22
22
  readonly workspace?: string | undefined;
23
23
  }
24
+ export type ApprovalVerdict = {
25
+ readonly kind: 'read';
26
+ } | {
27
+ readonly kind: 'ask';
28
+ readonly reason: string;
29
+ readonly question: ApprovalDetail;
30
+ readonly fallback: ApprovalDetail;
31
+ };
32
+ export type ApprovalPolicy = (call: ApprovalCall) => ApprovalVerdict | undefined;
24
33
  export interface ApprovalContext {
25
- readonly titleOf?: ((id: string) => string | undefined) | undefined;
26
34
  readonly writes?: boolean | undefined;
35
+ readonly policy?: ApprovalPolicy | undefined;
27
36
  readonly detailOf?: ((call: ApprovalCall) => ApprovalFacts | undefined | Promise<ApprovalFacts | undefined>) | undefined;
28
37
  }
29
38
  export declare function approvalFor(call: ApprovalCall, context?: ApprovalContext): Promise<AskDecision | undefined>;
@@ -1,190 +1,45 @@
1
- function compose(detail, texts) {
2
- if (detail === undefined || typeof detail.zh !== 'string' || detail.zh.trim() === '')
3
- return texts.generic;
4
- return { en: `${detail.en} — ${texts.question.en}`, zh: `${detail.zh} —— ${texts.question.zh}` };
1
+ function messageOf(error) {
2
+ return error instanceof Error ? error.message : String(error);
5
3
  }
6
- function actionOf(call) {
7
- const raw = call.arguments;
8
- if (typeof raw !== 'object' || raw === null)
9
- return {};
10
- const value = raw;
4
+ function compose(detail, verdict) {
5
+ if (detail === undefined || typeof detail.zh !== 'string' || detail.zh.trim() === '') {
6
+ return verdict.fallback;
7
+ }
11
8
  return {
12
- ...(typeof value.action === 'string' ? { action: value.action } : {}),
13
- ...(typeof value.id === 'string' ? { id: value.id } : typeof value.unit === 'string' ? { id: value.unit } : {}),
9
+ en: `${detail.en} — ${verdict.question.en}`,
10
+ zh: `${detail.zh} —— ${verdict.question.zh}`,
14
11
  };
15
12
  }
16
- const READ_ONLY_ACTIONS = new Set(['list', '']);
17
- const READ = { kind: 'read' };
18
- const UNKNOWN = { kind: 'unknown' };
19
- function ask(spec) {
20
- return { kind: 'ask', spec };
21
- }
22
- function confirmed(call) {
23
- const raw = call.arguments;
24
- if (typeof raw !== 'object' || raw === null)
13
+ export async function approvalFor(call, context = {}) {
14
+ if (context.writes !== true)
25
15
  return undefined;
26
- const value = raw.confirm;
27
- return typeof value === 'boolean' ? value : undefined;
28
- }
29
- function effectOf(call, context) {
30
- const name = call.name ?? '';
31
- const { action, id } = actionOf(call);
32
- const readAction = action !== undefined && action !== '' && READ_ONLY_ACTIONS.has(action);
33
- const what = () => {
34
- if (id === undefined || id === '')
35
- return '一个模块';
36
- return `「${context.titleOf?.(id) ?? id}」`;
37
- };
38
- if (name === 'workbench_cmdb_release') {
39
- if (confirmed(call) !== true)
40
- return READ;
41
- return ask({
42
- reason: 'confirm=true releases a version to a real environment',
43
- generic: {
44
- en: 'The agent wants to deploy a version for real (confirm: true) — it deploys on the platform (non-production). Allow?',
45
- zh: 'agent 想在非生产环境上**真的部署一个版本**(confirm: true)—— 会在平台上部署。允许吗?',
46
- },
47
- question: {
48
- en: 'it deploys on the platform for real (non-production). Allow?',
49
- zh: '会在平台上真的部署一次(非生产环境)。允许吗?',
50
- },
51
- });
52
- }
53
- if (name === 'workbench_coding_issue_comment' || name === 'workbench_coding_issue_transfer') {
54
- if (confirmed(call) !== true)
55
- return READ;
56
- const comment = name === 'workbench_coding_issue_comment';
57
- return ask({
58
- reason: comment
59
- ? 'confirm=true posts a comment on the platform'
60
- : 'confirm=true moves an issue to another status on the platform',
61
- generic: comment
62
- ? {
63
- en: 'The agent wants to post a comment on the platform for real (confirm: true) — it writes on the platform. Allow?',
64
- zh: 'agent 想**真的给一条缺陷加评论**(confirm: true)—— 会写平台数据。允许吗?',
65
- }
66
- : {
67
- en: 'The agent wants to move an issue to another status for real (confirm: true) — it writes on the platform. Allow?',
68
- zh: 'agent 想**真的把一条缺陷流转到别的状态**(confirm: true)—— 会写平台数据。允许吗?',
69
- },
70
- question: comment
71
- ? {
72
- en: 'it posts that comment on the platform for real. Allow?',
73
- zh: '会在平台上真的发表这条评论。允许吗?',
74
- }
75
- : {
76
- en: 'it changes that issue status on the platform for real. Allow?',
77
- zh: '会在平台上真的改这条缺陷的状态。允许吗?',
78
- },
79
- });
80
- }
81
- if (name === 'workbench_modules') {
82
- if (readAction)
83
- return READ;
84
- if (action === 'update-dependency') {
85
- return ask({
86
- reason: `workbench_modules update-dependency ${id ?? ''} upgrades a shared dependency on this machine`,
87
- generic: {
88
- en: `The agent wants to upgrade the shared dependency ${what()} (it changes what is installed on this machine). Allow?`,
89
- zh: `agent 想升级共用依赖 ${what()}(会改这台机器上装的东西)。允许吗?`,
90
- },
91
- question: {
92
- en: 'it upgrades that shared dependency on this machine. Allow?',
93
- zh: `会升级共用依赖 ${what()}。允许吗?`,
94
- },
95
- });
96
- }
97
- const verb = action === 'install' ? '装' : action === 'uninstall' ? '卸载' : action === 'update' ? '更新' : '装 / 更新 / 卸载';
98
- return ask({
99
- reason: `workbench_modules ${action ?? '(unknown action)'} ${id ?? ''} changes plugins on this machine`.trim(),
100
- generic: {
101
- en: `The agent wants to ${verb === '装' ? 'install' : verb === '卸载' ? 'remove' : verb === '更新' ? 'update' : 'install/update/remove'} the module ${what()} (it changes plugins on this machine). Allow?`,
102
- zh: `agent 想${verb}模块 ${what()}(会改这台机器上的插件)。允许吗?`,
103
- },
104
- question: {
105
- en: 'it changes plugins on this machine. Allow?',
106
- zh: '会改这台机器上的插件。允许吗?',
107
- },
108
- });
109
- }
110
- if (name !== 'workbench_panels')
111
- return UNKNOWN;
112
- if (readAction)
113
- return READ;
114
- if (action === 'hide') {
115
- return ask({
116
- reason: 'workbench_panels hide removes a module from the panel',
117
- generic: {
118
- en: `The agent wants to remove ${what()} from this project's panel (its agent tools go offline here too). Allow?`,
119
- zh: `agent 想把 ${what()} 从这个项目的面板上收起来(这个项目里它的 agent 工具也一起下线)。允许吗?`,
120
- },
121
- question: {
122
- en: 'it takes that module off this panel (its agent tools go offline here too). Allow?',
123
- zh: `${what()}会从这个项目的面板上收起来(这个项目里它的 agent 工具也一起下线)。允许吗?`,
124
- },
125
- });
126
- }
127
- if (action === 'move') {
128
- return ask({
129
- reason: 'workbench_panels move reorders the panel',
130
- generic: {
131
- en: `The agent wants to reorder this project's panel (put ${what()} first). Allow?`,
132
- zh: `agent 想调整这个项目面板的顺序(把 ${what()} 放前面)。允许吗?`,
133
- },
134
- question: {
135
- en: `it reorders this panel (${what()} goes first). Allow?`,
136
- zh: `会调整这个项目面板的顺序(${what()}放前面)。允许吗?`,
137
- },
138
- });
16
+ let verdict;
17
+ try {
18
+ verdict = context.policy?.(call);
139
19
  }
140
- if (action === 'reset') {
141
- return ask({
142
- reason: 'workbench_panels reset clears the panel',
143
- generic: {
144
- en: "The agent wants to clear this project's panel (every module goes offline here). Allow?",
145
- zh: 'agent 想清空这个项目的面板(所有模块在这个项目里都下线)。允许吗?',
146
- },
147
- question: {
148
- en: 'it clears this panel (every module goes offline here). Allow?',
149
- zh: '会清空这个项目的面板(所有模块在这个项目里都下线)。允许吗?',
150
- },
151
- });
20
+ catch (error) {
21
+ return {
22
+ kind: 'deny',
23
+ reason: `问"要不要审批"的时候套件的策略抛了错,已经停下:${messageOf(error)}`,
24
+ };
152
25
  }
153
- return ask({
154
- reason: `workbench_panels ${action ?? '(unknown action)'} changes the user's panel`,
155
- generic: {
156
- en: `The agent wants to add ${what()} to this project's panel. Allow?`,
157
- zh: `agent 想把 ${what()} 加到**这个项目**的面板上。允许吗?`,
158
- },
159
- question: {
160
- en: `it adds ${what()} to this project's panel. Allow?`,
161
- zh: `会把 ${what()} 加到**这个项目**的面板上。允许吗?`,
162
- },
163
- });
164
- }
165
- export async function approvalFor(call, context = {}) {
166
- const effect = effectOf(call, context);
167
- if (effect.kind === 'read')
26
+ if (verdict?.kind === 'read')
168
27
  return undefined;
169
- if (effect.kind === 'unknown') {
170
- if (context.writes !== true)
171
- return undefined;
28
+ if (verdict?.kind !== 'ask') {
172
29
  return {
173
30
  kind: 'deny',
174
- reason: `${call.name ?? '这把工具'} 在清单里声明会写东西,但审批表没认领这次调用 —— 停下:` +
175
- '给它在 tool-approval.ts 里补一条审批(含"这次会发生什么"),别让写静默过去',
31
+ reason: `${call.name ?? '这把工具'} 在清单里声明会写东西,但它的套件没认领这次调用 —— 停下:` +
32
+ '在套件的 needsApproval 里补一条审批(含"这次会发生什么"),别让写静默过去',
176
33
  };
177
34
  }
178
- const { spec } = effect;
179
35
  let facts;
180
36
  try {
181
37
  facts = await context.detailOf?.(call);
182
38
  }
183
39
  catch (error) {
184
- const why = error instanceof Error ? error.message : String(error);
185
- return { kind: 'deny', reason: `拿不到"这次会发生什么",已经停下:${why}` };
40
+ return { kind: 'deny', reason: `拿不到"这次会发生什么",已经停下:${messageOf(error)}` };
186
41
  }
187
42
  if (facts !== undefined && 'blocked' in facts)
188
43
  return { kind: 'deny', reason: facts.blocked };
189
- return { kind: 'ask', reason: spec.reason, displayReason: compose(facts, spec) };
44
+ return { kind: 'ask', reason: verdict.reason, displayReason: compose(facts, verdict) };
190
45
  }
@@ -4,7 +4,7 @@ import type { RouteTable } from '../host/http.js';
4
4
  import type { LongTaskBase, LongTaskHost, LongTaskRegistry } from '../host/long-task.js';
5
5
  import type { ProvisionHandler } from '../host/provision.js';
6
6
  import type { UnitCapability } from './unit-libs.js';
7
- import type { ApprovalCall, ApprovalFacts } from '../host/tool-approval.js';
7
+ import type { ApprovalCall, ApprovalFacts, ApprovalPolicy } from '../host/tool-approval.js';
8
8
  import type { ValueHandoff } from '../host/value-handoff.js';
9
9
  import type { WorkspaceStore } from '../host/workspace-store.js';
10
10
  export interface UnitMountDeps {
@@ -59,6 +59,7 @@ export interface UnitMountResult {
59
59
  readonly provision?: ProvisionHandler;
60
60
  readonly longTasks?: LongTaskRegistry<LongTaskBase> | undefined;
61
61
  readonly approvalDetail?: ((call: ApprovalCall) => ApprovalFacts | undefined | Promise<ApprovalFacts | undefined>) | undefined;
62
+ readonly needsApproval?: ApprovalPolicy | undefined;
62
63
  }
63
64
  export interface UnitHostFace {
64
65
  readonly manifest: UnitManifest;
@@ -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.',
@@ -30,11 +44,13 @@ export const en = {
30
44
  'panels.enabled': 'enabled in this project',
31
45
  'panels.disabled': 'not enabled in this project',
32
46
  'panels.choose': 'Choose modules',
47
+ 'panels.buildModule': 'Build a module',
33
48
  'panels.failed': 'Could not save the change. Try again.',
34
49
  'panels.hintEffect': 'Disabling = the agent cannot reach that data',
35
50
  'panels.dragHint': 'Drag to reorder',
36
51
  'panels.unknownStep': 'No suite provides this view',
37
52
  'panels.labelWithHint': '{label} ({hint})',
53
+ 'panels.dep.failed': '⚠ failed to start',
38
54
  'panels.dep.missingPlugin': '⚠ not ready yet',
39
55
  'panels.dep.missingCommands': '⚠ needs an update',
40
56
  'panels.dep.unchecked': '⚠ cannot check',
@@ -228,6 +244,8 @@ export const en = {
228
244
  'blocker.exec.hint': 'Make sure opencli is installed and on PATH (which opencli).',
229
245
  'blocker.unknown.title': 'Something went wrong',
230
246
  'panels.reading': 'Reading the panel setup…',
247
+ 'panels.cardBroken': 'This module will not render: {unit} ({reason})',
248
+ 'panels.cardRetry': 'Retry',
231
249
  'panels.empty': 'No modules on the panel yet',
232
250
  'panels.emptyHint': 'You can also just tell the agent: “add a module to the panel”.',
233
251
  'env.title': 'Preparing the workbench',
@@ -266,6 +284,8 @@ export const en = {
266
284
  'env.step.warm': 'Warming up site sessions',
267
285
  'env.warm.done': '{n} site(s) warmed up',
268
286
  'env.warm.short': 'Not warmed this time — harmless; the panel heals on first read',
287
+ 'env.unit.failed': 'This module cannot start',
288
+ 'env.unit.failedHint': 'It failed while assembling itself; other modules are unaffected.',
269
289
  'env.unit.missingPlugin': 'This module is not ready yet',
270
290
  'env.unit.missingCommandsHint': 'Updating it should fix this.',
271
291
  'env.unit.unchecked': 'Cannot check: no shell channel',
@@ -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': "这个项目还没有会话。";
@@ -32,11 +46,13 @@ export declare const dictionaries: {
32
46
  readonly 'panels.enabled': "这个项目已启用";
33
47
  readonly 'panels.disabled': "这个项目未启用";
34
48
  readonly 'panels.choose': "选择模块";
49
+ readonly 'panels.buildModule': "造一个模块";
35
50
  readonly 'panels.failed': "改配置失败了,再试一次。";
36
51
  readonly 'panels.hintEffect': "停用 = agent 拿不到相关数据";
37
52
  readonly 'panels.dragHint': "拖动调整顺序";
38
53
  readonly 'panels.unknownStep': "这一层没有套件提供";
39
54
  readonly 'panels.labelWithHint': "{label}({hint})";
55
+ readonly 'panels.dep.failed': "⚠ 起不来";
40
56
  readonly 'panels.dep.missingPlugin': "⚠ 还没装好";
41
57
  readonly 'panels.dep.missingCommands': "⚠ 需要更新";
42
58
  readonly 'panels.dep.unchecked': "⚠ 检测不了";
@@ -230,6 +246,8 @@ export declare const dictionaries: {
230
246
  readonly 'blocker.exec.hint': "确认 opencli 已安装且在 PATH 里(which opencli)。";
231
247
  readonly 'blocker.unknown.title': "出了点问题";
232
248
  readonly 'panels.reading': "正在读取面板配置…";
249
+ readonly 'panels.cardBroken': "这个模块起不来:{unit}({reason})";
250
+ readonly 'panels.cardRetry': "重试";
233
251
  readonly 'panels.empty': "面板里还没有模块";
234
252
  readonly 'panels.emptyHint': "也可以直接跟 agent 说:「把某个模块加到面板」。";
235
253
  readonly 'env.title': "准备工作台";
@@ -268,6 +286,8 @@ export declare const dictionaries: {
268
286
  readonly 'env.step.warm': "预热站点登录态";
269
287
  readonly 'env.warm.done': "已预热 {n} 个站点";
270
288
  readonly 'env.warm.short': "这次没热上 —— 不影响用,面板第一次读取会自己补";
289
+ readonly 'env.unit.failed': "这个模块起不来";
290
+ readonly 'env.unit.failedHint': "它自己装配时报了错,别的模块不受影响。";
271
291
  readonly 'env.unit.missingPlugin': "这个模块还没装好";
272
292
  readonly 'env.unit.missingCommandsHint': "更新之后就能用了。";
273
293
  readonly 'env.unit.unchecked': "检测不了:拿不到执行通道";
@@ -290,7 +310,7 @@ export declare const dictionaries: {
290
310
  readonly 'panel.conversation': "对话";
291
311
  readonly 'panels.label': "全局面板";
292
312
  };
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>;
313
+ 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.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
314
  };
295
315
  export type { WorkbenchMessages };
296
316
  export interface Dictionaries {
@@ -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;