index.ts 3.6 KB

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