From dafecc756add0ea719a2dbb8df00b8cee6776832 Mon Sep 17 00:00:00 2001 From: WilShi <47671779+WilShi@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:24:34 +0800 Subject: [PATCH] fix(cli): surface deviceId in devices list table (#114370) * fix(cli): surface deviceId in devices list table * docs(changelog): add devices list deviceId fix (#114279) * fix(cli): keep paired device IDs copyable Co-authored-by: WilShi <1638083992@qq.com> --------- Co-authored-by: Peter Steinberger --- src/cli/devices-cli.runtime.ts | 33 ++++++++++++++++++++------------- src/cli/devices-cli.test.ts | 28 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/cli/devices-cli.runtime.ts b/src/cli/devices-cli.runtime.ts index de8fb8a4ad85..5a9f41014916 100644 --- a/src/cli/devices-cli.runtime.ts +++ b/src/cli/devices-cli.runtime.ts @@ -928,32 +928,39 @@ export async function runDevicesListCommand(opts: DevicesRpcOpts): Promise } if (list.paired?.length) { const tableWidth = getTerminalTableWidth(); + const rows = list.paired.map((device) => ({ + Device: sanitizeForLog( + device.operatorLabel || device.displayName || device.clientId || device.deviceId, + ), + "Device ID": sanitizeForLog(device.deviceId), + Roles: device.roles?.length + ? device.roles.map((role) => sanitizeForLog(role)).join(", ") + : "", + Scopes: device.scopes?.length + ? device.scopes.map((scope) => sanitizeForLog(scope)).join(", ") + : "", + Tokens: formatTokenSummary(device.tokens), + IP: device.remoteIp ? sanitizeForLog(device.remoteIp) : "", + })); defaultRuntime.log(`${theme.heading("Paired")} ${theme.muted(`(${list.paired.length})`)}`); defaultRuntime.log( renderTable({ width: tableWidth, columns: [ { key: "Device", header: "Device", minWidth: 16, flex: true }, + { key: "Device ID", header: "Device ID", minWidth: 12, flex: true }, { key: "Roles", header: "Roles", minWidth: 12, flex: true }, { key: "Scopes", header: "Scopes", minWidth: 12, flex: true }, { key: "Tokens", header: "Tokens", minWidth: 12, flex: true }, { key: "IP", header: "IP", minWidth: 12 }, ], - rows: list.paired.map((device) => ({ - Device: sanitizeForLog( - device.operatorLabel || device.displayName || device.clientId || device.deviceId, - ), - Roles: device.roles?.length - ? device.roles.map((role) => sanitizeForLog(role)).join(", ") - : "", - Scopes: device.scopes?.length - ? device.scopes.map((scope) => sanitizeForLog(scope)).join(", ") - : "", - Tokens: formatTokenSummary(device.tokens), - IP: device.remoteIp ? sanitizeForLog(device.remoteIp) : "", - })), + rows, }).trimEnd(), ); + defaultRuntime.log(theme.muted("Full device IDs")); + for (const row of rows) { + defaultRuntime.log(` ${row["Device ID"]} ${row.Device}`); + } const nodeApprovalNotices = await findPairedDevicePendingNodeApprovalNotices(opts, list.paired); for (const notice of nodeApprovalNotices) { defaultRuntime.log(theme.warn(formatNodeApprovalNotice(notice))); diff --git a/src/cli/devices-cli.test.ts b/src/cli/devices-cli.test.ts index 7d0063486b19..46cc9da13aa9 100644 --- a/src/cli/devices-cli.test.ts +++ b/src/cli/devices-cli.test.ts @@ -1248,6 +1248,34 @@ describe("devices cli list", () => { expect(output).not.toContain("openclaw-macos"); expect(output).not.toContain("openclaw-ios"); }); + + it("shows a deviceId column so identical display names are distinguishable for remove", async () => { + const deviceIdA = "a".repeat(64); + const deviceIdB = "b".repeat(64); + callGateway.mockResolvedValueOnce({ + pending: [], + paired: [ + pairedDevice({ + deviceId: deviceIdA, + displayName: "OpenClaw Desktop", + clientId: "openclaw-macos", + }), + pairedDevice({ + deviceId: deviceIdB, + displayName: "OpenClaw Desktop", + clientId: "openclaw-macos", + }), + ], + }); + + await runDevicesCommand(["list"]); + + const output = stripAnsi(readRuntimeOutput()); + expect(output).toContain("Device ID"); + expect(output).toContain("Full device IDs"); + expect(output.split("\n")).toContain(` ${deviceIdA} OpenClaw Desktop`); + expect(output.split("\n")).toContain(` ${deviceIdB} OpenClaw Desktop`); + }); }); describe("devices cli rename", () => {