mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-30 10:53:39 +00:00
perf(cli): lazy-load agents actions for help (#84483)
Lazy-load agents CLI action modules from command callbacks so agents --help avoids importing the full agents runtime. Validated by GitHub required checks and local focused CLI gates.
This commit is contained in:
@@ -36,17 +36,29 @@ vi.mock("../../commands/agent-via-gateway.js", () => ({
|
||||
agentCliCommand: mocks.agentCliCommandMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../commands/agents.js", () => ({
|
||||
vi.mock("../../commands/agents.commands.add.js", () => ({
|
||||
agentsAddCommand: mocks.agentsAddCommandMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../commands/agents.commands.bind.js", () => ({
|
||||
agentsBindingsCommand: mocks.agentsBindingsCommandMock,
|
||||
agentsBindCommand: mocks.agentsBindCommandMock,
|
||||
agentsDeleteCommand: mocks.agentsDeleteCommandMock,
|
||||
agentsListCommand: mocks.agentsListCommandMock,
|
||||
agentsSetIdentityCommand: mocks.agentsSetIdentityCommandMock,
|
||||
agentsUnbindCommand: mocks.agentsUnbindCommandMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../globals.js", () => ({
|
||||
vi.mock("../../commands/agents.commands.delete.js", () => ({
|
||||
agentsDeleteCommand: mocks.agentsDeleteCommandMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../commands/agents.commands.identity.js", () => ({
|
||||
agentsSetIdentityCommand: mocks.agentsSetIdentityCommandMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../commands/agents.commands.list.js", () => ({
|
||||
agentsListCommand: mocks.agentsListCommandMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../global-state.js", () => ({
|
||||
setVerbose: mocks.setVerboseMock,
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,26 +1,68 @@
|
||||
import type { Command } from "commander";
|
||||
import { agentCliCommand } from "../../commands/agent-via-gateway.js";
|
||||
import {
|
||||
agentsAddCommand,
|
||||
agentsBindingsCommand,
|
||||
agentsBindCommand,
|
||||
agentsDeleteCommand,
|
||||
agentsListCommand,
|
||||
agentsSetIdentityCommand,
|
||||
agentsUnbindCommand,
|
||||
} from "../../commands/agents.js";
|
||||
import { setVerbose } from "../../globals.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js";
|
||||
import { formatDocsLink } from "../../terminal/links.js";
|
||||
import { theme } from "../../terminal/theme.js";
|
||||
import { runCommandWithRuntime } from "../cli-utils.js";
|
||||
import { hasExplicitOptions } from "../command-options.js";
|
||||
import { createDefaultDeps } from "../deps.js";
|
||||
import { formatHelpExamples } from "../help-format.js";
|
||||
import { collectOption } from "./helpers.js";
|
||||
|
||||
export function registerAgentCommands(program: Command, args: { agentChannelOptions: string }) {
|
||||
type AgentViaGatewayModule = typeof import("../../commands/agent-via-gateway.js");
|
||||
type AgentsAddModule = typeof import("../../commands/agents.commands.add.js");
|
||||
type AgentsBindModule = typeof import("../../commands/agents.commands.bind.js");
|
||||
type AgentsDeleteModule = typeof import("../../commands/agents.commands.delete.js");
|
||||
type AgentsIdentityModule = typeof import("../../commands/agents.commands.identity.js");
|
||||
type AgentsListModule = typeof import("../../commands/agents.commands.list.js");
|
||||
type CliDepsModule = typeof import("../deps.js");
|
||||
type GlobalStateModule = typeof import("../../global-state.js");
|
||||
|
||||
async function loadAgentCliCommand(): Promise<AgentViaGatewayModule["agentCliCommand"]> {
|
||||
return (await import("../../commands/agent-via-gateway.js")).agentCliCommand;
|
||||
}
|
||||
|
||||
async function loadAgentsAddCommand(): Promise<AgentsAddModule["agentsAddCommand"]> {
|
||||
return (await import("../../commands/agents.commands.add.js")).agentsAddCommand;
|
||||
}
|
||||
|
||||
async function loadAgentsBindCommand(): Promise<AgentsBindModule["agentsBindCommand"]> {
|
||||
return (await import("../../commands/agents.commands.bind.js")).agentsBindCommand;
|
||||
}
|
||||
|
||||
async function loadAgentsBindingsCommand(): Promise<AgentsBindModule["agentsBindingsCommand"]> {
|
||||
return (await import("../../commands/agents.commands.bind.js")).agentsBindingsCommand;
|
||||
}
|
||||
|
||||
async function loadAgentsUnbindCommand(): Promise<AgentsBindModule["agentsUnbindCommand"]> {
|
||||
return (await import("../../commands/agents.commands.bind.js")).agentsUnbindCommand;
|
||||
}
|
||||
|
||||
async function loadAgentsDeleteCommand(): Promise<AgentsDeleteModule["agentsDeleteCommand"]> {
|
||||
return (await import("../../commands/agents.commands.delete.js")).agentsDeleteCommand;
|
||||
}
|
||||
|
||||
async function loadAgentsSetIdentityCommand(): Promise<
|
||||
AgentsIdentityModule["agentsSetIdentityCommand"]
|
||||
> {
|
||||
return (await import("../../commands/agents.commands.identity.js")).agentsSetIdentityCommand;
|
||||
}
|
||||
|
||||
async function loadAgentsListCommand(): Promise<AgentsListModule["agentsListCommand"]> {
|
||||
return (await import("../../commands/agents.commands.list.js")).agentsListCommand;
|
||||
}
|
||||
|
||||
async function loadCreateDefaultDeps(): Promise<CliDepsModule["createDefaultDeps"]> {
|
||||
return (await import("../deps.js")).createDefaultDeps;
|
||||
}
|
||||
|
||||
async function loadSetVerbose(): Promise<GlobalStateModule["setVerbose"]> {
|
||||
return (await import("../../global-state.js")).setVerbose;
|
||||
}
|
||||
|
||||
export function registerAgentCommands(
|
||||
program: Command,
|
||||
args: { agentChannelOptions: string },
|
||||
): void {
|
||||
program
|
||||
.command("agent")
|
||||
.description("Run an agent turn via the Gateway (use --local for embedded)")
|
||||
@@ -77,13 +119,16 @@ ${formatHelpExamples([
|
||||
|
||||
${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.openclaw.ai/cli/agent")}`,
|
||||
)
|
||||
.action(async (opts) => {
|
||||
.action(async (opts): Promise<void> => {
|
||||
const verboseLevel =
|
||||
typeof opts.verbose === "string" ? normalizeLowercaseStringOrEmpty(opts.verbose) : "";
|
||||
setVerbose(verboseLevel === "on");
|
||||
// Build default deps (keeps parity with other commands; future-proofing).
|
||||
const deps = createDefaultDeps();
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
const setVerbose = await loadSetVerbose();
|
||||
setVerbose(verboseLevel === "on");
|
||||
// Build default deps (keeps parity with other commands; future-proofing).
|
||||
const createDefaultDeps = await loadCreateDefaultDeps();
|
||||
const deps = createDefaultDeps();
|
||||
const agentCliCommand = await loadAgentCliCommand();
|
||||
await agentCliCommand(opts, defaultRuntime, deps);
|
||||
});
|
||||
});
|
||||
@@ -102,8 +147,9 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.openclaw.ai/cli/age
|
||||
.description("List configured agents")
|
||||
.option("--json", "Output JSON instead of text", false)
|
||||
.option("--bindings", "Include routing bindings", false)
|
||||
.action(async (opts) => {
|
||||
.action(async (opts): Promise<void> => {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
const agentsListCommand = await loadAgentsListCommand();
|
||||
await agentsListCommand(
|
||||
{ json: Boolean(opts.json), bindings: Boolean(opts.bindings) },
|
||||
defaultRuntime,
|
||||
@@ -116,8 +162,9 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.openclaw.ai/cli/age
|
||||
.description("List routing bindings")
|
||||
.option("--agent <id>", "Filter by agent id")
|
||||
.option("--json", "Output JSON instead of text", false)
|
||||
.action(async (opts) => {
|
||||
.action(async (opts): Promise<void> => {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
const agentsBindingsCommand = await loadAgentsBindingsCommand();
|
||||
await agentsBindingsCommand(
|
||||
{
|
||||
agent: opts.agent as string | undefined,
|
||||
@@ -139,8 +186,9 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.openclaw.ai/cli/age
|
||||
[],
|
||||
)
|
||||
.option("--json", "Output JSON summary", false)
|
||||
.action(async (opts) => {
|
||||
.action(async (opts): Promise<void> => {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
const agentsBindCommand = await loadAgentsBindCommand();
|
||||
await agentsBindCommand(
|
||||
{
|
||||
agent: opts.agent as string | undefined,
|
||||
@@ -159,8 +207,9 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.openclaw.ai/cli/age
|
||||
.option("--bind <channel[:accountId]>", "Binding to remove (repeatable)", collectOption, [])
|
||||
.option("--all", "Remove all bindings for this agent", false)
|
||||
.option("--json", "Output JSON summary", false)
|
||||
.action(async (opts) => {
|
||||
.action(async (opts): Promise<void> => {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
const agentsUnbindCommand = await loadAgentsUnbindCommand();
|
||||
await agentsUnbindCommand(
|
||||
{
|
||||
agent: opts.agent as string | undefined,
|
||||
@@ -182,7 +231,7 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.openclaw.ai/cli/age
|
||||
.option("--bind <channel[:accountId]>", "Route channel binding (repeatable)", collectOption, [])
|
||||
.option("--non-interactive", "Disable prompts; requires --workspace", false)
|
||||
.option("--json", "Output JSON summary", false)
|
||||
.action(async (name, opts, command) => {
|
||||
.action(async (name, opts, command): Promise<void> => {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
const hasFlags = hasExplicitOptions(command, [
|
||||
"workspace",
|
||||
@@ -191,6 +240,7 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.openclaw.ai/cli/age
|
||||
"bind",
|
||||
"nonInteractive",
|
||||
]);
|
||||
const agentsAddCommand = await loadAgentsAddCommand();
|
||||
await agentsAddCommand(
|
||||
{
|
||||
name: typeof name === "string" ? name : undefined,
|
||||
@@ -238,8 +288,9 @@ ${formatHelpExamples([
|
||||
])}
|
||||
`,
|
||||
)
|
||||
.action(async (opts) => {
|
||||
.action(async (opts): Promise<void> => {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
const agentsSetIdentityCommand = await loadAgentsSetIdentityCommand();
|
||||
await agentsSetIdentityCommand(
|
||||
{
|
||||
agent: opts.agent as string | undefined,
|
||||
@@ -262,8 +313,9 @@ ${formatHelpExamples([
|
||||
.description("Delete an agent and prune workspace/state")
|
||||
.option("--force", "Skip confirmation", false)
|
||||
.option("--json", "Output JSON summary", false)
|
||||
.action(async (id, opts) => {
|
||||
.action(async (id, opts): Promise<void> => {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
const agentsDeleteCommand = await loadAgentsDeleteCommand();
|
||||
await agentsDeleteCommand(
|
||||
{
|
||||
id: String(id),
|
||||
@@ -275,8 +327,9 @@ ${formatHelpExamples([
|
||||
});
|
||||
});
|
||||
|
||||
agents.action(async () => {
|
||||
agents.action(async (): Promise<void> => {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
const agentsListCommand = await loadAgentsListCommand();
|
||||
await agentsListCommand({}, defaultRuntime);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
type RouteArgParser<TArgs> = (argv: string[]) => TArgs | null;
|
||||
|
||||
type ParsedRouteArgs<TParse extends RouteArgParser<unknown>> = Exclude<ReturnType<TParse>, null>;
|
||||
type AgentsListCommandModule = typeof import("../../commands/agents.commands.list.js");
|
||||
type ConfigCliModule = typeof import("../config-cli.js");
|
||||
type ModelsListCommandModule = typeof import("../../commands/models/list.list-command.js");
|
||||
type ModelsStatusCommandModule = typeof import("../../commands/models/list.status-command.js");
|
||||
@@ -41,6 +42,9 @@ function defineRoutedCommand<TParse extends RouteArgParser<unknown>>(
|
||||
}
|
||||
|
||||
const configCliLoader = createLazyImportLoader<ConfigCliModule>(() => import("../config-cli.js"));
|
||||
const agentsListCommandLoader = createLazyImportLoader<AgentsListCommandModule>(
|
||||
() => import("../../commands/agents.commands.list.js"),
|
||||
);
|
||||
const modelsListCommandLoader = createLazyImportLoader<ModelsListCommandModule>(
|
||||
() => import("../../commands/models/list.list-command.js"),
|
||||
);
|
||||
@@ -52,6 +56,10 @@ function loadConfigCli(): Promise<ConfigCliModule> {
|
||||
return configCliLoader.load();
|
||||
}
|
||||
|
||||
function loadAgentsListCommand(): Promise<AgentsListCommandModule> {
|
||||
return agentsListCommandLoader.load();
|
||||
}
|
||||
|
||||
function loadModelsListCommand(): Promise<ModelsListCommandModule> {
|
||||
return modelsListCommandLoader.load();
|
||||
}
|
||||
@@ -105,7 +113,7 @@ export const routedCommandDefinitions = {
|
||||
"agents-list": defineRoutedCommand({
|
||||
parseArgs: parseAgentsListRouteArgs,
|
||||
runParsedArgs: async (args) => {
|
||||
const { agentsListCommand } = await import("../../commands/agents.js");
|
||||
const { agentsListCommand } = await loadAgentsListCommand();
|
||||
await agentsListCommand(args, defaultRuntime);
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -53,7 +53,7 @@ vi.mock("../../commands/channels/status.js", () => ({
|
||||
channelsStatusCommand: channelsStatusCommandMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../commands/agents.js", () => ({
|
||||
vi.mock("../../commands/agents.commands.list.js", () => ({
|
||||
agentsListCommand: agentsListCommandMock,
|
||||
}));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user