zen-gitsync 2.4.11 → 2.4.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/package.json +1 -1
- package/src/config.js +2 -1
- package/src/ui/public/assets/index-CVjWGN3c.js +46 -0
- package/src/ui/public/assets/index-CwJ6ny4v.css +1 -0
- package/src/ui/public/assets/{vendor-C6SHWN-W.js → vendor-DJt7ABTC.js} +12 -12
- package/src/ui/public/index.html +3 -3
- package/src/ui/server/index.js +237 -6
- package/src/ui/public/assets/index-BjdGW78N.css +0 -1
- package/src/ui/public/assets/index-Cn7LQamN.js +0 -46
package/src/ui/public/index.html
CHANGED
|
@@ -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-
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-CVjWGN3c.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-
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CwJ6ny4v.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
14
14
|
<div id="app"></div>
|
package/src/ui/server/index.js
CHANGED
|
@@ -55,10 +55,12 @@ let recentPushStatus = {
|
|
|
55
55
|
validDuration: 10000 // 推送后10秒内认为分支状态是同步的
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
const showConsole = true;
|
|
58
59
|
async function startUIServer(noOpen = false, savePort = false) {
|
|
59
60
|
const app = express();
|
|
60
61
|
const httpServer = createServer(app);
|
|
61
62
|
const io = new Server(httpServer);
|
|
63
|
+
if (showConsole) console.log(`创建服务成功`)
|
|
62
64
|
|
|
63
65
|
// 获取当前项目的唯一标识(使用工作目录路径)
|
|
64
66
|
// 需要在切换目录时更新,故使用 let
|
|
@@ -78,14 +80,16 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
78
80
|
try {
|
|
79
81
|
let dirPath = process.cwd();
|
|
80
82
|
try {
|
|
83
|
+
if(showConsole) console.log(`记录最近打开目录`)
|
|
81
84
|
const { stdout } = await execGitCommand('git rev-parse --show-toplevel');
|
|
82
85
|
const root = stdout?.trim();
|
|
83
86
|
if (root) dirPath = root;
|
|
84
87
|
} catch (_) {
|
|
85
88
|
// 非Git仓库或命令失败,使用 CWD 即可
|
|
86
89
|
}
|
|
90
|
+
if (showConsole) console.log(`记录最近打开目录: ${dirPath}`)
|
|
87
91
|
await configManager.saveRecentDirectory(dirPath);
|
|
88
|
-
console.log(chalk.gray(`已记录最近打开目录: ${dirPath}`));
|
|
92
|
+
if (showConsole) console.log(chalk.gray(`已记录最近打开目录: ${dirPath}`));
|
|
89
93
|
} catch (e) {
|
|
90
94
|
console.warn(chalk.yellow(`记录最近目录失败: ${e?.message || e}`));
|
|
91
95
|
}
|
|
@@ -97,6 +101,9 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
97
101
|
next();
|
|
98
102
|
});
|
|
99
103
|
|
|
104
|
+
// 静态文件服务
|
|
105
|
+
app.use(express.static(path.join(__dirname, '../public')));
|
|
106
|
+
|
|
100
107
|
// 通用命令执行接口(非流式)
|
|
101
108
|
app.post('/api/exec', async (req, res) => {
|
|
102
109
|
try {
|
|
@@ -116,9 +123,6 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
116
123
|
}
|
|
117
124
|
});
|
|
118
125
|
|
|
119
|
-
// 静态文件服务
|
|
120
|
-
app.use(express.static(path.join(__dirname, '../public')));
|
|
121
|
-
|
|
122
126
|
// API路由
|
|
123
127
|
// 移除了 /api/status 端点,因为前端只使用 porcelain 格式
|
|
124
128
|
|
|
@@ -802,6 +806,98 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
802
806
|
}
|
|
803
807
|
});
|
|
804
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
|
+
|
|
805
901
|
// 打开文件
|
|
806
902
|
app.post('/api/open-file', async (req, res) => {
|
|
807
903
|
try {
|
|
@@ -996,9 +1092,12 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
996
1092
|
// 获取配置
|
|
997
1093
|
app.get('/api/config/getConfig', async (req, res) => {
|
|
998
1094
|
try {
|
|
1095
|
+
console.log('获取配置中。。。')
|
|
999
1096
|
const config = await configManager.loadConfig()
|
|
1097
|
+
console.log('获取配置成功')
|
|
1000
1098
|
res.json(config)
|
|
1001
1099
|
} catch (error) {
|
|
1100
|
+
console.log('获取配置失败')
|
|
1002
1101
|
res.status(500).json({ error: error.message })
|
|
1003
1102
|
}
|
|
1004
1103
|
})
|
|
@@ -2744,6 +2843,136 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
2744
2843
|
}
|
|
2745
2844
|
});
|
|
2746
2845
|
|
|
2846
|
+
// ========== NPM 脚本管理相关 API ==========
|
|
2847
|
+
|
|
2848
|
+
// 扫描项目目录及子目录下的所有package.json,并提取scripts
|
|
2849
|
+
app.get('/api/scan-npm-scripts', async (req, res) => {
|
|
2850
|
+
try {
|
|
2851
|
+
const projectRoot = process.cwd();
|
|
2852
|
+
const packageJsons = [];
|
|
2853
|
+
|
|
2854
|
+
// 递归扫描目录查找package.json
|
|
2855
|
+
async function scanDirectory(dir, depth = 0) {
|
|
2856
|
+
// 限制扫描深度,避免扫描过深
|
|
2857
|
+
if (depth > 5) return;
|
|
2858
|
+
|
|
2859
|
+
try {
|
|
2860
|
+
const items = await fs.readdir(dir, { withFileTypes: true });
|
|
2861
|
+
|
|
2862
|
+
for (const item of items) {
|
|
2863
|
+
const fullPath = path.join(dir, item.name);
|
|
2864
|
+
|
|
2865
|
+
// 跳过常见的不需要扫描的目录
|
|
2866
|
+
if (item.isDirectory()) {
|
|
2867
|
+
const dirName = item.name;
|
|
2868
|
+
if (dirName === 'node_modules' ||
|
|
2869
|
+
dirName === '.git' ||
|
|
2870
|
+
dirName === 'dist' ||
|
|
2871
|
+
dirName === 'build' ||
|
|
2872
|
+
dirName.startsWith('.')) {
|
|
2873
|
+
continue;
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2876
|
+
// 递归扫描子目录
|
|
2877
|
+
await scanDirectory(fullPath, depth + 1);
|
|
2878
|
+
} else if (item.name === 'package.json') {
|
|
2879
|
+
// 读取package.json文件
|
|
2880
|
+
try {
|
|
2881
|
+
const content = await fs.readFile(fullPath, 'utf8');
|
|
2882
|
+
const packageData = JSON.parse(content);
|
|
2883
|
+
|
|
2884
|
+
// 只有当scripts存在且至少有一个脚本时才添加
|
|
2885
|
+
if (packageData.scripts && Object.keys(packageData.scripts).length > 0) {
|
|
2886
|
+
const relativePath = path.relative(projectRoot, dir);
|
|
2887
|
+
packageJsons.push({
|
|
2888
|
+
path: dir,
|
|
2889
|
+
relativePath: relativePath || '.',
|
|
2890
|
+
name: packageData.name || path.basename(dir),
|
|
2891
|
+
scripts: packageData.scripts
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
} catch (error) {
|
|
2895
|
+
console.error(`读取package.json失败: ${fullPath}`, error);
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
} catch (error) {
|
|
2900
|
+
console.error(`扫描目录失败: ${dir}`, error);
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
|
|
2904
|
+
// 从项目根目录开始扫描
|
|
2905
|
+
await scanDirectory(projectRoot);
|
|
2906
|
+
|
|
2907
|
+
res.json({
|
|
2908
|
+
success: true,
|
|
2909
|
+
packages: packageJsons,
|
|
2910
|
+
totalScripts: packageJsons.reduce((sum, pkg) => sum + Object.keys(pkg.scripts).length, 0)
|
|
2911
|
+
});
|
|
2912
|
+
} catch (error) {
|
|
2913
|
+
console.error('扫描npm脚本失败:', error);
|
|
2914
|
+
res.status(500).json({
|
|
2915
|
+
success: false,
|
|
2916
|
+
error: `扫描npm脚本失败: ${error.message}`
|
|
2917
|
+
});
|
|
2918
|
+
}
|
|
2919
|
+
});
|
|
2920
|
+
|
|
2921
|
+
// 在新终端中执行npm脚本
|
|
2922
|
+
app.post('/api/run-npm-script', async (req, res) => {
|
|
2923
|
+
try {
|
|
2924
|
+
const { packagePath, scriptName } = req.body;
|
|
2925
|
+
|
|
2926
|
+
if (!packagePath || !scriptName) {
|
|
2927
|
+
return res.status(400).json({
|
|
2928
|
+
success: false,
|
|
2929
|
+
error: '缺少必要参数:packagePath 和 scriptName'
|
|
2930
|
+
});
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
console.log(`执行npm脚本: ${scriptName} in ${packagePath}`);
|
|
2934
|
+
|
|
2935
|
+
// 根据操作系统选择合适的终端命令
|
|
2936
|
+
let terminalCommand;
|
|
2937
|
+
const npmCommand = `npm run ${scriptName}`;
|
|
2938
|
+
|
|
2939
|
+
if (process.platform === 'win32') {
|
|
2940
|
+
// Windows: 使用 start 命令打开新的 cmd 窗口
|
|
2941
|
+
// /K 参数表示执行命令后保持窗口打开
|
|
2942
|
+
terminalCommand = `start cmd /K "cd /d ${packagePath} && ${npmCommand}"`;
|
|
2943
|
+
} else if (process.platform === 'darwin') {
|
|
2944
|
+
// macOS: 使用 osascript 打开 Terminal.app
|
|
2945
|
+
const script = `tell application "Terminal" to do script "cd ${packagePath} && ${npmCommand}"`;
|
|
2946
|
+
terminalCommand = `osascript -e '${script}'`;
|
|
2947
|
+
} else {
|
|
2948
|
+
// Linux: 尝试常见的终端模拟器
|
|
2949
|
+
// 优先使用 gnome-terminal, 然后是 xterm
|
|
2950
|
+
terminalCommand = `gnome-terminal -- bash -c "cd ${packagePath} && ${npmCommand}; exec bash" || xterm -e "cd ${packagePath} && ${npmCommand}; bash"`;
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
// 执行命令打开新终端
|
|
2954
|
+
const { exec } = await import('child_process');
|
|
2955
|
+
exec(terminalCommand, (error, stdout, stderr) => {
|
|
2956
|
+
if (error) {
|
|
2957
|
+
console.error('打开终端失败:', error);
|
|
2958
|
+
}
|
|
2959
|
+
});
|
|
2960
|
+
|
|
2961
|
+
res.json({
|
|
2962
|
+
success: true,
|
|
2963
|
+
message: `已在新终端中执行: ${scriptName}`,
|
|
2964
|
+
command: npmCommand,
|
|
2965
|
+
path: packagePath
|
|
2966
|
+
});
|
|
2967
|
+
} catch (error) {
|
|
2968
|
+
console.error('执行npm脚本失败:', error);
|
|
2969
|
+
res.status(500).json({
|
|
2970
|
+
success: false,
|
|
2971
|
+
error: `执行npm脚本失败: ${error.message}`
|
|
2972
|
+
});
|
|
2973
|
+
}
|
|
2974
|
+
});
|
|
2975
|
+
|
|
2747
2976
|
// Socket.io 实时更新
|
|
2748
2977
|
io.on('connection', (socket) => {
|
|
2749
2978
|
console.log('客户端已连接:', socket.id);
|
|
@@ -2971,7 +3200,7 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
2971
3200
|
try {
|
|
2972
3201
|
// 等待1秒,避免快速尝试多个端口
|
|
2973
3202
|
if (currentPort > startPort) {
|
|
2974
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
3203
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
2975
3204
|
console.log(`尝试端口 ${currentPort}...`);
|
|
2976
3205
|
}
|
|
2977
3206
|
|
|
@@ -3014,7 +3243,9 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
3014
3243
|
|
|
3015
3244
|
// 只有在noOpen为false时才打开浏览器
|
|
3016
3245
|
if (!noOpen) {
|
|
3017
|
-
|
|
3246
|
+
setTimeout(() => {
|
|
3247
|
+
open(`http://localhost:${currentPort}`);
|
|
3248
|
+
}, 0);
|
|
3018
3249
|
}
|
|
3019
3250
|
|
|
3020
3251
|
resolve();
|