@becrafter/prompt-manager 0.1.18 → 0.1.20

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 (63) hide show
  1. package/env.example +1 -1
  2. package/package.json +19 -7
  3. package/packages/server/server.js +2 -1
  4. package/packages/server/services/TerminalService.js +247 -13
  5. package/packages/server/toolm/tool-sync.service.js +8 -0
  6. package/packages/server/utils/config.js +1 -1
  7. package/packages/web/css/{main.3b61356b384d2f11f47f.css → main.196f434e6a88cd448158.css} +10 -0
  8. package/packages/web/index.html +1 -1
  9. package/packages/web/{main.77c2c4b553ca3fac223b.js → main.b427a9e6f77a32a2f87f.js} +2 -2
  10. package/app/desktop/assets/app.1.png +0 -0
  11. package/app/desktop/assets/app.png +0 -0
  12. package/app/desktop/assets/icons/icon.icns +0 -0
  13. package/app/desktop/assets/icons/icon.ico +0 -0
  14. package/app/desktop/assets/icons/icon.png +0 -0
  15. package/app/desktop/assets/icons/tray.png +0 -0
  16. package/app/desktop/assets/templates/about.html +0 -147
  17. package/app/desktop/assets/tray.1.png +0 -0
  18. package/app/desktop/assets/tray.png +0 -0
  19. package/app/desktop/docs/ASSETS_PLANNING.md +0 -351
  20. package/app/desktop/docs/REFACTORING_SUMMARY.md +0 -205
  21. package/app/desktop/main.js +0 -340
  22. package/app/desktop/package-lock.json +0 -6912
  23. package/app/desktop/package.json +0 -119
  24. package/app/desktop/preload.js +0 -7
  25. package/app/desktop/src/core/error-handler.js +0 -108
  26. package/app/desktop/src/core/event-emitter.js +0 -84
  27. package/app/desktop/src/core/logger.js +0 -130
  28. package/app/desktop/src/core/state-manager.js +0 -125
  29. package/app/desktop/src/services/module-loader.js +0 -330
  30. package/app/desktop/src/services/runtime-manager.js +0 -398
  31. package/app/desktop/src/services/service-manager.js +0 -210
  32. package/app/desktop/src/services/update-manager.js +0 -267
  33. package/app/desktop/src/ui/about-dialog-manager.js +0 -208
  34. package/app/desktop/src/ui/admin-window-manager.js +0 -757
  35. package/app/desktop/src/ui/splash-manager.js +0 -253
  36. package/app/desktop/src/ui/tray-manager.js +0 -186
  37. package/app/desktop/src/utils/icon-manager.js +0 -133
  38. package/app/desktop/src/utils/path-utils.js +0 -58
  39. package/app/desktop/src/utils/resource-paths.js +0 -49
  40. package/app/desktop/src/utils/resource-sync.js +0 -260
  41. package/app/desktop/src/utils/runtime-sync.js +0 -241
  42. package/app/desktop/src/utils/self-check.js +0 -288
  43. package/app/desktop/src/utils/template-renderer.js +0 -284
  44. package/app/desktop/src/utils/version-utils.js +0 -59
  45. package/packages/server/.eslintrc.js +0 -70
  46. package/packages/server/.husky/pre-commit +0 -8
  47. package/packages/server/.husky/pre-push +0 -8
  48. package/packages/server/.prettierrc +0 -14
  49. package/packages/server/dev-server.js +0 -90
  50. package/packages/server/jsdoc.conf.json +0 -39
  51. package/packages/server/package.json +0 -85
  52. package/packages/server/playwright.config.js +0 -62
  53. package/packages/server/scripts/generate-docs.js +0 -300
  54. package/packages/server/tests/e2e/terminal-e2e.test.js +0 -315
  55. package/packages/server/tests/integration/terminal-websocket.test.js +0 -372
  56. package/packages/server/tests/integration/tools.test.js +0 -264
  57. package/packages/server/tests/setup.js +0 -45
  58. package/packages/server/tests/unit/TerminalService.test.js +0 -410
  59. package/packages/server/tests/unit/WebSocketService.test.js +0 -403
  60. package/packages/server/tests/unit/core.test.js +0 -94
  61. package/packages/server/typedoc.json +0 -52
  62. package/packages/server/vitest.config.js +0 -74
  63. /package/packages/web/{main.77c2c4b553ca3fac223b.js.LICENSE.txt → main.b427a9e6f77a32a2f87f.js.LICENSE.txt} +0 -0
package/env.example CHANGED
@@ -7,7 +7,7 @@ PROMPTS_DIR=$HOME/.prompt-manager/prompts
7
7
 
