crewly 1.4.2 → 1.4.4
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/config/skills/orchestrator/reply-gchat/execute.sh +4 -4
- package/config/templates/dev-fullstack/norms/docker-deploy-cleanup.md +28 -0
- package/dist/backend/backend/src/constants.d.ts +45 -14
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +45 -14
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/cloud/cloud.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/cloud/cloud.controller.js +14 -3
- package/dist/backend/backend/src/controllers/cloud/cloud.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/cloud/relay.controller.d.ts +15 -3
- package/dist/backend/backend/src/controllers/cloud/relay.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/cloud/relay.controller.js +46 -8
- package/dist/backend/backend/src/controllers/cloud/relay.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/cloud/relay.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/cloud/relay.routes.js +2 -1
- package/dist/backend/backend/src/controllers/cloud/relay.routes.js.map +1 -1
- package/dist/backend/backend/src/controllers/messaging/messenger.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/messaging/messenger.routes.js +86 -3
- package/dist/backend/backend/src/controllers/messaging/messenger.routes.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts +5 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +33 -0
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-client.service.d.ts +27 -0
- package/dist/backend/backend/src/services/cloud/cloud-client.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-client.service.js +28 -0
- package/dist/backend/backend/src/services/cloud/cloud-client.service.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/relay-client.service.d.ts +47 -44
- package/dist/backend/backend/src/services/cloud/relay-client.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/relay-client.service.js +185 -152
- package/dist/backend/backend/src/services/cloud/relay-client.service.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/relay-server.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/relay-server.service.js +3 -12
- package/dist/backend/backend/src/services/cloud/relay-server.service.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/relay.types.d.ts +27 -4
- package/dist/backend/backend/src/services/cloud/relay.types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/relay.types.js.map +1 -1
- package/dist/backend/backend/src/services/messaging/adapters/telegram-messenger.adapter.d.ts +16 -8
- package/dist/backend/backend/src/services/messaging/adapters/telegram-messenger.adapter.d.ts.map +1 -1
- package/dist/backend/backend/src/services/messaging/adapters/telegram-messenger.adapter.js +30 -35
- package/dist/backend/backend/src/services/messaging/adapters/telegram-messenger.adapter.js.map +1 -1
- package/dist/backend/backend/src/services/messaging/response-router.service.d.ts +8 -0
- package/dist/backend/backend/src/services/messaging/response-router.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/messaging/response-router.service.js +16 -0
- package/dist/backend/backend/src/services/messaging/response-router.service.js.map +1 -1
- package/dist/backend/backend/src/services/telegram/index.d.ts +14 -0
- package/dist/backend/backend/src/services/telegram/index.d.ts.map +1 -0
- package/dist/backend/backend/src/services/telegram/index.js +11 -0
- package/dist/backend/backend/src/services/telegram/index.js.map +1 -0
- package/dist/backend/backend/src/services/telegram/telegram-credentials.service.d.ts +54 -0
- package/dist/backend/backend/src/services/telegram/telegram-credentials.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/telegram/telegram-credentials.service.js +98 -0
- package/dist/backend/backend/src/services/telegram/telegram-credentials.service.js.map +1 -0
- package/dist/backend/backend/src/services/telegram/telegram-initializer.d.ts +76 -0
- package/dist/backend/backend/src/services/telegram/telegram-initializer.d.ts.map +1 -0
- package/dist/backend/backend/src/services/telegram/telegram-initializer.js +131 -0
- package/dist/backend/backend/src/services/telegram/telegram-initializer.js.map +1 -0
- package/dist/backend/backend/src/services/telegram/telegram-orchestrator-bridge.d.ts +73 -0
- package/dist/backend/backend/src/services/telegram/telegram-orchestrator-bridge.d.ts.map +1 -0
- package/dist/backend/backend/src/services/telegram/telegram-orchestrator-bridge.js +172 -0
- package/dist/backend/backend/src/services/telegram/telegram-orchestrator-bridge.js.map +1 -0
- package/dist/backend/backend/src/services/telegram/telegram-thread-store.service.d.ts +86 -0
- package/dist/backend/backend/src/services/telegram/telegram-thread-store.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/telegram/telegram-thread-store.service.js +157 -0
- package/dist/backend/backend/src/services/telegram/telegram-thread-store.service.js.map +1 -0
- package/dist/backend/backend/src/services/telegram/telegram.service.d.ts +173 -0
- package/dist/backend/backend/src/services/telegram/telegram.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/telegram/telegram.service.js +304 -0
- package/dist/backend/backend/src/services/telegram/telegram.service.js.map +1 -0
- package/dist/cli/backend/src/constants.d.ts +45 -14
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +45 -14
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/cli/src/commands/migrate.d.ts +101 -0
- package/dist/cli/cli/src/commands/migrate.d.ts.map +1 -0
- package/dist/cli/cli/src/commands/migrate.js +335 -0
- package/dist/cli/cli/src/commands/migrate.js.map +1 -0
- package/dist/cli/cli/src/index.js +8 -0
- package/dist/cli/cli/src/index.js.map +1 -1
- package/frontend/dist/assets/{index-f1dc9f80.css → index-2b76b01d.css} +1 -1
- package/frontend/dist/assets/{index-c35cdbc5.js → index-83869ca7.js} +321 -321
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Relay Client Service
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* communication when direct connections are unavailable (NAT/firewall).
|
|
4
|
+
* HTTP polling-based client that connects to a Cloud Message Queue for
|
|
5
|
+
* inter-node communication when direct connections are unavailable (NAT/firewall).
|
|
6
6
|
*
|
|
7
|
-
* Flow: Node A (Agent) <-> Cloud (
|
|
7
|
+
* Flow: Node A (Agent) <-> Cloud (Queue) <-> Node B (Orchestrator)
|
|
8
8
|
*
|
|
9
9
|
* Features:
|
|
10
|
-
* -
|
|
10
|
+
* - HTTP-based registration and message polling (replaces WebSocket)
|
|
11
11
|
* - End-to-end encryption (E2EE) — relay never sees plaintext
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
12
|
+
* - Exponential backoff on registration failures
|
|
13
|
+
* - Automatic message acknowledgement after processing
|
|
14
14
|
*
|
|
15
15
|
* @module services/cloud/relay-client.service
|
|
16
16
|
*/
|
|
17
|
-
import WebSocket from 'ws';
|
|
18
17
|
import { EventEmitter } from 'events';
|
|
19
18
|
import { LoggerService } from '../core/logger.service.js';
|
|
20
19
|
import { CLOUD_CONSTANTS } from '../../constants.js';
|
|
21
|
-
import { isRelayMessage, } from './relay.types.js';
|
|
22
20
|
import { deriveKey, generateSalt, encrypt, decrypt, serializeEnvelope, deserializeEnvelope, } from './relay-crypto.service.js';
|
|
23
21
|
const RELAY = CLOUD_CONSTANTS.RELAY;
|
|
24
22
|
// ---------------------------------------------------------------------------
|
|
@@ -27,22 +25,22 @@ const RELAY = CLOUD_CONSTANTS.RELAY;
|
|
|
27
25
|
/**
|
|
28
26
|
* RelayClientService singleton.
|
|
29
27
|
*
|
|
30
|
-
* Manages
|
|
31
|
-
*
|
|
28
|
+
* Manages HTTP-based registration, polling, and message exchange with
|
|
29
|
+
* a Cloud Message Queue. Encrypts/decrypts messages for E2EE.
|
|
32
30
|
*/
|
|
33
31
|
export class RelayClientService extends EventEmitter {
|
|
34
32
|
static instance = null;
|
|
35
33
|
logger;
|
|
36
|
-
/** Current WebSocket connection */
|
|
37
|
-
ws = null;
|
|
38
34
|
/** Client configuration (set via connect()) */
|
|
39
35
|
config = null;
|
|
40
36
|
/** Current connection state */
|
|
41
37
|
state = 'disconnected';
|
|
42
|
-
/** Assigned
|
|
38
|
+
/** Assigned queue ID after registration (used as sessionId) */
|
|
43
39
|
sessionId = null;
|
|
44
|
-
/**
|
|
45
|
-
|
|
40
|
+
/** Peer's queue ID for sending messages */
|
|
41
|
+
peerQueueId = null;
|
|
42
|
+
/** Polling interval timer */
|
|
43
|
+
pollInterval = null;
|
|
46
44
|
/** Reconnection attempt counter */
|
|
47
45
|
reconnectAttempts = 0;
|
|
48
46
|
/** Reconnection timer */
|
|
@@ -53,6 +51,8 @@ export class RelayClientService extends EventEmitter {
|
|
|
53
51
|
salt = null;
|
|
54
52
|
/** Whether disconnect was intentional (skip reconnect) */
|
|
55
53
|
intentionalDisconnect = false;
|
|
54
|
+
/** Timestamp of last seen message for pagination */
|
|
55
|
+
lastSeenTimestamp = null;
|
|
56
56
|
constructor() {
|
|
57
57
|
super();
|
|
58
58
|
this.logger = LoggerService.getInstance().createComponentLogger('RelayClientService');
|
|
@@ -82,10 +82,10 @@ export class RelayClientService extends EventEmitter {
|
|
|
82
82
|
// Public API
|
|
83
83
|
// -------------------------------------------------------------------------
|
|
84
84
|
/**
|
|
85
|
-
* Connect to
|
|
85
|
+
* Connect to the Cloud Message Queue.
|
|
86
86
|
*
|
|
87
|
-
*
|
|
88
|
-
* and
|
|
87
|
+
* Registers via HTTP POST, derives the E2EE encryption key,
|
|
88
|
+
* and starts polling for messages if not immediately paired.
|
|
89
89
|
*
|
|
90
90
|
* @param config - Relay client configuration
|
|
91
91
|
*
|
|
@@ -93,7 +93,7 @@ export class RelayClientService extends EventEmitter {
|
|
|
93
93
|
* ```ts
|
|
94
94
|
* const client = RelayClientService.getInstance();
|
|
95
95
|
* client.connect({
|
|
96
|
-
*
|
|
96
|
+
* apiUrl: 'https://api.crewlyai.com',
|
|
97
97
|
* pairingCode: 'abc-123',
|
|
98
98
|
* role: 'agent',
|
|
99
99
|
* token: 'sk-xxx',
|
|
@@ -110,15 +110,16 @@ export class RelayClientService extends EventEmitter {
|
|
|
110
110
|
this.config = config;
|
|
111
111
|
this.intentionalDisconnect = false;
|
|
112
112
|
this.reconnectAttempts = 0;
|
|
113
|
+
this.lastSeenTimestamp = null;
|
|
113
114
|
// Derive encryption key from shared secret
|
|
114
115
|
this.salt = generateSalt();
|
|
115
116
|
this.encryptionKey = deriveKey(config.sharedSecret, this.salt);
|
|
116
|
-
this.
|
|
117
|
+
this.doRegister();
|
|
117
118
|
}
|
|
118
119
|
/**
|
|
119
|
-
* Disconnect from the
|
|
120
|
+
* Disconnect from the Cloud Message Queue.
|
|
120
121
|
*
|
|
121
|
-
*
|
|
122
|
+
* Stops polling and cleans up all timers.
|
|
122
123
|
*/
|
|
123
124
|
disconnect() {
|
|
124
125
|
this.intentionalDisconnect = true;
|
|
@@ -126,7 +127,7 @@ export class RelayClientService extends EventEmitter {
|
|
|
126
127
|
this.setState('disconnected');
|
|
127
128
|
}
|
|
128
129
|
/**
|
|
129
|
-
* Send an encrypted message to the paired peer via the
|
|
130
|
+
* Send an encrypted message to the paired peer via the Cloud Queue.
|
|
130
131
|
*
|
|
131
132
|
* The message is encrypted locally before being sent — the relay
|
|
132
133
|
* server only forwards the opaque ciphertext.
|
|
@@ -146,10 +147,12 @@ export class RelayClientService extends EventEmitter {
|
|
|
146
147
|
if (!this.encryptionKey) {
|
|
147
148
|
throw new Error('Encryption key not derived — cannot send');
|
|
148
149
|
}
|
|
150
|
+
if (!this.peerQueueId) {
|
|
151
|
+
throw new Error('No peer queue ID — cannot send');
|
|
152
|
+
}
|
|
149
153
|
const envelope = encrypt(plaintext, this.encryptionKey);
|
|
150
154
|
const payload = serializeEnvelope(envelope);
|
|
151
|
-
|
|
152
|
-
this.sendRaw(msg);
|
|
155
|
+
this.sendViaHttp(payload);
|
|
153
156
|
}
|
|
154
157
|
/**
|
|
155
158
|
* Get the current client state.
|
|
@@ -160,7 +163,7 @@ export class RelayClientService extends EventEmitter {
|
|
|
160
163
|
return this.state;
|
|
161
164
|
}
|
|
162
165
|
/**
|
|
163
|
-
* Get the assigned session ID (available after registration).
|
|
166
|
+
* Get the assigned session ID (queue ID, available after registration).
|
|
164
167
|
*
|
|
165
168
|
* @returns Session ID or null if not registered
|
|
166
169
|
*/
|
|
@@ -168,159 +171,204 @@ export class RelayClientService extends EventEmitter {
|
|
|
168
171
|
return this.sessionId;
|
|
169
172
|
}
|
|
170
173
|
// -------------------------------------------------------------------------
|
|
171
|
-
//
|
|
174
|
+
// Registration (replaces WebSocket connect)
|
|
172
175
|
// -------------------------------------------------------------------------
|
|
173
176
|
/**
|
|
174
|
-
*
|
|
177
|
+
* Register with the Cloud Message Queue via HTTP POST.
|
|
175
178
|
*/
|
|
176
|
-
|
|
179
|
+
async doRegister() {
|
|
177
180
|
if (!this.config)
|
|
178
181
|
return;
|
|
179
182
|
this.setState('connecting');
|
|
180
|
-
this.logger.info('
|
|
183
|
+
this.logger.info('Registering with Cloud Message Queue', { apiUrl: this.config.apiUrl });
|
|
181
184
|
try {
|
|
182
|
-
|
|
183
|
-
|
|
185
|
+
const response = await fetch(`${this.config.apiUrl}/api/v1/relay/queue/register`, {
|
|
186
|
+
method: 'POST',
|
|
187
|
+
headers: {
|
|
188
|
+
'Content-Type': 'application/json',
|
|
189
|
+
'Authorization': `Bearer ${this.config.token}`,
|
|
190
|
+
},
|
|
191
|
+
body: JSON.stringify({
|
|
192
|
+
role: this.config.role,
|
|
193
|
+
pairingCode: this.config.pairingCode,
|
|
194
|
+
}),
|
|
184
195
|
});
|
|
196
|
+
if (!response.ok) {
|
|
197
|
+
throw new Error(`Registration failed with HTTP ${response.status}`);
|
|
198
|
+
}
|
|
199
|
+
const data = await response.json();
|
|
200
|
+
if (!data.success || !data.queueId) {
|
|
201
|
+
throw new Error('Registration response missing required fields');
|
|
202
|
+
}
|
|
203
|
+
this.sessionId = data.queueId;
|
|
204
|
+
this.reconnectAttempts = 0;
|
|
205
|
+
this.logger.info('Registered with Cloud Queue', { queueId: data.queueId });
|
|
206
|
+
if (data.peerQueueId) {
|
|
207
|
+
// Already paired
|
|
208
|
+
this.peerQueueId = data.peerQueueId;
|
|
209
|
+
this.setState('paired');
|
|
210
|
+
this.emit('paired', data.peerQueueId, 'peer');
|
|
211
|
+
this.startPolling();
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
// Waiting for peer — start polling for pairing + messages
|
|
215
|
+
this.setState('registered');
|
|
216
|
+
this.startPolling();
|
|
217
|
+
}
|
|
185
218
|
}
|
|
186
219
|
catch (err) {
|
|
187
|
-
this.logger.error('Failed to
|
|
220
|
+
this.logger.error('Failed to register with Cloud Queue', {
|
|
188
221
|
error: err instanceof Error ? err.message : String(err),
|
|
189
222
|
});
|
|
190
223
|
this.setState('error');
|
|
191
224
|
this.scheduleReconnect();
|
|
192
|
-
return;
|
|
193
225
|
}
|
|
194
|
-
this.ws.on('open', () => {
|
|
195
|
-
this.logger.info('WebSocket connected, sending registration');
|
|
196
|
-
this.reconnectAttempts = 0;
|
|
197
|
-
this.sendRegister();
|
|
198
|
-
});
|
|
199
|
-
this.ws.on('message', (data) => {
|
|
200
|
-
this.handleMessage(data);
|
|
201
|
-
});
|
|
202
|
-
this.ws.on('close', (code, reason) => {
|
|
203
|
-
this.logger.info('WebSocket closed', { code, reason: reason.toString('utf8') });
|
|
204
|
-
this.cleanup();
|
|
205
|
-
if (!this.intentionalDisconnect) {
|
|
206
|
-
this.setState('disconnected');
|
|
207
|
-
this.scheduleReconnect();
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
this.ws.on('error', (err) => {
|
|
211
|
-
this.logger.error('WebSocket error', { error: err.message });
|
|
212
|
-
this.setState('error');
|
|
213
|
-
this.scheduleReconnect();
|
|
214
|
-
});
|
|
215
226
|
}
|
|
227
|
+
// -------------------------------------------------------------------------
|
|
228
|
+
// Polling (replaces WebSocket onMessage)
|
|
229
|
+
// -------------------------------------------------------------------------
|
|
216
230
|
/**
|
|
217
|
-
*
|
|
231
|
+
* Start the polling interval to fetch new messages from the queue.
|
|
218
232
|
*/
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
role: this.config.role,
|
|
225
|
-
pairingCode: this.config.pairingCode,
|
|
226
|
-
token: this.config.token,
|
|
227
|
-
};
|
|
228
|
-
this.sendRaw(msg);
|
|
233
|
+
startPolling() {
|
|
234
|
+
this.stopPolling();
|
|
235
|
+
this.pollInterval = setInterval(() => {
|
|
236
|
+
this.poll();
|
|
237
|
+
}, RELAY.POLL_INTERVAL_MS);
|
|
229
238
|
}
|
|
230
239
|
/**
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* @param data - Raw message data
|
|
240
|
+
* Stop the polling interval.
|
|
234
241
|
*/
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
parsed = JSON.parse(raw);
|
|
242
|
+
stopPolling() {
|
|
243
|
+
if (this.pollInterval) {
|
|
244
|
+
clearInterval(this.pollInterval);
|
|
245
|
+
this.pollInterval = null;
|
|
240
246
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Poll the queue for new messages and process them.
|
|
250
|
+
*/
|
|
251
|
+
async poll() {
|
|
252
|
+
if (!this.config || !this.sessionId)
|
|
247
253
|
return;
|
|
254
|
+
try {
|
|
255
|
+
let url = `${this.config.apiUrl}/api/v1/relay/queue/poll?queueId=${this.sessionId}`;
|
|
256
|
+
if (this.lastSeenTimestamp) {
|
|
257
|
+
url += `&since=${encodeURIComponent(this.lastSeenTimestamp)}`;
|
|
258
|
+
}
|
|
259
|
+
const response = await fetch(url, {
|
|
260
|
+
method: 'GET',
|
|
261
|
+
headers: {
|
|
262
|
+
'Authorization': `Bearer ${this.config.token}`,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
if (!response.ok) {
|
|
266
|
+
this.logger.error('Poll request failed', { status: response.status });
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const data = await response.json();
|
|
270
|
+
if (!data.messages || data.messages.length === 0)
|
|
271
|
+
return;
|
|
272
|
+
const messageIds = [];
|
|
273
|
+
for (const msg of data.messages) {
|
|
274
|
+
messageIds.push(msg.id);
|
|
275
|
+
// Update last seen timestamp for pagination
|
|
276
|
+
if (!this.lastSeenTimestamp || msg.createdAt > this.lastSeenTimestamp) {
|
|
277
|
+
this.lastSeenTimestamp = msg.createdAt;
|
|
278
|
+
}
|
|
279
|
+
// Decrypt and emit the message
|
|
280
|
+
this.handleQueueMessage(msg.payload);
|
|
281
|
+
}
|
|
282
|
+
// Acknowledge processed messages
|
|
283
|
+
if (messageIds.length > 0) {
|
|
284
|
+
await this.ackMessages(messageIds);
|
|
285
|
+
}
|
|
248
286
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
this.setState('registered');
|
|
254
|
-
this.startHeartbeat();
|
|
255
|
-
this.logger.info('Registered with relay', { sessionId: msg.sessionId });
|
|
256
|
-
break;
|
|
257
|
-
case 'paired':
|
|
258
|
-
this.setState('paired');
|
|
259
|
-
this.logger.info('Paired with peer', { peerSessionId: msg.peerSessionId, peerRole: msg.peerRole });
|
|
260
|
-
this.emit('paired', msg.peerSessionId, msg.peerRole);
|
|
261
|
-
break;
|
|
262
|
-
case 'relay':
|
|
263
|
-
this.handleRelayData(msg);
|
|
264
|
-
break;
|
|
265
|
-
case 'heartbeat_ack':
|
|
266
|
-
// Heartbeat acknowledged — no action needed
|
|
267
|
-
break;
|
|
268
|
-
case 'peer_disconnected':
|
|
269
|
-
this.setState('registered');
|
|
270
|
-
this.logger.info('Peer disconnected', { peerSessionId: msg.peerSessionId });
|
|
271
|
-
this.emit('peerDisconnected', msg.peerSessionId);
|
|
272
|
-
break;
|
|
273
|
-
case 'error':
|
|
274
|
-
this.logger.error('Relay server error', { code: msg.code, message: msg.message });
|
|
275
|
-
this.setState('error');
|
|
276
|
-
this.scheduleReconnect();
|
|
277
|
-
break;
|
|
278
|
-
default:
|
|
279
|
-
this.logger.warn('Unhandled relay message type', { type: msg.type });
|
|
287
|
+
catch (err) {
|
|
288
|
+
this.logger.error('Poll cycle failed', {
|
|
289
|
+
error: err instanceof Error ? err.message : String(err),
|
|
290
|
+
});
|
|
280
291
|
}
|
|
281
292
|
}
|
|
282
293
|
/**
|
|
283
|
-
* Handle
|
|
294
|
+
* Handle a single queue message payload by decrypting it.
|
|
284
295
|
*
|
|
285
|
-
* @param
|
|
296
|
+
* @param payload - Encrypted payload string
|
|
286
297
|
*/
|
|
287
|
-
|
|
298
|
+
handleQueueMessage(payload) {
|
|
288
299
|
if (!this.encryptionKey) {
|
|
289
300
|
this.logger.error('Cannot decrypt — no encryption key');
|
|
290
301
|
return;
|
|
291
302
|
}
|
|
292
303
|
try {
|
|
293
|
-
const envelope = deserializeEnvelope(
|
|
304
|
+
const envelope = deserializeEnvelope(payload);
|
|
294
305
|
const plaintext = decrypt(envelope, this.encryptionKey);
|
|
295
306
|
this.emit('message', plaintext);
|
|
296
307
|
}
|
|
297
308
|
catch (err) {
|
|
298
|
-
this.logger.error('Failed to decrypt
|
|
309
|
+
this.logger.error('Failed to decrypt queue message', {
|
|
299
310
|
error: err instanceof Error ? err.message : String(err),
|
|
300
311
|
});
|
|
301
|
-
// Log only — do not re-emit error to avoid uncaught exception crash
|
|
302
312
|
}
|
|
303
313
|
}
|
|
304
|
-
// -------------------------------------------------------------------------
|
|
305
|
-
// Heartbeat
|
|
306
|
-
// -------------------------------------------------------------------------
|
|
307
314
|
/**
|
|
308
|
-
*
|
|
315
|
+
* Acknowledge processed messages so they are not re-delivered.
|
|
316
|
+
*
|
|
317
|
+
* @param messageIds - Array of message IDs to acknowledge
|
|
309
318
|
*/
|
|
310
|
-
|
|
311
|
-
this.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
this.
|
|
315
|
-
|
|
319
|
+
async ackMessages(messageIds) {
|
|
320
|
+
if (!this.config || !this.sessionId)
|
|
321
|
+
return;
|
|
322
|
+
try {
|
|
323
|
+
await fetch(`${this.config.apiUrl}/api/v1/relay/queue/ack`, {
|
|
324
|
+
method: 'POST',
|
|
325
|
+
headers: {
|
|
326
|
+
'Content-Type': 'application/json',
|
|
327
|
+
'Authorization': `Bearer ${this.config.token}`,
|
|
328
|
+
},
|
|
329
|
+
body: JSON.stringify({
|
|
330
|
+
queueId: this.sessionId,
|
|
331
|
+
messageIds,
|
|
332
|
+
}),
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
catch (err) {
|
|
336
|
+
this.logger.error('Failed to acknowledge messages', {
|
|
337
|
+
error: err instanceof Error ? err.message : String(err),
|
|
338
|
+
});
|
|
339
|
+
}
|
|
316
340
|
}
|
|
341
|
+
// -------------------------------------------------------------------------
|
|
342
|
+
// Send via HTTP (replaces WebSocket send)
|
|
343
|
+
// -------------------------------------------------------------------------
|
|
317
344
|
/**
|
|
318
|
-
*
|
|
345
|
+
* Send an encrypted payload to the peer via HTTP POST.
|
|
346
|
+
*
|
|
347
|
+
* @param payload - Serialized encrypted envelope
|
|
319
348
|
*/
|
|
320
|
-
|
|
321
|
-
if (this.
|
|
322
|
-
|
|
323
|
-
|
|
349
|
+
async sendViaHttp(payload) {
|
|
350
|
+
if (!this.config || !this.peerQueueId)
|
|
351
|
+
return;
|
|
352
|
+
try {
|
|
353
|
+
const response = await fetch(`${this.config.apiUrl}/api/v1/relay/queue/send`, {
|
|
354
|
+
method: 'POST',
|
|
355
|
+
headers: {
|
|
356
|
+
'Content-Type': 'application/json',
|
|
357
|
+
'Authorization': `Bearer ${this.config.token}`,
|
|
358
|
+
},
|
|
359
|
+
body: JSON.stringify({
|
|
360
|
+
peerQueueId: this.peerQueueId,
|
|
361
|
+
payload,
|
|
362
|
+
}),
|
|
363
|
+
});
|
|
364
|
+
if (!response.ok) {
|
|
365
|
+
this.logger.error('Failed to send message', { status: response.status });
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
catch (err) {
|
|
369
|
+
this.logger.error('Send request failed', {
|
|
370
|
+
error: err instanceof Error ? err.message : String(err),
|
|
371
|
+
});
|
|
324
372
|
}
|
|
325
373
|
}
|
|
326
374
|
// -------------------------------------------------------------------------
|
|
@@ -328,6 +376,7 @@ export class RelayClientService extends EventEmitter {
|
|
|
328
376
|
// -------------------------------------------------------------------------
|
|
329
377
|
/**
|
|
330
378
|
* Schedule a reconnection attempt with exponential backoff.
|
|
379
|
+
* Applied to HTTP register failures.
|
|
331
380
|
*/
|
|
332
381
|
scheduleReconnect() {
|
|
333
382
|
if (this.intentionalDisconnect)
|
|
@@ -345,22 +394,12 @@ export class RelayClientService extends EventEmitter {
|
|
|
345
394
|
});
|
|
346
395
|
this.reconnectTimer = setTimeout(() => {
|
|
347
396
|
this.reconnectTimer = null;
|
|
348
|
-
this.
|
|
397
|
+
this.doRegister();
|
|
349
398
|
}, delay);
|
|
350
399
|
}
|
|
351
400
|
// -------------------------------------------------------------------------
|
|
352
401
|
// Helpers
|
|
353
402
|
// -------------------------------------------------------------------------
|
|
354
|
-
/**
|
|
355
|
-
* Send a raw relay message over the WebSocket.
|
|
356
|
-
*
|
|
357
|
-
* @param message - Relay message to send
|
|
358
|
-
*/
|
|
359
|
-
sendRaw(message) {
|
|
360
|
-
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
361
|
-
this.ws.send(JSON.stringify(message));
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
403
|
/**
|
|
365
404
|
* Update the client state and emit a stateChange event.
|
|
366
405
|
*
|
|
@@ -373,22 +412,16 @@ export class RelayClientService extends EventEmitter {
|
|
|
373
412
|
}
|
|
374
413
|
}
|
|
375
414
|
/**
|
|
376
|
-
* Clean up timers and
|
|
415
|
+
* Clean up timers and reset connection state.
|
|
377
416
|
*/
|
|
378
417
|
cleanup() {
|
|
379
|
-
this.
|
|
418
|
+
this.stopPolling();
|
|
380
419
|
if (this.reconnectTimer) {
|
|
381
420
|
clearTimeout(this.reconnectTimer);
|
|
382
421
|
this.reconnectTimer = null;
|
|
383
422
|
}
|
|
384
|
-
if (this.ws) {
|
|
385
|
-
this.ws.removeAllListeners();
|
|
386
|
-
if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {
|
|
387
|
-
this.ws.close(1000, 'Client disconnecting');
|
|
388
|
-
}
|
|
389
|
-
this.ws = null;
|
|
390
|
-
}
|
|
391
423
|
this.sessionId = null;
|
|
424
|
+
this.peerQueueId = null;
|
|
392
425
|
}
|
|
393
426
|
}
|
|
394
427
|
//# sourceMappingURL=relay-client.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-client.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/relay-client.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"relay-client.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/relay-client.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-server.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/relay-server.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAC;AAGjD,OAAO,EAEL,KAAK,YAAY,EAUlB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"relay-server.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/relay-server.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAC;AAGjD,OAAO,EAEL,KAAK,YAAY,EAUlB,MAAM,kBAAkB,CAAC;AAsB1B;;;;;GAKG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAmC;IAC1D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IAEzC,gCAAgC;IAChC,OAAO,CAAC,GAAG,CAAgC;IAC3C,2CAA2C;IAC3C,OAAO,CAAC,OAAO,CAAmD;IAClE,iEAAiE;IACjE,OAAO,CAAC,YAAY,CAA4C;IAChE,oCAAoC;IACpC,OAAO,CAAC,OAAO,CAAS;IAExB,OAAO;IAIP;;;;OAIG;IACH,MAAM,CAAC,WAAW,IAAI,kBAAkB;IAOxC;;OAEG;IACH,MAAM,CAAC,aAAa,IAAI,IAAI;IAQ5B;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,OAAO,EAAE;QAAE,UAAU,CAAC,EAAE,UAAU,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IA6B/E;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B3B;;;;OAIG;IACH,SAAS,IAAI,OAAO;IAIpB;;;;OAIG;IACH,cAAc,IAAI,MAAM;IAIxB;;;;OAIG;IACH,WAAW,IAAI,YAAY,EAAE;IAc7B;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IA0ExB;;;;;;;;OAQG;IACH,OAAO,CAAC,cAAc;IAgCtB;;;;;;;;OAQG;IACH,OAAO,CAAC,cAAc;IA+BtB;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe;IAiCvB;;;;;;;OAOG;IACH,OAAO,CAAC,WAAW;IAkBnB;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAgBvB;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IA6CxB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB;IAW3B;;;;;OAKG;IACH,OAAO,CAAC,IAAI;IAMZ;;;;;;OAMG;IACH,OAAO,CAAC,SAAS;CAKlB"}
|
|
@@ -16,15 +16,6 @@ import { LoggerService } from '../core/logger.service.js';
|
|
|
16
16
|
import { CLOUD_CONSTANTS } from '../../constants.js';
|
|
17
17
|
import { isRelayMessage, } from './relay.types.js';
|
|
18
18
|
const RELAY = CLOUD_CONSTANTS.RELAY;
|
|
19
|
-
/** Named WebSocket close codes used by the relay server. */
|
|
20
|
-
const WS_CLOSE = {
|
|
21
|
-
/** Normal server shutdown */
|
|
22
|
-
SERVER_SHUTDOWN: 1001,
|
|
23
|
-
/** Client failed to register within the handshake window */
|
|
24
|
-
HANDSHAKE_TIMEOUT: 4001,
|
|
25
|
-
/** Client missed heartbeat deadline */
|
|
26
|
-
HEARTBEAT_TIMEOUT: 4002,
|
|
27
|
-
};
|
|
28
19
|
// ---------------------------------------------------------------------------
|
|
29
20
|
// Service
|
|
30
21
|
// ---------------------------------------------------------------------------
|
|
@@ -120,7 +111,7 @@ export class RelayServerService {
|
|
|
120
111
|
if (client.heartbeatTimer) {
|
|
121
112
|
clearTimeout(client.heartbeatTimer);
|
|
122
113
|
}
|
|
123
|
-
client.ws.close(
|
|
114
|
+
client.ws.close(1001, 'Server shutting down');
|
|
124
115
|
}
|
|
125
116
|
this.clients.clear();
|
|
126
117
|
this.pairingQueue.clear();
|
|
@@ -179,7 +170,7 @@ export class RelayServerService {
|
|
|
179
170
|
// Handshake timeout: client must register within the window
|
|
180
171
|
const handshakeTimer = setTimeout(() => {
|
|
181
172
|
this.sendError(ws, 'HANDSHAKE_TIMEOUT', 'Registration timeout — send a register message');
|
|
182
|
-
ws.close(
|
|
173
|
+
ws.close(4001, 'Handshake timeout');
|
|
183
174
|
}, RELAY.HANDSHAKE_TIMEOUT_MS);
|
|
184
175
|
ws.on('message', (data) => {
|
|
185
176
|
const raw = typeof data === 'string' ? data : data.toString('utf8');
|
|
@@ -461,7 +452,7 @@ export class RelayServerService {
|
|
|
461
452
|
this.logger.warn('Heartbeat timeout', { sessionId });
|
|
462
453
|
const client = this.clients.get(sessionId);
|
|
463
454
|
if (client) {
|
|
464
|
-
client.ws.close(
|
|
455
|
+
client.ws.close(4002, 'Heartbeat timeout');
|
|
465
456
|
this.handleDisconnect(sessionId);
|
|
466
457
|
}
|
|
467
458
|
}, RELAY.HEARTBEAT_TIMEOUT_MS);
|