fix: 修正 logo 中 N 和 G 字母造型

N 添加对角线笔画(█▄ █),G 添加内横杠(█ ▀█),
避免与 O 字母造型雷同。同步更新 ui.ts 中的硬编码 wordmark。
This commit is contained in:
airlongdian
2026-06-14 09:48:03 +08:00
commit 9ea05df273
5757 changed files with 1170016 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
import { test } from "bun:test"
import { Context, Effect, Layer } from "effect"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
class A extends Context.Service<A, { readonly value: "a" }>()("test/A") {}
class B extends Context.Service<B, { readonly value: "b" }>()("test/B") {}
class C extends Context.Service<C, { readonly value: "c" }>()("test/C") {}
class LayerError {
readonly _tag = "LayerError"
}
class NotFoundError {
readonly _tag = "NotFoundError"
}
class DiskError {
readonly _tag = "DiskError"
}
class NetworkError {
readonly _tag = "NetworkError"
}
const aImplementation = Layer.succeed(A, A.of({ value: "a" }))
const bImplementation = Layer.effect(
B,
Effect.gen(function* () {
yield* A
return B.of({ value: "b" })
}),
)
const cImplementation = Layer.effect(
C,
Effect.gen(function* () {
yield* A
yield* B
return C.of({ value: "c" })
}),
)
const failingAImplementation = Layer.effect(A, Effect.fail(new LayerError()))
const notFoundAImplementation = Layer.effect(A, Effect.fail(new NotFoundError()))
const diskAImplementation = Layer.effect(A, Effect.fail(new DiskError()))
const networkAImplementation = Layer.effect(A, Effect.fail(new NetworkError()))
const notFoundOrDiskAImplementation = Layer.effect(A, Effect.fail(new NotFoundError() as NotFoundError | DiskError))
type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false
type Assert<T extends true> = T
type AProvides = Assert<Equal<Layer.Success<typeof aImplementation>, A>>
type ARequires = Assert<Equal<Layer.Services<typeof aImplementation>, never>>
type BProvides = Assert<Equal<Layer.Success<typeof bImplementation>, B>>
type BRequires = Assert<Equal<Layer.Services<typeof bImplementation>, A>>
type CRequires = Assert<Equal<Layer.Services<typeof cImplementation>, A | B>>
void (0 as unknown as AProvides)
void (0 as unknown as ARequires)
void (0 as unknown as BProvides)
void (0 as unknown as BRequires)
void (0 as unknown as CRequires)
const a = LayerNode.make(aImplementation, [])
const b = LayerNode.make(bImplementation, [a])
const c = LayerNode.make(cImplementation, [a, b])
const failingA = LayerNode.make(failingAImplementation, [])
const bWithFailingA = LayerNode.make(bImplementation, [failingA])
const notFoundA = LayerNode.make(notFoundAImplementation, [])
const diskA = LayerNode.make(diskAImplementation, [])
const networkA = LayerNode.make(networkAImplementation, [])
const notFoundOrDiskA = LayerNode.make(notFoundOrDiskAImplementation, [])
// @ts-expect-error B requires A
LayerNode.make(bImplementation, [])
// @ts-expect-error C requires both A and B
LayerNode.make(cImplementation, [a])
type ANodeProvides = Assert<Equal<typeof a, LayerNode.Node<A, never>>>
type BNodeProvides = Assert<Equal<typeof b, LayerNode.Node<B, never>>>
type CNodeProvides = Assert<Equal<typeof c, LayerNode.Node<C, never>>>
type FailingANodeError = Assert<Equal<typeof failingA, LayerNode.Node<A, LayerError>>>
type DependentNodeError = Assert<Equal<typeof bWithFailingA, LayerNode.Node<B, LayerError>>>
void (0 as unknown as ANodeProvides)
void (0 as unknown as BNodeProvides)
void (0 as unknown as CNodeProvides)
void (0 as unknown as FailingANodeError)
void (0 as unknown as DependentNodeError)
const closed = LayerNode.buildLayer(c)
const closedWithError = LayerNode.buildLayer(bWithFailingA)
type ClosedProvides = Assert<Equal<Layer.Success<typeof closed>, C>>
type ClosedRequires = Assert<Equal<Layer.Services<typeof closed>, never>>
type ClosedError = Assert<Equal<Layer.Error<typeof closedWithError>, LayerError>>
void (0 as unknown as ClosedProvides)
void (0 as unknown as ClosedRequires)
void (0 as unknown as ClosedError)
const replacement = LayerNode.make(Layer.succeed(A, A.of({ value: "a" })), [])
LayerNode.replace(a, Layer.succeed(A, A.of({ value: "a" })))
LayerNode.replace(notFoundOrDiskA, notFoundAImplementation)
LayerNode.replace(notFoundOrDiskA, diskAImplementation)
LayerNode.replaceWithNode(a, replacement)
// @ts-expect-error An override for A must still provide A
LayerNode.replaceWithNode(a, b)
// @ts-expect-error A replacement cannot introduce NetworkError
LayerNode.replace(notFoundOrDiskA, networkAImplementation)
// @ts-expect-error A replacement layer must not have unresolved dependencies
LayerNode.replace(b, bImplementation)
test("type exploration compiles", () => {})

View File

