Quellcode durchsuchen

feat(cli): auto-dispose InstanceContext after effectCmd handlers (#25481)

Kit Langton vor 3 Monaten
Ursprung
Commit
85bb9007ba
1 geänderte Dateien mit 18 neuen und 1 gelöschten Zeilen
  1. 18 1
      packages/opencode/src/cli/effect-cmd.ts

+ 18 - 1
packages/opencode/src/cli/effect-cmd.ts

@@ -2,6 +2,7 @@ import type { Argv } from "yargs"
 import { Effect, Schema } from "effect"
 import { Effect, Schema } from "effect"
 import { AppRuntime, type AppServices } from "@/effect/app-runtime"
 import { AppRuntime, type AppServices } from "@/effect/app-runtime"
 import { InstanceStore } from "@/project/instance-store"
 import { InstanceStore } from "@/project/instance-store"
+import { InstanceRef } from "@/effect/instance-ref"
 import { cmd } from "./cmd/cmd"
 import { cmd } from "./cmd/cmd"
 
 
 /**
 /**
@@ -21,6 +22,11 @@ export const fail = (message: string, exitCode = 1) => Effect.fail(new CliError(
  * Effect-native CLI command builder. Wraps yargs `cmd()` so the handler body is
  * Effect-native CLI command builder. Wraps yargs `cmd()` so the handler body is
  * an `Effect` with `InstanceRef` provided and any `AppServices` yieldable.
  * an `Effect` with `InstanceRef` provided and any `AppServices` yieldable.
  *
  *
+ * The handler is wrapped in `Effect.ensuring(store.dispose(ctx))` so the loaded
+ * InstanceContext is disposed (runDisposers + IPC `server.instance.disposed`)
+ * on every Exit — success, typed failure, defect, or interruption. Matches the
+ * legacy `bootstrap()` finally-disposal semantics without per-handler boilerplate.
+ *
  * Errors propagate to the existing top-level handler in `src/index.ts`; use
  * Errors propagate to the existing top-level handler in `src/index.ts`; use
  * `fail("...")` for user-visible domain failures (clean exit, formatted message).
  * `fail("...")` for user-visible domain failures (clean exit, formatted message).
  *
  *
@@ -47,6 +53,17 @@ export const effectCmd = <Args, A>(opts: {
       // yargs typing wraps Args in ArgumentsCamelCase<WithDoubleDash<...>>; cast at the boundary.
       // yargs typing wraps Args in ArgumentsCamelCase<WithDoubleDash<...>>; cast at the boundary.
       const args = rawArgs as unknown as Args
       const args = rawArgs as unknown as Args
       const directory = opts.directory?.(args) ?? process.cwd()
       const directory = opts.directory?.(args) ?? process.cwd()
-      await AppRuntime.runPromise(InstanceStore.Service.use((s) => s.provide({ directory }, opts.handler(args))))
+      await AppRuntime.runPromise(
+        InstanceStore.Service.use((store) =>
+          store.provide(
+            { directory },
+            Effect.gen(function* () {
+              const ctx = yield* InstanceRef
+              const body = opts.handler(args)
+              return ctx ? yield* body.pipe(Effect.ensuring(store.dispose(ctx))) : yield* body
+            }),
+          ),
+        ),
+      )
     },
     },
   })
   })