








@@ -30,6 +30,37 @@ function isDirectory(dirPath: string): boolean {
3030}
3131}
323233+function splitPathParts(pathEnv: string): Set<string> {
34+return new Set(
35+pathEnv
36+.split(path.delimiter)
37+.map((part) => part.trim())
38+.filter(Boolean),
39+);
40+}
41+42+function isKnownPathDir(existingPathParts: ReadonlySet<string>, dirPath: string): boolean {
43+return existingPathParts.has(dirPath) || isDirectory(dirPath);
44+}
45+46+function isLinuxbrewPath(dirPath: string): boolean {
47+return dirPath.split(path.sep).includes(".linuxbrew");
48+}
49+50+function resolvePathBootstrapBrewDirs(params: {
51+homeDir: string;
52+platform: NodeJS.Platform;
53+existingPathParts: ReadonlySet<string>;
54+}): string[] {
55+const candidates = resolveBrewPathDirs({ homeDir: params.homeDir });
56+if (params.platform !== "darwin") {
57+return candidates;
58+}
59+return candidates.filter(
60+(candidate) => !isLinuxbrewPath(candidate) || params.existingPathParts.has(candidate),
61+);
62+}
63+3364function mergePath(params: { existing: string; prepend?: string[]; append?: string[] }): string {
3465const partsExisting = params.existing
3566.split(path.delimiter)
@@ -49,7 +80,10 @@ function mergePath(params: { existing: string; prepend?: string[]; append?: stri
4980return merged.join(path.delimiter);
5081}
518252-function candidateBinDirs(opts: EnsureOpenClawPathOpts): { prepend: string[]; append: string[] } {
83+function candidateBinDirs(
84+opts: EnsureOpenClawPathOpts,
85+existingPathParts: ReadonlySet<string>,
86+): { prepend: string[]; append: string[] } {
5387const execPath = opts.execPath ?? process.execPath;
5488const cwd = opts.cwd ?? process.cwd();
5589const homeDir = opts.homeDir ?? os.homedir();
@@ -100,10 +134,10 @@ function candidateBinDirs(opts: EnsureOpenClawPathOpts): { prepend: string[]; ap
100134// shadow trusted OS binaries.
101135// This includes Brew/Homebrew dirs, which are useful for finding `openclaw`
102136// in launchd/minimal environments but must not be treated as trusted.
103-append.push(...resolveBrewPathDirs({ homeDir }));
137+append.push(...resolvePathBootstrapBrewDirs({ homeDir, platform, existingPathParts }));
104138const miseDataDir = process.env.MISE_DATA_DIR ?? path.join(homeDir, ".local", "share", "mise");
105139const miseShims = path.join(miseDataDir, "shims");
106-if (isDirectory(miseShims)) {
140+if (isKnownPathDir(existingPathParts, miseShims)) {
107141append.push(miseShims);
108142}
109143if (platform === "darwin") {
@@ -117,7 +151,10 @@ function candidateBinDirs(opts: EnsureOpenClawPathOpts): { prepend: string[]; ap
117151append.push(path.join(homeDir, ".bun", "bin"));
118152append.push(path.join(homeDir, ".yarn", "bin"));
119153120-return { prepend: prepend.filter(isDirectory), append: append.filter(isDirectory) };
154+return {
155+prepend: prepend.filter((candidate) => isKnownPathDir(existingPathParts, candidate)),
156+append: append.filter((candidate) => isKnownPathDir(existingPathParts, candidate)),
157+};
121158}
122159123160/**
@@ -131,7 +168,8 @@ export function ensureOpenClawCliOnPath(opts: EnsureOpenClawPathOpts = {}) {
131168process.env.OPENCLAW_PATH_BOOTSTRAPPED = "1";
132169133170const existing = opts.pathEnv ?? process.env.PATH ?? "";
134-const { prepend, append } = candidateBinDirs(opts);
171+const existingPathParts = splitPathParts(existing);
172+const { prepend, append } = candidateBinDirs(opts, existingPathParts);
135173if (prepend.length === 0 && append.length === 0) {
136174return;
137175}
此內容由慣性聚合(RSS閱讀器)自動聚合整理,僅供閱讀參考。 原文來自 — 版權歸原作者所有。