@becrafter/prompt-manager 0.0.18 → 0.1.1

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 (108) hide show
  1. package/IFLOW.md +175 -0
  2. package/README.md +145 -234
  3. package/app/desktop/assets/app.1.png +0 -0
  4. package/app/desktop/assets/app.png +0 -0
  5. package/app/desktop/assets/icons/icon.icns +0 -0
  6. package/app/desktop/assets/icons/icon.ico +0 -0
  7. package/app/desktop/assets/icons/icon.png +0 -0
  8. package/app/desktop/assets/icons/tray.png +0 -0
  9. package/app/desktop/assets/templates/about.html +147 -0
  10. package/app/desktop/assets/tray.png +0 -0
  11. package/app/desktop/main.js +187 -732
  12. package/app/desktop/package-lock.json +723 -522
  13. package/app/desktop/package.json +54 -25
  14. package/app/desktop/preload.js +7 -0
  15. package/app/desktop/src/core/error-handler.js +108 -0
  16. package/app/desktop/src/core/event-emitter.js +84 -0
  17. package/app/desktop/src/core/logger.js +108 -0
  18. package/app/desktop/src/core/state-manager.js +125 -0
  19. package/app/desktop/src/services/module-loader.js +214 -0
  20. package/app/desktop/src/services/runtime-manager.js +301 -0
  21. package/app/desktop/src/services/service-manager.js +169 -0
  22. package/app/desktop/src/services/update-manager.js +268 -0
  23. package/app/desktop/src/ui/about-dialog-manager.js +208 -0
  24. package/app/desktop/src/ui/admin-window-manager.js +757 -0
  25. package/app/desktop/src/ui/splash-manager.js +253 -0
  26. package/app/desktop/src/ui/tray-manager.js +186 -0
  27. package/app/desktop/src/utils/icon-manager.js +133 -0
  28. package/app/desktop/src/utils/path-utils.js +58 -0
  29. package/app/desktop/src/utils/resource-paths.js +49 -0
  30. package/app/desktop/src/utils/resource-sync.js +260 -0
  31. package/app/desktop/src/utils/runtime-sync.js +241 -0
  32. package/app/desktop/src/utils/template-renderer.js +284 -0
  33. package/app/desktop/src/utils/version-utils.js +59 -0
  34. package/examples/prompts/engineer/engineer-professional.yaml +92 -0
  35. package/examples/prompts/engineer/laowang-engineer.yaml +132 -0
  36. package/examples/prompts/engineer/nekomata-engineer.yaml +123 -0
  37. package/examples/prompts/engineer/ojousama-engineer.yaml +124 -0
  38. package/examples/prompts/recommend/human_3-0_growth_diagnostic_coach_prompt.yaml +105 -0
  39. package/examples/prompts/workflow/sixstep-workflow.yaml +192 -0
  40. package/package.json +18 -9
  41. package/packages/admin-ui/.babelrc +3 -0
  42. package/packages/admin-ui/admin.html +237 -4784
  43. package/packages/admin-ui/css/main.css +2592 -0
  44. package/packages/admin-ui/css/recommended-prompts.css +610 -0
  45. package/packages/admin-ui/package-lock.json +6981 -0
  46. package/packages/admin-ui/package.json +36 -0
  47. package/packages/admin-ui/src/codemirror.js +53 -0
  48. package/packages/admin-ui/src/index.js +3188 -0
  49. package/packages/admin-ui/webpack.config.js +76 -0
  50. package/packages/resources/tools/chrome-devtools/README.md +310 -0
  51. package/packages/resources/tools/chrome-devtools/chrome-devtools.tool.js +1703 -0
  52. package/packages/resources/tools/file-reader/README.md +289 -0
  53. package/packages/resources/tools/file-reader/file-reader.tool.js +1545 -0
  54. package/packages/resources/tools/filesystem/README.md +359 -0
  55. package/packages/resources/tools/filesystem/filesystem.tool.js +538 -0
  56. package/packages/resources/tools/ollama-remote/README.md +192 -0
  57. package/packages/resources/tools/ollama-remote/ollama-remote.tool.js +421 -0
  58. package/packages/resources/tools/pdf-reader/README.md +236 -0
  59. package/packages/resources/tools/pdf-reader/pdf-reader.tool.js +565 -0
  60. package/packages/resources/tools/playwright/README.md +306 -0
  61. package/packages/resources/tools/playwright/playwright.tool.js +1186 -0
  62. package/packages/resources/tools/todolist/README.md +394 -0
  63. package/packages/resources/tools/todolist/todolist.tool.js +1312 -0
  64. package/packages/server/README.md +142 -0
  65. package/packages/server/api/admin.routes.js +42 -11
  66. package/packages/server/api/surge.routes.js +43 -0
  67. package/packages/server/app.js +119 -14
  68. package/packages/server/index.js +39 -0
  69. package/packages/server/mcp/mcp.server.js +346 -28
  70. package/packages/server/mcp/{mcp.handler.js → prompt.handler.js} +108 -9
  71. package/packages/server/mcp/sequential-thinking.handler.js +318 -0
  72. package/packages/server/mcp/think-plan.handler.js +274 -0
  73. package/packages/server/middlewares/auth.middleware.js +6 -0
  74. package/packages/server/package.json +51 -0
  75. package/packages/server/server.js +37 -1
  76. package/packages/server/toolm/index.js +9 -0
  77. package/packages/server/toolm/package-installer.service.js +267 -0
  78. package/packages/server/toolm/test-tools.js +264 -0
  79. package/packages/server/toolm/tool-context.service.js +334 -0
  80. package/packages/server/toolm/tool-dependency.service.js +168 -0
  81. package/packages/server/toolm/tool-description-generator-optimized.service.js +375 -0
  82. package/packages/server/toolm/tool-description-generator.service.js +312 -0
  83. package/packages/server/toolm/tool-environment.service.js +200 -0
  84. package/packages/server/toolm/tool-execution.service.js +277 -0
  85. package/packages/server/toolm/tool-loader.service.js +219 -0
  86. package/packages/server/toolm/tool-logger.service.js +223 -0
  87. package/packages/server/toolm/tool-manager.handler.js +65 -0
  88. package/packages/server/toolm/tool-manual-generator.service.js +389 -0
  89. package/packages/server/toolm/tool-mode-handlers.service.js +224 -0
  90. package/packages/server/toolm/tool-storage.service.js +111 -0
  91. package/packages/server/toolm/tool-sync.service.js +138 -0
  92. package/packages/server/toolm/tool-utils.js +20 -0
  93. package/packages/server/toolm/tool-yaml-parser.service.js +81 -0
  94. package/packages/server/toolm/validate-system.js +421 -0
  95. package/packages/server/utils/config.js +49 -5
  96. package/packages/server/utils/util.js +65 -10
  97. package/scripts/build-icons.js +135 -0
  98. package/scripts/build.sh +57 -0
  99. package/scripts/surge/CNAME +1 -0
  100. package/scripts/surge/README.md +47 -0
  101. package/scripts/surge/package-lock.json +34 -0
  102. package/scripts/surge/package.json +20 -0
  103. package/scripts/surge/sync-to-surge.js +151 -0
  104. package/packages/admin-ui/js/closebrackets.min.js +0 -8
  105. package/packages/admin-ui/js/codemirror.min.js +0 -8
  106. package/packages/admin-ui/js/js-yaml.min.js +0 -2
  107. package/packages/admin-ui/js/markdown.min.js +0 -8
  108. /package/app/desktop/assets/{icon.png → tray.1.png} +0 -0
