refactor(opencode): bind instance bootstrap node (#34502)
This commit is contained in:
parent
9151af7045
commit
7a035d7fc0
26 changed files with 82 additions and 57 deletions
|
|
@ -53,7 +53,7 @@ const workspaceLayer = (experimentalWorkspaces: boolean) =>
|
|||
[
|
||||
[RuntimeFlags.node, RuntimeFlags.layer({ experimentalWorkspaces })],
|
||||
[
|
||||
InstanceBootstrap.node,
|
||||
InstanceStore.bootstrapNode,
|
||||
Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void })),
|
||||
],
|
||||
],
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import type * as Scope from "effect/Scope"
|
|||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
|
||||
import type { Config } from "@/config/config"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { InstanceRef } from "../../src/effect/instance-ref"
|
||||
import { InstanceBootstrap } from "../../src/project/bootstrap-service"
|
||||
import type { InstanceContext } from "../../src/project/instance-context"
|
||||
|
|
@ -17,7 +18,7 @@ import { InstanceStore } from "../../src/project/instance-store"
|
|||
import { TestLLMServer } from "../lib/llm-server"
|
||||
|
||||
const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void }))
|
||||
export const testInstanceStoreLayer = InstanceStore.defaultLayer.pipe(Layer.provide(noopBootstrap))
|
||||
export const testInstanceStoreLayer = LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, noopBootstrap]])
|
||||
|
||||
export async function provideTestInstance<R>(input: {
|
||||
directory: string
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { FetchHttpClient } from "effect/unstable/http"
|
||||
import { Layer } from "effect"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { FSUtil } from "@opencode-ai/core/fs-util"
|
||||
import { Auth } from "../../src/auth"
|
||||
|
|
@ -25,6 +26,5 @@ export const workspaceLayerWithRuntimeFlags = (overrides: Partial<RuntimeFlags.I
|
|||
Layer.provide(FetchHttpClient.layer),
|
||||
Layer.provide(FSUtil.defaultLayer),
|
||||
Layer.provide(RuntimeFlags.layer(overrides)),
|
||||
Layer.provide(InstanceStore.defaultLayer),
|
||||
Layer.provide(InstanceBootstrap.defaultLayer),
|
||||
Layer.provide(LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, InstanceBootstrap.node]])),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
|||
const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void }))
|
||||
const env = AppNodeBuilder.build(
|
||||
LayerNode.group([Permission.node, EventV2Bridge.node, CrossSpawnSpawner.node, InstanceStore.node]),
|
||||
[[InstanceBootstrap.node, noopBootstrap]],
|
||||
[[InstanceStore.bootstrapNode, noopBootstrap]],
|
||||
)
|
||||
const it = testEffect(env)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,13 +21,12 @@ import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
|||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
|
||||
const noopBootstrapLayer = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void }))
|
||||
|
||||
const it = testEffect(
|
||||
AppNodeBuilder.build(LayerNode.group([Plugin.node, Workspace.node, InstanceStore.node, Ripgrep.node]), [
|
||||
[Auth.node, AuthTest.empty],
|
||||
[Account.node, AccountTest.empty],
|
||||
[Npm.node, NpmTest.noop],
|
||||
[InstanceBootstrap.node, noopBootstrapLayer],
|
||||
[InstanceStore.bootstrapNode, noopBootstrapLayer],
|
||||
[RuntimeFlags.node, RuntimeFlags.layer({ disableDefaultPlugins: true, experimentalWorkspaces: true })],
|
||||
]),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,14 +4,19 @@ import path from "node:path"
|
|||
import { pathToFileURL } from "node:url"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||
import { Cause, Effect, Exit, Fiber, Layer } from "effect"
|
||||
import { Cause, Effect, Exit, Fiber } from "effect"
|
||||
import { bootstrap as cliBootstrap } from "../../src/cli/bootstrap"
|
||||
import { InstanceBootstrap } from "../../src/project/bootstrap"
|
||||
import { InstanceStore } from "../../src/project/instance-store"
|
||||
import { disposeAllInstances, tmpdirScoped } from "../fixture/fixture"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { waitGlobalBusEvent } from "../server/global-bus"
|
||||
|
||||
const it = testEffect(LayerNode.compile(LayerNode.group([InstanceStore.node, CrossSpawnSpawner.node])))
|
||||
const it = testEffect(
|
||||
LayerNode.compile(LayerNode.group([InstanceStore.node, CrossSpawnSpawner.node]), [
|
||||
[InstanceStore.bootstrapNode, InstanceBootstrap.node],
|
||||
]),
|
||||
)
|
||||
|
||||
// InstanceBootstrap must run before any code touches the instance —
|
||||
// originally tracked by PRs #25389 and #25449, now a permanent
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const noopBootstrap = Layer.succeed(
|
|||
|
||||
const it = testEffect(
|
||||
LayerNode.compile(LayerNode.group([InstanceStore.node, CrossSpawnSpawner.node]), [
|
||||
[InstanceBootstrap.node, noopBootstrap],
|
||||
[InstanceStore.bootstrapNode, noopBootstrap],
|
||||
]),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ import * as fs from "fs/promises"
|
|||
import path from "path"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { Effect } from "effect"
|
||||
import { InstanceBootstrap } from "../../src/project/bootstrap"
|
||||
import { InstanceStore } from "../../src/project/instance-store"
|
||||
import { Worktree } from "../../src/worktree"
|
||||
import { TestInstance } from "../fixture/fixture"
|
||||
import { testEffect } from "../lib/effect"
|
||||
|
||||
const it = testEffect(LayerNode.compile(Worktree.node))
|
||||
const it = testEffect(LayerNode.compile(Worktree.node, [[InstanceStore.bootstrapNode, InstanceBootstrap.node]]))
|
||||
const wintest = process.platform === "win32" ? it.instance : it.instance.skip
|
||||
|
||||
describe("Worktree.remove", () => {
|
||||
|
|
|
|||
|
|
@ -5,11 +5,17 @@ import { FSUtil } from "@opencode-ai/core/fs-util"
|
|||
import { Cause, Deferred, Effect, Exit, Fiber } from "effect"
|
||||
import { GlobalBus, type GlobalEvent } from "../../src/bus/global"
|
||||
import { Git } from "../../src/git"
|
||||
import { InstanceBootstrap } from "../../src/project/bootstrap"
|
||||
import { InstanceStore } from "../../src/project/instance-store"
|
||||
import { Worktree } from "../../src/worktree"
|
||||
import { disposeAllInstances, provideInstance, TestInstance } from "../fixture/fixture"
|
||||
import { testEffect } from "../lib/effect"
|
||||
|
||||
const it = testEffect(LayerNode.compile(LayerNode.group([Worktree.node, FSUtil.node, Git.node])))
|
||||
const it = testEffect(
|
||||
LayerNode.compile(LayerNode.group([Worktree.node, FSUtil.node, Git.node]), [
|
||||
[InstanceStore.bootstrapNode, InstanceBootstrap.node],
|
||||
]),
|
||||
)
|
||||
const wintest = process.platform !== "win32" ? it.instance : it.instance.skip
|
||||
|
||||
function normalize(input: string) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ import { Provider } from "@/provider/provider"
|
|||
|
||||
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
import { InstanceLayer } from "@/project/instance-layer"
|
||||
import { InstanceBootstrap } from "@/project/bootstrap"
|
||||
import { InstanceStore } from "@/project/instance-store"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
|
|
@ -1760,8 +1761,9 @@ it.instance(
|
|||
// Tests that need plugin file setup or multi-instance flows fall back to a
|
||||
// scoped tmpdir + provideInstance pattern via it.effect.
|
||||
|
||||
const instanceStoreLayer = LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, InstanceBootstrap.node]])
|
||||
const provideMultiInstance = <A, E, R>(eff: Effect.Effect<A, E, R>) =>
|
||||
eff.pipe(Effect.provide(InstanceLayer.layer), Effect.provide(CrossSpawnSpawner.defaultLayer))
|
||||
eff.pipe(Effect.provide(instanceStoreLayer), Effect.provide(CrossSpawnSpawner.defaultLayer))
|
||||
|
||||
it.effect("plugin config providers persist after instance dispose", () =>
|
||||
Effect.gen(function* () {
|
||||
|
|
@ -1864,7 +1866,7 @@ it.effect("opencode loader keeps paid models when config apiKey is present", ()
|
|||
Provider.use
|
||||
.list()
|
||||
.pipe(provideInstanceEffect(directory))
|
||||
.pipe(Effect.provide(InstanceLayer.layer), Effect.provide(CrossSpawnSpawner.defaultLayer))
|
||||
.pipe(Effect.provide(instanceStoreLayer), Effect.provide(CrossSpawnSpawner.defaultLayer))
|
||||
|
||||
const none = paid(yield* listIn(noneDir))
|
||||
const keyedCount = paid(yield* listIn(keyedDir))
|
||||
|
|
@ -1883,7 +1885,7 @@ it.effect("opencode loader keeps paid models when auth exists", () =>
|
|||
Provider.use
|
||||
.list()
|
||||
.pipe(provideInstanceEffect(directory))
|
||||
.pipe(Effect.provide(InstanceLayer.layer), Effect.provide(CrossSpawnSpawner.defaultLayer))
|
||||
.pipe(Effect.provide(instanceStoreLayer), Effect.provide(CrossSpawnSpawner.defaultLayer))
|
||||
|
||||
const none = paid(yield* listIn(noneDir))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { NodeHttpServer, NodeServices } from "@effect/platform-node"
|
||||
import { describe, expect } from "bun:test"
|
||||
import { Effect, Fiber, Layer, Schema } from "effect"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { HttpClient, HttpClientRequest, HttpRouter } from "effect/unstable/http"
|
||||
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi"
|
||||
import * as Socket from "effect/unstable/socket/Socket"
|
||||
|
|
@ -12,7 +13,8 @@ import { Ripgrep } from "@opencode-ai/core/ripgrep"
|
|||
import type { WorkspaceAdapter } from "../../src/control-plane/types"
|
||||
import { Workspace } from "../../src/control-plane/workspace"
|
||||
import { InstanceRef, WorkspaceRef } from "../../src/effect/instance-ref"
|
||||
import { InstanceLayer } from "../../src/project/instance-layer"
|
||||
import { InstanceBootstrap } from "../../src/project/bootstrap"
|
||||
import { InstanceStore } from "../../src/project/instance-store"
|
||||
import { Project } from "../../src/project/project"
|
||||
import { Session } from "../../src/session/session"
|
||||
import { disposeMiddleware, markInstanceForDisposal } from "../../src/server/routes/instance/httpapi/lifecycle"
|
||||
|
|
@ -51,7 +53,7 @@ const it = testEffect(
|
|||
testStateLayer,
|
||||
NodeHttpServer.layerTest,
|
||||
NodeServices.layer,
|
||||
InstanceLayer.layer,
|
||||
LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, InstanceBootstrap.node]]),
|
||||
Project.defaultLayer,
|
||||
workspaceLayer,
|
||||
).pipe(Layer.provide(Ripgrep.defaultLayer)),
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
import { NodeHttpServer, NodeServices } from "@effect/platform-node"
|
||||
import { describe, expect } from "bun:test"
|
||||
import { Deferred, Effect, Layer, Schema, Scope } from "effect"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import * as Stream from "effect/Stream"
|
||||
import { HttpClient, HttpRouter, HttpServerResponse } from "effect/unstable/http"
|
||||
import * as Socket from "effect/unstable/socket/Socket"
|
||||
|
|
@ -20,7 +21,8 @@ import { Ripgrep } from "@opencode-ai/core/ripgrep"
|
|||
import type { WorkspaceAdapter } from "../../src/control-plane/types"
|
||||
import { Workspace } from "../../src/control-plane/workspace"
|
||||
import { InstanceRef, WorkspaceRef } from "../../src/effect/instance-ref"
|
||||
import { InstanceLayer } from "../../src/project/instance-layer"
|
||||
import { InstanceBootstrap } from "../../src/project/bootstrap"
|
||||
import { InstanceStore } from "../../src/project/instance-store"
|
||||
import { Project } from "../../src/project/project"
|
||||
import { Session } from "../../src/session/session"
|
||||
import {
|
||||
|
|
@ -56,7 +58,7 @@ const it = testEffect(
|
|||
testStateLayer,
|
||||
NodeHttpServer.layerTest,
|
||||
NodeServices.layer,
|
||||
InstanceLayer.layer,
|
||||
LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, InstanceBootstrap.node]]),
|
||||
Project.defaultLayer,
|
||||
workspaceLayer,
|
||||
).pipe(Layer.provide(Ripgrep.defaultLayer)),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { HttpServer } from "effect/unstable/http"
|
|||
import { ChildProcessSpawner } from "effect/unstable/process"
|
||||
import { FSUtil } from "@opencode-ai/core/fs-util"
|
||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { createOpencodeClient } from "@opencode-ai/sdk/v2"
|
||||
import { validateSession } from "../../src/cli/tui/validate-session"
|
||||
|
|
@ -34,7 +35,7 @@ const it = testEffect(
|
|||
Layer.mergeAll(
|
||||
FSUtil.defaultLayer,
|
||||
CrossSpawnSpawner.defaultLayer,
|
||||
InstanceStore.defaultLayer.pipe(Layer.provide(noopBootstrap)),
|
||||
LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, noopBootstrap]]),
|
||||
Database.defaultLayer,
|
||||
httpApiLayer,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { HttpClient, HttpClientRequest, HttpClientResponse, HttpRouter, HttpServ
|
|||
import { layerWebSocketConstructorGlobal } from "effect/unstable/socket/Socket"
|
||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { Ripgrep } from "@opencode-ai/core/ripgrep"
|
||||
import { registerAdapter } from "../../src/control-plane/adapters"
|
||||
import type { WorkspaceAdapter } from "../../src/control-plane/types"
|
||||
|
|
@ -38,14 +39,14 @@ import { pollWithTimeout, testEffect } from "../lib/effect"
|
|||
|
||||
const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
|
||||
const workspaceLayer = Workspace.defaultLayer.pipe(
|
||||
Layer.provide(InstanceStore.defaultLayer),
|
||||
Layer.provide(InstanceBootstrap.defaultLayer),
|
||||
Layer.provide(LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, InstanceBootstrap.node]])),
|
||||
)
|
||||
const instanceStoreLayer = InstanceStore.defaultLayer.pipe(
|
||||
Layer.provide(
|
||||
const instanceStoreLayer = LayerNode.compile(InstanceStore.node, [
|
||||
[
|
||||
InstanceStore.bootstrapNode,
|
||||
Layer.succeed(InstanceBootstrapService.Service, InstanceBootstrapService.Service.of({ run: Effect.void })),
|
||||
),
|
||||
)
|
||||
],
|
||||
])
|
||||
const servedRoutes: Layer.Layer<never, Config.ConfigError, HttpServer.HttpServer> = HttpRouter.serve(
|
||||
HttpApiApp.routes,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { mkdir } from "node:fs/promises"
|
|||
import path from "node:path"
|
||||
import { Effect, Layer, Stream } from "effect"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { registerAdapter } from "../../src/control-plane/adapters"
|
||||
import { WorkspaceV2 } from "@opencode-ai/core/workspace"
|
||||
import type { WorkspaceAdapter } from "../../src/control-plane/types"
|
||||
|
|
@ -24,15 +25,14 @@ import { httpApiLayer, requestInDirectory } from "./httpapi-layer"
|
|||
|
||||
const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
|
||||
const workspaceLayer = Workspace.defaultLayer.pipe(
|
||||
Layer.provide(InstanceStore.defaultLayer),
|
||||
Layer.provide(InstanceBootstrap.defaultLayer),
|
||||
Layer.provide(LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, InstanceBootstrap.node]])),
|
||||
)
|
||||
const it = testEffect(
|
||||
Layer.mergeAll(
|
||||
Project.defaultLayer,
|
||||
Session.defaultLayer,
|
||||
workspaceLayer,
|
||||
InstanceStore.defaultLayer.pipe(Layer.provide(InstanceBootstrap.defaultLayer)),
|
||||
LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, InstanceBootstrap.node]]),
|
||||
Database.defaultLayer,
|
||||
httpApiLayer,
|
||||
).pipe(Layer.provide(Ripgrep.defaultLayer)),
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ afterEach(async () => {
|
|||
})
|
||||
|
||||
const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void }))
|
||||
const testInstanceStore = LayerNode.compile(InstanceStore.node, [[InstanceBootstrap.node, noopBootstrap]])
|
||||
const testInstanceStore = LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, noopBootstrap]])
|
||||
const it = testEffect(
|
||||
Layer.mergeAll(
|
||||
LayerNode.compile(LayerNode.group([FSUtil.node, Database.node, Snapshot.node])),
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ afterEach(async () => {
|
|||
})
|
||||
|
||||
const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void }))
|
||||
const testInstanceStore = LayerNode.compile(InstanceStore.node, [[InstanceBootstrap.node, noopBootstrap]])
|
||||
const testInstanceStore = LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, noopBootstrap]])
|
||||
|
||||
const it = testEffect(
|
||||
Layer.mergeAll(LayerNode.compile(LayerNode.group([FSUtil.node, Snapshot.node])), testInstanceStore, httpApiLayer),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue