import type { Command } from "commander"; import { defaultRuntime } from "../../runtime.js"; import { getNodesTheme, runNodesCommand } from "./cli-utils.js"; import { parsePairingList } from "./format.js"; import { renderPendingPairingRequestsTable } from "./pairing-render.js"; import { callGatewayCli, nodesCallOpts, resolveNodeId } from "./rpc.js"; import type { NodesRpcOpts } from "./types.js"; export function registerNodesPairingCommands(nodes: Command) { nodesCallOpts( nodes .command("pending") .description("List pending pairing requests") .action(async (opts: NodesRpcOpts) => { await runNodesCommand("pending", async () => { const result = await callGatewayCli("node.pair.list", opts, {}); const { pending } = parsePairingList(result); if (opts.json) { defaultRuntime.log(JSON.stringify(pending, null, 2)); return; } if (pending.length === 0) { const { muted } = getNodesTheme(); defaultRuntime.log(muted("No pending pairing requests.")); return; } const { heading, warn, muted } = getNodesTheme(); const tableWidth = Math.max(60, (process.stdout.columns ?? 120) - 1); const now = Date.now(); const rendered = renderPendingPairingRequestsTable({ pending, now, tableWidth, theme: { heading, warn, muted }, }); defaultRuntime.log(rendered.heading); defaultRuntime.log(rendered.table); }); }), ); nodesCallOpts( nodes .command("approve") .description("Approve a pending pairing request") .argument("", "Pending request id") .action(async (requestId: string, opts: NodesRpcOpts) => { await runNodesCommand("approve", async () => { const result = await callGatewayCli("node.pair.approve", opts, { requestId, }); defaultRuntime.log(JSON.stringify(result, null, 2)); }); }), ); nodesCallOpts( nodes .command("reject") .description("Reject a pending pairing request") .argument("", "Pending request id") .action(async (requestId: string, opts: NodesRpcOpts) => { await runNodesCommand("reject", async () => { const result = await callGatewayCli("node.pair.reject", opts, { requestId, }); defaultRuntime.log(JSON.stringify(result, null, 2)); }); }), ); nodesCallOpts( nodes .command("rename") .description("Rename a paired node (display name override)") .requiredOption("--node ", "Node id, name, or IP") .requiredOption("--name ", "New display name") .action(async (opts: NodesRpcOpts) => { await runNodesCommand("rename", async () => { const nodeId = await resolveNodeId(opts, String(opts.node ?? "")); const name = String(opts.name ?? "").trim(); if (!nodeId || !name) { defaultRuntime.error("--node and --name required"); defaultRuntime.exit(1); return; } const result = await callGatewayCli("node.rename", opts, { nodeId, displayName: name, }); if (opts.json) { defaultRuntime.log(JSON.stringify(result, null, 2)); return; } const { ok } = getNodesTheme(); defaultRuntime.log(ok(`node rename ok: ${nodeId} -> ${name}`)); }); }), ); }