coding-tool-x 3.3.6 → 3.3.8
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 +14 -0
- package/dist/web/assets/{Analytics-TtaduRqL.js → Analytics-DLpoDZ2M.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-BP2lLBMN.js → ConfigTemplates-D_hRb55W.js} +1 -1
- package/dist/web/assets/Home-BMoFdAwy.css +1 -0
- package/dist/web/assets/Home-DNwp-0J-.js +1 -0
- package/dist/web/assets/{PluginManager-HmISlyMK.js → PluginManager-JXsyym1s.js} +1 -1
- package/dist/web/assets/{ProjectList-DoN8Hjbu.js → ProjectList-DZWSeb-q.js} +1 -1
- package/dist/web/assets/{SessionList-Da8BYzNi.js → SessionList-Cs624DR3.js} +1 -1
- package/dist/web/assets/{SkillManager-DqLAXh9o.js → SkillManager-bEliz7qz.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-B_TxOgPW.js → WorkspaceManager-J3RecFGn.js} +1 -1
- package/dist/web/assets/{icons-B29onFfZ.js → icons-Cuc23WS7.js} +1 -1
- package/dist/web/assets/index-BXeSvAwU.js +2 -0
- package/dist/web/assets/index-DWAC3Tdv.css +1 -0
- package/dist/web/index.html +3 -3
- package/package.json +3 -2
- package/src/commands/daemon.js +44 -6
- package/src/commands/toggle-proxy.js +100 -5
- 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/config/paths.js +102 -19
- package/src/server/api/channels.js +9 -0
- package/src/server/api/codex-channels.js +9 -0
- package/src/server/api/codex-proxy.js +22 -11
- package/src/server/api/gemini-proxy.js +22 -11
- package/src/server/api/mcp.js +26 -4
- package/src/server/api/oauth-credentials.js +163 -0
- package/src/server/api/opencode-proxy.js +22 -10
- package/src/server/api/plugins.js +3 -1
- package/src/server/api/proxy.js +39 -44
- package/src/server/api/skills.js +91 -13
- package/src/server/codex-proxy-server.js +1 -11
- package/src/server/index.js +26 -2
- package/src/server/services/channels.js +18 -22
- package/src/server/services/codex-channels.js +124 -175
- package/src/server/services/codex-config.js +2 -5
- package/src/server/services/codex-settings-manager.js +12 -348
- package/src/server/services/config-export-service.js +572 -117
- package/src/server/services/gemini-channels.js +11 -9
- package/src/server/services/mcp-client.js +70 -13
- package/src/server/services/mcp-service.js +74 -29
- package/src/server/services/model-detector.js +1 -0
- package/src/server/services/native-keychain.js +243 -0
- package/src/server/services/native-oauth-adapters.js +890 -0
- package/src/server/services/oauth-credentials-service.js +786 -0
- package/src/server/services/oauth-utils.js +49 -0
- package/src/server/services/opencode-channels.js +13 -9
- package/src/server/services/opencode-settings-manager.js +169 -16
- package/src/server/services/plugins-service.js +22 -1
- package/src/server/services/settings-manager.js +13 -0
- package/src/server/services/skill-service.js +712 -332
- package/src/utils/port-helper.js +87 -2
- package/dist/web/assets/Home-BsSioaaB.css +0 -1
- package/dist/web/assets/Home-CbbyopS-.js +0 -1
- package/dist/web/assets/index-By3mDEvx.js +0 -2
- package/dist/web/assets/index-CsWInMQV.css +0 -1
|
@@ -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.4.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,8 @@ function buildExportReadme(exportData) {
|
|
|
125
206
|
- Agents / Skills / Commands
|
|
126
207
|
- 插件 (Plugins)
|
|
127
208
|
- MCP 服务器配置
|
|
209
|
+
- OAuth 凭证管理池
|
|
210
|
+
- 各平台原生配置(Claude / Codex / Gemini / OpenCode)
|
|
128
211
|
- UI 配置(主题、面板显示、排序等)
|
|
129
212
|
- Prompts 预设
|
|
130
213
|
- 安全配置
|
|
@@ -184,6 +267,288 @@ function buildCommandContent(command) {
|
|
|
184
267
|
return `${lines.join('\n')}${body}`;
|
|
185
268
|
}
|
|
186
269
|
|
|
270
|
+
function buildAgentExportItem(agent, platform) {
|
|
271
|
+
return {
|
|
272
|
+
platform,
|
|
273
|
+
fileName: agent.fileName,
|
|
274
|
+
name: agent.name,
|
|
275
|
+
description: agent.description,
|
|
276
|
+
tools: agent.tools,
|
|
277
|
+
model: agent.model,
|
|
278
|
+
permissionMode: agent.permissionMode,
|
|
279
|
+
skills: agent.skills,
|
|
280
|
+
path: agent.path,
|
|
281
|
+
systemPrompt: agent.systemPrompt,
|
|
282
|
+
fullContent: agent.fullContent
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function buildCommandExportItem(command, platform) {
|
|
287
|
+
return {
|
|
288
|
+
platform,
|
|
289
|
+
name: command.name,
|
|
290
|
+
namespace: command.namespace,
|
|
291
|
+
description: command.description,
|
|
292
|
+
allowedTools: command.allowedTools,
|
|
293
|
+
argumentHint: command.argumentHint,
|
|
294
|
+
path: command.path,
|
|
295
|
+
body: command.body,
|
|
296
|
+
fullContent: command.fullContent
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function exportAgentsSnapshotByPlatform() {
|
|
301
|
+
return AGENT_PLATFORMS.reduce((result, platform) => {
|
|
302
|
+
try {
|
|
303
|
+
const agentsService = new AgentsService(platform);
|
|
304
|
+
const { agents: rawAgents = [] } = agentsService.listAgents();
|
|
305
|
+
result[platform] = rawAgents.map(agent => buildAgentExportItem(agent, platform));
|
|
306
|
+
} catch (err) {
|
|
307
|
+
console.warn(`[ConfigExport] Failed to export agents for ${platform}:`, err.message);
|
|
308
|
+
result[platform] = [];
|
|
309
|
+
}
|
|
310
|
+
return result;
|
|
311
|
+
}, {});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function exportCommandsSnapshotByPlatform() {
|
|
315
|
+
return COMMAND_PLATFORMS.reduce((result, platform) => {
|
|
316
|
+
try {
|
|
317
|
+
const commandsService = new CommandsService(platform);
|
|
318
|
+
const { commands: rawCommands = [] } = commandsService.listCommands();
|
|
319
|
+
result[platform] = rawCommands.map(command => buildCommandExportItem(command, platform));
|
|
320
|
+
} catch (err) {
|
|
321
|
+
console.warn(`[ConfigExport] Failed to export commands for ${platform}:`, err.message);
|
|
322
|
+
result[platform] = [];
|
|
323
|
+
}
|
|
324
|
+
return result;
|
|
325
|
+
}, {});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function normalizePlatformItems(legacyItems = [], byPlatform = {}, supportedPlatforms = [], defaultPlatform = 'claude') {
|
|
329
|
+
const result = Object.fromEntries(supportedPlatforms.map(platform => [platform, []]));
|
|
330
|
+
const structuredPlatforms = new Set();
|
|
331
|
+
|
|
332
|
+
for (const platform of supportedPlatforms) {
|
|
333
|
+
if (Array.isArray(byPlatform?.[platform])) {
|
|
334
|
+
result[platform] = byPlatform[platform].map(item => ({
|
|
335
|
+
...item,
|
|
336
|
+
platform: supportedPlatforms.includes(item?.platform) ? item.platform : platform
|
|
337
|
+
}));
|
|
338
|
+
structuredPlatforms.add(platform);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (!Array.isArray(legacyItems)) {
|
|
343
|
+
return result;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
for (const item of legacyItems) {
|
|
347
|
+
const platform = supportedPlatforms.includes(item?.platform) ? item.platform : defaultPlatform;
|
|
348
|
+
if (structuredPlatforms.has(platform)) {
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
result[platform].push({ ...item, platform });
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function resolveAgentRelativePath(agent, platform) {
|
|
358
|
+
const pathCandidates = [agent?.path, agent?.fileName]
|
|
359
|
+
.filter(value => typeof value === 'string' && value.trim())
|
|
360
|
+
.map(value => value.trim());
|
|
361
|
+
|
|
362
|
+
for (const candidate of pathCandidates) {
|
|
363
|
+
if (path.extname(candidate)) {
|
|
364
|
+
return candidate;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const baseName = agent?.fileName || agent?.name;
|
|
369
|
+
if (!baseName) {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return platform === 'codex' ? `${baseName}.toml` : `${baseName}.md`;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function resolveCommandRelativePath(command) {
|
|
377
|
+
if (typeof command?.path === 'string' && command.path.trim()) {
|
|
378
|
+
return command.path.trim();
|
|
379
|
+
}
|
|
380
|
+
if (command?.namespace) {
|
|
381
|
+
return path.join(command.namespace, `${command.name}.md`);
|
|
382
|
+
}
|
|
383
|
+
return command?.name ? `${command.name}.md` : null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function pickPrimaryChannel(channels = []) {
|
|
387
|
+
if (!Array.isArray(channels) || channels.length === 0) {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
return channels.find(channel => channel.enabled !== false) || channels[0] || null;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function findMatchingPersistedChannel(existingChannels = [], importedChannel = {}, extraMatchers = []) {
|
|
394
|
+
if (!Array.isArray(existingChannels) || existingChannels.length === 0 || !importedChannel) {
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (importedChannel.id) {
|
|
399
|
+
const exactMatch = existingChannels.find(channel => channel.id === importedChannel.id);
|
|
400
|
+
if (exactMatch) {
|
|
401
|
+
return exactMatch;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
for (const matcher of extraMatchers) {
|
|
406
|
+
const matchedChannel = existingChannels.find(channel => matcher(channel, importedChannel));
|
|
407
|
+
if (matchedChannel) {
|
|
408
|
+
return matchedChannel;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function sanitizeOpenCodeProviderId(value) {
|
|
416
|
+
return String(value || '')
|
|
417
|
+
.trim()
|
|
418
|
+
.toLowerCase()
|
|
419
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
420
|
+
.replace(/-+/g, '-')
|
|
421
|
+
.replace(/^-|-$/g, '') || 'channel';
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function buildOpenCodeNativeConfig(channels = []) {
|
|
425
|
+
const config = { provider: {} };
|
|
426
|
+
const usedProviderIds = new Set();
|
|
427
|
+
let defaultModelRef = '';
|
|
428
|
+
|
|
429
|
+
channels.forEach((channel) => {
|
|
430
|
+
const baseProviderId = sanitizeOpenCodeProviderId(channel.providerKey || channel.name);
|
|
431
|
+
let providerId = baseProviderId;
|
|
432
|
+
let suffix = 2;
|
|
433
|
+
while (usedProviderIds.has(providerId)) {
|
|
434
|
+
providerId = `${baseProviderId}-${suffix}`;
|
|
435
|
+
suffix += 1;
|
|
436
|
+
}
|
|
437
|
+
usedProviderIds.add(providerId);
|
|
438
|
+
|
|
439
|
+
const provider = {
|
|
440
|
+
npm: '@ai-sdk/openai-compatible',
|
|
441
|
+
name: channel.name || providerId,
|
|
442
|
+
options: {
|
|
443
|
+
baseURL: channel.baseUrl || '',
|
|
444
|
+
apiKey: channel.apiKey || ''
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
const models = {};
|
|
449
|
+
const allowedModels = Array.isArray(channel.allowedModels) ? channel.allowedModels : [];
|
|
450
|
+
const allModels = allowedModels.length > 0
|
|
451
|
+
? allowedModels
|
|
452
|
+
: [channel.model].filter(Boolean);
|
|
453
|
+
|
|
454
|
+
allModels.forEach((modelId) => {
|
|
455
|
+
const normalizedModel = String(modelId || '').trim();
|
|
456
|
+
if (!normalizedModel) return;
|
|
457
|
+
models[normalizedModel] = { name: normalizedModel };
|
|
458
|
+
if (!defaultModelRef && (channel.enabled !== false || !pickPrimaryChannel(channels))) {
|
|
459
|
+
defaultModelRef = `${providerId}/${normalizedModel}`;
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
if (Object.keys(models).length > 0) {
|
|
464
|
+
provider.models = models;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
config.provider[providerId] = provider;
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
const preferredChannel = pickPrimaryChannel(channels);
|
|
471
|
+
if (!defaultModelRef && preferredChannel?.model) {
|
|
472
|
+
const providerId = sanitizeOpenCodeProviderId(preferredChannel.providerKey || preferredChannel.name);
|
|
473
|
+
defaultModelRef = `${providerId}/${preferredChannel.model}`;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (defaultModelRef) {
|
|
477
|
+
config.model = defaultModelRef;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return config;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function syncImportedChannelsToNativeConfigs(importChannelsByType, nativeConfigs, overwrite) {
|
|
484
|
+
const hasNativeConfigFor = (platform) => !!nativeConfigs?.[platform] && Object.keys(nativeConfigs[platform]).length > 0;
|
|
485
|
+
|
|
486
|
+
try {
|
|
487
|
+
if (!hasNativeConfigFor('claude')) {
|
|
488
|
+
const primaryClaudeChannel = pickPrimaryChannel(importChannelsByType.claude);
|
|
489
|
+
const persistedClaudeChannel = findMatchingPersistedChannel(
|
|
490
|
+
channelsService.getAllChannels?.() || [],
|
|
491
|
+
primaryClaudeChannel,
|
|
492
|
+
[
|
|
493
|
+
(channel, imported) => channel.name === imported.name && channel.baseUrl === imported.baseUrl
|
|
494
|
+
]
|
|
495
|
+
);
|
|
496
|
+
if (persistedClaudeChannel?.id) {
|
|
497
|
+
ensureDir(path.dirname(NATIVE_PATHS.claude.settings));
|
|
498
|
+
channelsService.applyChannelToSettings(persistedClaudeChannel.id);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
} catch (err) {
|
|
502
|
+
console.warn('[ConfigImport] Claude native sync fallback failed:', err.message);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
try {
|
|
506
|
+
if (!hasNativeConfigFor('codex')) {
|
|
507
|
+
codexChannelsService.writeCodexConfigForMultiChannel(importChannelsByType.codex || []);
|
|
508
|
+
}
|
|
509
|
+
} catch (err) {
|
|
510
|
+
console.warn('[ConfigImport] Codex native sync fallback failed:', err.message);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
try {
|
|
514
|
+
if (!hasNativeConfigFor('gemini')) {
|
|
515
|
+
const primaryGeminiChannel = pickPrimaryChannel(importChannelsByType.gemini);
|
|
516
|
+
const persistedGeminiChannel = findMatchingPersistedChannel(
|
|
517
|
+
geminiChannelsService.getChannels?.().channels || [],
|
|
518
|
+
primaryGeminiChannel,
|
|
519
|
+
[
|
|
520
|
+
(channel, imported) => channel.name === imported.name && channel.baseUrl === imported.baseUrl
|
|
521
|
+
]
|
|
522
|
+
);
|
|
523
|
+
if (persistedGeminiChannel?.id) {
|
|
524
|
+
geminiChannelsService.applyChannelToSettings(persistedGeminiChannel.id);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
} catch (err) {
|
|
528
|
+
console.warn('[ConfigImport] Gemini native sync fallback failed:', err.message);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
if (!hasNativeConfigFor('opencode')) {
|
|
533
|
+
const openCodeSpecs = getNativeConfigSpecs().opencode || {};
|
|
534
|
+
const preferredSpec = [
|
|
535
|
+
openCodeSpecs.opencodeJsonc,
|
|
536
|
+
openCodeSpecs.opencodeJson,
|
|
537
|
+
openCodeSpecs.configJson
|
|
538
|
+
].find(spec => spec?.path && fs.existsSync(spec.path))
|
|
539
|
+
|| openCodeSpecs.opencodeJson
|
|
540
|
+
|| openCodeSpecs.configJson;
|
|
541
|
+
|
|
542
|
+
if (preferredSpec?.path) {
|
|
543
|
+
const payload = buildOpenCodeNativeConfig(importChannelsByType.opencode || []);
|
|
544
|
+
writeTextFileAbsolute(preferredSpec.path, JSON.stringify(payload, null, 2), overwrite);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
} catch (err) {
|
|
548
|
+
console.warn('[ConfigImport] OpenCode native sync fallback failed:', err.message);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
187
552
|
function collectSkillFiles(baseDir) {
|
|
188
553
|
const files = [];
|
|
189
554
|
const stack = [baseDir];
|
|
@@ -277,8 +642,8 @@ function collectPluginFiles(pluginDir, basePath = '') {
|
|
|
277
642
|
return files;
|
|
278
643
|
}
|
|
279
644
|
|
|
280
|
-
function exportSkillsSnapshot() {
|
|
281
|
-
const skillService = new SkillService();
|
|
645
|
+
function exportSkillsSnapshot(platform = 'claude') {
|
|
646
|
+
const skillService = new SkillService(platform);
|
|
282
647
|
const installedSkills = skillService.getInstalledSkills();
|
|
283
648
|
const baseDir = skillService.installDir;
|
|
284
649
|
|
|
@@ -289,6 +654,7 @@ function exportSkillsSnapshot() {
|
|
|
289
654
|
return null;
|
|
290
655
|
}
|
|
291
656
|
return {
|
|
657
|
+
platform,
|
|
292
658
|
directory,
|
|
293
659
|
name: skill.name || directory,
|
|
294
660
|
description: skill.description || '',
|
|
@@ -297,6 +663,36 @@ function exportSkillsSnapshot() {
|
|
|
297
663
|
}).filter(Boolean);
|
|
298
664
|
}
|
|
299
665
|
|
|
666
|
+
function exportSkillsSnapshotByPlatform() {
|
|
667
|
+
return SKILL_PLATFORMS.reduce((result, platform) => {
|
|
668
|
+
try {
|
|
669
|
+
result[platform] = exportSkillsSnapshot(platform);
|
|
670
|
+
} catch (err) {
|
|
671
|
+
console.warn(`[ConfigExport] Failed to export skills for ${platform}:`, err.message);
|
|
672
|
+
result[platform] = [];
|
|
673
|
+
}
|
|
674
|
+
return result;
|
|
675
|
+
}, {});
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function exportNativeConfigs() {
|
|
679
|
+
const specs = getNativeConfigSpecs();
|
|
680
|
+
return Object.entries(specs).reduce((result, [platform, platformSpecs]) => {
|
|
681
|
+
const exportedEntries = Object.entries(platformSpecs).reduce((entries, [key, spec]) => {
|
|
682
|
+
const snapshot = readNativeConfigSnapshot(spec);
|
|
683
|
+
if (snapshot) {
|
|
684
|
+
entries[key] = snapshot;
|
|
685
|
+
}
|
|
686
|
+
return entries;
|
|
687
|
+
}, {});
|
|
688
|
+
|
|
689
|
+
if (Object.keys(exportedEntries).length > 0) {
|
|
690
|
+
result[platform] = exportedEntries;
|
|
691
|
+
}
|
|
692
|
+
return result;
|
|
693
|
+
}, {});
|
|
694
|
+
}
|
|
695
|
+
|
|
300
696
|
function exportLegacyPlugins() {
|
|
301
697
|
const plugins = [];
|
|
302
698
|
|
|
@@ -447,38 +843,13 @@ function exportAllConfigs() {
|
|
|
447
843
|
const favoritesService = require('./favorites');
|
|
448
844
|
const favorites = favoritesService.loadFavorites();
|
|
449
845
|
|
|
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
|
-
}));
|
|
846
|
+
// 获取 Agents / Skills / Commands 配置(多平台)
|
|
847
|
+
const agentsByPlatform = exportAgentsSnapshotByPlatform();
|
|
848
|
+
const skillsByPlatform = exportSkillsSnapshotByPlatform();
|
|
849
|
+
const commandsByPlatform = exportCommandsSnapshotByPlatform();
|
|
850
|
+
const agents = agentsByPlatform.claude || [];
|
|
851
|
+
const skills = skillsByPlatform.claude || [];
|
|
852
|
+
const commands = commandsByPlatform.claude || [];
|
|
482
853
|
|
|
483
854
|
// 获取 MCP 配置
|
|
484
855
|
const mcpService = require('./mcp-service');
|
|
@@ -486,9 +857,10 @@ function exportAllConfigs() {
|
|
|
486
857
|
|
|
487
858
|
// 获取 Plugins 配置
|
|
488
859
|
const plugins = exportPluginsSnapshot();
|
|
860
|
+
const nativeConfigs = exportNativeConfigs();
|
|
861
|
+
const oauthCredentials = readJsonFileSafe(PATHS.oauthCredentials);
|
|
489
862
|
|
|
490
863
|
// 读取 Markdown 配置文件
|
|
491
|
-
const { PATHS } = require('../../config/paths');
|
|
492
864
|
const markdownFiles = {};
|
|
493
865
|
const mdFileNames = ['CLAUDE.md', 'AGENTS.md', 'GEMINI.md'];
|
|
494
866
|
|
|
@@ -530,8 +902,11 @@ function exportAllConfigs() {
|
|
|
530
902
|
workspaces: workspaces || { workspaces: [] },
|
|
531
903
|
favorites: favorites || { favorites: [] },
|
|
532
904
|
agents: agents || [],
|
|
905
|
+
agentsByPlatform,
|
|
533
906
|
skills: skills || [],
|
|
907
|
+
skillsByPlatform,
|
|
534
908
|
commands: commands || [],
|
|
909
|
+
commandsByPlatform,
|
|
535
910
|
mcpServers: mcpServers || [],
|
|
536
911
|
plugins: plugins || [],
|
|
537
912
|
markdownFiles: markdownFiles,
|
|
@@ -539,6 +914,8 @@ function exportAllConfigs() {
|
|
|
539
914
|
prompts: prompts,
|
|
540
915
|
security: security,
|
|
541
916
|
appConfig: appConfig,
|
|
917
|
+
nativeConfigs,
|
|
918
|
+
oauthCredentials,
|
|
542
919
|
claudeHooks: claudeHooks
|
|
543
920
|
}
|
|
544
921
|
};
|
|
@@ -603,6 +980,8 @@ async function importConfigs(importData, options = {}) {
|
|
|
603
980
|
prompts: { success: 0, failed: 0, skipped: 0 },
|
|
604
981
|
security: { success: 0, failed: 0, skipped: 0 },
|
|
605
982
|
appConfig: { success: 0, failed: 0, skipped: 0 },
|
|
983
|
+
nativeConfigs: { success: 0, failed: 0, skipped: 0 },
|
|
984
|
+
oauthCredentials: { success: 0, failed: 0, skipped: 0 },
|
|
606
985
|
claudeHooks: { success: 0, failed: 0, skipped: 0 }
|
|
607
986
|
};
|
|
608
987
|
|
|
@@ -619,17 +998,26 @@ async function importConfigs(importData, options = {}) {
|
|
|
619
998
|
workspaces = null,
|
|
620
999
|
favorites = null,
|
|
621
1000
|
agents = [],
|
|
1001
|
+
agentsByPlatform = {},
|
|
622
1002
|
skills = [],
|
|
1003
|
+
skillsByPlatform = {},
|
|
623
1004
|
commands = [],
|
|
1005
|
+
commandsByPlatform = {},
|
|
624
1006
|
mcpServers = [],
|
|
625
1007
|
markdownFiles = {},
|
|
626
1008
|
uiConfig = null,
|
|
627
1009
|
prompts = null,
|
|
628
1010
|
security = null,
|
|
629
1011
|
appConfig = null,
|
|
1012
|
+
nativeConfigs = {},
|
|
1013
|
+
oauthCredentials = null,
|
|
630
1014
|
claudeHooks = null
|
|
631
1015
|
} = importData.data;
|
|
632
1016
|
|
|
1017
|
+
const importAgentsByPlatform = normalizePlatformItems(agents, agentsByPlatform, AGENT_PLATFORMS);
|
|
1018
|
+
const importSkillsByPlatform = normalizePlatformItems(skills, skillsByPlatform, SKILL_PLATFORMS);
|
|
1019
|
+
const importCommandsByPlatform = normalizePlatformItems(commands, commandsByPlatform, COMMAND_PLATFORMS);
|
|
1020
|
+
|
|
633
1021
|
const hasTypedChannels = channelsByType && typeof channelsByType === 'object';
|
|
634
1022
|
const importChannelsByType = {
|
|
635
1023
|
claude: hasTypedChannels && Array.isArray(channelsByType.claude)
|
|
@@ -759,26 +1147,28 @@ async function importConfigs(importData, options = {}) {
|
|
|
759
1147
|
}
|
|
760
1148
|
}
|
|
761
1149
|
|
|
762
|
-
// 导入 Agents
|
|
763
|
-
if (
|
|
1150
|
+
// 导入 Agents(多平台)
|
|
1151
|
+
if (AGENT_PLATFORMS.some(platform => importAgentsByPlatform[platform]?.length > 0)) {
|
|
764
1152
|
try {
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
const
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
1153
|
+
for (const platform of AGENT_PLATFORMS) {
|
|
1154
|
+
const platformAgents = importAgentsByPlatform[platform] || [];
|
|
1155
|
+
if (platformAgents.length === 0) continue;
|
|
1156
|
+
|
|
1157
|
+
const agentsService = new AgentsService(platform);
|
|
1158
|
+
const baseDir = agentsService.userAgentsDir;
|
|
1159
|
+
|
|
1160
|
+
for (const agent of platformAgents) {
|
|
1161
|
+
const filePath = resolveAgentRelativePath(agent, platform);
|
|
1162
|
+
const content = agent.fullContent || agent.content || buildAgentContent(agent);
|
|
1163
|
+
const status = filePath ? writeTextFile(baseDir, filePath, content, overwrite) : 'failed';
|
|
1164
|
+
|
|
1165
|
+
if (status === 'success') {
|
|
1166
|
+
results.agents.success++;
|
|
1167
|
+
} else if (status === 'skipped') {
|
|
1168
|
+
results.agents.skipped++;
|
|
1169
|
+
} else {
|
|
1170
|
+
results.agents.failed++;
|
|
1171
|
+
}
|
|
782
1172
|
}
|
|
783
1173
|
}
|
|
784
1174
|
} catch (err) {
|
|
@@ -786,56 +1176,61 @@ async function importConfigs(importData, options = {}) {
|
|
|
786
1176
|
}
|
|
787
1177
|
}
|
|
788
1178
|
|
|
789
|
-
// 导入 Skills
|
|
790
|
-
if (
|
|
1179
|
+
// 导入 Skills(多平台)
|
|
1180
|
+
if (SKILL_PLATFORMS.some(platform => importSkillsByPlatform[platform]?.length > 0)) {
|
|
791
1181
|
try {
|
|
792
|
-
const
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
const
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
if (!overwrite) {
|
|
806
|
-
results.skills.skipped++;
|
|
1182
|
+
for (const platform of SKILL_PLATFORMS) {
|
|
1183
|
+
const platformSkills = importSkillsByPlatform[platform] || [];
|
|
1184
|
+
if (platformSkills.length === 0) continue;
|
|
1185
|
+
|
|
1186
|
+
const skillService = new SkillService(platform);
|
|
1187
|
+
const baseDir = skillService.installDir;
|
|
1188
|
+
ensureDir(baseDir);
|
|
1189
|
+
|
|
1190
|
+
for (const skill of platformSkills) {
|
|
1191
|
+
const directory = skill.directory;
|
|
1192
|
+
const skillDir = resolveSafePath(baseDir, directory);
|
|
1193
|
+
if (!skillDir) {
|
|
1194
|
+
results.skills.failed++;
|
|
807
1195
|
continue;
|
|
808
1196
|
}
|
|
809
|
-
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
810
|
-
}
|
|
811
1197
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
if (!filePath) {
|
|
819
|
-
failed = true;
|
|
820
|
-
break;
|
|
1198
|
+
if (fs.existsSync(skillDir)) {
|
|
1199
|
+
if (!overwrite) {
|
|
1200
|
+
results.skills.skipped++;
|
|
1201
|
+
continue;
|
|
1202
|
+
}
|
|
1203
|
+
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
821
1204
|
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1205
|
+
|
|
1206
|
+
ensureDir(skillDir);
|
|
1207
|
+
const files = Array.isArray(skill.files) ? skill.files : [];
|
|
1208
|
+
let failed = false;
|
|
1209
|
+
|
|
1210
|
+
for (const file of files) {
|
|
1211
|
+
const filePath = resolveSafePath(skillDir, file.path);
|
|
1212
|
+
if (!filePath) {
|
|
1213
|
+
failed = true;
|
|
1214
|
+
break;
|
|
1215
|
+
}
|
|
1216
|
+
ensureDir(path.dirname(filePath));
|
|
1217
|
+
try {
|
|
1218
|
+
if (file.encoding === SKILL_FILE_ENCODING) {
|
|
1219
|
+
fs.writeFileSync(filePath, Buffer.from(file.content || '', SKILL_FILE_ENCODING));
|
|
1220
|
+
} else {
|
|
1221
|
+
fs.writeFileSync(filePath, file.content || '', file.encoding || 'utf8');
|
|
1222
|
+
}
|
|
1223
|
+
} catch (err) {
|
|
1224
|
+
failed = true;
|
|
1225
|
+
break;
|
|
828
1226
|
}
|
|
829
|
-
} catch (err) {
|
|
830
|
-
failed = true;
|
|
831
|
-
break;
|
|
832
1227
|
}
|
|
833
|
-
}
|
|
834
1228
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1229
|
+
if (failed || files.length === 0) {
|
|
1230
|
+
results.skills.failed++;
|
|
1231
|
+
} else {
|
|
1232
|
+
results.skills.success++;
|
|
1233
|
+
}
|
|
839
1234
|
}
|
|
840
1235
|
}
|
|
841
1236
|
} catch (err) {
|
|
@@ -980,27 +1375,28 @@ async function importConfigs(importData, options = {}) {
|
|
|
980
1375
|
}
|
|
981
1376
|
}
|
|
982
1377
|
|
|
983
|
-
// 导入 Commands
|
|
984
|
-
if (
|
|
1378
|
+
// 导入 Commands(多平台)
|
|
1379
|
+
if (COMMAND_PLATFORMS.some(platform => importCommandsByPlatform[platform]?.length > 0)) {
|
|
985
1380
|
try {
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
const
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1381
|
+
for (const platform of COMMAND_PLATFORMS) {
|
|
1382
|
+
const platformCommands = importCommandsByPlatform[platform] || [];
|
|
1383
|
+
if (platformCommands.length === 0) continue;
|
|
1384
|
+
|
|
1385
|
+
const commandsService = new CommandsService(platform);
|
|
1386
|
+
const baseDir = commandsService.userCommandsDir;
|
|
1387
|
+
|
|
1388
|
+
for (const command of platformCommands) {
|
|
1389
|
+
const relativePath = resolveCommandRelativePath(command);
|
|
1390
|
+
const content = command.fullContent || command.content || buildCommandContent(command);
|
|
1391
|
+
|
|
1392
|
+
const status = relativePath ? writeTextFile(baseDir, relativePath, content, overwrite) : 'failed';
|
|
1393
|
+
if (status === 'success') {
|
|
1394
|
+
results.commands.success++;
|
|
1395
|
+
} else if (status === 'skipped') {
|
|
1396
|
+
results.commands.skipped++;
|
|
1397
|
+
} else {
|
|
1398
|
+
results.commands.failed++;
|
|
1399
|
+
}
|
|
1004
1400
|
}
|
|
1005
1401
|
}
|
|
1006
1402
|
} catch (err) {
|
|
@@ -1073,6 +1469,12 @@ async function importConfigs(importData, options = {}) {
|
|
|
1073
1469
|
try {
|
|
1074
1470
|
const status = writeJsonFileAbsolute(CC_PROMPTS_PATH, prompts, overwrite);
|
|
1075
1471
|
if (status === 'success') {
|
|
1472
|
+
const promptsService = require('./prompts-service');
|
|
1473
|
+
if (prompts.activePresetId && prompts.presets?.[prompts.activePresetId]) {
|
|
1474
|
+
await promptsService.activatePreset(prompts.activePresetId);
|
|
1475
|
+
} else if (overwrite) {
|
|
1476
|
+
await promptsService.deactivatePrompt();
|
|
1477
|
+
}
|
|
1076
1478
|
results.prompts.success++;
|
|
1077
1479
|
} else if (status === 'skipped') {
|
|
1078
1480
|
results.prompts.skipped++;
|
|
@@ -1119,6 +1521,55 @@ async function importConfigs(importData, options = {}) {
|
|
|
1119
1521
|
}
|
|
1120
1522
|
}
|
|
1121
1523
|
|
|
1524
|
+
// 导入各平台原生配置
|
|
1525
|
+
if (nativeConfigs && typeof nativeConfigs === 'object' && Object.keys(nativeConfigs).length > 0) {
|
|
1526
|
+
const nativeConfigSpecs = getNativeConfigSpecs();
|
|
1527
|
+
|
|
1528
|
+
for (const [platform, platformEntries] of Object.entries(nativeConfigs)) {
|
|
1529
|
+
const platformSpecs = nativeConfigSpecs[platform];
|
|
1530
|
+
if (!platformSpecs || !platformEntries || typeof platformEntries !== 'object') {
|
|
1531
|
+
continue;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
for (const [key, entry] of Object.entries(platformEntries)) {
|
|
1535
|
+
const spec = platformSpecs[key];
|
|
1536
|
+
if (!spec) {
|
|
1537
|
+
continue;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
try {
|
|
1541
|
+
const status = writeNativeConfigAbsolute(spec, entry, overwrite);
|
|
1542
|
+
if (status === 'success') {
|
|
1543
|
+
results.nativeConfigs.success++;
|
|
1544
|
+
} else if (status === 'skipped') {
|
|
1545
|
+
results.nativeConfigs.skipped++;
|
|
1546
|
+
} else {
|
|
1547
|
+
results.nativeConfigs.failed++;
|
|
1548
|
+
}
|
|
1549
|
+
} catch (err) {
|
|
1550
|
+
console.error(`[ConfigImport] 导入 ${platform}.${key} 原生配置失败:`, err);
|
|
1551
|
+
results.nativeConfigs.failed++;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
if (oauthCredentials && typeof oauthCredentials === 'object') {
|
|
1558
|
+
try {
|
|
1559
|
+
const status = writeJsonFileAbsolute(PATHS.oauthCredentials, oauthCredentials, overwrite, { mode: 0o600 });
|
|
1560
|
+
if (status === 'success') {
|
|
1561
|
+
results.oauthCredentials.success++;
|
|
1562
|
+
} else if (status === 'skipped') {
|
|
1563
|
+
results.oauthCredentials.skipped++;
|
|
1564
|
+
} else {
|
|
1565
|
+
results.oauthCredentials.failed++;
|
|
1566
|
+
}
|
|
1567
|
+
} catch (err) {
|
|
1568
|
+
console.error('[ConfigImport] 导入 OAuth 凭证失败:', err);
|
|
1569
|
+
results.oauthCredentials.failed++;
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1122
1573
|
// 导入 Claude Hooks 配置
|
|
1123
1574
|
if (claudeHooks && typeof claudeHooks === 'object') {
|
|
1124
1575
|
let didWrite = false;
|
|
@@ -1172,6 +1623,8 @@ async function importConfigs(importData, options = {}) {
|
|
|
1172
1623
|
}
|
|
1173
1624
|
}
|
|
1174
1625
|
|
|
1626
|
+
syncImportedChannelsToNativeConfigs(importChannelsByType, nativeConfigs, overwrite);
|
|
1627
|
+
|
|
1175
1628
|
return {
|
|
1176
1629
|
success: true,
|
|
1177
1630
|
results,
|
|
@@ -1208,6 +1661,8 @@ function generateImportSummary(results) {
|
|
|
1208
1661
|
{ key: 'prompts', label: 'Prompts' },
|
|
1209
1662
|
{ key: 'security', label: '安全配置' },
|
|
1210
1663
|
{ key: 'appConfig', label: '高级配置' },
|
|
1664
|
+
{ key: 'nativeConfigs', label: '原生配置' },
|
|
1665
|
+
{ key: 'oauthCredentials', label: 'OAuth凭证' },
|
|
1211
1666
|
{ key: 'claudeHooks', label: 'Claude Hooks' }
|
|
1212
1667
|
];
|
|
1213
1668
|
|