constants.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import { existsSync } from "node:fs"
  2. import { join, dirname } from "node:path"
  3. import { spawnSync } from "node:child_process"
  4. import { getInstalledRipgrepPath, downloadAndInstallRipgrep } from "./downloader"
  5. export type GrepBackend = "rg" | "grep"
  6. interface ResolvedCli {
  7. path: string
  8. backend: GrepBackend
  9. }
  10. let cachedCli: ResolvedCli | null = null
  11. let autoInstallAttempted = false
  12. function findExecutable(name: string): string | null {
  13. const isWindows = process.platform === "win32"
  14. const cmd = isWindows ? "where" : "which"
  15. try {
  16. const result = spawnSync(cmd, [name], { encoding: "utf-8", timeout: 5000 })
  17. if (result.status === 0 && result.stdout.trim()) {
  18. return result.stdout.trim().split("\n")[0]
  19. }
  20. } catch {
  21. // Command execution failed
  22. }
  23. return null
  24. }
  25. function getDataDir(): string {
  26. if (process.platform === "win32") {
  27. return process.env.LOCALAPPDATA || process.env.APPDATA || join(process.env.USERPROFILE || ".", "AppData", "Local")
  28. }
  29. return process.env.XDG_DATA_HOME || join(process.env.HOME || ".", ".local", "share")
  30. }
  31. function getOpenCodeBundledRg(): string | null {
  32. const execPath = process.execPath
  33. const execDir = dirname(execPath)
  34. const isWindows = process.platform === "win32"
  35. const rgName = isWindows ? "rg.exe" : "rg"
  36. const candidates = [
  37. // OpenCode XDG data path (highest priority - where OpenCode installs rg)
  38. join(getDataDir(), "opencode", "bin", rgName),
  39. // Legacy paths relative to execPath
  40. join(execDir, rgName),
  41. join(execDir, "bin", rgName),
  42. join(execDir, "..", "bin", rgName),
  43. join(execDir, "..", "libexec", rgName),
  44. ]
  45. for (const candidate of candidates) {
  46. if (existsSync(candidate)) {
  47. return candidate
  48. }
  49. }
  50. return null
  51. }
  52. export function resolveGrepCli(): ResolvedCli {
  53. if (cachedCli) return cachedCli
  54. const bundledRg = getOpenCodeBundledRg()
  55. if (bundledRg) {
  56. cachedCli = { path: bundledRg, backend: "rg" }
  57. return cachedCli
  58. }
  59. const systemRg = findExecutable("rg")
  60. if (systemRg) {
  61. cachedCli = { path: systemRg, backend: "rg" }
  62. return cachedCli
  63. }
  64. const installedRg = getInstalledRipgrepPath()
  65. if (installedRg) {
  66. cachedCli = { path: installedRg, backend: "rg" }
  67. return cachedCli
  68. }
  69. const grep = findExecutable("grep")
  70. if (grep) {
  71. cachedCli = { path: grep, backend: "grep" }
  72. return cachedCli
  73. }
  74. cachedCli = { path: "rg", backend: "rg" }
  75. return cachedCli
  76. }
  77. export async function resolveGrepCliWithAutoInstall(): Promise<ResolvedCli> {
  78. const current = resolveGrepCli()
  79. if (current.backend === "rg") {
  80. return current
  81. }
  82. if (autoInstallAttempted) {
  83. return current
  84. }
  85. autoInstallAttempted = true
  86. try {
  87. const rgPath = await downloadAndInstallRipgrep()
  88. cachedCli = { path: rgPath, backend: "rg" }
  89. return cachedCli
  90. } catch {
  91. return current
  92. }
  93. }
  94. export const DEFAULT_MAX_DEPTH = 20
  95. export const DEFAULT_MAX_FILESIZE = "10M"
  96. export const DEFAULT_MAX_COUNT = 500
  97. export const DEFAULT_MAX_COLUMNS = 1000
  98. export const DEFAULT_CONTEXT = 2
  99. export const DEFAULT_TIMEOUT_MS = 300_000
  100. export const DEFAULT_MAX_OUTPUT_BYTES = 10 * 1024 * 1024
  101. export const RG_SAFETY_FLAGS = [
  102. "--no-follow",
  103. "--color=never",
  104. "--no-heading",
  105. "--line-number",
  106. "--with-filename",
  107. ] as const
  108. export const GREP_SAFETY_FLAGS = ["-n", "-H", "--color=never"] as const