6c691a1afa
Upstream OpenCode's Global.Path.bin resolves to ~/.cache/opencode/bin (XDG cache), where its ripgrep auto-installer and many LSP servers land. OMO's getOpenCodeBundledRg only checked the data-dir variant (~/.local/share/opencode/bin), so when OpenCode had already downloaded rg into its cache directory OMO would skip it and either redownload via its own fallback installer or fall back to system grep. Probe the cache-backed bin path first so OMO reuses tools OpenCode has already installed.
126 lines
3.3 KiB
TypeScript
126 lines
3.3 KiB
TypeScript
import { spawnSync } from "node:child_process"
|
|
import { existsSync } from "node:fs"
|
|
import { dirname, join } from "node:path"
|
|
import { downloadAndInstallRipgrep, getInstalledRipgrepPath } from "../tools/grep/downloader"
|
|
import { getDataDir, getOpenCodeCacheDir } from "./data-path"
|
|
import { log } from "./logger"
|
|
import { PUBLISHED_PACKAGE_NAME } from "./plugin-identity"
|
|
|
|
export type GrepBackend = "rg" | "grep"
|
|
|
|
export interface ResolvedCli {
|
|
path: string
|
|
backend: GrepBackend
|
|
}
|
|
|
|
export const DEFAULT_RG_THREADS = 4
|
|
|
|
let cachedCli: ResolvedCli | null = null
|
|
let autoInstallAttempted = false
|
|
|
|
function findExecutable(name: string): string | null {
|
|
const isWindows = process.platform === "win32"
|
|
const cmd = isWindows ? "where.exe" : "which"
|
|
|
|
try {
|
|
// #3919: Keep Windows executable probes hidden and shell-free in Desktop utility processes.
|
|
const result = spawnSync(cmd, [name], {
|
|
encoding: "utf-8",
|
|
timeout: 5000,
|
|
windowsHide: isWindows,
|
|
shell: false,
|
|
})
|
|
const stdout = result.stdout
|
|
if (result.status === 0 && stdout.trim()) {
|
|
return stdout.trim().split("\n")[0]
|
|
}
|
|
} catch {
|
|
return null
|
|
}
|
|
return null
|
|
}
|
|
|
|
function getOpenCodeBundledRg(): string | null {
|
|
const execPath = process.execPath
|
|
const execDir = dirname(execPath)
|
|
|
|
const isWindows = process.platform === "win32"
|
|
const rgName = isWindows ? "rg.exe" : "rg"
|
|
|
|
const candidates = [
|
|
// #3805: Upstream OpenCode's Global.Path.bin is cache-backed (~/.cache/opencode/bin),
|
|
// and its auto-downloaded ripgrep + LSP binaries live there. Probe it first so OMO
|
|
// reuses tools OpenCode already installed instead of triggering a duplicate download.
|
|
join(getOpenCodeCacheDir(), "bin", rgName),
|
|
join(getDataDir(), "opencode", "bin", rgName),
|
|
join(execDir, rgName),
|
|
join(execDir, "bin", rgName),
|
|
join(execDir, "..", "bin", rgName),
|
|
join(execDir, "..", "libexec", rgName),
|
|
]
|
|
|
|
for (const candidate of candidates) {
|
|
if (existsSync(candidate)) {
|
|
return candidate
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export function resolveGrepCli(): ResolvedCli {
|
|
if (cachedCli) {
|
|
return cachedCli
|
|
}
|
|
|
|
const rgPath = getOpenCodeBundledRg() ?? findExecutable("rg") ?? getInstalledRipgrepPath()
|
|
if (rgPath) {
|
|
cachedCli = { path: rgPath, backend: "rg" }
|
|
return cachedCli
|
|
}
|
|
|
|
const grep = findExecutable("grep")
|
|
if (grep) {
|
|
cachedCli = { path: grep, backend: "grep" }
|
|
return cachedCli
|
|
}
|
|
|
|
cachedCli = { path: "rg", backend: "rg" }
|
|
return cachedCli
|
|
}
|
|
|
|
export async function resolveGrepCliWithAutoInstall(): Promise<ResolvedCli> {
|
|
const current = resolveGrepCli()
|
|
|
|
if (current.backend === "rg" && current.path !== "rg") {
|
|
return current
|
|
}
|
|
|
|
if (autoInstallAttempted) {
|
|
return current
|
|
}
|
|
|
|
autoInstallAttempted = true
|
|
|
|
try {
|
|
const rgPath = await downloadAndInstallRipgrep()
|
|
cachedCli = { path: rgPath, backend: "rg" }
|
|
return cachedCli
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error)
|
|
|
|
if (current.backend === "grep") {
|
|
log(`[${PUBLISHED_PACKAGE_NAME}] Failed to auto-install ripgrep. Falling back to GNU grep.`, {
|
|
error: message,
|
|
grep_path: current.path,
|
|
})
|
|
} else {
|
|
log(`[${PUBLISHED_PACKAGE_NAME}] Failed to auto-install ripgrep and GNU grep was not found.`, {
|
|
error: message,
|
|
})
|
|
}
|
|
|
|
return current
|
|
}
|
|
}
|