@omnixal/openclaw-nats-plugin 0.1.15 → 0.1.17
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
package/sidecar/src/config.ts
CHANGED
|
@@ -17,7 +17,7 @@ export const envSchema = {
|
|
|
17
17
|
},
|
|
18
18
|
gateway: {
|
|
19
19
|
wsUrl: Env.string({ default: 'ws://localhost:18789', env: 'OPENCLAW_WS_URL' }),
|
|
20
|
-
token: Env.string({ default: '', env: '
|
|
20
|
+
token: Env.string({ default: '', env: 'OPENCLAW_DEVICE_TOKEN' }),
|
|
21
21
|
},
|
|
22
22
|
consumer: {
|
|
23
23
|
name: Env.string({ default: 'openclaw-main', env: 'NATS_CONSUMER_NAME' }),
|
|
@@ -15,6 +15,7 @@ export interface GatewayInjectPayload {
|
|
|
15
15
|
export class GatewayClientService extends BaseService implements OnModuleInit, OnModuleDestroy {
|
|
16
16
|
private ws: WebSocket | null = null;
|
|
17
17
|
private connected = false;
|
|
18
|
+
private connectSent = false;
|
|
18
19
|
private reconnectAttempt = 0;
|
|
19
20
|
private reconnectTimer: Timer | null = null;
|
|
20
21
|
private requestId = 0;
|
|
@@ -24,33 +25,35 @@ export class GatewayClientService extends BaseService implements OnModuleInit, O
|
|
|
24
25
|
async onModuleInit(): Promise<void> {
|
|
25
26
|
this.wsUrl = this.config.get('gateway.wsUrl');
|
|
26
27
|
this.token = this.config.get('gateway.token');
|
|
27
|
-
if (this.wsUrl) {
|
|
28
|
+
if (this.wsUrl && this.token) {
|
|
28
29
|
this.connect();
|
|
30
|
+
} else {
|
|
31
|
+
this.logger.warn('Gateway WebSocket not configured — skipping connection (need wsUrl + deviceToken)');
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
private connect(): void {
|
|
33
36
|
try {
|
|
34
|
-
|
|
35
|
-
this.ws = new WebSocket(
|
|
37
|
+
this.connectSent = false;
|
|
38
|
+
this.ws = new WebSocket(this.wsUrl);
|
|
36
39
|
|
|
37
40
|
this.ws.onopen = () => {
|
|
38
|
-
this.logger.info('Gateway WebSocket connected');
|
|
39
41
|
this.reconnectAttempt = 0;
|
|
40
|
-
this.
|
|
42
|
+
this.logger.info('Gateway WebSocket opened, waiting for connect.challenge');
|
|
41
43
|
};
|
|
42
44
|
|
|
43
45
|
this.ws.onmessage = (event) => {
|
|
44
|
-
this.handleMessage(event);
|
|
46
|
+
this.handleMessage(String(event.data));
|
|
45
47
|
};
|
|
46
48
|
|
|
47
49
|
this.ws.onclose = () => {
|
|
48
50
|
this.connected = false;
|
|
51
|
+
this.connectSent = false;
|
|
49
52
|
this.scheduleReconnect();
|
|
50
53
|
};
|
|
51
54
|
|
|
52
|
-
this.ws.onerror = (
|
|
53
|
-
this.logger.warn('Gateway WebSocket error'
|
|
55
|
+
this.ws.onerror = () => {
|
|
56
|
+
this.logger.warn('Gateway WebSocket error');
|
|
54
57
|
this.connected = false;
|
|
55
58
|
};
|
|
56
59
|
} catch (err) {
|
|
@@ -59,23 +62,77 @@ export class GatewayClientService extends BaseService implements OnModuleInit, O
|
|
|
59
62
|
}
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
private handleMessage(
|
|
65
|
+
private handleMessage(data: string): void {
|
|
66
|
+
let frame: any;
|
|
63
67
|
try {
|
|
64
|
-
|
|
65
|
-
if (frame.type === 'res' && frame.ok) {
|
|
66
|
-
this.connected = true;
|
|
67
|
-
}
|
|
68
|
+
frame = JSON.parse(data);
|
|
68
69
|
} catch {
|
|
69
|
-
|
|
70
|
+
this.logger.warn(`Failed to parse WebSocket message: ${data.slice(0, 200)}`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Server challenge — respond with connect frame
|
|
75
|
+
if (frame.type === 'event' && frame.event === 'connect.challenge') {
|
|
76
|
+
this.logger.debug('Received connect.challenge from server');
|
|
77
|
+
this.sendConnectFrame();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Some gateway versions send an event before challenge; treat any pre-connect event as trigger
|
|
82
|
+
if (!this.connectSent && frame.type === 'event') {
|
|
83
|
+
this.logger.debug('Received event before connect sent, sending connect frame');
|
|
84
|
+
this.sendConnectFrame();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Successful connect response — must be hello-ok
|
|
89
|
+
if (frame.type === 'res' && frame.ok === true) {
|
|
90
|
+
const payload = frame.payload;
|
|
91
|
+
if (payload?.type === 'hello-ok') {
|
|
92
|
+
if (!this.connected) {
|
|
93
|
+
this.connected = true;
|
|
94
|
+
this.logger.info('OpenClaw handshake complete — connected');
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// Regular RPC ok response (e.g. for inject calls)
|
|
99
|
+
this.logger.debug('Received RPC ok response', { id: frame.id });
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Error response
|
|
104
|
+
if (frame.type === 'res' && frame.ok === false) {
|
|
105
|
+
this.logger.warn('Gateway RPC error', { id: frame.id, error: frame.error });
|
|
70
106
|
}
|
|
71
107
|
}
|
|
72
108
|
|
|
73
|
-
private
|
|
109
|
+
private sendConnectFrame(): void {
|
|
110
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN || this.connectSent) return;
|
|
111
|
+
this.connectSent = true;
|
|
112
|
+
this.logger.info('Sending connect frame');
|
|
113
|
+
|
|
74
114
|
this.send({
|
|
75
115
|
type: 'req',
|
|
76
|
-
id: ++this.requestId
|
|
116
|
+
id: `connect-${++this.requestId}`,
|
|
77
117
|
method: 'connect',
|
|
78
|
-
params: {
|
|
118
|
+
params: {
|
|
119
|
+
minProtocol: 3,
|
|
120
|
+
maxProtocol: 3,
|
|
121
|
+
client: {
|
|
122
|
+
id: 'nats-sidecar',
|
|
123
|
+
version: '1.0.0',
|
|
124
|
+
platform: 'linux',
|
|
125
|
+
mode: 'backend',
|
|
126
|
+
},
|
|
127
|
+
role: 'operator',
|
|
128
|
+
scopes: ['operator.read'],
|
|
129
|
+
caps: [],
|
|
130
|
+
commands: [],
|
|
131
|
+
permissions: {},
|
|
132
|
+
auth: { token: this.token },
|
|
133
|
+
locale: 'en-US',
|
|
134
|
+
userAgent: 'nats-sidecar/1.0.0',
|
|
135
|
+
},
|
|
79
136
|
});
|
|
80
137
|
}
|
|
81
138
|
|
|
@@ -87,7 +144,7 @@ export class GatewayClientService extends BaseService implements OnModuleInit, O
|
|
|
87
144
|
|
|
88
145
|
private scheduleReconnect(): void {
|
|
89
146
|
if (this.reconnectTimer) return;
|
|
90
|
-
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempt),
|
|
147
|
+
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempt), 30_000);
|
|
91
148
|
this.reconnectAttempt++;
|
|
92
149
|
this.logger.debug(`Reconnecting to Gateway in ${delay}ms (attempt ${this.reconnectAttempt})`);
|
|
93
150
|
this.reconnectTimer = setTimeout(() => {
|
|
@@ -102,7 +159,7 @@ export class GatewayClientService extends BaseService implements OnModuleInit, O
|
|
|
102
159
|
}
|
|
103
160
|
this.send({
|
|
104
161
|
type: 'req',
|
|
105
|
-
id: ++this.requestId
|
|
162
|
+
id: `rpc-${++this.requestId}`,
|
|
106
163
|
method: 'send',
|
|
107
164
|
params: {
|
|
108
165
|
target: payload.target,
|
|
@@ -127,5 +184,6 @@ export class GatewayClientService extends BaseService implements OnModuleInit, O
|
|
|
127
184
|
this.ws = null;
|
|
128
185
|
}
|
|
129
186
|
this.connected = false;
|
|
187
|
+
this.connectSent = false;
|
|
130
188
|
}
|
|
131
189
|
}
|