CLI: add explicit agents bind/unbind/bindings commands

This commit is contained in:
Gustavo Madeira Santana
2026-02-25 17:58:42 -05:00
parent 8a746e047d
commit e108632e21
6 changed files with 596 additions and 0 deletions

View File

@@ -3,9 +3,12 @@ import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const agentCliCommandMock = vi.fn();
const agentsAddCommandMock = vi.fn();
const agentsBindingsCommandMock = vi.fn();
const agentsBindCommandMock = vi.fn();
const agentsDeleteCommandMock = vi.fn();
const agentsListCommandMock = vi.fn();
const agentsSetIdentityCommandMock = vi.fn();
const agentsUnbindCommandMock = vi.fn();
const setVerboseMock = vi.fn();
const createDefaultDepsMock = vi.fn(() => ({ deps: true }));
@@ -21,9 +24,12 @@ vi.mock("../../commands/agent-via-gateway.js", () => ({
vi.mock("../../commands/agents.js", () => ({
agentsAddCommand: agentsAddCommandMock,
agentsBindingsCommand: agentsBindingsCommandMock,
agentsBindCommand: agentsBindCommandMock,
agentsDeleteCommand: agentsDeleteCommandMock,
agentsListCommand: agentsListCommandMock,
agentsSetIdentityCommand: agentsSetIdentityCommandMock,
agentsUnbindCommand: agentsUnbindCommandMock,
}));
vi.mock("../../globals.js", () => ({
@@ -55,9 +61,12 @@ describe("registerAgentCommands", () => {
vi.clearAllMocks();
agentCliCommandMock.mockResolvedValue(undefined);
agentsAddCommandMock.mockResolvedValue(undefined);
agentsBindingsCommandMock.mockResolvedValue(undefined);
agentsBindCommandMock.mockResolvedValue(undefined);
agentsDeleteCommandMock.mockResolvedValue(undefined);
agentsListCommandMock.mockResolvedValue(undefined);
agentsSetIdentityCommandMock.mockResolvedValue(undefined);
agentsUnbindCommandMock.mockResolvedValue(undefined);
createDefaultDepsMock.mockReturnValue({ deps: true });
});
@@ -147,6 +156,52 @@ describe("registerAgentCommands", () => {
);
});
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-js:ops",
"--bind",
"telegram",
"--json",
]);
expect(agentsBindCommandMock).toHaveBeenCalledWith(
{
agent: "ops",
bind: ["matrix-js:ops", "telegram"],
json: true,
},
runtime,
);
});
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"]);
expect(agentsDeleteCommandMock).toHaveBeenCalledWith(

View File

@@ -2,9 +2,12 @@ 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";
@@ -102,6 +105,63 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.openclaw.ai/cli/age
});
});
agents
.command("bindings")
.description("List routing bindings")
.option("--agent <id>", "Filter by agent id")
.option("--json", "Output JSON instead of text", false)
.action(async (opts) => {
await runCommandWithRuntime(defaultRuntime, async () => {
await agentsBindingsCommand(
{
agent: opts.agent as string | undefined,
json: Boolean(opts.json),
},
defaultRuntime,
);
});
});
agents
.command("bind")
.description("Add routing bindings for an agent")
.option("--agent <id>", "Agent id (defaults to current default agent)")
.option("--bind <channel[:accountId]>", "Binding to add (repeatable)", collectOption, [])
.option("--json", "Output JSON summary", false)
.action(async (opts) => {
await runCommandWithRuntime(defaultRuntime, async () => {
await agentsBindCommand(
{
agent: opts.agent as string | undefined,
bind: Array.isArray(opts.bind) ? (opts.bind as string[]) : undefined,
json: Boolean(opts.json),
},
defaultRuntime,
);
});
});
agents
.command("unbind")
.description("Remove routing bindings for an agent")
.option("--agent <id>", "Agent id (defaults to current default agent)")
.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) => {
await runCommandWithRuntime(defaultRuntime, async () => {
await agentsUnbindCommand(
{
agent: opts.agent as string | undefined,
bind: Array.isArray(opts.bind) ? (opts.bind as string[]) : undefined,
all: Boolean(opts.all),
json: Boolean(opts.json),
},
defaultRuntime,
);
});
});
agents
.command("add [name]")
.description("Add a new isolated agent")