@@ -0,0 +1,204 @@
import { describe, expect, test } from "bun:test"
import { Cause, Context, Effect, Exit, Layer } from "effect"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
const { buildLayer: build, group, replace, replaceWithNode } = LayerNode
const node = LayerNode.make
class Value extends Context.Service<Value, { readonly value: string }>()("test/Value") {}
class Greeting extends Context.Service<Greeting, { readonly text: string }>()("test/Greeting") {}
const value = LayerNode.make(Layer.succeed(Value, Value.of({ value: "production" })), [])
const greetingImplementation = Layer.effect(
Greeting,
Effect.gen(function* () {
return Greeting.of({ text: `hello ${(yield* Value).value}` })
}),
)
const greeting = LayerNode.make(greetingImplementation, [value])
// @ts-expect-error Greeting requires Value
LayerNode.make(greetingImplementation, [])
describe("app graph", () => {
test("creates any selected dependency layer", async () => {
const result = Effect.gen(function* () {
return (yield* Greeting).text
}).pipe(Effect.provide(build(greeting)))
expect(await Effect.runPromise(result)).toBe("hello production")
})
test("applies overrides before dependency materialization", async () => {
const replacement = Layer.succeed(Value, Value.of({ value: "simulation" }))
const graph = build(greeting, { replacements: [replace(value, replacement)] })
const result = Effect.gen(function* () {
return (yield* Greeting).text
}).pipe(Effect.provide(graph))
expect(await Effect.runPromise(result)).toBe("hello simulation")
})
test("acquires a shared dependency once", async () => {
class Shared extends Context.Service<Shared, { readonly value: string }>()("test/Shared") {}
class Left extends Context.Service<Left, { readonly value: string }>()("test/Left") {}
class Right extends Context.Service<Right, { readonly value: string }>()("test/Right") {}
let acquisitions = 0
const shared = node(
Layer.effect(
Shared,
Effect.sync(() => {
acquisitions++
return Shared.of({ value: "shared" })
}),
),
[],
)
const left = node(
Layer.effect(
Left,
Effect.gen(function* () {
return Left.of({ value: `${(yield* Shared).value}-left` })
}),
),
[shared],
)
const right = node(
Layer.effect(
Right,
Effect.gen(function* () {
return Right.of({ value: `${(yield* Shared).value}-right` })
}),
),
[shared],
)
const result = Effect.gen(function* () {
return [(yield* Left).value, (yield* Right).value]
}).pipe(Effect.provide(build(group([left, right]))))
expect(await Effect.runPromise(result)).toEqual(["shared-left", "shared-right"])
expect(acquisitions).toBe(1)
})
test("applies a replacement to every transitive consumer", async () => {
class Left extends Context.Service<Left, { readonly value: string }>()("test/ReplacementLeft") {}
class Right extends Context.Service<Right, { readonly value: string }>()("test/ReplacementRight") {}
const left = node(
Layer.effect(
Left,
Effect.gen(function* () {
return Left.of({ value: (yield* Value).value })
}),
),
[value],
)
const right = node(
Layer.effect(
Right,
Effect.gen(function* () {
return Right.of({ value: (yield* Value).value })
}),
),
[value],
)
const replacement = Layer.succeed(Value, Value.of({ value: "simulation" }))
const graph = build(group([left, right]), { replacements: [replace(value, replacement)] })
const result = Effect.gen(function* () {
return [(yield* Left).value, (yield* Right).value]
}).pipe(Effect.provide(graph))
expect(await Effect.runPromise(result)).toEqual(["simulation", "simulation"])
})
test("propagates layer acquisition errors", async () => {
class AcquisitionError {
readonly _tag = "AcquisitionError"
}
const failing = node(Layer.effect(Value, Effect.fail(new AcquisitionError())), [])
const exit = await Effect.runPromiseExit(Effect.provide(Value, build(failing)))
expect(Exit.isFailure(exit)).toBe(true)
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(AcquisitionError)
})
test("groups expose every selected service", async () => {
class Count extends Context.Service<Count, { readonly value: number }>()("test/Count") {}
const count = node(Layer.succeed(Count, Count.of({ value: 3 })), [])
const result = Effect.gen(function* () {
return { text: (yield* Value).value, count: (yield* Count).value }
}).pipe(Effect.provide(build(group([value, count]))))
expect(await Effect.runPromise(result)).toEqual({ text: "production", count: 3 })
})
test("builds an empty group", async () => {
expect(await Effect.runPromise(Effect.succeed("ok").pipe(Effect.provide(build(group([])))))).toBe("ok")
})
test("builds replacements with their own dependencies", async () => {
class ReplacementConfig extends Context.Service<ReplacementConfig, { readonly value: string }>()(
"test/ReplacementConfig",
) {}
const replacementConfig = node(Layer.succeed(ReplacementConfig, ReplacementConfig.of({ value: "replacement" })), [])
const replacement = node(
Layer.effect(
Value,
Effect.gen(function* () {
return Value.of({ value: (yield* ReplacementConfig).value })
}),
),
[replacementConfig],
)
const result = Effect.gen(function* () {
return (yield* Greeting).text
}).pipe(Effect.provide(build(greeting, { replacements: [replaceWithNode(value, replacement)] })))
expect(await Effect.runPromise(result)).toBe("hello replacement")
})
test("does not acquire unreachable replacements", async () => {
let acquisitions = 0
const unreachable = node(Layer.succeed(Value, Value.of({ value: "unreachable" })), [])
const replacement = Layer.effect(
Value,
Effect.sync(() => {
acquisitions++
return Value.of({ value: "replacement" })
}),
)
await Effect.runPromise(
Effect.provide(Greeting, build(greeting, { replacements: [replace(unreachable, replacement)] })),
)
expect(acquisitions).toBe(0)
})
test("rejects a direct cycle", () => {
const cyclic = node(Layer.succeed(Value, Value.of({ value: "cyclic" })), [])
;(cyclic.dependencies as LayerNode.Node<unknown, unknown>[]).push(cyclic)
expect(() => build(cyclic)).toThrow("Cycle detected in app graph: layer#1 -> layer#1")
})
test("rejects an indirect cycle", () => {
const first = node(Layer.succeed(Value, Value.of({ value: "first" })), [])
const second = node(Layer.succeed(Value, Value.of({ value: "second" })), [first])
const third = node(Layer.succeed(Value, Value.of({ value: "third" })), [second])
;(first.dependencies as LayerNode.Node<unknown, unknown>[]).push(third)
expect(() => build(first)).toThrow("Cycle detected in app graph: layer#1 -> layer#2 -> layer#3 -> layer#1")
})
test("rejects a cycle introduced by a replacement", () => {
const replacement = node(Layer.succeed(Value, Value.of({ value: "replacement" })), [])
const consumer = node(greetingImplementation, [value])
;(replacement.dependencies as LayerNode.Node<unknown, unknown>[]).push(consumer)
expect(() => build(consumer, { replacements: [replaceWithNode(value, replacement)] })).toThrow(
"Cycle detected in app graph: layer#1 -> layer#2 -> layer#1",
)
})
})

View File

@@ -0,0 +1,99 @@
import { expect } from "bun:test"
import { Context, Deferred, Effect, Fiber, Layer, Logger } from "effect"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { AppLayer } from "../../src/effect/app-runtime"
import { EffectBridge } from "@/effect/bridge"
import { InstanceRef } from "../../src/effect/instance-ref"
import * as Observability from "@opencode-ai/core/observability"
import { attach } from "../../src/effect/run-service"
import { TestInstance } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
const it = testEffect(CrossSpawnSpawner.defaultLayer)
function check(loggers: ReadonlySet<Logger.Logger<unknown, any>>) {
return {
tracerLogger: loggers.has(Logger.tracerLogger),
size: loggers.size,
}
}
it.live("makeRuntime installs the observability logger", () =>
Effect.gen(function* () {
class Dummy extends Context.Service<Dummy, { readonly current: () => Effect.Effect<ReturnType<typeof check>> }>()(
"@test/Dummy",
) {}
const layer = Layer.effect(
Dummy,
Effect.gen(function* () {
return Dummy.of({
current: () => Effect.map(Effect.service(Logger.CurrentLoggers), check),
})
}),
)
const current = yield* Dummy.use((svc) => svc.current()).pipe(
Effect.provide(Layer.provideMerge(layer, Observability.layer)),
)
expect(current.size).toBeGreaterThan(0)
}),
)
it.live("AppLayer also installs the observability logger", () =>
Effect.gen(function* () {
const current = yield* Effect.map(Effect.service(Logger.CurrentLoggers), check).pipe(Effect.provide(AppLayer))
expect(current.size).toBeGreaterThan(0)
}),
)
it.instance(
"attach preserves InstanceRef from the current fiber context",
() =>
Effect.gen(function* () {
const test = yield* TestInstance
const current = yield* attach(
Effect.gen(function* () {
return (yield* InstanceRef)?.directory
}),
)
expect(current).toBe(test.directory)
}),
{ git: true },
)
it.instance(
"EffectBridge preserves logger and instance context across async boundaries",
() =>
Effect.gen(function* () {
const test = yield* TestInstance
const bridge = yield* EffectBridge.make()
const started = yield* Deferred.make<void>()
const fiber = yield* Effect.gen(function* () {
yield* Deferred.succeed(started, undefined)
return yield* Effect.promise(() =>
Promise.resolve().then(() =>
bridge.promise(
Effect.gen(function* () {
return {
directory: (yield* InstanceRef)?.directory,
...check(yield* Effect.service(Logger.CurrentLoggers)),
}
}),
),
),
)
}).pipe(Effect.forkScoped)
yield* Deferred.await(started)
const result = yield* Fiber.join(fiber)
expect(result.directory).toBe(test.directory)
expect(result.size).toBeGreaterThan(0)
}).pipe(Effect.provide(Observability.layer)),
{ git: true },
)

