Skip to content

Commit 9445b4d

Browse files
committed
refactor(server): inject config options
1 parent 0409e68 commit 9445b4d

17 files changed

Lines changed: 133 additions & 53 deletions

File tree

‎packages/cli/src/commands/handlers/mcp/add.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ export default Runtime.handler(
3030
? { type: "remote" as const, url, ...(headers ? { headers } : {}) }
3131
: { type: "local" as const, command, ...(environment ? { environment } : {}) }
3232

33-
const configPath = yield* Effect.promise(() => resolveConfigPath(input.global ? Global.Path.config : process.cwd()))
33+
const global = yield* Global.Service
34+
const configPath = yield* Effect.promise(() => resolveConfigPath(input.global ? global.config : process.cwd()))
3435
yield* Effect.promise(() => write(configPath, input.name, server))
3536
process.stdout.write(`MCP server "${input.name}" added to ${configPath}` + EOL)
3637
}),

‎packages/cli/src/index.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,14 @@ Effect.logInfo("cli starting", {
6060
Effect.annotateLogs({ role: "cli" }),
6161
Effect.provide(Config.layer),
6262
Effect.provide(Updater.layer),
63-
Effect.provide(LayerNode.compile(LayerNode.group([Global.node, AppProcess.node, Npm.node]))),
63+
Effect.provide(
64+
LayerNode.compile(LayerNode.group([Global.node, AppProcess.node, Npm.node]), [
65+
[
66+
Global.node,
67+
Global.layerWith(process.env.OPENCODE_CONFIG_DIR ? { config: process.env.OPENCODE_CONFIG_DIR } : {}),
68+
],
69+
]),
70+
),
6471
Effect.provide(
6572
Observability.layer({
6673
endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,

‎packages/cli/src/server-process.ts‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,14 @@ export type Options = {
2626
export const run = Effect.fnUntraced(function* (options: Options) {
2727
return yield* processEffect(options).pipe(
2828
Effect.provide(Updater.layer),
29-
Effect.provide(LayerNode.compile(LayerNode.group([Global.node, AppProcess.node]))),
29+
Effect.provide(
30+
LayerNode.compile(LayerNode.group([Global.node, AppProcess.node]), [
31+
[
32+
Global.node,
33+
Global.layerWith(process.env.OPENCODE_CONFIG_DIR ? { config: process.env.OPENCODE_CONFIG_DIR } : {}),
34+
],
35+
]),
36+
),
3037
Effect.provide(NodeServices.layer),
3138
)
3239
})
@@ -77,6 +84,12 @@ const processEffect = Effect.fnUntraced(function* (options: Options) {
7784
endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
7885
headers: process.env.OTEL_EXPORTER_OTLP_HEADERS,
7986
},
87+
config: {
88+
directory: process.env.OPENCODE_CONFIG_DIR,
89+
project: !truthy(
90+
process.env.OPENCODE_CONFIG_PROJECT_DISABLE ?? process.env.OPENCODE_DISABLE_PROJECT_CONFIG,
91+
),
92+
},
8093
fs: {
8194
filewatcher: !truthy(
8295
process.env.OPENCODE_FILEWATCHER_DISABLE ?? process.env.OPENCODE_DISABLE_FILEWATCHER,

‎packages/core/src/config.ts‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,14 @@ export interface Interface {
153153
readonly entries: () => Effect.Effect<Entry[]>
154154
}
155155

156+
export const Options = Schema.Struct({
157+
project: Schema.optional(Schema.Boolean),
158+
})
159+
export type Options = typeof Options.Type
160+
156161
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Config") {}
157162

158-
const layer = Layer.effect(
163+
export const layer = (options?: Options) => Layer.effect(
159164
Service,
160165
Effect.gen(function* () {
161166
const fs = yield* FSUtil.Service
@@ -239,7 +244,7 @@ const layer = Layer.effect(
239244
const globalAgentsDirectory = AbsolutePath.make(path.join(global.home, ".agents"))
240245
const globalClaudeDirectory = AbsolutePath.make(path.join(global.home, ".claude"))
241246
const locationIsGlobal = path.resolve(location.directory) === path.resolve(global.config)
242-
const discovered = locationIsGlobal
247+
const discovered = locationIsGlobal || options?.project === false
243248
? []
244249
: yield* fs
245250
.up({
@@ -396,6 +401,6 @@ const layer = Layer.effect(
396401

397402
export const node = makeLocationNode({
398403
service: Service,
399-
layer,
404+
layer: layer(),
400405
deps: [Watcher.node, EventV2.node, FSUtil.node, Global.node, Location.node, Credential.node, WellKnown.node],
401406
})

‎packages/core/src/effect/layer-node.ts‎

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ type CheckReplacementErrors<SourceError, ReplacementError> = [Exclude<Replacemen
121121
type CheckReplacement<Item> = Item extends readonly [Node<infer A, infer E, infer T>, infer Replacement]
122122
? Replacement extends Node<NoInfer<A>, infer E2, T>
123123
? CheckReplacementErrors<E, NoInfer<E2>>
124-
: Replacement extends Layer.Layer<NoInfer<A>, infer E2, never>
124+
: Replacement extends Layer.Layer<NoInfer<A>, infer E2, infer _R>
125125
? CheckReplacementErrors<E, NoInfer<E2>>
126126
: { readonly "Invalid replacement": Replacement }
127127
: { readonly "Invalid replacement": Item }
@@ -133,14 +133,16 @@ type CheckReplacements<Items extends Replacements> = {
133133
type ValidReplacements<Items extends Replacements> = Items & CheckReplacements<Items>
134134

135135
function replacementNode(source: AnyNode, replacement: AnyNode | Layer.Any) {
136-
const replacementNode = isNode(replacement)
136+
const replacementNode: AnyNode = isNode(replacement)
137137
? replacement
138-
: make({
139-
...nodeMakeIdentity(source),
140-
layer: replacement as Layer.Layer<unknown, unknown>,
141-
deps: [],
138+
: {
139+
kind: "layer",
140+
name: source.name,
141+
service: source.service,
142+
implementation: replacement,
143+
dependencies: source.dependencies,
142144
tag: source.tag,
143-
})
145+
}
144146
if (source.name !== replacementNode.name) {
145147
throw new Error(`Cannot replace ${source.name} with ${replacementNode.name}`)
146148
}
@@ -150,11 +152,6 @@ function replacementNode(source: AnyNode, replacement: AnyNode | Layer.Any) {
150152
return replacementNode
151153
}
152154

153-
function nodeMakeIdentity(node: AnyNode): NodeIdentity {
154-
if (node.service !== undefined) return { service: node.service }
155-
return { name: node.name }
156-
}
157-
158155
function isNode(input: Layer.Any | AnyNode): input is AnyNode {
159156
return "kind" in input && "dependencies" in input
160157
}

‎packages/core/src/filesystem/search.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@ export const layer = (options?: Options) => Layer.unwrap(
246246
}),
247247
)
248248

249-
export function nodeWith(options?: Options) {
250-
return makeLocationNode({ service: Service, layer: layer(options), deps: [FSUtil.node, Location.node, Ripgrep.node] })
251-
}
252-
253-
export const node = nodeWith()
249+
export const node = makeLocationNode({
250+
service: Service,
251+
layer: layer(),
252+
deps: [FSUtil.node, Location.node, Ripgrep.node],
253+
})

‎packages/core/src/filesystem/watcher.ts‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,7 @@ export const layer = (options?: Options) => Layer.effect(
144144
}),
145145
)
146146

147-
export function nodeWith(options?: Options) {
148-
return makeGlobalNode({ service: Service, layer: layer(options), deps: [] })
149-
}
150-
151-
export const node = nodeWith()
147+
export const node = makeGlobalNode({ service: Service, layer: layer(), deps: [] })
152148

153149
function subscribeDirectory(
154150
native: typeof import("@parcel/watcher") | undefined,

‎packages/core/src/global.ts‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { xdgData, xdgCache, xdgConfig, xdgState } from "xdg-basedir"
44
import os from "os"
55
import { Context, Effect, Layer } from "effect"
66
import { Flock } from "./util/flock"
7-
import { Flag } from "./flag/flag"
87
import { makeGlobalNode } from "./effect/app-node"
98

109
const app = "opencode"
@@ -61,7 +60,7 @@ export function make(input: Partial<Interface> = {}): Interface {
6160
home: Path.home,
6261
data: Path.data,
6362
cache: Path.cache,
64-
config: Flag.OPENCODE_CONFIG_DIR ?? Path.config,
63+
config: Path.config,
6564
state: Path.state,
6665
tmp: Path.tmp,
6766
bin: Path.bin,
@@ -73,7 +72,7 @@ export function make(input: Partial<Interface> = {}): Interface {
7372

7473
const layer = Layer.effect(
7574
Service,
76-
Effect.sync(() => Service.of(make())),
75+
Effect.sync(() => Service.of(make({ config: process.env.OPENCODE_CONFIG_DIR ?? Path.config }))),
7776
)
7877

7978
export const node = makeGlobalNode({ service: Service, layer: layer, deps: [] })

‎packages/core/src/instruction-discovery.ts‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ export * as InstructionDiscovery from "./instruction-discovery"
33
import { Array, Context, Effect, Layer, Schema } from "effect"
44
import { isAbsolute, join, relative, sep } from "path"
55
import { FSUtil } from "./fs-util"
6-
import { Flag } from "./flag/flag"
76
import { Global } from "./global"
87
import { Location } from "./location"
98
import { AbsolutePath } from "./schema"
@@ -22,9 +21,14 @@ export interface Interface {
2221
readonly load: () => Effect.Effect<Instructions.Instructions>
2322
}
2423

24+
export const Options = Schema.Struct({
25+
project: Schema.optional(Schema.Boolean),
26+
})
27+
export type Options = typeof Options.Type
28+
2529
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/InstructionDiscovery") {}
2630

27-
const layer = Layer.effect(
31+
export const layer = (options?: Options) => Layer.effect(
2832
Service,
2933
Effect.gen(function* () {
3034
const fs = yield* FSUtil.Service
@@ -52,7 +56,7 @@ const layer = Layer.effect(
5256
fromProject === "" || (fromProject !== ".." && !fromProject.startsWith(`..${sep}`) && !isAbsolute(fromProject))
5357
const discovered = new Set(
5458
yield* Effect.forEach(
55-
Flag.OPENCODE_DISABLE_PROJECT_CONFIG || !insideProject
59+
options?.project === false || !insideProject
5660
? []
5761
: yield* fs.up({
5862
targets: ["AGENTS.md"],
@@ -93,7 +97,11 @@ const layer = Layer.effect(
9397
}),
9498
)
9599

96-
export const node = makeLocationNode({ service: Service, layer, deps: [FSUtil.node, Global.node, Location.node] })
100+
export const node = makeLocationNode({
101+
service: Service,
102+
layer: layer(),
103+
deps: [FSUtil.node, Global.node, Location.node],
104+
})
97105

98106
function render(files: ReadonlyArray<File>) {
99107
return files.map((file) => `Instructions from: ${file.path}\n${file.content}`).join("\n\n")

‎packages/core/src/models-dev.ts‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -659,10 +659,6 @@ export const layer = (options?: Options) => Layer.effect(
659659
}),
660660
)
661661

662-
export function nodeWith(options?: Options) {
663-
return makeGlobalNode({ service: Service, layer: layer(options), deps: [FSUtil.node, EventV2.node, httpClient] })
664-
}
665-
666-
export const node = nodeWith()
662+
export const node = makeGlobalNode({ service: Service, layer: layer(), deps: [FSUtil.node, EventV2.node, httpClient] })
667663

668664
export * as ModelsDev from "./models-dev"

0 commit comments

Comments
 (0)