local-mcp 3.0.416 → 3.0.417
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/README.md +2 -2
- package/download.js +11 -13
- package/index.js +50 -12
- package/launch-event.js +115 -0
- package/package.json +4 -3
- package/setup.js +27 -19
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://local-mcp.com?ref=npm)
|
|
7
7
|
[](https://local-mcp.com?ref=npm)
|
|
8
8
|
[](https://local-mcp.com/en/privacy)
|
|
9
|
-
[](https://glama.ai/mcp/servers/colibird-ai/local-mcp-releases?ref=npm)
|
|
10
10
|
|
|
11
11
|
**Runs on your computer. No tokens. No API keys. Free.**
|
|
12
12
|
|
|
@@ -203,4 +203,4 @@ Stops all background processes, removes the LaunchAgent, deletes the app and bin
|
|
|
203
203
|
|
|
204
204
|
## Links
|
|
205
205
|
|
|
206
|
-
[Website](https://local-mcp.com?ref=npm) · [GitHub](https://github.com/
|
|
206
|
+
[Website](https://local-mcp.com?ref=npm) · [GitHub](https://github.com/colibird-ai/local-mcp-releases) · [Guides](https://local-mcp.com/guides) · Support: ctpo@colibird.co
|
package/download.js
CHANGED
|
@@ -31,6 +31,7 @@ function extractTar(tarPath, destDir) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
const BACKEND_URL = 'https://office-mcp-production.up.railway.app'
|
|
34
|
+
const LINUX_NO_SOPORTADO = 'Local MCP does not support Linux. It runs on macOS and Windows — https://local-mcp.com'
|
|
34
35
|
|
|
35
36
|
// Platform-aware cache directory:
|
|
36
37
|
// macOS: ~/.local/share/local-mcp/bin
|
|
@@ -178,7 +179,7 @@ function _getMachineId(deps = {}) {
|
|
|
178
179
|
} else if (platform === 'win32') {
|
|
179
180
|
const hw = _probeWinHardwareId(execSync)
|
|
180
181
|
if (hw) {
|
|
181
|
-
_writeWinMachineIdCache(cacheDir, hw)
|
|
182
|
+
if (!deps.soloLectura) _writeWinMachineIdCache(cacheDir, hw)
|
|
182
183
|
return hw
|
|
183
184
|
}
|
|
184
185
|
// Both probes failed or are blocked — fall back to whatever the go-server
|
|
@@ -193,6 +194,10 @@ function _getMachineId(deps = {}) {
|
|
|
193
194
|
if (mid) return mid
|
|
194
195
|
} catch {}
|
|
195
196
|
}
|
|
197
|
+
// El evento de lanzamiento (launch-event.js) pide soloLectura: no escribe el cache de arriba —
|
|
198
|
+
// en una máquina desinstalada recrearía el directorio que el usuario borró — y prefiere no
|
|
199
|
+
// tener id antes que uno derivado del hostname.
|
|
200
|
+
if (deps.soloLectura) return ''
|
|
196
201
|
// Fallback: hash hostname instead of sending raw PII
|
|
197
202
|
const crypto = require('crypto')
|
|
198
203
|
return crypto.createHash('sha256').update(os.hostname()).digest('hex').slice(0, 16)
|
|
@@ -331,6 +336,10 @@ async function downloadFile(url, destPath) {
|
|
|
331
336
|
* @returns {Promise<{binPath: string}>}
|
|
332
337
|
*/
|
|
333
338
|
async function ensureBinary(deps = {}) {
|
|
339
|
+
// Linux no está soportado (decisión de Dario, 2026-10-02; colibird-ai/lmcp-npm#9). Se dice
|
|
340
|
+
// ANTES de pedir nada: ni /runtime/latest ni un tarball. Anuncie lo que anuncie el API — llegó a
|
|
341
|
+
// anunciar URLs `linux_*` que daban 404, y la guarda vieja (sólo «si no hay URL») no saltaba.
|
|
342
|
+
if ((deps.platform || process.platform) === 'linux') throw new Error(LINUX_NO_SOPORTADO)
|
|
334
343
|
// Seam for tests (same idiom as ensureTray's `deps`, #2654): lets a test pin the
|
|
335
344
|
// platform and stub the network without touching process.platform or real HOME.
|
|
336
345
|
const {
|
|
@@ -361,17 +370,6 @@ async function ensureBinary(deps = {}) {
|
|
|
361
370
|
const archKey = arch.replace('-', '_') // windows-amd64 → windows_amd64
|
|
362
371
|
const publishedUrl = (info[archKey] && info[archKey].startsWith('http')) ? info[archKey] : null
|
|
363
372
|
|
|
364
|
-
// Linux has no published artifact today — unlike Windows, where windows_amd64
|
|
365
|
-
// IS published — so composing the fallback URL below only ever 404s: 817
|
|
366
|
-
// requests/week of dead traffic from MCP sandboxes testing the npm package
|
|
367
|
-
// (#3794). Fail before any network call instead of catching the 404 after.
|
|
368
|
-
if (platform === 'linux' && !publishedUrl) {
|
|
369
|
-
throw new Error(
|
|
370
|
-
'Local MCP does not support Linux yet.\n' +
|
|
371
|
-
' Check https://local-mcp.com for updates.'
|
|
372
|
-
)
|
|
373
|
-
}
|
|
374
|
-
|
|
375
373
|
let url, platformVersion
|
|
376
374
|
if (publishedUrl) {
|
|
377
375
|
platformVersion = versionFromArtifactUrl(publishedUrl, arch)
|
|
@@ -871,7 +869,7 @@ async function ensureSlackProxy(platform = process.platform) {
|
|
|
871
869
|
module.exports = {
|
|
872
870
|
ensureBinary, ensureRuntime, ensureTray, ensureTeamsProxy, ensureSlackProxy, versionFromArtifactUrl,
|
|
873
871
|
latestVersionForPlatform, getArch,
|
|
874
|
-
CACHE_DIR, TRAY_DIR, _getMachineId,
|
|
872
|
+
CACHE_DIR, TRAY_DIR, _getMachineId, LINUX_NO_SOPORTADO,
|
|
875
873
|
_probeWinHardwareId, _resetWinHardwareIdCacheForTests,
|
|
876
874
|
_readWinMachineIdCache, _writeWinMachineIdCache, WIN_MACHINE_ID_CACHE_NAME,
|
|
877
875
|
_retireSupersededTrays, _isUsableTrayBundle, SYSTEM_TRAY_APP,
|
package/index.js
CHANGED
|
@@ -17,6 +17,7 @@ const path = require('path')
|
|
|
17
17
|
const os = require('os')
|
|
18
18
|
const fs = require('fs')
|
|
19
19
|
const https = require('https')
|
|
20
|
+
const evento = require('./launch-event')
|
|
20
21
|
|
|
21
22
|
// ── Version + liveness helpers (module scope so they are testable) ────────────
|
|
22
23
|
|
|
@@ -232,12 +233,30 @@ function clearUninstallSentinels() {
|
|
|
232
233
|
for (const p of uninstallSentinelPaths()) { try { fs.unlinkSync(p) } catch {} }
|
|
233
234
|
}
|
|
234
235
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
//
|
|
236
|
+
const cmd = process.argv[2]
|
|
237
|
+
|
|
238
|
+
// Comandos que este wrapper entiende. Sin argumentos arranca el server MCP por stdio, que es
|
|
239
|
+
// como lo lanzan los clientes de IA — ese camino NO cambia.
|
|
240
|
+
const COMANDOS = ['setup', 'update', 'uninstall', 'status']
|
|
241
|
+
const PEDIDOS_DE_AYUDA = ['--help', '-h', 'help']
|
|
242
|
+
|
|
243
|
+
// Evento de lanzamiento (launch-event.js, #11): cada lanzamiento del camino del server reporta
|
|
244
|
+
// UNA vez cómo terminó. Un lanzamiento es: sin argumento, `stdio`, o un argumento desconocido.
|
|
245
|
+
// Los comandos y la ayuda no lo son y no reportan. El `exit` de abajo es la red: una salida de
|
|
246
|
+
// ese camino que nadie nombró —una excepción incluida— sale como `other`, nunca en silencio.
|
|
247
|
+
const ES_LANZAMIENTO = require.main === module && !COMANDOS.includes(cmd) && !PEDIDOS_DE_AYUDA.includes(cmd)
|
|
248
|
+
function reportarLanzamiento(outcome, opciones) {
|
|
249
|
+
if (ES_LANZAMIENTO) evento.reportar(outcome, evento.claseDeArgumento(cmd), opciones)
|
|
250
|
+
}
|
|
251
|
+
if (ES_LANZAMIENTO) process.on('exit', () => reportarLanzamiento('other'))
|
|
252
|
+
|
|
253
|
+
// Platform support: macOS (Swift tray + embedded server) and Windows (go-server standalone
|
|
254
|
+
// binary downloaded from R2). Linux passes this guard so that `--help`, `status`, `update` and
|
|
255
|
+
// `uninstall` answer, but it is NOT supported: the server path and `setup` say so (#9).
|
|
238
256
|
const SUPPORTED_PLATFORMS = ['darwin', 'win32', 'linux']
|
|
239
257
|
if (!SUPPORTED_PLATFORMS.includes(process.platform)) {
|
|
240
|
-
console.error(`LMCP supports macOS
|
|
258
|
+
console.error(`LMCP supports macOS and Windows. Your platform (${process.platform}) is not supported yet — see https://local-mcp.com`)
|
|
259
|
+
reportarLanzamiento('unsupported-platform')
|
|
241
260
|
process.exit(1)
|
|
242
261
|
}
|
|
243
262
|
|
|
@@ -250,13 +269,12 @@ if (parseInt(process.version.slice(1)) < 16) {
|
|
|
250
269
|
`If you use nvm, run: nvm alias default 22 && nvm uninstall ${process.version}\n` +
|
|
251
270
|
'Then restart Claude Desktop / Cursor.'
|
|
252
271
|
process.stderr.write(msg + '\n')
|
|
272
|
+
reportarLanzamiento('unsupported-node')
|
|
253
273
|
// Exit with a non-zero code so the MCP host marks the server as failed
|
|
254
274
|
// (vs hanging forever with no output).
|
|
255
275
|
process.exit(1)
|
|
256
276
|
}
|
|
257
277
|
|
|
258
|
-
const cmd = process.argv[2]
|
|
259
|
-
|
|
260
278
|
// Detect a MANUAL uninstall — the user dragged LocalMCPTray.app to the Trash (by far the
|
|
261
279
|
// most common way to remove a Mac app) instead of running `npx local-mcp uninstall` or the
|
|
262
280
|
// tray's Uninstall button, so no `.uninstalled` sentinel was written. Signature: the tray
|
|
@@ -279,15 +297,18 @@ function detectManualUninstall() {
|
|
|
279
297
|
)
|
|
280
298
|
}
|
|
281
299
|
|
|
282
|
-
//
|
|
283
|
-
//
|
|
284
|
-
|
|
300
|
+
// `stdio` no es un comando: es el modo por defecto dicho en voz alta. Las guías públicas
|
|
301
|
+
// mandaron a escribir `"args": ["-y", "local-mcp@latest", "stdio"]` en la config manual de un
|
|
302
|
+
// cliente de IA, y esas configs siguen en máquinas de usuarios. Tratarlo como desconocido
|
|
303
|
+
// (3.0.413 a 3.0.416) las rompió: ayuda por stdout y exit 2 donde el cliente espera JSON-RPC.
|
|
304
|
+
const SINONIMOS_DEL_SERVER = ['stdio']
|
|
285
305
|
|
|
286
306
|
function mostrarAyuda() {
|
|
287
307
|
console.log(`local-mcp — Local MCP Server
|
|
288
308
|
|
|
289
309
|
Uso:
|
|
290
310
|
local-mcp arranca el server MCP por stdio (lo que hace un cliente de IA)
|
|
311
|
+
local-mcp stdio lo mismo, dicho explícitamente
|
|
291
312
|
local-mcp setup instala y configura
|
|
292
313
|
local-mcp status muestra el estado
|
|
293
314
|
local-mcp update limpia el cache del runtime
|
|
@@ -303,11 +324,12 @@ async function main() {
|
|
|
303
324
|
// sólo buscaba ayuda de CLI. Un comando de consulta no instala software.
|
|
304
325
|
//
|
|
305
326
|
// Sin argumentos sigue arrancando el server, que es como lo invocan los clientes de IA.
|
|
306
|
-
if (cmd !== undefined && !COMANDOS.includes(cmd)) {
|
|
307
|
-
const pedidoDeAyuda =
|
|
327
|
+
if (cmd !== undefined && !COMANDOS.includes(cmd) && !SINONIMOS_DEL_SERVER.includes(cmd)) {
|
|
328
|
+
const pedidoDeAyuda = PEDIDOS_DE_AYUDA.includes(cmd)
|
|
308
329
|
mostrarAyuda()
|
|
309
330
|
if (!pedidoDeAyuda) {
|
|
310
331
|
console.error(`\nNo conozco el comando "${cmd}".`)
|
|
332
|
+
reportarLanzamiento('rejected-argument')
|
|
311
333
|
}
|
|
312
334
|
process.exit(pedidoDeAyuda ? 0 : 2)
|
|
313
335
|
}
|
|
@@ -317,7 +339,8 @@ async function main() {
|
|
|
317
339
|
clearUninstallSentinels()
|
|
318
340
|
const { runSetup } = require('./setup')
|
|
319
341
|
await runSetup()
|
|
320
|
-
|
|
342
|
+
// En Linux el setup no instaló nada (#9): quien lo encadena en un script tiene que verlo.
|
|
343
|
+
process.exit(process.platform === 'linux' ? 1 : 0)
|
|
321
344
|
}
|
|
322
345
|
|
|
323
346
|
if (cmd === 'update') {
|
|
@@ -402,6 +425,7 @@ async function main() {
|
|
|
402
425
|
// runtime-dir and durable App Support copies (see uninstallSentinelPaths).
|
|
403
426
|
{
|
|
404
427
|
if (anyUninstallSentinel()) {
|
|
428
|
+
reportarLanzamiento('uninstalled')
|
|
405
429
|
process.stderr.write(
|
|
406
430
|
'[LMCP] Local MCP was uninstalled — not starting.\n' +
|
|
407
431
|
' Reinstall: npx local-mcp setup\n' +
|
|
@@ -421,6 +445,7 @@ async function main() {
|
|
|
421
445
|
// and we short-circuit here, which is harmless.
|
|
422
446
|
{
|
|
423
447
|
if (detectManualUninstall()) {
|
|
448
|
+
reportarLanzamiento('uninstalled')
|
|
424
449
|
writeUninstallSentinels()
|
|
425
450
|
const trayPlist = path.join(os.homedir(), 'Library', 'LaunchAgents', 'com.local-mcp.tray.plist')
|
|
426
451
|
if (fs.existsSync(trayPlist)) {
|
|
@@ -435,6 +460,15 @@ async function main() {
|
|
|
435
460
|
}
|
|
436
461
|
}
|
|
437
462
|
|
|
463
|
+
// ── Linux no está soportado (#9) ────────────────────────────────────────────
|
|
464
|
+
// Se dice acá, antes de cualquier descarga: sin esto el wrapper pedía un binario que no se
|
|
465
|
+
// publica y terminaba en un 404. Va después de los chequeos de desinstalación, que no bajan nada.
|
|
466
|
+
if (process.platform === 'linux') {
|
|
467
|
+
process.stderr.write(require('./download').LINUX_NO_SOPORTADO + '\n')
|
|
468
|
+
reportarLanzamiento('unsupported-platform')
|
|
469
|
+
process.exit(1)
|
|
470
|
+
}
|
|
471
|
+
|
|
438
472
|
// ── Modo default: stdio MCP server ──────────────────────────────────────────
|
|
439
473
|
const { CACHE_DIR } = require('./download')
|
|
440
474
|
// macOS ships the Swift `local-mcp-server` (standalone fallback to the embedded
|
|
@@ -526,7 +560,10 @@ async function main() {
|
|
|
526
560
|
try { _pkgVer = require('./package.json').version || '' } catch {}
|
|
527
561
|
const child = spawn(binaryPath, spawnArgs, { stdio: 'inherit', env: { ...process.env, LMCP_NPM_VERSION: _pkgVer } })
|
|
528
562
|
noteSpawnedServer(child.pid)
|
|
563
|
+
// El server ya arrancó y este proceso queda vivo toda la sesión: el evento sale desde acá.
|
|
564
|
+
child.on('spawn', () => reportarLanzamiento('spawned', { enProceso: true }))
|
|
529
565
|
child.on('error', (err) => {
|
|
566
|
+
reportarLanzamiento('spawn-failed')
|
|
530
567
|
process.stderr.write(`Error ejecutando LMCP: ${err.message}\n`)
|
|
531
568
|
process.exit(1)
|
|
532
569
|
})
|
|
@@ -667,6 +704,7 @@ async function main() {
|
|
|
667
704
|
try {
|
|
668
705
|
runtime = await ensureRuntime()
|
|
669
706
|
} catch (err) {
|
|
707
|
+
reportarLanzamiento('download-failed')
|
|
670
708
|
process.stderr.write(`\nError al preparar el runtime de LMCP:\n${err.message}\n`)
|
|
671
709
|
process.stderr.write(`Intentá reinstalar: npx local-mcp update\n\n`)
|
|
672
710
|
process.exit(1)
|
package/launch-event.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict'
|
|
3
|
+
// launch-event.js — el evento de lanzamiento del wrapper (colibird-ai/lmcp-npm#11, parte C).
|
|
4
|
+
//
|
|
5
|
+
// Por qué existe: de 3.0.413 a 3.0.416 el wrapper rechazó `stdio` y ninguna config manual
|
|
6
|
+
// conectaba. Nadie lo vio en diez días: el wrapper salía antes de que existiera telemetría.
|
|
7
|
+
//
|
|
8
|
+
// Contrato: contracts/launch-telemetry.v1.json de lmcp-api (copia fijada en npm/contracts/, que
|
|
9
|
+
// NO se publica: por eso los conjuntos están acá como constantes, y un test los compara).
|
|
10
|
+
//
|
|
11
|
+
// Corre en cada arranque de cada cliente de IA. Lo que no puede pasar nunca:
|
|
12
|
+
// · escribir en stdout — es el protocolo;
|
|
13
|
+
// · demorar el arranque del server o una salida — sin reintentos, timeout corto, todo error
|
|
14
|
+
// tragado;
|
|
15
|
+
// · mandar algo que no sea uno de los cinco campos: ni el argumento, ni rutas, ni entorno.
|
|
16
|
+
const https = require('https')
|
|
17
|
+
const { spawn } = require('child_process')
|
|
18
|
+
const DESTINO = { hostname: 'office-mcp-production.up.railway.app', path: '/launch-event' }
|
|
19
|
+
const PLATAFORMAS = ['darwin', 'win32', 'linux', 'other']
|
|
20
|
+
const CLASES = ['none', 'stdio', 'unknown']
|
|
21
|
+
const OUTCOMES = ['spawned', 'rejected-argument', 'unsupported-node', 'unsupported-platform',
|
|
22
|
+
'download-failed', 'spawn-failed', 'uninstalled', 'other']
|
|
23
|
+
const RE_VERSION = /^\d{1,3}\.\d{1,3}\.\d{1,5}$/
|
|
24
|
+
const RE_MAQUINA = /^[A-Za-z0-9._:{}-]{1,64}$/
|
|
25
|
+
const [TIMEOUT_MS, TOPE_MS] = [3000, 4000] // el pedido · la vida del proceso suelto
|
|
26
|
+
|
|
27
|
+
// Del argumento sólo viaja su clase. `stdio` es el único que se nombra: es el del incidente.
|
|
28
|
+
function claseDeArgumento (cmd) {
|
|
29
|
+
if (cmd === undefined) return 'none'
|
|
30
|
+
return cmd === 'stdio' ? 'stdio' : 'unknown'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// El heartbeat no tiene compuerta de consentimiento (ver #11). Este evento respeta además el
|
|
34
|
+
// único interruptor que ya existe en el server de Mac.
|
|
35
|
+
function apagado (env = process.env) {
|
|
36
|
+
return ['1', 'true', 'yes'].includes(String(env.LMCP_DISABLE_TELEMETRY || '').toLowerCase())
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// El id que usa el heartbeat, sin escribir nada y sin el fallback del hostname. Lo que no
|
|
40
|
+
// calza con el patrón del contrato (una ruta en LMCP_MACHINE_ID, por ejemplo) no viaja.
|
|
41
|
+
function idDeMaquina () {
|
|
42
|
+
try {
|
|
43
|
+
const id = require('./download')._getMachineId({ soloLectura: true })
|
|
44
|
+
return RE_MAQUINA.test(id) ? id : undefined
|
|
45
|
+
} catch { return undefined }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// null = no se manda. Sólo salen valores de los conjuntos del contrato.
|
|
49
|
+
function cuerpo (outcome, clase, deps = {}) {
|
|
50
|
+
const version = 'version' in deps ? deps.version : require('./package.json').version
|
|
51
|
+
const plataforma = deps.platform || process.platform
|
|
52
|
+
if (!OUTCOMES.includes(outcome) || !CLASES.includes(clase) || !RE_VERSION.test(version)) return null
|
|
53
|
+
const c = {
|
|
54
|
+
package_version: version,
|
|
55
|
+
platform: PLATAFORMAS.includes(plataforma) ? plataforma : 'other',
|
|
56
|
+
arg_class: clase,
|
|
57
|
+
outcome
|
|
58
|
+
}
|
|
59
|
+
const id = 'machineId' in deps ? deps.machineId : idDeMaquina()
|
|
60
|
+
if (typeof id === 'string' && RE_MAQUINA.test(id)) c.machine_id = id
|
|
61
|
+
return c
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Un POST, sin reintento, respuesta ignorada. `soltar` es para el pedido que sale desde el
|
|
65
|
+
// wrapper vivo: el socket no retiene al proceso.
|
|
66
|
+
function enviar (c, { soltar = false } = {}) {
|
|
67
|
+
try {
|
|
68
|
+
const datos = JSON.stringify(c)
|
|
69
|
+
const req = https.request({
|
|
70
|
+
...DESTINO,
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(datos) },
|
|
73
|
+
timeout: TIMEOUT_MS
|
|
74
|
+
}, (res) => res.resume())
|
|
75
|
+
req.on('error', () => {})
|
|
76
|
+
req.on('timeout', () => { try { req.destroy() } catch {} })
|
|
77
|
+
if (soltar) req.on('socket', (s) => { try { s.unref() } catch {} })
|
|
78
|
+
req.end(datos)
|
|
79
|
+
} catch {}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Un evento por lanzamiento: la primera llamada decide, las demás no hacen nada.
|
|
83
|
+
// enProceso el server ya arrancó y el wrapper queda vivo toda la sesión (`spawned`): el
|
|
84
|
+
// pedido sale desde acá, sin proceso extra.
|
|
85
|
+
// si no el wrapper está por salir: un proceso suelto manda el evento y el wrapper no
|
|
86
|
+
// lo espera. Sin stdout heredado y, en Windows, sin ventana de consola.
|
|
87
|
+
// Una persona en una terminal (stdin TTY) no es un cliente de IA lanzando el server: no reporta.
|
|
88
|
+
let reportado = false
|
|
89
|
+
function reportar (outcome, clase, { enProceso = false } = {}) {
|
|
90
|
+
if (reportado) return
|
|
91
|
+
reportado = true
|
|
92
|
+
try {
|
|
93
|
+
if (apagado() || (process.stdin && process.stdin.isTTY)) return
|
|
94
|
+
if (!OUTCOMES.includes(outcome) || !CLASES.includes(clase)) return
|
|
95
|
+
if (enProceso) {
|
|
96
|
+
// Windows: el id sale de un PowerShell, que acá no se lanza. Sin id, ese `spawned` no se une a un heartbeat.
|
|
97
|
+
const c = cuerpo(outcome, clase, process.platform === 'win32' ? { machineId: process.env.LMCP_MACHINE_ID } : {})
|
|
98
|
+
if (c) enviar(c, { soltar: true })
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
const hijo = spawn(process.execPath, [__filename, outcome, clase],
|
|
102
|
+
{ detached: true, stdio: 'ignore', windowsHide: true })
|
|
103
|
+
hijo.on('error', () => {})
|
|
104
|
+
hijo.unref()
|
|
105
|
+
} catch {}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Como programa es el proceso suelto: `node launch-event.js <outcome> <clase>`.
|
|
109
|
+
if (require.main === module) {
|
|
110
|
+
setTimeout(() => process.exit(0), TOPE_MS).unref()
|
|
111
|
+
const c = apagado() ? null : cuerpo(process.argv[2], process.argv[3])
|
|
112
|
+
if (c) enviar(c)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = { PLATAFORMAS, CLASES, OUTCOMES, RE_VERSION, RE_MAQUINA, claseDeArgumento, apagado, cuerpo, reportar }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-mcp",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.417",
|
|
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": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"index.js",
|
|
15
15
|
"download.js",
|
|
16
16
|
"setup.js",
|
|
17
|
+
"launch-event.js",
|
|
17
18
|
"postinstall.js",
|
|
18
19
|
"README.md"
|
|
19
20
|
],
|
|
@@ -68,10 +69,10 @@
|
|
|
68
69
|
"homepage": "https://local-mcp.com",
|
|
69
70
|
"repository": {
|
|
70
71
|
"type": "git",
|
|
71
|
-
"url": "https://github.com/
|
|
72
|
+
"url": "https://github.com/colibird-ai/local-mcp-releases.git"
|
|
72
73
|
},
|
|
73
74
|
"bugs": {
|
|
74
|
-
"url": "https://github.com/
|
|
75
|
+
"url": "https://github.com/colibird-ai/local-mcp-releases/issues"
|
|
75
76
|
},
|
|
76
77
|
"mcpName": "com.local-mcp/local-mcp"
|
|
77
78
|
}
|
package/setup.js
CHANGED
|
@@ -237,11 +237,12 @@ function _launcherArgsForConfigureClients(platform, command, args) {
|
|
|
237
237
|
return platform === 'darwin' ? { command, args } : { command: null, args: [] }
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
// §3.8 — Linux's fixed result shape: no capability, no report, exit 0
|
|
241
|
-
//
|
|
242
|
-
//
|
|
240
|
+
// §3.8 — Linux's fixed result shape: no capability, no report, exit 1. It said exit 0 while
|
|
241
|
+
// Linux was "no writer yet"; since #9 Linux is not supported, and a setup that installed nothing
|
|
242
|
+
// is a failed setup: a script that chains on it has to see it. Extracted so the contract itself
|
|
243
|
+
// is directly testable instead of only reachable by reading runSetup's source.
|
|
243
244
|
function _notSupportedOnPlatformResult() {
|
|
244
|
-
return { runtime: 'not_supported_on_platform', exitCode:
|
|
245
|
+
return { runtime: 'not_supported_on_platform', exitCode: 1, report: null }
|
|
245
246
|
}
|
|
246
247
|
|
|
247
248
|
// Corre `--configure-clients --json`. Nunca cuelga el instalador: un binario que no responde
|
|
@@ -296,10 +297,27 @@ async function runSetup(opts = {}) {
|
|
|
296
297
|
// #3212 — `--json`: el reporte estructurado va solo por stdout; todo lo demás, a stderr.
|
|
297
298
|
const jsonMode = !!opts.json || process.argv.includes('--json')
|
|
298
299
|
if (jsonMode) console.log = (...a) => { process.stderr.write(a.join(' ') + '\n') }
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
|
|
300
|
+
// Linux no está soportado (#9): se dice y no se toca nada — sin descarga, sin launcher, sin
|
|
301
|
+
// eventos de instalación ni heartbeat (antes terminaba en «LMCP is installed and running»
|
|
302
|
+
// después de un 404). El reporte --json conserva su forma (§3.8); el código de salida es 1.
|
|
303
|
+
if (process.platform === 'linux') {
|
|
304
|
+
const sinLinux = _notSupportedOnPlatformResult()
|
|
305
|
+
console.log('\n ' + require('./download').LINUX_NO_SOPORTADO + '\n Nothing was installed.\n')
|
|
306
|
+
if (jsonMode) {
|
|
307
|
+
process.stdout.write(JSON.stringify({
|
|
308
|
+
schema: CLIENTS_REPORT_SCHEMA,
|
|
309
|
+
installer_version: require('./package.json').version,
|
|
310
|
+
runtime: sinLinux.runtime,
|
|
311
|
+
catalog: null,
|
|
312
|
+
clients: [],
|
|
313
|
+
permissions: 'unknown_until_tray_runs',
|
|
314
|
+
}) + '\n')
|
|
315
|
+
}
|
|
316
|
+
process.exitCode = sinLinux.exitCode
|
|
317
|
+
return
|
|
318
|
+
}
|
|
319
|
+
// Cross-platform: macOS configures the Swift tray + embedded server; Windows downloads the
|
|
320
|
+
// go-server standalone binary (download.js) and writes the MCP client config.
|
|
303
321
|
const forceAll = opts.all || process.argv.includes('--all')
|
|
304
322
|
const refArg = process.argv.find(a => a.startsWith('--ref='))
|
|
305
323
|
if (refArg) process.env.LMCP_REF = refArg.split('=')[1]
|
|
@@ -469,16 +487,6 @@ async function runSetup(opts = {}) {
|
|
|
469
487
|
console.log(' npx local-mcp setup\n')
|
|
470
488
|
console.log(' Full setup guide: https://local-mcp.com/setup')
|
|
471
489
|
}
|
|
472
|
-
} else {
|
|
473
|
-
// Linux (§3.8): no runtime-native config writer exists yet. Say so plainly,
|
|
474
|
-
// point at the manual guide, and touch nothing — no per-client guessing,
|
|
475
|
-
// no files written, no client identity hardcoded here.
|
|
476
|
-
clientsRun = _notSupportedOnPlatformResult()
|
|
477
|
-
_trackSetupStep('detect', 'not_supported_on_platform', '', { npmVersion: _npmPkgVersion })
|
|
478
|
-
console.log('\n LMCP is installed and running.')
|
|
479
|
-
console.log(' Automatic AI-client configuration is not available on Linux yet.')
|
|
480
|
-
console.log(' Add LMCP to your AI client\'s MCP config manually — see:')
|
|
481
|
-
console.log(' https://local-mcp.com/setup\n')
|
|
482
490
|
}
|
|
483
491
|
|
|
484
492
|
const cfgDir = _localMcpConfigDir()
|
|
@@ -678,7 +686,7 @@ async function runSetup(opts = {}) {
|
|
|
678
686
|
|
|
679
687
|
// #3212 — el reporte para quien instaló sin mirar (un agente): qué quedó hecho y qué espera
|
|
680
688
|
// a la persona. Los permisos, sólo si los midió el tray. clientsRun is always set by this
|
|
681
|
-
// point — darwin/win32 from runConfigureClients
|
|
689
|
+
// point — darwin/win32 from runConfigureClients (linux returned at the top, #9).
|
|
682
690
|
if (jsonMode) {
|
|
683
691
|
const rep = clientsRun.report || {}
|
|
684
692
|
const out = {
|