Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion packages/opencode/src/agent/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import path from "path"
import { Plugin } from "@/plugin"
import { Skill } from "../skill"
import { Effect, Context, Layer } from "effect"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"
import * as Option from "effect/Option"
import * as OtelTracer from "@effect/opentelemetry/Tracer"

Expand Down
4 changes: 2 additions & 2 deletions packages/opencode/src/bus/bus.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import z from "zod"
import { Effect, Exit, Layer, PubSub, Scope, Context, Stream } from "effect"
import { EffectBridge } from "@/effect/bridge"
import { EffectBridge } from "@/effect"
import { Log } from "../util/log"
import { BusEvent } from "./bus-event"
import { GlobalBus } from "./global"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"
import { makeRuntime } from "@/effect/run-service"

const log = Log.create({ service: "bus" })
Expand Down
4 changes: 2 additions & 2 deletions packages/opencode/src/command/command.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BusEvent } from "@/bus/bus-event"
import { InstanceState } from "@/effect/instance-state"
import { EffectBridge } from "@/effect/bridge"
import { InstanceState } from "@/effect"
import { EffectBridge } from "@/effect"
import type { InstanceContext } from "@/project/instance"
import { SessionID, MessageID } from "@/session/schema"
import { Effect, Layer, Context } from "effect"
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { isRecord } from "@/util/record"
import { ConfigPaths } from "./paths"
import type { ConsoleState } from "./console-state"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"
import { Context, Duration, Effect, Exit, Fiber, Layer, Option } from "effect"
import { Flock } from "@opencode-ai/shared/util/flock"
import { isPathPluginSpec, parsePluginSpecifier, resolvePathPluginTarget } from "@/plugin/shared"
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/config/tui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Flag } from "@/flag/flag"
import { Log } from "@/util/log"
import { isRecord } from "@/util/record"
import { Global } from "@/global"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"
import { makeRuntime } from "@/effect/run-service"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"

Expand Down
72 changes: 35 additions & 37 deletions packages/opencode/src/effect/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,43 @@ import { LocalContext } from "@/util/local-context"
import { InstanceRef, WorkspaceRef } from "./instance-ref"
import { attachWith } from "./run-service"

