@honor-claw/yoyo 1.2.1-beta.3 → 1.2.1-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.1-beta.3",
3
+ "version": "1.2.1-beta.5",
4
4
  "description": "OpenClaw Honor Yoyo connection plugin",
5
5
  "keywords": [
6
6
  "ai",
@@ -66,7 +66,7 @@ express_logistics_query
66
66
  ### 示例 1: 通用物流查询(未提供单号)
67
67
  **用户输入**: "查一下我的快递"
68
68
 
69
- **工具**: "express-logistics-query"
69
+ **工具**: "express_logistics_query"
70
70
 
71
71
  **JSON 参数**:
72
72
  ```json
@@ -75,12 +75,12 @@ express_logistics_query
75
75
 
76
76
  **Windows (Cmd) 执行命令**:
77
77
  ```bash
78
- cmd /c 'openclaw nodes invoke --node <ID> --command express-logistics-query --params \"{}\"'
78
+ cmd /c 'openclaw nodes invoke --node <ID> --command mcp.tool.call --params "{\"name\":\"express_logistics_query\",\"appPkg\":\"com.hihonor.magicvoice\",\"arguments\":{}}"'
79
79
  ```
80
80
 
81
81
  **Linux (Bash) 执行命令**:
82
82
  ```bash
83
- openclaw nodes invoke --node <ID> --command express-logistics-query --params '{}'
83
+ openclaw nodes invoke --node <ID> --command mcp.tool.call --params '{"name":"express_logistics_query","appPkg":"com.hihonor.magicvoice","arguments":{}}'
84
84
  ```
85
85
 
86
86
  ---
@@ -88,7 +88,7 @@ openclaw nodes invoke --node <ID> --command express-logistics-query --params '{}
88
88
  ### 示例 2: 签收状态核实(未提供单号)
89
89
  **用户输入**: "我的快递签收了吗"
90
90
 
91
- **工具**: "express-logistics-query"
91
+ **工具**: "express_logistics_query"
92
92
 
93
93
  **JSON 参数**:
94
94
  ```json
@@ -97,12 +97,12 @@ openclaw nodes invoke --node <ID> --command express-logistics-query --params '{}
97
97
 
98
98
  **Windows (Cmd) 执行命令**:
99
99
  ```bash
100
- cmd /c 'openclaw nodes invoke --node <ID> --command express-logistics-query --params \"{}\"'
100
+ cmd /c 'openclaw nodes invoke --node <ID> --command mcp.tool.call --params "{\"name\":\"express_logistics_query\",\"appPkg\":\"com.hihonor.magicvoice\",\"arguments\":{}}"'
101
101
  ```
102
102
 
103
103
  **Linux (Bash) 执行命令**:
104
104
  ```bash
105
- openclaw nodes invoke --node <ID> --command express-logistics-query --params '{}'
105
+ openclaw nodes invoke --node <ID> --command mcp.tool.call --params '{"name":"express_logistics_query","appPkg":"com.hihonor.magicvoice","arguments":{}}'
106
106
  ```
107
107
 
108
108
  ---
@@ -110,7 +110,7 @@ openclaw nodes invoke --node <ID> --command express-logistics-query --params '{}
110
110
  ### 示例 3: 指定运单号查询
111
111
  **用户输入**: "看看快递单号为123456789的快递"
112
112
 
113
- **工具**: "express-logistics-query"
113
+ **工具**: "express_logistics_query"
114
114
 
115
115
  **JSON 参数**:
116
116
  ```json
@@ -119,12 +119,12 @@ openclaw nodes invoke --node <ID> --command express-logistics-query --params '{}
119
119
 
120
120
  **Windows (Cmd) 执行命令**:
121
121
  ```bash
122
- cmd /c 'openclaw nodes invoke --node <ID> --command express-logistics-query --params \"{}\"'
122
+ cmd /c 'openclaw nodes invoke --node <ID> --command mcp.tool.call --params "{\"name\":\"express_logistics_query\",\"appPkg\":\"com.hihonor.magicvoice\",\"arguments\":{}}"'
123
123
  ```
124
124
 
125
125
  **Linux (Bash) 执行命令**:
126
126
  ```bash
127
- openclaw nodes invoke --node <ID> --command express-logistics-query --params '{}'
127
+ openclaw nodes invoke --node <ID> --command mcp.tool.call --params '{"name":"express_logistics_query","appPkg":"com.hihonor.magicvoice","arguments":{}}'
128
128
  ```
129
129
 
130
130
  ---
@@ -132,7 +132,7 @@ openclaw nodes invoke --node <ID> --command express-logistics-query --params '{}
132
132
  ### 示例 4: 指定快递公司与单号查询
133
133
  **用户输入**: "帮我查一下顺丰快递单号987654321现在到哪了"
134
134
 
135
- **工具**: "express-logistics-query"
135
+ **工具**: "express_logistics_query"
136
136
 
137
137
  **JSON 参数**:
138
138
  ```json
@@ -141,12 +141,12 @@ openclaw nodes invoke --node <ID> --command express-logistics-query --params '{}
141
141
 
142
142
  **Windows (Cmd) 执行命令**:
143
143
  ```bash
144
- cmd /c 'openclaw nodes invoke --node <ID> --command express-logistics-query --params \"{}\"'
144
+ cmd /c 'openclaw nodes invoke --node <ID> --command mcp.tool.call --params "{\"name\":\"express_logistics_query\",\"appPkg\":\"com.hihonor.magicvoice\",\"arguments\":{}}"'
145
145
  ```
146
146
 
147
147
  **Linux (Bash) 执行命令**:
148
148
  ```bash
149
- openclaw nodes invoke --node <ID> --command express-logistics-query --params '{}'
149
+ openclaw nodes invoke --node <ID> --command mcp.tool.call --params '{"name":"express_logistics_query","appPkg":"com.hihonor.magicvoice","arguments":{}}'
150
150
  ```
151
151
 
152
152
  ---
@@ -154,7 +154,7 @@ openclaw nodes invoke --node <ID> --command express-logistics-query --params '{}
154
154
  ### 示例 5: 到件/签收意图识别
155
155
  **用户输入**: "查询我的包裹到了吗"
156
156
 
157
- **工具**: "express-logistics-query"
157
+ **工具**: "express_logistics_query"
158
158
 
159
159
  **JSON 参数**:
160
160
  ```json
@@ -163,10 +163,10 @@ openclaw nodes invoke --node <ID> --command express-logistics-query --params '{}
163
163
 
164
164
  **Windows (Cmd) 执行命令**:
165
165
  ```bash
166
- cmd /c 'openclaw nodes invoke --node <ID> --command express-logistics-query --params \"{}\"'
166
+ cmd /c 'openclaw nodes invoke --node <ID> --command mcp.tool.call --params "{\"name\":\"express_logistics_query\",\"appPkg\":\"com.hihonor.magicvoice\",\"arguments\":{}}"'
167
167
  ```
168
168
 
169
169
  **Linux (Bash) 执行命令**:
170
170
  ```bash
171
- openclaw nodes invoke --node <ID> --command express-logistics-query --params '{}'
171
+ openclaw nodes invoke --node <ID> --command mcp.tool.call --params '{"name":"express_logistics_query","appPkg":"com.hihonor.magicvoice","arguments":{}}'
172
172
  ```
@@ -29,9 +29,13 @@ export class AdminClientManager {
29
29
  return;
30
30
  }
31
31
 
32
- const token = (getConfigManager().getGatewayAuthConfig()?.token || "") as string;
32
+ const gatewayAuthConfig = getConfigManager().getGatewayAuthConfig();
33
+ const token = typeof gatewayAuthConfig?.token === "string" ? gatewayAuthConfig.token : "";
34
+ const password =
35
+ typeof gatewayAuthConfig?.password === "string" ? gatewayAuthConfig.password : "";
33
36
  this.client = new AdminGatewayClient({
34
37
  token,
38
+ password,
35
39
  onAuthenticated: () => {
36
40
  useClawLogger().info("[yoyoclaw-channel] admin gateway client authenticated");
37
41
  this.ready = true;
@@ -196,7 +196,7 @@ export class ClawCloudSocketClient {
196
196
  useClawLogger().debug?.(
197
197
  `[yoyoclaw-channel] received cloud message from session ${message.wsOutputEvent?.sourceDeviceId}, deviceId ${
198
198
  message.wsOutputEvent?.sourceDeviceInfo?.deviceId || "nil"
199
- }, ${dataText.slice(0, 1000)}`,
199
+ }, ${dataText.slice(0, 3000)}`,
200
200
  );
