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.
- package/CHANGELOG.md +599 -0
- package/LICENSE +21 -0
- package/README.md +439 -0
- package/bin/ctx.js +8 -0
- package/dist/web/assets/Analytics-DN_YsnkW.js +39 -0
- package/dist/web/assets/Analytics-DuYvId7u.css +1 -0
- package/dist/web/assets/ConfigTemplates-Bidwfdf2.css +1 -0
- package/dist/web/assets/ConfigTemplates-DpXIMy0p.js +1 -0
- package/dist/web/assets/Home-38JTUlYt.js +1 -0
- package/dist/web/assets/Home-CjupSEWE.css +1 -0
- package/dist/web/assets/PluginManager-CX2tgq2H.js +1 -0
- package/dist/web/assets/PluginManager-ROyoZ-6m.css +1 -0
- package/dist/web/assets/ProjectList-C1lDcsn6.js +1 -0
- package/dist/web/assets/ProjectList-oJIyIRkP.css +1 -0
- package/dist/web/assets/SessionList-C55tjV7i.css +1 -0
- package/dist/web/assets/SessionList-CZ7T6rVx.js +1 -0
- package/dist/web/assets/SkillManager-D7pd-d_P.css +1 -0
- package/dist/web/assets/SkillManager-DLN9f79y.js +1 -0
- package/dist/web/assets/WorkspaceManager-CrwgQgmP.css +1 -0
- package/dist/web/assets/WorkspaceManager-DxlHZkpZ.js +1 -0
- package/dist/web/assets/icons-DRrXwWZi.js +1 -0
- package/dist/web/assets/index-CetESrXw.css +1 -0
- package/dist/web/assets/index-Cfvn-2Gb.js +2 -0
- package/dist/web/assets/markdown-BfC0goYb.css +10 -0
- package/dist/web/assets/markdown-C9MYpaSi.js +1 -0
- package/dist/web/assets/naive-ui-DlpKk-8M.js +1 -0
- package/dist/web/assets/vendors-DMjSfzlv.js +7 -0
- package/dist/web/assets/vue-vendor-DET08QYg.js +45 -0
- package/dist/web/favicon.ico +0 -0
- package/dist/web/index.html +20 -0
- package/dist/web/logo.png +0 -0
- package/docs/bannel.png +0 -0
- package/docs/home.png +0 -0
- package/docs/logo.png +0 -0
- package/docs/model-redirection.md +251 -0
- package/docs/multi-channel-load-balancing.md +249 -0
- package/package.json +80 -0
- package/src/commands/channels.js +551 -0
- package/src/commands/cli-type.js +101 -0
- package/src/commands/daemon.js +365 -0
- package/src/commands/doctor.js +333 -0
- package/src/commands/export-config.js +205 -0
- package/src/commands/list.js +222 -0
- package/src/commands/logs.js +261 -0
- package/src/commands/plugin.js +585 -0
- package/src/commands/port-config.js +135 -0
- package/src/commands/proxy-control.js +264 -0
- package/src/commands/proxy.js +152 -0
- package/src/commands/resume.js +137 -0
- package/src/commands/search.js +190 -0
- package/src/commands/security.js +37 -0
- package/src/commands/stats.js +398 -0
- package/src/commands/switch.js +48 -0
- package/src/commands/toggle-proxy.js +247 -0
- package/src/commands/ui.js +99 -0
- package/src/commands/update.js +97 -0
- package/src/commands/workspace.js +454 -0
- package/src/config/default.js +69 -0
- package/src/config/loader.js +149 -0
- package/src/config/model-metadata.js +167 -0
- package/src/config/model-metadata.json +125 -0
- package/src/config/model-pricing.js +35 -0
- package/src/config/paths.js +190 -0
- package/src/index.js +680 -0
- package/src/plugins/constants.js +15 -0
- package/src/plugins/event-bus.js +54 -0
- package/src/plugins/manifest-validator.js +129 -0
- package/src/plugins/plugin-api.js +128 -0
- package/src/plugins/plugin-installer.js +601 -0
- package/src/plugins/plugin-loader.js +229 -0
- package/src/plugins/plugin-manager.js +170 -0
- package/src/plugins/registry.js +152 -0
- package/src/plugins/schema/plugin-manifest.json +115 -0
- package/src/reset-config.js +94 -0
- package/src/server/api/agents.js +826 -0
- package/src/server/api/aliases.js +36 -0
- package/src/server/api/channels.js +368 -0
- package/src/server/api/claude-hooks.js +480 -0
- package/src/server/api/codex-channels.js +417 -0
- package/src/server/api/codex-projects.js +104 -0
- package/src/server/api/codex-proxy.js +195 -0
- package/src/server/api/codex-sessions.js +483 -0
- package/src/server/api/codex-statistics.js +57 -0
- package/src/server/api/commands.js +482 -0
- package/src/server/api/config-export.js +212 -0
- package/src/server/api/config-registry.js +357 -0
- package/src/server/api/config-sync.js +155 -0
- package/src/server/api/config-templates.js +248 -0
- package/src/server/api/config.js +521 -0
- package/src/server/api/convert.js +260 -0
- package/src/server/api/dashboard.js +142 -0
- package/src/server/api/env.js +144 -0
- package/src/server/api/favorites.js +77 -0
- package/src/server/api/gemini-channels.js +366 -0
- package/src/server/api/gemini-projects.js +91 -0
- package/src/server/api/gemini-proxy.js +173 -0
- package/src/server/api/gemini-sessions.js +376 -0
- package/src/server/api/gemini-statistics.js +57 -0
- package/src/server/api/health-check.js +31 -0
- package/src/server/api/mcp.js +399 -0
- package/src/server/api/opencode-channels.js +419 -0
- package/src/server/api/opencode-projects.js +99 -0
- package/src/server/api/opencode-proxy.js +207 -0
- package/src/server/api/opencode-sessions.js +327 -0
- package/src/server/api/opencode-statistics.js +57 -0
- package/src/server/api/plugins.js +463 -0
- package/src/server/api/pm2-autostart.js +269 -0
- package/src/server/api/projects.js +124 -0
- package/src/server/api/prompts.js +279 -0
- package/src/server/api/proxy.js +306 -0
- package/src/server/api/security.js +53 -0
- package/src/server/api/sessions.js +514 -0
- package/src/server/api/settings.js +142 -0
- package/src/server/api/skills.js +570 -0
- package/src/server/api/statistics.js +238 -0
- package/src/server/api/ui-config.js +64 -0
- package/src/server/api/workspaces.js +456 -0
- package/src/server/codex-proxy-server.js +681 -0
- package/src/server/dev-server.js +26 -0
- package/src/server/gemini-proxy-server.js +610 -0
- package/src/server/index.js +422 -0
- package/src/server/opencode-proxy-server.js +4771 -0
- package/src/server/proxy-server.js +669 -0
- package/src/server/services/agents-service.js +1137 -0
- package/src/server/services/alias.js +71 -0
- package/src/server/services/channel-health.js +234 -0
- package/src/server/services/channel-scheduler.js +240 -0
- package/src/server/services/channels.js +447 -0
- package/src/server/services/codex-channels.js +705 -0
- package/src/server/services/codex-config.js +90 -0
- package/src/server/services/codex-parser.js +322 -0
- package/src/server/services/codex-sessions.js +936 -0
- package/src/server/services/codex-settings-manager.js +619 -0
- package/src/server/services/codex-speed-test-template.json +24 -0
- package/src/server/services/codex-statistics-service.js +161 -0
- package/src/server/services/commands-service.js +574 -0
- package/src/server/services/config-export-service.js +1165 -0
- package/src/server/services/config-registry-service.js +828 -0
- package/src/server/services/config-sync-manager.js +941 -0
- package/src/server/services/config-sync-service.js +504 -0
- package/src/server/services/config-templates-service.js +913 -0
- package/src/server/services/enhanced-cache.js +196 -0
- package/src/server/services/env-checker.js +409 -0
- package/src/server/services/env-manager.js +436 -0
- package/src/server/services/favorites.js +165 -0
- package/src/server/services/format-converter.js +620 -0
- package/src/server/services/gemini-channels.js +459 -0
- package/src/server/services/gemini-config.js +73 -0
- package/src/server/services/gemini-sessions.js +689 -0
- package/src/server/services/gemini-settings-manager.js +263 -0
- package/src/server/services/gemini-statistics-service.js +157 -0
- package/src/server/services/health-check.js +85 -0
- package/src/server/services/mcp-client.js +790 -0
- package/src/server/services/mcp-service.js +1732 -0
- package/src/server/services/model-detector.js +1245 -0
- package/src/server/services/network-access.js +80 -0
- package/src/server/services/opencode-channels.js +366 -0
- package/src/server/services/opencode-gateway-adapters.js +1168 -0
- package/src/server/services/opencode-gateway-converter.js +639 -0
- package/src/server/services/opencode-sessions.js +931 -0
- package/src/server/services/opencode-settings-manager.js +478 -0
- package/src/server/services/opencode-statistics-service.js +161 -0
- package/src/server/services/plugins-service.js +1268 -0
- package/src/server/services/prompts-service.js +534 -0
- package/src/server/services/proxy-runtime.js +79 -0
- package/src/server/services/repo-scanner-base.js +708 -0
- package/src/server/services/request-logger.js +130 -0
- package/src/server/services/response-decoder.js +21 -0
- package/src/server/services/security-config.js +131 -0
- package/src/server/services/session-cache.js +127 -0
- package/src/server/services/session-converter.js +577 -0
- package/src/server/services/sessions.js +900 -0
- package/src/server/services/settings-manager.js +163 -0
- package/src/server/services/skill-service.js +1482 -0
- package/src/server/services/speed-test.js +1146 -0
- package/src/server/services/statistics-service.js +1043 -0
- package/src/server/services/ui-config.js +132 -0
- package/src/server/services/workspace-service.js +830 -0
- package/src/server/utils/pricing.js +73 -0
- package/src/server/websocket-server.js +513 -0
- package/src/ui/menu.js +139 -0
- package/src/ui/prompts.js +100 -0
- package/src/utils/format.js +43 -0
- package/src/utils/port-helper.js +108 -0
- 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;
|