local-mcp 3.0.226 → 3.0.228

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 (3) hide show
  1. package/download.js +1 -145
  2. package/index.js +1 -3
  3. package/package.json +1 -1
package/download.js CHANGED
@@ -493,148 +493,4 @@ async function ensureSlackProxy() {
493
493
  }
494
494
  }
495
495
 
496
- /**
497
- * Downloads local-mcp-helper on first install only.
498
- * CRITICAL: never replaces an existing helper binary — the helper owns
499
- * Calendar/Contacts/Reminders TCC grants via its cdhash. Replacing the
500
- * binary silently resets those grants and users would have to re-authorize.
501
- * @returns {Promise<string|null>} Path to the binary, or null if it fails
502
- */
503
- async function ensureHelper() {
504
- const binPath = path.join(CACHE_DIR, 'local-mcp-helper')
505
-
506
- // Already installed — never replace. Stable cdhash = stable TCC grants.
507
- if (fs.existsSync(binPath)) return binPath
508
-
509
- const info = await getLatestBinary()
510
- const version = info.version
511
- const arch = process.arch === 'arm64' ? 'darwin-arm64' : 'darwin-x64'
512
- const url = `https://download.local-mcp.com/local-mcp-helper-${version}-${arch}.tar.gz`
513
- process.stderr.write(`\nDownloading local-mcp-helper v${version}...\n`)
514
-
515
- fs.mkdirSync(CACHE_DIR, { recursive: true })
516
- const tarPath = path.join(CACHE_DIR, `local-mcp-helper-${version}.tar.gz`)
517
-
518
- try {
519
- await downloadFile(url, tarPath)
520
- const stat = fs.statSync(tarPath)
521
- if (stat.size < 1000) { fs.unlinkSync(tarPath); throw new Error(`Incomplete download (${stat.size} bytes)`) }
522
-
523
- extractTar(tarPath, CACHE_DIR)
524
- fs.unlinkSync(tarPath)
525
-
526
- if (!fs.existsSync(binPath)) throw new Error('local-mcp-helper not found after extraction')
527
- fs.chmodSync(binPath, 0o755)
528
- // Do NOT codesign: the pre-built binary's cdhash must remain exactly as
529
- // distributed. Codesigning a fresh binary produces the same cdhash only
530
- // when the content is identical, but we want the CI-signed cdhash to be
531
- // the canonical identity used in TCC grants.
532
-
533
- fs.writeFileSync(path.join(CACHE_DIR, '.helper-version'), version, 'utf8')
534
- process.stderr.write(` local-mcp-helper installed at ${binPath}\n`)
535
- return binPath
536
- } catch (err) {
537
- try { if (fs.existsSync(tarPath)) fs.unlinkSync(tarPath) } catch {}
538
- process.stderr.write(` (local-mcp-helper not available: ${err.message})\n`)
539
- return null
540
- }
541
- }
542
-
543
- /**
544
- * Downloads local-mcp-jxa-runner on first install only.
545
- * CRITICAL: never replaces an existing jxa-runner binary — the runner owns
546
- * Automation (JXA) TCC grants for Mail, Messages, Safari, etc. via its cdhash.
547
- * Replacing the binary silently resets those grants and users would have to
548
- * re-authorize in System Settings → Privacy & Security → Automation.
549
- * @returns {Promise<string|null>} Path to the binary, or null if it fails
550
- */
551
- async function ensureJXARunner() {
552
- const binPath = path.join(CACHE_DIR, 'local-mcp-jxa-runner')
553
-
554
- // Already installed — never replace. Stable cdhash = stable TCC grants.
555
- if (fs.existsSync(binPath)) return binPath
556
-
557
- const info = await getLatestBinary()
558
- const version = info.version
559
- const arch = process.arch === 'arm64' ? 'darwin-arm64' : 'darwin-x64'
560
- const url = `https://download.local-mcp.com/local-mcp-jxa-runner-${version}-${arch}.tar.gz`
561
- process.stderr.write(`\nDownloading local-mcp-jxa-runner v${version}...\n`)
562
-
563
- fs.mkdirSync(CACHE_DIR, { recursive: true })
564
- const tarPath = path.join(CACHE_DIR, `local-mcp-jxa-runner-${version}.tar.gz`)
565
-
566
- try {
567
- await downloadFile(url, tarPath)
568
- const stat = fs.statSync(tarPath)
569
- if (stat.size < 1000) { fs.unlinkSync(tarPath); throw new Error(`Incomplete download (${stat.size} bytes)`) }
570
-
571
- extractTar(tarPath, CACHE_DIR)
572
- fs.unlinkSync(tarPath)
573
-
574
- if (!fs.existsSync(binPath)) throw new Error('local-mcp-jxa-runner not found after extraction')
575
- fs.chmodSync(binPath, 0o755)
576
- // Do NOT codesign: the pre-built binary's cdhash must remain exactly as
577
- // distributed so the Automation TCC entry stays valid permanently.
578
-
579
- fs.writeFileSync(path.join(CACHE_DIR, '.jxa-runner-version'), version, 'utf8')
580
- process.stderr.write(` local-mcp-jxa-runner installed at ${binPath}\n`)
581
- return binPath
582
- } catch (err) {
583
- try { if (fs.existsSync(tarPath)) fs.unlinkSync(tarPath) } catch {}
584
- process.stderr.write(` (local-mcp-jxa-runner not available: ${err.message})\n`)
585
- return null
586
- }
587
- }
588
-
589
- async function _ensureTrayWindows() {
590
- const info = await getLatestBinary()
591
- const version = info.version
592
- const binDir = path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'), 'local-mcp', 'bin')
593
- const trayPath = path.join(binDir, 'lmcp-tray.exe')
594
- const verFile = path.join(binDir, '.tray-version')
595
-
596
- if (fs.existsSync(trayPath) && fs.existsSync(verFile)) {
597
- if (fs.readFileSync(verFile, 'utf8').trim() === version) return trayPath
598
- }
599
-
600
- const url = `https://download.local-mcp.com/lmcp-tray.exe?v=${version}`
601
- process.stderr.write(`\nDescargando tray v${version}...\n`)
602
- fs.mkdirSync(binDir, { recursive: true })
603
-
604
- // Download to a temp file first — if the tray is running, the exe is locked
605
- // and we can't write directly to it (EBUSY). Download first, then kill, then replace.
606
- const tmpPath = trayPath + '.tmp'
607
- try { if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath) } catch {}
608
- await downloadFile(url, tmpPath)
609
-
610
- // Kill the running tray (if any) before replacing the binary
611
- try { execSync('taskkill /F /IM lmcp-tray.exe /T', { stdio: 'ignore' }) } catch {}
612
- // Give Windows a moment to release the file handle
613
- await new Promise(r => setTimeout(r, 500))
614
-
615
- // Replace the old binary with the downloaded one
616
- try { if (fs.existsSync(trayPath)) fs.unlinkSync(trayPath) } catch {}
617
- fs.renameSync(tmpPath, trayPath)
618
-
619
- const stat = fs.statSync(trayPath)
620
- if (stat.size < 100000) {
621
- try { fs.unlinkSync(trayPath) } catch {}
622
- throw new Error(`Tray download incomplete (${stat.size} bytes)`)
623
- }
624
-
625
- fs.writeFileSync(verFile, version, 'utf8')
626
-
627
- // Auto-start on login
628
- const startupDir = path.join(process.env.APPDATA || '', 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup')
629
- if (fs.existsSync(startupDir)) {
630
- fs.writeFileSync(path.join(startupDir, 'LMCP Tray.cmd'), `@echo off\r\nstart "" "${trayPath}"`, 'ascii')
631
- }
632
-
633
- // Launch tray now (use cmd start on Windows for proper detach)
634
- try { execSync(`start "" "${trayPath}"`, { stdio: 'ignore', shell: true }) } catch {}
635
-
636
- process.stderr.write(` Tray installed at ${trayPath}\n`)
637
- return trayPath
638
- }
639
-
640
- module.exports = { ensureBinary, ensureRuntime, ensureTray, ensureTeamsProxy, ensureSlackProxy, ensureHelper, ensureJXARunner, CACHE_DIR, TRAY_DIR }
496
+ module.exports = { ensureBinary, ensureRuntime, ensureTray, ensureTeamsProxy, ensureSlackProxy, CACHE_DIR, TRAY_DIR }
package/index.js CHANGED
@@ -154,12 +154,10 @@ async function main() {
154
154
 
155
155
  async function ensureDarwinComponents() {
156
156
  if (process.platform !== 'darwin') return
157
- const { ensureTray, ensureTeamsProxy, ensureHelper, ensureJXARunner } = require('./download')
157
+ const { ensureTray, ensureTeamsProxy } = require('./download')
158
158
  await Promise.all([
159
159
  ensureTray().catch((e) => { process.stderr.write(`[LMCP] Tray install failed: ${e.message}\n`) }),
160
160
  ensureTeamsProxy().catch((e) => { process.stderr.write(`[LMCP] teams-proxy install failed: ${e.message}\n`) }),
161
- ensureHelper().catch((e) => { process.stderr.write(`[LMCP] helper install failed: ${e.message}\n`) }),
162
- ensureJXARunner().catch((e) => { process.stderr.write(`[LMCP] jxa-runner install failed: ${e.message}\n`) }),
163
161
  ])
164
162
  }
165
163
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "local-mcp",
3
- "version": "3.0.226",
3
+ "version": "3.0.228",
4
4
  "description": "LMCP — connect Claude Desktop, Cursor, Windsurf to Mail, Calendar, Contacts, Teams, OneDrive on macOS. Privacy-first: all data stays on your Mac.",
5
5
  "main": "index.js",
6
6
  "bin": {