fix(vscode): avoid POSIX login shell opencode detection (#921)
This commit is contained in:
@@ -127,6 +127,34 @@ function appendToPath(dir: string) {
|
||||
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 {
|
||||
const configured = (() => {
|
||||
try {
|
||||
@@ -185,11 +213,20 @@ function resolveOpencodeCliPath(): string | null {
|
||||
}
|
||||
}
|
||||
|
||||
if (cachedDetectedOpencodeCliPath) {
|
||||
if (isExecutable(cachedDetectedOpencodeCliPath)) {
|
||||
return cachedDetectedOpencodeCliPath;
|
||||
}
|
||||
cachedDetectedOpencodeCliPath = undefined;
|
||||
}
|
||||
|
||||
const home = os.homedir();
|
||||
const unixFallbacks = [
|
||||
path.join(home, '.opencode', 'bin', 'opencode'),
|
||||
path.join(home, '.bun', 'bin', 'opencode'),
|
||||
path.join(home, '.local', 'bin', 'opencode'),
|
||||
'/usr/local/bin/opencode',
|
||||
'/opt/homebrew/bin/opencode',
|
||||
path.join(home, 'bin', 'opencode'),
|
||||
];
|
||||
|
||||
@@ -214,9 +251,18 @@ function resolveOpencodeCliPath(): string | null {
|
||||
].filter(Boolean);
|
||||
})();
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
const fromPath = findExecutableInPath('opencode');
|
||||
if (fromPath) {
|
||||
cachedDetectedOpencodeCliPath = fromPath;
|
||||
return fromPath;
|
||||
}
|
||||
}
|
||||
|
||||
const fallbacks = process.platform === 'win32' ? winFallbacks : unixFallbacks;
|
||||
for (const candidate of fallbacks) {
|
||||
if (isExecutable(candidate)) {
|
||||
cachedDetectedOpencodeCliPath = candidate;
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
@@ -233,26 +279,8 @@ function resolveOpencodeCliPath(): string | null {
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean);
|
||||
const found = lines.find((line) => isExecutable(line));
|
||||
if (found) return 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)) {
|
||||
if (found) {
|
||||
cachedDetectedOpencodeCliPath = found;
|
||||
return found;
|
||||
}
|
||||
}
|
||||
@@ -377,40 +405,15 @@ function getLoginShellEnvSnapshot(): Record<string, string> | null {
|
||||
return cachedLoginShellEnvSnapshot;
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const windowsSnapshot = getWindowsShellEnvSnapshot();
|
||||
cachedLoginShellEnvSnapshot = windowsSnapshot;
|
||||
return windowsSnapshot;
|
||||
// Avoid interactive POSIX login shells in the extension host.
|
||||
if (process.platform !== 'win32') {
|
||||
cachedLoginShellEnvSnapshot = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
const shellCandidates = [process.env.SHELL, '/bin/zsh', '/bin/bash', '/bin/sh'].filter(Boolean) as string[];
|
||||
for (const shellPath of shellCandidates) {
|
||||
if (!isExecutable(shellPath)) {
|
||||
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;
|
||||
const windowsSnapshot = getWindowsShellEnvSnapshot();
|
||||
cachedLoginShellEnvSnapshot = windowsSnapshot;
|
||||
return windowsSnapshot;
|
||||
}
|
||||
|
||||
function mergePathValues(preferred: string, fallback: string): string {
|
||||
|
||||
Reference in New Issue
Block a user