chore: generate
Some checks failed
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
publish / version (push) Waiting to run
publish / build-tauri (map[host:blacksmith-4vcpu-ubuntu-2404 target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-4vcpu-windows-2025 target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-tauri (map[host:blacksmith-8vcpu-ubuntu-2404-arm target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:macos-latest target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-tauri (map[host:windows-2025 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run
docs-locale-sync / sync-locales (push) Has been cancelled

This commit is contained in:
opencode-agent[bot] 2026-04-29 20:52:19 +00:00
parent 9db5890ce5
commit 1124ae17b4
5 changed files with 37 additions and 23 deletions

View file

@ -30,9 +30,7 @@ export function headers(input: Request | HeadersInit | Record<string, string>, e
}
export function websocketProtocols(input: Request | Record<string, string | undefined>) {
const value = input instanceof Request
? input.headers.get("sec-websocket-protocol")
: input["sec-websocket-protocol"]
const value = input instanceof Request ? input.headers.get("sec-websocket-protocol") : input["sec-websocket-protocol"]
if (!value) return []
return value
.split(",")

View file

@ -1,6 +1,13 @@
import { ProxyUtil } from "@/server/proxy-util"
import { Effect, Stream } from "effect"
import { FetchHttpClient, HttpBody, HttpClient, HttpClientRequest, HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
import {
FetchHttpClient,
HttpBody,
HttpClient,
HttpClientRequest,
HttpServerRequest,
HttpServerResponse,
} from "effect/unstable/http"
import * as Socket from "effect/unstable/socket/Socket"
function webSource(request: HttpServerRequest.HttpServerRequest): Request | undefined {
@ -35,7 +42,9 @@ export function websocket(
Effect.catchReason("SocketError", "SocketCloseError", (reason) =>
writeInbound(new Socket.CloseEvent(reason.code, reason.closeReason)).pipe(Effect.catch(() => Effect.void)),
),
Effect.catch(() => writeInbound(new Socket.CloseEvent(1011, "proxy error")).pipe(Effect.catch(() => Effect.void))),
Effect.catch(() =>
writeInbound(new Socket.CloseEvent(1011, "proxy error")).pipe(Effect.catch(() => Effect.void)),
),
Effect.forkScoped,
)

View file

@ -27,10 +27,13 @@ type RequestPlan = Data.TaggedEnum<{
}>
const RequestPlan = Data.taggedEnum<RequestPlan>()
export class WorkspaceRouteContext extends Context.Service<WorkspaceRouteContext, {
readonly directory: string
readonly workspaceID?: WorkspaceID
}>()("@opencode/ExperimentalHttpApiWorkspaceRouteContext") {}
export class WorkspaceRouteContext extends Context.Service<
WorkspaceRouteContext,
{
readonly directory: string
readonly workspaceID?: WorkspaceID
}
>()("@opencode/ExperimentalHttpApiWorkspaceRouteContext") {}
export class WorkspaceRoutingMiddleware extends HttpApiMiddleware.Service<
WorkspaceRoutingMiddleware,
@ -110,7 +113,10 @@ function proxyRemote(
if (headers["upgrade"]?.toLowerCase() === "websocket") return yield* HttpApiProxy.websocket(request, proxyURL)
const response = yield* HttpApiProxy.http(proxyURL, target.headers, request)
const sync = Fence.parse(new Headers(response.headers))
if (sync) yield* Effect.promise(() => Fence.wait(workspace.id, sync, request.source instanceof Request ? request.source.signal : undefined))
if (sync)
yield* Effect.promise(() =>
Fence.wait(workspace.id, sync, request.source instanceof Request ? request.source.signal : undefined),
)
return response
})
}
@ -157,9 +163,7 @@ function routeWorkspace<E>(
MissingWorkspace: ({ workspaceID }) => Effect.succeed(missingWorkspaceResponse(workspaceID)),
Remote: ({ request, workspace, target, url }) => proxyRemote(request, workspace, target, url),
Local: ({ directory, workspaceID }) =>
effect.pipe(
Effect.provideService(WorkspaceRouteContext, WorkspaceRouteContext.of({ directory, workspaceID })),
),
effect.pipe(Effect.provideService(WorkspaceRouteContext, WorkspaceRouteContext.of({ directory, workspaceID }))),
})
}

View file

@ -90,9 +90,9 @@ const instanceApiRoutes = HttpApiBuilder.layer(InstanceHttpApi).pipe(
const rawInstanceRoutes = Layer.mergeAll(eventRoute, ptyConnectRoute).pipe(
Layer.provide(
instanceRouterMiddleware.combine(workspaceRouterMiddleware).layer.pipe(
Layer.provide(Socket.layerWebSocketConstructorGlobal),
),
instanceRouterMiddleware
.combine(workspaceRouterMiddleware)
.layer.pipe(Layer.provide(Socket.layerWebSocketConstructorGlobal)),
),
)
const instanceRoutes = Layer.mergeAll(rawInstanceRoutes, instanceApiRoutes).pipe(

View file

@ -22,14 +22,17 @@ describe("HttpApi workspace proxy", () => {
Effect.gen(function* () {
const req = yield* HttpServerRequest.HttpServerRequest
const body = yield* req.text
return yield* HttpServerResponse.json({ path: req.url, method: req.method, body }, {
status: 201,
headers: {
"content-encoding": "identity",
"content-length": "999",
"x-remote": "yes",
return yield* HttpServerResponse.json(
{ path: req.url, method: req.method, body },
{
status: 201,
headers: {
"content-encoding": "identity",
"content-length": "999",
"x-remote": "yes",
},
},
})
)
}),
)
const url = yield* serverUrl()