@adversity/coding-tool-x 3.0.6 → 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 +38 -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 +92 -13
- 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/ui.js +8 -1
- 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 +67 -4
- 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 +32 -19
- package/src/server/dev-server.js +1 -1
- package/src/server/gemini-proxy-server.js +17 -3
- package/src/server/index.js +164 -48
- package/src/server/opencode-proxy-server.js +4375 -0
- package/src/server/proxy-server.js +30 -19
- package/src/server/services/agents-service.js +61 -24
- package/src/server/services/channel-scheduler.js +9 -5
- package/src/server/services/channels.js +70 -12
- package/src/server/services/codex-channels.js +61 -23
- 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 +37 -15
- 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-BxudHPiX.js +0 -1
- package/dist/web/assets/index-D2VfwJBa.js +0 -14
- package/dist/web/assets/index-oXBzu0bd.css +0 -41
- package/dist/web/assets/naive-ui-DT-Uur8K.js +0 -1
- package/dist/web/assets/vue-vendor-6JaYHOiI.js +0 -44
- package/src/server/api/permissions.js +0 -385
- package/src/server/services/permission-templates-service.js +0 -308
|
@@ -13,9 +13,18 @@ const { getSchedulerState } = require('../services/channel-scheduler');
|
|
|
13
13
|
const { getChannelHealthStatus, resetChannelHealth } = require('../services/channel-health');
|
|
14
14
|
const { broadcastSchedulerState, broadcastLog } = require('../websocket-server');
|
|
15
15
|
const { isCodexInstalled } = require('../services/codex-config');
|
|
16
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
testChannelSpeed,
|
|
18
|
+
getLatencyLevel,
|
|
19
|
+
sanitizeBatchConcurrency,
|
|
20
|
+
runWithConcurrencyLimit
|
|
21
|
+
} = require('../services/speed-test');
|
|
17
22
|
const { clearCodexRedirectCache } = require('../codex-proxy-server');
|
|
18
|
-
const {
|
|
23
|
+
const {
|
|
24
|
+
fetchModelsFromProvider,
|
|
25
|
+
probeModelAvailability,
|
|
26
|
+
} = require('../services/model-detector');
|
|
27
|
+
const CODEX_GATEWAY_SOURCE_TYPE = 'codex';
|
|
19
28
|
|
|
20
29
|
module.exports = (config) => {
|
|
21
30
|
/**
|
|
@@ -58,11 +67,50 @@ module.exports = (config) => {
|
|
|
58
67
|
return res.status(404).json({ error: '渠道不存在' });
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
|
|
62
|
-
const
|
|
70
|
+
const gatewaySourceType = CODEX_GATEWAY_SOURCE_TYPE;
|
|
71
|
+
const listResult = await fetchModelsFromProvider(channel, 'openai_compatible');
|
|
72
|
+
const listedModels = Array.isArray(listResult.models) ? listResult.models : [];
|
|
73
|
+
let result;
|
|
74
|
+
|
|
75
|
+
if (listedModels.length > 0) {
|
|
76
|
+
result = listResult;
|
|
77
|
+
} else {
|
|
78
|
+
const usingConfiguredProbe = !!listResult.disabledByConfig;
|
|
79
|
+
const probe = await probeModelAvailability(channel, 'codex');
|
|
80
|
+
const probedModels = Array.isArray(probe.availableModels) ? probe.availableModels : [];
|
|
81
|
+
|
|
82
|
+
if (probedModels.length > 0) {
|
|
83
|
+
result = {
|
|
84
|
+
models: probedModels,
|
|
85
|
+
supported: true,
|
|
86
|
+
cached: !!probe.cached,
|
|
87
|
+
fallbackUsed: false,
|
|
88
|
+
lastChecked: probe.lastChecked || listResult.lastChecked || new Date().toISOString(),
|
|
89
|
+
error: null,
|
|
90
|
+
errorHint: listResult.error
|
|
91
|
+
? (usingConfiguredProbe
|
|
92
|
+
? '已按设置跳过 /v1/models,使用默认模型探测结果'
|
|
93
|
+
: '模型列表接口不可用,已自动切换为模型探测结果')
|
|
94
|
+
: null
|
|
95
|
+
};
|
|
96
|
+
} else {
|
|
97
|
+
result = {
|
|
98
|
+
models: [],
|
|
99
|
+
supported: false,
|
|
100
|
+
cached: !!probe.cached || !!listResult.cached,
|
|
101
|
+
fallbackUsed: false,
|
|
102
|
+
lastChecked: probe.lastChecked || listResult.lastChecked || new Date().toISOString(),
|
|
103
|
+
error: listResult.error || '无法探测可用模型',
|
|
104
|
+
errorHint: listResult.errorHint || (usingConfiguredProbe
|
|
105
|
+
? '已按设置跳过 /v1/models,且默认模型探测无可用结果'
|
|
106
|
+
: '模型列表接口不可用且模型探测无可用结果')
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
63
110
|
|
|
64
111
|
res.json({
|
|
65
112
|
channelId: id,
|
|
113
|
+
gatewaySourceType,
|
|
66
114
|
models: result.models,
|
|
67
115
|
supported: result.supported,
|
|
68
116
|
cached: result.cached,
|
|
@@ -91,10 +139,27 @@ module.exports = (config) => {
|
|
|
91
139
|
return res.status(404).json({ error: 'Codex CLI not installed' });
|
|
92
140
|
}
|
|
93
141
|
|
|
94
|
-
const {
|
|
142
|
+
const {
|
|
143
|
+
name,
|
|
144
|
+
providerKey,
|
|
145
|
+
baseUrl,
|
|
146
|
+
apiKey,
|
|
147
|
+
websiteUrl,
|
|
148
|
+
enabled,
|
|
149
|
+
weight,
|
|
150
|
+
maxConcurrency,
|
|
151
|
+
modelRedirects,
|
|
152
|
+
speedTestModel,
|
|
153
|
+
presetId,
|
|
154
|
+
gatewaySourceType
|
|
155
|
+
} = req.body;
|
|
156
|
+
|
|
157
|
+
if (!name || !providerKey || !baseUrl) {
|
|
158
|
+
return res.status(400).json({ error: 'Missing required fields: name, providerKey, baseUrl' });
|
|
159
|
+
}
|
|
95
160
|
|
|
96
|
-
if (!
|
|
97
|
-
return res.status(400).json({ error: 'Missing required fields' });
|
|
161
|
+
if (!apiKey) {
|
|
162
|
+
return res.status(400).json({ error: 'Missing required fields: apiKey' });
|
|
98
163
|
}
|
|
99
164
|
|
|
100
165
|
// wireApi 固定为 'responses' (OpenAI Responses API 格式)
|
|
@@ -102,7 +167,11 @@ module.exports = (config) => {
|
|
|
102
167
|
websiteUrl,
|
|
103
168
|
enabled,
|
|
104
169
|
weight,
|
|
105
|
-
maxConcurrency
|
|
170
|
+
maxConcurrency,
|
|
171
|
+
modelRedirects: modelRedirects || [],
|
|
172
|
+
speedTestModel: speedTestModel || null,
|
|
173
|
+
presetId: presetId || null,
|
|
174
|
+
gatewaySourceType
|
|
106
175
|
});
|
|
107
176
|
res.json(channel);
|
|
108
177
|
broadcastSchedulerState('codex', getSchedulerState('codex'));
|
|
@@ -140,14 +209,14 @@ module.exports = (config) => {
|
|
|
140
209
|
* DELETE /api/codex/channels/:channelId
|
|
141
210
|
* 删除渠道
|
|
142
211
|
*/
|
|
143
|
-
router.delete('/:channelId', (req, res) => {
|
|
212
|
+
router.delete('/:channelId', async (req, res) => {
|
|
144
213
|
try {
|
|
145
214
|
if (!isCodexInstalled()) {
|
|
146
215
|
return res.status(404).json({ error: 'Codex CLI not installed' });
|
|
147
216
|
}
|
|
148
217
|
|
|
149
218
|
const { channelId } = req.params;
|
|
150
|
-
const result = deleteChannel(channelId);
|
|
219
|
+
const result = await deleteChannel(channelId);
|
|
151
220
|
res.json(result);
|
|
152
221
|
broadcastSchedulerState('codex', getSchedulerState('codex'));
|
|
153
222
|
} catch (err) {
|
|
@@ -217,8 +286,10 @@ module.exports = (config) => {
|
|
|
217
286
|
return res.status(404).json({ error: '渠道不存在' });
|
|
218
287
|
}
|
|
219
288
|
|
|
220
|
-
const
|
|
289
|
+
const speedTestType = CODEX_GATEWAY_SOURCE_TYPE;
|
|
290
|
+
const result = await testChannelSpeed(channel, timeout, speedTestType);
|
|
221
291
|
result.level = getLatencyLevel(result.latency);
|
|
292
|
+
result.gatewaySourceType = speedTestType;
|
|
222
293
|
|
|
223
294
|
res.json(result);
|
|
224
295
|
} catch (error) {
|
|
@@ -237,18 +308,37 @@ module.exports = (config) => {
|
|
|
237
308
|
return res.json({ results: [], message: 'Codex CLI not installed' });
|
|
238
309
|
}
|
|
239
310
|
|
|
240
|
-
const { timeout = 10000 } = req.body;
|
|
311
|
+
const { timeout = 10000, concurrency } = req.body || {};
|
|
241
312
|
const data = getChannels();
|
|
242
313
|
const channels = data.channels || [];
|
|
314
|
+
const safeConcurrency = sanitizeBatchConcurrency(concurrency);
|
|
243
315
|
|
|
244
316
|
if (channels.length === 0) {
|
|
245
317
|
return res.json({ results: [], message: '没有可测试的渠道' });
|
|
246
318
|
}
|
|
247
319
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
320
|
+
const results = await runWithConcurrencyLimit(
|
|
321
|
+
channels,
|
|
322
|
+
safeConcurrency,
|
|
323
|
+
async channel => {
|
|
324
|
+
const speedTestType = CODEX_GATEWAY_SOURCE_TYPE;
|
|
325
|
+
const result = await testChannelSpeed(channel, timeout, speedTestType);
|
|
326
|
+
result.level = getLatencyLevel(result.latency);
|
|
327
|
+
result.gatewaySourceType = speedTestType;
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
// 成功在前,成功结果按延迟升序
|
|
333
|
+
results.sort((a, b) => {
|
|
334
|
+
if (a.success && !b.success) return -1;
|
|
335
|
+
if (!a.success && b.success) return 1;
|
|
336
|
+
if (a.success && b.success) {
|
|
337
|
+
const aLatency = (a.latency === null || a.latency === undefined) ? Infinity : a.latency;
|
|
338
|
+
const bLatency = (b.latency === null || b.latency === undefined) ? Infinity : b.latency;
|
|
339
|
+
return aLatency - bLatency;
|
|
340
|
+
}
|
|
341
|
+
return 0;
|
|
252
342
|
});
|
|
253
343
|
|
|
254
344
|
res.json({
|
|
@@ -257,7 +347,8 @@ module.exports = (config) => {
|
|
|
257
347
|
total: results.length,
|
|
258
348
|
success: results.filter(r => r.success).length,
|
|
259
349
|
failed: results.filter(r => !r.success).length,
|
|
260
|
-
avgLatency: calculateAvgLatency(results)
|
|
350
|
+
avgLatency: calculateAvgLatency(results),
|
|
351
|
+
concurrency: safeConcurrency
|
|
261
352
|
}
|
|
262
353
|
});
|
|
263
354
|
} catch (error) {
|
|
@@ -345,7 +436,9 @@ module.exports = (config) => {
|
|
|
345
436
|
|
|
346
437
|
// 计算平均延迟
|
|
347
438
|
function calculateAvgLatency(results) {
|
|
348
|
-
const successResults = results.filter(
|
|
439
|
+
const successResults = results.filter(
|
|
440
|
+
r => r.success && r.latency !== null && r.latency !== undefined
|
|
441
|
+
);
|
|
349
442
|
if (successResults.length === 0) return null;
|
|
350
443
|
const sum = successResults.reduce((acc, r) => acc + r.latency, 0);
|
|
351
444
|
return Math.round(sum / successResults.length);
|
|
@@ -15,9 +15,9 @@ const {
|
|
|
15
15
|
} = require('../services/codex-settings-manager');
|
|
16
16
|
const { getChannels, getEnabledChannels } = require('../services/codex-channels');
|
|
17
17
|
const { clearAllLogs } = require('../websocket-server');
|
|
18
|
+
const { PATHS, ensureStorageDirMigrated } = require('../../config/paths');
|
|
18
19
|
const fs = require('fs');
|
|
19
20
|
const path = require('path');
|
|
20
|
-
const os = require('os');
|
|
21
21
|
|
|
22
22
|
function sanitizeChannel(channel) {
|
|
23
23
|
if (!channel) return null;
|
|
@@ -32,14 +32,24 @@ function sanitizeChannel(channel) {
|
|
|
32
32
|
|
|
33
33
|
// 保存激活渠道ID
|
|
34
34
|
function saveActiveChannelId(channelId) {
|
|
35
|
-
|
|
35
|
+
ensureStorageDirMigrated();
|
|
36
|
+
const filePath = PATHS.activeChannel.codex;
|
|
37
|
+
const dir = path.dirname(filePath);
|
|
36
38
|
if (!fs.existsSync(dir)) {
|
|
37
39
|
fs.mkdirSync(dir, { recursive: true });
|
|
38
40
|
}
|
|
39
|
-
const filePath = path.join(dir, 'codex-active-channel.json');
|
|
40
41
|
fs.writeFileSync(filePath, JSON.stringify({ activeChannelId: channelId }, null, 2), 'utf8');
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
function removeActiveChannelFile() {
|
|
45
|
+
ensureStorageDirMigrated();
|
|
46
|
+
const filePath = PATHS.activeChannel.codex;
|
|
47
|
+
if (fs.existsSync(filePath)) {
|
|
48
|
+
fs.unlinkSync(filePath);
|
|
49
|
+
console.log('[Codex Proxy] Removed codex-active-channel.json');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
43
53
|
// 获取代理状态
|
|
44
54
|
router.get('/status', (req, res) => {
|
|
45
55
|
try {
|
|
@@ -147,11 +157,7 @@ router.post('/stop', async (req, res) => {
|
|
|
147
157
|
console.log('[Codex Proxy] Restored settings from backup');
|
|
148
158
|
|
|
149
159
|
// 删除 active-channel.json
|
|
150
|
-
|
|
151
|
-
if (fs.existsSync(activeChannelPath)) {
|
|
152
|
-
fs.unlinkSync(activeChannelPath);
|
|
153
|
-
console.log('[Codex Proxy] Removed codex-active-channel.json');
|
|
154
|
-
}
|
|
160
|
+
removeActiveChannelFile();
|
|
155
161
|
|
|
156
162
|
const response = {
|
|
157
163
|
success: true,
|
|
@@ -8,7 +8,24 @@ const express = require('express');
|
|
|
8
8
|
const { CommandsService } = require('../services/commands-service');
|
|
9
9
|
|
|
10
10
|
const router = express.Router();
|
|
11
|
-
const
|
|
11
|
+
const SUPPORTED_PLATFORMS = ['claude', 'opencode'];
|
|
12
|
+
const commandServices = 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 getCommandsService(req) {
|
|
23
|
+
const platform = getPlatform(req);
|
|
24
|
+
if (!commandServices.has(platform)) {
|
|
25
|
+
commandServices.set(platform, new CommandsService(platform));
|
|
26
|
+
}
|
|
27
|
+
return { platform, service: commandServices.get(platform) };
|
|
28
|
+
}
|
|
12
29
|
|
|
13
30
|
/**
|
|
14
31
|
* 获取命令列表
|
|
@@ -17,11 +34,13 @@ const commandsService = new CommandsService();
|
|
|
17
34
|
*/
|
|
18
35
|
router.get('/', (req, res) => {
|
|
19
36
|
try {
|
|
37
|
+
const { platform, service } = getCommandsService(req);
|
|
20
38
|
const { projectPath } = req.query;
|
|
21
|
-
const result =
|
|
39
|
+
const result = service.listCommands(projectPath || null);
|
|
22
40
|
|
|
23
41
|
res.json({
|
|
24
42
|
success: true,
|
|
43
|
+
platform,
|
|
25
44
|
...result
|
|
26
45
|
});
|
|
27
46
|
} catch (err) {
|
|
@@ -39,11 +58,13 @@ router.get('/', (req, res) => {
|
|
|
39
58
|
*/
|
|
40
59
|
router.get('/stats', (req, res) => {
|
|
41
60
|
try {
|
|
61
|
+
const { platform, service } = getCommandsService(req);
|
|
42
62
|
const { projectPath } = req.query;
|
|
43
|
-
const stats =
|
|
63
|
+
const stats = service.getStats(projectPath || null);
|
|
44
64
|
|
|
45
65
|
res.json({
|
|
46
66
|
success: true,
|
|
67
|
+
platform,
|
|
47
68
|
...stats
|
|
48
69
|
});
|
|
49
70
|
} catch (err) {
|
|
@@ -62,6 +83,7 @@ router.get('/stats', (req, res) => {
|
|
|
62
83
|
*/
|
|
63
84
|
router.get('/:scope/:name', (req, res) => {
|
|
64
85
|
try {
|
|
86
|
+
const { platform, service } = getCommandsService(req);
|
|
65
87
|
const { scope, name } = req.params;
|
|
66
88
|
const { projectPath, namespace } = req.query;
|
|
67
89
|
|
|
@@ -79,7 +101,7 @@ router.get('/:scope/:name', (req, res) => {
|
|
|
79
101
|
});
|
|
80
102
|
}
|
|
81
103
|
|
|
82
|
-
const command =
|
|
104
|
+
const command = service.getCommand(name, scope, projectPath || null, namespace || null);
|
|
83
105
|
|
|
84
106
|
if (!command) {
|
|
85
107
|
return res.status(404).json({
|
|
@@ -90,6 +112,7 @@ router.get('/:scope/:name', (req, res) => {
|
|
|
90
112
|
|
|
91
113
|
res.json({
|
|
92
114
|
success: true,
|
|
115
|
+
platform,
|
|
93
116
|
command
|
|
94
117
|
});
|
|
95
118
|
} catch (err) {
|
|
@@ -108,7 +131,8 @@ router.get('/:scope/:name', (req, res) => {
|
|
|
108
131
|
*/
|
|
109
132
|
router.post('/', (req, res) => {
|
|
110
133
|
try {
|
|
111
|
-
const {
|
|
134
|
+
const { platform, service } = getCommandsService(req);
|
|
135
|
+
const { name, scope, projectPath, namespace, description, allowedTools, argumentHint, agent, model, subtask, body } = req.body;
|
|
112
136
|
|
|
113
137
|
if (!name) {
|
|
114
138
|
return res.status(400).json({
|
|
@@ -131,7 +155,7 @@ router.post('/', (req, res) => {
|
|
|
131
155
|
});
|
|
132
156
|
}
|
|
133
157
|
|
|
134
|
-
const command =
|
|
158
|
+
const command = service.createCommand({
|
|
135
159
|
name,
|
|
136
160
|
scope,
|
|
137
161
|
projectPath: projectPath || null,
|
|
@@ -139,11 +163,15 @@ router.post('/', (req, res) => {
|
|
|
139
163
|
description: description || '',
|
|
140
164
|
allowedTools: allowedTools || '',
|
|
141
165
|
argumentHint: argumentHint || '',
|
|
166
|
+
agent: agent || '',
|
|
167
|
+
model: model || '',
|
|
168
|
+
subtask: typeof subtask === 'boolean' ? subtask : undefined,
|
|
142
169
|
body: body || ''
|
|
143
170
|
});
|
|
144
171
|
|
|
145
172
|
res.json({
|
|
146
173
|
success: true,
|
|
174
|
+
platform,
|
|
147
175
|
command,
|
|
148
176
|
message: '命令创建成功'
|
|
149
177
|
});
|
|
@@ -162,8 +190,9 @@ router.post('/', (req, res) => {
|
|
|
162
190
|
*/
|
|
163
191
|
router.put('/:scope/:name', (req, res) => {
|
|
164
192
|
try {
|
|
193
|
+
const { platform, service } = getCommandsService(req);
|
|
165
194
|
const { scope, name } = req.params;
|
|
166
|
-
const { projectPath, namespace, description, allowedTools, argumentHint, body } = req.body;
|
|
195
|
+
const { projectPath, namespace, description, allowedTools, argumentHint, agent, model, subtask, body } = req.body;
|
|
167
196
|
|
|
168
197
|
if (!['user', 'project'].includes(scope)) {
|
|
169
198
|
return res.status(400).json({
|
|
@@ -179,7 +208,7 @@ router.put('/:scope/:name', (req, res) => {
|
|
|
179
208
|
});
|
|
180
209
|
}
|
|
181
210
|
|
|
182
|
-
const command =
|
|
211
|
+
const command = service.updateCommand({
|
|
183
212
|
name,
|
|
184
213
|
scope,
|
|
185
214
|
projectPath: projectPath || null,
|
|
@@ -187,11 +216,15 @@ router.put('/:scope/:name', (req, res) => {
|
|
|
187
216
|
description: description || '',
|
|
188
217
|
allowedTools: allowedTools || '',
|
|
189
218
|
argumentHint: argumentHint || '',
|
|
219
|
+
agent: agent || '',
|
|
220
|
+
model: model || '',
|
|
221
|
+
subtask: typeof subtask === 'boolean' ? subtask : undefined,
|
|
190
222
|
body: body || ''
|
|
191
223
|
});
|
|
192
224
|
|
|
193
225
|
res.json({
|
|
194
226
|
success: true,
|
|
227
|
+
platform,
|
|
195
228
|
command,
|
|
196
229
|
message: '命令更新成功'
|
|
197
230
|
});
|
|
@@ -210,6 +243,7 @@ router.put('/:scope/:name', (req, res) => {
|
|
|
210
243
|
*/
|
|
211
244
|
router.delete('/:scope/:name', (req, res) => {
|
|
212
245
|
try {
|
|
246
|
+
const { platform, service } = getCommandsService(req);
|
|
213
247
|
const { scope, name } = req.params;
|
|
214
248
|
const { projectPath, namespace } = req.query;
|
|
215
249
|
|
|
@@ -227,9 +261,10 @@ router.delete('/:scope/:name', (req, res) => {
|
|
|
227
261
|
});
|
|
228
262
|
}
|
|
229
263
|
|
|
230
|
-
const result =
|
|
264
|
+
const result = service.deleteCommand(name, scope, projectPath || null, namespace || null);
|
|
231
265
|
|
|
232
266
|
res.json({
|
|
267
|
+
platform,
|
|
233
268
|
success: result.success,
|
|
234
269
|
message: result.message
|
|
235
270
|
});
|
|
@@ -251,12 +286,14 @@ router.delete('/:scope/:name', (req, res) => {
|
|
|
251
286
|
*/
|
|
252
287
|
router.get('/all', async (req, res) => {
|
|
253
288
|
try {
|
|
289
|
+
const { platform, service } = getCommandsService(req);
|
|
254
290
|
const { projectPath, refresh } = req.query;
|
|
255
291
|
const forceRefresh = refresh === '1';
|
|
256
|
-
const result = await
|
|
292
|
+
const result = await service.listAllCommands(projectPath || null, forceRefresh);
|
|
257
293
|
|
|
258
294
|
res.json({
|
|
259
295
|
success: true,
|
|
296
|
+
platform,
|
|
260
297
|
...result
|
|
261
298
|
});
|
|
262
299
|
} catch (err) {
|
|
@@ -274,9 +311,11 @@ router.get('/all', async (req, res) => {
|
|
|
274
311
|
*/
|
|
275
312
|
router.get('/repos', (req, res) => {
|
|
276
313
|
try {
|
|
277
|
-
const
|
|
314
|
+
const { platform, service } = getCommandsService(req);
|
|
315
|
+
const repos = service.getRepos();
|
|
278
316
|
res.json({
|
|
279
317
|
success: true,
|
|
318
|
+
platform,
|
|
280
319
|
repos
|
|
281
320
|
});
|
|
282
321
|
} catch (err) {
|
|
@@ -295,6 +334,7 @@ router.get('/repos', (req, res) => {
|
|
|
295
334
|
*/
|
|
296
335
|
router.post('/repos', (req, res) => {
|
|
297
336
|
try {
|
|
337
|
+
const { platform, service } = getCommandsService(req);
|
|
298
338
|
const { owner, name, branch = 'main', directory = '', enabled = true } = req.body;
|
|
299
339
|
|
|
300
340
|
if (!owner || !name) {
|
|
@@ -304,10 +344,11 @@ router.post('/repos', (req, res) => {
|
|
|
304
344
|
});
|
|
305
345
|
}
|
|
306
346
|
|
|
307
|
-
const repos =
|
|
347
|
+
const repos = service.addRepo({ owner, name, branch, directory, enabled });
|
|
308
348
|
|
|
309
349
|
res.json({
|
|
310
350
|
success: true,
|
|
351
|
+
platform,
|
|
311
352
|
repos
|
|
312
353
|
});
|
|
313
354
|
} catch (err) {
|
|
@@ -326,12 +367,14 @@ router.post('/repos', (req, res) => {
|
|
|
326
367
|
*/
|
|
327
368
|
router.delete('/repos/:owner/:name', (req, res) => {
|
|
328
369
|
try {
|
|
370
|
+
const { platform, service } = getCommandsService(req);
|
|
329
371
|
const { owner, name } = req.params;
|
|
330
372
|
const { directory = '' } = req.query;
|
|
331
|
-
const repos =
|
|
373
|
+
const repos = service.removeRepo(owner, name, directory);
|
|
332
374
|
|
|
333
375
|
res.json({
|
|
334
376
|
success: true,
|
|
377
|
+
platform,
|
|
335
378
|
repos
|
|
336
379
|
});
|
|
337
380
|
} catch (err) {
|
|
@@ -350,13 +393,15 @@ router.delete('/repos/:owner/:name', (req, res) => {
|
|
|
350
393
|
*/
|
|
351
394
|
router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
352
395
|
try {
|
|
396
|
+
const { platform, service } = getCommandsService(req);
|
|
353
397
|
const { owner, name } = req.params;
|
|
354
398
|
const { enabled, directory = '' } = req.body;
|
|
355
399
|
|
|
356
|
-
const repos =
|
|
400
|
+
const repos = service.toggleRepo(owner, name, directory, enabled);
|
|
357
401
|
|
|
358
402
|
res.json({
|
|
359
403
|
success: true,
|
|
404
|
+
platform,
|
|
360
405
|
repos
|
|
361
406
|
});
|
|
362
407
|
} catch (err) {
|
|
@@ -375,6 +420,7 @@ router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
|
375
420
|
*/
|
|
376
421
|
router.post('/install', async (req, res) => {
|
|
377
422
|
try {
|
|
423
|
+
const { platform, service } = getCommandsService(req);
|
|
378
424
|
const command = req.body;
|
|
379
425
|
|
|
380
426
|
if (!command || !command.repoOwner || !command.repoName) {
|
|
@@ -384,10 +430,11 @@ router.post('/install', async (req, res) => {
|
|
|
384
430
|
});
|
|
385
431
|
}
|
|
386
432
|
|
|
387
|
-
const result = await
|
|
433
|
+
const result = await service.installFromRemote(command);
|
|
388
434
|
|
|
389
435
|
res.json({
|
|
390
436
|
success: true,
|
|
437
|
+
platform,
|
|
391
438
|
...result
|
|
392
439
|
});
|
|
393
440
|
} catch (err) {
|
|
@@ -406,6 +453,7 @@ router.post('/install', async (req, res) => {
|
|
|
406
453
|
*/
|
|
407
454
|
router.post('/uninstall', (req, res) => {
|
|
408
455
|
try {
|
|
456
|
+
const { platform, service } = getCommandsService(req);
|
|
409
457
|
const { path } = req.body;
|
|
410
458
|
|
|
411
459
|
if (!path) {
|
|
@@ -415,10 +463,11 @@ router.post('/uninstall', (req, res) => {
|
|
|
415
463
|
});
|
|
416
464
|
}
|
|
417
465
|
|
|
418
|
-
const result =
|
|
466
|
+
const result = service.uninstallCommand(path);
|
|
419
467
|
|
|
420
468
|
res.json({
|
|
421
469
|
success: true,
|
|
470
|
+
platform,
|
|
422
471
|
...result
|
|
423
472
|
});
|
|
424
473
|
} catch (err) {
|
|
@@ -441,6 +490,7 @@ router.post('/uninstall', (req, res) => {
|
|
|
441
490
|
*/
|
|
442
491
|
router.post('/convert', (req, res) => {
|
|
443
492
|
try {
|
|
493
|
+
const { platform, service } = getCommandsService(req);
|
|
444
494
|
const { content, targetFormat } = req.body;
|
|
445
495
|
|
|
446
496
|
if (!content) {
|
|
@@ -457,10 +507,11 @@ router.post('/convert', (req, res) => {
|
|
|
457
507
|
});
|
|
458
508
|
}
|
|
459
509
|
|
|
460
|
-
const result =
|
|
510
|
+
const result = service.convertCommandFormat(content, targetFormat);
|
|
461
511
|
|
|
462
512
|
res.json({
|
|
463
513
|
success: true,
|
|
514
|
+
platform,
|
|
464
515
|
...result
|
|
465
516
|
});
|
|
466
517
|
} catch (err) {
|
|
@@ -479,6 +530,7 @@ router.post('/convert', (req, res) => {
|
|
|
479
530
|
*/
|
|
480
531
|
router.post('/detect-format', (req, res) => {
|
|
481
532
|
try {
|
|
533
|
+
const { platform, service } = getCommandsService(req);
|
|
482
534
|
const { content } = req.body;
|
|
483
535
|
|
|
484
536
|
if (!content) {
|
|
@@ -488,10 +540,11 @@ router.post('/detect-format', (req, res) => {
|
|
|
488
540
|
});
|
|
489
541
|
}
|
|
490
542
|
|
|
491
|
-
const format =
|
|
543
|
+
const format = service.detectFormat(content);
|
|
492
544
|
|
|
493
545
|
res.json({
|
|
494
546
|
success: true,
|
|
547
|
+
platform,
|
|
495
548
|
format
|
|
496
549
|
});
|
|
497
550
|
} catch (err) {
|
|
@@ -23,17 +23,11 @@ function buildPreviewSummary(data) {
|
|
|
23
23
|
version: data.version,
|
|
24
24
|
exportedAt: data.exportedAt,
|
|
25
25
|
counts: {
|
|
26
|
-
permissionTemplates: (data.data.permissionTemplates || []).length,
|
|
27
26
|
configTemplates: (data.data.configTemplates || []).length,
|
|
28
27
|
channels: (data.data.channels || []).length,
|
|
29
28
|
plugins: (data.data.plugins || []).length
|
|
30
29
|
},
|
|
31
30
|
items: {
|
|
32
|
-
permissionTemplates: (data.data.permissionTemplates || []).map(t => ({
|
|
33
|
-
id: t.id,
|
|
34
|
-
name: t.name,
|
|
35
|
-
description: t.description
|
|
36
|
-
})),
|
|
37
31
|
configTemplates: (data.data.configTemplates || []).map(t => ({
|
|
38
32
|
id: t.id,
|
|
39
33
|
name: t.name,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const express = require('express');
|
|
9
|
-
const { ConfigRegistryService, CONFIG_TYPES } = require('../services/config-registry-service');
|
|
9
|
+
const { ConfigRegistryService, CONFIG_TYPES, SUPPORTED_PLATFORMS } = require('../services/config-registry-service');
|
|
10
10
|
const { ConfigSyncManager } = require('../services/config-sync-manager');
|
|
11
11
|
|
|
12
12
|
const router = express.Router();
|
|
@@ -17,7 +17,7 @@ const syncManager = new ConfigSyncManager();
|
|
|
17
17
|
const VALID_TYPES = CONFIG_TYPES;
|
|
18
18
|
|
|
19
19
|
// Valid platforms
|
|
20
|
-
const VALID_PLATFORMS =
|
|
20
|
+
const VALID_PLATFORMS = SUPPORTED_PLATFORMS;
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Validate config type parameter
|
|
@@ -199,10 +199,14 @@ router.put('/:type/:name/toggle', async (req, res) => {
|
|
|
199
199
|
if (item.platforms?.codex) {
|
|
200
200
|
syncManager.syncToCodex(type, name);
|
|
201
201
|
}
|
|
202
|
+
if (item.platforms?.opencode) {
|
|
203
|
+
syncManager.syncToOpenCode(type, name);
|
|
204
|
+
}
|
|
202
205
|
} else {
|
|
203
206
|
// Remove from all platforms
|
|
204
207
|
syncManager.removeFromClaude(type, name);
|
|
205
208
|
syncManager.removeFromCodex(type, name);
|
|
209
|
+
syncManager.removeFromOpenCode(type, name);
|
|
206
210
|
}
|
|
207
211
|
|
|
208
212
|
res.json({
|
|
@@ -220,7 +224,7 @@ router.put('/:type/:name/toggle', async (req, res) => {
|
|
|
220
224
|
|
|
221
225
|
/**
|
|
222
226
|
* PUT /api/config-registry/:type/:name/platform/:platform
|
|
223
|
-
* Toggle platform (claude/codex) for an item
|
|
227
|
+
* Toggle platform (claude/codex/opencode) for an item
|
|
224
228
|
* Body: { enabled: boolean }
|
|
225
229
|
* - Updates registry
|
|
226
230
|
* - If enabling platform: sync to that platform (if item is enabled)
|
|
@@ -275,6 +279,8 @@ router.put('/:type/:name/platform/:platform', async (req, res) => {
|
|
|
275
279
|
syncManager.syncToClaude(type, name);
|
|
276
280
|
} else if (platform === 'codex') {
|
|
277
281
|
syncManager.syncToCodex(type, name);
|
|
282
|
+
} else if (platform === 'opencode') {
|
|
283
|
+
syncManager.syncToOpenCode(type, name);
|
|
278
284
|
}
|
|
279
285
|
} else {
|
|
280
286
|
// Remove from this platform
|
|
@@ -282,6 +288,8 @@ router.put('/:type/:name/platform/:platform', async (req, res) => {
|
|
|
282
288
|
syncManager.removeFromClaude(type, name);
|
|
283
289
|
} else if (platform === 'codex') {
|
|
284
290
|
syncManager.removeFromCodex(type, name);
|
|
291
|
+
} else if (platform === 'opencode') {
|
|
292
|
+
syncManager.removeFromOpenCode(type, name);
|
|
285
293
|
}
|
|
286
294
|
}
|
|
287
295
|
|