@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
@@ -8,6 +8,9 @@ import {
8
8
  type DeviceRole,
9
9
  } from "../types.js";
10
10
 
11
+ // 导入状态事件类型(可选导入,避免循环依赖)
12
+ export type { StatusEvent } from "../services/connection/status-tracker/events.js";
13
+
11
14
  export interface YoyoClawMessage {
12
15
  sourceRole?: DeviceRole;
13
16
  sourceDeviceId: string;
@@ -17,6 +20,12 @@ export interface YoyoClawMessage {
17
20
  port: number | string;
18
21
  data?: string;
19
22
  msgType: "userMessage" | "devicePairMessage";
23
+ /**
24
+ * 会话轮次等追加信息,只有配对消息有
25
+ */
26
+ sessionInfo?: {
27
+ nodeConnectTimestamp: number;
28
+ };
20
29
  }
21
30
 
22
31
  /**
@@ -43,6 +52,12 @@ export interface YoyoClawSocketWrapper<T> {
43
52
  extData?: {
44
53
  offlineSocketId?: string;
45
54
  };
55
+ /**
56
+ * 会话轮次等追加信息,只有配对消息有
57
+ */
58
+ sessionInfo?: {
59
+ nodeConnectTimestamp: number;
60
+ }
46
61
  }
47
62
 
48
63
  /**
@@ -57,6 +72,8 @@ export interface ClawChannelConfig {
57
72
  onClose?: () => void;
58
73
  // 当前设备未注册回调
59
74
  onDeviceNotRegistered?: () => void;
75
+ // 状态事件回调(用于状态跟踪)
76
+ onStatusEvent?: (event: any) => void;
60
77
  }
61
78
 
62
79
  export interface ClawSocketSourceInfo {
@@ -78,4 +95,17 @@ export interface ClawSocketClientOptions {
78
95
  onRemoteDeviceOffline?: (sourceDeviceId: string) => void;
79
96
  // 当前设备未注册回调
80
97
  onDeviceNotRegistered?: () => void;
98
+ // 额外的请求头
99
+ extraHeaders?: Record<string, string>;
100
+ // 状态事件回调(用于状态跟踪)
101
+ onStatusEvent?: (event: any) => void;
102
+ }
103
+
104
+ /**
105
+ * 设备会话信息
106
+ */
107
+ export interface DeviceSessionInfo {
108
+ sessionId: string;
109
+ timestamp: number;
110
+ sourceInfo: ClawSocketSourceInfo;
81
111
  }
@@ -0,0 +1,58 @@
1
+ import { getConfigManager } from "../../modules/claw-configs/config-manager.js";
2
+ import { useClawLogger } from "../../utils/logger.js";
3
+ import type { Command } from "commander";
4
+
5
+ export function registerEnvCommand(_: unknown, command: Command) {
6
+ const nextCommand = command
7
+ .command("env")
8
+ .description("Manage runtime environment (dev/test/production)")
9
+ .option("--set <env>", "Set environment: dev, test or production")
10
+ .option("--gray <tag>", "Set gray header")
11
+ .action(async (options) => {
12
+ const { set: setEnv, gray: grayTag } = options;
13
+ const logger = useClawLogger();
14
+
15
+ try {
16
+ const configManager = getConfigManager();
17
+
18
+ if (setEnv) {
19
+ // 设置环境
20
+ if (setEnv !== "test" && setEnv !== "dev" && setEnv !== "production") {
21
+ logger.error("❌ Invalid environment. Use 'test' or 'production'.");
22
+ return;
23
+ }
24
+
25
+ await configManager.updateEnv(setEnv);
26
+ logger.info(`✅ Environment set to: ${setEnv}`);
27
+ }
28
+
29
+ if (grayTag) {
30
+ // 设置灰度标签
31
+ await configManager.updateGrayTag(grayTag);
32
+ logger.info(`✅ Gray tag set to: ${grayTag}`);
33
+ } else if (grayTag === '') {
34
+ // 清除灰度标签
35
+ await configManager.updateGrayTag(undefined);
36
+ logger.info(`✅ Gray tag cleared`);
37
+ }
38
+
39
+ if (!setEnv && grayTag === undefined) {
40
+ // 获取当前环境信息
41
+ const currentEnv = configManager.getEnv();
42
+ const currentGrayTag = configManager.getGrayTag();
43
+
44
+ logger.info(`📋 Current environment: ${currentEnv}`);
45
+ if (currentGrayTag) {
46
+ logger.info(`📋 Current gray tag: ${currentGrayTag}`);
47
+ } else {
48
+ logger.info(`📋 Gray tag: not set`);
49
+ }
50
+ }
51
+ } catch (error) {
52
+ const errorMessage = error instanceof Error ? error.message : String(error);
53
+ logger.error(`❌ Failed to manage environment: ${errorMessage}`);
54
+ }
55
+ });
56
+
57
+ return nextCommand;
58
+ }
@@ -0,0 +1 @@
1
+ export * from './impl.js';
@@ -2,6 +2,8 @@ import { type OpenClawPluginApi } from "openclaw/plugin-sdk";
2
2
  import { registerLoginCommand } from "./login/index.js";
