zen-gitsync 2.7.11 → 2.7.13
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/README.md +148 -148
- package/package.json +1 -1
- package/src/gitCommit.js +7 -3
- package/src/ui/public/assets/index-C3qoeiHs.css +1 -0
- package/src/ui/public/assets/index-Dn7gn8cE.js +81 -0
- package/src/ui/public/assets/vendor-BWT9SBHo.css +1 -0
- package/src/ui/public/assets/vendor-VAJ9QkGT.js +75 -0
- package/src/ui/public/index.html +4 -4
- package/src/ui/server/index.js +175 -9
- package/src/utils/index.js +28 -24
- package/src/ui/public/assets/index-D8DCXO_t.js +0 -79
- package/src/ui/public/assets/index-DWUl9Ly5.css +0 -1
- package/src/ui/public/assets/vendor-DQLOxj05.css +0 -1
- package/src/ui/public/assets/vendor-DdOCbGzm.js +0 -68
package/src/ui/public/index.html
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>Zen GitSync</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/vendor-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-Dn7gn8cE.js"></script>
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-VAJ9QkGT.js">
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/vendor-BWT9SBHo.css">
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-C3qoeiHs.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="app"></div>
|
package/src/ui/server/index.js
CHANGED
|
@@ -10,9 +10,8 @@ import fs from 'fs/promises';
|
|
|
10
10
|
import fsSync from 'fs';
|
|
11
11
|
import os from 'os';
|
|
12
12
|
import { Server } from 'socket.io';
|
|
13
|
-
import { spawn } from 'child_process';
|
|
13
|
+
import { spawn, exec } from 'child_process';
|
|
14
14
|
import iconv from 'iconv-lite';
|
|
15
|
-
// import { exec } from 'child_process';
|
|
16
15
|
|
|
17
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
18
17
|
const __dirname = path.dirname(__filename);
|
|
@@ -373,8 +372,7 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
373
372
|
terminalCommand = `gnome-terminal -- bash -c "cd ${targetDir} && ${command}; exec bash" || xterm -e "cd ${targetDir} && ${command}; bash"`;
|
|
374
373
|
}
|
|
375
374
|
|
|
376
|
-
//
|
|
377
|
-
const { exec } = await import('child_process');
|
|
375
|
+
// 执行命令打开新终端(使用已导入的 exec)
|
|
378
376
|
exec(terminalCommand, (error, stdout, stderr) => {
|
|
379
377
|
if (error) {
|
|
380
378
|
console.error('打开终端失败:', error);
|
|
@@ -417,7 +415,7 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
417
415
|
try {
|
|
418
416
|
// 在 Windows 上需要使用 taskkill 来杀死整个进程树
|
|
419
417
|
if (process.platform === 'win32') {
|
|
420
|
-
|
|
418
|
+
// 使用已导入的 exec
|
|
421
419
|
// /F 强制终止, /T 终止进程树
|
|
422
420
|
exec(`taskkill /pid ${processInfo.childProcess.pid} /T /F`, (error) => {
|
|
423
421
|
if (error) {
|
|
@@ -1170,7 +1168,6 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
1170
1168
|
let terminalFound = false;
|
|
1171
1169
|
for (const terminal of terminals) {
|
|
1172
1170
|
try {
|
|
1173
|
-
const { exec } = await import('child_process');
|
|
1174
1171
|
exec(`which ${terminal.cmd}`, (error) => {
|
|
1175
1172
|
if (!error) {
|
|
1176
1173
|
command = terminal.cmd;
|
|
@@ -1327,7 +1324,7 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
1327
1324
|
try {
|
|
1328
1325
|
// 使用VSCode打开文件
|
|
1329
1326
|
// 尝试使用 'code' 命令打开文件
|
|
1330
|
-
|
|
1327
|
+
// 使用已导入的 spawn
|
|
1331
1328
|
|
|
1332
1329
|
// 创建一个Promise来处理spawn的异步结果
|
|
1333
1330
|
const spawnPromise = new Promise((resolve, reject) => {
|
|
@@ -4420,8 +4417,7 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
4420
4417
|
terminalCommand = `gnome-terminal -- bash -c "cd ${packagePath} && ${npmCommand}; exec bash" || xterm -e "cd ${packagePath} && ${npmCommand}; bash"`;
|
|
4421
4418
|
}
|
|
4422
4419
|
|
|
4423
|
-
//
|
|
4424
|
-
const { exec } = await import('child_process');
|
|
4420
|
+
// 执行命令打开新终端(使用已导入的 exec)
|
|
4425
4421
|
exec(terminalCommand, (error, stdout, stderr) => {
|
|
4426
4422
|
if (error) {
|
|
4427
4423
|
console.error('打开终端失败:', error);
|
|
@@ -4804,6 +4800,176 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
4804
4800
|
socket.emit('command_history_cleared', { success: result });
|
|
4805
4801
|
});
|
|
4806
4802
|
|
|
4803
|
+
// 交互式命令执行
|
|
4804
|
+
socket.on('exec_interactive', async (data) => {
|
|
4805
|
+
const { command, directory, sessionId } = data;
|
|
4806
|
+
|
|
4807
|
+
if (!command || typeof command !== 'string' || !command.trim()) {
|
|
4808
|
+
socket.emit('interactive_error', {
|
|
4809
|
+
sessionId,
|
|
4810
|
+
error: 'command 不能为空'
|
|
4811
|
+
});
|
|
4812
|
+
return;
|
|
4813
|
+
}
|
|
4814
|
+
|
|
4815
|
+
// 确定执行目录
|
|
4816
|
+
const execDirectory = directory && directory.trim()
|
|
4817
|
+
? (path.isAbsolute(directory) ? directory : path.join(currentProjectPath, directory))
|
|
4818
|
+
: currentProjectPath;
|
|
4819
|
+
|
|
4820
|
+
console.log(`[交互式命令] ${sessionId}: ${command} (目录: ${execDirectory})`);
|
|
4821
|
+
|
|
4822
|
+
// 分配进程 ID
|
|
4823
|
+
const processId = ++processIdCounter;
|
|
4824
|
+
|
|
4825
|
+
// 记录执行开始时间
|
|
4826
|
+
const startTime = Date.now();
|
|
4827
|
+
|
|
4828
|
+
// 用于收集输出(用于命令历史)
|
|
4829
|
+
let collectedStdout = '';
|
|
4830
|
+
let collectedStderr = '';
|
|
4831
|
+
|
|
4832
|
+
// 使用 spawn 执行命令
|
|
4833
|
+
const childProcess = spawn(command.trim(), [], {
|
|
4834
|
+
cwd: execDirectory,
|
|
4835
|
+
shell: true,
|
|
4836
|
+
env: {
|
|
4837
|
+
...process.env,
|
|
4838
|
+
GIT_CONFIG_PARAMETERS: "'core.quotepath=false'",
|
|
4839
|
+
FORCE_COLOR: '1',
|
|
4840
|
+
NPM_CONFIG_COLOR: 'always',
|
|
4841
|
+
// 不设置 CI=true,允许交互式输入
|
|
4842
|
+
PYTHONUNBUFFERED: '1'
|
|
4843
|
+
}
|
|
4844
|
+
});
|
|
4845
|
+
|
|
4846
|
+
// 存储进程信息
|
|
4847
|
+
runningProcesses.set(processId, {
|
|
4848
|
+
childProcess,
|
|
4849
|
+
command: command.trim(),
|
|
4850
|
+
startTime,
|
|
4851
|
+
directory: execDirectory,
|
|
4852
|
+
sessionId
|
|
4853
|
+
});
|
|
4854
|
+
|
|
4855
|
+
console.log(`[交互式命令] 创建进程 #${processId}: ${command.substring(0, 50)}`);
|
|
4856
|
+
|
|
4857
|
+
// 发送进程 ID 给客户端
|
|
4858
|
+
socket.emit('interactive_process_id', { sessionId, processId });
|
|
4859
|
+
|
|
4860
|
+
// 判断是否需要 GBK 转换
|
|
4861
|
+
const isWindows = process.platform === 'win32';
|
|
4862
|
+
const cmdBuiltins = ['dir', 'type', 'echo', 'set', 'path', 'cd', 'md', 'rd', 'del', 'copy', 'move', 'ren'];
|
|
4863
|
+
const needsGbkConversion = isWindows && cmdBuiltins.some(builtin =>
|
|
4864
|
+
command.trim().toLowerCase().startsWith(builtin + ' ') ||
|
|
4865
|
+
command.trim().toLowerCase() === builtin
|
|
4866
|
+
);
|
|
4867
|
+
|
|
4868
|
+
// 监听标准输出
|
|
4869
|
+
childProcess.stdout?.on('data', (data) => {
|
|
4870
|
+
let output = needsGbkConversion ? iconv.decode(data, 'gbk') : data.toString('utf8');
|
|
4871
|
+
collectedStdout += output;
|
|
4872
|
+
socket.emit('interactive_stdout', { sessionId, data: output });
|
|
4873
|
+
});
|
|
4874
|
+
|
|
4875
|
+
// 监听标准错误输出
|
|
4876
|
+
childProcess.stderr?.on('data', (data) => {
|
|
4877
|
+
let output = needsGbkConversion ? iconv.decode(data, 'gbk') : data.toString('utf8');
|
|
4878
|
+
collectedStderr += output;
|
|
4879
|
+
socket.emit('interactive_stderr', { sessionId, data: output });
|
|
4880
|
+
});
|
|
4881
|
+
|
|
4882
|
+
// 监听进程关闭
|
|
4883
|
+
childProcess.on('close', (code, signal) => {
|
|
4884
|
+
runningProcesses.delete(processId);
|
|
4885
|
+
console.log(`[交互式命令] 进程 #${processId} 已结束`);
|
|
4886
|
+
|
|
4887
|
+
// 计算执行时间
|
|
4888
|
+
const executionTime = Date.now() - startTime;
|
|
4889
|
+
|
|
4890
|
+
// 添加到命令历史
|
|
4891
|
+
const error = code !== 0 ? `Command exited with code ${code}` : null;
|
|
4892
|
+
addCommandToHistory(
|
|
4893
|
+
command.trim(),
|
|
4894
|
+
collectedStdout,
|
|
4895
|
+
collectedStderr,
|
|
4896
|
+
error,
|
|
4897
|
+
executionTime
|
|
4898
|
+
);
|
|
4899
|
+
|
|
4900
|
+
socket.emit('interactive_exit', {
|
|
4901
|
+
sessionId,
|
|
4902
|
+
code,
|
|
4903
|
+
success: code === 0
|
|
4904
|
+
});
|
|
4905
|
+
});
|
|
4906
|
+
|
|
4907
|
+
// 监听错误
|
|
4908
|
+
childProcess.on('error', (error) => {
|
|
4909
|
+
runningProcesses.delete(processId);
|
|
4910
|
+
console.error(`[交互式命令] 进程 #${processId} 出错:`, error);
|
|
4911
|
+
|
|
4912
|
+
const executionTime = Date.now() - startTime;
|
|
4913
|
+
addCommandToHistory(
|
|
4914
|
+
command.trim(),
|
|
4915
|
+
collectedStdout,
|
|
4916
|
+
collectedStderr,
|
|
4917
|
+
error.message,
|
|
4918
|
+
executionTime
|
|
4919
|
+
);
|
|
4920
|
+
|
|
4921
|
+
socket.emit('interactive_error', {
|
|
4922
|
+
sessionId,
|
|
4923
|
+
error: error.message
|
|
4924
|
+
});
|
|
4925
|
+
});
|
|
4926
|
+
|
|
4927
|
+
// 监听来自客户端的 stdin 输入
|
|
4928
|
+
socket.on(`interactive_stdin_${sessionId}`, (inputData) => {
|
|
4929
|
+
const { input } = inputData;
|
|
4930
|
+
console.log(`[交互式命令] 收到 stdin 输入 (${sessionId}):`, input);
|
|
4931
|
+
|
|
4932
|
+
if (childProcess.stdin && !childProcess.stdin.destroyed) {
|
|
4933
|
+
try {
|
|
4934
|
+
childProcess.stdin.write(input + '\n');
|
|
4935
|
+
} catch (err) {
|
|
4936
|
+
console.error(`[交互式命令] 写入 stdin 失败:`, err);
|
|
4937
|
+
socket.emit('interactive_error', {
|
|
4938
|
+
sessionId,
|
|
4939
|
+
error: `写入输入失败: ${err.message}`
|
|
4940
|
+
});
|
|
4941
|
+
}
|
|
4942
|
+
}
|
|
4943
|
+
});
|
|
4944
|
+
|
|
4945
|
+
// 监听停止命令请求
|
|
4946
|
+
socket.on(`interactive_stop_${sessionId}`, () => {
|
|
4947
|
+
console.log(`[交互式命令] 收到停止请求 (${sessionId})`);
|
|
4948
|
+
|
|
4949
|
+
if (childProcess && !childProcess.killed) {
|
|
4950
|
+
try {
|
|
4951
|
+
if (process.platform === 'win32') {
|
|
4952
|
+
// 使用已导入的 exec(ES Module 不支持 require)
|
|
4953
|
+
exec(`taskkill /pid ${childProcess.pid} /T /F`, (error) => {
|
|
4954
|
+
if (error) {
|
|
4955
|
+
console.error(`[交互式命令] taskkill 失败:`, error);
|
|
4956
|
+
}
|
|
4957
|
+
});
|
|
4958
|
+
} else {
|
|
4959
|
+
childProcess.kill('SIGTERM');
|
|
4960
|
+
setTimeout(() => {
|
|
4961
|
+
if (!childProcess.killed) {
|
|
4962
|
+
childProcess.kill('SIGKILL');
|
|
4963
|
+
}
|
|
4964
|
+
}, 2000);
|
|
4965
|
+
}
|
|
4966
|
+
} catch (err) {
|
|
4967
|
+
console.error(`[交互式命令] 停止进程失败:`, err);
|
|
4968
|
+
}
|
|
4969
|
+
}
|
|
4970
|
+
});
|
|
4971
|
+
});
|
|
4972
|
+
|
|
4807
4973
|
// 客户端断开连接
|
|
4808
4974
|
socket.on('disconnect', () => {
|
|
4809
4975
|
console.log(`客户端已断开连接: ${socket.id} (房间: ${projectRoomId})`);
|
package/src/utils/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import config from '../config.js'
|
|
|
29
29
|
|
|
30
30
|
const printTableWithHeaderUnderline = (head, content, style) => {
|
|
31
31
|
// 获取终端的列数(宽度)
|
|
32
|
-
const terminalWidth = process.stdout.columns;
|
|
32
|
+
const terminalWidth = process.stdout.columns || 100;
|
|
33
33
|
|
|
34
34
|
// 计算表格的宽度,保证至少有 2 个字符留给边框
|
|
35
35
|
const tableWidth = terminalWidth - 2; // 左右边框和分隔符的宽度
|
|
@@ -172,13 +172,12 @@ const tableLog = (commandLine, content, type) => {
|
|
|
172
172
|
content = content.map(item => {
|
|
173
173
|
let fontColor = calcColor(commandLine, item)
|
|
174
174
|
let row = item.replaceAll('\t', ' ')
|
|
175
|
-
|
|
176
175
|
// 截断过长的行
|
|
177
176
|
if (row.length > MAX_LINE_LENGTH) {
|
|
178
177
|
row = row.substring(0, MAX_LINE_LENGTH) + '...';
|
|
179
178
|
}
|
|
180
|
-
|
|
181
|
-
return
|
|
179
|
+
const result = chalk[fontColor](row)
|
|
180
|
+
return result
|
|
182
181
|
})
|
|
183
182
|
|
|
184
183
|
// 如果内容被截断,添加提示
|
|
@@ -534,7 +533,7 @@ async function printGitLog() {
|
|
|
534
533
|
// 使用 ASCII 记录分隔符 %x1E 作为字段分隔符
|
|
535
534
|
const logCommand = `git log -n ${n} --pretty=format:"%C(green)%h%C(reset) %x1E %C(cyan)%an%C(reset) %x1E %C(yellow)%ad%C(reset) %x1E %C(blue)%D%C(reset) %x1E %C(magenta)%s%C(reset)" --date=format:"%Y-%m-%d %H:%M" --graph --decorate --color`
|
|
536
535
|
try {
|
|
537
|
-
const logOutput =
|
|
536
|
+
const logOutput = await execGitCommand(logCommand, {
|
|
538
537
|
head: `git log`
|
|
539
538
|
});
|
|
540
539
|
} catch (error) {
|
|
@@ -558,33 +557,33 @@ function judgeUnmerged(statusOutput) {
|
|
|
558
557
|
}
|
|
559
558
|
}
|
|
560
559
|
|
|
561
|
-
function exec_push({exit, commitMessage}) {
|
|
560
|
+
async function exec_push({exit, commitMessage}) {
|
|
562
561
|
// 执行 git push
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
const
|
|
566
|
-
execGitCommand('git push', {
|
|
562
|
+
const spinner = ora('正在推送代码...').start();
|
|
563
|
+
try {
|
|
564
|
+
const {stdout, stderr} = await execGitCommand('git push', {
|
|
567
565
|
spinner
|
|
568
|
-
})
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
566
|
+
});
|
|
567
|
+
await printCommitLog({commitMessage});
|
|
568
|
+
return {stdout, stderr};
|
|
569
|
+
} catch (error) {
|
|
570
|
+
throw error;
|
|
571
|
+
}
|
|
573
572
|
}
|
|
574
573
|
|
|
575
|
-
function printCommitLog({commitMessage}) {
|
|
574
|
+
async function printCommitLog({commitMessage}) {
|
|
576
575
|
try {
|
|
577
576
|
// 获取项目名称(取git仓库根目录名)
|
|
578
|
-
const
|
|
579
|
-
const projectName = chalk.blueBright(path.basename(
|
|
577
|
+
const projectRootResult = await execGitCommand('git rev-parse --show-toplevel', {log: false});
|
|
578
|
+
const projectName = chalk.blueBright(path.basename(projectRootResult.stdout.trim()));
|
|
580
579
|
|
|
581
580
|
// 获取当前提交hash(取前7位)
|
|
582
|
-
const
|
|
583
|
-
const hashDisplay = chalk.yellow(
|
|
581
|
+
const commitHashResult = await execGitCommand('git rev-parse --short HEAD', {log: false});
|
|
582
|
+
const hashDisplay = chalk.yellow(commitHashResult.stdout.trim());
|
|
584
583
|
|
|
585
584
|
// 获取分支信息
|
|
586
|
-
const
|
|
587
|
-
const branchDisplay = chalk.magenta(
|
|
585
|
+
const branchResult = await execGitCommand('git branch --show-current', {log: false});
|
|
586
|
+
const branchDisplay = chalk.magenta(branchResult.stdout.trim());
|
|
588
587
|
|
|
589
588
|
// 构建信息内容
|
|
590
589
|
const message = [
|
|
@@ -888,6 +887,7 @@ async function execAddAndCommit({statusOutput, commitMessage, exit}) {
|
|
|
888
887
|
|
|
889
888
|
const question = rlPromisify(rl.question.bind(rl))
|
|
890
889
|
commitMessage = await question('请输入提交信息:') || commitMessage;
|
|
890
|
+
rl.close(); // 关闭 readline 接口
|
|
891
891
|
}
|
|
892
892
|
|
|
893
893
|
// 使用带锁定文件过滤的 git add
|
|
@@ -900,13 +900,16 @@ async function execAddAndCommit({statusOutput, commitMessage, exit}) {
|
|
|
900
900
|
if (!checkStatus.stdout.trim()) {
|
|
901
901
|
console.log(chalk.yellow('⚠️ 没有检测到可提交的变更'));
|
|
902
902
|
// exec_exit(exit)
|
|
903
|
-
return;
|
|
903
|
+
return commitMessage; // 返回提交信息(即使没有提交)
|
|
904
904
|
}
|
|
905
905
|
|
|
906
906
|
// 执行 git commit
|
|
907
907
|
if (statusOutput.includes('Untracked files:') || statusOutput.includes('Changes not staged for commit') || statusOutput.includes('Changes to be committed')) {
|
|
908
908
|
await execGitCommand(`git commit -m "${commitMessage}"`)
|
|
909
909
|
}
|
|
910
|
+
|
|
911
|
+
// 返回实际使用的提交信息
|
|
912
|
+
return commitMessage;
|
|
910
913
|
}
|
|
911
914
|
|
|
912
915
|
// 添加时间格式化函数
|
|
@@ -967,7 +970,8 @@ async function addResetScriptToPackageJson() {
|
|
|
967
970
|
}
|
|
968
971
|
|
|
969
972
|
// 获取当前分支名
|
|
970
|
-
const
|
|
973
|
+
const branchResult = await execGitCommand('git branch --show-current', {log: false});
|
|
974
|
+
const branch = branchResult.stdout.trim();
|
|
971
975
|
|
|
972
976
|
// 添加 g:reset 命令
|
|
973
977
|
if (!packageJson.scripts['g:reset']) {
|