@adversity/coding-tool-x 2.2.0 → 2.4.0
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 +44 -0
- package/README.md +12 -14
- package/dist/web/assets/index-Bu1oPcKu.js +4009 -0
- package/dist/web/assets/index-XSok7-mN.css +41 -0
- package/dist/web/index.html +2 -2
- package/package.json +5 -4
- package/src/config/default.js +1 -1
- package/src/index.js +2 -2
- package/src/server/api/agents.js +188 -0
- package/src/server/api/commands.js +261 -0
- package/src/server/api/config-export.js +122 -0
- package/src/server/api/config-templates.js +26 -5
- package/src/server/api/health-check.js +1 -89
- package/src/server/api/permissions.js +370 -0
- package/src/server/api/rules.js +188 -0
- package/src/server/api/skills.js +66 -14
- package/src/server/api/workspaces.js +30 -55
- package/src/server/index.js +7 -11
- package/src/server/services/agents-service.js +179 -1
- package/src/server/services/commands-service.js +231 -47
- package/src/server/services/config-export-service.js +209 -0
- package/src/server/services/config-templates-service.js +481 -107
- package/src/server/services/format-converter.js +506 -0
- package/src/server/services/health-check.js +1 -315
- package/src/server/services/permission-templates-service.js +339 -0
- package/src/server/services/repo-scanner-base.js +678 -0
- package/src/server/services/rules-service.js +179 -1
- package/src/server/services/skill-service.js +114 -61
- package/src/server/services/workspace-service.js +52 -1
- package/dist/web/assets/index-D1AYlFLZ.js +0 -3220
- package/dist/web/assets/index-aL3cKxSK.css +0 -41
- package/docs/CHANGELOG.md +0 -582
- package/docs/DIRECTORY_MIGRATION.md +0 -112
- package/docs/PROJECT_STRUCTURE.md +0 -396
package/src/server/api/agents.js
CHANGED
|
@@ -245,4 +245,192 @@ router.delete('/:scope/:fileName', (req, res) => {
|
|
|
245
245
|
}
|
|
246
246
|
});
|
|
247
247
|
|
|
248
|
+
// ==================== 仓库管理 API ====================
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* 获取所有代理(包括远程仓库)
|
|
252
|
+
* GET /api/agents/all
|
|
253
|
+
* Query: projectPath, refresh=1 强制刷新缓存
|
|
254
|
+
*/
|
|
255
|
+
router.get('/all', async (req, res) => {
|
|
256
|
+
try {
|
|
257
|
+
const { projectPath, refresh } = req.query;
|
|
258
|
+
const forceRefresh = refresh === '1';
|
|
259
|
+
const result = await agentsService.listAllAgents(projectPath || null, forceRefresh);
|
|
260
|
+
|
|
261
|
+
res.json({
|
|
262
|
+
success: true,
|
|
263
|
+
...result
|
|
264
|
+
});
|
|
265
|
+
} catch (err) {
|
|
266
|
+
console.error('[Agents API] List all agents error:', err);
|
|
267
|
+
res.status(500).json({
|
|
268
|
+
success: false,
|
|
269
|
+
message: err.message
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* 获取仓库列表
|
|
276
|
+
* GET /api/agents/repos
|
|
277
|
+
*/
|
|
278
|
+
router.get('/repos', (req, res) => {
|
|
279
|
+
try {
|
|
280
|
+
const repos = agentsService.getRepos();
|
|
281
|
+
res.json({
|
|
282
|
+
success: true,
|
|
283
|
+
repos
|
|
284
|
+
});
|
|
285
|
+
} catch (err) {
|
|
286
|
+
console.error('[Agents API] Get repos error:', err);
|
|
287
|
+
res.status(500).json({
|
|
288
|
+
success: false,
|
|
289
|
+
message: err.message
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* 添加仓库
|
|
296
|
+
* POST /api/agents/repos
|
|
297
|
+
* Body: { owner, name, branch, directory, enabled }
|
|
298
|
+
*/
|
|
299
|
+
router.post('/repos', (req, res) => {
|
|
300
|
+
try {
|
|
301
|
+
const { owner, name, branch = 'main', directory = '', enabled = true } = req.body;
|
|
302
|
+
|
|
303
|
+
if (!owner || !name) {
|
|
304
|
+
return res.status(400).json({
|
|
305
|
+
success: false,
|
|
306
|
+
message: 'Missing owner or name'
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const repos = agentsService.addRepo({ owner, name, branch, directory, enabled });
|
|
311
|
+
|
|
312
|
+
res.json({
|
|
313
|
+
success: true,
|
|
314
|
+
repos
|
|
315
|
+
});
|
|
316
|
+
} catch (err) {
|
|
317
|
+
console.error('[Agents API] Add repo error:', err);
|
|
318
|
+
res.status(500).json({
|
|
319
|
+
success: false,
|
|
320
|
+
message: err.message
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* 删除仓库
|
|
327
|
+
* DELETE /api/agents/repos/:owner/:name
|
|
328
|
+
* Query: directory - 可选,子目录路径
|
|
329
|
+
*/
|
|
330
|
+
router.delete('/repos/:owner/:name', (req, res) => {
|
|
331
|
+
try {
|
|
332
|
+
const { owner, name } = req.params;
|
|
333
|
+
const { directory = '' } = req.query;
|
|
334
|
+
const repos = agentsService.removeRepo(owner, name, directory);
|
|
335
|
+
|
|
336
|
+
res.json({
|
|
337
|
+
success: true,
|
|
338
|
+
repos
|
|
339
|
+
});
|
|
340
|
+
} catch (err) {
|
|
341
|
+
console.error('[Agents API] Remove repo error:', err);
|
|
342
|
+
res.status(500).json({
|
|
343
|
+
success: false,
|
|
344
|
+
message: err.message
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* 切换仓库启用状态
|
|
351
|
+
* PUT /api/agents/repos/:owner/:name/toggle
|
|
352
|
+
* Body: { enabled, directory }
|
|
353
|
+
*/
|
|
354
|
+
router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
355
|
+
try {
|
|
356
|
+
const { owner, name } = req.params;
|
|
357
|
+
const { enabled, directory = '' } = req.body;
|
|
358
|
+
|
|
359
|
+
const repos = agentsService.toggleRepo(owner, name, directory, enabled);
|
|
360
|
+
|
|
361
|
+
res.json({
|
|
362
|
+
success: true,
|
|
363
|
+
repos
|
|
364
|
+
});
|
|
365
|
+
} catch (err) {
|
|
366
|
+
console.error('[Agents API] Toggle repo error:', err);
|
|
367
|
+
res.status(500).json({
|
|
368
|
+
success: false,
|
|
369
|
+
message: err.message
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* 从远程仓库安装代理
|
|
376
|
+
* POST /api/agents/install
|
|
377
|
+
* Body: agent object from listAllAgents
|
|
378
|
+
*/
|
|
379
|
+
router.post('/install', async (req, res) => {
|
|
380
|
+
try {
|
|
381
|
+
const agent = req.body;
|
|
382
|
+
|
|
383
|
+
if (!agent || !agent.repoOwner || !agent.repoName) {
|
|
384
|
+
return res.status(400).json({
|
|
385
|
+
success: false,
|
|
386
|
+
message: 'Missing agent info or repo info'
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const result = await agentsService.installFromRemote(agent);
|
|
391
|
+
|
|
392
|
+
res.json({
|
|
393
|
+
success: true,
|
|
394
|
+
...result
|
|
395
|
+
});
|
|
396
|
+
} catch (err) {
|
|
397
|
+
console.error('[Agents API] Install agent error:', err);
|
|
398
|
+
res.status(500).json({
|
|
399
|
+
success: false,
|
|
400
|
+
message: err.message
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* 卸载代理
|
|
407
|
+
* POST /api/agents/uninstall
|
|
408
|
+
* Body: { fileName } - 代理的文件名(不含扩展名)
|
|
409
|
+
*/
|
|
410
|
+
router.post('/uninstall', (req, res) => {
|
|
411
|
+
try {
|
|
412
|
+
const { fileName } = req.body;
|
|
413
|
+
|
|
414
|
+
if (!fileName) {
|
|
415
|
+
return res.status(400).json({
|
|
416
|
+
success: false,
|
|
417
|
+
message: 'Missing fileName'
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const result = agentsService.uninstallAgent(fileName);
|
|
422
|
+
|
|
423
|
+
res.json({
|
|
424
|
+
success: true,
|
|
425
|
+
...result
|
|
426
|
+
});
|
|
427
|
+
} catch (err) {
|
|
428
|
+
console.error('[Agents API] Uninstall agent error:', err);
|
|
429
|
+
res.status(500).json({
|
|
430
|
+
success: false,
|
|
431
|
+
message: err.message
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
|
|
248
436
|
module.exports = router;
|
|
@@ -242,4 +242,265 @@ router.delete('/:scope/:name', (req, res) => {
|
|
|
242
242
|
}
|
|
243
243
|
});
|
|
244
244
|
|
|
245
|
+
// ==================== 仓库管理 API ====================
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* 获取所有命令(包括远程仓库)
|
|
249
|
+
* GET /api/commands/all
|
|
250
|
+
* Query: projectPath, refresh=1 强制刷新缓存
|
|
251
|
+
*/
|
|
252
|
+
router.get('/all', async (req, res) => {
|
|
253
|
+
try {
|
|
254
|
+
const { projectPath, refresh } = req.query;
|
|
255
|
+
const forceRefresh = refresh === '1';
|
|
256
|
+
const result = await commandsService.listAllCommands(projectPath || null, forceRefresh);
|
|
257
|
+
|
|
258
|
+
res.json({
|
|
259
|
+
success: true,
|
|
260
|
+
...result
|
|
261
|
+
});
|
|
262
|
+
} catch (err) {
|
|
263
|
+
console.error('[Commands API] List all commands error:', err);
|
|
264
|
+
res.status(500).json({
|
|
265
|
+
success: false,
|
|
266
|
+
message: err.message
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* 获取仓库列表
|
|
273
|
+
* GET /api/commands/repos
|
|
274
|
+
*/
|
|
275
|
+
router.get('/repos', (req, res) => {
|
|
276
|
+
try {
|
|
277
|
+
const repos = commandsService.getRepos();
|
|
278
|
+
res.json({
|
|
279
|
+
success: true,
|
|
280
|
+
repos
|
|
281
|
+
});
|
|
282
|
+
} catch (err) {
|
|
283
|
+
console.error('[Commands API] Get repos error:', err);
|
|
284
|
+
res.status(500).json({
|
|
285
|
+
success: false,
|
|
286
|
+
message: err.message
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* 添加仓库
|
|
293
|
+
* POST /api/commands/repos
|
|
294
|
+
* Body: { owner, name, branch, directory, enabled }
|
|
295
|
+
*/
|
|
296
|
+
router.post('/repos', (req, res) => {
|
|
297
|
+
try {
|
|
298
|
+
const { owner, name, branch = 'main', directory = '', enabled = true } = req.body;
|
|
299
|
+
|
|
300
|
+
if (!owner || !name) {
|
|
301
|
+
return res.status(400).json({
|
|
302
|
+
success: false,
|
|
303
|
+
message: 'Missing owner or name'
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const repos = commandsService.addRepo({ owner, name, branch, directory, enabled });
|
|
308
|
+
|
|
309
|
+
res.json({
|
|
310
|
+
success: true,
|
|
311
|
+
repos
|
|
312
|
+
});
|
|
313
|
+
} catch (err) {
|
|
314
|
+
console.error('[Commands API] Add repo error:', err);
|
|
315
|
+
res.status(500).json({
|
|
316
|
+
success: false,
|
|
317
|
+
message: err.message
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* 删除仓库
|
|
324
|
+
* DELETE /api/commands/repos/:owner/:name
|
|
325
|
+
* Query: directory - 可选,子目录路径
|
|
326
|
+
*/
|
|
327
|
+
router.delete('/repos/:owner/:name', (req, res) => {
|
|
328
|
+
try {
|
|
329
|
+
const { owner, name } = req.params;
|
|
330
|
+
const { directory = '' } = req.query;
|
|
331
|
+
const repos = commandsService.removeRepo(owner, name, directory);
|
|
332
|
+
|
|
333
|
+
res.json({
|
|
334
|
+
success: true,
|
|
335
|
+
repos
|
|
336
|
+
});
|
|
337
|
+
} catch (err) {
|
|
338
|
+
console.error('[Commands API] Remove repo error:', err);
|
|
339
|
+
res.status(500).json({
|
|
340
|
+
success: false,
|
|
341
|
+
message: err.message
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* 切换仓库启用状态
|
|
348
|
+
* PUT /api/commands/repos/:owner/:name/toggle
|
|
349
|
+
* Body: { enabled, directory }
|
|
350
|
+
*/
|
|
351
|
+
router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
352
|
+
try {
|
|
353
|
+
const { owner, name } = req.params;
|
|
354
|
+
const { enabled, directory = '' } = req.body;
|
|
355
|
+
|
|
356
|
+
const repos = commandsService.toggleRepo(owner, name, directory, enabled);
|
|
357
|
+
|
|
358
|
+
res.json({
|
|
359
|
+
success: true,
|
|
360
|
+
repos
|
|
361
|
+
});
|
|
362
|
+
} catch (err) {
|
|
363
|
+
console.error('[Commands API] Toggle repo error:', err);
|
|
364
|
+
res.status(500).json({
|
|
365
|
+
success: false,
|
|
366
|
+
message: err.message
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* 从远程仓库安装命令
|
|
373
|
+
* POST /api/commands/install
|
|
374
|
+
* Body: command object from listAllCommands
|
|
375
|
+
*/
|
|
376
|
+
router.post('/install', async (req, res) => {
|
|
377
|
+
try {
|
|
378
|
+
const command = req.body;
|
|
379
|
+
|
|
380
|
+
if (!command || !command.repoOwner || !command.repoName) {
|
|
381
|
+
return res.status(400).json({
|
|
382
|
+
success: false,
|
|
383
|
+
message: 'Missing command info or repo info'
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const result = await commandsService.installFromRemote(command);
|
|
388
|
+
|
|
389
|
+
res.json({
|
|
390
|
+
success: true,
|
|
391
|
+
...result
|
|
392
|
+
});
|
|
393
|
+
} catch (err) {
|
|
394
|
+
console.error('[Commands API] Install command error:', err);
|
|
395
|
+
res.status(500).json({
|
|
396
|
+
success: false,
|
|
397
|
+
message: err.message
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* 卸载命令
|
|
404
|
+
* POST /api/commands/uninstall
|
|
405
|
+
* Body: { path } - 命令的相对路径
|
|
406
|
+
*/
|
|
407
|
+
router.post('/uninstall', (req, res) => {
|
|
408
|
+
try {
|
|
409
|
+
const { path } = req.body;
|
|
410
|
+
|
|
411
|
+
if (!path) {
|
|
412
|
+
return res.status(400).json({
|
|
413
|
+
success: false,
|
|
414
|
+
message: 'Missing path'
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const result = commandsService.uninstallCommand(path);
|
|
419
|
+
|
|
420
|
+
res.json({
|
|
421
|
+
success: true,
|
|
422
|
+
...result
|
|
423
|
+
});
|
|
424
|
+
} catch (err) {
|
|
425
|
+
console.error('[Commands API] Uninstall command error:', err);
|
|
426
|
+
res.status(500).json({
|
|
427
|
+
success: false,
|
|
428
|
+
message: err.message
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// ==================== 格式转换 API ====================
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* 转换命令格式
|
|
437
|
+
* POST /api/commands/convert
|
|
438
|
+
* Body: { content, targetFormat }
|
|
439
|
+
* - content: 命令内容
|
|
440
|
+
* - targetFormat: 目标格式 ('claude' | 'codex')
|
|
441
|
+
*/
|
|
442
|
+
router.post('/convert', (req, res) => {
|
|
443
|
+
try {
|
|
444
|
+
const { content, targetFormat } = req.body;
|
|
445
|
+
|
|
446
|
+
if (!content) {
|
|
447
|
+
return res.status(400).json({
|
|
448
|
+
success: false,
|
|
449
|
+
message: '请提供命令内容'
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (!['claude', 'codex'].includes(targetFormat)) {
|
|
454
|
+
return res.status(400).json({
|
|
455
|
+
success: false,
|
|
456
|
+
message: '目标格式必须是 claude 或 codex'
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const result = commandsService.convertCommandFormat(content, targetFormat);
|
|
461
|
+
|
|
462
|
+
res.json({
|
|
463
|
+
success: true,
|
|
464
|
+
...result
|
|
465
|
+
});
|
|
466
|
+
} catch (err) {
|
|
467
|
+
console.error('[Commands API] Convert command error:', err);
|
|
468
|
+
res.status(500).json({
|
|
469
|
+
success: false,
|
|
470
|
+
message: err.message
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* 检测命令格式
|
|
477
|
+
* POST /api/commands/detect-format
|
|
478
|
+
* Body: { content }
|
|
479
|
+
*/
|
|
480
|
+
router.post('/detect-format', (req, res) => {
|
|
481
|
+
try {
|
|
482
|
+
const { content } = req.body;
|
|
483
|
+
|
|
484
|
+
if (!content) {
|
|
485
|
+
return res.status(400).json({
|
|
486
|
+
success: false,
|
|
487
|
+
message: '请提供命令内容'
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const format = commandsService.detectFormat(content);
|
|
492
|
+
|
|
493
|
+
res.json({
|
|
494
|
+
success: true,
|
|
495
|
+
format
|
|
496
|
+
});
|
|
497
|
+
} catch (err) {
|
|
498
|
+
console.error('[Commands API] Detect format error:', err);
|
|
499
|
+
res.status(500).json({
|
|
500
|
+
success: false,
|
|
501
|
+
message: err.message
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
|
|
245
506
|
module.exports = router;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置导出/导入 API 路由
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const express = require('express');
|
|
6
|
+
const configExportService = require('../services/config-export-service');
|
|
7
|
+
|
|
8
|
+
const router = express.Router();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 导出所有配置
|
|
12
|
+
* GET /api/config-export
|
|
13
|
+
*/
|
|
14
|
+
router.get('/', (req, res) => {
|
|
15
|
+
try {
|
|
16
|
+
const result = configExportService.exportAllConfigs();
|
|
17
|
+
|
|
18
|
+
if (result.success) {
|
|
19
|
+
// 设置响应头,触发文件下载
|
|
20
|
+
const filename = `ctx-config-${new Date().toISOString().split('T')[0]}.json`;
|
|
21
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
22
|
+
res.setHeader('Content-Type', 'application/json');
|
|
23
|
+
res.json(result.data);
|
|
24
|
+
} else {
|
|
25
|
+
res.status(500).json({
|
|
26
|
+
success: false,
|
|
27
|
+
message: result.message
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
} catch (err) {
|
|
31
|
+
console.error('[ConfigExport API] 导出失败:', err);
|
|
32
|
+
res.status(500).json({
|
|
33
|
+
success: false,
|
|
34
|
+
message: err.message
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 导入配置
|
|
41
|
+
* POST /api/config-export/import
|
|
42
|
+
* Body: { data: {...}, overwrite: boolean }
|
|
43
|
+
*/
|
|
44
|
+
router.post('/import', (req, res) => {
|
|
45
|
+
try {
|
|
46
|
+
const { data, overwrite = false } = req.body;
|
|
47
|
+
|
|
48
|
+
if (!data) {
|
|
49
|
+
return res.status(400).json({
|
|
50
|
+
success: false,
|
|
51
|
+
message: '缺少导入数据'
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const result = configExportService.importConfigs(data, { overwrite });
|
|
56
|
+
|
|
57
|
+
res.json(result);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
console.error('[ConfigExport API] 导入失败:', err);
|
|
60
|
+
res.status(500).json({
|
|
61
|
+
success: false,
|
|
62
|
+
message: err.message
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 预览导入配置(不实际导入)
|
|
69
|
+
* POST /api/config-export/preview
|
|
70
|
+
*/
|
|
71
|
+
router.post('/preview', (req, res) => {
|
|
72
|
+
try {
|
|
73
|
+
const { data } = req.body;
|
|
74
|
+
|
|
75
|
+
if (!data || !data.data) {
|
|
76
|
+
return res.status(400).json({
|
|
77
|
+
success: false,
|
|
78
|
+
message: '无效的导入数据格式'
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const summary = {
|
|
83
|
+
version: data.version,
|
|
84
|
+
exportedAt: data.exportedAt,
|
|
85
|
+
counts: {
|
|
86
|
+
permissionTemplates: (data.data.permissionTemplates || []).length,
|
|
87
|
+
configTemplates: (data.data.configTemplates || []).length,
|
|
88
|
+
channels: (data.data.channels || []).length
|
|
89
|
+
},
|
|
90
|
+
items: {
|
|
91
|
+
permissionTemplates: (data.data.permissionTemplates || []).map(t => ({
|
|
92
|
+
id: t.id,
|
|
93
|
+
name: t.name,
|
|
94
|
+
description: t.description
|
|
95
|
+
})),
|
|
96
|
+
configTemplates: (data.data.configTemplates || []).map(t => ({
|
|
97
|
+
id: t.id,
|
|
98
|
+
name: t.name,
|
|
99
|
+
description: t.description
|
|
100
|
+
})),
|
|
101
|
+
channels: (data.data.channels || []).map(c => ({
|
|
102
|
+
id: c.id,
|
|
103
|
+
name: c.name,
|
|
104
|
+
type: c.type
|
|
105
|
+
}))
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
res.json({
|
|
110
|
+
success: true,
|
|
111
|
+
data: summary
|
|
112
|
+
});
|
|
113
|
+
} catch (err) {
|
|
114
|
+
console.error('[ConfigExport API] 预览失败:', err);
|
|
115
|
+
res.status(500).json({
|
|
116
|
+
success: false,
|
|
117
|
+
message: err.message
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
module.exports = router;
|
|
@@ -46,8 +46,15 @@ router.get('/available-configs', (req, res) => {
|
|
|
46
46
|
* GET /api/config-templates/:id
|
|
47
47
|
* 获取单个配置模板详情
|
|
48
48
|
*/
|
|
49
|
-
|
|
49
|
+
// 注意:此路由需要放在所有静态子路由之后,避免把 /available-configs 等路径当成 id
|
|
50
|
+
router.get('/:id', (req, res, next) => {
|
|
50
51
|
try {
|
|
52
|
+
// 兜底:即便路由顺序被改动,也避免把保留路径当成模板 ID
|
|
53
|
+
const reservedIds = new Set(['available-configs']);
|
|
54
|
+
if (reservedIds.has(req.params.id)) {
|
|
55
|
+
return next();
|
|
56
|
+
}
|
|
57
|
+
|
|
51
58
|
const template = templatesService.getTemplateById(req.params.id);
|
|
52
59
|
if (!template) {
|
|
53
60
|
return res.status(404).json({
|
|
@@ -132,14 +139,21 @@ router.delete('/:id', (req, res) => {
|
|
|
132
139
|
*/
|
|
133
140
|
router.post('/:id/apply', (req, res) => {
|
|
134
141
|
try {
|
|
135
|
-
const { targetPath } = req.body;
|
|
142
|
+
const { targetPath, aiConfigType, aiConfigTypes } = req.body;
|
|
136
143
|
if (!targetPath) {
|
|
137
144
|
return res.status(400).json({
|
|
138
145
|
success: false,
|
|
139
146
|
message: '目标路径不能为空'
|
|
140
147
|
});
|
|
141
148
|
}
|
|
142
|
-
const
|
|
149
|
+
const options = {};
|
|
150
|
+
// 优先使用新的数组参数,兼容旧的单值参数
|
|
151
|
+
if (aiConfigTypes) {
|
|
152
|
+
options.aiConfigTypes = aiConfigTypes;
|
|
153
|
+
} else if (aiConfigType) {
|
|
154
|
+
options.aiConfigTypes = [aiConfigType];
|
|
155
|
+
}
|
|
156
|
+
const result = templatesService.applyTemplateToProject(targetPath, req.params.id, options);
|
|
143
157
|
res.json({
|
|
144
158
|
success: true,
|
|
145
159
|
message: '模板应用成功',
|
|
@@ -159,14 +173,21 @@ router.post('/:id/apply', (req, res) => {
|
|
|
159
173
|
*/
|
|
160
174
|
router.post('/:id/preview', (req, res) => {
|
|
161
175
|
try {
|
|
162
|
-
const { targetPath } = req.body;
|
|
176
|
+
const { targetPath, aiConfigType, aiConfigTypes } = req.body;
|
|
163
177
|
if (!targetPath) {
|
|
164
178
|
return res.status(400).json({
|
|
165
179
|
success: false,
|
|
166
180
|
message: '目标路径不能为空'
|
|
167
181
|
});
|
|
168
182
|
}
|
|
169
|
-
const
|
|
183
|
+
const options = {};
|
|
184
|
+
// 优先使用新的数组参数,兼容旧的单值参数
|
|
185
|
+
if (aiConfigTypes) {
|
|
186
|
+
options.aiConfigTypes = aiConfigTypes;
|
|
187
|
+
} else if (aiConfigType) {
|
|
188
|
+
options.aiConfigTypes = [aiConfigType];
|
|
189
|
+
}
|
|
190
|
+
const preview = templatesService.previewTemplateApplication(targetPath, req.params.id, options);
|
|
170
191
|
res.json({
|
|
171
192
|
success: true,
|
|
172
193
|
data: preview
|