@@ -1,786 +1,241 @@
1
- const electron = require('electron');
2
- const { app, BrowserWindow, Tray, Menu, clipboard, dialog, nativeImage, shell } = electron;
3
- const path = require('path');
4
- const fs = require('fs');
5
- const os = require('os');
6
- const { pathToFileURL } = require('url');
7
- const tar = require('tar');
8
- const { spawn } = require('child_process');
9
-
10
- // 设置日志文件
11
- const logFilePath = path.join(app.getPath('userData'), 'prompt-manager-desktop.log');
12
- let logStream;
13
-
14
- // 初始化日志流
15
- function initLogStream() {
16
- try {
17
- // 确保日志目录存在
18
- const logDir = path.dirname(logFilePath);
19
- if (!fs.existsSync(logDir)) {
20
- fs.mkdirSync(logDir, { recursive: true });
21
- }
22
-
23
- // 创建或追加到日志文件
24
- logStream = fs.createWriteStream(logFilePath, { flags: 'a' });
25
- console.log = function(...args) {
26
- const message = `[${new Date().toISOString()}] [INFO] ${args.join(' ')}\n`;
27
- process.stdout.write(message);
28
- if (logStream) {
29
- logStream.write(message);
30
- }
31
- };
32
-
33
- console.error = function(...args) {
34
- const message = `[${new Date().toISOString()}] [ERROR] ${args.join(' ')}\n`;
35
- process.stderr.write(message);
36
- if (logStream) {
37
- logStream.write(message);
38
- }
39
- };
40
-
41
- console.warn = function(...args) {
42
- const message = `[${new Date().toISOString()}] [WARN] ${args.join(' ')}\n`;
43
- process.stdout.write(message);
44
- if (logStream) {
45
- logStream.write(message);
46
- }
47
- };
48
-
49
- console.debug = function(...args) {
50
- const message = `[${new Date().toISOString()}] [DEBUG] ${args.join(' ')}\n`;
51
- process.stdout.write(message);
52
- if (logStream) {
53
- logStream.write(message);
54
- }
55
- };
56
-
57
- console.log(`Logging initialized. Log file: ${logFilePath}`);
58
- } catch (error) {
59
- console.error('Failed to initialize logging:', error);
60
- }
61
- }
1
+ /**
2
+ * 应用主入口
3
+ * 重构后的主程序,遵循SRP、KISS、DRY、YAGNI原则
4
+ */
62
5
 
