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.
- package/cli.mjs +122 -29
- 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
|
|
774
|
+
// ── Auto-register MCP server with mcporter ────────────────────────
|
|
775
775
|
// Writes directly to ~/.mcporter/mcporter.json for reliability.
|
|
776
776
|
//
|
|
777
|
-
//
|
|
778
|
-
//
|
|
779
|
-
//
|
|
780
|
-
// the
|
|
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
|
|
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
|
-
//
|
|
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
|
-
|
|
797
|
-
|
|
798
|
-
|
|
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
|
-
|
|
802
|
-
|
|
803
|
-
//
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
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
|
-
|
|
810
|
-
|
|
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)
|
|
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
|
|