@dsh-workbench/shared 0.1.14 → 0.1.16
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/components/CardHead.js +3 -2
- package/dist/components/Field.css +16 -0
- package/dist/components/Field.d.ts +8 -0
- package/dist/components/Field.js +8 -0
- package/dist/components/Link.css +22 -0
- package/dist/components/Link.d.ts +10 -0
- package/dist/components/Link.js +8 -0
- package/dist/components/LongTaskRow.css +0 -15
- package/dist/components/LongTaskRow.js +3 -4
- package/dist/components/TextField.css +5 -0
- package/dist/host/browser-find.d.ts +38 -0
- package/dist/host/browser-find.js +242 -0
- package/dist/host/browser-read.d.ts +26 -0
- package/dist/host/browser-read.js +240 -0
- package/dist/host/http.d.ts +6 -1
- package/dist/host/http.js +2 -1
- package/dist/host/opencli-runtime.d.ts +3 -0
- package/dist/host/opencli-runtime.js +10 -0
- package/dist/host/opencli-window.d.ts +2 -0
- package/dist/host/opencli-window.js +8 -0
- package/dist/host/opencli.js +2 -0
- package/dist/host.d.ts +2 -0
- package/dist/host.js +2 -0
- package/dist/i18n/en.js +3 -0
- package/dist/i18n/index.d.ts +6 -3
- package/dist/i18n/zh.d.ts +5 -2
- package/dist/i18n/zh.js +5 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
- package/src/components/CardHead.test.tsx +81 -0
- package/src/components/CardHead.tsx +4 -3
- package/src/components/Field.css +16 -0
- package/src/components/Field.test.tsx +27 -0
- package/src/components/Field.tsx +19 -0
- package/src/components/Link.css +22 -0
- package/src/components/Link.test.tsx +37 -0
- package/src/components/Link.tsx +26 -0
- package/src/components/LongTaskRow.css +0 -15
- package/src/components/LongTaskRow.tsx +3 -4
- package/src/components/TextField.css +5 -0
- package/src/host/__tests__/browser-find.test.ts +319 -0
- package/src/host/__tests__/browser-read.test.ts +197 -0
- package/src/host/browser-find.ts +311 -0
- package/src/host/browser-read.ts +308 -0
- package/src/host/http.ts +8 -1
- package/src/host/opencli-runtime.ts +13 -0
- package/src/host/opencli-window.ts +11 -0
- package/src/host/opencli.ts +2 -0
- package/src/host.ts +2 -0
- package/src/i18n/en.ts +3 -0
- package/src/i18n/zh.ts +5 -2
- package/src/index.ts +2 -0
- package/dist/components/StateDot.d.ts +0 -5
- package/dist/components/StateDot.js +0 -10
- package/dist/host/__tests__/fakes.d.ts +0 -41
- package/dist/host/__tests__/fakes.js +0 -76
- package/dist/host/__tests__/preflight-fixtures.d.ts +0 -19
- package/dist/host/__tests__/preflight-fixtures.js +0 -47
- package/dist/host/context-inject.d.ts +0 -24
- package/dist/host/context-inject.js +0 -74
- package/dist/host/events.d.ts +0 -9
- package/dist/host/events.js +0 -70
- package/dist/host/mount-units.d.ts +0 -52
- package/dist/host/mount-units.js +0 -186
- package/dist/host/preflight.d.ts +0 -58
- package/dist/host/preflight.js +0 -233
- package/dist/host/probe-cache.d.ts +0 -6
- package/dist/host/probe-cache.js +0 -19
- package/dist/host/probe-libs.d.ts +0 -45
- package/dist/host/probe-libs.js +0 -61
- package/dist/host/probe-units.d.ts +0 -17
- package/dist/host/probe-units.js +0 -102
- package/dist/host/probe-verdict.d.ts +0 -7
- package/dist/host/probe-verdict.js +0 -143
- package/dist/host/tool-scope.d.ts +0 -32
- package/dist/host/tool-scope.js +0 -84
- package/dist/host/unit-readiness.d.ts +0 -7
- package/dist/host/unit-readiness.js +0 -5
- package/dist/host/unit-registry.d.ts +0 -9
- package/dist/host/unit-registry.js +0 -33
- package/dist/host/version-range.d.ts +0 -2
- package/dist/host/version-range.js +0 -73
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { parseJsonLoose, tailLines } from '../lib/text.js';
|
|
2
|
+
import { backgroundLine } from './opencli-window.js';
|
|
3
|
+
import { runShell } from './shell.js';
|
|
4
|
+
export const BROWSER_BRIDGE_SESSION = 'workbench';
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 25000;
|
|
6
|
+
const DEFAULT_RETRY_MS = 1200;
|
|
7
|
+
const DEFAULT_SETTLE_MS = 900;
|
|
8
|
+
const AUTH_CODES = new Set([
|
|
9
|
+
'NOT_LOGGED_IN',
|
|
10
|
+
'NOT_LOGIN',
|
|
11
|
+
'AUTH_FAILED',
|
|
12
|
+
'AUTH_REQUIRED',
|
|
13
|
+
'UNAUTHENTICATED',
|
|
14
|
+
'UNAUTHORIZED',
|
|
15
|
+
'TOKEN_EXPIRED',
|
|
16
|
+
]);
|
|
17
|
+
const PERMISSION_CODES = new Set(['FORBIDDEN', 'PERMISSION_DENIED', 'NO_PERMISSION', 'EACCES']);
|
|
18
|
+
function asRecord(value) {
|
|
19
|
+
return typeof value === 'object' && value !== null ? value : undefined;
|
|
20
|
+
}
|
|
21
|
+
function textOf(value) {
|
|
22
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
23
|
+
}
|
|
24
|
+
function codeOf(page) {
|
|
25
|
+
const record = asRecord(page);
|
|
26
|
+
return record === undefined ? '' : textOf(record['code']);
|
|
27
|
+
}
|
|
28
|
+
function messageOf(page) {
|
|
29
|
+
const record = asRecord(page);
|
|
30
|
+
return record === undefined ? '' : textOf(record['message']);
|
|
31
|
+
}
|
|
32
|
+
function hintOf(page) {
|
|
33
|
+
const record = asRecord(page);
|
|
34
|
+
const hint = record === undefined ? '' : textOf(record['hint']);
|
|
35
|
+
return hint === '' ? undefined : hint;
|
|
36
|
+
}
|
|
37
|
+
function pageFailed(page) {
|
|
38
|
+
const record = asRecord(page);
|
|
39
|
+
return record !== undefined && record['ok'] === false;
|
|
40
|
+
}
|
|
41
|
+
function blockerFor(code) {
|
|
42
|
+
const normalized = code.trim().toUpperCase().replace(/-/g, '_');
|
|
43
|
+
if (normalized === '')
|
|
44
|
+
return undefined;
|
|
45
|
+
const matches = (set) => [...set].some((one) => normalized === one || normalized.endsWith(`_${one}`));
|
|
46
|
+
if (matches(PERMISSION_CODES))
|
|
47
|
+
return 'permission-denied';
|
|
48
|
+
if (matches(AUTH_CODES) || normalized.includes('UNAUTH'))
|
|
49
|
+
return 'auth-required';
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
function sleep(ms) {
|
|
53
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
54
|
+
}
|
|
55
|
+
async function shellRun(shell, command, argv, timeoutMs) {
|
|
56
|
+
const result = await runShell(shell, backgroundLine(command, argv), { timeoutMs });
|
|
57
|
+
if (result.timedOut) {
|
|
58
|
+
return { kind: 'timeout', detail: `没在 ${Math.round(timeoutMs / 1000)} 秒内返回`, stderr: result.stderr };
|
|
59
|
+
}
|
|
60
|
+
if (result.exitCode !== 0) {
|
|
61
|
+
return { kind: 'failed', detail: tailLines(result.stderr || result.stdout, 4), stderr: result.stderr };
|
|
62
|
+
}
|
|
63
|
+
return { kind: 'ok', stdout: result.stdout };
|
|
64
|
+
}
|
|
65
|
+
async function evalOnce(shell, command, session, script, timeoutMs, retryMs) {
|
|
66
|
+
const run = async () => {
|
|
67
|
+
let attempt;
|
|
68
|
+
try {
|
|
69
|
+
attempt = await shellRun(shell, command, ['browser', session, 'eval', script, '--window', 'background'], timeoutMs);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
return {
|
|
73
|
+
kind: 'failure',
|
|
74
|
+
result: {
|
|
75
|
+
ok: false,
|
|
76
|
+
code: 'cli-exec-failed',
|
|
77
|
+
message: '读不了:opencli 起不来',
|
|
78
|
+
hint: '确认这台机器上 opencli 能跑(工作台会自己把它挂好)',
|
|
79
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (attempt.kind === 'timeout') {
|
|
84
|
+
return {
|
|
85
|
+
kind: 'failure',
|
|
86
|
+
result: {
|
|
87
|
+
ok: false,
|
|
88
|
+
code: 'cli-timeout',
|
|
89
|
+
message: '这次没读到(等超时了)',
|
|
90
|
+
hint: '稍后重试',
|
|
91
|
+
detail: attempt.detail,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (attempt.kind === 'failed') {
|
|
96
|
+
const normalized = attempt.stderr.trim().toUpperCase().replace(/-/g, '_');
|
|
97
|
+
const forbidden = [...PERMISSION_CODES].some((one) => normalized.includes(one));
|
|
98
|
+
return {
|
|
99
|
+
kind: 'failure',
|
|
100
|
+
result: {
|
|
101
|
+
ok: false,
|
|
102
|
+
code: forbidden ? 'permission-denied' : 'cli-exec-failed',
|
|
103
|
+
message: forbidden ? '没有权限在这台机器上读' : '读不了(命令失败)',
|
|
104
|
+
hint: forbidden ? '换一个有权限的账号,或让用户开权限' : '把下面这段原始输出看一眼再重试',
|
|
105
|
+
detail: attempt.detail,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const value = parseJsonLoose(attempt.stdout);
|
|
110
|
+
if (value === undefined) {
|
|
111
|
+
return {
|
|
112
|
+
kind: 'failure',
|
|
113
|
+
result: {
|
|
114
|
+
ok: false,
|
|
115
|
+
code: 'cli-exec-failed',
|
|
116
|
+
message: '读到了、但不是 JSON',
|
|
117
|
+
hint: '脚本必须回 JSON',
|
|
118
|
+
detail: tailLines(attempt.stdout, 4),
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return { kind: 'value', value };
|
|
123
|
+
};
|
|
124
|
+
const first = await run();
|
|
125
|
+
if (first.kind === 'value')
|
|
126
|
+
return first;
|
|
127
|
+
if (first.result.code !== 'cli-exec-failed')
|
|
128
|
+
return first;
|
|
129
|
+
await sleep(retryMs);
|
|
130
|
+
return run();
|
|
131
|
+
}
|
|
132
|
+
async function openOnce(shell, command, session, url, timeoutMs) {
|
|
133
|
+
let attempt;
|
|
134
|
+
try {
|
|
135
|
+
attempt = await shellRun(shell, command, ['browser', session, 'open', url, '--window', 'background'], timeoutMs);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
return {
|
|
139
|
+
ok: false,
|
|
140
|
+
code: 'cli-exec-failed',
|
|
141
|
+
message: '打不开那个页面',
|
|
142
|
+
hint: '确认 opencli 能跑起来',
|
|
143
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
144
|
+
helpUrl: url,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (attempt.kind === 'ok')
|
|
148
|
+
return undefined;
|
|
149
|
+
return {
|
|
150
|
+
ok: false,
|
|
151
|
+
code: attempt.kind === 'timeout' ? 'cli-timeout' : 'cli-exec-failed',
|
|
152
|
+
message: '打不开那个页面',
|
|
153
|
+
hint: '在浏览器里手动打开一次,然后重试',
|
|
154
|
+
detail: attempt.detail,
|
|
155
|
+
helpUrl: url,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
export async function browserRead(options) {
|
|
159
|
+
const { shell, script, url, loginUrl, open = true } = options;
|
|
160
|
+
const command = options.command ?? 'opencli';
|
|
161
|
+
const session = options.session ?? BROWSER_BRIDGE_SESSION;
|
|
162
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
163
|
+
const retryMs = options.retryMs ?? DEFAULT_RETRY_MS;
|
|
164
|
+
const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
|
|
165
|
+
const at = Date.now();
|
|
166
|
+
const first = await evalOnce(shell, command, session, script, timeoutMs, retryMs);
|
|
167
|
+
if (first.kind === 'failure')
|
|
168
|
+
return { ...first.result, at };
|
|
169
|
+
let page = first.value;
|
|
170
|
+
if (codeOf(page) === 'not-on-site') {
|
|
171
|
+
if (!open || url === undefined) {
|
|
172
|
+
return {
|
|
173
|
+
ok: false,
|
|
174
|
+
code: 'cli-exec-failed',
|
|
175
|
+
at,
|
|
176
|
+
message: '没停在那套系统上',
|
|
177
|
+
hint: '先在那个浏览器里把它打开,再读一次',
|
|
178
|
+
detail: JSON.stringify(page),
|
|
179
|
+
...(url === undefined ? {} : { helpUrl: url }),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
const opened = await openOnce(shell, command, session, url, timeoutMs);
|
|
183
|
+
if (opened !== undefined)
|
|
184
|
+
return { ...opened, at };
|
|
185
|
+
await sleep(settleMs);
|
|
186
|
+
const second = await evalOnce(shell, command, session, script, timeoutMs, retryMs);
|
|
187
|
+
if (second.kind === 'failure')
|
|
188
|
+
return { ...second.result, at };
|
|
189
|
+
page = second.value;
|
|
190
|
+
if (codeOf(page) === 'not-on-site') {
|
|
191
|
+
return {
|
|
192
|
+
ok: false,
|
|
193
|
+
code: 'cli-exec-failed',
|
|
194
|
+
at,
|
|
195
|
+
message: '打开了页面、又读了一遍,仍然不在那套系统上',
|
|
196
|
+
hint: '确认这个地址就是那套系统(或者它跳到了别的域)',
|
|
197
|
+
detail: JSON.stringify(page),
|
|
198
|
+
helpUrl: url,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (!pageFailed(page))
|
|
203
|
+
return { ok: true, code: 'ok', at, value: page, message: '读到了' };
|
|
204
|
+
const pageCode = codeOf(page) || 'read-failed';
|
|
205
|
+
const message = messageOf(page) || `读不到(${pageCode})`;
|
|
206
|
+
const blocker = blockerFor(pageCode);
|
|
207
|
+
if (blocker === 'auth-required') {
|
|
208
|
+
return {
|
|
209
|
+
ok: false,
|
|
210
|
+
code: blocker,
|
|
211
|
+
at,
|
|
212
|
+
pageCode,
|
|
213
|
+
message,
|
|
214
|
+
hint: '去登录一次,再重试',
|
|
215
|
+
detail: JSON.stringify(page),
|
|
216
|
+
...(loginUrl === undefined && url === undefined ? {} : { helpUrl: loginUrl ?? url }),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
if (blocker === 'permission-denied') {
|
|
220
|
+
return {
|
|
221
|
+
ok: false,
|
|
222
|
+
code: blocker,
|
|
223
|
+
at,
|
|
224
|
+
pageCode,
|
|
225
|
+
message,
|
|
226
|
+
hint: '这个账号没有权限',
|
|
227
|
+
detail: JSON.stringify(page),
|
|
228
|
+
...(loginUrl === undefined && url === undefined ? {} : { helpUrl: loginUrl ?? url }),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
ok: false,
|
|
233
|
+
code: pageCode,
|
|
234
|
+
at,
|
|
235
|
+
pageCode,
|
|
236
|
+
message,
|
|
237
|
+
detail: JSON.stringify(page),
|
|
238
|
+
...(hintOf(page) === undefined ? {} : { hint: hintOf(page) }),
|
|
239
|
+
};
|
|
240
|
+
}
|
package/dist/host/http.d.ts
CHANGED
|
@@ -31,6 +31,11 @@ export declare function preflightHandler(preflight: {
|
|
|
31
31
|
snapshot(): unknown;
|
|
32
32
|
}): RouteHandler;
|
|
33
33
|
export declare function capabilityHandler(probe: (unit: string | undefined, force: boolean) => Promise<unknown> | unknown): RouteHandler;
|
|
34
|
-
export
|
|
34
|
+
export interface OpencliHealth {
|
|
35
|
+
readonly command: string;
|
|
36
|
+
readonly source: 'env' | 'isolated' | 'path';
|
|
37
|
+
readonly isolated: string | null;
|
|
38
|
+
}
|
|
39
|
+
export declare function healthHandler(ctx: DshHostContext, opencli?: OpencliHealth): RouteHandler;
|
|
35
40
|
export declare function registerPrefs(ctx: DshHostContext, resources: readonly PostResource[], wrote?: ((resource: string, write: PostWrite) => void) | undefined): () => void;
|
|
36
41
|
export declare function registerActions(ctx: DshHostContext, resources: readonly PostResource[], wrote?: ((resource: string, write: PostWrite) => void) | undefined): () => void;
|
package/dist/host/http.js
CHANGED
|
@@ -192,7 +192,7 @@ export function capabilityHandler(probe) {
|
|
|
192
192
|
return { ok: true, ...result };
|
|
193
193
|
};
|
|
194
194
|
}
|
|
195
|
-
export function healthHandler(ctx) {
|
|
195
|
+
export function healthHandler(ctx, opencli) {
|
|
196
196
|
return () => {
|
|
197
197
|
const profile = ctx.get('profileContext');
|
|
198
198
|
const dir = profile?.dir;
|
|
@@ -203,6 +203,7 @@ export function healthHandler(ctx) {
|
|
|
203
203
|
profileDir: dir ?? null,
|
|
204
204
|
pid: process.pid,
|
|
205
205
|
now: new Date().toISOString(),
|
|
206
|
+
...(opencli === undefined ? {} : { opencli }),
|
|
206
207
|
};
|
|
207
208
|
};
|
|
208
209
|
}
|
|
@@ -7,6 +7,9 @@ export declare const OPENCLI_NPM_PACKAGE = "@jackwener/opencli";
|
|
|
7
7
|
export declare function toolchainDir(): string;
|
|
8
8
|
export declare function toolchainOpencliDir(): string;
|
|
9
9
|
export declare function vendorPrefix(): string;
|
|
10
|
+
export declare function vendorPluginsDir(): string;
|
|
11
|
+
export declare const OPENCLI_CATALOG_ENV = "DSH_WB_OPENCLI_CATALOG";
|
|
12
|
+
export declare function cliCatalogPath(): string;
|
|
10
13
|
export declare function vendorPeerLink(): {
|
|
11
14
|
readonly dir: string;
|
|
12
15
|
readonly parent: string;
|
|
@@ -18,6 +18,16 @@ export function toolchainOpencliDir() {
|
|
|
18
18
|
export function vendorPrefix() {
|
|
19
19
|
return `${isolatedRoot()}/plugins-npm`;
|
|
20
20
|
}
|
|
21
|
+
export function vendorPluginsDir() {
|
|
22
|
+
return `${vendorPrefix()}/node_modules`;
|
|
23
|
+
}
|
|
24
|
+
export const OPENCLI_CATALOG_ENV = 'DSH_WB_OPENCLI_CATALOG';
|
|
25
|
+
export function cliCatalogPath() {
|
|
26
|
+
const override = process.env[OPENCLI_CATALOG_ENV]?.trim();
|
|
27
|
+
return override === undefined || override === ''
|
|
28
|
+
? `${toolchainOpencliDir()}/cli-manifest.json`
|
|
29
|
+
: override;
|
|
30
|
+
}
|
|
21
31
|
export function vendorPeerLink() {
|
|
22
32
|
const base = `${vendorPrefix()}/node_modules`;
|
|
23
33
|
const [scope] = OPENCLI_NPM_PACKAGE.split('/');
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { quoteShellArg } from '../lib/text.js';
|
|
2
|
+
const OPENCLI_WINDOW_ENV = 'OPENCLI_WINDOW=background';
|
|
3
|
+
export function backgroundPrefix() {
|
|
4
|
+
return [quoteShellArg('env'), quoteShellArg(OPENCLI_WINDOW_ENV)];
|
|
5
|
+
}
|
|
6
|
+
export function backgroundLine(command, argv) {
|
|
7
|
+
return [...backgroundPrefix(), quoteShellArg(command), ...argv.map(quoteShellArg)].join(' ');
|
|
8
|
+
}
|
package/dist/host/opencli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OPENCLI_MAX_CONCURRENT, OPENCLI_TIMEOUT_MS } from '../config/constants.js';
|
|
2
2
|
import { parseJsonLoose, quoteShellArg, tailLines } from '../lib/text.js';
|
|
3
3
|
import { helpUrlOf, parseCliErrorEnvelope } from './cli-error.js';
|
|
4
|
+
import { backgroundPrefix } from './opencli-window.js';
|
|
4
5
|
import { runShell } from './shell.js';
|
|
5
6
|
const FORBIDDEN_CODES = new Set(['FORBIDDEN', 'PERMISSION_DENIED']);
|
|
6
7
|
const SITE_SESSION_ARGS = ['--site-session', 'persistent'];
|
|
@@ -26,6 +27,7 @@ export class WorkbenchDataError extends Error {
|
|
|
26
27
|
}
|
|
27
28
|
export function opencliCommandLine(command, argv, session = 'persistent') {
|
|
28
29
|
return [
|
|
30
|
+
...backgroundPrefix(),
|
|
29
31
|
quoteShellArg(command),
|
|
30
32
|
...argv.map(quoteShellArg),
|
|
31
33
|
...(session === 'persistent' ? SITE_SESSION_ARGS : []),
|
package/dist/host.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export * from './lib/ui.js';
|
|
|
8
8
|
export * from './api/blocker-table.js';
|
|
9
9
|
export * from './host/cli-error.js';
|
|
10
10
|
export * from './host/data-root.js';
|
|
11
|
+
export * from './host/browser-read.js';
|
|
12
|
+
export * from './host/browser-find.js';
|
|
11
13
|
export * from './host/facts.js';
|
|
12
14
|
export * from './host/git.js';
|
|
13
15
|
export * from './host/http.js';
|
package/dist/host.js
CHANGED
|
@@ -8,6 +8,8 @@ export * from './lib/ui.js';
|
|
|
8
8
|
export * from './api/blocker-table.js';
|
|
9
9
|
export * from './host/cli-error.js';
|
|
10
10
|
export * from './host/data-root.js';
|
|
11
|
+
export * from './host/browser-read.js';
|
|
12
|
+
export * from './host/browser-find.js';
|
|
11
13
|
export * from './host/facts.js';
|
|
12
14
|
export * from './host/git.js';
|
|
13
15
|
export * from './host/http.js';
|
package/dist/i18n/en.js
CHANGED
|
@@ -94,6 +94,8 @@ export const en = {
|
|
|
94
94
|
'panels.market.badSource': 'The install method is incomplete — install it yourself',
|
|
95
95
|
'panels.market.empty': 'No modules available',
|
|
96
96
|
'panels.market.installedSection': 'Installed',
|
|
97
|
+
'panels.market.sectionLocal': 'Linked locally',
|
|
98
|
+
'panels.market.sectionUnlisted': 'Not from the market',
|
|
97
99
|
'panels.market.moduleOutdated': 'update available',
|
|
98
100
|
'panels.market.moduleRestartHint': 'installed but not active — restart dsh once to bring it up',
|
|
99
101
|
'panels.market.restartNeeded': 'The new version is installed — restart once to pick it up.',
|
|
@@ -302,6 +304,7 @@ export const en = {
|
|
|
302
304
|
'rightbar.refreshing': 'Refreshing…',
|
|
303
305
|
'rightbar.updatedAt': 'updated {time}',
|
|
304
306
|
'rightbar.noProject': 'The current session has no workspace',
|
|
307
|
+
'rightbar.openPlatform': 'Open on the platform',
|
|
305
308
|
'coding.notARepo.title': 'This workspace is not a Coding repository',
|
|
306
309
|
'error.unknown': 'Something went wrong',
|
|
307
310
|
'board.title': 'Workbench panel',
|
package/dist/i18n/index.d.ts
CHANGED
|
@@ -61,7 +61,7 @@ export declare const dictionaries: {
|
|
|
61
61
|
readonly 'panels.browseMarket': "浏览模块市场";
|
|
62
62
|
readonly 'panels.noneInstalled': "这些模块要的插件都还没装";
|
|
63
63
|
readonly 'panels.noneInstalledHint': "装好之后它们会回到这里,再由你决定放哪几个上板。";
|
|
64
|
-
readonly 'panels.market.installed': "
|
|
64
|
+
readonly 'panels.market.installed': "已安装";
|
|
65
65
|
readonly 'panels.market.outdated': "要更新";
|
|
66
66
|
readonly 'panels.market.hasNew': "有新版本";
|
|
67
67
|
readonly 'panels.market.missing': "未装";
|
|
@@ -95,7 +95,9 @@ export declare const dictionaries: {
|
|
|
95
95
|
readonly 'panels.market.noSource': "这个模块没声明怎么装,只能自己动手";
|
|
96
96
|
readonly 'panels.market.badSource': "安装方式没写全,只能自己动手";
|
|
97
97
|
readonly 'panels.market.empty': "暂无可用模块";
|
|
98
|
-
readonly 'panels.market.installedSection': "
|
|
98
|
+
readonly 'panels.market.installedSection': "已安装";
|
|
99
|
+
readonly 'panels.market.sectionLocal': "本地装的";
|
|
100
|
+
readonly 'panels.market.sectionUnlisted': "不是从市场装的";
|
|
99
101
|
readonly 'panels.market.moduleOutdated': "有新版";
|
|
100
102
|
readonly 'panels.market.moduleRestartHint': "装好了但还没生效 —— 重启一次 dsh 就会出来";
|
|
101
103
|
readonly 'panels.market.restartNeeded': "新版装好了,重启一次才生效。";
|
|
@@ -304,13 +306,14 @@ export declare const dictionaries: {
|
|
|
304
306
|
readonly 'rightbar.refreshing': "刷新中…";
|
|
305
307
|
readonly 'rightbar.updatedAt': "更新于 {time}";
|
|
306
308
|
readonly 'rightbar.noProject': "当前会话没有所属项目";
|
|
309
|
+
readonly 'rightbar.openPlatform': "在平台上打开";
|
|
307
310
|
readonly 'coding.notARepo.title': "这个项目不是 Coding 仓库";
|
|
308
311
|
readonly 'error.unknown': "出错了";
|
|
309
312
|
readonly 'board.title': "工作台面板";
|
|
310
313
|
readonly 'panel.conversation': "对话";
|
|
311
314
|
readonly 'panels.label': "全局面板";
|
|
312
315
|
};
|
|
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>;
|
|
316
|
+
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.sectionLocal" | "panels.market.sectionUnlisted" | "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" | "rightbar.openPlatform" | "coding.notARepo.title" | "error.unknown" | "board.title" | "panel.conversation" | "panels.label", string>;
|
|
314
317
|
};
|
|
315
318
|
export type { WorkbenchMessages };
|
|
316
319
|
export interface Dictionaries {
|
package/dist/i18n/zh.d.ts
CHANGED
|
@@ -59,7 +59,7 @@ export declare const zh: {
|
|
|
59
59
|
readonly 'panels.browseMarket': "浏览模块市场";
|
|
60
60
|
readonly 'panels.noneInstalled': "这些模块要的插件都还没装";
|
|
61
61
|
readonly 'panels.noneInstalledHint': "装好之后它们会回到这里,再由你决定放哪几个上板。";
|
|
62
|
-
readonly 'panels.market.installed': "
|
|
62
|
+
readonly 'panels.market.installed': "已安装";
|
|
63
63
|
readonly 'panels.market.outdated': "要更新";
|
|
64
64
|
readonly 'panels.market.hasNew': "有新版本";
|
|
65
65
|
readonly 'panels.market.missing': "未装";
|
|
@@ -93,7 +93,9 @@ export declare const zh: {
|
|
|
93
93
|
readonly 'panels.market.noSource': "这个模块没声明怎么装,只能自己动手";
|
|
94
94
|
readonly 'panels.market.badSource': "安装方式没写全,只能自己动手";
|
|
95
95
|
readonly 'panels.market.empty': "暂无可用模块";
|
|
96
|
-
readonly 'panels.market.installedSection': "
|
|
96
|
+
readonly 'panels.market.installedSection': "已安装";
|
|
97
|
+
readonly 'panels.market.sectionLocal': "本地装的";
|
|
98
|
+
readonly 'panels.market.sectionUnlisted': "不是从市场装的";
|
|
97
99
|
readonly 'panels.market.moduleOutdated': "有新版";
|
|
98
100
|
readonly 'panels.market.moduleRestartHint': "装好了但还没生效 —— 重启一次 dsh 就会出来";
|
|
99
101
|
readonly 'panels.market.restartNeeded': "新版装好了,重启一次才生效。";
|
|
@@ -302,6 +304,7 @@ export declare const zh: {
|
|
|
302
304
|
readonly 'rightbar.refreshing': "刷新中…";
|
|
303
305
|
readonly 'rightbar.updatedAt': "更新于 {time}";
|
|
304
306
|
readonly 'rightbar.noProject': "当前会话没有所属项目";
|
|
307
|
+
readonly 'rightbar.openPlatform': "在平台上打开";
|
|
305
308
|
readonly 'coding.notARepo.title': "这个项目不是 Coding 仓库";
|
|
306
309
|
readonly 'error.unknown': "出错了";
|
|
307
310
|
readonly 'board.title': "工作台面板";
|
package/dist/i18n/zh.js
CHANGED
|
@@ -59,7 +59,7 @@ export const zh = {
|
|
|
59
59
|
'panels.browseMarket': '浏览模块市场',
|
|
60
60
|
'panels.noneInstalled': '这些模块要的插件都还没装',
|
|
61
61
|
'panels.noneInstalledHint': '装好之后它们会回到这里,再由你决定放哪几个上板。',
|
|
62
|
-
'panels.market.installed': '
|
|
62
|
+
'panels.market.installed': '已安装',
|
|
63
63
|
'panels.market.outdated': '要更新',
|
|
64
64
|
'panels.market.hasNew': '有新版本',
|
|
65
65
|
'panels.market.missing': '未装',
|
|
@@ -93,7 +93,9 @@ export const zh = {
|
|
|
93
93
|
'panels.market.noSource': '这个模块没声明怎么装,只能自己动手',
|
|
94
94
|
'panels.market.badSource': '安装方式没写全,只能自己动手',
|
|
95
95
|
'panels.market.empty': '暂无可用模块',
|
|
96
|
-
'panels.market.installedSection': '
|
|
96
|
+
'panels.market.installedSection': '已安装',
|
|
97
|
+
'panels.market.sectionLocal': '本地装的',
|
|
98
|
+
'panels.market.sectionUnlisted': '不是从市场装的',
|
|
97
99
|
'panels.market.moduleOutdated': '有新版',
|
|
98
100
|
'panels.market.moduleRestartHint': '装好了但还没生效 —— 重启一次 dsh 就会出来',
|
|
99
101
|
'panels.market.restartNeeded': '新版装好了,重启一次才生效。',
|
|
@@ -302,6 +304,7 @@ export const zh = {
|
|
|
302
304
|
'rightbar.refreshing': '刷新中…',
|
|
303
305
|
'rightbar.updatedAt': '更新于 {time}',
|
|
304
306
|
'rightbar.noProject': '当前会话没有所属项目',
|
|
307
|
+
'rightbar.openPlatform': '在平台上打开',
|
|
305
308
|
'coding.notARepo.title': '这个项目不是 Coding 仓库',
|
|
306
309
|
'error.unknown': '出错了',
|
|
307
310
|
'board.title': '工作台面板',
|
package/dist/index.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export * from './components/SettingsSection.js';
|
|
|
27
27
|
export * from './components/Stack.js';
|
|
28
28
|
export * from './components/StateBlock.js';
|
|
29
29
|
export * from './components/TextField.js';
|
|
30
|
+
export * from './components/Field.js';
|
|
31
|
+
export * from './components/Link.js';
|
|
30
32
|
export * from './components/board-module.js';
|
|
31
33
|
export * from './components/icons.js';
|
|
32
34
|
export * from './config/constants.js';
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,8 @@ export * from './components/SettingsSection.js';
|
|
|
27
27
|
export * from './components/Stack.js';
|
|
28
28
|
export * from './components/StateBlock.js';
|
|
29
29
|
export * from './components/TextField.js';
|
|
30
|
+
export * from './components/Field.js';
|
|
31
|
+
export * from './components/Link.js';
|
|
30
32
|
export * from './components/board-module.js';
|
|
31
33
|
export * from './components/icons.js';
|
|
32
34
|
export * from './config/constants.js';
|
package/package.json
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { CardHead } from './CardHead.js';
|
|
3
|
+
|
|
4
|
+
interface AnyElement {
|
|
5
|
+
readonly type: unknown;
|
|
6
|
+
readonly props: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const t = (key: string): string => key;
|
|
10
|
+
|
|
11
|
+
function head(overrides: Record<string, unknown> = {}): AnyElement {
|
|
12
|
+
return CardHead({
|
|
13
|
+
t,
|
|
14
|
+
title: '套餐余额',
|
|
15
|
+
refreshing: false,
|
|
16
|
+
onRefresh: () => {},
|
|
17
|
+
...overrides,
|
|
18
|
+
} as never) as unknown as AnyElement;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function allChildren(node: AnyElement): AnyElement[] {
|
|
22
|
+
const raw = node.props['children'];
|
|
23
|
+
return (Array.isArray(raw) ? raw : [raw]).filter(
|
|
24
|
+
(one): one is AnyElement => typeof one === 'object' && one !== null && 'props' in (one as AnyElement),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function find(root: AnyElement, hit: (node: AnyElement) => boolean): AnyElement | undefined {
|
|
29
|
+
for (const child of allChildren(root)) {
|
|
30
|
+
if (hit(child)) return child;
|
|
31
|
+
const deeper = find(child, hit);
|
|
32
|
+
if (deeper !== undefined) return deeper;
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function byClass(root: AnyElement, needle: string): AnyElement | undefined {
|
|
38
|
+
return find(root, (node) => {
|
|
39
|
+
const className = node.props['className'];
|
|
40
|
+
return typeof className === 'string' && className.includes(needle);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function byLabel(root: AnyElement, label: string): AnyElement | undefined {
|
|
45
|
+
return find(root, (node) => node.props['aria-label'] === label);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
describe('CardHead 的那颗 ↗:platformHref 一个人说了算', () => {
|
|
49
|
+
it('只给 platformHref 也画得出来(少给 platform 不再静默丢一颗按钮)', () => {
|
|
50
|
+
const anchor = byClass(head({ platformHref: 'https://example.com/wallet' }), 'wb-cardOpen');
|
|
51
|
+
expect(anchor).toBeDefined();
|
|
52
|
+
expect(anchor?.props['href']).toBe('https://example.com/wallet');
|
|
53
|
+
expect(anchor?.props['target']).toBe('_blank');
|
|
54
|
+
expect(anchor?.props['rel']).toBe('noreferrer noopener');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('platformHref 不给就一颗都不画(不是画一颗没有链接的)', () => {
|
|
58
|
+
expect(byClass(head(), 'wb-cardOpen')).toBeUndefined();
|
|
59
|
+
expect(byClass(head({ platform: 'LLM Gateway' }), 'wb-cardOpen')).toBeUndefined();
|
|
60
|
+
expect(byClass(head({ platformTitle: '在 LLM Gateway 里打开钱包' }), 'wb-cardOpen')).toBeUndefined();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('aria-label:platform 优先,其次 platformTitle,都没有就退回那句话', () => {
|
|
64
|
+
expect(byClass(head({ platformHref: '/x' }), 'wb-cardOpen')?.props['aria-label']).toBe(
|
|
65
|
+
'rightbar.openPlatform',
|
|
66
|
+
);
|
|
67
|
+
expect(byClass(head({ platformHref: '/x', platform: 'LLM Gateway' }), 'wb-cardOpen')?.props['aria-label']).toBe(
|
|
68
|
+
'LLM Gateway',
|
|
69
|
+
);
|
|
70
|
+
expect(
|
|
71
|
+
byClass(head({ platformHref: '/x', platformTitle: '在 LLM Gateway 里打开钱包' }), 'wb-cardOpen')?.props[
|
|
72
|
+
'aria-label'
|
|
73
|
+
],
|
|
74
|
+
).toBe('在 LLM Gateway 里打开钱包');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('刷新那颗永远在(有没有 ↗ 都不影响它)', () => {
|
|
78
|
+
expect(byLabel(head(), 'rightbar.refresh')).toBeDefined();
|
|
79
|
+
expect(byLabel(head({ platformHref: '/x' }), 'rightbar.refresh')).toBeDefined();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -38,18 +38,19 @@ export function CardHead({
|
|
|
38
38
|
platformTitle,
|
|
39
39
|
}: CardHeadProps) {
|
|
40
40
|
const refreshLabel = refreshing ? t('rightbar.refreshing') : t('rightbar.refresh');
|
|
41
|
+
const platformLabel = platformTitle ?? platform ?? t('rightbar.openPlatform');
|
|
41
42
|
return (
|
|
42
43
|
<header className="wb-cardHead">
|
|
43
44
|
<h2 className="wb-cardTitle">{title}</h2>
|
|
44
45
|
{meta === undefined ? null : <span className="wb-cardMeta">{meta}</span>}
|
|
45
|
-
{platformHref === undefined
|
|
46
|
-
<Tooltip side="bottom" align="end" gap={6} portal label={
|
|
46
|
+
{platformHref === undefined ? null : (
|
|
47
|
+
<Tooltip side="bottom" align="end" gap={6} portal label={platformLabel}>
|
|
47
48
|
<a
|
|
48
49
|
className="wb-iconButton wb-cardOpen"
|
|
49
50
|
href={platformHref}
|
|
50
51
|
target="_blank"
|
|
51
52
|
rel="noreferrer noopener"
|
|
52
|
-
aria-label={platform}
|
|
53
|
+
aria-label={platform ?? platformLabel}
|
|
53
54
|
>
|
|
54
55
|
<IconRightUpOutlineRegular size={14} />
|
|
55
56
|
</a>
|