@adversity/coding-tool-x 2.5.0 → 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 +8 -0
- package/dist/web/assets/{index-CcYz-Mcz.css → index-CvHZsWbE.css} +2 -2
- package/dist/web/assets/index-DZjidyED.js +14 -0
- package/dist/web/index.html +2 -2
- package/package.json +2 -2
- package/src/server/api/config-export.js +122 -32
- package/src/server/services/config-export-service.js +331 -5
- package/src/server/services/ui-config.js +2 -0
- package/dist/web/assets/index-k9b43kTe.js +0 -14
package/dist/web/index.html
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
<link rel="icon" href="/favicon.ico">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>CC-TOOL - ClaudeCode增强工作助手</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-DZjidyED.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/vue-vendor-CEeI-Azr.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vendors-CzcvkTIS.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/icons-BALJo7bE.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/naive-ui-sh0u_0bf.js">
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CvHZsWbE.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div id="app"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adversity/coding-tool-x",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -72,4 +72,4 @@
|
|
|
72
72
|
"url": "https://github.com/ZeaoZhang/coding-tool/issues"
|
|
73
73
|
},
|
|
74
74
|
"homepage": "https://github.com/ZeaoZhang/coding-tool#readme"
|
|
75
|
-
}
|
|
75
|
+
}
|
|
@@ -4,23 +4,74 @@
|
|
|
4
4
|
|
|
5
5
|
const express = require('express');
|
|
6
6
|
const configExportService = require('../services/config-export-service');
|
|
7
|
+
const AdmZip = require('adm-zip');
|
|
7
8
|
|
|
8
9
|
const router = express.Router();
|
|
9
10
|
|
|
11
|
+
function parseConfigZip(buffer) {
|
|
12
|
+
const zip = new AdmZip(buffer);
|
|
13
|
+
const entry = zip.getEntry('config.json');
|
|
14
|
+
if (!entry) {
|
|
15
|
+
throw new Error('配置包缺少 config.json');
|
|
16
|
+
}
|
|
17
|
+
const content = entry.getData().toString('utf8');
|
|
18
|
+
return JSON.parse(content);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildPreviewSummary(data) {
|
|
22
|
+
return {
|
|
23
|
+
version: data.version,
|
|
24
|
+
exportedAt: data.exportedAt,
|
|
25
|
+
counts: {
|
|
26
|
+
permissionTemplates: (data.data.permissionTemplates || []).length,
|
|
27
|
+
configTemplates: (data.data.configTemplates || []).length,
|
|
28
|
+
channels: (data.data.channels || []).length
|
|
29
|
+
},
|
|
30
|
+
items: {
|
|
31
|
+
permissionTemplates: (data.data.permissionTemplates || []).map(t => ({
|
|
32
|
+
id: t.id,
|
|
33
|
+
name: t.name,
|
|
34
|
+
description: t.description
|
|
35
|
+
})),
|
|
36
|
+
configTemplates: (data.data.configTemplates || []).map(t => ({
|
|
37
|
+
id: t.id,
|
|
38
|
+
name: t.name,
|
|
39
|
+
description: t.description
|
|
40
|
+
})),
|
|
41
|
+
channels: (data.data.channels || []).map(c => ({
|
|
42
|
+
id: c.id,
|
|
43
|
+
name: c.name,
|
|
44
|
+
type: c.type
|
|
45
|
+
}))
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
10
50
|
/**
|
|
11
51
|
* 导出所有配置
|
|
12
52
|
* GET /api/config-export
|
|
13
53
|
*/
|
|
14
54
|
router.get('/', (req, res) => {
|
|
15
55
|
try {
|
|
16
|
-
const
|
|
56
|
+
const format = (req.query.format || 'json').toLowerCase();
|
|
57
|
+
const result = format === 'zip'
|
|
58
|
+
? configExportService.exportAllConfigsZip()
|
|
59
|
+
: configExportService.exportAllConfigs();
|
|
17
60
|
|
|
18
61
|
if (result.success) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
62
|
+
if (format === 'zip') {
|
|
63
|
+
// 设置响应头,触发文件下载
|
|
64
|
+
const filename = result.filename || `ctx-config-${new Date().toISOString().split('T')[0]}.zip`;
|
|
65
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
66
|
+
res.setHeader('Content-Type', 'application/zip');
|
|
67
|
+
res.send(result.data);
|
|
68
|
+
} else {
|
|
69
|
+
// 设置响应头,触发文件下载
|
|
70
|
+
const filename = `ctx-config-${new Date().toISOString().split('T')[0]}.json`;
|
|
71
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
72
|
+
res.setHeader('Content-Type', 'application/json');
|
|
73
|
+
res.json(result.data);
|
|
74
|
+
}
|
|
24
75
|
} else {
|
|
25
76
|
res.status(500).json({
|
|
26
77
|
success: false,
|
|
@@ -64,6 +115,34 @@ router.post('/import', (req, res) => {
|
|
|
64
115
|
}
|
|
65
116
|
});
|
|
66
117
|
|
|
118
|
+
/**
|
|
119
|
+
* 导入 ZIP 配置
|
|
120
|
+
* POST /api/config-export/import-zip
|
|
121
|
+
*/
|
|
122
|
+
router.post('/import-zip', express.raw({ type: ['application/zip', 'application/octet-stream'], limit: '100mb' }), (req, res) => {
|
|
123
|
+
try {
|
|
124
|
+
const overwrite = req.query.overwrite === 'true';
|
|
125
|
+
const buffer = req.body;
|
|
126
|
+
|
|
127
|
+
if (!Buffer.isBuffer(buffer) || buffer.length === 0) {
|
|
128
|
+
return res.status(400).json({
|
|
129
|
+
success: false,
|
|
130
|
+
message: '缺少 ZIP 文件内容'
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const data = parseConfigZip(buffer);
|
|
135
|
+
const result = configExportService.importConfigs(data, { overwrite });
|
|
136
|
+
res.json(result);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.error('[ConfigExport API] 导入 ZIP 失败:', err);
|
|
139
|
+
res.status(500).json({
|
|
140
|
+
success: false,
|
|
141
|
+
message: err.message
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
67
146
|
/**
|
|
68
147
|
* 预览导入配置(不实际导入)
|
|
69
148
|
* POST /api/config-export/preview
|
|
@@ -79,32 +158,7 @@ router.post('/preview', (req, res) => {
|
|
|
79
158
|
});
|
|
80
159
|
}
|
|
81
160
|
|
|
82
|
-
const summary =
|
|
83
|
-
version: data.version,
|
|
84
|
-
exportedAt: data.exportedAt,
|
|
85
|
-
counts: {
|
|
86
|
-
permissionTemplates: (data.data.permissionTemplates || []).length,
|
|
87
|
-
configTemplates: (data.data.configTemplates || []).length,
|
|
88
|
-
channels: (data.data.channels || []).length
|
|
89
|
-
},
|
|
90
|
-
items: {
|
|
91
|
-
permissionTemplates: (data.data.permissionTemplates || []).map(t => ({
|
|
92
|
-
id: t.id,
|
|
93
|
-
name: t.name,
|
|
94
|
-
description: t.description
|
|
95
|
-
})),
|
|
96
|
-
configTemplates: (data.data.configTemplates || []).map(t => ({
|
|
97
|
-
id: t.id,
|
|
98
|
-
name: t.name,
|
|
99
|
-
description: t.description
|
|
100
|
-
})),
|
|
101
|
-
channels: (data.data.channels || []).map(c => ({
|
|
102
|
-
id: c.id,
|
|
103
|
-
name: c.name,
|
|
104
|
-
type: c.type
|
|
105
|
-
}))
|
|
106
|
-
}
|
|
107
|
-
};
|
|
161
|
+
const summary = buildPreviewSummary(data);
|
|
108
162
|
|
|
109
163
|
res.json({
|
|
110
164
|
success: true,
|
|
@@ -119,4 +173,40 @@ router.post('/preview', (req, res) => {
|
|
|
119
173
|
}
|
|
120
174
|
});
|
|
121
175
|
|
|
176
|
+
/**
|
|
177
|
+
* 预览 ZIP 导入配置(不实际导入)
|
|
178
|
+
* POST /api/config-export/preview-zip
|
|
179
|
+
*/
|
|
180
|
+
router.post('/preview-zip', express.raw({ type: ['application/zip', 'application/octet-stream'], limit: '100mb' }), (req, res) => {
|
|
181
|
+
try {
|
|
182
|
+
const buffer = req.body;
|
|
183
|
+
if (!Buffer.isBuffer(buffer) || buffer.length === 0) {
|
|
184
|
+
return res.status(400).json({
|
|
185
|
+
success: false,
|
|
186
|
+
message: '缺少 ZIP 文件内容'
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const data = parseConfigZip(buffer);
|
|
191
|
+
if (!data || !data.data) {
|
|
192
|
+
return res.status(400).json({
|
|
193
|
+
success: false,
|
|
194
|
+
message: '无效的导入数据格式'
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const summary = buildPreviewSummary(data);
|
|
199
|
+
res.json({
|
|
200
|
+
success: true,
|
|
201
|
+
data: summary
|
|
202
|
+
});
|
|
203
|
+
} catch (err) {
|
|
204
|
+
console.error('[ConfigExport API] 预览 ZIP 失败:', err);
|
|
205
|
+
res.status(500).json({
|
|
206
|
+
success: false,
|
|
207
|
+
message: err.message
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
122
212
|
module.exports = router;
|
|
@@ -5,6 +5,8 @@
|
|
|
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,10 +15,20 @@ const { CommandsService } = require('./commands-service');
|
|
|
13
15
|
const { RulesService } = require('./rules-service');
|
|
14
16
|
const { SkillService } = require('./skill-service');
|
|
15
17
|
|
|
16
|
-
const CONFIG_VERSION = '1.
|
|
18
|
+
const CONFIG_VERSION = '1.1.0';
|
|
17
19
|
const SKILL_FILE_ENCODING = 'base64';
|
|
18
20
|
const SKILL_IGNORE_DIRS = new Set(['.git']);
|
|
19
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');
|
|
20
32
|
|
|
21
33
|
function ensureDir(dirPath) {
|
|
22
34
|
if (!fs.existsSync(dirPath)) {
|
|
@@ -24,6 +36,90 @@ function ensureDir(dirPath) {
|
|
|
24
36
|
}
|
|
25
37
|
}
|
|
26
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
|
+
}
|
|
91
|
+
|
|
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
|
+
|
|
27
123
|
function resolveSafePath(baseDir, relativePath) {
|
|
28
124
|
if (!relativePath || typeof relativePath !== 'string') return null;
|
|
29
125
|
const normalized = path.normalize(relativePath);
|
|
@@ -252,6 +348,27 @@ function exportAllConfigs() {
|
|
|
252
348
|
}
|
|
253
349
|
}
|
|
254
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
|
+
|
|
255
372
|
const exportData = {
|
|
256
373
|
version: CONFIG_VERSION,
|
|
257
374
|
exportedAt: new Date().toISOString(),
|
|
@@ -266,7 +383,14 @@ function exportAllConfigs() {
|
|
|
266
383
|
commands: commands || [],
|
|
267
384
|
rules: rules || [],
|
|
268
385
|
mcpServers: mcpServers || [],
|
|
269
|
-
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
|
|
270
394
|
}
|
|
271
395
|
};
|
|
272
396
|
|
|
@@ -283,6 +407,30 @@ function exportAllConfigs() {
|
|
|
283
407
|
}
|
|
284
408
|
}
|
|
285
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
|
+
|
|
286
434
|
/**
|
|
287
435
|
* 导入配置
|
|
288
436
|
* @param {Object} importData - 导入的配置对象
|
|
@@ -302,7 +450,14 @@ function importConfigs(importData, options = {}) {
|
|
|
302
450
|
commands: { success: 0, failed: 0, skipped: 0 },
|
|
303
451
|
rules: { success: 0, failed: 0, skipped: 0 },
|
|
304
452
|
mcpServers: { success: 0, failed: 0, skipped: 0 },
|
|
305
|
-
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 }
|
|
306
461
|
};
|
|
307
462
|
|
|
308
463
|
try {
|
|
@@ -322,7 +477,14 @@ function importConfigs(importData, options = {}) {
|
|
|
322
477
|
commands = [],
|
|
323
478
|
rules = [],
|
|
324
479
|
mcpServers = [],
|
|
325
|
-
markdownFiles = {}
|
|
480
|
+
markdownFiles = {},
|
|
481
|
+
uiConfig = null,
|
|
482
|
+
terminalConfig = null,
|
|
483
|
+
terminalCommands = null,
|
|
484
|
+
prompts = null,
|
|
485
|
+
security = null,
|
|
486
|
+
appConfig = null,
|
|
487
|
+
claudeHooks = null
|
|
326
488
|
} = importData.data;
|
|
327
489
|
|
|
328
490
|
// 导入权限模板
|
|
@@ -602,6 +764,162 @@ function importConfigs(importData, options = {}) {
|
|
|
602
764
|
}
|
|
603
765
|
}
|
|
604
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
|
+
|
|
605
923
|
return {
|
|
606
924
|
success: true,
|
|
607
925
|
results,
|
|
@@ -634,7 +952,14 @@ function generateImportSummary(results) {
|
|
|
634
952
|
{ key: 'commands', label: 'Commands' },
|
|
635
953
|
{ key: 'rules', label: 'Rules' },
|
|
636
954
|
{ key: 'mcpServers', label: 'MCP服务器' },
|
|
637
|
-
{ 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' }
|
|
638
963
|
];
|
|
639
964
|
|
|
640
965
|
for (const { key, label } of types) {
|
|
@@ -658,5 +983,6 @@ function generateImportSummary(results) {
|
|
|
658
983
|
|
|
659
984
|
module.exports = {
|
|
660
985
|
exportAllConfigs,
|
|
986
|
+
exportAllConfigsZip,
|
|
661
987
|
importConfigs
|
|
662
988
|
};
|
|
@@ -53,6 +53,8 @@ function readUIConfigFromFile() {
|
|
|
53
53
|
const data = JSON.parse(content);
|
|
54
54
|
// Merge with defaults to ensure all keys exist
|
|
55
55
|
return {
|
|
56
|
+
...DEFAULT_UI_CONFIG,
|
|
57
|
+
...data,
|
|
56
58
|
theme: data.theme || DEFAULT_UI_CONFIG.theme,
|
|
57
59
|
panelVisibility: { ...DEFAULT_UI_CONFIG.panelVisibility, ...data.panelVisibility },
|
|
58
60
|
channelLocks: { ...DEFAULT_UI_CONFIG.channelLocks, ...data.channelLocks },
|