mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 06:52:53 +00:00
306 lines
10 KiB
TypeScript
306 lines
10 KiB
TypeScript
import { Command } from "commander";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { registerAgentCommands } from "./register.agent.js";
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
agentCliCommandMock: vi.fn(),
|
|
agentsAddCommandMock: vi.fn(),
|
|
agentsBindingsCommandMock: vi.fn(),
|
|
agentsBindCommandMock: vi.fn(),
|
|
agentsDeleteCommandMock: vi.fn(),
|
|
agentsListCommandMock: vi.fn(),
|
|
agentsSetIdentityCommandMock: vi.fn(),
|
|
agentsUnbindCommandMock: vi.fn(),
|
|
setVerboseMock: vi.fn(),
|
|
runtime: {
|
|
log: vi.fn(),
|
|
error: vi.fn(),
|
|
exit: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
const agentCliCommandMock = mocks.agentCliCommandMock;
|
|
const agentsAddCommandMock = mocks.agentsAddCommandMock;
|
|
const agentsBindingsCommandMock = mocks.agentsBindingsCommandMock;
|
|
const agentsBindCommandMock = mocks.agentsBindCommandMock;
|
|
const agentsDeleteCommandMock = mocks.agentsDeleteCommandMock;
|
|
const agentsListCommandMock = mocks.agentsListCommandMock;
|
|
const agentsSetIdentityCommandMock = mocks.agentsSetIdentityCommandMock;
|
|
const agentsUnbindCommandMock = mocks.agentsUnbindCommandMock;
|
|
const setVerboseMock = mocks.setVerboseMock;
|
|
const runtime = mocks.runtime;
|
|
|
|
vi.mock("../../commands/agent-via-gateway.js", () => ({
|
|
agentCliCommand: mocks.agentCliCommandMock,
|
|
}));
|
|
|
|
vi.mock("../../commands/agents.commands.add.js", () => ({
|
|
agentsAddCommand: mocks.agentsAddCommandMock,
|
|
}));
|
|
|
|
vi.mock("../../commands/agents.commands.bind.js", () => ({
|
|
agentsBindingsCommand: mocks.agentsBindingsCommandMock,
|
|
agentsBindCommand: mocks.agentsBindCommandMock,
|
|
agentsUnbindCommand: mocks.agentsUnbindCommandMock,
|
|
}));
|
|
|
|
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,
|
|
}));
|
|
|
|
vi.mock("../../runtime.js", () => ({
|
|
defaultRuntime: mocks.runtime,
|
|
}));
|
|
|
|
describe("registerAgentCommands", () => {
|
|
async function runCli(args: string[]) {
|
|
const program = new Command();
|
|
registerAgentCommands(program, { agentChannelOptions: "last|telegram|discord" });
|
|
await program.parseAsync(args, { from: "user" });
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
runtime.exit.mockImplementation(() => {});
|
|
agentCliCommandMock.mockResolvedValue(undefined);
|
|
agentsAddCommandMock.mockResolvedValue(undefined);
|
|
agentsBindingsCommandMock.mockResolvedValue(undefined);
|
|
agentsBindCommandMock.mockResolvedValue(undefined);
|
|
agentsDeleteCommandMock.mockResolvedValue(undefined);
|
|
agentsListCommandMock.mockResolvedValue(undefined);
|
|
agentsSetIdentityCommandMock.mockResolvedValue(undefined);
|
|
agentsUnbindCommandMock.mockResolvedValue(undefined);
|
|
});
|
|
|
|
function commandCall(mock: { mock: { calls: unknown[][] } }, index = 0): unknown[] {
|
|
const call = mock.mock.calls[index];
|
|
if (!call) {
|
|
throw new Error(`expected command call ${index + 1}`);
|
|
}
|
|
return call;
|
|
}
|
|
|
|
it("runs agent command with verbose enabled for --verbose on", async () => {
|
|
await runCli(["agent", "--message", "hi", "--verbose", "ON", "--json"]);
|
|
|
|
expect(setVerboseMock).toHaveBeenCalledWith(true);
|
|
const [options, callRuntime, deps] = commandCall(agentCliCommandMock);
|
|
expect((options as { message?: string }).message).toBe("hi");
|
|
expect((options as { verbose?: string }).verbose).toBe("ON");
|
|
expect((options as { json?: boolean }).json).toBe(true);
|
|
expect(callRuntime).toBe(runtime);
|
|
expect(deps).toBeUndefined();
|
|
});
|
|
|
|
it("runs agent command with verbose disabled for --verbose off", async () => {
|
|
await runCli(["agent", "--message", "hi", "--verbose", "off"]);
|
|
|
|
expect(setVerboseMock).toHaveBeenCalledWith(false);
|
|
const [options, callRuntime, deps] = commandCall(agentCliCommandMock);
|
|
expect((options as { message?: string }).message).toBe("hi");
|
|
expect((options as { verbose?: string }).verbose).toBe("off");
|
|
expect(callRuntime).toBe(runtime);
|
|
expect(deps).toBeUndefined();
|
|
});
|
|
|
|
it("accepts a model override for one-shot agent runs", async () => {
|
|
await runCli(["agent", "--message", "hi", "--agent", "ops", "--model", "openai/gpt-5.4"]);
|
|
|
|
const [options, callRuntime, deps] = commandCall(agentCliCommandMock);
|
|
expect((options as { message?: string }).message).toBe("hi");
|
|
expect((options as { agent?: string }).agent).toBe("ops");
|
|
expect((options as { model?: string }).model).toBe("openai/gpt-5.4");
|
|
expect(callRuntime).toBe(runtime);
|
|
expect(deps).toBeUndefined();
|
|
});
|
|
|
|
it("forwards an explicit session key to the agent command", async () => {
|
|
await runCli(["agent", "--message", "hi", "--session-key", "agent:ops:incident-42"]);
|
|
|
|
const [options, callRuntime, deps] = commandCall(agentCliCommandMock);
|
|
expect((options as { message?: string }).message).toBe("hi");
|
|
expect((options as { sessionKey?: string }).sessionKey).toBe("agent:ops:incident-42");
|
|
expect(callRuntime).toBe(runtime);
|
|
expect(deps).toBeUndefined();
|
|
});
|
|
|
|
it("runs agents add and computes hasFlags based on explicit options", async () => {
|
|
await runCli(["agents", "add", "alpha"]);
|
|
const [alphaOptions, alphaRuntime, alphaFlags] = commandCall(agentsAddCommandMock, 0);
|
|
expect((alphaOptions as { name?: string }).name).toBe("alpha");
|
|
expect((alphaOptions as { workspace?: string }).workspace).toBeUndefined();
|
|
expect((alphaOptions as { bind?: string[] }).bind).toEqual([]);
|
|
expect(alphaRuntime).toBe(runtime);
|
|
expect(alphaFlags).toEqual({ hasFlags: false });
|
|
|
|
await runCli([
|
|
"agents",
|
|
"add",
|
|
"beta",
|
|
"--workspace",
|
|
"/tmp/ws",
|
|
"--bind",
|
|
"telegram",
|
|
"--bind",
|
|
"discord:acct",
|
|
"--non-interactive",
|
|
"--json",
|
|
]);
|
|
const [betaOptions, betaRuntime, betaFlags] = commandCall(agentsAddCommandMock, 1);
|
|
expect((betaOptions as { name?: string }).name).toBe("beta");
|
|
expect((betaOptions as { workspace?: string }).workspace).toBe("/tmp/ws");
|
|
expect((betaOptions as { bind?: string[] }).bind).toEqual(["telegram", "discord:acct"]);
|
|
expect((betaOptions as { nonInteractive?: boolean }).nonInteractive).toBe(true);
|
|
expect((betaOptions as { json?: boolean }).json).toBe(true);
|
|
expect(betaRuntime).toBe(runtime);
|
|
expect(betaFlags).toEqual({ hasFlags: true });
|
|
});
|
|
|
|
it("runs agents list when root agents command is invoked", async () => {
|
|
await runCli(["agents"]);
|
|
expect(agentsListCommandMock).toHaveBeenCalledWith({}, runtime);
|
|
});
|
|
|
|
it("forwards agents list options", async () => {
|
|
await runCli(["agents", "list", "--json", "--bindings"]);
|
|
expect(agentsListCommandMock).toHaveBeenCalledWith(
|
|
{
|
|
json: true,
|
|
bindings: true,
|
|
},
|
|
runtime,
|
|
);
|
|
});
|
|
|
|
it("forwards agents bindings options", async () => {
|
|
await runCli(["agents", "bindings", "--agent", "ops", "--json"]);
|
|
expect(agentsBindingsCommandMock).toHaveBeenCalledWith(
|
|
{
|
|
agent: "ops",
|
|
json: true,
|
|
},
|
|
runtime,
|
|
);
|
|
});
|
|
|
|
it("forwards agents bind options", async () => {
|
|
await runCli([
|
|
"agents",
|
|
"bind",
|
|
"--agent",
|
|
"ops",
|
|
"--bind",
|
|
"matrix:ops",
|
|
"--bind",
|
|
"telegram",
|
|
"--json",
|
|
]);
|
|
expect(agentsBindCommandMock).toHaveBeenCalledWith(
|
|
{
|
|
agent: "ops",
|
|
bind: ["matrix:ops", "telegram"],
|
|
json: true,
|
|
},
|
|
runtime,
|
|
);
|
|
});
|
|
|
|
it("documents bind accountId resolution behavior in help text", () => {
|
|
const program = new Command();
|
|
registerAgentCommands(program, { agentChannelOptions: "last|telegram|discord" });
|
|
const agents = program.commands.find((command) => command.name() === "agents");
|
|
const bind = agents?.commands.find((command) => command.name() === "bind");
|
|
const help = bind?.helpInformation() ?? "";
|
|
expect(help).toContain("accountId is resolved by channel defaults/hooks");
|
|
});
|
|
|
|
it("forwards agents unbind options", async () => {
|
|
await runCli(["agents", "unbind", "--agent", "ops", "--all", "--json"]);
|
|
expect(agentsUnbindCommandMock).toHaveBeenCalledWith(
|
|
{
|
|
agent: "ops",
|
|
bind: [],
|
|
all: true,
|
|
json: true,
|
|
},
|
|
runtime,
|
|
);
|
|
});
|
|
|
|
it("forwards agents delete options", async () => {
|
|
await runCli(["agents", "delete", "worker-a", "--force", "--json"]);
|
|
const [options, callRuntime] = commandCall(agentsDeleteCommandMock);
|
|
expect((options as { id?: string }).id).toBe("worker-a");
|
|
expect((options as { force?: boolean }).force).toBe(true);
|
|
expect((options as { json?: boolean }).json).toBe(true);
|
|
expect(callRuntime).toBe(runtime);
|
|
});
|
|
|
|
it("forwards set-identity options", async () => {
|
|
await runCli([
|
|
"agents",
|
|
"set-identity",
|
|
"--agent",
|
|
"main",
|
|
"--workspace",
|
|
"/tmp/ws",
|
|
"--identity-file",
|
|
"/tmp/ws/IDENTITY.md",
|
|
"--from-identity",
|
|
"--name",
|
|
"OpenClaw",
|
|
"--theme",
|
|
"ops",
|
|
"--emoji",
|
|
":lobster:",
|
|
"--avatar",
|
|
"https://example.com/openclaw.png",
|
|
"--json",
|
|
]);
|
|
expect(agentsSetIdentityCommandMock).toHaveBeenCalledWith(
|
|
{
|
|
agent: "main",
|
|
workspace: "/tmp/ws",
|
|
identityFile: "/tmp/ws/IDENTITY.md",
|
|
fromIdentity: true,
|
|
name: "OpenClaw",
|
|
theme: "ops",
|
|
emoji: ":lobster:",
|
|
avatar: "https://example.com/openclaw.png",
|
|
json: true,
|
|
},
|
|
runtime,
|
|
);
|
|
});
|
|
|
|
it("reports errors via runtime when a command fails", async () => {
|
|
agentsListCommandMock.mockRejectedValueOnce(new Error("list failed"));
|
|
|
|
await runCli(["agents"]);
|
|
|
|
expect(runtime.error).toHaveBeenCalledWith("Error: list failed");
|
|
expect(runtime.exit).toHaveBeenCalledWith(1);
|
|
});
|
|
|
|
it("reports errors via runtime when agent command fails", async () => {
|
|
agentCliCommandMock.mockRejectedValueOnce(new Error("agent failed"));
|
|
|
|
await runCli(["agent", "--message", "hello"]);
|
|
|
|
expect(runtime.error).toHaveBeenCalledWith("Error: agent failed");
|
|
expect(runtime.exit).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|