Преглед изворни кода

opencode: lazy-load top-level CLI commands

The CLI imports every top-level command before argument parsing has
decided which handler will run. This makes simple invocations pay for
the full command graph up front and slows down the default startup path.

Parse the root argv first and load only the command module that matches
the selected top-level command. Keep falling back to the default TUI
path for non-command positionals, and preserve root help, version and
completion handling
Simon Klee пре 1 недеља
родитељ
комит
75666b271b
1 измењених фајлова са 244 додато и 46 уклоњено
  1. 244 46
      packages/opencode/src/index.ts

+ 244 - 46
packages/opencode/src/index.ts

@@ -1,40 +1,17 @@
 import yargs from "yargs"
 import yargs from "yargs"
 import { hideBin } from "yargs/helpers"
 import { hideBin } from "yargs/helpers"
-import { RunCommand } from "./cli/cmd/run"
-import { GenerateCommand } from "./cli/cmd/generate"
 import { Log } from "./util/log"
 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 { UI } from "./cli/ui"
 import { Installation } from "./installation"
 import { Installation } from "./installation"
 import { NamedError } from "@opencode-ai/util/error"
 import { NamedError } from "@opencode-ai/util/error"
 import { FormatError } from "./cli/error"
 import { FormatError } from "./cli/error"
-import { ServeCommand } from "./cli/cmd/serve"
 import { Filesystem } from "./util/filesystem"
 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 { 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 path from "path"
 import { Global } from "./global"
 import { Global } from "./global"
 import { JsonMigration } from "./storage/json-migration"
 import { JsonMigration } from "./storage/json-migration"
 import { Database } from "./storage/db"
 import { Database } from "./storage/db"
 import { errorMessage } from "./util/error"
 import { errorMessage } from "./util/error"
-import { PluginCommand } from "./cli/cmd/plug"
 import { Heap } from "./cli/heap"
 import { Heap } from "./cli/heap"
 import { drizzle } from "drizzle-orm/bun-sqlite"
 import { drizzle } from "drizzle-orm/bun-sqlite"
 
 
@@ -52,6 +29,156 @@ process.on("uncaughtException", (e) => {
 
 
 const args = hideBin(process.argv)
 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) {
 function show(out: string) {
   const text = out.trimStart()
   const text = out.trimStart()
   if (!text.startsWith("opencode ")) {
   if (!text.startsWith("opencode ")) {
@@ -148,29 +275,100 @@ const cli = yargs(args)
   })
   })
   .usage("")
   .usage("")
   .completion("completion", "generate shell completion script")
   .completion("completion", "generate shell completion script")
-  .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)
+
+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
   .fail((msg, err) => {
   .fail((msg, err) => {
     if (
     if (
       msg?.startsWith("Unknown argument") ||
       msg?.startsWith("Unknown argument") ||