View File

@@ -0,0 +1,65 @@
import { describe, expect } from "bun:test"
import { Config, ConfigProvider, Context, Effect, Layer, Option } from "effect"
import { ConfigService } from "../../src/effect/config-service"
import { it } from "../lib/effect"
class TestConfig extends ConfigService.Service<TestConfig>()("@test/ConfigService", {
name: Config.string("NAME"),
token: Config.string("TOKEN").pipe(Config.option),
port: Config.number("PORT").pipe(Config.withDefault(3000)),
}) {}
const fromConfig = (input: Record<string, unknown>) =>
TestConfig.defaultLayer.pipe(Layer.provide(ConfigProvider.layer(ConfigProvider.fromUnknown(input))))
const readConfig = TestConfig.useSync((config) => config)
describe("ConfigService", () => {
it.effect("defaultLayer parses values from the active ConfigProvider", () =>
Effect.gen(function* () {
const config = yield* readConfig.pipe(
Effect.provide(
fromConfig({
NAME: "kit",
TOKEN: "secret",
PORT: "4096",
}),
),
)
expect(config.name).toBe("kit")
expect(config.token).toEqual(Option.some("secret"))
expect(config.port).toBe(4096)
}),
)
it.effect("defaultLayer applies Effect Config defaults", () =>
Effect.gen(function* () {
const config = yield* readConfig.pipe(Effect.provide(fromConfig({ NAME: "kit" })))
expect(config.name).toBe("kit")
expect(config.token).toEqual(Option.none())
expect(config.port).toBe(3000)
}),
)
it.effect("layer provides an already parsed service value", () =>
Effect.gen(function* () {
const config = yield* readConfig.pipe(
Effect.provide(
TestConfig.layer({
name: "direct",
token: Option.some("parsed"),
port: 9000,
}),
),
)
expect(config).toEqual({
name: "direct",
token: Option.some("parsed"),
port: 9000,
} satisfies Context.Service.Shape<typeof TestConfig>)
}),
)
})

View File

