refactor(core): bridge plugin runtime in app builder
This commit is contained in:
parent
041215996c
commit
d7de2de673
3 changed files with 47 additions and 40 deletions
|
|
@ -1,16 +1,21 @@
|
||||||
import { buildLocationServiceMap } from "../location-services"
|
import { buildLocationServiceMap } from "../location-services"
|
||||||
import { LocationServiceMap } from "../location-service-map"
|
import { LocationServiceMap } from "../location-service-map"
|
||||||
|
import { PluginRuntime } from "../plugin/runtime"
|
||||||
import { LayerNode } from "./layer-node"
|
import { LayerNode } from "./layer-node"
|
||||||
import { makeGlobalNode } from "./app-node"
|
import { makeGlobalNode } from "./app-node"
|
||||||
|
|
||||||
export function build<A, E>(root: LayerNode.Node<A, E, any>, replacements: LayerNode.Replacements = []) {
|
export function build<A, E>(root: LayerNode.Node<A, E, any>, replacements: LayerNode.Replacements = []) {
|
||||||
let allReplacements = replacements
|
const bridge = PluginRuntime.makeBridge()
|
||||||
|
let allReplacements = replacements.concat([
|
||||||
|
[PluginRuntime.node, PluginRuntime.nodeWithBridge(bridge)],
|
||||||
|
[PluginRuntime.providerNode, PluginRuntime.providerNodeWithBridge(bridge)],
|
||||||
|
])
|
||||||
|
|
||||||
// Only build the location service map if it's actually needed
|
// Only build the location service map if it's actually needed
|
||||||
if (LayerNode.hasUnbound(root, LocationServiceMap.node) && !hasReplacement(replacements, LocationServiceMap.node)) {
|
if (LayerNode.hasUnbound(root, LocationServiceMap.node) && !hasReplacement(allReplacements, LocationServiceMap.node)) {
|
||||||
const locationMap = buildLocationServiceMap(replacements)
|
const locationMap = buildLocationServiceMap(allReplacements)
|
||||||
const locationMapNode = makeGlobalNode({ service: LocationServiceMap.Service, layer: locationMap, deps: [] })
|
const locationMapNode = makeGlobalNode({ service: LocationServiceMap.Service, layer: locationMap, deps: [] })
|
||||||
allReplacements = replacements.concat([[LocationServiceMap.node, locationMapNode]])
|
allReplacements = allReplacements.concat([[LocationServiceMap.node, locationMapNode]])
|
||||||
}
|
}
|
||||||
|
|
||||||
return LayerNode.compile(root, allReplacements)
|
return LayerNode.compile(root, allReplacements)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
export * as PluginRuntime from "./runtime"
|
export * as PluginRuntime from "./runtime"
|
||||||
|
|
||||||
import { Context, Effect, Layer } from "effect"
|
import { Context, Deferred, Effect, Layer } from "effect"
|
||||||
import { AgentV2 } from "../agent"
|
import { AgentV2 } from "../agent"
|
||||||
import { makeGlobalNode } from "../effect/app-node"
|
import { makeGlobalNode } from "../effect/app-node"
|
||||||
import { Job } from "../job"
|
import { Job } from "../job"
|
||||||
|
|
@ -25,51 +25,44 @@ export interface Interface {
|
||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/PluginRuntime") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/PluginRuntime") {}
|
||||||
|
|
||||||
export interface Cell {
|
export interface Bridge {
|
||||||
runtime?: Interface
|
runtime: Deferred.Deferred<Interface>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const makeCell = (): Cell => ({})
|
export const makeBridge = (): Bridge => ({ runtime: Deferred.makeUnsafe<Interface>() })
|
||||||
|
|
||||||
const unavailable = <A, E, R>() => Effect.die("Plugin runtime is unavailable") as Effect.Effect<A, E, R>
|
const require = <A, E, R>(bridge: Bridge, f: (runtime: Interface) => Effect.Effect<A, E, R>) =>
|
||||||
const require = <A, E, R>(cell: Cell, f: (runtime: Interface) => Effect.Effect<A, E, R>) =>
|
Effect.suspend(() => Deferred.await(bridge.runtime).pipe(Effect.flatMap(f)))
|
||||||
Effect.suspend(() => {
|
|
||||||
const runtime = cell.runtime
|
|
||||||
if (runtime === undefined) return unavailable<A, E, R>()
|
|
||||||
return f(runtime)
|
|
||||||
})
|
|
||||||
|
|
||||||
const defaultCell = makeCell()
|
export const layerWithBridge = (bridge: Bridge) =>
|
||||||
|
|
||||||
export const layerWithCell = (cell: Cell) =>
|
|
||||||
Layer.succeed(
|
Layer.succeed(
|
||||||
Service,
|
Service,
|
||||||
Service.of({
|
Service.of({
|
||||||
session: {
|
session: {
|
||||||
get: (sessionID) => require(cell, (runtime) => runtime.session.get(sessionID)),
|
get: (sessionID) => require(bridge, (runtime) => runtime.session.get(sessionID)),
|
||||||
create: (input) => require(cell, (runtime) => runtime.session.create(input)),
|
create: (input) => require(bridge, (runtime) => runtime.session.create(input)),
|
||||||
messages: (input) => require(cell, (runtime) => runtime.session.messages(input)),
|
messages: (input) => require(bridge, (runtime) => runtime.session.messages(input)),
|
||||||
prompt: (input) => require(cell, (runtime) => runtime.session.prompt(input)),
|
prompt: (input) => require(bridge, (runtime) => runtime.session.prompt(input)),
|
||||||
resume: (sessionID) => require(cell, (runtime) => runtime.session.resume(sessionID)),
|
resume: (sessionID) => require(bridge, (runtime) => runtime.session.resume(sessionID)),
|
||||||
interrupt: (sessionID) => require(cell, (runtime) => runtime.session.interrupt(sessionID)),
|
interrupt: (sessionID) => require(bridge, (runtime) => runtime.session.interrupt(sessionID)),
|
||||||
synthetic: (input) => require(cell, (runtime) => runtime.session.synthetic(input)),
|
synthetic: (input) => require(bridge, (runtime) => runtime.session.synthetic(input)),
|
||||||
},
|
},
|
||||||
job: {
|
job: {
|
||||||
start: (input) => require(cell, (runtime) => runtime.job.start(input)),
|
start: (input) => require(bridge, (runtime) => runtime.job.start(input)),
|
||||||
wait: (input) => require(cell, (runtime) => runtime.job.wait(input)),
|
wait: (input) => require(bridge, (runtime) => runtime.job.wait(input)),
|
||||||
block: (input) => require(cell, (runtime) => runtime.job.block(input)),
|
block: (input) => require(bridge, (runtime) => runtime.job.block(input)),
|
||||||
background: (id) => require(cell, (runtime) => runtime.job.background(id)),
|
background: (id) => require(bridge, (runtime) => runtime.job.background(id)),
|
||||||
cancel: (id) => require(cell, (runtime) => runtime.job.cancel(id)),
|
cancel: (id) => require(bridge, (runtime) => runtime.job.cancel(id)),
|
||||||
},
|
},
|
||||||
location: {
|
location: {
|
||||||
agent: {
|
agent: {
|
||||||
list: (ref) => require(cell, (runtime) => runtime.location.agent.list(ref)),
|
list: (ref) => require(bridge, (runtime) => runtime.location.agent.list(ref)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const providerLayerWithCell = (cell: Cell) =>
|
export const providerLayerWithBridge = (bridge: Bridge) =>
|
||||||
Layer.effectDiscard(
|
Layer.effectDiscard(
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const sessions = yield* SessionV2.Service
|
const sessions = yield* SessionV2.Service
|
||||||
|
|
@ -96,22 +89,34 @@ export const providerLayerWithCell = (cell: Cell) =>
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cell.runtime = runtime
|
yield* Deferred.succeed(bridge.runtime, runtime)
|
||||||
yield* Effect.addFinalizer(() =>
|
yield* Effect.addFinalizer(() =>
|
||||||
Effect.sync(() => {
|
Effect.sync(() => {
|
||||||
if (cell.runtime === runtime) cell.runtime = undefined
|
bridge.runtime = Deferred.makeUnsafe<Interface>()
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const layer = layerWithCell(defaultCell)
|
const unsafeBridge = makeBridge()
|
||||||
export const providerLayer = providerLayerWithCell(defaultCell)
|
|
||||||
|
export const layer = layerWithBridge(unsafeBridge)
|
||||||
|
export const providerLayer = providerLayerWithBridge(unsafeBridge)
|
||||||
|
|
||||||
export const node = makeGlobalNode({ service: Service, layer, deps: [] })
|
export const node = makeGlobalNode({ service: Service, layer, deps: [] })
|
||||||
|
|
||||||
|
export const nodeWithBridge = (bridge: Bridge) =>
|
||||||
|
makeGlobalNode({ service: Service, layer: layerWithBridge(bridge), deps: [] })
|
||||||
|
|
||||||
export const providerNode = makeGlobalNode({
|
export const providerNode = makeGlobalNode({
|
||||||
name: "plugin-runtime-provider",
|
name: "plugin-runtime-provider",
|
||||||
layer: providerLayer,
|
layer: providerLayer,
|
||||||
deps: [node, SessionV2.node, Job.node, LocationServiceMap.node],
|
deps: [node, SessionV2.node, Job.node, LocationServiceMap.node],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const providerNodeWithBridge = (bridge: Bridge) =>
|
||||||
|
makeGlobalNode({
|
||||||
|
name: "plugin-runtime-provider",
|
||||||
|
layer: providerLayerWithBridge(bridge),
|
||||||
|
deps: [node, SessionV2.node, Job.node, LocationServiceMap.node],
|
||||||
|
})
|
||||||
|
|
|
||||||
|
|
@ -50,20 +50,17 @@ export function createRoutes(password?: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createEmbeddedRoutes(sdkPlugins?: SdkPlugins.Store) {
|
export function createEmbeddedRoutes(sdkPlugins?: SdkPlugins.Store) {
|
||||||
return makeRoutes(ServerAuth.Config.layer({ username: "opencode", password: Option.none() }), sdkPlugins)
|
return makeRoutes(ServerAuth.Config.configLayer({ username: "opencode", password: Option.none() }), sdkPlugins)
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeRoutes<AuthError, AuthServices>(
|
function makeRoutes<AuthError, AuthServices>(
|
||||||
auth: Layer.Layer<ServerAuth.Config, AuthError, AuthServices>,
|
auth: Layer.Layer<ServerAuth.Config, AuthError, AuthServices>,
|
||||||
sdkPlugins?: SdkPlugins.Store,
|
sdkPlugins?: SdkPlugins.Store,
|
||||||
) {
|
) {
|
||||||
const pluginRuntimeCell = PluginRuntime.makeCell()
|
|
||||||
const serviceLayer = AppNodeBuilder.build(
|
const serviceLayer = AppNodeBuilder.build(
|
||||||
applicationServices,
|
applicationServices,
|
||||||
[
|
[
|
||||||
[SessionExecution.node, SessionExecutionLocal.node],
|
[SessionExecution.node, SessionExecutionLocal.node],
|
||||||
[PluginRuntime.node, PluginRuntime.layerWithCell(pluginRuntimeCell)],
|
|
||||||
[PluginRuntime.providerNode, PluginRuntime.providerLayerWithCell(pluginRuntimeCell)],
|
|
||||||
...(sdkPlugins ? [[SdkPlugins.node, SdkPlugins.layerWithStore(sdkPlugins)] as const] : []),
|
...(sdkPlugins ? [[SdkPlugins.node, SdkPlugins.layerWithStore(sdkPlugins)] as const] : []),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue