@becrafter/prompt-manager 0.1.16 → 0.1.17
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.
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@becrafter/prompt-desktop",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@becrafter/prompt-desktop",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.17",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@becrafter/prompt-manager-core": "file:../../packages/server",
|
|
12
12
|
"@modelcontextprotocol/sdk": "^1.20.2",
|
package/app/desktop/package.json
CHANGED
package/env.example
CHANGED
package/package.json
CHANGED
package/packages/server/app.js
CHANGED
|
@@ -104,8 +104,8 @@ async function sendIndexHtml(req, res) {
|
|
|
104
104
|
// 从 adminUiRoot 中提取 asar 文件路径和相对路径
|
|
105
105
|
const asarPathMatch = adminUiRoot.match(/^(.*?\.asar)(\/.*)?$/);
|
|
106
106
|
const asarFilePath = asarPathMatch ? asarPathMatch[1] : adminUiRoot.replace(/\/.*$/, '.asar');
|
|
107
|
-
const relativePathInAsar = asarPathMatch && asarPathMatch[2] ? asarPathMatch[2].substring(1) : '
|
|
108
|
-
const indexPath = path.posix.join(relativePathInAsar, '
|
|
107
|
+
const relativePathInAsar = asarPathMatch && asarPathMatch[2] ? asarPathMatch[2].substring(1) : 'web';
|
|
108
|
+
const indexPath = path.posix.join(relativePathInAsar, 'index.html');
|
|
109
109
|
|
|
110
110
|
// 检查文件是否存在
|
|
111
111
|
const stat = asar.statFile(asarFilePath, indexPath);
|
|
@@ -121,17 +121,7 @@ async function sendIndexHtml(req, res) {
|
|
|
121
121
|
res.status(500).send('Internal Server Error');
|
|
122
122
|
}
|
|
123
123
|
} else {
|
|
124
|
-
|
|
125
|
-
const indexPath = path.join(adminUiRoot, 'index.html');
|
|
126
|
-
const adminPath = path.join(adminUiRoot, 'admin.html');
|
|
127
|
-
|
|
128
|
-
if (fs.existsSync(indexPath)) {
|
|
129
|
-
res.sendFile(indexPath);
|
|
130
|
-
} else if (fs.existsSync(adminPath)) {
|
|
131
|
-
res.sendFile(adminPath);
|
|
132
|
-
} else {
|
|
133
|
-
res.status(404).send('Admin UI not found');
|
|
134
|
-
}
|
|
124
|
+
res.sendFile(path.join(adminUiRoot, 'index.html'));
|
|
135
125
|
}
|
|
136
126
|
}
|
|
137
127
|
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
import { spawn } from 'child_process';
|
|
9
9
|
import { randomUUID } from 'crypto';
|
|
10
|
-
import fs from 'fs';
|
|
11
10
|
import { logger } from '../utils/logger.js';
|
|
12
11
|
import path from 'path';
|
|
13
12
|
import os from 'os';
|
|
@@ -69,7 +68,7 @@ class TerminalSession {
|
|
|
69
68
|
case 'win32':
|
|
70
69
|
return process.env.COMSPEC || 'cmd.exe';
|
|
71
70
|
case 'darwin':
|
|
72
|
-
return process.env.SHELL || '/bin/
|
|
71
|
+
return process.env.SHELL || '/bin/bash';
|
|
73
72
|
case 'linux':
|
|
74
73
|
return process.env.SHELL || '/bin/bash';
|
|
75
74
|
default:
|
|
@@ -83,34 +82,15 @@ class TerminalSession {
|
|
|
83
82
|
setupPtyEvents() {
|
|
84
83
|
if (!this.pty) return;
|
|
85
84
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
this.
|
|
89
|
-
|
|
90
|
-
this.emit('data', data);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
this.pty.on('exit', (exitCode, signal) => {
|
|
94
|
-
this.isActive = false;
|
|
95
|
-
this.emit('exit', { exitCode, signal });
|
|
96
|
-
});
|
|
97
|
-
} else {
|
|
98
|
-
// 对于fallback PTY进程(child_process.spawn)
|
|
99
|
-
this.pty.process.stdout?.on('data', (data) => {
|
|
100
|
-
this.lastActivity = new Date();
|
|
101
|
-
this.emit('data', data);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
this.pty.process.stderr?.on('data', (data) => {
|
|
105
|
-
this.lastActivity = new Date();
|
|
106
|
-
this.emit('data', data);
|
|
107
|
-
});
|
|
85
|
+
this.pty.on('data', (data) => {
|
|
86
|
+
this.lastActivity = new Date();
|
|
87
|
+
this.emit('data', data);
|
|
88
|
+
});
|
|
108
89
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
90
|
+
this.pty.on('exit', (exitCode, signal) => {
|
|
91
|
+
this.isActive = false;
|
|
92
|
+
this.emit('exit', { exitCode, signal });
|
|
93
|
+
});
|
|
114
94
|
}
|
|
115
95
|
|
|
116
96
|
/**
|
|
@@ -120,14 +100,7 @@ class TerminalSession {
|
|
|
120
100
|
if (!this.isActive || !this.pty) {
|
|
121
101
|
throw new Error('Terminal session is not active');
|
|
122
102
|
}
|
|
123
|
-
|
|
124
|
-
if (this.pty.isFallback) {
|
|
125
|
-
// fallback PTY使用child_process
|
|
126
|
-
this.pty.process.stdin?.write(data);
|
|
127
|
-
} else {
|
|
128
|
-
// 正常PTY
|
|
129
|
-
this.pty.write(data);
|
|
130
|
-
}
|
|
103
|
+
this.pty.write(data);
|
|
131
104
|
this.lastActivity = new Date();
|
|
132
105
|
}
|
|
133
106
|
|
|
@@ -138,14 +111,7 @@ class TerminalSession {
|
|
|
138
111
|
if (!this.isActive || !this.pty) {
|
|
139
112
|
throw new Error('Terminal session is not active');
|
|
140
113
|
}
|
|
141
|
-
|
|
142
|
-
if (this.pty.isFallback) {
|
|
143
|
-
// fallback PTY不支持resize,记录日志
|
|
144
|
-
logger.debug(`Fallback PTY resize requested: ${cols}x${rows} (not supported)`);
|
|
145
|
-
} else {
|
|
146
|
-
// 正常PTY支持resize
|
|
147
|
-
this.pty.resize(cols, rows);
|
|
148
|
-
}
|
|
114
|
+
this.pty.resize(cols, rows);
|
|
149
115
|
this.size = { cols, rows };
|
|
150
116
|
}
|
|
151
117
|
|
|
@@ -154,13 +120,7 @@ class TerminalSession {
|
|
|
154
120
|
*/
|
|
155
121
|
terminate() {
|
|
156
122
|
if (this.pty) {
|
|
157
|
-
|
|
158
|
-
// fallback PTY使用child_process
|
|
159
|
-
this.pty.process.kill();
|
|
160
|
-
} else {
|
|
161
|
-
// 正常PTY
|
|
162
|
-
this.pty.kill();
|
|
163
|
-
}
|
|
123
|
+
this.pty.kill();
|
|
164
124
|
}
|
|
165
125
|
this.isActive = false;
|
|
166
126
|
}
|
|
@@ -289,122 +249,20 @@ export class TerminalService {
|
|
|
289
249
|
* 创建PTY进程
|
|
290
250
|
*/
|
|
291
251
|
async createPtyProcess(options) {
|
|
252
|
+
const shell = options.shell || this.getDefaultShellForPlatform();
|
|
253
|
+
const args = this.getShellArgs(shell);
|
|
292
254
|
const cwd = options.workingDirectory || os.homedir();
|
|
293
255
|
const env = { ...process.env, ...options.environment };
|
|
294
|
-
const shells = this.getShellCandidates(options.shell);
|
|
295
|
-
let lastError = null;
|
|
296
|
-
|
|
297
|
-
// 在macOS上确保PATH包含常用目录
|
|
298
|
-
if (process.platform === 'darwin') {
|
|
299
|
-
const defaultPath = '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin';
|
|
300
|
-
if (!env.PATH || env.PATH === '') {
|
|
301
|
-
env.PATH = defaultPath;
|
|
302
|
-
} else if (!env.PATH.includes('/usr/local/bin')) {
|
|
303
|
-
env.PATH = `${env.PATH}:${defaultPath}`;
|
|
304
|
-
}
|
|
305
|
-
logger.debug(`macOS PATH configured: ${env.PATH}`);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
for (const candidate of shells) {
|
|
309
|
-
if (!candidate) continue;
|
|
310
|
-
const resolvedShell = this.resolveShellPath(candidate);
|
|
311
|
-
if (!resolvedShell) {
|
|
312
|
-
logger.debug(`Shell not found on system: ${candidate}`);
|
|
313
|
-
continue;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const args = this.getShellArgs(resolvedShell);
|
|
317
|
-
logger.debug(`Creating PTY with shell: ${resolvedShell}, args: ${args.join(' ')}, cwd: ${cwd}`);
|
|
318
256
|
|
|
319
|
-
|
|
320
|
-
const ptyProcess = pty.default.spawn(resolvedShell, args, {
|
|
321
|
-
name: 'xterm-color',
|
|
322
|
-
cols: options.size.cols,
|
|
323
|
-
rows: options.size.rows,
|
|
324
|
-
cwd: cwd,
|
|
325
|
-
env: env
|
|
326
|
-
});
|
|
257
|
+
logger.debug(`Creating PTY with shell: ${shell}, args: ${args.join(' ')}, cwd: ${cwd}`);
|
|
327
258
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
logger.debug(`PTY data received: ${data.length} bytes`);
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
ptyProcess.on('exit', (code, signal) => {
|
|
334
|
-
logger.debug(`PTY process exited: code=${code}, signal=${signal}`);
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
return ptyProcess;
|
|
338
|
-
} catch (error) {
|
|
339
|
-
lastError = error;
|
|
340
|
-
logger.warn(`Failed to spawn shell ${resolvedShell}: ${error.message}`);
|
|
341
|
-
|
|
342
|
-
// 如果是posix_spawnp失败,尝试备用方案
|
|
343
|
-
if (error.message.includes('posix_spawnp')) {
|
|
344
|
-
logger.warn('posix_spawnp failed, trying alternative approach...');
|
|
345
|
-
try {
|
|
346
|
-
// 尝试使用child_process.spawn作为备用方案
|
|
347
|
-
const { spawn } = await import('child_process');
|
|
348
|
-
const child = spawn(resolvedShell, args, {
|
|
349
|
-
cwd: cwd,
|
|
350
|
-
env: env,
|
|
351
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
352
|
-
shell: false
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
// 包装child_process.spawn的结果以兼容PTY接口
|
|
356
|
-
return this.createFallbackPtyProcess(child, options);
|
|
357
|
-
} catch (fallbackError) {
|
|
358
|
-
logger.warn(`Fallback spawn also failed: ${fallbackError.message}`);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
throw lastError || new Error('Unable to create PTY session: no suitable shell found');
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* 创建备用PTY进程(使用child_process.spawn)
|
|
369
|
-
*/
|
|
370
|
-
createFallbackPtyProcess(childProcess, options) {
|
|
371
|
-
// 创建一个兼容PTY接口的对象
|
|
372
|
-
const fallbackPty = {
|
|
373
|
-
pid: childProcess.pid,
|
|
259
|
+
return pty.default.spawn(shell, args, {
|
|
260
|
+
name: 'xterm-color',
|
|
374
261
|
cols: options.size.cols,
|
|
375
262
|
rows: options.size.rows,
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
write(data) {
|
|
380
|
-
if (childProcess.stdin) {
|
|
381
|
-
childProcess.stdin.write(data);
|
|
382
|
-
}
|
|
383
|
-
},
|
|
384
|
-
|
|
385
|
-
resize(cols, rows) {
|
|
386
|
-
this.cols = cols;
|
|
387
|
-
this.rows = rows;
|
|
388
|
-
// child_process不支持resize,记录日志
|
|
389
|
-
logger.debug(`Fallback PTY resize requested: ${cols}x${rows} (not supported)`);
|
|
390
|
-
},
|
|
391
|
-
|
|
392
|
-
kill(signal = 'SIGTERM') {
|
|
393
|
-
childProcess.kill(signal);
|
|
394
|
-
},
|
|
395
|
-
|
|
396
|
-
on(event, callback) {
|
|
397
|
-
if (event === 'data') {
|
|
398
|
-
childProcess.stdout?.on('data', callback);
|
|
399
|
-
childProcess.stderr?.on('data', callback);
|
|
400
|
-
} else if (event === 'exit') {
|
|
401
|
-
childProcess.on('exit', callback);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
};
|
|
405
|
-
|
|
406
|
-
logger.info('Created fallback PTY process (limited functionality)');
|
|
407
|
-
return fallbackPty;
|
|
263
|
+
cwd: cwd,
|
|
264
|
+
env: env
|
|
265
|
+
});
|
|
408
266
|
}
|
|
409
267
|
|
|
410
268
|
/**
|
|
@@ -415,8 +273,7 @@ export class TerminalService {
|
|
|
415
273
|
case 'win32':
|
|
416
274
|
return process.env.COMSPEC || 'cmd.exe';
|
|
417
275
|
case 'darwin':
|
|
418
|
-
|
|
419
|
-
return process.env.SHELL || '/bin/zsh';
|
|
276
|
+
return process.env.SHELL || '/bin/bash';
|
|
420
277
|
case 'linux':
|
|
421
278
|
return process.env.SHELL || '/bin/bash';
|
|
422
279
|
default:
|
|
@@ -434,63 +291,9 @@ export class TerminalService {
|
|
|
434
291
|
}
|
|
435
292
|
return ['/c'];
|
|
436
293
|
}
|
|
437
|
-
|
|
438
|
-
// 某些精简 shell(如 /bin/sh)不支持 -l
|
|
439
|
-
if (shell.endsWith('/sh')) {
|
|
440
|
-
return ['-i'];
|
|
441
|
-
}
|
|
442
|
-
|
|
443
294
|
return ['-l'];
|
|
444
295
|
}
|
|
445
296
|
|
|
446
|
-
/**
|
|
447
|
-
* 获取 shell 候选列表(按优先级)
|
|
448
|
-
*/
|
|
449
|
-
getShellCandidates(preferredShell) {
|
|
450
|
-
const candidates = [];
|
|
451
|
-
|
|
452
|
-
if (preferredShell) candidates.push(preferredShell);
|
|
453
|
-
if (process.env.SHELL) candidates.push(process.env.SHELL);
|
|
454
|
-
|
|
455
|
-
if (process.platform === 'darwin') {
|
|
456
|
-
candidates.push('/bin/zsh', '/bin/bash', '/bin/sh');
|
|
457
|
-
} else if (process.platform === 'linux') {
|
|
458
|
-
candidates.push('/bin/bash', '/bin/sh');
|
|
459
|
-
} else if (process.platform === 'win32') {
|
|
460
|
-
candidates.push(process.env.COMSPEC || 'cmd.exe');
|
|
461
|
-
} else {
|
|
462
|
-
candidates.push('/bin/sh');
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
return [...new Set(candidates)];
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* 确保 shell 路径在当前系统存在
|
|
470
|
-
*/
|
|
471
|
-
resolveShellPath(shellPath) {
|
|
472
|
-
// 绝对路径直接检查
|
|
473
|
-
if (shellPath.startsWith('/')) {
|
|
474
|
-
return fs.existsSync(shellPath) ? shellPath : null;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// Windows 可执行文件
|
|
478
|
-
if (process.platform === 'win32') {
|
|
479
|
-
return shellPath;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// 如果是相对路径,尝试在常见目录查找
|
|
483
|
-
const searchPaths = ['/bin', '/usr/bin', '/usr/local/bin'];
|
|
484
|
-
for (const base of searchPaths) {
|
|
485
|
-
const fullPath = path.join(base, shellPath);
|
|
486
|
-
if (fs.existsSync(fullPath)) {
|
|
487
|
-
return fullPath;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
return null;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
297
|
/**
|
|
495
298
|
* 获取会话
|
|
496
299
|
*/
|
|
@@ -121,7 +121,7 @@ export class Config {
|
|
|
121
121
|
|
|
122
122
|
// 其他配置
|
|
123
123
|
this.serverName = process.env.MCP_SERVER_NAME || 'prompt-manager';
|
|
124
|
-
this.serverVersion = process.env.MCP_SERVER_VERSION || '0.1.
|
|
124
|
+
this.serverVersion = process.env.MCP_SERVER_VERSION || '0.1.17';
|
|
125
125
|
this.logLevel = process.env.LOG_LEVEL || 'info';
|
|
126
126
|
this.maxPrompts = parseInt(process.env.MAX_PROMPTS) || 1000;
|
|
127
127
|
this.recursiveScan = process.env.RECURSIVE_SCAN !== 'false'; // 默认启用递归扫描
|