@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
|
@@ -12,15 +12,22 @@ const { spawn } = require('child_process');
|
|
|
12
12
|
const http = require('http');
|
|
13
13
|
const https = require('https');
|
|
14
14
|
const { McpClient } = require('./mcp-client');
|
|
15
|
+
const { NATIVE_PATHS } = require('../../config/paths');
|
|
15
16
|
|
|
16
17
|
// MCP 配置文件路径
|
|
17
|
-
const CC_TOOL_DIR = path.join(os.homedir(), '.
|
|
18
|
+
const CC_TOOL_DIR = path.join(os.homedir(), '.cc-tool');
|
|
18
19
|
const MCP_SERVERS_FILE = path.join(CC_TOOL_DIR, 'mcp-servers.json');
|
|
19
20
|
|
|
20
21
|
// 各平台配置文件路径
|
|
21
22
|
const CLAUDE_CONFIG_PATH = path.join(os.homedir(), '.claude.json');
|
|
22
23
|
const CODEX_CONFIG_PATH = path.join(os.homedir(), '.codex', 'config.toml');
|
|
23
24
|
const GEMINI_CONFIG_PATH = path.join(os.homedir(), '.gemini', 'settings.json');
|
|
25
|
+
const OPENCODE_CONFIG_DIR = NATIVE_PATHS.opencode.config;
|
|
26
|
+
const OPENCODE_CONFIG_PATHS = {
|
|
27
|
+
jsonc: path.join(OPENCODE_CONFIG_DIR, 'opencode.jsonc'),
|
|
28
|
+
json: path.join(OPENCODE_CONFIG_DIR, 'opencode.json'),
|
|
29
|
+
legacy: path.join(OPENCODE_CONFIG_DIR, 'config.json')
|
|
30
|
+
};
|
|
24
31
|
|
|
25
32
|
// MCP 客户端连接池
|
|
26
33
|
// serverId -> { client, timestamp }
|
|
@@ -301,15 +308,140 @@ function writeTomlFile(filePath, data) {
|
|
|
301
308
|
fs.renameSync(tempPath, filePath);
|
|
302
309
|
}
|
|
303
310
|
|
|
311
|
+
/**
|
|
312
|
+
* 去除 JSONC 注释
|
|
313
|
+
*/
|
|
314
|
+
function stripJsonComments(input) {
|
|
315
|
+
let result = '';
|
|
316
|
+
let inString = false;
|
|
317
|
+
let quote = '';
|
|
318
|
+
let index = 0;
|
|
319
|
+
|
|
320
|
+
while (index < input.length) {
|
|
321
|
+
const ch = input[index];
|
|
322
|
+
const next = input[index + 1];
|
|
323
|
+
|
|
324
|
+
if (inString) {
|
|
325
|
+
result += ch;
|
|
326
|
+
if (ch === '\\') {
|
|
327
|
+
if (next) {
|
|
328
|
+
result += next;
|
|
329
|
+
index += 2;
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
} else if (ch === quote) {
|
|
333
|
+
inString = false;
|
|
334
|
+
}
|
|
335
|
+
index += 1;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (ch === '"' || ch === '\'') {
|
|
340
|
+
inString = true;
|
|
341
|
+
quote = ch;
|
|
342
|
+
result += ch;
|
|
343
|
+
index += 1;
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (ch === '/' && next === '/') {
|
|
348
|
+
index += 2;
|
|
349
|
+
while (index < input.length && input[index] !== '\n') {
|
|
350
|
+
index += 1;
|
|
351
|
+
}
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (ch === '/' && next === '*') {
|
|
356
|
+
index += 2;
|
|
357
|
+
while (index < input.length - 1 && !(input[index] === '*' && input[index + 1] === '/')) {
|
|
358
|
+
index += 1;
|
|
359
|
+
}
|
|
360
|
+
index += 2;
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
result += ch;
|
|
365
|
+
index += 1;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return result;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* 选择 OpenCode 配置文件路径
|
|
373
|
+
*/
|
|
374
|
+
function selectOpenCodeConfigPath() {
|
|
375
|
+
if (fs.existsSync(OPENCODE_CONFIG_PATHS.jsonc)) return OPENCODE_CONFIG_PATHS.jsonc;
|
|
376
|
+
if (fs.existsSync(OPENCODE_CONFIG_PATHS.json)) return OPENCODE_CONFIG_PATHS.json;
|
|
377
|
+
if (fs.existsSync(OPENCODE_CONFIG_PATHS.legacy)) return OPENCODE_CONFIG_PATHS.legacy;
|
|
378
|
+
return OPENCODE_CONFIG_PATHS.json;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* 读取 OpenCode 配置
|
|
383
|
+
*/
|
|
384
|
+
function readOpenCodeConfig() {
|
|
385
|
+
const filePath = selectOpenCodeConfigPath();
|
|
386
|
+
|
|
387
|
+
if (!fs.existsSync(filePath)) {
|
|
388
|
+
return { path: filePath, config: {} };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
try {
|
|
392
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
393
|
+
if (!raw.trim()) {
|
|
394
|
+
return { path: filePath, config: {} };
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const content = filePath.endsWith('.jsonc') ? stripJsonComments(raw) : raw;
|
|
398
|
+
return {
|
|
399
|
+
path: filePath,
|
|
400
|
+
config: JSON.parse(content)
|
|
401
|
+
};
|
|
402
|
+
} catch (err) {
|
|
403
|
+
console.error(`[MCP] Failed to read OpenCode config:`, err.message);
|
|
404
|
+
return { path: filePath, config: {} };
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* 写入 OpenCode 配置(保持 JSON 格式)
|
|
410
|
+
*/
|
|
411
|
+
function writeOpenCodeConfig(filePath, data) {
|
|
412
|
+
ensureDir(path.dirname(filePath));
|
|
413
|
+
const tempPath = filePath + '.tmp';
|
|
414
|
+
fs.writeFileSync(tempPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
415
|
+
fs.renameSync(tempPath, filePath);
|
|
416
|
+
}
|
|
417
|
+
|
|
304
418
|
// ============================================================================
|
|
305
419
|
// MCP 数据管理
|
|
306
420
|
// ============================================================================
|
|
307
421
|
|
|
422
|
+
function normalizeServerApps(apps = {}) {
|
|
423
|
+
return {
|
|
424
|
+
claude: apps.claude !== undefined ? !!apps.claude : true,
|
|
425
|
+
codex: !!apps.codex,
|
|
426
|
+
gemini: !!apps.gemini,
|
|
427
|
+
opencode: !!apps.opencode
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
308
431
|
/**
|
|
309
432
|
* 获取所有 MCP 服务器
|
|
310
433
|
*/
|
|
311
434
|
function getAllServers() {
|
|
312
|
-
|
|
435
|
+
const servers = readJsonFile(MCP_SERVERS_FILE, {});
|
|
436
|
+
|
|
437
|
+
for (const server of Object.values(servers)) {
|
|
438
|
+
if (!server || typeof server !== 'object') {
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
server.apps = normalizeServerApps(server.apps);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return servers;
|
|
313
445
|
}
|
|
314
446
|
|
|
315
447
|
/**
|
|
@@ -341,7 +473,9 @@ async function saveServer(server) {
|
|
|
341
473
|
|
|
342
474
|
// 确保 apps 字段存在
|
|
343
475
|
if (!server.apps) {
|
|
344
|
-
server.apps = { claude: true, codex: false, gemini: false };
|
|
476
|
+
server.apps = { claude: true, codex: false, gemini: false, opencode: false };
|
|
477
|
+
} else {
|
|
478
|
+
server.apps = normalizeServerApps(server.apps);
|
|
345
479
|
}
|
|
346
480
|
|
|
347
481
|
servers[server.id] = server;
|
|
@@ -384,7 +518,7 @@ async function toggleServerApp(serverId, app, enabled) {
|
|
|
384
518
|
throw new Error(`MCP 服务器 "${serverId}" 不存在`);
|
|
385
519
|
}
|
|
386
520
|
|
|
387
|
-
if (!['claude', 'codex', 'gemini'].includes(app)) {
|
|
521
|
+
if (!['claude', 'codex', 'gemini', 'opencode'].includes(app)) {
|
|
388
522
|
throw new Error(`无效的平台: ${app}`);
|
|
389
523
|
}
|
|
390
524
|
|
|
@@ -466,6 +600,12 @@ async function syncServerToAllPlatforms(server) {
|
|
|
466
600
|
} else {
|
|
467
601
|
await removeServerFromPlatform(server.id, 'gemini');
|
|
468
602
|
}
|
|
603
|
+
|
|
604
|
+
if (apps.opencode) {
|
|
605
|
+
await syncServerToPlatform(server, 'opencode');
|
|
606
|
+
} else {
|
|
607
|
+
await removeServerFromPlatform(server.id, 'opencode');
|
|
608
|
+
}
|
|
469
609
|
}
|
|
470
610
|
|
|
471
611
|
/**
|
|
@@ -475,6 +615,7 @@ async function removeServerFromAllPlatforms(serverId) {
|
|
|
475
615
|
await removeServerFromPlatform(serverId, 'claude');
|
|
476
616
|
await removeServerFromPlatform(serverId, 'codex');
|
|
477
617
|
await removeServerFromPlatform(serverId, 'gemini');
|
|
618
|
+
await removeServerFromPlatform(serverId, 'opencode');
|
|
478
619
|
}
|
|
479
620
|
|
|
480
621
|
/**
|
|
@@ -492,6 +633,9 @@ async function syncServerToPlatform(server, platform) {
|
|
|
492
633
|
case 'gemini':
|
|
493
634
|
syncToGeminiConfig(server);
|
|
494
635
|
break;
|
|
636
|
+
case 'opencode':
|
|
637
|
+
syncToOpenCodeConfig(server);
|
|
638
|
+
break;
|
|
495
639
|
}
|
|
496
640
|
console.log(`[MCP] Synced "${server.id}" to ${platform}`);
|
|
497
641
|
} catch (err) {
|
|
@@ -515,6 +659,9 @@ async function removeServerFromPlatform(serverId, platform) {
|
|
|
515
659
|
case 'gemini':
|
|
516
660
|
removeFromGeminiConfig(serverId);
|
|
517
661
|
break;
|
|
662
|
+
case 'opencode':
|
|
663
|
+
removeFromOpenCodeConfig(serverId);
|
|
664
|
+
break;
|
|
518
665
|
}
|
|
519
666
|
console.log(`[MCP] Removed "${serverId}" from ${platform}`);
|
|
520
667
|
} catch (err) {
|
|
@@ -647,6 +794,143 @@ function removeFromGeminiConfig(serverId) {
|
|
|
647
794
|
}
|
|
648
795
|
}
|
|
649
796
|
|
|
797
|
+
// ============================================================================
|
|
798
|
+
// OpenCode 配置同步
|
|
799
|
+
// ============================================================================
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* 转换为 OpenCode 配置格式
|
|
803
|
+
*/
|
|
804
|
+
function convertToOpenCodeFormat(spec) {
|
|
805
|
+
const sourceType = spec.type || 'stdio';
|
|
806
|
+
|
|
807
|
+
if (sourceType === 'local' || sourceType === 'remote') {
|
|
808
|
+
const result = { ...spec };
|
|
809
|
+
result.enabled = spec.enabled !== false;
|
|
810
|
+
if (sourceType === 'local' && typeof result.command === 'string') {
|
|
811
|
+
result.command = result.command ? [result.command] : [];
|
|
812
|
+
}
|
|
813
|
+
return result;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
if (sourceType === 'stdio') {
|
|
817
|
+
const command = [];
|
|
818
|
+
if (spec.command) {
|
|
819
|
+
command.push(spec.command);
|
|
820
|
+
}
|
|
821
|
+
if (Array.isArray(spec.args) && spec.args.length > 0) {
|
|
822
|
+
command.push(...spec.args);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const result = {
|
|
826
|
+
type: 'local',
|
|
827
|
+
command,
|
|
828
|
+
enabled: true
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
if (spec.env && Object.keys(spec.env).length > 0) {
|
|
832
|
+
result.environment = spec.env;
|
|
833
|
+
}
|
|
834
|
+
if (spec.cwd) {
|
|
835
|
+
result.cwd = spec.cwd;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
return result;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
const result = {
|
|
842
|
+
type: 'remote',
|
|
843
|
+
url: spec.url || '',
|
|
844
|
+
enabled: true
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
if (spec.headers && Object.keys(spec.headers).length > 0) {
|
|
848
|
+
result.headers = spec.headers;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
return result;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* 从 OpenCode 格式转换到通用格式
|
|
856
|
+
*/
|
|
857
|
+
function convertFromOpenCodeFormat(spec) {
|
|
858
|
+
const sourceType = spec.type || (Array.isArray(spec.command) ? 'local' : 'remote');
|
|
859
|
+
|
|
860
|
+
if (sourceType === 'local') {
|
|
861
|
+
const result = { type: 'stdio' };
|
|
862
|
+
if (Array.isArray(spec.command) && spec.command.length > 0) {
|
|
863
|
+
result.command = spec.command[0];
|
|
864
|
+
if (spec.command.length > 1) {
|
|
865
|
+
result.args = spec.command.slice(1);
|
|
866
|
+
}
|
|
867
|
+
} else if (typeof spec.command === 'string') {
|
|
868
|
+
result.command = spec.command;
|
|
869
|
+
} else {
|
|
870
|
+
result.command = '';
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
if (spec.environment && typeof spec.environment === 'object') {
|
|
874
|
+
result.env = spec.environment;
|
|
875
|
+
} else if (spec.env && typeof spec.env === 'object') {
|
|
876
|
+
result.env = spec.env;
|
|
877
|
+
}
|
|
878
|
+
if (spec.cwd) {
|
|
879
|
+
result.cwd = spec.cwd;
|
|
880
|
+
}
|
|
881
|
+
return result;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (sourceType === 'remote') {
|
|
885
|
+
const result = {
|
|
886
|
+
type: 'http',
|
|
887
|
+
url: spec.url || ''
|
|
888
|
+
};
|
|
889
|
+
if (spec.headers && typeof spec.headers === 'object') {
|
|
890
|
+
result.headers = spec.headers;
|
|
891
|
+
}
|
|
892
|
+
return result;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// 已经是通用格式时直接兼容处理
|
|
896
|
+
if (sourceType === 'stdio' || sourceType === 'http' || sourceType === 'sse') {
|
|
897
|
+
return convertFromCodexFormat(spec);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
return {
|
|
901
|
+
type: 'stdio',
|
|
902
|
+
command: ''
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* 同步到 OpenCode 配置
|
|
908
|
+
*/
|
|
909
|
+
function syncToOpenCodeConfig(server) {
|
|
910
|
+
const { path: configPath, config } = readOpenCodeConfig();
|
|
911
|
+
const nextConfig = config && typeof config === 'object' ? config : {};
|
|
912
|
+
|
|
913
|
+
if (!nextConfig.mcp || typeof nextConfig.mcp !== 'object') {
|
|
914
|
+
nextConfig.mcp = {};
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
nextConfig.mcp[server.id] = convertToOpenCodeFormat(server.server);
|
|
918
|
+
writeOpenCodeConfig(configPath, nextConfig);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/**
|
|
922
|
+
* 从 OpenCode 配置移除
|
|
923
|
+
*/
|
|
924
|
+
function removeFromOpenCodeConfig(serverId) {
|
|
925
|
+
const { path: configPath, config } = readOpenCodeConfig();
|
|
926
|
+
const nextConfig = config && typeof config === 'object' ? config : {};
|
|
927
|
+
|
|
928
|
+
if (nextConfig.mcp && nextConfig.mcp[serverId]) {
|
|
929
|
+
delete nextConfig.mcp[serverId];
|
|
930
|
+
writeOpenCodeConfig(configPath, nextConfig);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
|
|
650
934
|
// ============================================================================
|
|
651
935
|
// 导入功能
|
|
652
936
|
// ============================================================================
|
|
@@ -668,6 +952,9 @@ async function importFromPlatform(platform) {
|
|
|
668
952
|
case 'gemini':
|
|
669
953
|
importedCount = importFromGemini(servers);
|
|
670
954
|
break;
|
|
955
|
+
case 'opencode':
|
|
956
|
+
importedCount = importFromOpenCode(servers);
|
|
957
|
+
break;
|
|
671
958
|
default:
|
|
672
959
|
throw new Error(`无效的平台: ${platform}`);
|
|
673
960
|
}
|
|
@@ -700,7 +987,7 @@ function importFromClaude(servers) {
|
|
|
700
987
|
id,
|
|
701
988
|
name: id,
|
|
702
989
|
server: spec,
|
|
703
|
-
apps: { claude: true, codex: false, gemini: false },
|
|
990
|
+
apps: { claude: true, codex: false, gemini: false, opencode: false },
|
|
704
991
|
createdAt: Date.now(),
|
|
705
992
|
updatedAt: Date.now()
|
|
706
993
|
};
|
|
@@ -735,7 +1022,7 @@ function importFromCodex(servers) {
|
|
|
735
1022
|
id,
|
|
736
1023
|
name: id,
|
|
737
1024
|
server: convertedSpec,
|
|
738
|
-
apps: { claude: false, codex: true, gemini: false },
|
|
1025
|
+
apps: { claude: false, codex: true, gemini: false, opencode: false },
|
|
739
1026
|
createdAt: Date.now(),
|
|
740
1027
|
updatedAt: Date.now()
|
|
741
1028
|
};
|
|
@@ -767,7 +1054,39 @@ function importFromGemini(servers) {
|
|
|
767
1054
|
id,
|
|
768
1055
|
name: id,
|
|
769
1056
|
server: spec,
|
|
770
|
-
apps: { claude: false, codex: false, gemini: true },
|
|
1057
|
+
apps: { claude: false, codex: false, gemini: true, opencode: false },
|
|
1058
|
+
createdAt: Date.now(),
|
|
1059
|
+
updatedAt: Date.now()
|
|
1060
|
+
};
|
|
1061
|
+
count++;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
return count;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* 从 OpenCode 导入
|
|
1070
|
+
*/
|
|
1071
|
+
function importFromOpenCode(servers) {
|
|
1072
|
+
const { config } = readOpenCodeConfig();
|
|
1073
|
+
const mcpServers = config.mcp || {};
|
|
1074
|
+
let count = 0;
|
|
1075
|
+
|
|
1076
|
+
for (const [id, spec] of Object.entries(mcpServers)) {
|
|
1077
|
+
const convertedSpec = convertFromOpenCodeFormat(spec || {});
|
|
1078
|
+
|
|
1079
|
+
if (servers[id]) {
|
|
1080
|
+
if (!servers[id].apps.opencode) {
|
|
1081
|
+
servers[id].apps.opencode = true;
|
|
1082
|
+
count++;
|
|
1083
|
+
}
|
|
1084
|
+
} else {
|
|
1085
|
+
servers[id] = {
|
|
1086
|
+
id,
|
|
1087
|
+
name: id,
|
|
1088
|
+
server: convertedSpec,
|
|
1089
|
+
apps: { claude: false, codex: false, gemini: false, opencode: true },
|
|
771
1090
|
createdAt: Date.now(),
|
|
772
1091
|
updatedAt: Date.now()
|
|
773
1092
|
};
|
|
@@ -838,7 +1157,8 @@ function getStats() {
|
|
|
838
1157
|
total: serverList.length,
|
|
839
1158
|
claude: serverList.filter(s => s.apps?.claude).length,
|
|
840
1159
|
codex: serverList.filter(s => s.apps?.codex).length,
|
|
841
|
-
gemini: serverList.filter(s => s.apps?.gemini).length
|
|
1160
|
+
gemini: serverList.filter(s => s.apps?.gemini).length,
|
|
1161
|
+
opencode: serverList.filter(s => s.apps?.opencode).length
|
|
842
1162
|
};
|
|
843
1163
|
}
|
|
844
1164
|
|
|
@@ -1300,7 +1620,7 @@ function updateServerOrder(serverIds) {
|
|
|
1300
1620
|
|
|
1301
1621
|
/**
|
|
1302
1622
|
* 导出所有 MCP 配置
|
|
1303
|
-
* @param {string} format - 导出格式: 'json' | 'claude' | 'codex'
|
|
1623
|
+
* @param {string} format - 导出格式: 'json' | 'claude' | 'codex' | 'opencode'
|
|
1304
1624
|
*/
|
|
1305
1625
|
function exportServers(format = 'json') {
|
|
1306
1626
|
const servers = getAllServers();
|
|
@@ -1310,6 +1630,8 @@ function exportServers(format = 'json') {
|
|
|
1310
1630
|
return exportForClaude(servers);
|
|
1311
1631
|
case 'codex':
|
|
1312
1632
|
return exportForCodex(servers);
|
|
1633
|
+
case 'opencode':
|
|
1634
|
+
return exportForOpenCode(servers);
|
|
1313
1635
|
case 'json':
|
|
1314
1636
|
default:
|
|
1315
1637
|
return exportAsJson(servers);
|
|
@@ -1371,6 +1693,25 @@ function exportForCodex(servers) {
|
|
|
1371
1693
|
};
|
|
1372
1694
|
}
|
|
1373
1695
|
|
|
1696
|
+
/**
|
|
1697
|
+
* 导出为 OpenCode 格式
|
|
1698
|
+
*/
|
|
1699
|
+
function exportForOpenCode(servers) {
|
|
1700
|
+
const mcp = {};
|
|
1701
|
+
|
|
1702
|
+
for (const [id, server] of Object.entries(servers)) {
|
|
1703
|
+
if (server.apps?.opencode) {
|
|
1704
|
+
mcp[id] = convertToOpenCodeFormat(server.server);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
return {
|
|
1709
|
+
format: 'opencode',
|
|
1710
|
+
content: JSON.stringify({ mcp }, null, 2),
|
|
1711
|
+
filename: 'opencode-mcp-config.json'
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1374
1715
|
module.exports = {
|
|
1375
1716
|
getAllServers,
|
|
1376
1717
|
getServer,
|