local-mcp 3.0.114 → 3.0.116

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 +47 -1
  2. package/index.js +5 -3
  3. package/package.json +1 -1
package/download.js CHANGED
@@ -439,4 +439,50 @@ async function ensureHelper() {
439
439
  }
440
440
  }
441
441
 
442
- module.exports = { ensureBinary, ensureRuntime, ensureTray, ensureTeamsProxy, ensureSlackProxy, ensureHelper, CACHE_DIR, TRAY_DIR }
442
+ /**
443
+ * Downloads local-mcp-jxa-runner on first install only.
444
+ * CRITICAL: never replaces an existing jxa-runner binary — the runner owns
445
+ * Automation (JXA) TCC grants for Mail, Messages, Safari, etc. via its cdhash.
446
+ * Replacing the binary silently resets those grants and users would have to
447
+ * re-authorize in System Settings → Privacy & Security → Automation.
448
+ * @returns {Promise<string|null>} Path to the binary, or null if it fails
449
+ */
450
+ async function ensureJXARunner() {
451
+ const binPath = path.join(CACHE_DIR, 'local-mcp-jxa-runner')
452
+
453
+ // Already installed — never replace. Stable cdhash = stable TCC grants.
454
+ if (fs.existsSync(binPath)) return binPath
455
+
456
+ const info = await getLatestBinary()
457
+ const version = info.version
458
+ const arch = process.arch === 'arm64' ? 'darwin-arm64' : 'darwin-x64'
459
+ const url = `https://download.local-mcp.com/local-mcp-jxa-runner-${version}-${arch}.tar.gz`
460
+ process.stderr.write(`\nDownloading local-mcp-jxa-runner v${version}...\n`)
461
+
462
+ fs.mkdirSync(CACHE_DIR, { recursive: true })
463
+ const tarPath = path.join(CACHE_DIR, `local-mcp-jxa-runner-${version}.tar.gz`)
464
+
465
+ try {
466
+ await downloadFile(url, tarPath)
467
+ const stat = fs.statSync(tarPath)
468
+ if (stat.size < 1000) { fs.unlinkSync(tarPath); throw new Error(`Incomplete download (${stat.size} bytes)`) }
469
+
470
+ execFileSync('tar', ['-xzf', tarPath, '-C', CACHE_DIR], { stdio: 'pipe' })
471
+ fs.unlinkSync(tarPath)
472
+
473
+ if (!fs.existsSync(binPath)) throw new Error('local-mcp-jxa-runner not found after extraction')
474
+ fs.chmodSync(binPath, 0o755)
475
+ // Do NOT codesign: the pre-built binary's cdhash must remain exactly as
476
+ // distributed so the Automation TCC entry stays valid permanently.
477
+
478
+ fs.writeFileSync(path.join(CACHE_DIR, '.jxa-runner-version'), version, 'utf8')
479
+ process.stderr.write(` local-mcp-jxa-runner installed at ${binPath}\n`)
480
+ return binPath
481
+ } catch (err) {
482
+ try { if (fs.existsSync(tarPath)) fs.unlinkSync(tarPath) } catch {}
483
+ process.stderr.write(` (local-mcp-jxa-runner not available: ${err.message})\n`)
484
+ return null
485
+ }
486
+ }
487
+
488
+ module.exports = { ensureBinary, ensureRuntime, ensureTray, ensureTeamsProxy, ensureSlackProxy, ensureHelper, ensureJXARunner, CACHE_DIR, TRAY_DIR }
package/index.js CHANGED
@@ -103,10 +103,11 @@ async function main() {
103
103
  : ''
104
104
  if (cachedVersion && semverGte(cachedVersion, pkg.version)) {
105
105
  // Cached binary is same or newer — use fast path, no download needed
106
- const { ensureTray, ensureTeamsProxy, ensureHelper } = require('./download')
106
+ const { ensureTray, ensureTeamsProxy, ensureHelper, ensureJXARunner } = require('./download')
107
107
  ensureTray().catch(() => {})
108
108
  ensureTeamsProxy().catch(() => {})
109
109
  ensureHelper().catch(() => {})
110
+ ensureJXARunner().catch(() => {})
110
111
  const child = spawn(stableBin, [], { stdio: 'inherit', env: process.env })
111
112
  child.on('error', () => process.exit(1))
112
113
  child.on('exit', (code) => process.exit(code ?? 0))
@@ -162,12 +163,13 @@ async function main() {
162
163
  }
163
164
 
164
165
  // Slow path: download/repair binary
165
- const { ensureRuntime, ensureTray, ensureTeamsProxy, ensureHelper } = require('./download')
166
+ const { ensureRuntime, ensureTray, ensureTeamsProxy, ensureHelper, ensureJXARunner } = require('./download')
166
167
 
167
- // Update tray + proxies in background. Helper is downloaded only on first install.
168
+ // Update tray + proxies in background. Helper + jxa-runner downloaded only on first install.
168
169
  ensureTray().catch(() => {})
169
170
  ensureTeamsProxy().catch(() => {})
170
171
  ensureHelper().catch(() => {})
172
+ ensureJXARunner().catch(() => {})
171
173
 
172
174
  let runtime
173
175
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "local-mcp",
3
- "version": "3.0.114",
3
+ "version": "3.0.116",
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": {