@becrafter/prompt-manager 0.1.12 → 0.1.15

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 (139) hide show
  1. package/app/desktop/assets/app.1.png +0 -0
  2. package/app/desktop/assets/app.png +0 -0
  3. package/app/desktop/assets/icons/icon.icns +0 -0
  4. package/app/desktop/assets/icons/icon.ico +0 -0
  5. package/app/desktop/assets/icons/icon.png +0 -0
  6. package/app/desktop/assets/icons/tray.png +0 -0
  7. package/app/desktop/assets/templates/about.html +147 -0
  8. package/app/desktop/assets/tray.1.png +0 -0
  9. package/app/desktop/assets/tray.png +0 -0
  10. package/app/desktop/docs/ASSETS_PLANNING.md +351 -0
  11. package/app/desktop/docs/REFACTORING_SUMMARY.md +205 -0
  12. package/app/desktop/main.js +340 -0
  13. package/app/desktop/package-lock.json +6912 -0
  14. package/app/desktop/package.json +119 -0
  15. package/app/desktop/preload.js +7 -0
  16. package/app/desktop/src/core/error-handler.js +108 -0
  17. package/app/desktop/src/core/event-emitter.js +84 -0
  18. package/app/desktop/src/core/logger.js +130 -0
  19. package/app/desktop/src/core/state-manager.js +125 -0
  20. package/app/desktop/src/services/module-loader.js +330 -0
  21. package/app/desktop/src/services/runtime-manager.js +398 -0
  22. package/app/desktop/src/services/service-manager.js +210 -0
  23. package/app/desktop/src/services/update-manager.js +267 -0
  24. package/app/desktop/src/ui/about-dialog-manager.js +208 -0
  25. package/app/desktop/src/ui/admin-window-manager.js +757 -0
  26. package/app/desktop/src/ui/splash-manager.js +253 -0
  27. package/app/desktop/src/ui/tray-manager.js +186 -0
  28. package/app/desktop/src/utils/icon-manager.js +133 -0
  29. package/app/desktop/src/utils/path-utils.js +58 -0
  30. package/app/desktop/src/utils/resource-paths.js +49 -0
  31. package/app/desktop/src/utils/resource-sync.js +260 -0
  32. package/app/desktop/src/utils/runtime-sync.js +241 -0
  33. package/app/desktop/src/utils/self-check.js +288 -0
  34. package/app/desktop/src/utils/template-renderer.js +284 -0
  35. package/app/desktop/src/utils/version-utils.js +59 -0
  36. package/env.example +1 -1
  37. package/examples/prompts/developer/code-review.yaml +32 -0
  38. package/examples/prompts/developer/code_refactoring.yaml +31 -0
  39. package/examples/prompts/developer/doc-generator.yaml +36 -0
  40. package/examples/prompts/developer/error-code-fixer.yaml +35 -0
  41. package/examples/prompts/engineer/engineer-professional.yaml +92 -0
  42. package/examples/prompts/engineer/laowang-engineer.yaml +132 -0
  43. package/examples/prompts/engineer/nekomata-engineer.yaml +123 -0
  44. package/examples/prompts/engineer/ojousama-engineer.yaml +124 -0
  45. package/examples/prompts/generator/gen_3d_edu_webpage_html.yaml +117 -0
  46. package/examples/prompts/generator/gen_3d_webpage_html.yaml +75 -0
  47. package/examples/prompts/generator/gen_bento_grid_html.yaml +112 -0
  48. package/examples/prompts/generator/gen_html_web_page.yaml +88 -0
  49. package/examples/prompts/generator/gen_knowledge_card_html.yaml +83 -0
  50. package/examples/prompts/generator/gen_magazine_card_html.yaml +82 -0
  51. package/examples/prompts/generator/gen_mimeng_headline_title.yaml +71 -0
  52. package/examples/prompts/generator/gen_podcast_script.yaml +69 -0
  53. package/examples/prompts/generator/gen_prd_prototype_html.yaml +175 -0
  54. package/examples/prompts/generator/gen_summarize.yaml +157 -0
  55. package/examples/prompts/generator/gen_title.yaml +119 -0
  56. package/examples/prompts/generator/others/api_documentation.yaml +32 -0
  57. package/examples/prompts/generator/others/build_mcp_server.yaml +26 -0
  58. package/examples/prompts/generator/others/project_architecture.yaml +31 -0
  59. package/examples/prompts/generator/others/test_case_generator.yaml +30 -0
  60. package/examples/prompts/generator/others/writing_assistant.yaml +72 -0
  61. package/examples/prompts/recommend/human_3-0_growth_diagnostic_coach_prompt.yaml +105 -0
  62. package/examples/prompts/workflow/sixstep-workflow.yaml +192 -0
  63. package/package.json +13 -1
  64. package/packages/server/.eslintrc.js +70 -0
  65. package/packages/server/.husky/pre-commit +8 -0
  66. package/packages/server/.husky/pre-push +8 -0
  67. package/packages/server/.prettierrc +14 -0
  68. package/packages/server/dev-server.js +90 -0
  69. package/packages/server/jsdoc.conf.json +39 -0
  70. package/packages/server/playwright.config.js +62 -0
  71. package/packages/server/scripts/generate-docs.js +300 -0
  72. package/packages/server/server.js +1 -0
  73. package/packages/server/services/TerminalService.js +84 -11
  74. package/packages/server/tests/e2e/terminal-e2e.test.js +315 -0
  75. package/packages/server/tests/integration/terminal-websocket.test.js +372 -0
  76. package/packages/server/tests/integration/tools.test.js +264 -0
  77. package/packages/server/tests/setup.js +45 -0
  78. package/packages/server/tests/unit/TerminalService.test.js +410 -0
  79. package/packages/server/tests/unit/WebSocketService.test.js +403 -0
  80. package/packages/server/tests/unit/core.test.js +94 -0
  81. package/packages/server/typedoc.json +52 -0
  82. package/packages/server/utils/config.js +1 -1
  83. package/packages/server/utils/util.js +59 -5
  84. package/packages/server/vitest.config.js +74 -0
  85. package/packages/web/0.d1c5a72339dfc32ad86a.js +1 -0
  86. package/packages/web/112.8807b976372b2b0541a8.js +1 -0
  87. package/packages/web/130.584c7e365da413f5d9be.js +1 -0
  88. package/packages/web/142.72c985bc29720f975cca.js +1 -0
  89. package/packages/web/165.a05fc53bf84d18db36b8.js +2 -0
  90. package/packages/web/165.a05fc53bf84d18db36b8.js.LICENSE.txt +9 -0
  91. package/packages/web/203.724ab9f717b80554c397.js +1 -0
  92. package/packages/web/241.bf941d4f02866795f64a.js +1 -0
  93. package/packages/web/249.54cfb224af63f5f5ec55.js +1 -0
  94. package/packages/web/291.6df35042f8f296fca7cd.js +1 -0
  95. package/packages/web/319.2fab900a31b29873f666.js +1 -0
  96. package/packages/web/32.c78d866281995ec33a7b.js +1 -0
  97. package/packages/web/325.9ca297d0f73f38468ce9.js +1 -0
  98. package/packages/web/366.2f9b48fdbf8eee039e57.js +1 -0
  99. package/packages/web/378.6be08c612cd5a3ef97dc.js +1 -0
  100. package/packages/web/393.7a2f817515c5e90623d7.js +1 -0
  101. package/packages/web/412.062df5f732d5ba203415.js +1 -0
  102. package/packages/web/426.08656fef4918b3fb19ad.js +1 -0
  103. package/packages/web/465.2be8018327130a3bd798.js +1 -0
  104. package/packages/web/48.8ca96fc93667a715e67a.js +1 -0
  105. package/packages/web/480.44c1f1a2927486ac3d4f.js +1 -0
  106. package/packages/web/489.e041a8d0db15dc96d607.js +1 -0
  107. package/packages/web/490.9ffb26c907de020d671b.js +1 -0
  108. package/packages/web/492.58781369e348d91fc06a.js +1 -0
  109. package/packages/web/495.ed63e99791a87167c6b3.js +1 -0
  110. package/packages/web/510.4cc07ab7d30d5c1cd17f.js +1 -0
  111. package/packages/web/543.3af155ed4fa237664308.js +1 -0
  112. package/packages/web/567.f04ab60f8e2c2fb0745a.js +1 -0
  113. package/packages/web/592.f3ad085fa9c1849daa06.js +1 -0
  114. package/packages/web/616.b03fb801b3433b17750f.js +1 -0
  115. package/packages/web/617.d88def54921d2c4dc44c.js +1 -0
  116. package/packages/web/641.d30787d674f548928261.js +1 -0
  117. package/packages/web/672.5269c8399fa42a5af95d.js +1 -0
  118. package/packages/web/731.97cab92b71811c502bda.js +1 -0
  119. package/packages/web/746.3947c6f0235407e420fb.js +1 -0
  120. package/packages/web/756.a53233b3f3913900d5ac.js +1 -0
  121. package/packages/web/77.68801af593a28a631fbf.js +1 -0
  122. package/packages/web/802.53b2bff3cf2a69f7b80c.js +1 -0
  123. package/packages/web/815.b6dfab82265f56c7e046.js +1 -0
  124. package/packages/web/821.f5a13e5c735aac244eb9.js +1 -0
  125. package/packages/web/846.b9bf97d5f559270675ce.js +1 -0
  126. package/packages/web/869.7c10403f500e6201407f.js +1 -0
  127. package/packages/web/885.135050364f99e6924fb5.js +1 -0
  128. package/packages/web/901.fd5aeb9df630609a2b43.js +1 -0
  129. package/packages/web/928.f67e590de3caa4daa3ae.js +1 -0
  130. package/packages/web/955.d833403521ba4dd567ee.js +1 -0
  131. package/packages/web/981.a45cb745cf424044c8c8.js +1 -0
  132. package/packages/web/992.645320b60c74c8787482.js +1 -0
  133. package/packages/web/996.ed9a963dc9e7439eca9a.js +1 -0
  134. package/packages/web/css/codemirror-theme_xq-light.css +43 -0
  135. package/packages/web/css/codemirror.css +344 -0
  136. package/packages/web/css/main.3b61356b384d2f11f47f.css +7268 -0
  137. package/packages/web/index.html +3 -0
  138. package/packages/web/main.77c2c4b553ca3fac223b.js +2 -0
  139. package/packages/web/main.77c2c4b553ca3fac223b.js.LICENSE.txt +3 -0
