fix(vscode): avoid POSIX login shell opencode detection (#921)

This commit is contained in:
Yifan
2026-04-16 16:31:07 +03:00
committed by GitHub
parent 4f228f768d
commit d0fa9d5a5f
+55 -52
View File
@@ -127,6 +127,34 @@ function appendToPath(dir: string) {
process.env.PATH = [trimmed, ...parts].join(path.delimiter); process.env.PATH = [trimmed, ...parts].join(path.delimiter);
} }
function findExecutableInPath(binaryName: string): string | null {
const trimmed = (binaryName || '').trim();
if (!trimmed) {
return null;
}
const current = process.env.PATH || '';
if (!current) {
return null;
}
for (const segment of current.split(path.delimiter)) {
const dir = segment.trim();
if (!dir) {
continue;
}
const candidate = path.join(dir, trimmed);
if (isExecutable(candidate)) {
return candidate;
}
}
return null;
}
let cachedDetectedOpencodeCliPath: string | undefined;
function resolveOpencodeCliPath(): string | null { function resolveOpencodeCliPath(): string | null {
const configured = (() => { const configured = (() => {
try { try {
@@ -185,11 +213,20 @@ function resolveOpencodeCliPath(): string | null {
} }
} }
if (cachedDetectedOpencodeCliPath) {
if (isExecutable(cachedDetectedOpencodeCliPath)) {
return cachedDetectedOpencodeCliPath;
}
cachedDetectedOpencodeCliPath = undefined;
}
const home = os.homedir(); const home = os.homedir();
const unixFallbacks = [ const unixFallbacks = [
path.join(home, '.opencode', 'bin', 'opencode'), path.join(home, '.opencode', 'bin', 'opencode'),
path.join(home, '.bun', 'bin', 'opencode'), path.join(home, '.bun', 'bin', 'opencode'),
path.join(home, '.local', 'bin', 'opencode'), path.join(home, '.local', 'bin', 'opencode'),
'/usr/local/bin/opencode',
'/opt/homebrew/bin/opencode',
path.join(home, 'bin', 'opencode'), path.join(home, 'bin', 'opencode'),
]; ];
@@ -214,9 +251,18 @@ function resolveOpencodeCliPath(): string | null {
].filter(Boolean); ].filter(Boolean);
})(); })();
if (process.platform !== 'win32') {
const fromPath = findExecutableInPath('opencode');
if (fromPath) {
cachedDetectedOpencodeCliPath = fromPath;
return fromPath;
}
}
const fallbacks = process.platform === 'win32' ? winFallbacks : unixFallbacks; const fallbacks = process.platform === 'win32' ? winFallbacks : unixFallbacks;
for (const candidate of fallbacks) { for (const candidate of fallbacks) {
if (isExecutable(candidate)) { if (isExecutable(candidate)) {
cachedDetectedOpencodeCliPath = candidate;
return candidate; return candidate;
} }
} }
@@ -233,26 +279,8 @@ function resolveOpencodeCliPath(): string | null {
.map((line) => line.trim()) .map((line) => line.trim())
.filter(Boolean); .filter(Boolean);
const found = lines.find((line) => isExecutable(line)); const found = lines.find((line) => isExecutable(line));
if (found) return found; if (found) {
} cachedDetectedOpencodeCliPath = found;
} catch {
// ignore
}
return null;
}
// Non-Windows: try a login shell PATH lookup.
const shells = [process.env.SHELL, '/bin/zsh', '/bin/bash', '/bin/sh'].filter(Boolean) as string[];
for (const shell of shells) {
if (!isExecutable(shell)) continue;
try {
const result = spawnSync(shell, ['-lic', 'command -v opencode'], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
if (result.status === 0) {
const found = (result.stdout || '').trim().split(/\s+/).pop() || '';
if (found && isExecutable(found)) {
return found; return found;
} }
} }
@@ -377,40 +405,15 @@ function getLoginShellEnvSnapshot(): Record<string, string> | null {
return cachedLoginShellEnvSnapshot; return cachedLoginShellEnvSnapshot;
} }
if (process.platform === 'win32') { // Avoid interactive POSIX login shells in the extension host.
const windowsSnapshot = getWindowsShellEnvSnapshot(); if (process.platform !== 'win32') {
cachedLoginShellEnvSnapshot = windowsSnapshot; cachedLoginShellEnvSnapshot = null;
return windowsSnapshot; return null;
} }
const shellCandidates = [process.env.SHELL, '/bin/zsh', '/bin/bash', '/bin/sh'].filter(Boolean) as string[]; const windowsSnapshot = getWindowsShellEnvSnapshot();
for (const shellPath of shellCandidates) { cachedLoginShellEnvSnapshot = windowsSnapshot;
if (!isExecutable(shellPath)) { return windowsSnapshot;
continue;
}
try {
const result = spawnSync(shellPath, ['-lic', 'env -0'], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
maxBuffer: 10 * 1024 * 1024,
windowsHide: true,
});
if (result.status !== 0) {
continue;
}
const parsed = parseNullSeparatedEnvSnapshot(result.stdout || '');
if (parsed) {
cachedLoginShellEnvSnapshot = parsed;
return parsed;
}
} catch {
continue;
}
}
cachedLoginShellEnvSnapshot = null;
return null;
} }
function mergePathValues(preferred: string, fallback: string): string { function mergePathValues(preferred: string, fallback: string): string {