@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
|
@@ -18,6 +18,61 @@ function detectAvailableTerminals() {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* 获取系统 Shell 路径
|
|
23
|
+
*/
|
|
24
|
+
function getSystemShell() {
|
|
25
|
+
if (process.platform === 'win32') {
|
|
26
|
+
const candidates = [];
|
|
27
|
+
if (process.env.COMSPEC) {
|
|
28
|
+
candidates.push(process.env.COMSPEC);
|
|
29
|
+
}
|
|
30
|
+
const systemRoot = process.env.SystemRoot || process.env.windir;
|
|
31
|
+
if (systemRoot) {
|
|
32
|
+
candidates.push(path.join(systemRoot, 'System32', 'cmd.exe'));
|
|
33
|
+
}
|
|
34
|
+
return candidates.find((candidate) => candidate && fs.existsSync(candidate)) || null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (process.env.SHELL && fs.existsSync(process.env.SHELL)) {
|
|
38
|
+
return process.env.SHELL;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const commonShells = [
|
|
42
|
+
'/bin/zsh',
|
|
43
|
+
'/bin/bash',
|
|
44
|
+
'/bin/sh',
|
|
45
|
+
'/usr/bin/zsh',
|
|
46
|
+
'/usr/bin/bash',
|
|
47
|
+
'/usr/bin/sh'
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
for (const shell of commonShells) {
|
|
51
|
+
if (fs.existsSync(shell)) {
|
|
52
|
+
return shell;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function createSystemShellEntry(isDefault = false) {
|
|
60
|
+
const shell = getSystemShell();
|
|
61
|
+
if (!shell) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
id: 'system-shell',
|
|
67
|
+
name: '系统 Shell',
|
|
68
|
+
available: true,
|
|
69
|
+
isDefault,
|
|
70
|
+
command: 'Web 终端(系统 Shell)',
|
|
71
|
+
supportsLocalLaunch: false,
|
|
72
|
+
shell
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
21
76
|
/**
|
|
22
77
|
* Windows 终端检测
|
|
23
78
|
* 只保留经过验证、确定能自动执行命令的终端
|
|
@@ -82,6 +137,11 @@ function detectWindowsTerminals() {
|
|
|
82
137
|
}
|
|
83
138
|
}
|
|
84
139
|
|
|
140
|
+
const systemShell = createSystemShellEntry(false);
|
|
141
|
+
if (systemShell) {
|
|
142
|
+
terminals.push(systemShell);
|
|
143
|
+
}
|
|
144
|
+
|
|
85
145
|
return terminals;
|
|
86
146
|
}
|
|
87
147
|
|
|
@@ -245,6 +305,11 @@ function detectMacTerminals() {
|
|
|
245
305
|
// Rio 不可用
|
|
246
306
|
}
|
|
247
307
|
|
|
308
|
+
const systemShell = createSystemShellEntry(false);
|
|
309
|
+
if (systemShell) {
|
|
310
|
+
terminals.push(systemShell);
|
|
311
|
+
}
|
|
312
|
+
|
|
248
313
|
return terminals;
|
|
249
314
|
}
|
|
250
315
|
|
|
@@ -254,6 +319,8 @@ function detectMacTerminals() {
|
|
|
254
319
|
function detectLinuxTerminals() {
|
|
255
320
|
const terminals = [];
|
|
256
321
|
|
|
322
|
+
const systemShell = createSystemShellEntry(false);
|
|
323
|
+
|
|
257
324
|
const terminalConfigs = [
|
|
258
325
|
{ id: 'gnome-terminal', name: 'GNOME Terminal', cmd: 'gnome-terminal', args: '-- bash -c "cd \'{cwd}\' && claude -r {sessionId}; exec bash"' },
|
|
259
326
|
{ id: 'konsole', name: 'Konsole', cmd: 'konsole', args: '-e bash -c "cd \'{cwd}\' && claude -r {sessionId}; exec bash"' },
|
|
@@ -288,6 +355,13 @@ function detectLinuxTerminals() {
|
|
|
288
355
|
}
|
|
289
356
|
});
|
|
290
357
|
|
|
358
|
+
if (systemShell) {
|
|
359
|
+
if (!terminals.find(t => t.isDefault)) {
|
|
360
|
+
systemShell.isDefault = true;
|
|
361
|
+
}
|
|
362
|
+
terminals.push(systemShell);
|
|
363
|
+
}
|
|
364
|
+
|
|
291
365
|
return terminals;
|
|
292
366
|
}
|
|
293
367
|
|
|
@@ -302,5 +376,6 @@ function getDefaultTerminal() {
|
|
|
302
376
|
|
|
303
377
|
module.exports = {
|
|
304
378
|
detectAvailableTerminals,
|
|
305
|
-
getDefaultTerminal
|
|
379
|
+
getDefaultTerminal,
|
|
380
|
+
getSystemShell
|
|
306
381
|
};
|
|
@@ -611,51 +611,72 @@ function removeProjectFromWorkspace(workspaceId, projectName, removeWorktrees =
|
|
|
611
611
|
|
|
612
612
|
/**
|
|
613
613
|
* 获取所有渠道(Claude/Codex/Gemini)的项目并集
|
|
614
|
-
* @returns {Array} 去重后的项目列表
|
|
614
|
+
* @returns {Promise<Array>} 去重后的项目列表
|
|
615
615
|
*/
|
|
616
|
-
function getAllAvailableProjects() {
|
|
617
|
-
const {
|
|
616
|
+
async function getAllAvailableProjects() {
|
|
617
|
+
const { loadConfig } = require('../../config/loader');
|
|
618
618
|
const sessionsService = require('./sessions');
|
|
619
|
+
const codexSessionsService = require('./codex-sessions');
|
|
620
|
+
const geminiSessionsService = require('./gemini-sessions');
|
|
621
|
+
const { isCodexInstalled } = require('./codex-config');
|
|
622
|
+
const { isGeminiInstalled } = require('./gemini-config');
|
|
619
623
|
|
|
620
624
|
const allProjects = [];
|
|
621
|
-
const
|
|
625
|
+
const seenKeys = new Set();
|
|
626
|
+
|
|
627
|
+
function addProject(channel, project) {
|
|
628
|
+
if (!project || !project.name) return;
|
|
629
|
+
|
|
630
|
+
const projectPath = project.fullPath || project.path || null;
|
|
631
|
+
const keyBase = projectPath || project.name;
|
|
632
|
+
const projectKey = `${channel}:${keyBase}`;
|
|
633
|
+
if (seenKeys.has(projectKey)) return;
|
|
634
|
+
seenKeys.add(projectKey);
|
|
635
|
+
|
|
636
|
+
const displayName = project.displayName || (projectPath ? path.basename(projectPath) : project.name);
|
|
637
|
+
const lastUsedValue = project.lastUsed || project.lastUpdated || null;
|
|
638
|
+
const lastUsed = typeof lastUsedValue === 'string'
|
|
639
|
+
? new Date(lastUsedValue).getTime()
|
|
640
|
+
: (lastUsedValue || 0);
|
|
641
|
+
|
|
642
|
+
allProjects.push({
|
|
643
|
+
name: project.name,
|
|
644
|
+
displayName,
|
|
645
|
+
fullPath: projectPath || project.name,
|
|
646
|
+
channel,
|
|
647
|
+
sessionCount: project.sessionCount || 0,
|
|
648
|
+
lastUsed,
|
|
649
|
+
isGitRepo: projectPath ? isGitRepo(projectPath) : false
|
|
650
|
+
});
|
|
651
|
+
}
|
|
622
652
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
653
|
+
try {
|
|
654
|
+
const config = loadConfig();
|
|
655
|
+
const claudeProjects = await sessionsService.getProjectsWithStats(config, { force: true });
|
|
656
|
+
const list = Array.isArray(claudeProjects) ? claudeProjects : [];
|
|
657
|
+
list.forEach(project => addProject('claude', project));
|
|
658
|
+
} catch (error) {
|
|
659
|
+
console.error('获取 claude 项目失败:', error.message);
|
|
660
|
+
}
|
|
629
661
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
662
|
+
try {
|
|
663
|
+
if (isCodexInstalled()) {
|
|
664
|
+
const codexProjects = codexSessionsService.getProjects();
|
|
665
|
+
const list = Array.isArray(codexProjects) ? codexProjects : [];
|
|
666
|
+
list.forEach(project => addProject('codex', project));
|
|
667
|
+
}
|
|
668
|
+
} catch (error) {
|
|
669
|
+
console.error('获取 codex 项目失败:', error.message);
|
|
670
|
+
}
|
|
635
671
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
const projectPath = proj.fullPath;
|
|
642
|
-
const projectKey = `${projectPath}:${channel.name}`;
|
|
643
|
-
if (projectPath && !seenPaths.has(projectKey)) {
|
|
644
|
-
seenPaths.add(projectKey);
|
|
645
|
-
allProjects.push({
|
|
646
|
-
name: proj.name,
|
|
647
|
-
displayName: proj.displayName,
|
|
648
|
-
fullPath: projectPath,
|
|
649
|
-
channel: channel.name,
|
|
650
|
-
sessionCount: proj.sessionCount || 0,
|
|
651
|
-
lastUsed: proj.lastUsed,
|
|
652
|
-
isGitRepo: isGitRepo(projectPath)
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
} catch (error) {
|
|
657
|
-
console.error(`获取 ${channel.name} 项目失败:`, error.message);
|
|
672
|
+
try {
|
|
673
|
+
if (isGeminiInstalled()) {
|
|
674
|
+
const geminiProjects = geminiSessionsService.getProjects();
|
|
675
|
+
const list = Array.isArray(geminiProjects) ? geminiProjects : [];
|
|
676
|
+
list.forEach(project => addProject('gemini', project));
|
|
658
677
|
}
|
|
678
|
+
} catch (error) {
|
|
679
|
+
console.error('获取 gemini 项目失败:', error.message);
|
|
659
680
|
}
|
|
660
681
|
|
|
661
682
|
// 按最后使用时间排序
|
|
@@ -392,6 +392,7 @@ function broadcastSchedulerState(source, schedulerState) {
|
|
|
392
392
|
// ============ 终端 WebSocket 消息处理 ============
|
|
393
393
|
|
|
394
394
|
const { getCommandForChannel } = require('./services/terminal-commands');
|
|
395
|
+
const { getWebTerminalShellConfig } = require('./services/terminal-config');
|
|
395
396
|
|
|
396
397
|
/**
|
|
397
398
|
* 处理终端相关的 WebSocket 消息
|
|
@@ -444,13 +445,16 @@ function handleTerminalCreate(ws, message) {
|
|
|
444
445
|
// 获取启动命令
|
|
445
446
|
const startCommand = getCommandForChannel(channel, sessionId, workDir);
|
|
446
447
|
|
|
448
|
+
const shellConfig = getWebTerminalShellConfig();
|
|
449
|
+
|
|
447
450
|
// 创建终端
|
|
448
451
|
const terminal = ptyManager.create({
|
|
449
452
|
cwd: workDir,
|
|
450
453
|
channel,
|
|
451
454
|
sessionId,
|
|
452
455
|
projectName,
|
|
453
|
-
startCommand
|
|
456
|
+
startCommand,
|
|
457
|
+
...shellConfig
|
|
454
458
|
});
|
|
455
459
|
|
|
456
460
|
// 绑定 WebSocket
|
|
@@ -476,7 +480,7 @@ function handleTerminalCreate(ws, message) {
|
|
|
476
480
|
* 绑定到已有终端
|
|
477
481
|
*/
|
|
478
482
|
function handleTerminalAttach(ws, message) {
|
|
479
|
-
const { terminalId } = message;
|
|
483
|
+
const { terminalId, includeHistory, cols, rows } = message;
|
|
480
484
|
|
|
481
485
|
if (!terminalId) {
|
|
482
486
|
ws.send(JSON.stringify({
|
|
@@ -498,7 +502,14 @@ function handleTerminalAttach(ws, message) {
|
|
|
498
502
|
|
|
499
503
|
// 绑定 WebSocket
|
|
500
504
|
ws.terminalId = terminalId;
|
|
501
|
-
|
|
505
|
+
const shouldIncludeHistory = typeof includeHistory === 'boolean' ? includeHistory : true;
|
|
506
|
+
if (Number.isFinite(cols) && Number.isFinite(rows) && cols > 0 && rows > 0) {
|
|
507
|
+
ptyManager.resize(terminalId, cols, rows);
|
|
508
|
+
}
|
|
509
|
+
ptyManager.attachWebSocket(terminalId, ws, {
|
|
510
|
+
includeHistory: shouldIncludeHistory,
|
|
511
|
+
trimLastLine: true
|
|
512
|
+
});
|
|
502
513
|
|
|
503
514
|
ws.send(JSON.stringify({
|
|
504
515
|
type: 'terminal:attached',
|