zen-gitsync 2.4.10 → 2.4.12

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.
@@ -5,10 +5,10 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Zen-GitSync - Git同步工具</title>
8
- <script type="module" crossorigin src="/assets/index-COr7M_hD.js"></script>
9
- <link rel="modulepreload" crossorigin href="/assets/vendor-D-Vg0AbY.js">
8
+ <script type="module" crossorigin src="/assets/index-BFihJBzC.js"></script>
9
+ <link rel="modulepreload" crossorigin href="/assets/vendor-DJt7ABTC.js">
10
10
  <link rel="stylesheet" crossorigin href="/assets/vendor-D9qDBEE1.css">
11
- <link rel="stylesheet" crossorigin href="/assets/index-BmMYpCWy.css">
11
+ <link rel="stylesheet" crossorigin href="/assets/index-CK7Vijoe.css">
12
12
  </head>
13
13
  <body>
14
14
  <div id="app"></div>
@@ -2,7 +2,7 @@ import express from 'express';
2
2
  import { createServer } from 'http';
3
3
  import { fileURLToPath } from 'url';
4
4
  import path from 'path';
5
- import { execGitCommand, getCommandHistory, clearCommandHistory, registerSocketIO, execGitAddWithLockFilter } from '../../utils/index.js';
5
+ import { execGitCommand, getCommandHistory, addCommandToHistory, clearCommandHistory, registerSocketIO, execGitAddWithLockFilter } from '../../utils/index.js';
6
6
  import open from 'open';
7
7
  import config from '../../config.js';
8
8
  import chalk from 'chalk';
@@ -10,6 +10,7 @@ import fs from 'fs/promises';
10
10
  import os from 'os';
11
11
  import { Server } from 'socket.io';
12
12
  import chokidar from 'chokidar';
13
+ import { spawn } from 'child_process';
13
14
  // import { exec } from 'child_process';
14
15
 
15
16
  const __filename = fileURLToPath(import.meta.url);
@@ -54,10 +55,12 @@ let recentPushStatus = {
54
55
  validDuration: 10000 // 推送后10秒内认为分支状态是同步的
55
56
  };
56
57
 
58
+ const showConsole = true;
57
59
  async function startUIServer(noOpen = false, savePort = false) {
58
60
  const app = express();
59
61
  const httpServer = createServer(app);
60
62
  const io = new Server(httpServer);
63
+ if (showConsole) console.log(`创建服务成功`)
61
64
 
62
65
  // 获取当前项目的唯一标识(使用工作目录路径)
63
66
  // 需要在切换目录时更新,故使用 let
@@ -77,14 +80,16 @@ async function startUIServer(noOpen = false, savePort = false) {
77
80
  try {
78
81
  let dirPath = process.cwd();
79
82
  try {
83
+ if(showConsole) console.log(`记录最近打开目录`)
80
84
  const { stdout } = await execGitCommand('git rev-parse --show-toplevel');
81
85
  const root = stdout?.trim();
82
86
  if (root) dirPath = root;
83
87
  } catch (_) {
84
88
  // 非Git仓库或命令失败,使用 CWD 即可
85
89
  }
90
+ if (showConsole) console.log(`记录最近打开目录: ${dirPath}`)
86
91
  await configManager.saveRecentDirectory(dirPath);
87
- console.log(chalk.gray(`已记录最近打开目录: ${dirPath}`));
92
+ if (showConsole) console.log(chalk.gray(`已记录最近打开目录: ${dirPath}`));
88
93
  } catch (e) {
89
94
  console.warn(chalk.yellow(`记录最近目录失败: ${e?.message || e}`));
90
95
  }
@@ -96,6 +101,9 @@ async function startUIServer(noOpen = false, savePort = false) {
96
101
  next();
97
102
  });
98
103
 
104
+ // 静态文件服务
105
+ app.use(express.static(path.join(__dirname, '../public')));
106
+
99
107
  // 通用命令执行接口(非流式)
100
108
  app.post('/api/exec', async (req, res) => {
101
109
  try {
@@ -115,9 +123,6 @@ async function startUIServer(noOpen = false, savePort = false) {
115
123
  }
116
124
  });
117
125
 
118
- // 静态文件服务
119
- app.use(express.static(path.join(__dirname, '../public')));
120
-
121
126
  // API路由
122
127
  // 移除了 /api/status 端点,因为前端只使用 porcelain 格式
123
128
 
@@ -801,6 +806,98 @@ async function startUIServer(noOpen = false, savePort = false) {
801
806
  }
802
807
  });
803
808
 
