@becrafter/prompt-manager 0.1.18 → 0.1.21

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.
Files changed (70) hide show
  1. package/env.example +4 -8
  2. package/package.json +19 -9
  3. package/packages/resources/tools/cognitive-thinking/README.md +284 -0
  4. package/packages/resources/tools/cognitive-thinking/cognitive-thinking.tool.js +837 -0
  5. package/packages/server/api/admin.routes.js +19 -0
  6. package/packages/server/mcp/mcp.server.js +6 -6
  7. package/packages/server/server.js +6 -4
  8. package/packages/server/services/TerminalService.js +247 -13
  9. package/packages/server/services/WebSocketService.js +15 -5
  10. package/packages/server/toolm/tool-sync.service.js +8 -0
  11. package/packages/server/utils/config.js +24 -6
  12. package/packages/server/utils/util.js +63 -18
  13. package/packages/web/css/{main.3b61356b384d2f11f47f.css → main.196f434e6a88cd448158.css} +10 -0
  14. package/packages/web/css/terminal-fix.css +571 -0
  15. package/packages/web/index.html +1 -1
  16. package/packages/web/{main.77c2c4b553ca3fac223b.js → main.dceff50c7307dda04873.js} +2 -2
  17. package/app/desktop/assets/app.1.png +0 -0
  18. package/app/desktop/assets/app.png +0 -0
  19. package/app/desktop/assets/icons/icon.icns +0 -0
  20. package/app/desktop/assets/icons/icon.ico +0 -0
  21. package/app/desktop/assets/icons/icon.png +0 -0
  22. package/app/desktop/assets/icons/tray.png +0 -0
  23. package/app/desktop/assets/templates/about.html +0 -147
  24. package/app/desktop/assets/tray.1.png +0 -0
  25. package/app/desktop/assets/tray.png +0 -0
  26. package/app/desktop/docs/ASSETS_PLANNING.md +0 -351
  27. package/app/desktop/docs/REFACTORING_SUMMARY.md +0 -205
  28. package/app/desktop/main.js +0 -340
  29. package/app/desktop/package-lock.json +0 -6912
  30. package/app/desktop/package.json +0 -119
  31. package/app/desktop/preload.js +0 -7
  32. package/app/desktop/src/core/error-handler.js +0 -108
  33. package/app/desktop/src/core/event-emitter.js +0 -84
  34. package/app/desktop/src/core/logger.js +0 -130
  35. package/app/desktop/src/core/state-manager.js +0 -125
  36. package/app/desktop/src/services/module-loader.js +0 -330
  37. package/app/desktop/src/services/runtime-manager.js +0 -398
  38. package/app/desktop/src/services/service-manager.js +0 -210
  39. package/app/desktop/src/services/update-manager.js +0 -267
  40. package/app/desktop/src/ui/about-dialog-manager.js +0 -208
  41. package/app/desktop/src/ui/admin-window-manager.js +0 -757
  42. package/app/desktop/src/ui/splash-manager.js +0 -253
  43. package/app/desktop/src/ui/tray-manager.js +0 -186
  44. package/app/desktop/src/utils/icon-manager.js +0 -133
  45. package/app/desktop/src/utils/path-utils.js +0 -58
  46. package/app/desktop/src/utils/resource-paths.js +0 -49
  47. package/app/desktop/src/utils/resource-sync.js +0 -260
  48. package/app/desktop/src/utils/runtime-sync.js +0 -241
  49. package/app/desktop/src/utils/self-check.js +0 -288
  50. package/app/desktop/src/utils/template-renderer.js +0 -284
  51. package/app/desktop/src/utils/version-utils.js +0 -59
  52. package/packages/server/.eslintrc.js +0 -70
  53. package/packages/server/.husky/pre-commit +0 -8
  54. package/packages/server/.husky/pre-push +0 -8
  55. package/packages/server/.prettierrc +0 -14
  56. package/packages/server/dev-server.js +0 -90
  57. package/packages/server/jsdoc.conf.json +0 -39
  58. package/packages/server/package.json +0 -85
  59. package/packages/server/playwright.config.js +0 -62
  60. package/packages/server/scripts/generate-docs.js +0 -300
  61. package/packages/server/tests/e2e/terminal-e2e.test.js +0 -315
  62. package/packages/server/tests/integration/terminal-websocket.test.js +0 -372
  63. package/packages/server/tests/integration/tools.test.js +0 -264
  64. package/packages/server/tests/setup.js +0 -45
  65. package/packages/server/tests/unit/TerminalService.test.js +0 -410
  66. package/packages/server/tests/unit/WebSocketService.test.js +0 -403
  67. package/packages/server/tests/unit/core.test.js +0 -94
  68. package/packages/server/typedoc.json +0 -52
  69. package/packages/server/vitest.config.js +0 -74
  70. /package/packages/web/{main.77c2c4b553ca3fac223b.js.LICENSE.txt → main.dceff50c7307dda04873.js.LICENSE.txt} +0 -0
@@ -16,6 +16,7 @@ import {adminAuthMiddleware} from '../middlewares/auth.middleware.js'
16
16
  import { templateManager } from '../services/template.service.js';
