mcp-osp-prompt 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,95 @@
1
+ import http from 'http';
2
+ import url from 'url';
3
+
4
+ let resolverQueue = [];
5
+ let server = null;
6
+ let serverPort = null;
7
+
8
+ export function startDialogServer() {
9
+ if (server && serverPort) {
10
+ console.log(`[Dialog Server] Reusing existing server on port ${serverPort}`);
11
+ return Promise.resolve(serverPort);
12
+ }
13
+
14
+ return new Promise((resolve) => {
15
+ server = http.createServer((req, res) => {
16
+ const parsedUrl = url.parse(req.url, true);
17
+
18
+ // 设置CORS头
19
+ res.setHeader('Access-Control-Allow-Origin', '*');
20
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
21
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
22
+
23
+ if (req.method === 'OPTIONS') {
24
+ res.writeHead(200);
25
+ res.end();
26
+ return;
27
+ }
28
+
29
+ if (parsedUrl.pathname === '/submit') {
30
+ const response = parsedUrl.query.text || '';
31
+ console.log(`[Dialog Server] Received response: "${response}"`);
32
+
33
+ // 设置CORS头并返回成功响应
34
+ res.writeHead(200, {
35
+ 'Content-Type': 'application/json',
36
+ 'Access-Control-Allow-Origin': '*'
37
+ });
38
+ res.end(JSON.stringify({ success: true, message: 'Response received successfully' }));
39
+
40
+ // 解决最新的Promise
41
+ if (resolverQueue.length > 0) {
42
+ const resolver = resolverQueue.shift(); // 取出最早的resolver
43
+ console.log(`[Dialog Server] Resolving promise with response: "${response}" (${resolverQueue.length} remaining)`);
44
+
45
+ try {
46
+ resolver(response);
47
+ console.log('[Dialog Server] Promise resolved successfully');
48
+ } catch (error) {
49
+ console.log('[Dialog Server] Error resolving promise:', error);
50
+ }
51
+ } else {
52
+ console.log('[Dialog Server] Warning: No resolver waiting for response');
53
+ }
54
+ } else {
55
+ res.writeHead(404);
56
+ res.end('Not found');
57
+ }
58
+ });
59
+
60
+ server.listen(0, 'localhost', () => {
61
+ serverPort = server.address().port;
62
+ console.log(`[Dialog Server] Started on http://localhost:${serverPort}`);
63
+ resolve(serverPort);
64
+ });
65
+ });
66
+ }
67
+
68
+ export function waitForResponse() {
69
+ return new Promise((resolve) => {
70
+ console.log(`[Dialog Server] Adding resolver to queue (current queue size: ${resolverQueue.length})`);
71
+ resolverQueue.push(resolve);
72
+
73
+ // 30秒超时
74
+ setTimeout(() => {
75
+ const index = resolverQueue.indexOf(resolve);
76
+ if (index !== -1) {
77
+ console.log('[Dialog Server] Response timeout (30s), removing resolver from queue');
78
+ resolverQueue.splice(index, 1);
79
+ resolve('');
80
+ }
81
+ }, 30000);
82
+ });
83
+ }
84
+
85
+ export function stopDialogServer() {
86
+ if (server) {
87
+ server.close();
88
+ server = null;
89
+ serverPort = null;
90
+ // 清理所有pending resolvers
91
+ resolverQueue.forEach(resolver => resolver(''));
92
+ resolverQueue = [];
93
+ console.log('[Dialog Server] Stopped');
94
+ }
95
+ }
@@ -0,0 +1,106 @@
1
+ import { showInputDialog as nativeInput, showConfirmDialog as nativeConfirm, showSelectDialog as nativeSelect, showPlanAdjustmentDialog as nativePlanAdjustment } from './native.js';
2
+ import { showInputDialog as browserInput, showConfirmDialog as browserConfirm, showSelectDialog as browserSelect, showPlanAdjustmentDialog as browserPlanAdjustment } from './browser.js';
3
+ // ✅ UNIFIED: 导入统一的常量定义,避免重复定义
4
+ import {
5
+ BASE_DIALOG_BUTTONS as BASE_BUTTONS,
6
+ STANDARD_DIALOG_BUTTONS as STD_BUTTONS,
7
+ getStandardButtons as getStandardButtonsFromConstants,
8
+ validateDialogButtons as validateDialogButtonsFromConstants
9
+ } from './constants.js';
10
+
11
+ // 🔵 UNIFIED: 重新导出统一的常量和函数,保持API兼容性
12
+ export const BASE_DIALOG_BUTTONS = BASE_BUTTONS;
13
+ export const STANDARD_DIALOG_BUTTONS = STD_BUTTONS;
14
+
15
+ function getDialogMode() {
16
+ return process.env.DIALOG_MODE || 'auto'; // 'native', 'browser', 'auto'
17
+ }
18
+
19
+ export async function showInputDialog(opts) {
20
+ // 🔵 REFACTOR: 统一按钮标准 - 输入弹窗使用默认【继续】和【修改计划】
21
+ const standardizedOpts = {
22
+ ...opts,
23
+ buttons: opts.buttons || getStandardButtons()
24
+ };
25
+
26
+ const mode = getDialogMode();
27
+ if (mode === 'browser') {
28
+ return await browserInput(standardizedOpts);
29
+ } else if (mode === 'native') {
30
+ return await nativeInput(standardizedOpts);
31
+ } else {
32
+ try {
33
+ return await nativeInput(standardizedOpts);
34
+ } catch (_) {
35
+ return await browserInput(standardizedOpts);
36
+ }
37
+ }
38
+ }
39
+
40
+ export async function showConfirmDialog(opts = {}) {
41
+ // 🔵 REFACTOR: 统一按钮标准 - 确认弹窗使用默认【继续】和【修改计划】
42
+ const standardizedOpts = {
43
+ ...opts,
44
+ buttons: opts.buttons || getStandardButtons()
45
+ };
46
+
47
+ const mode = getDialogMode();
48
+ if (mode === 'browser') {
49
+ return await browserConfirm(standardizedOpts);
50
+ } else if (mode === 'native') {
51
+ return await nativeConfirm(standardizedOpts);
52
+ } else {
53
+ try {
54
+ return await nativeConfirm(standardizedOpts);
55
+ } catch (_) {
56
+ return await browserConfirm(standardizedOpts);
57
+ }
58
+ }
59
+ }
60
+
61
+ export async function showSelectDialog(opts) {
62
+ // 🔵 REFACTOR: Task 1.1 - 应用标准按钮配置到选择弹窗
63
+ const standardizedOpts = {
64
+ ...opts,
65
+ // 对于选择弹窗,应用统一的标准按钮配置
66
+ items: getStandardButtons(opts.items)
67
+ };
68
+
69
+ const mode = getDialogMode();
70
+ if (mode === 'browser') {
71
+ return await browserSelect(standardizedOpts);
72
+ } else if (mode === 'native') {
73
+ return await nativeSelect(standardizedOpts);
74
+ } else {
75
+ try {
76
+ return await nativeSelect(standardizedOpts);
77
+ } catch (_) {
78
+ return await browserSelect(standardizedOpts);
79
+ }
80
+ }
81
+ }
82
+
83
+ export async function showPlanAdjustmentDialog(opts = {}) {
84
+ // 🔵 REFACTOR: 统一按钮标准 - 计划调整弹窗使用默认【继续】和【修改计划】
85
+ const standardizedOpts = {
86
+ ...opts,
87
+ options: opts.options || getStandardButtons()
88
+ };
89
+
90
+ const mode = getDialogMode();
91
+ if (mode === 'browser') {
92
+ return await browserPlanAdjustment(standardizedOpts);
93
+ } else if (mode === 'native') {
94
+ return await nativePlanAdjustment(standardizedOpts);
95
+ } else {
96
+ try {
97
+ return await nativePlanAdjustment(standardizedOpts);
98
+ } catch (_) {
99
+ return await browserPlanAdjustment(standardizedOpts);
100
+ }
101
+ }
102
+ }
103
+
104
+ // ✅ UNIFIED: 重新导出统一的函数,避免重复实现
105
+ export const getStandardButtons = getStandardButtonsFromConstants;
106
+ export const validateDialogButtons = validateDialogButtonsFromConstants;
@@ -0,0 +1,345 @@
1
+ import os from 'os';
2
+ import { execSync } from 'child_process';
3
+ import { escapeAppleScriptString } from './utils.js';
4
+ // 🟢 GREEN: Task 2.2 - 导入CFG配置用于native弹窗宽度设置
5
+ import { CFG } from '../config.js';
6
+ // ✅ UNIFIED: 导入统一的按钮管理常量
7
+ import { BASE_DIALOG_BUTTONS, getSafeButtons, validateDialogButtons } from './constants.js';
8
+
9
+ export async function showInputDialog({ title = '输入', message = '请输入内容:', defaultValue = '' }) {
10
+ // Handle automated testing mode - return default value without showing dialog
11
+ if (process.env.AUTOMATED_MODE === 'true') {
12
+ console.log(`🤖 [AUTOMATED] showInputDialog: ${title}, returning: "${defaultValue}"`);
13
+ return defaultValue;
14
+ }
15
+
16
+ const platform = os.platform();
17
+
18
+ // 🎯 USER-REQUIRED: 创建真正的20行高度输入框(用户明确要求)
19
+ let expandedDefaultText = defaultValue || '';
20
+
21
+ if (!expandedDefaultText) {
22
+ // 创建简洁的20行高度输入框(用户要求更空白)
23
+ const lines = [
24
+ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' // 15个空行
25
+ ];
26
+ expandedDefaultText = lines.join('\n');
27
+ } else {
28
+ // 如果有默认文本,强制扩展到20行
29
+ const lines = expandedDefaultText.split('\n');
30
+ const targetLines = Math.max(20, lines.length);
31
+ if (lines.length < targetLines) {
32
+ expandedDefaultText += '\n'.repeat(targetLines - lines.length);
33
+ }
34
+ }
35
+
36
+ // 验证行数
37
+ const actualLines = expandedDefaultText.split('\n').length;
38
+ console.log(`[Native Dialog] 📏 REAL 20-line input box: ${actualLines} lines created (target: 20 lines, CFG.nativeDialogWidth=${CFG.nativeDialogWidth}px)`);
39
+
40
+ try {
41
+ let command, args;
42
+ if (platform === 'darwin') {
43
+ // Use the improved escapeAppleScriptString from utils.js for consistency
44
+ const escapeForAppleScript = (str) => {
45
+ return escapeAppleScriptString(str);
46
+ };
47
+
48
+ const safeTitle = escapeForAppleScript(title);
49
+ const safeMessage = escapeForAppleScript(message);
50
+ const safeDefaultText = escapeForAppleScript(expandedDefaultText);
51
+
52
+ // Use optimized AppleScript to increase input box size
53
+ const applescript = `
54
+ set dialogResult to display dialog "${safeMessage}" with title "${safeTitle}" ¬
55
+ default answer "${safeDefaultText}" ¬
56
+ buttons {"确认"} ¬
57
+ default button "确认" ¬
58
+ giving up after 300
59
+
60
+ if button returned of dialogResult is "确认" then
61
+ return text returned of dialogResult
62
+ else
63
+ error "用户取消"
64
+ end if
65
+ `;
66
+
67
+ try {
68
+ console.log(`[Native Dialog] 🔄 Executing AppleScript for textarea...`);
69
+ const result = execSync(`osascript -e '${applescript}'`, {
70
+ encoding: 'utf8',
71
+ timeout: 1800000 // 30 minutes timeout (用户要求增加timeout)
72
+ }).trim();
73
+
74
+ console.log(`[Native Dialog] ✅ AppleScript returned result (length: ${result.length})`);
75
+
76
+ // Clean up the result - remove the placeholder text we added
77
+ let cleanedResult = result;
78
+ if (!defaultValue && result.includes('在此输入您的内容...')) {
79
+ cleanedResult = result.replace(/在此输入您的内容\.\.\.\n*/g, '').trim();
80
+ }
81
+
82
+ return cleanedResult;
83
+
84
+ } catch (error) {
85
+ console.log(`[Native Dialog] ⚠️ Textarea AppleScript error:`, {
86
+ message: error.message,
87
+ status: error.status,
88
+ signal: error.signal
89
+ });
90
+
91
+ // 更精确的用户取消检测
92
+ const isUserCancelled =
93
+ error.message && (
94
+ error.message.includes('User canceled') ||
95
+ error.message.includes('用户取消') ||
96
+ error.message.includes('execution error: User canceled') ||
97
+ error.message.includes('button returned:取消') ||
98
+ (error.status === 1 && error.message.includes('button returned'))
99
+ );
100
+
101
+ if (isUserCancelled) {
102
+ console.log(`[Native Dialog] 👤 User cancelled textarea dialog`);
103
+ return defaultValue;
104
+ }
105
+
106
+ console.log(`[Native Dialog] 💥 Textarea system error: ${error.message}`);
107
+ return defaultValue;
108
+ }
109
+
110
+ } else {
111
+ // Linux zenity support for multiline
112
+ // Use text-info with editable mode for multiline
113
+ const command = 'zenity';
114
+ const args = [
115
+ '--text-info',
116
+ '--editable',
117
+ '--title', title,
118
+ '--text', `${message}\n\n\n\n请输入内容(支持多行):`
119
+ ];
120
+
121
+ let result;
122
+ if (defaultValue) {
123
+ // Special handling for Linux multiline with default value
124
+ result = execSync(`echo "${expandedDefaultText || defaultValue}" | "${command}" ${args.map(a => `"${a.replace(/"/g, '\\"')}"`).join(' ')}`, {
125
+ encoding: 'utf8',
126
+ timeout: 30000
127
+ });
128
+ } else {
129
+ result = execSync(`"${command}" ${args.map(a => `"${a.replace(/"/g, '\\"')}"`).join(' ')}`, {
130
+ encoding: 'utf8',
131
+ timeout: 30000
132
+ });
133
+ }
134
+
135
+ // 清理结果 - 移除我们添加的placeholder文本
136
+ let cleanedResult = result.trim();
137
+ if (!defaultValue && cleanedResult.includes('在此输入您的内容...')) {
138
+ cleanedResult = cleanedResult.replace(/在此输入您的内容\.\.\.\n*/g, '').trim();
139
+ }
140
+
141
+ return cleanedResult;
142
+ }
143
+ } catch (error) {
144
+ console.error('❌ Dialog error:', error.message);
145
+ return defaultValue;
146
+ }
147
+ }
148
+
149
+ export async function showConfirmDialog({ title = '确认', message = '请选择:', buttons }) {
150
+ // ✅ UNIFIED: 使用统一的按钮管理系统
151
+ buttons = getSafeButtons(buttons);
152
+ console.log('🔧 [BUTTON-UNIFIED] Native dialog using unified button management:', buttons);
153
+
154
+ // Handle automated testing mode
155
+ if (process.env.AUTOMATED_MODE === 'true') {
156
+ // ✅ UNIFIED: 验证按钮包含"修改计划"选项,确保统一标准
157
+ if (!validateDialogButtons(buttons)) {
158
+ console.warn(`⚠️ [AUTOMATED] Native dialog missing '修改计划' option:`, buttons);
159
+ }
160
+ console.log(`🤖 [AUTOMATED] Native showConfirmDialog: ${title}, returning: "${buttons[0]}" (buttons: ${JSON.stringify(buttons)})`);
161
+ return buttons[0];
162
+ }
163
+
164
+ const platform = os.platform();
165
+
166
+ console.log('🖥️ [NATIVE DIALOG] Showing confirmation dialog:', { title, message, buttons, platform });
167
+
168
+ try {
169
+ let command, args;
170
+ if (platform === 'darwin') {
171
+ // Use enhanced escaping for all text content to handle special characters safely
172
+ const safeTitle = escapeAppleScriptString(title).trim() || 'Confirmation';
173
+ const safeMessage = escapeAppleScriptString(message).trim();
174
+
175
+ // Clean button text and ensure they're safe for AppleScript
176
+ const safeButtons = buttons.map(b => {
177
+ // Remove emojis from buttons but keep meaningful text
178
+ let cleanButton = b
179
+ .replace(/[\u{1F600}-\u{1F64F}]|[\u{1F300}-\u{1F5FF}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu, '')
180
+ .trim();
181
+ // 🔥 关键修复:在调用escapeAppleScriptString之前,先移除逗号(全角和半角)
182
+ // 因为escapeAppleScriptString会将全角逗号转为半角逗号,而半角逗号会破坏AppleScript按钮语法
183
+ cleanButton = cleanButton.replace(/[,,]/g, ' '); // 移除全角和半角逗号,替换为空格
184
+ return escapeAppleScriptString(cleanButton) || '确定';
185
+ });
186
+
187
+ const buttonsList = safeButtons.map(b => `"${b}"`).join(', ');
188
+ const script = `set selectedButton to button returned of (display dialog "${safeMessage}" with title "${safeTitle}" buttons {${buttonsList}} default button "${safeButtons[0]}")`;
189
+
190
+ console.log('📱 [APPLESCRIPT] Executing script...');
191
+ console.log('🔧 [DEBUG] Safe title:', safeTitle);
192
+ console.log('🔧 [DEBUG] Safe buttons:', safeButtons);
193
+ console.log('🔧 [DEBUG] Script length:', script.length);
194
+
195
+ const result = execSync(`osascript -e '${script}'`, {
196
+ encoding: 'utf8',
197
+ timeout: 1800000, // 30 minutes timeout (用户要求增加timeout)
198
+ stdio: ['pipe', 'pipe', 'pipe']
199
+ });
200
+
201
+ const selectedButton = result.trim();
202
+ console.log('✅ [USER SELECTED]:', selectedButton);
203
+
204
+ // Map safe button back to original button
205
+ const safeIndex = safeButtons.indexOf(selectedButton);
206
+ return safeIndex >= 0 ? buttons[safeIndex] : buttons[0];
207
+
208
+ } else {
209
+ // Linux zenity
210
+ console.log('🐧 [ZENITY] Showing dialog...');
211
+ const result = execSync(`zenity --question --title="${title}" --text="${message}" --ok-label="${buttons[0]}" --cancel-label="${buttons[1]}"`, {
212
+ encoding: 'utf8',
213
+ timeout: 300000
214
+ });
215
+ console.log('✅ [USER SELECTED]:', result.trim());
216
+ return result.trim() || buttons[0];
217
+ }
218
+ } catch (error) {
219
+ console.error('❌ [DIALOG ERROR]:', error.message);
220
+ console.log('⚠️ Dialog failed to display - this may indicate a system issue');
221
+
222
+ // Throw a specific error that indicates dialog system failure
223
+ throw new Error(`DIALOG_SYSTEM_FAILURE: ${error.message}`);
224
+ }
225
+ }
226
+
227
+ export async function showPlanAdjustmentDialog({ title = '', message = '', currentPlan = '', options }) { // ✅ UNIFIED: 只接收传入的选项,不设置默认值
228
+ if (!title || !message) {
229
+ throw new Error('Title and message are required for plan adjustment dialog');
230
+ }
231
+
232
+ // ✅ UNIFIED: 使用统一的按钮管理系统
233
+ options = getSafeButtons(options);
234
+ console.log('🔧 [BUTTON-UNIFIED] Native plan dialog using unified button management:', options);
235
+
236
+ // Handle automated mode
237
+ if (process.env.AUTOMATED_MODE === 'true') {
238
+ // ✅ UNIFIED: 验证选项包含"修改计划"选项,确保统一标准
239
+ if (!validateDialogButtons(options)) {
240
+ console.warn(`⚠️ [AUTOMATED] Native plan dialog missing '修改计划' option:`, options);
241
+ }
242
+ console.log(`🤖 [AUTOMATED] Native showPlanAdjustmentDialog: ${title}, returning: "${options[0]}" (options: ${JSON.stringify(options)})`);
243
+ return options[0]; // Return first option
244
+ }
245
+
246
+ const platform = os.platform();
247
+
248
+ // Enhanced message with current plan
249
+ let fullMessage = message;
250
+ if (currentPlan) {
251
+ fullMessage += `\n\n📝 当前计划:\n${currentPlan}`;
252
+ }
253
+
254
+ try {
255
+ let result;
256
+
257
+ if (platform === 'darwin') {
258
+ // Use enhanced escaping for all text content to handle special characters safely
259
+ const safeTitle = escapeAppleScriptString(title).trim() || 'Dialog';
260
+ const safeMessage = escapeAppleScriptString(fullMessage);
261
+ const safeOptions = options.map(b => {
262
+ // Remove emojis from options but keep meaningful text
263
+ let cleanOption = b
264
+ .replace(/[\u{1F600}-\u{1F64F}]|[\u{1F300}-\u{1F5FF}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu, '')
265
+ .trim();
266
+ // 🔥 关键修复:在调用escapeAppleScriptString之前,先移除逗号(全角和半角)
267
+ cleanOption = cleanOption.replace(/[,,]/g, ' '); // 移除全角和半角逗号,替换为空格
268
+ return escapeAppleScriptString(cleanOption) || '继续';
269
+ });
270
+
271
+ // Build script with properly escaped content
272
+ const scriptParts = [
273
+ 'set selectedButton to button returned of',
274
+ `(display dialog "${safeMessage}"`,
275
+ `with title "${safeTitle}"`,
276
+ `buttons {"${safeOptions.join('", "')}"} `,
277
+ `default button "${safeOptions[0]}")`
278
+ ];
279
+
280
+ const script = scriptParts.join(' ');
281
+ console.log('🔧 [DEBUG] Plan adjustment script length:', script.length);
282
+ result = execSync(`osascript -e '${script}'`, { encoding: 'utf8', timeout: 30000 }).trim();
283
+
284
+ // If "修改计划" is selected, show input dialog using improved showInputDialog
285
+ if (result.includes('修改计划')) {
286
+ // 使用改进的showInputDialog函数,支持多行输入
287
+ const inputValue = await showInputDialog({
288
+ title: '计划修改',
289
+ message: '请输入您的调整建议:',
290
+ defaultValue: '请在此输入您的修改建议或调整要求...'
291
+ });
292
+
293
+ return {
294
+ action: result,
295
+ input: inputValue
296
+ };
297
+ }
298
+
299
+ } else {
300
+ // Linux fallback using zenity
301
+ result = execSync(`zenity --list --title="${title}" --text="${fullMessage}" --column=Options ${options.map(i=>`\"${i}\"`).join(' ')}`, { encoding: 'utf8', timeout: 30000 }).trim();
302
+
303
+ // Handle input for adjustment if needed
304
+ if (result.includes('修改计划')) {
305
+ const inputValue = execSync(`zenity --text-info --editable --title="计划修改建议" --text="请输入您的调整建议:"`, { encoding: 'utf8', timeout: 300000 }).trim();
306
+
307
+ return {
308
+ action: result,
309
+ input: inputValue
310
+ };
311
+ }
312
+ }
313
+
314
+ return result;
315
+ } catch (error) {
316
+ console.warn(`Native plan adjustment dialog failed: ${error.message}, returning default option`);
317
+ return options[0];
318
+ }
319
+ }
320
+ export async function showSelectDialog({ title = '选择', message = '请选择', items = [] }) {
321
+ const platform = os.platform();
322
+ if (!items.length) return '';
323
+ try {
324
+ if (platform === 'darwin') {
325
+ // Use enhanced escaping for all text content
326
+ const safeItems = items.map(i => `\"${escapeAppleScriptString(i)}\"`).join(', ');
327
+ const safeTitle = escapeAppleScriptString(title);
328
+ const safeMessage = escapeAppleScriptString(message);
329
+ const script = `set theChoice to choose from list {${safeItems}} with title \"${safeTitle}\" with prompt \"${safeMessage}\"`;
330
+ console.log('🔧 [DEBUG] Select dialog script length:', script.length);
331
+ const result = execSync(`osascript -e "${script}"`, { encoding: 'utf8', timeout: 30000 });
332
+ return result.trim();
333
+
334
+ } else {
335
+ // linux zenity list
336
+ const result = execSync(`zenity --list --title="${title}" --column=Options ${items.map(i=>`\"${i}\"`).join(' ')}`, { encoding: 'utf8', timeout: 30000 });
337
+ return result.trim();
338
+ }
339
+ } catch (_) {
340
+ /* ignore */
341
+ }
342
+ // fallback input
343
+ return await showInputDialog({ title, message: `${message}\n输入选项:`, defaultValue: items[0] });
344
+ }
345
+