@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.
- package/app/desktop/assets/app.1.png +0 -0
- package/app/desktop/assets/app.png +0 -0
- package/app/desktop/assets/icons/icon.icns +0 -0
- package/app/desktop/assets/icons/icon.ico +0 -0
- package/app/desktop/assets/icons/icon.png +0 -0
- package/app/desktop/assets/icons/tray.png +0 -0
- package/app/desktop/assets/templates/about.html +147 -0
- package/app/desktop/assets/tray.1.png +0 -0
- package/app/desktop/assets/tray.png +0 -0
- package/app/desktop/docs/ASSETS_PLANNING.md +351 -0
- package/app/desktop/docs/REFACTORING_SUMMARY.md +205 -0
- package/app/desktop/main.js +340 -0
- package/app/desktop/package-lock.json +6912 -0
- package/app/desktop/package.json +119 -0
- 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 +130 -0
- package/app/desktop/src/core/state-manager.js +125 -0
- package/app/desktop/src/services/module-loader.js +330 -0
- package/app/desktop/src/services/runtime-manager.js +398 -0
- package/app/desktop/src/services/service-manager.js +210 -0
- package/app/desktop/src/services/update-manager.js +267 -0
- package/app/desktop/src/ui/about-dialog-manager.js +208 -0
- package/app/desktop/src/ui/admin-window-manager.js +757 -0
- package/app/desktop/src/ui/splash-manager.js +253 -0
- package/app/desktop/src/ui/tray-manager.js +186 -0
- package/app/desktop/src/utils/icon-manager.js +133 -0
- package/app/desktop/src/utils/path-utils.js +58 -0
- package/app/desktop/src/utils/resource-paths.js +49 -0
- package/app/desktop/src/utils/resource-sync.js +260 -0
- package/app/desktop/src/utils/runtime-sync.js +241 -0
- package/app/desktop/src/utils/self-check.js +288 -0
- package/app/desktop/src/utils/template-renderer.js +284 -0
- package/app/desktop/src/utils/version-utils.js +59 -0
- package/env.example +1 -1
- package/examples/prompts/developer/code-review.yaml +32 -0
- package/examples/prompts/developer/code_refactoring.yaml +31 -0
- package/examples/prompts/developer/doc-generator.yaml +36 -0
- package/examples/prompts/developer/error-code-fixer.yaml +35 -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/generator/gen_3d_edu_webpage_html.yaml +117 -0
- package/examples/prompts/generator/gen_3d_webpage_html.yaml +75 -0
- package/examples/prompts/generator/gen_bento_grid_html.yaml +112 -0
- package/examples/prompts/generator/gen_html_web_page.yaml +88 -0
- package/examples/prompts/generator/gen_knowledge_card_html.yaml +83 -0
- package/examples/prompts/generator/gen_magazine_card_html.yaml +82 -0
- package/examples/prompts/generator/gen_mimeng_headline_title.yaml +71 -0
- package/examples/prompts/generator/gen_podcast_script.yaml +69 -0
- package/examples/prompts/generator/gen_prd_prototype_html.yaml +175 -0
- package/examples/prompts/generator/gen_summarize.yaml +157 -0
- package/examples/prompts/generator/gen_title.yaml +119 -0
- package/examples/prompts/generator/others/api_documentation.yaml +32 -0
- package/examples/prompts/generator/others/build_mcp_server.yaml +26 -0
- package/examples/prompts/generator/others/project_architecture.yaml +31 -0
- package/examples/prompts/generator/others/test_case_generator.yaml +30 -0
- package/examples/prompts/generator/others/writing_assistant.yaml +72 -0
- package/examples/prompts/recommend/human_3-0_growth_diagnostic_coach_prompt.yaml +105 -0
- package/examples/prompts/workflow/sixstep-workflow.yaml +192 -0
- package/package.json +13 -1
- package/packages/server/.eslintrc.js +70 -0
- package/packages/server/.husky/pre-commit +8 -0
- package/packages/server/.husky/pre-push +8 -0
- package/packages/server/.prettierrc +14 -0
- package/packages/server/dev-server.js +90 -0
- package/packages/server/jsdoc.conf.json +39 -0
- package/packages/server/playwright.config.js +62 -0
- package/packages/server/scripts/generate-docs.js +300 -0
- package/packages/server/server.js +1 -0
- package/packages/server/services/TerminalService.js +84 -11
- package/packages/server/tests/e2e/terminal-e2e.test.js +315 -0
- package/packages/server/tests/integration/terminal-websocket.test.js +372 -0
- package/packages/server/tests/integration/tools.test.js +264 -0
- package/packages/server/tests/setup.js +45 -0
- package/packages/server/tests/unit/TerminalService.test.js +410 -0
- package/packages/server/tests/unit/WebSocketService.test.js +403 -0
- package/packages/server/tests/unit/core.test.js +94 -0
- package/packages/server/typedoc.json +52 -0
- package/packages/server/utils/config.js +1 -1
- package/packages/server/utils/util.js +59 -5
- package/packages/server/vitest.config.js +74 -0
- package/packages/web/0.d1c5a72339dfc32ad86a.js +1 -0
- package/packages/web/112.8807b976372b2b0541a8.js +1 -0
- package/packages/web/130.584c7e365da413f5d9be.js +1 -0
- package/packages/web/142.72c985bc29720f975cca.js +1 -0
- package/packages/web/165.a05fc53bf84d18db36b8.js +2 -0
- package/packages/web/165.a05fc53bf84d18db36b8.js.LICENSE.txt +9 -0
- package/packages/web/203.724ab9f717b80554c397.js +1 -0
- package/packages/web/241.bf941d4f02866795f64a.js +1 -0
- package/packages/web/249.54cfb224af63f5f5ec55.js +1 -0
- package/packages/web/291.6df35042f8f296fca7cd.js +1 -0
- package/packages/web/319.2fab900a31b29873f666.js +1 -0
- package/packages/web/32.c78d866281995ec33a7b.js +1 -0
- package/packages/web/325.9ca297d0f73f38468ce9.js +1 -0
- package/packages/web/366.2f9b48fdbf8eee039e57.js +1 -0
- package/packages/web/378.6be08c612cd5a3ef97dc.js +1 -0
- package/packages/web/393.7a2f817515c5e90623d7.js +1 -0
- package/packages/web/412.062df5f732d5ba203415.js +1 -0
- package/packages/web/426.08656fef4918b3fb19ad.js +1 -0
- package/packages/web/465.2be8018327130a3bd798.js +1 -0
- package/packages/web/48.8ca96fc93667a715e67a.js +1 -0
- package/packages/web/480.44c1f1a2927486ac3d4f.js +1 -0
- package/packages/web/489.e041a8d0db15dc96d607.js +1 -0
- package/packages/web/490.9ffb26c907de020d671b.js +1 -0
- package/packages/web/492.58781369e348d91fc06a.js +1 -0
- package/packages/web/495.ed63e99791a87167c6b3.js +1 -0
- package/packages/web/510.4cc07ab7d30d5c1cd17f.js +1 -0
- package/packages/web/543.3af155ed4fa237664308.js +1 -0
- package/packages/web/567.f04ab60f8e2c2fb0745a.js +1 -0
- package/packages/web/592.f3ad085fa9c1849daa06.js +1 -0
- package/packages/web/616.b03fb801b3433b17750f.js +1 -0
- package/packages/web/617.d88def54921d2c4dc44c.js +1 -0
- package/packages/web/641.d30787d674f548928261.js +1 -0
- package/packages/web/672.5269c8399fa42a5af95d.js +1 -0
- package/packages/web/731.97cab92b71811c502bda.js +1 -0
- package/packages/web/746.3947c6f0235407e420fb.js +1 -0
- package/packages/web/756.a53233b3f3913900d5ac.js +1 -0
- package/packages/web/77.68801af593a28a631fbf.js +1 -0
- package/packages/web/802.53b2bff3cf2a69f7b80c.js +1 -0
- package/packages/web/815.b6dfab82265f56c7e046.js +1 -0
- package/packages/web/821.f5a13e5c735aac244eb9.js +1 -0
- package/packages/web/846.b9bf97d5f559270675ce.js +1 -0
- package/packages/web/869.7c10403f500e6201407f.js +1 -0
- package/packages/web/885.135050364f99e6924fb5.js +1 -0
- package/packages/web/901.fd5aeb9df630609a2b43.js +1 -0
- package/packages/web/928.f67e590de3caa4daa3ae.js +1 -0
- package/packages/web/955.d833403521ba4dd567ee.js +1 -0
- package/packages/web/981.a45cb745cf424044c8c8.js +1 -0
- package/packages/web/992.645320b60c74c8787482.js +1 -0
- package/packages/web/996.ed9a963dc9e7439eca9a.js +1 -0
- package/packages/web/css/codemirror-theme_xq-light.css +43 -0
- package/packages/web/css/codemirror.css +344 -0
- package/packages/web/css/main.3b61356b384d2f11f47f.css +7268 -0
- package/packages/web/index.html +3 -0
- package/packages/web/main.77c2c4b553ca3fac223b.js +2 -0
- 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;
|