CLI: route gateway status before program registration

This commit is contained in:
Vincent Koc
2026-03-15 20:26:38 -07:00
parent 8ab01c5c93
commit 3b26da4b82
2 changed files with 120 additions and 0 deletions

View File

@@ -5,6 +5,7 @@ const runConfigGetMock = vi.hoisted(() => vi.fn(async () => {}));
const runConfigUnsetMock = vi.hoisted(() => vi.fn(async () => {}));
const modelsListCommandMock = vi.hoisted(() => vi.fn(async () => {}));
const modelsStatusCommandMock = vi.hoisted(() => vi.fn(async () => {}));
const gatewayStatusCommandMock = vi.hoisted(() => vi.fn(async () => {}));
vi.mock("../config-cli.js", () => ({
runConfigGet: runConfigGetMock,
@@ -16,6 +17,10 @@ vi.mock("../../commands/models.js", () => ({
modelsStatusCommand: modelsStatusCommandMock,
}));
vi.mock("../../commands/gateway-status.js", () => ({
gatewayStatusCommand: gatewayStatusCommandMock,
}));
describe("program routes", () => {
beforeEach(() => {
vi.clearAllMocks();
@@ -48,6 +53,73 @@ describe("program routes", () => {
expect(shouldLoad(["node", "openclaw", "health", "--json"])).toBe(false);
});
it("matches gateway status route without plugin preload", () => {
const route = expectRoute(["gateway", "status"]);
expect(route?.loadPlugins).toBeUndefined();
});
it("returns false for gateway status route when option values are missing", async () => {
await expectRunFalse(["gateway", "status"], ["node", "openclaw", "gateway", "status", "--url"]);
await expectRunFalse(
["gateway", "status"],
["node", "openclaw", "gateway", "status", "--token"],
);
await expectRunFalse(
["gateway", "status"],
["node", "openclaw", "gateway", "status", "--password"],
);
await expectRunFalse(
["gateway", "status"],
["node", "openclaw", "gateway", "status", "--timeout"],
);
await expectRunFalse(["gateway", "status"], ["node", "openclaw", "gateway", "status", "--ssh"]);
await expectRunFalse(
["gateway", "status"],
["node", "openclaw", "gateway", "status", "--ssh-identity"],
);
});
it("passes parsed gateway status flags through", async () => {
const route = expectRoute(["gateway", "status"]);
await expect(
route?.run([
"node",
"openclaw",
"--profile",
"work",
"gateway",
"status",
"--url",
"ws://127.0.0.1:18789",
"--token",
"abc",
"--password",
"def",
"--timeout",
"5000",
"--ssh",
"user@host",
"--ssh-identity",
"~/.ssh/id_test",
"--ssh-auto",
"--json",
]),
).resolves.toBe(true);
expect(gatewayStatusCommandMock).toHaveBeenCalledWith(
{
url: "ws://127.0.0.1:18789",
token: "abc",
password: "def",
timeout: "5000",
json: true,
ssh: "user@host",
sshIdentity: "~/.ssh/id_test",
sshAuto: true,
},
expect.any(Object),
);
});
it("returns false when status timeout flag value is missing", async () => {
await expectRunFalse(["status"], ["node", "openclaw", "status", "--timeout"]);
});

View File

@@ -53,6 +53,53 @@ const routeStatus: RouteSpec = {
},
};
const routeGatewayStatus: RouteSpec = {
match: (path) => path[0] === "gateway" && path[1] === "status",
run: async (argv) => {
const url = getFlagValue(argv, "--url");
if (url === null) {
return false;
}
const token = getFlagValue(argv, "--token");
if (token === null) {
return false;
}
const password = getFlagValue(argv, "--password");
if (password === null) {
return false;
}
const timeout = getFlagValue(argv, "--timeout");
if (timeout === null) {
return false;
}
const ssh = getFlagValue(argv, "--ssh");
if (ssh === null) {
return false;
}
const sshIdentity = getFlagValue(argv, "--ssh-identity");
if (sshIdentity === null) {
return false;
}
const sshAuto = hasFlag(argv, "--ssh-auto");
const json = hasFlag(argv, "--json");
const { gatewayStatusCommand } = await import("../../commands/gateway-status.js");
await gatewayStatusCommand(
{
url: url ?? undefined,
token: token ?? undefined,
password: password ?? undefined,
timeout: timeout ?? undefined,
json,
ssh: ssh ?? undefined,
sshIdentity: sshIdentity ?? undefined,
sshAuto,
},
defaultRuntime,
);
return true;
},
};
const routeSessions: RouteSpec = {
// Fast-path only bare `sessions`; subcommands (e.g. `sessions cleanup`)
// must fall through to Commander so nested handlers run.
@@ -251,6 +298,7 @@ const routeModelsStatus: RouteSpec = {
const routes: RouteSpec[] = [
routeHealth,
routeStatus,
routeGatewayStatus,
routeSessions,
routeAgentsList,
routeMemoryStatus,