@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
@@ -1,5 +1,6 @@
1
1
  import { createHonorAuthClient } from '../apis/honor-auth.js';
2
2
  import type { DeviceInfo, HonorUserInfo } from '../types.js';
3
+ import { useClawLogger } from '../utils/logger.js';
3
4
  import { startCallbackServer } from './callback-server.js';
4
5
  import { getConfig } from './config.js';
5
6
  import { saveToken } from './token-manager.js';
@@ -9,23 +10,20 @@ import open from 'open';
9
10
  /**
10
11
  * 执行OAuth2授权流程(自动打开浏览器)
11
12
  */
12
- export async function performOAuth2AuthWithBrowser(deviceInfo: DeviceInfo, configOverrides?: Partial<HonorAuthConfig>) {
13
+ export async function performOAuth2AuthWithBrowser(_: DeviceInfo, configOverrides?: Partial<HonorAuthConfig>) {
13
14
  const config = getConfig(configOverrides);
14
-
15
- console.log('🔐 开始荣耀OAuth2认证流程(自动打开浏览器)...');
16
- console.log(`📱 设备信息: ${deviceInfo.deviceName} (${deviceInfo.deviceModel})`);
17
- console.log(`🆔 设备ID: ${deviceInfo.deviceId}`);
15
+ const logger = useClawLogger();
16
+
17
+ logger.debug('Starting Honor login flow...');
18
18
 
19
19
  // 创建认证客户端
20
20
  const authClient = createHonorAuthClient(config);
21
21
 
22
22
  // 生成PKCE参数
23
23
  const pkceParams = authClient.generatePKCEParams();
24
- console.log('🔑 PKCE参数已生成');
25
24
 
26
25
  // 构建授权URL
27
26
  const authUrl = authClient.buildAuthUrl(pkceParams);
28
- console.log(`🔗 授权URL: ${authUrl}`);
29
27
 
30
28
  // 启动本地回调服务器
31
29
  let receivedCode: string | null = null;
@@ -36,36 +34,35 @@ export async function performOAuth2AuthWithBrowser(deviceInfo: DeviceInfo, confi
36
34
  receivedCode = code;
37
35
  },
38
36
  onError: error => {
39
- console.error(' 回调服务器错误:', error);
37
+ console.error('callback server error:', error);
40
38
  },
41
39
  });
42
40
 
43
41
  // 打开浏览器
44
- console.log('🌐 正在打开浏览器...');
42
+ console.log('🌐 open browser...');
45
43
  try {
46
44
  await open(authUrl);
47
45
  } catch (error) {
48
- console.warn('⚠️ 自动打开浏览器失败,请手动访问授权URL');
46
+ throw new Error('failed to open browser, check your permissions');
49
47
  }
50
48
 
51
49
  // 等待回调
52
50
  try {
53
51
  await serverPromise;
54
52
  } catch (error) {
55
- throw new Error(`等待授权超时: ${error instanceof Error ? error.message : String(error)}`);
53
+ throw new Error(`authorization timeout: ${error instanceof Error ? error.message : String(error)}`);
56
54
  }
57
55
 
58
56
  if (!receivedCode) {
59
- throw new Error('未收到授权码');
57
+ throw new Error('no auth code');
60
58
  }
61
59
 
62
60
  // 使用授权码换取Token
63
- console.log('🔄 正在换取Token...');
64
61
  try {
65
62
  const tokenInfo = await authClient.exchangeToken(receivedCode);
66
63
 
67
64
  if (!tokenInfo.token || !tokenInfo.userInfo?.userId) {
68
- throw new Error('获取用户信息失败,未返回用户ID');
65
+ throw new Error('failed to get user info');
69
66
  }
70
67
 
71
68
  // 保存Token
@@ -77,6 +74,6 @@ export async function performOAuth2AuthWithBrowser(deviceInfo: DeviceInfo, confi
77
74
  userName: tokenInfo.userInfo.displayName,
78
75
  } as HonorUserInfo;
79
76
  } catch (error) {
80
- throw new Error(`换取Token失败: ${error instanceof Error ? error.message : String(error)}`);
77
+ throw new Error(`get token failed: ${error instanceof Error ? error.message : String(error)}`);
81
78
  }
82
79
  }
@@ -37,7 +37,6 @@ export function startCallbackServer(options: CallbackServerOptions): Promise<voi
37
37
 
