tui.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import { Global } from "../../global"
  2. import { Provider } from "../../provider/provider"
  3. import { Server } from "../../server/server"
  4. import { bootstrap } from "../bootstrap"
  5. import { UI } from "../ui"
  6. import { cmd } from "./cmd"
  7. import path from "path"
  8. import fs from "fs/promises"
  9. import { Installation } from "../../installation"
  10. import { Config } from "../../config/config"
  11. import { Bus } from "../../bus"
  12. export const TuiCommand = cmd({
  13. command: "$0 [project]",
  14. describe: "start opencode tui",
  15. builder: (yargs) =>
  16. yargs.positional("project", {
  17. type: "string",
  18. describe: "path to start opencode in",
  19. }),
  20. handler: async (args) => {
  21. while (true) {
  22. const cwd = args.project ? path.resolve(args.project) : process.cwd()
  23. try {
  24. process.chdir(cwd)
  25. } catch (e) {
  26. UI.error("Failed to change directory to " + cwd)
  27. return
  28. }
  29. const result = await bootstrap({ cwd }, async (app) => {
  30. const providers = await Provider.list()
  31. if (Object.keys(providers).length === 0) {
  32. return "needs_provider"
  33. }
  34. const server = Server.listen({
  35. port: 0,
  36. hostname: "127.0.0.1",
  37. })
  38. let cmd = ["go", "run", "./main.go"]
  39. let cwd = Bun.fileURLToPath(
  40. new URL("../../../../tui/cmd/opencode", import.meta.url),
  41. )
  42. if (Bun.embeddedFiles.length > 0) {
  43. const blob = Bun.embeddedFiles[0] as File
  44. let binaryName = blob.name
  45. if (process.platform === "win32" && !binaryName.endsWith(".exe")) {
  46. binaryName += ".exe"
  47. }
  48. const binary = path.join(Global.Path.cache, "tui", binaryName)
  49. const file = Bun.file(binary)
  50. if (!(await file.exists())) {
  51. await Bun.write(file, blob, { mode: 0o755 })
  52. await fs.chmod(binary, 0o755)
  53. }
  54. cwd = process.cwd()
  55. cmd = [binary]
  56. }
  57. const proc = Bun.spawn({
  58. cmd: [...cmd, ...process.argv.slice(2)],
  59. cwd,
  60. stdout: "inherit",
  61. stderr: "inherit",
  62. stdin: "inherit",
  63. env: {
  64. ...process.env,
  65. OPENCODE_SERVER: server.url.toString(),
  66. OPENCODE_APP_INFO: JSON.stringify(app),
  67. },
  68. onExit: () => {
  69. server.stop()
  70. },
  71. })
  72. ;(async () => {
  73. if (Installation.VERSION === "dev") return
  74. if (Installation.isSnapshot()) return
  75. const config = await Config.global()
  76. if (config.autoupdate === false) return
  77. const latest = await Installation.latest().catch(() => {})
  78. if (!latest) return
  79. if (Installation.VERSION === latest) return
  80. const method = await Installation.method()
  81. if (method === "unknown") return
  82. await Installation.upgrade(method, latest)
  83. .then(() => {
  84. Bus.publish(Installation.Event.Updated, { version: latest })
  85. })
  86. .catch(() => {})
  87. })()
  88. await proc.exited
  89. server.stop()
  90. return "done"
  91. })
  92. if (result === "done") break
  93. if (result === "needs_provider") {
  94. UI.empty()
  95. UI.println(UI.logo(" "))
  96. const result = await Bun.spawn({
  97. cmd: [process.execPath, "auth", "login"],
  98. cwd: process.cwd(),
  99. stdout: "inherit",
  100. stderr: "inherit",
  101. stdin: "inherit",
  102. }).exited
  103. if (result !== 0) return
  104. UI.empty()
  105. }
  106. }
  107. },
  108. })