@@ -0,0 +1,391 @@
import { expect } from "bun:test"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { $ } from "bun"
import { Context, Deferred, Duration, Effect, Exit, Fiber, Layer } from "effect"
import { InstanceState } from "@/effect/instance-state"
import {
disposeAllInstancesEffect,
provideInstanceEffect,
reloadInstance,
testInstanceStoreLayer,
tmpdirScoped,
} from "../fixture/fixture"
import { testEffect } from "../lib/effect"
const it = testEffect(Layer.mergeAll(CrossSpawnSpawner.defaultLayer, testInstanceStoreLayer))
const access = <A, E>(state: InstanceState.InstanceState<A, E>, dir: string) =>
InstanceState.get(state).pipe(provideInstanceEffect(dir))
const tmpdirGitScoped = Effect.gen(function* () {
const dir = yield* tmpdirScoped({ git: true })
yield* Effect.promise(() => $`git commit --allow-empty --amend -m ${`root commit ${dir}`}`.cwd(dir).quiet())
return dir
})
it.live("InstanceState caches values per directory", () =>
Effect.gen(function* () {
const dir = yield* tmpdirScoped()
let n = 0
const state = yield* InstanceState.make(() => Effect.sync(() => ({ n: ++n })))
const a = yield* access(state, dir)
const b = yield* access(state, dir)
expect(a).toBe(b)
expect(n).toBe(1)
}),
)
it.live("InstanceState isolates directories", () =>
Effect.gen(function* () {
const one = yield* tmpdirScoped()
const two = yield* tmpdirScoped()
let n = 0
const state = yield* InstanceState.make((dir) => Effect.sync(() => ({ dir, n: ++n })))
const a = yield* access(state, one)
const b = yield* access(state, two)
const c = yield* access(state, one)
expect(a).toBe(c)
expect(a).not.toBe(b)
expect(n).toBe(2)
}),
)
it.live("InstanceState invalidates on reload", () =>
Effect.gen(function* () {
const dir = yield* tmpdirScoped()
const seen: string[] = []
let n = 0
const state = yield* InstanceState.make(() =>
Effect.acquireRelease(
Effect.sync(() => ({ n: ++n })),
(value) =>
Effect.sync(() => {
seen.push(String(value.n))
}),
),
)
const a = yield* access(state, dir)
yield* reloadInstance({ directory: dir })
const b = yield* access(state, dir)
expect(a).not.toBe(b)
expect(seen).toEqual(["1"])
}),
)
it.live("InstanceState invalidates on disposeAll", () =>
Effect.gen(function* () {
const one = yield* tmpdirScoped()
const two = yield* tmpdirScoped()
const seen: string[] = []
const state = yield* InstanceState.make((ctx) =>
Effect.acquireRelease(
Effect.sync(() => ({ dir: ctx.directory })),
(value) =>
Effect.sync(() => {
seen.push(value.dir)
}),
),
)
yield* access(state, one)
yield* access(state, two)
yield* disposeAllInstancesEffect
expect(seen.sort()).toEqual([one, two].sort())
}),
)
it.live("InstanceState.get reads the current directory lazily", () =>
Effect.gen(function* () {
const one = yield* tmpdirScoped()
const two = yield* tmpdirScoped()
interface Api {
readonly get: () => Effect.Effect<string>
}
class Test extends Context.Service<Test, Api>()("@test/InstanceStateLazy") {
static readonly layer = Layer.effect(
Test,
Effect.gen(function* () {
const state = yield* InstanceState.make((ctx) => Effect.sync(() => ctx.directory))
const get = InstanceState.get(state)
return Test.of({
get: Effect.fn("Test.get")(function* () {
return yield* get
}),
})
}),
)
}
yield* Effect.gen(function* () {
const a = yield* Test.use((svc) => svc.get()).pipe(provideInstanceEffect(one))
const b = yield* Test.use((svc) => svc.get()).pipe(provideInstanceEffect(two))
expect(a).toBe(one)
expect(b).toBe(two)
}).pipe(Effect.provide(Test.layer))
}),
)
it.live("InstanceState preserves directory across async boundaries", () =>
Effect.gen(function* () {
const one = yield* tmpdirGitScoped
const two = yield* tmpdirGitScoped
const three = yield* tmpdirGitScoped
interface Api {
readonly get: () => Effect.Effect<{ directory: string; worktree: string; project: string }>
}
class Test extends Context.Service<Test, Api>()("@test/InstanceStateAsync") {
static readonly layer = Layer.effect(
Test,
Effect.gen(function* () {
const state = yield* InstanceState.make((ctx) =>
Effect.sync(() => ({
directory: ctx.directory,
worktree: ctx.worktree,
project: ctx.project.id,
})),
)
return Test.of({
get: Effect.fn("Test.get")(function* () {
yield* Effect.sleep(Duration.millis(1))
yield* Effect.sleep(Duration.millis(1))
for (let i = 0; i < 100; i++) {
yield* Effect.yieldNow
}
for (let i = 0; i < 100; i++) {
yield* Effect.promise(() => Promise.resolve())
}
yield* Effect.sleep(Duration.millis(2))
yield* Effect.sleep(Duration.millis(1))
return yield* InstanceState.get(state)
}),
})
}),
)
}
yield* Effect.gen(function* () {
const [a, b, c] = yield* Effect.all(
[one, two, three].map((dir) => Test.use((svc) => svc.get()).pipe(provideInstanceEffect(dir))),
{ concurrency: "unbounded" },
)
expect(a).toEqual({ directory: one, worktree: one, project: a.project })
expect(b).toEqual({ directory: two, worktree: two, project: b.project })
expect(c).toEqual({ directory: three, worktree: three, project: c.project })
expect(a.project).not.toBe(b.project)
expect(a.project).not.toBe(c.project)
expect(b.project).not.toBe(c.project)
}).pipe(Effect.provide(Test.layer))
}),
)
it.live("InstanceState survives high-contention concurrent access", () =>
Effect.gen(function* () {
const dirs = yield* Effect.all(
Array.from({ length: 20 }, () => tmpdirScoped()),
{ concurrency: "unbounded" },
)
interface Api {
readonly get: () => Effect.Effect<string>
}
class Test extends Context.Service<Test, Api>()("@test/HighContention") {
static readonly layer = Layer.effect(
Test,
Effect.gen(function* () {
const state = yield* InstanceState.make((ctx) => Effect.sync(() => ctx.directory))
return Test.of({
get: Effect.fn("Test.get")(function* () {
for (let i = 0; i < 10; i++) {
yield* Effect.sleep(Duration.millis(Math.random() * 3))
yield* Effect.yieldNow
yield* Effect.promise(() => Promise.resolve())
}
return yield* InstanceState.get(state)
}),
})
}),
)
}
yield* Effect.gen(function* () {
const results = yield* Effect.all(
dirs.map((dir) => Test.use((svc) => svc.get()).pipe(provideInstanceEffect(dir))),
{ concurrency: "unbounded" },
)
expect(results).toEqual(dirs)
}).pipe(Effect.provide(Test.layer))
}),
)
it.live("InstanceState correct after interleaved init and dispose", () =>
Effect.gen(function* () {
const one = yield* tmpdirScoped()
const two = yield* tmpdirScoped()
interface Api {
readonly get: () => Effect.Effect<string>
}
class Test extends Context.Service<Test, Api>()("@test/InterleavedDispose") {
static readonly layer = Layer.effect(
Test,
Effect.gen(function* () {
const state = yield* InstanceState.make((ctx) =>
Effect.gen(function* () {
yield* Effect.sleep(Duration.millis(5))
return ctx.directory
}),
)
return Test.of({
get: Effect.fn("Test.get")(function* () {
return yield* InstanceState.get(state)
}),
})
}),
)
}
yield* Effect.gen(function* () {
const a = yield* Test.use((svc) => svc.get()).pipe(provideInstanceEffect(one))
expect(a).toBe(one)
const [, b] = yield* Effect.all(
[reloadInstance({ directory: one }), Test.use((svc) => svc.get()).pipe(provideInstanceEffect(two))],
{ concurrency: "unbounded" },
)
expect(b).toBe(two)
const c = yield* Test.use((svc) => svc.get()).pipe(provideInstanceEffect(one))
expect(c).toBe(one)
}).pipe(Effect.provide(Test.layer))
}),
)
it.live("InstanceState mutation in one directory does not leak to another", () =>
Effect.gen(function* () {
const one = yield* tmpdirScoped()
const two = yield* tmpdirScoped()
const state = yield* InstanceState.make(() => Effect.sync(() => ({ count: 0 })))
const s1 = yield* access(state, one)
s1.count = 42
const s2 = yield* access(state, two)
expect(s2.count).toBe(0)
const s1again = yield* access(state, one)
expect(s1again.count).toBe(42)
expect(s1again).toBe(s1)
}),
)
it.live("InstanceState dedupes concurrent lookups", () =>
Effect.gen(function* () {
const dir = yield* tmpdirScoped()
let n = 0
const state = yield* InstanceState.make(() =>
Effect.gen(function* () {
n += 1
yield* Effect.sleep(Duration.millis(10))
return { n }
}),
)
const [a, b] = yield* Effect.all([access(state, dir), access(state, dir)], { concurrency: "unbounded" })
expect(a).toBe(b)
expect(n).toBe(1)
}),
)
it.live("InstanceState survives deferred resume from the same instance context", () =>
Effect.gen(function* () {
const dir = yield* tmpdirScoped({ git: true })
interface Api {
readonly get: (gate: Deferred.Deferred<void>) => Effect.Effect<string>
}
class Test extends Context.Service<Test, Api>()("@test/DeferredResume") {
static readonly layer = Layer.effect(
Test,
Effect.gen(function* () {
const state = yield* InstanceState.make((ctx) => Effect.sync(() => ctx.directory))
return Test.of({
get: Effect.fn("Test.get")(function* (gate: Deferred.Deferred<void>) {
yield* Deferred.await(gate)
return yield* InstanceState.get(state)
}),
})
}),
)
}
yield* Effect.gen(function* () {
const gate = yield* Deferred.make<void>()
const fiber = yield* Test.use((svc) => svc.get(gate)).pipe(provideInstanceEffect(dir), Effect.forkScoped)
yield* Deferred.succeed(gate, undefined).pipe(provideInstanceEffect(dir))
const exit = yield* Fiber.await(fiber)
expect(Exit.isSuccess(exit)).toBe(true)
if (Exit.isSuccess(exit)) expect(exit.value).toBe(dir)
}).pipe(Effect.provide(Test.layer))
}),
)
it.live("InstanceState survives deferred resume outside ALS when InstanceRef is set", () =>
Effect.gen(function* () {
const dir = yield* tmpdirScoped({ git: true })
interface Api {
readonly get: (gate: Deferred.Deferred<void>) => Effect.Effect<string>
}
class Test extends Context.Service<Test, Api>()("@test/DeferredResumeOutside") {
static readonly layer = Layer.effect(
Test,
Effect.gen(function* () {
const state = yield* InstanceState.make((ctx) => Effect.sync(() => ctx.directory))
return Test.of({
get: Effect.fn("Test.get")(function* (gate: Deferred.Deferred<void>) {
yield* Deferred.await(gate)
return yield* InstanceState.get(state)
}),
})
}),
)
}
yield* Effect.gen(function* () {
const gate = yield* Deferred.make<void>()
const fiber = yield* Test.use((svc) => svc.get(gate)).pipe(provideInstanceEffect(dir), Effect.forkScoped)
yield* Deferred.succeed(gate, undefined)
const exit = yield* Fiber.await(fiber)
expect(Exit.isSuccess(exit)).toBe(true)
if (Exit.isSuccess(exit)) expect(exit.value).toBe(dir)
}).pipe(Effect.provide(Test.layer))
}),
)

View File

