perf(cli): speed up startup
This commit is contained in:
@@ -1,15 +1,7 @@
|
||||
import type { Command } from "commander";
|
||||
import type { ProgramContext } from "./context.js";
|
||||
import { registerBrowserCli } from "../browser-cli.js";
|
||||
import { registerConfigCli } from "../config-cli.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 { buildParseArgv, getPrimaryCommand, hasHelpOrVersion } from "../argv.js";
|
||||
import { resolveActionArgs } from "./helpers.js";
|
||||
import { registerSubCliCommands } from "./register.subclis.js";
|
||||
|
||||
type CommandRegisterParams = {
|
||||
@@ -23,60 +15,198 @@ export type CommandRegistration = {
|
||||
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",
|
||||
register: ({ program }) => registerSetupCommand(program),
|
||||
commands: [{ name: "setup", description: "Setup helpers" }],
|
||||
register: async ({ program }) => {
|
||||
const mod = await import("./register.setup.js");
|
||||
mod.registerSetupCommand(program);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "onboard",
|
||||
register: ({ program }) => registerOnboardCommand(program),
|
||||
commands: [{ name: "onboard", description: "Onboarding helpers" }],
|
||||
register: async ({ program }) => {
|
||||
const mod = await import("./register.onboard.js");
|
||||
mod.registerOnboardCommand(program);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "configure",
|
||||
register: ({ program }) => registerConfigureCommand(program),
|
||||
commands: [{ name: "configure", description: "Configure wizard" }],
|
||||
register: async ({ program }) => {
|
||||
const mod = await import("./register.configure.js");
|
||||
mod.registerConfigureCommand(program);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "config",
|
||||
register: ({ program }) => registerConfigCli(program),
|
||||
commands: [{ name: "config", description: "Config helpers" }],
|
||||
register: async ({ program }) => {
|
||||
const mod = await import("../config-cli.js");
|
||||
mod.registerConfigCli(program);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "maintenance",
|
||||
register: ({ program }) => registerMaintenanceCommands(program),
|
||||
commands: [{ name: "maintenance", description: "Maintenance commands" }],
|
||||
register: async ({ program }) => {
|
||||
const mod = await import("./register.maintenance.js");
|
||||
mod.registerMaintenanceCommands(program);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "message",
|
||||
register: ({ program, ctx }) => registerMessageCommands(program, ctx),
|
||||
commands: [{ name: "message", description: "Send, read, and manage messages" }],
|
||||
register: async ({ program, ctx }) => {
|
||||
const mod = await import("./register.message.js");
|
||||
mod.registerMessageCommands(program, ctx);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "memory",
|
||||
register: ({ program }) => registerMemoryCli(program),
|
||||
commands: [{ name: "memory", description: "Memory commands" }],
|
||||
register: async ({ program }) => {
|
||||
const mod = await import("../memory-cli.js");
|
||||
mod.registerMemoryCli(program);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "agent",
|
||||
register: ({ program, ctx }) =>
|
||||
registerAgentCommands(program, { agentChannelOptions: ctx.agentChannelOptions }),
|
||||
commands: [{ name: "agent", description: "Agent commands" }],
|
||||
register: async ({ program, ctx }) => {
|
||||
const mod = await import("./register.agent.js");
|
||||
mod.registerAgentCommands(program, { agentChannelOptions: ctx.agentChannelOptions });
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "subclis",
|
||||
register: ({ program, argv }) => registerSubCliCommands(program, argv),
|
||||
commands: [
|
||||
{ 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",
|
||||
register: ({ program }) => registerStatusHealthSessionsCommands(program),
|
||||
},
|
||||
{
|
||||
id: "browser",
|
||||
register: ({ program }) => registerBrowserCli(program),
|
||||
commands: [{ name: "browser", description: "Browser tools" }],
|
||||
register: async ({ program }) => {
|
||||
const mod = await import("../browser-cli.js");
|
||||
mod.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(
|
||||
program: Command,
|
||||
ctx: ProgramContext,
|
||||
argv: string[] = process.argv,
|
||||
) {
|
||||
for (const entry of commandRegistry) {
|
||||
entry.register({ program, ctx, argv });
|
||||
}
|
||||
registerCoreCliCommands(program, ctx, argv);
|
||||
registerSubCliCommands(program, argv);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user