kirincode 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. #!/usr/bin/env node
  2. const childProcess = require("child_process")
  3. const fs = require("fs")
  4. const path = require("path")
  5. const os = require("os")
  6. const forwardedSignals = ["SIGINT", "SIGTERM", "SIGHUP"]
  7. function run(target) {
  8. const child = childProcess.spawn(target, process.argv.slice(2), {
  9. stdio: "inherit",
  10. })
  11. child.on("error", (error) => {
  12. console.error(error.message)
  13. process.exit(1)
  14. })
  15. const forwarders = {}
  16. for (const signal of forwardedSignals) {
  17. forwarders[signal] = () => {
  18. try {
  19. child.kill(signal)
  20. } catch {
  21. // The child may have already exited.
  22. }
  23. }
  24. process.on(signal, forwarders[signal])
  25. }
  26. child.on("exit", (code, signal) => {
  27. for (const forwardedSignal of forwardedSignals) {
  28. process.removeListener(forwardedSignal, forwarders[forwardedSignal])
  29. }
  30. if (signal) {
  31. process.kill(process.pid, signal)
  32. return
  33. }
  34. process.exit(typeof code === "number" ? code : 0)
  35. })
  36. }
  37. const envPath = process.env.OPENCODE_BIN_PATH
  38. const scriptPath = fs.realpathSync(__filename)
  39. const scriptDir = path.dirname(scriptPath)
  40. //
  41. const cached = path.join(scriptDir, ".opencode")
  42. const platformMap = {
  43. darwin: "darwin",
  44. linux: "linux",
  45. win32: "windows",
  46. }
  47. const archMap = {
  48. x64: "x64",
  49. arm64: "arm64",
  50. arm: "arm",
  51. }
  52. let platform = platformMap[os.platform()]
  53. if (!platform) {
  54. platform = os.platform()
  55. }
  56. let arch = archMap[os.arch()]
  57. if (!arch) {
  58. arch = os.arch()
  59. }
  60. const base = "opencode-" + platform + "-" + arch
  61. const binary = platform === "windows" ? "opencode.exe" : "opencode"
  62. function supportsAvx2() {
  63. if (arch !== "x64") return false
  64. if (platform === "linux") {
  65. try {
  66. return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
  67. } catch {
  68. return false
  69. }
  70. }
  71. if (platform === "darwin") {
  72. try {
  73. const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], {
  74. encoding: "utf8",
  75. timeout: 1500,
  76. })
  77. if (result.status !== 0) return false
  78. return (result.stdout || "").trim() === "1"
  79. } catch {
  80. return false
  81. }
  82. }
  83. if (platform === "windows") {
  84. const cmd =
  85. '(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
  86. for (const exe of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
  87. try {
  88. const result = childProcess.spawnSync(exe, ["-NoProfile", "-NonInteractive", "-Command", cmd], {
  89. encoding: "utf8",
  90. timeout: 3000,
  91. windowsHide: true,
  92. })
  93. if (result.status !== 0) continue
  94. const out = (result.stdout || "").trim().toLowerCase()
  95. if (out === "true" || out === "1") return true
  96. if (out === "false" || out === "0") return false
  97. } catch {
  98. continue
  99. }
  100. }
  101. return false
  102. }
  103. return false
  104. }
  105. const names = (() => {
  106. const avx2 = supportsAvx2()
  107. const baseline = arch === "x64" && !avx2
  108. if (platform === "linux") {
  109. const musl = (() => {
  110. try {
  111. if (fs.existsSync("/etc/alpine-release")) return true
  112. } catch {
  113. // ignore
  114. }
  115. try {
  116. const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
  117. const text = ((result.stdout || "") + (result.stderr || "")).toLowerCase()
  118. if (text.includes("musl")) return true
  119. } catch {
  120. // ignore
  121. }
  122. return false
  123. })()
  124. if (musl) {
  125. if (arch === "x64") {
  126. if (baseline) return [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
  127. return [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
  128. }
  129. return [`${base}-musl`, base]
  130. }
  131. if (arch === "x64") {
  132. if (baseline) return [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
  133. return [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
  134. }
  135. return [base, `${base}-musl`]
  136. }
  137. if (arch === "x64") {
  138. if (baseline) return [`${base}-baseline`, base]
  139. return [base, `${base}-baseline`]
  140. }
  141. return [base]
  142. })()
  143. function findBinary(startDir) {
  144. let current = startDir
  145. for (;;) {
  146. const modules = path.join(current, "node_modules")
  147. if (fs.existsSync(modules)) {
  148. for (const name of names) {
  149. const candidate = path.join(modules, name, "bin", binary)
  150. if (fs.existsSync(candidate)) return candidate
  151. }
  152. }
  153. const parent = path.dirname(current)
  154. if (parent === current) {
  155. return
  156. }
  157. current = parent
  158. }
  159. }
  160. const resolved = envPath || (fs.existsSync(cached) ? cached : findBinary(scriptDir))
  161. if (!resolved) {
  162. console.error(
  163. "It seems that your package manager failed to install the right version of the opencode CLI for your platform. You can try manually installing " +
  164. names.map((n) => `\"${n}\"`).join(" or ") +
  165. " package",
  166. )
  167. process.exit(1)
  168. }
  169. run(resolved)