@phidiassj/aiyoperps-mcp-installer 0.5.4 → 0.5.5

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 CHANGED
@@ -28,3 +28,4 @@ npx -y @phidiassj/aiyoperps-mcp-installer --url http://127.0.0.1:5078/mcp
28
28
  - For Codex, the installer writes `startup_timeout_sec = 60` because the first `npx` launch may take longer than the default 10-second startup timeout.
29
29
  - Before writing host config, the installer prewarms the bridge package with `npx ... --help` to reduce first-run startup latency.
30
30
  - The generated MCP entry includes `--startup-ping` so the bridge verifies the AiyoPerps MCP endpoint before entering stdio mode.
31
+ - Before installation, the installer probes candidate MCP URLs and only writes config if it finds a reachable endpoint.
@@ -229,7 +229,13 @@ async function installAll(hosts, url) {
229
229
  return;
230
230
  }
231
231
 
232
- await prewarmBridgePackage(url);
232
+ const resolvedUrl = await resolveInstallUrl(url, cli.urlExplicit);
233
+ if (!resolvedUrl) {
234
+ return;
235
+ }
236
+
237
+ bindInstallActions(selected, resolvedUrl);
238
+ await prewarmBridgePackage(resolvedUrl);
233
239
  await runForHosts(selected, 'install');
234
240
  }
235
241
 
@@ -252,7 +258,13 @@ async function installAnyOf(rl, hosts, url) {
252
258
  return;
253
259
  }
254
260
 
255
- await prewarmBridgePackage(url);
261
+ const resolvedUrl = await resolveInstallUrl(url, cli.urlExplicit);
262
+ if (!resolvedUrl) {
263
+ return;
264
+ }
265
+
266
+ bindInstallActions(selected, resolvedUrl);
267
+ await prewarmBridgePackage(resolvedUrl);
256
268
  await runForHosts(selected, 'install');
257
269
  }
258
270
 
@@ -317,6 +329,13 @@ async function runNonInteractive(cliArgs, hosts, url) {
317
329
  requireSupported: true,
318
330
  requireInstalled: false
319
331
  });
332
+ const resolvedUrl = await resolveInstallUrl(url, cliArgs.urlExplicit);
333
+ if (!resolvedUrl) {
334
+ return;
335
+ }
336
+
337
+ bindInstallActions(selected, resolvedUrl);
338
+ await prewarmBridgePackage(resolvedUrl);
320
339
  await runForHosts(selected, 'install');
321
340
  return;
322
341
  }
@@ -358,6 +377,27 @@ function selectHostsByIds(hosts, ids, requirements) {
358
377
  });
359
378
  }
360
379
 
