mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-13 18:21:27 +00:00
refactor: share cli command registrar engine
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
import type { Command } from "commander";
|
||||
import { getPrimaryCommand } from "../argv.js";
|
||||
import { resolveCliArgvInvocation } from "../argv-invocation.js";
|
||||
import { shouldRegisterPrimaryCommandOnly } from "../command-registration-policy.js";
|
||||
import { removeCommandByName } from "./command-tree.js";
|
||||
import type { ProgramContext } from "./context.js";
|
||||
import {
|
||||
type CoreCliCommandDescriptor,
|
||||
getCoreCliCommandDescriptors,
|
||||
getCoreCliCommandsWithSubcommands,
|
||||
} from "./core-command-descriptors.js";
|
||||
import { registerLazyCommand } from "./register-lazy-command.js";
|
||||
import {
|
||||
registerCommandGroupByName,
|
||||
registerCommandGroups,
|
||||
type CommandGroupEntry,
|
||||
} from "./register-command-groups.js";
|
||||
import { registerSubCliCommands } from "./register.subclis.js";
|
||||
|
||||
export { getCoreCliCommandDescriptors, getCoreCliCommandsWithSubcommands };
|
||||
@@ -26,7 +29,7 @@ export type CommandRegistration = {
|
||||
|
||||
type CoreCliEntry = {
|
||||
commands: CoreCliCommandDescriptor[];
|
||||
register: (params: CommandRegisterParams) => Promise<void> | void;
|
||||
registerWithParams: (params: CommandRegisterParams) => Promise<void> | void;
|
||||
};
|
||||
|
||||
// Note for humans and agents:
|
||||
@@ -41,7 +44,7 @@ const coreEntries: CoreCliEntry[] = [
|
||||
hasSubcommands: false,
|
||||
},
|
||||
],
|
||||
register: async ({ program }) => {
|
||||
registerWithParams: async ({ program }) => {
|
||||
const mod = await import("./register.setup.js");
|
||||
mod.registerSetupCommand(program);
|
||||
},
|
||||
@@ -54,7 +57,7 @@ const coreEntries: CoreCliEntry[] = [
|
||||
hasSubcommands: false,
|
||||
},
|
||||
],
|
||||
register: async ({ program }) => {
|
||||
registerWithParams: async ({ program }) => {
|
||||
const mod = await import("./register.onboard.js");
|
||||
mod.registerOnboardCommand(program);
|
||||
},
|
||||
@@ -68,7 +71,7 @@ const coreEntries: CoreCliEntry[] = [
|
||||
hasSubcommands: false,
|
||||
},
|
||||
],
|
||||
register: async ({ program }) => {
|
||||
registerWithParams: async ({ program }) => {
|
||||
const mod = await import("./register.configure.js");
|
||||
mod.registerConfigureCommand(program);
|
||||
},
|
||||
@@ -82,7 +85,7 @@ const coreEntries: CoreCliEntry[] = [
|
||||
hasSubcommands: true,
|
||||
},
|
||||
],
|
||||
register: async ({ program }) => {
|
||||
registerWithParams: async ({ program }) => {
|
||||
const mod = await import("../config-cli.js");
|
||||
mod.registerConfigCli(program);
|
||||
},
|
||||
@@ -95,7 +98,7 @@ const coreEntries: CoreCliEntry[] = [
|
||||
hasSubcommands: true,
|
||||
},
|
||||
],
|
||||
register: async ({ program }) => {
|
||||
registerWithParams: async ({ program }) => {
|
||||
const mod = await import("./register.backup.js");
|
||||
mod.registerBackupCommand(program);
|
||||
},
|
||||
@@ -123,7 +126,7 @@ const coreEntries: CoreCliEntry[] = [
|
||||
hasSubcommands: false,
|
||||
},
|
||||
],
|
||||
register: async ({ program }) => {
|
||||
registerWithParams: async ({ program }) => {
|
||||
const mod = await import("./register.maintenance.js");
|
||||
mod.registerMaintenanceCommands(program);
|
||||
},
|
||||
@@ -136,7 +139,7 @@ const coreEntries: CoreCliEntry[] = [
|
||||
hasSubcommands: true,
|
||||
},
|
||||
],
|
||||
register: async ({ program, ctx }) => {
|
||||
registerWithParams: async ({ program, ctx }) => {
|
||||
const mod = await import("./register.message.js");
|
||||
mod.registerMessageCommands(program, ctx);
|
||||
},
|
||||
@@ -149,7 +152,7 @@ const coreEntries: CoreCliEntry[] = [
|
||||
hasSubcommands: true,
|
||||
},
|
||||
],
|
||||
register: async ({ program }) => {
|
||||
registerWithParams: async ({ program }) => {
|
||||
const mod = await import("../mcp-cli.js");
|
||||
mod.registerMcpCli(program);
|
||||
},
|
||||
@@ -167,7 +170,7 @@ const coreEntries: CoreCliEntry[] = [
|
||||
hasSubcommands: true,
|
||||
},
|
||||
],
|
||||
register: async ({ program, ctx }) => {
|
||||
registerWithParams: async ({ program, ctx }) => {
|
||||
const mod = await import("./register.agent.js");
|
||||
mod.registerAgentCommands(program, {
|
||||
agentChannelOptions: ctx.agentChannelOptions,
|
||||
@@ -197,80 +200,42 @@ const coreEntries: CoreCliEntry[] = [
|
||||
hasSubcommands: true,
|
||||
},
|
||||
],
|
||||
register: async ({ program }) => {
|
||||
registerWithParams: async ({ program }) => {
|
||||
const mod = await import("./register.status-health-sessions.js");
|
||||
mod.registerStatusHealthSessionsCommands(program);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
function resolveCoreCommandGroups(ctx: ProgramContext, argv: string[]): CommandGroupEntry[] {
|
||||
return coreEntries.map((entry) => ({
|
||||
placeholders: entry.commands,
|
||||
register: async (program) => {
|
||||
await entry.registerWithParams({ program, ctx, argv });
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
export function getCoreCliCommandNames(): string[] {
|
||||
return getCoreCliCommandDescriptors().map((command) => command.name);
|
||||
}
|
||||
|
||||
function removeEntryCommands(program: Command, entry: CoreCliEntry) {
|
||||
// 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) {
|
||||
removeCommandByName(program, cmd.name);
|
||||
}
|
||||
}
|
||||
|
||||
function registerLazyCoreCommand(
|
||||
program: Command,
|
||||
ctx: ProgramContext,
|
||||
entry: CoreCliEntry,
|
||||
command: CoreCliCommandDescriptor,
|
||||
) {
|
||||
registerLazyCommand({
|
||||
program,
|
||||
name: command.name,
|
||||
description: command.description,
|
||||
removeNames: entry.commands.map((cmd) => cmd.name),
|
||||
register: async () => {
|
||||
await entry.register({ program, ctx, argv: process.argv });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
removeEntryCommands(program, entry);
|
||||
await entry.register({ program, ctx, argv });
|
||||
return true;
|
||||
return registerCommandGroupByName(program, resolveCoreCommandGroups(ctx, argv), name);
|
||||
}
|
||||
|
||||
export function registerCoreCliCommands(program: Command, ctx: ProgramContext, argv: string[]) {
|
||||
const primary = getPrimaryCommand(argv);
|
||||
if (primary && shouldRegisterPrimaryCommandOnly(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);
|
||||
}
|
||||
}
|
||||
const { primary } = resolveCliArgvInvocation(argv);
|
||||
registerCommandGroups(program, resolveCoreCommandGroups(ctx, argv), {
|
||||
eager: false,
|
||||
primary,
|
||||
registerPrimaryOnly: Boolean(primary && shouldRegisterPrimaryCommandOnly(argv)),
|
||||
});
|
||||
}
|
||||
|
||||
export function registerProgramCommands(
|
||||
|
||||
88
src/cli/program/register-command-groups.ts
Normal file
88
src/cli/program/register-command-groups.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import type { Command } from "commander";
|
||||
import { removeCommandByName } from "./command-tree.js";
|
||||
import { registerLazyCommand } from "./register-lazy-command.js";
|
||||
|
||||
export type CommandGroupPlaceholder = {
|
||||
name: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type CommandGroupEntry = {
|
||||
placeholders: readonly CommandGroupPlaceholder[];
|
||||
register: (program: Command) => Promise<void> | void;
|
||||
};
|
||||
|
||||
function findCommandGroupEntry(
|
||||
entries: readonly CommandGroupEntry[],
|
||||
name: string,
|
||||
): CommandGroupEntry | undefined {
|
||||
return entries.find((entry) =>
|
||||
entry.placeholders.some((placeholder) => placeholder.name === name),
|
||||
);
|
||||
}
|
||||
|
||||
export async function registerCommandGroupByName(
|
||||
program: Command,
|
||||
entries: readonly CommandGroupEntry[],
|
||||
name: string,
|
||||
): Promise<boolean> {
|
||||
const entry = findCommandGroupEntry(entries, name);
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
for (const placeholder of entry.placeholders) {
|
||||
removeCommandByName(program, placeholder.name);
|
||||
}
|
||||
await entry.register(program);
|
||||
return true;
|
||||
}
|
||||
|
||||
function registerLazyCommandGroup(
|
||||
program: Command,
|
||||
entry: CommandGroupEntry,
|
||||
placeholder: CommandGroupPlaceholder,
|
||||
) {
|
||||
registerLazyCommand({
|
||||
program,
|
||||
name: placeholder.name,
|
||||
description: placeholder.description,
|
||||
removeNames: entry.placeholders.map((candidate) => candidate.name),
|
||||
register: async () => {
|
||||
await entry.register(program);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function registerCommandGroups(
|
||||
program: Command,
|
||||
entries: readonly CommandGroupEntry[],
|
||||
params: {
|
||||
eager: boolean;
|
||||
primary: string | null;
|
||||
registerPrimaryOnly: boolean;
|
||||
},
|
||||
) {
|
||||
if (params.eager) {
|
||||
for (const entry of entries) {
|
||||
void entry.register(program);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.primary && params.registerPrimaryOnly) {
|
||||
const entry = findCommandGroupEntry(entries, params.primary);
|
||||
if (entry) {
|
||||
const placeholder = entry.placeholders.find((candidate) => candidate.name === params.primary);
|
||||
if (placeholder) {
|
||||
registerLazyCommandGroup(program, entry, placeholder);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (const entry of entries) {
|
||||
for (const placeholder of entry.placeholders) {
|
||||
registerLazyCommandGroup(program, entry, placeholder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
import type { Command } from "commander";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { getPrimaryCommand } from "../argv.js";
|
||||
import { resolveCliArgvInvocation } from "../argv-invocation.js";
|
||||
import {
|
||||
shouldEagerRegisterSubcommands,
|
||||
shouldRegisterPrimarySubcommandOnly,
|
||||
} from "../command-registration-policy.js";
|
||||
import { removeCommandByName } from "./command-tree.js";
|
||||
import { registerLazyCommand as registerLazyCommandPlaceholder } from "./register-lazy-command.js";
|
||||
import {
|
||||
registerCommandGroupByName,
|
||||
registerCommandGroups,
|
||||
type CommandGroupEntry,
|
||||
} from "./register-command-groups.js";
|
||||
import {
|
||||
getSubCliCommandsWithSubcommands,
|
||||
getSubCliEntries as getSubCliEntryDescriptors,
|
||||
@@ -311,47 +314,26 @@ const entries: SubCliEntry[] = [
|
||||
},
|
||||
];
|
||||
|
||||
function resolveSubCliCommandGroups(): CommandGroupEntry[] {
|
||||
return entries.map((entry) => ({
|
||||
placeholders: [entry],
|
||||
register: entry.register,
|
||||
}));
|
||||
}
|
||||
|
||||
export function getSubCliEntries(): ReadonlyArray<SubCliDescriptor> {
|
||||
return getSubCliEntryDescriptors();
|
||||
}
|
||||
|
||||
export async function registerSubCliByName(program: Command, name: string): Promise<boolean> {
|
||||
const entry = entries.find((candidate) => candidate.name === name);
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
removeCommandByName(program, entry.name);
|
||||
await entry.register(program);
|
||||
return true;
|
||||
}
|
||||
|
||||
function registerLazyCommand(program: Command, entry: SubCliEntry) {
|
||||
registerLazyCommandPlaceholder({
|
||||
program,
|
||||
name: entry.name,
|
||||
description: entry.description,
|
||||
register: async () => {
|
||||
await entry.register(program);
|
||||
},
|
||||
});
|
||||
return registerCommandGroupByName(program, resolveSubCliCommandGroups(), name);
|
||||
}
|
||||
|
||||
export function registerSubCliCommands(program: Command, argv: string[] = process.argv) {
|
||||
if (shouldEagerRegisterSubcommands()) {
|
||||
for (const entry of entries) {
|
||||
void entry.register(program);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const primary = getPrimaryCommand(argv);
|
||||
if (primary && shouldRegisterPrimarySubcommandOnly(argv)) {
|
||||
const entry = entries.find((candidate) => candidate.name === primary);
|
||||
if (entry) {
|
||||
registerLazyCommand(program, entry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (const candidate of entries) {
|
||||
registerLazyCommand(program, candidate);
|
||||
}
|
||||
const { primary } = resolveCliArgvInvocation(argv);
|
||||
registerCommandGroups(program, resolveSubCliCommandGroups(), {
|
||||
eager: shouldEagerRegisterSubcommands(),
|
||||
primary,
|
||||
registerPrimaryOnly: Boolean(primary && shouldRegisterPrimarySubcommandOnly(argv)),
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user