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 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
- console.error('[Dialog-KeepAlive] Waiting for user input in textarea...');
69
- }, 15000);
94
+ sendHeartbeat('input');
95
+ }, 5000);
70
96
 
71
97
  try {
72
- const result = execSync(`osascript -e '${applescript}'`, {
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 (用户要求增加timeout)
75
- }).trim();
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
- console.error('[Dialog-KeepAlive] Waiting for user to select button...');
165
- }, 15000);
195
+ sendHeartbeat('button selection');
196
+ }, 5000);
166
197
 
167
198
  try {
168
- let command, args;
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
- // Removed: DEBUG logs with array/object output (causes JSON parse errors in MCP client)
217
+ console.error('[Dialog] Calling osascript with async exec (non-blocking)...');
187
218
 
188
- const result = execSync(`osascript -e '${script}'`, {
219
+ // 🔧 关键修改:使用异步 exec 替代 execSync,避免阻塞事件循环
220
+ const { stdout } = await execAsync(`osascript -e '${script}'`, {
189
221
  encoding: 'utf8',
190
- timeout: 1800000, // 30 minutes timeout (用户要求增加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 = result.trim();
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 result = execSync(`zenity --question --title="${title}" --text="${message}" --ok-label="${buttons[0]}" --cancel-label="${buttons[1]}"`, {
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: 300000
238
+ timeout: 3600000 // 60 minutes timeout
208
239
  });
209
240
  clearInterval(keepAliveInterval); // 清理定时器
210
- return result.trim() || buttons[0];
241
+ return stdout.trim() || buttons[0];
211
242
  }
212
243
  } catch (error) {
213
244
  clearInterval(keepAliveInterval); // 出错时也要清理
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-osp-prompt",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "MCP server for fetching and caching prompt templates from GitHub, GitLab, or local directories",
5
5
  "main": "server.js",
6
6
  "bin": {
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
- // 每10秒输出一次日志,保持连接活跃
67
+ // 每5秒输出一次日志,保持连接活跃(防止客户端超时)
68
68
  keepAliveInterval = setInterval(() => {
69
- console.error('[MCP-KeepAlive] Initializing tools, please wait...');
70
- }, 10000);
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