809
+ // 在终端中打开当前目录
810
+ app.post('/api/open_terminal', async (req, res) => {
811
+ try {
812
+ // 获取要打开的目录路径,如果没有提供,则使用当前目录
813
+ const directoryPath = req.body.path || process.cwd();
814
+
815
+ try {
816
+ // 检查目录是否存在
817
+ await fs.access(directoryPath);
818
+
819
+ // 根据不同操作系统打开终端
820
+ const platform = os.platform();
821
+ let command;
822
+ let args;
823
+
824
+ switch (platform) {
825
+ case 'win32':
826
+ // Windows: 将start命令的参数分开传递,避免引号转义问题
827
+ // 参数顺序:start [title] /D [path] [command]
828
+ command = 'cmd';
829
+ args = ['/c', 'start', '', '/D', directoryPath, 'cmd'];
830
+ break;
831
+ case 'darwin':
832
+ // macOS: 使用 Terminal.app
833
+ command = 'open';
834
+ args = ['-a', 'Terminal', directoryPath];
835
+ break;
836
+ case 'linux':
837
+ // Linux: 尝试使用常见的终端模拟器
838
+ // 优先级: gnome-terminal, konsole, xterm
839
+ const terminals = [
840
+ { cmd: 'gnome-terminal', args: ['--working-directory', directoryPath] },
841
+ { cmd: 'konsole', args: ['--workdir', directoryPath] },
842
+ { cmd: 'xterm', args: ['-e', `cd "${directoryPath}" && $SHELL`] }
843
+ ];
844
+
845
+ // 尝试找到可用的终端
846
+ let terminalFound = false;
847
+ for (const terminal of terminals) {
848
+ try {
849
+ const { exec } = await import('child_process');
850
+ exec(`which ${terminal.cmd}`, (error) => {
851
+ if (!error) {
852
+ command = terminal.cmd;
853
+ args = terminal.args;
854
+ terminalFound = true;
855
+ }
856
+ });
857
+ if (terminalFound) break;
858
+ } catch (e) {
859
+ continue;
860
+ }
861
+ }
862
+
863
+ if (!terminalFound) {
864
+ return res.status(400).json({
865
+ success: false,
866
+ error: '未找到可用的终端模拟器'
867
+ });
868
+ }
869
+ break;
870
+ default:
871
+ return res.status(400).json({
872
+ success: false,
873
+ error: `不支持的操作系统: ${platform}`
874
+ });
875
+ }
876
+
877
+ // 执行命令打开终端
878
+ spawn(command, args, {
879
+ detached: true,
880
+ stdio: 'ignore'
881
+ }).unref();
882
+
883
+ res.json({
884
+ success: true,
885
+ message: '已在终端中打开目录'
886
+ });
887
+ } catch (error) {
888
+ res.status(400).json({
889
+ success: false,
890
+ error: `无法打开终端 "${directoryPath}": ${error.message}`
891
+ });
892
+ }
893
+ } catch (error) {
894
+ res.status(500).json({
895
+ success: false,
896
+ error: error.message
897
+ });
898
+ }
899
+ });
900
+
804
901
  // 打开文件
