mcp-osp-prompt 1.0.2 → 1.0.3
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/dialog/native.js +51 -20
- package/package.json +1 -1
- package/server.js +3 -3
- package/tools.js +6 -7
package/dialog/native.js
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
import os from 'os';
|
|
2
|
-
import { execSync } from 'child_process';
|
|
2
|
+
import { execSync, exec } from 'child_process';
|
|
3
|
+
import { promisify } from 'util';
|
|
3
4
|
import { escapeAppleScriptString } from './utils.js';
|
|
5
|
+
|
|
6
|
+
// 使用 promisify 将 exec 转为 Promise 版本,避免阻塞事件循环
|
|
7
|
+
const execAsync = promisify(exec);
|
|
8
|
+
|
|
9
|
+
// 🔧 JSON-RPC 心跳函数:通过 stdout 发送 notification,防止 MCP 客户端超时
|
|
10
|
+
let heartbeatCounter = 0;
|
|
11
|
+
function sendHeartbeat(context = 'dialog') {
|
|
12
|
+
heartbeatCounter++;
|
|
13
|
+
|
|
14
|
+
// 尝试使用 MCP 标准的 progress notification 格式
|
|
15
|
+
const notification = {
|
|
16
|
+
jsonrpc: '2.0',
|
|
17
|
+
method: 'notifications/progress',
|
|
18
|
+
params: {
|
|
19
|
+
progressToken: `heartbeat-${Date.now()}`,
|
|
20
|
+
progress: heartbeatCounter,
|
|
21
|
+
total: -1 // -1 表示未知总数
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// 写入 stdout,这是 MCP 客户端监听的通道
|
|
26
|
+
process.stdout.write(JSON.stringify(notification) + '\n');
|
|
27
|
+
// 同时写入 stderr 供调试(只保留一行日志)
|
|
28
|
+
console.error(`[Heartbeat #${heartbeatCounter}] progress notification sent`);
|
|
29
|
+
}
|
|
4
30
|
// 🟢 GREEN: Task 2.2 - 导入CFG配置用于native弹窗宽度设置
|
|
5
31
|
import { CFG } from '../config.js';
|
|
6
32
|
// ✅ UNIFIED: 导入统一的按钮管理常量
|
|
@@ -63,16 +89,21 @@ export async function showInputDialog({ title = '输入', message = '请输入
|
|
|
63
89
|
end if
|
|
64
90
|
`;
|
|
65
91
|
|
|
66
|
-
// 🆕
|
|
92
|
+
// 🆕 添加保活机制(5秒间隔,通过 stdout 发送 JSON-RPC notification)
|
|
67
93
|
const keepAliveInterval = setInterval(() => {
|
|
68
|
-
|
|
69
|
-
},
|
|
94
|
+
sendHeartbeat('input');
|
|
95
|
+
}, 5000);
|
|
70
96
|
|
|
71
97
|
try {
|
|
72
|
-
|
|
98
|
+
console.error('[Dialog] Calling osascript for input dialog with async exec (non-blocking)...');
|
|
99
|
+
|
|
100
|
+
// 🔧 关键修改:使用异步 exec 替代 execSync,避免阻塞事件循环
|
|
101
|
+
const { stdout } = await execAsync(`osascript -e '${applescript}'`, {
|
|
73
102
|
encoding: 'utf8',
|
|
74
|
-
timeout: 1800000 // 30 minutes timeout
|
|
75
|
-
})
|
|
103
|
+
timeout: 1800000 // 30 minutes timeout
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const result = stdout.trim();
|
|
76
107
|
|
|
77
108
|
clearInterval(keepAliveInterval); // 清理定时器
|
|
78
109
|
|
|
@@ -159,13 +190,13 @@ export async function showConfirmDialog({ title = '确认', message = '请选择
|
|
|
159
190
|
|
|
160
191
|
const platform = os.platform();
|
|
161
192
|
|
|
162
|
-
// 🆕
|
|
193
|
+
// 🆕 添加保活机制(5秒间隔,通过 stdout 发送 JSON-RPC notification)
|
|
163
194
|
const keepAliveInterval = setInterval(() => {
|
|
164
|
-
|
|
165
|
-
},
|
|
195
|
+
sendHeartbeat('button selection');
|
|
196
|
+
}, 5000);
|
|
166
197
|
|
|
167
198
|
try {
|
|
168
|
-
let
|
|
199
|
+
let result;
|
|
169
200
|
if (platform === 'darwin') {
|
|
170
201
|
// Use enhanced escaping for all text content to handle special characters safely
|
|
171
202
|
const safeTitle = escapeAppleScriptString(title).trim() || 'Confirmation';
|
|
@@ -183,31 +214,31 @@ export async function showConfirmDialog({ title = '确认', message = '请选择
|
|
|
183
214
|
const buttonsList = safeButtons.map(b => `"${b}"`).join(', ');
|
|
184
215
|
const script = `set selectedButton to button returned of (display dialog "${safeMessage}" with title "${safeTitle}" buttons {${buttonsList}} default button "${safeButtons[0]}")`;
|
|
185
216
|
|
|
186
|
-
|
|
217
|
+
console.error('[Dialog] Calling osascript with async exec (non-blocking)...');
|
|
187
218
|
|
|
188
|
-
|
|
219
|
+
// 🔧 关键修改:使用异步 exec 替代 execSync,避免阻塞事件循环
|
|
220
|
+
const { stdout } = await execAsync(`osascript -e '${script}'`, {
|
|
189
221
|
encoding: 'utf8',
|
|
190
|
-
timeout:
|
|
191
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
222
|
+
timeout: 3600000 // 60 minutes timeout
|
|
192
223
|
});
|
|
193
224
|
|
|
194
225
|
clearInterval(keepAliveInterval); // 清理定时器
|
|
195
226
|
|
|
196
|
-
const selectedButton =
|
|
227
|
+
const selectedButton = stdout.trim();
|
|
197
228
|
|
|
198
229
|
// Map safe button back to original button
|
|
199
230
|
const safeIndex = safeButtons.indexOf(selectedButton);
|
|
200
231
|
return safeIndex >= 0 ? buttons[safeIndex] : buttons[0];
|
|
201
232
|
|
|
202
233
|
} else {
|
|
203
|
-
// Linux zenity
|
|
234
|
+
// Linux zenity - 也改为异步
|
|
204
235
|
console.log('🐧 [ZENITY] Showing dialog...');
|
|
205
|
-
const
|
|
236
|
+
const { stdout } = await execAsync(`zenity --question --title="${title}" --text="${message}" --ok-label="${buttons[0]}" --cancel-label="${buttons[1]}"`, {
|
|
206
237
|
encoding: 'utf8',
|
|
207
|
-
timeout:
|
|
238
|
+
timeout: 3600000 // 60 minutes timeout
|
|
208
239
|
});
|
|
209
240
|
clearInterval(keepAliveInterval); // 清理定时器
|
|
210
|
-
return
|
|
241
|
+
return stdout.trim() || buttons[0];
|
|
211
242
|
}
|
|
212
243
|
} catch (error) {
|
|
213
244
|
clearInterval(keepAliveInterval); // 出错时也要清理
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -64,10 +64,10 @@ async function initializeServerSync() {
|
|
|
64
64
|
try {
|
|
65
65
|
console.error('[MCP-Server] Starting synchronous initialization...');
|
|
66
66
|
|
|
67
|
-
// 每
|
|
67
|
+
// 每5秒输出一次日志,保持连接活跃(防止客户端超时)
|
|
68
68
|
keepAliveInterval = setInterval(() => {
|
|
69
|
-
console.error('[MCP-KeepAlive]
|
|
70
|
-
},
|
|
69
|
+
console.error('[MCP-KeepAlive] I\'m alive, Heartbeat, Heartbeat...');
|
|
70
|
+
}, 5000);
|
|
71
71
|
|
|
72
72
|
// 同步初始化prompt管理器
|
|
73
73
|
globalToolsConfig = await initializePrompts();
|
package/tools.js
CHANGED
|
@@ -478,12 +478,17 @@ export async function handleDevManual() {
|
|
|
478
478
|
}
|
|
479
479
|
|
|
480
480
|
export async function handleDevFeedback(args) {
|
|
481
|
+
console.error('[DEBUG] handleDevFeedback called with args:', JSON.stringify(args, null, 2));
|
|
482
|
+
|
|
481
483
|
// Support both old simple format and new enhanced format
|
|
482
484
|
if (args.phase || args.context || args.allowPlanAdjustment) {
|
|
485
|
+
console.error('[DEBUG] Taking enhanced feedback branch');
|
|
483
486
|
// Use enhanced feedback for new format
|
|
484
487
|
return await enhancedDevFeedback(args);
|
|
485
488
|
}
|
|
486
489
|
|
|
490
|
+
console.error('[DEBUG] Taking legacy feedback branch');
|
|
491
|
+
|
|
487
492
|
// Legacy support for old format
|
|
488
493
|
const { title, message, options = [] } = args;
|
|
489
494
|
if (process.env.AUTOMATED_MODE === 'true') {
|
|
@@ -495,10 +500,7 @@ export async function handleDevFeedback(args) {
|
|
|
495
500
|
// 简化逻辑:直接传入options,函数内部自动判断场景
|
|
496
501
|
const finalOptions = getStandardButtons(options.length > 0 ? options : null);
|
|
497
502
|
|
|
498
|
-
|
|
499
|
-
const keepAliveInterval = setInterval(() => {
|
|
500
|
-
console.error('[MCP-KeepAlive] Waiting for user dialog interaction...');
|
|
501
|
-
}, 15000);
|
|
503
|
+
console.error('[DEBUG] Calling showConfirmDialog (heartbeat is handled in native.js)...');
|
|
502
504
|
|
|
503
505
|
try {
|
|
504
506
|
const result = await showConfirmDialog({
|
|
@@ -507,8 +509,6 @@ export async function handleDevFeedback(args) {
|
|
|
507
509
|
buttons: finalOptions
|
|
508
510
|
});
|
|
509
511
|
|
|
510
|
-
clearInterval(keepAliveInterval); // 用户选择后清理定时器
|
|
511
|
-
|
|
512
512
|
// If user selects a "modify" option, automatically show input dialog
|
|
513
513
|
if (result && (result.includes('修改') || result.includes('调整') || result.includes('建议'))) {
|
|
514
514
|
|
|
@@ -530,7 +530,6 @@ export async function handleDevFeedback(args) {
|
|
|
530
530
|
}
|
|
531
531
|
return result;
|
|
532
532
|
} catch (error) {
|
|
533
|
-
clearInterval(keepAliveInterval); // 出错时也要清理定时器
|
|
534
533
|
console.error('❌ [FEEDBACK ERROR]:', error.message);
|
|
535
534
|
|
|
536
535
|
// Check if this is a dialog system failure
|