@@ -0,0 +1,119 @@
1
+ {
2
+ "name": "@becrafter/prompt-desktop",
3
+ "version": "0.1.15",
4
+ "description": "Menu bar desktop wrapper for @becrafter/prompt-manager",
5
+ "author": "BeCrafter",
6
+ "homepage": "https://github.com/BeCrafter/prompt-manager",
7
+ "private": true,
8
+ "main": "main.js",
9
+ "scripts": {
10
+ "dev": "electron .",
11
+ "start": "npm run dev",
12
+ "prebuild": "cd ../.. && npm run build:icons",
13
+ "build": "electron-builder"
14
+ },
15
+ "devDependencies": {
16
+ "@electron/rebuild": "^4.0.2",
17
+ "electron": "39.0.0",
18
+ "electron-builder": "^24.13.3",
19
+ "electron-rebuild": "^3.2.9"
20
+ },
21
+ "dependencies": {
22
+ "@becrafter/prompt-manager-core": "file:../../packages/server",
23
+ "@modelcontextprotocol/sdk": "^1.20.2",
24
+ "cors": "^2.8.5",
25
+ "dotenv": "^17.2.3",
26
+ "express": "^5.1.0",
27
+ "fs-extra": "^11.2.0",
28
+ "http-proxy-middleware": "^3.0.5",
29
+ "js-yaml": "^4.1.0",
30
+ "multer": "^2.0.2",
31
+ "node-pty": "^1.0.0",
32
+ "tar": "^7.5.2",
33
+ "ws": "^8.18.0",
34
+ "yaml": "^2.4.1",
35
+ "zod": "^3.23.8"
36
+ },
37
+ "build": {
38
+ "appId": "com.becrafter.promptserver",
39
+ "productName": "Prompt Manager",
40
+ "icon": "assets/icons/icon.png",
41
+ "npmRebuild": true,
42
+ "files": [
43
+ "**/*",
44
+ "assets/**/*",
45
+ "!docs",
46
+ "!.npmignore",
47
+ "!assets/**/*.1.png",
48
+ "!node_modules/**/test/**",
49
+ "!node_modules/**/example/**",
50
+ "!node_modules/**/*.md",
51
+ "!node_modules/**/LICENSE",
52
+ {
53
+ "from": "../../packages/web/",
54
+ "to": "web/",
55
+ "filter": [
56
+ "**/*"
57
+ ]
58
+ }
59
+ ],
60
+ "directories": {
61
+ "output": "../../dist",
62
+ "buildResources": "assets"
63
+ },
64
+ "extraResources": [
65
+ {
66
+ "from": "../../packages/resources/tools/",
67
+ "to": "runtime/toolbox/",
68
+ "filter": [
69
+ "**/*"
70
+ ]
71
+ },
72
+ {
73
+ "from": "../../env.example",
74
+ "to": "runtime/.env"
75
+ },
76
+ {
77
+ "from": "../../packages/server/configs/",
78
+ "to": "runtime/configs/"
79
+ }
80
+ ],
81
+ "mac": {
82
+ "category": "public.app-category.productivity",
83
+ "icon": "assets/icons/icon.icns",
84
+ "target": [
85
+ {
86
+ "target": "dmg",
87
+ "arch": [
88
+ "x64",
89
+ "arm64"
90
+ ]
91
+ }
92
+ ]
93
+ },
94
+ "win": {
95
+ "target": [
96
+ {
97
+ "target": "nsis",
98
+ "arch": [
99
+ "x64",
100
+ "arm64"
101
+ ]
102
+ }
103
+ ],
104
+ "icon": "assets/icons/icon.ico"
105
+ },
106
+ "linux": {
107
+ "target": [
108
+ "AppImage",
109
+ "tar.xz"
110
+ ],
111
+ "category": "Utility",
112
+ "maintainer": "support@becrafter.com",
113
+ "synopsis": "Menu bar desktop wrapper for Prompt Manager",
114
+ "description": "Prompt Manager is a desktop application that provides a menu bar interface for managing prompts via MCP (Model Context Protocol).",
115
+ "desktop": {},
116
+ "artifactName": "${productName}-${version}.${ext}"
117
+ }
118
+ }
119
+ }
@@ -0,0 +1,7 @@
1
+ const { contextBridge, ipcRenderer } = require('electron');
2
+
3
+ // 安全地暴露API到渲染进程
4
+ contextBridge.exposeInMainWorld('electronAPI', {
5
+ sendAboutWindowClick: (data) => ipcRenderer.send('about-window-click', data),
6
+ onAboutWindowClick: (callback) => ipcRenderer.on('about-window-click-response', callback)
7
+ });
@@ -0,0 +1,108 @@
1
+ const { dialog } = require('electron');
2
+
3
+ class ErrorHandler {
4
+ constructor(logger) {
5
+ this.logger = logger;
6
+ this.errorMessages = {
7
+ MODULE_LOAD_FAILED: '模块加载失败',
8
+ SERVER_INIT_FAILED: '服务器初始化失败',
9
+ DEPENDENCY_INSTALL_FAILED: '依赖安装失败',
10
+ SERVICE_START_FAILED: '服务启动失败',
11
+ SERVICE_STOP_FAILED: '服务停止失败',
12
+ UPGRADE_FAILED: '升级失败',
13
+ ICON_LOAD_FAILED: '图标加载失败'
14
+ };
15
+ }
16
+
17
+ handleError(errorType, error, context = {}) {
18
+ const errorMessage = this.formatErrorMessage(errorType, error, context);
19
+
20
+ // 记录错误日志
21
+ this.logger.error(errorMessage, error, {
22
+ errorType,
23
+ context,
24
+ timestamp: new Date().toISOString()
25
+ });
26
+
27
+ // 显示用户友好的错误提示
28
+ this.showErrorDialog(errorType, errorMessage, context);
29
+
30
+ return errorMessage;
31
+ }
32
+
33
+ formatErrorMessage(errorType, error, context) {
34
+ const baseMessage = this.errorMessages[errorType] || '发生未知错误';
35
+ const detailMessage = error?.message || String(error);
36
+
37
+ let fullMessage = `${baseMessage}: ${detailMessage}`;
38
+
39
+ if (context.logFilePath) {
40
+ fullMessage += `\n\n请查看日志文件: ${context.logFilePath}`;
41
+ }
42
+
43
+ return fullMessage;
44
+ }
45
+
46
+ showErrorDialog(errorType, message, context = {}) {
47
+ const options = {
48
+ type: 'error',
49
+ title: '错误',
50
+ message: message.split('\n')[0], // 第一行作为标题
51
+ detail: message.split('\n').slice(1).join('\n') // 其余作为详情
52
+ };
53
+
54
+ // 特殊错误类型的自定义处理
55
+ switch (errorType) {
56
+ case 'SERVICE_START_FAILED':
57
+ if (context.failureCount >= 3) {
58
+ options.buttons = ['重启应用', '取消'];
59
+ options.defaultId = 0;
60
+ options.cancelId = 1;
61
+ options.message = '服务启动失败';
62
+ options.detail = '多次尝试启动服务均失败,建议重启应用以恢复正常状态。';
63
+ }
64
+ break;
65
+
66
+ case 'DEPENDENCY_INSTALL_FAILED':
67
+ options.message = '依赖安装失败';
68
+ options.detail = `无法安装服务器依赖: ${context.detail || '未知错误'}\n\n请查看日志文件获取更多信息。`;
69
+ break;
70
+ }
71
+
72
+ return dialog.showMessageBox(options);
73
+ }
74
+
75
+ async handleServiceStartError(error, failureCount, logFilePath) {
76
+ const context = { failureCount, logFilePath };
77
+
78
+ if (failureCount >= 3) {
79
+ const { response } = await this.showErrorDialog('SERVICE_START_FAILED', '', context);
80
+ return response === 0; // 返回是否需要重启
81
+ } else {
82
+ this.handleError('SERVICE_START_FAILED', error, context);
83
+ return false;
84
+ }
85
+ }
86
+
87
+ handleModuleLoadError(error, context = {}) {
88
+ return this.handleError('MODULE_LOAD_FAILED', error, context);
89
+ }
90
+
91
+ handleServerInitError(error, context = {}) {
92
+ return this.handleError('SERVER_INIT_FAILED', error, context);
93
+ }
94
+
95
+ handleDependencyInstallError(error, context = {}) {
96
+ return this.handleError('DEPENDENCY_INSTALL_FAILED', error, context);
97
+ }
98
+
99
+ handleUpgradeError(error, context = {}) {
100
+ return this.handleError('UPGRADE_FAILED', error, context);
101
+ }
102
+
103
+ handleIconLoadError(error, context = {}) {
104
+ return this.handleError('ICON_LOAD_FAILED', error, context);
105
+ }
106
+ }
107
+
108
+ module.exports = ErrorHandler;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * 事件发射器基类
3
+ * 提供标准化的事件处理功能
4
+ */
5
+
6
+ class EventEmitter {
7
+ constructor() {
8
+ this.listeners = {};
9
+ }
10
+
11
+ /**
12
+ * 添加事件监听器
13
+ * @param {string} event - 事件名称
14
+ * @param {function} listener - 监听器函数
15
+ */
16
+ on(event, listener) {
17
+ if (!this.listeners[event]) {
18
+ this.listeners[event] = [];
19
+ }
20
+ this.listeners[event].push(listener);
21
+ }
22
+
23
+ /**
24
+ * 移除事件监听器
25
+ * @param {string} event - 事件名称
26
+ * @param {function} listener - 监听器函数
27
+ */
28
+ off(event, listener) {
29
+ if (!this.listeners[event]) return;
30
+
31
+ const index = this.listeners[event].indexOf(listener);
32
+ if (index > -1) {
33
+ this.listeners[event].splice(index, 1);
34
+ }
35
+ }
36
+
37
+ /**
38
+ * 发射事件
39
+ * @param {string} event - 事件名称
40
+ * @param {...any} args - 传递给监听器的参数
41
+ */
42
+ emit(event, ...args) {
43
+ if (!this.listeners[event]) return;
44
+
45
+ this.listeners[event].forEach(listener => {
46
+ try {
47
+ listener(...args);
48
+ } catch (error) {
49
+ console.error(`Error in event listener for ${event}:`, error);
50
+ }
51
+ });
52
+ }
53
+
54
+ /**
55
+ * 移除指定事件的所有监听器
56
+ * @param {string} event - 事件名称
57
+ */
58
+ removeAllListeners(event) {
59
+ if (event) {
60
+ delete this.listeners[event];
61
+ } else {
62
+ this.listeners = {};
63
+ }
64
+ }
65
+
66
+ /**
67
+ * 获取指定事件的监听器数量
68
+ * @param {string} event - 事件名称
69
+ * @returns {number} - 监听器数量
70
+ */
71
+ listenerCount(event) {
72
+ return this.listeners[event] ? this.listeners[event].length : 0;
73
+ }
74
+
75
+ /**
76
+ * 获取所有事件名称
77
+ * @returns {string[]} - 事件名称数组
78
+ */
79
+ eventNames() {
80
+ return Object.keys(this.listeners);
81
+ }
82
+ }
83
+
84
+ module.exports = EventEmitter;
@@ -0,0 +1,130 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { app } = require('electron');
4
+
5
+ class Logger {
6
+ constructor(options = {}) {
7
+ this.logFilePath = options.logFilePath || path.join(app.getPath('userData'), 'prompt-manager-desktop.log');
8
+ this.debugEnabled = options.debugEnabled || false;
9
+ this.logStream = null;
10
+ this.initialized = false;
11
+ }
12
+
13
+ async initialize() {
14
+ if (this.initialized) return;
15
+
16
+ try {
17
+ const logDir = path.dirname(this.logFilePath);
18
+ if (!fs.existsSync(logDir)) {
19
+ fs.mkdirSync(logDir, { recursive: true });
20
+ }
21
+
22
+ this.logStream = fs.createWriteStream(this.logFilePath, { flags: 'a' });
23
+ this.initialized = true;
24
+
25
+ this.info('Logging initialized', { logFile: this.logFilePath });
26
+ this.info(`Debug logging is ${this.debugEnabled ? 'enabled' : 'disabled'}`);
27
+ } catch (error) {
28
+ console.error('Failed to initialize logging:', error);
29
+ throw error;
30
+ }
31
+ }
32
+
33
+ formatMessage(level, message, meta = {}) {
34
+ const timestamp = new Date().toISOString();
35
+ const metaStr = Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : '';
36
+ return `[${timestamp}] [${level.toUpperCase()}] ${message}${metaStr}\n`;
37
+ }
38
+
39
+ write(level, message, meta = {}) {
40
+ if (!this.initialized && level !== 'error') return;
41
+
42
+ const formattedMessage = this.formatMessage(level, message, meta);
43
+
44
+ try {
45
+ // 始终输出到控制台,但先检查流是否可写
46
+ if (level === 'error') {
47
+ if (process.stderr && process.stderr.writable) {
48
+ process.stderr.write(formattedMessage);
49
+ } else {
50
+ // 如果 stderr 不可用,使用 console.error 作为后备
51
+ console.error(formattedMessage.trim());
52
+ }
53
+ } else {
54
+ if (process.stdout && process.stdout.writable) {
55
+ process.stdout.write(formattedMessage);
56
+ } else {
57
+ // 如果 stdout 不可用,使用 console.log 作为后备
58
+ console.log(formattedMessage.trim());
59
+ }
60
+ }
61
+
62
+ // 写入日志文件,检查流是否可写
63
+ if (this.logStream && this.logStream.writable) {
64
+ const shouldWrite = level === 'error' ||
65
+ (this.debugEnabled && ['debug', 'warn'].includes(level)) ||
66
+ (!this.debugEnabled && ['info'].includes(level));
67
+
68
+ if (shouldWrite) {
69
+ this.logStream.write(formattedMessage);
70
+ }
71
+ }
72
+ } catch (error) {
73
+ // 静默处理写入错误,避免应用崩溃
74
+ // 只在调试模式下记录错误
75
+ if (this.debugEnabled) {
76
+ try {
77
+ console.error(`Logger write error: ${error.message}`);
78
+ } catch (e) {
79
+ // 如果连 console.error 都失败,就放弃
80
+ }
81
+ }
82
+ }
83
+ }
84
+
85
+ info(message, meta = {}) {
86
+ this.write('info', message, meta);
87
+ }
88
+
89
+ error(message, error = null, meta = {}) {
90
+ const errorMeta = { ...meta };
91
+ if (error) {
92
+ errorMeta.error = error.message || String(error);
93
+ errorMeta.stack = error.stack;
94
+ }
95
+ this.write('error', message, errorMeta);
96
+ }
97
+
98
+ warn(message, meta = {}) {
99
+ this.write('warn', message, meta);
100
+ }
101
+
102
+ debug(message, meta = {}) {
103
+ if (this.debugEnabled) {
104
+ this.write('debug', message, meta);
105
+ }
106
+ }
107
+
108
+ setDebugEnabled(enabled) {
109
+ this.debugEnabled = enabled;
110
+ this.info(`Debug logging ${enabled ? 'enabled' : 'disabled'}`);
111
+ }
112
+
113
+ getLogFilePath() {
114
+ return this.logFilePath;
115
+ }
116
+
117
+ async close() {
118
+ if (this.logStream) {
119
+ return new Promise((resolve) => {
120
+ this.logStream.end(() => {
121
+ this.logStream = null;
122
+ this.initialized = false;
123
+ resolve();
124
+ });
125
+ });
126
+ }
127
+ }
128
+ }
129
+
130
+ module.exports = Logger;
@@ -0,0 +1,125 @@
1
+ const { app } = require('electron');
2
+
3
+ /**
4
+ * 应用状态管理器
5
+ * 遵循单一职责原则,集中管理应用状态
6
+ */
7
+
8
+ class AppState {
9
+ constructor() {
10
+ this.state = {
11
+ service: 'stopped', // stopped | starting | running | stopping | error
12
+ server: null,
13
+ module: null,
14
+ moduleVersion: 0,
15
+ moduleLoading: null,
16
+ isQuitting: false,
17
+ runtimeRoot: null,
18
+ failureCount: 0,
19
+ debugEnabled: false
20
+ };
21
+
22
+ this.clickCounters = {
23
+ aboutButton: { count: 0, lastTime: 0 },
24
+ aboutWindow: { count: 0, lastTime: 0 }
25
+ };
26
+
27
+ this.ui = {
28
+ tray: null,
29
+ adminWindow: null,
30
+ aboutWindow: null
31
+ };
32
+ }
33
+
34
+ get(key) {
35
+ return key ? this.state[key] : { ...this.state };
36
+ }
37
+
38
+ set(key, value) {
39
+ if (typeof key === 'object') {
40
+ Object.assign(this.state, key);
41
+ } else {
42
+ this.state[key] = value;
43
+ }
44
+ return this;
45
+ }
46
+
47
+ getServiceStatus() {
48
+ const statusMap = {
49
+ running: '运行中',
50
+ starting: '启动中',
51
+ stopping: '停止中',
52
+ error: '启动失败',
53
+ stopped: '已停止'
54
+ };
55
+ return statusMap[this.state.service] || '未知状态';
56
+ }
57
+
58
+ getClickCounter(type) {
59
+ return this.clickCounters[type] || { count: 0, lastTime: 0 };
60
+ }
61
+
62
+ updateClickCounter(type, increment = 1) {
63
+ const now = Date.now();
64
+ const counter = this.clickCounters[type];
65
+
66
+ if (now - counter.lastTime > 3000) {
67
+ counter.count = 0;
68
+ }
69
+
70
+ counter.count += increment;
71
+ counter.lastTime = now;
72
+
73
+ return counter.count;
74
+ }
75
+
76
+ resetClickCounter(type) {
77
+ this.clickCounters[type] = { count: 0, lastTime: 0 };
78
+ return this;
79
+ }
80
+
81
+ getUI(component) {
82
+ return component ? this.ui[component] : { ...this.ui };
83
+ }
84
+
85
+ setUI(component, instance) {
86
+ this.ui[component] = instance;
87
+ return this;
88
+ }
89
+
90
+ isServiceRunning() {
91
+ return this.state.service === 'running';
92
+ }
93
+
94
+ isServiceStarting() {
95
+ return this.state.service === 'starting';
96
+ }
97
+
98
+ isServiceStopping() {
99
+ return this.state.service === 'stopping';
100
+ }
101
+
102
+ canStartService() {
103
+ return ['stopped', 'error'].includes(this.state.service);
104
+ }
105
+
106
+ canStopService() {
107
+ return ['running', 'error'].includes(this.state.service);
108
+ }
109
+
110
+ incrementFailureCount() {
111
+ this.state.failureCount++;
112
+ return this.state.failureCount;
113
+ }
114
+
115
+ resetFailureCount() {
116
+ this.state.failureCount = 0;
117
+ return this;
118
+ }
119
+
120
+ shouldPromptForRestart() {
121
+ return this.state.failureCount >= 3;
122
+ }
123
+ }
124
+
125
+ module.exports = AppState;