local-mcp 3.0.253 → 3.0.255
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 +6 -119
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-mcp",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.255",
|
|
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
|
@@ -535,97 +535,12 @@ async function runSetup(opts = {}) {
|
|
|
535
535
|
} catch { /* config not created yet — will be created on first run */ }
|
|
536
536
|
}
|
|
537
537
|
|
|
538
|
-
// Interactive email prompt
|
|
539
|
-
//
|
|
540
|
-
//
|
|
541
|
-
//
|
|
542
|
-
//
|
|
543
|
-
//
|
|
544
|
-
// when launched from Go tray installer). Pre-test uses 'r+' (same flags as
|
|
545
|
-
// the actual open) to avoid a race where the pre-test passes but actual open fails.
|
|
546
|
-
let _hasTty = false
|
|
547
|
-
if (_IS_WIN) {
|
|
548
|
-
// Windows: check stdin.isTTY — false when spawned as a subprocess without console
|
|
549
|
-
_hasTty = process.stdin && process.stdin.isTTY === true
|
|
550
|
-
} else {
|
|
551
|
-
try { const fd = fs.openSync('/dev/tty', 'r+'); fs.closeSync(fd); _hasTty = true }
|
|
552
|
-
catch { /* ENXIO or ENOENT — no controlling terminal */ }
|
|
553
|
-
}
|
|
554
|
-
if (!email && !_hasTty) {
|
|
555
|
-
_trackEmailPrompt('skipped_no_tty', '', '')
|
|
556
|
-
} else if (!email) {
|
|
557
|
-
let emailPromptResult = 'not_shown_no_tty'
|
|
558
|
-
let emailPromptError = ''
|
|
559
|
-
try {
|
|
560
|
-
let rl
|
|
561
|
-
let ttyFd = null
|
|
562
|
-
|
|
563
|
-
if (_IS_WIN) {
|
|
564
|
-
// Windows: use process.stdin directly (works in PowerShell and cmd)
|
|
565
|
-
rl = require('readline').createInterface({ input: process.stdin, output: process.stderr })
|
|
566
|
-
} else {
|
|
567
|
-
// macOS/Linux: open /dev/tty to read from terminal even when stdin is piped.
|
|
568
|
-
// If this throws (ENXIO/ENOENT), treat as no-tty, not a failure.
|
|
569
|
-
try { ttyFd = fs.openSync('/dev/tty', 'r+') }
|
|
570
|
-
catch (ttyErr) {
|
|
571
|
-
_trackEmailPrompt('skipped_no_tty', '', '')
|
|
572
|
-
ttyFd = null
|
|
573
|
-
// Skip the rest of the prompt block
|
|
574
|
-
throw Object.assign(new Error('no_tty'), { _skipPrompt: true })
|
|
575
|
-
}
|
|
576
|
-
const ttyIn = require('stream').Readable.from(
|
|
577
|
-
(function* () {
|
|
578
|
-
const buf = Buffer.alloc(256)
|
|
579
|
-
let line = ''
|
|
580
|
-
while (true) {
|
|
581
|
-
const n = fs.readSync(ttyFd, buf, 0, 1, null)
|
|
582
|
-
if (n === 0) break
|
|
583
|
-
const ch = buf.slice(0, n).toString()
|
|
584
|
-
if (ch === '\n' || ch === '\r') break
|
|
585
|
-
line += ch
|
|
586
|
-
}
|
|
587
|
-
yield line
|
|
588
|
-
})()
|
|
589
|
-
)
|
|
590
|
-
const ttyOut = new (require('stream').Writable)({
|
|
591
|
-
write(chunk, _enc, cb) { fs.writeSync(ttyFd, chunk); cb() },
|
|
592
|
-
})
|
|
593
|
-
rl = require('readline').createInterface({ input: ttyIn, output: ttyOut, terminal: true })
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
process.stderr.write('\n Email for update notifications (optional, press Enter to skip): ')
|
|
597
|
-
emailPromptResult = 'shown'
|
|
598
|
-
const ans = await Promise.race([
|
|
599
|
-
new Promise(res => {
|
|
600
|
-
rl.once('line', a => { rl.close(); if (ttyFd !== null) try { fs.closeSync(ttyFd) } catch {} ; res((a || '').trim()) })
|
|
601
|
-
}),
|
|
602
|
-
new Promise(res => setTimeout(() => {
|
|
603
|
-
try { rl.close() } catch {}
|
|
604
|
-
if (ttyFd !== null) try { fs.closeSync(ttyFd) } catch {}
|
|
605
|
-
res('')
|
|
606
|
-
}, 30000)),
|
|
607
|
-
])
|
|
608
|
-
|
|
609
|
-
if (ans && ans.includes('@')) {
|
|
610
|
-
email = ans
|
|
611
|
-
emailPromptResult = 'submitted'
|
|
612
|
-
try {
|
|
613
|
-
const r = _safeReadConfig(cfgFile)
|
|
614
|
-
const cfg = r.data || {}
|
|
615
|
-
if (!cfg.license_email) { cfg.license_email = email; _atomicWriteConfig(cfgFile, cfg) }
|
|
616
|
-
} catch {}
|
|
617
|
-
console.log(' ✓ Email saved')
|
|
618
|
-
} else {
|
|
619
|
-
emailPromptResult = 'skipped'
|
|
620
|
-
}
|
|
621
|
-
} catch (err) {
|
|
622
|
-
if (err && err._skipPrompt) { /* skipped_no_tty already tracked above */ }
|
|
623
|
-
else { emailPromptResult = 'failed'; emailPromptError = (err && err.message) || String(err) }
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// Track prompt outcome so we can measure conversion and diagnose failures
|
|
627
|
-
_trackEmailPrompt(emailPromptResult, emailPromptResult === 'submitted' ? email : '', emailPromptError)
|
|
628
|
-
}
|
|
538
|
+
// Interactive email prompt removed (2026-06-05) — same change already made in
|
|
539
|
+
// install.sh on 2026-04-19. It paused `curl | bash` up to 30s on a confusing
|
|
540
|
+
// mid-install prompt while ground-truth telemetry showed ~98% of machines that
|
|
541
|
+
// hit it reached a heartbeat anyway (no conversion impact). Email is still
|
|
542
|
+
// captured non-interactively: via LMCP_EMAIL above, the anon tunnel register,
|
|
543
|
+
// and the tray/settings UI later.
|
|
629
544
|
|
|
630
545
|
// Health check — verify binary works before showing success
|
|
631
546
|
const healthOk = _runHealthCheck()
|
|
@@ -950,34 +865,6 @@ function _trackConfigWritten(clientId, clientName) {
|
|
|
950
865
|
} catch { /* non-fatal */ }
|
|
951
866
|
}
|
|
952
867
|
|
|
953
|
-
function _trackEmailPrompt(result, email, errorMsg) {
|
|
954
|
-
// result: 'shown' | 'submitted' | 'skipped' | 'not_shown_no_tty' | 'failed'
|
|
955
|
-
try {
|
|
956
|
-
const https = require('https')
|
|
957
|
-
const machineId = _getMachineId()
|
|
958
|
-
const payload = {
|
|
959
|
-
stage: 'email_prompt',
|
|
960
|
-
email_prompt_result: result,
|
|
961
|
-
machine_id: machineId,
|
|
962
|
-
install_id: process.env.INSTALL_ID || '',
|
|
963
|
-
}
|
|
964
|
-
if (email) payload.email = email
|
|
965
|
-
if (errorMsg) payload.ref = errorMsg.slice(0, 200) // reuse ref field; fits in existing schema
|
|
966
|
-
const data = JSON.stringify(payload)
|
|
967
|
-
const req = https.request({
|
|
968
|
-
hostname: BACKEND_HOST,
|
|
969
|
-
path: '/install-event',
|
|
970
|
-
method: 'POST',
|
|
971
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': data.length },
|
|
972
|
-
timeout: 5000,
|
|
973
|
-
})
|
|
974
|
-
req.on('response', (res) => res.resume())
|
|
975
|
-
req.on('error', () => {})
|
|
976
|
-
req.write(data)
|
|
977
|
-
req.end()
|
|
978
|
-
} catch { /* non-fatal */ }
|
|
979
|
-
}
|
|
980
|
-
|
|
981
868
|
function _trackRestartPrompted(clientName) {
|
|
982
869
|
try {
|
|
983
870
|
const https = require('https')
|