805
902
  app.post('/api/open-file', async (req, res) => {
806
903
  try {
@@ -995,9 +1092,12 @@ async function startUIServer(noOpen = false, savePort = false) {
995
1092
  // 获取配置
996
1093
  app.get('/api/config/getConfig', async (req, res) => {
997
1094
  try {
1095
+ console.log('获取配置中。。。')
998
1096
  const config = await configManager.loadConfig()
1097
+ console.log('获取配置成功')
999
1098
  res.json(config)
1000
1099
  } catch (error) {
1100
+ console.log('获取配置失败')
1001
1101
  res.status(500).json({ error: error.message })
1002
1102
  }
1003
1103
  })
@@ -1392,6 +1492,192 @@ async function startUIServer(noOpen = false, savePort = false) {
1392
1492
  res.status(500).json({ success: false, error: error.message });
1393
1493
  }
1394
1494
  });
1495
+
1496
+ // 带进度的推送更改 (SSE)
1497
+ app.post('/api/push-with-progress', async (req, res) => {
1498
+ // 设置SSE响应头
1499
+ res.setHeader('Content-Type', 'text/event-stream');
1500
+ res.setHeader('Cache-Control', 'no-cache');
1501
+ res.setHeader('Connection', 'keep-alive');
1502
+ res.flushHeaders();
1503
+
1504
+ const sendProgress = (data) => {
1505
+ res.write(`data: ${JSON.stringify(data)}\n\n`);
1506
+ };
1507
+
1508
+ try {
1509
+ // 获取当前工作目录 - 与execGitCommand保持一致
1510
+ const cwdArg = process.argv.find(arg => arg.startsWith('--path')) || process.argv.find(arg => arg.startsWith('--cwd'));
1511
+ let workDir = process.cwd();
1512
+ if (cwdArg) {
1513
+ const [, value] = cwdArg.split('=');
1514
+ workDir = value || process.cwd();
1515
+ }
1516
+
1517
+ console.log('开始推送,工作目录:', workDir);
1518
+
1519
+ // 记录开始时间
1520
+ const startTime = Date.now();
1521
+
1522
+ // 发送开始消息
1523
+ sendProgress({
1524
+ type: 'progress',
1525
+ message: '开始推送到远程仓库...'
1526
+ });
1527
+
1528
+ // 使用spawn执行git push --progress
1529
+ const gitPush = spawn('git', ['push', '--progress'], {
1530
+ cwd: workDir,
1531
+ env: {
1532
+ ...process.env,
1533
+ GIT_CONFIG_PARAMETERS: "'core.quotepath=false'" // 关闭路径转义
1534
+ }
1535
+ });
1536
+
1537
+ let errorOutput = '';
1538
+ let standardOutput = '';
1539
+
1540
+ // Git的进度信息在stderr中
1541
+ gitPush.stderr.on('data', (data) => {
1542
+ const output = data.toString();
1543
+ errorOutput += output;
1544
+
1545
+ // 解析进度信息
1546
+ const lines = output.split('\n');
1547
+ for (const line of lines) {
1548
+ if (line.trim()) {
1549
+ // 发送原始行
1550
+ sendProgress({
1551
+ type: 'progress',
1552
+ message: line.trim()
1553
+ });
1554
+
1555
+ // 识别不同阶段并解析百分比
1556
+ const percentMatch = line.match(/(\d+)%/);
1557
+ if (percentMatch) {
1558
+ const percent = parseInt(percentMatch[1]);
1559
+ let stage = 'unknown';
1560
+
1561
+ if (line.includes('Enumerating objects')) {
1562
+ stage = 'enumerating';
1563
+ } else if (line.includes('Counting objects')) {
1564
+ stage = 'counting';
1565
+ } else if (line.includes('Compressing objects')) {
1566
+ stage = 'compressing';
1567
+ } else if (line.includes('Writing objects')) {
1568
+ stage = 'writing';
1569
+ } else if (line.includes('Resolving deltas')) {
1570
+ stage = 'resolving';
1571
+ }
1572
+
1573
+ sendProgress({
1574
+ type: 'stage-progress',
1575
+ stage: stage,
1576
+ percent: percent,
1577
+ message: line.trim()
1578
+ });
1579
+ }
1580
+ }
1581
+ }
1582
+ });
1583
+
1584
+ gitPush.stdout.on('data', (data) => {
1585
+ standardOutput += data.toString();
1586
+ });
1587
+
1588
+ gitPush.on('close', (code) => {
1589
+ console.log(`Git push 进程结束,退出码: ${code}`);
1590
+ console.log('标准输出:', standardOutput);
1591
+ console.log('错误输出:', errorOutput);
1592
+
1593
+ // 计算执行时间
1594
+ const executionTime = Date.now() - startTime;
1595
+
1596
+ if (code === 0) {
1597
+ // 推送成功
1598
+ recentPushStatus = {
1599
+ justPushed: true,
1600
+ pushTime: Date.now(),
1601
+ validDuration: 10000
1602
+ };
1603
+
1604
+ // 添加到命令历史
1605
+ addCommandToHistory(
1606
+ 'git push --progress',
1607
+ standardOutput,
1608
+ errorOutput,
1609
+ null,
1610
+ executionTime
1611
+ );
1612
+
1613
+ sendProgress({
1614
+ type: 'complete',
1615
+ success: true,
1616
+ message: standardOutput || errorOutput || 'Push successful'
1617
+ });
1618
+ } else {
1619
+ // 推送失败
1620
+ console.error('推送失败:', errorOutput || standardOutput);
1621
+
1622
+ // 添加到命令历史(失败情况)
1623
+ addCommandToHistory(
1624
+ 'git push --progress',
1625
+ standardOutput,
1626
+ errorOutput,
1627
+ errorOutput || standardOutput || `Push failed with code ${code}`,
1628
+ executionTime
1629
+ );
1630
+
1631
+ sendProgress({
1632
+ type: 'complete',
1633
+ success: false,
1634
+ error: errorOutput || standardOutput || `Push failed with code ${code}`
1635
+ });
1636
+ }
1637
+ res.end();
1638
+ });
1639
+
1640
+ gitPush.on('error', (error) => {
1641
+ console.error('Git push 进程错误:', error);
1642
+
1643
+ // 计算执行时间
1644
+ const executionTime = Date.now() - startTime;
1645
+
1646
+ // 添加到命令历史(错误情况)
1647
+ addCommandToHistory(
1648
+ 'git push --progress',
1649
+ '',
1650
+ '',
1651
+ error.message,
1652
+ executionTime
1653
+ );
1654
+
1655
+ sendProgress({
1656
+ type: 'complete',
1657
+ success: false,
1658
+ error: error.message
1659
+ });
1660
+ res.end();
1661
+ });
1662
+
1663
+ } catch (error) {
1664
+ // 添加到命令历史(异常情况)
1665
+ addCommandToHistory(
1666
+ 'git push --progress',
1667
+ '',
1668
+ '',
1669
+ error.message,
1670
+ 0
1671
+ );
1672
+
1673
+ sendProgress({
1674
+ type: 'complete',
1675
+ success: false,
1676
+ error: error.message
1677
+ });
1678
+ res.end();
1679
+ }
1680
+ });
1395
1681
 
1396
1682
  // 添加git pull API端点
1397
1683
  app.post('/api/pull', async (req, res) => {
@@ -2784,7 +3070,7 @@ async function startUIServer(noOpen = false, savePort = false) {
2784
3070
  try {
2785
3071
  // 等待1秒,避免快速尝试多个端口
2786
3072
  if (currentPort > startPort) {
2787
- await new Promise(resolve => setTimeout(resolve, 1000));
3073
+ await new Promise(resolve => setTimeout(resolve, 800));
2788
3074
  console.log(`尝试端口 ${currentPort}...`);
2789
3075
  }
2790
3076
 
@@ -2827,7 +3113,9 @@ async function startUIServer(noOpen = false, savePort = false) {
2827
3113
 
2828
3114
  // 只有在noOpen为false时才打开浏览器
2829
3115
  if (!noOpen) {
2830
- open(`http://localhost:${currentPort}`);
3116
+ setTimeout(() => {
3117
+ open(`http://localhost:${currentPort}`);
3118
+ }, 0);
2831
3119
  }
2832
3120
 
2833
3121
  resolve();
@@ -331,6 +331,45 @@ function getCommandHistory() {
331
331
  return [...commandHistory];
332
332
  }
333
333
 
334
+ // Function to manually add command to history (for commands not using execGitCommand)
335
+ function addCommandToHistory(command, stdout = '', stderr = '', error = null, executionTime = 0) {
336
+ const MAX_OUTPUT_LENGTH = 5000;
337
+
338
+ // Truncate outputs if too long
339
+ const isStdoutTruncated = stdout.length > MAX_OUTPUT_LENGTH;
340
+ const isStderrTruncated = stderr.length > MAX_OUTPUT_LENGTH;
341
+ const truncatedStdout = isStdoutTruncated ? stdout.substring(0, MAX_OUTPUT_LENGTH) + '...[truncated]' : stdout;
342
+ const truncatedStderr = isStderrTruncated ? stderr.substring(0, MAX_OUTPUT_LENGTH) + '...[truncated]' : stderr;
343
+
344
+ const historyItem = {
345
+ command,
346
+ stdout: truncatedStdout || '',
347
+ stderr: truncatedStderr || '',
348
+ error: error ? (typeof error === 'string' ? error : error.message) : null,
349
+ executionTime,
350
+ timestamp: new Date().toISOString(),
351
+ success: !error,
352
+ isStdoutTruncated,
353
+ isStderrTruncated
354
+ };
355
+
356
+ // Add to history (limited size)
357
+ commandHistory.unshift(historyItem);
358
+ if (commandHistory.length > MAX_HISTORY_SIZE) {
359
+ commandHistory.pop();
360
+ }
361
+
362
+ // Broadcast via WebSocket if available
363
+ if (ioInstance) {
364
+ ioInstance.emit('command_history_update', {
365
+ newCommand: historyItem,
366
+ fullHistory: commandHistory.slice(0, 10)
367
+ });
368
+ }
369
+
370
+ return historyItem;
371
+ }
372
+
334
373
  const getCwd = () => {
335
374
  const cwdArg = process.argv.find(arg => arg.startsWith('--path')) || process.argv.find(arg => arg.startsWith('--cwd'));
336
375
  if (cwdArg) {
@@ -914,7 +953,7 @@ async function addResetScriptToPackageJson() {
914
953
 
915
954
  export {
916
955
  coloredLog, errorLog, execSyncGitCommand,
917
- execGitCommand, getCommandHistory, // Add this export
956
+ execGitCommand, getCommandHistory, addCommandToHistory, // Add command history exports
918
957
  clearCommandHistory,
919
958
  registerSocketIO, // 导出注册Socket.io的函数
920
959
  getCwd, judgePlatform, showHelp, judgeLog, printGitLog,