@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,300 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 文档生成脚本
|
|
5
|
+
*
|
|
6
|
+
* 自动生成JSDoc和TypeDoc文档,并创建索引页面
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'fs-extra';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
import { execSync } from 'child_process';
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
16
|
+
const projectRoot = path.join(__dirname, '..');
|
|
17
|
+
|
|
18
|
+
// 颜色输出
|
|
19
|
+
const colors = {
|
|
20
|
+
reset: '\x1b[0m',
|
|
21
|
+
bright: '\x1b[1m',
|
|
22
|
+
green: '\x1b[32m',
|
|
23
|
+
red: '\x1b[31m',
|
|
24
|
+
yellow: '\x1b[33m',
|
|
25
|
+
blue: '\x1b[34m',
|
|
26
|
+
cyan: '\x1b[36m'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function log(msg, color = 'reset') {
|
|
30
|
+
console.log(`${colors[color]}${msg}${colors.reset}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function success(msg) {
|
|
34
|
+
log(`✓ ${msg}`, 'green');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function error(msg) {
|
|
38
|
+
log(`✗ ${msg}`, 'red');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function warning(msg) {
|
|
42
|
+
log(`⚠ ${msg}`, 'yellow');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function info(msg) {
|
|
46
|
+
log(`ℹ ${msg}`, 'cyan');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 清理文档目录
|
|
51
|
+
*/
|
|
52
|
+
async function cleanDocsDir() {
|
|
53
|
+
const docsDir = path.join(projectRoot, 'docs');
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
await fs.remove(docsDir);
|
|
57
|
+
await fs.ensureDir(docsDir);
|
|
58
|
+
success('文档目录已清理');
|
|
59
|
+
} catch (err) {
|
|
60
|
+
error(`清理文档目录失败: ${err.message}`);
|
|
61
|
+
throw err;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 生成JSDoc文档
|
|
67
|
+
*/
|
|
68
|
+
async function generateJSDoc() {
|
|
69
|
+
try {
|
|
70
|
+
info('生成JSDoc文档...');
|
|
71
|
+
|
|
72
|
+
const jsdocConfig = path.join(projectRoot, 'jsdoc.conf.json');
|
|
73
|
+
const command = `npx jsdoc -c ${jsdocConfig}`;
|
|
74
|
+
|
|
75
|
+
execSync(command, {
|
|
76
|
+
cwd: projectRoot,
|
|
77
|
+
stdio: 'inherit'
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
success('JSDoc文档生成完成');
|
|
81
|
+
} catch (err) {
|
|
82
|
+
error(`JSDoc文档生成失败: ${err.message}`);
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 生成API文档索引
|
|
89
|
+
*/
|
|
90
|
+
async function generateAPIIndex() {
|
|
91
|
+
try {
|
|
92
|
+
info('生成API文档索引...');
|
|
93
|
+
|
|
94
|
+
const indexPath = path.join(projectRoot, 'docs', 'index.md');
|
|
95
|
+
const content = `# Prompt Manager API 文档
|
|
96
|
+
|
|
97
|
+
本文档包含 Prompt Manager 项目的完整 API 参考。
|
|
98
|
+
|
|
99
|
+
## 文档类型
|
|
100
|
+
|
|
101
|
+
### 📚 JSDoc 文档
|
|
102
|
+
基于代码注释生成的详细 API 文档,包含所有类、方法和参数说明。
|
|
103
|
+
|
|
104
|
+
[查看 JSDoc 文档](./jsdoc/)
|
|
105
|
+
|
|
106
|
+
## 主要模块
|
|
107
|
+
|
|
108
|
+
### 服务层
|
|
109
|
+
- **TerminalService** - 终端会话管理
|
|
110
|
+
- **WebSocketService** - WebSocket 连接管理
|
|
111
|
+
- **ToolMService** - 工具系统管理
|
|
112
|
+
|
|
113
|
+
### 协议层
|
|
114
|
+
- **MCP Server** - Model Context Protocol 服务器
|
|
115
|
+
- **API Routes** - RESTful API 路由
|
|
116
|
+
|
|
117
|
+
### 工具层
|
|
118
|
+
- **Tool Loader** - 工具加载器
|
|
119
|
+
- **Tool Manager** - 工具管理器
|
|
120
|
+
|
|
121
|
+
## 开发指南
|
|
122
|
+
|
|
123
|
+
### 添加新 API
|
|
124
|
+
1. 在代码中添加 JSDoc 注释
|
|
125
|
+
2. 运行 \`npm run docs\` 生成文档
|
|
126
|
+
3. 提交更新后的文档
|
|
127
|
+
|
|
128
|
+
### 文档格式
|
|
129
|
+
- 使用 JSDoc 标准注释格式
|
|
130
|
+
- 包含参数类型、返回值说明
|
|
131
|
+
- 提供使用示例
|
|
132
|
+
|
|
133
|
+
## 版本信息
|
|
134
|
+
|
|
135
|
+
- 生成时间: ${new Date().toISOString()}
|
|
136
|
+
- Node.js 版本: ${process.version}
|
|
137
|
+
- 平台: ${process.platform}
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
*此文档由自动化脚本生成,请勿手动编辑。*
|
|
142
|
+
`;
|
|
143
|
+
|
|
144
|
+
await fs.writeFile(indexPath, content);
|
|
145
|
+
success('API文档索引生成完成');
|
|
146
|
+
} catch (err) {
|
|
147
|
+
error(`API文档索引生成失败: ${err.message}`);
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 生成变更日志
|
|
154
|
+
*/
|
|
155
|
+
async function generateChangelog() {
|
|
156
|
+
try {
|
|
157
|
+
info('生成变更日志...');
|
|
158
|
+
|
|
159
|
+
const changelogPath = path.join(projectRoot, 'docs', 'CHANGELOG.md');
|
|
160
|
+
|
|
161
|
+
// 尝试从git获取最近的提交
|
|
162
|
+
let recentCommits = '';
|
|
163
|
+
try {
|
|
164
|
+
const gitLog = execSync('git log --oneline -10', {
|
|
165
|
+
cwd: projectRoot,
|
|
166
|
+
encoding: 'utf8'
|
|
167
|
+
});
|
|
168
|
+
recentCommits = gitLog;
|
|
169
|
+
} catch (err) {
|
|
170
|
+
warning('无法获取Git提交历史');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const content = `# 变更日志
|
|
174
|
+
|
|
175
|
+
## 最近更新
|
|
176
|
+
|
|
177
|
+
${recentCommits ? '### 最近提交\n\n```\n' + recentCommits + '\n```\n\n' : ''}
|
|
178
|
+
|
|
179
|
+
## 版本历史
|
|
180
|
+
|
|
181
|
+
### v0.0.24 - 当前版本
|
|
182
|
+
- ✨ 新增终端增强功能
|
|
183
|
+
- 🔧 集成 WebSocket + PTY + xterm.js
|
|
184
|
+
- 🧪 添加完整的测试套件
|
|
185
|
+
- 📚 完善文档和代码质量工具
|
|
186
|
+
|
|
187
|
+
### v0.0.23
|
|
188
|
+
- 🐛 修复终端菜单点击无响应问题
|
|
189
|
+
- 🔧 优化webpack配置
|
|
190
|
+
|
|
191
|
+
### v0.0.22
|
|
192
|
+
- ✨ 添加交互式终端功能
|
|
193
|
+
- 🎨 改进用户界面
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
*注意: 此为自动生成的变更日志,详细信息请查看Git提交历史。*
|
|
198
|
+
`;
|
|
199
|
+
|
|
200
|
+
await fs.writeFile(changelogPath, content);
|
|
201
|
+
success('变更日志生成完成');
|
|
202
|
+
} catch (err) {
|
|
203
|
+
error(`变更日志生成失败: ${err.message}`);
|
|
204
|
+
throw err;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 生成项目统计信息
|
|
210
|
+
*/
|
|
211
|
+
async function generateStats() {
|
|
212
|
+
try {
|
|
213
|
+
info('生成项目统计信息...');
|
|
214
|
+
|
|
215
|
+
const statsPath = path.join(projectRoot, 'docs', 'stats.json');
|
|
216
|
+
|
|
217
|
+
// 收集统计信息
|
|
218
|
+
const stats = {
|
|
219
|
+
generatedAt: new Date().toISOString(),
|
|
220
|
+
version: require(path.join(projectRoot, 'package.json')).version,
|
|
221
|
+
nodeVersion: process.version,
|
|
222
|
+
platform: process.platform,
|
|
223
|
+
files: {
|
|
224
|
+
javascript: 0,
|
|
225
|
+
test: 0,
|
|
226
|
+
config: 0,
|
|
227
|
+
total: 0
|
|
228
|
+
},
|
|
229
|
+
lines: {
|
|
230
|
+
code: 0,
|
|
231
|
+
comment: 0,
|
|
232
|
+
total: 0
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// 统计文件数量
|
|
237
|
+
const countFiles = async (dir, pattern) => {
|
|
238
|
+
try {
|
|
239
|
+
const files = await fs.readdir(dir, { withFileTypes: true });
|
|
240
|
+
let count = 0;
|
|
241
|
+
|
|
242
|
+
for (const file of files) {
|
|
243
|
+
const fullPath = path.join(dir, file.name);
|
|
244
|
+
if (file.isDirectory()) {
|
|
245
|
+
count += await countFiles(fullPath, pattern);
|
|
246
|
+
} else if (file.name.match(pattern)) {
|
|
247
|
+
count++;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return count;
|
|
251
|
+
} catch (err) {
|
|
252
|
+
return 0;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
stats.files.javascript = await countFiles(projectRoot, /\.js$/);
|
|
257
|
+
stats.files.test = await countFiles(path.join(projectRoot, 'tests'), /\.test\.js$/);
|
|
258
|
+
stats.files.config = await countFiles(projectRoot, /\.(json|yml|yaml|jsdoc|md)$/);
|
|
259
|
+
stats.files.total = stats.files.javascript + stats.files.test + stats.files.config;
|
|
260
|
+
|
|
261
|
+
await fs.writeFile(statsPath, JSON.stringify(stats, null, 2));
|
|
262
|
+
success('项目统计信息生成完成');
|
|
263
|
+
} catch (err) {
|
|
264
|
+
warning(`项目统计信息生成失败: ${err.message}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* 主函数
|
|
270
|
+
*/
|
|
271
|
+
async function main() {
|
|
272
|
+
log('\n╔════════════════════════════════════════════════════════╗', 'cyan');
|
|
273
|
+
log('║ Prompt Manager 文档生成工具 ║', 'cyan');
|
|
274
|
+
log('╚════════════════════════════════════════════════════════╝', 'cyan');
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
await cleanDocsDir();
|
|
278
|
+
await generateJSDoc();
|
|
279
|
+
await generateAPIIndex();
|
|
280
|
+
await generateChangelog();
|
|
281
|
+
await generateStats();
|
|
282
|
+
|
|
283
|
+
log('\n╔════════════════════════════════════════════════════════╗', 'green');
|
|
284
|
+
log('║ 🎉 生成完成! ║', 'green');
|
|
285
|
+
log('╚════════════════════════════════════════════════════════╝', 'green');
|
|
286
|
+
|
|
287
|
+
log('\n📚 文档位置:', 'blue');
|
|
288
|
+
log(` JSDoc: ${path.join(projectRoot, 'docs', 'jsdoc', 'index.html')}`, 'blue');
|
|
289
|
+
log(` TypeDoc: ${path.join(projectRoot, 'docs', 'typedoc', 'index.html')}`, 'blue');
|
|
290
|
+
log(` 索引: ${path.join(projectRoot, 'docs', 'index.md')}`, 'blue');
|
|
291
|
+
|
|
292
|
+
} catch (error) {
|
|
293
|
+
log('\n❌ 文档生成失败:', 'red');
|
|
294
|
+
log(error.message, 'red');
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// 运行脚本
|
|
300
|
+
main();
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { spawn } from 'child_process';
|
|
9
9
|
import { randomUUID } from 'crypto';
|
|
10
|
+
import fs from 'fs';
|
|
10
11
|
import { logger } from '../utils/logger.js';
|
|
11
12
|
import path from 'path';
|
|
12
13
|
import os from 'os';
|
|
@@ -249,20 +250,37 @@ export class TerminalService {
|
|
|
249
250
|
* 创建PTY进程
|
|
250
251
|
*/
|
|
251
252
|
async createPtyProcess(options) {
|
|
252
|
-
const shell = options.shell || this.getDefaultShellForPlatform();
|
|
253
|
-
const args = this.getShellArgs(shell);
|
|
254
253
|
const cwd = options.workingDirectory || os.homedir();
|
|
255
254
|
const env = { ...process.env, ...options.environment };
|
|
255
|
+
const shells = this.getShellCandidates(options.shell);
|
|
256
|
+
let lastError = null;
|
|
257
|
+
|
|
258
|
+
for (const candidate of shells) {
|
|
259
|
+
if (!candidate) continue;
|
|
260
|
+
const resolvedShell = this.resolveShellPath(candidate);
|
|
261
|
+
if (!resolvedShell) {
|
|
262
|
+
logger.debug(`Shell not found on system: ${candidate}`);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
256
265
|
|
|
257
|
-
|
|
266
|
+
const args = this.getShellArgs(resolvedShell);
|
|
267
|
+
logger.debug(`Creating PTY with shell: ${resolvedShell}, args: ${args.join(' ')}, cwd: ${cwd}`);
|
|
258
268
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
269
|
+
try {
|
|
270
|
+
return pty.default.spawn(resolvedShell, args, {
|
|
271
|
+
name: 'xterm-color',
|
|
272
|
+
cols: options.size.cols,
|
|
273
|
+
rows: options.size.rows,
|
|
274
|
+
cwd: cwd,
|
|
275
|
+
env: env
|
|
276
|
+
});
|
|
277
|
+
} catch (error) {
|
|
278
|
+
lastError = error;
|
|
279
|
+
logger.warn(`Failed to spawn shell ${resolvedShell}: ${error.message}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
throw lastError || new Error('Unable to create PTY session: no suitable shell found');
|
|
266
284
|
}
|
|
267
285
|
|
|
268
286
|
/**
|
|
@@ -273,7 +291,8 @@ export class TerminalService {
|
|
|
273
291
|
case 'win32':
|
|
274
292
|
return process.env.COMSPEC || 'cmd.exe';
|
|
275
293
|
case 'darwin':
|
|
276
|
-
|
|
294
|
+
// 在 macOS 上优先使用用户的 SHELL 环境变量
|
|
295
|
+
return process.env.SHELL || '/bin/zsh' || '/bin/bash';
|
|
277
296
|
case 'linux':
|
|
278
297
|
return process.env.SHELL || '/bin/bash';
|
|
279
298
|
default:
|
|
@@ -291,9 +310,63 @@ export class TerminalService {
|
|
|
291
310
|
}
|
|
292
311
|
return ['/c'];
|
|
293
312
|
}
|
|
313
|
+
|
|
314
|
+
// 某些精简 shell(如 /bin/sh)不支持 -l
|
|
315
|
+
if (shell.endsWith('/sh')) {
|
|
316
|
+
return ['-i'];
|
|
317
|
+
}
|
|
318
|
+
|
|
294
319
|
return ['-l'];
|
|
295
320
|
}
|
|
296
321
|
|
|
322
|
+
/**
|
|
323
|
+
* 获取 shell 候选列表(按优先级)
|
|
324
|
+
*/
|
|
325
|
+
getShellCandidates(preferredShell) {
|
|
326
|
+
const candidates = [];
|
|
327
|
+
|
|
328
|
+
if (preferredShell) candidates.push(preferredShell);
|
|
329
|
+
if (process.env.SHELL) candidates.push(process.env.SHELL);
|
|
330
|
+
|
|
331
|
+
if (process.platform === 'darwin') {
|
|
332
|
+
candidates.push('/bin/zsh', '/bin/bash', '/bin/sh');
|
|
333
|
+
} else if (process.platform === 'linux') {
|
|
334
|
+
candidates.push('/bin/bash', '/bin/sh');
|
|
335
|
+
} else if (process.platform === 'win32') {
|
|
336
|
+
candidates.push(process.env.COMSPEC || 'cmd.exe');
|
|
337
|
+
} else {
|
|
338
|
+
candidates.push('/bin/sh');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return [...new Set(candidates)];
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* 确保 shell 路径在当前系统存在
|
|
346
|
+
*/
|
|
347
|
+
resolveShellPath(shellPath) {
|
|
348
|
+
// 绝对路径直接检查
|
|
349
|
+
if (shellPath.startsWith('/')) {
|
|
350
|
+
return fs.existsSync(shellPath) ? shellPath : null;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Windows 可执行文件
|
|
354
|
+
if (process.platform === 'win32') {
|
|
355
|
+
return shellPath;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// 如果是相对路径,尝试在常见目录查找
|
|
359
|
+
const searchPaths = ['/bin', '/usr/bin', '/usr/local/bin'];
|
|
360
|
+
for (const base of searchPaths) {
|
|
361
|
+
const fullPath = path.join(base, shellPath);
|
|
362
|
+
if (fs.existsSync(fullPath)) {
|
|
363
|
+
return fullPath;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
|
|
297
370
|
/**
|
|
298
371
|
* 获取会话
|
|
299
372
|
*/
|