coding-tool-x 3.7.6 → 3.7.8

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 (44) hide show
  1. package/dist/web/assets/{Analytics-wCRLv26m.js → Analytics-BE5H9JGG.js} +1 -1
  2. package/dist/web/assets/{ConfigTemplates-aa1blEHh.js → ConfigTemplates-z118k1BE.js} +1 -1
  3. package/dist/web/assets/{Home-eIxO50-k.js → Home-CBhtmOIN.js} +1 -1
  4. package/dist/web/assets/{PluginManager-BAwGFNjN.js → PluginManager-CGhfZ3t8.js} +1 -1
  5. package/dist/web/assets/ProjectList-BMPLJtgQ.js +1 -0
  6. package/dist/web/assets/ProjectList-VeMNXfgO.css +1 -0
  7. package/dist/web/assets/SessionList-C4chCPxK.css +1 -0
  8. package/dist/web/assets/SessionList-D9e7wGF1.js +1 -0
  9. package/dist/web/assets/{SkillManager-Bhzs1O5S.js → SkillManager-DJpdUmnk.js} +1 -1
  10. package/dist/web/assets/{WorkspaceManager-CLdX-pTT.js → WorkspaceManager-C0Ud_V7v.js} +1 -1
  11. package/dist/web/assets/index-CeTaG4IX.js +2 -0
  12. package/dist/web/assets/index-S8wrU2Nf.css +1 -0
  13. package/dist/web/index.html +2 -2
  14. package/package.json +1 -1
  15. package/src/commands/daemon.js +3 -1
  16. package/src/commands/port-config.js +0 -2
  17. package/src/commands/switch.js +1 -5
  18. package/src/commands/ui.js +4 -3
  19. package/src/commands/workspace.js +35 -3
  20. package/src/config/loader.js +76 -5
  21. package/src/index.js +11 -0
  22. package/src/server/api/config.js +0 -3
  23. package/src/server/api/omp-channels.js +29 -9
  24. package/src/server/api/omp-config.js +6 -8
  25. package/src/server/api/settings.js +0 -2
  26. package/src/server/api/workspaces.js +39 -1
  27. package/src/server/services/dashboard-snapshot-worker.js +1 -0
  28. package/src/server/services/https-cert.js +3 -2
  29. package/src/server/services/model-detector.js +106 -26
  30. package/src/server/services/omp-auth-providers.js +1 -0
  31. package/src/server/services/omp-config.js +22 -0
  32. package/src/server/services/omp-settings-manager.js +31 -16
  33. package/src/server/services/session-snapshots.js +1 -0
  34. package/src/server/services/sessions.js +35 -19
  35. package/src/server/services/web-build.js +2 -1
  36. package/src/server/services/workspace-service.js +204 -98
  37. package/src/utils/cli-flags.js +7 -0
  38. package/src/utils/session.js +10 -4
  39. package/dist/web/assets/ProjectList-CRiAEixW.js +0 -1
  40. package/dist/web/assets/ProjectList-Dw2omgOH.css +0 -1
  41. package/dist/web/assets/SessionList-BQ0FW6Gd.js +0 -1
  42. package/dist/web/assets/SessionList-kYJ6-Ies.css +0 -1
  43. package/dist/web/assets/index-B_nFqixU.css +0 -1
  44. package/dist/web/assets/index-ClMNA9Hf.js +0 -2
@@ -3,7 +3,7 @@ const fs = require('fs');
3
3
  const path = require('path');
4
4
  const os = require('os');
5
5
  const DEFAULT_CONFIG = require('./default');
6
- const { PATHS, ensureStorageDirMigrated } = require('./paths');
6
+ const { PATHS, NATIVE_PATHS, ensureStorageDirMigrated } = require('./paths');
7
7
  const { resolvePreferredHomeDir } = require('../utils/home-dir');
8
8
  const eventBus = require('../plugins/event-bus');
9
9
 
