zen-gitsync 2.15.30 → 2.15.31

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.
@@ -10,7 +10,7 @@
10
10
  <link rel="preconnect" href="https://fonts.googleapis.com" />
11
11
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
12
12
  <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,400&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
13
- <script type="module" crossorigin src="/assets/index-DpwdXh8e.js"></script>
13
+ <script type="module" crossorigin src="/assets/index-CAW0IiDm.js"></script>
14
14
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-DAXXjFlN.js">
15
15
  <link rel="modulepreload" crossorigin href="/assets/element-plus-DBGf-skY.js">
16
16
  <link rel="modulepreload" crossorigin href="/assets/monaco-KftCdSZr.js">
@@ -23,9 +23,9 @@
23
23
  <link rel="modulepreload" crossorigin href="/assets/ImagePreview-CeSeWU0t.js">
24
24
  <link rel="modulepreload" crossorigin href="/assets/FileDiffViewer-C_Rp8g6W.js">
25
25
  <link rel="modulepreload" crossorigin href="/assets/socket-io-bk7R1Z85.js">
26
- <link rel="modulepreload" crossorigin href="/assets/gitStore-T6PtHRWi.js">
26
+ <link rel="modulepreload" crossorigin href="/assets/gitStore-D2xg5IwV.js">
27
27
  <link rel="modulepreload" crossorigin href="/assets/toolsStore-AZ-U898D.js">
28
- <link rel="modulepreload" crossorigin href="/assets/ResetToRemoteButton-BNbaEjgP.js">
28
+ <link rel="modulepreload" crossorigin href="/assets/ResetToRemoteButton-UAGJ4Nvx.js">
29
29
  <link rel="modulepreload" crossorigin href="/assets/workbenchStatus-Dy0YOLlr.js">
30
30
  <link rel="modulepreload" crossorigin href="/assets/editorTabs-CXh38lX6.js">
31
31
  <link rel="modulepreload" crossorigin href="/assets/TemplateManager-phCNTUTY.js">
@@ -16,7 +16,7 @@ import fs from 'fs/promises';
16
16
  import { asyncRoute, HttpError } from '../utils/asyncRoute.js';
17
17
  import path from 'path';
18
18
  import open from 'open';
19
- import { spawn } from 'child_process';
19
+ import { spawn, spawnSync } from 'child_process';
20
20
 
