@adversity/coding-tool-x 2.4.0 → 2.4.2
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/CHANGELOG.md +29 -0
- package/dist/web/assets/icons-BkBtk3H1.js +1 -0
- package/dist/web/assets/index-Cgq2DyzS.css +41 -0
- package/dist/web/assets/index-DOsR4qc6.js +14 -0
- package/dist/web/assets/naive-ui-D-gb0WfN.js +1 -0
- package/dist/web/assets/vendors-Bd5vxA1-.js +7 -0
- package/dist/web/assets/vue-vendor-hRp8vsrL.js +44 -0
- package/dist/web/index.html +6 -2
- package/package.json +1 -1
- package/src/commands/export-config.js +205 -0
- package/src/server/api/config-sync.js +155 -0
- package/src/server/api/projects.js +2 -2
- package/src/server/api/sessions.js +70 -70
- package/src/server/api/skills.js +206 -0
- package/src/server/api/terminal.js +26 -0
- package/src/server/api/workspaces.js +2 -2
- package/src/server/index.js +5 -2
- package/src/server/services/config-export-service.js +229 -23
- package/src/server/services/config-sync-service.js +515 -0
- package/src/server/services/enhanced-cache.js +196 -0
- package/src/server/services/pty-manager.js +35 -1
- package/src/server/services/sessions.js +133 -46
- package/src/server/services/skill-service.js +252 -2
- package/src/server/services/terminal-commands.js +5 -0
- package/src/server/services/workspace-service.js +90 -51
- package/src/server/websocket-server.js +4 -1
- package/dist/web/assets/index-Bu1oPcKu.js +0 -4009
- package/dist/web/assets/index-XSok7-mN.css +0 -41
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置同步 API 路由
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const express = require('express');
|
|
6
|
+
const { ConfigSyncService } = require('../services/config-sync-service');
|
|
7
|
+
|
|
8
|
+
const router = express.Router();
|
|
9
|
+
const configSyncService = new ConfigSyncService();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 获取可同步的配置列表
|
|
13
|
+
* GET /api/config-sync/available
|
|
14
|
+
* Query: source=global|workspace, projectPath=...
|
|
15
|
+
*/
|
|
16
|
+
router.get('/available', (req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
const { source = 'global', projectPath } = req.query;
|
|
19
|
+
|
|
20
|
+
if (source === 'workspace' && !projectPath) {
|
|
21
|
+
return res.status(400).json({
|
|
22
|
+
success: false,
|
|
23
|
+
message: '获取工作区配置需要指定 projectPath'
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const configs = configSyncService.getAvailableConfigs(source, projectPath);
|
|
28
|
+
|
|
29
|
+
res.json({
|
|
30
|
+
success: true,
|
|
31
|
+
source,
|
|
32
|
+
configs
|
|
33
|
+
});
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.error('[ConfigSync API] Get available configs error:', err);
|
|
36
|
+
res.status(500).json({
|
|
37
|
+
success: false,
|
|
38
|
+
message: err.message
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 获取同步统计信息
|
|
45
|
+
* GET /api/config-sync/stats
|
|
46
|
+
* Query: projectPath=...
|
|
47
|
+
*/
|
|
48
|
+
router.get('/stats', (req, res) => {
|
|
49
|
+
try {
|
|
50
|
+
const { projectPath } = req.query;
|
|
51
|
+
const stats = configSyncService.getStats(projectPath);
|
|
52
|
+
|
|
53
|
+
res.json({
|
|
54
|
+
success: true,
|
|
55
|
+
stats
|
|
56
|
+
});
|
|
57
|
+
} catch (err) {
|
|
58
|
+
console.error('[ConfigSync API] Get stats error:', err);
|
|
59
|
+
res.status(500).json({
|
|
60
|
+
success: false,
|
|
61
|
+
message: err.message
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 预览同步结果
|
|
68
|
+
* POST /api/config-sync/preview
|
|
69
|
+
* Body: { source, target, configTypes, projectPath, selectedItems }
|
|
70
|
+
*/
|
|
71
|
+
router.post('/preview', (req, res) => {
|
|
72
|
+
try {
|
|
73
|
+
const { source, target, configTypes, projectPath, selectedItems } = req.body;
|
|
74
|
+
|
|
75
|
+
if (!source || !target) {
|
|
76
|
+
return res.status(400).json({
|
|
77
|
+
success: false,
|
|
78
|
+
message: '请指定源和目标'
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!configTypes || configTypes.length === 0) {
|
|
83
|
+
return res.status(400).json({
|
|
84
|
+
success: false,
|
|
85
|
+
message: '请选择要同步的配置类型'
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const preview = configSyncService.previewSync({
|
|
90
|
+
source,
|
|
91
|
+
target,
|
|
92
|
+
configTypes,
|
|
93
|
+
projectPath,
|
|
94
|
+
selectedItems
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
res.json({
|
|
98
|
+
success: true,
|
|
99
|
+
preview
|
|
100
|
+
});
|
|
101
|
+
} catch (err) {
|
|
102
|
+
console.error('[ConfigSync API] Preview sync error:', err);
|
|
103
|
+
res.status(500).json({
|
|
104
|
+
success: false,
|
|
105
|
+
message: err.message
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 执行同步
|
|
112
|
+
* POST /api/config-sync/execute
|
|
113
|
+
* Body: { source, target, configTypes, projectPath, selectedItems, overwrite }
|
|
114
|
+
*/
|
|
115
|
+
router.post('/execute', (req, res) => {
|
|
116
|
+
try {
|
|
117
|
+
const { source, target, configTypes, projectPath, selectedItems, overwrite = false } = req.body;
|
|
118
|
+
|
|
119
|
+
if (!source || !target) {
|
|
120
|
+
return res.status(400).json({
|
|
121
|
+
success: false,
|
|
122
|
+
message: '请指定源和目标'
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!configTypes || configTypes.length === 0) {
|
|
127
|
+
return res.status(400).json({
|
|
128
|
+
success: false,
|
|
129
|
+
message: '请选择要同步的配置类型'
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const result = configSyncService.executeSync({
|
|
134
|
+
source,
|
|
135
|
+
target,
|
|
136
|
+
configTypes,
|
|
137
|
+
projectPath,
|
|
138
|
+
selectedItems,
|
|
139
|
+
overwrite
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
res.json({
|
|
143
|
+
success: true,
|
|
144
|
+
result
|
|
145
|
+
});
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.error('[ConfigSync API] Execute sync error:', err);
|
|
148
|
+
res.status(500).json({
|
|
149
|
+
success: false,
|
|
150
|
+
message: err.message
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
module.exports = router;
|
|
@@ -4,9 +4,9 @@ const { getProjectsWithStats, saveProjectOrder, getProjectOrder, deleteProject }
|
|
|
4
4
|
|
|
5
5
|
module.exports = (config) => {
|
|
6
6
|
// GET /api/projects - Get all projects with stats
|
|
7
|
-
router.get('/', (req, res) => {
|
|
7
|
+
router.get('/', async (req, res) => {
|
|
8
8
|
try {
|
|
9
|
-
const projects = getProjectsWithStats(config);
|
|
9
|
+
const projects = await getProjectsWithStats(config);
|
|
10
10
|
const order = getProjectOrder(config);
|
|
11
11
|
|
|
12
12
|
// Sort projects by saved order
|
|
@@ -45,10 +45,10 @@ module.exports = (config) => {
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
// GET /api/sessions/:projectName - Get sessions for a project
|
|
48
|
-
router.get('/:projectName', (req, res) => {
|
|
48
|
+
router.get('/:projectName', async (req, res) => {
|
|
49
49
|
try {
|
|
50
50
|
const { projectName } = req.params;
|
|
51
|
-
const result = getSessionsForProject(config, projectName);
|
|
51
|
+
const result = await getSessionsForProject(config, projectName);
|
|
52
52
|
const aliases = loadAliases();
|
|
53
53
|
|
|
54
54
|
// Parse project path info
|
|
@@ -222,8 +222,8 @@ module.exports = (config) => {
|
|
|
222
222
|
});
|
|
223
223
|
|
|
224
224
|
// GET /api/sessions/:projectName/:sessionId/messages - Get session messages with pagination
|
|
225
|
-
router.get('/:projectName/:sessionId/messages', async (req, res) => {
|
|
226
|
-
|
|
225
|
+
router.get('/:projectName/:sessionId/messages', async (req, res) => {
|
|
226
|
+
try {
|
|
227
227
|
const { projectName, sessionId } = req.params;
|
|
228
228
|
const { page = 1, limit = 20, order = 'desc' } = req.query;
|
|
229
229
|
|
|
@@ -271,86 +271,86 @@ router.get('/:projectName/:sessionId/messages', async (req, res) => {
|
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
// Read and parse session file
|
|
274
|
-
|
|
275
|
-
|
|
274
|
+
const allMessages = [];
|
|
275
|
+
const metadata = {};
|
|
276
276
|
|
|
277
|
-
|
|
278
|
-
|
|
277
|
+
const stream = fs.createReadStream(sessionFile, { encoding: 'utf8' });
|
|
278
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
279
279
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
280
|
+
try {
|
|
281
|
+
for await (const line of rl) {
|
|
282
|
+
if (!line.trim()) continue;
|
|
283
|
+
try {
|
|
284
|
+
const json = JSON.parse(line);
|
|
285
285
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
286
|
+
if (json.type === 'summary' && json.summary) {
|
|
287
|
+
metadata.summary = json.summary;
|
|
288
|
+
}
|
|
289
|
+
if (json.gitBranch) {
|
|
290
|
+
metadata.gitBranch = json.gitBranch;
|
|
291
|
+
}
|
|
292
|
+
if (json.cwd) {
|
|
293
|
+
metadata.cwd = json.cwd;
|
|
294
|
+
}
|
|
295
295
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
296
|
+
if (json.type === 'user' || json.type === 'assistant') {
|
|
297
|
+
const message = {
|
|
298
|
+
type: json.type,
|
|
299
|
+
content: null,
|
|
300
|
+
timestamp: json.timestamp || null,
|
|
301
|
+
model: json.model || null
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
if (json.type === 'user') {
|
|
305
|
+
if (typeof json.message?.content === 'string') {
|
|
306
|
+
message.content = json.message.content;
|
|
307
|
+
} else if (Array.isArray(json.message?.content)) {
|
|
308
|
+
const parts = [];
|
|
309
|
+
for (const item of json.message.content) {
|
|
310
|
+
if (item.type === 'text' && item.text) {
|
|
311
|
+
parts.push(item.text);
|
|
312
|
+
} else if (item.type === 'tool_result') {
|
|
313
|
+
const resultContent = typeof item.content === 'string'
|
|
314
|
+
? item.content
|
|
315
|
+
: JSON.stringify(item.content, null, 2);
|
|
316
|
+
parts.push(`**[工具结果]**\n\`\`\`\n${resultContent}\n\`\`\``);
|
|
317
|
+
} else if (item.type === 'image') {
|
|
318
|
+
parts.push('[图片]');
|
|
319
|
+
}
|
|
319
320
|
}
|
|
321
|
+
message.content = parts.join('\n\n') || '[工具交互]';
|
|
320
322
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
parts.push(`**[思考]**\n${item.thinking}`);
|
|
323
|
+
} else if (json.type === 'assistant') {
|
|
324
|
+
if (Array.isArray(json.message?.content)) {
|
|
325
|
+
const parts = [];
|
|
326
|
+
for (const item of json.message.content) {
|
|
327
|
+
if (item.type === 'text' && item.text) {
|
|
328
|
+
parts.push(item.text);
|
|
329
|
+
} else if (item.type === 'tool_use') {
|
|
330
|
+
const inputStr = JSON.stringify(item.input, null, 2);
|
|
331
|
+
parts.push(`**[调用工具: ${item.name}]**\n\`\`\`json\n${inputStr}\n\`\`\``);
|
|
332
|
+
} else if (item.type === 'thinking' && item.thinking) {
|
|
333
|
+
parts.push(`**[思考]**\n${item.thinking}`);
|
|
334
|
+
}
|
|
334
335
|
}
|
|
336
|
+
message.content = parts.join('\n\n') || '[处理中...]';
|
|
337
|
+
} else if (typeof json.message?.content === 'string') {
|
|
338
|
+
message.content = json.message.content;
|
|
335
339
|
}
|
|
336
|
-
message.content = parts.join('\n\n') || '[处理中...]';
|
|
337
|
-
} else if (typeof json.message?.content === 'string') {
|
|
338
|
-
message.content = json.message.content;
|
|
339
340
|
}
|
|
340
|
-
}
|
|
341
341
|
|
|
342
|
-
|
|
343
|
-
|
|
342
|
+
if (message.content && message.content !== 'Warmup') {
|
|
343
|
+
allMessages.push(message);
|
|
344
|
+
}
|
|
344
345
|
}
|
|
346
|
+
} catch (err) {
|
|
347
|
+
// Skip invalid lines
|
|
345
348
|
}
|
|
346
|
-
} catch (err) {
|
|
347
|
-
// Skip invalid lines
|
|
348
349
|
}
|
|
350
|
+
} finally {
|
|
351
|
+
rl.close();
|
|
352
|
+
stream.destroy();
|
|
349
353
|
}
|
|
350
|
-
} finally {
|
|
351
|
-
rl.close();
|
|
352
|
-
stream.destroy();
|
|
353
|
-
}
|
|
354
354
|
|
|
355
355
|
// Sort messages (desc = newest first)
|
|
356
356
|
if (order === 'desc') {
|
package/src/server/api/skills.js
CHANGED
|
@@ -312,6 +312,212 @@ router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
|
312
312
|
}
|
|
313
313
|
});
|
|
314
314
|
|
|
315
|
+
// ==================== 多文件技能管理 API ====================
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* 创建带多文件的技能
|
|
319
|
+
* POST /api/skills/create-with-files
|
|
320
|
+
* Body: { directory, files: [{path, content, isBase64?}] }
|
|
321
|
+
*/
|
|
322
|
+
router.post('/create-with-files', (req, res) => {
|
|
323
|
+
try {
|
|
324
|
+
const { directory, files } = req.body;
|
|
325
|
+
|
|
326
|
+
if (!directory) {
|
|
327
|
+
return res.status(400).json({
|
|
328
|
+
success: false,
|
|
329
|
+
message: '请输入目录名称'
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 校验目录名:只允许英文、数字、横杠、下划线
|
|
334
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(directory)) {
|
|
335
|
+
return res.status(400).json({
|
|
336
|
+
success: false,
|
|
337
|
+
message: '目录名只能包含英文、数字、横杠和下划线'
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (!files || !Array.isArray(files) || files.length === 0) {
|
|
342
|
+
return res.status(400).json({
|
|
343
|
+
success: false,
|
|
344
|
+
message: '请提供文件列表'
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const result = skillService.createSkillWithFiles({ directory, files });
|
|
349
|
+
|
|
350
|
+
res.json({
|
|
351
|
+
success: true,
|
|
352
|
+
...result
|
|
353
|
+
});
|
|
354
|
+
} catch (err) {
|
|
355
|
+
console.error('[Skills API] Create skill with files error:', err);
|
|
356
|
+
res.status(500).json({
|
|
357
|
+
success: false,
|
|
358
|
+
message: err.message
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* 获取技能文件列表
|
|
365
|
+
* GET /api/skills/:directory/files
|
|
366
|
+
*/
|
|
367
|
+
router.get('/:directory/files', (req, res) => {
|
|
368
|
+
try {
|
|
369
|
+
const { directory } = req.params;
|
|
370
|
+
const files = skillService.getSkillFiles(directory);
|
|
371
|
+
|
|
372
|
+
res.json({
|
|
373
|
+
success: true,
|
|
374
|
+
directory,
|
|
375
|
+
files
|
|
376
|
+
});
|
|
377
|
+
} catch (err) {
|
|
378
|
+
console.error('[Skills API] Get skill files error:', err);
|
|
379
|
+
res.status(500).json({
|
|
380
|
+
success: false,
|
|
381
|
+
message: err.message
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* 获取技能文件内容
|
|
388
|
+
* GET /api/skills/:directory/files/:filePath
|
|
389
|
+
* 注意:filePath 可能包含子目录,使用通配符
|
|
390
|
+
*/
|
|
391
|
+
router.get('/:directory/file/*', (req, res) => {
|
|
392
|
+
try {
|
|
393
|
+
const { directory } = req.params;
|
|
394
|
+
const filePath = req.params[0];
|
|
395
|
+
|
|
396
|
+
if (!filePath) {
|
|
397
|
+
return res.status(400).json({
|
|
398
|
+
success: false,
|
|
399
|
+
message: '请指定文件路径'
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const result = skillService.getSkillFileContent(directory, filePath);
|
|
404
|
+
|
|
405
|
+
res.json({
|
|
406
|
+
success: true,
|
|
407
|
+
...result
|
|
408
|
+
});
|
|
409
|
+
} catch (err) {
|
|
410
|
+
console.error('[Skills API] Get skill file content error:', err);
|
|
411
|
+
res.status(500).json({
|
|
412
|
+
success: false,
|
|
413
|
+
message: err.message
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* 添加文件到技能
|
|
420
|
+
* POST /api/skills/:directory/files
|
|
421
|
+
* Body: { files: [{path, content, isBase64?}] }
|
|
422
|
+
*/
|
|
423
|
+
router.post('/:directory/files', (req, res) => {
|
|
424
|
+
try {
|
|
425
|
+
const { directory } = req.params;
|
|
426
|
+
const { files } = req.body;
|
|
427
|
+
|
|
428
|
+
if (!files || !Array.isArray(files) || files.length === 0) {
|
|
429
|
+
return res.status(400).json({
|
|
430
|
+
success: false,
|
|
431
|
+
message: '请提供文件列表'
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const result = skillService.addSkillFiles(directory, files);
|
|
436
|
+
|
|
437
|
+
res.json({
|
|
438
|
+
success: true,
|
|
439
|
+
...result
|
|
440
|
+
});
|
|
441
|
+
} catch (err) {
|
|
442
|
+
console.error('[Skills API] Add skill files error:', err);
|
|
443
|
+
res.status(500).json({
|
|
444
|
+
success: false,
|
|
445
|
+
message: err.message
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* 删除技能中的文件
|
|
452
|
+
* DELETE /api/skills/:directory/file/*
|
|
453
|
+
*/
|
|
454
|
+
router.delete('/:directory/file/*', (req, res) => {
|
|
455
|
+
try {
|
|
456
|
+
const { directory } = req.params;
|
|
457
|
+
const filePath = req.params[0];
|
|
458
|
+
|
|
459
|
+
if (!filePath) {
|
|
460
|
+
return res.status(400).json({
|
|
461
|
+
success: false,
|
|
462
|
+
message: '请指定文件路径'
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const result = skillService.deleteSkillFile(directory, filePath);
|
|
467
|
+
|
|
468
|
+
res.json({
|
|
469
|
+
success: true,
|
|
470
|
+
...result
|
|
471
|
+
});
|
|
472
|
+
} catch (err) {
|
|
473
|
+
console.error('[Skills API] Delete skill file error:', err);
|
|
474
|
+
res.status(500).json({
|
|
475
|
+
success: false,
|
|
476
|
+
message: err.message
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* 更新技能文件内容
|
|
483
|
+
* PUT /api/skills/:directory/file/*
|
|
484
|
+
* Body: { content, isBase64? }
|
|
485
|
+
*/
|
|
486
|
+
router.put('/:directory/file/*', (req, res) => {
|
|
487
|
+
try {
|
|
488
|
+
const { directory } = req.params;
|
|
489
|
+
const filePath = req.params[0];
|
|
490
|
+
const { content, isBase64 = false } = req.body;
|
|
491
|
+
|
|
492
|
+
if (!filePath) {
|
|
493
|
+
return res.status(400).json({
|
|
494
|
+
success: false,
|
|
495
|
+
message: '请指定文件路径'
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (content === undefined) {
|
|
500
|
+
return res.status(400).json({
|
|
501
|
+
success: false,
|
|
502
|
+
message: '请提供文件内容'
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const result = skillService.updateSkillFile(directory, filePath, content, isBase64);
|
|
507
|
+
|
|
508
|
+
res.json({
|
|
509
|
+
success: true,
|
|
510
|
+
...result
|
|
511
|
+
});
|
|
512
|
+
} catch (err) {
|
|
513
|
+
console.error('[Skills API] Update skill file error:', err);
|
|
514
|
+
res.status(500).json({
|
|
515
|
+
success: false,
|
|
516
|
+
message: err.message
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
|
|
315
521
|
// ==================== 格式转换 API ====================
|
|
316
522
|
|
|
317
523
|
/**
|
|
@@ -28,6 +28,32 @@ router.get('/list', (req, res) => {
|
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* GET /api/terminal/health - 检查终端健康状态
|
|
33
|
+
*/
|
|
34
|
+
router.get('/health', (req, res) => {
|
|
35
|
+
try {
|
|
36
|
+
const isPtyAvailable = ptyManager.isPtyAvailable();
|
|
37
|
+
const ptyError = ptyManager.getPtyError();
|
|
38
|
+
|
|
39
|
+
res.json({
|
|
40
|
+
success: isPtyAvailable,
|
|
41
|
+
pty: {
|
|
42
|
+
available: isPtyAvailable,
|
|
43
|
+
error: ptyError,
|
|
44
|
+
nodeVersion: process.version,
|
|
45
|
+
platform: process.platform
|
|
46
|
+
},
|
|
47
|
+
shell: {
|
|
48
|
+
default: ptyManager.getDefaultShell(),
|
|
49
|
+
env: process.env.SHELL
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
} catch (err) {
|
|
53
|
+
res.status(500).json({ success: false, error: err.message });
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
31
57
|
/**
|
|
32
58
|
* GET /api/terminal/commands/config - 获取命令配置
|
|
33
59
|
* 注意:此路由必须在 /:id 之前定义,否则会被动态路由捕获
|
|
@@ -109,9 +109,9 @@ router.get('/check-git/*', (req, res) => {
|
|
|
109
109
|
* GET /api/workspaces/available-projects
|
|
110
110
|
* 获取所有渠道(Claude/Codex/Gemini)的项目并集
|
|
111
111
|
*/
|
|
112
|
-
router.get('/available-projects', (req, res) => {
|
|
112
|
+
router.get('/available-projects', async (req, res) => {
|
|
113
113
|
try {
|
|
114
|
-
const projects = workspaceService.getAllAvailableProjects();
|
|
114
|
+
const projects = await workspaceService.getAllAvailableProjects();
|
|
115
115
|
res.json({
|
|
116
116
|
success: true,
|
|
117
117
|
data: projects
|
package/src/server/index.js
CHANGED
|
@@ -149,6 +149,9 @@ async function startServer(port) {
|
|
|
149
149
|
// 配置导出/导入 API
|
|
150
150
|
app.use('/api/config-export', require('./api/config-export'));
|
|
151
151
|
|
|
152
|
+
// 配置同步 API
|
|
153
|
+
app.use('/api/config-sync', require('./api/config-sync'));
|
|
154
|
+
|
|
152
155
|
// 健康检查 API
|
|
153
156
|
app.use('/api/health-check', require('./api/health-check')(config));
|
|
154
157
|
|
|
@@ -261,7 +264,7 @@ function autoRestoreProxies() {
|
|
|
261
264
|
}
|
|
262
265
|
|
|
263
266
|
// 启动时执行健康检查
|
|
264
|
-
function performStartupHealthCheck() {
|
|
267
|
+
async function performStartupHealthCheck() {
|
|
265
268
|
const { healthCheckAllProjects } = require('./services/health-check');
|
|
266
269
|
const { getProjects } = require('./services/sessions');
|
|
267
270
|
|
|
@@ -270,7 +273,7 @@ function performStartupHealthCheck() {
|
|
|
270
273
|
|
|
271
274
|
// 获取所有项目
|
|
272
275
|
const config = loadConfig();
|
|
273
|
-
const projects = getProjects(config);
|
|
276
|
+
const projects = await getProjects(config);
|
|
274
277
|
|
|
275
278
|
if (projects.length === 0) {
|
|
276
279
|
console.log(chalk.gray(' 未发现项目,跳过健康检查'));
|