17
17
  import { modelManager } from '../services/model.service.js';
18
18
  import { optimizationService } from '../services/optimization.service.js';
19
+ import { webSocketService } from '../services/WebSocketService.js';
19
20
 
20
21
  const router = express.Router();
21
22
 
@@ -36,6 +37,24 @@ router.get('/config', (req, res) => {
36
37
  });
37
38
  });
38
39
 
40
+ // 获取公开配置端点(无需认证)
41
+ router.get('/config/public', (req, res) => {
42
+ const publicConfig = config.getPublicConfig();
43
+
44
+ // 如果 WebSocket 服务已启动,使用实际分配的端口
45
+ if (webSocketService.isRunning) {
46
+ publicConfig.websocketPort = webSocketService.getPort();
47
+ } else {
48
+ // 如果 WebSocket 服务未启动,返回 null 表示尚未确定
49
+ publicConfig.websocketPort = null;
50
+ }
51
+
52
+ res.json({
53
+ success: true,
54
+ data: publicConfig
55
+ });
56
+ });
57
+
39
58
  // 登录端点
40
59
  router.post('/login', (req, res) => {
41
60
  // 检查是否启用了管理员功能
@@ -130,12 +130,12 @@ export const getMcpServer = async () => {
130
130
  return handleToolM(args);
131
131
  }
132
132
  },
133
- {
134
- name: 'thinking',
135
- description: `🧭 **智能思考工具箱**\n\n【规范名称】promptmanager_thinking\n【调用说明】在提示词中使用 promptmanager_thinking,实际调用时自动映射到 mcp__[server]__action\n\n⚠️ **务必先读说明**:每次首次使用某个 scenario 时,先发送仅包含 \`{"scenario":"..."}\` 的请求获取完整描述,确认理解适用场景与参数要求后,再携带 payload 调用。跳过此步骤易导致字段缺失或流程误用。\n\n## 核心特性\n\n- **双模式思考** - 提供顺序思考(exploratory)和思考规划(execution)两种模式\n- **智能引导** - 通过场景参数自动匹配合适的思考策略\n- **结构化流程** - 支持多轮思考追踪和分支管理\n- **错误预防** - 强制预读说明,避免参数配置错误\n- **灵活扩展** - 支持修订、分支和会话管理\n\n## 何时使用 Thinking Toolkit\n\n### 快速决策(IF-THEN 规则):\n- IF 需要探索性思考、诊断问题、发散推理 → 使用 scenario: "exploratory"\n- IF 需要结构化规划、制定执行步骤 → 使用 scenario: "execution"\n- IF 看到 scenario 参数 → 使用 thinking_toolkit 调用\n- IF 不确定场景用法 → 先用仅包含 scenario 的请求查看说明\n\n### 首次使用任何场景\n⚠️ **必须先发送仅包含 scenario 的请求** 阅读场景完整描述\n⚠️ 示例:thinking_toolkit with scenario: "exploratory" (无payload)\n\n## 如何使用 Thinking Toolkit\n\n### 模式 1:查看场景说明(首次使用)\n\n\`\`\`javascript\nmcp_mcp-router_thinking_toolkit({\n scenario: "exploratory"\n})\n\`\`\`\n\n**重要**:每次使用新场景前必须先执行此步骤,了解场景的具体参数要求和使用方法。\n\n### 模式 2:执行顺序思考\n\n\`\`\`javascript\nmcp_mcp-router_thinking_toolkit({\n scenario: "exploratory",\n payload: {\n thought: "分析性能下降的可能原因",\n totalThoughts: 5,\n nextThoughtNeeded: true\n }\n})\n\`\`\`\n\n### 模式 3:执行思考规划\n\n\`\`\`javascript\nmcp_mcp-router_thinking_toolkit({\n scenario: "execution",\n payload: {\n thought: "需要上线新版本",\n plan: "1. 备份 2. 部署 3. 验证",\n action: "先执行备份脚本",\n thoughtNumber: "TP-001"\n }\n})\n\`\`\`\n\n## 关键规则\n\n### ✅ 正确格式\n- 先发送 \`{"scenario": "..."}\` 获取场景说明\n- 根据说明确认参数要求后,再携带 payload 调用\n- scenario 必填:exploratory(顺序思考)或 execution(思考规划)\n- payload 根据场景填写对应字段\n\n### ❌ 常见错误\n- 不要跳过场景说明,直接携带 payload 调用(易导致参数错误)\n- 不要混用不同场景的参数字段\n- 不要在首次使用场景时直接执行 payload\n\n## 支持的思考场景\n\n### Exploratory(顺序思考)\n适合探索、诊断、发散推理场景,支持多轮思考追踪和分支管理。\n\n### Execution(思考规划)\n适合结构化计划制定和行动追踪,按步骤执行任务规划。\n\n更多场景正在开发中...`,
136
- inputSchema: thinkingToolkitInputSchema,
137
- handler: async (args) => handleThinkingToolkit(args)
138
- }
133
+ // {
134
+ // name: 'thinking',
135
+ // description: `🧭 **智能思考工具箱**\n\n【规范名称】promptmanager_thinking\n【调用说明】在提示词中使用 promptmanager_thinking,实际调用时自动映射到 mcp__[server]__action\n\n⚠️ **务必先读说明**:每次首次使用某个 scenario 时,先发送仅包含 \`{"scenario":"..."}\` 的请求获取完整描述,确认理解适用场景与参数要求后,再携带 payload 调用。跳过此步骤易导致字段缺失或流程误用。\n\n## 核心特性\n\n- **双模式思考** - 提供顺序思考(exploratory)和思考规划(execution)两种模式\n- **智能引导** - 通过场景参数自动匹配合适的思考策略\n- **结构化流程** - 支持多轮思考追踪和分支管理\n- **错误预防** - 强制预读说明,避免参数配置错误\n- **灵活扩展** - 支持修订、分支和会话管理\n\n## 何时使用 Thinking Toolkit\n\n### 快速决策(IF-THEN 规则):\n- IF 需要探索性思考、诊断问题、发散推理 → 使用 scenario: "exploratory"\n- IF 需要结构化规划、制定执行步骤 → 使用 scenario: "execution"\n- IF 看到 scenario 参数 → 使用 thinking_toolkit 调用\n- IF 不确定场景用法 → 先用仅包含 scenario 的请求查看说明\n\n### 首次使用任何场景\n⚠️ **必须先发送仅包含 scenario 的请求** 阅读场景完整描述\n⚠️ 示例:thinking_toolkit with scenario: "exploratory" (无payload)\n\n## 如何使用 Thinking Toolkit\n\n### 模式 1:查看场景说明(首次使用)\n\n\`\`\`javascript\nmcp_mcp-router_thinking_toolkit({\n scenario: "exploratory"\n})\n\`\`\`\n\n**重要**:每次使用新场景前必须先执行此步骤,了解场景的具体参数要求和使用方法。\n\n### 模式 2:执行顺序思考\n\n\`\`\`javascript\nmcp_mcp-router_thinking_toolkit({\n scenario: "exploratory",\n payload: {\n thought: "分析性能下降的可能原因",\n totalThoughts: 5,\n nextThoughtNeeded: true\n }\n})\n\`\`\`\n\n### 模式 3:执行思考规划\n\n\`\`\`javascript\nmcp_mcp-router_thinking_toolkit({\n scenario: "execution",\n payload: {\n thought: "需要上线新版本",\n plan: "1. 备份 2. 部署 3. 验证",\n action: "先执行备份脚本",\n thoughtNumber: "TP-001"\n }\n})\n\`\`\`\n\n## 关键规则\n\n### ✅ 正确格式\n- 先发送 \`{"scenario": "..."}\` 获取场景说明\n- 根据说明确认参数要求后,再携带 payload 调用\n- scenario 必填:exploratory(顺序思考)或 execution(思考规划)\n- payload 根据场景填写对应字段\n\n### ❌ 常见错误\n- 不要跳过场景说明,直接携带 payload 调用(易导致参数错误)\n- 不要混用不同场景的参数字段\n- 不要在首次使用场景时直接执行 payload\n\n## 支持的思考场景\n\n### Exploratory(顺序思考)\n适合探索、诊断、发散推理场景,支持多轮思考追踪和分支管理。\n\n### Execution(思考规划)\n适合结构化计划制定和行动追踪,按步骤执行任务规划。\n\n更多场景正在开发中...`,
136
+ // inputSchema: thinkingToolkitInputSchema,
137
+ // handler: async (args) => handleThinkingToolkit(args)
138
+ // }
139
139
  // {
140
140
  // name: 'reload_prompts',
141
141
  // description: 'Force a reload of all preset prompts to overwrite the cache.',
@@ -73,7 +73,8 @@ async function _handleConfig(options) {
73
73
  promptManager.promptsDir = promptsDir;
74
74
  await config.ensurePromptsDir();
75
75
  await util.seedPromptsIfEmpty();
76
- await util.seedBuiltInConfigsIfEmpty();
76
+ // 内置配置不需要同步到用户目录,直接从 packages/server/configs/ 读取
77
+ // await util.seedBuiltInConfigsIfEmpty();
77
78
  await config.validate();
78
79
  config.showConfig();
79
80
  }
@@ -149,10 +150,11 @@ export async function startServer(options = {}) {
149
150
  // 设置服务器实例
150
151
  serverInstance = server;
151
152
 
152
- // 启动WebSocket服务
153
+ // 启动WebSocket服务,传入 HTTP 服务器端口
153
154
  try {
154
- await webSocketService.start();
155
- logger.info(`WebSocket服务启动成功,端口: 5622`);
155
+ await webSocketService.start({ port: config.getPort() });
156
+ const wsPort = webSocketService.getPort();
157
+ logger.info(`WebSocket服务启动成功,端口: ${wsPort}`);
156
158
  } catch (wsError) {
157
159
  logger.error('WebSocket服务启动失败:', wsError.message);
158
160
  // WebSocket服务失败不影响主服务器运行
@@ -17,9 +17,10 @@ let PTY_AVAILABLE = false;
17
17
  let PTY_LOAD_ERROR = null;
18
18
 
19
19
  /**
20
- * 尝试加载 node-pty 模块
20
+ * 尝试加载 node-pty 模块,如果失败则尝试自动修复
21
21
  */
22
22
  async function tryLoadNodePty() {
23
+ logger.info('开始尝试加载 node-pty 模块...');
23
24
  try {
24
25
  const ptyModule = await import('node-pty');
25
26
  pty = ptyModule;
@@ -30,10 +31,52 @@ async function tryLoadNodePty() {
30
31
  return true;
31
32
  }
32
33
  } catch (error) {
34
+ logger.info('node-pty 模块加载失败,准备自动修复...');
33
35
  PTY_LOAD_ERROR = error;
34
36
  PTY_AVAILABLE = false;
35
- logger.warn('node-pty 模块不可用,终端功能将被禁用:', error.message);
36
- logger.warn('提示: 运行 "npm rebuild node-pty" 来修复此问题');
37
+ logger.warn('node-pty 模块不可用,尝试自动修复...');
38
+
39
+ // 尝试自动修复
40
+ try {
41
+ const { spawn } = await import('child_process');
42
+ const { promisify } = await import('util');
43
+ const exec = promisify(spawn);
44
+
45
+ logger.info('正在重新编译 node-pty...');
46
+ const rebuildProcess = spawn('npm', ['rebuild', 'node-pty'], {
47
+ stdio: 'inherit',
48
+ cwd: process.cwd()
49
+ });
50
+
51
+ await new Promise((resolve, reject) => {
52
+ rebuildProcess.on('close', (code) => {
53
+ if (code === 0) {
54
+ resolve();
55
+ } else {
56
+ reject(new Error(`npm rebuild 失败,退出码: ${code}`));
57
+ }
58
+ });
59
+ rebuildProcess.on('error', reject);
60
+ });
61
+
62
+ // 重新尝试加载
63
+ try {
64
+ const ptyModule = await import('node-pty');
65
+ pty = ptyModule;
66
+ if (pty && pty.default && pty.default.spawn) {
67
+ PTY_AVAILABLE = true;
68
+ logger.info('node-pty 模块修复成功!');
69
+ return true;
70
+ }
71
+ } catch (retryError) {
72
+ logger.error('自动修复失败,请手动运行: npm rebuild node-pty');
73
+ }
74
+ } catch (fixError) {
75
+ logger.error('自动修复过程失败:', fixError.message);
76
+ logger.error('请手动运行: npm rebuild node-pty');
77
+ }
78
+
79
+ logger.warn('终端功能将被禁用,请修复 node-pty 后再重启服务');
37
80
  return false;
38
81
  }
39
82
  return false;
@@ -192,6 +235,64 @@ export class TerminalService {
192
235
  }, 60000); // 每分钟检查一次
193
236
 
194
237
  logger.info('TerminalService initialized');
238
+
239
+ // 修复 node-pty 二进制文件权限
240
+ this.fixNodePtyPermissions();
241
+ }
242
+
243
+ /**
244
+ * 修复 node-pty 二进制文件权限
245
+ * 这是解决 posix_spawnp failed 错误的关键
246
+ */
247
+ async fixNodePtyPermissions() {
248
+ try {
249
+ const { execSync } = await import('child_process');
250
+ const platform = process.platform;
251
+
252
+ // 只在 Unix-like 系统上修复权限(macOS, Linux)
253
+ if (platform !== 'win32') {
254
+ logger.info('🔧 检查并修复 node-pty 二进制文件权限...');
255
+
256
+ // 尝试多个可能的 node-pty 路径
257
+ const possiblePaths = [
258
+ // 路径1: 在包的 node_modules 中(开发环境)
259
+ path.join(path.dirname(path.dirname(new URL(import.meta.url).pathname)), 'node_modules', 'node-pty', 'prebuilds'),
260
+ // 路径2: 在根 node_modules 中(npm 安装环境)
261
+ path.join(process.cwd(), 'node_modules', 'node-pty', 'prebuilds'),
262
+ // 路径3: 相对于当前工作目录
263
+ path.join(process.cwd(), 'node_modules', '@becrafter', 'prompt-manager', 'node_modules', 'node-pty', 'prebuilds')
264
+ ];
265
+
266
+ let ptyPath = null;
267
+ const fs = await import('fs');
268
+
269
+ for (const possiblePath of possiblePaths) {
270
+ if (fs.existsSync(possiblePath)) {
271
+ ptyPath = possiblePath;
272
+ break;
273
+ }
274
+ }
275
+
276
+ if (ptyPath) {
277
+ try {
278
+ // 添加执行权限 - 使用 find 命令来处理所有平台
279
+ execSync(`find ${ptyPath} -type f -name "*.node" -o -name "spawn-helper" | xargs chmod +x 2>/dev/null || true`, {
280
+ stdio: 'pipe',
281
+ timeout: 5000
282
+ });
283
+ logger.info('✅ node-pty 权限修复完成');
284
+ } catch (error) {
285
+ // 静默失败,不影响服务启动
286
+ logger.debug('node-pty 权限修复失败:', error.message);
287
+ }
288
+ } else {
289
+ logger.debug('未找到 node-pty prebuilds 目录,跳过权限修复');
290
+ }
291
+ }
292
+ } catch (error) {
293
+ // 静默失败,不影响服务启动
294
+ logger.debug('node-pty 权限修复失败:', error.message);
295
+ }
195
296
  }
196
297
 
197
298
  /**
@@ -252,17 +353,148 @@ export class TerminalService {
252
353
  const shell = options.shell || this.getDefaultShellForPlatform();
253
354
  const args = this.getShellArgs(shell);
254
355
  const cwd = options.workingDirectory || os.homedir();
255
- const env = { ...process.env, ...options.environment };
256
356
 
257
- logger.debug(`Creating PTY with shell: ${shell}, args: ${args.join(' ')}, cwd: ${cwd}`);
357
+ // 确保环境变量正确,特别是 PATH
358
+ const env = {
359
+ ...process.env,
360
+ ...options.environment,
361
+ // 确保 SHELL 环境变量正确设置
362
+ SHELL: shell,
363
+ // 确保 LANG 和 LC_* 变量设置
364
+ LANG: process.env.LANG || 'en_US.UTF-8',
365
+ LC_ALL: process.env.LC_ALL || process.env.LANG || 'en_US.UTF-8',
366
+ LC_CTYPE: process.env.LC_CTYPE || process.env.LANG || 'en_US.UTF-8',
367
+ // 确保 TERM 变量设置
368
+ TERM: process.env.TERM || 'xterm-256color'
369
+ };
258
370
 
259
- return pty.default.spawn(shell, args, {
260
- name: 'xterm-color',
261
- cols: options.size.cols,
262
- rows: options.size.rows,
263
- cwd: cwd,
264
- env: env
265
- });
371
+ logger.info(`🔧 创建终端会话 - Shell: ${shell}, Args: [${args.join(', ')}], CWD: ${cwd}`);
372
+ logger.info(`🔧 环境变量 - SHELL: ${env.SHELL}, TERM: ${env.TERM}, LANG: ${env.LANG}`);
373
+ logger.info(`🔧 Shell 可执行性检查: ${shell} ${args.join(' ')}`);
374
+
375
+ // 检查 shell 是否可执行
376
+ try {
377
+ const fs = await import('fs');
378
+ if (!fs.existsSync(shell)) {
379
+ throw new Error(`Shell 不存在: ${shell}`);
380
+ }
381
+
382
+ const stats = fs.statSync(shell);
383
+ if (!stats.isFile()) {
384
+ throw new Error(`Shell 不是文件: ${shell}`);
385
+ }
386
+
387
+ if (!(stats.mode & parseInt('111', 8))) {
388
+ throw new Error(`Shell 没有执行权限: ${shell}`);
389
+ }
390
+
391
+ logger.info(`✅ Shell 验证通过: ${shell}`);
392
+ } catch (error) {
393
+ logger.error(`❌ Shell 验证失败:`, error.message);
394
+ throw error;
395
+ }
396
+
397
+ // 创建 PTY 进程 - 使用多级回退机制
398
+ logger.info(`🔧 尝试创建 PTY 进程...`);
399
+
400
+ // 定义尝试策略的优先级
401
+ const strategies = [
402
+ // 策略 1: 使用用户指定的 shell 和 xterm-256color
403
+ {
404
+ name: 'User shell with xterm-256color',
405
+ shell: shell,
406
+ args: args,
407
+ term: 'xterm-256color'
408
+ },
409
+ // 策略 2: 使用用户指定的 shell 和 xterm
410
+ {
411
+ name: 'User shell with xterm',
412
+ shell: shell,
413
+ args: args,
414
+ term: 'xterm'
415
+ },
416
+ // 策略 3: 使用 /bin/sh 和 xterm-256color
417
+ {
418
+ name: '/bin/sh with xterm-256color',
419
+ shell: '/bin/sh',
420
+ args: [],
421
+ term: 'xterm-256color'
422
+ },
423
+ // 策略 4: 使用 /bin/sh 和 xterm
424
+ {
425
+ name: '/bin/sh with xterm',
426
+ shell: '/bin/sh',
427
+ args: [],
428
+ term: 'xterm'
429
+ },
430
+ // 策略 5: 使用 /bin/sh 和 ansi
431
+ {
432
+ name: '/bin/sh with ansi',
433
+ shell: '/bin/sh',
434
+ args: [],
435
+ term: 'ansi'
436
+ }
437
+ ];
438
+
439
+ let lastError = null;
440
+
441
+ for (let i = 0; i < strategies.length; i++) {
442
+ const strategy = strategies[i];
443
+
444
+ try {
445
+ logger.info(`🔄 尝试策略 ${i + 1}/${strategies.length}: ${strategy.name}`);
446
+
447
+ const ptyProcess = pty.default.spawn(strategy.shell, strategy.args, {
448
+ name: strategy.term,
449
+ cols: options.size.cols,
450
+ rows: options.size.rows,
451
+ cwd: cwd,
452
+ env: {
453
+ ...env,
454
+ TERM: strategy.term,
455
+ SHELL: strategy.shell
456
+ }
457
+ });
458
+
459
+ logger.info(`✅ PTY 进程创建成功,PID: ${ptyProcess.pid}`);
460
+ logger.info(`✅ 使用策略: ${strategy.name}`);
461
+
462
+ // 更新会话选项以反映实际使用的 shell
463
+ options.shell = strategy.shell;
464
+
465
+ return ptyProcess;
466
+
467
+ } catch (error) {
468
+ lastError = error;
469
+ logger.warn(`❌ 策略 ${i + 1} 失败: ${error.message}`);
470
+
471
+ // 继续尝试下一个策略
472
+ continue;
473
+ }
474
+ }
475
+
476
+ // 所有策略都失败了
477
+ logger.error(`❌ 所有 PTY 创建策略都失败了`);
478
+ logger.error(`❌ 最后一个错误: ${lastError?.message}`);
479
+ logger.error(`❌ 系统信息 - 平台: ${process.platform}, Node: ${process.version}`);
480
+ logger.error(`❌ 环境信息 - SHELL: ${env.SHELL}, TERM: ${env.TERM}`);
481
+ logger.error(`❌ 原始 Shell 路径: ${shell}, 参数: [${args.join(', ')}]`);
482
+ logger.error(`❌ 工作目录: ${cwd}`);
483
+
484
+ // 提供用户友好的错误信息
485
+ const error = new Error(
486
+ `终端创建失败:所有 PTY 创建策略都失败了。\n` +
487
+ `最后一个错误: ${lastError?.message}\n` +
488
+ `建议解决方案:\n` +
489
+ `1. 运行: npm rebuild node-pty\n` +
490
+ `2. 检查系统权限和 macOS 安全设置\n` +
491
+ `3. 确认 shell 路径正确: ${shell}\n` +
492
+ `4. 重启系统后再试\n` +
493
+ `5. 检查是否有其他进程占用了 PTY 资源`
494
+ );
495
+ error.code = 'TERMINAL_CREATION_FAILED';
496
+ error.originalError = lastError;
497
+ throw error;
266
498
  }
267
499
 
268
500
  /**
@@ -291,7 +523,9 @@ export class TerminalService {
291
523
  }
292
524
  return ['/c'];
293
525
  }
294
- return ['-l'];
526
+ // 不使用 -l 参数,避免 login shell 导致的 posix_spawnp 失败
527
+ // 如果需要交互式 shell,可以通过环境变量或 shell 配置来实现
528
+ return [];
295
529
  }
296
530
 
297
531
  /**
@@ -274,7 +274,7 @@ class WebSocketConnection {
274
274
  export class WebSocketService {
275
275
  constructor(options = {}) {
276
276
  this.options = {
277
- port: 5622,
277
+ port: options.port || 0, // 0 表示让系统自动分配端口,或使用外部指定的端口
278
278
  host: '0.0.0.0',
279
279
  maxConnections: 100,
280
280
  heartbeatInterval: 30000, // 30秒心跳
@@ -318,11 +318,14 @@ export class WebSocketService {
318
318
 
319
319
  this.wss.on('listening', () => {
320
320
  this.isRunning = true;
321
- logger.info(`WebSocket server listening on ${this.options.host}:${this.options.port}`);
322
-
321
+ // 获取实际分配的端口
322
+ const address = this.wss.address();
323
+ this.actualPort = typeof address === 'string' ? parseInt(address) : address.port;
324
+ logger.info(`WebSocket server listening on ${this.options.host}:${this.actualPort}`);
325
+
323
326
  // 启动心跳检测
324
327
  this.startHeartbeat();
325
-
328
+
326
329
  resolve();
327
330
  });
328
331
 
@@ -457,6 +460,13 @@ export class WebSocketService {
457
460
  return Array.from(this.connections.values());
458
461
  }
459
462
 
463
+ /**
464
+ * 获取实际分配的端口
465
+ */
466
+ getPort() {
467
+ return this.actualPort || this.options.port;
468
+ }
469
+
460
470
  /**
461
471
  * 获取服务状态
462
472
  */
@@ -467,7 +477,7 @@ export class WebSocketService {
467
477
 
468
478
  return {
469
479
  isRunning: this.isRunning,
470
- port: this.options.port,
480
+ port: this.getPort(),
471
481
  host: this.options.host,
472
482
  totalConnections: this.connections.size,
473
483
  activeConnections: activeConnections.length,
@@ -116,6 +116,14 @@ export async function syncSystemTools() {
116
116
  const sandboxToolFile = path.join(sandboxDir, `${toolName}.tool.js`);
117
117
  await fs.copyFile(toolFile, sandboxToolFile);
118
118
 
119
+ // 复制 README.md 文件(如果存在)
120
+ const readmeFile = path.join(toolDir, 'README.md');
121
+ if (await pathExists(readmeFile)) {
122
+ const sandboxReadmeFile = path.join(sandboxDir, 'README.md');
123
+ await fs.copyFile(readmeFile, sandboxReadmeFile);
124
+ logger.debug(`已复制 README.md: ${toolName}`);
125
+ }
126
+
119
127
  // 创建或更新 package.json
120
128
  const packageJsonPath = path.join(sandboxDir, 'package.json');
121
129
  let packageJson = {};
@@ -77,14 +77,12 @@ MCP Prompt Server - 智能 Prompt 管理服务器
77
77
  -v, --version 显示版本信息
78
78
 
79
79
  环境变量:
80
- MCP_SERVER_NAME 服务器名称 (默认: prompt-manager)
81
80
  SERVER_PORT 服务器端口 (默认: 5621)
82
81
  PROMPTS_DIR Prompts目录路径
83
- MCP_SERVER_VERSION 服务器版本
84
82
  LOG_LEVEL 日志级别 (默认: info)
85
83
  MAX_PROMPTS 最大prompt数量限制 (默认: 1000)
86
84
  RECURSIVE_SCAN 是否启用递归扫描子目录 (默认: true)
87
- ADMIN_ENABLE 是否启用管理员功能 (默认: true)
85
+ ADMIN_ENABLE 是否启用管理界面 (默认: true)
88
86
  ADMIN_REQUIRE_AUTH 是否需要登录认证 (默认: true)
89
87
  ADMIN_USERNAME 管理员用户名 (默认: admin)
90
88
  ADMIN_PASSWORD 管理员密码 (默认: admin)
@@ -120,8 +118,8 @@ export class Config {
120
118
  this.port = cliArgs.port || process.env.SERVER_PORT || 5621;
121
119
 
122
120
  // 其他配置
123
- this.serverName = process.env.MCP_SERVER_NAME || 'prompt-manager';
124
- this.serverVersion = process.env.MCP_SERVER_VERSION || '0.1.18';
121
+ this.serverName = 'prompt-manager';
122
+ this.serverVersion = '0.1.20';
125
123
  this.logLevel = process.env.LOG_LEVEL || 'info';
126
124
  this.maxPrompts = parseInt(process.env.MAX_PROMPTS) || 1000;
127
125
  this.recursiveScan = process.env.RECURSIVE_SCAN !== 'false'; // 默认启用递归扫描
@@ -310,6 +308,26 @@ export class Config {
310
308
  }
311
309
  }
312
310
 
311
+ /**
312
+ * 获取公开配置(前端可访问的配置信息)
313
+ */
314
+ getPublicConfig() {
315
+ return {
316
+ serverName: this.serverName,
317
+ serverVersion: this.serverVersion,
318
+ port: this.port,
319
+ adminEnable: this.adminEnable,
320
+ adminPath: this.adminPath,
321
+ websocketPort: null, // 这个会在 admin.routes.js 中动态设置
322
+ features: {
323
+ terminal: true,
324
+ optimization: true,
325
+ templates: true,
326
+ models: true
327
+ }
328
+ };
329
+ }
330
+
313
331
  /**
314
332
  * 显示当前配置(输出到 stderr,不干扰 MCP 通信)
315
333
  */
@@ -322,7 +340,7 @@ export class Config {
322
340
  process.stderr.write(` 递归扫描: ${this.recursiveScan ? '启用' : '禁用'}\n`);
323
341
  process.stderr.write(` Prompts目录: ${this.promptsDir}\n`);
324
342
  process.stderr.write(` 最大Prompt数量: ${this.maxPrompts}\n`);
325
- process.stderr.write(` 管理员功能: ${this.adminEnable ? '启用' : '禁用'}\n`);
343
+ process.stderr.write(` 管理界面: ${this.adminEnable ? '启用' : '禁用'}\n`);
326
344
  if (this.adminEnable) {
327
345
  process.stderr.write(` 登录认证: ${this.adminRequireAuth ? '需要' : '不需要'}\n`);
328
346
  }
@@ -385,39 +385,84 @@ export class Util {
385
385
  }
386
386
 
387
387
  getWebUiRoot() {
388
+ const __filename = fileURLToPath(import.meta.url);
389
+ const __dirname = path.dirname(__filename);
390
+
391
+ console.log('🔍 getWebUiRoot() called from:', __dirname);
392
+
388
393
  // 检查是否在 Electron 环境中运行
389
- const isElectron = typeof process !== 'undefined' &&
390
- process.versions &&
394
+ const isElectron = typeof process !== 'undefined' &&
395
+ process.versions &&
391
396
  process.versions.electron;
392
-
397
+
393
398
  // 检查是否是打包应用
394
399
  if (isElectron && process.resourcesPath) {
395
- // 检查是否在打包模式(在我们的应用目录下有 app.asar)
396
400
  const ourAppAsar = path.join(process.resourcesPath, 'app.asar');
397
401
  if (fs.existsSync(ourAppAsar)) {
398
- // 在打包的 Electron 应用中,web UI 位于 app.asar
399
- return path.join(process.resourcesPath, 'app.asar', 'web');
402
+ const asarPath = path.join(process.resourcesPath, 'app.asar', 'web');
403
+ console.log('📦 Using Electron ASAR path:', asarPath);
404
+ return asarPath;
400
405
  }
401
406
  }
402
-
403
- // 在开发环境中,web UI 位于项目目录中的 packages/web
404
- const __filename = fileURLToPath(import.meta.url);
405
- const __dirname = path.dirname(__filename);
406
- const devWebPath = path.join(__dirname, '..', 'web');
407
407
 
408
+ // 在开发环境中,web UI 位于项目目录中的 packages/web
409
+ const devWebPath = path.join(__dirname, '..', '..', 'web');
410
+ console.log('🔍 Checking dev path:', devWebPath);
408
411
  if (this._pathExistsSync(devWebPath)) {
412
+ console.log('✅ Using dev environment path:', devWebPath);
409
413
  return devWebPath;
410
414
  }
411
415
 
412
- // 在 npm 包环境中,优先查找 packages/web(编译后的)
413
- const projectRoot = path.resolve(__dirname, '../../..');
414
- const webPath = path.join(projectRoot, 'packages', 'web');
415
- if (this._pathExistsSync(webPath)) {
416
- return webPath;
416
+ // 在 npm 包环境中,查找 packages/web
417
+ // 从当前文件位置向上查找可能的包根目录
418
+ let currentDir = __dirname;
419
+ let searchDepth = 0;
420
+ const maxDepth = 5;
421
+
422
+ while (searchDepth < maxDepth) {
423
+ // 检查 packages/web
424
+ const webPath = path.join(currentDir, 'packages', 'web');
425
+ console.log('🔍 Checking npm package path:', webPath);
426
+ if (this._pathExistsSync(webPath)) {
427
+ console.log('✅ Found web UI in npm package:', webPath);
428
+ return webPath;
429
+ }
430
+
431
+ // 检查直接的 web 目录(某些打包场景)
432
+ const altWebPath = path.join(currentDir, 'web');
433
+ console.log('🔍 Checking alternative path:', altWebPath);
434
+ if (this._pathExistsSync(altWebPath)) {
435
+ console.log('✅ Found web UI in alternative path:', altWebPath);
436
+ return altWebPath;
437
+ }
438
+
439
+ // 向上查找
440
+ const parentDir = path.dirname(currentDir);
441
+ if (parentDir === currentDir) {
442
+ // 已经到达根目录
443
+ break;
444
+ }
445
+ currentDir = parentDir;
446
+ searchDepth++;
447
+ }
448
+
449
+ // 如果都找不到,使用当前工作目录作为最后尝试
450
+ const cwdWebPath = path.join(process.cwd(), 'packages', 'web');
451
+ console.log('🔍 Checking CWD path:', cwdWebPath);
452
+ if (this._pathExistsSync(cwdWebPath)) {
453
+ console.log('✅ Found web UI in CWD:', cwdWebPath);
454
+ return cwdWebPath;
417
455
  }
418
456
 
419
- // 返回默认路径
420
- return devWebPath;
457
+ // 最后的fallback
458
+ console.error('❌ Web UI not found in any location. This will cause blank pages.');
459
+ console.error('Current directory:', __dirname);
460
+ console.error('Working directory:', process.cwd());
461
+ console.error('Checked paths:');
462
+ console.error(' - Dev path:', devWebPath);
463
+ console.error(' - CWD path:', cwdWebPath);
464
+
465
+ return devWebPath; // 返回开发路径作为fallback,虽然它不存在
421
466
  };
422
467
 
423
468
  /**
@@ -891,6 +891,7 @@ aside {
891
891
  display: flex;
892
892
  align-items: center;
893
893
  justify-content: center;
894
+ pointer-events: none;
894
895
  }
895
896
 
896
897
  .prompt-item:hover {
@@ -991,6 +992,7 @@ aside {
991
992
  rgba(238, 238, 238, 0.6) 60%,
992
993
  rgba(238, 238, 238, 0) 100%);
993
994
  z-index: 1;
995
+ pointer-events: auto;
994
996
  }
995
997
 
996
998
  .prompt-item {
@@ -5323,6 +5325,10 @@ body .xterm.theme-dark .xterm-selection .xterm-char {
5323
5325
  transition: all 0.3s ease;
5324
5326
  }
5325
5327
 
5328
+ .recommended-prompt-modal.hidden {
5329
+ display: none !important;
5330
+ }
5331
+
5326
5332
  .recommended-prompt-modal.active {
5327
5333
  opacity: 1;
5328
5334
  visibility: visible;
@@ -5496,6 +5502,10 @@ body .xterm.theme-dark .xterm-selection .xterm-char {
5496
5502
  transition: all 0.3s ease;
5497
5503
  }
5498
5504
 
5505
+ .sync-prompt-modal.hidden {
5506
+ display: none !important;
5507
+ }
5508
+
5499
5509
  .sync-prompt-modal.active {
5500
5510
  opacity: 1;
5501
5511
  visibility: visible;