index.ts 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import { BusEvent } from "@/bus/bus-event"
  2. import { Bus } from "@/bus"
  3. import { spawn } from "bun"
  4. import z from "zod"
  5. import { NamedError } from "@opencode-ai/util/error"
  6. import { Log } from "../util/log"
  7. const SUPPORTED_IDES = [
  8. { name: "Windsurf" as const, cmd: "windsurf" },
  9. { name: "Visual Studio Code - Insiders" as const, cmd: "code-insiders" },
  10. { name: "Visual Studio Code" as const, cmd: "code" },
  11. { name: "Cursor" as const, cmd: "cursor" },
  12. { name: "VSCodium" as const, cmd: "codium" },
  13. ]
  14. export namespace Ide {
  15. const log = Log.create({ service: "ide" })
  16. export const Event = {
  17. Installed: BusEvent.define(
  18. "ide.installed",
  19. z.object({
  20. ide: z.string(),
  21. }),
  22. ),
  23. }
  24. export const AlreadyInstalledError = NamedError.create("AlreadyInstalledError", z.object({}))
  25. export const InstallFailedError = NamedError.create(
  26. "InstallFailedError",
  27. z.object({
  28. stderr: z.string(),
  29. }),
  30. )
  31. export function ide() {
  32. if (process.env["TERM_PROGRAM"] === "vscode") {
  33. const v = process.env["GIT_ASKPASS"]
  34. for (const ide of SUPPORTED_IDES) {
  35. if (v?.includes(ide.name)) return ide.name
  36. }
  37. }
  38. return "unknown"
  39. }
  40. export function alreadyInstalled() {
  41. return process.env["OPENCODE_CALLER"] === "vscode" || process.env["OPENCODE_CALLER"] === "vscode-insiders"
  42. }
  43. export async function install(ide: (typeof SUPPORTED_IDES)[number]["name"]) {
  44. const cmd = SUPPORTED_IDES.find((i) => i.name === ide)?.cmd
  45. if (!cmd) throw new Error(`Unknown IDE: ${ide}`)
  46. const p = spawn([cmd, "--install-extension", "sst-dev.opencode"], {
  47. stdout: "pipe",
  48. stderr: "pipe",
  49. })
  50. await p.exited
  51. const stdout = await new Response(p.stdout).text()
  52. const stderr = await new Response(p.stderr).text()
  53. log.info("installed", {
  54. ide,
  55. stdout,
  56. stderr,
  57. })
  58. if (p.exitCode !== 0) {
  59. throw new InstallFailedError({ stderr })
  60. }
  61. if (stdout.includes("already installed")) {
  62. throw new AlreadyInstalledError({})
  63. }
  64. }
  65. }