Преглед изворни кода

feat(httpapi): bridge worktree read endpoint (#24366)

Kit Langton пре 4 месеци
родитељ
комит
b749866f0b

+ 1 - 1
packages/opencode/specs/effect/http-api.md

@@ -164,7 +164,7 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho
 | `mcp`                    | `bridged` partial | status only                                                                           |
 | `workspace`              | `bridged`         | list, get, enter                                                                      |
 | top-level instance reads | `bridged`         | path, vcs, command, agent, skill, lsp, formatter                                      |
-| experimental JSON routes | `bridged` partial | console reads, tool ids, resource list; worktree and global session list remain later |
+| experimental JSON routes | `bridged` partial | console reads, tool ids, worktree list, resource list; global session list remains later |
 | `session`                | `later/special`   | large stateful surface plus streaming                                                 |
 | `sync`                   | `later`           | process/control side effects                                                          |
 | `event`                  | `special`         | SSE                                                                                   |

+ 22 - 0
packages/opencode/src/server/routes/instance/httpapi/experimental.ts

@@ -1,6 +1,8 @@
 import { Account } from "@/account/account"
 import { Config } from "@/config"
+import { InstanceState } from "@/effect"
 import { MCP } from "@/mcp"
+import { Project } from "@/project"
 import { ToolRegistry } from "@/tool"
 import { Effect, Layer, Option, Schema } from "effect"
 import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
@@ -27,10 +29,13 @@ const ConsoleOrgList = Schema.Struct({
 
 const ToolIDs = Schema.Array(Schema.String).annotate({ identifier: "ToolIDs" })
 
+const WorktreeList = Schema.Array(Schema.String).annotate({ identifier: "WorktreeList" })
+
 export const ExperimentalPaths = {
   console: "/experimental/console",
   consoleOrgs: "/experimental/console/orgs",
   toolIDs: "/experimental/tool/ids",
+  worktree: "/experimental/worktree",
   resource: "/experimental/resource",
 } as const
 
@@ -66,6 +71,15 @@ export const ExperimentalApi = HttpApi.make("experimental")
               "Get a list of all available tool IDs, including both built-in tools and dynamically registered tools.",
           }),
         ),
+        HttpApiEndpoint.get("worktree", ExperimentalPaths.worktree, {
+          success: WorktreeList,
+        }).annotateMerge(
+          OpenApi.annotations({
+            identifier: "worktree.list",
+            summary: "List worktrees",
+            description: "List all sandbox worktrees for the current project.",
+          }),
+        ),
         HttpApiEndpoint.get("resource", ExperimentalPaths.resource, {
           success: Schema.Record(Schema.String, MCP.Resource),
         }).annotateMerge(
@@ -97,6 +111,7 @@ export const experimentalHandlers = Layer.unwrap(
     const account = yield* Account.Service
     const config = yield* Config.Service
     const mcp = yield* MCP.Service
+    const project = yield* Project.Service
     const registry = yield* ToolRegistry.Service
 
     const getConsole = Effect.fn("ExperimentalHttpApi.console")(function* () {
@@ -139,6 +154,11 @@ export const experimentalHandlers = Layer.unwrap(
       return yield* registry.ids()
     })
 
+    const worktree = Effect.fn("ExperimentalHttpApi.worktree")(function* () {
+      const ctx = yield* InstanceState.context
+      return yield* project.sandboxes(ctx.project.id)
+    })
+
     const resource = Effect.fn("ExperimentalHttpApi.resource")(function* () {
       return yield* mcp.resources()
     })
@@ -148,6 +168,7 @@ export const experimentalHandlers = Layer.unwrap(
         .handle("console", getConsole)
         .handle("consoleOrgs", listConsoleOrgs)
         .handle("toolIDs", toolIDs)
+        .handle("worktree", worktree)
         .handle("resource", resource),
     )
   }),
@@ -155,5 +176,6 @@ export const experimentalHandlers = Layer.unwrap(
   Layer.provide(Account.defaultLayer),
   Layer.provide(Config.defaultLayer),
   Layer.provide(MCP.defaultLayer),
+  Layer.provide(Project.defaultLayer),
   Layer.provide(ToolRegistry.defaultLayer),
 )

+ 1 - 0
packages/opencode/src/server/routes/instance/index.ts

@@ -49,6 +49,7 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
     app.get(ExperimentalPaths.console, (c) => handler(c.req.raw, context))
     app.get(ExperimentalPaths.consoleOrgs, (c) => handler(c.req.raw, context))
     app.get(ExperimentalPaths.toolIDs, (c) => handler(c.req.raw, context))
+    app.get(ExperimentalPaths.worktree, (c) => handler(c.req.raw, context))
     app.get(ExperimentalPaths.resource, (c) => handler(c.req.raw, context))
     app.get("/provider", (c) => handler(c.req.raw, context))
     app.get("/provider/auth", (c) => handler(c.req.raw, context))

+ 5 - 1
packages/opencode/test/server/httpapi-experimental.test.ts

@@ -41,10 +41,11 @@ describe("experimental HttpApi", () => {
     })
 
     const headers = { "x-opencode-directory": tmp.path }
-    const [consoleState, consoleOrgs, toolIDs, resources] = await Promise.all([
+    const [consoleState, consoleOrgs, toolIDs, worktrees, resources] = await Promise.all([
       app().request(ExperimentalPaths.console, { headers }),
       app().request(ExperimentalPaths.consoleOrgs, { headers }),
       app().request(ExperimentalPaths.toolIDs, { headers }),
+      app().request(ExperimentalPaths.worktree, { headers }),
       app().request(ExperimentalPaths.resource, { headers }),
     ])
 
@@ -60,6 +61,9 @@ describe("experimental HttpApi", () => {
     expect(toolIDs.status).toBe(200)
     expect(await toolIDs.json()).toContain("bash")
 
+    expect(worktrees.status).toBe(200)
+    expect(await worktrees.json()).toEqual([])
+
     expect(resources.status).toBe(200)
     expect(await resources.json()).toEqual({})
   })