refactor(flags): route session workspaces through runtime flags (#27335)
This commit is contained in:
parent
8345152319
commit
eed0eddc63
6 changed files with 39 additions and 18 deletions
|
|
@ -23,6 +23,7 @@ export class Service extends ConfigService.Service<Service>()("@opencode/Runtime
|
||||||
experimentalLspTool: enabledByExperimental("OPENCODE_EXPERIMENTAL_LSP_TOOL"),
|
experimentalLspTool: enabledByExperimental("OPENCODE_EXPERIMENTAL_LSP_TOOL"),
|
||||||
experimentalPlanMode: enabledByExperimental("OPENCODE_EXPERIMENTAL_PLAN_MODE"),
|
experimentalPlanMode: enabledByExperimental("OPENCODE_EXPERIMENTAL_PLAN_MODE"),
|
||||||
experimentalEventSystem: enabledByExperimental("OPENCODE_EXPERIMENTAL_EVENT_SYSTEM"),
|
experimentalEventSystem: enabledByExperimental("OPENCODE_EXPERIMENTAL_EVENT_SYSTEM"),
|
||||||
|
experimentalWorkspaces: enabledByExperimental("OPENCODE_EXPERIMENTAL_WORKSPACES"),
|
||||||
client: Config.string("OPENCODE_CLIENT").pipe(Config.withDefault("cli")),
|
client: Config.string("OPENCODE_CLIENT").pipe(Config.withDefault("cli")),
|
||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { BusEvent } from "@/bus/bus-event"
|
||||||
import { Bus } from "@/bus"
|
import { Bus } from "@/bus"
|
||||||
import { Decimal } from "decimal.js"
|
import { Decimal } from "decimal.js"
|
||||||
import { type ProviderMetadata, type LanguageModelUsage } from "ai"
|
import { type ProviderMetadata, type LanguageModelUsage } from "ai"
|
||||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
|
||||||
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||||
|
|
||||||
import { Database } from "@/storage/db"
|
import { Database } from "@/storage/db"
|
||||||
|
|
@ -38,6 +37,7 @@ import { Permission } from "@/permission"
|
||||||
import { Global } from "@opencode-ai/core/global"
|
import { Global } from "@opencode-ai/core/global"
|
||||||
import { Effect, Layer, Option, Context, Schema, Types } from "effect"
|
import { Effect, Layer, Option, Context, Schema, Types } from "effect"
|
||||||
import { NonNegativeInt, optionalOmitUndefined } from "@opencode-ai/core/schema"
|
import { NonNegativeInt, optionalOmitUndefined } from "@opencode-ai/core/schema"
|
||||||
|
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||||
|
|
||||||
const log = Log.create({ service: "session" })
|
const log = Log.create({ service: "session" })
|
||||||
|
|
||||||
|
|
@ -507,12 +507,13 @@ export type Patch = Types.DeepMutable<SyncEvent.Event<typeof Event.Updated>["dat
|
||||||
const db = <T>(fn: (d: Parameters<typeof Database.use>[0] extends (trx: infer D) => any ? D : never) => T) =>
|
const db = <T>(fn: (d: Parameters<typeof Database.use>[0] extends (trx: infer D) => any ? D : never) => T) =>
|
||||||
Effect.sync(() => Database.use(fn))
|
Effect.sync(() => Database.use(fn))
|
||||||
|
|
||||||
export const layer: Layer.Layer<Service, never, Bus.Service | Storage.Service | SyncEvent.Service> = Layer.effect(
|
export const layer: Layer.Layer<Service, never, Bus.Service | Storage.Service | SyncEvent.Service | RuntimeFlags.Service> = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const bus = yield* Bus.Service
|
const bus = yield* Bus.Service
|
||||||
const storage = yield* Storage.Service
|
const storage = yield* Storage.Service
|
||||||
const sync = yield* SyncEvent.Service
|
const sync = yield* SyncEvent.Service
|
||||||
|
const flags = yield* RuntimeFlags.Service
|
||||||
|
|
||||||
const createNext = Effect.fn("Session.createNext")(function* (input: {
|
const createNext = Effect.fn("Session.createNext")(function* (input: {
|
||||||
id?: SessionID
|
id?: SessionID
|
||||||
|
|
@ -550,7 +551,7 @@ export const layer: Layer.Layer<Service, never, Bus.Service | Storage.Service |
|
||||||
|
|
||||||
yield* sync.run(Event.Created, { sessionID: result.id, info: result })
|
yield* sync.run(Event.Created, { sessionID: result.id, info: result })
|
||||||
|
|
||||||
if (!Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) {
|
if (!flags.experimentalWorkspaces) {
|
||||||
// This only exist for backwards compatibility. We should not be
|
// This only exist for backwards compatibility. We should not be
|
||||||
// manually publishing this event; it is a sync event now
|
// manually publishing this event; it is a sync event now
|
||||||
yield* bus.publish(Event.Updated, {
|
yield* bus.publish(Event.Updated, {
|
||||||
|
|
@ -570,7 +571,7 @@ export const layer: Layer.Layer<Service, never, Bus.Service | Storage.Service |
|
||||||
|
|
||||||
const list = Effect.fn("Session.list")(function* (input?: ListInput) {
|
const list = Effect.fn("Session.list")(function* (input?: ListInput) {
|
||||||
const ctx = yield* InstanceState.context
|
const ctx = yield* InstanceState.context
|
||||||
return Array.from(listByProject({ projectID: ctx.project.id, ...input }))
|
return Array.from(listByProject({ projectID: ctx.project.id, experimentalWorkspaces: flags.experimentalWorkspaces, ...input }))
|
||||||
})
|
})
|
||||||
|
|
||||||
const children = Effect.fn("Session.children")(function* (parentID: SessionID) {
|
const children = Effect.fn("Session.children")(function* (parentID: SessionID) {
|
||||||
|
|
@ -860,11 +861,13 @@ export const defaultLayer = layer.pipe(
|
||||||
Layer.provide(Bus.layer),
|
Layer.provide(Bus.layer),
|
||||||
Layer.provide(Storage.defaultLayer),
|
Layer.provide(Storage.defaultLayer),
|
||||||
Layer.provide(SyncEvent.defaultLayer),
|
Layer.provide(SyncEvent.defaultLayer),
|
||||||
|
Layer.provide(RuntimeFlags.defaultLayer),
|
||||||
)
|
)
|
||||||
|
|
||||||
function* listByProject(
|
function* listByProject(
|
||||||
input: ListInput & {
|
input: ListInput & {
|
||||||
projectID: ProjectID
|
projectID: ProjectID
|
||||||
|
experimentalWorkspaces: boolean
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const conditions = [eq(SessionTable.project_id, input.projectID)]
|
const conditions = [eq(SessionTable.project_id, input.projectID)]
|
||||||
|
|
@ -882,7 +885,7 @@ function* listByProject(
|
||||||
: or(...conds)!,
|
: or(...conds)!,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (input.scope !== "project" && !Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) {
|
} else if (input.scope !== "project" && !input.experimentalWorkspaces) {
|
||||||
if (input.directory) {
|
if (input.directory) {
|
||||||
conditions.push(eq(SessionTable.directory, input.directory))
|
conditions.push(eq(SessionTable.directory, input.directory))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ describe("RuntimeFlags", () => {
|
||||||
expect(flags.experimentalLspTool).toBe(true)
|
expect(flags.experimentalLspTool).toBe(true)
|
||||||
expect(flags.experimentalPlanMode).toBe(true)
|
expect(flags.experimentalPlanMode).toBe(true)
|
||||||
expect(flags.experimentalEventSystem).toBe(true)
|
expect(flags.experimentalEventSystem).toBe(true)
|
||||||
|
expect(flags.experimentalWorkspaces).toBe(true)
|
||||||
expect(flags.client).toBe("desktop")
|
expect(flags.client).toBe("desktop")
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ process.env["XDG_CONFIG_HOME"] = path.join(dir, "config")
|
||||||
process.env["XDG_STATE_HOME"] = path.join(dir, "state")
|
process.env["XDG_STATE_HOME"] = path.join(dir, "state")
|
||||||
process.env["OPENCODE_MODELS_PATH"] = path.join(import.meta.dir, "tool", "fixtures", "models-api.json")
|
process.env["OPENCODE_MODELS_PATH"] = path.join(import.meta.dir, "tool", "fixtures", "models-api.json")
|
||||||
process.env["OPENCODE_EXPERIMENTAL_EVENT_SYSTEM"] = "true"
|
process.env["OPENCODE_EXPERIMENTAL_EVENT_SYSTEM"] = "true"
|
||||||
|
process.env["OPENCODE_EXPERIMENTAL_WORKSPACES"] = "true"
|
||||||
|
|
||||||
// Set test home directory to isolate tests from user's actual home directory
|
// Set test home directory to isolate tests from user's actual home directory
|
||||||
// This prevents tests from picking up real user configs/skills from ~/.claude/skills
|
// This prevents tests from picking up real user configs/skills from ~/.claude/skills
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,28 @@
|
||||||
import { afterEach, describe, expect } from "bun:test"
|
import { afterEach, describe, expect } from "bun:test"
|
||||||
import { Effect } from "effect"
|
import { Effect, Layer } from "effect"
|
||||||
import { Session as SessionNs } from "@/session/session"
|
import { Session as SessionNs } from "@/session/session"
|
||||||
import * as Log from "@opencode-ai/core/util/log"
|
import * as Log from "@opencode-ai/core/util/log"
|
||||||
import { disposeAllInstances, provideInstance, TestInstance } from "../fixture/fixture"
|
import { disposeAllInstances, provideInstance, TestInstance } from "../fixture/fixture"
|
||||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
|
||||||
import { mkdir } from "fs/promises"
|
import { mkdir } from "fs/promises"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { Database } from "@/storage/db"
|
import { Database } from "@/storage/db"
|
||||||
import { SessionTable } from "@/session/session.sql"
|
import { SessionTable } from "@/session/session.sql"
|
||||||
import { eq } from "drizzle-orm"
|
import { eq } from "drizzle-orm"
|
||||||
import { testEffect } from "../lib/effect"
|
import { testEffect } from "../lib/effect"
|
||||||
|
import { Bus } from "@/bus"
|
||||||
|
import { Storage } from "@/storage/storage"
|
||||||
|
import { SyncEvent } from "@/sync"
|
||||||
|
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||||
|
|
||||||
void Log.init({ print: false })
|
void Log.init({ print: false })
|
||||||
const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
|
const it = testEffect(
|
||||||
const it = testEffect(SessionNs.defaultLayer)
|
SessionNs.layer.pipe(
|
||||||
|
Layer.provide(Bus.layer),
|
||||||
|
Layer.provide(Storage.defaultLayer),
|
||||||
|
Layer.provide(SyncEvent.defaultLayer),
|
||||||
|
Layer.provide(RuntimeFlags.layer({ experimentalWorkspaces: false })),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
const withSession = (input?: Parameters<SessionNs.Interface["create"]>[0]) =>
|
const withSession = (input?: Parameters<SessionNs.Interface["create"]>[0]) =>
|
||||||
Effect.acquireRelease(
|
Effect.acquireRelease(
|
||||||
|
|
@ -22,7 +31,6 @@ const withSession = (input?: Parameters<SessionNs.Interface["create"]>[0]) =>
|
||||||
)
|
)
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = originalWorkspaces
|
|
||||||
await disposeAllInstances()
|
await disposeAllInstances()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -31,7 +39,6 @@ describe("session.list", () => {
|
||||||
"does not filter by directory when directory is omitted",
|
"does not filter by directory when directory is omitted",
|
||||||
() =>
|
() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = false
|
|
||||||
const test = yield* TestInstance
|
const test = yield* TestInstance
|
||||||
yield* Effect.promise(() => mkdir(path.join(test.directory, "packages", "opencode"), { recursive: true }))
|
yield* Effect.promise(() => mkdir(path.join(test.directory, "packages", "opencode"), { recursive: true }))
|
||||||
yield* Effect.promise(() => mkdir(path.join(test.directory, "packages", "app"), { recursive: true }))
|
yield* Effect.promise(() => mkdir(path.join(test.directory, "packages", "app"), { recursive: true }))
|
||||||
|
|
@ -60,7 +67,6 @@ describe("session.list", () => {
|
||||||
"filters by directory when directory is provided",
|
"filters by directory when directory is provided",
|
||||||
() =>
|
() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = false
|
|
||||||
const test = yield* TestInstance
|
const test = yield* TestInstance
|
||||||
yield* Effect.promise(() => mkdir(path.join(test.directory, "packages", "opencode"), { recursive: true }))
|
yield* Effect.promise(() => mkdir(path.join(test.directory, "packages", "opencode"), { recursive: true }))
|
||||||
yield* Effect.promise(() => mkdir(path.join(test.directory, "packages", "app"), { recursive: true }))
|
yield* Effect.promise(() => mkdir(path.join(test.directory, "packages", "app"), { recursive: true }))
|
||||||
|
|
@ -91,7 +97,6 @@ describe("session.list", () => {
|
||||||
"filters by path and ignores directory when path is provided",
|
"filters by path and ignores directory when path is provided",
|
||||||
() =>
|
() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = false
|
|
||||||
const test = yield* TestInstance
|
const test = yield* TestInstance
|
||||||
yield* Effect.promise(() =>
|
yield* Effect.promise(() =>
|
||||||
mkdir(path.join(test.directory, "packages", "opencode", "src", "deep"), { recursive: true }),
|
mkdir(path.join(test.directory, "packages", "opencode", "src", "deep"), { recursive: true }),
|
||||||
|
|
@ -129,7 +134,6 @@ describe("session.list", () => {
|
||||||
"falls back to directory when filtering legacy sessions without path",
|
"falls back to directory when filtering legacy sessions without path",
|
||||||
() =>
|
() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = false
|
|
||||||
const test = yield* TestInstance
|
const test = yield* TestInstance
|
||||||
yield* Effect.promise(() =>
|
yield* Effect.promise(() =>
|
||||||
mkdir(path.join(test.directory, "packages", "opencode", "src"), { recursive: true }),
|
mkdir(path.join(test.directory, "packages", "opencode", "src"), { recursive: true }),
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,29 @@ import { Deferred, Effect, Exit, Layer } from "effect"
|
||||||
import { Session as SessionNs } from "@/session/session"
|
import { Session as SessionNs } from "@/session/session"
|
||||||
import { GlobalBus, type GlobalEvent } from "../../src/bus/global"
|
import { GlobalBus, type GlobalEvent } from "../../src/bus/global"
|
||||||
import * as Log from "@opencode-ai/core/util/log"
|
import * as Log from "@opencode-ai/core/util/log"
|
||||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
|
||||||
import { MessageV2 } from "../../src/session/message-v2"
|
import { MessageV2 } from "../../src/session/message-v2"
|
||||||
import { MessageID, PartID, type SessionID } from "../../src/session/schema"
|
import { MessageID, PartID, type SessionID } from "../../src/session/schema"
|
||||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||||
import { provideInstance, tmpdirScoped } from "../fixture/fixture"
|
import { provideInstance, tmpdirScoped } from "../fixture/fixture"
|
||||||
import { testEffect } from "../lib/effect"
|
import { testEffect } from "../lib/effect"
|
||||||
|
import { Bus } from "@/bus"
|
||||||
|
import { Storage } from "@/storage/storage"
|
||||||
|
import { SyncEvent } from "@/sync"
|
||||||
|
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||||
|
|
||||||
void Log.init({ print: false })
|
void Log.init({ print: false })
|
||||||
|
|
||||||
const it = testEffect(Layer.mergeAll(SessionNs.defaultLayer, CrossSpawnSpawner.defaultLayer))
|
const it = testEffect(
|
||||||
|
Layer.mergeAll(
|
||||||
|
SessionNs.layer.pipe(
|
||||||
|
Layer.provide(Bus.layer),
|
||||||
|
Layer.provide(Storage.defaultLayer),
|
||||||
|
Layer.provide(SyncEvent.defaultLayer),
|
||||||
|
Layer.provide(RuntimeFlags.layer({ experimentalWorkspaces: false })),
|
||||||
|
),
|
||||||
|
CrossSpawnSpawner.defaultLayer,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
const awaitDeferred = <T>(deferred: Deferred.Deferred<T>, message: string) =>
|
const awaitDeferred = <T>(deferred: Deferred.Deferred<T>, message: string) =>
|
||||||
Effect.race(
|
Effect.race(
|
||||||
|
|
@ -56,8 +69,6 @@ describe("session.created event", () => {
|
||||||
|
|
||||||
it.instance("session.created event should be emitted before session.updated", () =>
|
it.instance("session.created event should be emitted before session.updated", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
if (Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) return
|
|
||||||
|
|
||||||
const session = yield* SessionNs.Service
|
const session = yield* SessionNs.Service
|
||||||
const events: string[] = []
|
const events: string[] = []
|
||||||
const received = yield* Deferred.make<string[]>()
|
const received = yield* Deferred.make<string[]>()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue