@adversity/coding-tool-x 2.4.2 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/web/assets/{icons-BkBtk3H1.js → icons-BALJo7bE.js} +1 -1
- package/dist/web/assets/{index-Cgq2DyzS.css → index-CvHZsWbE.css} +3 -3
- package/dist/web/assets/index-DZjidyED.js +14 -0
- package/dist/web/assets/{naive-ui-D-gb0WfN.js → naive-ui-sh0u_0bf.js} +1 -1
- package/dist/web/assets/{vendors-Bd5vxA1-.js → vendors-CzcvkTIS.js} +1 -1
- package/dist/web/assets/{vue-vendor-hRp8vsrL.js → vue-vendor-CEeI-Azr.js} +1 -1
- package/dist/web/index.html +6 -6
- package/package.json +2 -1
- package/src/commands/security.js +36 -0
- package/src/index.js +19 -3
- package/src/server/api/config-export.js +122 -32
- package/src/server/api/security.js +53 -0
- package/src/server/api/terminal.js +5 -1
- package/src/server/index.js +1 -0
- package/src/server/services/config-export-service.js +611 -38
- package/src/server/services/pty-manager.js +250 -28
- package/src/server/services/security-config.js +131 -0
- package/src/server/services/terminal-config.js +81 -1
- package/src/server/services/terminal-detector.js +76 -1
- package/src/server/services/ui-config.js +2 -0
- package/src/server/websocket-server.js +14 -3
- package/dist/web/assets/index-DOsR4qc6.js +0 -14
|
@@ -5,11 +5,257 @@
|
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
const AdmZip = require('adm-zip');
|
|
8
10
|
const permissionTemplatesService = require('./permission-templates-service');
|
|
9
11
|
const configTemplatesService = require('./config-templates-service');
|
|
10
12
|
const channelsService = require('./channels');
|
|
13
|
+
const { AgentsService } = require('./agents-service');
|
|
14
|
+
const { CommandsService } = require('./commands-service');
|
|
15
|
+
const { RulesService } = require('./rules-service');
|
|
16
|
+
const { SkillService } = require('./skill-service');
|
|
17
|
+
|
|
18
|
+
const CONFIG_VERSION = '1.1.0';
|
|
19
|
+
const SKILL_FILE_ENCODING = 'base64';
|
|
20
|
+
const SKILL_IGNORE_DIRS = new Set(['.git']);
|
|
21
|
+
const SKILL_IGNORE_FILES = new Set(['.DS_Store']);
|
|
22
|
+
const CC_TOOL_DIR = path.join(os.homedir(), '.claude', 'cc-tool');
|
|
23
|
+
const LEGACY_CC_TOOL_DIR = path.join(os.homedir(), '.cc-tool');
|
|
24
|
+
const CLAUDE_SETTINGS_PATH = path.join(os.homedir(), '.claude', 'settings.json');
|
|
25
|
+
const CC_UI_CONFIG_PATH = path.join(CC_TOOL_DIR, 'ui-config.json');
|
|
26
|
+
const CC_TERMINAL_CONFIG_PATH = path.join(CC_TOOL_DIR, 'terminal-config.json');
|
|
27
|
+
const CC_TERMINAL_COMMANDS_PATH = path.join(CC_TOOL_DIR, 'terminal-commands.json');
|
|
28
|
+
const CC_PROMPTS_PATH = path.join(CC_TOOL_DIR, 'prompts.json');
|
|
29
|
+
const CC_SECURITY_PATH = path.join(CC_TOOL_DIR, 'security.json');
|
|
30
|
+
const LEGACY_UI_CONFIG_PATH = path.join(LEGACY_CC_TOOL_DIR, 'ui-config.json');
|
|
31
|
+
const LEGACY_NOTIFY_HOOK_PATH = path.join(LEGACY_CC_TOOL_DIR, 'notify-hook.js');
|
|
32
|
+
|
|
33
|
+
function ensureDir(dirPath) {
|
|
34
|
+
if (!fs.existsSync(dirPath)) {
|
|
35
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readJsonFileSafe(filePath) {
|
|
40
|
+
if (!filePath || !fs.existsSync(filePath)) return null;
|
|
41
|
+
try {
|
|
42
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
43
|
+
return JSON.parse(content);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function readTextFileSafe(filePath) {
|
|
50
|
+
if (!filePath || !fs.existsSync(filePath)) return null;
|
|
51
|
+
try {
|
|
52
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
53
|
+
} catch (err) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function writeJsonFileAbsolute(filePath, data, overwrite, options = {}) {
|
|
59
|
+
if (data === undefined) {
|
|
60
|
+
return 'failed';
|
|
61
|
+
}
|
|
62
|
+
if (fs.existsSync(filePath) && !overwrite) return 'skipped';
|
|
63
|
+
ensureDir(path.dirname(filePath));
|
|
64
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
|
|
65
|
+
if (options.mode) {
|
|
66
|
+
try {
|
|
67
|
+
fs.chmodSync(filePath, options.mode);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
// ignore chmod errors
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return 'success';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function writeTextFileAbsolute(filePath, content, overwrite, options = {}) {
|
|
76
|
+
if (content === undefined || content === null) {
|
|
77
|
+
return 'failed';
|
|
78
|
+
}
|
|
79
|
+
if (fs.existsSync(filePath) && !overwrite) return 'skipped';
|
|
80
|
+
ensureDir(path.dirname(filePath));
|
|
81
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
82
|
+
if (options.mode) {
|
|
83
|
+
try {
|
|
84
|
+
fs.chmodSync(filePath, options.mode);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
// ignore chmod errors
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return 'success';
|
|
90
|
+
}
|
|
11
91
|
|
|
12
|
-
|
|
92
|
+
function getConfigFilePath() {
|
|
93
|
+
try {
|
|
94
|
+
const loaderPath = require.resolve('../../config/loader');
|
|
95
|
+
return path.resolve(path.dirname(loaderPath), '../../config.json');
|
|
96
|
+
} catch (err) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function buildExportReadme(exportData) {
|
|
102
|
+
const exportedAt = exportData.exportedAt ? new Date(exportData.exportedAt).toLocaleString('zh-CN') : '';
|
|
103
|
+
return `# Coding Tool 配置导出包
|
|
104
|
+
|
|
105
|
+
导出时间: ${exportedAt}
|
|
106
|
+
版本: ${exportData.version || CONFIG_VERSION}
|
|
107
|
+
|
|
108
|
+
## 📦 包含内容
|
|
109
|
+
- 权限模板、配置模板、频道配置、工作区、收藏
|
|
110
|
+
- Agents / Skills / Commands / Rules
|
|
111
|
+
- MCP 服务器配置
|
|
112
|
+
- UI 配置(主题、面板显示、排序等)
|
|
113
|
+
- 终端配置与 CLI 命令配置
|
|
114
|
+
- Prompts 预设
|
|
115
|
+
- 安全配置
|
|
116
|
+
- 高级配置(端口、日志、性能等)
|
|
117
|
+
- Claude Hooks 配置(如通知脚本)
|
|
118
|
+
|
|
119
|
+
> 注意:配置包可能包含 API Key、Webhook 等敏感信息,请妥善保管。
|
|
120
|
+
`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function resolveSafePath(baseDir, relativePath) {
|
|
124
|
+
if (!relativePath || typeof relativePath !== 'string') return null;
|
|
125
|
+
const normalized = path.normalize(relativePath);
|
|
126
|
+
if (path.isAbsolute(normalized)) return null;
|
|
127
|
+
const resolved = path.resolve(baseDir, normalized);
|
|
128
|
+
const relative = path.relative(baseDir, resolved);
|
|
129
|
+
if (relative.startsWith('..') || path.isAbsolute(relative)) return null;
|
|
130
|
+
return resolved;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function buildAgentContent(agent) {
|
|
134
|
+
const lines = ['---'];
|
|
135
|
+
if (agent.name) lines.push(`name: ${agent.name}`);
|
|
136
|
+
if (agent.description) lines.push(`description: "${agent.description}"`);
|
|
137
|
+
if (agent.tools) lines.push(`tools: ${agent.tools}`);
|
|
138
|
+
if (agent.model) lines.push(`model: ${agent.model}`);
|
|
139
|
+
if (agent.permissionMode) lines.push(`permissionMode: ${agent.permissionMode}`);
|
|
140
|
+
if (agent.skills) lines.push(`skills: ${agent.skills}`);
|
|
141
|
+
lines.push('---');
|
|
142
|
+
const body = agent.systemPrompt || '';
|
|
143
|
+
return `${lines.join('\n')}\n\n${body}`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function buildCommandContent(command) {
|
|
147
|
+
const frontmatter = {};
|
|
148
|
+
if (command.description) frontmatter.description = command.description;
|
|
149
|
+
if (command.allowedTools) frontmatter['allowed-tools'] = command.allowedTools;
|
|
150
|
+
if (command.argumentHint) frontmatter['argument-hint'] = command.argumentHint;
|
|
151
|
+
|
|
152
|
+
const lines = [];
|
|
153
|
+
if (Object.keys(frontmatter).length > 0) {
|
|
154
|
+
lines.push('---');
|
|
155
|
+
if (frontmatter.description) {
|
|
156
|
+
lines.push(`description: "${frontmatter.description}"`);
|
|
157
|
+
}
|
|
158
|
+
if (frontmatter['allowed-tools']) {
|
|
159
|
+
lines.push(`allowed-tools: ${frontmatter['allowed-tools']}`);
|
|
160
|
+
}
|
|
161
|
+
if (frontmatter['argument-hint']) {
|
|
162
|
+
lines.push(`argument-hint: ${frontmatter['argument-hint']}`);
|
|
163
|
+
}
|
|
164
|
+
lines.push('---');
|
|
165
|
+
lines.push('');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const body = command.body || '';
|
|
169
|
+
return `${lines.join('\n')}${body}`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function buildRuleContent(rule) {
|
|
173
|
+
const lines = [];
|
|
174
|
+
if (rule.paths) {
|
|
175
|
+
lines.push('---');
|
|
176
|
+
lines.push(`paths: ${rule.paths}`);
|
|
177
|
+
lines.push('---');
|
|
178
|
+
lines.push('');
|
|
179
|
+
}
|
|
180
|
+
const body = rule.body || '';
|
|
181
|
+
return `${lines.join('\n')}${body}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function collectSkillFiles(baseDir) {
|
|
185
|
+
const files = [];
|
|
186
|
+
const stack = [baseDir];
|
|
187
|
+
|
|
188
|
+
while (stack.length > 0) {
|
|
189
|
+
const currentDir = stack.pop();
|
|
190
|
+
let entries = [];
|
|
191
|
+
try {
|
|
192
|
+
entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
193
|
+
} catch (err) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
for (const entry of entries) {
|
|
198
|
+
if (entry.isDirectory()) {
|
|
199
|
+
if (SKILL_IGNORE_DIRS.has(entry.name)) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
stack.push(path.join(currentDir, entry.name));
|
|
203
|
+
} else if (entry.isFile()) {
|
|
204
|
+
if (SKILL_IGNORE_FILES.has(entry.name)) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
208
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
209
|
+
try {
|
|
210
|
+
const content = fs.readFileSync(fullPath);
|
|
211
|
+
files.push({
|
|
212
|
+
path: relativePath,
|
|
213
|
+
encoding: SKILL_FILE_ENCODING,
|
|
214
|
+
content: content.toString(SKILL_FILE_ENCODING)
|
|
215
|
+
});
|
|
216
|
+
} catch (err) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
224
|
+
return files;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function exportSkillsSnapshot() {
|
|
228
|
+
const skillService = new SkillService();
|
|
229
|
+
const installedSkills = skillService.getInstalledSkills();
|
|
230
|
+
const baseDir = skillService.installDir;
|
|
231
|
+
|
|
232
|
+
return installedSkills.map((skill) => {
|
|
233
|
+
const directory = skill.directory;
|
|
234
|
+
const skillDir = resolveSafePath(baseDir, directory);
|
|
235
|
+
if (!skillDir || !fs.existsSync(skillDir)) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
return {
|
|
239
|
+
directory,
|
|
240
|
+
name: skill.name || directory,
|
|
241
|
+
description: skill.description || '',
|
|
242
|
+
files: collectSkillFiles(skillDir)
|
|
243
|
+
};
|
|
244
|
+
}).filter(Boolean);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function writeTextFile(baseDir, relativePath, content, overwrite) {
|
|
248
|
+
if (!content && content !== '') {
|
|
249
|
+
return 'failed';
|
|
250
|
+
}
|
|
251
|
+
const targetPath = resolveSafePath(baseDir, relativePath);
|
|
252
|
+
if (!targetPath) return 'failed';
|
|
253
|
+
if (fs.existsSync(targetPath) && !overwrite) return 'skipped';
|
|
254
|
+
|
|
255
|
+
ensureDir(path.dirname(targetPath));
|
|
256
|
+
fs.writeFileSync(targetPath, content, 'utf8');
|
|
257
|
+
return 'success';
|
|
258
|
+
}
|
|
13
259
|
|
|
14
260
|
/**
|
|
15
261
|
* 导出所有配置为JSON
|
|
@@ -38,20 +284,49 @@ function exportAllConfigs() {
|
|
|
38
284
|
const favorites = favoritesService.loadFavorites();
|
|
39
285
|
|
|
40
286
|
// 获取 Agents 配置
|
|
41
|
-
const agentsService =
|
|
42
|
-
const agents = agentsService.
|
|
287
|
+
const agentsService = new AgentsService();
|
|
288
|
+
const { agents: rawAgents } = agentsService.listAgents();
|
|
289
|
+
const agents = rawAgents.map(agent => ({
|
|
290
|
+
fileName: agent.fileName,
|
|
291
|
+
name: agent.name,
|
|
292
|
+
description: agent.description,
|
|
293
|
+
tools: agent.tools,
|
|
294
|
+
model: agent.model,
|
|
295
|
+
permissionMode: agent.permissionMode,
|
|
296
|
+
skills: agent.skills,
|
|
297
|
+
path: agent.path,
|
|
298
|
+
systemPrompt: agent.systemPrompt,
|
|
299
|
+
fullContent: agent.fullContent
|
|
300
|
+
}));
|
|
43
301
|
|
|
44
302
|
// 获取 Skills 配置
|
|
45
|
-
const
|
|
46
|
-
const skills = skillService.getAllSkills();
|
|
303
|
+
const skills = exportSkillsSnapshot();
|
|
47
304
|
|
|
48
305
|
// 获取 Commands 配置
|
|
49
|
-
const commandsService =
|
|
50
|
-
const commands = commandsService.
|
|
306
|
+
const commandsService = new CommandsService();
|
|
307
|
+
const { commands: rawCommands } = commandsService.listCommands();
|
|
308
|
+
const commands = rawCommands.map(command => ({
|
|
309
|
+
name: command.name,
|
|
310
|
+
namespace: command.namespace,
|
|
311
|
+
description: command.description,
|
|
312
|
+
allowedTools: command.allowedTools,
|
|
313
|
+
argumentHint: command.argumentHint,
|
|
314
|
+
path: command.path,
|
|
315
|
+
body: command.body,
|
|
316
|
+
fullContent: command.fullContent
|
|
317
|
+
}));
|
|
51
318
|
|
|
52
319
|
// 获取 Rules 配置
|
|
53
|
-
const rulesService =
|
|
54
|
-
const rules = rulesService.
|
|
320
|
+
const rulesService = new RulesService();
|
|
321
|
+
const { rules: rawRules } = rulesService.listRules();
|
|
322
|
+
const rules = rawRules.map(rule => ({
|
|
323
|
+
fileName: rule.fileName,
|
|
324
|
+
directory: rule.directory,
|
|
325
|
+
paths: rule.paths,
|
|
326
|
+
path: rule.path,
|
|
327
|
+
body: rule.body,
|
|
328
|
+
fullContent: rule.fullContent
|
|
329
|
+
}));
|
|
55
330
|
|
|
56
331
|
// 获取 MCP 配置
|
|
57
332
|
const mcpService = require('./mcp-service');
|
|
@@ -73,6 +348,27 @@ function exportAllConfigs() {
|
|
|
73
348
|
}
|
|
74
349
|
}
|
|
75
350
|
|
|
351
|
+
// 获取 UI / 前端配置
|
|
352
|
+
const { loadUIConfig } = require('./ui-config');
|
|
353
|
+
const { loadTerminalConfig } = require('./terminal-config');
|
|
354
|
+
const { loadTerminalCommands } = require('./terminal-commands');
|
|
355
|
+
const { loadConfig } = require('../../config/loader');
|
|
356
|
+
|
|
357
|
+
const uiConfigFile = readJsonFileSafe(CC_UI_CONFIG_PATH);
|
|
358
|
+
const uiConfig = uiConfigFile || loadUIConfig();
|
|
359
|
+
const terminalConfig = loadTerminalConfig();
|
|
360
|
+
const terminalCommands = loadTerminalCommands();
|
|
361
|
+
const prompts = readJsonFileSafe(CC_PROMPTS_PATH);
|
|
362
|
+
const security = readJsonFileSafe(CC_SECURITY_PATH);
|
|
363
|
+
const appConfig = loadConfig();
|
|
364
|
+
|
|
365
|
+
const claudeSettings = readJsonFileSafe(CLAUDE_SETTINGS_PATH);
|
|
366
|
+
const claudeHooks = {
|
|
367
|
+
uiConfig: readJsonFileSafe(LEGACY_UI_CONFIG_PATH),
|
|
368
|
+
notifyScript: readTextFileSafe(LEGACY_NOTIFY_HOOK_PATH),
|
|
369
|
+
stopHook: claudeSettings?.hooks?.Stop || null
|
|
370
|
+
};
|
|
371
|
+
|
|
76
372
|
const exportData = {
|
|
77
373
|
version: CONFIG_VERSION,
|
|
78
374
|
exportedAt: new Date().toISOString(),
|
|
@@ -87,7 +383,14 @@ function exportAllConfigs() {
|
|
|
87
383
|
commands: commands || [],
|
|
88
384
|
rules: rules || [],
|
|
89
385
|
mcpServers: mcpServers || [],
|
|
90
|
-
markdownFiles: markdownFiles
|
|
386
|
+
markdownFiles: markdownFiles,
|
|
387
|
+
uiConfig: uiConfig,
|
|
388
|
+
terminalConfig: terminalConfig,
|
|
389
|
+
terminalCommands: terminalCommands,
|
|
390
|
+
prompts: prompts,
|
|
391
|
+
security: security,
|
|
392
|
+
appConfig: appConfig,
|
|
393
|
+
claudeHooks: claudeHooks
|
|
91
394
|
}
|
|
92
395
|
};
|
|
93
396
|
|
|
@@ -104,6 +407,30 @@ function exportAllConfigs() {
|
|
|
104
407
|
}
|
|
105
408
|
}
|
|
106
409
|
|
|
410
|
+
/**
|
|
411
|
+
* 导出所有配置为 ZIP
|
|
412
|
+
* @returns {Object} { success, data: Buffer, filename }
|
|
413
|
+
*/
|
|
414
|
+
function exportAllConfigsZip() {
|
|
415
|
+
const result = exportAllConfigs();
|
|
416
|
+
if (!result.success) {
|
|
417
|
+
return result;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const exportData = result.data;
|
|
421
|
+
const zip = new AdmZip();
|
|
422
|
+
const jsonContent = JSON.stringify(exportData, null, 2);
|
|
423
|
+
|
|
424
|
+
zip.addFile('config.json', Buffer.from(jsonContent, 'utf8'));
|
|
425
|
+
zip.addFile('README.md', Buffer.from(buildExportReadme(exportData), 'utf8'));
|
|
426
|
+
|
|
427
|
+
return {
|
|
428
|
+
success: true,
|
|
429
|
+
data: zip.toBuffer(),
|
|
430
|
+
filename: `ctx-config-${new Date().toISOString().split('T')[0]}.zip`
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
107
434
|
/**
|
|
108
435
|
* 导入配置
|
|
109
436
|
* @param {Object} importData - 导入的配置对象
|
|
@@ -123,7 +450,14 @@ function importConfigs(importData, options = {}) {
|
|
|
123
450
|
commands: { success: 0, failed: 0, skipped: 0 },
|
|
124
451
|
rules: { success: 0, failed: 0, skipped: 0 },
|
|
125
452
|
mcpServers: { success: 0, failed: 0, skipped: 0 },
|
|
126
|
-
markdownFiles: { success: 0, failed: 0, skipped: 0 }
|
|
453
|
+
markdownFiles: { success: 0, failed: 0, skipped: 0 },
|
|
454
|
+
uiConfig: { success: 0, failed: 0, skipped: 0 },
|
|
455
|
+
terminalConfig: { success: 0, failed: 0, skipped: 0 },
|
|
456
|
+
terminalCommands: { success: 0, failed: 0, skipped: 0 },
|
|
457
|
+
prompts: { success: 0, failed: 0, skipped: 0 },
|
|
458
|
+
security: { success: 0, failed: 0, skipped: 0 },
|
|
459
|
+
appConfig: { success: 0, failed: 0, skipped: 0 },
|
|
460
|
+
claudeHooks: { success: 0, failed: 0, skipped: 0 }
|
|
127
461
|
};
|
|
128
462
|
|
|
129
463
|
try {
|
|
@@ -143,7 +477,14 @@ function importConfigs(importData, options = {}) {
|
|
|
143
477
|
commands = [],
|
|
144
478
|
rules = [],
|
|
145
479
|
mcpServers = [],
|
|
146
|
-
markdownFiles = {}
|
|
480
|
+
markdownFiles = {},
|
|
481
|
+
uiConfig = null,
|
|
482
|
+
terminalConfig = null,
|
|
483
|
+
terminalCommands = null,
|
|
484
|
+
prompts = null,
|
|
485
|
+
security = null,
|
|
486
|
+
appConfig = null,
|
|
487
|
+
claudeHooks = null
|
|
147
488
|
} = importData.data;
|
|
148
489
|
|
|
149
490
|
// 导入权限模板
|
|
@@ -251,15 +592,24 @@ function importConfigs(importData, options = {}) {
|
|
|
251
592
|
}
|
|
252
593
|
|
|
253
594
|
// 导入 Agents
|
|
254
|
-
if (agents && agents.length > 0
|
|
595
|
+
if (agents && agents.length > 0) {
|
|
255
596
|
try {
|
|
256
|
-
const agentsService =
|
|
597
|
+
const agentsService = new AgentsService();
|
|
598
|
+
const baseDir = agentsService.userAgentsDir;
|
|
599
|
+
|
|
257
600
|
for (const agent of agents) {
|
|
258
|
-
|
|
259
|
-
|
|
601
|
+
const relativePath = agent.path || agent.fileName || agent.name;
|
|
602
|
+
const filePath = relativePath && relativePath.endsWith('.md')
|
|
603
|
+
? relativePath
|
|
604
|
+
: (relativePath ? `${relativePath}.md` : null);
|
|
605
|
+
const content = agent.fullContent || agent.content || buildAgentContent(agent);
|
|
606
|
+
|
|
607
|
+
const status = filePath ? writeTextFile(baseDir, filePath, content, overwrite) : 'failed';
|
|
608
|
+
if (status === 'success') {
|
|
260
609
|
results.agents.success++;
|
|
261
|
-
}
|
|
262
|
-
|
|
610
|
+
} else if (status === 'skipped') {
|
|
611
|
+
results.agents.skipped++;
|
|
612
|
+
} else {
|
|
263
613
|
results.agents.failed++;
|
|
264
614
|
}
|
|
265
615
|
}
|
|
@@ -269,16 +619,55 @@ function importConfigs(importData, options = {}) {
|
|
|
269
619
|
}
|
|
270
620
|
|
|
271
621
|
// 导入 Skills
|
|
272
|
-
if (skills && skills.length > 0
|
|
622
|
+
if (skills && skills.length > 0) {
|
|
273
623
|
try {
|
|
274
|
-
const skillService =
|
|
624
|
+
const skillService = new SkillService();
|
|
625
|
+
const baseDir = skillService.installDir;
|
|
626
|
+
ensureDir(baseDir);
|
|
627
|
+
|
|
275
628
|
for (const skill of skills) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
629
|
+
const directory = skill.directory;
|
|
630
|
+
const skillDir = resolveSafePath(baseDir, directory);
|
|
631
|
+
if (!skillDir) {
|
|
632
|
+
results.skills.failed++;
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (fs.existsSync(skillDir)) {
|
|
637
|
+
if (!overwrite) {
|
|
638
|
+
results.skills.skipped++;
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
ensureDir(skillDir);
|
|
645
|
+
const files = Array.isArray(skill.files) ? skill.files : [];
|
|
646
|
+
let failed = false;
|
|
647
|
+
|
|
648
|
+
for (const file of files) {
|
|
649
|
+
const filePath = resolveSafePath(skillDir, file.path);
|
|
650
|
+
if (!filePath) {
|
|
651
|
+
failed = true;
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
ensureDir(path.dirname(filePath));
|
|
655
|
+
try {
|
|
656
|
+
if (file.encoding === SKILL_FILE_ENCODING) {
|
|
657
|
+
fs.writeFileSync(filePath, Buffer.from(file.content || '', SKILL_FILE_ENCODING));
|
|
658
|
+
} else {
|
|
659
|
+
fs.writeFileSync(filePath, file.content || '', file.encoding || 'utf8');
|
|
660
|
+
}
|
|
661
|
+
} catch (err) {
|
|
662
|
+
failed = true;
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
if (failed || files.length === 0) {
|
|
281
668
|
results.skills.failed++;
|
|
669
|
+
} else {
|
|
670
|
+
results.skills.success++;
|
|
282
671
|
}
|
|
283
672
|
}
|
|
284
673
|
} catch (err) {
|
|
@@ -287,15 +676,25 @@ function importConfigs(importData, options = {}) {
|
|
|
287
676
|
}
|
|
288
677
|
|
|
289
678
|
// 导入 Commands
|
|
290
|
-
if (commands && commands.length > 0
|
|
679
|
+
if (commands && commands.length > 0) {
|
|
291
680
|
try {
|
|
292
|
-
const commandsService =
|
|
681
|
+
const commandsService = new CommandsService();
|
|
682
|
+
const baseDir = commandsService.userCommandsDir;
|
|
683
|
+
|
|
293
684
|
for (const command of commands) {
|
|
294
|
-
|
|
295
|
-
|
|
685
|
+
const relativePath = command.path || (
|
|
686
|
+
command.namespace
|
|
687
|
+
? path.join(command.namespace, `${command.name}.md`)
|
|
688
|
+
: `${command.name}.md`
|
|
689
|
+
);
|
|
690
|
+
const content = command.fullContent || command.content || buildCommandContent(command);
|
|
691
|
+
|
|
692
|
+
const status = relativePath ? writeTextFile(baseDir, relativePath, content, overwrite) : 'failed';
|
|
693
|
+
if (status === 'success') {
|
|
296
694
|
results.commands.success++;
|
|
297
|
-
}
|
|
298
|
-
|
|
695
|
+
} else if (status === 'skipped') {
|
|
696
|
+
results.commands.skipped++;
|
|
697
|
+
} else {
|
|
299
698
|
results.commands.failed++;
|
|
300
699
|
}
|
|
301
700
|
}
|
|
@@ -305,15 +704,25 @@ function importConfigs(importData, options = {}) {
|
|
|
305
704
|
}
|
|
306
705
|
|
|
307
706
|
// 导入 Rules
|
|
308
|
-
if (rules && rules.length > 0
|
|
707
|
+
if (rules && rules.length > 0) {
|
|
309
708
|
try {
|
|
310
|
-
const rulesService =
|
|
709
|
+
const rulesService = new RulesService();
|
|
710
|
+
const baseDir = rulesService.userRulesDir;
|
|
711
|
+
|
|
311
712
|
for (const rule of rules) {
|
|
312
|
-
|
|
313
|
-
|
|
713
|
+
const relativePath = rule.path || (
|
|
714
|
+
rule.directory
|
|
715
|
+
? path.join(rule.directory, `${rule.fileName || rule.name}.md`)
|
|
716
|
+
: `${rule.fileName || rule.name}.md`
|
|
717
|
+
);
|
|
718
|
+
const content = rule.fullContent || rule.content || buildRuleContent(rule);
|
|
719
|
+
|
|
720
|
+
const status = relativePath ? writeTextFile(baseDir, relativePath, content, overwrite) : 'failed';
|
|
721
|
+
if (status === 'success') {
|
|
314
722
|
results.rules.success++;
|
|
315
|
-
}
|
|
316
|
-
|
|
723
|
+
} else if (status === 'skipped') {
|
|
724
|
+
results.rules.skipped++;
|
|
725
|
+
} else {
|
|
317
726
|
results.rules.failed++;
|
|
318
727
|
}
|
|
319
728
|
}
|
|
@@ -355,6 +764,162 @@ function importConfigs(importData, options = {}) {
|
|
|
355
764
|
}
|
|
356
765
|
}
|
|
357
766
|
|
|
767
|
+
// 导入 UI 配置
|
|
768
|
+
if (uiConfig && typeof uiConfig === 'object') {
|
|
769
|
+
try {
|
|
770
|
+
if (fs.existsSync(CC_UI_CONFIG_PATH) && !overwrite) {
|
|
771
|
+
results.uiConfig.skipped++;
|
|
772
|
+
} else {
|
|
773
|
+
const { saveUIConfig } = require('./ui-config');
|
|
774
|
+
saveUIConfig(uiConfig);
|
|
775
|
+
results.uiConfig.success++;
|
|
776
|
+
}
|
|
777
|
+
} catch (err) {
|
|
778
|
+
console.error('[ConfigImport] 导入 UI 配置失败:', err);
|
|
779
|
+
results.uiConfig.failed++;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// 导入终端配置
|
|
784
|
+
if (terminalConfig && typeof terminalConfig === 'object') {
|
|
785
|
+
try {
|
|
786
|
+
if (fs.existsSync(CC_TERMINAL_CONFIG_PATH) && !overwrite) {
|
|
787
|
+
results.terminalConfig.skipped++;
|
|
788
|
+
} else {
|
|
789
|
+
const { saveTerminalConfig } = require('./terminal-config');
|
|
790
|
+
saveTerminalConfig(terminalConfig);
|
|
791
|
+
results.terminalConfig.success++;
|
|
792
|
+
}
|
|
793
|
+
} catch (err) {
|
|
794
|
+
console.error('[ConfigImport] 导入终端配置失败:', err);
|
|
795
|
+
results.terminalConfig.failed++;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// 导入终端命令配置
|
|
800
|
+
if (terminalCommands && typeof terminalCommands === 'object') {
|
|
801
|
+
try {
|
|
802
|
+
if (fs.existsSync(CC_TERMINAL_COMMANDS_PATH) && !overwrite) {
|
|
803
|
+
results.terminalCommands.skipped++;
|
|
804
|
+
} else {
|
|
805
|
+
const { saveTerminalCommands } = require('./terminal-commands');
|
|
806
|
+
const ok = saveTerminalCommands(terminalCommands);
|
|
807
|
+
if (ok) {
|
|
808
|
+
results.terminalCommands.success++;
|
|
809
|
+
} else {
|
|
810
|
+
results.terminalCommands.failed++;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
} catch (err) {
|
|
814
|
+
console.error('[ConfigImport] 导入终端命令配置失败:', err);
|
|
815
|
+
results.terminalCommands.failed++;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// 导入 Prompts 配置
|
|
820
|
+
if (prompts && typeof prompts === 'object') {
|
|
821
|
+
try {
|
|
822
|
+
const status = writeJsonFileAbsolute(CC_PROMPTS_PATH, prompts, overwrite);
|
|
823
|
+
if (status === 'success') {
|
|
824
|
+
results.prompts.success++;
|
|
825
|
+
} else if (status === 'skipped') {
|
|
826
|
+
results.prompts.skipped++;
|
|
827
|
+
} else {
|
|
828
|
+
results.prompts.failed++;
|
|
829
|
+
}
|
|
830
|
+
} catch (err) {
|
|
831
|
+
console.error('[ConfigImport] 导入 Prompts 失败:', err);
|
|
832
|
+
results.prompts.failed++;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// 导入安全配置
|
|
837
|
+
if (security && typeof security === 'object') {
|
|
838
|
+
try {
|
|
839
|
+
const status = writeJsonFileAbsolute(CC_SECURITY_PATH, security, overwrite, { mode: 0o600 });
|
|
840
|
+
if (status === 'success') {
|
|
841
|
+
results.security.success++;
|
|
842
|
+
} else if (status === 'skipped') {
|
|
843
|
+
results.security.skipped++;
|
|
844
|
+
} else {
|
|
845
|
+
results.security.failed++;
|
|
846
|
+
}
|
|
847
|
+
} catch (err) {
|
|
848
|
+
console.error('[ConfigImport] 导入安全配置失败:', err);
|
|
849
|
+
results.security.failed++;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// 导入高级配置(config.json)
|
|
854
|
+
if (appConfig && typeof appConfig === 'object') {
|
|
855
|
+
try {
|
|
856
|
+
const configFilePath = getConfigFilePath();
|
|
857
|
+
if (configFilePath && fs.existsSync(configFilePath) && !overwrite) {
|
|
858
|
+
results.appConfig.skipped++;
|
|
859
|
+
} else {
|
|
860
|
+
const { saveConfig } = require('../../config/loader');
|
|
861
|
+
saveConfig(appConfig);
|
|
862
|
+
results.appConfig.success++;
|
|
863
|
+
}
|
|
864
|
+
} catch (err) {
|
|
865
|
+
console.error('[ConfigImport] 导入高级配置失败:', err);
|
|
866
|
+
results.appConfig.failed++;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// 导入 Claude Hooks 配置
|
|
871
|
+
if (claudeHooks && typeof claudeHooks === 'object') {
|
|
872
|
+
let didWrite = false;
|
|
873
|
+
let didSkip = false;
|
|
874
|
+
let didFail = false;
|
|
875
|
+
|
|
876
|
+
try {
|
|
877
|
+
if (claudeHooks.uiConfig && typeof claudeHooks.uiConfig === 'object') {
|
|
878
|
+
const status = writeJsonFileAbsolute(LEGACY_UI_CONFIG_PATH, claudeHooks.uiConfig, overwrite);
|
|
879
|
+
if (status === 'success') didWrite = true;
|
|
880
|
+
else if (status === 'skipped') didSkip = true;
|
|
881
|
+
else didFail = true;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (claudeHooks.notifyScript !== undefined && claudeHooks.notifyScript !== null) {
|
|
885
|
+
const status = writeTextFileAbsolute(LEGACY_NOTIFY_HOOK_PATH, claudeHooks.notifyScript, overwrite, { mode: 0o755 });
|
|
886
|
+
if (status === 'success') didWrite = true;
|
|
887
|
+
else if (status === 'skipped') didSkip = true;
|
|
888
|
+
else didFail = true;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
if (claudeHooks.stopHook !== undefined) {
|
|
892
|
+
if (!overwrite && fs.existsSync(CLAUDE_SETTINGS_PATH)) {
|
|
893
|
+
didSkip = true;
|
|
894
|
+
} else {
|
|
895
|
+
const settings = readJsonFileSafe(CLAUDE_SETTINGS_PATH) || {};
|
|
896
|
+
settings.hooks = settings.hooks || {};
|
|
897
|
+
if (claudeHooks.stopHook) {
|
|
898
|
+
settings.hooks.Stop = claudeHooks.stopHook;
|
|
899
|
+
} else if (settings.hooks.Stop) {
|
|
900
|
+
delete settings.hooks.Stop;
|
|
901
|
+
if (Object.keys(settings.hooks).length === 0) {
|
|
902
|
+
delete settings.hooks;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
writeJsonFileAbsolute(CLAUDE_SETTINGS_PATH, settings, true);
|
|
906
|
+
didWrite = true;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
} catch (err) {
|
|
910
|
+
console.error('[ConfigImport] 导入 Claude Hooks 失败:', err);
|
|
911
|
+
didFail = true;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
if (didFail) {
|
|
915
|
+
results.claudeHooks.failed++;
|
|
916
|
+
} else if (didWrite) {
|
|
917
|
+
results.claudeHooks.success++;
|
|
918
|
+
} else if (didSkip) {
|
|
919
|
+
results.claudeHooks.skipped++;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
358
923
|
return {
|
|
359
924
|
success: true,
|
|
360
925
|
results,
|
|
@@ -387,7 +952,14 @@ function generateImportSummary(results) {
|
|
|
387
952
|
{ key: 'commands', label: 'Commands' },
|
|
388
953
|
{ key: 'rules', label: 'Rules' },
|
|
389
954
|
{ key: 'mcpServers', label: 'MCP服务器' },
|
|
390
|
-
{ key: 'markdownFiles', label: 'Markdown文件' }
|
|
955
|
+
{ key: 'markdownFiles', label: 'Markdown文件' },
|
|
956
|
+
{ key: 'uiConfig', label: 'UI配置' },
|
|
957
|
+
{ key: 'terminalConfig', label: '终端配置' },
|
|
958
|
+
{ key: 'terminalCommands', label: '终端命令' },
|
|
959
|
+
{ key: 'prompts', label: 'Prompts' },
|
|
960
|
+
{ key: 'security', label: '安全配置' },
|
|
961
|
+
{ key: 'appConfig', label: '高级配置' },
|
|
962
|
+
{ key: 'claudeHooks', label: 'Claude Hooks' }
|
|
391
963
|
];
|
|
392
964
|
|
|
393
965
|
for (const { key, label } of types) {
|
|
@@ -411,5 +983,6 @@ function generateImportSummary(results) {
|
|
|
411
983
|
|
|
412
984
|
module.exports = {
|
|
413
985
|
exportAllConfigs,
|
|
986
|
+
exportAllConfigsZip,
|
|
414
987
|
importConfigs
|
|
415
988
|
};
|