coding-tool-x 3.3.6 → 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-TtaduRqL.js → Analytics-IW6eAy9u.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-BP2lLBMN.js → ConfigTemplates-BPtkTMSc.js} +1 -1
- package/dist/web/assets/{Home-CbbyopS-.js → Home-obifg_9E.js} +1 -1
- package/dist/web/assets/{PluginManager-HmISlyMK.js → PluginManager-BGx9MSDV.js} +1 -1
- package/dist/web/assets/{ProjectList-DoN8Hjbu.js → ProjectList-BCn-mrCx.js} +1 -1
- package/dist/web/assets/{SessionList-Da8BYzNi.js → SessionList-CzLfebJQ.js} +1 -1
- package/dist/web/assets/{SkillManager-DqLAXh9o.js → SkillManager-CXz2vBQx.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-B_TxOgPW.js → WorkspaceManager-CHtgMfKc.js} +1 -1
- package/dist/web/assets/index-C7LPdVsN.js +2 -0
- package/dist/web/assets/{index-CsWInMQV.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/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/mcp.js +26 -4
- package/src/server/index.js +25 -2
- package/src/server/services/config-export-service.js +550 -116
- package/src/server/services/mcp-client.js +70 -13
- package/src/server/services/mcp-service.js +41 -13
- package/src/server/services/model-detector.js +1 -0
- package/src/utils/port-helper.js +87 -2
- package/dist/web/assets/index-By3mDEvx.js +0 -2
|
@@ -16,7 +16,7 @@ const { CommandsService } = require('./commands-service');
|
|
|
16
16
|
const { SkillService } = require('./skill-service');
|
|
17
17
|
const { PATHS, NATIVE_PATHS } = require('../../config/paths');
|
|
18
18
|
|
|
19
|
-
const CONFIG_VERSION = '1.
|
|
19
|
+
const CONFIG_VERSION = '1.3.0';
|
|
20
20
|
const SKILL_FILE_ENCODING = 'base64';
|
|
21
21
|
const SKILL_IGNORE_DIRS = new Set(['.git']);
|
|
22
22
|
const SKILL_IGNORE_FILES = new Set(['.DS_Store']);
|
|
@@ -44,6 +44,41 @@ const CC_PROMPTS_PATH = path.join(CC_TOOL_DIR, 'prompts.json');
|
|
|
44
44
|
const CC_SECURITY_PATH = path.join(CC_TOOL_DIR, 'security.json');
|
|
45
45
|
const LEGACY_UI_CONFIG_PATH = path.join(LEGACY_CC_TOOL_DIR, 'ui-config.json');
|
|
46
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
|
+
}
|
|
47
82
|
|
|
48
83
|
function ensureDir(dirPath) {
|
|
49
84
|
if (!fs.existsSync(dirPath)) {
|
|
@@ -70,6 +105,39 @@ function readTextFileSafe(filePath) {
|
|
|
70
105
|
}
|
|
71
106
|
}
|
|
72
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
|
+
|
|
73
141
|
function writeJsonFileAbsolute(filePath, data, overwrite, options = {}) {
|
|
74
142
|
if (data === undefined) {
|
|
75
143
|
return 'failed';
|
|
@@ -104,6 +172,19 @@ function writeTextFileAbsolute(filePath, content, overwrite, options = {}) {
|
|
|
104
172
|
return 'success';
|
|
105
173
|
}
|
|
106
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
|
+
|
|
107
188
|
function getConfigFilePath() {
|
|
108
189
|
try {
|
|
109
190
|
const { getConfigFilePath: resolveConfigPath } = require('../../config/loader');
|
|
@@ -125,6 +206,7 @@ function buildExportReadme(exportData) {
|
|
|
125
206
|
- Agents / Skills / Commands
|
|
126
207
|
- 插件 (Plugins)
|
|
127
208
|
- MCP 服务器配置
|
|
209
|
+
- 各平台原生配置(Claude / Codex / Gemini / OpenCode)
|
|
128
210
|
- UI 配置(主题、面板显示、排序等)
|
|
129
211
|
- Prompts 预设
|
|
130
212
|
- 安全配置
|
|
@@ -184,6 +266,288 @@ function buildCommandContent(command) {
|
|
|
184
266
|
return `${lines.join('\n')}${body}`;
|
|
185
267
|
}
|
|
186
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
|
+
|
|
187
551
|
function collectSkillFiles(baseDir) {
|
|
188
552
|
const files = [];
|
|
189
553
|
const stack = [baseDir];
|
|
@@ -277,8 +641,8 @@ function collectPluginFiles(pluginDir, basePath = '') {
|
|
|
277
641
|
return files;
|
|
278
642
|
}
|
|
279
643
|
|
|
280
|
-
function exportSkillsSnapshot() {
|
|
281
|
-
const skillService = new SkillService();
|
|
644
|
+
function exportSkillsSnapshot(platform = 'claude') {
|
|
645
|
+
const skillService = new SkillService(platform);
|
|
282
646
|
const installedSkills = skillService.getInstalledSkills();
|
|
283
647
|
const baseDir = skillService.installDir;
|
|
284
648
|
|
|
@@ -289,6 +653,7 @@ function exportSkillsSnapshot() {
|
|
|
289
653
|
return null;
|
|
290
654
|
}
|
|
291
655
|
return {
|
|
656
|
+
platform,
|
|
292
657
|
directory,
|
|
293
658
|
name: skill.name || directory,
|
|
294
659
|
description: skill.description || '',
|
|
@@ -297,6 +662,36 @@ function exportSkillsSnapshot() {
|
|
|
297
662
|
}).filter(Boolean);
|
|
298
663
|
}
|
|
299
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
|
+
|
|
300
695
|
function exportLegacyPlugins() {
|
|
301
696
|
const plugins = [];
|
|
302
697
|
|
|
@@ -447,38 +842,13 @@ function exportAllConfigs() {
|
|
|
447
842
|
const favoritesService = require('./favorites');
|
|
448
843
|
const favorites = favoritesService.loadFavorites();
|
|
449
844
|
|
|
450
|
-
// 获取 Agents
|
|
451
|
-
const
|
|
452
|
-
const
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
tools: agent.tools,
|
|
458
|
-
model: agent.model,
|
|
459
|
-
permissionMode: agent.permissionMode,
|
|
460
|
-
skills: agent.skills,
|
|
461
|
-
path: agent.path,
|
|
462
|
-
systemPrompt: agent.systemPrompt,
|
|
463
|
-
fullContent: agent.fullContent
|
|
464
|
-
}));
|
|
465
|
-
|
|
466
|
-
// 获取 Skills 配置
|
|
467
|
-
const skills = exportSkillsSnapshot();
|
|
468
|
-
|
|
469
|
-
// 获取 Commands 配置
|
|
470
|
-
const commandsService = new CommandsService();
|
|
471
|
-
const { commands: rawCommands } = commandsService.listCommands();
|
|
472
|
-
const commands = rawCommands.map(command => ({
|
|
473
|
-
name: command.name,
|
|
474
|
-
namespace: command.namespace,
|
|
475
|
-
description: command.description,
|
|
476
|
-
allowedTools: command.allowedTools,
|
|
477
|
-
argumentHint: command.argumentHint,
|
|
478
|
-
path: command.path,
|
|
479
|
-
body: command.body,
|
|
480
|
-
fullContent: command.fullContent
|
|
481
|
-
}));
|
|
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 || [];
|
|
482
852
|
|
|
483
853
|
// 获取 MCP 配置
|
|
484
854
|
const mcpService = require('./mcp-service');
|
|
@@ -486,6 +856,7 @@ function exportAllConfigs() {
|
|
|
486
856
|
|
|
487
857
|
// 获取 Plugins 配置
|
|
488
858
|
const plugins = exportPluginsSnapshot();
|
|
859
|
+
const nativeConfigs = exportNativeConfigs();
|
|
489
860
|
|
|
490
861
|
// 读取 Markdown 配置文件
|
|
491
862
|
const { PATHS } = require('../../config/paths');
|
|
@@ -530,8 +901,11 @@ function exportAllConfigs() {
|
|
|
530
901
|
workspaces: workspaces || { workspaces: [] },
|
|
531
902
|
favorites: favorites || { favorites: [] },
|
|
532
903
|
agents: agents || [],
|
|
904
|
+
agentsByPlatform,
|
|
533
905
|
skills: skills || [],
|
|
906
|
+
skillsByPlatform,
|
|
534
907
|
commands: commands || [],
|
|
908
|
+
commandsByPlatform,
|
|
535
909
|
mcpServers: mcpServers || [],
|
|
536
910
|
plugins: plugins || [],
|
|
537
911
|
markdownFiles: markdownFiles,
|
|
@@ -539,6 +913,7 @@ function exportAllConfigs() {
|
|
|
539
913
|
prompts: prompts,
|
|
540
914
|
security: security,
|
|
541
915
|
appConfig: appConfig,
|
|
916
|
+
nativeConfigs,
|
|
542
917
|
claudeHooks: claudeHooks
|
|
543
918
|
}
|
|
544
919
|
};
|
|
@@ -603,6 +978,7 @@ async function importConfigs(importData, options = {}) {
|
|
|
603
978
|
prompts: { success: 0, failed: 0, skipped: 0 },
|
|
604
979
|
security: { success: 0, failed: 0, skipped: 0 },
|
|
605
980
|
appConfig: { success: 0, failed: 0, skipped: 0 },
|
|
981
|
+
nativeConfigs: { success: 0, failed: 0, skipped: 0 },
|
|
606
982
|
claudeHooks: { success: 0, failed: 0, skipped: 0 }
|
|
607
983
|
};
|
|
608
984
|
|
|
@@ -619,17 +995,25 @@ async function importConfigs(importData, options = {}) {
|
|
|
619
995
|
workspaces = null,
|
|
620
996
|
favorites = null,
|
|
621
997
|
agents = [],
|
|
998
|
+
agentsByPlatform = {},
|
|
622
999
|
skills = [],
|
|
1000
|
+
skillsByPlatform = {},
|
|
623
1001
|
commands = [],
|
|
1002
|
+
commandsByPlatform = {},
|
|
624
1003
|
mcpServers = [],
|
|
625
1004
|
markdownFiles = {},
|
|
626
1005
|
uiConfig = null,
|
|
627
1006
|
prompts = null,
|
|
628
1007
|
security = null,
|
|
629
1008
|
appConfig = null,
|
|
1009
|
+
nativeConfigs = {},
|
|
630
1010
|
claudeHooks = null
|
|
631
1011
|
} = importData.data;
|
|
632
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
|
+
|
|
633
1017
|
const hasTypedChannels = channelsByType && typeof channelsByType === 'object';
|
|
634
1018
|
const importChannelsByType = {
|
|
635
1019
|
claude: hasTypedChannels && Array.isArray(channelsByType.claude)
|
|
@@ -759,26 +1143,28 @@ async function importConfigs(importData, options = {}) {
|
|
|
759
1143
|
}
|
|
760
1144
|
}
|
|
761
1145
|
|
|
762
|
-
// 导入 Agents
|
|
763
|
-
if (
|
|
1146
|
+
// 导入 Agents(多平台)
|
|
1147
|
+
if (AGENT_PLATFORMS.some(platform => importAgentsByPlatform[platform]?.length > 0)) {
|
|
764
1148
|
try {
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
const
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
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
|
+
}
|
|
782
1168
|
}
|
|
783
1169
|
}
|
|
784
1170
|
} catch (err) {
|
|
@@ -786,56 +1172,61 @@ async function importConfigs(importData, options = {}) {
|
|
|
786
1172
|
}
|
|
787
1173
|
}
|
|
788
1174
|
|
|
789
|
-
// 导入 Skills
|
|
790
|
-
if (
|
|
1175
|
+
// 导入 Skills(多平台)
|
|
1176
|
+
if (SKILL_PLATFORMS.some(platform => importSkillsByPlatform[platform]?.length > 0)) {
|
|
791
1177
|
try {
|
|
792
|
-
const
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
const
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
if (!overwrite) {
|
|
806
|
-
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++;
|
|
807
1191
|
continue;
|
|
808
1192
|
}
|
|
809
|
-
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
ensureDir(skillDir);
|
|
813
|
-
const files = Array.isArray(skill.files) ? skill.files : [];
|
|
814
|
-
let failed = false;
|
|
815
1193
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
1194
|
+
if (fs.existsSync(skillDir)) {
|
|
1195
|
+
if (!overwrite) {
|
|
1196
|
+
results.skills.skipped++;
|
|
1197
|
+
continue;
|
|
1198
|
+
}
|
|
1199
|
+
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
821
1200
|
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
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;
|
|
828
1222
|
}
|
|
829
|
-
} catch (err) {
|
|
830
|
-
failed = true;
|
|
831
|
-
break;
|
|
832
1223
|
}
|
|
833
|
-
}
|
|
834
1224
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1225
|
+
if (failed || files.length === 0) {
|
|
1226
|
+
results.skills.failed++;
|
|
1227
|
+
} else {
|
|
1228
|
+
results.skills.success++;
|
|
1229
|
+
}
|
|
839
1230
|
}
|
|
840
1231
|
}
|
|
841
1232
|
} catch (err) {
|
|
@@ -980,27 +1371,28 @@ async function importConfigs(importData, options = {}) {
|
|
|
980
1371
|
}
|
|
981
1372
|
}
|
|
982
1373
|
|
|
983
|
-
// 导入 Commands
|
|
984
|
-
if (
|
|
1374
|
+
// 导入 Commands(多平台)
|
|
1375
|
+
if (COMMAND_PLATFORMS.some(platform => importCommandsByPlatform[platform]?.length > 0)) {
|
|
985
1376
|
try {
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
const
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
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
|
+
}
|
|
1004
1396
|
}
|
|
1005
1397
|
}
|
|
1006
1398
|
} catch (err) {
|
|
@@ -1073,6 +1465,12 @@ async function importConfigs(importData, options = {}) {
|
|
|
1073
1465
|
try {
|
|
1074
1466
|
const status = writeJsonFileAbsolute(CC_PROMPTS_PATH, prompts, overwrite);
|
|
1075
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
|
+
}
|
|
1076
1474
|
results.prompts.success++;
|
|
1077
1475
|
} else if (status === 'skipped') {
|
|
1078
1476
|
results.prompts.skipped++;
|
|
@@ -1119,6 +1517,39 @@ async function importConfigs(importData, options = {}) {
|
|
|
1119
1517
|
}
|
|
1120
1518
|
}
|
|
1121
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
|
+
|
|
1122
1553
|
// 导入 Claude Hooks 配置
|
|
1123
1554
|
if (claudeHooks && typeof claudeHooks === 'object') {
|
|
1124
1555
|
let didWrite = false;
|
|
@@ -1172,6 +1603,8 @@ async function importConfigs(importData, options = {}) {
|
|
|
1172
1603
|
}
|
|
1173
1604
|
}
|
|
1174
1605
|
|
|
1606
|
+
syncImportedChannelsToNativeConfigs(importChannelsByType, nativeConfigs, overwrite);
|
|
1607
|
+
|
|
1175
1608
|
return {
|
|
1176
1609
|
success: true,
|
|
1177
1610
|
results,
|
|
@@ -1208,6 +1641,7 @@ function generateImportSummary(results) {
|
|
|
1208
1641
|
{ key: 'prompts', label: 'Prompts' },
|
|
1209
1642
|
{ key: 'security', label: '安全配置' },
|
|
1210
1643
|
{ key: 'appConfig', label: '高级配置' },
|
|
1644
|
+
{ key: 'nativeConfigs', label: '原生配置' },
|
|
1211
1645
|
{ key: 'claudeHooks', label: 'Claude Hooks' }
|
|
1212
1646
|
];
|
|
1213
1647
|
|