coding-tool-x 3.2.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.
Files changed (185) hide show
  1. package/CHANGELOG.md +599 -0
  2. package/LICENSE +21 -0
  3. package/README.md +439 -0
  4. package/bin/ctx.js +8 -0
  5. package/dist/web/assets/Analytics-DN_YsnkW.js +39 -0
  6. package/dist/web/assets/Analytics-DuYvId7u.css +1 -0
  7. package/dist/web/assets/ConfigTemplates-Bidwfdf2.css +1 -0
  8. package/dist/web/assets/ConfigTemplates-DpXIMy0p.js +1 -0
  9. package/dist/web/assets/Home-38JTUlYt.js +1 -0
  10. package/dist/web/assets/Home-CjupSEWE.css +1 -0
  11. package/dist/web/assets/PluginManager-CX2tgq2H.js +1 -0
  12. package/dist/web/assets/PluginManager-ROyoZ-6m.css +1 -0
  13. package/dist/web/assets/ProjectList-C1lDcsn6.js +1 -0
  14. package/dist/web/assets/ProjectList-oJIyIRkP.css +1 -0
  15. package/dist/web/assets/SessionList-C55tjV7i.css +1 -0
  16. package/dist/web/assets/SessionList-CZ7T6rVx.js +1 -0
  17. package/dist/web/assets/SkillManager-D7pd-d_P.css +1 -0
  18. package/dist/web/assets/SkillManager-DLN9f79y.js +1 -0
  19. package/dist/web/assets/WorkspaceManager-CrwgQgmP.css +1 -0
  20. package/dist/web/assets/WorkspaceManager-DxlHZkpZ.js +1 -0
  21. package/dist/web/assets/icons-DRrXwWZi.js +1 -0
  22. package/dist/web/assets/index-CetESrXw.css +1 -0
  23. package/dist/web/assets/index-Cfvn-2Gb.js +2 -0
  24. package/dist/web/assets/markdown-BfC0goYb.css +10 -0
  25. package/dist/web/assets/markdown-C9MYpaSi.js +1 -0
  26. package/dist/web/assets/naive-ui-DlpKk-8M.js +1 -0
  27. package/dist/web/assets/vendors-DMjSfzlv.js +7 -0
  28. package/dist/web/assets/vue-vendor-DET08QYg.js +45 -0
  29. package/dist/web/favicon.ico +0 -0
  30. package/dist/web/index.html +20 -0
  31. package/dist/web/logo.png +0 -0
  32. package/docs/bannel.png +0 -0
  33. package/docs/home.png +0 -0
  34. package/docs/logo.png +0 -0
  35. package/docs/model-redirection.md +251 -0
  36. package/docs/multi-channel-load-balancing.md +249 -0
  37. package/package.json +80 -0
  38. package/src/commands/channels.js +551 -0
  39. package/src/commands/cli-type.js +101 -0
  40. package/src/commands/daemon.js +365 -0
  41. package/src/commands/doctor.js +333 -0
  42. package/src/commands/export-config.js +205 -0
  43. package/src/commands/list.js +222 -0
  44. package/src/commands/logs.js +261 -0
  45. package/src/commands/plugin.js +585 -0
  46. package/src/commands/port-config.js +135 -0
  47. package/src/commands/proxy-control.js +264 -0
  48. package/src/commands/proxy.js +152 -0
  49. package/src/commands/resume.js +137 -0
  50. package/src/commands/search.js +190 -0
  51. package/src/commands/security.js +37 -0
  52. package/src/commands/stats.js +398 -0
  53. package/src/commands/switch.js +48 -0
  54. package/src/commands/toggle-proxy.js +247 -0
  55. package/src/commands/ui.js +99 -0
  56. package/src/commands/update.js +97 -0
  57. package/src/commands/workspace.js +454 -0
  58. package/src/config/default.js +69 -0
  59. package/src/config/loader.js +149 -0
  60. package/src/config/model-metadata.js +167 -0
  61. package/src/config/model-metadata.json +125 -0
  62. package/src/config/model-pricing.js +35 -0
  63. package/src/config/paths.js +190 -0
  64. package/src/index.js +680 -0
  65. package/src/plugins/constants.js +15 -0
  66. package/src/plugins/event-bus.js +54 -0
  67. package/src/plugins/manifest-validator.js +129 -0
  68. package/src/plugins/plugin-api.js +128 -0
  69. package/src/plugins/plugin-installer.js +601 -0
  70. package/src/plugins/plugin-loader.js +229 -0
  71. package/src/plugins/plugin-manager.js +170 -0
  72. package/src/plugins/registry.js +152 -0
  73. package/src/plugins/schema/plugin-manifest.json +115 -0
  74. package/src/reset-config.js +94 -0
  75. package/src/server/api/agents.js +826 -0
  76. package/src/server/api/aliases.js +36 -0
  77. package/src/server/api/channels.js +368 -0
  78. package/src/server/api/claude-hooks.js +480 -0
  79. package/src/server/api/codex-channels.js +417 -0
  80. package/src/server/api/codex-projects.js +104 -0
  81. package/src/server/api/codex-proxy.js +195 -0
  82. package/src/server/api/codex-sessions.js +483 -0
  83. package/src/server/api/codex-statistics.js +57 -0
  84. package/src/server/api/commands.js +482 -0
  85. package/src/server/api/config-export.js +212 -0
  86. package/src/server/api/config-registry.js +357 -0
  87. package/src/server/api/config-sync.js +155 -0
  88. package/src/server/api/config-templates.js +248 -0
  89. package/src/server/api/config.js +521 -0
  90. package/src/server/api/convert.js +260 -0
  91. package/src/server/api/dashboard.js +142 -0
  92. package/src/server/api/env.js +144 -0
  93. package/src/server/api/favorites.js +77 -0
  94. package/src/server/api/gemini-channels.js +366 -0
  95. package/src/server/api/gemini-projects.js +91 -0
  96. package/src/server/api/gemini-proxy.js +173 -0
  97. package/src/server/api/gemini-sessions.js +376 -0
  98. package/src/server/api/gemini-statistics.js +57 -0
  99. package/src/server/api/health-check.js +31 -0
  100. package/src/server/api/mcp.js +399 -0
  101. package/src/server/api/opencode-channels.js +419 -0
  102. package/src/server/api/opencode-projects.js +99 -0
  103. package/src/server/api/opencode-proxy.js +207 -0
  104. package/src/server/api/opencode-sessions.js +327 -0
  105. package/src/server/api/opencode-statistics.js +57 -0
  106. package/src/server/api/plugins.js +463 -0
  107. package/src/server/api/pm2-autostart.js +269 -0
  108. package/src/server/api/projects.js +124 -0
  109. package/src/server/api/prompts.js +279 -0
  110. package/src/server/api/proxy.js +306 -0
  111. package/src/server/api/security.js +53 -0
  112. package/src/server/api/sessions.js +514 -0
  113. package/src/server/api/settings.js +142 -0
  114. package/src/server/api/skills.js +570 -0
  115. package/src/server/api/statistics.js +238 -0
  116. package/src/server/api/ui-config.js +64 -0
  117. package/src/server/api/workspaces.js +456 -0
  118. package/src/server/codex-proxy-server.js +681 -0
  119. package/src/server/dev-server.js +26 -0
  120. package/src/server/gemini-proxy-server.js +610 -0
  121. package/src/server/index.js +422 -0
  122. package/src/server/opencode-proxy-server.js +4771 -0
  123. package/src/server/proxy-server.js +669 -0
  124. package/src/server/services/agents-service.js +1137 -0
  125. package/src/server/services/alias.js +71 -0
  126. package/src/server/services/channel-health.js +234 -0
  127. package/src/server/services/channel-scheduler.js +240 -0
  128. package/src/server/services/channels.js +447 -0
  129. package/src/server/services/codex-channels.js +705 -0
  130. package/src/server/services/codex-config.js +90 -0
  131. package/src/server/services/codex-parser.js +322 -0
  132. package/src/server/services/codex-sessions.js +936 -0
  133. package/src/server/services/codex-settings-manager.js +619 -0
  134. package/src/server/services/codex-speed-test-template.json +24 -0
  135. package/src/server/services/codex-statistics-service.js +161 -0
  136. package/src/server/services/commands-service.js +574 -0
  137. package/src/server/services/config-export-service.js +1165 -0
  138. package/src/server/services/config-registry-service.js +828 -0
  139. package/src/server/services/config-sync-manager.js +941 -0
  140. package/src/server/services/config-sync-service.js +504 -0
  141. package/src/server/services/config-templates-service.js +913 -0
  142. package/src/server/services/enhanced-cache.js +196 -0
  143. package/src/server/services/env-checker.js +409 -0
  144. package/src/server/services/env-manager.js +436 -0
  145. package/src/server/services/favorites.js +165 -0
  146. package/src/server/services/format-converter.js +620 -0
  147. package/src/server/services/gemini-channels.js +459 -0
  148. package/src/server/services/gemini-config.js +73 -0
  149. package/src/server/services/gemini-sessions.js +689 -0
  150. package/src/server/services/gemini-settings-manager.js +263 -0
  151. package/src/server/services/gemini-statistics-service.js +157 -0
  152. package/src/server/services/health-check.js +85 -0
  153. package/src/server/services/mcp-client.js +790 -0
  154. package/src/server/services/mcp-service.js +1732 -0
  155. package/src/server/services/model-detector.js +1245 -0
  156. package/src/server/services/network-access.js +80 -0
  157. package/src/server/services/opencode-channels.js +366 -0
  158. package/src/server/services/opencode-gateway-adapters.js +1168 -0
  159. package/src/server/services/opencode-gateway-converter.js +639 -0
  160. package/src/server/services/opencode-sessions.js +931 -0
  161. package/src/server/services/opencode-settings-manager.js +478 -0
  162. package/src/server/services/opencode-statistics-service.js +161 -0
  163. package/src/server/services/plugins-service.js +1268 -0
  164. package/src/server/services/prompts-service.js +534 -0
  165. package/src/server/services/proxy-runtime.js +79 -0
  166. package/src/server/services/repo-scanner-base.js +708 -0
  167. package/src/server/services/request-logger.js +130 -0
  168. package/src/server/services/response-decoder.js +21 -0
  169. package/src/server/services/security-config.js +131 -0
  170. package/src/server/services/session-cache.js +127 -0
  171. package/src/server/services/session-converter.js +577 -0
  172. package/src/server/services/sessions.js +900 -0
  173. package/src/server/services/settings-manager.js +163 -0
  174. package/src/server/services/skill-service.js +1482 -0
  175. package/src/server/services/speed-test.js +1146 -0
  176. package/src/server/services/statistics-service.js +1043 -0
  177. package/src/server/services/ui-config.js +132 -0
  178. package/src/server/services/workspace-service.js +830 -0
  179. package/src/server/utils/pricing.js +73 -0
  180. package/src/server/websocket-server.js +513 -0
  181. package/src/ui/menu.js +139 -0
  182. package/src/ui/prompts.js +100 -0
  183. package/src/utils/format.js +43 -0
  184. package/src/utils/port-helper.js +108 -0
  185. package/src/utils/session.js +240 -0
