@becrafter/prompt-manager 0.0.16 → 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 +66 -80
- 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 +178 -460
- package/app/desktop/package-lock.json +1150 -412
- package/app/desktop/package.json +54 -11
- 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 +10 -3
- package/packages/admin-ui/admin.html +2 -2
- package/packages/resources/tools/filesystem/filesystem.tool.js +184 -0
- package/packages/resources/tools/index.js +16 -0
- package/packages/server/api/admin.routes.js +450 -0
- package/packages/server/api/open.routes.js +89 -0
- package/packages/server/app.js +163 -0
- package/packages/server/mcp/mcp.handler.js +265 -0
- package/packages/server/mcp/mcp.server.js +181 -0
- package/packages/server/mcp/toolx.handler.js +131 -0
- package/packages/server/middlewares/auth.middleware.js +34 -0
- package/packages/server/server.js +42 -908
- package/packages/server/{manager.js → services/manager.js} +13 -5
- package/packages/server/{config.js → utils/config.js} +27 -27
- package/packages/server/utils/util.js +356 -0
- package/scripts/build-icons.js +105 -0
- package/scripts/icns-builder/package.json +12 -0
- package/packages/server/mcp.js +0 -234
- package/packages/server/mcpManager.js +0 -205
- /package/app/desktop/assets/{icon.png → icons/icon.png} +0 -0
- /package/packages/server/{logger.js → utils/logger.js} +0 -0
package/app/desktop/main.js
CHANGED
|
@@ -1,496 +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
|
-
let tray = null;
|
|
11
|
-
let adminWindow = null;
|
|
12
|
-
let serviceState = 'stopped';
|
|
13
|
-
let currentServerState = null;
|
|
14
|
-
let serverModule = null;
|
|
15
|
-
let serverModuleVersion = 0;
|
|
16
|
-
let serverModuleLoading = null;
|
|
17
|
-
let isQuitting = false;
|
|
18
|
-
let runtimeServerRoot = null;
|
|
19
|
-
|
|
20
|
-
const desktopPackageJson = JSON.parse(
|
|
21
|
-
fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
function resolveBaseServerRoot() {
|
|
25
|
-
return path.resolve(__dirname, '..', '..');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async function ensureRuntimeServerRoot() {
|
|
29
|
-
if (runtimeServerRoot) {
|
|
30
|
-
return runtimeServerRoot;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (!app.isPackaged) {
|
|
34
|
-
runtimeServerRoot = resolveBaseServerRoot();
|
|
35
|
-
return runtimeServerRoot;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const packagedRoot = path.join(process.resourcesPath, 'prompt-manager');
|
|
39
|
-
const runtimeRoot = path.join(app.getPath('userData'), 'prompt-manager');
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
await fs.promises.access(runtimeRoot, fs.constants.F_OK);
|
|
43
|
-
} catch (error) {
|
|
44
|
-
await fs.promises.mkdir(runtimeRoot, { recursive: true });
|
|
45
|
-
await fs.promises.cp(packagedRoot, runtimeRoot, { recursive: true });
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
runtimeServerRoot = runtimeRoot;
|
|
49
|
-
return runtimeServerRoot;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async function loadServerModule(options = {}) {
|
|
53
|
-
if (options.forceReload) {
|
|
54
|
-
serverModule = null;
|
|
55
|
-
serverModuleVersion += 1;
|
|
56
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 应用主入口
|
|
3
|
+
* 重构后的主程序,遵循SRP、KISS、DRY、YAGNI原则
|
|
4
|
+
*/
|
|
57
5
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
6
|
+
const { app, BrowserWindow, ipcMain } = require('electron');
|
|
7
|
+
const path = require('path');
|
|
61
8
|
|
|
62
|
-
|
|
63
|
-
|
|
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;
|
|
39
|
+
|
|
40
|
+
try {
|
|
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');
|
|
64
|
+
|
|
65
|
+
} catch (error) {
|
|
66
|
+
this.logger.error('Application initialization failed', error);
|
|
67
|
+
this.errorHandler.handleError('APP_INIT_FAILED', error, {
|
|
68
|
+
logFilePath: this.logger.getLogFilePath()
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
64
73
|
}
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
setupApplicationMenu() {
|
|
76
|
+
if (process.platform === 'darwin') {
|
|
77
|
+
app.dock.hide();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 隐藏默认应用菜单
|
|
81
|
+
const { Menu } = require('electron');
|
|
82
|
+
Menu.setApplicationMenu(null);
|
|
83
|
+
}
|
|
84
|
+
|
|
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());
|
|
92
|
+
|
|
93
|
+
// 服务管理器事件
|
|
94
|
+
this.serviceManager.on('status-update', () => this.trayManager.updateMenu());
|
|
95
|
+
this.serviceManager.on('restart-requested', () => this.restartApplication());
|
|
96
|
+
|
|
97
|
+
// IPC事件
|
|
98
|
+
ipcMain.on('about-window-click', (event, data) => {
|
|
99
|
+
this.aboutDialogManager.handleAboutWindowClick(data);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// 应用事件
|
|
103
|
+
app.on('window-all-closed', (event) => {
|
|
104
|
+
event.preventDefault(); // 防止应用退出
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
app.on('before-quit', async (event) => {
|
|
108
|
+
if (this.stateManager.get('isQuitting')) return;
|
|
109
|
+
|
|
110
|
+
event.preventDefault();
|
|
111
|
+
await this.quitApplication();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
app.on('activate', () => {
|
|
115
|
+
if (!this.trayManager.tray) {
|
|
116
|
+
this.trayManager.initialize(this.stateManager);
|
|
117
|
+
}
|
|
77
118
|
});
|
|
78
|
-
return serverModuleLoading;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function getServerStatusLabel() {
|
|
82
|
-
switch (serviceState) {
|
|
83
|
-
case 'running':
|
|
84
|
-
return '运行中';
|
|
85
|
-
case 'starting':
|
|
86
|
-
return '启动中';
|
|
87
|
-
case 'stopping':
|
|
88
|
-
return '停止中';
|
|
89
|
-
case 'error':
|
|
90
|
-
return '启动失败';
|
|
91
|
-
default:
|
|
92
|
-
return '已停止';
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async function startService() {
|
|
97
|
-
if (serviceState === 'running' || serviceState === 'starting') {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
serviceState = 'starting';
|
|
102
|
-
refreshTrayMenu();
|
|
103
|
-
|
|
104
|
-
try {
|
|
105
|
-
const module = await loadServerModule();
|
|
106
|
-
await module.startServer();
|
|
107
|
-
currentServerState = module.getServerState();
|
|
108
|
-
serviceState = 'running';
|
|
109
|
-
} catch (error) {
|
|
110
|
-
serviceState = 'error';
|
|
111
|
-
currentServerState = null;
|
|
112
|
-
dialog.showErrorBox('服务启动失败', error?.message || String(error));
|
|
113
|
-
} finally {
|
|
114
|
-
refreshTrayMenu();
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
async function stopService() {
|
|
119
|
-
if (serviceState !== 'running') {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
serviceState = 'stopping';
|
|
124
|
-
refreshTrayMenu();
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
const module = await loadServerModule();
|
|
128
|
-
await module.stopServer();
|
|
129
|
-
currentServerState = null;
|
|
130
|
-
serviceState = 'stopped';
|
|
131
|
-
} catch (error) {
|
|
132
|
-
dialog.showErrorBox('停止服务失败', error?.message || String(error));
|
|
133
|
-
serviceState = 'error';
|
|
134
|
-
} finally {
|
|
135
|
-
refreshTrayMenu();
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function ensureTray() {
|
|
140
|
-
if (tray) {
|
|
141
|
-
return tray;
|
|
142
119
|
}
|
|
143
120
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
];
|
|
150
|
-
|
|
151
|
-
let iconPath = null;
|
|
152
|
-
let icon = null;
|
|
153
|
-
|
|
154
|
-
console.log('App is packaged:', app.isPackaged);
|
|
155
|
-
console.log('__dirname:', __dirname);
|
|
156
|
-
console.log('process.resourcesPath:', process.resourcesPath);
|
|
157
|
-
|
|
158
|
-
for (const possiblePath of possiblePaths) {
|
|
159
|
-
console.log('Checking icon path:', possiblePath);
|
|
160
|
-
console.log('File exists:', fs.existsSync(possiblePath));
|
|
161
|
-
if (fs.existsSync(possiblePath)) {
|
|
162
|
-
iconPath = possiblePath;
|
|
163
|
-
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;
|
|
164
126
|
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (!iconPath) {
|
|
168
|
-
console.error('Icon file not found in any of the expected locations');
|
|
169
|
-
// 创建一个简单的文本托盘作为后备
|
|
170
|
-
tray = new Tray(nativeImage.createEmpty());
|
|
171
|
-
tray.setToolTip('Prompt Server - Icon Missing');
|
|
172
|
-
refreshTrayMenu();
|
|
173
|
-
return tray;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
console.log('Attempting to load icon from:', iconPath);
|
|
177
|
-
|
|
178
|
-
try {
|
|
179
|
-
icon = nativeImage.createFromPath(iconPath);
|
|
180
|
-
console.log('Icon loaded successfully');
|
|
181
|
-
console.log('Icon is empty:', icon.isEmpty());
|
|
182
|
-
console.log('Icon size:', icon.getSize());
|
|
183
127
|
|
|
184
|
-
|
|
185
|
-
icon = icon.resize({ width: 18, height: 18 });
|
|
186
|
-
icon.setTemplateImage(true);
|
|
187
|
-
console.log('Icon resized and set as template image');
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
tray = new Tray(icon);
|
|
191
|
-
tray.setToolTip('Prompt Server');
|
|
192
|
-
console.log('Tray created successfully');
|
|
193
|
-
refreshTrayMenu();
|
|
194
|
-
return tray;
|
|
195
|
-
} catch (error) {
|
|
196
|
-
console.error('Failed to create tray:', error);
|
|
197
|
-
// 创建一个简单的文本托盘作为后备
|
|
198
|
-
tray = new Tray(nativeImage.createEmpty());
|
|
199
|
-
tray.setToolTip('Prompt Server - Error');
|
|
200
|
-
refreshTrayMenu();
|
|
201
|
-
return tray;
|
|
128
|
+
return await this.serviceManager.startService(serverRoot, this.stateManager);
|
|
202
129
|
}
|
|
203
|
-
}
|
|
204
130
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return;
|
|
131
|
+
async stopService() {
|
|
132
|
+
return await this.serviceManager.stopService(this.stateManager);
|
|
208
133
|
}
|
|
209
134
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
{ type: 'separator' },
|
|
216
|
-
{
|
|
217
|
-
label: serviceState === 'running' ? '停止服务' : '启动服务',
|
|
218
|
-
click: () => (serviceState === 'running' ? stopService() : startService())
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
label: '复制服务地址',
|
|
222
|
-
enabled: serviceState === 'running',
|
|
223
|
-
click: () => clipboard.writeText(`${address}/mcp`)
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
label: '打开管理后台',
|
|
227
|
-
enabled: serviceState === 'running',
|
|
228
|
-
click: () => openAdminWindow(adminUrl)
|
|
229
|
-
},
|
|
230
|
-
{ type: 'separator' },
|
|
231
|
-
{
|
|
232
|
-
label: '检查更新',
|
|
233
|
-
click: () => checkForUpdates()
|
|
234
|
-
},
|
|
235
|
-
{
|
|
236
|
-
label: '关于服务',
|
|
237
|
-
click: () => showAboutDialog()
|
|
238
|
-
},
|
|
239
|
-
{ type: 'separator' },
|
|
240
|
-
{
|
|
241
|
-
label: '退出服务',
|
|
242
|
-
click: async () => {
|
|
243
|
-
isQuitting = true;
|
|
244
|
-
await stopService();
|
|
245
|
-
app.quit();
|
|
246
|
-
}
|
|
135
|
+
async checkForUpdates() {
|
|
136
|
+
try {
|
|
137
|
+
await this.updateManager.checkForUpdates();
|
|
138
|
+
} catch (error) {
|
|
139
|
+
this.logger.error('Update check failed', error);
|
|
247
140
|
}
|
|
248
|
-
];
|
|
249
|
-
|
|
250
|
-
const menu = Menu.buildFromTemplate(template);
|
|
251
|
-
tray.setContextMenu(menu);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function openAdminWindow(url) {
|
|
255
|
-
if (adminWindow) {
|
|
256
|
-
adminWindow.focus();
|
|
257
|
-
return;
|
|
258
141
|
}
|
|
259
142
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
height: 800,
|
|
263
|
-
title: 'Prompt Server 管理后台',
|
|
264
|
-
webPreferences: {
|
|
265
|
-
contextIsolation: true,
|
|
266
|
-
preload: undefined
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
adminWindow.loadURL(url);
|
|
271
|
-
adminWindow.on('closed', () => {
|
|
272
|
-
adminWindow = null;
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
async function getCurrentServiceVersion() {
|
|
277
|
-
try {
|
|
278
|
-
const serverRoot = await ensureRuntimeServerRoot();
|
|
279
|
-
const pkgRaw = await fs.promises.readFile(path.join(serverRoot, 'package.json'), 'utf8');
|
|
280
|
-
const pkg = JSON.parse(pkgRaw);
|
|
281
|
-
return pkg.version;
|
|
282
|
-
} catch (error) {
|
|
283
|
-
return 'unknown';
|
|
143
|
+
showAboutDialog() {
|
|
144
|
+
this.aboutDialogManager.showAboutDialog();
|
|
284
145
|
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function compareVersions(a, b) {
|
|
288
|
-
const toNumbers = (value = '') => value.split('.').map((part) => parseInt(part, 10) || 0);
|
|
289
|
-
const [a1, a2, a3] = toNumbers(a);
|
|
290
|
-
const [b1, b2, b3] = toNumbers(b);
|
|
291
|
-
if (a1 !== b1) return a1 - b1;
|
|
292
|
-
if (a2 !== b2) return a2 - b2;
|
|
293
|
-
return a3 - b3;
|
|
294
|
-
}
|
|
295
146
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const response = await fetch('https://registry.npmjs.org/@becrafter/prompt-manager');
|
|
301
|
-
if (!response.ok) {
|
|
302
|
-
throw new Error('无法获取最新版本信息');
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const metadata = await response.json();
|
|
306
|
-
const latestVersion = metadata?.['dist-tags']?.latest;
|
|
307
|
-
|
|
308
|
-
if (!latestVersion) {
|
|
309
|
-
throw new Error('未能解析最新版本号');
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
if (compareVersions(latestVersion, currentVersion) <= 0) {
|
|
313
|
-
await dialog.showMessageBox({
|
|
314
|
-
type: 'info',
|
|
315
|
-
message: '已是最新版本',
|
|
316
|
-
detail: `服务当前版本:${currentVersion}`
|
|
317
|
-
});
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const { response: action } = await dialog.showMessageBox({
|
|
322
|
-
type: 'question',
|
|
323
|
-
buttons: ['立即升级', '打开发布页', '取消'],
|
|
324
|
-
defaultId: 0,
|
|
325
|
-
cancelId: 2,
|
|
326
|
-
message: `发现新版本 ${latestVersion}`,
|
|
327
|
-
detail: '升级期间服务会短暂停止,是否继续?'
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
if (action === 1) {
|
|
331
|
-
shell.openExternal('https://github.com/BeCrafter/prompt-manager/releases/latest');
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
if (action !== 0) {
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const wasRunning = serviceState === 'running';
|
|
340
|
-
if (wasRunning) {
|
|
341
|
-
await stopService();
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
await performInPlaceUpgrade(latestVersion);
|
|
345
|
-
|
|
346
|
-
await dialog.showMessageBox({
|
|
347
|
-
type: 'info',
|
|
348
|
-
message: '升级成功',
|
|
349
|
-
detail: `服务已升级到 ${latestVersion}`
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
if (wasRunning) {
|
|
353
|
-
await startService();
|
|
354
|
-
}
|
|
355
|
-
} catch (error) {
|
|
356
|
-
dialog.showErrorBox('检查更新失败', error?.message || String(error));
|
|
147
|
+
handleAboutWindowClick(data) {
|
|
148
|
+
// 委托给AboutDialogManager处理
|
|
149
|
+
this.aboutDialogManager.handleAboutWindowClick(data);
|
|
357
150
|
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
async function performInPlaceUpgrade(version) {
|
|
361
|
-
const tarballUrl = `https://registry.npmjs.org/@becrafter/prompt-manager/-/@becrafter/prompt-manager-${version}.tgz`;
|
|
362
|
-
const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'prompt-manager-upgrade-'));
|
|
363
|
-
const tarballPath = path.join(tmpDir, `${version}.tgz`);
|
|
364
|
-
|
|
365
|
-
try {
|
|
366
|
-
const response = await fetch(tarballUrl);
|
|
367
|
-
if (!response.ok) {
|
|
368
|
-
throw new Error('无法下载升级包');
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
const buffer = Buffer.from(await response.arrayBuffer());
|
|
372
|
-
await fs.promises.writeFile(tarballPath, buffer);
|
|
373
|
-
|
|
374
|
-
await tar.x({ file: tarballPath, cwd: tmpDir });
|
|
375
|
-
const extractedPath = path.join(tmpDir, 'package');
|
|
376
|
-
|
|
377
|
-
const serverRoot = await ensureRuntimeServerRoot();
|
|
378
|
-
const examplesDir = path.join(serverRoot, 'examples', 'prompts');
|
|
379
|
-
const examplesBackup = path.join(tmpDir, 'examples-prompts');
|
|
380
151
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
const targetExamples = path.join(serverRoot, 'examples', 'prompts');
|
|
391
|
-
await fs.promises.mkdir(path.dirname(targetExamples), { recursive: true });
|
|
392
|
-
await fs.promises.cp(examplesBackup, targetExamples, { recursive: true });
|
|
152
|
+
async restartApplication() {
|
|
153
|
+
this.logger.info('Restarting application');
|
|
154
|
+
|
|
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);
|
|
393
161
|
}
|
|
394
|
-
|
|
395
|
-
await installServerDependencies(serverRoot);
|
|
396
|
-
await loadServerModule({ forceReload: true });
|
|
397
|
-
} finally {
|
|
398
|
-
await fs.promises.rm(tmpDir, { recursive: true, force: true });
|
|
399
162
|
}
|
|
400
|
-
}
|
|
401
163
|
|
|
402
|
-
async
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
164
|
+
async quitApplication() {
|
|
165
|
+
this.logger.info('Quitting application');
|
|
166
|
+
|
|
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
|
+
}
|
|
408
185
|
}
|
|
409
186
|
}
|
|
410
187
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
const args = ['install', '--omit=dev'];
|
|
414
|
-
|
|
415
|
-
await new Promise((resolve, reject) => {
|
|
416
|
-
const child = spawn(npmCommand, args, {
|
|
417
|
-
cwd: targetDir,
|
|
418
|
-
stdio: ['ignore', 'pipe', 'pipe']
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
let stderr = '';
|
|
422
|
-
child.stdout.on('data', (data) => {
|
|
423
|
-
console.log(`[npm] ${data.toString().trim()}`);
|
|
424
|
-
});
|
|
425
|
-
child.stderr.on('data', (data) => {
|
|
426
|
-
stderr += data.toString();
|
|
427
|
-
console.error(`[npm] ${data.toString().trim()}`);
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
child.on('error', (error) => {
|
|
431
|
-
reject(error);
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
child.on('close', (code) => {
|
|
435
|
-
if (code === 0) {
|
|
436
|
-
resolve();
|
|
437
|
-
} else {
|
|
438
|
-
reject(new Error(`npm install failed with exit code ${code}: ${stderr}`));
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
async function showAboutDialog() {
|
|
445
|
-
const serviceVersion = await getCurrentServiceVersion();
|
|
446
|
-
const lines = [
|
|
447
|
-
// `桌面应用版本:${desktopPackageJson.version}`,
|
|
448
|
-
`服务版本:${serviceVersion}`,
|
|
449
|
-
`Electron:${process.versions.electron}`,
|
|
450
|
-
// `Chromium:${process.versions.chrome}`,
|
|
451
|
-
`Node.js:${process.versions.node}`
|
|
452
|
-
];
|
|
453
|
-
|
|
454
|
-
await dialog.showMessageBox({
|
|
455
|
-
type: 'info',
|
|
456
|
-
title: '关于 Prompt Server',
|
|
457
|
-
message: '组件版本信息',
|
|
458
|
-
detail: lines.join('\n')
|
|
459
|
-
});
|
|
460
|
-
}
|
|
188
|
+
// 应用启动
|
|
189
|
+
const promptManagerApp = new PromptManagerApp();
|
|
461
190
|
|
|
462
191
|
app.whenReady().then(async () => {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
Menu.setApplicationMenu(null);
|
|
469
|
-
if (process.platform === 'darwin') {
|
|
470
|
-
app.dock.hide();
|
|
192
|
+
try {
|
|
193
|
+
await promptManagerApp.initialize();
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.error('Failed to start application:', error);
|
|
196
|
+
app.quit();
|
|
471
197
|
}
|
|
472
|
-
|
|
473
|
-
await ensureRuntimeServerRoot();
|
|
474
|
-
ensureTray();
|
|
475
|
-
await startService();
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
app.on('window-all-closed', (event) => {
|
|
479
|
-
event.preventDefault();
|
|
480
198
|
});
|
|
481
199
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
200
|
+
// 异常处理
|
|
201
|
+
process.on('uncaughtException', (error) => {
|
|
202
|
+
console.error('Uncaught Exception:', error);
|
|
203
|
+
if (promptManagerApp.logger) {
|
|
204
|
+
promptManagerApp.logger.error('Uncaught Exception', error);
|
|
485
205
|
}
|
|
486
|
-
event.preventDefault();
|
|
487
|
-
isQuitting = true;
|
|
488
|
-
await stopService();
|
|
489
206
|
app.quit();
|
|
490
207
|
});
|
|
491
208
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
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);
|
|
495
213
|
}
|
|
496
|
-
});
|
|
214
|
+
});
|