@becrafter/prompt-manager 0.0.16 → 0.0.18

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.
@@ -7,6 +7,59 @@ const { pathToFileURL } = require('url');
7
7
  const tar = require('tar');
8
8
  const { spawn } = require('child_process');
9
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
+ }
62
+
10
63
  let tray = null;
11
64
  let adminWindow = null;
12
65
  let serviceState = 'stopped';
@@ -16,6 +69,7 @@ let serverModuleVersion = 0;
16
69
  let serverModuleLoading = null;
17
70
  let isQuitting = false;
18
71
  let runtimeServerRoot = null;
72
+ let startFailureCount = 0;
19
73
 
20
74
  const desktopPackageJson = JSON.parse(
21
75
  fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')
@@ -27,55 +81,153 @@ function resolveBaseServerRoot() {
27
81
 
28
82
  async function ensureRuntimeServerRoot() {
29
83
  if (runtimeServerRoot) {
84
+ console.log('Using cached runtimeServerRoot:', runtimeServerRoot);
30
85
  return runtimeServerRoot;
31
86
  }
32
87
 
33
88
  if (!app.isPackaged) {
89
+ console.log('App is not packaged, using development server root');
34
90
  runtimeServerRoot = resolveBaseServerRoot();
91
+ console.log('Development server root:', runtimeServerRoot);
35
92
  return runtimeServerRoot;
36
93
  }
37
94
 
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
+
38
99
  const packagedRoot = path.join(process.resourcesPath, 'prompt-manager');
39
100
  const runtimeRoot = path.join(app.getPath('userData'), 'prompt-manager');
101
+
102
+ console.log('packagedRoot:', packagedRoot);
103
+ console.log('runtimeRoot:', runtimeRoot);
40
104
 
41
105
  try {
42
- await fs.promises.access(runtimeRoot, fs.constants.F_OK);
106
+ // 检查打包的资源目录是否存在
107
+ 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');
120
+
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
+ } 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');
133
+
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
+ }
152
+ }
43
153
  } catch (error) {
44
- await fs.promises.mkdir(runtimeRoot, { recursive: true });
45
- await fs.promises.cp(packagedRoot, runtimeRoot, { recursive: true });
154
+ console.error('Error in ensureRuntimeServerRoot:', error);
155
+ dialog.showErrorBox('服务器初始化失败', `无法设置服务器运行环境: ${error.message}\n\n请查看日志文件: ${logFilePath}`);
156
+ throw error;
46
157
  }
47
158
 
48
159
  runtimeServerRoot = runtimeRoot;
160
+ console.log('Final runtimeServerRoot:', runtimeServerRoot);
49
161
  return runtimeServerRoot;
50
162
  }
51
163
 
