mirror of
https://github.com/farcasclaudiu/openclaw.git
synced 2026-06-28 21:01:43 +03:00
perf(cli): speed up startup
This commit is contained in:
@@ -64,8 +64,10 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Sessions/Agents: pass `agentId` through status and usage transcript-resolution paths (auto-reply, gateway usage APIs, and session cost/log loaders) so non-default agents can resolve absolute session files without path-validation failures. (#15103) Thanks @jalehman.
|
- Sessions/Agents: pass `agentId` through status and usage transcript-resolution paths (auto-reply, gateway usage APIs, and session cost/log loaders) so non-default agents can resolve absolute session files without path-validation failures. (#15103) Thanks @jalehman.
|
||||||
- Sessions: archive previous transcript files on `/new` and `/reset` session resets (including gateway `sessions.reset`) so stale transcripts do not accumulate on disk. (#14869) Thanks @mcaxtr.
|
- Sessions: archive previous transcript files on `/new` and `/reset` session resets (including gateway `sessions.reset`) so stale transcripts do not accumulate on disk. (#14869) Thanks @mcaxtr.
|
||||||
- Status/Sessions: stop clamping derived `totalTokens` to context-window size, keep prompt-token snapshots wired through session accounting, and surface context usage as unknown when fresh snapshot data is missing to avoid false 100% reports. (#15114) Thanks @echoVic.
|
- Status/Sessions: stop clamping derived `totalTokens` to context-window size, keep prompt-token snapshots wired through session accounting, and surface context usage as unknown when fresh snapshot data is missing to avoid false 100% reports. (#15114) Thanks @echoVic.
|
||||||
|
- Gateway/Routing: speed up hot paths for session listing (derived titles + previews), WS broadcast, and binding resolution.
|
||||||
- CLI/Completion: route plugin-load logs to stderr and write generated completion scripts directly to stdout to avoid `source <(openclaw completion ...)` corruption. (#15481) Thanks @arosstale.
|
- CLI/Completion: route plugin-load logs to stderr and write generated completion scripts directly to stdout to avoid `source <(openclaw completion ...)` corruption. (#15481) Thanks @arosstale.
|
||||||
- CLI: lazily load outbound provider dependencies and remove forced success-path exits so commands terminate naturally without killing intentional long-running foreground actions. (#12906) Thanks @DrCrinkle.
|
- CLI: lazily load outbound provider dependencies and remove forced success-path exits so commands terminate naturally without killing intentional long-running foreground actions. (#12906) Thanks @DrCrinkle.
|
||||||
|
- CLI: speed up startup by lazily registering core commands (keeps rich `--help` while reducing cold-start overhead).
|
||||||
- Security/Gateway + ACP: block high-risk tools (`sessions_spawn`, `sessions_send`, `gateway`, `whatsapp_login`) from HTTP `/tools/invoke` by default with `gateway.tools.{allow,deny}` overrides, and harden ACP permission selection to fail closed when tool identity/options are ambiguous while supporting `allow_always`/`reject_always`. (#15390) Thanks @aether-ai-agent.
|
- Security/Gateway + ACP: block high-risk tools (`sessions_spawn`, `sessions_send`, `gateway`, `whatsapp_login`) from HTTP `/tools/invoke` by default with `gateway.tools.{allow,deny}` overrides, and harden ACP permission selection to fail closed when tool identity/options are ambiguous while supporting `allow_always`/`reject_always`. (#15390) Thanks @aether-ai-agent.
|
||||||
- Security/ACP: prompt for non-read/search permission requests in ACP clients (reduces silent tool approval risk). Thanks @aether-ai-agent.
|
- Security/ACP: prompt for non-read/search permission requests in ACP clients (reduces silent tool approval risk). Thanks @aether-ai-agent.
|
||||||
- Security/Gateway: breaking default-behavior change - canvas IP-based auth fallback now only accepts machine-scoped addresses (RFC1918, link-local, ULA IPv6, CGNAT); public-source IP matches now require bearer token auth. (#14661) Thanks @sumleo.
|
- Security/Gateway: breaking default-behavior change - canvas IP-based auth fallback now only accepts machine-scoped addresses (RFC1918, link-local, ULA IPv6, CGNAT); public-source IP matches now require bearer token auth. (#14661) Thanks @sumleo.
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import path from "node:path";
|
|||||||
import { resolveStateDir } from "../config/paths.js";
|
import { resolveStateDir } from "../config/paths.js";
|
||||||
import { routeLogsToStderr } from "../logging/console.js";
|
import { routeLogsToStderr } from "../logging/console.js";
|
||||||
import { pathExists } from "../utils.js";
|
import { pathExists } from "../utils.js";
|
||||||
|
import { getCoreCliCommandNames, registerCoreCliByName } from "./program/command-registry.js";
|
||||||
|
import { getProgramContext } from "./program/program-context.js";
|
||||||
import { getSubCliEntries, registerSubCliByName } from "./program/register.subclis.js";
|
import { getSubCliEntries, registerSubCliByName } from "./program/register.subclis.js";
|
||||||
|
|
||||||
const COMPLETION_SHELLS = ["zsh", "bash", "powershell", "fish"] as const;
|
const COMPLETION_SHELLS = ["zsh", "bash", "powershell", "fish"] as const;
|
||||||
@@ -240,6 +242,16 @@ export function registerCompletionCli(program: Command) {
|
|||||||
// the completion script written to stdout.
|
// the completion script written to stdout.
|
||||||
routeLogsToStderr();
|
routeLogsToStderr();
|
||||||
const shell = options.shell ?? "zsh";
|
const shell = options.shell ?? "zsh";
|
||||||
|
|
||||||
|
// Completion needs the full Commander command tree (including nested subcommands).
|
||||||
|
// Our CLI defaults to lazy registration for perf; force-register core commands here.
|
||||||
|
const ctx = getProgramContext(program);
|
||||||
|
if (ctx) {
|
||||||
|
for (const name of getCoreCliCommandNames()) {
|
||||||
|
await registerCoreCliByName(program, ctx, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Eagerly register all subcommands to build the full tree
|
// Eagerly register all subcommands to build the full tree
|
||||||
const entries = getSubCliEntries();
|
const entries = getSubCliEntries();
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ import { registerProgramCommands } from "./command-registry.js";
|
|||||||
import { createProgramContext } from "./context.js";
|
import { createProgramContext } from "./context.js";
|
||||||
import { configureProgramHelp } from "./help.js";
|
import { configureProgramHelp } from "./help.js";
|
||||||
import { registerPreActionHooks } from "./preaction.js";
|
import { registerPreActionHooks } from "./preaction.js";
|
||||||
|
import { setProgramContext } from "./program-context.js";
|
||||||
|
|
||||||
export function buildProgram() {
|
export function buildProgram() {
|
||||||
const program = new Command();
|
const program = new Command();
|
||||||
const ctx = createProgramContext();
|
const ctx = createProgramContext();
|
||||||
const argv = process.argv;
|
const argv = process.argv;
|
||||||
|
|
||||||
|
setProgramContext(program, ctx);
|
||||||
configureProgramHelp(program, ctx);
|
configureProgramHelp(program, ctx);
|
||||||
registerPreActionHooks(program, ctx.programVersion);
|
registerPreActionHooks(program, ctx.programVersion);
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
import type { Command } from "commander";
|
import type { Command } from "commander";
|
||||||
import type { ProgramContext } from "./context.js";
|
import type { ProgramContext } from "./context.js";
|
||||||
import { registerBrowserCli } from "../browser-cli.js";
|
import { buildParseArgv, getPrimaryCommand, hasHelpOrVersion } from "../argv.js";
|
||||||
import { registerConfigCli } from "../config-cli.js";
|
import { resolveActionArgs } from "./helpers.js";
|
||||||
import { registerMemoryCli } from "../memory-cli.js";
|
|
||||||
import { registerAgentCommands } from "./register.agent.js";
|
|
||||||
import { registerConfigureCommand } from "./register.configure.js";
|
|
||||||
import { registerMaintenanceCommands } from "./register.maintenance.js";
|
|
||||||
import { registerMessageCommands } from "./register.message.js";
|
|
||||||
import { registerOnboardCommand } from "./register.onboard.js";
|
|
||||||
import { registerSetupCommand } from "./register.setup.js";
|
|
||||||
import { registerStatusHealthSessionsCommands } from "./register.status-health-sessions.js";
|
|
||||||
import { registerSubCliCommands } from "./register.subclis.js";
|
import { registerSubCliCommands } from "./register.subclis.js";
|
||||||
|
|
||||||
type CommandRegisterParams = {
|
type CommandRegisterParams = {
|
||||||
@@ -23,60 +15,198 @@ export type CommandRegistration = {
|
|||||||
register: (params: CommandRegisterParams) => void;
|
register: (params: CommandRegisterParams) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const commandRegistry: CommandRegistration[] = [
|
type CoreCliEntry = {
|
||||||
|
commands: Array<{ name: string; description: string }>;
|
||||||
|
register: (params: CommandRegisterParams) => Promise<void> | void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const shouldRegisterCorePrimaryOnly = (argv: string[]) => {
|
||||||
|
if (hasHelpOrVersion(argv)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const coreEntries: CoreCliEntry[] = [
|
||||||
{
|
{
|
||||||
id: "setup",
|
commands: [{ name: "setup", description: "Setup helpers" }],
|
||||||
register: ({ program }) => registerSetupCommand(program),
|
register: async ({ program }) => {
|
||||||
|
const mod = await import("./register.setup.js");
|
||||||
|
mod.registerSetupCommand(program);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "onboard",
|
commands: [{ name: "onboard", description: "Onboarding helpers" }],
|
||||||
register: ({ program }) => registerOnboardCommand(program),
|
register: async ({ program }) => {
|
||||||
|
const mod = await import("./register.onboard.js");
|
||||||
|
mod.registerOnboardCommand(program);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "configure",
|
commands: [{ name: "configure", description: "Configure wizard" }],
|
||||||
register: ({ program }) => registerConfigureCommand(program),
|
register: async ({ program }) => {
|
||||||
|
const mod = await import("./register.configure.js");
|
||||||
|
mod.registerConfigureCommand(program);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "config",
|
commands: [{ name: "config", description: "Config helpers" }],
|
||||||
register: ({ program }) => registerConfigCli(program),
|
register: async ({ program }) => {
|
||||||
|
const mod = await import("../config-cli.js");
|
||||||
|
mod.registerConfigCli(program);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "maintenance",
|
commands: [{ name: "maintenance", description: "Maintenance commands" }],
|
||||||
register: ({ program }) => registerMaintenanceCommands(program),
|
register: async ({ program }) => {
|
||||||
|
const mod = await import("./register.maintenance.js");
|
||||||
|
mod.registerMaintenanceCommands(program);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "message",
|
commands: [{ name: "message", description: "Send, read, and manage messages" }],
|
||||||
register: ({ program, ctx }) => registerMessageCommands(program, ctx),
|
register: async ({ program, ctx }) => {
|
||||||
|
const mod = await import("./register.message.js");
|
||||||
|
mod.registerMessageCommands(program, ctx);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "memory",
|
commands: [{ name: "memory", description: "Memory commands" }],
|
||||||
register: ({ program }) => registerMemoryCli(program),
|
register: async ({ program }) => {
|
||||||
|
const mod = await import("../memory-cli.js");
|
||||||
|
mod.registerMemoryCli(program);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "agent",
|
commands: [{ name: "agent", description: "Agent commands" }],
|
||||||
register: ({ program, ctx }) =>
|
register: async ({ program, ctx }) => {
|
||||||
registerAgentCommands(program, { agentChannelOptions: ctx.agentChannelOptions }),
|
const mod = await import("./register.agent.js");
|
||||||
|
mod.registerAgentCommands(program, { agentChannelOptions: ctx.agentChannelOptions });
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "subclis",
|
commands: [
|
||||||
register: ({ program, argv }) => registerSubCliCommands(program, argv),
|
{ name: "status", description: "Gateway status" },
|
||||||
|
{ name: "health", description: "Gateway health" },
|
||||||
|
{ name: "sessions", description: "Session management" },
|
||||||
|
],
|
||||||
|
register: async ({ program }) => {
|
||||||
|
const mod = await import("./register.status-health-sessions.js");
|
||||||
|
mod.registerStatusHealthSessionsCommands(program);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "status-health-sessions",
|
commands: [{ name: "browser", description: "Browser tools" }],
|
||||||
register: ({ program }) => registerStatusHealthSessionsCommands(program),
|
register: async ({ program }) => {
|
||||||
},
|
const mod = await import("../browser-cli.js");
|
||||||
{
|
mod.registerBrowserCli(program);
|
||||||
id: "browser",
|
},
|
||||||
register: ({ program }) => registerBrowserCli(program),
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export function getCoreCliCommandNames(): string[] {
|
||||||
|
const seen = new Set<string>();
|
||||||
|
const names: string[] = [];
|
||||||
|
for (const entry of coreEntries) {
|
||||||
|
for (const cmd of entry.commands) {
|
||||||
|
if (seen.has(cmd.name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
seen.add(cmd.name);
|
||||||
|
names.push(cmd.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeCommand(program: Command, command: Command) {
|
||||||
|
const commands = program.commands as Command[];
|
||||||
|
const index = commands.indexOf(command);
|
||||||
|
if (index >= 0) {
|
||||||
|
commands.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerLazyCoreCommand(
|
||||||
|
program: Command,
|
||||||
|
ctx: ProgramContext,
|
||||||
|
entry: CoreCliEntry,
|
||||||
|
command: { name: string; description: string },
|
||||||
|
) {
|
||||||
|
const placeholder = program.command(command.name).description(command.description);
|
||||||
|
placeholder.allowUnknownOption(true);
|
||||||
|
placeholder.allowExcessArguments(true);
|
||||||
|
placeholder.action(async (...actionArgs) => {
|
||||||
|
removeCommand(program, placeholder);
|
||||||
|
await entry.register({ program, ctx, argv: process.argv });
|
||||||
|
const actionCommand = actionArgs.at(-1) as Command | undefined;
|
||||||
|
const root = actionCommand?.parent ?? program;
|
||||||
|
const rawArgs = (root as Command & { rawArgs?: string[] }).rawArgs;
|
||||||
|
const actionArgsList = resolveActionArgs(actionCommand);
|
||||||
|
const fallbackArgv = actionCommand?.name()
|
||||||
|
? [actionCommand.name(), ...actionArgsList]
|
||||||
|
: actionArgsList;
|
||||||
|
const parseArgv = buildParseArgv({
|
||||||
|
programName: program.name(),
|
||||||
|
rawArgs,
|
||||||
|
fallbackArgv,
|
||||||
|
});
|
||||||
|
await program.parseAsync(parseArgv);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function registerCoreCliByName(
|
||||||
|
program: Command,
|
||||||
|
ctx: ProgramContext,
|
||||||
|
name: string,
|
||||||
|
argv: string[] = process.argv,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const entry = coreEntries.find((candidate) =>
|
||||||
|
candidate.commands.some((cmd) => cmd.name === name),
|
||||||
|
);
|
||||||
|
if (!entry) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some registrars install multiple top-level commands (e.g. status/health/sessions).
|
||||||
|
// Remove placeholders/old registrations for all names in the entry before re-registering.
|
||||||
|
for (const cmd of entry.commands) {
|
||||||
|
const existing = program.commands.find((c) => c.name() === cmd.name);
|
||||||
|
if (existing) {
|
||||||
|
removeCommand(program, existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await entry.register({ program, ctx, argv });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerCoreCliCommands(program: Command, ctx: ProgramContext, argv: string[]) {
|
||||||
|
const primary = getPrimaryCommand(argv);
|
||||||
|
if (primary && shouldRegisterCorePrimaryOnly(argv)) {
|
||||||
|
const entry = coreEntries.find((candidate) =>
|
||||||
|
candidate.commands.some((cmd) => cmd.name === primary),
|
||||||
|
);
|
||||||
|
if (entry) {
|
||||||
|
const cmd = entry.commands.find((c) => c.name === primary);
|
||||||
|
if (cmd) {
|
||||||
|
registerLazyCoreCommand(program, ctx, entry, cmd);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const entry of coreEntries) {
|
||||||
|
for (const cmd of entry.commands) {
|
||||||
|
registerLazyCoreCommand(program, ctx, entry, cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function registerProgramCommands(
|
export function registerProgramCommands(
|
||||||
program: Command,
|
program: Command,
|
||||||
ctx: ProgramContext,
|
ctx: ProgramContext,
|
||||||
argv: string[] = process.argv,
|
argv: string[] = process.argv,
|
||||||
) {
|
) {
|
||||||
for (const entry of commandRegistry) {
|
registerCoreCliCommands(program, ctx, argv);
|
||||||
entry.register({ program, ctx, argv });
|
registerSubCliCommands(program, argv);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,22 @@ const ALLOWED_INVALID_GATEWAY_SUBCOMMANDS = new Set([
|
|||||||
"restart",
|
"restart",
|
||||||
]);
|
]);
|
||||||
let didRunDoctorConfigFlow = false;
|
let didRunDoctorConfigFlow = false;
|
||||||
|
let configSnapshotPromise: Promise<Awaited<ReturnType<typeof readConfigFileSnapshot>>> | null =
|
||||||
|
null;
|
||||||
|
|
||||||
function formatConfigIssues(issues: Array<{ path: string; message: string }>): string[] {
|
function formatConfigIssues(issues: Array<{ path: string; message: string }>): string[] {
|
||||||
return issues.map((issue) => `- ${issue.path || "<root>"}: ${issue.message}`);
|
return issues.map((issue) => `- ${issue.path || "<root>"}: ${issue.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getConfigSnapshot() {
|
||||||
|
// Tests often mutate config fixtures; caching can make those flaky.
|
||||||
|
if (process.env.VITEST === "true") {
|
||||||
|
return readConfigFileSnapshot();
|
||||||
|
}
|
||||||
|
configSnapshotPromise ??= readConfigFileSnapshot();
|
||||||
|
return configSnapshotPromise;
|
||||||
|
}
|
||||||
|
|
||||||
export async function ensureConfigReady(params: {
|
export async function ensureConfigReady(params: {
|
||||||
runtime: RuntimeEnv;
|
runtime: RuntimeEnv;
|
||||||
commandPath?: string[];
|
commandPath?: string[];
|
||||||
@@ -38,7 +49,7 @@ export async function ensureConfigReady(params: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const snapshot = await readConfigFileSnapshot();
|
const snapshot = await getConfigSnapshot();
|
||||||
const commandName = commandPath[0];
|
const commandName = commandPath[0];
|
||||||
const subcommandName = commandPath[1];
|
const subcommandName = commandPath[1];
|
||||||
const allowInvalid = commandName
|
const allowInvalid = commandName
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import { defaultRuntime } from "../../runtime.js";
|
|||||||
import { getCommandPath, getVerboseFlag, hasHelpOrVersion } from "../argv.js";
|
import { getCommandPath, getVerboseFlag, hasHelpOrVersion } from "../argv.js";
|
||||||
import { emitCliBanner } from "../banner.js";
|
import { emitCliBanner } from "../banner.js";
|
||||||
import { resolveCliName } from "../cli-name.js";
|
import { resolveCliName } from "../cli-name.js";
|
||||||
import { ensurePluginRegistryLoaded } from "../plugin-registry.js";
|
|
||||||
import { ensureConfigReady } from "./config-guard.js";
|
|
||||||
|
|
||||||
function setProcessTitleForCommand(actionCommand: Command) {
|
function setProcessTitleForCommand(actionCommand: Command) {
|
||||||
let current: Command = actionCommand;
|
let current: Command = actionCommand;
|
||||||
@@ -48,9 +46,11 @@ export function registerPreActionHooks(program: Command, programVersion: string)
|
|||||||
if (commandPath[0] === "doctor" || commandPath[0] === "completion") {
|
if (commandPath[0] === "doctor" || commandPath[0] === "completion") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const { ensureConfigReady } = await import("./config-guard.js");
|
||||||
await ensureConfigReady({ runtime: defaultRuntime, commandPath });
|
await ensureConfigReady({ runtime: defaultRuntime, commandPath });
|
||||||
// Load plugins for commands that need channel access
|
// Load plugins for commands that need channel access
|
||||||
if (PLUGIN_REQUIRED_COMMANDS.has(commandPath[0])) {
|
if (PLUGIN_REQUIRED_COMMANDS.has(commandPath[0])) {
|
||||||
|
const { ensurePluginRegistryLoaded } = await import("../plugin-registry.js");
|
||||||
ensurePluginRegistryLoaded();
|
ensurePluginRegistryLoaded();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import type { Command } from "commander";
|
||||||
|
import type { ProgramContext } from "./context.js";
|
||||||
|
|
||||||
|
const PROGRAM_CONTEXT_SYMBOL: unique symbol = Symbol.for("openclaw.cli.programContext");
|
||||||
|
|
||||||
|
export function setProgramContext(program: Command, ctx: ProgramContext): void {
|
||||||
|
(program as Command & { [PROGRAM_CONTEXT_SYMBOL]?: ProgramContext })[PROGRAM_CONTEXT_SYMBOL] =
|
||||||
|
ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProgramContext(program: Command): ProgramContext | undefined {
|
||||||
|
return (program as Command & { [PROGRAM_CONTEXT_SYMBOL]?: ProgramContext })[
|
||||||
|
PROGRAM_CONTEXT_SYMBOL
|
||||||
|
];
|
||||||
|
}
|
||||||
+9
-2
@@ -93,9 +93,16 @@ export async function runCli(argv: string[] = process.argv) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const parseArgv = rewriteUpdateFlagArgv(normalizedArgv);
|
const parseArgv = rewriteUpdateFlagArgv(normalizedArgv);
|
||||||
// Register the primary subcommand if one exists (for lazy-loading)
|
// Register the primary command (builtin or subcli) so help and command parsing
|
||||||
|
// are correct even with lazy command registration.
|
||||||
const primary = getPrimaryCommand(parseArgv);
|
const primary = getPrimaryCommand(parseArgv);
|
||||||
if (primary && shouldRegisterPrimarySubcommand(parseArgv)) {
|
if (primary) {
|
||||||
|
const { getProgramContext } = await import("./program/program-context.js");
|
||||||
|
const ctx = getProgramContext(program);
|
||||||
|
if (ctx) {
|
||||||
|
const { registerCoreCliByName } = await import("./program/command-registry.js");
|
||||||
|
await registerCoreCliByName(program, ctx, primary, parseArgv);
|
||||||
|
}
|
||||||
const { registerSubCliByName } = await import("./program/register.subclis.js");
|
const { registerSubCliByName } = await import("./program/register.subclis.js");
|
||||||
await registerSubCliByName(program, primary);
|
await registerSubCliByName(program, primary);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user