@adversity/coding-tool-x 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -18
- package/README.md +8 -8
- package/dist/web/assets/ConfigTemplates-Bidwfdf2.css +1 -0
- package/dist/web/assets/ConfigTemplates-ZrK_s7ma.js +1 -0
- package/dist/web/assets/Home-B8YfhZ3c.js +1 -0
- package/dist/web/assets/Home-Di2qsylF.css +1 -0
- package/dist/web/assets/PluginManager-BD7QUZbU.js +1 -0
- package/dist/web/assets/PluginManager-ROyoZ-6m.css +1 -0
- package/dist/web/assets/ProjectList-C1fQb9OW.css +1 -0
- package/dist/web/assets/ProjectList-DRb1DuHV.js +1 -0
- package/dist/web/assets/SessionList-BGJWyneI.css +1 -0
- package/dist/web/assets/SessionList-lZ0LKzfT.js +1 -0
- package/dist/web/assets/SkillManager-C1xG5B4Q.js +1 -0
- package/dist/web/assets/SkillManager-D7pd-d_P.css +1 -0
- package/dist/web/assets/Terminal-DGNJeVtc.css +1 -0
- package/dist/web/assets/Terminal-DksBo_lM.js +1 -0
- package/dist/web/assets/WorkspaceManager-Burx7XOo.js +1 -0
- package/dist/web/assets/WorkspaceManager-CrwgQgmP.css +1 -0
- package/dist/web/assets/icons-kcfLIMBB.js +1 -0
- package/dist/web/assets/index-Ufv5rCa5.css +1 -0
- package/dist/web/assets/index-lAkrRC3h.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-CSrLusZZ.js +1 -0
- package/dist/web/assets/{vendors-D2HHw_aW.js → vendors-CO3Upi1d.js} +2 -2
- package/dist/web/assets/vue-vendor-DqyWIXEb.js +45 -0
- package/dist/web/assets/xterm-6GBZ9nXN.css +32 -0
- package/dist/web/assets/xterm-BJzAjXCH.js +13 -0
- package/dist/web/index.html +8 -6
- package/package.json +4 -2
- package/src/commands/channels.js +48 -1
- package/src/commands/cli-type.js +4 -2
- package/src/commands/daemon.js +81 -12
- package/src/commands/doctor.js +10 -9
- package/src/commands/list.js +1 -1
- package/src/commands/logs.js +6 -4
- package/src/commands/port-config.js +24 -4
- package/src/commands/proxy-control.js +12 -6
- package/src/commands/search.js +1 -1
- package/src/commands/security.js +3 -2
- package/src/commands/stats.js +226 -52
- package/src/commands/switch.js +1 -1
- package/src/commands/toggle-proxy.js +31 -6
- package/src/commands/update.js +97 -0
- package/src/commands/workspace.js +1 -1
- package/src/config/default.js +39 -2
- package/src/config/loader.js +74 -8
- package/src/config/paths.js +105 -33
- package/src/index.js +64 -3
- package/src/plugins/constants.js +3 -2
- package/src/plugins/plugin-api.js +1 -1
- package/src/reset-config.js +4 -2
- package/src/server/api/agents.js +57 -14
- package/src/server/api/channels.js +112 -33
- package/src/server/api/codex-channels.js +111 -18
- package/src/server/api/codex-proxy.js +14 -8
- package/src/server/api/commands.js +71 -18
- package/src/server/api/config-export.js +0 -6
- package/src/server/api/config-registry.js +11 -3
- package/src/server/api/config.js +376 -5
- package/src/server/api/convert.js +133 -0
- package/src/server/api/dashboard.js +22 -6
- package/src/server/api/gemini-channels.js +107 -18
- package/src/server/api/gemini-proxy.js +14 -8
- package/src/server/api/gemini-sessions.js +1 -1
- package/src/server/api/health-check.js +4 -3
- package/src/server/api/mcp.js +3 -3
- 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 +198 -0
- package/src/server/api/opencode-sessions.js +403 -0
- package/src/server/api/opencode-statistics.js +57 -0
- package/src/server/api/plugins.js +66 -19
- package/src/server/api/prompts.js +2 -2
- package/src/server/api/proxy.js +7 -4
- package/src/server/api/sessions.js +3 -0
- package/src/server/api/skills.js +69 -18
- package/src/server/api/workspaces.js +78 -6
- package/src/server/codex-proxy-server.js +30 -18
- package/src/server/dev-server.js +1 -1
- package/src/server/gemini-proxy-server.js +15 -3
- package/src/server/index.js +165 -58
- package/src/server/opencode-proxy-server.js +4375 -0
- package/src/server/proxy-server.js +27 -18
- package/src/server/services/agents-service.js +61 -24
- package/src/server/services/channel-scheduler.js +9 -5
- package/src/server/services/channels.js +64 -37
- package/src/server/services/codex-channels.js +56 -43
- package/src/server/services/codex-settings-manager.js +271 -49
- package/src/server/services/codex-statistics-service.js +2 -2
- package/src/server/services/commands-service.js +84 -25
- package/src/server/services/config-export-service.js +7 -45
- package/src/server/services/config-registry-service.js +63 -17
- package/src/server/services/config-sync-manager.js +160 -7
- package/src/server/services/config-templates-service.js +204 -51
- package/src/server/services/env-checker.js +26 -12
- package/src/server/services/env-manager.js +126 -18
- package/src/server/services/favorites.js +5 -3
- package/src/server/services/gemini-channels.js +33 -44
- package/src/server/services/gemini-statistics-service.js +2 -2
- package/src/server/services/mcp-service.js +350 -9
- package/src/server/services/model-detector.js +707 -221
- package/src/server/services/network-access.js +80 -0
- package/src/server/services/opencode-channels.js +206 -0
- package/src/server/services/opencode-gateway-converter.js +639 -0
- package/src/server/services/opencode-sessions.js +663 -0
- package/src/server/services/opencode-settings-manager.js +342 -0
- package/src/server/services/opencode-statistics-service.js +255 -0
- package/src/server/services/plugins-service.js +479 -22
- package/src/server/services/prompts-service.js +53 -11
- package/src/server/services/proxy-runtime.js +1 -1
- package/src/server/services/repo-scanner-base.js +1 -1
- package/src/server/services/security-config.js +1 -1
- package/src/server/services/session-cache.js +1 -1
- package/src/server/services/skill-service.js +300 -46
- package/src/server/services/speed-test.js +464 -186
- package/src/server/services/statistics-service.js +2 -2
- package/src/server/services/terminal-commands.js +10 -3
- package/src/server/services/terminal-config.js +1 -1
- package/src/server/services/ui-config.js +1 -1
- package/src/server/services/workspace-service.js +57 -100
- package/src/server/websocket-server.js +132 -3
- package/src/ui/menu.js +49 -40
- package/src/utils/port-helper.js +22 -8
- package/src/utils/session.js +5 -4
- package/dist/web/assets/icons-CO_2OFES.js +0 -1
- package/dist/web/assets/index-DI8QOi-E.js +0 -14
- package/dist/web/assets/index-uLHGdeZh.css +0 -41
- package/dist/web/assets/naive-ui-B1re3c-e.js +0 -1
- package/dist/web/assets/vue-vendor-6JaYHOiI.js +0 -44
- package/src/server/api/oauth.js +0 -294
- package/src/server/api/permissions.js +0 -385
- package/src/server/config/oauth-providers.js +0 -68
- package/src/server/services/oauth-callback-server.js +0 -284
- package/src/server/services/oauth-service.js +0 -378
- package/src/server/services/oauth-token-storage.js +0 -135
- package/src/server/services/permission-templates-service.js +0 -308
|
@@ -8,7 +8,24 @@ const express = require('express');
|
|
|
8
8
|
const { PluginsService } = require('../services/plugins-service');
|
|
9
9
|
|
|
10
10
|
const router = express.Router();
|
|
11
|
-
const
|
|
11
|
+
const SUPPORTED_PLATFORMS = ['claude', 'opencode'];
|
|
12
|
+
const pluginServices = new Map();
|
|
13
|
+
|
|
14
|
+
function resolvePlatform(rawPlatform) {
|
|
15
|
+
return SUPPORTED_PLATFORMS.includes(rawPlatform) ? rawPlatform : 'claude';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getPlatform(req) {
|
|
19
|
+
return resolvePlatform(req.query?.platform || req.body?.platform);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getPluginsService(req) {
|
|
23
|
+
const platform = getPlatform(req);
|
|
24
|
+
if (!pluginServices.has(platform)) {
|
|
25
|
+
pluginServices.set(platform, new PluginsService(platform));
|
|
26
|
+
}
|
|
27
|
+
return { platform, service: pluginServices.get(platform) };
|
|
28
|
+
}
|
|
12
29
|
|
|
13
30
|
/**
|
|
14
31
|
* 获取插件列表
|
|
@@ -16,10 +33,12 @@ const pluginsService = new PluginsService();
|
|
|
16
33
|
*/
|
|
17
34
|
router.get('/', (req, res) => {
|
|
18
35
|
try {
|
|
19
|
-
const
|
|
36
|
+
const { platform, service } = getPluginsService(req);
|
|
37
|
+
const result = service.listPlugins();
|
|
20
38
|
|
|
21
39
|
res.json({
|
|
22
40
|
success: true,
|
|
41
|
+
platform,
|
|
23
42
|
...result
|
|
24
43
|
});
|
|
25
44
|
} catch (err) {
|
|
@@ -37,10 +56,12 @@ router.get('/', (req, res) => {
|
|
|
37
56
|
*/
|
|
38
57
|
router.get('/market', async (req, res) => {
|
|
39
58
|
try {
|
|
40
|
-
const
|
|
59
|
+
const { platform, service } = getPluginsService(req);
|
|
60
|
+
const plugins = await service.getMarketPlugins();
|
|
41
61
|
|
|
42
62
|
res.json({
|
|
43
63
|
success: true,
|
|
64
|
+
platform,
|
|
44
65
|
plugins
|
|
45
66
|
});
|
|
46
67
|
} catch (err) {
|
|
@@ -55,26 +76,29 @@ router.get('/market', async (req, res) => {
|
|
|
55
76
|
/**
|
|
56
77
|
* 安装插件
|
|
57
78
|
* POST /api/plugins/install
|
|
58
|
-
* Body: { directory, repo: { owner, name, branch } }
|
|
79
|
+
* Body: { directory, repo: { owner, name, branch } } or { source }
|
|
59
80
|
*/
|
|
60
81
|
router.post('/install', async (req, res) => {
|
|
61
82
|
try {
|
|
62
|
-
const {
|
|
83
|
+
const { platform, service } = getPluginsService(req);
|
|
84
|
+
const { directory, repo, gitUrl, source } = req.body;
|
|
63
85
|
|
|
64
86
|
// Support both new format (directory + repo) and legacy format (gitUrl)
|
|
65
87
|
let installUrl;
|
|
66
|
-
if (
|
|
88
|
+
if (source) {
|
|
89
|
+
installUrl = source;
|
|
90
|
+
} else if (directory && repo) {
|
|
67
91
|
installUrl = `https://github.com/${repo.owner}/${repo.name}/tree/${repo.branch || 'main'}/${directory}`;
|
|
68
92
|
} else if (gitUrl) {
|
|
69
93
|
installUrl = gitUrl;
|
|
70
94
|
} else {
|
|
71
95
|
return res.status(400).json({
|
|
72
96
|
success: false,
|
|
73
|
-
message: 'Either (directory + repo) or gitUrl is required'
|
|
97
|
+
message: 'Either source, (directory + repo), or gitUrl is required'
|
|
74
98
|
});
|
|
75
99
|
}
|
|
76
100
|
|
|
77
|
-
const result = await
|
|
101
|
+
const result = await service.installPlugin(installUrl);
|
|
78
102
|
|
|
79
103
|
if (!result.success) {
|
|
80
104
|
return res.status(400).json({
|
|
@@ -85,6 +109,7 @@ router.post('/install', async (req, res) => {
|
|
|
85
109
|
|
|
86
110
|
res.json({
|
|
87
111
|
success: true,
|
|
112
|
+
platform,
|
|
88
113
|
plugin: result.plugin,
|
|
89
114
|
message: `Plugin "${result.plugin.name}" installed successfully`
|
|
90
115
|
});
|
|
@@ -105,9 +130,11 @@ router.post('/install', async (req, res) => {
|
|
|
105
130
|
*/
|
|
106
131
|
router.get('/repos', (req, res) => {
|
|
107
132
|
try {
|
|
108
|
-
const
|
|
133
|
+
const { platform, service } = getPluginsService(req);
|
|
134
|
+
const repos = service.getRepos();
|
|
109
135
|
res.json({
|
|
110
136
|
success: true,
|
|
137
|
+
platform,
|
|
111
138
|
repos
|
|
112
139
|
});
|
|
113
140
|
} catch (err) {
|
|
@@ -126,6 +153,7 @@ router.get('/repos', (req, res) => {
|
|
|
126
153
|
*/
|
|
127
154
|
router.post('/repos', (req, res) => {
|
|
128
155
|
try {
|
|
156
|
+
const { platform, service } = getPluginsService(req);
|
|
129
157
|
const repo = req.body;
|
|
130
158
|
|
|
131
159
|
if (!repo || !repo.url) {
|
|
@@ -135,10 +163,11 @@ router.post('/repos', (req, res) => {
|
|
|
135
163
|
});
|
|
136
164
|
}
|
|
137
165
|
|
|
138
|
-
const repos =
|
|
166
|
+
const repos = service.addRepo(repo);
|
|
139
167
|
|
|
140
168
|
res.json({
|
|
141
169
|
success: true,
|
|
170
|
+
platform,
|
|
142
171
|
repos,
|
|
143
172
|
message: 'Repository added successfully'
|
|
144
173
|
});
|
|
@@ -157,12 +186,14 @@ router.post('/repos', (req, res) => {
|
|
|
157
186
|
*/
|
|
158
187
|
router.delete('/repos/:owner/:name', (req, res) => {
|
|
159
188
|
try {
|
|
189
|
+
const { platform, service } = getPluginsService(req);
|
|
160
190
|
const { owner, name } = req.params;
|
|
161
191
|
|
|
162
|
-
const repos =
|
|
192
|
+
const repos = service.removeRepo(owner, name);
|
|
163
193
|
|
|
164
194
|
res.json({
|
|
165
195
|
success: true,
|
|
196
|
+
platform,
|
|
166
197
|
repos,
|
|
167
198
|
message: 'Repository removed successfully'
|
|
168
199
|
});
|
|
@@ -182,6 +213,7 @@ router.delete('/repos/:owner/:name', (req, res) => {
|
|
|
182
213
|
*/
|
|
183
214
|
router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
184
215
|
try {
|
|
216
|
+
const { platform, service } = getPluginsService(req);
|
|
185
217
|
const { owner, name } = req.params;
|
|
186
218
|
const { enabled } = req.body;
|
|
187
219
|
|
|
@@ -192,10 +224,11 @@ router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
|
192
224
|
});
|
|
193
225
|
}
|
|
194
226
|
|
|
195
|
-
const repos =
|
|
227
|
+
const repos = service.toggleRepo(owner, name, enabled);
|
|
196
228
|
|
|
197
229
|
res.json({
|
|
198
230
|
success: true,
|
|
231
|
+
platform,
|
|
199
232
|
repos,
|
|
200
233
|
message: `Repository ${enabled ? 'enabled' : 'disabled'} successfully`
|
|
201
234
|
});
|
|
@@ -214,10 +247,12 @@ router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
|
214
247
|
*/
|
|
215
248
|
router.post('/repos/sync', async (req, res) => {
|
|
216
249
|
try {
|
|
217
|
-
const
|
|
250
|
+
const { platform, service } = getPluginsService(req);
|
|
251
|
+
const result = await service.syncRepos();
|
|
218
252
|
|
|
219
253
|
res.json({
|
|
220
254
|
success: true,
|
|
255
|
+
platform,
|
|
221
256
|
...result,
|
|
222
257
|
message: 'Repositories synced successfully'
|
|
223
258
|
});
|
|
@@ -236,10 +271,12 @@ router.post('/repos/sync', async (req, res) => {
|
|
|
236
271
|
*/
|
|
237
272
|
router.post('/sync', async (req, res) => {
|
|
238
273
|
try {
|
|
239
|
-
const
|
|
274
|
+
const { platform, service } = getPluginsService(req);
|
|
275
|
+
const result = await service.syncPlugins();
|
|
240
276
|
|
|
241
277
|
res.json({
|
|
242
278
|
success: true,
|
|
279
|
+
platform,
|
|
243
280
|
...result,
|
|
244
281
|
message: 'Plugins synced successfully'
|
|
245
282
|
});
|
|
@@ -259,6 +296,7 @@ router.post('/sync', async (req, res) => {
|
|
|
259
296
|
*/
|
|
260
297
|
router.get('/:name/readme', async (req, res) => {
|
|
261
298
|
try {
|
|
299
|
+
const { platform, service } = getPluginsService(req);
|
|
262
300
|
const { name } = req.params;
|
|
263
301
|
const { repoOwner, repoName, repoBranch, directory, source, repoUrl } = req.query;
|
|
264
302
|
|
|
@@ -272,10 +310,11 @@ router.get('/:name/readme', async (req, res) => {
|
|
|
272
310
|
repoUrl
|
|
273
311
|
};
|
|
274
312
|
|
|
275
|
-
const readme = await
|
|
313
|
+
const readme = await service.getPluginReadme(pluginInfo);
|
|
276
314
|
|
|
277
315
|
res.json({
|
|
278
316
|
success: true,
|
|
317
|
+
platform,
|
|
279
318
|
readme
|
|
280
319
|
});
|
|
281
320
|
} catch (err) {
|
|
@@ -294,9 +333,10 @@ router.get('/:name/readme', async (req, res) => {
|
|
|
294
333
|
*/
|
|
295
334
|
router.get('/:name', (req, res) => {
|
|
296
335
|
try {
|
|
336
|
+
const { platform, service } = getPluginsService(req);
|
|
297
337
|
const { name } = req.params;
|
|
298
338
|
|
|
299
|
-
const plugin =
|
|
339
|
+
const plugin = service.getPlugin(name);
|
|
300
340
|
|
|
301
341
|
if (!plugin) {
|
|
302
342
|
return res.status(404).json({
|
|
@@ -307,6 +347,7 @@ router.get('/:name', (req, res) => {
|
|
|
307
347
|
|
|
308
348
|
res.json({
|
|
309
349
|
success: true,
|
|
350
|
+
platform,
|
|
310
351
|
plugin
|
|
311
352
|
});
|
|
312
353
|
} catch (err) {
|
|
@@ -324,9 +365,10 @@ router.get('/:name', (req, res) => {
|
|
|
324
365
|
*/
|
|
325
366
|
router.delete('/:name', (req, res) => {
|
|
326
367
|
try {
|
|
368
|
+
const { platform, service } = getPluginsService(req);
|
|
327
369
|
const { name } = req.params;
|
|
328
370
|
|
|
329
|
-
const result =
|
|
371
|
+
const result = service.uninstallPlugin(name);
|
|
330
372
|
|
|
331
373
|
if (!result.success) {
|
|
332
374
|
return res.status(400).json({
|
|
@@ -337,6 +379,7 @@ router.delete('/:name', (req, res) => {
|
|
|
337
379
|
|
|
338
380
|
res.json({
|
|
339
381
|
success: true,
|
|
382
|
+
platform,
|
|
340
383
|
message: result.message
|
|
341
384
|
});
|
|
342
385
|
} catch (err) {
|
|
@@ -355,6 +398,7 @@ router.delete('/:name', (req, res) => {
|
|
|
355
398
|
*/
|
|
356
399
|
router.put('/:name/toggle', (req, res) => {
|
|
357
400
|
try {
|
|
401
|
+
const { platform, service } = getPluginsService(req);
|
|
358
402
|
const { name } = req.params;
|
|
359
403
|
const { enabled } = req.body;
|
|
360
404
|
|
|
@@ -365,10 +409,11 @@ router.put('/:name/toggle', (req, res) => {
|
|
|
365
409
|
});
|
|
366
410
|
}
|
|
367
411
|
|
|
368
|
-
const plugin =
|
|
412
|
+
const plugin = service.togglePlugin(name, enabled);
|
|
369
413
|
|
|
370
414
|
res.json({
|
|
371
415
|
success: true,
|
|
416
|
+
platform,
|
|
372
417
|
plugin,
|
|
373
418
|
message: `Plugin "${name}" ${enabled ? 'enabled' : 'disabled'} successfully`
|
|
374
419
|
});
|
|
@@ -388,6 +433,7 @@ router.put('/:name/toggle', (req, res) => {
|
|
|
388
433
|
*/
|
|
389
434
|
router.put('/:name/config', (req, res) => {
|
|
390
435
|
try {
|
|
436
|
+
const { platform, service } = getPluginsService(req);
|
|
391
437
|
const { name } = req.params;
|
|
392
438
|
const { config } = req.body;
|
|
393
439
|
|
|
@@ -398,10 +444,11 @@ router.put('/:name/config', (req, res) => {
|
|
|
398
444
|
});
|
|
399
445
|
}
|
|
400
446
|
|
|
401
|
-
const result =
|
|
447
|
+
const result = service.updatePluginConfig(name, config);
|
|
402
448
|
|
|
403
449
|
res.json({
|
|
404
450
|
success: true,
|
|
451
|
+
platform,
|
|
405
452
|
message: result.message
|
|
406
453
|
});
|
|
407
454
|
} catch (err) {
|
|
@@ -203,7 +203,7 @@ router.get('/platform/:platform', (req, res) => {
|
|
|
203
203
|
try {
|
|
204
204
|
const { platform } = req.params;
|
|
205
205
|
|
|
206
|
-
if (!['claude', 'codex', 'gemini'].includes(platform)) {
|
|
206
|
+
if (!['claude', 'codex', 'gemini', 'opencode'].includes(platform)) {
|
|
207
207
|
return res.status(400).json({
|
|
208
208
|
success: false,
|
|
209
209
|
error: `无效的平台: ${platform}`
|
|
@@ -234,7 +234,7 @@ router.post('/import/:platform', (req, res) => {
|
|
|
234
234
|
const { platform } = req.params;
|
|
235
235
|
const { name } = req.body;
|
|
236
236
|
|
|
237
|
-
if (!['claude', 'codex', 'gemini'].includes(platform)) {
|
|
237
|
+
if (!['claude', 'codex', 'gemini', 'opencode'].includes(platform)) {
|
|
238
238
|
return res.status(400).json({
|
|
239
239
|
success: false,
|
|
240
240
|
error: `无效的平台: ${platform}`
|
package/src/server/api/proxy.js
CHANGED
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
} = require('../services/settings-manager');
|
|
13
13
|
const { getAllChannels } = require('../services/channels');
|
|
14
14
|
const { clearAllLogs } = require('../websocket-server');
|
|
15
|
+
const { PATHS, ensureStorageDirMigrated } = require('../../config/paths');
|
|
15
16
|
const fs = require('fs');
|
|
16
17
|
const path = require('path');
|
|
17
18
|
const os = require('os');
|
|
@@ -28,18 +29,20 @@ function sanitizeChannelForResponse(channel) {
|
|
|
28
29
|
|
|
29
30
|
// 保存激活渠道ID
|
|
30
31
|
function saveActiveChannelId(channelId) {
|
|
31
|
-
|
|
32
|
+
ensureStorageDirMigrated();
|
|
33
|
+
const filePath = PATHS.activeChannel.claude;
|
|
34
|
+
const dir = path.dirname(filePath);
|
|
32
35
|
if (!fs.existsSync(dir)) {
|
|
33
36
|
fs.mkdirSync(dir, { recursive: true });
|
|
34
37
|
}
|
|
35
|
-
const filePath = path.join(dir, 'active-channel.json');
|
|
36
38
|
fs.writeFileSync(filePath, JSON.stringify({ activeChannelId: channelId }, null, 2), 'utf8');
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
// 加载上次激活的渠道ID
|
|
41
43
|
function loadActiveChannelId() {
|
|
42
|
-
|
|
44
|
+
ensureStorageDirMigrated();
|
|
45
|
+
const filePath = PATHS.activeChannel.claude;
|
|
43
46
|
try {
|
|
44
47
|
if (fs.existsSync(filePath)) {
|
|
45
48
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
@@ -246,7 +249,7 @@ router.post('/stop', async (req, res) => {
|
|
|
246
249
|
}
|
|
247
250
|
}
|
|
248
251
|
|
|
249
|
-
const activeChannelPath =
|
|
252
|
+
const activeChannelPath = PATHS.activeChannel.claude;
|
|
250
253
|
if (fs.existsSync(activeChannelPath)) {
|
|
251
254
|
fs.unlinkSync(activeChannelPath);
|
|
252
255
|
console.log('✅ Removed active-channel.json');
|
|
@@ -189,6 +189,9 @@ module.exports = (config) => {
|
|
|
189
189
|
try {
|
|
190
190
|
const { projectName } = req.params;
|
|
191
191
|
const { order } = req.body;
|
|
192
|
+
if (!Array.isArray(order)) {
|
|
193
|
+
return res.status(400).json({ error: 'order must be an array' });
|
|
194
|
+
}
|
|
192
195
|
saveSessionOrder(projectName, order);
|
|
193
196
|
res.json({ success: true });
|
|
194
197
|
} catch (error) {
|
package/src/server/api/skills.js
CHANGED
|
@@ -6,7 +6,24 @@ const express = require('express');
|
|
|
6
6
|
const { SkillService } = require('../services/skill-service');
|
|
7
7
|
|
|
8
8
|
const router = express.Router();
|
|
9
|
-
const
|
|
9
|
+
const SUPPORTED_PLATFORMS = ['claude', 'codex', 'opencode'];
|
|
10
|
+
const skillServices = new Map();
|
|
11
|
+
|
|
12
|
+
function resolvePlatform(rawPlatform) {
|
|
13
|
+
return SUPPORTED_PLATFORMS.includes(rawPlatform) ? rawPlatform : 'claude';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getPlatform(req) {
|
|
17
|
+
return resolvePlatform(req.query?.platform || req.body?.platform);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getSkillService(req) {
|
|
21
|
+
const platform = getPlatform(req);
|
|
22
|
+
if (!skillServices.has(platform)) {
|
|
23
|
+
skillServices.set(platform, new SkillService(platform));
|
|
24
|
+
}
|
|
25
|
+
return { platform, service: skillServices.get(platform) };
|
|
26
|
+
}
|
|
10
27
|
|
|
11
28
|
/**
|
|
12
29
|
* 获取技能列表
|
|
@@ -15,10 +32,12 @@ const skillService = new SkillService();
|
|
|
15
32
|
*/
|
|
16
33
|
router.get('/', async (req, res) => {
|
|
17
34
|
try {
|
|
35
|
+
const { platform, service } = getSkillService(req);
|
|
18
36
|
const forceRefresh = req.query.refresh === '1';
|
|
19
|
-
const skills = await
|
|
37
|
+
const skills = await service.listSkills(forceRefresh);
|
|
20
38
|
res.json({
|
|
21
39
|
success: true,
|
|
40
|
+
platform,
|
|
22
41
|
skills,
|
|
23
42
|
total: skills.length,
|
|
24
43
|
installed: skills.filter(s => s.installed).length
|
|
@@ -38,6 +57,7 @@ router.get('/', async (req, res) => {
|
|
|
38
57
|
*/
|
|
39
58
|
router.get('/detail/*', async (req, res) => {
|
|
40
59
|
try {
|
|
60
|
+
const { platform, service } = getSkillService(req);
|
|
41
61
|
const directory = req.params[0]; // 获取通配符匹配的路径
|
|
42
62
|
if (!directory) {
|
|
43
63
|
return res.status(400).json({
|
|
@@ -46,9 +66,10 @@ router.get('/detail/*', async (req, res) => {
|
|
|
46
66
|
});
|
|
47
67
|
}
|
|
48
68
|
|
|
49
|
-
const result = await
|
|
69
|
+
const result = await service.getSkillDetail(directory);
|
|
50
70
|
res.json({
|
|
51
71
|
success: true,
|
|
72
|
+
platform,
|
|
52
73
|
...result
|
|
53
74
|
});
|
|
54
75
|
} catch (err) {
|
|
@@ -66,9 +87,11 @@ router.get('/detail/*', async (req, res) => {
|
|
|
66
87
|
*/
|
|
67
88
|
router.get('/installed', (req, res) => {
|
|
68
89
|
try {
|
|
69
|
-
const
|
|
90
|
+
const { platform, service } = getSkillService(req);
|
|
91
|
+
const skills = service.getInstalledSkills();
|
|
70
92
|
res.json({
|
|
71
93
|
success: true,
|
|
94
|
+
platform,
|
|
72
95
|
skills
|
|
73
96
|
});
|
|
74
97
|
} catch (err) {
|
|
@@ -89,6 +112,7 @@ router.get('/installed', (req, res) => {
|
|
|
89
112
|
*/
|
|
90
113
|
router.post('/install', async (req, res) => {
|
|
91
114
|
try {
|
|
115
|
+
const { platform, service } = getSkillService(req);
|
|
92
116
|
const { directory, fullDirectory, repo } = req.body;
|
|
93
117
|
|
|
94
118
|
if (!directory) {
|
|
@@ -105,7 +129,7 @@ router.post('/install', async (req, res) => {
|
|
|
105
129
|
});
|
|
106
130
|
}
|
|
107
131
|
|
|
108
|
-
const result = await
|
|
132
|
+
const result = await service.installSkill(
|
|
109
133
|
directory,
|
|
110
134
|
{
|
|
111
135
|
owner: repo.owner,
|
|
@@ -117,6 +141,7 @@ router.post('/install', async (req, res) => {
|
|
|
117
141
|
|
|
118
142
|
res.json({
|
|
119
143
|
success: true,
|
|
144
|
+
platform,
|
|
120
145
|
...result
|
|
121
146
|
});
|
|
122
147
|
} catch (err) {
|
|
@@ -135,6 +160,7 @@ router.post('/install', async (req, res) => {
|
|
|
135
160
|
*/
|
|
136
161
|
router.post('/create', (req, res) => {
|
|
137
162
|
try {
|
|
163
|
+
const { platform, service } = getSkillService(req);
|
|
138
164
|
const { name, directory, description, content } = req.body;
|
|
139
165
|
|
|
140
166
|
if (!directory) {
|
|
@@ -159,7 +185,7 @@ router.post('/create', (req, res) => {
|
|
|
159
185
|
});
|
|
160
186
|
}
|
|
161
187
|
|
|
162
|
-
const result =
|
|
188
|
+
const result = service.createCustomSkill({
|
|
163
189
|
name: name || directory,
|
|
164
190
|
directory,
|
|
165
191
|
description: description || '',
|
|
@@ -168,6 +194,7 @@ router.post('/create', (req, res) => {
|
|
|
168
194
|
|
|
169
195
|
res.json({
|
|
170
196
|
success: true,
|
|
197
|
+
platform,
|
|
171
198
|
...result
|
|
172
199
|
});
|
|
173
200
|
} catch (err) {
|
|
@@ -186,6 +213,7 @@ router.post('/create', (req, res) => {
|
|
|
186
213
|
*/
|
|
187
214
|
router.post('/uninstall', (req, res) => {
|
|
188
215
|
try {
|
|
216
|
+
const { platform, service } = getSkillService(req);
|
|
189
217
|
const { directory } = req.body;
|
|
190
218
|
|
|
191
219
|
if (!directory) {
|
|
@@ -195,10 +223,11 @@ router.post('/uninstall', (req, res) => {
|
|
|
195
223
|
});
|
|
196
224
|
}
|
|
197
225
|
|
|
198
|
-
const result =
|
|
226
|
+
const result = service.uninstallSkill(directory);
|
|
199
227
|
|
|
200
228
|
res.json({
|
|
201
229
|
success: true,
|
|
230
|
+
platform,
|
|
202
231
|
...result
|
|
203
232
|
});
|
|
204
233
|
} catch (err) {
|
|
@@ -216,9 +245,11 @@ router.post('/uninstall', (req, res) => {
|
|
|
216
245
|
*/
|
|
217
246
|
router.get('/repos', (req, res) => {
|
|
218
247
|
try {
|
|
219
|
-
const
|
|
248
|
+
const { platform, service } = getSkillService(req);
|
|
249
|
+
const repos = service.loadRepos();
|
|
220
250
|
res.json({
|
|
221
251
|
success: true,
|
|
252
|
+
platform,
|
|
222
253
|
repos
|
|
223
254
|
});
|
|
224
255
|
} catch (err) {
|
|
@@ -238,6 +269,7 @@ router.get('/repos', (req, res) => {
|
|
|
238
269
|
*/
|
|
239
270
|
router.post('/repos', (req, res) => {
|
|
240
271
|
try {
|
|
272
|
+
const { platform, service } = getSkillService(req);
|
|
241
273
|
const { owner, name, branch = 'main', directory = '', enabled = true } = req.body;
|
|
242
274
|
|
|
243
275
|
if (!owner || !name) {
|
|
@@ -247,10 +279,11 @@ router.post('/repos', (req, res) => {
|
|
|
247
279
|
});
|
|
248
280
|
}
|
|
249
281
|
|
|
250
|
-
const repos =
|
|
282
|
+
const repos = service.addRepo({ owner, name, branch, directory, enabled });
|
|
251
283
|
|
|
252
284
|
res.json({
|
|
253
285
|
success: true,
|
|
286
|
+
platform,
|
|
254
287
|
repos
|
|
255
288
|
});
|
|
256
289
|
} catch (err) {
|
|
@@ -269,12 +302,14 @@ router.post('/repos', (req, res) => {
|
|
|
269
302
|
*/
|
|
270
303
|
router.delete('/repos/:owner/:name', (req, res) => {
|
|
271
304
|
try {
|
|
305
|
+
const { platform, service } = getSkillService(req);
|
|
272
306
|
const { owner, name } = req.params;
|
|
273
307
|
const { directory = '' } = req.query;
|
|
274
|
-
const repos =
|
|
308
|
+
const repos = service.removeRepo(owner, name, directory);
|
|
275
309
|
|
|
276
310
|
res.json({
|
|
277
311
|
success: true,
|
|
312
|
+
platform,
|
|
278
313
|
repos
|
|
279
314
|
});
|
|
280
315
|
} catch (err) {
|
|
@@ -294,13 +329,15 @@ router.delete('/repos/:owner/:name', (req, res) => {
|
|
|
294
329
|
*/
|
|
295
330
|
router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
296
331
|
try {
|
|
332
|
+
const { platform, service } = getSkillService(req);
|
|
297
333
|
const { owner, name } = req.params;
|
|
298
334
|
const { enabled, directory = '' } = req.body;
|
|
299
335
|
|
|
300
|
-
const repos =
|
|
336
|
+
const repos = service.toggleRepo(owner, name, directory, enabled);
|
|
301
337
|
|
|
302
338
|
res.json({
|
|
303
339
|
success: true,
|
|
340
|
+
platform,
|
|
304
341
|
repos
|
|
305
342
|
});
|
|
306
343
|
} catch (err) {
|
|
@@ -321,6 +358,7 @@ router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
|
321
358
|
*/
|
|
322
359
|
router.post('/create-with-files', (req, res) => {
|
|
323
360
|
try {
|
|
361
|
+
const { platform, service } = getSkillService(req);
|
|
324
362
|
const { directory, files } = req.body;
|
|
325
363
|
|
|
326
364
|
if (!directory) {
|
|
@@ -345,10 +383,11 @@ router.post('/create-with-files', (req, res) => {
|
|
|
345
383
|
});
|
|
346
384
|
}
|
|
347
385
|
|
|
348
|
-
const result =
|
|
386
|
+
const result = service.createSkillWithFiles({ directory, files });
|
|
349
387
|
|
|
350
388
|
res.json({
|
|
351
389
|
success: true,
|
|
390
|
+
platform,
|
|
352
391
|
...result
|
|
353
392
|
});
|
|
354
393
|
} catch (err) {
|
|
@@ -366,11 +405,13 @@ router.post('/create-with-files', (req, res) => {
|
|
|
366
405
|
*/
|
|
367
406
|
router.get('/:directory/files', (req, res) => {
|
|
368
407
|
try {
|
|
408
|
+
const { platform, service } = getSkillService(req);
|
|
369
409
|
const { directory } = req.params;
|
|
370
|
-
const files =
|
|
410
|
+
const files = service.getSkillFiles(directory);
|
|
371
411
|
|
|
372
412
|
res.json({
|
|
373
413
|
success: true,
|
|
414
|
+
platform,
|
|
374
415
|
directory,
|
|
375
416
|
files
|
|
376
417
|
});
|
|
@@ -390,6 +431,7 @@ router.get('/:directory/files', (req, res) => {
|
|
|
390
431
|
*/
|
|
391
432
|
router.get('/:directory/file/*', (req, res) => {
|
|
392
433
|
try {
|
|
434
|
+
const { platform, service } = getSkillService(req);
|
|
393
435
|
const { directory } = req.params;
|
|
394
436
|
const filePath = req.params[0];
|
|
395
437
|
|
|
@@ -400,10 +442,11 @@ router.get('/:directory/file/*', (req, res) => {
|
|
|
400
442
|
});
|
|
401
443
|
}
|
|
402
444
|
|
|
403
|
-
const result =
|
|
445
|
+
const result = service.getSkillFileContent(directory, filePath);
|
|
404
446
|
|
|
405
447
|
res.json({
|
|
406
448
|
success: true,
|
|
449
|
+
platform,
|
|
407
450
|
...result
|
|
408
451
|
});
|
|
409
452
|
} catch (err) {
|
|
@@ -422,6 +465,7 @@ router.get('/:directory/file/*', (req, res) => {
|
|
|
422
465
|
*/
|
|
423
466
|
router.post('/:directory/files', (req, res) => {
|
|
424
467
|
try {
|
|
468
|
+
const { platform, service } = getSkillService(req);
|
|
425
469
|
const { directory } = req.params;
|
|
426
470
|
const { files } = req.body;
|
|
427
471
|
|
|
@@ -432,10 +476,11 @@ router.post('/:directory/files', (req, res) => {
|
|
|
432
476
|
});
|
|
433
477
|
}
|
|
434
478
|
|
|
435
|
-
const result =
|
|
479
|
+
const result = service.addSkillFiles(directory, files);
|
|
436
480
|
|
|
437
481
|
res.json({
|
|
438
482
|
success: true,
|
|
483
|
+
platform,
|
|
439
484
|
...result
|
|
440
485
|
});
|
|
441
486
|
} catch (err) {
|
|
@@ -453,6 +498,7 @@ router.post('/:directory/files', (req, res) => {
|
|
|
453
498
|
*/
|
|
454
499
|
router.delete('/:directory/file/*', (req, res) => {
|
|
455
500
|
try {
|
|
501
|
+
const { platform, service } = getSkillService(req);
|
|
456
502
|
const { directory } = req.params;
|
|
457
503
|
const filePath = req.params[0];
|
|
458
504
|
|
|
@@ -463,10 +509,11 @@ router.delete('/:directory/file/*', (req, res) => {
|
|
|
463
509
|
});
|
|
464
510
|
}
|
|
465
511
|
|
|
466
|
-
const result =
|
|
512
|
+
const result = service.deleteSkillFile(directory, filePath);
|
|
467
513
|
|
|
468
514
|
res.json({
|
|
469
515
|
success: true,
|
|
516
|
+
platform,
|
|
470
517
|
...result
|
|
471
518
|
});
|
|
472
519
|
} catch (err) {
|
|
@@ -485,6 +532,7 @@ router.delete('/:directory/file/*', (req, res) => {
|
|
|
485
532
|
*/
|
|
486
533
|
router.put('/:directory/file/*', (req, res) => {
|
|
487
534
|
try {
|
|
535
|
+
const { platform, service } = getSkillService(req);
|
|
488
536
|
const { directory } = req.params;
|
|
489
537
|
const filePath = req.params[0];
|
|
490
538
|
const { content, isBase64 = false } = req.body;
|
|
@@ -503,10 +551,11 @@ router.put('/:directory/file/*', (req, res) => {
|
|
|
503
551
|
});
|
|
504
552
|
}
|
|
505
553
|
|
|
506
|
-
const result =
|
|
554
|
+
const result = service.updateSkillFile(directory, filePath, content, isBase64);
|
|
507
555
|
|
|
508
556
|
res.json({
|
|
509
557
|
success: true,
|
|
558
|
+
platform,
|
|
510
559
|
...result
|
|
511
560
|
});
|
|
512
561
|
} catch (err) {
|
|
@@ -529,6 +578,7 @@ router.put('/:directory/file/*', (req, res) => {
|
|
|
529
578
|
*/
|
|
530
579
|
router.post('/convert', (req, res) => {
|
|
531
580
|
try {
|
|
581
|
+
const { platform, service } = getSkillService(req);
|
|
532
582
|
const { content, targetFormat } = req.body;
|
|
533
583
|
|
|
534
584
|
if (!content) {
|
|
@@ -545,10 +595,11 @@ router.post('/convert', (req, res) => {
|
|
|
545
595
|
});
|
|
546
596
|
}
|
|
547
597
|
|
|
548
|
-
const result =
|
|
598
|
+
const result = service.convertSkillFormat(content, targetFormat);
|
|
549
599
|
|
|
550
600
|
res.json({
|
|
551
601
|
success: true,
|
|
602
|
+
platform,
|
|
552
603
|
...result
|
|
553
604
|
});
|
|
554
605
|
} catch (err) {
|