index.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  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(
  41. `Share ID: ${Share.URL.replace("api.", "")}/share?id=${session.id}`,
  42. );
  43. let index = 0;
  44. Bus.subscribe(Storage.Event.Write, async (payload) => {
  45. const [root, , type, messageID] = payload.properties.key.split("/");
  46. if (root !== "session" && type !== "message") return;
  47. const message = await Session.messages(session.id).then((x) =>
  48. x.find((x) => x.id === messageID),
  49. );
  50. if (!message) return;
  51. for (; index < message.parts.length; index++) {
  52. const part = message.parts[index];
  53. if (part.type === "text") continue;
  54. if (part.type === "step-start") continue;
  55. if (
  56. part.type === "tool-invocation" &&
  57. part.toolInvocation.state !== "result"
  58. )
  59. break;
  60. if (part.type === "tool-invocation") {
  61. console.log(`🔧 ${part.toolInvocation.toolName}`);
  62. if (
  63. part.toolInvocation.state === "result" &&
  64. "result" in part.toolInvocation
  65. ) {
  66. const result = part.toolInvocation.result;
  67. if (typeof result === "string") {
  68. const lines = result.split("\n");
  69. const truncated = lines.slice(0, 4);
  70. if (lines.length > 4) truncated.push("...");
  71. console.log(truncated.join("\n"));
  72. } else if (result && typeof result === "object") {
  73. const jsonStr = JSON.stringify(result, null, 2);
  74. const lines = jsonStr.split("\n");
  75. const truncated = lines.slice(0, 4);
  76. if (lines.length > 4) truncated.push("...");
  77. console.log(truncated.join("\n"));
  78. }
  79. }
  80. continue;
  81. }
  82. console.log(part);
  83. }
  84. });
  85. const result = await Session.chat(session.id, {
  86. type: "text",
  87. text: message.join(" "),
  88. });
  89. for (const part of result.parts) {
  90. if (part.type === "text") {
  91. console.log("opencode:", part.text);
  92. }
  93. }
  94. });
  95. });
  96. cli.help();
  97. cli.version("1.0.0");
  98. cli.parse();