201
201
 
202
202
  if (message.code === "YOYO_CLAW_100000") {
@@ -184,6 +184,7 @@ export class ProtocolGatewayClient extends GatewayClient {
184
184
  scopes: this.config.scopes,
185
185
  auth: {
186
186
  token: this.protocolOpts.token,
187
+ password: this.protocolOpts.password,
187
188
  },
188
189
  device,
189
190
  };
@@ -43,6 +43,7 @@ export interface GatewayClientOptions {
43
43
 
44
44
  export interface ProtocolClientOptions extends GatewayClientOptions {
45
45
  token?: string;
46
+ password?: string;
46
47
  onAuthenticated?: () => void;
47
48
  onReconnectFailed?: () => void;
48
49
  }
@@ -101,22 +101,15 @@ export class ConfigManager {
101
101
  return undefined;
102
102
  }
103
103
 
104
- const result: GatewayAuthConfig = { token: "", password: "" };
105
-
106
- if (authConfig.token) {
107
- result.token = authConfig.token;
108
- }
109
-
110
- if (authConfig.password) {
111
- result.password = authConfig.password;
112
- }
104
+ const token = authConfig.token ?? "";
105
+ const password = authConfig.password ?? "";
113
106
 
114
107
  // 如果没有任何认证信息,返回 undefined
115
- if (Object.keys(result).length === 0) {
108
+ if (!token && !password) {
116
109
  return undefined;
117
110
  }
118
111
 
119
- return result;
112
+ return { token, password };
120
113
  } catch (error) {
121
114
  console.error(`[claw-configs] Failed to read gateway auth config: ${error}`);
122
115
  return undefined;
@@ -24,6 +24,7 @@ export interface PersistedIdentity {
24
24
  publicKeyPem?: string;
25
25
  privateKeyPem?: string;
26
26
  createdAtMs?: number;
27
+ gatewayAuthMd5?: string;
27
28
  version: IdentityVersion;
28
29
  }
29
30
 
@@ -130,6 +131,10 @@ export async function updatePersistedIdentity(
130
131
  updatedConfig.version = identity.version;
131
132
  }
132
133
 
134
+ if ("gatewayAuthMd5" in identity) {
135
+ updatedConfig.gatewayAuthMd5 = identity.gatewayAuthMd5;
136
+ }
137
+
133
138
  // 写入配置
134
139
  const configPath = getConfigPath();
135
140
  await safeWriteFile({
@@ -0,0 +1,25 @@
1
+ import { md5 } from "../../utils/hash.js";
2
+ import { GatewayAuthConfig } from "../configs/types.js";
3
+
4
+ function normalizeSecretValue(value: unknown): string {
5
+ if (typeof value === "string") {
6
+ return value;
7
+ }
8
+ if (value == null) {
9
+ return "";
10
+ }
11
+ return JSON.stringify(value);
12
+ }
13
+
14
+ export function createGatewayAuthMd5(gatewayAuthConfig: GatewayAuthConfig | undefined): string {
15
+ return md5(
16
+ JSON.stringify({
17
+ token: normalizeSecretValue(gatewayAuthConfig?.token),
18
+ password: normalizeSecretValue(gatewayAuthConfig?.password),
19
+ }),
20
+ );
21
+ }
22
+
23
+ export function isSameGatewayAuthMd5(persist: string | undefined, right: string): boolean {
24
+ return (persist ?? "") === right;
25
+ }
@@ -114,6 +114,14 @@ export async function loadOrCreateDeviceIdentity(): Promise<DeviceIdentity> {
114
114
  return identity;
115
115
  }
116
116
 
117
+ /**
118
+ * 只读取已持久化的 Gateway 认证 MD5
119
+ */
120
+ export async function loadPersistedGatewayAuthMd5(): Promise<string | undefined> {
121
+ const persisted = await getPersistedIdentity();
122
+ return persisted?.gatewayAuthMd5;
123
+ }
124
+
117
125
  /**
118
126
  * Sign a payload using device private key
119
127
  * @param privateKeyPem - Private key in PEM format
@@ -6,3 +6,4 @@ export * from "./types.js";
6
6
  export * from "./helpers.js";
7
7
  export * from "./auth.js";
8
8
  export * from "./credential-builder.js";
9
+ export * from "./gateway-auth.js";
@@ -1,7 +1,11 @@
1
1
  import { createClawCloudClient } from "../../apis/claw-cloud.js";
2
2
  import { isOKResponse } from "../../apis/index.js";
3
3
  import type { DeviceInfo, HonorUserInfo } from "../../types.js";
4
- import { getConfigManager } from "../configs/index.js";
4
+ import { formatHashForLog } from "../../utils/hash.js";
5
+ import { useClawLogger } from "../../utils/logger.js";
6
+ import { getConfigManager, updatePersistedIdentity } from "../configs/index.js";
7
+ import { createGatewayAuthMd5, isSameGatewayAuthMd5 } from "./gateway-auth.js";
8
+ import { loadPersistedGatewayAuthMd5 } from "./identity.js";
5
9
 
6
10
  /**
7
11
  * 注册设备到 Claw Cloud
@@ -25,3 +29,33 @@ export async function registerDevice(deviceInfo: DeviceInfo, userInfo: HonorUser
25
29
  throw new Error(`注册失败:${response.data?.cnMessage}`);
26
30
  }
27
31
  }
32
+
33
+ /**
34
+ * Gateway 认证信息发生变化时重新注册设备
35
+ */
36
+ export async function registerDeviceIfGatewayAuthChanged(
37
+ deviceInfo: DeviceInfo,
38
+ userInfo: HonorUserInfo,
39
+ ) {
40
+ const configManager = getConfigManager();
41
+ const gatewayAuthConfig = configManager.getGatewayAuthConfig();
42
+ const gatewayAuthMd5 = createGatewayAuthMd5(gatewayAuthConfig);
43
+ const persistedGatewayAuthMd5 = await loadPersistedGatewayAuthMd5();
44
+ const logger = useClawLogger();
45
+
46
+ logger.info(
47
+ `[yoyoclaw-registry] checking gateway auth md5: current=${formatHashForLog(
48
+ gatewayAuthMd5,
49
+ )}, persisted=${formatHashForLog(persistedGatewayAuthMd5)}`,
50
+ );
51
+
52
+ if (isSameGatewayAuthMd5(persistedGatewayAuthMd5, gatewayAuthMd5)) {
53
+ logger.info("[yoyoclaw-registry] gateway auth unchanged, skipping device registration");
54
+ return;
55
+ }
56
+
57
+ logger.info("[yoyoclaw-registry] gateway auth changed, registering device");
58
+ await registerDevice(deviceInfo, userInfo);
59
+ await updatePersistedIdentity({ gatewayAuthMd5 });
60
+ logger.info("[yoyoclaw-registry] gateway auth md5 persisted after device registration");
61
+ }
@@ -2,7 +2,11 @@ import { OpenClawPluginApi, OpenClawPluginService } from "openclaw/plugin-sdk";
2
2
  import { ClawChannel } from "../../cloud-channel/channel.js";
3
3
  import { loadToken, clearToken } from "../../honor-auth/token-manager.js";
4
4
  import { getConfigManager } from "../../modules/configs/config-manager.js";
5
- import { getDeviceInfo, registerDevice } from "../../modules/device/index.js";
5
+ import {
6
+ getDeviceInfo,
7
+ registerDevice,
8
+ registerDeviceIfGatewayAuthChanged,
9
+ } from "../../modules/device/index.js";
6
10
  import { DeviceInfo, HonorUserInfo } from "../../types.js";
7
11
  import { useClawLogger } from "../../utils/logger.js";
8
12
  import { StatusTracker, StatusEventType } from "./status-tracker/index.js";
@@ -79,9 +83,13 @@ export const createClawConnectionService = (api: OpenClawPluginApi) =>
79
83
  * 创建channel
80
84
  */
81
85
  async function createChannel(deviceInfo: DeviceInfo, userInfo: HonorUserInfo): Promise<void> {
82
- clawConnection.status = "connecting";
83
86
  useClawLogger().info(`[yoyoclaw-conn] creating new channel for device: ${deviceInfo.deviceId}`);
84
87
 
88
+ await registerDeviceIfGatewayAuthChanged(deviceInfo, userInfo);
89
+
90
+ const previousStatus = clawConnection.status;
91
+ clawConnection.status = "connecting";
92
+
85
93
  // 通知状态跟踪器:连接状态变更
86
94
  if (statusTracker) {
87
95
  await statusTracker.handleEvent({
@@ -89,7 +97,7 @@ async function createChannel(deviceInfo: DeviceInfo, userInfo: HonorUserInfo): P
89
97
  timestamp: Date.now(),
90
98
  data: {
91
99
  status: "connecting",
92
- previousStatus: clawConnection.status,
100
+ previousStatus,
93
101
  },
94
102
  });
95
103
  }
@@ -0,0 +1,9 @@
1
+ import crypto from "node:crypto";
2
+
3
+ export function md5(value: string): string {
4
+ return crypto.createHash("md5").update(value).digest("hex");
5
+ }
6
+
7
+ export function formatHashForLog(value: string | undefined): string {
8
+ return value ? `${value.slice(0, 8)}...` : "none";
9
+ }