8
8
  # MCP服务器配置
9
9
  MCP_SERVER_NAME=prompt-manager
10
- MCP_SERVER_VERSION=0.1.18
10
+ MCP_SERVER_VERSION=0.1.20
11
11
 
12
12
  # 日志级别 (error, warn, info, debug)
13
13
  LOG_LEVEL=info
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@becrafter/prompt-manager",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "Remote MCP Server for managing prompts",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -55,7 +55,8 @@
55
55
  "format": "cd packages/server && npm run format",
56
56
  "format:check": "cd packages/server && npm run format:check",
57
57
  "check:env": "bash scripts/check-env.sh",
58
- "setup:env": "node scripts/preinstall-check.js"
58
+ "setup:env": "node scripts/preinstall-check.js",
59
+ "postinstall": "npm rebuild node-pty && chmod +x node_modules/node-pty/prebuilds/*/pty.node node_modules/node-pty/prebuilds/*/spawn-helper 2>/dev/null || true"
59
60
  },
60
61
  "keywords": [
61
62
  "mcp",
@@ -89,12 +90,23 @@
89
90
  "zod": "^3.23.8"
90
91
  },
91
92
  "files": [
92
- "app/",
93
- "packages/server/",
94
- "packages/resources/",
93
+ "app/cli/",
94
+ "packages/server/index.js",
95
+ "packages/server/server.js",
96
+ "packages/server/app.js",
97
+ "packages/server/services/*.js",
98
+ "packages/server/mcp/*.js",
99
+ "packages/server/mcp/**/*.js",
100
+ "packages/server/api/*.js",
101
+ "packages/server/utils/*.js",
102
+ "packages/server/middlewares/*.js",
103
+ "packages/server/toolm/*.js",
104
+ "packages/server/toolm/**/*.js",
105
+ "packages/server/configs/**/*.yaml",
106
+ "packages/resources/tools/**/*.js",
107
+ "packages/resources/tools/**/*.md",
95
108
  "packages/web/",
96
- "packages/configs/",
97
- "examples/",
109
+ "examples/**/*.yaml",
98
110
  "env.example",
99
111
  "README.md",
100
112
  "LICENSE"
@@ -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
  }
@@ -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
  /**
@@ -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 = {};
@@ -121,7 +121,7 @@ export class Config {
121
121
 
122
122
  // 其他配置
123
123
  this.serverName = process.env.MCP_SERVER_NAME || 'prompt-manager';
124
- this.serverVersion = process.env.MCP_SERVER_VERSION || '0.1.18';
124
+ this.serverVersion = process.env.MCP_SERVER_VERSION || '0.1.20';
125
125
  this.logLevel = process.env.LOG_LEVEL || 'info';
126
126
  this.maxPrompts = parseInt(process.env.MAX_PROMPTS) || 1000;
127
127
  this.recursiveScan = process.env.RECURSIVE_SCAN !== 'false'; // 默认启用递归扫描
@@ -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;
@@ -1,3 +1,3 @@
1
1
  <!doctype html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Prompt Manager 后台</title><link rel="stylesheet" href="./css/codemirror.css"><link rel="stylesheet" href="./css/codemirror-theme_xq-light.css"><link rel="stylesheet" href="./css/terminal-fix.css"><style>.hidden {
2
2
  display: none;
3
- }</style><script defer="defer" src="main.77c2c4b553ca3fac223b.js"></script><link href="css/main.3b61356b384d2f11f47f.css" rel="stylesheet"></head><body><div id="login-wrapper"></div><div id="main" style="display: none; height: 100vh; flex-direction: column;"><div id="header-wrapper"></div><main><div id="primary-nav-wrapper"></div><div id="sidebar-wrapper"></div><div id="tools-area-wrapper"></div><div id="terminal-area-wrapper"></div><div id="prompts-area-wrapper"></div></main></div><div id="toastContainer" class="toast-container"></div></body></html>
3
+ }</style><script defer="defer" src="main.b427a9e6f77a32a2f87f.js"></script><link href="css/main.196f434e6a88cd448158.css" rel="stylesheet"></head><body><div id="login-wrapper"></div><div id="main" style="display: none; height: 100vh; flex-direction: column;"><div id="header-wrapper"></div><main><div id="primary-nav-wrapper"></div><div id="sidebar-wrapper"></div><div id="tools-area-wrapper"></div><div id="terminal-area-wrapper"></div><div id="prompts-area-wrapper"></div></main></div><div id="toastContainer" class="toast-container"></div></body></html>