@honor-claw/yoyo 1.2.1-beta.4 → 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.4",
3
+ "version": "1.2.1-beta.5",
4
4
  "description": "OpenClaw Honor Yoyo connection plugin",
5
5
  "keywords": [
6
6
  "ai",
@@ -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
+ }