coding-tool-x 3.6.7 → 3.6.9

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.
Files changed (54) hide show
  1. package/dist/web/assets/{Analytics-CKycBI0G.js → Analytics-BuBlsbcI.js} +1 -1
  2. package/dist/web/assets/{ConfigTemplates-CACLQb8e.js → ConfigTemplates-DQczxuOX.js} +1 -1
  3. package/dist/web/assets/Home-BvAzzWnB.js +1 -0
  4. package/dist/web/assets/Home-CIua-b-o.css +1 -0
  5. package/dist/web/assets/{PluginManager-CQJl_uso.js → PluginManager-C8gAlaps.js} +1 -1
  6. package/dist/web/assets/{ProjectList-DSkvQFEP.js → ProjectList-nqgi6_cR.js} +1 -1
  7. package/dist/web/assets/{SessionList-Czjs1GN1.js → SessionList-D8AxMYy6.js} +1 -1
  8. package/dist/web/assets/{SkillManager-Cg1xZ5Cv.js → SkillManager-BDbNbNkK.js} +1 -1
  9. package/dist/web/assets/{WorkspaceManager-COjYeKvN.js → WorkspaceManager-DGGaGW2c.js} +1 -1
  10. package/dist/web/assets/index-Dsh4Q5Cg.css +1 -0
  11. package/dist/web/assets/index-y-eEayxR.js +2 -0
  12. package/dist/web/assets/{naive-ui-DM897mIP.js → naive-ui-kcWDeJwl.js} +1 -1
  13. package/dist/web/index.html +3 -3
  14. package/package.json +1 -1
  15. package/src/commands/stats.js +1 -1
  16. package/src/config/channel-preset-websites.js +2 -1
  17. package/src/config/paths.js +8 -0
  18. package/src/server/api/agents.js +16 -54
  19. package/src/server/api/claude-hooks.js +1 -1
  20. package/src/server/api/claude-statistics.js +57 -0
  21. package/src/server/api/commands.js +16 -54
  22. package/src/server/api/dashboard.js +2 -2
  23. package/src/server/api/gemini-channels.js +2 -0
  24. package/src/server/api/hooks.js +4 -1
  25. package/src/server/api/plugins.js +46 -69
  26. package/src/server/api/skills.js +21 -77
  27. package/src/server/api/validation-errors.js +34 -0
  28. package/src/server/gemini-proxy-server.js +205 -21
  29. package/src/server/index.js +1 -0
  30. package/src/server/services/agents-service.js +166 -75
  31. package/src/server/services/base/base-channel-service.js +15 -0
  32. package/src/server/services/base/response-usage-parser.js +44 -0
  33. package/src/server/services/channel-balance.js +86 -1
  34. package/src/server/services/claude-statistics-service.js +139 -0
  35. package/src/server/services/commands-service.js +203 -121
  36. package/src/server/services/config-artifact-paths.js +116 -0
  37. package/src/server/services/config-export-service.js +7 -6
  38. package/src/server/services/config-registry-service.js +2 -2
  39. package/src/server/services/config-sync-manager.js +45 -11
  40. package/src/server/services/config-templates-service.js +21 -19
  41. package/src/server/services/format-converter.js +42 -4
  42. package/src/server/services/gemini-channels.js +30 -1
  43. package/src/server/services/gemini-sessions.js +347 -61
  44. package/src/server/services/mcp-client.js +236 -49
  45. package/src/server/services/mcp-service.js +51 -64
  46. package/src/server/services/notification-hooks.js +659 -88
  47. package/src/server/services/opencode-channels.js +15 -0
  48. package/src/server/services/plugins-service.js +1137 -86
  49. package/src/server/services/repo-scanner-base.js +6 -29
  50. package/src/server/services/skill-service.js +198 -65
  51. package/dist/web/assets/Home-BeGkC4bb.css +0 -1
  52. package/dist/web/assets/Home-Bk6sP5Ed.js +0 -1
  53. package/dist/web/assets/index-0isNzXNY.css +0 -1
  54. package/dist/web/assets/index-fWgvlZQx.js +0 -2
@@ -7,9 +7,10 @@
7
7
  const express = require('express');
8
8
  const { PluginsService } = require('../services/plugins-service');
9
9
  const { maskToken } = require('../services/oauth-utils');
10
+ const { sendApiError } = require('./validation-errors');
10
11
 
11
12
  const router = express.Router();
12
- const SUPPORTED_PLATFORMS = ['claude', 'opencode'];
13
+ const SUPPORTED_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
13
14
  const pluginServices = new Map();
