From 00023b808be2bd3c0820e889dce2cae76a76783f Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Wed, 18 Mar 2026 14:11:06 +0800 Subject: [PATCH] Improve X assistant pairing required guidance --- src/app/api/openclaw/assistant/route.ts | 4 +- .../openclaw/OpenClawAssistantPane.tsx | 84 +++++++++++++++++-- src/lib/openclaw/types.ts | 2 + 3 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/app/api/openclaw/assistant/route.ts b/src/app/api/openclaw/assistant/route.ts index 5cef89a..b61a9b3 100644 --- a/src/app/api/openclaw/assistant/route.ts +++ b/src/app/api/openclaw/assistant/route.ts @@ -89,7 +89,7 @@ function formatGatewayError(error: OpenClawGatewayError | null, client: OpenClaw const requestId = stringValue(details.requestId) const reason = stringValue(details.reason) return [ - '需要先在 OpenClaw 网关审批该设备配对请求。', + '等待管理员审批当前设备后,X 助手才能继续连接。', requestId ? `requestId: ${requestId}` : '', client.deviceId ? `deviceId: ${client.deviceId}` : '', reason ? `reason: ${reason}` : '', @@ -382,6 +382,8 @@ async function handleSend(body: SendBody, request: NextRequest): Promise | null; + deviceId?: string; +}; + export type OpenClawAssistantViewState = { connectionState: ConnectionState; healthBadge: string; @@ -84,6 +91,47 @@ function pickCopy(isChinese: boolean, zh: string, en: string): string { return isChinese ? zh : en; } +function asRecord(value: unknown): Record { + return value && typeof value === "object" && !Array.isArray(value) + ? (value as Record) + : {}; +} + +function stringValue(value: unknown): string | undefined { + return typeof value === "string" && value.trim().length > 0 ? value : undefined; +} + +function formatAssistantApiError(params: { + payload: AssistantApiErrorPayload; + isChinese: boolean; + fallback: string; +}): string { + const message = params.payload.error?.trim(); + if (params.payload.code !== "PAIRING_REQUIRED") { + return message || params.fallback; + } + + const details = asRecord(params.payload.details); + const requestId = stringValue(details.requestId); + const deviceId = + params.payload.deviceId?.trim() || stringValue(details.deviceId); + const reason = stringValue(details.reason); + + return [ + pickCopy( + params.isChinese, + "等待管理员审批当前设备后,X 助手才能继续连接。", + "Waiting for an administrator to approve this device before X Assistant can continue.", + ), + deviceId ? `deviceId: ${deviceId}` : "", + requestId ? `requestId: ${requestId}` : "", + reason ? `reason: ${reason}` : "", + message && message !== params.fallback ? message : "", + ] + .filter(Boolean) + .join("\n"); +} + function renderMarkdown(value: string): string { return DOMPurify.sanitize(marked.parse(value) as string); } @@ -517,10 +565,16 @@ export function OpenClawAssistantPane({ const payload = (await response.json()) as | OpenClawBootstrapResponse - | { error?: string }; + | AssistantApiErrorPayload; if (!response.ok || "error" in payload) { - throw new Error((payload as { error?: string }).error || copy.bootstrapFailed); + throw new Error( + formatAssistantApiError({ + payload: payload as AssistantApiErrorPayload, + isChinese, + fallback: copy.bootstrapFailed, + }), + ); } const data = payload as OpenClawBootstrapResponse; @@ -546,6 +600,7 @@ export function OpenClawAssistantPane({ copy.bootstrapFailed, copy.connectFailed, copy.serverMissing, + isChinese, openclawToken, openclawOrigin, openclawUrl, @@ -672,8 +727,14 @@ export function OpenClawAssistantPane({ if (!response.ok || !response.body) { const payload = await response .json() - .catch(() => ({ error: copy.sendFailed })); - throw new Error(payload.error || copy.sendFailed); + .catch(() => ({ error: copy.sendFailed } as AssistantApiErrorPayload)); + throw new Error( + formatAssistantApiError({ + payload: payload as AssistantApiErrorPayload, + isChinese, + fallback: copy.sendFailed, + }), + ); } const reader = response.body.getReader(); @@ -715,7 +776,18 @@ export function OpenClawAssistantPane({ } if (event.type === "error") { - setErrorMessage(event.message); + setErrorMessage( + formatAssistantApiError({ + payload: { + error: event.message, + code: event.code, + details: event.details ?? null, + deviceId: event.deviceId, + }, + isChinese, + fallback: copy.sendFailed, + }), + ); } } } @@ -1151,7 +1223,7 @@ export function OpenClawAssistantPane({ ) : null} {errorMessage ? ( -
+
{errorMessage}
) : null} diff --git a/src/lib/openclaw/types.ts b/src/lib/openclaw/types.ts index d9232dd..f28ae11 100644 --- a/src/lib/openclaw/types.ts +++ b/src/lib/openclaw/types.ts @@ -75,6 +75,8 @@ export type OpenClawStreamEvent = type: 'error' message: string code?: string + details?: Record | null + deviceId?: string } export type IntegrationDefaults = {