@@ -0,0 +1,89 @@
import { expect } from "bun:test"
import { Effect, Layer, Context } from "effect"
import { InstanceRef } from "../../src/effect/instance-ref"
import { makeRuntime } from "../../src/effect/run-service"
import { ProjectV2 } from "@opencode-ai/core/project"
import { it } from "../lib/effect"
class Shared extends Context.Service<Shared, { readonly id: number }>()("@test/Shared") {}
const testDirectory = "/tmp/opencode-test"
it.live("makeRuntime shares dependent layers through the shared memo map", () =>
Effect.gen(function* () {
let n = 0
const shared = Layer.effect(
Shared,
Effect.sync(() => {
n += 1
return Shared.of({ id: n })
}),
)
class One extends Context.Service<One, { readonly get: () => Effect.Effect<number> }>()("@test/One") {}
const one = Layer.effect(
One,
Effect.gen(function* () {
const svc = yield* Shared
return One.of({
get: Effect.fn("One.get")(() => Effect.succeed(svc.id)),
})
}),
).pipe(Layer.provide(shared))
class Two extends Context.Service<Two, { readonly get: () => Effect.Effect<number> }>()("@test/Two") {}
const two = Layer.effect(
Two,
Effect.gen(function* () {
const svc = yield* Shared
return Two.of({
get: Effect.fn("Two.get")(() => Effect.succeed(svc.id)),
})
}),
).pipe(Layer.provide(shared))
const { runPromise: runOne } = makeRuntime(One, one)
const { runPromise: runTwo } = makeRuntime(Two, two)
expect(yield* Effect.promise(() => runOne((svc) => svc.get()))).toBe(1)
expect(yield* Effect.promise(() => runTwo((svc) => svc.get()))).toBe(1)
expect(n).toBe(1)
}),
)
it.live("makeRuntime inherits InstanceRef from the current fiber", () =>
Effect.gen(function* () {
class NeedsInstance extends Context.Service<
NeedsInstance,
{ readonly directory: () => Effect.Effect<string | undefined> }
>()("@test/NeedsInstance") {}
const runtime = makeRuntime(
NeedsInstance,
Layer.succeed(
NeedsInstance,
NeedsInstance.of({
directory: () =>
Effect.gen(function* () {
return (yield* InstanceRef)?.directory
}),
}),
),
)
const actual = yield* Effect.promise(() => runtime.runPromise((svc) => svc.directory()))
expect(actual).toBe(testDirectory)
}).pipe(
Effect.provideService(InstanceRef, {
directory: testDirectory,
worktree: testDirectory,
project: {
id: ProjectV2.ID.global,
worktree: testDirectory,
time: { created: 0, updated: 0 },
sandboxes: [],
},
}),
),
)

View File

