Apply PR #22753: core: move plugin intialisation to config layer override

This commit is contained in:
opencode-agent[bot] 2026-04-27 20:49:07 +00:00
commit fa311141a7
5 changed files with 42 additions and 17 deletions

View file

@ -6,6 +6,7 @@ import os from "node:os"
import { Server } from "../../server/server" import { Server } from "../../server/server"
import { cmd } from "./cmd" import { cmd } from "./cmd"
import { withNetworkOptions, resolveNetworkOptions } from "../network" import { withNetworkOptions, resolveNetworkOptions } from "../network"
import { bootstrap } from "../bootstrap"
import { Flag } from "@opencode-ai/core/flag/flag" import { Flag } from "@opencode-ai/core/flag/flag"
import { PushRelay } from "../../server/push-relay" import { PushRelay } from "../../server/push-relay"
import * as Log from "@opencode-ai/core/util/log" import * as Log from "@opencode-ai/core/util/log"
@ -214,7 +215,7 @@ export const ServeCommand = cmd({
}), }),
describe: "starts a headless opencode server", describe: "starts a headless opencode server",
handler: async (args) => { handler: async (args) => {
const opts = await resolveNetworkOptions(args) const opts = await bootstrap(process.cwd(), () => resolveNetworkOptions(args))
const relayURL = ( const relayURL = (
args["relay-url"] ?? args["relay-url"] ??
process.env.OPENCODE_EXPERIMENTAL_PUSH_RELAY_URL ?? process.env.OPENCODE_EXPERIMENTAL_PUSH_RELAY_URL ??

View file

@ -8,6 +8,7 @@ import { UI } from "@/cli/ui"
import * as Log from "@opencode-ai/core/util/log" import * as Log from "@opencode-ai/core/util/log"
import { errorMessage } from "@/util/error" import { errorMessage } from "@/util/error"
import { withTimeout } from "@/util/timeout" import { withTimeout } from "@/util/timeout"
import { Instance } from "@/project/instance"
import { withNetworkOptions, resolveNetworkOptionsNoConfig } from "@/cli/network" import { withNetworkOptions, resolveNetworkOptionsNoConfig } from "@/cli/network"
import { Filesystem } from "@/util/filesystem" import { Filesystem } from "@/util/filesystem"
import type { GlobalEvent } from "@opencode-ai/sdk/v2" import type { GlobalEvent } from "@opencode-ai/sdk/v2"
@ -187,7 +188,11 @@ export const TuiThreadCommand = cmd({
const prompt = await input(args.prompt) const prompt = await input(args.prompt)
const config = await TuiConfig.get() const config = await TuiConfig.get()
const network = resolveNetworkOptionsNoConfig(args) const network = await Instance.provide({
directory: cwd,
fn: () => resolveNetworkOptionsNoConfig(args),
})
const external = const external =
process.argv.includes("--port") || process.argv.includes("--port") ||
process.argv.includes("--hostname") || process.argv.includes("--hostname") ||

View file

@ -5,6 +5,7 @@ import { withNetworkOptions, resolveNetworkOptions } from "../network"
import { Flag } from "@opencode-ai/core/flag/flag" import { Flag } from "@opencode-ai/core/flag/flag"
import open from "open" import open from "open"
import { networkInterfaces } from "os" import { networkInterfaces } from "os"
import { bootstrap } from "../bootstrap"
function getNetworkIPs() { function getNetworkIPs() {
const nets = networkInterfaces() const nets = networkInterfaces()
@ -36,7 +37,7 @@ export const WebCommand = cmd({
if (!Flag.OPENCODE_SERVER_PASSWORD) { if (!Flag.OPENCODE_SERVER_PASSWORD) {
UI.println(UI.Style.TEXT_WARNING_BOLD + "! OPENCODE_SERVER_PASSWORD is not set; server is unsecured.") UI.println(UI.Style.TEXT_WARNING_BOLD + "! OPENCODE_SERVER_PASSWORD is not set; server is unsecured.")
} }
const opts = await resolveNetworkOptions(args) const opts = await bootstrap(process.cwd(), () => resolveNetworkOptions(args))
const server = await Server.listen(opts) const server = await Server.listen(opts)
UI.empty() UI.empty()
UI.println(UI.logo(" ")) UI.println(UI.logo(" "))

View file

@ -46,16 +46,34 @@ import { Pty } from "@/pty"
import { Installation } from "@/installation" import { Installation } from "@/installation"
import { ShareNext } from "@/share/share-next" import { ShareNext } from "@/share/share-next"
import { SessionShare } from "@/share/session" import { SessionShare } from "@/share/session"
import * as Effect from "effect/Effect"
import { Npm } from "@opencode-ai/core/npm" import { Npm } from "@opencode-ai/core/npm"
import { memoMap } from "@opencode-ai/core/effect/memo-map" import { memoMap } from "@opencode-ai/core/effect/memo-map"
// Adjusts the default Config layer to ensure that plugins are always initialised before
// any other layers read the current config
const ConfigWithPluginPriority = Layer.effect(
Config.Service,
Effect.gen(function* () {
const config = yield* Config.Service
const plugin = yield* Plugin.Service
return {
...config,
get: () => Effect.andThen(plugin.init(), config.get),
getGlobal: () => Effect.andThen(plugin.init(), config.getGlobal),
getConsoleState: () => Effect.andThen(plugin.init(), config.getConsoleState),
}
}),
).pipe(Layer.provide(Layer.merge(Plugin.defaultLayer, Config.defaultLayer)))
export const AppLayer = Layer.mergeAll( export const AppLayer = Layer.mergeAll(
Npm.defaultLayer, Npm.defaultLayer,
AppFileSystem.defaultLayer, AppFileSystem.defaultLayer,
Bus.defaultLayer, Bus.defaultLayer,
Auth.defaultLayer, Auth.defaultLayer,
Account.defaultLayer, Account.defaultLayer,
Config.defaultLayer, ConfigWithPluginPriority,
Git.defaultLayer, Git.defaultLayer,
Ripgrep.defaultLayer, Ripgrep.defaultLayer,
File.defaultLayer, File.defaultLayer,

View file

@ -1,4 +1,3 @@
import { Plugin } from "../plugin"
import { Format } from "../format" import { Format } from "../format"
import { LSP } from "@/lsp/lsp" import { LSP } from "@/lsp/lsp"
import { File } from "../file" import { File } from "../file"
@ -9,6 +8,7 @@ import { Bus } from "../bus"
import { Command } from "../command" import { Command } from "../command"
import { Instance } from "./instance" import { Instance } from "./instance"
import * as Log from "@opencode-ai/core/util/log" import * as Log from "@opencode-ai/core/util/log"
import { Plugin } from "../plugin"
import { FileWatcher } from "@/file/watcher" import { FileWatcher } from "@/file/watcher"
import { ShareNext } from "@/share/share-next" import { ShareNext } from "@/share/share-next"
import * as Effect from "effect/Effect" import * as Effect from "effect/Effect"
@ -16,20 +16,20 @@ import { Config } from "@/config/config"
export const InstanceBootstrap = Effect.gen(function* () { export const InstanceBootstrap = Effect.gen(function* () {
Log.Default.info("bootstrapping", { directory: Instance.directory }) Log.Default.info("bootstrapping", { directory: Instance.directory })
// everything depends on config so eager load it for nice traces
yield* Config.Service.use((svc) => svc.get())
// Plugin can mutate config so it has to be initialized before anything else.
yield* Plugin.Service.use((svc) => svc.init())
yield* Effect.all( yield* Effect.all(
[ [
LSP.Service, Config.Service.use((i) => i.get()),
ShareNext.Service, ...[
Format.Service, Plugin.Service,
File.Service, LSP.Service,
FileWatcher.Service, ShareNext.Service,
Vcs.Service, Format.Service,
Snapshot.Service, File.Service,
].map((s) => Effect.forkDetach(s.use((i) => i.init()))), FileWatcher.Service,
Vcs.Service,
Snapshot.Service,
].map((s) => s.use((i) => i.init())),
].map((e) => Effect.forkDetach(e)),
).pipe(Effect.withSpan("InstanceBootstrap.init")) ).pipe(Effect.withSpan("InstanceBootstrap.init"))
yield* Bus.Service.use((svc) => yield* Bus.Service.use((svc) =>