Compare commits

..

2 Commits

Author SHA1 Message Date
Simon Klee b89ec4b11b opencode: lazily compose default layers
defaultLayer is currently composed at import time. This makes module evaluation
sensitive to transitive import order, even though the layer graph itself is
unchanged.

Wrap these compositions in Layer.suspend() so the same graph is built on
demand instead of during module load. This reduces init-order coupling and
makes import reordering for startup work safer.
2026-04-12 11:40:35 +02:00
Simon Klee 3729fd5706 chore(github): vouch simonklee (#22127) 2026-04-12 11:33:38 +02:00
7 changed files with 75 additions and 266 deletions
+1
View File
@@ -26,6 +26,7 @@ kommander
r44vc0rp
rekram1-node
-robinmordasiewicz
simonklee
-spider-yamet clawdbot/llm psychosis, spam pinging the team
thdxr
-toastythebot
+7 -5
View File
@@ -398,11 +398,13 @@ export namespace Agent {
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Provider.defaultLayer),
Layer.provide(Auth.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(Skill.defaultLayer),
export const defaultLayer = Layer.suspend(() =>
layer.pipe(
Layer.provide(Provider.defaultLayer),
Layer.provide(Auth.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(Skill.defaultLayer),
),
)
const { runPromise } = makeRuntime(Service, defaultLayer)
+2 -4
View File
@@ -183,9 +183,7 @@ export namespace Command {
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Config.defaultLayer),
Layer.provide(MCP.defaultLayer),
Layer.provide(Skill.defaultLayer),
export const defaultLayer = Layer.suspend(() =>
layer.pipe(Layer.provide(Config.defaultLayer), Layer.provide(MCP.defaultLayer), Layer.provide(Skill.defaultLayer)),
)
}
+46 -244
View File
@@ -1,17 +1,40 @@
import yargs from "yargs"
import { hideBin } from "yargs/helpers"
import { RunCommand } from "./cli/cmd/run"
import { GenerateCommand } from "./cli/cmd/generate"
import { Log } from "./util/log"
import { ConsoleCommand } from "./cli/cmd/account"
import { ProvidersCommand } from "./cli/cmd/providers"
import { AgentCommand } from "./cli/cmd/agent"
import { UpgradeCommand } from "./cli/cmd/upgrade"
import { UninstallCommand } from "./cli/cmd/uninstall"
import { ModelsCommand } from "./cli/cmd/models"
import { UI } from "./cli/ui"
import { Installation } from "./installation"
import { NamedError } from "@opencode-ai/util/error"
import { FormatError } from "./cli/error"
import { ServeCommand } from "./cli/cmd/serve"
import { Filesystem } from "./util/filesystem"
import { DebugCommand } from "./cli/cmd/debug"
import { StatsCommand } from "./cli/cmd/stats"
import { McpCommand } from "./cli/cmd/mcp"
import { GithubCommand } from "./cli/cmd/github"
import { ExportCommand } from "./cli/cmd/export"
import { ImportCommand } from "./cli/cmd/import"
import { AttachCommand } from "./cli/cmd/tui/attach"
import { TuiThreadCommand } from "./cli/cmd/tui/thread"
import { AcpCommand } from "./cli/cmd/acp"
import { EOL } from "os"
import { WebCommand } from "./cli/cmd/web"
import { PrCommand } from "./cli/cmd/pr"
import { SessionCommand } from "./cli/cmd/session"
import { DbCommand } from "./cli/cmd/db"
import path from "path"
import { Global } from "./global"
import { JsonMigration } from "./storage/json-migration"
import { Database } from "./storage/db"
import { errorMessage } from "./util/error"
import { PluginCommand } from "./cli/cmd/plug"
import { Heap } from "./cli/heap"
import { drizzle } from "drizzle-orm/bun-sqlite"
@@ -29,156 +52,6 @@ process.on("uncaughtException", (e) => {
const args = hideBin(process.argv)
type Mode =
| "all"
| "none"
| "tui"
| "attach"
| "run"
| "acp"
| "mcp"
| "generate"
| "debug"
| "console"
| "providers"
| "agent"
| "upgrade"
| "uninstall"
| "serve"
| "web"
| "models"
| "stats"
| "export"
| "import"
| "github"
| "pr"
| "session"
| "plugin"
| "db"
const map = new Map<string, Mode>([
["attach", "attach"],
["run", "run"],
["acp", "acp"],
["mcp", "mcp"],
["generate", "generate"],
["debug", "debug"],
["console", "console"],
["providers", "providers"],
["auth", "providers"],
["agent", "agent"],
["upgrade", "upgrade"],
["uninstall", "uninstall"],
["serve", "serve"],
["web", "web"],
["models", "models"],
["stats", "stats"],
["export", "export"],
["import", "import"],
["github", "github"],
["pr", "pr"],
["session", "session"],
["plugin", "plugin"],
["plug", "plugin"],
["db", "db"],
])
function flag(arg: string, name: string) {
return arg === `--${name}` || arg === `--no-${name}` || arg.startsWith(`--${name}=`)
}
function value(arg: string, name: string) {
return arg === `--${name}` || arg.startsWith(`--${name}=`)
}
// Match the root parser closely enough to decide which top-level module to load.
function pick(argv: string[]): Mode {
for (let i = 0; i < argv.length; i++) {
const arg = argv[i]
if (!arg) continue
if (arg === "--") return "tui"
if (arg === "completion") return "all"
if (arg === "--help" || arg === "-h") return "all"
if (arg === "--version" || arg === "-v") return "none"
if (flag(arg, "print-logs") || flag(arg, "pure")) continue
if (value(arg, "log-level")) {
if (arg === "--log-level") i += 1
continue
}
if (arg.startsWith("-") && !arg.startsWith("--")) {
if (arg.includes("h")) return "all"
if (arg.includes("v")) return "none"
return "tui"
}
if (arg.startsWith("-")) return "tui"
return map.get(arg) ?? "tui"
}
return "tui"
}
const mode = pick(args)
const all = mode === "all"
const none = mode === "none"
function load<T>(on: boolean, get: () => Promise<T>): Promise<T | undefined> {
if (!on) {
return Promise.resolve(undefined)
}
return get()
}
const [
TuiThreadCommand,
AttachCommand,
RunCommand,
AcpCommand,
McpCommand,
GenerateCommand,
DebugCommand,
ConsoleCommand,
ProvidersCommand,
AgentCommand,
UpgradeCommand,
UninstallCommand,
ServeCommand,
WebCommand,
ModelsCommand,
StatsCommand,
ExportCommand,
ImportCommand,
GithubCommand,
PrCommand,
SessionCommand,
PluginCommand,
DbCommand,
] = await Promise.all([
load(!none && (all || mode === "tui"), () => import("./cli/cmd/tui/thread").then((x) => x.TuiThreadCommand)),
load(!none && (all || mode === "attach"), () => import("./cli/cmd/tui/attach").then((x) => x.AttachCommand)),
load(!none && (all || mode === "run"), () => import("./cli/cmd/run").then((x) => x.RunCommand)),
load(!none && (all || mode === "acp"), () => import("./cli/cmd/acp").then((x) => x.AcpCommand)),
load(!none && (all || mode === "mcp"), () => import("./cli/cmd/mcp").then((x) => x.McpCommand)),
load(!none && (all || mode === "generate"), () => import("./cli/cmd/generate").then((x) => x.GenerateCommand)),
load(!none && (all || mode === "debug"), () => import("./cli/cmd/debug").then((x) => x.DebugCommand)),
load(!none && (all || mode === "console"), () => import("./cli/cmd/account").then((x) => x.ConsoleCommand)),
load(!none && (all || mode === "providers"), () => import("./cli/cmd/providers").then((x) => x.ProvidersCommand)),
load(!none && (all || mode === "agent"), () => import("./cli/cmd/agent").then((x) => x.AgentCommand)),
load(!none && (all || mode === "upgrade"), () => import("./cli/cmd/upgrade").then((x) => x.UpgradeCommand)),
load(!none && (all || mode === "uninstall"), () => import("./cli/cmd/uninstall").then((x) => x.UninstallCommand)),
load(!none && (all || mode === "serve"), () => import("./cli/cmd/serve").then((x) => x.ServeCommand)),
load(!none && (all || mode === "web"), () => import("./cli/cmd/web").then((x) => x.WebCommand)),
load(!none && (all || mode === "models"), () => import("./cli/cmd/models").then((x) => x.ModelsCommand)),
load(!none && (all || mode === "stats"), () => import("./cli/cmd/stats").then((x) => x.StatsCommand)),
load(!none && (all || mode === "export"), () => import("./cli/cmd/export").then((x) => x.ExportCommand)),
load(!none && (all || mode === "import"), () => import("./cli/cmd/import").then((x) => x.ImportCommand)),
load(!none && (all || mode === "github"), () => import("./cli/cmd/github").then((x) => x.GithubCommand)),
load(!none && (all || mode === "pr"), () => import("./cli/cmd/pr").then((x) => x.PrCommand)),
load(!none && (all || mode === "session"), () => import("./cli/cmd/session").then((x) => x.SessionCommand)),
load(!none && (all || mode === "plugin"), () => import("./cli/cmd/plug").then((x) => x.PluginCommand)),
load(!none && (all || mode === "db"), () => import("./cli/cmd/db").then((x) => x.DbCommand)),
])
function show(out: string) {
const text = out.trimStart()
if (!text.startsWith("opencode ")) {
@@ -275,100 +148,29 @@ const cli = yargs(args)
})
.usage("")
.completion("completion", "generate shell completion script")
if (TuiThreadCommand) {
cli.command(TuiThreadCommand)
}
if (AttachCommand) {
cli.command(AttachCommand)
}
if (AcpCommand) {
cli.command(AcpCommand)
}
if (McpCommand) {
cli.command(McpCommand)
}
if (RunCommand) {
cli.command(RunCommand)
}
if (GenerateCommand) {
cli.command(GenerateCommand)
}
if (DebugCommand) {
cli.command(DebugCommand)
}
if (ConsoleCommand) {
cli.command(ConsoleCommand)
}
if (ProvidersCommand) {
cli.command(ProvidersCommand)
}
if (AgentCommand) {
cli.command(AgentCommand)
}
if (UpgradeCommand) {
cli.command(UpgradeCommand)
}
if (UninstallCommand) {
cli.command(UninstallCommand)
}
if (ServeCommand) {
cli.command(ServeCommand)
}
if (WebCommand) {
cli.command(WebCommand)
}
if (ModelsCommand) {
cli.command(ModelsCommand)
}
if (StatsCommand) {
cli.command(StatsCommand)
}
if (ExportCommand) {
cli.command(ExportCommand)
}
if (ImportCommand) {
cli.command(ImportCommand)
}
if (GithubCommand) {
cli.command(GithubCommand)
}
if (PrCommand) {
cli.command(PrCommand)
}
if (SessionCommand) {
cli.command(SessionCommand)
}
if (PluginCommand) {
cli.command(PluginCommand)
}
if (DbCommand) {
cli.command(DbCommand)
}
cli
.command(AcpCommand)
.command(McpCommand)
.command(TuiThreadCommand)
.command(AttachCommand)
.command(RunCommand)
.command(GenerateCommand)
.command(DebugCommand)
.command(ConsoleCommand)
.command(ProvidersCommand)
.command(AgentCommand)
.command(UpgradeCommand)
.command(UninstallCommand)
.command(ServeCommand)
.command(WebCommand)
.command(ModelsCommand)
.command(StatsCommand)
.command(ExportCommand)
.command(ImportCommand)
.command(GithubCommand)
.command(PrCommand)
.command(SessionCommand)
.command(PluginCommand)
.command(DbCommand)
.fail((msg, err) => {
if (
msg?.startsWith("Unknown argument") ||
+3 -1
View File
@@ -678,7 +678,9 @@ export namespace Session {
}),
)
export const defaultLayer = layer.pipe(Layer.provide(Bus.layer), Layer.provide(Storage.defaultLayer))
export const defaultLayer = Layer.suspend(() =>
layer.pipe(Layer.provide(Bus.layer), Layer.provide(Storage.defaultLayer)),
)
const { runPromise } = makeRuntime(Service, defaultLayer)
+9 -7
View File
@@ -339,12 +339,14 @@ export namespace ShareNext {
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Bus.layer),
Layer.provide(Account.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(FetchHttpClient.layer),
Layer.provide(Provider.defaultLayer),
Layer.provide(Session.defaultLayer),
export const defaultLayer = Layer.suspend(() =>
layer.pipe(
Layer.provide(Bus.layer),
Layer.provide(Account.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(FetchHttpClient.layer),
Layer.provide(Provider.defaultLayer),
Layer.provide(Session.defaultLayer),
),
)
}
+7 -5
View File
@@ -230,11 +230,13 @@ export namespace Skill {
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Discovery.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(Bus.layer),
Layer.provide(AppFileSystem.defaultLayer),
export const defaultLayer = Layer.suspend(() =>
layer.pipe(
Layer.provide(Discovery.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(Bus.layer),
Layer.provide(AppFileSystem.defaultLayer),
),
)
export function fmt(list: Info[], opts: { verbose: boolean }) {