@@ -0,0 +1,514 @@
import { describe, expect } from "bun:test"
import { Cause, Deferred, Effect, Exit, Fiber, Latch, Ref, Scope } from "effect"
import { Runner } from "@/effect/runner"
import { it } from "../lib/effect"
const waitForState = <A, E>(runner: Runner.Runner<A, E>, tag: Runner.State<A, E>["_tag"]) =>
Effect.gen(function* () {
while (runner.state._tag !== tag) yield* Effect.yieldNow
}).pipe(Effect.timeout("1 second"))
describe("Runner", () => {
// --- ensureRunning semantics ---
it.live(
"ensureRunning starts work and returns result",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
const result = yield* runner.ensureRunning(Effect.succeed("hello"))
expect(result).toBe("hello")
expect(runner.state._tag).toBe("Idle")
expect(runner.busy).toBe(false)
}),
)
it.live(
"ensureRunning propagates work failures",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string, string>(s)
const exit = yield* runner.ensureRunning(Effect.fail("boom")).pipe(Effect.exit)
expect(Exit.isFailure(exit)).toBe(true)
expect(runner.state._tag).toBe("Idle")
}),
)
it.live(
"concurrent callers share the same run",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
const calls = yield* Ref.make(0)
const work = Effect.gen(function* () {
yield* Ref.update(calls, (n) => n + 1)
yield* Effect.sleep("10 millis")
return "shared"
})
const [a, b] = yield* Effect.all([runner.ensureRunning(work), runner.ensureRunning(work)], {
concurrency: "unbounded",
})
expect(a).toBe("shared")
expect(b).toBe("shared")
expect(yield* Ref.get(calls)).toBe(1)
}),
)
it.live(
"concurrent callers all receive same error",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string, string>(s)
const work = Effect.gen(function* () {
yield* Effect.sleep("10 millis")
return yield* Effect.fail("boom")
})
const [a, b] = yield* Effect.all(
[runner.ensureRunning(work).pipe(Effect.exit), runner.ensureRunning(work).pipe(Effect.exit)],
{ concurrency: "unbounded" },
)
expect(Exit.isFailure(a)).toBe(true)
expect(Exit.isFailure(b)).toBe(true)
}),
)
it.live(
"ensureRunning can be called again after previous run completes",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
expect(yield* runner.ensureRunning(Effect.succeed("first"))).toBe("first")
expect(yield* runner.ensureRunning(Effect.succeed("second"))).toBe("second")
}),
)
it.live(
"second ensureRunning ignores new work if already running",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
const ran = yield* Ref.make<string[]>([])
const first = Effect.gen(function* () {
yield* Ref.update(ran, (a) => [...a, "first"])
yield* Effect.sleep("50 millis")
return "first-result"
})
const second = Effect.gen(function* () {
yield* Ref.update(ran, (a) => [...a, "second"])
return "second-result"
})
const [a, b] = yield* Effect.all([runner.ensureRunning(first), runner.ensureRunning(second)], {
concurrency: "unbounded",
})
expect(a).toBe("first-result")
expect(b).toBe("first-result")
expect(yield* Ref.get(ran)).toEqual(["first"])
}),
)
// --- cancel semantics ---
it.live(
"cancel interrupts running work",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
const started = yield* Deferred.make<void>()
const fiber = yield* runner
.ensureRunning(
Effect.gen(function* () {
yield* Deferred.succeed(started, void 0)
return yield* Effect.never.pipe(Effect.as("never"))
}),
)
.pipe(Effect.forkChild)
yield* Deferred.await(started)
expect(runner.busy).toBe(true)
expect(runner.state._tag).toBe("Running")
yield* runner.cancel
expect(runner.busy).toBe(false)
const exit = yield* Fiber.await(fiber)
expect(Exit.isFailure(exit)).toBe(true)
}),
)
it.live(
"cancel on idle is a no-op",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
yield* runner.cancel
expect(runner.busy).toBe(false)
}),
)
it.live(
"cancel with onInterrupt resolves callers gracefully",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s, { onInterrupt: Effect.succeed("fallback") })
const fiber = yield* runner.ensureRunning(Effect.never.pipe(Effect.as("never"))).pipe(Effect.forkChild)
yield* waitForState(runner, "Running")
yield* runner.cancel
const exit = yield* Fiber.await(fiber)
expect(Exit.isSuccess(exit)).toBe(true)
if (Exit.isSuccess(exit)) expect(exit.value).toBe("fallback")
}),
)
it.live(
"cancel with queued callers resolves all",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s, { onInterrupt: Effect.succeed("fallback") })
const a = yield* runner.ensureRunning(Effect.never.pipe(Effect.as("x"))).pipe(Effect.forkChild)
yield* waitForState(runner, "Running")
const b = yield* runner.ensureRunning(Effect.succeed("y")).pipe(Effect.forkChild)
yield* Effect.yieldNow
yield* runner.cancel
const [exitA, exitB] = yield* Effect.all([Fiber.await(a), Fiber.await(b)])
expect(Exit.isSuccess(exitA)).toBe(true)
expect(Exit.isSuccess(exitB)).toBe(true)
if (Exit.isSuccess(exitA)) expect(exitA.value).toBe("fallback")
if (Exit.isSuccess(exitB)) expect(exitB.value).toBe("fallback")
}),
)
it.live(
"work can be started after cancel",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
const fiber = yield* runner.ensureRunning(Effect.never.pipe(Effect.as("x"))).pipe(Effect.forkChild)
yield* waitForState(runner, "Running")
yield* runner.cancel
yield* Fiber.await(fiber)
const result = yield* runner.ensureRunning(Effect.succeed("after-cancel"))
expect(result).toBe("after-cancel")
}),
)
it.live(
"cancel does not deadlock when replacement work starts before interrupted run exits",
Effect.gen(function* () {
const s = yield* Scope.Scope
const hit = yield* Deferred.make<void>()
const hold = yield* Deferred.make<void>()
const done = yield* Deferred.make<void>()
yield* Effect.gen(function* () {
const runner = Runner.make<string>(s)
const first = Effect.never.pipe(
Effect.onInterrupt(() => Deferred.succeed(hit, undefined)),
Effect.ensuring(Deferred.await(hold)),
Effect.as("first"),
)
const a = yield* runner.ensureRunning(first).pipe(Effect.exit, Effect.forkChild)
yield* waitForState(runner, "Running")
const stop = yield* runner.cancel.pipe(Effect.forkChild)
yield* Deferred.await(hit).pipe(Effect.timeout("250 millis"))
const b = yield* runner.ensureRunning(Deferred.await(done).pipe(Effect.as("second"))).pipe(Effect.forkChild)
yield* Effect.yieldNow
expect(runner.busy).toBe(true)
yield* Deferred.succeed(hold, undefined)
const stopExit = yield* Fiber.await(stop).pipe(Effect.timeout("250 millis"))
expect(Exit.isSuccess(stopExit)).toBe(true)
expect(runner.busy).toBe(true)
yield* Deferred.succeed(done, undefined)
expect(yield* Fiber.join(b).pipe(Effect.timeout("250 millis"))).toBe("second")
expect(runner.busy).toBe(false)
const exit = yield* Fiber.join(a)
expect(Exit.isFailure(exit)).toBe(true)
}).pipe(
Effect.ensuring(
Effect.all([Deferred.succeed(hold, undefined), Deferred.succeed(done, undefined)], { discard: true }).pipe(
Effect.ignore,
),
),
)
}),
)
// --- shell semantics ---
it.live(
"shell runs exclusively",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
const result = yield* runner.startShell(Effect.succeed("shell-done"))
expect(result).toBe("shell-done")
expect(runner.busy).toBe(false)
}),
)
it.live(
"shell rejects when run is active",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
const started = yield* Deferred.make<void>()
const fiber = yield* runner
.ensureRunning(
Effect.gen(function* () {
yield* Deferred.succeed(started, undefined)
return yield* Effect.never.pipe(Effect.as("x"))
}),
)
.pipe(Effect.forkChild)
yield* Deferred.await(started).pipe(Effect.timeout("250 millis"))
yield* Effect.gen(function* () {
while (runner.state._tag !== "Running") yield* Effect.yieldNow
}).pipe(Effect.timeout("250 millis"))
const exit = yield* runner.startShell(Effect.succeed("nope")).pipe(Effect.exit)
expect(Exit.isFailure(exit)).toBe(true)
yield* runner.cancel
yield* Fiber.await(fiber).pipe(Effect.timeout("250 millis"))
}),
)
it.live(
"shell rejects when another shell is running",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
const gate = yield* Deferred.make<void>()
const sh = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("first"))).pipe(Effect.forkChild)
yield* waitForState(runner, "Shell")
const exit = yield* runner.startShell(Effect.succeed("second")).pipe(Effect.exit)
expect(Exit.isFailure(exit)).toBe(true)
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Runner.Busy)
yield* Deferred.succeed(gate, undefined)
yield* Fiber.await(sh)
}),
)
it.live(
"cancel interrupts shell",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
const gate = yield* Deferred.make<void>()
const sh = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("ignored"))).pipe(Effect.forkChild)
yield* waitForState(runner, "Shell")
const stop = yield* runner.cancel.pipe(Effect.forkChild)
const stopExit = yield* Fiber.await(stop).pipe(Effect.timeout("250 millis"))
expect(Exit.isSuccess(stopExit)).toBe(true)
expect(runner.busy).toBe(false)
const shellExit = yield* Fiber.await(sh)
expect(Exit.isFailure(shellExit)).toBe(true)
yield* Deferred.succeed(gate, undefined).pipe(Effect.ignore)
}),
)
it.live(
"cancel does not mask shell defects",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s, { onInterrupt: Effect.succeed("interrupted") })
const ready = yield* Latch.make()
const sh = yield* runner
.startShell(
Effect.gen(function* () {
yield* ready.open
return yield* Effect.never.pipe(Effect.as("ignored"))
}).pipe(Effect.ensuring(Effect.die("boom"))),
ready,
)
.pipe(Effect.forkChild)
yield* ready.await.pipe(Effect.timeout("250 millis"))
yield* runner.cancel
expect(Exit.isFailure(yield* Fiber.await(sh))).toBe(true)
}),
)
// --- shell→run handoff ---
it.live(
"ensureRunning queues behind shell then runs after",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
const gate = yield* Deferred.make<void>()
const sh = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("shell-result"))).pipe(Effect.forkChild)
yield* waitForState(runner, "Shell")
expect(runner.state._tag).toBe("Shell")
const run = yield* runner.ensureRunning(Effect.succeed("run-result")).pipe(Effect.forkChild)
yield* waitForState(runner, "ShellThenRun")
expect(runner.state._tag).toBe("ShellThenRun")
yield* Deferred.succeed(gate, undefined)
yield* Fiber.await(sh)
const exit = yield* Fiber.await(run)
expect(Exit.isSuccess(exit)).toBe(true)
if (Exit.isSuccess(exit)) expect(exit.value).toBe("run-result")
expect(runner.state._tag).toBe("Idle")
}),
)
it.live(
"multiple ensureRunning callers share the queued run behind shell",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
const calls = yield* Ref.make(0)
const gate = yield* Deferred.make<void>()
const sh = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("shell"))).pipe(Effect.forkChild)
yield* waitForState(runner, "Shell")
const work = Effect.gen(function* () {
yield* Ref.update(calls, (n) => n + 1)
return "run"
})
const a = yield* runner.ensureRunning(work).pipe(Effect.forkChild)
const b = yield* runner.ensureRunning(work).pipe(Effect.forkChild)
yield* waitForState(runner, "ShellThenRun")
yield* Deferred.succeed(gate, undefined)
yield* Fiber.await(sh)
const [exitA, exitB] = yield* Effect.all([Fiber.await(a), Fiber.await(b)])
expect(Exit.isSuccess(exitA)).toBe(true)
expect(Exit.isSuccess(exitB)).toBe(true)
expect(yield* Ref.get(calls)).toBe(1)
}),
)
it.live(
"cancel during shell_then_run cancels both",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
const sh = yield* runner.startShell(Effect.never.pipe(Effect.as("aborted"))).pipe(Effect.forkChild)
yield* waitForState(runner, "Shell")
const run = yield* runner.ensureRunning(Effect.succeed("y")).pipe(Effect.forkChild)
yield* waitForState(runner, "ShellThenRun")
expect(runner.state._tag).toBe("ShellThenRun")
yield* runner.cancel
expect(runner.busy).toBe(false)
yield* Fiber.await(sh)
const exit = yield* Fiber.await(run)
expect(Exit.isFailure(exit)).toBe(true)
}),
)
// --- lifecycle callbacks ---
it.live(
"onIdle fires when returning to idle from running",
Effect.gen(function* () {
const s = yield* Scope.Scope
const count = yield* Ref.make(0)
const runner = Runner.make<string>(s, {
onIdle: Ref.update(count, (n) => n + 1),
})
yield* runner.ensureRunning(Effect.succeed("ok"))
expect(yield* Ref.get(count)).toBe(1)
}),
)
it.live(
"onIdle fires on cancel",
Effect.gen(function* () {
const s = yield* Scope.Scope
const count = yield* Ref.make(0)
const runner = Runner.make<string>(s, {
onIdle: Ref.update(count, (n) => n + 1),
})
const fiber = yield* runner.ensureRunning(Effect.never.pipe(Effect.as("x"))).pipe(Effect.forkChild)
yield* waitForState(runner, "Running")
yield* runner.cancel
yield* Fiber.await(fiber)
expect(yield* Ref.get(count)).toBeGreaterThanOrEqual(1)
}),
)
it.live(
"onBusy fires when shell starts",
Effect.gen(function* () {
const s = yield* Scope.Scope
const count = yield* Ref.make(0)
const runner = Runner.make<string>(s, {
onBusy: Ref.update(count, (n) => n + 1),
})
yield* runner.startShell(Effect.succeed("done"))
expect(yield* Ref.get(count)).toBe(1)
}),
)
// --- busy flag ---
it.live(
"busy is true during run",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
const gate = yield* Deferred.make<void>()
const fiber = yield* runner.ensureRunning(Deferred.await(gate).pipe(Effect.as("ok"))).pipe(Effect.forkChild)
yield* waitForState(runner, "Running")
expect(runner.busy).toBe(true)
yield* Deferred.succeed(gate, undefined)
yield* Fiber.await(fiber)
expect(runner.busy).toBe(false)
}),
)
it.live(
"busy is true during shell",
Effect.gen(function* () {
const s = yield* Scope.Scope
const runner = Runner.make<string>(s)
const gate = yield* Deferred.make<void>()
const fiber = yield* runner.startShell(Deferred.await(gate).pipe(Effect.as("ok"))).pipe(Effect.forkChild)
yield* waitForState(runner, "Shell")
expect(runner.busy).toBe(true)
yield* Deferred.succeed(gate, undefined)
yield* Fiber.await(fiber)
expect(runner.busy).toBe(false)
}),
)
})

