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.
@@ -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-D8DCXO_t.js"></script>
10
- <link rel="modulepreload" crossorigin href="/assets/vendor-DdOCbGzm.js">
11
- <link rel="stylesheet" crossorigin href="/assets/vendor-DQLOxj05.css">
12
- <link rel="stylesheet" crossorigin href="/assets/index-DWUl9Ly5.css">
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>
@@ -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
- const { exec } = await import('child_process');
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
- const { spawn } = await import('child_process');
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})`);
@@ -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 chalk[fontColor](row)
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 = execSyncGitCommand(logCommand, {
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
- // execSyncGitCommand(`git push`);
564
- return new Promise((resolve, reject) => {
565
- const spinner = ora('正在推送代码...').start();
566
- execGitCommand('git push', {
562
+ const spinner = ora('正在推送代码...').start();
563
+ try {
564
+ const {stdout, stderr} = await execGitCommand('git push', {
567
565
  spinner
568
- }).then(({stdout, stderr}) => {
569
- printCommitLog({commitMessage})
570
- resolve({stdout, stderr})
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 projectRoot = execSyncGitCommand('git rev-parse --show-toplevel', {log: false});
579
- const projectName = chalk.blueBright(path.basename(projectRoot.trim()));
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 commitHash = execSyncGitCommand('git rev-parse --short HEAD', {log: false}).trim();
583
- const hashDisplay = chalk.yellow(commitHash);
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 branch = execSyncGitCommand('git branch --show-current', {log: false}).trim();
587
- const branchDisplay = chalk.magenta(branch);
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 branch = execSyncGitCommand('git branch --show-current', {log: false}).trim();
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']) {