index.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import { App } from "./app";
  2. import { Server } from "./server/server";
  3. import fs from "node:fs/promises";
  4. import path from "node:path";
  5. import { Bus } from "./bus";
  6. import { Session } from "./session/session";
  7. import cac from "cac";
  8. import { Share } from "./share/share";
  9. import { Storage } from "./storage/storage";
  10. const cli = cac("opencode");
  11. cli.command("", "Start the opencode in interactive mode").action(async () => {
  12. await App.provide({ directory: process.cwd() }, async () => {
  13. await Share.init();
  14. Server.listen();
  15. });
  16. });
  17. cli.command("generate", "Generate OpenAPI and event specs").action(async () => {
  18. const specs = await Server.openapi();
  19. const dir = "gen";
  20. await fs.rmdir(dir, { recursive: true }).catch(() => {});
  21. await fs.mkdir(dir, { recursive: true });
  22. await Bun.write(
  23. path.join(dir, "openapi.json"),
  24. JSON.stringify(specs, null, 2),
  25. );
  26. await Bun.write(
  27. path.join(dir, "event.json"),
  28. JSON.stringify(Bus.specs(), null, 2),
  29. );
  30. });
  31. cli
  32. .command("run [...message]", "Run a chat message")
  33. .action(async (message: string[]) => {
  34. await App.provide({ directory: process.cwd() }, async () => {
  35. console.log("Thinking...");
  36. await Share.init();
  37. const session = await Session.create();
  38. const shareID = await Session.share(session.id);
  39. if (shareID)
  40. console.log("Share ID: https://dev.opencode.ai/share?id=" + session.id);
  41. let index = 0;
  42. Bus.subscribe(Storage.Event.Write, async (payload) => {
  43. const [root, , type, messageID] = payload.properties.key.split("/");
  44. if (root !== "session" && type !== "message") return;
  45. const message = await Session.messages(session.id).then((x) =>
  46. x.find((x) => x.id === messageID),
  47. );
  48. if (!message) return;
  49. for (; index < message.parts.length; index++) {
  50. const part = message.parts[index];
  51. if (part.type === "text") continue;
  52. if (part.type === "step-start") continue;
  53. if (
  54. part.type === "tool-invocation" &&
  55. part.toolInvocation.state !== "result"
  56. )
  57. break;
  58. if (part.type === "tool-invocation") {
  59. console.log(`🔧 ${part.toolInvocation.toolName}`);
  60. if (
  61. part.toolInvocation.state === "result" &&
  62. "result" in part.toolInvocation
  63. ) {
  64. const result = part.toolInvocation.result;
  65. if (typeof result === "string") {
  66. const lines = result.split("\n");
  67. const truncated = lines.slice(0, 4);
  68. if (lines.length > 4) truncated.push("...");
  69. console.log(truncated.join("\n"));
  70. } else if (result && typeof result === "object") {
  71. const jsonStr = JSON.stringify(result, null, 2);
  72. const lines = jsonStr.split("\n");
  73. const truncated = lines.slice(0, 4);
  74. if (lines.length > 4) truncated.push("...");
  75. console.log(truncated.join("\n"));
  76. }
  77. }
  78. continue;
  79. }
  80. console.log(part);
  81. }
  82. });
  83. const result = await Session.chat(session.id, {
  84. type: "text",
  85. text: message.join(" "),
  86. });
  87. for (const part of result.parts) {
  88. if (part.type === "text") {
  89. console.log("opencode:", part.text);
  90. }
  91. }
  92. });
  93. });
  94. cli.help();
  95. cli.version("1.0.0");
  96. cli.parse();