index.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. import yargs from "yargs"
  2. import { hideBin } from "yargs/helpers"
  3. import { RunCommand } from "./cli/cmd/run"
  4. import { GenerateCommand } from "./cli/cmd/generate"
  5. import { Log } from "./util/log"
  6. import { AuthCommand } from "./cli/cmd/auth"
  7. import { AgentCommand } from "./cli/cmd/agent"
  8. import { UpgradeCommand } from "./cli/cmd/upgrade"
  9. import { UninstallCommand } from "./cli/cmd/uninstall"
  10. import { ModelsCommand } from "./cli/cmd/models"
  11. import { UI } from "./cli/ui"
  12. import { Installation } from "./installation"
  13. import { NamedError } from "@opencode-ai/util/error"
  14. import { FormatError } from "./cli/error"
  15. import { ServeCommand } from "./cli/cmd/serve"
  16. import { DebugCommand } from "./cli/cmd/debug"
  17. import { StatsCommand } from "./cli/cmd/stats"
  18. import { McpCommand } from "./cli/cmd/mcp"
  19. import { GithubCommand } from "./cli/cmd/github"
  20. import { ExportCommand } from "./cli/cmd/export"
  21. import { ImportCommand } from "./cli/cmd/import"
  22. import { AttachCommand } from "./cli/cmd/tui/attach"
  23. import { TuiThreadCommand } from "./cli/cmd/tui/thread"
  24. import { AcpCommand } from "./cli/cmd/acp"
  25. import { EOL } from "os"
  26. import { WebCommand } from "./cli/cmd/web"
  27. import { PrCommand } from "./cli/cmd/pr"
  28. import { SessionCommand } from "./cli/cmd/session"
  29. process.on("unhandledRejection", (e) => {
  30. Log.Default.error("rejection", {
  31. e: e instanceof Error ? e.message : e,
  32. })
  33. })
  34. process.on("uncaughtException", (e) => {
  35. Log.Default.error("exception", {
  36. e: e instanceof Error ? e.message : e,
  37. })
  38. })
  39. const cli = yargs(hideBin(process.argv))
  40. .parserConfiguration({ "populate--": true })
  41. .scriptName("opencode")
  42. .wrap(100)
  43. .help("help", "show help")
  44. .alias("help", "h")
  45. .version("version", "show version number", Installation.VERSION)
  46. .alias("version", "v")
  47. .option("print-logs", {
  48. describe: "print logs to stderr",
  49. type: "boolean",
  50. })
  51. .option("log-level", {
  52. describe: "log level",
  53. type: "string",
  54. choices: ["DEBUG", "INFO", "WARN", "ERROR"],
  55. })
  56. .middleware(async (opts) => {
  57. await Log.init({
  58. print: process.argv.includes("--print-logs"),
  59. dev: Installation.isLocal(),
  60. level: (() => {
  61. if (opts.logLevel) return opts.logLevel as Log.Level
  62. if (Installation.isLocal()) return "DEBUG"
  63. return "INFO"
  64. })(),
  65. })
  66. process.env.AGENT = "1"
  67. process.env.OPENCODE = "1"
  68. Log.Default.info("opencode", {
  69. version: Installation.VERSION,
  70. args: process.argv.slice(2),
  71. })
  72. })
  73. .usage("\n" + UI.logo())
  74. .completion("completion", "generate shell completion script")
  75. .command(AcpCommand)
  76. .command(McpCommand)
  77. .command(TuiThreadCommand)
  78. .command(AttachCommand)
  79. .command(RunCommand)
  80. .command(GenerateCommand)
  81. .command(DebugCommand)
  82. .command(AuthCommand)
  83. .command(AgentCommand)
  84. .command(UpgradeCommand)
  85. .command(UninstallCommand)
  86. .command(ServeCommand)
  87. .command(WebCommand)
  88. .command(ModelsCommand)
  89. .command(StatsCommand)
  90. .command(ExportCommand)
  91. .command(ImportCommand)
  92. .command(GithubCommand)
  93. .command(PrCommand)
  94. .command(SessionCommand)
  95. .fail((msg, err) => {
  96. if (
  97. msg?.startsWith("Unknown argument") ||
  98. msg?.startsWith("Not enough non-option arguments") ||
  99. msg?.startsWith("Invalid values:")
  100. ) {
  101. if (err) throw err
  102. cli.showHelp("log")
  103. }
  104. if (err) throw err
  105. process.exit(1)
  106. })
  107. .strict()
  108. try {
  109. await cli.parse()
  110. } catch (e) {
  111. let data: Record<string, any> = {}
  112. if (e instanceof NamedError) {
  113. const obj = e.toObject()
  114. Object.assign(data, {
  115. ...obj.data,
  116. })
  117. }
  118. if (e instanceof Error) {
  119. Object.assign(data, {
  120. name: e.name,
  121. message: e.message,
  122. cause: e.cause?.toString(),
  123. stack: e.stack,
  124. })
  125. }
  126. if (e instanceof ResolveMessage) {
  127. Object.assign(data, {
  128. name: e.name,
  129. message: e.message,
  130. code: e.code,
  131. specifier: e.specifier,
  132. referrer: e.referrer,
  133. position: e.position,
  134. importKind: e.importKind,
  135. })
  136. }
  137. Log.Default.error("fatal", data)
  138. const formatted = FormatError(e)
  139. if (formatted) UI.error(formatted)
  140. if (formatted === undefined) {
  141. UI.error("Unexpected error, check log file at " + Log.file() + " for more details" + EOL)
  142. console.error(e instanceof Error ? e.message : String(e))
  143. }
  144. process.exitCode = 1
  145. } finally {
  146. // Some subprocesses don't react properly to SIGTERM and similar signals.
  147. // Most notably, some docker-container-based MCP servers don't handle such signals unless
  148. // run using `docker run --init`.
  149. // Explicitly exit to avoid any hanging subprocesses.
  150. process.exit()
  151. }