index.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  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 { Bus } from "./bus"
  7. import { Session } from "./session/session"
  8. import cac from "cac"
  9. import { Share } from "./share/share"
  10. import { Message } from "./session/message"
  11. import { Global } from "./global"
  12. import { Provider } from "./provider/provider"
  13. declare global {
  14. const OPENCODE_VERSION: string
  15. }
  16. const cli = cac("opencode")
  17. const version = typeof OPENCODE_VERSION === "string" ? OPENCODE_VERSION : "dev"
  18. cli.command("", "Start the opencode in interactive mode").action(async () => {
  19. await App.provide({ cwd: process.cwd(), version }, async () => {
  20. await Share.init()
  21. const server = Server.listen()
  22. let cmd = ["go", "run", "./main.go"]
  23. let cwd = new URL("../../tui", import.meta.url).pathname
  24. if (Bun.embeddedFiles.length > 0) {
  25. const blob = Bun.embeddedFiles[0] as File
  26. const binary = path.join(Global.cache(), "tui", blob.name)
  27. const file = Bun.file(binary)
  28. if (!(await file.exists())) {
  29. console.log("installing tui binary...")
  30. await Bun.write(file, blob, { mode: 0o755 })
  31. await fs.chmod(binary, 0o755)
  32. }
  33. cwd = process.cwd()
  34. cmd = [binary]
  35. }
  36. const proc = Bun.spawn({
  37. cmd,
  38. cwd,
  39. stdout: "inherit",
  40. stderr: "inherit",
  41. stdin: "inherit",
  42. onExit: () => {
  43. server.stop()
  44. },
  45. })
  46. await proc.exited
  47. await server.stop()
  48. })
  49. })
  50. cli.command("generate", "Generate OpenAPI and event specs").action(async () => {
  51. const specs = await Server.openapi()
  52. const dir = "gen"
  53. await fs.rmdir(dir, { recursive: true }).catch(() => {})
  54. await fs.mkdir(dir, { recursive: true })
  55. await Bun.write(
  56. path.join(dir, "openapi.json"),
  57. JSON.stringify(specs, null, 2),
  58. )
  59. })
  60. cli
  61. .command("run [...message]", "Run a chat message")
  62. .option("--session <id>", "Session ID")
  63. .action(async (message: string[], options) => {
  64. await App.provide({ cwd: process.cwd(), version }, async () => {
  65. await Share.init()
  66. const session = options.session
  67. ? await Session.get(options.session)
  68. : await Session.create()
  69. console.log("Session:", session.id)
  70. Bus.subscribe(Message.Event.Updated, async () => {
  71. console.log("Thinking...")
  72. })
  73. const unsub = Bus.subscribe(Session.Event.Updated, async (message) => {
  74. if (message.properties.info.share?.url)
  75. console.log("Share:", message.properties.info.share.url)
  76. unsub()
  77. })
  78. const [provider] = await Provider.active().then((val) =>
  79. val.values().toArray(),
  80. )
  81. if (!provider) throw new Error("no providers found")
  82. const model = provider.models[0]
  83. if (!model) throw new Error("no models found")
  84. console.log("using", provider.id, model.id)
  85. const result = await Session.chat({
  86. sessionID: session.id,
  87. providerID: provider.id,
  88. modelID: model.id,
  89. parts: [
  90. {
  91. type: "text",
  92. text: message.join(" "),
  93. },
  94. ],
  95. })
  96. for (const part of result.parts) {
  97. if (part.type === "text") {
  98. console.log("opencode:", part.text)
  99. }
  100. }
  101. console.log({
  102. cost: result.metadata.assistant?.cost,
  103. tokens: result.metadata.assistant?.tokens,
  104. })
  105. })
  106. })
  107. cli.version(typeof OPENCODE_VERSION === "string" ? OPENCODE_VERSION : "dev")
  108. cli.help()
  109. cli.parse()