@adversity/coding-tool-x 2.3.0 → 2.4.1
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 +41 -0
- package/README.md +8 -0
- package/dist/web/assets/icons-Dom8a0SN.js +1 -0
- package/dist/web/assets/index-CQeUIH7E.css +41 -0
- package/dist/web/assets/index-YrjlFzC4.js +14 -0
- package/dist/web/assets/naive-ui-BjMHakwv.js +1 -0
- package/dist/web/assets/vendors-DtJKdpSj.js +7 -0
- package/dist/web/assets/vue-vendor-VFuFB5f4.js +44 -0
- package/dist/web/index.html +6 -2
- package/package.json +2 -2
- package/src/commands/export-config.js +205 -0
- package/src/config/default.js +1 -1
- package/src/server/api/config-export.js +122 -0
- package/src/server/api/config-sync.js +155 -0
- package/src/server/api/config-templates.js +12 -6
- package/src/server/api/health-check.js +1 -89
- package/src/server/api/permissions.js +92 -69
- 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/index.js +7 -11
- package/src/server/services/config-export-service.js +415 -0
- package/src/server/services/config-sync-service.js +515 -0
- package/src/server/services/config-templates-service.js +61 -38
- package/src/server/services/enhanced-cache.js +196 -0
- package/src/server/services/health-check.js +1 -315
- package/src/server/services/permission-templates-service.js +339 -0
- package/src/server/services/pty-manager.js +35 -1
- package/src/server/services/sessions.js +122 -44
- package/src/server/services/skill-service.js +252 -2
- package/src/server/services/workspace-service.js +44 -84
- package/src/server/websocket-server.js +4 -1
- package/dist/web/assets/index-dhun1bYQ.js +0 -3555
- package/dist/web/assets/index-hHb7DAda.css +0 -41
|
@@ -25,6 +25,7 @@ const express = require('express');
|
|
|
25
25
|
const fs = require('fs');
|
|
26
26
|
const path = require('path');
|
|
27
27
|
const os = require('os');
|
|
28
|
+
const permissionTemplatesService = require('../services/permission-templates-service');
|
|
28
29
|
|
|
29
30
|
const router = express.Router();
|
|
30
31
|
|
|
@@ -207,81 +208,15 @@ router.post('/all-allow', (req, res) => {
|
|
|
207
208
|
});
|
|
208
209
|
|
|
209
210
|
/**
|
|
210
|
-
*
|
|
211
|
+
* 获取所有权限模版(内置 + 自定义)
|
|
211
212
|
* GET /api/permissions/templates
|
|
212
213
|
*/
|
|
213
214
|
router.get('/templates', (req, res) => {
|
|
214
215
|
try {
|
|
215
|
-
const templates =
|
|
216
|
-
safe: {
|
|
217
|
-
name: '安全模式',
|
|
218
|
-
description: '仅允许只读命令,危险操作需要确认',
|
|
219
|
-
allow: [
|
|
220
|
-
'Bash(cat:*)',
|
|
221
|
-
'Bash(ls:*)',
|
|
222
|
-
'Bash(pwd)',
|
|
223
|
-
'Bash(echo:*)',
|
|
224
|
-
'Bash(head:*)',
|
|
225
|
-
'Bash(tail:*)',
|
|
226
|
-
'Bash(grep:*)',
|
|
227
|
-
'Read(*)'
|
|
228
|
-
],
|
|
229
|
-
deny: [
|
|
230
|
-
'Bash(rm:*)',
|
|
231
|
-
'Bash(sudo:*)',
|
|
232
|
-
'Bash(git push:*)',
|
|
233
|
-
'Bash(git reset --hard:*)',
|
|
234
|
-
'Bash(chmod:*)',
|
|
235
|
-
'Bash(chown:*)',
|
|
236
|
-
'Edit(*)'
|
|
237
|
-
]
|
|
238
|
-
},
|
|
239
|
-
balanced: {
|
|
240
|
-
name: '平衡模式',
|
|
241
|
-
description: '允许常用开发命令,危险操作需要确认',
|
|
242
|
-
allow: [
|
|
243
|
-
'Bash(cat:*)',
|
|
244
|
-
'Bash(ls:*)',
|
|
245
|
-
'Bash(pwd)',
|
|
246
|
-
'Bash(echo:*)',
|
|
247
|
-
'Bash(head:*)',
|
|
248
|
-
'Bash(tail:*)',
|
|
249
|
-
'Bash(grep:*)',
|
|
250
|
-
'Bash(find:*)',
|
|
251
|
-
'Bash(git status)',
|
|
252
|
-
'Bash(git diff:*)',
|
|
253
|
-
'Bash(git log:*)',
|
|
254
|
-
'Bash(npm run:*)',
|
|
255
|
-
'Bash(pnpm:*)',
|
|
256
|
-
'Bash(yarn:*)',
|
|
257
|
-
'Read(*)',
|
|
258
|
-
'Edit(*)'
|
|
259
|
-
],
|
|
260
|
-
deny: [
|
|
261
|
-
'Bash(rm -rf:*)',
|
|
262
|
-
'Bash(sudo:*)',
|
|
263
|
-
'Bash(git push --force:*)',
|
|
264
|
-
'Bash(git reset --hard:*)'
|
|
265
|
-
]
|
|
266
|
-
},
|
|
267
|
-
permissive: {
|
|
268
|
-
name: '宽松模式',
|
|
269
|
-
description: '允许大多数命令,仅阻止极度危险的操作',
|
|
270
|
-
allow: [
|
|
271
|
-
'Bash(*)',
|
|
272
|
-
'Read(*)',
|
|
273
|
-
'Edit(*)'
|
|
274
|
-
],
|
|
275
|
-
deny: [
|
|
276
|
-
'Bash(rm -rf /*)',
|
|
277
|
-
'Bash(sudo rm -rf:*)'
|
|
278
|
-
]
|
|
279
|
-
}
|
|
280
|
-
};
|
|
281
|
-
|
|
216
|
+
const templates = permissionTemplatesService.getAllTemplates();
|
|
282
217
|
res.json({
|
|
283
218
|
success: true,
|
|
284
|
-
templates
|
|
219
|
+
data: templates
|
|
285
220
|
});
|
|
286
221
|
} catch (err) {
|
|
287
222
|
console.error('[Permissions API] Get templates error:', err);
|
|
@@ -292,6 +227,94 @@ router.get('/templates', (req, res) => {
|
|
|
292
227
|
}
|
|
293
228
|
});
|
|
294
229
|
|
|
230
|
+
/**
|
|
231
|
+
* 获取单个权限模版
|
|
232
|
+
* GET /api/permissions/templates/:id
|
|
233
|
+
*/
|
|
234
|
+
router.get('/templates/:id', (req, res) => {
|
|
235
|
+
try {
|
|
236
|
+
const template = permissionTemplatesService.getTemplateById(req.params.id);
|
|
237
|
+
if (!template) {
|
|
238
|
+
return res.status(404).json({
|
|
239
|
+
success: false,
|
|
240
|
+
message: '模版不存在'
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
res.json({
|
|
244
|
+
success: true,
|
|
245
|
+
data: template
|
|
246
|
+
});
|
|
247
|
+
} catch (err) {
|
|
248
|
+
console.error('[Permissions API] Get template error:', err);
|
|
249
|
+
res.status(500).json({
|
|
250
|
+
success: false,
|
|
251
|
+
message: err.message
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 创建自定义权限模版
|
|
258
|
+
* POST /api/permissions/templates
|
|
259
|
+
*/
|
|
260
|
+
router.post('/templates', (req, res) => {
|
|
261
|
+
try {
|
|
262
|
+
const template = permissionTemplatesService.createTemplate(req.body);
|
|
263
|
+
res.json({
|
|
264
|
+
success: true,
|
|
265
|
+
data: template,
|
|
266
|
+
message: '模版创建成功'
|
|
267
|
+
});
|
|
268
|
+
} catch (err) {
|
|
269
|
+
console.error('[Permissions API] Create template error:', err);
|
|
270
|
+
res.status(400).json({
|
|
271
|
+
success: false,
|
|
272
|
+
message: err.message
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* 更新自定义权限模版
|
|
279
|
+
* PUT /api/permissions/templates/:id
|
|
280
|
+
*/
|
|
281
|
+
router.put('/templates/:id', (req, res) => {
|
|
282
|
+
try {
|
|
283
|
+
const template = permissionTemplatesService.updateTemplate(req.params.id, req.body);
|
|
284
|
+
res.json({
|
|
285
|
+
success: true,
|
|
286
|
+
data: template,
|
|
287
|
+
message: '模版更新成功'
|
|
288
|
+
});
|
|
289
|
+
} catch (err) {
|
|
290
|
+
console.error('[Permissions API] Update template error:', err);
|
|
291
|
+
res.status(400).json({
|
|
292
|
+
success: false,
|
|
293
|
+
message: err.message
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* 删除自定义权限模版
|
|
300
|
+
* DELETE /api/permissions/templates/:id
|
|
301
|
+
*/
|
|
302
|
+
router.delete('/templates/:id', (req, res) => {
|
|
303
|
+
try {
|
|
304
|
+
permissionTemplatesService.deleteTemplate(req.params.id);
|
|
305
|
+
res.json({
|
|
306
|
+
success: true,
|
|
307
|
+
message: '模版删除成功'
|
|
308
|
+
});
|
|
309
|
+
} catch (err) {
|
|
310
|
+
console.error('[Permissions API] Delete template error:', err);
|
|
311
|
+
res.status(400).json({
|
|
312
|
+
success: false,
|
|
313
|
+
message: err.message
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
|
|
295
318
|
/**
|
|
296
319
|
* 获取各 CLI 工具的启动参数配置说明
|
|
297
320
|
* GET /api/permissions/cli-config
|
|
@@ -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 之前定义,否则会被动态路由捕获
|
package/src/server/index.js
CHANGED
|
@@ -146,6 +146,12 @@ async function startServer(port) {
|
|
|
146
146
|
// 命令执行权限 API
|
|
147
147
|
app.use('/api/permissions', require('./api/permissions'));
|
|
148
148
|
|
|
149
|
+
// 配置导出/导入 API
|
|
150
|
+
app.use('/api/config-export', require('./api/config-export'));
|
|
151
|
+
|
|
152
|
+
// 配置同步 API
|
|
153
|
+
app.use('/api/config-sync', require('./api/config-sync'));
|
|
154
|
+
|
|
149
155
|
// 健康检查 API
|
|
150
156
|
app.use('/api/health-check', require('./api/health-check')(config));
|
|
151
157
|
|
|
@@ -259,7 +265,7 @@ function autoRestoreProxies() {
|
|
|
259
265
|
|
|
260
266
|
// 启动时执行健康检查
|
|
261
267
|
function performStartupHealthCheck() {
|
|
262
|
-
const { healthCheckAllProjects
|
|
268
|
+
const { healthCheckAllProjects } = require('./services/health-check');
|
|
263
269
|
const { getProjects } = require('./services/sessions');
|
|
264
270
|
|
|
265
271
|
try {
|
|
@@ -289,16 +295,6 @@ function performStartupHealthCheck() {
|
|
|
289
295
|
console.log(chalk.green(` ✓ 所有 ${healthResult.summary.healthy} 个项目状态正常`));
|
|
290
296
|
}
|
|
291
297
|
|
|
292
|
-
// 扫描旧文件
|
|
293
|
-
const legacyResult = scanLegacySessionFiles();
|
|
294
|
-
|
|
295
|
-
if (legacyResult.found && legacyResult.projectCount > 0) {
|
|
296
|
-
console.log(chalk.yellow(`\n ⚠ 发现 ${legacyResult.projectCount} 个项目的旧会话文件在全局目录`));
|
|
297
|
-
console.log(chalk.gray(' 💡 提示: 可通过 Web UI 或 API 清理这些文件'));
|
|
298
|
-
console.log(chalk.gray(` - Web UI: 设置 -> 系统维护 -> 清理旧文件`));
|
|
299
|
-
console.log(chalk.gray(` - API: POST /api/health-check/clean-legacy`));
|
|
300
|
-
}
|
|
301
|
-
|
|
302
298
|
console.log('');
|
|
303
299
|
} catch (err) {
|
|
304
300
|
console.error(chalk.red(' ✗ 健康检查失败:'), err.message);
|