380
+ function bindInstallActions(hosts, url) {
381
+ for (const host of hosts) {
382
+ host.install = createInstallAction(host, url);
383
+ }
384
+ }
385
+
386
+ function createInstallAction(host, url) {
387
+ switch (host.id) {
388
+ case 'codex':
389
+ return () => installCodex(host.path, url);
390
+ case 'claude-code':
391
+ return () => installClaudeCode(url);
392
+ case 'claude-desktop':
393
+ return () => installClaudeDesktop(host.path, url);
394
+ case 'openclaw':
395
+ return () => installOpenClaw(url);
396
+ default:
397
+ return async () => { };
398
+ }
399
+ }
400
+
361
401
  function installCodex(configPath, url) {
362
402
  ensureParentDir(configPath);
363
403
 
@@ -514,6 +554,88 @@ async function prewarmBridgePackage(url) {
514
554
  stdout.write(' prewarm complete\n');
515
555
  }
516
556
 
557
+ async function resolveInstallUrl(defaultUrl, urlExplicit) {
558
+ const candidates = urlExplicit
559
+ ? [defaultUrl]
560
+ : buildCandidateUrls(defaultUrl);
561
+
562
+ stdout.write('\nProbing MCP endpoint candidates...\n');
563
+ for (const candidate of candidates) {
564
+ const ok = await canReachMcpEndpoint(candidate);
565
+ stdout.write(` ${ok ? 'ok' : 'fail'} ${candidate}\n`);
566
+ if (ok) {
567
+ return candidate;
568
+ }
569
+ }
570
+
571
+ stdout.write(
572
+ 'No reachable AiyoPerps MCP endpoint was detected. ' +
573
+ 'Start the AiyoPerps HTTP API first or pass a valid --url, then retry.\n');
574
+ return null;
575
+ }
576
+
577
+ function buildCandidateUrls(defaultUrl) {
578
+ const urls = [
579
+ defaultUrl,
580
+ 'http://localhost:5078/mcp',
581
+ 'http://winhost:5078/mcp',
582
+ 'http://host.docker.internal:5078/mcp'
583
+ ];
584
+
585
+ const wslGatewayUrl = detectWslGatewayUrl();
586
+ if (wslGatewayUrl) {
587
+ urls.push(wslGatewayUrl);
588
+ }
589
+
590
+ return [...new Set(urls)];
591
+ }
592
+
593
+ function detectWslGatewayUrl() {
594
+ if (!isWsl()) {
595
+ return null;
596
+ }
597
+
598
+ try {
599
+ const content = fs.readFileSync('/etc/resolv.conf', 'utf8');
600
+ const match = content.match(/^nameserver\s+([0-9.]+)\s*$/m);
601
+ return match ? `http://${match[1]}:5078/mcp` : null;
602
+ } catch {
603
+ return null;
604
+ }
605
+ }
606
+
607
+ async function canReachMcpEndpoint(url) {
608
+ const controller = new AbortController();
609
+ const timeout = setTimeout(() => controller.abort(), 2500);
610
+
611
+ try {
612
+ const response = await fetch(url, {
613
+ method: 'POST',
614
+ headers: {
615
+ 'Content-Type': 'application/json'
616
+ },
617
+ body: JSON.stringify({
618
+ jsonrpc: '2.0',
619
+ id: 'installer-probe',
620
+ method: 'ping',
621
+ params: {}
622
+ }),
623
+ signal: controller.signal
624
+ });
625
+
626
+ if (!response.ok) {
627
+ return false;
628
+ }
629
+
630
+ const payload = await response.json();
631
+ return !payload?.error;
632
+ } catch {
633
+ return false;
634
+ } finally {
635
+ clearTimeout(timeout);
636
+ }
637
+ }
638
+
517
639
  function hasCodexBlock(content) {
518
640
  return new RegExp(`^\\[mcp_servers\\.${SERVER_NAME}\\]\\s*$`, 'm').test(content);
519
641
  }
@@ -570,6 +692,10 @@ function resolveTargetUrl(args) {
570
692
  return env.AIYOPERPS_MCP_URL || DEFAULT_URL;
571
693
  }
572
694
 
695
+ function isWsl() {
696
+ return Boolean(env.WSL_DISTRO_NAME || env.WSL_INTEROP);
697
+ }
698
+
573
699
  function parseCliArgs(args) {
574
700
  const install = [];
575
701
  const uninstall = [];
@@ -577,6 +703,7 @@ function parseCliArgs(args) {
577
703
  let statusOnly = false;
578
704
  let nonInteractive = false;
579
705
  let url = env.AIYOPERPS_MCP_URL || DEFAULT_URL;
706
+ let urlExplicit = Boolean(env.AIYOPERPS_MCP_URL);
580
707
 
581
708
  for (let index = 0; index < args.length; index += 1) {
582
709
  const arg = args[index];
@@ -626,11 +753,13 @@ function parseCliArgs(args) {
626
753
 
627
754
  if (arg.startsWith('--url=')) {
628
755
  url = arg.slice('--url='.length);
756
+ urlExplicit = true;
629
757
  continue;
630
758
  }
631
759
 
632
760
  if (arg === '--url' && index + 1 < args.length) {
633
761
  url = args[index + 1];
762
+ urlExplicit = true;
634
763
  index += 1;
635
764
  continue;
636
765
  }
@@ -642,7 +771,8 @@ function parseCliArgs(args) {
642
771
  installAll,
643
772
  statusOnly,
644
773
  nonInteractive,
645
- url
774
+ url,
775
+ urlExplicit
646
776
  };
647
777
  }
648
778
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phidiassj/aiyoperps-mcp-installer",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "Interactive installer for registering AiyoPerps MCP with supported AI agent hosts.",
5
5
  "license": "MIT",
6
6
  "type": "module",