openclaw-navigator 4.3.5 → 4.3.7

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/cli.mjs +122 -29
  2. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -771,56 +771,66 @@ ${BOLD}How it works:${RESET}
771
771
 
772
772
  ok(`MCP server started (PID ${mcpProcess.pid}) — bridge: http://localhost:${port}`);
773
773
 
774
- // ── Auto-register MCP server with mcporter (if available) ─────────
774
+ // ── Auto-register MCP server with mcporter ────────────────────────
775
775
  // Writes directly to ~/.mcporter/mcporter.json for reliability.
776
776
  //
777
- // KEY FIX: We resolve stable paths instead of using process.execPath
778
- // which may point to a temp npx cache (/Users/x/.npm/_npx/<hash>/node)
779
- // that disappears after npx exits. We find the REAL node binary and
780
- // the globally-installed mcp.mjs (or realpath of the current one).
777
+ // npx runs from a temp cache dir (.npm/_npx/<hash>/) that disappears.
778
+ // To get stable paths we:
779
+ // 1. Install openclaw-navigator globally (npm i -g)
780
+ // 2. Use the global install path for mcporter config
781
+ // 3. If global install fails, find node's real path via `which node`
781
782
  try {
782
- const { readFileSync, writeFileSync, mkdirSync, realpathSync } = await import("node:fs");
783
+ const { readFileSync, writeFileSync, mkdirSync } = await import("node:fs");
784
+ const { execSync } = await import("node:child_process");
783
785
  const { homedir } = await import("node:os");
784
786
 
785
- // Resolve stable node binary (follow symlinks out of npx temp dir)
787
+ // Step 1: Install globally to get a stable path
786
788
  let stableNode;
787
- try {
788
- stableNode = realpathSync(process.execPath);
789
- } catch {
790
- stableNode = process.execPath;
791
- }
792
-
793
- // Resolve stable mcp.mjs path
794
789
  let stableMcpPath;
790
+
795
791
  try {
796
- stableMcpPath = realpathSync(mcpPath);
797
- } catch {
798
- stableMcpPath = mcpPath;
799
- }
792
+ info(" Installing openclaw-navigator globally for stable paths...");
793
+ execSync("npm install -g openclaw-navigator@latest", {
794
+ stdio: ["ignore", "ignore", "ignore"],
795
+ timeout: 30000,
796
+ });
797
+
798
+ // Find the global node and mcp.mjs
799
+ const globalPrefix = execSync("npm prefix -g", { encoding: "utf8" }).trim();
800
+ stableMcpPath = join(globalPrefix, "lib/node_modules/openclaw-navigator/mcp.mjs");
801
+ // Find node via `which` — always gives the real PATH-resolved binary
802
+ stableNode = execSync("which node", { encoding: "utf8" }).trim();
803
+
804
+ if (!existsSync(stableMcpPath)) {
805
+ throw new Error(`Global mcp.mjs not found at ${stableMcpPath}`);
806
+ }
800
807
 
801
- // If paths are still in npx temp cache, try to find the real install
802
- if (stableMcpPath.includes("_npx") || stableMcpPath.includes(".npm/_npx")) {
803
- // Check if openclaw-navigator is installed globally
804
- const nodeDir = dirname(stableNode);
805
- const globalMcp = join(dirname(nodeDir), "lib/node_modules/openclaw-navigator/mcp.mjs");
806
- if (existsSync(globalMcp)) {
807
- stableMcpPath = globalMcp;
808
+ ok(`Global install: ${stableMcpPath}`);
809
+ } catch (installErr) {
810
+ // Global install failed (permissions?) fallback to which node + current mcpPath
811
+ warn(`Global install failed: ${installErr.message}`);
812
+ try {
813
+ stableNode = execSync("which node", { encoding: "utf8" }).trim();
814
+ } catch {
815
+ stableNode = process.execPath;
808
816
  }
809
- // If still temp, that's OK — mcporter will use the path we give it
810
- // and the bridge running alongside keeps the MCP server alive anyway
817
+ stableMcpPath = mcpPath;
818
+ info(` Falling back to: ${stableNode} ${stableMcpPath}`);
811
819
  }
812
820
 
813
821
  info(` mcporter: node=${stableNode}`);
814
822
  info(` mcporter: mcp=${stableMcpPath}`);
815
823
 
816
- // Write directly to ~/.mcporter/mcporter.json
824
+ // Step 2: Write directly to ~/.mcporter/mcporter.json
817
825
  const mcporterDir = join(homedir(), ".mcporter");
818
826
  const mcporterConfigPath = join(mcporterDir, "mcporter.json");
819
827
 
820
828
  let mcporterConfig = { mcpServers: {}, imports: [] };
821
829
  try {
822
830
  mcporterConfig = JSON.parse(readFileSync(mcporterConfigPath, "utf8"));
823
- if (!mcporterConfig.mcpServers) mcporterConfig.mcpServers = {};
831
+ if (!mcporterConfig.mcpServers) {
832
+ mcporterConfig.mcpServers = {};
833
+ }
824
834
  } catch {
825
835
  // File doesn't exist or invalid — start fresh
826
836
  }
@@ -841,6 +851,89 @@ ${BOLD}How it works:${RESET}
841
851
  info(" You can manually configure mcporter for Navigator MCP");
842
852
  }
843
853
 
854
+ // ── Install launchd auto-start (macOS only) ──────────────────────────
855
+ // Creates ~/Library/LaunchAgents/com.openclaw.navigator-bridge.plist
856
+ // so the bridge + MCP server starts automatically on login.
857
+ if (process.platform === "darwin") {
858
+ try {
859
+ const {
860
+ writeFileSync: writePlist,
861
+ existsSync: plistExists,
862
+ mkdirSync: mkPlist,
863
+ } = await import("node:fs");
864
+ const { homedir: homeDir } = await import("node:os");
865
+ const home = homeDir();
866
+ const launchAgentsDir = join(home, "Library/LaunchAgents");
867
+ const plistPath = join(launchAgentsDir, "com.openclaw.navigator-bridge.plist");
868
+
869
+ // Find stable node/npx paths via `which` (not temp npx cache)
870
+ const { execSync: whichExec } = await import("node:child_process");
871
+ let nodeForPlist;
872
+ try {
873
+ nodeForPlist = whichExec("which node", { encoding: "utf8" }).trim();
874
+ } catch {
875
+ nodeForPlist = process.execPath;
876
+ }
877
+ const npxForPlist = join(dirname(nodeForPlist), "npx");
878
+
879
+ const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
880
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
881
+ <plist version="1.0">
882
+ <dict>
883
+ <key>Label</key>
884
+ <string>com.openclaw.navigator-bridge</string>
885
+ <key>ProgramArguments</key>
886
+ <array>
887
+ <string>${npxForPlist}</string>
888
+ <string>openclaw-navigator@latest</string>
889
+ <string>--mcp</string>
890
+ <string>--port</string>
891
+ <string>${port}</string>
892
+ </array>
893
+ <key>EnvironmentVariables</key>
894
+ <dict>
895
+ <key>PATH</key>
896
+ <string>${dirname(nodeForPlist)}:/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>
897
+ </dict>
898
+ <key>RunAtLoad</key>
899
+ <true/>
900
+ <key>KeepAlive</key>
901
+ <true/>
902
+ <key>StandardOutPath</key>
903
+ <string>${home}/.openclaw/navigator-bridge.log</string>
904
+ <key>StandardErrorPath</key>
905
+ <string>${home}/.openclaw/navigator-bridge.log</string>
906
+ <key>WorkingDirectory</key>
907
+ <string>${home}</string>
908
+ </dict>
909
+ </plist>
910
+ `;
911
+ mkPlist(launchAgentsDir, { recursive: true });
912
+ mkPlist(join(home, ".openclaw"), { recursive: true });
913
+ writePlist(plistPath, plistContent, "utf8");
914
+
915
+ // Load the service (unload first if already loaded)
916
+ try {
917
+ const { execSync: ex } = await import("node:child_process");
918
+ try {
919
+ ex(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
920
+ } catch {
921
+ /* not loaded */
922
+ }
923
+ // Don't load now — we're already running. It'll start on next login.
924
+ } catch {
925
+ /* launchctl not available */
926
+ }
927
+
928
+ ok("Auto-start installed — bridge will start on login");
929
+ info(` Plist: ${plistPath}`);
930
+ info(` Logs: ~/.openclaw/navigator-bridge.log`);
931
+ info(` To disable: launchctl unload "${plistPath}"`);
932
+ } catch (err) {
933
+ info(` Auto-start setup skipped: ${err.message}`);
934
+ }
935
+ }
936
+
844
937
  console.log("");
845
938
  }
846
939
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-navigator",
3
- "version": "4.3.5",
3
+ "version": "4.3.7",
4
4
  "description": "One-command bridge + tunnel for the Navigator browser — works on any machine, any OS",
5
5
  "keywords": [
6
6
  "browser",