@honor-claw/yoyo 1.2.0-beta.8 → 1.2.0

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.
@@ -7,7 +7,6 @@ description: >
7
7
  # Usb Shared Network USB共享网络
8
8
 
9
9
  ## Tool Command
10
-
11
10
  ```bash
12
11
  usb-shared-network
13
12
  ```
@@ -15,7 +14,6 @@ usb-shared-network
15
14
  ## 集成工作流
16
15
 
17
16
  ### Step 1: 意图解析与参数组装
18
-
19
17
  ```
20
18
  ┌─────────────────────────────────────────────────────────────┐
21
19
  │ 意图识别与参数提取 │
@@ -43,18 +41,21 @@ usb-shared-network
43
41
  ```
44
42
 
45
43
  ### Step 2: 执行调用
46
-
47
44
  - 根据操作系统选择对应的命令格式
48
45
  - Windows: Cmd 格式(双引号需转义)
49
46
  - Linux: Bash 格式(单引号包裹)
50
47
 
51
48
  ## 参数定义
52
-
53
49
  ```json
54
50
  {
55
51
  "actionType": {
56
52
  "type": "string",
57
- "enum": ["设置", "查询", "打开", "关闭"],
53
+ "enum": [
54
+ "设置",
55
+ "查询",
56
+ "打开",
57
+ "关闭"
58
+ ],
58
59
  "description": "必填参数,USB 共享网络相关操作的动作类型,用于明确本次指令的主要行为。当涉及调整 USB 共享网络配置时使用设置;当获取当前 USB 共享网络状态时使用查询;当启用 USB 共享网络功能时使用打开;当停用 USB 共享网络功能时使用关闭。"
59
60
  },
60
61
  "app": {
@@ -65,7 +66,6 @@ usb-shared-network
65
66
  ```
66
67
 
67
68
  ### 参数注意事项
68
-
69
69
  1. 必填参数:`actionType`是必填参数,其取值必须满足枚举值范围,必须是`"设置"`、`"查询"`、`"打开"`、`"关闭"`之一。
70
70
  2. 同义词/间接表述映射: "开启/打开/启用"统一识别为`actionType`的`打开`;"停用/禁用/关闭"统一识别为`关闭`;"设置/设定/调整/配置/进入设置页面"统一识别为`设置`;"查看/查询/了解状态"统一识别为`查询`。
71
71
  3. 默认值或缺省行为: 未指定`app`时视为对系统全局设置进行操作;`actionType`为必填,无明确语义时需追问用户。
@@ -81,7 +81,6 @@ usb-shared-network
81
81
  **工具**: "usb-shared-network"
82
82
 
83
83
  **JSON 参数**:
84
-
85
84
  ```json
86
85
  {
87
86
  "actionType": "打开"
@@ -89,13 +88,11 @@ usb-shared-network
89
88
  ```
90
89
 
91
90
  **Windows (Cmd) 执行命令**:
92
-
93
91
  ```bash
94
92
  cmd /c 'openclaw nodes invoke --node <ID> --command usb-shared-network --params "{\"actionType\":\"打开\"}"'
95
93
  ```
96
94
 
97
95
  **Linux (Bash) 执行命令**:
98
-
99
96
  ```bash
100
97
  openclaw nodes invoke --node <ID> --command usb-shared-network --params '{"actionType":"打开"}'
101
98
  ```
@@ -109,7 +106,6 @@ openclaw nodes invoke --node <ID> --command usb-shared-network --params '{"actio
109
106
  **工具**: "usb-shared-network"
110
107
 
111
108
  **JSON 参数**:
112
-
113
109
  ```json
114
110
  {
115
111
  "actionType": "关闭"
@@ -117,13 +113,11 @@ openclaw nodes invoke --node <ID> --command usb-shared-network --params '{"actio
117
113
  ```
118
114
 
119
115
  **Windows (Cmd) 执行命令**:
120
-
121
116
  ```bash
122
117
  cmd /c 'openclaw nodes invoke --node <ID> --command usb-shared-network --params "{\"actionType\":\"关闭\"}"'
123
118
  ```
124
119
 
125
120
  **Linux (Bash) 执行命令**:
126
-
127
121
  ```bash
128
122
  openclaw nodes invoke --node <ID> --command usb-shared-network --params '{"actionType":"关闭"}'
129
123
  ```
@@ -137,7 +131,6 @@ openclaw nodes invoke --node <ID> --command usb-shared-network --params '{"actio
137
131
  **工具**: "usb-shared-network"
138
132
 
139
133
  **JSON 参数**:
140
-
141
134
  ```json
142
135
  {
143
136
  "actionType": "设置"
@@ -145,13 +138,11 @@ openclaw nodes invoke --node <ID> --command usb-shared-network --params '{"actio
145
138
  ```
146
139
 
147
140
  **Windows (Cmd) 执行命令**:
148
-
149
141
  ```bash
150
142
  cmd /c 'openclaw nodes invoke --node <ID> --command usb-shared-network --params "{\"actionType\":\"设置\"}"'
151
143
  ```
152
144
 
153
145
  **Linux (Bash) 执行命令**:
154
-
155
146
  ```bash
156
147
  openclaw nodes invoke --node <ID> --command usb-shared-network --params '{"actionType":"设置"}'
157
148
  ```
@@ -165,7 +156,6 @@ openclaw nodes invoke --node <ID> --command usb-shared-network --params '{"actio
165
156
  **工具**: "usb-shared-network"
166
157
 
167
158
  **JSON 参数**:
168
-
169
159
  ```json
170
160
  {
171
161
  "actionType": "打开"
@@ -173,13 +163,11 @@ openclaw nodes invoke --node <ID> --command usb-shared-network --params '{"actio
173
163
  ```
174
164
 
175
165
  **Windows (Cmd) 执行命令**:
176
-
177
166
  ```bash
178
167
  cmd /c 'openclaw nodes invoke --node <ID> --command usb-shared-network --params "{\"actionType\":\"打开\"}"'
179
168
  ```
180
169
 
181
170
  **Linux (Bash) 执行命令**:
182
-
183
171
  ```bash
184
172
  openclaw nodes invoke --node <ID> --command usb-shared-network --params '{"actionType":"打开"}'
185
173
  ```
@@ -193,7 +181,6 @@ openclaw nodes invoke --node <ID> --command usb-shared-network --params '{"actio
193
181
  **工具**: "usb-shared-network"
194
182
 
195
183
  **JSON 参数**:
196
-
197
184
  ```json
198
185
  {
199
186
  "actionType": "设置"
@@ -201,13 +188,11 @@ openclaw nodes invoke --node <ID> --command usb-shared-network --params '{"actio
201
188
  ```
202
189
 
203
190
  **Windows (Cmd) 执行命令**:
204
-
205
191
  ```bash
206
192
  cmd /c 'openclaw nodes invoke --node <ID> --command usb-shared-network --params "{\"actionType\":\"设置\"}"'
207
193
  ```
208
194
 
209
195
  **Linux (Bash) 执行命令**:
210
-
211
196
  ```bash
212
197
  openclaw nodes invoke --node <ID> --command usb-shared-network --params '{"actionType":"设置"}'
213
- ```
198
+ ```
@@ -26,7 +26,6 @@ export class ClawCloudSocketClient {
26
26
  private retryTimer: NodeJS.Timeout | null = null;
27
27
  private isManualClose = false;
28
28
  private isRetryPaused = false; // 重试状态
29
- private hasRetried = false;
30
29
  // ping/pong 配置
31
30
  private pingTimer: NodeJS.Timeout | null = null;
32
31
  // 当前连接上下文
@@ -76,7 +75,6 @@ export class ClawCloudSocketClient {
76
75
  this.isManualClose = false;
77
76
  this.retryCount = 0;
78
77
  this.isRetryPaused = false;
79
- this.hasRetried = false;
80
78
 
81
79
  this.startPingTimer();
82
80
 
@@ -257,25 +255,7 @@ export class ClawCloudSocketClient {
257
255
 
258
256
  this.clearRetryTimer();
259
257
 
260
- // 首次重试立即执行,不等待
261
- if (!this.hasRetried) {
262
- this.hasRetried = true;
263
- useClawLogger().info(`[claw-cloud-socket] first retry, reconnecting immediately`);
264
-
265
- this.options.onStatusEvent?.({
266
- type: StatusEventType.CLOUD_SOCKET_RETRY,
267
- timestamp: Date.now(),
268
- data: {
269
- retryCount: this.retryCount,
270
- delay: 0,
271
- },
272
- });
273
-
274
- this.connect(true);
275
- return;
276
- }
277
-
278
- // 后续重试使用指数退避: 1s → 2s → 4s,封顶4s无限重试
258
+ // 使用指数退避重试: 1s → 2s → 4s,封顶4s无限重试
279
259
  const delay = this.calculateRetryDelay();
280
260
  this.retryCount = Math.min(this.retryCount + 1, RETRY_COUNT_CAP);
281
261
 
@@ -365,7 +345,6 @@ export class ClawCloudSocketClient {
365
345
 
366
346
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
367
347
  this.retryCount = 0;
368
- this.hasRetried = false;
369
348
  this.connect(true);
370
349
  }
371
350
  }
@@ -8,6 +8,7 @@ import {
8
8
  type DeviceSessionInfo,
9
9
  type YOYOClawServiceContext,
10
10
  type StatusEvent,
11
+ type RawGatewayMessage,
11
12
  StatusEventType,
12
13
  } from "./types.js";
13
14
 
@@ -358,7 +359,7 @@ export class MessageHandler {
358
359
  const targetDeviceId = sessionInfo.sourceInfo.sourceDeviceId;
359
360
 
360
361
  // 收到NOT_PAIRED错误时,自动获取待配对设备并审批
361
- let rawData: Record<string, any>;
362
+ let rawData: RawGatewayMessage;
362
363
  try {
363
364
  rawData = JSON.parse(data);
364
365
  } catch {
@@ -367,18 +368,16 @@ export class MessageHandler {
367
368
  }
368
369
 
369
370
  if (!rawData.ok && rawData.error?.code === "NOT_PAIRED") {
370
- const requestId = rawData.error?.details?.requestId;
371
+ const requestId = rawData.error?.details?.requestId as string | undefined;
371
372
  if (!requestId) {
372
373
  useClawLogger().warn(`${LOG_PREFIX} NOT_PAIRED without requestId, ignoring...`);
373
374
  return;
374
375
  }
375
- const success = await this.handleAutoPair(requestId);
376
- if (success) {
377
- const bizExtInfo = { id: rawData.id, type: "reconnect-required" };
378
- this.sendMessage("deviceControl", targetDeviceId, traceInfo, "", undefined, bizExtInfo);
379
- useClawLogger().info(`${LOG_PREFIX} auto pair is completed, requestId: ${requestId}`);
380
- return;
381
- }
376
+ await this.handleAutoPair(requestId);
377
+ const bizExtInfo = { id: rawData.id, type: "reconnect-required" };
378
+ this.sendMessage("deviceControl", targetDeviceId, traceInfo, "", undefined, bizExtInfo);
379
+ useClawLogger().info(`${LOG_PREFIX} auto pair is completed, requestId: ${requestId}`);
380
+ return;
382
381
  }
383
382
 
384
383
  useClawLogger().debug?.(
@@ -398,7 +397,7 @@ export class MessageHandler {
398
397
  }
399
398
  };
400
399
 
401
- private async handleAutoPair(requestId: string): Promise<boolean> {
400
+ private async handleAutoPair(requestId: string) {
402
401
  const adminClient = this.adminClientManager.getClient();
403
402
  if (!adminClient) {
404
403
  useClawLogger().warn(`${LOG_PREFIX} admin client not available for auto pair`);
@@ -407,16 +406,11 @@ export class MessageHandler {
407
406
 
408
407
  try {
409
408
  useClawLogger().info(`${LOG_PREFIX} auto pair approve, requestId: ${requestId}`);
410
- const res = await adminClient.devicePairApprove(requestId);
411
- if (!res.ok) {
412
- throw new Error(JSON.stringify(res.error));
413
- }
414
- return true;
409
+ await adminClient.devicePairApprove(requestId);
415
410
  } catch (error) {
416
411
  useClawLogger().error(
417
- `${LOG_PREFIX} auto pair approve failed, requestId: ${requestId}, error: ${String(error)}`,
412
+ `${LOG_PREFIX} auto pair approve failed, requestId: ${requestId}, error: ${String(error)}, ignore...`,
418
413
  );
419
- return false;
420
414
  }
421
415
  }
422
416
 
@@ -42,7 +42,7 @@ export interface YOYOClawServiceEvent {
42
42
  /**
43
43
  * 业务扩展信息
44
44
  */
45
- bizExtInfo?: Record<string, any>;
45
+ bizExtInfo?: Record<string, unknown>;
46
46
  }
47
47
 
48
48
  /**
@@ -126,3 +126,18 @@ export interface DeviceSessionInfo {
126
126
  timestamp: number;
127
127
  sourceInfo: ClawSocketSourceInfo;
128
128
  }
129
+
130
+ /**
131
+ * 通用网关原始消息结构
132
+ * 用于 JSON.parse 后的数据结构,允许任意扩展字段
133
+ */
134
+ export interface RawGatewayMessage {
135
+ ok?: boolean;
136
+ id?: string;
137
+ error?: {
138
+ code?: string;
139
+ message?: string;
140
+ details?: Record<string, unknown>;
141
+ };
142
+ [key: string]: unknown;
143
+ }
@@ -25,7 +25,6 @@ export function registerLogoutCommand(api: OpenClawPluginApi, command: Command)
25
25
  } catch (error) {
26
26
  const errorMessage = error instanceof Error ? error.message : String(error);
27
27
  console.error("❌ Logout failed:", errorMessage);
28
- throw error;
29
28
  }
30
29
  });
31
30
 
@@ -3,41 +3,35 @@ import { isOKResponse } from "../apis/index.js";
3
3
  import { getConfigManager } from "../modules/configs/index.js";
4
4
  import { getDeviceInfo } from "../modules/device/device-info.js";
5
5
  import type { HonorUserInfo } from "../types.js";
6
- import { wrapError } from "../utils/error.js";
7
6
  import { clearToken } from "./token-manager.js";
8
7
 
9
8
  /**
10
9
  * 执行登出流程
11
10
  */
12
11
  export async function performLogout(): Promise<void> {
13
- try {
14
- const configManager = getConfigManager();
15
- const userConfig = configManager.getUserConfig();
12
+ const configManager = getConfigManager();
13
+ const userConfig = configManager.getUserConfig();
16
14
 
17
- if (!userConfig?.token) {
18
- console.log("⚠️ Not logged in");
19
- return;
20
- }
21
-
22
- // 获取设备信息
23
- const deviceInfo = await getDeviceInfo();
15
+ if (!userConfig?.token) {
16
+ throw new Error("⚠️ Not logged in");
17
+ }
24
18
 
25
- // 调用登出接口
26
- const client = createClawCloudClient();
19
+ // 获取设备信息
20
+ const deviceInfo = await getDeviceInfo();
27
21
 
28
- const userInfo: HonorUserInfo = {
29
- token: userConfig.token,
30
- };
22
+ // 调用登出接口
23
+ const client = createClawCloudClient();
31
24
 
32
- const response = await client.logoutDevice(deviceInfo, userInfo);
25
+ const userInfo: HonorUserInfo = {
26
+ token: userConfig.token,
27
+ };
33
28
 
34
- if (!isOKResponse(response)) {
35
- throw new Error(`Logout failed: ${response.data?.cnMessage || "Unknown error"}`);
36
- }
29
+ const response = await client.logoutDevice(deviceInfo, userInfo);
37
30
 
38
- // 清除Token
39
- await clearToken();
40
- } catch (error) {
41
- throw wrapError(error, "failed to logout");
31
+ if (!isOKResponse(response)) {
32
+ throw new Error(response.data?.cnMessage || "Unknown error");
42
33
  }
34
+
35
+ // 清除Token
36
+ await clearToken();
43
37
  }
@@ -40,6 +40,31 @@ function getRegistryStringValueAsync(
40
40
  });
41
41
  }
42
42
 
43
+ /**
44
+ * 异步检查注册表键是否存在
45
+ * 使用 winreg 的 values() 方法,键不存在时会报错
46
+ */
47
+ function registryKeyExistsAsync(hive: number, keyPath: string): Promise<boolean> {
48
+ return new Promise((resolve) => {
49
+ try {
50
+ const regKey = new Registry({
51
+ hive,
52
+ key: keyPath,
53
+ });
54
+
55
+ regKey.values((err: Error | null) => {
56
+ if (err) {
57
+ resolve(false);
58
+ } else {
59
+ resolve(true);
60
+ }
61
+ });
62
+ } catch {
63
+ resolve(false);
64
+ }
65
+ });
66
+ }
67
+
43
68
  export class WindowsDeviceInfoProvider implements DeviceInfoProvider {
44
69
  private cache: DeviceInfoCache = {
45
70
  deviceId: "",
@@ -111,11 +136,17 @@ export class WindowsDeviceInfoProvider implements DeviceInfoProvider {
111
136
  systemManufacturer2,
112
137
  ];
113
138
 
114
- const manufacturer = manufacturerSources.find((m) => m && m.trim()) || "";
115
- if (manufacturer.toLowerCase().includes("honor")) {
139
+ const manufacturer = manufacturerSources.find((m) => m && m.toLowerCase().includes("honor"));
140
+ if (manufacturer) {
116
141
  this.cache.deviceBrand = "HONOR";
117
142
  } else {
118
- this.cache.deviceBrand = "";
143
+ // 最后兜底:检查 HKLM:\SOFTWARE\HONOR 注册表键是否存在
144
+ const honorKeyExists = await registryKeyExistsAsync(Registry.HKLM, "\\SOFTWARE\\HONOR");
145
+ if (honorKeyExists) {
146
+ this.cache.deviceBrand = "HONOR";
147
+ } else {
148
+ this.cache.deviceBrand = "";
149
+ }
119
150
  }
120
151
  } catch {
121
152
  // 初始化失败,使用默认值