38
38
  if (code && !hasReceivedCode) {
39
39
  hasReceivedCode = true;
40
- console.log('✅ 成功获取授权码 code');
41
40
 
42
41
  // 返回成功响应
43
42
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
@@ -64,7 +63,7 @@ export function startCallbackServer(options: CallbackServerOptions): Promise<voi
64
63
  res.end('<h1>授权成功!可以关闭此窗口</h1>');
65
64
  }
66
65
  } catch (error) {
67
- console.error(' 处理回调请求失败:', error);
66
+ console.error('authorize callback error:', error);
68
67
  res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
69
68
  res.end('<h1>服务器错误</h1>');
70
69
  }
@@ -82,15 +81,13 @@ export function startCallbackServer(options: CallbackServerOptions): Promise<voi
82
81
  }
83
82
  });
84
83
 
85
- server.listen(port, () => {
86
- console.log(`📡 本地回调服务器已启动,监听端口: ${port}`);
87
-
84
+ server.listen(port, () => {
88
85
  // 设置超时
89
86
  timeoutId = setTimeout(() => {
90
87
  if (!serverClosed) {
91
88
  serverClosed = true;
92
89
  server.close();
93
- const error = new Error('等待授权超时');
90
+ const error = new Error('authorize timeout');
94
91
  onError?.(error);
95
92
  if (!promiseResolved) {
96
93
  promiseResolved = true;
@@ -1,30 +1,35 @@
1
1
  import { createHonorAuthClient } from '../apis/honor-auth.js';
2
+ import { createClawCloudClient } from '../apis/claw-cloud.js';
3
+ import { isOKResponse } from '../apis/index.js';
2
4
  import type { DeviceInfo, HonorUserInfo } from '../types.js';
5
+ import { getConfigManager, takeApiHost } from '../modules/claw-configs/index.js';
6
+ import { getDeviceInfo } from '../modules/device/device-info.js';
3
7
  import { startCallbackServer } from './callback-server.js';
4
8
  import { getConfig } from './config.js';
5
- import { saveToken } from './token-manager.js';
9
+ import { saveToken, clearToken } from './token-manager.js';
6
10
  import type { HonorAuthConfig } from './types.js';
11
+ import { useClawLogger } from '../utils/logger.js';
7
12
 
8
13
  /**
9
14
  * 执行云侧OAuth2授权流程
10
15
  */
11
16
  export async function performOAuth2Auth(deviceInfo: DeviceInfo, configOverrides?: Partial<HonorAuthConfig>) {
12
17
  const config = getConfig(configOverrides);
18
+ const logger = useClawLogger();
13
19
 
14
- console.log('🔐 开始荣耀OAuth2认证流程...');
15
- console.log(`📱 设备信息: ${deviceInfo.deviceName} (${deviceInfo.deviceModel})`);
16
- console.log(`🆔 设备ID: ${deviceInfo.deviceId}`);
20
+ logger.debug('Starting Honor OAuth2 authentication flow...');
21
+ logger.debug(`Device info: ${deviceInfo.deviceName} (${deviceInfo.deviceModel})`);
22
+ logger.debug(`Device ID: ${deviceInfo.deviceId}`);
17
23
 
18
24
  // 创建认证客户端
19
25
  const authClient = createHonorAuthClient(config);
20
26
 
21
27
  // 生成PKCE参数
22
28
  const pkceParams = authClient.generatePKCEParams();
23
- console.log('🔑 PKCE参数已生成');
29
+ logger.debug('PKCE parameters generated');
24
30
 
25
31
  // 构建授权URL
26
32
  const authUrl = authClient.buildAuthUrl(pkceParams);
27
- console.log(`🔗 授权URL: ${authUrl}`);
28
33
 
29
34
  // 启动本地回调服务器
30
35
  let receivedCode: string | null = null;
@@ -36,24 +41,23 @@ export async function performOAuth2Auth(deviceInfo: DeviceInfo, configOverrides?
36
41
  receivedCode = code;
37
42
  },
38
43
  onError: error => {
39
- console.error('❌ 回调服务器错误:', error);
44
+ logger.error(`Callback server error: ${error.message}`);
40
45
  },
41
46
  });
42
47
  } catch (error) {
43
- throw new Error(`回调服务器启动失败: ${error instanceof Error ? error.message : String(error)}`);
48
+ throw new Error(`Failed to start callback server: ${error instanceof Error ? error.message : String(error)}`);
44
49
  }
45
50
 
46
51
  if (!receivedCode) {
47
- throw new Error('未收到授权码');
52
+ throw new Error('No authorization code received');
48
53
  }
49
54
 
50
55
  // 使用授权码换取Token
51
- console.log('🔄 正在换取Token...');
52
56
  try {
53
57
  const tokenInfo = await authClient.exchangeToken(receivedCode);
54
58
 
55
59
  if (!tokenInfo.token || !tokenInfo.userInfo?.userId) {
56
- throw new Error('获取用户信息失败,未返回用户ID');
60
+ throw new Error('Failed to get user info, no user ID returned');
57
61
  }
58
62
 
59
63
  // 保存Token
@@ -65,6 +69,55 @@ export async function performOAuth2Auth(deviceInfo: DeviceInfo, configOverrides?
65
69
  userName: tokenInfo.userInfo.displayName,
66
70
  } as HonorUserInfo;
67
71
  } catch (error) {
68
- throw new Error(`换取Token失败: ${error instanceof Error ? error.message : String(error)}`);
72
+ throw new Error(`Failed to exchange token: ${error instanceof Error ? error.message : String(error)}`);
73
+ }
74
+ }
75
+
76
+ /**
77
+ * 执行登出流程
78
+ */
79
+ export async function performLogout(): Promise<void> {
80
+ try {
81
+ const configManager = getConfigManager();
82
+ const userConfig = configManager.getUserConfig();
83
+
84
+ if (!userConfig || !userConfig.userId) {
85
+ console.log('⚠️ Not logged in');
86
+ return;
87
+ }
88
+
89
+ // 获取设备信息
90
+ const deviceInfo = getDeviceInfo();
91
+
92
+ // 调用登出接口
93
+ const hosts = takeApiHost();
94
+ const baseUrl = `https://${hosts.clawCloud}/aicloud/yoyo-claw-service`;
95
+
96
+ // 如果有灰度标签,设置默认headers
97
+ const options = hosts.grayTag ? {
98
+ defaultHeaders: {
99
+ 'x-gray': hosts.grayTag
100
+ }
101
+ } : undefined;
102
+
103
+ const client = createClawCloudClient(baseUrl, options);
104
+
105
+ const userInfo: HonorUserInfo = {
106
+ userId: userConfig.userId,
107
+ token: userConfig.token,
108
+ userName: userConfig.userName,
109
+ };
110
+
111
+ const response = await client.logoutDevice(deviceInfo, userInfo);
112
+
113
+ if (!isOKResponse(response)) {
114
+ throw new Error(`Logout failed: ${response.data?.cnMessage || 'Unknown error'}`);
115
+ }
116
+
117
+ // 清除Token
118
+ await clearToken();
119
+ } catch (error) {
120
+ const errorMessage = error instanceof Error ? error.message : String(error);
121
+ throw new Error(`failed to logout: ${errorMessage}`);
69
122
  }
70
123
  }
@@ -2,34 +2,42 @@
2
2
  * 荣耀认证配置管理
3
3
  */
4
4
 
5
- import type { HonorAuthConfig } from './types.js';
5
+ import type { HonorAuthConfig } from "./types.js";
6
+ import { getYoyoEnv } from "../runtime.js";
6
7
 
7
8
  /**
8
9
  * 默认配置
9
10
  */
10
- const DEFAULT_CONFIG: HonorAuthConfig = {
11
- authHost: 'https://hnoauth-login-test-drcn.cloud.honor.com',
12
- clientId: '221641491',
13
- redirectUri: 'http://127.0.0.1:8081/deepLink',
11
+ const TEST_ENV_CONFIG: HonorAuthConfig = {
12
+ authHost: "https://hnoauth-login-test-drcn.cloud.honor.com",
13
+ clientId: "221641491",
14
+ redirectUri: "http://127.0.0.1:8081/deepLink",
14
15
  localPort: 8081,
15
- scope: 'openid profile',
16
- reqClientType: '110',
17
- loginChannel: '99011000',
16
+ scope: "openid profile",
17
+ reqClientType: "110",
18
+ loginChannel: "99011000",
19
+ };
20
+ const PRD_ENV_CONFIG: HonorAuthConfig = {
21
+ authHost: "https://hnoauth-login-drcn.cloud.honor.com",
22
+ clientId: "221000597",
23
+ redirectUri: "http://127.0.0.1:8081/deepLink",
24
+ localPort: 8081,
25
+ scope: "openid profile",
26
+ reqClientType: "110",
27
+ loginChannel: "99011000",
18
28
  };
19
29
 
20
30
  /**
21
31
  * 获取配置(支持自定义覆盖)
32
+ * 根据当前运行环境自动选择对应的配置
22
33
  */
23
- export function getConfig(overrides?: Partial<HonorAuthConfig>): HonorAuthConfig {
34
+ export function getConfig(
35
+ overrides?: Partial<HonorAuthConfig>
36
+ ): HonorAuthConfig {
37
+ const env = getYoyoEnv();
38
+ const baseConfig = env === 'production' ? PRD_ENV_CONFIG : TEST_ENV_CONFIG;
24
39
  return {
25
- ...DEFAULT_CONFIG,
40
+ ...baseConfig,
26
41
  ...overrides,
27
42
  };
28
43
  }
29
-
30
- /**
31
- * 获取默认配置
32
- */
33
- export function getDefaultConfig(): HonorAuthConfig {
34
- return { ...DEFAULT_CONFIG };
35
- }
@@ -1,2 +1,3 @@
1
1
  export * from './browser.js';
2
2
  export * from './cloud.js';
3
+ export * from './token-manager.js';
@@ -2,18 +2,21 @@
2
2
  * Token管理器 - 负责token的读写缓存
3
3
  */
4
4
 
5
- import type { TokenResponse } from './types.js';
6
- import type { HonorUserInfo } from '../types.js';
7
- import { getConfigManager } from '../modules/claw-configs/index.js';
8
- import { useClawLogger } from '../utils/logger.js';
5
+ import type { TokenResponse } from "./types.js";
6
+ import type { HonorUserInfo } from "../types.js";
7
+ import { getConfigManager } from "../modules/claw-configs/index.js";
8
+ import { useClawLogger } from "../utils/logger.js";
9
9
 
10
10
  /**
11
11
  * 保存Token到openclaw配置文件
12
12
  */
13
- export async function saveToken(token: TokenResponse, saveToFile: boolean = true): Promise<void> {
13
+ export async function saveToken(
14
+ token: TokenResponse,
15
+ saveToFile: boolean = true
16
+ ): Promise<void> {
14
17
  try {
15
18
  if (!saveToFile) {
16
- console.log('💾 Token已获取(未保存到文件)');
19
+ console.log("💾 Token已获取(未保存到文件)");
17
20
  return;
18
21
  }
19
22
 
@@ -29,10 +32,10 @@ export async function saveToken(token: TokenResponse, saveToFile: boolean = true
29
32
  userId: token.userInfo?.userId,
30
33
  userName: token.userInfo?.displayName,
31
34
  });
32
-
33
- console.log('💾 Token已保存到openclaw配置文件');
34
35
  } catch (error) {
35
- throw new Error(`保存Token失败: ${error instanceof Error ? error.message : String(error)}`);
36
+ throw new Error(
37
+ `保存Token失败: ${error instanceof Error ? error.message : String(error)}`
38
+ );
36
39
  }
37
40
  }
38
41
 
@@ -50,19 +53,24 @@ export async function loadToken(): Promise<HonorUserInfo | null> {
50
53
  }
51
54
 
52
55
  // 检查是否过期
53
- if (userConfig.expired && userConfig.expired < Math.floor(Date.now() / 1000)) {
56
+ if (
57
+ userConfig.expired &&
58
+ userConfig.expired < Math.floor(Date.now() / 1000)
59
+ ) {
54
60
  // Token已过期,从配置中移除
55
61
  await configManager.clearUserConfig();
56
62
  return null;
57
63
  }
58
64
 
59
65
  return {
60
- userId: userConfig.userId || '',
66
+ userId: userConfig.userId || "",
61
67
  token: userConfig.token,
62
68
  userName: userConfig.userName,
63
69
  };
64
70
  } catch (error) {
65
- throw new Error(`加载Token失败: ${error instanceof Error ? error.message : String(error)}`);
71
+ throw new Error(
72
+ `加载Token失败: ${error instanceof Error ? error.message : String(error)}`
73
+ );
66
74
  }
67
75
  }
68
76
 
@@ -73,8 +81,10 @@ export async function clearToken(): Promise<void> {
73
81
  try {
74
82
  const configManager = getConfigManager();
75
83
  await configManager.clearUserConfig();
76
- useClawLogger().info('[yoyoclaw-auth] token cleared');
84
+ useClawLogger().info("[yoyoclaw-auth] token cleared");
77
85
  } catch (error) {
78
- throw new Error(`清除Token失败: ${error instanceof Error ? error.message : String(error)}`);
86
+ throw new Error(
87
+ `清除Token失败: ${error instanceof Error ? error.message : String(error)}`
88
+ );
79
89
  }
80
90
  }