View File

@@ -0,0 +1,373 @@
import { describe, expect } from "bun:test"
import { ConfigProvider, Effect, Layer } from "effect"
import { RuntimeFlags } from "../../src/effect/runtime-flags"
import { it } from "../lib/effect"
const fromConfig = (input: Record<string, unknown>) =>
RuntimeFlags.defaultLayer.pipe(Layer.provide(ConfigProvider.layer(ConfigProvider.fromUnknown(input))))
const readFlags = RuntimeFlags.Service.useSync((flags) => flags)
describe("RuntimeFlags", () => {
it.effect("defaultLayer defaults autoShare to false", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({})))
expect(flags.autoShare).toBe(false)
}),
)
it.effect("defaultLayer parses plugin flags from the active ConfigProvider", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(
Effect.provide(
fromConfig({
OPENCODE_PURE: "true",
OPENCODE_DISABLE_DEFAULT_PLUGINS: "true",
OPENCODE_AUTO_SHARE: "true",
OPENCODE_DISABLE_EMBEDDED_WEB_UI: "true",
OPENCODE_DISABLE_EXTERNAL_SKILLS: "true",
OPENCODE_DISABLE_LSP_DOWNLOAD: "true",
OPENCODE_EXPERIMENTAL: "true",
OPENCODE_ENABLE_EXA: "true",
OPENCODE_ENABLE_PARALLEL: "true",
OPENCODE_ENABLE_EXPERIMENTAL_MODELS: "true",
OPENCODE_ENABLE_QUESTION_TOOL: "true",
OPENCODE_CLIENT: "desktop",
}),
),
)
expect(flags.pure).toBe(true)
expect(flags.autoShare).toBe(true)
expect(flags.disableDefaultPlugins).toBe(true)
expect(flags.disableEmbeddedWebUi).toBe(true)
expect(flags.disableExternalSkills).toBe(true)
expect(flags.disableLspDownload).toBe(true)
expect(flags.disableClaudeCodePrompt).toBe(false)
expect(flags.enableExa).toBe(true)
expect(flags.enableParallel).toBe(true)
expect(flags.enableExperimentalModels).toBe(true)
expect(flags.enableQuestionTool).toBe(true)
expect(flags.experimentalReferences).toBe(true)
expect(flags.experimentalBackgroundSubagents).toBe(true)
expect(flags.experimentalLspTy).toBe(false)
expect(flags.experimentalLspTool).toBe(true)
expect(flags.experimentalOxfmt).toBe(true)
expect(flags.experimentalPlanMode).toBe(true)
expect(flags.experimentalEventSystem).toBe(true)
expect(flags.experimentalWorkspaces).toBe(true)
expect(flags.experimentalIconDiscovery).toBe(true)
expect(flags.experimentalNativeLlm).toBe(false)
expect(flags.experimentalWebSockets).toBe(false)
expect(flags.client).toBe("desktop")
}),
)
it.effect("defaultLayer parses OPENCODE_EXPERIMENTAL_LSP_TY", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(
Effect.provide(
fromConfig({
OPENCODE_EXPERIMENTAL_LSP_TY: "true",
}),
),
)
expect(flags.experimentalLspTy).toBe(true)
}),
)
it.effect("enables native LLM via dedicated flag only", () =>
Effect.gen(function* () {
const explicit = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_EXPERIMENTAL_NATIVE_LLM: "true" })))
const umbrella = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_EXPERIMENTAL: "true" })))
expect(explicit.experimentalNativeLlm).toBe(true)
expect(umbrella.experimentalNativeLlm).toBe(false)
}),
)
it.effect("enables WebSockets via dedicated flag only", () =>
Effect.gen(function* () {
const explicit = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_EXPERIMENTAL_WEBSOCKETS: "true" })))
const umbrella = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_EXPERIMENTAL: "true" })))
expect(explicit.experimentalWebSockets).toBe(true)
expect(umbrella.experimentalWebSockets).toBe(false)
}),
)
it.effect("layer accepts partial test overrides and fills defaults from Config definitions", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(
Effect.provide(RuntimeFlags.layer({ disableDefaultPlugins: true, bashDefaultTimeoutMs: 1_000 })),
)
expect(flags.pure).toBe(false)
expect(flags.autoShare).toBe(false)
expect(flags.disableDefaultPlugins).toBe(true)
expect(flags.disableEmbeddedWebUi).toBe(false)
expect(flags.disableExternalSkills).toBe(false)
expect(flags.disableLspDownload).toBe(false)
expect(flags.disableClaudeCodePrompt).toBe(false)
expect(flags.disableClaudeCodeSkills).toBe(false)
expect(flags.enableExa).toBe(false)
expect(flags.experimentalIconDiscovery).toBe(false)
expect(flags.experimentalOxfmt).toBe(false)
expect(flags.outputTokenMax).toBeUndefined()
expect(flags.bashDefaultTimeoutMs).toBe(1_000)
expect(flags.enableExperimentalModels).toBe(false)
expect(flags.client).toBe("cli")
}),
)
it.effect("experimentalIconDiscovery defaults to false", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({})))
expect(flags.experimentalIconDiscovery).toBe(false)
}),
)
it.effect("disableExternalSkills defaults to false", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({})))
expect(flags.disableExternalSkills).toBe(false)
}),
)
it.effect("disableExternalSkills reads OPENCODE_DISABLE_EXTERNAL_SKILLS", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_DISABLE_EXTERNAL_SKILLS: "true" })))
expect(flags.disableExternalSkills).toBe(true)
}),
)
it.effect("disableLspDownload defaults to false", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({})))
expect(flags.disableLspDownload).toBe(false)
}),
)
it.effect("disableLspDownload reads OPENCODE_DISABLE_LSP_DOWNLOAD", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_DISABLE_LSP_DOWNLOAD: "true" })))
expect(flags.disableLspDownload).toBe(true)
}),
)
it.effect("disableClaudeCodePrompt defaults to false", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({})))
expect(flags.disableClaudeCodePrompt).toBe(false)
}),
)
it.effect("disableClaudeCodePrompt reads OPENCODE_DISABLE_CLAUDE_CODE_PROMPT", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_DISABLE_CLAUDE_CODE_PROMPT: "true" })))
expect(flags.disableClaudeCodePrompt).toBe(true)
}),
)
it.effect("disableClaudeCodePrompt inherits OPENCODE_DISABLE_CLAUDE_CODE", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_DISABLE_CLAUDE_CODE: "true" })))
expect(flags.disableClaudeCodePrompt).toBe(true)
}),
)
it.effect("experimentalIconDiscovery reads OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_EXPERIMENTAL_ICON_DISCOVERY: "true" })))
expect(flags.experimentalIconDiscovery).toBe(true)
}),
)
it.effect("experimentalIconDiscovery inherits OPENCODE_EXPERIMENTAL", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_EXPERIMENTAL: "true" })))
expect(flags.experimentalIconDiscovery).toBe(true)
}),
)
it.effect("specific experimental flags override OPENCODE_EXPERIMENTAL", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(
Effect.provide(
fromConfig({
OPENCODE_EXPERIMENTAL: "true",
OPENCODE_EXPERIMENTAL_ICON_DISCOVERY: "false",
}),
),
)
expect(flags.experimentalIconDiscovery).toBe(false)
}),
)
it.effect("experimentalOxfmt defaults to false", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({})))
expect(flags.experimentalOxfmt).toBe(false)
}),
)
it.effect("experimentalOxfmt is enabled by OPENCODE_EXPERIMENTAL_OXFMT", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(
Effect.provide(
fromConfig({
OPENCODE_EXPERIMENTAL_OXFMT: "true",
}),
),
)
expect(flags.experimentalOxfmt).toBe(true)
}),
)
it.effect("experimentalOxfmt inherits OPENCODE_EXPERIMENTAL", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(
Effect.provide(
fromConfig({
OPENCODE_EXPERIMENTAL: "true",
}),
),
)
expect(flags.experimentalOxfmt).toBe(true)
}),
)
for (const input of [
{ name: "absent", config: {}, expected: undefined },
{
name: "valid positive integer",
config: { OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "1234" },
expected: 1234,
},
{
name: "invalid string",
config: { OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "nope" },
expected: undefined,
},
{ name: "zero", config: { OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "0" }, expected: undefined },
{ name: "negative", config: { OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "-1" }, expected: undefined },
{
name: "non-integer",
config: { OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "1.5" },
expected: undefined,
},
]) {
it.effect(`parses bashDefaultTimeoutMs from config: ${input.name}`, () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig(input.config)))
expect(flags.bashDefaultTimeoutMs).toBe(input.expected)
}),
)
}
for (const input of [
{ name: "absent", config: {}, expected: undefined },
{
name: "valid positive integer",
config: { OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX: "1234" },
expected: 1234,
},
{
name: "invalid string",
config: { OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX: "nope" },
expected: undefined,
},
{ name: "zero", config: { OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX: "0" }, expected: undefined },
{ name: "negative", config: { OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX: "-1" }, expected: undefined },
{
name: "non-integer",
config: { OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX: "1.5" },
expected: undefined,
},
]) {
it.effect(`parses outputTokenMax from config: ${input.name}`, () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig(input.config)))
expect(flags.outputTokenMax).toBe(input.expected)
}),
)
}
it.effect("layer ignores the active ConfigProvider for omitted test overrides", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(
Effect.provide(RuntimeFlags.layer()),
Effect.provide(
ConfigProvider.layer(
ConfigProvider.fromUnknown({
OPENCODE_PURE: "true",
OPENCODE_DISABLE_DEFAULT_PLUGINS: "true",
OPENCODE_DISABLE_EXTERNAL_SKILLS: "true",
OPENCODE_DISABLE_LSP_DOWNLOAD: "true",
OPENCODE_EXPERIMENTAL: "true",
OPENCODE_ENABLE_EXA: "true",
OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS: "1234",
OPENCODE_CLIENT: "desktop",
}),
),
),
)
expect(flags.pure).toBe(false)
expect(flags.disableDefaultPlugins).toBe(false)
expect(flags.disableEmbeddedWebUi).toBe(false)
expect(flags.disableExternalSkills).toBe(false)
expect(flags.disableLspDownload).toBe(false)
expect(flags.disableClaudeCodePrompt).toBe(false)
expect(flags.disableClaudeCodeSkills).toBe(false)
expect(flags.enableExa).toBe(false)
expect(flags.experimentalIconDiscovery).toBe(false)
expect(flags.experimentalOxfmt).toBe(false)
expect(flags.outputTokenMax).toBeUndefined()
expect(flags.bashDefaultTimeoutMs).toBeUndefined()
expect(flags.client).toBe("cli")
}),
)
it.effect("disableClaudeCodeSkills defaults to false", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({})))
expect(flags.disableClaudeCodeSkills).toBe(false)
}),
)
it.effect("disableClaudeCodeSkills reads OPENCODE_DISABLE_CLAUDE_CODE_SKILLS", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_DISABLE_CLAUDE_CODE_SKILLS: "true" })))
expect(flags.disableClaudeCodeSkills).toBe(true)
}),
)
it.effect("disableClaudeCodeSkills inherits OPENCODE_DISABLE_CLAUDE_CODE", () =>
Effect.gen(function* () {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({ OPENCODE_DISABLE_CLAUDE_CODE: "true" })))
expect(flags.disableClaudeCodeSkills).toBe(true)
}),
)
})