export namespace EffectBridge {
export interface Shape {
readonly promise: <A, E, R>(effect: Effect.Effect<A, E, R>) => Promise<A>
readonly fork: <A, E, R>(effect: Effect.Effect<A, E, R>) => Fiber.Fiber<A, E>
}
export interface Shape {
readonly promise: <A, E, R>(effect: Effect.Effect<A, E, R>) => Promise<A>
readonly fork: <A, E, R>(effect: Effect.Effect<A, E, R>) => Fiber.Fiber<A, E>
}

function restore<R>(instance: InstanceContext | undefined, workspace: string | undefined, fn: () => R): R {
if (instance && workspace !== undefined) {
return WorkspaceContext.restore(workspace, () => Instance.restore(instance, fn))
}
if (instance) return Instance.restore(instance, fn)
if (workspace !== undefined) return WorkspaceContext.restore(workspace, fn)
return fn()
function restore<R>(instance: InstanceContext | undefined, workspace: string | undefined, fn: () => R): R {
if (instance && workspace !== undefined) {
return WorkspaceContext.restore(workspace, () => Instance.restore(instance, fn))
}
if (instance) return Instance.restore(instance, fn)
if (workspace !== undefined) return WorkspaceContext.restore(workspace, fn)
return fn()
}

export function make(): Effect.Effect<Shape> {
return Effect.gen(function* () {
const ctx = yield* Effect.context()
const value = yield* InstanceRef
const instance =
value ??
(() => {
try {
return Instance.current
} catch (err) {
if (!(err instanceof LocalContext.NotFound)) throw err
}
})()
const workspace = (yield* WorkspaceRef) ?? WorkspaceContext.workspaceID
const attach = <A, E, R>(effect: Effect.Effect<A, E, R>) => attachWith(effect, { instance, workspace })
const wrap = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
attach(effect).pipe(Effect.provide(ctx)) as Effect.Effect<A, E, never>
export function make(): Effect.Effect<Shape> {
return Effect.gen(function* () {
const ctx = yield* Effect.context()
const value = yield* InstanceRef
const instance =
value ??
(() => {
try {
return Instance.current
} catch (err) {
if (!(err instanceof LocalContext.NotFound)) throw err
}
})()
const workspace = (yield* WorkspaceRef) ?? WorkspaceContext.workspaceID
const attach = <A, E, R>(effect: Effect.Effect<A, E, R>) => attachWith(effect, { instance, workspace })
const wrap = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
attach(effect).pipe(Effect.provide(ctx)) as Effect.Effect<A, E, never>

return {
promise: <A, E, R>(effect: Effect.Effect<A, E, R>) =>
restore(instance, workspace, () => Effect.runPromise(wrap(effect))),
fork: <A, E, R>(effect: Effect.Effect<A, E, R>) =>
restore(instance, workspace, () => Effect.runFork(wrap(effect))),
} satisfies Shape
})
}
return {
promise: <A, E, R>(effect: Effect.Effect<A, E, R>) =>
restore(instance, workspace, () => Effect.runPromise(wrap(effect))),
fork: <A, E, R>(effect: Effect.Effect<A, E, R>) =>
restore(instance, workspace, () => Effect.runFork(wrap(effect))),
} satisfies Shape
})
}
2 changes: 2 additions & 0 deletions packages/opencode/src/effect/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * as InstanceState from "./instance-state"
export * as EffectBridge from "./bridge"
112 changes: 55 additions & 57 deletions packages/opencode/src/effect/instance-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,72 +13,70 @@ export interface InstanceState<A, E = never, R = never> {
readonly cache: ScopedCache.ScopedCache<string, A, E, R>
}

export namespace InstanceState {
export const bind = <F extends (...args: any[]) => any>(fn: F): F => {
try {
return Instance.bind(fn)
} catch (err) {
if (!(err instanceof LocalContext.NotFound)) throw err
}
const fiber = Fiber.getCurrent()
const ctx = fiber ? Context.getReferenceUnsafe(fiber.context, InstanceRef) : undefined
if (!ctx) return fn
return ((...args: any[]) => Instance.restore(ctx, () => fn(...args))) as F
export const bind = <F extends (...args: any[]) => any>(fn: F): F => {
try {
return Instance.bind(fn)
} catch (err) {
if (!(err instanceof LocalContext.NotFound)) throw err
}
const fiber = Fiber.getCurrent()
const ctx = fiber ? Context.getReferenceUnsafe(fiber.context, InstanceRef) : undefined
if (!ctx) return fn
return ((...args: any[]) => Instance.restore(ctx, () => fn(...args))) as F
}

export const context = Effect.gen(function* () {
return (yield* InstanceRef) ?? Instance.current
})
export const context = Effect.gen(function* () {
return (yield* InstanceRef) ?? Instance.current
})

export const workspaceID = Effect.gen(function* () {
return (yield* WorkspaceRef) ?? WorkspaceContext.workspaceID
})
export const workspaceID = Effect.gen(function* () {
return (yield* WorkspaceRef) ?? WorkspaceContext.workspaceID
})

export const directory = Effect.map(context, (ctx) => ctx.directory)
export const directory = Effect.map(context, (ctx) => ctx.directory)

export const make = <A, E = never, R = never>(
init: (ctx: InstanceContext) => Effect.Effect<A, E, R | Scope.Scope>,
): Effect.Effect<InstanceState<A, E, Exclude<R, Scope.Scope>>, never, R | Scope.Scope> =>
Effect.gen(function* () {
const cache = yield* ScopedCache.make<string, A, E, R>({
capacity: Number.POSITIVE_INFINITY,
lookup: () =>
Effect.gen(function* () {
return yield* init(yield* context)
}),
})
export const make = <A, E = never, R = never>(
init: (ctx: InstanceContext) => Effect.Effect<A, E, R | Scope.Scope>,
): Effect.Effect<InstanceState<A, E, Exclude<R, Scope.Scope>>, never, R | Scope.Scope> =>
Effect.gen(function* () {
const cache = yield* ScopedCache.make<string, A, E, R>({
capacity: Number.POSITIVE_INFINITY,
lookup: () =>
Effect.gen(function* () {
return yield* init(yield* context)
}),
})

const off = registerDisposer((directory) =>
Effect.runPromise(ScopedCache.invalidate(cache, directory).pipe(Effect.provide(EffectLogger.layer))),
)
yield* Effect.addFinalizer(() => Effect.sync(off))
const off = registerDisposer((directory) =>
Effect.runPromise(ScopedCache.invalidate(cache, directory).pipe(Effect.provide(EffectLogger.layer))),
)
yield* Effect.addFinalizer(() => Effect.sync(off))

return {
[TypeId]: TypeId,
cache,
}
})
return {
[TypeId]: TypeId,
cache,
}
})

export const get = <A, E, R>(self: InstanceState<A, E, R>) =>
Effect.gen(function* () {
return yield* ScopedCache.get(self.cache, yield* directory)
})
export const get = <A, E, R>(self: InstanceState<A, E, R>) =>
Effect.gen(function* () {
return yield* ScopedCache.get(self.cache, yield* directory)
})

export const use = <A, E, R, B>(self: InstanceState<A, E, R>, select: (value: A) => B) =>
Effect.map(get(self), select)
export const use = <A, E, R, B>(self: InstanceState<A, E, R>, select: (value: A) => B) =>
Effect.map(get(self), select)

export const useEffect = <A, E, R, B, E2, R2>(
self: InstanceState<A, E, R>,
select: (value: A) => Effect.Effect<B, E2, R2>,
) => Effect.flatMap(get(self), select)
export const useEffect = <A, E, R, B, E2, R2>(
self: InstanceState<A, E, R>,
select: (value: A) => Effect.Effect<B, E2, R2>,
) => Effect.flatMap(get(self), select)

export const has = <A, E, R>(self: InstanceState<A, E, R>) =>
Effect.gen(function* () {
return yield* ScopedCache.has(self.cache, yield* directory)
})
export const has = <A, E, R>(self: InstanceState<A, E, R>) =>
Effect.gen(function* () {
return yield* ScopedCache.has(self.cache, yield* directory)
})

export const invalidate = <A, E, R>(self: InstanceState<A, E, R>) =>
Effect.gen(function* () {
return yield* ScopedCache.invalidate(self.cache, yield* directory)
})
}
export const invalidate = <A, E, R>(self: InstanceState<A, E, R>) =>
Effect.gen(function* () {
return yield* ScopedCache.invalidate(self.cache, yield* directory)
})
2 changes: 1 addition & 1 deletion packages/opencode/src/env/env.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Context, Effect, Layer } from "effect"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"

type State = Record<string, string | undefined>

Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/file/file.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BusEvent } from "@/bus/bus-event"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"

import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { Git } from "@/git"
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/file/time.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DateTime, Effect, Layer, Option, Semaphore, Context } from "effect"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { Flag } from "@/flag/flag"
import type { SessionID } from "@/session/schema"
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/file/watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import path from "path"
import z from "zod"
import { Bus } from "@/bus"
import { BusEvent } from "@/bus/bus-event"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"
import { Flag } from "@/flag/flag"
import { Git } from "@/git"
import { Instance } from "@/project/instance"
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/format/format.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Effect, Layer, Context } from "effect"
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"
import path from "path"
import { mergeDeep } from "remeda"
import z from "zod"
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/lsp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { Flag } from "@/flag/flag"
import { Process } from "../util/process"
import { spawn as lspspawn } from "./launch"
import { Effect, Layer, Context } from "effect"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"

