@honor-claw/yoyo 1.1.2 → 1.1.4-beta.1
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/index.ts +25 -25
- package/package.json +5 -5
- package/src/apis/claw-cloud.ts +124 -124
- package/src/apis/helpers.ts +10 -10
- package/src/apis/honor-auth.ts +158 -158
- package/src/apis/http-client.ts +239 -239
- package/src/apis/index.ts +8 -8
- package/src/apis/types.ts +77 -73
- package/src/cloud-channel/channel.ts +117 -117
- package/src/cloud-channel/client.ts +3 -3
- package/src/cloud-channel/index.ts +4 -4
- package/src/cloud-channel/message-handler.ts +50 -42
- package/src/cloud-channel/session-manager.ts +14 -9
- package/src/cloud-channel/types.ts +115 -115
- package/src/commands/env/impl.ts +58 -58
- package/src/commands/env/index.ts +1 -1
- package/src/commands/index.ts +30 -30
- package/src/commands/login/impl.ts +30 -30
- package/src/commands/login/index.ts +1 -1
- package/src/commands/logout/index.ts +1 -1
- package/src/commands/status/index.ts +194 -194
- package/src/gateway-client/client.ts +90 -90
- package/src/gateway-client/device/auth.ts +57 -57
- package/src/gateway-client/device/builder.ts +105 -105
- package/src/gateway-client/device/helpers.ts +40 -40
- package/src/gateway-client/device/identity.ts +251 -251
- package/src/gateway-client/device/index.ts +40 -40
- package/src/gateway-client/device/types.ts +57 -57
- package/src/gateway-client/index.ts +5 -5
- package/src/gateway-client/protocol-client.ts +49 -34
- package/src/honor-auth/browser.ts +2 -2
- package/src/honor-auth/callback-server.ts +109 -109
- package/src/honor-auth/cloud.ts +57 -57
- package/src/honor-auth/config.ts +43 -43
- package/src/honor-auth/index.ts +3 -3
- package/src/honor-auth/token-manager.ts +90 -90
- package/src/honor-auth/types.ts +50 -50
- package/src/index.ts +10 -10
- package/src/modules/claw-configs/config-manager.ts +409 -409
- package/src/modules/claw-configs/hosts.ts +48 -48
- package/src/modules/claw-configs/index.ts +8 -8
- package/src/modules/claw-configs/provider.ts +394 -394
- package/src/modules/claw-configs/types.ts +34 -34
- package/src/modules/device/device-info.ts +1 -1
- package/src/modules/device/index.ts +3 -3
- package/src/modules/device/providers/base.ts +32 -32
- package/src/modules/device/providers/pad.ts +107 -107
- package/src/modules/device/providers/windows.ts +130 -130
- package/src/modules/device/registry.ts +48 -43
- package/src/modules/login/impl.ts +31 -26
- package/src/modules/login/index.ts +6 -6
- package/src/schemas.ts +23 -23
- package/src/services/connection/impl.ts +339 -339
- package/src/services/connection/status-tracker/events.ts +127 -127
- package/src/services/connection/status-tracker/index.ts +31 -31
- package/src/services/connection/status-tracker/storage.ts +133 -133
- package/src/services/connection/status-tracker/tracker.ts +370 -370
- package/src/services/connection/status-tracker/types.ts +131 -131
- package/src/services/connection/types.ts +20 -20
- package/src/types.ts +64 -64
- package/src/utils/id.ts +8 -8
- package/src/utils/jwt.ts +37 -37
- package/src/utils/logger.ts +20 -20
- package/src/utils/proxy.ts +58 -58
- package/src/utils/version.ts +29 -29
- package/src/utils/ws.ts +21 -21
|
@@ -1,194 +1,194 @@
|
|
|
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";
|
|
6
|
-
|
|
7
|
-
export function registerStatusCommand(
|
|
8
|
-
api: OpenClawPluginApi,
|
|
9
|
-
command: Command
|
|
10
|
-
) {
|
|
11
|
-
const nextCommand = command
|
|
12
|
-
.command("status")
|
|
13
|
-
.description("Show YOYOClaw connection status")
|
|
14
|
-
.action(async () => {
|
|
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;
|
|
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);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
return nextCommand;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Display detailed status information
|
|
36
|
-
*/
|
|
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 {
|
|
133
|
-
switch (status) {
|
|
134
|
-
case "connected":
|
|
135
|
-
return "✅";
|
|
136
|
-
case "connecting":
|
|
137
|
-
return "⏳";
|
|
138
|
-
case "idle":
|
|
139
|
-
default:
|
|
140
|
-
return "⚪";
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Get text for connection status
|
|
146
|
-
*/
|
|
147
|
-
function getStateText(status: "idle" | "connecting" | "connected"): string {
|
|
148
|
-
switch (status) {
|
|
149
|
-
case "connected":
|
|
150
|
-
return "Connected";
|
|
151
|
-
case "connecting":
|
|
152
|
-
return "Connecting";
|
|
153
|
-
case "idle":
|
|
154
|
-
default:
|
|
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;
|
|
192
|
-
}
|
|
193
|
-
return str.substring(0, maxLength - 3) + "...";
|
|
194
|
-
}
|
|
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";
|
|
6
|
+
|
|
7
|
+
export function registerStatusCommand(
|
|
8
|
+
api: OpenClawPluginApi,
|
|
9
|
+
command: Command
|
|
10
|
+
) {
|
|
11
|
+
const nextCommand = command
|
|
12
|
+
.command("status")
|
|
13
|
+
.description("Show YOYOClaw connection status")
|
|
14
|
+
.action(async () => {
|
|
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;
|
|
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);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return nextCommand;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Display detailed status information
|
|
36
|
+
*/
|
|
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 {
|
|
133
|
+
switch (status) {
|
|
134
|
+
case "connected":
|
|
135
|
+
return "✅";
|
|
136
|
+
case "connecting":
|
|
137
|
+
return "⏳";
|
|
138
|
+
case "idle":
|
|
139
|
+
default:
|
|
140
|
+
return "⚪";
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get text for connection status
|
|
146
|
+
*/
|
|
147
|
+
function getStateText(status: "idle" | "connecting" | "connected"): string {
|
|
148
|
+
switch (status) {
|
|
149
|
+
case "connected":
|
|
150
|
+
return "Connected";
|
|
151
|
+
case "connecting":
|
|
152
|
+
return "Connecting";
|
|
153
|
+
case "idle":
|
|
154
|
+
default:
|
|
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;
|
|
192
|
+
}
|
|
193
|
+
return str.substring(0, maxLength - 3) + "...";
|
|
194
|
+
}
|
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
import { WebSocket } from 'ws';
|
|
2
|
-
import type { GatewayClientOptions } from './protocol/index.js';
|
|
3
|
-
import { getConfigManager } from '../modules/claw-configs/config-manager.js';
|
|
4
|
-
import { rawDataToString } from '../utils/ws.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 纯透传 Gateway Client
|
|
8
|
-
* - 建立 WebSocket 连接
|
|
9
|
-
* - 所有消息直接透传,不做任何协议处理
|
|
10
|
-
*/
|
|
11
|
-
export class GatewayClient {
|
|
12
|
-
protected ws: WebSocket | null = null;
|
|
13
|
-
protected opts: GatewayClientOptions;
|
|
14
|
-
protected closed = false;
|
|
15
|
-
|
|
16
|
-
constructor(opts: GatewayClientOptions = {}) {
|
|
17
|
-
this.opts = opts;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
private getUrl(): string {
|
|
21
|
-
const configManager = getConfigManager();
|
|
22
|
-
return `ws://127.0.0.1:${configManager.getGatewayPort()}`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
isConnected(): boolean {
|
|
26
|
-
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* 启动连接
|
|
30
|
-
*/
|
|
31
|
-
connect(): void {
|
|
32
|
-
if (this.closed) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const url = this.getUrl();
|
|
37
|
-
this.ws = new WebSocket(url, { maxPayload: 25 * 1024 * 1024 });
|
|
38
|
-
|
|
39
|
-
this.ws.on('open', () => {
|
|
40
|
-
this.opts.onOpen?.();
|
|
41
|
-
this.onOpen();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
this.ws.on('message', data => {
|
|
45
|
-
const dataText = rawDataToString(data);
|
|
46
|
-
this.opts.onMessage?.(dataText);
|
|
47
|
-
this.onMessage(dataText);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
this.ws.on('close', (code, reason) => {
|
|
51
|
-
const reasonText = rawDataToString(reason);
|
|
52
|
-
this.ws = null;
|
|
53
|
-
this.onClose(code, reasonText);
|
|
54
|
-
this.opts.onClose?.(`code: ${code}, reason: ${reasonText ?? ''}`);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
this.ws.on('error', err => {
|
|
58
|
-
this.onError(err as Error);
|
|
59
|
-
this.opts.onClose?.(`socket error: ${(err as Error).message}`);
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* 停止连接
|
|
65
|
-
*/
|
|
66
|
-
stop(): void {
|
|
67
|
-
this.closed = true;
|
|
68
|
-
this.ws?.close();
|
|
69
|
-
this.ws = null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* 发送消息(透传)
|
|
74
|
-
*/
|
|
75
|
-
send(data: Buffer | string): void {
|
|
76
|
-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
77
|
-
throw new Error('gateway not connected');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
this.ws.send(data);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
protected onOpen(): void { }
|
|
84
|
-
|
|
85
|
-
protected onMessage(_data: string): void { }
|
|
86
|
-
|
|
87
|
-
protected onClose(_code: number, _reason: string): void { }
|
|
88
|
-
|
|
89
|
-
protected onError(_error: Error): void { }
|
|
90
|
-
}
|
|
1
|
+
import { WebSocket } from 'ws';
|
|
2
|
+
import type { GatewayClientOptions } from './protocol/index.js';
|
|
3
|
+
import { getConfigManager } from '../modules/claw-configs/config-manager.js';
|
|
4
|
+
import { rawDataToString } from '../utils/ws.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 纯透传 Gateway Client
|
|
8
|
+
* - 建立 WebSocket 连接
|
|
9
|
+
* - 所有消息直接透传,不做任何协议处理
|
|
10
|
+
*/
|
|
11
|
+
export class GatewayClient {
|
|
12
|
+
protected ws: WebSocket | null = null;
|
|
13
|
+
protected opts: GatewayClientOptions;
|
|
14
|
+
protected closed = false;
|
|
15
|
+
|
|
16
|
+
constructor(opts: GatewayClientOptions = {}) {
|
|
17
|
+
this.opts = opts;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private getUrl(): string {
|
|
21
|
+
const configManager = getConfigManager();
|
|
22
|
+
return `ws://127.0.0.1:${configManager.getGatewayPort()}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
isConnected(): boolean {
|
|
26
|
+
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 启动连接
|
|
30
|
+
*/
|
|
31
|
+
connect(): void {
|
|
32
|
+
if (this.closed) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const url = this.getUrl();
|
|
37
|
+
this.ws = new WebSocket(url, { maxPayload: 25 * 1024 * 1024 });
|
|
38
|
+
|
|
39
|
+
this.ws.on('open', () => {
|
|
40
|
+
this.opts.onOpen?.();
|
|
41
|
+
this.onOpen();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
this.ws.on('message', data => {
|
|
45
|
+
const dataText = rawDataToString(data);
|
|
46
|
+
this.opts.onMessage?.(dataText);
|
|
47
|
+
this.onMessage(dataText);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
this.ws.on('close', (code, reason) => {
|
|
51
|
+
const reasonText = rawDataToString(reason);
|
|
52
|
+
this.ws = null;
|
|
53
|
+
this.onClose(code, reasonText);
|
|
54
|
+
this.opts.onClose?.(`code: ${code}, reason: ${reasonText ?? ''}`);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
this.ws.on('error', err => {
|
|
58
|
+
this.onError(err as Error);
|
|
59
|
+
this.opts.onClose?.(`socket error: ${(err as Error).message}`);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 停止连接
|
|
65
|
+
*/
|
|
66
|
+
stop(): void {
|
|
67
|
+
this.closed = true;
|
|
68
|
+
this.ws?.close();
|
|
69
|
+
this.ws = null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 发送消息(透传)
|
|
74
|
+
*/
|
|
75
|
+
send(data: Buffer | string): void {
|
|
76
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
77
|
+
throw new Error('gateway not connected');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.ws.send(data);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
protected onOpen(): void { }
|
|
84
|
+
|
|
85
|
+
protected onMessage(_data: string): void { }
|
|
86
|
+
|
|
87
|
+
protected onClose(_code: number, _reason: string): void { }
|
|
88
|
+
|
|
89
|
+
protected onError(_error: Error): void { }
|
|
90
|
+
}
|