coding-tool-x 3.6.9 → 3.6.11
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/package.json
CHANGED
|
@@ -31,6 +31,15 @@ function resolveChannelsByType(exportData) {
|
|
|
31
31
|
|
|
32
32
|
function buildPreviewSummary(data) {
|
|
33
33
|
const channelsByType = resolveChannelsByType(data);
|
|
34
|
+
const pluginsByPlatform = data?.data?.pluginsByPlatform && typeof data.data.pluginsByPlatform === 'object'
|
|
35
|
+
? data.data.pluginsByPlatform
|
|
36
|
+
: {};
|
|
37
|
+
const platformPlugins = Object.values(pluginsByPlatform).flatMap((snapshot) => (
|
|
38
|
+
Array.isArray(snapshot?.plugins) ? snapshot.plugins : []
|
|
39
|
+
));
|
|
40
|
+
const plugins = platformPlugins.length > 0
|
|
41
|
+
? platformPlugins
|
|
42
|
+
: (Array.isArray(data.data.plugins) ? data.data.plugins : []);
|
|
34
43
|
const allChannels = [
|
|
35
44
|
...channelsByType.claude.map(c => ({ ...c, type: c.type || 'claude' })),
|
|
36
45
|
...channelsByType.codex.map(c => ({ ...c, type: c.type || 'codex' })),
|
|
@@ -44,7 +53,7 @@ function buildPreviewSummary(data) {
|
|
|
44
53
|
counts: {
|
|
45
54
|
configTemplates: (data.data.configTemplates || []).length,
|
|
46
55
|
channels: allChannels.length,
|
|
47
|
-
plugins:
|
|
56
|
+
plugins: plugins.length
|
|
48
57
|
},
|
|
49
58
|
items: {
|
|
50
59
|
configTemplates: (data.data.configTemplates || []).map(t => ({
|
|
@@ -57,9 +66,10 @@ function buildPreviewSummary(data) {
|
|
|
57
66
|
name: c.name,
|
|
58
67
|
type: c.type
|
|
59
68
|
})),
|
|
60
|
-
plugins:
|
|
69
|
+
plugins: plugins.map(p => ({
|
|
61
70
|
name: p.name,
|
|
62
71
|
type: p.type,
|
|
72
|
+
platform: p.platform,
|
|
63
73
|
version: p.version
|
|
64
74
|
}))
|
|
65
75
|
}
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const AdmZip = require('adm-zip');
|
|
9
|
+
const toml = require('toml');
|
|
10
|
+
const tomlStringify = require('@iarna/toml').stringify;
|
|
9
11
|
const configTemplatesService = require('./config-templates-service');
|
|
10
12
|
const channelsService = require('./channels');
|
|
11
13
|
const codexChannelsService = require('./codex-channels');
|
|
@@ -14,6 +16,7 @@ const opencodeChannelsService = require('./opencode-channels');
|
|
|
14
16
|
const { AgentsService } = require('./agents-service');
|
|
15
17
|
const { CommandsService } = require('./commands-service');
|
|
16
18
|
const { SkillService } = require('./skill-service');
|
|
19
|
+
const { PluginsService } = require('./plugins-service');
|
|
17
20
|
const { PATHS, NATIVE_PATHS } = require('../../config/paths');
|
|
18
21
|
|
|
19
22
|
const CONFIG_VERSION = '1.4.0';
|
|
@@ -48,6 +51,14 @@ const GEMINI_SETTINGS_PATH = path.join(path.dirname(NATIVE_PATHS.gemini.env), 's
|
|
|
48
51
|
const AGENT_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
|
|
49
52
|
const COMMAND_PLATFORMS = ['claude', 'gemini', 'opencode'];
|
|
50
53
|
const SKILL_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
|
|
54
|
+
const PLUGIN_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode', 'pi'];
|
|
55
|
+
const CLAUDE_MARKETPLACES_REGISTRY = path.join(CLAUDE_PLUGINS_DIR, 'known_marketplaces.json');
|
|
56
|
+
const CODEX_PLUGINS_DIR = path.join(path.dirname(NATIVE_PATHS.codex.config), 'plugins');
|
|
57
|
+
const CODEX_PLUGINS_CACHE_DIR = path.join(CODEX_PLUGINS_DIR, 'cache');
|
|
58
|
+
const OPENCODE_PLUGINS_DIR = path.join(NATIVE_PATHS.opencode.config, 'plugins');
|
|
59
|
+
const OPENCODE_LEGACY_PLUGINS_DIR = path.join(NATIVE_PATHS.opencode.config, 'plugin');
|
|
60
|
+
const PI_SETTINGS_PATH = NATIVE_PATHS.pi?.settings || path.join(PATHS.base, 'pi-settings.json');
|
|
61
|
+
const PI_EXTENSIONS_DIR = NATIVE_PATHS.pi?.extensions || path.join(PATHS.base, 'pi-extensions');
|
|
51
62
|
|
|
52
63
|
function getOpenCodeConfigPaths() {
|
|
53
64
|
try {
|
|
@@ -517,7 +528,7 @@ function syncImportedChannelsToNativeConfigs(importChannelsByType, nativeConfigs
|
|
|
517
528
|
}
|
|
518
529
|
|
|
519
530
|
try {
|
|
520
|
-
if (!hasNativeConfigFor('codex')) {
|
|
531
|
+
if (!hasNativeConfigFor('codex') && (importChannelsByType.codex || []).length > 0) {
|
|
521
532
|
codexChannelsService.writeCodexConfigForMultiChannel(importChannelsByType.codex || []);
|
|
522
533
|
}
|
|
523
534
|
} catch (err) {
|
|
@@ -543,7 +554,7 @@ function syncImportedChannelsToNativeConfigs(importChannelsByType, nativeConfigs
|
|
|
543
554
|
}
|
|
544
555
|
|
|
545
556
|
try {
|
|
546
|
-
if (!hasNativeConfigFor('opencode')) {
|
|
557
|
+
if (!hasNativeConfigFor('opencode') && (importChannelsByType.opencode || []).length > 0) {
|
|
547
558
|
const openCodeSpecs = getNativeConfigSpecs().opencode || {};
|
|
548
559
|
const preferredSpec = [
|
|
549
560
|
openCodeSpecs.opencodeJsonc,
|
|
@@ -656,6 +667,295 @@ function collectPluginFiles(pluginDir, basePath = '') {
|
|
|
656
667
|
return files;
|
|
657
668
|
}
|
|
658
669
|
|
|
670
|
+
function getOpenCodePluginsDir() {
|
|
671
|
+
if (fs.existsSync(OPENCODE_LEGACY_PLUGINS_DIR) && !fs.existsSync(OPENCODE_PLUGINS_DIR)) {
|
|
672
|
+
return OPENCODE_LEGACY_PLUGINS_DIR;
|
|
673
|
+
}
|
|
674
|
+
return OPENCODE_PLUGINS_DIR;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
function detectPluginManifest(pluginDir, candidates = []) {
|
|
678
|
+
for (const candidate of candidates) {
|
|
679
|
+
const manifestPath = path.join(pluginDir, candidate);
|
|
680
|
+
const manifest = readJsonFileSafe(manifestPath);
|
|
681
|
+
if (manifest) {
|
|
682
|
+
return {
|
|
683
|
+
manifest,
|
|
684
|
+
manifestPath: candidate
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return {
|
|
689
|
+
manifest: {},
|
|
690
|
+
manifestPath: ''
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function buildManagedPluginExportItem(plugin, platform, baseDir, options = {}) {
|
|
695
|
+
const directory = plugin.directory || plugin.name;
|
|
696
|
+
const installPath = plugin.installPath || (directory ? path.join(baseDir, directory) : '');
|
|
697
|
+
if (!installPath || !fs.existsSync(installPath)) {
|
|
698
|
+
return null;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
const pluginDir = assertPathInside(baseDir, installPath);
|
|
702
|
+
if (!pluginDir) {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const { manifest, manifestPath } = detectPluginManifest(pluginDir, options.manifestCandidates || []);
|
|
707
|
+
const files = collectPluginFiles(pluginDir);
|
|
708
|
+
if (files.length === 0) {
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
return {
|
|
713
|
+
platform,
|
|
714
|
+
type: options.type || 'managed',
|
|
715
|
+
pluginType: plugin.pluginType || options.pluginType || 'local',
|
|
716
|
+
pluginKind: plugin.pluginKind || options.pluginKind || 'plugin',
|
|
717
|
+
name: manifest.name || plugin.name || path.basename(pluginDir),
|
|
718
|
+
version: manifest.version || plugin.version || '1.0.0',
|
|
719
|
+
description: manifest.description || plugin.description || '',
|
|
720
|
+
author: manifest.author || plugin.author || '',
|
|
721
|
+
directory: path.relative(baseDir, pluginDir),
|
|
722
|
+
marketplace: plugin.marketplace || '',
|
|
723
|
+
enabled: plugin.enabled !== false,
|
|
724
|
+
installed: plugin.installed !== false,
|
|
725
|
+
source: plugin.source || '',
|
|
726
|
+
installSource: plugin.installSource || '',
|
|
727
|
+
repoId: plugin.repoId || '',
|
|
728
|
+
repoProvider: plugin.repoProvider || '',
|
|
729
|
+
repoOwner: plugin.repoOwner || '',
|
|
730
|
+
repoName: plugin.repoName || '',
|
|
731
|
+
repoBranch: plugin.repoBranch || '',
|
|
732
|
+
repoDirectory: plugin.repoDirectory || '',
|
|
733
|
+
repoHost: plugin.repoHost || '',
|
|
734
|
+
repoProjectPath: plugin.repoProjectPath || '',
|
|
735
|
+
repoLocalPath: plugin.repoLocalPath || '',
|
|
736
|
+
repoUrl: plugin.repoUrl || '',
|
|
737
|
+
manifest,
|
|
738
|
+
manifestPath,
|
|
739
|
+
files
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
function assertPathInside(baseDir, targetPath) {
|
|
744
|
+
if (!baseDir || !targetPath) return null;
|
|
745
|
+
const resolvedBase = path.resolve(baseDir);
|
|
746
|
+
const resolvedTarget = path.resolve(targetPath);
|
|
747
|
+
const relative = path.relative(resolvedBase, resolvedTarget);
|
|
748
|
+
if (relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
749
|
+
return null;
|
|
750
|
+
}
|
|
751
|
+
return resolvedTarget;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function readJsonConfigSnapshot(filePath) {
|
|
755
|
+
const content = readJsonFileSafe(filePath);
|
|
756
|
+
return content ? { path: filePath, content } : null;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function exportPluginControlSnapshot(platform, service) {
|
|
760
|
+
const snapshot = {};
|
|
761
|
+
const reposPath = PATHS.pluginRepos?.[platform];
|
|
762
|
+
const marketCachePath = PATHS.pluginMarketCache?.[platform];
|
|
763
|
+
const repos = readJsonConfigSnapshot(reposPath);
|
|
764
|
+
const marketCache = readJsonConfigSnapshot(marketCachePath);
|
|
765
|
+
|
|
766
|
+
if (repos) {
|
|
767
|
+
snapshot.repos = repos;
|
|
768
|
+
}
|
|
769
|
+
if (marketCache) {
|
|
770
|
+
snapshot.marketCache = marketCache;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
try {
|
|
774
|
+
const serviceRepos = service.getRepos?.() || [];
|
|
775
|
+
if (serviceRepos.length > 0) {
|
|
776
|
+
snapshot.resolvedRepos = serviceRepos;
|
|
777
|
+
}
|
|
778
|
+
} catch (err) {
|
|
779
|
+
// repo metadata is supplemental; file snapshots remain the source of truth
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return snapshot;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
function exportClaudePluginsByPlatform(service) {
|
|
786
|
+
const installedPlugins = service.listPlugins().plugins || [];
|
|
787
|
+
const plugins = [];
|
|
788
|
+
|
|
789
|
+
for (const plugin of installedPlugins) {
|
|
790
|
+
if (plugin.installPath && fs.existsSync(plugin.installPath)) {
|
|
791
|
+
const pluginDir = assertPathInside(CLAUDE_PLUGINS_DIR, plugin.installPath)
|
|
792
|
+
|| assertPathInside(LEGACY_PLUGINS_DIR, plugin.installPath);
|
|
793
|
+
if (pluginDir) {
|
|
794
|
+
const { manifest, manifestPath } = detectPluginManifest(pluginDir, [
|
|
795
|
+
'.claude-plugin/plugin.json',
|
|
796
|
+
'plugin.json',
|
|
797
|
+
'package.json'
|
|
798
|
+
]);
|
|
799
|
+
const files = collectPluginFiles(pluginDir);
|
|
800
|
+
if (files.length > 0) {
|
|
801
|
+
plugins.push({
|
|
802
|
+
platform: 'claude',
|
|
803
|
+
type: 'native',
|
|
804
|
+
pluginType: plugin.pluginType || 'native',
|
|
805
|
+
pluginKind: plugin.pluginKind || 'plugin',
|
|
806
|
+
name: manifest.name || plugin.name || path.basename(pluginDir),
|
|
807
|
+
marketplace: plugin.marketplace || '',
|
|
808
|
+
version: manifest.version || plugin.version || '1.0.0',
|
|
809
|
+
description: manifest.description || plugin.description || '',
|
|
810
|
+
author: manifest.author || plugin.author || '',
|
|
811
|
+
directory: path.relative(CLAUDE_PLUGINS_DIR, pluginDir),
|
|
812
|
+
enabled: plugin.enabled !== false,
|
|
813
|
+
installedAt: plugin.installedAt,
|
|
814
|
+
scope: plugin.scope,
|
|
815
|
+
source: plugin.source || '',
|
|
816
|
+
repoId: plugin.repoId || '',
|
|
817
|
+
repoProvider: plugin.repoProvider || '',
|
|
818
|
+
repoOwner: plugin.repoOwner || '',
|
|
819
|
+
repoName: plugin.repoName || '',
|
|
820
|
+
repoBranch: plugin.repoBranch || '',
|
|
821
|
+
repoDirectory: plugin.repoDirectory || '',
|
|
822
|
+
repoHost: plugin.repoHost || '',
|
|
823
|
+
repoProjectPath: plugin.repoProjectPath || '',
|
|
824
|
+
repoLocalPath: plugin.repoLocalPath || '',
|
|
825
|
+
repoUrl: plugin.repoUrl || '',
|
|
826
|
+
manifest,
|
|
827
|
+
manifestPath,
|
|
828
|
+
files
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
const legacyPluginNames = new Set(plugins.map(plugin => plugin.name));
|
|
836
|
+
for (const plugin of exportLegacyPlugins()) {
|
|
837
|
+
if (!legacyPluginNames.has(plugin.name)) {
|
|
838
|
+
plugins.push({ ...plugin, platform: 'claude' });
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
const control = exportPluginControlSnapshot('claude', service);
|
|
843
|
+
const installedRegistry = readJsonConfigSnapshot(NATIVE_PLUGINS_REGISTRY);
|
|
844
|
+
const knownMarketplaces = readJsonConfigSnapshot(CLAUDE_MARKETPLACES_REGISTRY);
|
|
845
|
+
const legacyRegistry = readJsonConfigSnapshot(LEGACY_PLUGINS_REGISTRY);
|
|
846
|
+
if (installedRegistry) control.installedRegistry = installedRegistry;
|
|
847
|
+
if (knownMarketplaces) control.knownMarketplaces = knownMarketplaces;
|
|
848
|
+
if (legacyRegistry) control.legacyRegistry = legacyRegistry;
|
|
849
|
+
|
|
850
|
+
return { plugins, control };
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
function exportCodexPluginsByPlatform(service) {
|
|
854
|
+
const plugins = (service.listPlugins().plugins || [])
|
|
855
|
+
.map(plugin => buildManagedPluginExportItem(plugin, 'codex', CODEX_PLUGINS_CACHE_DIR, {
|
|
856
|
+
type: 'codex-cache',
|
|
857
|
+
pluginType: 'cache',
|
|
858
|
+
manifestCandidates: ['.codex-plugin/plugin.json', 'plugin.json', 'package.json']
|
|
859
|
+
}))
|
|
860
|
+
.filter(Boolean);
|
|
861
|
+
const control = exportPluginControlSnapshot('codex', service);
|
|
862
|
+
const codexConfig = readNativeConfigSnapshot({ path: NATIVE_PATHS.codex.config, format: 'text' });
|
|
863
|
+
if (codexConfig) {
|
|
864
|
+
control.nativeConfig = codexConfig;
|
|
865
|
+
}
|
|
866
|
+
return { plugins, control };
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
function exportOpenCodePluginsByPlatform(service) {
|
|
870
|
+
const pluginsDir = getOpenCodePluginsDir();
|
|
871
|
+
const plugins = (service.listPlugins().plugins || [])
|
|
872
|
+
.map((plugin) => {
|
|
873
|
+
if (plugin.pluginType === 'npm' || plugin.source === 'opencode-config') {
|
|
874
|
+
return {
|
|
875
|
+
platform: 'opencode',
|
|
876
|
+
type: 'opencode-package',
|
|
877
|
+
pluginType: 'npm',
|
|
878
|
+
pluginKind: plugin.pluginKind || 'plugin',
|
|
879
|
+
name: plugin.name,
|
|
880
|
+
directory: plugin.directory || plugin.name,
|
|
881
|
+
version: plugin.version || 'latest',
|
|
882
|
+
description: plugin.description || '',
|
|
883
|
+
enabled: plugin.enabled !== false,
|
|
884
|
+
installed: plugin.installed !== false,
|
|
885
|
+
source: plugin.source || 'opencode-config'
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
return buildManagedPluginExportItem(plugin, 'opencode', pluginsDir, {
|
|
889
|
+
type: 'opencode-local',
|
|
890
|
+
pluginType: plugin.pluginType || 'local',
|
|
891
|
+
manifestCandidates: ['package.json', 'plugin.json']
|
|
892
|
+
});
|
|
893
|
+
})
|
|
894
|
+
.filter(Boolean);
|
|
895
|
+
const control = exportPluginControlSnapshot('opencode', service);
|
|
896
|
+
return { plugins, control };
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function exportPiPluginsByPlatform(service) {
|
|
900
|
+
const plugins = (service.listPlugins().plugins || [])
|
|
901
|
+
.map((plugin) => {
|
|
902
|
+
if (plugin.pluginType === 'package' || plugin.source === 'pi-settings') {
|
|
903
|
+
return {
|
|
904
|
+
platform: 'pi',
|
|
905
|
+
type: 'pi-package',
|
|
906
|
+
pluginType: 'package',
|
|
907
|
+
pluginKind: plugin.pluginKind || 'package',
|
|
908
|
+
name: plugin.name,
|
|
909
|
+
directory: plugin.directory || plugin.name,
|
|
910
|
+
version: plugin.version || 'latest',
|
|
911
|
+
description: plugin.description || '',
|
|
912
|
+
enabled: plugin.enabled !== false,
|
|
913
|
+
installed: plugin.installed !== false,
|
|
914
|
+
source: plugin.source || 'pi-settings'
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
return buildManagedPluginExportItem(plugin, 'pi', PI_EXTENSIONS_DIR, {
|
|
918
|
+
type: 'pi-extension',
|
|
919
|
+
pluginType: plugin.pluginType || 'extension',
|
|
920
|
+
pluginKind: plugin.pluginKind || 'extension',
|
|
921
|
+
manifestCandidates: ['pi.json', 'extension.json', 'plugin.json', 'package.json']
|
|
922
|
+
});
|
|
923
|
+
})
|
|
924
|
+
.filter(Boolean);
|
|
925
|
+
const control = exportPluginControlSnapshot('pi', service);
|
|
926
|
+
const piSettings = readNativeConfigSnapshot({ path: PI_SETTINGS_PATH, format: 'json' });
|
|
927
|
+
if (piSettings) {
|
|
928
|
+
control.nativeSettings = piSettings;
|
|
929
|
+
}
|
|
930
|
+
return { plugins, control };
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
function exportPluginsSnapshotByPlatform() {
|
|
934
|
+
return PLUGIN_PLATFORMS.reduce((result, platform) => {
|
|
935
|
+
try {
|
|
936
|
+
const service = new PluginsService(platform);
|
|
937
|
+
if (platform === 'claude') {
|
|
938
|
+
result[platform] = exportClaudePluginsByPlatform(service);
|
|
939
|
+
} else if (platform === 'codex') {
|
|
940
|
+
result[platform] = exportCodexPluginsByPlatform(service);
|
|
941
|
+
} else if (platform === 'opencode') {
|
|
942
|
+
result[platform] = exportOpenCodePluginsByPlatform(service);
|
|
943
|
+
} else if (platform === 'pi') {
|
|
944
|
+
result[platform] = exportPiPluginsByPlatform(service);
|
|
945
|
+
} else {
|
|
946
|
+
result[platform] = {
|
|
947
|
+
plugins: service.listPlugins().plugins || [],
|
|
948
|
+
control: exportPluginControlSnapshot(platform, service)
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
} catch (err) {
|
|
952
|
+
console.warn(`[ConfigExport] Failed to export plugins for ${platform}:`, err.message);
|
|
953
|
+
result[platform] = { plugins: [], control: {} };
|
|
954
|
+
}
|
|
955
|
+
return result;
|
|
956
|
+
}, {});
|
|
957
|
+
}
|
|
958
|
+
|
|
659
959
|
function exportSkillsSnapshot(platform = 'claude') {
|
|
660
960
|
const skillService = new SkillService(platform);
|
|
661
961
|
const installedSkills = skillService.getInstalledSkills();
|
|
@@ -814,6 +1114,381 @@ function exportPluginsSnapshot() {
|
|
|
814
1114
|
return [...legacyPlugins, ...nativePlugins];
|
|
815
1115
|
}
|
|
816
1116
|
|
|
1117
|
+
function getPluginsByPlatformItems(plugins = [], pluginsByPlatform = {}) {
|
|
1118
|
+
const result = Object.fromEntries(PLUGIN_PLATFORMS.map(platform => [platform, []]));
|
|
1119
|
+
const hasStructuredPlugins = pluginsByPlatform
|
|
1120
|
+
&& typeof pluginsByPlatform === 'object'
|
|
1121
|
+
&& Object.keys(pluginsByPlatform).length > 0;
|
|
1122
|
+
|
|
1123
|
+
if (hasStructuredPlugins) {
|
|
1124
|
+
for (const platform of PLUGIN_PLATFORMS) {
|
|
1125
|
+
const platformSnapshot = pluginsByPlatform[platform];
|
|
1126
|
+
const platformPlugins = Array.isArray(platformSnapshot?.plugins)
|
|
1127
|
+
? platformSnapshot.plugins
|
|
1128
|
+
: (Array.isArray(platformSnapshot) ? platformSnapshot : []);
|
|
1129
|
+
result[platform] = platformPlugins.map(plugin => ({
|
|
1130
|
+
...plugin,
|
|
1131
|
+
platform
|
|
1132
|
+
}));
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
if (!hasStructuredPlugins && Array.isArray(plugins)) {
|
|
1137
|
+
for (const plugin of plugins) {
|
|
1138
|
+
const platform = PLUGIN_PLATFORMS.includes(plugin?.platform) ? plugin.platform : 'claude';
|
|
1139
|
+
result[platform].push({ ...plugin, platform });
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
return result;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
function writePluginFiles(pluginDir, files = []) {
|
|
1147
|
+
let failed = false;
|
|
1148
|
+
|
|
1149
|
+
for (const file of files) {
|
|
1150
|
+
const filePath = resolveSafePath(pluginDir, file.path);
|
|
1151
|
+
if (!filePath) {
|
|
1152
|
+
failed = true;
|
|
1153
|
+
break;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
ensureDir(path.dirname(filePath));
|
|
1157
|
+
|
|
1158
|
+
try {
|
|
1159
|
+
if (file.encoding === SKILL_FILE_ENCODING) {
|
|
1160
|
+
fs.writeFileSync(filePath, Buffer.from(file.content || '', SKILL_FILE_ENCODING));
|
|
1161
|
+
} else {
|
|
1162
|
+
fs.writeFileSync(filePath, file.content || '', file.encoding || 'utf8');
|
|
1163
|
+
}
|
|
1164
|
+
} catch (err) {
|
|
1165
|
+
console.error(`[ConfigImport] Failed to write plugin file ${file.path}:`, err);
|
|
1166
|
+
failed = true;
|
|
1167
|
+
break;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
return !failed && files.length > 0;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
function pluginNameForPath(plugin = {}) {
|
|
1175
|
+
return plugin.name || plugin.id || plugin.directory || 'plugin';
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
function sanitizePackageName(value = '') {
|
|
1179
|
+
return String(value || '')
|
|
1180
|
+
.trim()
|
|
1181
|
+
.replace(/\\/g, '/')
|
|
1182
|
+
.split('/')
|
|
1183
|
+
.filter(Boolean)
|
|
1184
|
+
.pop()
|
|
1185
|
+
?.replace(/[^a-zA-Z0-9._@+-]+/g, '-')
|
|
1186
|
+
.replace(/^-+|-+$/g, '') || 'plugin';
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
function resolvePluginImportDir(baseDir, plugin = {}) {
|
|
1190
|
+
const directory = plugin.directory && !String(plugin.directory).includes('..')
|
|
1191
|
+
? plugin.directory
|
|
1192
|
+
: sanitizePackageName(pluginNameForPath(plugin));
|
|
1193
|
+
return resolveSafePath(baseDir, directory);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
function readTomlFileSafe(filePath) {
|
|
1197
|
+
if (!filePath || !fs.existsSync(filePath)) return {};
|
|
1198
|
+
try {
|
|
1199
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
1200
|
+
return content.trim() ? toml.parse(content) : {};
|
|
1201
|
+
} catch (err) {
|
|
1202
|
+
return {};
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
function mergeTomlFile(filePath, updates = {}, overwrite = true) {
|
|
1207
|
+
if (!filePath || !updates || typeof updates !== 'object') {
|
|
1208
|
+
return 'failed';
|
|
1209
|
+
}
|
|
1210
|
+
if (fs.existsSync(filePath) && !overwrite) {
|
|
1211
|
+
return 'skipped';
|
|
1212
|
+
}
|
|
1213
|
+
const existing = readTomlFileSafe(filePath);
|
|
1214
|
+
const nextValue = {
|
|
1215
|
+
...existing,
|
|
1216
|
+
...updates,
|
|
1217
|
+
plugins: {
|
|
1218
|
+
...((existing.plugins && typeof existing.plugins === 'object') ? existing.plugins : {}),
|
|
1219
|
+
...((updates.plugins && typeof updates.plugins === 'object') ? updates.plugins : {})
|
|
1220
|
+
},
|
|
1221
|
+
marketplaces: {
|
|
1222
|
+
...((existing.marketplaces && typeof existing.marketplaces === 'object') ? existing.marketplaces : {}),
|
|
1223
|
+
...((updates.marketplaces && typeof updates.marketplaces === 'object') ? updates.marketplaces : {})
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
if (Object.keys(nextValue.plugins).length === 0) delete nextValue.plugins;
|
|
1227
|
+
if (Object.keys(nextValue.marketplaces).length === 0) delete nextValue.marketplaces;
|
|
1228
|
+
|
|
1229
|
+
ensureDir(path.dirname(filePath));
|
|
1230
|
+
fs.writeFileSync(filePath, tomlStringify(JSON.parse(JSON.stringify(nextValue))), 'utf8');
|
|
1231
|
+
return 'success';
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
function mergePluginRepoConfig(platform, snapshot = {}, overwrite = true) {
|
|
1235
|
+
const reposPath = PATHS.pluginRepos?.[platform];
|
|
1236
|
+
const marketCachePath = PATHS.pluginMarketCache?.[platform];
|
|
1237
|
+
let changed = 0;
|
|
1238
|
+
let skipped = 0;
|
|
1239
|
+
let failed = 0;
|
|
1240
|
+
|
|
1241
|
+
const reposContent = snapshot.control?.repos?.content;
|
|
1242
|
+
if (reposPath && reposContent && typeof reposContent === 'object') {
|
|
1243
|
+
const status = writeJsonFileAbsolute(reposPath, reposContent, overwrite);
|
|
1244
|
+
if (status === 'success') changed++;
|
|
1245
|
+
else if (status === 'skipped') skipped++;
|
|
1246
|
+
else failed++;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
const marketCacheContent = snapshot.control?.marketCache?.content;
|
|
1250
|
+
if (marketCachePath && marketCacheContent && typeof marketCacheContent === 'object') {
|
|
1251
|
+
const status = writeJsonFileAbsolute(marketCachePath, marketCacheContent, overwrite);
|
|
1252
|
+
if (status === 'success') changed++;
|
|
1253
|
+
else if (status === 'skipped') skipped++;
|
|
1254
|
+
else failed++;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
return { changed, skipped, failed };
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
function mergeCodexPluginConfig(snapshot = {}, plugins = [], overwrite = true) {
|
|
1261
|
+
const nativeConfig = snapshot.control?.nativeConfig;
|
|
1262
|
+
if (nativeConfig?.content !== undefined) {
|
|
1263
|
+
return writeNativeConfigAbsolute({ path: NATIVE_PATHS.codex.config, format: 'text' }, nativeConfig, overwrite);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
const pluginEntries = {};
|
|
1267
|
+
const marketplaces = {};
|
|
1268
|
+
for (const plugin of plugins) {
|
|
1269
|
+
if (!plugin?.name) continue;
|
|
1270
|
+
const pluginKey = plugin.marketplace ? `${plugin.name}@${plugin.marketplace}` : plugin.name;
|
|
1271
|
+
pluginEntries[pluginKey] = { enabled: plugin.enabled !== false };
|
|
1272
|
+
if (plugin.marketplace && plugin.repoUrl) {
|
|
1273
|
+
marketplaces[plugin.marketplace] = {
|
|
1274
|
+
source_type: plugin.repoLocalPath ? 'local' : 'git',
|
|
1275
|
+
source: plugin.repoUrl,
|
|
1276
|
+
last_updated: new Date().toISOString()
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
if (Object.keys(pluginEntries).length === 0 && Object.keys(marketplaces).length === 0) {
|
|
1282
|
+
return 'skipped';
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
return mergeTomlFile(NATIVE_PATHS.codex.config, {
|
|
1286
|
+
...(Object.keys(pluginEntries).length > 0 ? { plugins: pluginEntries } : {}),
|
|
1287
|
+
...(Object.keys(marketplaces).length > 0 ? { marketplaces } : {})
|
|
1288
|
+
}, overwrite);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
function writeOpenCodePluginConfig(packages = [], overwrite = true) {
|
|
1292
|
+
if (packages.length === 0) return 'skipped';
|
|
1293
|
+
const openCodePaths = getOpenCodeConfigPaths();
|
|
1294
|
+
const configPath = [
|
|
1295
|
+
openCodePaths.opencodec,
|
|
1296
|
+
openCodePaths.opencode,
|
|
1297
|
+
openCodePaths.config
|
|
1298
|
+
].find(filePath => filePath && fs.existsSync(filePath))
|
|
1299
|
+
|| openCodePaths.opencode
|
|
1300
|
+
|| path.join(NATIVE_PATHS.opencode.config, 'opencode.json');
|
|
1301
|
+
const existing = readJsonFileSafe(configPath) || {};
|
|
1302
|
+
if (fs.existsSync(configPath) && !overwrite) {
|
|
1303
|
+
return 'skipped';
|
|
1304
|
+
}
|
|
1305
|
+
const existingPlugins = Array.isArray(existing.plugin) ? existing.plugin : [];
|
|
1306
|
+
const mergedPlugins = Array.from(new Set([...existingPlugins, ...packages]));
|
|
1307
|
+
return writeJsonFileAbsolute(configPath, { ...existing, plugin: mergedPlugins }, true);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
function writePiPluginSettings(plugins = [], snapshot = {}, overwrite = true) {
|
|
1311
|
+
const nativeSettings = snapshot.control?.nativeSettings;
|
|
1312
|
+
if (nativeSettings?.content !== undefined) {
|
|
1313
|
+
return writeNativeConfigAbsolute({ path: PI_SETTINGS_PATH, format: 'json' }, nativeSettings, overwrite);
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
const settings = readJsonFileSafe(PI_SETTINGS_PATH) || {};
|
|
1317
|
+
if (fs.existsSync(PI_SETTINGS_PATH) && !overwrite) {
|
|
1318
|
+
return 'skipped';
|
|
1319
|
+
}
|
|
1320
|
+
const existingPackages = Array.isArray(settings.packages) ? settings.packages : [];
|
|
1321
|
+
const existingDisabled = Array.isArray(settings.disabledPackages) ? settings.disabledPackages : [];
|
|
1322
|
+
const packagePlugins = plugins.filter(plugin => plugin.type === 'pi-package' || plugin.pluginType === 'package');
|
|
1323
|
+
const packages = packagePlugins.map(plugin => plugin.name).filter(Boolean);
|
|
1324
|
+
const disabledPackages = packagePlugins
|
|
1325
|
+
.filter(plugin => plugin.enabled === false)
|
|
1326
|
+
.map(plugin => plugin.name)
|
|
1327
|
+
.filter(Boolean);
|
|
1328
|
+
|
|
1329
|
+
if (packages.length === 0 && disabledPackages.length === 0) {
|
|
1330
|
+
return 'skipped';
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
return writeJsonFileAbsolute(PI_SETTINGS_PATH, {
|
|
1334
|
+
...settings,
|
|
1335
|
+
packages: Array.from(new Set([...existingPackages, ...packages])),
|
|
1336
|
+
disabledPackages: Array.from(new Set([...existingDisabled, ...disabledPackages]))
|
|
1337
|
+
}, true);
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
function importPluginToDirectory(platform, plugin, baseDir, overwrite) {
|
|
1341
|
+
const files = Array.isArray(plugin.files) ? plugin.files : [];
|
|
1342
|
+
if (files.length === 0) {
|
|
1343
|
+
return 'skipped';
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
const pluginDir = resolvePluginImportDir(baseDir, plugin);
|
|
1347
|
+
if (!pluginDir) {
|
|
1348
|
+
return 'failed';
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
if (fs.existsSync(pluginDir)) {
|
|
1352
|
+
if (!overwrite) {
|
|
1353
|
+
return 'skipped';
|
|
1354
|
+
}
|
|
1355
|
+
fs.rmSync(pluginDir, { recursive: true, force: true });
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
ensureDir(pluginDir);
|
|
1359
|
+
const wroteFiles = writePluginFiles(pluginDir, files);
|
|
1360
|
+
if (!wroteFiles) {
|
|
1361
|
+
return 'failed';
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
const sourceMeta = {
|
|
1365
|
+
repoId: plugin.repoId || '',
|
|
1366
|
+
repoProvider: plugin.repoProvider || '',
|
|
1367
|
+
repoOwner: plugin.repoOwner || '',
|
|
1368
|
+
repoName: plugin.repoName || '',
|
|
1369
|
+
repoBranch: plugin.repoBranch || '',
|
|
1370
|
+
repoDirectory: plugin.repoDirectory || '',
|
|
1371
|
+
repoHost: plugin.repoHost || '',
|
|
1372
|
+
repoProjectPath: plugin.repoProjectPath || '',
|
|
1373
|
+
repoLocalPath: plugin.repoLocalPath || '',
|
|
1374
|
+
repoUrl: plugin.repoUrl || '',
|
|
1375
|
+
marketplace: plugin.marketplace || '',
|
|
1376
|
+
source: plugin.source || ''
|
|
1377
|
+
};
|
|
1378
|
+
if (Object.values(sourceMeta).some(Boolean)) {
|
|
1379
|
+
writeJsonFileAbsolute(path.join(pluginDir, '.cc-tool-plugin-source.json'), sourceMeta, true);
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
if (platform === 'claude') {
|
|
1383
|
+
updateClaudePluginRegistry(plugin, pluginDir);
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
return 'success';
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
function applyImportStatus(results, status) {
|
|
1390
|
+
if (status === 'success') {
|
|
1391
|
+
results.plugins.success++;
|
|
1392
|
+
} else if (status === 'skipped') {
|
|
1393
|
+
results.plugins.skipped++;
|
|
1394
|
+
} else {
|
|
1395
|
+
results.plugins.failed++;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
function importPluginsByPlatformSnapshot(snapshotByPlatform = {}, legacyPlugins = [], overwrite = true, results) {
|
|
1400
|
+
const pluginsByPlatform = getPluginsByPlatformItems(legacyPlugins, snapshotByPlatform);
|
|
1401
|
+
|
|
1402
|
+
for (const platform of PLUGIN_PLATFORMS) {
|
|
1403
|
+
const snapshot = snapshotByPlatform?.[platform] && typeof snapshotByPlatform[platform] === 'object'
|
|
1404
|
+
? snapshotByPlatform[platform]
|
|
1405
|
+
: {};
|
|
1406
|
+
const repoStatus = mergePluginRepoConfig(platform, snapshot, overwrite);
|
|
1407
|
+
results.plugins.success += repoStatus.changed;
|
|
1408
|
+
results.plugins.skipped += repoStatus.skipped;
|
|
1409
|
+
results.plugins.failed += repoStatus.failed;
|
|
1410
|
+
|
|
1411
|
+
const plugins = pluginsByPlatform[platform] || [];
|
|
1412
|
+
const hasControl = snapshot?.control && Object.keys(snapshot.control).length > 0;
|
|
1413
|
+
|
|
1414
|
+
if (platform === 'codex' && (hasControl || plugins.length > 0)) {
|
|
1415
|
+
applyImportStatus(results, mergeCodexPluginConfig(snapshot, plugins, overwrite));
|
|
1416
|
+
} else if (platform === 'opencode' && (hasControl || plugins.length > 0)) {
|
|
1417
|
+
const packages = plugins
|
|
1418
|
+
.filter(plugin => plugin.type === 'opencode-package' || plugin.pluginType === 'npm')
|
|
1419
|
+
.map(plugin => plugin.name || plugin.directory)
|
|
1420
|
+
.filter(Boolean);
|
|
1421
|
+
applyImportStatus(results, writeOpenCodePluginConfig(packages, overwrite));
|
|
1422
|
+
} else if (platform === 'pi' && (hasControl || plugins.length > 0)) {
|
|
1423
|
+
applyImportStatus(results, writePiPluginSettings(plugins, snapshot, overwrite));
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
for (const plugin of plugins) {
|
|
1427
|
+
try {
|
|
1428
|
+
if (platform === 'codex') {
|
|
1429
|
+
applyImportStatus(results, importPluginToDirectory(platform, plugin, CODEX_PLUGINS_CACHE_DIR, overwrite));
|
|
1430
|
+
} else if (platform === 'opencode') {
|
|
1431
|
+
if (plugin.type === 'opencode-package' || plugin.pluginType === 'npm') {
|
|
1432
|
+
continue;
|
|
1433
|
+
}
|
|
1434
|
+
applyImportStatus(results, importPluginToDirectory(platform, plugin, getOpenCodePluginsDir(), overwrite));
|
|
1435
|
+
} else if (platform === 'pi') {
|
|
1436
|
+
if (plugin.type === 'pi-package' || plugin.pluginType === 'package') {
|
|
1437
|
+
continue;
|
|
1438
|
+
}
|
|
1439
|
+
applyImportStatus(results, importPluginToDirectory(platform, plugin, PI_EXTENSIONS_DIR, overwrite));
|
|
1440
|
+
} else if (platform === 'claude') {
|
|
1441
|
+
const isLegacy = plugin.type === 'legacy';
|
|
1442
|
+
applyImportStatus(results, importPluginToDirectory(
|
|
1443
|
+
platform,
|
|
1444
|
+
plugin,
|
|
1445
|
+
isLegacy ? LEGACY_PLUGINS_DIR : CLAUDE_PLUGINS_DIR,
|
|
1446
|
+
overwrite
|
|
1447
|
+
));
|
|
1448
|
+
}
|
|
1449
|
+
} catch (err) {
|
|
1450
|
+
console.error(`[ConfigImport] Failed to import ${platform} plugin ${plugin?.name}:`, err);
|
|
1451
|
+
results.plugins.failed++;
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
function updateClaudePluginRegistry(plugin, pluginDir) {
|
|
1458
|
+
const pluginName = plugin.name || path.basename(pluginDir);
|
|
1459
|
+
const marketplace = plugin.marketplace || '';
|
|
1460
|
+
const nativeKey = marketplace ? `${pluginName}@${marketplace}` : pluginName;
|
|
1461
|
+
const installed = readJsonFileSafe(NATIVE_PLUGINS_REGISTRY) || { version: 2, plugins: {} };
|
|
1462
|
+
installed.version = installed.version || 2;
|
|
1463
|
+
installed.plugins = installed.plugins && typeof installed.plugins === 'object' ? installed.plugins : {};
|
|
1464
|
+
installed.plugins[nativeKey] = [{
|
|
1465
|
+
scope: plugin.scope || 'user',
|
|
1466
|
+
installPath: pluginDir,
|
|
1467
|
+
version: plugin.version || '1.0.0',
|
|
1468
|
+
installedAt: plugin.installedAt || new Date().toISOString(),
|
|
1469
|
+
lastUpdated: new Date().toISOString(),
|
|
1470
|
+
...(plugin.source ? { source: plugin.source } : {}),
|
|
1471
|
+
...(plugin.repoUrl ? { repoUrl: plugin.repoUrl } : {}),
|
|
1472
|
+
...(plugin.repoProvider ? { repoProvider: plugin.repoProvider } : {}),
|
|
1473
|
+
...(plugin.repoOwner ? { repoOwner: plugin.repoOwner } : {}),
|
|
1474
|
+
...(plugin.repoName ? { repoName: plugin.repoName } : {}),
|
|
1475
|
+
...(plugin.repoBranch ? { repoBranch: plugin.repoBranch } : {}),
|
|
1476
|
+
...(plugin.repoDirectory ? { repoDirectory: plugin.repoDirectory } : {}),
|
|
1477
|
+
...(plugin.repoHost ? { repoHost: plugin.repoHost } : {}),
|
|
1478
|
+
...(plugin.repoProjectPath ? { repoProjectPath: plugin.repoProjectPath } : {}),
|
|
1479
|
+
...(plugin.repoLocalPath ? { repoLocalPath: plugin.repoLocalPath } : {}),
|
|
1480
|
+
...(plugin.repoId ? { repoId: plugin.repoId } : {})
|
|
1481
|
+
}];
|
|
1482
|
+
writeJsonFileAbsolute(NATIVE_PLUGINS_REGISTRY, installed, true);
|
|
1483
|
+
|
|
1484
|
+
const settings = readJsonFileSafe(CLAUDE_SETTINGS_PATH) || {};
|
|
1485
|
+
settings.enabledPlugins = settings.enabledPlugins && typeof settings.enabledPlugins === 'object'
|
|
1486
|
+
? settings.enabledPlugins
|
|
1487
|
+
: {};
|
|
1488
|
+
settings.enabledPlugins[nativeKey] = plugin.enabled !== false;
|
|
1489
|
+
writeJsonFileAbsolute(CLAUDE_SETTINGS_PATH, settings, true);
|
|
1490
|
+
}
|
|
1491
|
+
|
|
817
1492
|
function writeTextFile(baseDir, relativePath, content, overwrite) {
|
|
818
1493
|
if (!content && content !== '') {
|
|
819
1494
|
return 'failed';
|
|
@@ -871,6 +1546,7 @@ function exportAllConfigs() {
|
|
|
871
1546
|
|
|
872
1547
|
// 获取 Plugins 配置
|
|
873
1548
|
const plugins = exportPluginsSnapshot();
|
|
1549
|
+
const pluginsByPlatform = exportPluginsSnapshotByPlatform();
|
|
874
1550
|
const nativeConfigs = exportNativeConfigs();
|
|
875
1551
|
const oauthCredentials = readJsonFileSafe(PATHS.oauthCredentials);
|
|
876
1552
|
|
|
@@ -923,6 +1599,7 @@ function exportAllConfigs() {
|
|
|
923
1599
|
commandsByPlatform,
|
|
924
1600
|
mcpServers: mcpServers || [],
|
|
925
1601
|
plugins: plugins || [],
|
|
1602
|
+
pluginsByPlatform,
|
|
926
1603
|
markdownFiles: markdownFiles,
|
|
927
1604
|
uiConfig: uiConfig,
|
|
928
1605
|
prompts: prompts,
|
|
@@ -1017,6 +1694,8 @@ async function importConfigs(importData, options = {}) {
|
|
|
1017
1694
|
skillsByPlatform = {},
|
|
1018
1695
|
commands = [],
|
|
1019
1696
|
commandsByPlatform = {},
|
|
1697
|
+
plugins = [],
|
|
1698
|
+
pluginsByPlatform = {},
|
|
1020
1699
|
mcpServers = [],
|
|
1021
1700
|
markdownFiles = {},
|
|
1022
1701
|
uiConfig = null,
|
|
@@ -1252,8 +1931,17 @@ async function importConfigs(importData, options = {}) {
|
|
|
1252
1931
|
}
|
|
1253
1932
|
}
|
|
1254
1933
|
|
|
1255
|
-
// 导入 Plugins
|
|
1256
|
-
if (
|
|
1934
|
+
// 导入 Plugins(优先使用多平台结构,兼容旧结构 plugins)
|
|
1935
|
+
if (pluginsByPlatform && typeof pluginsByPlatform === 'object' && Object.keys(pluginsByPlatform).length > 0) {
|
|
1936
|
+
importPluginsByPlatformSnapshot(pluginsByPlatform, plugins, overwrite, results);
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
// 导入旧版 Plugins 结构。新包已经通过 pluginsByPlatform 处理过,避免重复写入。
|
|
1940
|
+
if (
|
|
1941
|
+
(!pluginsByPlatform || Object.keys(pluginsByPlatform).length === 0) &&
|
|
1942
|
+
importData.data.plugins &&
|
|
1943
|
+
importData.data.plugins.length > 0
|
|
1944
|
+
) {
|
|
1257
1945
|
const plugins = importData.data.plugins;
|
|
1258
1946
|
|
|
1259
1947
|
try {
|