mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 04:29:36 +00:00
refactor(cli): extract shared command-removal and timeout action helpers
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import type { Command } from "commander";
|
import type { Command } from "commander";
|
||||||
import { getPrimaryCommand, hasHelpOrVersion } from "../argv.js";
|
import { getPrimaryCommand, hasHelpOrVersion } from "../argv.js";
|
||||||
import { reparseProgramFromActionArgs } from "./action-reparse.js";
|
import { reparseProgramFromActionArgs } from "./action-reparse.js";
|
||||||
|
import { removeCommandByName } from "./command-tree.js";
|
||||||
import type { ProgramContext } from "./context.js";
|
import type { ProgramContext } from "./context.js";
|
||||||
import { registerSubCliCommands } from "./register.subclis.js";
|
import { registerSubCliCommands } from "./register.subclis.js";
|
||||||
|
|
||||||
@@ -229,22 +230,11 @@ export function getCoreCliCommandsWithSubcommands(): string[] {
|
|||||||
return collectCoreCliCommandNames((command) => command.hasSubcommands);
|
return collectCoreCliCommandNames((command) => command.hasSubcommands);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 removeEntryCommands(program: Command, entry: CoreCliEntry) {
|
function removeEntryCommands(program: Command, entry: CoreCliEntry) {
|
||||||
// Some registrars install multiple top-level commands (e.g. status/health/sessions).
|
// 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.
|
// Remove placeholders/old registrations for all names in the entry before re-registering.
|
||||||
for (const cmd of entry.commands) {
|
for (const cmd of entry.commands) {
|
||||||
const existing = program.commands.find((c) => c.name() === cmd.name);
|
removeCommandByName(program, cmd.name);
|
||||||
if (existing) {
|
|
||||||
removeCommand(program, existing);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
39
src/cli/program/command-tree.test.ts
Normal file
39
src/cli/program/command-tree.test.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { Command } from "commander";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { removeCommand, removeCommandByName } from "./command-tree.js";
|
||||||
|
|
||||||
|
describe("command-tree", () => {
|
||||||
|
it("removes a command instance when present", () => {
|
||||||
|
const program = new Command();
|
||||||
|
const alpha = program.command("alpha");
|
||||||
|
program.command("beta");
|
||||||
|
|
||||||
|
expect(removeCommand(program, alpha)).toBe(true);
|
||||||
|
expect(program.commands.map((command) => command.name())).toEqual(["beta"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns false when command instance is already absent", () => {
|
||||||
|
const program = new Command();
|
||||||
|
program.command("alpha");
|
||||||
|
const detached = new Command("beta");
|
||||||
|
|
||||||
|
expect(removeCommand(program, detached)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes by command name", () => {
|
||||||
|
const program = new Command();
|
||||||
|
program.command("alpha");
|
||||||
|
program.command("beta");
|
||||||
|
|
||||||
|
expect(removeCommandByName(program, "alpha")).toBe(true);
|
||||||
|
expect(program.commands.map((command) => command.name())).toEqual(["beta"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns false when name does not exist", () => {
|
||||||
|
const program = new Command();
|
||||||
|
program.command("alpha");
|
||||||
|
|
||||||
|
expect(removeCommandByName(program, "missing")).toBe(false);
|
||||||
|
expect(program.commands.map((command) => command.name())).toEqual(["alpha"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
19
src/cli/program/command-tree.ts
Normal file
19
src/cli/program/command-tree.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import type { Command } from "commander";
|
||||||
|
|
||||||
|
export function removeCommand(program: Command, command: Command): boolean {
|
||||||
|
const commands = program.commands as Command[];
|
||||||
|
const index = commands.indexOf(command);
|
||||||
|
if (index < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
commands.splice(index, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeCommandByName(program: Command, name: string): boolean {
|
||||||
|
const existing = program.commands.find((command) => command.name() === name);
|
||||||
|
if (!existing) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return removeCommand(program, existing);
|
||||||
|
}
|
||||||
@@ -24,6 +24,21 @@ function parseTimeoutMs(timeout: unknown): number | null | undefined {
|
|||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function runWithVerboseAndTimeout(
|
||||||
|
opts: { verbose?: boolean; debug?: boolean; timeout?: unknown },
|
||||||
|
action: (params: { verbose: boolean; timeoutMs: number | undefined }) => Promise<void>,
|
||||||
|
): Promise<void> {
|
||||||
|
const verbose = resolveVerbose(opts);
|
||||||
|
setVerbose(verbose);
|
||||||
|
const timeoutMs = parseTimeoutMs(opts.timeout);
|
||||||
|
if (timeoutMs === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||||
|
await action({ verbose, timeoutMs });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function registerStatusHealthSessionsCommands(program: Command) {
|
export function registerStatusHealthSessionsCommands(program: Command) {
|
||||||
program
|
program
|
||||||
.command("status")
|
.command("status")
|
||||||
@@ -56,20 +71,14 @@ export function registerStatusHealthSessionsCommands(program: Command) {
|
|||||||
`\n${theme.muted("Docs:")} ${formatDocsLink("/cli/status", "docs.openclaw.ai/cli/status")}\n`,
|
`\n${theme.muted("Docs:")} ${formatDocsLink("/cli/status", "docs.openclaw.ai/cli/status")}\n`,
|
||||||
)
|
)
|
||||||
.action(async (opts) => {
|
.action(async (opts) => {
|
||||||
const verbose = resolveVerbose(opts);
|
await runWithVerboseAndTimeout(opts, async ({ verbose, timeoutMs }) => {
|
||||||
setVerbose(verbose);
|
|
||||||
const timeout = parseTimeoutMs(opts.timeout);
|
|
||||||
if (timeout === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
|
||||||
await statusCommand(
|
await statusCommand(
|
||||||
{
|
{
|
||||||
json: Boolean(opts.json),
|
json: Boolean(opts.json),
|
||||||
all: Boolean(opts.all),
|
all: Boolean(opts.all),
|
||||||
deep: Boolean(opts.deep),
|
deep: Boolean(opts.deep),
|
||||||
usage: Boolean(opts.usage),
|
usage: Boolean(opts.usage),
|
||||||
timeoutMs: timeout,
|
timeoutMs,
|
||||||
verbose,
|
verbose,
|
||||||
},
|
},
|
||||||
defaultRuntime,
|
defaultRuntime,
|
||||||
@@ -90,17 +99,11 @@ export function registerStatusHealthSessionsCommands(program: Command) {
|
|||||||
`\n${theme.muted("Docs:")} ${formatDocsLink("/cli/health", "docs.openclaw.ai/cli/health")}\n`,
|
`\n${theme.muted("Docs:")} ${formatDocsLink("/cli/health", "docs.openclaw.ai/cli/health")}\n`,
|
||||||
)
|
)
|
||||||
.action(async (opts) => {
|
.action(async (opts) => {
|
||||||
const verbose = resolveVerbose(opts);
|
await runWithVerboseAndTimeout(opts, async ({ verbose, timeoutMs }) => {
|
||||||
setVerbose(verbose);
|
|
||||||
const timeout = parseTimeoutMs(opts.timeout);
|
|
||||||
if (timeout === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
|
||||||
await healthCommand(
|
await healthCommand(
|
||||||
{
|
{
|
||||||
json: Boolean(opts.json),
|
json: Boolean(opts.json),
|
||||||
timeoutMs: timeout,
|
timeoutMs,
|
||||||
verbose,
|
verbose,
|
||||||
},
|
},
|
||||||
defaultRuntime,
|
defaultRuntime,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { OpenClawConfig } from "../../config/config.js";
|
|||||||
import { isTruthyEnvValue } from "../../infra/env.js";
|
import { isTruthyEnvValue } from "../../infra/env.js";
|
||||||
import { getPrimaryCommand, hasHelpOrVersion } from "../argv.js";
|
import { getPrimaryCommand, hasHelpOrVersion } from "../argv.js";
|
||||||
import { reparseProgramFromActionArgs } from "./action-reparse.js";
|
import { reparseProgramFromActionArgs } from "./action-reparse.js";
|
||||||
|
import { removeCommand, removeCommandByName } from "./command-tree.js";
|
||||||
|
|
||||||
type SubCliRegistrar = (program: Command) => Promise<void> | void;
|
type SubCliRegistrar = (program: Command) => Promise<void> | void;
|
||||||
|
|
||||||
@@ -296,23 +297,12 @@ export function getSubCliCommandsWithSubcommands(): string[] {
|
|||||||
return entries.filter((entry) => entry.hasSubcommands).map((entry) => entry.name);
|
return entries.filter((entry) => entry.hasSubcommands).map((entry) => entry.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeCommand(program: Command, command: Command) {
|
|
||||||
const commands = program.commands as Command[];
|
|
||||||
const index = commands.indexOf(command);
|
|
||||||
if (index >= 0) {
|
|
||||||
commands.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function registerSubCliByName(program: Command, name: string): Promise<boolean> {
|
export async function registerSubCliByName(program: Command, name: string): Promise<boolean> {
|
||||||
const entry = entries.find((candidate) => candidate.name === name);
|
const entry = entries.find((candidate) => candidate.name === name);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const existing = program.commands.find((cmd) => cmd.name() === entry.name);
|
removeCommandByName(program, entry.name);
|
||||||
if (existing) {
|
|
||||||
removeCommand(program, existing);
|
|
||||||
}
|
|
||||||
await entry.register(program);
|
await entry.register(program);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user