@honor-claw/yoyo 1.2.0-beta.9 → 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.
package/package.json
CHANGED
|
@@ -47,6 +47,7 @@ alarm.create
|
|
|
47
47
|
│ - realRepeatType = 5(指定日期) 时,必须设置 specialTimeMillis │
|
|
48
48
|
│ - realRepeatType = 9(大小周) 时,必须设置 bigWeek │
|
|
49
49
|
│ - 校验时间格式是否为 THH:mm:ss │
|
|
50
|
+
| - 指代日期(如:明天、后天)的具体日期推理 → specialTimeMillis(时间戳)|
|
|
50
51
|
└─────────────────────────────────────────────────────────────┘
|
|
51
52
|
↓
|
|
52
53
|
┌─────────────────────────────────────────────────────────────┐
|
|
@@ -73,6 +74,7 @@ alarm.create
|
|
|
73
74
|
| **创建指定日期闹钟** | 2026年3月20日早上八点的闹钟 | `time`, `realRepeatType: 5`, `specialTimeMillis` | - |
|
|
74
75
|
| **创建节假日跳过闹钟** | 工作日闹钟,跳过节假日 | `time`, `realRepeatType: 1`, `skipHoliday: true` | - |
|
|
75
76
|
| **创建大小周闹钟** | 大小周工作日上午九点的闹钟 | `time`, `realRepeatType: 9`, `bigWeek` | - |
|
|
77
|
+
| **创建指代日期闹钟** | 明天早上九点的闹钟 | `time`, `specialTimeMillis` | - |
|
|
76
78
|
|
|
77
79
|
## 参数定义
|
|
78
80
|
|
|
@@ -471,6 +473,41 @@ cmd /c 'openclaw nodes invoke --node <ID> --command alarm.create --params "{\"ti
|
|
|
471
473
|
openclaw nodes invoke --node <ID> --command alarm.create --params '{"time":"T08:30:00","realRepeatType":10,"daysOfWeek":0,"specialTimeMillis":0,"skipHoliday":false}'
|
|
472
474
|
```
|
|
473
475
|
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
### 示例 12: 创建指代日期闹钟
|
|
479
|
+
|
|
480
|
+
**用户输入**: "创建明天早上八点的闹钟"
|
|
481
|
+
**处理逻辑**:
|
|
482
|
+
1. 获取当前的日期,以“2026年4月15日12:09:57”为例,当前日期为 2026-04-15 12:09:57 对应时间戳: 1776226197000
|
|
483
|
+
2. 根据用户输入推理闹钟时间为 2026-04-16 08:00:00,将时间转换为时间戳: 1776297600000
|
|
484
|
+
3. `realRepeatType` 为 5,即为指定日期闹钟
|
|
485
|
+
|
|
486
|
+
**JSON 参数**:
|
|
487
|
+
|
|
488
|
+
```json
|
|
489
|
+
{
|
|
490
|
+
"time": "T08:00:00",
|
|
491
|
+
"realRepeatType": 5,
|
|
492
|
+
"daysOfWeek": 0,
|
|
493
|
+
"specialTimeMillis": 1776297600000,
|
|
494
|
+
"skipHoliday": false
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
**Windows (Cmd) 执行命令**:
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
cmd /c 'openclaw nodes invoke --node <ID> --command alarm.create --params "{\"time\":\"T08:00:00\",\"realRepeatType\":5,\"daysOfWeek\":0,\"specialTimeMillis\":1776297600000,\"skipHoliday\":false}"'
|
|
502
|
+
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**Linux (Bash) 执行命令**:
|
|
506
|
+
|
|
507
|
+
```bash
|
|
508
|
+
openclaw nodes invoke --node <ID> --command alarm.create --params '{"time":"T08:00:00","realRepeatType":5,"daysOfWeek":0,"specialTimeMillis":1776297600000,"skipHoliday":false}'
|
|
509
|
+
```
|
|
510
|
+
|
|
474
511
|
## 错误处理
|
|
475
512
|
|
|
476
513
|
如果工具执行错误(300),可能原因如下:
|
|
@@ -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:
|
|
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
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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)
|
|
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`);
|
|
@@ -408,12 +407,10 @@ export class MessageHandler {
|
|
|
408
407
|
try {
|
|
409
408
|
useClawLogger().info(`${LOG_PREFIX} auto pair approve, requestId: ${requestId}`);
|
|
410
409
|
await adminClient.devicePairApprove(requestId);
|
|
411
|
-
return true;
|
|
412
410
|
} catch (error) {
|
|
413
411
|
useClawLogger().error(
|
|
414
|
-
`${LOG_PREFIX} auto pair approve failed, requestId: ${requestId}, error: ${String(error)}
|
|
412
|
+
`${LOG_PREFIX} auto pair approve failed, requestId: ${requestId}, error: ${String(error)}, ignore...`,
|
|
415
413
|
);
|
|
416
|
-
return false;
|
|
417
414
|
}
|
|
418
415
|
}
|
|
419
416
|
|
|
@@ -42,7 +42,7 @@ export interface YOYOClawServiceEvent {
|
|
|
42
42
|
/**
|
|
43
43
|
* 业务扩展信息
|
|
44
44
|
*/
|
|
45
|
-
bizExtInfo?: Record<string,
|
|
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
|
+
}
|
|
@@ -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.
|
|
115
|
-
if (manufacturer
|
|
139
|
+
const manufacturer = manufacturerSources.find((m) => m && m.toLowerCase().includes("honor"));
|
|
140
|
+
if (manufacturer) {
|
|
116
141
|
this.cache.deviceBrand = "HONOR";
|
|
117
142
|
} else {
|
|
118
|
-
|
|
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
|
// 初始化失败,使用默认值
|