21
21
  function spawnDetached(command, args, options = {}) {
22
22
  return new Promise((resolve, reject) => {
@@ -34,6 +34,59 @@ function spawnDetached(command, args, options = {}) {
34
34
  });
35
35
  }
36
36
 
37
+ function shQuote(s) {
38
+ return `'${String(s).replace(/'/g, `'\\''`)}'`;
39
+ }
40
+
41
+ // Linux 终端模拟器探测顺序:x-terminal-emulator 是 Debian/Ubuntu alternatives
42
+ // 指向用户默认终端的通用入口,优先使用;其余为常见桌面终端的兜底
43
+ const LINUX_TERMINALS = [
44
+ { cmd: 'x-terminal-emulator', args: (shell) => ['-e', 'bash', '-c', shell] },
45
+ { cmd: 'gnome-terminal', args: (shell) => ['--', 'bash', '-c', shell] },
46
+ { cmd: 'konsole', args: (shell) => ['-e', 'bash', '-c', shell] },
47
+ { cmd: 'xfce4-terminal', args: (shell) => ['-x', 'bash', '-c', shell] },
48
+ { cmd: 'mate-terminal', args: (shell) => ['-e', `bash -c ${shQuote(shell)}`] },
49
+ { cmd: 'alacritty', args: (shell) => ['-e', 'bash', '-c', shell] },
50
+ { cmd: 'kitty', args: (shell) => ['bash', '-c', shell] },
51
+ { cmd: 'xterm', args: (shell) => ['-e', 'bash', '-c', shell] },
52
+ ];
53
+
54
+ function findLinuxTerminal() {
55
+ for (const term of LINUX_TERMINALS) {
56
+ const r = spawnSync('which', [term.cmd], { stdio: 'pipe' });
57
+ if (r.status === 0) return term;
58
+ }
59
+ return null;
60
+ }
61
+
62
+ // 拼一段在终端里执行的 shell:先 cd 到目标目录再启动 CLI;
63
+ // 进程非零退出时停住窗口,否则 CLI 启动失败会秒关窗口,用户看不到任何报错
64
+ function buildTerminalShell(dirPath, command, cliArgs) {
65
+ const cmdStr = [command, ...cliArgs].map(shQuote).join(' ');
66
+ return `cd ${shQuote(dirPath)} && ${cmdStr}; rc=$?; if [ $rc -ne 0 ]; then echo; echo "${command} 退出码 $rc,按回车关闭窗口"; read -r _; fi`;
67
+ }
68
+
69
+ // Linux/macOS 没有统一的"新开终端窗口"API:直接 spawn TUI 程序只会在后台
70
+ // 跑一个无 TTY 的进程(stdio 被 ignore),用户屏幕上看不到任何东西,
71
+ // 必须显式起终端模拟器把命令跑在窗口里
72
+ async function launchInTerminal(dirPath, command, cliArgs = []) {
73
+ const shell = buildTerminalShell(dirPath, command, cliArgs);
74
+
75
+ if (process.platform === 'darwin') {
76
+ const script = `tell application "Terminal"\nactivate\ndo script ${JSON.stringify(shell)}\nend tell`;
77
+ return spawnDetached('osascript', ['-e', script]);
78
+ }
79
+
80
+ if (!process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) {
81
+ throw new Error('当前会话没有图形环境(DISPLAY / WAYLAND_DISPLAY 均为空),无法打开终端窗口');
82
+ }
83
+ const term = findLinuxTerminal();
84
+ if (!term) {
85
+ throw new Error('未检测到可用的终端模拟器(gnome-terminal / konsole / xterm 等)');
86
+ }
87
+ return spawnDetached(term.cmd, term.args(shell));
88
+ }
89
+
37
90
  async function launchClaudeCode(dirPath, { permissionMode } = {}) {
38
91
  // 透传可选的权限模式参数到 claude CLI(如 acceptEdits)
39
92
  // 注意:permissionMode 必须是一个 token 字符串,避免 shell 注入
@@ -49,9 +102,7 @@ async function launchClaudeCode(dirPath, { permissionMode } = {}) {
49
102
  });
50
103
  }
51
104
 
52
- return spawnDetached('claude', cliArgs, {
53
- cwd: dirPath
54
- });
105
+ return launchInTerminal(dirPath, 'claude', cliArgs);
55
106
  }
56
107
 
57
108
  async function launchCodex(dirPath) {
@@ -61,7 +112,7 @@ async function launchCodex(dirPath) {
61
112
  cwd: dirPath
62
113
  });
63
114
  }
64
- return spawnDetached('codex', [], { cwd: dirPath });
115
+ return launchInTerminal(dirPath, 'codex');
65
116
  }
66
117
 
67
118
  async function launchOpenCode(dirPath) {
@@ -71,7 +122,7 @@ async function launchOpenCode(dirPath) {
71
122
  cwd: dirPath
72
123
  });
73
124
  }
74
- return spawnDetached('opencode', [], { cwd: dirPath });
125
+ return launchInTerminal(dirPath, 'opencode');
75
126
  }
76
127
 
77
128
  export function registerFileOpenRoutes({
@@ -290,7 +341,7 @@ export function registerFileOpenRoutes({
290
341
  } catch (error) {
291
342
  res.status(400).json({
292
343
  success: false,
293
- error: '未检测到 Claude Code,请先安装并确保可以在终端中直接运行 claude'
344
+ error: error.message || '未检测到 Claude Code,请先安装并确保可以在终端中直接运行 claude'
294
345
  });
295
346
  }
296
347
  } catch (error) {
@@ -318,7 +369,7 @@ export function registerFileOpenRoutes({
318
369
  } catch (error) {
319
370
  res.status(400).json({
320
371
  success: false,
321
- error: '未检测到 Codex,请先安装并确保可以在终端中直接运行 codex'
372
+ error: error.message || '未检测到 Codex,请先安装并确保可以在终端中直接运行 codex'
322
373
  });
323
374
  }
324
375
  } catch (error) {
@@ -346,7 +397,7 @@ export function registerFileOpenRoutes({
346
397
  } catch (error) {
347
398
  res.status(400).json({
348
399
  success: false,
349
- error: '未检测到 OpenCode,请先安装并确保可以在终端中直接运行 opencode'
400
+ error: error.message || '未检测到 OpenCode,请先安装并确保可以在终端中直接运行 opencode'
350
401
  });
351
402
  }
352
403
  } catch (error) {