Dax Raad 6 месяцев назад
Родитель
Сommit
7505fa61b9
2 измененных файлов с 122 добавлено и 0 удалено
  1. 30 0
      packages/opencode/src/server/server.ts
  2. 92 0
      packages/opencode/src/session/index.ts

+ 30 - 0
packages/opencode/src/server/server.ts

@@ -592,6 +592,36 @@ export namespace Server {
           return c.json(msg)
         },
       )
+      .post(
+        "/session/:id/command",
+        describeRoute({
+          description: "Run a bash command",
+          operationId: "session.chat",
+          responses: {
+            200: {
+              description: "Created message",
+              content: {
+                "application/json": {
+                  schema: resolver(MessageV2.Assistant),
+                },
+              },
+            },
+          },
+        }),
+        zValidator(
+          "param",
+          z.object({
+            id: z.string().openapi({ description: "Session ID" }),
+          }),
+        ),
+        zValidator("json", Session.CommandInput.omit({ sessionID: true })),
+        async (c) => {
+          const sessionID = c.req.valid("param").id
+          const body = c.req.valid("json")
+          const msg = await Session.command({ ...body, sessionID })
+          return c.json(msg)
+        },
+      )
       .post(
         "/session/:id/revert",
         describeRoute({

+ 92 - 0
packages/opencode/src/session/index.ts

@@ -43,6 +43,8 @@ import { Plugin } from "../plugin"
 import { Agent } from "../agent/agent"
 import { Permission } from "../permission"
 import { Wildcard } from "../util/wildcard"
+import { BashTool } from "../tool/bash"
+import { ulid } from "ulid"
 
 export namespace Session {
   const log = Log.create({ service: "session" })
@@ -997,6 +999,96 @@ export namespace Session {
     return result
   }
 
+  export const CommandInput = z.object({
+    sessionID: Identifier.schema("session"),
+    agent: z.string(),
+    command: z.string(),
+  })
+  export type CommandInput = z.infer<typeof CommandInput>
+  export async function command(input: CommandInput) {
+    using abort = lock(input.sessionID)
+    const msg: MessageV2.Assistant = {
+      id: Identifier.ascending("message"),
+      sessionID: input.sessionID,
+      system: [],
+      mode: input.agent,
+      cost: 0,
+      path: {
+        cwd: App.info().path.cwd,
+        root: App.info().path.root,
+      },
+      time: {
+        created: Date.now(),
+      },
+      role: "assistant",
+      tokens: {
+        input: 0,
+        output: 0,
+        reasoning: 0,
+        cache: { read: 0, write: 0 },
+      },
+      modelID: "",
+      providerID: "",
+    }
+    await updateMessage(msg)
+    const part: MessageV2.Part = {
+      type: "tool",
+      id: Identifier.ascending("part"),
+      messageID: msg.id,
+      sessionID: input.sessionID,
+      tool: "bash",
+      callID: ulid(),
+      state: {
+        status: "running",
+        time: {
+          start: Date.now(),
+        },
+        input: {
+          command: input.command,
+        },
+      },
+    }
+    await updatePart(part)
+    const tool = await BashTool.init()
+    const result = await tool.execute(
+      {
+        command: input.command,
+        description: "User command",
+      },
+      {
+        messageID: msg.id,
+        sessionID: input.sessionID,
+        abort: abort.signal,
+        callID: part.callID,
+        agent: input.agent,
+        metadata: async (e) => {
+          if (part.state.status === "running") {
+            part.state.title = e.title
+            part.state.metadata = e.metadata
+            await updatePart(part)
+          }
+        },
+      },
+    )
+    msg.time.completed = Date.now()
+    await updateMessage(msg)
+    if (part.state.status === "running") {
+      part.state = {
+        status: "completed",
+        time: {
+          ...part.state.time,
+          end: Date.now(),
+        },
+        input: part.state.input,
+        title: result.title,
+        metadata: result.metadata,
+        output: result.output,
+      }
+      await updatePart(part)
+    }
+    return { info: msg, parts: [part] }
+  }
+
   function createProcessor(assistantMsg: MessageV2.Assistant, model: ModelsDev.Model) {
     const toolcalls: Record<string, MessageV2.ToolPart> = {}
     let snapshot: string | undefined