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