@adversity/coding-tool-x 2.4.1 → 2.5.0
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 +27 -0
- package/dist/web/assets/{icons-Dom8a0SN.js → icons-BALJo7bE.js} +1 -1
- package/dist/web/assets/{index-CQeUIH7E.css → index-CcYz-Mcz.css} +3 -3
- package/dist/web/assets/index-k9b43kTe.js +14 -0
- package/dist/web/assets/{naive-ui-BjMHakwv.js → naive-ui-sh0u_0bf.js} +1 -1
- package/dist/web/assets/{vendors-DtJKdpSj.js → vendors-CzcvkTIS.js} +1 -1
- package/dist/web/assets/vue-vendor-CEeI-Azr.js +44 -0
- 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/security.js +53 -0
- package/src/server/api/terminal.js +5 -1
- package/src/server/api/workspaces.js +2 -2
- package/src/server/index.js +3 -2
- package/src/server/services/config-export-service.js +280 -33
- package/src/server/services/pty-manager.js +250 -28
- package/src/server/services/security-config.js +131 -0
- package/src/server/services/sessions.js +13 -4
- package/src/server/services/terminal-commands.js +5 -0
- package/src/server/services/terminal-config.js +81 -1
- package/src/server/services/terminal-detector.js +76 -1
- package/src/server/services/workspace-service.js +58 -37
- package/src/server/websocket-server.js +14 -3
- package/dist/web/assets/index-YrjlFzC4.js +0 -14
- package/dist/web/assets/vue-vendor-VFuFB5f4.js +0 -44
|
@@ -9,6 +9,7 @@ const path = require('path');
|
|
|
9
9
|
const fs = require('fs');
|
|
10
10
|
|
|
11
11
|
const { ptyManager } = require('../services/pty-manager');
|
|
12
|
+
const { getWebTerminalShellConfig } = require('../services/terminal-config');
|
|
12
13
|
const {
|
|
13
14
|
loadTerminalCommands,
|
|
14
15
|
saveTerminalCommands,
|
|
@@ -171,13 +172,16 @@ router.post('/create', (req, res) => {
|
|
|
171
172
|
// 获取启动命令
|
|
172
173
|
const startCommand = getCommandForChannel(channel, sessionId, workDir);
|
|
173
174
|
|
|
175
|
+
const shellConfig = getWebTerminalShellConfig();
|
|
176
|
+
|
|
174
177
|
// 创建终端
|
|
175
178
|
const terminal = ptyManager.create({
|
|
176
179
|
cwd: workDir,
|
|
177
180
|
channel,
|
|
178
181
|
sessionId,
|
|
179
182
|
projectName,
|
|
180
|
-
startCommand
|
|
183
|
+
startCommand,
|
|
184
|
+
...shellConfig
|
|
181
185
|
});
|
|
182
186
|
|
|
183
187
|
res.json({
|
|
@@ -109,9 +109,9 @@ router.get('/check-git/*', (req, res) => {
|
|
|
109
109
|
* GET /api/workspaces/available-projects
|
|
110
110
|
* 获取所有渠道(Claude/Codex/Gemini)的项目并集
|
|
111
111
|
*/
|
|
112
|
-
router.get('/available-projects', (req, res) => {
|
|
112
|
+
router.get('/available-projects', async (req, res) => {
|
|
113
113
|
try {
|
|
114
|
-
const projects = workspaceService.getAllAvailableProjects();
|
|
114
|
+
const projects = await workspaceService.getAllAvailableProjects();
|
|
115
115
|
res.json({
|
|
116
116
|
success: true,
|
|
117
117
|
data: projects
|
package/src/server/index.js
CHANGED
|
@@ -109,6 +109,7 @@ async function startServer(port) {
|
|
|
109
109
|
app.use('/api/aliases', require('./api/aliases')());
|
|
110
110
|
app.use('/api/favorites', require('./api/favorites'));
|
|
111
111
|
app.use('/api/ui-config', require('./api/ui-config'));
|
|
112
|
+
app.use('/api/security', require('./api/security'));
|
|
112
113
|
app.use('/api/channels', require('./api/channels'));
|
|
113
114
|
app.use('/api/proxy', require('./api/proxy'));
|
|
114
115
|
app.use('/api/codex/proxy', require('./api/codex-proxy'));
|
|
@@ -264,7 +265,7 @@ function autoRestoreProxies() {
|
|
|
264
265
|
}
|
|
265
266
|
|
|
266
267
|
// 启动时执行健康检查
|
|
267
|
-
function performStartupHealthCheck() {
|
|
268
|
+
async function performStartupHealthCheck() {
|
|
268
269
|
const { healthCheckAllProjects } = require('./services/health-check');
|
|
269
270
|
const { getProjects } = require('./services/sessions');
|
|
270
271
|
|
|
@@ -273,7 +274,7 @@ function performStartupHealthCheck() {
|
|
|
273
274
|
|
|
274
275
|
// 获取所有项目
|
|
275
276
|
const config = loadConfig();
|
|
276
|
-
const projects = getProjects(config);
|
|
277
|
+
const projects = await getProjects(config);
|
|
277
278
|
|
|
278
279
|
if (projects.length === 0) {
|
|
279
280
|
console.log(chalk.gray(' 未发现项目,跳过健康检查'));
|
|
@@ -8,8 +8,158 @@ const path = require('path');
|
|
|
8
8
|
const permissionTemplatesService = require('./permission-templates-service');
|
|
9
9
|
const configTemplatesService = require('./config-templates-service');
|
|
10
10
|
const channelsService = require('./channels');
|
|
11
|
+
const { AgentsService } = require('./agents-service');
|
|
12
|
+
const { CommandsService } = require('./commands-service');
|
|
13
|
+
const { RulesService } = require('./rules-service');
|
|
14
|
+
const { SkillService } = require('./skill-service');
|
|
11
15
|
|
|
12
16
|
const CONFIG_VERSION = '1.0.0';
|
|
17
|
+
const SKILL_FILE_ENCODING = 'base64';
|
|
18
|
+
const SKILL_IGNORE_DIRS = new Set(['.git']);
|
|
19
|
+
const SKILL_IGNORE_FILES = new Set(['.DS_Store']);
|
|
20
|
+
|
|
21
|
+
function ensureDir(dirPath) {
|
|
22
|
+
if (!fs.existsSync(dirPath)) {
|
|
23
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function resolveSafePath(baseDir, relativePath) {
|
|
28
|
+
if (!relativePath || typeof relativePath !== 'string') return null;
|
|
29
|
+
const normalized = path.normalize(relativePath);
|
|
30
|
+
if (path.isAbsolute(normalized)) return null;
|
|
31
|
+
const resolved = path.resolve(baseDir, normalized);
|
|
32
|
+
const relative = path.relative(baseDir, resolved);
|
|
33
|
+
if (relative.startsWith('..') || path.isAbsolute(relative)) return null;
|
|
34
|
+
return resolved;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function buildAgentContent(agent) {
|
|
38
|
+
const lines = ['---'];
|
|
39
|
+
if (agent.name) lines.push(`name: ${agent.name}`);
|
|
40
|
+
if (agent.description) lines.push(`description: "${agent.description}"`);
|
|
41
|
+
if (agent.tools) lines.push(`tools: ${agent.tools}`);
|
|
42
|
+
if (agent.model) lines.push(`model: ${agent.model}`);
|
|
43
|
+
if (agent.permissionMode) lines.push(`permissionMode: ${agent.permissionMode}`);
|
|
44
|
+
if (agent.skills) lines.push(`skills: ${agent.skills}`);
|
|
45
|
+
lines.push('---');
|
|
46
|
+
const body = agent.systemPrompt || '';
|
|
47
|
+
return `${lines.join('\n')}\n\n${body}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function buildCommandContent(command) {
|
|
51
|
+
const frontmatter = {};
|
|
52
|
+
if (command.description) frontmatter.description = command.description;
|
|
53
|
+
if (command.allowedTools) frontmatter['allowed-tools'] = command.allowedTools;
|
|
54
|
+
if (command.argumentHint) frontmatter['argument-hint'] = command.argumentHint;
|
|
55
|
+
|
|
56
|
+
const lines = [];
|
|
57
|
+
if (Object.keys(frontmatter).length > 0) {
|
|
58
|
+
lines.push('---');
|
|
59
|
+
if (frontmatter.description) {
|
|
60
|
+
lines.push(`description: "${frontmatter.description}"`);
|
|
61
|
+
}
|
|
62
|
+
if (frontmatter['allowed-tools']) {
|
|
63
|
+
lines.push(`allowed-tools: ${frontmatter['allowed-tools']}`);
|
|
64
|
+
}
|
|
65
|
+
if (frontmatter['argument-hint']) {
|
|
66
|
+
lines.push(`argument-hint: ${frontmatter['argument-hint']}`);
|
|
67
|
+
}
|
|
68
|
+
lines.push('---');
|
|
69
|
+
lines.push('');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const body = command.body || '';
|
|
73
|
+
return `${lines.join('\n')}${body}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function buildRuleContent(rule) {
|
|
77
|
+
const lines = [];
|
|
78
|
+
if (rule.paths) {
|
|
79
|
+
lines.push('---');
|
|
80
|
+
lines.push(`paths: ${rule.paths}`);
|
|
81
|
+
lines.push('---');
|
|
82
|
+
lines.push('');
|
|
83
|
+
}
|
|
84
|
+
const body = rule.body || '';
|
|
85
|
+
return `${lines.join('\n')}${body}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function collectSkillFiles(baseDir) {
|
|
89
|
+
const files = [];
|
|
90
|
+
const stack = [baseDir];
|
|
91
|
+
|
|
92
|
+
while (stack.length > 0) {
|
|
93
|
+
const currentDir = stack.pop();
|
|
94
|
+
let entries = [];
|
|
95
|
+
try {
|
|
96
|
+
entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
97
|
+
} catch (err) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
for (const entry of entries) {
|
|
102
|
+
if (entry.isDirectory()) {
|
|
103
|
+
if (SKILL_IGNORE_DIRS.has(entry.name)) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
stack.push(path.join(currentDir, entry.name));
|
|
107
|
+
} else if (entry.isFile()) {
|
|
108
|
+
if (SKILL_IGNORE_FILES.has(entry.name)) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
112
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
113
|
+
try {
|
|
114
|
+
const content = fs.readFileSync(fullPath);
|
|
115
|
+
files.push({
|
|
116
|
+
path: relativePath,
|
|
117
|
+
encoding: SKILL_FILE_ENCODING,
|
|
118
|
+
content: content.toString(SKILL_FILE_ENCODING)
|
|
119
|
+
});
|
|
120
|
+
} catch (err) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
128
|
+
return files;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function exportSkillsSnapshot() {
|
|
132
|
+
const skillService = new SkillService();
|
|
133
|
+
const installedSkills = skillService.getInstalledSkills();
|
|
134
|
+
const baseDir = skillService.installDir;
|
|
135
|
+
|
|
136
|
+
return installedSkills.map((skill) => {
|
|
137
|
+
const directory = skill.directory;
|
|
138
|
+
const skillDir = resolveSafePath(baseDir, directory);
|
|
139
|
+
if (!skillDir || !fs.existsSync(skillDir)) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
directory,
|
|
144
|
+
name: skill.name || directory,
|
|
145
|
+
description: skill.description || '',
|
|
146
|
+
files: collectSkillFiles(skillDir)
|
|
147
|
+
};
|
|
148
|
+
}).filter(Boolean);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function writeTextFile(baseDir, relativePath, content, overwrite) {
|
|
152
|
+
if (!content && content !== '') {
|
|
153
|
+
return 'failed';
|
|
154
|
+
}
|
|
155
|
+
const targetPath = resolveSafePath(baseDir, relativePath);
|
|
156
|
+
if (!targetPath) return 'failed';
|
|
157
|
+
if (fs.existsSync(targetPath) && !overwrite) return 'skipped';
|
|
158
|
+
|
|
159
|
+
ensureDir(path.dirname(targetPath));
|
|
160
|
+
fs.writeFileSync(targetPath, content, 'utf8');
|
|
161
|
+
return 'success';
|
|
162
|
+
}
|
|
13
163
|
|
|
14
164
|
/**
|
|
15
165
|
* 导出所有配置为JSON
|
|
@@ -38,20 +188,49 @@ function exportAllConfigs() {
|
|
|
38
188
|
const favorites = favoritesService.loadFavorites();
|
|
39
189
|
|
|
40
190
|
// 获取 Agents 配置
|
|
41
|
-
const agentsService =
|
|
42
|
-
const agents = agentsService.
|
|
191
|
+
const agentsService = new AgentsService();
|
|
192
|
+
const { agents: rawAgents } = agentsService.listAgents();
|
|
193
|
+
const agents = rawAgents.map(agent => ({
|
|
194
|
+
fileName: agent.fileName,
|
|
195
|
+
name: agent.name,
|
|
196
|
+
description: agent.description,
|
|
197
|
+
tools: agent.tools,
|
|
198
|
+
model: agent.model,
|
|
199
|
+
permissionMode: agent.permissionMode,
|
|
200
|
+
skills: agent.skills,
|
|
201
|
+
path: agent.path,
|
|
202
|
+
systemPrompt: agent.systemPrompt,
|
|
203
|
+
fullContent: agent.fullContent
|
|
204
|
+
}));
|
|
43
205
|
|
|
44
206
|
// 获取 Skills 配置
|
|
45
|
-
const
|
|
46
|
-
const skills = skillService.getAllSkills();
|
|
207
|
+
const skills = exportSkillsSnapshot();
|
|
47
208
|
|
|
48
209
|
// 获取 Commands 配置
|
|
49
|
-
const commandsService =
|
|
50
|
-
const commands = commandsService.
|
|
210
|
+
const commandsService = new CommandsService();
|
|
211
|
+
const { commands: rawCommands } = commandsService.listCommands();
|
|
212
|
+
const commands = rawCommands.map(command => ({
|
|
213
|
+
name: command.name,
|
|
214
|
+
namespace: command.namespace,
|
|
215
|
+
description: command.description,
|
|
216
|
+
allowedTools: command.allowedTools,
|
|
217
|
+
argumentHint: command.argumentHint,
|
|
218
|
+
path: command.path,
|
|
219
|
+
body: command.body,
|
|
220
|
+
fullContent: command.fullContent
|
|
221
|
+
}));
|
|
51
222
|
|
|
52
223
|
// 获取 Rules 配置
|
|
53
|
-
const rulesService =
|
|
54
|
-
const rules = rulesService.
|
|
224
|
+
const rulesService = new RulesService();
|
|
225
|
+
const { rules: rawRules } = rulesService.listRules();
|
|
226
|
+
const rules = rawRules.map(rule => ({
|
|
227
|
+
fileName: rule.fileName,
|
|
228
|
+
directory: rule.directory,
|
|
229
|
+
paths: rule.paths,
|
|
230
|
+
path: rule.path,
|
|
231
|
+
body: rule.body,
|
|
232
|
+
fullContent: rule.fullContent
|
|
233
|
+
}));
|
|
55
234
|
|
|
56
235
|
// 获取 MCP 配置
|
|
57
236
|
const mcpService = require('./mcp-service');
|
|
@@ -251,15 +430,24 @@ function importConfigs(importData, options = {}) {
|
|
|
251
430
|
}
|
|
252
431
|
|
|
253
432
|
// 导入 Agents
|
|
254
|
-
if (agents && agents.length > 0
|
|
433
|
+
if (agents && agents.length > 0) {
|
|
255
434
|
try {
|
|
256
|
-
const agentsService =
|
|
435
|
+
const agentsService = new AgentsService();
|
|
436
|
+
const baseDir = agentsService.userAgentsDir;
|
|
437
|
+
|
|
257
438
|
for (const agent of agents) {
|
|
258
|
-
|
|
259
|
-
|
|
439
|
+
const relativePath = agent.path || agent.fileName || agent.name;
|
|
440
|
+
const filePath = relativePath && relativePath.endsWith('.md')
|
|
441
|
+
? relativePath
|
|
442
|
+
: (relativePath ? `${relativePath}.md` : null);
|
|
443
|
+
const content = agent.fullContent || agent.content || buildAgentContent(agent);
|
|
444
|
+
|
|
445
|
+
const status = filePath ? writeTextFile(baseDir, filePath, content, overwrite) : 'failed';
|
|
446
|
+
if (status === 'success') {
|
|
260
447
|
results.agents.success++;
|
|
261
|
-
}
|
|
262
|
-
|
|
448
|
+
} else if (status === 'skipped') {
|
|
449
|
+
results.agents.skipped++;
|
|
450
|
+
} else {
|
|
263
451
|
results.agents.failed++;
|
|
264
452
|
}
|
|
265
453
|
}
|
|
@@ -269,16 +457,55 @@ function importConfigs(importData, options = {}) {
|
|
|
269
457
|
}
|
|
270
458
|
|
|
271
459
|
// 导入 Skills
|
|
272
|
-
if (skills && skills.length > 0
|
|
460
|
+
if (skills && skills.length > 0) {
|
|
273
461
|
try {
|
|
274
|
-
const skillService =
|
|
462
|
+
const skillService = new SkillService();
|
|
463
|
+
const baseDir = skillService.installDir;
|
|
464
|
+
ensureDir(baseDir);
|
|
465
|
+
|
|
275
466
|
for (const skill of skills) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
467
|
+
const directory = skill.directory;
|
|
468
|
+
const skillDir = resolveSafePath(baseDir, directory);
|
|
469
|
+
if (!skillDir) {
|
|
470
|
+
results.skills.failed++;
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (fs.existsSync(skillDir)) {
|
|
475
|
+
if (!overwrite) {
|
|
476
|
+
results.skills.skipped++;
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
ensureDir(skillDir);
|
|
483
|
+
const files = Array.isArray(skill.files) ? skill.files : [];
|
|
484
|
+
let failed = false;
|
|
485
|
+
|
|
486
|
+
for (const file of files) {
|
|
487
|
+
const filePath = resolveSafePath(skillDir, file.path);
|
|
488
|
+
if (!filePath) {
|
|
489
|
+
failed = true;
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
ensureDir(path.dirname(filePath));
|
|
493
|
+
try {
|
|
494
|
+
if (file.encoding === SKILL_FILE_ENCODING) {
|
|
495
|
+
fs.writeFileSync(filePath, Buffer.from(file.content || '', SKILL_FILE_ENCODING));
|
|
496
|
+
} else {
|
|
497
|
+
fs.writeFileSync(filePath, file.content || '', file.encoding || 'utf8');
|
|
498
|
+
}
|
|
499
|
+
} catch (err) {
|
|
500
|
+
failed = true;
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (failed || files.length === 0) {
|
|
281
506
|
results.skills.failed++;
|
|
507
|
+
} else {
|
|
508
|
+
results.skills.success++;
|
|
282
509
|
}
|
|
283
510
|
}
|
|
284
511
|
} catch (err) {
|
|
@@ -287,15 +514,25 @@ function importConfigs(importData, options = {}) {
|
|
|
287
514
|
}
|
|
288
515
|
|
|
289
516
|
// 导入 Commands
|
|
290
|
-
if (commands && commands.length > 0
|
|
517
|
+
if (commands && commands.length > 0) {
|
|
291
518
|
try {
|
|
292
|
-
const commandsService =
|
|
519
|
+
const commandsService = new CommandsService();
|
|
520
|
+
const baseDir = commandsService.userCommandsDir;
|
|
521
|
+
|
|
293
522
|
for (const command of commands) {
|
|
294
|
-
|
|
295
|
-
|
|
523
|
+
const relativePath = command.path || (
|
|
524
|
+
command.namespace
|
|
525
|
+
? path.join(command.namespace, `${command.name}.md`)
|
|
526
|
+
: `${command.name}.md`
|
|
527
|
+
);
|
|
528
|
+
const content = command.fullContent || command.content || buildCommandContent(command);
|
|
529
|
+
|
|
530
|
+
const status = relativePath ? writeTextFile(baseDir, relativePath, content, overwrite) : 'failed';
|
|
531
|
+
if (status === 'success') {
|
|
296
532
|
results.commands.success++;
|
|
297
|
-
}
|
|
298
|
-
|
|
533
|
+
} else if (status === 'skipped') {
|
|
534
|
+
results.commands.skipped++;
|
|
535
|
+
} else {
|
|
299
536
|
results.commands.failed++;
|
|
300
537
|
}
|
|
301
538
|
}
|
|
@@ -305,15 +542,25 @@ function importConfigs(importData, options = {}) {
|
|
|
305
542
|
}
|
|
306
543
|
|
|
307
544
|
// 导入 Rules
|
|
308
|
-
if (rules && rules.length > 0
|
|
545
|
+
if (rules && rules.length > 0) {
|
|
309
546
|
try {
|
|
310
|
-
const rulesService =
|
|
547
|
+
const rulesService = new RulesService();
|
|
548
|
+
const baseDir = rulesService.userRulesDir;
|
|
549
|
+
|
|
311
550
|
for (const rule of rules) {
|
|
312
|
-
|
|
313
|
-
|
|
551
|
+
const relativePath = rule.path || (
|
|
552
|
+
rule.directory
|
|
553
|
+
? path.join(rule.directory, `${rule.fileName || rule.name}.md`)
|
|
554
|
+
: `${rule.fileName || rule.name}.md`
|
|
555
|
+
);
|
|
556
|
+
const content = rule.fullContent || rule.content || buildRuleContent(rule);
|
|
557
|
+
|
|
558
|
+
const status = relativePath ? writeTextFile(baseDir, relativePath, content, overwrite) : 'failed';
|
|
559
|
+
if (status === 'success') {
|
|
314
560
|
results.rules.success++;
|
|
315
|
-
}
|
|
316
|
-
|
|
561
|
+
} else if (status === 'skipped') {
|
|
562
|
+
results.rules.skipped++;
|
|
563
|
+
} else {
|
|
317
564
|
results.rules.failed++;
|
|
318
565
|
}
|
|
319
566
|
}
|