@honor-claw/yoyo 1.2.0-beta.2 → 1.2.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@honor-claw/yoyo",
3
- "version": "1.2.0-beta.2",
3
+ "version": "1.2.0-beta.5",
4
4
  "description": "OpenClaw Honor Yoyo connection plugin",
5
5
  "keywords": [
6
6
  "ai",
@@ -125,6 +125,7 @@ export class ClawCloudClient {
125
125
  data = {
126
126
  clientId: params.authConfig.clientId,
127
127
  code: params.code,
128
+ redirectUri: "http://127.0.0.1:8081/deepLink",
128
129
  };
129
130
  }
130
131
 
package/src/apis/types.ts CHANGED
@@ -90,6 +90,7 @@ export type TokenResponseV2 = {
90
90
  export interface ExchangeTokenRequest {
91
91
  clientId?: string;
92
92
  code?: string;
93
+ redirectUri?: string;
93
94
  }
94
95
 
95
96
  /**
@@ -337,11 +337,11 @@ export class MessageHandler {
337
337
  /**
338
338
  * 处理来自Gateway的消息,转发到云侧
339
339
  */
340
- private handleNodeGatewayMessage = (
340
+ private handleNodeGatewayMessage = async (
341
341
  sourceDeviceId: string,
342
342
  traceInfo: YOYOClawServiceEvent["traceInfo"],
343
343
  data: string,
344
- ): void => {
344
+ ) => {
345
345
  const hardwareDeviceId = this.sessionManager.getHardwareDeviceId(sourceDeviceId);
346
346
  const sessionInfo = this.sessionManager.getSession(sourceDeviceId);
347
347
  const deviceId = sessionInfo?.sourceInfo.sourceDeviceInfo?.deviceId;
@@ -356,16 +356,36 @@ export class MessageHandler {
356
356
  try {
357
357
  // 使用当前接收到的socket对应deviceId的来源信息
358
358
  const targetDeviceId = sessionInfo.sourceInfo.sourceDeviceId;
359
+ const gatewayRawData = JSON.parse(data);
360
+ let cloudData = data;
361
+
362
+ // 收到NOT_PAIRED错误时,自动获取待配对设备并审批
363
+ if (!gatewayRawData.ok && gatewayRawData.error?.code === "NOT_PAIRED") {
364
+ try {
365
+ await this.handleAutoPair();
366
+ cloudData = JSON.stringify({
367
+ ok: true,
368
+ type: "res",
369
+ id: gatewayRawData.id,
370
+ payload: {
371
+ type: "hello-ok",
372
+ },
373
+ });
374
+ useClawLogger().info(`${LOG_PREFIX} auto pair succeefully`);
375
+ } catch (error) {
376
+ useClawLogger().error(`${LOG_PREFIX} auto pair failed: ${String(error)}`);
377
+ }
378
+ }
359
379
 
360
380
  useClawLogger().debug?.(
361
- `${LOG_PREFIX} trans gateway msg to cloud device: ${targetDeviceId}, ${data.slice(0, 1000)}`,
381
+ `${LOG_PREFIX} trans gateway msg to cloud device: ${targetDeviceId}, ${cloudData.slice(0, 1000)}`,
362
382
  );
363
383
 
364
384
  const result = this.sendMessage(
365
385
  "userMessage",
366
386
  targetDeviceId,
367
387
  traceInfo,
368
- data,
388
+ cloudData,
369
389
  undefined,
370
390
  deviceId,
371
391
  );
@@ -381,6 +401,29 @@ export class MessageHandler {
381
401
  }
382
402
  };
383
403
 
404
+ private async handleAutoPair(): Promise<void> {
405
+ const adminClient = this.adminClientManager.getClient();
406
+ if (!adminClient) {
407
+ useClawLogger().warn(`${LOG_PREFIX} admin client not available for auto pair`);
408
+ return;
409
+ }
410
+
411
+ useClawLogger().info(`${LOG_PREFIX} NOT_PAIRED detected, fetching pending pair list...`);
412
+ const pairList = await adminClient.devicePairList();
413
+ const pendingDevices = pairList.pending ?? [];
414
+
415
+ for (const device of pendingDevices) {
416
+ useClawLogger().info(
417
+ `${LOG_PREFIX} auto approving device: ${device.deviceId}, requestId: ${device.requestId}`,
418
+ );
419
+ await adminClient.devicePairApprove(device.requestId);
420
+ }
421
+
422
+ if (pendingDevices.length === 0) {
423
+ useClawLogger().info(`${LOG_PREFIX} no pending devices found for auto pair`);
424
+ }
425
+ }
426
+
384
427
  private async handleContextRequest(message: YOYOClawServiceEvent): Promise<void> {
385
428
  const { sourceDeviceId, traceInfo } = message;
386
429
  const msgType = message.msgType as ContextMsgType;
@@ -7,6 +7,8 @@ import type {
7
7
  PrimayModelResult,
8
8
  SessionListResult,
9
9
  SessionItem,
10
+ DevicePairListResult,
11
+ DevicePairApproveResult,
10
12
  } from "./types/protocol.js";
11
13
  import { SkillStatusReport } from "./types/skills.js";
12
14
 
@@ -56,6 +58,16 @@ export class AdminGatewayClient extends ProtocolGatewayClient {
56
58
  return (result.sessions?.find((session) => session?.key === "agent:main:main") ??
57
59
  {}) as SessionItem;
58
60
  }
61
+
62
+ async devicePairList(): Promise<DevicePairListResult> {
63
+ return this.sendRequest("device.pair.list", {}) as Promise<DevicePairListResult>;
64
+ }
65
+
66
+ async devicePairApprove(requestId: string): Promise<DevicePairApproveResult> {
67
+ return this.sendRequest("device.pair.approve", {
68
+ requestId,
69
+ }) as Promise<DevicePairApproveResult>;
70
+ }
59
71
  }
60
72
 
61
73
  export default AdminGatewayClient;
@@ -41,10 +41,10 @@ export class GatewayClient {
41
41
  this.onOpen();
42
42
  });
43
43
 
44
- this.ws.on("message", (data) => {
44
+ this.ws.on("message", async (data) => {
45
45
  const dataText = rawDataToString(data);
46
- this.opts.onMessage?.(dataText);
47
- this.onMessage(dataText);
46
+ await this.opts.onMessage?.(dataText);
47
+ await this.onMessage(dataText);
48
48
  });
49
49
 
50
50
  this.ws.on("close", (code, reason) => {
@@ -82,7 +82,7 @@ export class GatewayClient {
82
82
 
83
83
  protected onOpen(): void {}
84
84
 
85
- protected onMessage(_data: string): void {}
85
+ protected onMessage(_data: string): void | Promise<void> {}
86
86
 
87
87
  protected onClose(_code: number, _reason: string): void {}
88
88
 
@@ -35,7 +35,7 @@ export type GatewayClientId = (typeof GATEWAY_CLIENT_IDS)[keyof typeof GATEWAY_C
35
35
  export type GatewayClientMode = (typeof GATEWAY_CLIENT_MODES)[keyof typeof GATEWAY_CLIENT_MODES];
36
36
 
37
37
  export interface GatewayClientOptions {
38
- onMessage?: (data: string) => void;
38
+ onMessage?: (data: string) => void | Promise<void>;
39
39
  onOpen?: () => void;
40
40
  onClose?: (reason: string) => void;
41
41
  onError?: (error: Error) => void;
@@ -143,3 +143,55 @@ export interface SessionItem {
143
143
  deliveryContext: Object[];
144
144
  lastChannel: string;
145
145
  }
146
+
147
+ export interface PendingDevice {
148
+ requestId: string;
149
+ deviceId: string;
150
+ displayName?: string;
151
+ role?: string;
152
+ remoteIp?: string;
153
+ isRepair?: boolean;
154
+ ts?: number;
155
+ }
156
+
157
+ export interface DeviceTokenSummary {
158
+ id: string;
159
+ rotatedAtMs?: number;
160
+ revokedAtMs?: number;
161
+ lastUsedAtMs?: number;
162
+ }
163
+
164
+ export interface PairedDevice {
165
+ deviceId: string;
166
+ displayName?: string;
167
+ roles?: string[];
168
+ scopes?: string[];
169
+ remoteIp?: string;
170
+ tokens?: DeviceTokenSummary[];
171
+ createdAtMs?: number;
172
+ approvedAtMs?: number;
173
+ }
174
+
175
+ export interface DevicePairListResult {
176
+ ok: boolean;
177
+ pending?: PendingDevice[];
178
+ paired?: PairedDevice[];
179
+ error?: {
180
+ code: string;
181
+ message: string;
182
+ details?: unknown;
183
+ retryable?: boolean;
184
+ };
185
+ }
186
+
187
+ export interface DevicePairApproveResult {
188
+ ok: boolean;
189
+ requestId?: string;
190
+ device?: PairedDevice;
191
+ error?: {
192
+ code: string;
193
+ message: string;
194
+ details?: unknown;
195
+ retryable?: boolean;
196
+ };
197
+ }