@honor-claw/yoyo 0.0.1-beta.2 → 0.0.1-beta.21

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.
Files changed (79) hide show
  1. package/index.ts +2 -2
  2. package/openclaw.plugin.json +7 -0
  3. package/package.json +20 -20
  4. package/skills/search/SKILL.md +182 -0
  5. package/skills/search/scripts/search.sh +69 -0
  6. package/skills/yoyo-control/SKILL.md +105 -120
  7. package/skills/yoyo-control/references/alarm-create.md +473 -0
  8. package/skills/yoyo-control/references/app-close.md +183 -0
  9. package/skills/yoyo-control/references/app-open.md +178 -0
  10. package/skills/yoyo-control/references/call-phone.md +250 -0
  11. package/skills/yoyo-control/references/capture-screenshot.md +205 -54
  12. package/skills/yoyo-control/references/contact-search.md +235 -0
  13. package/skills/yoyo-control/references/hotspot.md +208 -0
  14. package/skills/yoyo-control/references/local-search.md +224 -15
  15. package/skills/yoyo-control/references/message-send.md +246 -0
  16. package/skills/yoyo-control/references/mobile-data.md +248 -0
  17. package/skills/yoyo-control/references/no-disturb.md +239 -0
  18. package/skills/yoyo-control/references/quiet-mode.md +228 -0
  19. package/skills/yoyo-control/references/ringing-mode.md +223 -0
  20. package/skills/yoyo-control/references/screen-record.md +220 -0
  21. package/skills/yoyo-control/references/vibration-mode.md +235 -0
  22. package/skills/yoyo-control/references/volume-operate.md +274 -0
  23. package/skills/yoyo-control/scripts/invoke.js +33 -111
  24. package/src/agent/copy-templates.ts +56 -0
  25. package/src/agent/index.ts +3 -0
  26. package/src/agent/templates/AGENTS.md +223 -0
  27. package/src/apis/claw-cloud.ts +70 -23
  28. package/src/apis/honor-auth.ts +20 -10
  29. package/src/apis/types.ts +24 -1
  30. package/src/cloud-channel/channel.ts +245 -58
  31. package/src/cloud-channel/client.ts +87 -12
  32. package/src/cloud-channel/types.ts +30 -0
  33. package/src/commands/env/impl.ts +58 -0
  34. package/src/commands/env/index.ts +1 -0
  35. package/src/commands/index.ts +11 -1
  36. package/src/commands/login/impl.ts +17 -8
  37. package/src/commands/logout/impl.ts +23 -0
  38. package/src/commands/logout/index.ts +1 -53
  39. package/src/commands/status/index.ts +172 -42
  40. package/src/gateway-client/client.deprecated.ts +1 -1
  41. package/src/gateway-client/client.ts +15 -20
  42. package/src/gateway-client/types.ts +2 -2
  43. package/src/honor-auth/browser.ts +12 -15
  44. package/src/honor-auth/callback-server.ts +3 -6
  45. package/src/honor-auth/cloud.ts +65 -12
  46. package/src/honor-auth/config.ts +25 -17
  47. package/src/honor-auth/index.ts +1 -0
  48. package/src/honor-auth/token-manager.ts +24 -14
  49. package/src/modules/claw-configs/config-manager.ts +211 -11
  50. package/src/modules/claw-configs/hosts.ts +48 -0
  51. package/src/modules/claw-configs/index.ts +1 -0
  52. package/src/modules/claw-configs/types.ts +4 -0
  53. package/src/modules/device/device-info.ts +20 -9
  54. package/src/modules/device/providers/linux.ts +128 -0
  55. package/src/modules/device/providers/macos.ts +123 -0
  56. package/src/modules/device/providers/pad.ts +0 -16
  57. package/src/modules/device/registry.ts +12 -3
  58. package/src/modules/login/impl.ts +38 -16
  59. package/src/runtime.ts +44 -0
  60. package/src/schemas.ts +4 -1
  61. package/src/services/connection/impl.ts +89 -9
  62. package/src/services/connection/status-tracker/events.ts +127 -0
  63. package/src/services/connection/status-tracker/index.ts +31 -0
  64. package/src/services/connection/status-tracker/storage.ts +133 -0
  65. package/src/services/connection/status-tracker/tracker.ts +370 -0
  66. package/src/services/connection/status-tracker/types.ts +131 -0
  67. package/src/types.ts +0 -4
  68. package/src/utils/fs-safe.ts +544 -0
  69. package/src/utils/version.ts +29 -0
  70. package/src/utils/ws.ts +21 -0
  71. package/skills/yoyo-control/references/open-app.md +0 -54
  72. package/skills/yoyo-control/references/phone-call.md +0 -217
  73. package/skills/yoyo-control/references/schedule.md +0 -107
  74. package/skills/yoyo-control/references/screen-recorder.md +0 -67
  75. package/skills/yoyo-control/references/search-contact.md +0 -37
  76. package/skills/yoyo-control/references/send-message.md +0 -155
  77. package/skills/yoyo-control/references/volume.md +0 -536
  78. package/skills/yoyo-control/scripts/README.md +0 -103
  79. package/skills/yoyo-control/scripts/volume-up.json +0 -7
