local-mcp 3.0.417 → 3.0.418
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/download.js +59 -8
- package/index.js +2 -2
- package/launch-event.js +87 -8
- package/package.json +1 -1
- package/setup.js +8 -4
package/download.js
CHANGED
|
@@ -109,6 +109,26 @@ function _writeWinMachineIdCache(cacheDir, id) {
|
|
|
109
109
|
// memoized too, on purpose: a run where PowerShell is blocked shouldn't retry
|
|
110
110
|
// it on every subsequent call in the same process.
|
|
111
111
|
let _winHardwareIdCache
|
|
112
|
+
// Valores que Win32_ComputerSystemProduct.UUID devuelve y que NO identifican a una máquina:
|
|
113
|
+
// relleno, o —el caso peligroso— el MISMO valor en máquinas distintas (defaults de OEM/BIOS).
|
|
114
|
+
// Aceptar uno junta a N máquinas en una identidad, y la separa de su propio heartbeat, porque el
|
|
115
|
+
// cliente Go lo rechaza. Copia a mano de `knownGarbageHardwareIDs` de lmcp-win
|
|
116
|
+
// (internal/machineid/probe_windows.go, #1594): si esa lista cambia, ésta cambia con ella
|
|
117
|
+
// (colibird-ai/lmcp-npm#29). En mayúsculas; la comparación no distingue mayúsculas.
|
|
118
|
+
const UUIDS_DE_HARDWARE_BASURA = new Set([
|
|
119
|
+
'NIL', // lo que imprime PowerShell cuando WMI no tiene nada que dar
|
|
120
|
+
'00000000-0000-0000-0000-000000000000',
|
|
121
|
+
'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF',
|
|
122
|
+
'03000200-0400-0500-0006-000700080009', // el default clásico de OEM/BIOS
|
|
123
|
+
])
|
|
124
|
+
// Tope de cada llamada del probe (#42, O1). execSync bloquea el event loop: con WMI colgado, el
|
|
125
|
+
// proceso suelto del evento de lanzamiento no llegaba nunca a su propio tope de 4 s y quedaba vivo,
|
|
126
|
+
// oculto, para siempre. Con el tope, una llamada colgada se mata y el probe sigue (o termina vacío).
|
|
127
|
+
const TOPE_PROBE_MS = 2000
|
|
128
|
+
function _esIdDeHardwareValido(id) {
|
|
129
|
+
return !!id && !UUIDS_DE_HARDWARE_BASURA.has(String(id).toUpperCase())
|
|
130
|
+
}
|
|
131
|
+
|
|
112
132
|
// execImpl is injectable (defaults to the real execSync) so tests can pin the
|
|
113
133
|
// PowerShell-then-wmic ORDER without a Windows machine — see
|
|
114
134
|
// npm/test/machine-id-windows-order.test.js.
|
|
@@ -120,21 +140,24 @@ function _probeWinHardwareId(execImpl = require('child_process').execSync) {
|
|
|
120
140
|
// between npm and the go-server and reintroduced #1544 (0 of 9 Windows
|
|
121
141
|
// installs joined their heartbeat because the two sides disagreed on id).
|
|
122
142
|
try {
|
|
143
|
+
// windowsHide: lo corre un proceso sin consola propia (el emisor del evento de lanzamiento,
|
|
144
|
+
// el setup lanzado por un cliente de IA). Sin la opción, Windows puede mostrar una consola; el
|
|
145
|
+
// cliente Go pone HideWindow por lo mismo (#31). Medido sin ventana en 3.0.417; esto la descarta.
|
|
123
146
|
const ps = execImpl(
|
|
124
147
|
'powershell -NoProfile -Command "(Get-WmiObject Win32_ComputerSystemProduct).UUID"',
|
|
125
|
-
{ stdio: 'pipe' }
|
|
148
|
+
{ stdio: 'pipe', windowsHide: true, timeout: TOPE_PROBE_MS }
|
|
126
149
|
).toString().trim()
|
|
127
150
|
// PowerShell prints the literal string "nil", not an empty line, when WMI
|
|
128
151
|
// has no UUID to give — an emptiness check alone would accept it as an id.
|
|
129
|
-
if (ps
|
|
152
|
+
if (_esIdDeHardwareValido(ps)) {
|
|
130
153
|
_winHardwareIdCache = ps
|
|
131
154
|
return _winHardwareIdCache
|
|
132
155
|
}
|
|
133
156
|
} catch {}
|
|
134
157
|
try {
|
|
135
|
-
const wmic = execImpl('wmic csproduct get UUID /value', { stdio: 'pipe' }).toString()
|
|
158
|
+
const wmic = execImpl('wmic csproduct get UUID /value', { stdio: 'pipe', windowsHide: true, timeout: TOPE_PROBE_MS }).toString()
|
|
136
159
|
const match = wmic.match(/UUID=(.+)/)
|
|
137
|
-
if (match) {
|
|
160
|
+
if (match && _esIdDeHardwareValido(match[1].trim())) {
|
|
138
161
|
_winHardwareIdCache = match[1].trim()
|
|
139
162
|
return _winHardwareIdCache
|
|
140
163
|
}
|
|
@@ -155,6 +178,19 @@ function _resetWinHardwareIdCacheForTests() {
|
|
|
155
178
|
// que invertir la prioridad hardware-antes-que-cache —la decision irreversible de
|
|
156
179
|
// machineid.go— los pasaba a todos en verde. Sin argumentos se comporta igual que
|
|
157
180
|
// antes; ningun caller de produccion pasa deps.
|
|
181
|
+
// The machine_id the macOS tray persisted in its config.json, or '' when there is none yet.
|
|
182
|
+
function _readTrayMachineId(deps = {}) {
|
|
183
|
+
const home = deps.homeDir || require('os').homedir()
|
|
184
|
+
const read = deps.readFileImpl || ((p) => require('fs').readFileSync(p, 'utf8'))
|
|
185
|
+
try {
|
|
186
|
+
const cfg = JSON.parse(read(require('path').join(home, 'Library', 'Application Support', 'Local MCP', 'config.json')))
|
|
187
|
+
const id = typeof cfg.machine_id === 'string' ? cfg.machine_id.trim() : ''
|
|
188
|
+
return id
|
|
189
|
+
} catch {
|
|
190
|
+
return ''
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
158
194
|
function _getMachineId(deps = {}) {
|
|
159
195
|
const platform = deps.platform || process.platform
|
|
160
196
|
const cacheDir = deps.cacheDir || CACHE_DIR
|
|
@@ -166,6 +202,13 @@ function _getMachineId(deps = {}) {
|
|
|
166
202
|
|
|
167
203
|
const execSync = deps.execImpl || require('child_process').execSync
|
|
168
204
|
if (platform === 'darwin') {
|
|
205
|
+
// QA 3.0.418 (Dario, m4): the tray's own id lives in its config.json (it read the keychain
|
|
206
|
+
// when IT could). An npm run in a context without keychain access (an agent or SSH session)
|
|
207
|
+
// fell to the hardware UUID and minted the cloud token for a SECOND identity of the same Mac,
|
|
208
|
+
// so the server refused that Mac's device key, its machine approval and the agent mesh.
|
|
209
|
+
// The id the tray already uses comes first.
|
|
210
|
+
const trayId = _readTrayMachineId(deps)
|
|
211
|
+
if (trayId) return trayId
|
|
169
212
|
try {
|
|
170
213
|
const r = execSync('security find-generic-password -s com.local-mcp.machine-id -a machine-id -w 2>/dev/null', { stdio: 'pipe' })
|
|
171
214
|
const id = r.toString().trim()
|
|
@@ -252,12 +295,20 @@ async function getLatestBinary() {
|
|
|
252
295
|
let data = ''
|
|
253
296
|
res.on('data', chunk => data += chunk)
|
|
254
297
|
res.on('end', () => {
|
|
298
|
+
let info
|
|
255
299
|
try {
|
|
256
|
-
|
|
257
|
-
resolve(info)
|
|
300
|
+
info = JSON.parse(data)
|
|
258
301
|
} catch (e) {
|
|
259
|
-
reject(new Error(`Respuesta inválida de /runtime/latest: ${data}`))
|
|
302
|
+
return reject(new Error(`Respuesta inválida de /runtime/latest: ${data}`))
|
|
303
|
+
}
|
|
304
|
+
// Un error del API también es JSON (`{"detail": …}` en un 5xx o un 429). Aceptarlo dejaba
|
|
305
|
+
// `version` en undefined y los co-procesos se pedían como `teams-proxy-undefined-…`: 25
|
|
306
|
+
// pedidos en un día de API caído (#38). Sin un 200 con versión no hay respuesta, y todo
|
|
307
|
+
// el que llama ya trata un rechazo como «no disponible».
|
|
308
|
+
if (res.statusCode !== 200 || !info || !/^[0-9][0-9A-Za-z.+-]*$/.test(String(info.version || ''))) {
|
|
309
|
+
return reject(new Error(`/runtime/latest no dio una versión (HTTP ${res.statusCode})`))
|
|
260
310
|
}
|
|
311
|
+
resolve(info)
|
|
261
312
|
})
|
|
262
313
|
})
|
|
263
314
|
req.on('error', reject)
|
|
@@ -870,7 +921,7 @@ module.exports = {
|
|
|
870
921
|
ensureBinary, ensureRuntime, ensureTray, ensureTeamsProxy, ensureSlackProxy, versionFromArtifactUrl,
|
|
871
922
|
latestVersionForPlatform, getArch,
|
|
872
923
|
CACHE_DIR, TRAY_DIR, _getMachineId, LINUX_NO_SOPORTADO,
|
|
873
|
-
_probeWinHardwareId, _resetWinHardwareIdCacheForTests,
|
|
924
|
+
_probeWinHardwareId, _resetWinHardwareIdCacheForTests, UUIDS_DE_HARDWARE_BASURA, _esIdDeHardwareValido,
|
|
874
925
|
_readWinMachineIdCache, _writeWinMachineIdCache, WIN_MACHINE_ID_CACHE_NAME,
|
|
875
926
|
_retireSupersededTrays, _isUsableTrayBundle, SYSTEM_TRAY_APP,
|
|
876
927
|
_bundleVersion, _isTrayRunningFrom, _versionIsNewer,
|
package/index.js
CHANGED
|
@@ -248,7 +248,8 @@ const ES_LANZAMIENTO = require.main === module && !COMANDOS.includes(cmd) && !PE
|
|
|
248
248
|
function reportarLanzamiento(outcome, opciones) {
|
|
249
249
|
if (ES_LANZAMIENTO) evento.reportar(outcome, evento.claseDeArgumento(cmd), opciones)
|
|
250
250
|
}
|
|
251
|
-
|
|
251
|
+
// Antes que nada: un `spawned` que en Windows esperaba su id sale ahora, por el proceso suelto (#21).
|
|
252
|
+
if (ES_LANZAMIENTO) process.on('exit', () => { evento.soltarPendiente(); reportarLanzamiento('other') })
|
|
252
253
|
|
|
253
254
|
// Platform support: macOS (Swift tray + embedded server) and Windows (go-server standalone
|
|
254
255
|
// binary downloaded from R2). Linux passes this guard so that `--help`, `status`, `update` and
|
|
@@ -404,7 +405,6 @@ async function main() {
|
|
|
404
405
|
try {
|
|
405
406
|
const s = JSON.parse(data)
|
|
406
407
|
console.log(`LMCP v${s.version} — ${s.server}`)
|
|
407
|
-
console.log(`Licencia: ${s.license?.status} (${s.license?.days_left}d)`)
|
|
408
408
|
} catch { console.log(data) }
|
|
409
409
|
})
|
|
410
410
|
}).on('error', () => console.log('Servidor no disponible en localhost:8765'))
|
package/launch-event.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// tragado;
|
|
15
15
|
// · mandar algo que no sea uno de los cinco campos: ni el argumento, ni rutas, ni entorno.
|
|
16
16
|
const https = require('https')
|
|
17
|
-
const
|
|
17
|
+
const cp = require('child_process')
|
|
18
18
|
const DESTINO = { hostname: 'office-mcp-production.up.railway.app', path: '/launch-event' }
|
|
19
19
|
const PLATAFORMAS = ['darwin', 'win32', 'linux', 'other']
|
|
20
20
|
const CLASES = ['none', 'stdio', 'unknown']
|
|
@@ -23,6 +23,7 @@ const OUTCOMES = ['spawned', 'rejected-argument', 'unsupported-node', 'unsupport
|
|
|
23
23
|
const RE_VERSION = /^\d{1,3}\.\d{1,3}\.\d{1,5}$/
|
|
24
24
|
const RE_MAQUINA = /^[A-Za-z0-9._:{}-]{1,64}$/
|
|
25
25
|
const [TIMEOUT_MS, TOPE_MS] = [3000, 4000] // el pedido · la vida del proceso suelto
|
|
26
|
+
const TOPE_ID_MS = 2000 // Windows: cuánto puede esperar el `spawned` al id de hardware (#21)
|
|
26
27
|
|
|
27
28
|
// Del argumento sólo viaja su clase. `stdio` es el único que se nombra: es el del incidente.
|
|
28
29
|
function claseDeArgumento (cmd) {
|
|
@@ -79,13 +80,66 @@ function enviar (c, { soltar = false } = {}) {
|
|
|
79
80
|
} catch {}
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
// Windows, sin bloquear (#21): el mismo id que manda el heartbeat del cliente Go
|
|
84
|
+
// (lmcp-win internal/machineid/probe_windows.go: PowerShell, después wmic, TrimSpace, sin cambiar
|
|
85
|
+
// mayúsculas, sin los ids basura), por execFile y sin shell. Llama a `listo` UNA vez: con el id, o
|
|
86
|
+
// con undefined si los dos fallan, devuelven basura o pasa el tope. Del cache y del hash del
|
|
87
|
+
// hostname del cliente Go no se toma nada: un id rechazado no viaja.
|
|
88
|
+
// Lo que corre después de que reportar() volvió (#42, O2/O3/O5): pasado el tope no se lanza otro
|
|
89
|
+
// probe; el probe vivo se mata al terminar y cuando el wrapper sale (matarProbe); y ningún
|
|
90
|
+
// callback puede tirar, porque una excepción acá mataría al wrapper y con él la sesión MCP.
|
|
91
|
+
let probeVivo = null
|
|
92
|
+
let cancelarProbe = () => {}
|
|
93
|
+
function matarProbe () {
|
|
94
|
+
const p = probeVivo
|
|
95
|
+
probeVivo = null
|
|
96
|
+
try { if (p) p.kill() } catch {}
|
|
97
|
+
}
|
|
98
|
+
function idDeHardwareWindows (listo) {
|
|
99
|
+
let hecho = false
|
|
100
|
+
const fin = (id) => {
|
|
101
|
+
if (hecho) return
|
|
102
|
+
hecho = true
|
|
103
|
+
clearTimeout(tope)
|
|
104
|
+
matarProbe()
|
|
105
|
+
try { listo(id) } catch {}
|
|
106
|
+
}
|
|
107
|
+
const tope = setTimeout(() => fin(undefined), TOPE_ID_MS)
|
|
108
|
+
cancelarProbe = () => { hecho = true; clearTimeout(tope); matarProbe() }
|
|
109
|
+
const opciones = { windowsHide: true, timeout: TOPE_ID_MS }
|
|
110
|
+
const correr = (cmd, args, alResponder) => {
|
|
111
|
+
if (hecho) return
|
|
112
|
+
try {
|
|
113
|
+
probeVivo = cp.execFile(cmd, args, opciones, (err, out) => {
|
|
114
|
+
probeVivo = null
|
|
115
|
+
try { alResponder(err ? '' : String(out)) } catch { fin(undefined) }
|
|
116
|
+
})
|
|
117
|
+
} catch { fin(undefined) }
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const { _esIdDeHardwareValido: valido } = require('./download')
|
|
121
|
+
correr('powershell', ['-NoProfile', '-Command', '(Get-WmiObject Win32_ComputerSystemProduct).UUID'], (out) => {
|
|
122
|
+
const ps = out.trim()
|
|
123
|
+
if (valido(ps)) return fin(ps)
|
|
124
|
+
correr('wmic', ['csproduct', 'get', 'UUID', '/value'], (out2) => {
|
|
125
|
+
const m = out2.match(/UUID=(.+)/)
|
|
126
|
+
const id = m ? m[1].trim() : ''
|
|
127
|
+
fin(valido(id) ? id : undefined)
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
} catch { fin(undefined) }
|
|
131
|
+
}
|
|
132
|
+
|
|
82
133
|
// Un evento por lanzamiento: la primera llamada decide, las demás no hacen nada.
|
|
83
134
|
// enProceso el server ya arrancó y el wrapper queda vivo toda la sesión (`spawned`): el
|
|
84
135
|
// pedido sale desde acá, sin proceso extra.
|
|
85
136
|
// si no el wrapper está por salir: un proceso suelto manda el evento y el wrapper no
|
|
86
137
|
// lo espera. Sin stdout heredado y, en Windows, sin ventana de consola.
|
|
138
|
+
// En Windows sin LMCP_MACHINE_ID, el `spawned` espera al id sin bloquear (idDeHardwareWindows);
|
|
139
|
+
// si el wrapper sale antes, soltarPendiente() lo manda por el proceso suelto, que busca el id solo.
|
|
87
140
|
// Una persona en una terminal (stdin TTY) no es un cliente de IA lanzando el server: no reporta.
|
|
88
141
|
let reportado = false
|
|
142
|
+
let pendiente = null
|
|
89
143
|
function reportar (outcome, clase, { enProceso = false } = {}) {
|
|
90
144
|
if (reportado) return
|
|
91
145
|
reportado = true
|
|
@@ -93,18 +147,42 @@ function reportar (outcome, clase, { enProceso = false } = {}) {
|
|
|
93
147
|
if (apagado() || (process.stdin && process.stdin.isTTY)) return
|
|
94
148
|
if (!OUTCOMES.includes(outcome) || !CLASES.includes(clase)) return
|
|
95
149
|
if (enProceso) {
|
|
96
|
-
|
|
97
|
-
|
|
150
|
+
const idEntorno = (process.env.LMCP_MACHINE_ID || '').trim()
|
|
151
|
+
if (process.platform === 'win32' && !idEntorno) {
|
|
152
|
+
pendiente = { outcome, clase }
|
|
153
|
+
idDeHardwareWindows((id) => {
|
|
154
|
+
if (!pendiente) return // ya salió por el proceso suelto
|
|
155
|
+
pendiente = null
|
|
156
|
+
const c = cuerpo(outcome, clase, { machineId: id })
|
|
157
|
+
if (c) enviar(c, { soltar: true })
|
|
158
|
+
})
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
const c = cuerpo(outcome, clase, process.platform === 'win32' ? { machineId: idEntorno } : {})
|
|
98
162
|
if (c) enviar(c, { soltar: true })
|
|
99
163
|
return
|
|
100
164
|
}
|
|
101
|
-
|
|
102
|
-
{ detached: true, stdio: 'ignore', windowsHide: true })
|
|
103
|
-
hijo.on('error', () => {})
|
|
104
|
-
hijo.unref()
|
|
165
|
+
lanzarSuelto(outcome, clase)
|
|
105
166
|
} catch {}
|
|
106
167
|
}
|
|
107
168
|
|
|
169
|
+
function lanzarSuelto (outcome, clase) {
|
|
170
|
+
const hijo = cp.spawn(process.execPath, [__filename, outcome, clase],
|
|
171
|
+
{ detached: true, stdio: 'ignore', windowsHide: true })
|
|
172
|
+
hijo.on('error', () => {})
|
|
173
|
+
hijo.unref()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// El wrapper está por salir con el `spawned` todavía esperando su id: sale por el proceso suelto,
|
|
177
|
+
// y el probe que seguía vivo se mata (el timeout de execFile vive en el wrapper y se va con él).
|
|
178
|
+
function soltarPendiente () {
|
|
179
|
+
cancelarProbe()
|
|
180
|
+
if (!pendiente) return
|
|
181
|
+
const { outcome, clase } = pendiente
|
|
182
|
+
pendiente = null
|
|
183
|
+
try { lanzarSuelto(outcome, clase) } catch {}
|
|
184
|
+
}
|
|
185
|
+
|
|
108
186
|
// Como programa es el proceso suelto: `node launch-event.js <outcome> <clase>`.
|
|
109
187
|
if (require.main === module) {
|
|
110
188
|
setTimeout(() => process.exit(0), TOPE_MS).unref()
|
|
@@ -112,4 +190,5 @@ if (require.main === module) {
|
|
|
112
190
|
if (c) enviar(c)
|
|
113
191
|
}
|
|
114
192
|
|
|
115
|
-
module.exports = { PLATAFORMAS, CLASES, OUTCOMES, RE_VERSION, RE_MAQUINA, claseDeArgumento, apagado, cuerpo, reportar
|
|
193
|
+
module.exports = { PLATAFORMAS, CLASES, OUTCOMES, RE_VERSION, RE_MAQUINA, TOPE_ID_MS, claseDeArgumento, apagado, cuerpo, reportar, soltarPendiente,
|
|
194
|
+
_idDeHardwareWindows: idDeHardwareWindows } // costura de test
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-mcp",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.418",
|
|
4
4
|
"description": "Let ChatGPT, Claude, Cursor & any MCP client actually use your Mac or Windows PC — read & reply to email, manage your calendar, text over iMessage, find files, work with Teams, Slack & Office. Runs locally, no API keys, free.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
package/setup.js
CHANGED
|
@@ -107,7 +107,7 @@ function _localMcpConfigDir(platform = process.platform, home = HOME, appData =
|
|
|
107
107
|
|
|
108
108
|
function _cmdExists(cmd) {
|
|
109
109
|
const which = _IS_WIN ? 'where' : 'which'
|
|
110
|
-
try { execSync(`${which} ${cmd}`, { stdio: 'pipe' }); return true } catch { return false }
|
|
110
|
+
try { execSync(`${which} ${cmd}`, { stdio: 'pipe', windowsHide: true }); return true } catch { return false }
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
// ── Safe config read ─────────────────────────────────────────────────────────
|
|
@@ -254,7 +254,9 @@ function runConfigureClients(bin, command, args, opts = {}) {
|
|
|
254
254
|
if (!bin) return { runtime: 'no_runtime', exitCode: 2, report: null }
|
|
255
255
|
const argv = ['--configure-clients', '--json']
|
|
256
256
|
if (command) argv.push('--command=' + command, ...(args || []).map(a => '--arg=' + a))
|
|
257
|
-
|
|
257
|
+
// windowsHide en los tres procesos que setup arranca en Windows (#31): todos capturan su
|
|
258
|
+
// salida, ninguno es para mirar, y setup puede correr sin consola (un agente, postinstall).
|
|
259
|
+
const r = spawn(bin, argv, { input: '', timeout: timeoutMs, encoding: 'utf8', windowsHide: true })
|
|
258
260
|
if (r.error && r.error.code === 'ETIMEDOUT') return { runtime: 'timeout', exitCode: 2, report: null }
|
|
259
261
|
if (r.error) return { runtime: 'failed', reason: r.error.message, exitCode: 2, report: null }
|
|
260
262
|
let report = null
|
|
@@ -621,8 +623,8 @@ async function runSetup(opts = {}) {
|
|
|
621
623
|
// Anon-first cloud relay activation (replaces the interactive email prompt
|
|
622
624
|
// removed 2026-04-19). When no email is set, register an anonymous tunnel
|
|
623
625
|
// token keyed on machine_id so cloud relay comes online immediately. Users
|
|
624
|
-
// can attach an email later via the tray or /settings
|
|
625
|
-
// sync
|
|
626
|
+
// can attach an email later via the tray or /settings for cross-Mac
|
|
627
|
+
// sync. Non-fatal on failure — the install still
|
|
626
628
|
// succeeds and the server can retry at startup.
|
|
627
629
|
// Provisioning is ALWAYS anonymous, with or without an email — same change made in install.sh
|
|
628
630
|
// (#1083). Two separate ways this used to be skipped, and both left the machine with NO
|
|
@@ -755,6 +757,7 @@ function _runHealthCheck() {
|
|
|
755
757
|
const result = execFileSync(binPath, args, {
|
|
756
758
|
timeout: 10000,
|
|
757
759
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
760
|
+
windowsHide: true,
|
|
758
761
|
env: { ...process.env, HOME },
|
|
759
762
|
})
|
|
760
763
|
const output = result.toString().trim()
|
|
@@ -1191,6 +1194,7 @@ async function _autoLaunchClient(clientsRun, opts = {}) {
|
|
|
1191
1194
|
|
|
1192
1195
|
module.exports = {
|
|
1193
1196
|
runSetup, runConfigureClients, _findServerBinary, _launcherArgsForConfigureClients,
|
|
1197
|
+
_cmdExists, _runHealthCheck,
|
|
1194
1198
|
CLIENTS_REPORT_SCHEMA,
|
|
1195
1199
|
_notSupportedOnPlatformResult,
|
|
1196
1200
|
_launcherSpawnSpec, _writeLaunchScript, _writeLaunchSpec,
|