52
164
  async function loadServerModule(options = {}) {
53
- if (options.forceReload) {
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');
54
173
  serverModule = null;
55
174
  serverModuleVersion += 1;
175
+ serverModuleLoading = null;
56
176
  }
57
177
 
58
178
  if (serverModule) {
179
+ console.log('Returning cached server module');
59
180
  return serverModule;
60
181
  }
61
182
 
62
183
  if (serverModuleLoading) {
184
+ console.log('Module loading in progress, returning promise');
63
185
  return serverModuleLoading;
64
186
  }
65
187
 
66
- const serverRoot = await ensureRuntimeServerRoot();
67
- const serverEntry = path.join(serverRoot, 'packages', 'server', 'server.js');
68
- const entryUrl = pathToFileURL(serverEntry);
69
- entryUrl.searchParams.set('v', String(serverModuleVersion));
70
- serverModuleLoading = import(entryUrl.href)
71
- .then((mod) => {
72
- serverModule = mod;
73
- return mod;
74
- })
75
- .finally(() => {
76
- serverModuleLoading = null;
77
- });
78
- return serverModuleLoading;
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}`);
202
+ }
203
+
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;
230
+ }
79
231
  }
80
232
 
81
233
  function getServerStatusLabel() {
@@ -94,29 +246,57 @@ function getServerStatusLabel() {
94
246
  }
95
247
 
96
248
  async function startService() {
249
+ console.log('startService called');
250
+ // 如果正在运行或正在启动,直接返回
97
251
  if (serviceState === 'running' || serviceState === 'starting') {
252
+ console.log('Service is already running or starting, returning');
98
253
  return;
99
254
  }
100
255
 
101
256
  serviceState = 'starting';
257
+ console.log('Service state set to starting');
102
258
  refreshTrayMenu();
103
259
 
104
260
  try {
105
- const module = await loadServerModule();
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');
265
+
266
+ if (!module || typeof module.startServer !== 'function') {
267
+ throw new Error('Invalid server module or missing startServer function');
268
+ }
269
+
106
270
  await module.startServer();
271
+ console.log('Server started successfully');
107
272
  currentServerState = module.getServerState();
108
273
  serviceState = 'running';
274
+ startFailureCount = 0; // 重置失败计数
275
+ console.log('Service state set to running');
109
276
  } catch (error) {
110
277
  serviceState = 'error';
111
278
  currentServerState = null;
112
- dialog.showErrorBox('服务启动失败', error?.message || String(error));
279
+ startFailureCount++;
280
+
281
+ console.error('服务启动失败:', error);
282
+ console.error('Error stack:', error.stack);
283
+
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
+ }
113
291
  } finally {
292
+ console.log('Refreshing tray menu');
114
293
  refreshTrayMenu();
115
294
  }
116
295
  }
117
296
 
118
297
  async function stopService() {
119
- if (serviceState !== 'running') {
298
+ // 如果服务未运行或正在停止,直接返回
299
+ if (serviceState !== 'running' && serviceState !== 'error') {
120
300
  return;
121
301
  }
122
302
 
@@ -125,10 +305,16 @@ async function stopService() {
125
305
 
126
306
  try {
127
307
  const module = await loadServerModule();
128
- await module.stopServer();
308
+ if (module && typeof module.stopServer === 'function') {
309
+ await module.stopServer();
310
+ }
129
311
  currentServerState = null;
130
312
  serviceState = 'stopped';
313
+
314
+ // 清理MCP会话和传输
315
+ console.log('Clearing MCP sessions and transports');
131
316
  } catch (error) {
317
+ console.error('停止服务失败:', error);
132
318
  dialog.showErrorBox('停止服务失败', error?.message || String(error));
133
319
  serviceState = 'error';
134
320
  } finally {
@@ -228,6 +414,16 @@ function refreshTrayMenu() {
228
414
  click: () => openAdminWindow(adminUrl)
229
415
  },
230
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
+ },
231
427
  {
232
428
  label: '检查更新',
233
429
  click: () => checkForUpdates()
@@ -242,6 +438,9 @@ function refreshTrayMenu() {
242
438
  click: async () => {
243
439
  isQuitting = true;
244
440
  await stopService();
441
+ if (logStream) {
442
+ logStream.end();
443
+ }
245
444
  app.quit();
246
445
  }
247
446
  }
@@ -358,43 +557,63 @@ async function checkForUpdates() {
358
557
  }
359
558
 
360
559
  async function performInPlaceUpgrade(version) {
560
+ console.log('Performing in-place upgrade to version:', version);
361
561
  const tarballUrl = `https://registry.npmjs.org/@becrafter/prompt-manager/-/@becrafter/prompt-manager-${version}.tgz`;
362
562
  const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'prompt-manager-upgrade-'));
363
563
  const tarballPath = path.join(tmpDir, `${version}.tgz`);
564
+
565
+ console.log('Temporary directory:', tmpDir);
364
566
 
365
567
  try {
568
+ console.log('Downloading tarball from:', tarballUrl);
366
569
  const response = await fetch(tarballUrl);
367
570
  if (!response.ok) {
368
- throw new Error('无法下载升级包');
571
+ throw new Error(`Failed to download tarball: ${response.status} ${response.statusText}`);
369
572
  }
370
573
 
371
574
  const buffer = Buffer.from(await response.arrayBuffer());
372
575
  await fs.promises.writeFile(tarballPath, buffer);
576
+ console.log('Tarball downloaded to:', tarballPath);
373
577
 
374
578
  await tar.x({ file: tarballPath, cwd: tmpDir });
375
579
  const extractedPath = path.join(tmpDir, 'package');
580
+ console.log('Tarball extracted to:', extractedPath);
376
581
 
377
582
  const serverRoot = await ensureRuntimeServerRoot();
378
583
  const examplesDir = path.join(serverRoot, 'examples', 'prompts');
379
584
  const examplesBackup = path.join(tmpDir, 'examples-prompts');
380
585
 
381
586
  if (await pathExists(examplesDir)) {
587
+ console.log('Backing up examples directory');
382
588
  await fs.promises.cp(examplesDir, examplesBackup, { recursive: true });
383
589
  }
384
590
 
591
+ console.log('Removing old server root');
385
592
  await fs.promises.rm(serverRoot, { recursive: true, force: true });
386
593
  await fs.promises.mkdir(serverRoot, { recursive: true });
594
+
595
+ console.log('Copying extracted package to server root');
387
596
  await fs.promises.cp(extractedPath, serverRoot, { recursive: true });
388
597
 
389
598
  if (await pathExists(examplesBackup)) {
599
+ console.log('Restoring examples directory');
390
600
  const targetExamples = path.join(serverRoot, 'examples', 'prompts');
391
601
  await fs.promises.mkdir(path.dirname(targetExamples), { recursive: true });
392
602
  await fs.promises.cp(examplesBackup, targetExamples, { recursive: true });
393
603
  }
394
604
 
605
+ console.log('Installing server dependencies');
395
606
  await installServerDependencies(serverRoot);
607
+
608
+ console.log('Reloading server module');
396
609
  await loadServerModule({ forceReload: true });
610
+
611
+ console.log('Upgrade completed successfully');
612
+ } catch (error) {
613
+ console.error('Upgrade failed:', error);
614
+ throw error;
397
615
  } finally {
616
+ console.log('Cleaning up temporary directory');
398
617
  await fs.promises.rm(tmpDir, { recursive: true, force: true });
399
618
  }
