index.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import "zod-openapi/extend"
  2. import { App } from "./app/app"
  3. import { Server } from "./server/server"
  4. import fs from "fs/promises"
  5. import path from "path"
  6. import { Share } from "./share/share"
  7. import { Global } from "./global"
  8. import yargs from "yargs"
  9. import { hideBin } from "yargs/helpers"
  10. import { RunCommand } from "./cli/cmd/run"
  11. import { GenerateCommand } from "./cli/cmd/generate"
  12. import { VERSION } from "./cli/version"
  13. import { ScrapCommand } from "./cli/cmd/scrap"
  14. import { Log } from "./util/log"
  15. import { AuthCommand, AuthLoginCommand } from "./cli/cmd/auth"
  16. import { Provider } from "./provider/provider"
  17. import { UI } from "./cli/ui"
  18. const cli = yargs(hideBin(process.argv))
  19. .scriptName("opencode")
  20. .version(VERSION)
  21. .option("print-logs", {
  22. describe: "Print logs to stderr",
  23. type: "boolean",
  24. })
  25. .middleware(async () => {
  26. await Log.init({ print: process.argv.includes("--print-logs") })
  27. Log.Default.info("opencode", {
  28. version: VERSION,
  29. args: process.argv.slice(2),
  30. })
  31. })
  32. .usage("\n" + UI.logo())
  33. .command({
  34. command: "$0 [project]",
  35. describe: "Start OpenCode TUI",
  36. builder: (yargs) =>
  37. yargs.positional("project", {
  38. type: "string",
  39. describe: "path to start opencode in",
  40. }),
  41. handler: async (args) => {
  42. while (true) {
  43. const cwd = args.project ? path.resolve(args.project) : process.cwd()
  44. process.chdir(cwd)
  45. const result = await App.provide(
  46. { cwd, version: VERSION },
  47. async () => {
  48. const providers = await Provider.list()
  49. if (Object.keys(providers).length === 0) {
  50. return "needs_provider"
  51. }
  52. await Share.init()
  53. const server = Server.listen()
  54. let cmd = ["go", "run", "./main.go"]
  55. let cwd = new URL("../../tui/cmd/opencode", import.meta.url)
  56. .pathname
  57. if (Bun.embeddedFiles.length > 0) {
  58. const blob = Bun.embeddedFiles[0] as File
  59. const binary = path.join(Global.Path.cache, "tui", blob.name)
  60. const file = Bun.file(binary)
  61. if (!(await file.exists())) {
  62. await Bun.write(file, blob, { mode: 0o755 })
  63. await fs.chmod(binary, 0o755)
  64. }
  65. cwd = process.cwd()
  66. cmd = [binary]
  67. }
  68. const proc = Bun.spawn({
  69. cmd: [...cmd, ...process.argv.slice(2)],
  70. cwd,
  71. stdout: "inherit",
  72. stderr: "inherit",
  73. stdin: "inherit",
  74. env: {
  75. ...process.env,
  76. OPENCODE_SERVER: server.url.toString(),
  77. },
  78. onExit: () => {
  79. server.stop()
  80. },
  81. })
  82. await proc.exited
  83. await server.stop()
  84. return "done"
  85. },
  86. )
  87. if (result === "done") break
  88. if (result === "needs_provider") {
  89. UI.empty()
  90. UI.println(UI.logo(" "))
  91. UI.empty()
  92. await AuthLoginCommand.handler(args)
  93. }
  94. }
  95. },
  96. })
  97. .command(RunCommand)
  98. .command(GenerateCommand)
  99. .command(ScrapCommand)
  100. .command(AuthCommand)
  101. .fail((msg, err) => {
  102. if (
  103. msg.startsWith("Unknown argument") ||
  104. msg.startsWith("Not enough non-option arguments")
  105. ) {
  106. cli.showHelp("log")
  107. }
  108. Log.Default.error(msg, {
  109. err,
  110. })
  111. })
  112. .strict()
  113. try {
  114. await cli.parse()
  115. } catch (e) {
  116. Log.Default.error(e)
  117. }