3
3
  import { registerStatusCommand } from "./status/index.js";
4
4
  import { registerLogoutCommand } from "./logout/index.js";
5
+ import { registerEnvCommand } from "./env/index.js";
6
+ import { isBetaVersion } from "../utils/version.js";
5
7
 
6
8
  export function registerCommands(api: OpenClawPluginApi) {
7
9
  api.registerCli(
@@ -10,9 +12,17 @@ export function registerCommands(api: OpenClawPluginApi) {
10
12
  .command("honor")
11
13
  .description("Commands for honor yoyoclaw");
12
14
 
15
+ // @ts-ignore
13
16
  registerLoginCommand(api, rootCommand);
17
+ // @ts-ignore
14
18
  registerStatusCommand(api, rootCommand);
15
- // registerLogoutCommand(api, rootCommand);
19
+ // @ts-ignore
20
+ registerLogoutCommand(api, rootCommand);
21
+
22
+ // 只在 beta 版本时注册 env 命令
23
+ if (isBetaVersion()) {
24
+ registerEnvCommand(api, rootCommand);
25
+ }
16
26
  },
17
27
  { commands: ["honor"] }
18
28
  );
@@ -1,20 +1,29 @@
1
1
  import { type OpenClawPluginApi } from "openclaw/plugin-sdk";
2
2
  import { performLogin } from "../../modules/login/impl.js";
3
3
  import { type Command } from "commander";
4
+ import { isBetaVersion } from "../../utils/version.js";
4
5
 
5
6
  export function registerLoginCommand(api: OpenClawPluginApi, command: Command) {
6
- const nextCommand = command
7
+ let nextCommand = command
7
8
  .command("login")
8
- .description("Login to YOYOClaw and register devices")
9
- .option("--skip-auth", "debug mode, no auth required")
9
+ .description("login to yoyoclaw and register devices");
10
+
11
+ if (isBetaVersion()) {
12
+ nextCommand = nextCommand.option(
13
+ "--skip-auth",
14
+ "debug mode, no auth required"
15
+ );
16
+ }
17
+
18
+ nextCommand = nextCommand
19
+ .option("-u, --userId <userId>", "user ID for direct login")
20
+ .option("--token <token>", "token for direct login")
10
21
  .action(async (options) => {
11
- const { skipAuth } = options;
22
+ const { skipAuth, userId, token } = options;
12
23
 
13
- api.logger.info(
14
- `honor login CLI command called with skip auth: ${!!skipAuth}`
15
- );
24
+ api.logger.debug?.('honor login CLI command called');
16
25
 
17
- await performLogin({ noAuth: skipAuth });
26
+ await performLogin({ noAuth: skipAuth, userId, token });
18
27
  });
19
28
 
20
29
  return nextCommand;
@@ -0,0 +1,23 @@
1
+ import { type OpenClawPluginApi } from "openclaw/plugin-sdk";
2
+ import { type Command } from "commander";
3
+ import { performLogout } from "../../honor-auth/cloud.js";
4
+
5
+ export function registerLogoutCommand(api: OpenClawPluginApi, command: Command) {
6
+ const nextCommand = command
7
+ .command("logout")
8
+ .description("Logout and clear user configuration")
9
+ .action(async () => {
10
+ api.logger.info("logout CLI command called");
11
+
12
+ try {
13
+ await performLogout();
14
+ console.log("✅ Logout successful, gateway will automatically restart to handle new configuration");
15
+ } catch (error) {
16
+ const errorMessage = error instanceof Error ? error.message : String(error);
17
+ console.error("❌ Logout failed:", errorMessage);
18
+ throw error;
19
+ }
20
+ });
21
+
22
+ return nextCommand;
23
+ }
@@ -1,53 +1 @@
1
- import { type OpenClawPluginApi } from "openclaw/plugin-sdk";
2
- import { getConnectionManager } from "../../services/connection-manager.js";
3
- import { type Command } from "commander";
4
-
5
- export function registerLogoutCommand(api: OpenClawPluginApi, command: Command) {
6
- const nextCommand = command
7
- .command("logout")
8
- .description("Logout and disconnect from YOYOClaw")
9
- .option("--force", "Force logout without confirmation")
10
- .action(async (options) => {
11
- api.logger.info("honor logout CLI command called");
12
-
13
- const { force } = options;
14
- const connectionManager = getConnectionManager();
15
- const isConnected = connectionManager.isConnected();
16
- const connectedDevices = connectionManager.getConnectedDevices();
17
-
18
- if (!isConnected) {
19
- console.log("⚠️ 当前未连接到 YOYOClaw");
20
- return;
21
- }
22
-
23
- // 如果有活跃的设备连接且不是强制模式,提示确认
24
- if (connectedDevices.length > 0 && !force) {
25
- console.log(`⚠️ 当前有 ${connectedDevices.length} 个活跃的设备连接:`);
26
- for (const device of connectedDevices) {
27
- console.log(` - ${device}`);
28
- }
29
- console.log("\n⚠️ 断开连接将停止所有设备间的通信");
30
- console.log('💡 使用 --force 选项可以跳过此确认');
31
-
32
- // 注意:在命令行工具中,我们无法直接等待用户输入
33
- // 这里我们给出提示,然后继续执行
34
- console.log("\n🔌 正在断开连接...");
35
- } else {
36
- console.log("🔌 正在断开连接...");
37
- }
38
-
39
- try {
40
- await connectionManager.cleanup();
41
- console.log("✅ WebSocket 连接已关闭");
42
- console.log("✅ 所有设备连接已清理");
43
- console.log("✅ 资源已释放");
44
- console.log("👋 已登出");
45
- } catch (error) {
46
- const errorMessage = error instanceof Error ? error.message : String(error);
47
- console.error("❌ 登出失败:", errorMessage);
48
- throw error;
49
- }
50
- });
51
-
52
- return nextCommand;
53
- }
1
+ export * from "./impl.js";
@@ -1,64 +1,194 @@
1
- import { type OpenClawPluginApi } from 'openclaw/plugin-sdk';
2
- import { type Command } from 'commander';
3
- import { getConnectionStatus } from '../../services/connection/impl.js';
1
+ import { type OpenClawPluginApi } from "openclaw/plugin-sdk";
2
+ import { type Command } from "commander";
3
+ import { StatusStorage } from "../../services/connection/status-tracker/index.js";
4
+ import type { ConnectionStatusData } from "../../services/connection/status-tracker/index.js";
5
+ import { loadToken } from "../../honor-auth/token-manager.js";
4
6
 
5
- export function registerStatusCommand(api: OpenClawPluginApi, command: Command) {
7
+ export function registerStatusCommand(
8
+ api: OpenClawPluginApi,
9
+ command: Command
10
+ ) {
6
11
  const nextCommand = command
7
- .command('status')
8
- .description('Show YOYOClaw connection status')
12
+ .command("status")
13
+ .description("Show YOYOClaw connection status")
9
14
  .action(async () => {
10
- api.logger.info('YOYOClaw status CLI command called');
11
-
12
- // 获取连接状态
13
- const status = getConnectionStatus();
14
-
15
- // 连接状态
16
- const stateEmoji = getStateEmoji(status);
17
- const stateText = getStateText(status);
18
-
19
- api.logger.info(`连接状态: ${stateEmoji} ${stateText}`);
20
-
21
- if (status === 'connected') {
22
- // 已连接状态
23
- api.logger.info('\n✅ YOYOClaw 连接正常');
24
- } else if (status === 'connecting') {
25
- // 连接中状态
26
- api.logger.info('\n⏳ 正在建立连接...');
27
- } else {
28
- // 未连接状态
29
- api.logger.info("\n💡 请先执行 'yoyoclaw login' 建立连接");
15
+ api.logger.debug?.("YOYOClaw status CLI command called");
16
+
17
+ // Check if user is logged in
18
+ const userInfo = await loadToken();
19
+ if (!userInfo) {
20
+ console.log("❌ You need to login first. Please run: openclaw honor login");
21
+ return;
30
22
  }
23
+
24
+ // Try to load detailed status from status file
25
+ const statusStorage = new StatusStorage();
26
+ const statusData = await statusStorage.load();
27
+
28
+ displayDetailedStatus(statusData);
31
29
  });
32
30
 
33
31
  return nextCommand;
34
32
  }
35
33
 
36
34
  /**
37
- * 获取状态对应的 emoji
35
+ * Display detailed status information
38
36
  */
39
- function getStateEmoji(status: 'idle' | 'connecting' | 'connected'): string {
37
+ function displayDetailedStatus(statusData: ConnectionStatusData | null): void {
38
+ if (!statusData) {
39
+ console.log("\n❌ No status data available");
40
+ return;
41
+ }
42
+
43
+ // Cloud Socket status
44
+ console.log("\n📡 Cloud Socket:");
45
+ const cloudStatus = statusData.cloudSocket;
46
+ const cloudEmoji = cloudStatus.connected ? "✅" : "❌";
47
+ console.log(
48
+ ` Status: ${cloudEmoji} ${cloudStatus.connected ? "Connected" : "Disconnected"}`
49
+ );
50
+
51
+ if (cloudStatus.connectedAt) {
52
+ console.log(` Connected at: ${formatDateTime(cloudStatus.connectedAt)}`);
53
+ }
54
+
55
+ if (cloudStatus.lastDisconnectedAt) {
56
+ console.log(
57
+ ` Last disconnected: ${formatDateTime(cloudStatus.lastDisconnectedAt)}`
58
+ );
59
+ }
60
+
61
+ console.log(` Retry count: ${cloudStatus.retryCount}`);
62
+ console.log(` Last error: ${cloudStatus.lastError || "None"}`);
63
+
64
+ // Gateway connection statistics
65
+ console.log("\n🌐 Gateway Connections:");
66
+ console.log(` Total connections: ${statusData.gateway.totalConnections}`);
67
+ console.log(` Active connections: ${statusData.gateway.activeConnections}`);
68
+
69
+ if (statusData.gateway.connections.length > 0) {
70
+ console.log(" ┌────────────────────────────┬────────────────────────────┬────────────┐");
71
+ console.log(" │ Session ID │ Hardware Device ID │ Connected │");
72
+ console.log(" ├────────────────────────────┼────────────────────────────┼────────────┤");
73
+
74
+ for (const conn of statusData.gateway.connections) {
75
+ const sessionId = truncateString(conn.sessionId, 26);
76
+ const deviceId = truncateString(conn.hardwareDeviceId, 26);
77
+ const connTime = formatTime(conn.connectedAt);
78
+ console.log(
79
+ ` │ ${sessionId.padEnd(28)}│ ${deviceId.padEnd(28)}│ ${connTime.padEnd(12)}│`
80
+ );
81
+ }
82
+
83
+ console.log(" └────────────────────────────┴────────────────────────────┴────────────┘");
84
+ }
85
+
86
+ // Physical device statistics
87
+ console.log("\n📱 Physical Devices:");
88
+ console.log(` Unique devices: ${statusData.devices.uniqueHardwareDevices}`);
89
+
90
+ if (statusData.devices.devices.length > 0) {
91
+ console.log(" ┌────────────────────────────┬─────────┬────────────┐");
92
+ console.log(" │ Hardware Device ID │ Sessions │ Last Active│");
93
+ console.log(" ├────────────────────────────┼─────────┼────────────┤");
94
+
95
+ for (const device of statusData.devices.devices) {
96
+ const deviceId = truncateString(device.hardwareDeviceId, 26);
97
+ const sessions = String(device.sessions).padEnd(9);
98
+ const lastActive = formatTime(device.lastActiveAt);
99
+ console.log(
100
+ ` │ ${deviceId.padEnd(28)}│ ${sessions}│ ${lastActive.padEnd(12)}│`
101
+ );
102
+ }
103
+
104
+ console.log(" └────────────────────────────┴─────────┴────────────┘");
105
+ }
106
+
107
+ // Connection history
108
+ console.log("\n📈 Connection History:");
109
+ console.log(` Total connections: ${statusData.history.connectionCount}`);
110
+ console.log(` Total disconnections: ${statusData.history.disconnectionCount}`);
111
+
112
+ if (statusData.history.lastConnectionAt) {
113
+ console.log(
114
+ ` Last connection: ${formatDateTime(statusData.history.lastConnectionAt)}`
115
+ );
116
+ }
117
+
118
+ if (statusData.history.lastDisconnectionAt) {
119
+ console.log(
120
+ ` Last disconnection: ${formatDateTime(statusData.history.lastDisconnectionAt)}`
121
+ );
122
+ }
123
+
124
+ // Update time
125
+ console.log("\n🕐 Status Updated At:");
126
+ console.log(` ${formatDateTime(statusData.updatedAt)}`);
127
+ }
128
+
129
+ /**
130
+ * Get emoji for connection status
131
+ */
132
+ function getStateEmoji(status: "idle" | "connecting" | "connected"): string {
40
133
  switch (status) {
41
- case 'connected':
42
- return '';
43
- case 'connecting':
44
- return '';
45
- case 'idle':
134
+ case "connected":
135
+ return "";
136
+ case "connecting":
137
+ return "";
138
+ case "idle":
46
139
  default:
47
- return '';
140
+ return "";
48
141
  }
49
142
  }
50
143
 
51
144
  /**
52
- * 获取状态对应的文本
145
+ * Get text for connection status
53
146
  */
54
- function getStateText(status: 'idle' | 'connecting' | 'connected'): string {
147
+ function getStateText(status: "idle" | "connecting" | "connected"): string {
55
148
  switch (status) {
56
- case 'connected':
57
- return '已连接';
58
- case 'connecting':
59
- return '连接中';
60
- case 'idle':
149
+ case "connected":
150
+ return "Connected";
151
+ case "connecting":
152
+ return "Connecting";
153
+ case "idle":
61
154
  default:
62
- return '未连接';
155
+ return "Disconnected";
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Format date time (YYYY-MM-DD HH:mm:ss)
161
+ */
162
+ function formatDateTime(isoString: string): string {
163
+ const date = new Date(isoString);
164
+ const year = date.getFullYear();
165
+ const month = String(date.getMonth() + 1).padStart(2, "0");
166
+ const day = String(date.getDate()).padStart(2, "0");
167
+ const hours = String(date.getHours()).padStart(2, "0");
168
+ const minutes = String(date.getMinutes()).padStart(2, "0");
169
+ const seconds = String(date.getSeconds()).padStart(2, "0");
170
+
171
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
172
+ }
173
+
174
+ /**
175
+ * Format time (HH:mm:ss)
176
+ */
177
+ function formatTime(isoString: string): string {
178
+ const date = new Date(isoString);
179
+ const hours = String(date.getHours()).padStart(2, "0");
180
+ const minutes = String(date.getMinutes()).padStart(2, "0");
181
+ const seconds = String(date.getSeconds()).padStart(2, "0");
182
+
183
+ return `${hours}:${minutes}:${seconds}`;
184
+ }
185
+
186
+ /**
187
+ * Truncate string with ellipsis
188
+ */
189
+ function truncateString(str: string, maxLength: number): string {
190
+ if (str.length <= maxLength) {
191
+ return str;
63
192
  }
193
+ return str.substring(0, maxLength - 3) + "...";
64
194
  }
@@ -16,7 +16,7 @@ import {
16
16
  } from "./device/identity.js";
17
17
  import { buildDeviceAuthPayloadV3 } from "./device/auth.js";
18
18
  import type { DeviceIdentity } from "./device/types.js";
19
- import { rawDataToString } from "openclaw/plugin-sdk";
19
+ import { rawDataToString } from "../utils/ws.js";
20
20
 
21
21
  /**
22
22
  * 插件 Gateway Client 配置选项(简化版)
@@ -1,9 +1,7 @@
1
- import { rawDataToString } from "openclaw/plugin-sdk";
2
- import { WebSocket } from "ws";
3
- import { GatewayClientOptions } from "./types.js";
4
- import { useClawLogger } from "../utils/logger.js";
5
-
6
- const GATEWAY_SERVER_URL = "ws://127.0.0.1:18789";
1
+ import { WebSocket } from 'ws';
2
+ import { GatewayClientOptions } from './types.js';
3
+ import { getConfigManager } from '../modules/claw-configs/config-manager.js';
4
+ import { rawDataToString } from '../utils/ws.js';
7
5
 
8
6
  /**
9
7
  * 纯透传 Gateway Client
@@ -26,29 +24,28 @@ export class GatewayClient {
26
24
  if (this.closed) {
27
25
  return;
28
26
  }
29
- const url = GATEWAY_SERVER_URL;
27
+
28
+ const configManager = getConfigManager();
29
+ const url = `ws://127.0.0.1:${configManager.getGatewayPort()}`;
30
30
  this.ws = new WebSocket(url, { maxPayload: 25 * 1024 * 1024 });
31
31
 
32
- this.ws.on("open", () => {
32
+ this.ws.on('open', () => {
33
33
  this.opts.onOpen?.();
34
34
  });
35
35
 
36
- this.ws.on("message", (data) => {
36
+ this.ws.on('message', data => {
37
37
  const dataText = rawDataToString(data);
38
- useClawLogger().debug?.(`[yoyoclaw-gateway] received message:, ${dataText.slice(0, 500)}`);
39
38
  this.opts.onMessage?.(dataText);
40
39
  });
41
40
 
42
- this.ws.on("close", (code, reason) => {
41
+ this.ws.on('close', (code, reason) => {
43
42
  const reasonText = rawDataToString(reason);
44
43
  this.ws = null;
45
- useClawLogger().info(`[yoyoclaw-gateway] closed (${code}): ${reasonText}`);
46
- this.opts.onClose?.();
44
+ this.opts.onClose?.(`code: ${code}, reason: ${reasonText ?? ''}`);
47
45
  });
48
46
 
49
- this.ws.on("error", (err) => {
50
- useClawLogger().error(`[yoyoclaw-gateway] error: ${String(err)}`);
51
- this.opts.onClose?.();
47
+ this.ws.on('error', err => {
48
+ this.opts.onClose?.(`socket error: ${(err as Error).message}`);
52
49
  });
53
50
  }
54
51
 
@@ -66,11 +63,9 @@ export class GatewayClient {
66
63
  */
67
64
  send(data: Buffer | string): void {
68
65
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
69
- useClawLogger().error(
70
- "[yoyoclaw-gateway] send failed: gateway not connected"
71
- );
72
- return;
66
+ throw new Error('gateway not connected');
73
67
  }
68
+
74
69
  this.ws.send(data);
75
70
  }
76
71
  }
@@ -4,5 +4,5 @@
4
4
  export interface GatewayClientOptions {
5
5
  onMessage?: (data: string) => void; // 原始消息透传
6
6
  onOpen?: () => void;
7
- onClose?: () => void;
8
- }
7
+ onClose?: (reason: string) => void;
8
+ }