@@ -23,12 +23,79 @@ function getConfigFilePath() {
23
23
  * 展开 ~ 为用户主目录
24
24
  */
25
25
  function expandHome(filepath) {
26
+ if (typeof filepath !== 'string') {
27
+ return filepath;
28
+ }
26
29
  if (filepath.startsWith('~')) {
27
30
  return path.join(HOME_DIR, filepath.slice(1));
28
31
  }
29
32
  return filepath;
30
33
  }
31
34
 
35
+ function collapseHome(filepath) {
36
+ if (typeof filepath !== 'string') {
37
+ return filepath;
38
+ }
39
+ if (filepath === HOME_DIR) {
40
+ return '~';
41
+ }
42
+ const prefix = `${HOME_DIR}${path.sep}`;
43
+ if (filepath.startsWith(prefix)) {
44
+ return path.join('~', filepath.slice(prefix.length));
45
+ }
46
+ return filepath;
47
+ }
48
+
49
+ function getNativeClaudeProjectsDir() {
50
+ return NATIVE_PATHS?.claude?.projects || path.join(HOME_DIR, '.claude', 'projects');
51
+ }
52
+
53
+ function hasExplicitProjectsDir(config = {}) {
54
+ return Object.prototype.hasOwnProperty.call(config, 'projectsDir') &&
55
+ typeof config.projectsDir === 'string' &&
56
+ config.projectsDir.trim() !== '';
57
+ }
58
+
59
+ function resolveClaudeProjectsDir(config = {}) {
60
+ if (!hasExplicitProjectsDir(config) || isDefaultProjectsDir(config.projectsDir)) {
61
+ return getNativeClaudeProjectsDir();
62
+ }
63
+ return expandHome(config.projectsDir);
64
+ }
65
+
66
+ function normalizeComparablePath(filepath) {
67
+ if (typeof filepath !== 'string' || !filepath.trim()) {
68
+ return '';
69
+ }
70
+ return path.normalize(expandHome(filepath.trim()));
71
+ }
72
+
73
+ function isDefaultProjectsDir(projectsDir) {
74
+ const normalized = normalizeComparablePath(projectsDir);
75
+ if (!normalized) {
76
+ return true;
77
+ }
78
+ const nativeDefault = normalizeComparablePath(getNativeClaudeProjectsDir());
79
+ const legacyHomeDefault = normalizeComparablePath(path.join(HOME_DIR, '.claude', 'projects'));
80
+ if (normalized === nativeDefault || normalized === legacyHomeDefault) {
81
+ return true;
82
+ }
83
+ const normalizedParts = normalized.replace(/\\/g, '/').split('/').filter(Boolean);
84
+ const last = normalizedParts[normalizedParts.length - 1];
85
+ const previous = normalizedParts[normalizedParts.length - 2];
86
+ return previous === '.claude' && last === 'projects' && !fs.existsSync(normalized);
87
+ }
88
+
89
+ function normalizeConfigForSave(config = {}) {
90
+ const normalized = { ...config };
91
+ if (isDefaultProjectsDir(normalized.projectsDir)) {
92
+ delete normalized.projectsDir;
93
+ } else if (typeof normalized.projectsDir === 'string') {
94
+ normalized.projectsDir = collapseHome(normalized.projectsDir);
95
+ }
96
+ return normalized;
97
+ }
98
+
32
99
  function mergePricing(defaultPricing, overrides = {}) {
33
100
  const merged = {};
34
101
  Object.keys(defaultPricing).forEach((key) => {
@@ -96,7 +163,7 @@ function loadConfig() {
96
163
  if (fs.existsSync(configFilePath)) {
97
164
  const userConfig = readJsonFile(configFilePath);
98
165
  const config = { ...DEFAULT_CONFIG, ...userConfig };
99
- config.projectsDir = expandHome(config.projectsDir || DEFAULT_CONFIG.projectsDir);
166
+ config.projectsDir = resolveClaudeProjectsDir(userConfig);
100
167
 
101
168
  // 合并 ports 配置
102
169
  config.ports = { ...DEFAULT_CONFIG.ports, ...userConfig.ports };
@@ -120,7 +187,7 @@ function loadConfig() {
120
187
  }
121
188
  const defaultConfig = {
122
189
  ...DEFAULT_CONFIG,
123
- projectsDir: expandHome(DEFAULT_CONFIG.projectsDir),
190
+ projectsDir: resolveClaudeProjectsDir(),
124
191
  currentProject: DEFAULT_CONFIG.defaultProject
125
192
  };
126
193
  eventBus.emitSync('config:loaded', { config: defaultConfig });
@@ -137,8 +204,9 @@ function saveConfig(config) {
137
204
  if (!fs.existsSync(dir)) {
138
205
  fs.mkdirSync(dir, { recursive: true });
139
206
  }
140
- fs.writeFileSync(configFilePath, JSON.stringify(config, null, 2), 'utf8');
141
- eventBus.emitSync('config:saved', { config });
207
+ const normalizedConfig = normalizeConfigForSave(config);
208
+ fs.writeFileSync(configFilePath, JSON.stringify(normalizedConfig, null, 2), 'utf8');
209
+ eventBus.emitSync('config:saved', { config: normalizedConfig });
142
210
  } catch (error) {
143
211
  console.error(`保存配置失败 (${configFilePath}):`, error.message);
144
212
  }
@@ -148,5 +216,8 @@ module.exports = {
148
216
  loadConfig,
149
217
  saveConfig,
150
218
  expandHome,
219
+ resolveClaudeProjectsDir,
220
+ normalizeConfigForSave,
221
+ isDefaultProjectsDir,
151
222
  getConfigFilePath
152
223
  };
package/src/index.js CHANGED
@@ -17,6 +17,7 @@ const { ensureStorageDirMigrated } = require('./config/paths');
17
17
  const { normalizePlatformKey } = require('./shared/platforms');
18
18
  const PluginManager = require('./plugins/plugin-manager');
19
19
  const eventBus = require('./plugins/event-bus');
20
+ const { hasHostFlag } = require('./utils/cli-flags');
20
21
  const chalk = require('chalk');
21
22
  const path = require('path');
22
23
  const fs = require('fs');
@@ -54,9 +55,12 @@ function showHelp() {
54
55
  console.log(' ctx ui 前台启动 Web UI(仅本地访问)');
55
56
  console.log(' ctx ui --https 前台启动 Web UI(本地 HTTPS)');
56
57
  console.log(' ctx ui --host 前台启动 Web UI(允许 LAN 访问,默认允许远程写操作)');
58
+ console.log(' ctx ui --hosts 前台启动 Web UI(同 --host)');
59
+ console.log(' ctx --hosts 前台启动 Web UI(同 ctx ui --host)');
57
60
  console.log(' ctx ui start 后台启动 Web UI');
58
61
  console.log(' ctx ui start --https 后台启动 Web UI(本地 HTTPS)');
59
62
  console.log(' ctx ui start --host 后台启动 Web UI(允许 LAN 访问,默认允许远程写操作)');
63
+ console.log(' ctx ui start --hosts 后台启动 Web UI(同 --host)');
60
64
  console.log(' ctx ui stop 停止 Web UI');
61
65
  console.log(' ctx ui restart 重启 Web UI\n');
62
66
  console.log(chalk.gray(' 提示: 如需禁止 LAN 远程写操作,可设置 CC_TOOL_ALLOW_REMOTE_WRITE=false\n'));
@@ -149,6 +153,13 @@ async function main() {
149
153
  // 处理命令行参数
150
154
  const args = process.argv.slice(2);
151
155
 
156
+ // LAN 快捷入口,等同于 ctx ui --host/--hosts
157
+ if (hasHostFlag(process.argv) && !args.some((arg) => arg && !arg.startsWith('-'))) {
158
+ const { handleUI } = require('./commands/ui');
159
+ await handleUI();
160
+ return;
161
+ }
162
+
152
163
  // --version 或 -v - 显示版本号
153
164
  if (args[0] === '--version' || args[0] === '-v') {
154
165
  console.log(getVersion());
@@ -330,7 +330,6 @@ router.post('/default-models', (req, res) => {
330
330
  // Save config
331
331
  const newConfig = {
332
332
  ...config,
333
- projectsDir: config.projectsDir.replace(require('os').homedir(), '~'),
334
333
  defaultModels: newDefaultModels
335
334
  };
336
335
 
@@ -378,7 +377,6 @@ router.post('/default-models/reset', (req, res) => {
378
377
  // Save config
379
378
  const newConfig = {
380
379
  ...config,
381
- projectsDir: config.projectsDir.replace(require('os').homedir(), '~'),
382
380
  defaultModels: newDefaultModels
383
381
  };
384
382
 
@@ -490,7 +488,6 @@ router.post('/advanced', (req, res) => {
490
488
  // 更新配置
491
489
  const newConfig = {
492
490
  ...config,
493
- projectsDir: config.projectsDir.replace(require('os').homedir(), '~'),
494
491
  ports: normalizedPorts,
495
492
  maxLogs: maxLogs !== undefined ? parseInt(maxLogs) : config.maxLogs,
496
493
  statsInterval: statsInterval !== undefined ? parseInt(statsInterval) : config.statsInterval,
@@ -24,8 +24,7 @@ const {
24
24
  const {
25
25
  findAuthProviderForKey,
26
26
  getCachedOmpAuthProviderSnapshot,
27
- getOmpAuthProviderCacheMeta,
28
- warmOmpAuthProviderSnapshot
27
+ getOmpAuthProviderCacheMeta
29
28
  } = require('../services/omp-auth-providers');
30
29
 
31
30
  const CHANNEL_LIST_AUTH_OPTIONS = { accountCheck: false, includeStatus: false };
@@ -77,6 +76,8 @@ async function discoverOmpModels(channel, gatewaySourceType, { forceRefresh = fa
77
76
  models: preferredModels,
78
77
  supported: preferredModels.length > 0,
79
78
  cached: false,
79
+ stale: false,
80
+ retryAfter: null,
80
81
  fallbackUsed: true,
81
82
  fetchedAt: new Date().toISOString(),
82
83
  error: preferredModels.length > 0 ? null : 'OAuth 渠道未配置 API Key,无法直接调用远端模型列表接口',
@@ -91,11 +92,27 @@ async function discoverOmpModels(channel, gatewaySourceType, { forceRefresh = fa
91
92
  });
92
93
  const listedModels = Array.isArray(listResult.models) ? uniqueModels(listResult.models) : [];
93
94
 
95
+ if (listResult.backoff || listResult.stale) {
96
+ return {
97
+ models: listedModels,
98
+ supported: listedModels.length > 0,
99
+ cached: !!listResult.cached,
100
+ stale: true,
101
+ retryAfter: listResult.retryAfter || null,
102
+ fallbackUsed: false,
103
+ fetchedAt: listResult.lastChecked || new Date().toISOString(),
104
+ error: listResult.error || (listedModels.length > 0 ? null : '模型目录缓存已过期'),
105
+ errorHint: listResult.errorHint || '可点击“刷新可选模型”更新,或手动填写模型名称。'
106
+ };
107
+ }
108
+
94
109
  if (listedModels.length > 0) {
95
110
  return {
96
111
  models: listedModels,
97
112
  supported: true,
98
113
  cached: !!listResult.cached,
114
+ stale: false,
115
+ retryAfter: null,
99
116
  fallbackUsed: false,
100
117
  fetchedAt: listResult.lastChecked || new Date().toISOString(),
101
118
  error: null,
@@ -114,6 +131,8 @@ async function discoverOmpModels(channel, gatewaySourceType, { forceRefresh = fa
114
131
  models: probedModels,
115
132
  supported: probedModels.length > 0,
116
133
  cached: !!probe.cached || !!listResult.cached,
134
+ stale: false,
135
+ retryAfter: null,
117
136
  fallbackUsed: probedModels.length > 0,
118
137
  fetchedAt: probe.lastChecked || listResult.lastChecked || new Date().toISOString(),
119
138
  error: probedModels.length > 0 ? null : (listResult.error || '无法获取可用模型'),
@@ -142,17 +161,12 @@ function attachAuthProvider(channel, snapshot) {
142
161
 
143
162
  function getAuthSnapshotForChannelList() {
144
163
  const snapshot = getCachedOmpAuthProviderSnapshot(CHANNEL_LIST_AUTH_OPTIONS);
145
- const shouldWarm = !snapshot || snapshot.stale;
146
- if (shouldWarm) {
147
- warmOmpAuthProviderSnapshot(CHANNEL_LIST_AUTH_OPTIONS);
148
- }
149
164
  const meta = getOmpAuthProviderCacheMeta(CHANNEL_LIST_AUTH_OPTIONS);
150
165
  return {
151
166
  snapshot,
152
167
  meta: {
153
168
  ...meta,
154
169
  stale: !snapshot || Boolean(snapshot.stale),
155
- refreshing: shouldWarm || meta.refreshing,
156
170
  fallback: !snapshot
157
171
  }
158
172
  };
@@ -241,7 +255,8 @@ module.exports = () => {
241
255
  model,
242
256
  speedTestModel,
243
257
  allowedModels,
244
- modelRedirects
258
+ modelRedirects,
259
+ forceRefresh
245
260
  } = req.body || {};
246
261
  if (!baseUrl) {
247
262
  return res.status(400).json({ error: 'baseUrl is required' });
@@ -259,11 +274,14 @@ module.exports = () => {
259
274
  modelRedirects: Array.isArray(modelRedirects) ? modelRedirects : []
260
275
  };
261
276
  const resolvedGatewaySourceType = resolveGatewaySourceType(tempChannel);
262
- const result = await discoverOmpModels(tempChannel, resolvedGatewaySourceType, { forceRefresh: true });
277
+ const result = await discoverOmpModels(tempChannel, resolvedGatewaySourceType, { forceRefresh: forceRefresh === true });
263
278
  res.json({
264
279
  models: result.models,
265
280
  supported: result.supported,
266
281
  fallbackUsed: result.fallbackUsed,
282
+ cached: result.cached,
283
+ stale: result.stale,
284
+ retryAfter: result.retryAfter,
267
285
  error: result.error,
268
286
  errorHint: result.errorHint
269
287
  });
@@ -291,6 +309,8 @@ module.exports = () => {
291
309
  models: result.models,
292
310
  supported: result.supported,
293
311
  cached: result.cached,
312
+ stale: result.stale,
313
+ retryAfter: result.retryAfter,
294
314
  fallbackUsed: result.fallbackUsed,
295
315
  fetchedAt: result.fetchedAt,
296
316
  error: result.error,
@@ -6,8 +6,7 @@ const {
6
6
  getOmpPaths,
7
7
  getOmpStatus,
8
8
  readJsonFile,
9
- readYamlFile,
10
- readOmpSettings
9
+ readYamlFile
11
10
  } = require('../services/omp-config');
12
11
  const {
13
12
  getOmpAuthProviderSnapshot
@@ -89,13 +88,12 @@ function buildCapabilities() {
89
88
 
90
89
  router.get('/', (req, res) => {
91
90
  try {
92
- const paths = getOmpPaths();
93
- const settings = readOmpSettings();
91
+ const paths = getOmpPaths(process.env, { resolveRuntime: false });
92
+ const settings = readYamlFile(paths.settings, readJsonFile(paths.settingsJsonLegacy, {}));
94
93
  const resources = {
95
94
  config: readYamlFile(paths.config, {}),
96
95
  settings: readYamlFile(paths.settings, {}),
97
96
  auth: fs.existsSync(paths.auth) ? { exists: true, path: paths.auth } : { exists: false, path: paths.auth },
98
- authProviders: getOmpAuthProviderSnapshot({ accountCheck: false }),
99
97
  models: readYamlFile(paths.modelsYml, {}),
100
98
  legacyModels: readJsonFile(paths.modelsJsonLegacy, {}),
101
99
  packages: normalizeArray(settings.packages),
@@ -110,7 +108,7 @@ router.get('/', (req, res) => {
110
108
 
111
109
  res.json({
112
110
  success: true,
113
- status: getOmpStatus(),
111
+ status: getOmpStatus(process.env, { resolveRuntime: false }),
114
112
  capabilities: buildCapabilities(),
115
113
  resources
116
114
  });
@@ -143,8 +141,8 @@ router.get('/auth-providers', (req, res) => {
143
141
 
144
142
  router.get('/resources', (req, res) => {
145
143
  try {
146
- const paths = getOmpPaths();
147
- const settings = readOmpSettings();
144
+ const paths = getOmpPaths(process.env, { resolveRuntime: false });
145
+ const settings = readYamlFile(paths.settings, readJsonFile(paths.settingsJsonLegacy, {}));
148
146
  res.json({
149
147
  success: true,
150
148
  paths,
@@ -90,7 +90,6 @@ function handleSaveModelSettings(req, res) {
90
90
  const config = loadConfig();
91
91
  const newConfig = {
92
92
  ...config,
93
- projectsDir: config.projectsDir.replace(require('os').homedir(), '~'),
94
93
  modelMetadataOverrides: overrides && typeof overrides === 'object'
95
94
  ? overrides
96
95
  : (config.modelMetadataOverrides || {})
@@ -124,7 +123,6 @@ function handleDeleteModelOverride(req, res) {
124
123
 
125
124
  const newConfig = {
126
125
  ...config,
127
- projectsDir: config.projectsDir.replace(require('os').homedir(), '~'),
128
126
  modelMetadataOverrides: overrides
129
127
  };
130
128
  saveConfig(newConfig);
@@ -33,6 +33,24 @@ function validateBranchName(branchName) {
33
33
  }
34
34
  }
35
35
 
36
+ function normalizeBranchMode(branchMode) {
37
+ if (typeof branchMode !== 'string' || !branchMode.trim()) {
38
+ return '';
39
+ }
40
+ return branchMode.trim();
41
+ }
42
+
43
+ function validateBranchMode(branchMode) {
44
+ const normalized = normalizeBranchMode(branchMode);
45
+ if (!normalized) {
46
+ return { valid: true, normalized: '' };
47
+ }
48
+ if (normalized !== 'existing' && normalized !== 'new') {
49
+ return { valid: false, normalized, message: `分支模式不合法: ${normalized}` };
50
+ }
51
+ return { valid: true, normalized };
52
+ }
53
+
36
54
  /**
37
55
  * GET /api/workspaces
38
56
  * 获取所有工作区列表
@@ -226,6 +244,17 @@ router.post('/', (req, res) => {
226
244
  });
227
245
  }
228
246
 
247
+ const branchModeValidation = validateBranchMode(proj.branchMode);
248
+ if (!branchModeValidation.valid) {
249
+ return res.status(400).json({
250
+ success: false,
251
+ message: branchModeValidation.message
252
+ });
253
+ }
254
+ if (branchModeValidation.normalized) {
255
+ proj.branchMode = branchModeValidation.normalized;
256
+ }
257
+
229
258
  const normalizedBranch = normalizeBranchName(proj.branch);
230
259
  if (normalizedBranch) {
231
260
  const branchValidation = validateBranchName(normalizedBranch);
@@ -329,9 +358,10 @@ router.put('/:id/last-used', (req, res) => {
329
358
  router.post('/:id/projects', (req, res) => {
330
359
  try {
331
360
  const { id } = req.params;
332
- const { sourcePath, name, createWorktree, branch, baseBranch } = req.body;
361
+ const { sourcePath, name, createWorktree, branch, baseBranch, branchMode } = req.body;
333
362
  const normalizedBranch = normalizeBranchName(branch);
334
363
  const normalizedBaseBranch = normalizeBranchName(baseBranch);
364
+ const branchModeValidation = validateBranchMode(branchMode);
335
365
 
336
366
  if (!sourcePath || !sourcePath.trim()) {
337
367
  return res.status(400).json({
@@ -340,6 +370,13 @@ router.post('/:id/projects', (req, res) => {
340
370
  });
341
371
  }
342
372
 
373
+ if (!branchModeValidation.valid) {
374
+ return res.status(400).json({
375
+ success: false,
376
+ message: branchModeValidation.message
377
+ });
378
+ }
379
+
343
380
  if (normalizedBranch) {
344
381
  const branchValidation = validateBranchName(normalizedBranch);
345
382
  if (!branchValidation.valid) {
@@ -364,6 +401,7 @@ router.post('/:id/projects', (req, res) => {
364
401
  sourcePath,
365
402
  name,
366
403
  createWorktree,
404
+ branchMode: branchModeValidation.normalized || branchMode,
367
405
  branch: normalizedBranch || branch,
368
406
  baseBranch: normalizedBaseBranch || baseBranch
369
407
  });
@@ -130,6 +130,7 @@ function runDashboardSnapshotWorker(kind, source, config = {}, options = {}) {
130
130
  return new Promise((resolve, reject) => {
131
131
  const child = fork(__filename, [], {
132
132
  stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
133
+ windowsHide: true,
133
134
  env: {
134
135
  ...process.env,
135
136
  CC_TOOL_DASHBOARD_SNAPSHOT_WORKER: '1'
@@ -42,7 +42,7 @@ function resolveOpenSslCommand() {
42
42
 
43
43
  for (const command of commands) {
44
44
  try {
45
- execFileSync(command, ['version'], { stdio: 'ignore' });
45
+ execFileSync(command, ['version'], { stdio: 'ignore', windowsHide: true });
46
46
  return command;
47
47
  } catch {
48
48
  // Try next candidate.
@@ -96,7 +96,8 @@ function generatePemCertificateWithOpenSsl(command) {
96
96
  '-config',
97
97
  configPath
98
98
  ], {
99
- stdio: 'ignore'
99
+ stdio: 'ignore',
100
+ windowsHide: true
100
101
  });
101
102
 
102
103
  writeMeta({