Browse Source

feat: output-format flag to stream json output (#2471)

Co-authored-by: Siddhant Choudhary <[email protected]>
Co-authored-by: rekram1-node <[email protected]>
Siddhant Choudhary 5 months ago
parent
commit
449994f120
1 changed files with 24 additions and 0 deletions
  1. 24 0
      packages/opencode/src/cli/cmd/run.ts

+ 24 - 0
packages/opencode/src/cli/cmd/run.ts

@@ -64,6 +64,12 @@ export const RunCommand = cmd({
         type: "string",
         type: "string",
         describe: "agent to use",
         describe: "agent to use",
       })
       })
+      .option("format", {
+        type: "string",
+        choices: ["default", "json"],
+        default: "default",
+        describe: "format: default (formatted) or json (raw JSON events)",
+      })
   },
   },
   handler: async (args) => {
   handler: async (args) => {
     let message = args.message.join(" ")
     let message = args.message.join(" ")
@@ -144,6 +150,20 @@ export const RunCommand = cmd({
         )
         )
       }
       }
 
 
+      function outputJsonEvent(type: string, data: any) {
+        if (args.format === "json") {
+          const jsonEvent = {
+            type,
+            timestamp: Date.now(),
+            sessionID: session?.id,
+            ...data,
+          }
+          process.stdout.write(JSON.stringify(jsonEvent) + "\n")
+          return true
+        }
+        return false
+      }
+
       let text = ""
       let text = ""
 
 
       Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => {
       Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => {
@@ -152,6 +172,7 @@ export const RunCommand = cmd({
         const part = evt.properties.part
         const part = evt.properties.part
 
 
         if (part.type === "tool" && part.state.status === "completed") {
         if (part.type === "tool" && part.state.status === "completed") {
+          if (outputJsonEvent("tool_use", { part })) return
           const [tool, color] = TOOL[part.tool] ?? [part.tool, UI.Style.TEXT_INFO_BOLD]
           const [tool, color] = TOOL[part.tool] ?? [part.tool, UI.Style.TEXT_INFO_BOLD]
           const title =
           const title =
             part.state.title ||
             part.state.title ||
@@ -169,6 +190,7 @@ export const RunCommand = cmd({
           text = part.text
           text = part.text
 
 
           if (part.time?.end) {
           if (part.time?.end) {
+            if (outputJsonEvent("text", { part })) return
             UI.empty()
             UI.empty()
             UI.println(UI.markdown(text))
             UI.println(UI.markdown(text))
             UI.empty()
             UI.empty()
@@ -189,6 +211,7 @@ export const RunCommand = cmd({
         }
         }
         errorMsg = errorMsg ? errorMsg + "\n" + err : err
         errorMsg = errorMsg ? errorMsg + "\n" + err : err
 
 
+        if (outputJsonEvent("error", { error })) return
         UI.error(err)
         UI.error(err)
       })
       })
 
 
@@ -225,6 +248,7 @@ export const RunCommand = cmd({
       const isPiped = !process.stdout.isTTY
       const isPiped = !process.stdout.isTTY
       if (isPiped) {
       if (isPiped) {
         const match = result.parts.findLast((x: any) => x.type === "text") as any
         const match = result.parts.findLast((x: any) => x.type === "text") as any
+        if (outputJsonEvent("text", { text: match })) return
         if (match) process.stdout.write(UI.markdown(match.text))
         if (match) process.stdout.write(UI.markdown(match.text))
         if (errorMsg) process.stdout.write(errorMsg)
         if (errorMsg) process.stdout.write(errorMsg)
       }
       }