refactor(simulation): scope Drive lifecycle with Effect (#36908)

This commit is contained in:
Kit Langton
2026-07-14 17:16:50 -04:00
committed by GitHub
parent a149a61a89
commit 947566f611
30 changed files with 1313 additions and 600 deletions
+31 -35
View File
@@ -197,42 +197,38 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
const exit = { epilogue: undefined as string | undefined, reason: undefined as unknown }
const result = yield* Effect.scoped(
Effect.gen(function* () {
const renderer = yield* Effect.acquireRelease(
Effect.tryPromise({
try: async () => {
const options = {
externalOutputMode: "passthrough",
targetFps: 60,
gatherStats: false,
exitOnCtrlC: false,
useKittyKeyboard: {},
autoFocus: false,
openConsoleOnError: false,
useMouse: !Flag.OPENCODE_DISABLE_MOUSE && config.mouse,
consoleOptions: {
keyBindings: [{ name: "y", ctrl: true, action: "copy-selection" }],
},
} satisfies CliRendererConfig
if (handoff) {
handoff.renderer.useMouse = options.useMouse
return handoff.renderer
}
if (process.env.OPENCODE_DRIVE) {
const { Drive } = await import("@opencode-ai/simulation/frontend")
return Drive.create(options)
}
return createCliRenderer(options)
},
catch: (error) => (error instanceof Error ? error : new Error(String(error))),
}),
(renderer) =>
Effect.sync(() => {
destroyRenderer(renderer)
const options = {
externalOutputMode: "passthrough",
targetFps: 60,
gatherStats: false,
exitOnCtrlC: false,
useKittyKeyboard: {},
autoFocus: false,
openConsoleOnError: false,
useMouse: !Flag.OPENCODE_DISABLE_MOUSE && config.mouse,
consoleOptions: {
keyBindings: [{ name: "y", ctrl: true, action: "copy-selection" }],
},
} satisfies CliRendererConfig
const renderer = yield* Effect.gen(function* () {
if (handoff) {
handoff.renderer.useMouse = options.useMouse
return yield* Effect.acquireRelease(Effect.succeed(handoff.renderer), (renderer) =>
Effect.sync(() => destroyRenderer(renderer)),
)
}
if (process.env.OPENCODE_DRIVE) {
const { Drive } = yield* Effect.promise(() => import("@opencode-ai/simulation/frontend"))
return yield* Drive.create(options)
}
return yield* Effect.acquireRelease(
Effect.tryPromise({
try: () => createCliRenderer(options),
catch: (error) => (error instanceof Error ? error : new Error(String(error))),
}),
)
(renderer) => Effect.sync(() => destroyRenderer(renderer)),
)
})
win32DisableProcessedInput()
const finalizers = new Set<() => Promise<void>>()
yield* Effect.addFinalizer(() =>
+3 -3
View File
@@ -1,6 +1,6 @@
import { expect, mock, test } from "bun:test"
import { createTestRenderer } from "@opentui/core/testing"
import { Effect } from "effect"
import { Effect, FileSystem } from "effect"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { Global } from "@opencode-ai/core/global"
import { createEventStream, createFetch, directory, json } from "./fixture/tui-client"
@@ -33,7 +33,7 @@ test("SIGHUP clears title and disposes scoped resources once", async () => {
packages: { resolve: async () => undefined },
args: {},
log: () => {},
}).pipe(Effect.provide(AppNodeBuilder.build(Global.node))),
}).pipe(Effect.provide(AppNodeBuilder.build(Global.node)), Effect.provide(FileSystem.layerNoop({}))),
)
await ready
process.emit("SIGHUP")
@@ -105,7 +105,7 @@ test("session lifecycle updates the terminal title and prints the epilogue after
packages: { resolve: async () => undefined },
args: { sessionID: "dummy" },
log: () => {},
}).pipe(Effect.provide(AppNodeBuilder.build(Global.node))),
}).pipe(Effect.provide(AppNodeBuilder.build(Global.node)), Effect.provide(FileSystem.layerNoop({}))),
)
await initialTitleSet