Fix CLI text command hangs (#74220)

* fix(cli): keep agents list off plugin preload

* docs(changelog): note cli text hang fix

* test(cli): update preaction agents list expectations
This commit is contained in:
NianJiu
2026-04-30 14:36:24 +08:00
committed by GitHub
parent c4a4c189f1
commit 43ca7399e5
8 changed files with 73 additions and 20 deletions

View File

@@ -201,7 +201,7 @@ describe("registerPreActionHooks", () => {
await preActionHook(program, actionCommand);
}
it("handles debug mode and plugin-required command preaction", async () => {
it("handles debug mode and config-only command preaction", async () => {
const processTitleSetSpy = vi.spyOn(process, "title", "set");
await runPreAction({
parseArgv: ["status"],
@@ -229,7 +229,7 @@ describe("registerPreActionHooks", () => {
runtime: runtimeMock,
commandPath: ["agents", "list"],
});
expect(ensurePluginRegistryLoadedMock).toHaveBeenCalledWith({ scope: "all" });
expect(ensurePluginRegistryLoadedMock).not.toHaveBeenCalled();
processTitleSetSpy.mockRestore();
});
@@ -512,19 +512,13 @@ describe("registerPreActionHooks", () => {
expect(loggingState.forceConsoleToStderr).toBe(false);
});
it("does not route logs to stderr during plugin loading without --json", async () => {
let stderrDuringPluginLoad = false;
ensurePluginRegistryLoadedMock.mockImplementation(() => {
stderrDuringPluginLoad = loggingState.forceConsoleToStderr;
});
it("does not preload plugins or route logs to stderr for agents list without --json", async () => {
await runPreAction({
parseArgv: ["agents", "list"],
processArgv: ["node", "openclaw", "agents", "list"],
});
expect(ensurePluginRegistryLoadedMock).toHaveBeenCalled();
expect(stderrDuringPluginLoad).toBe(false);
expect(ensurePluginRegistryLoadedMock).not.toHaveBeenCalled();
expect(loggingState.forceConsoleToStderr).toBe(false);
});

View File

@@ -111,6 +111,10 @@ describe("route-args", () => {
json: true,
bindings: true,
});
expect(parseAgentsListRouteArgs(["node", "openclaw", "agents"])).toEqual({
json: false,
bindings: false,
});
});
it("parses config routes", () => {

View File

@@ -11,6 +11,7 @@ const tasksListJsonCommandMock = vi.hoisted(() => vi.fn(async () => {}));
const tasksAuditJsonCommandMock = vi.hoisted(() => vi.fn(async () => {}));
const channelsListCommandMock = vi.hoisted(() => vi.fn(async () => {}));
const channelsStatusCommandMock = vi.hoisted(() => vi.fn(async () => {}));
const agentsListCommandMock = vi.hoisted(() => vi.fn(async () => {}));
vi.mock("../config-cli.js", () => ({
runConfigGet: runConfigGetMock,
@@ -49,6 +50,10 @@ vi.mock("../../commands/channels/status.js", () => ({
channelsStatusCommand: channelsStatusCommandMock,
}));
vi.mock("../../commands/agents.js", () => ({
agentsListCommand: agentsListCommandMock,
}));
describe("program routes", () => {
beforeEach(() => {
vi.clearAllMocks();
@@ -80,6 +85,34 @@ describe("program routes", () => {
expect(expectRoute(["channels", "status"])?.loadPlugins).toBeUndefined();
});
it("matches agents read-only routes without plugin preload", () => {
expect(expectRoute(["agents"])?.loadPlugins).toBeUndefined();
expect(expectRoute(["agents", "list"])?.loadPlugins).toBeUndefined();
});
it("passes parsed agents list flags through", async () => {
await expect(expectRoute(["agents"])?.run(["node", "openclaw", "agents"])).resolves.toBe(true);
expect(agentsListCommandMock).toHaveBeenCalledWith(
{ json: false, bindings: false },
expect.any(Object),
);
await expect(
expectRoute(["agents", "list"])?.run([
"node",
"openclaw",
"agents",
"list",
"--json",
"--bindings",
]),
).resolves.toBe(true);
expect(agentsListCommandMock).toHaveBeenLastCalledWith(
{ json: true, bindings: true },
expect.any(Object),
);
});
it("passes parsed channel read-only route flags through", async () => {
const listRoute = expectRoute(["channels", "list"]);
await expect(