local-mcp 3.0.125 → 3.0.127
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/package.json +1 -1
- package/setup.js +69 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-mcp",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.127",
|
|
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": {
|
package/setup.js
CHANGED
|
@@ -244,27 +244,53 @@ async function runSetup(opts = {}) {
|
|
|
244
244
|
} catch { /* config not created yet — will be created on first run */ }
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
// Interactive email prompt —
|
|
248
|
-
//
|
|
249
|
-
if (
|
|
247
|
+
// Interactive email prompt — uses /dev/tty so it works even from `curl | bash`
|
|
248
|
+
// (stdin is the pipe in that context; /dev/tty is always the real terminal).
|
|
249
|
+
// Non-fatal: if /dev/tty is unavailable (CI, automated scripts) we catch and continue.
|
|
250
|
+
if (!email) {
|
|
251
|
+
let emailPromptResult = 'not_shown_no_tty'
|
|
250
252
|
try {
|
|
251
|
-
const
|
|
253
|
+
const ttyFd = fs.openSync('/dev/tty', 'r+')
|
|
254
|
+
const ttyIn = require('stream').Readable.from(
|
|
255
|
+
(function* () {
|
|
256
|
+
const buf = Buffer.alloc(256)
|
|
257
|
+
let line = ''
|
|
258
|
+
while (true) {
|
|
259
|
+
const n = fs.readSync(ttyFd, buf, 0, 1, null)
|
|
260
|
+
if (n === 0) break
|
|
261
|
+
const ch = buf.slice(0, n).toString()
|
|
262
|
+
if (ch === '\n' || ch === '\r') break
|
|
263
|
+
line += ch
|
|
264
|
+
}
|
|
265
|
+
yield line
|
|
266
|
+
})()
|
|
267
|
+
)
|
|
268
|
+
const ttyOut = new (require('stream').Writable)({
|
|
269
|
+
write(chunk, _enc, cb) { fs.writeSync(ttyFd, chunk); cb() },
|
|
270
|
+
})
|
|
271
|
+
const rl = require('readline').createInterface({ input: ttyIn, output: ttyOut, terminal: true })
|
|
272
|
+
|
|
273
|
+
process.stdout.write('\n 📧 Email for update notifications (optional, press Enter to skip): ')
|
|
274
|
+
emailPromptResult = 'shown'
|
|
252
275
|
const ans = await new Promise(res => {
|
|
253
|
-
rl.
|
|
254
|
-
rl.close()
|
|
255
|
-
res((a || '').trim())
|
|
256
|
-
})
|
|
276
|
+
rl.once('line', a => { rl.close(); fs.closeSync(ttyFd); res((a || '').trim()) })
|
|
257
277
|
})
|
|
278
|
+
|
|
258
279
|
if (ans && ans.includes('@')) {
|
|
259
280
|
email = ans
|
|
281
|
+
emailPromptResult = 'submitted'
|
|
260
282
|
try {
|
|
261
283
|
const cfg = _readJson(cfgFile)
|
|
262
284
|
if (!cfg.license_email) { cfg.license_email = email; _writeJson(cfgFile, cfg) }
|
|
263
285
|
} catch {}
|
|
264
|
-
|
|
265
|
-
|
|
286
|
+
console.log(' ✓ Email saved')
|
|
287
|
+
} else {
|
|
288
|
+
emailPromptResult = 'skipped'
|
|
266
289
|
}
|
|
267
|
-
} catch {
|
|
290
|
+
} catch { emailPromptResult = 'failed' }
|
|
291
|
+
|
|
292
|
+
// Track prompt outcome so we can measure conversion and diagnose failures
|
|
293
|
+
_trackEmailPrompt(emailPromptResult)
|
|
268
294
|
}
|
|
269
295
|
|
|
270
296
|
// Health check — verify binary works before showing success
|
|
@@ -348,6 +374,14 @@ async function runSetup(opts = {}) {
|
|
|
348
374
|
}
|
|
349
375
|
}
|
|
350
376
|
} catch { /* non-fatal */ }
|
|
377
|
+
|
|
378
|
+
// Show settings URL with machine_id so user can register email (LMC-887)
|
|
379
|
+
try {
|
|
380
|
+
const mid = _getMachineId()
|
|
381
|
+
if (mid) {
|
|
382
|
+
console.log(`\n \x1b[1m📧 Get update notifications:\x1b[0m https://local-mcp.com/settings?m=${mid}`)
|
|
383
|
+
}
|
|
384
|
+
} catch { /* non-fatal */ }
|
|
351
385
|
}
|
|
352
386
|
|
|
353
387
|
// LMCP_METHOD lets install.sh pass 'curl' so we can distinguish it from
|
|
@@ -493,6 +527,30 @@ function _migrateCurlLaunchAgent() {
|
|
|
493
527
|
} catch { /* non-fatal — don't block install on unexpected plist content */ }
|
|
494
528
|
}
|
|
495
529
|
|
|
530
|
+
function _trackEmailPrompt(result) {
|
|
531
|
+
// result: 'shown' | 'submitted' | 'skipped' | 'not_shown_no_tty' | 'failed'
|
|
532
|
+
try {
|
|
533
|
+
const https = require('https')
|
|
534
|
+
const machineId = _getMachineId()
|
|
535
|
+
const data = JSON.stringify({
|
|
536
|
+
stage: 'email_prompt',
|
|
537
|
+
email_prompt_result: result,
|
|
538
|
+
machine_id: machineId,
|
|
539
|
+
install_id: process.env.INSTALL_ID || '',
|
|
540
|
+
})
|
|
541
|
+
const req = https.request({
|
|
542
|
+
hostname: BACKEND_HOST,
|
|
543
|
+
path: '/install-event',
|
|
544
|
+
method: 'POST',
|
|
545
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': data.length },
|
|
546
|
+
timeout: 5000,
|
|
547
|
+
})
|
|
548
|
+
req.on('error', () => {})
|
|
549
|
+
req.write(data)
|
|
550
|
+
req.end()
|
|
551
|
+
} catch { /* non-fatal */ }
|
|
552
|
+
}
|
|
553
|
+
|
|
496
554
|
function _pingInstall(clients, method, email = '') {
|
|
497
555
|
try {
|
|
498
556
|
const https = require('https')
|