zen-gitsync 2.11.39 → 2.12.3

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.
Files changed (58) hide show
  1. package/LICENSE +190 -21
  2. package/README.md +695 -695
  3. package/index.js +25 -11
  4. package/package.json +2 -2
  5. package/scripts/convert-colors-to-vars.cjs +286 -272
  6. package/scripts/convert-fontsize-to-vars.cjs +221 -207
  7. package/scripts/convert-spacing-to-vars.cjs +256 -242
  8. package/scripts/convert-to-standard-vars.cjs +282 -268
  9. package/scripts/release.js +599 -585
  10. package/src/config.js +350 -336
  11. package/src/gitCommit.js +455 -440
  12. package/src/ui/public/assets/EditorView-CbqSI9nw.css +1 -0
  13. package/src/ui/public/assets/EditorView-GS5cmh99.js +21 -0
  14. package/src/ui/public/assets/SourceMapView-DyMK80hS.css +1 -0
  15. package/src/ui/public/assets/SourceMapView-_YRtzmZZ.js +3 -0
  16. package/src/ui/public/assets/index-ML5Y-5lO.css +1 -0
  17. package/src/ui/public/assets/index-yky0Sd13.js +73 -0
  18. package/src/ui/public/assets/{ts.worker-Dth06zuC.js → ts.worker-METxwbDZ.js} +1 -16
  19. package/src/ui/public/assets/{vendor-B1T2uxYO.js → vendor-DITsiaGj.js} +294 -287
  20. package/src/ui/public/assets/vendor-q83wvJns.css +1 -0
  21. package/src/ui/public/index.html +4 -4
  22. package/src/ui/server/.claude/codediff.txt +6 -0
  23. package/src/ui/server/index.js +410 -396
  24. package/src/ui/server/middleware/requestLogger.js +51 -37
  25. package/src/ui/server/routes/branchStatus.js +101 -87
  26. package/src/ui/server/routes/code.js +110 -96
  27. package/src/ui/server/routes/codeAnalysis.js +995 -981
  28. package/src/ui/server/routes/config.js +1172 -1158
  29. package/src/ui/server/routes/exec.js +272 -258
  30. package/src/ui/server/routes/fileOpen.js +279 -265
  31. package/src/ui/server/routes/fs.js +701 -699
  32. package/src/ui/server/routes/git/diff.js +352 -338
  33. package/src/ui/server/routes/git/diffUtils.js +128 -114
  34. package/src/ui/server/routes/git/stash.js +552 -538
  35. package/src/ui/server/routes/git/tags.js +172 -158
  36. package/src/ui/server/routes/git.js +190 -176
  37. package/src/ui/server/routes/gitOps.js +1179 -1165
  38. package/src/ui/server/routes/instances.js +38 -24
  39. package/src/ui/server/routes/npm.js +1023 -1009
  40. package/src/ui/server/routes/process.js +82 -68
  41. package/src/ui/server/routes/status.js +67 -53
  42. package/src/ui/server/routes/terminal.js +319 -305
  43. package/src/ui/server/socket/registerUiSocketHandlers.js +226 -212
  44. package/src/ui/server/utils/createSavePortToFile.js +46 -32
  45. package/src/ui/server/utils/instanceRegistry.js +270 -256
  46. package/src/ui/server/utils/pathGuard.js +155 -0
  47. package/src/ui/server/utils/pathGuard.test.js +138 -0
  48. package/src/ui/server/utils/randomStartPort.js +51 -37
  49. package/src/ui/server/utils/startServerOnAvailablePort.js +101 -87
  50. package/src/utils/index.js +1058 -1044
  51. package/src/ui/public/assets/devopicons-QN4QXivI.woff2 +0 -0
  52. package/src/ui/public/assets/file-icons-C0jOugUK.woff2 +0 -0
  53. package/src/ui/public/assets/fontawesome-B-jkhYfk.woff2 +0 -0
  54. package/src/ui/public/assets/index-BvVl-092.js +0 -95
  55. package/src/ui/public/assets/index-DXO3Lvqi.css +0 -1
  56. package/src/ui/public/assets/mfixx-CpAhKOZz.woff2 +0 -0
  57. package/src/ui/public/assets/octicons-CaZ_fok2.woff2 +0 -0
  58. package/src/ui/public/assets/vendor-hOO_r_AU.css +0 -1
