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);
|
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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user