@@ -0,0 +1,370 @@
1
+ /**
2
+ * 状态跟踪器
3
+ * 核心状态跟踪逻辑,处理所有状态事件并维护状态数据
4
+ */
5
+
6
+ import type {
7
+ ConnectionStatusData,
8
+ GatewayConnectionInfo,
9
+ } from './types.js';
10
+ import { createEmptyStatusData } from './types.js';
11
+ import { StatusStorage } from './storage.js';
12
+ import {
13
+ StatusEventType,
14
+ type StatusEvent,
15
+ type CloudSocketConnectedData,
16
+ type CloudSocketDisconnectedData,
17
+ type CloudSocketErrorData,
18
+ type CloudSocketRetryData,
19
+ type GatewayClientConnectedData,
20
+ type GatewayClientDisconnectedData,
21
+ type DevicePairingData,
22
+ type DeviceUnpairingData,
23
+ type ConnectionStatusChangedData,
24
+ } from './events.js';
25
+
26
+ /**
27
+ * 状态跟踪器类
28
+ */
29
+ export class StatusTracker {
30
+ private currentStatus: ConnectionStatusData;
31
+ private storage: StatusStorage;
32
+ private autoSave: boolean;
33
+ private saveTimer: NodeJS.Timeout | null;
34
+
35
+ constructor(autoSave = true) {
36
+ this.currentStatus = createEmptyStatusData();
37
+ this.storage = new StatusStorage();
38
+ this.autoSave = autoSave;
39
+ this.saveTimer = null;
40
+ }
41
+
42
+ /**
43
+ * 处理状态事件
44
+ */
45
+ async handleEvent(event: StatusEvent): Promise<void> {
46
+ const { type, timestamp, data } = event;
47
+
48
+ switch (type) {
49
+ case StatusEventType.CLOUD_SOCKET_CONNECTING:
50
+ this.handleCloudSocketConnecting(data, timestamp);
51
+ break;
52
+
53
+ case StatusEventType.CLOUD_SOCKET_CONNECTED:
54
+ this.handleCloudSocketConnected(data, timestamp);
55
+ break;
56
+
57
+ case StatusEventType.CLOUD_SOCKET_DISCONNECTED:
58
+ this.handleCloudSocketDisconnected(data, timestamp);
59
+ break;
60
+
61
+ case StatusEventType.CLOUD_SOCKET_ERROR:
62
+ this.handleCloudSocketError(data, timestamp);
63
+ break;
64
+
65
+ case StatusEventType.CLOUD_SOCKET_RETRY:
66
+ this.handleCloudSocketRetry(data, timestamp);
67
+ break;
68
+
69
+ case StatusEventType.GATEWAY_CLIENT_CONNECTED:
70
+ this.handleGatewayClientConnected(data, timestamp);
71
+ break;
72
+
73
+ case StatusEventType.GATEWAY_CLIENT_DISCONNECTED:
74
+ this.handleGatewayClientDisconnected(data, timestamp);
75
+ break;
76
+
77
+ case StatusEventType.DEVICE_PAIRING:
78
+ this.handleDevicePairing(data, timestamp);
79
+ break;
80
+
81
+ case StatusEventType.DEVICE_UNPAIRING:
82
+ this.handleDeviceUnpairing(data, timestamp);
83
+ break;
84
+
85
+ case StatusEventType.CONNECTION_STATUS_CHANGED:
86
+ this.handleConnectionStatusChanged(data, timestamp);
87
+ break;
88
+
89
+ default:
90
+ console.warn(`[StatusTracker] Unknown event type: ${type}`);
91
+ }
92
+
93
+ // 更新时间戳
94
+ this.currentStatus.updatedAt = new Date(timestamp).toISOString();
95
+
96
+ // 自动保存
97
+ if (this.autoSave) {
98
+ this.scheduleSave();
99
+ }
100
+ }
101
+
102
+ /**
103
+ * 获取当前状态
104
+ */
105
+ getStatus(): ConnectionStatusData {
106
+ return { ...this.currentStatus };
107
+ }
108
+
109
+ /**
110
+ * 加载已保存的状态
111
+ */
112
+ async loadStatus(): Promise<ConnectionStatusData> {
113
+ const savedStatus = await this.storage.load();
114
+ if (savedStatus) {
115
+ this.currentStatus = savedStatus;
116
+ }
117
+ return this.getStatus();
118
+ }
119
+
120
+ /**
121
+ * 加载并清理状态
122
+ * 在服务启动时调用,清除临时的连接状态(gateway连接和物理设备)
123
+ * 保留持久化的状态(云socket状态和连接历史)
124
+ */
125
+ async loadAndCleanStatus(): Promise<ConnectionStatusData> {
126
+ const savedStatus = await this.storage.load();
127
+ if (savedStatus) {
128
+ // 保留云socket状态和连接历史
129
+ const cloudSocketStatus = savedStatus.cloudSocket;
130
+ const connectionHistory = savedStatus.history;
131
+
132
+ // 创建新的空状态
133
+ this.currentStatus = createEmptyStatusData();
134
+
135
+ // 恢复云socket状态
136
+ this.currentStatus.cloudSocket = cloudSocketStatus;
137
+
138
+ // 恢复连接历史
139
+ this.currentStatus.history = connectionHistory;
140
+
141
+ // 重置云socket的连接状态(因为重启后连接会断开)
142
+ this.currentStatus.cloudSocket.connected = false;
143
+ this.currentStatus.cloudSocket.readyState = 3; // WebSocket.CLOSED
144
+
145
+ // 保存清理后的状态
146
+ await this.saveStatus();
147
+ }
148
+ return this.getStatus();
149
+ }
150
+
151
+ /**
152
+ * 保存当前状态
153
+ */
154
+ async saveStatus(): Promise<void> {
155
+ await this.storage.save(this.currentStatus);
156
+ }
157
+
158
+ /**
159
+ * 清除状态
160
+ */
161
+ async clearStatus(): Promise<void> {
162
+ this.currentStatus = createEmptyStatusData();
163
+ await this.storage.clear();
164
+ }
165
+
166
+ // ==================== 事件处理方法 ====================
167
+
168
+ private handleCloudSocketConnecting(data: any, timestamp: number): void {
169
+ this.currentStatus.cloudSocket.url = data.url || '';
170
+ this.currentStatus.cloudSocket.connected = false;
171
+ }
172
+
173
+ private handleCloudSocketConnected(
174
+ data: CloudSocketConnectedData,
175
+ timestamp: number
176
+ ): void {
177
+ this.currentStatus.cloudSocket.connected = true;
178
+ this.currentStatus.cloudSocket.connectedAt = data.connectedAt;
179
+ this.currentStatus.cloudSocket.readyState = 1; // WebSocket.OPEN
180
+ this.currentStatus.cloudSocket.url = data.url;
181
+ this.currentStatus.cloudSocket.retryCount = 0;
182
+ this.currentStatus.cloudSocket.lastError = null;
183
+
184
+ // 更新连接历史
185
+ this.currentStatus.history.connectionCount++;
186
+ this.currentStatus.history.lastConnectionAt = data.connectedAt;
187
+ }
188
+
189
+ private handleCloudSocketDisconnected(
190
+ data: CloudSocketDisconnectedData,
191
+ timestamp: number
192
+ ): void {
193
+ this.currentStatus.cloudSocket.connected = false;
194
+ this.currentStatus.cloudSocket.lastDisconnectedAt = data.disconnectedAt;
195
+ this.currentStatus.cloudSocket.readyState = 3; // WebSocket.CLOSED
196
+
197
+ // 更新连接历史
198
+ this.currentStatus.history.disconnectionCount++;
199
+ this.currentStatus.history.lastDisconnectionAt = data.disconnectedAt;
200
+ }
201
+
202
+ private handleCloudSocketError(
203
+ data: CloudSocketErrorData,
204
+ timestamp: number
205
+ ): void {
206
+ this.currentStatus.cloudSocket.lastError = data.error;
207
+ }
208
+
209
+ private handleCloudSocketRetry(
210
+ data: CloudSocketRetryData,
211
+ timestamp: number
212
+ ): void {
213
+ this.currentStatus.cloudSocket.retryCount = data.retryCount;
214
+ if (data.lastError) {
215
+ this.currentStatus.cloudSocket.lastError = data.lastError;
216
+ }
217
+ }
218
+
219
+ private handleGatewayClientConnected(
220
+ data: GatewayClientConnectedData,
221
+ timestamp: number
222
+ ): void {
223
+ const connectionInfo: GatewayConnectionInfo = {
224
+ sessionId: data.sessionId,
225
+ hardwareDeviceId: data.hardwareDeviceId,
226
+ connectedAt: data.connectedAt,
227
+ deviceInfo: data.deviceInfo,
228
+ };
229
+
230
+ // 添加到连接列表
231
+ this.currentStatus.gateway.connections.push(connectionInfo);
232
+ this.currentStatus.gateway.totalConnections++;
233
+ this.currentStatus.gateway.activeConnections++;
234
+
235
+ // 更新设备信息
236
+ this.updateDeviceInfo(data.hardwareDeviceId, data.deviceInfo, timestamp);
237
+ }
238
+
239
+ private handleGatewayClientDisconnected(
240
+ data: GatewayClientDisconnectedData,
241
+ timestamp: number
242
+ ): void {
243
+ // 从连接列表中移除
244
+ const index = this.currentStatus.gateway.connections.findIndex(
245
+ (c) => c.sessionId === data.sessionId
246
+ );
247
+
248
+ if (index !== -1) {
249
+ this.currentStatus.gateway.connections.splice(index, 1);
250
+ this.currentStatus.gateway.activeConnections--;
251
+ }
252
+
253
+ // 更新设备信息
254
+ this.updateDeviceInfo(data.hardwareDeviceId, null, timestamp);
255
+ }
256
+
257
+ private handleDevicePairing(data: DevicePairingData, timestamp: number): void {
258
+ // 设备配对时更新设备信息
259
+ this.updateDeviceInfo(data.hardwareDeviceId, data.deviceInfo, timestamp);
260
+ }
261
+
262
+ private handleDeviceUnpairing(
263
+ data: DeviceUnpairingData,
264
+ timestamp: number
265
+ ): void {
266
+ // 设备取消配对时从设备列表中移除
267
+ const index = this.currentStatus.devices.devices.findIndex(
268
+ (d) => d.hardwareDeviceId === data.hardwareDeviceId
269
+ );
270
+
271
+ if (index !== -1) {
272
+ this.currentStatus.devices.devices.splice(index, 1);
273
+ this.recalculateDeviceStats();
274
+ }
275
+ }
276
+
277
+ private handleConnectionStatusChanged(
278
+ data: ConnectionStatusChangedData,
279
+ timestamp: number
280
+ ): void {
281
+ this.currentStatus.status = data.status;
282
+ }
283
+
284
+ // ==================== 辅助方法 ====================
285
+
286
+ /**
287
+ * 更新设备信息
288
+ */
289
+ private updateDeviceInfo(
290
+ hardwareDeviceId: string,
291
+ deviceInfo: any,
292
+ timestamp: number
293
+ ): void {
294
+ let device = this.currentStatus.devices.devices.find(
295
+ (d) => d.hardwareDeviceId === hardwareDeviceId
296
+ );
297
+
298
+ if (!device) {
299
+ // 创建新设备记录
300
+ device = {
301
+ hardwareDeviceId,
302
+ sessions: 0,
303
+ lastActiveAt: new Date(timestamp).toISOString(),
304
+ deviceInfo,
305
+ };
306
+ this.currentStatus.devices.devices.push(device);
307
+ } else {
308
+ // 更新现有设备记录
309
+ device.lastActiveAt = new Date(timestamp).toISOString();
310
+ if (deviceInfo) {
311
+ device.deviceInfo = deviceInfo;
312
+ }
313
+ }
314
+
315
+ // 重新计算设备统计
316
+ this.recalculateDeviceStats();
317
+ }
318
+
319
+ /**
320
+ * 重新计算设备统计信息
321
+ */
322
+ private recalculateDeviceStats(): void {
323
+ this.currentStatus.devices.totalDevices =
324
+ this.currentStatus.devices.devices.length;
325
+ this.currentStatus.devices.uniqueHardwareDevices =
326
+ this.currentStatus.devices.devices.length;
327
+
328
+ // 更新每个设备的会话数
329
+ for (const device of this.currentStatus.devices.devices) {
330
+ const sessionCount = this.currentStatus.gateway.connections.filter(
331
+ (c) => c.hardwareDeviceId === device.hardwareDeviceId
332
+ ).length;
333
+ device.sessions = sessionCount;
334
+ }
335
+ }
336
+
337
+ /**
338
+ * 调度保存(防抖)
339
+ */
340
+ private scheduleSave(): void {
341
+ if (this.saveTimer) {
342
+ clearTimeout(this.saveTimer);
343
+ }
344
+
345
+ this.saveTimer = setTimeout(async () => {
346
+ try {
347
+ await this.saveStatus();
348
+ } catch (error) {
349
+ console.error(
350
+ `[StatusTracker] Failed to save status: ${
351
+ error instanceof Error ? error.message : String(error)
352
+ }`
353
+ );
354
+ }
355
+ }, 100); // 100ms 防抖
356
+ }
357
+
358
+ /**
359
+ * 销毁跟踪器
360
+ */
361
+ async destroy(): Promise<void> {
362
+ if (this.saveTimer) {
363
+ clearTimeout(this.saveTimer);
364
+ this.saveTimer = null;
365
+ }
366
+
367
+ // 最后保存一次
368
+ await this.saveStatus();
369
+ }
370
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * 状态跟踪类型定义
3
+ * 定义连接状态的完整数据结构
4
+ */
5
+
6
+ /**
7
+ * 连接状态类型
8
+ */
9
+ export type ConnectionStatus = 'idle' | 'connecting' | 'connected';
10
+
11
+ /**
12
+ * Gateway连接信息
13
+ */
14
+ export interface GatewayConnectionInfo {
15
+ sessionId: string;
16
+ hardwareDeviceId: string;
17
+ connectedAt: string;
18
+ deviceInfo: any;
19
+ }
20
+
21
+ /**
22
+ * 物理设备信息
23
+ */
24
+ export interface PhysicalDeviceInfo {
25
+ hardwareDeviceId: string;
26
+ sessions: number;
27
+ lastActiveAt: string;
28
+ deviceInfo?: any;
29
+ }
30
+
31
+ /**
32
+ * 云侧Socket状态信息
33
+ */
34
+ export interface CloudSocketStatus {
35
+ connected: boolean;
36
+ connectedAt: string | null;
37
+ lastDisconnectedAt: string | null;
38
+ retryCount: number;
39
+ lastError: string | null;
40
+ url: string;
41
+ readyState: number;
42
+ }
43
+
44
+ /**
45
+ * Gateway连接统计
46
+ */
47
+ export interface GatewayStatus {
48
+ totalConnections: number;
49
+ activeConnections: number;
50
+ connections: GatewayConnectionInfo[];
51
+ }
52
+
53
+ /**
54
+ * 物理设备统计
55
+ */
56
+ export interface DevicesStatus {
57
+ totalDevices: number;
58
+ uniqueHardwareDevices: number;
59
+ devices: PhysicalDeviceInfo[];
60
+ }
61
+
62
+ /**
63
+ * 连接历史统计
64
+ */
65
+ export interface ConnectionHistory {
66
+ connectionCount: number;
67
+ disconnectionCount: number;
68
+ lastConnectionAt: string | null;
69
+ lastDisconnectionAt: string | null;
70
+ }
71
+
72
+ /**
73
+ * 完整的连接状态数据
74
+ */
75
+ export interface ConnectionStatusData {
76
+ // 基础状态
77
+ status: ConnectionStatus;
78
+ updatedAt: string;
79
+
80
+ // 云侧Socket
81
+ cloudSocket: CloudSocketStatus;
82
+
83
+ // Gateway连接
84
+ gateway: GatewayStatus;
85
+
86
+ // 物理设备
87
+ devices: DevicesStatus;
88
+
89
+ // 连接历史
90
+ history: ConnectionHistory;
91
+ }
92
+
93
+ /**
94
+ * 空的连接状态数据(初始化用)
95
+ */
96
+ export function createEmptyStatusData(): ConnectionStatusData {
97
+ const now = new Date().toISOString();
98
+ return {
99
+ status: 'idle',
100
+ updatedAt: now,
101
+
102
+ cloudSocket: {
103
+ connected: false,
104
+ connectedAt: null,
105
+ lastDisconnectedAt: null,
106
+ retryCount: 0,
107
+ lastError: null,
108
+ url: '',
109
+ readyState: 3, // WebSocket.CLOSED
110
+ },
111
+
112
+ gateway: {
113
+ totalConnections: 0,
114
+ activeConnections: 0,
115
+ connections: [],
116
+ },
117
+
118
+ devices: {
119
+ totalDevices: 0,
120
+ uniqueHardwareDevices: 0,
121
+ devices: [],
122
+ },
123
+
124
+ history: {
125
+ connectionCount: 0,
126
+ disconnectionCount: 0,
127
+ lastConnectionAt: null,
128
+ lastDisconnectionAt: null,
129
+ },
130
+ };
131
+ }
package/src/types.ts CHANGED
@@ -45,10 +45,6 @@ export type YoyoPluginConfig = z.infer<typeof YoyoPluginConfigSchema>;
45
45
  * 设备信息
46
46
  */
47
47
  export interface ClawDeviceInfo extends DeviceInfo {
48
- /**
49
- * claw需要用户信息~
50
- */
51
- userId: string;
52
48
  /**
53
49
  * 当前设备的连接状态
54
50
  */