@@ -1,212 +1,226 @@
1
- export function registerUiSocketHandlers({
2
- io,
3
- getProjectRoomId,
4
- getCurrentProjectPath,
5
- getAndBroadcastStatus,
6
- getCommandHistory,
7
- clearCommandHistory,
8
- addCommandToHistory,
9
- runningProcesses,
10
- nextProcessId,
11
- spawn,
12
- exec,
13
- path,
14
- iconv
15
- }) {
16
- io.on('connection', (socket) => {
17
- console.log('客户端已连接:', socket.id);
18
-
19
- const projectRoomId = getProjectRoomId();
20
- socket.join(projectRoomId);
21
- console.log(`客户端 ${socket.id} 已加入房间: ${projectRoomId}`);
22
-
23
- getAndBroadcastStatus();
24
-
25
- const history = getCommandHistory();
26
- socket.emit('initial_command_history', { history });
27
-
28
- socket.on('request_full_history', () => {
29
- const fullHistory = getCommandHistory();
30
- socket.emit('full_command_history', { history: fullHistory });
31
- });
32
-
33
- socket.on('clear_command_history', () => {
34
- const result = clearCommandHistory();
35
- socket.emit('command_history_cleared', { success: result });
36
- });
37
-
38
- socket.on('exec_interactive', async (data) => {
39
- const { command, directory, sessionId } = data;
40
-
41
- if (!command || typeof command !== 'string' || !command.trim()) {
42
- socket.emit('interactive_error', {
43
- sessionId,
44
- error: 'command 不能为空'
45
- });
46
- return;
47
- }
48
-
49
- const currentProjectPath = getCurrentProjectPath();
50
- const execDirectory = directory && directory.trim()
51
- ? (path.isAbsolute(directory) ? directory : path.join(currentProjectPath, directory))
52
- : currentProjectPath;
53
-
54
- console.log(`[交互式命令] ${sessionId}: ${command} (目录: ${execDirectory})`);
55
-
56
- const processId = nextProcessId();
57
- const startTime = Date.now();
58
-
59
- let collectedStdout = '';
60
- let collectedStderr = '';
61
-
62
- const childProcess = spawn(command.trim(), [], {
63
- cwd: execDirectory,
64
- shell: true,
65
- env: {
66
- ...process.env,
67
- GIT_CONFIG_PARAMETERS: "'color.ui=always' 'color.status=always' 'core.quotepath=false'",
68
- FORCE_COLOR: '3',
69
- NPM_CONFIG_COLOR: 'always',
70
- TERM: 'xterm-256color',
71
- COLORTERM: 'truecolor',
72
- CLICOLOR_FORCE: '1',
73
- PYTHONUNBUFFERED: '1'
74
- }
75
- });
76
-
77
- runningProcesses.set(processId, {
78
- childProcess,
79
- command: command.trim(),
80
- startTime,
81
- directory: execDirectory,
82
- sessionId
83
- });
84
-
85
- console.log(`[交互式命令] 创建进程 #${processId}: ${command.substring(0, 50)}`);
86
-
87
- socket.emit('interactive_process_id', { sessionId, processId });
88
-
89
- const isWindows = process.platform === 'win32';
90
- const cmdBuiltins = ['dir', 'type', 'echo', 'set', 'path', 'cd', 'md', 'rd', 'del', 'copy', 'move', 'ren'];
91
- const needsGbkConversion = isWindows && cmdBuiltins.some(builtin =>
92
- command.trim().toLowerCase().startsWith(builtin + ' ') ||
93
- command.trim().toLowerCase() === builtin
94
- );
95
-
96
- childProcess.stdout?.on('data', (stdoutData) => {
97
- const output = needsGbkConversion ? iconv.decode(stdoutData, 'gbk') : stdoutData.toString('utf8');
98
- collectedStdout += output;
99
- socket.emit('interactive_stdout', { sessionId, data: output });
100
- });
101
-
102
- childProcess.stderr?.on('data', (stderrData) => {
103
- let output;
104
-
105
- if (isWindows) {
106
- const utf8Output = stderrData.toString('utf8');
107
-
108
- if (!utf8Output.includes('�') || utf8Output.match(/[\u4e00-\u9fa5]/)) {
109
- output = utf8Output;
110
- } else {
111
- try {
112
- output = iconv.decode(stderrData, 'gbk');
113
- } catch (e) {
114
- output = utf8Output;
115
- }
116
- }
117
- } else {
118
- output = stderrData.toString('utf8');
119
- }
120
-
121
- collectedStderr += output;
122
- socket.emit('interactive_stderr', { sessionId, data: output });
123
- });
124
-
125
- childProcess.on('close', (code, signal) => {
126
- runningProcesses.delete(processId);
127
- console.log(`[交互式命令] 进程 #${processId} 已结束`);
128
-
129
- const executionTime = Date.now() - startTime;
130
- const error = code !== 0 ? `Command exited with code ${code}` : null;
131
- addCommandToHistory(
132
- command.trim(),
133
- collectedStdout,
134
- collectedStderr,
135
- error,
136
- executionTime
137
- );
138
-
139
- socket.emit('interactive_exit', {
140
- sessionId,
141
- code,
142
- success: code === 0
143
- });
144
- });
145
-
146
- childProcess.on('error', (error) => {
147
- runningProcesses.delete(processId);
148
- console.error(`[交互式命令] 进程 #${processId} 出错:`, error);
149
-
150
- const executionTime = Date.now() - startTime;
151
- addCommandToHistory(
152
- command.trim(),
153
- collectedStdout,
154
- collectedStderr,
155
- error.message,
156
- executionTime
157
- );
158
-
159
- socket.emit('interactive_error', {
160
- sessionId,
161
- error: error.message
162
- });
163
- });
164
-
165
- socket.on(`interactive_stdin_${sessionId}`, (inputData) => {
166
- const { input } = inputData;
167
- console.log(`[交互式命令] 收到 stdin 输入 (${sessionId}):`, input);
168
-
169
- if (childProcess.stdin && !childProcess.stdin.destroyed) {
170
- try {
171
- childProcess.stdin.write(input + '\n');
172
- } catch (err) {
173
- console.error(`[交互式命令] 写入 stdin 失败:`, err);
174
- socket.emit('interactive_error', {
175
- sessionId,
176
- error: `写入输入失败: ${err.message}`
177
- });
178
- }
179
- }
180
- });
181
-
182
- socket.on(`interactive_stop_${sessionId}`, () => {
183
- console.log(`[交互式命令] 收到停止请求 (${sessionId})`);
184
-
185
- if (childProcess && !childProcess.killed) {
186
- try {
187
- if (process.platform === 'win32') {
188
- exec(`taskkill /pid ${childProcess.pid} /T /F`, (error) => {
189
- if (error) {
190
- console.error(`[交互式命令] taskkill 失败:`, error);
191
- }
192
- });
193
- } else {
194
- childProcess.kill('SIGTERM');
195
- setTimeout(() => {
196
- if (!childProcess.killed) {
197
- childProcess.kill('SIGKILL');
198
- }
199
- }, 2000);
200
- }
201
- } catch (err) {
202
- console.error(`[交互式命令] 停止进程失败:`, err);
203
- }
204
- }
205
- });
206
- });
207
-
208
- socket.on('disconnect', () => {
209
- console.log(`客户端已断开连接: ${socket.id} (房间: ${getProjectRoomId()})`);
210
- });
211
- });
212
- }
1
+ // Copyright 2026 xz333221
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+ export function registerUiSocketHandlers({
16
+ io,
17
+ getProjectRoomId,
18
+ getCurrentProjectPath,
19
+ getAndBroadcastStatus,
20
+ getCommandHistory,
21
+ clearCommandHistory,
22
+ addCommandToHistory,
23
+ runningProcesses,
24
+ nextProcessId,
25
+ spawn,
26
+ exec,
27
+ path,
28
+ iconv
29
+ }) {
30
+ io.on('connection', (socket) => {
31
+ console.log('客户端已连接:', socket.id);
32
+
33
+ const projectRoomId = getProjectRoomId();
34
+ socket.join(projectRoomId);
35
+ console.log(`客户端 ${socket.id} 已加入房间: ${projectRoomId}`);
36
+
37
+ getAndBroadcastStatus();
38
+
39
+ const history = getCommandHistory();
40
+ socket.emit('initial_command_history', { history });
41
+
42
+ socket.on('request_full_history', () => {
43
+ const fullHistory = getCommandHistory();
44
+ socket.emit('full_command_history', { history: fullHistory });
45
+ });
46
+
47
+ socket.on('clear_command_history', () => {
48
+ const result = clearCommandHistory();
49
+ socket.emit('command_history_cleared', { success: result });
50
+ });
51
+
52
+ socket.on('exec_interactive', async (data) => {
53
+ const { command, directory, sessionId } = data;
54
+
55
+ if (!command || typeof command !== 'string' || !command.trim()) {
56
+ socket.emit('interactive_error', {
57
+ sessionId,
58
+ error: 'command 不能为空'
59
+ });
60
+ return;
61
+ }
62
+
63
+ const currentProjectPath = getCurrentProjectPath();
64
+ const execDirectory = directory && directory.trim()
65
+ ? (path.isAbsolute(directory) ? directory : path.join(currentProjectPath, directory))
66
+ : currentProjectPath;
67
+
68
+ console.log(`[交互式命令] ${sessionId}: ${command} (目录: ${execDirectory})`);
69
+
70
+ const processId = nextProcessId();
71
+ const startTime = Date.now();
72
+
73
+ let collectedStdout = '';
74
+ let collectedStderr = '';
75
+
76
+ const childProcess = spawn(command.trim(), [], {
77
+ cwd: execDirectory,
78
+ shell: true,
79
+ env: {
80
+ ...process.env,
81
+ GIT_CONFIG_PARAMETERS: "'color.ui=always' 'color.status=always' 'core.quotepath=false'",
82
+ FORCE_COLOR: '3',
83
+ NPM_CONFIG_COLOR: 'always',
84
+ TERM: 'xterm-256color',
85
+ COLORTERM: 'truecolor',
86
+ CLICOLOR_FORCE: '1',
87
+ PYTHONUNBUFFERED: '1'
88
+ }
89
+ });
90
+
91
+ runningProcesses.set(processId, {
92
+ childProcess,
93
+ command: command.trim(),
94
+ startTime,
95
+ directory: execDirectory,
96
+ sessionId
97
+ });
98
+
99
+ console.log(`[交互式命令] 创建进程 #${processId}: ${command.substring(0, 50)}`);
100
+
101
+ socket.emit('interactive_process_id', { sessionId, processId });
102
+
103
+ const isWindows = process.platform === 'win32';
104
+ const cmdBuiltins = ['dir', 'type', 'echo', 'set', 'path', 'cd', 'md', 'rd', 'del', 'copy', 'move', 'ren'];
105
+ const needsGbkConversion = isWindows && cmdBuiltins.some(builtin =>
106
+ command.trim().toLowerCase().startsWith(builtin + ' ') ||
107
+ command.trim().toLowerCase() === builtin
108
+ );
109
+
110
+ childProcess.stdout?.on('data', (stdoutData) => {
111
+ const output = needsGbkConversion ? iconv.decode(stdoutData, 'gbk') : stdoutData.toString('utf8');
112
+ collectedStdout += output;
113
+ socket.emit('interactive_stdout', { sessionId, data: output });
114
+ });
115
+
116
+ childProcess.stderr?.on('data', (stderrData) => {
117
+ let output;
118
+
119
+ if (isWindows) {
120
+ const utf8Output = stderrData.toString('utf8');
121
+
122
+ if (!utf8Output.includes('') || utf8Output.match(/[\u4e00-\u9fa5]/)) {
123
+ output = utf8Output;
124
+ } else {
125
+ try {
126
+ output = iconv.decode(stderrData, 'gbk');
127
+ } catch (e) {
128
+ output = utf8Output;
129
+ }
130
+ }
131
+ } else {
132
+ output = stderrData.toString('utf8');
133
+ }
134
+
135
+ collectedStderr += output;
136
+ socket.emit('interactive_stderr', { sessionId, data: output });
137
+ });
138
+
139
+ childProcess.on('close', (code, signal) => {
140
+ runningProcesses.delete(processId);
141
+ console.log(`[交互式命令] 进程 #${processId} 已结束`);
142
+
143
+ const executionTime = Date.now() - startTime;
144
+ const error = code !== 0 ? `Command exited with code ${code}` : null;
145
+ addCommandToHistory(
146
+ command.trim(),
147
+ collectedStdout,
148
+ collectedStderr,
149
+ error,
150
+ executionTime
151
+ );
152
+
153
+ socket.emit('interactive_exit', {
154
+ sessionId,
155
+ code,
156
+ success: code === 0
157
+ });
158
+ });
159
+
160
+ childProcess.on('error', (error) => {
161
+ runningProcesses.delete(processId);
162
+ console.error(`[交互式命令] 进程 #${processId} 出错:`, error);
163
+
164
+ const executionTime = Date.now() - startTime;
165
+ addCommandToHistory(
166
+ command.trim(),
167
+ collectedStdout,
168
+ collectedStderr,
169
+ error.message,
170
+ executionTime
171
+ );
172
+
173
+ socket.emit('interactive_error', {
174
+ sessionId,
175
+ error: error.message
176
+ });
177
+ });
178
+
179
+ socket.on(`interactive_stdin_${sessionId}`, (inputData) => {
180
+ const { input } = inputData;
181
+ console.log(`[交互式命令] 收到 stdin 输入 (${sessionId}):`, input);
182
+
183
+ if (childProcess.stdin && !childProcess.stdin.destroyed) {
184
+ try {
185
+ childProcess.stdin.write(input + '\n');
186
+ } catch (err) {
187
+ console.error(`[交互式命令] 写入 stdin 失败:`, err);
188
+ socket.emit('interactive_error', {
189
+ sessionId,
190
+ error: `写入输入失败: ${err.message}`
191
+ });
192
+ }
193
+ }
194
+ });
195
+
196
+ socket.on(`interactive_stop_${sessionId}`, () => {
197
+ console.log(`[交互式命令] 收到停止请求 (${sessionId})`);
198
+
199
+ if (childProcess && !childProcess.killed) {
200
+ try {
201
+ if (process.platform === 'win32') {
202
+ exec(`taskkill /pid ${childProcess.pid} /T /F`, (error) => {
203
+ if (error) {
204
+ console.error(`[交互式命令] taskkill 失败:`, error);
205
+ }
206
+ });
207
+ } else {
208
+ childProcess.kill('SIGTERM');
209
+ setTimeout(() => {
210
+ if (!childProcess.killed) {
211
+ childProcess.kill('SIGKILL');
212
+ }
213
+ }, 2000);
214
+ }
215
+ } catch (err) {
216
+ console.error(`[交互式命令] 停止进程失败:`, err);
217
+ }
218
+ }
219
+ });
220
+ });
221
+
222
+ socket.on('disconnect', () => {
223
+ console.log(`客户端已断开连接: ${socket.id} (房间: ${getProjectRoomId()})`);
224
+ });
225
+ });
226
+ }
@@ -1,32 +1,46 @@
1
- export function createSavePortToFile({ savePort, fs, path, cwdFn = process.cwd }) {
2
- let savedPort = null;
3
-
4
- return async function savePortToFile(port) {
5
- try {
6
- if (savePort && savedPort !== port) {
7
- savedPort = port;
8
-
9
- const portFilePath = path.join(cwdFn(), '.port');
10
- await fs.writeFile(portFilePath, port.toString(), 'utf8');
11
- console.log(`端口号 ${port} 已保存到 ${portFilePath}`);
12
-
13
- try {
14
- const clientPath = path.join(cwdFn(), 'src', 'ui', 'client');
15
- const envPath = path.join(clientPath, '.env.local');
16
-
17
- await fs.access(clientPath).catch(() => {
18
- console.log(`客户端目录 ${clientPath} 不存在,跳过环境变量设置`);
19
- return Promise.reject(new Error('Client directory not found'));
20
- });
21
-
22
- await fs.writeFile(envPath, `VITE_BACKEND_PORT=${port}\n`, 'utf8');
23
- console.log(`端口号环境变量已保存到 ${envPath}`);
24
- } catch (envError) {
25
- console.error('保存端口号到环境变量失败,但不影响主要功能:', envError);
26
- }
27
- }
28
- } catch (error) {
29
- console.error('保存端口号到文件失败:', error);
30
- }
31
- };
32
- }
1
+ // Copyright 2026 xz333221
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+ export function createSavePortToFile({ savePort, fs, path, cwdFn = process.cwd }) {
16
+ let savedPort = null;
17
+
18
+ return async function savePortToFile(port) {
19
+ try {
20
+ if (savePort && savedPort !== port) {
21
+ savedPort = port;
22
+
23
+ const portFilePath = path.join(cwdFn(), '.port');
24
+ await fs.writeFile(portFilePath, port.toString(), 'utf8');
25
+ console.log(`端口号 ${port} 已保存到 ${portFilePath}`);
26
+
27
+ try {
28
+ const clientPath = path.join(cwdFn(), 'src', 'ui', 'client');
29
+ const envPath = path.join(clientPath, '.env.local');
30
+
31
+ await fs.access(clientPath).catch(() => {
32
+ console.log(`客户端目录 ${clientPath} 不存在,跳过环境变量设置`);
33
+ return Promise.reject(new Error('Client directory not found'));
34
+ });
35
+
36
+ await fs.writeFile(envPath, `VITE_BACKEND_PORT=${port}\n`, 'utf8');
37
+ console.log(`端口号环境变量已保存到 ${envPath}`);
38
+ } catch (envError) {
39
+ console.error('保存端口号到环境变量失败,但不影响主要功能:', envError);
40
+ }
41
+ }
42
+ } catch (error) {
43
+ console.error('保存端口号到文件失败:', error);
44
+ }
45
+ };
46
+ }