CLI: add nodes status
This commit is contained in:
@@ -32,6 +32,9 @@ type NodeListNode = {
|
||||
platform?: string;
|
||||
version?: string;
|
||||
remoteIp?: string;
|
||||
deviceFamily?: string;
|
||||
modelIdentifier?: string;
|
||||
caps?: string[];
|
||||
connected?: boolean;
|
||||
};
|
||||
|
||||
@@ -177,6 +180,44 @@ export function registerNodesCli(program: Command) {
|
||||
.command("nodes")
|
||||
.description("Manage gateway-owned node pairing");
|
||||
|
||||
nodesCallOpts(
|
||||
nodes
|
||||
.command("status")
|
||||
.description("List paired nodes with connection status and capabilities")
|
||||
.action(async (opts: NodesRpcOpts) => {
|
||||
try {
|
||||
const result = (await callGatewayCli("node.list", opts, {})) as unknown;
|
||||
if (opts.json) {
|
||||
defaultRuntime.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
}
|
||||
const nodes = parseNodeList(result);
|
||||
const connectedCount = nodes.filter((n) => Boolean(n.connected)).length;
|
||||
defaultRuntime.log(
|
||||
`Paired: ${nodes.length} · Connected: ${connectedCount}`,
|
||||
);
|
||||
for (const n of nodes) {
|
||||
const name = n.displayName || n.nodeId;
|
||||
const ip = n.remoteIp ? ` · ${n.remoteIp}` : "";
|
||||
const device = n.deviceFamily ? ` · device: ${n.deviceFamily}` : "";
|
||||
const hw = n.modelIdentifier ? ` · hw: ${n.modelIdentifier}` : "";
|
||||
const caps =
|
||||
Array.isArray(n.caps) && n.caps.length > 0
|
||||
? `[${n.caps.map(String).filter(Boolean).sort().join(",")}]`
|
||||
: Array.isArray(n.caps)
|
||||
? "[]"
|
||||
: "?";
|
||||
defaultRuntime.log(
|
||||
`- ${name} · ${n.nodeId}${ip}${device}${hw} · ${n.connected ? "connected" : "disconnected"} · caps: ${caps}`,
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
defaultRuntime.error(`nodes status failed: ${String(err)}`);
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
nodesCallOpts(
|
||||
nodes
|
||||
.command("list")
|
||||
|
||||
@@ -62,6 +62,37 @@ describe("cli program", () => {
|
||||
expect(runtime.log).toHaveBeenCalledWith("Pending: 0 · Paired: 0");
|
||||
});
|
||||
|
||||
it("runs nodes status and calls node.list", async () => {
|
||||
callGateway.mockResolvedValue({
|
||||
ts: Date.now(),
|
||||
nodes: [
|
||||
{
|
||||
nodeId: "ios-node",
|
||||
displayName: "iOS Node",
|
||||
remoteIp: "192.168.0.88",
|
||||
deviceFamily: "iPad",
|
||||
modelIdentifier: "iPad16,6",
|
||||
caps: ["canvas", "camera"],
|
||||
connected: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
const program = buildProgram();
|
||||
runtime.log.mockClear();
|
||||
await program.parseAsync(["nodes", "status"], { from: "user" });
|
||||
|
||||
expect(callGateway).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ method: "node.list", params: {} }),
|
||||
);
|
||||
|
||||
const output = runtime.log.mock.calls.map((c) => String(c[0] ?? "")).join("\n");
|
||||
expect(output).toContain("Paired: 1 · Connected: 1");
|
||||
expect(output).toContain("iOS Node");
|
||||
expect(output).toContain("device: iPad");
|
||||
expect(output).toContain("hw: iPad16,6");
|
||||
expect(output).toContain("caps: [camera,canvas]");
|
||||
});
|
||||
|
||||
it("runs nodes approve and calls node.pair.approve", async () => {
|
||||
callGateway.mockResolvedValue({
|
||||
requestId: "r1",
|
||||
|
||||
Reference in New Issue
Block a user