@@ -0,0 +1,357 @@
1
+ /**
2
+ * Config Registry API 路由
3
+ *
4
+ * Exposes config registry functionality via REST API.
5
+ * Manages skills, commands, agents, plugins with enable/disable and per-platform support.
6
+ */
7
+
8
+ const express = require('express');
9
+ const { ConfigRegistryService, CONFIG_TYPES, SUPPORTED_PLATFORMS } = require('../services/config-registry-service');
10
+ const { ConfigSyncManager } = require('../services/config-sync-manager');
11
+
12
+ const router = express.Router();
13
+ const registryService = new ConfigRegistryService();
14
+ const syncManager = new ConfigSyncManager();
15
+
16
+ // Valid config types
17
+ const VALID_TYPES = CONFIG_TYPES;
18
+
19
+ // Valid platforms
20
+ const VALID_PLATFORMS = SUPPORTED_PLATFORMS;
21
+
22
+ /**
23
+ * Validate config type parameter
24
+ * @param {string} type - Config type
25
+ * @returns {string|null} Error message or null if valid
26
+ */
27
+ function validateType(type) {
28
+ if (!VALID_TYPES.includes(type)) {
29
+ return `Invalid config type: ${type}. Must be one of: ${VALID_TYPES.join(', ')}`;
30
+ }
31
+ return null;
32
+ }
33
+
34
+ /**
35
+ * Validate platform parameter
36
+ * @param {string} platform - Platform name
37
+ * @returns {string|null} Error message or null if valid
38
+ */
39
+ function validatePlatform(platform) {
40
+ if (!VALID_PLATFORMS.includes(platform)) {
41
+ return `Invalid platform: ${platform}. Must be one of: ${VALID_PLATFORMS.join(', ')}`;
42
+ }
43
+ return null;
44
+ }
45
+
46
+ /**
47
+ * GET /api/config-registry/stats
48
+ * Get statistics for all config types
49
+ */
50
+ router.get('/stats', async (req, res) => {
51
+ try {
52
+ const stats = registryService.getStats();
53
+
54
+ res.json({
55
+ success: true,
56
+ stats
57
+ });
58
+ } catch (err) {
59
+ console.error('[ConfigRegistry API] Get stats error:', err);
60
+ res.status(500).json({
61
+ success: false,
62
+ message: err.message
63
+ });
64
+ }
65
+ });
66
+
67
+ /**
68
+ * GET /api/config-registry/:type
69
+ * List all items for a config type (skills, commands, agents, plugins)
70
+ * Returns { success: true, items: { name: registryEntry } }
71
+ */
72
+ router.get('/:type', async (req, res) => {
73
+ try {
74
+ const { type } = req.params;
75
+
76
+ const typeError = validateType(type);
77
+ if (typeError) {
78
+ return res.status(400).json({
79
+ success: false,
80
+ message: typeError
81
+ });
82
+ }
83
+
84
+ const items = registryService.listItems(type);
85
+
86
+ res.json({
87
+ success: true,
88
+ type,
89
+ items
90
+ });
91
+ } catch (err) {
92
+ console.error('[ConfigRegistry API] List items error:', err);
93
+ res.status(500).json({
94
+ success: false,
95
+ message: err.message
96
+ });
97
+ }
98
+ });
99
+
100
+ /**
101
+ * POST /api/config-registry/:type/import
102
+ * Import configs from Claude Code native directories to cc-tool
103
+ * - Scans ~/.claude/{type}/
104
+ * - Copies new items to cc-tool/configs/{type}/
105
+ * - Registers them with enabled: true
106
+ * - Syncs back to Claude (since they were already there)
107
+ * Returns { success: true, imported: number, skipped: number, items: [...] }
108
+ */
109
+ router.post('/:type/import', async (req, res) => {
110
+ try {
111
+ const { type } = req.params;
112
+
113
+ const typeError = validateType(type);
114
+ if (typeError) {
115
+ return res.status(400).json({
116
+ success: false,
117
+ message: typeError
118
+ });
119
+ }
120
+
121
+ // Import from Claude Code directories
122
+ const result = registryService.importFromClaude(type);
123
+
124
+ // Sync imported items back to Claude (they were already there but now managed by cc-tool)
125
+ // This ensures consistency between registry and actual files
126
+ if (result.imported > 0) {
127
+ for (const name of result.items) {
128
+ const item = registryService.getItem(type, name);
129
+ if (item && item.enabled && item.platforms?.claude) {
130
+ syncManager.syncToClaude(type, name);
131
+ }
132
+ }
133
+ }
134
+
135
+ res.json({
136
+ success: true,
137
+ type,
138
+ imported: result.imported,
139
+ skipped: result.skipped,
140
+ items: result.items
141
+ });
142
+ } catch (err) {
143
+ console.error('[ConfigRegistry API] Import error:', err);
144
+ res.status(500).json({
145
+ success: false,
146
+ message: err.message
147
+ });
148
+ }
149
+ });
150
+
151
+ /**
152
+ * PUT /api/config-registry/:type/:name/toggle
153
+ * Toggle enabled/disabled status
154
+ * Body: { enabled: boolean }
155
+ * - Updates registry
156
+ * - If enabling: sync to platforms where platform=true
157
+ * - If disabling: remove from all platforms
158
+ * Returns { success: true, item: updatedEntry }
159
+ */
160
+ router.put('/:type/:name/toggle', async (req, res) => {
161
+ try {
162
+ const { type } = req.params;
163
+ const name = decodeURIComponent(req.params.name);
164
+ const { enabled } = req.body;
165
+
166
+ const typeError = validateType(type);
167
+ if (typeError) {
168
+ return res.status(400).json({
169
+ success: false,
170
+ message: typeError
171
+ });
172
+ }
173
+
174
+ if (typeof enabled !== 'boolean') {
175
+ return res.status(400).json({
176
+ success: false,
177
+ message: 'enabled must be a boolean'
178
+ });
179
+ }
180
+
181
+ // Check if item exists
182
+ const existing = registryService.getItem(type, name);
183
+ if (!existing) {
184
+ return res.status(404).json({
185
+ success: false,
186
+ message: `Item "${name}" not found in ${type}`
187
+ });
188
+ }
189
+
190
+ // Update registry
191
+ const item = registryService.toggleEnabled(type, name, enabled);
192
+
193
+ // Apply sync based on new state
194
+ if (enabled) {
195
+ // Sync to platforms where platform=true
196
+ if (item.platforms?.claude) {
197
+ syncManager.syncToClaude(type, name);
198
+ }
199
+ if (item.platforms?.codex) {
200
+ syncManager.syncToCodex(type, name);
201
+ }
202
+ if (item.platforms?.gemini) {
203
+ syncManager.syncToGemini(type, name);
204
+ }
205
+ if (item.platforms?.opencode) {
206
+ syncManager.syncToOpenCode(type, name);
207
+ }
208
+ } else {
209
+ // Remove from all platforms
210
+ syncManager.removeFromClaude(type, name);
211
+ syncManager.removeFromCodex(type, name);
212
+ syncManager.removeFromGemini(type, name);
213
+ syncManager.removeFromOpenCode(type, name);
214
+ }
215
+
216
+ res.json({
217
+ success: true,
218
+ item
219
+ });
220
+ } catch (err) {
221
+ console.error('[ConfigRegistry API] Toggle enabled error:', err);
222
+ res.status(500).json({
223
+ success: false,
224
+ message: err.message
225
+ });
226
+ }
227
+ });
228
+
229
+ /**
230
+ * PUT /api/config-registry/:type/:name/platform/:platform
231
+ * Toggle platform (claude/codex/gemini/opencode) for an item
232
+ * Body: { enabled: boolean }
233
+ * - Updates registry
234
+ * - If enabling platform: sync to that platform (if item is enabled)
235
+ * - If disabling platform: remove from that platform
236
+ * Returns { success: true, item: updatedEntry }
237
+ */
238
+ router.put('/:type/:name/platform/:platform', async (req, res) => {
239
+ try {
240
+ const { type, platform } = req.params;
241
+ const name = decodeURIComponent(req.params.name);
242
+ const { enabled } = req.body;
243
+
244
+ const typeError = validateType(type);
245
+ if (typeError) {
246
+ return res.status(400).json({
247
+ success: false,
248
+ message: typeError
249
+ });
250
+ }
251
+
252
+ const platformError = validatePlatform(platform);
253
+ if (platformError) {
254
+ return res.status(400).json({
255
+ success: false,
256
+ message: platformError
257
+ });
258
+ }
259
+
260
+ if (typeof enabled !== 'boolean') {
261
+ return res.status(400).json({
262
+ success: false,
263
+ message: 'enabled must be a boolean'
264
+ });
265
+ }
266
+
267
+ // Check if item exists
268
+ const existing = registryService.getItem(type, name);
269
+ if (!existing) {
270
+ return res.status(404).json({
271
+ success: false,
272
+ message: `Item "${name}" not found in ${type}`
273
+ });
274
+ }
275
+
276
+ // Update registry
277
+ const item = registryService.togglePlatform(type, name, platform, enabled);
278
+
279
+ // Apply sync based on new state
280
+ if (enabled && item.enabled) {
281
+ // Sync to this platform (only if item is enabled)
282
+ if (platform === 'claude') {
283
+ syncManager.syncToClaude(type, name);
284
+ } else if (platform === 'codex') {
285
+ syncManager.syncToCodex(type, name);
286
+ } else if (platform === 'gemini') {
287
+ syncManager.syncToGemini(type, name);
288
+ } else if (platform === 'opencode') {
289
+ syncManager.syncToOpenCode(type, name);
290
+ }
291
+ } else {
292
+ // Remove from this platform
293
+ if (platform === 'claude') {
294
+ syncManager.removeFromClaude(type, name);
295
+ } else if (platform === 'codex') {
296
+ syncManager.removeFromCodex(type, name);
297
+ } else if (platform === 'gemini') {
298
+ syncManager.removeFromGemini(type, name);
299
+ } else if (platform === 'opencode') {
300
+ syncManager.removeFromOpenCode(type, name);
301
+ }
302
+ }
303
+
304
+ res.json({
305
+ success: true,
306
+ item
307
+ });
308
+ } catch (err) {
309
+ console.error('[ConfigRegistry API] Toggle platform error:', err);
310
+ res.status(500).json({
311
+ success: false,
312
+ message: err.message
313
+ });
314
+ }
315
+ });
316
+
317
+ /**
318
+ * POST /api/config-registry/:type/sync
319
+ * Force sync all items of a type to their platforms based on registry
320
+ * Returns { success: true, synced: number }
321
+ */
322
+ router.post('/:type/sync', async (req, res) => {
323
+ try {
324
+ const { type } = req.params;
325
+
326
+ const typeError = validateType(type);
327
+ if (typeError) {
328
+ return res.status(400).json({
329
+ success: false,
330
+ message: typeError
331
+ });
332
+ }
333
+
334
+ // Get all items for this type
335
+ const items = registryService.listItems(type);
336
+
337
+ // Sync all items based on their registry state
338
+ const result = syncManager.syncAll(type, items);
339
+
340
+ res.json({
341
+ success: true,
342
+ type,
343
+ synced: result.synced.length,
344
+ removed: result.removed.length,
345
+ errors: result.errors,
346
+ warnings: result.warnings
347
+ });
348
+ } catch (err) {
349
+ console.error('[ConfigRegistry API] Sync all error:', err);
350
+ res.status(500).json({
351
+ success: false,
352
+ message: err.message
353
+ });
354
+ }
355
+ });
356
+
357
+ module.exports = router;
@@ -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;