From d353a6bc24f34ca4c247297df9f48389e570e27a Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Thu, 14 May 2026 14:25:22 -0400 Subject: [PATCH] fix(worktree): accept missing create payload (#27582) --- .../instance/httpapi/groups/experimental.ts | 3 +- .../server/worktree-endpoint-repro.test.ts | 44 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts index 160bafb1e3..4cda970e87 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts @@ -166,8 +166,9 @@ export const ExperimentalApi = HttpApi.make("experimental") }), ), HttpApiEndpoint.post("worktreeCreate", ExperimentalPaths.worktree, { + disableCodecs: true, query: WorkspaceRoutingQuery, - payload: Schema.optional(Worktree.CreateInput), + payload: Schema.UndefinedOr(Worktree.CreateInput), success: described(Worktree.Info, "Worktree created"), error: WorktreeApiError, }).annotateMerge( diff --git a/packages/opencode/test/server/worktree-endpoint-repro.test.ts b/packages/opencode/test/server/worktree-endpoint-repro.test.ts index a1f6bf45a8..10777e1724 100644 --- a/packages/opencode/test/server/worktree-endpoint-repro.test.ts +++ b/packages/opencode/test/server/worktree-endpoint-repro.test.ts @@ -129,6 +129,10 @@ function createWorktreeScoped(input: { input.timeoutLabel, input.timeoutMs, ) + if (response.status !== 200) { + const message = yield* Effect.promise(() => response.text()) + throw new Error(`${input.timeoutLabel} failed: ${response.status} ${message}`) + } expect(response.status).toBe(200) const body = yield* json(response) return { directory: body.directory, body, ready: waitReady(body.directory) } satisfies ScopedWorktree @@ -186,6 +190,46 @@ describe("worktree endpoint reproduction", () => { { git: true }, ) + worktreeTest( + "direct HttpApi worktree create accepts missing body", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const server = yield* serverScoped() + + const response = yield* createWorktreeScoped({ + server, + directory: test.directory, + path: `${ExperimentalPaths.worktree}?directory=${encodeURIComponent(test.directory)}`, + init: { method: "POST", headers: { "content-type": "application/json" } }, + timeoutLabel: "direct worktree create without body", + }) + + expect(response).toMatchObject({ directory: expect.any(String) }) + }), + { git: true }, + ) + + worktreeTest( + "direct HttpApi worktree create accepts missing content type and body", + () => + Effect.gen(function* () { + const test = yield* TestInstance + const server = yield* serverScoped() + + const response = yield* createWorktreeScoped({ + server, + directory: test.directory, + path: `${ExperimentalPaths.worktree}?directory=${encodeURIComponent(test.directory)}`, + init: { method: "POST" }, + timeoutLabel: "direct worktree create without content type or body", + }) + + expect(response).toMatchObject({ directory: expect.any(String) }) + }), + { git: true }, + ) + worktreeTest( "workspace worktree create does not hang", () =>