@becrafter/prompt-manager 0.1.2 → 0.1.8
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/README.md +304 -121
- package/app/cli/commands/start.js +28 -4
- package/app/cli/support/argv.js +6 -0
- package/env.example +32 -0
- package/package.json +36 -6
- package/packages/server/api/admin.routes.js +409 -1
- package/packages/server/api/open.routes.js +7 -2
- package/packages/server/api/tool.routes.js +479 -0
- package/packages/server/app.js +97 -25
- package/packages/server/configs/models/built-in/bigmodel.yaml +6 -0
- package/packages/server/configs/models/providers.yaml +50 -0
- package/packages/server/configs/templates/built-in/general-iteration.yaml +60 -0
- package/packages/server/configs/templates/built-in/general-optimize.yaml +63 -0
- package/packages/server/configs/templates/built-in/output-format-optimize.yaml +95 -0
- package/packages/server/mcp/heartbeat-patch.js +73 -0
- package/packages/server/mcp/mcp.server.js +63 -314
- package/packages/server/mcp/prompt.handler.js +26 -0
- package/packages/server/mcp/thinking-toolkit.handler.js +380 -0
- package/packages/server/package.json +35 -3
- package/packages/server/server.js +114 -12
- package/packages/server/services/TerminalService.js +498 -0
- package/packages/server/services/WebSocketService.js +484 -0
- package/packages/server/services/manager.js +38 -7
- package/packages/server/services/model.service.js +473 -0
- package/packages/server/services/optimization.service.js +457 -0
- package/packages/server/services/template.service.js +333 -0
- package/packages/server/toolm/tool-description-generator-optimized.service.js +5 -2
- package/packages/server/toolm/tool-sync.service.js +47 -3
- package/packages/server/utils/config.js +8 -1
- package/packages/server/utils/port-checker.js +63 -0
- package/packages/server/utils/util.js +27 -0
- package/IFLOW.md +0 -175
- 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 +0 -147
- package/app/desktop/assets/tray.1.png +0 -0
- package/app/desktop/assets/tray.png +0 -0
- package/app/desktop/main.js +0 -241
- package/app/desktop/package-lock.json +0 -5026
- package/app/desktop/package.json +0 -100
- package/app/desktop/preload.js +0 -7
- package/app/desktop/src/core/error-handler.js +0 -108
- package/app/desktop/src/core/event-emitter.js +0 -84
- package/app/desktop/src/core/logger.js +0 -108
- package/app/desktop/src/core/state-manager.js +0 -125
- package/app/desktop/src/services/module-loader.js +0 -214
- package/app/desktop/src/services/runtime-manager.js +0 -301
- package/app/desktop/src/services/service-manager.js +0 -169
- package/app/desktop/src/services/update-manager.js +0 -267
- package/app/desktop/src/ui/about-dialog-manager.js +0 -208
- package/app/desktop/src/ui/admin-window-manager.js +0 -757
- package/app/desktop/src/ui/splash-manager.js +0 -253
- package/app/desktop/src/ui/tray-manager.js +0 -186
- package/app/desktop/src/utils/icon-manager.js +0 -133
- package/app/desktop/src/utils/path-utils.js +0 -58
- package/app/desktop/src/utils/resource-paths.js +0 -49
- package/app/desktop/src/utils/resource-sync.js +0 -260
- package/app/desktop/src/utils/runtime-sync.js +0 -241
- package/app/desktop/src/utils/template-renderer.js +0 -284
- package/app/desktop/src/utils/version-utils.js +0 -59
- package/examples/prompts/developer/code-review.yaml +0 -32
- package/examples/prompts/developer/code_refactoring.yaml +0 -31
- package/examples/prompts/developer/doc-generator.yaml +0 -36
- package/examples/prompts/developer/error-code-fixer.yaml +0 -35
- package/examples/prompts/engineer/engineer-professional.yaml +0 -92
- package/examples/prompts/engineer/laowang-engineer.yaml +0 -132
- package/examples/prompts/engineer/nekomata-engineer.yaml +0 -123
- package/examples/prompts/engineer/ojousama-engineer.yaml +0 -124
- package/examples/prompts/generator/gen_3d_edu_webpage_html.yaml +0 -117
- package/examples/prompts/generator/gen_3d_webpage_html.yaml +0 -75
- package/examples/prompts/generator/gen_bento_grid_html.yaml +0 -112
- package/examples/prompts/generator/gen_html_web_page.yaml +0 -88
- package/examples/prompts/generator/gen_knowledge_card_html.yaml +0 -83
- package/examples/prompts/generator/gen_magazine_card_html.yaml +0 -82
- package/examples/prompts/generator/gen_mimeng_headline_title.yaml +0 -71
- package/examples/prompts/generator/gen_podcast_script.yaml +0 -69
- package/examples/prompts/generator/gen_prd_prototype_html.yaml +0 -175
- package/examples/prompts/generator/gen_summarize.yaml +0 -157
- package/examples/prompts/generator/gen_title.yaml +0 -119
- package/examples/prompts/generator/others/api_documentation.yaml +0 -32
- package/examples/prompts/generator/others/build_mcp_server.yaml +0 -26
- package/examples/prompts/generator/others/project_architecture.yaml +0 -31
- package/examples/prompts/generator/others/test_case_generator.yaml +0 -30
- package/examples/prompts/generator/others/writing_assistant.yaml +0 -72
- package/examples/prompts/recommend/human_3-0_growth_diagnostic_coach_prompt.yaml +0 -105
- package/examples/prompts/workflow/sixstep-workflow.yaml +0 -192
- package/packages/admin-ui/.babelrc +0 -3
- package/packages/admin-ui/admin.html +0 -412
- package/packages/admin-ui/css/codemirror-theme_xq-light.css +0 -43
- package/packages/admin-ui/css/codemirror.css +0 -344
- package/packages/admin-ui/css/main.css +0 -2592
- package/packages/admin-ui/css/recommended-prompts.css +0 -610
- package/packages/admin-ui/package-lock.json +0 -6973
- package/packages/admin-ui/package.json +0 -36
- package/packages/admin-ui/src/codemirror.js +0 -53
- package/packages/admin-ui/src/index.js +0 -3188
- package/packages/admin-ui/webpack.config.js +0 -76
- package/packages/server/toolm/test-tools.js +0 -264
- package/scripts/build-icons.js +0 -135
- package/scripts/build.sh +0 -57
- package/scripts/postinstall.js +0 -34
- package/scripts/surge/CNAME +0 -1
- package/scripts/surge/README.md +0 -47
- package/scripts/surge/package-lock.json +0 -34
- package/scripts/surge/package.json +0 -20
- package/scripts/surge/sync-to-surge.js +0 -151
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { pathToFileURL } = require('url');
|
|
4
|
-
const { spawn } = require('child_process');
|
|
5
|
-
const { constants } = require('fs');
|
|
6
|
-
|
|
7
|
-
class ModuleLoader {
|
|
8
|
-
constructor(logger, errorHandler) {
|
|
9
|
-
this.logger = logger;
|
|
10
|
-
this.errorHandler = errorHandler;
|
|
11
|
-
this.moduleCache = new Map();
|
|
12
|
-
this.loadingPromises = new Map();
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async loadServerModule(serverRoot, forceReload = false) {
|
|
16
|
-
const cacheKey = serverRoot;
|
|
17
|
-
|
|
18
|
-
// 如果正在加载,返回现有promise
|
|
19
|
-
if (this.loadingPromises.has(cacheKey) && !forceReload) {
|
|
20
|
-
this.logger.debug('Returning existing module loading promise');
|
|
21
|
-
return this.loadingPromises.get(cacheKey);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// 强制重新加载时清理缓存
|
|
25
|
-
if (forceReload) {
|
|
26
|
-
this.logger.debug('Force reloading server module');
|
|
27
|
-
this.moduleCache.delete(cacheKey);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// 检查缓存
|
|
31
|
-
if (this.moduleCache.has(cacheKey)) {
|
|
32
|
-
this.logger.debug('Returning cached server module');
|
|
33
|
-
return this.moduleCache.get(cacheKey);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const loadingPromise = this._loadModuleInternal(serverRoot);
|
|
37
|
-
this.loadingPromises.set(cacheKey, loadingPromise);
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
const module = await loadingPromise;
|
|
41
|
-
this.moduleCache.set(cacheKey, module);
|
|
42
|
-
return module;
|
|
43
|
-
} catch (error) {
|
|
44
|
-
this.logger.error('Failed to load server module', error);
|
|
45
|
-
throw error;
|
|
46
|
-
} finally {
|
|
47
|
-
this.loadingPromises.delete(cacheKey);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async _loadModuleInternal(serverRoot) {
|
|
52
|
-
this.logger.info('Loading server module', { serverRoot });
|
|
53
|
-
|
|
54
|
-
// 简化路径查找逻辑,优先查找 app.asar 包中的核心库
|
|
55
|
-
const pathsToCheck = [
|
|
56
|
-
// 1. 优先从 app.asar 包中的 node_modules 加载(打包应用中)
|
|
57
|
-
path.join(process.resourcesPath, 'app.asar', 'node_modules', '@becrafter', 'prompt-manager-core', 'index.js'),
|
|
58
|
-
// 2. 尝试从 serverRoot 的 packages/server 加载(开发环境)
|
|
59
|
-
path.join(serverRoot, 'packages', 'server', 'index.js'),
|
|
60
|
-
// 3. 尝试从 serverRoot 的 node_modules 加载
|
|
61
|
-
path.join(serverRoot, 'node_modules', '@becrafter', 'prompt-manager-core', 'index.js'),
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
for (const libPath of pathsToCheck) {
|
|
65
|
-
if (await this._pathExists(libPath)) {
|
|
66
|
-
const entryUrl = pathToFileURL(libPath);
|
|
67
|
-
// 添加版本参数以防止缓存
|
|
68
|
-
entryUrl.searchParams.set('v', Date.now().toString());
|
|
69
|
-
|
|
70
|
-
const module = await import(entryUrl.href);
|
|
71
|
-
this._validateServerModule(module);
|
|
72
|
-
this.logger.info('Server module loaded successfully');
|
|
73
|
-
return module;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// 如果所有路径都没有找到,抛出错误
|
|
78
|
-
throw new Error(`Could not find core library in any of the expected paths: ${pathsToCheck.join(', ')}`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async _pathExists(targetPath) {
|
|
82
|
-
try {
|
|
83
|
-
await fs.promises.access(targetPath, constants.F_OK);
|
|
84
|
-
return true;
|
|
85
|
-
} catch (error) {
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async _validateServerEntry(serverEntry) {
|
|
91
|
-
try {
|
|
92
|
-
// 验证库的 package.json 存在
|
|
93
|
-
const packageJsonPath = path.join(serverEntry, 'package.json');
|
|
94
|
-
await fs.promises.access(packageJsonPath, fs.constants.F_OK);
|
|
95
|
-
this.logger.debug('Server module package.json exists', { serverEntry });
|
|
96
|
-
} catch (error) {
|
|
97
|
-
this.logger.error('Server module not found', error);
|
|
98
|
-
throw new Error(`Server module not found: ${serverEntry}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
_validateServerModule(module) {
|
|
103
|
-
if (!module || typeof module.startServer !== 'function') {
|
|
104
|
-
throw new Error('Invalid server module: missing startServer function');
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
this.logger.debug('Server module validation passed');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async installDependencies(targetDir) {
|
|
111
|
-
this.logger.info('Installing server dependencies', { targetDir });
|
|
112
|
-
|
|
113
|
-
// 验证 package.json 存在
|
|
114
|
-
await this._validatePackageJson(targetDir);
|
|
115
|
-
|
|
116
|
-
// 检查并添加缺失的依赖
|
|
117
|
-
await this._ensureRequiredDependencies(targetDir);
|
|
118
|
-
|
|
119
|
-
return new Promise((resolve, reject) => {
|
|
120
|
-
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
121
|
-
const args = ['install', '--omit=dev', '--no-audit', '--no-fund'];
|
|
122
|
-
|
|
123
|
-
this.logger.debug('Running npm install', { command: npmCommand, args });
|
|
124
|
-
|
|
125
|
-
const child = spawn(npmCommand, args, {
|
|
126
|
-
cwd: targetDir,
|
|
127
|
-
stdio: ['ignore', 'pipe', 'pipe']
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
let stdout = '';
|
|
131
|
-
let stderr = '';
|
|
132
|
-
|
|
133
|
-
child.stdout.on('data', (data) => {
|
|
134
|
-
const output = data.toString().trim();
|
|
135
|
-
stdout += output;
|
|
136
|
-
this.logger.debug(`[npm stdout] ${output}`);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
child.stderr.on('data', (data) => {
|
|
140
|
-
const output = data.toString().trim();
|
|
141
|
-
stderr += output;
|
|
142
|
-
this.logger.warn(`[npm stderr] ${output}`);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
child.on('error', (error) => {
|
|
146
|
-
this.logger.error('npm process error', error);
|
|
147
|
-
reject(new Error(`Failed to start npm process: ${error.message}`));
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
child.on('close', (code) => {
|
|
151
|
-
this.logger.debug('npm process exited', { code });
|
|
152
|
-
|
|
153
|
-
if (code === 0) {
|
|
154
|
-
this.logger.info('Dependencies installed successfully');
|
|
155
|
-
resolve();
|
|
156
|
-
} else {
|
|
157
|
-
const errorMsg = `npm install failed with exit code ${code}`;
|
|
158
|
-
this.logger.error(errorMsg, new Error(stderr));
|
|
159
|
-
reject(new Error(errorMsg));
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async _validatePackageJson(targetDir) {
|
|
166
|
-
const pkgPath = path.join(targetDir, 'package.json');
|
|
167
|
-
|
|
168
|
-
try {
|
|
169
|
-
await fs.promises.access(pkgPath, fs.constants.F_OK);
|
|
170
|
-
this.logger.debug('package.json found in target directory');
|
|
171
|
-
} catch (error) {
|
|
172
|
-
this.logger.error('package.json not found in target directory', { pkgPath });
|
|
173
|
-
throw new Error(`package.json not found in ${targetDir}`);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
async _ensureRequiredDependencies(targetDir) {
|
|
178
|
-
const pkgPath = path.join(targetDir, 'package.json');
|
|
179
|
-
|
|
180
|
-
try {
|
|
181
|
-
const pkgContent = await fs.promises.readFile(pkgPath, 'utf8');
|
|
182
|
-
const pkg = JSON.parse(pkgContent);
|
|
183
|
-
|
|
184
|
-
pkg.dependencies = pkg.dependencies || {};
|
|
185
|
-
|
|
186
|
-
// 确保 @becrafter/prompt-manager-core 依赖存在
|
|
187
|
-
if (!pkg.dependencies['@becrafter/prompt-manager-core']) {
|
|
188
|
-
pkg.dependencies['@becrafter/prompt-manager-core'] = '^0.0.19';
|
|
189
|
-
await fs.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2), 'utf8');
|
|
190
|
-
this.logger.info('Added @becrafter/prompt-manager-core to dependencies');
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// 检查并添加 @modelcontextprotocol/sdk
|
|
194
|
-
if (!pkg.dependencies['@modelcontextprotocol/sdk']) {
|
|
195
|
-
pkg.dependencies['@modelcontextprotocol/sdk'] = '^1.20.2';
|
|
196
|
-
await fs.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2), 'utf8');
|
|
197
|
-
this.logger.info('Added @modelcontextprotocol/sdk to dependencies');
|
|
198
|
-
}
|
|
199
|
-
} catch (error) {
|
|
200
|
-
this.logger.error('Error checking/adding dependencies', error);
|
|
201
|
-
throw error;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
clearCache(serverRoot) {
|
|
206
|
-
const cacheKey = serverRoot;
|
|
207
|
-
if (this.moduleCache.has(cacheKey)) {
|
|
208
|
-
this.moduleCache.delete(cacheKey);
|
|
209
|
-
this.logger.debug('Cleared module cache', { serverRoot });
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
module.exports = ModuleLoader;
|
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { app } = require('electron');
|
|
4
|
-
|
|
5
|
-
class RuntimeManager {
|
|
6
|
-
constructor(logger, errorHandler) {
|
|
7
|
-
this.logger = logger;
|
|
8
|
-
this.errorHandler = errorHandler;
|
|
9
|
-
this.runtimeRoot = null;
|
|
10
|
-
// 更准确地判断是否为打包应用
|
|
11
|
-
// 在开发模式下,即使 app.isPackaged 为 true,我们的项目目录结构也不同于打包应用
|
|
12
|
-
this.isPackaged = this._checkIfActuallyPackaged();
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
_checkIfActuallyPackaged() {
|
|
16
|
-
// 简化环境检测逻辑
|
|
17
|
-
const appPath = app.getAppPath();
|
|
18
|
-
const isDev = appPath.includes('node_modules/electron') ||
|
|
19
|
-
process.env.NODE_ENV === 'development' ||
|
|
20
|
-
process.defaultApp;
|
|
21
|
-
|
|
22
|
-
return !isDev && app.isPackaged;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async ensureRuntimeEnvironment() {
|
|
26
|
-
if (this.runtimeRoot) {
|
|
27
|
-
this.logger.debug('Using cached runtime root', { path: this.runtimeRoot });
|
|
28
|
-
return this.runtimeRoot;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
if (this.isPackaged) {
|
|
33
|
-
this.runtimeRoot = await this._setupPackagedEnvironment();
|
|
34
|
-
} else {
|
|
35
|
-
this.runtimeRoot = await this._setupDevelopmentEnvironment();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
this.logger.info('Runtime environment ensured', {
|
|
39
|
-
root: this.runtimeRoot,
|
|
40
|
-
isPackaged: this.isPackaged
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
return this.runtimeRoot;
|
|
44
|
-
} catch (error) {
|
|
45
|
-
this.logger.error('Failed to ensure runtime environment', error);
|
|
46
|
-
throw error;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async _setupDevelopmentEnvironment() {
|
|
51
|
-
this.logger.info('Setting up development environment');
|
|
52
|
-
|
|
53
|
-
// Go from app/desktop/src/services -> project root
|
|
54
|
-
const devRoot = path.resolve(__dirname, '..', '..', '..', '..');
|
|
55
|
-
|
|
56
|
-
// 验证开发环境
|
|
57
|
-
await this._validateDevelopmentEnvironment(devRoot);
|
|
58
|
-
|
|
59
|
-
this.runtimeRoot = devRoot;
|
|
60
|
-
return devRoot;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async _setupPackagedEnvironment() {
|
|
64
|
-
this.logger.info('Setting up packaged environment');
|
|
65
|
-
|
|
66
|
-
// 在打包的 Electron 应用中,资源文件位于 app.asar 中
|
|
67
|
-
const packagedRoot = path.join(process.resourcesPath, 'app.asar');
|
|
68
|
-
const runtimeRoot = path.join(app.getPath('userData'), 'prompt-manager');
|
|
69
|
-
|
|
70
|
-
this.logger.debug('Environment paths', {
|
|
71
|
-
packagedRoot,
|
|
72
|
-
runtimeRoot,
|
|
73
|
-
resourcesPath: process.resourcesPath,
|
|
74
|
-
isMountedVolume: process.resourcesPath.includes('/Volumes/')
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// 简化逻辑:仅检查是否是挂载卷
|
|
78
|
-
const isFromMountedVolume = process.resourcesPath.includes('/Volumes/');
|
|
79
|
-
|
|
80
|
-
if (isFromMountedVolume) {
|
|
81
|
-
this.logger.debug('Detected mounted volume, attempting to find appropriate path');
|
|
82
|
-
|
|
83
|
-
// 尝试主要路径
|
|
84
|
-
try {
|
|
85
|
-
await this._validatePackagedResources(packagedRoot);
|
|
86
|
-
this.logger.debug('Validated packaged resources from mounted volume path', { path: packagedRoot });
|
|
87
|
-
} catch (error) {
|
|
88
|
-
// 在挂载卷中,即使验证失败,我们也要继续执行
|
|
89
|
-
// 因为文件可能存在于挂载卷中,但由于权限问题无法验证
|
|
90
|
-
this.logger.warn('Could not validate packaged resources from mounted volume, continuing anyway', {
|
|
91
|
-
error: error.message
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
} else {
|
|
95
|
-
// 非挂载卷,使用正常验证流程
|
|
96
|
-
await this._validatePackagedResources(packagedRoot);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// 设置运行时目录(仅复制必要的文件,不包括 ASAR 包)
|
|
100
|
-
await this._setupRuntimeDirectory(packagedRoot, runtimeRoot);
|
|
101
|
-
|
|
102
|
-
this.runtimeRoot = runtimeRoot;
|
|
103
|
-
return runtimeRoot;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async _validateDevelopmentEnvironment(devRoot) {
|
|
107
|
-
const requiredPaths = [
|
|
108
|
-
path.join(devRoot, 'package.json')
|
|
109
|
-
];
|
|
110
|
-
|
|
111
|
-
for (const requiredPath of requiredPaths) {
|
|
112
|
-
try {
|
|
113
|
-
await fs.promises.access(requiredPath, fs.constants.F_OK);
|
|
114
|
-
} catch (error) {
|
|
115
|
-
throw new Error(`Development environment validation failed: ${requiredPath} not found`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// 检查 @becrafter/prompt-manager-core 在 node_modules 中是否存在
|
|
120
|
-
const coreLibPath = path.join(devRoot, 'node_modules', '@becrafter', 'prompt-manager-core', 'index.js');
|
|
121
|
-
try {
|
|
122
|
-
await fs.promises.access(coreLibPath, fs.constants.F_OK);
|
|
123
|
-
} catch (error) {
|
|
124
|
-
this.logger.debug('Core library not found in node_modules, checking for local packages/server');
|
|
125
|
-
// 如果 node_modules 中不存在,检查 local packages/server 目录
|
|
126
|
-
const localServerPath = path.join(devRoot, 'packages', 'server', 'server.js');
|
|
127
|
-
try {
|
|
128
|
-
await fs.promises.access(localServerPath, fs.constants.F_OK);
|
|
129
|
-
} catch (localError) {
|
|
130
|
-
throw new Error(`Development environment validation failed: neither core library nor local server found (${coreLibPath}, ${localServerPath})`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
this.logger.debug('Development environment validation passed');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async _validatePackagedResources(packagedRoot) {
|
|
138
|
-
this.logger.debug('Validating packaged resources', { packagedRoot });
|
|
139
|
-
|
|
140
|
-
// 检查 app.asar 文件是否存在
|
|
141
|
-
if (packagedRoot.endsWith('.asar')) {
|
|
142
|
-
// 在 Electron 中,当代码在 ASAR 包内运行时,对 ASAR 文件的访问方式不同
|
|
143
|
-
// 我们需要检查文件是否存在,但不能依赖 stats.isFile()
|
|
144
|
-
try {
|
|
145
|
-
// 首先尝试使用 fs.access 来检查文件是否存在和可访问
|
|
146
|
-
await fs.promises.access(packagedRoot, fs.constants.F_OK);
|
|
147
|
-
this.logger.debug('Packaged ASAR is accessible via fs.access', { path: packagedRoot });
|
|
148
|
-
return; // Found ASAR file, validation passed
|
|
149
|
-
} catch (accessError) {
|
|
150
|
-
// 如果 fs.access 失败,尝试使用 fs.stat 并检查错误类型
|
|
151
|
-
try {
|
|
152
|
-
const stats = await fs.promises.stat(packagedRoot);
|
|
153
|
-
// 在 ASAR 环境中,即使文件存在,stats.isFile() 也可能返回 false
|
|
154
|
-
// 我们只检查是否存在错误,而不检查文件类型
|
|
155
|
-
this.logger.debug('Packaged ASAR exists via fs.stat', {
|
|
156
|
-
path: packagedRoot,
|
|
157
|
-
isFile: stats.isFile(),
|
|
158
|
-
isDirectory: stats.isDirectory()
|
|
159
|
-
});
|
|
160
|
-
return; // Found ASAR file, validation passed
|
|
161
|
-
} catch (statError) {
|
|
162
|
-
// 如果两种方法都失败,记录详细信息然后抛出错误
|
|
163
|
-
this.logger.error('Failed to validate packaged resources', {
|
|
164
|
-
path: packagedRoot,
|
|
165
|
-
accessError: accessError.message,
|
|
166
|
-
accessCode: accessError.code,
|
|
167
|
-
statError: statError.message,
|
|
168
|
-
statCode: statError.code,
|
|
169
|
-
processResourcesPath: process.resourcesPath,
|
|
170
|
-
appPath: app.getAppPath()
|
|
171
|
-
});
|
|
172
|
-
throw new Error(`Packaged resources not found: ${packagedRoot} (access: ${accessError.message}, stat: ${statError.message})`);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
} else {
|
|
176
|
-
try {
|
|
177
|
-
const stats = await fs.promises.stat(packagedRoot);
|
|
178
|
-
if (stats.isDirectory() || stats.isFile()) {
|
|
179
|
-
this.logger.debug('Packaged root exists', { path: packagedRoot });
|
|
180
|
-
}
|
|
181
|
-
} catch (error) {
|
|
182
|
-
this.logger.error('Packaged root not found or not accessible', { packagedRoot, error: error.message });
|
|
183
|
-
throw new Error(`Packaged resources not found: ${packagedRoot}`);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// 移除了无用的 _generateVolumePaths 方法
|
|
189
|
-
|
|
190
|
-
async _setupRuntimeDirectory(packagedRoot, runtimeRoot) {
|
|
191
|
-
try {
|
|
192
|
-
await fs.promises.access(runtimeRoot, fs.constants.F_OK);
|
|
193
|
-
this.logger.debug('Runtime root already exists');
|
|
194
|
-
} catch (error) {
|
|
195
|
-
this.logger.info('Creating runtime directory', { path: runtimeRoot });
|
|
196
|
-
await fs.promises.mkdir(runtimeRoot, { recursive: true });
|
|
197
|
-
|
|
198
|
-
this.logger.info('Copying packaged resources to runtime directory');
|
|
199
|
-
|
|
200
|
-
// 检查是否是 ASAR 文件
|
|
201
|
-
if (packagedRoot.endsWith('.asar')) {
|
|
202
|
-
// 对于 ASAR 文件,我们不需要解压整个包,只需要复制它
|
|
203
|
-
// 但我们需要确保文件存在且可访问
|
|
204
|
-
try {
|
|
205
|
-
await fs.promises.access(packagedRoot, fs.constants.F_OK);
|
|
206
|
-
this.logger.debug('ASAR file exists, copying to runtime directory');
|
|
207
|
-
|
|
208
|
-
// 复制 ASAR 文件到运行时目录
|
|
209
|
-
const targetAsarPath = path.join(runtimeRoot, 'app.asar');
|
|
210
|
-
await fs.promises.copyFile(packagedRoot, targetAsarPath);
|
|
211
|
-
this.logger.debug('Copied ASAR file to runtime directory', { source: packagedRoot, target: targetAsarPath });
|
|
212
|
-
} catch (copyError) {
|
|
213
|
-
this.logger.error('Failed to copy ASAR file to runtime directory', {
|
|
214
|
-
source: packagedRoot,
|
|
215
|
-
error: copyError.message
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
// 如果复制失败,记录错误但继续执行
|
|
219
|
-
// 应用程序可能仍然可以运行,因为它可以直接从原始位置访问 ASAR 文件
|
|
220
|
-
this.logger.warn('Continuing without copying ASAR file to runtime directory');
|
|
221
|
-
}
|
|
222
|
-
} else {
|
|
223
|
-
// 如果是普通目录,直接复制
|
|
224
|
-
await fs.promises.cp(packagedRoot, runtimeRoot, { recursive: true });
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
async _installDependencies(runtimeRoot) {
|
|
230
|
-
this.logger.info('Installing dependencies in runtime directory');
|
|
231
|
-
|
|
232
|
-
const ModuleLoader = require('./module-loader');
|
|
233
|
-
const moduleLoader = new ModuleLoader(this.logger, this.errorHandler);
|
|
234
|
-
|
|
235
|
-
try {
|
|
236
|
-
await moduleLoader.installDependencies(runtimeRoot);
|
|
237
|
-
this.logger.info('Dependencies installed successfully');
|
|
238
|
-
} catch (error) {
|
|
239
|
-
this.logger.error('Failed to install dependencies', error);
|
|
240
|
-
throw error;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
getServerRoot() {
|
|
245
|
-
if (!this.runtimeRoot) {
|
|
246
|
-
throw new Error('Runtime environment not initialized');
|
|
247
|
-
}
|
|
248
|
-
return this.runtimeRoot;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
getPackageInfo() {
|
|
252
|
-
// 优先从 app.asar 压缩包中读取 package.json
|
|
253
|
-
const asarPackagePath = path.join(process.resourcesPath, 'app.asar', 'package.json');
|
|
254
|
-
|
|
255
|
-
// 尝试从 app.asar 中读取
|
|
256
|
-
try {
|
|
257
|
-
const packageContent = fs.readFileSync(asarPackagePath, 'utf8');
|
|
258
|
-
this.logger.debug('Read package.json from app.asar', { path: asarPackagePath });
|
|
259
|
-
return JSON.parse(packageContent);
|
|
260
|
-
} catch (asarError) {
|
|
261
|
-
// 如果从 app.asar 读取失败,尝试从 app.getAppPath() 读取(开发环境或备用方案)
|
|
262
|
-
try {
|
|
263
|
-
const appPathPackagePath = path.join(app.getAppPath(), 'package.json');
|
|
264
|
-
const packageContent = fs.readFileSync(appPathPackagePath, 'utf8');
|
|
265
|
-
this.logger.debug('Read package.json from app path', { path: appPathPackagePath });
|
|
266
|
-
return JSON.parse(packageContent);
|
|
267
|
-
} catch (appPathError) {
|
|
268
|
-
// 最后尝试从 runtimeRoot 读取(向后兼容)
|
|
269
|
-
if (this.runtimeRoot) {
|
|
270
|
-
try {
|
|
271
|
-
const runtimePackagePath = path.join(this.runtimeRoot, 'package.json');
|
|
272
|
-
const packageContent = fs.readFileSync(runtimePackagePath, 'utf8');
|
|
273
|
-
this.logger.debug('Read package.json from runtime root', { path: runtimePackagePath });
|
|
274
|
-
return JSON.parse(packageContent);
|
|
275
|
-
} catch (runtimeError) {
|
|
276
|
-
this.logger.error('Failed to read package info from all locations', {
|
|
277
|
-
asarError: asarError.message,
|
|
278
|
-
appPathError: appPathError.message,
|
|
279
|
-
runtimeError: runtimeError.message
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
} else {
|
|
283
|
-
this.logger.error('Failed to read package info from app.asar and app path', {
|
|
284
|
-
asarError: asarError.message,
|
|
285
|
-
appPathError: appPathError.message
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// 所有尝试都失败,返回默认值
|
|
292
|
-
return { version: 'unknown' };
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
async cleanup() {
|
|
296
|
-
this.logger.info('Cleaning up runtime environment');
|
|
297
|
-
this.runtimeRoot = null;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
module.exports = RuntimeManager;
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
const { dialog, shell } = require('electron');
|
|
2
|
-
const EventEmitter = require('../core/event-emitter');
|
|
3
|
-
|
|
4
|
-
class ServiceManager extends EventEmitter {
|
|
5
|
-
constructor(logger, errorHandler, moduleLoader) {
|
|
6
|
-
super();
|
|
7
|
-
this.logger = logger;
|
|
8
|
-
this.errorHandler = errorHandler;
|
|
9
|
-
this.moduleLoader = moduleLoader;
|
|
10
|
-
this.currentModule = null;
|
|
11
|
-
this.serverState = null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async startService(serverRoot, stateManager) {
|
|
15
|
-
this.logger.info('Starting service', { serverRoot });
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
// 验证服务状态
|
|
19
|
-
if (!stateManager.canStartService()) {
|
|
20
|
-
this.logger.warn('Service cannot be started in current state', {
|
|
21
|
-
currentState: stateManager.get('service')
|
|
22
|
-
});
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// 更新状态
|
|
27
|
-
stateManager.set('service', 'starting');
|
|
28
|
-
this.emitStatusUpdate(stateManager);
|
|
29
|
-
|
|
30
|
-
// 加载服务器模块
|
|
31
|
-
const module = await this.moduleLoader.loadServerModule(serverRoot, true);
|
|
32
|
-
|
|
33
|
-
// 停止现有服务实例
|
|
34
|
-
await this.stopExistingService(module);
|
|
35
|
-
|
|
36
|
-
// 启动新服务
|
|
37
|
-
await module.startServer();
|
|
38
|
-
|
|
39
|
-
// 获取服务器状态
|
|
40
|
-
this.serverState = module.getServerState();
|
|
41
|
-
this.currentModule = module;
|
|
42
|
-
|
|
43
|
-
// 更新状态
|
|
44
|
-
stateManager.set({
|
|
45
|
-
service: 'running',
|
|
46
|
-
server: this.serverState,
|
|
47
|
-
module: module
|
|
48
|
-
});
|
|
49
|
-
stateManager.resetFailureCount();
|
|
50
|
-
|
|
51
|
-
this.logger.info('Service started successfully', { serverState: this.serverState });
|
|
52
|
-
this.emitStatusUpdate(stateManager);
|
|
53
|
-
|
|
54
|
-
return true;
|
|
55
|
-
|
|
56
|
-
} catch (error) {
|
|
57
|
-
this.logger.error('Failed to start service', error);
|
|
58
|
-
|
|
59
|
-
// 更新状态
|
|
60
|
-
stateManager.set('service', 'error');
|
|
61
|
-
const failureCount = stateManager.incrementFailureCount();
|
|
62
|
-
|
|
63
|
-
// 处理错误
|
|
64
|
-
const shouldRestart = await this.errorHandler.handleServiceStartError(
|
|
65
|
-
error,
|
|
66
|
-
failureCount,
|
|
67
|
-
this.logger.getLogFilePath()
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
if (shouldRestart) {
|
|
71
|
-
this.logger.info('User requested application restart');
|
|
72
|
-
this.emitRestartRequested();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
this.emitStatusUpdate(stateManager);
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async stopService(stateManager) {
|
|
81
|
-
this.logger.info('Stopping service');
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
// 验证服务状态
|
|
85
|
-
if (!stateManager.canStopService()) {
|
|
86
|
-
this.logger.warn('Service cannot be stopped in current state', {
|
|
87
|
-
currentState: stateManager.get('service')
|
|
88
|
-
});
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// 更新状态
|
|
93
|
-
stateManager.set('service', 'stopping');
|
|
94
|
-
this.emitStatusUpdate(stateManager);
|
|
95
|
-
|
|
96
|
-
// 停止服务
|
|
97
|
-
if (this.currentModule && typeof this.currentModule.stopServer === 'function') {
|
|
98
|
-
await this.currentModule.stopServer();
|
|
99
|
-
this.logger.info('Server stopped successfully');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// 清理状态
|
|
103
|
-
this.currentModule = null;
|
|
104
|
-
this.serverState = null;
|
|
105
|
-
|
|
106
|
-
// 更新状态
|
|
107
|
-
stateManager.set({
|
|
108
|
-
service: 'stopped',
|
|
109
|
-
server: null,
|
|
110
|
-
module: null
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// 清理模块缓存
|
|
114
|
-
const serverRoot = stateManager.get('runtimeRoot');
|
|
115
|
-
if (serverRoot) {
|
|
116
|
-
this.moduleLoader.clearCache(serverRoot);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
this.logger.info('Service stopped successfully');
|
|
120
|
-
this.emitStatusUpdate(stateManager);
|
|
121
|
-
|
|
122
|
-
return true;
|
|
123
|
-
|
|
124
|
-
} catch (error) {
|
|
125
|
-
this.logger.error('Failed to stop service', error);
|
|
126
|
-
|
|
127
|
-
// 更新状态
|
|
128
|
-
stateManager.set('service', 'error');
|
|
129
|
-
this.errorHandler.handleError('SERVICE_STOP_FAILED', error);
|
|
130
|
-
this.emitStatusUpdate(stateManager);
|
|
131
|
-
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async stopExistingService(module) {
|
|
137
|
-
if (!module || typeof module.stopServer !== 'function') {
|
|
138
|
-
this.logger.debug('No existing service to stop');
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
this.logger.debug('Stopping existing service instance');
|
|
144
|
-
await module.stopServer();
|
|
145
|
-
this.logger.info('Existing service instance stopped');
|
|
146
|
-
} catch (error) {
|
|
147
|
-
this.logger.warn('Failed to stop existing service instance', error);
|
|
148
|
-
// 不抛出错误,继续启动新服务
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
getServiceStatus() {
|
|
153
|
-
return {
|
|
154
|
-
state: this.serverState,
|
|
155
|
-
module: this.currentModule,
|
|
156
|
-
isRunning: this.currentModule !== null
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
emitStatusUpdate(stateManager) {
|
|
161
|
-
this.emit('status-update', stateManager.getServiceStatus());
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
emitRestartRequested() {
|
|
165
|
-
this.emit('restart-requested');
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
module.exports = ServiceManager;
|