mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 12:40:43 +00:00
fix(cli): retry admin device approval after ownership denial
This commit is contained in:
@@ -171,6 +171,36 @@ describe("devices cli approve", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("retries explicit approval with admin scope when a paired-device session is ownership-denied", async () => {
|
||||
callGateway
|
||||
.mockResolvedValueOnce({
|
||||
pending: [],
|
||||
paired: [],
|
||||
})
|
||||
.mockRejectedValueOnce(new Error("GatewayClientRequestError: device pairing approval denied"))
|
||||
.mockResolvedValueOnce({ device: { deviceId: "device-2" } });
|
||||
|
||||
await runDevicesApprove(["req-cross-device"]);
|
||||
|
||||
expect(callGateway).toHaveBeenCalledTimes(3);
|
||||
expect(callGateway).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({
|
||||
method: "device.pair.approve",
|
||||
params: { requestId: "req-cross-device" },
|
||||
scopes: undefined,
|
||||
}),
|
||||
);
|
||||
expect(callGateway).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
expect.objectContaining({
|
||||
method: "device.pair.approve",
|
||||
params: { requestId: "req-cross-device" },
|
||||
scopes: ["operator.admin"],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("uses admin scope when a repair approval would inherit an admin token", async () => {
|
||||
callGateway
|
||||
.mockResolvedValueOnce({
|
||||
|
||||
@@ -137,6 +137,12 @@ function normalizeErrorMessage(error: unknown): string {
|
||||
return String(error);
|
||||
}
|
||||
|
||||
function isDevicePairingApprovalDenied(error: unknown): boolean {
|
||||
return normalizeLowercaseStringOrEmpty(normalizeErrorMessage(error)).includes(
|
||||
"device pairing approval denied",
|
||||
);
|
||||
}
|
||||
|
||||
function shouldUseLocalPairingFallback(opts: DevicesRpcOpts, error: unknown): boolean {
|
||||
const message = normalizeLowercaseStringOrEmpty(normalizeErrorMessage(error));
|
||||
if (!readConnectPairingRequiredMessage(message)) {
|
||||
@@ -197,6 +203,14 @@ async function approvePairingWithFallback(
|
||||
scopes ? { scopes } : undefined,
|
||||
);
|
||||
} catch (error) {
|
||||
if (isDevicePairingApprovalDenied(error) && !scopes?.includes(ADMIN_SCOPE)) {
|
||||
return await callGatewayCli(
|
||||
"device.pair.approve",
|
||||
opts,
|
||||
{ requestId },
|
||||
{ scopes: [ADMIN_SCOPE] },
|
||||
);
|
||||
}
|
||||
if (!shouldUseLocalPairingFallback(opts, error)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user