index.ts 1.8 KB

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