coding-tool-x 3.4.1 → 3.4.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.
- package/package.json +1 -1
- package/src/commands/daemon.js +1 -1
- package/src/commands/doctor.js +2 -2
- package/src/commands/resume.js +1 -0
- package/src/commands/update.js +2 -1
- package/src/plugins/plugin-installer.js +1 -0
- package/src/server/api/claude-hooks.js +2 -3
- package/src/server/api/workspaces.js +2 -1
- package/src/server/services/base/base-channel-service.js +5 -1
- package/src/server/services/codex-env-manager.js +32 -2
- package/src/server/services/codex-settings-manager.js +20 -5
- package/src/server/services/mcp-client.js +2 -1
- package/src/server/services/notification-hooks.js +9 -8
- package/src/server/services/opencode-sessions.js +4 -2
- package/src/server/services/plugins-service.js +2 -1
- package/src/server/services/skill-service.js +4 -2
- package/src/server/services/workspace-service.js +1 -0
- package/src/utils/port-helper.js +19 -9
package/package.json
CHANGED
package/src/commands/daemon.js
CHANGED
|
@@ -83,7 +83,7 @@ function shouldTreatPortOwnershipAsReady(ownsPort) {
|
|
|
83
83
|
return ownsPort === true || ownsPort === null;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
async function waitForServiceReady(port, timeoutMs =
|
|
86
|
+
async function waitForServiceReady(port, timeoutMs = 15000, intervalMs = 500) {
|
|
87
87
|
const startAt = Date.now();
|
|
88
88
|
let lastProcess = null;
|
|
89
89
|
let stablePassCount = 0;
|
package/src/commands/doctor.js
CHANGED
|
@@ -262,7 +262,7 @@ async function checkProcessStatus() {
|
|
|
262
262
|
|
|
263
263
|
// 检查是否有 PM2 进程
|
|
264
264
|
try {
|
|
265
|
-
const { stdout } = await execAsync('pm2 list');
|
|
265
|
+
const { stdout } = await execAsync('pm2 list', { windowsHide: true });
|
|
266
266
|
if (stdout.includes('cc-tool')) {
|
|
267
267
|
return {
|
|
268
268
|
name: '进程状态',
|
|
@@ -295,7 +295,7 @@ async function checkProcessStatus() {
|
|
|
295
295
|
*/
|
|
296
296
|
async function checkDiskSpace() {
|
|
297
297
|
try {
|
|
298
|
-
const { stdout } = await execAsync('df -h ~');
|
|
298
|
+
const { stdout } = await execAsync('df -h ~', { windowsHide: true });
|
|
299
299
|
const lines = stdout.trim().split('\n');
|
|
300
300
|
if (lines.length > 1) {
|
|
301
301
|
const parts = lines[1].split(/\s+/);
|
package/src/commands/resume.js
CHANGED
package/src/commands/update.js
CHANGED
|
@@ -26,7 +26,8 @@ function runNpmInstall(packageName, version) {
|
|
|
26
26
|
return new Promise((resolve, reject) => {
|
|
27
27
|
const npmCommand = resolveNpmCommand();
|
|
28
28
|
const child = spawn(npmCommand, ['install', '-g', `${packageName}@${version}`], {
|
|
29
|
-
stdio: 'inherit'
|
|
29
|
+
stdio: 'inherit',
|
|
30
|
+
windowsHide: true
|
|
30
31
|
});
|
|
31
32
|
|
|
32
33
|
child.on('error', (err) => {
|
|
@@ -132,7 +132,7 @@ const timestamp = new Date().toLocaleString('zh-CN');
|
|
|
132
132
|
const cmd = generateSystemNotificationCommand(systemNotification.type);
|
|
133
133
|
script += `// 系统通知
|
|
134
134
|
try {
|
|
135
|
-
execSync(${JSON.stringify(cmd)}, { stdio: 'ignore' });
|
|
135
|
+
execSync(${JSON.stringify(cmd)}, { stdio: 'ignore', windowsHide: true });
|
|
136
136
|
} catch (e) {
|
|
137
137
|
console.error('系统通知失败:', e.message);
|
|
138
138
|
}
|
|
@@ -542,8 +542,7 @@ router.post('/test', (req, res) => {
|
|
|
542
542
|
// 测试系统通知
|
|
543
543
|
const command = generateSystemNotificationCommand(type || 'notification');
|
|
544
544
|
const { execSync } = require('child_process');
|
|
545
|
-
execSync(command, { stdio: 'ignore' });
|
|
546
|
-
res.json({ success: true, message: '系统测试通知已发送' });
|
|
545
|
+
execSync(command, { stdio: 'ignore', windowsHide: true });
|
|
547
546
|
}
|
|
548
547
|
} catch (error) {
|
|
549
548
|
console.error('Error testing notification:', error);
|
|
@@ -216,7 +216,11 @@ class BaseChannelService {
|
|
|
216
216
|
normalized.enabled = !!normalized.enabled;
|
|
217
217
|
}
|
|
218
218
|
normalized.weight = normalizeNumber(normalized.weight, 1, 100);
|
|
219
|
-
|
|
219
|
+
// maxConcurrency: null 表示不限制并发;只有用户显式设置正整数时才生效
|
|
220
|
+
const rawConcurrency = normalized.maxConcurrency;
|
|
221
|
+
normalized.maxConcurrency = (rawConcurrency !== null && rawConcurrency !== undefined && Number(rawConcurrency) > 0)
|
|
222
|
+
? Number(rawConcurrency)
|
|
223
|
+
: null;
|
|
220
224
|
normalized.gatewaySourceType = normalizeGatewaySourceType(
|
|
221
225
|
normalized.gatewaySourceType,
|
|
222
226
|
this.defaultGatewaySource
|
|
@@ -332,13 +332,32 @@ function runLaunchctlCommand(args, execSync) {
|
|
|
332
332
|
try {
|
|
333
333
|
execSync('launchctl', args, {
|
|
334
334
|
stdio: ['ignore', 'ignore', 'ignore'],
|
|
335
|
-
timeout: 3000
|
|
335
|
+
timeout: 3000,
|
|
336
|
+
windowsHide: true
|
|
336
337
|
});
|
|
337
338
|
} catch {
|
|
338
339
|
// ignore launchctl failures; shell profile remains the durable source
|
|
339
340
|
}
|
|
340
341
|
}
|
|
341
342
|
|
|
343
|
+
function broadcastWindowsSettingChange(execSync) {
|
|
344
|
+
const script = [
|
|
345
|
+
'Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @"',
|
|
346
|
+
'[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]',
|
|
347
|
+
'public static extern IntPtr SendMessageTimeout(',
|
|
348
|
+
' IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,',
|
|
349
|
+
' uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);',
|
|
350
|
+
'"@',
|
|
351
|
+
'$HWND_BROADCAST = [IntPtr]0xffff',
|
|
352
|
+
'$WM_SETTINGCHANGE = 0x1a',
|
|
353
|
+
'$SMTO_ABORTIFHUNG = 0x0002',
|
|
354
|
+
'$result = [UIntPtr]::Zero',
|
|
355
|
+
'[Win32.NativeMethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE,',
|
|
356
|
+
' [UIntPtr]::Zero, "Environment", $SMTO_ABORTIFHUNG, 5000, [ref]$result) | Out-Null'
|
|
357
|
+
].join('\n');
|
|
358
|
+
runWindowsEnvCommand(script, execSync);
|
|
359
|
+
}
|
|
360
|
+
|
|
342
361
|
function syncWindowsEnvironment(nextValues, previousState, options) {
|
|
343
362
|
const { stateFilePath, execSync } = options;
|
|
344
363
|
const nextKeys = Object.keys(nextValues).sort();
|
|
@@ -357,6 +376,15 @@ function syncWindowsEnvironment(nextValues, previousState, options) {
|
|
|
357
376
|
changed = true;
|
|
358
377
|
}
|
|
359
378
|
|
|
379
|
+
// 广播 WM_SETTINGCHANGE,通知已打开的应用(如 VSCode)刷新环境变量
|
|
380
|
+
if (changed) {
|
|
381
|
+
try {
|
|
382
|
+
broadcastWindowsSettingChange(execSync);
|
|
383
|
+
} catch {
|
|
384
|
+
// 广播失败不影响主流程,环境变量已写入注册表
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
360
388
|
if (nextKeys.length > 0) {
|
|
361
389
|
writeJsonFile(stateFilePath, {
|
|
362
390
|
version: 1,
|
|
@@ -386,7 +414,8 @@ function runWindowsEnvCommand(script, execSync) {
|
|
|
386
414
|
try {
|
|
387
415
|
execSync(command, ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
388
416
|
stdio: ['ignore', 'ignore', 'ignore'],
|
|
389
|
-
timeout: 5000
|
|
417
|
+
timeout: 5000,
|
|
418
|
+
windowsHide: true
|
|
390
419
|
});
|
|
391
420
|
return;
|
|
392
421
|
} catch (error) {
|
|
@@ -443,6 +472,7 @@ function syncCodexUserEnvironment(envMap = {}, options = {}) {
|
|
|
443
472
|
module.exports = {
|
|
444
473
|
syncCodexUserEnvironment,
|
|
445
474
|
_test: {
|
|
475
|
+
broadcastWindowsSettingChange,
|
|
446
476
|
buildHomeRelativeShellPath,
|
|
447
477
|
buildNextEnvValues,
|
|
448
478
|
buildSourceSnippet,
|
|
@@ -196,6 +196,9 @@ function restoreSettings() {
|
|
|
196
196
|
removeKeys: ['CC_PROXY_KEY']
|
|
197
197
|
});
|
|
198
198
|
|
|
199
|
+
// 清理 process.env 中的代理 key(与 setProxyConfig 中的设置对应)
|
|
200
|
+
delete process.env.CC_PROXY_KEY;
|
|
201
|
+
|
|
199
202
|
console.log('Codex settings restored from backup');
|
|
200
203
|
return { success: true };
|
|
201
204
|
} catch (err) {
|
|
@@ -206,11 +209,18 @@ function restoreSettings() {
|
|
|
206
209
|
// 设置代理配置
|
|
207
210
|
function setProxyConfig(proxyPort) {
|
|
208
211
|
try {
|
|
209
|
-
//
|
|
210
|
-
|
|
212
|
+
// 先备份(config.toml 不存在时跳过备份)
|
|
213
|
+
if (configExists()) {
|
|
214
|
+
backupSettings();
|
|
215
|
+
}
|
|
211
216
|
|
|
212
|
-
//
|
|
213
|
-
|
|
217
|
+
// 读取当前配置(不存在时使用空配置)
|
|
218
|
+
let config;
|
|
219
|
+
try {
|
|
220
|
+
config = readConfig();
|
|
221
|
+
} catch (err) {
|
|
222
|
+
config = {};
|
|
223
|
+
}
|
|
214
224
|
|
|
215
225
|
// 设置 model_provider 为 proxy
|
|
216
226
|
config.model_provider = 'cc-proxy';
|
|
@@ -225,12 +235,17 @@ function setProxyConfig(proxyPort) {
|
|
|
225
235
|
name: 'cc-proxy',
|
|
226
236
|
base_url: `http://127.0.0.1:${proxyPort}/v1`,
|
|
227
237
|
wire_api: 'responses',
|
|
228
|
-
env_key: 'CC_PROXY_KEY'
|
|
238
|
+
env_key: 'CC_PROXY_KEY',
|
|
239
|
+
requires_openai_auth: false
|
|
229
240
|
};
|
|
230
241
|
|
|
231
242
|
// 写入配置
|
|
232
243
|
writeConfig(config);
|
|
233
244
|
|
|
245
|
+
// Windows: 注册表写入后当前进程和已打开的终端不会自动刷新环境变量
|
|
246
|
+
// 直接设置 process.env 确保从本进程派生的 Codex CLI 能读到 CC_PROXY_KEY
|
|
247
|
+
process.env.CC_PROXY_KEY = 'PROXY_KEY';
|
|
248
|
+
|
|
234
249
|
const envResult = syncCodexUserEnvironment({
|
|
235
250
|
CC_PROXY_KEY: 'PROXY_KEY'
|
|
236
251
|
}, {
|
|
@@ -389,7 +389,8 @@ class McpClient extends EventEmitter {
|
|
|
389
389
|
this._child = spawn(resolvedCommand, args, {
|
|
390
390
|
env: mergedEnv,
|
|
391
391
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
392
|
-
cwd: finalCwd
|
|
392
|
+
cwd: finalCwd,
|
|
393
|
+
windowsHide: true
|
|
393
394
|
});
|
|
394
395
|
} catch (err) {
|
|
395
396
|
clearTimeout(timer);
|
|
@@ -200,7 +200,8 @@ function fire(eventType) {
|
|
|
200
200
|
\`--event-type=\${eventType}\`
|
|
201
201
|
], {
|
|
202
202
|
detached: true,
|
|
203
|
-
stdio: 'ignore'
|
|
203
|
+
stdio: 'ignore',
|
|
204
|
+
windowsHide: true
|
|
204
205
|
})
|
|
205
206
|
child.unref()
|
|
206
207
|
} catch (error) {
|
|
@@ -320,7 +321,7 @@ function notify(mode, message) {
|
|
|
320
321
|
const appleScript = 'display dialog "' + escapeForAppleScript(message) +
|
|
321
322
|
'" with title "' + escapeForAppleScript(title) +
|
|
322
323
|
'" buttons {"好的"} default button 1 with icon note'
|
|
323
|
-
execSync('osascript -e ' + JSON.stringify(appleScript), { stdio: 'ignore' })
|
|
324
|
+
execSync('osascript -e ' + JSON.stringify(appleScript), { stdio: 'ignore', windowsHide: true })
|
|
324
325
|
} else {
|
|
325
326
|
const fallbackScript = 'display notification "' + escapeForAppleScript(message) +
|
|
326
327
|
'" with title "' + escapeForAppleScript(title) + '" sound name "Glass"'
|
|
@@ -329,7 +330,7 @@ function notify(mode, message) {
|
|
|
329
330
|
' -message ' + JSON.stringify(message) +
|
|
330
331
|
' -sound Glass -activate com.apple.Terminal; ' +
|
|
331
332
|
'else osascript -e ' + JSON.stringify(fallbackScript) + '; fi'
|
|
332
|
-
execSync(command, { stdio: 'ignore' })
|
|
333
|
+
execSync(command, { stdio: 'ignore', windowsHide: true })
|
|
333
334
|
}
|
|
334
335
|
return
|
|
335
336
|
}
|
|
@@ -339,7 +340,7 @@ function notify(mode, message) {
|
|
|
339
340
|
const ps = "Add-Type -AssemblyName PresentationFramework; [System.Windows.MessageBox]::Show('" +
|
|
340
341
|
escapeForPowerShellSingleQuote(message) + "', '" +
|
|
341
342
|
escapeForPowerShellSingleQuote(title) + "', 'OK', 'Information')"
|
|
342
|
-
execSync('powershell -NoProfile -Command ' + JSON.stringify(ps), { stdio: 'ignore' })
|
|
343
|
+
execSync('powershell -NoProfile -Command ' + JSON.stringify(ps), { stdio: 'ignore', windowsHide: true })
|
|
343
344
|
} else {
|
|
344
345
|
const toastXml = '<toast><visual><binding template="ToastGeneric"><text>' +
|
|
345
346
|
escapeForXml(title) + '</text><text>' + escapeForXml(message) +
|
|
@@ -353,7 +354,7 @@ function notify(mode, message) {
|
|
|
353
354
|
"[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('Coding Tool').Show($toast) " +
|
|
354
355
|
"} catch { $wshell = New-Object -ComObject Wscript.Shell; $wshell.Popup('" +
|
|
355
356
|
escapeForPowerShellSingleQuote(message) + "', 5, '" + escapeForPowerShellSingleQuote(title) + "', 0x40) }"
|
|
356
|
-
execSync('powershell -NoProfile -Command ' + JSON.stringify(ps), { stdio: 'ignore' })
|
|
357
|
+
execSync('powershell -NoProfile -Command ' + JSON.stringify(ps), { stdio: 'ignore', windowsHide: true })
|
|
357
358
|
}
|
|
358
359
|
return
|
|
359
360
|
}
|
|
@@ -364,10 +365,10 @@ function notify(mode, message) {
|
|
|
364
365
|
execSync(
|
|
365
366
|
'zenity --info --title="' + escapedTitle + '" --text="' + escapedMessage +
|
|
366
367
|
'" 2>/dev/null || notify-send "' + escapedTitle + '" "' + escapedMessage + '"',
|
|
367
|
-
{ stdio: 'ignore' }
|
|
368
|
+
{ stdio: 'ignore', windowsHide: true }
|
|
368
369
|
)
|
|
369
370
|
} else {
|
|
370
|
-
execSync('notify-send "' + escapedTitle + '" "' + escapedMessage + '"', { stdio: 'ignore' })
|
|
371
|
+
execSync('notify-send "' + escapedTitle + '" "' + escapedMessage + '"', { stdio: 'ignore', windowsHide: true })
|
|
371
372
|
}
|
|
372
373
|
} catch (error) {
|
|
373
374
|
// ignore system notification failures
|
|
@@ -916,7 +917,7 @@ function testNotification({ type, testFeishu, webhookUrl } = {}) {
|
|
|
916
917
|
return sendFeishuTest(webhookUrl);
|
|
917
918
|
}
|
|
918
919
|
|
|
919
|
-
execSync(generateSystemNotificationCommand(type || 'notification', '这是一条测试通知'), { stdio: 'ignore' });
|
|
920
|
+
execSync(generateSystemNotificationCommand(type || 'notification', '这是一条测试通知'), { stdio: 'ignore', windowsHide: true });
|
|
920
921
|
}
|
|
921
922
|
|
|
922
923
|
module.exports = {
|
|
@@ -168,7 +168,8 @@ function runSqliteQuery(sql) {
|
|
|
168
168
|
const output = execFileSync('sqlite3', ['-json', OPENCODE_DB_PATH, sql], {
|
|
169
169
|
encoding: 'utf8',
|
|
170
170
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
171
|
-
maxBuffer: 10 * 1024 * 1024
|
|
171
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
172
|
+
windowsHide: true
|
|
172
173
|
}).trim();
|
|
173
174
|
|
|
174
175
|
if (!output) {
|
|
@@ -191,7 +192,8 @@ function runSqliteExec(sql) {
|
|
|
191
192
|
execFileSync('sqlite3', [OPENCODE_DB_PATH, sql], {
|
|
192
193
|
encoding: 'utf8',
|
|
193
194
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
194
|
-
maxBuffer: 10 * 1024 * 1024
|
|
195
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
196
|
+
windowsHide: true
|
|
195
197
|
});
|
|
196
198
|
}
|
|
197
199
|
|
|
@@ -1055,7 +1055,8 @@ class PluginsService {
|
|
|
1055
1055
|
execSync(`claude plugin marketplace add ${repo.url}`, {
|
|
1056
1056
|
encoding: 'utf8',
|
|
1057
1057
|
timeout: 30000,
|
|
1058
|
-
stdio: 'pipe'
|
|
1058
|
+
stdio: 'pipe',
|
|
1059
|
+
windowsHide: true
|
|
1059
1060
|
});
|
|
1060
1061
|
results.push({ repo: repo.url, success: true });
|
|
1061
1062
|
} catch (err) {
|
|
@@ -849,7 +849,8 @@ class SkillService {
|
|
|
849
849
|
const output = execFileSync(command, args, {
|
|
850
850
|
encoding: 'utf-8',
|
|
851
851
|
timeout: 3000,
|
|
852
|
-
stdio: ['ignore', 'pipe', 'ignore']
|
|
852
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
853
|
+
windowsHide: true
|
|
853
854
|
}).trim();
|
|
854
855
|
return output || null;
|
|
855
856
|
} catch {
|
|
@@ -866,7 +867,8 @@ class SkillService {
|
|
|
866
867
|
input: `protocol=https\nhost=${hostname}\n\n`,
|
|
867
868
|
encoding: 'utf-8',
|
|
868
869
|
timeout: 3000,
|
|
869
|
-
stdio: ['pipe', 'pipe', 'ignore']
|
|
870
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
871
|
+
windowsHide: true
|
|
870
872
|
});
|
|
871
873
|
const passwordLine = output
|
|
872
874
|
.split(/\r?\n/)
|
package/src/utils/port-helper.js
CHANGED
|
@@ -137,27 +137,37 @@ function findProcessByPort(port) {
|
|
|
137
137
|
const isWindows = isWindowsLikeRuntime();
|
|
138
138
|
if (isWindows) {
|
|
139
139
|
try {
|
|
140
|
-
// Windows:
|
|
141
|
-
const result = execSync(
|
|
140
|
+
// Windows: 优先使用 findstr 过滤,避免解析全量 netstat 输出(全量输出在连接数多时极慢)
|
|
141
|
+
const result = execSync(`netstat -ano | findstr ":${port} "`, { encoding: 'utf-8', windowsHide: true });
|
|
142
142
|
return parsePidsFromNetstatOutput(result, port);
|
|
143
143
|
} catch (e) {
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
// findstr 未匹配到任何行时 exit code = 1,属于正常情况
|
|
145
|
+
if (e.status === 1) {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
// findstr 不可用时回退到全量解析
|
|
149
|
+
try {
|
|
150
|
+
const result = execSync('netstat -ano', { encoding: 'utf-8', windowsHide: true });
|
|
151
|
+
return parsePidsFromNetstatOutput(result, port);
|
|
152
|
+
} catch (e2) {
|
|
153
|
+
if (isMissingCommandError(e2)) {
|
|
154
|
+
rememberPortToolIssue(createPortToolIssue('netstat', 'lookup', true));
|
|
155
|
+
}
|
|
156
|
+
return [];
|
|
146
157
|
}
|
|
147
|
-
return [];
|
|
148
158
|
}
|
|
149
159
|
}
|
|
150
160
|
|
|
151
161
|
let lsofMissing = false;
|
|
152
162
|
try {
|
|
153
163
|
// macOS/Linux 使用 lsof
|
|
154
|
-
const result = execSync(`lsof -ti :${port}`, { encoding: 'utf-8' }).trim();
|
|
164
|
+
const result = execSync(`lsof -ti :${port}`, { encoding: 'utf-8', windowsHide: true }).trim();
|
|
155
165
|
return result.split('\n').filter(pid => pid);
|
|
156
166
|
} catch (err) {
|
|
157
167
|
lsofMissing = isMissingCommandError(err);
|
|
158
168
|
// 如果 lsof 失败,尝试使用 fuser(某些 Linux 系统)
|
|
159
169
|
try {
|
|
160
|
-
const result = execSync(`fuser ${port}/tcp 2>/dev/null`, { encoding: 'utf-8' }).trim();
|
|
170
|
+
const result = execSync(`fuser ${port}/tcp 2>/dev/null`, { encoding: 'utf-8', windowsHide: true }).trim();
|
|
161
171
|
return result.split(/\s+/).filter(pid => pid);
|
|
162
172
|
} catch (e) {
|
|
163
173
|
if (lsofMissing && isMissingCommandError(e)) {
|
|
@@ -183,9 +193,9 @@ function killProcessByPort(port) {
|
|
|
183
193
|
pids.forEach(pid => {
|
|
184
194
|
try {
|
|
185
195
|
if (isWindows) {
|
|
186
|
-
execSync(`taskkill /F /PID ${pid}`, { stdio: 'ignore' });
|
|
196
|
+
execSync(`taskkill /F /PID ${pid}`, { stdio: 'ignore', windowsHide: true });
|
|
187
197
|
} else {
|
|
188
|
-
execSync(`kill -9 ${pid}`, { stdio: 'ignore' });
|
|
198
|
+
execSync(`kill -9 ${pid}`, { stdio: 'ignore', windowsHide: true });
|
|
189
199
|
}
|
|
190
200
|
killedAny = true;
|
|
191
201
|
} catch (err) {
|