@becrafter/prompt-manager 0.0.18 → 0.0.19
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/IFLOW.md +175 -0
- package/README.md +1 -1
- package/app/desktop/assets/icons/icon.icns +0 -0
- package/app/desktop/assets/icons/icon.ico +0 -0
- package/app/desktop/assets/icons/icon_1024x1024.png +0 -0
- package/app/desktop/assets/icons/icon_128x128.png +0 -0
- package/app/desktop/assets/icons/icon_16x16.png +0 -0
- package/app/desktop/assets/icons/icon_24x24.png +0 -0
- package/app/desktop/assets/icons/icon_256x256.png +0 -0
- package/app/desktop/assets/icons/icon_32x32.png +0 -0
- package/app/desktop/assets/icons/icon_48x48.png +0 -0
- package/app/desktop/assets/icons/icon_512x512.png +0 -0
- package/app/desktop/assets/icons/icon_64x64.png +0 -0
- package/app/desktop/assets/icons/icon_96x96.png +0 -0
- package/app/desktop/assets/templates/about.html +147 -0
- package/app/desktop/main.js +160 -732
- package/app/desktop/package-lock.json +567 -534
- package/app/desktop/package.json +45 -10
- package/app/desktop/preload.js +7 -0
- package/app/desktop/src/core/error-handler.js +108 -0
- package/app/desktop/src/core/event-emitter.js +84 -0
- package/app/desktop/src/core/logger.js +108 -0
- package/app/desktop/src/core/state-manager.js +125 -0
- package/app/desktop/src/services/module-loader.js +193 -0
- package/app/desktop/src/services/runtime-manager.js +152 -0
- package/app/desktop/src/services/service-manager.js +169 -0
- package/app/desktop/src/services/update-manager.js +268 -0
- package/app/desktop/src/ui/about-dialog-manager.js +208 -0
- package/app/desktop/src/ui/tray-manager.js +202 -0
- package/app/desktop/src/utils/icon-manager.js +141 -0
- package/app/desktop/src/utils/path-utils.js +58 -0
- package/app/desktop/src/utils/resource-paths.js +72 -0
- package/app/desktop/src/utils/template-renderer.js +284 -0
- package/app/desktop/src/utils/version-utils.js +59 -0
- package/examples/prompts/engineer/engineer-professional.yaml +92 -0
- package/examples/prompts/engineer/laowang-engineer.yaml +132 -0
- package/examples/prompts/engineer/nekomata-engineer.yaml +123 -0
- package/examples/prompts/engineer/ojousama-engineer.yaml +124 -0
- package/examples/prompts/workflow/sixstep-workflow.yaml +192 -0
- package/package.json +9 -3
- package/packages/admin-ui/admin.html +1 -1
- package/packages/resources/tools/filesystem/filesystem.tool.js +184 -0
- package/packages/resources/tools/index.js +16 -0
- package/packages/server/mcp/mcp.handler.js +108 -9
- package/packages/server/mcp/mcp.server.js +126 -27
- package/packages/server/mcp/toolx.handler.js +131 -0
- package/packages/server/utils/config.js +1 -1
- package/scripts/build-icons.js +105 -0
- package/scripts/icns-builder/package.json +12 -0
- /package/app/desktop/assets/{icon.png → icons/icon.png} +0 -0
package/app/desktop/main.js
CHANGED
|
@@ -1,786 +1,214 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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;
|
|
1
|
+
/**
|
|
2
|
+
* 应用主入口
|
|
3
|
+
* 重构后的主程序,遵循SRP、KISS、DRY、YAGNI原则
|
|
4
|
+
*/
|
|
13
5
|
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
}
|
|
62
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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 IconManager = require('./src/utils/icon-manager');
|
|
20
|
+
|
|
21
|
+
class PromptManagerApp {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.stateManager = new AppState();
|
|
24
|
+
this.logger = new Logger({ debugEnabled: false });
|
|
25
|
+
this.errorHandler = new ErrorHandler(this.logger);
|
|
26
|
+
this.runtimeManager = new RuntimeManager(this.logger, this.errorHandler);
|
|
27
|
+
this.moduleLoader = new ModuleLoader(this.logger, this.errorHandler);
|
|
28
|
+
this.iconManager = new IconManager();
|
|
29
|
+
this.serviceManager = new ServiceManager(this.logger, this.errorHandler, this.moduleLoader);
|
|
30
|
+
this.updateManager = new UpdateManager(this.logger, this.errorHandler, this.runtimeManager);
|
|
31
|
+
this.aboutDialogManager = new AboutDialogManager(this.logger, this.runtimeManager, this.iconManager);
|
|
32
|
+
this.trayManager = new TrayManager(this.logger, this.errorHandler, this.iconManager);
|
|
33
|
+
|
|
34
|
+
this.isInitialized = false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async initialize() {
|
|
38
|
+
if (this.isInitialized) return;
|
|
104
39
|
|
|
105
|
-
try {
|
|
106
|
-
// 检查打包的资源目录是否存在
|
|
107
40
|
try {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
41
|
+
this.logger.info('Initializing Prompt Manager Desktop Application');
|
|
42
|
+
|
|
43
|
+
// 初始化日志系统
|
|
44
|
+
await this.logger.initialize();
|
|
45
|
+
|
|
46
|
+
// 设置应用菜单
|
|
47
|
+
this.setupApplicationMenu();
|
|
48
|
+
|
|
49
|
+
// 初始化系统托盘
|
|
50
|
+
await this.trayManager.initialize(this.stateManager);
|
|
51
|
+
|
|
52
|
+
// 设置事件监听
|
|
53
|
+
this.setupEventListeners();
|
|
54
|
+
|
|
55
|
+
// 确保运行时环境
|
|
56
|
+
const serverRoot = await this.runtimeManager.ensureRuntimeEnvironment();
|
|
57
|
+
this.stateManager.set('runtimeRoot', serverRoot);
|
|
58
|
+
|
|
59
|
+
// 启动服务
|
|
60
|
+
await this.startService();
|
|
61
|
+
|
|
62
|
+
this.isInitialized = true;
|
|
63
|
+
this.logger.info('Application initialized successfully');
|
|
120
64
|
|
|
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
65
|
} catch (error) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
66
|
+
this.logger.error('Application initialization failed', error);
|
|
67
|
+
this.errorHandler.handleError('APP_INIT_FAILED', error, {
|
|
68
|
+
logFilePath: this.logger.getLogFilePath()
|
|
69
|
+
});
|
|
133
70
|
|
|
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
|
-
}
|
|
71
|
+
throw error;
|
|
152
72
|
}
|
|
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
73
|
}
|
|
177
74
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
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}`);
|
|
75
|
+
setupApplicationMenu() {
|
|
76
|
+
if (process.platform === 'darwin') {
|
|
77
|
+
app.dock.hide();
|
|
202
78
|
}
|
|
203
79
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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;
|
|
80
|
+
// 隐藏默认应用菜单
|
|
81
|
+
const { Menu } = require('electron');
|
|
82
|
+
Menu.setApplicationMenu(null);
|
|
230
83
|
}
|
|
231
|
-
}
|
|
232
84
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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');
|
|
85
|
+
setupEventListeners() {
|
|
86
|
+
// 托盘事件
|
|
87
|
+
this.trayManager.on('service-start-requested', () => this.startService());
|
|
88
|
+
this.trayManager.on('service-stop-requested', () => this.stopService());
|
|
89
|
+
this.trayManager.on('update-check-requested', () => this.checkForUpdates());
|
|
90
|
+
this.trayManager.on('about-dialog-requested', () => this.showAboutDialog());
|
|
91
|
+
this.trayManager.on('quit-requested', () => this.quitApplication());
|
|
265
92
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
93
|
+
// 服务管理器事件
|
|
94
|
+
this.serviceManager.on('status-update', () => this.trayManager.updateMenu());
|
|
95
|
+
this.serviceManager.on('restart-requested', () => this.restartApplication());
|
|
269
96
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
startFailureCount = 0; // 重置失败计数
|
|
275
|
-
console.log('Service state set to running');
|
|
276
|
-
} catch (error) {
|
|
277
|
-
serviceState = 'error';
|
|
278
|
-
currentServerState = null;
|
|
279
|
-
startFailureCount++;
|
|
97
|
+
// IPC事件
|
|
98
|
+
ipcMain.on('about-window-click', (event, data) => {
|
|
99
|
+
this.aboutDialogManager.handleAboutWindowClick(data);
|
|
100
|
+
});
|
|
280
101
|
|
|
281
|
-
|
|
282
|
-
|
|
102
|
+
// 应用事件
|
|
103
|
+
app.on('window-all-closed', (event) => {
|
|
104
|
+
event.preventDefault(); // 防止应用退出
|
|
105
|
+
});
|
|
283
106
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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';
|
|
107
|
+
app.on('before-quit', async (event) => {
|
|
108
|
+
if (this.stateManager.get('isQuitting')) return;
|
|
109
|
+
|
|
110
|
+
event.preventDefault();
|
|
111
|
+
await this.quitApplication();
|
|
112
|
+
});
|
|
313
113
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
serviceState = 'error';
|
|
320
|
-
} finally {
|
|
321
|
-
refreshTrayMenu();
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function ensureTray() {
|
|
326
|
-
if (tray) {
|
|
327
|
-
return tray;
|
|
114
|
+
app.on('activate', () => {
|
|
115
|
+
if (!this.trayManager.tray) {
|
|
116
|
+
this.trayManager.initialize(this.stateManager);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
328
119
|
}
|
|
329
120
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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;
|
|
121
|
+
async startService() {
|
|
122
|
+
const serverRoot = this.stateManager.get('runtimeRoot');
|
|
123
|
+
if (!serverRoot) {
|
|
124
|
+
this.logger.error('Cannot start service: runtime root not set');
|
|
125
|
+
return false;
|
|
350
126
|
}
|
|
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
127
|
|
|
370
|
-
|
|
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;
|
|
128
|
+
return await this.serviceManager.startService(serverRoot, this.stateManager);
|
|
388
129
|
}
|
|
389
|
-
}
|
|
390
130
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
return;
|
|
131
|
+
async stopService() {
|
|
132
|
+
return await this.serviceManager.stopService(this.stateManager);
|
|
394
133
|
}
|
|
395
134
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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
|
-
}
|
|
135
|
+
async checkForUpdates() {
|
|
136
|
+
try {
|
|
137
|
+
await this.updateManager.checkForUpdates();
|
|
138
|
+
} catch (error) {
|
|
139
|
+
this.logger.error('Update check failed', error);
|
|
446
140
|
}
|
|
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
141
|
}
|
|
458
142
|
|
|
459
|
-
|
|
460
|
-
|
|
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';
|
|
143
|
+
showAboutDialog() {
|
|
144
|
+
this.aboutDialogManager.showAboutDialog();
|
|
483
145
|
}
|
|
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
|
-
|
|
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
146
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
} catch (error) {
|
|
555
|
-
dialog.showErrorBox('检查更新失败', error?.message || String(error));
|
|
147
|
+
handleAboutWindowClick(data) {
|
|
148
|
+
// 委托给AboutDialogManager处理
|
|
149
|
+
this.aboutDialogManager.handleAboutWindowClick(data);
|
|
556
150
|
}
|
|
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
151
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
await fs.promises.mkdir(serverRoot, { recursive: true });
|
|
152
|
+
async restartApplication() {
|
|
153
|
+
this.logger.info('Restarting application');
|
|
594
154
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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');
|
|
155
|
+
try {
|
|
156
|
+
await this.stopService();
|
|
157
|
+
app.relaunch();
|
|
158
|
+
app.exit(0);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
this.logger.error('Failed to restart application', error);
|
|
652
161
|
}
|
|
653
|
-
} catch (error) {
|
|
654
|
-
console.error('Error checking/adding @modelcontextprotocol/sdk to package.json:', error);
|
|
655
162
|
}
|
|
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
163
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
child.stdout.on('data', (data) => {
|
|
672
|
-
const output = data.toString().trim();
|
|
673
|
-
stdout += output;
|
|
674
|
-
console.log(`[npm stdout] ${output}`);
|
|
675
|
-
});
|
|
164
|
+
async quitApplication() {
|
|
165
|
+
this.logger.info('Quitting application');
|
|
676
166
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
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);
|
|
167
|
+
try {
|
|
168
|
+
this.stateManager.set('isQuitting', true);
|
|
169
|
+
|
|
170
|
+
// 停止服务
|
|
171
|
+
await this.stopService();
|
|
172
|
+
|
|
173
|
+
// 清理资源
|
|
174
|
+
await this.aboutDialogManager.closeAboutDialog();
|
|
175
|
+
|
|
176
|
+
this.trayManager.destroy();
|
|
177
|
+
await this.logger.close();
|
|
178
|
+
await this.runtimeManager.cleanup();
|
|
179
|
+
|
|
180
|
+
app.quit();
|
|
181
|
+
} catch (error) {
|
|
182
|
+
this.logger.error('Error during application shutdown', error);
|
|
183
|
+
app.quit(); // 强制退出
|
|
184
|
+
}
|
|
715
185
|
}
|
|
716
186
|
}
|
|
717
187
|
|
|
718
|
-
|
|
719
|
-
|
|
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
|
-
}
|
|
188
|
+
// 应用启动
|
|
189
|
+
const promptManagerApp = new PromptManagerApp();
|
|
735
190
|
|
|
736
191
|
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
192
|
try {
|
|
753
|
-
await
|
|
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');
|
|
193
|
+
await promptManagerApp.initialize();
|
|
759
194
|
} catch (error) {
|
|
760
|
-
console.error('
|
|
761
|
-
|
|
195
|
+
console.error('Failed to start application:', error);
|
|
196
|
+
app.quit();
|
|
762
197
|
}
|
|
763
198
|
});
|
|
764
199
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
if (isQuitting) {
|
|
771
|
-
return;
|
|
772
|
-
}
|
|
773
|
-
event.preventDefault();
|
|
774
|
-
isQuitting = true;
|
|
775
|
-
await stopService();
|
|
776
|
-
if (logStream) {
|
|
777
|
-
logStream.end();
|
|
200
|
+
// 异常处理
|
|
201
|
+
process.on('uncaughtException', (error) => {
|
|
202
|
+
console.error('Uncaught Exception:', error);
|
|
203
|
+
if (promptManagerApp.logger) {
|
|
204
|
+
promptManagerApp.logger.error('Uncaught Exception', error);
|
|
778
205
|
}
|
|
779
206
|
app.quit();
|
|
780
207
|
});
|
|
781
208
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
209
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
210
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
211
|
+
if (promptManagerApp.logger) {
|
|
212
|
+
promptManagerApp.logger.error('Unhandled Rejection', reason);
|
|
785
213
|
}
|
|
786
|
-
});
|
|
214
|
+
});
|