coding-tool-x 3.6.13 → 3.7.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/README.md +45 -33
- package/dist/web/assets/{Analytics-CnA75GxO.js → Analytics-Dpje9L84.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-D8XEscQY.js → ConfigTemplates-Cz6kasek.js} +1 -1
- package/dist/web/assets/Home-BwTaYu3q.js +1 -0
- package/dist/web/assets/Home-Jom_ZcdY.css +1 -0
- package/dist/web/assets/{PluginManager-BzKZ8TYu.js → PluginManager-DsN-xNXx.js} +1 -1
- package/dist/web/assets/ProjectList-4BCObimU.js +1 -0
- package/dist/web/assets/{ProjectList-DL4JK6ci.css → ProjectList-D3a0oRum.css} +1 -1
- package/dist/web/assets/SessionList-D4c8FRzq.js +1 -0
- package/dist/web/assets/{SkillManager-fxOvFTYD.js → SkillManager-C-KYbXB5.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-Ch2mM8jm.js → WorkspaceManager-B-BlPJHX.js} +1 -1
- package/dist/web/assets/{icons-QFXzEgqM.js → icons-Dop5j7Bf.js} +1 -1
- package/dist/web/assets/index-Btfvh0GG.js +2 -0
- package/dist/web/assets/index-CLNa6DhH.css +1 -0
- package/dist/web/assets/{naive-ui-kcWDeJwl.js → naive-ui-CVyvdmg1.js} +1 -1
- package/dist/web/index.html +4 -4
- package/package.json +3 -1
- package/src/commands/channels.js +85 -24
- package/src/commands/daemon.js +7 -4
- package/src/commands/doctor.js +17 -8
- package/src/commands/logs.js +46 -6
- package/src/commands/port-config.js +83 -75
- package/src/commands/proxy-control.js +59 -26
- package/src/commands/stats.js +37 -19
- package/src/commands/switch.js +5 -1
- package/src/commands/toggle-proxy.js +131 -53
- package/src/commands/ui.js +3 -4
- package/src/commands/workspace.js +3 -35
- package/src/config/default.js +19 -2
- package/src/config/loader.js +5 -76
- package/src/config/paths.js +96 -11
- package/src/index.js +10 -16
- package/src/server/api/commands.js +1 -1
- package/src/server/api/config-export.js +4 -2
- package/src/server/api/config-registry.js +40 -34
- package/src/server/api/config.js +5 -1
- package/src/server/api/dashboard.js +22 -6
- package/src/server/api/mcp.js +5 -3
- package/src/server/api/omp-channels.js +349 -0
- package/src/server/api/omp-config.js +164 -0
- package/src/server/api/omp-projects.js +76 -0
- package/src/server/api/omp-proxy.js +154 -0
- package/src/server/api/omp-sessions.js +334 -0
- package/src/server/api/omp-statistics.js +40 -0
- package/src/server/api/plugins.js +1 -1
- package/src/server/api/prompts.js +3 -2
- package/src/server/api/settings.js +2 -0
- package/src/server/api/skills.js +1 -1
- package/src/server/api/workspaces.js +3 -41
- package/src/server/index.js +41 -0
- package/src/server/omp-proxy-server.js +66 -0
- package/src/server/services/agents-service.js +2 -1
- package/src/server/services/base/proxy-utils.js +1 -0
- package/src/server/services/channel-balance.js +151 -1
- package/src/server/services/channel-scheduler.js +7 -1
- package/src/server/services/commands-service.js +11 -2
- package/src/server/services/config-export-service.js +296 -39
- package/src/server/services/config-registry-service.js +20 -18
- package/src/server/services/config-sync-manager.js +115 -6
- package/src/server/services/config-sync-service.js +4 -2
- package/src/server/services/config-templates-service.js +183 -45
- package/src/server/services/mcp-service.js +217 -33
- package/src/server/services/notification-hooks.js +138 -6
- package/src/server/services/omp-auth-providers.js +294 -0
- package/src/server/services/omp-channels.js +94 -0
- package/src/server/services/omp-config.js +269 -0
- package/src/server/services/omp-sessions.js +490 -0
- package/src/server/services/omp-settings-manager.js +975 -0
- package/src/server/services/omp-statistics-service.js +317 -0
- package/src/server/services/plugins-service.js +673 -11
- package/src/server/services/prompts-service.js +112 -7
- package/src/server/services/proxy-log-helper.js +1 -0
- package/src/server/services/proxy-runtime.js +5 -1
- package/src/server/services/sessions.js +19 -35
- package/src/server/services/skill-service.js +13 -3
- package/src/server/services/speed-test.js +34 -28
- package/src/server/services/ui-config.js +50 -12
- package/src/server/services/workspace-service.js +117 -225
- package/src/server/websocket-server.js +7 -1
- package/src/shared/platforms.js +201 -0
- package/src/utils/session.js +4 -10
- package/dist/web/assets/Home-BiaKZMah.js +0 -1
- package/dist/web/assets/Home-CIua-b-o.css +0 -1
- package/dist/web/assets/ProjectList-BPfG_92X.js +0 -1
- package/dist/web/assets/SessionList-BIVtw00v.js +0 -1
- package/dist/web/assets/index-BpBFF3b2.js +0 -2
- package/dist/web/assets/index-Dsh4Q5Cg.css +0 -1
- package/src/utils/cli-flags.js +0 -7
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* - Codex CLI: ~/.codex/skills/, ~/.codex/prompts/
|
|
7
7
|
* - Gemini CLI: ~/.gemini/{skills,commands,agents}/
|
|
8
8
|
* - OpenCode CLI: ~/.config/opencode/{skills,commands,agents,plugins}/
|
|
9
|
+
* - OMP: ~/.omp/agent/{skills,commands,prompts,extensions}/
|
|
9
10
|
*
|
|
10
11
|
* Config types:
|
|
11
12
|
* - skills: directory-based (each skill is a dir with SKILL.md)
|
|
@@ -25,10 +26,11 @@ const { PATHS, NATIVE_PATHS, HOME_DIR, ensureStorageDirMigrated } = require('../
|
|
|
25
26
|
// Paths
|
|
26
27
|
const HOME = HOME_DIR || os.homedir();
|
|
27
28
|
const CC_TOOL_CONFIGS = PATHS.configs;
|
|
28
|
-
const CLAUDE_CODE_DIR = path.join(HOME, '.claude');
|
|
29
|
+
const CLAUDE_CODE_DIR = NATIVE_PATHS.claude.dir || path.dirname(NATIVE_PATHS.claude.settings) || path.join(HOME, '.claude');
|
|
29
30
|
const CODEX_DIR = path.join(HOME, '.codex');
|
|
30
31
|
const GEMINI_DIR = path.join(HOME, '.gemini');
|
|
31
32
|
const OPENCODE_DIR = NATIVE_PATHS.opencode.config;
|
|
33
|
+
const OMP_AGENT_DIR = NATIVE_PATHS.omp.dir;
|
|
32
34
|
const CODEX_CONFIG_PATH = NATIVE_PATHS.codex.config;
|
|
33
35
|
|
|
34
36
|
// Config type definitions
|
|
@@ -44,7 +46,9 @@ const CONFIG_TYPES = {
|
|
|
44
46
|
geminiSupported: true,
|
|
45
47
|
opencodeTarget: 'skills',
|
|
46
48
|
opencodeLegacyTarget: 'skill',
|
|
47
|
-
opencodeSupported: true
|
|
49
|
+
opencodeSupported: true,
|
|
50
|
+
ompTarget: 'skills',
|
|
51
|
+
ompSupported: true
|
|
48
52
|
},
|
|
49
53
|
commands: {
|
|
50
54
|
isDirectory: false,
|
|
@@ -58,7 +62,9 @@ const CONFIG_TYPES = {
|
|
|
58
62
|
geminiSupported: true,
|
|
59
63
|
opencodeTarget: 'commands',
|
|
60
64
|
opencodeLegacyTarget: 'command',
|
|
61
|
-
opencodeSupported: true
|
|
65
|
+
opencodeSupported: true,
|
|
66
|
+
ompTarget: 'commands',
|
|
67
|
+
ompSupported: true
|
|
62
68
|
},
|
|
63
69
|
agents: {
|
|
64
70
|
isDirectory: false,
|
|
@@ -69,7 +75,8 @@ const CONFIG_TYPES = {
|
|
|
69
75
|
geminiSupported: true,
|
|
70
76
|
opencodeTarget: 'agents',
|
|
71
77
|
opencodeLegacyTarget: 'agent',
|
|
72
|
-
opencodeSupported: true
|
|
78
|
+
opencodeSupported: true,
|
|
79
|
+
ompSupported: false
|
|
73
80
|
},
|
|
74
81
|
plugins: {
|
|
75
82
|
isDirectory: true,
|
|
@@ -78,7 +85,9 @@ const CONFIG_TYPES = {
|
|
|
78
85
|
geminiSupported: false,
|
|
79
86
|
opencodeTarget: 'plugins',
|
|
80
87
|
opencodeLegacyTarget: 'plugin',
|
|
81
|
-
opencodeSupported: true
|
|
88
|
+
opencodeSupported: true,
|
|
89
|
+
ompTarget: 'extensions',
|
|
90
|
+
ompSupported: true
|
|
82
91
|
}
|
|
83
92
|
};
|
|
84
93
|
|
|
@@ -90,6 +99,7 @@ class ConfigSyncManager {
|
|
|
90
99
|
this.codexDir = CODEX_DIR;
|
|
91
100
|
this.geminiDir = GEMINI_DIR;
|
|
92
101
|
this.opencodeDir = OPENCODE_DIR;
|
|
102
|
+
this.ompDir = OMP_AGENT_DIR;
|
|
93
103
|
this.configTypes = CONFIG_TYPES;
|
|
94
104
|
}
|
|
95
105
|
|
|
@@ -574,10 +584,90 @@ class ConfigSyncManager {
|
|
|
574
584
|
}
|
|
575
585
|
}
|
|
576
586
|
|
|
587
|
+
/**
|
|
588
|
+
* Sync a config item to OMP.
|
|
589
|
+
* OMP treats commands as slash-command files and plugins as extensions/packages.
|
|
590
|
+
*/
|
|
591
|
+
syncToOmp(type, name) {
|
|
592
|
+
const config = this.configTypes[type];
|
|
593
|
+
if (!config) {
|
|
594
|
+
return { success: false, error: `Unknown config type: ${type}` };
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if (!config.ompSupported) {
|
|
598
|
+
console.log(`[ConfigSyncManager] ${type} not supported natively by OMP, skipping`);
|
|
599
|
+
return { success: true, skipped: true, reason: 'Not supported natively by OMP' };
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const safeName = this._normalizeSafeRelativeName(name);
|
|
603
|
+
if (!safeName) {
|
|
604
|
+
return { success: false, error: 'Invalid config item name' };
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const sourcePath = path.join(this.ccToolConfigs, type, safeName);
|
|
608
|
+
if (!fs.existsSync(sourcePath)) {
|
|
609
|
+
console.log(`[ConfigSyncManager] Source not found: ${sourcePath}`);
|
|
610
|
+
return { success: false, error: 'Source not found' };
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const targetPath = path.join(this.ompDir, config.ompTarget, safeName);
|
|
614
|
+
try {
|
|
615
|
+
if (config.isDirectory) {
|
|
616
|
+
this._ensureDir(path.dirname(targetPath));
|
|
617
|
+
this._copyDirRecursive(sourcePath, targetPath);
|
|
618
|
+
console.log(`[ConfigSyncManager] Synced ${type}/${name} to OMP (directory)`);
|
|
619
|
+
} else {
|
|
620
|
+
this._ensureDir(path.dirname(targetPath));
|
|
621
|
+
this._copyFile(sourcePath, targetPath);
|
|
622
|
+
console.log(`[ConfigSyncManager] Synced ${type}/${name} to OMP (file)`);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
return { success: true, target: targetPath };
|
|
626
|
+
} catch (err) {
|
|
627
|
+
console.error('[ConfigSyncManager] Sync to OMP failed:', err.message);
|
|
628
|
+
return { success: false, error: err.message };
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
removeFromOmp(type, name) {
|
|
633
|
+
const config = this.configTypes[type];
|
|
634
|
+
if (!config) {
|
|
635
|
+
return { success: false, error: `Unknown config type: ${type}` };
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const safeName = this._normalizeSafeRelativeName(name);
|
|
639
|
+
if (!safeName) {
|
|
640
|
+
return { success: false, error: 'Invalid config item name' };
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
if (!config.ompSupported) {
|
|
644
|
+
return { success: true, skipped: true, reason: 'Not supported natively by OMP' };
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const targetPath = path.join(this.ompDir, config.ompTarget, safeName);
|
|
648
|
+
if (!fs.existsSync(targetPath)) {
|
|
649
|
+
return { success: true, message: 'Already removed' };
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
try {
|
|
653
|
+
if (config.isDirectory) {
|
|
654
|
+
this._removeRecursive(targetPath);
|
|
655
|
+
} else {
|
|
656
|
+
fs.unlinkSync(targetPath);
|
|
657
|
+
this._cleanupEmptyParents(path.dirname(targetPath), path.join(this.ompDir, config.ompTarget));
|
|
658
|
+
}
|
|
659
|
+
console.log(`[ConfigSyncManager] Removed ${type}/${name} from OMP`);
|
|
660
|
+
return { success: true };
|
|
661
|
+
} catch (err) {
|
|
662
|
+
console.error('[ConfigSyncManager] Remove from OMP failed:', err.message);
|
|
663
|
+
return { success: false, error: err.message };
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
577
667
|
/**
|
|
578
668
|
* Batch sync based on registry data
|
|
579
669
|
* @param {string} type - Config type
|
|
580
|
-
* @param {Object} registryItems - Registry items { name: { enabled, platforms: { claude, codex, gemini, opencode } } }
|
|
670
|
+
* @param {Object} registryItems - Registry items { name: { enabled, platforms: { claude, codex, gemini, opencode, omp } } }
|
|
581
671
|
* @returns {Object} Results summary
|
|
582
672
|
*/
|
|
583
673
|
syncAll(type, registryItems) {
|
|
@@ -662,6 +752,20 @@ class ConfigSyncManager {
|
|
|
662
752
|
results.removed.push({ type, name, platform: 'opencode' });
|
|
663
753
|
}
|
|
664
754
|
}
|
|
755
|
+
|
|
756
|
+
if (platforms.omp) {
|
|
757
|
+
const result = this.syncToOmp(type, name);
|
|
758
|
+
if (result.success && !result.skipped) {
|
|
759
|
+
results.synced.push({ type, name, platform: 'omp' });
|
|
760
|
+
} else if (!result.success) {
|
|
761
|
+
results.errors.push({ type, name, platform: 'omp', error: result.error });
|
|
762
|
+
}
|
|
763
|
+
} else {
|
|
764
|
+
const result = this.removeFromOmp(type, name);
|
|
765
|
+
if (result.success && !result.message && !result.skipped) {
|
|
766
|
+
results.removed.push({ type, name, platform: 'omp' });
|
|
767
|
+
}
|
|
768
|
+
}
|
|
665
769
|
} else {
|
|
666
770
|
// Item disabled, remove from all platforms
|
|
667
771
|
const claudeResult = this.removeFromClaude(type, name);
|
|
@@ -683,6 +787,11 @@ class ConfigSyncManager {
|
|
|
683
787
|
if (opencodeResult.success && !opencodeResult.message && !opencodeResult.skipped) {
|
|
684
788
|
results.removed.push({ type, name, platform: 'opencode' });
|
|
685
789
|
}
|
|
790
|
+
|
|
791
|
+
const ompResult = this.removeFromOmp(type, name);
|
|
792
|
+
if (ompResult.success && !ompResult.message && !ompResult.skipped) {
|
|
793
|
+
results.removed.push({ type, name, platform: 'omp' });
|
|
794
|
+
}
|
|
686
795
|
}
|
|
687
796
|
}
|
|
688
797
|
|
|
@@ -15,10 +15,12 @@
|
|
|
15
15
|
|
|
16
16
|
const fs = require('fs');
|
|
17
17
|
const path = require('path');
|
|
18
|
-
const { HOME_DIR } = require('../../config/paths');
|
|
18
|
+
const { HOME_DIR, NATIVE_PATHS } = require('../../config/paths');
|
|
19
19
|
|
|
20
20
|
// 全局配置目录
|
|
21
|
-
const GLOBAL_CONFIG_DIR =
|
|
21
|
+
const GLOBAL_CONFIG_DIR = NATIVE_PATHS.claude.dir
|
|
22
|
+
|| path.dirname(NATIVE_PATHS.claude.settings)
|
|
23
|
+
|| path.join(HOME_DIR, '.claude');
|
|
22
24
|
|
|
23
25
|
// 配置类型定义
|
|
24
26
|
const CONFIG_TYPES = {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
|
+
const yaml = require('js-yaml');
|
|
10
11
|
const { PATHS } = require('../../config/paths');
|
|
11
12
|
const { AgentsService } = require('./agents-service');
|
|
12
13
|
const { CommandsService } = require('./commands-service');
|
|
@@ -23,14 +24,16 @@ const AI_CONFIG_MAP = {
|
|
|
23
24
|
claude: { fileName: 'CLAUDE.md', name: 'Claude' },
|
|
24
25
|
codex: { fileName: 'AGENTS.md', name: 'Codex' },
|
|
25
26
|
gemini: { fileName: 'GEMINI.md', name: 'Gemini' },
|
|
26
|
-
opencode: { fileName: '.opencode/AGENTS.md', name: 'OpenCode' }
|
|
27
|
+
opencode: { fileName: '.opencode/AGENTS.md', name: 'OpenCode' },
|
|
28
|
+
omp: { fileName: null, name: 'OMP command templates' }
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
const CLI_DEFAULT_AI_TYPE = {
|
|
30
32
|
claude: 'claude',
|
|
31
33
|
codex: 'codex',
|
|
32
34
|
gemini: 'gemini',
|
|
33
|
-
opencode: 'opencode'
|
|
35
|
+
opencode: 'opencode',
|
|
36
|
+
omp: 'omp'
|
|
34
37
|
};
|
|
35
38
|
|
|
36
39
|
/**
|
|
@@ -111,7 +114,8 @@ function normalizeAiConfigs(aiConfigs = {}, claudeMd = null) {
|
|
|
111
114
|
claude: { enabled: false, content: '' },
|
|
112
115
|
codex: { enabled: false, content: '' },
|
|
113
116
|
gemini: { enabled: false, content: '' },
|
|
114
|
-
opencode: { enabled: false, content: '' }
|
|
117
|
+
opencode: { enabled: false, content: '' },
|
|
118
|
+
omp: { enabled: false, content: '' }
|
|
115
119
|
};
|
|
116
120
|
|
|
117
121
|
for (const key of Object.keys(normalized)) {
|
|
@@ -369,36 +373,62 @@ function readCurrentConfig(targetDir) {
|
|
|
369
373
|
* 返回用户级的 agents, commands, plugins + MCP 服务器列表
|
|
370
374
|
*/
|
|
371
375
|
function getAvailableConfigs() {
|
|
372
|
-
const
|
|
373
|
-
const
|
|
376
|
+
const agentPlatforms = ['claude', 'codex', 'gemini', 'opencode'];
|
|
377
|
+
const commandPlatforms = ['claude', 'codex', 'gemini', 'opencode', 'omp'];
|
|
374
378
|
const agentMap = new Map();
|
|
375
379
|
const commandMap = new Map();
|
|
380
|
+
const agentsByPlatform = {};
|
|
381
|
+
const commandsByPlatform = {};
|
|
376
382
|
|
|
377
|
-
for (const
|
|
383
|
+
for (const platform of agentPlatforms) {
|
|
384
|
+
const service = new AgentsService(platform);
|
|
378
385
|
const { agents } = service.listAgents();
|
|
379
386
|
for (const agent of agents || []) {
|
|
380
387
|
if (agent.scope !== 'user') continue;
|
|
388
|
+
const normalizedAgent = {
|
|
389
|
+
fileName: agent.fileName,
|
|
390
|
+
name: agent.name,
|
|
391
|
+
description: agent.description,
|
|
392
|
+
tools: agent.tools,
|
|
393
|
+
model: agent.model,
|
|
394
|
+
permissionMode: agent.permissionMode,
|
|
395
|
+
skills: agent.skills,
|
|
396
|
+
systemPrompt: agent.systemPrompt
|
|
397
|
+
};
|
|
398
|
+
agentsByPlatform[platform] = agentsByPlatform[platform] || [];
|
|
399
|
+
agentsByPlatform[platform].push(normalizedAgent);
|
|
381
400
|
const key = `${agent.fileName || agent.name}|${agent.model || ''}|${agent.description || ''}`;
|
|
382
401
|
if (!agentMap.has(key)) {
|
|
383
|
-
agentMap.set(key,
|
|
402
|
+
agentMap.set(key, normalizedAgent);
|
|
384
403
|
}
|
|
385
404
|
}
|
|
386
405
|
}
|
|
387
406
|
|
|
388
|
-
for (const
|
|
407
|
+
for (const platform of commandPlatforms) {
|
|
408
|
+
const service = new CommandsService(platform);
|
|
389
409
|
const { commands } = service.listCommands();
|
|
390
410
|
for (const command of commands || []) {
|
|
391
411
|
if (command.scope !== 'user') continue;
|
|
412
|
+
const normalizedCommand = {
|
|
413
|
+
name: command.name,
|
|
414
|
+
namespace: command.namespace,
|
|
415
|
+
description: command.description,
|
|
416
|
+
allowedTools: command.allowedTools,
|
|
417
|
+
argumentHint: command.argumentHint,
|
|
418
|
+
body: command.body
|
|
419
|
+
};
|
|
420
|
+
commandsByPlatform[platform] = commandsByPlatform[platform] || [];
|
|
421
|
+
commandsByPlatform[platform].push(normalizedCommand);
|
|
392
422
|
const key = command.namespace ? `${command.namespace}/${command.name}` : command.name;
|
|
393
423
|
if (!commandMap.has(key)) {
|
|
394
|
-
commandMap.set(key,
|
|
424
|
+
commandMap.set(key, normalizedCommand);
|
|
395
425
|
}
|
|
396
426
|
}
|
|
397
427
|
}
|
|
398
428
|
|
|
399
429
|
// 按平台分别获取 skills(每个平台有独立的安装目录)
|
|
400
430
|
const skillsByPlatform = {};
|
|
401
|
-
for (const platform of ['claude', 'codex', 'gemini', 'opencode']) {
|
|
431
|
+
for (const platform of ['claude', 'codex', 'gemini', 'opencode', 'omp']) {
|
|
402
432
|
const service = new SkillService(platform);
|
|
403
433
|
skillsByPlatform[platform] = service.getInstalledSkills().map(skill => ({
|
|
404
434
|
directory: skill.directory,
|
|
@@ -440,24 +470,10 @@ function getAvailableConfigs() {
|
|
|
440
470
|
|
|
441
471
|
return {
|
|
442
472
|
skillsByPlatform,
|
|
443
|
-
agents: Array.from(agentMap.values())
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
tools: a.tools,
|
|
448
|
-
model: a.model,
|
|
449
|
-
permissionMode: a.permissionMode,
|
|
450
|
-
skills: a.skills,
|
|
451
|
-
systemPrompt: a.systemPrompt
|
|
452
|
-
})),
|
|
453
|
-
commands: Array.from(commandMap.values()).map(c => ({
|
|
454
|
-
name: c.name,
|
|
455
|
-
namespace: c.namespace,
|
|
456
|
-
description: c.description,
|
|
457
|
-
allowedTools: c.allowedTools,
|
|
458
|
-
argumentHint: c.argumentHint,
|
|
459
|
-
body: c.body
|
|
460
|
-
})),
|
|
473
|
+
agents: Array.from(agentMap.values()),
|
|
474
|
+
agentsByPlatform,
|
|
475
|
+
commands: Array.from(commandMap.values()),
|
|
476
|
+
commandsByPlatform,
|
|
461
477
|
plugins: installedPlugins.map(p => ({
|
|
462
478
|
name: p.name,
|
|
463
479
|
description: p.description || '',
|
|
@@ -503,6 +519,48 @@ function buildTemplateCommandFileName(commandName, format) {
|
|
|
503
519
|
return `${commandName}.${format === 'gemini' ? 'toml' : 'md'}`;
|
|
504
520
|
}
|
|
505
521
|
|
|
522
|
+
function getCommandPreviewExtension(prefix) {
|
|
523
|
+
return prefix === '.gemini/commands' ? 'toml' : 'md';
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function writeJsonFile(filePath, data) {
|
|
527
|
+
ensureDir(path.dirname(filePath));
|
|
528
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function writeYamlFile(filePath, data) {
|
|
532
|
+
ensureDir(path.dirname(filePath));
|
|
533
|
+
fs.writeFileSync(filePath, yaml.dump(data || {}, {
|
|
534
|
+
lineWidth: 120,
|
|
535
|
+
noRefs: true,
|
|
536
|
+
sortKeys: false
|
|
537
|
+
}), 'utf-8');
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function mergeOmpProjectPackages(targetDir, plugins = []) {
|
|
541
|
+
const packages = plugins.map(plugin => plugin.name).filter(Boolean);
|
|
542
|
+
if (packages.length === 0) {
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const ompDir = path.join(targetDir, '.omp');
|
|
547
|
+
const settingsPath = path.join(ompDir, 'config.yml');
|
|
548
|
+
let settings = {};
|
|
549
|
+
if (fs.existsSync(settingsPath)) {
|
|
550
|
+
try {
|
|
551
|
+
const parsed = yaml.load(fs.readFileSync(settingsPath, 'utf-8'));
|
|
552
|
+
settings = parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
553
|
+
} catch (err) {
|
|
554
|
+
settings = {};
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const existingPackages = Array.isArray(settings.packages) ? settings.packages : [];
|
|
559
|
+
settings.packages = Array.from(new Set([...existingPackages, ...packages]));
|
|
560
|
+
writeYamlFile(settingsPath, settings);
|
|
561
|
+
return true;
|
|
562
|
+
}
|
|
563
|
+
|
|
506
564
|
/**
|
|
507
565
|
* 转换为 OpenCode MCP 结构(local/remote)
|
|
508
566
|
*/
|
|
@@ -542,12 +600,36 @@ function convertToOpenCodeMcpSpec(spec = {}) {
|
|
|
542
600
|
return result;
|
|
543
601
|
}
|
|
544
602
|
|
|
603
|
+
function convertToOmpMcpSpec(spec = {}) {
|
|
604
|
+
const type = spec.type || 'stdio';
|
|
605
|
+
const copyCommonFields = (target) => {
|
|
606
|
+
for (const key of ['timeout', 'auth', 'oauth']) {
|
|
607
|
+
if (spec[key] !== undefined) {
|
|
608
|
+
target[key] = spec[key];
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return target;
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
if (type === 'streamable_http') {
|
|
615
|
+
const result = {
|
|
616
|
+
type: 'http',
|
|
617
|
+
url: spec.url || ''
|
|
618
|
+
};
|
|
619
|
+
if (spec.headers && typeof spec.headers === 'object') {
|
|
620
|
+
result.headers = spec.headers;
|
|
621
|
+
}
|
|
622
|
+
return copyCommonFields(result);
|
|
623
|
+
}
|
|
624
|
+
return copyCommonFields({ ...spec });
|
|
625
|
+
}
|
|
626
|
+
|
|
545
627
|
/**
|
|
546
628
|
* 应用模板到项目目录(完整应用,写入实际文件)
|
|
547
629
|
* @param {string} targetDir - 目标项目目录
|
|
548
630
|
* @param {string} templateId - 模板 ID
|
|
549
631
|
* @param {object} options - 可选配置
|
|
550
|
-
* @param {string|string[]} options.aiConfigTypes - 选择的 AI 配置类型数组: ['claude', 'codex', 'gemini', 'opencode']
|
|
632
|
+
* @param {string|string[]} options.aiConfigTypes - 选择的 AI 配置类型数组: ['claude', 'codex', 'gemini', 'opencode', 'omp']
|
|
551
633
|
* @param {string} options.aiConfigType - (兼容旧版) 单个 AI 配置类型
|
|
552
634
|
*/
|
|
553
635
|
function applyTemplateToProject(targetDir, templateId, options = {}) {
|
|
@@ -572,8 +654,10 @@ function applyTemplateToProject(targetDir, templateId, options = {}) {
|
|
|
572
654
|
|
|
573
655
|
for (const aiConfigType of aiConfigTypes) {
|
|
574
656
|
const aiConfig = resolveAiConfig(template, aiConfigType);
|
|
575
|
-
|
|
576
|
-
|
|
657
|
+
const configInfo = AI_CONFIG_MAP[aiConfigType];
|
|
658
|
+
if (!configInfo?.fileName) {
|
|
659
|
+
pushSkipped(results.skipped, 'aiConfig', configInfo?.name || aiConfigType, 'OMP 项目级命令模板通过 .omp/commands 写入,未生成单独 AI 配置文件');
|
|
660
|
+
} else if (aiConfig?.enabled && aiConfig?.content) {
|
|
577
661
|
const configPath = path.join(targetDir, configInfo.fileName);
|
|
578
662
|
ensureDir(path.dirname(configPath));
|
|
579
663
|
fs.writeFileSync(configPath, aiConfig.content, 'utf-8');
|
|
@@ -613,6 +697,9 @@ function applyTemplateToProject(targetDir, templateId, options = {}) {
|
|
|
613
697
|
if (aiConfigTypes.includes('codex')) {
|
|
614
698
|
pushSkipped(results.skipped, 'agent', fileName, 'Codex agents 仅支持用户级配置,项目目录应用时已跳过');
|
|
615
699
|
}
|
|
700
|
+
if (aiConfigTypes.includes('omp')) {
|
|
701
|
+
pushSkipped(results.skipped, 'agent', fileName, 'OMP agents 需通过扩展或包提供,项目目录应用时已跳过');
|
|
702
|
+
}
|
|
616
703
|
if (written) {
|
|
617
704
|
results.agents.applied++;
|
|
618
705
|
}
|
|
@@ -633,6 +720,9 @@ function applyTemplateToProject(targetDir, templateId, options = {}) {
|
|
|
633
720
|
if (aiConfigTypes.includes('gemini')) {
|
|
634
721
|
commandTargets.push({ baseDir: path.join(targetDir, '.gemini', 'commands'), prefix: '.gemini/commands', format: 'gemini' });
|
|
635
722
|
}
|
|
723
|
+
if (aiConfigTypes.includes('omp')) {
|
|
724
|
+
commandTargets.push({ baseDir: path.join(targetDir, '.omp', 'commands'), prefix: '.omp/commands', format: 'omp' });
|
|
725
|
+
}
|
|
636
726
|
|
|
637
727
|
for (const target of commandTargets) {
|
|
638
728
|
ensureDir(target.baseDir);
|
|
@@ -668,7 +758,7 @@ function applyTemplateToProject(targetDir, templateId, options = {}) {
|
|
|
668
758
|
|
|
669
759
|
if (template.plugins?.length > 0) {
|
|
670
760
|
results.plugins.items = template.plugins.map(p => p.name);
|
|
671
|
-
if (aiConfigTypes.includes('opencode')) {
|
|
761
|
+
if (aiConfigTypes.includes('opencode') || aiConfigTypes.includes('omp')) {
|
|
672
762
|
results.plugins.applied = template.plugins.length;
|
|
673
763
|
} else {
|
|
674
764
|
for (const plugin of template.plugins) {
|
|
@@ -678,9 +768,13 @@ function applyTemplateToProject(targetDir, templateId, options = {}) {
|
|
|
678
768
|
}
|
|
679
769
|
|
|
680
770
|
const hasMcp = template.mcpServers?.length > 0;
|
|
771
|
+
const writesGenericMcpConfig = hasMcp && aiConfigTypes.some(type => type !== 'omp');
|
|
772
|
+
const writesOmpMcpConfig = hasMcp && aiConfigTypes.includes('omp');
|
|
681
773
|
const hasPluginsForOpenCode = aiConfigTypes.includes('opencode') && template.plugins?.length > 0;
|
|
682
|
-
|
|
774
|
+
const hasPluginsForOmp = aiConfigTypes.includes('omp') && template.plugins?.length > 0;
|
|
775
|
+
if (hasMcp || hasPluginsForOpenCode || hasPluginsForOmp) {
|
|
683
776
|
const mcpConfig = { mcpServers: {} };
|
|
777
|
+
const ompMcpConfig = { mcpServers: {} };
|
|
684
778
|
const opencodeConfig = { mcp: {}, plugin: [] };
|
|
685
779
|
const allServers = mcpService.getAllServers();
|
|
686
780
|
const presets = mcpService.getPresets();
|
|
@@ -696,17 +790,29 @@ function applyTemplateToProject(targetDir, templateId, options = {}) {
|
|
|
696
790
|
}
|
|
697
791
|
}
|
|
698
792
|
if (serverSpec) {
|
|
699
|
-
|
|
793
|
+
if (writesGenericMcpConfig) {
|
|
794
|
+
mcpConfig.mcpServers[serverId] = serverSpec;
|
|
795
|
+
}
|
|
796
|
+
if (writesOmpMcpConfig) {
|
|
797
|
+
ompMcpConfig.mcpServers[serverId] = convertToOmpMcpSpec(serverSpec);
|
|
798
|
+
}
|
|
700
799
|
opencodeConfig.mcp[serverId] = convertToOpenCodeMcpSpec(serverSpec);
|
|
701
|
-
|
|
800
|
+
if (writesGenericMcpConfig || writesOmpMcpConfig) {
|
|
801
|
+
results.mcpServers.applied++;
|
|
802
|
+
}
|
|
702
803
|
} else {
|
|
703
804
|
pushSkipped(results.skipped, 'mcpServer', serverId, '未找到对应 MCP 服务配置,已跳过');
|
|
704
805
|
}
|
|
705
806
|
}
|
|
706
807
|
|
|
707
|
-
if (Object.keys(mcpConfig.mcpServers).length > 0) {
|
|
808
|
+
if (writesGenericMcpConfig && Object.keys(mcpConfig.mcpServers).length > 0) {
|
|
708
809
|
const mcpPath = path.join(targetDir, '.mcp.json');
|
|
709
|
-
|
|
810
|
+
writeJsonFile(mcpPath, mcpConfig);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
if (writesOmpMcpConfig && Object.keys(ompMcpConfig.mcpServers).length > 0) {
|
|
814
|
+
const ompMcpPath = path.join(targetDir, '.omp', 'mcp.json');
|
|
815
|
+
writeJsonFile(ompMcpPath, ompMcpConfig);
|
|
710
816
|
}
|
|
711
817
|
|
|
712
818
|
if (hasPluginsForOpenCode) {
|
|
@@ -716,7 +822,11 @@ function applyTemplateToProject(targetDir, templateId, options = {}) {
|
|
|
716
822
|
const opencodeDir = path.join(targetDir, '.opencode');
|
|
717
823
|
ensureDir(opencodeDir);
|
|
718
824
|
const opencodePath = path.join(opencodeDir, 'opencode.json');
|
|
719
|
-
|
|
825
|
+
writeJsonFile(opencodePath, opencodeConfig);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
if (hasPluginsForOmp) {
|
|
829
|
+
mergeOmpProjectPackages(targetDir, template.plugins || []);
|
|
720
830
|
}
|
|
721
831
|
}
|
|
722
832
|
|
|
@@ -748,7 +858,7 @@ function applyTemplateToProject(targetDir, templateId, options = {}) {
|
|
|
748
858
|
* @param {string} targetDir - 目标项目目录
|
|
749
859
|
* @param {string} templateId - 模板 ID
|
|
750
860
|
* @param {object} options - 可选配置
|
|
751
|
-
* @param {string|string[]} options.aiConfigTypes - 选择的 AI 配置类型数组: ['claude', 'codex', 'gemini', 'opencode']
|
|
861
|
+
* @param {string|string[]} options.aiConfigTypes - 选择的 AI 配置类型数组: ['claude', 'codex', 'gemini', 'opencode', 'omp']
|
|
752
862
|
* @param {string} options.aiConfigType - (兼容旧版) 单个 AI 配置类型
|
|
753
863
|
*/
|
|
754
864
|
function previewTemplateApplication(targetDir, templateId, options = {}) {
|
|
@@ -776,8 +886,10 @@ function previewTemplateApplication(targetDir, templateId, options = {}) {
|
|
|
776
886
|
|
|
777
887
|
for (const aiConfigType of aiConfigTypes) {
|
|
778
888
|
const aiConfig = resolveAiConfig(template, aiConfigType);
|
|
779
|
-
|
|
780
|
-
|
|
889
|
+
const configInfo = AI_CONFIG_MAP[aiConfigType];
|
|
890
|
+
if (!configInfo?.fileName) {
|
|
891
|
+
pushSkipped(preview.skipped, 'aiConfig', configInfo?.name || aiConfigType, 'OMP 项目级命令模板通过 .omp/commands 写入,预览不生成单独 AI 配置文件');
|
|
892
|
+
} else if (aiConfig?.enabled && aiConfig?.content) {
|
|
781
893
|
const configPath = path.join(targetDir, configInfo.fileName);
|
|
782
894
|
if (fs.existsSync(configPath)) {
|
|
783
895
|
preview.willOverwrite.push(configInfo.fileName);
|
|
@@ -818,6 +930,9 @@ function previewTemplateApplication(targetDir, templateId, options = {}) {
|
|
|
818
930
|
if (aiConfigTypes.includes('codex')) {
|
|
819
931
|
pushSkipped(preview.skipped, 'agent', fileName, 'Codex agents 仅支持用户级配置,项目目录预览时已跳过');
|
|
820
932
|
}
|
|
933
|
+
if (aiConfigTypes.includes('omp')) {
|
|
934
|
+
pushSkipped(preview.skipped, 'agent', fileName, 'OMP agents 需通过扩展或包提供,项目目录预览时已跳过');
|
|
935
|
+
}
|
|
821
936
|
if (applicable) {
|
|
822
937
|
preview.summary.agents++;
|
|
823
938
|
}
|
|
@@ -830,12 +945,13 @@ function previewTemplateApplication(targetDir, templateId, options = {}) {
|
|
|
830
945
|
if (aiConfigTypes.includes('codex')) commandPrefixes.push('.codex/prompts');
|
|
831
946
|
if (aiConfigTypes.includes('opencode')) commandPrefixes.push('.opencode/commands');
|
|
832
947
|
if (aiConfigTypes.includes('gemini')) commandPrefixes.push('.gemini/commands');
|
|
948
|
+
if (aiConfigTypes.includes('omp')) commandPrefixes.push('.omp/commands');
|
|
833
949
|
|
|
834
950
|
for (const command of template.commands) {
|
|
835
951
|
const commandName = resolveItemName(command.name, null, 'command');
|
|
836
952
|
let applicable = false;
|
|
837
953
|
for (const prefix of commandPrefixes) {
|
|
838
|
-
const extension = prefix
|
|
954
|
+
const extension = getCommandPreviewExtension(prefix);
|
|
839
955
|
const relativePath = command.namespace
|
|
840
956
|
? `${prefix}/${command.namespace}/${commandName}.${extension}`
|
|
841
957
|
: `${prefix}/${commandName}.${extension}`;
|
|
@@ -853,6 +969,8 @@ function previewTemplateApplication(targetDir, templateId, options = {}) {
|
|
|
853
969
|
}
|
|
854
970
|
}
|
|
855
971
|
|
|
972
|
+
const writesGenericMcpConfig = template.mcpServers?.length > 0 && aiConfigTypes.some(type => type !== 'omp');
|
|
973
|
+
const writesOmpMcpConfig = template.mcpServers?.length > 0 && aiConfigTypes.includes('omp');
|
|
856
974
|
const allServers = mcpService.getAllServers();
|
|
857
975
|
const presets = mcpService.getPresets();
|
|
858
976
|
let resolvableMcpCount = 0;
|
|
@@ -865,13 +983,15 @@ function previewTemplateApplication(targetDir, templateId, options = {}) {
|
|
|
865
983
|
}
|
|
866
984
|
}
|
|
867
985
|
if (serverSpec) {
|
|
868
|
-
|
|
986
|
+
if (writesGenericMcpConfig || writesOmpMcpConfig) {
|
|
987
|
+
resolvableMcpCount++;
|
|
988
|
+
}
|
|
869
989
|
} else {
|
|
870
990
|
pushSkipped(preview.skipped, 'mcpServer', serverId, '未找到对应 MCP 服务配置,预览已跳过');
|
|
871
991
|
}
|
|
872
992
|
}
|
|
873
993
|
|
|
874
|
-
if (resolvableMcpCount > 0) {
|
|
994
|
+
if (writesGenericMcpConfig && resolvableMcpCount > 0) {
|
|
875
995
|
const mcpPath = path.join(targetDir, '.mcp.json');
|
|
876
996
|
if (fs.existsSync(mcpPath)) {
|
|
877
997
|
preview.willOverwrite.push('.mcp.json');
|
|
@@ -881,6 +1001,16 @@ function previewTemplateApplication(targetDir, templateId, options = {}) {
|
|
|
881
1001
|
preview.summary.mcpServers = resolvableMcpCount;
|
|
882
1002
|
}
|
|
883
1003
|
|
|
1004
|
+
if (writesOmpMcpConfig && resolvableMcpCount > 0) {
|
|
1005
|
+
const ompMcpPath = path.join(targetDir, '.omp/mcp.json');
|
|
1006
|
+
if (fs.existsSync(ompMcpPath)) {
|
|
1007
|
+
preview.willOverwrite.push('.omp/mcp.json');
|
|
1008
|
+
} else {
|
|
1009
|
+
preview.willCreate.push('.omp/mcp.json');
|
|
1010
|
+
}
|
|
1011
|
+
preview.summary.mcpServers = resolvableMcpCount;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
884
1014
|
if (aiConfigTypes.includes('opencode') && (resolvableMcpCount > 0 || template.plugins?.length > 0)) {
|
|
885
1015
|
const opencodeConfigPath = path.join(targetDir, '.opencode/opencode.json');
|
|
886
1016
|
if (fs.existsSync(opencodeConfigPath)) {
|
|
@@ -891,8 +1021,16 @@ function previewTemplateApplication(targetDir, templateId, options = {}) {
|
|
|
891
1021
|
}
|
|
892
1022
|
|
|
893
1023
|
if (template.plugins?.length > 0) {
|
|
894
|
-
if (aiConfigTypes.includes('opencode')) {
|
|
1024
|
+
if (aiConfigTypes.includes('opencode') || aiConfigTypes.includes('omp')) {
|
|
895
1025
|
preview.summary.plugins = template.plugins.length;
|
|
1026
|
+
if (aiConfigTypes.includes('omp')) {
|
|
1027
|
+
const ompSettingsPath = path.join(targetDir, '.omp/config.yml');
|
|
1028
|
+
if (fs.existsSync(ompSettingsPath)) {
|
|
1029
|
+
preview.willOverwrite.push('.omp/config.yml');
|
|
1030
|
+
} else {
|
|
1031
|
+
preview.willCreate.push('.omp/config.yml');
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
896
1034
|
} else {
|
|
897
1035
|
for (const plugin of template.plugins) {
|
|
898
1036
|
pushSkipped(preview.skipped, 'plugin', plugin.name, '当前未选择 OpenCode,已跳过插件写入预览');
|