Skip to content

Commit 4b9660b

Browse files
authored
refactor(core): move more responsibility to workspace routing (anomalyco#19455)
1 parent e5f0e81 commit 4b9660b

5 files changed

Lines changed: 102 additions & 97 deletions

File tree

packages/opencode/src/control-plane/adaptors/worktree.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,7 @@ export const WorktreeAdaptor: Adaptor = {
3232
const config = Config.parse(info)
3333
await Worktree.remove({ directory: config.directory })
3434
},
35-
async fetch(info, input: RequestInfo | URL, init?: RequestInit) {
36-
const { Server } = await import("../../server/server")
37-
38-
const config = Config.parse(info)
39-
const url = input instanceof Request || input instanceof URL ? input : new URL(input, "http://opencode.internal")
40-
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined))
41-
headers.set("x-opencode-directory", config.directory)
42-
43-
const request = new Request(url, { ...init, headers })
44-
return Server.Default().fetch(request)
35+
async fetch(_info, _input: RequestInfo | URL, _init?: RequestInit) {
36+
throw new Error("fetch not implemented")
4537
},
4638
}

packages/opencode/src/control-plane/workspace-router-middleware.ts

Lines changed: 0 additions & 64 deletions
This file was deleted.

packages/opencode/src/server/instance.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { Global } from "../global"
1414
import { LSP } from "../lsp"
1515
import { Command } from "../command"
1616
import { Flag } from "../flag/flag"
17-
import { Filesystem } from "@/util/filesystem"
1817
import { QuestionRoutes } from "./routes/question"
1918
import { PermissionRoutes } from "./routes/permission"
2019
import { ProjectRoutes } from "./routes/project"
@@ -26,7 +25,6 @@ import { ConfigRoutes } from "./routes/config"
2625
import { ExperimentalRoutes } from "./routes/experimental"
2726
import { ProviderRoutes } from "./routes/provider"
2827
import { EventRoutes } from "./routes/event"
29-
import { InstanceBootstrap } from "../project/bootstrap"
3028
import { errorHandler } from "./middleware"
3129

3230
const log = Log.create({ service: "server" })
@@ -45,26 +43,6 @@ const csp = (hash = "") =>
4543
export const InstanceRoutes = (app?: Hono) =>
4644
(app ?? new Hono())
4745
.onError(errorHandler(log))
48-
.use(async (c, next) => {
49-
const raw = c.req.query("directory") || c.req.header("x-opencode-directory") || process.cwd()
50-
const directory = Filesystem.resolve(
51-
(() => {
52-
try {
53-
return decodeURIComponent(raw)
54-
} catch {
55-
return raw
56-
}
57-
})(),
58-
)
59-
60-
return Instance.provide({
61-
directory,
62-
init: InstanceBootstrap,
63-
async fn() {
64-
return next()
65-
},
66-
})
67-
})
6846
.route("/project", ProjectRoutes())
6947
.route("/pty", PtyRoutes())
7048
.route("/config", ConfigRoutes())
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import type { MiddlewareHandler } from "hono"
2+
import { getAdaptor } from "@/control-plane/adaptors"
3+
import { WorkspaceID } from "@/control-plane/schema"
4+
import { Workspace } from "@/control-plane/workspace"
5+
import { lazy } from "@/util/lazy"
6+
import { Filesystem } from "@/util/filesystem"
7+
import { Instance } from "@/project/instance"
8+
import { InstanceBootstrap } from "@/project/bootstrap"
9+
import { InstanceRoutes } from "./instance"
10+
11+
type Rule = { method?: string; path: string; exact?: boolean; action: "local" | "forward" }
12+
13+
const RULES: Array<Rule> = [
14+
{ path: "/session/status", action: "forward" },
15+
{ method: "GET", path: "/session", action: "local" },
16+
]
17+
18+
function local(method: string, path: string) {
19+
for (const rule of RULES) {
20+
if (rule.method && rule.method !== method) continue
21+
const match = rule.exact ? path === rule.path : path === rule.path || path.startsWith(rule.path + "/")
22+
if (match) return rule.action === "local"
23+
}
24+
return false
25+
}
26+
27+
const routes = lazy(() => InstanceRoutes())
28+
29+
export const WorkspaceRouterMiddleware: MiddlewareHandler = async (c) => {
30+
const raw = c.req.query("directory") || c.req.header("x-opencode-directory") || process.cwd()
31+
const directory = Filesystem.resolve(
32+
(() => {
33+
try {
34+
return decodeURIComponent(raw)
35+
} catch {
36+
return raw
37+
}
38+
})(),
39+
)
40+
41+
const url = new URL(c.req.url)
42+
const workspaceParam = url.searchParams.get("workspace")
43+
44+
// TODO: If session is being routed, force it to lookup the
45+
// project/workspace
46+
47+
// If no workspace is provided we use the "project" workspace
48+
if (!workspaceParam) {
49+
return Instance.provide({
50+
directory,
51+
init: InstanceBootstrap,
52+
async fn() {
53+
return routes().fetch(c.req.raw, c.env)
54+
},
55+
})
56+
}
57+
58+
const workspaceID = WorkspaceID.make(workspaceParam)
59+
const workspace = await Workspace.get(workspaceID)
60+
if (!workspace) {
61+
return new Response(`Workspace not found: ${workspaceID}`, {
62+
status: 500,
63+
headers: {
64+
"content-type": "text/plain; charset=utf-8",
65+
},
66+
})
67+
}
68+
69+
// Handle local workspaces directly so we can pass env to `fetch`,
70+
// necessary for websocket upgrades
71+
if (workspace.type === "worktree") {
72+
return Instance.provide({
73+
directory: workspace.directory!,
74+
init: InstanceBootstrap,
75+
async fn() {
76+
return routes().fetch(c.req.raw, c.env)
77+
},
78+
})
79+
}
80+
81+
// Remote workspaces
82+
83+
if (local(c.req.method, url.pathname)) {
84+
// No instance provided because we are serving cached data; there
85+
// is no instance to work with
86+
return routes().fetch(c.req.raw, c.env)
87+
}
88+
89+
const adaptor = await getAdaptor(workspace.type)
90+
const headers = new Headers(c.req.raw.headers)
91+
headers.delete("x-opencode-workspace")
92+
93+
return adaptor.fetch(workspace, `${url.pathname}${url.search}`, {
94+
method: c.req.method,
95+
body: c.req.method === "GET" || c.req.method === "HEAD" ? undefined : await c.req.raw.arrayBuffer(),
96+
signal: c.req.raw.signal,
97+
headers,
98+
})
99+
}

packages/opencode/src/server/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import z from "zod"
88
import { Auth } from "../auth"
99
import { Flag } from "../flag/flag"
1010
import { ProviderID } from "../provider/schema"
11-
import { WorkspaceRouterMiddleware } from "../control-plane/workspace-router-middleware"
11+
import { WorkspaceRouterMiddleware } from "./router"
1212
import { websocket } from "hono/bun"
1313
import { errors } from "./error"
1414
import { GlobalRoutes } from "./routes/global"

0 commit comments

Comments
 (0)