local-mcp 3.0.419 → 3.0.420
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 +1 -1
- package/download.js +22 -6
- package/index.js +20 -1
- package/launch-event.js +1 -1
- package/package.json +3 -1
- package/setup.js +9 -9
- package/unconfigure.js +101 -0
- package/user-agent.js +40 -0
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
[](https://local-mcp.com/en/privacy)
|
|
9
9
|
[](https://glama.ai/mcp/servers/colibird-ai/local-mcp-releases?ref=npm)
|
|
10
10
|
|
|
11
|
-
**Runs on your computer. No tokens. No API keys
|
|
11
|
+
**Runs on your computer. No tokens. No API keys.**
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
package/download.js
CHANGED
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
* Cache: ~/.local/share/local-mcp/bin/{version}/
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const https
|
|
9
|
-
const http = require('http')
|
|
8
|
+
const { https, http } = require('./user-agent')
|
|
10
9
|
const fs = require('fs')
|
|
11
10
|
const path = require('path')
|
|
12
11
|
const os = require('os')
|
|
@@ -543,20 +542,37 @@ async function ensureRuntime() {
|
|
|
543
542
|
return { pythonBin: binPath, serverPath: binPath, runtimeDir: versionDir, binPath, version }
|
|
544
543
|
}
|
|
545
544
|
|
|
545
|
+
/**
|
|
546
|
+
* The version a co-process is published at (lmcp-mac#382). `/runtime/latest` names it apart from
|
|
547
|
+
* the server's (`coprocess_version`, #3598); the Mac updater installs exactly that one and writes
|
|
548
|
+
* the same `.<name>-version` file. Asking for `version` instead made the two writers disagree
|
|
549
|
+
* whenever the two differ, and each re-downloaded what the other had just written.
|
|
550
|
+
*/
|
|
551
|
+
function coprocessVersion(info) {
|
|
552
|
+
const cp = info && info.coprocess_version
|
|
553
|
+
return typeof cp === 'string' && /^[0-9][0-9A-Za-z.+-]*$/.test(cp) ? cp : info.version
|
|
554
|
+
}
|
|
555
|
+
|
|
546
556
|
/**
|
|
547
557
|
* Descarga el co-proceso teams-proxy si no está instalado o es una versión diferente.
|
|
548
558
|
* El binary se instala en ~/.local/share/local-mcp/bin/teams-proxy
|
|
559
|
+
*
|
|
560
|
+
* lmcp-mac#382: not when the tray app is installed. The tray's updater owns teams-proxy (it
|
|
561
|
+
* verifies its digest and Developer ID; this does not), and trays 3.0.418-3.0.419 delete it every
|
|
562
|
+
* update cycle: npx put it back on every spawn — 140 addresses downloaded it 5-49 times a day.
|
|
549
563
|
* @returns {Promise<string|null>} Ruta al binary, o null si falla
|
|
550
564
|
*/
|
|
551
|
-
async function ensureTeamsProxy(platform = process.platform) {
|
|
565
|
+
async function ensureTeamsProxy(platform = process.platform, deps = {}) {
|
|
566
|
+
const { trayApps = [TRAY_APP, SYSTEM_TRAY_APP] } = deps
|
|
552
567
|
// Only published for darwin (darwin-arm64/darwin-x64 artifacts) — mirrors the
|
|
553
568
|
// guard ensureTray() already has. Without it, every Windows/Linux install fired
|
|
554
569
|
// a doomed download attempt and printed a confusing "not available" error (#1390).
|
|
555
570
|
// platform is a parameter (default process.platform) so the guard is testable
|
|
556
571
|
// without a network mock — same pattern as _writeLaunchScript's isWin.
|
|
557
572
|
if (platform !== 'darwin') return null
|
|
573
|
+
if (trayApps.some(app => app && _isUsableTrayBundle(app))) return null
|
|
558
574
|
const info = await getLatestBinary()
|
|
559
|
-
const version = info
|
|
575
|
+
const version = coprocessVersion(info)
|
|
560
576
|
const arch = process.arch === 'arm64' ? 'darwin-arm64' : 'darwin-x64'
|
|
561
577
|
const binPath = path.join(CACHE_DIR, 'teams-proxy')
|
|
562
578
|
const verFile = path.join(CACHE_DIR, '.teams-proxy-version')
|
|
@@ -874,7 +890,7 @@ async function ensureSlackProxy(platform = process.platform) {
|
|
|
874
890
|
// without a network mock — same pattern as _writeLaunchScript's isWin.
|
|
875
891
|
if (platform !== 'darwin') return null
|
|
876
892
|
const info = await getLatestBinary()
|
|
877
|
-
const version = info
|
|
893
|
+
const version = coprocessVersion(info) // lmcp-mac#382: the same version the tray installs
|
|
878
894
|
const arch = process.arch === 'arm64' ? 'darwin-arm64' : 'darwin-x64'
|
|
879
895
|
const binPath = path.join(CACHE_DIR, 'slack-proxy')
|
|
880
896
|
const verFile = path.join(CACHE_DIR, '.slack-proxy-version')
|
|
@@ -918,7 +934,7 @@ async function ensureSlackProxy(platform = process.platform) {
|
|
|
918
934
|
}
|
|
919
935
|
|
|
920
936
|
module.exports = {
|
|
921
|
-
ensureBinary, ensureRuntime, ensureTray, ensureTeamsProxy, ensureSlackProxy, versionFromArtifactUrl,
|
|
937
|
+
ensureBinary, ensureRuntime, ensureTray, ensureTeamsProxy, ensureSlackProxy, versionFromArtifactUrl, coprocessVersion,
|
|
922
938
|
latestVersionForPlatform, getArch,
|
|
923
939
|
CACHE_DIR, TRAY_DIR, _getMachineId, LINUX_NO_SOPORTADO,
|
|
924
940
|
_probeWinHardwareId, _resetWinHardwareIdCacheForTests, UUIDS_DE_HARDWARE_BASURA, _esIdDeHardwareValido,
|
package/index.js
CHANGED
|
@@ -16,7 +16,7 @@ const { spawn, execFileSync, execSync } = require('child_process')
|
|
|
16
16
|
const path = require('path')
|
|
17
17
|
const os = require('os')
|
|
18
18
|
const fs = require('fs')
|
|
19
|
-
const https = require('
|
|
19
|
+
const { https } = require('./user-agent')
|
|
20
20
|
const evento = require('./launch-event')
|
|
21
21
|
|
|
22
22
|
// ── Version + liveness helpers (module scope so they are testable) ────────────
|
|
@@ -405,6 +405,25 @@ async function main() {
|
|
|
405
405
|
// copy and the durable App Support copy (see uninstallSentinelPaths).
|
|
406
406
|
writeUninstallSentinels()
|
|
407
407
|
|
|
408
|
+
// #48 (2): quitar la entrada de los clientes de IA que se alcanzan (mismo catálogo que el setup);
|
|
409
|
+
// lo que no se puede tocar se dice. Sin catálogo (sin red) se dice también, no se adivina.
|
|
410
|
+
try {
|
|
411
|
+
const { unconfigureClients } = require('./unconfigure')
|
|
412
|
+
const catalogFile = process.env.LMCP_CLIENTS_CATALOG_FILE
|
|
413
|
+
const catalog = catalogFile
|
|
414
|
+
? JSON.parse(fs.readFileSync(catalogFile, 'utf8'))
|
|
415
|
+
: await require('./setup')._fetchClientsCatalog()
|
|
416
|
+
if (!catalog) {
|
|
417
|
+
console.log('No se pudo leer el catálogo de clientes: las entradas "local-mcp" de tus clientes de IA quedan; quitalas a mano.')
|
|
418
|
+
} else {
|
|
419
|
+
const r = unconfigureClients(catalog)
|
|
420
|
+
for (const x of r.removed) console.log(`Entrada local-mcp eliminada de ${x.client}: ${x.file}`)
|
|
421
|
+
for (const x of r.left) console.log(`Dejada en ${x.client}${x.file ? ' (' + x.file + ')' : ''}: ${x.reason}; quitá "local-mcp" a mano.`)
|
|
422
|
+
}
|
|
423
|
+
} catch (e) {
|
|
424
|
+
console.log('No se pudieron quitar las entradas de los clientes de IA: ' + e.message)
|
|
425
|
+
}
|
|
426
|
+
|
|
408
427
|
if (fs.existsSync(CACHE_DIR)) {
|
|
409
428
|
fs.rmSync(CACHE_DIR, { recursive: true, force: true })
|
|
410
429
|
console.log(`Runtime eliminado de ${CACHE_DIR}`)
|
package/launch-event.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// · demorar el arranque del server o una salida — sin reintentos, timeout corto, todo error
|
|
14
14
|
// tragado;
|
|
15
15
|
// · mandar algo que no sea uno de los cinco campos: ni el argumento, ni rutas, ni entorno.
|
|
16
|
-
const https = require('
|
|
16
|
+
const { https } = require('./user-agent')
|
|
17
17
|
const cp = require('child_process')
|
|
18
18
|
const DESTINO = { hostname: 'office-mcp-production.up.railway.app', path: '/launch-event' }
|
|
19
19
|
const PLATAFORMAS = ['darwin', 'win32', 'linux', 'other']
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-mcp",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.420",
|
|
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": {
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"download.js",
|
|
16
16
|
"setup.js",
|
|
17
17
|
"launch-event.js",
|
|
18
|
+
"user-agent.js",
|
|
19
|
+
"unconfigure.js",
|
|
18
20
|
"postinstall.js",
|
|
19
21
|
"README.md"
|
|
20
22
|
],
|
package/setup.js
CHANGED
|
@@ -67,7 +67,7 @@ function _writeLaunchScript(npxAbsPath, cacheDir, isWin = _IS_WIN) {
|
|
|
67
67
|
'NODE_MAJOR=$(echo "$NODE_VER" | sed \'s/v//\' | cut -d. -f1)',
|
|
68
68
|
'if [ "${NODE_MAJOR:-0}" -lt 16 ] 2>/dev/null; then',
|
|
69
69
|
' # curl telemetry — no Node dependency, fire-and-forget',
|
|
70
|
-
` curl -sf --max-time 3 -X POST "https://${BACKEND_HOST}/install-event" \\`,
|
|
70
|
+
` curl -sf --max-time 3 -A "LMCP-npm (launcher)" -X POST "https://${BACKEND_HOST}/install-event" \\`,
|
|
71
71
|
' -H "Content-Type: application/json" \\',
|
|
72
72
|
' -d "{\\\"stage\\\":\\\"node_too_old\\\",\\\"version\\\":\\\"$NODE_VER\\\"}" &>/dev/null &',
|
|
73
73
|
' printf \'LMCP requires Node 16+. Current: %s\\nFix: nvm alias default 22 && nvm uninstall %s — then restart Claude Desktop.\\n\' "$NODE_VER" "$NODE_VER" >&2',
|
|
@@ -328,7 +328,7 @@ async function runSetup(opts = {}) {
|
|
|
328
328
|
const _preToken = process.env.LMCP_PRE_TOKEN || ''
|
|
329
329
|
if (_preToken.startsWith('lmcp-pre-')) {
|
|
330
330
|
try {
|
|
331
|
-
const https = require('
|
|
331
|
+
const { https } = require('./user-agent')
|
|
332
332
|
const req = https.request({
|
|
333
333
|
hostname: BACKEND_HOST,
|
|
334
334
|
path: `/install/started/${_preToken}`,
|
|
@@ -819,7 +819,7 @@ function _getMachineId() {
|
|
|
819
819
|
async function _associateEmail(email, token) {
|
|
820
820
|
return new Promise((resolve) => {
|
|
821
821
|
try {
|
|
822
|
-
const https = require('
|
|
822
|
+
const { https } = require('./user-agent')
|
|
823
823
|
const payload = JSON.stringify({
|
|
824
824
|
stage: 'email_prompt',
|
|
825
825
|
email_prompt_result: 'submitted',
|
|
@@ -892,7 +892,7 @@ function _writeCloudToken(cacheDir, token) {
|
|
|
892
892
|
/// treat failure as non-fatal and fall through to the email-based path.
|
|
893
893
|
async function _registerAnonToken() {
|
|
894
894
|
return new Promise((resolve) => {
|
|
895
|
-
const https = require('
|
|
895
|
+
const { https } = require('./user-agent')
|
|
896
896
|
const machineId = _getMachineId()
|
|
897
897
|
const payload = JSON.stringify({ machine_id: machineId })
|
|
898
898
|
const req = https.request({
|
|
@@ -951,7 +951,7 @@ function _migrateCurlLaunchAgent() {
|
|
|
951
951
|
|
|
952
952
|
function _trackSetupStep(step, status, client, detail) {
|
|
953
953
|
try {
|
|
954
|
-
const https = require('
|
|
954
|
+
const { https } = require('./user-agent')
|
|
955
955
|
const payload = {
|
|
956
956
|
machine_id: _getMachineId(),
|
|
957
957
|
install_id: process.env.INSTALL_ID || '',
|
|
@@ -989,7 +989,7 @@ function _trackSetupStep(step, status, client, detail) {
|
|
|
989
989
|
|
|
990
990
|
function _trackConfigWritten(clientId, clientName) {
|
|
991
991
|
try {
|
|
992
|
-
const https = require('
|
|
992
|
+
const { https } = require('./user-agent')
|
|
993
993
|
const machineId = _getMachineId()
|
|
994
994
|
const data = JSON.stringify({
|
|
995
995
|
stage: 'config_written',
|
|
@@ -1015,7 +1015,7 @@ function _trackConfigWritten(clientId, clientName) {
|
|
|
1015
1015
|
|
|
1016
1016
|
function _trackRestartPrompted(clientName) {
|
|
1017
1017
|
try {
|
|
1018
|
-
const https = require('
|
|
1018
|
+
const { https } = require('./user-agent')
|
|
1019
1019
|
const machineId = _getMachineId()
|
|
1020
1020
|
const payload = {
|
|
1021
1021
|
stage: 'restart_prompted',
|
|
@@ -1040,7 +1040,7 @@ function _trackRestartPrompted(clientName) {
|
|
|
1040
1040
|
|
|
1041
1041
|
function _pingInstall(clients, method, email = '', binaryVersion = '') {
|
|
1042
1042
|
try {
|
|
1043
|
-
const https = require('
|
|
1043
|
+
const { https } = require('./user-agent')
|
|
1044
1044
|
const pkg = require('./package.json')
|
|
1045
1045
|
const machineId = _getMachineId()
|
|
1046
1046
|
// Persist the attribution ref so the server reports it on every heartbeat —
|
|
@@ -1133,7 +1133,7 @@ function _pingInstall(clients, method, email = '', binaryVersion = '') {
|
|
|
1133
1133
|
// callers treat that as "don't launch anything," never as an error.
|
|
1134
1134
|
function _fetchClientsCatalog(totalMs = 4000) {
|
|
1135
1135
|
return new Promise((resolve) => {
|
|
1136
|
-
const https = require('
|
|
1136
|
+
const { https } = require('./user-agent')
|
|
1137
1137
|
const req = https.request({
|
|
1138
1138
|
hostname: BACKEND_HOST, path: '/clients/catalog', method: 'GET', timeout: totalMs,
|
|
1139
1139
|
}, (res) => {
|
package/unconfigure.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
// #48 (2): `npx local-mcp uninstall` quita la entrada `local-mcp` de las configs de los clientes de IA
|
|
3
|
+
// que puede alcanzar, con el MISMO catálogo que usa el setup (nada de lista de clientes aquí). Sin esto
|
|
4
|
+
// el cliente sigue lanzando el wrapper en cada arranque contra una máquina ya desinstalada.
|
|
5
|
+
//
|
|
6
|
+
// Sólo toca lo que puede deshacer sin riesgo: JSON estándar (cfgSchema mcpServers / servers /
|
|
7
|
+
// context_servers) y sólo la clave `local-mcp` dentro de ese objeto. Lo demás (TOML/YAML, esquemas
|
|
8
|
+
// propios, JSON con comentarios o inválido, comodines sin carpeta) no se reescribe: se devuelve en
|
|
9
|
+
// `left` con el motivo, para que el comando diga qué dejó.
|
|
10
|
+
const fs = require('fs')
|
|
11
|
+
const os = require('os')
|
|
12
|
+
const path = require('path')
|
|
13
|
+
|
|
14
|
+
const ENTRY = 'local-mcp'
|
|
15
|
+
const KEY_BY_SCHEMA = { mcpServers: 'mcpServers', servers: 'servers', context_servers: 'context_servers' }
|
|
16
|
+
|
|
17
|
+
function _expand(p, env) {
|
|
18
|
+
return p
|
|
19
|
+
.replace(/^~(?=[\\/]|$)/, env.home)
|
|
20
|
+
.replace(/%APPDATA%/g, env.appData)
|
|
21
|
+
.replace(/%LOCALAPPDATA%/g, env.localAppData)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Un `*` sólo vale dentro de UN segmento; se resuelve contra el disco.
|
|
25
|
+
function _glob(p) {
|
|
26
|
+
if (!p.includes('*')) return [p]
|
|
27
|
+
const parts = p.split(/[\\/]/)
|
|
28
|
+
let bases = [parts[0] === '' ? path.sep : parts[0]]
|
|
29
|
+
for (const seg of parts.slice(1)) {
|
|
30
|
+
const next = []
|
|
31
|
+
for (const b of bases) {
|
|
32
|
+
if (!seg.includes('*')) { next.push(path.join(b, seg)); continue }
|
|
33
|
+
const re = new RegExp('^' + seg.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*') + '$')
|
|
34
|
+
let names = []
|
|
35
|
+
try { names = fs.readdirSync(b) } catch {}
|
|
36
|
+
for (const n of names) if (re.test(n)) next.push(path.join(b, n))
|
|
37
|
+
}
|
|
38
|
+
bases = next
|
|
39
|
+
}
|
|
40
|
+
return bases
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function _paths(client, plat, env) {
|
|
44
|
+
const platform = (client.platforms || {})[plat]
|
|
45
|
+
if (!platform || !Array.isArray(platform.cfgPaths)) return []
|
|
46
|
+
const hosts = Array.isArray(platform.hosts) && platform.hosts.length ? platform.hosts : [null]
|
|
47
|
+
const out = []
|
|
48
|
+
for (const raw of platform.cfgPaths) {
|
|
49
|
+
for (const h of hosts) {
|
|
50
|
+
const withHost = h === null ? raw : raw.replace(/\{host\}/g, h)
|
|
51
|
+
if (withHost.includes('{host}')) continue
|
|
52
|
+
for (const f of _glob(_expand(withHost, env))) if (!out.includes(f)) out.push(f)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return out
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function _writeAtomic(file, data) {
|
|
59
|
+
const tmp = file + '.lmcp-tmp'
|
|
60
|
+
fs.writeFileSync(tmp, JSON.stringify(data, null, 2) + '\n', 'utf8')
|
|
61
|
+
fs.renameSync(tmp, file)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {object} catalog el catálogo servido (`clients[]`).
|
|
66
|
+
* @param {object} [opts] platform, home, appData, localAppData (para probar sin tocar el disco real).
|
|
67
|
+
* @returns {{removed: {client:string,file:string}[], left: {client:string,file?:string,reason:string}[]}}
|
|
68
|
+
*/
|
|
69
|
+
function unconfigureClients(catalog, opts = {}) {
|
|
70
|
+
const home = opts.home || os.homedir()
|
|
71
|
+
const platform = opts.platform || process.platform
|
|
72
|
+
const env = {
|
|
73
|
+
home,
|
|
74
|
+
appData: opts.appData || process.env.APPDATA || path.join(home, 'AppData', 'Roaming'),
|
|
75
|
+
localAppData: opts.localAppData || process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local'),
|
|
76
|
+
}
|
|
77
|
+
const plat = platform === 'darwin' ? 'macos' : platform === 'win32' ? 'windows' : null
|
|
78
|
+
const removed = []
|
|
79
|
+
const left = []
|
|
80
|
+
if (!plat || !catalog || !Array.isArray(catalog.clients)) return { removed, left }
|
|
81
|
+
for (const c of catalog.clients) {
|
|
82
|
+
const files = _paths(c, plat, env).filter((f) => { try { return fs.statSync(f).isFile() } catch { return false } })
|
|
83
|
+
for (const file of files) {
|
|
84
|
+
const key = KEY_BY_SCHEMA[c.cfgSchema]
|
|
85
|
+
if (!key) { left.push({ client: c.id, file, reason: 'schema ' + c.cfgSchema }); continue }
|
|
86
|
+
let data
|
|
87
|
+
try { data = JSON.parse(fs.readFileSync(file, 'utf8')) } catch {
|
|
88
|
+
left.push({ client: c.id, file, reason: 'not plain JSON' }); continue
|
|
89
|
+
}
|
|
90
|
+
const block = data && typeof data === 'object' ? data[key] : null
|
|
91
|
+
if (!block || typeof block !== 'object' || !(ENTRY in block)) continue
|
|
92
|
+
delete block[ENTRY]
|
|
93
|
+
try { _writeAtomic(file, data); removed.push({ client: c.id, file }) } catch (e) {
|
|
94
|
+
left.push({ client: c.id, file, reason: e.message })
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { removed, left }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = { unconfigureClients }
|
package/user-agent.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
// Every outbound HTTP request from npx carries `User-Agent: LMCP-npm/<version> (<platform>)`.
|
|
3
|
+
// Node sends none by default, and an empty-UA download of a binary from a fresh process looks
|
|
4
|
+
// like malware to a security gateway. Drop-in `https`/`http` wrappers: same signatures,
|
|
5
|
+
// the header is added unless the caller already set one. Resolve `request`/`get` at call time.
|
|
6
|
+
const https = require('https')
|
|
7
|
+
const http = require('http')
|
|
8
|
+
|
|
9
|
+
let VERSION = '0'
|
|
10
|
+
try { VERSION = require('./package.json').version || '0' } catch {}
|
|
11
|
+
|
|
12
|
+
function userAgent (platform = process.platform) {
|
|
13
|
+
return `LMCP-npm/${VERSION} (${platform})`
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function conUA (opts) {
|
|
17
|
+
const o = { ...(opts || {}) }
|
|
18
|
+
const h = { ...(o.headers || {}) }
|
|
19
|
+
if (!Object.keys(h).some((k) => k.toLowerCase() === 'user-agent')) h['User-Agent'] = userAgent()
|
|
20
|
+
o.headers = h
|
|
21
|
+
return o
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function envolver (mod) {
|
|
25
|
+
return {
|
|
26
|
+
request (opts, cb) {
|
|
27
|
+
if (typeof opts === 'string' || opts instanceof URL) return mod.request(opts, conUA({}), cb)
|
|
28
|
+
return mod.request(conUA(opts), cb)
|
|
29
|
+
},
|
|
30
|
+
get (url, opts, cb) {
|
|
31
|
+
if (typeof url === 'string' || url instanceof URL) {
|
|
32
|
+
if (typeof opts === 'function') return mod.get(url, conUA({}), opts)
|
|
33
|
+
return mod.get(url, conUA(opts), cb)
|
|
34
|
+
}
|
|
35
|
+
return mod.get(conUA(url), typeof opts === 'function' ? opts : cb)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { userAgent, conUA, https: envolver(https), http: envolver(http) }
|