@becrafter/prompt-manager 0.1.2 → 0.1.9
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 +65 -4
- package/app/cli/support/argv.js +6 -0
- package/env.example +32 -0
- package/package.json +36 -7
- 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
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TerminalService - 终端服务管理类
|
|
3
|
+
*
|
|
4
|
+
* 提供跨平台终端会话管理,支持PTY(伪终端)和实时交互
|
|
5
|
+
* 支持Windows、macOS和Linux系统的原生终端命令
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawn } from 'child_process';
|
|
9
|
+
import { randomUUID } from 'crypto';
|
|
10
|
+
import { logger } from '../utils/logger.js';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import os from 'os';
|
|
13
|
+
|
|
14
|
+
// 延迟加载 node-pty,避免编译错误
|
|
15
|
+
let pty = null;
|
|
16
|
+
let PTY_AVAILABLE = false;
|
|
17
|
+
let PTY_LOAD_ERROR = null;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 尝试加载 node-pty 模块
|
|
21
|
+
*/
|
|
22
|
+
async function tryLoadNodePty() {
|
|
23
|
+
try {
|
|
24
|
+
const ptyModule = await import('node-pty');
|
|
25
|
+
pty = ptyModule;
|
|
26
|
+
// 测试模块是否真正可用
|
|
27
|
+
if (pty && pty.default && pty.default.spawn) {
|
|
28
|
+
PTY_AVAILABLE = true;
|
|
29
|
+
logger.info('node-pty 模块加载成功');
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
} catch (error) {
|
|
33
|
+
PTY_LOAD_ERROR = error;
|
|
34
|
+
PTY_AVAILABLE = false;
|
|
35
|
+
logger.warn('node-pty 模块不可用,终端功能将被禁用:', error.message);
|
|
36
|
+
logger.warn('提示: 运行 "npm rebuild node-pty" 来修复此问题');
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 终端会话类
|
|
44
|
+
*/
|
|
45
|
+
class TerminalSession {
|
|
46
|
+
constructor(id, ptyProcess, options = {}) {
|
|
47
|
+
this.id = id;
|
|
48
|
+
this.pty = ptyProcess;
|
|
49
|
+
this.createdAt = new Date();
|
|
50
|
+
this.lastActivity = new Date();
|
|
51
|
+
this.workingDirectory = options.workingDirectory || os.homedir();
|
|
52
|
+
this.shell = options.shell || this.getDefaultShell();
|
|
53
|
+
this.platform = options.platform || process.platform;
|
|
54
|
+
this.size = options.size || { cols: 80, rows: 24 };
|
|
55
|
+
this.environment = options.environment || process.env;
|
|
56
|
+
this.isActive = true;
|
|
57
|
+
this.isFallback = options.isFallback || false; // 标记是否使用回退方案
|
|
58
|
+
|
|
59
|
+
// 绑定PTY事件
|
|
60
|
+
this.setupPtyEvents();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 获取默认Shell
|
|
65
|
+
*/
|
|
66
|
+
getDefaultShell() {
|
|
67
|
+
switch (process.platform) {
|
|
68
|
+
case 'win32':
|
|
69
|
+
return process.env.COMSPEC || 'cmd.exe';
|
|
70
|
+
case 'darwin':
|
|
71
|
+
return process.env.SHELL || '/bin/bash';
|
|
72
|
+
case 'linux':
|
|
73
|
+
return process.env.SHELL || '/bin/bash';
|
|
74
|
+
default:
|
|
75
|
+
return '/bin/sh';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 设置PTY事件监听
|
|
81
|
+
*/
|
|
82
|
+
setupPtyEvents() {
|
|
83
|
+
if (!this.pty) return;
|
|
84
|
+
|
|
85
|
+
this.pty.on('data', (data) => {
|
|
86
|
+
this.lastActivity = new Date();
|
|
87
|
+
this.emit('data', data);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
this.pty.on('exit', (exitCode, signal) => {
|
|
91
|
+
this.isActive = false;
|
|
92
|
+
this.emit('exit', { exitCode, signal });
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 写入数据到终端
|
|
98
|
+
*/
|
|
99
|
+
write(data) {
|
|
100
|
+
if (!this.isActive || !this.pty) {
|
|
101
|
+
throw new Error('Terminal session is not active');
|
|
102
|
+
}
|
|
103
|
+
this.pty.write(data);
|
|
104
|
+
this.lastActivity = new Date();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 调整终端大小
|
|
109
|
+
*/
|
|
110
|
+
resize(cols, rows) {
|
|
111
|
+
if (!this.isActive || !this.pty) {
|
|
112
|
+
throw new Error('Terminal session is not active');
|
|
113
|
+
}
|
|
114
|
+
this.pty.resize(cols, rows);
|
|
115
|
+
this.size = { cols, rows };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 终止会话
|
|
120
|
+
*/
|
|
121
|
+
terminate() {
|
|
122
|
+
if (this.pty) {
|
|
123
|
+
this.pty.kill();
|
|
124
|
+
}
|
|
125
|
+
this.isActive = false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 获取会话信息
|
|
130
|
+
*/
|
|
131
|
+
getInfo() {
|
|
132
|
+
return {
|
|
133
|
+
id: this.id,
|
|
134
|
+
shell: this.shell,
|
|
135
|
+
workingDirectory: this.workingDirectory,
|
|
136
|
+
platform: this.platform,
|
|
137
|
+
size: this.size,
|
|
138
|
+
createdAt: this.createdAt,
|
|
139
|
+
lastActivity: this.lastActivity,
|
|
140
|
+
isActive: this.isActive
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 简单的事件发射器实现
|
|
146
|
+
*/
|
|
147
|
+
emit(event, ...args) {
|
|
148
|
+
if (this._events && this._events[event]) {
|
|
149
|
+
this._events[event].forEach(callback => callback(...args));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 事件监听器
|
|
155
|
+
*/
|
|
156
|
+
on(event, callback) {
|
|
157
|
+
if (!this._events) this._events = {};
|
|
158
|
+
if (!this._events[event]) this._events[event] = [];
|
|
159
|
+
this._events[event].push(callback);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* 移除事件监听器
|
|
164
|
+
*/
|
|
165
|
+
off(event, callback) {
|
|
166
|
+
if (!this._events || !this._events[event]) return;
|
|
167
|
+
const index = this._events[event].indexOf(callback);
|
|
168
|
+
if (index > -1) {
|
|
169
|
+
this._events[event].splice(index, 1);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* TerminalService 主类
|
|
176
|
+
*/
|
|
177
|
+
export class TerminalService {
|
|
178
|
+
constructor(options = {}) {
|
|
179
|
+
this.sessions = new Map();
|
|
180
|
+
this.defaultOptions = {
|
|
181
|
+
shell: null, // 自动检测
|
|
182
|
+
workingDirectory: os.homedir(),
|
|
183
|
+
size: { cols: 80, rows: 24 },
|
|
184
|
+
timeout: 300000, // 5分钟超时
|
|
185
|
+
maxSessions: 10, // 最大会话数
|
|
186
|
+
...options
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// 定期清理非活跃会话
|
|
190
|
+
this.cleanupInterval = setInterval(() => {
|
|
191
|
+
this.cleanupInactiveSessions();
|
|
192
|
+
}, 60000); // 每分钟检查一次
|
|
193
|
+
|
|
194
|
+
logger.info('TerminalService initialized');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 创建新的终端会话
|
|
199
|
+
*/
|
|
200
|
+
async createSession(options = {}) {
|
|
201
|
+
// 首次使用时尝试加载 node-pty
|
|
202
|
+
if (pty === null && PTY_AVAILABLE === false) {
|
|
203
|
+
await tryLoadNodePty();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 检查PTY是否可用
|
|
207
|
+
if (!PTY_AVAILABLE) {
|
|
208
|
+
throw new Error('Terminal functionality is disabled - node-pty module is not available. Run "npm rebuild node-pty" to fix this.');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const sessionId = options.id || randomUUID();
|
|
212
|
+
const sessionOptions = { ...this.defaultOptions, ...options };
|
|
213
|
+
|
|
214
|
+
// 检查会话数限制
|
|
215
|
+
if (this.sessions.size >= sessionOptions.maxSessions) {
|
|
216
|
+
throw new Error(`Maximum sessions limit reached: ${sessionOptions.maxSessions}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// 检查会话ID是否已存在
|
|
220
|
+
if (this.sessions.has(sessionId)) {
|
|
221
|
+
throw new Error(`Session with ID ${sessionId} already exists`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
const ptyProcess = await this.createPtyProcess(sessionOptions);
|
|
226
|
+
const session = new TerminalSession(sessionId, ptyProcess, sessionOptions);
|
|
227
|
+
|
|
228
|
+
// 添加事件监听
|
|
229
|
+
session.on('data', (data) => {
|
|
230
|
+
this.handleSessionData(sessionId, data);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
session.on('exit', (info) => {
|
|
234
|
+
this.handleSessionExit(sessionId, info);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// 存储会话
|
|
238
|
+
this.sessions.set(sessionId, session);
|
|
239
|
+
|
|
240
|
+
logger.info(`Terminal session created: ${sessionId}`);
|
|
241
|
+
return session;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
logger.error(`Failed to create terminal session: ${error.message}`);
|
|
244
|
+
throw error;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* 创建PTY进程
|
|
250
|
+
*/
|
|
251
|
+
async createPtyProcess(options) {
|
|
252
|
+
const shell = options.shell || this.getDefaultShellForPlatform();
|
|
253
|
+
const args = this.getShellArgs(shell);
|
|
254
|
+
const cwd = options.workingDirectory || os.homedir();
|
|
255
|
+
const env = { ...process.env, ...options.environment };
|
|
256
|
+
|
|
257
|
+
logger.debug(`Creating PTY with shell: ${shell}, args: ${args.join(' ')}, cwd: ${cwd}`);
|
|
258
|
+
|
|
259
|
+
return pty.default.spawn(shell, args, {
|
|
260
|
+
name: 'xterm-color',
|
|
261
|
+
cols: options.size.cols,
|
|
262
|
+
rows: options.size.rows,
|
|
263
|
+
cwd: cwd,
|
|
264
|
+
env: env
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* 获取平台对应的默认Shell
|
|
270
|
+
*/
|
|
271
|
+
getDefaultShellForPlatform() {
|
|
272
|
+
switch (process.platform) {
|
|
273
|
+
case 'win32':
|
|
274
|
+
return process.env.COMSPEC || 'cmd.exe';
|
|
275
|
+
case 'darwin':
|
|
276
|
+
return process.env.SHELL || '/bin/bash';
|
|
277
|
+
case 'linux':
|
|
278
|
+
return process.env.SHELL || '/bin/bash';
|
|
279
|
+
default:
|
|
280
|
+
return '/bin/sh';
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* 获取Shell启动参数
|
|
286
|
+
*/
|
|
287
|
+
getShellArgs(shell) {
|
|
288
|
+
if (process.platform === 'win32') {
|
|
289
|
+
if (shell.includes('powershell') || shell.includes('pwsh')) {
|
|
290
|
+
return ['-NoLogo', '-ExecutionPolicy', 'Bypass', '-NoExit'];
|
|
291
|
+
}
|
|
292
|
+
return ['/c'];
|
|
293
|
+
}
|
|
294
|
+
return ['-l'];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* 获取会话
|
|
299
|
+
*/
|
|
300
|
+
getSession(sessionId) {
|
|
301
|
+
return this.sessions.get(sessionId);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* 获取所有会话
|
|
306
|
+
*/
|
|
307
|
+
getAllSessions() {
|
|
308
|
+
return Array.from(this.sessions.values());
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* 会话是否存在
|
|
313
|
+
*/
|
|
314
|
+
hasSession(sessionId) {
|
|
315
|
+
return this.sessions.has(sessionId);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* 删除会话
|
|
320
|
+
*/
|
|
321
|
+
async removeSession(sessionId) {
|
|
322
|
+
const session = this.sessions.get(sessionId);
|
|
323
|
+
if (session) {
|
|
324
|
+
session.terminate();
|
|
325
|
+
this.sessions.delete(sessionId);
|
|
326
|
+
logger.info(`Terminal session removed: ${sessionId}`);
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* 处理会话数据
|
|
334
|
+
*/
|
|
335
|
+
handleSessionData(sessionId, data) {
|
|
336
|
+
// 这里可以添加数据处理逻辑,如日志记录、过滤等
|
|
337
|
+
logger.debug(`Session ${sessionId} data: ${data.length} bytes`);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* 处理会话退出
|
|
342
|
+
*/
|
|
343
|
+
handleSessionExit(sessionId, exitInfo) {
|
|
344
|
+
logger.info(`Terminal session ${sessionId} exited: ${JSON.stringify(exitInfo)}`);
|
|
345
|
+
this.sessions.delete(sessionId);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* 清理非活跃会话
|
|
350
|
+
*/
|
|
351
|
+
cleanupInactiveSessions() {
|
|
352
|
+
const now = new Date();
|
|
353
|
+
const timeoutMs = this.defaultOptions.timeout;
|
|
354
|
+
|
|
355
|
+
for (const [sessionId, session] of this.sessions) {
|
|
356
|
+
if (!session.isActive || (now - session.lastActivity) > timeoutMs) {
|
|
357
|
+
logger.info(`Cleaning up inactive session: ${sessionId}`);
|
|
358
|
+
this.removeSession(sessionId);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* 执行命令(非交互式)
|
|
365
|
+
*/
|
|
366
|
+
async executeCommand(command, options = {}) {
|
|
367
|
+
return new Promise((resolve, reject) => {
|
|
368
|
+
const shell = this.getDefaultShellForPlatform();
|
|
369
|
+
const args = process.platform === 'win32' ? ['/c', command] : ['-c', command];
|
|
370
|
+
const cwd = options.workingDirectory || this.defaultOptions.workingDirectory;
|
|
371
|
+
|
|
372
|
+
const child = spawn(shell, args, {
|
|
373
|
+
cwd: cwd,
|
|
374
|
+
env: { ...process.env, ...options.environment },
|
|
375
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
let stdout = '';
|
|
379
|
+
let stderr = '';
|
|
380
|
+
|
|
381
|
+
child.stdout?.on('data', (data) => {
|
|
382
|
+
stdout += data.toString();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
child.stderr?.on('data', (data) => {
|
|
386
|
+
stderr += data.toString();
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
child.on('close', (code) => {
|
|
390
|
+
resolve({
|
|
391
|
+
exitCode: code,
|
|
392
|
+
stdout: stdout,
|
|
393
|
+
stderr: stderr
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
child.on('error', (error) => {
|
|
398
|
+
reject(error);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// 超时处理
|
|
402
|
+
if (options.timeout) {
|
|
403
|
+
setTimeout(() => {
|
|
404
|
+
child.kill();
|
|
405
|
+
reject(new Error(`Command execution timeout: ${command}`));
|
|
406
|
+
}, options.timeout);
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* 获取工作目录
|
|
413
|
+
*/
|
|
414
|
+
async getWorkingDirectory(sessionId) {
|
|
415
|
+
const session = this.getSession(sessionId);
|
|
416
|
+
if (session) {
|
|
417
|
+
return session.workingDirectory;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// 如果没有会话,返回默认工作目录
|
|
421
|
+
return this.defaultOptions.workingDirectory;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* 设置工作目录
|
|
426
|
+
*/
|
|
427
|
+
async setWorkingDirectory(sessionId, directory) {
|
|
428
|
+
const session = this.getSession(sessionId);
|
|
429
|
+
if (session) {
|
|
430
|
+
session.workingDirectory = directory;
|
|
431
|
+
// 发送cd命令到终端
|
|
432
|
+
session.write(`cd "${directory}"\n`);
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* 获取服务状态
|
|
440
|
+
*/
|
|
441
|
+
getStatus() {
|
|
442
|
+
const activeSessions = this.getAllSessions().filter(s => s.isActive);
|
|
443
|
+
return {
|
|
444
|
+
totalSessions: this.sessions.size,
|
|
445
|
+
activeSessions: activeSessions.length,
|
|
446
|
+
maxSessions: this.defaultOptions.maxSessions,
|
|
447
|
+
uptime: process.uptime(),
|
|
448
|
+
platform: process.platform,
|
|
449
|
+
nodeVersion: process.version
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* 关闭服务
|
|
455
|
+
*/
|
|
456
|
+
async shutdown() {
|
|
457
|
+
// 清理所有会话
|
|
458
|
+
for (const [sessionId, session] of this.sessions) {
|
|
459
|
+
session.terminate();
|
|
460
|
+
}
|
|
461
|
+
this.sessions.clear();
|
|
462
|
+
|
|
463
|
+
// 清理定时器
|
|
464
|
+
if (this.cleanupInterval) {
|
|
465
|
+
clearInterval(this.cleanupInterval);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
logger.info('TerminalService shutdown');
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* 获取 PTY 状态信息
|
|
474
|
+
*/
|
|
475
|
+
export function getPtyStatus() {
|
|
476
|
+
return {
|
|
477
|
+
available: PTY_AVAILABLE,
|
|
478
|
+
loadError: PTY_LOAD_ERROR ? PTY_LOAD_ERROR.message : null,
|
|
479
|
+
nodeVersion: process.version,
|
|
480
|
+
platform: process.platform
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* 尝试重新加载 PTY 模块
|
|
486
|
+
*/
|
|
487
|
+
export async function reloadPty() {
|
|
488
|
+
PTY_AVAILABLE = false;
|
|
489
|
+
PTY_LOAD_ERROR = null;
|
|
490
|
+
pty = null;
|
|
491
|
+
return await tryLoadNodePty();
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// 创建单例实例
|
|
495
|
+
export const terminalService = new TerminalService();
|
|
496
|
+
|
|
497
|
+
// 导出类型定义
|
|
498
|
+
export { TerminalSession };
|