agentic-relay 3.8.3 → 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 +113 -12
  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,24 +610,125 @@ function getExistingKey() {
610
610
 
611
611
  /**
612
612
  * Link a bare global `agentrelay` command so users skip `npx` after install.
613
- * Installs the exact package copy being run (works whether invoked via npx or a
614
- * checkout). No-ops if `agentrelay` already resolves; warns + falls back to a
615
- * manual instruction if the global prefix isn't writable (EACCES etc.).
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`.
616
628
  */
617
- function linkGlobalCli() {
629
+ function resolveCommandPath(cmd) {
618
630
  try {
619
- execSync("agentrelay version", { stdio: "ignore" });
620
- success("`agentrelay` already on your PATH");
621
- return;
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);
622
660
  } catch {
623
- // not linked yet — proceed to install it
661
+ return p;
624
662
  }
663
+ }
664
+
665
+ function linkGlobalCli() {
625
666
  const pkgRoot = join(__dirname, "..");
667
+ let version = null;
626
668
  try {
627
- execSync(`npm install -g "${pkgRoot}"`, { stdio: "ignore" });
628
- success("linked global `agentrelay` CLI");
669
+ version = JSON.parse(readFileSync(join(pkgRoot, "package.json"), "utf-8")).version;
629
670
  } catch {
630
- warn("could not auto-link the global CLI (permissions?). Run once: npm i -g agentic-relay");
671
+ // unknown version fall through to the path install
672
+ }
673
+
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
686
+ }
687
+ }
688
+
689
+ const attempts = version ? [`agentic-relay@${version}`, `"${pkgRoot}"`] : [`"${pkgRoot}"`];
690
+ let lastErr = null;
691
+ let installedFrom = null;
692
+ for (const spec of attempts) {
693
+ try {
694
+ execSync(`npm install -g ${spec}`, { stdio: ["ignore", "ignore", "pipe"], encoding: "utf-8" });
695
+ installedFrom = spec;
696
+ break;
697
+ } catch (err) {
698
+ lastErr = err;
699
+ }
700
+ }
701
+ if (!installedFrom) {
702
+ const stderr = String((lastErr && lastErr.stderr) || "");
703
+ const detail =
704
+ stderr.split("\n").find((l) => /EACCES|EEXIST|E404|ERR!|error/i.test(l)) || "";
705
+ warn(
706
+ `could not auto-link the global CLI${detail ? ` — ${detail.trim()}` : ""}.` +
707
+ ` Run once: npm i -g agentic-relay` +
708
+ (/EACCES/.test(stderr) ? " (npm's global prefix isn't writable — fix permissions or use a user-level prefix)" : ""),
709
+ );
710
+ return;
711
+ }
712
+
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;
718
+ try {
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;
723
+ } catch {
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`);
631
732
  }
632
733
  }
633
734
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-relay",
3
- "version": "3.8.3",
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"