63
- let tray = null;
64
- let adminWindow = null;
65
- let serviceState = 'stopped';
66
- let currentServerState = null;
67
- let serverModule = null;
68
- let serverModuleVersion = 0;
69
- let serverModuleLoading = null;
70
- let isQuitting = false;
71
- let runtimeServerRoot = null;
72
- let startFailureCount = 0;
73
-
74
- const desktopPackageJson = JSON.parse(
75
- fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')
76
- );
77
-
78
- function resolveBaseServerRoot() {
79
- return path.resolve(__dirname, '..', '..');
80
- }
81
-
82
- async function ensureRuntimeServerRoot() {
83
- if (runtimeServerRoot) {
84
- console.log('Using cached runtimeServerRoot:', runtimeServerRoot);
85
- return runtimeServerRoot;
86
- }
87
-
88
- if (!app.isPackaged) {
89
- console.log('App is not packaged, using development server root');
90
- runtimeServerRoot = resolveBaseServerRoot();
91
- console.log('Development server root:', runtimeServerRoot);
92
- return runtimeServerRoot;
93
- }
6
+ const { app, BrowserWindow, ipcMain } = require('electron');
7
+ const path = require('path');
94
8
 
95
- console.log('App is packaged, setting up runtime server root');
96
- console.log('process.resourcesPath:', process.resourcesPath);
97
- console.log('app.getPath("userData"):', app.getPath('userData'));
98
-
99
- const packagedRoot = path.join(process.resourcesPath, 'prompt-manager');
100
- const runtimeRoot = path.join(app.getPath('userData'), 'prompt-manager');
101
-
102
- console.log('packagedRoot:', packagedRoot);
103
- console.log('runtimeRoot:', runtimeRoot);
9
+ // 导入管理器模块
10
+ const AppState = require('./src/core/state-manager');
11
+ const Logger = require('./src/core/logger');
12
+ const ErrorHandler = require('./src/core/error-handler');
13
+ const RuntimeManager = require('./src/services/runtime-manager');
14
+ const ModuleLoader = require('./src/services/module-loader');
15
+ const ServiceManager = require('./src/services/service-manager');
16
+ const TrayManager = require('./src/ui/tray-manager');
17
+ const UpdateManager = require('./src/services/update-manager');
18
+ const AboutDialogManager = require('./src/ui/about-dialog-manager');
19
+ const SplashManager = require('./src/ui/splash-manager');
20
+ const IconManager = require('./src/utils/icon-manager');
21
+ const RuntimeSync = require('./src/utils/runtime-sync');
22
+
23
+ class PromptManagerApp {
24
+ constructor() {
25
+ this.stateManager = new AppState();
26
+ this.logger = new Logger({ debugEnabled: false });
27
+ this.errorHandler = new ErrorHandler(this.logger);
28
+ this.runtimeManager = new RuntimeManager(this.logger, this.errorHandler);
29
+ this.moduleLoader = new ModuleLoader(this.logger, this.errorHandler);
30
+ this.iconManager = new IconManager();
31
+ this.serviceManager = new ServiceManager(this.logger, this.errorHandler, this.moduleLoader);
32
+ this.updateManager = new UpdateManager(this.logger, this.errorHandler, this.runtimeManager);
33
+ this.aboutDialogManager = new AboutDialogManager(this.logger, this.runtimeManager, this.iconManager);
34
+ this.trayManager = new TrayManager(this.logger, this.errorHandler, this.iconManager);
35
+ this.splashManager = new SplashManager(this.logger, this.iconManager);
36
+
37
+ this.isInitialized = false;
38
+ }
39
+
40
+ async initialize() {
41
+ if (this.isInitialized) return;
104
42
 
105
- try {
106
- // 检查打包的资源目录是否存在
107
43
  try {
108
- await fs.promises.access(packagedRoot, fs.constants.F_OK);
109
- console.log('Packaged root exists');
110
- } catch (error) {
111
- console.error('Packaged root does not exist:', packagedRoot);
112
- throw new Error(`Packaged root not found: ${packagedRoot}`);
113
- }
114
-
115
- // 检查运行时目录是否存在
116
- let needsInstall = false;
117
- try {
118
- await fs.promises.access(runtimeRoot, fs.constants.F_OK);
119
- console.log('Runtime root already exists');
44
+ this.logger.info('Initializing Prompt Manager Desktop Application');
45
+
46
+ // 显示启动画面
47
+ await this.splashManager.showSplash();
48
+ this.splashManager.updateStatus('正在初始化...', 20);
49
+
50
+ // 同步环境配置
51
+ await RuntimeSync.syncRuntime();
52
+ this.splashManager.updateStatus('正在同步工具箱...', 35);
53
+
54
+ // 初始化日志系统
55
+ await this.logger.initialize();
56
+
57
+ // 设置应用菜单
58
+ this.setupApplicationMenu();
59
+
60
+ // 初始化系统托盘
61
+ this.splashManager.updateStatus('正在启动托盘服务...', 60);
62
+ await this.trayManager.initialize(this.stateManager);
63
+
64
+ // 设置事件监听
65
+ this.setupEventListeners();
66
+
67
+ // 确保运行时环境
68
+ this.splashManager.updateStatus('正在准备运行时环境...', 70);
69
+ const serverRoot = await this.runtimeManager.ensureRuntimeEnvironment();
70
+ this.stateManager.set('runtimeRoot', serverRoot);
71
+
72
+ // 启动服务
73
+ this.splashManager.updateStatus('正在启动核心服务...', 80);
74
+ await this.startService();
75
+
76
+ this.isInitialized = true;
77
+ this.logger.info('Application initialized successfully');
78
+
79
+ // 关闭启动画面
80
+ this.splashManager.updateStatus('准备就绪...', 100);
81
+ setTimeout(() => {
82
+ this.splashManager.closeSplash();
83
+ }, 500); // 短暂延迟以显示完成状态
120
84
 
121
- // 检查是否需要安装依赖
122
- try {
123
- await fs.promises.access(path.join(runtimeRoot, 'node_modules'), fs.constants.F_OK);
124
- console.log('node_modules already exists in runtime root');
125
- } catch (error) {
126
- console.log('node_modules not found in runtime root, will install dependencies');
127
- needsInstall = true;
128
- }
129
85
  } catch (error) {
130
- console.log('Runtime root does not exist, creating and copying files');
131
- await fs.promises.mkdir(runtimeRoot, { recursive: true });
132
- console.log('Created runtime root directory');
86
+ this.logger.error('Application initialization failed', error);
133
87
 
134
- // 复制文件并显示进度
135
- console.log('Copying files from packaged root to runtime root...');
136
- await fs.promises.cp(packagedRoot, runtimeRoot, { recursive: true });
137
- console.log('File copy completed');
138
- needsInstall = true;
139
- }
140
-
141
- // 如果需要安装依赖
142
- if (needsInstall) {
143
- console.log('Installing dependencies in runtime root');
144
- try {
145
- await installServerDependencies(runtimeRoot);
146
- console.log('Dependencies installed successfully');
147
- } catch (error) {
148
- console.error('Failed to install dependencies:', error);
149
- dialog.showErrorBox('依赖安装失败', `无法安装服务器依赖: ${error.message}\n\n请查看日志文件: ${logFilePath}`);
150
- throw error;
151
- }
88
+ // 关闭启动画面
89
+ this.splashManager.closeSplash();
90
+
91
+ this.errorHandler.handleError('APP_INIT_FAILED', error, {
92
+ logFilePath: this.logger.getLogFilePath()
93
+ });
94
+
95
+ throw error;
152
96
  }
153
- } catch (error) {
154
- console.error('Error in ensureRuntimeServerRoot:', error);
155
- dialog.showErrorBox('服务器初始化失败', `无法设置服务器运行环境: ${error.message}\n\n请查看日志文件: ${logFilePath}`);
156
- throw error;
157
- }
158
-
159
- runtimeServerRoot = runtimeRoot;
160
- console.log('Final runtimeServerRoot:', runtimeServerRoot);
161
- return runtimeServerRoot;
162
- }
163
-
164
- async function loadServerModule(options = {}) {
165
- console.log('loadServerModule called with options:', options);
166
- console.log('Current serviceState:', serviceState);
167
- console.log('serverModule exists:', !!serverModule);
168
- console.log('serverModuleLoading exists:', !!serverModuleLoading);
169
-
170
- // 如果强制重新加载或服务状态异常,则清理缓存
171
- if (options.forceReload || serviceState === 'error') {
172
- console.log('Forcing module reload, clearing cache');
173
- serverModule = null;
174
- serverModuleVersion += 1;
175
- serverModuleLoading = null;
176
- }
177
-
178
- if (serverModule) {
179
- console.log('Returning cached server module');
180
- return serverModule;
181
97
  }
182
98
 
183
- if (serverModuleLoading) {
184
- console.log('Module loading in progress, returning promise');
185
- return serverModuleLoading;
186
- }
187
-
188
- try {
189
- const serverRoot = await ensureRuntimeServerRoot();
190
- console.log('Server root resolved to:', serverRoot);
191
-
192
- const serverEntry = path.join(serverRoot, 'packages', 'server', 'server.js');
193
- console.log('Server entry file:', serverEntry);
194
-
195
- // 检查入口文件是否存在
196
- try {
197
- await fs.promises.access(serverEntry, fs.constants.F_OK);
198
- console.log('Server entry file exists');
199
- } catch (error) {
200
- console.error('Server entry file does not exist:', serverEntry);
201
- throw new Error(`Server entry file not found: ${serverEntry}`);
99
+ setupApplicationMenu() {
100
+ if (process.platform === 'darwin') {
101
+ app.dock.hide();
202
102
  }
203
103
 
204
- const entryUrl = pathToFileURL(serverEntry);
205
- entryUrl.searchParams.set('v', String(serverModuleVersion));
206
- console.log('Entry URL:', entryUrl.href);
207
-
208
- serverModuleLoading = import(entryUrl.href)
209
- .then((mod) => {
210
- console.log('Module loaded successfully');
211
- serverModule = mod;
212
- return mod;
213
- })
214
- .catch((error) => {
215
- console.error('Module loading failed:', error);
216
- // 清理加载状态,以便下次重试
217
- serverModuleLoading = null;
218
- serverModule = null;
219
- throw new Error(`Failed to load server module: ${error.message}`);
220
- })
221
- .finally(() => {
222
- serverModuleLoading = null;
223
- });
224
-
225
- return serverModuleLoading;
226
- } catch (error) {
227
- console.error('Error in loadServerModule:', error);
228
- dialog.showErrorBox('模块加载失败', error.message);
229
- throw error;
104
+ // 隐藏默认应用菜单
105
+ const { Menu } = require('electron');
106
+ Menu.setApplicationMenu(null);
230
107
  }
231
- }
232
108
 
233
- function getServerStatusLabel() {
234
- switch (serviceState) {
235
- case 'running':
236
- return '运行中';
237
- case 'starting':
238
- return '启动中';
239
- case 'stopping':
240
- return '停止中';
241
- case 'error':
242
- return '启动失败';
243
- default:
244
- return '已停止';
245
- }
246
- }
247
-
248
- async function startService() {
249
- console.log('startService called');
250
- // 如果正在运行或正在启动,直接返回
251
- if (serviceState === 'running' || serviceState === 'starting') {
252
- console.log('Service is already running or starting, returning');
253
- return;
254
- }
255
-
256
- serviceState = 'starting';
257
- console.log('Service state set to starting');
258
- refreshTrayMenu();
259
-
260
- try {
261
- console.log('Loading server module with force reload');
262
- // 强制重新加载模块以确保获取最新状态
263
- const module = await loadServerModule({ forceReload: true });
264
- console.log('Server module loaded, starting server');
109
+ setupEventListeners() {
110
+ // 托盘事件
111
+ this.trayManager.on('service-start-requested', () => this.startService());
112
+ this.trayManager.on('service-stop-requested', () => this.stopService());
113
+ this.trayManager.on('update-check-requested', () => this.checkForUpdates());
114
+ this.trayManager.on('about-dialog-requested', () => this.showAboutDialog());
115
+ this.trayManager.on('quit-requested', () => this.quitApplication());
265
116
 
266
- if (!module || typeof module.startServer !== 'function') {
267
- throw new Error('Invalid server module or missing startServer function');
268
- }
117
+ // 服务管理器事件
118
+ this.serviceManager.on('status-update', () => this.trayManager.updateMenu());
119
+ this.serviceManager.on('restart-requested', () => this.restartApplication());
269
120
 
270
- await module.startServer();
271
- console.log('Server started successfully');
272
- currentServerState = module.getServerState();
273
- serviceState = 'running';
274
- startFailureCount = 0; // 重置失败计数
275
- console.log('Service state set to running');
276
- } catch (error) {
277
- serviceState = 'error';
278
- currentServerState = null;
279
- startFailureCount++;
121
+ // IPC事件
122
+ ipcMain.on('about-window-click', (event, data) => {
123
+ this.aboutDialogManager.handleAboutWindowClick(data);
124
+ });
280
125
 
281
- console.error('服务启动失败:', error);
282
- console.error('Error stack:', error.stack);
126
+ // 应用事件
127
+ app.on('window-all-closed', (event) => {
128
+ event.preventDefault(); // 防止应用退出
129
+ });
283
130
 
284
- // 如果连续失败3次,提示用户重启应用
285
- if (startFailureCount >= 3) {
286
- console.log('Too many start failures, prompting for restart');
287
- await promptForRestart();
288
- } else {
289
- dialog.showErrorBox('服务启动失败', `${error?.message || String(error)}\n\n请查看控制台日志获取更多信息。`);
290
- }
291
- } finally {
292
- console.log('Refreshing tray menu');
293
- refreshTrayMenu();
294
- }
295
- }
296
-
297
- async function stopService() {
298
- // 如果服务未运行或正在停止,直接返回
299
- if (serviceState !== 'running' && serviceState !== 'error') {
300
- return;
301
- }
302
-
303
- serviceState = 'stopping';
304
- refreshTrayMenu();
305
-
306
- try {
307
- const module = await loadServerModule();
308
- if (module && typeof module.stopServer === 'function') {
309
- await module.stopServer();
310
- }
311
- currentServerState = null;
312
- serviceState = 'stopped';
131
+ app.on('before-quit', async (event) => {
132
+ if (this.stateManager.get('isQuitting')) return;
133
+
134
+ event.preventDefault();
135
+ await this.quitApplication();
136
+ });
313
137
 
314
- // 清理MCP会话和传输
315
- console.log('Clearing MCP sessions and transports');
316
- } catch (error) {
317
- console.error('停止服务失败:', error);
318
- dialog.showErrorBox('停止服务失败', error?.message || String(error));
319
- serviceState = 'error';
320
- } finally {
321
- refreshTrayMenu();
322
- }
323
- }
324
-
325
- function ensureTray() {
326
- if (tray) {
327
- return tray;
138
+ app.on('activate', () => {
139
+ if (!this.trayManager.tray) {
140
+ this.trayManager.initialize(this.stateManager);
141
+ }
142
+ });
328
143
  }
329
144
 
330
- // 尝试多种可能的图标路径
331
- const possiblePaths = [
332
- path.join(__dirname, 'assets', 'icon.png'),
333
- path.join(process.resourcesPath, 'assets', 'icon.png'),
334
- path.join(__dirname, '..', 'assets', 'icon.png')
335
- ];
336
-
337
- let iconPath = null;
338
- let icon = null;
339
-
340
- console.log('App is packaged:', app.isPackaged);
341
- console.log('__dirname:', __dirname);
342
- console.log('process.resourcesPath:', process.resourcesPath);
343
-
344
- for (const possiblePath of possiblePaths) {
345
- console.log('Checking icon path:', possiblePath);
346
- console.log('File exists:', fs.existsSync(possiblePath));
347
- if (fs.existsSync(possiblePath)) {
348
- iconPath = possiblePath;
349
- break;
145
+ async startService() {
146
+ const serverRoot = this.stateManager.get('runtimeRoot');
147
+ if (!serverRoot) {
148
+ this.logger.error('Cannot start service: runtime root not set');
149
+ return false;
350
150
  }
351
- }
352
-
353
- if (!iconPath) {
354
- console.error('Icon file not found in any of the expected locations');
355
- // 创建一个简单的文本托盘作为后备
356
- tray = new Tray(nativeImage.createEmpty());
357
- tray.setToolTip('Prompt Server - Icon Missing');
358
- refreshTrayMenu();
359
- return tray;
360
- }
361
-
362
- console.log('Attempting to load icon from:', iconPath);
363
-
364
- try {
365
- icon = nativeImage.createFromPath(iconPath);
366
- console.log('Icon loaded successfully');
367
- console.log('Icon is empty:', icon.isEmpty());
368
- console.log('Icon size:', icon.getSize());
369
151
 
370
- if (process.platform === 'darwin') {
371
- icon = icon.resize({ width: 18, height: 18 });
372
- icon.setTemplateImage(true);
373
- console.log('Icon resized and set as template image');
374
- }
375
-
376
- tray = new Tray(icon);
377
- tray.setToolTip('Prompt Server');
378
- console.log('Tray created successfully');
379
- refreshTrayMenu();
380
- return tray;
381
- } catch (error) {
382
- console.error('Failed to create tray:', error);
383
- // 创建一个简单的文本托盘作为后备
384
- tray = new Tray(nativeImage.createEmpty());
385
- tray.setToolTip('Prompt Server - Error');
386
- refreshTrayMenu();
387
- return tray;
152
+ return await this.serviceManager.startService(serverRoot, this.stateManager);
388
153
  }
389
- }
390
154
 
391
- function refreshTrayMenu() {
392
- if (!tray) {
393
- return;
155
+ async stopService() {
156
+ return await this.serviceManager.stopService(this.stateManager);
394
157
  }
395
158
 
396
- const address = currentServerState?.address ?? 'http://127.0.0.1:5621';
397
- const adminUrl = currentServerState?.adminPath ? `${address}${currentServerState.adminPath}` : `${address}/admin`;
398
-
399
- const template = [
400
- { label: `状态:${getServerStatusLabel()}`, enabled: false },
401
- { type: 'separator' },
402
- {
403
- label: serviceState === 'running' ? '停止服务' : '启动服务',
404
- click: () => (serviceState === 'running' ? stopService() : startService())
405
- },
406
- {
407
- label: '复制服务地址',
408
- enabled: serviceState === 'running',
409
- click: () => clipboard.writeText(`${address}/mcp`)
410
- },
411
- {
412
- label: '打开管理后台',
413
- enabled: serviceState === 'running',
414
- click: () => openAdminWindow(adminUrl)
415
- },
416
- { type: 'separator' },
417
- {
418
- label: '查看日志文件',
419
- click: () => {
420
- try {
421
- shell.openPath(logFilePath);
422
- } catch (error) {
423
- dialog.showErrorBox('打开日志文件失败', error.message);
424
- }
425
- }
426
- },
427
- {
428
- label: '检查更新',
429
- click: () => checkForUpdates()
430
- },
431
- {
432
- label: '关于服务',
433
- click: () => showAboutDialog()
434
- },
435
- { type: 'separator' },
436
- {
437
- label: '退出服务',
438
- click: async () => {
439
- isQuitting = true;
440
- await stopService();
441
- if (logStream) {
442
- logStream.end();
443
- }
444
- app.quit();
445
- }
159
+ async checkForUpdates() {
160
+ try {
161
+ await this.updateManager.checkForUpdates();
162
+ } catch (error) {
163
+ this.logger.error('Update check failed', error);
446
164
  }
447
- ];
448
-
449
- const menu = Menu.buildFromTemplate(template);
450
- tray.setContextMenu(menu);
451
- }
452
-
453
- function openAdminWindow(url) {
454
- if (adminWindow) {
455
- adminWindow.focus();
456
- return;
457
165
  }
458
166
 
459
- adminWindow = new BrowserWindow({
460
- width: 1200,
461
- height: 800,
462
- title: 'Prompt Server 管理后台',
463
- webPreferences: {
464
- contextIsolation: true,
465
- preload: undefined
466
- }
467
- });
468
-
469
- adminWindow.loadURL(url);
470
- adminWindow.on('closed', () => {
471
- adminWindow = null;
472
- });
473
- }
474
-
475
- async function getCurrentServiceVersion() {
476
- try {
477
- const serverRoot = await ensureRuntimeServerRoot();
478
- const pkgRaw = await fs.promises.readFile(path.join(serverRoot, 'package.json'), 'utf8');
479
- const pkg = JSON.parse(pkgRaw);
480
- return pkg.version;
481
- } catch (error) {
482
- return 'unknown';
167
+ showAboutDialog() {
168
+ this.aboutDialogManager.showAboutDialog();
483
169
  }
484
- }
485
-
486
- function compareVersions(a, b) {
487
- const toNumbers = (value = '') => value.split('.').map((part) => parseInt(part, 10) || 0);
488
- const [a1, a2, a3] = toNumbers(a);
489
- const [b1, b2, b3] = toNumbers(b);
490
- if (a1 !== b1) return a1 - b1;
491
- if (a2 !== b2) return a2 - b2;
492
- return a3 - b3;
493
- }
494
-
495
- async function checkForUpdates() {
496
- try {
497
- const currentVersion = await getCurrentServiceVersion();
498
-
499
- const response = await fetch('https://registry.npmjs.org/@becrafter/prompt-manager');
500
- if (!response.ok) {
501
- throw new Error('无法获取最新版本信息');
502
- }
503
-
504
- const metadata = await response.json();
505
- const latestVersion = metadata?.['dist-tags']?.latest;
506
-
507
- if (!latestVersion) {
508
- throw new Error('未能解析最新版本号');
509
- }
510
170
 
511
- if (compareVersions(latestVersion, currentVersion) <= 0) {
512
- await dialog.showMessageBox({
513
- type: 'info',
514
- message: '已是最新版本',
515
- detail: `服务当前版本:${currentVersion}`
516
- });
517
- return;
518
- }
519
-
520
- const { response: action } = await dialog.showMessageBox({
521
- type: 'question',
522
- buttons: ['立即升级', '打开发布页', '取消'],
523
- defaultId: 0,
524
- cancelId: 2,
525
- message: `发现新版本 ${latestVersion}`,
526
- detail: '升级期间服务会短暂停止,是否继续?'
527
- });
528
-
529
- if (action === 1) {
530
- shell.openExternal('https://github.com/BeCrafter/prompt-manager/releases/latest');
531
- return;
532
- }
533
-
534
- if (action !== 0) {
535
- return;
536
- }
537
-
538
- const wasRunning = serviceState === 'running';
539
- if (wasRunning) {
540
- await stopService();
541
- }
542
-
543
- await performInPlaceUpgrade(latestVersion);
544
-
545
- await dialog.showMessageBox({
546
- type: 'info',
547
- message: '升级成功',
548
- detail: `服务已升级到 ${latestVersion}`
549
- });
550
-
551
- if (wasRunning) {
552
- await startService();
553
- }
554
- } catch (error) {
555
- dialog.showErrorBox('检查更新失败', error?.message || String(error));
171
+ handleAboutWindowClick(data) {
172
+ // 委托给AboutDialogManager处理
173
+ this.aboutDialogManager.handleAboutWindowClick(data);
556
174
  }
557
- }
558
-
559
- async function performInPlaceUpgrade(version) {
560
- console.log('Performing in-place upgrade to version:', version);
561
- const tarballUrl = `https://registry.npmjs.org/@becrafter/prompt-manager/-/@becrafter/prompt-manager-${version}.tgz`;
562
- const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'prompt-manager-upgrade-'));
563
- const tarballPath = path.join(tmpDir, `${version}.tgz`);
564
-
565
- console.log('Temporary directory:', tmpDir);
566
-
567
- try {
568
- console.log('Downloading tarball from:', tarballUrl);
569
- const response = await fetch(tarballUrl);
570
- if (!response.ok) {
571
- throw new Error(`Failed to download tarball: ${response.status} ${response.statusText}`);
572
- }
573
-
574
- const buffer = Buffer.from(await response.arrayBuffer());
575
- await fs.promises.writeFile(tarballPath, buffer);
576
- console.log('Tarball downloaded to:', tarballPath);
577
-
578
- await tar.x({ file: tarballPath, cwd: tmpDir });
579
- const extractedPath = path.join(tmpDir, 'package');
580
- console.log('Tarball extracted to:', extractedPath);
581
-
582
- const serverRoot = await ensureRuntimeServerRoot();
583
- const examplesDir = path.join(serverRoot, 'examples', 'prompts');
584
- const examplesBackup = path.join(tmpDir, 'examples-prompts');
585
-
586
- if (await pathExists(examplesDir)) {
587
- console.log('Backing up examples directory');
588
- await fs.promises.cp(examplesDir, examplesBackup, { recursive: true });
589
- }
590
175
 
591
- console.log('Removing old server root');
592
- await fs.promises.rm(serverRoot, { recursive: true, force: true });
593
- await fs.promises.mkdir(serverRoot, { recursive: true });
176
+ async restartApplication() {
177
+ this.logger.info('Restarting application');
594
178
 
595
- console.log('Copying extracted package to server root');
596
- await fs.promises.cp(extractedPath, serverRoot, { recursive: true });
597
-
598
- if (await pathExists(examplesBackup)) {
599
- console.log('Restoring examples directory');
600
- const targetExamples = path.join(serverRoot, 'examples', 'prompts');
601
- await fs.promises.mkdir(path.dirname(targetExamples), { recursive: true });
602
- await fs.promises.cp(examplesBackup, targetExamples, { recursive: true });
603
- }
604
-
605
- console.log('Installing server dependencies');
606
- await installServerDependencies(serverRoot);
607
-
608
- console.log('Reloading server module');
609
- await loadServerModule({ forceReload: true });
610
-
611
- console.log('Upgrade completed successfully');
612
- } catch (error) {
613
- console.error('Upgrade failed:', error);
614
- throw error;
615
- } finally {
616
- console.log('Cleaning up temporary directory');
617
- await fs.promises.rm(tmpDir, { recursive: true, force: true });
618
- }
619
- }
620
-
621
- async function pathExists(targetPath) {
622
- try {
623
- await fs.promises.access(targetPath, fs.constants.F_OK);
624
- return true;
625
- } catch (error) {
626
- return false;
627
- }
628
- }
629
-
630
- async function installServerDependencies(targetDir) {
631
- console.log('Installing server dependencies in:', targetDir);
632
-
633
- // 检查 targetDir 是否存在 package.json
634
- const pkgPath = path.join(targetDir, 'package.json');
635
- try {
636
- await fs.promises.access(pkgPath, fs.constants.F_OK);
637
- console.log('package.json found in target directory');
638
- } catch (error) {
639
- console.error('package.json not found in target directory:', pkgPath);
640
- throw new Error(`package.json not found in ${targetDir}`);
641
- }
642
-
643
- // 检查 @modelcontextprotocol/sdk 是否在 dependencies 中
644
- try {
645
- const pkgContent = await fs.promises.readFile(pkgPath, 'utf8');
646
- const pkg = JSON.parse(pkgContent);
647
- if (!pkg.dependencies || !pkg.dependencies['@modelcontextprotocol/sdk']) {
648
- console.warn('@modelcontextprotocol/sdk not found in dependencies, adding it');
649
- pkg.dependencies = pkg.dependencies || {};
650
- pkg.dependencies['@modelcontextprotocol/sdk'] = '^1.20.2';
651
- await fs.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2), 'utf8');
179
+ try {
180
+ await this.stopService();
181
+ app.relaunch();
182
+ app.exit(0);
183
+ } catch (error) {
184
+ this.logger.error('Failed to restart application', error);
652
185
  }
653
- } catch (error) {
654
- console.error('Error checking/adding @modelcontextprotocol/sdk to package.json:', error);
655
186
  }
656
-
657
- const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
658
- const args = ['install', '--omit=dev', '--no-audit', '--no-fund'];
659
-
660
- console.log('Running npm install with args:', args);
661
-
662
- await new Promise((resolve, reject) => {
663
- const child = spawn(npmCommand, args, {
664
- cwd: targetDir,
665
- stdio: ['ignore', 'pipe', 'pipe']
666
- });
667
187
 
668
- let stdout = '';
669
- let stderr = '';
670
-
671
- child.stdout.on('data', (data) => {
672
- const output = data.toString().trim();
673
- stdout += output;
674
- console.log(`[npm stdout] ${output}`);
675
- });
188
+ async quitApplication() {
189
+ this.logger.info('Quitting application');
676
190
 
677
- child.stderr.on('data', (data) => {
678
- const output = data.toString().trim();
679
- stderr += output;
680
- console.error(`[npm stderr] ${output}`);
681
- });
682
-
683
- child.on('error', (error) => {
684
- console.error('npm process error:', error);
685
- reject(new Error(`Failed to start npm process: ${error.message}`));
686
- });
687
-
688
- child.on('close', (code) => {
689
- console.log('npm process exited with code:', code);
690
- if (code === 0) {
691
- console.log('npm install completed successfully');
692
- resolve();
693
- } else {
694
- const errorMsg = `npm install failed with exit code ${code}: ${stderr}`;
695
- console.error(errorMsg);
696
- reject(new Error(errorMsg));
697
- }
698
- });
699
- });
700
- }
701
-
702
- async function promptForRestart() {
703
- const { response } = await dialog.showMessageBox({
704
- type: 'error',
705
- buttons: ['重启应用', '取消'],
706
- defaultId: 0,
707
- cancelId: 1,
708
- message: '服务启动失败',
709
- detail: '多次尝试启动服务均失败,建议重启应用以恢复正常状态。'
710
- });
711
-
712
- if (response === 0) {
713
- app.relaunch();
714
- app.exit(0);
191
+ try {
192
+ this.stateManager.set('isQuitting', true);
193
+
194
+ // 关闭启动画面
195
+ await this.splashManager.closeSplash();
196
+
197
+ // 停止服务
198
+ await this.stopService();
199
+
200
+ // 清理资源
201
+ await this.aboutDialogManager.closeAboutDialog();
202
+
203
+ this.trayManager.destroy();
204
+ await this.logger.close();
205
+ await this.runtimeManager.cleanup();
206
+
207
+ app.quit();
208
+ } catch (error) {
209
+ this.logger.error('Error during application shutdown', error);
210
+ app.quit(); // 强制退出
211
+ }
715
212
  }
716
213
  }
717
214
 
718
- async function showAboutDialog() {
719
- const serviceVersion = await getCurrentServiceVersion();
720
- const lines = [
721
- // `桌面应用版本:${desktopPackageJson.version}`,
722
- `服务版本:${serviceVersion}`,
723
- `Electron:${process.versions.electron}`,
724
- // `Chromium:${process.versions.chrome}`,
725
- `Node.js:${process.versions.node}`
726
- ];
727
-
728
- await dialog.showMessageBox({
729
- type: 'info',
730
- title: '关于 Prompt Server',
731
- message: '组件版本信息',
732
- detail: lines.join('\n')
733
- });
734
- }
215
+ // 应用启动
216
+ const promptManagerApp = new PromptManagerApp();
735
217
 
736
218
  app.whenReady().then(async () => {
737
- // 初始化日志
738
- initLogStream();
739
- console.log('App is packaged:', app.isPackaged);
740
- console.log('App data path:', app.getPath('userData'));
741
- console.log('Resources path:', process.resourcesPath);
742
- console.log('__dirname:', __dirname);
743
- console.log('Process platform:', process.platform);
744
- console.log('Process version:', process.version);
745
- console.log('Electron version:', process.versions.electron);
746
-
747
- Menu.setApplicationMenu(null);
748
- if (process.platform === 'darwin') {
749
- app.dock.hide();
750
- }
751
-
752
219
  try {
753
- await ensureRuntimeServerRoot();
754
- console.log('Runtime server root ensured successfully');
755
- ensureTray();
756
- console.log('System tray ensured successfully');
757
- await startService();
758
- console.log('Service started successfully');
220
+ await promptManagerApp.initialize();
759
221
  } catch (error) {
760
- console.error('Error during app initialization:', error);
761
- dialog.showErrorBox('应用初始化失败', `启动过程中发生错误: ${error.message}\n\n请查看日志文件: ${logFilePath}`);
222
+ console.error('Failed to start application:', error);
223
+ app.quit();
762
224
  }
763
225
  });
764
226
 
765
- app.on('window-all-closed', (event) => {
766
- event.preventDefault();
767
- });
768
-
769
- app.on('before-quit', async (event) => {
770
- if (isQuitting) {
771
- return;
772
- }
773
- event.preventDefault();
774
- isQuitting = true;
775
- await stopService();
776
- if (logStream) {
777
- logStream.end();
227
+ // 异常处理
228
+ process.on('uncaughtException', (error) => {
229
+ console.error('Uncaught Exception:', error);
230
+ if (promptManagerApp.logger) {
231
+ promptManagerApp.logger.error('Uncaught Exception', error);
778
232
  }
779
233
  app.quit();
780
234
  });
781
235
 
782
- app.on('activate', () => {
783
- if (!tray) {
784
- ensureTray();
236
+ process.on('unhandledRejection', (reason, promise) => {
237
+ console.error('Unhandled Rejection at:', promise, 'reason:', reason);
238
+ if (promptManagerApp.logger) {
239
+ promptManagerApp.logger.error('Unhandled Rejection', reason);
785
240
  }
786
- });
241
+ });