ソースを参照

fix: accept workspace create payload without extra (#25371)

Kit Langton 3 ヶ月 前
コミット
481f8667a4

+ 4 - 1
packages/opencode/src/server/routes/instance/httpapi/groups/workspace.ts

@@ -9,7 +9,10 @@ import { WorkspaceRoutingMiddleware } from "../middleware/workspace-routing"
 import { described } from "./metadata"
 
 const root = "/experimental/workspace"
-export const CreatePayload = Schema.Struct(Struct.omit(Workspace.CreateInput.fields, ["projectID"]))
+export const CreatePayload = Schema.Struct({
+  ...Struct.omit(Workspace.CreateInput.fields, ["projectID", "extra"]),
+  extra: Schema.optional(Workspace.CreateInput.fields.extra),
+})
 export const SessionRestorePayload = Schema.Struct(Struct.omit(Workspace.SessionRestoreInput.fields, ["workspaceID"]))
 export const SessionRestoreResponse = Schema.Struct({
   total: NonNegativeInt,

+ 1 - 0
packages/opencode/src/server/routes/instance/httpapi/handlers/workspace.ts

@@ -24,6 +24,7 @@ export const workspaceHandlers = HttpApiBuilder.group(InstanceHttpApi, "workspac
       return yield* workspace
         .create({
           ...ctx.payload,
+          extra: ctx.payload.extra ?? null,
           projectID: instance.project.id,
         })
         .pipe(Effect.mapError(() => new HttpApiError.BadRequest({})))

+ 51 - 2
packages/opencode/test/server/httpapi-workspace.test.ts

@@ -27,9 +27,9 @@ const it = testEffect(
   Layer.mergeAll(NodeServices.layer, Project.defaultLayer, Session.defaultLayer, Workspace.defaultLayer),
 )
 
-function request(path: string, directory: string, init: RequestInit = {}) {
+function request(path: string, directory: string, init: RequestInit = {}, httpApi = true) {
   return Effect.promise(() => {
-    Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = true
+    Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = httpApi
     const headers = new Headers(init.headers)
     headers.set("x-opencode-directory", directory)
     return Promise.resolve(Server.Default().app.request(path, { ...init, headers }))
@@ -195,6 +195,55 @@ describe("workspace HttpApi", () => {
     }),
   )
 
+  it.live("creates workspace with the TUI payload shape", () =>
+    Effect.gen(function* () {
+      Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = true
+      const dir = yield* tmpdirScoped({ git: true })
+      const project = yield* Project.use.fromDirectory(dir)
+      registerAdapter(project.project.id, "local-test", localAdapter(path.join(dir, ".workspace")))
+
+      const created = yield* request(WorkspacePaths.list, dir, {
+        method: "POST",
+        headers: { "content-type": "application/json" },
+        body: JSON.stringify({ type: "local-test", branch: null }),
+      })
+
+      expect(created.status).toBe(200)
+      expect((yield* Effect.promise(() => created.json())) as Workspace.Info).toMatchObject({
+        type: "local-test",
+        name: "local-test",
+        extra: null,
+      })
+    }),
+  )
+
+  it.live("documents legacy Hono accepting the TUI payload shape", () =>
+    Effect.gen(function* () {
+      Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = true
+      const dir = yield* tmpdirScoped({ git: true })
+      const project = yield* Project.use.fromDirectory(dir)
+      registerAdapter(project.project.id, "local-test", localAdapter(path.join(dir, ".workspace")))
+
+      const created = yield* request(
+        WorkspacePaths.list,
+        dir,
+        {
+          method: "POST",
+          headers: { "content-type": "application/json" },
+          body: JSON.stringify({ type: "local-test", branch: null }),
+        },
+        false,
+      )
+
+      expect(created.status).toBe(200)
+      expect((yield* Effect.promise(() => created.json())) as Workspace.Info).toMatchObject({
+        type: "local-test",
+        name: "local-test",
+        extra: null,
+      })
+    }),
+  )
+
   it.live("routes local workspace requests through the workspace target directory", () =>
     Effect.gen(function* () {
       Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = true