feat(server): add lazy simulation layer replacements (#35024)

This commit is contained in:
James Long 2026-07-02 16:45:09 -04:00 committed by GitHub
parent bc2e270f82
commit b0ca5520a1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 37 additions and 10 deletions

View file

@ -16,7 +16,7 @@ import { SdkPlugins } from "@opencode-ai/core/plugin/sdk"
import { ToolOutputStore } from "@opencode-ai/core/tool-output-store"
import { HttpRouter, HttpServer } from "effect/unstable/http"
import { HttpApiBuilder } from "effect/unstable/httpapi"
import { Layer, Option } from "effect"
import { Effect, Layer, Option } from "effect"
import { Api } from "./api"
import { ServerAuth } from "./auth"
import { handlers } from "./handlers"
@ -58,15 +58,24 @@ function makeRoutes<AuthError, AuthServices>(
sdkPlugins?: SdkPlugins.Store,
) {
const pluginRuntimeCell = PluginRuntime.makeCell()
const serviceLayer = AppNodeBuilder.build(
applicationServices,
[
[SessionExecution.node, SessionExecutionLocal.node],
[PluginRuntime.node, PluginRuntime.layerWithCell(pluginRuntimeCell)],
[PluginRuntime.providerNode, PluginRuntime.providerNodeWithCell(pluginRuntimeCell)],
...(sdkPlugins ? [[SdkPlugins.node, SdkPlugins.layerWithStore(sdkPlugins)] as const] : []),
],
)
const replacements: LayerNode.Replacements = [
[SessionExecution.node, SessionExecutionLocal.node],
[PluginRuntime.node, PluginRuntime.layerWithCell(pluginRuntimeCell)],
[PluginRuntime.providerNode, PluginRuntime.providerNodeWithCell(pluginRuntimeCell)],
...(sdkPlugins ? [[SdkPlugins.node, SdkPlugins.layerWithStore(sdkPlugins)] as const] : []),
]
// Simulation replacements are loaded via dynamic import so the simulation
// module is never eagerly loaded. Layer.unwrap defers both the import and
// the app-node build to layer-build time; when simulation is off the branch
// is byte-for-byte identical to a plain AppNodeBuilder.build call.
const serviceLayer = simulationEnabled()
? Layer.unwrap(
Effect.gen(function* () {
const { simulationReplacements } = yield* Effect.promise(() => import("./simulation"))
return AppNodeBuilder.build(applicationServices, [...replacements, ...simulationReplacements])
}),
)
: AppNodeBuilder.build(applicationServices, replacements)
return HttpApiBuilder.layer(Api, { openapiPath: "/openapi.json" }).pipe(
Layer.provide(handlers),
@ -79,6 +88,10 @@ function makeRoutes<AuthError, AuthServices>(
)
}
function simulationEnabled() {
return !!process.env.OPENCODE_SIMULATION
}
export const routes = createRoutes()
export const webHandler = () =>

View file

@ -0,0 +1,14 @@
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
/**
* Layer replacements applied when the server is built in simulation mode.
*
* Empty for now; simulation-mode implementations will populate this with
* replacement nodes/layers that swap real services for simulated ones (e.g.
* a fake filesystem). The server merges these into the app node build when
* `OPENCODE_SIMULATION` is enabled, via a dynamic import so this module is
* never loaded eagerly.
*/
export const simulationReplacements: LayerNode.Replacements = []
export * as Simulation from "./index"