@@ -5,22 +5,29 @@ import { disposeInstance } from "@/effect/instance-registry"
55import { makeRuntime } from "@/effect/run-service"
66import { AppFileSystem } from "@opencode-ai/core/filesystem"
77import { Context , Deferred , Duration , Effect , Exit , Layer , Scope } from "effect"
8- import { context , type InstanceContext } from "./instance-context"
8+ import { type InstanceContext } from "./instance-context"
99import * as Project from "./project"
1010
11- export interface LoadInput {
11+ export interface LoadInput < R = never > {
1212 directory : string
13- init ?: ( ) => Promise < unknown >
13+ /**
14+ * Additional setup to run after the default InstanceBootstrap.
15+ * Mainly used by tests for env-var setup or file writes that need the instance ALS context.
16+ */
17+ init ?: Effect . Effect < void , never , R >
1418 worktree ?: string
1519 project ?: Project . Info
1620}
1721
1822export interface Interface {
19- readonly load : ( input : LoadInput ) => Effect . Effect < InstanceContext >
20- readonly reload : ( input : LoadInput ) => Effect . Effect < InstanceContext >
23+ readonly load : < R = never > ( input : LoadInput < R > ) => Effect . Effect < InstanceContext , never , R >
24+ readonly reload : < R = never > ( input : LoadInput < R > ) => Effect . Effect < InstanceContext , never , R >
2125 readonly dispose : ( ctx : InstanceContext ) => Effect . Effect < void >
2226 readonly disposeAll : ( ) => Effect . Effect < void >
23- readonly provide : < A , E , R > ( input : LoadInput , effect : Effect . Effect < A , E , R > ) => Effect . Effect < A , E , R >
27+ readonly provide : < A , E , R , R2 = never > (
28+ input : LoadInput < R2 > ,
29+ effect : Effect . Effect < A , E , R > ,
30+ ) => Effect . Effect < A , E , R | R2 >
2431}
2532
2633export class Service extends Context . Service < Service , Interface > ( ) ( "@opencode/InstanceStore" ) { }
@@ -36,25 +43,25 @@ export const layer: Layer.Layer<Service, never, Project.Service> = Layer.effect(
3643 const scope = yield * Scope . Scope
3744 const cache = new Map < string , Entry > ( )
3845
39- const boot = Effect . fn ( "InstanceStore.boot" ) ( function * ( input : LoadInput & { directory : string } ) {
40- const ctx =
41- input . project && input . worktree
42- ? {
43- directory : input . directory ,
44- worktree : input . worktree ,
45- project : input . project ,
46- }
47- : yield * project . fromDirectory ( input . directory ) . pipe (
48- Effect . map ( ( result ) => ( {
46+ const boot = < R > ( input : LoadInput < R > & { directory : string } ) =>
47+ Effect . gen ( function * ( ) {
48+ const ctx : InstanceContext =
49+ input . project && input . worktree
50+ ? {
4951 directory : input . directory ,
50- worktree : result . sandbox ,
51- project : result . project ,
52- } ) ) ,
53- )
54- const init = input . init
55- if ( init ) yield * Effect . promise ( ( ) => context . provide ( ctx , init ) )
56- return ctx
57- } )
52+ worktree : input . worktree ,
53+ project : input . project ,
54+ }
55+ : yield * project . fromDirectory ( input . directory ) . pipe (
56+ Effect . map ( ( result ) => ( {
57+ directory : input . directory ,
58+ worktree : result . sandbox ,
59+ project : result . project ,
60+ } ) ) ,
61+ )
62+ if ( input . init ) yield * input . init . pipe ( Effect . provideService ( InstanceRef , ctx ) )
63+ return ctx
64+ } ) . pipe ( Effect . withSpan ( "InstanceStore.boot" ) )
5865
5966 const removeEntry = ( directory : string , entry : Entry ) =>
6067 Effect . sync ( ( ) => {
@@ -63,11 +70,12 @@ export const layer: Layer.Layer<Service, never, Project.Service> = Layer.effect(
6370 return true
6471 } )
6572
66- const completeLoad = Effect . fnUntraced ( function * ( directory : string , input : LoadInput , entry : Entry ) {
67- const exit = yield * Effect . exit ( boot ( { ...input , directory } ) )
68- if ( Exit . isFailure ( exit ) ) yield * removeEntry ( directory , entry )
69- yield * Deferred . done ( entry . deferred , exit ) . pipe ( Effect . asVoid )
70- } )
73+ const completeLoad = < R > ( directory : string , input : LoadInput < R > , entry : Entry ) =>
74+ Effect . gen ( function * ( ) {
75+ const exit = yield * Effect . exit ( boot ( { ...input , directory } ) )
76+ if ( Exit . isFailure ( exit ) ) yield * removeEntry ( directory , entry )
77+ yield * Deferred . done ( entry . deferred , exit ) . pipe ( Effect . asVoid )
78+ } )
7179
7280 const emitDisposed = ( input : { directory : string ; project ?: string } ) =>
7381 Effect . sync ( ( ) =>
@@ -98,9 +106,9 @@ export const layer: Layer.Layer<Service, never, Project.Service> = Layer.effect(
98106 return true
99107 } )
100108
101- const load = Effect . fn ( "InstanceStore.load" ) ( function * ( input : LoadInput ) {
109+ const load = < R > ( input : LoadInput < R > ) : Effect . Effect < InstanceContext , never , R > => {
102110 const directory = AppFileSystem . resolve ( input . directory )
103- return yield * Effect . uninterruptibleMask ( ( restore ) =>
111+ return Effect . uninterruptibleMask ( ( restore ) =>
104112 Effect . gen ( function * ( ) {
105113 const existing = cache . get ( directory )
106114 if ( existing ) return yield * restore ( Deferred . await ( existing . deferred ) )
@@ -113,12 +121,12 @@ export const layer: Layer.Layer<Service, never, Project.Service> = Layer.effect(
113121 } ) . pipe ( Effect . forkIn ( scope , { startImmediately : true } ) )
114122 return yield * restore ( Deferred . await ( entry . deferred ) )
115123 } ) ,
116- )
117- } )
124+ ) . pipe ( Effect . withSpan ( "InstanceStore.load" ) )
125+ }
118126
119- const reload = Effect . fn ( "InstanceStore.reload" ) ( function * ( input : LoadInput ) {
127+ const reload = < R > ( input : LoadInput < R > ) : Effect . Effect < InstanceContext , never , R > => {
120128 const directory = AppFileSystem . resolve ( input . directory )
121- return yield * Effect . uninterruptibleMask ( ( restore ) =>
129+ return Effect . uninterruptibleMask ( ( restore ) =>
122130 Effect . gen ( function * ( ) {
123131 const previous = cache . get ( directory )
124132 const entry : Entry = { deferred : Deferred . makeUnsafe < InstanceContext > ( ) }
@@ -134,8 +142,8 @@ export const layer: Layer.Layer<Service, never, Project.Service> = Layer.effect(
134142 } ) . pipe ( Effect . forkIn ( scope , { startImmediately : true } ) )
135143 return yield * restore ( Deferred . await ( entry . deferred ) )
136144 } ) ,
137- )
138- } )
145+ ) . pipe ( Effect . withSpan ( "InstanceStore.reload" ) )
146+ }
139147
140148 const dispose = Effect . fn ( "InstanceStore.dispose" ) ( function * ( ctx : InstanceContext ) {
141149 const entry = cache . get ( ctx . directory )
@@ -170,7 +178,10 @@ export const layer: Layer.Layer<Service, never, Project.Service> = Layer.effect(
170178 return yield * cachedDisposeAll
171179 } )
172180
173- const provide = < A , E , R > ( input : LoadInput , effect : Effect . Effect < A , E , R > ) : Effect . Effect < A , E , R > =>
181+ const provide = < A , E , R , R2 > (
182+ input : LoadInput < R2 > ,
183+ effect : Effect . Effect < A , E , R > ,
184+ ) : Effect . Effect < A , E , R | R2 > =>
174185 load ( input ) . pipe ( Effect . flatMap ( ( ctx ) => effect . pipe ( Effect . provideService ( InstanceRef , ctx ) ) ) )
175186
176187 yield * Effect . addFinalizer ( ( ) => disposeAll ( ) . pipe ( Effect . ignore ) )
0 commit comments