Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/opencode/src/project/instance-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export const reloadInstance = (input: LoadInput) =>
export const disposeInstance = (ctx: Parameters<InstanceStore.Interface["dispose"]>[0]) =>
AppRuntime.runPromise(InstanceStore.Service.use((store) => store.dispose(ctx)))

export const disposeDirectory = (directory: string) =>
AppRuntime.runPromise(InstanceStore.Service.use((store) => store.disposeDirectory(directory)))

export const disposeAllInstances = () =>
AppRuntime.runPromise(InstanceStore.Service.use((store) => store.disposeAll()))

Expand Down
13 changes: 13 additions & 0 deletions packages/opencode/src/project/instance-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface Interface {
readonly load: (input: LoadInput) => Effect.Effect<InstanceContext, unknown>
readonly reload: (input: LoadInput) => Effect.Effect<InstanceContext, unknown>
readonly dispose: (ctx: InstanceContext) => Effect.Effect<void>
readonly disposeDirectory: (directory: string) => Effect.Effect<void>
readonly disposeAll: () => Effect.Effect<void>
readonly provide: <A, E, R>(input: LoadInput, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E | unknown, R>
}
Expand Down Expand Up @@ -186,6 +187,17 @@ export const layer = Layer.effect(
yield* disposeEntry(directory, entry, ctx).pipe(Effect.asVoid)
})

const disposeDirectory = (inputDirectory: string) =>
Effect.gen(function* () {
const directory = Filesystem.resolve(inputDirectory)
const entry = entries.get(directory)
if (!entry) return

const exit = yield* Deferred.await(entry.deferred).pipe(Effect.exit)
if (Exit.isFailure(exit)) return yield* removeEntry(directory, entry).pipe(Effect.asVoid)
yield* disposeEntry(directory, entry, exit.value).pipe(Effect.asVoid)
})

const disposeAllOnce = Effect.gen(function* () {
yield* Effect.forEach(
[...entries.entries()],
Expand Down Expand Up @@ -222,6 +234,7 @@ export const layer = Layer.effect(
load,
reload,
dispose,
disposeDirectory,
disposeAll,
provide: (input, effect) => load(input).pipe(Effect.flatMap((ctx) => effect.pipe(Effect.provideService(InstanceRef, ctx)))),
}
Expand Down
6 changes: 6 additions & 0 deletions packages/opencode/src/project/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ export const Instance = {
await instanceRuntime.disposeInstance(ctx)
directories.delete(ctx.directory)
},
async disposeDirectory(inputDirectory: string) {
const directory = Filesystem.resolve(inputDirectory)
const instanceRuntime = await runtime()
await instanceRuntime.disposeDirectory(directory)
directories.delete(directory)
},
async disposeAll() {
const { disposeAllLoadedInstances } = await import("./instance-store")
await disposeAllLoadedInstances()
Expand Down
23 changes: 23 additions & 0 deletions packages/opencode/test/project/instance-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,29 @@ describe("InstanceStore", () => {
}),
)

it.live("disposeDirectory disposes loaded entries without loading missing directories", () =>
Effect.gen(function* () {
const loaded = yield* tmpdirScoped()
const missing = yield* tmpdirScoped()
const store = yield* InstanceStore.Service
const disposed: string[] = []
const off = registerDisposer(async (directory) => {
disposed.push(directory)
})
yield* Effect.addFinalizer(() => Effect.sync(off))

yield* store.load({ directory: loaded })

bootstrapRun = Effect.sync(() => {
throw new Error("disposeDirectory must not bootstrap unloaded directories")
})
yield* store.disposeDirectory(missing)
yield* store.disposeDirectory(loaded)

expect(disposed).toEqual([loaded])
}),
)

it.live("drops failed loads so the next attempt can boot again", () =>
Effect.gen(function* () {
const dir = yield* tmpdirScoped()
Expand Down
9 changes: 5 additions & 4 deletions packages/opencode/test/session/export.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ import { LLMTrace } from "../../src/session/llm-trace"
const projectRoot = path.join(__dirname, "../..")
void Log.init({ print: false })

async function removeSessionProjectDirectory(dir: string) {
async function removeLoadedSessionProjectDirectory(dir: string) {
await Instance.disposeDirectory(dir)
await fs.rm(dir, {
recursive: true,
force: true,
maxRetries: process.platform === "win32" ? 30 : 5,
maxRetries: 5,
Comment thread
Astro-Han marked this conversation as resolved.
Outdated
retryDelay: 100,
})
}
Expand Down Expand Up @@ -201,7 +202,7 @@ describe("Export.session", () => {
})

try {
await removeSessionProjectDirectory(sessionProject.path)
await removeLoadedSessionProjectDirectory(sessionProject.path)
await Instance.provide({
directory: currentProject.path,
fn: async () => {
Expand Down Expand Up @@ -271,7 +272,7 @@ describe("Export.session", () => {
})

await Config.invalidate(true)
await removeSessionProjectDirectory(sessionProject.path)
await removeLoadedSessionProjectDirectory(sessionProject.path)
await Instance.provide({
directory: currentProject.path,
fn: async () => {
Expand Down
Loading