export namespace LSP {
const log = Log.create({ service: "lsp" })
Expand Down
4 changes: 2 additions & 2 deletions packages/opencode/src/mcp/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import { Bus } from "@/bus"
import { TuiEvent } from "@/cli/cmd/tui/event"
import open from "open"
import { Effect, Exit, Layer, Option, Context, Stream } from "effect"
import { EffectBridge } from "@/effect/bridge"
import { InstanceState } from "@/effect/instance-state"
import { EffectBridge } from "@/effect"
import { InstanceState } from "@/effect"
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner"

Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/permission/permission.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Bus } from "@/bus"
import { BusEvent } from "@/bus/bus-event"
import { Config } from "@/config"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"
import { ProjectID } from "@/project/schema"
import { MessageID, SessionID } from "@/session/schema"
import { PermissionTable } from "@/session/session.sql"
Expand Down
4 changes: 2 additions & 2 deletions packages/opencode/src/plugin/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import { gitlabAuthPlugin as GitlabAuthPlugin } from "opencode-gitlab-auth"
import { PoeAuthPlugin } from "opencode-poe-auth"
import { CloudflareAIGatewayAuthPlugin, CloudflareWorkersAuthPlugin } from "./cloudflare"
import { Effect, Layer, Context, Stream } from "effect"
import { EffectBridge } from "@/effect/bridge"
import { InstanceState } from "@/effect/instance-state"
import { EffectBridge } from "@/effect"
import { InstanceState } from "@/effect"
import { errorMessage } from "@/util/error"
import { PluginLoader } from "./loader"
import { parsePluginSpecifier, readPluginId, readV1Plugin, resolvePluginId } from "./shared"
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/project/vcs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { formatPatch, structuredPatch } from "diff"
import path from "path"
import { Bus } from "@/bus"
import { BusEvent } from "@/bus/bus-event"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { FileWatcher } from "@/file/watcher"
import { Git } from "@/git"
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/provider/auth.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { AuthOAuthResult, Hooks } from "@opencode-ai/plugin"
import { NamedError } from "@opencode-ai/shared/util/error"
import { Auth } from "@/auth"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"
import { zod } from "@/util/effect-zod"
import { withStatics } from "@/util/schema"
import { Plugin } from "../plugin"
Expand Down
4 changes: 2 additions & 2 deletions packages/opencode/src/provider/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import { iife } from "@/util/iife"
import { Global } from "../global"
import path from "path"
import { Effect, Layer, Context } from "effect"
import { EffectBridge } from "@/effect/bridge"
import { InstanceState } from "@/effect/instance-state"
import { EffectBridge } from "@/effect"
import { InstanceState } from "@/effect"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { isRecord } from "@/util/record"

Expand Down
4 changes: 2 additions & 2 deletions packages/opencode/src/pty/service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BusEvent } from "@/bus/bus-event"
import { Bus } from "@/bus"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"
import { Instance } from "@/project/instance"
import type { Proc } from "#pty"
import z from "zod"
Expand All @@ -10,7 +10,7 @@ import { Shell } from "@/shell/shell"
import { Plugin } from "@/plugin"
import { PtyID } from "./schema"
import { Effect, Layer, Context } from "effect"
import { EffectBridge } from "@/effect/bridge"
import { EffectBridge } from "@/effect"

const log = Log.create({ service: "pty" })

Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/question/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Deferred, Effect, Layer, Schema, Context } from "effect"
import { Bus } from "@/bus"
import { BusEvent } from "@/bus/bus-event"
import { InstanceState } from "@/effect/instance-state"
import { InstanceState } from "@/effect"
import { SessionID, MessageID } from "@/session/schema"
import { zod } from "@/util/effect-zod"
import { Log } from "@/util/log"
Expand Down
Loading
Loading