agentic-relay 3.8.4 → 3.8.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.
Files changed (2) hide show
  1. package/bin/install.mjs +81 -28
  2. package/package.json +1 -1
package/bin/install.mjs CHANGED
@@ -29,7 +29,7 @@
29
29
  * deeplink Print URL the agent registers (tome://, raycast://) — user clicks Install
30
30
  */
31
31
 
32
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
32
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, realpathSync } from "fs";
33
33
  import { homedir } from "os";
34
34
  import { join, dirname } from "path";
35
35
  import { fileURLToPath } from "url";
@@ -610,15 +610,58 @@ function getExistingKey() {
610
610
 
611
611
  /**
612
612
  * Link a bare global `agentrelay` command so users skip `npx` after install.
613
- * No-ops only when the resolved `agentrelay` already matches this package's
614
- * version — a stale global install gets upgraded, not skipped. Prefers a
615
- * registry install (`agentic-relay@<version>`) over the local path: when this
616
- * runs via npx, the local path is an ephemeral cache dir that npm would
617
- * link/copy from. Falls back to the path for unpublished checkouts. Surfaces
618
- * npm's actual error (EACCES etc.) instead of swallowing it, and verifies the
619
- * command resolves afterwards so a PATH mismatch is reported, not discovered
620
- * later as `command not found`.
613
+ * No-ops only when a GENUINE install of `agentrelay` already matches this
614
+ * package's version — a stale global install gets upgraded, not skipped.
615
+ *
616
+ * npx trap: `npx agentic-relay` prepends its ephemeral cache's .bin to this
617
+ * process's PATH, so `agentrelay` ALWAYS resolves here (to the very copy being
618
+ * run) and disappears when npx exits. A naive "already on your PATH" check
619
+ * therefore never links anything and the user gets `command not found` in
620
+ * their next shell. We resolve the command's real location and ignore it when
621
+ * it lives in an npx/npm-exec cache or inside this package itself.
622
+ *
623
+ * Prefers a registry install (`agentic-relay@<version>`) over the local path
624
+ * (the npx cache dir is ephemeral; npm would link/copy from it). Falls back to
625
+ * the path for unpublished checkouts. Surfaces npm's actual error (EACCES
626
+ * etc.) instead of swallowing it, and verifies the command resolves afterwards
627
+ * so a PATH mismatch is reported, not discovered later as `command not found`.
621
628
  */
629
+ function resolveCommandPath(cmd) {
630
+ try {
631
+ const probe = process.platform === "win32" ? `where ${cmd}` : `command -v ${cmd}`;
632
+ const out = execSync(probe, { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
633
+ return out.split("\n")[0].trim() || null;
634
+ } catch {
635
+ return null;
636
+ }
637
+ }
638
+
639
+ /** True when a resolved bin path is an ephemeral npx/npm-exec shim (or this package's own copy), not a real install. */
640
+ function isEphemeralBin(binPath, pkgRoot) {
641
+ if (!binPath) return false;
642
+ let real = binPath;
643
+ try {
644
+ real = realpathSync(binPath);
645
+ } catch {
646
+ // dangling symlink — definitely not a usable install
647
+ return true;
648
+ }
649
+ const sep = process.platform === "win32" ? "\\" : "/";
650
+ return (
651
+ real.includes(`${sep}_npx${sep}`) ||
652
+ binPath.includes(`${sep}_npx${sep}`) ||
653
+ real.startsWith(realpathSafe(pkgRoot))
654
+ );
655
+ }
656
+
657
+ function realpathSafe(p) {
658
+ try {
659
+ return realpathSync(p);
660
+ } catch {
661
+ return p;
662
+ }
663
+ }
664
+
622
665
  function linkGlobalCli() {
623
666
  const pkgRoot = join(__dirname, "..");
624
667
  let version = null;
@@ -628,16 +671,19 @@ function linkGlobalCli() {
628
671
  // unknown version — fall through to the path install
629
672
  }
630
673
 
631
- try {
632
- const out = execSync("agentrelay version", { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
633
- const installed = (out.trim().split(/\s+/).pop() || "").trim();
634
- if (version && installed === version) {
635
- success(`\`agentrelay\` ${version} already on your PATH`);
636
- return;
674
+ const binPath = resolveCommandPath("agentrelay");
675
+ if (binPath && !isEphemeralBin(binPath, pkgRoot)) {
676
+ try {
677
+ const out = execSync("agentrelay version", { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
678
+ const installed = (out.trim().split(/\s+/).pop() || "").trim();
679
+ if (version && installed === version) {
680
+ success(`\`agentrelay\` ${version} already on your PATH`);
681
+ return;
682
+ }
683
+ log(`global \`agentrelay\` is ${installed || "an unknown version"} — updating to ${version}…`);
684
+ } catch {
685
+ // resolves but doesn't run — reinstall it
637
686
  }
638
- log(`global \`agentrelay\` is ${installed || "an unknown version"} — updating to ${version}…`);
639
- } catch {
640
- // not linked yet — proceed to install it
641
687
  }
642
688
 
643
689
  const attempts = version ? [`agentic-relay@${version}`, `"${pkgRoot}"`] : [`"${pkgRoot}"`];
@@ -664,18 +710,25 @@ function linkGlobalCli() {
664
710
  return;
665
711
  }
666
712
 
667
- // Verify it actually resolves — npm's global bin dir may not be on this shell's PATH.
713
+ // Verify the global bin actually landed. Don't probe the bare command here:
714
+ // in this process it resolves to the ephemeral npx shim (which shadows the
715
+ // global install), so check the file in npm's global prefix directly.
716
+ let binDir = null;
717
+ let globalBin = null;
668
718
  try {
669
- execSync("agentrelay version", { stdio: "ignore" });
670
- success(`linked global \`agentrelay\` CLI (${installedFrom})`);
719
+ const prefix = execSync("npm prefix -g", { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
720
+ binDir = process.platform === "win32" ? prefix : join(prefix, "bin");
721
+ const cand = join(binDir, process.platform === "win32" ? "agentrelay.cmd" : "agentrelay");
722
+ if (existsSync(cand)) globalBin = cand;
671
723
  } catch {
672
- let binDir = "npm's global bin directory";
673
- try {
674
- binDir = execSync("npm prefix -g", { encoding: "utf-8" }).trim() + "/bin";
675
- } catch {
676
- // keep the generic phrasing
677
- }
678
- warn(`installed, but \`agentrelay\` doesn't resolve — add ${binDir} to your PATH`);
724
+ // can't resolve the prefix fall through to the warn
725
+ }
726
+ if (globalBin) {
727
+ success(`linked global \`agentrelay\` CLI (${installedFrom})`);
728
+ const onPath = (process.env.PATH || "").split(process.platform === "win32" ? ";" : ":").includes(binDir);
729
+ if (!onPath) warn(`note: ${binDir} doesn't appear to be on your PATH — add it to use \`agentrelay\` directly`);
730
+ } else {
731
+ warn(`installed, but no global \`agentrelay\` found in ${binDir || "npm's global bin directory"} — run once: npm i -g agentic-relay`);
679
732
  }
680
733
  }
681
734
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-relay",
3
- "version": "3.8.4",
3
+ "version": "3.8.5",
4
4
  "description": "Install Agent Relay AI search across 17+ agents — Claude Code, Codex, Cursor, Gemini CLI, Goose, Windsurf, Cline, BoltAI, Claude Desktop, VS Code, Amazon Q, Roo Code, Witsy, LibreChat, OpenClaw, Tome, Raycast — plus the `agentrelay` CLI for token-cheap parallel agent consulting",
5
5
  "bin": {
6
6
  "agentrelay": "bin/cli.mjs"