@adversity/coding-tool-x 3.1.0 → 3.1.2
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 +39 -18
- package/README.md +8 -8
- package/dist/web/assets/ConfigTemplates-Bidwfdf2.css +1 -0
- package/dist/web/assets/ConfigTemplates-DvcbKKdS.js +1 -0
- package/dist/web/assets/Home-BJKPCBuk.css +1 -0
- package/dist/web/assets/Home-Cw-F_Wnu.js +1 -0
- package/dist/web/assets/PluginManager-ROyoZ-6m.css +1 -0
- package/dist/web/assets/PluginManager-jy_4GVxI.js +1 -0
- package/dist/web/assets/ProjectList-C1fQb9OW.css +1 -0
- package/dist/web/assets/ProjectList-Df1-NcNr.js +1 -0
- package/dist/web/assets/SessionList-BGJWyneI.css +1 -0
- package/dist/web/assets/SessionList-UWcZtC2r.js +1 -0
- package/dist/web/assets/SkillManager-D7pd-d_P.css +1 -0
- package/dist/web/assets/SkillManager-IRdseMKB.js +1 -0
- package/dist/web/assets/Terminal-BasTyDut.js +1 -0
- package/dist/web/assets/Terminal-DGNJeVtc.css +1 -0
- package/dist/web/assets/WorkspaceManager-CrwgQgmP.css +1 -0
- package/dist/web/assets/WorkspaceManager-D-D2kK1V.js +1 -0
- package/dist/web/assets/icons-kcfLIMBB.js +1 -0
- package/dist/web/assets/index-CoB3zF0K.css +1 -0
- package/dist/web/assets/index-CryrSLv8.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 +41 -2
- package/src/config/loader.js +74 -8
- package/src/config/model-metadata.js +415 -0
- package/src/config/model-pricing.js +23 -93
- 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 +497 -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 +345 -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/settings.js +111 -0
- package/src/server/api/skills.js +69 -18
- package/src/server/api/workspaces.js +78 -6
- package/src/server/codex-proxy-server.js +36 -22
- package/src/server/dev-server.js +1 -1
- package/src/server/gemini-proxy-server.js +21 -7
- package/src/server/index.js +174 -58
- package/src/server/opencode-proxy-server.js +5486 -0
- package/src/server/proxy-server.js +33 -22
- 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-sessions.js +105 -6
- 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 +50 -13
- package/src/server/services/env-manager.js +155 -19
- 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 +208 -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 +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/response-decoder.js +21 -0
- 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 +156 -8
- 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) {
|
|
@@ -2,6 +2,8 @@ const express = require('express');
|
|
|
2
2
|
const router = express.Router();
|
|
3
3
|
const { detectAvailableTerminals } = require('../services/terminal-detector');
|
|
4
4
|
const { loadTerminalConfig, saveTerminalConfig, getSelectedTerminal } = require('../services/terminal-config');
|
|
5
|
+
const { MODEL_METADATA, resolveModelMetadata, METADATA_LAST_UPDATED } = require('../../config/model-metadata');
|
|
6
|
+
const { loadConfig, saveConfig } = require('../../config/loader');
|
|
5
7
|
|
|
6
8
|
// GET /api/settings/terminals - 获取可用终端列表
|
|
7
9
|
router.get('/terminals', (req, res) => {
|
|
@@ -58,4 +60,113 @@ router.post('/terminal-config', (req, res) => {
|
|
|
58
60
|
}
|
|
59
61
|
});
|
|
60
62
|
|
|
63
|
+
// GET /api/settings/model-metadata - 获取内置模型元数据表(limit + pricing)
|
|
64
|
+
router.get('/model-metadata', (req, res) => {
|
|
65
|
+
try {
|
|
66
|
+
// Return built-in defaults merged with any user overrides
|
|
67
|
+
const config = loadConfig();
|
|
68
|
+
const overrides = config.modelMetadataOverrides || {};
|
|
69
|
+
|
|
70
|
+
// Build merged table: built-in + user overrides
|
|
71
|
+
const merged = {};
|
|
72
|
+
for (const [id, meta] of Object.entries(MODEL_METADATA)) {
|
|
73
|
+
merged[id] = overrides[id]
|
|
74
|
+
? {
|
|
75
|
+
limit: { ...meta.limit, ...(overrides[id].limit || {}) },
|
|
76
|
+
pricing: { ...meta.pricing, ...(overrides[id].pricing || {}) }
|
|
77
|
+
}
|
|
78
|
+
: meta;
|
|
79
|
+
}
|
|
80
|
+
// Also include any user-added custom models from overrides
|
|
81
|
+
for (const [id, meta] of Object.entries(overrides)) {
|
|
82
|
+
if (!merged[id]) {
|
|
83
|
+
merged[id] = meta;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
res.json({
|
|
88
|
+
models: merged,
|
|
89
|
+
overrides,
|
|
90
|
+
lastUpdated: METADATA_LAST_UPDATED
|
|
91
|
+
});
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('Error getting model metadata:', error);
|
|
94
|
+
res.status(500).json({ error: error.message });
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// POST /api/settings/model-metadata - 保存模型元数据覆盖项
|
|
99
|
+
// Body: { overrides: { [modelId]: { limit?: {...}, pricing?: {...} } } }
|
|
100
|
+
router.post('/model-metadata', (req, res) => {
|
|
101
|
+
try {
|
|
102
|
+
const { overrides } = req.body;
|
|
103
|
+
if (!overrides || typeof overrides !== 'object') {
|
|
104
|
+
return res.status(400).json({ error: 'overrides must be an object' });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Validate each override entry
|
|
108
|
+
for (const [modelId, meta] of Object.entries(overrides)) {
|
|
109
|
+
if (typeof modelId !== 'string' || !modelId.trim()) {
|
|
110
|
+
return res.status(400).json({ error: `Invalid model ID: "${modelId}"` });
|
|
111
|
+
}
|
|
112
|
+
if (meta.limit !== undefined) {
|
|
113
|
+
if (typeof meta.limit !== 'object') {
|
|
114
|
+
return res.status(400).json({ error: `${modelId}: limit must be an object` });
|
|
115
|
+
}
|
|
116
|
+
if (meta.limit.context !== undefined && (typeof meta.limit.context !== 'number' || meta.limit.context <= 0)) {
|
|
117
|
+
return res.status(400).json({ error: `${modelId}: limit.context must be a positive number` });
|
|
118
|
+
}
|
|
119
|
+
if (meta.limit.output !== undefined && (typeof meta.limit.output !== 'number' || meta.limit.output <= 0)) {
|
|
120
|
+
return res.status(400).json({ error: `${modelId}: limit.output must be a positive number` });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (meta.pricing !== undefined) {
|
|
124
|
+
if (typeof meta.pricing !== 'object') {
|
|
125
|
+
return res.status(400).json({ error: `${modelId}: pricing must be an object` });
|
|
126
|
+
}
|
|
127
|
+
for (const field of ['input', 'output']) {
|
|
128
|
+
if (meta.pricing[field] !== undefined && (typeof meta.pricing[field] !== 'number' || meta.pricing[field] < 0)) {
|
|
129
|
+
return res.status(400).json({ error: `${modelId}: pricing.${field} must be a non-negative number` });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const config = loadConfig();
|
|
136
|
+
const newConfig = {
|
|
137
|
+
...config,
|
|
138
|
+
projectsDir: config.projectsDir.replace(require('os').homedir(), '~'),
|
|
139
|
+
modelMetadataOverrides: overrides
|
|
140
|
+
};
|
|
141
|
+
saveConfig(newConfig);
|
|
142
|
+
|
|
143
|
+
res.json({ success: true, overrides });
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error('Error saving model metadata:', error);
|
|
146
|
+
res.status(500).json({ error: error.message });
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// DELETE /api/settings/model-metadata/:modelId - 删除单个模型覆盖项(恢复内置默认值)
|
|
151
|
+
router.delete('/model-metadata/:modelId', (req, res) => {
|
|
152
|
+
try {
|
|
153
|
+
const modelId = decodeURIComponent(req.params.modelId);
|
|
154
|
+
const config = loadConfig();
|
|
155
|
+
const overrides = { ...(config.modelMetadataOverrides || {}) };
|
|
156
|
+
delete overrides[modelId];
|
|
157
|
+
|
|
158
|
+
const newConfig = {
|
|
159
|
+
...config,
|
|
160
|
+
projectsDir: config.projectsDir.replace(require('os').homedir(), '~'),
|
|
161
|
+
modelMetadataOverrides: overrides
|
|
162
|
+
};
|
|
163
|
+
saveConfig(newConfig);
|
|
164
|
+
|
|
165
|
+
res.json({ success: true, modelId });
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.error('Error deleting model metadata override:', error);
|
|
168
|
+
res.status(500).json({ error: error.message });
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
61
172
|
module.exports = router;
|