mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-20 19:06:22 +00:00
effect(config): extract ConfigPaths.Service from config/paths
This commit is contained in:
@@ -7,7 +7,7 @@ import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import * as ConfigPaths from "@/config/paths"
|
||||
import { ConfigPaths } from "@/config/paths"
|
||||
|
||||
const log = Log.create({ service: "tui.migrate" })
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { createBindingLookup } from "@opentui/keymap/extras"
|
||||
import { mergeDeep, unique } from "remeda"
|
||||
import { Cause, Context, Effect, Fiber, Layer, Schema } from "effect"
|
||||
import { ConfigParse } from "@/config/parse"
|
||||
import * as ConfigPaths from "@/config/paths"
|
||||
import { ConfigPaths } from "@/config/paths"
|
||||
import { migrateTuiConfig } from "./tui-migrate"
|
||||
import { KeymapLeaderTimeoutDefault, resolveAttentionSoundPaths, TuiInfo } from "./tui-schema"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
@@ -99,6 +99,7 @@ function dropUnknownKeybinds(input: Record<string, unknown>, configFilepath: str
|
||||
const loadState = Effect.fn("TuiConfig.loadState")(function* (ctx: { directory: string }) {
|
||||
const afs = yield* AppFileSystem.Service
|
||||
let appliedOrder = 0
|
||||
const paths = yield* ConfigPaths.Service
|
||||
|
||||
const resolvePlugins = (config: Info, configFilepath: string): Effect.Effect<Info> =>
|
||||
Effect.gen(function* () {
|
||||
@@ -191,10 +192,10 @@ const loadState = Effect.fn("TuiConfig.loadState")(function* (ctx: { directory:
|
||||
|
||||
// Every config dir we may read from: global config dir, any `.opencode`
|
||||
// folders between cwd and home, and OPENCODE_CONFIG_DIR.
|
||||
const directories = yield* ConfigPaths.directories(ctx.directory)
|
||||
const directories = yield* paths.directories(ctx.directory)
|
||||
yield* Effect.promise(() => migrateTuiConfig({ directories, cwd: ctx.directory }))
|
||||
|
||||
const projectFiles = Flag.OPENCODE_DISABLE_PROJECT_CONFIG ? [] : yield* ConfigPaths.files("tui", ctx.directory)
|
||||
const projectFiles = Flag.OPENCODE_DISABLE_PROJECT_CONFIG ? [] : yield* paths.projectFiles("tui", ctx.directory)
|
||||
|
||||
const acc: Acc = {
|
||||
result: {},
|
||||
@@ -295,7 +296,11 @@ export const layer = Layer.effect(
|
||||
}).pipe(Effect.withSpan("TuiConfig.layer")),
|
||||
)
|
||||
|
||||
export const defaultLayer = layer.pipe(Layer.provide(Npm.defaultLayer), Layer.provide(AppFileSystem.defaultLayer))
|
||||
export const defaultLayer = layer.pipe(
|
||||
Layer.provide(Npm.defaultLayer),
|
||||
Layer.provide(AppFileSystem.defaultLayer),
|
||||
Layer.provide(ConfigPaths.defaultLayer),
|
||||
)
|
||||
|
||||
const { runPromise } = makeRuntime(Service, defaultLayer)
|
||||
|
||||
|
||||
@@ -370,6 +370,7 @@ export const layer = Layer.effect(
|
||||
const accountSvc = yield* Account.Service
|
||||
const env = yield* Env.Service
|
||||
const npmSvc = yield* Npm.Service
|
||||
const paths = yield* ConfigPaths.Service
|
||||
|
||||
const readConfigFile = (filepath: string) => fs.readFileStringSafe(filepath).pipe(Effect.orDie)
|
||||
|
||||
@@ -562,7 +563,7 @@ export const layer = Layer.effect(
|
||||
}
|
||||
|
||||
if (!Flag.OPENCODE_DISABLE_PROJECT_CONFIG) {
|
||||
for (const file of yield* ConfigPaths.files("opencode", ctx.directory, ctx.worktree).pipe(Effect.orDie)) {
|
||||
for (const file of yield* paths.projectFiles("opencode", ctx.directory, ctx.worktree).pipe(Effect.orDie)) {
|
||||
yield* merge(file, yield* loadFile(file), "local")
|
||||
}
|
||||
}
|
||||
@@ -571,7 +572,7 @@ export const layer = Layer.effect(
|
||||
result.mode = result.mode || {}
|
||||
result.plugin = result.plugin || []
|
||||
|
||||
const directories = yield* ConfigPaths.directories(ctx.directory, ctx.worktree)
|
||||
const directories = yield* paths.directories(ctx.directory, ctx.worktree)
|
||||
|
||||
if (Flag.OPENCODE_CONFIG_DIR) {
|
||||
log.debug("loading config from OPENCODE_CONFIG_DIR", { path: Flag.OPENCODE_CONFIG_DIR })
|
||||
@@ -829,6 +830,7 @@ export const defaultLayer = layer.pipe(
|
||||
Layer.provide(Auth.defaultLayer),
|
||||
Layer.provide(Account.defaultLayer),
|
||||
Layer.provide(Npm.defaultLayer),
|
||||
Layer.provide(ConfigPaths.defaultLayer),
|
||||
)
|
||||
|
||||
export * as Config from "./config"
|
||||
|
||||
@@ -4,42 +4,73 @@ import path from "path"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { unique } from "remeda"
|
||||
import * as Effect from "effect/Effect"
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||
|
||||
export const files = Effect.fn("ConfigPaths.projectFiles")(function* (
|
||||
name: string,
|
||||
directory: string,
|
||||
worktree?: string,
|
||||
) {
|
||||
const afs = yield* AppFileSystem.Service
|
||||
return (yield* afs.up({
|
||||
targets: [`${name}.jsonc`, `${name}.json`],
|
||||
start: directory,
|
||||
stop: worktree,
|
||||
})).toReversed()
|
||||
})
|
||||
|
||||
export const directories = Effect.fn("ConfigPaths.directories")(function* (directory: string, worktree?: string) {
|
||||
const afs = yield* AppFileSystem.Service
|
||||
return unique([
|
||||
Global.Path.config,
|
||||
...(!Flag.OPENCODE_DISABLE_PROJECT_CONFIG
|
||||
? yield* afs.up({
|
||||
targets: [".opencode"],
|
||||
start: directory,
|
||||
stop: worktree,
|
||||
})
|
||||
: []),
|
||||
...(yield* afs.up({
|
||||
targets: [".opencode"],
|
||||
start: Global.Path.home,
|
||||
stop: Global.Path.home,
|
||||
})),
|
||||
...(Flag.OPENCODE_CONFIG_DIR ? [Flag.OPENCODE_CONFIG_DIR] : []),
|
||||
])
|
||||
})
|
||||
import { ConfigParse } from "./parse"
|
||||
|
||||
export function fileInDirectory(dir: string, name: string) {
|
||||
return [path.join(dir, `${name}.json`), path.join(dir, `${name}.jsonc`)]
|
||||
}
|
||||
|
||||
export interface Interface {
|
||||
readonly projectFiles: (
|
||||
name: string,
|
||||
directory: string,
|
||||
worktree?: string,
|
||||
) => Effect.Effect<string[], AppFileSystem.Error>
|
||||
readonly directories: (directory: string, worktree?: string) => Effect.Effect<string[], AppFileSystem.Error>
|
||||
readonly readFile: (filepath: string) => Effect.Effect<string | undefined, AppFileSystem.Error>
|
||||
readonly parseText: (text: string, filepath: string) => Effect.Effect<unknown>
|
||||
}
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/ConfigPaths") {}
|
||||
|
||||
export const layer = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
const afs = yield* AppFileSystem.Service
|
||||
|
||||
const projectFiles = Effect.fn("ConfigPaths.projectFiles")(function* (
|
||||
name: string,
|
||||
directory: string,
|
||||
worktree?: string,
|
||||
) {
|
||||
return (yield* afs.up({
|
||||
targets: [`${name}.jsonc`, `${name}.json`],
|
||||
start: directory,
|
||||
stop: worktree,
|
||||
})).toReversed()
|
||||
})
|
||||
|
||||
const directories = Effect.fn("ConfigPaths.directories")(function* (directory: string, worktree?: string) {
|
||||
return unique([
|
||||
Global.Path.config,
|
||||
...(!Flag.OPENCODE_DISABLE_PROJECT_CONFIG
|
||||
? yield* afs.up({
|
||||
targets: [".opencode"],
|
||||
start: directory,
|
||||
stop: worktree,
|
||||
})
|
||||
: []),
|
||||
...(yield* afs.up({
|
||||
targets: [".opencode"],
|
||||
start: Global.Path.home,
|
||||
stop: Global.Path.home,
|
||||
})),
|
||||
...(Flag.OPENCODE_CONFIG_DIR ? [Flag.OPENCODE_CONFIG_DIR] : []),
|
||||
])
|
||||
})
|
||||
|
||||
const readFile = Effect.fn("ConfigPaths.readFile")(function* (filepath: string) {
|
||||
return yield* afs.readFileStringSafe(filepath)
|
||||
})
|
||||
|
||||
const parseText = Effect.fn("ConfigPaths.parseText")(function* (text: string, filepath: string) {
|
||||
return ConfigParse.jsonc(text, filepath)
|
||||
})
|
||||
|
||||
return Service.of({ projectFiles, directories, readFile, parseText })
|
||||
}),
|
||||
)
|
||||
|
||||
export const defaultLayer = layer.pipe(Layer.provide(AppFileSystem.defaultLayer))
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
printParseErrorCode,
|
||||
} from "jsonc-parser"
|
||||
|
||||
import * as ConfigPaths from "@/config/paths"
|
||||
import { ConfigPaths } from "@/config/paths"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
import { Flock } from "@opencode-ai/core/util/flock"
|
||||
|
||||
@@ -6,6 +6,7 @@ import { pathToFileURL } from "url"
|
||||
import { Agent } from "../../src/agent/agent"
|
||||
import { Bus } from "../../src/bus"
|
||||
import { Config } from "../../src/config/config"
|
||||
import { ConfigPaths } from "../../src/config/paths"
|
||||
import { Env } from "../../src/env"
|
||||
import { RuntimeFlags } from "../../src/effect/runtime-flags"
|
||||
import { Plugin } from "../../src/plugin"
|
||||
@@ -29,6 +30,7 @@ const configLayer = Config.layer.pipe(
|
||||
Layer.provide(AuthTest.empty),
|
||||
Layer.provide(AccountTest.empty),
|
||||
Layer.provide(NpmTest.noop),
|
||||
Layer.provide(ConfigPaths.defaultLayer),
|
||||
)
|
||||
const pluginLayer = Plugin.layer.pipe(
|
||||
Layer.provide(Bus.layer),
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Effect, Layer, Option } from "effect"
|
||||
import { NodeFileSystem, NodePath } from "@effect/platform-node"
|
||||
import { Config } from "@/config/config"
|
||||
import { ConfigManaged } from "@/config/managed"
|
||||
import { ConfigPaths } from "@/config/paths"
|
||||
import { ConfigParse } from "../../src/config/parse"
|
||||
import { EffectFlock } from "@opencode-ai/core/util/effect-flock"
|
||||
|
||||
@@ -57,6 +58,7 @@ const layer = Config.layer.pipe(
|
||||
Layer.provide(emptyAccount),
|
||||
Layer.provideMerge(infra),
|
||||
Layer.provide(noopNpm),
|
||||
Layer.provide(ConfigPaths.defaultLayer),
|
||||
)
|
||||
|
||||
const it = testEffect(layer)
|
||||
@@ -548,6 +550,7 @@ test("resolves env templates in account config with account token", async () =>
|
||||
Layer.provide(fakeAccount),
|
||||
Layer.provideMerge(infra),
|
||||
Layer.provide(noopNpm),
|
||||
Layer.provide(ConfigPaths.defaultLayer),
|
||||
)
|
||||
|
||||
try {
|
||||
@@ -1059,6 +1062,7 @@ test("installs dependencies in writable OPENCODE_CONFIG_DIR", async () => {
|
||||
Layer.provide(emptyAccount),
|
||||
Layer.provideMerge(infra),
|
||||
Layer.provide(noopNpm),
|
||||
Layer.provide(ConfigPaths.defaultLayer),
|
||||
)
|
||||
|
||||
try {
|
||||
@@ -1944,6 +1948,7 @@ test("project config overrides remote well-known config", async () => {
|
||||
Layer.provide(emptyAccount),
|
||||
Layer.provideMerge(infra),
|
||||
Layer.provide(noopNpm),
|
||||
Layer.provide(ConfigPaths.defaultLayer),
|
||||
)
|
||||
|
||||
try {
|
||||
@@ -2002,6 +2007,7 @@ test("wellknown URL with trailing slash is normalized", async () => {
|
||||
Layer.provide(emptyAccount),
|
||||
Layer.provideMerge(infra),
|
||||
Layer.provide(noopNpm),
|
||||
Layer.provide(ConfigPaths.defaultLayer),
|
||||
)
|
||||
|
||||
try {
|
||||
@@ -2074,6 +2080,7 @@ test("wellknown remote_config supports templated env vars in headers", async ()
|
||||
Layer.provide(emptyAccount),
|
||||
Layer.provideMerge(infra),
|
||||
Layer.provide(noopNpm),
|
||||
Layer.provide(ConfigPaths.defaultLayer),
|
||||
)
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user