coding-tool-x 3.3.5 → 3.3.7
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 +9 -0
- package/dist/web/assets/{Analytics-B6CWdkhx.js → Analytics-IW6eAy9u.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-BW6LEgd8.js → ConfigTemplates-BPtkTMSc.js} +1 -1
- package/dist/web/assets/{Home-B2B2gS2-.js → Home-obifg_9E.js} +1 -1
- package/dist/web/assets/{PluginManager-Bqc7ldY-.js → PluginManager-BGx9MSDV.js} +1 -1
- package/dist/web/assets/{ProjectList-BFdZZm_8.js → ProjectList-BCn-mrCx.js} +1 -1
- package/dist/web/assets/{SessionList-B_Tp37kM.js → SessionList-CzLfebJQ.js} +1 -1
- package/dist/web/assets/{SkillManager-ul2rcS3o.js → SkillManager-CXz2vBQx.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-Dp5Jvdtu.js → WorkspaceManager-CHtgMfKc.js} +1 -1
- package/dist/web/assets/index-C7LPdVsN.js +2 -0
- package/dist/web/assets/{index-DxRneGyu.css → index-eEmjZKWP.css} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/src/commands/daemon.js +44 -6
- package/src/commands/update.js +21 -6
- package/src/config/default.js +1 -1
- package/src/config/model-metadata.js +2 -2
- package/src/config/model-metadata.json +7 -2
- package/src/server/api/config-export.js +21 -2
- package/src/server/api/mcp.js +26 -4
- package/src/server/index.js +25 -2
- package/src/server/services/config-export-service.js +639 -138
- package/src/server/services/mcp-client.js +162 -18
- package/src/server/services/mcp-service.js +130 -15
- package/src/server/services/model-detector.js +1 -0
- package/src/utils/port-helper.js +87 -2
- package/dist/web/assets/index-CSBDZxYn.js +0 -2
|
@@ -8,12 +8,15 @@ const path = require('path');
|
|
|
8
8
|
const AdmZip = require('adm-zip');
|
|
9
9
|
const configTemplatesService = require('./config-templates-service');
|
|
10
10
|
const channelsService = require('./channels');
|
|
11
|
+
const codexChannelsService = require('./codex-channels');
|
|
12
|
+
const geminiChannelsService = require('./gemini-channels');
|
|
13
|
+
const opencodeChannelsService = require('./opencode-channels');
|
|
11
14
|
const { AgentsService } = require('./agents-service');
|
|
12
15
|
const { CommandsService } = require('./commands-service');
|
|
13
16
|
const { SkillService } = require('./skill-service');
|
|
14
17
|
const { PATHS, NATIVE_PATHS } = require('../../config/paths');
|
|
15
18
|
|
|
16
|
-
const CONFIG_VERSION = '1.
|
|
19
|
+
const CONFIG_VERSION = '1.3.0';
|
|
17
20
|
const SKILL_FILE_ENCODING = 'base64';
|
|
18
21
|
const SKILL_IGNORE_DIRS = new Set(['.git']);
|
|
19
22
|
const SKILL_IGNORE_FILES = new Set(['.DS_Store']);
|
|
@@ -41,6 +44,41 @@ const CC_PROMPTS_PATH = path.join(CC_TOOL_DIR, 'prompts.json');
|
|
|
41
44
|
const CC_SECURITY_PATH = path.join(CC_TOOL_DIR, 'security.json');
|
|
42
45
|
const LEGACY_UI_CONFIG_PATH = path.join(LEGACY_CC_TOOL_DIR, 'ui-config.json');
|
|
43
46
|
const LEGACY_NOTIFY_HOOK_PATH = path.join(LEGACY_CC_TOOL_DIR, 'notify-hook.js');
|
|
47
|
+
const GEMINI_SETTINGS_PATH = path.join(path.dirname(NATIVE_PATHS.gemini.env), 'settings.json');
|
|
48
|
+
const AGENT_PLATFORMS = ['claude', 'codex', 'opencode'];
|
|
49
|
+
const COMMAND_PLATFORMS = ['claude', 'opencode'];
|
|
50
|
+
const SKILL_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
|
|
51
|
+
|
|
52
|
+
function getOpenCodeConfigPaths() {
|
|
53
|
+
try {
|
|
54
|
+
const { CONFIG_PATHS } = require('./opencode-settings-manager');
|
|
55
|
+
return CONFIG_PATHS || {};
|
|
56
|
+
} catch (err) {
|
|
57
|
+
return {};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getNativeConfigSpecs() {
|
|
62
|
+
const openCodeConfigPaths = getOpenCodeConfigPaths();
|
|
63
|
+
return {
|
|
64
|
+
claude: {
|
|
65
|
+
settings: { path: NATIVE_PATHS.claude.settings, format: 'json' }
|
|
66
|
+
},
|
|
67
|
+
codex: {
|
|
68
|
+
config: { path: NATIVE_PATHS.codex.config, format: 'text' },
|
|
69
|
+
auth: { path: NATIVE_PATHS.codex.auth, format: 'json', mode: 0o600 }
|
|
70
|
+
},
|
|
71
|
+
gemini: {
|
|
72
|
+
env: { path: NATIVE_PATHS.gemini.env, format: 'text', mode: 0o600 },
|
|
73
|
+
settings: { path: GEMINI_SETTINGS_PATH, format: 'json' }
|
|
74
|
+
},
|
|
75
|
+
opencode: {
|
|
76
|
+
opencodeJsonc: { path: openCodeConfigPaths.opencodec, format: 'text' },
|
|
77
|
+
opencodeJson: { path: openCodeConfigPaths.opencode, format: 'text' },
|
|
78
|
+
configJson: { path: openCodeConfigPaths.config, format: 'text' }
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
44
82
|
|
|
45
83
|
function ensureDir(dirPath) {
|
|
46
84
|
if (!fs.existsSync(dirPath)) {
|
|
@@ -67,6 +105,39 @@ function readTextFileSafe(filePath) {
|
|
|
67
105
|
}
|
|
68
106
|
}
|
|
69
107
|
|
|
108
|
+
function readNativeConfigSnapshot(spec) {
|
|
109
|
+
if (!spec?.path || !fs.existsSync(spec.path)) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const rawContent = fs.readFileSync(spec.path, 'utf8');
|
|
115
|
+
if (spec.format === 'json') {
|
|
116
|
+
try {
|
|
117
|
+
return {
|
|
118
|
+
format: 'json',
|
|
119
|
+
fileName: path.basename(spec.path),
|
|
120
|
+
content: JSON.parse(rawContent)
|
|
121
|
+
};
|
|
122
|
+
} catch (err) {
|
|
123
|
+
return {
|
|
124
|
+
format: 'text',
|
|
125
|
+
fileName: path.basename(spec.path),
|
|
126
|
+
content: rawContent
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
format: spec.format || 'text',
|
|
133
|
+
fileName: path.basename(spec.path),
|
|
134
|
+
content: rawContent
|
|
135
|
+
};
|
|
136
|
+
} catch (err) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
70
141
|
function writeJsonFileAbsolute(filePath, data, overwrite, options = {}) {
|
|
71
142
|
if (data === undefined) {
|
|
72
143
|
return 'failed';
|
|
@@ -101,6 +172,19 @@ function writeTextFileAbsolute(filePath, content, overwrite, options = {}) {
|
|
|
101
172
|
return 'success';
|
|
102
173
|
}
|
|
103
174
|
|
|
175
|
+
function writeNativeConfigAbsolute(spec, entry, overwrite) {
|
|
176
|
+
if (!spec?.path || !entry || entry.content === undefined) {
|
|
177
|
+
return 'failed';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const format = entry.format || spec.format || 'text';
|
|
181
|
+
if (format === 'json' && entry.content && typeof entry.content === 'object') {
|
|
182
|
+
return writeJsonFileAbsolute(spec.path, entry.content, overwrite, { mode: spec.mode });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return writeTextFileAbsolute(spec.path, String(entry.content), overwrite, { mode: spec.mode });
|
|
186
|
+
}
|
|
187
|
+
|
|
104
188
|
function getConfigFilePath() {
|
|
105
189
|
try {
|
|
106
190
|
const { getConfigFilePath: resolveConfigPath } = require('../../config/loader');
|
|
@@ -122,6 +206,7 @@ function buildExportReadme(exportData) {
|
|
|
122
206
|
- Agents / Skills / Commands
|
|
123
207
|
- 插件 (Plugins)
|
|
124
208
|
- MCP 服务器配置
|
|
209
|
+
- 各平台原生配置(Claude / Codex / Gemini / OpenCode)
|
|
125
210
|
- UI 配置(主题、面板显示、排序等)
|
|
126
211
|
- Prompts 预设
|
|
127
212
|
- 安全配置
|
|
@@ -181,6 +266,288 @@ function buildCommandContent(command) {
|
|
|
181
266
|
return `${lines.join('\n')}${body}`;
|
|
182
267
|
}
|
|
183
268
|
|
|
269
|
+
function buildAgentExportItem(agent, platform) {
|
|
270
|
+
return {
|
|
271
|
+
platform,
|
|
272
|
+
fileName: agent.fileName,
|
|
273
|
+
name: agent.name,
|
|
274
|
+
description: agent.description,
|
|
275
|
+
tools: agent.tools,
|
|
276
|
+
model: agent.model,
|
|
277
|
+
permissionMode: agent.permissionMode,
|
|
278
|
+
skills: agent.skills,
|
|
279
|
+
path: agent.path,
|
|
280
|
+
systemPrompt: agent.systemPrompt,
|
|
281
|
+
fullContent: agent.fullContent
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function buildCommandExportItem(command, platform) {
|
|
286
|
+
return {
|
|
287
|
+
platform,
|
|
288
|
+
name: command.name,
|
|
289
|
+
namespace: command.namespace,
|
|
290
|
+
description: command.description,
|
|
291
|
+
allowedTools: command.allowedTools,
|
|
292
|
+
argumentHint: command.argumentHint,
|
|
293
|
+
path: command.path,
|
|
294
|
+
body: command.body,
|
|
295
|
+
fullContent: command.fullContent
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function exportAgentsSnapshotByPlatform() {
|
|
300
|
+
return AGENT_PLATFORMS.reduce((result, platform) => {
|
|
301
|
+
try {
|
|
302
|
+
const agentsService = new AgentsService(platform);
|
|
303
|
+
const { agents: rawAgents = [] } = agentsService.listAgents();
|
|
304
|
+
result[platform] = rawAgents.map(agent => buildAgentExportItem(agent, platform));
|
|
305
|
+
} catch (err) {
|
|
306
|
+
console.warn(`[ConfigExport] Failed to export agents for ${platform}:`, err.message);
|
|
307
|
+
result[platform] = [];
|
|
308
|
+
}
|
|
309
|
+
return result;
|
|
310
|
+
}, {});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function exportCommandsSnapshotByPlatform() {
|
|
314
|
+
return COMMAND_PLATFORMS.reduce((result, platform) => {
|
|
315
|
+
try {
|
|
316
|
+
const commandsService = new CommandsService(platform);
|
|
317
|
+
const { commands: rawCommands = [] } = commandsService.listCommands();
|
|
318
|
+
result[platform] = rawCommands.map(command => buildCommandExportItem(command, platform));
|
|
319
|
+
} catch (err) {
|
|
320
|
+
console.warn(`[ConfigExport] Failed to export commands for ${platform}:`, err.message);
|
|
321
|
+
result[platform] = [];
|
|
322
|
+
}
|
|
323
|
+
return result;
|
|
324
|
+
}, {});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function normalizePlatformItems(legacyItems = [], byPlatform = {}, supportedPlatforms = [], defaultPlatform = 'claude') {
|
|
328
|
+
const result = Object.fromEntries(supportedPlatforms.map(platform => [platform, []]));
|
|
329
|
+
const structuredPlatforms = new Set();
|
|
330
|
+
|
|
331
|
+
for (const platform of supportedPlatforms) {
|
|
332
|
+
if (Array.isArray(byPlatform?.[platform])) {
|
|
333
|
+
result[platform] = byPlatform[platform].map(item => ({
|
|
334
|
+
...item,
|
|
335
|
+
platform: supportedPlatforms.includes(item?.platform) ? item.platform : platform
|
|
336
|
+
}));
|
|
337
|
+
structuredPlatforms.add(platform);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (!Array.isArray(legacyItems)) {
|
|
342
|
+
return result;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
for (const item of legacyItems) {
|
|
346
|
+
const platform = supportedPlatforms.includes(item?.platform) ? item.platform : defaultPlatform;
|
|
347
|
+
if (structuredPlatforms.has(platform)) {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
result[platform].push({ ...item, platform });
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return result;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function resolveAgentRelativePath(agent, platform) {
|
|
357
|
+
const pathCandidates = [agent?.path, agent?.fileName]
|
|
358
|
+
.filter(value => typeof value === 'string' && value.trim())
|
|
359
|
+
.map(value => value.trim());
|
|
360
|
+
|
|
361
|
+
for (const candidate of pathCandidates) {
|
|
362
|
+
if (path.extname(candidate)) {
|
|
363
|
+
return candidate;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const baseName = agent?.fileName || agent?.name;
|
|
368
|
+
if (!baseName) {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return platform === 'codex' ? `${baseName}.toml` : `${baseName}.md`;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function resolveCommandRelativePath(command) {
|
|
376
|
+
if (typeof command?.path === 'string' && command.path.trim()) {
|
|
377
|
+
return command.path.trim();
|
|
378
|
+
}
|
|
379
|
+
if (command?.namespace) {
|
|
380
|
+
return path.join(command.namespace, `${command.name}.md`);
|
|
381
|
+
}
|
|
382
|
+
return command?.name ? `${command.name}.md` : null;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function pickPrimaryChannel(channels = []) {
|
|
386
|
+
if (!Array.isArray(channels) || channels.length === 0) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
return channels.find(channel => channel.enabled !== false) || channels[0] || null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function findMatchingPersistedChannel(existingChannels = [], importedChannel = {}, extraMatchers = []) {
|
|
393
|
+
if (!Array.isArray(existingChannels) || existingChannels.length === 0 || !importedChannel) {
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (importedChannel.id) {
|
|
398
|
+
const exactMatch = existingChannels.find(channel => channel.id === importedChannel.id);
|
|
399
|
+
if (exactMatch) {
|
|
400
|
+
return exactMatch;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
for (const matcher of extraMatchers) {
|
|
405
|
+
const matchedChannel = existingChannels.find(channel => matcher(channel, importedChannel));
|
|
406
|
+
if (matchedChannel) {
|
|
407
|
+
return matchedChannel;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function sanitizeOpenCodeProviderId(value) {
|
|
415
|
+
return String(value || '')
|
|
416
|
+
.trim()
|
|
417
|
+
.toLowerCase()
|
|
418
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
419
|
+
.replace(/-+/g, '-')
|
|
420
|
+
.replace(/^-|-$/g, '') || 'channel';
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function buildOpenCodeNativeConfig(channels = []) {
|
|
424
|
+
const config = { provider: {} };
|
|
425
|
+
const usedProviderIds = new Set();
|
|
426
|
+
let defaultModelRef = '';
|
|
427
|
+
|
|
428
|
+
channels.forEach((channel) => {
|
|
429
|
+
const baseProviderId = sanitizeOpenCodeProviderId(channel.providerKey || channel.name);
|
|
430
|
+
let providerId = baseProviderId;
|
|
431
|
+
let suffix = 2;
|
|
432
|
+
while (usedProviderIds.has(providerId)) {
|
|
433
|
+
providerId = `${baseProviderId}-${suffix}`;
|
|
434
|
+
suffix += 1;
|
|
435
|
+
}
|
|
436
|
+
usedProviderIds.add(providerId);
|
|
437
|
+
|
|
438
|
+
const provider = {
|
|
439
|
+
npm: '@ai-sdk/openai-compatible',
|
|
440
|
+
name: channel.name || providerId,
|
|
441
|
+
options: {
|
|
442
|
+
baseURL: channel.baseUrl || '',
|
|
443
|
+
apiKey: channel.apiKey || ''
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
const models = {};
|
|
448
|
+
const allowedModels = Array.isArray(channel.allowedModels) ? channel.allowedModels : [];
|
|
449
|
+
const allModels = allowedModels.length > 0
|
|
450
|
+
? allowedModels
|
|
451
|
+
: [channel.model].filter(Boolean);
|
|
452
|
+
|
|
453
|
+
allModels.forEach((modelId) => {
|
|
454
|
+
const normalizedModel = String(modelId || '').trim();
|
|
455
|
+
if (!normalizedModel) return;
|
|
456
|
+
models[normalizedModel] = { name: normalizedModel };
|
|
457
|
+
if (!defaultModelRef && (channel.enabled !== false || !pickPrimaryChannel(channels))) {
|
|
458
|
+
defaultModelRef = `${providerId}/${normalizedModel}`;
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
if (Object.keys(models).length > 0) {
|
|
463
|
+
provider.models = models;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
config.provider[providerId] = provider;
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
const preferredChannel = pickPrimaryChannel(channels);
|
|
470
|
+
if (!defaultModelRef && preferredChannel?.model) {
|
|
471
|
+
const providerId = sanitizeOpenCodeProviderId(preferredChannel.providerKey || preferredChannel.name);
|
|
472
|
+
defaultModelRef = `${providerId}/${preferredChannel.model}`;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (defaultModelRef) {
|
|
476
|
+
config.model = defaultModelRef;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return config;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function syncImportedChannelsToNativeConfigs(importChannelsByType, nativeConfigs, overwrite) {
|
|
483
|
+
const hasNativeConfigFor = (platform) => !!nativeConfigs?.[platform] && Object.keys(nativeConfigs[platform]).length > 0;
|
|
484
|
+
|
|
485
|
+
try {
|
|
486
|
+
if (!hasNativeConfigFor('claude')) {
|
|
487
|
+
const primaryClaudeChannel = pickPrimaryChannel(importChannelsByType.claude);
|
|
488
|
+
const persistedClaudeChannel = findMatchingPersistedChannel(
|
|
489
|
+
channelsService.getAllChannels?.() || [],
|
|
490
|
+
primaryClaudeChannel,
|
|
491
|
+
[
|
|
492
|
+
(channel, imported) => channel.name === imported.name && channel.baseUrl === imported.baseUrl
|
|
493
|
+
]
|
|
494
|
+
);
|
|
495
|
+
if (persistedClaudeChannel?.id) {
|
|
496
|
+
ensureDir(path.dirname(NATIVE_PATHS.claude.settings));
|
|
497
|
+
channelsService.applyChannelToSettings(persistedClaudeChannel.id);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
} catch (err) {
|
|
501
|
+
console.warn('[ConfigImport] Claude native sync fallback failed:', err.message);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
try {
|
|
505
|
+
if (!hasNativeConfigFor('codex')) {
|
|
506
|
+
codexChannelsService.writeCodexConfigForMultiChannel(importChannelsByType.codex || []);
|
|
507
|
+
}
|
|
508
|
+
} catch (err) {
|
|
509
|
+
console.warn('[ConfigImport] Codex native sync fallback failed:', err.message);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
try {
|
|
513
|
+
if (!hasNativeConfigFor('gemini')) {
|
|
514
|
+
const primaryGeminiChannel = pickPrimaryChannel(importChannelsByType.gemini);
|
|
515
|
+
const persistedGeminiChannel = findMatchingPersistedChannel(
|
|
516
|
+
geminiChannelsService.getChannels?.().channels || [],
|
|
517
|
+
primaryGeminiChannel,
|
|
518
|
+
[
|
|
519
|
+
(channel, imported) => channel.name === imported.name && channel.baseUrl === imported.baseUrl
|
|
520
|
+
]
|
|
521
|
+
);
|
|
522
|
+
if (persistedGeminiChannel?.id) {
|
|
523
|
+
geminiChannelsService.applyChannelToSettings(persistedGeminiChannel.id);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
} catch (err) {
|
|
527
|
+
console.warn('[ConfigImport] Gemini native sync fallback failed:', err.message);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
try {
|
|
531
|
+
if (!hasNativeConfigFor('opencode')) {
|
|
532
|
+
const openCodeSpecs = getNativeConfigSpecs().opencode || {};
|
|
533
|
+
const preferredSpec = [
|
|
534
|
+
openCodeSpecs.opencodeJsonc,
|
|
535
|
+
openCodeSpecs.opencodeJson,
|
|
536
|
+
openCodeSpecs.configJson
|
|
537
|
+
].find(spec => spec?.path && fs.existsSync(spec.path))
|
|
538
|
+
|| openCodeSpecs.opencodeJson
|
|
539
|
+
|| openCodeSpecs.configJson;
|
|
540
|
+
|
|
541
|
+
if (preferredSpec?.path) {
|
|
542
|
+
const payload = buildOpenCodeNativeConfig(importChannelsByType.opencode || []);
|
|
543
|
+
writeTextFileAbsolute(preferredSpec.path, JSON.stringify(payload, null, 2), overwrite);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
} catch (err) {
|
|
547
|
+
console.warn('[ConfigImport] OpenCode native sync fallback failed:', err.message);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
184
551
|
function collectSkillFiles(baseDir) {
|
|
185
552
|
const files = [];
|
|
186
553
|
const stack = [baseDir];
|
|
@@ -274,8 +641,8 @@ function collectPluginFiles(pluginDir, basePath = '') {
|
|
|
274
641
|
return files;
|
|
275
642
|
}
|
|
276
643
|
|
|
277
|
-
function exportSkillsSnapshot() {
|
|
278
|
-
const skillService = new SkillService();
|
|
644
|
+
function exportSkillsSnapshot(platform = 'claude') {
|
|
645
|
+
const skillService = new SkillService(platform);
|
|
279
646
|
const installedSkills = skillService.getInstalledSkills();
|
|
280
647
|
const baseDir = skillService.installDir;
|
|
281
648
|
|
|
@@ -286,6 +653,7 @@ function exportSkillsSnapshot() {
|
|
|
286
653
|
return null;
|
|
287
654
|
}
|
|
288
655
|
return {
|
|
656
|
+
platform,
|
|
289
657
|
directory,
|
|
290
658
|
name: skill.name || directory,
|
|
291
659
|
description: skill.description || '',
|
|
@@ -294,6 +662,36 @@ function exportSkillsSnapshot() {
|
|
|
294
662
|
}).filter(Boolean);
|
|
295
663
|
}
|
|
296
664
|
|
|
665
|
+
function exportSkillsSnapshotByPlatform() {
|
|
666
|
+
return SKILL_PLATFORMS.reduce((result, platform) => {
|
|
667
|
+
try {
|
|
668
|
+
result[platform] = exportSkillsSnapshot(platform);
|
|
669
|
+
} catch (err) {
|
|
670
|
+
console.warn(`[ConfigExport] Failed to export skills for ${platform}:`, err.message);
|
|
671
|
+
result[platform] = [];
|
|
672
|
+
}
|
|
673
|
+
return result;
|
|
674
|
+
}, {});
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
function exportNativeConfigs() {
|
|
678
|
+
const specs = getNativeConfigSpecs();
|
|
679
|
+
return Object.entries(specs).reduce((result, [platform, platformSpecs]) => {
|
|
680
|
+
const exportedEntries = Object.entries(platformSpecs).reduce((entries, [key, spec]) => {
|
|
681
|
+
const snapshot = readNativeConfigSnapshot(spec);
|
|
682
|
+
if (snapshot) {
|
|
683
|
+
entries[key] = snapshot;
|
|
684
|
+
}
|
|
685
|
+
return entries;
|
|
686
|
+
}, {});
|
|
687
|
+
|
|
688
|
+
if (Object.keys(exportedEntries).length > 0) {
|
|
689
|
+
result[platform] = exportedEntries;
|
|
690
|
+
}
|
|
691
|
+
return result;
|
|
692
|
+
}, {});
|
|
693
|
+
}
|
|
694
|
+
|
|
297
695
|
function exportLegacyPlugins() {
|
|
298
696
|
const plugins = [];
|
|
299
697
|
|
|
@@ -414,6 +812,14 @@ function writeTextFile(baseDir, relativePath, content, overwrite) {
|
|
|
414
812
|
return 'success';
|
|
415
813
|
}
|
|
416
814
|
|
|
815
|
+
function getAllChannelsByType() {
|
|
816
|
+
const claude = channelsService.getAllChannels() || [];
|
|
817
|
+
const codex = codexChannelsService.getChannels()?.channels || [];
|
|
818
|
+
const gemini = geminiChannelsService.getChannels()?.channels || [];
|
|
819
|
+
const opencode = opencodeChannelsService.getChannels()?.channels || [];
|
|
820
|
+
return { claude, codex, gemini, opencode };
|
|
821
|
+
}
|
|
822
|
+
|
|
417
823
|
/**
|
|
418
824
|
* 导出所有配置为JSON
|
|
419
825
|
* @returns {Object} 配置导出对象
|
|
@@ -424,8 +830,9 @@ function exportAllConfigs() {
|
|
|
424
830
|
const allConfigTemplates = configTemplatesService.getAllTemplates();
|
|
425
831
|
const customConfigTemplates = allConfigTemplates.filter(t => !t.isBuiltin);
|
|
426
832
|
|
|
427
|
-
//
|
|
428
|
-
const
|
|
833
|
+
// 获取所有频道配置(向后兼容:channels 仍保留 Claude 渠道)
|
|
834
|
+
const channelsByType = getAllChannelsByType();
|
|
835
|
+
const channels = channelsByType.claude || [];
|
|
429
836
|
|
|
430
837
|
// 获取工作区配置
|
|
431
838
|
const workspaceService = require('./workspace-service');
|
|
@@ -435,38 +842,13 @@ function exportAllConfigs() {
|
|
|
435
842
|
const favoritesService = require('./favorites');
|
|
436
843
|
const favorites = favoritesService.loadFavorites();
|
|
437
844
|
|
|
438
|
-
// 获取 Agents
|
|
439
|
-
const
|
|
440
|
-
const
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
tools: agent.tools,
|
|
446
|
-
model: agent.model,
|
|
447
|
-
permissionMode: agent.permissionMode,
|
|
448
|
-
skills: agent.skills,
|
|
449
|
-
path: agent.path,
|
|
450
|
-
systemPrompt: agent.systemPrompt,
|
|
451
|
-
fullContent: agent.fullContent
|
|
452
|
-
}));
|
|
453
|
-
|
|
454
|
-
// 获取 Skills 配置
|
|
455
|
-
const skills = exportSkillsSnapshot();
|
|
456
|
-
|
|
457
|
-
// 获取 Commands 配置
|
|
458
|
-
const commandsService = new CommandsService();
|
|
459
|
-
const { commands: rawCommands } = commandsService.listCommands();
|
|
460
|
-
const commands = rawCommands.map(command => ({
|
|
461
|
-
name: command.name,
|
|
462
|
-
namespace: command.namespace,
|
|
463
|
-
description: command.description,
|
|
464
|
-
allowedTools: command.allowedTools,
|
|
465
|
-
argumentHint: command.argumentHint,
|
|
466
|
-
path: command.path,
|
|
467
|
-
body: command.body,
|
|
468
|
-
fullContent: command.fullContent
|
|
469
|
-
}));
|
|
845
|
+
// 获取 Agents / Skills / Commands 配置(多平台)
|
|
846
|
+
const agentsByPlatform = exportAgentsSnapshotByPlatform();
|
|
847
|
+
const skillsByPlatform = exportSkillsSnapshotByPlatform();
|
|
848
|
+
const commandsByPlatform = exportCommandsSnapshotByPlatform();
|
|
849
|
+
const agents = agentsByPlatform.claude || [];
|
|
850
|
+
const skills = skillsByPlatform.claude || [];
|
|
851
|
+
const commands = commandsByPlatform.claude || [];
|
|
470
852
|
|
|
471
853
|
// 获取 MCP 配置
|
|
472
854
|
const mcpService = require('./mcp-service');
|
|
@@ -474,6 +856,7 @@ function exportAllConfigs() {
|
|
|
474
856
|
|
|
475
857
|
// 获取 Plugins 配置
|
|
476
858
|
const plugins = exportPluginsSnapshot();
|
|
859
|
+
const nativeConfigs = exportNativeConfigs();
|
|
477
860
|
|
|
478
861
|
// 读取 Markdown 配置文件
|
|
479
862
|
const { PATHS } = require('../../config/paths');
|
|
@@ -514,11 +897,15 @@ function exportAllConfigs() {
|
|
|
514
897
|
data: {
|
|
515
898
|
configTemplates: customConfigTemplates,
|
|
516
899
|
channels: channels || [],
|
|
900
|
+
channelsByType,
|
|
517
901
|
workspaces: workspaces || { workspaces: [] },
|
|
518
902
|
favorites: favorites || { favorites: [] },
|
|
519
903
|
agents: agents || [],
|
|
904
|
+
agentsByPlatform,
|
|
520
905
|
skills: skills || [],
|
|
906
|
+
skillsByPlatform,
|
|
521
907
|
commands: commands || [],
|
|
908
|
+
commandsByPlatform,
|
|
522
909
|
mcpServers: mcpServers || [],
|
|
523
910
|
plugins: plugins || [],
|
|
524
911
|
markdownFiles: markdownFiles,
|
|
@@ -526,6 +913,7 @@ function exportAllConfigs() {
|
|
|
526
913
|
prompts: prompts,
|
|
527
914
|
security: security,
|
|
528
915
|
appConfig: appConfig,
|
|
916
|
+
nativeConfigs,
|
|
529
917
|
claudeHooks: claudeHooks
|
|
530
918
|
}
|
|
531
919
|
};
|
|
@@ -590,6 +978,7 @@ async function importConfigs(importData, options = {}) {
|
|
|
590
978
|
prompts: { success: 0, failed: 0, skipped: 0 },
|
|
591
979
|
security: { success: 0, failed: 0, skipped: 0 },
|
|
592
980
|
appConfig: { success: 0, failed: 0, skipped: 0 },
|
|
981
|
+
nativeConfigs: { success: 0, failed: 0, skipped: 0 },
|
|
593
982
|
claudeHooks: { success: 0, failed: 0, skipped: 0 }
|
|
594
983
|
};
|
|
595
984
|
|
|
@@ -602,20 +991,39 @@ async function importConfigs(importData, options = {}) {
|
|
|
602
991
|
const {
|
|
603
992
|
configTemplates = [],
|
|
604
993
|
channels = [],
|
|
994
|
+
channelsByType = null,
|
|
605
995
|
workspaces = null,
|
|
606
996
|
favorites = null,
|
|
607
997
|
agents = [],
|
|
998
|
+
agentsByPlatform = {},
|
|
608
999
|
skills = [],
|
|
1000
|
+
skillsByPlatform = {},
|
|
609
1001
|
commands = [],
|
|
1002
|
+
commandsByPlatform = {},
|
|
610
1003
|
mcpServers = [],
|
|
611
1004
|
markdownFiles = {},
|
|
612
1005
|
uiConfig = null,
|
|
613
1006
|
prompts = null,
|
|
614
1007
|
security = null,
|
|
615
1008
|
appConfig = null,
|
|
1009
|
+
nativeConfigs = {},
|
|
616
1010
|
claudeHooks = null
|
|
617
1011
|
} = importData.data;
|
|
618
1012
|
|
|
1013
|
+
const importAgentsByPlatform = normalizePlatformItems(agents, agentsByPlatform, AGENT_PLATFORMS);
|
|
1014
|
+
const importSkillsByPlatform = normalizePlatformItems(skills, skillsByPlatform, SKILL_PLATFORMS);
|
|
1015
|
+
const importCommandsByPlatform = normalizePlatformItems(commands, commandsByPlatform, COMMAND_PLATFORMS);
|
|
1016
|
+
|
|
1017
|
+
const hasTypedChannels = channelsByType && typeof channelsByType === 'object';
|
|
1018
|
+
const importChannelsByType = {
|
|
1019
|
+
claude: hasTypedChannels && Array.isArray(channelsByType.claude)
|
|
1020
|
+
? channelsByType.claude
|
|
1021
|
+
: (Array.isArray(channels) ? channels : []),
|
|
1022
|
+
codex: hasTypedChannels && Array.isArray(channelsByType.codex) ? channelsByType.codex : [],
|
|
1023
|
+
gemini: hasTypedChannels && Array.isArray(channelsByType.gemini) ? channelsByType.gemini : [],
|
|
1024
|
+
opencode: hasTypedChannels && Array.isArray(channelsByType.opencode) ? channelsByType.opencode : []
|
|
1025
|
+
};
|
|
1026
|
+
|
|
619
1027
|
// 导入配置模板
|
|
620
1028
|
for (const template of configTemplates) {
|
|
621
1029
|
try {
|
|
@@ -643,29 +1051,72 @@ async function importConfigs(importData, options = {}) {
|
|
|
643
1051
|
}
|
|
644
1052
|
}
|
|
645
1053
|
|
|
646
|
-
//
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
1054
|
+
// 导入频道配置(兼容旧结构 channels 和新结构 channelsByType)
|
|
1055
|
+
const importTypedChannels = (type, service, createChannel, findExisting = null) => {
|
|
1056
|
+
const sourceChannels = importChannelsByType[type];
|
|
1057
|
+
for (const channel of sourceChannels) {
|
|
1058
|
+
try {
|
|
1059
|
+
const existingChannels = service.getChannels
|
|
1060
|
+
? (service.getChannels()?.channels || [])
|
|
1061
|
+
: (service.getAllChannels?.() || []);
|
|
1062
|
+
const existing = typeof findExisting === 'function'
|
|
1063
|
+
? findExisting(existingChannels, channel)
|
|
1064
|
+
: existingChannels.find(c => c.id === channel.id);
|
|
1065
|
+
|
|
1066
|
+
if (existing && !overwrite) {
|
|
1067
|
+
results.channels.skipped++;
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
656
1070
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
1071
|
+
if (existing && overwrite) {
|
|
1072
|
+
service.updateChannel(existing.id, { ...channel, id: existing.id });
|
|
1073
|
+
} else {
|
|
1074
|
+
createChannel(channel);
|
|
1075
|
+
}
|
|
1076
|
+
results.channels.success++;
|
|
1077
|
+
} catch (err) {
|
|
1078
|
+
console.error(`[ConfigImport] 导入${type}频道失败: ${channel.name}`, err);
|
|
1079
|
+
results.channels.failed++;
|
|
662
1080
|
}
|
|
663
|
-
results.channels.success++;
|
|
664
|
-
} catch (err) {
|
|
665
|
-
console.error(`[ConfigImport] 导入频道失败: ${channel.name}`, err);
|
|
666
|
-
results.channels.failed++;
|
|
667
1081
|
}
|
|
668
|
-
}
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
importTypedChannels('claude', channelsService, channel => {
|
|
1085
|
+
const { name, baseUrl, apiKey, websiteUrl, ...extraConfig } = channel;
|
|
1086
|
+
channelsService.createChannel(name, baseUrl, apiKey, websiteUrl, extraConfig);
|
|
1087
|
+
}, (existingChannels, channel) => existingChannels.find(c => c.id === channel.id));
|
|
1088
|
+
|
|
1089
|
+
importTypedChannels('codex', codexChannelsService, channel => {
|
|
1090
|
+
const {
|
|
1091
|
+
name,
|
|
1092
|
+
providerKey,
|
|
1093
|
+
baseUrl,
|
|
1094
|
+
apiKey,
|
|
1095
|
+
wireApi,
|
|
1096
|
+
...extraConfig
|
|
1097
|
+
} = channel;
|
|
1098
|
+
codexChannelsService.createChannel(name, providerKey, baseUrl, apiKey, wireApi, extraConfig);
|
|
1099
|
+
}, (existingChannels, channel) => existingChannels.find(c =>
|
|
1100
|
+
(channel.id && c.id === channel.id) ||
|
|
1101
|
+
(channel.providerKey && c.providerKey === channel.providerKey)
|
|
1102
|
+
));
|
|
1103
|
+
|
|
1104
|
+
importTypedChannels('gemini', geminiChannelsService, channel => {
|
|
1105
|
+
const { name, baseUrl, apiKey, model, ...extraConfig } = channel;
|
|
1106
|
+
geminiChannelsService.createChannel(name, baseUrl, apiKey, model, extraConfig);
|
|
1107
|
+
}, (existingChannels, channel) => existingChannels.find(c =>
|
|
1108
|
+
(channel.id && c.id === channel.id) ||
|
|
1109
|
+
(channel.name && c.name === channel.name)
|
|
1110
|
+
));
|
|
1111
|
+
|
|
1112
|
+
importTypedChannels('opencode', opencodeChannelsService, channel => {
|
|
1113
|
+
const { name, baseUrl, apiKey, ...extraConfig } = channel;
|
|
1114
|
+
opencodeChannelsService.createChannel(name, baseUrl, apiKey, extraConfig);
|
|
1115
|
+
}, (existingChannels, channel) => existingChannels.find(c =>
|
|
1116
|
+
(channel.id && c.id === channel.id) ||
|
|
1117
|
+
(channel.providerKey && c.providerKey === channel.providerKey) ||
|
|
1118
|
+
(channel.name && channel.baseUrl && c.name === channel.name && c.baseUrl === channel.baseUrl)
|
|
1119
|
+
));
|
|
669
1120
|
|
|
670
1121
|
// 导入工作区配置
|
|
671
1122
|
if (workspaces && overwrite) {
|
|
@@ -692,26 +1143,28 @@ async function importConfigs(importData, options = {}) {
|
|
|
692
1143
|
}
|
|
693
1144
|
}
|
|
694
1145
|
|
|
695
|
-
// 导入 Agents
|
|
696
|
-
if (
|
|
1146
|
+
// 导入 Agents(多平台)
|
|
1147
|
+
if (AGENT_PLATFORMS.some(platform => importAgentsByPlatform[platform]?.length > 0)) {
|
|
697
1148
|
try {
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
const
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
1149
|
+
for (const platform of AGENT_PLATFORMS) {
|
|
1150
|
+
const platformAgents = importAgentsByPlatform[platform] || [];
|
|
1151
|
+
if (platformAgents.length === 0) continue;
|
|
1152
|
+
|
|
1153
|
+
const agentsService = new AgentsService(platform);
|
|
1154
|
+
const baseDir = agentsService.userAgentsDir;
|
|
1155
|
+
|
|
1156
|
+
for (const agent of platformAgents) {
|
|
1157
|
+
const filePath = resolveAgentRelativePath(agent, platform);
|
|
1158
|
+
const content = agent.fullContent || agent.content || buildAgentContent(agent);
|
|
1159
|
+
const status = filePath ? writeTextFile(baseDir, filePath, content, overwrite) : 'failed';
|
|
1160
|
+
|
|
1161
|
+
if (status === 'success') {
|
|
1162
|
+
results.agents.success++;
|
|
1163
|
+
} else if (status === 'skipped') {
|
|
1164
|
+
results.agents.skipped++;
|
|
1165
|
+
} else {
|
|
1166
|
+
results.agents.failed++;
|
|
1167
|
+
}
|
|
715
1168
|
}
|
|
716
1169
|
}
|
|
717
1170
|
} catch (err) {
|
|
@@ -719,56 +1172,61 @@ async function importConfigs(importData, options = {}) {
|
|
|
719
1172
|
}
|
|
720
1173
|
}
|
|
721
1174
|
|
|
722
|
-
// 导入 Skills
|
|
723
|
-
if (
|
|
1175
|
+
// 导入 Skills(多平台)
|
|
1176
|
+
if (SKILL_PLATFORMS.some(platform => importSkillsByPlatform[platform]?.length > 0)) {
|
|
724
1177
|
try {
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
if (!overwrite) {
|
|
739
|
-
results.skills.skipped++;
|
|
1178
|
+
for (const platform of SKILL_PLATFORMS) {
|
|
1179
|
+
const platformSkills = importSkillsByPlatform[platform] || [];
|
|
1180
|
+
if (platformSkills.length === 0) continue;
|
|
1181
|
+
|
|
1182
|
+
const skillService = new SkillService(platform);
|
|
1183
|
+
const baseDir = skillService.installDir;
|
|
1184
|
+
ensureDir(baseDir);
|
|
1185
|
+
|
|
1186
|
+
for (const skill of platformSkills) {
|
|
1187
|
+
const directory = skill.directory;
|
|
1188
|
+
const skillDir = resolveSafePath(baseDir, directory);
|
|
1189
|
+
if (!skillDir) {
|
|
1190
|
+
results.skills.failed++;
|
|
740
1191
|
continue;
|
|
741
1192
|
}
|
|
742
|
-
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
ensureDir(skillDir);
|
|
746
|
-
const files = Array.isArray(skill.files) ? skill.files : [];
|
|
747
|
-
let failed = false;
|
|
748
1193
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
1194
|
+
if (fs.existsSync(skillDir)) {
|
|
1195
|
+
if (!overwrite) {
|
|
1196
|
+
results.skills.skipped++;
|
|
1197
|
+
continue;
|
|
1198
|
+
}
|
|
1199
|
+
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
754
1200
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
1201
|
+
|
|
1202
|
+
ensureDir(skillDir);
|
|
1203
|
+
const files = Array.isArray(skill.files) ? skill.files : [];
|
|
1204
|
+
let failed = false;
|
|
1205
|
+
|
|
1206
|
+
for (const file of files) {
|
|
1207
|
+
const filePath = resolveSafePath(skillDir, file.path);
|
|
1208
|
+
if (!filePath) {
|
|
1209
|
+
failed = true;
|
|
1210
|
+
break;
|
|
1211
|
+
}
|
|
1212
|
+
ensureDir(path.dirname(filePath));
|
|
1213
|
+
try {
|
|
1214
|
+
if (file.encoding === SKILL_FILE_ENCODING) {
|
|
1215
|
+
fs.writeFileSync(filePath, Buffer.from(file.content || '', SKILL_FILE_ENCODING));
|
|
1216
|
+
} else {
|
|
1217
|
+
fs.writeFileSync(filePath, file.content || '', file.encoding || 'utf8');
|
|
1218
|
+
}
|
|
1219
|
+
} catch (err) {
|
|
1220
|
+
failed = true;
|
|
1221
|
+
break;
|
|
761
1222
|
}
|
|
762
|
-
} catch (err) {
|
|
763
|
-
failed = true;
|
|
764
|
-
break;
|
|
765
1223
|
}
|
|
766
|
-
}
|
|
767
1224
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
1225
|
+
if (failed || files.length === 0) {
|
|
1226
|
+
results.skills.failed++;
|
|
1227
|
+
} else {
|
|
1228
|
+
results.skills.success++;
|
|
1229
|
+
}
|
|
772
1230
|
}
|
|
773
1231
|
}
|
|
774
1232
|
} catch (err) {
|
|
@@ -913,27 +1371,28 @@ async function importConfigs(importData, options = {}) {
|
|
|
913
1371
|
}
|
|
914
1372
|
}
|
|
915
1373
|
|
|
916
|
-
// 导入 Commands
|
|
917
|
-
if (
|
|
1374
|
+
// 导入 Commands(多平台)
|
|
1375
|
+
if (COMMAND_PLATFORMS.some(platform => importCommandsByPlatform[platform]?.length > 0)) {
|
|
918
1376
|
try {
|
|
919
|
-
const
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
const
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1377
|
+
for (const platform of COMMAND_PLATFORMS) {
|
|
1378
|
+
const platformCommands = importCommandsByPlatform[platform] || [];
|
|
1379
|
+
if (platformCommands.length === 0) continue;
|
|
1380
|
+
|
|
1381
|
+
const commandsService = new CommandsService(platform);
|
|
1382
|
+
const baseDir = commandsService.userCommandsDir;
|
|
1383
|
+
|
|
1384
|
+
for (const command of platformCommands) {
|
|
1385
|
+
const relativePath = resolveCommandRelativePath(command);
|
|
1386
|
+
const content = command.fullContent || command.content || buildCommandContent(command);
|
|
1387
|
+
|
|
1388
|
+
const status = relativePath ? writeTextFile(baseDir, relativePath, content, overwrite) : 'failed';
|
|
1389
|
+
if (status === 'success') {
|
|
1390
|
+
results.commands.success++;
|
|
1391
|
+
} else if (status === 'skipped') {
|
|
1392
|
+
results.commands.skipped++;
|
|
1393
|
+
} else {
|
|
1394
|
+
results.commands.failed++;
|
|
1395
|
+
}
|
|
937
1396
|
}
|
|
938
1397
|
}
|
|
939
1398
|
} catch (err) {
|
|
@@ -1006,6 +1465,12 @@ async function importConfigs(importData, options = {}) {
|
|
|
1006
1465
|
try {
|
|
1007
1466
|
const status = writeJsonFileAbsolute(CC_PROMPTS_PATH, prompts, overwrite);
|
|
1008
1467
|
if (status === 'success') {
|
|
1468
|
+
const promptsService = require('./prompts-service');
|
|
1469
|
+
if (prompts.activePresetId && prompts.presets?.[prompts.activePresetId]) {
|
|
1470
|
+
await promptsService.activatePreset(prompts.activePresetId);
|
|
1471
|
+
} else if (overwrite) {
|
|
1472
|
+
await promptsService.deactivatePrompt();
|
|
1473
|
+
}
|
|
1009
1474
|
results.prompts.success++;
|
|
1010
1475
|
} else if (status === 'skipped') {
|
|
1011
1476
|
results.prompts.skipped++;
|
|
@@ -1052,6 +1517,39 @@ async function importConfigs(importData, options = {}) {
|
|
|
1052
1517
|
}
|
|
1053
1518
|
}
|
|
1054
1519
|
|
|
1520
|
+
// 导入各平台原生配置
|
|
1521
|
+
if (nativeConfigs && typeof nativeConfigs === 'object' && Object.keys(nativeConfigs).length > 0) {
|
|
1522
|
+
const nativeConfigSpecs = getNativeConfigSpecs();
|
|
1523
|
+
|
|
1524
|
+
for (const [platform, platformEntries] of Object.entries(nativeConfigs)) {
|
|
1525
|
+
const platformSpecs = nativeConfigSpecs[platform];
|
|
1526
|
+
if (!platformSpecs || !platformEntries || typeof platformEntries !== 'object') {
|
|
1527
|
+
continue;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
for (const [key, entry] of Object.entries(platformEntries)) {
|
|
1531
|
+
const spec = platformSpecs[key];
|
|
1532
|
+
if (!spec) {
|
|
1533
|
+
continue;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
try {
|
|
1537
|
+
const status = writeNativeConfigAbsolute(spec, entry, overwrite);
|
|
1538
|
+
if (status === 'success') {
|
|
1539
|
+
results.nativeConfigs.success++;
|
|
1540
|
+
} else if (status === 'skipped') {
|
|
1541
|
+
results.nativeConfigs.skipped++;
|
|
1542
|
+
} else {
|
|
1543
|
+
results.nativeConfigs.failed++;
|
|
1544
|
+
}
|
|
1545
|
+
} catch (err) {
|
|
1546
|
+
console.error(`[ConfigImport] 导入 ${platform}.${key} 原生配置失败:`, err);
|
|
1547
|
+
results.nativeConfigs.failed++;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1055
1553
|
// 导入 Claude Hooks 配置
|
|
1056
1554
|
if (claudeHooks && typeof claudeHooks === 'object') {
|
|
1057
1555
|
let didWrite = false;
|
|
@@ -1105,6 +1603,8 @@ async function importConfigs(importData, options = {}) {
|
|
|
1105
1603
|
}
|
|
1106
1604
|
}
|
|
1107
1605
|
|
|
1606
|
+
syncImportedChannelsToNativeConfigs(importChannelsByType, nativeConfigs, overwrite);
|
|
1607
|
+
|
|
1108
1608
|
return {
|
|
1109
1609
|
success: true,
|
|
1110
1610
|
results,
|
|
@@ -1141,6 +1641,7 @@ function generateImportSummary(results) {
|
|
|
1141
1641
|
{ key: 'prompts', label: 'Prompts' },
|
|
1142
1642
|
{ key: 'security', label: '安全配置' },
|
|
1143
1643
|
{ key: 'appConfig', label: '高级配置' },
|
|
1644
|
+
{ key: 'nativeConfigs', label: '原生配置' },
|
|
1144
1645
|
{ key: 'claudeHooks', label: 'Claude Hooks' }
|
|
1145
1646
|
];
|
|
1146
1647
|
|