14
15
 
15
16
  function resolvePlatform(rawPlatform) {
@@ -41,7 +42,8 @@ function extractRepoPayload(source = {}) {
41
42
  projectPath: repo.projectPath || source.projectPath || '',
42
43
  localPath: repo.localPath || source.localPath || '',
43
44
  repoUrl: repo.repoUrl || repo.url || source.repoUrl || source.url || '',
44
- token: repo.token || source.token || ''
45
+ token: repo.token || source.token || '',
46
+ marketplace: repo.marketplace || source.marketplace || ''
45
47
  };
46
48
  }
47
49
 
@@ -63,6 +65,28 @@ function sanitizeRepos(service, repos = []) {
63
65
  return (Array.isArray(repos) ? repos : []).map(sanitizeRepo);
64
66
  }
65
67
 
68
+ /**
69
+ * 获取平台插件能力
70
+ * GET /api/plugins/capabilities
71
+ */
72
+ router.get('/capabilities', (req, res) => {
73
+ try {
74
+ const { platform, service } = getPluginsService(req);
75
+ const capabilities = typeof service.getCapabilities === 'function'
76
+ ? service.getCapabilities()
77
+ : { platform, supportsPlugins: true };
78
+
79
+ res.json({
80
+ success: true,
81
+ platform,
82
+ capabilities
83
+ });
84
+ } catch (err) {
85
+ console.error('[Plugins API] Get capabilities error:', err);
86
+ sendApiError(res, err);
87
+ }
88
+ });
89
+
66
90
  /**
67
91
  * 获取插件列表
68
92
  * GET /api/plugins
@@ -79,10 +103,7 @@ router.get('/', (req, res) => {
79
103
  });
80
104
  } catch (err) {
81
105
  console.error('[Plugins API] List plugins error:', err);
82
- res.status(500).json({
83
- success: false,
84
- message: err.message
85
- });
106
+ sendApiError(res, err);
86
107
  }
87
108
  });
88
109
 
@@ -107,10 +128,7 @@ router.get('/market', async (req, res) => {
107
128
  });
108
129
  } catch (err) {
109
130
  console.error('[Plugins API] Get market plugins error:', err);
110
- res.status(500).json({
111
- success: false,
112
- message: err.message
113
- });
131
+ sendApiError(res, err);
114
132
  }
115
133
  });
116
134
 
@@ -123,12 +141,13 @@ router.post('/install', async (req, res) => {
123
141
  try {
124
142
  const { platform, service } = getPluginsService(req);
125
143
  const { directory, repo, gitUrl, source } = req.body;
144
+ const hasDirectoryField = Object.prototype.hasOwnProperty.call(req.body, 'directory');
126
145
 
127
146
  // Support both new format (directory + repo) and legacy format (gitUrl)
128
147
  let installUrl;
129
148
  if (source) {
130
149
  installUrl = source;
131
- } else if (directory && repo) {
150
+ } else if (repo && hasDirectoryField) {
132
151
  installUrl = '';
133
152
  } else if (gitUrl) {
134
153
  installUrl = gitUrl;
@@ -141,10 +160,10 @@ router.post('/install', async (req, res) => {
141
160
 
142
161
  const result = await service.installPlugin(
143
162
  installUrl,
144
- directory && repo
163
+ repo && hasDirectoryField
145
164
  ? {
146
165
  ...extractRepoPayload({ repo }),
147
- directory
166
+ directory: directory || ''
148
167
  }
149
168
  : null
150
169
  );
@@ -164,10 +183,7 @@ router.post('/install', async (req, res) => {
164
183
  });
165
184
  } catch (err) {
166
185
  console.error('[Plugins API] Install plugin error:', err);
167
- res.status(500).json({
168
- success: false,
169
- message: err.message
170
- });
186
+ sendApiError(res, err);
171
187
  }
172
188
  });
173
189
 
@@ -188,10 +204,7 @@ router.get('/repos', (req, res) => {
188
204
  });
189
205
  } catch (err) {
190
206
  console.error('[Plugins API] Get repos error:', err);
191
- res.status(500).json({
192
- success: false,
193
- message: err.message
194
- });
207
+ sendApiError(res, err);
195
208
  }
196
209
  });
197
210
 
@@ -223,10 +236,7 @@ router.post('/repos', (req, res) => {
223
236
  });
224
237
  } catch (err) {
225
238
  console.error('[Plugins API] Add repo error:', err);
226
- res.status(500).json({
227
- success: false,
228
- message: err.message
229
- });
239
+ sendApiError(res, err);
230
240
  }
231
241
  });
232
242
 
@@ -243,10 +253,7 @@ router.delete('/repos', (req, res) => {
243
253
  });
244
254
  } catch (err) {
245
255
  console.error('[Plugins API] Remove repo error:', err);
246
- res.status(500).json({
247
- success: false,
248
- message: err.message
249
- });
256
+ sendApiError(res, err);
250
257
  }
251
258
  });
252
259
 
@@ -271,10 +278,7 @@ router.put('/repos/toggle', (req, res) => {
271
278
  });
272
279
  } catch (err) {
273
280
  console.error('[Plugins API] Toggle repo error:', err);
274
- res.status(500).json({
275
- success: false,
276
- message: err.message
277
- });
281
+ sendApiError(res, err);
278
282
  }
279
283
  });
280
284
 
@@ -298,10 +302,7 @@ router.delete('/repos/:owner/:name', (req, res) => {
298
302
  });
299
303
  } catch (err) {
300
304
  console.error('[Plugins API] Remove repo error:', err);
301
- res.status(500).json({
302
- success: false,
303
- message: err.message
304
- });
305
+ sendApiError(res, err);
305
306
  }
306
307
  });
307
308
 
@@ -333,10 +334,7 @@ router.put('/repos/:owner/:name/toggle', (req, res) => {
333
334
  });
334
335
  } catch (err) {
335
336
  console.error('[Plugins API] Toggle repo error:', err);
336
- res.status(500).json({
337
- success: false,
338
- message: err.message
339
- });
337
+ sendApiError(res, err);
340
338
  }
341
339
  });
342
340
 
@@ -367,10 +365,7 @@ router.put('/repos/auth', (req, res) => {
367
365
  });
368
366
  } catch (err) {
369
367
  console.error('[Plugins API] Update repo auth error:', err);
370
- res.status(500).json({
371
- success: false,
372
- message: err.message
373
- });
368
+ sendApiError(res, err);
374
369
  }
375
370
  });
376
371
 
@@ -391,10 +386,7 @@ router.post('/repos/sync', async (req, res) => {
391
386
  });
392
387
  } catch (err) {
393
388
  console.error('[Plugins API] Sync repos error:', err);
394
- res.status(500).json({
395
- success: false,
396
- message: err.message
397
- });
389
+ sendApiError(res, err);
398
390
  }
399
391
  });
400
392
 
@@ -415,10 +407,7 @@ router.post('/sync', async (req, res) => {
415
407
  });
416
408
  } catch (err) {
417
409
  console.error('[Plugins API] Sync plugins error:', err);
418
- res.status(500).json({
419
- success: false,
420
- message: err.message
421
- });
410
+ sendApiError(res, err);
422
411
  }
423
412
  });
424
413
 
@@ -504,10 +493,7 @@ router.get('/:name', (req, res) => {
504
493
  });
505
494
  } catch (err) {
506
495
  console.error('[Plugins API] Get plugin error:', err);
507
- res.status(500).json({
508
- success: false,
509
- message: err.message
510
- });
496
+ sendApiError(res, err);
511
497
  }
512
498
  });
513
499
 
@@ -536,10 +522,7 @@ router.delete('/:name', (req, res) => {
536
522
  });
537
523
  } catch (err) {
538
524
  console.error('[Plugins API] Uninstall plugin error:', err);
539
- res.status(500).json({
540
- success: false,
541
- message: err.message
542
- });
525
+ sendApiError(res, err);
543
526
  }
544
527
  });
545
528
 
@@ -571,10 +554,7 @@ router.put('/:name/toggle', (req, res) => {
571
554
  });
572
555
  } catch (err) {
573
556
  console.error('[Plugins API] Toggle plugin error:', err);
574
- res.status(500).json({
575
- success: false,
576
- message: err.message
577
- });
557
+ sendApiError(res, err);
578
558
  }
579
559
  });
580
560
 
@@ -605,10 +585,7 @@ router.put('/:name/config', (req, res) => {
605
585
  });
606
586
  } catch (err) {
607
587
  console.error('[Plugins API] Update plugin config error:', err);
608
- res.status(500).json({
609
- success: false,
610
- message: err.message
611
- });
588
+ sendApiError(res, err);
612
589
  }
613
590
  });
614
591
 
@@ -5,6 +5,7 @@
5
5
  const express = require('express');
6
6
  const { SkillService } = require('../services/skill-service');
7
7
  const { maskToken } = require('../services/oauth-utils');
8
+ const { sendApiError } = require('./validation-errors');
8
9
 
9
10
  const router = express.Router();
10
11
  const SUPPORTED_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
@@ -84,10 +85,7 @@ router.get('/', async (req, res) => {
84
85
  });
85
86
  } catch (err) {
86
87
  console.error('[Skills API] List skills error:', err);
87
- res.status(500).json({
88
- success: false,
89
- message: err.message
90
- });
88
+ sendApiError(res, err);
91
89
  }
92
90
  });
93
91
 
@@ -116,10 +114,7 @@ router.get('/detail/*', async (req, res) => {
116
114
  });
117
115
  } catch (err) {
118
116
  console.error('[Skills API] Get skill detail error:', err);
119
- res.status(500).json({
120
- success: false,
121
- message: err.message
122
- });
117
+ sendApiError(res, err);
123
118
  }
124
119
  });
125
120
 
@@ -138,10 +133,7 @@ router.get('/installed', (req, res) => {
138
133
  });
139
134
  } catch (err) {
140
135
  console.error('[Skills API] Get installed skills error:', err);
141
- res.status(500).json({
142
- success: false,
143
- message: err.message
144
- });
136
+ sendApiError(res, err);
145
137
  }
146
138
  });
147
139
 
@@ -184,10 +176,7 @@ router.post('/install', async (req, res) => {
184
176
  });
185
177
  } catch (err) {
186
178
  console.error('[Skills API] Install skill error:', err);
187
- res.status(500).json({
188
- success: false,
189
- message: err.message
190
- });
179
+ sendApiError(res, err);
191
180
  }
192
181
  });
193
182
 
@@ -209,7 +198,7 @@ router.post('/install-local', (req, res) => {
209
198
  res.json({ success: true, platform, ...result });
210
199
  } catch (err) {
211
200
  console.error('[Skills API] Install local skill error:', err);
212
- res.status(500).json({ success: false, message: err.message });
201
+ sendApiError(res, err);
213
202
  }
214
203
  });
215
204
 
@@ -259,10 +248,7 @@ router.post('/create', (req, res) => {
259
248
  });
260
249
  } catch (err) {
261
250
  console.error('[Skills API] Create skill error:', err);
262
- res.status(500).json({
263
- success: false,
264
- message: err.message
265
- });
251
+ sendApiError(res, err);
266
252
  }
267
253
  });
268
254
 
@@ -292,10 +278,7 @@ router.post('/uninstall', (req, res) => {
292
278
  });
293
279
  } catch (err) {
294
280
  console.error('[Skills API] Uninstall skill error:', err);
295
- res.status(500).json({
296
- success: false,
297
- message: err.message
298
- });
281
+ sendApiError(res, err);
299
282
  }
300
283
  });
301
284
 
@@ -314,10 +297,7 @@ router.get('/repos', (req, res) => {
314
297
  });
315
298
  } catch (err) {
316
299
  console.error('[Skills API] Get repos error:', err);
317
- res.status(500).json({
318
- success: false,
319
- message: err.message
320
- });
300
+ sendApiError(res, err);
321
301
  }
322
302
  });
323
303
 
@@ -349,10 +329,7 @@ router.post('/repos', (req, res) => {
349
329
  });
350
330
  } catch (err) {
351
331
  console.error('[Skills API] Add repo error:', err);
352
- res.status(500).json({
353
- success: false,
354
- message: err.message
355
- });
332
+ sendApiError(res, err);
356
333
  }
357
334
  });
358
335
 
@@ -369,10 +346,7 @@ router.delete('/repos', (req, res) => {
369
346
  });
370
347
  } catch (err) {
371
348
  console.error('[Skills API] Remove repo error:', err);
372
- res.status(500).json({
373
- success: false,
374
- message: err.message
375
- });
349
+ sendApiError(res, err);
376
350
  }
377
351
  });
378
352
 
@@ -390,10 +364,7 @@ router.put('/repos/toggle', (req, res) => {
390
364
  });
391
365
  } catch (err) {
392
366
  console.error('[Skills API] Toggle repo error:', err);
393
- res.status(500).json({
394
- success: false,
395
- message: err.message
396
- });
367
+ sendApiError(res, err);
397
368
  }
398
369
  });
399
370
 
@@ -425,10 +396,7 @@ router.put('/repos/auth', (req, res) => {
425
396
  });
426
397
  } catch (err) {
427
398
  console.error('[Skills API] Update repo auth error:', err);
428
- res.status(500).json({
429
- success: false,
430
- message: err.message
431
- });
399
+ sendApiError(res, err);
432
400
  }
433
401
  });
434
402
 
@@ -451,10 +419,7 @@ router.delete('/repos/:owner/:name', (req, res) => {
451
419
  });
452
420
  } catch (err) {
453
421
  console.error('[Skills API] Remove repo error:', err);
454
- res.status(500).json({
455
- success: false,
456
- message: err.message
457
- });
422
+ sendApiError(res, err);
458
423
  }
459
424
  });
460
425
 
@@ -479,10 +444,7 @@ router.put('/repos/:owner/:name/toggle', (req, res) => {
479
444
  });
480
445
  } catch (err) {
481
446
  console.error('[Skills API] Toggle repo error:', err);
482
- res.status(500).json({
483
- success: false,
484
- message: err.message
485
- });
447
+ sendApiError(res, err);
486
448
  }
487
449
  });
488
450
 
@@ -529,10 +491,7 @@ router.post('/create-with-files', (req, res) => {
529
491
  });
530
492
  } catch (err) {
531
493
  console.error('[Skills API] Create skill with files error:', err);
532
- res.status(500).json({
533
- success: false,
534
- message: err.message
535
- });
494
+ sendApiError(res, err);
536
495
  }
537
496
  });
538
497
 
@@ -554,10 +513,7 @@ router.get('/:directory/files', (req, res) => {
554
513
  });
555
514
  } catch (err) {
556
515
  console.error('[Skills API] Get skill files error:', err);
557
- res.status(500).json({
558
- success: false,
559
- message: err.message
560
- });
516
+ sendApiError(res, err);
561
517
  }
562
518
  });
563
519
 
@@ -588,10 +544,7 @@ router.get('/:directory/file/*', (req, res) => {
588
544
  });
589
545
  } catch (err) {
590
546
  console.error('[Skills API] Get skill file content error:', err);
591
- res.status(500).json({
592
- success: false,
593
- message: err.message
594
- });
547
+ sendApiError(res, err);
595
548
  }
596
549
  });
597
550
 
@@ -622,10 +575,7 @@ router.post('/:directory/files', (req, res) => {
622
575
  });
623
576
  } catch (err) {
624
577
  console.error('[Skills API] Add skill files error:', err);
625
- res.status(500).json({
626
- success: false,
627
- message: err.message
628
- });
578
+ sendApiError(res, err);
629
579
  }
630
580
  });
631
581
 
@@ -655,10 +605,7 @@ router.delete('/:directory/file/*', (req, res) => {
655
605
  });
656
606
  } catch (err) {
657
607
  console.error('[Skills API] Delete skill file error:', err);
658
- res.status(500).json({
659
- success: false,
660
- message: err.message
661
- });
608
+ sendApiError(res, err);
662
609
  }
663
610
  });
664
611
 
@@ -697,10 +644,7 @@ router.put('/:directory/file/*', (req, res) => {
697
644
  });
698
645
  } catch (err) {
699
646
  console.error('[Skills API] Update skill file error:', err);
700
- res.status(500).json({
701
- success: false,
702
- message: err.message
703
- });
647
+ sendApiError(res, err);
704
648
  }
705
649
  });
706
650
 
@@ -0,0 +1,34 @@
1
+ const VALIDATION_ERROR_PATTERNS = [
2
+ /^Invalid\b/i,
3
+ /不能为空/,
4
+ /不合法/,
5
+ /只能包含/,
6
+ /不能包含/,
7
+ /必须/,
8
+ /不能/,
9
+ /Missing\b/i,
10
+ /requires?/i,
11
+ /required/i,
12
+ /escapes/i,
13
+ /not support/i,
14
+ /不支持/
15
+ ];
16
+
17
+ function isValidationError(error) {
18
+ const message = String(error?.message || error || '');
19
+ return VALIDATION_ERROR_PATTERNS.some(pattern => pattern.test(message));
20
+ }
21
+
22
+ function sendApiError(res, error, fallbackStatus = 500, extra = {}) {
23
+ const status = isValidationError(error) ? 400 : fallbackStatus;
24
+ return res.status(status).json({
25
+ success: false,
26
+ message: error?.message || String(error),
27
+ ...extra
28
+ });
29
+ }
30
+
31
+ module.exports = {
32
+ isValidationError,
33
+ sendApiError
34
+ };