400
619
  }
@@ -409,8 +628,36 @@ async function pathExists(targetPath) {
409
628
  }
410
629
 
411
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');
652
+ }
653
+ } catch (error) {
654
+ console.error('Error checking/adding @modelcontextprotocol/sdk to package.json:', error);
655
+ }
656
+
412
657
  const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
413
- const args = ['install', '--omit=dev'];
658
+ const args = ['install', '--omit=dev', '--no-audit', '--no-fund'];
659
+
660
+ console.log('Running npm install with args:', args);
414
661
 
415
662
  await new Promise((resolve, reject) => {
416
663
  const child = spawn(npmCommand, args, {
@@ -418,29 +665,56 @@ async function installServerDependencies(targetDir) {
418
665
  stdio: ['ignore', 'pipe', 'pipe']
419
666
  });
420
667
 
668
+ let stdout = '';
421
669
  let stderr = '';
670
+
422
671
  child.stdout.on('data', (data) => {
423
- console.log(`[npm] ${data.toString().trim()}`);
672
+ const output = data.toString().trim();
673
+ stdout += output;
674
+ console.log(`[npm stdout] ${output}`);
424
675
  });
676
+
425
677
  child.stderr.on('data', (data) => {
426
- stderr += data.toString();
427
- console.error(`[npm] ${data.toString().trim()}`);
678
+ const output = data.toString().trim();
679
+ stderr += output;
680
+ console.error(`[npm stderr] ${output}`);
428
681
  });
429
682
 
430
683
  child.on('error', (error) => {
431
- reject(error);
684
+ console.error('npm process error:', error);
685
+ reject(new Error(`Failed to start npm process: ${error.message}`));
432
686
  });
433
687
 
434
688
  child.on('close', (code) => {
689
+ console.log('npm process exited with code:', code);
435
690
  if (code === 0) {
691
+ console.log('npm install completed successfully');
436
692
  resolve();
437
693
  } else {
438
- reject(new Error(`npm install failed with exit code ${code}: ${stderr}`));
694
+ const errorMsg = `npm install failed with exit code ${code}: ${stderr}`;
695
+ console.error(errorMsg);
696
+ reject(new Error(errorMsg));
439
697
  }
440
698
  });
441
699
  });
442
700
  }
443
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);
715
+ }
716
+ }
717
+
444
718
  async function showAboutDialog() {
445
719
  const serviceVersion = await getCurrentServiceVersion();
446
720
  const lines = [
@@ -460,19 +734,32 @@ async function showAboutDialog() {
460
734
  }
461
735
 
462
736
  app.whenReady().then(async () => {
737
+ // 初始化日志
738
+ initLogStream();
463
739
  console.log('App is packaged:', app.isPackaged);
464
740
  console.log('App data path:', app.getPath('userData'));
465
741
  console.log('Resources path:', process.resourcesPath);
466
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);
467
746
 
468
747
  Menu.setApplicationMenu(null);
469
748
  if (process.platform === 'darwin') {
470
749
  app.dock.hide();
471
750
  }
472
751
 
473
- await ensureRuntimeServerRoot();
474
- ensureTray();
475
- await startService();
752
+ 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');
759
+ } catch (error) {
760
+ console.error('Error during app initialization:', error);
761
+ dialog.showErrorBox('应用初始化失败', `启动过程中发生错误: ${error.message}\n\n请查看日志文件: ${logFilePath}`);
762
+ }
476
763
  });
477
764
 
478
765
  app.on('window-all-closed', (event) => {
@@ -486,6 +773,9 @@ app.on('before-quit', async (event) => {
486
773
  event.preventDefault();
487
774
  isQuitting = true;
488
775
  await stopService();
776
+ if (logStream) {
777
+ logStream.end();
778
+ }
489
779
  app.quit();
490
780
  });
491
781